C入門---位運算

程序中的全部數在計算機內存中都是以二進制的形式儲存的。位運算直接對整數在內存中的二進制位進行操做。因爲位運算直接對內存數據進行操做,不須要轉成十進制,所以處理速度很是快。算法

(1),與(&)運算編程

"&"運算一般用於二進制取位操做,例如一個數 & 1 的結果就是取二進制的最末位。這能夠用來判斷一個整數的奇偶,二進制的最末位爲0表示該數爲偶數,最末位爲1表示該數爲奇數。windows

(2),或(|)運算數據結構

」|「運算一般用於二進制特定位上的無條件賦值,例如一個數 | 1 的結果就是把二進制最末位強行變成1。若是須要把二進制最末位變成0,對這個數| 1以後再減1就能夠了,其實際意義就是把這個數強行變成最接近的偶數。函數

(3),異或(^)運算加密

"^"運算一般用於對二進制的特定一位進行取反操做,由於異或能夠這樣定義: 0和1 異或0都不變,異或1則取反。對象

 "^"運算的逆運算是它自己,也就是說兩次異或同一個數最後結果不變,即(a^b)^b = a。」^"運算能夠用於簡單的加密,好比你想對一個朋友說1314520,但怕別人知道,因而雙方約定拿生日19800205做爲密鑰,1314520 ^ 19800205 = 20590165, 因而就把20590165告訴這個朋友。你的朋友再次計算20590165^19800205的值,獲得1314520,因而它就明白了你的意圖。內存

加法和減法互爲逆運算,而且加法知足交換律。能夠寫出一個不須要臨時變量的swap函數it

void swap(int a, int b)io

{

    a = a + b;

    b = a - b;

    a = a - b;

}

因爲^的逆運算是它自己,因而就有了一個很特別的swap過程:

void swap(int a, int b)

{

    a = a ^ b;

    b = a ^ b;

    a = a ^ b;

}

(4),取反(~)運算

「~」運算的定義是把內存中的0和1所有取反。使用「~」運算時要格外當心,須要注意整數類型有沒有符號。若是「~」的對象是無符號整數(不能表示負數),那麼獲得的值就是它與該類型上界的差。由於無符號類型的數是用00到$FFFF依次表示的。下面的程序返回65435。

#include <stdio.h>

int main()

{

    unsigned short a = 100;

    a = ~a;

    printf("%d\n",a);

    return 0;

}

若是「~」的對象是有符號的整數,狀況就不同了, 稍後會提到。

(5),左移(<<)運算

  a << b 表示把 a 轉爲二進制後左移 b 位(在後面添b個0)。如100的二進制爲1100100,而110010000轉成十進制是400.能夠看出 a<< b的值實際上就是a 乘以 2的 b次方,由於在二進制數後面添加一個0 就至關於該數乘以2.

一般認爲 a <<1 比 a * 2更快,由於前者是更底層的操做,所以程序中乘以2的操做請儘可能用左移一位來代替。

定義一些常量可能會用到 <<運算。能夠方便地用(1 << 16) -1來表示65535.不少算法和數據結構要求數據規模必須是2的冪,此時能夠用<<來定義Max_N等常量。

(6),右移(>>)運算

    和<<類似,a >> b 表示二進制右移b位(去掉末b位),至關於a除以2的b次方(取整)。此外也常常用 >> 1來代替除以2,好比二分查找, 堆的插入操做等。想辦法用>>代替除法運算,可使程序效率大大提升。最大公約數的二進制算法用除以2操做來代替mod運算,效率能夠提升60%。

常見的二進制位的變換操做

功能 示例 位運算
去掉最後一位 (101101--->10110)  x >> 1
在最後加一個0 (101101--->1011010)  x << 1
在最後加一個1 (101101--->1011011) (x << 1) + 1
把最後一位變成1 (101100--->101101) x | 1
把最後一位變成0 (101101--->101100) (x|1)-1
最後一位取反 (101101--->101100) x ^ 1
把右數第k位變成1 (101001--->101101, k = 3) x | (1 << (k -1))
把右數第k位變成0 (101101--->101001,k = 3) x & ~(1 <<(k -1))
右數第K位取反 (101001--->101101,K= 3)  x ^(1 << (k - 1))
取末位三位 (1101101--->101) x & 7 
取末K位 (1101101--->1101,k = 5) x & ((1 << k ) -1)
取右數第 k位 (1101101--->1, k = 4)  x >>(k -1) &1
把末K位變成1 (101001--->101111, k = 4)  x | ((1 << k ) -1)
末K位取反 (101001--->100110, k = 4) x ^ ((1<<k ) - 1)
去掉整數最右邊的1 (100101111--->10010110) x & ( x - 1)
把右邊連續的1變成0 (100101111--->100100000)  x & ( x + 1)
把右起第一個0 變成1 (100101111--->100111111)  x | (x + 1)
把右邊連續的0變成1 (11011000--->11011111)  x | (x - 1)
去掉右起第一個1 的左邊 (100101000--->1000)

x & (x ^(x -1))

取右邊連續的1 (100101111--->1111)

(x ^(x+1)) >> 1

在實際的編程過程當中,每每會用一個整數的不一樣位表示不一樣的數據信息。在訪問該整數時,就須要經過位運算來得到或者改變整數的某幾位數值。好比在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的值。

(2)將第n 位置位或清零。

#define BITN (1 << n)

置位: a |= BITN;

清零: a &= ~BITN;

(3)清除整數a最右邊的1

方法: a & (a -1)

問題: 如何判斷整數x的二進制中含有多少個1?

int func(x)

{

    int countx = 0;

    while(x)

    {

        countx ++;

        x = x & (x -1);

    }

    return countx;

}

(4)將一個整數對齊到n:

    a = (a + n -1) & ~(n -1)

相關文章
相關標籤/搜索