Ответ 1
Вернувшись в С++ 03, нулевой указатель был определен спецификацией ISO (& sect; 4.10/1) как
Константа нулевого указателя является интегральным постоянным выражением (5.19) rvalue целочисленного типа, который вычисляется до нуля.
Вот почему в С++ вы можете написать
int* ptr = 0;
В C это правило аналогично, но немного отличается (& sect; 6.3.2.3/3):
Целочисленное константное выражение со значением 0 или такое выражение, отлитое для типа
void *
, называется константой нулевого указателя .55) Если константа нулевого указателя преобразуется в тип указателя, полученный указатель, называемый нулевым указателем, гарантированно сравнивает неравные к указателю на любой объект или функцию.
Следовательно, оба
int* ptr = 0;
и
int* ptr = (void *)0
являются законными. Тем не менее, я предполагаю, что приведение void*
выполняется таким образом, чтобы такие выражения, как
int x = NULL;
выдает предупреждение компилятора для большинства систем. В С++ это не будет законным, потому что вы не можете неявно преобразовывать void*
в другой тип указателя неявно без приведения. Например, это незаконно:
int* ptr = (void*)0; // Legal C, illegal C++
Однако это приводит к проблемам, потому что код
int x = NULL;
является законным С++. Из-за этого и последующего путаницы (и другого случая, показанного ниже), поскольку С++ 11, есть ключевое слово nullptr
, представляющее нулевой указатель:
int* ptr = nullptr;
У этого нет никаких проблем.
Другим преимуществом nullptr
over 0 является то, что он лучше работает с системой типа С++. Например, предположим, что у меня есть две функции:
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
), это будет правильно компилироваться.
Надеюсь, это поможет!