在計(jì)算機(jī)程序中,數(shù)據(jù)的位是可以操作的小數(shù)據(jù)單位,理論上可以用“位運(yùn)算”來完成所有的運(yùn)算和操作。
一般的位操作是用來控制硬件的,或者做數(shù)據(jù)變換使用,但是,靈活的位操作可以有效地提高程序運(yùn)行的效率。C語言提供了位運(yùn)算的功能, 這使得C語言也能像匯編語言一樣用來編寫系統(tǒng)程序。
位運(yùn)算符C語言提供了六種位運(yùn)算符:
& 按位與
| 按位或
^ 按位異或
~ 取反
<< 左移
>> 右移
1. 按位與運(yùn)算
按位與運(yùn)算符"&"是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相與。只有對(duì)應(yīng)的兩個(gè)二進(jìn)位均為1時(shí),結(jié)果位才為1 ,否則為0。參與運(yùn)算的數(shù)以補(bǔ)碼方式出現(xiàn)。
例如:9&5可寫算式如下: 00001001 (9的二進(jìn)制補(bǔ)碼)&00000101 (5的二進(jìn)制補(bǔ)碼) 00000001 (1的二進(jìn)制補(bǔ)碼)可見9&5=1。
按位與運(yùn)算通常用來對(duì)某些位清0或保留某些位。例如把a(bǔ) 的高八位清 0 , 保留低八位, 可作 a&255 運(yùn)算 ( 255 的二進(jìn)制數(shù)為0000000011111111)。
應(yīng)用:
a. 清零特定位 (mask中特定位置0,其它位為1,s=s&mask)
b. 取某數(shù)中指定位 (mask中特定位置1,其它位為0,s=s&mask)
2. 按位或運(yùn)算
按位或運(yùn)算符“|”是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相或。只要對(duì)應(yīng)的二個(gè)二進(jìn)位有一個(gè)為1時(shí),結(jié)果位就為1。參與運(yùn)算的兩個(gè)數(shù)均以補(bǔ)碼出現(xiàn)。
例如:9|5可寫算式如下:
00001001|00000101
00001101 (十進(jìn)制為13)可見9|5=13
應(yīng)用:
常用來將源操作數(shù)某些位置1,其它位不變。 (mask中特定位置1,其它位為0 s=s|mask)
3. 按位異或運(yùn)算
按位異或運(yùn)算符“^”是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相異或,當(dāng)兩對(duì)應(yīng)的二進(jìn)位相異時(shí),結(jié)果為1。參與運(yùn)算數(shù)仍以補(bǔ)碼出現(xiàn),例如9^5可寫成算式如下:
00001001^00000101 00001100 (十進(jìn)制為12)
應(yīng)用:
a. 使特定位的值取反 (mask中特定位置1,其它位為0 s=s^mask)
b. 不引入第三變量,交換兩個(gè)變量的值 (設(shè) a=a1,b=b1)
目 標(biāo) 操 作 操作后狀態(tài)
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. 求反運(yùn)算
求反運(yùn)算符~為單目運(yùn)算符,具有右結(jié)合性。 其功能是對(duì)參與運(yùn)算的數(shù)的各二進(jìn)位按位求反。例如~9的運(yùn)算為: ~(0000000000001001)結(jié)果為:1111111111110110
5. 左移運(yùn)算
左移運(yùn)算符“<<”是雙目運(yùn)算符。其功能把“<< ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部左移若干位,由“<<”右邊的數(shù)指定移動(dòng)的位數(shù), 高位丟棄,低位補(bǔ)0。 其值相當(dāng)于乘2。例如: a<<4 指把a(bǔ)的各二進(jìn)位向左移動(dòng)4位。如a=00000011(十進(jìn)制3),左移4位后為00110000(十進(jìn)制48)。
6. 右移運(yùn)算
右移運(yùn)算符“>>”是雙目運(yùn)算符。其功能是把“>> ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部右移若干位,“>>”右邊的數(shù)指定移動(dòng)的位數(shù)。其值相當(dāng)于除2。
例如:設(shè) a=15,a>>2 表示把000001111右移為00000011(十進(jìn)制3)。對(duì)于左邊移出的空位,如果是正數(shù)則空位補(bǔ)0,若為負(fù)數(shù),可能補(bǔ)0或補(bǔ)1,這取決于所用的計(jì)算機(jī)系統(tǒng)。移入0的叫邏輯右移,移入1的叫算術(shù)右移,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);
}
浮點(diǎn)數(shù)的存儲(chǔ)格式:
浮點(diǎn)數(shù)的存儲(chǔ)格式是符號(hào)+階碼(定點(diǎn)整數(shù))+尾數(shù)(定點(diǎn)小數(shù))
SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
即1位符號(hào)位(0為正,1為負(fù)),8位指數(shù)位,23位尾數(shù)位
浮點(diǎn)數(shù)存儲(chǔ)前先轉(zhuǎn)化成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就是指數(shù),加127后組成8位指數(shù)位
5.5的指數(shù)位就是2+127 = 129 = 10000001
A2A3.....An就是尾數(shù)位,不足23位后補(bǔ)0
所以5.5 = 01000000101000000000000000000000 = 40A00000
所以,對(duì)浮點(diǎn)數(shù)*2、/2只要對(duì)8位符號(hào)位+、- 即可,但不是左移、右移
關(guān)于unsigned int 和 int 的在位運(yùn)算上的不同,下面有個(gè)CU上的例子描述的很清楚:
[問題]:這個(gè)函數(shù)有什么問題嗎?
/**
* 本函數(shù)將兩個(gè)16比特位的值連結(jié)成為一個(gè)32比特位的值。
* 參數(shù):sHighBits 高16位
* sLowBits 低16位
* 返回:32位值
**/
long CatenateBits16(short sHighBits, short sLowBits)
{
long lResult = 0; /* 32位值的臨時(shí)變量*/
/* 將個(gè)16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個(gè)16位值放入32位值的低16位 */
lResult |= (long)sLowBits;
return lResult;
}
/////////////////////////////////////////////////
[問題的發(fā)現(xiàn)]:
我們先看如下測(cè)試代碼:
/////////////////////////////////////////////////
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);
}
/////////////////////////////////////////////////
運(yùn)行結(jié)果為:
[sHighBits1 + sLowBits1]
lResult = 7fff7bcd
lResult = 8f127bcd
lResult = ff127bcd
嗯,運(yùn)行很正確嘛……于是我們就放心的在自己的程序中使用起這個(gè)函數(shù)來了。
可是忽然有,我們的一個(gè)程序無論如何結(jié)果都不對(duì)!經(jīng)過n個(gè)小時(shí)的檢查和調(diào)試,終于追蹤到……CatenateBits16() !?它的返回值居然是錯(cuò)的!!
“郁悶!”你說,“這個(gè)函數(shù)怎么會(huì)有問題呢!?”
可是,更郁悶的還在后頭呢,因?yàn)槟惆殉绦蛑械妮斎肓孔鳛閰?shù),在一個(gè)簡(jiǎn)單的main()里面單步調(diào)試:
/////////////////////////////////////////////////
int main()
{
short sHighBits1 = 0x7FFF;
short sHighBits2 = 0x8F12;
unsigned short usHighBits3 = 0x8F12;
short sLowBits1 = 0x7BCD; //你實(shí)際使用的參數(shù)
short sLowBits2 = 0x8BCD; //你實(shí)際使用的參數(shù)
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;
}
/////////////////////////////////////////////////
發(fā)現(xiàn)結(jié)果竟然是:
[sHighBits1 + sLowBits1]
lResult = 7fff7bcd
lResult = 8f127bcd
lResult = 8f127bcd
[sHighBits1 + sLowBits2]
lResult = ffff8bcd //oops!
lResult = ffff8bcd //oops!
lResult = ffff8bcd //oops!
前一次還好好的,后一次就ffff了?X檔案?
[X檔案的真相]:
注意那兩個(gè)我們用來當(dāng)作低16位值的sLowBits1和sLowBits2。
已知:
使用 sLowBits1 = 0x7bcd 時(shí),函數(shù)返回正確的值;
使用 sLowBits2 = 0x8bcd 時(shí),函數(shù)中發(fā)生X檔案。
那么,sLowBits1與sLowBits2有什么區(qū)別?
注意了,sLowBits1和sLowBits2都是short型(而不是unsigned short),所以在這里,sLowBits1代表一個(gè)正數(shù)值,而sLowBits2卻代表了一個(gè)負(fù)數(shù)值(因?yàn)?即是二進(jìn)制1000,sLowBits2位是1)。
再看CatenateBits16()函數(shù):
/////////////////////////////////////////////////
long CatenateBits16(short sHighBits, short sLowBits)
{
long lResult = 0; /* 32位值的臨時(shí)變量*/
/* 將個(gè)16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個(gè)16位值放入32位值的低16位 */
lResult |= (long)sLowBits; //注意這一句!!!!
return lResult;
}
/////////////////////////////////////////////////
如果我們?cè)诤瘮?shù)中用
printf("sLowBits = %04x ", sLowBits);
打印傳入的sLowBits值,會(huì)發(fā)現(xiàn)
sLowBits = 0x7bcd 時(shí),打印結(jié)果為
sLowBits = 7bcd
而sLowBits = 0x8bcd時(shí),打印結(jié)果為
sLowBits = ffff8bcd
是的,即使用%04x也打印出8位十六進(jìn)制。
因此,我們看出來了:
當(dāng)sLowBits = 0x8bcd時(shí),函數(shù)中 "lResult |= (long)sLowBits;" 這一句執(zhí)行,會(huì)先將sLowBits轉(zhuǎn)換為
0xffff8bcd
再與lResult做或運(yùn)算。由于現(xiàn)在lResult的值為 0xXXXX0000 (其中XXXX是任何值),所以顯然,無論sHighBits是什么值,結(jié)果都會(huì)是
0xffff8bcd
而當(dāng)sLowBits = 0x7bcd時(shí),函數(shù)中 "lResult |= (long)sLowBits;" 這一句執(zhí)行,會(huì)先將sLowBits轉(zhuǎn)換為
0x00007bcd
再與lResult做或運(yùn)算。這樣做或運(yùn)算出來的結(jié)果當(dāng)然就是對(duì)的。
也就是說,CatenateBits16()在sLowBits的位為0的時(shí)候表現(xiàn)正常,而在位為1的時(shí)候出現(xiàn)偏差。
[教訓(xùn):在某些情況下作位運(yùn)算和位處理的時(shí)候,考慮使用無符號(hào)數(shù)值——因?yàn)檫@個(gè)時(shí)候往往不需要處理符號(hào)。即使你需要的有符號(hào)的數(shù)值,那么也應(yīng)該考慮自行在調(diào)用CatenateBits16()前后做轉(zhuǎn)換——畢竟在位處理中,有符號(hào)數(shù)值相當(dāng)詭異!
下面這個(gè)CatenateBits16()版本應(yīng)該會(huì)好一些:
/////////////////////////////////////////////////
unsigned long CatenateBits16(unsigned short sHighBits, unsigned short sLowBits)
{
long lResult = 0;
/* 將個(gè)16位值放入32位值的高16位 */
lResult = sHighBits;
lResult <<= 16;
/* 清除32位值的低16位 */
lResult &= 0xFFFF0000;
/* 將第二個(gè)16位值放入32位值的低16位 */
lResult |= (long)sLowBits & 0x0000FFFF;
return lResult;
}
/////////////////////////////////////////////////
注意其中的 "lResult |= (long)sLowBits & 0x0000FFFF;"。事實(shí)上,現(xiàn)在即使我們把CatenateBits16()函數(shù)的參數(shù)(特別是sLowBits)聲明為short,結(jié)果也會(huì)是對(duì)的。
如果有你把一只兔子扔給一只老虎,老虎把兔子吃了,第二天把一只老鼠扔給它,它又吃了,那么說明第你看錯(cuò)了:它本來就是一只貓。
C語言中的位運(yùn)算
更新時(shí)間: 2007-07-16 15:36:07來源: 粵嵌教育瀏覽量:923
粵嵌動(dòng)態(tài)
推薦閱讀
- ·在資源受限(如內(nèi)存不足)的設(shè)備中如何實(shí)現(xiàn)功能擴(kuò)展?
- ·廣東賽鉑安智能科技有限公司專場(chǎng)招聘會(huì)
- ·設(shè)計(jì)一個(gè)低功耗傳感器采集系統(tǒng),需考慮哪些因素?
- ·校企聯(lián)動(dòng)促實(shí)踐!湖北工程學(xué)院 300 + 學(xué)子開啟企業(yè)級(jí)實(shí)訓(xùn)之旅!!
- ·如何保障固件升級(jí)的安全性:防斷電與校驗(yàn)機(jī)制設(shè)計(jì)
- ·2025年智能家房系統(tǒng)設(shè)計(jì)與工程安裝師資培訓(xùn)班(省級(jí)培訓(xùn)))順利結(jié)業(yè)
- ·嵌入式開發(fā):ROTS與Linux的技術(shù)本質(zhì)差異
- ·STM32單片機(jī)引腳分類詳解與開發(fā)實(shí)踐
- ·攜手共進(jìn),共筑智能海洋裝備新未來 —— 粵嵌科技與智能海洋裝備現(xiàn)代產(chǎn)業(yè)學(xué)院簽約儀式年
- ·校企合作新突破 | 粵嵌科技與三亞學(xué)院共探產(chǎn)教融合新路徑