七章节指针.ppt
《七章节指针.ppt》由会员分享,可在线阅读,更多相关《七章节指针.ppt(97页珍藏版)》请在三一办公上搜索。
1、第七章 指针,程序执行中数据存于内存。在可用期间数据有确定存储位置,占据一些存储单元。内存单元的编号:地址。机器语言通过地址访问数据。高级语言用变量等作为存储单元/地址的抽象。,建立变量就是安排存储。赋值时存入,用值时从中提取。外部变量/静态变量有全局存在期,程序执行前安排存储位置,保持到程序结束。自动变量在函数调用时安排存储,至函数结束。再调用时重新安排存储。,变量存在期就是它占据所安排存储的期间。任何变量在存在期间总有确定存储位置,有固定的地址。寄存器变量可能放在寄存器,无地址。本章不考虑寄存器变量。,变量存在时有地址,地址用二进制编码,因此可能成为程序处理的数据。问题:地址作为数据有什么
2、用?,若程序可以处理对象地址,就可通过地址处理相关对象。对象(如变量)地址也被作为数据,地址值/指针值。以地址为值的变量称为指针变量/指针(pointer)。,指针是一种访问其他对象的手段,利用这种机制能更灵活方便地实施对各种对象的操作。,主要操作指针赋值:将程序对象的地址存入指针变量。间接访问:通过指针访问被指对象。指针还能保存其他对象的地址。下面讨论以变量为例。,指针保存着变量x地址,也说指针指向x。示意图:,指针可赋值,其指向在执行中可变。p某时指x,后可能指向y。这样,通过p访问被指对象的语句,前次访问x,后来就访问y。这种新灵活性很有用。,C中用指针常能写出更简洁有效的程序。有些问题
3、必须用指针处理。指针在大型复杂软件中使用广泛。指针使用的水平是评价人的C程序设计能力的重要方面。C指针灵活/功能强。掌握有难度,易用错,应特别注意。应特别注意使用指针的常见错误,注意!,7.2 指针变量的定义和使用,指针有类型,只能保存特定类型的变量的地址。指向int的指针p只能指向int变量。p所指也看作int,从p间接访问当作int。常说int指针p1等。,定义指针需指明指向类型。定义指向int的指针变量:int*p,*q;指针变量可以与其他变量一起定义:int*p,n,a10,*q,*p1,m;,指针是变量,可赋值取值,定义域与存在期。应赋给类型正确的指针值,取出的值也是特定类型的指针值
4、。用(int*)表示整型指针的类型,其他类似。,指针操作取地址运算符&和间接访问操作用*。一元运算符。,取地址运算,多个指针可能同时指向同一变量。变量相等就是值相等,指针变量相等说明两个指针指向程序里同一东西。,间接运算间接运算得到被指针所指的变量,这种表达式可以像普通变量一样使用。设p指向n。间接赋值:*p=17;这里写*p相当于直接写n。另一个赋值:m=*p+*q*n;/*访问n三次*/,+*p;/*使变量n的值加1,变成18*/(*p)+;/*使变量n的值再加1,变成19。*/*p+=*q+n;/*变量n被赋以新值57*/q=/*指针q指向了数组a的元素*/,指针作为函数参数指针作为函数
5、参数有特殊意义,利用这种参数可写出能修改调用时环境的函数。函数调用处的环境指在调用函数的位置能访问的变量全体。前面函数的特点:可使用调用处环境中变量的值(通过参数),但不能修改这些变量(数组参数除外)。在函数f里调用g,可将f局部变量作为实参。用g改变f局部变量的唯一方法是将g的返回值赋给f的局部变量。这种方法局限性太强,例如无法在一个调用中修改两个局部变量的值。利用指针可以改变这种情况。,例:定义函数swap,希望用它交换两个变量的值。因为要改变两个变量,无法通过返回值解决。,下面定义不行:void swap0(int x,int y)int t=x;x=y;y=t;int f()int a
6、=5,b=10;swap0(a,b);/*不行*/,函数内修改形参,不会改变调用时的实参。,分析:要(在另一函数里)调用函数g修改调用处的变量(如局部变量),必须在g里面掌握这个变量。,用指针可以解决问题。把m的地址(也是值)通过指针参数p传给g,函数内对p间接访问就能操作m,包括对m赋值(改变m)。,例:void set3(int*np)*np=3;使用实例:int main()int n,m;set3(,请回忆scanf的情况。,通过参数改变调用环境的方案包括三方面:1)函数定义中用指针参数;2)函数内用间接操作实际变量;3)调用时以被操作变量的地址作为实参。函数swap可定义为:void
7、 swap(int*p,int*q)int t=*p;*p=*q;*q=t;交换变量m和n的值,调用形式是:swap(,swap参数类型是(int*),实参必须是合法整型变量的地址。,设有变量定义:int a10,k;调用实例:swap(,介绍标准库函数scanf时,强调在接受输入的变量前必须写&,就是将变量的地址传给scanf。scanf采用与swap一样的技术,通过间接访问为指定变量赋值,把输入的值赋给指定变量。,例:改造上章输入整数值并检查值范围的函数,引进指针参数,使之能更好处理输入错误。前面实现里用特殊整数值指明输入出错,有缺点(可能不存在合适的值)。指针参数提供了另一种方法:,函数
8、增加一个指针参数,通过它送回读入值。用函数返回值传递函数执行的状态信息。返回1表示输入成功,0表示输入未能正常完成。,新函数定义:int getnumber(char prompt,int imin,int imax,int repeat,int*np)int i;*np=0;for(i=0;repeatimax)printf(Correct range%d,%d.n,imin,imax);while(getchar()!=n);else return 1;return 0;,前面的调用现在可以重写为:getnumber(Choose a range 0,n.Input n:,2,32767,
9、5,更合适的调用形式:if(getnumber(Your guess:,0,m-1,5,&guess)=0)/*处理输入出错的程序片段*/这类函数的形参为指针,实参必须是合法变量地址。,这里可以看到标准库函数的样子。通过返回值表示函数的工作情况,是一种常用技术。,与指针有关的一些问题空指针值:特殊指针值,表示指针变量闲置(未指向任何变量)。是唯一对任何指针类型都合法的值。空指针值用0表示,标准库定义符号常量 NULL:p=NULL;和p=0;相同前一写法易看到是指针,用时必须包含标准头文件。,指针初始化指针变量定义时可用合法指针值初始化:int n,*p=若没有初始化,外部指针和局部静态指针自
10、动初始化为用空;普通局部指针不初始化。,指针使用中的常见错误使用指针的最常见错误是非法间接访问:在指针未指向合法变量的情况下做间接。如:int f(.)int*p,n=3;*p=2;.p没有初始化,没有指向合法变量。,“悬空指针”指值不是(当时)合法的变量地址的指针变量,也常被称为“野指针”。间接访问悬空指针是严重错误,后果可能很严重。,常见错误写法(设p是悬空int指针,n是int变量):swap(p,编译程序不能发现scanf的错误。有些系统可能对第一个例子(假设p未初始化)给出警告。间接访问空指针也同样无理和非法。,通用指针类型(void*),可以指向任何变量。声明:int n,*p;d
11、ouble x,*q;void*gp1,*gp2;,任何指针值可以赋给通用指针(不必转换)。例:gp1=/gp2指向x,若通用指针gpt指向g,g类型是指针pt的指向类型,将gpt赋给pt(要写强制转换)通过pt保证正确访问g。gp1=/*合法,p是(int*)*/,q=(double*)gp1;/*不合法,q是(double*)*/,不合法?,上面的转换既不改变指针的值,也不改变其所指对象的类型,但却让程序今后把n当作double型数据看待,这就非法了。,指针类型代表一种观点。被整型指针所指的变量总看成是整型;被双精度指针指向。指针转换是观点转换。从整型指针转换到通用指针就是丢掉类型信息。保
12、证恢复到原有类型(转回整型指针),被指对象还可用。,通用指针不能做间接运算。被普通指针所指变量的类型明确,间接后可以作为该类型的变量使用。通用指针可指向任何变量,间接访问的意义无法确定。通用指针没提供被指对象的类型信息,所以不能通过它们直接使用被指对象。通用指针最无用,唯一用途就是提供指针值。标准库的某些函数使用了通用指针。,7.3 指针与数组,C指针与数组关系密切,以指针为媒介可以完成各种数组操作。常能使程序更加简洁有效。用指针做数组操作同样要特别注意越界错误。指针和数组的关系是C语言特有的,除了由C派生出的语言(如C+),一般语言中没有这种关系。,指向数组元素的指针类型合适的指针可以指向数
13、组元素。假定有定义:int*p1,*p2,*p3,*p4;int a10=1,2,3,4,5,6,7,8,9,10;,可以写:p1=,p4没指向a的元素,是指向a最后元素向后一个位置。C语言保证这个地址存在,但写*p4 是错误的。,写数组名得到数组首元素地址,元素类型的指针值。“p1=,指针运算当指针p指向数组元素时说p指到了数组里。这时由p可以访问被p指的元素,还可访问数组的其他元素。例:p1指向a首元素,值合法(a0的地址),p1+1也合法(a1的地址)。p1+2、p1+3、也合法,分别为a其他元素的地址。由它们可间接访问a各元素。,例:*(p1+2)=3;/*给a2赋值*/p2=p1+5
14、;/*使p2指向a5*/,也可由指向非首元素的指针出发访问数组其他元素:*(p2+2)=5;/*给a7赋值*/可用减法访问所指位置之前的元素:*(p2-2)=4;/*给a3赋值*/,通过指针访问数组元素时必须保证不越界。运算取得的指针值(即使不间接访问)必须在数组范围内(可过末元素一位置),否则无定义。这类运算称为“指针运算”。其他常用指针运算:用指针运算得到的值做指针更新:p2=p2-2;/*这使p2改指向a3*/,用增/减量操作做指针更新(指针应指在数组里):p3=p2;+p3;-p2;p3+=2;如果两指针指在同一个数组里,可以求差,得到它们间的数组元素个数(带符号整数)。n=p3 p2
15、;/*也可以求 p2 p3*/,指在同一个数组里的指针可以比较大小:if(p3 p2).当p3所指的元素在p2所指的元素之后时条件成立(值为1),否则不成立(值为0)。两个指针不指在同一数组里时,比较大小没有意义。,两个同类型指针可用=和!=比较相等或不等;任何指针都能与通用指针比较相等或不等,任何指针可与空指针值(0或NULL)比较相等或不等。两指针指向同一数据元素,或同为空值时它们相等。,数组写法与指针写法如果指针指在数组里,通过指针访问数组元素也可用下标形式写。设p1指向数组a0,p3指向a5。可写:p13=5;p32=8;p13一类写法称为数组写法,*(p+3)一类写法称为指针写法。两
16、类写法有等价效力,可自由选用。,数组名求值得到指针值可参与一些指针运算,可采用指针写法。a3可写为*(a+3)。(数组名可以“看作”常量指针)数组名可以与其他指针比大小,相等与不相等,等等。注意:数组名不是指针变量,特别是不能赋值,不能更改。若a为数组,下面操作是错误的:a+;a+=3;a=p;有些运算虽不赋值但也可能没意义。如 a3 不可能得到合法指针值,结果超出数组界限。,指针运算原理当一个指针指向数组里的元素时,为什么能算出下一元素位置?(这是指针运算的基础)指针有指向类型,p指向数组a时,p的类型与a元素类型一致,数据对象的大小可以确定。p+1的值可根据p的值和数组元素大小算出。由一个
17、数组元素位置可以算出下一元素位置,或几个元素之后的元素位置。指针运算的基础。,通用指针即使指到数组里,因没有确定指向类型,因此不能做一般指针计算,只能做指针比较。,基于指针的数组程序设计指针运算是处理数组元素的另一方式,有时很方便。设有int数组a和指针p1,p2,下面代码都打印a的元素:,for(p1=a,p2=a+10;p1 p2;+p1)printf(%dn,*p1);for(p1=a;p1 a+10;+p1)printf(%dn,*p1);for(p1=p2=a;p1-p2 10;+p1)printf(%dn,*p1);for(p1=a;p1-a 10;+p1)printf(%dn,*
18、p1);,数组参数的意义C规定,数组参数就是相应的指针参数:int f(int n,int d).和int f(int n,int*d).意义相同。数组参数的作用就是这样实现的。对应d的实参是被处理数组的名字,求值得到指针值,符合形参需要,使d指向该数组的“首元素”。,前面函数体里参数用数组写法(对指针可这样写)。通过指针形参d访问的相应实参数组里的各元素。(数组参数就是利用指针实现的!)这也使采用数组参数的函数能修改实参数组。,函数里也可用指针方式做元素访问。int intsum(int n,int a)int i,m=0;for(i=0;i n;+i)m+=*(a+i);return m;
19、,函数里不能用sizeof确定数组实参大小:函数的数组形参实际是指针,求sizeof算出的是指针的大小。所有指针大小都一样,它们保存的都是地址值,各种类型的地址值采用同样表示方式。另一方面,sizeof的计算是在编译中完成的。实参是动态运行中的东西。,使用数组的一段元素以数组为参数的函数可处理一段元素。求元素和:double sum(int n,double a);设有双精度数组b,40个元素已有值:,用sum可求b所有元素之和/前一段元素之和:x=sum(40,b);y=sum(20,b);sum不知道b的大小,它由参数得到数组首元素地址,从这里开始求连续40或20个元素的和。也可用sum求
20、b中下标12到24的一段元素之和。,z=sum(13,b+12);,指针与数组操作函数实例例1,用指针方式实现字符串长度函数。一种方式:int strLength(const char*s)int n=0;while(*s!=0)s+;n+;return n;通过局部指针(参数是局部变量)扫描串中字符。,另一实现:int strLength(const char*s)char*p=s;while(*p!=0)p+;return p-s;,参数是指针类型(char*),实参应是字符串或存字符串的数组。可用指向字符串的指针作为参数。,例2,用指针实现字符串复制函数。直接定义:void strCop
21、y(char*s,const char*t)while(*s=*t)!=0)s+;t+;赋值表达式有值,0就是0,函数可简化:void strCopy(char*s,const char*t)while(*s=*t)s+;t+;,把指针更新操作也写在循环测试条件里,程序是:void strCopy(char*s,const char*t)while(*s+=*t+);/空语句注意优先级与结合性,增量运算的作用与值等。,例3,利用指针,输出int数组里一段元素:void prt_seq(int*begin,int*end)for(;begin!=end;+begin)printf(%dn,*be
22、gin);,prt_seq(a,a+10);prt_seq(a+5,a+10);prt_seq(a,a+3);prt_seq(a+2,a+6);prt_seq(a+4,a+4);prt_seq(a+10,a+10);最后两个调用对应空序列。序列为“半闭半开”。,还可写出许多类似函数。“设置”函数:void set_seq(int*b,int*e,int v)for(;b!=e;+b)*b=v;,序列中每个元素都用其平方根取代:void sqrt_seq(double*b,double*e)for(;b!=e;+b)*b=sqrt(*b);,求平均值:double avrg(double*b,d
23、ouble*e)double*p,x=0.0;if(b=e)return 0.0;for(p=b;p!=e;+p)x+=*p;return x/(e-b);,字符指针与字符数组,定义字符指针时可用字符串常量初始化,如:char*p=Programming;1)定义p;2)建字符串常量“Programming”;3)令p指向字符串常量。见下图。,常用字符指针指向字符数组元素。如指向常量字符串或存着字符串的字符数组,通常指向字符串开始。也可以指到字符串中间,把它指的东西当字符串用。,比较:char a=Programming;1)定义12个字符元素的数组;2)用“Programming”中各字符和
24、空字符初始化a的各元素。如下图。,1)指针p可重新赋值(a不能赋值),如:p=Programming Language C;2)p和a类型不同,大小不同。a占12个字符的空间。3)a的元素可以重新赋值。如:a8=e;a9=r;a10=0;a变成“Programmer”。按规定不得修改字符串常量。,可定义字符指针变量并让它指向已有字符数组。C程序常用这种方式使用和操作字符数组内容。例如,输入一行到数组里:,enum NLINE=256;char lineNLINE;int count;char*p;/*-*/p=line;while(plineNLINE-1/*统计e的个数*/,7.4 指针数组
25、,复杂C程序里常用指针的数组。例:需要一组字符串,常用字符指针数组索引它们。如软件中错误信息常用一组字符串表示。分散管理不便。可定义指针数组,指针分别指向输出信息串常量。也可定义其他类型的指针数组。,定义字符指针数组:char*pa10;优先级也适用于定义。优先级高,pa是数组,其元素是字符指针。,定义字符指针数组时可用字符串常量提供初始值。例:char*days=Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday;,简单实例:printf(Work days:);for(i=1;i6;+i)printf(%s,daysi);pri
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 章节 指针
链接地址:https://www.31ppt.com/p-5357612.html