Bitwise Leftshift(<<")奇怪的行为

时间:2013-06-02 02:38:27

标签: c gcc bit-manipulation bitwise-operators bit-shift

gcc按位Leftshift(<<)奇怪的行为。这是我的代码:

#include <stdio.h>
#include <string.h>

void foo(int n){
  printf("1<<32:%d\n", 1<<32);
  printf("1<<(32-n):%d\n", 1<<(32-n));
}

int main(){
    foo(0);
}

如果我将0作为参数传递,结果可能会有所不同。编译源代码:

$gcc main.c -o demo -lm -pthread -lgmp -lreadline 2>&1
main.c: In function 'foo':
main.c:5:3: warning: left shift count >= width of type [enabled by default]

执行程序:

$demo

1<<32:0
1<<(32-n):1

这个结果是我从compile online site

得到的

如果将0传递给它,如何使foo函数输出0? (目前输出1代替)

6 个答案:

答案 0 :(得分:2)

移动一个等于或大于左操作数的提升类型宽度的值是未定义的行为,因此您必须专门测试并避免这种情况。此外,导致溢出的带符号类型的左移也是未定义的行为,因此您还需要避免移位31:

printf("1<<(32-n):%d\n", (n > 1 && n < 33) ? 1 << (32-n) : 0);

此特定表达式对于未定义的情况使用0,但如果需要,可以以不同方式处理。

答案 1 :(得分:1)

由于您正在移位32位整数,因此移位32位将导致零值。但是,CPU的位移操作只能移位0到31位,因为其他任何东西通常都没用,只会使计算复杂化。

第一个示例1<<32似乎有效的原因是编译器在编译时将其优化为0,同时还打印警告。另一个例子,1<<(32-n)具有在编译时无法确定的移位值(因此也没有警告)。相反,CPU使用减法32 - n == 32的结果进行移位操作,但CPU只取五个最低位,因此溢出为0,结果为1 << 0 == 1

要解决此问题,您必须使用特殊情况n == 0,使用更宽的数据类型,或者只使用更少的位。

答案 2 :(得分:0)

gcc告诉你警告的问题是什么:

main.c:5:3: warning: left shift count >= width of type [enabled by default]

您的班次需要小于该类型的大小,否则它是未定义的行为。 C99标准草案部分6.5.7 按位移位运算符 3 表示:

  

[...]如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

为什么第一个printf与第二个-fdump-tree-original不同?如果您使用foo进行构建,则会看到为printf ((const char * restrict) "1<<32:%d\n", 0); printf ((const char * restrict) "1<<(32-n):%d\n", 1 << 32 - n); 生成以下代码:

0

似乎第一种情况是它被优化到{{1}},这与未定义的行为一致,编译器可以做任何事情,包括似乎有用的行为。

答案 3 :(得分:0)

不要惊讶。你正在处理一个32位的int,所以当你做1&lt;&lt; 32时,你已经将该位置移到了int的末尾,并将整个事件归零。

e.g。二进制:

    33222222 22211111 11111000 00000000
    10987654 32109876 54321098 76543210
 1: 00000000 00000000 00000000 00000001

   ^--- position #32

答案 4 :(得分:0)

根据C标准ISO 9899:1999第6.5.7章Bitwise shift operators

  

对每个操作数执行整数提升。类型   结果是提升左操作数的结果。如果值   右操作数为负数或大于或等于宽度   升级后的左操作数,行为未定。

奇怪的是,编译器以不同的方式处理这两个表达式。但是,由于这会导致不确定的行为,所以这不是问题。您需要做的是在评估之前检查操作数,以确保它是一个有效的表达式。

答案 5 :(得分:-1)

我终于想出了一个解决方案,至少使输出相同。

#include <stdio.h>
#include <string.h>

void foo(int n){
  printf("1<<32:%d\n", 1<<32);
  printf("1<<(32-n):%d\n", (1<<(31-n))<<1);
}

int main(){
    foo(0);
}