Можно ли предположить, что константа NULL равна нулю? - PullRequest
7 голосов
/ 20 марта 2020

Книга Понимание и использование C Указателей Ричардом Ризом гласит:

Концепция null - это абстракция, поддерживаемая константой нулевого указателя. Эта константа может быть или не быть константой ноль. Программисту C не нужно заботиться об их фактическом внутреннем представлении.

Мой вопрос такой, поскольку "эта константа может быть или не быть константой ноль", безопасно ли мне делать что-то как показано ниже в моем коде:

int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address

if(!ptr)
{
   ERROR();
}

Если NULL не равен 0, есть вероятность, что предложение if будет иметь значение true.

Ответы [ 3 ]

6 голосов
/ 20 марта 2020

Можно ли предположить, что константа NULL равна нулю?

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


OP смешивает как минимум 4 вещи: NULL, константа нулевого указателя , нулевой указатель , сравнение нулевого указателя с 0. C не определяет NULL-константу .

NULL

NULL - это макрос ", который расширяется до константы нулевого указателя, определяемой реализацией" C17dr § 7.19 3

константа нулевого указателя

Выражение из целочисленной константы со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя . C17dr § § 6.3.2.3 3

Таким образом, тип константы нулевого указателя может быть int, unsigned, long, ... или void *.

Когда целое число 1 , константа нулевого указателя значение равно 0. Как указатель типа ((void *)0), его значение / кодировка не указана. Он повсеместно имеет битовую комбинацию нулей, но не указывается так.

Может быть много констант нулевого указателя . Все они сравниваются равными друг другу.

Примечание: размер константы нулевого указателя , когда она является целым числом, может отличаться от размера объекта указатель. Этой разницы в размерах часто можно избежать, добавляя L или два суффикса при необходимости.

нулевой указатель

Если ноль константа указателя преобразуется в тип указателя, результирующий указатель, называемый нулевой указатель , гарантированно сравнивается с неравным указателем на любой объект или функцию. C17dr § 6.3.2.3 3

Преобразование нулевого указателя в другой тип указателя дает нулевой указатель этого типа. Любые два нулевых указателя должны сравниваться равными. C17dr § 6.3.2.3 4

Тип нулевой указатель - это некоторый указатель, либо указатель объекта, например, int *, char *, либо указатель функции, например, int (*)(int, int) или void *. .

Значение нулевого указателя не указано. Он повсеместно имеет битовую комбинацию нулей, но не указывается так.

Все нулевой указатель сравниваются как равные, независимо от их кодировки.

сравнение нулевой указатель на 0

if(!ptr) совпадает с if(!(ptr != 0)). Когда указатель ptr, который является нулевым указателем , сравнивается с 0, ноль преобразуется в указатель, нулевой указатель того же типа: int *. Эти 2 нулевые указатели , которые могут иметь разные битовые комбинации, сравниваются как равные.


Поэтому, когда небезопасно предполагать, что NULL константа равна нулю?

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

printf("%ju\n", (uintmax_t)(uintptr_t)NULL); // Possible not 0

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

C spe c охватывает многие старые способы ведения дел и открыт для новых новых. Я никогда не сталкивался с реализацией, в которой NULL не был битовым шаблоном со всеми нулями. Учитывая, что существует большой код, который предполагает, что NULL - это все нулевые биты, я подозреваю, что только старые неизвестные реализации когда-либо использовали ненулевой битовый шаблон NULL, и что NULL может быть почти наверняка полностью нулевым битовым шаблоном.


1 Константа нулевого указателя равна 1) целому числу или 2) a void*. «Когда целое число ... «относится к первому случаю, а не к преобразованию или преобразованию второго случая, как в (int)((void*)0).

4 голосов
/ 20 марта 2020

if(!ptr) - это безопасный способ проверить NULL-указатель.

Выражение !x в точности эквивалентно 0 == x. Константа 0 является константой NULL-указателя , и любой указатель может сравниваться на равенство с константой NULL-указателя.

Это верно даже в том случае, если представление нулевого указателя не "все биты 0".

Раздел 6.5.3.3p5 стандарта C относительно ! оператор состояния:

Результат оператора логического отрицания ! равен 0, если значение его операнда сравнивается с 0, 1, если значение его операнда сравнивается равным 0. Результат имеет тип int. Выражение !E эквивалентно (0==E).

и разделу 6.3.2.3p3 относительно состояний преобразования указателя:

Целочисленное константное выражение со значением 0, или такое выражение, приведенное к типу void *, называется константой нулевого указателя . Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевой указатель , гарантированно сравнивается с неравным указателем на любой объект или функцию.

2 голосов
/ 20 марта 2020

Чукс написал хороший, подробный ответ, но в отношении этой книги я был бы скептичен c о ее качестве:

  • Эта константа может или может не может быть постоянным нулем

    Это неправильно, оно всегда должно быть нулем или нулем, приведенным к void*. Определение константы нулевого указателя находится в C17. 6.3.2.3/3:

    Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константа нулевого указателя . Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевой указатель , гарантированно сравнивается с неравным указателем на любой объект или функцию.

    Это означает, что все целочисленные константы, такие как 0, 0L, 0u, 0x0, '\0' и c, являются константой нулевого указателя. Если любой из них приведен к void*, он также является константой нулевого указателя.

  • Программисту C не нужно заботиться об их фактическом внутреннем представлении.

    Автор явно смешивает два формальных термина: константа нулевого указателя и нулевой указатель . Программисту не нужно заботиться о внутреннем представлении нулевого указателя. Им нужно знать, что делает действительный нулевой указатель постоянным. Самый безопасный и самый читаемый способ - использовать макрос NULL, который гарантированно будет константой нулевого указателя.

Что касается вашего вопроса ", безопасно ли мне что-то делать как ниже в моем коде "- да, совершенно безопасно сделать !ptr, чтобы проверить нулевой указатель, даже если ptr==NULL более читаемый код.

...