【问题标题】:Strange printf behaviour? [duplicate]奇怪的 printf 行为? [复制]
【发布时间】:2011-05-23 00:20:38
【问题描述】:
[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

我不知道为什么会发生这种情况。请问任何人都可以 详细解释一下这里发生了什么?

【问题讨论】:

  • 不是printf 导致了奇怪的行为。
  • 你应该知道,编写测试程序(我希望这是一个测试,而不是在生产代码中使用)是好的,但在一个编译器上获得预期(或意外)的结果并不能保证其他编译器的行为方式相同。 secure.wikimedia.org/wikipedia/en/wiki/Sequence_point阅读Sequence points in C and C++下的第4项

标签: c


【解决方案1】:

C 没有定义函数调用参数的求值顺序。你有麻烦了;)。

更新:
澄清什么是定义的,什么不是:

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

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

【讨论】:

【解决方案2】:

这与 C 上的 ii++++i 表达式的评估顺序有关。举个例子是可以的,但在实际代码中,如果您想避免奇怪的问题,请不要依赖该顺序。

【讨论】:

    【解决方案3】:

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

    Compilers and argument order of evaluation in C++

    【讨论】:

      【解决方案4】:

      这是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 并推送它,但是此时 i 已经增加了两倍,所以它是 12。 这真的是未定义的行为!看:

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

      产生:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-26
        • 1970-01-01
        • 2012-10-03
        相关资源
        最近更新 更多