C结构体的内存对齐

术语说明

pack 可以指定内存对齐值,单位是字节,这个是值需要时2的次幂(1,2,4,6,8)。如不设置也有默认值,这个值我理解的是操作系统的字长。

#pragma pack(8)

对齐规则

  1. 结构体第一个成员放在struct offset 0 的位置。
  2. 计算每个成员的对齐值,计算方式:min(size(成员), 默认对齐值)。
  3. 计算总体的对齐值,计算方式:min(max(所有成员的大小), 默认对齐值)。

详解

上面的说明较为笼统,下面使用一个例子来解释。

# include <stdio.h>
# pragma pack(8)     # 手动设置对齐值。如不手动设置这个值,不同环境这个值不同,不方面我们接下来的计算。

struct Tag1 {
    double a;
    int b;
    char c;
};

  1. 首先将double类型的a,存放在Tag1结构体offset=0的地址上,double类型长度是8,更新offset是8.
  2. 计算int b变量的对齐值,min(size(int), 默认对齐值) = min(4, 8) = 4, 然后判断offset是4的正整数倍数,显然是。将b变量放置在9的位置,此时offset变更成12.
  3. 计算char c变量的对齐值,min(size(char), 默认对齐值) = 1, 然后判断offset是不是4的整数倍呢?12是1的正整数倍,将c变量放置在13位置。
  4. 最后,计算总的对齐值,min(max(double、int、char), 默认对齐值) = min(8, 8) = 8, 13不是8的正整数倍数,填充到16。
  5. 最终 C语言的sizeof(struct Tag1) 的最终值是 16.

接下来将double 与 int的顺序修改下

# include <stdio.h>
# pragma pack(8)     # 手动设置对齐值。如不手动设置这个值,不同环境这个值不同,不方面我们接下来的计算。

struct Tag1 {
    int b;      
    double a;
    char c;
};

  1. 首先将int类型b,放在Tag1结构体offset=0的地址上,更新offset=4.
  2. 接下来计算变量double a的对齐值,min(8, 8) = 8, 判断offset=4不是8的整数倍,填充到16(为什么是16?因为需要是8的最小正整数倍),此时offset=16。
  3. 接下来计算变量char c的对齐值,min(1, 8) = 1, 判断offset=16是1的整数倍,将c变量放在17的位置。
  4. 最后计算总的对齐值,min(max(int、double、char), 默认对齐值) = min(8, 8) = 8, 17不是8的正数倍,填充到24.
  5. 最终 sizeof(struct Tag1) 的值是 24.

嵌套的结构体的对齐

主要流程差不多,把内部的结构体当作一个变量,先计算这个变量的sizeof。

注意点

  1. 在一些网络通讯中,如果主机A发送一个结构体的数据到主机B,此时对数据流进行类型转换,可能会出现问题。解决办法是主机A、主机B都设置 # pragma pack(1) .
原文地址:https://www.cnblogs.com/ruiqingliang/p/14471039.html