《c语言程序设计第三版谭浩强第十章指针.ppt》由会员分享,可在线阅读,更多相关《c语言程序设计第三版谭浩强第十章指针.ppt(110页珍藏版)》请在三一办公上搜索。
1、第十章,指针,10.1地址和指针的概念,为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。,内存区的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。,、按变量地址存取变量值的方式称为“直接访问”方式,另一种存取变量值的方式称为“间接访问”的方式。即,将变量的地址存放在另一个变量中。,在语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量i_pointer用来存放整型变量的地址,它被分配地址为(3010)、(3011)的两个字节。可以通过语句:i_pointer;将的地址(2000)
2、存放到i_pointer中。这时,i_pointer的值就是(2000),即变量所占用单元的起始地址。要存取变量的值,可以采用间接方式:先找到存放“的地址”的变量i_pointer,从中取出的地址(2000),然后到2000、200字节取出的值()。,一个变量的地址称为该变量的“指针”。例如,地址2000是变量的指针。如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的i_pointer就是一个指针变量。,指针和指针变量的定义:,指针变量的值(即指针变量中存放的值)是地址(即指针)。请区分“指针”和“指针变量”这两个概念。,10.2 变量的指针和指向变量的指针变量,
3、10.1 定义一个指针变量,定义指针变量的一般形式为基类型*指针变量名;,下面都是合法的定义:float*pointer_;/pointer_是指向float型变量的指针变量char*pointer_;/pointer_是指向字符型变量的指针变量可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。如:pointer_;pointer_;,在定义指针变量时要注意两点:,10.指针变量的引用,请牢记,指针变量中只能存放地址(指针),不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量。,例10.通过指针变量访问整型变量,#include voidmain()int,;
4、int*pointer_,*pointer_;pointer_;/*把变量的地址赋给 pointer_1*/,pointer_;/*把变量的地址赋给 pointer_*/printf(%,%,);printf(%,%,*pointer_,*pointer_);,对“”和“*”运算符说明:如果已执行了语句 pointer_;(1)*pointer_的含义是什么?“”和“*”两个运算符的优先级别相同,但按自右而左方向结合,因此先进行*pointer_的运算,它就是变量,再执行运算。因此,*pointer_与相同,即变量a的地址。如果有pointer_2*pointer_;它的作用是将(的地址)赋给
5、pointer_2,如果pointer_2原来指向,经过重新赋值后它已不再指向了,而指向了。,(2)*的含义是什么?先进行运算,得的地址,再进行*运算。即所指向的变量,也就是变量a。*和*pointer_的作用是一样的,它们都等价于变量。即*与等价。,(3)(*pointer_)相当于。注意括号是必要的,如果没有括号,就成为了*pointer_,从附录可知:+和*为同一优先级别,而结合方向为自右而左,因此它相当于*(pointer_)。由于+在pointer_1的右侧,是“后加”,因此先对pointer_的原值进行*运算,得到的值,然后使pointer_的值改变,这样pointer_不再指向了
6、。,例10.2 输入和两个整数,按先大后小的顺序输出 和。,#include void main()int*1,*2,*,;scanf(,);1;if();printf(=,=,);printf(max=,min=,*1,*2);,运行情况如下:,当输入,时,由于,将和交换。交换前的情况见图(),交换后见图()。,10.3 指针变量作为函数参数,例10.3 对输入的两个整数按大小顺序输出,#include void main()void swap(int*,int*);int,;int*pointer_,*pointer_;scanf(,);pointer_;pointer_2;if(swap
7、(pointer_,pointer_2);printf(,);,void swap(int*,int*)int temp;temp*1;*;*temp;,例10.输入、3个整数,按大小顺序输出,#include void main()void exchange(int*1,int*2,int*3);int,*,*,*;scanf(%,%,%,&,&,&);exchange(,);printf(,);,void exchange(int*,int*,int*)void swap(int*,int*);if(*)swap(,);if(*)swap(,);if(*swap(,);void swap(
8、int*,int*)int temp;temp*;*;*temp;,10.3 数组与指针,一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址。,定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。例如:;(定义为包含个整型数据的数组)*;(定义为指向整型变量的指针变量)应当注意,如果数组为型,则指针变量的基类型亦应为型。,10.3.1 指向数组元素的指针,对该指针变量赋值:;把元素的地址赋给指针变量。也就是
9、使指向数组的第号元素,如图:,10.通过指针引用数组元素,引用一个数组元素,可以用:()下标法,如形式;()指针法,如*()或*()。其中是数组名,是指向数组元素的指针变量,其初值。,例10.5 输出数组中的全部元素,假设有一个数组,整型,有个元素。要输出各元素的值有三种方法:,(1)下标法#include void main()int;int;for(;)scanf(,);printf();for(;)printf(,);,(2)通过数组名计算数组元素地址,找出元素的值。#include voidmain()int;int;for(;)scanf(,);printf();for(;)prin
10、tf(,*();,(3)用指针变量指向数组元素。#include void main()int;int*,;for(;)scanf(,);printf();for(;();)printf(,*);,例10.通过指针变量输出数组的个元素。,有人编写出以下程序:#include void main()int*,;for(;)scanf(,);printf();for(;,)printf(,*);,这个程序乍看起来好像没有什么问题。有的人即使已被告知此程序有问题,还是找不出它有什么问题。我们先看一下运行情况:1 2 3 4 5 6 7 8 9 022153 234 0 0 30036 25202 1
11、1631 8259 8237 28483显然输出的数值并不是数组中各元素的值,解决这个问题的办法,只要在第二个循环之前加一个赋值语句:;,#include void main()int*,;for(;)scanf(,);printg();p=a;for(;,)printf(,*);,10.3 用数组名作函数参数,在第8章8.7节中介绍过可以用数组名作函数的参数如:void main()i(int,int);int;(,);void(int,int),f(int arr,int n)但在编译时是将arr按指针变量处理的,相当于将函数f的首部写成f(int*arr,int n)以上两种写法是等价的
12、。,需要说明的是:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。,例10 将数组中个整数按相反顺序存放,#include void main()void inv(int,int);int,;printf(The original array:);for(;)printf(,);printf();inv(,);printf(The array has been in verted:);for(;)printf(,);printf();,void
13、 inv(int,int)/*形参x是数组名*/int temp,();for(;);temp;temp;return;,运行情况如下:The original array:,The array has been inverted:,,#include void main()void inv(int*,int);int,;printf(The original array:);for(;)printf(,);printf();inv(,);printf(The array has been in verted:);for(;)printf(,);printf();,对这个程序可以作一些改动。将
14、函数inv中的形参改成指针变量。,void inv(int*,int)/*形参x为指针变量*/int,temp,*,*,();for(;,)emp*;*;*temp;return;,归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下种情况:,(1)形参和实参都用数组名,如:void main()void(int,int)int;(,);,(2)实参用数组名,形参用指针变量。如:void()void(int*,int)int;(,);,(3)实参形参都用指针变量。例如:void main()void(int*,int)int,*p=a;(p,);,(4)实
15、参为指针变量,形参为数组名。如:void main()void(int x,int),*p=a;(p,);,#include void main()void inv(int*,int);int,*;printf(The original array:n);for(;,)scanf(,);printf();inv(,);/*实参为指针变量*/printf(The array has been inverted:);for(;)printf(,*);printf();,void inv(int*,int)int,temp,*,*;();for(;,)emp*;*;*temp;return;,例10
16、9 用选择法对个整数按由大到小顺序排序,#include void main()void sort(int,int);int*,10;for(;)scanf(,);sort(,);for(,;)(,*);,void sort(int,int)int,;for(;);for(;)();(!);,10.多维数组与指针,用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。,1.多维数组元素的地址,先回顾一下多维数组的性质,可以认为二维数组是“数组的数组”,例:定义int a34=1,3,5,7,;则二维数组a是由3个一维数组所
17、组成的。设二维数组的首行的首地址为,则,例10.0 输出二维数组有关的值,#include define FROMAT,void main()int 341,3,5,7,9,;printf(,*);printf(,0,*();printf(,0,00);printf(,1,);printf(,10,*(+)+);printf(,*();printf(,);printf(,*(*();,某一次运行结果如下:,(0行首地址和0行0列元素地址),(0行0列元素地址),(0行0首地址和0行0列元素地址),(1行0列元素地址和1行首地址),(1行0列元素地址),(2行0列元素地址),(2行首地址),(1
18、行0列元素的值),2.指向多维数组元素的指针变量,在了解上面的概念后,可以用指针变量指向多维数组的元素。(1)指向数组元素的指针变量,例10.11 用指针变量输出二维数组元素的值,#include void main()int 341,3,5,7,9,11,13,15,17,19,21,23;int*;for(;)()printf();printf(,*);,运行结果如下:1 3 5 7 9 11 13 1519 21 23,可将程序最后两个语句改为printf(addr,value2,*);在TC+环境下某一次运行时输出如下:,,(2)指向由个元素组成的一维数组的指针变量,例10.13 出二
19、维数组任一行任一列元素的值,#include void main()int 1,3,5,7,9,11,13,15,;int(*),;scanf(,);printf(,*(*();,运行情况如下:,(本行为键盘输入),,运行情况如下:,(本行为键盘输入),,3.用指向数组的指针作函数参数,例10.13 有一个班,个学生,各学门课,计算总平均分数以及第个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数求总平均成绩,用函数找出并输出第个学生的成绩。,#include void main()void average(float*p,int n);void sea
20、rch(float(*p)4,int n);float score34=65,67,70,60,80,87,90,81,90,99,100,98;average(*score,12);*求12个分数的平均分*search(score,);*求序号为的学生的成绩*,void average(float*,int)float*_;float,;_;(;_;)(*);printf(average,aver);,void search(float(*)4,int)/*p是指向具有4个元素的一维数组的指针*/int;printf(the score of No.%are:,);for(;)printf(
21、5.2,*(*();程序运行结果如下:.,例10.4 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。,#include void main()void search(float(*p)4,int n);/*函数声明*/float score34=65,57,70,60,58,87,90,81,90,99,100,98;search(score,);,void search(float(*p)4,int)int,;for(;)flag;for(;)if(*(*())flag;if()printf(No.%d fails,his scores are:n,j+1);fo
22、r(;)printf(%.,*(*();printf();,程序运行结果如下:s,:.s,.,10.字符串与指针,10字符串的表示形式,例 10.5 定义一个字符数组,对它初始化,然后输出该字符串,#include void main()char string!;printf(,);,(1)用字符数组存放一个字符串,然后输出该字符串。,(2)用字符指针指向一个字符串,可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。例106 定义字符指针#include void main()charstring!;printf(,);,例10.7 将字符串复制为字符串,对字符串中字符的存
23、取,可以用下标方法,也可以用指针方法,#include void()char am a boy,20;int;for(;*()!;)*()*();*();printf(string a is:,);printf(:);for(;!;)printf(,);printf();,也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。,例10.8 用指针变量来处理例107问题。,#include void main()char=I am a boy.,20,*p1,*p2;int;for(;*!;p1,p2),*;*;printf(string is:,);printf(:);for(;!;)p
24、rintf(,);printf();,程序必须保证使和同步移动。,10.字符指针作函数参数,例1019 用函数调用实现字符串的复制,#include void main()void copy_string(char from,char to);char a=am a teacher;char=you are a student;printf(“string a=string,);printf(copy string a to string b:n);copy_string(,);printf(nstring a=%snstring b=%sn,a,b);,(1)用字符数组作参数,void co
25、py_string(char from,char to)int;while(!);,程序运行结果如下:copy string a to string b:,()形参用字符指针变量,#include void main()void copy_string(char*,char*);char*am a teacher.;char*you are a student;printf(string a=,);printf(copy string a to string b:n);_(,);printf(nstring a=%snstring b=%sn,a,b);,void copy_string(ch
26、ar*,char*)for(;*from!;from,to)*;*;,()对 copy_string 函数还可作简化,1、将copy_string函数改写为void copy_string(char*,char*)while(*)!);,2、copy_string函数的函数体还可改为 while(*to*from)!);,3、copy_string函数的函数体还可写成 while(*!)*;*;,4、上面的while语句还可以进一步简化为下面的while语句:while(*);它与下面语句等价:while(*to*from)!);将*赋给*,如果赋值后的*值等于,则循环终止(已赋给*),5、函
27、数体中语句也可以改用语句:for(;(*to*from)!;);或for(;*to*from;);,6、也可用指针变量,函数copy_string可写为void copy_string(char from,char)*,*;while(*p2*p1)!);,10.对使用字符指针变量和字符数组的讨论,虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点:,(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。(2)赋值方式。对字符数组只能对各个元素赋
28、值,不 能用以下办法对字符数组赋值。char str;str!;而对字符指针变量,可以采用下面方法赋值:char*;!;但注意赋给的不是字符,而是字符串第一个元素 的地址。,(3)对字符指针变量赋初值:char*love China!;等价于 char*;!;而对数组的初始化:char str love China!;不能等价于 char str;str!;,(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指
29、向一个确定的字符数据。,如:char str;scanf(,str);是可以的。而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的:char*;scanf(,);,应当这样:*,;(,);,(5)指针变量的值是可以改变的,如:,例10.0改变指针变量的值,#include void()char*I love China!;printf(,);,需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。例101#include voidmain()char*love hina!;int;printf(“The sixt
30、h character is%cn,a5);for(;!;)printf(,);,10.指向函数的指针,10.用函数指针变量调用函数,可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。,例10.2 求和中的大者。先列出按一般 方法的程序。,#include voidmain()int max(int,int);int,;scanf(,);(,);printf(,);int max(int,int)int;if();else;return();,将 main 函数改写为#include voidmain()in
31、t(int,int);int(*)();int,;scanf(,);(*)(,);printf(,);,10.用指向函数的指针作函数参数,函数指针变量常用的用途之一是把指针作为参数传递到其他函数。前面介绍过,函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。现在介绍指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。它的原理可以简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2),定义x1和x2为指向函数的指针变量。在调用函数sub时,实参为两个函数名和,给形参传递的是函数和的地址。这样在函数中就可以调用和函数了。
32、,实参函数名 f1 void(int(*x1)(int),int(*x2)(int,int))int,;(*)();*调用函数*(*)(,);*调用函数*,例10.3 设一个函数process,在调用它的时候,每次实现不同的功能。输入和两个数,第一次调用process时找出和中大者,第二次找出其中小者,第三次求与之和。,#include void main()int max(int,int);/*函数声明*/int min(int,int);/*函数声明*/int add(int,int);/*函数声明*/void process(int,int,int(*fun)();/*函数声明*/int
33、,;printf(:);scanf(,);,();(,);();(,);();(,);,int(int,int)/*函数定义*/;();();int(int,int)/*函数定义*/;();();,int(int,int)/*函数定义*/;();void process(int,int,int(*fun)(int,int);(*)(,);(,);,10.返回指针值的函数,一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。这种带回指针值的函数,一般定义形式为类型名*函数名(参数表列);例如:*(int,int);,例
34、104 有若干个学生的成绩(每个学生有门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。,#include void main()float*score 4=60,70,80,90,56,89,67,88,34,78,90,66;float*search(float(*pointer)4,int n);float*;int,;printf(enter the number of student:);scanf(,);printf(The scores of No are:,);,search(,);for(;printf(,*();,float*search(fl
35、oat(*)4,int)float*;*();return();运行情况如下:enter the number of student:The scores of No.are:56.00 89.00 67.00 88.00,例105 对上例中的学生,找出其中有不及格课程的学生及其学生号。,#include void main()float score 4=60,70,80,90,56,89,67,88,34,78,90,66;float search(float(*pointer)4);float*;i,;,f(;)();(*()printf(scores:,);for(;)printf(,*
36、();printf();,10.指针数组和指向指针的指针,10.指针数组的概念,一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为类型名数组名数组长度;例如:*;,例106 将若干字符串按字母顺序(由小到大)输出。,#include#include void main()void sort(char*name,int n);void printf(char*name,int n);char*name=Follow me,BASIC,Great Wall,FORTRAN,Computer design;int;sort
37、(,);print(,);,void sort(char*,int)char*;int,;for(;for(;if(strcmp(name,name)0)=;if(!)temp=namei;namei=namek;namek=temp;,void print(char*,int);(;)printf(,);,运行结果为:,10 指向指针的指针,怎样定义一个指向指针数据的指针变量呢?如下:*;的前面有两个*号。*运算符的结合性是从右到左,因此*相当于*(*),显然*是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量是指向一个字符
38、指针变量的。*就是所指向的另一个指针变量。,例107 使用指向指针的指针,#include void main()char*name=Follow me,BASIC,Great Wall,FORTRAN,Computer design;char*;int;for(;);printf(,*);,例10.28 一个指针数组的元素指向整型数据的简单例子,#include void main()int,;int*num5=&a0,&a1,&a2,&a3,&a4;int*,;for(;printf(,*);,10.指针数组作函数的形参,指针数组的一个重要应用是作为main函数的形参。在以往的程序中,函数
39、的第一行一般写成以下形式:void main()括弧中是空的。实际上,main函数可以有参数,例如:void main(int argc,char*argv)argc和argv就是main函数的形参。main函数是由操作系统调用的。那么,main函数的形参的值从何处得到呢?显然不可能在程序中得到。实际上实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给main函数的参数。命令行的一般形式为命令名 参数 参数参数,如果有一个名为的文件,它包含以下的函数:void main(int,char*)();(,);在DOS命令状态下输入的命令行为则执行以上命令行将会输出以下信息:,10.有
40、关指针的数据类型和指针运算的小结,有关指针的数据类型的小结,10.8.2 指针运算小结,(1)指针变量加(减)一个整数例如:、等。,(2)指针变量赋值将一个变量地址赋给一个指针变量。如:;(将变量的地址赋给)array;(将数组首元素地址赋给)array;(将数组第个元素 的地址赋给)max;(为已定义的函数,将的入口 地址赋给);(和都是指针变量,将的 值赋给),(3)指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:;,(4)两个指针变量可以相减如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数,(5)两个指针变量比较若两个指针指向同一个数组
41、的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。,10.8.3 void指针类型,ANSIC新标准增加了一种“void”指针类型,即可定义一个指针变量,但不指定它是指向哪一种类型数据的。ANSIC标准规定用动态存储分配函数时返回void指针,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。例如:*;*;(*);,同样可以用(*)将的值转换成*类型。如:(*);也可以将一个函数定义为*类型,如:*(char ch1,char ch2)表示函数返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如对该函数调用得到的地址要进行以下转换:(*)(h,h);,