0
void main()
{
  int i=-3, j=2, k=0,m;
  m = ++i && ++j || ++k;
  printf("\n%d %d %d %d",i,j,k,m);
}

This snippet prints -2 3 0 1 but why not -2 3- 1 1 . Eventhough the ++ operator of k has precedence over logical operator, why k is not incremented because of short circuiting? More generally how to apply precendence when there are logical operators and side effects involved?

muru
  • 111
  • 1
  • 1
  • 10

2 Answers2

1

Short circuiting here means if the left operand of the logical AND operator && evaluates to false, then it won't evaluate its right operand. Similarly, if the left operand of the logical OR operator || evaluates to true, then its right operand is not evaluated. That's because, then the result is not affected whatever the right operand might evaluate to.

Also, && has higher precedence than || operator.

Therefore, the expression m = ++i && ++j || ++k; is equivalent to

m = (++i) && (++j || ++k);

The left operand ++i evaluates to -2 which is true (non-zero). Therefore, now the second expression (++j || ++k) is evaluated. The left operand of || operator is ++j which evaluates to 3, i.e., true. This means the right operand of ||, i.e., the expression ++k is not evaluated.

Therefore, only the sub-expressions ++i and ++j get evaluated. The whole expression evaluates to 1 as it is true.

ajay
  • 9,402
  • 8
  • 44
  • 71
0

Firstly, the main function (unless you use ancient Turbo C) should return int.

Secondly, a decent compiler (clang for me) complains about the dubious expression:

clang++ -S -mllvm --x86-asm-syntax=intel ls.cpp 
ls.cpp:5:11: warning: '&&' within '||' [-Wlogical-op-parentheses]
  m = ++i && ++j || ++k;
      ~~~~^~~~~~ ~~
ls.cpp:5:11: note: place parentheses around the '&&' expression to silence this warning
  m = ++i && ++j || ++k;
          ^
      (         )
1 warning generated.

Thirdly, let's see the assembly output:

    mov dword ptr [rbp - 8], -3       # this is i
    mov dword ptr [rbp - 12], 2       # this is j
    mov dword ptr [rbp - 16], 0       # this is k

    mov eax, dword ptr [rbp - 8]      # this is i++
    add eax, 1
    mov dword ptr [rbp - 8], eax
    cmp eax, 0                        # is i++ == 0?
    je  .LBB0_2
# BB#1:                               # no, i++ is NOT 0
    mov al, 1
    mov ecx, dword ptr [rbp - 12]     # this is j++
    add ecx, 1
    mov dword ptr [rbp - 12], ecx     
    cmp ecx, 0
    mov byte ptr [rbp - 21], al       # store al as a flag somewhere else
                                      # it will be the final result of the operation
    jne .LBB0_3                       # if j++ was NOT zero, go down to LBB0_3


.LBB0_2:                             # Yes, i++ is 0. Did you remark that
                                         # it didn't increment j in this case?
                                         # but it needs to increment k since the first
                                         # part of the OR evaluated to false.
    mov eax, dword ptr [rbp - 16]    # This is k++
    add eax, 1
    mov dword ptr [rbp - 16], eax
    cmp eax, 0                       # is c++ 0?
    setne   cl                       # if yes, set cl as a flag to 1
    mov byte ptr [rbp - 21], cl # 1-byte Spill

    # and here we get if j++ was not zero, 
    # so did you remark that k was not incremented?
.LBB0_3:
    mov al, byte ptr [rbp - 21] # 1-byte Reload
    lea rdi, qword ptr [.L.str]
    and al, 1
    movzx   ecx, al
    mov dword ptr [rbp - 20], ecx

    # there goes the printf    
    mov esi, dword ptr [rbp - 8]
    mov edx, dword ptr [rbp - 12]
    mov ecx, dword ptr [rbp - 16]
    mov r8d, dword ptr [rbp - 20]
    mov al, 0
    call    printf
Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • Do you really think that dumping assembly in front of someone who doesn't understand basic operators would help? – devnull Apr 29 '14 at 07:25
  • Together with explanations I think it's a good point to start. But let's see what the future will bring. – Ferenc Deak Apr 29 '14 at 07:34