Чому це твердження if, що поєднує призначення та перевірку рівності, повертає істину?


216

Я думав про деякі помилки початківців, і закінчився тим, що було ifзаявлено. Я трохи розширив код до цього:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

Я бачив , що ifоператор повертає вірно, і це cout«S , iяк 1. Якщо iпризначається 1в заяві , якщо, чому зробив i == 0повернення true?


83
Хлопці, це не питання друку. ОП хоче знати, чому оператор if вводиться з цим кодом, оскільки iвстановлено 1.
NathanOliver

24
Або присвоює результат 1 && i == 0?
JVApen

4
Пропозиція для початківців: Вони не повинні використовувати таку «просунуту» мовну конструкцію. Просто призначте змінну окремо. Це також дозволить уникнути можливих проблем з точкою послідовності. Цей вид коду в практичному коді зазвичай теж виглядає погано.
користувач202729

1
це обов'язково закінчиться питанням інтерв'ю
RAZ_Muh_Taz

Відповіді:


391

Це стосується переваги оператора .

if (i = 1 && i == 0)

не

if ((i = 1) && (i == 0))

тому що обидва &&і ==мають вищий пріоритет ніж =. Що це насправді працює

if (i = (1 && (i == 0)))

який присвоює результат 1 && (i == 0)до i. Отже, якщо iзапускається в 0той час i == 0є true, то 1 && trueє true(або 1), а потім iвстановлюється 1. Тоді оскільки 1це правда, ви вводите блок if і друкуєте значення, яке вам призначено i.


2
@ JörgWMittag Це дуже круто. Мені подобається, що це змушує вас використовувати дужки.
NathanOliver

В основному i = !i; if (i)правильно написано
Cacahuete Frito

@NathanOliver: Фортеця була досить класною мовою, яка дуже багато зрозуміла. (Головним дизайнером був Гай Л. Стіл, тому немає ніякого сюрпризу.) На жаль, його не взяли до фінального раунду фінансування DARPA, а згодом його зафіксували Oracle.
Йорг W Міттаг

6
Природно, прошу крапельку попереджень від компілятора виявив би , що помилка,
Deduplicator

4
Будь-яка мова, яка не передбачає, що вбудовані та булеві символи є рівноцінними, також підбирає це.
rghome

16

Припустимо, що ваш код насправді виглядає так:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

Тоді це:

if (i = 1 && i == 0) {

оцінює як

 if (i = (1 && i == 0)) {

і так iвстановлено 1.


39
Чи дійсно потрібен був додатковий код? Здається, досить очевидно, що це було б так, як інакше б не працювати.
Modelmat

13
Не тільки непотрібний додатковий код. Відповідь не дозволяє чітко пояснити перевагу оператора.
Франциско Зарабобозо

34
Оскільки ми на поїзді "Нітпік" ... Я бачу using namespace std!
Матін Ульхак

8
Є додатковий код - але це все ще невірний код. І відповідь все-таки правильна. Звичайно, це не пояснює перевагу оператора. Але хтось може запропонувати його додати замість прямого голосування!
Б Чарльз Н

14
Нічого собі, -4 - це суворо, вважаючи, що це питання відповідає правильно, хоча, можливо, не оптимально. Він не розширюється на пріоритет оператора так само, як на іншу відповідь, але він говорить про нього досить просто в контексті коду, щоб кожен, хто думав, що =прийшов раніше, &&міг бачити проблему. Крім того, так, розширення є стороннім, але я не думаю, що це має велике значення. Я не можу повірити, що такі незначні відмінності змушують людей голосувати від 151 до -4.
JoL

-4

Це пов'язано з розбором правил справа наліво. Наприклад, y = x + 5.
Всі під вирази зважуються за значенням. Два вирази однакової важливості оцінюються праворуч ліворуч,. Сторона вираження && робиться спочатку, після чого - LHS.

Має сенс для мене.


1
Асоціативність ("правила праворуч ліворуч") не має нічого спільного з цим. Йдеться про пріоритетність ("важливість"), і використовувані оператори не мають однакового переваги.
Гонки легкості по орбіті

-4

Фактична відповідь:

  1. Компілятор надає перевагу "i == 0", що оцінює істинно.
  2. Тоді він оцінить i = 1 як TRUE або FALSE, а оскільки компільовані оператори присвоєння ніколи не виходять з ладу (інакше вони не збираються), він також оцінює як true.
  3. Оскільки обидва твердження оцінюються як істинні, а TRUE && TRUE оцінюється як TRUE, оператор if оцінюється як TRUE.

Як доказ, просто подивіться на вихід ASM вашого компілятора для коду, який ви ввели (усі коментарі - це мої власні):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

Вихід ASM вище був від CLANG, але всі інші компілятори, на які я дивився, дали подібний вихід. Це справедливо для всіх компіляторів на цьому веб-сайті, чи є вони чистими компіляторами C або C ++, і всі без будь-яких прагм змінити режим компілятора (який за замовчуванням є C ++ для компіляторів C ++)

Зауважте, що ваш компілятор насправді не встановив i = 1, але i = TRUE (це означає, що будь-яке 32-бітове не нульове ціле число). Це тому, що оператор && оцінює лише те, чи твердження є ІСТИЧНИМ чи ЛІЖНИМ, і потім встановлює результати відповідно до цього результату. Як доказ, спробуйте змінити i = 1 на i = 2, і ви можете самі помітити, що нічого не зміниться. Побачте, використовуючи будь-який онлайн-компілятор у Провідник Compiler


1
1) Документи посилаються на пріоритет оператора C, коли це питання позначено тегом C ++. Дві різні мови. 2а) i = 1- оператор присвоєння [не еквівалент]; 2б) Я можу запевнити, що if (i = 0)оцінюватимуть хибний стан як у C, так і у C ++, тож чи оцінюється воно як справжнє wrt, "воно ніколи не виходить з ладу", є дещо оманливим.
TrebledJ

1
and cl, 1 ; = operator always TRUE<< виправте мене, якщо я помиляюся, але я не бачу присвоєння тут. Він являє собою 1 &&частину виразу. Тож ця відповідь в основному оцінюється на false.
syck

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

"Виправте мене, якщо я помиляюся, але я не бачу тут присвоєння" - Тоді дозвольте виправити вас! 1 - це безпосереднє значення, а не результат будь-якого тесту чи розрахунку. Це те, що називається "імовірним істинним" значенням. Єдиний тест, який має місце для твердження i == 0, тобто - "cmp dword ptr [rbp - 8], 0". Ви будете правильні, якби він сказав "movzx edx, 1". Згідно ВСІХ повідомлень, що передували моєму, слід порівняти два, але в реальному житті є лише одне, і вихід ASM ВСЕГО головного компілятора доводить, що ці пости є абсолютно невірними.
ar18

2
Окрім помилки пріоритету (див. Відповідь NathanOliver для правильного розбору), ви заявляєте помилкове твердження, що оператор присвоєння завжди оцінює TRUE. Спробуйте if ( i = 0 ) { print something }. Також ваша відповідь суперечить самій собі; на початку ви говорите, що i=1оцінюється раніше &&, а потім в кінці ви говорите, що iвстановлено на результат &&оператора.
ММ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.