【问题标题】:How to allocate an array of structs with flexible array members comformingly?如何统一分配具有灵活数组成员的结构数组?
【发布时间】:2019-04-03 19:10:03
【问题描述】:

我有以下带有灵活数组成员的结构:

struct test {
    size_t sz;
    const char str[];
};

现在我想分配一些内存来连续放置这个结构(比如在数组中)。问题是像struct test test_arr[] 这样的声明是未定义的行为。 6.7.2.1(p3):

具有多个命名成员的结构的最后一个成员 可能有不完整的数组类型;这样的结构(以及任何工会 可能递归地包含这样一个结构的成员) 不得是结构的成员或数组的元素。

我们知道malloc 返回的指针可以转换为指向任何具有基本对齐的对象类型的指针。考虑以下代码:

void *obj= malloc(100 * sizeof(struct test)); //enough memory
struct test *t1 = obj;
t1 -> sz = 2;
t1 -> str = {'a', 'b'};
struct test *t2 = (void *) (((char *) obj) + sizeof(struct test) + sizeof(char[2])); // non conforming 

这样做的合规方式是什么?

【问题讨论】:

  • struct test *myText = malloc(size * sizeof (*myText)) 有什么问题?
  • 或者更重要的是,您要解决的实际问题是什么?
  • @Broman 没关系。但是这个怎么样:struct test *myStruct = myText + 1;
  • @Broman 从inotify 文件描述符文件中读取提供的缓冲区,其中struct inotify_event 包含灵活的数组成员。

标签: c language-lawyer flexible-array-member


【解决方案1】:

具有灵活数组成员的struct 不能成为数组的成员,如您给出的报价所规定的那样。

处理这个问题的最佳方法是将灵活数组成员更改为指针并为其单独分配空间。

struct test {
    size_t sz;
    char *str;
};

...

struct test *arr = malloc(100 * sizeof(struct test));

arr[0].sz = 2;
arr[0].str = malloc(2);
arr[0].str[0] = 'a';
arr[0].str[1] = 'b';

arr[1].sz = 3;
arr[1].str = malloc(3);
arr[1].str[0] = 'c';
arr[1].str[1] = 'd';
arr[1].str[2] = 'e';

另外,拥有const 成员通常不是一个好主意。

【讨论】:

  • 因此,malloc 为对象或对象数组分配没有迂腐的方式。对吗?
  • @SomeName 这不是关于malloc,而是关于指针算法。因为每个struct可以有不同的大小,所以数组不能正常索引。
  • 同意,因为指针运算仅在同一数组的元素上定义良好,谢谢。
【解决方案2】:

大多数实现可以配置为支持的“流行扩展”(如果它们不总是这样做)是允许将结构类型的地址转换为共享公共初始序列的另一个地址,并且用于访问该序列的成员,直到通过转换后的指针以外的方式访问该结构,或者执行进入将发生这种情况的函数或循环。

在支持该扩展的实现上,可以通过声明一个结构来实现请求的语义,该结构的布局与具有灵活数组成员的结构的布局相匹配。例如:

struct POINT { int x, y; };
struct POLYGON { int sides; struct POINT coords[]; };
struct TRIANGLE { int sides; struct POINT coords[3]; };

void draw_polygon(struct POLYGON const *p);
void test(void)
{
  struct TRIANGLE my_triangle = {3, {{1,2}, {3,4], {5,6}};
  draw_polygon((struct POLYGON*)&my_triangle);
}

即使启用了基于类型的别名,某些编译器(如 icc 和 MSVC)已经足够复杂,可以支持此扩展。其他如 gcc 和 clang 只能通过使用 -fno-strict-aliasing 选项来支持此扩展。

尽管使用此扩展的代码并非严格符合标准,但标准委员会在已发布的基本原理中表示,他们不想让该语言仅用于编写可移植程序。相反,他们期望质量实现将支持各种“流行的扩展”,方法是处理一些对他们的客户有用的结构,即使标准允许他们这样做。自 1974 年以来,在结构类型之间转换指针的能力一直是该标准所描述的语言的基本部分,几乎所有实现都可以配置为支持它。因此,与依赖非标准句法扩展来实现类似语义的代码相比,上述代码应该被认为更具可移植性。

【讨论】:

  • 我会争论禁用严格别名以访问不兼容结构类型的常见初始序列。将这两种类型都作为联合成员并从union 声明可见的任何地方访问它们的公共初始序列是一种符合要求的方式,不是吗?
  • @SomeName:在 gcc/clang 解释下,不支持获取联合成员地址并使用结果指针的操作 *即使在获取地址之间没有任何其他访问存储的情况下并使用指针。即使像*(someUnion.memberOfArrayType+index) 这样的东西也可能会失败。该标准并未禁止此类行为,因为作者预计编译器编写者会将规则的应用限制在涉及看似不相关对象的别名的情况。更一般地说,该标准故意允许...
  • ...专门用于狭窄目的的实现以有利于这些目的的方式运行,即使这会使它们不适合许多其他目的。因此,它不会尝试判断哪些行为可能会使实现不适合几乎任何目的。
猜你喜欢
  • 2020-04-13
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 1970-01-01
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多