《指针类型和存储区的关系详解》对在keilC51中使用指针的研究
- 格式:doc
- 大小:27.00 KB
- 文档页数:9
C51指针类型和存储区的关系详解2007-01-11 22:59一、存储类型与存储区关系data ---> 可寻址片内rambdata ---> 可位寻址的片内ramidata ---> 可寻址片内ram,允许访问全部内部rampdata ---> 分页寻址片外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个字节的内存空间,地址是外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。
c51指针用法
C51是一种常用的单片机,使用指针可以更灵活地操作内存和外设。
在C51中,指针可以用于访问和操作内存中的数据,也可以用于操作外设寄存器。
首先,我们可以使用指针来访问和操作内存中的数据。
在C51中,内存被分为不同的存储区域,如代码区、数据区和堆栈区。
通过使用指针,我们可以直接访问和修改这些存储区域中的数据。
例如,我们可以使用指针来读取和修改数组中的元素,而不需要使用数组索引。
这种方法可以提高程序的执行效率。
其次,指针还可以用于操作外设寄存器。
在单片机编程中,外设寄存器用于控制和配置外部设备,如GPIO(通用输入输出)、定时器和串口等。
通过使用指针,我们可以直接访问和修改这些寄存器的值,从而控制外部设备的行为。
例如,我们可以使用指针来设置GPIO引脚的状态,或者配置定时器的工作模式。
除了访问和操作内存和外设寄存器,指针还可以用于动态内存分配和数据结构的操作。
在C51中,我们可以使用指针来动态分配内存,并在运行时根据需要分配和释放内存。
这种灵活的内存管理方式可以提高程序的效率和灵活性。
此外,指针还可以用于创建和操作复杂的数据结构,如链表和树等。
通过使用指针,我们可以方便地插入、删除
和修改数据结构中的元素。
总之,C51中的指针用法非常重要,可以帮助我们更灵活地操作内存和外设。
通过使用指针,我们可以提高程序的执行效率,实现动态内存分配,以及创建和操作复杂的数据结构。
因此,熟练掌握C51指针的用法对于单片机编程非常重要。
C51 存储器类型与51 单片机的物理区域
1、data 区空间小,所以只有频繁用到或对运算速度要求很高的变量才放到data 区内,比如for 循环中的计数值。
2、data 区内最好放局部变量。
因为局部变量的空间是可以覆盖的(某个函数的局部变量空间在退出该函数是就释放,由别的函数的局部变量覆盖),可以提高内存利用率。
当然静态局部
变量除外,其内存使用方式与全局变量相同;
扩展阅读:51 单片机的特殊功能寄存器(SFR)
3、确保你的程序中没有未调用的函数。
在Keil C 里遇到未调用函数,编译器就将其认为可能是中断函数。
函数里用的局部变量的空间是不释放,也就是同全局变量一样处理。
这一点Keil C 做得很愚蠢,但也没办法。
4、程序中遇到的逻辑标志变量可以定义到bdata 中,可以大大降低内存占用空间。
在51 系列芯片中有16 个字节位寻址区bdata,其中可以定义8*16=128 个逻辑变量。
定义方法是:bdata bit LedState;但位类型不能用在数组和结构体。
C51 指针问题一般指针一般指针的声明和使用均与标准C 相同,不过同时还可以说明指针的存储类型,例如:long * state;为一个指向long 型整数的指针,而state 本身则依存储模式存放。
char * xdata ptr;ptr 为一个指向char 数据的指针,而ptr本身放于外部RAM 区,以上的long,char 等指针指向的数据可存放于任何存储器中。
一般指针本身用3 个字节存放,分别为存储器类型,高位偏移,低位偏移量。
2.存储器指针基于存储器的指针说明时即指定了存贮类型,例如:chardata * str;str 指向data 区中char 型数据int xdata * pow; pow 指向外部RAM 的int 型整数。
这种指针存放时,只需一个字节或2 个字节就够了,因为只需存放偏移量。
/*.........................................................................................char * xdata ptr;//ptr 本身放在Xdata(XRAM)区char xdata * ptr;//ptr 指向的数据放在Xdata(XRAM)区........................................................................................*/char xdata * pxchar xdata * data pxdata char xdata * px 这3 者有什么不同??char xdata * pxpx 本身存在于自动分配的空间,一般位于data 中,指向的内容位于xdatachar xdata * data pxpx 本身存在于data 空间,指向的内容位于xdatadata char xdata * px =char xdata *data pxdata:固定指前面0x00-0x7f 的128 个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。
C51的数据类型引言概述:C51是一种常用的单片机型号,它具有丰富的数据类型,这些数据类型在嵌入式系统中具有重要的作用。
本文将详细介绍C51的数据类型,包括基本数据类型、指针类型、数组类型、结构体类型以及枚举类型。
一、基本数据类型1.1 位类型(bit):C51提供了位类型,用于表示一个二进制位的数据。
位类型可以用于节省内存空间,特别适用于对一个变量的各个位进行操作的场景。
1.2 字符类型(char):C51的字符类型用于表示一个字符的数据,它占用一个字节的内存空间。
字符类型可以用于表示ASCII码字符,也可以用于表示整数。
1.3 整数类型(int):C51的整数类型用于表示整数数据。
根据不同的编译器和硬件平台,整数类型的长度可以不同,一般为2个字节或4个字节。
二、指针类型2.1 指针类型(*):C51的指针类型用于表示一个变量的地址。
通过指针类型,可以实现对变量的间接访问,提高程序的灵活性和效率。
2.2 空指针(NULL):C51提供了空指针常量NULL,用于表示一个无效的指针。
空指针在程序中常用于初始化指针变量或判断指针是否有效。
2.3 指针运算:C51支持指针的运算,包括指针的加法、减法和比较运算。
指针运算可以用于实现数组的访问和遍历。
三、数组类型3.1 一维数组:C51的一维数组用于存储相同类型的数据,可以通过下标访问数组元素。
一维数组在嵌入式系统中广泛应用,用于存储大量的数据。
3.2 多维数组:C51的多维数组是一种特殊的一维数组,它可以存储多维的数据。
多维数组可以用于表示矩阵、图像等复杂的数据结构。
3.3 字符串数组:C51的字符串数组是一种特殊的字符数组,用于存储字符串数据。
字符串数组在嵌入式系统中常用于存储文本信息。
四、结构体类型4.1 结构体定义:C51的结构体类型用于表示一组相关的数据,可以包含不同类型的成员变量。
通过结构体类型,可以方便地组织和操作复杂的数据结构。
4.2 结构体成员访问:C51使用点操作符(.)来访问结构体的成员变量。
关于KeilC51指针的使用(参见page106-113,keiluv2user'sguide09,2001)Keil中的指针分为两种:一种是普通指针,兼容标准C语言的指针;另一种我翻译成内存特殊指针(memory-specific pointers,翻译得不好:>)一.普通指针普通指针的定义方式如下:char * ptr;跟标准C的定义方式一样。
这种指针占三个字节,第一个字节是标识存储类型,是指针指向的变量的数据类型;第二个字节是指针存储地址的高位字节;第三个字节是指针存储地址的低位字节。
普通指针默认存储在内部存储器data,即片上RAM,如果想指定指针的存储位置,可以在*后加上存储类型,如下面几种定义方式:char * data ptr; //与char * ptr;等价,即默认的定义方式char * xdata ptr; //指针存储在片外RAMchar * idata ptr; //指针存储在idatachar * pdata ptr; //指针存储在pdata由定义普通指针写的程序最终代码比较长,运行速度相对较慢,因为Keil 在编译的时候不知道这个指针将要指向的变量的存储位置,只有当程序执行的时候才能知道,所以编译器不能对这段代码进行优化,不过,这样做的优点是此指针可以指向存储在任何位置的变量。
二.内存特殊指针内存特殊指针的定义方式为:char xdata * ptr;这个指针存储的时候占的字节数是不一定的,占一个字节的变量类型为:idata,data, pdata, bdata;占两个字节的变量类型为:code,xdata。
注意:char xdata * ptr;这里定义的ptr所指向的变量存储在xdata中,即外部变量,这样的话指针变量ptr占两个字节,我们再定义一个外部变量。
char xdata variable1;ptr=&variable1; //这样是正确的这段程序中,变量variable1存储在外部存储器中,是最合适的。
Keil C51 中的函数指针和再入函数,函数指针与overlay=====调用树的保存C51不把函数参数压栈(除非使用再入函数)。
函数参数和全局变量被存入寄存器或固定的存储空间。
这样阻止函数的再入。
例如,一个函数调用它自己,它将覆盖它自己的参数或存储空间。
函数的再入问题通过关键字“reentrant”来解决。
函数指针的非再入函数的副作用,在执行中出现问题。
MAIN函数调用FUNC和FUNC_CALLER(根据调用树)。
用函数的指针来传递参数,调用树往往会出错。
=====转这篇文章是由Keil C51 的英文文档翻译过来的,很多语句都是根据自己的理解翻译的,肯定还有许多地方需要推敲。
希望读者能吸取到有用的部分,不要被误解了,自己多理解。
Overlay修改用于数据覆盖的调用树。
如果在用户程序里使用了函数指针,或者使用了像实时操作系统中调度器那样的跳转(k 指的是指针方式的调用吗?),那么,修改程序调用树将是很有必要的。
混合编程:函数名符号名解释viod func(void FUNC 无参数传递,或不含寄存器参数的函数名不作改变转入目标文件中,名字只是简单地转为大写形式viod func(char _FUNC 带寄存器参数的函数名加入“_”字符前缀,它表明这类函数包含寄存器内的参数传递viod func(viod reentrant _?FUNC “_?”表示可重入,它表明该行数包含栈内的参数传递。
====将下面这篇的难点内容提到前面使用函数指针的附加说明如果你在C51中使用函数指针编程,有几个附加的说明你必须注意。
参数列表的限制通过函数指针传递参数给函数必须把所有的参数存入寄存器。
在大部分情况下,3个参数能够自动通过寄存器传递。
在C51的用户手册中能找到传递参数进入寄存器的运算法则。
但是并不保证,任何的3个数据类型可以传递。
因为C51在寄存器中传递3个参数,用于传递参数的存储空间是不被分配的,除非函数指向一个要求更多参数的函数。
单片机接口技术(C51版)第五章指针、结构、联合和枚举内容概述指针是C51语言的精华也是难点。
本章主要介绍指针的概念、定义指针的方法,介绍指向一维数组、二维数组、字符数组的指针使用方法,指针数组的概念以及指针作为函数参数的使用方法。
结构、联合和枚举是另外的构造型数据,本章介绍了这三种类型数据的定义、初始化以及使用方法。
教学目标1.理解指针的概念,掌握指针与地址的关系,能区别指针变量与变量的指针,根据需要定义并使用指针变量,理解地址运算的方法。
2.理解指针与数组的关系,熟练使用指针指向一维数组、二维数组,理解并掌握利用指针表达数组元素的几种表现形式。
3. 能利用指针指向字符数组,处理字符串。
4.理解指针数组的概念,该数组的元素是指针。
5.掌握指针作为函数形参的使用方法,理解指针作为函数形参传递整个数组的作用。
6.了解结构体的作用及应用场合,能定义并使用结构体变量,掌握结构变量成员的引用方法。
能定义并使用结构数组,会引用结构数组元素成员,7.掌握结构指针的定义方法,利用结构指针访问结构变量的成员。
8. 了解结构变量的作为结构成员的结构嵌套定义方法以及位结构的概念。
9. 理解联合的概念,能定义并使用联合体变量,掌握联合与结构的区别。
10. 能定义并使用枚举变量。
5.1指针5.1.1 指针和地址5.1.1.1 指针变量的定义⏹C51语言中, 对于变量的访问形式之一, 就是先求出变量的地址,然后再通过地址对它进行访问, 这就是这里所要论述的指针及其指针变量。
⏹所谓变量的指针, 实际上指变量的地址⏹变量的地址虽然在形式上好象类似于整数, 但在概念上不同于以前介绍过的整数, 它属于一种新的数据类型, 即指针类型。
⏹C51中, 一般用“指针”来指明这样一个表达式&x的类型,而用“地址”作为它的值, 也就是说, 若x为一整型变量, 则表达式&x的类型是指向整数的指针, 而它的值是变量x的地址。
⏹同样, 若double d;则&d的类型是指向双精度数d的指针, 而&d的值是双精度变量d的地址。
Keil C51中函数指针的使用3■江西理工大学 朱博 许伦辉 函数指针在C语言中应用较为灵活。
在单片机系统中,嵌入式操作系统、文件系统和网络协议栈等一些较为复杂的应用都大量地使用了函数指针。
K eil公司推出的C51编译器是事实上80C51C编程的工业标准,它针对8051系列CPU硬件在标准ANSI C的基础上进行了扩展;但由于编译器及8051体系结构的限制,造成了在使用函数指针时有很多与ANSI C不同的地方。
下面举例说明在不同的情形下函数指针的使用。
以下代码均在K eilμVision3、v8.08 C51、默认优化等级的开发环境下验证通过。
1 指向固定地址的指针在程序设计中,常需要跳转到某一特定的地址上执行,如引导程序的设计。
可通过如下C语言实现: int main(void){((void(code3)(void))0x2000)();return0;}此代码使得主函数执行位于0x2000地址的程序代码。
其中((void(code3)(void))是一种数据类型,表示一指向代码段函数的指针,该函数无参数和无返回值。
它对数据0x2000进行了强制类型转换,使函数指针指向地址为0x2000的代码段地址。
关于复杂类型的声明详见参考文献[1]。
通过反汇编窗口可看到编译器生成了如下汇编代码: C:0x000F 122000 LCALL C:2000由上可以看出,Keil C51是非常高效的编译器,产生了非常简洁的输出。
这正是我们所期望的。
2 无参数的函数指针Keil C51中不带参数的函数指针的使用方法与ANSI3国家自然科学基金项目(No:60664001):基于分布式多智能体的城市交通协调控制理论及应用研究。
C基本相同。
示例如下: void foo(void){ return;}int main(void){ void(3pfoo)(void);//声明函数指针pfoo pfoo=foo;//对该指针赋值,使该指针指向某一函数 (3pfoo)();//通过指针调用其指向的函数 return0;}3 带参数的函数指针一般来说,函数参数是通过堆栈来传递,用PUSH和POP汇编指令来实现的;但由于8051体系及其编译器的一些限制,使得其函数参数的传递需要一些特殊的方法。
《_指针类型和存储区的关系详解_》对在keilC51中使用指针的_研究指针类型和存储区的关系详解一、存储类型与存储区关系data ---> 可寻址片内rambdata ---> 可位寻址的片内ramidata ---> 可寻址片内ram,允许访问全部内部rampdata ---> 分页寻址片外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个字节的内存空间,地址是外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, AINC DPTRMOV 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, #0x01MOV @DPTR, AINC DPTRMOV DPTR, #0x000AMOV A, #tmp(0x00)MOV @DPTR, AINC DPTRMOV A, #tmp(0x00)MOVX @DPTR, A这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。
小结一下:大家看到了以上的6种情况,其中效率最高的是第2种情况,既可以正确访问ram区又节约了代码,效率最差的是第 6 种,但不是说大家只使用第2种方式就可以了,还要因情况而定,一般说来应用51系列的系统架构的内部ram资源都很紧张,最好大家在定义函数内部或程序段内部的局部变量使用内ram,而尽量不要把全局变量声明为内ram区中。
所以对于全局指针变量我建议使用第3 种情况,而对于局部的指针变量使用第2种方式。
C51是很灵活的,也很好理解和使用,但要成为笑傲江湖的一代高手还是要多想多练,没有实际项目的锻炼是不容易提高的。
希望这篇文章对大家一点用处。
c8051f系列单片机开发与C语言编程》童长飞编著(11页)(41页)存储组织结构分数据存储区(片内内存+片外内存)和代码存储区(Flash ROM)。
数据存储区:分RAM和XRAM:RAM分普通内存和特殊寄存器:普通内存:0x00~0x7f可直接寻址;0x80~0xff只能间接寻址,直接寻址指令指向的是FSR特殊寄存器。
片外内存 RAM(汇编:读操作:MOV 、写操作:)(c语言:读操作:、写操作:)片外内存XRAM(汇编:读操作:MOVX 、写操作:MOVX)PSCTL.0清0;(c语言:读操作:、写操作:)代码存储区:Flash ROM(汇编:读操作:MOVC 、写操作:MOVX )PSCTL.0置1;(c语言:读操作:code 、写操作:xdata)由于MOVX指令也是外部内存读/写操作指令,为了能正确区分指令操作对象,在对Flash ROM进行写操作前,需将相关控制寄存器正确设置(PSCTL.0置1)。
Flash ROM写操作完毕后,再将PSCTL.0清0,以便使MOVX指令指向外部内存。
/////擦除(ok)void FLASH_PageErase (unsigned int addr){bit EA_SAVE = EA; // Preserve EAchar xdata * data pwrite; // FLASH write pointer//在data(内部ram)开指针变量空间指向外部空间EA = 0; // Disable interrupts pwrite = (char xdata *) addr;FLKEY = 0xA5; // Key Sequence 1FLKEY = 0xF1; // Key Sequence 2PSCTL |= 0x03; // PSWE = 1; PSEE = 1 *pwrite = 0; // Initiate page erase PSCTL &= ~0x03; // PSWE = 0; PSEE = 0 EA = EA_SAVE; // Restore interrupts//写入void FLASH_ByteWrite (unsigned int addr, char byte){bit EA_SAVE = EA; // Preserve EAchar xdata * data pwrite; // FLASH write pointer EA = 0; // Disable interrupts // change clock speed to slow, then restore laterpwrite = (char xdata *) addr;FLKEY = 0xA5; // Key Sequence 1FLKEY = 0xF1; // Key Sequence 2PSCTL |= 0x01; // PSWE = 1*pwrite = byte; // Write the bytePSCTL &= ~0x01; // PSWE = 0EA = EA_SAVE; // Restore interrupts }//读取unsigned char FLASH_ByteRead (unsigned int addr){bit EA_SAVE = EA; // Preserve EAchar code * data pread; // FLASH read pointer unsigned char byte;EA = 0; // Disable interrupts pread = (char code *) addr;byte = *pread; // Read the byteEA = EA_SAVE; // Restore interrupts return byte;}动刀data xdata code。