1、2023-1-301l10.1 地址和指针的概念l10.2 变量的指针和指向变量的指针变量l10.3 数组的指针和指向数组的指针变量l10.4 字符串的指针和指向字符串的指针变量 本章要点2023-1-30210.5 函数的指针和指向函数的指针变量10.6 返回指针值的函数10.7 指针数组和指向指针的指针10.8 有关指针的数据类型和指针运算的小结2023-1-303 1、字符串的表示形式、字符串的表示形式在C程序中,可以用两种方法访问一个字符串。(1)用字符数组存放一个字符串,然后输出该字符串。例EX10_16 字符数组的应用#includevoid main()char string=“
2、I love China!”;printf(“%sn”,string);2023-1-304 (2)用字符指针指向一个字符串 和数组的属性一样,string也是数组名,它代表字符数组的首地址(P251图10.31)。string4代表数组中序号为4的元素(v),实际上string4就是*(string+4),string+4是一个地址,它指向字符“v”。可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。2023-1-305例EX10_16_2 字符指针的应用#includevoid main()char*string=“I love China!”;printf(“%sn”
3、,string);2023-1-306 C 语言对字符串常量是按字符数组处理的,在内存开辟了一个字符数组用来存放字符串常量。程序在定义字符指针变量string时把字符串首地址(即存放字符串的字符数组的首地址)赋给string(P252图10.32)。注意string是一个指针,而不是字符变量。语句:char*string=“I love China!”;等价于下面两行:char*string;string=“I love China!”;对字符串中字符的存取,可以用下标法,也可以用指针法。2023-1-307 (3)字符指针变量的应用 例EX10_17 输出字符串中第n个字符后的所有字符。#i
4、ncludemain()char*ps=this is a book;int n=10;ps=ps+n;printf(%sn,ps);2023-1-308例EX10_17_2 在输入的字符串中查找有无k字符。#includemain()char st20,*ps;int i;printf(input a string:n);ps=st;scanf(%s,ps);for(i=0;psi!=0;i+)if(psi=k)printf(there is a k in the stringn);printf(“The k in the string of%d charactern”,i+1);break
5、;if(psi=0)printf(There is no k in the stringn);2023-1-309例EX10_18 将字符串a复制为字符串b。要求把一个字符串的内容复制到另一个字符串中,并且不能使用strcpy函数。#includevoid 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
6、(“n”);2023-1-3010例EX10_19 用指针变量来处理例EX10_18问题。#includevoid main()char a=“I am a boy.”,b20,*p1,*p2;int i;p1=a;p2=b;for(;*p1!=0;p1+,p2+)*p2=*p1;*p2=0;printf(“String a is:%sn”,a);printf(“String b is:%sn”,b);2023-1-3011也可以把第68行简化为以下形式:while(*p2+=*p1+)!=0);即把指针的移动和赋值合并在一个语句中。进一步分析还可发现0的ASC码为0,对于while语句只看表
7、达式的值为非0就循环,为0则结束循环,因此也可省去“!=0”这一判断部分,而写为以下形式:while(*p2+=*p1+);表达式的意义可解释为,源字符向目标字符赋值,移动指针,若所赋值为非0则循环,否则结束循环。这样使程序更加简洁。在下面的程序中将会应用这一点。2023-1-3012 2、字符串指针作为函数参数、字符串指针作为函数参数 将一个字符串从一个函数传递到另一个函数,可以用地址传递的办法,即用字符数组名作参数或用指向字符串的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。2023-1-3013例EX10_20 用函数调用实现字符串的复制,方法
8、一,用字符数组作参数。#includevoid copy_string(char from,char to)int i=0;while(fromi!=0)toi=fromi;i+;toi=0;void main()char a=“I am a teacher.”;char b=“you are a student.”;printf(“String a is:%snString b is:%sn”,a,b);copy_string(a,b);printf(“String a is:%snString b is:%sn”,a,b);2023-1-3014例EX10_20_2 用函数调用实现字符串的
9、复制,方法二,形参用字符指针变量。#includevoid copy_string(char*from,char*to)for(;*from!=0;from+,to+)*to=*from;*to=0;void main()char*a=“I am a teacher.”;char*b=“you are a student.”;printf(“String a is:%snString b is:%sn”,a,b);copy_string(a,b);printf(“String a is:%snString b is:%sn”,a,b);2023-1-3015 归纳起来,作为函数的参数,有以下几
10、种情况:实参形参数组名数组名字符指针变量字符指针变量数组名字符指针变量字符指针变量数组名2023-1-3016 3、对使用字符串指针变量与字符数组、对使用字符串指针变量与字符数组的讨论的讨论 用字符数组和字符指针变量都可实现字符串的存储和运算。但是两者是有区别的。在使用时应注意以下几个问题:(1)字符数组由若干个元素组成,每个元素中放一个字符,并以0作为串的结束。而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中。2023-1-3017 (2)对字符数组作初始化赋值,必须采用外部类型或静态类型,如:static char st=“C Language”;而对字符串
11、指针变量则无此限制,如:char*ps=C Language;(3)对字符串指针方式 char*ps=C Language;可以写为:char*ps;ps=C Language;而对数组方式:static char st=C Language;不能写为:char st20;st=C Language;而只能对字符数组的各元素逐个赋值。2023-1-3018 (4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个地址值,也就是说,该指针变量可以指向一个字符数组,但如果未对它赋予一个地址值,则它并未具体指向一个确定
12、的字符数组。2023-1-3019 对数组方式:char str10;scanf(“%s”,str);是成立的,而对指针方式:char*p;scanf(“%s”,p);是不成立的,因为p并未指向一个具体的字符数组。必须写成以下的形式:char*p,str10;p=str;scanf(“%s”,p);2023-1-3020 (5)指针变量的值是可以改变的(例EX10_17)。需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符(例EX10_17_2)。(6)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。如:char*f
13、ormat;format=“a=%d,b=%dn”;printf(format,a,b);相当于:printf(“a=%d,b=%dn”,a,b);从以上几点可以看出字符串指针变量与字符数组在使用时的区别,同时也可看出使用指针变量更加方便。2023-1-3021 在语言中规定,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针变量”。2023-1-3022 1、用函数指针调用函数、用函数指针调用函数 可以用
14、指针变量指向整型变量、字符串、数组,当然也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。2023-1-3023 函数指针变量定义的一般形式为:类型说明符类型说明符(*指针变量名指针变量名)();其中“类型说明符”表示被指函数的返回值的类型。“(*指针变量名)”表示“*”后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。例如:int(*pf)();表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。2023-1-3024 下面通过例子来说明用指针形式实现对函数调
15、用的方法。例EX10_23 求a和b中的大者。先写出用一般方法的程序。#includeint max(int x,int y)if(xy)return x;else return y;void main()int max(int x,int y);int a,b,c;printf(input two numbers:n);scanf(%d,%d,&a,&b);c=max(a,b);printf(a=%d,b=%d,max=%dn,a,b,c);2023-1-3025 主函数中“c=max(a,b);”包括了一次函数调用(调用max函数)。每一个函数都占用一段内存单元,它们有一个起始地址。因此,
16、可以用一个指针变量指向一个函数,通过指针变量来访问它指向的函数。改写main函数如下:void main()int max(int x,int y);int(*p)();/*定义一个指向函数的指针变量*p*/int a,b,c;p=max;/*将函数max的首地址赋予指针变量p*/printf(input two numbers:n);scanf(%d,%d,&a,&b);c=(*p)(a,b);/*通过指针变量调用该指针指向的函数*/printf(a=%d,b=%d,max=%dn,a,b,c);2023-1-3026 说明:(1)指向函数的指针变量的一般定义形式为:数据类型(*指针变量名)
17、()这里的“数据类型”是指函数返回值的类型。(2)函数的调用可以通过函数名调用,也可以通过函数指针调用(即用指向函数的指针变量调用)。2023-1-3027 (3)(*p)()表示定义一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。在一个程序中,一个指针变量可以先后指向不同的函数。2023-1-3028 (4)在给函数指针变量赋值时,只需给出函数名而不必给出参数,如:p=max;因为将函数入口地址赋给p,而不牵涉到实参与形参的结合问题。(5)用函数指针变量调用函数时,
18、只需将(*p)代替函数名即可(p为指针变量名),在(*p)之后的括弧中根据需要写上实参。如:c=(*p)(a,b);(6)对指向函数的指针变量,指针运算是无效的。2023-1-3029 2、用指向函数的指针作函数、用指向函数的指针作函数参数参数 函数指针变量常用的用途之一是把指针作为参数传递到其他函数。这个问题是 C语言应用的一个比较深入的部分,这里只作简单介绍,以便今后进一步理解和掌握打下一点基础。2023-1-3030 例EX10_24 设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次调用它时找出两个数中的大者,第二次调用时找出两个数中的小者,第三次求
19、这两个数的和。2023-1-3031#includevoid main()int max(int,int);int min(int,int);int add(int,int);int a,b;printf(“Enter a and b:n”);scanf(“%d,%d”,&a,&b);printf(“Max=”);process(a,b,max);printf(“Min=”);process(a,b,min);printf(“Add=”);process(a,b,add);2023-1-3032max(int x,int y)if(xy)return x;else return y;min(i
20、nt x,int y)if(xy)return x;else return y;add(int x,int y)return(x+y);process(int x,int y,int(*fun)()int result;result=(*fun)(x,y);printf(“%dn”,result);2023-1-3033 一个函数可以带回一个整型值、字符值、实型值,也可以带回指针型的数据,即地址。在语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针型函数。其概念与以前类似,只是带回的值的类型是指针而已。2023-1-3034 定义指针型函数的一般形式为:类型说明符类型
21、说明符*函数名(参数表)函数名(参数表)其中函数名之前加了“*”号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。2023-1-3035如:int*a(int x,int y)./*函数体函数体*/a是函数名,调用它以后能得到一个指向整型数据的指针(地址)。x、y是函数a的形参,为整型。2023-1-3036 应该特别注意的是函数指针变量和指针型函数这两者在写法和意义上的区别。如int(*p)()和int*p()是两个完全不同的量。int(*p)()是一个变量说明,说明p 是一个指向函数入口的指针变量,该函数的返回值是整型量,(*p)的两边的括号不能少
22、。int*p()则不是变量说明而是函数说明,说明p是一个指针型函数,其返回值是一个指向整型量的指针,*p两边没有括号。作为函数说明,在括号内最好写入形式参数,这样便于与变量说明区别。对于指针型函数定义,int*p()只是函数头部分,一般还应该有函数体部分。2023-1-3037#includemain()int i;char*day_name(int n);printf(Input Day No:n);scanf(%d,&i);printf(Day No:%2d-%sn,i,day_name(i);char*day_name(int n)static char*name=Illegal day
23、,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday;return(n7)?name0:namen);2023-1-3038 本程序是通过指针函数,输入一个17之间的整数,输出对应的星期名。程序中定义了一个指针型函数 day_name,它的返回值指向一个字符串。该函数中定义了一个静态指针数组name。name 数组初始化赋值为八个字符串,分别表示各个星期名及出错提示。形参n表示与星期名所对应的整数。在主函数中,把输入的整数i作为实参,在printf语句中调用day_name函数并把i值传送给形参 n。day_name函数中的retu
24、rn语句包含一个条件表达式,n 值若大于7或小于1则把name0 指针返回主函数输出出错提示字符串“Illegal day”。否则返回主函数输出对应的星期名。2023-1-3039 例EX10_25 有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号后,能输出该学生的全部成绩。(用指针函数来实现)#includefloat search(float(*pointer)4,int n)float pt;pt=*(pointer+n);return(pt);2023-1-3040void main()score4=60,70,80,90,56,89,67,88,34,78,90,66
25、;float*search(float(*pointer)4,int n);float*p;int i,m;printf(“Enter the number of student:”);scanf(“%d”,&m);printf(“nThe scores of No.%d are:n”,m);p=search(score,m);for(i=0;i4;i+)printf(“%5.2ft”,*(p+i);printf(“n”);2023-1-3041例EX10_26 对上例中的学生,找出其中有不及格课程的学生及其学生号。#include float*search(float(*pointer)4)
26、float*pt;int i;pt=*(pointer+1);for(i=0;i4;i+)if(*(*pointer+i)60)pt=*pointer;return(pt);2023-1-3042void main()float score4=60,70,80,90,56,89,67,88,34,78,90,66;float*p;int i,j;for(i=0;i3;i+)p=search(score+i);if(p=*(score+i)printf(“No.%d score:”,i);for(j=0;j4;j+)printf(“%5.2ft”,*(p+j);printf(“n”);2023-
27、1-3043 1、指针数组的概念、指针数组的概念 指针数组的说明与使用一个数组的元素值为指针则是指针数组。指针数组是一组有序的指针的集合。指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。一维指针数组的定义形式为:类型说明符类型说明符*数组名数组名数组长度数组长度 2023-1-3044 其中类型说明符为指针值所指向的变量的类型。例如:int*p3 表示p是一个指针数组,它有三个数组元素,每个元素值都是一个指针,指向整型变量。由于 比*优先级高,因此p先与3结合,形成p3形式,这显然是数组形式,然后再与*结合,表示为指针类型,因此形成指针数组的形式。注意:不要写成int(
28、*p)3的形式,它是指向一维数组的指针变量(P229最后一段说明)。2023-1-3045 通常可用一个指针数组来指向一个二维数组。指针数组中的每个元素被赋予二维数组每一行的首地址,因此也可理解为指向一个一维数组。它比较适合于用来指向若干个字符串,使字符串处理更加方便灵活。每个字符串都是存放在一个一维字符数组中的,如下所示:2023-1-3046Follow meF o llo wm e 0BASICB A S IC 0Great WallG re a tW all0FORTRANF O R T R A N 0Computer designC o m p u terdes ig n 0字符串字
29、符串的存储形式2023-1-3047name0Follow mename1BASICname2Great Wallname3FORTRANname4Computer designname指针数组字符串2023-1-3048 可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串(上图),由此可以方便地对各字符串进行处理。如对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素的指向(即改变各元素的值,即各字符串的首地址)。这样,各字符串的长度可以不同,而且移动指针变量的值(地址)要比移动字符串所花时间少得多。2023-1-3049例10_27 将若干字符串按字母顺序(由小到大)输
30、出。#include#include void sort(char*name,int n)char*temp;int i,j,k;for(i=0;in-1;i+)k=i;for(j=i+1;j0)k=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);2023-1-3050void main()void sort(char*name,int n);void print(char*name,int n);char*name=“F
31、ollow me”,“BASIC”,“Great Wall”,“FORTRAN”,“Computer design”;int n=5;printf(“The strings before sort:n”);print(name,n);sort(name,n);printf(“nThe strings after sorted:n”);print(name,n);2023-1-3051 排序操作的执行过程是移动了指向原字符串(数组)的指针,而没有改变字符串的存储单元,简化了操作过程。name0Follow mename1BASICname2Great Wallname3FORTRANname4C
32、omputer designname指针数组字符串2023-1-3052Print函数也可以改写为以下形式:void print(char*name,int n)int i=0;char*p;p=name0;while(in)p=*(name+i+);printf(“%sn”,*p);2023-1-3053例EX10_27_2 输出数组中各元素的值。#includeint a33=1,2,3,4,5,6,7,8,9;int*pa3=a0,a1,a2;int*p=a0;main()int i;for(i=0;i3;i+)printf(%d,%d,%dn,ai2-i,*ai,*(*(a+i)+i)
33、;for(i=0;i3;i+)printf(%d,%d,%dn,*pai,pi,*(p+i);2023-1-3054 本例程序中,pa是一个指针数组,三个元素分别指向二维数组a的各行。然后用循环语句输出指定的数组元素。其中*ai表示i行0列元素值;*(*(a+i)+i)表示i行i列的元素值;*pai表示i行0列元素值;由于p与a0相同,故pi表示0行i列的值;*(p+i)表示0行i列的值。读者可仔细领会元素值的各种不同的表示方法。应该注意指针数组和二维数组指针变量的区别。这两者虽然都可用来表示二维数组,但是其表示方法和意义是不同的。2023-1-3055例EX10_27_3 将若干个国家按字母
34、排序。#include#includevoid sort(char*name,int n)char*p;int i,j,k;for(i=0;in-1;i+)k=i;for(j=i+1;j0)k=j;if(k!=i)p=namei;namei=namek;namek=p;2023-1-3056void print(char*name,int n)int i;for(i=0;in;i+)printf(%sn,namei);main()void sort(char*name,int n);void print(char*name,int n);char*name=CHINA,AMERICA,AUST
35、RALIA,FRANCE,GERMAN;int n=5;sort(name,n);print(name,n);2023-1-3057例EX10_27_4 指针数组作指针型函数的参数#include char*day_name(char*name,int n)char*pp1,*pp2;pp1=*name;pp2=*(name+n);return(n7)?pp1:pp2);2023-1-3058main()static char*name=Illegal day,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday;char*ps;in
36、t i;printf(input Day No:n);scanf(%d,&i);ps=day_name(name,i);printf(Day No:%2d-%sn,i,ps);2023-1-3059name0Follow mename1BASICname2Great Wallname3FORTRANname4Computer design指针数组字符串 2、指向指针的指针 在掌握了指针数组的概念的基础上,下面介绍指向指针数据的指针变量,简称为指向指针的指针。如下图所示,p为指向指针的指针。namep2023-1-3060 定义一个指向指针数据的指针变量的一般形式为:类型说明符类型说明符 *指针
37、变量名指针变量名 如:char*p;其中p为指向指针的指针变量。P的前面有两个*号,*运算符的结合性是从右到左,因此*p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那么是定义了一个指向字符数据的指针变量。现在它的前面又有一个*号,表示指针变量p是指向一个字符指针的变量(即指向字符型数据的指针变量)的。2023-1-3061 *p就是p所指向的另一个指针变量,如果有:p=name+3;printf(“%dn”,*p);printf(“%sn”,*p);则第一个printf函数输出name3的值(它是一个地址),第二个printf函数则输出字符串“FORTRAN”。2023
38、-1-3062例EX10_28 指向指针的指针变量的简单应用。#includevoid main()char*name=“Follow me”,“BASIC”,“Great Wall”,“FORTRAN”,“Computer design”;char*p;int i=0;for(i=0;i5;i+)p=name+i;printf(“%dtt”,*p);printf(“%sn”,*p);2023-1-3063例例EX10_29 指针数组的元素指向整型数据指针数组的元素指向整型数据的简单应用。的简单应用。#includevoid main()int a5=1,3,5,7,9;int*num5;in
39、t*p,i;for(i=0;i5;i+)numi=&ai;p=num;for(i=0;i5;i+)printf(“%dt”,*p);p+;2023-1-30641、有关指针的数据类型的小结 定 义含 义int i;定义整型变量iint*p;p为指向整型数据的指针变量int an;定义整型数组a,它有n个元素int*pn;定义指针数组p,它由n个指向整型数据的指针元素int(*p)n;p为指向含n个元素的一维数组的指针变量int f();f为带回整型函数值的函数int*p();p为带回一个指针的函数,该指针指向整型数据int(*p)();p为指向函数的指针,该函数返回一个整型值int*p;p是一
40、个指针变量,它指向一个指向整型数据的指针2023-1-3065 2、指针运算小结、指针运算小结 (1)指针变量加(减)一个整数例如:p+、p、p+i、pi、p+=i、p=i等。C 语言规定,一个指针变量加(减)一个整数并不是简单地将指针变量的原值加(减)一个整数,而是将该指针变量的原值(是一个地址)和它指向的变量所占用的内存单元字节数相加(减)。如p+i代表地址计算:p+ci(其中c为字节数,对字符数据c=1,整型数据c=2,实型数据c=4)。这样才能保证*(p+i)指向p下面的第i个元素,它才有实际意义。2023-1-3066 (2)指针变量赋值 将一个变量存储单元的地址赋给一个指针变量。如
41、:p=&a;/*将变量a的地址赋给p*/p=array;/*将数组array首地址赋给p*/p=&arrayi;/*将数组array第i个元素的地址赋给p*/p=max;/*max为已定义的函数,将max的入口地址赋给p*/p1=p2;/*p1和p2都是指针变量,将p2的值赋给p1*/注意:不能把一个整数赋给指针变量,也不应该把指针变量的值赋给一个整型变量。2023-1-3067 (3)指针变量可以有空值,即该指针变量不指向任何变量。定义p=NULL;为指针变量p为空。(4)两个指针变量可以相减。(5)两个指针变量可以比较。2023-1-3068 3、void指针类型指针类型 ANSI标准增加
42、了一种“void”指针类型,即可定义一个指针变量,但不指定它是指向哪一种类型数据。ANSI C标准规定用动态存储分配函数时返回void指针,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。2023-1-3069 本章内容到此结束,在讲义的最后对本章内容进行了小结,希望同学们能阅读一下,以加深对指针的理解和应用。从初学者的角度考虑,指针的含义是很深奥的,也是很难理解的。但从应用的角度出发,指针是很有用处的。为什么C语言能得到广大程序设计人员的青睐,显然指针及其运用正是它的昧力所在。对本章内容进行一次有效地完整地总结,将会对以后的学习有所帮助。2023-1-3070