关于位运算相关内容分为两个大博文,分别为位运算1,即该博文;主要介绍二进制、位、字节的基本概念、进制转换问题,以及C的~
&
|
^
按位运算符及应用方法
由于篇幅限制,位运算2主要讲述左移和右移运算及编程练习以及字段运算
通过应用可以看出,位运算主要应用在计算机软件与硬件交互的过程中使用,或者可以说是更底层的东西
C语言也是编写设备驱动程序和嵌入式开发的首选语言
二进制数(binary number):
以2为基底表示的数字被称为二进制数
例如二进制数1101可表示为:1x23+1x22+0x21+1x20
二进制系统可以把任意整数表示为0和1的组合,关于进制转换问题请参考博文进制转换相关内容
通常,1字节包含8位。C语言用字节(byte)表示存储系统字符集所需的大小
所以C字节可能是8位、9位、16位或其它值。但是,描述存储器芯片和数据传输率中所用的字节指的是8位字节。
本博文内容假设1字节=8位
通常用用术语 八位组(octet) 这个术语特指8位字节
计算机中从左往右给八位分别变好7~0。编号为7的成为高阶位(high-order bit) ,编号是0的位称为低阶位(low-oder bit) 每一位对应2的相应指数
如上图,1字节能表示的最大数字为11111111
其对应的十进制值为255
11111111=1x27+1x26+1x25+1x24+1x23+1x22+1x21+1x20=255
因此1字节可以存储0~255范围内的数字,总共256个值
对于unsigned char 中1字节存储范围为0~255,
对于signed char 中1字节表示范围为-128~+127
如何表示有符号整数取决于硬件,而不是C语言。一般地,C中用高阶位状态1表示负数,高阶位0表示正数
针对具体数的表示问题上及硬件关系,提出数的源码、反码、补码表示方法,具体请参见博文源码、反码、补码部分
浮点数分为两部分存储:二进制小数和二进制指数
1. 二进制小数表示方法
.101
的二进制表示为:1/21+0/22+1/23
.101
的十进制表示为:1/2+0/4+1/8=0.5+0+0.125
即,.101对应的十进制值为0.625
可以看出,二进制的表示法以2的指数进行计算,即二进制表示法只能精确的表示多个1/2的幂的和。
3/4和7/8可以精确的表示为二进制小数,但1/3和2/5却不能
2. 浮点数表示方法:
其它进制数包括八进制和十六进制数,相关内容参看博文:八进制、十六进制部分
C中有两个操作位的工具。第一个工具为一套(6个)作用于位的按位运算符,第二个工具是字段(field) 数据形式,用于访问int中的位。该部分将介绍相关按位运算符。
按位运算符分为两类:一类是按位逻辑运算符,另一类是位移运算符
对于按位逻辑运算符请注意:
即,1字节8位,按位运算符对8位中的某一位操作,而常规逻辑运算符对整个字节操作
由于浮点型数据的表示上的特殊性,所以按位运算只在整型数据上应用
~
一元运算符~
将1变成0,0变成1
例如:
原始值10011010
进行取反操作:~(10011010)
结果值为:01100101
值得注意的是:
~
不会改变原始值,但是该运算符创造了一个可以使用或赋值的新的值。
如:
int val=154;// val=10011010
int newVal=0;
newVal=~val;// ~(10011010)
printf("val=%d\t,newVal=%d\n",val,newVal);
&
二元运算符&
通过逐位比较两个运算对象,生成一个新值。
对于每个位,只有两个运算对象中相应的值都为1时,结果才为1(从真假方面看,只有当两个位都为真时,结果才为真)。
如:
int valA=147;// valA=10010011
int valB=61;//valB=00111101
int newVal=0;
newVal=valA&valB;// newVal=00010001
printf("valA=%d\n,valB=%d\n,newVal=%d\n",valA,valB,newVal);
&=
按位与运算符和赋值结合运算符,针对上面代码,可以写成如下形式
int valA = 147;// valA=10010011
valA &= 61;// ValA=00010001
printf("valA=%d\n",valA);
|
二元运算符|
通过逐位比较两个运算对象,生成一个新值。
对于每个位,如果两个运算对象中相应的位为1,结果就为1(从真假方面看,如果两个运算对象中相应的一个位为真或者两个位都为真,那么结果为真)
如:
int valA=163;//valA=10010011
int valB=61;//valB=00111101
int newVal=0;
newVal=valA|valB;//newVal=191
printf("valA=%d\nvalB=%d\nnewVal=%d\n",valA,valB,newVal);
针对
|=
有如下书写
int val=163;
val |= 61; ====等价于 val= val|61;
此时val的值为191
^
二元运算符^
逐位比较两个运算对象。
对于每个位,如果两个运算对象中相应的位一个为1(但不是两个为1),结果为1(从真假方面看,如果两个运算对象中相应的一个位为真且不是两个位同为1,那么结果为真)
如:
int valA=147;//valA=10010011
int valB=61;//valB=00111101
int newVal=valA^valB;
printf("valA=%d\nvalB=%d\nnewVal=%d",valA,valB,newVal);
同样有
^=
运算符
int val=147;
val ^= 61; ======等价于 val= val^61
val=174
按位与&
运算符常用于掩码(mask)。
掩码指的是一些设置为开(1)或关(0)的位组合。
掩码的使用过程
设定义符号常量MASK=2(二进制形式为00000010)
则语句:
int flages=150;//flages=10010110
int MASK=2;//MASK=00000010
flages &= MASK;
printf("flages=%d\n",flages);
表示掩码的使用,其作用过程如下
有时,需要打开一个值中的特定位,同时保持其它位不变
例如,向一台IBM PC 通过向端口发送值来控制硬件;
例如,为了打开内置扬声器,必须打开1号位,同时保持其他位不变。这种情况可以使用按位或运算符
按位或|
运算符常用于位的打开
例如:
int flages=31;//flages=00001111
int MASK=182;//MASK=10110110
flages |= MASK;
printf("flages=%d\n",flages);
将某个位值设为0,通常需要&
运算符
如:
int MASK=182;//MASK=10110110
int flags=15;//falgs=00001111
flags &= ~MASK;
printf("flags=%d",flags);
此操作关闭flags的第1 2 号位
切换位值得是打开已关闭的位或者关闭已打开的位
通常使用按位异或^
运算符
如:
int flags=15;//flags=00001111
int MASK=182;//MASK=10110110
flags ^= MASK;
printf("flags=%d\n",flags);
有时需要检查某位的值是否被设定为期望值
比如为比较flags中1号位的值是否为1
通常的做法为:
int MASK=2;//MASK=00000010
int flags=54;//flags=00110110
if((flags&MASK) == MASK)
{
puts("number 2 has been set as 1");
}
else
{
puts("number 2 set error");
}