Используете ли вы NULL или 0 (ноль) для указателей на С++?
В первые дни С++, когда он был прикреплен болтами поверх C, вы не могли использовать NULL, поскольку он был определен как (void*)0
. Вы не могли назначить NULL любому указателю, отличному от void*
, что сделало его бесполезным. В те дни было принято, что для нулевых указателей вы использовали 0
(ноль).
До сих пор я продолжал использовать нуль в качестве нулевого указателя, но те, кто вокруг меня, настаивают на использовании NULL
. Я лично не вижу никакой пользы от того, чтобы дать имя (NULL
) существующему значению - и так как мне также нравится тестировать указатели как значения истинности:
if (p && !q)
do_something();
тогда использование нуля имеет больше смысла (как в случае, если вы используете NULL
, вы не можете логически использовать p && !q
- вам нужно явно сравнивать с NULL
, если вы не предполагаете, что NULL
равно нулю, и в этом случае используйте NULL
).
Есть ли какие-либо объективные причины, чтобы предпочесть ноль по NULL (или наоборот), или это все личные предпочтения?
Изменить: я должен добавить (и должен был изначально сказать), что с RAII и исключениями, я редко использую указатели с нулевым /NULL, но иногда они вам все еще нужны.
Ответы
Ответ 1
Здесь Stroustrup возьмет на себя следующее: Часто задаваемые вопросы по стилю и технике С++
В С++ определение NULL
равно 0, поэтому существует только эстетическая разница. Я предпочитаю избегать макросов, поэтому я использую 0. Еще одна проблема с NULL
заключается в том, что люди иногда ошибочно полагают, что они отличаются от 0 и/или не целого. В предстандартном коде NULL
был/иногда определен чем-то неподходящим, и поэтому его/его следует избегать. Это менее распространено в наши дни.
Если вам нужно называть нулевой указатель, назовите его nullptr
; что то, что он назвал в С++ 11. Тогда nullptr
будет ключевым словом.
Тем не менее, не потейте мелкие вещи.
Ответ 2
Есть несколько аргументов (один из которых относительно недавно), которые, я считаю, противоречат позиции Бьярна на этом.
- Документация о намерениях
Использование NULL
позволяет использовать поисковые запросы, а также подчеркивает, что разработчик хотел использовать указатель NULL
, независимо от того, интерпретируется ли он компилятором как NULL
или нет.
- Перегрузка указателя и 'int' относительно редка
Пример, который цитируются всеми:
void foo(int*);
void foo (int);
void bar() {
foo (NULL); // Calls 'foo(int)'
}
Однако, по крайней мере, по-моему, проблема с вышеизложенным заключается не в том, что мы используем NULL для константы нулевого указателя, это значит, что у нас есть перегрузки 'foo', которые принимают очень разные аргументы. Параметр должен быть также int
, так как любой другой тип приведет к двусмысленному вызову и поэтому генерирует полезное предупреждение компилятора.
- Инструменты анализа могут помочь СЕГОДНЯ!
Даже в отсутствие С++ 0x сегодня доступны инструменты, которые проверяют, что NULL
используется для указателей, и что 0
используется для целых типов.
- С++ 11 будет иметь новый тип
std::nullptr_t
.
Это самый новый аргумент в таблице. Проблема 0
и NULL
активно решается для С++ 0x, и вы можете гарантировать, что для каждой реализации, которая предоставляет NULL
, самое первое, что они будут делать:
#define NULL nullptr
Для тех, кто использует NULL
, а не 0
, это изменение будет улучшением безопасности типа с небольшим или никаким усилием - если что-нибудь может также уловить несколько ошибок, в которых они использовали NULL
для 0
. Для тех, кто использует 0
сегодня.... erm... ну, надеюсь, у них есть хорошее знание регулярных выражений...
Ответ 3
Используйте NULL. NULL показывает ваше намерение. То, что это 0, является деталью реализации, которая не имеет значения.
Ответ 4
Я прекратил использовать NULL в пользу 0 уже давно (как и большинство других макросов). Я сделал это не только потому, что я хотел как можно больше избегать макросов, но также потому, что NULL, похоже, слишком сильно используется в коде C и С++. Кажется, он используется всякий раз, когда требуется значение 0, а не только для указателей.
В новых проектах я помещаю это в заголовок проекта:
static const int nullptr = 0;
Теперь, когда приходят компиляторы, совместимые с С++ 0x, все, что мне нужно сделать, это удалить эту строку.
Хорошим преимуществом этого является то, что Visual Studio уже распознает nullptr как ключевое слово и выделяет его соответствующим образом.
Ответ 5
Я всегда использую:
NULL for pointers
'\0' for chars
0.0 for floats and doubles
где 0 будет хорошо. Это вопрос сигнализации. Тем не менее, я не анал об этом.
Ответ 6
cerr << sizeof(0) << endl;
cerr << sizeof(NULL) << endl;
cerr << sizeof(void*) << endl;
============
On a 64-bit gcc RHEL platform you get:
4
8
8
================
Мораль истории. Вы должны использовать NULL, когда имеете дело с указателями.
1) Он заявляет о своем намерении (не заставляйте меня искать весь код, пытаясь выяснить, является ли переменная указателем или некоторым числовым типом).
2) В некоторых вызовах API, которые ожидают переменные аргументы, они будут использовать указатель NULL для указания конца списка аргументов. В этом случае использование "0" вместо NULL может вызвать проблемы. На 64-битной платформе вызов va_arg требует 64-битного указателя, но вы будете передавать только 32-битное целое число. Кажется мне, как вы полагаетесь на другие 32-битные, чтобы вас обнулили? Я видел некоторые компиляторы (например, Intel icpc), которые не так любезны - и это привело к ошибкам во время выполнения.
Ответ 7
Если я правильно помню, NULL определяется по-разному в заголовках, которые я использовал. Для C он определяется как (void *) 0, а для С++ он определяется как 0. Код выглядел примерно так:
#ifndef __cplusplus
#define NULL (void*)0
#else
#define NULL 0
#endif
Лично я все еще использую значение NULL для представления нулевых указателей, оно делает его явным, что вы используете указатель, а не какой-то целостный тип. Да внутренне значение NULL равно 0, но оно не представлено как таковое.
Кроме того, я не полагаюсь на автоматическое преобразование целых чисел в логические значения, но явно сравниваю их.
Например, предпочитаете использовать:
if (pointer_value != NULL || integer_value == 0)
а не:
if (pointer_value || !integer_value)
Достаточно сказать, что все это исправлено в С++ 11, где можно просто использовать nullptr
вместо NULL
, а также nullptr_t
, который является типом nullptr
.
Ответ 8
Я бы сказал, что история говорила, и те, кто выступал за использование 0 (ноль), ошибались (в том числе Бьярне Страуструп). Аргументы в пользу 0 были в основном эстетикой и "личными предпочтениями".
После создания С++ 11 с его новым типом nullptr некоторые компиляторы начали жаловаться (с параметрами по умолчанию) о передаче 0 в функции с аргументами указателя, потому что 0 не является указателем.
Если код был написан с использованием NULL, простой простой поиск и замена могли быть выполнены через кодовую базу, чтобы вместо этого сделать nullptr. Если вы застряли с кодом, написанным с использованием 0 в качестве указателя, гораздо более утомительно его обновить.
И если вам нужно написать новый код прямо сейчас до стандарта С++ 03 (и не может использовать nullptr), вам действительно нужно просто использовать NULL. Это облегчит вам обновление в будущем.
Ответ 9
Я когда-то работал на машине, где 0 был действительным адресом, а NULL определялся как специальное восьмеричное значение. На этой машине (0!= NULL), поэтому код, например
char *p;
...
if (p) { ... }
не будет работать так, как вы ожидаете. Вы должны были написать
if (p != NULL) { ... }
Хотя я считаю, что большинство компиляторов определяют NULL как 0 в эти дни, я до сих пор помню урок из тех лет назад: NULL не обязательно 0.
Ответ 10
Я обычно использую 0. Мне не нравятся макросы, и нет гарантии, что какой-то третий заголовок, который вы используете, не переопределяет NULL как нечто странное.
Вы можете использовать объект nullptr, предложенный Скоттом Мейерсом и другими, до тех пор, пока С++ не получит ключевое слово nullptr:
const // It is a const object...
class nullptr_t
{
public:
template<class T>
operator T*() const // convertible to any type of null non-member pointer...
{ return 0; }
template<class C, class T>
operator T C::*() const // or any type of null member pointer...
{ return 0; }
private:
void operator&() const; // Can't take address of nullptr
} nullptr = {};
Google "nullptr" для получения дополнительной информации.
Ответ 11
Я думаю, что стандарт гарантирует, что NULL == 0, так что вы можете это сделать. Я предпочитаю NULL, потому что он документирует ваши намерения.
Ответ 12
Использование 0 или NULL будет иметь тот же эффект.
Однако это не означает, что они являются хорошей практикой программирования. Учитывая, что нет никакой разницы в производительности, выбор варианта с низким уровнем осведомленности по альтернативной/абстрактной альтернативе - плохая практика программирования. Помогите читателям вашего кода понять ваш мыслительный процесс.
NULL, 0, 0.0, '\ 0', 0x00 и whatelse все переводятся на одно и то же, но разные логические сущности в вашей программе. Они должны использоваться как таковые. NULL - это указатель, 0 - количество, 0x0 - это значение, чьи биты интересны и т.д. Вы бы не назначили '\ 0' указателю, компилируется он или нет.
Я знаю, что некоторые сообщества поощряют демонстрацию глубоких знаний об окружающей среде, нарушая контракты на окружающую среду. Однако ответственные программисты делают код, поддерживающий код, и сохраняют такую практику из своего кода.
Ответ 13
Странно, никто, включая Stroustroup, не упоминал об этом. Говоря много о стандартах и эстетике, никто не заметил, что опасно использовать 0
в NULL
stead, например, в списке переменных аргументов в архитектуре, где sizeof(int) != sizeof(void*)
. Как и Stroustroup, я предпочитаю 0
по эстетическим соображениям, но нужно быть осторожным, чтобы не использовать его там, где его тип может быть неоднозначным.
Ответ 14
Я стараюсь избегать всего вопроса, используя ссылки на С++, где это возможно. Вместо
void foo(const Bar* pBar) { ... }
вы часто можете написать
void foo(const Bar& bar) { ... }
Конечно, это не всегда работает; но нулевые указатели могут быть чрезмерными.
Ответ 15
Я с Страуступом на этом:-)
Поскольку NULL не является частью языка, я предпочитаю использовать 0.
Ответ 16
Я предпочитаю использовать NULL, поскольку ясно, что ваше намерение - это значение, представляющее указатель, а не арифметическое значение. Тот факт, что это макрос, является неудачным, но так как он настолько сильно укоренился там, что мало опасности (если кто-то не делает что-то действительно пронзительное). Я бы хотел, чтобы это было ключевое слово с самого начала, но что вы можете сделать?
Тем не менее, у меня нет проблем с использованием указателей как значений истинности в себе. Как и в случае с NULL, это укоренившаяся идиома.
С++ 09 добавит конструкцию nullptr, которая, как мне кажется, давно назрела.
Ответ 17
В основном личные предпочтения, хотя можно сделать аргумент, что NULL делает совершенно очевидным, что объект является указателем, который в настоящее время не указывает ни на что, например.
void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it a pointer and not being used
IIRC, стандарт не требует, чтобы NULL равнялся 0, поэтому используя все, что определено в < stddef.h > вероятно, лучше всего подходит для вашего компилятора.
Еще один аспект аргумента заключается в том, следует ли использовать логические сравнения (неявное преобразование в bool) или проверку на явное сходство с NULL, но это также сводится к читаемости.
Ответ 18
Я всегда использую 0. Не для какой-либо реальной мысли, потому что, когда я впервые изучал C++, я читал то, что рекомендовал использовать 0, и я всегда делал это именно так. В теории может быть проблема путаницы в удобочитаемости, но на практике я никогда не сталкивался с такой проблемой в тысячах человеко-часов и в миллионах строк кода. Как говорит Страуступ, это действительно просто личная эстетическая проблема, пока стандарт не станет nullptr.
Ответ 19
Кто-то сказал мне однажды... Я собираюсь переопределить NULL до 69. С тех пор я не использую его: P
Это делает ваш код очень уязвимым.
Edit:
Не все в стандарте идеально. Макрос NULL представляет собой константу константы нулевого указателя С++, не полностью совместимую с макросом C NULL, что, кроме того, что тип скрывает неявное преобразование его в бесполезный и подверженный ошибкам инструмент.
NULL не ведет себя как нулевой указатель, а как литерал O/OL.
Скажите, что следующий пример не путает:
void foo(char *);
void foo(int);
foo(NULL); // calls int version instead of pointer version!
Из-за всего этого в новом стандарте появляется std:: nullptr_t
Если вы не хотите ждать нового стандарта и хотите использовать nullptr, используйте по крайней мере достойный, как предложенный Meyers (см. комментарий jon.h).
Ответ 20
Хорошо, я утверждаю, что не использовал 0 или NULL-указатели, когда это было возможно.
Использование их рано или поздно приведет к ошибкам сегментации в вашем коде. По моему опыту это, и указатели в gereral - один из самых больших источников ошибок в С++
также, это приводит к операторам "if-not-null" по всему вашему коду. Гораздо приятнее, если вы можете положиться на всегда действующее состояние.
Существует почти всегда лучшая альтернатива.
Ответ 21
Установка указателя на 0 просто не понятна. Особенно если вы придете на язык, отличный от С++. Это включает в себя C, а также Javascript.
Недавно я использовал некоторый код:
virtual void DrawTo(BITMAP *buffer) =0;
для чистой виртуальной функции в первый раз. Я думал, что это какая-то магия jiberjash в течение недели. Когда я понял, что это просто установка указателя функции на null
(поскольку в большинстве случаев виртуальные функции являются указателями на объекты в большинстве случаев для С++), я пнул себя.
virtual void DrawTo(BITMAP *buffer) =null;
было бы менее запутанным, чем это расторжение без надлежащего расстояния до моих новых глаз. На самом деле, мне интересно, почему С++ не использует строчные буквы null
, так как теперь он использует нижний регистр false и true.