2

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

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 

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

4

2 回答 2

3

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

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

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

于 2019-04-03T19:17:34.643 回答
1

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

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

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 年以来,在结构类型之间转换指针的能力一直是该标准所描述的语言的基本部分,几乎所有实现都可以配置为支持它。因此,像上面这样的代码应该被认为比依赖非标准语法扩展来实现类似语义的代码更具可移植性。

于 2019-04-05T20:14:11.633 回答