首先,我們先瞭解一下相關的概念,我們目前的PC機上面,普遍使用的是32位機,一個整型int為4個位元組,一個char為1個位元組,一個位元組為8位,這裡的位的概念就是今天的主角,在嵌入式領域及系統軟體應用非常廣泛。c/c++是很特殊的進階語言,可以直接操作位,甚至還有位域的概念,可以為一個位元組中的位分別定義概念。使用位,有什麼好處呢,我想應該是可以節約記憶體,效能較高,作為標誌位時,意義非常清晰。在win32 API裡面的標誌位通常就是用這個來完成的,一個位元組就可以用於表示32個標誌了。用上|或者&操作,看起來,多麼完美啊。如我們用於判斷路徑是否為檔案夾:
WIN32_FIND_DATA fileinfo;// File Information StructureHANDLEhFile;// File HandlehFile = FindFirstFile(sDir,&fileinfo);// if the file exists and it is a directoryif(fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){// Directory Exists close fileFindClose(hFile);}else{if(!CreateDirectory(sDir,NULL)){AfxMessageBox("本地建立目錄[" + sDir + "]失敗。");return FALSE;}}
…
c的位操作主要包括
| 操作 |
運算子 |
等式及結果 |
點評 |
| 或 |
| |
0000 0000 | 0000 1001 = 0000 1001 |
對每一位而言,逢1則為1 |
| 並且 |
& |
0010 1111 & 1000 0000 = 0000 0000 |
對每位計算,逢0則為0,全1才為1 |
| 取反 |
~ |
~1010 0111 = 01011000 |
這個最好理解,逢1為0,逢0為1 |
| 異或 |
^ |
0000 1001 ^ 1010 1010 = 1010 0111 |
兩個操作位不同則為1,相同則為0 |
| 左移 |
<< |
0101 1000 << 2 = 0110 0000 |
將所有位向左邊移動,並將左邊的移動位拋棄,右邊補0 |
| 右移 |
>> |
01100000 >> 4 = 0000 0110 |
將所有位向右邊移動,右邊移動位拋棄,左邊補0 |
針對這幾個運算子,我們實驗一把
C的位操作#include <stdlib.h>#include <string.h>#include <assert.h>void print_bit(unsigned char b){/*因無法直接存取位值,只能通過&運算來測試某位是等於或者是*//*當然,現在我們這個辦法是比較蠢的,因為變成你不得不得去計算這幾個十六進位的值,等會再想個好點的辦法*/printf("%c",(b & 0x80)?'1':'0');/*1000 0000*/printf("%c",(b & 0x40)?'1':'0');/*0100 0000*/printf("%c",(b & 0x20)?'1':'0');/*0010 0000*/printf("%c",(b & 0x10)?'1':'0');/*0001 0000*/printf("%c",(b & 0x08)?'1':'0');/*0000 1000*/printf("%c",(b & 0x04)?'1':'0');/*0000 0100*/printf("%c",(b & 0x02)?'1':'0');/*0000 0010*/printf("%c",(b & 0x01)?'1':'0');/*0000 0001*/}int main(){/*00000000*/unsigned char b = 0;printf("初始化時:\n");print_bit(b);printf("\n----------------------------------\n\n");/*設定第,4位為00000000 | 00001001 = 00001001*/print_bit(b);b |= 0x9;printf(" | 00001001 = ");print_bit(b);printf("\n----------------------------------\n\n");/*異或00001001 ^ 10101010 = 10100111*/printf("異或^操作:");print_bit(b);printf(" ^ 10101010=");b ^= 0xAA;print_bit(b);printf("\n----------------------------------\n\n");/*取反~10100111 = 01011000*/printf("取反~操作:~");print_bit(b);printf("=");b = ~b;print_bit(b);printf("\n----------------------------------\n\n");/*左移01011000 << 2 = 01100000*/printf("取左移<<2操作:");print_bit(b);b = b<<2;printf(" <<2 =");print_bit(b);printf("\n----------------------------------\n\n");/*右移位*/printf("右移>>4操作:");print_bit(b);b = b>>4;printf(" >>4 =");print_bit(b);printf("\n----------------------------------\n\n");return 0;}
下面,我們再來看看C++的位操作是怎樣的。C++的位操作,當然是STL的bitset了。
以下表摘自CPPPrimer第二版4.12
| 操作 |
功能 |
用法 |
| test(pos) |
pos位是否為1? |
a.test(4) |
| any() |
任意位是否為1? |
a.any() |
| none() |
是否沒有位為1? |
a.none() |
| count() |
值是1的位的個數 |
a.count() |
| size() |
位元素的個數 |
a.size() |
| [pos] |
訪問pos位 |
a[4] |
| flip() |
翻轉所有的位 |
a.flip() |
| set() |
將所有位置設為1 |
a.set() |
| set(pos) |
將pos位置設為1 |
a.set(4) |
| reset() |
將所有位置設為0 |
a.reset() |
| reset(pos) |
將pos位置設為0 |
a.reset(4) |
| |
|
|
針對上面的c程式,我們可以寫出對應的c++版本,這裡也不去寫了,畢竟針對bitset的操作不用像C那樣做麻煩的16進位的計算
總結:
bitset相對於原始C的位操作,相對直觀,代碼清晰,易於理解,不需要程式做機械性的2進位及16進位的換算。對於比較少接觸位操作的
人們而言未嘗不是一種福音,但是對於經常做位操作的嵌入式程式員來講,估計會覺得還是直接用位操作符比較省事。也許這種差別跟Unix的
CLI(命令列模式)及WIN的GUI(視窗介面)一樣,從初步來看滑鼠比較易用,但對於熟練人員而言(比如程式員),往往用命令列更加的高效,且
便於批處理。扯遠了。其實,從辨證的角度是不是可以兩者結合使用呢?像win專註於用戶端,而讓unix專註於伺服器呢?又一次扯遠了!
Technorati 標籤: 位操作,bitset,c,c++,對比