谈谈Keil 中C51的内存分配与优化
| 本帖最后由 Cresta 于 2014-1-21 10:49 编辑
看到这篇C51的内存分配和优化的文章,个人觉得分析的十分到位,在这里转给大家
C51的内存分配不同于一般的PC,内存空间有限,采用覆盖和共享技术。在Keil编译器中,经过编译后,会形成一个M51文件,在其内部可以详细的看到内存的分配情况。
C51内存常见的两个误区:
(1) 变量超过128字节后必须用COMPACT模式。
其实,只要不超过256字节,都可以用SMALL模式
(2) 内部RAM,128字节以上的是SFR用,不给程序用。
其实,由于C51寻址的不同,高128字节也可以用来存储变量,虽与SFR地址相同,但寻址的方式不同。
下面通过几个程序来看内存的分配。
*******************************************************************************
//程序1:
#include <reg51.h>
void main()
{}
Program Size: data=9.0 xdata=0 code=16
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
IDATA 0008H 0001H UNIT ?STACK
*******************************************************************************
从上面可以看到,即使程序内部无任何变量和函数data也会为9.0。这9个字节内存分别为R0-R8和一个堆栈指针(C51的堆栈是“grow up”,即使堆栈中没有内容,也会有一个栈底指针)。data区中由于R0-R8占有8个存储空间,因此data区最大为120字节(栈在所有的变量空间 之后),如果超过120个字节则由idata显式的指定为间接寻址。对于整个内部256字节的RAM,在极端的情况下,最大的变量为247字节。
当定义全局变量时
*******************************************************************************
//程序2:
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar a;
uint b;
void main()
{}
Program Size: data=12.0 xdata=0 code=16
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0003H UNIT ?DT?MAIN
IDATA 000BH 0001H UNIT ?STACK
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 000CH UNIT ?C_C51STARTUP
CODE 000FH 0001H UNIT ?PR?MAIN?MAIN
*******************************************************************************
存在全局变量时,根据全局变量的类型分配相应的存储空间。
看下面的程序
*****************************************************************
//程序3:
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar a;
uint b;
uint sum(uint c)
{
uint d;
d = c;
return d;
}
void main()
{}
Program Size: data=12.0 xdata=0 code=17
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0003H UNIT ?DT?MAIN
IDATA 000BH 0001H UNIT ?STACK
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 000CH UNIT ?C_C51STARTUP
CODE 000FH 000CH UNIT ?PR?MAIN?MAIN
CODE 001BH 0001H UNIT ?PR?_SUM?MAIN
********************************************************************
与上面的程序想比较,发现内存并没有任何的变化。
看下面的程序
***************************************************************************
//程序4:
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar a;
uint b;
uint sum(uint c)
{
uint d;
d = c;
return d;
}
void main()
{
b = sum(5);
}
Program Size: data=12.0 xdata=0 code=28
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0003H UNIT ?DT?MAIN
IDATA 000BH 0001H UNIT ?STACK
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 000CH UNIT ?C_C51STARTUP
CODE 000FH 000CH UNIT ?PR?MAIN?MAIN
CODE 001BH 0001H UNIT ?PR?_SUM?MAIN
****************************************************************************
这与上面的内存使用相同,在这个程序中通过反汇编,查看编译后的汇编程序可以发现,参数的传递通过通用寄存器完成,没有占用新的内存。编译器将其优 化的通用寄存器(寄存器一般传递3个参数,超过3个参数时,多余的参数通过分配空间地址的方式来访问。但是分配的内存空间包含了寄存器传递的3个参数在内 的所有参数的空间。详见《Parameter And Local Variable 》和《Parameter and Register》)和栈中(程序7),但是如果参数或局部变量过多,则情况就完全不同(程序6)。
再看下面的程序
*******************************************************************
//程序5
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar a;
uint b;
uint sum(uint c)
{
uint d;
}
void main()
{
}
Program Size: data=16.0 xdata=0 code=21
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0004H UNIT ?DT?_SUM?MAIN
DATA 000CH 0003H UNIT ?DT?MAIN
IDATA 000FH 0001H UNIT ?STACK
* * * * * * * C O D E M E M O R Y * * * * * * *
CODE 0000H 0003H ABSOLUTE
CODE 0003H 000CH UNIT ?C_C51STARTUP
CODE 000FH 0005H UNIT ?PR?_SUM?MAIN
CODE 0014H 0001H UNIT ?PR?MAIN?MAIN
******************************************************************
同样的程序,内部RAM的使用情况又发生了变化。函数未被调用,且参数与局部变量都没有使用。
源文件经过编译后形成obj文件,各个obj文件就是一个模块,每个模块中都含有代码段和数据段,也就是,代码在ROM占有多少CODE空间,数据在RAM里占用多少空间等信息。
obj(lib)文件然后经过l51.exe(bl51.exe),就是说把可执行代码模块根据连接定位参数地址上连接在一起();数据段也连接在 一起,在ram空间中分配.对ram空间的分配中就有一个连接过程"覆盖分析".调用一个c函数,就会为这个函数所使用的ram空间进行分配(一些局部变 量),这个函数返回时再回收分配给他的ram空间,根据函数互相之间的调用前后关系,编译器就可以时实的知道ram空间的使用情况(其中就存在一个函数重 入的问题),作为连接时ram空间分配的参数. 如果源文件中的函数(模块)从来没有被任何函数显示的调用(所谓非显示调用就是这段代码,连接器目前还不知道这段代码什么时候会被调用或是否会被调用), 连接时就会为它分配永远有效的ram空间(就象全局变量),不会被回收。
*******************************************************************
//程序6
#include<reg51.h>
#define uint unsigned int
#define uchar unsigned char
uchar a;
uint b;
uint sum2(uint e,uint f,uint g,uint j)
{
uint i;
i = e+f+g+j;
return i;
}
void main()
{
b = sum2(1,2,3,4);
}
Program Size: data=20.0 xdata=0 code=58
TYPE BASE LENGTH RELOCATION SEGMENT NAME
-----------------------------------------------------
* * * * * * * D A T A M E M O R Y * * * * * * *
REG 0000H 0008H ABSOLUTE "REG BANK 0"
DATA 0008H 0008H UNIT _DATA_GROUP_
DATA 0010H 0003H UNIT ?DT?MAIN
IDATA 0013H 0001H UNIT ?STACK
**************************************************************
在上面的程序中如果将sum2中的j去掉,那么data = 12.0。
通过程序5和6可以看到,两个程序的M51文件中的蓝色部分的segement name的名称有很大的区别。_DATA_GROUP_是一个OVERALY GROUPS(覆盖组)。它是链接器产生的可覆盖的一个数据段。而上面的程序5中的?DT?_SUM?MAIN,则是一个函数段。
在Keil中对OVERALY GROUPS的解释为:
When performing overlay analysis, the linker creates groups of segments that are overlaid. The group name indicates the memory type of the variables that it includes.
Group Name
Segment
Prefix
Memory
Model
Description
_BIT_GROUP_
?BI?
All
Variables and arguments of type bit.
_DATA_GROUP_
?DT?
SMALL
Variables and arguments other than bit.
_PDATA_GROUP_
?PD?
COMPACT
Variables and arguments other than bit.
_XDATA_GROUP_
?XD?
LARGE
Variables and arguments other than bit.
When the linker overlays function data memory and creates a group, that groups appears in the linker map file's memory map section.
START STOP LENGTH ALIGN RELOC MEMORY CLASS SEGMENT NAME
======================================================================
* * * * * * * * * * * D A T A M E M O R Y * * * * * * * * * * * * *
000000H 000007H 000008H --- AT.. DATA "REG BANK 0"
000008H 00001AH 000013H BYTE UNIT DATA _DATA_GROUP_
00001BH 00001BH 000001H BYTE UNIT IDATA ?STACK
Groups are created based on the memory model of the function (which specifies the memory class where parameters and variables are stored) and on any variables defined with a specific memory space.
在keil生成的M51函数中有OVERLAY MAP OF MODULE,对程序中函数调用的覆盖有详细的说明。
//程序7
#define LEN 120
data uchar tt1[LEN];
idata uchar tt2[127];
void main()
{
uchar i,j;
for(i = 0; i < LEN; ++i )
{
j = i;
tt1[j] = 0x55;
}
}
变量i和j通过编译器的优化占用了通用寄存器,R0-8[8]+tt1[120]+tt2[127]+SP[1]共256字节;
而如果将程序7中j=i删掉。上面声明了uchar j,但是下面没有应用,因此编译器不知道该如何处理j,就让其占用了一个RAM空间不再存在通用寄存器中,出现了内存溢出。(有的编译器会将不用的变量自动删除)
局部变量占用通用寄存器,变量在声明时就被分配了空间;局部变量只有在被声明、赋值且被使用后才认为是局部变量被放置在通用寄存器中,否则被认为是全局变量;在循环中,R7中放置循环数。(自加)
由于data区的存取速度快,变量尽量的放在该区,但是由于idata可以访问整个256字节的RAM,如果data区变量较少,idata型的变量也会占有data区,减少了可直接访问的存储空间,因此在变量定义的时候尽量将idata型的变量定义在data型变量后。
uchar c1;
idata uchar c2;
uchar c3;
变量 c2 肯定会以间接寻址,但它有可能落在 data 区域,就浪费了一个可直接寻址的空间
变量的优化
(1) 将访问频率高的变量放在data区
(2) 提高内存的复用率。尽可能的利用局部变量,由于局部变量的存取速度快。在程序7中可以看到,i和j没有占用内存,子程序中使用内存数量不大的变量尽量定义为局部变量。
(3) 对于指针数组的定义,尽量指明存储类型。尽量使用无符号类型变量。8051不支持有符号数计算,需要通过其他的库来处理,大大降低了运行的速度,增加了内存的利用。
(4) 避免出现内存空洞。在M51中可以很清楚的看到内存的分配情况,如果全局变量和局部变量的分配不合理,有时会出现下面的情况:
0010H 0012H *** GAP ***
表示从0010开始有0012H个字节未利用。造成这种情况的原因是局变量太多、多个子程序中的局部变量数目差异太大、使用了寄存器切换但未充分利用
(5)避免出现未使用的变量或者函数。
|
谈谈Keil 中C51的内存分配与优化的更多相关文章
- Keil C51内存分配与优化
C51的内存分配不同于一般的PC,内存空间有限,采用覆盖和共享技术.在Keil编译器中,经过编译后,会形成一个M51文件,在其内部可以详细的看到内存的分配情况. C51内存常见的两个误区: A.变量超 ...
- Java 中数组的内存分配
Java 中数组的内存分配 1.Java 程序在运行时,需要在内存中分配空间.为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据和内存管理方式. 2.数组基本概念 数组是 ...
- 第10课 C++中的动态内存分配
C++中的动态内存分配 C语言是通过库函数来完成动态内存分配的,而C++是通过关键字从语言层面支持的. C语言中的malloc是基于字节来进行内存申请的,C++中是基于类型来进行的. delete加上 ...
- JAVA中堆栈和内存分配原理
1.栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量 ...
- JAVA中堆栈和内存分配
(一).栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或 ...
- 从malloc中窥探Linux内存分配策略
malloc函数是C/C++中常用内存分配库函数,本篇文章将以Linux平台上的malloc为剖析对象,深入了解分配一块内存的旅程. malloc入门 使用malloc,需要包含头文 ...
- 解析STL中典型的内存分配
1 vector 在C++中使用vector应该是非常频繁的,但是你是否知道vector在计算内存分配是如何么? 在c++中vector是非常类似数组,但是他比数组更加灵活,这就表现在他的大小是可以自 ...
- JAVA中堆栈和内存分配详解(摘抄)
在Java中,有六个不同的地方可以存储数据: 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存 ...
- C#中字符串的内存分配与驻留池
完全引用http://www.cnblogs.com/instance/archive/2011/05/24/2056091.html 驻留池:是一张记录了所有在代码中使用字面量声明的字符串实例的引用 ...
随机推荐
- 查看git提交细节
git log git show fdf39277f54dd0484a9fefc012463924544e07af
- 二维码PDF417简介及其解码实现(zxing-cpp)
二维码PDF417是一种堆叠式二维条码.PDF417条码是由美国SYMBOL公司发明的,PDF(Portable Data File)意思是"便携数据文件".组成条码的每一个条码字 ...
- 33 -jQuery 属性操作,文档操作(未完成)
- 3931: [CQOI2015]网络吞吐量
3931: [CQOI2015]网络吞吐量 链接 分析: 跑一遍dijkstra,加入可以存在于最短路中的点,拆点最大流. 代码: #include<cstdio> #include< ...
- 提交到开源git时出现:fatal: refusing to merge unrelated histories的解决办法
解决办法 创建本地库和fetch远程分支这些前面的步骤这里略过.可以自行百度. 解决办法: 1.cmd进入项目的根目录. 2.执行下面的命令:git pull origin master --al ...
- 洛咕 P4131 [WC2005]友好的生物
洛咕 P4131 [WC2005]友好的生物 首先可以发现\(C\)是没有用的,可以乘进所有的权值里面做 考虑没有最后一维的限制,那么两个生物的友好值就是 \(\sum_{i=1}^k|a_i-b_i ...
- ubuntu 图形化界面 gui 桌面版 root登录 sorry,that didn't work.please try again! 抱歉,认证失败。请重试
出现这种问题,用下面的方法就行了 https://jingyan.baidu.com/article/bad08e1e224b2709c85121f1.html 而且我发现,因为我用的是英文版的ubu ...
- angular 服务 service factory provider constant value
angular服务 服务是对公共代码的抽象,由于依赖注入的要求,服务都是单例的,这样我们才能到处注入它们,而不用去管理它们的生命周期. angular的服务有以下几种类型: 常量(Constant): ...
- python面试题(三)
一.项目技术点梳理 (一)Django项目 本项目是用python的Django框架开发的前后端不分离项目.项目采用MVT架构,使用的MySQL和redis数据库,用Django自带的orm与数据库交 ...
- 128 C语言实现文件复制功能(包括文本文件和二进制文件)
文件的复制是常用的功能,要求写一段代码,让用户输入要复制的文件以及新建的文件,然后对文件进行复制.能够复制的文件包括文本文件和二进制文件,你可以复制1G的电影,也可以复制1Byte的txt文档. 实现 ...