C 语言结构体填充

17 Mar 2025 | 6 分钟阅读

结构体内存对齐(Structure padding)是 C 语言中的一个概念,它通过在内存地址之间添加一个或多个空字节来对齐内存中的数据。

让我们先通过一个简单的场景来理解 C 语言中的结构体内存对齐。

假设我们创建了一个用户自定义的结构体。当我们创建该结构体的对象时,会为结构体成员分配连续的内存。

在上面的示例中,我们创建了一个名为 student 的结构体。我们声明了该结构体的对象名为 'stud1'。对象创建后,将为它的结构体成员分配连续的内存块。首先,内存将分配给 'a' 变量,然后是 'b' 变量,最后是 'c' 变量。

struct student 的大小是多少?

现在,我们计算 struct student 的大小。我们假设 int 的大小是 4 字节,char 的大小是 1 字节。

在上面的情况下,当我们计算 struct student 的大小时,得到的大小是 6 字节。但这个答案是错误的。现在,我们将理解为什么这个答案是错误的?我们需要理解结构体内存对齐的概念。

结构体内存对齐

处理器不是一次读取 1 个字节。它一次读取 1 个字(word)。

1 个字是什么意思?

如果我们有一个 32 位处理器,那么处理器一次读取 4 个字节,这意味着 1 个字等于 4 个字节。

如果我们有一个 64 位处理器,那么处理器一次读取 8 个字节,这意味着 1 个字等于 8 个字节。

因此,我们可以说 32 位处理器一次可以访问 4 个字节,而 64 位处理器一次可以访问 8 个字节。这取决于体系结构,字的大小是多少。

为什么需要结构体内存对齐?

如果我们有一个 32 位处理器(一次 4 个字节),那么上面结构的内存图示如下:

Structure Padding in C

正如我们所知,结构体占据连续的内存块,如上图所示,即 char a 为 1 字节,char b 为 1 字节,int c 为 4 字节。那么在这种情况下会遇到什么问题?

有什么问题?

我们可以一次访问 4 个字节(因为我们正在考虑 32 位架构)。问题在于,在一个 CPU 周期内,可以访问 char a 的一个字节、char b 的一个字节以及 int c 的 2 个字节。我们在访问 char achar b 时不会遇到任何问题,因为这两个变量都可以在一个 CPU 周期内访问,但当我们访问 int c 变量时就会遇到问题,因为访问 'c' 变量的值需要 2 个 CPU 周期。在第一个 CPU 周期中,访问前两个字节,在第二个周期中,访问另外两个字节。

假设我们不想访问 'a' 和 'b' 变量,只想访问变量 'c',这需要两个周期。变量 'c' 是 4 个字节,所以它也可以在一个周期内访问,但在这种情况下,它占用了 2 个周期。这是 CPU 周期不必要的浪费。由于这个原因,引入了结构体内存对齐的概念来节省 CPU 周期。结构体内存对齐是由编译器自动完成的。现在,我们将看到如何进行结构体内存对齐。

如何进行结构体内存对齐?

Structure Padding in C

为了实现结构体内存对齐,在左侧创建了一个空行(如上图所示),并且 'c' 变量左侧占用的两个字节被移到了右侧。这样,'c' 变量的所有四个字节都位于右侧。现在,'c' 变量可以在单个 CPU 周期内访问。结构体内存对齐后,结构体占用的总内存为 8 字节(1 字节 + 1 字节 + 2 字节 + 4 字节),这比之前要大。虽然在这种情况下内存被浪费了,但可以在单个周期内访问变量。

我们来创建一个简单的结构体程序。

在上面的代码中,我们创建了一个名为 'student' 的结构体。在 main() 方法内部,我们声明了一个 student 类型的变量,即 stud1,然后我们使用 sizeof() 运算符计算 student 的大小。由于我们已经讨论过的结构体内存对齐的概念,输出将是 8 字节

输出

Structure Padding in C

更改变量顺序

现在,我们将看到更改变量顺序会如何影响程序的输出。让我们考虑相同的程序。

上面的代码与之前的代码类似,我们改变的唯一内容是 structure student 内部变量的顺序。由于顺序的改变,两种情况下的输出将不同。在前一种情况下,输出是 8 字节,但在这种情况下,输出是 12 字节,正如我们在下面的屏幕截图中观察到的。

输出

Structure Padding in C

现在,我们需要理解“**为什么在这种情况下输出会不同**”。

  • 首先,内存分配给 char a 变量,即 1 字节。
    Structure Padding in C
  • 现在,内存将分配给 int b。由于 int 变量占用 4 个字节,但左侧只有 3 个字节可用。将在这些 3 个字节上创建一个空行,int 变量将占用另外 4 个字节,以便 int 变量可以在单个 CPU 周期内访问。
    Structure Padding in C
  • 现在,内存将分配给 char c。CPU 一次可以访问 1 个字,等于 4 个字节,所以 CPU 将使用 4 个字节来访问 'c' 变量。因此,所需的总内存是 12 字节(4 字节 + 4 字节 + 4 字节),即 4 字节用于访问 char a 变量,4 字节用于访问 int b 变量,以及另外 4 字节用于访问 'c' 变量的单个字符。
    Structure Padding in C

如何在 C 语言中避免结构体内存对齐?

结构体内存对齐是一个内置过程,由编译器自动完成。有时需要避免 C 语言中的结构体内存对齐,因为它会使结构体的大小大于结构体成员的大小。

我们可以在 C 语言中通过两种方式避免结构体内存对齐:

  • 使用 #pragma pack(1) 指令
  • 使用属性

使用 #pragma pack(1) 指令

在上面的代码中,我们使用了 #pragma pack(1) 指令来避免结构体内存对齐。如果我们不使用此指令,那么该程序的输出将是 16 字节。但结构体成员的实际大小是 13 字节,因此浪费了 3 字节。为了避免内存浪费,我们使用 #pragma pack(1) 指令提供 1 字节的打包。

输出

Structure Padding in C
  • 通过使用属性

输出

Structure Padding in C
下一主题C Union