Cprogramming 简明教程

Structure Padding and Packing in C

What is Structure Padding in C?

C 中的 Structure 填充是由 CPU architecture 处理的过程。结构填充在结构内添加一定数量的空字节,以便数据成员在内存中自然对齐。对齐要求由处理器架构而不是语言本身决定。自然,对齐要求会根据数据总线大小或特定 CPU 架构的其他架构考虑因素而更改。

Understanding Structure Padding with Examples

我们先定义结构类型如下 −

struct struct1 {
   char x;
   int y;
   char z;
};

Example 1

让我们检查此类型变量所需的字节数 −

#include <stdio.h>

struct struct1{
   char a;
   char b;
   int c;
};

int main(){

   printf("Size: %d", sizeof(struct struct1));
   return 0;
}

当你运行这段代码时,它将产生以下输出:

Size: 8

结果与预期相反。

考虑到 char 类型需要 1 个字节,而 int 类型需要 4 个字节,人们可能会认为输出应该是“1 + 1 + 4 = 6 个字节”。

structure padding 1

然而,CPU 架构有必要更改此结构。考虑到我们正在使用具有 32 位处理器的 CPU,它一次读取 4 个字节,这意味着 1 个字等于 4 个字节。

在一次 CPU 周期中,它访问字符“a”,然后字符“b”和整数“c”的前两个字节。在第二个周期中,访问另外两个字节。

即使我们只想读取“c”,也需要两个 CPU 周期。为此,CPU 在存储“c”值所在字节之前添加两个空字节。此机制称为 padding

structure padding 2

这解释了我们在上面获得的结果,即结构类型的尺寸为 8 个字节。

Example 2

让我们更改上述结构类型的成员顺序,并将“ b ”的类型和“ c ”的类型设置如下。

#include <stdio.h>

struct struct1{
   char a;
   int b;
   char c;
};

int main(){

   printf("size: %d", sizeof(struct struct1));
   return 0;
}

运行代码并检查其输出:

size: 12

在前一个字的 4 个字节中,第一个字节分配给字符“a”,后跟三个空字节。

形成下一个字的下一个 4 个字节用于存储整数“b”。随后,在下一组 4 个字节中,仅一个字节用于“c”。然而,结构大小为 12。

What is Structure Packing in C?

另一方面,结构打包是一种最小化填充影响的机制,从而试图减少浪费的内存空间。我们可以使用某些编译指示和属性来实现打包。

Understanding Structure Packing with Examples

由 CPU 架构强制的填充是不可避免的,但是有办法最小化填充。可以使用以下方法来完成 −

  1. Using #pragma pack(1) directive

  2. Using packed attribute

Using

#pragma pack(1) 预处理器指令强制编译器在内存分配过程中忽略填充,并将结构成员端对端对齐。

让我们在之前使用的代码顶部添加此指令,并查看结果 −

#include <stdio.h>
#pragma pack(1)

struct struct1{
   char a;
   int b;
   char c;
};

int main(){

   printf("size: %d", sizeof(struct struct1));
   return 0;
}

运行代码并检查其输出:

size: 6

我们可以看到,避免了结构填充并减少了内存浪费。

Using _attribute_packed

通过 GCC,我们可以使用属性指定结构和联合类型的各种特殊属性。属性包括: aligned, deprecated, packed, transparent_union, unusedvisibility 。应用这些属性的语法是“ attribute …​ ”。

在此,我们将在结构类型的定义中使用 packed 属性。

#include <stdio.h>

struct __attribute__((packed)) struct1{
   char a;
   int b;
   char c;
};

int main(){

   printf("size: %d", sizeof(struct struct1));
   return 0;
}

运行代码并检查其输出:

size: 6

此方法还避免了填充的影响。