Почему указатели NULL по-разному определяются в C и C ++? - PullRequest
70 голосов
/ 10 августа 2011

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

Ответы [ 3 ]

101 голосов
/ 10 августа 2011

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

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

16 голосов
/ 11 августа 2011

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

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

0 голосов
/ 11 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...