Подтвердить что ты не робот

Почему NULL-указатели различаются по-разному на C и С++?

В C, NULL определяется как (void *)0, тогда как в С++ это 0. Почему это так? В C я могу понять, что если NULL не является typecast для (void *), то компиляторы могут/не могут генерировать предупреждение. Кроме этого, есть ли причина?

4b9b3361

Ответ 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), это будет правильно компилироваться.

Надеюсь, это поможет!

Ответ 2

В C, NULL расширяется до константы нулевого указателя, определяемой реализацией. Константа нулевого указателя является либо целочисленным константным выражением со значением 0, либо выражением, выраженным в void*. Таким образом, реализация C может определять NULL либо как 0, либо как ((void*)0).

В С++ правила для констант нулевого указателя различны. В частности, ((void*)0) не является константой нулевого указателя С++, поэтому реализация С++ не может определить NULL таким образом.

Ответ 3

Язык C был создан для упрощения программирования микропроцессоров. Указатель C используется для хранения адреса данных в памяти. Необходимо было указать, что указатель не имеет действительного значения. Нулевой адрес был выбран, поскольку все микропроцессоры использовали этот адрес для загрузки. Поскольку он не может быть использован ни для чего другого, ноль был хорошим выбором для представления указателя без допустимого значения. С++ обратно совместим с C, поэтому он унаследовал это соглашение.

Требование лить ноль при использовании в качестве указателя - это только недавнее добавление. Позже поколения C хотели иметь более строгую (и, надеюсь, меньше ошибок), чтобы они стали более педантичными в отношении синтаксиса.