通过上面的阅读,我们知道下载到MCU上的程序代码已开始堵在Flash(Rom)上,上电运行后,变量会被搬运到SRAM(RAM)上,然后由CPU运行(const 修饰的变量除外)。
1. 内存分配
内存分配(这里说的是 RAM 上)的方式有四种,对应的也就有四个区域:
- 常量区分配 。内存在程序编译的时候就已经分配好,在程序结束释放。
- 静态(Static)区分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,程序结束释放。 如全局变量、static变量。
- 栈(HEAP)上创建。在执行函数时,函数内局部变量就在栈上创建,函数执行结束时这些存储单元伴随着栈帧的弹出自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
-
堆(STACK)上分配,亦称动态内存分配。程序在运行的时候用
malloc/calloc
申请的内存,需要手动使用free
释放内存,否则容易造成内存泄漏,优点是申请内存灵活。
全局变量和static修饰的变量都会被保存到 静态数据区 。静态数据区又分 RW-data 和 ZI-data 两块。其中有被初始化的变量放在RW-data中(如 static int a = 10;
),未被初始化的变量放在ZI-data中(如 static int a;
)。
1.1 样例
#include <stdio.h>
#include <stdlib.h>
char buff[50]; // 静态区 -> ZI-data
int count = 1; // 静态区 -> RW-data
int main()
{
int a = 10; // 栈区
int b; // 栈区
static int c = 1; // 静态区 -> RW-data
char *p1 = "abcd"; // abcd\0 存放在常量区,p1在栈上
char *p2 = "abcd";
int *p3; // p3在栈上
printf("p1 指向地址=%p\n", p1);
printf("p1 自身地址=%p\n", &p1);
printf("p2 指向地址=%p\n", p2);
printf("p2 自身地址=%p\n", &p2);
p3 = (int *)malloc(2 * sizeof(int)); // p3指向的int数组地址,在堆区
printf("p3 指向地址=%p\n", p3);
printf("p3 自身地址=%p\n", &p3);
free(p3);
return 0;
}
输出===
p1 指向地址=0x10552af36
p1 自身地址=0x7ffeea6d8508
p2 指向地址=0x10552af36
p2 自身地址=0x7ffeea6d8500
p3 指向地址=0x7fefa3405940
p3 自身地址=0x7ffeea6d84f8
因为是在64位系统运行,p1、p2、p3自身地址是连续的,并且相差8个字节。
p1、p2指向的"abcd"是同一个地址,说明"abcd"在常量区。
2. 实例
2.1 51单片机
以8位的8051单片机(STC12C5A60S2)数据存储器(SRAM)为例。
2.1.1 内部RAM
内部RAM共256字节,分为三部分:
- 低128字节(与传统8051兼容)。可直接寻址,可间接寻址。
- 高128字节(与8052兼容)。只能间接寻址。
- 特殊功能寄存器(SFRs)。与高128共用地址映射,物理上独立,通过不同汇编指令区分。
低128字节可分为三个区:
- 工作寄存器组区:00H~1FH,共32字节,分4个寄存器组,每组编号R0~R7。程序状态PSW寄存器中的RS1、RS0决定当前使用那个寄存器组。
- 可位寻址区:20H~2FH,共16个字节,128个位,这些位对应的地址是00H~7FH,低128字节RAM寻址和这128个位寻址是使用不同指令区分。
- 用户RAM区/堆栈区:30H~7FH,共80个字节,用户RAM区和堆栈区共用同一段地址,堆栈指针SP用于指向堆栈区的栈顶(建议初始化后,设置SP从80H开始)。
2.1.2 内部扩展RAM
内部扩展RAM共1024字节(0000H~03FFH),C语言中使用xdata
声明存储类型。如 unsigned char xdata i = 0;
。
data
: 指定数据存储在内部低128字节数据存储器区(00H~7FH),在定义变量时不指定存储区域,则默认为data类型。这部分存储区可片内直接寻址,速度最快。由于空间较小,所以只有频繁用到或对访问速度要求很高的变量才可放在data区,比如for循环中的计数值。栈内变量一般也可放到这里。
idata
: 指定数据存储在内部低256字节数据存储器区(00H~FFH)。
pdata
: 指定数据存储在内部/外部扩展RAM中(00H~FFH,最大256字节),使用 MOVX @Ri指令访问 @Ri 只能访问00H-FFH单元。
xdata
: 指定数据存储在内部/外部扩展RAM中(0000H~FFFFH,最大64KB),可用 MOVX @DPTR访问。对于不会被频繁访问或者对访问速度要求不高的变量可放在此区域中。
code
: 指定数据存储在代码存储区,数据是在编译后跟代码一起写入代码区,运行过程中不可更改。