ImageVerifierCode 换一换
格式:DOCX , 页数:20 ,大小:24.86KB ,
资源ID:7583524      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/7583524.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(笔记整理之函数.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

笔记整理之函数.docx

1、笔记整理之函数笔记整理之函数1、C/C+语言中的变量类型1.变量的分类2.四类存储类型3.register的限制4.变量的作用域与生命期(文件与函数级)5.static变量特点6.存储区之堆和栈(摘录)2、函数简介1.函数的声明与定义2.函数参数3.动态分配的局部变量之异位引用4.函数黑盒性3、传值调用及引用调用1.函数调用2.传值调用3.引用调用4、main函数与自定义函数1.main函数2.自定义函数5、指针函数与函数指针1、指针函数2、函数指针3、函数指针的功能6、函数递归、函数重载和inline函数1.inline函数2.函数递归3.函数重载4.函数压轧7、常见三种排序方式1.选择排序

2、2.冒泡排序3.插入排序4.标准排序函数sort()1、C/C+语言中的变量类型1、变量的分类按变量作用域(即空间)可分为全局变量和局部变量;按变量值存在时间(即生命期)可分为静态存储类型和动态存储类型。全局变量属于静态存储,即static类型;而函数形参、局部变量、函数调用时的现场保护和返回地址等为动态存储。2、四类存储类型分别为:static(静态),auto(自动),extern(外部),register(寄存器)。其中auto和register都属于局部变量,不可声明为全局变量。Auto局部型变量是被分配在内存的堆栈中。另外,这四个关 键字都是作为修饰符使用,来说明一个变量的存储类型,

3、加载一般变量声明语句之前,如:Static int i=0;/这里的i为int型静态存储型变量而在无任何其他存储类修饰符修饰时,局部变量默认为auto型存出类型。另,对于全局变量和静态存储型变量,系统在其声明且未初始时,会以默认方式给以初始化。3、register的限制将一个变量声明为register类型,该变量的存储都将在CPU中的寄存器中存储和读取,提升变量读取和存放的速率。由此,便不能对register变量进行求地址运算,它的值不在内存中。而且,只有局部自动变量和形式参数可以作为寄存器变量,即寄存器变量属于动态存储类别。4、变量的作用域与生命期(文件与函数级)这部分对作用域仅作简单介绍。

4、作用域即指一个变量可以涉及的程序块区域,在这个区域是可见的;而生命期指一个变量存在的时间段。一般来说,一个变量的作用域自其声明语句开始,不过函数内部静态变量的作用域自其第一次声明语句开始到其后任意一次函数执行涉及的语句块。全局变量作用域为整个源程序。其生命期是其生命开始至程序文件结束。Auto型局部变量的作用域在其最邻近包含语句块内部,其生命期也只在该语句块执行期间。同一层语句块不允许同名的两个变量存在。但不相交接的语句块层可以有同名变量(以下例子中也存在)。对于相包含的两个作用域中出现同名变量时,遵循外层隐蔽,内层可见的操作原则。(一般是在函数级作用域及以上)而静态变量生命期一般为全局性的,

5、自其生命开始至文件运行结束。所以在对一个变量进行操作前,应注意是否在其作用域范围以及其生命期之内。有以下程序片段:#includeusing namespace std;int check() i+; return i;int i;int main() for(int i=0;i10;i+) coutcheck()endl; return 0;在执行过程中,系统会报错,如:error C2065: i : undeclared identifier因为在函数check()函数体中有对变量i进行操作,而i的声明是在之后。5、static变量特点Static是作为静态存储的关键字,用其声明的变量具

6、有多种特性:A、在函数内部声明时,变量的值在函数调用过程中维持原值不变。其生命期是函数第一次调用,变量声明处开始,知道程序运行结束。期间其值一直保存最近一次被修改的值。并且自第一次声明语句结束之后,该声明语句在之后的程序进程中失效。如以下程序:#includeusing namespace std;int check();int main() for(int i=0;i5;i+) coutcheck()endl; return 0;int check() static int i=1; i+; return i;输出结果为:23456在本程序运行中,除第一次static int i=1;声明语

7、句有效外,其他时刻都被忽略。而auto型局部变量,其生命期只在该函数执行至其声明语句开始至函数一次调用结束。B、使用static声明的模块层变量能被模块内所有函数访问。C、使用static声明的模块层变量不能被模块外其他函数访问。(主要应用于文件内部变量和内部函数的声明,用以防止其他文件通过头文件引用和外部声明方式来使用它)6、存储区之堆和栈(摘录)一般认为在c中分为这几个存储区:1栈 - 有编译器自动分配释放 2堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局

8、变量和未初始化的静态变量在相邻的另一块区域,程序结束时释放 4一个专门放常量的地方,在程序结束时释放 在函数体中定义的变量通常是存储在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区)。另外,函数中的adgfdf这样的字符串存放在常量区。 比如: int a = 0; 全局初始化区 char *p1; 全局未初始化区 main() int b; 栈char s = abc;栈 char *p2; 栈 char *p3 = 123456; 1234560在常量区,p3

9、在栈上。 static int c =0;全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得的10和20字节的区域就在堆区。strcpy(p1, 123456); 1234560放在常量区,编译器可能会将它与p3所指向的123456优化成一块。 还有就是函数调用时会在栈上有一系列的保留现场及传递参数的操作。栈的空间大小有限定,vc的缺省是2M。栈不够用的情况一般是程序中分配了大量数组和递归函数层次太深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管理的,不用你操心。堆是动态分配

10、内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏,并且频繁地malloc和free会产生内存碎片(有点类似磁盘碎片),因为c分配动态内存时是寻找匹配的内存的。而用栈则不会产生碎片。在栈上存取数据比通过指针在堆上存取数据快些。一般大家说的堆栈和栈是一样的,就是栈(stack),而说堆时才是堆heap。栈是先入后出的,一般是由高地址向低地址生长。 2、函数简介1、函数的声明与定义对某种功能用若干语句进行实现所获得的抽象即为函数,如fabs()即为绝对值求值实现的抽象。A、函数原型函数原型声明一般包括类型说明、函数名、参数列表,如下形式:(返回值类型) (参数表);如果函数仅为若干过程

11、语句,不需返回值,可声明返回值类型为void型。注:在C语言中,自定义函数如未声明返回值类型,则默认其为int型。C+则并须声明有返回值类型。B、函数定义函数定义格式:(返回值类型) (参数表)函数体;/用于实现特定功能的语句块注:函数不可以嵌套定义。2、函数参数一般函数参数在声明时可允许仅说明参数类型,如:Int f(int ,float );但在函数定义时如果要在函数体中调用形参,必须为其添加变量名,如: Int f(int s,float i)函数体;函数的形参允许设置默认值,但需注意以下几点:A、指定默认值时,要从参数表有右端开始,在制定了默认值的参数右边不允许有未指定默认值的形参出现

12、,如:Int f(int a,int m=0,int n);/该声明将使编译器报错Int f(int a,int m=0,int n=2);/此声明正确B、在函数调用时,给定的实参值将替换默认值,为给定实参值的形参将使用默认值。C、如果一个函数需要说明,默认的参数值应设置在函数的说明中,而不是在函数的定义中。当没有函数说明时,默认的参数值可设在函数的定义中。D、再给形参设置默认值时,可以是数值,也可以是表达式。对于表达式,默认值一般由全局变量组成,也可以是函数,但不可以是局部变量。因为默认参数的函数调用是在编译时确定的,而局部变量在编译时无法确定。3、动态分配的局部变量之异位引用在变量类型中讲

13、到过,函数形参属于动态存储类,是局部变量。而对于函数体中所有局部变量,都是采取动态分配的形式,且自定义函数与主函数的内存分配空间相分离。每次调用函数结束后,函数内部所有动态存储类变量名都将被销毁,但其原来所在的地址中的值是被保存下来的,这些内存此时已被释放(释放并不代表值被清空),供下一个自定义函数调用时分配使用。由此,在使用多个指针函数(会在下文提到)时,会出现一个有趣的现象(我仅在此称它为异位引用,下文好叙述一些),借助这个例子可以更理解关于函数调用时内存分配的问题,接下来我们认识一下它的奇特之处。先看一个代码:#includeusing namespace std;int* f(int

14、a,int b)/ab,返回1;否,返回0 int j=ab; return &j;int *f1(float c,float d)/cd,返回1;否,返回0 int k=cd; return &k;main() int a=10,b=1,*p,*p1; float c=1.1,d=3.1; p1=f1(c,d); p=f(a,b); cout*p1endl;这段代码很简单,从实际应用来看,确实没多大价值,而且采用这种函数体内局部变量地址返回还会产生警告。先不管这些不足,先分析一下,结果。从运算上来看,输出值应该为0.相信没有前面的铺垫叙述,很多人对此都深信不疑,难道判断不对么?不是的,判断是

15、对的,在执行p1=f1(c,d); 结束后,p1指向地址所存入的值确实应为0,但在p=f(a,b); 之后,p1指向地址的值发生变化,被修改为1,即输出为1;如果在主函数末尾加上“coutp p1endl;”你会惊奇地发现其输出地址值相同,这也是P1值被修改的原因。由于函数f1()内部局部变量k在其调用结束后被销毁,其内存被系统在调用f()函数时重新分配给了j,才会发生这样的结果。另外,注意到参数类型没有影响分配函数体中局部变量内存时对应的位置,是因为参数的内存分配位置与其内部变量略有不同,互不影响。那是因为系统会先识别不同函数形参的类型和个数,并为其分配替换地址。对于异位引用现象,在平常编程

16、中不常遇到,只有在定义了多个结构相似的函数并做地址返回操作时,发生的可能性较大,这种错误一旦发生不易检查出来。除了函数体中局部变量外,对参数列表中的局部变量进行地址返回操作也同样能导致异位引用的发生。3、传值调用及引用调用1、函数调用一个构造完成的函数可供我们调用来实现特定功能。函数的调用过程实际上是对栈空间的操作过程,因为调用函数是使用栈空间来保存信息的。其调用过程大致描述如下:A、建立被调用函数的栈空间;B、保护调用函数的运行状态和返回地址;C、传值函数实参值给形参;反过来说,我们通常认为不成立。D、执行被调用函数的函数体内语句;E、将控制权和返回值转交给调用函数。函数将一分独立的代码封装

17、,完成特定的功能,增加了源文件可读性,实践模块化编程的思想,但是也产生了调用函数带来的额外的时间开销和空间开销。2、传值调用传值调用又分为值传值和地址传值,用于不同的传值需要。A、值传值这种传值方式是将实参的数据值通过复制获得其副本并将副本存放在被调用函数的栈区中,通过这种方式将值传给形参。B、地址传值传值调用方式是将地址值直接传递给形参,即形参为指针,使之指向实参地址,这里便不需要复制实参副本,所有对形参指针指向地址的值操作都将反馈到形参中。(数组形参类似,真正要修改的值(包括地址值)需在形参中设置高一级的指针来同步指向)3、引用调用引用调用是在函数形参列表中设置形参引用,该形参名即为对应实

18、参的别名。用这种传值方式,不仅省去实参副本赋值,还省去指针调用的麻烦,可直接进行地址调用。(实际上,引用调用与地址传值相类似,都是地址传递,只是更为简便些)用返回值方式,只能做到传递一个值,而引用调用和地址传值能返还多个值,地址调用通常用于数组、指针,而引用调用则可包含传值调用的作用范围,应用更为广泛。4、Main函数与自定义函数1、main函数在C/C+的程序中,必定有唯以一个主函数,它具有一些特性:A、主函数只能被系统调用B、主函数具有形参,只能通过系统为其传值主函数的形参类型已被固定,argc为命令行中参数个数,argv为指向字符指针数组的指针:Main(int argc,char *a

19、rgv)Main(int argc,char *argv)以上两种形式皆合法。另外,对于形参名是可以随用户自定义的,如:Main(int a,char *c)注:这样声明也可以:Main(int a,char *c1)C、一个源程序在编译时是从主函数开始编译的D、C/C+主函数的默认返回值为int型。2、自定义函数自定义函数一般由用户自己编辑定义,用以实现某种特定功能。其声明如上所述。不同于主函数,自定义函数在一般情况下可供其他任意函数嵌套调用(包括主函数),也可自身调用自身,即递归。自定义函数在被调用之前,必须在调用函数之前进行声明或直接定义。5、指针函数与函数指针1、指针函数指针函数本质为

20、函数,返回指针类型的值,其声明如下:(指针指向值类型)*函数名(形参列表);2、函数指针函数指针本质为指针,该指针指向一个函数,其声明如下:(指针指向函数返回值类型)(*指针名)(函数形参列表)也可以在函数定义之后声明一个指针指向它,如:Int f(int ,int );在主函数中作如下操作:Int *p=f;这样,指针p便指向函数f();调用时便有两种形式:Int a=p(1,2);或int a=f(1,2);然而事实上,采用前者并不能提升效率,反而比后者多增了一个取值操作(先取得函数名的常量地址)。然而既然存在则必有其原因。3、函数指针的功能有如下代码:#includeusing name

21、space std;int *f(int a,int b) int j=ab; return &j;int *f1(int c,int d) int j=!(cd); return &j;typedef int* (*pn)(int a,int b);pn ff(int s) switch(s) case 0:return f; case 1:return f1; int main() int a=10,b=1,c,*p; int *p1; cinc; p=ff(c)(a,b); cout*pb时返回1,f1()则返回0;当我们输入:1则输出:0此时调用了f1();当我们输入:0则输出:1此时

22、调用了f()。我们通过函数指针将f()和f1()相关联,依据不同指令调用不同函数,在多个功能相关的返回值类型、参数类型及个数相同的函数应用中,可以使用函数指针实现动态联编的效果。6、函数递归、函数重载和inline函数1、inline函数inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。用inline声明的函数也是真正的函数,与其他函数的区别在于编译时其代码直接内嵌到调用函数源文件代码中,从而在一定程度上减去了调用函数产生的额外时间和空间开销。主要适用于代码较少,使用频繁而不涉及复杂运算和循环结构的函数。所以一般inline函数代码少,不涉及复杂结

23、构(如,循环语句,switch语句)。主要是因为在编译时内联函数带有太多代码会产生累赘。内联函数产生过多代码会被编译器视为普通函数,忽略其inline关键字除此外,内联函数满足一般函数调用的一般性质,只要在被调用函数调用前检测到inline函数声明,则该函数就可被调用。例子(略)2、函数递归递归的实现类似于迭代,是迭代的模块化呈现。即将一个问题分解为相似的一个个具有嵌套层次的小问题,如:#includeusing namespace std;int Fibonacc(int i) if(i=0) return 0; else if(i=1) return 1; else return Fibo

24、nacc(i-1)+Fibonacc(i-2);int main() int n; while(cinn&n=0) coutFibonacc(n)=2),在n不算大时可以实现。这段程序中的自定义函数采用了递归思想,函数即为递归函数,是对求值中的fi=fi-1+fi-2的单一实现(2=i=n),即将求值问题分成相关似的小问题求解.注意到自定义函数中的条件语句,实现一个递归,至少需要三个条件:A、递归起点B、递归终结点C、递归语句只有满足这三个条件,递归才完整。以上递归函数中的前两个判断条件即为递归终结点;第三个条件语句为递归语句,是利用调用自身实现内嵌式递归调用的,调用的函数和被调用的其他自身函

25、数是分别独立的,构成类似塔形结构,由下至上延伸之终结点。3、递归函数的形式转换递归可以转换,用其他形式实现相同功能,以下用求两个整数的最大公约数的程序来说明。A、递归求解#includeusing namespace std;int gcd(int a,int b) if(a%b=0) return b; return gcd(b,a%b);int main() int m,n; cinmn; coutgcd(m,n)endl; return 0;B、栈解法#include#includeusing namespace std;int gcd(int a,int b) stack x; x.p

26、ush(a),x.push(b); while(1) int y=x.top(); x.pop(); int z=x.top(); x.pop(); if(z%y=0) return y; x.push(y); x.push(z%y); int main() int m,n; cinmn; coutgcd(m,n)endl; return 0;C、迭代#include#includeusing namespace std;int gcd(int a,int b) for(int temp;b;a=b,b=temp) temp=a%b; return a;int main() int m,n;

27、cinmn; coutgcd(m,n)endl; return 0;仅以上三种替换方式来说明递归思想,其原理相同,只是实现过程略有不同而已。4、函数重载函数重载是C+引入的用于实现多态性的编程机制,即对于同一问题可以有不同处理。重载函数的一个重要特征在于他们有相同函数名,而在其他方面有所不同,主要体现在函数原型上,与相应的函数体无关。函数重载必须满足条件:A、函数名相同B、形参列表有所不同(形参类型,形参个数,只要有一点不同即可),这是因为系统在确定调用函数时是通过类型匹配来判断的,如果没有完全精确地匹配对象,系统会通过系统内置的标准类型转换来匹配。注:返回值类型不同不能成为函数重载的条件。以

28、下用一个简单的求和例子说明:#include#includeusing namespace std;int sum(int a,int b) return a+b;double sum(double a,double b) return a+b;int main() double a=1.2,b=5.7; int m=3,n=8; coutsum(a,b)endl; coutsum(m,n)endl; return 0;输出结果:6.9 11使用sum()函数来实现两种类型的求和运算。4、函数压轧在不同的程序语言中,内部都制定了严格的命名机制,对于我们在源代码编辑时为自定义函数所起的函数名在系统中会加上附缀名,使其与其他文件里的函数名不相同。于是,当我们用C+编译C需加externc 修饰,以阻止函数压轧。7、 常见三种排序方式1、选择排序例:找出第i个数及之后数的最小项(以下标访问数组的形式)并交换,对第i+1个数做同样操作,最后完成递增排序(0=i=n,n为数组维数)至多交换次数(n-1),比较次数1/2*n*(n-1).函数代码如下:Void selectionSort(Datatypee an,int i)Int smallIndex;/下标访问Int i,j;For(i=0;in-1;i+)

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1