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(){
unsigned a,b;
printf("input a number: ");
scanf("%d",&a);
b=a>>5;
b=b&15;
printf("a=%d b=%d ",a,b);
}
再看一例:
main(){
char a='a',b='b';
int p,c,d;
p=a;
p=(p<<8)|b;
d=p&0xff;
c=(p&0xff00)>>8;
printf("a=%d b=%d c=%d d=%d ",a,b,c,d);
}
浮點數的存儲格式:
浮點數的存儲格式是符號+階碼(定點整數)+尾數(定點小數)
SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
即1位符號位(0爲正,1爲負),8位指數位,23位尾數位
浮點數存儲前先轉化成2的k次方形式,即:
f = A1*2^k + A2*2^(k-1) + ... + Ak +... +An*2^(-m) (Ai = {0, 1}, 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)
{
long lResult = 0; /* 32位值的臨時變量*/
/* 將第一個16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個16位值放入32位值的低16位 */
lResult |= (long)sLowBits;
return lResult;
}
/////////////////////////////////////////////////
[問題的發現]:
我們先看如下測試代碼:
/////////////////////////////////////////////////
int main()
{
short sHighBits1 = 0x7fff;
short sHighBits2 = 0x8f12;
unsigned short usHighBits3 = 0xff12;
short sLowBits1 = 0x7bcd;
long lResult = 0;
printf("[sHighBits1 + sLowBits1] ";
lResult = CatenateBits16(sHighBits1, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(sHighBits2, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(usHighBits3, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
}
/////////////////////////////////////////////////
運行結果爲:
[sHighBits1 + sLowBits1]
lResult = 7fff7bcd
lResult = 8f127bcd
lResult = ff127bcd
嗯,運行很正確嘛……於是我們就放心的在自己的程序中使用起這個函數來了。
可是忽然有一天,我們的一個程序無論如何結果都不對!經過n個小時的檢查和調試,最後終於追蹤到……CatenateBits16() !?它的返回值居然是錯的!!
“鬱悶!”你說,“這個函數怎麼會有問題呢!?”
可是,更鬱悶的還在後頭呢,因爲你把程序中的輸入量作爲參數,在一個簡單的main()裏面單步調試:
/////////////////////////////////////////////////
int main()
{
short sHighBits1 = 0x7FFF;
short sHighBits2 = 0x8F12;
unsigned short usHighBits3 = 0x8F12;
short sLowBits1 = 0x7BCD; //你實際使用的參數
short sLowBits2 = 0x8BCD; //你實際使用的參數
long lResult = 0;
printf("[sHighBits1 + sLowBits1] ";
lResult = CatenateBits16(sHighBits1, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(sHighBits2, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(usHighBits3, sLowBits1);
printf("lResult = %08x ", lResult, lResult);
printf(" [sHighBits1 + sLowBits2] ";
lResult = CatenateBits16(sHighBits1, sLowBits2);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(sHighBits2, sLowBits2);
printf("lResult = %08x ", lResult, lResult);
lResult = CatenateBits16(usHighBits3, sLowBits2);
printf("lResult = %08x ", lResult, lResult);
return 0;
}
/////////////////////////////////////////////////
發現結果竟然是:
[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)
{
long lResult = 0; /* 32位值的臨時變量*/
/* 將第一個16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個16位值放入32位值的低16位 */
lResult |= (long)sLowBits; //注意這一句!!!!
return lResult;
}
/////////////////////////////////////////////////
如果我們在函數中用
printf("sLowBits = %04x ", sLowBits);
打印傳入的sLowBits值,會發現
sLowBits = 0x7bcd 時,打印結果爲
sLowBits = 7bcd
而sLowBits = 0x8bcd時,打印結果爲
sLowBits = ffff8bcd
是的,即使用%04x也打印出8位十六進制。
因此,我們看出來了:
當sLowBits = 0x8bcd時,函數中 "lResult |= (long)sLowBits;" 這一句執行,會先將sLowBits轉換爲
0xffff8bcd
再與lResult做或運算。由於現在lResult的值爲 0xXXXX0000 (其中XXXX是任何值),所以顯然,無論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)
{
long lResult = 0;
/* 將第一個16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個16位值放入32位值的低16位 */
lResult |= (long)sLowBits & 0x0000FFFF;
return lResult;
}
/////////////////////////////////////////////////
注意其中的 "lResult |= (long)sLowBits & 0x0000FFFF;"。事實上,現在即使我們把CatenateBits16()函數的參數(特別是sLowBits)聲明爲short,結果也會是對的。
如果有一天你把一隻兔子扔給一隻老虎,老虎把兔子吃了,第二天把一隻老鼠扔給它,它又吃了,那麼說明第一天你看錯了:它本來就是一隻貓。