221

I've been thinking of some beginner mistakes and I ended up with the one on the if statement. I expanded a bit the code to this:

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

I have seen that the if statement returns true, and it cout's i as 1. If i is assigned 1 in the if statement, why did i == 0 return true?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
TehMattGR
  • 1,710
  • 2
  • 7
  • 19
  • 83
    Guys, this is not a typo question. The OP wants to know why the if statement is entered with this code since `i` is set to `1`. – NathanOliver May 22 '19 at 20:40
  • 25
    Or does it assign the result of `1 && i == 0`? – JVApen May 22 '19 at 20:43
  • 4
    Suggestion for beginners: They should not use such "advanced" language construct. Just assign the variable separately. That will also avoid possible problems with sequence point. This kind of code in practical code will usually looks bad too. – user202729 May 23 '19 at 13:36
  • 2
    this is bound to end up on an interview question – RAZ_Muh_Taz May 23 '19 at 22:13

4 Answers4

398

This has to do with operator precedence.

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

is not

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

because both && and == have a higher precedence than =. What it really works out to is

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

which assigns the result of 1 && (i == 0) to i. So, if i starts at 0 then i == 0 is true, so 1 && true is true (or 1), and then i gets set to 1. Then since 1 is true, you enter the if block and print the value you assigned to i.

syck
  • 2,984
  • 1
  • 13
  • 23
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
18

Assuming your code actually looks like this:

#include <iostream>
using namespace std;

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

Then this:

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

evaluates as

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

and so i is set to 1.

  • 40
    Was the extra code really necessary? It seems pretty obvious that this would be the case as it would not run otherwise. – IllustriousMagenta May 23 '19 at 08:09
  • 13
    Not only unnecessary extra code. The answer fails to clearly explain operator precedence. – Francisco Zarabozo May 23 '19 at 08:35
  • 35
    Since we're on the nitpick train... I see a `using namespace std`! – Mateen Ulhaq May 23 '19 at 10:15
  • 8
    There is extra code - but it is still not incorrect code. And the answer is still right. Of course, it does not explain operator precedence. But someone could suggest it to be added, instead of outright down-voting! – B Charles H May 23 '19 at 14:20
  • 15
    Wow, -4 is harsh, considering this answers the question correctly, though maybe not optimally. It doesn't expand on operator precedence as much as the other answer, but it does say just enough about it in the context of the code so that anyone who thought that `=` came before `&&` can see the issue. Also, yes, the expansion is extraneous, but I don't think it matters that much. I can't believe such minor differences cause people to vote 151 to -4. – JoL May 23 '19 at 15:21
  • 6
    Also, I see that the expansion is not so extraneous if it's based on one of the first few revisions of the question. Mateen nitpicks on `using namespace std`, but Neil only added it because the first revision of TehMattGR's question needed it to make sense. This is one of those cases where I wish I could downvote comments. – JoL May 23 '19 at 15:29
  • 6
    @JoL i feel like the vote discrepancy seen here is a canonical example of what a lot of stackoverflow has become... i know 40+ people both offline/online who never contribute because of... reasons. – Trevor Boyd Smith May 23 '19 at 21:23
  • 2
    @JoL It does not need `using namespace std` to make sense. `using std::cout` does the job perfectly well, without the infamous problems of the former. – David Conrad May 24 '19 at 18:39
  • 2
    @DavidConrad It doesn't matter in the context of the question, though, does it? The question used simply `cout` in the first revision and Neil expanded to the most likely guess of what OP had in his file just to communicate that he's assuming that `cout` is from `std` and not something else. It's not like Neil is promoting the use of `using namespace std` here. If that's the reason why votes are 251 to 4 right now for practically identical answers posted at practically the same time and why when I got here Neil had -4, I find this totally absurd. – JoL May 24 '19 at 19:09
  • 2
    @JoL Putting it in a StackOverflow answer certainly is promoting it. – David Conrad May 24 '19 at 21:57
  • @DavidConrad You put it in a StackOverflow comment. By the same logic, is that not promoting it? What's the difference? As far as I can see neither of you recommended nor made any praise of it. Neil is just guessing what I believe anyone would agree are the most likely, however awful, surroundings of OP's code in the first revision. I'm astounded by the community reaction here, but I'm also surprised that you can say with such conviction that this behavior from the community is warranted. This page really leaves a bad taste in my mouth. – JoL May 24 '19 at 22:40
  • 2
    @JoL I put it in a comment criticizing its use and pointing out how it can be avoided. People look at SO answers as a model to imitate. When a newbie comes along and sees an answer from someone with 28k reputation, they may well assume that the code is good code unless there are caveats. This answer isn't so bad, but it adds nothing to the accepted answer while modeling a bad practice without mentioning it as such. If it is "however awful", the answer ought to say as much. – David Conrad May 24 '19 at 22:57
-3

The actual answer is:

  1. The compiler gives precedence to "i == 0", which evaluates to true.
  2. Then it will evaluate i=1 as TRUE or FALSE, and since compiled assignment operators never fail (otherwise they wouldn't compile), it also evaluates to true.
  3. Since both statements evaluate as true, and TRUE && TRUE evaluates to TRUE, the if statement will evaluate to TRUE.

As proof, just look at the asm output of your compiler for the code you entered (all comments are my own):

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

The asm output above was from CLANG, but all other compilers I looked at gave similar output. This is true for all the compilers on that site, whether they are pure C or C++ compilers, all without any pragmas to change the mode of the compiler (which by default is C++ for the C++ compilers)

Note that your compiler did not actually set i=1, but i=TRUE (which means any 32-bit not zero integer value). That's because the && operator only evaluates whether a statement is TRUE or FALSE, and then sets the results according to that result. As proof, try changing i=1 to i=2 and you can observe for yourself that nothing will change. See for yourself using any online compiler at Compiler Explorer

ar18
  • 335
  • 2
  • 5
  • 2
    1) The docs link to C operator precedence, when this question is tagged with C++. Two different languages. 2a) `i = 1` is an assignment [not an equivalence] operator; 2b) I can assure you that `if (i = 0)` will evaluate to false condition in both C and C++, so whether it evaluates to true wrt "it never fails" is somewhat misleading. – TrebledJ May 29 '19 at 05:11
  • 1
    `and cl, 1 ; = operator always TRUE` << correct me if I am wrong, but I see no assignation here. It represents the `1 &&` part of the expression. So this answer basically evaluates to `false`. – syck May 29 '19 at 17:04
  • "The docs link to C operator precedence, when this question is tagged with C++. Two different languages" -- and when you compare C to C++ operator precedence, what is the difference between the two? They have the same precedence in regards to this topic, which isn't surprising, seeing as C++ is a direct derivative of C (or another way to put it is, C is a subset of the C++ language, so of course they will have a lot in common, including precedence). I will fix my post anyways, in case that is confusing. – ar18 May 30 '19 at 14:53
  • "Correct me if I am wrong, but I see no assignation here" -- Then let me correct you! The 1 is an immediate value and not the result of any test or calculation. It is what is called a "presumed TRUE" value. The only test that takes place is for the i==0 statement, i.e. -- "cmp dword ptr [rbp - 8], 0". You would only be correct if it had said "movzx edx,1". According to ALL the posts preceding mine, there should be two comparisons, but in real life there is only one, and the asm output of EVERY major compiler proves those posts are completely incorrect. – ar18 May 30 '19 at 23:43
  • 2
    As well as getting the precedence wrong (see NathanOliver's answer for the correct parsing), you make the false claim that the assignment operator always evalutes to TRUE. Try `if ( i = 0 ) { print something }`. Also your answer contradicts itself; at the start you say that `i=1` is evaluated before `&&` is applied, and then at the end you say that `i` is set to the result of the `&&` operator. – M.M May 31 '19 at 00:02
  • `and cl,1` corresponds to `1 && cl` storing the result in `cl`. The previous value of `cl` was the result of `i == 0`. Then `mov dword ptr [rbp - 8], edx` stores the result of the `and` (which was copied from `cl` to `edx`) into the memory location of `i`. – M.M May 31 '19 at 00:10
  • "As well as getting the precedence wrong" -- Well you better tell every compiler manufacture in the world that they got it wrong. Good luck on that! – ar18 Jun 01 '19 at 13:33
  • "Then mov dword ptr [rbp - 8], edx stores the result of the and (which was copied from cl to edx)" -- That is just a guess and it isn't correct. Like I said, change the 1 to a 2 AND NOTHING WILL CHANGE. – ar18 Jun 01 '19 at 13:37
  • 1) The code I provided was generated by a C++ compiler and there was no pragma to place in the C mode 2) The output of ALL the compilers on Compiler Explorer tells us what the precedence is -- anything else is blind faith assertion 3) Like I said, changing the code doesn't change the result, which completely contradicts your claim of what the program is doing in assembly. – ar18 Oct 02 '19 at 23:02
-4

It has to do with parsing an the right to left rules. Eg y = x+5.
All sub-expressions are weighted in importance. Two expressions of equal importance are evaluated right to left, . The && expression side is done first, followed by the LHS.

Makes sense to me.