打印

【分享】keil c51编译器的一些使用心得

[复制链接]
3010|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lovelyegle|  楼主 | 2009-12-3 23:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
现在的存储器已经不像七八年前那样昂贵了,但是ram相对于rom和eeprom的价格还是不可同样看待的,所以程序中节省内存在现在看来还是非常关键的。原因有以下几点:
1.ram的存取速度相对于eeprom的存取速度要快很多倍,不在一个数量级上,主要是因为eeprom的存储要想写入就必须先擦除,而且eeprom的擦出需要成块擦除(这是由于eeprom的擦除原理是场效应管的栅极上电擦除的,为了节省成本厂家一般都是8Bytes/page 64Bytes/page),所以使用ram来处理中间的数据是能够符合速度要求的。
2.无论是xram还是eeprom都是外部存储器,在负值时都要用到16bit地址空间(8位机),这样无形中就增大了程序的code的体积并且使得速度上也受到影响,所以尽量把indata区的ram用到极限是非常有意义的。
本人总结了一些节省内存的规律,提供给大家讨论一下,看看是否可行。
1.内存分配的基本原理:
keil与其他的c语言编译器我认为从内存分配的原理上是基本相同的。总结起来,其实很简单,就是选最长的路径进行编译(话糙理不糙),例如下面的两段程序
program 1:
unsigned char a();
void b();
void main()
{
unsigned char byte1;
unsigned char byte2;
byte1 = byte2 = 3;
if( a() == 3)
{
   b();
}
a();
return;
}
// a function
unsigned char a()
{
unsigned char byte_a1;
unsigned char byte_a2;
byte_a1 = byte_a2 = 3;
byte_a1 = 4;
byte_a2 = 5;
return byte_a1;
}
// b function
void b()
{
unsigned char byte_b1;
unsigned char byte_b2;
unsigned char byte_b3;
byte_b1 = byte_b2 = byte_b3 = 3;
return;
}
program 2:
void a();
void b();
void main()
{
unsigned char byte1;
unsigned char byte2;
byte1 = byte2 = 3;
a();
return;
}
// a function
void a()
{
unsigned char byte_a1;
unsigned char byte_a2;
byte_a1 = byte_a2 = 3;
if (byte_a1 == 3)
{
   b();
}
byte_a1 = 4;
byte_a2 = 5;
return;
}
// b function
void b()
{
unsigned char byte_b1;
unsigned char byte_b2;
unsigned char byte_b3;
byte_b1 = byte_b2 = byte_b3 = 3;
return;
}
两段程序的作用是相同的,都是先执行函数a,然后根据byte_a1的值判断去执行b程序,但是用keil编译的结果却不相同program 1 编译的结果是data:14 code:48,而program 2 编译的结果是data:16 code:56,可见program 1 比 program 2 即节省了code又节省了内存。
看一下反汇编代码,就可以了解原因了,在a函数中调用b函数,a函数定义的byte_a1和byte_a2变量没有被释放,所以program 2 的内存分配是 8(SFR) + 1(STACK) + 2(MAIN FUNC) + 2(A FUNC) + 3(B FUNC) = 16 Bytes,而program 1 的内存分配是 8(SFR) + 1(STACK) + 3(B FUNC) = 14Bytes, 由于B函数和A函数是并行的,所以节省了a函数需要的2个字节。
这样总结看来程序不要串行,应尽量并行,充分利用有限的ram资源,这样既可以使code区变小,也可以使速度变快。
沙发
lovelyegle|  楼主 | 2009-12-3 23:02 | 只看该作者
2.uncalled segment 影响内存分配:
不知道大家是否发现过当存在没有调用的函数时,内存空间很有可能会溢出,这个原因其实也非常简单例如:
program 3:
void a();
void b();
void c(unsigned char byte_input);
void main()
{
unsigned char byte1;
unsigned char byte2;

byte1 = byte2 = 3;

a();

c(3);

return;
}

// a function
void a()
{
unsigned char byte_a1;
unsigned char byte_a2;

byte_a1 = byte_a2 = 3;

if (byte_a1 == 3)
{
   b();
}

byte_a1 = 4;
byte_a2 = 5;

return;
}

// b function
void b()
{
unsigned char byte_b1;
unsigned char byte_b2;
unsigned char byte_b3;

byte_b1 = byte_b2 = byte_b3 = 3;

return;
}

void c(unsigned char byte_input)
{
unsigned char byte_c;

byte_c = byte_input;

return;
}

program 3 所示如果再main.c里面调用c(3)编译后data:16,而如果不调用c(3),编译后data:17,原因是调用c(3) data = 8(SFR) + 1(STACK) + 2(MAIN FUNC) + 2(A FUNC) + 3(B FUNC) = 16Bytes,而如果不调用c(3) data = 8(SFR) + 1(STACK) + 2(MAIN FUNC) + 2(A FUNC) + 3(B FUNC) + 1(C FUNC) = 17 Bytes。
所以,建议大家如果暂时不调用的函数最好屏蔽掉,以免影响整体的内存分配。

这次先写到这里吧,希望大家多和我讨论讨论,下一次我想和大家讨论一下有关keil中data_group的问题。

//-------------------------

对于 "uncalled segment 影响内存分配" 这个问题,发表一点看法(因为彼人也非JSJ毕业).
程序源文件(c, a51文件),先经过编译得到obj文件,所谓的目标文件.各个obj文件就是一个个的模块,每个模块基本上都含有代码段和数据段,也就是说,代码在rom里面要占用多少CODE空间,数据在ram里面要占用多少ram空间等等信息.我以为lib文件也和obj文件类似,只是文件结构有些不一样.
obj(lib)文件然后经过l51.exe(bl51.exe),就是说把可执行代码模块根据连接定位参数地址上连接在一起();数据段也连接在一起,在ram空间中分配.对ram空间的分配中就有一个连接过程"覆盖分析".调用一个c函数,就会为这个函数所使用的ram空间进行分配(一些局部变量),这个函数返回时再回收分配给他的ram空间,根据函数互相之间的调用前后关系,编译器就可以时实的知道ram空间的使用情况(其中就存在一个函数重入的问题),作为连接时ram空间分配的参数. 如果源文件中的函数(模块)从来没有被任何函数显示的调用(所谓非显示调用就是这段代码,连接器目前还不知道这段代码什么时候会被调用或是否会被调用),连接时就会为它分配永远有效的ram空间(就象全局变量),不会被回收.

使用特权

评论回复
板凳
suoma| | 2009-12-4 21:49 | 只看该作者
好资料!

使用特权

评论回复
地板
David_ming| | 2009-12-5 10:47 | 只看该作者
EEPROM和FLASH是不一样滴~~楼主写错了吧

使用特权

评论回复
5
wangwo| | 2009-12-6 16:13 | 只看该作者
收下了,没事了仔细看看

使用特权

评论回复
6
huamunv| | 2009-12-10 22:17 | 只看该作者
精神可嘉!

使用特权

评论回复
7
god_like| | 2009-12-10 22:20 | 只看该作者
不错,好东西,学习了

使用特权

评论回复
8
love_life| | 2009-12-10 22:41 | 只看该作者
写的很实在

使用特权

评论回复
9
幸福至上| | 2014-11-27 12:18 | 只看该作者
最近本来也在找关于Keil C51内存分配机制的知识。之前我以为如果是裸机程序,那么调用子函数后的局部变量空间是不会被释放的,会保留不变,等着下次调用子函数时,再调用该变量地址进行数据操作。现在看了楼主这篇**,明白了两点:1、Keil C51编译子程序时,局部变量被调用后,是会释放的,不会一直占着数据空间,这点确实有利于RAM空间的有效利用。2、在一个子函数A中,再调用其他子函数B、C,因为子函数运行期间,对应的局部变量是不能被释放的,所以如果A中调用B,那么编译器必须分配足够A,B局部变量的空间,如果再调C的话,更需要C的局部变量的空间。而并行的话,那么A调用完释放掉的空间又可以供B程序用,这样,编译器在编译时,确实会少分配一些空间。3、如果子程序不被调用,那么编译器无法确定该子程序何时被调用,必须为该子程序的局部变量保留足够的有效空间,这是一种机制,所以不用的子程序,最好先屏蔽掉。:)

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

640

帖子

1

粉丝