author:&Carlton
tag:C语言
topic:位运算
book:谭浩强 《C语言程序设计》 第五版
date:2023年7月10日
位运算和位运算符
位运算指以二进制位为对象的运算,参加位运算的对象只能是整型或字符型。
如果参加位运算的是负数,则以补码形式表示为二进制数。
运算符 | 含义 |
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~ | 按位取反 |
<< | 左移 |
>> | 右移 |
按位与“&”
参加运算的两个数据按二进制位进行”与“运算。如果两个相应的二进制都为1,则该位的结果值为1,否则为0。
即0&0=0,0&1=0,1&0=0,1&1=1
重要用途
①清零
原来的数(二进制形式下)中为1的数,找一个相应位为0的数,其他位不做要求,然后做”&“运算。
②取一个数中某些指定位
要想将哪一位保留下来,就与一个数进行”&“运算,此数在该位取1,其他位取0
按位或“|”
两个对应的二进制位中只要有一个为1,该位的结果值为1。
即0|0=0,0|1=1,1|0=1,1|1=1
重要用途
常用来对一个数据的某些位定值为1。
找一个相应位为1,其他位为0的数与这个数进行“|”运算即可。
“异或”运算“^”
也称XOR运算符。
参加运算的两个二进制位异号,则得到1(真),若同号,则结果为0(假)。
即0^0=0,0^1=1,1^0=1,0^0=0
重要用途
①使特定位翻转
要使哪几位翻转就找一个相应位为1,其余位为0的数与其做“^”运算。
原理:
相应位为1:1^1=0,0^1=1 //翻转
取余位为0:1^0=1,0^0=0 //不变
②交换两个值,不用临时变量(temp等第三方)
a=a^b;
b=b^a;
a=a^b;
//完成值交换操作
原理:(注意追踪变量变化情况,用原始a、b的值参与证明)
(1)第二行执行时,b=b^a即b=b^(a^b) //没有加粗的a,b值代表了原始值
(2)A^B=B^A,即顺序可以调换,不影响结果,满足交换律(性质)
(3)结合(1)(2),b=(a^b)^b
(4)另外(b^a)^b=(a^b)^b,则(b^a)^b=b=b^(a^b),即满足“结合率”,(推论)
(5)A^A=0,A^0=A,与自己异或值为0,以0异或值为自己。
(6)则b=(a^b)^b=a^(b^b)=a^0=a,a的值给了b(结论)
(7)类似的,第三行a=a^b= (a^b)^a=a^a^b=0^b=b,b的值给了a。
//没有加粗的a,b值代表了原始值
取反运算“~”
将0变为1,将1变为0。
取反运算符的优先级比算术、关系、逻辑和其他位运算符都高
重要用途
使得特定位为0
有些时候找一个特定位为1,其余位为0的数要比找一个特定位为0,取余位为1的数要简单(特别是在特定位数量比其余位数量大很多时),此时结合“~”和“&”运算可以使目标数特定位为0
e.g
让数a的最后一位变为0,只需 a=a & ~1
左移运算“<<”
“<<”用来将一个数的各二进制位全部左移若干位,右补0,溢出的舍弃
左移n位等于该数乘以2的n次方(只适用于溢出舍弃的高位中不包含1的情况),左移运算比乘法运算快很多,因此用左移进行大幅度的数值倍增能提高程序性能。
右移运算“>>”
将一个数的各二进制位全部右移若干位,左补0或1,溢出的低位舍弃
对于无符号整数,左补0
对于有符号且为负的整数,左补0为逻辑右移,左补1为算术右移
右移n位相等于除以2的n次方(只适用于溢出舍弃的高位中不包含1的情况)
对不同长度的数据进行位运算
例如int和short型,系统会将二者按右端对齐,若short型数据为有符号的负数,左端补满1,其余情况补满0
位运算举例
取位
把整数a从右端开始的4~7位取出来
核心算法:
①简单移位,方便寻找另外一个数搭配位运算 a>>4
②寻找一个数用于取位 ~(~0<<4)
③位运算 &
答案: (a>>4)&~(~0<<4)
循环移位
限定:原数据为无符号整数,16位
核心算法:
①取右边n位,并找另外一个变量存储这n位
①将右边n位左移到高位处,并存储到另一个变量去 b=a<<(16-n)
②剩下的右移n位 c=a>>n
③这两个二进制数进行位运算 c= b | c
答案:见算法
位段
有时存储一个信息不必用一个或多个字节,常常在一个字节中放几个信息。
所以我们要探究怎样向一个字节中的一个或几个二进制位赋值或改变它们的值呢?
人为地将一个整型变量data分为几段
例如,人为规划一个16位的整型变量k为a,b,c,d四段(分别占2、6、4、4位),现想改变c段的值
核心算法:
①c段清零 k=k & ~240 或者 k=k&~(15<<4) //240为十进制,其二进制为 1111 0000
②找一个在c段满足要存放数值的数,比如3 t=0011 0000 或者 t=3<<4
③两数按位运算 k=k|t
答案:见算法
感悟:位移还挺好用的
使用位段
可以使用以位为单位的结构体
类型名 [成员名]:宽度
struct Packed_data
{
unsigned a:2;
unsigned b:2;
unsigned c:2;
unsigned d:2;
short i;
}data;
其余详细细节暂不一 一列举,请看参考其他文献。
重点是介绍了位运算符,分析了一些经典的位运算例子
欢迎指正与分享,谢谢!