【问题标题】:Two logically identical C instructions give different results两条逻辑相同的 C 指令给出不同的结果
【发布时间】:2021-04-29 16:48:18
【问题描述】:

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

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

在我看来 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]];
}

【问题讨论】:

  • var2^string[i] 最终是 > 255?了解积分促销
  • 制作string unsigned char*
  • 避免对有符号变量进行按位运算。
  • ^ 执行整数提升,如果恰好是负数,您的 string[i] 将得到符号扩展。所以这将成为杂草:table[var2 ^ string[i]]
  • 问题中没有程序集。

标签: c operators xor


【解决方案1】:

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

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

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

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

【讨论】:

  • 我从维基百科撕下了签名的代码,它工作得很好;在调试和发布模式下,完整版本是完全定义和功能的:unsigned char cHash = cLen; for (int i = 0; i &lt; cLen; ++i) cHash ^= cPerm256[cHash ^ csString[i]]; 工作干净并产生统一的哈希。未签名版本会产生非常低质量的哈希,我想在程序集文件中实现 signed 版本。我自己怎么做这个整数提升的事情?
  • 仅仅将 "csString" 更改为 unsigned char* 会破坏散列的质量,因此在我的汇编实现中,我必须自己执行整数提升。
  • @IdioticShrike 我从 Wikipedia 撕下了签名的代码 好吧,您刚刚了解了 Wikipedia 的质量。通过带符号的转换,cPerm256 数组需要在正方向有 2 gig 的元素,在负方向有 2 gig 的元素,假设为 32 位 int 值。如果不是,则代码为 BROKEN
  • @AndrewHenle 维基百科代码完美运行!我还把它从 Python 翻译成 C,所以签名的问题很可能不是维基百科的错……但它工作得很好——“正确的”未签名版本没有。
  • @IdioticShrike 那么你应该使用unsigned char just like the Wikipedia example actually does。所以你没有从维基百科上得到签名的代码。
猜你喜欢
  • 2016-05-11
  • 1970-01-01
  • 2014-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
相关资源
最近更新 更多