文档库 最新最全的文档下载
当前位置:文档库 › keil c语言编程常见错误分析

keil c语言编程常见错误分析

keil c语言编程常见错误分析
keil c语言编程常见错误分析

1. Warning 280:?i?:unreferenced local variable

说明局部变量i 在函数中未作任何的存取操作解决方法消除函数中i 变量的宣告及即定义的参数在程序中并未调用

2 Warning 206:?Music3?:missing function-prototype

说明Music3( )函数未作宣告或未作外部宣告所以无法给其他函数调用

解决方法将叙述void Music3(void)写在程序的最前端作宣告如果是其他文件的函数则要写成extern void Music3(void),即作外部宣告

3Error:318:can?t open file …beep.h?

说明在编译C:\8051\MANN.C 程序过程中由于main.c 用了指令#i nclude “beep.h”,但却找不到所致解决方法编写一个beep.h 的包含档并存入到c:\8051 的工作目录中

4 Error 237:?LedOn?:function already has a body

说明LedOn( )函数名称重复定义即有两个以上一样的函数名称

解决方法修正其中的一个函数名称使得函数名称都是独立的

5 ***WARNING 16:UNCALLED SEGMENT,IGNORED FOR OVERLAY PROCESS

SEGMENT: ?PR?_DELAYX1MS?DELAY

说明DelayX1ms( )函数未被其它函数调用也会占用程序记忆体空间

解决方法去掉DelayX1ms( )函数或利用条件编译#if …..#endif,可保留该函数并不编译

6 ***WARNING 6 :XDATA SPACE MEMORY OVERLAP

FROM : 0025H

TO: 0025H

说明外部资料ROM 的0025H 重复定义地址

解决方法外部资料ROM 的定义如下Pdata unsigned char

XFR_ADC _at_0x25 其中XFR_ADC 变量的名称为0x25,请检查是否有其它的变量名称也是定义在0x25 处并修正它

7 WARNING 206:?DelayX1ms?: missing function-prototype C:\8051\INPUT.C

Error 267 :?DelayX1ms …:requires ANSI-style prototype

C:\8051\INPUT.C

说明程序中有调用DelayX1ms 函数但该函数没定义即未编写程序内容或函数已定义但未作宣告

解决方法编写DelayX1ms 的内容编写完后也要作宣告或作外部宣告可在delay.h 的包含档宣告成外部以便其它函数调用

8 ***WARNING 1:UNRESOLVED EXTERNAL SYMBOL SYMBOL:MUSIC3

解决办法:

1.是文件没有添加到工程里。

2.可能是因为存在没有被调用的已经定义的函数。

3.不知道你有没有把Source group组下面的A51.C删掉,如果没有删,在A51.c 上点右键,选择remove file " ".

4.建一个新的c文件,里面写一个空的函数,把该文件添加到project中,注意该文件不能再选generate assembler SRC file和assemble SRC file选项。重新编译工程,如果警告该函数没被调用,在主文件中调一下。

5.建一个新的c文件,把主文件中的几个函数移至该文件,把该文件添加到project中,注意该文件不能再选generate assembler SRC file和assemble SRC file选项。重新编译工程

9***WARNING 2:REFERENCE MADE TO UNRESOLVED EXTERNAL

SYMBOL:MUSIC3

MODULE:C:\8051\MUSIC.OBJ(MUSIC)

ADDRESS:0018H

在MUSIC3函数里面MUSIC这个参数有使用,没有申明。或者申明了没有实体。也就是说对于这个参数,编译器无法解析。

10 ***ERROR 107:ADDESS SPACE OVERFLOW

SPACE: DATA

SEGMENT: _DATA_GOUP_

LENGTH: 0018H

说明data空间已经不够用,原因是你可能有好多函数,而函数内部的局部变量又没有定义其空间,这种情况下,系统会将变量分配到你在Otions for Target 对话框里的设置的空间。如果你在下图所示中的Memory Model 里设置成Small:variables in DATA,则DATA空间很快便用完,导致data空间不够用。解决的办法有两种,一是通过更改Memory Model设置,可以设置成pdata或xdata,以便有足够大的空间,但这又带来新的问题,程序运行速度减慢,而且code代码也会加大,因为如果一个局部变量被存放在了xdata空间,汇编语言访问xdata空间的代码大小要比访问data空间的代码大,变量一旦很多,程序的代码也会逐渐增大;二是根据自己的要求设置变量的空间。所以这涉及到代码优化的问题,遇到具

体问题时,在运行速度和代码大小之间取得适合自己的情况

Project---->Option for target---->BL51 Locate选项卡,如上图红圈部分所示,根据自己系统的存储器分布情况,可以设置代码区间和XDATA区间。通常默认情况下,代码区间很小,所以会造成107号错误,根据需求,调大该范围即可。

11ERROR L105: PUBLIC REFERS TO IGNORED SEGMENT SYMBOL: USARTDATACOUNT

SEGMENT: ?DT?USART_READWRITE

Ram空间不足:

外部变量:定义处不用加External,声明处要加External

将以data 型别定义的公共变量修改为idata 型别的定义

12***ERROR 118: REFERENCE MADE TO ERRONEOUS EXTERNAL

SYMBOL: VOLUME

MODULE: C:\8051\OSDM.OBJ (OSDM)

ADDRESS: 4036H

定义了某某函数或全部变量在不同文件里面想调用它,却在包含头文件里面少了extern语句,或只有主程序和包含头文件没有(EXTERN......定义语句(函数)).

如果调试时有些if结构里的语句符合条件没有执行,或者某些语句不符合条件也被执行,那是因为if和else里有相同的语句,编译的时候作优化处理了。

13WARNING 15 (MULTIPLE CALL TO SEGMENT) 症状

原因

Warning 15向我们表明了linker发现了一个函数,这个函数不仅在main code里被调用了,而且在ISR(或者被ISR调用的函数中)被调用了。或者是被同时被多个ISR同时调用了。

这样会产生一个问题,就是在此函数不是一个可重入函数,而当此函数已经在执行时它可能被另一个ISR所调用。这样就会导致结果是可变的而且很可能会导致一些参数的错误。

另一个问题就是本地变量和参数所使用的内存可能被其他函数的内存覆盖。如果函数是由中断所调用的,则此函数的内存就会被使用。这会引起其它函数的内存错误。

解决方法

有几种方法去解决这个问题

如果你100%确认这个函数的两个副本都不会同时执行(如果此函数是被main调用并且中断是未被使能的)并且此函数没有使用内存(只使用的寄存器),那么你就可以忽略此警告

如果此函数使用了内存,你就要使用OVERLAY directive来将此函数从覆盖分析(overlay anaysis)中移除。举例如下:

OVERLAY (?PR?_WRITE_GMVLX1_REG?D_GMVLX1 ! *)

如上语句能阻止被此函数使用的内存遭到覆盖。如果这个函数调用了你程序中其他的在别处的函数,那么你可能需要将这些函数也排除在覆盖分析之外。

如果当此函数在执行时可以被调用,那么事情就会变得比较的复杂。你可能需要:无论何时当从main中调用此函数时,需要关闭中断。你可能需要对被调用的函数使用#pragma disable。你也必须使用

OVERLAY directive将此函数从overlay analysis中移除。为此函数创建两个副本。一个给main,一个给ISR。

使此函数可重入。

14E:\VCWORK\2815.C(826): error C236: '_wrbyte': different length of parameter lists

子函数里的形参声明的方式不对,需要每个参数都定义一下类型

E:\VCWORK\2815.C(743): error C183: unmodifiable lvalue

出现error C183: unmodifiable lvalue的错误,最后发现时存在一个数组是uchar code xx[5],后边把它用作接受串口的缓冲区,显示uchar code是不能改变的,是写在rom中的。应该改成uchar xx[5],这是写在ram中的

原因:修改了不能改变的变量,

E:\VCWORK\2815.C(799): error C242: 'array[]': too many initializers

15 ERROR L104: MULTIPLE PUBLIC DEFINITIONS SYMBOL: _WRITE_DATA MODULE: .\ds18b20start.obj (DS18B20

c/c++语言中有很多地方要用到extern,但是如果没有真正的了解它的意义,会给编程带来很大的麻烦,为了使大家少走弯路,特祥细的说明一下。

对于比较小的程序,一般只有一个c文件和一个头文件,全局变量我们通常会直接定义在c文件中,在程序之前加int i定义。如果要在头文件中定义有以下两种方法:用extern来声明:extern int i;这一句只是对变量i进行声明,在c 文件的程序之前必须加上int i进行定义。extern int i=0;这一句声明和定义都做了。

对于大一点的程序,有很多c文件和头文件,这个时候全局变量就必须在头文件中声明(不需要初始化),然后在一个c文件中定义(该初始化的要初始化)。如果在头文件中定义,则编译的时候会出现重复定义的错误。如果只有头文件中声明就会出现没有定义有警告。

*** ERROR L104: MULTIPLE PUBLIC DEFINITIONS

SYMBOL: K

MODULE: 222.obj (222)

出现上述错误则是因为变量k重复定义,把你的头文件中的变量定义前加extern(只是变量声明不用初始化),再在某一个你要调用该变量的c文件的程序之前再定义(注意第一个调用的c文件要负责附带初始化该变量,其他调用的c 文件就不需要初始化过程啦)

14MAIN.C(85): warning C259: 'parameter': pointer: different mspace

原因,函数调用时候的实参和声明时候的形参存储空间不同,修改成一致即可。

16 E:\VC\2815\2815\FTOC.C(32): warning C231: '_memcpy': attempt to redefine intrinsic function

17*** ERROR L121: IMPROPER FIXUP

访问内存指令超出指令的寻址范围了,例如MOVX @Ri 指令超出了PDATA段的范围,或者是ACALL 指令超出了2k的寻址范围.

检查你的调用子函数的命令.特别是那些LCALL,ACALL等

18*** WARNING L2: REFERENCE MADE TO UNRESOLVED EXTERNAL

SYMBOL: MAIN

MODULE: C:\KEIL\C51\LIB\C51S.LIB (?C_INIT)

ADDRESS: 080DH

在main函数里面C_INIT这个参数有使用,没有申明。或者申明了没有实体。也就是说对于这个参数,编译器无法解析。

19 keil4 warning C316:unterminated conditionals

今天用Keil4写程序时遇到这个问题:warning C316:unterminated conditionals

跑了几个论坛,在审视了一遍代码之后,知道了原因:

像类似XX.C(99):warning C316:unterminated conditionals这种警告的话XX.c文件有一个凌乱的条件编译或预编译。因为C语言中有些头文件中的预编译或宏定义,那么条件编译就避免不了。写条件编译时,可能有忘写一个基本的语句。比如说,你用了条件编译#ifndef而忘记写#endif。因为他们本来就是配套的。有前者必有后者。不能丢掉其中任何一个。一个include文件最后的#endif 少了# 前缀或者没有#endif,都会出现类似警告。就像你写C语句,你不会写了int i 而不能忘记写" ; " ,否者就不能把一个语句表达完整。

总之,出现上述问题。先看看整个C文件中是否出现上述错误,或整个工程中自己写的那些头文件中里面的条件编译是否都写对了,即:前面写了#ifndef,后面是否有对应的#endif。

20 DS1302.C(86): error C183: unmodifiable lvalue

code的内容只能读,不能改. 定义数组时把code去掉。

21 keil编译警告…Argument':conversion:pointer to

non-pointer是什么问题

应该是参数传递错误,指针参数处传递了非指针参数。

22 *** ERROR L114: SEGMENT DOES NOT FIT

块大小与目标设备不符。段溢出了,你的DATA区超过了256字节

你的idata变量太大(CEH),与器件容量不匹配。可能你的单片机型号选成31了,选个256字节内部RAM的应该就行,将定义为data的变量定义为xdata类型,问题解决了。

23 error C193 :bad operand type

% 取模不能用浮点数,

frequence 要转成整型来取模,小数位可以乘10后转整型来得到。

24 常见错误

error 1: Out of memory 内存溢出

error 2: Identifier expected 缺标识符

error 3: Unknown identifier 未定义的标识符

error 4: Duplicate identifier 重复定义的标识符

error 5: Syntax error 语法错误

error 6: Error in real constant 实型常量错误

error 7: Error in integer constant 整型常量错误

error 8: String constant exceeds line 字符串常量超过一行

error 10: Unexpected end of file 文件非正常结束

error 11: Line too long 行太长

error 12: Type identifier expected 未定义的类型标识符

error 13: Too many open files 打开文件太多

error 14: Invalid file name 无效的文件名

error 15: File not found 文件未找到

error 16: Disk full 磁盘满

error 17: Invalid compiler directive 无效的编译命令

error 18: Too many files 文件太多

error 19: Undefined type in pointer def 指针定义中未定义类型

error 20: Variable identifier expected 缺变量标识符

error 21: Error in type 类型错误

error 22: Structure too large 结构类型太长

error 23: Set base type out of range 集合基类型越界

error 24: File components may not be files or objectsfile分量不能是文件或对象

error 25: Invalid string length 无效的字符串长度

error 26: Type mismatch 类型不匹配

error 27:error 27:Invalid subrange base type 无效的子界基类型

error 28:Lower bound greater than upper bound 下界超过上界

error 29:Ordinal type expected 缺有序类型

error 30:Integer constant expected 缺整型常量

error 31:Constant expected 缺常量

error 32:Integer or real constant expected 缺整型或实型常量

error 33:Pointer Type identifier expected 缺指针类型标识符

error 34:Invalid function result type 无效的函数结果类型

error 35:Label identifier expected 缺标号标识符

error 36:BEGIN expected 缺BEGIN

error 37:END expected 缺END

error 38:Integer expression expected 缺整型表达式

error 39:Ordinal expression expected 缺有序类型表达式

error 40:Boolean expression expected 缺布尔表达式

error 41:Operand types do not match 操作数类型不匹配

error 42:Error in expression 表达式错误

error 43:Illegal assignment 非法赋值

error 44:Field identifier expected 缺域标识符

error 45:Object file too large 目标文件太大

error 46:Undefined external 未定义的外部过程与函数error 47:Invalid object file record 无效的OBJ文件格式

error 48:Code segment too large 代码段太长

error 49:Data segment too large 数据段太长

error 50:DO expected 缺DO

error 51:Invalid PUBLIC definition 无效的PUBLIC定义

error 52:Invalid EXTRN definition 无效的EXTRN定义

error 53: Too many EXTRN definitions 太多的EXTRN定义error 54:OF expected 缺OF

error 55:INTERFACE expected 缺INTERFACE

error 56:Invalid relocatable reference 无效的可重定位引用

error 57:THEN expected 缺THEN

error 58:TO or DOWNTO expected 缺TO或DOWNTO error 59:Undefined forward 提前引用未经定义的说明error 61:Invalid typecast 无效的类型转换

error 62:Division by zero 被零除

error 63:Invalid file type 无效的文件类型

error 64:Cannot read or write variables of this type 不能读写此类型变量error 65:Pointer variable expected 缺指针类型变量

error 66:String variable expected 缺字符串变量

error 67:String expression expected 缺字符串表达式

error 68:Circular unit reference 单元UNIT部件循环引用

error 69:Unit name mismatch 单元名不匹配

error 70:Unit version mismatch 单元版本不匹配

error 71:Internal stack overflow 内部堆栈溢出

error 72:Unit file format error 单元文件格式错误

error 73:IMPLEMENTATION expected 缺IMPLEMENTATION

error 74:Constant and case types do not match 常量和CASE类型不匹配

error 75:Record or object variable expected 缺记录或对象变量

error 76:Constant out of range 常量越界

error 77:File variable expected 缺文件变量

error 78:Pointer expression expected 缺指针表达式

error 79:Integer or real expression expected 缺整型或实型表达式

error 80:Label not within current block 标号不在当前块内

error 81:Label already defined 标号已定义

error 82:Undefined label in preceding statement part 在前面未定义标号

error 83:Invalid @ argument 无效的@参数

error 84:UNIT expected 缺UNIT

error 85: ";" expected 缺“;”

error 86: ":" expected 缺“:”

error 87: "," expected 缺“,”

error 88: "(" expected 缺“(”

error 89: ")" expected 缺“)”

error 90: "=" expected 缺“=”

error 91: ":=" expected 缺“:=”

error 92: "[" or "(." Expected 缺“[”或“(.”

error 93: "]" or ".)" expected 缺“]”或“.)”

error 94: "." expected 缺“.”

error 95: ".." expected 缺“..”

error 96:Too many variables 变量太多

error 97:Invalid FOR control variable 无效的FOR循环控制变量

error 98:Integer variable expected 缺整型变量

error 99:Files and procedure types are not allowed here 该处不允许文件和过程类型

error 100:String length mismatch 字符串长度不匹配

25 error C2085: 'Delete' : not in formal parameter list 不在形参列表中,意思是在函数定义后丢掉了‘;’

25..\QXJ\TEST.C(371): error C100: unprintable character 0xA1 skipped

在语句后面出现了不该出现的字符。

二、C语言浮点数的存储方式

C语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明

一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE 的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。

无论是单精度还是双精度在存储中都分为三个部分:

1.符号位(Sign) : 0代表正,1代表为负

2.指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位

存储

3.尾数部分(Mantissa):尾数部分

其中float的存储方式如下图所示:

而双精度的存储方式为:

R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25*,而120.5可以表示为:1.205*,这

些小学的知识就不用多说了吧。而我们傻蛋计算机根本不认识十进制的数据,他只认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:1110110.1用二进制的科学计数法

表示1000.01可以表示为1.0001*,1110110.1可以表示为1.1101101*,任何

一个数都的科学计数法表示都为1.xxx*,尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里,那24bit能精确到小数

点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据+127,下面就看看8.25和120.5在内存中真正的存储方式。

首先看下8.25,用二进制的科学计数法表示为:1.0001*

按照上面的存储方式,符号位为:0,表示为正,指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:

而单精度浮点数120.5的存储方式如下图所示:

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:

根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*=120.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的

下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果

float f = 2.2f;

double d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

f = 2.25f;

d = (double)f;

Console.WriteLine(d.ToString("0.0000000000000"));

可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?其实通过上面关于两种存储结果的介绍,我们已经大概能找到答案。首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数*2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.8*2=1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为:

但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果

三、xdata idata pdata data

DATA: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。

IDATA:固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。

IDATA是用类似C中的指针方式访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c 中IDATA做指针式的访问效果很好)

XDATA: 外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。

PDATA: 外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。这个比较特殊,而且C51好象有对此BUG, 建议少用。但也有他的优点,具体用法属于中级问题,这里不提。

STARTUP.A51 的作用,和汇编一样,在C中定义的那些变量和数组的初始化就在STARTUP.A51中进行,如果你在定义全局变量时带有数值,如unsigned char dA Ta xxx="100";,那STARTUP.A51中就会有相关的赋值。如果没有=100, STARTUP.A51就会把他清0。(STARTUP.A51==变量的初始化)。这些初始化完毕后,还会设置SP指针。对非变量区域, 如堆栈区,将不会有赋值或清零动作。有人喜欢改STARTUP.A51为了满足自己一些想当然的爱好,这是不必要的,有可能错误的。比如掉电保护的时候想保存一些变量, 但改STARTUP.A51来实现是很笨的方法,实际只要利用非变量区域的特性,定义一个指针变量指向堆栈低部:0xff处就可实现。, 为什么还要去改? 可以这么说:任何时候都可以不需要改STARTUP.A51,如果你明白它的特性。

bit是在内部数据存储空间中20H .. 2FH 区域中一个位的地址,这在DA TA的20H以后以字节形式出现,可互相参照。另外加上8051 可寻址的SFR,但刚刚试过,只是00H--7FH起作用,也就是说当数据有变化时颜色变红,以后的从80H到--FFH就不是位寻址区了,是位寻址的特殊寄存器,如涉及到了可位寻址的那11个当然会有反应。复位后,程序计数器PC的内容为0000H,内部RAM各单元的值不确定。各功能寄存器的复位值如下:堆栈指针SP的复位值为07H,累加器ACC、寄存器B的复位值为00H,数据指针DPTR的复位值为0000H,而p0、p1、p2、p3四个口的复位值为0FFH。其他SFR如PSW、TCON、TMOD、TL0、TH0、TL1、TH1的复位值也为00H。

wave中是低128字节和高128字节(0-7FH),低128字节是片内RAM区,高128字节(80-FFH)是SFR(特殊功能寄存器)bit则是位于低128字节的20H .. 2FH 区域,即data的20H .. 2FH 区域

code是在0000H .. 0FFFFH 之间的一个代码地址。

我用ORG 5000H

TAB: DB 22H,3BH,43H,66H,5H,6DH,88H后,

CODE从5000H开始以后变成DB各位

DATA是在0 到127 之间的一个数据存储器地址,或者加128 .. 255 范围内的一个特殊功能寄存器(SFR)地址。两者访问的方式不同。实际上由于PSW的复位设置PSW.3=RS0和PSW.4=RS1皆为0,所以通用工作寄存器区就是第0区,所以data的00--07H部分是与REG栏中的R0--R7

对应的。以后的则仅代表低128字节的内部RAM。

IDATA是0 to 255 范围内的一个IDATA存储器地址, IDATA与DA TA重合低128字节,有的地方只有DATA表示256字节的片内RAM,

XDATA 是0-65535 范围内的一个XDATA 存储器地址。

指针类型和存储区的关系详解

一、存储类型与存储区关系

data ---> 可寻址片内ram

bdata ---> 可位寻址的片内ram

idata ---> 可寻址片内ram,允许访问全部内部ram

pdata ---> 分页寻址片外ram (MOVX @R0) (256 BYTE/页)

xdata ---> 可寻址片外ram (64k 地址范围FFFFH)

code ---> 程序存储区(64k 地址范围),对应MOVC @DPTR

二、指针类型和存储区的关系

对变量进行声明时可以指定变量的存储类型如:

uchar data x和data uchar x相等价都是在内ram区分配一个字节的变量。同样对于指针变量的声明,因涉及到指针变量本身的存储位置和指针所指向的存储区位置不同而进行相应的存储区类型关键字的使用如:

uchar xdata * data pstr

是指在内ram区分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata 区("*"前xdata关键字的作用),可能初学C51时有点不好懂也不好记。没关系,我们马上就可以看到对应“*”前后不同的关键字的使用在编译时出现什么情况。

uchar xdata tmp[10]; //在外ram区开辟10个字节的内存空间,地址是外ram的0x0000-0x0009 .第1种情况:

uchar data * data pstr;

pstr="tmp";

首先要提醒大家这样的代码是有bug的, 他不能通过这种方式正确的访问到tmp空间。为什么?我们把编译后看到下面的汇编代码:

MOV 0x08,#tmp(0x00) ;0x08是指针pstr的存储地址

看到了吗!本来访问外ram需要2 byte来寻址64k空间,但因为使用data关键字(在"*"号前的那个),所以按KeilC编译环境来说就把他编译成指向内ram的指针变量了,这也是初学C51的朋友们不理解各个存储类型的关键字定义而造成的bug。特别是当工程中的默认的存储区类为large 时,又把tmp[10] 声明为uchar tmp[10] 时,这样的bug是很隐秘的不容易被发现。

第2种情况:

uchar xdata * data pstr;

pstr = tmp;

这种情况是没问题的,这样的使用方法是指在内ram分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用)。编译后的汇编代码如下。MOV 0x08,#tmp(0x00) ;0x08和0x09是在内ram区分配的pstr指针变量地址空间

MOV 0x09,#tmp(0x00)

这种情况应该是在这里所有介绍各种情况中效率最高的访问外ram的方法了,请大家记住他。第3种情况:

uchar xdata * xdata pstr;

pstr="tmp";

这中情况也是对的,但效率不如第2种情况。编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A,0x000B是在外ram区分配的pstr指针变量地址空间

MOV A, #tmp(0x00)

MOV @DPTR, A

INC DPTR

MOV A, #tmp(0x00)

MOVX @DPTR, A

这种方式一般用在内ram资源相对紧张而且对效率要求不高的项目中。

第4种情况:

uchar data * xdata pstr;

pstr="tmp";

如果详细看了第1种情况的读者发现这种写法和第1种很相似,是的,同第1 种情况一样这样也是有bug的,但是这次是把pstr分配到了外ram区了。编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A是在外ram区分配的pstr指针变量的地址空间

MOV A, #tmp(0x00)

MOVX @DPTR, A

第5种情况:

uchar * data pstr;

pstr="tmp";

大家注意到"*"前的关键字声明没有了,是的这样会发生什么事呢?下面这么写呢!对了用齐豫的一首老歌名来说就是“请跟我来”,请跟我来看看编译后的汇编代码,有人问这不是在讲C51吗?为什么还要给我们看汇编代码。C51要想用好就要尽可能提升C51编译后的效率,看看编译后的汇编会帮助大家尽快成为生产高效C51代码的高手的。还是看代码吧!

MOV 0x08, #0X01 ;0x08-0x0A是在内ram区分配的pstr指针变量的地址空间

MOV 0x09, #tmp(0x00)

MOV 0x0A, #tmp(0x00)

注意:这是新介绍给大家的,大家会疑问为什么在前面的几种情况的pstr指针变量都用2 byte 空间而到这里就用3 byte空间了呢?这是KeilC的一个系统内部处理,在KeilC中一个指针变量最多占用 3 byte空间,对于没有声明指针指向存储空间类型的指针,系统编译代码时都强制加载一个字节的指针类型分辩值。具体的对应关系可以参考KeilC的help中C51 User's Guide。第6种情况:

uchar * pstr;

pstr="tmp";

这是最直接最简单的指针变量声明,但他的效率也最低。还是那句话,大家一起说好吗!编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A-0x000C是在外ram区分配的pstr指针变量地址空间MOV A, #0x01

MOV @DPTR, A

INC DPTR

MOV DPTR, #0x000A

MOV A, #tmp(0x00)

MOV @DPTR, A

INC DPTR

MOV A, #tmp(0x00)

MOVX @DPTR, A

这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。

相关文档