版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
从问题到程序整理ppt第七章,指针整理ppt程序执行中数据存于内存。在可用期间数据有确定存储位置,占据一些存储单元。内存单元的编号:地址。机器语言通过地址访问数据。高级语言用变量等作为存储单元/地址的抽象。建立变量就是安排存储。赋值时存入,用值时从中提取外部变量/静态变量有全局存在期,程序执行前安排存储位置,保持到程序结束。自动变量在函数调用时安排存储,至函数结束。再调用时重新安排存储。7.1地址与指针整理ppt变量存在期就是它占据所安排存储的期间。任何变量在存在期间总有确定存储位置,有固定地址。寄存器变量可能放在寄存器,无地址。本章不考虑寄存器变量。变量存在时有地址,地址用二进制编码,因此可能成为程序处理的数据。问题:地址作为数据有什么用?若程序可以处理对象地址,就可通过地址处理相关对象。对象(如变量)地址也被作为数据,地址值/指针值。以地址为值的变量称为指针变量/指针(pointer)。指针是一种访问其他对象的手段,利用这种机制能更灵活方便地实施对各种对象的操作。整理ppt指针的主要操作指针赋值:将程序对象的地址存入指针变量。间接访问:通过指针访问被指对象。指针还能保存其他对象的地址。下面讨论以变量为例。指针p保存着变量x地址,也说指针p指向x。图示:整理ppt在C中使用指针常能写出更简洁有效的程序。有些问题必须用指针处理。指针在大型复杂软件中使用广泛。指针使用的水平是评价人的C程序设计能力的重要方面。C指针灵活/功能强。掌握有难度,易用错,应特别注意。应特别注意使用指针的常见错误,注意!指针是变量,可赋值,其指向可以改变。现在p指向x,以后可能指向y。通过p访问被指对象的语句目前访问x,后来就访问y。这种新的灵活性很有用。整理ppt7.2指针变量的定义和使用指针有类型,只能保存特定类型的变量的地址指向int的指针p只能指向int变量。p所指也看作int,从p间接访问当作int。常说int指针p1等。定义指针需指明指向类型。定义指向int的指针变量:
int*p,*q;指针变量可以与其他变量一起定义:int*p,n,a[10],*q,*p1,m;指针是变量,可赋值取值,有定义域与存在期。应赋给类型正确的指针值,取出的值是特定类型的指针值。用(int*)表示整型指针的类型,其他类似。整理ppt指针操作取地址运算符&和间接访问操作
*。都是一元运算符取地址运算&写在变量描述(如变量名)前取变量地址,是对应类型的指针值,可赋给类型合适的指针。例: p=&n;q=p; p1=&a[1];整理ppt多个指针可能同时指向同一变量。变量相等是值相等,两个指针变量相等说明它们指向程序里同一东西。间接运算间接运算得到被指针所指的变量,这种表达式可以像普通变量一样使用。设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的元素*/整理ppt指针作为函数参数指针作为函数参数有特殊意义,利用这种参数可写出能修改调用时环境的函数。函数调用处的环境指在调用函数的位置能访问的变量全体。前面函数的特点:可使用调用处环境中变量的值(通过参数),但不能修改这些变量(数组参数除外)。在函数f里调用g,可将f的局部变量作为实参。但用g改变f局部变量的唯一方法是将g的返回值赋给f的局部变量。这种方法局限性太强,例如无法在一个函数调用中修改两个局部变量的值。利用指针可以改变这种情况。例:定义函数swap,希望用它交换两个变量的值。因为要改变两个变量,无法通过返回值解决。整理ppt下面定义不行:voidswap0(intx,inty){intt=x;x=y;y=t;}intf(…){inta=5,b=10;swap0(a,b);…/*不行*/}函数内修改形参,不会改变调用时的实参。分析:要(在一函数里)通过调用函数g修改调用处的变量(如局部变量),必须在g里掌握这个变量。整理ppt用指针可以解决问题。把m的地址(也是值)通过指针参数p传给g,函数内对p间接访问就能操作m,包括对m赋值(改变m)。整理ppt例,通过函数调用把变量值设置为3:voidset3(int*np){*np=3;}使用实例:intmain(){intn,m;set3(&n);/*设置n和m*/set3(&m);printf("%d,%d\n",n,m);return0;}请回忆scanf的情况。整理ppt通过参数改变调用环境的方案包括三方面:函数定义中用指针参数;函数内用间接操作实际变量;调用时以被操作变量的地址作为实参。函数swap可定义为:voidswap(int*p,int*q){
intt=*p;*p=*q;*q=t;}交换变量m和n的值,调用形式是:
swap(&m,&n);整理pptswap的参数类型是(int*),实参必须是合法的整型变量的地址。整理ppt设有变量定义: inta[10],k;调用swap的实例:swap(&a[0],&a[5]);swap(&a[1],&k);介绍标准库函数scanf时,强调在接受输入的变量前必须写&,就是将变量的地址传给scanf。scanf采用与swap一样的技术,通过间接访问为指定变量赋值,把输入的值赋给指定变量。整理ppt例:改造上章输入整数值并检查值范围的函数,引进指针参数,使之能更好处理输入错误。前面实现里用特殊整数值指明输入出错,有很大缺点(可能不存在合适的值)。指针参数提供了另一种方法:
函数增加一个指针参数,通过它送回读入值。用函数返回值传递函数执行的状态信息。返回1表示输入成功,0表示输入未能正常完成。
整理ppt新函数定义:
intgetnumber(charprompt[],intimin,intimax,intrepeat,int*np){inti;for(i=0;repeat<=0||i<repeat;++i){printf("%s",prompt);if(scanf("%d",np)!=1||*np<imin||*np>imax){printf("Correctrange[%d,%d].\n",imin,imax);while(getchar()!='\n');}elsereturn1;}return0;}整理ppt前面的调用现在可以重写为: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的样子。通过返回值表示函数的工作情况,是一种常用技术。整理ppt与指针有关的一些问题空指针值:一个特殊指针值,表示指针变量闲置(未指向任何变量)。唯一对任何指针类型都合法的值空指针值用0表示,标准库专门定义了符号常量NULLp=NULL;和p=0;相同前一写法易看到是指针,用时必须包含标准头文件。指针初始化指针变量定义时可用合法指针值初始化:intn,*p=&n,*q=NULL;若没有初始化,外部指针和局部静态指针自动初始化为用空;局部自动指针不自动初始化。整理ppt指针使用中的常见错误使用指针的最常见错误是非法间接访问:在指针未指向合法变量的情况下做间接。如:intf(...){int*p,n=3;*p=2;...}p没有初始化,没有指向合法变量。“悬空指针”指值不是(当时)合法的变量地址的指针变量,也常被称为“野指针”。间接访问悬空指针是严重错误,后果可能很严重。整理ppt常见错误写法(设p是悬空int指针,n是int变量):swap(p,&n);scanf("...",p);scanf("...",n);编译程序不能发现scanf的错误。有些系统可能对第一个例子(假设p未初始化)给出警告。间接访问空指针也同样无理,是非法的。整理ppt通用指针类型(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*)*/整理ppt其他使用方式没有任何保证。编译程序不能识别强制转换错误。指针类型转换并不改变指针值将整型变量n的地址赋给通用指针gpt,gpt存的就是n的地址。赋回整型指针不会有问题。将该地址赋给double指针,就造成了混乱。指针类型代表一种观点。被整型指针所指的变量总看成是整型的变量;被双精度指针指向……。指针转换是观点转换。从整型指针转换到通用指针就是丢掉类型信息。C保证恢复到原有类型(转回整型指针),被指对象还可用。整理ppt通用指针不能做间接运算被普通指针指向的变量的类型明确,间接后可以作为该类型的变量使用通用指针可以指向任何变量,通过通用指针间接访问的意义无法确定。通用指针没提供被指对象的类型信息,所以不能通过它们直接使用被指对象通用指针最无用,唯一用途就是提供指针值标准库的某些函数使用了通用指针(后面会看到)整理ppt7.3指针与数组C指针与数组关系密切,以指针为媒介可以完成各种数组操作。常能使程序更加简洁有效。用指针做数组操作同样要特别注意越界错误。指针和数组的关系是C语言特有的,除了由C派生出的语言(如C++),一般语言中没有这种关系。指向数组元素的指针类型合适的指针可以指向数组元素。假定有定义:int*p1,*p2,*p3,*p4;inta[10]={1,2,3,4,5,6,7,8,9,10};整理ppt可以写:p1=&a[0];p2=p1;p3=&a[5];p4=&a[10];
p4没指向a的元素,是指向a最后元素向后一个位置。C语言保证这个地址存在,但写*p4是错误的。写数组名得到数组首元素地址,元素类型的指针值。“p1=&a[0];”可简写为:p1=a;整理ppt指针运算当指针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]赋值*/整理ppt通过指针访问数组元素时必须保证不越界。运算取得的指针值(即使不间接访问)必须在数组范围内(可过末元素一位置),否则无定义。这类运算称为“指针运算”。其他常用指针运算:用指针运算得到的值做指针更新:p2=p2-2;/*这使p2改指向a[3]*/用增/减量操作做指针更新(指针应指在数组里):p3=p2;++p3;--p2;p3+=2;如果两指针指在同一个数组里,可以求差,得到它们间的数组元素个数(带符号整数)。n=p3–p2;/*也可以求p2–p3*/整理ppt指在同一个数组里的指针可以比较大小:if(p3>p2)....当p3所指的元素在p2所指的元素之后时条件成立(值为1),否则不成立(值为0)。两个指针不指在同一数组里时,比较大小没有意义。两个同类型指针可用==和!=比较相等或不等;任何指针都能与通用指针比较相等或不等,任何指针可与空指针值(0或NULL)比较相等或不等。两指针指向同一数据元素,或同为空值时它们相等。整理ppt数组写法与指针写法如果一个指针指在一个数组里,通过指针访问数组元素的操作也可用下标形式写。设p1指向数组a[0],p3指向a[5]。可写: p1[3]=5;p3[2]=8;p1[3]一类写法称为数组写法,*(p+3)一类写法称为指针写法。两类写法有等价效力,可以自由选用。整理ppt对数组名求值得到指向数组首元素的指针值数组名可以“看作”常量指针,可参与一些指针运算,与其他指针比大小,比较相等与不相等。通过数组名的元素访问也可以采用指针写法。a[3]可写为*(a+3)。注意:数组名不是指针变量,特别是不能赋值,不能更改。若a为数组,下面操作都是错误的: a++; a+=3; a=p;有些运算虽不赋值但也可能没意义。如a–3不可能得到合法指针值,因其结果超出数组界限整理ppt指针运算原理当一个指针指向某数组里的元素时,为什么能算出下一元素位置?(这是指针运算的基础)指针有指向类型,p指向数组a时,由于p的指向类型与a的元素类型一致,数据对象的大小可以确定。p+1的值可根据p的值和数组元素大小算出。由一个数组元素位置可以算出下一元素位置,或几个元素之后的元素位置。指针运算的基础。通用指针即使指到数组里,因没有确定指向类型,因此不能做一般指针计算,只能做指针比较。整理ppt基于指针的数组程序设计指针运算是处理数组元素的另一方式,有时很方便。设有int数组a和指针p1,p2,下面代码都打印a的元素:for(p1=a,p2=a+10;p1<p2;++p1)printf("%d\n",*p1);for(p1=a;p1<a+10;++p1)printf("%d\n",*p1);for(p1=p2=a;p1-p2<10;++p1)printf("%d\n",*p1);for(p1=a;p1-a<10;++p1)printf("%d\n",*p1);整理ppt数组参数的意义C规定,数组参数就是相应的指针参数:intf(intn,intd[]){......}和intf(intn,int*d){......}意义相同。数组参数的作用就是这样实现的。对应d的实参是被处理数组的名字,求值得到指针值,符合形参需要,使d指向该数组的“首元素”。前面函数体里参数用数组写法(对指针可这样写)。通过指针形参d访问的相应实参数组里的各元素。(数组参数就是利用指针实现的!)这也使采用数组参数的函数能修改实参数组。整理ppt函数里也可用指针方式做元素访问。intintsum(intn,inta[]){inti,m=0;for(i=0;i<n;++i)m+=*(a+i);returnm;}函数里不能用sizeof确定数组实参大小:函数的数组形参实际是指针,求sizeof算出的是指针的大小。所有指针大小都一样,它们保存的都是地址值,各种类型的地址值采用同样表示方式。另一方面,sizeof的计算是在编译中完成的。实参是动态运行中确定的东西。整理ppt使用数组的一段元素以数组为参数的函数可处理一段元素。求元素和:doublesum(intn,doublea[]);设有双精度数组b,40个元素已有值:用sum可求b所有元素之和/前一段元素之和: x=sum(40,b); y=sum(20,b);sum不知道b的大小,它由参数得到数组首元素地址,从这里开始求连续40或20个元素的和。也可用sum求b中下标12到24的一段元素之和。 z=sum(13,b+12);整理ppt指针与数组操作函数实例例1,用指针方式实现字符串长度函数。一种方式:intstrLength(constchar*s){intn=0;/*通过局部指针扫描串中字符*/while(*s!='\0'){s++;n++;}returnn;}另一实现:intstrLength(constchar*s){char*p=s;while(*p!='\0')p++;returnp-s;}参数类型(char*),实参应是字符串或存字符串的数组整理ppt例2,用指针实现字符串复制函数。直接定义:voidstrCopy(char*s,constchar*t){while((*s=*t)!='\0'){s++;t++;}}赋值表达式有值,'\0'就是0,函数可简化:voidstrCopy(char*s,constchar*t){while(*s=*t){s++;t++;}}把指针更新操作也写在循环测试条件里,程序是:voidstrCopy(char*s,constchar*t){while(*s++=*t++);//空语句}注意优先级与结合性,增量运算的作用与值等。整理ppt例3,利用指针,输出int数组里一段元素:voidprt_seq(int*begin,int*end){for(;begin!=end;++begin)printf("%d\n",*begin);}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);最后两个调用对应空序列。序列为“半闭半开”。
整理ppt还可写出许多类似函数。“设置”函数:voidset_seq(int*b,int*e,intv){ for(;b!=e;++b)*b=v;}把序列中每个元素都用其平方根取代:voidsqrt_seq(double*b,double*e){for(;b!=e;++b)*b=sqrt(*b);}求平均值:doubleavrg(double*b,double*e){double*p,x=0.0;if(b==e)return0.0;for(p=b;p!=e;++p)x+=*p;returnx/(e-b);}整理ppt字符指针与字符数组常用字符指针指向字符数组元素。如指向常量字符串或存着字符串的字符数组,通常指向字符串开始。也可指到字符串中间,把指的东西当字符串用。整理ppt定义字符指针时可用字符串常量初始化,如:char*p="Programming";1)定义了指针p2)建立了一个字符串常量,内容为"Programming"3)令p指向该字符串常量。图(a)chara[]="Programming";1)定义了一个12个字符元素的数组2)用"Programming"各字符初始化a的元素,图(b)整理ppt1)指针p可重新赋值(数组不能赋值):p="ProgrammingLanguageC";2)p和a类型不同,大小不同。a占12个字符的空间。3)a的元素可以重新赋值。如:a[8]='e';a[9]='r';a[10]='\0'; a的内容现在变成“Programmer”按规定,字符串常量不得修改整理ppt可定义字符指针变量并让它指向已有字符数组。C程序常用这种方式使用和操作字符数组内容。例如,输入一行到数组里:enum{NLINE=256};charline[NLINE];intcount;char*p;/*-----------------------------------------*/p=line;while(p–line<NLINE-1&&(*p=gerchar())!='\n')++p;*p='\0';/*做成字符串*//*-----------------------------------------*/for(count=0,p=line;*p!='\0';++p)if(*p=='e')++count;/*统计e的个数*/整理ppt7.4指针数组复杂C程序里常用到指针的数组。例:需要一组字符串,常用字符指针数组索引它们。如软件中错误信息常用一组字符串表示。分散管理不便。可定义指针数组,指针分别指向输出信息串常量。也可定义其他类型的指针数组,如指向整数或者其他类型的指针的数组,下面讨论以字符指针为例。定义字符指针数组:
char*pa[10];优先级也适用于定义。[]优先级高,pa是数组,其元素是字符指针。整理ppt定义字符指针数组时用字符串常量提供初始值。例:char*days[]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};简单实例:printf("Workdays:");for(i=1;i<6;++i)printf("%s",days[i]);printf("\nWeekend:");printf("%s%s\n",days[6],days[0]);整理ppt字符指针数组实例:改写第6章的C语言关键字统计程序,把原来的两维字符数组keywords改为字符指针数组。只需定义下面数组,并用(关键字)字符串对各指针做初始化:char*keywords[]={"auto","break",........"volatile","while"};其他部分不需要改,程序可以正常工作。整理ppt指针数组与两维数组两维字符数组与字符指针数组不同。定义:charcolor1[][6]={"RED","GREEN","BLUE"};char*color[]={"RED","GREEN","BLUE"};整理ppt命令行参数的处理启动程序的基本方式是输入命令,要求OS装入程序代码文件并执行。命令行:描述命令的字符行。在图形用户界面系统(如Windows)里,命令行存在于图标/菜单的定义中。源文件abcd.c得到可执行文件abcd.exe。键入命令:abcd该程序就会被装入执行。整理ppt除命令名外,命令行常包括其他信息。DOS命令:copya:\file1.txtdir\windows\system/p附加信息也是字符序列,称为命令行参数。前面的程序都没有包含处理命令行的功能要写能够处理命令行参数的程序,需要用C语言的命令行参数机制。处理命令行参数很像处理函数参数,写程序时要考虑和处理程序启动时实际命令行提供的信息。整理ppt命令行被看作空格分隔的字段,各个命令行参数。命令名编号为0,其余依次编号。程序启动时把各命令行参数做成字符串,程序里可按规定方式使用。设有程序prog1;设启动程序的命令行是:prog1therearefivearguments这时prog1是编为0的命令行参数,there是编号1的命令行参数,…;共5个命令行参数。通过main的参数可获取命令行参数。main(void)表示不处理命令行参数,main的另一形式带两个参数:intmain(intargc,char*argv[]);整理pptmain开始执行时:argc是命令行参数的个数argv指向含argc+1个指针字符指针数组,前argc个指针指向各命令行参数串,最后有一个空指针图为执行下面命令时main函数里的现场情况prog1therearefiveargumentsmain参数常用argc、argv作为名字(实际上可以用其他名字)。参数类型确定。整理ppt可由argc得到参数个数,通过argv访问它们。可以访问启动程序的命令名本身。在一些系统里,0号参数还包括完整的目录路径。例:写程序echo打印各命令行参数。写程序时不知道调用时的命令行参数是什么,但可以打印它们:#include<stdio.h>intmain(intargc,char*argv[]){inti;for(i=0;i<argc;++i)printf("Args[%d]:%s\n",i,argv[i]);return0;}书上有另一种定义方式,其中利用了最后的空指针整理ppt用IDE开发程序时,编辑/调试/执行等工作都在环境里完成,执行程序时如何提供命令行参数?集成开发环境都有专门机制为启动命令行提供参数(如Turbo-C的Options/Arguments)。可转到IDE之外在命令行状态下启动程序。在图形用户界面系统里,有关命令行参数的讨论同样有效。建立程序项、命令菜单项等也要写出实际命令行,包括提供必需的命令行参数。一些图形界面系统里可把数据文件拖到程序文件上作为处理对象。此时将自动产生一个命令行。整理ppt7.5多维数组作为参数的通用函数函数的两维或多维数组参数必须说明除第一维外各维的大小,这使函数失去了一般通用性。ANSIC没提供定义处理多维数组的通用函数的标准方法。可以通过技术解决。(C99提供了标准方式)下面以两维数组为例,多维数组可以类似处理。考虑:intfun1(intn,intmat[][10]){......mat[i][j]......}只能对第二维长10的数组使用。不能处理其他数组整理pptmat的指向类型是int数组(两维数组的元素是一维数组)。定义没给出一维数组大小,指针定义不完全这样,编译程序虽然知道mat[0]的位置,但却无法算出mat[1]等子数组位置以及mat[i][j]位置。因此编译工作无法完成。实际中确实需定义处理多维数组的通用函数。改写为:intfun2(intn,intm,intmat[][]){......mat[i][j]......}这个定义错误,无法通过编译。为什么?整理ppt解决方案:考虑数组inta[10][8];首元位置&a[0][0]。每行8元素,第1行开始位置是:&a[0][0]+8访问第1行首元素用表达式:*(&a[0][0]+8)访问第i行首元素用表达式:*(&a[0][0]+i*8)访问a[i][j]:*(&a[0][0]+i*8+j)可见,有了一些信息后,可以算出元素的位置整理ppt处理两维数组所需信息:1)基本元素类型;2)数组两个维的长度;3)数组开始位置。通过参数可得到数组的首元素位置和各维长。输出两维整型数组的函数,每行输出在一行。voidprtMatrix(intm,intn,int*mp){inti,j;for(i=0;i<m;++i){for(j=0;j<n;++j)printf("%d",*(mp+i*n+j));putchar('\n');}}打印数组a和另一20×36的整型数组mat的调用形式:prtMatrix(10,8,&a[0][0]);prtMatrix(20,36,&mat[0][0]);整理ppt7.6动态存储管理变量(简单变量/数组等)用于保存数据,需安排存储(称为存储分配)。高级语言编程不需要考虑存储细节,有关工作由编译程序完成。编程效率高。在C语言里外部变量/局部静态变量在编译的时候确定存储,开始执行前分配存储自动变量在执行进入定义函数时分配存储共同性质:变量大小都是静态确定的。整理ppt例:函数中变量和参数决定了函数执行时所需要存储空间量,C语言要求自动数组的大小用静态表达式描述。这样,函数需要的存储量就可在编译时确定。静态处理存储的优点是方便,效率高,执行中的工作简单,速度快。但对编程方式加了限制,有些问题不好解决。整理ppt例:要处理学生成绩,需要用数组存放。但编程时并不知道运行时需要处理多少学生成绩,每次处理的成绩项数也可能不同。能否先通知数据项数,再建数据表示?理想方式(现在行不通): intn; ... scanf("%d",&n); doublescores[n];/*不行!*/ .../*读入数据,然后处理*/不能用变量说明scores大小(必须静态确定)。至今讨论的机制无法很好解决这类问题。整理ppt可能解决方案:1)分析问题,定义适当大小的数组。若分析正确,一般都能处理。但数据很多时程序就不能用。2)定义尽可能大的数组以满足任何需要。浪费大量存储资源。如有多个这种数组就更难办。系统可能无法容纳几个大数组,但实际上它们并不同时需要很大空间。解决的办法是“动态存储分配”。在程序运行中做存储分配工作。这里的问题:程序运行中需要使用存储,有时程序对存储的需求量在写程序时不能确定。整理ppt动态存储分配与释放根据运行中的需要分配存储,取得存储块使用,称为动态存储分配。在运行中根据需要动态进行。程序里怎样使用分配的存储块?程使用变量是通过名字。动态分配的存储块没有名字,因此需要其他访问途径。借助于指针。用指针指向存储块,间接使用被指存储。访问动态分配存储是指针的最重要用途。与此对应:动态释放,不用的动态存储块应交还。动态分配/释放由动态存储管理系统完成,这是程序运行系统的子系统,管理着称作堆(英文heap)的存储区。大部分常规语言都有这种机制。整理pptC语言的动态存储管理机制用标准库函数实现,<stdlib.h>/<malloc.h>1)存储分配函数malloc()。原型:void*malloc(size_tn);/*size_t是某整型*/分配一块不小于n的存储,返回其地址。无法满足时返回空指针值。intn;double*data;...scanf("%d",&n);data=(double*)malloc(n*sizeof(double));if(data==NULL){..../*分配未完成时的处理*/}..data[i]..*(data+j)../*正常处理*/整理pptmalloc的返回值(void*)应通过类型强制转为特定指针类型后赋给指针变量。使用注意事项:分配存储块大小应该用sizeof计算动态分配必须检查成功与否动态分配的块大小也是确定的。越界使用(尤其是越界赋值)是严重错误,可能导致程序或系统垮台整理ppt2)带计数和清0的存储分配函数calloc。原型:void*calloc(size_tn,size_tsize);size是元素大小,n是个数。分配一块存储,足够存n个大小为size的元素,并把元素全部清0;无法分配时返回空指针值。前面的存储分配问题也可用下面语句实现:data=(double*)calloc(n,sizeof(double));主要差别:malloc对所分配的区域不做任何事情,calloc对整个区域自动清0。整理ppt3)动态存储释放函数free。原型:voidfree(void*p);free释放p指的存储块。注意:该块必须是通过动态存储分配得到的p值为空时什么也不做执行free(p)后p值未变,被指块可能已变。不允许间接访问已释放存储块不要对并非指向动态分配块的指针用本操作为保证动态存储的有效使用,动态分配块不再用时应释放。动态存储块的释放只能通过调用free完成。整理ppt程序例子:intfun(...){int*p;...p=(int*)malloc(...);...free(p);return...;}/*退出函数前应释放函数内分配且已无用的动态存储*/fun退出时p存在期结束,若没有访问分配块的其他途径,将不可能再用到函数里分配的存储块。动态存储的流失。如程序长期执行,存储流失就可能成为严重问题。对实际系统可能是很严重的问题。整理ppt4)分配调整函数realloc。函数原型是:void*realloc(void*p,size_tn);更改已有分配。p指原分配块,n是新大小要求。返回大小至少为n的存储块指针。新块与原块一致:新块小时保存原块n范围内数据;新块大时原数据存在,新增部分不初始化。分配成功后原块可能改变。无法满足时返回空指针,原块不变。常用写法(防止分配失败导致原存储块丢失):q=(double*)realloc(p,m*sizeof(double));if(q==NULL){/*未成功,p仍指原块,特殊处理*/}else{p=q;/*令p指向新块,正常处理*/...}整理ppt例1:修改筛法程序,由命令行参数得到所需范围。如无命令行参数则要求用户输入确定范围的整数值。
先考虑整体设计。为了清晰,将筛法用函数实现。命令行参数(字符串)需要转换到整数,定义函数:ints2int(constchar*s);
函数实现(标准库有类似函数atoi):ints2int(constchar*s){intn;for(n=0;isdigit(*s);++s)n=10*n+(*s-'0');returnn;}整理ppt筛法计算包装为函数:voidsieve(intlim,intan[]){inti,j,upb=sqrt(lim+1);an[0]=an[1]=0;//建立初始向量for(i=2;i<=lim;++i)an[i]=1;for(i=2;i<=upb;++i)if(an[i]==1)//i是素数for(j=i*2;j<=lim;j+=i)an[j]=0;//i的倍数不是素数}main的工作:1)获取范围(由命令行或用户),2)分配空间及初始化,3)执行筛法,4)打印输出。整理pptenum{LARG=65535};intmain(intargc,char**argv){inti,j,n,*ns;if(argc==2)n=s2int(argv[1]);elsegetnumber("Largest:",2,LARG,5,&n);if(n<2||n>LARG){printf("Largestmustin[2,%d]",LARG);return1;}ns=(int*)malloc(sizeof(int)*(n+1))if(ns==NULL){printf("Noenoughmemory!\n");return2;}sieve(n,ns);.../*输出略*/free(ns);return0;}整理ppt例2,改造成绩直方图程序,使之能处理任意个数据。如何处理事先无法确定数目的数据集合。用数组限制了能处理的项数,现在改用动态分配。让getscores根据需要申请存储块,返回动态分配的块和实际项数。函数原型:double*readscores(int*np);由np送回项数无法分配存储时返回NULL如果已读了部分数据,但在扩大存储失败,就给出信息并返回部分数据整理ppt主函数:intmain(){intn;double*scores=readscores(&n);if(scores==NULL)return1;.../*其他不必修改*/}由于原来的设计比较得当,现在只需做少许修改由此可见良好设计的重要性整理pptreadscores的定义:事先不知道数据项数,可以先分配一块,读入中发现不够用时扩大。开始分配多大?采用什么扩大策略?下面采用开始分配一块,随后不够时加倍的策略。有关存储分配和扩大策略的讨论见书。这个定义主要显示分配调整技术,没有追求完善。读入中遇到错误数据就立即结束。数据检查和处理问题前面已讨论过,修改这个函数,使之能合理处理输入数据错误,给出有用信息,或增加其他有用功能等都留作练习。整理pptenum{INUM=40};double*readscores(int*np){unsignedsize=INUM,n;double*q,x,*p;if((p=(double*)malloc(INUM*sizeof(double)))==NULL){printf("Nomemory.Stop\n");*np=0;returnNULL;}for(n=0;scanf("%lf",&x)==1;++n){if(n==size){/*块满了,需要重新分配*/size*=2;q=(double*)realloc(p,size*sizeof(double));if(q==NULL){printf("Processonly%dscores.\n",n);break;}p=q;}p[n]=x;}*np=n;returnp;}整理ppt函数、指针和动态存储分配要用函数处理一组数据,得到处理结果,最好的方式是直接为函数提供数组位置和元素个数(或结束位置)。前面经常采用这种方式。这时函数不必知道处理的是数组变量还是动态存储。例如,完全可以用如下方式调用筛法函数:intns[1000];intmain(){...sieve(1000,ns);...return0;}这时存储的问题在一个层次中管理(定义变量或动态分配)。责任清晰,易于把握,是最好的处理方案。
整理ppt有时无法采用上述方法。例如前面直方图程序,只能由readscores根据情况分配存储,送出存储块地址。main用指针接收。这种做法完全正确动态分配的块将一直存在到明确释放为止(直到对它调用free),与分配所在的函数无关。readscores返回存储块地址不仅传回数据,也将管理这个块的责任转交给main。可见前面的main结束前缺了free(scores)调用。应该加上。整理ppt前面的readscores返回块地址,由参数传回块的大小(将int变量地址给int*)。另一种可能方式是让readscores返回块大小,无法分配存储时返回0。现在考虑这种设计的问题:调用方式:if(readscores(...)==0){/*处理错误*/}函数原型应该是:intreadscores(???);/*参数类型?*/现在需要的是通过实参得到动态块的地址,这是一个(double*)值整理ppt已知,要想通过函数的参数取得送出来的int值,参数就应该为(int*)类型,实参用int变量地址要通过参数送出(double*)类型的值,实参就应该传递(double*)变量的地址。因此,形参应该是指向(double*)类型的指针,也就是(double**)类型正确的原型:intreadscores(double**dpp);调用:if(readscores(&scores)==0){/*错误处理*/}这里讨论了不同函数层次之间存储分配、传递和使用的几种技术。各有适用之处,可根据情况选择。整理ppt关于动态存储分配计算机系统的内存由OS(操作系统)管理。启动一个程序时,OS为它分配内存,存放它的代码和数据。程序结束时OS收回该程序所占用的所有内存。C程序启动时从OS得到的内存,其中一大块由自己的动态存储管理系统管理。程序里调用malloc将在这块里分配存储,free将内存块退回动态存储管理系统。这是C程序内部的事情。至于C动态存储管理系统如何与OS打交道的问题是看不见的(透明的)。一个C程序结束时,OS收回原先分配给它的全部内存。这是OS的存储管理问题,与我们的程序无关。整理ppt7.7定义类型基本类型有类型名,可用于定义/说明变量,描述函数参数与返回值,做类型强制等。数组、指针等等可能使说明变得很复杂,使用不便。也不容易保证多个类型描述的一致性。如果能把复杂类型描述看作类型(用户定义类型)加以命名,可带来很大方便,特别是在实现复杂程序/软件系统时。定义类型是重要语言机制,定义好的类型最好能像内部类型一样使用。C语言类型定义机制较弱,其主要作用是简化描述。整理ppt类型定义用关键字typedef,其后的描述形式与变量定义相似,使原变量位置的标识符成为新类型名。typedefunsignedlongintULI;定义后的ULI可以像基本类型名一样用:ULIx,y,*p;ULIfun1(doublex,ULIn);p=(ULI*)malloc(n*sizeof(ULI));这种类型定义可简化程序书写,有一定实际价值。整理ppt有时定义新类型可提高可读性和清晰性。如:typedefdoubleLENGTH;typedefdoubleAREA;定义不同类型名可能帮人看到不一致情况。注:C语言认为定义的只是原类型的别名。LENGTH和AREA是double的别名。提高程序的可读性。用预处理命令可产生类似效果。如:#defineLENGTHdouble#defineAREAdouble但两种写法处理过程不同,类型定义由编译程序处理。有些类型不能通过宏的方式(例如数组类型)。整理ppt定义数组类型数组类型定义形式符合前面解释。例: typedefdoubleVECT4[4];此后可以写:VECT4v1,v2;intintprod(VECT4v,VECT4u);5×5的双精度数组类型:typedefdoubleMAT[5][5];MATa1,a2,a3;/*定义5×5数组变量*/doubledet(MATm);/*说明函数参数*/MAT*p=(MAT*)malloc(sizeof(MAT));整理ppt定义指针类型例如:typedefint*IP;typedefMAT*MATP;用宏定义代替它们,可能造成意想不到的结果。例如写#defineMIPint*而后希望定义两个指针:MIPmp1,mp2;可发现mp2不是指针,而是一个int变量。只要把这个描述按规则展开,就不难发现其中的问题。宏是低级正文代换机制,应尽量少用。这又是一例。需要定义类型名时,应该用typedef。整理ppt复杂类型描述与解读类型描述可能变得很复杂(C语言的缺点)。应尽量不写复杂描述。用typedef分解,易理解/少出错。定义后可多次使用/节省时间/方便维护。读程序时可能遇到复杂类型描述。应了解类型描述的一般构造及解读方式。描述类型时也能更清楚,知道怎样写,什么地方需要加括号等。类型描述中可出现:已定义类型名,被定义标识符,构造符号(三个(组)运算符): * [] () 指针 数组 函数可加圆括号改变结合关系。又增加了理解难度。整理ppt解读方式:符号意义不变,优先级和结合关系按运算符规定。[]和()结合性强,*结合力弱;[]和()从左到右结合,*从右向左结合。首先辨认被说明(定义)的标识符,从它向外分析。这个标识符常出现在描述中间,被其他符号包围。一些例子:1)int*f(int);被说明的是f,f是一个函数。2)int(*fp)(int);说明的是fp。fp是一个指针,指向...3)char**argv;argv是个指针,指向(char*)整理ppt4)int(*dp)[16];dp是指针,它指向有16个元素的整型数组。5)int*dp1[16];dp1是个16个元素的数组,其元素是整型指针。6)int(*(*g(int))[4])(double);被说明的是函数g,有一整型参数,返回指针,指针指向4个元素的数组,数组元素是指向函数的指针,被指函数有一个双精度参数并返回整数值。类型描述就是用三种构造符号及表示结合性的括号,逐层构造起来。很复杂的类型描述非常少见。有些构造不合法:函数不能返回数组,函数不能返回函数,函数不能作数组元素等。整理ppt利用typedef能更清晰地描述复杂类型。下面以例6)为例。实际分解应该根据需要,看哪个(哪些)层次的类型有逻辑意义,有用处。原描述:int(*(*g(int))[4])(double);先定义函数指针类型和函数指针数组类型:typedefint(*funp)(double);typedeffunpfparray[4];有这两个类型,g的类型可以简单地说明为:fparray*g(int);g是函数,有一个int参数,返回指向fparray的指针上面定义的两个类型也可以用于定义变量等。整理ppt7.8指向函数的指针例:设需要定义求数学函数的根。前面定义过一个函数,但那只是玩具,只能当作练习答案,实际中没什么用。原因:求根函数规定了被求根数学函数的名字。规定函数名则使求根函数不能用于多个函数,只能对一个特定的函数求根。程序可能需要求多个函数的根,应只写一个求根函数。提高函数通用性方法:引进新参数。要使求根函数能处理不同的数学函数,必须为它引进与函数有关的参数。整理ppt其他许多语言也支持函数的“函数参数”,但采用的方式可能与C语言不同。C采用函数指针是为简化语言,同时又能提供最大灵活性,可以支持其他程序设计技术。C语言里的函数指针也有类型,一个函数指针只能指向某种特定类型(具有特定原型)的函数C允许指向函数的指针(函数指针),可通过这种参数传递被处理函数,对不同调用可传递不同函数。函数指针是C语言里指针的另一重要用途整理ppt要定义带有函数指针参数的函数,最好是首先定义函数指针类型。指向数学函数的指针类型:typedefdouble(*MFP)(double);这里假定数学函数都是有一个双精度参数、返回双精度值的。注意类型MFP的定义形式。(*MFP)的括号不能少,去掉后意义就变了。下面定义的FUNP是另一函数指针类型,指向有两个int参数,返回double指针值的函数:typedefdouble*(*FUNP)(int,int);整理ppt函数指针变量的定义和使用用函数指针类型定义指针变量:MFPp1,p2;没定义指针类型时定义必须写(不提倡):double(*p1)(double),(*p2)(double);给函数指针变量赋值:对函数名求值,得到的是指向该函数的指针值,可以赋给类型合适的函数指针变量。无论是库函数,还是自己定义的函数,都可以赋给类型合适的函数指针变量整理ppt例,(设已经包含<math.h>)函数指针赋值:p1=sin;赋过值的函数指针可直接当作函数使用,调用被指函数。在上面赋值之后,写x=p1(3.24);
相当于写 x=sin(3.24);写间接也可以(注意括号):x=(*p1)(3.24);通过函数指针调用函数,被调函数由函数指针当时的值决定。同样语句在不同时刻可能调用不同函数。为程序提供了新的灵活性。绝不能调用悬空的函数指针!整理ppt函数指针作为函数的参数求根问题:以函数指针作为求根函数的参数。重新定义弦线法求函数根的函数:doublecross(MFPfp,doublex1,doublex2){doubley1=fp(x1),y2=fp(x2);return(x1*y2-x2*y1)/(y2-y1);}doubleroot(MFPfp,doublex1,doublex2){doublex,y,y1=fp(x1);do{x=cross(fp,x1,x2);y=fp(x);if(y*y1>0.0){y1=y;x1=x;}elsex2=x;}while(y>=1E-6||y<=-1E-6);returnx;}整理ppt函数使用实例(设fun是类型合适的函数):x=root(sin,0.4,4.5);y=root(fun,1.26,7.03);可以在函数头部直接描述函数指针参数,如:doubleroot(double(*fp)(double),...)...采用先定义类型的写法,程序更清晰易读,也有利于一致性的维护和程序的修改。在任
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年揭阳货运从业资格证考题
- 2025年德州下载b2货运从业资格证模拟考试考试
- 2025年商丘驾校考试货运从业资格证模拟考试
- 电视台合同范本(2篇)
- 电力服务绩效合同(2篇)
- 山西省阳曲县八年级地理上册 第二章 自然环境 我们赖以生存的基本条件说课稿 晋教版
- 2024-2025学年五年级语文上册第二单元5装满昆虫的衣袋教案设计苏教版
- 2024-2025学年高中历史第四单元中国社会主义建设发展道路的探索第19课经济体制改革教案含解析岳麓版必修2
- 马栗种子提取物片说明书
- 湘教版地理八年级下册:9 建设《永续发展的美丽中国》 听课评课记录
- 对违反政治纪律行为的处分心得体会
- 大学生职业生涯发展与规划(第二版)PPT完整全套教学课件
- 《深度学习革命》读书笔记思维导图PPT模板下载
- SAP可配置产品学习课件
- 传统运动疗法易筋经教案5
- GB/T 8014.1-2005铝及铝合金阳极氧化氧化膜厚度的测量方法第1部分:测量原则
- 股票基础知识(入市必读)-PPT
- 雅思阅读题型与技巧课件
- 招商银行房地产贷款压力测试
- 公文与公文写作课件
- 车削成形面和表面修饰加工课件
评论
0/150
提交评论