В вычислении, указатель NULL или пустая ссылка является сохраненным значением для указания, что указатель или ссылка не относится к действительному объекту. Программы обычно используют нулевые указатели для представления таких условий, как конец списка неизвестной длины или невозможность выполнить какое-либо действие; это использование нулевых указателей можно сравнить с типами, допускающими значение NULL, и со значением Nothing в типе параметра.
Нулевой указатель не следует путать с неинициализированным указателем : нулевой указатель гарантированно не равен любому указателю, указывающему на действительный объект. Однако, в зависимости от языка и реализации, неинициализированный указатель может не иметь такой гарантии. Он может сравниваться с другими действительными указателями; или он может сравнивать равные нулевые указатели. И то и другое могло происходить в разное время; или сравнение может быть неопределенным.
В C два нулевых указателя любого типа гарантированно сравниваются как равные. Макрос препроцессора NULL
определяется как определяемая реализацией константа нулевого указателя, которая в C99 может быть переносимо выражена как ((void *)0)
означающая, что целочисленное значение 0
преобразовано в тип void*
(указатель на void ). Стандарт C не говорит, что нулевой указатель совпадает с указателем на адрес памяти 0, хотя на практике это может иметь место. Разыменование нулевого указателя является неопределенным поведением в C, и соответствующая реализация может предполагать, что любой указатель, для которого разыменован, не является нулевым.
На практике разыменование нулевого указателя может привести к попытке чтения или записи из памяти, которая не отображается, вызывая ошибку сегментации или нарушение доступа к памяти. Это может проявляться как сбой программы или трансформироваться в исключение программного обеспечения, которое может быть обнаружено программным кодом. Однако есть определенные обстоятельства, когда это не так. Например, в реальном режиме x86 адрес доступен для чтения и обычно для записи, и разыменование указателя на этот адрес является вполне допустимым, но обычно нежелательным действием, которое может привести к неопределенному, но не вызывающему сбоев поведению приложения. Бывают случаи, когда разыменование указателя на нулевой адрес является преднамеренным и четко определенным; например, код BIOS, написанный на C для 16-битных устройств x86 реального режима, может записывать IDT по физическому адресу 0 машины путем разыменования нулевого указателя для записи. Компилятор также может оптимизировать разыменование нулевого указателя, избегая ошибки сегментации, но вызывая другое нежелательное поведение. 0000:0000
В C ++, хотя NULL
макрос был унаследован от C, целочисленный литерал для нуля традиционно предпочитался представлять константу нулевого указателя. Однако в C ++ 11 nullptr
вместо нее используется явная константа нулевого указателя.
В некоторых средах языков программирования (по крайней мере, в одной проприетарной реализации Lisp, например) значение, используемое в качестве нулевого указателя (вызываемого nil
в Lisp ), может фактически быть указателем на блок внутренних данных, полезных для реализации (но не доступный явно из пользовательские программы), что позволяет использовать один и тот же регистр в качестве полезной константы и быстрого доступа к внутренним компонентам реализации. Это известно как nil
вектор.
В языках с тегированной архитектурой возможно нулевой указатель может быть заменен тегированным объединением, которое обеспечивает явную обработку исключительного случая; фактически, возможно, нулевой указатель можно рассматривать как помеченный указатель с вычисляемым тегом.
В языках программирования для нулевого указателя используются разные литералы. В Python, например, вызывается нулевое значение None
. В Pascal и Swift вызывается нулевой указатель nil
. В Eiffel это называется void
ссылкой.
Поскольку нулевой указатель не указывает на значимый объект, попытка разыменования (т. Е. Доступа к данным, хранящимся в этой ячейке памяти) нулевого указателя обычно (но не всегда) вызывает ошибку времени выполнения или немедленный сбой программы.
nil
представляет собой нулевой указатель на первый адрес в памяти, который также используется для инициализации управляемых переменных. Разыменование его вызывает внешнее исключение ОС, которое отображается на экземпляр исключения Pascal EAccessViolation, если модуль System.SysUtils связан в предложении uses. NullPointerException
(NPE), который может быть обнаружен кодом обработки ошибок, но предпочтительной практикой является обеспечение того, чтобы такие исключения никогда не возникали.nil
объекту (который является нулевым указателем), не вызывая прерывания программы; сообщение просто игнорируется, а возвращаемое значение (если есть) - nil
или 0
, в зависимости от типа.Существуют методы, облегчающие отладку разыменования нулевого указателя. Бонд и др. предлагаем изменить JVM, чтобы отслеживать нулевое распространение. Идея системы Casper заключается в использовании преобразования исходного кода для отслеживания этого распространения без изменения JVM. В некоторых случаях можно автоматически сгенерировать патч для исправления исключений с нулевым указателем.
В 2009 году сэр Тони Хоара заявил, что он изобрел ссылку нулевой в 1965 году как часть Алгол W языка. В этой ссылке 2009 года Хоар описывает свое изобретение как «ошибку на миллиард долларов»:
Я называю это своей ошибкой на миллиард долларов. Это было изобретение нулевой ссылки в 1965 году. В то время я проектировал первую всеобъемлющую систему типов для ссылок на объектно-ориентированном языке (АЛГОЛ W). Моя цель состояла в том, чтобы гарантировать, что любое использование ссылок должно быть абсолютно безопасным, с автоматической проверкой компилятором. Но я не мог устоять перед соблазном вставить пустую ссылку просто потому, что это было так легко реализовать. Это привело к бесчисленным ошибкам, уязвимостям и системным сбоям, которые, вероятно, причинили боль и ущерб на миллиард долларов за последние сорок лет.