Используете ли вы NULL или 0 (ноль) для указателей в C ++?
Вопрос
В первые дни C ++, когда он был прикреплен поверх C, вы не могли использовать NULL, поскольку он был определен как (void*)0
.Вы не могли бы присвоить значение NULL какому-либо указателю, отличному от void*
, что делало его в некотором роде бесполезным.В те дни было принято, что вы использовали 0
(ноль) для нулевых указателей.
По сей день я продолжаю использовать ноль в качестве нулевого указателя, но окружающие меня настаивают на использовании NULL
.Лично я не вижу никакой пользы в том, чтобы называть имя (NULL
) к существующему значению - и поскольку мне также нравится тестировать указатели как истинностные значения:
if (p && !q)
do_something();
тогда использование нуля имеет больше смысла (например, если вы используете NULL
, вы не можете логически использовать p && !q
- вам нужно явно сравнить с NULL
, если только вы не предполагаете NULL
равно нулю, и в этом случае зачем использовать NULL
).
Есть ли какая-то объективная причина предпочесть ноль НУЛЮ (или наоборот), или это просто личные предпочтения?
Редактировать:Я должен добавить (и изначально хотел сказать), что с RAII и исключениями я редко использую нулевые указатели, но иногда они вам все же нужны.
Решение
Вот что делает Страуструп: Часто задаваемые вопросы по стилю и технике C ++
В C ++ определение
NULL
равно 0, поэтому есть только эстетическое различие. Я предпочитаю избегать макросов, поэтому я использую 0. Другая проблема сnullptr
заключается в том, что люди иногда ошибочно полагают, что он отличается от 0 и / или не является целым числом. В предстандартном коде <=> иногда было / определено как нечто неподходящее, и поэтому его следует / нужно избегать. Это менее распространено в наши дни.Если вам нужно назвать нулевой указатель, назовите его <=>; это то, что называется в C ++ 11. Тогда <=> будет ключевым словом. Р>
Тем не менее, не парься по мелочам.
Другие советы
Есть несколько аргументов (один из которых является относительно недавним), которые, по моему мнению, противоречат позиции Бьярне по этому вопросу.
<Ол> Использование NULL
позволяет осуществлять поиск по его использованию, а также подчеркивает, что разработчик хотел использовать указатель int
, независимо от того, интерпретируется ли он компилятором как 0
или нет.
- Перегрузка указателя и значения int относительно редки Ол>
- Инструменты анализа могут помочь СЕГОДНЯ! Ол>
- C ++ 11 будет иметь новый тип <=>. Ол>
Пример, который все цитируют:
void foo(int*);
void foo (int);
void bar() {
foo (NULL); // Calls 'foo(int)'
}
Однако, по крайней мере, на мой взгляд, проблема с вышеприведенным не в том, что мы используем NULL для константы нулевого указателя, а в том, что у нас есть перегрузки 'foo', которые принимают аргументы очень разных типов. Параметр также должен быть std::nullptr_t
, так как любой другой тип приведет к неоднозначному вызову и, следовательно, сгенерирует полезное предупреждение компилятора.
Даже в отсутствие C ++ 0x на сегодняшний день существуют инструменты, которые проверяют, что <=> используется для указателей и <=> используется для целочисленных типов.
Это новейший аргумент таблицы. Проблема <=> и <=> активно решается для C ++ 0x, и вы можете гарантировать, что для каждой реализации, которая предоставляет <=>, самое первое, что они сделают, это:
#define NULL nullptr
Для тех, кто использует <=>, а не <=>, изменение будет улучшением безопасности типов без особых усилий или усилий - во всяком случае, это также может выявить несколько ошибок, когда они использовали <=> для <=>. Для тех, кто сегодня использует <=> .... хм ... ну, надеюсь, они хорошо знают регулярные выражения ...
Используйте NULL. NULL показывает ваше намерение. То, что это 0, является деталью реализации, которая не должна иметь значения.
Я давно перестал использовать NULL в пользу 0 (как и большинства других макросов). Я сделал это не только потому, что хотел как можно больше избегать макросов, но и потому, что NULL, похоже, стал чрезмерно использоваться в коде C и C ++. Кажется, он используется всякий раз, когда необходимо значение 0, а не только для указателей.
В новых проектах я помещаю это в заголовок проекта:
static const int nullptr = 0;
Теперь, когда приходят компиляторы, совместимые с C ++ 0x, все, что мне нужно сделать, это удалить эту строку. Приятным преимуществом этого является то, что Visual Studio уже распознает nullptr в качестве ключевого слова и соответствующим образом выделяет его.
Я всегда использую:
NULL
для указателей'\0'
для символов0.0
для поплавков и дублей
где 0 было бы вполне достаточно.Это вопрос сигнализации о намерении.Тем не менее, я не отношусь к этому анально.
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), которые не так хороши - и это приводило к ошибкам во время выполнения.
Если я правильно помню, 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
, который является типом <=>.
Я бы сказал, что история рассказала, и те, кто высказывался в пользу использования 0 (ноль), ошибались (включая Бьярне Страуструпа). Аргументы в пользу 0 были в основном эстетическими и & Quot; личные предпочтения & Quot;.
После создания C ++ 11 с его новым типом nullptr некоторые компиляторы начали жаловаться (с параметрами по умолчанию) на передачу 0 функциям с аргументами-указателями, поскольку 0 не является указателем. Р>
Если бы код был написан с использованием NULL, можно было бы выполнить простой поиск и замену через кодовую базу, чтобы вместо него сделать его nullptr. Если вы застряли с кодом, написанным с использованием выбора 0 в качестве указателя, его гораздо утомительнее обновить.
И если вам нужно прямо сейчас написать новый код для стандарта C ++ 03 (и вы не можете использовать nullptr), вам действительно следует просто использовать NULL. В будущем вам будет намного проще обновляться.
Я обычно использую 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.
Я думаю, что стандарт гарантирует, что NULL == 0, так что вы можете сделать либо. Я предпочитаю NULL, потому что это документирует ваше намерение.
Использование 0 или NULL будет иметь тот же эффект.
Однако это не означает, что они оба являются хорошими практиками программирования. Принимая во внимание, что нет разницы в производительности, выбор низкоуровневого варианта вместо независимой / абстрактной альтернативы является плохой практикой программирования. Помогите читателям вашего кода понять ваш мыслительный процесс . Р>
NULL, 0, 0.0, '\ 0', 0x00 и все остальные переводятся в одно и то же, но являются разными логическими объектами в вашей программе. Их следует использовать как таковые. NULL - это указатель, 0 - это количество, 0x0 - это значение, чьи биты интересны и т. Д. Вы не назначали бы '\ 0' указателю независимо от того, компилируется он или нет.
Я знаю, что некоторые сообщества поощряют демонстрацию глубоких знаний об окружающей среде, нарушая условия окружающей среды. Ответственные программисты, однако, создают поддерживаемый код и не допускают такой практики в свой код.
Странно, но никто, включая Страустроупа, не упоминал об этом.Пока много говорили о стандартах и эстетике, никто не заметил, что это опасный для использования 0
в NULL
вместо этого, например, в списке аргументов переменной в архитектуре, где sizeof(int) != sizeof(void*)
.Как и Страустроуп, я предпочитаю 0
по эстетическим соображениям, но нужно быть осторожным, чтобы не использовать его там, где его тип может быть неоднозначным.
Я стараюсь избегать всего вопроса, используя ссылки на C ++, где это возможно. Вместо того, чтобы
void foo(const Bar* pBar) { ... }
вы часто можете писать
void foo(const Bar& bar) { ... }
Конечно, это не всегда работает; но нулевые указатели могут быть чрезмерно использованы.
Я с Страуструпом на этом :-) Поскольку NULL не является частью языка, я предпочитаю использовать 0.
В основном личные предпочтения, хотя можно привести аргумент, что NULL делает совершенно очевидным, что объект является указателем, который в настоящее время ни на что не указывает, например.
void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used
IIRC, стандарт не требует, чтобы NULL было равно 0, поэтому используйте все, что определено в < stddef.h > вероятно, лучше всего подходит для вашего компилятора.
Другим аспектом аргумента является то, следует ли использовать логические сравнения (неявное приведение к bool) или проверку чистоты по NULL, но это также сводится к удобочитаемости.
Я предпочитаю использовать NULL, поскольку ясно, что ваше намерение - это значение, представляющее указатель, а не арифметическое значение. Тот факт, что это макрос, вызывает сожаление, но, поскольку он так широко укоренился, нет никакой опасности (если кто-то не делает что-то действительно сумасшедшее). Хотелось бы, чтобы это было ключевое слово с самого начала, но что вы можете сделать?
Тем не менее, у меня нет проблем с использованием указателей в качестве истинностных ценностей самих по себе. Как и в случае с NULL, это укоренившаяся идиома.
C ++ 09 добавит конструкцию nullptr, которая, я думаю, давно назрела. Р>
Я всегда использую 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 " заявления по всему вашему коду. Гораздо приятнее, если вы всегда можете положиться на действительное состояние.
Почти всегда есть лучшая альтернатива.
Установка указателя на 0 просто не так понятна.Особенно, если вы используете язык, отличный от C ++.Это включает в себя C, а также Javascript.
Недавно я применил некоторый код, подобный этому:
virtual void DrawTo(BITMAP *buffer) =0;
впервые для чисто виртуальной функции.Я думал, что это будет какой-нибудь волшебный джиберджаш на неделю.Когда я понял, что это была просто установка указателя функции на null
(поскольку виртуальные функции в большинстве случаев являются просто указателями на функции для C ++) Я пнул себя.
virtual void DrawTo(BITMAP *buffer) =null;
было бы менее запутанно, чем это ублюдочное высказывание, без должного расстояния для моих новых глаз.На самом деле, мне интересно, почему C ++ не использует нижний регистр null
очень похоже на то, что сейчас в нем используются строчные буквы false и true.