如何统一分配具有灵活数组成员的结构体数组?

How to allocate an array of structs with flexible array members comformingly?

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

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

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

the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

我们知道 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 不能成为数组成员,正如您给出的引号所规定的那样。

最好的处理方法是将灵活的数组成员更改为指针并为其单独分配space。

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 个结构成员通常不是一个好主意。

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

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

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 选项来支持此扩展。

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