第10章指针的进一步讨论.docx

上传人:b****7 文档编号:11164150 上传时间:2023-02-25 格式:DOCX 页数:35 大小:99.46KB
下载 相关 举报
第10章指针的进一步讨论.docx_第1页
第1页 / 共35页
第10章指针的进一步讨论.docx_第2页
第2页 / 共35页
第10章指针的进一步讨论.docx_第3页
第3页 / 共35页
第10章指针的进一步讨论.docx_第4页
第4页 / 共35页
第10章指针的进一步讨论.docx_第5页
第5页 / 共35页
点击查看更多>>
下载资源
资源描述

第10章指针的进一步讨论.docx

《第10章指针的进一步讨论.docx》由会员分享,可在线阅读,更多相关《第10章指针的进一步讨论.docx(35页珍藏版)》请在冰豆网上搜索。

第10章指针的进一步讨论.docx

第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=

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1