C语言 在动态分配的结构(数组的结构)中分配动态数组



这个问题实际上是关于如何在Python/C API中使用可变长度类型(PyObject_NewVarPyObject_VAR_HEADPyTypeObject.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 *datachar data中的任何一个?特别是,我对struct工作原理的直观理解是,声明data的正确方法是char data根本没有指针或数组符号。最后,我在两种实现中对basicsizeitemsize的计算是否正确?特别是,这种offsetof的使用是否保证对所有机器都是正确的?

更新

显然,这被称为结构黑客:在 C99 中,您可以使用灵活的数组成员:

struct listarray2 {
    size_t elems;
    char data[];
}

了解您将在运行时为data malloc足够的空间。在C99之前,data[1]声明很常见。所以我现在的问题是,为什么要声明char data[1]char data[]而不是char *datachar data

声明 char data[1]char data[] 而不是 char *datachar 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,但(恕我直言(在道德上是回避的。这种方法的两个主要问题是:

  1. 你想要一个字符数组,但现在你不能直接将data成员作为数组访问。您需要将指针指向 data 的地址才能将其作为数组进行访问。即

    char *as_array = &listarray.data;

  2. 您的结构定义(以及代码对结构的使用(会完全误导任何阅读代码的人。当你真正指的是一个字符数组时,为什么要声明一个char

鉴于这两件事,我不知道为什么有人会使用char data来支持char data[1].它只是对任何有替代方案的人都没有好处。

相关内容

  • 没有找到相关文章

最新更新