对 51 单片机内存的认识,很多人有误解,最常见的是以下两种

  1. 超过变量128后必须使用compact模式编译,实际的情况是只要内存占用量不超过 256.0 就可以用 small 模式编译
  2. 128以上的某些地址为特殊寄存器使用,不能给程序用,与 PC 机不同,51 单片机不使用线性编址,特殊寄存器与 RAM 使用重复的重复的地址。但访问时采用不同的指令,所以并不会占用 RAM 空间。

由于内存比较小,一般要进行内存优化,尽量提高内存的使用效率。

以 Keil C 编译器为例,small 模式下未指存储类型的变量默认为data型,即直接寻址,只能访问低 128 个字节,但这  128 个字节也不是全为我们的程序所用,寄存器 R0-R7必须映射到低RAM,要占去 8 个字节,如果使用寄存组切换,占用的更多。

所以可以使用 data 区最大为 120 字节,超出 120 个字节则必须用 idata 显式的指定为间接寻址,另外堆栈至少要占用一个字节,所以极限情况下可以定义的变量可占 247 个字节。当然,实际应用中堆栈为一个字节肯定是不够用的,但如果嵌套调用层数不深,有十几个字节也够有了。

为了验上面的观点,写了个例子

#define LEN 120
data UCHAR tt1[LEN];
idata UCHAR tt2[];

void main()
{
    UCHAR i,j;

    ;  i < LEN; ++i )
    {
        j = i;
        tt1[j] = 0x55;
    }
}

可以计算 R0-7(8) + tt1(120) + tt2(127) + SP(1) 总共 256 个字节

keil 编译的结果如下:
Program Size: data=256.0 xdata=0 code=30
creating hex file from ".\Debug\Test"...
".\Debug\Test" - 0 Error(s), 0 Warning(s).
(测试环境为 XP + Keil C 7.5)

  这段代码已经达到了内存分配的极限,再定义任何全局变量或将数组加大,编译都会报错 107

  这里要引出一个问题:为什么变量 i、j 不计算在内?
  这是因为 i、j 是局部变量,编译器会试着将其优化到寄存器 Rx 或栈。问题也就在这了,如果局部变量过多或定义了局部数组,编译器无法将其优化,就必须使用 RAM 空间,虽然全局变量的分配经过精心计算没有超出使用范围,仍会产生内存溢出的错误!

  而编译器是否能成功的优化变量是根据代码来的
  上面的代码中,循环是臃肿的,变量 j 完全不必要,那么将代码改成

UCHAR i;
UCHAR j;

;  i < LEN; ++i )
{
    tt1[i] = 0x55;
}

再编译看看,出错了吧。因为编译器不知道该如何使用 j,所以没能优化,j 须占 RAM 空间,RAM 就溢出了。(智能一点的编译器会自动将这个无用的变量去掉,但这个不在讨论之列了)。另外,对 idata 的定义的变量最好放在 data 变量之后。

对于这一种定义

uchar c1;
idata uchar c2;
uchar c3;

变量 c2 肯定会以间接寻址,但它有可能落在 data 区域,就浪费了一个可直接寻址的空间。

变量优化一般要注意几点:

①让尽可能多的变量使用直接寻址,提高速度。假如有两个单字节的变量,一个长119的字符型数组,因为总长超过 120 字节,不可能都定义在 data 区。按这条原则,定义的方式如下:

data UCHAR tab[];
data UCAHR c1;
idata UCHaR c2;

但也不是绝的,如果 c1, c2 需要以极高的频率访问,而 tab 访问不那么频繁,则应该让访问量大的变量使用直接寻址:

data UCAHR c1;
data UCHaR c2;
idata UCHAR tab[];

这个是要根据具体项目需求来确定的

②提高内存的重复利用率
      就是尽可能的利用局部变量,局部变量还有个好处是访问速度比较快。由前面的例子可以看出,局部变量 i, j 是没有单独占用内存的子程序中使用内存数目不大的变量尽量定义为局部变量

③对于指针数组的定义,尽可能指明存储类型
       尽量使用无符号类型变量
      一般指针需要一个字节额外的字节指明存储类型
     8051 系列本身不支持符号数,需要外加库来处理符号数,一是大大降低程序运行效率,二是需要额外的内存

④避免出现内存空洞
      可以通过查看编译器输出符号表文件(.M51)查看
      对前面的代码,M51文件中关于内存一节如下:

* * * * * * *   D A T A   M E M O R Y   * * * * * * *
REG     0000H     0008H     ABSOLUTE     "REG BANK 0"
DATA    0008H     0078H     UNIT         ?DT?TEST
IDATA   0080H     007FH     UNIT         ?ID?TEST
IDATA   00FFH     0001H     UNIT         ?STACK

第一行显示寄存器组0从地址0000H开始,占用0008H个字节
第二行显示DATA区变量从0008H开始,占用0078H个字节
第三行显示IDATA区变量从0080H开始,占用007F个字节
第四行显示堆栈从00FFH开始,占0001H个字节

由于前面代码中变量定义比较简单,且连续用完了所有空间,所以这里显示比较简单
变量定义较多时,这里会有很多行

如果全局变量与局部变量分配不合理,就有可能出现类似下面的行

0010H     0012H                  *** GAP ***
      该行表示从0010H开始连续0012H个字节未充分利用或根本未用到。出现这种情况最常见的原因是局变量太多、多个子程序中的局部变量数目差异太大、使用了寄存器切换但未充分利用

浅谈C51内存优化的更多相关文章

  1. 浅谈mysql配置优化和sql语句优化【转】

    做优化,我在这里引用淘宝系统分析师蒋江伟的一句话:只有勇于承担,才能让人有勇气,有承担自己的错误的勇气.有承担错误的勇气,就有去做事得勇气.无论做什么事,只要是对的,就要去做,勇敢去做.出了错误,承担 ...

  2. 浅谈MySQL中优化sql语句查询常用的30种方法 - 转载

    浅谈MySQL中优化sql语句查询常用的30种方法 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中使 ...

  3. 浅谈C++ 内存泄漏及其检测工具

    浅谈C++ 内存泄漏及其检测工具 http://wenku.baidu.com/link?url=1DGkOOvd_ITZyB8IHAwfhCOx2tfO6id8UfuyQkAMHZU6sasaAXz ...

  4. 80C51存储器与C51内存优化

    80C51在物理结构上有四个存储空间:片内程序存储器.片外程序存储器.片内数据存储器和片外数据存储器.但在逻辑上,即从用户使用的角度上,80C51有三个存储空间:片内外统一编址的64KB的程序存储器地 ...

  5. (转)运维角度浅谈MySQL数据库优化

    转自:http://lizhenliang.blog.51cto.com/7876557/1657465 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架 ...

  6. 运维角度浅谈MySQL数据库优化(转)

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

  7. 从运维角度浅谈 MySQL 数据库优化

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

  8. 运维角度浅谈MySQL数据库优化

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

  9. 浅谈SQL语句优化经验

    (1) 选择最有效率的表名顺序(只在基于规则的seo/' target='_blank'>优化器中有效):ORACLE 的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后 ...

随机推荐

  1. JSP学习笔记(一):JDK的安装及环境变量的配置

    一.JDK的安装. JDK可以在Oracle(甲骨文)的官网下载,连接地址:http://www.oracle.com/technetwork/java/javase/downloads/index- ...

  2. SpringMVC进行文件的上传以及多文件的上传(转)

    基本的SpringMVC的搭建在我的上一篇文章里已经写过了,这篇文章主要说明一下如何使用SpringMVC进行表单上的文件上传以及多个文件同时上传的步骤 SpringMVC 基础教程 框架分析:htt ...

  3. (转)Maven实战(五)坐标详解

    1.为什么要定义Maven坐标      在我们开发Maven项目的时候,需要为其定义适当的坐标,这是Maven强制要求的.在这个基础上,其他Maven项目才能应用该项目生成的构件. 2.Maven坐 ...

  4. POJ3080:Blue Jeans

    Description The Genographic Project is a research partnership between IBM and The National Geographi ...

  5. DBParameter比拼接字符串慢的解决办法

    List<DBParameter> param = new List<DBParameter>(){       new DBParameter("@Question ...

  6. linux内核--中断处理程序

    一个设备的中断处理程序是它设备驱动程序的一部分--设备驱动程序是用于对设备进行管理的内核代码.中断处理程序与其他内核函数的真正区别在于,中断处理程序是被内核调用来响应中断的,而它们运行于我们称之为中断 ...

  7. 1033 - Merging Maps

    Pictures taken from an airplane or satellite of an ar ea to be mapped are often of sufficiently high ...

  8. 【转】iOS实时卡顿监控

    转自http://www.tanhao.me/code/151113.html/ 在移动设备上开发软件,性能一直是我们最为关心的话题之一,我们作为程序员除了需要努力提高代码质量之外,及时发现和监控软件 ...

  9. MVC4数据访问EF查询linq语句的时候报错找不到表名问题

    一天做项目的时候遇到这样的问题,MVC4用EF访问数据查询用linq语句的时候报错找不到表名:报错如下图: 研究了几种情况,最后还是没有找到正真的问题所在,不过可能是和路由解析问题有关,暂时还没有进行 ...

  10. 让页脚footer永远固定在页面的底部,而不是永远固定在显示器屏幕的底部的方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...