北大裘宗燕《从问题到程序》第七章_指针

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

从问题到程序裘宗燕北京大学数学学院2005年第七章,指针程序执行中数据存于内存。在可用期间数据有确定存储位置,占据一些存储单元。内存单元的编号:地址。机器语言通过地址访问数据。高级语言用变量等作为存储单元/地址的抽象。建立变量就是安排存储。赋值时存入,用值时从中提取外部变量/静态变量有全局存在期,程序执行前安排存储位置,保持到程序结束。自动变量在函数调用时安排存储,至函数结束。再调用时重新安排存储。7.1地址与指针变量存在期就是它占据所安排存储的期间。任何变量在存在期间总有确定存储位置,有固定地址。寄存器变量可能放在寄存器,无地址。本章不考虑寄存器变量。变量存在时有地址,地址用二进制编码,因此可能成为程序处理的数据。问题:地址作为数据有什么用?若程序可以处理对象地址,就可通过地址处理相关对象。对象(如变量)地址也被作为数据,地址值/指针值。以地址为值的变量称为指针变量/指针(pointer)。指针是一种访问其他对象的手段,利用这种机制能更灵活方便地实施对各种对象的操作。指针的主要操作•指针赋值:将程序对象的地址存入指针变量。•间接访问:通过指针访问被指对象。指针还能保存其他对象的地址。下面讨论以变量为例。指针p保存着变量x地址,也说指针p指向x。图示:指针变量p变量x图7.1 指针与被指的变量在C中使用指针常能写出更简洁有效的程序。有些问题必须用指针处理。指针在大型复杂软件中使用广泛。指针使用的水平是评价人的C程序设计能力的重要方面。C指针灵活/功能强。掌握有难度,易用错,应特别注意。应特别注意使用指针的常见错误,注意!指针是变量,可赋值,其指向可以改变。现在p指向x,以后可能指向y。通过p访问被指对象的语句目前访问x,后来就访问y。这种新的灵活性很有用。7.2指针变量的定义和使用指针有类型,只能保存特定类型的变量的地址指向int的指针p只能指向int变量。p所指也看作int,从p间接访问当作int。常说int指针p1等。定义指针需指明指向类型。定义指向int的指针变量:int*p,*q;指针变量可以与其他变量一起定义:int*p,n,a[10],*q,*p1,m;指针是变量,可赋值取值,有定义域与存在期。应赋给类型正确的指针值,取出的值是特定类型的指针值。用(int*)表示整型指针的类型,其他类似。指针操作取地址运算符&和间接访问操作*。都是一元运算符指针变量p变量n图7.2 两个指针指向同一个变量的情况指针变量q取地址运算&写在变量描述(如变量名)前取变量地址,是对应类型的指针值,可赋给类型合适的指针。例:p=&n;q=p;p1=&a[1];多个指针可能同时指向同一变量。变量相等是值相等,两个指针变量相等说明它们指向程序里同一东西。间接运算间接运算得到被指针所指的变量,这种表达式可以像普通变量一样使用。设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=&a[0];/*指针q指向了数组a的元素*/指针作为函数参数指针作为函数参数有特殊意义,利用这种参数可写出能修改调用时环境的函数。函数调用处的环境指在调用函数的位置能访问的变量全体。前面函数的特点:可使用调用处环境中变量的值(通过参数),但不能修改这些变量(数组参数除外)。在函数f里调用g,可将f的局部变量作为实参。但用g改变f局部变量的唯一方法是将g的返回值赋给f的局部变量。这种方法局限性太强,例如无法在一个函数调用中修改两个局部变量的值。利用指针可以改变这种情况。例:定义函数swap,希望用它交换两个变量的值。因为要改变两个变量,无法通过返回值解决。下面定义不行:voidswap0(intx,inty){intt=x;x=y;y=t;}intf(…){inta=5,b=10;swap0(a,b);…/*不行*/}函数内修改形参,不会改变调用时的实参。分析:要(在一函数里)通过调用函数g修改调用处的变量(如局部变量),必须在g里掌握这个变量。用指针可以解决问题。把m的地址(也是值)通过指针参数p传给g,函数内对p间接访问就能操作m,包括对m赋值(改变m)。g的函数体参数p变量m在函数体里用*p可以访问和改变实参变量m.图7.3 在函数里通过指针可以访问外面的变量例,通过函数调用把变量值设置为3:voidset3(int*np){*np=3;}使用实例:intmain(){intn,m;set3(&n);/*设置n和m*/set3(&m);printf(%d,%d\n,n,m);return0;}请回忆scanf的情况。通过参数改变调用环境的方案包括三方面:1.函数定义中用指针参数;2.函数内用间接操作实际变量;3.调用时以被操作变量的地址作为实参。函数swap可定义为:voidswap(int*p,int*q){intt=*p;*p=*q;*q=t;}交换变量m和n的值,调用形式是:swap(&m,&n);swap的参数类型是(int*),实参必须是合法的整型变量的地址。swap的函数体p变量m图7.4 函数调用swap(&m,&n)形成的现场变量nq设有变量定义:inta[10],k;调用swap的实例:swap(&a[0],&a[5]);swap(&a[1],&k);介绍标准库函数scanf时,强调在接受输入的变量前必须写&,就是将变量的地址传给scanf。scanf采用与swap一样的技术,通过间接访问为指定变量赋值,把输入的值赋给指定变量。例:改造上章输入整数值并检查值范围的函数,引进指针参数,使之能更好处理输入错误。前面实现里用特殊整数值指明输入出错,有很大缺点(可能不存在合适的值)。指针参数提供了另一种方法:函数增加一个指针参数,通过它送回读入值。用函数返回值传递函数执行的状态信息。返回1表示输入成功,0表示输入未能正常完成。新函数定义:intgetnumber(charprompt[],intimin,intimax,intrepeat,int*np){inti;for(i=0;repeat=0||irepeat;++i){printf(%s,prompt);if(scanf(%d,np)!=1||*npimin||*npimax){printf(Correctrange[%d,%d].\n,imin,imax);while(getchar()!='\n');}elsereturn1;}return0;}前面的调用现在可以重写为:getnumber(Choosearange[0,n].Inputn:,2,32767,5,&m);getnumber(Yourguess:,0,m-1,5,&guess);更合适的调用形式:if(getnumber(Yourguess:,0,m-1,5,&guess)==0){…/*处理输入出错的程序片段*/}这类函数的形参为指针,实参必须是合法变量地址。这里可以看到标准库函数scanf的样子。通过返回值表示函数的工作情况,是一种常用技术。与指针有关的一些问题空指针值:一个特殊指针值,表示指针变量闲置(未指向任何变量)。唯一对任何指针类型都合法的值空指针值用0表示,标准库专门定义了符号常量NULLp=NULL;和p=0;相同前一写法易看到是指针,用时必须包含标准头文件。指针初始化指针变量定义时可用合法指针值初始化:intn,*p=&n,*q=NULL;若没有初始化,外部指针和局部静态指针自动初始化为用空;局部自动指针不自动初始化。指针使用中的常见错误使用指针的最常见错误是非法间接访问:在指针未指向合法变量的情况下做间接。如:intf(...){int*p,n=3;*p=2;...}p没有初始化,没有指向合法变量。“悬空指针”指值不是(当时)合法的变量地址的指针变量,也常被称为“野指针”。间接访问悬空指针是严重错误,后果可能很严重。常见错误写法(设p是悬空int指针,n是int变量):swap(p,&n);scanf(...,p);scanf(...,n);编译程序不能发现scanf的错误。有些系统可能对第一个例子(假设p未初始化)给出警告。间接访问空指针也同样无理,是非法的。通用指针类型(void*),可以指向任何变量。声明:intn,*p;doublex,*q;void*gp1,*gp2;任何指针值可以赋给通用指针(不必转换)。例:gp1=&n;//gp1指向n(值是n的地址)gp2=&x;//gp2指向x若通用指针gpt指向g,g类型是指针pt的指向类型,将gpt赋给pt(要写强制转换)通过pt保证正确访问g。gp1=&n;p=(int*)gp1;/*合法,p是(int*)*/q=(double*)gp1;/*不合法,q是(double*)*/其他使用方式没有任何保证。编译程序不能识别强制转换错误。指针类型转换并不改变指针值•将整型变量n的地址赋给通用指针gpt,gpt存的就是n的地址。赋回整型指针不会有问题。•将该地址赋给double指针,就造成了混乱。指针类型代表一种观点。被整型指针所指的变量总看成是整型的变量;被双精度指针指向……。指针转换是观点转换。从整型指针转换到通用指针就是丢掉类型信息。C保证恢复到原有类型(转回整型指针),被指对象还可用。通用指针不能做间接运算•被普通指针指向的变量的类型明确,间接后可以作为该类型的变量使用•通用指针可以指向任何变量,通过通用指针间接访问的意义无法确定。通用指针没提供被指对象的类型信息,所以不能通过它们直接使用被指对象•通用指针最无用,唯一用途就是提供指针值•标准库的某些函数使用了通用指针(后面会看到)7.3指针与数组C指针与数组关系密切,以指针为媒介可以完成各种数组操作。常能使程序更加简洁有效。用指针做数组操作同样要特别注意越界错误。指针和数组的关系是C语言特有的,除了由C派生出的语言(如C++),一般语言中没有这种关系。指向数组元素的指针类型合适的指针可以指向数组元素。假定有定义:int*p1,*p2,*p3,*p4;inta[10]={1,2,3,4,5,6,7,8,9,10};可以写:p1=&a[0];p2=p1;p3=&a[5];p4=&a[10];p4没指向a的元素,是指向a最后元素向后一个位置。C语言保证这个地址存在,但写*p4是错误的。数组ap1图7.5指向数组a的元素的4个指针p2p4p3写数组名得到数组首元素地址,元素类型的指针值。“p1=&a[0];”可简写为:p1=a;指针运算当指针p指向数组元素时说p指到了数组里。这时由p可以访问被p指的元素,还可访问数组的其他元素。例:p1指向a首元素,值合法(a[0]的地址),p1+1也合法(a[1]的地址)。p1+2、p1+3、…也合法,分别为a其他元素的地址。由它们可间接访问a各元素。例:*(p1+2)=3;/*给a[2]赋值*/p2=p1+5;/*使p2指向a[5]*/也可由指向非首元素的指针出发访问数组其他元素:*(p2+2)=5;/*给a[7]赋值*/可用减法访问所指位置之前的元素:*(p2-2)=4;/*给a[3]赋值*/通过指针访问数组元素时必须保证不越界。运算取得的指针值(即使不间接访问)必须在数组范围内(可过末元素一位置),否则无定义。这类运算称为“指针运算”。其他常用指针运算:用指针运算得到的值做指针更新:p2=p2-2;/*这使p2改指向a[3]*/用增/减量操作做指针更新(指针应指在数组里):p3=p2;++p3;--p2;p3+=2;如果两指针指在同一个数组里,可以求差,得到它们间的数组元素个数(带符号整数)。n=p3–p2;/*也可以求p2–p3*/指在同一个数组里的指针可以比较大小:if(p3p2)....当p3所指的元素在p2所指的元素之后时条件成立(值为1),否则不成立(值为0)。两个指针不指在同一数组里时,比较大小没有意义。两个同类型指针可用==和!=比较

1 / 108
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功