---------------------------------------------------------------------------------------------------
字段说明:
Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区。
RO-data:Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。例如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占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用malloc动态申请堆空间,编译器会优化,不把堆空间计算在内)。
CODE+RO-data+ZI-data:是在单片机Flash中占用的大小
RW-data+ZI-data:是在单片机RAM中占用的大小
------------------------------------------------------------------------------------------------------
举例1:
如:void test(int x,int y)
{
printf("x+y=%d\r\n",x+y);
}
这样的代码编译后会放置到CODE段。
很多时候使用宏,会增加CODE大小,需要注意。
举例二:
unsigned char sendbuf[10]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
因为是已经初始化的buf,所以这样的代码编译会会放置到RW-data段。
举例三:
在嵌入式开发过程中,我们经常用用到没有字库的LCD或者数码管,这时就需要自己设计字符表。字符表通常不会被更改,所以会被设置成只读属性。如:
const unsigned char table[]={0xC0,0xF9,0xA4,0xB0,0x99, //0~4
0x92,0x82,0xF8,0x80,0x90, //5~9
0x88,0x83,0xA7,0xA1,0x86,0x8E}; //A~F
这样的代码编译后会放置在RO-data段。
举例四:
在驱动开发过程中,有时读写Flash可能会开辟一块全局buf如:
unsigned char testbuf[1024]={0};
这样的代码编译后会放置在ZI-data段。
-----------------------------------------------------------------------------------------------------
小建议
建议一:数码管、LCD字库设置为const比较保险,查表时比较稳定。
如果是C51单片机可以试试code关键字,很有意思。
建议二 :全局的buf不是越大越好,够用就可以了。如果嵌入式系统模块较多,你开辟了很多buf,随着开发进度的深入,你可能 会发现RAM不够用了,到时候再去挤bug,就很难找了。
建议三:调试的log,用完后最好注释掉,或者用帅气的宏开关屏蔽。因为这些log,一定占用了很多code段大小,所以如果 flash小的话,要小心咯。
举例:观察以下两个代码段,请问哪种代码段关闭log的方式比较好?
代码段一:
------------------------------------------
#define LOG_PRINT mylogprint
void mylogprint(char * string)
{
printf(string);
}
int fputc(int ch, FILE *f)
{}
int main(void)
{
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
while(1) {}
}
代码段二:
------------------------------------------
#define LOG_PRINT //mylogprint
void mylogprint(char * string)
{
printf(string);
}
int fputc(int ch, FILE *f)
{}
int main(void)
{
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
LOG_PRINT("HelloWord1");
LOG_PRINT("HelloWord2");
LOG_PRINT("HelloWord3");
while(1) {}
}
-------------------------------------------------------
我个人的理解,两种方式都能达到关闭log的效果。
但是在log很多的时候,用代码段二的方式,可以大大缩短实际编译的固件程序大小。我的一次亲身经历,是随着项目的深入,代码越来越多,后面发现flash快满了,这时我采用代码段二的方式关闭log,节省了将近40Kcode段大小。也就是flash上可以再写入40K的实际功能代码。对于Flash大小紧张的嵌入式芯片,40K的空间爽不爽?