2
int main(int argc, char *argv[]) {
    int i = 0;
    for (i = 0; i < 50; i++)
        if (false) break;
}

使用 VS 2010 编译和执行(VS 2008 中的相同问题)。我在最后一行(右括号)放了一个断点,并通过调试器查看变量 i。此代码将 i 保留为 0。为什么?

int main(int argc, char *argv[]) {
    int i = 0;
    for (i = 0; i < 50; i++)
        if (false) break;;
}

在此之后 - 请注意 break 后的第二个分号 - i 如预期的那样是 50。

有人可以向我解释这种奇怪的行为吗?

4

3 回答 3

3

此代码无法在符合标准的编译器上编译,因为它是无效的 C++ ( void main)。

也就是说,结果值i是无关紧要的:编译器可以为所欲为。

原因是它i永远不会在循环之外读取(它本身被证明没有效果)并且没有声明volatile,因此编译器可以很容易地证明没有可观察到的副作用,无论i.

于 2012-08-03T10:36:12.730 回答
3

查看生成的汇编代码,objdump -d -S我们可以看到 GDB 跳过循环的可能原因:

0000000000400584 <main>:
int main()
{
  400584:   55                      push   %rbp
  400585:   48 89 e5                mov    %rsp,%rbp
    volatile int i;

    for (i = 0; i < 5; i++)
  400588:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  40058f:   eb 09                   jmp    40059a <main+0x16>
  400591:   8b 45 fc                mov    -0x4(%rbp),%eax
  400594:   83 c0 01                add    $0x1,%eax
  400597:   89 45 fc                mov    %eax,-0x4(%rbp)
  40059a:   8b 45 fc                mov    -0x4(%rbp),%eax
  40059d:   83 f8 04                cmp    $0x4,%eax
  4005a0:   0f 9e c0                setle  %al
  4005a3:   84 c0                   test   %al,%al
  4005a5:   75 ea                   jne    400591 <main+0xd>
  4005a7:   b8 00 00 00 00          mov    $0x0,%eax
        if (false)
            break;
}
  4005ac:   c9                      leaveq 
  4005ad:   c3                      retq   
  4005ae:   90                      nop
  4005af:   90                      nop

即使在关闭优化的情况下编译(-O0标志为 g++),实际上也不会为循环体生成代码。这可能意味着 GDB 会将循环视为单个语句,并且不会正确地单步执行循环。

我使用了 GCC 4.4.5 版和 GDB 7.0.1 版。

于 2012-08-03T10:46:30.807 回答
2

在 MSVC10 中,这是可重现的。我检查了拆卸。pdb文件生成似乎有问题。返回循环开头的跳转指令与下一个源代码行混合,就是这样。如果按 step 到下一行,它将从 return 语句回到 for 循环的开头,并按预期执行循环 50 次。 查看代码.

于 2012-08-03T11:10:34.257 回答