结构中成员的顺序是否重要?

Mil*_*dek 74 c struct

我在C中发现了一种奇特的行为.请考虑以下代码:

 struct s {
     int a;
 };      

 struct z {
     int a;
     struct s b[];
 };  

 int main(void) {
     return 0;
 }   
Run Code Online (Sandbox Code Playgroud)

它编译得很好.然后z像这样改变struct成员的顺序

struct z {
    struct s b[];
    int a; 
};  
Run Code Online (Sandbox Code Playgroud)

突然间,我们得到了编译错误field has incomplete type 'struct s []'.

这是为什么?

das*_*ght 88

a struct中字段的顺序很重要 - 编译器不允许对字段重新排序,因此struct可能会因添加一些填充而改变.

但是,在这种情况下,您要定义一个所谓的弹性成员,一个可以更改大小的数组.灵活成员的规则就是这样

  • 可能永远不会有多个这样的会员,
  • 如果存在,柔性构件必须是struct,和中的最后一个
  • struct必须除了灵活的至少一个成员.

请参阅此问答,了解使用柔性结构构件的小图.

  • 看起来像一个非常差的诊断,然后(虽然技术上准确). (5认同)
  • @LightnessRacesinOrbit你是对的,我也不是特别喜欢错误信息. (5认同)

Bre*_*dan 21

编译器无法计算struct s b[];将消耗多少内存.这意味着如果结构后面有任何字段,编译器就无法确定这些字段的位置.

过去(在旧版本的C中)(例如)struct s b[];不允许作为结构的成员.这使得高效的内存管理变得烦人.举一个简单的例子,假设你有一个包含"name"字符串的结构(可能只是几个字符或很多字符).您可以使用固定大小的数组,该数组足以容纳最大的名称(浪费空间),或者使用指针并分配2个内存(一个用于结构,一个用于可变长度名称字符串).或者,您可以使用指针并使其指向结构末尾之外的额外空间,最终会出现如下情况:

    length = strlen(my_string);
    foo = malloc(sizeof(MYSTRUCTURE) + length + 1);
    foo->name = (void *)foo + sizeof(MYSTRUCTURE);   // Set pointer to extra bytes past end of structure
    memcpy(foo->name, my_string, length + 1);
Run Code Online (Sandbox Code Playgroud)

这是最有效的选择; 但它也很丑陋且容易出错.

为了解决这个问题,编译器添加了非标准扩展,以允许在结构的末尾使用"未知大小的数组".这使程序员更容易一些,并使其更高效(因为不需要额外的指针成员).这最终被C标准采用(也许在C99 - 我不记得了).

  • 有趣的是,在灵活的阵列成员作为标准提供之前,我记得使用一些编译器(无论是设计还是偶然)会让代码声明一个大小为零的数组作为结构的一部分,可能是因为省略了零数组大小检查会导致默认情况下允许这样的数组.使用空`[]`在语义上有点不同,因为编译器将允许直接实例化包含零大小数组的结构(尽管使用该数组将是UB),而FLA或包含一个的结构必须是结构的最后一个元素. (2认同)

Mar*_* A. 15

成员的顺序通常很重要(即某些填充可能插入字段之间),但在您的特定情况下,您使用的是灵活的成员数组,这在C99中是标准化的 - 6.7.2.1.16

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型; 这被称为灵活的阵列成员.在大多数情况下,将忽略灵活数组成员.特别地,结构的尺寸好像省略了柔性阵列构件,除了它可以具有比省略意味着更多的拖尾填充.

您的struct s b[];成员旨在用于访问多个struct s元素的动态堆分配.


bar*_*nos 5

你的问题的标题是“成员的顺序有struct问题吗?”。

您代码中的明显问题与您struct包含一个灵活成员的事实有关。

因此,这里有一个与 a 中成员顺序的一般问题相关的附加问题struct


以下面两种结构为例:

struct s1
{
    int a;
    short b;
    char c;
};

struct s2
{
    char c;
    short b;
    int a;
};
Run Code Online (Sandbox Code Playgroud)

大多数编译器会添加填充,以便将每个成员与可被其大小整除的地址对齐。

所以struct s2可能最终编译成:

struct s2
{
    char c;
    char pad1;
    short b;
    short pad2;
    short pad3;
    int a;
};
Run Code Online (Sandbox Code Playgroud)

这最终将导致类型struct s1struct s2.