单片机/C/C++八股:(十五)内存对齐、结构体内存对齐

上一篇 下一篇
const 关键字的作用(和 define 比呢?)

目 录


内存对齐、结构体内存对齐

视频教学连接:CPU 到底是怎么读内存的?十分钟搞懂内存对齐_哔哩哔哩_bilibili

1)分析讲解

一切的根本原因是:

CPU 从内存中读取数据,32 位系统一次会读取 32 位(4字节),64 位系统一次会读取 64 位(8字节),以 32 位系统为例:一次读 4 字节,就意味着从 0 地址开始的话,每次都会从 4 的整数倍地址处开始读。

所以为了保证读取效率和兼容性,数据存储的时候,会自动进行内存对齐,实现内存对齐的方式是在变量值之间填充字节(padding)。

数据不进行内存对齐的话,就需要读取多次才能拼出完整的值:

内存对齐规则:

数据类型的大小,就是它的对齐边界(数据类型占 n n n 个字节,那么其起始地址就必须存放在 n n n 的倍数地址上)。

比如在 32 系统中:

  • char 对齐边界:1字节(任意地址);
  • short 对齐边界:2字节(偶数地址);
  • int / float 对齐边界:4字节(4的倍数地址);
  • double 对齐边界:8字节(8的倍数地址)。

内存对齐是通用的 硬件/ABI 要求,不仅限于结构体,主要考察结构体内存对齐还有另一个原因 ⟶ \longrightarrow ⟶

结构体的特性:

结构体的大小,必须是占内存最大成员的整数倍。

核心原因:为了支持结构体数组的正确内存布局,确保结构体数组中每一个元素的起始地址都满足其内部成员的对齐要求,从而保证程序在所有平台上正确、高效运行。

具体来说:假设定义了一个结构体 S,并声明一个数组:struct S arr[2]; ,结构体数组元素在内存中是紧挨着的,即 &arr[1] == &arr[0] + sizeof(struct S),为了让 arr[1] 中的每个成员也依然满足对齐要求 ,sizeof(struct S) 必须是一个"安全步长",这个步长必须能保证下一个结构体实例的起始地址,仍然满足其内部所有成员的对齐约束。而这个"安全步长"的最小值,就是 结构体中最大成员的对齐要求 ,也就是结构体的大小,必须是占内存最大成员的整数倍。为了达到这个效果,编译器会在结构体尾部填充字节。

以如下结构体为例:

c 复制代码
struct BadStudent
{
	char grade;		// 1字节
	int id;			// 4字节
	char gender;	// 1字节
};

以为是 1+4+1=6 字节,实际上是 12 字节:先内存对齐到 1+(3)+4+1=9 字节 → 再填充字节到 int 型的整数倍 12 字节。

缺点及优化方法:

  • 缺点:内存对齐会导致填充字节无法使用,造成内存浪费。

  • 方法:结构体成员按数据类型从大到小排列(如果定义了一个 char 型的数组,那这个数组不用放在最前面,单个元素只是 char ,但数组长度可能会造成一定的影响)。

还以那个结构体为例,其元素换个顺序:

c 复制代码
struct BadStudent
{
	int id;			// 4字节
	char grade;		// 1字节
	char gender;	// 1字节
};

现在实际上占用 4+1+1=6 → 8 字节。

2)总结

内存对齐源于 CPU 根据系统位数按固定字长(如 4 或 8 字节)高效读取内存的机制,要求数据起始地址为其类型大小的整数倍。

内存对齐的规则:数据类型占 n n n 个字节,那么其起始地址就必须存放在 n n n 的倍数地址上(32/64位系统中,数据类型所占字节数不同)。

而为了确保结构体数组中每一个元素的起始地址都能满足其内部成员的对齐要求,所以结构体的大小,必须是占内存最大成员的整数倍。

虽然内存对齐提高了 CPU 的访问效率,但会造成所填字节的浪费,所以为了节省空间,结构体成员最好要按数据类型从大到小排列。


相关推荐
Ramble_Naylor7 小时前
Rust深度解读:异步流(Async Streams)与生成器的处理艺术
开发语言·后端·rust
努力实干7 小时前
简单工厂模式(Simple Factory Pattern)——C++
开发语言·c++·简单工厂模式
熊猫钓鱼>_>7 小时前
平行视界兼容模式与魔法变量配置避坑【鸿蒙心迹】
运维·服务器·开发语言·笔记·华为·harmonyos·openharmony
大鹏说大话8 小时前
PHP 大文件上传解决方案:分片上传完整思路
开发语言·php
Wang's Blog8 小时前
Java 项目实战:交友项目 - 我的访客:需求说明
java·开发语言·交友
不昀9 小时前
RMII切到10Mbps,50MHz参考时钟也要降到5MHz吗?
单片机·嵌入式硬件·硬件设计·以太网接口
布莱克6059 小时前
C++ 中堆和栈的区别详解
开发语言·c++·栈·堆
像风一样自由20209 小时前
3.Dockerfile如何构建一个Python算法镜像
开发语言·人工智能·python·大模型
路上有只喵10 小时前
认识元器件 · 单片机(MCU/MPU/SOC) · 实战篇6
单片机·嵌入式硬件
BuHuaX10 小时前
Lua 与 ToLua:运行机制及热更新原理
开发语言·unity·junit·c#·lua