两条逻辑上相同的 C 指令给出不同的结果

Two logically identical C instructions give different results

编辑:答案是,对有符号值的按位运算会做一些奇怪的事情!

在调试过程中,我注意到一个奇怪的差异,我将其翻译成下面一个易于阅读的示例。

在我看来,var1 和 var2 应该是相同的:在调试器上仔细检查之后,似乎 var1 和 var2 在第一次迭代中是相同的,但在第二次迭代中是不同的。我在尝试将“var2”的表达式转换为程序集时发现了这个错误,并注意到我的逻辑翻译(我用“var1”显示)给出了不同的结果。对我来说,“var1”的计算与“var2”的复杂表达式的相同解开 - 我哪里出错了?

这是使用 Visual Community 2019、x64、调试编译的。

// x is an unsigned char, equivalent to the length of the string
// taking the null terminator into account

unsigned char var1 = x;
unsigned char var2 = x;

for (int i = 0; i < x; ++i) {
    unsigned char temp1 = string[i];
    unsigned char temp2 = var1 ^ temp1;
    unsigned char temp3 = table[temp2];

    var1 ^= temp3;
    var2 ^= table[var2 ^ string[i]];
}

table[var2 ^ string[i]];中,var2unsigned char值为0到255,string[i]可能有符号的char值为−128到+127。 (我们假设八位字节和二进制补码,它们在现代系统中无处不在。)

与大多数 C 运算符一样,整数提升 应用于操作数,在这种情况下,它会产生 int 个操作数。对于从 0 到 255 的 unsigned char 值,这会产生一个 int,其位仅设置在低八位中。对于 char 值 −128 到 −1,这会产生一个 int,在整个 int 中设置位,特别是在高位。

然后异或运算的结果是一个int,高位被设置,包括符号位,所以它有一个负值。然后 table 用负下标索引,超出数组的范围。因此 C 标准未定义此行为。

要解决此问题,请将 table 的元素类型更改为 unsigned char 或将 string[i] 转换为 unsigned char,然后再将其用于按位运算。