Используете ли вы 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 или нет.

  1. Перегрузка указателя и 'int' относительно редка

Пример, который цитируются всеми:

void foo(int*);
void foo (int);

void bar() {
  foo (NULL);  // Calls 'foo(int)'
}

Однако, по крайней мере, по-моему, проблема с вышеизложенным заключается не в том, что мы используем NULL для константы нулевого указателя, это значит, что у нас есть перегрузки 'foo', которые принимают очень разные аргументы. Параметр должен быть также int, так как любой другой тип приведет к двусмысленному вызову и поэтому генерирует полезное предупреждение компилятора.

  1. Инструменты анализа могут помочь СЕГОДНЯ!

Даже в отсутствие С++ 0x сегодня доступны инструменты, которые проверяют, что NULL используется для указателей, и что 0 используется для целых типов.

  1. С++ 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.