我已经编写了这个C代码,用于查找所有整数的总和,它们等于数字的阶乘之和。在没有任何GCC优化标志的情况下完成工作需要一分钟左右,使用-O1将时间缩短约15-20秒但是当我尝试使用-O2,-O3或-Os时,它会陷入无限循环
int main()
{
int i, j, factorials[10];
int Result=0;
for(i=0; i<10; i++)
{
factorials[i]=1;
for(j=i; j>0; j--)
{
factorials[i] *= j;
}
}
for(i=3; i>2; i++) //This is the loop the program gets stuck on
{
int Sum=0, number=i;
while(number)
{
Sum += factorials[number % 10];
number /= 10;
}
if(Sum == i)
Result += Sum;
}
printf("%d\n", Result);
return 0;
}
我发现for(i=3; i>2; i++)
是导致问题的原因。显然i
永远不会少于2?
这是否与整数溢出行为未定义有关?如果是这样,在这些情况下,还有关于程序究竟发生了什么的更多信息?
编辑:我想我应该提到,我知道其他写for循环的方法,以便它不会使用溢出(我希望INT_MAX + 1等于INT_MIN,是&lt; 2)但是这只是作为一个随机测试来完成,看看会发生什么,我在这里发布它以找出究竟发生了什么:)
答案 0 :(得分:4)
循环为for (i = 3; i > 2; i++)
且没有break
语句或其他退出条件。
最终i
将达到INT_MAX
,然后i++
将导致整数溢出,从而导致未定义的行为。
可能Sum
或Result
也会在i
之前溢出。
当程序保证触发未定义的行为时,程序的整个行为是未定义的。
众所周知,gcc积极优化触发UB的路径。您可以检查汇编代码,看看您的情况究竟发生了什么。也许-O2
和更高的案例删除了循环结束条件检查,但-O1
将其留在那里并“依赖”INT_MAX + 1
导致INT_MIN
。
答案 1 :(得分:1)
我发现以下代码在没有优化和-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(&#34;%d \ n&#34;, - 2147483648);
答案 2 :(得分:0)
正如您所注意到的那样,未定义有符号整数溢出。编译器决定推断您的程序,假设您足够聪明,永远不会导致未定义的行为。因此可以得出结论,因为i
被初始化为大于2的数字并且仅增加,所以它永远不会低于或等于2,这意味着i > 2
永远不会为假。这反过来意味着循环永远不会终止,并且可以优化为无限循环。
答案 3 :(得分:0)
正如你所说,它是未定义的行为,所以你不能依赖任何特定的行为。
您最有可能看到的两件事是:
答案 4 :(得分:0)
我不知道你在尝试什么,但是如果你想处理整数溢出,只需在你的源代码中包含limits.h
并在你的for循环中写下这一行。
if (i >= INT_MAX) break;
这将使您能够检查您的变量是否大于它是否适合整数。
答案 5 :(得分:0)
for循环是for(i=3; i>2; i++)
,并且此循环内部i
未被修改,也没有break
或任何其他方式退出循环。您依靠整数溢出来导致退出条件发生,但编译器不会考虑这一点。
相反,编译器看到i
从3开始,而i
只是递增,因此i>2
始终为真。因此,在此上下文中根本不需要存在i
,因为这必须是无限循环。
如果您将i
更改为unsigned int
并将循环退出的条件设置为匹配,则此“优化”将不再出现。