Можно ли предположить, что NULL всегда преобразуется в false в C?
void *somePtr = NULL;
if (!somePtr) {
/* This will always be executed? */
}
Или должна быть сделана явная проверка по значению NULL?
Я просто отсылаю вас к Вопрос 5.3 в C-FAQ. Он отвечает именно на этот вопрос.





Никогда не безопасно что-либо предполагать.
Явная проверка также дает более четкое представление о том, что вы тестируете.
Раньше мы делали еще один шаг вперед и определяли значение nil для каждого из наших типов элементов: структур, int, longs и т. д. Таким образом, условное выражение будет читать «if (foo! = FooNil) {etc.}». Отказ от ответственности: раньше я работал на Симони в Xerox, поэтому мой мозг был заражен.
@Peter Rowell: довольно долгое время я использовал #define NIL (x) ((x) 0) для достижения примерно такого эффекта: NIL (foo), NIL (char *), NIL (bar) и т. д.
@ Джонатан: на каком языке - C или C++? В C99 в этом нет необходимости, потому что «Любые два нулевых указателя должны сравниваться как равные».
NULL определяется как постоянный указатель, который гарантированно указывает на бесполезное / несуществующее место в памяти. Большинство реализаций NULL - это ((void *)0), но это не обязательно.
В C это правильно. Имейте в виду, что в C++ это чаще всего просто 0, в основном потому, что вы не можете преобразовать указатель void в любой другой тип без явного приведения.
В любом правильном компиляторе C "NULL == 0" истинно, даже если нулевой указатель на самом деле не содержит 0. Что вы не можете сделать, так это запомнить указатель, а затем предположить, что он будет NULL.
* NULL всегда нацелен на 0x00L. Вы можете считать это ложным, но для уверенности всегда проводите явную проверку.
NULL зависит от реализации. В некоторых случаях, например, на встроенных машинах, вы можете захотеть получить доступ к самому первому байту в ОЗУ, который будет иметь адрес 0. NULL определяется стандартом как указатель, который гарантированно не указывает на какую-либо полезную точку в ОЗУ.
Разыменование самого NULL или нулевого указателя вызывает неопределенное поведение - или, на достаточно больших машинах с MMU, дамп ядра или эквивалент. Синдром "VAX всего мира" умер некоторое время назад - там разграничение NULL действительно давало нулевой байт.
Младшие 128 байтов vax-машины выдают нарушение прав доступа при их разыменовании. У вас есть программа, демонстрирующая наблюдаемые вами симптомы?
Да. 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, правильность которой гарантируется языком. :)
я бы заплакал, если бы весь мой код на c использовал их, если материал теперь недействителен :)
Стандарт не очень ясен в этом вопросе: гарантируется, что 0, приведенный к типу указателя, будет нулевым указателем, но, как правило, он не указывает, что произойдет, когда нулевой указатель будет приведен к типу int!
вы не приводите к int. вы сравниваете 0 с указателем. 0 - константа нулевого указателя. и если вы сравните константу нулевого указателя с указателем, константа нулевого указателя станет нулевым указателем правильного типа. а нулевой указатель не равен каждому действительному указателю. это то, что он хочет
Ситуация в стандарте более запутанная: он позволяет нулевому указателю иметь адрес, например. 0xdeadbeef, но ИМХО по-прежнему оценивается как ложь. Это потому, что в предложении if нет преобразования, условие должно иметь скалярный тип (указатель является скалярным типом), а указатель NULL сравнивается с 0
да jpalecek в том-то и дело. «нулевой указатель» не означает «нулевые биты». нулевой указатель - это черный ящик. В if "! = 0" неявно, а в! Ptr "== 0" неявно. поэтому, если вы сделаете if (ptr), он сравнит указатель с константой нулевого указателя.
Может быть, tvanfosson может поцарапать часть о повышении до целого числа для логической проверки?
NULL is essentially the zero address это не совсем правильно. NULL равен 0 при преобразовании в числовое значение, но значение нулевого указателя может быть ненулевым Всегда ли NULL равен нулю в C?. Адрес 0 может быть действительным, особенно. на встроенных системах
Ссылки на Википедию - не лучшая идея ... Есть ссылки на стандарты?
Язык 'C' восходит к эпохе, когда (void *) 0 действительно мог быть действительным указателем. Совсем недавно микропроцессоры 8080 и Z80 имели вектор прерывания по адресу 0. Столкнувшись с таким выбором архитектуры, он не мог ничего делать, кроме как позволить файлу заголовка объявлять значение NULL. Были некоторые компиляторы, теперь давно забытые, где NULL не был равен (void *) 0 (0xffff был следующей альтернативой), что давало вашему оператору if () неопределенное поведение.
C++ милостиво положил этому конец, нулевой указатель может быть назначен и протестирован против 0.
MMU, вероятно, были более значимыми, чем C++, в этом изменении.
О вашем предложении на C++: это неправильно. 0 не является нулевым указателем. 0 - константа нулевого указателя. void * p = 0; теперь p - нулевой указатель. это разница, потому что нулевой указатель имеет тип указателя, где константа нулевого указателя имеет целочисленный тип и является константным выражением
C99 гарантирует, что приведение 0 к типу указателя также будет нулевым указателем - я никогда не удосужился прочитать предыдущие стандарты, поэтому не знаю, когда это произошло ...
Не так давно с 8086 по 80686 имели вектор прерывания по адресу 0 в реальном режиме.
0 по-прежнему остается вполне допустимым адресом для каждой арки, для которой я написал mmu-код. Это просто соглашение многих сред выполнения C, которое делает недействительным его использование. В современном Linux, как root, вы можете настроить объект на 0 с помощью MAP_FIXED. В недавнем UNIX нулевую страницу, доступную только для чтения, было обычным делом отображать на ноль, чтобы strcmp (NULL, ...) не приводил к ошибке, а соответствовал только пустой строке.
Да, в основном.
Во-первых, NULL - это определение типа. Я мог бы вас по-королевски обмануть, сказав в ранее включенном заголовке
#define NULL 1Возможно, в этом нет большого смысла, но с каких это пор код других людей когда-либо имел смысл? :)
Кроме того, хотя он, вероятно, синтаксически безопасен, он семантически неверен. NULL означает «ничего», ни истину, ни ложь, ни логическое значение, ни int, ни строку. Это означает «символ ничего». Так что тестирование на NULL больше похоже на философскую проблему: если дерево падает в лесу и if (listener), издает ли оно звук?
Сделайте всем одолжение и четко относитесь к тестированию против NULL.
@Matt: Разве вы не имеете в виду макрос :-)?
Да (по крайней мере, для любого компилятора 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, вы, по крайней мере, получите предупреждение о том, что вы конвертируете целое число в указатель.
Интересно. Вы можете отключить его, выполнив: #define NULL (void *) 1
Это было бы совершенно неправильно. Компилятор и стандартная библиотека C связаны вместе не просто так. Если вы запутаетесь с этим, вы нарушите это.
Да, если вы переопределите его, значение изменится, но это вряд ли имеет значение.
Да, 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
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 ( 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.
Ваш код не использует значение, если NULL