简体   繁体   English

按位左移(<<)奇怪的行为

[英]Bitwise Leftshift (<<) strange behavior

gcc bitwise Leftshift ( << ) strange behavior. gcc按位左移( << )奇怪的行为。 Here is my code: 这是我的代码:

#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);
}

If I pass 0 as parameter, the result could be different. 如果我将0作为参数传递,则结果可能会有所不同。 Compiling the source code: 编译源代码:

$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]

Executing the program: 执行程序:

$demo

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

This result is what I've got from compile online site 这个结果就是我从编译在线站点获得的结果

How can I make the foo function output 0 if I pass 0 to it? 如果将0传递给foo函数,如何将其输出为0? (currently it outputs 1 instead) (当前它输出1)

Shifting by a value that is equal or greater than the width of the promoted type of the left operand is undefined behaviour, so you must specifically test for and avoid this. 移位等于或大于左操作数的提升类型的宽度的值是未定义的行为,因此您必须专门测试并避免这种情况。 In addition, a left-shift of a signed type that results in overflow is also undefined behaviour, so you need to also avoid a shift of 31, too: 另外,导致溢出的带符号类型的左移也是未定义的行为,因此,您还需要避免31的移位:

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

This particular expression uses 0 for the cases that are otherwise undefined, but you can handle those differently if you need to. 对于未定义的情况,此特定表达式使用0,但是如果需要,可以用不同的方式处理。

Since you're shifting 32-bit ints, shifting by 32 bits would result in a zero value. 由于您要移位32位整数,因此,将32位移位将导致零值。 However, the bitshift operation of the CPU can only shift by 0 to 31 bits as anything else is generally not useful and would only complicate the computation. 但是,CPU的移位操作只能移位0到31位,因为其他任何东西通常都没有用,只会使计算复杂化。

The reason that the first example, 1<<32 , seems to work, is that the compiler optimises this to 0 at compile time, while also printing a warning. 第一个示例1<<32似乎起作用的原因是,编译器在编译时将其优化为0 ,同时还显示警告。 The other example, 1<<(32-n) , however, has a shift value that cannot be determined at compile time (thus no warning either). 但是,另一个示例1<<(32-n)具有在编译时无法确定的偏移值(因此也没有警告)。 Instead, the CPU uses the result of the subtraction 32 - n == 32 for its shift operation, but the CPU only takes the five lowest bits and thus overflows to 0, and the result is 1 << 0 == 1 . 取而代之的是,CPU将减法32 - n == 32用于其移位操作,但是CPU仅采用最低的五个位,因此溢出到0,结果为1 << 0 == 1

To work around this, you will have to either special-case n == 0 , use a wider data type, or simply use fewer bits. 要解决此问题,您将必须使用特殊情况n == 0 ,使用更广泛的数据类型或仅使用更少的位。

gcc is telling you what the problem is with the warning: gcc告诉您警告出现了什么问题:

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

Your shifts need to be less than the size of the type otherwise it is undefined behavior. 您的班次必须小于类型的大小,否则是未定义的行为。 The C99 draft standard section 6.5.7 Bitwise shift operators paragraph 3 says: C99标准草案第6.5.7节“ 按位移位运算符”3段说:

[...]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. [...]如果右操作数的值为负或大于或等于提升后的左操作数的宽度,则行为不确定。

Why is the first printf different from the second one? 为什么第一个printf与第二个printf不同? If you build using -fdump-tree-original you will see the following code generated for foo : 如果使用-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);

It seems the first case it being optimized away to 0 which is consistent with undefined behavior , the compiler can do anything, including behavior that appears to work. 似乎第一种情况是将其优化为0 ,这与未定义的行为保持一致,编译器可以执行任何操作,包括看起来可行的行为。

Don't be surprised. 不要惊讶 you're dealing with a 32bit int, so when you do 1<<32, you've shifted that set bit right off the end of the int and zeroed out the whole thing. 您正在处理32位int,因此当您执行1 << 32时,您已将该设置位移到int的末尾,并将整个对象清零。

eg in binary: 例如二进制:

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

   ^--- position #32

According to the C standard ISO 9899:1999 Chapter 6.5.7 Bitwise shift operators : 根据C标准ISO 9899:1999第6.5.7章, Bitwise shift operators

The integer promotions are performed on each of the operands. 对每个操作数执行整数提升。 The type of the result is that of the promoted left operand. 结果的类型是提升后的左操作数的类型。 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. 如果右操作数的值为负或大于或等于提升后的左操作数的宽度,则行为未定义。

It is strange that the compiler treats the two expressions differently. 奇怪的是,编译器对待两个表达式的区别。 But since this causes undefined behavior anyway, it's not a problem. 但是由于这仍然会导致未定义的行为,所以这不是问题。 What you need to do is to check the operands before evaluation to make sure it's a valid expression. 您需要做的是在求值前检查操作数,以确保它是有效的表达式。

I finally figure out a work-around solution, at least make the output identical. 我终于找到了一种解决方法,至少使输出相同。

#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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM