单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51

通常认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上,甚至认为指针是C语言的灵魂。这里说通常,是广义上的,因为随着编程语言的发展,指针也饱受争议,并不是所有人都承认指针的“强大”和“优点”。在单片机领域,指针同样有着应用,本章节针对Keil C-51环境下的指针意义做简要分析。

1 指针与变量

指针是一个变量,它与其他变量一样,都是RAM中的一个区域,且都可以被赋值,如程序①所示。

#include "REG52.H"

unsigned int j;

unsigned char *p;

void main()

{

while(1)

{

j=0xabcd;

p=0xaa;

}

}

在Debug Session模式下,将鼠标指针移到到变量“j”“p”位置,可以显示变量的物理地址,如图1-1、1-2所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第1张图片

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第2张图片

图中箭头所指处即为变量在RAM中的“首地址”,为什么是“首地址”呢?变量根据类型可分为8位(单字节)、16位(双字节),程序中变量“j”是无符号整型,所占物理空间应为2字节,而在8位单片机中,RAM的一个存储单元大小是8位,即1字节,因此需2个存储单元才满足变量“j”长度。所以实际上变量“j”的物理地址为“08H”“09H”。同理,“p(D:0x0A)”即变量“p”的首地址为“0AH”。

下面通过单步执行程序来观察RAM内的数据变化,打开两个Memory Windows窗口,在Keil软件下方显示为Memory1和Memory2,在两个窗口中,分别做如图2-1、2-2所示的设置。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第3张图片

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第4张图片

两个Address填写的内容分别是:D:0x08、D:0x0A,即变量“j”和变量“p”的首地址,输入后回车,便可监视RAM中该地址下的数据。设置好后,准备调试。

在Debug Session模式中,箭头所指处即为即将执行的语句,单击“Step”功能按钮(或按F11键),让程序运行,如图3所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第5张图片

第一次单击“Step”按钮后,Memory1窗口内数据如图4所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第6张图片

由调试结果可知,08H数据由00H变为ABH,09H数据由00H变为CDH,出现这种变化是因为执行了语句j=0xabcd;08H为变量“j”高八位,存储“AB”,09H为变量“j”低八位,存储“CD”。

第二次单击“Step”按钮,执行语句:p=0xaa;此时需观察Memory2窗口内数据,如图5所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第7张图片

由调试结果可知,0CH处值由00变为“AAH”,程序相吻合。这里需要注意,在Keil C-51编译环境下,指针变量,不管长度是单字节或是双字节,指针变量所占字节数为3字节。故此处“AAH”不是存储在0AH而存储在0CH(0A+2)地址中。

综上所述,指针实际上是变量,都是映射到RAM中的一段存储空间,区别是,指针占用3字节,而其他变量可根据需要设定其所占RAM是1字节(char)、2字节(int)、4字节(long)。

2 指针作用

指针的作用是什么呢?先来看下面的程序:

程序②

#include "REG52.H"

unsigned chartab1[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

unsigned char codetab2[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};

unsigned char N1,N2;

void main()

{

N1=tab1[0];

N2=tab2[0];

}

显然,程序执行的结果是N1=0x01,N2=0x10。这里都是讲数组内的数据赋值给变量,但存在区别,tab1数组使用的是单片机RAM空间,而tab2数组使用的是单片机程序存储区(ROM)空间。尽管使用C语言为变量赋值时语句相同,但编译结果并不相同,此程序编译后的结果如图6所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第8张图片 

由编译结果可知,N1=tab1[0]语句实际上是直接寻址,而N2=tab2[0]是寄存器变址寻址。不管是何种寻址方式,都是将一个物理地址内的数据取出来使用:tab1数组中,tab[0]对应的RAM地址是0x0A,tab[1]对应的RAM地址是0x0B……以此类推;tab2数组中,tab[0]对应的ROM地址是0x00A5,tab[1]对应的ROM地址是0x00A6……以此类推。不管这些数组或变量所在的RAM或ROM地址如何,用户最终需要的是数组或变量的数据,而指针,就是通过变量或数组的物理地址访问数据,也就是说,通过指针,同样可以访问数组或变量数据。现将程序②做出调整,得到程序③如下:

#include "REG52.H"

unsigned chartab1[8]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};

unsigned char code tab2[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80};

unsigned char N1,N2;

unsigned char  *p;

void main()

{

unsignedchar i;

p=tab1;

for(i=0;i<8;i++,p++)

N1=*p;

p=tab2;

for(i=0;i<8;i++,p++)

N2=*p;

}

程序执行结果:tab1数组内的8个数值依次被赋值给N1;tab2数组内的8个数值依次被赋值给N2;

程序③执行Debug Session功能后,打Watch Windows窗口,在Watch1窗口下添加需要监视的变量,此处为“p”和“N1”,如图7所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第9张图片

Value为当前变量数值,程序为运行前,p值为0x00,单击Step按键功能后,执行p=tab1;p值变为0x0A,如图8所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第10张图片

0x0A是什么值呢?将鼠标移至tab1数组位置,可显示出数组所在的物理地址,0x0A就是数组tab1的首地址,如图9所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第11张图片

p=tab1就是将tab1数组的首地址赋值给变量p,执行p++即地址值加1;*p则是此物理地址内的具体数据,因此for循环中,N1=*p是依次将tab1数组中的数据赋值给变量N1。由此可见,指针是作为一个变量,指向某一个地址。

那么指针到底是如何将某个地址内的数据“拿”出来的?下面通过N1=*p语句做演示说明,N1=*p编译后的汇编代码如图10所示。

单片机c语言指针作用,单片机C语言指针意义浅析—Keil-C51_第12张图片

C:0x00A0至C:0x00A9的汇编代码即是C程序中的N1=*p。程序先将变量p的值赋值给R3、R2、R1三个通用寄存器,程序为:

MOV   R3,p(0x12)

MOV   R2,0x13

MOV   R1,0x14

然后调用了一个子函数:LCALL  C?CLDPTR(C:00E4),而C程序中,未定义或使用任何子函数,那么这个子函数是哪里来的?作用是什么?根据标号C:00E4可找到该子函数,程序代码如下:

C:0x00E4   BB0106   CJNE     R3,#0x01,C:00ED

C:0x00E7   8982     MOV      DPL(0x82),R1

C:0x00E9   8A83     MOV      DPH(0x83),R2

C:0x00EB   E0       MOVX     A,@DPTR

C:0x00EC   22       RET

C:0x00ED   5002     JNC      C:00F1

C:0x00EF   E7       MOV      A,@R1

C:0x00F0   22       RET

C:0x00F1   BBFE02   CJNE     R3,#0xFE,C:00F6

C:0x00F4   E3       MOVX     A,@R1

C:0x00F5   22       RET

C:0x00F6    8982    MOV      DPL(0x82),R1

C:0x00F8   8A83     MOV      DPH(0x83),R2

C:0x00FA   E4       CLR      A

C:0x00FB   93       MOVC     A,@A+DPTR

C:0x00FC   22       RET

此程序功能是:先用R3寄存器的值与0x01比较,当R3的值大于0x01时,再和0xFE做比较,比较的结果有如下情况:

(1)R3的值等于0x01时,执行如下程序:

C:0x00E7   8982     MOV      DPL(0x82),R1

C:0x00E9   8A83     MOV      DPH(0x83),R2

C:0x00EB   E0       MOVX     A,@DPTR

C:0x00EC   22       RET

程序功能:读取扩展RAM内的数据并赋值给A,寻址范围0~65535。当数组用xdata定义时,会跳转到此处。

(2)R3的值小于0x01即等于0x00时,执行如下程序:

C:0x00EF   E7       MOV      A,@R1

C:0x00F0   22       RET

程序功能:读取单片机内部256字节RAM内的数据并赋值给A,寻址范围0~255。当数组用data或idata定义时,会跳转到此处。如执行N1=*p语句时,即跳转到自处,读取内部RAM地址内的数据。

(3)R3的值不等于0x00或0x01时,通过JNC指令跳转到C:0x00F1处,开始与0xFE做比较。R3的值等于0xFE时,执行如下程序:

C:0x00F4   E3       MOVX     A,@R1

C:0x00F5   22       RET

程序功能:读取单片机片外RAM内的数据并赋值给A,寻址范围0~255。当数组用pdata定义时,会跳转到此处。通常8051单片机不使用pdata定义变量或数组。

(4)R3的值不等于0xFE时,即R3的值等于0xFF时,跳转到C:0x00F6处执行如下程序:

C:0x00F6   8982     MOV      DPL(0x82),R1

C:0x00F8   8A83     MOV      DPH(0x83),R2

C:0x00FA   E4       CLR      A

C:0x00FB   93       MOVC     A,@A+DPTR

C:0x00FC   22       RET

关键字:单片机  C语言  指针  Keil-C51

你可能感兴趣的:(单片机c语言指针作用)