位运算计算与位运算应用
位运算包括与,或,取反,异或,左移,右移等。
一 位运算计算
1 与运算:&
操作符&将2个数的二进制位进行与操作,2个数对应的位都为1,运算结果为1;否则运算结果为0。
比如 6&8,6的二进制为:0110 8的二进制为:1000。所以6&8 = 0000 = 0
2 或运算:|
操作符|将2个数的二进制位进行或操作,2个数对应的位有一个为1,运算结果为1;否则运算结果为0。
比如 6&8,6的二进制为:0110 8的二进制为:1000。所以6&8 = 1110 = 14
3 取反运算:~
操作符~将每位二进制位取反,1变0,0变1。
比如 ~6,6的二进制位为0110,~6 = ~0110 = 1001 = 9
4 异或:^
操作符^将2个数的每个二进制位异或,2个数对应的位相同,运算结果为0,否则运算结果为1。
比如 6^10,6的二进制为0110,9的二进制为1010, 0110^1010 = 1100 = 12
5 右移运算符:>> 右移运算分二种:逻辑右移和算术右移
①逻辑右移
逻辑右移在移动过程中,左边位用0填充。比如1000 0011右移3位,变成0001 0000
②算术右移
算术右移在移动过程中,左边位用符号位填充。比如1000 0011右移3位,变成1111 0000
6 左移运算符:<<
左移过程中,右边一律用0填充。0000 1100左移2位为0011 0000
在实际的编程过程中,往往会用一个整数的不同位表示不同的数据信息。在访问该整数时,就需要通过位运算来获得或者改变整数的某几位数值。比如在Windows中创建文件时使用的Create数据结构:
struct
{
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT POINTER_ALIGNMENT FileAttributes;
USHORT ShareAccess;
ULONG POINTER_ALIGNMENT EaLength;
PVOID EaBuffer;
LARGE_INTEGER AllocationSize;
} Create;
通常会引用其中的Options如下:
Data->Iopb->Parameters.Create.Options
ULONG Options是一个Windows文件创建过程中的无符号长整数,指示在创建和打开文件时的不同选项。其中高8位指示了CreateDisposition参数(如FILE_OPEN,FILE_CREATE),低24位指示了CreateOptions参数(如FILE_DELETE_ON_CLOSE)。 为了得到CreateDisposition的值,采取下面的位操作:
(Data->Iopb->Parameters.Create.Options >> 24) & 0x000000ff;
将该整数右移24位,再与0xff做与操作,即可获得CreateDisposition的值。
二 位运算应用
1.任何一个数和0异或是它的本身,和自身异或为0:
a^0=a
a^a=0
利用上述性质,可以用来计算2个数的交换。
大家应该知道,在计算机里,两个数互相交换,需要定义一个中间的变量来参与交换。如:
int tmp;
int a=10;
int b=20;
tmp=a;
a=b;
b=tmp;
上述代码计算之后,a和b的值完成交换,a的值为20,b的值为10。
如果用异或运算来交换2个数,可以如下方法:
int a=10;
int b=20;
a=a^b;
b=a^b;
a=a^b;
上述运行之后,a和b依然完成了值的交换,但由于是异或位运算,所以效率比上面的代码要高。
证明:
a=10^20
b=a^b=(10^20)^20=10^20^20=10^0=10
a=a^b=10^20^10=10^10^20=0^20=20
把上述代码,可以封装为一个交换2个数的函数如下:
void swap(int *a, int *b)
{
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
或者用宏来定义:
#define SWAP(a,b) \
do { \
a = a^b; \
b = a^b; \
a = a^b; \
} while(0)
但按照下面的方法来写一个函数,试着将两个数进行交换,是错误的(想想为什么?)
void swap(int a, int b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
2.将整数的第n位置位或清零:
#define BITN (1《n)
置位:a |= BITN;
清零:a &= ~BITN
3.清除整数a最右边的1。
方法:a & (a – 1)//该运算将会清除掉整数a二进制中最右边的1。
问题:如何判断判断整数x的二进制中含有多少个1?
分析:此题是微软公司的一道笔试题。下面用&运算来解决此题。 代码如下:
int func(int x )
{
int countx = 0;
while ( x )
{
countx++;
x = x&(x-1);
}
return countx;
}
4.用异或运算设计一个只有一个指针域的双向链表:
提示:
要实现该设计要求,需要记住链表的头结点和尾结点,并在链表结点的的next域存放前一个结点和后一个结点的异或值。即:
p->next=pl^pr;//头结点的左边结点为NULL,尾结点的右边结点为NULL。
在遍历的时候,从头结点往右遍历的方法:
pl=NULL;
p=Head;
while(p!=Tail)
{
pr=pl^(p->next);
pl=p;
p=pr;
}
从尾结点往左遍历的方法:
pr=NULL;
p=Tail;
while(p!=Tail)
{
pl=pr^(p->next);
pr=p;
p=pl;
}
5.计算下面表达式的值
(char)(127<<1)+1
(char)(-1>>1)+1
1<<2+3
解答:
(char)(127<<1)+1=(01111111<<1)+1=11111110+1=11111111=-1
(char)(-1>>1)+1=(11111111>>1)+1=11111111+1=0
1<<2+3=1<<(2+3)=1<<5=2^5=32(注意《和+的优先级)