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

StackOverflow https://stackoverflow.com/questions/176989

  •  05-07-2019
  •  | 
  •  

Вопрос

В первые дни 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 или нет.

    1. Перегрузка указателя и значения int относительно редки
    2. Пример, который все цитируют:

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

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

      1. Инструменты анализа могут помочь СЕГОДНЯ!
      2. Даже в отсутствие C ++ 0x на сегодняшний день существуют инструменты, которые проверяют, что <=> используется для указателей и <=> используется для целочисленных типов.

        1. C ++ 11 будет иметь новый тип <=>.
        2. Это новейший аргумент таблицы. Проблема <=> и <=> активно решается для 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.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top