C 如何使用灵活的数组成员初始化结构

C 如何使用灵活的数组成员初始化结构,c,struct,flexible-array-member,C,Struct,Flexible Array Member,我有以下结构 typedef struct _person { int age; char sex; char name[]; }person; 我已经做了一些基本的internet搜索(但没有成功),了解如何在不使用malloc()的情况下创建实例并使用灵活的数组成员初始化结构 例如:对于正常结构,如 struct a { int age; int sex; }; 我们可以创建一个struct a的实例,并像 struct a p1 = {10,

我有以下结构

typedef struct _person {
    int age;
    char sex;
    char name[];
}person;
我已经做了一些基本的internet搜索(但没有成功),了解如何在不使用
malloc()
的情况下创建实例并使用灵活的数组成员初始化结构

例如:对于正常结构,如

struct a {
    int age; 
    int sex;
};
我们可以创建一个
struct a
的实例,并像

struct a p1 = {10, 'm'};
但是对于具有灵活数组的结构(如上面提到的
\u person
),我们如何创建实例并进行初始化,就像我们对普通
结构所做的那样

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

(或)


使用灵活数组创建结构的唯一方法是使用C99规范中提到的
malloc()
6.7.2.1结构和联合说明符-point#17
,这是真的吗

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

person p = { 10, 'x' };

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

否,灵活数组必须始终手动分配。但是您可以使用
calloc
来初始化柔性部件,使用复合文字来初始化固定部件。我将其包装在分配
内联
函数中,如下所示:

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
的返回。到目前为止,在我编程的所有系统中,这将相对很好地中止。总的来说,我是这样认为的,但在这个问题上意见分歧很大

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

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

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

有一些技巧可以使用。这取决于您的具体应用

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

   struct  {
        int age;
        char sex;
        char name[sizeof("THE_NAME")];
    } your_variable = { 55, 'M', "THE_NAME" };
问题是您必须使用指针转换将变量解释为“person”(例如“*(person*)(&your_变量)”。但您可以使用包含联合来避免这种情况:

union {
 struct { ..., char name[sizeof("THE_NAME")]; } x;
 person p;
} your_var = { 55, 'M', "THE_NAME" };
因此,您的_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");

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

更安全。您不能,结构必须具有编译时大小。@Anycorn:具有灵活数组成员的结构确实具有编译时大小。GCC有一个扩展,允许您执行类似于
struct{size\u t len;int data[];}x={4,{1,2,3,4}可以工作,但不可移植。您可以随时查看平台版本的
alloca
,以寻找一个可能更具可移植性的解决方案,但您必须确保它们的行为方式相同,并且具有相同的实现怪癖。@Charles不是海报的意思-一定有某种malloc或等效的解决方案。默认情况下,它似乎处于启用状态(假设您没有使用
-ansi
或任何会禁用它的东西)。如果您正在使用相同的语法,则有一个GCC扩展允许您使用该语法指定灵活数组成员。对
calloc()
的结果进行未经检查的复制是危险的;如果分配失败,您将获得一个核心转储(或其他不太可能是您想要的未定义行为)。@JonathanLeffler(右)将修改。我将把它集成到函数中。对于marcro,我将只参考我关于检查
malloc
返回的一般性咆哮。您的内联函数解决方案非常棒。宏没有任何优势。它不可读,不检查calloc返回值,并且不会执行得更好。宏一般不比内联函数(有时更糟——考虑传递STARLY()到宏,它对它进行两次评估)。有更多的优化允许与他们,请参阅我的编辑。我们可能会尝试将这两种方法结合起来,使用一个函数来执行类似于
calloc\u和\u copy\u或\u fail
.Nice的操作。顺便说一句,而不是
person*ret=calloc(sizeof(person)+n,1)
,一个更安全的习惯是写
person*ret=calloc(sizeof(*ret)+(sizeof(ret->name[0])*n),1)