Как проверить, является ли указатель NULL-указателем?

Я всегда думаю просто, if(p != NULL){..} выполнит эту работу. Но после прочтения этого вопроса о переполнении стека , похоже, нет.

Итак, каков канонический способ проверки указателей NULL после поглощения всего обсуждения в этом вопросе, который говорит, что указатели NULL могут иметь ненулевое значение?

    Я всегда думаю просто, если (p! = NULL) {..} выполнит эту работу.

    Будет.

    Во-первых, чтобы быть на 100% понятным, здесь нет никакой разницы между C и C ++. И, во-вторых, вопрос о переполнении стека, который вы цитируете, не говорит о нулевых указателях; он вводит неверные указатели; которые, по крайней мере, в отношении стандарта, вызывают неопределенное поведение, просто пытаясь их сравнить. Нет никакого способа проверить, действительно ли указатель действителен.

    В конце концов, существует три распространенных способа проверки нулевого указателя:

     if ( p != NULL ) ... if ( p != 0 ) ... if ( p ) ... 

    Вся работа, независимо от представления нулевого указателя на машине. И все, так или иначе, вводят в заблуждение; который вы выбираете, является вопросом выбора наименее плохого. Формально первые два являются индентичными для компилятора; константа NULL или 0 преобразуется в нулевой указатель типа p , а результаты преобразования сравниваются с p . Независимо от представления нулевого указателя.

    Третий немного отличается: p неявно преобразован в bool . Но неявное преобразование определяется как результат p != 0 , поэтому вы оказываетесь в одном и том же. (Это означает, что нет действительного аргумента для использования третьего стиля – он запутывается с неявным преобразованием без каких-либо компенсирующих преимуществ.)

    Какой из первых двух вы предпочитаете в основном вопрос стиля, возможно, частично продиктованный вашим стилем программирования в другом месте: в зависимости от вовлеченной идиомы одна из лжи будет более назойливой, чем другая. Если бы это был только вопрос сравнения, я думаю, что большинство людей предпочитают NULL , но что-то вроде f( NULL ) , перегрузка, которая будет выбрана, – f( int ) , а не перегрузка с указателем. Аналогично, если f является шаблоном функции, f( NULL ) будет создавать экземпляр шаблона по int . (Конечно, некоторые компиляторы, такие как g ++, генерируют предупреждение, если NULL используется в контексте не указателя; если вы используете g ++, вам действительно нужно использовать NULL .)

    В C ++ 11 , конечно, предпочтительная идиома:

     if ( p != nullptr ) ... 

    , что позволяет избежать большинства проблем с другими решениями. (Но это не C-совместимо :-).)

    Компилятор должен предоставить систему согласованного типа и предоставить набор стандартных преобразований. Ни целочисленное значение 0, ни указатель NULL не должны быть представлены всеми нулевыми битами, но компилятор должен позаботиться о преобразовании токена «0» во входном файле в правильное представление для целочисленного нуля, а тип cast to pointer должен преобразовать из целочисленного в представление указателя.

    Следствием этого является то, что

     void *p; memset(&p, 0, sizeof p); if(p) { ... } 

    не гарантированно ведет себя одинаково во всех целевых системах, поскольку вы делаете предположение о битовой схеме здесь.

    В качестве примера у меня встроенная платформа, которая не имеет защиты памяти и сохраняет векторы прерываний по адресу 0, поэтому по соглашению целые числа и указатели XORed с 0x2000000 при преобразовании, что оставляет (void *) 0, указывая на адрес, который генерирует ошибку шины при разыменовании, однако тестирование указателя с помощью оператора if сначала возвращает его в целочисленное представление, а затем все нули.

    Фактическое представление нулевого указателя здесь не имеет значения. Целочисленный литерал со значением 0 (включая 0 и любое допустимое определение NULL ) можно преобразовать в любой тип указателя, указав нулевой указатель независимо от фактического представления. Итак, p != NULL , p != 0 и p – все допустимые тесты для непустого указателя.

    Вы можете столкнуться с проблемами с ненулевыми представлениями нулевого указателя, если вы написали что-то скрученное как p != reinterpret_cast(0) , поэтому не делайте этого.

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

    Видимо, вы ссылаетесь на C++ .

    В C ваш fragment всегда будет работать. Мне нравится более простое, if (p) { /* ... */ } .

    Представление указателей не имеет значения для их сравнения, поскольку все сравнения в C имеют место как значения, не представляющие. Единственный способ сравнить представление будет чем-то отвратительным, как:

     static const char ptr_rep[sizeof ptr] = { 0 }; if (!memcmp(&ptr, ptr_rep, sizeof ptr)) ... 

    Ну, этот вопрос задавали и отвечали еще в 2011 году, но в C ++ 11 есть nullptr . Это все, что я использую сейчас.

    Вы можете прочитать больше из Stack Overflow, а также из этой статьи .