七、指针7.1指针概念7.2指针变量的定义和使用7.3指针与一维数组7.4指针与函数7.5指针数组、多级指针与指向一维数组的指针7.6指针与字符串7.1指针概念所谓指针,某种程度上就相当于地址,但要比地址的含义更加丰富•变量的指针变量的指针指的就是该变量的首地址•指针变量指针变量是以指针(地址)为值的一种变量•指针指向变量如有普通变量x及指针变量p,且p=&x;,则称:指针p指向变量x•指针变量的类型指针变量按其所指向变量类型的不同,也分成相应的多种不同类型,指针变量只允许指向相应类型的变量。例如:有定义:inta,*p;floatb,*q;则允许:p=&a;q=&b;不允许:p=&b;q=&a;p=q;q=p;•空指针指向地址为0的指针称为空指针,空指针用于表示指向空,记为NULL,NULL是一个指针常量,对应0地址,在头文件stdio.h中定义。7.2指针变量的定义和使用•指针变量的定义一般形式:数据类型*指针变量名;如:intn,*p;floatx,*p1,*p2;•取址运算符&intn,*p;p=&n;•取值运算符*intm,n,*p;p=&n;*p=5;m=*p;•指针使用举例【例7.1】输入一个整数,通过指针方式赋值给另一个变量后输出。#includestdio.hmain(){intn,m,*p;scanf(%d,&n);p=&n;m=*p;printf(%d\n,m);}0x2000123412340x20000x20020x2004nmp地址内存变量•指针变量的初始化inti,*p=&i;char*q=abcde;把字符串首地址赋给qint*p=NULL;0地址赋给p(指向空)•指针必须先赋值再使用intn,*p;定义指针变量p*p=5;可怕的错误p=&n;*p=5;先赋值再使用7.3指针与一维数组7.3.1一维数组的内存安排inta[]={10,11,12,13,14};int*p=a;0x20021112130x20040x2006a[1]a[2]pa[0]a[3]a[4]10140x20000x20080x2000p0x200a地址内存变量7.3.2指向数组的指针运算1.指向数组的指针•指针可以指向数组中的某个元素intc[5],*p,*q;p=&c[0];/*与p=c;等价*/q=&c[1];•指向整型数组的指针与指向单个整型变量的指针在指针类型上是一样的,都是指向整型量的指针intn,c[5],*p,*q=c;p=&n;2.指针的加减整数运算指针加减一个整数,该整数表示的是该指针所指向数据类型的数据的个数,指针实际的增加量或减少量是该整数乘以指针所指向数据类型的长度(字节数)。例如:p为指针变量,则p=p+1后指向了原指向的数据的后面一个数据的首地址。如果p为字符型指针变量,则其增量为1字节;如果p为整型指针变量,则其增量为2字节;如果p为实型指针变量,则其增量为4字节。3.指针和所指向值的自增自减运算•指针可以进行自增自减运算设p为指针,则有:p++;++p;p--;--p;•对指针所指向的值也可以进行自增自减运算,如:(*p)++;或++(*p);相当于*p=*p+1;(*p)--;或--(*p);相当于*p=*p-1;•指针与++、--和*运算符++、--和*运算都是单目运算符,具有同等的优先级,C语言规定单目运算符是右结合的设有:intn,a[5]={0,1,2,3,4},*p=&a[2];则:表达式作用和意义*++p先++p使p指向a[3],再取*p即a[3]值++*pp指向的元素值增1,即++a[2](*p)++先取*p即a[2]值,再使a[2]值增1,即a[2]++*(p++)先取*p即a[2]值,再使p增1指向a[3]注:*p++等价于*(p++)【例7.2】分析以下程序的运行结果。#includestdio.hmain(){inta[]={10,11,12,13,14},*p,n,i;p=&a[0];n=*(p++);n+=*(++p);n+=(*(p+=2))++;n-=(*(--p))--;n-=*(--p-1);printf(%d\n,n);for(i=1;i5;i++)printf(%d,a[i]);}/*n=a[0]=10p→a[1]*//*p→a[2]n=n+a[2]=22*//*p→a[4]n=n+a[4]=36a[4]=15*//*p→a[3]n=n-a[3]=23a[3]=12*//*p→a[2]n=n-a[1]=12*//*p→a[0]*//*12*//*1011121215*/4.指针的比较运算•任意两个指针可以进行相等比较,以确定它们是否指向同一个变量•两个指针指向同一数组时,可进行比较运算(含、=、、=、!=、==)。以确定它们在数组中的前后位置值。例:指针关系运算举例。#includestdlib.h#includetime.hmain(){inta[20],*p,*q,n1,n2;randomize();/*初始化随机数发生器*/n1=random(20);n2=random(20);p=a+n1;/*p指向a[n1]*/q=a+n2;/*q指向a[n2]*/if(p==q)printf(\npandqpointssameelement.);elseif(pq)printf(\nppointsforwards.);elseprintf(\nqpointsforwards.);printf(\np=%xq=%x,p,q);}5.指针的相减运算只有指向同一组类型相同数据的指针之间,进行减法运算才有意义,运算结果为整数,表示两指针间相隔的数据个数。例:指针间的加减运算示例。main(){inta[20],*p,*q;p=&a[2];q=&a[6];printf(\nq=%x\np=%x\nq-p=%d,q,p,q-p);}/*q-p应为4。*/7.3.3下标法与指针法表示的等价性1.数组名作为指针常量•数组名作为一个指针,表示整个数组的首地址,它同时也是数组第一个元素的地址。设有定义:inta[5],*p;则:p=a;与p=&a[0];等价p=a+1;与p=&a[1];等价*(a+2)与a[2]等价•数组名作为指针类型的常量,下列运算非法的:a++、--a、a=…、a+=…等2.在指针中使用下标运算符•下标运算符[]可以和指针配合使用,可将指针看成是一个指向某个数组的指针,允许使用下标法表示所指向数组中的元素如有定义:inta[5],*p=a;则:a[2]p[2]*(a+2)*(p+2)等价•当指针不是指向数组的第一个元素时,同一个数组元素的下标是不同的,并允许下标取负数如有定义:inta[5],*p=a,*q=a+2;则:q[0]与a[2]为同一个数组元素*(q-2)与q[-2]都是a[0]3.下标法表示与指针法表示的等价性变量定义语句inta[5],*p=a,*q=a+2;表达式含义apq下标指针下标指针下标指针数值第1个元素a[0]*ap[0]*pq[-2]*(q-2)第3个元素a[2]*(a+2)p[2]*(p+2)q[0]*q指针指向第1个&a[0]a&p[0]p&q[-2]q-2指向第3个&a[2]a+2&p[2]p+2&q[0]q等价性x[i]与*(x+i)等价&x[i]与x+i等价7.3.4程序举例【例7.3】使用指针法输入5个整数,计算并输出平均值。#defineN5/*下标法*/main(){inta[N],i,s;for(i=0;iN;i++)scanf(%d,&a[i]);for(s=0,i=0;iN;i++)s+=a[i];printf(%f\n,(float)s/N);}/*指针法*/#defineN5main(){inta[N],i,s,*p;for(p=a;pa+N;p++)scanf(%d,p);for(s=0,p=a;pa+N;p++)s+=*p;printf(%f\n,(float)s/N);}例:写出下列程序的输出结果。#includestdio.hmain(){voidfun(int*,int,int);inti,s[]={0,1,2,3,4,5};fun(s,0,3);fun(s,2,5);for(i=0;i6;i++)printf(s[%d]=%d,i,s[i]);}voidfun(int*a,intn,intm){intt;while(nm){t=*(a+n);*(a+n)=*(a+m);*(a+m)=t;n++;m--;}}运行结果:s[0]=3s[1]=2s[2]=5s[3]=4s[4]=0s[5]=17.4指针与函数7.4.1指针作为函数参数1.传递单个变量指针将单个变量的指针传递给函数往往是为了让函数能通过该指针反过来修改主调函数的变量值例如:scanf(%d,&n);主调函数将变量n的指针(&n)传递给scanf函数,scanf函数将键盘上输入的数值通过该指针存到主调函数的变量n中。【例7.4】编写函数,使得能交换二个变量的值。voidswap(int*p1,int*p2){intt;t=*p1;*p1=*p2;*p2=t;}main(){intx,y;scanf(%d%d,&x,&y);swap(&x,&y);printf(x=%d,y=%d\n,x,y);}2.传递数组传递数组传递的其实是数组的首地址【例7.5】编写函数,使用冒泡法对数组从小到大排序。voidswap(int*p1,int*p2){intt;t=*p1;*p1=*p2;*p2=t;}voidsort(inta[],intn){inti,j;for(i=0;in-1;i++)for(j=0;jn-1-i;j++)if(a[j]a[j+1])swap(&a[j],&a[j+1]);}main(){intx[10],i;for(i=0;i10;i++)scanf(%d,&x[i]);sort(x,10);for(i=0;i10;i++)printf(%d,x[i]);printf(\n);}7.4.2指针做为函数返回类型【例7.6】编写函数,在一个已从小到大排序的数组中查找一个指定的数,如果找到,返回指向数组中该数的指针,否则返回空指针。int*find(int*a,intn,intdata)/*数组a长度n待查数data*/{int*p=a,*q=a+n,*r;while(pq)/*待查找区不为空时*/{r=p+(q-p)/2;/*待查找区的中点位置*/if(*r==data)returnr;/*如找到,返回该指针*/elseif(*rdata)p=r+1;/*比中点大,在后半区*/elseq=r-1;/*比中点小,在前半区*/}returnNULL;/*没找到,返回空指针*/}main(){intx[]={11,22,33,44,55,66,77,88,99,100},d,*p;scanf(%d,&d);/*输入要查找的数*/p=find(x,10,d);/*调用查找,结果赋值给p*/if(p!=NULL)/*判断是否找到数*/printf(Foundat%d\n,p-x);/*找到,输出下标*/elseprintf(NotFound.\n);/*没找到*/}7.4.3动态内存分配与释放函数在处理实际问题时,常会遇到数组的大小要在程序执行中才能确定的情况,合理的方法是在程序中再决定数组的大小。C语言提供了动态存储分配函数,可在程序运行过程中,动态分配内存区供数据存储,可根据需要指定大小,以及不需时予以释放。下面介绍动态存储分配函数中的malloc()和free(),在stdlib.h和alloc.h头文件中均含有它们的原型。•内存分配函数malloc()原型:void*malloc(unsignedsize);功能:内存中分配一块连续的size个字节的未初始化的存储区。返回一指向该区首址的void*类型(称通用型或无类型)指针,须按实际需要的数据类型进行强制转换,才能赋