大学课程《C语言程序设计》PPT课件:第八章.pptx
- 文档编号:18905762
- 上传时间:2024-02-10
- 格式:PPTX
- 页数:33
- 大小:429.47KB
大学课程《C语言程序设计》PPT课件:第八章.pptx
《大学课程《C语言程序设计》PPT课件:第八章.pptx》由会员分享,可在线阅读,更多相关《大学课程《C语言程序设计》PPT课件:第八章.pptx(33页珍藏版)》请在冰点文库上搜索。
第八章指针8.1指针的概念8.2指针变量8.3指针与数组8.4指针与字符串8.5指针与函数8.6指向指针的指针8.7综合应用8.1指针的概念在计算机中,所有的数据都存放在存储器中。
一般把存储器中的一个字节称为一个内存单元。
为了能够访问到内存单元的数据,给每个内存单元编上号,通过编号来访问内存单元。
就像一个同学到某寝室楼找另外一个同学,就必须知道他的寝室编号才可以找到该同学。
我们把内存单元的编号称为内存地址。
在C语言中,为了更形象地描述内存地址,把这个地址称为指针。
在每个内存单元中犹如有一个指针指向它。
不同的数据类型所占用的内存单元数不等,如整型量占2个单元、字符型量占1个单元等。
在前几章的知识中我们学过,在一个程序中定义了一个变量,在编译时就会给这个变量分配内存单元。
由于不同的数据类型所占用的内存单元数不等,如整型量占2个单元、单精度浮点数占4个单元等,编译系统会根据定义的变量类型,分配相应数量的内存单元。
例如以下程序:
voidmain()inta;floatb;printf(a=%lx,b=%lx,&a,&b);程序的运行结果如下:
4020,4022本程序中定义了整型a和单精度浮点型b两个变量。
这两个变量在存储器中分配内存单元的存储如图8.1所示。
图图8.18.1内存分配示意图内存分配示意图8.1指针的概念变量a是整型,在存储器中占有2个长度的内存单元,其地址为4020H、4021H;变量b是单精度浮点型,在存储器中占有4个长度的内存单元,其地址为4022H、4023H、4024H、4025H。
在上例中,printf函数输出的是变量a和b在存储器中所占内存单元的首地址,即第一个内存单元的地址。
注意:
&a、&b表示取变量a和b的地址。
“&”符号称作取地址符。
如果需要从键盘输入数据到a、b变量中,调用格式输入函数scanf(%d%f,&a,&b)。
从键盘输入1010.5。
系统根据&a找到变量a的地址4020H,将数据10存放在4020H、4021H两个内存单元中;根据&b找到变量b的地址4022H,将数据10.5存放在4022H、4023H、4024H、4025H连续的四个内存单元中。
这就是为什么格式输入函数scanf的格式字符串后应该是输入表列的形式,而不能写成scanf(%d%f,a,b)。
这种通过变量的地址存取变量值的方式称为“直接访问”方式。
在C语言中,还可以采用另一种方法来存储变量值,即“间接访问”方式,如图8.2所示。
比如定义了一个整型变量t,其值是9,在内存空间的首地址是4000H。
再定义一个指针变量pt,其值是变量t的首地址4000H。
我们可以通过变量名t直接访问到存储单元的数据。
也可以通过变量pt的值来得到变量t的值,这就是“间接访问”方式。
根据变量pt所占用内存单元的首地址,读取其中所存放的数据,也就是变量t所占用的内存单元的首地址4000H。
然后根据该地址以及变量t所占用的存储单元的长度,得到变量t的值。
变量pt可以通过其值来访问到变量t的值,我们就称变量pt指向变量t。
图8.2很形象地说明了这种关系。
图图8.28.2间接访问方式间接访问方式8.1指针的概念一定要注意区分指针与指针变量这两个概念。
所谓指针,就是变量所占用的存储单元的首地址。
而指针变量是用来存放某个变量首地址的变量。
指针变量也像以前我们学过的普通变量一样,需要占用一定的存储空间。
但与普通变量不同的是,指针变量中存放的是地址,而普通变量中存放的是数据。
8.2指针变量8.2.18.2.18.2.18.2.1指针变量的定义指针变量的定义指针变量的定义指针变量的定义在C语言中,定义指针变量的一般形式如下:
存储类型数据类型*指针变量名=初始地址;其中:
存储类型是可选项,有auto、register、static、extern这4种类型。
数据类型是指针变量指向的变量的数据类型,可以是int、long、char、double等基本数据类型,也可以是数组、结构体等构造类型。
注意:
指针变量名前的符号“*”一定不能省略或漏写。
这表示定义的变量是一个指针变量。
数据类型也必须指定。
因为不同的数据类型所占的字节数不同。
只有指明了数据类型,指针变量在做运算(+、-)的时候,就会根据其指向变量的数据类型,移动若干个字节。
例如,数据类型是float类型,float类型的变量要占4个字节,当指针变量加1时,不是将指针变量的值加1,而是加4,意味着指针要移动4个字节位置。
例如,int*pt1;/*pt1是指向整型变量的指针变量*/long*pt2;/*pt2是指向长整型变量的指针变量*/char*pt3/*pt3是指向字符类型变量的指针变量*/float*pt4;/*pt4是指向单精度浮点型变量的指针变量*/8.2指针变量上面的例中,pt1、pt2、pt3、pt4分别是指针变量的名字,而不要认为*pt1、*pt2、*pt3、*pt4是变量名。
pt1可以指向整型变量,pt2可以指向长整型变量,pt3可以指向字符型变量,pt4可以指向单精度浮点型变量。
pt1、pt2、pt3、pt4这四个变量的值是指针。
不能将整型数据的值赋值给变量pt1。
比如下面的赋值是不合法的:
inta=30;pt1=a;定义过了指针变量,指针变量就可以指向指定类型的变量,但是指针变量的值是随机的,不能确定它具体指向了哪个内存单元,所以必须为其赋值才有意义。
如何给指针变量赋值呢?
8.2指针变量8.2.28.2.28.2.28.2.2指针变量的赋值指针变量的赋值指针变量的赋值指针变量的赋值指针变量定义之后必须为其赋值才可以使用。
若使用了未初始化或未赋值的指针变量有导致系统崩溃的可能。
指针变量的赋值一般有两种方法。
1.1.取变量地址赋值法取变量地址赋值法其一般格式如下:
指针变量&变量名;例如,inta=5;longl=9999;charc=X;floatf=2.1;pt1=&a;pt2=&l;pt3=&c;pt4=&f;其中,pt1、pt2、pt3、pt4分别保存了变量a、l、c、f的地址,如图8.3所示。
注意:
指针变量只能指向其指定的数据类型的变量。
图图8.38.3赋值语句的效果赋值语句的效果8.2指针变量2.2.地址直接赋值法地址直接赋值法其一般格式如下:
指针变量=malloc(分配内存空间的大小);例如,int*p=(int*)malloc
(2);其中,malloc是一个标准库函数。
malloc向系统申请分配指定2个字节的内存空间。
返回值是2个字节内存空间的首地址。
返回类型是void*类型。
void*表示未确定类型的指针。
本例中将void*类型强制转换为整型的指针。
当内存不再使用时,应使用free函数将内存块释放。
8.2指针变量8.2.38.2.38.2.38.2.3指针变量的引用指针变量的引用指针变量的引用指针变量的引用C语言为我们提供了两个运算符来引用指针变量。
(1)&为取地址运算符。
其功能是取变量的地址。
&是单目运算符,其结合性为自右向左。
(2)*为取内容运算符。
其功能是取所指向变量的数据。
*是单目运算符,其结合性为自右向左。
【例【例8-18-1】通过指针变量访问其指向的变量。
#includevoidmain()inta=5;floatb=3.1f;int*p1;float*p2;p1=&a;p2=&b;printf(a=%d,b=%fn,a,b);编码接下页编码接下页8.2指针变量printf(a=%d,b=%fn,*p1,*p2);printf(a=%d,b=%fn,*&a,*&b);printf(a=%lx,b=%lxn,&*p1,&*p2);程序运行结果如图8.4所示。
分析:
分别将变量a、b的地址赋值给指针变量p1、p2。
p1指向了整型变量a,p2指向了单精度浮点类型变量b。
输出指针变量p1与p2指向的值,在指针变量名前必须加*号,表示要取的是指针变量指向的值,而不是指针变量本身的值。
程序中的*&a,*&b,&*p1,&*p2该如何理解?
首先明白“&”和“*”两个运算符的优先级是相同的,运算结合方向是自右而左。
例如*&a,先运算&a,即取a的地址,然后运算“*”号,取a变量的值,因此*&a就等价于a。
又如&*p1,先运算*p1,它就是变量a,然后运算“&”号,取a变量的地址,因此&*p1就等价于p1。
提示:
本程序的最后一行结果只表示本程序本次运行的结果,在不同计算机上运行时,结果可能不同。
图图8.48.4例例8-18-1运行结果运行结果8.2指针变量8.2.48.2.48.2.48.2.4指针变量的运算指针变量的运算指针变量的运算指针变量的运算指针变量中保存的是指针,因此指针变量的运算不同于普通变量,它只允许有限的几种运算。
1.1.赋值运算赋值运算
(1)指针变量初始化。
例如,inta;int*p=&a;
(2)指针变量之间赋值。
例如,inta;int*p1=&a,*p2;p2=p1;/*p1的值赋值给指针变量p2,p1与p2同时指向变量a*/(3)用数组的首地址赋值。
例如,intarray10;int*p;p=array;注意:
p=array;这条语句中,array前不能加“&”取地址符。
因为数组名本身就表示数组的首地址。
8.2指针变量(4)用字符串的首地址赋值。
例如,char*pstr;pstr=VeryGood;2.2.指针与整数的加减运算指针与整数的加减运算指针变量可以与一个整数进行加、减运算来移动指针。
假设p是指向某个数组的指针变量,p+n、p-n、p+、p-这些运算都是合法的。
其中,n必须是整数。
进行加法运算时,表示指针p向后移动;进行减法运算时,表示指针p向前移动。
需要注意的是,p+n不是表示向后移动了n个字节,移动的长度取决于它所指向的数组的数据类型。
假设p是指向T类型的指针,n是整型表达式,pn表示在p原值的基础上增加或减少了n*sizeof(T)字节。
例如,intarray10;int*p;p=array;/*p指向数组的第1个元素array0*/p=p+3;/*此时p指向数组的第4个元素array3*/注意:
指针变量与整数的加减运算对于指向数组的指针变量才有意义。
【例【例8-38-3】分析下面程序。
8.2指针变量#includevoidmain()intarray=1,2,3,4,5,6,7,8,9;int*p=array;printf(%d,%dn,*p+2,*(p+2);printf(%dn,*p+);printf(%dn,*+p);printf(%dn,(*p)+);printf(%dn,array2);程序运行结果如图8.6所示。
分析:
p最初指向数组的第1个元素array0。
*p+2表示p指向的元素的值加2,即1+2结果是3。
*(p+2)表示p指针向后移动2个元素的位置再取其值,即取array2的值3。
*p+与*(p+)是等价的,因为+符号在p的后面,所以先进行*p的操作,取p指向的元素的值,即array0的值1,输出后再执行p=p+1。
注意:
此时p指向了第2个元素array1。
*+p的操作等价于*(+p),先将指针p加1,此时p指向了第3个元素array2,再取其指向的元素的值3。
(*p)+是使p所指向元素的值加1,因为+在后,所以先输出p指向的值3,然后再加1。
因此下一句输出array2的值是4,而不是3。
8.2指针变量3.3.指针变量的关系运算指针变量的关系运算指向同一个数组的成员的两个同类型的指针可以进行比较(包括、=、=、=和!
=),比较的结果可以反映出两个指针变量所指向的元素的位置。
如果前者大于后者,说明前者比后者指向的元素靠后;如果相等,说明前者与后者指向同一个元素;如果前者小于后者,说明前者比后者指向的元素靠前。
注意:
一般参与关系运算的两个指针变量是指向同一个数组的某两个成员。
不同类型指针之间或者不是指向同一数组的指针变量之间进行比较是没有实际意义的。
【例【例8-48-4】分析下面程序。
#includevoidmain()intarray=1,2,3,4;int*p1,*p2;p1=&array0;/*p1指向第一个元素*/p2=&array1;/*p2指向第二个元素*/printf(%dn,p1p2);printf(%dn,p1p2);p2+;/*指针向后移动一个元素位置*/printf(%dn,p1=p2);程序运行结果如图8.7所示。
分析:
执行p1=&array0;p2=&array1;这两句后p1指向第1个元素、p2指向第2个元素。
显然p1p2表达式为假,p1p2表达式为真。
执行p1+=2;这句后p1指针向后移动两个元素位置,指向第3个元素,显然p1p2表达式为真。
执行p2+;这句后p2指针向后移动一个元素位置,指向第3个元素。
此时p1与p2指向了同一个元素,所以p1=p2表达式为真。
图图8.78.7例例8-48-4运行结果运行结果8.3指针与数组8.3.18.3.18.3.18.3.1指针与一维数组指针与一维数组指针与一维数组指针与一维数组1.1.指针指向一维数组指针指向一维数组定义一个一维数组,将该数组的首地址传给指针变量,则该指针就指向了这个一维数组。
例如,intarray5=1,2,3,4,5;/*定义一个一维数组*/int*p;/*指针的变量*/p=array;/*将数组array的首地址赋值给指针变量p*/上例中,指针变量p指向了一维数组array。
其实数组名array表示一维数组的首地址,首地址也就是第1个元素的地址,因此,最后一句也可以改写成p=&array0。
2.2.指针指向一维数组元素指针指向一维数组元素数组元素其实可以看成普通变量,使用指针变量操作数组元素与操作普通变量是一样的。
前面学习数组这章内容的时候学习过引用一维数组的元素可以使用以下形式:
数组名元素下标;这种形式我们称之为下标法。
本章前面内容学习过指针可以与整数进行加减运算操作数组元素。
例如,指针p指向数组a的首地址,如果要让指针p指向该数组的第3个元素a2,可以通过p+2实现。
8.3指针与数组通过指针引用数组元素an的形式就是:
*(p+n)或*(a+n)其中,p是指针且指向数组a的首地址。
这种形式我们称之为指针法。
8.3指针与数组8.3.28.3.28.3.28.3.2指针与二维数组指针与二维数组指针与二维数组指针与二维数组指针可以操作一维数组,同样也可以操作多维数组。
本小节以二维数组为例,讲解指针操作多维数组。
设有一个二维数组,它有2行3列。
其定义如下:
inta23=1,2,3,4,5,6;二维数组可以看作是数组的数组,即其有两个元素a0、a1。
a0可以看成是一个一维数组,其包含了三个元素a00、a01和a02。
那么a1也包含了三个元素a10、a11和a12。
数组名a可以表示该二维数组的首地址,a+1表示的就是第1行的首地址。
元素a0不像一维数组那样表示某个元素而是表示第0行首地址,即第0行第0列元素的地址,&a00。
那么,a0+1表示的不是a1的地址,而是a01的地址。
1.1.指针指向二维数组指针指向二维数组例如,inta23=1,2,3,4,5,6;/*定义一个二维数组*/int*p;/*整型指针变量*/p=a;/*将数组a的首地址赋值给指针变量p*/上例中,指针变量p指向了二维数组a。
最后一句能否改写成p=&a0呢?
前面讲过,a0就是a00的地址,所以可以改写成p=a0,不要取地址符&。
8.3指针与数组2.2.指针指向二维数组元素指针指向二维数组元素数组章节中讲过二维数组元素引用可以使用以下形式:
数组名行下标列下标;如何使用指针变量引用二维数组的元素呢?
首先指针变量指向元素,即将该元素的地址赋值给指针变量,然后使用取内容符“*”来引用元素的值。
3.3.数组的指针数组的指针前面讲过可以把二维数组看成是数组的数组,那么可以通过数组的指针来操作二维数组。
定义指向数组类型的指针变量的一般形式为:
类型名(*指针变量名)元素个数;例如,int(*p)3;inta23=1,2,3,4,5,6;p=a;注意:
指针变量前后的小括号不能省略,并且元素个数要与指向的二维数组的列数保持一致。
8.3指针与数组8.3.38.3.38.3.38.3.3指针数组指针数组指针数组指针数组当数组中元素的类型是指针类型时,这样的数组称为指针数组。
指针数组中的每一个元素都是指针变量。
定义一维指针数组的一般形式如下:
数组类型*数组名数组长度;【例【例8-118-11】分析以下程序。
#includemain()inta3=1,2,3;intb4=2,4,6,8;intc5=1,3,5,7,9;int*p3,i;p0=a;p1=b;p2=c;编码接下页编码接下页8.3指针与数组for(i=0;i3;i+,p0+)printf(%d,*p0);printf(n);for(i=0;i4;i+,p1+)printf(%d,*p1);printf(n);for(i=0;i5;i+,p2+)printf(%d,*p2);printf(n);程序运行结果如图8.14所示。
分析:
语句int*p3;定义了一个名称是p的指向整型的指针数组,其元素个数是3,即它能存放3个指针。
这3个指针元素分别指向整型数组a、b、c,通过这3个指针来操作数组。
注意:
本节讲的指针数组与上节讲的数组指针是完全不同的两个概念。
数组指针(也称行指针),其本质是一个指针变量。
在定义时要在变量名及*前后加上小括号,如int(*p)n。
在执行p+1时,p要跨过n个整型数据的长度。
而指针数组,其本质是一个数组,是一个数组元素,是指针的数组。
图图8.148.14例例8-118-11运行结果运行结果8.4指针与字符串在C语言中,基本数据类型中没有字符串类型。
我们可以用字符数组存放字符串,也可以用字符指针来操作字符串。
例如,chararray=VeryGood!
;/字符数组存放字符串char*pstr=VeryGood!
;/字符指针指向字符串注意:
字符串“VeryGood!
”是将其首地址赋值给了字符指针变量,而不是把“VeryGood!
”整个值赋值给了字符指针变量。
【例【例8-128-12】字符数组存放字符串,并输出字符串。
#includemain()inti;charstr=IloveC.;printf(%sn,str);for(i=0;stri!
=0;i+)printf(%c,stri);printf(n);8.4指针与字符串程序运行结果如图8.15所示。
分析:
“IloveC.”这个字符串赋值给一个字符数组。
数组名str表示数组的首地址。
printf(%sn,str);输出函数中,%s是将字符串整体输出,str不能写成str下标的形式。
其后的for循环语句是将数组中的每一个元素依次输出,输出函数中的格式控制字符是%c,不能再使用%s。
图图8.158.15例例8-128-12运行结果运行结果8.5指针与函数指针可以作为函数的参数。
函数的返回值类型可以是指针类型。
指针不仅可以指向普通变量、数组,也可以指向函数。
8.5.18.5.18.5.18.5.1指针变量作为函数的参数指针变量作为函数的参数指针变量作为函数的参数指针变量作为函数的参数前面学习函数这章内容的时候,函数的参数主要是整型、实型、字符型等数据类型。
指针作为一种数据类型,也可以声明参数。
指针变量做参数,它的作用是将一个变量的地址传送到另一个函数中。
【例【例8-158-15】交换输入的两个数的值。
#includevoidswap(int*pa,int*pb)inttemp;temp=*pa;*pa=*pb;*pb=temp;voidmain()编码接下页编码接下页8.5指针与函数inta,b;printf(请输入要交换的两个整型数据:
);scanf(%d%d,&a,&b);printf(a=%db=%dn,a,b);swap(&a,&b);printf(a=%db=%dn,a,b);程序运行结果如图8.18所示。
分析:
swap函数是自定义函数,其作用是交换两个整型变量的值。
它有两个形式参数,数据类型都是指针类型。
由本例可以看出,定义一个指针类型的参数与定义一个指针类型的变量是一样的。
在函数体中,pa、pb两个变量前的指向内容符*千万不能省略。
如果省略就不能完成交换的目的。
调用swap函数时,实际参数不是a和b,而是&a和&b。
也就是将a和b的地址分别传给了pa和pb,然后执行swap的函数体,将*pa和*pb的值进行交换。
最后在主函数中输出a和b的值。
当数组名作函数的实际参数时,也可以使用指针类型的形式参数。
图图8.188.18例例8-158-15运行结果运行结果8.5指针与函数8.5.28.5.28.5.28.5.2指针函数的返回值指针函数的返回值指针函数的返回值指针函数的返回值后面我们将学习,函数的返回类型可以是整型、实型、字符型等类型。
事实上,指针类型也可以定义函数的返回类型。
返回指针的函数一般形式如下:
数据类型*函数名(参数列表)函数体;其中,函数名前面的“*”一定不能省略,它说明了函数的返回值类型是指针类型。
【例【例8-178-17】定义一个函数,求一维数组a的所有元素的和,最后利用指针返回结果。
#includeint*sum(int*pa,intn)ints=0,i;编码接下页编码接下页8.5指针与函数for(i=0;in;i+,pa+)s+=*pa;return&s;voidmain()inta5=1,2,3,4,5;int*ps;ps=sum(a,5);printf(a数组的和是:
%dn,*ps);程序运行结果如图8.20所示。
图图8.208.20例例8-178-17运行结果运行结果8.5指针与函数8.5.38.5.38.5.38.5.3指向函数的指针指向函数的指针指向函数的指针指向函数的指针编译后的函数代码在内存中被执行。
代码在内存中占用一定的内存单元,我们把这些内存单元的首地址称为函数的入口地址。
这个入口地址可以赋值给一个指针变量,也就是这个指针变量指向了该函数。
因此,可以通过指针函数的指针变量调用该函数。
定义指向函数的指针的一般形式如下:
数据类型(*函数指针变量名)();其中,数据类型为函数返回值的类型,变量名前面的“*”不能漏写,这表明其后的变量是一个指针变量。
注意:
“*”和变量名前后的小括号不能省略,如果省略了,就成了返回指针的函数一般形式。
通过函数指针调用函数的一般形式如下:
函数指针变量名(参数);8.6指向指针的指针一个指针变量存放的是普通变量的地址,该变量就称为指针变量;如果一个指针变量中存放的是另外一个指针变量的地址,该变量就称为指向指针的指针变量。
定义指向指针的指针其一般形式如下:
类型名*指针变量名;【例【例8-208-20】分析下面程序。
#includevoidmain()intm=2;int*p1=&m;int*p2=&p1;printf(m=%dn,*p1);printf(m=%dn,*p2);程序运行结果如图8.23所示。
图图8.238.23例例8-208-20运行结果运行结果8.6指向指针的指针该例中变量m、p1、p2的关系如图8.24所示。
分析:
整型变量m的地址是&m,将其传递给指针变量p1,则p1指向m。
p2是指向指针的指针变量,将p1的地址&p1传递给p2,则p2指向p1。
语句printf(m=%dn,*p1);通过指针变量p1取m的值并输出。
语句printf(m=%dn,*p2);通过指向指针的指针变量p2取m的值并输出。
注意:
p2前面有两个*号,为什么要使用两个*号呢?
由于指针运算符“*”是自右至左结合,*p2等价于*(*p2)。
又因为*p2等价于p1,所以*p2等价于*p1。
图图8.248.24指针示意图指针示意图8.7综合应用【例例8-228-22】编写一个函数,分别统计输入字符串中26个字母的个数,如果有大写字母,先将其转换成小写字母再统计。
#includeint*c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C语言程序设计 大学 课程 语言程序设计 PPT 课件 第八