標籤:
最近在重新學習C語言,使用的書為Brian W.Kernignan 和 Dennis M.Ritchie的《C程式設計語言》 。今晚讀到了位操作,並寫了一些簡單的測試程式。
C語言提供了6個位操作運算子。這些運算子只能作用於整型運算元,即只能作用於帶著有符號或無符號的char、short、int與long類型。
這六種位操作運算子為:
& 按位與| 按位或^ 按位抑或<< 左移>> 右移~ 按位求反
位操作常用於一些開關性質的操作。例如:
x = x | SET_ON
就可以用來表示置某一位為1,假如置第3位(從左邊數,第一位為0)為1,則SET_ON = 0100
若要置某一位為0,則
x = x & SET_OFF
若同樣將第三位置0,則SET_OFF = 1011
關於按位求反的一個比較特殊的用法是:
x = x & ~077
將x的最後6位置0。運算式~077與機器字長無關,他比形式0177700的運算式要好,因為後者假定x是16位的數值。這種可移植的形式並沒有增加額外開銷,因為~077是常量運算式,可以在編譯時間求值。
在對unsigned類型的無符號值進行右移位時,左邊空出的部分將用0填補;當對signed類型的帶符號值進行右移時,某些機器將對左邊空出的部分用符號為填補(即“算術運算”),而另一些機器則對左邊空出的部分用0填補(即“邏輯移位”)。
下面介紹幾個使用位操作的執行個體:
1、編寫函數getbits(x,p,n),它返回x中從右邊數第p位開始向右數n位的欄位。這裡假定最右邊的一位是第0位,n與p都是合理的正值。
此題為該書的執行個體程式,不再過多介紹,代碼如下:
unsigned getbits(unsigned x,int p,int n){ return (x >> (p + 1 - n)) & ~(~0 << n);}
2、編寫一個函數setbits(x,p,n,y),該函數返回對x執行下列操作後的結果值:將x中從p位開始的n個(二進位)位設定為y中最右邊n位的值,x的其餘各位保持不變
如所示,首先將x 與 ~(~(~0 << n) << (p + 1 - n))相與,清零x的左邊第p位向右n位,得到結果1。
其次,將y與~(~0 << n) 相與以清零y除0到n-1的位,在上述結果左移p + 1 - n 位,即將y的低n位左移,得到結果2
最後將結果1與結果2或,則得到結果,代碼如下:
unsigned setbits(unsigned x, int p, int n, unsigned y){ return ~(~(~0 << n) << (p + 1 - n)) & x | ((~(~0 << n) & y) << (p + 1 - n));}
3、編寫一個函數invert(x,p,n),該函數返回對x執行下列操作後的結果值:將x中從第p位開始的n個(二進位)位求反,x的其餘位保持不變
此題就不再畫圖了,直接說明思路即可
若使用按位求反,則就將x的每一位求反,不符合要求。值得注意的是異或^操作,0與1異或的結果是1,1與1異或的結果是0,可見與1異或的 結果便是對該位求反。另外0與0異或的結果是0,1與0異或的結果還是1,,可見與0異或的結果是保持自己不變。利用此特性可以解決該題目。即讓x與類似 於000011110000(可表達為~(~0 << n) << (p + 1 - n)的值異或。代碼如下:
unsigned invert(unsigned x, int p, int n){ return x ^ (~(~0 << n) << (p + 1 - n)); }
4、編寫一個函數rightrot(x,n),該函數返回將x迴圈右移(即從最右端移出的位將從最左端移入)n(二進位)位後所得到的值
基本的左移與右移函數,與移位方向相反的一端一般補充0或符號位,無法達到迴圈移位的目的。為了達到迴圈移位,可以將x的最右邊n位首先左移至最左邊,然後將x右移n位,將兩結果進行或操作即可。代碼如下:
unsigned rightrot(unsigned x, int n){ unsigned l = sizeof(x) * 8; n %= l; return (~(~0 << n) & x) << (l - n) | x >> n;}
5、最後介紹一種求一個整數x的二進位中1的個數的方法。
最直接暴力的方法就是直接將x的二進位表示計算出來,然後數一數1的個數。但還有一個更“聰明”的方法。
要理解更“聰明”的想法,首先需清楚 x & (x - 1)的結果所表達的含義。此操作會將x的二進位中最右邊的一個1變成0。至於為什麼,讀者可以自己思考。
這樣就很容易的寫出代碼了:
unsigned bitcount(unsigned x){ int i = 0; for(; x != 0;x &= (x - 1)) ++i; return i;}
上述的5個題目中涵蓋了C語言中六種位操作的基本使用方法。學習使用位操作不僅僅是學習位操作,我更覺得通過此種學習方法可以鍛煉自己的邏輯思維,加強自己對邏輯的敏感度。
PS:完成上述的幾個函數時,幾次都掉進了坑裡。這個坑就是運算子的優先順序問題。位元運算符的優先順序是低於加(+)減(-)操作符的。故 x << n + 1指的是x左移n+1位,而不是x左移n位再加1。剛開始嘗試編寫上述幾個函數時,使用了加法,後經思考將加法替換成了或操作。
C語言位操作