第9章指针本章要点指针的定义、引用和运算方法数组、函数和字符串与指针的关系本章难点指针变量的引用指针作为函数参数的运用预备知识内存:就是内部存储器,是由存储单元组成的。它的特点是存储单元是线性连续的。存储单元的最小单位是字节。1.内存的概念地址:为了访问内存中的某个存储单元,我们要为它编号,这种编号称为内存地址。通过地址我们就能够访问该地址所标识的存储单元。2.地址的概念变量的地址:变量在内存中总占用几个连续的字节,开始字节的地址,就是变量的地址。2007存储单元9.1指针的概述指针:一个变量的地址称为该变量的指针。指针变量:若一个变量专用于存放另一个变量的地址(指针),则该变量称为指针变量。指针的对象:当把变量的地址存入指针变量后,我们就可以说这个指针指向了该变量。变量的存取方法:直接存取和间接存取。直接存取:直接根据变量名存取数据。间接存取:通过指针变量存取相应变量的数据。9.2变量的指针和指向变量的指针变量一、指针变量的定义一般形式:类型标识符*变量名;例如:int*ptr1,*ptr2;指针变量的类型:指明了该指针指向的内存空间所存储的数据类型。在定义指针变量时要注意以下几个问题:⑴变量名ptr2前面的“*”不能省略,如果写成int*ptr1,ptr2;则ptr2被定义为整型变量,而非整型指针变量。⑵定义中的“*”表示所定义的变量是指针变量,但指针变量名是ptr1、ptr2,而非*ptr1、*ptr2。⑶指针变量只能指向定义时所规定类型的变量。这个规定的类型称为该指针变量的“基类型”。如:上例中ptr1、ptr2只能指向整型变量,不能指向实型或字符型变量。其“基类型”相同,都是整型。⑷定义指针变量后,并未确定该变量指向何处。也就是说该变量的值是不确定的。在引用指针变量前必须首先让它指向一个变量,这一点非常重要。二、指针变量的运算㈠指针运算符(“&”和“*”)“&”(地址运算符):取变量的存储地址。“*”(引用运算符):是取指针所指向变量的内容。例如:&a是求变量a的地址。ptr指向了i变量,*ptr表示i的值,即3我们还可以用这种方法实现对变量的改变:*ptr=15;等价于i=15;由此可见:通过指针运算符“*”可以引用一个变量。如:当ptr已经指向变量i后,*ptr就等同于i。进一步理解“&”和“*”:“&”运算和“*”运算是一对互逆运算。&*ptr&iptr*&i*ptri等价于㈡指针的赋值运算(=)指针的赋值运算:就是把地址赋值给指针变量。指针的赋值运算可以是以下三种方式:⑴使用取地址运算符,把地址值赋值给指针变量。如:inti,*pi;pi=&i;⑵把指针变量的值赋给另一个指针变量。如:inti,*pa,*pb;pa=&i;pb=pa;⑶给指针变量赋值为符号常量NULL。说明:NULL是一个空指针,表示该指针变量的值没有意义。作用是为了避免对没有被初始化的指针变量的非法引用。NULL的定义在“stdio.h”中。如:int*pi;pi=NULL;说明:⑴在定义指针变量时,可以立即将一个地址值赋给指针变量,这就是指针变量的初始化。指针变量的初始化也是指针的赋值运算。如:floatflt,*f_ptr=&flt;注意:这不是给*f_ptr赋值。⑵指针变量间的赋值和引用应保证基类型相同。若有定义:int*p,i;float*q,x;则:q=&i;╳p=&x;╳㈢移动指针的运算•指针的加减运算(+、-)•指针的自加自减运算(++,--,+=,-=)1.指针的+、-运算指针±整数指针-指针+、-说明:⑴指针与整型值加减的结果是指针,表示使该指针指向该指针下移或上移存储单元个数(整型值)之后的内存地址。存储单元的大小就是该指针的数据类型所需的内存大小。例如:ptr+n(指针ptr,n为整数)这个指针值代表的内存单元的地址是:ptr+n*d(其中d是指针所指向变量的数据类型所占内存字节数),即指针移动了n个元素。⑵指针与指针的加运算毫无意义,所以指针与指针没有加运算。⑶指针与指针的减运算要求相减的两个指针属于同一类型,其结果是整数,表示两个指针之间的数据的个数。其结果值的计算公式是:ptr1-ptr2=(ptr1的值-ptr2的值)/指针的数据类型所占的字节数例如:int*ptr1,*ptr2,*ptr3,x;intary[5]={2,4,8,16,32};ptr1=&ary[0];ptr2=&ary[3];ptr3=ary;x=ptr2-ptr1;x的值是32.指针的++、--、+=、-=运算++、+=:是移动指针到下一个或下几个存储单元。--、-=:是移动指针到上一个或上几个存储单元。例如:int*ptr,ary[5]={2,4,6,8,10};ptr=ary;ptr+=3;ptr--;想一想:*ptr++和(*ptr)++有什么的区别?㈣关系运算基类型相同的两个指针进行比较运算,其意义是两个指针的位置比较,结果是逻辑值。指针运算的程序举例:把a,b按大小顺序输出。main(){int*p1,*p2,*p,a,b;a=45;b=76;p1=&a;p2=&b;if(*p1*p2){p=p1;p1=p2;p2=p;}printf(a=%d,b=%d,max=%d,min=%d,a,b,*p1,*p2);}输出结果:a=45,b=76,max=76,min=45三、指针变量作为函数参数指针可以用作函数参数,这在调用函数希望改变参数的值时非常有用。例如:用指针变量编写实现两个数的交换的函数voidswap(int*p1,int*p2);main(){intx1=100,x2=200;printf(x1=%d,x2=%d\n,x1,x2);swap(&x1,&x2);printf(x1=%d,x2=%d\n,x1,x2);}voidswap(int*p1,int*p2){inttemp;temp=*p1;*p1=*p2;*p2=temp;}图示交换过程中存储单元内容的变化:123456想一想:如果函数的参数不用指针而用整数,能否实现值的交换?为什么?通过函数调用得到n个要改变的值的方法:⑴在主调函数中设n个变量,用n个指针变量指向它们;⑵将指针变量作实参,将这n个变量的地址传给所调用的函数的形参;⑶通过形参指针变量,改变该n个变量的值;⑷主调函数中就可以使用这些改变了值的变量。注意:不能企图通过改变指针形参的值而使指针实参的值改变。如:上例swap函数中不能写成:temp=p1;p1=p2;;p2=temp;9.3数组的指针和指向数组的指针变量一、数组的指针和指向数组的指针变量的概念数组的指针:是数组的起始地址。数组元素的指针:是数组元素的地址。当指针变量指向数组或数组元素时,它就是指向数组的指针变量。C规定:⑴数组名代表数组的首地址(起始地址),也就是第一个元素的地址。⑵当指针变量p指向数组时,p+1指向数组的下一个元素。假设一个整型元素占两个字节,p+1是使p的地址加2个字节。如:inta[10],*p;则:p=a;与p=&a[0];等价称指针变量p指向数组元素a[0]p+i、a+i、&a[i]都是a[i]的地址。二、数组元素的引用1.用下标法引用数组元素如:a[3]=45;b[2][5]=200;2.用指针法引用数组元素假如:inta[10],*p,i;p=a;则:⑴*(p+i)、*(a+i)则代表元素a[i]⑵*(p+i)也可以写成p[i]⑶*(p+i)、*(a+i)、a[i]、p[i]等价,都代表数组a的第i+1个元素。程序举例:输出10个元素数组中的全部元素。方法二:通过数组名计算数组元素地址,找出元素的值。main(){inta[10]={54,65,8,2,3,56,8,21,57,98},i;for(printf(\n),i=0;i10;i++)printf(%4d,*(a+i));}方法一:下标法。main(){inta[10]={54,65,8,2,3,56,8,21,57,98},i;for(printf(\n),i=0;i10;i++)printf(%4d,a[i]);}方法三:用指针变量指向数组元素main(){inta[10]={54,65,8,2,3,56,8,21,57,98},*p,i;p=a;for(printf(\n),i=0;i10;i++)printf(%4d,*p++);}以上三种方法,利用指针变量效率最高。说明:⑴指针变量与数组名的区别:指针变量是地址变量,数组名是地址常量。即指针变量的内容可以在程序运行过程中被改变;而数组名一旦被定义,它的值就不能被改变了。例如:inti,*p,a[6];则:p=&i;√a=&i;a++;a+=i;不能给常量赋值⑵利用指针变量编程时特别要注意指针变量的当前值。例如:通过指针变量输入输出a数组元素。main(){int*p,i,a[10];p=a;for(i=0;i10;i++)scanf(%d,p++);for(printf(“\n”),i=0;i10;i++)printf(“%6d”,*p++);}应插入语句p=a;注意:*p++、*(p++)、(*p)++、*(++p)的含义三、数组名作函数的参数例如:f(intarr[],intn){……}main(){intarray[10];……f(array,10);……}现在解释:实际上,能够接受并存放地址值的只能是指针变量。因此,C编译系统都是将形参数组名作为指针变量来处理的。上例中f(intarr[],intn)等价于f(int*arr,intn)。使用形参数组的概念只是为了与实参数组对应,直观,便于理解而已。例:从10个数中找出其中最大值和最小值。voidmax_min(inta[],intn,int*max,int*min);main(){inti,a[]={2,4,1,6,7,32,45,75,45,90},max,min;for(printf(Theoriginalarray=),i=0;i10;i++)printf(%5d,a[i]);max_min(a,10,&max,&min);printf(max=%dmin=%d,max,min);}voidmax_min(inta[],intn,int*max,int*min){inti;*max=*min=a[0];for(i=0;in;i++){if(*maxa[i])*max=a[i];if(*mina[i])*min=a[i];}}上例中如果形参数组用指针变量,则程序如下:voidmax_min(int*x,intn,int*max,int*min);main(){inti,a[10]={2,4,1,6,7,32,45,75,45,90},max,min;for(printf(Theoriginalarray=),i=0;i10;i++)printf(%5d,a[i]);max_min(a,10,&max,&min);printf(=%dmin=%d,max,min);}voidmax_min(int*x,intn,int*max,int*min){inti;*max=*min=*x;for(i=1;in;i++,x++){if(*max*x)*max=*x;if(*min*x)*min=*x;}}数组名做函数参数小结:如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参都可用数组名或指针变量其对应关系有以下4种情况:⑴实参与形参都用数组名;⑵实参用数组名,形参用指针变量;⑶实参、形参都用指针变量;⑷实参为指针变量,形参用数组名。注意:用指针变量作实参时一定要有确定的值。例:实参、形参都用指针变量的形式main(){inta[10],*p;p=a;……f(p,10);……}f(int*x,intn){……}例:实参为指针变量,形参用数组名。main(){inta[10],*p;p=a;……f(p,10);……}f(intx[],intn){……}四、二维数组的指针1.二维数