c - 为什么 GCC 以不同的方式对待两个相似的循环?

标签 c loops gcc

注意事项:

如果我对下面的代码理解正确,它会跳过整个循环,因为当比较unsigned (j) 和signed (-1) 时,似乎-1 将转换为 UINT_MAX。 (像这样 question explained )


第一个循环:

unsigned int j = 10;

for (; j > -1; --j) {     --->  `>`
    printf("%u", j);
}

第一个循环的部分汇编代码:

movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    %edi, -20(%rbp)
movq    %rsi, -32(%rbp)
movl    $10, -4(%rbp)
nop                           --->**elision**
popq    %rbp
.cfi_def_cfa 7, 8
ret

第二个循环的第二个循环:

unsigned int j = 10;

for (; j >= -1; --j) {  --->  `>=`
    printf("%u", j);
}

部分汇编代码:

movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movl    %edi, -20(%rbp)
movq    %rsi, -32(%rbp)
movl    $10, -4(%rbp)
jmp .L2                        --->** still a loop here **

.L3:

movl    -4(%rbp), %eax
movl    %eax, %esi
movl    $.LC0, %edi
movl    $0, %eax
call    printf
subl    $1, -4(%rbp)

.L2:

cmpl    $-1, -4(%rbp)
je  .L3
leave
.cfi_def_cfa 7, 8
ret

所以我的问题是

  • 为什么 gcc(我使用 GCC:(Ubuntu 4.8.2-19ubuntu1)4.8.2)处理类似的情况,它优化了第一个但没有优化第二个? (* 还是我对代码的理解有误?) (跟汇编有关系?)

编辑:你可以去这个site去检查。如果您只使用-S编译器选项(或不使用编译器选项),您将得到与我相同的结果。 (感谢@Raymond Chen 提醒)

第一步:

打开上面的站点并将以下代码复制到 Code Eidtor。

 #include <stdio.h>
 int main (int argc, char *argv[]) {

   unsigned int j = 10;

   for (; j > -1; --j) {    
      printf("%u", j);
   }
 }

第 2 步:

选择 g++ 4.8 作为编译器。 编译器选项为空。(或 -S)

第 3 步:

你得到第一种情况。现在,将 j > -1 更改为 j >= -1,您可以看到第二个。

最佳答案

适用的转换在 C 标准 n1570 S6.3.1.3 中描述如下:

...if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

因此 -1 被转换为 UINT_MAX,对于 32 位算术,它是 0xffffffff。这是相同的位模式,因此在汇编语言术语中它是一个空操作。

在第一种情况下,编译器可以确定循环退出条件对于循环变量的所有值都为真。无需进一步分析,并且在适当的优化级别应该省略循环。

在第二种情况下,没有这样简单的分析可用。但是,如果编译器执行数据流分析,它会发现在进入循环之前满足循环退出条件。在适当(但可能不同)的优化级别上,也可以省略此循环。

所需的分析在每种情况下都不同,而在第二种情况下则更难。但是,我不关心预测哪些编译器会在哪些情况下执行循环省略。您必须对它们进行测试才能找出答案(就像您所做的那样)。

术语说明:当编译器决定完全省略代码时,术语省略是更准确的描述。当编译器在不同的可能代码生成策略之间做出选择时,最好使用术语优化,可能是在速度和空间之间做出选择。

关于c - 为什么 GCC 以不同的方式对待两个相似的循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25095713/

相关文章:

objective-c - Apple 的 BetterAuthorizationSample 在 10.6.6 上不起作用

php - 在 A-Z 列表中回显数组

python - 在Python 3.3中通过循环将键和值迭代到字典中

c++ - Lambda 语法或 gcc 错误的最后一刻更改?

c - 对 C 分析器的建议?

c - 将一个字符分配给一个字符串

c - 将动态指针数组传递给 C 函数

c - 我不明白这个算法的时间复杂度是如何计算的

c - 内部内核结构无法分配错误?

ios - 结构填充编译标志