C语言中嵌套结构体的内存布局

8

我理解C语言中结构体的概念,也知道结构体在内存中的存储方式。因此,我创建了以下内容:

  struct tag1 {
      char a;
      int b;
  }var1;

在gcc中,该结构的大小为8字节。

内存布局示例。

 -----------
|  |  |  |  | ---> char a (only 1 byte is used and remaining 3 bytes are padding)
 -----------
|  |  |  |  | ---> int b (4 byte is used)
 -----------

现在我对这个示例程序有些疑问。嵌套结构体是如何存储在内存中的。

  struct tag1{
      int a;
      struct tag2{
          long b;
      }var1;
  }var2;

3
您可以在测试程序中使用 offsetof() 宏来查看编译器如何布局不同的结构体。需要注意的是,具体的细节技术上取决于实现。例如,根据 long 是否为 64 位类型或 32 位类型,您可能会看到不同之处。 - Michael Burr
1
var1只是tag1结构体内的另一个成员,就像相同结构体内的a一样。var1是一个结构体并不重要。因此,如果您想为嵌套结构的第二个示例执行“内存布局示例”,请将嵌套结构放在var1所在的位置,就像您在第一个示例中放置了b一样,区别在于您有long var1.b而不是b - Some programmer dude
2
只需递归地应用规则即可。 - too honest for this site
谢谢您先生。在gcc中,该结构的大小为16字节。请解释一下。 - sakthi
1
结构体的大小包括每个成员的大小和对齐填充。正如其他评论所暗示的那样,填充可以受到编译器设置的影响。(使用#pragma pack宏) - ryyker
3个回答

6
正如评论所述,结构体在内存中的确切布局是与实现相关的。通常情况下,布局受成员类型、内存寻址(32位或64位)以及pragma pack调用的影响,这些调用会调整沿内存边界的对齐方式。
以下是一个示例,当我将两个原始结构体实例作为子结构体成员放置到另一个结构体中(为了上下文和强制连续内存分配),然后为该结构体的一个实例分配值以提供可区分的(非零)内存布局内容时,我在我的系统上看到的情况:(32位寻址,没有pragma语句)
typedef struct tag1{
      int a;
      struct tag2{
          long b;
      }var1;
}var2;

typedef struct {
    var2 s1;
    var2 s2;
}S;

在主函数中:

 S x = {0};

  x.s1.a = 1;
  x.s1.var1.b = 2;
  x.s2.a = 3;
  x.s2.var1.b = 4;

图片第一行中所示的struct S的内存:

enter image description here

请注意,直接使用结构体或子结构体构造不需要额外的内存使用。但是,间接地,由于定义结构体中包含的数据类型大小不同且不能在寻址边界上自然对齐,会更多的内存用于对齐填充(往往会有)。当出现这种情况时,填充将影响计算structsizeof

您可以看到,原始结构的每个实例的大小为8(两个实例为16),因为此实例中的intlong完美对齐(并且大小相同),因此似乎没有填充。如果将long替换为long long,情况将不再如此。几乎肯定需要填充。

为了提供下面信息的上下文,我的机器(编译为32位)上相关数据的大小如下:

int sizeChar = sizeof(char);     //1 (always)
int sizeInt  = sizeof(int);      //4
int sizeLong = sizeof(long);     //4
int sizeLL   = sizeof(long long);//8

使用更广泛的数据类型,填充和Pragma 的效果将会有更多明显的证据。以下结构包含4种数据类型:

 typedef struct tag1 {
      char a;
      int b;
      long c;
      long long d;
  }var1;

使用这个结构定义,有趣的是使用pragma pack调用引起的填充差异。在我的机器上,编译为32位内存映射,在这里我看到:

enter image description here

顺便说一下,在这个主题中有一个相关的讨论here


2

嵌套结构在内存中是如何存储的?

struct tag1{
      int a;
      struct tag2{
          long b;
      }var1;
  }var2;

首先,您需要检查机器的字长(即32位 / 64位),以及安装了哪个gcc编译器。根据体系结构,变量的内存分配将会有所不同。 sizeof 运算符将根据系统特定返回变量大小。
例如(带填充)存储在内存中的变量
在32位机器上,int和long都分配4字节。 | var2.var1.b | ==> 0x601058 | var2.a | ==> 0x601054
在64位机器上,int分配4字节,long分配8字节。 | var2.var1.b | ==> 0x601058 | var2.a | ==> 0x601050
结构成员的内存对齐方式在这里解释: C中的对齐

0
为了弄清楚嵌套结构在内存中的存储方式,您可以运行以下代码:
#include <stdio.h>
#include <limits.h>
#include <stddef.h>

struct tag1{
    int a;
    struct tag2{
        long b;
    } var1;
} var2;

int main(void){
    printf("CHAR_BIT is\t\t\t\t%d\n", CHAR_BIT);
    puts("");
    printf("sizeof var2 is\t\t\t\t%zu\n", sizeof var2);
    printf("sizeof var2.var1 is\t\t\t%zu\n", sizeof var2.var1);
    printf("sizeof var2.var1.b is\t\t\t%zu\n", sizeof var2.var1.b);
    puts("");
    printf("offsetof(struct tag1, a) is\t\t%zu\n", offsetof(struct tag1, a));
    printf("offsetof(struct tag1, var1) is\t\t%zu\n", offsetof(struct tag1, var1));
    printf("offsetof(struct tag1, var1.b) is\t%zu\n", offsetof(struct tag1, var1.b));
    printf("offsetof(struct tag2, b) is\t\t%zu\n", offsetof(struct tag2, b));
    return 0;
}

我要求你在你的实现上运行这段代码,而不是直接给你一个答案,因为:
  1. sizeof T 是实现定义的。如果你有 sizeof (long) == 1CHAR_BIT == 32 呢?
  2. 结构体中可能会有填充位/字节。
  3. 其他因素。例如,使用 #pragma pack(n)

在我的实现(OS X 下的 clang)上,输出结果为:

CHAR_BIT is             8

sizeof var2 is              16
sizeof var2.var1 is         8
sizeof var2.var1.b is           8

offsetof(struct tag1, a) is     0
offsetof(struct tag1, var1) is      8
offsetof(struct tag1, var1.b) is    8
offsetof(struct tag2, b) is     0

嗯,在这里看起来不整齐,但在我的屏幕上显示时格式很好。

因此(对于我的实现),struct tag1 的内存布局将如下所示:

 -----------------------
|  |  |  |  |  |  |  |  | ---> int a (8 bytes are used)
 -----------------------
|  |  |  |  |  |  |  |  | ---> struct tag2 / long b (8 bytes are used)
 -----------------------

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接