2012-10-15 128 views
1

假設我有結構用C這樣與結構陣列結構的劈

struct A { 
int len; 
char s[1]; 
} 

我想有上述結構的陣列,但結構A的char s[1]構件可以是可變長度的。我們應該怎麼做?即使C99中的struct hack技巧在這裏似乎也不起作用。一種解決方案是將char *作爲最後一個成員並進行動態內存分配,但我希望struct的所有數據位於連續的位置,因爲我的實現需要緩存不經意。

+2

這只是C99之前的「黑客」,如果你在C90中這樣做,任何事情都可能發生。在C99中,它是一個定義明確的特性,被稱爲_flexible array member_。 – Lundin

回答

5

你不能擁有一個可變大小的對象數組,所以你不能擁有一個使用struct hack的結構數組。數組中的所有對象必須具有相同的大小。如果它們的大小完全相同,則結構必須隱含大小,因此畢竟不會使用struct hack;在結構中數組s的維度中將會有一個非1的大小(除非1大到足夠大)。原因在於a[i]的存儲位置(其中a是數組的名稱,而i是數組中的索引)必須可計算爲'a的字節地址加上(i乘以數組中的一個對象的大小)'。所以數組中對象的大小(在本例中是結構體)必須是已知的並且是固定的。

作爲替代方案,您可以有一個指向可變大小對象的指針數組;您只需安排分配適當大小的每個對象,並將指針保存到數組中。

需要注意的是C99摒棄了「結構黑客」(這是從未正式便攜,但實際上它是),並介紹了「靈活數組成員」,而不是:

struct A { 
    int len; 
    char data[]; 
}; 

然而,上面還建議適用。

+0

那麼在這種情況下,它不能是結構數組,它將是指向結構的指針數組... –

+0

正確;我澄清了我的答案(我希望)強調爲什麼它必須如此。 –

+0

由於struct padding字節,它在實踐中從不可移植。無論何時從具有類似架構的平臺(如Windows和Linux之間的平臺)移植,它都可能正常工作。如果你從8位微控制器移植到Windows,你很可能會遇到問題,因爲它們會有完全不同的對齊要求。 – Lundin

1

如果存在「s」的最大尺寸,則可以使用該尺寸代替[1]。這使得一切都保持連續。

如果你真的不想使用動態內存,那麼你不能使用數組。你需要你自己的「管理員」,它將單獨使用每個成員的結構黑客技巧 - 但這意味着你不能進行索引查找 - 你必須查看每個元素,看看它有多大,並跳轉正確的字節數到下一個元素。

+0

我想到了這一點,我想如果我沒有得到更好的東西,我必須這樣做.. –

1

在C中,數組索引包括將基地址乘以單個元素的編譯時常量大小。因爲這個原因,你不能直接用「struct hack」來使用內置的數組支持,因爲每個s元素將被精確地分配給你請求的1個字節,並且超越結構的索引將訪問數組中的S元素(或完全消失,可能會崩潰)。

如果你真的需要緩存訪問速度連續數據,你可以自己收拾它,就可以解決這個(最喜歡的東西)與間接...有S*連續陣列,並手動收拾你數據到另一個連續緩衝區(malloc()或堆棧分配足夠的內存用於所有的S對象,包括所有s[]成員的實際數據大小)。如果int len元素沒有針對您的架構進行最佳(正確)對齊,您的性能可能會受損(或您的操作系統崩潰),因此您可能需要在S實例之間手動填充。

S* index[100]    char data[10000]; 
(S*)(data) --------------> S with 14-byte s[] using data[0]..[17] 
(S*)(data + 20) -----\  2 byte padding so next S is 4-byte aligned 
(S*)(data + 32) --\ \---> S with 7-byte s[] using data[20]..[30] 
        \  1 byte padding... 
        \-----> ... 

不幸的是,這是相當不靈活的數據佈局 - 你不能只生長在一個元素的s成員的數據量,而不schuffling所有其他數據的方式進行,並修補指數,但這是正常的對於數組,如果你已經在考慮使用它們,那麼這可能適合你。另一個麻煩是計算S結構(包括s[]和任何填充)前端的總大小....