📜  C中的结构体填充

📅  最后修改于: 2020-10-22 06:58:31             🧑  作者: Mango

C中的结构填充

结构填充是C语言中的一个概念,它在内存地址之间添加一个或多个空字节以对齐内存中的数据。

让我们首先通过一个简单的场景了解C中的结构填充,如下所示:

假设我们创建了一个用户定义的结构。当我们创建这种结构的对象时,连续内存将分配给该结构成员。

struct student
{
   char a;
   char b;
   int c;
} stud1;

在上面的示例中,我们创建了Student类型的结构。我们已将该结构的对象声明为“ stud1″。创建对象后,将连续的内存块分配给其结构成员。首先,将内存分配给“ a”变量,然后分配给“ b”变量,然后分配给“ c”变量。

结构学生的人数是多少?

现在,我们计算结构学生的大小。我们假设int的大小为4个字节,而char的大小为1个字节。

struct student
{
  char a; // 1 byte
  char b; // 1 byte
  int c; // 4 bytes 
}

在上述情况下,当我们计算结构学生的大小时,大小为6个字节。但是这个答案是错误的。现在,我们将理解为什么这个答案是错误的?我们需要了解结构填充的概念。

结构填充

处理器一次不读取1个字节。它一次读取1个字。

1个字是什么意思?

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

1 word = 4 bytes

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

1 word = 8 bytes

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

为什么要进行结构填充?

struct student
{
  char a; // 1 byte
  char b; // 1 byte
  int c; // 4 bytes 
}

如果我们有一个32位处理器(一次4个字节),则上述结构的内存的图形表示为:

如我们所知,结构占用了上图所示的连续内存块,即char a为1个字节,char b为1个字节,int c为4个字节,那么在这种情况下我们要面对什么问题。

有什么问题?

考虑使用32位架构时,一次可以访问4个字节。问题在于,在一个CPU周期中,可以访问char a的一个字节,char b的一个字节和int c的2个字节。访问char a和char b时不会遇到任何问题,因为两个变量都可以在一个CPU周期中访问,但是当我们访问int c变量时将面临问题,因为需要2个CPU周期才能访问value。 ‘c’变量。在第一个CPU周期中,访问前两个字节,在第二个周期中,访问其他两个字节。

假设我们不想访问’a’和’b’变量,我们只想访问变量’c’,这需要两个周期。变量“ c”为4个字节,因此也可以在一个周期内访问它,但是在这种情况下,它使用2个周期。这是不必要的CPU周期浪费。由于这个原因,引入了结构填充概念以节省CPU周期数。结构填充由编译器自动完成。现在,我们将看到如何完成结构填充。

结构填充如何完成?

为了实现结构填充,如上图所示,在左侧创建了一个空行,并将左侧’c’变量占用的两个字节向右移动。因此,’c’变量的所有四个字节都在右侧。现在,可以在单个CPU周期中访问’c’变量。在进行结构填充之后,结构占用的总内存为8个字节(1个字节+ 1个字节+2个字节+4个字节),大于前一个字节。尽管在这种情况下会浪费内存,但是可以在单个周期内访问变量。

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

#include 
struct student
{
   char a;
   char b;
   int c;
};
int main()
{
   struct student stud1; // variable declaration of the student type..
   // Displaying the size of the structure student.
   printf("The size of the student structure is %d", sizeof(stud1));
   return 0;
}

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

输出量

改变变量的顺序

现在,我们将看到更改变量顺序时会发生什么,它是否影响程序的输出。让我们考虑相同的程序。

#include 
struct student
{
   char a;
   int b;
  char c;
};
int main()
{
   struct student stud1; // variable declaration of the student type..
   // Displaying the size of the structure student.
   printf("The size of the student structure is %d", sizeof(stud1));
   return 0;
}

上面的代码与先前的代码相似;我们唯一改变的是结构学生内部变量的顺序。由于顺序的变化,两种情况下的输出都会不同。在前一种情况下,输出为8个字节,但是在这种情况下,输出为12个字节,如下面的屏幕截图所示。

输出量

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

  • 首先,将内存分配给char一个变量,即1个字节。C中的结构填充
  • 现在,内存将分配给int b,因为int变量占用4个字节,但是在左侧,只有3个字节可用。空行将在这3个字节上创建,而int变量将占据其他4个字节,以便可以在单个CPU周期中访问整数变量。C中的结构填充
  • 现在,内存将分配给字符c 。一次,CPU可以访问1个字,该字等于4个字节,因此CPU将使用4个字节来访问“ c”变量。因此,所需的总内存为12个字节(4个字节+4个字节+4个字节),即访问char变量所需的4个字节,访问int b变量所需的4个字节以及访问单个字符所需的其他4个字节’ c ‘变量。C中的结构填充

如何避免C中的结构填充?

结构填充是编译器自动完成的内置过程。有时需要避免使用C中的结构填充,因为这会使结构的大小大于结构成员的大小。

我们可以通过两种方式避免在C中进行结构填充:

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

使用#pragma pack(1)指令

#include 
#pragma pack(1)
struct base
{
    int a;
    char b;
    double c;
};
int main()
{
  struct base var; // variable declaration of type base
  // Displaying the size of the structure base
  printf("The size of the var is : %d", sizeof(var));
return 0;
}

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

输出量

  • 通过使用属性
#include 

struct base
{
    int a;
    char b;
    double c;
}__attribute__((packed));  ;
int main()
{
  struct base var; // variable declaration of type base
  // Displaying the size of the structure base
  printf("The size of the var is : %d", sizeof(var));

    return 0;
}

输出量