NULL макрос или NULL const
В настоящее время я читаю книгу на С++, и в этой книге автор объясняет, что лучше использовать макрос константы, а не NULL
, но не объяснять причину или давать какие-либо преимущества или недостатки.
Так почему это лучше использовать:
const int NULL = 0;
int *ptr = NULL;
вместо:
int *ptr = NULL;
Единственное объяснение заключается в том, что макрос NULL
не является безопасным для типов.
Ответы
Ответ 1
Все устарели.
Используйте nullptr
вместо этого. Это особое значение для указателя, который не указывает ни на что. Он даже имеет свой собственный тип, std::nullptr_t
.
Нет гарантии, что адрес 0x0 соответствует значению "неинициализированного" указателя, но обратите внимание, что буквальное значение 0 гарантируется преобразованием в значение нулевого указателя.
Ответ 2
Если вы используете С++ 11, тогда рекомендуется использовать nullptr
вместо NULL.
Ниже приведены несколько строк из Язык программирования С++ по Bjarne Stroustrup
- В более раннем коде вместо nullptr обычно используется 0 или NULL (§7.2.2). Однако использование nullptr устраняет потенциальную путаницу между целыми числами (такими как 0 или NULL) и указателями (такими как nullptr).
- существуют различия в определении NULL в разных реализациях; например, NULL может быть 0 или 0L. В C NULL обычно (void *) 0, что делает его незаконным в С++ (§7.2.1):
- Использование nullptr делает код более читаемым, чем альтернативы, и позволяет избежать путаницы, когда функция перегружена, чтобы принять либо указатель, либо целое число
Надеюсь, это поможет вам понять.
Ответ 3
Автор прав, что макросы препроцессора не печатаются (в противоположность, например, переменным, где ввод текста применяется во время их объявления). Таким образом, макросы более опасны в этом отношении, так как компилятор не может проверить правильность текста в выражениях, и вы можете пропустить важное предупреждение/сообщение об ошибке во время компиляции
Конечно, создатели компилятора могут (и, как правило, делать, я надеюсь) предоставить версию NULL-макроса, которая расширяется до значения с литым, эквивалентным (не идентичным!) Определению C:
#define N ((void*)0)
BTW: поскольку С++ 11 NULL может оценивать тип std:: nullptr_t.
Поэтому я не ожидал бы слишком больших проблем с NULL, но вы можете избежать использования собственных макросов. Или, по крайней мере, используйте свои макросы с осторожностью, если вы не уверены, что можете предвидеть множество контекстов, в которых ваш макрос может расширяться в различных выражениях.
Для краткого упражнения вы можете попробовать следующее, чтобы увидеть, что макросы не имеют типа, поэтому их тип выводится в выражениях, с учетом, например, арифметические преобразования или рекламные акции. В приведенном ниже примере макросы MY_NULL (= без кастинга) приводят к довольно опасным назначениям. Это то, что имеет в виду ваш автор книг и пытается вас предупредить.
Макросы MY_TYPED оценивают кастированные выражения, что гарантирует, что компиляторы поймают ошибку при попытке, например. i = MY_TYPED_NULL;
#define MY_NULL 0
#define MY_TYPED_NULL ((void*)0)
int i;
float f;
void* v;
i = MY_NULL; // bad: no compiler error
f = MY_NULL; // bad: no compiler error
v = MY_NULL; // seems to match programmer intention
i = MY_TYPED_NULL; // good: compiler error
f = MY_TYPED_NULL; // good: compiler error
v = MY_TYPED_NULL; // correct
Ответ 4
Действительно, "NULL macro" не безопасен для типов. Это означает, что ваш компилятор не знает, используете ли вы правильный тип. Например, при использовании memcpy
:
SomeClass a;
AnotherClass b;
memcpy((void*)&a, (void*)&b, sizeof(b));
(взято из там)
Компилятор видит только два указателя в памяти. Но SomeClass
и AnotherClass
являются неполными типами.
Как говорили другие, если вы можете использовать С++ 11, просто используйте nullptr
.