• 本来通过一个实例来解释C语言指针

    单片机C语言变量分别有char型,int型,long型,float型等,它们分别占用内存为1个单元,2单元,4单元,4单元等,另外数组和字符串则占用了内存某段连续的单元。这些变量的共同特征是它们都装的数值。那如果一个变量里面装的不是数值,而是地址,这个变量则称为指针变量。所以指针变量用于存储器的地址号。

    那么问题又来了,指针变量占多大内存呢?比如char *p,p指针变量前面用char修饰,莫非占一个单元?再例如 long *r,r指针变量前用long修饰,莫非是占4个单元?我们知道C51中,RAM的地址号是从0到255,ROM则是从0到65535。最大的地址号是正整数65535,显然有两个字节便可装下。因此C51中指针变量自身只占两个单元。

    既然如此那这些类型修饰符可以不要?我们知道,指针变量中装的是一个地址,也就是说通过这个地址,我便可以找到这个地址中的信息。如果指针指向的是一个char型变量,那么其通过指针(装载char型变量的地址)我找到了这个变量。但如果是指向一个float型变量呢,如果没有这个float来修饰指针变量,那么我们巡着指针只能找到一个字节的数据,而一个float型有4个字节,显然计算机就分不清是不是该把指针后面的三个字节视为一个完整的数据。所以指针变量前面的类型修饰符不是用于修饰指针变量自身所占用的地址数量,而是用于修饰指针变量中所存储的地址与后续地址之间的关联情况。

    接下来我们用一个实验来感受下指针的用法。

    实验、从单片机发送字符串0123456789到桌面串口调试助手。

    #include "reg51.h"
    #define u16 unsigned int
    #define u8 unsigned char
    
    void delay(u16 x)
    {
    	while(x--);
    }
    
    void Uart_Init() //串口初始化
    {
    	SCON=0x50; //8位异步模式
    	TMOD|=0x20;	//定时器1工作方式2
    	TH1=253;//9600bit/s
    	TR1=1;
    }
    
    void Send_Byte(u8 dat)	  //串口发送一个字节
    {
    	SBUF=dat; //启动发送,只需要把发送内容给SBUF这个寄存器
    	while(TI==0); //等待发送完成,因为TI为1时表示在发送停止位
    	TI=0;
    }
    
     void Send_String(u8 *str)	 //发送一个字符串	*str为字符串第一个字符的地址
     {
    	while(*str)
    	{
    		Send_Byte(*str);
    		str++;
    	}
    
     }
    
    void main()
    {
    	Uart_Init();
    	while(1)
    	{
    	   Send_String("0123456789");
    	   Send_Byte(10); //回车键
    	   delay(60000);
    	   delay(60000);
    	}
    }
    

    从仿真串口接收器来看,这个程序运行得很好。现在我把void Send_String(u8 *str)修改为void Send_String(u16 *str),重新编译并仿真,运行得到


    13579?跟着一串乱码,这又是为什么?u16是一个无符号int型,Send_String函数每次会取两个单元作为一个数传进来,进入while循环后,第一次就取"0"*256+"1"传给了Send_Byte函数,而Send_Byte只能装下u8型即一个单元数据,就就是高位的0要溢出,就只剩下1了。所以一直取到了9,之后因为while(*str),继续取字符串后续的内存单元,而这些内存单元里面是什么并不确定,所以显示乱码。

    同样我们可以尝试void Send_String(long *str)等等。另外补充一个知识点,定义chara; char *p; p=&a; 其中&是C语言求首地址运算符。


    展开全文
  • 单片机C语言指针意义浅析—Keil-C51 2017年06月24日 16:33:00lucky_草木皆兵阅读数:482 通常认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上,甚至认为指针是C语言的灵魂。这里说通常,...

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

    2017年06月24日 16:33:00 lucky_草木皆兵 阅读数:482

    通常认为,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所示。 






    图中箭头所指处即为变量在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所示的设置。






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

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

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

    由调试结果可知,08H数据由00H变为ABH,09H数据由00H变为CDH,出现这种变化是因为执行了语句j=0xabcd;08H为变量“j”高八位,存储“AB”,09H为变量“j”低八位,存储“CD”。
    第二次单击“Step”按钮,执行语句:p=0xaa;此时需观察Memory2窗口内数据,如图5所示。

    由调试结果可知,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所示。
     

    由编译结果可知,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所示。

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

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

    p=tab1就是将tab1数组的首地址赋值给变量p,执行p++即地址值加1;*p则是此物理地址内的具体数据,因此for循环中,N1=*p是依次将tab1数组中的数据赋值给变量N1。由此可见,指针是作为一个变量,指向某一个地址。
    那么指针到底是如何将某个地址内的数据“拿”出来的?下面通过N1=*p语句做演示说明,N1=*p编译后的汇编代码如图10所示。

    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
    程序功能:读取单片机内部ROM内的数据并赋值给A,寻址范围0~65535。当数组用code定义时,如程序③中,tab2数组用code定义,执行p=tab2后,R3的值被赋值为0xFF,再执行N2=*p语句时,即跳转到自处,读取内部ROM地址内的数据。  
    由此可见,子函数“C?CLDPTR”的作用是,根据数据所在存储空间,用不同的寻址方式读取某地址下的数据。R3用于确定寻址方式,R3的值与对应的寻址方式对应关系为:
    1、R3值等于0x00时,片内RAM间接寻址;此时数据用dataidata定义。
    2、R3值等于0x01时,片外RAM(扩展RAM)间接寻址;此时数据用xdata定义。
    3、R3值等于0xFE时,片外RAM(扩展RAM)低246字节间接寻址;此时数据用pdata定义
    4、R3值等于0xFF时,从存储存储器(ROM)进行变址寻址;此时数据用code定义。

    3、指针结构
    R3、R2、R1的值是RAM中0x12、0x13、0x14地址内的值,即变量p映射的RAM地址。而而8位单片机中,不管是何种寻址方式,最大寻址范围是2字节长度(0~65535),为什么指针*p却占用了3字节RAM空间呢?下面通过程序④说明。
    程序④:
    #include "REG52.H"         
    unsigned char tab1[8];
    unsigned char idata tab2[8];
    unsigned char xdata tab3[8];   
    unsigned char pdata tab4[8];
    unsigned char codetab5[8]={0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80}; 
    unsigned char  *p;
    void main()
    {        
             p=tab1;
             p=tab2;
             p=tab3;
             p=tab4;
             p=tab5;
    }
    在Debug Session模式下可知,程序中数组与变量所映射的物理地址为及物理存储区分别为:
    tab1 :        0x08~0x0F                        单片机内部RAM
    tab2:     0x03~0x1A                       单片机内部RAM(idata)
    tab3:     0x08~0x0F                        单片机扩展RAM(xdata)
    tab4:     0x00~0x08                        单片机扩展RAM低256字节(pdata)
    tab5:     0x0003D~0x0044            单片机程序存储区(code)
    p:            0x10~0x12                        单片机内部RAM
    注:扩展RAM可以在物理上可以分为片内或片外,如STC15系列增强型单片机的扩展RAM与单片机是封装在一起的,即片内扩展RAM;传统8051单片机没有片内扩展RAM,需连接外部RAM芯片,此为片外扩展RAM。
    在Memory Windows窗口下,监视变量p映射的RAM地址:0x10~0x12的数值变化,如图11所示。

    通过“Step”功能按钮执行住函数中的5调语句,可观察到0x10~0x12寄存器的数据变化:

    执行p=tab1后,0x10、0x11、0x12:0x00、0x00、0x08
    执行p=tab2后,0x10、0x11、0x12:0x00、0x00、0x13
    执行p=tab3后,0x10、0x11、0x12:0x01、0x00、0x08
    执行p=tab4后,0x10、0x11、0x12:0xFE、0x00、0x00
    执行p=tab5后,0x10、0x11、0x12:0xFF、0x00、0x3D
    由此可知,0x10的赋值取决于p指向的物理存储区,0x11、0x12的值是数据存储区的地址。指针所映射的首地址,会根据指向的物理存储区被编译器赋不同的值:0x00,0x01,0xFE,0xFF。这与程序③得到的结论一致,程序③中,寄存器R3、R2、R1对应值实际上就是指针所映射的3字寄存器数值。
    结合程序③编译分析,当需要引用某物理地址内数据时,会调用“C?CLDPTR”函数,函数功能就是根据这些赋值确定使用何种寻址方式引用数据。而这一过程包括“C?CLDPTR”函数都是编译器自动完成的。
    在汇编语言中,R1寄存器可以用于间接寻址,如:MOV  A,@R1。不能写为MOV A,@12H。因此在程序③中,将变量p对应的3字节数据赋值给R3、R2、R1。
    综上所述,Keil C-51编译环境下,指针是一个占3字节的特殊变量,编译器编译程序时,自动生成判断寻址方式的子函数,并根据根据目标数据所在的物理存储区不同,为指针首字节赋值,根据赋值的不同,进行不同方式的寻址;指针的后2字节,用于存放引用的地址。

    调试训练:
    下面的程序编译器会怎样编译?与程序③有何不同?请根据程序③和程序④的分析方式分析程序⑤的执行结果。
    程序⑤
    #include "REG52.H"         
    unsigned char tab1[8];
    unsigned char codetab2[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; 
    unsigned char  *p;
    void main()
    {        
             unsignedchar i;
             p=tab1;
             for(i=0;i<8;i++,p++)
             *p=i;
             
             p=tab2;
             for(i=0;i<8;i++,p++) 
             *p=i;
    }

    思考:下列语句中:

    p=tab2;

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

    *p=i;

    执行完for循环后,tab2数组内的值会改变吗?为什么?

    4、指针意义

    在汇编编程中,由于单片机数据存放的物理存储区不同,导致有不同的寻址方式,用户进行必须根据这一规律设计程序。而在C语言中,不管目标数据所在的物理存储区如何,指针都可指向该地址,并自动编译寻址方式。

    但指针并不是万能的,如程序⑤中:

    p=tab2;

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

    *p=i;

    这些语句编译时并不会报错,但却不能实现功能,因为tab2数组是定义在程序存储器(ROM)的常量数组,ROM内的数据更改是不能通过这种方式实现的。因此,当用户不明确单片机的物理存储区特性时,使用指针会容易出错。先将程序⑤中的主函数语句做如下修改,得到程序⑥:

    #include"REG52.H"         

    unsignedchar tab1[8];

    unsignedchar code tab2[8]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

    unsignedchar  *p;

    voidmain()

    {        

             unsigned char i;

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

             tab1[ i]=i;

             

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

             tab2[ i]=i;

    }

    单独看第一个for循环,可实现与程序⑤一样的效果,即tab1数组内被赋值为:0,1,2,3,4,5,6,7。

    第二个for循环从语句上可认为是与程序⑤功能相同,但实际上,不管是程序⑤还是程序⑥,都不能实现对tab2数组的赋值。但在程序⑥中,编译器会提示错误,如图12所示。

    因此,指针的使用不当,不仅会带来程序运行结果的不正确,同时也难以发现这些错误。

    对比程序⑤和程序⑥中的两段程序:

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

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

    *p=i;


    它们执行的结果是一样的,那么哪种更好呢?对于初学者来说,显然是后者,因为后者更易于理解程序含义,而前者必须要理解指针在此处的作用;那么对于有经验的程序员呢?也是后者,因为程序执行效率上,后者也要大于前者,因为程序⑤在编译过程中,编译器始终会生成一个子函数用于确定寻址方式,再赋值;程序⑥则是直接确定了寻址方式执并行进行赋值。尽管执行效率的降低在接受范围内,但对于一个简单、明了的功能来说,用简单的方式实现要比复杂方式合理。
    设计者在程序中使用指针的目的往往是让程序具有可移植性,但8051单片机的功能是有限的,它实现的功能相对固化,如时间显示、数据采集等等,这些功能确定后,几乎不会做出更改,基于此特点,8051单片机的代码代码量都不长。因此即便是不同构架的单片机程序互相移植,代码的修改并不复杂,移植过程中,也几乎都是针对不同构架单片机的I/O工作方式不同、指令周期不同做常规修改;或是关键字的修改。因此合理的设计单片机程序,尽可能的提高程序的效率、稳定性、可阅读性才是程序设计的核心主旨。指针在8051单片机中固然可以使用,但并不能说明指针的使用就一定是高效、准确、易于他人理解。

    展开全文
  • 51单片机-指针 1. 指针 变量a的指针=变量a的地址 再定义一个变量p,来存放a的地址(指针),p就是指针变量,简称指针 1.1 指针变量的声明的两种方式 //直接初始化赋值 unsigned char a; unsigned char *p = &a; ...

    51单片机-指针方法-串口实例


    1. 指针

    变量a的指针=变量a的地址
    再定义一个变量p,来存放a的地址(指针),p就是指针变量,简称指针

    1.1 指针声明的两种方式

    //直接初始化赋值
    unsigned char a;
    unsigned char *p = &a;
    
    //定义后再进行赋值
    unsigned char a;
    unsigned char *p;
    P = &a;
    

    1.2 两个重要区别(P194)

    1. 第指针变量p与普通变量a的区别:
      a可直接赋值,p要赋地址
    2. 定义指针变量*p和取值运算*p的区别
      *的三种用法:
    
    num = a * b;		//乘号
    
    unsigned char *p;	//定义指针变量
    
    num = *p;			//取值运算,取p指向的值,赋值给num
    
    

    1.3 指针流水灯

    写流水灯的方法汇总:

    软件延时,定时器

    算法移位,移位函数,指针

    
    #include <reg52.h>
    
    unsigned char T0RH,T0RL;
    
    void ConfigTimer0(unsigned char ms);
    
    void main(void)
    {
    	ConfigTimer0(1);
    	while(1){}
    }
    
    void ConfigTimer0(unsigned char ms)
    {
    	unsigned long tmp;
    
    	tmp = 11059200 / 12;
    	tmp = tmp * ms / 1000;
    	tmp = 65536 - tmp;
    	tmp += 6;
    
    	T0RH = (unsigned char)(tmp>>8);
    	T0RL = (unsigned char)tmp;
    
    	TH0 = T0RH;
    	TL0 = T0RL;
    	TMOD &= 0xF0;
    	TMOD |= 0x01;
    	EA = 1;
    	ET0 = 1;
    	TF0 = 0;
    	TR0 = 1;
    }
    
    void LedFlow(void)
    {
    	static unsigned char buff = 0x01;
    	static unsigned char *p = &buff;
    
    	P1 = ~buff;
    	*p <<= 1;
    	if(0==buff)
    	{
    		buff = 0x01;
    	}
    }
    
    void InterruptTimer0(void) interrupt 1
    {
    	static unsigned int tmr1s = 0;
    
    	TH0 = T0RH;
    	TL0 = T0RL;
    
    	tmr1s++;
    	if(tmr1s>=1000)
    	{
    		tmr1s = 0;
    		LedFlow();
    	}
    }
    
    

    2. 数组的指针

    2.1 数组指针的声明

    unsigned char num[10] = 
    {
    	0,1,2,3,4,5,6,7,8,9
    };
    unsigned char *p = &num[0];
    

    2.2 指针本身的运算(P197)

    1. 比较运算
      两个指针需要指向,有同种数据类型的对象。

    2. 指针和整数的加减

    3. 两个指针相减

    2.3 数组指针和sizeof()的用法实例

    上位机发送命令,下位机回发对应数组值

    
    #include <reg52.h>
    
    unsigned char table1[1] = {1};
    unsigned char table2[2] = {1,2};
    unsigned char table3[4] = {1,2,3,4};
    unsigned char table4[8] = {1,2,3,4,5,6,7,8};
    
    bit UARTArrived = 0;
    unsigned char UARTNum;
    unsigned char *UARTTable;
    unsigned char TableCnt;
    
    void ConfigUART(unsigned long baud);
    
    void main(void)
    {
    	ConfigUART(9600);
    	while(1)
    	{
    		if(UARTArrived)
    		{
    			UARTArrived = 0;
    			switch(UARTNum)
    			{
    				case 1 : UARTTable = &table1; TableCnt = sizeof(table1); TI = 1; break;
    				case 2 : UARTTable = &table2; TableCnt = sizeof(table2); TI = 1; break;
    				case 3 : UARTTable = &table3; TableCnt = sizeof(table3); TI = 1; break;
    				case 4 : UARTTable = &table4; TableCnt = sizeof(table4); TI = 1; break;
    				default : break;
    			}
    		}
    	}
    }
    
    void ConfigUART(unsigned long baud)
    {
    	TH1 = 256 - (11059200/12/32) / baud;
    	TL1 = TH1;
    	SCON = 0x50;
    	TMOD &= 0x0F;
    	TMOD |= 0x20;
    	EA = 1;
    	ET1 = 0;
    	ES = 1;
    	TF1 = 0;
    	TR1 = 1;
    }
    
    void InterruptUART(void) interrupt 4
    {
    	if(RI)
    	{
    		RI = 0;
    		UARTNum = SBUF;
    		UARTArrived = 1;
    	}
    	if(TI)
    	{
    		TI = 0;
    		if(TableCnt>0)
    		{
    			SBUF = *UARTTable++;
    			TableCnt--;
    		}
    	}
    }
    
    

    3.字符数组&字符指针

    3.1 常量

    常量:整形常量,浮点型常量,字符型常量,字符串常量

    3.2 直接常量

    就是一个值

    3.3 符号常量

    符号常量声明:

    const声明

    const 类型 符号常量名字 = 常量值;
    const unsigned long BAUD = 9600;
    

    预处理命令#define

    #define 符号常量名字 常量值
    #define BAUD 9600
    

    3.4 区别实例

    区别字符串,字符数组,常量数组
    上位机发送命令,下位机回发对应数组值

    
    #include <reg52.h>
    
    const unsigned long BAUD = 9600;
    
    unsigned char table1[] = {"1-Hello!\r\n"};
    unsigned char table2[] = {'2','-','H','e','l','l','o','!','\r','\n'};
    unsigned char table3[] = {51,45,72,101,108,108,111,33,13,10};
    unsigned char table4[] = {"4-Hello!\r\n"};
    
    bit arrived = 0;
    unsigned char index;
    unsigned char cnt;
    unsigned char *p;
    
    void ConfigUART(void);
    
    void main(void)
    {
    	ConfigUART();
    	while(1)
    	{
    		if(arrived)
    		{
    			arrived = 0;
    			switch(index)
    			{
    				case 1 : p = &table1; cnt = sizeof(table1); TI = 1; break;
    				case 2 : p = &table2; cnt = sizeof(table2); TI = 1; break;
    				case 3 : p = &table3; cnt = sizeof(table3); TI = 1; break;
    				case 4 : p = &table4; cnt = sizeof(table4)-1; TI = 1; break;
    				default : break;
    			}
    		}
    	}
    }
    
    void ConfigUART(void)
    {
    	TH1 = 256 - (11059200/12/32) / BAUD;
    	TL1 = TH1;
    	SCON = 0x50;
    	TMOD &= 0x0F;
    	TMOD |= 0x20;
    	EA = 1;
    	ET1 = 0;
    	ES = 1;
    	TF1 = 0;
    	TR1 = 1;
    }
    
    void InterruptUART(void) interrupt 4
    {
    	if(RI)
    	{
    		RI = 0;
    		index = SBUF;
    		arrived = 1;
    	}
    	if(TI)
    	{
    		TI = 0;
    		if(cnt>0)
    		{
    			SBUF = *p++;
    			cnt--;
    		}
    	}
    }
    
    
    展开全文
  • 单片机C语言指针变量

    2019-03-26 19:23:03
    C语言的指针指针传递概述指针及其应用运行结果 概述 C语言中指针是一个神奇的东西,可以为编程带来极大方便。但使用不当又会产生意想不到的情景,需要大家慎重。下面用Arduino Nano编辑一段程序,看如何更好的...

    C语言的指针及指针传递

    概述

    C语言中指针是一个神奇的东西,可以为编程带来极大方便。但使用不当又会产生不可预料的结果,需要大家慎重。下面用Arduino Nano编辑一段程序,看如何更好的使用C语言的指针。

    指针及其应用

    首先在程序的开始部分先定义两个变量:

    unsigned char Pixel[128];
    unsigned char *pixel_pt;
    

    第一个变量Pixel是一个数组,同时也是一个数组的指针。*pixel_pt是一个无符号变量的指针。下面用程序来说明如何使用指针型的变量。

    void setup() 
    {
      Serial.begin(115200);
      pixel_pt = Pixel;
      for (unsigned char i = 0; i < 128; i++)
        *pixel_pt++ = i;  
    }
    
    void loop() {
    
      if(Serial.available() > 0)
      {
        incomingByte = Serial.read();
        // printing title
        Serial.print("No");
        Serial.print("\t\t");
        Serial.print("Pixel");
        Serial.print("\t\t");
        Serial.print("*pixel_pt");
        Serial.println();
        pixel_pt = Pixel;      // 地址指针又指向Pixel[0]
        for (unsigned char i = 0; i < 128; i++)
        {
          Serial.print(i);
          Serial.print("\t\t");
          Serial.print(*pixel_pt++, HEX);
          Serial.print("\t\t");
          Serial.print(Pixel[i], HEX);
          Serial.println();
        }
          pixel_pt = Pixel;
          pixel_pt++;
          pixel_pt++;
          pixel_pt++;
          pixel_pt++;
          pixel_pt++;
          pixel_pt++;
          Serial.print("\r\n");
          Serial.print(Pixel[6], HEX);
          Serial.print("\r\n");  
          Serial.print(*pixel_pt, HEX);
          Serial.println();
          Serial.print(pixel_pt[6], HEX);
          if (memcmp(&pixel_pt[6], &Pixel[0x0C], 1) == 0)
            Serial.print("\r\nThe compare is OK");
          else
            Serial.print("\r\nThe compare is not OK");
      }
     
      delay(1000);  
    }
    

    运行结果

    No Pixel *pixel_pt
    0 0 0
    1 1 1
    2 2 2
    3 3 3
    4 4 4
    5 5 5
    6 6 6
    7 7 7
    8 8 8
    9 9 9
    … 省略
    126 7E 7E
    127 7F 7F

    6
    6
    C
    The compare is OK

    注意,在程序中使用了指针操作,pixel_pt++命令,相当于Pixel[i++]变址。用pixel_pt[6]也可以对地址变化后的第六个元素进行寻址。用Serial.print(pixel_pt[6], HEX);输出结果是C。

    展开全文
  • STC89C52_51单片机_指针_指针流水灯 指针变量的声明的两种方式 //直接初始化赋值 unsigned char a; unsigned char *p = &a; //定义后再进行赋值 unsigned char a; unsigned char *p; P = &a; 两个...

    STC89C52_51单片机_指针_指针流水灯_串口







    1.指针变量的声明的两种方式

    //直接初始化赋值
    unsigned char a;
    unsigned char *p = &a;
    
    //定义后再进行赋值
    unsigned char a;
    unsigned char *p;
    P = &a;
    

    两个重要区别(P194)

    1. 第指针变量p与普通变量a的区别
    2. 定义指针变量p和取值运算p的区别

    指针的简单实例:流水灯

    
    #include<reg52.h>
    
    unsigned char ledbuff = 0x01;
    unsigned long i;
    
    void remove(unsigned char *p);
    
    void main(void)
    {
    	while(1)
    	{
    		P1 = ~ledbuff;
    		for(i=0;i<9000;i++){}
    		remove(&ledbuff);
    		if(ledbuff==0)
    		{
    			ledbuff = 0x01;
    		}
    	}
    }
    
    void remove(unsigned char *p)
    {
    	*p = *p << 1;
    }
    
    

    2.指向数组元素的指针(变量的指针)

    unsigned char num[10] = 
    {
    	0,1,2,3,4,5,6,7,8,9
    };
    unsigned char *p;
    p = &num[0];
    

    数组指针和sizeof()的用法,串口算法改进

    上位机发送命令,下位机回发对应数组值

    
    #include<reg52.h>
    
    unsigned char array1[1] = {1};
    unsigned char array2[2] = {1,2};
    unsigned char array3[4] = {1,2,3,4};
    unsigned char array4[8] = {1,2,3,4,5,6,7,8};
    
    bit cmdarrived = 0;
    unsigned char cmdindex = 0;
    unsigned char cnt = 0;
    unsigned char *ptr;
    
    void configUART(unsigned long baud);
    
    void main(void)
    {
    	EA = 1;
    	configUART(9600);
    	while(1)
    	{
    		if(cmdarrived)
    		{
    			cmdarrived = 0;
    			switch(cmdindex)
    			{
    				case 1 : ptr = array1;cnt = sizeof(array1);TI = 1;break;
    				case 2 : ptr = array2;cnt = sizeof(array2);TI = 1;break;
    				case 3 : ptr = array3;cnt = sizeof(array3);TI = 1;break;
    				case 4 : ptr = array4;cnt = sizeof(array4);TI = 1;break;
    				default : break;
    			}
    		}
    	}
    }
    
    void configUART(unsigned long baud)
    {
    	TH1 = 256 - (11059200 / 12 / 2 / 16) / baud;
    	TL1 = TH1;
    	SCON = 0x50;
    	TMOD &= ~(0xF<<4);
    	TMOD |= 0x2<<4;
    	ET1 = 0;
    	ES = 1;
    	TR1 = 1;
    }
    
    void UART(void) interrupt 4
    {
    	if(RI)
    	{
    		RI = 0;
    		cmdindex = SBUF;
    		cmdarrived = 1;
    	}
    	if(TI)
    	{
    		TI = 0;
    		if(cnt>0)
    		{
    			SBUF = *ptr;
    			cnt--;
    			ptr++;
    		}
    	}
    }
    
    

    3.字符数组&字符指针

    常量&直接常量&符号常量

    常量:整形常量,浮点型常量,字符型常量,字符串常量

    声明

    用const声明

    const 类型 符号常量名字 = 常量值;
    const unsigned long BAUD = 9600;
    

    预处理命令#define

    #define 符号常量名字 常量值
    #define BAUD 9600
    

    字符和字符串数组实例

    区别字符串,字符数组,常量数组
    上位机发送命令,下位机回发对应数组值

    
    #include<reg52.h>
    
    unsigned char array1[] = "1-Hello!\r\n";
    unsigned char array2[] = {'2','-','H','e','l','l','o','!','\r','\n'};
    unsigned char array3[] = {51,45,72,101,108,108,111,33,13,10};
    unsigned char array4[] = "4-Hello!\r\n";
    
    //const unsigned long BAUD = 9600;
    bit cmdarrived = 0;
    unsigned char cnt = 0;
    unsigned char *ptr;
    unsigned char cmdindex = 0;
    
    void configUART(unsigned long baud);
    
    void main(void)
    {
    	EA = 1;
    	configUART(9600);
    	while(1)
    	{
    		if(cmdarrived)
    		{
    			cmdarrived = 0;
    			switch(cmdindex)
    			{
    				case 1 : ptr = array1;cnt = sizeof(array1);TI = 1;break;
    				case 2 : ptr = array2;cnt = sizeof(array2);TI = 1;break;
    				case 3 : ptr = array3;cnt = sizeof(array3);TI = 1;break;
    				case 4 : ptr = array4;cnt = sizeof(array4) - 1;TI = 1;break;
    				default : break;
    			}
    		}
    	}
    }
    
    void configUART(unsigned long baud)
    {
    	TH1 = 256 - (11059200 / 12/ 2 /16) / baud;
    	TL1 = TH1;
    	SCON = 0x50;
    	TMOD &= ~(0xF<<4);
    	TMOD |= 0x2<<4;
    	ET1 = 0;
    	ES = 1;
    	TR1 = 1;
    }
    
    void UART(void) interrupt 4
    {
    	if(RI)
    	{
    		RI = 0;
    		cmdindex = SBUF;
    		cmdarrived = 1;
    	}
    	if(TI)
    	{
    		TI = 0;
    		if(cnt>0)
    		{
    			SBUF = *ptr;
    			cnt--;
    			ptr++;
    		}
    	}
    }
    
    
    展开全文
  • 我们都知道,单片机串口传输的单位是字节,而浮点数是占四个字节,简单思路是用一个char型指针指向浮点数据,利用指针寻址即可以将浮点数拆成四个char数据。接收端接收到四个char数,为了还原成float数据,...
  • (int*)(0x400ff0d4) 代表强制转换地址0x400ff0d4为int32位指针。 *((int*)(0x400ff0d4)) 代表取地址指针的内容 所以有   接下来看看C语言结构图的深入应用 1.首先生成GPIO_MEMapPtr的结构体...
  • 版权声明:本文为CSDN博主「逍遥l天」的原创文章,遵循 CC 4.0 BY-SA ...我们在上C语言课的时候,学到指针,每一位教C语言的老师都会告诉我们一句:指针是C语言的灵魂。由此可见,指针是否学会是判断一个人是否真正...
  • 本系列文章讲述了基于proteus仿真的51单片机学习,内容全面,不仅讲解电路原理,还讲解了单片机c语言,实例丰富,内容全面。
  • 指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *、double *、char * 等。 如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针指针...
  • 指针初始化与赋值

    2019-09-26 21:34:05
    1、指针的初始化 指针初始化时,“=”的右操作数必须为内存中数据的地址,不能够是变量,也不能够直接用整型地址值(可是intp=0;除外,该语句表示指针为空)。此时,p仅仅是表示定义的是个指针变量,并没有间接取值的...
  • 函数指针指针函数

    2012-11-18 13:28:05
    这正如用指针变量可指向整型变量、字符、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数...
  • 在很多大型的集成系统中都会用到状态机来跳转到指定的函数,而状态机的使用过程中就离不开函数指针、结构体数组等等,下面就介绍下函数指针与状态机的用法。 结构体数组平时我们接触到的数组的元素都是数字或者字符...
  • 12.4.3 指针型函数 12.5 指针与字符串 12.5.1 字符串的表达形式 12.5.2 字符指针作为函数参数 12.5.3 使用字符指针与字符数组的区别 12.6 指针数组与命令行参数 12.6.1 指针数组的定义和使用 12.6.2 指向指针的...
  • 指针是C语言的精华,它是一柄“双刃剑”,用的好与坏就看使用者的功力了。下面就一道面试题,看一下指针与数组的区别。   char *p1, *p2; char ch[12]; char **pp; p1 = ch; pp = &ch; p2 = *pp; ...
  • 单片机:基础知识

    2019-07-20 08:43:19
    单片机内部结构 单片机是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、...
  • 概要: C语言中,任何一个变量都必须占有一个地址,而这个地址空间内的0-1代码就是这个变量的值。不同的数据类型占有的空间大小不一,但是他们都必须有个地址,而这个地址就是硬件访问的依据,而名字只是提供给...
1 2 3 4 5 ... 20
收藏数 5,025
精华内容 2,010
热门标签