有符号数右移引发的 bug

| 分类 C-Family  | 标签 c  bitwise  shift 

最近在整理以前的代码,想让整个工程可以顺利的在 64 位系统上运行,同时趁机改掉尽可能多的编警告。整理过程中发现很多的警告都是由有符号数 (signed value) 和无符号数 (unsigned value) 的隐式转换因起来的比如:

1: void StoreValue(int value); // declared in other sources.
2: 
3: void TestFunc()
4: {
5:     unsigned int value = 0;
6:     // operations on value...
7:     StoreValue(value);
8: }

To make compiler happy,很多地方简单的改成了:

1: void TestFunc()
2: {
3:      int value = 0;
4:     // operations on value...
5:     StoreValue(value);
6: }

貌似没什么问题,但是有一个地方的类似改动却让程序进入了死循环:

1: void* test_func(int value)
2: {
3:     unsigned v = value; // 之后改成了 int v = value;
4:     while (v >>= 1) {
5:         // other operations...
6:     }
7: }

死循环之后在 XCode 中直接将程序暂停,发现此时 v = -1 ,并且之后始终为 -1。后来查了一下,发现 C 语言的左移右移有如下的规定:

  • If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
  • The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros.
    • If E1 has an unsigned type, the value of the result is E1 × 2^E2 , reduced modulo one more than the maximum value representable in the result type:

      \(mod(E1*2^{E2}, MaxValueOfThisType)\)

    • If E1 has a signed type and non-negative value,
      • If E1 × 2^E2 is representable in the result type, then that is the resulting value;
      • Otherwise, the behavior is undefined.
  • The result of E1 >> E2 is E1 right-shifted E2 bit positions.
    • If E1 has an unsigned type or if E1 has a signed type and a nonnegative value: the value of the result is the integral part of the quotient of E1 / 2^E2 .
    • If E1 has a signed type and a negative value, resulting value is implementation-defined.

上面的例子中, 3 中如果将其换成 int ,并且输入为负数,4 中 clang 自动将补齐的位置位成 1,最后导致 v 永远为 -1,循环就永远也出不来了。。

感兴趣的可以看看这里


上一篇     下一篇