C语言中的移位运算
我曾经写过一个显示int类型数据二进制表示(补码)的小程序,代码如下:
#include <limits.h> #include <stdio.h>
int main(void) { int i = 0;int value = -128; // 0xffffff80 int shift = INT_MIN; // 0x80000000
printf("The decimal value is: %d\n\n", value);
printf("* * * *\n");// print marks
for (i = 0; i < 32; ++i) { if (shift & value) { printf("1");} else { printf("0");}
shift = shift >> 1; // right shift 1 bit } // print bits
return 0;}预期的输出如下:
The decimal value is: -128
* * * * 11111111111111111111111110000000然而实际的输出如下:
The decimal value is: -128
* * * * 11111111111111111111111111111111追踪变量shift的值,发现其最高位(MSB)始终为1,我恍然大悟:这货是算术右移,也就是说会保留符号位。于是,将shift改为(unsigned int)类型,果然解决了这个bug.说明,对于无符号数,进行的是逻辑右移,在移走的位上填0.
下面我们自然会联想到,是不是无符号数的左移为逻辑左移,而有符号数的左移为算术左移(即保留符号位)呢?答案是非也,事实上逻辑左移与算术左移是相同的,都不会保留符号位。
右移运算的结果是不会超出表示范围的。对于有符号数,右移一位后符号位空出,则必须保留原符号位的值;对于无符号数,没有符号位,自然采取逻辑右移。
左移运算则不同,它是有可能超出表示范围的,也就是溢出。对于MSB位为0、MSB-1位为1的有符号正数,及MSB位为1、MSB-1位为0的有符号负数,左移必将益出(以int类型为例,其表示范围是-2^31~2^31-1,对于大于等于2^30和小于等于-2^30-1的值左移1位,也就是乘以2,必然超出了表示范围),且只有在这些情况下符号位才发生变化。既然已经溢出了,保留原符号位还有什么意义?
综上,C语言中可以进行位运算的char/short/int/long (int)/long long (int)类型是有符号的,加上unsigned前缀则是无符号的,两者在右移运算上是有区别的。
补充:软件开发 , C语言 ,