1. 位域
我們來看一個表示日期的結構體:
typedef struct{ unsigned int year; unsigned int month; unsigned int day;}MyDate;
但是我們可以發現,其實year最大也不會超過四位元,month也就是12,而day最大也就是31。但是我們在上述的結構體中,卻為其分配了4*3=12位元組的記憶體,是不是很浪費呢?
C語言為瞭解決這個問題,提出了一個概念,叫位域。看段代碼:
typedef struct{ unsigned int year:7; unsigned int month:4; unsigned int day:5;}MyDate;
這個的意思是,我為結構體中的每一個欄位分配指定的位元,比如,我為year分配了7位。這樣就有效地節省了記憶體。
網上有著有限的幾篇關於位域的文章,我覺得在CSDN的一個文章中,對於關於位域分配記憶體的情況說的最為詳細,原封不動地拿下來了:
C99規定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對此作了擴充,
允許其它類型類型的存在。
使用位域的主要目的是壓縮儲存,其大致規則為:
1) 如果相鄰位域欄位的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字
段將緊鄰前一個欄位儲存,直到不能容納為止;
2) 如果相鄰位域欄位的類型相同,但其位寬之和大於類型的sizeof大小,則後面的字
段將從新的儲存單元開始,其位移量為其類型大小的整數倍;
3) 如果相鄰的位域欄位的類型不同,則各編譯器的具體實現有差異,VC6採取不壓縮方
式,Dev-C++採取壓縮方式;
4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮;
5) 整個結構體的總大小為最寬基本類型成員大小的整數倍。
2. 再談聯合體
在之前,我們談到過使用聯合體來節約記憶體空間,但是聯合體也經常被用於一個其他的目的:從兩個或更多的角度去看待記憶體塊。
例如:
union int_Date{ int i; MyDate dt;};
在上面的程式中,我們就可以用兩種視角(整數視角和日期視角)來看待一個資料。
我們知道,在X86的機器中,我們常常會用到4個16位寄存器,這四個16位寄存器又可以分成8個8位的寄存器。例如,當我們改變ax的時候,其實al和ah也同時發生了改變,那麼我們可以把這個關係用聯合體來表示。
typedef union { struct { short ax,bx,cx,dx; }word; struct { char al,ah,bl,bh,cl,ch,dl,dh; }byte;}Regs;int main (void){ Regs regs; regs.byte.ah=0x12; regs.byte.al=0x34; printf("%x",regs.word.ax); return 0;}
3. volatile限定符
volatile告訴編譯器,這段記憶體空間所儲存的值是易變的。volatile通常用於指向易變記憶體空間的指標。有個例子我覺得非常恰當。
我們假設*p指向的記憶體空間用於存放使用者通過鍵盤所輸入的字元,然後我們讀取到這個字元,再將其放入到一個數組中。
但是一些優秀的編譯器會發現,在這個過程中,p和*p都未被程式顯式地改變,這樣編譯器就會對其作出最佳化,使*p只被取一次,這樣就讀入了數組中一些重複的資料,這明顯不是我們想要的。
但是當我們在*p前加上volatile限定符,其實就是在告訴編譯器,不要對該段程式進行最佳化,因為這段程式是易變的,每次的讀取都要從記憶體中去重新取得。
在C#中也有volatile關鍵字,目的也是一樣,volatile關鍵字代表某個變數可能會被多個並發的線程進行修改,所以通知編譯器不要對其進行最佳化,這樣就能保證每次取出來的值不是被緩衝的,而是最新的值。