版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第第8 8章章 C C语言指针语言指针下一页下一页返返 回回本章要点本章要点 指针的概念指针的概念 指针变量的定义和使用指针变量的定义和使用 指针在数组和函数中的应用指针在数组和函数中的应用 字符指针的引用字符指针的引用学习方法建议学习方法建议 本章的内容是本章的内容是C语言中最难理解的部分,学习时应注意语言中最难理解的部分,学习时应注意归纳。首先要理解指针的概念,然后掌握指针变量与一般归纳。首先要理解指针的概念,然后掌握指针变量与一般变量的区别,掌握其定义方法,同时还要理解指针与数组、变量的区别,掌握其定义方法,同时还要理解指针与数组、指针与函数的关系,这一章要多下一些功夫才能真正学会指针与
2、函数的关系,这一章要多下一些功夫才能真正学会指针的应用。指针的应用。目目 录录8.18.1 指针与指针变量指针与指针变量8.28.2 指针与数组指针与数组8.38.3 指针与字符串指针与字符串8.58.5 指针应用举例指针应用举例 返返 回回上一页上一页8.68.6 本章小结本章小结 8.48.4 指针与函数指针与函数 8.1 指针与指针变量指针与指针变量下一页下一页返返 回回8.1.1 指针的概念指针的概念计算机的内存是以字节为单位的一片连续的存储空间,每一个字节都有一个计算机的内存是以字节为单位的一片连续的存储空间,每一个字节都有一个编号,这个编号就称为内存地址。编号,这个编号就称为内存地
3、址。 一般来讲,程序中的变量经过编译系统处理后都对应着内存中的一个地址,一般来讲,程序中的变量经过编译系统处理后都对应着内存中的一个地址,也就是说,编译系统根据变量的类型,为其分配相应的内存单元,以便存放也就是说,编译系统根据变量的类型,为其分配相应的内存单元,以便存放变量的内容。不同类型的变量所分配的内存单元的长度是不一样的。变量的内容。不同类型的变量所分配的内存单元的长度是不一样的。若在程序中定义了一个变量,若在程序中定义了一个变量,C编译系统就会根据定义中变量的类型,为其编译系统就会根据定义中变量的类型,为其分配一定字节的内存空间,此后,这个变量的内存地址也就确定了。例如,分配一定字节的
4、内存空间,此后,这个变量的内存地址也就确定了。例如,若有定义:若有定义:int a;这时,将如下图所示:系统为变量;这时,将如下图所示:系统为变量a分配分配4个字节的存储个字节的存储单元,图中的数字只是示意的字节地址。每个变量的地址是指该变量所占存单元,图中的数字只是示意的字节地址。每个变量的地址是指该变量所占存储单元的第储单元的第1个字节地址。在这里,我们称个字节地址。在这里,我们称a的地址为的地址为1201。变量变量a1201 1202 1203 1204 在在C语言中,我们还可以定义一种特殊的变量,这种变量是专门用来存放内存语言中,我们还可以定义一种特殊的变量,这种变量是专门用来存放内存
5、地址的。如图地址的。如图8.2(a)所示,假设我们定义了一个这样的变量)所示,假设我们定义了一个这样的变量p,它也有自已,它也有自已的地址(的地址(2002);若将变量);若将变量a的内存地址(的内存地址(1201)存放到变量)存放到变量p中,这时要访中,这时要访问变量问变量a所代表的存储单元,可以先找到变量所代表的存储单元,可以先找到变量p的地址(的地址(2002),从中取出),从中取出a的的地址(地址(1201),然后再去访问以),然后再去访问以1201为首地址的存储单元。这种通过变量为首地址的存储单元。这种通过变量p间间接得到变量接得到变量a的地址,然后再存取变量的地址,然后再存取变量a
6、的方式称为的方式称为“间接存取间接存取”方式。方式。上一页上一页下一页下一页返返 回回在上述情况下,通常称变量在上述情况下,通常称变量p指向了变量指向了变量a,变量,变量a是变量是变量p所指的对象;它所指的对象;它们之间的关系可用图们之间的关系可用图8.2(b)表示。这种)表示。这种“指向指向”关系是通过地址建立的,关系是通过地址建立的,图中的图中的“”只是一种示意,形似只是一种示意,形似“指针指针”。从这个意义上讲,我们将一。从这个意义上讲,我们将一个变量的个变量的“地址地址”称为称为“指针指针”。如:变量。如:变量a的地址就称为变量的地址就称为变量a的指针。的指针。显然,指针是地址的一个形
7、象化的名称。相应地,用来存放指针(即地址)显然,指针是地址的一个形象化的名称。相应地,用来存放指针(即地址)的变量就称作的变量就称作“指针变量指针变量”。上述变量。上述变量p就是一个指针变量。所以就是一个指针变量。所以“变量变量p指向了变量指向了变量a”的含义就是指针变量的含义就是指针变量p中存放了变量中存放了变量a的地址。的地址。上一页上一页下一页下一页返返 回回1指针变量的定义指针变量的定义任何变量在使用前都必须先定义,指针变量也一样。指针变量定义的一般形任何变量在使用前都必须先定义,指针变量也一样。指针变量定义的一般形式为:式为:数据类型数据类型 *指针变量名指针变量名1,*指针变量名指
8、针变量名2,;例如:例如:int *p1,*p2;说明:说明:(1)定义两个指针变量,变量名为)定义两个指针变量,变量名为p1和和p2,这两个变量只能用来存放整型,这两个变量只能用来存放整型变量的地址。变量的地址。(2)这里)这里“*”表示定义的是指针变量,是指针类型的标志。表示定义的是指针变量,是指针类型的标志。(3)“类型名类型名”表示该指针变量所指向的变量的类型,也称该指针变量的表示该指针变量所指向的变量的类型,也称该指针变量的基类型,定义指针变量时必须要有基类型,因为一个指针变量中存放的是一基类型,定义指针变量时必须要有基类型,因为一个指针变量中存放的是一个存储单元的地址值,而这里的个
9、存储单元的地址值,而这里的“一一”对于不同类型的数据,所代表的字节对于不同类型的数据,所代表的字节数是不同的:对于数是不同的:对于int型数据为型数据为4个字节,对于个字节,对于double型数据为型数据为8个字节,一个字节,一个指针变量只能指向同一种类型的变量。个指针变量只能指向同一种类型的变量。8.1.2 指针变量的定义和使用指针变量的定义和使用2指针变量的使用指针变量的使用在利用指针变量进行间接访问之前,必须使其指向一确定的变量。在利用指针变量进行间接访问之前,必须使其指向一确定的变量。C语言中提供了两个与指针有关的运算符:语言中提供了两个与指针有关的运算符:& 和和 *。(1)“&”是
10、取地址运算符,它的功能是取得变量所占用的存储单元的首地是取地址运算符,它的功能是取得变量所占用的存储单元的首地址。在利用指针变量进行间接访问之前,一般都必须使用该运算符将某变量址。在利用指针变量进行间接访问之前,一般都必须使用该运算符将某变量的地址赋给相应的指针变量。的地址赋给相应的指针变量。例如:例如:int a,b,*pa,*pb;pa=&a; pb=&b; 可以通过赋值运算,把一个指针变量的地址值赋给具有相同基类型的另一个可以通过赋值运算,把一个指针变量的地址值赋给具有相同基类型的另一个指针变量,从而使这两个指针变量获得同一个地址值,即指向同一个变量。指针变量,从而使这两个指针变量获得同
11、一个地址值,即指向同一个变量。例如:例如:pb=pa;使指针使指针pb中也存放了中也存放了a的地址,也就是指针变量的地址,也就是指针变量p和和q都指向同一个变量都指向同一个变量a。除了给指针变量赋地址值外,还可以给指针变量赋除了给指针变量赋地址值外,还可以给指针变量赋NULL值,例如:值,例如:pa=NULL;上一页上一页下一页下一页返返 回回NULL是在是在stdio.h头文件中定义的预定义符,因此在使用头文件中定义的预定义符,因此在使用NULL时,应该在时,应该在程序的前面出现预定义行:程序的前面出现预定义行:#include stdio.h。NULL的值为的值为0,当执行了,当执行了以上
12、的赋值语句后,称以上的赋值语句后,称p为空指针。因为为空指针。因为NULL的代码为的代码为0,所以以上语句等,所以以上语句等价于:价于:pa=0; 或或 pa=0;这时,指针这时,指针p并不是指向地址为并不是指向地址为0的存储单元,而是具有一个确定的值,即的存储单元,而是具有一个确定的值,即空值。企图通过一个空值去访问一个存储单元时,将会得到一个出错信息。空值。企图通过一个空值去访问一个存储单元时,将会得到一个出错信息。(2)“*”是间接访问运算符(又称指针运算符),它的功能是通过指针变是间接访问运算符(又称指针运算符),它的功能是通过指针变量来间接访问它所指向的变量。例如:量来间接访问它所指
13、向的变量。例如:int i,j,*p;p=&i;*p=10; /*将将10赋给赋给p所指向的变量即变量所指向的变量即变量i,等价于赋值语句等价于赋值语句i=10;*/j=*p+1; /*取指针变量取指针变量p所指向的存储单元中的值加所指向的存储单元中的值加1后赋给变量后赋给变量j,j的值为的值为11*/*p=*p+1 /*取指针变量取指针变量p所指向的存储单元中的值,加所指向的存储单元中的值,加1后再放入后再放入p所指向的所指向的存储单元中,也就是使变量存储单元中,也就是使变量i的值增的值增1变为变为11*/上一页上一页下一页下一页返返 回回上一页上一页下一页下一页返返 回回【例【例8.1】
14、从键盘上输入任意从键盘上输入任意3个实数,利用指针的方法将这个实数,利用指针的方法将这3个实数按由大个实数按由大到小的顺序进行排序。到小的顺序进行排序。【程序代码】【程序代码】#include stdio.hmain()float a,b,c,*p1,*p2,*p3,*p;printf(请输入请输入3个实数:个实数:);scanf(%f%f%f,&a,&b,&c);p1=&a;p2=&b;p3=&c;if(ab) p=p1;p1=p2;p2=p;if(ac)p=p1;p1=p3;p3=p;if(bc)p=p2;p2=p3;p3=p;printf(%.2f,%.2f,%.2fn,*p1,*p2,
15、*p3);【例【例7.3】 编写程序,调用例编写程序,调用例7.1中的函数。中的函数。【程序代码】【程序代码】#include stdio.hint sum(int n) int i;int s=0;for( i=1; i=n; i+)s+=i;return s;main( ) int n,s;for(n=10;n40;n+=10) s=sum(n);printf(n=%d, s=%dn,n,s);上一页上一页下一页下一页返返 回回运行结果如下:运行结果如下: C语言中规定数组名是一个指针,该指针指向该数组的首元素,即数组名语言中规定数组名是一个指针,该指针指向该数组的首元素,即数组名中存放了
16、数组中第一个元素的地址值,即数组的首地址。也就是该数组所中存放了数组中第一个元素的地址值,即数组的首地址。也就是该数组所占一串连续存储单元的起始地址,因此,数组名是一个常量指针,在程序占一串连续存储单元的起始地址,因此,数组名是一个常量指针,在程序中,不可以给数组名重新赋值。中,不可以给数组名重新赋值。因此,引用数组元素可以用下标法,也可以用指针法,即通过指向数组元因此,引用数组元素可以用下标法,也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,运行速度快)。运行速度快)。上一页上一页
17、下一页下一页返返 回回8.2 指针与数组指针与数组8.2.1 指针和一维数组指针和一维数组1指针的移动指针的移动 当指针变量指向一串连续的存储单元(即数组)时,可以对指针变量加上当指针变量指向一串连续的存储单元(即数组)时,可以对指针变量加上或减去一个整数来进行指针的移动和定位。例如有如下语句:或减去一个整数来进行指针的移动和定位。例如有如下语句:int a5=1,2,3,4,5,*p,*q,k;p=&a0;则:则:p+2 p-3 p+ p- +p -p等,都是合法的。进行加法运算时,表示等,都是合法的。进行加法运算时,表示p向地址增大的方向移动;进向地址增大的方向移动;进行减法运算时,表示行
18、减法运算时,表示p向地址减小的方向移动。它们的含义分别是:向地址减小的方向移动。它们的含义分别是:p+2 /*指针后移,指向当前位置指针后移,指向当前位置p后的第后的第2个存储单元。个存储单元。*/p-3 /*指针前移,指向当前位置前的第指针前移,指向当前位置前的第3个存储单元。个存储单元。*/p+或或+p /*当前指针位置后移一个存储单元。当前指针位置后移一个存储单元。*/p-或或-p /*当前指针位置前移一个存储单元。当前指针位置前移一个存储单元。*/移动的具体长度取决于指针的数据类型,由系统自动确定。这里的数字移动的具体长度取决于指针的数据类型,由系统自动确定。这里的数字“1”不代表十进
19、制整数不代表十进制整数“1”,而是指,而是指1个存储单元长度;至于个存储单元长度;至于1个存储单个存储单元的长度占多少存储空间,则视指针的基类型而定。元的长度占多少存储空间,则视指针的基类型而定。 若有语句若有语句k=q-p;则则k中的值为两个指针间相隔元素的个数。若表达式中的值为两个指针间相隔元素的个数。若表达式pq的值为真,则说明的值为真,则说明p中存放的地址值小于中存放的地址值小于q中存放的地址值。中存放的地址值。 上一页上一页下一页下一页返返 回回上一页上一页下一页下一页返返 回回【例【例8.3】 把一个字符串中的字符逆序输出。把一个字符串中的字符逆序输出。【程序代码】【程序代码】#i
20、nclude stdio.hmain( ) char s = abcdefgh;char *p;for(p=s+7;p+1!=s;p-)printf(%c,*p);printf(n);运行结果如下:运行结果如下:程序中,定义了一个字符数组程序中,定义了一个字符数组s,又定义了一个,又定义了一个char型指针变量型指针变量p。通过指。通过指针加上整数运算,先使指针针加上整数运算,先使指针p指向已知字符串的最后一个字符指向已知字符串的最后一个字符h,再输出,再输出P所所指向的字符,每次循环指向的字符,每次循环p指针减指针减1。判断。判断p+1与与s是否相等,是否相等,s是数组名,即是是数组名,即是
21、指向数组首元素的指针。如果指向数组首元素的指针。如果p+1与与s相等。则说明相等。则说明p将指向该数组首元素前将指向该数组首元素前边的一个元素,该元素实际是不存在的,这时退出边的一个元素,该元素实际是不存在的,这时退出for循环,并完成对该字循环,并完成对该字符串的逆序输出。符串的逆序输出。2通过指针引用一维数组元素通过指针引用一维数组元素有如下定义:有如下定义:int a 10;int *pa;pa=&a0; 或或 pa=a;则根据指针变量的间接访问规则可知:则根据指针变量的间接访问规则可知:*paa0,*(pa+1)a1,*(pa+2)a2,*(pa+i)ai。又。又由于数组名由于数组名a
22、代表数组的首地址代表数组的首地址&a0,a+1代表代表&a1,a+2代表代表&a2,a+i代表代表&ai,则有,则有*aa0,*(a+1)a1,*(a+2)a2,*(a+i) ai。综上所述,若有:综上所述,若有:int a10, *pa, i;pa=a;则任意一个数组元素则任意一个数组元素ai可用以下方法来表示:可用以下方法来表示:下标法:下标法:ai 或或 pai指针法:指针法:*(pa+i) 或或 *(a+i)上一页上一页下一页下一页返返 回回【例【例8.4】 指针与一维数组的关系。指针与一维数组的关系。【程序代码】【程序代码】#include stdio.hmain( ) int a5
23、,*p,i;for(i=0;i5;i+) ai=i+1;printf(a%d=%d,i,ai);printf(n);for(i=0;i5;i+)printf(*(a+%d)=%d,i,*(a+i);printf(n);p=a;for(i=0;i5;i+)printf(*(p+%d)=%d,i,*(p+i);运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回【例【例8.5】 有一个数组有一个数组a,存放,存放10个学生的成绩,求平均成绩,要求通过指针个学生的成绩,求平均成绩,要求通过指针变量来访问数组元素。变量来访问数组元素。【编程思路】【编程思路】(1)定义一个数组)定义一个数组a
24、10,用来存放,用来存放10个学生的成绩。定义一个存放个学生的成绩。定义一个存放10个元个元素和值的变量素和值的变量sum使其初值置为使其初值置为0,定义表示平均值的变量,定义表示平均值的变量ave,定义指针变量定义指针变量p.。(2)使)使p指向数组的第一个元素。指向数组的第一个元素。(3)以)以p为循环控制变量,终值为为循环控制变量,终值为a+9,通过间接访问运算求和,从而得到,通过间接访问运算求和,从而得到均值。均值。【程序代码】【程序代码】#include stdio.hmain()float a10,*p,sum=0,ave;printf(请输入请输入10个学生成绩:个学生成绩:);
25、for(p=a;pa+10;p+)scanf(%f,p);for(p=a;pa+10;p+)sum+=*p;ave=sum/10;printf(平均成绩平均成绩=%.2fn,ave);上一页上一页下一页下一页返返 回回运行结果为:运行结果为:上一页上一页下一页下一页返返 回回8.2.2 指针和二维数组指针和二维数组若有以下定义若有以下定义 :int *p,a34,i,j;1二维数组元素的地址二维数组元素的地址(1)二维数组)二维数组a由若干个一维数组组成由若干个一维数组组成在在C语言中定义的二维数组实际上是一个一维数组,这个一维数组的每语言中定义的二维数组实际上是一个一维数组,这个一维数组的每
26、个成员又是一个一维数组。如以上定义的个成员又是一个一维数组。如以上定义的a数组,可认为由数组,可认为由a0、 a1、 a2等三个元素组成,而等三个元素组成,而a0、a1、 a2等每个元素又分别是由等每个元素又分别是由4个整个整型元素组成的一维数组。可用型元素组成的一维数组。可用a00、 a01等来引用等来引用a0中的每个元中的每个元素,其它依次类推。素,其它依次类推。 前面已讲过,前面已讲过,C语言中,数组名是一个地址常量,其值为数组中第一个元语言中,数组名是一个地址常量,其值为数组中第一个元素的地址,在以上的二维数组中,素的地址,在以上的二维数组中,a0、 a1、 a2都是一维数组名,同样也
27、都是一维数组名,同样也代表一个不可改变的地址常量,其值依次为二维数组每行第一个元素的地址,代表一个不可改变的地址常量,其值依次为二维数组每行第一个元素的地址,其基类型就是数组元素的类型。因此,对于二维数组,象其基类型就是数组元素的类型。因此,对于二维数组,象a0+这样的表达式这样的表达式是非法的。是非法的。在以上定义中,指针变量在以上定义中,指针变量p的基类型与的基类型与ai(0i3)相同,因此,赋值语句)相同,因此,赋值语句p=ai;是合法的,我们已知;是合法的,我们已知ai也可以写成:也可以写成:*(a+i),故以上赋值语句也可写,故以上赋值语句也可写成成p=*(a+i);(2)二维数组元
28、素的地址)二维数组元素的地址二维数组元素的地址可以由表达式二维数组元素的地址可以由表达式&aij求得;也可以通过每行的首地址表示。求得;也可以通过每行的首地址表示。以上二维数组以上二维数组a 中,每个元素的地址可以通过每行的首地址:中,每个元素的地址可以通过每行的首地址:a0、 a1、 a2等来表示。如地址等来表示。如地址&a00可以用可以用a0+0来表示,地址来表示,地址&a01可以用可以用a0+1表表示;若示;若0i3、0j4,则,则aij的地址可用以下表达式求得:的地址可用以下表达式求得: &aij ai+j *(a+i)+j上一页上一页下一页下一页返返 回回2通过地址引用二维数组元素通
29、过地址引用二维数组元素若有以下定义:若有以下定义:int a34,i,j;且当且当0i3、0j4,则,则a数组元素可用以下三种表达式来引用:数组元素可用以下三种表达式来引用: aij *(ai+j) *(*(a+i)+j)说明:说明:在在中,表达式中,表达式*(ai+j)中因为中因为ai的基类型为的基类型为int ,j的位移量为的位移量为4*j字节。字节。在在中,表达式中,表达式*(*(a+i)+j)中,)中,a 的基类型为的基类型为4个元素的一维数组,个元素的一维数组,i的位的位移量为移量为4*4*i字节;而字节;而*(a+i)的基类型为)的基类型为int,j的位移量为的位移量为4*j字节。
30、字节。上一页上一页下一页下一页返返 回回上一页上一页下一页下一页返返 回回【例【例8.6】 分析下列程序的输出结果。分析下列程序的输出结果。【程序代码】【程序代码】#include stdio.hmain( ) int b34=1,2,3,4,5,6,7,8,9,10,11,12;int i,j;for(i=0;i3;i+) for(j=0;j4;j+)printf(%5d,*(*(b+i)+j);printf(n);运行结果如下:运行结果如下: 3通过建立一个指针数组来引用二维数组元素通过建立一个指针数组来引用二维数组元素当当个数组的元素类型为某种指针类型时,该数组就称为指针数组。指针数个数
31、组的元素类型为某种指针类型时,该数组就称为指针数组。指针数组的定义形式为:组的定义形式为:类型名类型名 *数组名数组名常量表达式常量表达式 ;例如例如 int *p3,a32,i,j;定义指针数组定义指针数组p,它有,它有3个元素,每个元素都是指向个元素,每个元素都是指向int型变量的指针变量。和型变量的指针变量。和一般的数组定义一样,数组名一般的数组定义一样,数组名p是第一个元素即是第一个元素即p0的地址,也是地址常量。的地址,也是地址常量。若满足条件若满足条件0i3,则,则pi和和ai的基类型相同,的基类型相同,pi=ai是合法的赋值表达式。是合法的赋值表达式。若有以下循环:若有以下循环:
32、for(i=0;i3;i+) pi=ai;在这里,赋值号右边的在这里,赋值号右边的ai是常量,表示是常量,表示a数组每行的首地址,赋值号左边的数组每行的首地址,赋值号左边的pi是指针变量,循环执行的结果使是指针变量,循环执行的结果使p0、p1、p2分别指向分别指向a数组每行的开数组每行的开头。这时,数组头。这时,数组aij的引用形式的引用形式*(ai+j)和和*(pi+j)是完全等价的。由此可见,是完全等价的。由此可见,这时可以通过指针数组这时可以通过指针数组p来引用来引用a数组的元素,它们的等价形式如下:数组的元素,它们的等价形式如下: pij /* 与与aij对应对应 */ *(pi+j)
33、 /* 与与*(ai+j)对应对应 */*(*(p+i)+j) /* 与与*(*(a+i)+j)对应对应 */不同的是:不同的是:pi中的值是可变的,而中的值是可变的,而ai 的值是不可变的。的值是不可变的。 上一页上一页下一页下一页返返 回回上一页上一页下一页下一页返返 回回【例【例8.7】 分析下列程序的输出结果。分析下列程序的输出结果。【程序代码】【程序代码】#include stdio.hmain( ) int a23=1,2,3,4,5,6;int i, *p2;for(i=0;i2;i+)pi=ai;printf(%d, %dn,p11,*(p1+2);运行结果如下:运行结果如下:
34、该程序中该程序中p是一个一维指针数组名,它有是一个一维指针数组名,它有2个元素,每个元素是指向个元素,每个元素是指向int型的型的指针。通过指针。通过for循环给循环给p数组赋值,分别使数组赋值,分别使p0赋值为赋值为a0,pl赋值为赋值为a1。上一页上一页下一页下一页返返 回回 4通过建立一个行指针来引用二维数组元素通过建立一个行指针来引用二维数组元素 在在C语言中,对于某类型的二维数组语言中,对于某类型的二维数组amn的数组名的数组名a,在表达式中出,在表达式中出现时,被转换成指向现时,被转换成指向n个元素组成的一维数组的指针,该一维数组是由个元素组成的一维数组的指针,该一维数组是由a00
35、,a01, ,a0n-1 n个元素组成的,这时个元素组成的,这时a+1就指向另一个一维数组就指向另一个一维数组a10,a11, ,a1n-1的指针;的指针;a+2就是指向下一个一维数组就是指向下一个一维数组a20,a21, ,a2n-1的指针;的指针;a+(m-1)就是指向数组就是指向数组am-10,am-11, ,am-1n-1的指针。由此可见,的指针。由此可见,a 的指向是以一个由若干元素组的指向是以一个由若干元素组成的一维数组(或二维数组的一行)为单位的,成的一维数组(或二维数组的一行)为单位的,a 不可能指向某一行的某个不可能指向某一行的某个字符,这种指向二维数组每一行的指针称行指针,
36、其定义的一般形式为:字符,这种指向二维数组每一行的指针称行指针,其定义的一般形式为:类型名(类型名(*指针变量名)常量表达式,指针变量名)常量表达式,;其中其中“常量表达式常量表达式”为二维数组中每一行中元素的个数,即与列下标同值。为二维数组中每一行中元素的个数,即与列下标同值。 若有以下定义:若有以下定义:int a32, (*prt)2;上一页上一页下一页下一页返返 回回其中在其中在(*prt)2说明符中,由于一对圆括号的存在,使说明符中,由于一对圆括号的存在,使“*”首先与指针变量首先与指针变量名名prt结合,说明结合,说明prt是一个指针变量,然后再与说明符是一个指针变量,然后再与说明
37、符2结合,说明指针变结合,说明指针变量量prt的基类型是一个包含两个的基类型是一个包含两个int 元素的数组。在这里,元素的数组。在这里,prt的基类型与数的基类型与数组名组名a的基类型相同,因此的基类型相同,因此prt=a;是合法的赋值语句。所以;是合法的赋值语句。所以prt+1a+1a1。当。当prt指向指向a数组开头时,即数组开头时,即prt=a;时,则可用以下;时,则可用以下形式来引用二维数组元素形式来引用二维数组元素aij: prtij /* 与与aij对应对应 */ *(prti+j) /* 与与*(ai+j)对应对应 */*(*(prt+i)+j) /* 与与*(*(a+i)+j
38、)对应对应 */在这里,在这里,prt是指针变量,它的值可变,而是指针变量,它的值可变,而a是一个地址常量。是一个地址常量。【例【例8.8】 分析下列程序的输出结果。分析下列程序的输出结果。【程序代码】【程序代码】#include stdio.hmain( ) int a35=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;int (*p)5;p=a+1;printf(%d,%dn,p00,*(*(p+1)+1);上一页上一页下一页下一页返返 回回运行结果如下:运行结果如下:字符数组通常用来存放字符串,指针指向字符数组也就指向了字符串,因此字符数组通常用来存放字符串,指
39、针指向字符数组也就指向了字符串,因此通过指针可以引用它所指向的字符串,本节主要讲指向字符串指针的定义、通过指针可以引用它所指向的字符串,本节主要讲指向字符串指针的定义、赋值及引用方法。赋值及引用方法。1指向字符串指针变量的定义及赋值指向字符串指针变量的定义及赋值定义的一般形式如下:定义的一般形式如下:char *指针变量;指针变量;如:如:char *ps1,*ps2=abcd;以上的定义中以上的定义中ps2在定义的同时直接赋了初值在定义的同时直接赋了初值abcd,既将存放字符串的存,既将存放字符串的存储单元起始地址直接赋给了指针变量储单元起始地址直接赋给了指针变量ps2。如果已经定义了一个字
40、符型指针变量,也可以通过赋值运算将某个字符串的如果已经定义了一个字符型指针变量,也可以通过赋值运算将某个字符串的起始地址赋给它,从而使指针指向一个字符串,如:起始地址赋给它,从而使指针指向一个字符串,如:char *ps1; ps1=abcd;8.3 指针与字符串指针与字符串上一页上一页下一页下一页返返 回回 2字符指针的引用字符指针的引用当一个字符型指针变量指向了某个字符串后,就可以利用指针变量来处理这当一个字符型指针变量指向了某个字符串后,就可以利用指针变量来处理这个字符串。主要有两种处理方式:个字符串。主要有两种处理方式:(1)整体处理字符串)整体处理字符串输出字符串输出字符串 prin
41、tf(%s,指针变量指针变量);或;或puts(指针变量指针变量);输入字符串输入字符串 scanf(%s,指针变量,指针变量);或;或gets(指针变量指针变量);(2)单个处理字符串中的字符)单个处理字符串中的字符引用第引用第i个字符的格式为:个字符的格式为:*(指针变量指针变量+i);【例【例8.9】 利用指针实现两个字符串的连接利用指针实现两个字符串的连接【编程思路】【编程思路】(1)指针)指针p指向第一个串末尾(最后一个字符的后面),指针指向第一个串末尾(最后一个字符的后面),指针q指向第二个指向第二个串的首部。串的首部。(2)将第二个串中字符依次放入第一个串后。)将第二个串中字符依
42、次放入第一个串后。上一页上一页下一页下一页返返 回回【程序代码】【程序代码】#include stdio.h#include string.hmain()char str120,str210,*p,*q;printf(请输入两个字符串:请输入两个字符串:);gets(str1);gets(str2);p=str1+strlen(str1); /*p指向第一个串的末尾指向第一个串的末尾*/q=str2;while(*q!=0)*p=*q;p+;q+;*p=0;printf(连接后的串为:连接后的串为:);puts(str1);运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回【例【例
43、8.10】 通过指针以单个字符的形式输出一个字符串通过指针以单个字符的形式输出一个字符串【程序代码】【程序代码】#include stdio.h#include string.hmain( )char c10,*p;int i,len;printf(输入字符串为:输入字符串为:n);gets(c);p=c;len=strlen(c);printf(输出字符串为:输出字符串为:n);for(i=0;ilen;i+) printf(%c,*(p+i);printf(n);运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回使用指针作函数的形参时,实参要求使用地址值,从而实现变量地址的传使
44、用指针作函数的形参时,实参要求使用地址值,从而实现变量地址的传递,这种调用称为传址调用。它的特点是可以在被调用函数中通过改变形递,这种调用称为传址调用。它的特点是可以在被调用函数中通过改变形参所指的变量的值,来改变调用函数中实参所指的变量的值。参所指的变量的值,来改变调用函数中实参所指的变量的值。1变量地址作函数参数变量地址作函数参数当实参为一个变量的地址(或已经指向某变量的指针变量)时,形参必须当实参为一个变量的地址(或已经指向某变量的指针变量)时,形参必须是一个基类型与它相同的指针变量。是一个基类型与它相同的指针变量。8.4 指针与函数指针与函数8.4.1 指针作函数的参数指针作函数的参数
45、上一页上一页下一页下一页返返 回回【例【例8.11】 分析下列程序输出结果分析下列程序输出结果【程序代码】【程序代码】#include stdio.hmain( ) int a,b,c;a=b=c=5;fun(a,&b,&c);printf(%d,%d,%dn,a,b,c);fun(int x,int *y,int *z) x*=2;*y+=x;*z=x+*y;printf(%d,%d,%dn,x,*y,*z);运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回2数组名作函数参数数组名作函数参数(1)一维数组名作实参)一维数组名作实参【例【例8.12】 编写程序,调用函数输出数组中
46、的数据。编写程序,调用函数输出数组中的数据。【程序代码】【程序代码】#include stdio.h#define M 10void arrout(int *);main( ) int sM=1,2,3,4,5,6,7,8,9,10;arrout(s);void arrout(int *a) int i;for(i=0;iM;i+)printf(i+1)%5= =0)? %4dn: %4d,*(a+i); /* 根据根据i的值来确定使用不同的格式串的值来确定使用不同的格式串 */printf(n);运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回当数组名作实参时,对应的形参除了应
47、该是指针外,形参还可以用另外两种当数组名作实参时,对应的形参除了应该是指针外,形参还可以用另外两种形式。对于上例中的函数调用形式。对于上例中的函数调用arrout(s),对应的函数首部可以写成以下三种,对应的函数首部可以写成以下三种形式:形式: arrout(int *a) arrout(int a ) arrout(int aM)在第在第和第和第种形式中,虽然说明形式与数组说明相同,但种形式中,虽然说明形式与数组说明相同,但C编译程序都把编译程序都把a处理成第一种指针形式。处理成第一种指针形式。(2)二维数组名作实参)二维数组名作实参当二维数组名作实参时,对应的形参必须是一个行指针变量,例如
48、,主函数当二维数组名作实参时,对应的形参必须是一个行指针变量,例如,主函数中以下的定义和调用语句:中以下的定义和调用语句:#define M 5#define N 3main( ) double sMN;fun(s);上一页上一页下一页下一页返返 回回则则fun函数的首部可以是以下三种形式之一:函数的首部可以是以下三种形式之一: fun(double (*a)N) fun(double a N) fun(double aMN)注意:列下标不可缺。无论是哪种方式,系统都把注意:列下标不可缺。无论是哪种方式,系统都把a处理成一个行指针。和一处理成一个行指针。和一维数组相同,数组名传送给函数的是一个
49、地址值,因此,对应的形参也必定维数组相同,数组名传送给函数的是一个地址值,因此,对应的形参也必定是一个基类型相同的指针变量,在函数中引用的将是主函数中的数组元素,是一个基类型相同的指针变量,在函数中引用的将是主函数中的数组元素,系统只为形参开辟一个存放地址的存储单元,而不可能在调用函数时为形参系统只为形参开辟一个存放地址的存储单元,而不可能在调用函数时为形参开辟一系列存放数组的存储单元。开辟一系列存放数组的存储单元。 上一页上一页下一页下一页返返 回回【例【例8.13】 假定有假定有5个学生,每个学生有个学生,每个学生有3门课的成绩,编程输出某个学生的门课的成绩,编程输出某个学生的成绩。成绩。
50、【程序代码】【程序代码】#include stdio.hsearch(float (*p)3, int a) int i;printf(the scores of student NO. %d are:n,a);for(i=0;i3;i+)printf(%5.1f,pa-1i);printf(n);main( ) float score53=86,90,78,86,84,96,9l,76,88,90,77,66,78,97,68;int n;printf(input student No.(1-5):);scanf(%d,&n);search(score,n);运行结果如下:运行结果如下:上一
51、页上一页下一页下一页返返 回回 在在C语言中,函数可以返回整型值、字符值、实型值等,也可以返回某种语言中,函数可以返回整型值、字符值、实型值等,也可以返回某种基类型的指针值,即地址。返回指针值的函数与以前介绍的函数在概念上是基类型的指针值,即地址。返回指针值的函数与以前介绍的函数在概念上是完全一致的,只是对这类函数的调用,其返回值的类型是某种指针类型而已。完全一致的,只是对这类函数的调用,其返回值的类型是某种指针类型而已。定义返回指针值的函数,一般形式为:定义返回指针值的函数,一般形式为: 类型名类型名 *函数名函数名(形参表形参表) 例如:例如: int *f(int x, int y) 函
52、数函数f( )返回指向返回指向int型数据的指针,其中型数据的指针,其中x,y是函数是函数f( )的形参,注意在的形参,注意在函数名的两侧分别为函数名的两侧分别为“*”运算符和运算符和“( )”运算符,由于运算符,由于“( )”的优先级高于的优先级高于“*”,因此,函数名先与因此,函数名先与“( )”结合,在函数名之前的结合,在函数名之前的*,表示函数返回值为指针类型。,表示函数返回值为指针类型。8.4.2 返回指针值的函数返回指针值的函数上一页上一页下一页下一页返返 回回【例【例8.14】对例】对例8.13 用返回指针值的函数来实现。用返回指针值的函数来实现。 【程序代码】【程序代码】#in
53、clude stdio.hmain( ) float score53=86,90,78,86,84,96,9l,76,88,90,77,66,78,97,68;float *search( float (*)3,int);float *p; int i,n;printf(input student No.(1-5):);scanf(%d,&n);printf(The scores of student NO. %d are:n,n);p=search(score,n);for(i=0;i3;i+)printf(%5.2ft,*(p+i);printf(n);float *search(floa
54、t (* pointer)3,int n) float *pt;pt=*(pointer+n-1); return(pt);上一页上一页下一页下一页返返 回回运行结果如下:运行结果如下:注意:学生序号是从注意:学生序号是从0号算起。函数号算起。函数search被定义为指针型函数,它的形被定义为指针型函数,它的形参参pointer是指向包含是指向包含3个元素的一维数组的指针变量。个元素的一维数组的指针变量。pointer+1指向指向score数组第一行。数组第一行。*(pointer+1)指向第)指向第1行第行第0列元素。列元素。pt是指针变量,是指针变量,它指向实型变量(而不是指向一维数组)。
55、它指向实型变量(而不是指向一维数组)。main函数调用函数调用search函数,函数,将将score数组的首地址传给数组的首地址传给pointer(注意注意score也是指向行的指针,而不是也是指向行的指针,而不是指向列元素的指针指向列元素的指针)。n是要查找的学生序号。调用是要查找的学生序号。调用search函数后,得到函数后,得到一个地址(指向第一个地址(指向第n个学生第个学生第0门课程),赋给门课程),赋给p。然后将此学生的。然后将此学生的3门课门课的成绩打印出来。的成绩打印出来。*(p+i)表示此学生第)表示此学生第i门课的成绩。门课的成绩。上一页上一页下一页下一页返返 回回1指向函数
56、的指针变量的定义指向函数的指针变量的定义在在C语言中,指针变量除能指向数据对象外,也可指向函数。一个函数在语言中,指针变量除能指向数据对象外,也可指向函数。一个函数在编译时,被分配给一个入口地址这个入口地址就称为函数的指针。函数编译时,被分配给一个入口地址这个入口地址就称为函数的指针。函数的指针能赋给一个指向函数的指针变量,并能通过指向函数的指针变量调的指针能赋给一个指向函数的指针变量,并能通过指向函数的指针变量调用它所指向的函数。用它所指向的函数。定义指向函数的指针变量的一般形式为:定义指向函数的指针变量的一般形式为:类型名类型名 (*指针变量名指针变量名)(形参形参1类型,形参类型,形参2
57、类型类型 );例如:例如:int (*p)( );定义了一个指向函数的指针变量定义了一个指向函数的指针变量p,它用于指向一个函数返回值为,它用于指向一个函数返回值为int型的型的函数,注意,函数,注意,*p两侧的括号是必需的,表示两侧的括号是必需的,表示p先与先与*结合,是结合,是个指针变量。个指针变量。然后与随后的然后与随后的( )结合,表示指针变量结合,表示指针变量p所指向的对象是函数。如果写成所指向的对象是函数。如果写成“int *p( )”,因,因“( )”优先级高于优先级高于“*”,就变成是说明一个函数,就变成是说明一个函数p( ),该函,该函数的返回值是指向数的返回值是指向int型
58、的指针。型的指针。8.4.3 指向函数的指针变量指向函数的指针变量上一页上一页下一页下一页返返 回回2用指向函数的指针变量调用函数用指向函数的指针变量调用函数 C语言约定,函数名本身就是函数入口地址,这如同数组名是数组存语言约定,函数名本身就是函数入口地址,这如同数组名是数组存储区域的起始地址一样。储区域的起始地址一样。 指向函数的指针变量并不固定指向某个函数,在程序中把哪个函数指向函数的指针变量并不固定指向某个函数,在程序中把哪个函数的入口地址赋给它,它就指向哪个函数。并可根据需要向它赋不同函数入的入口地址赋给它,它就指向哪个函数。并可根据需要向它赋不同函数入口的地址,使它指向不同的函数。口
59、的地址,使它指向不同的函数。 通过指向函数的指针变量调用其所指的函数,一般形式为:通过指向函数的指针变量调用其所指的函数,一般形式为: (*指针变量名指针变量名)(实参表实参表)或或 指针变量名指针变量名(实参表实参表)上一页上一页下一页下一页返返 回回【例【例8.15】 求求x,y中的较小数中的较小数【程序代码】【程序代码】#include stdio.hmain( ) int min(int ,int); int (*p)(int ,int),x,y,z;printf(Enter x,y:);scanf(%d%d,&x,&y);p=min;z=(*p)(x,y);printf(Min(%d
60、,%d)=%dn,x,y,z);int min(int a,int b) return ab?a:b; 运行结果如下:运行结果如下:上一页上一页下一页下一页返返 回回例例8.16编写程序,利用指针实现把一个数组中的偶数存入另一个数组中并编写程序,利用指针实现把一个数组中的偶数存入另一个数组中并输入此新数组中的内容。输入此新数组中的内容。【编程思路】【编程思路】(1)用两个指针)用两个指针p1和和p2分别指向数组分别指向数组x和和y。(2)利用指针移动逐个取出数组)利用指针移动逐个取出数组x中的每个元素,如果当前元素的值为中的每个元素,如果当前元素的值为偶数,则存入数组偶数,则存入数组y中。中。
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年度生态治理与恢复施工合同
- 二零二四年度企业知识产权管理与保护服务合同
- 二零二四年工业园区标识牌制作合同
- 电供暖合同范本
- 二零二四年度工程咨询与监理合同
- 二零二四年影视制作承包合同
- 二零二四年度山塘文化产业发展承包合同
- 公司结对帮扶贫困村扶贫合同协议书范本
- 2024年度印刷设计与制作合同
- 2024年度装修工程质量合同:某健身房设备安装工程
- 人教鄂教版科学六年级下册全册教案
- 建筑施工危险作业管理制度.doc
- 《客舱服务与的管理》课程标准.doc
- 材料成型概论 第四章 挤压成型
- 脚手架(门式)安全技术规范JGJ128-2010
- 三峡教学案例
- 【公开课课件】第三单元就业与创业复习课件-2021-2022学年高中政治统编版选择性必修2法律与生活
- 一年级上册科学课件-第12课 轮子的故事_苏教版 (共17张PPT)
- 四宫格-儿童数独-练习60题 可打印
- APQP项目开发进度表
- 新能源小客车购车充电条件确认书
评论
0/150
提交评论