【问题标题】:Understanding behavior of old C++ code了解旧 C++ 代码的行为
【发布时间】:2018-01-21 13:10:15
【问题描述】:

我正在迁移旧 C++ 代码的某些部分,这些代码最初是使用 CodeGear C++Builder® 2009 版本 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

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];

我匹配旧代码的行为很重要。谁能帮我理解为什么原始编译器会产生这些结果?

【问题讨论】:

  • 编译标志有符号/无符号字符???
  • 如果我将 char 更改为 unsigned char,这将产生 222。 (这是有道理的,因为这是 8 位 -34 的 2 的补码值)
  • 为了调试,能不能改一下代码,用原来的CodeGear编译器重新编译?因为我看到尽管代码说了什么,但看起来正在执行的是Key[1] = Key[1] ^ Key[3],因为结果为 84。
  • 我希望在我提交此评论的第二次被证明是错误的,但它对我来说就像一个编译器错误 - 如果这是合法行为,那么 c++ 整数提升/转换规则比我想象的还要疯狂.最坏的情况:创建一个复制原始编译器错误行为的查找表(并记录其中的垃圾)。
  • 看来我可以用 Key[i]=Key[i]^Key[2*i+1] 重现原始行为(删除2*),正如@lxop 所建议的那样,但我真的不明白这里发生了什么。

标签: c++ c++builder c++builder-2009


【解决方案1】:

这似乎是一个错误:

线

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

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

但如果我使用“新”(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 得到相同的结果),但它产生了正确的结果。

【讨论】:

  • 疯了。感谢您的详细回答。除了^ 之外的其他运算符也会失败。
  • 嗯...我会看看它是否是一个已知的错误(如果可以的话——QC 离线),否则将它报告给 QP。
  • 报告:quality.embarcadero.com/browse/RSP-18831。使用+ 而不是^,这似乎发生在几个运营商身上。
  • FWIW,在 Mac (Xcode) 上,clang 编译器生成几乎相同(但 64 位)的代码。我没想到会这样。
  • 谢谢。你的意思是 clang 生成了同样没有优化的代码,不是错误的,对吧?
猜你喜欢
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
相关资源
最近更新 更多