《第7章指针.ppt》由会员分享,可在线阅读,更多相关《第7章指针.ppt(94页珍藏版)》请在三一办公上搜索。
1、第7章 指针,C语言大学实用教程,西南财经大学经济信息工程学院刘家芬,数据在内存中的存储,计算机的内存区由连续的内存单元构成,每个内存单元的大小是一个字节,每一个字节有一个编号,也就是常说的地址。每一个变量在内存中都占据一定内存空间,空间的大小由变量的类型确定。整型变量在内存中占用2个字节,float型变量在内存中占用4个字节。在程序中一般是通过变量名来对内存单元进行存取操作的。而实际上,编译过程中所有的变量名都会被转换为变量的地址,计算机对变量的值的存取,都是通过地址来进行的。,假设程序中定义了2个整型变量i和j,编译器在编译的时候,为变量i分配地址编号为2000和2001的两个字节;而为变
2、量j分配2002、2003的两个字节。如果有语句 j=i+j,计算机在执行过程中将首先从地址2000、2001中取出其中的值(变量i的值),然后从地址2002、2003中再取出其中的值(变量j的值),然后将这两个值进行相加,将相加的结果保存在内存地址2002、2003中。这种通过变量地址直接存取变量值的方式,称为直接访问方式。,除了直接访问方式外,还可以采用间接访问方式,来对一个变量的值进行存取:将变量i的地址2000,存放在另一个变量i_pointer当中,访问的时候,通过取出i_pointer的值,找到变量i的地址2000,然后再从2000、2001中,取出变量i的值。,2000,i_po
3、inter,i,2000,这种间接访问,从变量i_pointer开始,最终也能访问到变量i的值。,由于通过地址能找到所需的变量,因此可以说地址指向该变量单元。在C语言中,将地址形象化地称作“指针”。,变量的值保存在内存单元中,该内存单元可以通过地址来标识。变量的地址即存放数据的内存地址,就是该变量的指针。指针可以保存在变量中,这种专门用来保存指针的变量,称为指针变量。指针变量i_pointer中保存了变量i的地址2000。2000指向变量i,我们也可以说指针变量i_pointer指向变量i。,指针和地址的概念,变量的指针和指向变量的指针变量,变量的指针就是变量的地址,而指针变量就是指存放地址型
4、数据的变量。为了表示指针变量和所指向的变量之间的联系,C语言中用*表示“指向”关系。右图中(*i_pointer)就是指针变 量i_pointer所指向的变量,即i。指针变量的定义 格式:基类型*指针变量名;基类型是该指针变量指向的变量的类型。例如:float*p;(p是指向浮点型变量的指针变量)int*q;(q是指向整型变量的指针变量),2000,i_pointer,3,i,2000,*i_pointer,注意:指针变量名前面的*表明该变量为指针变量,指针变量的名字是p、q,而不是*p、*q。在定义指针变量时必须指定基类型。基类型规定了指针类型变量所指向的变量的类型,一个指针变量只能指向同一
5、类型的变量。例如:float*p;这里定义的指针类型变量p只能指向float型的变量,也就是说,它只能保存一个float型变量的地址,而不能保存其他类型变量(比如int型的变量)的地址。以下是错误的:int i;float*p=,定义了指针变量后,如何使其指向某个变量?可以通过赋值语句使指针变量得到另一个变量的地址,从而使它指向另一个变量:int i=6;int*p;p=(这个赋值语句将整型变量i的地址赋给了指针变量p,从而使p指向了变量i),p,&i,i,6,指针变量的引用,指针变量只能存放地址,不能将一个整数(或其他非地址类型的数据)赋给一个指针变量。例如下面是错误的:int*p=100;
6、两个有关的运算符:(1),#include void main()int a,b;int*p1,*p2;a=100;b=10;p1=,例7.1,说明:在开始定义了两个指针变量p1和p2,但是定义的时候,并没有给它们赋初值,这个时候,p1和p2并未指向任何变量。p1=这里*p1和*p2分别代表p1和p2所指向的变量的内容。,有如下程序段:int a,*p;p=问:(1)&*p的含义是什么?分析:&和*这两个运算符的优先级相同,按照右结合,先进行*p的运算,它就是变量a,然后再执行&运算,即&a运算,得到变量a的地址。(2)*&a的含义是什么?分析:和上面类似,&a运算是取变量a的地址,然后执行*
7、运算,即&a所指向的变量,即是变量a。,输入a和b两个整数,按照先大后小的顺序输出a和b。#include void main()int*p1,*p2,*p,a,b;scanf(%d,%d,例7.2,这个程序的算法思想是:使用p1和p2分别指向a和b。当a小于b的时候,交换p1和p2的内容,使得p1指向b,p2指向a。输出时,先输出p1指向的那个变量,后输出p2指向的那个变量。,p1,&a,a,5,&b,b,10,p2,p1,&b,a,5,&a,b,10,p2,(1)开始时,(2)输出时,指针变量作为函数参数,指针变量也可以作为函数参数来传递信息例7.3 输入两个整数,按照先大后小的顺序输出。
8、,#include void main()void swap(int*p1,int*p2);int a,b,*point_1,*point_2;scanf(%d,%d,分析程序:1.在main函数中,swap的实参是point_1,point_2。这两个指针变量分别保存了a和b的地址。2.在swap被调用时,形参p1和p2分别被赋予实参point_1和point_2的值,因此,p1指向变量a,p2指向变量b3.swap函数的执行,使p1指向的变量(a)的内容和p2指向的变量(b)的内容进行了交换。4.在main中按先a后b的顺序打印结果。这个时候,a中保存的值,大于b中保存的值的。,p1,&a
9、,a,5,&a,point_1,p2,&b,b,9,&b,point_2,p1,&a,a,9,&a,point_1,p2,&b,b,5,&b,point_2,swap开始时,swap结束时,能否使用普通变量作函数参数,即void swap(int x,int y)int temp;temp=x;x=y;y=temp;并在main函数中调用swap:swap(a,b);,思考,答案:不行。因为a和b的值在传递给swap的形参x和y后,不会改变(我们称为值传递):若要使被调函数对某个变量的值的改变,在主调函数中可见,那么可以使用指针变量作函数参数。调用时,传递变量的地址给被调函数。,a,5,b,9
10、,x,5,y,9,swap开始时,a,5,b,9,x,9,y,5,swap即将结束时,另一个解决方案,是否可行?#include void main()void swap(int*p1,int*p2);int a,b,*point_1,*point_2;scanf(%d,%d,编程者意图通过swap,交换main函数中的point_1和point_2的内容。但是这是不能实现的,因为swap只能交换形参p1和p2的内容,而不能把这种影响反映到main中的point_1和point_2中去。,再一个解决方案,是否可行?#include void main()void swap(int*p1,int
11、*p2);int a,b,*point_1,*point_2;scanf(%d,%d,*p是指针变量p所指向的变量,而p指向哪里?如果对*p赋值,可能造成系统崩溃!,数组和指针,数组也要占用内存。数组占用一个连续的内存区域,数组中元素在内存中的存储位置是相邻的。例如:int a6;假定数组a的首地址为2000:,a0,a1,a2,a3,a4,a5,2000,2002,2004,2006,2008,2010,a,指向数组元素的指针,数组元素相当于变量,所以指向数组元素的指针,和指向变量的指针相同:int a10;int*p;p=,通过指针引用数组元素,程序段:int a10;int*p=的作用是
12、?将数组a中最后一个元素a9赋值为100,C语言规定:如果指针变量p已经指向数组中的一个元素,则p+1指向同一个数组中的下一个元素。例如:int a10;int*p=)这里p+1不是简单的对地址数值进行加1。假设数组a的首地址为2000,p初始值为2000,p+1的值为2002,因为int型的变量在内存中占两个字节,p+1为2002,指向元素a1若p指向数组中第一个元素a0,p+i和a+i就是ai的地址,指向数组元素ai;*(p+i)和*(a+i)就代表数组元素ai。,注意:指向数组的指针变量也可以带下标,如pi与*(p+i)等价数组名也是一个指针变量,它指向数组的首地址。因此:int a10
13、;*(a+5)=100;等价于a5=100;,(1)使用下标法访问#include void main()int a10;int i;for(i=0;i10;i+)scanf(%d,例7.4 输出数组中全部元素,(2)使用数组名计算数组元素地址,找出元素的值#include void main()int a10;int i;for(i=0;i10;i+)scanf(%d,(3)使用指针变量指向数组元素#include void main()int a10;int i,*p;for(i=0;i10;i+)scanf(%d,注意:p作为指向数组元素的指针变量,可以通过改变指针变量的值指向不同的元素
14、。可以进行p+操作,即p的值可以更改;数组名a虽然也作为指针,但是a的值不能修改,因此不能进行a+操作。,注意:1.int a10;int*p=a;则p+10指向的内存地址,已经超出了数组a的范围。如果程序中出现*(p+10)=100;已经超出了数组a的边界。因为不确定(p+10)指向的内存区域用于何种目的,所以这样赋值可能会使系统崩溃。2.比较*(p+)*(+p)+(*p),*p,然后p指向数组下一元素,p+使p指向数组下一元素,读取当前*p,使p指向的内存单元中的值加1,指针变量1-指针变量2结果为:两个指针指向的数组元素间的距离例:指向同一数组的指针变量相减float x10,*p1=x
15、+2,*p2=p1-p2 的值是-p2-p1 的值是说明两个元素之间的距离是6,指向同一数组的指针变量之间的减法运算,用数组名作函数参数,第六章中已经讲过:用数组名作函数参数时,实际上传递的只是实参数组的首地址;在内存中只分配了一个数组空间,而不是两个。被调函数中对形参数组的修改,在主调函数中可见。在掌握了指针的概念之后,我们再来深入分析一下用数组名作函数参数。,void f(int arr,int n)实参数组名array代表该数组的首地址,形参用来接收从实参传递过来的首地址,因此形参应该是一个指针变量,因为只有指针变量能存放地址。使用数组名作函数参数时,形参int arr 表示arr是数组
16、名,C编译器会将形参数组名作为指针变量来处理。因此void f(int arr,int n)和void f(int*arr,int n)是等价的,编译器会建立一个指针变量arr,用来存放从主调函数中传过来的实参数组的首地址。,void main()int array10;f(array,10);,实参数组名为array,当调函数f时,形参arr接受array数组的首地址,因此arr也指向array数组。根据前面讲的指向数组元素的指针所具有的性质,我们可以使用几种等价的方式来访问数组array中的元素。例如array1还可以用以下两种方式来表示:arr1、*(arr+1)。,arr,array,
17、arr+1,数组名作函数参数时,C编译器将形参数组名作为指针变量来处理。传递给这个形参的值是实参数组的首地址。注意:1.因为形参数组名被编译成指针变量,所以可以改变它的值。例如 arr+;是允许的。2.而实参数组名array虽然也指向数组中第一个元素,但是array的值不能被修改,它是一个指针常量。例如 array+;是错误的。,例7.5 将数组a中的n个整数按相反顺序存放#include void main()void inv(int x,int n);int i,a10=3,7,9,1,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10
18、;i+)printf(%d,ai);printf(n);inv(a,10);printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,void inv(int x,int n)int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+)j=n-1-i;temp=xi;xi=xj;xj=temp;,i,m,j,算法:将a0与an-1互换,再将a1与an-2互换直到中间元素。用for循环处理,i和j分别指示需要对换的两个元素位置,初值为0和n-1,然后i+并且j-,直到i=(n-1)/
19、2。第一个形参x实际上是一个指针变量,x指向数组a的首元素。通过xi指向的实际上就是ai。第二个形参n用来接收需要进行处理的元素的个数。可以通过inv(a,5)表示对数组前5个元素进行颠倒。,注意:算法并不是唯一的!,对程序进行修改,将inv函数中的参数x改成指针:#include void main()void inv(int*x,int n);int i,a10=1,3,5,7,9,11,13,15,17,19;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);inv(a,10);printf(The a
20、rray has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,void inv(int*x,int n)int*p,temp,*i,*j,m=(n-1)/2;i=x;j=x+n-1;p=x+m;for(;i=p;i+,j-)temp=*i;*i=*j;*j=temp;这个函数里采用了指向整型变量的指针变量i和j,分别指向将要交换的两个数组元素。,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a数组,i,x,p=x+m,j,归纳起来,如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参和形参可分别为:形参
21、和实参都用数组名实参用数组名,形参用指针变量实参形参都用指针变量实参用指针变量,形参用数组名,#include void main()void inv(int*x,int n);int i,*a;printf(The original array:n);for(i=0;i10;i+)scanf(%d,a+i);printf(n);inv(a,10);printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,*(a+i);printf(n);,这个main()有问题吗?,#include void main()int a=5,*
22、p1=这条语句,对这个未知内存空间的值进行改变,这是很危险的。如果这个地址存放的是系统关键程序内容,可能会使系统崩溃。总结:在指针变量的值未被初始化时,没有指向某个变量,不要使用该指针变量!,例7.6选择法排序,void sort(int x,int n)int i,j,k,t;for(i=0;ixi)k=j;if(k!=i)t=ki;xi=xk;xk=t;,多维数组与指针,回顾一维数组与指针小节所讲的内容:1.数组名是个指针常量,它指向数组中的首元素。2.若数组名为a,a+i指向数组中第i+1个元素ai;对数组中第i个元素的访问,可以使用ai,也可以使用*(a+i)。,多维数组元素的地址,C
23、语言把二维数组,作为一维数组来处理;这个一维数组的每个元素,又是一个一维数组。例如int a34=1,3,5,7,9,11,13,15,17,19,21,23数组a是一个二维数组。C语言把a被看作一个一维数组,它包含3个元素a0,a1,a2。而每一个元素,又是一个一维数组。例如a0是一维数组名,a0这个一维数组包括了a00,a01,a02,a03这4个元素。,a0,a1,a2,1,3,5,7,9,11,13,15,17,19,21,23,=,=,=,a(2000),按照一维数组的知识:数组名是个指针常量,它指向数组中的第一个元素。可以得出:二维数组名a是个指针常量,它指向数组中第一个元素a0(
24、a0同时也是一个一维数组1,3,5,7)。同时,我们也可以得出:a=2000的话,a+1=2008,因为a+1指向a数组中第二个元素a1(每一行是一个一维数组,包含4个整型数,占用8字节)因为a+1指向a数组中的第二个元素a1,因此可以用*(a+1)来表示第二个元素a1(a1是一维数组9,11,13,15),a0,a1,a2,1,3,5,7,9,11,13,15,17,19,21,23,=,=,=,a(2000),a0,a1,a2是3个一维数组。因此,a0作为一维数组的名字,代表数组1,3,5,7的首地址,即&a00。同样,a1中的值是&a10,a2中的值是&a20。考虑0行1列的元素地址如何
25、表示?答:a0+1。也可以用&a01来表示。a0的值为2000,a0+1的值为2002。a0可以用*(a+0)来表示因此0行1列的元素的地址a0+1也可以用*(a+0)+1来表示 i行j列的元素的地址为ai+j或*(a+i)+j那么i行j列的元素的值,可以用*(*(a+i)+j)来表示。,在分析二维数组的时候,首先把二维数组看成一个一维数组,然后按照一维数组的方法来处理。二维数组作为一维数组处理,其中的每一个元素,又是一个一维数组。对这些一维数组,也使用一维数组的方法来处理。,指向多维数组元素的指针变量,(1)指向数组元素的指针变量例7.7 用指针变量输出二维数组元素的值include voi
26、d main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int*p;for(p=a0;pa0+12;p+)if(p-a0)%4=0)printf(n);printf(%4d,*p);printf(n);,分析:因为p是指向int型变量的指针变量,每次p+操作,p指向下一个数组元素。二维数组元素按行依次存放,先存第一行,然后存第二行,再存第三行.,9,11,13,15,17,19,21,23,1,3,5,7,p,p+1,注意:aij的地址是&a00+i*m+j。其中m是二维数组的第二维的大小,m=4,i=1,j=2,a12是a00后面第1*4+2个元素,指向
27、一维数组的指针变量,分析下面几个定义的含义:int*p;(定义一个指针变量p,p指向一个整型变量)int a10;(定义一个长度为10的整型数组a。a是数组名,因此可以看作指针常量,a指向数组的首元素)int(*q)10;(定义一个指针变量q,它指向一个长度为10的整型数组)以上定义的p和a,它们指向的内容是一个整型元素,占2个字节。但指针q,它指向的是一个数组,占2*10个字节。,例7.8 输出二维数组中任意一个元素include void main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int(*p)4,i,j;/*定义p为指针变量,指向包含4个元
28、素的整型数组*/p=a;for(i=0;i3;i+)for(j=0;j4;j+)printf(%2d,*(*(p+i)+j);printf(n);,说明:“int(*p)4;”表示p是一个指针变量,它指向包含4个整型元素的一维数组。这里如果不加括号int*p4,表示定义一个指针数组(后面会讲到),因此括号不能省略。a是二维数组名,因此a指向数组中的第一个元素a0。a0是一个包含4个整型元素的一维数组。因此,p=a这个赋值是合理的。,3.p+i是二维数组a的i行的起始地址,即元素ai的地址。而*(p+i)表示的是二维数组的第i个元素ai,它即是包含4个整型元素的普通一维数组名。因此对应的第j个元
29、素的地址可以用*(p+i)+j来表示,这个地址中存放的值就用*(*(p+i)+j)表示。,1,3,5,7,9,11,13,15,17,19,21,23,p,p+2,a0,a1,a2,用指向数组的指针作函数参数,一维数组名可以作函数参数,二维数组名也可以作函数参数。二维数组名,就是一个指向一维数组的指针。例7.9 有一个班,3个学生,学习4门课程,计算总的平均分,以及第n个学生的成绩。,#include void main()void average(float*p,int n);void search(float(*p)4,int n);float score34=65,67,70,60,80
30、,87,90,81,90,99,100,98;average(*score,12);/*12个成绩的平均值*/search(score,2);/*求序号为2的学生成绩*/void average(float*p,int n)/*指向数组元素的指针p*/float*p_end;float sum=0,aver;p_end=p+n-1;for(;p=p_end;p+)sum=sum+(*p);aver=sum/n;printf(average=%5.2f,aver);,void search(float(*p)4,int n)/*指向长度为4的一维数组的指针p*/int i;printf(the
31、score of No.%d are:n,n);for(i=0;i4;i+)printf(%5.2f,*(*(p+n)+i);,字符串与指针,在C语言中,可以用两种方法访问一个字符串:1.用字符数组存放一个字符串,然后输出该字符串。例如:void main()char string=I love china!;printf(%sn,string);,2.用字符指针指向一个字符串。例如:void main()char*string=I love China!;printf(%sn,string);C编译器首先在内存中分配一个字符数组空间来保存字符串常量“I love China!”,然后,把这个
32、字符串的第一个字符的地址,赋给指针变量string;不要误认为是把整个字符串I love China!赋给了变量string。对一个字符串,可以通过首地址,使用%s来输出。,例7.10 将字符串a复制到字符串bvoid main()char a=I am a boy.,b20;int i;for(i=0;*(a+i)!=0;i+)*(b+i)=*(a+i);*(b+i)=0;printf(string a is:%sn,a);printf(string b is:);for(i=0;bi!=0;i+)printf(%c,bi);printf(n);,程序中,a和b都是数组名,因此*(a+i)表
33、示的就是ai,*(b+i)表示的是bi。输出一个字符串,可以使用%s输出,还可以通过%c依次输出每个字符。,字符指针作函数参数,例7.11用函数调用实现字符串的复制(1)用字符数组作函数参数void main()void copy(char from,char to);char a=I am a teacher;char b=You are a student;copy(a,b);printf(string b=%sn,b);,void copy(char from,char to)int i=0;while(fromi!=0)toi=fromi;i+;toi=0;,(2)使用字符指针变量作形
34、参void main()void copy(char*from,char*to);char a=I am a teacher;char b=You are a student;char*x=a,*y=b;copy(x,y);/*使用copy(a,b);也可以*/printf(string b=%sn,b);void copy(char*from,char*to)for(;*from!=0;form+,to+)*to=*from;*to=0;,对字符指针变量和字符数组的说明1.字符数组由若干个元素组成,每个元素存放一个字符。而字符指针变量用来存放一个地址。2.字符指针变量可以通过赋值进行字符串的
35、复制,但字符数组不行,只能通过字符串拷贝函数来复制。char*p;char str14,a=I am a boy;p=a;(正确)str=a;(错误),3.对字符指针变量赋初值:char*p=I am a boy;是将字符串“I am a boy”第一个元素的地址赋给p。4.指针变量的值是可以改变的。例void main()char*a=I love China;a=a+7;printf(%sn,a);输出结果:China,指向函数的指针,函数也要占用内存。一个函数在编译之后,会生成二进制代码,并且编译器会为这个函数分配一个地址空间,这个地址空间的起始地址(入口地址),就称为函数的指针。可以用
36、一个指针变量指向函数,然后通过这个指针变量来调用此函数。,回顾第一章的例子:#include void main()int max(int x,int y);int a,b,c;scanf(%d,%d,printf(max=%d,c),int max(int x,int y)int z;if(xy)z=x;else z=y;return(z);,改写这个例子中的主函数:#include void main()int max(int x,int y);int(*p)(int,int);/*定义指向函数的指针变量p*/p=max;/*max是函数名,指向函数入口地址*/int a,b,c;scan
37、f(%d,%d,/*通过指针p来调用max*/printf(max=%d,c),分析:1.int(*p)(int,int);这条语句是定义一个指向函数的指针变量p,该函数具有两个整型参数,并且函数返回值为整型。定义中(*p)的括号不能省略,如果省去括号 int*p(int,int);代表声明一个函数p,它的返回值是一个指向整型变量的指针。,2.与数组名指向数组首元素类似,函数名max也是一个地址常量,它保存该函数的起始地址。p=max;这条赋值语句 使p指向函数max。3.要调用max函数,可以直接使用函数名:max(a,b);也可以通过指针p(*p)(a,b);因此,程序里有语句:c=(*p
38、)(a,b);,指令1,指令2,maxp,指向函数的指针变量的定义的一般形式为:数据类型(*指针变量名)(函数参数表列)例如:int(*p)(int,int);指向函数的指针p,不能使用p+使p指向下一条指令。p+n或者p-n是无意义的。,指向函数的指针变量作函数参数,例7.12 设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次调用process时找出a和b中大的那个数,第二次找出其中小者,第三次求a与b之和。,#include void main()int max(int,int);int min(int,int);int add(int,int);vo
39、id process(int,int,int(*fun)(int,int);int a,b;scanf(%d,%d,void process(int x,int y,int(*fun)(int,int)int result;result=(*fun)(x,y);printf(%dn,result);,说明:在这个程序中,max,min,add分别是三个函数名,分别指向三个函数的起始位置。main函数中3次调用了process,每一次的执行都不相同。因为process使用了指向函数的指针作参数,3次执行,分别调用了max,min和add函数。这种程序设计方法增加了设计的灵活性。在UNIX操作系统
40、中,内核和驱动程序接口部分的代码,就大量使用了指向函数的指针变量作函数参数。,返回指针值的函数,一个函数可以返回整型值,字符型值,浮点型值,还可以返回指针型值。返回指针值的函数的一般形式:类型名*函数名(参数表列);例如int*a(int x,int y);a是函数名,调用它之后,能得到一个指向整型数据的指针。x和y是函数a的形参。注意:*a的两侧没有加括号,如果加了括号 int(*a)(int x,int y);就是定义了一个指向函数的指针变量a。,例7.13 有若干名学生的成绩(每个学生4门课程),要求在用户输入学生序号之后,输出该学生的全部成绩。#include void main()f
41、loat score4=60,70,80,90,56,89,67,88,34,78,90,66;float*search(float(*pointer)4,int n);float*p;int i,m;printf(“Enter the number of student:);scanf(,score是一个二维数组,score指向数组的第一行,a.形参pointer是一个指向一维浮点数组的指针b.函数的返回值,是一个指向float型变量的指针。,实参score和形参pointer都是指向float型一维数组的指针,score传值给pointer。,float*search(float(*poi
42、nter)4,int n)float*pt;pt=*(pointer+n);return(pt);,pt为指向浮点型变量的指针,pt=*(pointer+n);pointer接受来自实参score的值,指向二维数组的第0行,因此pointer+n指向二维数组的第n行。*(pointer+n)即为第n行构成的一维数组的首地址。将*(pointer+n)赋值给pt,使得pt指向这个一维数组的首元素。然后由return语句返回指针pt的值。,调用search函数返回的指针,即指向第n的学生的第一门成绩(这是第n行一维数组的第一个元素)。因此,可以使用循环语句打印*(p+i),即为各门成绩,指针数组和
43、指向指针的指针,指针数组 本质是一个数组,其中每个元素均为指针类型,称为指针数组。指针数组的一般定义形式:类型名*数组名数组长度;例如:int*p4;定义了一个一维数组p,该数组长度为4,每个数组元素都为指向整型变量的指针。,指针数组适合用来指向若干个字符串,使字符串处理更加灵活。考虑:图书馆有若干本书,书名为字符串,因此考虑用字符数组来存放书名。因为每个书名都是一个字符串,那么很多本书,就需要二维数组来存放。从图中可以看出来,二维数组存放,比较浪费内存空间,且需要一个很大的连续内存空间才能存放。,F,o,l,l,o,w,m,e,0,B,A,S,I,C,G,r,e,a,t,W,a,l,l,0,
44、F,O,R,T,R,A,N,0,C,o,m,p,u,t,e,r,d,e,s,i,g,n,0,0,指针数组的用途,如果使用指针数组,各元素分别指向各书名构成的字符串。这些字符串可单独存放,比用二维数组存放更节省空间。如果想对字符串排序也不用改变字符串的位置,直接对指针数组中的元素值进行修改,指向不同的字符串即可。,Follow me,BASIC,Great Wall,FORTRAN,Computer design,name0,name1,name2,name3,name4,指针数组name,字符串,例7.14 将若干字符串按字母顺序输出#include#include void main()vo
45、id sort(char*name,int n);void print(char*name,int n);char*name=Follow me,BASIC“,Great Wall,FORTRAN,Computer design;int n=5;sort(name,n);print(name,n);,定义了一个指针数组name,它有5个元素,初值分别是Follow me,BASIC“,Great Wall,FORTRAN,Computer design这5个字符串的起始地址。,void sort(char*name,int n)char*temp;int i,j,k;for(i=0;i0)k=
46、j;if(k!=i)temp=namei;namei=namek;namek=temp;,void print(char*name,int n)int i;for(i=0;in;i+)printf(%sn,namei);,sort函数用于对字符串进行排序。sort函数的形参与实参名字相同name,是指针数组名。数组名作为函数参数时,形参name数组和实参name数组指向同一个数组。,sort函数使用strcmp对字符串进行比较。strcmp的两个实参namek和namej分别指向第k个和第j个字符串。strcmp的返回值表明了两个字符串的大小。,sort函数中交换的是name数组中的元素,其实
47、就是使name数组中的指针元素,按从小到大的次序依次指向5个字符串。5个字符串在内存中始终未改变其存储地址。,指向指针的指针,回顾指针数组,指针数组名name,是个指针常量,它指向数组中第一个元素name0。而元素name0 又是一个指针,指向字符串“Follow me。因此,name就是指向指针的指针。可以定义一个指针变量p,使它指向指针数组的元素,那么p就是指向指针的指针变量。,如何定义指向指针的指针变量?int*p;相当于int*(*p);先把(*p)看成一个整体,因此可以知道(*p)是一个指向整型量的指针变量。再对(*p)进行分解,p是指向指针变量的指针变量。如果有:p=name+2;
48、printf(%o,*p);printf(%s,*p);第一个打印语句输出name2的值,即字符串“Great Wall”的地址;第二个打印语句输出Great Wall。,例7.15 使用指向指针的指针#include void main()char*name=Follow me,BASIC,Great Wall,FORTRAN,Computer design;char*p;int i;for(i=0;i5;i+)p=name+i;printf(%sn,*p);,定义了一个指针数组name并进行初始化,使数组中每一个元素都指向一个字符串。name作为数组名,指向首元素name0。,定义了一个指
49、向指针的指针变量p,p和name都是指向指针的指针,处于等同的地位。用name对p赋值:p=name+i;这个赋值语句使p指向数组元素namei。引用*p等同于引用namei。,printf语句通过%s输出namei,指针相关的数据类型总结,一维数组与指针,int a10;int*p;p=p为指向数组元素的指针p=&a0等价于p=a通过ai或者*(p+i)来访问数组元素,二维数组与指针,int a34=1,3,5,7,9,11,13,15,17,19,21,23;a是一个二维数组,包含3个元素a0,a1,a2。a作为数组名,指向其第一个元素a0,a=p是一个一维数组的名字,该数组的元素为指向整
50、型量的指针,指针与函数,int(*p)(int,int);指针p为指向函数的指针,该函数具有两个整型参数,并且返回值为整型。赋值语句为p=max;函数调用语句max(a,b);相当于(*p)(a,b);int*p(int,int);定义一个函数p,该函数的返回值为指向整型量的指针。,指针相关的运算,指针变量加(减)一个整数:例如:p+、p-、p+i、p-i、p+=i、p-=i指针变量加1(减)是将该指针变量的值(为地址)和它指向的变量所占用的内存单元字节数进行加(减)。两个指针变量可以相减:如果两个指针变量指向同一个数组的元素,则两个指针变量值之差是两个指针之间的元素个数。,指针相关的运算,指