在 C 语言中使用结体构对齐和填充

Jinku Hu 2023年10月12日
  1. 了解 C 语言中对齐和填充的基础知识
  2. 在 C 语言中使用成员重新排序技术来节省对象的空间
在 C 语言中使用结体构对齐和填充

本文将介绍几种在 C 语言中使用结构体来对齐和填充的方法。

了解 C 语言中对齐和填充的基础知识

内存中的所有对象都以主要数据类型表示,如:charshortintlongpointer 等。charshortintlongpointer 等。这些数据类型在内存中都有其相应的大小。在大多数当代 64 位桌面处理器上,char 的大小是 1 个字节,short 是 2 个字节,int 是 4 个字节,pointer 是 8 个字节,以此类推。请注意,这些都不是保证大小(除了 char),但可以使用 sizeof 操作符检索对象的大小。现在,对齐是编译器用来放置内存中的变量的方法,意味着每个基本数据类型都存储在被相应大小除以的地址上。

通常,利用对齐来更快、更有效地访问数据对象。对齐迫使连续声明的不同数据类型在其地址之间包含一些间距。也就是说,如果我们声明一个结构 st1,其中有一个指针和一个 char,如下面的例子所示,它总共会占用 16 个字节。不过要注意,一个指针需要 8 个字节,一个 char 需要一个字节,所以人们会认为 st1 结构一定会占用 9 个字节。但它的表现就像所有成员都是按照最大成员的大小(即 8 个字节)对齐的。st2 结构展示了一个类似的结构,它占据了同样的内存量,只是它有一个 7 个 char 成员的数组。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
  typedef struct {
    char *p;
    char c2;
  } st1;

  typedef struct {
    char *p;
    char c2;
    char padding[7];
  } st2;

  printf("sizeof st1 = %zu\n", sizeof(st1));
  printf("sizeof st2 = %zu\n", sizeof(st2));

  exit(EXIT_SUCCESS);
}

输出:

sizeof st1 = 16
sizeof st2 = 16

在 C 语言中使用成员重新排序技术来节省对象的空间

前面的例子表明,当结构包括不同类型,并且没有填满对齐边界时,会有一些内存的浪费。虽然,在某些情况下,可能会重新排列结构成员,节省额外的空间。

接下来的示例代码中定义了 foo1 结构,它的中间是最大的成员(char *),而 foo2 结构的成员与第一个结构相同。这两个对象的大小是不同的–24 字节和 16 字节。这与数据成员的排序有关。在 foo1 结构中,p 需要在被 8 整除的地址上对齐,所以前面的 intshort 总共会占用 8 个字节,后面的两个 char 也会占用这 8 个字节。虽然,如果我们把 p 移到第一位,下面的成员将挤进 8 个字节,也满足对齐规则。这样,foo2 的大小一共是 16 个字节,就叫打包 struct。请注意,gcc 编译器有特殊的 __attribute__((packed)) 指定符,该说明符甚至可以强制打包无序的 struct 成员。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
  typedef struct {
    int n1;
    short s1;
    char *p;
    char c1;
    char c2;
  } foo1;

  typedef struct {
    char *p;
    int n1;
    short s1;
    char c1;
    char c2;
  } foo2;

  typedef struct {
    int n1;
    short s1;
    char *p;
    char c1;
    char c2;
  } __attribute__((packed)) foo3;

  printf("sizeof foo1 = %zu\n", sizeof(foo1));
  printf("sizeof foo2 = %zu\n", sizeof(foo2));
  printf("sizeof foo3 = %zu\n", sizeof(foo3));

  exit(EXIT_SUCCESS);
}

输出:

sizeof foo1 = 24
sizeof foo2 = 16
sizeof foo3 = 16
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 创始人。Jinku 在机器人和汽车行业工作了8多年。他在自动测试、远程测试及从耐久性测试中创建报告时磨练了自己的编程技能。他拥有电气/电子工程背景,但他也扩展了自己的兴趣到嵌入式电子、嵌入式编程以及前端和后端编程。

LinkedIn Facebook

相关文章 - C Struct