移位运算符详解

移位运算符

<<和>>

对于左移运算(<<):

高位移出,低位补0

3<<2

3是int型,占4个字节(32位),二进制表示为0000 0000 0000 0000 0000 0000 0000 0011

左移2位后,变成0000 0000 0000 0000 0000 0000 0000 1100

十进制是12.

对于右移运算(>>):

低位移出,高位空出的位可以由0填充,或者由符号位的副本填充(如果是正数,补0;如果是负数,补1).

至于高位究竟如何填充,

-8>>2

原码:0000 0000 0000 0000 0000 0000 0000 1000

反码:1111 1111 1111 1111 1111 1111 1111 0111

补码:1111 1111 1111 1111 1111 1111 1111 1000

右移2位后,变成:

1111 1111 1111 1111 1111 1111 1111 1110

#include <stdio.h>
// linux下打印-8>>2后的二进制,itoa函数不可用
int main()
{
	int x = -8;
	int int_bits = 31;
	x = x >> 2;
	printf("x二进制=");
	for(;int_bits >= 0; int_bits--)
		printf("%1d", (x&(1<<int_bits))?1:0);
	printf("\n");
	return 0;
}
//x二进制=11111111111111111111111111111110

空出的高位被符号位填充。

在vs2019下测试高位空出部分也是用符号位填充。

备注:VS2019下,本来想用itoa()函数转换二进制的,结果提示我使用_itoa_s()函数,然后这个函数貌似不能处理负数,正数可以

一个int型数据可以移位的范围是多少?

如果被移动对象长度是n位,那么移动位数必须>=0,并且严格小于n。

int型数据是32位,至多可移动31位,至少可移动0位。

#include <stdio.h>

// centos7.4下gcc编译
int main()
{
	int x = 1;
	int y1, y2, y3, y4;
	int int_bits = 31;
	y1 = x << 0;
	y2 = x << 31;	//注意,最高位被置1,变成负数
	y3 = x << -1;
	y4 = x << 32;
	printf("y1=%d\ny2=%d\ny3=%d\ny4=%d\n", y1, y2, y3, y4);
	return 0;
}
/*
编译警告:
main.c:11:2: warning: left shift count is negative [enabled by default]
  y3 = x << -1;
  ^
main.c:12:2: warning: left shift count >= width of type [enabled by default]
  y4 = x << 32;

运行结果:
y1=1
y2=-2147483648
y3=-2147483648
y4=1
*/
#include <stdio.h>
// centos7.4下gcc编译
int main()
{
	int x = 1;
	int y1, y2, y3, y4;
	int int_bits = 31;
	y1 = x << 0;
	y2 = x << 31;
	y3 = x << -2;	// 改为左移-2位
	y4 = x << 33;	// 改为左移33位
	printf("y1=%d\ny2=%d\ny3=%d\ny4=%d\n", y1, y2, y3, y4);
	return 0;
}

/*
警告:
main.c:11:2: warning: left shift count is negative [enabled by default]
  y3 = x << -2;
  ^
main.c:12:2: warning: left shift count >= width of type [enabled by default]
  y4 = x << 33;
  
运行结果:
y1=1
y2=-2147483648
y3=1073741824
y4=2
*/

不过,通过上面2个例子,即使移位超出范围,编译器给出了警告,他还是有值的。

当位移运算符的操作数超出范围时如何处理呢?

假设X<<n,n是操作数,当n>32时,会变成n%32=商…余数,取余数,然后再X<<余数。

所以上面例子中x<<32,x<<33等价于x<<0,x<<1;

当n<0时,可以这样记:每次实际移位是X<<(32-n的绝对值)。

即x<<-1,x<<-2等价于x<<31,x<<30;

以上结果在gcc编译器下测得,在VS2019下编译发现结果不一样

#include <stdio.h>
// msvc2017编译器测试
int main()
{
	printf("%d\n", 1 << 32);	// 0
	printf("%d\n", 1 << 33); 	// 0
	printf("%d\n", 1 << -1);	// 0
	printf("%d\n", 1 << -2);	// 0
	printf("%d\n", 1 << -3);	// 0
	return 0;
}
/*
编译警告:
1>C:\Users\Administrator\source\数据结构\C++\C++\main.c(6,24): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
1>C:\Users\Administrator\source\数据结构\C++\C++\main.c(7,24): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
1>C:\Users\Administrator\source\数据结构\C++\C++\main.c(8,24): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
1>C:\Users\Administrator\source\数据结构\C++\C++\main.c(9,24): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
1>C:\Users\Administrator\source\数据结构\C++\C++\main.c(10,24): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
*/

超出数据长度范围的操作数,结果都是0。

移位运算符只适用于整数
#include <stdio.h>

int main()
{
	char c[10] = "hello";
	c = c << 1;
	printf("c=%d\n", c);
	return 0;
}
/*
编译错误:
main.c:7:8: error: invalid operands to binary << (have ‘char *’ and ‘int’)
  c = c << 1;
*/
#include <stdio.h>
#include <stdlib.h>

int main()
{
	double d = 1.0;
	d = d << 1;
	printf("d=%d\n", d);
	return 0;
}
/*
编译错误:
main.c:7:8: error: invalid operands to binary << (have ‘double’ and ‘int’)
  d = d << 1;
*/
#include <stdio.h>
#include <stdlib.h>

int main()
{
	char c = 'a';	//c默认为有符号字符,即signed char
	c = c << 2;
	printf("c=%d\nsizeof(c)=%d\n", c, sizeof(c));
	return 0;
}

/*
c=-124
sizeof(c)=1
*/

对于字符C,值为ASCII字符a,对应十进制为97,二进制为:

0110 0001,一个字节

左移2位后,变成0110 0001 00,再赋值给c,由于c只占1个字节,最后变成1000 0100,这是一个负数,

负数需要以补码表示,变成1111 1100,十进制就是-(12+16+64+32)=-124。

移位运算符快在哪?
	int x = 1;
00007FF77754175A  mov         dword ptr [x],1  
	x = x << 2;
00007FF777541761  mov         eax,dword ptr [x]  
00007FF777541764  shl         eax,2  
00007FF777541767  mov         dword ptr [x],eax  
	int x = 1;
00007FF74C29175A  mov         dword ptr [x],1  
	x = x * 2;
00007FF74C291761  mov         eax,dword ptr [x]  
00007FF74C291764  shl         eax,1  
00007FF74C291766  mov         dword ptr [x],eax
	int x = 8;
00007FF75C51175A  mov         dword ptr [x],8  
	x = x >> 2;
00007FF75C511761  mov         eax,dword ptr [x]  
00007FF75C511764  sar         eax,2  
00007FF75C511767  mov         dword ptr [x],eax  
	int x = 8;
00007FF7D325175A  mov         dword ptr [x],8  
	x = x / 2;
00007FF7D3251761  mov         eax,dword ptr [x]  
00007FF7D3251764  cdq  
00007FF7D3251765  sub         eax,edx  
00007FF7D3251767  sar         eax,1  
00007FF7D3251769  mov         dword ptr [x],eax  

相对于除法/,位运算符会快些。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值