概述
看到过很多C语言中的移位操作相关的博文,但是这些博文之间的深度实在是差别太大了,对于初学者来说,想要弄明白移位操作并不容易,有的博文仅仅是讲了怎么进行位移,初学者可能会因此写下bug,有的博文虽然到位但只是泛泛而过,让初学者难以理解。此文章由博主查阅多处博客、课程整理,主要面向初学者,同时又不乏深度,由浅及深,助力初学者更好地掌握C语言左移右移操作符。
移位是将数值向左向右移动,对于十进制来说就是实现放大十倍和缩小十倍的效果,而对于二进制而言就是放大两倍和缩小两倍的效果。
大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。
在C语言中,char、short、int、long、unsigned char、unsigned short、unsigned int、unsigned long都可以进行移位操作,而double、float、bool、long double则不可以进行移位操作。
移位操作符分为算术移位和逻辑移位:
算术移位右边丢弃,左边补上原符号位,符号位和原首位之间补0,在visual studio中进行的是算术移位;
逻辑移位,就是不考虑符号位,移位的结果只是数据所有的位数进行移位。根据移位操作的目的,左移时,低位补0,右移时,高位补0。对于有符号数而言,逻辑位移没有太大意义,如果一个负数,逻辑右移,结果就会变成正数,例如10000101=-5>>1=01000010=66。
对于无符号类型的数据,所有移位操作都是逻辑移位。
对于有符号类型的数据,依据编译器不同而选择到底采用逻辑移位还是算术移位。(所以平台不同会导致移植性问题!)本例中,Visual Studio 2019进行的是算数移位,若想达到逻辑右移的效果C语言中可以利用“((unsigned int)(-5))>>n”来达到逻辑右移的效果(这里强调一样的效果,至于原理是否相通值得深究),这样最高位就就是补0了。
1)右移
右移操作符移动的是二进制位。
我们现在定义变量a,并赋值为-1,那么它在内存里存储为10000000 00000000 00000000 00000001
进行移位操作时,是对a的补码进行移位,也就是对
11111111 11111111 11111111 11111111 进行移位
int main()
{
int a = -1;
//a为10000000 00000000 00000000 00000001
//移位时移动的是11111111 11111111 11111111 11111111
return 0;
}
接下来我们对a进行移位操作,向右移动一位
#include <stdio.h>
int main()
{
int a = -1;
int b = a>>1;
printf("%dn",b);
return 0;
}
在进行调试后,发现得到的值仍为-1
这是因为移位时是对数值的补码进行移位,并且是进行的算术移位(上文有提到Visual Studio 2019进行的是算数移位)。
-1的补码为
11111111 11111111 11111111 11111111
右移1位可得
0111111 11111111 11111111 111111111
符号位补1
11111111 11111111 11111111 11111111
打印时还原成原码为
10000000 00000000 00000000 00000001
我们现在定义变量a,并赋值为16,对其进行向右移1位的操作
a为00000000 00000000 00000000 00010000
此时a为正数,补码与原码相同。
#include <stdio.h>
int main()
{
int a = 16;
int b = a>>1;
printf("%dn",b);
return 0;
}
在进行调试后,在进行调试后,发现得到的值为8
我们对其移位过程进行分析:
16的补码为
00000000 00000000 00000000 00010000
右移1位可得
00000000 00000000 00000000 00001000
移位前,根据二进制转十进制法则可得a=1*2^4=16
移位后,同上法则可得a=1*2^3=8
如果再对a右移1位可以得到4,这里不再演示。
这里可以发现右移有除以2的效果,向右移几位就是除以2的几次幂
但是有符号整数向右移位运算不等同于除以2的某次幂,如(-1)/2和(-1)>>1,前者的结果一般是0,后者一般是-1,所以在C语言中,负数向右移动1位并不等同于除以2。
此外,负的偶数向右移动1位也是除以2,负的奇数向右移动1位等于此数除以2,再减1,可自行调试。
2)左移
左移相对右移更为简单,即左边丢弃,右边补0,统统进行逻辑移位。
我们现在定义变量a,并赋值为5,那么它在内存里存储为00000000 00000000 00000000 00000101
也就是要对00000000 00000000 00000000 00000101进行移位
#include <stdio.h>
int main()
{
int a = 5;
int b = a << 1;
printf("%dn", b);
return 0;
}
经调试,可得到10
如果对5向左移2位可得到20,这里不再演示,可见左移有乘以2的效果。
如果对负数进行左移,如-1
#include <stdio.h>
int main()
{
int a = -1;
int b = a << 1;
printf("%dn", b);
return 0;
}
经调试可得-2
但这并不意味着符号位不动,也不意味着所有的负数左移能得到乘以2的效果,上文提到过左移操作是对补码进行操作。打印时候是对原码进行打印。很多人都把左移当成是对原码左移了,所以也不是符号位不动。
是因为-1的补码中有太多的1,-1的补码为11111111 11111111 11111111 11111111,向左移动1位并不会影响到后边的1成为符号位,位数足够所以最高位仍为1。
现在对-1073741825向左移1位
#include <stdio.h>
int main()
{
int a = -1073741825;
int b = a << 1;
printf("%dn", b);
return 0;
}
经调试发现结果为正数
这是因为
-1073741825的补码为
10111111 11111111 11111111 11111111
左移1位可得
01111111 11111111 11111111 11111110
由于符号位为0此时原码和移位过的补码相等,打印的是下边的原码
01111111 11111111 11111111 11111110
对于正数来说,虽然左移不需要考虑符号位,但也要保证位数足够,不然高位会被舍弃!这里不再演示。
上文已提到过
大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。
有人倾向用移位代替乘除法,这样效率高得多。但是请注意一下数据类型,到底是几位的,左移时会不会由于位数不够而高位被舍弃?符号位会不会改变?若位数不够或符号位改变,通过移位来代替乘除法就会产生bug。
如有错误或需要补充,欢迎与博主联系和探讨。
最后
以上就是可爱帅哥为你收集整理的C语言左移右移操作符详解的全部内容,希望文章能够帮你解决C语言左移右移操作符详解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复