C語言位域解析&符號位擴展規則

從一個例子說起:

int main(void){  
    union{
        int i;
        struct{
            char a : 1;
            char b : 1;
            char c : 2;
        }bits;
    }num;

    printf("Input an integer for i(0~15): ");
    scanf("%d", &num.i);
    printf("i = %d, cba = %d %d %d\n", num.i, num.bits.c, num.bits.b, num.bits.a); 
    return 0;
}

輸入i值爲11,則輸出爲i = 11, cba = -2 -1 -1。   爲什麼?

1,位域的定義

    在結構體的定義中,指定元素所佔用的bit數, 並指定類型。 按照結構體的成員調用方式進行調用。

2,位域的內存對應規則

    一個字節按照從高位到低位 bit7 ~ bit0,對於位域的定義,是從低位bit0 開始算起的(注意不是從高位開始對應)。也就是說,上面例子中的位域,在一個字節中對應的存儲是 0000 ccba, a在最低位,然後是b,和佔兩個bit的c。 c成員中按照bit3高位、bit2低位存儲。

3,大小端問題

    對於小端來說,低字節存放在低地址中,int的存儲從0x00地址到0x03地址,依次是 00001011 00000000 00000000 00000000。

    聯合體從頭開始,是對內存中數據的截斷和強轉, 根據剛纔位域的存儲結構,cc的截斷是10, b和a的截斷都是1。

4,爲什麼打印出來是負數? ---》補碼的規則

    在計算機的內存中,所有的數據存儲都是按照補碼存儲的。 對於有符號數來說,正數的補碼是正數自身,負數的補碼是反碼+1。這都沒問題。  

    問題的核心還是符號位。計算機裏從低精度數向高精度數轉換時,比如從char到short, 又比如這裏從10兩個bit填充爲一個char的8個bit, 肯定會在前面擴展一些bit位,從而達到高精度數的長度。那麼擴展時,是補0還是補1呢?這裏有個原則就是,有符號數擴展符號位,無符號數擴展0。對應到這裏也就是1。注意,這裏說的是有符號數和無符號數,對於有符號的正數,因爲符號位是0,所以也是補零。然而我們在位域的定義中,定義了abc都是有符號的char型。所以在向8位擴展時,因爲第一位都是1,所以往前都擴展1,a和b在內存中爲11111111, c爲11111110,都是補碼。按照%d打印出來以後, 就是-1 和 -2。 如果這裏定義成 unsigned char,按照定義前面補0,打印結果就會是正數了。

 

   對於各種類型的數據轉換問題, 扣到計算機內存存儲的本質, 就萬變不離其宗了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章