位运算计算与位运算应用

位运算包括与,或,取反,异或,左移,右移等。

一 位运算计算

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(注意《和+的优先级)

  

  

posted on 2019-05-27 12:26  锋邢天下  阅读(713)  评论(0编辑  收藏  举报

导航