Почему указатели NULL по-разному определяются в C и C++?
В С, NULL
определяется как (void *)0
тогда как в C++ это 0
, Почему это так? В C I можно понять, что если NULL
это не тип, чтобы (void *)
тогда компиляторы могут / не могут генерировать предупреждения. Кроме этого, есть ли причина?
3 ответа
Назад в C++03, нулевой указатель был определен спецификацией ISO (§4.10/1) как
Константа нулевого указателя - это целочисленное константное выражение (5.19) r целого типа, которое оценивается как ноль.
Вот почему в C++ вы можете написать
int* ptr = 0;
В Си это правило похоже, но немного отличается (§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
), это будет правильно скомпилировано.
Надеюсь это поможет!
В С, NULL
расширяется до определенной в реализации "константы нулевого указателя". Константа нулевого указателя является либо выражением целочисленной константы со значением 0, либо таким выражением, приведенным к void*
, Таким образом, реализация C может определить NULL
либо как 0
или как ((void*)0)
,
В C++ правила для констант нулевого указателя разные. Особенно, ((void*)0)
не является константой нулевого указателя C++, поэтому реализация C++ не может определить NULL
сюда.
Язык C был создан для облегчения программирования микропроцессоров. Указатель переменного тока используется для хранения адреса данных в памяти. Требовался способ показать, что указатель не имеет допустимого значения. Нулевой адрес был выбран, так как все микропроцессоры использовали этот адрес для загрузки. Поскольку его нельзя было использовать ни для чего другого, ноль был хорошим выбором для представления указателя без действительного значения. C++ обратно совместим с C, поэтому он унаследовал это соглашение.
Требование обнуления при использовании в качестве указателя является лишь недавним дополнением. Последующие поколения C хотели иметь больше строгости (и, надеюсь, меньше ошибок), поэтому они стали более педантичны в отношении синтаксиса.