【C语言 内存对齐】 结构体字节对齐之`#pragma pack(push) `、`#pragma pack(pop) `和`#pragma pack()`的使用

优秀文章参考:

#pragma pack(push) 和#pragma pack(pop) 以及#pragma pack() - Crystal_Guang - 博客园

c语言内存对齐与#pragma pack(n) - long_ago - 博客园

目录

问题引入
一、#pragma pack(push,n)、#pragma pack(pop)和#pragma pack(n)

1.区别与联系
2.字节对齐概念

二、功能与使用

1.#pragma pack(push,n)
2.#pragma pack(pop)
3.#pragma pack(n)
4.实例:嵌套使用对齐方式

三、使用建议

四、注意事项

问题引入

结构体内存对齐字节可以通过#pragma pack(n) 的方式来指定。

但是,有没有想过一个问题,某些时候想4字节对齐,有些时候我又想1字节或者8字节对齐,那么怎么解决这个问题呢?

此时,#pragma pack(push) #pragma pack(pop) 以及#pragma pack()应运而生。

一、#pragma pack(push, n)#pragma pack(pop)#pragma pack(n)

用于控制结构体、联合体或类的数据对齐方式的编译器指令。

1. 区别与联系
特性 #pragma pack(push, n) #pragma pack(pop) #pragma pack(n)
对齐方式修改 临时修改为 n 字节 恢复到之前保存的对齐方式 修改全局对齐方式为 n 字节
对全局默认对齐影响 不改变全局默认对齐 不改变全局默认对齐 改变全局默认对齐
对齐方式恢复 保存当前对齐方式,便于恢复 恢复最近的 push 设置 无恢复机制
推荐使用场景 临时修改,代码块对齐 恢复对齐设置,避免全局影响 全局调整对齐方式
嵌套支持 支持嵌套 支持嵌套 不支持嵌套
2. 字节对齐概念

单字节对齐

指数据在内存中的排列方式,它确保每个数据成员按照**1字节(8位)**的边界对齐,而不是按照系统默认的对齐方式(通常是4字节或更高)。

在内存中,不同的数据类型通常有不同的对齐规则:

  • 默认对齐:系统会按照数据类型的大小对齐,例如:
  • uint8 按 1 字节对齐。
  • uint16 按 2 字节对齐。
  • uint32 按 4 字节对齐。

对齐的目的:提高内存访问效率,但有时会引入填充字节,以满足对齐要求。

二、功能与使用

1. #pragma pack(push, n)
  • 功能:将当前的对齐方式保存到一个堆栈中,并设置新的对齐方式为 n(字节对齐);
  • 典型用法:用于在需要临时改变对齐规则时保存当前状态,便于后续恢复。
/*
StructA 按 2 字节对齐,节省空间。
StructB 按默认对齐方式(假设为 4 字节)对齐,内存占用更多。
*/
#include <stdio.h>

#pragma pack(push, 2) // 保存当前对齐方式,并设置为 2 字节对齐
typedef struct {
    
    
    char a;   // 1 字节
    int  b;   // 4 字节
} StructA;
#pragma pack(pop) // 恢复之前的对齐方式

typedef struct {
    
    
    char a;   // 默认对齐
    int  b;   // 默认对齐
} StructB;

int main() {
    
    
    printf("StructA size: %zu bytes\n", sizeof(StructA)); // 紧凑对齐
    printf("StructB size: %zu bytes\n", sizeof(StructB)); // 默认对齐
    return 0;
}
2.#pragma pack(pop)
  • 功能:从堆栈中恢复上一次保存的对齐方式;
  • 典型用法:与 #pragma pack(push, n) 配合使用,确保代码在临时修改对齐方式后恢复默认状态,避免影响后续代码。
/*
StructC 紧凑对齐,占用 5 字节。
StructD 恢复默认对齐方式(假设为 4 字节),内存占用可能更大。
*/
#pragma pack(push, 1) // 设置为 1 字节对齐
typedef struct {
    
    
    char a;
    int  b;
} StructC;

#pragma pack(pop) // 恢复默认对齐方式
typedef struct {
    
    
    char a;
    int  b;
} StructD;
3. #pragma pack(n)
  • 功能直接修改全局对齐方式n 字节,无需保存之前的状态。
  • 典型用法:用于代码中需要长时间使用特定对齐方式的场景。
#pragma pack(2) // 设置全局对齐方式为 2 字节
typedef struct {
    
    
    char a;
    int  b;
} StructE;

// 此处所有代码均受 2 字节对齐方式影响
4. 实例:嵌套使用对齐方式

假设需要定义一组与硬件通信协议相关的结构体,有不同的对齐要求:

  1. 全局默认对齐为 4 字节。
  2. 某些结构体需要紧凑对齐(如 1 字节)。
  3. 在局部范围内调整为特定对齐方式(如 2 字节)。
#include <stdio.h>

// 全局默认对齐为 4 字节
#pragma pack(4)

typedef struct {
    
    
    char a;       // 1 字节
    int  b;       // 4 字节
} GlobalAlignedStruct;

#pragma pack(push, 1) // 保存全局 4 字节对齐,临时调整为 1 字节对齐

typedef struct {
    
    
    char a;       // 1 字节
    int  b;       // 紧凑对齐,无填充,占用 4 字节
    short c;      // 2 字节
} TightlyPackedStruct;

#pragma pack(push, 2) // 在 1 字节对齐的基础上进一步设置为 2 字节对齐

typedef struct {
    
    
    char a;       // 1 字节
    short b;      // 2 字节对齐
    int c;        // 4 字节对齐
} TwoByteAlignedStruct;

#pragma pack(pop) // 恢复到 1 字节对齐

typedef struct {
    
    
    char a;       // 1 字节
    int  b;       // 紧凑对齐,无填充,占用 4 字节
    short c;      // 2 字节
} RestoredTightlyPackedStruct;

#pragma pack(pop) // 恢复到全局默认 4 字节对齐

typedef struct {
    
    
    char a;       // 1 字节
    int  b;       // 默认对齐,4 字节
    short c;      // 默认对齐,4 字节
} DefaultAlignedStruct;

int main() {
    
    
    printf("Size of GlobalAlignedStruct: %zu bytes\n", sizeof(GlobalAlignedStruct));
    printf("Size of TightlyPackedStruct: %zu bytes\n", sizeof(TightlyPackedStruct));
    printf("Size of TwoByteAlignedStruct: %zu bytes\n", sizeof(TwoByteAlignedStruct));
    printf("Size of RestoredTightlyPackedStruct: %zu bytes\n", sizeof(RestoredTightlyPackedStruct));
    printf("Size of DefaultAlignedStruct: %zu bytes\n", sizeof(DefaultAlignedStruct));
    return 0;
}

解释

  1. 全局对齐方式
  • 默认对齐为 4 字节,GlobalAlignedStruct 使用全局对齐方式。
  1. 1 字节对齐范围
  • 使用 #pragma pack(push, 1) 保存当前对齐方式(4 字节),并临时设置为 1 字节对齐。
  • TightlyPackedStruct 使用 1 字节对齐,所有成员紧凑排列,无填充。
  1. 嵌套对齐调整
  • 在 1 字节对齐范围内再次使用 #pragma pack(push, 2) 设置为 2 字节对齐。
  • TwoByteAlignedStruct 受 2 字节对齐规则影响,适当填充以满足对齐要求。
  1. 恢复到外层对齐
  • 使用 #pragma pack(pop) 恢复到 1 字节对齐。
  • RestoredTightlyPackedStruct 使用恢复的 1 字节对齐规则。
  1. 全局对齐恢复
  • 使用第二个 #pragma pack(pop) 恢复全局 4 字节对齐。
  • DefaultAlignedStruct 使用默认的 4 字节对齐规则。
输出示例(可能因编译器不同而略有差异)
Size of GlobalAlignedStruct: 8 bytes
Size of TightlyPackedStruct: 7 bytes
Size of TwoByteAlignedStruct: 8 bytes
Size of RestoredTightlyPackedStruct: 7 bytes
Size of DefaultAlignedStruct: 12 bytes

三、使用建议

  1. 推荐使用 #pragma pack(push, n)#pragma pack(pop)
    • 适合局部调整对齐方式,尤其是在多个文件中包含不同的对齐要求时。
    • 保持代码清晰,易于维护,避免无意间影响其他代码。
  2. 谨慎使用 #pragma pack(n)
    • 会修改全局对齐方式,可能导致不易察觉的问题。
    • 如果使用,务必在之后通过 #pragma pack() 恢复默认设置。

四、注意事项

  • 性能影响
    • 非对齐内存访问可能会导致性能下降(取决于硬件架构)。
    • 避免在高性能关键路径中使用严格的对齐方式(如 1 字节对齐)。
  • 跨平台兼容性
    • 不同平台的默认对齐方式可能不同,因此明确设置对齐方式可以提高可移植性。
  • 协议解析和硬件交互
    • 通常使用 1 字节对齐,以确保内存布局与外部设备或协议数据结构严格一致。

猜你喜欢

转载自blog.csdn.net/Thmos_vader/article/details/145057644
今日推荐