C Primer Plus(十五)
第十五章 位操作
15.1 二进制数、位和字节
以2为基数表示的数字称为二进制数,可以使用二进制数将任何整数表示为1和0的一个组合,这种系统非常适合于数字计算机使用。
15.1.1 二进制整数
描述存储器芯片和数据传输率时使用的字节指8位字节。
最小的二进制数是00000000,或一个简单的0.一个字节可以存储的数的范围是0到255.
通过改变对位模式的解释方式,一个字节可以存储从-128到+127之间的整数。
15.1.2 有符号整数
有符号数的表示方法是由硬件决定,而不是由C决定。其中最简单的方法就是保留1位来表述数的符号。
但是该方法有一个确定就是有两个零+0和-0.
二进制补码避免了这个问题。
两种方法的区别在于确定该负数值的方法。从一个9位组合100000000减去一个负数的位组合,结果是该负数值的数量。
例如一个负数的位组合为10000000,值为100000000-10000000 即10000000 128,因此该数为-128. 同理10000001是-127,11111111是-1.
15.1.3 二进制浮点数
浮点数分两部分存储:一个二进制小数和一个二进制指数。
像1/3这样的许多小数不能用十进制计数法精确的表示。同样,许多小数也不能用二进制计数法精确表示。
要在计算机中表示一个浮点数,需要留出若干位存放一个二进制小数,其它位存放一个指数。
15.2 C的运算符
15.2.1 位逻辑运算符
4个位运算符用于整形数据,包括char。这些运算符称为位运算符的原因是它们对每位进行操作,而不影响左右两侧的位。
一、二进制反码或按位取反:~
一元运算符~将每个1变为0,每个0变为1.
二、位与:&
二进制运算符&通过对两个操作数逐位进行比较产生一个新值。对于每个位,只有两个操作数的对应位都为1时结果才为1.
三、位或:|
二进制运算符&通过对两个操作数逐位进行比较产生一个新值。对于每个位,如果其中任意操作数中对应的位为1时结果为1.
四、位异或^
二进制运算符&通过对两个操作数逐位进行比较产生一个新值。对于每个位,如果操作数中对应的位有一个为1(但是不都为1)那么结果为1.
15.2.2 用法:掩码
“位与”运算符通常跟掩码一起使用。掩码是某些位设为开而某些位设置为关的位组合。
假设定义符号常量MASK为2,即二进制的00000010,只有位1是非零。
那么flags&=MASK;这个语句将导致flags的除位1之外的所有位都被设为0.
因为掩码中的零覆盖了flags中相应的位,所以该过程成为“使用掩码”。
15.2.3 用法:打开位
有时,您可能需要打开一个值中特定的位,同时保持其它位不变。
例如,MASK其位1设为1,下列语句中将flags中的位1设为1,并保留其它所有位不变:flags|=MASK;
15.2.4 用法:关闭位
有时,您可能需要关闭一个值中特定的位,同时保持其它位不变。
例如,MASK其位1设为1,下列语句中将flags中的位1设为0,并保留其它所有位不变:flag&=~MASK;
15.2.5 用法:转置位
转置一个位表示如果该位打开则将其关闭,否则将其打开。
该值中对应掩码位为1的位被转置,对应掩码位为0的位则不改变。flag^=MASK;
15.2.6 用法:查看一位的值
假设您希望查看一位的值。例如flag的位1是否为1?
if((flag&MASK)==MASK)
15.2.7 移位运算符
一、左移
左移运算符<<将其左侧操作数的值的每位向左移动,移动的位数由其右侧操作数指定。空出的位用0填充,并且丢弃移出左侧操作数末端的位。(10001010)<<2 ----00101000
二、右移
左移运算符<<将其左侧操作数的值的每位向左移动,移动的位数由其右侧操作数指定。丢弃移出右侧操作数右端的位。对于unsigned类型,使用0填充左端空出的位。对于有符号类型,结果依赖于机器。
三、用法:移位运算符
移位运算能够提供快捷、高效的对2的幂的乘法和除法。
number<<n //number乘以2的n次幂
number>>n //如果number非负,则用number除以2的n次幂
例如:假设您希望使用一个unsigned long值代表颜色值,其中低位字节存放红色亮度,下一字节存放绿色亮度,第三个字节存放蓝色亮度。假设您随后希望每种颜色的亮度存储在各自的unsigned char变量中
#define BYTE_MASK 0xff
unsigned long color=0x002a162f;
unsigned char blue,green,red;
red=color&BYTE_MASK;
green=(color>>8)&BYTE_MASK;
blue=(color>>16)&BYTE_MASK;
四、用法:反转一个值中的最后n位
int invert_end(int num,int bits)
{
int mask=0;
int bitval=1;
while(bits-->0)
{
mask|=bitval;
bitval<<1;
}
return num ^mask;
}
15.3 字位段
对位进行操作的第二种方法是使用位字段,位字段是一个signed int或unsigned int中一组相邻的位。位字段由一个结构声明建立,该结构声明为每个字段提供标签,并决定字段的宽度。
例如,以下声明建立了4个1位字段:
struct{
unsigned int autfd: 1;
unsigned int bldfc: 1;
unsigned int undln: 1;
unsigned int itals: 1;
}prnt;
可以使用普通的结构成员运算符将值赋给单独的字段:例如prnt.itals=0;
因为每个字段都正好为1位,所以1和0是惟一可以用于赋值的值。变量prnt被存储在一个Int大小的存储单元中,但是在本例中仅有其中的4位被使用。
当然也可以创建不同位大小的字段。