「編程」 C 語言之柔性數組

一 歷史

在c99標準出來之前。如果要在某個結構體中使用字符串變時,為了使字符串變量存儲地址能與結構體整體連在一起,需要這樣實現

<code>#include <stdio.h>#include <malloc.h>#include <string.h>typedef struct pen{ int len; char *data;//字符串變量}pen;int main(int argc, char **argv){ char str[] = "this is a string";//需要填入的字符串 /* 動態申請一個pen類型結構體變量, 它的大小為,pen類型的本身長度, 再加上str(需要填入字符串的長度),再加1, */ struct pen *p = (struct pen*)malloc(sizeof(pen) + strlen(str) + 1); p->data= NULL; //設置p的長度為目標字符串的長度 p->len = strlen(str); /* 將目標字符串拷貝到結構體對應的位置 此處為什麼p+1之後指向的是pen結構體存儲空間後的位置,而不是隻加一呢? 因為此處的p+1偏移的是p指向類型大小的偏移量,什麼意思呢?p指向的類型為pen類型的結構體, 而pen類型的結構體大小為 len(4字節)加上 data(8個字節),由於此處有內存對齊的情況, 所以實際上pen大小為 4 + 8 + 4(這個4為內存對齊的多餘空間,如果再增加一個int類型的變量, pen的大小還是為16)=16字節 所以此處p+1向後偏移了16字節,通過下方地址打印可以詳細看出 */ strcpy((char*)(p + 1), str); //int所佔字節數,不同機器不同。一般64位為4字節 printf("sizeof(int): %ld\\n", sizeof(int)); //上文已說明,16字節 printf("sizeof(pen): %ld\\n", sizeof(pen)); //起始地址 printf("start: %p\\n\\n", (char*)p); //上文已說明,偏移後的地址 printf("(p+1) : %p\\n", (char*)(p+1)); //偏移後,對應的字符串 printf("(char*)(p+1): %s\\n\\n", (char*)(p+1)); //結構體變量data的地址 printf("&(p->data): %p\\n", &(p->data)); //數據,null,此處為空,故此變量已經被浪費。訪問對應字符串數據需要(char *)(p+1) printf("p->data: %s\\n\\n", p->data);}/<string.h>/<malloc.h>/<stdio.h>/<code>

二 柔性數組

通過上文我們可以看到,data字段是一個被浪費的指針(8個字節)。並且我們想取到結構體下的字符串變量時需要(char *)(p+1)寫這麼一串東西,既不好看,也容易出錯,那有沒有可以直接用p->data取到字符串並且內存是連續的,而且又不浪費data字段呢,

柔性數組 就是用來幹這個的。

<code>#include <stdio.h>#include <malloc.h>#include <string.h>typedef struct pen{ int len; char data[];//柔性數組}pen;int main(int argc, char **argv){ char str[] = "this is a new string";//需要填入的字符串 struct pen *p = (struct pen*)malloc(sizeof(pen) + strlen(str) + 1); p->data= NULL; p->len = strlen(str); strcpy((char *)(p+1), str); printf("pen->data: %s\\n", p->data);}/<string.h>/<malloc.h>/<stdio.h>/<code>

C99使用不完整類型實現柔性數組成員,在C99 中,結構中的最後一個元素允許是未知大小的數組,這就叫做柔性數組(flexible array)成員(也叫伸縮性數組成員),但結構中的柔性數組成員前面必須至少一個其他成員。柔性數組成員允許結構中包含一個大小可變的數組柔性數組成員只作為一個符號地址存在,而且必須是結構體的最後一個成員,sizeof 返回的這種結構大小不包括柔性數組的內存