第10章指针的进一步讨论.docx
- 文档编号:11164150
- 上传时间:2023-02-25
- 格式:DOCX
- 页数:35
- 大小:99.46KB
第10章指针的进一步讨论.docx
《第10章指针的进一步讨论.docx》由会员分享,可在线阅读,更多相关《第10章指针的进一步讨论.docx(35页珍藏版)》请在冰豆网上搜索。
第10章指针的进一步讨论
第10章指针的进一步讨论
在第8章中已经对指针进行了讨论,介绍了指针和指针变量的概念,指针变量的定义、初始化和运算,一维数组和二维数组的指针,字符指针与字符串,以及指针作为函数参数等。
在第9章中又介绍了结构体类型数据的指针及其用法。
在C语言中,指针的用途非常广泛且用法非常灵活,本章将对指针进行进一步的讨论,主要介绍指针数组和指向指针的指针、函数的指针和指向函数的指针变量、返回指针值的函数等概念及其应用,以及用指针处理线性链表。
10.1指针数组与多级指针
10.1.1指针数组的概念及其应用
1.指针数组的概念
指针数组是指每一个数组元素均用来存储一个指针值的数组,即指针数组中的每一个元素都是指针变量。
指针数组的定义形式为:
类型标识符*数组名[数组长度];
例如,语句:
int*p[5];
定义了一个指针数组,数组名为p,共有5个元素,每个元素都是一个可以指向int型存储单元(或变量)的指针变量。
注意它与第8章已介绍的指向一维数组的指针变量的定义形式上的区别。
这里,运算符[]的优先级高于*,因此,p先与[5]结合,形成数组p[5],然后p[5]再与*结合,*表示此数组是指针数组。
指针数组的每一个元素(均为指针变量)的共同数据类型是“int*”。
对于如下语句:
inta[4][5];
定义的二维数组,我们知道,a[i](0≤i<4)均是指针,它指向二维数组元素a[i][0]。
因此我们可以用a[0]、a[1]、…、a[3]来初始化指针数组p,语句如下:
int*p[4]={a[0],a[1],a[2],a[3]};
这样,指针数组p的第i个元素p[i](它与*(p+i)等价)就指向二维数组a的第i行的第0个元素a[i][0],p[i]+j则指向a[i][j],因此,a[i][j]与*(p[i]+j)、*(*(p+i)+j)等价。
如图10-1所示。
注意:
p[i]并不是指向二维数组a的第i行,而是指向第i行的第0个元素a[i][0]。
也就是说,p[i]仅是一个元素指针,而指向二维数组第i行的是一个行指针。
2.指针数组的应用
指针数组特别适合于用来处理指向若干个字符串的问题,它将使字符串的处理更加方便灵活。
例如,资料室有若干本书,每本书都有一个书名(可看成是一个字符串),我们可以采用二维字符数组来处理该资料室的图书资料管理。
定义二维字符数组并初始化如下:
图10-1指针数组的定义与初始化
charc[4][40]={TheCProgrammingLanguage,DatabaseDesign,
DatabaseSystemImplementation,SoftwareEngineering};
其存储形式如图10-2所示。
利用二维数组来处理的缺点是每一本书的书名所占字节数(即二维数组的列数)相等。
但实际上每一本书的书名(字符串)的长度是不相等的,二维数组的列数必须按最大字符串的长度来准备,造成许多内存单元的浪费。
不仅浪费内存单元,而且在诸如字符串的排序等问题的处理上也较慢,因为它要将整个字符串的内容进行交换。
上述问题如果采用指针数组来处理,许多麻烦可以迎刃而解。
首先我们可以定义一个字符指针数组book,然后让指针数组的每一个元素(均为指针变量)分别指向一个字符串,如图10-3所示。
在第8章中我们已经介绍了通过指向字符的指针变量来处理字符串的问题。
图10-3利用指针数组处理字符串
图10-2利用二维数组处理字符串
例10.1对所有图书按字母顺序(由小到大)排序输出书名。
#include
#include
voidmain(){
voidsort(char*p[],int),prn(char*p[],int);//声明被调用函数的原型
char*book[]={TheCProgrammingLanguage,DatabaseDesign,
DatabaseSystemImplementation,SoftwareEngineering};
sort(book,4);//实参book是指针数组名
prn(book,4);
}
voidsort(char*p[],intn){//按升序对指针数组p所指向的字符串序列排序
inti,j,minpost;
char*t;
for(i=0;i minpost=i; for(j=i+1;j if(strcmp(p[j],p[minpost])<0)minpost=j; if(minpost! =i){//交换指针数组元素的指向 t=p[i];p[i]=p[minpost];p[minpost]=t; } } } voidprn(char*p[],intn){ inti; for(i=0;i printf(%s\n,p[i]); } 运行结果为: DatabaseDesign DatabaseSystemImplementation SoftwareEngineering TheCProgrammingLanguage 其中,排序函数sort实现字符串序列的升序排序,排序过程中,并没有真正移动字符串的存储,仅仅是通过交换指向字符串的指针来实现排序,最后,按指针数组book中数组元素的顺序得到的字符串(即指针数组元素所指向的字符串)序列就是升序排序的。 10.1.2指针数组作main函数的形参 在以前的程序中,主函数main()都是不带参数的。 这是一般用户程序的特点。 但在有些情况下(例如用C语言编写系统命令),当程序开始执行时,希望通过命令行将某些参数传递给程序,以控制程序的执行,这时main函数就需要带参数,以便来接受命令行的参数。 命令行的一般形式为: 命令名参数1参数2…参数n 带参数的main函数的一般格式如下: voidmain(intargc,char*argv[]){ … } 按照约定,main函数可以带有两个名为argc和argv的参数。 其中,argc是int型参数,它接受的值是命令行参数的数目;argv是指向char型的指针数组参数,它的每个数组元素接受的值分别是指向命令行各参数字符串的指针。 例如,有如下C程序: #include voidmain(intargc,char*argv[]){ inti; for(i=1;i printf(%s,argv[i]); printf(\n); } 假设该C程序的可执行文件名为CProgram.exe,如果命令行为: C>CProgramPleaseinputnumber: 则形参argc的值为4,形参数组argv的值如图10-4所示。 程序运行结果为: Pleaseinputnumber: 图10-4main函数的形参指针数组的指向 10.1.3行指针数组 前面已经讨论了指针数组的概念。 在第8章中我们还讨论过指向一维数组的指针、指针变量,并分别简称为行指针、行指针变量。 因此,行指针数组是指每一个数组元素都是存储行指针(即指向一维数组的指针)的数组,即指针数组中的每一个元素都是行指针变量。 行指针数组的定义形式为: 类型标识符(*数组名[指针数组长度])[指向的数组长度]; 例如,语句: int(*p[4])[6]; 定义了一个行指针数组,数组名为p,共有4个元素,每个元素都是一个可以指向包含6个元素的int型一维数组的指针变量,即行指针变量。 对于如下语句: inta[4][6]; 定义的二维数组,我们知道,a+i(0≤i<4)是行指针,它指向二维数组的第i行(包含6个元素),即指向一个包含6个元素的一维数组。 因此我们可以用a,a+1,…,a+3来初始化行指针数组p,语句如下: int(*p[4])[6]={a,a+1,a+2,a+3}; 这样,行指针数组p的第i个元素p[i]就指向二维数组a的第i行,即行指针数组元素p[i]中存储的是一个行指针。 根据行指针的特点可知,*p[i]是指向二维数组a的第i行第0列的元素指针,它指向a[i][0]元素,因此*p[i]+j则指向a[i][j],即a[i][j]与*(*p[i]+j)等价。 例10.2某班有4个学生,学5门课程,每个学生5门课程的成绩及总成绩已给出,要求将他们按总成绩由高到低排序后输出。 定义二维数组score[4][7]来存放4个学生的成绩信息,第0列用来存放学生编号,第1至第5列分别用来存放5门课程的成绩,第6列用来存放总成绩。 定义行指针数组p[4],并将p[i]赋值为score+i,即让p[i]指向二维数组score的第i行。 如图10-5所示。 图10-5行指针数组的定义与赋值 程序如下: #include voidmain(){ intscore[4][7]={{1,80,82,95,88,93,438},{2,86,54,80,95,57,372}, {3,80,70,56,88,93,387},{4,95,89,87,80,96,447}};inti,j,(*p[4])[7]; voidsort(int(*p[])[7],int);//声明被调用函数的原型 for(i=0;i<4;i++) p[i]=score+i;//令行指针数组元素p[i]指向二维数组score的第i行 sort(p,4);//实参p是行指针数组名 printf(No.\tscore1\tscore2\tscore3\tscore4\tscore5\ttotal\n); for(i=0;i<4;i++){ for(j=0;j<7;j++) printf(%3d\t,*(*p[i]+j)); printf(\n); } } voidsort(int(*p[])[7],intn){//对行指针数组p所指向的学生成绩表排序 inti,j,post; int(*t)[7]; for(i=0;i post=i; for(j=i+1;j if(*(*p[j]+6)>*(*p[post]+6))//比较两个学生总成绩的大小 post=j; if(post! =i){//交换行指针数组元素的指向 t=p[i];p[i]=p[post];p[post]=t;} } } 运行结果如下: No.score1score2score3score4score5total 49589878096447 18082958893438 38070568893387 28654809557372 其中,排序函数sort实现对学生成绩表按总成绩的降序排序,排序过程中,并没有真正移动学生成绩表的存储,仅仅是通过交换指向学生成绩的行指针来实现排序,最后,按行指针数组p中数组元素的顺序得到的学生成绩(即行指针数组元素所指向的学生成绩行)表就是按总成绩降序排序的。 10.1.4多级指针 1.指向指针的指针与指向行指针的指针 例10.1中定义了指针数组book,它的每一个元素book[i]是一个指向字符的指针变量;数组名book也是一个指针,它是指向book[0]的指针。 因此book是一个指向指针的指针。 假设有一个指向int型变量i的指针变量p,p的值是指针,即变量i的指针&i;指针变量p本身又有指针(即&p),它是指向指针的指针;我们再定义一个指针变量b来存放指向指针的指针&p,这样指针变量b就指向了指针变量p,称指针变量b为指向指针的指针变量。 如图10-6所示。 图10-6指向指针的指针、指针变量 指向指针的指针变量的定义形式如下: 类型标识符**变量名; 例如,语句: int**b; 定义了一个指向指针的指针变量b。 b的类型为int**,*b的类型为int*,**b的类型为int。 指向行指针的指针变量的定义形式如下: 类型标识符(**变量名)[指向的数组长度]; 例如,语句: int(**b)[4]; 定义了一个指向行指针的指针变量b。 它的值*b是一个行指针,即*b指向一个包含4个int型元素的一维数组;**b是一个指向int型存储单元的指针,即**b指向*b所指向一维数组的第0个元素;***b是一个int型数据,即***b就是*b所指向一维数组的第0个元素。 例10.3指向指针的指针变量和指向行指针的指针变量。 #include voidmain(){ inta[3][4]={{11,12,13,14},{21,22,23,24},{31,32,33,34}}; int(*p[3])[4]={a,a+1,a+2};//定义行指针数组p char*q[]={WANCX,SHUW,LUOSW,LIUXP};//定义指针数组q voidprn1(char**,int),prn2(int(**)[4],int,int);//声明被调函数的原型 prn1(q,4);//实参q是指向指针的指针 prn2(p,3,4);//实参p是指向行指针的指针 } voidprn1(char**b,intk){//形参b是指向指针的指针变量 inti; for(i=0;i printf(%c,**b++);//输出*b所指向字符串的第一个字符 printf(\n); } voidprn2(int(**b)[4],intm,intn){//形参b是指向行指针的指针变量 inti,j; for(i=0;i for(j=0;j printf(%d,*(**b+j)); printf(\n); } } 执行结果如下: WSLL 11121314 21222324 31323334 2.指向指针的指针数组与指向行指针的指针数组 如果数组中的每一个元素都是用来存储一个指向指针的指针,则该数组称为指向指针的指针数组,即每一个数组元素都是指向指针的指针变量。 指向指针的指针数组的定义形式为: 类型标识符**数组名[数组长度]; 例如,语句: int**b[5]; 定义了一个指向指针的指针数组,数组名为b,共有5个元素,每个元素b[i](即*(b+i))都是一个指向指针的指针变量,它的值*b[i](即*(*(b+i)))是一个指向int型存储单元的指针,因此,**b[i](即**(*(b+i)))是一个int型数据。 指向行指针的指针数组的定义形式如下: 类型标识符(**变量名[数组长度])[指向的数组长度]; 例如,语句: int(**b[5])[4]; 定义了一个指向行指针的指针数组,数组名为b,共有5个元素,每个元素b[i](即*(b+i))都是一个指向行指针的指针变量,它的值*b[i](即*(*(b+i)))是一个指向包含4个元素的一维数组的指针,因此,**b[i](即**(*(b+i)))是一个指向一维数组(它是*b[i]所指向的一维数组)的第0个元素的指针,***b[i](即***(*(b+i)))是一维数组(它是*b[i]所指向的一维数组)的第0个元素。 例10.4指向指针的指针数组。 #include voidmain(){ char*p[]={WANCX,SHUW,LUOSW,LIUXP};//定义指针数组p char**b[4];//定义指向指针的指针数组b inti; for(i=0;i<4;i++) b[i]=p+i;//给指向指针的指针数组元素b[i]赋值 for(i=0;i<4;i++) printf(%s\n,*b[i]); printf(\n); } 运行结果如下: WANCX SHUW LUOSW LIUXP 10.2返回指针的函数 10.2.1返回指针的函数 一个函数不仅可以带回一个整型值、字符值、实型值等,也可以带回一个指针型的值。 这种返回指针的函数头(即函数的说明部分)的一般定义形式如下: 类型标识符*函数名(形参列表) 例10.5返回指针的函数。 #include voidmain(){ inta[10]={60,30,27,54,59,92,47,76,82,18},*max(int*),*p; p=max(a); printf(Max=%d\n,*p); } int*max(int*p){ inti,*q=p; for(i=1;i<10;i++) if(*(p+i)>*q)q=p+i; returnq; } 执行结果如下: Max=92 函数max的功能是查找实参传过来的一维数组a中最大值的元素,并将指向该最大值元素的指针返回给主调函数,即返回一个指向int型存储单元的指针。 如图10-7所示。 函数调用结束后返回92存储单元的指针 图10-7返回指针的函数 注意: 函数所返回的指针必须是指向尚未释放的存储单元的指针,如全局变量、主调函数定义变量所分配的存储单元,或动态存储分配函数所分配的存储单元(见10.4.2小节)等。 如果函数所返回的指针是指向本函数所分配的存储单元,则可能得不到正确的结果。 例如,如果将例10.5中的max函数修改为如下形式,则main函数中的输出结果可能不正确,这是因为返回指针所指向的m变量在函数返回后被释放了。 int*max(int*p){ inti,m=*p; for(i=1;i<10;i++) if(*(p+i)>m)m=*(p+i); return&m; } 10.2.2返回行指针的函数 把返回指针的函数和二维数组行指针的概念结合起来使用,就可得到返回行指针的函数头(即函数的说明部分)的一般定义形式: 类型标识符(*函数名(形参列表))[数组长度] 例10.6二维数组score[4][7]存放了4个学生5门课程的成绩及总成绩,第0列存放学生编号,第1至5列存放5门课程的成绩,第6列存放总成绩。 要求调用函数max找出总成绩最高的学生,并返回指向该学生的行指针,然后在主函数中输出该学生的成绩。 程序如下: #include voidmain(){ intscore[4][7]={{1,80,82,95,88,93,438},{2,86,54,80,95,57,372}, {3,80,70,56,88,93,387},{4,95,89,87,80,96,447}}; intj,(*p)[7]; int(*max(int(*)[7],int))[7];//声明被调用函数的原型 p=max(score,4);//实参score是二维数组名,即行指针 printf(No.\tscore1\tscore2\tscore3\tscore4\tscore5\ttotal\n); for(j=0;j<7;j++) printf(%3d\t,*(*p+j)); printf(\n); } int(*max(int(*p)[7],intn))[7]{//指出总成绩最高的学生 inti,(*q)[7]=p; for(i=1;i if(p[i][6]>(*q)[6])q=p+i; returnq;//返回总成绩最高的学生的行指针 } 运行结果如下: No.score1score2score3score4score5total 49589878096447 10.3函数的指针 程序在编译时,每一个函数都分配了一个入口地址,这个入口地址就称为函数的指针。 10.3.1指向函数的指针变量 指针变量不仅可以指向一般变量,也可以指向数组和结构体类型变量,还可以指向函数。 我们可以定义一个指针变量来指向函数。 指向函数的指针变量的一般定义形式如下: 类型标识符(*变量名)(); 例如,语句: int(*p)(); 定义了一个指针变量p,指针变量p可以用来指向一个返回int型值的函数。 这里,*p两侧的括号不可少,(*p)代表p是一个指针变量,(*p)()则代表p是一个指向函数的指针变量。 指向函数的指针变量定义后,并没有明确表示指向哪一个函数。 将某个函数的指针(函数名就代表该函数的指针)赋给指向函数的指针变量后,该指针变量就指向该函数,以后就可以通过该指针变量来调用该函数。 通过指向函数的指针变量调用所指向函数的一般调用形式为: (*指针变量名)(实参列表); 例如,有一返回int型值的函数max,则: int(*p)();/*定义指向函数的指针变量p*/ p=max;/*使指针变量p指向函数max*/ z=(*p)(a,b);/*通过指针变量p调用函数max*/ 等价于: z=max(a,b); 例10.7用梯形法求定积分的近似值。 用梯形法求定积分的近似值的公式为: 其中, 为等分小区间数, 。 以下程序调用trap函数求定积分,被积函数分别是: ①fun1(x)=1+x+x*x,且a=0,b=2,n=2000。 ②fun2(x)=5+2*x+3*x*x,且a=1,b=2,n=1000。 程序如下: #include #include doublefun1(doublex){ return(1.0+x+x*x); } doublefun2(doublex){ return(5.0+2*x+3*x*x); } doubletrap(double(*p)(double),doublea,doubleb,doublen){ doublet,h; inti; t=0.5*((*p)(a)+(*p)(b)); h=fabs(b-a)/n; for(i=1;i<=n-1;i++) t=t+(*p)(a+i*h); t=t*h; return(t); } voidmain(){ doubley1,y2,(*p1)(double),(*p2)(double); p1=fun1; p2=fun2; y1=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第10章 指针的进一步讨论 10 指针 进一步 讨论