C 语言学习(12) ---- C语言结构体对齐

2023-05-08  本文已影响0人  特立独行的佩奇

字节对齐

现在计算机系统的内存都是按照字节划分的,理论上对任何变量的访问可以从任何地址开始,但是实际情况是访问特定的变量经常在特定的内存地址访问,这就需要各种类型的数据按照一定的规则在空间上进行排列,而不是顺序排放,这就是对齐的概念;
比如在32bit system 下,一个4字节的int,如果它的地址是0x00000004 (4 的倍数),那么它就是对齐的;如果是0x00000002(非4 的倍数),那么它就是非对齐的

结构体对齐

结构体对齐的规则首先要看有没有用#pragma pack宏声明,使用这个预编译指令可以改变对齐规则,
有宏定义的情况下结构体自身的大小应为预编译指定规定对齐的大小的整数倍,同时结构体内的成员的地址也会按照预编译指定规定的大小对齐,#pragma pack 参数可以是 "1" "2" "4" "8" 或者 "16"

未使用pragma pack宏声明

未使用pragma pack宏声明的情况下,遵循下面三个原则:

  1. 第一个成员的首地址假设为0
  2. 每个成员的首地址大小是其自身大小的整数倍
  3. 结构体的总大小,是其成员中所包含的最大类型size大小的整数倍

示例一:

//  struct = 1+2+4+8
struct demostruct {
                 //  baseAddr     length  padding
    uint8_t a;  //  addr         1        1
    uint16_t b; //  addr+2       2        0
    uint32_t c; //  addr+4       4        0
    uint64_t d; //  addr+8       8        0
};
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 2 的整数倍,所以在 a 后补充一个字节,b的首地址变为 add + 2,长度为 2
  3. 第三个成员 c 首地址理论上为 addr + 4,符合是自身长度整数倍的原则,长度为 4
  4. 第四个成员 d 首地址 addr + 8,符合是自身长度整数倍的原则
  5. 结构体大小为 16字节,结构体内包含最大成员占用 8 字节,符合上述原则

打印出的结构体大小和各成员内存地址如下:
struct_a size 16 a addr:008726E8 b:008726EA c addr:008726EC d addr:008726F0

对齐方法01.jpg

示例二:

//  struct = 1+8+1+1
struct demostruct_b {
    //  baseAddr     length  padding
    //  addr         1        7
    //  addr+8       8        0
    //  addr+16      1        0
    //  addr+17      1        0
    uint8_t a; 
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 8 的整数倍,所以在 a 后补充7个字节,首地址变为 add + 8 长度为 8,
  3. 第三个成员 c 首地址为 addr + 16,长度为 1
  4. 第四个成员 d 首地址 addr + 17,长度为 1
  5. 结构体大小为 18 字节,结构体内包含最大成员占用 8 字节,所以还需要在最后补充 6 字节,共 24 字节

打印出的结构体大小和各成员内存地址如下:
struct_b size 24 a addr:008726F8 b:00872700 c addr:00872708 addr:00872709

对齐方法02.jpg

使用#pragma pack宏声明

#pragma pack(push)
#pragma pack(4)
//  struct = 1+8+1+1
struct demostruct_c {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       8        0
    //  addr+12      1        0
    //  addr+13      1        0
    uint8_t a;
    uint64_t b;
    uint8_t c;
    uint8_t d;
};
#pragma pack(pop)
  1. 第一个成员 a 字节首地址 addr,长度为 1
  2. 第二个成员 b 字节首地址理论上为 addr + 1,但是按照地址对齐原则,地址必须是 4 的整数倍,所以在 a 后补充3个字节,首地址变为 add + 4 长度为 8,
  3. 第三个成员 c 首地址为 addr + 12,长度为 1
  4. 第四个成员 d 首地址 addr + 13,长度为 1
  5. 结构体大小为14 字节,不是 pack(4) 的整数倍,所以在最后补充 2 字节,共 16 字节

struct_c size 16 a addr:00872710 b:00872714 c addr:0087271C addr:0087271D

pack对齐方法01.jpg

注意:
当#pragma pack 设定的 pack 值大于结构体中最大成员的size 值时,应当以结构体中最大成员的size作为 pack 的值
换句话说,应该以这两者中较小的值作为 pack 的值

#pragma pack(push)
#pragma pack(8)
//  struct = 1+4 +2
struct demostruct_d {
    //  baseAddr     length  padding
    //  addr         1        3
    //  addr+4       4        0
    //  addr+8       2        0
    uint8_t a;
    uint32_t c;
    uint16_t b;
};
#pragma pack(pop)

验证结果是12 字节,不是以设定的 pack 值 8
*struct_d size 12 a addr:00EF2720 b:00EF2728 c addr:00EF2724

上一篇 下一篇

猜你喜欢

热点阅读