【问题标题】:Is returning a struct with flexible array member allowed?是否允许返回具有灵活数组成员的结构?
【发布时间】:2020-02-10 17:00:55
【问题描述】:

GCC 编译函数返回一个具有灵活数组成员的结构。该标准在 6.7.2.1 中给出了如何处理此类结构的定义:

在大多数情况下,灵活数组成员被忽略。在 特别是,结构的大小就像灵活的数组 成员被省略,除了它可能有更多的尾随填充比 遗漏意味着。

由于具有灵活数组成员的结构的大小已知,因此根据 6.2.5 中给出的完整性定义,类型是完整的:

在翻译单元内的不同点,对象类型可能是 不完整(缺乏足够的信息来确定 该类型的对象)或完整的(具有足够的信息)。 37)

还有,6.5.2.2

表示被调用函数的表达式 96) 应具有类型 指向返回 void 或返回完整对象类型的函数的指针 不是数组类型。

所以使用灵活的数组成员返回structs 应该是合法的。


如何修复下面的示例以使其正常工作(我需要具有灵活数组成员的堆栈分配结构):

#include <stdio.h>

struct test{
    size_t sz;
    char data[];
};

struct test get_test(void){
    int sz = 5;
    char data[5] = "test";
    struct test test = {.sz = 5};
    //How to copy char data[5] into the struct test test?
    return test;
}

int main(void){
    struct test tst = get_test();
    printf("%s\n", tst.data);
}

【问题讨论】:

  • 可以创建这样的对象,但不能访问灵活的数组元素。
  • 如果你想访问灵活的成员,不要使用自动变量。来自mallocif availble P99_FMALLOC 家族。
  • 您说:我需要具有灵活数组成员的堆栈分配结构。 标准说您可以创建这样的结构,但分配的大小不会包括灵活的任何空间数组成员。因此,这种结构很少有用。您可以处理指向此类结构的指针——它们需要动态分配(使用malloc() 等)才能有用(非空的灵活数组成员)。编译器可以免费提供扩展——GCC 可以。但是“语言律师”标签意味着您不关心此类扩展。
  • @JonathanLeffler 通过使用 GCC 的扩展如何做到这一点?我的意思是堆栈分配的具有初始化灵活数组成员的结构。
  • GCC 文档的后半部分关于 C 扩展和 Arrays of Length Zero。目前还不清楚您是否可以返回这样的值。如果它是静态分配的,你可以返回一个指向它的指针。

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


【解决方案1】:

如何修复下面的示例以使其正常工作(我需要堆栈 分配具有灵活数组成员的结构):

灵活的数组成员允许对结构和 FAM 进行单一动态分配。 (你不能有一个堆栈分配结构和动态FAM——如果你想这样做,不要使用FAM,而是使用data的指针。)要分配和使用FAM,你需要改变你的函数返回一个指向struct test类型的指针(例如struct test*),然后为get_test中的"test"声明和分配存储空间,并返回一个指向分配块的指针,例如

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct test {
    size_t sz;
    char data[];
};

struct test *get_test (void)    /* declare as type struct test* */
{
    size_t sz = 5;
    char data[] = "test";

    /* allocate storage for struct + FAM */
    struct test *test = malloc (sizeof *test + sizeof data);
    if (!test)
        return NULL;

    test->sz = sz;                              /* assign sz */
    memcpy (test->data, data, sizeof data);     /* copy data */

    return test;
}

int main (void) {

    struct test *tst = get_test();

    printf ("test->sz   : %zu\ntest->data : %s\n", tst->sz, tst->data);

    free (tst);     /* don't forget to free what you allocate */
}

使用/输出示例

$ ./bin/fam
test->sz   : 5
test->data : test

【讨论】:

    【解决方案2】:

    是的,返回这样的值是有效的 C,但不会复制任何数组元素。自动变量的行为就好像它确实分配了一个长度为 1 的数组(因为 0 的数组长度在标准 C 中是无效的),但是对 .data[0] 的访问将具有 UB - 实际的 size的对象可能包括 .data[0] 甚至更多的连续元素 - 或者不包括。(C11 6.7.2.1p18)。

    不可能在标准C中定义一个在灵活数组成员中包含任何内容的自动变量(扩展可能并且确实存在)!

    赋值也是有效的(因此也会返回),但在赋值之后,灵活的数组成员将再次包含不确定的值 (C11 6.7.2.1p25)。

    【讨论】:

    • 说“自动变量的行为就像分配了一个长度为 1 的数组一样”是有问题的。 C 标准在 C 2018 6.7.2.1 18 中有一些与此类似的措辞,但它正在讨论使用 .-&gt; 运算符引用灵活数组成员,而不是整个结构的行为或使用自动对象。例如,自动对象的大小不会像数组长度为 1。
    • (另外,从技术上讲,我不相信“在标准 C 中定义一个在灵活数组成员中具有任何内容的自动变量是不可能的”。灵活数组成员的行为如下如果替换为适合对象的最大可能数组。如果对象必须具有填充,则可能会为元素提供空间。例如,使用struct { double x; char y; char z[]; } t;,那么在许多 C 实现中,t.z 的行为就好像它有至少 7 个元素。我不建议利用这一点。)
    • @EricPostpischil 该标准明确表示元素是不确定的并且访问未定义,所以我不会对此进行太多争论。
    猜你喜欢
    • 1970-01-01
    • 2020-04-13
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多