• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

C语言左移右移操作符

武飞扬头像
工业废气
帮助1

看到过很多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("%d\n",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("%d\n",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("%d\n", b);

return 0;

}

经调试,可得到10

学新通 

如果对5向左移2位可得到20,这里不再演示,可见左移有乘以2的效果。

如果对负数进行左移,如-1

#include <stdio.h>

int main()

{

int a = -1;

int b = a << 1;

printf("%d\n", 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("%d\n", b);

return 0;

}

经调试发现结果为正数

学新通 

这是因为

-1073741825的补码为

10111111 11111111 11111111 11111111

左移1位可得

01111111 11111111 11111111 11111110

由于符号位为0此时原码和移位过的补码相等,打印的是下边的原码

01111111 11111111 11111111 11111110

对于正数来说,虽然左移不需要考虑符号位,但也要保证位数足够,不然高位会被舍弃!这里不再演示。

上文已提到过

大部分的C编译器,用移位的方法得到代码比调用乘除法子程序生成的代码效率高。

有人倾向用移位代替乘除法,这样效率高得多。但是请注意一下数据类型,到底是几位的,左移时会不会由于位数不够而高位被舍弃?符号位会不会改变?若位数不够或符号位改变,通过移位来代替乘除法就会产生bug。

如有错误或需要补充,欢迎与博主联系和探讨。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcbaeb
系列文章
更多 icon
同类精品
更多 icon
继续加载