Чи можу я використовувати, якщо (вказівник) замість 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: Так, ви не повинні використовувати NULLC ++ з цього приводу, тому що NULLмакрос залежний від реалізації, який може викликати неоднозначне поведінку.
Алок Зберегти

3
Хоча це не випадок "якщо", перегляньте цю демо-версію ideone щодо того, чому вам слід уникати "NULL" та "0" для покажчиків у C ++: ideone.com/tbvXNs
kfsone

Відповіді:


197

Ти можеш; нульовий покажчик неявно перетворюється на булеву помилку, тоді як ненульові покажчики перетворюються на істинні. Зі стандарту C ++ 11, розділ " Булеві перетворення":

Первинне значення арифметичного, нескопаного перерахування, вказівника або вказівника на тип члена може бути перетворене в перше значення типу bool. Нульове значення, нульове значення вказівника або значення вказівника нульового члена перетворюється в false; будь-яке інше значення перетворюється в true . Первісне значення типу std::nullptr_t може бути перетворене в первісне значення типу bool ; отримане значення дорівнює false .


42

Так, ви могли.

  • Нульовий покажчик неявно перетворюється на false
  • ненульовий покажчик перетворюється на true.

Це частина стандартної конверсії C ++, яка впадає в булеву статтю перетворення :

§ 4.12 Булеві перетворення

Первісне значення арифметичного, нескопаного перерахування, вказівника або вказівника на тип члена може бути перетворене в перше значення типу bool. Нульове значення, нульове значення вказівника або значення вказівника нульового члена перетворюється на помилкове; будь-яке інше значення перетворюється на істинне. Первісне значення типу std :: nullptr_t може бути перетворене в перше значення типу bool; отримане значення хибне.


29

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

Також зауважте, що введено C ++ 11, nullptrяке є кращим NULL.


10
Вказівник - не булевий вираз. Це конвертується неявно. Якщо краще читати, коли вам потрібно запам'ятати цю конверсію, щоб зрозуміти, це ваша думка. Це лише один вид стилю кодування.
харпер

7
@harper Можна сказати, що це стиль кодування. Але ви можете застосувати ту саму логіку до if(som_integer)vs, if(some_integer != 0)оскільки цілі числа також не булеві, правда? Я вважаю за краще уникати 0або NULLу висловленні if.
Yu Hao

13
Я погодився б, це просто питання стилю кодування. Я віддавав перевагу if (pointer)собі, але if (ptr != nullptr)здається мені цілком законним. З іншого боку, якщо я бачив когось із своєї команди, який писав, if (some_integer)я би змусив їх змінити if (some_integer != 0). Однак я не буду робити вигляд, що це не відносно довільна перевага з мого боку - я просто вважаю за краще не обробляти покажчики та цілі числа однаково.
Джоель

1
@YuHao А оскільки це стиль коду, я б не зазначав "він кращий", але "я вважаю за краще".
харпер

5
@ franji1 Тоді як if(isReady) if(filePtr) if(serviceNo)? Навмисне створення неправильних імен змінних в цьому випадку не означає багато. У всякому разі, я вже зрозумів вашу думку і зрозумів це, але я можу наполягати на використанні власного стилю кодування, гаразд?
Yu Hao

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
Використання = замість == майже завжди генерує попередження про компілятор у дні, коли цього не використовували б люди, якщо (NULL == ptr)
пауль

@paulm, що я щойно додав цей пункт під назвою Yoda Condition, деяким людям це не подобається як його менш читабельний.
Grijesh Chauhan

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 може надати підказку компілятору про те, що ви намагаєтесь зробити, ерго призводить до менш схильної до помилок.

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


8

Так, ти можеш. Можливість порівнювати значення з нулями неявно успадкована від C, і є вона у всіх версіях C ++. Ви також if (!pointer)можете перевірити покажчики на NULL.


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тут не повинно бути нульовим ... ну чому б це буде нульовим? " В той час, як однолінійна версія дуже чітко говорить: "Якщо ви можете сміливо 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 також є спеціальні інструменти, коли є дійсно серйозна можливість виходу з ладу (як і для цілого ряду інших речей): монади. Але це не місце для їх пояснення.

⟨/ Реклама⟩


1
Я бачу лише одне речення в цій непереборній стяжці, яка насправді відповідає на питання.
Маркіз Лорн

@EJP: якщо ви ставитесь до питання буквально ("чи можу я використовувати"), то воно взагалі не відповідає чітко (відповідь просто "так"). Я намагався навести належні причини, чому ОП насправді слід використовувати, if(ptr)а не if(ptr != nullptr), про що можна сказати ще трохи.
близько

1

так, звісно! насправді, писати if (покажчик) є більш зручним способом написання, а не якщо (покажчик! = NULL), тому що: 1. це легко налагодити 2. легко зрозуміти 3. якщо випадково визначено значення NULL, тоді і код не вийде з ладу



0

Як вже добре відповіли інші, вони обоє взаємозамінні.

Тим не менше, варто згадати, що може бути випадок, коли ви можете скористатися явним твердженням, тобто pointer != NULL.

Дивіться також https://stackoverflow.com/a/60891279/2463963


-1

Так, ви завжди можете це зробити так, як "IF" умова оцінюється лише тоді, коли умова всередині неї справдиться. C не має булевого типу повернення і, таким чином, повертає ненульове значення, коли умова істинна, тоді як повертає 0, коли умова у "IF" виявляється помилковою. Ненульове значення, повернене за замовчуванням, становить 1. Таким чином, обидва способи написання коду є правильними, тоді як я завжди віддаю перевагу другому.


1
Ненульове значення за замовчуванням не визначене, якщо я правильно пам'ятаю.
Маркіз Лорн

-1

Я думаю, як правило, якщо ваш if-вираз можна переписати як

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

таким чином, що це спричиняє НЕ ПЕРЕГЛЯДУВАННЯ, тоді ТАКЕ повинен бути кращим стилем для виразу if . (Я знаю, що отримую попередження, коли я призначаю старий C BOOL( #define BOOL int) C ++ bool, не кажучи вже про вказівники.)


-1

"Це безпечно ..?" - питання про мовний стандарт та згенерований код.

"Це хороша практика?" це питання про те, наскільки добре це твердження розуміє будь-який довільний людський читач твердження. Якщо ви ставите це запитання, це говорить про те, що "безпечна" версія менш зрозуміла майбутнім читачам та письменникам.


Мій намір був запитати, чи безпечно це. Тому я використав це формулювання. Однак те, що ви тут написали, - це не відповідь на запитання. Натомість це має бути коментар під питанням. Ви можете видалити відповідь і додати коментар до цього питання.
danijar

@danijar Ви не пам’ятаєте, коли ви були новаком у StackOverflow та шукали розділ «Коментар» без успіху? Хтось із 7 репутацією не може цього зробити.
Broxzier

@JimBalter Це дуже заплутано, оскільки ви можете бачити інших, що роблять це. Коли я був новачок у ТАК, хтось звинувачував мене в цьому.
Broxzier

@JimBalter Я не вбивства та крадіжок. Я казав danijar, що Фред Мітчелл - новий користувач і не може публікувати коментарі.
Broxzier

@JimBalter З якого ви розпочали сьогодні. Також ви натомість не розумієте. Цей коментар лише підтримує заплутаність цього.
Broxzier
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.