原创 C51指针类型和存储区的关系

2007-4-3 18:46 4921 5 7 分类: MCU/ 嵌入式

C51指针类型和存储区的关系详解(转贴)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


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


    data     --->    可寻址片内ram
    bdata    --->   
可位寻址的片内ram
    idata    --->   
可寻址片内ram,允许访问全部内部ram
    pdata    --->   
分页寻址片外ram (MOVX @R0) (256 BYTE/)
    xdata    --->   
可寻址片外ram (64k 地址范围)
    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个字节的内存空间,地址是外ram0x00000x0009
    ......


    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)        ;0x080x09是在内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;0x080x0A是在内ram区分配的pstr指针变量的地址空间
    MOV 0x09, #tmp(0x00)
    MOV 0x0A, #tmp(0x00)


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


    6种情况:


    uchar * pstr;
    pstr="tmp";


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


    MOV DPTR, #0x000A        ;0x000A0x000C是在外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空间了又增加了指针类型的分辨值。


    小结一下:大家看到了以上的6种情况,其中效率最高的是第2种情况,既可以正确访问ram区又节约了代码,效率最差的是第 6种,但不是说大家只使用第2种方式就可以了,还要因情况而定,一般说来应用51系列的系统架构的内部ram资源都很紧张,最好大家在定义函数内部或程序段内部的局部变量使用内ram,而尽量不要把全局变量声明为内ram区中。所以对于全局指针变量我建议使用第3 种情况,而对于局部的指针变量使用第2种方式。


    C51是很灵活的,也很好理解和使用,但要成为笑傲江湖的一代高手还是要多想多练,没有实际项目的锻炼是不容易提高的。希望这篇文章对大家一点用处


 

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

tengjingshu_112148725 2009-4-12 22:44

很好,我转载到我blog了

用户569793 2008-8-26 18:42

太精彩了!!正缺这点知识呢,纳闷了好久!!

daiqiumiao_806246054 2008-8-15 23:23

硬件部分见: http://blog.ednchina.com/likee/143654/message.aspx

daiqiumiao_806246054 2008-8-15 23:22

方案部分网址: http://blog.ednchina.com/likee/132484/message.aspx

用户1438129 2008-8-15 17:04

恭喜

用户95766 2008-8-12 02:00

不错,终于完成了。
相关推荐阅读
用户1122550 2008-09-19 22:42
Allegro封装(焊盘)制作
    在Allegro系统中,建立一个零件(Symbol)之前,必须先建立零件的管脚(Pin)。元件封装大体上分两种,表贴和直插。针对不同的封装,需要制作不同的Padstack。Allegro中Pa...
用户1122550 2008-07-03 17:32
VC\GCC宏一览
C\C++宏大全(转)一、标准预定义宏The standard predefined macros are specified by the relevant language standards, ...
用户1122550 2008-07-03 17:06
ARM编译器中预定义的宏
*****************************************************ARM编译器中预定义的宏***********************************...
用户1122550 2007-08-02 10:26
分散加载描述文件
        分散加载描述文件供ARM-ADS链接器使用,用来决定各个代码段和数据段的存储位置,下面为一个添加注释后的.scf文件例子:;YL-LPC2294片内FLASH分散加载文件;Intern...
用户1122550 2007-08-02 10:13
Linux重引导
把光盘中的DOSUTILS文件复制到你的硬盘上,然后再在计算机启动时进入DOS下,进入DOSUTILS目录运行:loadlib autoboot\vmlinuz root="/dev/hd"**就可以...
用户1122550 2007-08-02 09:22
ADS中生成的映像文件内部三种输出段:RO段、RW段和ZI段
一个程序包括只读的代码段和可读写的数据段。在ARM的集成开发环境中,只读的代码段和常量被称作RO段(ReadOnly);可读写的全局变量和静态变量被称作RW段(ReadWrite);RW段中要被初始化...
我要评论
2
5
关闭 站长推荐上一条 /3 下一条