现在的存储器已经不像七八年前那样昂贵了,但是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 = ;

    )
    {
       b();
    }
    a();
    return;
}

// a function
unsigned char a()
{
    unsigned char byte_a1;
    unsigned char byte_a2;
    byte_a1 = byte_a2 = ;
    byte_a1 = ;
    byte_a2 = ;
    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 = ;
    return;
} 
//program 2:
void a();
void b();
void main()
{
    unsigned char byte1;
    unsigned char byte2;
    byte1 = byte2 = ;
    a();
    return;
}

// a function
void a()
{
    unsigned char byte_a1;
    unsigned char byte_a2;
    byte_a1 = byte_a2 = ;
    )
    {
       b();
    }
    byte_a1 = ;
    byte_a2 = ;
    return;
}

// b function
void b()
{
    unsigned char byte_b1;
    unsigned char byte_b2;
    unsigned char byte_b3;
    byte_b1 = byte_b2 = byte_b3 = ;
    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区变小,也可以使速度变快。

  2、uncalled segment 影响内存分配:
  不知道大家是否发现过当存在没有调用的函数时,内存空间很有可能会溢出,这个原因其实也非常简单例如:

//program :
void a();
void b();
void c(unsigned char byte_input);
void main()
{
    unsigned char byte1;
    unsigned char byte2;
    byte1 = byte2 = ;
    a();
    c();
    return;
}

// a function
void a()
{
    unsigned char byte_a1;
    unsigned char byte_a2;
    byte_a1 = byte_a2 = ;
    )
    {
       b();
    }
    byte_a1 = ;
    byte_a2 = ;
    return;
}

// b function
void b()
{
    unsigned char byte_b1;
    unsigned char byte_b2;
    unsigned char byte_b3;
    byte_b1 = byte_b2 = byte_b3 = ;
    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空间(就象全局变量),不会被回收

keil c51编译器的一些使用心得的更多相关文章

  1. 深入剖析keil c51 --- 从汇编到c51

    第一节 main()函数和启动代码 汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和 ...

  2. Keil C51中函数指针的使用

    函数指针在C语言中应用较为灵活.在单片机系统中,嵌入式操作系统.文件系统和网络协议栈等一些较为复杂的应用都大量地使用了函数指针.Keil公司推出的C51编译器是事实上80C51 C编程的工业标准,它针 ...

  3. keil c51中C程序的启动过程

    汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接.下面看看它和main()函数是 ...

  4. keil MDK编译器警告和错误详解(不定期更新)

    工作后从单片机转成ARM,刚开始用ADS1.2编译器,用了一段时间,因为我接手的项目的老程序正是用ADS编译的,部门也大都在用.在学单片机的时候用的是keil c51编译器,ads和这个编译器在易用性 ...

  5. keil c51笔记

    第一章 Keil C51开发系统基本知识 第一节 系统概述 Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上.结构性.可读性. ...

  6. 关于Keil C51中using关键字的使用心得

    刚才看到一位很牛的师兄写的一篇日志中提到了Keil C51中using这个关键字的用法,粗心的我本来一直都没有留意它是用来干嘛的(因为我一般看见它都是在中断服务函数的定义开头处,好像没有了它也可以中断 ...

  7. Keil C51总线外设操作问题的深入分析

    阅读了<单片机与嵌入式系统应用>2005年第10期杂志<经验交流>栏目的一篇文章<Keil C51对同一端口的连续读取方法>(原文)后,笔者认为该文并未就此问题进行 ...

  8. KEIL C51 中嵌入汇编以及C51与A51间的相互调用

    如何在 KEIL C51(v6.21) 中调用汇编函数的一个示例 有关c51调用汇编的方法已经有很多帖子讲到,但是一般只讲要点,很少有对整个过程作详细描述,对于初学者是不够的,这里笔者通过一个简单例子 ...

  9. KEIL C51高级编程

    第一节 绝对地址访问C51提供了三种访问绝对地址的方法: 1. 绝对宏:在程序中,用“#include”即可使用其中定义的宏来访问绝对地址,包括:CBYTE.XBYTE.PWORD.DBYTE.CWO ...

随机推荐

  1. cf493D Vasya and Chess

    D. Vasya and Chess time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. javascript笔记6之函数

    /* function box() { //函数的声明 alert('我只有被调用才可以执行!'); //函数本身没有运行功能 } //必须调用才可以执行 box(); //调用 function b ...

  3. 第32讲 UI组件之 时间日期控件DatePicker和TimePicker

    第32讲 UI组件之 时间日期控件DatePicker和TimePicker 在Android中,时间日期控件相对来说还是比较丰富的.其中, DatePicker用来实现日期输入设置,    Time ...

  4. 在CentOS中编译安装VIM 7.3

    默认安装的 Vim 不带有多字符支持,所以不支持中文.无论是将 CentOS 本来的语系改为中文还是将 Vim 的语系设置改为中文,都不能正常显示中文.为了在 Vim 中能够正常处理中文,我们需要在编 ...

  5. cocos2d移植到安卓引入第三方so文件时候编译会删除解决方式

    在游戏中对接支付的SDK的时候引入支付的so文件的时候在编译的时候总是被删除,后来经过查找资料自己整理出了一个解决方式 方案例如以下 在项目导入安卓中之后.在相应的jni目录中创建一个prebuilt ...

  6. Wikioi 1080一维树状数组

    半个月时间最终把那些杂七杂八的学完了,尽管学完也,也仅仅是有了个模板,自己手敲还是不太行.所以如今開始要疯狂刷题了! ! .!!! 这题裸的树状数组.曾经写那道<敌兵布阵>的时候写过,所以 ...

  7. VMware vSphere 5.5的12个更新亮点(1)

    [IT专家网虚拟化]在VMworld 2013大会上发布的VMware vSphere 5.5版本提供的增强和改进,横跨从hypervisor到管理整个堆栈,提升了VMware的性能.可伸缩性和可用性 ...

  8. 如何给你的Android 安装文件(APK)瘦身

    如何给你的Android 安装文件(APK)瘦身 本文翻译自:Putting Your APKs on Diet           原作者:Cyril Mottier Android的apk文件越来 ...

  9. IPMI 配置BMC用户设置

    IPMI 配置BMC用户设置 本文档共介绍5条ipmi设置user的命令,这些命令需要使用root权限才能使用,其中- H为需要操作的BMC ip,-I lanplus为使用rmcp+协议发送命令,- ...

  10. SpinLock(自旋锁)

    SpinLock(自旋锁) SpinLock 结构是一个低级别的互斥同步基元,它在等待获取锁时进行旋转. 在多核计算机上,当等待时间预计较短且极少出现争用情况时,SpinLock 的性能将高于其他类型 ...