C/C++内存对齐详解

内存对齐规则

  • 对齐系数(也叫对齐模数):gcc中默认 #pragma pack(4),可以通过预编译命令 #pragma pack(n),n = 1,2,4,8,16 来改变这一系数
  • 有效对齐值:是给定值 #pragma pack(n) 和结构体中 最长数据类型长度中较小的那个。有效对齐值也叫 对齐单位
  1. 规则一:结构体第一个成员的偏移量 offset 为 0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节

  2. 规则二:结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节

    注意:上面两条规则都需要得到满足
    注意:成员变量首地址偏移和对齐都是与【有效对齐值】进行比较,而有效对齐值是对齐系数与结构体中最长数据类型中的较小者

// 64 位程序
struct
{
    int i;    
    char c1;  
    char c2;  
}x1;

struct{
    char c1;  
    int i;    
    char c2;  
}x2;

struct{
    char c1;  
    char c2; 
    int i;    
}x3;
struct
{
    short i;    
    char c1;  
    char c2;  
}y1;

struct{
    char c1;  
    short i;    
    char c2;  
}y2;

struct{
    char c1;  
    char c2; 
    short i;    
}y3;

int main()
{
    printf("%ld\n",sizeof(x1));  // 输出8
    printf("%ld\n",sizeof(x2));  // 输出12
    printf("%ld\n",sizeof(x3));  // 输出8
    
    cout << "----------------" << endl;
    printf("%ld\n",sizeof(y1));  // 输出4
    printf("%ld\n",sizeof(y2));  // 输出6
    printf("%ld\n",sizeof(y3));  // 输出4
    return 0;
}

既要考虑首地址偏移,又要是有效对齐值(对齐单位)的整数倍 图片显示

y1 y2 y3结构体:
以上测试都是在 Linux 环境下进行的,linux 下默认 #pragma pack(4),且结构体中最长的数据类型为 2 个字节,所以有效对齐单位为 2 字节,下面根据上面所说的规则以 y2 来分析其内存布局:

首先使用规则 1,对成员变量进行对齐:

sizeof(c1) = 1 <= 2 (有效对齐位),按照 1 字节对齐,占用第 0 单元;

sizeof(i) = 2 <= 2 (有效对齐位),相对于结构体首地址的偏移要为 2 的倍数,占用第 2,3 单元;

sizeof(c2) = 1 <= 2 (有效对齐位),相对于结构体首地址的偏移要为 1 的倍数,占用第 4 单元;

然后使用规则 2,对结构体整体进行对齐:

y2 中变量 i 占用内存最大占 2 字节,而有对齐系数为 4 字节,两者较小值就是 2 字节。因此整体也是按照 2 字节对齐。由规则 1 得到 y2 占 5 个字节,此处再按照规则 2 进行整体的 2 字节对齐,所以整个结构体占用 6 个字节。