Введение
Значение, указывающее на то, что ссылаемый набор данных недействителен или не существует.
В вычислительной технике нулевой указатель или нулевая ссылка — это значение, используемое для обозначения того, что указатель или ссылка не указывает на допустимый объект. Программы часто используют нулевые указатели для представления таких ситуаций, как конец списка неизвестной длины или невозможность выполнения какого-либо действия; такое использование нулевых указателей можно сравнить с допускающими значения null и значением Nothing в типе Option. Нулевой указатель не следует путать с неинициализированным указателем: гарантируется, что нулевой указатель при сравнении не равен ни одному указателю, указывающему на допустимый объект. Однако, как правило, большинство языков не предоставляют такой гарантии. Он может оказаться равным другим допустимым указателям; или равным другим нулевым указателям. Он может делать и то, и другое в разное время; или сравнение может привести к неопределенному поведению. Кроме того, в языках, поддерживающих такую возможность, правильное использование зависит от опыта каждого разработчика и инструментов статического анализа. Даже при правильном использовании нулевые указатели семантически неполны, поскольку они не позволяют выразить разницу между значениями "Неприменимо", "Неизвестно" или "Будущее". Поскольку нулевой указатель не указывает на значимый объект, попытка доступа к данным, хранящимся по этому (недействительному) адресу памяти, может вызвать ошибку во время выполнения или немедленный сбой программы. Это ошибка нулевого указателя. Это один из самых распространенных типов уязвимостей программного обеспечения, и Тони Хоар, который ввел эту концепцию, назвал её «ошибкой на миллиард долларов».
С
В C два нулевых указателя любого типа гарантированно сравниваются как равные. Макрос препроцессора NULL определяется как константа нулевого указателя, зависящая от реализации, в <stdlib.h>, которую в C99 можно переносимо представить как ((void *)0) – целочисленное значение 0, преобразованное в тип void* (см. указатель на тип void). Стандарт C не утверждает, что нулевой указатель идентичен указателю на адрес памяти 0, хотя это может иметь место на практике. Разыменование нулевого указателя является неопределённым поведением в C.
C++
В C++, хотя макрос NULL и был унаследован от C, целочисленный литерал 0 традиционно использовался для представления константы нулевого указателя. Однако в C++11 был представлен явный указатель на null – `nullptr` и тип `nullptr_t`, которые следует использовать вместо него.
Другие языки
В некоторых средах программирования (например, в одной проприетарной реализации Lisp) значение, используемое как нулевой указатель (обозначаемое как nil в Lisp), может на самом деле быть указателем на блок внутренних данных, полезных для реализации (но недоступных из пользовательских программ напрямую), что позволяет использовать один и тот же регистр как полезную константу и быстрый способ доступа к внутренним компонентам реализации. Это известно как нулевой вектор. В языках с помеченной архитектурой, потенциально нулевой указатель может быть заменен помеченным объединением, которое обеспечивает явную обработку исключительного случая; фактически, потенциально нулевой указатель можно рассматривать как помеченный указатель с вычисляемым тегом. Различные языки программирования используют разные литералы для обозначения нулевого указателя. Например, в Python нулевое значение называется None. В Pascal и Swift нулевой указатель называется nil. В Eiffel он называется void-ссылкой.
Ничтожная реферация
Поскольку нулевой указатель не указывает на значимый объект, попытка разыменования (т.е. доступа к данным, хранящимся по этому адресу памяти) нулевого указателя обычно (но не всегда) вызывает ошибку времени выполнения или немедленный сбой программы. MITRE включает ошибку нулевого указателя в список наиболее часто эксплуатируемых уязвимостей программного обеспечения. В языке C разыменование нулевого указателя является неопределённым поведением. Многие реализации приводят к остановке программы с нарушением доступа, поскольку представление нулевого указателя выбирается как адрес, который система никогда не выделяет для хранения объектов. Однако такое поведение не является универсальным. Оно также не гарантируется, поскольку компиляторам разрешено оптимизировать программы, предполагая, что они не содержат неопределённого поведения. В Delphi и многих других реализациях Pascal константа представляет собой нулевой указатель на первый адрес в памяти, который также используется для инициализации управляемых переменных. Разыменование этого указателя вызывает внешнее исключение операционной системы, которое отображается на экземпляр исключения Pascal, если модуль связан в секции uses. В Java доступ к нулевой ссылке вызывает исключение NullPointerException (NPE), которое можно перехватить с помощью кода обработки ошибок, но предпочтительной практикой является обеспечение того, чтобы такие исключения никогда не возникали. В Lisp является объектом первого класса. По соглашению, (first nil) возвращает , как и (rest nil). Таким образом, разыменование в этих контекстах не вызовет ошибку, но плохо написанный код может привести к бесконечному циклу. В .NET доступ к нулевой ссылке приводит к выбросу исключения ReferenceNullException. Хотя перехват таких исключений обычно считается плохой практикой, этот тип исключения можно перехватить и обработать программой. В Objective-C сообщения можно отправлять объекту (который является нулевым указателем) без прерывания программы; сообщение просто игнорируется, а возвращаемое значение (если оно есть) равно или , в зависимости от типа. До внедрения Supervisor Mode Access Prevention (SMAP) уязвимость, связанная с разыменованием нулевого указателя, могла быть использована путём отображения нулевой страницы в адресное пространство атакующего, что приводило к тому, что нулевой указатель указывал на эту область. В некоторых случаях это могло привести к выполнению кода.
Смягчение
Существуют методы, облегчающие отладку разыменований нулевых указателей. Бонд и др. D и Rust. Подобный анализ можно выполнять с использованием внешних инструментов в некоторых языках программирования.
Альтернативы нулевым указателям
Как правило, для каждого типа структуры или класса следует определять объекты, представляющие определенное состояние бизнес-логики, заменяющие неопределенное поведение при нулевом значении. Например, "future" для обозначения поля внутри структуры, которое пока недоступно (но заранее известно, что оно будет определено в будущем), "not applicable" для обозначения поля в ненормализованной структуре, "error" и "timeout" для обозначения того, что поле не удалось инициализировать (что, вероятно, приведет к остановке нормального выполнения программы, потока, запроса или команды).