Почему этот оператор if, объединяющий присваивание и проверку на равенство, возвращает true?


217

Я думал о некоторых ошибках новичка, и я закончил тем, что на ifутверждении. Я немного расширил код до этого:

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

Я видел , что ifоператор возвращает верно, и это cout«S , iкак 1. Если в операторе if iназначено 1значение, почему i == 0вернули true?


83
Ребята, это не опечатка. ОП хочет знать, почему оператор if вводится с этим кодом, поскольку для iнего установлено значение 1.
— Натан Оливер

24
Или это присваивает результат 1 && i == 0?
— JVApen

4
Предложение для начинающих: им не следует использовать такую ​​«продвинутую» языковую конструкцию. Просто назначьте переменную отдельно. Это также позволит избежать возможных проблем с точкой последовательности. Этот вид кода в практическом коде также обычно выглядит плохо.
— user202729

1
это должно закончиться на вопросе интервью
— RAZ_Muh_Taz

Ответы:


392

Это связано с приоритетом оператора .

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. Тогда, поскольку 1true, вы вводите блок if и печатаете значение, которое вы присвоили i.


2
@ JörgWMittag Это круто. Мне нравится, что это заставляет вас использовать скобки.
— Натан Оливер

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

@NathanOliver: Крепость была довольно классным языком, в котором многое было правильно. (Главным дизайнером был Гай Л. Стил, так что неудивительно.) К сожалению, он не был выбран для финального раунда финансирования DARPA, и впоследствии был одобрен Oracle.
— Йорг Миттаг

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

Это связано с разбором правил справа налево. Например, у = х + 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-битное не нулевое целое значение). Это связано с тем, что оператор && только оценивает, является ли оператор TRUE или FALSE, а затем устанавливает результаты в соответствии с этим результатом. В качестве доказательства попробуйте изменить i = 1 на i = 2, и вы сами сможете убедиться, что ничего не изменится. Убедитесь сами, используя любой онлайн-компилятор в Compiler Explorer


1
1) Документы ссылаются на приоритет оператора C, когда этот вопрос помечен как C ++. Два разных языка. 2a) i = 1является оператором присваивания [не эквивалентности]; 2b) Я могу заверить вас, что if (i = 0)в C и C ++ будет выполнено ложное условие, поэтому то, что оно оценивается как истинное по отношению к «никогда не выходит из строя», несколько обманчиво.
— TrebledJ

1
and cl, 1 ; = operator always TRUE<< поправьте меня, если я ошибаюсь, но я не вижу здесь никакого назначения. Он представляет собой 1 &&часть выражения. Так что этот ответ в основном оценивается как false.
— Сик

«Документы ссылаются на приоритет оператора 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установлено на результат &&оператора.
— MM
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.