理解旧版C++代码的行为

8

我正在迁移一些旧的C++代码,最初是使用CodeGear C++Builder® 2009 Version 12.0.3170.16989编译的。

下面的代码是一个较大代码片段的最小版本,使用任何现代编译器都会输出-34。然而,在原始平台上它输出84

char Key[4];    
Key[0] = 0x1F;
Key[1] = 0x01;
Key[2] = 0x8B;
Key[3] = 0x55;

for(int i = 0; i < 2; i++) {
    Key[i] = Key[2*i] ^ Key[2*i + 1];
}

std::cout << (int) Key[1] << std::endl;

以下代码在旧版和新版编译器中都输出-34

enter image description here

for(int i = 0; i < 2; i++) {
    char a = Key[2*i];
    char b = Key[2*i + 1];
    char c = a ^ b;
    Key[i] = c;
}

此外,手动展开循环似乎在两个编译器中都有效:

Key[0] = Key[0] ^ Key[1];
Key[1] = Key[2] ^ Key[3];

我需要与旧代码的行为匹配,所以了解原始编译器为什么会产生这些结果很重要。请问有谁能帮我理解吗?


3
编译标志签名/无符号字符? - Jacek Cz
4
为了调试,你能否使用原来的CodeGear编译器更改代码并重新编译?因为我发现尽管代码中写的是另一种操作,但实际执行的似乎是 Key[1] = Key[1] ^ Key[3],因为这会得到84的结果。 - lxop
1
我希望提交此评论后能被证明是错误的,但对我来说它看起来像是编译器的一个bug - 如果这是合法行为,那么C++整数提升/转换规则比我想象的更加疯狂。最坏的情况是:创建一个查找表,复制原始编译器的错误行为(并详细记录)。 - MikeMB
1
很可能是编译器的一个bug。但至少在这种情况下,行为可以在新代码中复制,你很幸运 :-) - lxop
1
这并不是真正的复制,因为他改变了原始代码。有可能更大的代码部分(未发布)中的任何代码部分会以某种方式混淆它们吗?我还怀疑你的条目被混合了,你得到了1 ^ 3,也就是84。你尝试过更改值来查看是否也会创建结果吗?(例如将key [3]更改为0x54,看看是否会给您0x53) - Giel
显示剩余10条评论
1个回答

5

这似乎是一个bug:

这一行代码

Key[i] = Key[2*i] ^ Key[2*i + 1];

生成以下代码:
00401184 8B55F8           mov edx,[ebp-$08]
00401187 8A4C55FD         mov cl,[ebp+edx*2-$03]
0040118B 8B5DF8           mov ebx,[ebp-$08]
0040118E 304C1DFC         xor [ebp+ebx-$04],cl

这没有任何意义。这有点像:

Key[i] ^= Key[i*2 + 1];

这就解释了结果为什么是这样的:0x01 ^ 0x55 确实是 0x54,即 84

应该是这样的:

mov edx,[ebp-$08]
mov cl,[ebp+edx*2-$04]
xor cl,[ebp+edx*2-$03]
mov [ebp+ebx-$04],cl

所以这肯定是一个代码生成的错误。它似乎一直存在,直到现在,对于“经典”(Borland)编译器的C++Builder 10.2 Tokyo。

但是如果我使用“新的”(clang)编译器,它将产生222。生成的代码如下:

File7.cpp.12: Key[i] = Key[2*i] ^ Key[2*i + 1];
004013F5 8B45EC           mov eax,[ebp-$14]
004013F8 C1E001           shl eax,$01
004013FB 0FB64405F0       movzx eax,[ebp+eax-$10]
00401400 8B4DEC           mov ecx,[ebp-$14]
00401403 C1E101           shl ecx,$01
00401406 0FB64C0DF1       movzx ecx,[ebp+ecx-$0f]
0040140B 31C8             xor eax,ecx
0040140D 88C2             mov dl,al
0040140F 8B45EC           mov eax,[ebp-$14]
00401412 885405F0         mov [ebp+eax-$10],dl

在我看来,这不是最优解(我使用O2和O3得到了相同的结果),但它能够产生正确的结果。


太疯狂了。感谢您详细的回答。它在使用除 ^ 以外的其他运算符时也会失败。 - Iban Cereijo
嗯...我会查看是否是已知的错误(如果可以的话--QC离线了),否则将其报告给QP。 - Rudy Velthuis
报告:https://quality.embarcadero.com/browse/RSP-18831。使用了`+`而不是`^`,因为这似乎发生在几个运算符中。 - Rudy Velthuis
就我所知,在 Mac(Xcode)上,clang 编译器生成的代码几乎相同(但是 64 位)。我没有想到过。 - Rudy Velthuis
谢谢。你的意思是clang生成的代码不是错误的,只是没有经过优化,对吗? - Iban Cereijo
@ibancg:是的,我认为clang代码看起来极其未经优化。我本来以为clang会拥有更好的优化器。但在Mac上,使用Xcode,我得到了类似的生成代码。 - Rudy Velthuis

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