Всегда ли NULL ложно?

Можно ли предположить, что NULL всегда преобразуется в false в C?

void *somePtr = NULL;

if (!somePtr) {
  /* This will always be executed? */
}

Или должна быть сделана явная проверка по значению NULL?

Ваш код не использует значение, если NULL

Johannes Schaub - litb 07.12.2011 20:11

Я просто отсылаю вас к Вопрос 5.3 в C-FAQ. Он отвечает именно на этот вопрос.

anon 20.01.2009 06:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
70
2
47 397
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Никогда не безопасно что-либо предполагать.

Явная проверка также дает более четкое представление о том, что вы тестируете.

Раньше мы делали еще один шаг вперед и определяли значение nil для каждого из наших типов элементов: структур, int, longs и т. д. Таким образом, условное выражение будет читать «if (foo! = FooNil) {etc.}». Отказ от ответственности: раньше я работал на Симони в Xerox, поэтому мой мозг был заражен.

Peter Rowell 20.01.2009 03:52

@Peter Rowell: довольно долгое время я использовал #define NIL (x) ((x) 0) для достижения примерно такого эффекта: NIL (foo), NIL (char *), NIL (bar) и т. д.

Jonathan Leffler 20.01.2009 04:02

@ Джонатан: на каком языке - C или C++? В C99 в этом нет необходимости, потому что «Любые два нулевых указателя должны сравниваться как равные».

Christoph 20.01.2009 04:05

NULL определяется как постоянный указатель, который гарантированно указывает на бесполезное / несуществующее место в памяти. Большинство реализаций NULL - это ((void *)0), но это не обязательно.

В C это правильно. Имейте в виду, что в C++ это чаще всего просто 0, в основном потому, что вы не можете преобразовать указатель void в любой другой тип без явного приведения.

Jonathan Leffler 20.01.2009 03:58

В любом правильном компиляторе C "NULL == 0" истинно, даже если нулевой указатель на самом деле не содержит 0. Что вы не можете сделать, так это запомнить указатель, а затем предположить, что он будет NULL.

Captain Segfault 20.01.2009 04:14

* NULL всегда нацелен на 0x00L. Вы можете считать это ложным, но для уверенности всегда проводите явную проверку.

NULL зависит от реализации. В некоторых случаях, например, на встроенных машинах, вы можете захотеть получить доступ к самому первому байту в ОЗУ, который будет иметь адрес 0. NULL определяется стандартом как указатель, который гарантированно не указывает на какую-либо полезную точку в ОЗУ.

dreamlax 20.01.2009 03:38

Разыменование самого NULL или нулевого указателя вызывает неопределенное поведение - или, на достаточно больших машинах с MMU, дамп ядра или эквивалент. Синдром "VAX всего мира" умер некоторое время назад - там разграничение NULL действительно давало нулевой байт.

Jonathan Leffler 20.01.2009 04:03

Младшие 128 байтов vax-машины выдают нарушение прав доступа при их разыменовании. У вас есть программа, демонстрирующая наблюдаемые вами симптомы?

EvilTeach 20.01.2009 04:44
Ответ принят как подходящий

Да. NULL принимает значение false, поскольку C считает любое ненулевое значение истинным, а любое нулевое значение - ложным. NULL - это, по сути, адрес zero и рассматривается как таковой при сравнении, и я считаю, что для логической проверки он будет повышен до int. Я ожидал, что ваш код будет доступен для чтения всем, кто знаком с C, хотя, вероятно, я бы сделал проверку явной.

In C and C++ programming, two null pointers are guaranteed to compare equal; ANSI C guarantees that any null pointer will be equal to 0 in a comparison with an integer type; furthermore the macro NULL is defined as a null pointer constant, that is value 0 (either as an integer type or converted to a pointer to void), so a null pointer will compare equal to NULL.

Ссылка: http://en.wikipedia.org/wiki/Null_pointer#Null_pointer

Явная проверка может быть более ясной в некоторых случаях, но это идиома C, правильность которой гарантируется языком. :)

Greg D 20.01.2009 04:01

я бы заплакал, если бы весь мой код на c использовал их, если материал теперь недействителен :)

Johannes Schaub - litb 20.01.2009 04:06

Стандарт не очень ясен в этом вопросе: гарантируется, что 0, приведенный к типу указателя, будет нулевым указателем, но, как правило, он не указывает, что произойдет, когда нулевой указатель будет приведен к типу int!

Christoph 20.01.2009 04:10

вы не приводите к int. вы сравниваете 0 с указателем. 0 - константа нулевого указателя. и если вы сравните константу нулевого указателя с указателем, константа нулевого указателя станет нулевым указателем правильного типа. а нулевой указатель не равен каждому действительному указателю. это то, что он хочет

Johannes Schaub - litb 20.01.2009 04:14

Ситуация в стандарте более запутанная: он позволяет нулевому указателю иметь адрес, например. 0xdeadbeef, но ИМХО по-прежнему оценивается как ложь. Это потому, что в предложении if нет преобразования, условие должно иметь скалярный тип (указатель является скалярным типом), а указатель NULL сравнивается с 0

jpalecek 20.01.2009 04:16

да jpalecek в том-то и дело. «нулевой указатель» не означает «нулевые биты». нулевой указатель - это черный ящик. В if "! = 0" неявно, а в! Ptr "== 0" неявно. поэтому, если вы сделаете if (ptr), он сравнит указатель с константой нулевого указателя.

Johannes Schaub - litb 20.01.2009 04:21

Может быть, tvanfosson может поцарапать часть о повышении до целого числа для логической проверки?

Christoph 20.01.2009 04:28
NULL is essentially the zero address это не совсем правильно. NULL равен 0 при преобразовании в числовое значение, но значение нулевого указателя может быть ненулевым Всегда ли NULL равен нулю в C?. Адрес 0 может быть действительным, особенно. на встроенных системах
phuclv 30.11.2019 09:02

Ссылки на Википедию - не лучшая идея ... Есть ссылки на стандарты?

Yasushi Shoji 26.02.2021 09:46

Язык 'C' восходит к эпохе, когда (void *) 0 действительно мог быть действительным указателем. Совсем недавно микропроцессоры 8080 и Z80 имели вектор прерывания по адресу 0. Столкнувшись с таким выбором архитектуры, он не мог ничего делать, кроме как позволить файлу заголовка объявлять значение NULL. Были некоторые компиляторы, теперь давно забытые, где NULL не был равен (void *) 0 (0xffff был следующей альтернативой), что давало вашему оператору if () неопределенное поведение.

C++ милостиво положил этому конец, нулевой указатель может быть назначен и протестирован против 0.

MMU, вероятно, были более значимыми, чем C++, в этом изменении.

Jonathan Leffler 20.01.2009 04:00

О вашем предложении на C++: это неправильно. 0 не является нулевым указателем. 0 - константа нулевого указателя. void * p = 0; теперь p - нулевой указатель. это разница, потому что нулевой указатель имеет тип указателя, где константа нулевого указателя имеет целочисленный тип и является константным выражением

Johannes Schaub - litb 20.01.2009 04:03

C99 гарантирует, что приведение 0 к типу указателя также будет нулевым указателем - я никогда не удосужился прочитать предыдущие стандарты, поэтому не знаю, когда это произошло ...

Christoph 20.01.2009 04:03
"что дает вашему оператору if () неопределенное поведение". - C FAQ не согласен
jfs 16.06.2016 18:50

Не так давно с 8086 по 80686 имели вектор прерывания по адресу 0 в реальном режиме.

S.S. Anne 25.11.2019 15:20

0 по-прежнему остается вполне допустимым адресом для каждой арки, для которой я написал mmu-код. Это просто соглашение многих сред выполнения C, которое делает недействительным его использование. В современном Linux, как root, вы можете настроить объект на 0 с помощью MAP_FIXED. В недавнем UNIX нулевую страницу, доступную только для чтения, было обычным делом отображать на ноль, чтобы strcmp (NULL, ...) не приводил к ошибке, а соответствовал только пустой строке.

mevets 25.11.2019 23:23

Да, в основном.

Во-первых, NULL - это определение типа. Я мог бы вас по-королевски обмануть, сказав в ранее включенном заголовке

#define NULL 1

Возможно, в этом нет большого смысла, но с каких это пор код других людей когда-либо имел смысл? :)

Кроме того, хотя он, вероятно, синтаксически безопасен, он семантически неверен. NULL означает «ничего», ни истину, ни ложь, ни логическое значение, ни int, ни строку. Это означает «символ ничего». Так что тестирование на NULL больше похоже на философскую проблему: если дерево падает в лесу и if (listener), издает ли оно звук?

Сделайте всем одолжение и четко относитесь к тестированию против NULL.

@Matt: Разве вы не имеете в виду макрос :-)?

anon 20.01.2009 06:35

Да (по крайней мере, для любого компилятора C, соответствующего стандартам!)

Из comp.lang.c FAQ:

Q: Is the abbreviated pointer comparison ``if (p)'' to test for non-null pointers valid? What if the internal representation for null pointers is nonzero?

A: It is always valid.

NULL - это просто определение препроцессора. Это в stdio.h. Обычно только сумасшедший может изменить это определение, но это возможно. Пример:

#include <stdio.h>
#ifdef NULL
#undef NULL
#define NULL 1
#endif

void main()
{

        if (NULL)
                printf("NULL is true\n");
        else
                printf("NULL is false\n");
}

Этот код напечатает «NULL is true». Попробуйте, если вы мне не верите. Ваш компилятор может даже не предупредить вас о том, что вы делаете что-то странное.

Но если вы определите NULL равным 1 и назначите somePtr = NULL, вы, по крайней мере, получите предупреждение о том, что вы конвертируете целое число в указатель.

Mark James 20.01.2009 09:11

Интересно. Вы можете отключить его, выполнив: #define NULL (void *) 1

bdowling 21.01.2009 03:10

Это было бы совершенно неправильно. Компилятор и стандартная библиотека C связаны вместе не просто так. Если вы запутаетесь с этим, вы нарушите это.

Spidey 30.11.2012 16:11

Да, если вы переопределите его, значение изменится, но это вряд ли имеет значение.

klutt 23.11.2019 13:23

Да, if (!p) действителен и гарантированно работает.

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3

Это означает, что (void*)0 является нулевым указателем. Это также означает, что если p является указателем, то p==0 эквивалентен p==(void*)0.

Это также означает, что если p не является нулевым указателем, то p==(void*)0 будет оценивать как 0.

Все идет нормально.

Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p4

Обратите внимание, что «Любые два нулевых указателя должны сравниваться как равные».. Это означает, что если p является нулевым указателем, тогда p==0 будет оценивать как истинное, потому что 0 будет повышен до (void*)0, который является нулевым указателем. Это также означает, что никакой ненулевой указатель не может быть равен нулевому указателю.

Давайте посмотрим на оператор отрицания.

The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E).

http://port70.net/~nsz/c/c11/n1570.html#6.5.3.3p5

Это говорит нам о том, что !p по определению совпадает с p==0, что совпадает с упомянутым выше p==(void*)0.

И принимая во внимание тот факт, что все нулевые указатели равны, это означает, что p==(void*)0 может оценивать значение true только в том случае, если p является нулевым указателем, и только ложным, если p не является нулевым указателем.

Итак, да, if (!p) - это совершенно безопасный способ проверить, является ли p нулевым указателем или нет.

В моей копии ISO / IEC 9899: TC3 (проект комитета - 7 сентября 2007 г.) говорится:

6.3 Conversions

1 Several operators convert operand values from one type to another automatically.

6.3.2.3 Pointers

3 An integer constant expression with the value 0 [...] is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

Пока что ptr!=0 является истинным (1) для каждого ненулевого ptr, но он все еще открыт, как сравниваются два нулевых указателя.

6.5.9 Equality operators

5 [...] If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer.

6 Two pointers compare equal if and only if both are null pointers, both are [...]

Следовательно, ptr==0 равен 1 (а ptr!=0 равен 0), если и только если ptr является нулевым указателем.

6.5.3.3 Unary arithmetic operators

5 The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E).

То же самое и с !ptr.

6.8.4.1 The if statement

1 The controlling expression of an if statement shall have scalar type.

2 In both forms, the first substatement is executed if the expression compares unequal to 0.

Обратите внимание, что скалярный тип является арифметический тип или тип указателя (см. «6.2.5 Типы», раздел 21). Собирая все вместе, мы получаем:

  • if (ptr) успешен ⇔ ptr!=0 равен 1 ⇔ ptr не является нулевым указателем.
  • if (!ptr) завершается успешно ⇔ ptr==0 равен 1 ⇔ ptr является нулевым указателем.

Цитируемый документ доступен на open-std.org/jtc1/sc22/wg14/www/standards

Ralph 28.11.2019 22:47


Что такое NULL?

The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant;


Каково значение константы нулевого указателя?

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

Пример определения:

#define NULL ((void*)0)


Оценка if (NULL)

if ( expression ) statement

if ( expression ) statement else statement

In both forms, the first substatement is executed if the expression compares unequal to 0. In the else form, the second substatement is executed if the expression compares equal §6.8.4.1 Language 133 ISO/IEC 9899:TC3 Committee Draft — Septermber 7, 2007 WG14/N1256 to 0. If the first substatement is reached via a label, the second substatement is not executed.


Итак, да, если компилятор соответствует ISO C99, вы можете предположить, что приведенный ниже оператор всегда будет выполняться.

if (!NULL) { statement; } 


Приведенные выше цитаты взяты из ISO / IEC 9899: 1999 (C99). Вы можете прочитать это здесь.

Да, это:

Стандарт C 6.3.2.3

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

и 6.3.2.6

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

-

When any scalar value is converted to _Bool, the result is 0 if the value compares equal to 0; otherwise, the result is 1

На мой взгляд, это не всегда безопасно. Поскольку, в зависимости от того, какие заголовки были включены в программу, ее можно было переопределить. Но согласно стандарту константа нулевого указателя гарантированно не указывает на какой-либо реальный объект и имеет тип void *.

В нашем случае объявление void *somePtr = NULL также может быть void *somePtr = 0 с 0 в качестве нулевого указателя.

“An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.” https://www.geeksforgeeks.org/few-bytes-on-null-pointer-in-c/

Это просто означает, что оценка *somePtr в этот конкретный момент может привести к ложному результату.

Другой ссылкой может быть https://www.gnu.org/software/libc/manual/pdf/libc.pdf на странице 944 A.3 о константе указателя NULL.

Другие вопросы по теме