指针不完全手册

本文详细介绍了C语言中的指针概念,包括指针的基本定义、类型和特性,以及如何通过指针访问不同的存储区域。文章探讨了一般指针、基于存储器指针和抽象指针的区别,并提供了具体的示例来展示它们的用法。此外,还涵盖了指针在数组、函数以及多级指针中的应用,展示了指针在C语言编程中的灵活性和重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

New Page 2

指针不完全手册

第一节 指针基本概念
1、指针定义:c语言的一种基本数据类型,地位等同于char ,int,*等;这种数据类型的变
量总是存放一个内存单元地址。
2、指针的特性:指针具有指向性,即指向某一数据实体。
3、指针变量:用指针数据类型定义的变量,其内容总是一个内存单元地址或NULL。指针变
量本身也占有一定的内存单元,占几个字节跟此指针变量的类型有关,最多3字节,最少1字
节。若此变量存放A数据实体的内存单元地址,我们称此指针变量指向A数据实体
4、指针变量的三个要素:⑴、所指的对象类型、⑵、对象位于的存储空间、⑶、指针变量
自己位于的存储空间;
每一指针总具有这三个要素,只不过有的要素明确定义决定,有的要素隐含定义决定,有的
要素程序运行时才决定。
5、指针变量的类型:
根据三要素定义与否指针可分为三种类型:一般指针、基于存储器指针、抽象指针,其中要
素3不影响指针类型,只影响指针变量本身存储区域。
①、一般指针(Generic Pointers):要素1明确定义的,要素2未定义;同标准C语言的指
针。
此类指针变量总占3个字节,第一字节表明所指对象的存储器类型(对象位于的存储空
间),存储器类型编码(第一字节内容)0x00—idata/data/bdata;0x01—xdata;0xfe—
pdata;0xff—code;第二字节存放所指对象的内存单元地址的高位字节,第三字节存放所指
对象的内存单元地址的低位字节。
程序运行时,才能决定他指向哪个存储区域的对象;因此可以访问8052的所有存储空间。定
义好之后未使用之前,他不指向任何单元,使用时赋给他xdata的地址它就指向xdata区的对
象(二三字节均为有效值),赋给他data的地址它就指向data区的对象(二字节为00,三字节
为有效值)。
例:char *p; //定义
char xdata x;
int data y;
p = &x; //p 指向 xdata 区
p = (char *)&y //p 指向 data区 ,&y指向整数,所指对象类型不同,必须强制转
换,
②、基于存储器指针(Memory-specific pointers)要素1、2明确定义的;属keil的扩展定
义,我们比较常用。
此类指针变量占1-2个字节,均存放所指对象的内存单元地址。基于idata/data/pdata时占1
字节,基于xdata/code时占2字节。
所指对象的存储区域是编译期间决定的,程序运行期间不会改变,因此生成代码短于一般指
针,运行速度较快,但应用缺乏灵活性。
例:char xdata *p; //定义
char xdata x;
int data y;
p = &x; //p 指向 xdata 区
p = (char *)&y //p 还指向 xdata区 &y指向整数,所指对象类型不同,必须强制
转换,
③、抽象指针(Abstract pointer)要素1、2未明确定义的指针或一个具体数值;我们不常
用。
此类指针所指对象的类型及对象的存储空间都是NULL。
此类指针一般用来产生绝对调用或用来访问某存储区域的任意绝对地址。使用时一般是把抽
象指针强制类型转换为别的类型指针。
例:void *p; //抽象指针定义
char xdata *px; //定义基于存储器指针
char c;

应用:
c = *((char xdata *)0xff00);
//绝对访问xdata区的地址为0xff00单元。换句话说0xff00是一个抽象指针,被强制类型转
化为指向xdata区的char 类型。再把它的对象赋给c

px = *((char xdata * xdata *)0x4000);
//绝对访问xdata区的地址为0x4000单元。换句话说0x4000是一个抽象指针,被强制类型转
化为指向xdata区的char xdata * 类型。”char xdata *”也是一个指针,指向位于xdata
区的char 类型,把char xdata *这个指针变量赋给px.

px = ((char xdata * xdata *)0x4000)[1];
//绝对访问xdata区的地址为0x4002,0x4003单元。换句话说0x4000是一个抽象指针,被强
制类型转化为指向xdata区的char xdata * 类型。”char xdata *”也是一个指针,指向位
于xdata区的char 类型,因为后者指向xdata 区,所以占连续两个字节。 现在访问以
0x4000为起点的第二个元素,把第二个元素的内容赋给px。第一个元素占00,01两个单元,
所以,第二个元素占02,03两个单元。这与<absacc.h>中的绝对访问整数的道理是一样的。
<absacc.h>的定义如下:(以下这段英文来自keil的帮助文件)
#define XWORD ((unsigned int volatile xdata*) 0)
You may use this macro in your programs as follows:
rval = XWORD [2];
XWORD [2] = 57;
to read or write the contents of the word in external data memory at address 
0004h (2 × sizeof (unsigned int) = 4).

i = ((int(code*)(void))0xff00)();
//不带参数执行从0xff00开始的函数,函数返回值是一个整数,把返回值赋给I。换句话说
0xff00是一个抽象指针,被强制类型转化为函数指针。

i = ((int(code*)(unsigned char, int))0xff00)(a,b);
//带参数a,b执行从0xff00开始的函数,形式参数的类型为unsigned char 和int;;a,b 为
实参;函数返回值是一个整数,把返回值赋给I。换句话说0xff00是一个抽象指针,被强制
类型转化为函数指针。好多人想了很久的方法!

也可以用一般指针做绝对地址访问,方法如下:
char *p; //定义一般指针
char data c;
p = (char *) 0x50003L;//p 指向code区的0x0003单元
c = * p //绝对访问code区的0x0003单元

char *p; //定义一般指针
char data c;
p = 0x50003l;//p 指向code区的0x0003单元,默认强制类型转换。
c = * p //绝对访问code区的0x0003单元
其中0x5000L的意思是5赋给一般指针的第一字节,(5会自动编译为 0xff送第一字节,为什
么要送5,而不直接送0xff,可能是为了与早期版本兼容的原因(谁查到确切原因请告诉我。
Data 送 4;pdata 送 3;xdata 送 2;code 送 5);这就是为什么马忠梅的书与徐爱军
的书讲到指针编码时有冲突的原因;早期弗兰克林的c51<asbacc.h>的宏就是如下定义的
#define XBYTE (unsigned char *)0x20000L)。)0003赋给二三字节
6、指针类型的转换
大多数库函数的参数为一般指针,而我们经常定义基于基于存储器指针,因此需要相互转
化。指针类型的转换上面的例子已涉及到一些,下面主要论述以下一般指针与基于存储器指
针的相互转化。(以下这段英文来自keil的帮助文件)
①、generic * to code * The offset section (2 bytes) of the generic pointer is 
used.
②、generic * to xdata * The offset section (2 bytes) of the generic pointer is 
used.
③、generic * to data * The low-order byte of the generic pointer offset is 
used,The high-order byte is discarded.
④、generic * to idata * The low-order byte of the generic pointer offset is 
used,The high-order byte is discarded.
⑤、generic * to pdata * The low-order byte of the generic pointer offset is 
used,The high-order byte is discarded.

①、code * to generic * The memory type of the generic pointer is set to 0xFF 
for code.The 2-byte offset of the code * is used.
②、xdata * to generic * The memory type of the generic pointer is set to 0x01 
for xdata.The 2-byte offset of the xdata * is used.
③、data * to generic * The 1-byte offset of the idata * / data * is converted 
to an unsigned int and used as the offset.
④、idata * to generic * The memory type of the generic pointer is set to 0x00 
for idata / data.
⑤、pdata * to generic * The memory type of the generic pointer is set to 0xFE 
for pdata.The 1-byte offset of the pdata * is converted to an unsigned int 
andused as the offset.



第二节 指针的应用
1、 指针与变量
定义一个变量,再定义一个指向该变量类型的指针,把该变量的地址赋给此指针,然后通过
此指针访问该变量,教科书上都是这样说的,但恐怕没有人这样用,因为还不如直接访问变
量名直截了当。问题是为什么这两种方法都可以访问那个特定内存单元呢?答案只有一个:
变量名和变量的指针含有相同的信息:1那个特定内存单元地址,2连续访问单元数目(对象
所占的字节数)。对于指针来讲1号信息通过赋值实现(可改变),2号信息通过定义实现,
不可改变(char 为1,int 为 2),以后就会看到只要定义指针2号信息必须定义(要素
1),这就是为什么抽象指针不能访问对象的原因(计算机不知道到底该读几个单元)。对
于变量名来讲这两条信息都是通过定义实现的,而且不可改变。
一般说来,变量的指针总是指向变量的低地址;变量的指针+1总是加上连续访问单元数目。
灵活的改变这两个信息就可以灵活的使用指针
例:
unsigned int v;
unsigned char word_hi , word_low;
char * p;
v = 0x1234;
p = ( char * )&v;
word_hi = *p++;
word_low = *p;
这就是一个把一个整数即可以当整数访问,也可以当字节访问的方法。灵活的利用这个例子
你也可以把数组当作任意类型的数据进行访问。
2、 指针与数组
指针与数组的关系非常密切,数组所有的操作都可以用指针来实现。用指针也可以把一段内
存区域当数组来访问。
数组名表示存放数组内存单元的首地址,是这个地址的标号。数组总占内存的一段连续的存
储单元。
①、指针与一维数组
unsigned char array[10];
unsigned char *p
下面2种方法都可以把指针指向数组
p = array;
p = &arrary [0];
引用数组元素的方法
p + i 和array + i都是数组元素array[i]的地址
*(p + i) 和*(array + i)都是数组元素array[i]
*(p + i)和 p[i] 都是数组元素array[i]
p + i是指向数组的下一个元素,实际地址是p+i*d,d是连续访问单元数目(对象所占的字节
数)。
定义并赋值一个指针p之后,就可以用 p[i]的方式把一段内存区域当数组来访问。

②、指针与多维数组
Unsigned char t[3][4];
Unsigned char * p;
P = &t[0][0]; //若p = t会产生警告错误
//此时整个t数组被看作一维数组
*(p+4)与t[1][1]、p[4]等同,表示t[1][1]的值

Unsigned char t[3][4];
Unsigned char (*ph)[4];
Ph = t;
//此时整个t数组被看作具有三个 一维数组t_col[4](包含4个char类型元素)元素的一维
数组t_row[3]

ph指向t_row数组的第0个元素(一维数组)与t[0][0]的地址值相同,不等同于*(ph)。
ph+2指向t_row数组的第二个元素(一维数组);与t[2][0]的地址值相同,不等同于*
(ph+2)。
*(ph+2)指向t_col的第0个元素(char类型)与ph[2],&t[2][0]等同,表示t[2][0]的地
址 
*(*(ph+2))与t[2][0]等同,表示t[2][0]的值 
*(ph+2)+1与&t[2][1]等同,表示t[2][1]的地址 
*(*(ph+2)+1)与t[2][1]、(*(ph+2))[1]、ph[2][1]等同,表示t[2][1]的值
因为ph 表示的是一个指针,此指针指向包含4个元素的一维数组。或者说ph是一个指针, 此
指针所指对象是包含4个元素的一维数组,此一维数组是作为一个整体被此指针访问,连续
访问单元数目是4*1(char型)。整体访问数组的办法是给出这个数组的指针。
这个例子很容易与后面讲到的指针数组相混淆,注意区别。
虽然上面列举了很多访问数组元素的方法,只不过想说明每种指针的不同而已;建议访问数
组元素还是用p++,*p这样的方式来访问,不要用*(p+1)来访问。前一种方法指针直接指向
要访问的元素,而后一种方法只是把指针当作一个起点,然后起点加上偏移量,访问速度
慢,而且也没有体现出使用指针本来的用意。后一种方法与直接用数组名访问数组元素相
同。
③、指针数组
指针数组是讲一个元素全部是指针的数组。
定义方法同普通数组: 数据类型 * 数组名[数组长度]
例:char * s[2];//定义一个含有2个指针元素的数组,元素指针指向char类型,属一般指
针。
前面提到的指向多维数组的指针是这样定义的:数据类型 * 指针名[一维长度]
例:char (* s)[2];//注意这里的s是一个指针,而上例s是一个数组名,二者截然不同,
指针可以实现自加运算,而数组名不能。
指针数组的典型应用是通过对字符数组赋初值而实现各维长度不一的多维数组定义。
例:char code season[4] = {“spring”,”summer”,”fall”,”winter”}
3、 指针与函数
①、指针变量作为函数的参数
指针变量作为函数的参数时,形势参数指针和实际参数指针二者进行的是值传递,直接把实
参的值(注意:这个值是一个其它变量地址。)传给形参,传递的结果不会影响到实参的
值。例pa实参原指向00H单元,传递后仍然指向00H单元,但函数执行后,00H单元里的内容
(*pa)可能会发生变化。
②、返回指针数据的函数
返回指针数据的函数:某函数的返回值是一个指针。
定义方法:数据类型 * 函数名(形参)
例:int * x(char a)
③、函数形指针
函数形指针:一个指向函数入口地址的指针
函数形指针可代替函数名进行函数调用,通常是将一个函数作为参数传递给另一个函数。
定义方法:数据类型 (* 指针名)(形参)
例: int (* pfunc)();//定义一个指向返回值为整形的函数的指针
赋值方法:函数形指针变量名 = 函数名
引用方法:(* 指针名)(实参)或 指针名(实参)
④、函数指针数组
函数指针数组:定义一个数组,数组元素全部为函数形指针
例:code void (code * keyproctab[])() = {k0,k1,k2}
keyproctab :数组名
k0,k1,k2:已定义的函数名
第一个code表示数组的存储空间
void code 表示各数组元素的类型
引用方法:(* keyproctab[2])();//执行函数k2
以上这个例子就是汇编语言中常用的散转,数组下标,表示按键号,对应此下标的函数是键
处理程序。
4、 指针与指针
①、指针形指针
指针形指针:指向另一个指针变量的指针,称多级指针。
定义:数据类型 **指针名
数据类型说明一个被指针形指针所指向的指针变量所指向的变量数据类型
例:int **p;

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值