我发现在没有优化和-Os
优化的情况下编译的以下代码的汇编程序结果之间存在差异。
#include <stdio.h>
int main(){
int i;
for(i=3;i>2;i++);
printf("%d\n",i);
return 0;
}
没有优化代码结果:
000000000040052d <main>:
40052d: 55 push %rbp
40052e: 48 89 e5 mov %rsp,%rbp
400531: 48 83 ec 10 sub $0x10,%rsp
400535: c7 45 fc 03 00 00 00 movl $0x3,-0x4(%rbp)
40053c: c7 45 fc 03 00 00 00 movl $0x3,-0x4(%rbp)
400543: eb 04 jmp 400549 <main+0x1c>
400545: 83 45 fc 01 addl $0x1,-0x4(%rbp)
400549: 83 7d fc 02 cmpl $0x2,-0x4(%rbp)
40054d: 7f f6 jg 400545 <main+0x18>
40054f: 8b 45 fc mov -0x4(%rbp),%eax
400552: 89 c6 mov %eax,%esi
400554: bf f4 05 40 00 mov $0x4005f4,%edi
400559: b8 00 00 00 00 mov $0x0,%eax
40055e: e8 ad fe ff ff callq 400410 <printf@plt>
400563: b8 00 00 00 00 mov $0x0,%eax
400568: c9 leaveq
400569: c3 retq
,输出为:-2147483648(正如我在电脑上预期的那样)
使用-Os代码结果:
0000000000400400 <main>:
400400: eb fe jmp 400400 <main>
我认为第二个结果是错误!!!我认为编译器应该编译对应于代码的东西:
printf("%d\n",-2147483648);
答案 0 :(得分:6)
编译器正在按预期工作。
有符号整数溢出在C中是非法的,并导致未定义的行为。任何依赖它的程序都会被破坏。
编译器将for(i=3;i>2;i++);
替换为while(1);
,因为它看到i
从3开始并且仅增加,因此值永远不会小于3.
只有溢出才能导致循环退出。但这是非法的,编译器假定你永远不会做这么脏的事情。
因为存在无限循环,所以永远不会到达printf
并且可以将其删除。
未经优化的版本只能偶然使用。编译器可以在那里做同样的事情,它本来也同样有效。
答案 1 :(得分:4)
好吧,允许编译器假定程序永远不会出现未定义的行为。
在第一种情况下你得到INT_MIN,因为当INT_MAX + 1
给出INT_MIN
(*)时你有溢出,但这是未定义的行为。并且C99草案(n1556)在6.5表达式中表示§5:如果在评估表达式期间发生异常情况(即,如果
结果不是数学定义的,或者不在其类型的可表示值范围内),行为未定义。
所以编译器可以说:
使用as-if规则(5.1.2.3程序执行§3 An 实际实现不需要评估表达式的一部分,如果它可以推导出它 如果不使用值并且不产生所需的副作用),它可以用无限循环替换你的循环。无法再访问以下说明,可以将其删除。
您调用了未定义的行为并获得了......未定义的行为。
(*)甚至这显然依赖于实现,如果你有1的补码,则INT_MIN可以是-2147483647
,8000000
可以是负0,或者溢出可以引发信号...