可變長度的陣列(定義陣列時長度為0)
在標準C和C++中,長度為0的陣列是被禁止使用的。不過在GNU C中,存在一個非常奇怪的用法,那就是長度為0的陣列,比如Array[0];很多人可能覺得不可思議,長度為0的陣列是沒有什麼意義的,不過在這兒,它表示的完全是另外的一層意思,這個特性是不可移植的,所以,如果你致力於編寫可移植,或者是稍稍需要跨平臺的程式碼,這些伎倆最好還是收起來的好
用途 :長度為0的陣列的主要用途是為了滿足需要變長度 的結構體。
用法 :在一個結構體的最後 ,申明一個長度為0的陣列,就可以使得這個結構體是可變長的。對於 編譯器來說,此時長度為0的陣列並不佔用空間,因為陣列名本身不佔空間,它只是一個偏移量, 陣列名這個符號本身代 表了一個不可修改的地址常量 (注意:陣列名永遠都不會是指標! ),但對於這個陣列的大小,我們可以進行動態分配。例如:
typedef struct
{
int len;
char data[0];
}test_t;
int my_length = 10;
test_t *p_test = (test_t *)malloc(sizeof(test_t) + my_length);
p_test->len = my_length;
......
free(p_test);
之後對於結構體中的陣列可以像一般的陣列一樣進行訪問。
注意 :如果結構體是通過calloc、malloc或 者new等動態分配方式生成,在不需要時要釋放相應的空間。
優點 :比起在結構體中宣告一個指標變數、再進行動態分 配的辦法,這種方法效率要高。因為在訪問陣列內容時,不需要間接訪問,避免了兩次訪存。
缺點 :在結構體中,陣列為0的陣列必須在最後宣告,使 用上有一定限制。
案例
近日在看專案中的框架程式碼時,發現了了一個奇特的語法:長度為0的陣列例如
uint8_t buf[0];
我從未見過這樣的寫法,所以在網上查了查資料,瞭解並記錄下來.
在標準的C/C++中,長度為0的陣列是不被允許的,它算是一個C/C++擴充套件,如果你的編譯器支援這個擴充套件,你就可以使用它.
VS系列編譯器不完全支援這個擴充套件,如果你這樣定義,多半會在編譯時出現這樣的警告:warning C4200: 使用了非標準擴充套件 : 結構/聯合中的零大小陣列,當 UDT 包含大小為零的陣列時,無法生成複製建構函式或副本賦值運算子
GUN編譯器完全支援這個擴充套件,你可以合法的宣告長度為0的陣列,但這種宣告的最典型的用法就是位於陣列中的最後一項,為了方便記憶體緩衝區的管理,例如:
struct Line{
uint32_t length;
char contents[0];
};
在結構體中,長度為0的陣列不會佔用儲存空間 ,在上述例子中 sizeof(Line)=4
在申請記憶體空間時,緩衝區的空間可以和結構體的空間一起申請,一次操作就可以完成.例如
uint32_t length = 10;
struct Line *pLine = (struct Line *)malloc(sizeof (struct Line) + length);
pLine->length = length;
上述程式碼就動態地為結構體申請了長度(length)為10byte的緩衝區,而且由於是同一次malloc操作,緩衝區與結構體的記憶體地址是連續的,而且可以按照陣列下標訪問緩衝區元素,例如
for(uint32_t i = 0;i < pLine->length;++i)
{
pLine->contents[i] = i;
}
由於緩衝區與結構體的記憶體地址是連續的,在釋放記憶體的時候,只需要一次free操作.
綜上所述,比起在結構體中定義一個指標指向另一片緩衝區地址的做法,使用長度為0的陣列有以下好處:
1->指標本身需要佔用記憶體,而長度為0的陣列不需要
2->長度為0的陣列定義出的緩衝區可以和結構體處在同一片連續地址中,只要一次malloc操作和free操作.如果用指標,需要分別申請和釋放結構體記憶體和指標指向的記憶體塊,至少需要兩次以上的記憶體操作.
例項:
#include <stdio.h>
#include <stdint.h>
#include <malloc.h>
struct Line{
uint32_t length;
uint8_t contents[0];//在結構體中,陣列為0的陣列必須在最後宣告,使 用上有一定限制
};
int32_t main()
{
uint32_t length = 10, i;
printf("sizeof(Line)=%d\n", sizeof(struct Line));//列印結構體長度,發現大小為4,說明陣列長度為0不佔用空間
//申請記憶體
struct Line *pLine = (struct Line *)malloc(sizeof (struct Line) + length);
pLine->length = length;
//向動態陣列中存放數
for (i = 0; i < pLine->length; ++i)
{
pLine->contents[i] = i;
}
//列印動態數組裡面的內容
for (i = 0; i < pLine->length; ++i)
{
printf("i=%d,contents[i]=%d\n", i, pLine->contents[i]);
}
//釋放記憶體
free(pLine);
return 0;
}
結果: