2012年(41)
分类: LINUX
2012-03-04 21:52:18
所谓的位运算指的是二进制位的运算。在系统软件中,常要处理二进制位的问题。例如,将一个存储单元中的二进制位左移或右移以为,两个数按位相加等等。
C语言中提供了如表1所列出的位运算符。
表 1
运算符 |
含义 |
运算符 |
含义 |
& |
按位与 |
~ |
取反 |
| |
按位或 |
<< |
左移 |
^ |
按位异或 |
>> |
右移 |
说明:
1、位运算中除~以外,均为二目运算符,即要求两侧各有一个运算量。
2、运算量只能是整型或字符型的数据,不能为实型数据。
下面对各个运算符做简单的介绍。
按位与运算符(&)参加运算的两个数据,按二进制位进行“与”运算。运算规则为:如果两个相应的位上的二进制为都为1,则该位的运算结果也为1,否则为0。即如下的表达式:
0&0=0 0&1=0 1&0=0 1&1=1
如果参加位运算的操作数为负数,则以补码的形式表示二进制数,然后按位进行与运算。
按位与运算有一些特殊的用途如下:
1、清零。
如果想将一个单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合下面的条件:原来数中为1的位置,新数中相应的位为0,然后使二者进行与运算,既可以达到清零的母的。
2、取一个数中某些指定的位。只要将要取的某些位与1相与,就可以得到想要取得位的值。
3、将某一位留下来。要想将某一位留下来,就与一个数进行与运算,次数在该位取1。这一点在嵌入式开发中应用的比较广泛。例如,在点亮一个LED灯的时候,通过控制与之相连的处理器的IO口,取1或者取0 就可以控制LED灯的亮和灭。
按位或运算符( | )两个相应的二进制位中只要有一位为1,该位相或之后的结果就为1。规则如下:
0|0=0 0|1=1 1|0=1 1|1=1
或运算主要是用将一个数据的某些位置1。
异或运算符(^)异或运算符^也成为XOR运算符。它的规则是:若参加运算的两个二进制位同号,则结果为0,异号则为1。即 0^0=0,0^1=1,1^0=1,1^1=0。异或的意思就是判断两个相应的位值是否为“异”,为“异”就为真,否则为假。
我印象中异或运算的一个比较有意思的应用是交换两个值,不使用中间变量。
假设a=3,b=4,想交换a和b的值,可以用下面的赋值语:
a=a^b;
b=b^a;
a=a^b;
上面的三个赋值语句就可以实现交换两个数的值。
说明:
1、执行前两个赋值语句:“a=a^b和b=b^a”相当于b=b^(a^b)。而b^a^b等于a^b^b。 b^b的结果为0,因为同一个数与本身相异或,结果必为0。因此,b的值就等于a^0, 其值为3。
2、再执行第三个赋值语句:a=a^b。由于a的值的等于(a^b),b的值等于(b^a^b), 因此,相当于a=a^b^b^a^b,即a的值等于b的值,即4。
注:
回顾我们所学过的,实现交换两个数的值的方法有4种。
取反运算符(~)~是一个单目运算符,用来对一个二进制数按位取反,即将0变为1,将1变0。取反运算符的优先级比算术运算符,关系运算符,逻辑运算符和其他位运算符都要高。
左移运算符(<<)用来将一个数的各二进制位全部左移若干位。高位左移溢出后就舍弃。左移1 位相当于该数乘以2,左移两位就相当于乘以2的平方,依次类推,左移多少位就相当于该数乘以2的多少次方,但是此结论只适用于该数左移时被溢出舍弃的高位中不含1的情况。
右移运算符(>>)用于将一个数的各二进制位全部右移若干位,移到右端的低位被舍弃,对去符号数,高位补0.右移以为相当于除以2,右移n位相当于除以2的n次方。在右移的过程中,需要注意符号位的问题。对于无符号数,右移时左边高位补0,对于有符号的值,如果原来符号位为0,则左边也是一如0;如果符号位为1,,则左边移入的是1还是0,要取决于所用的计算机系统。移入0的称为“逻辑右移”,移入1的称为“算术右移”。
补充知识:位段位段以位为单位定义的结构体中成员所占用存储空间的长度,含有位段的结构体类型称为位段结构。位段结构也是一种结构体类型,只不过其中含有以位为单位定义存储长度的整数类型位段成员。采用位段结构既节省存储空间,又可方便操作。
位段结构中位段的定义格式为: unsigned <成员名> : <二进制位数>
例:
struct bytedata
{
unsigned a:2; /*位段a,占2位*/
unsigned:6; /*无名位段,占6位,但不能访问*/
unsigned:0; /*无名位段,占0位,表下一位段从下一字边界开始*/
unsigned b:10; /*位段b,占10位*/
int i; /*成员i,从下一字边界开始*/
}data;
位段数据的引用:同结构体成员的数据引用一样,但是应该注意位段的最大值范围不要超过二进制位数定的范围,否则超出部分会丢弃。例如:data.a=2;但是data.a=10;就超出了范围。
关于位段数据,注意以下几点:
(1)一个位段必须存储在同一存储单元(即字)之中,不能跨两个单元。如果其单元空间不够,则剩余空间不用,从下一个单元起存放该位段。
(2)可以通过定义长度为0的位段的方式使下一位段从下一存储单元开始。
(3)可以定义无名位段。
(4)位段的长度不能大于存储单元的长度。
(5)位段无地址,不能对位段进行取地址运算。
(6)位段可以以%d,%o,%x格式输出。
(7)位段若出现在表达式中,将被系统自动转换成整数。
结构体大小原则:由于存储变量时地址对齐的要求,编译器在编译程序时会遵循两条原则:第一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)。第二、结构体大小必须是所有成员大小的整数倍。