位运算(包括OR,AND,XOR,NOR,NAND,XNOR,以及&0xFF等常见应用)
位运算是C语言的重要特点,是其他计算机高级语言所没有的,位运算是以二进制为对象的运算,二进制表示法与计算机内存完全对应,每个单元(位)都可以设置成开(1)或关(0)。而且位运算比正常的运算符速度要快很多,因为位运算是一种底层的运算,但是理解起来会偏难一些。
其他补充:
1、位运算符中除~外,均为耳目运算符
2、运算只能是整型或者字符型的数据,不然会报错
常见的位运算符
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 0&0=0; 0&1=0; 1&0=0; 1&1=1 同1为1 |
| | 或 | 0|0=0; 0|1=1; 1|0=1; 1&1=1 同0为0 |
~ | 取反 | ~1=0; ~0=1 取反 |
^ | 异或 | 0^0=0; 0^1=1; 1^0=1; 1^1=0 不同为1,相同为0 |
<< | 左移 | 各二进制全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进制全部右移若干位,低位丢弃。对正数左补0,负数左补1 |
1、按位与操作
例如: 7&5的结果为5,计算如:
常见用途:
清零: 如果想将一个单元清零,让其与一个各位都为0的数值相与
取数(常见保留低八位,或者低16位,图像中常见):
例如,X=1001110101, 进行操作 X&0xFF 之后得到 01110101 Y=10000000000100111,进行操作 Y&0xFFFF 之后得到 0000000000100111
2、按位或操作
例如: 7|5的结果为7,计算如:
常见用途:
负数补码
将一个数的某些‘’位‘置为1 例如:把Z=10100110的低4位的数置为1,Z|00001111即可满足目的。
3、取反运算
例如:~7的结果为-8,计算如:
常见用途:
使一个数的最低位为0,例如:Q&~1, ~1的值为1111111111111110,在与操作之后,最低位一定为0,~的优先级高于算数运算符、关系运算符、逻辑运算符等
4、异或运算 不同为1,相同为0
例如:10^-9的结果为-3 计算如:
即 0000 1010 ^ 1111 0111 = 1111 1101(补码) 原码即为1000 0011 即10^-9 = -3
常见用途:
使特定位翻转:例如Q=10110110,使Q低4位翻转,用X ^ 0000 1111 = 1011 1001即可得到
与0相异,保留原值: Q^0000 0000 = 10110110
交换A和B:
void swap(int &a, int &b)
{
a = a^b;
b = a^b;
a = a^b;
}
不推荐以上这个交换方式,相应还有一种加减的方法交换数值(如下所示),同样不推荐,难懂且效率不如中间临时变量的方法。
void swap(int &a, int &b)
{
a = a + b;
b = a - b;
a = a - b;
}
5、左移运算
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)
例如:X =18; X= X <<2,结果为72 计算如:
上述左移一位后X=X*2; 若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。
6、右移运算
将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃
例如:X =18; X= X >>2,结果为4 计算如:
常见用途:
操作数每右移一位,相当于该数除以2(向下取整)。
注意:
移位运算符在C++中会生成一个新值,但不回修改原来的值,例如:
int x = 27;
int y = x << 2;
cout << y << endl;
y << 3;
cout << y << endl;
上述代码两次输出的值均为108,即不会修改x的值,表达式x<<2使用x的值来生成一个新的值,就像x+3会申城一个新值,但不会修改x一样,如果要用移位运算符来修改变量的值,则必须使用赋值运算符,可以使用常规的赋值运算符或者<=运算符,如:
x = x<<4;
y <<=2
位运算的一些其他应用:
1、判断奇偶数
对于除0之外的任何数,使用X&1==1作为逻辑判断即可,例如:
x = 21;
if(x&1==1)
{
cout << "x为奇数";
}
2、判断某个二进制是否为1
//例如判断x的第五位是否为1,十六进制的0x10转换为二进制是0001 0000
if(x&0x10==1)
{
cout << "x为奇数";
}
3、求平均数
int average(int a, int b)
{
return (a & b) + ((a ^ b) >> 1);
//也可以用:return a - ((a - b) >> 1);
//上面第一个会向下取整,第二个向上取整,例如输入10,11时,第一个返回10,第二个返回11
}
4、判断两个数是否异号
bool jtn(int a, int b)
{
return ((a^b) < 0);
}
5、数据加密
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define KEY 0x75
int main()
{
char p_data[16] = { "我和我的祖国" };
char Encrypt[16] = { 0 }, Decrypt[16] = { 0 };
for (int i = 0; i < strlen(p_data); i++)//加密
{
Encrypt[i] = p_data[i] ^ KEY;
}
for (int i = 0; i < strlen(Encrypt); i++)//解密
{
Decrypt[i] = Encrypt[i] ^ KEY;
}
printf("Initial date: %s\n", p_data);
printf("Encrypt date: %s\n", Encrypt);
printf("Decode date: %s\n", Decrypt);
return 0;
}
上述输出结果为:
Initial date: 我和我的祖国
Encrypt date: 户细户辣虖
Decode date: 我和我的祖国
6、取绝对值(效率高)
int abs(int n)
{
return (n ^ (n >> 31)) - (n >> 31);
}
或者
int abs(int n)
{
int i = n >> 31;
return i == 0 ? n : (~n + 1);
}
其他操作参见https://zhuanlan.zhihu.com/p/148790042
另外对于一些符号,例如OR,AND,XOR,NOR,NAND,XNOR以及其实现等简单说一下
OR | 或 | | |
---|---|---|
AND | 与 | & |
XOR | 异或 | ^ |
NOR | 或非 | ~(a|b) |
NAND | 与非 | ~(a&b) |
XNOR | 异或非 | ~(a^b) |
对于以上提到的&0xFF可以参考 https://blog.csdn.net/i6223671/article/details/88924481
更多位运算(涉及原码补码等)参考 https://zhuanlan.zhihu.com/p/42175031
很多代码参考:https://zhuanlan.zhihu.com/p/148790042
https://blog.csdn.net/weixin_42216574/article/details/82885102
https://tangxuan1023.github.io/2018/02/09/%E4%BA%A4%E6%8D%A2%E4%B8%A4%E6%95%B0%E7%9A%84%E5%80%BC/#more