0
[cprg]$ cat test.c
#include  <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
        int i=10;
        printf("i=%d\ni++=%d\n++i=%d\n",i,i++,++i);
        return 0;
}
[cprg]$ make
gcc -g -Wall -o test test.c
test.c: In function ‘main’:
test.c:7: warning: operation on ‘i’ may be undefined
test.c:7: warning: operation on ‘i’ may be undefined
[cprg]$ ./test
i=12
i++=11
++i=12

I have no idea why this thing is happening. Please can anyone explain me in detail as to what is happening here ?

Onkar Mahajan
  • 944
  • 2
  • 13
  • 16
  • 4
    It's not `printf` that's causing the strange behavior. – Fred Larson Dec 09 '10 at 17:18
  • You should be aware that writing test programs (I hope this is a test and not used in production code) is fine and dandy but getting an expected (or unexpected) result on one compiler is no guarantee that other compilers will behave the same way. https://secure.wikimedia.org/wikipedia/en/wiki/Sequence_point Read the 4th item under **Sequence points in C and C++** – Praetorian Dec 09 '10 at 17:25

4 Answers4

6

C does not define in which order function call arguments get evaluated. You are in for trouble there ;).

Update:
To clarify what is defined and what not:

The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

From ISO/IEC 9899:1999, Section 6.5.2.2, Function calls

Marcus Borkenhagen
  • 6,536
  • 1
  • 30
  • 33
1

Here's the disassembly of main (the part we need):

0x080483ed <+9>:    movl   $0xa,0x1c(%esp)         # initializes i
0x080483f5 <+17>:   addl   $0x1,0x1c(%esp)         # i += 1
0x080483fa <+22>:   mov    0x1c(%esp),%edx        # edx = i = 11
0x080483fe <+26>:   addl   $0x1,0x1c(%esp)         # i += 1
0x08048403 <+31>:   mov    $0x80484f0,%eax         # address of string
0x08048408 <+36>:   mov    0x1c(%esp),%ecx        # ecx = i = 12
0x0804840c <+40>:   mov    %ecx,0xc(%esp)         # pushes ecx (++i)
0x08048410 <+44>:   mov    %edx,0x8(%esp)         # and edx (i++)
0x08048414 <+48>:   mov    0x1c(%esp),%edx        # now gets edx (i)
0x08048418 <+52>:   mov    %edx,0x4(%esp)         # and pushes it
0x0804841c <+56>:   mov    %eax,(%esp)            # push address of string
0x0804841f <+59>:   call   0x804831c <printf@plt> # write

now, since arguments are pushed on the stack in reverse order, the disassembly shows that the first that is pushed is ecx, so we can assume it's ++i (since it's the last argument in printf), so edx is i++. Strangely, it decides to compute first i++, then ++i. At the end it loads i and pushes it, but, at this point, i has been increased two times, so it's 12. This is really undefined behavior! Look:

printf("++i=%d\ni++=%d\ni=%d\n",++i,i++,i);

produces:

++i=12
i++=10
i=12
Gabe
  • 84,912
  • 12
  • 139
  • 238
BlackBear
  • 22,411
  • 10
  • 48
  • 86
0

Check this StackOverflow link for further information on argument evaluation order:

Compilers and argument order of evaluation in C++

Community
  • 1
  • 1
Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
-2

It has to do with evaluation order of i, i++ and ++i expression on C. As an example is OK, but in real code do not rely on that order if you want to avoid weird issues.

Pablo Santa Cruz
  • 176,835
  • 32
  • 241
  • 292