linux下程式設計之 位運算

2021-04-15 10:19:43 字數 4918 閱讀 5132

在電腦程式中,資料的位是可以操作的最小資料單位,理論上可以用「位運算」來完成所有的運算和操作。一般的位操作是用來控制硬體的,或者做資料變換使用,但是,靈活的位操作可以有效地提高程式執行的效率。c語言提供了位運算的功能, 這使得c語言也能像組合語言一樣用來編寫系統程式。

位運算子c語言提供了六種位運算子:

&   按位與

|   按位或

^  按位異或

~  取反

<< 左移

>> 右移

1. 按位與運算 按位與運算子"&"是雙目運算子。其功能是參與運算的兩數各對應的二進位相與。只有對應的兩個二進位均為1時,結果位才為1 ,否則為0。參與運算的數以補碼方式出現。

例如:9&5可寫算式如下: 00001001 (9的二進位制補碼)&00000101 (5的二進位制補碼) 00000001 (1的二進位制補碼)可見9&5=1。

按位與運算通常用來對某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 運算 ( 255 的二進位制數為0000000011111111)。

應用:      a. 清零特定位 (mask中特定位置0,其它位為1,s=s&mask)

b. 取某數中指定位 (mask中特定位置1,其它位為0,s=s&mask)

2. 按位或運算 按位或運算子「|」是雙目運算子。其功能是參與運算的兩數各對應的二進位相或。只要對應的二個二進位有乙個為1時,結果位就為1。參與運算的兩個數均以補碼出現。

例如:9|5可寫算式如下:

00001001|00000101

00001101 (十進位制為13)可見9|5=13

應用:常用來將源運算元某些位置1,其它位不變。 (mask中特定位置1,其它位為0 s=s|mask)   

3. 按位異或運算 按位異或運算子「^」是雙目運算子。其功能是參與運算的兩數各對應的二進位相異或,當兩對應的二進位相異時,結果為1。參與運算數仍以補碼出現,例如9^5可寫成算式如下:

00001001^00000101 00001100 (十進位制為12)

應用:a. 使特定位的值取反 (mask中特定位置1,其它位為0 s=s^mask)

b. 不引入第三變數,交換兩個變數的值  (設 a=a1,b=b1)

目 標           操 作              操作後狀態

a=a1^b1         a=a^b              a=a1^b1,b=b1

b=a1^b1^b1      b=a^b              a=a1^b1,b=a1

a=b1^a1^a1      a=a^b              a=b1,b=a1

4. 求反運算 求反運算子~為單目運算子,具有右結合性。 其功能是對參與運算的數的各二進位按位求反。例如~9的運算為: ~(0000000000001001)結果為:1111111111110110

5. 左移運算 左移運算子「<<」是雙目運算子。其功能把「<< 」左邊的運算數的各二進位全部左移若干位,由「<<」右邊的數指定移動的位數, 高位丟棄,低位補0。  其值相當於乘2。例如: a<<4 指把a的各二進位向左移動4位。如a=00000011(十進位制3),左移4位後為00110000(十進位制48)。

6. 右移運算 右移運算子「>>」是雙目運算子。其功能是把「>> 」左邊的運算數的各二進位全部右移若干位,「>>」右邊的數指定移動的位數。其值相當於除2。

例如:設 a=15,a>>2 表示把000001111右移為00000011(十進位制3)。對於左邊移出的空位,如果是正數則空位補0,若為負數,可能補0或補1,這取決於所用的計算機系統。移入0的叫邏輯右移,移入1的叫算術右移,turbo c採用邏輯右移。

main()

再看一例:

main() 

浮點數的儲存格式:

浮點數的儲存格式是符號+階碼(定點整數)+尾數(定點小數)

seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm

即1位符號位(0為正,1為負),8位指數字,23位尾數字

浮點數儲存前先轉化成2的k次方形式,即:

f = a1*2^k + a2*2^(k-1) + ... + ak +... +an*2^(-m) (ai = , a1 = 1)

如5.5=2^2 + 2^0 + 2^(-1)

其中的k就是指數,加127後組成8位指數字

5.5的指數字就是2+127 = 129 = 10000001

a2a3.....an就是尾數字,不足23位後補0

所以5.5 = 01000000101000000000000000000000 = 40a00000

所以,對浮點數*2、/2只要對8位符號位+、- 即可,但不是左移、右移 

關於unsigned int 和 int 的在位運算上的不同,下面有個cu上的例子描述的很清楚:

[問題]:這個函式有什麼問題嗎?

//**

* 本函式將兩個16位元位的值鏈結成為乙個32位元位的值。

* 引數:shighbits 高16位

*       slowbits  低16位

* 返回:32位值

**/long catenatebits16(short shighbits, short slowbits)

/[問題的發現]:

我們先看如下測試**:

/int main()

/執行結果為:

[shighbits1 + slowbits1]

lresult = 7fff7bcd

lresult = 8f127bcd

lresult = ff127bcd

嗯,執行很正確嘛……於是我們就放心的在自己的程式中使用起這個函式來了。

可是忽然有一天,我們的乙個程式無論如何結果都不對!經過n個小時的檢查和除錯,最後終於追蹤到……catenatebits16() !?它的返回值居然是錯的!!

「鬱悶!」你說,「這個函式怎麼會有問題呢!?」

可是,更鬱悶的還在後頭呢,因為你把程式中的輸入量作為引數,在乙個簡單的main()裡面單步除錯:

/int main()

/發現結果竟然是:

[shighbits1 + slowbits1]

lresult = 7fff7bcd

lresult = 8f127bcd

lresult = 8f127bcd

[shighbits1 + slowbits2]

lresult = ffff8bcd          //oops!

lresult = ffff8bcd          //oops!

lresult = ffff8bcd          //oops!

前一次還好好的,後一次就ffff了?x檔案?

[x檔案的真相]:

注意那兩個我們用來當作低16位值的slowbits1和slowbits2。

已知:使用 slowbits1 = 0x7bcd 時,函式返回正確的值;

使用 slowbits2 = 0x8bcd 時,函式中發生x檔案。

那麼,slowbits1與slowbits2有什麼區別?

注意了,slowbits1和slowbits2都是short型(而不是unsigned short),所以在這裡,slowbits1代表乙個正數值,而slowbits2卻代表了乙個負數值(因為8即是二進位制1000,slowbits2最高位是1)。

再看catenatebits16()函式:

/long catenatebits16(short shighbits, short slowbits)

/如果我們在函式中用

printf("slowbits = %04x ", slowbits);

列印傳入的slowbits值,會發現

slowbits = 0x7bcd 時,列印結果為

slowbits = 7bcd

而slowbits = 0x8bcd時,列印結果為

slowbits = ffff8bcd

是的,即使用%04x也列印出8位十六進製制。

因此,我們看出來了:

當slowbits = 0x8bcd時,函式中 "lresult |= (long)slowbits;" 這一句執行,會先將slowbits轉換為

0xffff8bcd

再與lresult做或運算。由於現在lresult的值為 0***xx0000 (其中***x是任何值),所以顯然,無論shighbits是什麼值,最後結果都會是

0xffff8bcd

而當slowbits = 0x7bcd時,函式中 "lresult |= (long)slowbits;" 這一句執行,會先將slowbits轉換為

0x00007bcd

再與lresult做或運算。這樣做或運算出來的結果當然就是對的。

也就是說,catenatebits16()在slowbits的最高位為0的時候表現正常,而在最高位為1的時候出現偏差。

[教訓:在某些情況下作位運算和位處理的時候,考慮使用無符號數值——因為這個時候往往不需要處理符號。即使你需要的有符號的數值,那麼也應該考慮自行在呼叫catenatebits16()前後做轉換——畢竟在位處理中,有符號數值相當詭異!]

下面這個catenatebits16()版本應該會好一些:

/unsigned long catenatebits16(unsigned short shighbits, unsigned short slowbits)

/注意其中的 "lresult |= (long)slowbits & 0x0000ffff;"。事實上,現在即使我們把catenatebits16()函式的引數(特別是slowbits)宣告為short,結果也會是對的。

如果有一天你把乙隻兔子扔給乙隻老虎,老虎把兔子吃了,第二天把乙隻老鼠扔給它,它又吃了,那麼說明第一天你看錯了:它本來就是乙隻貓。

java程式設計之 按位與運算,等運算規則

參加運算的兩個資料,按二進位制位進行 與 運算。運算規則 0 0 0 0 1 0 1 0 0 1 1 1 即 兩位同時為 1 結果才為 1 否則為0 例如 3 5 即 0000 0011 0000 0101 0000 0001 因此,3 5的值得1。另,負數按補碼形式參加按位與運算。與運算 的特殊用...

Linux下socket程式設計之UDP

如下 udp client.c include include include include include include include static void usage char proc int main int argc,char argv int sock socket af ine...

Linux網路程式設計之擴充套件 下

一 單播 前面介紹的資料報傳送方式只有乙個接受方,稱為單播。二 廣播 同時發給區域網中的所有主機,稱為廣播。只有使用者資料報 使用udp協議 套接字才能廣播。三 廣播位址 1 以192.168.1.0 255.255.255.0 網段為例,最大的主機位址192.168.1.255代表該網段的廣播位址...