在前面的文章中的dnw原始碼,使用了這樣一個結構體:
struct download_buffer { uint32_t load_addr; /* load address */ uint32_t size; /* data size */ //size=地址(4位)+大小(4位)+資料+校正(2位) uint8_t data[0];//0長度數組,指向資料 /* uint16_t checksum; */ //數組後緊接著的兩位是校正位 };
其中data[0]是一個0長度數組,用於柔性拓展結構體。
分配記憶體空間:
buffer = alloc_buffer(file_stat.st_size);
也就是:
buffer = malloc(data_size + sizeof(struct download_buffer) + 2);
data_size是結構體成員buffer->data的長度,使用sizeof(struct download_buffer)取得結構體長度時,不計算
data成員的長度,這樣就實現了一個可以動態調節大小的結構體,根據所需要的空間分配給data成員空間。
再來看一個網上的例子:
定義一個結構體
typedef struct user_def{ char * name; int length; char bytes[0];} user_def_t;
在這裡我們使用sizeof(bytes)會發現結果等於0,這個bytes就是一個零長度的柔性數組,使用方法如下:
int alloc_user_def_t(user_def_t * p, int length){ p = (user_def_t)malloc(sizeof(user_def_t) + length); if (NULL == p) { return -1; } p->name = NULL; p->length = length; memset(p->bytes, 0, length); return 0;}
這樣我們就將length長度的0字元寫進bytes數組,相當與一個附件附在結構體後面,可以通過這個數組訪問。
再看一個網上例子:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>struct helloworld_t{ int num; char helloworld[0];//主要是用來得到一個數組的地址,再由數組的個數來訪問};int main(){struct helloworld_t *p;unsigned int size = sizeof(struct helloworld_t) + strlen("Hello World!\n") + 1; p = (struct helloworld_t *) malloc(size);assert(p!=NULL);memcpy(p, "\x01\x00\x00\x00Hello World!\n", size);//\x01\x00\x00\x00四位元組,就是給num賦值//在記憶體中高8位和低8位反一下...//printf("%d\n",p->num);while (p->num--){printf(p->helloworld);}//printf("%d \n", sizeof(helloworld_t));free((void *)p);return 0;}
列印結果是:Hello World!
C99中,結構中的最後一個元素允許是未知大小的數組,這就叫做柔性數群組成員,但結
構中的柔性數群組成員前面必須至少一個其他成員。 柔性數群組成員允許結構中包含一個大小可
變的數組。sizeof返回的這種結構大小不包括柔性數組的記憶體。包含柔性數群組成員的結構用
malloc ()函數進行記憶體的動態分配,並且分配的記憶體應該大於結構的大小,以適應柔性數組
的預期大小。
在定義這個結構體的時候,結構體的大小就已經確定不包含柔性數組的記憶體大小。
柔性數組只是編外人員,不佔結構體的編製。只是說在使用柔性數組時需要把它當作結
構體的一個成員,僅此而已。再說白點,柔性數組其實與結構體沒什麼關係,只是“掛羊頭
賣狗肉”而已,算不得結構體的正式成員。
需要說明的是:C89不支援這種東西,C99把它作為一種特例加入了標準。但是,C99所
支援的是 incomplete type,而不是 zero array,形同 int item[0];這種形式是非法的,C99支援
的形式是形同 int item[];只不過有些編譯器把 int item[0];作為非標準擴充來支援,而且在C99
發布之前已經有了這種非標準擴充了,C99發布之後,有些編譯器把兩者合而為一了。當然,上面
既然用 malloc函數分配了記憶體,肯定就需要用free函數來釋放記憶體:
free(p);
舉個例子:
typedef struct st_type{int i;int a[0];}type_a;
GNU的編譯器可以通過編譯,有些編譯器會報錯無法編譯(例如VS系列)可以改成:
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->a[n]就能簡單地訪問可變長元素(p->a[0]~p->[99])。
但是這時候我們再用 sizeof(*p)測試結構體的大小,發現仍然為 4。
那麼,為什麼不在結構體裡使用指標成員來指向該字串所在的動態記憶體空間呢?
例如:
struct ptest{ int a; double b; char *c;};
指標成員c指向字串。這種方法會造成字串與結構體分離,malloc和free對記憶體管理產生片段,也不利於操作。
如果我們把字串和結構體串連在一起,字串位置緊接著結構體,這樣修改代碼:
char Str[] = "hello world";
struct ptest *Test = (struct ptest*)malloc(sizeof(struct ptest)+strlen(Str)+1);//+1是因為strlen不計算結束符
strcpy(Test+1,Str)
(char*)(Test+1)就是字串“hello world”的地址了。c就成了多餘的東西,可以去掉。然而,直接使用(char*)(Test+1)很不方便。於是,這種既能直接引用字串,還不佔用結構體空間,能靈活分配資料域大小的要求,柔性數組就能夠滿足。
在這個結構體中,c是柔性數群組成員,如果把Test指向的動態記憶體分配看作一個整體,c就是一個長度可以變化的柔性數組,c在結構體中長度為0,不佔用Test的空間,Test->c就是“hello world”的首地址。