聊聊c語言的flexible array member
本文將flexible array member翻譯爲彈性數組(成員),將介紹彈性數組的語法,好處與代價,以及擴展聊聊關於c語言操作內存靈活性方面的思考。
語法
struct Foo { int a; char b[]; // 有時也寫成 char b[0]; };
如上面例子展示,將結構體的最後一個數據成員定義爲不寫長度(或長度爲0)的數組即爲彈性數組。
這種寫法,b數據成員不佔用大小,看如下代碼:
struct Foo foo; printf("%d %d\n", sizeof(foo), sizeof(foo.a));
在我的mac電腦上使用 clang-1100.0.33.12
編譯,打印結果爲 4 4
,說明foo變量大小等於foo變量中a數據成員的大小。
使用如下方法爲彈性數組分配內存會產生編譯錯誤:
foo.b = malloc(128);
編譯錯誤信息: error: array type 'char []' is not assignable
正確的使用方式是:
struct Foo *foo = malloc(sizeof(struct Foo) + 128);
該方式共申請了 4+128
字節大小的內存,該132字節內存是連續的,前4個字節分配給 foo->a
,後128字節可以通過 foo->b
訪問。
好處與代價
一般來說,彈性數組用於元素個數在運行期動態決定的場景。你可能會說,爲什麼不直接用指針呢,就像下面這樣:
struct Foo { int a; char *b; };
上面這種寫法確實可以實現同樣的功能,但是存儲相同大小的數據時,兩種方式存在一些區別:
第一,使用這種寫法,b指針變量本身要佔用內存,注意,不管你是否爲b指針分配內存,即使 b==NULL
,變量自身都需要佔用內存。在64位系統,一個指針變量是8個字節,別小看這8個字節,在內存總量比較小的場景,或結構體變量非常多的場景還是很客觀的。
第二,使用彈性數組,彈性數組的內存地址和它之前的數據成員的地址是連續的。訪問時內存的空間局部性也更好些。
但是話說回來,使用彈性數組並不只有好處,它也有代價。
彈性數組的方式,由於結構體中的數據成員和後面掛着的這個數據成員是通過一個malloc申請的內存,這也意味:
第一,整個結構體都要分配在堆上。
第二,當需要對數組內存進行擴容時,你需要對整塊內存realloc。
彈性數組是語法糖,有威力的是c語言操作內存的自由度
其實,我們不使用彈性數組也可以達到彈性數組的效果,如下面代碼:
struct Foo { int a; }; struct Foo *foo = malloc(sizeof(struct Foo) + 128); char *b = (char *)foo + sizeof(struct Foo);
彈性數組只是一個語法糖,它在結構體最後增加一個成員變量,讓我們可以使用 foo->b
這種方式,直接訪問結構體之後的內存。事實上,你如果自己計算偏移量,也可以到達一樣的效果。
這裏要撇開彈性數組,聊聊閒篇。
在操作內存方面,c語言給它的使用者提供了非常高的自由度,它自身並不標記內存中存儲的是什麼類型的數據,使用者可以對內存地址做任意前後偏移,通過指針類型強轉,解引用,可以把內存按任意類型解析,寫入,讀取。當然,前提是不要發生越界,並且讀寫一致,邏輯符合使用者的預期。
自由度越高,就越可以在更多的場景做更多的優化。但帶來的代價,則是和高級語言相比,可讀性差些,也容易寫出bug。當然,這句話只對於同等水平的初中級程序員有效哈,高手可以無視。
語法補充
最後,對語法做些補充。
第一,在我的環境,如果使用 char b[]
這種寫法,再使用 sizeof(foo.b)
獲取b數據成員大小,將產生編譯錯誤:
error: invalid application of 'sizeof' to an incomplete type 'char []'
第二,彈性數組一般作爲結構體的最後一個數據成員出現,如下代碼會產生編譯錯誤:
struct Foo { char b[]; int a; };
編譯錯誤信息: error: flexible array member 'b' with type 'char []' is not at the end of struct
原文鏈接: https://pengrl.com/p/20013/
原文出處: yoko blog ( https://pengrl.com )
原文作者: yoko ( https://github.com/q191201771 )
版權聲明:本文歡迎任何形式轉載,轉載時完整保留本聲明信息(包含原文鏈接、原文出處、原文作者、版權聲明)即可。本文後續所有修改都會第一時間在原始地址更新。