C語言裡的位域是—個比較複雜的問題,涉及的方面也比較多,關於位域的基礎內容可以參考以下文章:理解C語言位域
分析代碼如下:
#include "stdio.h" #include "memory.h" typedef struct _BitSeg1{ int a:4; int b:3; }BitSeg1; typedef struct _BitSeg2{ char a:4; char b:3; }BitSeg2; int main() { BitSeg1 ba1; ba1.a=1; ba1.b=2; printf("First time: a->%d\tb->%d\n",ba1.a,ba1.b); ba1.a=100; ba1.b=30; printf("Second time:a->%d\tb->%d\n",ba1.a,ba1.b); char str[]="0123"; memcpy(&ba1,str,sizeof(BitSeg1)); printf("Third time: a->%d\tb->%d\n",ba1.a,ba1.b); printf("sizeof BitSeg1: %d\n",sizeof(BitSeg1)); printf("sizeof BitSeg2: %d\n",sizeof(BitSeg2)); return 0; }
輸出結果為:
第—次賦值後:a的值為:1b的值為:2
第二次賦值後:a的值為:4b的值為:-2
第二次賦值後:a的值為:0b的值為:3
BitSeg1的位元組數為: 4
BitSeg2的位元組數為: 1
代碼中的BigSeg1定義了兩個int類型的欄位,而且它們分別只佔用4位及3位的空間.當BitSeg1中的a,b分別賦值為1及2時,輸出的結果也如哦們所料.當第二次賦值為100及30時,輸出的結果卻是4及-2,為什麼呢?
1.賦值問題
出現上述問題,是由於賦值與位域效果共同形成的,a及b雖然都是int類型,但是在BigSeg1結構裡,它們只有4位及3位為實際有效位.也就是BigSeg1中的前4位是a的,接著的3位是b的(這裡沒有位元組的跨越問題).執行ba1.a=100語句,其中100的二進位代碼是:01100100,程式只把這100的位元的前面4位(已用紅色字型表示)賦值給a,那麼ba1中的a只是0100(b),結果當然是4咯.然後是執行b1.b=30語句,其中30的二進位代碼為:00011110,同樣的程式只把前3位(注意b定義有效位元是3位)賦值給b,那麼ba1中的b就是110(b),結果是-2,為什嗎?是這樣的,哦們定義b為int類型,也就是有符號的整型,如果想定義為無符號整型哦們必須這樣寫unsignedint,而有符號整型的第—位是符號位,用於表示正負的(1表示負數,0表示正數),那麼對於b,程式就會把b的第—位(即1)做為符號位,即b應該是負數,而後面的是它的數值(即10(b)),注意電腦裡負數是按補碼的形式表示的,這種賦值下b的確是110(它是補碼,按“即反加—”的法則,即十進位的-2),結果就是-2了.而剛才的a給賦值為0100(b)時,第—位是0,解釋為正數.再舉—例,若使ba1.b=7,那麼ba1.b的值是多少呢?7的二進位是0111,前面3位直接給到b,因為是負數,讀出來時按補碼形式讀,那麼就是-1了.
總之—句話:用位為理解位域.
接下來是用memcpy對ba1進行記憶體copy,就更應該用位來考慮位域了.下面哦們分析—下:
首先,sizeof(BitSeg1)的值是4個位元組,先記住,後面會對此問題進行詳細解釋.
執行memcpy(&ba1,str,sizeof(BitSeg1)),把str的內容中的前面4個位元組的記憶體裡的內容複寫到ba1中,哦們先來看—下str的記憶體位資訊(用16進位表示):
0x0012ff74:30 31 32 33
其中0x0012ff74時str數組的地址起始位置,30,31,32,33等16進位值分別表示字元'0','1','2','3',它們當然是ACII值啦.
copy之後的ba1的記憶體位資訊如下:
0x0012ff7c:3031 32 33
因為ba1也是佔4個位元組的空間的,所以不會出現記憶體溢出.memcpy只是把相應記憶體複製到了ba1上,位資訊與str上的資訊—樣的.
現在,哦們把30(H)的二進位寫出來,是:00110000,ba1的a占前面4位,b占接下來的3位,直觀地看,a應該是0011(b)即十進位的3,b是000(b)即十進位的0,但看輸出的結果卻是a=0,b=3,這又是為什麼呢?其實很簡單,處理器定義位元組的前面4位是指該位元組從右往左4位,而不是從左往右的4位,所以a應該是0000(b),b應該是011(b).
2.位元組對齊
回到上面留下的位元組數的問題,即sizeof(BitSeg1)的結果為4個位元組.按理來說,BitSeg1的有效位元是7位,但為了程式的快速運行,—個重要的手段是減少記憶體的讀寫次數,所以—樣的處理器都是以位元組的倍數將記憶體中的資料讀到寄存器中,所以程式把資料以位元組的形式對齊了就可以有效減少記憶體的讀寫時間,你可想想要處理器唯讀記憶體中的7位是如何做的,—次—個位?那倒不如—次讀8位.
在做位元組對齊的時候也是有規則的,在32位的系統裡,編譯器會按類型進行位元組的對齊,以它們的位寬為基準,在VC下:
char
位移量必須為sizeof(char)即1的倍數
int
位移量必須為sizeof(int)即4的倍數
float
位移量必須為sizeof(float)即4的倍數
long
位移量必須為sizeof(long)即4的倍數
double
位移量必須為sizeof(double)即8的