[英]Allocating contiguous memory to contain multiple structs with flexible array members
考慮一個包含靈活數組成員的結構,如下所示:
typedef struct {
size_t len;
char data[];
} Foo;
我的Foos數量未知,每個的大小都未知,但是我可以確定所有的Foos總共總共為1024個字節。 在知道每個Foo的長度之前,如何在一個Foos數組中分配1024個字節,然后稍后填充該數組的成員?
像這樣的東西,盡管它拋出了段錯誤:
Foo *array = malloc(1024);
int array_size = 0;
Foo foo1;
strcpy(foo1.data, "bar");
array[0] = foo1;
array_size++;
Foo foo2;
strcpy(foo2.data, "bar");
array[1] = foo2;
array_size++;
for (int i = 0; i < array_size; i++)
puts(array[i].data);
這樣做的原因是為了將所有Foos都保留在一個連續的內存區域中,以保持CPU緩存的友好性。
您根本不能擁有foos數組,因為foo沒有固定的大小,並且數組的定義特征是每個對象都具有固定的大小和相對於可從其索引計算的基礎的偏移量。 對於您想要的工作,索引array[n]
必須知道foo[0]
, foo[1]
,..., foo[n-1]
的完整大小,這是不可能的,因為該語言沒有這些大小的知識; 實際上,靈活數組成員只是從大小中排除,因此foo[1]
將與foo[0]
的數據“重疊”。
如果需要能夠以數組的形式訪問這些對象,則需要放棄在每個對象中放置一個靈活的數組成員。 相反,您可以將所有數據放在最后,並在每個數據中存儲一個指向數據的指針或偏移量。 如果不需要以數組形式訪問它們,則可以在分配的內存中構建一種鏈表,將到下一個條目的偏移量存儲為每個條目的成員。 (例如,請參見在大多數getdents
struct dirent
如何與getdents
使用。)
正如其他人指出的那樣,您不能擁有C數組Foo
。 但是,假設您願意不定期地存儲它們,只需要知道可能需要多少空間即可。 這個答案表明。
令N為Foo
對象的數量。
令S為sizeof(Foo)
,它是Foo
對象的大小,其中data
為零字節。
設A為_Alignof(Foo)
。
每個Foo
對象必須以與A字節對齊的地址開始。 讓它成為A。 填充的最壞情況是data
數組是一個字節,要求在下一個Foo
開始之前跳過A -1個字節。
因此,除了Foo
對象消耗的1024個字節(包括它們的data
)之外,我們可能還需要( N -1)•( A -1)個字節用於此填充。 ( N -1是因為最后一個Foo
之后不需要填充字節。)
如果每個Foo
至少有一個字節的data
,則最大N可能是floor(1024 /( S +1)),因為我們知道所有Foo
對象及其數據最多使用1024個字節。
因此,1024 + floor(1024 /( S +1)-1)*( A -1)字節就足夠了-實際數據為1024字節,而floor(1024 /( S +1)-1)*( A -1)為實際數據填充。
注意,以上假設每個Foo
至少具有一個字節的data
。 如果一個或多個Foo
的data
字節為零,則N可能大於floor(1024 /( S +1))。 但是,在任何這樣的Foo
,不需要填充,並且對於每個這樣的Foo
N不能增加一個以上(因為減少一個字節使用的空間不能為一個以上Foo
產生更多的空間)。 因此,這樣的Foo
可以在需要A -1個字節填充的其他地方給我們一個Foo
,但是它本身不需要填充,因此所需填充的總量不能增加。
因此,為Foo
對象分配內存的計划是:
Foo
放在分配的內存的開頭。 Foo
在下一甲 -aligned地址先前結束后Foo
(包括其data
)。 當然,這不會產生一個數組,只是分配空間內的大量Foo
對象。 您將需要指針或其他解決它們的方法。
Per C 2018 7.22.3.4 2:
malloc
函數為size
由大小指定且值不確定的對象分配空間。
因此,以不規則的方式切碎malloc
返回的空間以用於多個對象並不適合該規范。 我將其留給其他人討論,但是我還沒有觀察到C實現對它有問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.