[英]Allocating a dynamic array in a dynamically allocated struct (struct of arrays)
這個問題實際上是關於如何了Python / C API(在使用可變長度類型PyObject_NewVar
, PyObject_VAR_HEAD
, PyTypeObject.tp_basicsize
和.tp_itemsize
,但我可以問這個問題,而不與API的細節困擾,只是假設我需要在struct
內部使用數組。
我可以通過以下兩種方式之一創建列表數據結構。 (我現在只討論char
列表,但這沒關系。)第一個使用指針,並且需要兩個分配 。 忽略#include
和錯誤處理:
struct listptr {
size_t elems;
char *data;
};
struct listptr *listptr_new(size_t elems) {
size_t basicsize = sizeof(struct listptr), itemsize = sizeof(char);
struct listptr *lp;
lp = malloc(basicsize);
lp->elems = elems;
lp->data = malloc(elems * itemsize);
return lp;
}
創建列表的第二種方法使用數組符號和一個分配 。 (我知道第二個實現有效,因為我已經對其進行了徹底的測試。)
struct listarray {
size_t elems;
char data[1];
};
struct listarray *listarray_new(size_t elems) {
size_t basicsize = offsetof(struct listarray, data), itemsize = sizeof(char);
struct listarray *la;
la = malloc(basicsize + elems * itemsize);
la->elems = elems;
return lp;
}
在這兩種情況下,都可以使用lp->data[index]
訪問該數組。
我的問題是第二種方法為何有效? 為什么要聲明char data[1]
而不是char data[]
, char data[0]
, char *data
或char data
? 特別是,我對struct
的工作方式的直觀理解是,聲明data
的正確方法是完全沒有指針或數組表示法的char data
。 最后, 在兩個實現中我對basicsize
和itemsize
計算itemsize
正確 ? 特別是,是否對所有機器正確使用了offsetof
?
顯然,這稱為struct hack :在C99中,您可以使用靈活的數組成員 :
struct listarray2 {
size_t elems;
char data[];
}
了解到您將在運行時為data
malloc
足夠的空間。 在C99之前, data[1]
聲明很常見。 因此,我的問題是現在為什么要聲明char data[1]
或char data[]
而不是char *data
或char data
?
您聲明char data[1]
或char data[]
而不是char *data
或char data
是為了使結構直接可序列化和反序列化 。 在將這些類型的結構寫入磁盤或通過網絡套接字等的情況下,這一點很重要。
例如,需要兩個分配的第一個代碼段。 您的listptr類型不能直接序列化。 即listptr.elems和listptr.data指向的數據不在連續的內存中。 使用通用功能無法從磁盤讀取/寫入此結構。 您需要特定於您的struct listptr
類型的自定義函數來執行此操作。 即在序列化時,您必須首先將elems
寫入磁盤,然后再寫入數據指針指向的數據。 反序列化時,您必須讀取elems,將適當的空間分配給listptr.data,然后從磁盤讀取數據。
使用靈活的數組成員可以解決此問題,因為listptr.elem和listptr.data駐留在連續的內存空間中。 因此,要對其進行序列化,您只需寫出該結構的總分配大小,然后寫出該結構本身。 在反序列化時,您首先要讀取分配的大小,分配所需的空間,然后將listptr結構讀取到該空間中。
您可能想知道為什么您真的需要此功能,但是它可能是非常寶貴的功能。 考慮異構類型的數據流。 如果您定義了一個標頭,該標頭定義了您擁有的異構類型及其大小,並且在該標頭之前定義了流中的每種類型,則可以非常優雅,高效地進行序列化和反序列化。
我知道選擇char data[1]
不是char data[]
的唯一原因是,是否要定義一個需要在C99和C ++之間移植的API,因為C ++不支持靈活的數組成員。
另外,想指出的是,在char data[1]
您可以執行以下操作以獲得所需的總結構大小:
size_t totalsize = offsetof(struct listarray, data[elems]);
您還問為什么不使用char data
代替char data[1]
或char data[]
。 雖然從技術上講可以僅使用簡單的舊char data
,但是(IMHO)在道德上應避免使用。 這種方法的兩個主要問題是:
您需要一個char數組,但是現在您不能直接作為數組訪問data
成員。 您需要將指針指向data
地址以作為數組進行訪問。 即
char * as_array =&listarray.data;
您的結構定義(以及您的代碼對結構的使用)將完全誤導任何閱讀該代碼的人。 為什么要聲明一個char
時,你的真正用意字符數組?
鑒於這兩件事,我不知道為什么有人會使用char data
來代替char data[1]
。 給出替代方案的任何人都不會從中受益。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.