资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第,*,页,C语言程序设计,第9章 指针,第章 指针,本章概述,本章的学习目标,主要内容,1,本章概述,指针是,C,语言的特色之一,灵活运用指针可使程序更加高效、简洁。,指针就是内存地址,不同于通过变量名引用内存单元的直接访问方式,使用指针可实现对内存单元的间接访问。,本章介绍指针的有关概念,介绍指针与数组的关系,介绍与函数的关系。,2,本章的学习目标,本章教学目的:理解指针的概念和内存访问的关系,掌握指针的定义、引用。熟练使用指针访问基本类型的数据。理解指针与数组的关系,运用指针实现对数组的访问,包括对一维数组和二维数组处理。理解指针与字符串(字符数组)的关系,运用指针操作字符串。理解指针数组的概念,能运用指针数组处理相关的数据类型,尤其是运用字符指针数组处理多个字符串。理解指针与函数的关系,运用指针实现函数调用。理解带参数的,main,函数作用及各个参数的含义。,本章教学重点:指针的概念,指针与数组的关系。,本章教学难点:指针数组,指针与函数,多重指针。,3,主要内容,9.1 指针的基本概念,9.2 指针与一维数组,9.3 指针与字符串,9.4 指针与二维数组,9.5 指针数组与多级指针的概念,9.6 指针与函数,9.7 命令行参数,9.8 程序设计举例,4,9.1,指针的基本概念,内存是计算机的重要组成部分,在程序的执行过程中,所用到的数据都存于内存中。,内存单元的基本单位是字节,为方便对内存的访问,内存单元的每个字节都有一个编号,,这个编号就是内存的地址。,C程序中的每一个变量,在内存中都占用一定数量的内存单元。给变量赋值就是将数据存入对应的内存单元,使用变量时是按照变量所占用的内存单元的地址,从该地址所对应的内存单元中取出变量的值。,因为我们是通过变量的地址来找到存储变量值的内存单元,从而取得了变量的值,,所以将变量的地址又称为变量的指针。,5,9.1,指针的基本概念,如,右,图所示,整型变量,i占2000、2001两个字节的内存,整型变量j占2002、2003两个字节的内存,其内存单元中存放的是整型数据。而变量p占3000、3001两个字节的内存,其内存单元中存放的是变量i的地址2000(一个变量占多个字节的内存单元时,以首地址表示该变量的地址)。这种存放另一个变量的地址的变量称为指针变量。称变量p指向变量i,p中存储的是变量i的指针。,对一个变量的访问(访问是指取出其值或向它赋值)方式有两种:,(1),直接访问,:,通过变量名访问,如通过变量名,i直接访问i。,(2),间接访问,:,通过指向该变量的指针变量来访问,如通过,p访问变量i。,6,指针变量的定义,指针变量是一类特殊的变量,其值是一个变量的地址。既然是变量,就需要在使用之前先对其定义,然后才可以使用。,指针变量定义的一般形式:,类型标识符 *标识符,;,其中,,类型标识符,用于指出所定义指针变量可以指向的变量类型,称为指针变量的“,基类型,”,,标识符,是指针变量名,,“*”,表示定义指针变量。,7,指针变量的定义,例如:,int *p1;,/*定义指针变量p1,指向整型变量*/,float *p2;,/*定义指针变量p2,指向实型变量*/,char *p3;,/*定义指针变量p3,指向字符型变量*/,指针变量定义的同时也可以对其初始化,例如:,int,i;,int,*p=,这样定义的指针变量,p,,就指向整型变量,i,。,8,指针变量的定义,定义指针变量时应该,注意,:,(1),标识符前的“*”只是一个符号,表示其后的变量是一个指针变量。,(2),指针变量的基类型必须与所指向的变量类型一致,否则会出错。,例如:,float x;,int,*p1=,是错误的。,因为,p1,的基类型为整型,而变量,x,是单精度类型,数据类型不一致。,9,指针变量的引用,(1)&,:取地址运算符,,用于取变量的地址。,例如:,int,i,*p1;,p1=,取,i,的地址赋给指针变量,p1,。,(2)*,:指针运算符,,用于访问指针变量所指向的变量。,例如:,int,i,*p1;,p1=,*,p1=100;,则,*,p1,与,i,等价,访问,i,时,可以用间接方式,用*,p1,代表,i,,语句“*,p1=100;”,与“,i=100;”,相同,为,i,赋值,100,。,10,指针变量的引用,若有:,int i=100;,int*p1;,p1=,则指针变量p1指向变量i,对变量i有如下两种访问方式:,(1)直接访问。,如 printf(“%d”,i);,(2)通过指针变量间接访问。,如:printf(“%d”,*p1);,11,9.1.2指针变量的引用,#include,int main(),int i,j,*p;,p=/*p指向i*/,i=10;,printf(“%d,%dn”,i,*p);,*p=100;,printf(“%d,%dn”,i,*p);,p=,*p=200;,printf(“%d,%dn”,j,*p);,return 0;,程序运行结果为:,10,10,100,100,200,200,程序说明:,(1)将变量i的地址赋给了指针变量p之后,*p与i等价。,(2)语句“*p=100;”的作用是将100存入由p所指向的变量中,即存入整型变量i中。,例9.1:指针变量的引用,12,9.1.2指针变量的引用,#include,int main(),int num1=12,*p1;float num2=3.14,*p2;char ch=p,*p3;,p1=,p2=,return 0;,程序的运行结果为:,num1=12,*p1=12,num2=3.14,*p2=3.14,ch=p,*p3=p,例9.2 写出以下程序的运行结果。,程序说明:从程序的运行结果可以看出num1和*p1、num2和*p2、ch1和*p3是等价的。,13,9.1.2指针变量的引用,#include,int main(),int num1,num2;,int*p1=,printf(“Input the first number:”);,scanf(“%d”,p1);,printf(“Input the second number:”);,scanf(“%d”,p2);,printf(“num1=%d,num2=%dn”,num1,num2);,if(*p1 *p2),p=p1;p1=p2;p2=p;,printf(“min=%d,max=%dn”,*p1,*p2);,return 0;,程序运行结果:,Input the first number:9,Input the second number:6,num1=9,num2=6,min=6,max=9,例9.3 使用指针变量求解:输入2个整数,按从小到大顺序输出。,14,程序说明:,(1)程序中的if语句,若*p1*p2(即num1num2),则交换指针,使p1指向变量num2(较小值),p2指向变量num1(较大值)。,(2)“printf(“min=%d,max=%dn”,*p1,*p2);”语句,通过指针变量,间接访问变量的值。,本例的处理思路是:交换指针变量p1和p2的值,而不是交换变量num1和num2的值(变量num1和num2并未交换,仍保持原值),最后通过指针变量输出处理结果。,例9.3 使用指针变量求解:输入2个整数,按从小到大顺序输出。,15,9.2 指针与一维数组,数组和指针有密切的关系,,数组名代表数组的首地址,。,当一个指针指向数组后,对数组元素的访问,既可以使用数组下标,也可以使用指针。,用指针访问数组元素,程序的效率更高;用下标访问数组元素程序更清晰。,16,9.2.1 指向数组元素的指针,在C语言中,当一个数组a被定义后,数组名a本身就代表了该数组的首地址,并且它是一个地址常量,数组元素的地址可以通过数组名a加下标值来取得。,a即a+0代表数组元素a0的地址,a+1代表数组元素a1的地址,a+2代表数组元素a2的地址,。,若定义一个指针变量,使其指向一个数组,则该指针变量称为指向数组的指针变量,其定义与指向普通变量的指针变量的定义方法是一样的。,例,如:,int a10,*p;,则:,pa;,或者:,p=,上面,两个语句是等价的,都表示把数组,a的首地址赋给指针变量p。,17,指针变量也可以指向数组的其它元素。例如:,int*p1,*p2;,int a10=1,2,3,4,5,6,7,8,9,10;,此后可以赋值:,p1=,p2=,完成了赋值之后,指针p1指向数组a的首元素,p2指向数组元素a4。,对于上面所写&a0的表达形式,由于数组元素访问运算符的优先级更高,所以这里不必写成&(a0)的形式。,9.2.1 指向数组元素的指针,18,9.2.2 通过指针引用数组元素,数组定义和初始化之后,数组元素可以通过数组的下标、数组名或指向数组的指针变量来引用。若定义一个一维数组:,int a10=1,2,3,4,5,6,7,8,9,10;,可以通过数组的下标访问数组元素:如:,printf(,“,%d,”,a5);,也可以通过数组名访问数组元素:如:,*(a+0)即*(a)与a0相等,*(a+1)与a1相等,,*(a+2)与a2相等,*(a+i)与ai相等,。,但使用数组名时,注意不能用a+的方式,因为a是地址常量,常量是不能被重新赋值的。,19,同样,若定义指针变量,p,(int*p;),并使p指向数组a(即p=a),则可以用指针变量来访问数组元素,如右图所示。,p,指向数组的第一个元素a0,则p+1指向数组的下一个元素a1,p+2指向数组元素a2,,p+i,指向数组元素ai,。,可以使用*(p+i)访问元素ai。,p+i等价于,a+i,,都表示元素ai的地址。,指向数组的指针变量也可以带下标,如pi与*(p+i)等价,表示元素ai。,9.2.2 通过指针引用数组元素,数组元素,ai的4,种引用方式:,ai、*(a+i)、*(p+i)、pi。,引用,数组元素,ai地址的4种方式为:,&ai、a+i、p+i、&pi。,20,另外,在使用指向数组的指针变量时,注意对指针变量,p,+1的理解。,若数组元素是int型,p+1所表示的地址是,p,的地址值加2个字节;若数组元素是float型,p+1所表示的地址是,p,的地址值加4个字节;若数组元素是char型,p+1所表示的地址是,p,的地址值加1个字节。,9.2.2 通过指针引用数组元素,21,当指针变量,p,中存储的是数组a的首地址(p=a)时,执行p+后,p中存储的是数组元素a1的地址,再执行p+后,p中存储的是数组元素a2的地址,。,若数组元素是int型,执行p+后,p中存储的地址是p中原来存储的地址值加2个字节;若数组元素是float型,执行p+后,p中存储的地址是p中原来存储的地址值加4个字节(如右图);若数组元素是char型,执行p+后,p中存储的地址是p中原来存储的地址值加1个字节。,若p中存储的是元素ai(,i,1)的地址,根据上面的叙述,读者可以思考如何理解p-1、p,-,。,float *p,p-1,p,p+1,p+2,*(p-1),*p,*(p+1),*(p+2),9.2.2 通过指针引用数组元素,22,9.2.2 通过指针引用数组元素,int,main(),int a10,*p=a,i,sum,;,printf(“Input 10 numbers:”);,for(i=0;i10;i+),scanf(“%d”,p+i);,/*,使用指针变量来输入数组元素的值,*/,printf(“a,rray a,:”);,for(i=0;i10;i+),printf(“%d”,*(p+i),;,/*,使用指向数组的指针变量输出数组元素的值,*/,sum=sum+,*(p+i),;,printf(“n,sum=%d n,”,sum,);,return 0;,程序运行情况:,Input 10 numbers:0 1 2 3 4 5 6 7 8 9,array a:0 1 2 3 4 5 6 7 8 9,sum=45,例9.4,使用指向数组的指针变量来引用数组元素。,23,#,include,int,main(),int a10,*p=a,i,sum,;,printf(“Input 10 numbers:”);,for(i=0;i10;i+,p+),scanf(“%d”,p);,printf(“a,rray a,:”);,p=a;,/*,使,p,重新指向数组的第一个元素,*/,for(i=0;i p1表示q1 所指元素位于p1 所指元素之后,,q1 p1 表示q1 所指元素位于p1 所指元素之前。,如果两个指针不指向同一个数组,对它们的比较也没有意义。,5如果两个指针q1和 q1指向同一个数组,那么可以对两个指针进行比较:,例如“n=p2 p1;”,当两个指针不指向同一数组时,求它们的差就完全没有意义了。,6当两个指针指向同一数组时,可以求它们的差,得到的结果是对应的数组元素的下标之差,(可能是一个负数)。,27,9.3 指针与字符串,在语言中,既可以用字符数组表示字符串,也可用字符指针变量来表示字符串。,既可以逐个字符引用字符串,也可以整体引用字符串。,字符串在内存中的起始地址称为字符串的指针,可以定义一个字符指针变量指向一个字符串。,28,9.3.1 字符串的表现形式,在C语言中,有两种方式可以操作字符串:,1.使用字符数组,#include,int main(),char string =“she is our teacher“;,printf(%sn,string);,return 0;,例9.5,使用字符数组来输出一串字符串。,程序运行输出:she is our teacher,29,2.使用字符指针,#include,int main(),char*string=“she is our teacher”;,printf(“%sn”,string);,return 0;,程序中,string,是一个指针变量,,语句:,char*string=“she is our teacher”;,等价于:,char *string;,string=“she is our teacher”;,上面语句把字符串常量的首地址赋给指针,string,。不能理解为把字符串常量赋值给指针变量。若写成下面形式,是错误的,:,*string=“she is our teacher”;,9.3.1 字符串的表现形式,例9.6 使用字符指针来输出一串字符串。,30,从以上两个例子,可以看出:,(1)字符数组和字符指针的概念不同。,(2)字符指针指向字符串,在C语言中,字符串按数组方式处理,因此,字符数组和字符指针的访问方式相同。,9.3.1 字符串的表现形式,例如,无论使用字符数组或是字符指针引用字符串,都可以使用%s格式控制符进行整体输入输出。,注意,,如果不是字符数组,而是整型、实型等数字型数组,不能用%s,只能逐个元素处理。,31,9.3.2 字符指针作函数参数,将一个字符串从一个函数传递到另一个函数,可以用字符数组名或字符指针变量作参数。有以下四种情况:,1实参和形参都为字符数组。,2实参为字符指针,形参为数组名。,3实参和形参都为字符指针变量。,4实参为数组名,形参为字符指针变量。,32,例9.7 用函数调用实现字符串的复制,1.实参和形参都为字符数组,#,include,v,oid,copy_string(char str1,char str2),int i=0;,while(str1i!=0),str2i=str1i;i+;,str2i=0;,int,main(),char a,20,=“I,love china!,”;,char b,20,=“good!”;,printf(“a=%sn b=%sn”,a,b);,copy_string(a,b);,printf(“a=%sn b=%sn”,a,b);,return 0;,9.3.2 字符指针作函数参数,33,2.实参为字符指针,形参为数组名,#,include,v,oid,copy_string(char str1,char str2),int i=0;,while(str1i!=0),str2i=str1i;i+;,str2i=0;,int,main(),int i;,char*a=“I,love china!,”;,char*b=“good!”;,printf(“a=%sn b=%sn”,a,b);,copy_string(a,b);,for(i=0;*(a+,i,)!=,0,;i+),putchar(*(a+,i,);,printf(“n”);,for(i=0;*(b+,i,)!=,0,;i+),putchar(*(b+,i,);,return 0;,例9.7 用函数调用实现字符串的复制,9.3.2 字符指针作函数参数,34,3.实参和形参都为字符指针变量,#,include,void,copy_string,(char*str1,char*str2),for(;*,str1,!=0;str1+,str2+),*str2=*str1;,*str2=0;,例9.7 用函数调用实现字符串的复制,9.3.2 字符指针作函数参数,int main(),char*a=“I love china!”;,char*b=“good!”;,for(i=0;*(a+i)!=0;i+),putchar(*(a+i);,printf(“n”);,for(i=0;*(b+i)!=0;i+),putchar(*(b+i);,printf(“n”);,copy_string(a,b);,puts(a);puts(b);,return 0;,35,4.实参为数组名,形参为字符指针变量,#,include,void copy_string(char*str1,char*str2),int i=0;,for(;(*(str2+i)=*(str1+i)!=0;i+);,/*,循环体为空语句,*/,int,main(),char,a,20=“I,love china!,”;,char,b,20,=,“,good!,”;,printf(“a=%sn b=%sn”,a,b);,copy_string(,a,b,);,p,u,t,s,(a,);puts(b);,return 0;,程序说明:以上四种方式实现相同的功能,即把a中的字符串拷贝到b中,不同的地方是拷贝函数参数的方式不同。,在第四种方式中,语句,for(;(*(str2+i)=*(str1+i)!=0;i+);,的执行过程为:首先将源串中的当前字符,复制到目标串中;然后判断该字符是否是字符串结束标志。如果不是,则相对位置变量,i,的值增,1,,以便复制下一个字符;否则结束循环。其特点是:先复制、后判断,循环结束前,结束标志已经复制了。,例9.7 用函数调用实现字符串的复制,9.3.2 字符指针作函数参数,上述四个程序的运行结果都是:,a=I love china!,b=I love china!,36,9.3.3 字符指针变量与字符数组的区别,虽然用字符指针变量和字符数组都能实现字符串的存储和处理,但二者是有区别的:,(1)存储内容不同,字符指针变量中存储的是字符串的首地址,而字符数组中存储的是字符串本身(数组的每个元素存放一个字符)。,(2)赋值方式不同,对字符指针变量,可采用下面的赋值语句赋值:,char *pointer;,pointer=This is a example.;,而字符数组,虽然可以在定义时初始化,但不能用赋值语句整体赋值。下面的用法是非法的:,char char_array20;,char_array=This is a example.;/*非法用法*/,37,9.3.3 字符指针变量与字符数组的区别,(3)指针变量的值是可以改变的,字符指针变量也不例外;而数组名代表数组的起始地址,是一个常量,而常量是不能被改变的。,例如:char*a=“abcdefg”;,a=a+3;puts(a);,执行后输出“defg”。,但下面的执行后却要出错:,char a6=“china”;,a=a+3;puts(a);,因为a=a+3是非法的。,38,9.4 指针与二维数组,指针可以指向一维数组,同样也可以指向二维数组,或指向更高维数组,但指向二维数组或多维数组的指针更复杂一些。,39,9.4.1 二维数组的指针,二维数组是一个由类型相同的变量构成的集合,其元素,以行优先顺序,存放在内存中。可以将二维数组看成是由几个一维数组作为元素组成的一个一维数组。,例如下面定义的数组a:,int a34,=1,2,3,,4,,5,6,7,8,,9,10,11,12;,由12个整型变量构成,这12个数组元素按行顺序存放,如右图所示。可以将a看成是一维数组,它由,a0,、,a1,、,a2,三个数组元素组成;且,a0,、,a1,、,a2,也是一维数组,长度为。,二维数组的地址或指针可以分成两种:一种用来表示真实的数组元素的地址,称为,元素地址,,也就是元素指针;另一种用来表示数组的每一行的地址,称为,行地址,,也就是行指针。,40,二维数组,a,中元素,aij,的地址或指针可以用,&,aij,来表示。由于我们将二维数组看成是由几个一维数组作为元素组成的一个一维数组,将二维数组的每一行看成是一个一维数组,,ai,是一维数组名,由一维数组与指针的关系知,,元素,aij,的地址或指针可以用,ai+j,来表示,。,语言规定数组名代表数组的首地址。二维数组名,a,是一个行地址,a+0、a+1、a+2分别是下标为0、1、2行的地址,即,以,a0、a1、a2为一维数组名的行地址。,即a(a+0)是一维数组a0的首地址,a+1是一维数组a1的首地址,a+2是一维数组a2的首地址,,如右图所示。,9.4.1 二维数组的指针,41,如果b是一维数组名,b,i,为b数组的一个元素,那么有b,i,等价于,*(,b+,i),。同样,a是二维数组名,,ai,为,a,数组的一个元素(将a看成一维数组时,,ai,为,a,数组的一个元素),也有,ai,等价于,*(,a+,i),。,所以二维数组,a,中元素,aij,的地址或指针既可以用,ai+j,来表示,也可以用,*(,a+,i)+j,来表示。,9.4.1 二维数组的指针,综上所述,二维数组a中元素aij的地址或指针可以表示成以下几种形式:,&aij、ai+j、*(a+i)+j,对应地,数组元素aij 可以表示成以下几种形式:,aij、*(ai+j)、*(*(a+i)+j),42,9.4.2 行指针变量,二维数组是由相同类型的一维数组作为元素而构成的一维数组,所谓行指针变量就是用来存放“行地址”的变量。,行指针变量的定义形式如下:,类型标识符,(*,行指针变量名,),数组长度,;,注意:“*行指针变量名”外的括号不能缺,否则成了指针数组(数组的每个元素都是一个指针)。,int (*p)4;,这里(*p)是一个含有4个元素的一维数组,是一维数组的名。因此,p就是指向这个一维数组的行指针变量。,43,例如:,int (*p)4;,int a34=1,2,3,4,5,6,7,8,9,10,11,12;,p=a;,则p为指向4个元素的一维数组的行指针变量;a为一个3行4列的二维数组,即每一行相当于一个含有4个元素的一维数组。,二维数组名是一个行指针类型的地址常量,语句“p=a;”的作用是将数组a的首地址赋给p,使行指针变量p指向二维数组的首行,即p指向一维数组a0。,9.4.2 行指针变量,44,通过行指针可以表示二维数组的首地址、行地址、元素地址、元素等。假设行指针变量p指向二维数组a的第i行,则:,(1)p:等价于a+i,指向第i行的首地址;,(2)p+1:等价于a+,i,+1,指向第,i,+1的首地址;,(3)p+:p向后移动一行,等价于a+,i,+1;,(4)*p:第i行的第0个元素的地址,等价于*(a+,i,)或a,i,;,(5)*p+j:第i行第j列元素的地址,等价于*(a+i)+j、ai+j或&aij;,(6)*(*p+j):第i行第j列元素的值,等价于*(*(a+,i,)+j)、*(ai+j)或aij。,9.4.2 行指针变量,45,例9.8给定二维数组任一元素的行下标和列下标,使用行指针变量输出该元素值。,#include,int main(),int a34=,1,2,3,4,5,6,7,8,9,10,11,12;,int(*p)4,row,col;,p=a;,printf(“Input row=);scanf(%d,printf(Input col=);scanf(%d,printf(a%d%d=%dn,row,col,*(*(p+row)+col);,return 0;,程序运行情况:,Input row=1,Input col=2,a12=7,9.4.2 行指针变量,本题也可以直接使用数组名a作指针,将程序中的*(*(p+row)+col)修改为*(*(a+row)+col)即可。,46,#include,int main(),int a34=1,2,3,4,5,6,7,8,9,10,11,12;,int*p,row,col;/*定义指针变量p*/,p=a0;/*给指针变量p赋值*/,printf(“Input row=”);scanf(“%d”,printf(“Input col=”);scanf(“%d”,printf(“a%d%d=%dn”,row,col,*(p+(row*4+col);,return 0;,此问题也可以不使用行指针变量,而使用对二维数组元素指针的引用,达到访问二维数组的目的,即如下编写:,9.4.2 行指针变量,47,9.4.3 二维数组的指针作函数参数,例9.9 从键盘输入5名学生的课程成绩,若每个学生共学习3门课程,计算总的平均成绩,并根据给定的学生序号,计算这名学生3门课程的平均成绩。,二维数组的指针同一维数组的指针一样可以作函数的参数。,48,#include,int main(),void total_average(float*p,int n);,void student_average(float(*p)3,int n);,int i,j,n;float x;static float score53;,printf(“input score:n”);,for(i=0;i5;i+),for(j=0;j3;j+),scanf(“%f”,total_average(score,15);,printf(“Input No.of student:n”);,scanf(“%d”,student_average(score0,n);,return 0;,void total_average(float*p,int n),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.2fn”,aver);,void student_average(float(*p)3,int n),int i;float sum1=0.0,aver;,for(i=0;i3;i+),sum1=sum1+*(*(p+n)+i);,aver=sum1/3;,printf(“The average score of,No.%d is:%5.2f”,n,aver);,9.4.3 二维数组的指针作函数参数,49,9.5 指针数组与多级指针的概念,9.5.1 指针数组,指针数组中的每一个元素是一个指针变量。指针数组的定义的一般形式:,类型标识符 *数组名数组长度,如:,int *p4;,定义一个指针数组,数组名为p,有4个元素,每一个元素是指向整型变量的指针变量。,注意:,指针数组与上一节所讲的行指针是不同的。如:,int (*p)4;,定义了一个指针变量p,即p为指向一维数组的指针变量,指向了具有4个元素的一维数组,可用于存放二维数组某一行的行地址。,50,下面定义并初始化一个指针数组:,float a=2.5,b=3.0,c=5.5;,float *p3=,这样使p0指向变量a,p1指向变量b,p2指向变量c。,9.5.1 指针数组,51,对于多个字符串也可以用指针数组来处理,如下面的语句:,char*addr=“Shanghai”,“Beijing”,“Guangzhou”,“Hangzhou”,“Nanjing”;,定义并初始化了五个字符型指针变量,指针数组addr的每个元素指向一个字符串常量,如图9.5所示。,可以使用语句“,printf(“%s”,addr1);”,输出字符串“Beijing”。,9.5.1 指针数组,52,例9.10用指针数组实现字符串按字母顺序(由小到大)输出。,#include#include,void sort(char*addr,int n);/*函数声明*/void print(char*addr,int n);/*函数声明*/,int main(),char*addr=“Shanghai”,“Beijing”,“Guangzhou”,“Hangzhou”,“Nanjing”;,int n=5;,sort(addr,n);/*排序函数的调用*/,print(addr,n);/*输出函数的调用*/,return 0;,9.5.1 指针数组,53,void sort(char*addr,int n)/*选择法排序*/,char*temp;int i,j,k;,for(i=0;in-1;i+)/*n个字符串,外循环n-1次*/,k=i;,for(j=i+1;j 0)k=j;,/*比较addrk与addrj所对应字符串的大小*/,if(k!=i)/*交换addri与addrk的指向*/,temp=addri;,addri=addrk;,addrk=temp;,void print(char*addr,int n),int i;,for(i=0;in;i+)printf(%sn,addri);,图9.6 调用函数sort后指针数组的情况,9.5.1 指针数组,程序运行结果为:,Beijing,Guangzhou,Hangzhou,Nanjing,Shanghai,54,9.5.2 多级指针,直接指向数据对象的指针为一级指针,一级指针变量中存放的是数据的地址;而二级指针是不直接指向数据对象,而是指向一级指针变量,即二级指针变量存放的地址是一个一级指针的地址;同样三级指针变量所存放的是一个二级指针的地址,三级指针是指向二级指针变量的指针,可依次类推。,二级指针变量的一般定义形式为,:,类型标识符 *指针变量名;,指针变量前面有两个*,表示它为一个二级指针变量。,55,若要实现把一个二级指针变量p2指向一个一级指针变量p1,而一级指针变量指向变量a,则可以用如下语句来实现:,int a=10,*p1,*p2;p1=,如图9.7所示。,9.5.2 多级指针,图,9.7,二级指针、一级指针和变量的关系,56,输出如下:,Shanghai,Shanghai,Beijing,Beijing,Guangzhou,Guangzhou,Hangzhou,Hangzhou,Nanjing,Nanjing,#include#include,int main(),char*addr =“Shanghai”,“Beijing”,“Guangzhou”,“Hangzhou”,“Nanjing”;,char*p;int n;,p=addr;,for(n=0;ny)z=x;,else z=y;,return z;,9.6.2 函数的指针,64,通过上例可以看到:,(1)除函数名用(*指针变量名)代替外,函数指针的定义形式与函数的原型相同。例:,int(*p)(int,int);,仅当形参类型是int时,可以省略形参类型,一般不要省略。,(2)通过函数名引用函数的指针,即函数的入口地址。语句,“p=max;”把函数max的入口地址赋给函数指针p,,因此语句,c=(*p)(a,b);等价于 c=max(a,b);,语句中的*p代表max。,注意:,语句“p=max;”中,函数名代表函数的入口地址,max后不带有函数参数。用函数指针调用函数时,应指定实参。,(3)(*p)()中的p是一个指向函数的指针变量,它可以先后指向不同的函数,但这些函数的返回值及参数情况应该一致。,9.6.2 函数的指针,65,3函数指针作函数的参数的实例,例9.14,已知契比雪夫多项式定义如下:,输入n(整数)和x(实数),计算多项式的值。,(要求用函数指针作函数的参数来实现),9.6.2 函数的指针,66,#include,float f1(float x);/*,函数声明,*/,float f2(float x);/*,函数声明,*/,float f3(float x);/*,函数声明,*/,float f4(float x);/*,函数声明,*/,int main(),void test(float(*f)(float);/*,函数声明,*/,int n;printf(“ninput n:”);scanf(“%d”,switch(n),case 1:test(f1);break;/*,把函数,f1,的入口地址传给函数,test,的形参,*/,case 2:test(f2);break;/*,把函数,f2,的入口地址传给函数,test,的形参,*/,case 3:test(f3);break;/*,把函数,f3,的入口地址传给函数,test,的形参,*/,case 4:test(f4);break;/*,把函数,f4,的入口地址传给函数,test,的形参,*/,return 0;,9.6.2 函数的指针,67,void test(float(*f)(float)/*,函数,test,定义,*/,float x,result;,printf(“ninput x:”);scanf(“%f”,result=(*f)(x);,printf(“nresult=%f”,result);,float f1(float x)/*,函数,f1,定义,*/,return x;,float f2(float x)/*,函数,f2,定义,*/,return 2*x*x-1;,float f3(float x)/*,函数,f3,定义,*/,return 4*x*x*x-3*x;,float f4(float x)/*,函数,f4,定义,*/,return 8*x*x*x*x-8*x*x+1;,9.6.2 函数的指针,68,9.6.3 返回指针值的函数,一个函数可以返回一个int型、float型、char型的数据,也可以返回一个指针类型的数据。,返回指针值的函数(简称指针函数)的定义格式如下:,函数类型 *函数名(形参表列),例9.15 使用指针函数实现:对于存储在内存中的一个字符串,给定一个字符,查找该字符在字符串中第一次出现的位置(用内存地址值表示),然后输出从该地址开始、直到字符串尾的所有字符。,69,#include,char*seek(char*q,char ch);,int main(),char str80,ch,*p;,printf(“请输入一串字符:”);,gets(str);,printf(“请输入要查找的一个字符:”);,scanf(“%c”,p=seek(str,ch);,if(p=NULL),printf(“字符%c在本字符串中不存在。n”,ch);,else,printf(“在字符串中,字符%c第一次出现的地址是:%d。n”,ch,p);,printf(“从地址%d开始,直到字符串尾的所有字符是:
展开阅读全文