了解旧 C++ 代码的行为
Understanding behavior of old C++ code
我正在迁移最初使用 CodeGear C++Builder® 2009 版本 12.0.3170.16989 编译的部分旧 C++ 代码
以下代码 - 较大部分的最小版本 - 使用任何现代编译器输出 -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];
重要的是我要匹配旧代码的行为。谁能帮我理解为什么原始编译器会产生这些结果?
这似乎是一个错误:
行
Key[i] = Key[2*i] ^ Key[2*i + 1];
生成以下代码:
00401184 8B55F8 mov edx,[ebp-]
00401187 8A4C55FD mov cl,[ebp+edx*2-]
0040118B 8B5DF8 mov ebx,[ebp-]
0040118E 304C1DFC xor [ebp+ebx-],cl
这没有意义。这类似于:
Key[i] ^= Key[i*2 + 1];
这就解释了结果是如何产生的:0x01 ^ 0x55
确实是 0x54
,或 84
。
应该是这样的:
mov edx,[ebp-]
mov cl,[ebp+edx*2-]
xor cl,[ebp+edx*2-]
mov [ebp+ebx-],cl
所以这绝对是一个代码生成错误。它似乎一直持续到现在,C++Builder 10.2 Tokyo,用于 "classic" (Borland) 编译器。
但是如果我使用 "new" (clang) 编译器,它会生成 222
。产生的代码是:
File7.cpp.12: Key[i] = Key[2*i] ^ Key[2*i + 1];
004013F5 8B45EC mov eax,[ebp-]
004013F8 C1E001 shl eax,
004013FB 0FB64405F0 movzx eax,[ebp+eax-]
00401400 8B4DEC mov ecx,[ebp-]
00401403 C1E101 shl ecx,
00401406 0FB64C0DF1 movzx ecx,[ebp+ecx-[=14=]f]
0040140B 31C8 xor eax,ecx
0040140D 88C2 mov dl,al
0040140F 8B45EC mov eax,[ebp-]
00401412 885405F0 mov [ebp+eax-],dl
这对我来说不是最佳选择(我使用 O2 和 O3 得到了相同的结果),但它产生了正确的结果。
我正在迁移最初使用 CodeGear C++Builder® 2009 版本 12.0.3170.16989 编译的部分旧 C++ 代码
以下代码 - 较大部分的最小版本 - 使用任何现代编译器输出 -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];
重要的是我要匹配旧代码的行为。谁能帮我理解为什么原始编译器会产生这些结果?
这似乎是一个错误:
行
Key[i] = Key[2*i] ^ Key[2*i + 1];
生成以下代码:
00401184 8B55F8 mov edx,[ebp-]
00401187 8A4C55FD mov cl,[ebp+edx*2-]
0040118B 8B5DF8 mov ebx,[ebp-]
0040118E 304C1DFC xor [ebp+ebx-],cl
这没有意义。这类似于:
Key[i] ^= Key[i*2 + 1];
这就解释了结果是如何产生的:0x01 ^ 0x55
确实是 0x54
,或 84
。
应该是这样的:
mov edx,[ebp-]
mov cl,[ebp+edx*2-]
xor cl,[ebp+edx*2-]
mov [ebp+ebx-],cl
所以这绝对是一个代码生成错误。它似乎一直持续到现在,C++Builder 10.2 Tokyo,用于 "classic" (Borland) 编译器。
但是如果我使用 "new" (clang) 编译器,它会生成 222
。产生的代码是:
File7.cpp.12: Key[i] = Key[2*i] ^ Key[2*i + 1];
004013F5 8B45EC mov eax,[ebp-]
004013F8 C1E001 shl eax,
004013FB 0FB64405F0 movzx eax,[ebp+eax-]
00401400 8B4DEC mov ecx,[ebp-]
00401403 C1E101 shl ecx,
00401406 0FB64C0DF1 movzx ecx,[ebp+ecx-[=14=]f]
0040140B 31C8 xor eax,ecx
0040140D 88C2 mov dl,al
0040140F 8B45EC mov eax,[ebp-]
00401412 885405F0 mov [ebp+eax-],dl
这对我来说不是最佳选择(我使用 O2 和 O3 得到了相同的结果),但它产生了正确的结果。