【问题标题】:How to initialize a structure with flexible array member如何使用灵活的数组成员初始化结构
【发布时间】:2011-12-31 10:45:55
【问题描述】:

我有以下结构

typedef struct _person {
    int age;
    char sex;
    char name[];
}person;

我已经完成了一些基本的互联网搜索(但未成功),了解如何在不使用 malloc() 的情况下创建实例并使用灵活的数组成员初始化结构。

例如:对于像

这样的普通结构
struct a {
    int age; 
    int sex;
};

我们可以创建一个struct a 的实例并像这样初始化它

struct a p1 = {10, 'm'};

但是对于其中包含灵活数组的结构(如上面提到的_person),我们如何创建实例并像普通structures 那样进行初始化?

有可能吗?如果是这样,我们如何在初始化过程中传递数组大小和要初始化的实际值?

(或)

创建具有灵活数组的结构的唯一方法是使用 C99 规范中提到的 malloc() - 6.7.2.1 Structure and union specifiers - point #17,这是真的吗?!

【问题讨论】:

  • 不能,struct 必须有编译时大小。
  • @Anycorn:具有灵活数组成员的结构确实具有编译时大小。
  • GCC 有一个扩展,允许你做类似struct { size_t len; int data[]; } x = { 4, { 1, 2, 3, 4 } }; 的事情,它会工作,但它不是可移植的。您可以随时查看您的平台版本的 alloca 以获得可能更便携的解决方案,但您必须确保它们都以相同的方式运行并具有相同的实现怪癖。
  • @Charles 不是海报的意思 - 必须有某种 malloc 或等价物。
  • gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html 默认情况下它似乎是打开的(假设你没有使用-ansi 或任何会禁用它的东西)。

标签: c struct flexible-array-member


【解决方案1】:

不,灵活数组必须始终手动分配。但是您可以使用calloc 来初始化灵活部分,并使用复合文字来初始化固定部分。我会将它包装在分配 inline 函数中,如下所示:

typedef struct person {
  unsigned age;
  char sex;
  size_t size;
  char name[];
} person;

inline
person* alloc_person(int a, char s, size_t n) {
  person * ret = calloc(sizeof(person) + n, 1);
  if (ret) memcpy(ret,
                  &(person const){ .age = a, .sex = s, .size = n},
                  sizeof(person));
  return ret;
}

请注意,这会将分配是否成功的检查留给调用者。

如果您不需要我在这里包含的size 字段,那么宏就足够了。只是在执行memcpy 之前无法检查calloc 的返回。在我到目前为止编程的所有系统下,这将相对较好地中止。一般来说,我认为return of malloc is of minor importance,但在这个问题上意见分歧很大。

这可能(在这种特殊情况下)为优化器提供更多机会将代码集成到环境中:

#define ALLOC_PERSON(A,  S,  N)                                 \
((person*)memcpy(calloc(sizeof(person) + (N), 1),               \
                 &(person const){ .age = (A), .sex = (S) },     \
                 sizeof(person)))

编辑:这可能比函数更好的情况是AS 是编译时常量。在这种情况下,复合文字,因为它是 const 限定的,可以静态分配,它的初始化可以在编译时完成。此外,如果代码中出现多个具有相同值的分配,则编译器将被允许仅实现该复合文字的一个副本。

【讨论】:

  • calloc() 的结果进行未经检查的复制是危险的;如果分配失败,您将获得核心转储(或其他不太可能是您想要的未定义行为)。
  • @JonathanLeffler,对,会修改。我将把它集成到函数中。对于宏,我将仅参考我关于检查 malloc 的返回的一般咆哮。
  • 您的内联函数解决方案很棒。恕我直言,宏没有任何优势。它不可读,不检查 calloc 返回值,并且性能不会更好。宏的性能通常不比内联函数好(有时更差 - 考虑将 strlen() 传递给宏,宏会对其进行两次评估)。
  • @ugoren, const 合格的复合文字在优化方面是特殊的。它们允许进行更多优化,请参阅我的编辑。可以尝试通过使用类似calloc_and_copy_or_fail 的函数来结合这两种方法。
  • 不错。 BTW,而不是person *ret = calloc(sizeof(person) + n, 1);,一个更安全的习惯是写person *ret = calloc(sizeof(*ret) + (sizeof(ret->name[0]) * n), 1);
【解决方案2】:

您可以使用一些技巧。这取决于您的特定应用程序。

如果要初始化单个变量,可以定义正确大小的结构体:

   struct  {
        int age;
        char sex;
        char name[sizeof("THE_NAME")];
    } your_variable = { 55, 'M', "THE_NAME" };

问题是您必须使用指针转换将变量解释为“person”(例如“*(person *)(&your_variable)”。但是您可以使用包含联合来避免这种情况:

union {
 struct { ..., char name[sizeof("THE_NAME")]; } x;
 person p;
} your_var = { 55, 'M', "THE_NAME" };

所以,your_var.p 的类型是“person”。 你也可以使用宏来定义你的初始化器,这样你就可以只写一次字符串:

#define INIVAR(x_, age_, sex_ ,s_) \
   union {\
     struct { ..., char name[sizeof(s_)]; } x;\
     person p;\
    } x_ = { (age_), (sex_), (s_) }

INIVAR(your_var, 55, 'M', "THE NAME");

另一个问题是这个技巧不适合创建“人”数组。数组的问题是所有元素必须具有相同的大小。在这种情况下,使用const char * 而不是char[] 会更安全。或者使用动态分配;)

【讨论】:

    【解决方案3】:

    具有灵活数组成员的结构类型可以被视为省略了灵活数组成员,因此您可以像这样初始化结构。

    person p = { 10, 'x' };
    

    但是,没有分配灵活数组的成员,任何访问灵活数组成员或形成指向超出其末尾的指针的任何尝试都是无效的。创建具有灵活数组成员的结构实例的唯一方法是动态为其分配内存,例如使用malloc

    【讨论】:

    • 有一个 GCC 扩展,可以让您使用相同的语法指定灵活的数组成员,如果您喜欢的话。
    猜你喜欢
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 1970-01-01
    • 2014-06-12
    • 1970-01-01
    相关资源
    最近更新 更多