从单片机基础到程序框架(全集 2019pdf版).pdf - 第306页

第七十一节: 结构体的内存和赋值。 【71.1 结构体的内存生效。 】 上一 节讲到 结构 体有 三道标 准工 序“ 造模” 和“ 生成 ”和“ 调用 ” ,那 么,结 构体 在哪 道工 序的 时候 才 会开 始占用 内存(或 者说内 存生 效) ?答 案是在 第二 道工序 “生 成” ( 或者 说定 义) 的时候 才产 生内存 开销 。 第一道工序 仅“造模”不“ 生成”是不 会产生 内存的。什么 意思呢?请看 下面的例子。 第一种情…

100%1 / 836
其它部分的代码不要动。编译后,把程序下载进带串口 51 习板,通过电脑端的串口助手软件就可以观
察到不同的变量数值,详细方法请看第十一节内容。
第七十一节: 结构体的内存和赋值。
【71.1 结构体的内存生效。
上一节讲到结构体有三道标准工序“造模”和“生成”和“调用,那么,结构体在哪道工序的时候
会开始占用内存(或者说内存生效)?答案是在第二道工序“生成”或者说定义)的时候才产生内存开销
第一道工序仅“造模”不“生成”是不会产生内存的。什么意思呢?请看下面的例子。
第一种情况:仅“造模”不“生成”
struct StructMould //“造模
{
unsigned char u8Data_A;
unsigned char u8Data_B;
};
分析:这种情况是没有内存开销的,尽管你已经写下了数行代码,但是 C 编译器在翻译此代码的时候,
它会识别到你偷工减料仅仅“造模”而不“生成”新变量,此 C 编译器会把你这段代码忽略而过。
第二种情况:先“造模”再“生成”
struct StructMould //“造模
{
unsigned char u8Data_A;
unsigned char u8Data_B;
};
struct StructMould GtMould_1; //“生成”一个变 GtMould_1。占用 2 个字节内存
struct StructMould GtMould_2; //“生成”一个变 GtMould_2。占用 2 个字节内存
分析:这种情况才会占用内存。你“生成”变量越多,占用的内存就越大。像本例子,“生成”了两个
变量 GtMould_1 GtMould_2,一个变量占用 2 个字节,两个就一共占用 4 个字节。结论:内存的占用是
跟变量的“生成”有关。
【71.2 结构体的内存对齐。
什么是对齐?为了确保内存的地址能整除某个对齐倍数”(比如 4)如以 4 “对齐倍数”在地
0 存放一个变量 a,因为地址 0 能整“对齐倍数”4,所以符合地址对齐”接着往下再存放第二个变量 b,
紧接着的地 1 不能整除“对齐倍数”4,此时,为了内存对齐,本来打算把变量 b 放到地址 1 ,现在
要更改挪到地址 4 才符合“地址对齐”这就是内存对齐的含义。“对齐倍数”是什么?“对齐倍数”就是
片机的位数除以 8。比如 8 位单片机的“对齐倍数”是 1(8 除以 8),16 位单片机是 2(16 除以 8)32 位单
片机是 4(32 除以 8)本教程所用的单片机是 8 位的 51 内核单片机因此“对齐倍数” 1。1 是可以被
何整数整除的,因此,8 位单片机在结构体的使用上被内存对齐的“干扰”是最小的。
为什么要对齐?单片机内部硬件层面一条指令处理的数据宽度是固定的,比如,因为一个字节 8 位,
所以,8 位的单片机一次处理的数据宽度是 1 个字节(8 除以 8 等于 1)16 位的单片机一次处理的数据宽
2 个字节(16 位除以 8 等于 2)32 位的单片机一次处理的数据宽度是 4 个字(32 位除以 8 位等 4)
如果字节不对齐,本来单片机一个指令能处理的数据可能就要分解成 2 个指令甚至更多的指令,所以 C 编译
器为了让单片机处于最佳状态,在某些情况就会涉及内存对齐,结构体就涉及到内存对齐。
结构体的内存对齐表现在哪里呢?请看下面两个例子:
第一个例子:8 位单片机
struct StructMould_1 //“造模”
{
unsigned char u8Data; //一个 unsigned char 占用 1 个字节
unsigned long u32Data; //一个 unsigned long 4 个字节。
};
struct StructMould_1 GtMould_1; //占用多少个字节内存呢?
分析:GtMould_1 个变量占用多少个内存字节呢?假 GtMould_1 首地址是 0,那么地 0 存放
u8Data,u8Data 1 个字下来 1(0+1)了, 1 4
个字节的成 u32Data 吗?因为 8 位单片机的对齐倍数” 1(8 除以 8)那么地址 1 显然是可以整除“对
齐倍数”1 的,因此地址 1 是可以果断存储 u32Data 成员的。因此GtMould_1 占用的总字节数是 5(1+4)
也就是 u8Data u32Data 两者所占字节数之和。
第二个例子:32 位单片机。
struct StructMould_1 //“造模”
{
unsigned char u8Data; //一个 unsigned char 占用 1 个字节
unsigned long u32Data; //一个 unsigned long 4 个字节。
};
struct StructMould_1 GtMould_1; //占用多少个字节内存呢?
分析:GtMould_1 个变量占用多少个内存字节呢?假 GtMould_1 首地址是 0,那么地 0 存放
成员 u8Data,u8Data 占用 1 个字节,所以接下来的地址是 1(0+1)那么问题来了,地址 1 能直接存放占
4 个字节的成 u32Data ?不能。因 32 单片机的“对齐倍数” 4(32 8),那么地址 1 显然是
不可以整除“对齐倍数”4 的,因此,就要把地址 1 更改挪到地 4 这里才符合“地址对齐这样,就意味
着多插入了 3 “填充的字节因此GtMould_1 占用的总字节数是 8(1+3+4)也就“1 个字 u8Data,
3 个填充字节,4 u32Data”三者所占字节数之和。那么问题又来了如果把结构体内部成员 u8Data u32Data
的位置顺序更改一下,内存容量会有所改变吗?位置顺序更改后如下。
struct StructMould_1 //“造模”