谈谈Keil-MDK编译输出的:Code-data,RO-data,RW-data,ZI-data( 其中有的可能分析得不太正确,希望可以得到大佬们的指点纠正)
浅谈Keil-MDK创建项目&编译过程Code-data,RO-data,RW-data,ZI-data 程序运行时加载过程
这些有个大概的了解即可,不用研究太细,主要是让你工程过大的时候规划哈数据怎么存放,硬件资源有限的,开发时得做好规划:
程序存储时占用的ROM区大小(内部Flash):
Code + RO-data + RW-data
程序执行时的只读区域(RO)(Flash) : Code + RO data
程序执行时的可读写区域(RW)(SRAM) : RW data + ZI data
首先来解释哈各表示什么含义
Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM 区(也就是STM32 的内部 Flash 中)
RO-data:Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在 ROM 区( STM32 内部 Flash ),因而程序不能修改其内容。例如 C 语言中 const 关键字定义的变量就是典型的 RO-data
RW-data:Read Write data,即可读写数据域,它指初始化为“非 0 值”的可读写数据,程序刚运行时,这些数据具有非 0 的初始值,且运行的时候它们会常驻在 RAM 区,因而应用程序可以修改其内容。例如 C 语言中使用定义的全局变量
,且定义时赋予“非 0 值”
给该变量 进行初始化
ZI-data:Zero Initialie data,即 0 初始化数据,它指初始化为“0 值”的可读写数据域,它与 RW-data 的区别是程序刚运行时这些数据初始值全都为 0,而后续运行过程与 RW-data 的性质一样,它们也常驻在 RAM 区,因而应用程序可以更改其内容。例如 C 语言中使用定义的全局变量,且定义时赋予“0 值”给该变量进行初始化(若定义该变量时没有赋予初始值,编译器会把它当 ZI-data 来对待,初始化为 0)
ZI-data 的栈空间(Stack)及堆空间(Heap):在 C 语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用 malloc 动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于 ZI-data 区域的,这些空间都会被初始值化为 0 值。
(这里值得注意的是栈和堆里面的没初始化的数据是随机数,自己仔细斟酌,通过测试 ZI-data的大小的确等于堆栈的大小之和)
编译器给出的 ZI-data 占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把堆空间计算在内)综上所述,以程序的组成构件为例,它们所属的区域类别如图:
程序组件所属类别机器指令Code常量RO-data初始非0的全局变量RW-data初值为0的全局变量ZI-data局部变量ZI-data 栈空间使用malloc动态分配的空间ZI-data 堆空间
完成了,定义的解释,接下来就来实战检验吧
首先建工程包含一个启动文件和main.c 文件
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00000400 ; 0x400 = 1024 Byte
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000200 ; 0x200 = 512 Byte
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
值得注意的堆栈的溢出,程序会进入一个中断里面时循环
这就对了,由于没有使用到堆,所以被编译器优化了ZI-data = 1024刚好是栈的大小(正确)
感兴趣你可以去研究哈自己实现 __use_two_region_memory 怎么写,可以去这里查它该怎么写:
来测测 RO-data 区域:主要测试const 关键字修饰的变量
Test01:
const unsigned char test = 1;
int main()
{
}
void SystemInit(){
}
编译结果显示:RO-data 没有变化,进行Test02 测试
Test02:
const unsigned char test = 1;
int main()
{
if( test == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
编译结果显示:RO-data 没有变化,进行Test03 测试
Test03:
const unsigned char test[1] = { 1 };
int main()
{
}
void SystemInit(){
}
编译结果显示:RO-data 没有变化,进行Test04 测试
Test04:
const unsigned char test[1] = { 1 };
int main()
{
if( test[0] == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
Test05:
int main()
{
const unsigned char test[1] = { 1 };
if( test[0] == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
编译结果显示:哈? RO-data 又回去了, 怎么回事? 在进行测试Test06
Test06:
int main()
{
const unsigned char test[2] = { 1 };
if( test[0] == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
Test07:
int main()
{
const unsigned char test = 1 ;
if( test == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
编译结果显示:RO-data 没有变化 所以我也是一串 ?????,测试Test08,Test09
Teat08:
const unsigned char test[ 1024 * 1024 ] = { 1 };
int main()
{
if( test[0] == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
Test09:
int main()
{
const unsigned char test[ 1024 * 1024 ] = { 1 };
if( test[0] == 1 ){
// // 打开 GPIOB 端口的时钟
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
Test08
Test09
再抛一个神奇的测试:
#define N 7
int main()
{
const char str[N] = "123456";
}
void SystemInit(){
}
N = 1,2, ; 测试结果
N = 3, 4 ; 测试结果
N = 5, 6 , 7, 8; 测试结果
N = 9, 10, 11, 12; 测试结果
哈,似乎发现了什么规律,再测一哈全局的
#define N 4
const char str[N] = "1";
int main()
{
if( str[0] == '1')
{
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
N = 1,2; 测试结果
N = 3,4; 测试结果
N = 9, 10; 测试结果
Test10:
#define N 10
const int str[N] = {1};
int main()
{
if( str[0] == 1)
{
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
RW-data --ZI-data 测试
int a = 1;
int main()
{
if( a == 1)
{
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
测试结果: RW-data 增加了
Test11:
unsigned char a[1024] = { 0 , 1 , 2};
int main()
{
if( a[0] == 1)
{
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
测试结果:RW-data 增加了1024
Test12:
unsigned char a[1024] = { 0};
int main()
{
if( a[0] == 1)
{
*( unsigned int * )0x40021018 |= ( (1) << 3 );
}
}
void SystemInit(){
}
测试结果:ZI-data 增加了1024
在 ZI-data 添加上堆的大小
#include <stdlib.h>
int main()
{
unsigned char * p;
p = (unsigned char *)malloc(10);
}
void SystemInit(){
}
最后温馨提示:如果定义了没有用到的变量,也就是比较冗余的变量的(编译器这么觉得),很有可能被编译器优化,自己注意一下
Code-data,RO-data,RW-data,ZI-data,程序运行时的加载过程
标签:RO,Code,ZI,int,unsigned,main,data From: https://blog.51cto.com/u_15918664/5956369