结构体占用内存的大小,字节对齐问题汇总

内存对齐与结构体布局:不同字节对齐情况下数据占用与结构实例
文章分析了不同字节对齐(如1字节、2字节、4字节等)对C语言结构体`person`内存占用的影响,以及嵌套结构体在内存中的排列。讨论了如何影响性能和内存效率。
32os64os
char1字节对齐1字节对齐
short2字节对齐2字节对齐
int4字节对齐4字节对齐
long4字节对齐8字节对齐
float4字节对齐4字节对齐
double4字节对齐(分配两次)
总共使用8字节
8字节对齐
struct person
{
	char name[20];
	int age;
};

![[../Images/c-Page-2.drawio.png]]

24字节对齐情况
![[../Images/Pasted image 20240317145142.png]]

struct person
{
	char a;
	char b[10];
	int c ;
};

![[../Images/c-字节对齐02.drawio.png]]

16字节对齐情况

struct person
{
	char a;
	short b;
	char c;
	int d;
};

![[../Images/c-字节对齐03.drawio.png]]

12 字节对齐情况
![[../Images/Pasted image 20240317145244.png]]

嵌套结构体

struct inner {
    char a;
    int b;
    long c;
};

struct outer {
    char c;
    char e;
    struct inner d;
    char f;
    long g;
};

![[../Images/Pasted image 20240317150544.png]]
嵌套模型运行情况
在这里插入图片描述
嵌套模型字节对齐情况

struct inner {  
    char a;  
    int b;  
};  
struct char1 {  
    char a;  
};  
struct outer {  
    char c;  
    char e;  
    struct char1 h;  
    struct inner d;  
    char f;  
    long g;  
};

![[../Images/Pasted image 20240317154452.png]]

嵌套模型验证2
![[../Images/c-字节对齐05.drawio.png]]

在 C 语言中,**对齐(alignment)与补齐(padding)** 是结构体(`struct`)内存布局中的重要概念,它们直接影响结构体大小以及数据在内存中的存储方式。理解这些概念对于编写高效、可移植的 C 程序(尤其是在嵌入式系统、网络协议、文件格式解析等领域)至关重要。 --- ### 一、基本概念 #### 1. **数据对齐(Alignment)** 现代 CPU 访问内存时,通常要求某些类型的数据存放在特定地址上,这个地址必须是某个数的倍数,称为“对齐边界”。 例如: - `char`(1 字节):对齐要求为 1 字节(任何地址都可) - `short`(2 字节):对齐要求通常是 2 字节(地址必须是 2 的倍数) - `int`(4 字节):对齐要求通常是 4 字节 - `double`(8 字节):对齐要求通常是 8 字节(在 64 位平台) > 如果数据没有正确对齐,可能导致性能下降,甚至在某些架构(如 ARM 默认配置)上引发**硬件异常**(SIGBUS)。 #### 2. **补齐(Padding)** 为了满足对齐要求,编译器会在结构体成员之间或末尾插入未使用的字节,称为“填充字节”(padding)。 --- ### 二、结构体中的对齐与补齐示例 ```c #include <stdio.h> struct Example1 { char a; // 1 byte // +1 byte padding (to align 'b' to 4-byte boundary) int b; // 4 bytes char c; // 1 byte // +3 bytes padding at the end (so total size is multiple of 4, for alignment of next struct if in array) }; ``` 我们来分析这个结构体内存布局: | 偏移 | 成员 | 大小 | 说明 | |------|------|------|------| | 0 | a | 1 | 放在偏移 0 | | 1 | - | 3 | 填充字节(padding),因为 `int b` 需要 4 字节对齐 | | 4 | b | 4 | 正确对齐 | | 8 | c | 1 | 可以紧接放置 | | 9 | - | 3 | 结构体大小需对齐到最大成员对齐单位(这里是 4),所以补 3 字节 | ```c printf("Size of struct Example1: %zu\n", sizeof(struct Example1)); // 输出 12 ``` --- ### 三、影响因素:成员顺序很重要! 改变成员顺序可以减少 padding: ```c struct Example2 { char a; // 1 byte char c; // 1 byte // 2 bytes padding? Not yet — but wait. int b; // 4 bytes → needs 4-byte alignment }; ``` 内存布局: | 偏移 | 成员 | 大小 | |------|------|------| | 0 | a | 1 | | 1 | c | 1 | | 2 | - | 2 | 填充,使 b 对齐到 4 字节边界 | 4 | b | 4 | 总大小:8 字节(最后还要补齐到 4 的倍数?其实已经 8 是了) ```c printf("Size of struct Example2: %zu\n", sizeof(struct Example2)); // 输出 8 ``` ✅ 比 `Example1` 节省了 4 字节! > **最佳实践:按大小降序排列成员(或按对齐需求从大到小)以减少 padding** --- ### 四、结构体大小的对齐(Struct Alignment) 整个结构体大小也必须是其**最大对齐要求**的倍数。这是因为当数组中存放多个结构体时,每个元素都必须满足内部成员的对齐要求。 ```c struct Example3 { char a; double b; // 8-byte aligned int c; }; ``` 假设在 64 位系统上: - `a` 在 offset 0(1 字节) - 然后需要 7 字节 padding 使 `b` 对齐到 8 字节边界 → offset 8 - `b` 占用 8 字节 → 到 offset 16 - `c` 占用 4 字节 → offset 16~20 - 结构体大小为 20,但最大对齐是 8(来自 `double`),所以总大小必须是 8 的倍数 → 补 4 字节 → 总大小 24 ```c printf("Size of struct Example3: %zu\n", sizeof(struct Example3)); // 输出 24 ``` --- ### 五、控制对齐:`#pragma pack` 和 `__attribute__((packed))` 有时我们希望**禁用 padding**,比如处理网络包头、二进制文件格式等。 #### 方法 1:使用 `#pragma pack` ```c #pragma pack(push, 1) // 设置对齐为 1 字节,即不填充 struct PackedStruct { char a; // 1 int b; // 4 → 紧跟 a 后,无 padding char c; // 1 }; // 总大小 = 6 #pragma pack(pop) printf("Packed size: %zu\n", sizeof(struct PackedStruct)); // 输出 6 ``` #### 方法 2:GCC 扩展 `__attribute__((packed))` ```c struct __attribute__((packed)) PackedStruct2 { char a; int b; char c; }; // 同样输出 6 printf("Packed with attribute: %zu\n", sizeof(struct PackedStruct2)); ``` ⚠️ 注意:打包结构体可能降低访问速度,甚至在某些架构上导致崩溃(未对齐访问不被支持)。 --- ### 六、查询对齐信息:`_Alignof`(C11) C11 引入了 `_Alignof` 运算符来获取类型的对齐要求: ```c #include <stdalign.h> printf("Alignof char: %zu\n", _Alignof(char)); // 1 printf("Alignof int: %zu\n", _Alignof(int)); // 4 printf("Alignof double: %zu\n", _Alignof(double)); // 8 printf("Alignof struct: %zu\n", _Alignof(struct Example1)); // 4 ``` 还可以使用 `alignas` 和 `alignof`(C++ 更常用)。 --- ### 七、总结:对齐与补齐规则 1. **每个成员按其自身对齐要求对齐**(如 `int` 要求偏移是 4 的倍数) 2. **编译器在必要时插入 padding** 3. **结构体大小是其最大成员对齐值的整数倍** 4. **成员顺序影响结构体大小** 5. **可用 `#pragma pack` 或 `__attribute__((packed))` 控制对齐** --- ### 相关代码汇总 ```c #include <stdio.h> #include <stdalign.h> struct Example1 { char a; int b; char c; }; struct Example2 { char a; char c; int b; }; #pragma pack(push, 1) struct Packed { char a; int b; char c; }; #pragma pack(pop) int main() { printf("Size of Example1: %zu\n", sizeof(struct Example1)); // 12 printf("Size of Example2: %zu\n", sizeof(struct Example2)); // 8 printf("Size of Packed: %zu\n", sizeof(struct Packed)); // 6 printf("Alignof int: %zu\n", _Alignof(int)); printf("Alignof struct Example1: %zu\n", _Alignof(struct Example1)); return 0; } ``` --- ### 解释 上述代码展示了: - 不同成员顺序导致不同内存占用 - 使用 `#pragma pack(1)` 强制紧凑布局 - 查询类型的对齐要求 - 编译器自动添加 padding 以满足对齐约束 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值