繁体   English   中英

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

[英]Does the order of members in a struct matter?

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

 struct s {
     int a;
 };      

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

 int main(void) {
     return 0;
 }   

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

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

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

这是为什么?

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

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

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

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

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

过去曾经(在旧版本的C中)(例如) struct sb[]; 不允许作为结构的成员。 这使得高效的内存管理变得烦人。 举一个简单的例子,假设你有一个包含“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);

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

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

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

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

你的struct sb[]; member用于访问多个struct s元素的动态堆分配。

你的问题的标题是“ struct中成员的顺序是否重要?”。

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

所以这是一个与struct中成员顺序的一般问题相关的另一个问题:


以下面两个结构为例:

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

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

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

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

struct s2
{
    char c;
    char pad1;
    short b;
    short pad2;
    short pad3;
    int a;
};

这最终会导致struct s1struct s2类型实例的大小不同。

在这种情况下,订单很重要。 struct z包含一个由structs s组成的数组。 但是,此数组没有与之关联的大小,因此编译器不知道如何分配适当的堆栈空间,因为之后还有另一个struct( int a )字段。 它将如何工作的一个例子:

struct s {
  int a;
}

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

int main(void) {
  return 0;
}

如果你真的需要数组来改变大小,最好是在堆上分配整个结构,并将数组作为指向struct s指针,然后动态地重新分配它以适应不断变化的数组大小。 查找malloc (3)realloc 3)calloc (3)free (3)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM