1、指针与指针变量指针与指针变量8.18.2指针与数据指针与数据8.3指针与函数指针与函数指针与字符串指针与字符串8.48.5指针数组与指向指针数组与指向指针的指针指针的指针8.1.1 8.1.1 指针的概念指针的概念 为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。内存区的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。按变量地址存取变量值的方式称为按变量地址存取变量值的方式称为“直接访问直接访问”方式方式(,);(,);例如:例如:8.1.1 8.1.1 指针的概念指针的概念 另一种存取变量值的方式
2、称为另一种存取变量值的方式称为“间接访问间接访问”的方式。即,的方式。即,将变量将变量的地址存放在另一个变量中的地址存放在另一个变量中8.1.1 8.1.1 指针的概念指针的概念 在语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量i_pointer用来存放整型变量的地址,它被分配地址为(3010)、(3011)的两个字节。可以通过语句:i_pointer;将的地址(2000)存放到i_pointer中。这时,i_pointer的值就是(2000),即变量所占用单元的起始地址。要存取变量的值,可以采用间接方式:先找到存放“的地址”的变量i_pointer,从中取出的地址(
3、2000),然后到2000、2001字节取出的值()。8.1.1 8.1.1 指针的概念指针的概念 8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 基本类型基本类型 *指针变量名指针变量名;定义指针变量的一般形式为:1.1.指针变量的定义指针变量的定义“先定义,后使用先定义,后使用”下面都是合法的定义:float*op3;/*定义了一个实型指针变量*/char*op4;/*定义了一个字符型指针变量*/8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 定义指针变量时应注意以下几点:2定义指针变量时必须指定其类型。指针数据都是地址,原本是没有区别的
4、。但不同的变量占据的内存大小不同,其适合的操作也不同,在使用指针变量进行间接访问操作时,必须知道其数据长度及适合运算,因此必须指定其类型。1指针变量前面的*表示要定义的是指针变量,而不是算术运算中的乘号,指针变量名是*后面的标识符。每定义一个指针变量,就要写一个*。8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 3各类型的指针变量只能指向同类型的变量。2 2.指针变量的初始化和引用指针变量的初始化和引用 C语言中有两个地址运算符,分别对应指针变量的两种运算:&:取地址运算符,运算对象为变量,运算结果为运算对象的内存地址。例如:int i,*pi;pi=&i;/*pi指
5、向i*/8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 *:间接访问运算符,运算对象为指针变量,在一个指针变量前加*,表示该指针所指向的内存单元的值。也就是说,指针变量保存的是一个内存的起始地址,指针变量加*后表示该地址所对应的存储单元的值。例如:*pi=5;/*间接访问pi,即以pi的内容1000为地址的存储单元的内容为5*/8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 定义了指针 pi 并赋值后,内存的分配情况如图所示:i(1000)i(1000)100010005 5*pipipipi 指针变量同普通变量一样,使用之前不仅要定义说明,
6、而且必须赋予具体的值。在定义指针变量时,可以给指针变量赋一个初始值,这一过程称为初始化。未经赋值的指针变量不能使用。指针变量只能赋予地址,不能赋予其它任何数据,否则将引起错误,这时就用到了指针运算符&和*。8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 (1)Pf 是一个指针变量,存放了变量f的指针(也就是变量f的内存地址)。*pf 表示指针所对应的内存单元的值,实际上就是变量f的值。因此,对*pf 的赋值会直接改变变量f的值。(2)*运算符的优先级比算术运算符高,因此,*pf+1.0等价于(*pf)+1.0。8.1.2 8.1.2 指针的定义、初始化与应用指针的定义
7、、初始化与应用 【例【例】输入两个整数 x 和 y,比较后降序输出。include void main()int*op1,*op2,*op,x,yscanf(%d%d,&x,&y);op1=&x;op2=&y;if(xy)op=op1;op1=op2;op2=op;/*两个指针变量交换,交换以后,op1指向存放大数的单元,op2指向存放小数的单元*/printf(x=%d,y=%dn,x,y);printf(max=%d,min=%dn,*op1,*op2);8.1.2 8.1.2 指针的定义、初始化与应用指针的定义、初始化与应用 运行结果:128365a=365,b=128Max=365,m
8、in=128128365opop1op2128365opop1op2op1op2xy8.1.3 8.1.3 指针变量作为函数参数指针变量作为函数参数 用指针变量作为函数的参数,形参和实参都应当为指针类型数据。使用指针变量作参数,在函数执行过程中能够使指针变量所指向的变量值发生变化,从而在函数调用结束后这些变化后的值得以保存,并在主调函数中使用它们。【例【例】指针作为函数参数示例。参考程序:参考程序:void Doub(float*pNum)*pNum=*pNum*2;main()float f=10.0;Doub(&f);printf(nn=%f,f);运行结果:运行结果:n=20.00000
9、08.1.3 8.1.3 指针变量作为函数参数指针变量作为函数参数 传递指针的过程是值由实参向形参的传递指针的过程是值由实参向形参的单向传递过程,形式参数的变化不会单向传递过程,形式参数的变化不会影响到实际参数的值。需要注意的是,影响到实际参数的值。需要注意的是,不能企图通过改变指针形参的值而使不能企图通过改变指针形参的值而使指针实参的值也改变指针实参的值也改变 。8.2.1 8.2.1 指针数据元素的指针指针数据元素的指针 所谓数组的指针是指数组的起始地址,数组元素的指针是所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。数组元素的地址。一个数组是由连续的一块内存单元组成的,
10、数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的,每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址。8.2.1 8.2.1 指针数据元素的指针指针数据元素的指针&a0pa013151719:a9p9*(a+9)或*(p+9)8.2.1 8.2.1 指针数据元素的指针指针数据元素的指针 下面两个语句等价:op=&a0;op=a;在定义指针变量时可以赋给初值:int*op=&a0;它等效于:int*op;op=&a0;当然定义时也可以写成:int*op=a;数组指针变量说明的数组指针变量说明的一般形式为:一般形
11、式为:基类型基类型 *指针变量名;指针变量名;8.2.2 8.2.2 通过指针引用数据元素通过指针引用数据元素通过下标的引用通过下标的引用通过指针的引用通过指针的引用 即用ai来引用数组a的第i个元素。在前面介绍数组时都是采用这种方法。即采用*(a+i)或*(op+i)形式,用间接访问的方法来访问数组元素,其中a是数组名,op是指向数组的指针变量,其初值op=a。方法方法引用一个数组元素可以采用以下两种方法:8.2.2 8.2.2 通过指针引用数据元素通过指针引用数据元素【例例】输出数组中的全部元素(用指针变量指向元素)。参考程序:参考程序:#include void main()int a1
12、0,i,*op;op=a;for(i=0;i10;i+)*(op+i)=i;for(i=0;i10;i+)oprintf(a%d=%dn,i,*(op+i);8.2.2 8.2.2 通过指针引用数据元素通过指针引用数据元素1)指针变量可以实现本身的值的改变。如op+是合法的,而a+是错误的。因为a是数组名,它是数组的首地址,是常量。2)3)指针变量的当前值。虽然定义数组时指定它包含10个元素,但指针变量可以指到数组以后的内存单元,系统并不认为非法。对指针变量值进行改变时应注意以下几点:8.2.2 8.2.2 通过指针引用数据元素通过指针引用数据元素4)*op+,由于+和*同优先级,结合方向自右
13、而左,等价于*(op+)。6)5)(op+)与*(+op)作用不同。若op的初值为a,则*(op+)等价a0,*(+op)等价a1。(*op)+表示op所指向的元素值加1。7)如果op当前指向a数组中的第i个元素,则:*(op-)相当于ai-;*(+op)相当于a+i;*(-op)相当于a-i。8.2.3 8.2.3 数组名作为函数参数数组名作为函数参数 如果有一个实参数组,想在函数中改变此数组的元素值,则实参与形参的对应关系有以下4种:1 1.形参和实参都是数组名形参和实参都是数组名void main()int a10;f(a,10)f(int x,int n)和和指的是同一个数组。指的是同
14、一个数组。8.2.3 8.2.3 数组名作为函数参数数组名作为函数参数2 2.实参用数组名,形参用指针变量实参用数组名,形参用指针变量void main()int a10;f(a,10)f(int*x,int n)实参a为数组名,形参x为指针,通过x指针值的变化,可以指向数组a的任意元素。8.2.3 8.2.3 数组名作为函数参数数组名作为函数参数3 3.形参和实参都用指针变量形参和实参都用指针变量void main()int a10,*p;p=a;f(p,10);f(int*x,int n)实参p和形参x都是指针变量,通过指针p将数组a的地址传给x,再通过x指针值的变化指向数组a的任意元素。
15、8.2.3 8.2.3 数组名作为函数参数数组名作为函数参数void main()int a10,*p;p=a;f(p,10);f(int x,int n)实参p为指针变量,通过p将数组的首地址传给形参x,而x是数组名,虽然把x当做指针处理,但实际上x的使用与基本数组类型的使用方法是一样的。4.4.实参用指针变量,形参用数组名实参用指针变量,形参用数组名8.3.1 8.3.1 指针作为函数参数指针作为函数参数 使用指针作函数的形参时,要求实参用地址值,实现变量地址的传递,这种调用称为传址调用。它的特点是可以在被调用函数中通过改变形参的内容(即形参所指的变量的值)来改变调用函数中实参的值。1.1
16、.基本数据类型的指针作为函数参数基本数据类型的指针作为函数参数 它是指针作为函数参数是最常用的一种方式,它可以实现函数之间的数据传递,即可将被调函数中的值通过参数传递给主调函数,这种传递既安全可靠,又可一次实现多个数据传递。8.3.1 8.3.1 指针作为函数参数指针作为函数参数2.2.数组名作为函数参数数组名作为函数参数 在C语言中,数组名实质上是一个指针,是首元素的地址值。用数组名作为函数参数,实现的是传址调用。数组名可以作为函数的形参,也可以作为函数的实参;另外,可用数组名作为实参,用指针作为形参;还可用数组名作为形参,指针作为实参。8.3.1 8.3.1 指针作为函数参数指针作为函数参
17、数3.3.指向数组的指针作为函数参数指向数组的指针作为函数参数 字符指针是指向字符串的指针。该指针实际上是指向一维字符数组首元素的指针,即指向字符串的首字符的指针。4.4.字符指针作为函数参数字符指针作为函数参数8.3.1 8.3.1 指针作为函数参数指针作为函数参数5.5.指向指针的指针作为函数参数指向指针的指针作为函数参数 指向指针的指针是一个二级指针,该指针所指向的变量还是一个指针,而这个指针指的是一个非指针变量,如果这个指针所指的仍然是一个指针,则该指针为三级指针。8.3.1 8.3.1 指针作为函数参数指针作为函数参数6.6.指向数组作为函数参数指向数组作为函数参数 指针数组作为函数
18、参数实际上与数组名作为函数参数和指向指针的指针作为函数参数是相同的。8.3.2 8.3.2 指针函数指针函数 前面所用到的函数中,实参或形参都可以是指针变量,但返回值都不是指针,而是某一实际值。实际上,不同类型的指针是可以作为函数返回值的。指针函数就是返回值指针的函数。类型说明符类型说明符 函数名(参数列表)函数名(参数列表);指针函数的定义格式如下:指针函数的定义格式如下:char*linkstr(str1,str2);这是一个指针函数,函数名是linkstr,该函数有两个参数 str1和 str2,该函数的返回值是一个char型指针。8.3.3 8.3.3 指向函数的指针变量指向函数的指针
19、变量 变量是与其存储单元相联系的,通过变量名可以得到变量的存储地址。一个函数包含一组指令序列,存储在某一段内存中,这段内存空间的起始地址为函数的入口地址,通过该地址也可以找到这个函数,故称函数的入口地址为函数的指针。在实际应用中,可以定义一个指针变量,其值等于该函数的入口地址,使其指向这个函数,这样通过这个指针变量也可以调用这个函数。这种指针变量成为指向函数的指针变量。而这种调用函数的方式与前面用函数名来调用函数的方式是不一样的。定义指向函数的指针变量的一般格式如下:类型说明符类型说明符 (*指针变量名指针变量名)();)();8.3.3 8.3.3 指向函数的指针变量指向函数的指针变量 用一
20、个函数名给指向函数的指针赋值,则该指针指向该函数所存放在内存中的入口地址,即一个函数存放在内存中的入口地址是用函数名来表示的,而不必给出参数,也不要圆括号。用指向函数的指针调用函数时,只需使用如下格式:*(指针变量名)(实参表)8.4.1 8.4.1 字符串的表示形式字符串的表示形式1.1.用字符数组实现用字符数组实现2.2.用字符指针实现用字符指针实现 可以不定义字符数组来实现字符串的操作,而通过字符指针来访问字符串中的存储区。char*ps;ps=modi string;此时,字符指针ps指向该字符串常量中的首字符“m”,使用指针ps的加、减运算即可移动指针,以便能访问到该字符串的任何一个
21、字符,并进行存取操作。8.4.2 8.4.2 字符指针作为函数参数字符指针作为函数参数 将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作为参数或用指向字符串的指针作为参数。在被调函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。8.4.3 8.4.3 使用字符指针变量与字符数组的区别使用字符指针变量与字符数组的区别(1)字符数组由若干元素组成,每个元素中存放一个具体的字符,而字符指针变量中存放的是地址即字符串的首地址,通过指针指向相应的字符,而不是用指针变量去存放字符串中的每一个字符。(2)赋初值的方式不同。对数组赋初值要用static存储类型,例如
22、:static str1=I will do my best!;而对字符指针变量赋初值不必加static存储类型,例如:char*ps=I will do my best!;这是因为并没有对数组初始化,只是对指针变量初始化。8.4.3 8.4.3 使用字符指针变量与字符数组的区别使用字符指针变量与字符数组的区别(3)赋值方式不同。字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值:char str14;str=I will do my best!;而对字符指针变量,可以采用下面的方法赋值:char*a;a=I will do my best!;但赋给a的不是字符,而是字符串的首地址。8.
23、4.3 8.4.3 使用字符指针变量与字符数组的区别使用字符指针变量与字符数组的区别(4)指针变量赋初值时方法灵活多样。例如,对以下的变量定义和赋初值:char*a=I will do my best!;等价于:char*a;a=I will do my best!;而对数组初始化时,可以在变量定义时整体赋初值,但不能在赋值语句中整体赋值,例如:static char str114=I will do my best!;不等价于:char str114;str1=I will do my best!;8.4.3 8.4.3 使用字符指针变量与字符数组的区别使用字符指针变量与字符数组的区别(5)
24、定义一个数组,在编译时即已分配内存单元,有确定的地址。而定义一个字符指针变量,给指针变量分配内存单元,在其中可以放一个地址值,即该指针变量可以指向一个字符型数据,但如果未赋予它一个地址,则它并不具体指向哪一个字符数据。(6)指针变量的值是可以改变的,而数组名代表的是字符串的首地址,不能改变。8.5.1 8.5.1 指针数组指针数组 指针数组是指数组元素为指针的数组。前面介绍过数组元素可以为int型、float型和double型数值,称为数值数组;数组元素为char型的称为字符数组。由若干指向同一数据类型的指针构成的数组,则称为指针数组,即每个元素都是指针的数组。最常用的是一维一级指针数组,即该
25、数组是一维的,它的每个元素是一个一级指针或一维数组。8.5.1 8.5.1 指针数组指针数组指针数组定义格式如下:类型说明符类型说明符 *(数组名数组名);指向一维数组的指针定义格式如下:类型说明符类型说明符 (*(数组名数组名);8.5.1 8.5.1 指针数组指针数组int*p5;int(*g)5;其中,其中,p p是一维一级指针数组名,而是一维一级指针数组名,而g g是是指向一维数组的指针名。指向一维数组的指针名。8.5.1 8.5.1 指针数组指针数组 指针数组比较适合于对若干字符串的处理。字符串本身是一个一维字符数组,如果将若干字符串放到一个二维字符数组中,则每一行的元素个数要求相同
26、;实际上各字符串长度不等,只好以字符串中最长的作为每行的元素个数,这样将会造成内存空间的浪费。如果采用指针数组,便可解决存储空间浪费的问题。因为指针数组中各个指针元素可以指向不同长度的字符串,所以实际编程中常用字符型的指针数组存放字符串。8.5.2 8.5.2 指向指针的指针指向指针的指针 与直接访问变量的方法相比,通过指针访问变量是一种间接访问变量的方法。如果一个指针变量中存放另外一个指针变量,而这另外一个指针指向的才是一个基本类型的变量,则称这个指针变量是指向指针的指针。由于指针变量直接指向变量,所以称为单级间接访问。而如果通过指向指针的指针变量来访问变量则构成了二级或多级间接访问。在语言程序中,对间接访问的级数并未明确限制,但是间接访问级数太多时不容易理解,也容易出错,因此,一般很少采用超过二级的间接访问。8.5.2 8.5.2 指向指针的指针指向指针的指针指向指针的指针变量说明的一般形式为:指向指针的指针变量说明的一般形式为:类型说明符类型说明符 *指针变量名;指针变量名;int*op;