Используете ли вы NULL или 0 (ноль) для указателей в C++?

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

По сей день я продолжаю использовать ноль в качестве нулевого указателя, но окружающие меня настаивают на использовании NULL, Я лично не вижу никакой пользы в названии (NULL) к существующему значению - и так как я также люблю тестировать указатели как значения истинности:

if (p && !q)
  do_something();

тогда использование нуля имеет больше смысла (как если бы вы использовали NULL, вы не можете логически использовать p && !q - вам нужно явно сравнить с NULL, если вы не предполагаете NULL равен нулю, и в этом случае зачем NULL).

Есть ли объективная причина предпочесть ноль, а не NULL (или наоборот), или это всего лишь личные предпочтения?

Редактировать: я должен добавить (и изначально хотел сказать), что с RAII и исключениями я редко использую указатели с нулевым /NULL, но иногда они вам все же нужны.

21 ответ

Решение

Вот что говорит Страуструп: FAQ по стилю и технике C++

В C++ определение NULL 0, так что есть только эстетическая разница. Я предпочитаю избегать макросов, поэтому я использую 0. Еще одна проблема с NULL в том, что люди иногда ошибочно полагают, что оно отличается от 0 и / или не является целым числом. В стандартном коде, NULL был / иногда определяется как нечто неподходящее и поэтому его / нужно избегать. Это менее распространено в наши дни.

Если вам нужно назвать нулевой указатель, назовите его nullptr; это то, что называется в C++11. Затем, nullptr будет ключевым словом.

Тем не менее, не парься по мелочи.

Есть несколько аргументов (один из которых является относительно недавним), которые, как мне кажется, противоречат позиции Бьярне по этому вопросу.

  1. Документация о намерениях

С помощью NULL позволяет искать его использование, а также подчеркивает, что разработчик хотел использовать NULL указатель, независимо от того, интерпретируется ли он компилятором как NULL или нет.

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

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

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

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

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

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

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

  1. C++ 11 будет иметь новый std::nullptr_t тип.

Это новейший аргумент таблицы. Проблема 0 а также NULL активно используется для C++ 0x, и вы можете гарантировать, что для каждой реализации, которая обеспечивает NULL, самое первое, что они сделают, это:

#define NULL  nullptr

Для тех, кто использует NULL скорее, чем 0 изменение будет улучшением безопасности типов с минимальными усилиями или без них - во всяком случае, это также может отловить несколько ошибок, которые они использовали NULL за 0, Для тех, кто использует 0 сегодня.... хм... ну, надеюсь, они хорошо знают регулярные выражения...

Используйте NULL. NULL показывает ваше намерение. То, что это 0 - это деталь реализации, которая не должна иметь значения.

Я всегда использую:

  • NULL для указателей
  • '\0' для персонажей
  • 0.0 для поплавков и парных разрядов

где 0 будет хорошо. Это вопрос сигнализации намерения. Тем не менее, я не анальный об этом.

Я давно перестал использовать NULL в пользу 0 (как и большинство других макросов). Я сделал это не только потому, что хотел как можно больше избегать макросов, но и потому, что NULL, похоже, стал чрезмерно использоваться в коде C и C++. Кажется, он используется всякий раз, когда требуется значение 0, а не только для указателей.

На новых проектах я помещаю это в заголовок проекта:

static const int nullptr = 0;

Теперь, когда приходят компиляторы, совместимые с C++0x, все, что мне нужно сделать, это удалить эту строку. Приятным преимуществом этого является то, что Visual Studio уже распознает nullptr в качестве ключевого слова и выделяет его соответствующим образом.

    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-битные, которые обнуляются для тебя? Я видел некоторые компиляторы (например, icpc от Intel), которые не так хороши - и это приводило к ошибкам во время выполнения.

Я бы сказал, что история говорила, и те, кто высказывался в пользу использования 0 (ноль), ошибались (включая Бьярне Страуструпа). Аргументы в пользу 0 были в основном эстетическими и "личными предпочтениями".

После создания C++11 с его новым типом nullptr некоторые компиляторы начали жаловаться (с параметрами по умолчанию) на передачу 0 функциям с аргументами-указателями, поскольку 0 не является указателем.

Если бы код был написан с использованием NULL, можно было бы выполнить простой поиск и замену по базе кода, чтобы вместо него сделать его nullptr. Если вы застряли с кодом, написанным с использованием выбора 0 в качестве указателя, его гораздо утомительнее обновить.

И если вам нужно прямо сейчас написать новый код для стандарта C++03 (и вы не можете использовать nullptr), вам действительно следует просто использовать NULL. В будущем вам будет намного проще обновляться.

Если я правильно помню, NULL определяется по-разному в заголовках, которые я использовал. Для C это определяется как (void*)0, а для C++ это определяется как просто 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)

Достаточно сказать, что это все исправлено в C++11, где можно просто использовать nullptr вместо NULL, а также nullptr_t это тип nullptr,

Я обычно использую 0. Мне не нравятся макросы, и нет никакой гарантии, что какой-то сторонний заголовок, который вы используете, не определяет NULL как нечто странное.

Вы можете использовать объект nullptr, предложенный Скоттом Мейерсом и другими, пока C++ не получит ключевое слово 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" для получения дополнительной информации.

Однажды я работал на машине, где 0 был действительным адресом, а NULL был определен как специальное восьмеричное значение. На этой машине (0!= NULL), поэтому такой код, как

char *p;

...

if (p) { ... }

не будет работать, как вы ожидаете. Вы должны были написать

if (p != NULL) { ... }

Хотя я полагаю, что большинство компиляторов определяют NULL как 0 в эти дни, я все еще помню урок тех лет назад: NULL не обязательно равен 0.

Использование 0 или NULL будет иметь тот же эффект.

Однако это не означает, что они оба являются хорошими практиками программирования. Принимая во внимание, что нет разницы в производительности, выбор низкоуровневого варианта вместо независимой / абстрактной альтернативы является плохой практикой программирования. Помогите читателям вашего кода понять ваш мыслительный процесс.

NULL, 0, 0.0, '\0', 0x00 и все остальные означают одно и то же, но являются разными логическими объектами в вашей программе. Они должны быть использованы как таковые. NULL - это указатель, 0 - это количество, 0x0 - это значение, чьи биты интересны и т. Д. Вы не назначали бы '\0' для указателя, независимо от того, компилируется он или нет.

Я знаю, что некоторые сообщества поощряют демонстрацию глубоких знаний об окружающей среде, нарушая условия окружающей среды. Ответственные программисты, однако, создают поддерживаемый код и не допускают такой практики в свой код.

Я думаю, что стандарт гарантирует, что NULL == 0, так что вы можете сделать либо. Я предпочитаю NULL, потому что это документирует ваши намерения.

Странно, никто, включая Страуструп, не упомянул это. Говоря много о стандартах и ​​эстетике, никто не заметил, что это опасно для использования 0 в NULLвместо, например, в списке переменных аргументов в архитектуре, где sizeof(int) != sizeof(void*), Как и Страуструп, я предпочитаю 0 по эстетическим соображениям, но нужно быть осторожным, чтобы не использовать его там, где его тип может быть неоднозначным.

Я пытаюсь избежать всего вопроса, используя ссылки на C++, где это возможно. Скорее, чем

void foo(const Bar* pBar) { ... }

Вы часто можете писать

void foo(const Bar& bar) { ... }

Конечно, это не всегда работает; но нулевые указатели могут быть чрезмерно использованы.

В основном личные предпочтения, хотя можно привести аргумент, что NULL делает совершенно очевидным, что объект является указателем, который в настоящее время ни на что не указывает, например

void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used

IIRC, стандарт не требует, чтобы NULL был равен 0, поэтому использование того, что определено в , вероятно, лучше всего подходит для вашего компилятора.

Другим аспектом аргумента является то, следует ли использовать логические сравнения (неявное приведение к bool) или проверку чистоты по отношению к NULL, но это также сводится к удобочитаемости.

Я предпочитаю использовать NULL, поскольку это ясно, что ваше намерение - это значение, представляющее указатель, а не арифметическое значение. Тот факт, что это макрос, вызывает сожаление, но, поскольку он так широко укоренился, нет никакой опасности (если кто-то не делает что-то действительно сумасшедшее). Хотелось бы, чтобы это было ключевое слово с самого начала, но что вы можете сделать?

Тем не менее, у меня нет проблем с использованием указателей в качестве истинных ценностей самих по себе. Как и в случае с NULL, это укоренившаяся идиома.

C++ 09 добавит конструкцию nullptr, которая, я думаю, давно назрела.

На этом я со Страуструпом:-) Так как NULL не является частью языка, я предпочитаю использовать 0.

Я всегда использую 0. Не по какой-либо реальной причине, просто когда я впервые изучал C++, я прочитал что-то, что рекомендовало использовать 0, и я всегда делал это именно так. В теории может возникнуть путаница с удобочитаемостью, но на практике я ни разу не сталкивался с такой проблемой в тысячах человеко-часов и миллионах строк кода. Как говорит Страуструп, на самом деле это вопрос личной эстетики, пока стандартом не станет nullptr.

Кто-то сказал мне однажды... Я собираюсь переопределить NULL на 69. С тех пор я не использую его:P

Это делает ваш код довольно уязвимым.

Редактировать:

Не все в стандарте идеально. Макрос NULL - это определяемая реализацией константа нулевого указателя C++, не полностью совместимая с макросом C NULL, которая, кроме скрытого типа, неявно преобразует его в бесполезный и подверженный ошибкам инструмент.

NULL ведет себя не как нулевой указатель, а как литерал O/OL.

Подскажите следующий пример не смущает:

void foo(char *); 
void foo(int); 
foo(NULL); // calls int version instead of pointer version! 

Из-за всего этого в новом стандарте появляется std::nullptr_t

Если вы не хотите ждать нового стандарта и хотите использовать nullptr, используйте хотя бы приличный, такой как предложенный Мейерсом (см. Комментарий jon.h).

Ну, я утверждаю, что вообще не использую указатели 0 или NULL, когда это возможно.

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

также, это приводит к операторам if-not-null во всем вашем коде. Намного приятнее, если вы всегда можете положиться на действительное состояние.

Почти всегда есть лучшая альтернатива.

Исходя из моих знаний, это хорошо для предпочтения nullptr так как 0 является int а также NULL является 0L (длинный тип), если у вас есть три разные функции перегрузки

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

потом звоню foo(0) позвонить int один не указатель а звонящий foo(NULL) может быть неоднозначным для компилятора, потому что длинный тип может преобразовать в int а также bool а также pointer без проблем, так как компилятор знает, какую функцию вы вызываете.

это пример ситуации, которую вы должны учитывать, что не используйте 0 и NULL в качестве nullptr, но если вы используете 0 а также NULL в контексте, который компилятор может интерпретировать как указатель, он это сделает.

Установка указателя на 0 не совсем понятна. Особенно если вы приехали на языке, отличном от C++. Это включает в себя C, а также Javascript.

Я недавно разобрался с некоторым кодом:

virtual void DrawTo(BITMAP *buffer) =0;

для чисто виртуальной функции в первый раз. Я думал, что это будет волшебная спячка на неделю. Когда я понял, что это просто в основном установка указателя функции на null (поскольку виртуальные функции - это просто указатели функций в большинстве случаев для C++), я пнул себя.

virtual void DrawTo(BITMAP *buffer) =null;

было бы менее запутанным, чем это взволнование, без должного расстояния между моими новыми глазами. На самом деле, мне интересно, почему C++ не использует строчные null так же, как он использует строчные ложные и правда сейчас.

Другие вопросы по тегам