几种位操作符
<< ——左移
>> ——右移
& 与——相同位都为1,操作结果才位1
| 或——相同位只要有1,操作结果就为1
~ 非——取反操作
^ 异或——对应位不相同时,操作结果为1
简单应用
1. << >>
//为正数时
int a = 10;
int v = a >> 2;
printf("v=%d\n", v); //v=2
int z = a << 2;
printf("z=%d\n", z); //z=40
右移一位=a÷2;左移一位=ax2
因此,右移n位之后的值为 a÷2^n
左移n位之后的值为 a x 2^n
//为负数时
int b = -10;
int w = b >> 2;
printf("w=%d\n", w);
int r = b << 28;
printf("r=%d\n", r); //左移 右边补0,负数左移有可能会变成正数
结果为
w=-3
r=1610612736
//移动负数位 (两个数的绝对值相加=该类型的位数)
//右移
int c = 10; //int 只有32位(4x8) 0-31为一个循环
int m = c >> 32; //m=c=10;移动32位回到自身
int d = c >> 31;
int q = c >> -1; //右移-1位相当于右移了31位
printf("q=%d\n", q); //q=0
printf("d=%d\n", d); //d=0
//左移
int n = c << -28; //左移-28位相当于左移4位
int h = c << 4;
printf("n=%d\n", n); //n=160
printf("h=%d\n", h); //h=160
2. ^ 异或的简单操作
不创建临时变量,实现两个数的交换
int a = 10;
int b = 20;
//第一种方法 有缺陷,当数值太大时,可能会超出int的范围
printf("a=%d,b=%d\n", a, b);
a = a + b; //30
b = a - b; //10
a = a - b; //20
printf("a=%d,b=%d\n", a, b);
//第二种方法 用异或 ^ 相同为0,不同为1
a = a^b;
b = a^b;
a = a^b;
printf("a=%d,b=%d\n", a, b);
10 0000 0000 0000 0000 0000 0000 0000 1010 a
^
20 0000 0000 0000 0000 0000 0000 0001 0100 b
________________________________________________________
0000 0000 0000 0000 0000 0000 0001 1110 // a=a^b=30
^
20 0000 0000 0000 0000 0000 0000 0001 0100 b
________________________________________________________
0000 0000 0000 0000 0000 0000 0000 1010 // b=30^20=10
^
0000 0000 0000 0000 0000 0000 0001 1110 30
________________________________________________________
0000 0000 0000 0000 0000 0000 0001 0100 a=10^30=20
3.&的简单用法
求一个数存储在内存中的二进制中1的个数
//方法一:一个位一个位判断 效率低
unsigned char a = 56; //0011 1000
int count = 0;
for (int i = 0; i < 8; ++i){ //char只有8个比特位
if (a & 0x01){
count++;
}
a >>= 1;
}
printf("count=%d\n", count);
//方法二:不用循环8次,当没有1时,循环结束
while (a){
count += (a & 0x01);
a >>= 1;
}
printf("count=%d\n",count);
//算数运算
while (a){ //当a=0时 循环结束
if (a % 2 == 1) //说明当前位为1
count++;
a /= 2; //每次都除2,相当于右移一位
//a >>= 1; //位运算效率比算数运算高一点
}
printf("count=%d\n", count);
方法二本质上与方法一是一样的,只是改变了循环条件,不用循环8次,当最后一个1检测完毕后,a变为0,退出循环。本例中,方法二执行6次。
上述三个写法,本质上是一样的,只是在效率上稍有不同。
//方法三:
while (a){
a &= (a - 1);
count++;
}
printf("count=%d\n", count);
//效率高,只与1的个数有关 每&一次消除一个1
// 0100 1101 a
// 0100 1100 a-1 a&(a-1)=0100 1100
//---------------
// 0100 1100 a
// 0100 1011 a-1 a&(a-1)=0100 1000
//---------------
// 0100 1000 a
// 0100 0111 a-1 a&(a-1)=0100 0000
//---------------
// 0100 0000 a
// 0011 1111 a-1 a&(a-1)=0000 0000
//---------------
// 0000 0000 到0循环结束
4.& | ~ 的简单应用
一道面试题:
实现对一个8 bit 数据(unsigned char 类型)的指定位的置0或者置1操作,并保持其他位不变
p_data 是指定的源数据,position是指定位(取值范围1~8) flag表示置0 置1操作,true:置1 false:置0.
思路:将1100 1000 中的第3位 置1时 只需将源数据与0000 0100相或即可
1100 1000
0000 0100
------------------|
1100 1100 其他位不改变将1100 1000中的第4位 置0时 只需将源数据与1111 1011相与即可(即& ~0000 0100)
1100 1000
1111 1011
------------------&
1100 1000
void bit_set1(unsigned char*p_data, unsigned char position, bool flag){
if (flag){
//置1
switch (position){
case 1:
*p_data |= 0x01;
break;
case 2:
*p_data |= 0x02;
break;
case 3:
*p_data |= 0x04;
break;
case 4:
*p_data |= 0x08;
break;
case 5:
*p_data |= 0x10;
break;
case 6:
*p_data |= 0x20;
break;
case 7:
*p_data |= 0x40;
break;
case 8:
*p_data |= 0x80;
break;
default:
break;
}
}
else{
//置0
switch (position){
case 1:
*p_data &= ~0x01;
break;
case 2:
*p_data &= ~0x02;
break;
case 3:
*p_data &= ~0x04;
break;
case 4:
*p_data &= ~0x08;
break;
case 5:
*p_data &= ~0x10;
break;
case 6:
*p_data &= ~0x20;
break;
case 7:
*p_data &= ~0x40;
break;
case 8:
*p_data &= ~0x80;
break;
default:
break;
}
}
}
//简洁版
void bit_set2(unsigned char*p_data, unsigned char position, bool flag){
if (flag)
//置1
*p_data |= (0x01 << (position - 1));
else
//置0
*p_data &= ~(0x01 << (position - 1));
}