NULL всегда ложно?
Можно ли предположить, что NULL
всегда переводится как false в C?
void *somePtr = NULL;
if (!somePtr) {
/* This will always be executed? */
}
Или следует явная проверка значения NULL
быть сделано?
9 ответов
Да. NULL оценивается как ложное, поскольку C считает любое ненулевое значение истинным, а любое нулевое значение ложным. NULL по существу zero
адрес и рассматривается как таковой в сравнениях, и я считаю, что будет повышен до int для логической проверки. Я ожидаю, что ваш код доступен для чтения любому, кто знаком с C, хотя я, вероятно, сделаю проверку явной.
В программировании на C и C++ два нулевых указателя гарантированно сравниваются; ANSI C гарантирует, что любой нулевой указатель будет равен 0 в сравнении с целочисленным типом; кроме того, макрос NULL определен как константа нулевого указателя, то есть значение 0 (либо целочисленный тип, либо преобразованный в указатель на void), поэтому нулевой указатель будет сравниваться равным NULL.
Язык 'C' датируется эпохой, когда (void*)0 действительно мог быть действительным указателем. Не так давно микропроцессоры 8080 и Z80 имели вектор прерывания по адресу 0. Столкнувшись с таким выбором архитектуры, он ничего не мог сделать, кроме как позволить заголовочному файлу объявить значение NULL. Существовали некоторые компиляторы, теперь давно забытые, где NULL не был равен (void*)0 (следующей альтернативой был 0xffff), что давало вашему оператору if() неопределенное поведение.
С ++, к счастью, положил этому конец, нулевой указатель присваивается и проверяется на 0.
Никогда не безопасно предполагать что-либо.
Явная проверка также более понятна в отношении того, что вы тестируете.
Да (по крайней мере, для любого совместимого со стандартами C-компилятора!)
Из comp.lang.c FAQ:
В: Действительно ли сокращенное сравнение указателей ``if(p)'' для проверки ненулевых указателей допустимо? Что если внутреннее представление для нулевых указателей отлично от нуля?
A: Это всегда верно.
Моя копия ISO/IEC 9899:TC3 (проект комитета - 7 сентября 2007 г.) говорит:
6.3 Конверсии
1 Несколько операторов автоматически преобразуют значения операндов из одного типа в другой.
6.3.2.3 Указатели
3 Целочисленное постоянное выражение со значением 0 [...] называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно не будет сравниваться с указателем на любой объект или функцию.
Уже, ptr!=0
верно (1) для любого ненулевого ptr
, но все еще открыто, как сравниваются два нулевых указателя.
6.5.9 Операторы равенства
5 [...] Если один операнд является указателем, а другой - константой нулевого указателя, константа нулевого указателя преобразуется в тип указателя.
6 Два указателя сравниваются как равные тогда и только тогда, когда оба являются нулевыми указателями, оба [...]
Следовательно, ptr==0
равно 1 (и ptr!=0
равно 0), тогда и только тогда, когда ptr
- нулевой указатель.
6.5.3.3 Унарные арифметические операторы
5 Результат оператора логического отрицания! равен 0, если значение его операнда не равно 0, 1, если значение его операнда сравнивается с 0. Результат имеет тип int. Выражение!E эквивалентно (0==E).
То же самое верно и для !ptr
.
6.8.4.1 Оператор if
1 Управляющее выражение оператора if должно иметь скалярный тип.
2 В обеих формах первая подгруппа выполняется, если выражение не равно 0.
Обратите внимание, что скалярный тип - это арифметический тип или тип указателя (см. "6.2.5 Типы", раздел 21). Собирая все вместе, мы получаем:
if (ptr)
успешно ⇔ptr!=0
равно 1 ⇔ptr
не является нулевым указателем.if (!ptr)
успешно ⇔ptr==0
равно 1 ⇔ptr
- нулевой указатель.
Да, if(!p)
действует и гарантированно работает.
Целочисленное постоянное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно не будет сравниваться с указателем на любой объект или функцию.
https://port70.net/~nsz/c/c11/n1570.html
Это значит, что (void*)0
- нулевой указатель. Это также означает, что еслиp
это указатель, тогда p==0
эквивалентно p==(void*)0
.
Это также означает, что если p
не является нулевым указателем, тогда p==(void*)0
будет оценивать 0
.
Все идет нормально.
Преобразование нулевого указателя в другой тип указателя дает нулевой указатель этого типа. Любые два нулевых указателя должны сравниваться как равные.
http://port70.net/~nsz/c/c11/n1570.html
Обратите внимание, что "Любые два нулевых указателя должны сравниваться как равные". Это означает, что еслиp
является нулевым указателем, тогда p==0
будет оцениваться как истина, потому что 0
будет повышен до (void*)0
который является нулевым указателем. Это также означает, что никакой ненулевой указатель не может быть равен нулевому указателю.
Давайте посмотрим на оператор отрицания.
Результат оператора логического отрицания! равен 0, если сравниваемое значение его операнда не равно 0, 1, если значение его операнда сравнивается с 0. Результат имеет тип int. Выражение!E эквивалентно (0==E).
http://port70.net/~nsz/c/c11/n1570.html
Это говорит нам, что !p
такой же как p==0
по определению, что совпадает с p==(void*)0
как уже упоминалось выше.
И учитывая тот факт, что все нулевые указатели равны, это означает, что p==(void*)0
может быть истинным, только если p
является нулевым указателем и только ложным, если p
не является нулевым указателем.
Так да, if(!p)
это совершенно безопасный способ проверить, p
является нулевым указателем или нет.
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?
Макрос NULL определен в
(и других заголовках) как константа нулевого указателя;
Каково значение константы нулевого указателя?
Целочисленное постоянное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя.
Пример определения:
#define NULL ((void*)0)
Оценка if(NULL)
оператор if (выражение)
оператор if (выражение) оператор else
В обеих формах первый подзапрос выполняется, если выражение сравнивается с не равным 0. В форме else второе подзаполнение выполняется, если выражение сравнивается с равным §6.8.4.1. Язык 133 ISO/IEC 9899: Проект комитета TC3 - 7 сентября, 2007 WG14/N1256 - 0. Если первая часть достигается через метку, вторая часть не выполняется.
Итак, да, если компилятор соответствует ISO C99, вы можете предположить, что приведенный ниже оператор всегда будет выполняться.
if (!NULL) { statement; }
Приведенные выше цитаты взяты из ISO/IEC 9899:1999 (C99). Вы можете прочитать это здесь.
Да, это:
Стандарт C 6.3.2.3
Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя. Если константа нулевого указателя преобразована в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно будет сравните неравно с указателем на любой объект или функцию.
и 6.3.2.6
Любой тип указателя может быть преобразован в целочисленный тип. За исключением случаев, указанных ранее, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение не определено. Результат не обязательно должен быть в диапазоне значений какого-либо целочисленного типа.
-
Когда любое скалярное значение преобразуется в _Bool, результат равен 0, если значение сравнивается с 0; в противном случае результат будет 1
Я просто отсылаю вас к вопросу 5.3 C-FAQ. Это отвечает на этот точный вопрос.
NULL
определяется как постоянный указатель, который гарантированно указывает на бесполезное / несуществующее место в памяти. Большинство реализаций NULL
являются ((void *)0)
но это не обязательно, что это так.
На мой взгляд, это не всегда безопасно. Поскольку, в зависимости от того, какие заголовки были включены в программу, ее можно было переопределить. Но согласно стандарту константа нулевого указателя гарантированно не указывает на какой-либо реальный объект и имеет типvoid *
.
В нашем случае декларация void *somePtr = NULL
также может быть void *somePtr = 0
с 0
как нулевой указатель.
"Целочисленное постоянное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой с нулевым указателем. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно не будет сравниваться с указателем на любой объект или функцию ". 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 - это typedef. Я мог бы по-королевски напортачить, сказав в ранее включенном заголовке
#define NULL 1
Это может не иметь большого смысла, но с каких пор код других людей имеет смысл?:)
Кроме того, хотя это, вероятно, синтаксически безопасно, это не семантически правильно. NULL означает "ничего", ни true, ни false, ни логическое значение, ни int, ни string. Это означает "символ для ничего". Таким образом, тестирование на NULL больше похоже на философскую проблему: если дерево падает в лесу, и if(listener)
это издает звук?
Сделайте всем одолжение и будьте уверены в тестировании против NULL.
*NULL всегда нацелен на 0x00L. Вы можете считать это ложным, но для уверенности всегда делайте явную проверку.