聊聊c語言的flexible array member

本文將 flexible array member 翻譯為彈性數組(成員),將介紹彈性數組的語法,好處與代價,以及擴展聊聊關於 c 語言操作內存靈活性方面的思考。

語法

<code>struct Foo {    int a;    char b[]; // 有時也寫成 char b[0];};/<code>

如上面例子展示,將結構體的最後一個數據成員定義為不寫長度(或長度為 0)的數組即為彈性數組。

這種寫法,b 數據成員不佔用大小,看如下代碼:

<code>struct Foo foo;printf("%d %d\\n", sizeof(foo), sizeof(foo.a));/<code>

在我的 mac 電腦上使用clang-1100.0.33.12編譯,打印結果為4 4,說明 foo 變量大小等於 foo 變量中 a 數據成員的大小。

使用如下方法為彈性數組分配內存會產生編譯錯誤:

<code>foo.b = malloc(128);/<code>

編譯錯誤信息: error: array type 'char []' is not assignable

正確的使用方式是:

<code>struct Foo *foo = malloc(sizeof(struct Foo) + 128);/<code>

該方式共申請了4+128字節大小的內存,該 132 字節內存是連續的,前 4 個字節分配給foo->a,後 128 字節可以通過foo->b訪問。

好處與代價

一般來說,彈性數組用於元素個數在運行期動態決定的場景。你可能會說,為什麼不直接用指針呢,就像下面這樣:

<code>struct Foo {    int a;    char *b;};/<code>

上面這種寫法確實可以實現同樣的功能,但是存儲相同大小的數據時,兩種方式存在一些區別:

第一,使用這種寫法,b 指針變量本身要佔用內存,注意,不管你是否為 b 指針分配內存,即使b==NULL,變量自身都需要佔用內存。在 64 位系統,一個指針變量是 8 個字節,別小看這 8 個字節,在內存總量比較小的場景,或結構體變量非常多的場景還是很客觀的。

第二,使用彈性數組,彈性數組的內存地址和它之前的數據成員的地址是連續的。訪問時內存的空間局部性也更好些。

但是話說回來,使用彈性數組並不只有好處,它也有代價。

彈性數組的方式,由於結構體中的數據成員和後面掛著的這個數據成員是通過一個 malloc 申請的內存,這也意味:

第一,整個結構體都要分配在堆上。

第二,當需要對數組內存進行擴容時,你需要對整塊內存 realloc。

彈性數組是語法糖,有威力的是 c 語言操作內存的自由度

其實,我們不使用彈性數組也可以達到彈性數組的效果,如下面代碼:

<code>struct Foo {    int a;};struct Foo *foo = malloc(sizeof(struct Foo) + 128);char *b = (char *)foo + sizeof(struct Foo);/<code>

彈性數組只是一個語法糖,它在結構體最後增加一個成員變量,讓我們可以使用foo->b這種方式,直接訪問結構體之後的內存。事實上,你如果自己計算偏移量,也可以到達一樣的效果。

這裡要撇開彈性數組,聊聊閒篇。

在操作內存方面,c 語言給它的使用者提供了非常高的自由度,它自身並不標記內存中存儲的是什麼類型的數據,使用者可以對內存地址做任意前後偏移,通過指針類型強轉,解引用,可以把內存按任意類型解析,寫入,讀取。當然,前提是不要發生越界,並且讀寫一致,邏輯符合使用者的預期。

自由度越高,就越可以在更多的場景做更多的優化。但帶來的代價,則是和高級語言相比,可讀性差些,也容易寫出 bug。當然,這句話只對於同等水平的初中級程序員有效哈,高手可以無視。

語法補充

最後,對語法做些補充。

第一,在我的環境,如果使用char b[]這種寫法,再使用sizeof(foo.b)獲取 b 數據成員大小,將產生編譯錯誤:

error: invalid application of 'sizeof' to an incomplete type 'char []'

第二,彈性數組一般作為結構體的最後一個數據成員出現,如下代碼會產生編譯錯誤:

<code>struct Foo {    char b[];    int a;};/<code>

編譯錯誤信息: error: flexible array member 'b' with type 'char []' is not at the end of struct


聊聊c語言的flexible array member


分享到:


相關文章: