《C语言课件 第07章》由会员分享,可在线阅读,更多相关《C语言课件 第07章(38页珍藏版)》请在金锄头文库上搜索。
1、第第7章章 地址和指针地址和指针7.1 地址和指针的概念地址和指针的概念7.2 指针变量的定义和指针变量的基类型指针变量的定义和指针变量的基类型7.3 给指针变量赋值给指针变量赋值7.4 对指针变量的操作对指针变量的操作7.5 指针在函数方面的应用指针在函数方面的应用7.1 地址和指针的概念地址和指针的概念7.1.17.1.1地址和指针的概念地址和指针的概念1“地址地址”的概念的概念2存储单元存储单元“地址地址”的表示的表示3“指针指针”的概念的概念7.1.27.1.2为什么使用指针为什么使用指针使使用用指指针针,实实现现了了“间间接接访访问问”变变量量,这这只只是是一一个个过过程程,其其实实
2、质质是是可可以以达达到到方方便便地地引引用用数数组组,提提高高计计算算机机的的运运行行效效率率,调调用用函函数数时时可可以以修修改改或或返返回回多多个个参参数数值值,还还可可以以描描述述更更复复杂杂的的数数据据结结构构,为为了了加加深深读读者者对对指指针针重重要要性性的的理理解解,下下面面我我们们举举一函数调用的实例。一函数调用的实例。例例7.17.1 欲想调用欲想调用fun函数,计算出两个函数,计算出两个数相加和相减的值,然后在数相加和相减的值,然后在main函数中显函数中显示结果。示结果。程序如下:程序如下:fun ( int x , int y ) int add=0 , sub=0 ;
3、 add=x+y ; sub=xy ;main ( ) int a , b , add=0 , sub=0 ; scanf ( %d %d , &a , &b ) ; printf ( a=%d , b=%d n , a , b ) ; fun ( a , b ) ; printf ( %d + %d =%d n , a , b , add ) ; printf ( %d %d =%d n , a , b , sub ) ;假设程序运行时给变量假设程序运行时给变量a输入输入8,给变,给变量量b输入输入5,程序运行结果:,程序运行结果:a=8 , b=58 + 5=08 5=0程序运行结果与我们
4、程序运行结果与我们“想象想象”的不一致,的不一致,为什么呢?其原因是为什么呢?其原因是fun 函数中变量函数中变量add和和sub与与main函数中变量函数中变量add和和sub各自占用各自占用自己的存储单元,因此自己的存储单元,因此fun函数中变量函数中变量add存放的两个数的求和值无法传回到存放的两个数的求和值无法传回到main函函数的变量数的变量add中,同理,中,同理,fun函数中变量函数中变量sub存放的两个数的相减值也无法传回到存放的两个数的相减值也无法传回到main函数的变量中。下面我们修改一下源程序,函数的变量中。下面我们修改一下源程序,借助指针,将借助指针,将fun函数中运行
5、的结果传回到函数中运行的结果传回到main函数中。函数中。7.2 指针变量的定义和指指针变量的定义和指针变量的基类型针变量的基类型7.2.17.2.1 指指针针变变量量的的定定义义和和指指针变量的基类型针变量的基类型定义指针变量的一般形式如下:定义指针变量的一般形式如下:类型名类型名 *指针变量名指针变量名1,*指针变量名指针变量名2,;例如:例如:int *pi , *pj ;以上定义语句中,以上定义语句中,pi和和pj都是用户标都是用户标识符,表示具体的变量名,现在每个变量识符,表示具体的变量名,现在每个变量名前加一星号名前加一星号“*”说明符,用来说明所定说明符,用来说明所定义的变量是指
6、针变量。义的变量是指针变量。注意:变量名前的星号注意:变量名前的星号“*”不可省略,不可省略,若省略了星号若省略了星号“*”说明符,就变成了普通说明符,就变成了普通整型变量。整型变量。int是数据类型名,在这里,说明了是数据类型名,在这里,说明了pi和和pj是两个指向整型(是两个指向整型(int类型)变量的指类型)变量的指针,也就是说变量针,也就是说变量pi和和pj中只能存放中只能存放int类类型变量的地址,这时我们称型变量的地址,这时我们称int是指针变量是指针变量pi和和pj的基类型。又如:的基类型。又如:double *pd ;char *s1 , *s2 ; 在这里定义了三个指针变量在
7、这里定义了三个指针变量pd、s1和和s2,其中指针变量其中指针变量pd的基类型为的基类型为double类类型,在指针变量型,在指针变量pd中,只能存放中,只能存放double类类型变量的地址,指针变量型变量的地址,指针变量s1和和s2的基类型的基类型为为char类型,在指针变量类型,在指针变量s1和和s2中只能存中只能存放放char类型变量的地址。又如:类型变量的地址。又如:int *p ;以上是定义了一个指向指针的指针变以上是定义了一个指向指针的指针变量量p,该指针变量该指针变量p只能存放基类型为只能存放基类型为int类类型的指针变量的地址。又如:型的指针变量的地址。又如:int *pi ,
8、 *p , k ;以上语句是在同一语句中,同时定义以上语句是在同一语句中,同时定义了指针变量了指针变量pi、指向指针的指针变量指向指针的指针变量p和变和变量量k,这是允许的。这是允许的。7.2.27.2.2指针变量的基类型的作用指针变量的基类型的作用任任何何一一个个指指针针变变量量都都是是用用于于存存放放它它所所指指向向变变量量的的地地址址,只只要要能能存存放放地地址址就就可可以以了,为何还要区别不同的基类型呢?了,为何还要区别不同的基类型呢?其其原原理理是是:不不同同的的数数据据类类型型变变量量,C语语言言系系统统为为它它们们开开辟辟的的存存储储空空间间的的字字节节数数是不同的,是不同的,i
9、nt类型的数据存储空间是类型的数据存储空间是2个字节,个字节,float类型的数据存储空间是类型的数据存储空间是4个字节,个字节,系统表示每一个存储空间的地址时,是,系统表示每一个存储空间的地址时,是取该存储空间的第取该存储空间的第1个字节的地址作为该变个字节的地址作为该变量存储空间的地址。那么当一个基类型为量存储空间的地址。那么当一个基类型为int类型的指针变量类型的指针变量p指向了一个指向了一个int类型的类型的变量变量a时,是将该变量时,是将该变量a所占的所占的2个字节的存个字节的存储空间中的第储空间中的第1个字节存储空间的个字节存储空间的“地址地址”存入指针变量存入指针变量p中,如图中
10、,如图7-2(b)所示。所示。图图7-2 指针和指针所指向的对象指针和指针所指向的对象所以根据指针变量所以根据指针变量p中存放的中存放的“地址地址”,只能寻找到变量,只能寻找到变量a第第1个字节的存储空间,个字节的存储空间,如果只提取变量如果只提取变量a所占存储空间第所占存储空间第1个字节个字节的数据,显而易见不是的数据,显而易见不是int类型变量类型变量a的原值,的原值,因为变量因为变量a的原值是通过的原值是通过2个字节来存储的个字节来存储的数据。此时我们可以通过指针变量数据。此时我们可以通过指针变量p的基类的基类型解决问题,知道了变量型解决问题,知道了变量a的第的第1个字节的个字节的地址,
11、再根据指针变量地址,再根据指针变量p的基类型为的基类型为int类型,类型,系统就将从变量系统就将从变量a的第的第1个字节所在的地址个字节所在的地址开始,连续提取开始,连续提取2个字节中的数据,此时的个字节中的数据,此时的数据就是数据就是int类型变量类型变量a的原值。的原值。同理,基类型为同理,基类型为float类型的指针变量,根据类型的指针变量,根据指针变量中存放指针变量中存放float类型变量的地址值,可以寻类型变量的地址值,可以寻找到所需存储空间中的第找到所需存储空间中的第1个字节所在位置,然个字节所在位置,然后再根据基类型为后再根据基类型为float类型,连续地提取类型,连续地提取4个
12、字个字节中的数据,作为被访问的数据,这才是节中的数据,作为被访问的数据,这才是float类类型变量中存放的真实数据。由此,我们可以看到型变量中存放的真实数据。由此,我们可以看到指针变量在定义时,其基类型是何等的重要。因指针变量在定义时,其基类型是何等的重要。因此,定义什么样的基类型指针变量,该指针变量此,定义什么样的基类型指针变量,该指针变量只能存放什么样类型变量的地址,两者必须一致,只能存放什么样类型变量的地址,两者必须一致,否则就可能出现了张冠李戴的错误现象。否则就可能出现了张冠李戴的错误现象。7.3 给指针变量赋值给指针变量赋值7.3.17.3.1使指针指向一个对象使指针指向一个对象1通
13、通过过求求地地址址运运算算符符(&)把把一一个变量的地址赋给指针变量个变量的地址赋给指针变量“&”是是求求地地址址运运算算符符,该该运运算算符符为为单单目目运运算算符符,用用于于求求变变量量的的地地址址,且且该该变变量量必须为内存变量。必须为内存变量。例如:例如:int k=1 , j =2 , *q1 , *q2 , *p ; float x=4.5 ; q1=&k ; q2=&j ;以上第三条语句,是把变量以上第三条语句,是把变量k的地址赋的地址赋给了指针变量给了指针变量q1,使指针变量使指针变量q1中存放了中存放了变量变量k的地址,或称指针变量的地址,或称指针变量q1指向了变量指向了变量
14、k。同理,以上第四条语句,是把变量同理,以上第四条语句,是把变量j的的地址赋给了指针变量地址赋给了指针变量q2,使指针变量使指针变量q2中中存放了变量存放了变量j的地址,或称指针变量的地址,或称指针变量q2指向指向了变量了变量j。注意:在使用注意:在使用 & 运算符求变量的地址,运算符求变量的地址,并赋给指针变量时,一定要确保所求地址并赋给指针变量时,一定要确保所求地址的变量数据类型与存放该变量地址的指针的变量数据类型与存放该变量地址的指针变量基类型一致。变量基类型一致。接上例,如执行接上例,如执行 p=&x ; 语句,则是绝语句,则是绝对错误的。为什么?请读者想一想!对错误的。为什么?请读者
15、想一想!读者还记得在前面调用读者还记得在前面调用 scanf 函数时,函数时,其函数调用格式中,输入数据所对应的各其函数调用格式中,输入数据所对应的各变量名之前都必须加运算符变量名之前都必须加运算符 & ,这就是我,这就是我们所说的求地址运算符。们所说的求地址运算符。scanf 函数把从终函数把从终端读入的数据依次放入这些地址所代表的端读入的数据依次放入这些地址所代表的存储单元中,也就是说存储单元中,也就是说scanf 函数要求输入函数要求输入项是地址值。因此接上例,项是地址值。因此接上例,scanf ( %d %d , &k , &j ) ; 语句和语句和scanf ( %d %d , q1
16、 , q2 ) ; 语句是等价的,都是将终端输语句是等价的,都是将终端输入的整型数据存入到变量入的整型数据存入到变量k和变量和变量j所在的所在的存储单元中。存储单元中。2同类型指针变量之间可以直同类型指针变量之间可以直接赋值接赋值可以把指针变量的值赋给指针变量,可以把指针变量的值赋给指针变量,但一定要确保这两个指针变量的基类型是但一定要确保这两个指针变量的基类型是相同的。相同的。接上例:接上例:p=q1 ;执行以上语句后,使指针变量执行以上语句后,使指针变量p也存也存放了变量放了变量k的地址,也就是说指针变量的地址,也就是说指针变量p和和q1同时指向了变量同时指向了变量k。7.3.27.3.2
17、 给给指指针针变变量量赋赋“空空”值值为为了了使使指指针针变变量量有有一一确确定定的的数数值值,除除了了给给指指针针变变量量赋赋一一地地址址值值外外,当当指指针针变变量量没没有有指指向向的的对对象象时时,也也可可以以给给指指针针变变量量赋赋NULL值值,此此值值为为空空值值。例例如如:int *p ; p=NULL ; 表示指针变量表示指针变量p的值为空。的值为空。NULL是在是在stdio.h头文件中定义的预头文件中定义的预定义符,因此在使用定义符,因此在使用NULL时,应该在程时,应该在程序的前面出现预定义命令行:序的前面出现预定义命令行:# include stdio.h 。NULL的代
18、码值为的代码值为0,所以语句,所以语句p=NULL ;等价于:等价于:p=0 ;,都是表示指针都是表示指针变量变量p是一个空指针,没有指向任何对象。是一个空指针,没有指向任何对象。7.4 对指针变量的操作对指针变量的操作7.4.17.4.1 通通过过指指针针或或地地址址引引用用一个存储单元一个存储单元当当指指针针变变量量中中存存放放了了一一个个确确切切的的地地址址值值时时,通通过过指指针针可可以以用用“间间接接运运算算符符”(*)来引用该地址所代表的存储单元。)来引用该地址所代表的存储单元。1在赋值号右边由在赋值号右边由“*”运算符运算符和指针变量组成的表达式,代表指和指针变量组成的表达式,代
19、表指针所指存储单元的内容针所指存储单元的内容例如:例如: int *p , i=10 , j ; p=&i ; j=*p ;第二条语句是把变量第二条语句是把变量i的地址赋给了指的地址赋给了指针变量针变量p,第三条语句是把指针变量第三条语句是把指针变量p所指所指向的变量向的变量i存储单元的值存储单元的值10赋给变量赋给变量j。“*”号在这里是一个号在这里是一个“间接运算符间接运算符”,它为单目运算符,与运算对象自右至左结它为单目运算符,与运算对象自右至左结合,且运算对象必须为一个地址对象。合,且运算对象必须为一个地址对象。例如:例如: j=*&i ;该语句中该语句中“&”运算符求出变量运算符求出
20、变量i的地的地址,址,“*”运算符取变量运算符取变量i地址中的值地址中的值10赋赋给变量给变量j。例如:例如: int *pp , *p , k=20 , j ; p=&k ; pp=&p ; j=*pp ;以以上上语语句句执执行行后后的的结结果果是是:把把变变量量k的的地地址址赋赋给给了了指指针针变变量量p,把把指指针针变变量量p的的地地址址赋赋给给了了指指向向指指针针的的指指针针变变量量pp,这这种种关关系系如如图图7-4所所示示。最最后后一一条条语语句句是是把把变变量量k中中的的值值20赋赋给给了了变变量量j,这这条条语语句句使使用用了了两两个个“间间接接运运算算符符”,*p表表示示获获
21、取取指指针针变变量量p中中的的值值(此此值值为为变变量量k的的地地址址值值),*pp表示通过指针变量表示通过指针变量p,再获取变量再获取变量k中的值中的值20。图图7-4 变量变量pp、p和和k之间的关系之间的关系2在在赋赋值值号号左左边边由由“*”和和指指针针变变量量组组成成的的表表达达式式,代代表表指指针针所所指指的存储单元的存储单元例如:例如: int *p , k=0 ; p=&k ; *p=150 ;以以上上第第三三条条语语句句是是把把整整数数150存存入入变变量量k中。中。 *p=*p +1 ; 或或 *p+=1 ;以上语句是获取指针变量以上语句是获取指针变量p所指向的存所指向的存
22、储单元储单元k中的值中的值150,然后加,然后加1再放入指针变再放入指针变量量p所指向的存储单元所指向的存储单元k中,此时变量中,此时变量k中存中存放的数值为放的数值为151。例如:例如: int *p , *p , k ; p=&k ; pp=&p ; *pp=200 ;以上第四条语句是将整数以上第四条语句是将整数200存入变量存入变量k中。中。例例7.37.3 指指针针指指向向三三个个变变量量,通通过过指指针针运算选出数值最小的那个数并打印出来。运算选出数值最小的那个数并打印出来。main() int a , b , c , min , *pa , *pb , *pc , *pmin ;
23、pa=&a ; pb=&b ; pc=&c ; pmin=&min ; scanf ( %d %d %d , pa , pb , pc ) ; /* 将将键键盘盘输输入入的的三三个个整整数数依依次次放放入入变变量量a、b、c中中 */ printf ( a=%d , b=%d , c=%d n , a , b , c ) ; *pmin= *pa ; /* 假定变量假定变量a中的数值最小,将其中的数值最小,将其放入变量放入变量min */ if (*pmin *pb ) *pmin= *pb ; /* 若若b中的数值比中的数值比min小,将其放入变量小,将其放入变量min */ if (*pm
24、in *pc ) *pmin= *pc ; /* 若若c中的数值比中的数值比min小,将其放入变量小,将其放入变量min */ printf ( min=%d n , min ) ; /* 通过直接访问方式输出变量通过直接访问方式输出变量min中的值中的值 */ printf ( min=%d n , *pmin ) ; /* 通过间接访问方式输出变量通过间接访问方式输出变量min中的值中的值 */当运行程序时输入当运行程序时输入 25 12 36 程序运行结果:程序运行结果:a=25 , b=12 , c=36min=12min=12程序运行结果说明,当指针指向变量程序运行结果说明,当指针指
25、向变量后,可以通过指针对所指向的存储单元进后,可以通过指针对所指向的存储单元进行数据的存取,直接访问方式和间接访问行数据的存取,直接访问方式和间接访问方式的结果是相同的。方式的结果是相同的。7.4.27.4.2指针指针(变量变量)的运算的运算1指针的移动指针的移动2指针的比较指针的比较7.5 指针在函数方面的应用指针在函数方面的应用7.5.17.5.1 在在被被调调用用函函数数中中直直接接改变主调用函数中的参数值改变主调用函数中的参数值在在第第6章章中中,我我们们曾曾经经介介绍绍过过,通通过过return语语句句可可以以返返回回函函数数值值,但但这这种种方方式式只只能能返返回回一一个个数数据据
26、,若若想想修修改改或或传传回回多多个个数值,可以借用指针。数值,可以借用指针。7.5.27.5.2使函数返回一个地址使函数返回一个地址一一个个函函数数运运行行结结束束时时,可可以以返返回回一一个个整整型型值值、实实型型值值、字字符符型型值值等等,同同样样也也可可以以返返回回指指针针型型的的数数据据,即即返返回回地地址址值值。这这种返回指针型数据的函数,定义的形式为:种返回指针型数据的函数,定义的形式为: 类型名类型名*函数名(参数表)函数名(参数表) 说说明明:函函数数名名前前的的星星号号“*”,说说明明了了此此函函数数是是一一个个指指针针型型函函数数,函函数数的的返返回回值是一个地址值。值是一个地址值。