通用指针和定向指针
参考资料:Keil > Help > uVision Help > Cx51 Compiler User’s Guide > Language Extensions > Pointers
一、Cx51指针的几种用法
int *ptr; /* 指向:任意空间的int变量, 存储在:RAM(data) 区, 占用:3字节 */
int xdata *ptr; /* 指向:RAM(xdata)区的int变量,存储在:RAM(data) 区, 占用:2字节 */
int * xdata ptr; /* 指向:任意空间的int变量, 存储在:RAM(xdata)区, 占用:3字节 */
int xdata * xdata ptr; /* 指向:RAM(xdata)区的int变量,存储在:RAM(xdata)区, 占用:2字节 */
二、Cx51的指针类型
Cx51编译器支持使用符号 * 声明的指针变量,用标准C语言定义的指针变量在Cx51编译环境中可以完全兼容使用。但是,因为8051单片机独特的内存架构,Cx51编译环境提供两种不同类型的指针:
Generic Pointers-----------------通用指针
Memory-Specific Pointers-------定向指针
使用Memory-Specific Pointers
可以明显的加速C语言执行效率。使用不同类型的指针,在处理相同的代码时,在代码大小、占用内存大小和执行时间上存在明显的差异。
三、Generic Pointers(通用指针)
声明通用指针和标准C语言用法一样,使用时通用指针可以存放任何空间的变量地址。
int *ptr; /* 通用指针变量ptr:存储在 RAM data 区,占用3个字节空间 */
void main (void)
{
int data a; /* 变量a:存储在 RAM(data) */
int xdata b; /* 变量b:存储在 RAM(xdata) */
int code c= 9; /* 变量c:存储在 Flash(code) */
ptr = &a; /* ptr:指向 RAM(data) 区变量的地址 */
ptr = &b; /* ptr:指向 RAM(xdata) 区变量的地址 */
ptr = &c; /* ptr:指向 Flash(code)区变量的地址 */
}
另外,用户可以通过内存类型声明,声明指针变量存放的位置,比如:
/* 这3个指针变量是Generic Pointers,分别存放在不同的内存空间 */
int * xdata ptr_x; /* 通用指针变量ptr_x:存储在 RAM(xdata) 区,占用3个字节空间 */
int * data ptr_y; /* 通用指针变量ptr_y:存储在 RAM(data) 区,占用3个字节空间 */
int * idata ptr_z; /* 通用指针变量ptr_z:存储在 RAM(idata) 区,占用3个字节空间 */
四、Memory-Specific Pointers(定向指针)
定向指针在声明时,同时需要声明指向的区域,注意 * 位置。定向指针只能指向声明区域的变量,优点:有效提高代码执行效率,缺点:使用灵活性降低。
int data *ptr_x; /* 定向指针变量ptr_x:指向 RAM(data) 区 */
int xdata *ptr_y; /* 定向指针变量ptr_y:指向 RAM(xdata) 区 */
int code *ptr_z; /* 定向指针变量ptr_z:指向 Flash(code)区 */
因为内存类型在编译时确定的,所以定向指针占用的空间大小也是确定的,与8051的地址总线宽度一致。
data, idata, bdata, pdata定向指针,占用1个字节。
xdata, code定向指针,占用2个字节 。
和通用指针一样,定向指针也可以声明指针变量存储的位置,例如:
int data * xdata ptr_x; /* 定向指针变量ptr_x:占用2字节,存储在 RAM(xdata) 区,指向 RAM(data) 区 */
int xdata * data ptr_y; /* 定向指针变量ptr_y:占用1字节,存储在 RAM(data) 区,指向 RAM(xdata) 区 */
int code * idata ptr_z; /* 定向指针变量ptr_z:占用1字节,存储在 RAM(idata) 区,指向 Flash(code)区 */
五、指针类型转换
Cx51编译器转换通用指针和定向指针时,可以使用()强制类型转换,也可以由编译器隐式转换。
extern int printf (void *format, ...); /* 形参:通用指针, 占用3字节 */
extern int myfunc (void code *p, int xdata *pq); /* 形参:定向指针,占用2字节 */
int xdata *px; /* 变量:定向指针,占用2字节 */
char code *fmt = "value = %d | %04XH\n"; /* 变量:定向指针,占用2字节 */
void debug_print (void)
{
printf (fmt, *px, *px); /* 编译器隐式转换fmt,2字节转换为3字节 */
myfunc (fmt, px); /* 类型匹配,无转换 */
}
通用指针 (*) 转换为定向指针(code *, xdata *, data *, idata *, pdata *)的过程:
定向指针(code *, xdata *, data *, idata *, pdata )转换为通用指针 () 的过程:
int *p1; /* generic ptr (3 bytes) */
int xdata *p2; /* xdata ptr (2 bytes) */
int idata *p3; /* idata ptr (1 byte) */
int code *p4; /* code ptr (2 bytes */
void pconvert (void)
{
p1 = p2; /* xdata* to generic* */
p1 = p3; /* idata* to generic* */
p1 = p4; /* code* to generic* */
p4 = p1; /* generic* to code* */
p3 = p1; /* generic* to idata* */
p2 = p1; /* generic* to xdata* */
p2 = p3; /* idata* to xdata* (WARN) */
*** WARNING 259 IN LINE 15 OF P.C: pointer: different mspace
p3 = p4; /* code* to idata* (WARN) */
*** WARNING 259 IN LINE 16 OF P.C: pointer: different mspace
}
六、指针转换应用
使用抽象指针可以访问固定的存储空间,也可以调用固定地址的函数。
使用以下变量,对抽象指针进行描述。
char xdata *px; /* ptr:指向 RAM(xdata) 区; 存储在 RAM(data) 区 */
char idata *pi; /* ptr:指向 RAM(idata) 区; 存储在 RAM(data) 区 */
char code *pc; /* ptr:指向 Flash(code) 区;存储在 RAM(data) 区 */
char c; /* char variable:存储在 RAM(data) 区 */
int i; /* int variable:存储在 RAM(data) 区 */
- 把函数地址分配给指针
pc = (void *) main;
0000 750000 R MOV pc,#HIGH main
0003 750000 R MOV pc+01H,#LOW main
- data变量地址 转换为 idata指针
pi = (char idata *) &i;
0000 750000 R MOV pi,#LOW i
i 类型(int data),&i 类型(int data *),pi 类型(char idata *)。
i 存储在 RAM(data) 区,对 RAM(data) 间接访问就是idata,这个转换是有效的。
- xdata指针 转换为 idata指针
pi = (char idata *) px;
0000 850000 R MOV pi,px+01H
xdata指针占用2个字节, idata指针占用1个字节。进行转换时,会丢掉px的高字节,因此转换之后可能不会出现预期的效果。
- 常数值 转换为 code指针
pc = (char code *) 0x1234;
0000 750012 R MOV pc,#012H
0003 750034 R MOV pc+01H,#034H
把0x1234强制转换为 Flash(code) 区的地址,没有问题;
5.常数值 转换为 函数指针
i = ((int (code *)(void)) 0xFF00) ();
0000 12FF00 LCALL 0FF00H
0003 8E00 R MOV i,R6
0005 8F00 R MOV i+01H,R7
把0xFF00强制转换为函数指针,函数原型的形参(void),返回值(int);
函数指针类型指向Flash(code) ,指针大小为2字节。
- 常量值 转换为 code指针,取出指针空间内的数据
c = *((char code *) 0x8000);
0000 908000 MOV DPTR,#08000H
0003 E4 CLR A
0004 93 MOVC A,@A+DPTR
0005 F500 R MOV c,A
0x8000强制转换为code指针,取出指针空间内的数据,赋值给c。
- 常量值 转换为 xdata指针,取出指针空间内的数据
c += *((char xdata *) 0xFF00);
0000 90FF00 MOV DPTR,#0FF00H
0003 E0 MOVX A,@DPTR
0004 2500 R ADD A,c
0006 F500 R MOV c,A
0xFF00强制转换为xdata指针,取出指针空间内的数据,对c变量进行累加。
- 常量值 转换为 idata指针,取出指针空间内的数据
c += *((char idata *) 0xF0);
0000 78F0 MOV R0,#0F0H
0002 E6 MOV A,@R0
0003 2500 R ADD A,c
0005 F500 R MOV c,A
0xF0强制转换为idata指针,取出指针空间内的数据,对c变量进行累加。
- 常量值 转换为 pdata指针,取出指针空间内的数据
c += *((char pdata *) 0xE8);
0000 78E8 MOV R0,#0E8H
0002 E2 MOVX A,@R0
0003 2500 R ADD A,c
0005 F500 R MOV c,A
0xE8强制转换为pdata指针,取出指针空间内的数据,对c变量进行累加。
参考原文:《8051单片机基础6:通用指针和定向指针》