在 GNU 编译器中发现错误.更高版本?



我发现这段代码在优化时会导致 gnu C++编译器出现惊人的错误。

#include <stdio.h>
int main()
{
int a = 333666999, b = 0;
for (short i = 0; i<7; ++i)
{
b += a; 
printf("%d  ", b);
}
return 9;
}

使用可执行文件g++ -Os fail.cpp进行编译不会打印七个数字,它会永远持续下去,打印和打印。 我正在使用 -

-rwxr-xr-x 4 root root 700388 Jun  3  2013 /usr/bin/g++

有没有后来的更正版本?

编译器非常非常少出错。在这种情况下,b溢出,这是有符号整数的未定义行为:

$ g++ --version
g++ (GCC) 10.2.0
...
$ g++ -Os -otest test.cpp
test.cpp: In function ‘int main()’:
test.cpp:8:11: warning: iteration 6 invokes undefined behavior [-Waggressive-loop-optimizations]
8 |         b += a;
|         ~~^~~~
test.cpp:6:24: note: within this loop
6 |     for (short i = 0; i<7; ++i)
|                       ~^~

如果你调用未定义的行为,编译器可以自由地做任何它喜欢的事情,包括让你的程序永不终止。

>Edit:有些人似乎认为UB应该只影响b的值,而不是循环迭代。这不是根据标准(UB 实际上会导致任何事情发生),但这是一个合理的想法,所以让我们看看生成的程序集,看看为什么循环不会终止。

第一个没有-Os

.LC0:
.string "%d  "
main:
push    rbp
mov     rbp, rsp
sub     rsp, 16
mov     DWORD PTR [rbp-12], 333666999
mov     DWORD PTR [rbp-4], 0
mov     WORD PTR [rbp-6], 0
.L3:
cmp     WORD PTR [rbp-6], 6      # Compare i to 6
jg      .L2                      # If greater, jump to end
mov     eax, DWORD PTR [rbp-12]
add     DWORD PTR [rbp-4], eax
mov     eax, DWORD PTR [rbp-4]
mov     esi, eax
mov     edi, OFFSET FLAT:.LC0
mov     eax, 0
call    printf
movzx   eax, WORD PTR [rbp-6]
add     eax, 1
mov     WORD PTR [rbp-6], ax
jmp     .L3
.L2:
mov     eax, 9
leave
ret

然后用-Os

.LC0:
.string "%d  "
main:
push    rbx
xor     ebx, ebx
.L2:
add     ebx, 333666999
mov     edi, OFFSET FLAT:.LC0
xor     eax, eax
mov     esi, ebx
call    printf
jmp     .L2

比较和跳转指令完全消失了。具有讽刺意味的是,编译器完全按照您的要求做了:优化大小,因此在遵守C++标准的同时尽可能多地删除指令。-O3-O2生成与此处-Os完全相同的代码。

-O1生成一个非常有趣的输出:

.LC0:
.string "%d  "
main:
push    rbx
mov     ebx, 0
.L2:
add     ebx, 333666999
mov     esi, ebx
mov     edi, OFFSET FLAT:.LC0
mov     eax, 0
call    printf
cmp     ebx, -1959298303
jne     .L2
mov     eax, 9
pop     rbx
ret

在这里,编译器优化了循环计数器i,只是将b的值与 7 次迭代后的最终值进行比较,利用了在此平台上根据 2 的补码发生的事实!厚脸皮,不是吗?:)

我正在使用g ++版本4.8.1。 Thomas的版本为10.2.0,在添加两个有符号整数时,它显然发出了有关"未定义行为"的警告。 但是,只是一个警告仍然会继续编译程序。 但是,在所有情况下,"未定义的行为"应该只涉及要添加的整数。 实际上,这些整数确实遵守了 2 的补码预期结果。 "未定义的行为"不应覆盖程序中的其他变量。 否则,根本无法信任可执行文件。 如果它不可信,就不应该被编译。 也许有一个更新版本的 gnu 编译器在优化时可以正常工作?

相关内容

最新更新