1、第十章 指针用户在程序设计中用户在程序设计中定义一个定义一个变量变量内存区每一个字节内存区每一个字节有一个编号有一个编号地址地址编译过程中:编译过程中:1、根据变量类型,分配一定长度、根据变量类型,分配一定长度的内存空间;的内存空间;2、变量名变量名转换为所分配内存的转换为所分配内存的地址地址2、用户存取数据方式、用户存取数据方式变量名变量名地址地址地址所标志的地址所标志的内存段内存段一个比方一个比方:旅旅 客客 变量数据变量数据 旅旅 客客 名名 字字 变量名变量名 旅馆房间旅馆房间 内内 存存 区区 旅馆房间号旅馆房间号 地地 址址 查找旅客查找旅客:旅客名字:旅客名字房间号码房间号码房房
2、 间间找到旅客找到旅客 存取数据存取数据:变量名变量名 内存地址内存地址内存区内存区数数 据据注意注意:内存单元的地址内存单元的地址与与内存单元的内容内存单元的内容是不同的。是不同的。内存单元内存单元地址地址旅馆房间旅馆房间号码号码 内存单元内存单元内容内容住在房间内的住在房间内的旅客旅客。内存地址内存地址 331 220 2000 2002 20042006内存用户数据区内存用户数据区内存中存内存中存放的数据放的数据3、数据存取举例、数据存取举例 内存地址内存地址3 6 9 2000 2002 2004内存用户数据区内存用户数据区变量变量 变量变量 变量变量 相加得相加得9 送到送到K占用的
3、占用的2004,20054、直接访问与间接访问、直接访问与间接访问 直接访问:直接访问:在数据存取中,直接得到变量在数据存取中,直接得到变量 i 地址然后地址然后 按变量按变量 i 的地址存取变量的地址存取变量 i 的值的方式。的值的方式。间接访问:间接访问:在数据存放中,变量在数据存放中,变量 i 的地址不是直接得到,的地址不是直接得到,而是而是存存 放在另一个变量放在另一个变量i_pointer中,须先从变量中,须先从变量i_pointer中中 获取变量获取变量 i 地址,然后按变量地址,然后按变量i地址存取变量值地址存取变量值i的方式。的方式。一个比方(取抽屉一个比方(取抽屉A中的东西)
4、:中的东西):直接访问:直接访问:直接得到钥匙直接得到钥匙A 打开抽屉打开抽屉A,取出东西取出东西(直接获得变量(直接获得变量 i 地址地址按变量按变量 i 地址存取变量的地址存取变量的 i 值值)间接访问:间接访问:钥匙钥匙A在抽屉在抽屉B中中用钥匙用钥匙B打开抽屉打开抽屉B得到钥匙得到钥匙A打开抽打开抽屉屉A取得东西取得东西变量变量i地址放在地址放在 变变量量i_pointer中中读取变量读取变量i_pointer值得到变量值得到变量i的地址的地址按变量按变量i地址存取地址存取i值值 内存地址内存地址3 6 92000 2000 2002 20043010内存用户数据区内存用户数据区变量变
5、量 变量变量 变量变量 变量变量 对地址的操作对地址的操作之前我们可以通过之前我们可以通过&运算符得到变量的地址。既然有了地址,运算符得到变量的地址。既然有了地址,就应该可以对该地址进行读写。就应该可以对该地址进行读写。int i=300;int j;j=&i;变量变量j 中存放的是一个数,是中存放的是一个数,是i的地址的地址但我们要如何才能把一个数如但我们要如何才能把一个数如 200 存放到变量存放到变量i所在的内存所在的内存中呢?中呢?最简单的方式最简单的方式 i=200;这是赋值语句,编译器会自动的把这是赋值语句,编译器会自动的把200送到送到i所在的内存单元所在的内存单元但我们如何实现
6、直接对地址操作?但我们如何实现直接对地址操作?j =200;不可行。此语句是把不可行。此语句是把200送到变量送到变量j所在内存单元,所在内存单元,对变量对变量i没有任何影响没有任何影响指针指针C语言和其他语言的一个重要区别在于:语言和其他语言的一个重要区别在于:C语言提供了一种方法,使程序员可以不通语言提供了一种方法,使程序员可以不通过变量名,而通过变量地址直接对变量所过变量名,而通过变量地址直接对变量所在的内存单元进行操作。这样,在的内存单元进行操作。这样,C语言就语言就有了低级语言的特性,直接对地址进行操有了低级语言的特性,直接对地址进行操作也更高效。作也更高效。C中要直接对地址(内存)
7、进行操作,需中要直接对地址(内存)进行操作,需要使用指针变量要使用指针变量指针和指向指针和指向 int i;int *i_pointer;定义一个指针变量定义一个指针变量 i=3;i_pointer=&i;分配一块分配一块2字节的内存,假设地址为字节的内存,假设地址为2000,并用符号,并用符号i表示这表示这个地址个地址分配一块分配一块2字节的内存,假设地址为字节的内存,假设地址为2004,并用符号,并用符号i_pointer代表这个地址代表这个地址i3;然后把然后把3写入写入i的地址,就是写到内存的地址,就是写到内存2000处处i_pointer=&i;然后把然后把i的地址的地址2000写入
8、写入i_pointer的地址,的地址,就是写到内存就是写到内存2004处处指针和指向指针和指向i_pointerC C语言提供一种特殊的表示方式:语言提供一种特殊的表示方式:i_pointer 用它表示用它表示i_pointeri_pointer所指向的内存单元所指向的内存单元20003内存内存2000i_pointer与与 i 是等效的,都表示同一变量,该变量存放地址为是等效的,都表示同一变量,该变量存放地址为2000。因此:因此:i5;i_pointer5 两语句作用相同,后者直接操作内存两语句作用相同,后者直接操作内存内存内存2004现在,内存中的情况如下现在,内存中的情况如下我们称:我
9、们称:i_pointer指向变指向变量量i,为什么?,为什么?i_pointer存放存放i的地址,通的地址,通过过i_pointer,可以找到,可以找到i问题:如何给问题:如何给i赋值为赋值为5?i_pointer=5;5*i_pointer=5;把把5写入到写入到i_pointer所指向的内存单元所指向的内存单元 510.2.1定义一个指针变量定义一个指针变量例例:int i,j;int pointer_1,pointer_2;第一条命令定义了两个整型变量第一条命令定义了两个整型变量 i,j;第二条命令定义了第二条命令定义了 两个指针变量两个指针变量 pointer_1,pointer_2,
10、它们它们指向整型变量;指向整型变量;两个整型变量两个整型变量pointer_1,pointer_2,它们与它们与 i,j为相同类型,被为相同类型,被pointer_1,pointer_2两个指针所指向;两个指针所指向;基类型基类型:指针变量的基类型是指该指针变量可以指向的变量的指针变量的基类型是指该指针变量可以指向的变量的类型;类型;定义指针变量的一般形式定义指针变量的一般形式:基类型基类型 指针变量名指针变量名例:例:float pointer_3;(pointer_3是指向实型变量的指针变量,即其中存放是指向实型变量的指针变量,即其中存放的应该是一个的应该是一个float类型变量的地址)类
11、型变量的地址)char pointer_4;(pointer_4是指向字符型变量的指针变量)是指向字符型变量的指针变量)pointer_1=&ipointer_1ipointer_1i执行赋值语句之前执行赋值语句之前执行赋值语句之后执行赋值语句之后例例10.2.2指针变量的引用指针变量的引用说明说明1:指针变量中只能存放地址(指针),而且指针变量中只能存放地址(指针),而且“地址地址”也有类型,不能把其它类型的数据赋给指针变量。也有类型,不能把其它类型的数据赋给指针变量。例例:int p;为整型指针变量为整型指针变量 float f;p=&f;p=100;都为非法赋值。实际运行时,编译器只提出
12、警告,程都为非法赋值。实际运行时,编译器只提出警告,程序仍可以运行,但是往往会导致系统错误。序仍可以运行,但是往往会导致系统错误。-“该程序执行了非法操作该程序执行了非法操作”.遇到问题问题需要关闭,我们对此引起的不便遇到问题问题需要关闭,我们对此引起的不便表示抱歉表示抱歉思考思考指针变量也是变量,指针变量占多少字节指针变量也是变量,指针变量占多少字节呢?它的大小又有什么含义呢?呢?它的大小又有什么含义呢?i_pointer20003内存内存2000内存内存2004整型变量整型变量i在在16位计算机中指针是位计算机中指针是2字节,在字节,在32位计算机中是位计算机中是4字节:字节:4字节表示指
13、针变量字节表示指针变量i_pointer占占4字节,而不管它所指向的字节,而不管它所指向的是什么,即指针变量只和编译器相关,和指向类型无关是什么,即指针变量只和编译器相关,和指向类型无关指针的大小表示了它的指针的大小表示了它的”寻址能力寻址能力“,4字节最多可以表示字节最多可以表示的地址是:的地址是:0 xffffffff,即,即2324G指针的运算符指针的运算符有关的两个运算符:有关的两个运算符:(1)&:取地址运算符;:取地址运算符;(2):指针运算符(或称为:指针运算符(或称为“间接访问间接访问”运算符)运算符)例:例:&a为变量为变量a的的地址地址,p为指针变量为指针变量p所指所指向的
14、向的存储单元存储单元;注意:注意:*有多重含义:有多重含义:int*p;*p=3;p*3;例程说明例程说明 例例二二.指针变量的引用指针变量的引用例例10.1main()int a ,b;int *p1 ,*p2;a=100;b=10;p1=&a;p2=&b;printf(“%d,%d n”,a,b);/*直接访问方直接访问方式式*/printf(“%d,%d n”,*p1 ,*p2);/*间接访间接访问问 结果结果:100,10 100,10 程序中程序中:*p1表示指针变量表示指针变量 p1所指向的变量所指向的变量,即即 a.*p2表示指针变量表示指针变量 p2所指向的变量所指向的变量,即
15、即 b.p1 a&a&b10010p2 b *p1*p2关于关于&与与*运算符的说明运算符的说明:1:&是取地址运算符是取地址运算符.如如:&a ,&b 等等.*是指针运算符是指针运算符.用于定义时表示其后的标识用于定义时表示其后的标识符是指针变量符是指针变量.而在程序中而在程序中*p 则表示指针变量则表示指针变量 p所指向的变量所指向的变量,即即目标变量目标变量。2.&,*,+,同优先级同优先级,按从右至左方向结合按从右至左方向结合.a:如如:int a,*p1,*p2;p1=&a;则则:&*p1 与与&a 等效等效 p2=&*p1;或或 p2=&a;则则:p2 也指向变量也指向变量 a.b
16、:如如:int a,*p1;p1=&a;则则:*&a 与与*p1 等效等效,即等价于变量即等价于变量a.c:如如:int a,*p1;p1=&a;则则:(*p1)+等价于等价于 a+但注意但注意 *p1+不等价于不等价于 (*p1)+&ap1*p1*&a a*p1+等价于等价于*(p1+)即先得即先得 p1 所指向变量的值所指向变量的值,再使再使指针变量指针变量 p1的值自增的值自增,此时此时 p1 已不在指向原来的变量了已不在指向原来的变量了.例例 10.2 main()int *p1,*p2,*p,a,b;scanf(“%d,%d”,&a,&b);p1=&a;p2=&b;if(a b)p=
17、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=5 (注意注意:本程序是采用交换变量本程序是采用交换变量 a 和和 b 的地址来的地址来实现两个数的比较的实现两个数的比较的.且比较前后且比较前后 a,b 的值并未的值并未发生变化发生变化)&a&b59&b&a95p1p2p pp1p2aba bswap(int p1,int p2)int p;p=p1;p1=p2;p2=p;main()int a,b;
18、scanf(“%d,%d”,&a,&b);if(ab)swap(a,b);printf(“n%d,%d n”,a,b);为什么为什么C语言不让实参和形参双向传递呢语言不让实参和形参双向传递呢?1.这种情况会出错这种情况会出错:swap(2,3);2.大多数情况大多数情况,函数参数都是单向传递的函数参数都是单向传递的,通过返回值通过返回值得到结果得到结果,这样更符合结构化程序设计的思想这样更符合结构化程序设计的思想,在不在不主动要求改变的情况下主动要求改变的情况下,函数不应该改变实参的值函数不应该改变实参的值,否则可能导致程序设计时情况变得复杂否则可能导致程序设计时情况变得复杂三三.指针变量作为
19、函数的参数指针变量作为函数的参数 指针变量可以作函数的参数指针变量可以作函数的参数,其作用是将一个其作用是将一个变量的变量的地址地址传送到另一个函数中。传送到另一个函数中。例例 10.3 swap(int *p1,int *p2)int p;p=*p1;*p1=*p2;*p2=p;main()int a=1,b=2,*pa ,*pb;pa=&a;pb=&b;swap(pa,pb);printf(“%d,%d n”,a,b);swap(int *p1,int *p2)int p;p=*p1;*p1=*p2;*p2=p;main()int a=5,b=9,*pa ,*pb;pa=&a;pb=&b;
20、swap(pa,pb);printf(“%d,%d n”,a,b);abpap1p2p向向00写写5向向02写写9000204080a0cpb59060002调用调用swap交换交换0002595 如果把如果把 swap 函数改成函数改成:swap(int *p1,int *p2)int *p;*p=*p1;(此句有问题此句有问题)*p1=*p2;*p2=*p;又如又如:swap(int x,int y)int t ;t=x;x=y;y=t;P无确定的地址值无确定的地址值,它,它的的值不可预见,的的值不可预见,*p所指向的单元也是不可所指向的单元也是不可预见,对预见,对*p赋值可能赋值可能会破
21、坏系统的正常状态会破坏系统的正常状态。注意参数的传递注意参数的传递类型为类型为“值传递值传递”55995995abxya bxy(a)(b)说明说明:1.不能通过改变不能通过改变形参指针变量的值形参指针变量的值而使而使实参指针变实参指针变量的值量的值改变改变.2.可以通过改变形参指针变量所指向的变量可以通过改变形参指针变量所指向的变量的值来改变实参指针变量所指向的变量的值的值来改变实参指针变量所指向的变量的值.3.如想通过函数调用得到如想通过函数调用得到 n 个要改变的值个要改变的值,可以可以:(1)在主调函数中设在主调函数中设 n 个变量个变量,并用并用 n 个指针变量指个指针变量指 向它们
22、向它们;(2)将指针变量作实参将指针变量作实参,使使 n 个变量的地址传给所调个变量的地址传给所调用的函数形参用的函数形参;(3)通过形参指针变量通过形参指针变量,改变该改变该 n 个变量的值个变量的值;(4)主调函数中就可以使用这些改变了值的变量主调函数中就可以使用这些改变了值的变量。swap(int *p1,int *p2)int *p;p=p1;p1=p2;p2=p;main()int a=1,b=2,*pa ,*pb;pa=&a;pb=&b;swap(pa,pb);printf(“n%d,%d n”,a,b);本函数中试图通过改变形参本函数中试图通过改变形参指针变量的值来使实参指针指针
23、变量的值来使实参指针变量的值改变变量的值改变.swap(int *p1,int *p2)int *p;p=p1;p1=p2;p2=p;main()int a=1,b=2,*pa ,*pb;pa=&a;pb=&b;swap(pa,pb);printf(“%d,%d n”,a,b);abpap1p2p向向00写写1向向02写写2000204080a0cpb12060002调用调用swap交换交换0002交换交换p1,p2的值的值0200虽然改变了虽然改变了“指向指向”,但没有,但没有改变实参的值,即改变实参的值,即Pa和和Pb没变,没变,a,b也没有变也没有变printf(“%d%d n”,*pa
24、,*pb);总结:指针总结:指针(地址地址)做为函数参数的意义做为函数参数的意义:因为变量的作用域的关系因为变量的作用域的关系,调用函数和被调用函数中调用函数和被调用函数中的变量尽管名字可以相同的变量尽管名字可以相同,但内存地址不同但内存地址不同.实参和形参实参和形参的内存地址也不同的内存地址也不同,而且是而且是”单向传递单向传递”,想要通过形参想要通过形参改变而影响实参是做不到的改变而影响实参是做不到的,要在被调用函数中通过要在被调用函数中通过”变量名变量名”对调用函数中的变量操作也不可行对调用函数中的变量操作也不可行.唯一的办法是唯一的办法是,在被调用函数中直接对在被调用函数中直接对”调用
25、调用”函数函数中变量的中变量的”内存地址内存地址”操作操作.可行的原因在于可行的原因在于”函数函数”中局部变量的中局部变量的”生存期生存期”是随着函数的消亡而消亡的是随着函数的消亡而消亡的,主调函数还没有结束时主调函数还没有结束时,其中的局部变量仍然存在于内其中的局部变量仍然存在于内存中存中,只是由于种种原因只是由于种种原因,通过变量名无法访问通过变量名无法访问.最终的手段是直接操作地址最终的手段是直接操作地址,而函数间的通信可以通而函数间的通信可以通过参数传递过参数传递,所以所以,把地址做为参数就达到了我们的目的把地址做为参数就达到了我们的目的思考思考:既然这样既然这样,假设知道了假设知道了
26、”被调用函数被调用函数”中某个中某个变量地址变量地址,我们是否可以直接在我们是否可以直接在”主调用函数主调用函数”中中直接对直接对”被调用函数被调用函数”中的变量进行操作呢中的变量进行操作呢?#include int*test(int x)int k=111;return&k;int main(int argc,char*argv)int a=5;int*pa;pa=test(a);printf(%dn,*pa);return 0;#include int*test(int x)int k=111;return&k;void tt()int t=3,b=4;b=t;int main(int a
27、rgc,char*argv)int a=5;int*pa;pa=test(a);tt();printf(%dn,*pa);return 0;什么时候需要地址传递:什么时候需要地址传递:1.传递整个数组传递整个数组 2.需要在被调用函数中修改调用函数需要在被调用函数中修改调用函数中的变量中的变量 3.需要多个需要多个”返回值返回值”,但不想使用全但不想使用全局变量局变量,可以在调用函数中设置多个指针可以在调用函数中设置多个指针,而被调用函数中则把要而被调用函数中则把要”返回的值返回的值”写入写入到相应的内存地址到相应的内存地址例例 10.4输入输入a,b,c3个整数,按大小顺序输出个整数,按大小
28、顺序输出swap(pt1,pt2)int *pt1,*pt2;int p;p=*pt1;*pt1=*pt2;*pt2=p;exchange(int *q1,*q2,*q3;)if(*q1 *q2)swap(q1,q2);if(*q1 *q3)swap(q1,q3);if(*q2 *q3)swap(q2,q3);main()int a,b,c,*p1,*p2,*p3;scanf(“%d,%d,%d”,&a,&b,&c);p1=&a;p2=&b;p3=&c;exchange(p1,p2,p3);printf(“n%d,%d,%dn”,a,b,c);10.3 数组的指针和指向数组的指针变量数组的指针
29、和指向数组的指针变量 C语言中,指针变量可以指向变量,也可以指向数组语言中,指针变量可以指向变量,也可以指向数组和数组元素和数组元素.数组的指针数组的指针:数组的起始地址数组的起始地址.数组元素的指针数组元素的指针:数组元素的地址数组元素的地址.一一.指向数组的指针变量的定义与赋值指向数组的指针变量的定义与赋值 指向数组的指针变量的定义同指向变量的指针变量指向数组的指针变量的定义同指向变量的指针变量的定义相同的定义相同.如如:int a10;int *p ;若若:p=&a0;则则 p 指向了指向了a 数组的第数组的第 0 号元素号元素.由于由于数组名代表数组的首地址数组名代表数组的首地址(即起
30、始地址即起始地址).故故:p=&a0;等价于等价于 p=a;也可也可:int a10;int *p=&a0;int*p=a;注意注意 int *p=&a0;的含义是将数组首地址赋给指的含义是将数组首地址赋给指针变量变量 p,而不是赋给而不是赋给(*p).二二.通过指针引用数组元素通过指针引用数组元素 如如:int a10 ,*p ;p=a;则则:(1)p a0的地址的地址;p+1 a1的地址的地址;p+i ai的地址的地址.(2)*p=a0,*(p+1)=a1,*(p+i)=ai 说明说明:1.数组元素在内存中是连续存放的数组元素在内存中是连续存放的,C语言语言规定规定,指针变量指针变量 p+
31、1 指向下一个元素指向下一个元素(不是简单的不是简单的加加 1).&a0 1 320 a0a1a9p2.(p+i)表示指向表示指向 ai 的地址的地址,而而 a+i 也表示也表示 ai 的地址的地址,故故 程序中程序中(p+i)等价于等价于a+i.如如:p+2;a+2;3.指向数组的指针变量可以带下标指向数组的指针变量可以带下标.如如:pi *(p+i)a数组数组 p p+1,a+1 p+i,a+i p+9,a+9 a0 a1 ai a9*(p+i)综上所述综上所述:数组元素的引用可以数组元素的引用可以:(假定假定:int a10 ,*p=a;)(1)下标法下标法:数组名数组名下标下标 或或
32、指针变量名指针变量名下标下标 ai pi(2)指针法指针法:*(p+i)或或 *(a+i)思考:思考:数组的指针数组的指针 和和 数组名有何数组名有何异同?异同?int a10,b10 ,*p=a;相同点:相同点:p和和a都表示地址,都可以进行地址的运都表示地址,都可以进行地址的运算,如:算,如:p+1,a+1,p5,a5,*a 等等不同点:不同点:p是变量,可以被改变。而数组名是变量,可以被改变。而数组名a是是“常量常量”,只能引用不能被改变(赋值),只能引用不能被改变(赋值)p+1,a+3,p+可以,可以,a+错错p=p3;可以,;可以,a=a+3错错p=b;p=&b2 可以,可以,a=&
33、b3,a=b 错错例例 10.5 用四种方法输出数组各元素用四种方法输出数组各元素.(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)用数组名计算元素地址用数组名计算元素地址.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);例例 10.5 用四种方法输出数组各元素用四种方法输出数组各元素.(3)指
34、针下标指针下标 main()int a10,i,*p=a;for(i=0;i 10;i+)scanf(“%d”,&pi);printf(“n”);for(i=0;i 10;i+)printf(“%d”,pi);(4)指针计算指针计算 main()int a10,i,*p=a;for(i=0;i 10;i+)scanf(“%d”,p+i);printf(“n”);for(i=0;i 10;i+)printf(“%d”,*(p+i);main()int a10,i,*p=a;for(i=0;i 10;i+)scanf(“%d”,p+i);注意是注意是p+i不是不是&(p+i)printf(“n”)
35、;for(i=0;i 10;i+)printf(“%d”,*(p+i);思考:既然指针可以替代数组,那么上面的程序中思考:既然指针可以替代数组,那么上面的程序中是否可以省略掉数组定义,只使用指针呢?像下面是否可以省略掉数组定义,只使用指针呢?像下面这样这样:main()int i,*p;for(i=0;i 10;i+)scanf(“%d”,p+i);for(i=0;i 10;i+)printf(“%d”,*(p+i);1.指针指向不确定指针指向不确定2.即使指针指向确定,但系统只给指针分配了一块即使指针指向确定,但系统只给指针分配了一块内存,只有这块内存才可以内存,只有这块内存才可以“安全,合
36、理安全,合理”使用使用的的3.如果有一块连续的安全内存可用,当然可以不用如果有一块连续的安全内存可用,当然可以不用数组数组4.注意上面条件,注意上面条件,“连续连续”,如果不连续,又有什,如果不连续,又有什么问题?么问题?注意几点注意几点:(1)指针变量可以作自增指针变量可以作自增,自减运算自减运算.如如:+p ,p .而数组名不能作自增而数组名不能作自增,自减运算自减运算.如如 a+,a 等等,均不合法均不合法.因为因为数组名是常量数组名是常量.(2)注意指针变量的当前值注意指针变量的当前值.如如:例例 10.6:main()int a10,i,*p;p=a;for(i=0;i 10;i+)
37、scanf(“%d”,p+);printf(“n”);for(i=0;i 10;i+,p+)printf(“%d”,*p);(3)注意指针变量的运算注意指针变量的运算.如如:int a10,i,*p;p=a;则则:a.p+(或或 p+=1)表示表示 p 指向指向 a1,此时若执行此时若执行*p,则则 取出取出 a1 元素的值元素的值.b.“*”与与“+”同优先级同优先级,自右往左结合自右往左结合.如如:*p+等效于等效于*(p+),即先取即先取 p 所指向所指向变量的值变量的值,再使再使 p+1.而而*(p+)与与*(+p)的作用不同的作用不同.前者先取前者先取*p 的值的值,后使后使p+1;
38、后者后者是先使是先使 p+1,再取再取*p的值的值.d.(*p)+表示使目标变量的值加表示使目标变量的值加 1.而不是指针变量的而不是指针变量的值加值加 1。e.*(p+)等价于等价于 ai+.*(+p)等价于等价于 a+i 即先使即先使 p 自增自增,再作再作*运算运算.如如:main()int a100,*p;p=a;while(p a+100)printf(“%d”,*p+);main()int a100,*p;p=a;while(p a+100)printf(“%d”,*p);p+;三三.数组名作函数参数数组名作函数参数 数组名作函数参数时数组名作函数参数时,实际上是将实际上是将实参实
39、参数组的首地址传给形参数组的首地址传给形参。这样实参数组与这样实参数组与形参数组共占同一段内存形参数组共占同一段内存。使得在。使得在 调用函调用函数过程中,形参数组中元素值发生变化也数过程中,形参数组中元素值发生变化也就使实参数组的元素值随之而发生变化。就使实参数组的元素值随之而发生变化。如:如:main()int array10;f(array,10);f(arr,n)int arr ,n;arrayarr1)实参传递给形参后实参传递给形参后arr和和arry指向同一内存地址;指向同一内存地址;2)将将“10”传递给形参传递给形参n后,后,界定了形参数组的范围;界定了形参数组的范围;3)这种
40、这种“数组名数组元素数组名数组元素个数个数”的参数形式是常用的。的参数形式是常用的。例例 10.7 将数组将数组 a 中中 n 个整数按相反顺序存放个整数按相反顺序存放.题意分析:题意分析:本题的关键是最后交换的两个元素的上下标值的确本题的关键是最后交换的两个元素的上下标值的确定。即将第一个元素和最后一个元素对换定。即将第一个元素和最后一个元素对换,将第二个同倒将第二个同倒数第二个对换数第二个对换.即两两对换即两两对换,直到直到:a(n 1)/2 与与 an int(n 1)/2)对换为止。其中对换为止。其中an int(n 1)/2)与与an 1 (n 1)/2)等效。等效。3 7 9 11
41、 0 6 7 5 4 2 2 4 5 7 6 0 11 9 7 3imjvoid inv(x,n)int x ,n;int t,i,j,m=(n 1)/2;for(i=0;i=m;i+)j=n1i;t=xi;xi=xj;xj=t;return;main()static int i,a10=3,7,9,11,0,6,7,5,4,2;printf(“The original array :n”);for(i=0;i10;i+)printf(“%d”,ai);printf(“n”);inv(a,10);printf(“The array has been inverted:n”);for(i=0;i
42、 10;i+)printf(“%d”,ai);printf(“n”);例10.8 从 10 个数中找出其中最大值和最小值int max,min;void max_min_value(int array,int n)int *p,*array_end;array_end=array+n;max=min=*array;for(p=array+1;pmax)max=*p;else if(*p min)min=*p ;main()int i,number10;printf(“enter 10 data n”);for(i=0;i10;i+)scanf(“%d”,&numberi);max_min_va
43、lue(number,10);printf(“max=%d,min=%d n”,max,min);等效于等效于*(array+0)即即 array0此例也可改用指针变量来传送地址此例也可改用指针变量来传送地址,程序可改为程序可改为:int max ,min;void max_min_value(int*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 ;return;main()int i,number10 ,*p;p=
44、number;printf(“enter 10 data n”);for(i=0;i 10;i+,p+)scanf(“%d”,p);printf(“the 10 data:n”);for(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+)等效于:等效于:不使用全局变量void max_min_value(int*array,int n,int*max,int*min;)int *p
45、 ,*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 ,max,min;for(i=0;i 10;i+,p+)scanf(“%d”,&number i );max_min_value(number,10,&max,&min);printf(“max=%d,min=%d n”,max,min);数组名是地址,故可以传送给 int *array 综上所述综上所述,对于实参数组对于实参数组,想在被调函数中想在被调函
46、数中改变此数组改变此数组元素的值元素的值,实参与形参的对应关系实参与形参的对应关系可以如下可以如下:(1)二者都用数组名二者都用数组名 main()int a10;f(a,10);f(x,n)int x ,n;特点特点:a 和和 x 数组共用同数组共用同一段内存单元。一段内存单元。(2)实参为数组名实参为数组名,形参用形参用指针变量指针变量.main()int a10;f(a,10);f(x,n)int *x ,n;特点特点:实参将数组的首地址传给实参将数组的首地址传给形参指针变量形参指针变量,通过指针变量指通过指针变量指向数组中的任一元素向数组中的任一元素,进而作相进而作相应的处理。应的处理
47、。(3)二者都用指针变量。二者都用指针变量。main()int a10 ,*p;p=a;f(p,10);f(x,n)int *x ,n;特点特点:先使先使 p 指向指向 a 数组数组,再将再将 p 传给传给 x,使使 x 也指向也指向 a 数组数组,从而进行处理从而进行处理.(4)实参为指针变量实参为指针变量,而形参而形参为数组名。为数组名。main()int a10 ,*p;p=a;f(p,10);f(x,n)int x ,n;特点特点:利用指针变量将利用指针变量将 a 数组数组的首地址传给的首地址传给 x 数组数组.使两数组使两数组共用同一段内存单元共用同一段内存单元.利用利用xi值值的变
48、化的变化,使使ai的值也发生变化的值也发生变化.注意注意:在上述四种处理方式中在上述四种处理方式中,当用当用指针变量作实指针变量作实参时参时,必须先使指针变量有确定的值必须先使指针变量有确定的值,即指向一即指向一个已定义的数组个已定义的数组。四、四、指针与二维数组指针与二维数组1、指针与二维数组、指针与二维数组 一个数组的名字代表该数组的首地址,并一个数组的名字代表该数组的首地址,并可看成是地址常量,这一规定对二维数组或更高可看成是地址常量,这一规定对二维数组或更高维数组同样适用。维数组同样适用。若有定义:若有定义:float*p,d35;则:则:d24d23d22d21d20d14d13d1
49、2d11d10d04d03d02d01d00d2d1d0d2.二维数组元素和二维数组元素的地址二维数组元素和二维数组元素的地址假设数组名为假设数组名为a,起始地址设为起始地址设为200 int a34=1,3,5,7,9,11,13,15,17,19,21,23;则:则:a代表整个二维数组的首地址,即第代表整个二维数组的首地址,即第0行的首地址行的首地址 a+1是数组是数组a第第1行首地址行首地址(208)a0,a1,a2是二维数组中三个一维数组的名字(地是二维数组中三个一维数组的名字(地址),是第址),是第0行,第行,第1行,第行,第2行的首地址,即:行的首地址,即:a0=a+0、a1=a+
50、1、a2=a+2 ai+j是第是第i行行j列的地址列的地址*(ai+j)是该地址存储的值,是该地址存储的值,即即aij考虑考虑 *(a2+3)=?a0a1a2a数组数组a(200)a+1a+2(216)注意:注意:ai和和*(a+i)无条件等价无条件等价 a+i、ai、*(a+i)、&ai0均表示第均表示第i行首地址;行首地址;&aij、ai+j、*(a+i)+j都是第都是第i行行j列元素的列元素的地址地址;aij、*(ai+j)、*(*(a+i)+j)都是第都是第i行行j列元素的列元素的值值;例例:将将a矩阵与矩阵与b矩阵相加矩阵相加,和存入和存入c矩阵。矩阵。int i,j;main()i