К сожалению, литерал нулевого указателя является одной из запутанных частей языка. Позвольте мне повторить:
Для любого типа существует понятие «указатель на этот тип». Например, вы можете иметь целые числа и указатель на целые числа (int x; int *y;
), удваивать и указатель на двойные (double x; double *y;
), Персона и указатель на Персона (Person x,*y;
). Если X
является типом, то «указатель на X» является самим типом , и, следовательно, вы даже можете найти указатели на указатели на целые числа (int **x;
) или указатели на указатели на указатели на символы (char ***x;
).
Для любого типа указателя есть значение null pointer
. Это значение, которое на самом деле не указывает на объект, поэтому попытка разыменовать его является ошибкой («разыменование» указателя означает чтение или запись объекта, на который указывает указатель). Обратите внимание, что язык C ++ не гарантирует, что вы получите сообщение или сбой при использовании нулевого указателя для попытки добраться до несуществующего заостренного объекта, но только то, что вы не должны делать это в программе, потому что последствия непредсказуемы. Язык просто предполагает, что вы не собираетесь делать такого рода ошибки.
Как нулевой указатель выражается в программе? Здесь начинается сложная часть. По непонятным причинам язык C ++ использует странное правило: если вы получаете любое постоянное целочисленное выражение со значением ноль , то это можно (если необходимо) считать нулевым указателем для любого типа.
Последнее правило чрезвычайно странное и нелогичное, например, означает, что
char *x = 0; // x is a pointer to char with the null pointer value (ok)
char *y = (1-1); // exactly the same (what??)
char *z = !! !! !! !! !! !!
!!! !! !! !! !! !!
!!!! !! !! !! !! !!
!! !! !! !! !! !! !!
!! !!!! !! !! !! !!
!! !!! !! !! !! !!
!! !! !!!!!! !!!!!!! !!!!!!1; // Same again (!)
и это верно для любого типа указателя.
Почему стандарт предписывает, что любое выражение, а не просто нулевой литерал, может рассматриваться как значение нулевого указателя? На самом деле не знаю.
Очевидно, что Страуструп также нашел забавную вещь вместо того, чтобы быть отвратительной, какой она должна быть (последний пример с текстом «NULL», написанным с нечетным числом отрицаний, представлен в книге «Язык программирования C ++»).
Также обратите внимание, что в стандартных заголовках определен символ NULL
, который обеспечивает правильное определение значения нулевого указателя для любого типа. В «C» допустимое определение могло бы быть (void *)0
, но это недопустимо в C ++, потому что указатели void не могут быть неявно преобразованы в другие типы указателей, как они делают в «C».
Обратите внимание, что в литературе вы можете найти термин NUL
(только один L), но это символ ASCII с кодом 0 (представлен в C / C ++ с '\0'
) и является логически отличная вещь от указателя или целого числа.
К сожалению, в C ++ символы тоже являются целыми числами, и поэтому, например, '\0'
является допустимым значением нулевого указателя, то же самое относится и к ('A'-'A')
(это целочисленные константные выражения с нулевым значением).
C ++ 11 увеличивает сложность этих и без того сомнительных правил с std::nullptr_t
и nullptr
. Я не могу объяснить эти правила, потому что сам не понял их (и еще не уверен на 100%, что хочу их понять).