版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第十章指针
指针:C的一个重要概念、重要特色。它使C具备了强大的功能,使C成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。不掌握指针等于没有掌握C语言的精华。第十章指针110.1指针的概念
简单地说,指针就是地址。要掌握指针的概念就必须弄清:
■内存地址概念?
■变量与地址的关系?
■如何通过地址进行变量的存取?10.1指针的概念2说明例:
内存用户数据
10003
i10026
j10049
k对变量值的存取总是按地址进行的----直接访问。inti,j,k;i=3;j=6;k=i+j;程序经编译后,变量名就不复存在,以地址对应。说明例:inti,j,k;程序经编译后,变量名就不复存在3也可以采用“间接访问”方式:
先将变量i的地址存放到另一变量p1中,要访问i时,先取出p1的内容(变量i的地址),再去访问该地址所对应的内存单元中的内容(变量i的值)。c语言程序设计》10-指针4
内存用户数据10003i10026j10049k20001000p120041002p2inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;int5在以上概念的基础上对指针下定义:变量的地址就是该变量的指针。存放地址的变量称指针变量。若p1存放了变量i的地址,则称p1是指向变量i的指针变量。10001002100410001002ijkp1p2369inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;10001002100410001002ijkp1p2369610.2变量的指针和指向变量的指针变量
变量的指针
指针变量
指向变量的指针变量用“*”代表“指向”如*p1代表它所指向的变量i,同一内存单元。以下两个语句等价:
i=3;直接访问
*p1=3;间接访问
inti,*p1; p1=&i;10.2变量的指针和指向变量的指针变量7内存用户数据
10003i10026j
10049k20021000p12004
1002p2inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;*p1=5;*p2=8;58inti,j,k;588■指针变量的定义指针变量也必须先定义后使用。
int*p1;注意:①*表示该变量为指针变量,但变量名是p1。②一个指针变量只能指向同一类型的变量。inti,*p1;floata;p1=&i;合法p1=&a;不合法■指针变量的定义9
■指针变量的引用两种用法:
①用地址运算符&p1=&i;
②用指针运算符*(实行间接访问)*p1=100;k=*p1;注意:指针变量只能放地址(指针)。p1=100;不允许使用指针运算符*之前,p1必须被赋值(即p1必须存放了某个变量的地址)■指针变量的引用10例:T10-1.c注意:要区别定义和引用中的“*”main(){inta=100,b=10;int*p1,*p2;定义指针变量,尚无体指向p1=&a;p1指向ap2=&b;p2指向b
printf(“*p1=%d,*p2=%d\n”,*p1,*p2);printf(“&a=%x,&b=%x\n”,&a,&b);printf(“p1=%x,p2=%x\n”,p1,p2);printf(“&p1=%x,&p2=%x\n”,&p1,&p2);}运算结果:*p1=100,*p2=10&a=12ff7c,&b=12ff78p1=12ff7c,p2=12ff78&p1=12ff74,&p2=12ff70例:T10-1.c注意:要区别定义和引用中的“*”运11要特别注意以下用法的后果:egp1.cint*p1;*p1=100;c语言程序设计》10-指针12例:输入a和b两个整数,按先大后小的顺序输出
main()T10-2.c{inta,b,*p1,*p2,*p;
scanf(“%d,%d”,&a,&b);
1000
5a
p1=&a;p2=&b;
1002
9b
if(a<b)
{p=p1;p1=p2;p2=p;}
printf(“\n%d,%d”,a,b);
2000
p1
printf(“\n%d,%d”,*p1,*p2);
2004
p2}
2006
p
改变p1和p2的指向10001002100210001000例:输入a和b两个整数,按先大后小的顺序输出main13重要概念:
只要将某一变量的地址存入指针变量中,就可通过指针变量间接访问该变量。
配钥匙!c语言程序设计》10-指针14swap(intp1,intp2){intt;t=p1;
p1=p2;
p2=t;
10005a
}
1002
9
b
main(){inta,b;
scanf(“%d,%d”,&a,&b);
2000p1
if(a<b)swap(a,b);
2004p2
printf(“\n%d,%d”,a,b);
2006t
}T10-3-1.c59595例不用指针变量作函数参数,将两个整数按大小顺序输出。怎样直接修改a、b的值呢?修改原件——用指针!swap(intp1,intp2)59595例15
■指针变量作为函数的参数可将指针变量作函数的参数,接受实参地址,获得具体指向,进而通过指针变量间接访问主调函数的变量。■指针变量作为函数的参数16例T10-3.c用指针变量作函数参数,将两个整数按大小顺序输出。swap(int*p1,int*p2){intt;t=*p1;
*p1=*p2;
*p2=t;
10005a
}
1002
9
b
main(){inta,b;
scanf(“%d,%d”,&a,&b);
2000p1
if(a<b)swap(&a,&b);
2004p2
printf(“\n%d,%d”,a,b);
2006t
}10001002595例T10-3.c用指针变量作函数参数,将两个整数按大小顺序输17
重要概念:使用指针变量作函数参数,被调函数可以将多个结果交给主调函数。数组名做参数,可返回该数组所有元素还记得那个求成绩最高、最低和平均值的例子?还需要用全局变量吗?c语言程序设计》10-指针18例T10-4-2.c:求n个数的最大值、最小值和平均值。floataver(inta[],intn,int*max,int*min){inti;floats=0;*max=a[0];*min=a[0];for(i=0;i<n;i++){s+=a[i];if(a[i]>*max)*max=a[i];if(a[i]<*min)*min=a[i];}return(s/100);}main(){inta[100],i,max,min;floatav;for(i=0;i<100;i++)scanf(“%d”,&a[i]);av=aver(a,100,&max,&min);printf(“%d,%d,%f”,max,min,av);}例T10-4-2.c:求n个数的最大值、最小值和平均值。m19例T10-4-1.c
:编写函数,求一元二次方程的两个实根。#include“math.h”
?root(floata,floatb,floatc,){floatd;d=b*b-4*a*c;if(d<0||a==0)return(0);
?=(-b+sqrt(d))/2/a;
?=(-b-sqrt(d))/2/a;return(1);}float*x1,float*x2*x1*x2int
main(){intk;floata,b,c,x1,x2;scanf(“%f,%f,%f”,&a,&b,&c);k=root(a,b,c,&x1,&x2);if(k)printf(“\n%f,%f”,x1,x2);}输入:1,-10,25输出:5.000000,5.000000函数参数:如不需改变(只读):一般参数如需改变(读写):指数参数例T10-4-1.c:编写函数,求一元二次方程的两个实根。2010.3数组的指针和指向数组的指针变量数组有一个首地址:
数组的指针。每个数组元素也都有地址:
数组元素的指针。53216874200020022004200610.3数组的指针和指向数组的指针变量532168721
■指向数组元素的指针变量
inta[10],*p;p=a;
指向数组p=&a[0];
指向数组元素
51247680392000200220042006p■指向数组元素的指针变量5124768039200022
■通过指针引用数组元素
inta[10],*p;p=&a[0];或者p=a;
//p指向a[0]*p=1;等效于a[0]=1;即可通过p来访问a[0]也可以通过p来访问其它元素:*(p+1)=3;等效于a[1]=3;其中p+1指向a[1]注意:p+1不是地址加1,而是加一个数据类型单位。■通过指针引用数组元素23一般地,当p指向a[0]时,即p=&a[0];或者p=a;
p+i∽a+i
∽&a[i]*(p+i)∽*(a+i)
∽a[i]
∽p[i]即以下几个语句等效:a[i]=10;*(p+i)=10;*(a+i)=10;p[i]=10;c语言程序设计》10-指针24举例:用三种方法输出数组元素例T10-5-1.c用“数组名+下标”的方法main(){inti,a[5];for(i=0;i<5;i++) scanf(“%d”,&a[i]);for(i=0;i<5;i++) printf(“&a[%d]=%x,a[%d]=%d\n”,i,&a[i],i,a[i]);}输入:5566778899
输出:&a[0]=ffce,a[0]=55&a[1]=ffd0,a[1]=66&a[2]=ffd2,a[2]=77&a[3]=ffd4,a[3]=88&a[4]=ffd6,a[4]=99ffcc55a[0]ffce66a[1]ffd077a[2]ffd288a[3]ffd499a[4]ffd6a[5]举例:用三种方法输出数组元素输出:ffcc25举例:用三种方法输出数组元素例T10-5-2.c计算“数组名计算地址”的方法main(){inti,a[5];for(i=0;i<5;i++) scanf(“%d”,a+i);for(i=0;i<5;i++) printf(“a+%d)=%x,*(a+%d)=%d\n”,i,(a+i),i,*(a+i));}输出:(a+0)=ffcc,*(a+0)=55(a+1)=ffce,*(a+1)=66(a+2)=ffd0,*(a+2)=77(a+3)=ffd2,*(a+3)=88(a+4)=ffd4,*(a+4)=99ffcc55a[0]ffce66a[1]ffd077a[2]ffd288a[3]ffd499a[4]ffd6a[5]举例:用三种方法输出数组元素输出:ffcc26举例:用三种方法输出数组元素例T10-5-3_new.c用“指针变量+下标”的方法(指针法I)main(){int*p,a[5],i;p=a;for(i=0;i<5;i++)
scanf(“%d”,p+i);for(i=0;i<5;i++)
printf("&p[%d]=%x,p[%d]=%d,*(p+%d)=%d\n",i,&p[i],i,p[i],i,*(p+i));}12ff6c55p[0]12ff7066p[1]12ff7477p[2]12ff7888p[3]12ff7c99p[4]输出:&p[0]=12ff6c,p[0]=55,*(p+0)=55&p[1]=12ff70,p[1]=66,*(p+1)=66&p[2]=12ff74,p[2]=77,*(p+2)=77&p[3]=12ff78,p[3]=88,*(p+3)=88&p[4]=12ff7c,p[4]=99,*(p+4)=99举例:用三种方法输出数组元素12ff6c5527举例:用三种方法输出数组元素(例T10-5-4_new.c)用“指针变量指向数组元素”法(指针法II)main(){int*p,a[5];for(p=a;p<(a+5);p++) scanf(“%d”,p+i);for(p=a;p<(a+5);p++) printf(“p=%x,*p=%d\n”,p,*p);}输出:p=12ff6c,*p=55p=12ff70,*p=66p=12ff74,*p=77p=12ff78,*p=88p=12ff7c,*p=9912ff6c55a[0]12ff7066a[1]12ff7477a[2]12ff7888a[3]12ff7c99a[4]p++:合法,因p是指针变量,而变量可以用++运算符的a++:不合法,因为a是数组名,其值是数组元素的首地址,分配之后不可变。举例:用三种方法输出数组元素(例T10-5-4_new.c28举例:用三种方法输出数组元素(例T10-5-4.c)指针变量当作数组名使用(混合指针法)
main(){inta[5],*p=a,i;for(p=a;p<a+5;p++)scanf(“%d”,p);
p=a; //不能漏!!for(i=0;i<5;i++)
printf(“&p[%d]=%x,p[%d]=%d,*(p+%d)=%d\n”, i,&p[i],i,p[i],i,*(p+i));}输出:&p[0]=12ff6c,p[0]=55,*(p+0)=55&p[1]=12ff70,p[1]=66,*(p+1)=66&p[2]=12ff74,p[2]=77,*(p+2)=77&p[3]=12ff78,p[3]=88,*(p+3)=88&p[4]=12ff7c,p[4]=99,*(p+4)=9912ff6c55p12ff7066p+112ff7477p+212ff7888p+312ff7c99p+4举例:用三种方法输出数组元素(例T10-5-4.c)输出:29通过指针引用数组元素的方法小结例:累加求和的各种用法:int*p,a[10];s=0;p=a;for(i=0;i<10;i++)s+=a[i];for(i=0;i<10;i++)s+=*(a+i);for(i=0;i<10;i++)s+=*(p+i);for(i=0;i<10;i++)s+=p[i];for(i=0;i<10;i++)s+=*p++;for(p=a;p<a+10;p++)s+=*p;
最后一种用法效率高。数组元素地址法指针法I指针变量指向数组元素指针法II指针变量当作数组名注意不能使用a++通过指针引用数组元素的方法小结数组元素地址法指针法I指针法I30指针变量使用时的几个问题,若p当前指向a数组的第i个元素,则:10-plus.c①*p:是取出a[i]的值.②p++:指针指向a[i+1].③*p++:等价于*(p++)及a[i++]
,优先级同为2,从右向左结合,将a[i]
的值取出,后再使p增1(不是单纯的加一,是p指向a[i+1]).④*(++p):与a[++i]等价,先将p指向a[i+1],然后将该元素取出.⑤(*p)++:将p所指的数组元素a[i]的值取出,使a[i]的值增1.⑥p--:指针指向a[i-1].⑦*(p--):与a[i--]等价,将p所指向的第i个数组元素取出,然后使p指向i-1元素.⑧*(--p):与a[--i]等价,先将p减1指向第i-1个元素,然后将其取出.⑨(*p)--:将p所指的数组元素a[i]的值取出,使a[i]的值减1.指针变量使用时的几个问题,31■数组名作为函数参数有了指针概念的基础上,重新回顾数组名作为函数参数时,数据的传递情况:voidsort(inta[],intn)voidsort(int*a,intn)■数组名作为函数参数32例:选择法排序函数voidsort(int*a,intn)例T10-7-1.c
{inti,j,t;for(i=0;i<n-1;i++)for(j=i+1;j<n;j++)if(a[i]>a[j]){t=a[i];a[i]=a[j];a[j]=t;}}只将形参改为指针变量,仍按下标法使用
voidsort(int*a,intn){inti,j,t;for(i=0;i<n-1;i++)for(j=i+1;j<n;j++)if(*(a+i)>*(a+j)){t=*(a+i);*(a+i)=*(a+j);*(a+j)=t;}}按指针法使用例T10-7-2.c
例:选择法排序函数voidsort(int*a33进一步优化:都用指针,效率更高!(例T10-7-3.c)voidsort(int*a,intn){int*i,*j,t;for(i=a;i<a+n-1;i++)for(j=i+1;j<a+n;j++)if(*i>*j){t=*i;*i=*j;*j=t;}}main(){inta[10],j;for(j=0;j<10;j++)scanf(“%d”,a+j);sort(a,10);for(j=0;j<10;j++)printf(“%5d”,a[j]);}
进一步优化:都用指针,效率更高!(例T10-7-3.c34分段排序?例T10-7-4.c
main(){inta[10],j;for(j=0;j<10;j++)scanf(“%d”,a+j);for(j=0;j<10;j++) printf(“%5d”,a[j]);}sort(a,5);sort(a+5,5);voidsort(int*a,intn){int*i,*j,t;for(i=a;i<a+n-1;i++)for(j=i+1;j<a+n;j++)if(*i>*j) {t=*i;*i=*j;*j=t;}}分段排序?例T10-7-4.csort(a,5);sort35■指向多维数组的指针和指针变量
从本质上说,多维数组的指针与一维数组的指针相同,但在概念上和使用上,多维数组的指针要复杂些。以二维数组的指针为例:■指向多维数组的指针和指针变量36●二维数组的地址:一维:a,&a[i],a+i
二维:aa+i
(行指针:指向第i行)a[i]∽*(a+i)
(特殊的一维数组,列指针,指向行中的列)a[i]+j∽*(a+i)+j∽&a[i][j]inta[3][4]
1357911131517192123100010081016a+0a+1a+2a[0]讨论以下用法的效果:T10-8-1.cfor(i=0;i<3;i++)scanf(“%d”,a+i);输入数据:123讨论以下用法的效果:T10-8-2.cfor(i=0;i<3;i++)scanf(“%d”,a[1]+i);输入数据:123讨论以下用法的效果:T10-8-3.cfor(i=0;i<3;i++)scanf(“%d”,a[1]+i+2);输入数据:123讨论以下用法的效果:T10-8-4.cfor(i=0;i<3;i++)scanf(“%d”,a+i+1);输入数据:123a[0]+1a[0]+2a[0]+3●二维数组的地址:inta[3][4]135791137一维数组与二维数组的比较第i个一维数组的第j个元素的地址无意义*(a+i)+j第i个一维数组的第j个元素的值无意义*(*(a+i)+j)第i个一维数组的值
第i个一维数组的首地址第i个元素的值*(a+i)第i个元素
的地址一维数组第i个元素的地址a+i二维数组的首地址一维数组的首地址a二维数组一维数组一维数组与二维数组的比较第i个一维数组的第j个元素的地址无意38注意:指针运算符*作用在行指针上的结果仍是指针----列指针;*作用在列指针上的结果---具体元素。*(a+0),*(a+1),*(a+2)——仍是地址。*(a+i)∽a[i]*(a[0]),*(a[1]),*(a[1])——具体元素值。*(a[i])*(a+i)+j也是地址,但要区别:(a+i)+j——行指针
(a+1)+1?
*(a+i)+j——列指针
*(a+1)+1?
100010081016a+0a+1a+2a[0]a[1]a[2]*(a+1)1357911131517192123注意:100010081016a+0a+1a+2a[0]a[39
如果要通过a+i形式的地址访问数组元素的具体内容,则:*(*(a+i))或*(*(a+i)+j)如:*(*(a+1))——a[1][0]*(*(a+1)+2)——a[1][2]讨论:例T10-8-5.c*(a+2)*(*(a+1)+3)*(a[1]+1)*(*(a+1)+5)123456789111012如果要通过a+i形式的地址访问数组元素的具体内容,则:140例:求数组a的所有元素之和。
可有多种用法:例T10-9-1.c~T10-9-3.c
M行N列for(i=0;i<M;i++)for(i=0;i<M;i++)for(j=0;j<N;j++)for(j=0;j<N;j++)s+=a[i][j];s+=*(a[i]+j);for(i=0;i<M;i++)for(j=0;j<N;j++)s+=*(*(a+i)+j);
a[i][j]~*(a[i]+j)~*(*(a+i)+j)例:求数组a的所有元素之和。a[i][j]~*(a[i]+j41●指向二维数组的指针变量同样可使一个指针变量p指向二维数组a,再通过p访问数组元素。例T10-10.cmain(){inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};inti,k,*p;p=a;k=*p;?k=*(p+2);?for(p=a;p<a+2;p++)printf(“%3d,”,*p);?}N列:a[i][j]~*(p+i*N+j)~p[i*N+j]123456789101112k=p[1][2];不合法k=*(*(p+1)+2);k=p[1*4+2];合法k=*(p+1*4+2);●指向二维数组的指针变量123456789101112k=p42例:求矩阵的上三角元素之和。例T10-11.cmain(){inta[3][4],*p,i,j,s=0;输入ap=a;for(i=0;i<3;i++) for(j=i;j<4;j++) s+=p[i*4+j];
或s+=*(p+4*i+j)printf(“\n%d”,s);}//s=54N列:a[i][j]~*(p+i*N+j)~p[i*N+j]123456789101112例:求矩阵的上三角元素之和。例T10-11.c123456743●多维数组的指针作函数参数用于接受实参数组地址的形参可用两种:行指针和列指针。以方阵转置为例:例T10-13-1.c
voidat(int(*p)[3])用行指针{inti,j,t;缺点:不通用(必须制定列数)for(i=0;i<3;i++)for(j=i+1;j<3;j++){t=p[i][j];p[i][j]=p[j][i];p[j][i]=t;}}p指向一个包含3个整形元素的一维数组(行指针),p相当于一个二维数组!●多维数组的指针作函数参数44用列指针:例T10-13-2.c
voidat(int*p,intn)//n为列数{inti,j,t;for(i=0;i<n;i++)for(j=i+1;j<n;j++){t=p[i*n+j];p[i*n+j]=p[j*n+i];
p[j*n+i]=t;}}
优点:通用在编通用函数时,一般使用列指针。用列指针:例T10-13-2.c45用列指针:例T10-13-3.c
intsum(int*p,intm,intn)//m行n列{inti,j,s=0;
for(i=0;i<m;i++) for(j=i;j<n;j++) s+=p[i*n+j];
或s+=*(p+n*i+j) return(s);}
优点:通用在编通用函数时,一般使用列指针。用列指针:例T10-13-3.c4610.4字符串的指针和指向字符串的指针变量■字符串的表示形式可用两种方法访问字符串:
①用字符数组存放字符串
②用字符指针指向一个字符串10.4字符串的指针和指向字符串的指针变量47例T10-14.cmain(){charstr[5]=”abc”;定义字符数组,并将字符串存入char*p=str;定义指针变量,指向字符串
printf(“%s”,str);通过数组名访问字符串
printf(“%s”,p);通过指针变量访问字符串
printf(“%c”,*(p+2));通过指针变量访问字符}strabc\0abc\010001000p例T10-14.cstrabc\0abc\01000100048与其它一维数组的指针相比,字符串的指针有其独特之处:
①可以通过指针对字符串进行整体访问。
②对字符串的操作依赖于结束符。
③可以整体赋初值。
④有各种字符串处理函数。与其它一维数组的指针相比,字符串的指针有其独特之处:49
本节重点掌握:
①通过数组和通过指针操作字符串的基本方法。
②常用的字符串处理方法。本节重点掌握:50例:字符串拷贝操作。例T10-15-1.c,例T10-15-2.cmain(){chara[]=”abcdef”,b[20];inti;for(i=0;*(a+i)!=’\0’;i++)*(b+i)=*(a+i);*(b+i)=’\0’;printf(“%s”,b);}main(){chara[]=”abcdef”,b[20],*p1,*p2;p1=a;p2=b;for(;*p1!=’\0’;p1++,p2++)*p2=*p1;*p2=’\0’;printf(“%s”,b);}用指针变量处理例:字符串拷贝操作。例T10-15-1.c,例T10-15-51将拷贝操作编成一函数:例T10-15-3.c,例T10-15-4.cvoidcopy_string(char*from,char*to){for(;*from;from++,to++)*to=*from;*to=‘\0’;}还可以改成:voidcopy_string(char*from,char*to){for(;*from;)*to++=*from++;*to=‘\0’;}将拷贝操作编成一函数:例T10-15-3.c,例T10-1552字符串合并函数:例T10-16.c,voidappend_string(char*from,char*to){for(;*to;to++);for(;*from;)*to++=*from++;*to=0;}字符串合并函数:例T10-16.c,53阅读程序:例T10-17.cvoidf(char*c)main()
{{c+=2;charc[20]=”abcdef”;
(*c)++;f(c+1);c++;*c=0;printf(“%s”,c);}}
阅读程序:例T10-17.c54■内存空间的动态分配在程序设计中,对于要处理的批量数据,我们往往是选用数组作为存放这些数据的数据结构,然而,数组有一个明显的缺点,就是在定义数组时,其长度必须是常值,无法根据需要动态地定义。这样,在很多情况下,不是定义的数组长度不够,就是定义太长以至于浪费。采用动态分配可以克服这一缺点,并且可以随时释放。■内存空间的动态分配55
动态分配内存空间步骤:①定义一指针变量。
②申请一片内存空间,并将其首地址赋给指针变量。此时便可通过指针变量访问这片内存;不成功则返回地址为0③用完后释放这片内存空间。
int*p;p=malloc(字节数);……
free(p);sizeof(类型):返回该类型数据在当前编译系统中所占的字节数。以上函数的原形在stdio.h中。p动态分配内存空间步骤:p56例:对n个学生的分数排序后输出。例T10-18.c
#include“stdio.h”voidsort(int*a,intn){┈}main(){int*a,j,n;scanf(“%d”,&n);a=malloc(n*sizeof(int));
if(!a)exit(0);for(j=0;j<n;j++)scanf(“%d”,a+j);sort(a,n);for(j=0;j<n;j++)printf(“%5d”,a[j]);
free(a);}//exit(0):正常运行程序并退出程序//exit(1):非正常运行程序导致退出程序例:对n个学生的分数排序后输出。例T10-18.c5710.5函数的指针和指向函数的指针变量■用函数指针变量调用函数可以用指针变量指向一个函数。一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针。可以用指针变量指向函数,然后通过该指针变量调用此函数。10.5函数的指针和指向函数的指针变量58intmax(intx,inty){intz;if(x>y)z=x;elsez=y;return(z);}main(){int(*p)();
定义指向函数的指针变量pinta,b,c;
p=max;
将p指向函数max
scanf(“%d%d”,&a,&b);
c=(*p)(a,b);
通过p调用函数max等效于c=max(a,b);
printf(“\n%d”,c);}例T10-19.cintmax(intx,inty)59■把指向函数的指针变量作为函数参数指向函数的指针变量最常见的用途是把它作为函数的参数,用于接受主调函数传来的某一函数的入口地址,从而在被调函数中可以通过该指针变量调用它所指向的函数,这样,被调函数中就可实现非固定函数的调用,以达到编写通用函数的目的。
例:用矩形法编写一个通用的求定积分的函数。关键问题:如何处理被积函数是未知的。■把指向函数的指针变量作为函数参数60doubleintgral(doublea,doubleb,intn,
double(*f)()){inti;doubleh,x,y,s=0;h=(b-a)/n;for(i=1;i<=n;i++){x=a+(i-1)*h;y=(*f)(x);s+=h*y;}return(s);}例T10-20.cdoublef1(doublex){return(3*x*x+2*x-1);}main(){doubles;s=intgral(1.0,2.0,100,f1);}doubleintgral(doublea,dou6110.8指针型数据小结一、有关指针的数据类型(见下表)p是一个指针变量,它指向一个指向整型数据的指针变量int**pp为指向函数的指针,该函数返回一个整型值int(*p)()p为一个函数,它返回一个指针,该指针指向一个整型数据int*p()f为返回整型数据的函数intf()定义指针变量p,它指向一个含n个整型元素的一维数组int(*p)[n]定义指针数组p,它由n个指向整型数据的指针元素组成int*p[n]定义整型数组a,它有n个元素inta[n]定义指向整型数据的指针变量pint*p定义整型变量iinti含义定义10.8指针型数据小结一、有关指针的数据类型(见下62二、指针运算小结1、指针变量加减一个整数如:p++p--p+ip-ip+=ip-=i等实际含义如p+i代表地址计算:p+c*i,c为字节数,例如字符型c=1。
2、指针变量赋值如:p=&aa为变量,p为指针变量p=arrayarray为数组名p=&array[i]p=maxmax为函数名p1=p2p2为指针变量注意:不能把一个整数或常数赋给p,如p=1000同样也不能把p赋给整型变量,如i=p二、指针运算小结633、指针变量可以赋空值如:p=NULL;实际NULL是整数0。
4、两个指针变量可以相减如两个指针变量指向同一个数组的元素(如右图),则p2-p1=4-1=3但p1+p2无意义。
5、两个指针变量比较
若两个指针指向同一个数组的元素,则可以比较,如:p1<p2p1>p23、指针变量可以赋空值64c语言程序设计》10-指针65inv(intx[],intn){ inti,j,m,t; main() m=(n-1)/2; { for(i=0;i<=m;i++) inta[10],i; { 输入a j=n-1-i; inv(a,10); t=x[i]; 输出a x[i]=x[j]; } x[j]=t; }}ax例T10-6-1.c:将数组a中的n个数按相反顺序存放。a与x共用同一片内存单元inv(intx[],intn)ax例T10-6-66inv(int*x,intn)指针变量作函数参数时的传递情况{inti,j,m,t;main()m=(n-1)/2;{for(i=0;i<=m;i++)inta[10],i;{j=n-1-i;输入at=x[i];inv(a,10);x[i]=x[j];输出ax[j]=t;}}下标法例T10-6-2.c
}
a10001000x{j=n-1-i;t=*(x+i);*(x+i)=*(x+j);*(x+j)=t;}指针法例T10-6-3.c
inv(int*x,intn)指针变量67进一步优化:例T10-6-4.c
intinv(int*x,intn){main()int*i=x,*j=x+n-1,t;{for(;i<j;i++,j--)inta[10],i;{输入a
t=*i;inv(a,10);
*i=*j;
输出a
*j=t;}}}优点:简练效率高ij进一步优化:例T10-6-4.cij68第十章指针
指针:C的一个重要概念、重要特色。它使C具备了强大的功能,使C成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。不掌握指针等于没有掌握C语言的精华。第十章指针6910.1指针的概念
简单地说,指针就是地址。要掌握指针的概念就必须弄清:
■内存地址概念?
■变量与地址的关系?
■如何通过地址进行变量的存取?10.1指针的概念70说明例:
内存用户数据
10003
i10026
j10049
k对变量值的存取总是按地址进行的----直接访问。inti,j,k;i=3;j=6;k=i+j;程序经编译后,变量名就不复存在,以地址对应。说明例:inti,j,k;程序经编译后,变量名就不复存在71也可以采用“间接访问”方式:
先将变量i的地址存放到另一变量p1中,要访问i时,先取出p1的内容(变量i的地址),再去访问该地址所对应的内存单元中的内容(变量i的值)。c语言程序设计》10-指针72
内存用户数据10003i10026j10049k20001000p120041002p2inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;int73在以上概念的基础上对指针下定义:变量的地址就是该变量的指针。存放地址的变量称指针变量。若p1存放了变量i的地址,则称p1是指向变量i的指针变量。10001002100410001002ijkp1p2369inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;10001002100410001002ijkp1p23697410.2变量的指针和指向变量的指针变量
变量的指针
指针变量
指向变量的指针变量用“*”代表“指向”如*p1代表它所指向的变量i,同一内存单元。以下两个语句等价:
i=3;直接访问
*p1=3;间接访问
inti,*p1; p1=&i;10.2变量的指针和指向变量的指针变量75内存用户数据
10003i10026j
10049k20021000p12004
1002p2inti,j,k;i=3;j=6;k=i+j;int*p1,*p2;p1=&i;p2=&j;*p1=5;*p2=8;58inti,j,k;5876■指针变量的定义指针变量也必须先定义后使用。
int*p1;注意:①*表示该变量为指针变量,但变量名是p1。②一个指针变量只能指向同一类型的变量。inti,*p1;floata;p1=&i;合法p1=&a;不合法■指针变量的定义77
■指针变量的引用两种用法:
①用地址运算符&p1=&i;
②用指针运算符*(实行间接访问)*p1=100;k=*p1;注意:指针变量只能放地址(指针)。p1=100;不允许使用指针运算符*之前,p1必须被赋值(即p1必须存放了某个变量的地址)■指针变量的引用78例:T10-1.c注意:要区别定义和引用中的“*”main(){inta=100,b=10;int*p1,*p2;定义指针变量,尚无体指向p1=&a;p1指向ap2=&b;p2指向b
printf(“*p1=%d,*p2=%d\n”,*p1,*p2);printf(“&a=%x,&b=%x\n”,&a,&b);printf(“p1=%x,p2=%x\n”,p1,p2);printf(“&p1=%x,&p2=%x\n”,&p1,&p2);}运算结果:*p1=100,*p2=10&a=12ff7c,&b=12ff78p1=12ff7c,p2=12ff78&p1=12ff74,&p2=12ff70例:T10-1.c注意:要区别定义和引用中的“*”运79要特别注意以下用法的后果:egp1.cint*p1;*p1=100;c语言程序设计》10-指针80例:输入a和b两个整数,按先大后小的顺序输出
main()T10-2.c{inta,b,*p1,*p2,*p;
scanf(“%d,%d”,&a,&b);
1000
5a
p1=&a;p2=&b;
1002
9b
if(a<b)
{p=p1;p1=p2;p2=p;}
printf(“\n%d,%d”,a,b);
2000
p1
printf(“\n%d,%d”,*p1,*p2);
2004
p2}
2006
p
改变p1和p2的指向10001002100210001000例:输入a和b两个整数,按先大后小的顺序输出main81重要概念:
只要将某一变量的地址存入指针变量中,就可通过指针变量间接访问该变量。
配钥匙!c语言程序设计》10-指针82swap(intp1,intp2){intt;t=p1;
p1=p2;
p2=t;
10005a
}
1002
9
b
main(){inta,b;
scanf(“%d,%d”,&a,&b);
2000p1
if(a<b)swap(a,b);
2004p2
printf(“\n%d,%d”,a,b);
2006t
}T10-3-1.c59595例不用指针变量作函数参数,将两个整数按大小顺序输出。怎样直接修改a、b的值呢?修改原件——用指针!swap(intp1,intp2)59595例83
■指针变量作为函数的参数可将指针变量作函数的参数,接受实参地址,获得具体指向,进而通过指针变量间接访问主调函数的变量。■指针变量作为函数的参数84例T10-3.c用指针变量作函数参数,将两个整数按大小顺序输出。swap(int*p1,int*p2){intt;t=*p1;
*p1=*p2;
*p2=t;
10005a
}
1002
9
b
main(){inta,b;
scanf(“%d,%d”,&a,&b);
2000p1
if(a<b)swap(&a,&b);
2004p2
printf(“\n%d,%d”,a,b);
2006t
}10001002595例T10-3.c用指针变量作函数参数,将两个整数按大小顺序输85
重要概念:使用指针变量作函数参数,被调函数可以将多个结果交给主调函数。数组名做参数,可返回该数组所有元素还记得那个求成绩最高、最低和平均值的例子?还需要用全局变量吗?c语言程序设计》10-指针86例T10-4-2.c:求n个数的最大值、最小值和平均值。floataver(inta[],intn,int*max,int*min){inti;floats=0;*max=a[0];*min=a[0];for(i=0;i<n;i++){s+=a[i];if(a[i]>*max)*max=a[i];if(a[i]<*min)*min=a[i];}return(s/100);}main(){inta[100],i,max,min;floatav;for(i=0;i<100;i++)scanf(“%d”,&a[i]);av=aver(a,100,&max,&min);printf(“%d,%d,%f”,m
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 拉萨市当雄县2025-2026学年第二学期六年级语文第五单元测试卷部编版含答案
- 百色市德保县2025-2026学年第二学期五年级语文第四单元测试卷(部编版含答案)
- 葫芦岛市南票区2025-2026学年第二学期六年级语文第五单元测试卷部编版含答案
- 孝感市汉川市2025-2026学年第二学期六年级语文第五单元测试卷部编版含答案
- 芜湖市芜湖县2025-2026学年第二学期六年级语文第五单元测试卷部编版含答案
- 深度解析(2026)《CBT 3608-1993辅锅炉安装及其系统效用试验质量要求》
- 深度解析(2026)《CBT 702-1992船用柴油机铸铁气缸套技术条件》
- 深度解析(2026)《AQ 4272-2016铝镁制品机械加工粉尘防爆安全技术规范》
- 幼师舞蹈考试题目及答案
- 第三单元 (晨背悦读)语文统编版选择性必修上(共5份打包)
- 人音版音乐四年级下册《第七课 回声》大单元整体教学设计2022课标
- 危险化学品目录(2023年版)
- 2024年水溶性肥项目申请报告范稿
- 金属非金属矿山运输安全管理制度
- 木工包工承揽合同
- 水库调度规程
- 重症肺炎诊断及治疗
- 中西文化鉴赏智慧树知到期末考试答案章节答案2024年郑州大学
- MOOC 物理与艺术-南京航空航天大学 中国大学慕课答案
- 中外政治思想史-形成性测试四-国开(HB)-参考资料
- 《多源图像融合技术及其遥感应用-图像融合技术》课件
评论
0/150
提交评论