3.15.2017

MSB, LSB, 位元運算

原本這一篇想要記下來的C語言中「位元運算」;可是,我印象中好像有寫過類似心得的記錄,剛剛又翻了BLOG中"Linux"和"C & C++"的標籤,發現還真的沒寫過!再翻自己的筆記本,居然是寫在筆記本上面?因為自己有個習慣,工作上的一些心得或是技巧,都會先記在私人的筆記本上面,等到有空時再整理到BLOG。既然我沒有整理過的話,那就順便把更早之前記下的MSB和LSB的觀念也記一下。

MSB (the Most Significant Byte)LSB (the Least Significant Byte)中的"Significant",中文解釋為「有意義的、具有權重的、意味深長的」;而前面再加上個副詞"Most / Least"來修飾後面的Significant,就變成「最有意義、具有權重的位元組」或是「最沒意義、舉無輕重的位元組」。

那麼何謂「最有意義、最沒意義的位元組」。這裡以一組印度阿拉伯數字─12,345為例,中文念成「一萬兩千三百四十五」,把萬位數的'1'刪除,就會變成2,345─兩千三百四十五,相差了10,000,所以'1'就是MSB;而把個位數的'5'刪除,就會變成12,340─一萬兩千三百四十,相差了5,所以'5'就是LSB

要如何知道自己所應用的CPU (Chipset)是屬於MSB還是LSB?可以寫一個類似下面的程式碼來驗證一下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int 
main(void)
{
    unsigned char ucTemp[4];
    int a = 0xDEADBEEF;

    memset(ucTemp, 0x00, sizeof(unsigned char));
    memcpy(ucTemp, &a, sizeof(int));
    printf("%02X %02X %02X %02X \n", ucTemp[0], ucTemp[1], \
        ucTemp[2], ucTemp[3]);

    return 0;
}
如果程序碼的執行結果是「DE AD BE EF」,表MSB;若為「EF BE AD DE」,則是LSB。

咦?不對呀!16進制的0xDEADBEEF,等於10進制的3,735,928,559;「DE」就是MSB,「EF」就是LSB。那為何會有兩種不一樣的結果?因為這又牽扯到Big EndianLittle Endian的問題。

Big Endian就是「記憶體位址中,較"低"的位址,存放MSB」,ucTemp[0]的記憶體位址,必定要比ucTemp[3]來的低;ucTemp[0]存放MSB (DE),ucTemp[3]存放LSB (EF),,這是Big Endian的概念。

Little Endian就是「記憶體位址中,較"高"的位址,存放MSB」,ucTemp[3]的記憶體位址,必定要比ucTemp[0]來的高;ucTemp[3]存放MSB (DE),ucTemp[0]存放LSB (EF),這是Little Endian的觀念。

寫得文謅謅的,好像很難懂?不懂沒關係,觀念通了就行了。由此可見,Big Endian / MSB,比較符合人類的思考習慣和視覺閱讀上的方式。

簡而言之,Big / Little Endian和MSB / LSB的想法就先記到這,爾后的Driver Programming (驅動程式)Socket Programming (網路程式)都還會再用到。接下來,要記錄的是「位元運算」的一些技巧。為了方便講解,先宣告一個值:char C = 0xA5; 16進制的0xA5,等於10進制的165,等於2進制的「1010 0101」。我們把取出來的值,儲存在char cRet = 0x00; 至於應用的部份,請看以下的範例會比較快一些:
#include <stdio.h>

int 
main(void)
{
    char C = 0xA5, cRet = 0x00, X;

    cRet = (C >> X) & 0x01; // 取第X位元的值
    cRet = C & (0x01 << X); // 取第X位元的值

    cRet = C & 0x0F;        // 取最後4位元的值
    cRet = C & 0xF0;        // 取最前4位元的值

    C &= ~(0x01 << X);      // 把C的第X位元設為0
    C &= ~0x0F;             // 把C的最後4個位元設為0
    C &= ~0xF0;             // 把C的最前4個位元設為0
    // 把C的中間4個位元 (2, 3, 4, 5 Bit)均設成0
    C &= ~0x3C;

    C |= (1 << X);          // 把C的第X位元設為1
    C |= 0x0F;              // 把C的最後4個位元設為1
    C |= 0xF0;              // 把C的最前4個位元設為1
    // 把C的中間4個位元 (2, 3, 4, 5 Bit)均設成1
    C |= 0x3C;

    // 把C的中間4個位元 (2, 3, 4, 5 Bit)設成1010
    C &= ~0x3C;             // 先把中間4個位元都設成0
    C |= (0x0A << 2);       /* 把0x0A (0000 1010),往左移2個位
                               元,變成0010 1000,再做OR運算 */

    // 把C的第X位元做Toggle (0變成1, 1變成0)
    C ^= (1 << X);

    return 0;
}

沒有留言:

張貼留言