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 个字节),那么上面结构的内存图示如下: ![]() 正如我们所知,结构体占据连续的内存块,如上图所示,即 char a 为 1 字节,char b 为 1 字节,int c 为 4 字节。那么在这种情况下会遇到什么问题? 有什么问题?我们可以一次访问 4 个字节(因为我们正在考虑 32 位架构)。问题在于,在一个 CPU 周期内,可以访问 char a 的一个字节、char b 的一个字节以及 int c 的 2 个字节。我们在访问 char a 和 char b 时不会遇到任何问题,因为这两个变量都可以在一个 CPU 周期内访问,但当我们访问 int c 变量时就会遇到问题,因为访问 'c' 变量的值需要 2 个 CPU 周期。在第一个 CPU 周期中,访问前两个字节,在第二个周期中,访问另外两个字节。 假设我们不想访问 'a' 和 'b' 变量,只想访问变量 'c',这需要两个周期。变量 'c' 是 4 个字节,所以它也可以在一个周期内访问,但在这种情况下,它占用了 2 个周期。这是 CPU 周期不必要的浪费。由于这个原因,引入了结构体内存对齐的概念来节省 CPU 周期。结构体内存对齐是由编译器自动完成的。现在,我们将看到如何进行结构体内存对齐。 如何进行结构体内存对齐?![]() 为了实现结构体内存对齐,在左侧创建了一个空行(如上图所示),并且 'c' 变量左侧占用的两个字节被移到了右侧。这样,'c' 变量的所有四个字节都位于右侧。现在,'c' 变量可以在单个 CPU 周期内访问。结构体内存对齐后,结构体占用的总内存为 8 字节(1 字节 + 1 字节 + 2 字节 + 4 字节),这比之前要大。虽然在这种情况下内存被浪费了,但可以在单个周期内访问变量。 我们来创建一个简单的结构体程序。 在上面的代码中,我们创建了一个名为 'student' 的结构体。在 main() 方法内部,我们声明了一个 student 类型的变量,即 stud1,然后我们使用 sizeof() 运算符计算 student 的大小。由于我们已经讨论过的结构体内存对齐的概念,输出将是 8 字节。 输出 ![]() 更改变量顺序现在,我们将看到更改变量顺序会如何影响程序的输出。让我们考虑相同的程序。 上面的代码与之前的代码类似,我们改变的唯一内容是 structure student 内部变量的顺序。由于顺序的改变,两种情况下的输出将不同。在前一种情况下,输出是 8 字节,但在这种情况下,输出是 12 字节,正如我们在下面的屏幕截图中观察到的。 输出 ![]() 现在,我们需要理解“**为什么在这种情况下输出会不同**”。
如何在 C 语言中避免结构体内存对齐?结构体内存对齐是一个内置过程,由编译器自动完成。有时需要避免 C 语言中的结构体内存对齐,因为它会使结构体的大小大于结构体成员的大小。 我们可以在 C 语言中通过两种方式避免结构体内存对齐:
使用 #pragma pack(1) 指令 在上面的代码中,我们使用了 #pragma pack(1) 指令来避免结构体内存对齐。如果我们不使用此指令,那么该程序的输出将是 16 字节。但结构体成员的实际大小是 13 字节,因此浪费了 3 字节。为了避免内存浪费,我们使用 #pragma pack(1) 指令提供 1 字节的打包。 输出 ![]()
输出 ![]() 下一主题C Union |
我们请求您订阅我们的新闻通讯以获取最新更新。