В C ++ 03 нулевой указатель был определен в спецификации ISO (& sect; 4.10 / 1) как
Константа нулевого указателя - это целочисленное константное выражение (5.19) r целого типа с нулевым значением.
Вот почему в C ++ вы можете написать
int* ptr = 0;
В C это правило похоже, но немного отличается (& sect; 6.3.2.3/3):
Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу
void *
, называется константой нулевого указателя.55) Если константа нулевого указателя преобразуется в
тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнивает неравные
на указатель на любой объект или функцию.
Следовательно, оба
int* ptr = 0;
и
int* ptr = (void *)0
являются законными. Тем не менее, я предполагаю, что приведение void*
здесь, так что такие утверждения, как
int x = NULL;
выдает предупреждение компилятора на большинстве систем. В C ++ это было бы недопустимо, потому что вы не можете неявно преобразовать void*
в другой тип указателя неявно без приведения. Например, это незаконно:
int* ptr = (void*)0; // Legal C, illegal C++
Однако это приводит к проблемам, потому что код
int x = NULL;
является законным C ++. Из-за этого и последовавшей за этим путаницы (и другого случая, показанного ниже), начиная с C ++ 11, существует ключевое слово nullptr
, представляющее нулевой указатель:
int* ptr = nullptr;
С этими проблемами не сталкиваются.
Другое преимущество nullptr
перед 0 состоит в том, что он лучше работает с системой типов C ++. Например, предположим, у меня есть эти две функции:
void DoSomething(int x);
void DoSomething(char* x);
Если я позвоню
DoSomething(NULL);
Это эквивалентно
DoSomething(0);
, который вызывает DoSomething(int)
вместо ожидаемого DoSomething(char*)
. Тем не менее, с nullptr
я мог бы написать
DoSomething(nullptr);
И он будет вызывать функцию DoSomething(char*)
, как и ожидалось.
Аналогично, предположим, что у меня есть vector<Object*>
и я хочу установить для каждого элемента нулевой указатель. Используя алгоритм std::fill
, я мог бы попробовать написать
std::fill(v.begin(), v.end(), NULL);
Однако, это не компилируется, потому что система шаблонов обрабатывает NULL
как int
, а не как указатель. Чтобы это исправить, мне нужно написать
std::fill(v.begin(), v.end(), (Object*)NULL);
Это некрасиво и несколько противоречит цели системы шаблонов. Чтобы это исправить, я могу использовать nullptr
:
std::fill(v.begin(), v.end(), nullptr);
И поскольку известно, что nullptr
имеет тип, соответствующий нулевому указателю (в частности, std::nullptr_t
), он будет скомпилирован правильно.
Надеюсь, это поможет!