您的位置 首页 应用

C51中关于指针的种种用法

#includereg51.H>f(){}f1(){}f2(){}main(){{intx;int*px;//下面这些表示虽然很烦,但是生成的代码却及其简洁:(黑体部…

#i nclude

f(){}
f1(){}
f2(){}

main()
{
{
int x;
int *px;

//下面这些表明尽管很烦,可是生成的代码却及其简练:(黑体部分实际运用过)

//将 xdata 型指针 0x4000 赋给 px
px=(int xdata *)0x4000;

//表明从 xdata 0x4000处取一个 char 给x
x=*((char xdata *)0x4000); (能够将0X4000处,改成一个整形变量,便利进行操作。)

或许*((char xdata *)0x4000)=X;//表明给存在xdata中,地址为0x4000的空间赋值。

// 表明从 code 0x4000处取一个 word 作为 xdata 型的指针 给
px
px=*((int xdata * xdata *)0x4000);

//表明从 code 0x4000处取一个 word 作为 xdata 型的指针,
//再把这个指针指向的char数据赋给x
x=**((char xdata * code *)0x4000);

//表明把函数f()进口地址当作xdata型指针,再把指向的xdata
//中的int型数据作为code型指针,把指向的code字节
//赋给x(晕,这样有含义吗?)
x=**(int code * xdata *)f;

//把f()进口地址处的ROM中两个code型字节,
//赋给仓库指针SP指向的字节(想干什么?编操作体系?)
*(unsigned int idata *)SP=*(unsigned int code *)&f;

//表明把f()进口地址处的ROM中两个code型字节,
//作为一个xdata指针寻址,
//把指向的数据作为pdata指针寻址,
//再把把指向的数据作为idata指针寻址,
//把该地址处的一个字节赋给x (我靠,累死了)
x= ****(unsigned int data * idata * pdata * xdata * code
*)&f;

//总归,一个括号里边外面的”*”相同多就表明指向的是数据。
}

{
//数组函数
code void (*ArrFn[])(void) =
{ &f1,
&f2,
};
//能够像引证数组相同调用函数啦:
(*ArrFn[0])();
(*ArrFn[1])();
}

{
//这样将使函数调用0000H处:
void (*reset) (void);
reset=0x0;
(*reset)();
reset();

//或许直接这样,只是生成一条指令LCALL 1234H
((void (code *)(void))0x1234)();
}

{
//这样能够调用RETI指令:
#define INT_NUM 30 //闲暇中止号
((void (code *)(void))(INT_NUM*8+3))();
//当然需求在外面声明 int_rpt()interrupt INT_NUM {}
}
{
//这样调用RETI指令太反常:
code unsigned char ret_i=0x32;
((void (code *)(void))(&ret_i))();
}
}
int_rpt()interrupt INT_NUM {}

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

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

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个字节的内存空间,地址是外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 Users 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空间了又增加了指针类型的分辩值。

小结一下:咱们看到了以上的6种状况,其间功率最高的是第2种状况,既能够正确拜访ram区又节省了代码,功率最差的是第 6
种,但不是说咱们只运用第2种办法就能够了,还要因状况而定,一般说来运用51系列的体系架构的内部ram资源都很严重,最好咱们
在界说函数内部或程序段内部的部分变量运用内ram,而尽量不要把大局变量声明为内ram区中。所以关于大局指针变量我主张运用第
3 种状况,而关于部分的指针变量运用第2种办法。

与指针有关的各种阐明和含义见下表。
int *p;     p为指向整型量的指针变量;

int xdata *p; 存在外部数据RAM;

int data *p; 存在内部数据RAM;

int code *p; 存在程序代码空间;

int data *xdata p;外部RAM指针,指向内部RAM整形数据

int xdata *data p;内部RAM指针,指向外部RAM整形数据

int *p[n];   p为指针数组,由n个指向整型量的指针元素组成。

int (*p)[n];   p为指向整型二维数组的指针变量,二维数组的列数为n

int *p()    p为回来指针值的函数,该指针指向整型量

int (*p)()   p为指向函数的指针,该函数回来整型量

int **p     p为一个指向另一指针的指针变量,该指针指向一个整型量。

{ int x;
int *px;

//下面这些表明尽管很烦,可是生成的代码却及其简练:

//将 xdata 型指针 0x4000 赋给 px
px=(int xdata *)0x4000;

//表明从 xdata 0x4000处取一个 char 给x
x=*((char xdata *)0x4000);

}

阅览组合阐明符的规矩是“从里向外”。
从标识符开端,先看它右边有无方括号或园括号,如有则先作出解说,再看左面有无*号。 假如在任何时候遇到了闭括号,则在持续之前必须用相同的规矩处理括号内的内容。例如:
int*(*(*a)())[10]
↑ ↑↑↑↑↑↑
7 6 4 2 1 3 5
上面给出了由内向外的阅览次序,下面来解说它:
(1)标识符a被阐明为;
(2) 一个指针变量,它指向;
(3)一个函数,它回来;
(4)一个指针,该指针指向;
(5)一个有10个元素的数组,其类型为;
(6) 指针型,它指向;
(7)int型数据。
因而a是一个函数指针变量,该函数回来的一个指针值又指向一个指针数组,该指针数组的元素指向整型量。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/yingyong/272361.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部