從一個例子說起:
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,打印結果就會是正數了。
對於各種類型的數據轉換問題, 扣到計算機內存存儲的本質, 就萬變不離其宗了。