Мой код на C++ имеет следующее:
if (p == NULL || *p == 0)
Интересно, это лишнее?
(p == NULL)
— это то же самое, что (*p == 0)
?
Почему или почему нет?
Выражение *p
в точности равно p[0]
. Итак, *p == 0
проверяет, равен ли первый элемент p
нулю.
Никто не проверяет, является ли указатель нулевым, в то время как другой проверяет, является ли указанное значение нулевым или нет. Хорошая книга по C++
Также обратите внимание, что порядок имеет значение. Если p
является нулевым указателем, его нельзя разыменовать (это приводит к неопределенному поведению). А логический оператор ИЛИ ||
использует сокращенную оценку , что означает, что если p == NULL
истинно, то *p == 0
не будет оцениваться.
С другой стороны, NULL
— это макрос, который обычно расширяется до 0
и сохраняется для обратной совместимости с C. В C++ действительно следует использовать nullptr
.
Видите звездочку *
? Если да, то почему вы думаете, что оба сравнения одинаковы.
p
подскажет, где что находится; *p
расскажет вам, что это за штука. Вы знаете разницу между тем, где вы находитесь, и тем, кто вы есть.
Является ли (p == NULL) тем же, что и (*p == 0) в C++?
Нет, они не одинаковы.
Первый p == NULL
проверяет, является ли указатель p
нулевым или нет.
Последний *p==0
проверяет, является ли значение, на которое указывает указатель p
, нулевым или нет. Это потому, что *p
эквивалентно p[0]
, что дает нам указанное значение.
Обратите внимание, что в современном C++ следует использовать nullptr
вместо NULL
.
Эти 2 не одинаковы.
Выражение p == NULL
проверяет адрес, хранящийся в самом указателе. Это правда, если адрес NULL
.
Выражение *p == 0
разыменовывает указатель и проверяет, на что он указывает (обратите внимание, что *p
эквивалентно p[0]
). Это правда, если указатель указывает на значение 0
.
Размещенный вами код вполне типичен, когда вы хотите проверить, указывает ли указатель на 0
или любое другое значение. Это связано с тем, что вам необходимо проверить, что указатель содержит действительный (не нулевой) адрес (если вы не уверены в этом), прежде чем проверять, на что он указывает. Разыменование нулевого указателя вызывает UB (Неопределённое поведение).
Примечание: NULL
— это наследие C. Фактически, это макрос, определенный как 0
. В C++ указатель лучше сравнивать с nullptr
.
@RemyLebeau спасибо за исправления опечаток и т. д. Однако «iff» не было опечаткой — я имел в виду если-и-только-если. Но, возможно, использование обычного «если» не сбивает с толку.
p == whatever
сравнивает значение p
с чем угодно.
*p == whatever
разыменовывает p
и сравнивает указатель с whatever
.
Если p
является нулевым указателем, его разыменование не определено.
*p
может равняться 0
, когда p
указывает на ячейку памяти, где хранится значение 0
.
Они одинаковы? Нет.
Почему? Потому что значение указателя не является значением указателя.
Код предполагает, что p
является либо допустимым указателем, либо нулевым указателем. Он проверяет, является ли это нулевым указателем, если нет, то может продолжить его разыменование.
Такое предположение довольно распространено, однако оно небезопасно. Существует множество недопустимых указателей, которые не являются нулевыми и не могут быть разыменованы. Лучшая альтернатива — вообще не использовать необработанные указатели.
Если p
является указателем, его можно сравнить с NULL
или nullptr
, что обычно означает, что указатель инициализирован, но еще не установлен для указания на какие-либо данные.
*p
— это разыменование, то есть доступ к содержимому указанного адреса. Это можно сделать только с указателями, указывающими на действительные данные: разыменование нулевого указателя вызывает неопределенное поведение.
Следовательно, если мы хотим проверить, является ли какое-либо содержимое данных, доступ к которому осуществляется через указатель, 0
, но мы не уверены, указывает ли указатель на какое-либо значимое место, нам придется проверить, является ли это нулевым указателем, прежде чем обращаться к нему.
Самый простой и понятный способ сделать это:
if (p != NULL)
{
if (*p == 0)
{
// do stuff
}
}
Однако это также можно переписать как 100% эквивалентное логическое выражение if (p == NULL || *p == 0)
.
Это происходит из-за так называемой «короткой оценки» операторов &&
и ||
. В отличие от большинства операторов в C++, эти два имеют четко определенный порядок вычислений слева направо, поэтому p==NULL
гарантированно будет вычисляться раньше *p == 0
.
Кроме того, также гарантируется, что если результат оператора можно получить после вычисления только левого, то правый не будет оцениваться. То есть в случае 0 && x
или 1 || x
нам не нужно заботиться о том, что может оценивать правильный операнд, поскольку независимо от значения правильного операнда результат в конечном итоге будет иметь значение false
или true
соответственно.
Таким образом, в случае вычисления p == NULL
до true
нам не нужно вычислять *p == 0
, чтобы знать, что результатом ||
будет true
. Это делает эти логические выражения похожими на операторы if
. Мы будем точно знать, что p
не является нулевым указателем, когда *p == 0
будет выполнено.
Нет, это не то же самое.
p == NULL
проверяет адрес, хранящийся в указателе.*p == 0
разыменовывает указатель и проверяет, на что он указывает.