C語言中的按位移動及其簡單引用


C語言中的按位移動及其簡單應用

在C語言中按位左移用”<<”表示,按位右移用”>>”表示。

按位左移和按位右移運算經常被用來替換乘二除二運算,但是要注意,這兩者之間並不完全等價。下面就分析一下:

首先明確,按位移動分為邏輯移動和算術移動,具體就是:邏輯左移、算術左移;邏輯右移、算術右移。

算術左移、算術右移、邏輯左移、邏輯右移的操作如下圖:

 

 

可以看到,邏輯左移=算術左移:都是左移然后右邊補零

算術右移和邏輯右移有所區別:邏輯右移是右移並且左邊補0,而算術左移是右移並且左邊補1(也就是補符號位)

 

C語言中:左移采用的邏輯左移(和算術左移的效果相同)和算術右移。由於算術左移和邏輯左移的效果相同,所以我們可以認為,C語言中采用的是算術移動對於有符號數而言)。

 

那么算術左移、算術右移和乘二、除二運算結果是否一致呢?

先來看左移:

先給出結論:無論是正數還是負數,只要結果不溢出(也就是不超過取值范圍)結果就是乘2。

比如:

 

 

輸出為:

 

 

對於結果溢出的(也就是超出取值范圍的),比如:

 

 

輸出為:

 

 

發現沒,兩個結果都不對,因為char是8位的,表示范圍為-128~127,結果產生溢出了。那為什么會產生這樣的結果呢?

a = 65. 二進制位:0100 0001

邏輯左移一位結果為: 1000 0010 ,我們發現,這個數的符號位變成了1,也就是負數了,就是-126。細心的人也會發現,理想結果130、實際結果 -126、27,這三者之間貌似有某種聯系,的確有聯系,這里就不深究了。

再來看看右移:

先給結論:首先,右移,並不會造成結果溢出的情況。對於正數,放心大膽地去右移吧,結果都相當於除二;但是對於負數,結果不一定相等,最好不要用右移代替除二。

比如:對於正數

 

 

輸出為:

 

 

結果都是正確的。

 

但是對於負數,比如:

 

 

輸出為:

 

 

可以看到對於偶數的結果是正確的。但是對於奇數,結果的絕對值比除二的結果的絕對值大1

 

那么能否實現邏輯右移呢?

上面提到了C語言中采用的是算術右移,那么有沒有辦法實現邏輯右移呢?可以的。

我們需要注意一下無符號數,無符號數並沒有符號位,所以對無符號數進行的位移都是邏輯位移。對於一個有符號數,如果我們想對他進行邏輯右移,那么我們可以先將該數轉換成對應對的無符號數,然后再進行右移操作

比如對於char a = -6。

二進制位 1111 1010

如果是算術右移,結果應該為: 1111 1101 也就是-3

如果是邏輯右移,結果應該為: 0111 110 也就是125

下面我們驗證一下:

 

 

輸出為:

 

 

正確

 

位移運算的簡單應用

有這樣一個題,要求是將一個int型的數據轉換成二進制和16進制的字符串輸出。

比如對於整型量456789

我們要輸出:(空格不用了)

00000000  00000110  11111000  01010101

0x0006F855

 

對於求二進制,該數在內存中就是二進制的表示,我們只需要把每一位變成字符然后存到字符數組中即可,方法就是按位與和位移相結合,比如我們想得到第一個位(最高位),那么我們可以先按位與上10000000 00000000 00000000 00000000 然后將結果按位右移31位(需要注意,應該是采用邏輯右移,而不是算術右移,也就是前面要補0,而不是補1,方法是上面提到的,先將該數轉換成無符號數),就得到了第一位的值,之后轉換成字符即可。

 

對於求16進制,我們則需要以四個二進制位為一個單位,方法類似,思路就是想辦法把當前要轉換的四個二進制位移動到最低四位,因此我們可以用一個char來保存8位二進制,然后想辦法把高四位置為0,把待轉換的四位放到第四位。

比如說,我們現在想轉換標紅的部分

00000000  00000110  11111000  01010101

應該是轉換成F,那么怎么轉換呢?

首先我們先將該數左移16位,也就是4*4位,改數就變成了

11111000  01010101 00000000 00000000

然后我們在右移28位,也就是32-4位(特別注意,這里應該是邏輯右移,而不是算術右移,也就是要保證前面補0,而不是補1,方法就是上面提到的,先將該數轉換成無符號數,然后再右移

結果為:

00000000 00000000 00000000 00001111

最后在用char類型將數據的低8位截斷,並轉換成字符F即可。

 

源代碼如下:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

 

char *get_bin_string(int num)

{

    unsigned int u_num = (unsigned int)num;

    char *buffer = (char *)malloc(33);

    if(buffer == NULL)

         return NULL;

    buffer[32] = '\0';

    int i = 0;

    for(;i < 32;i++)

    {

         unsigned int temp = u_num &(1<<(31 - i));

         temp = temp>>(31-i);

         buffer[i] = temp == 0 ? '0':'1';

    }

    return buffer;

}

char *get_hex_string(int num)

{

    unsigned int u_num = (unsigned int)num;

    char *buffer = (char *)malloc(11);

    if(buffer == NULL)

         return NULL;

    //填寫固定的部分

    buffer[0] = '0';

    buffer[1] = 'x';

    buffer[10] = '\0';

    char *temp = buffer + 2;

    int i = 0;

    for(;i < 8;i++)

    {

         unsigned int af = u_num<<(4 * i);

         af = af >> 28;

         temp[i] = (char)(u_num<<(4 * i)>>28);

         temp[i] = temp[i] < 10 ? temp[i] + 48 :temp[i] + 55;

    }

    return buffer;

}

int main()

{

    int num = 456789;

    char *bin = get_bin_string(num);

    char *hex = get_hex_string(num);

    printf("二進制為:%s\n",bin);

    printf("十六進制為:%s\n",hex);

    free(hex);

    free(bin);

    return 0;

 

}

 

 

最后再強調一下,右移的時候一定要考慮清楚,我們是想要前面補0還是補1,也就是采用邏輯右移還是算術右移,如果是采用邏輯右移,要先將該數轉換成無符號數。

 

就到這里了。

最后附上word文件和源代碼文件

鏈接:http://pan.baidu.com/s/1nuYBXjj 密碼:5huf

 

如果你覺得對你有用,請贊一個吧~~~

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM