C语言学习日记 · 内存与内存对齐篇

一、学习概述

本次学习串联了从硬件到语法的完整知识链:梳理了位 / 字节的基础单位、内存硬件的分层结构、CPU 读取内存的底层机制,最终落地到 C 语言结构体内存对齐的规则演算,理解了对齐规则并非单纯的语法约定,而是软件对硬件限制的适配。

二、位与字节:基础单位换算

1. 核心单位定义

  • bit(比特 / 位,简写 b):计算机最小的信息单位,单个 bit 只能存储 01

  • Byte(字节,简写 B):内存寻址的最小单位,也是编程中最常用的基础单位

    核心换算:1 Byte = 8 bit

严格区分大小写:小写b代表 bit 位,大写B代表 Byte 字节,二者相差 8 倍。

2. 容量换算关系

1 Byte = 8 bit
1 KB = 1024 Byte
1 MB = 1024 KB
1 GB = 1024 MB

注:硬件厂商常使用十进制计量(1GB=1000MB),但编程、操作系统底层一律按 1024 计算。

3. 常见整数类型对应大小

类型字节数比特数
int8 / char1B8bit
int16 / short2B16bit
int32 / int4B32bit
int64 / long long8B64bit

三、内存硬件层级结构(从主板到存储单元)

内存是分层组织的硬件系统,从宏观到微观层级如下:

  1. 主板层级:通道(Channel)
    一块主板通常支持多通道(如主流双通道),每个通道可插入多根内存条;双通道可并行读写,直接提升内存带宽。

  2. 内存条层级:DIMM(内存条)
    一根物理内存条就是一个 DIMM,通常正反两面各有一组存储阵列,称为Rank

  3. Rank 层级:内存颗粒(Chip)
    一个 Rank 由多颗内存颗粒(chip / 显存颗粒)组成,例如图中一个 Rank 包含 4 颗颗粒。

  4. 颗粒内部:Bank 与存储单元

    • 每颗颗粒内部划分多个 Bank,是内存寻址的内部单元;

    • 重要勘误:不是每个 Bank 存 1bit,而是每个存储单元格存放 8bit(1 字节),每次从一个 Bank 中读出 8bit 数据。

    • 对应图中问题解答:一个 4 字节的 int32 数据,会拆分成 4 个独立字节,分别存放在对应的存储单元中,并非整体存放在单颗颗粒里。

四、CPU 读取内存的底层原理

1. 地址总线的硬件限制

CPU 通过地址总线和数据总线访问内存,总线位数决定了访问粒度,这是内存对齐的根本来源

  • 32 位系统:数据总线一次传输 32bit(4 字节),只能从被 4 整除的地址开始,连续读取 4 字节;

  • 64 位系统:数据总线一次传输 64bit(8 字节),只能从被 8 整除的地址开始,连续读取 8 字节。

地址总线位数是硬件成本与性能的平衡,不可能做到 “任意地址读取任意长度”,这种寻址的 “不自由”,就是内存对齐的底层硬件原因。

2. 对齐与不对齐的性能差异

以 64 位 CPU 读取 int64(8 字节)为例:

  • 对齐存放:变量起始地址是 8 的倍数,数据完整落在一个 8 字节读取块内,CPU 只需 1 次内存读取即可拿到完整数据。

  • 不对齐存放:变量跨两个 8 字节块存放,CPU 需要执行 2 次内存读取,再拼接数据才能得到完整值,访问效率大幅下降;部分嵌入式硬件架构甚至会直接触发硬件异常。

五、结构体内存对齐规则

内存对齐是编译器自动执行的内存布局规则,目的是适配 CPU 读取特性,保证访问效率。

1. 两条核心对齐规则

  1. 成员对齐规则:结构体每个成员的起始偏移地址,必须是该成员自身大小的整数倍;不满足时,在前一个成员后填充空白字节(padding)。

  2. 整体对齐规则:整个结构体的总大小,必须是结构体中最大成员大小的整数倍;不满足时,在结构体末尾填充空白字节。

2. 实例演算(64 位平台)

示例结构体:

struct T {
    char a;     // 1字节
    long b;     // 8字节
    int c;      // 4字节
    short d;    // 2字节
};

逐成员布局计算:

  1. 成员 a:起始偏移 0,占 1 字节,结束于偏移 0;

  2. 成员 b:自身 8 字节,要求起始地址是 8 的倍数。下一个可用地址是 1,不满足条件,因此填充 7 字节 padding(偏移 1-7);b 从偏移 8 开始,占 8 字节,结束于偏移 15;

  3. 成员 c:自身 4 字节,下一个地址 16 是 4 的倍数,满足条件。c 从偏移 16 开始,占 4 字节,结束于偏移 19;

  4. 成员 d:自身 2 字节,下一个地址 20 是 2 的倍数,满足条件。d 从偏移 20 开始,占 2 字节,结束于偏移 21。

整体对齐校验:结构体最大成员为 8 字节,当前总大小 22 字节,不是 8 的倍数。因此在末尾填充 2 字节 padding,最终总大小为 24 字节。

最终内存布局

内容字节数偏移范围
成员 a10
padding 填充71-7
成员 b88-15
成员 c416-19
成员 d220-21
末尾 padding222-23

拓展优化:将结构体成员按 “从大到小” 的顺序排列,可以有效减少 padding 空洞,节省内存空间。

六、内存对齐的原因总结

  1. 直接原因:提升内存访问效率,优化缓存命中率。对齐的数据可以被 CPU 一次读取完成,避免多次读取和数据拼接开销。

  2. 根本原因:地址总线的硬件限制导致内存寻址并非 “完全自由”,内存对齐是软件层面对硬件限制的妥协与适配,是硬件成本与运行性能平衡的产物。

七、易错点整理

  1. 混淆 bit 和 Byte:注意大小写含义不同,1 字节 = 8 位,二者不可混用;

  2. 误以为 padding 是前面变量占用的空间:padding 是空白的内存空洞,不存储有效数据,仅用于占位对齐;

  3. 把内存对齐当成单纯的 “语法规则”:对齐本质是硬件底层的访问限制,编译器只是遵循硬件特性完成内存布局;

  4. 内存颗粒存储误区:每个存储单元存放 8bit(1 字节),不是 1bit;多字节数据会拆分存放在多个单元中。

八、课后思考

  1. 如果把上面的结构体成员顺序调整为 b、c、d、a,64 位平台下总大小是多少?

  2. 32 位平台下,同一个结构体的总大小会发生变化吗?为什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kelvins轩

感谢大佬投喂!暴富发财好运爆棚

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值