В вычислении , указатель NULL или пустая ссылка является сохраненным значением для указания , что указатель или ссылка не относится к действительному объекту . Программы обычно используют нулевые указатели для представления таких условий, как конец списка неизвестной длины или невозможность выполнить какое-либо действие; это использование нулевых указателей можно сравнить с типами, допускающими значение NULL, и со значением Nothing в типе параметра .
Нулевой указатель не следует путать с неинициализированным указателем : нулевой указатель гарантированно не будет сравниваться с любым указателем, указывающим на действительный объект. Однако, в зависимости от языка и реализации, неинициализированный указатель может не иметь такой гарантии. Он может сравниваться с другими действительными указателями; или он может сравнивать равные нулевым указателям. И то, и другое могло происходить в разное время; или сравнение может быть неопределенным .
C
В C два нулевых указателя любого типа гарантированно сравниваются как равные. [1] Макрос препроцессора NULL
определяется как константа нулевого указателя, определяемая реализацией, [2] которая в C99 может быть переносимо выражена как целочисленное значение, 0
преобразованное в тип void*
(указатель на void ). [3] Стандарт C не говорит, что нулевой указатель совпадает с указателем на адрес памяти 0, хотя на практике это может иметь место. Разыменование нулевого указателя является неопределенным поведением в C, [4], и соответствующая реализация может предполагать, что любой указатель, который разыменован, не является нулевым.
На практике разыменование нулевого указателя может привести к попытке чтения или записи из памяти, которая не отображается, вызывая ошибку сегментации или нарушение доступа к памяти. Это может проявляться как сбой программы или трансформироваться в исключение программного обеспечения, которое может быть обнаружено программным кодом. Однако есть определенные обстоятельства, когда это не так. Например, в реальном режиме x86 адрес доступен для чтения и обычно для записи, и разыменование указателя на этот адрес является вполне допустимым, но обычно нежелательным действием, которое может привести к неопределенному, но не вызывающему сбоев поведению приложения. Бывают случаи, когда разыменование указателя на нулевой адрес является преднамеренным и четко определенным; например, код BIOS, написанный на C для 16-битных устройств x86 реального режима, может записывать IDT по физическому адресу 0 машины путем разыменования нулевого указателя для записи. Компилятор также может оптимизировать разыменование нулевого указателя, избегая ошибки сегментации, но вызывая другое нежелательное поведение .0000:0000
C ++
В C ++, хотя NULL
макрос был унаследован от C, целочисленный литерал для нуля традиционно предпочитался представлять константу нулевого указателя. [5] Однако в C ++ 11nullptr
вместо нее используется явная константа нулевого указателя .
Другие языки
В некоторых средах языков программирования (по крайней мере, в одной частной реализации Lisp, например) [ необходима цитата ] значение, используемое в качестве нулевого указателя (вызываемого nil
в Lisp ), может фактически быть указателем на блок внутренних данных, полезных для реализации (но не доступны явно из пользовательских программ), что позволяет использовать один и тот же регистр в качестве полезной константы и быстрого доступа к внутренним компонентам реализации. Это известно как nil
вектор .
В языках с тегированной архитектурой возможно нулевой указатель может быть заменен тегированным объединением, которое обеспечивает явную обработку исключительного случая; фактически, возможно, нулевой указатель можно рассматривать как помеченный указатель с вычисляемым тегом.
В языках программирования для нулевого указателя используются разные литералы . В Python, например, вызывается нулевое значение None
. В Pascal и Swift вызывается нулевой указатель nil
. В Eiffel это называется void
ссылкой.
Нулевое разыменование
Поскольку нулевой указатель не указывает на значимый объект, попытка разыменования (т. Е. Доступа к данным, хранящимся в этой ячейке памяти) нулевого указателя обычно (но не всегда) вызывает ошибку времени выполнения или немедленный сбой программы.
- В C разыменование нулевого указателя является неопределенным поведением . [4] Многие реализации приводят к тому, что такой код приводит к остановке программы с нарушением прав доступа , поскольку в качестве представления нулевого указателя выбирается адрес, который никогда не выделяется системой для хранения объектов. Однако такое поведение не универсально. Это также не гарантируется, поскольку компиляторам разрешено оптимизировать программы в предположении, что они не имеют неопределенного поведения.
- В Delphi и многих других реализациях Паскаля константа
nil
представляет собой нулевой указатель на первый адрес в памяти, который также используется для инициализации управляемых переменных. Разыменование его вызывает внешнее исключение ОС, которое отображается на экземпляр исключения Pascal EAccessViolation, если модуль System.SysUtils связан в предложении uses. - В Java доступ к нулевой ссылке запускает
NullPointerException
(NPE), который может быть обнаружен кодом обработки ошибок, но предпочтительной практикой является обеспечение того, чтобы такие исключения никогда не возникали. - В .NET доступ к нулевой ссылке вызывает исключение NullReferenceException. Хотя их перехват обычно считается плохой практикой, этот тип исключения может быть перехвачен и обработан программой.
- В Objective-C сообщения могут быть отправлены
nil
объекту (который является нулевым указателем), не вызывая прерывания программы; сообщение просто игнорируется, а возвращаемое значение (если есть) -nil
или0
, в зависимости от типа. [6] - До появления SMAP ошибка разыменования нулевого указателя могла быть использована путем сопоставления нуля страницы с адресным пространством злоумышленника и, следовательно, для указания нулевого указателя на эту область. В некоторых случаях это могло привести к выполнению кода . [7]
Смягчение
Существуют методы, облегчающие отладку разыменования нулевого указателя. [8] [9] Бонд и др. [8] предлагают модифицировать JVM, чтобы отслеживать распространение нуля. Идея системы Casper [9] заключается в использовании преобразования исходного кода для отслеживания этого распространения без изменения JVM. В некоторых случаях можно автоматически сгенерировать патч для исправления исключений с нулевым указателем. [10]
История
В 2009 году Тони Хоара (CAR Hoare) заявил [11] , что он изобрел ссылку нулевой в 1965 году как часть Алгол W языка. В этой ссылке 2009 года Хоар описывает свое изобретение как «ошибку на миллиард долларов»:
Я называю это своей ошибкой на миллиард долларов. Это было изобретение нулевой ссылки в 1965 году. В то время я проектировал первую всеобъемлющую систему типов для ссылок на объектно-ориентированном языке (АЛГОЛ W). Моя цель состояла в том, чтобы гарантировать, что любое использование ссылок должно быть абсолютно безопасным, с автоматической проверкой компилятором. Но я не мог устоять перед соблазном вставить пустую ссылку просто потому, что это было так легко реализовать. Это привело к бесчисленным ошибкам, уязвимостям и системным сбоям, которые, вероятно, причинили боль и ущерб на миллиард долларов за последние сорок лет.
Смотрите также
- Отладчик памяти
- Нулевая страница
Рекомендации
Цитаты
- ^ ISO / IEC 9899 , пункт 6.3.2.3, пункт 4.
- ^ ISO / IEC 9899 , пункт 7.17, пункт 3: NULL ... который расширяется до константы нулевого указателя, определяемой реализацией ...
- ^ ISO / IEC 9899 , пункт 6.3.2.3, пункт 3.
- ^ a b ISO / IEC 9899 , пункт 6.5.3.2, пункт 4, особенно. сноска 87.
- ^ Страуструп, Бьярне (март 2001). «Глава 5: классификатор (§5.4) предотвращает случайное переопределение и гарантирует , что можно использовать там , где требуется постоянная.». Язык программирования C ++ (14-е издание 3-го изд.). США и Канада: Аддисон – Уэсли. п. 88 . ISBN
const
NULL
NULL
0-201-88954-4. CS1 maint: обескураженный параметр ( ссылка ) - ^ Язык программирования Objective-C 2.0 , раздел «Отправка сообщений на ноль» .
- ^ Разыменование NULL-указателя уязвимого ядра OS X в AppleGraphicsDeviceControl
- ^ а б Бонд, Майкл Д .; Nethercote, Николас; Кент, Стивен У .; Guyer, Samuel Z .; МакКинли, Кэтрин С. (2007). «Отслеживание плохих яблок»: 405. doi : 10.1145 / 1297027.1297057 . Цитировать журнал требует
|journal=
( помощь ) - ^ а б Корню, Бенуа; Barr, Earl T .; Сейнтюрье, Лайонел; Монперрус, Мартин (2016). «Casper: Автоматическое отслеживание нулевых разыменований на начало со следами причинности» . Журнал систем и программного обеспечения . 122 : 52–62. DOI : 10.1016 / j.jss.2016.08.062 . ISSN 0164-1212 .
- ^ Дюрье, Томас; Корню, Бенуа; Сейнтюрье, Лайонел; Монперрус, Мартин (2017). «Динамическое создание исправлений для исключений с нулевым указателем с использованием метапрограммирования» (PDF) . 24-я Международная конференция по анализу, эволюции и реинжинирингу программного обеспечения, IEEE, 2017 (SANER) . IEEE: 349–358. arXiv : 1812.00409 . DOI : 10,1109 / SANER.2017.7884635 . ISBN 978-1-5090-5501-2.
- ^ Тони Хоар (2009-08-25). «Нулевые ссылки: ошибка в миллиард долларов» . InfoQ.com.
Источники
- Объединенный технический комитет ISO / IEC JTC 1, Подкомитет SC 22, Рабочая группа WG 14 (2007-09-08). Международный стандарт ISO / IEC 9899 (PDF) (проект комитета).CS1 maint: несколько имен: список авторов ( ссылка )