移位运算符
<<和>>
对于左移运算(<<):
高位移出,低位补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
相对于除法/,位运算符会快些。