struct 是個神奇的關鍵字,它將一些相關聯的數據打包成一個整體,方便使用。
在網絡協議、通信控制、嵌入式系統、驅動開發等地方,我們經常要傳送的不是簡單的字節流(char 型數組),而是多種數據組合起來的一個整體,其表現形式是一個結構體。
經驗不足的開發人員往往將所有需要傳送的內容依順序保存在char 型數組中,通過指針偏移的方法傳送網絡報文等信息。這樣做編程復雜,易出錯,而且一旦控制方式及通信協議有所變化,程序就要進行非常細致的修改,非常容易出錯。這個時候只需要一個結構體就能搞定。平時我們要求函數的參數盡量不多于4 個,如果函數的參數多于4 個使用起來非常容易出錯(包括每個參數的意義和順序都容易弄錯),效率也會降低(與具體CPU 有關,ARM芯片對于超過4 個參數的處理就有講究,具體請參考相關資料)。這個時候,可以用結構體壓縮參數個數。
struct student
{
}stu;
sizeof(stu)的值是多少呢?在Visual C++ 6.0 上測試一下。
很遺憾,不是0,而是1。為什么呢?你想想,如果我們把struct student 看成一個模子的話,你能造出一個沒有任何容積的模子嗎?
顯然不行。編譯器也是如此認為。 編譯器認為任何一種數據類型都有其大小,用它來定義一個變量能夠分配確定大小的空間。既然如此,編譯器就理所當然的認為任何一個結構體都是有大小的,哪怕這個結構體為空。那萬一結構體真的為空,它的大小為什么值比較合適呢?
假設結構體內只有一個char 型的數據成員,那其大小為1byte(這里先不考慮內存對齊的情況).也就是說非空結構體類型數據最少需要占一個字節的空間,而空結構體類型數據總不能比最小的非空結構體類型數據所占的空間大吧。這就麻煩了,空結構體的大小既不能為0,也不能大于1,怎么辦?定義為0.5個byte?但是內存地址的最小單位是1 個byte,0.5 個byte 怎么處理?解決這個問題的最好辦法就是折中,編譯器理所當然的認為你構造一個結構體數據類型是用來打包一些數據成員的,而最小的數據成員需要1 個byte,編譯器為每個結構體類型數據至少預留1 個byte的空間。所以,空結構體的大小就定位1 個byte。
C99 中,結構中的最后一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構中的柔性數組成員前面必須至少一個其他成員。柔性數組成員允許結構中包含一個大小可變的數組。sizeof 返回的這種結構大小不包括柔性數組的內存。包含柔性數組成員的結構用malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
柔性數組到底如何使用呢?看下面例子:
typedef struct st_type
{
? ?int i;
? ?int a[0];
}type_a;
有些編譯器會報錯無法編譯可以改成:
typedef struct st_type
{
? ?int i;
? ?int a[];
}type_a;
這樣我們就可以定義一個可變長的結構體, 用sizeof(type_a) 得到的只有4 , 就是sizeof(i)=sizeof(int)。那個0 個元素的數組沒有占用空間,而后我們可以進行變長操作了。通過如下表達式給結構體分配內存:
? ?type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
這樣我們為結構體指針p 分配了一塊內存。用p->item[n]就能簡單地訪問可變長元素。
但是這時候我們再用sizeof(*p)測試結構體的大小,發現仍然為4。是不是很詭異?我們不是給這個數組分配了空間么?
別急,先回憶一下我們前面講過的“模子”。在定義這個結構體的時候,模子的大小就已經確定不包含柔性數組的內存大小。柔性數組只是編外人員,不占結構體的編制。只是說在使用柔性數組時需要把它當作結構體的一個成員,僅此而已。再說白點,柔性數組其實與結構體沒什么關系,只是“掛羊頭賣狗肉”而已,算不得結構體的正式成員。
需要說明的是:C89 不支持這種東西,C99 把它作為一種特例加入了標準。但是,C99所支持的是incomplete type,而不是zero array,形同int item[0];這種形式是非法的,C99 支持的形式是形同int item[];只不過有些編譯器把int item[0];作為非標準擴展來支持,而且在C99 發布之前已經有了這種非標準擴展了,C99 發布之后,有些編譯器把兩者合而為一了。
當然,上面既然用malloc 函數分配了內存,肯定就需要用free 函數來釋放內存:
? ?free(p);
經過上面的講解,相信你已經掌握了這個看起來似乎很神秘的東西。不過實在要是沒掌握也無所謂,這個東西實在很少用。
當然,關于結構體的討論遠沒有結束,在指針與數組那一章,你還會要和它打交道的。
在網絡協議、通信控制、嵌入式系統、驅動開發等地方,我們經常要傳送的不是簡單的字節流(char 型數組),而是多種數據組合起來的一個整體,其表現形式是一個結構體。
經驗不足的開發人員往往將所有需要傳送的內容依順序保存在char 型數組中,通過指針偏移的方法傳送網絡報文等信息。這樣做編程復雜,易出錯,而且一旦控制方式及通信協議有所變化,程序就要進行非常細致的修改,非常容易出錯。這個時候只需要一個結構體就能搞定。平時我們要求函數的參數盡量不多于4 個,如果函數的參數多于4 個使用起來非常容易出錯(包括每個參數的意義和順序都容易弄錯),效率也會降低(與具體CPU 有關,ARM芯片對于超過4 個參數的處理就有講究,具體請參考相關資料)。這個時候,可以用結構體壓縮參數個數。
一、空結構體多大?
結構體所占的內存大小是其成員所占內存之和(關于結構體的內存對齊,請參考預處理那章)。這點很容易理解,但是下面的這種情況呢?struct student
{
}stu;
sizeof(stu)的值是多少呢?在Visual C++ 6.0 上測試一下。
很遺憾,不是0,而是1。為什么呢?你想想,如果我們把struct student 看成一個模子的話,你能造出一個沒有任何容積的模子嗎?
顯然不行。編譯器也是如此認為。 編譯器認為任何一種數據類型都有其大小,用它來定義一個變量能夠分配確定大小的空間。既然如此,編譯器就理所當然的認為任何一個結構體都是有大小的,哪怕這個結構體為空。那萬一結構體真的為空,它的大小為什么值比較合適呢?
假設結構體內只有一個char 型的數據成員,那其大小為1byte(這里先不考慮內存對齊的情況).也就是說非空結構體類型數據最少需要占一個字節的空間,而空結構體類型數據總不能比最小的非空結構體類型數據所占的空間大吧。這就麻煩了,空結構體的大小既不能為0,也不能大于1,怎么辦?定義為0.5個byte?但是內存地址的最小單位是1 個byte,0.5 個byte 怎么處理?解決這個問題的最好辦法就是折中,編譯器理所當然的認為你構造一個結構體數據類型是用來打包一些數據成員的,而最小的數據成員需要1 個byte,編譯器為每個結構體類型數據至少預留1 個byte的空間。所以,空結構體的大小就定位1 個byte。
二、柔性數組
也許你從來沒有聽說過柔性數組(flexible array)這個概念,但是它確實是存在的。C99 中,結構中的最后一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構中的柔性數組成員前面必須至少一個其他成員。柔性數組成員允許結構中包含一個大小可變的數組。sizeof 返回的這種結構大小不包括柔性數組的內存。包含柔性數組成員的結構用malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
柔性數組到底如何使用呢?看下面例子:
typedef struct st_type
{
? ?int i;
? ?int a[0];
}type_a;
有些編譯器會報錯無法編譯可以改成:
typedef struct st_type
{
? ?int i;
? ?int a[];
}type_a;
這樣我們就可以定義一個可變長的結構體, 用sizeof(type_a) 得到的只有4 , 就是sizeof(i)=sizeof(int)。那個0 個元素的數組沒有占用空間,而后我們可以進行變長操作了。通過如下表達式給結構體分配內存:
? ?type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
這樣我們為結構體指針p 分配了一塊內存。用p->item[n]就能簡單地訪問可變長元素。
但是這時候我們再用sizeof(*p)測試結構體的大小,發現仍然為4。是不是很詭異?我們不是給這個數組分配了空間么?
別急,先回憶一下我們前面講過的“模子”。在定義這個結構體的時候,模子的大小就已經確定不包含柔性數組的內存大小。柔性數組只是編外人員,不占結構體的編制。只是說在使用柔性數組時需要把它當作結構體的一個成員,僅此而已。再說白點,柔性數組其實與結構體沒什么關系,只是“掛羊頭賣狗肉”而已,算不得結構體的正式成員。
需要說明的是:C89 不支持這種東西,C99 把它作為一種特例加入了標準。但是,C99所支持的是incomplete type,而不是zero array,形同int item[0];這種形式是非法的,C99 支持的形式是形同int item[];只不過有些編譯器把int item[0];作為非標準擴展來支持,而且在C99 發布之前已經有了這種非標準擴展了,C99 發布之后,有些編譯器把兩者合而為一了。
當然,上面既然用malloc 函數分配了內存,肯定就需要用free 函數來釋放內存:
? ?free(p);
經過上面的講解,相信你已經掌握了這個看起來似乎很神秘的東西。不過實在要是沒掌握也無所謂,這個東西實在很少用。
三、struct 與class 的區別
在C++里struct 關鍵字與class 關鍵字一般可以通用,只有一個很小的區別。struct 的成員默認情況下屬性是public 的,而class 成員卻是private 的。很多人覺得不好記,其實很容易。你平時用結構體時用public 修飾它的成員了嗎?既然struct 關鍵字與class 關鍵字可以通用,你也不要認為結構體內不能放函數了。當然,關于結構體的討論遠沒有結束,在指針與數組那一章,你還會要和它打交道的。