1. gzyueqian
      18078865874
      首頁 > 新聞中心 > > 正文

      C語言中的位運(yùn)算

      更新時(shí)間: 2007-07-16 15:36:07來源: 粵嵌教育瀏覽量:923

        在計(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ò)了:它本來就是一只貓。

      免費(fèi)預(yù)約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 日本一区中文字幕最新在线 | 日韩乱偷区自中文 | 在线播放国产日韩 | 亚洲无线观看国产超清 | 亚洲欧美一二区日韩精品在线 | 亚洲一区二区三区片 |