Могу ли я использовать if (указатель) вместо if (указатель! = NULL)?

171

Безопасно ли проверять указатель на отсутствие NULLнаписания просто if(pointer)или я должен использовать if(pointer != NULL)?

Данияр
источник
13
Правда в том, что если вы собираетесь использовать явную проверку, то столь же эффективно - и часто предпочтительнее - проверять на 0или nullptr. ( NULLявляется C'ism и требует включения заголовочного файла.)
cHao
5
@danijar Вы можете использовать nullptr в современном C ++.
SurvivalMachine
9
@cHao Где смысл "стремиться к совместимости с C"?
QDII
5
@danijar: Да, вы не должны использовать его NULLв C ++, потому что NULLэто макрос, зависящий от реализации, который может дать вам неоднозначное поведение.
Alok Save
3
Хотя это не случай «если», посмотрите в этой демонстрации ideone live, почему вы должны избегать «NULL» и «0» для указателей в C ++: ideone.com/tbvXNs
kfsone

Ответы:

198

Ты можешь; нулевой указатель неявно преобразуется в логическое значение false, а ненулевые указатели преобразуются в истину. Из стандарта C ++ 11, раздел о булевых преобразованиях:

Значение арифметики, перечисление с незаданной областью, указатель или указатель на тип элемента может быть преобразовано в значение типа bool. Нулевое значение, нулевое значение указателя или нулевое значение указателя члена преобразуется в false; любое другое значение преобразуется в true . Значение типа std::nullptr_t может быть преобразовано в значение типа bool ; результирующее значение false .

Joni
источник
42

Да, ты мог.

  • Пустой указатель неявно преобразуется в ложный
  • ненулевой указатель преобразуется в true.

Это часть стандартного преобразования C ++, которое входит в условие преобразования Boolean :

§ 4.12. Булевы преобразования

Значение арифметики, перечисление с незаданной областью, указатель или указатель на тип члена может быть преобразовано в значение типа bool. Нулевое значение, нулевое значение указателя или нулевое значение указателя члена преобразуется в ложь; любое другое значение преобразуется в true. Значение типа std :: nullptr_t может быть преобразовано в значение типа bool; результирующее значение ложно.

billz
источник
29

Да, ты можешь. На самом деле, я предпочитаю использовать, if(pointer)потому что читать и писать проще, когда вы к этому привыкнете.

Также обратите внимание, что в C ++ 11 введено, nullptrчто предпочтительнее, чем NULL.

Ю Хао
источник
10
Указатель не является логическим выражением. Это конвертируется неявно. Если вам лучше читать, когда вам нужно запомнить это преобразование, чтобы понять, это ваше мнение. Это всего лишь один из видов стиля кодирования.
Harper
7
@harper Можно сказать, что это стиль кодирования. Но вы можете применить ту же логику к if(som_integer)vs, if(some_integer != 0)потому что целые числа также не булевы, верно? Я предпочитаю избегать 0или NULLв if-утверждении.
Ю Хао
13
Я бы согласился, что это просто вопрос стиля кодирования. Я пришел, чтобы предпочесть if (pointer)себя, но мне if (ptr != nullptr)кажется вполне законным. С другой стороны, если бы я увидел кого-то в моей команде, который написал, if (some_integer)я бы заставил его изменить его if (some_integer != 0). Однако я не буду притворяться, что это не относительно произвольное предпочтение с моей стороны - я просто предпочитаю не обращаться с указателями и целыми числами одинаково.
Джоэл
1
@YuHao И так как это стиль кода, я бы сказал не «это предпочтительнее», а «я предпочитаю».
Арфист
5
@ franji1 Тогда как насчет if(isReady) if(filePtr) if(serviceNo)? Создание неправильных имен переменных не имеет большого значения в этом случае. В любом случае, я уже понял вашу точку зрения и понял ее, но я могу сам настаивать на использовании своего собственного стиля кодирования, хорошо?
Ю Хао
13

На вопрос дан ответ, но я бы хотел добавить свои очки.

Я всегда буду предпочитать if(pointer)вместо if(pointer != NULL)и if(!pointer)вместо if(pointer == NULL):

  • Это просто, маленький
  • Меньше шансов написать ошибочный код, предположим, что если я неправильно опишу оператор проверки равенства ==с =
    if(pointer == NULL)ошибкой, то if(pointer = NULL)я буду избегать этого, лучше всего просто if(pointer).
    (Я также предложил некоторые условия Йоды в одном ответе , но это разные вопросы)

  • Аналогично while (node != NULL && node->data == key), я просто напишу, while (node && node->data == key)что для меня более очевидно (показывает, что используется короткое замыкание).

  • (может быть глупой причиной) Поскольку NULL является макросом, если предположить, что один из них переопределен по ошибке с другим значением.
Грижеш Чаухан
источник
6
Использование = вместо == почти всегда генерирует предупреждение компилятора, в те дни, когда этого не было, люди использовали бы if (NULL == ptr)
paulm
@ paulm, что я только что добавил этот пункт, который называется « Состояние Йоды», и некоторые люди не любят его, так как он менее читабелен.
Грижеш Чаухан
2
(boolean expression)? true : falseсовершенно бессмысленно. Выражение оценивает либо в, trueлибо в false; Вы говорите: «Если это правда, дай мне правду, если это ложь, дай мне ложь». Вкратце: это полностью эквивалентно самому логическому выражению. Обратите внимание, что node == NULLэто логическое выражение. Кстати, ваши две реализации возвращают в точности противоположные друг другу. Либо вы хотите !=в первом, или только один !во втором.
celtschk
Кстати, одна из возможных мер защиты =вместо того ==, чтобы делать ваши переменные, constкогда это возможно. Например, вы можете определить свою функцию как isEmnpy(node* const head) { ... }, и тогда компилятор откажется компилировать ее, если вы случайно написали node = NULLвместо node == NULL. Конечно, это работает только для переменных, которые вам не нужно менять.
celtschk
2
Потому что классы интеллектуальных указателей должны T* get() constвместо того, operator T*() constчтобы избежать неявных преобразований. Однако они имеют operator bool() const.
StellarVortex
13

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

введите описание изображения здесь

Minqi Pan
источник
8

Да, ты можешь. Возможность неявно сравнивать значения с нулями была унаследована от C и присутствует во всех версиях C ++. Вы также можете использовать if (!pointer)для проверки указателей на NULL.

dasblinkenlight
источник
2

Соответствующие варианты использования для нулевых указателей

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

    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
    if(derived_ptr != nullptr) { ... }

    (или, предпочтительно, auto derived_ptr = ...). Теперь это плохо, потому что он оставляет (возможно, недопустимый, то есть нулевой) производный указатель за пределами области действия защитного ifблока. В этом нет необходимости, поскольку C ++ позволяет вводить переменные, логически конвертируемые в if-условии :

    if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }

    который не только короче и безопаснее по объему, но и гораздо более понятен в своем намерении: когда вы проверяете на null в отдельном условии if, читатель задается вопросом: « derived_ptrХорошо, поэтому здесь не должно быть null ... ну, зачем это будет ноль? В то время как однострочная версия говорит очень просто: «Если вы можете безопасно привести base_ptrк Derived*, то используйте ее для ...».

    То же самое работает и для любой другой операции возможного сбоя, которая возвращает указатель, хотя в IMO следует избегать этого: лучше использовать что-то вроде boost::optional«контейнера» для результатов возможных неудачных операций, а не указателей.

Итак, если основной сценарий использования нулевых указателей всегда должен быть написан в варианте неявного стиля приведения, я бы сказал, что по соображениям согласованности всегда следует использовать этот стиль, т. Е. Я бы рекомендовал использовать if(ptr)более if(ptr!=nullptr).


Боюсь, я должен закончить рекламой: if(auto bla = ...)синтаксис - это всего лишь немного громоздкое приближение к реальному решению таких проблем: сопоставление с образцом . Зачем вам сначала предпринимать какие-то действия (например, приведение указателя), а затем считать, что может произойти сбой ... Я имею в виду, это смешно, не правда ли? Как будто у вас есть продукты и вы хотите приготовить суп. Вы передаете его своему помощнику с задачей извлечь сок, если он окажется мягким овощем. Вы сначала не смотрите на это. Когда у вас есть картошка, вы все равно отдаете ее своему помощнику, но они бьют ее по лицу с запиской об ошибке. Ах, императивное программирование!

Намного лучше: рассмотрите сразу все случаи, с которыми вы можете столкнуться. Тогда действуй соответственно. Haskell:

makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
 | isSoft vegetable  = squeeze vegetable <> salt
makeSoupOf stuff  = boil (throwIn (water<>salt) stuff)

На Haskell также есть специальные инструменты для случаев, когда существует серьезная вероятность отказа (как и для целого ряда других вещей): монады. Но это не место для объяснения этого.

⟨/объявление⟩

leftaroundabout
источник
1
Я вижу только одно предложение в этой бесконечной стяжке, которое фактически отвечает на вопрос.
Маркиз Лорн
@EJP: если вы воспринимаете вопрос буквально (« могу ли я использовать»), то на него вообще нет явного ответа (ответ просто «да»). Я попытался привести правильные причины того, почему ФП должен на самом деле использовать, if(ptr)а не то if(ptr != nullptr), на что есть что сказать.
оставил около
1

Ну конечно; естественно! на самом деле запись if (указатель) является более удобным способом записи, чем if (pointer! = NULL), потому что: 1. это легко отладить 2. легко понять 3. если случайно, значение NULL определено, тогда и код не вылетит

Палак джайн
источник
0

Как уже хорошо ответили другие, они оба взаимозаменяемы.

Тем не менее, стоит отметить , что может быть случай , когда вы можете использовать явное заявление, то есть pointer != NULL.

Смотрите также https://stackoverflow.com/a/60891279/2463963

z3moon
источник
-1

Да, вы всегда можете сделать это, поскольку условие «ЕСЛИ» оценивается только тогда, когда условие внутри него сбывается. C не имеет логического возвращаемого типа и, таким образом, возвращает ненулевое значение, когда условие истинно, и возвращает 0 всякий раз, когда условие в 'IF' оказывается ложным. Ненулевое значение, возвращаемое по умолчанию, равно 1. Таким образом, оба способа написания кода верны, а я всегда предпочитаю второй.

user2280507
источник
1
Ненулевое значение по умолчанию не определено, если я правильно помню.
Маркиз Лорн
-1

Я думаю, как правило, если ваше if-выражение может быть переписано как

const bool local_predicate = *if-expression*;
if (local_predicate) ...

так что это не вызывает никаких предупреждений, то это должно быть предпочтительным стилем для выражения if . (Я знаю, что получаю предупреждения, когда назначаю старый C BOOL( #define BOOL int) для C ++ bool, не говоря уже о указателях.)

franji1
источник
-1

"Это безопасно..?" это вопрос о стандарте языка и сгенерированном коде.

"Это хорошая практика?" это вопрос о том, насколько хорошо это утверждение понято любым произвольным человеком, читающим это утверждение. Если вы задаете этот вопрос, это говорит о том, что «безопасная» версия менее понятна будущим читателям и писателям.

Фред Митчелл
источник
Я хотел спросить, безопасно ли это? Поэтому я использовал эту формулировку. Однако то, что вы написали здесь, не является ответом на вопрос. Вместо этого должен быть комментарий под вопросом. Вы можете удалить ответ и добавить комментарий под вопросом.
Данияр
@danijar Разве вы не помните, когда вы были новичком в StackOverflow и безуспешно искали раздел «Комментарий»? Кто-то с 7 репутацией не может этого сделать.
Broxzier
@JimBalter Это очень сбивает с толку, так как вы можете видеть, как другие делают это. Когда я был новичком в SO, кто-то обвинил меня в этом.
Broxzier
@JimBalter Я не убиваю и не краду. Я говорил Дэниару, что Фред Митчелл - новый пользователь и не может оставлять комментарии.
Broxzier
@JimBalter Который вы начали сегодня. Также вы тот, кто не понимает вместо этого. Этот комментарий только поддерживает замешательство этого.
Broxzier