【问题标题】:for(i=0;i<10000000000;++i) compiles to endless loop?for(i=0;i<10000000000;++i) 编译成无限循环?
【发布时间】:2014-01-19 14:18:52
【问题描述】:

我正在运行一些测试以查看 ++i 和 i++ 如何转换为 asm。我写了一个简单的:

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

gcc test.c -O0 -o test编译它,并用objdump -d test检查asm:

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 

到目前为止一切顺利。奇怪的事情(如果我正确理解 asm 代码)是当我写 i

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>

根据我的理解,这是无限循环,导致生成完全相同的 asm:

 for(i=0;;++i);

问题是,真的有可能编译成无限循环吗?为什么? 我使用的是 Ubuntu 13.04,x86_64。

谢谢。

【问题讨论】:

  • 您认为i 可以在您的架构中保留10000000000 吗?
  • 对如此大的数字使用long long int。并查看limits.h 以了解整数类型的大小。
  • 任何理智的编译器都会为i++++i 生成完全相同的代码,除非您实际使用结果。
  • 如果你用优化编译,所有这样的循环都应该编译成nothing,因为它们显然什么都不做。如果你在没有优化的情况下编译,你的发现可能毫无用处。
  • @hacks 我猜 int 对于这样的数字来说很小,但是这里 cmpl $0xf423f,-0x4(%rbp) 这个数字不在 int 中,它只是一个 const,所以我希望从编译器知道如何处理它

标签: c assembly for-loop


【解决方案1】:

发生这种情况是因为架构上 int 的最大值永远不会达到 10000000000。它会在达到该值之前的某个时间点溢出。因此,条件i &lt; 10000000000 将始终评估为真,这意味着这是一个无限循环。

编译器能够在编译时扣除这一点,这就是它为无限循环生成适当程序集的原因。

编译器能够就此发出警告。为此,您可以启用“额外”警告级别:

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 是的,即使在 O0 时也会执行一些微不足道的优化。
  • @BlackBear 删除本质上是对 false 和 true 的持续比较是非常微不足道的。我批评的不是关于比较不变的警告
  • @NikosC。谢谢,-Wextra 在这种情况下很有帮助:警告:由于数据类型的范围有限,比较总是正确的 [-Wtype-limits]
  • @BlackBear -- 这不是优化的问题。如果代码未优化,则计数器达到 2,147,483,647,下一个增量将使其变为 -2,147,483,646,因此永远不会 > 10,000,000,000。
【解决方案2】:

整数范围是:–2,147,483,648 到 2,147,483,647

你就像在它之上。

【讨论】:

  • int 范围可能更小或更大。根据 C11 §5.2.4.2.1,minimum 范围是 -32767 到 +32767。在 2014 年的嵌入式处理器中,这种 16 位范围 -32768 到 +32767 很常见。
  • 这就像 int16 但 2014 处理器不再使用 int16。如果您有一个非常旧的处理器,可以。但是现在几乎每个 int 都被 int32 处理了
  • @JonasLibbrecht:不是运行微波炉的 CPU。
  • MicroChip 在 2013 年制造了超过 10 亿颗 PIC 处理器。很大一部分是 16 位。 - 我认为最大的。 C 在此类处理器中非常流行。使用 16 位 int 编程可以赚很多钱。显示您的数据以支持“2014 处理器不使用 int16”。
【解决方案3】:

如果 10000000000 超出 int 范围,但在 long 或 long long 范围内,对于您的编译器,则 i

编译器意识到它总是错误的,然后删除了多余的比较。

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

【讨论】:

    【解决方案4】:

    这是因为你使用int来存储这么大的数字。
    结果,i wraps 围绕自身,永远不会达到 for 循环的终止条件。
    当您超出 C/C++ 中数据类型的限制时,可能会发生有趣的事情。
    编译器可以在编译时检测到这些东西,从而生成汇编语言的无限循环代码。

    【讨论】:

      【解决方案5】:

      问题是: 你不能在“i”中存储这么大的数字。

      查看https://en.wikipedia.org/wiki/Integer_%28computer_science%29了解更多信息。

      “i”(变量)无法达到 10000000000,因此循环始终评估为 true 并运行无限次。

      您可以为 i 使用较小的数字或其他容器,例如 Boost 的多精度库: http://www.boost.org/doc/libs/1_53_0/libs/multiprecision/doc/html/boost_multiprecision/intro.html

      【讨论】:

        【解决方案6】:

        发生这种情况是因为编译器看到您正在使用一个永远不会为假的条件,因此根本不会评估该条件。

        int 永远不能保存与10000000000 一样大的值,因此该值将始终低于该值。当变量达到最大值并且您尝试进一步增加它时,它将环绕并从其可能的最小值开始。

        如果您使用 true 的字面值,同样会删除条件:

        for (i = 0; true; ++i);
        

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

        【讨论】:

          猜你喜欢
          • 2018-01-11
          • 1970-01-01
          • 2011-06-14
          • 2022-01-05
          • 2016-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-27
          相关资源
          最近更新 更多