C语言程序设计章指针_第1页
C语言程序设计章指针_第2页
C语言程序设计章指针_第3页
C语言程序设计章指针_第4页
C语言程序设计章指针_第5页
已阅读5页,还剩69页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1第十章第十章 指指 针针 指针是指针是C C语言的精华。语言的精华。 利用指针可以实现:利用指针可以实现:复杂的数据结构复杂的数据结构;动态分配内存动态分配内存;灵活处理字符串和数组灵活处理字符串和数组;直接处理内存地址直接处理内存地址;从函数调用从函数调用中变相获取多个返回值中变相获取多个返回值等。等。210.1 10.1 指针的概念指针的概念 用户在程序中定义一个用户在程序中定义一个变量变量; 编译系统根据编译系统根据变量类型变量类型,分配一定长度的存储空间,并确定相,分配一定长度的存储空间,并确定相应的存储格式;应的存储格式; 将将变量名变量名转换为所分配内存的转换为所分配内存的地址地

2、址(内存区中每一个字节都有(内存区中每一个字节都有一个编号,这就是一个编号,这就是“地址地址”) ; 用户在程序中通过使用变量名来存取内存单元中的内容(变量用户在程序中通过使用变量名来存取内存单元中的内容(变量的值),这就是的值),这就是“直接访问方式直接访问方式”。变量名变量名地址地址地址所标志的地址所标志的内存单元内存单元存取数据存取数据3则编译时系统分别为则编译时系统分别为 i , j , k 各分配各分配 2 个字节的内存单元。个字节的内存单元。如左图如左图: 2000H , 2001H i 2002H , 2003H j 2004H , 2005H k例如例如: : int i ,

3、j , k ; i = 3 ; j = 6 ; k = 9 ; printf(“%d %d %d”, i, j, k) ;2000H变量变量 i变量变量 j变量变量 k用户数据区用户数据区3692002H2004H4 这里我们要讲一种这里我们要讲一种特殊的变量特殊的变量,这种变量,这种变量专门用于存放专门用于存放变量的地址变量的地址,这就是,这就是“指针变量指针变量”。这个指向变量所在存储。这个指向变量所在存储单元的地址,就叫做单元的地址,就叫做“指针指针”。 通过访问通过访问我们可以知道相应我们可以知道相应,再通,再通过这个地址我们就可访问到相应的过这个地址我们就可访问到相应的,这种访问方式

4、就叫,这种访问方式就叫做做“间接访问方式间接访问方式”。 比如:我们用一个指针变量比如:我们用一个指针变量i_pointeri_pointer来存放整型变量来存放整型变量i i的地址(的地址(i_pointer=&i;i_pointer=&i;),再通过),再通过i_pointeri_pointer去去变量变量i i中的值。如下图所示:中的值。如下图所示: 前面我们讲过,前面我们讲过,C C 语言中可以定义多种类型的变量来存语言中可以定义多种类型的变量来存放不同类型的数据。放不同类型的数据。52000Hi33直接访问方式的直接访问方式的写写操作:操作:将将3 3送到变量送到变量

5、i i 所占的内存单元中所占的内存单元中间接访问方式的间接访问方式的写写操作:操作:将将3 3送到变量送到变量 i_pointer i_pointer 所所指向指向的内存单元中的内存单元中2000Hi 2000Hi_pointer3010H332000H变量变量 i变量变量 j变量变量 k用户数据区用户数据区3692002H2004H2000H变量变量 i_ pointer3010H610.2.1 10.2.1 指针变量的定义指针变量的定义 格式:格式:基类型基类型 * *指针变量名指针变量名 说明:基类型是指该指针变量可以指向的变量的类型。说明:基类型是指该指针变量可以指向的变量的类型。 例

6、如例如: : int i, j;int i, j; int int * *pointer_1, pointer_1, * *pointer_2;pointer_2; float float * *p1, p1, * *p2;p2; 若有若有 pointer_1 = &i; pointer_2 = &j ; pointer_1 = &i; pointer_2 = &j ; 则则 &ipointer_1 i&jpointer_2 j10.2 10.2 变量的指针和指向变量的指针变量变量的指针和指向变量的指针变量7 1 1、基类型不同的指针变量之间不能

7、相互赋值,比如:基类型不同的指针变量之间不能相互赋值,比如: p1=pointer_1; p2=pointer_2; p1=pointer_1; p2=pointer_2; 是是不合法不合法的。的。 2 2、不能把一个整型量或其它任何非地址类型的数据赋给一不能把一个整型量或其它任何非地址类型的数据赋给一个指针变量。如:个指针变量。如:int int * *p ; p ; p = 100;p = 100;(不合法)(不合法) 但但C C语言规定:可以直接给指针赋一个语言规定:可以直接给指针赋一个“空值空值” ,方法,方法是是p=NULL p=NULL 或或p=0 p=0 或或p=0p=0 。这并

8、不意味着。这并不意味着 p p 指向地址为指向地址为0 0 的单元,而是不指向任何单元,但的单元,而是不指向任何单元,但 p p 中有确定的值。中有确定的值。 3 3、(补充)、(补充)指针变量所占内存空间的长度跟基类型和计算指针变量所占内存空间的长度跟基类型和计算机的字长没有关系,准确地说,是跟段大小和编译模式有关。机的字长没有关系,准确地说,是跟段大小和编译模式有关。具体占多少字节,我们可以使用具体占多少字节,我们可以使用sizeofsizeof测出。测出。比如:比如:char a=y; char char a=y; char * *p=&a; p=&a; printf(“

9、%d”,sizeof(p);printf(“%d”,sizeof(p);输出值为输出值为2 2。对于对于TC2TC2环境下,指针变量通常占环境下,指针变量通常占2 2个字节。个字节。注意注意: :8补充:补充:指针的长度并非跟计算机字长有关,准确地说,是跟段大小指针的长度并非跟计算机字长有关,准确地说,是跟段大小和编译模式有关。你所看到的和编译模式有关。你所看到的1616位位cpucpu指针两个字节、指针两个字节、3232位位cpucpu指针指针四个字节其实是一种错觉。四个字节其实是一种错觉。 指针所指向的实体,无非是指针所指向的实体,无非是_code_code、_data_data、_bss

10、_bss、constconst和和stackstack段。指针的值是这些段的段内偏移,段内偏移结合段寄存器段。指针的值是这些段的段内偏移,段内偏移结合段寄存器的段值(实模式)或者段描述子(保护模式)构成实际的物理地址的段值(实模式)或者段描述子(保护模式)构成实际的物理地址。VCVC等编译器工作在保护模式,在保护模式中,一个段最大可以是等编译器工作在保护模式,在保护模式中,一个段最大可以是4G4G,其偏移量由,其偏移量由3232位表示,因此位表示,因此VCVC的指针是四个字节;而的指针是四个字节;而tc2.0tc2.0等等编译器工作在实模式,一个段最大编译器工作在实模式,一个段最大64k64k

11、,偏移量以,偏移量以1616位表示,因此位表示,因此在在minimini、smallsmall、mediummedium三种模式下,三种模式下,tc2.0tc2.0的指针通常是两个字节的指针通常是两个字节,运行在,运行在windowswindows中的中的tc2.0tc2.0工作在虚拟工作在虚拟80868086模式,这个模式通过分模式,这个模式通过分页把页把tc2.0tc2.0的工作空间映射回实模式使用的第一个的工作空间映射回实模式使用的第一个1M1M内存,段大小内存,段大小仍然是仍然是64k64k,因此指针大小仍然是,因此指针大小仍然是1616位,但是,有一点不同的是,位,但是,有一点不同的

12、是,工作在工作在compactcompact、lardgelardge和和hugehuge模式的指针,默认是包含模式的指针,默认是包含1616位的段位的段值的,因此这三个模式下的指针,默认是四个字节的,当然,在值的,因此这三个模式下的指针,默认是四个字节的,当然,在minimini、smallsmall和和mediummedium模式中也可以使用包含段值的指针,模式中也可以使用包含段值的指针,tc2.0tc2.0的的非标准扩展关键字非标准扩展关键字farfar和和hugehuge就是起到这个作用,使用就是起到这个作用,使用farfar和和hugehuge修修饰的指针例如饰的指针例如char h

13、ugechar huge* *p p;其长度为四个字节。;其长度为四个字节。9例例10.1 ( p159 ) main ( ) int a=100 , b=10 ; int *p1 , *p2 ; p1 = &a ; p2 = &b ; printf ( “%d, %dn ”, a , b ) ; “直接访问方式直接访问方式” printf ( “%d, %dn ”, *p1, *p2 ) ; “间接访问方式间接访问方式” 10.2.2 10.2.2 指针变量的引用指针变量的引用p1100 a *p1&a两个有关的运算符:两个有关的运算符:1 1)& &

14、:取址运算符:取址运算符2 2)* *:指针运算符,或称间接访问运算符。表示这样一个操作:指针运算符,或称间接访问运算符。表示这样一个操作:指向指向地址所标志的存储单元。地址所标志的存储单元。注意注意要和定义指针变量时要和定义指针变量时所用的指针类型标识符所用的指针类型标识符* * 相区别,它不是运算符。相区别,它不是运算符。&ap210 b*p2&b&b10 a:a: 若有:若有: int a=3, *p1, *p2; p1=&a ; 则:则: & &* *p1p1 是什么含义?是什么含义?&ap1*p1*&a a运算符运算符&

15、amp; &与与* *在实际应用中的几点说明在实际应用中的几点说明: : c:c: 如如: int a , *p1 ; p1 = &a ; 则则: ( (* *p1)+p1)+ 等价于等价于a + + 但注意但注意 * *p1+p1+ 不等价于不等价于 (*p1) + + ,因为,因为*与与+的优先的优先级相同,且结合性均为自右至左,故级相同,且结合性均为自右至左,故应等价于应等价于* *( (p1+)p1+)。 答:答:& &和和* *的优先级相同,结合性是自右向左。因此,的优先级相同,结合性是自右向左。因此,& &* *p1p1与与&

16、a &a 等效,得到的是等效,得到的是a a 的地址。的地址。p2=&p2=&* *p1; p1; 等效于等效于p2=&a ;p2=&a ;结果使结果使p2 p2 也指向变量也指向变量a a 。b:b: 同样,若有同样,若有: int a , *p1 ; p1 = &a ; 则则: * *&a&a 与与 *p1 等效等效, 即等价于变量即等价于变量a 。*11 例例10.2 10.2 输入输入a a和和b b两个整数,按先大后小的顺序输出两个整数,按先大后小的顺序输出a a和和b b 。 main ( ) int *p1, *p2

17、, *p, a, b; scanf ( “%d, %d”, &a, &b ) ; p1=&a; p2=&b; if ( ab ) p=p1 ; p1=p2 ; p2=p ; ( (进行地址交换进行地址交换) ) printf ( “ a = %d , b = %d n”, a , b ) ; printf ( “ max = %d , min = %d n ”, *p1 , *p2 ) ; 如输入: 5 , 9 则输出: a = 5 , b = 9 max = 9 , min = 512&a&b59&b&a95p1p2p pp1p

18、2aba b注意注意: : 变量变量a a和和b b的值并未交换,发生交换的是分别指向变量的值并未交换,发生交换的是分别指向变量a a、b b的指针变量的指针变量p1p1、p2p2,即改变了,即改变了p1p1、p2p2的原有指向。的原有指向。13 swap (int *p1, int *p2 ); int temp ; temp = *p1; / /* *实际上是在对主函数中的实际上是在对主函数中的a,ba,b进行交换进行交换* */ / *p1 = *p2 ; *p2 = temp ; main ( ) int a , b , *pointer_1 , *pointer_2 ; scanf

19、( “ %d , %d ” , &a , &b ) ; pointer_1 = &a ; pointer_2 = &b ; if ( a b ) swap ( pointer_1 , pointer_2 ) ; printf ( “%d , %d n ”, a , b ) ; 例例10.310.3题目同例题目同例10.210.2,即对输入的两个整数按大小顺序输出。,即对输入的两个整数按大小顺序输出。若输入若输入:5,9:5,9 输出为输出为:9,5:9,510.2.3 10.2.3 指针变量作为函数的参数指针变量作为函数的参数 指针变量可以作函数的参数指针变量可

20、以作函数的参数, ,其作用是将一个变量的其作用是将一个变量的地地址址传送到另一个函数中。传送到另一个函数中。14&a&b9abpointer_1pointer_2(a)&a&b&a&b9abp1p2pointer_1pointer_2(b)&a&a&b&b9p1pointer_1p2pointer_2ab(c)&a&b9abpointer_1pointer_2(d)注意:红色部分是注意:红色部分是在主函数中开辟的在主函数中开辟的整型整型指针变量和整型变量存储空间;指针变量和整型变量存储空间;p1p1

21、、p2p2的的值不变,但值不变,但p1p1、p2p2所指向的变量所指向的变量a a、b b的值的值要改变。恰好同例要改变。恰好同例10.210.2相反。相反。15 如果把 swap 函数改成 : swap (int * p1, int * p2 ) int *temp; *temp = *p1 ; *p1 = *p2 ; *p2 = *temp ; 如果又改为: swap ( int x , int y ) int t ; t = x ; x = y ; y = t ; 55995995abxya bxy(a) (b) temp无确定的地址值,故由无确定的地址值,故由*temp所指向的单元就是

22、不可预所指向的单元就是不可预见的,对见的,对*temp赋值可能会破坏赋值可能会破坏系统的正常状态,出现严重错误。系统的正常状态,出现严重错误。这是一个典型的没有给变量赋初这是一个典型的没有给变量赋初值就引用变量的错误。值就引用变量的错误。注意参数传递的是注意参数传递的是整型变量的值。整型变量的值。16函数调用时函数调用时&ap1&aq1&bq1在被调函数中被改为在被调函数中被改为&a p1说明说明: :1.1. 不能通过改变形参不能通过改变形参指针变量本身的值指针变量本身的值而使实参指针变量的值改变。而使实参指针变量的值改变。如下图中,如下图中,q1q1的地址值

23、变成了的地址值变成了&b&b,但返回到主调函数中,但返回到主调函数中,p1p1依然依然是是&a &a 。2.2. 可以通过改变形参可以通过改变形参指针变量所指向的变量的值指针变量所指向的变量的值来改变实参指针变量来改变实参指针变量所指向的变量的值。所指向的变量的值。 总之总之, C, C语言中实参和形参之间的数据传递语言中实参和形参之间的数据传递始终遵循单向值传递始终遵循单向值传递 。在被调函数中修改变量在被调函数中修改变量a5 a函数调用时函数调用时&aq117 C C语言中,指针变量可以指向变量,也可以指向数组和数语言中,指针变量可以指向变量,也可以

24、指向数组和数组元素。组元素。 数组的指针:数组的指针: 数组的起始地址。数组的起始地址。 数组元素的指针:数组元素的指针: 数组元素的地址。数组元素的地址。10.3.1 10.3.1 指向数组的指针变量的定义与赋值指向数组的指针变量的定义与赋值 指向数组的指针变量的定义同指向变量的指针变量的定义相同。指向数组的指针变量的定义同指向变量的指针变量的定义相同。 如:如:int a10; int *p; 若:若:p = &a0; 则则 p p 指向了指向了a a 数组的第数组的第1 个元素。个元素。 C C语言规定:数组名代表数组的首地址语言规定:数组名代表数组的首地址( (即起始地址即起始

25、地址) )。 故:故:p = &a0; 等价于等价于 p = a ; 也可使用对指针变量初始化的办法来实现以上的操作也可使用对指针变量初始化的办法来实现以上的操作: : int a10; int *p = &a0; /* 或者或者 int *p = a; */注意:注意:int *p = &a0; 的含义是:用数组的含义是:用数组a a的首地址去初始化指针变的首地址去初始化指针变量量p ,而不是初始化,而不是初始化*p 。10.3 10.3 数组的指针和指向数组的指针变量数组的指针和指向数组的指针变量1810.3.2 10.3.2 通过指针引用数组元素通过指针引用数组元

26、素若若: int a10, *p; p = a ;则:则:*p=5; 表示将数值表示将数值5赋值给赋值给a0元素。元素。 说明说明: : 1.1. 数组元素在内存中是连续存放的。数组元素在内存中是连续存放的。C C语言规语言规定,指针变量定,指针变量 p+1p+1的含义是的含义是指向下一个元指向下一个元素素 ( (注意:不是简单的加注意:不是简单的加 1 )1 ), ,即即a 5 373 a0a1a9p指针引用与数组元素的对应关系:指针引用与数组元素的对应关系: *p 等于等于 a0 , *(p +1) 等于等于 a1 , *(p +i) 等于等于 ai。指针与数组元素地址的对应关系:指针与数

27、组元素地址的对应关系: p a0的地址;的地址; p + 1 a1的地址的地址; p + i ai的地址。的地址。 实际地址为:实际地址为:p+1p+1 d d ( (d d为一个数组元素所占的字节数为一个数组元素所占的字节数) )比如数组元素为实型时,比如数组元素为实型时,p+1p+1所表示的实际地址就是所表示的实际地址就是 p+1p+1 4 4&a0192. 如果指针变量如果指针变量p已经指向已经指向a数组的起始地址,那么数组的起始地址,那么 p+i 就等价于就等价于a+i ,均表示,均表示 ai 的地址,其地址的计算方法也相同。的地址,其地址的计算方法也相同。3. 指向数组的指针

28、变量同样也可以带下标,如:指向数组的指针变量同样也可以带下标,如: pi 等价于等价于 *(p +i) 4. 实际上,对于数组元素实际上,对于数组元素ai,编译系统就是把它当作,编译系统就是把它当作*(a+i)来处理来处理的,即的,即用数组的首地址加上一个偏移量就得到了该元素的实际地用数组的首地址加上一个偏移量就得到了该元素的实际地址,从而便可实现对该元素的访问(存取)址,从而便可实现对该元素的访问(存取)。 综上所述,我们在书写程序时要实现对数组元素的引用,就可综上所述,我们在书写程序时要实现对数组元素的引用,就可以使用以下的方法:以使用以下的方法: (1)(1)下标法下标法: : 数组名数

29、组名 下标下标 或或 指针变量名指针变量名 下标下标 (2) (2)指针法指针法: : * *(p+i)(p+i) 或或 * *(a+i)(a+i) 20 例例10.5 (P212) 10.5 (P212) 用三种方法输出数组中的全部元素用三种方法输出数组中的全部元素。 (1) (1) 下标法下标法 main ( ) int a10 , i ; for ( i = 0 ; i 10 ; i + + ) scanf ( “%d”, &ai ) ; printf ( “ n” ) ; for ( i = 0 ; i 10 ; i + + ) printf ( “%d ”, ai ) ; 2

30、1(2)(2) 用数组名计算元素地址。用数组名计算元素地址。 main ( ) int a10 , i ; for ( i = 0 ; i 10 ; i + + ) scanf ( “ %d ”, &ai ) ; printf ( “ n” ) ; for ( i = 0 ; i 10 ; i + + ) printf ( “%d ”, *(a+i) ) ; 22 (3)(3) 用指针变量指向数组元素。用指针变量指向数组元素。 main ( ) int a10 , i , *p ; for ( i = 0 ; i 10 ; i + + ) scanf ( “ %d ”, &ai

31、 ) ; printf ( “ n” ) ; for ( p = a ; p (a +10) ; p + + ) printf ( “%d ”, *p ) ; 23注意几点注意几点: :(1) (1) 指针变量可以作自增、自减运算,如指针变量可以作自增、自减运算,如: + + p: + + p,p p ;而数组名不能作自增,自减运算而数组名不能作自增,自减运算. . 如如 a + +a + +, a a 等,均不合法,因为等,均不合法,因为数组名是常量数组名是常量。 (2) (2) 注意指针变量的当前值。请看以下程序注意指针变量的当前值。请看以下程序 例例 10.6 (p214)10.6 (p

32、214) main ( ) int a10 , i , *p ; p = a ; for ( i = 0 ; i 10 ; i + + ) scanf ( “ %d ”, p + + ) ; printf ( “ n” ) ; for ( i = 0 ; i 10 ; i + + , p + + ) printf ( “%d ”, *p ) ; a0a1a9ppa1024 (3)(3) 注意指针变量的运算。注意指针变量的运算。 如:如:int a10, i=0, int a10, i=0, * *p; p = a; p; p = a; 则则: : p+ + ( p+ + (或或 p + = 1

33、 )p + = 1 )的结果是的结果是 p p 指向指向a1a1,此时若,此时若执行执行* *p p,则是取出,则是取出 a1 a1 元素的值。元素的值。 “ “* *” ” 与与 “+ +” “+ +” 同优先级,自右往左结合。如:同优先级,自右往左结合。如: * *p+ + p+ + 等效于等效于 * *(p+ +)(p+ +) , 即先取出即先取出 p p 所指向的变量的值,再所指向的变量的值,再使使 p+1p+1。 但是,但是,* *(p+ +)(p+ +)与与* *(+ + p )(+ + p )的作用不同。前者先取的作用不同。前者先取 * *p p 的值,后使的值,后使 p+1;

34、p+1; 后者是先使后者是先使 p+1p+1,再取,再取 * *p p 的值。的值。 ( (* *p) + +p) + + 表示使表示使 p p 所指向的变量的值加所指向的变量的值加 1 1 ,而不是,而不是指针变量的值加指针变量的值加 1 1 。 25 main ( ) int a100 , *p ; p = a ; while ( p a +100 ) printf ( “ %d ”, *p ) ; p + + ; main ( ) int a100 , *p ; p = a ; while ( p a +100 ) printf ( “ %d ”, *p + + ) ;将将+和和- -

35、-运算符用于控制指针变量向前或向后移动,这是非常有运算符用于控制指针变量向前或向后移动,这是非常有效的。比如我们要输出效的。比如我们要输出a a数组中的数组中的100100个元素,可这样操作:个元素,可这样操作:2610.3.3 10.3.3 数组名作函数参数数组名作函数参数首先回忆例题首先回忆例题8.138.13选择法排序:选择法排序:main ( ) sort( int array , int n ) int a 10 ; sort ( a, 10 ) ; 我们知道,数组名代表着一个数组的起始地址,因此,数组名我们知道,数组名代表着一个数组的起始地址,因此,数组名作函数实参,不是传递的数组

36、各元素的值而是传递的数组的起作函数实参,不是传递的数组各元素的值而是传递的数组的起始地址。始地址。然而,能够接收并存放地址值的只可能是指针变量。实际上,然而,能够接收并存放地址值的只可能是指针变量。实际上,C编译系统就是编译系统就是将形参数组名作为指针变量来处理将形参数组名作为指针变量来处理的。因此,的。因此,以上被调函数实质上就是作为一下形式来处理的:以上被调函数实质上就是作为一下形式来处理的:sort( int *array, int n )27这样,被调函数就能够通过指针变量中的地址值去访问到这样,被调函数就能够通过指针变量中的地址值去访问到主调函数中该数组的各元素。所以,我们说主调函数

37、中该数组的各元素。所以,我们说实参数组与形参实参数组与形参数组同占一段内存空间数组同占一段内存空间。注意,在被调函数的执行过程中,如果形参数组中的元素注意,在被调函数的执行过程中,如果形参数组中的元素值发生变化就会使实参数组中的元素值也随之发生变化。值发生变化就会使实参数组中的元素值也随之发生变化。a&a0array28ij=n-1-im=(n-1)/2例例10.710.7 将数组将数组 a a 中中 n n 个整数按相反顺序存放,如下图所示:个整数按相反顺序存放,如下图所示: 即将第一个元素和最后一个元素对换,将第二个元素同即将第一个元素和最后一个元素对换,将第二个元素同倒数第二个元

38、素对换,倒数第二个元素对换,. . 以此类推,直到以此类推,直到: a: a(n1)/2(n1)/2 与与 aan1(n1)/2n1(n1)/2 对换为止。对换为止。3791106754224576011 97304929void inv ( int x , int n ) int t , i , j , m = (n 1) /2 ; for ( i = 0 ; i = m ; i + + ) j = n 1 i ; t = xi ; xi = xj ; xj = t ; return ;main ( ) int i , a10 = 3 , 7 , 9 , 11 , 0 , 6 , 7 , 5

39、 , 4 , 2 ; printf ( “ The original array : n ” ) ; for ( i = 0 ; i 10 ; i + + ) printf ( “ %d ”, ai ) ; printf ( “ n ” ) ; inv ( a , 10 ) ; printf ( “ The array has been inverted : n ” ) ; for ( i = 0 ; i 10 ; i + + ) printf ( “ %d ”, ai ) ; printf ( “ n ” ) ;30 int max , min ; void max_min_value( i

40、nt array , int n ) int *p , *array_end ; array_end = array + n ; max = min = *array ; for ( p = array + 1 ; p max ) max = *p ; else if ( *p min ) min = *p ; main ( ) int i , number10 ; printf ( “Please enter 10 data:n ”) ; for ( i = 0; i 10; i + + ) scanf ( “%d”, &numberi ) ; max_min_value( numb

41、er , 10 ) ; printf ( “ n max = %d , min = %d n ”, max , min ) ; 等效于等效于 * *(array+0)(array+0) 即即 array0array0例例10.8 (p219)10.8 (p219)从从 10 10 个数中找出其中最大值和最小值。个数中找出其中最大值和最小值。31int max , min ;void max_min_value ( int * array, int n ) int *p , *array_end ; array_end = array + n ; max = min = *array ; for

42、 ( p=array+1; p max ) max = *p ; else if ( *p min ) min = *p ; return ;主程序见下页主程序见下页 此例也可改用指针变量来传送地址此例也可改用指针变量来传送地址,程序可改为程序可改为:32main ( ) int i , number10 , *p ; p = number ; printf ( “ enter 10 data n ”) ; for ( i = 0 ; i 10 ; i + + , p + + ) scanf ( “ %d ”, p ) ; printf ( “ the 10 data : n ” ) ; fo

43、r ( p=number, i=0; i10; i+ +, p+ + ) printf ( “%d”, *p ) ; p = number ; max_min_value( p, 10 ); printf ( “ n max = %d , min = %d n ”, max , min ) ; for ( p=number; p(number+10); p+ + );提问:此语句中提问:此语句中如果不要如果不要 i 变量,应如何修改程序?变量,应如何修改程序?33综上所述,要在被调函数中实现改变实参数组元素的值,则实综上所述,要在被调函数中实现改变实参数组元素的值,则实参与形参之间的对应关系可

44、以是:参与形参之间的对应关系可以是: (1)(1) 二者都用数组名二者都用数组名 main ( ) f (int x , n ) int a10 ; f ( a , 10 ) ; (2)(2) 实参为数组名,形参用指针变量实参为数组名,形参用指针变量 main ( ) f (int *x , n ) int a10 ; f ( a , 10 ) ; 34(3)(3) 二者都用指针变量二者都用指针变量 main ( ) f ( int *x, n ) int a10 , *p ; p = a ; f ( p , 10 ) ; (4) 实参为指针变量,而形参为数组名实参为指针变量,而形参为数组名

45、main ( ) f (int x , n ) int a10 , *p ; p = a ; f ( p , 10 ) ; 例例10.910.9和和10.1010.10是用指针变量改写的程序,请是用指针变量改写的程序,请大家按以上的分析自己阅读。大家按以上的分析自己阅读。3510.3.4 10.3.4 指向多维数组的指针和指针变量指向多维数组的指针和指针变量1 1、多维数组的指针和引用方式:、多维数组的指针和引用方式:前面,我们曾讲过,可以把一个多维前面,我们曾讲过,可以把一个多维数组看成多个一维数组,这里,我们依然要使用这种思想。数组看成多个一维数组,这里,我们依然要使用这种思想。比如有比如

46、有 int a34=1,3,5,7,9,11,13,15,17,19,21,23; 1357911131517192123a2a1a0a+2a+1a说明:说明:(1) 第一个层面:二维数组第一个层面:二维数组a 有三个元素有三个元素(即三行即三行),数组名,数组名a 就是该就是该数组第数组第0个元素个元素(即第即第0行行)的地址;的地址;a+1就是跳过一个元素就是跳过一个元素(即跳过一即跳过一行行),代表第,代表第1行的首地址。假设这个二维数组在内存空间中是从行的首地址。假设这个二维数组在内存空间中是从2000H开始存放的,则开始存放的,则a的值就为的值就为2000H,a+1为为a+2*4*1

47、(即即2008H);相应地,;相应地,a+2为为a+2*4*2(即(即2010H)。)。36(2)第二个层面:第二个层面:a0、a1、a2分别是第分别是第0行、第行、第1行、第行、第2行一维数组行一维数组的数组名,他们也就是相应的一维数组第的数组名,他们也就是相应的一维数组第0列元素的地址。即列元素的地址。即a0的值就是的值就是&a00的值,同样的值,同样a1为为&a10,a2为为&a20。那么,。那么,a1+1就是就是a+2*4*1+2*1的值(即的值(即 &a11,200AH )。)。(3)下标法中的一个下标法中的一个i等价于指针法中的一个等价于指针法中的一

48、个*( +i)。例如:。例如: int b10; 其中其中b2就等价于就等价于*(b+2) 这样,我们要引用二维数组这样,我们要引用二维数组a第第i 行第行第j 列的元素,方法有以下几种:列的元素,方法有以下几种:aij,*(ai+j),*(*(a+i)+j),(*(a+i)j,*(&a00+m*i +j)。其中,。其中,a+i称为称为行指针行指针,*(a+i)+j称为称为列指针列指针,相应的运算分别称为,相应的运算分别称为行运算和列运算。行运算和列运算。对行指针作一次指针运算,变为列指针;对列指针作一次对行指针作一次指针运算,变为列指针;对列指针作一次指针运算,则变为相应的数组元素。

49、指针运算,则变为相应的数组元素。(4) 注意,注意,a 是行指针,是行指针,*a是列指针,是列指针,*a是第是第0行第行第0列的元素。列的元素。(5) 行列指针的定义:行列指针的定义:int a, b34, *p, (*pt)4; p=&a; pt=b; 若有若有pt=b0; 则编译时会报则编译时会报“可疑指针转换可疑指针转换”的警告错误。的警告错误。37例例10.11、10.12和和10.13都比较简单,自学。都比较简单,自学。下面对例下面对例10.13的程序做一点修改,请问输出结果是什么?的程序做一点修改,请问输出结果是什么?main() int a34=1,3,5,7,9,11,

50、13,15,17,19,21,23; int (*p)4, (*pt)2, i, j; p=a; pt=a; printf(“%dn”, *(*(p+1)+3); printf(“%dn”, *(*(pt+1)+3);输出结果:输出结果:15 111,3,5,7,9,11,13,15,17,19,21,231,3,5,7,9,11,13,15,17,19,21,23会有一个警告错误:会有一个警告错误:suspicious pointer conversion in function main 但程序能够运行出结果但程序能够运行出结果38main( ) void average(float *p

51、, int n); void search(float (*p)4, int n); float score34=65,67,70,60,80,87,90,81, 90,99,100,98; average(*score,12); search(score,2);例例10.14 有有3个学生,都学了个学生,都学了4门功课,请计算所有学生的所有科目门功课,请计算所有学生的所有科目的总平均成绩,以及打印输出指定学生的各科成绩。的总平均成绩,以及打印输出指定学生的各科成绩。是是*(score+0)+0的缩写,即的缩写,即score00的地址,这是一个列指针。的地址,这是一个列指针。是是score+0

52、的缩写,即的缩写,即score0的地址,的地址,这是一个行指针。这是一个行指针。2 2、多维数组的指针作函数参数、多维数组的指针作函数参数 多维数组的指针作函数参数,关键是要注意多维数组的指针作函数参数,关键是要注意实参指针和形参指针实参指针和形参指针变量的类型匹配变量的类型匹配( (包括数据类型,行列指针类型包括数据类型,行列指针类型) )问题。问题。39void 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

53、; printf(“average=%5.2fn”,aver);void search(float (*p)4, int n) int i; printf(“the score of No.%d are:n”,n); for(i=0; i y ) z = x ; else z = y ; return ( z ) ; 56 对于上例,也可在对于上例,也可在main main 函数中用指向函数的指针变量函数中用指向函数的指针变量来调用来调用max 函数,程序改写为:函数,程序改写为: main ( ) int max( int, int ) ; int (*p ) ( ) ; int a ,b,

54、 c ; p = max ; scanf ( “ %d ,%d ” , &a ,&b) ; c = (*p) (a, b); / /* * 等价于等价于c = max(a,b); c = max(a,b); * */ / printf ( “ a = %d , b = %d , max = %d ” , a , b , c ) ; 57说明说明: : 1.1. C C语言中,函数调用可以有两种形式,即语言中,函数调用可以有两种形式,即 函数名调用函数名调用和和函函数指针调用。数指针调用。2.2. ( (* *p ) ( ) p ) ( ) 表示一个指向函数的指针变量,用于存放函

55、数表示一个指向函数的指针变量,用于存放函数的入口地址。的入口地址。若把某一个函数的入口地址赋给它,它就指若把某一个函数的入口地址赋给它,它就指向该函数。这样,在程序运行过程中,一个函数指针变量向该函数。这样,在程序运行过程中,一个函数指针变量可以先后指向不同的函数。可以先后指向不同的函数。3.3. 对于指向函数的指针变量作自增或自减运算是没有意义的。对于指向函数的指针变量作自增或自减运算是没有意义的。 如如: : int (int (* *p ) ( ); p ) ( ); p+ +; p+n; p; p+ +; p+n; p; 都是毫无意义的。因为都是毫无意义的。因为 p p 只只能指向函数

56、的入口地址,而不能指向函数内的某一条指令。能指向函数的入口地址,而不能指向函数内的某一条指令。5810.5.2 10.5.2 用指向函数的指针变量作函数参数用指向函数的指针变量作函数参数 函数指针变量作函数参数时函数指针变量作函数参数时, ,它是将函数名它是将函数名( (即函数的入口地即函数的入口地址址) ) 传给形参。如:假设传给形参。如:假设f1f1、f2f2分别是两个已定义的函数,则分别是两个已定义的函数,则 函数调用:函数调用:sub( f1, f2); sub ( int (*x1 ) ( ), int (*x2 ) ( ) ) int a , b , i , j ; a = (*x

57、1 ) ( i ) ; / /* * 实现调用实现调用f1f1函数函数 * */ / b = (*x2 ) ( i , j ) ; / /* * 实现调用实现调用f2f2函数函数 * */ / f1函数函数x1f1f2函数函数x2f259 例例10.24 (P243)10.24 (P243) main ( ) int max( int, int ); / /* * 函数声明函数声明 * */ / int min( int, int ); / /* * 函数声明函数声明 * */ / int add( int, int ); / /* * 函数声明函数声明 * */ / int a , b ;

58、scanf ( “ %d , %d ”, &a , &b ) ; printf ( “ max = ” ) ; process ( a , b , max ) ; printf ( “ min = ” ) ; process ( a , b , min ) ; printf ( “ sum = ” ) ; process ( a , b , add ) ; process ( int x, int y, int (*fun) () ) int result ; result = (*fun ) ( x , y ) ; printf ( “ %dn”, result ) ; ma

59、x( int x, int y ) int z ; if (x y ) z = x ; else z = y ; return ( z ) ; min( int x, int y ) int z ; if (x y ) z = x ; else z = y ; return ( z ) ;add( int x, int y ) int z ; z = x + y ; return ( z ) ;60max函数函数add函数函数min函数函数fun值得注意的是:值得注意的是:1 1、在此例中,在此例中,只需给出不同的函数名作实参,只需给出不同的函数名作实参,processprocess函数函数就

60、可以实现不同的功能就可以实现不同的功能, ,这符合结构化程序设计方法的原则,这符合结构化程序设计方法的原则,实现了实现了代码的重复利用代码的重复利用。2 2、把函数名作为实参前,应在主调函数中做出相应的声明,把函数名作为实参前,应在主调函数中做出相应的声明,告诉编译系统它是一个函数名,而不是其它的标识符。故,告诉编译系统它是一个函数名,而不是其它的标识符。故,在上一程序中必须对在上一程序中必须对maxmax、minmin、addadd三个函数做出声明!三个函数做出声明!funfun以上程序的运行结果为:以上程序的运行结果为: 2 , 6 max = 6 min = 2 sum = 86110.

61、6 10.6 返回指针值的函数返回指针值的函数 以前我们讲过,一个函数的返回值的类型可以是字符型、以前我们讲过,一个函数的返回值的类型可以是字符型、整型、实型,甚至是整型、实型,甚至是void型,其实,函数还可以返回一个指型,其实,函数还可以返回一个指针类型的数据(即地址)。其原理和概念均与前述相同,只是针类型的数据(即地址)。其原理和概念均与前述相同,只是它返回值的类型是指针类型罢了。它返回值的类型是指针类型罢了。 返回指针类型的函数,一般定义格式为:返回指针类型的函数,一般定义格式为: 类型名类型名 *函数名函数名( 参数表参数表 ) 例如:例如:int *fun( int x, int y ) 表示函数表示函数fun的返回值的返回值是一个整型数据的指针(即该整型数

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论