这个问题实际上是关于如何在Python / C API(PyObject_NewVar
,PyObject_VAR_HEAD
,PyTypeObject.tp_basicsize
and .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
的正确计算?特别是,offsetof
的使用是否保证对所有机器都正确?
显然这被称为struct hack:在C99中,您可以使用flexible array member:
struct listarray2 {
size_t elems;
char data[];
}
了解到malloc
在运行时data
有足够的空间。在C99之前,data[1]
声明很常见。所以现在我的问题是为什么要声明char data[1]
或char data[]
而不是char *data
或char data
?
答案 0 :(得分:1)
您声明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 ++不支持灵活的数组成员。 / p>
另外,想指出在char data[1]
中您可以执行以下操作以获得所需的总体结构大小:
size_t totalsize = offsetof(struct listarray, data[elems]);
您还会问为什么不使用char data
代替char data[1]
或char data[]
。虽然技术上可以使用普通的旧char data
,但它会(恕我直言)在道德上被避开。这种方法的两个主要问题是:
您想要一个字符数组,但现在您无法直接作为数组访问data
成员。您需要指向data
地址的指针才能将其作为数组访问。即。
char * as_array =& listarray.data;
您的结构定义(以及您的代码对结构的使用)将完全误导任何阅读代码的人。为什么当你真正意味着一个char数组时声明一个char
?
考虑到这两件事,我不知道为什么有人会使用char data
来支持char data[1]
。如果给出替代方案,那对任何人都没有好处。