奇怪的printf行为?

时间:2010-12-09 17:12:32

标签: c

[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

我不知道为什么会这件事发生。任何人都可以 我详细解释了这里发生了什么?

4 个答案:

答案 0 :(得分:6)

C没有定义函数调用参数的评估顺序。你在那里遇到麻烦;)。

更新:
澄清定义的内容和不定义的内容:

  

函数指示符的评估顺序,实际参数和   实际参数中的子表达式未指定,但有一个序列点    在实际通话之前。

从ISO / IEC 9899:1999,第6.5.2.2节,函数调用

答案 1 :(得分:1)

这是main(我们需要的部分)的反汇编:

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

现在,由于参数以相反的顺序被压入堆栈,反汇编显示第一个被推送的是ecx,所以我们可以假设它是++ i(因为它是printf中的最后一个参数),所以edx是i ++ 。奇怪的是,它决定先计算i ++,然后计算++ i。最后它加载i并推动它,但是,此时,我已经增加了两次,所以它是12。 这确实是未定义的行为!看:

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

产生

++i=12
i++=10
i=12

答案 2 :(得分:0)

检查此StackOverflow链接以获取有关参数评估顺序的更多信息:

Compilers and argument order of evaluation in C++

答案 3 :(得分:-2)

它与 C 上的ii++++i表达式的评估顺序有关。例如没关系,但是如果你想避免奇怪的问题,在实际代码中不要依赖那个命令。