"for(i=0;i<10000000000;++i)"是否会编译成无限循环?

6

我正在运行一些测试,以查看++i和i++如何转换为汇编语言。我编写了一个简单的for循环:

int main()
{
    int i;
    for(i=0;i<1000000;++i);
    return 0;
}  

使用gcc test.c -O0 -o test编译代码,并使用objdump -d test命令检查汇编代码:

4004ed: 48 89 e5                mov    %rsp,%rbp
4004f0: c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)  // i=0;
4004f7: eb 04                   jmp    4004fd <main+0x11>
4004f9: 83 45 fc 01             addl   $0x1,-0x4(%rbp)     // ++i;
4004fd: 81 7d fc 3f 42 0f 00    cmpl   $0xf423f,-0x4(%rbp) // 
400504: 7e f3                   jle    4004f9 <main+0xd>   //i<1000000;
400506: b8 00 00 00 00          mov    $0x0,%eax
40050b: 5d                      pop    %rbp
40050c: c3                      retq 

目前为止一切都很好。不过有个奇怪的事情(如果我理解汇编代码正确的话),当我把i < 1000000改成了i < 10000000000时。停止条件为i < 10000000000的完全相同的for循环被翻译为以下汇编代码:

4004ed: 48 89 e5                mov    %rsp,%rbp
4004f0: c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
4004f7: 83 45 fc 01             addl   $0x1,-0x4(%rbp)
4004fb: eb fa                   jmp    4004f7 <main+0xb>

根据我的理解,这是一个无限循环,因为完全相同的汇编代码被生成:
 for(i=0;;++i);

问题是,它是否真的可能编译成无限循环?为什么? 我正在使用Ubuntu 13.04,x86_64。
谢谢。

4
你认为在你的架构上,变量i能否容纳10000000000 - haccks
对于如此大的数字,请使用 long long int。并检查 limits.h 以了解整数类型的大小。 - pzaenger
3
除非你真正使用结果,否则任何明智的编译器都会为 i++++i 生成完全相同的代码。 - SLaks
如果您使用优化编译,所有这样的循环都应该编译成“无操作”,因为它们显然“什么也不做”。如果您没有使用优化编译,您的发现可能是无用的。 - user395760
@haccks 我猜int对于这么大的数字来说有点小,但是在cmpl $0xf423f,-0x4(%rbp)中,这个数字并不是int类型,它只是一个常量,所以我希望编译器知道如何处理它。 - Dabo
6个回答

17

这是因为你的架构中,int 的最大值永远无法达到10000000000。在达到该值之前,它将溢出。因此,条件i < 10000000000 将始终计算为true,意味着这是一个无限循环。

编译器能够在编译时推断出这一点,因此会生成适当的汇编代码以表示无限循环。

编译器能够向你发出警告。为此,您可以使用以下命令启用“额外”警告级别:

gcc -Wextra

例如,GCC 4.8.2 将告诉您:

warning: comparison is always true due to limited range of data type [-Wtype-limits]
for (i = 0; i < 10000000000; ++i);
^

它甚至告诉你具体的警告选项来精确控制这种警告 (Wtype-limits)。


然而,OP使用了-O0选项,这应该防止任何类型的优化。或者这是一个如此微不足道的问题? - BlackBear
@BlackBear 是的,即使在 O0 级别下也会执行一些微不足道的优化。 - Nikos C.
1
@BlackBear 移除本质上是 false 和 true 的常量比较非常微不足道。我要批评的不是警告常量比较。 - Jon Hanna
@NikosC。谢谢,-Wextra在这种情况下很有帮助: 警告:由于数据类型的限制,比较始终为真[-Wtype-limits] - Dabo
@BlackBear -- 这不是优化的问题。如果代码没有被优化,计数器将达到2,147,483,647,下一个增量将使其变为-2,147,483,646,因此永远不会大于10,000,000,000。 - James Anderson
显示剩余2条评论

5

整数范围为:-2,147,483,648至2,147,483,647。

你远远超过了这个范围。


2
int范围可能较小或较大。根据C11 §5.2.4.2.1,_最小_范围为-32767至+32767。在2014年的嵌入式处理器中通常可以找到-32768至+32767的16位范围。 - chux - Reinstate Monica
这就像int16,但是2014年的处理器不再使用int16了。如果你有一个非常老的处理器,好吧,是的。但现在几乎每个int都由int32处理。 - Jonas Libbrecht
1
@JonasLibbrecht:不是运行你的微波炉的CPU。 - Zan Lynx
1
MicroChip在2013年生产了超过十亿个PIC处理器。其中很大一部分是16位的,我认为这是最多的。C语言在这些处理器中非常流行。使用16位int进行编程可以赚取大量的利润。如果要证明“2014年的处理器不使用int16”,请展示你的数据。 - chux - Reinstate Monica
@JonasLibbrech 16位PIC24微控制器和dsPIC®数字信号控制器 - chux - Reinstate Monica
是的,我指的是重型CPU :p,但我忘了微处理器:p,我的错! - Jonas Libbrecht

4

如果10000000000超出了int类型的范围,但在long或long long类型的范围内(对于您的编译器而言),则在进行比较之前将i < 10000000000强制转换为long或long long。

编译器会意识到这个比较永远是false,然后删除多余的比较。

我希望编译器会有某种警告。


2
这是因为编译器发现你使用的条件永远不可能为假,所以该条件根本不会被计算。
一个整数(int)无法保存与10000000000相同大小的值,因此该值始终低于该值。当变量达到其最大值并尝试进一步增加时,它将绕回并从其最低可能值开始。
如果使用字面值true,则同样会移除该条件:
for (i = 0; true; ++i);

编译器只会把它变成一个没有条件的循环,它实际上不会在每次迭代中评估true值,以查看它是否仍然为真。

2
这是因为您使用了`int`来存储如此大的数字。
结果,变量i循环自己,并且永远无法达到for循环的终止条件。
当超出C / C ++中数据类型的限制时,可能会发生有趣的事情。
编译器可以在编译时检测到这些问题,因此会生成汇编语言中的无限循环代码。

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接