移位操作符
<< 左移操作符
>> 右移操作符
移位操作符的操作数只能是正整数,不要移动负数位
因为数据在内存中存放的是补码,所以位操作符移动的是二进制位
<< 左移操作符
移位规则:左边抛弃、右边补0
正数
//正数
int main()
{
int a = 6;
int b = (a << 1);
a 00000000 00000000 00000000 00000110
b 00000000 00000000 00000000 00001100
printf("%d\n", b);//12
printf("%d\n", a);//6
return 0;
}
负数
//负数
int main()
{
int a = -6;
int b = (a << 1);
a 原码10000000 00000000 00000000 00000110
反码11111111 11111111 11111111 11111001
补码11111111 11111111 11111111 11111010
b 补码11111111 11111111 11111111 11110100
b 原码10000000 00000000 00000000 00001100
printf("%d\n", b);//-12
printf("%d\n", a);//-6
return 0;
}
>> 右移操作符
右移操作符有逻辑右移和算术右移。
具体是右移运算符到底是算术右移还是逻辑右移是取决于编译器的,常见的编译器都是算术右移
正数
正数的逻辑右移和算术右移一样的,补位值都为0
//正数
int main()
{
int num = 10;
int n = num >> 1;
num 补码 00000000 00000000 00000000 00001010
n 补码 00000000 00000000 00000000 00000101
printf("n = %d\n", n);//5
printf("num = %d\n", num);//10
return 0;
}
负数
逻辑右移
左边用0填充,右边丢弃(没有分正负)
//负数
int main()
{
int num = -10;
int n = num >> 1;
num 原码 10000000 00000000 00000000 00001010
反码 11111111 11111111 11111111 11110101
补码 11111111 11111111 11111111 11110110
n 补码 01111111 11111111 11111111 11111011
原码 00000000 00000000 00000000 00000100
printf("n = %d\n", n);//5
printf("num = %d\n", num);//-10
return 0;
}
算术右移
左边用原该值的符号位填充,右边丢弃
//负数
int main()
{
int num = -10;
int n = num >> 1;
num 原码 10000000 00000000 00000000 00001010
反码 11111111 11111111 11111111 11110101
补码 11111111 11111111 11111111 11110110
n 补码 11111111 11111111 11111111 11111011
原码 10000000 00000000 00000000 00000100
printf("n = %d\n", n);//-5
printf("num = %d\n", num);//-10
return 0;
}
m = ( left + right )/2
m = left +(( right - left) >> 1 )
//经过观察可以发现 右移一位相当于除以二,
//右移运算符很少越界
位操作符:&、|、^、~
按位与:&
两个中有一个0就为0,两个同为1才是1。
int main()
{
int a = 3;
int b = -5;
int c = a & b;
00000000 00000000 00000000 00000011 3的补码
10000000 00000000 00000000 00000101 -5的原码
11111111 11111111 11111111 11111010 反码
11111111 11111111 11111111 11111011 -5的补码
00000000 00000000 00000000 00000011 c 结果
printf("%d\n", c);//3
return 0;
}
按位或:|
两个中有一个1就是1,两个同时为0才是0.
int main()
{
int a = 3;
int b = -5;
int c = a | b;
00000000 00000000 00000000 00000011 3 补码
10000000 00000000 00000000 00000101 -5 原码
11111111 11111111 11111111 11111010
11111111 11111111 11111111 11111011 -5 补码
11111111 11111111 11111111 11111011 c 补码 结果
10000000 00000000 00000000 00000101 原码 取反加一
printf("%d\n", c);//-5
return 0;
}
按位异或:^
相同为0,相异为1
int main()
{
int a = 3;
int b = -5;
int c = a ^ b;
00000000 00000000 00000000 00000011 3 补码
10000000 00000000 00000000 00000101 -5 原码
11111111 11111111 11111111 11111010 -5 反码
11111111 11111111 11111111 11111011 -5 补码
11111111 11111111 11111111 11111000 c 补码 结果
10000000 00000000 00000000 00001000 原码 取反加一
printf("%d\n", c);//-8
return 0;
}
0与一个数按位异或还是这个数
两个相同数按位异或结果为0
a ^ a = 0 ;
a ^ 0 = 0 ;
这样可以解决不创建临时变量,实现两个数转换,可以参考我之前一篇文章 《实现两个数的交换》。
按位取反:~
所有位置取反
int main()
{
int a = 0;
int c = ~a;
00000000 00000000 00000000 00000000 a 原码
11111111 11111111 11111111 11111111 a 补码
10000000 00000000 00000000 00000000 c 补码
10000000 00000000 00000000 00000001 c 原码
printf("%d\n", c);//-1
return 0;
}
练习
练习:求一个整数存储在内存中的二进制中1的个数
求一个整数存储在内存中的二进制中1的个数
int main()
{
int num = 10;//变成unsigned int可以解决负数问题
int count = 0;
while (num)
{
if (num % 2 == 1)
count++;
num = num / 2;
}
printf("二进制中1的个数= %d\n", count);
return 0;
}
int main()
{
int num = -1;
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (num & (1 << i)) //&有一个0就是0,全是1才是1
count++;
}
printf("二进制中1的个数= %d\n", count);
return 0;
}
n = n & (n-1)
n 111101
n-1 111100
n 111100
n-1 111011
n 111000
n-1 110111
n 110000
n-1 101111
n 100000
n-1 011111
n 000000
int main()
{
int num = -1;
int i = 0;
int count = 0;
while (num)
{
count++;
num = num & (num - 1);
}
printf("二进制中1的个数= %d\n", count);
return 0;
}
练习2:如何判断一个数是否是2的次方数
int main()
{
int a = 0;
scanf("%d", &a);
if ((a & (a - 1))==0)
{
printf("%d就是二次方数",a);
}
else
{
printf("%d不是二次方数",a);
}
return 0;
}
练习3:将13二进制序列的第5位修改为1,然后再改回0
int main()
{
int a = 13;
a = a | (1 << 4); // | 有1就是1,两个0才为0
a 补码 00000000 00000000 00000000 00001101
1<<4 00000000 00000000 00000000 00010000
结果 00000000 00000000 00000000 00011101
printf("a = %d\n", a);//29
a = a & ~(1 << 4);// & 有0就是0,两个1才是1
a 补码 00000000 00000000 00000000 00011101
~(1<<4) 11111111 11111111 11111111 11101111
结果 00000000 00000000 00000000 00001101
printf("a = %d \n", a);//13
return 0;
}