NULL против нуля в C

Я недавно прочитал Могу ли я использовать NULL в качестве замены значения 0?

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

Но в Можно ли предположить, что константа NULL равна нулю?, короче говоря, было сказано, что предположение if(!ptr)//ptr is a pointer не совсем неверно .

Я знаю, что содержание вопросов отличается, но как это можно объяснить, что использование NULL в качестве замены 0 неверно, а if(!ptr) верно? Потому что if(!ptr) эквивалентно if(ptr==0) (я полагаю, это правильно, не уверен).

Кроме того, я использовал if(ptr==0), и у меня это никогда не работало неправильно (чтобы проверить, является ли ptr NULL), и я назначил 0 указателю ptr, и когда я отлаживал мой код, ptr был NULL < / сильный>. Безопасны ли эти два опыта?


person hanie    schedule 03.04.2020    source источник


Ответы (3)


Из этой NULL ссылки на указатель:

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

[выделено мной]

Таким образом, целочисленная константа 0 является допустимой константой нулевого указателя.

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

Кроме того, нулевой указатель всегда является «ложным», а ненулевой указатель всегда «истинным», поэтому такие условия, как if (ptr) или if (!ptr), работают хорошо.

person Some programmer dude    schedule 03.04.2020

Я знаю, что содержание вопросов отличается, но как это можно объяснить, что использование NULL в качестве замены 0 неверно, а if(!ptr) верно? Потому что if(!ptr) эквивалентно if(ptr==0) (я полагаю, это правильно, не уверен).

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

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

person John Bollinger    schedule 03.04.2020

NULL - это макрос. Это «константа нулевого указателя, определяемая реализацией»; C17dr § 7.19 3.

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

Таким образом, NULL может иметь тип void *, int, long, unsigned, long long и т. Д.

0 - это int константа.

Когда все в порядке.

Назначение. Оба нижеприведенных элемента присваивают p,q некоторому нулевому указателю.

void *p = 0;
void *q = NULL; 

Сравнение кода: p==q верно, поскольку все нулевые указатели приравниваются. Все нулевые указатели не соответствуют адресу какого-либо объекта. !p и !q равны 1.

Когда это не нормально.

Аргумент функции

Тип и размер NULL определяется реализацией.

printf("%d\n", 0);            // OK - %d expects an int
printf("%d\n", NULL);         // Not OK, NULL could be long, void *, etc.
printf("%p\n", NULL);         // Not OK, NULL could be int, long, long long
printf("%p\n", (void*) NULL); // OK - %p expects a void*

_Generic()

Результат ниже определяется реализацией.

_Generic((NULL), \
  void *: "void *", \
  int: "int", \
  long: "long", \
  default: "TBD")

Сравнение макросов

Ниже приведено сообщение «ошибка: оператор '*' не имеет правильного операнда». #if !0 был в порядке.

#if !NULL
#error foo
#endif
person chux - Reinstate Monica    schedule 03.04.2020
comment
это ptr=0 нормально? Я имею в виду, должна ли использоваться эта инициализация вместо ptr=NULL right? - person hanie; 03.04.2020
comment
Реализации могут или не могут гарантировать что-либо обо всех printf примерах, но те, которые определяют, что они определяют NULL определенным образом, потребуются для осмысленной обработки второй или третьей строки not ok, в зависимости от того, насколько точно они задают NULL. - person supercat; 03.04.2020
comment
Первый преобразует int 0 в указатель, нулевой указатель, а затем присваивает. Хорошо определено. Второй преобразует любой тип NULL в указатель, нулевой указатель, а затем назначает. Также хорошо определен. Используйте любой из них, лучше всего для кодирования в соответствии со стандартом / стилем кодирования вашей группы. - person chux - Reinstate Monica; 03.04.2020
comment
Реализации @supercat могут или не могут гарантировать что-либо в отношении всех примеров printf, - ›Что не гарантируется в отношении printf("%d\n", 0);? - person chux - Reinstate Monica; 03.04.2020
comment
@ chux-ReinstateMonica: От них потребуют предоставить гарантии в отношении некоторых, но не всех. - person supercat; 03.04.2020
comment
@supercat Я надеюсь, что мы согласны с тем, что 2-й и 3-й являются потенциальными UB, учитывая возможность, что спецификатор и тип аргумента могут не совпадать. - person chux - Reinstate Monica; 03.04.2020
comment
@ chux-ReinstateMonica: В некоторых реализациях каждый будет UB. В Стандарте отсутствует терминология для описания действий, которые могут быть UB или нет, основанные на чертах, определяемых реализацией; если он классифицирует такие действия как все, он имеет тенденцию классифицировать их как UB без учета того, должны ли некоторые реализации определять их [например, что C99, вероятно, предназначен только для устранения поведенческих требований C89 для сдвига влево отрицательных чисел в реализациях знаковой величины, что имело бы смысл, но для этого требуется поведение, определенное на всех платформах, в UB на всех платформах. - person supercat; 03.04.2020