讲函数及变量的作用域.ppt_第1页
讲函数及变量的作用域.ppt_第2页
讲函数及变量的作用域.ppt_第3页
讲函数及变量的作用域.ppt_第4页
讲函数及变量的作用域.ppt_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第四讲 函数及变量的作用域,函数的定义和说明 函数的调用 函数的参数 内联函数 函数重载 函数的嵌套调用和递归调用 作用域 C+的系统函数,主要内容,4.1 函数的定义和说明,函数的定义格式 函数的说明方法,一个C+函数由函数头与函数体两部分组成。 形式如下: 函数类型 函数名(形式参数类型 形式参数名,) 函数体 如: int fun1(char c,float f) int m=1; m+=c+f; return m; ,函数的定义格式,即函数返回值的类型。无返回值为void类型,语句序列。描述了实现功能的过程,并一般要最后执行一条return语句。,可包含多个形式参数。定义了函数将从调用函数中接收多少个数据及其类型,函数的说明方法,其形式为:函数类型 函数名(参数类型表); 如: int absolute_value ( int ,double); 要求在调用函数前让编译器知道其函数原型,以便编译器利用函数原型提供的信息来检查调用的合法性,强制参数为适当类型,保证参数的正确传递。 而编译器获得函数原型有两种情况: ()当函数定义在调用之前时,则从定义中抽取函数原型。 ()当函数定义在调用之后时,则程序员须在调用之前用函数原型对函数进行声明,让编译器获得函数原型。,即所有参数的数据类型,对于标准库函数的函数原型都在头文件中提供了,程序可用#include命令包含进来即可; 注意:函数原型和函数定义在返回类型、函数名和参数表上必须完全一致,否则编译错误。,注 意,#include void f1 ( ) ; /函数说明 void f2 ( ) ; void main( ) f1( ); /函数调用 f2( ); void f1( ) /函数定义 cout “Function f1.n“;f2( ); void f2( ) cout “Function f2 .n“;,main(),f1(),f2(),4.2 函数的调用,函数的值和类型 函数的传值调用 函数的引用调用,函数的值和类型,函数调用通过调用表达式进行。 表达式形式为:函数名(实参表列) 函数调用过程是: 为形参及函数体中的变量分配存储空间; 用实参向形参传递数据; 中断现行(调用)函数,将控制转交给被调用函数执行。 函数调用后的返回过程是: 先计算出返回语句中的值 若表达式值的类型与函数类型不一致,则强制转换。 将计算出的表达式值返回给调用函数作为返回值。 将控制由被调用函数转向调用函数,执行后面的语句。,如: #include int lmax(int,int); /函数声明 void main( ) int limit=32; char ch=A; long mval; mval=lmax(limit,ch); /调用表达式 coutb?a:b); ,实参的个数、类型及顺序要与形参保持一致,形式参数,实际参数,说明: (1)实参在进行函数调用时,必须具有确定的值,以便把这些值传送给形参。 (2)形参变量只有在被调用时,才分配内存单元;调用结束时,即刻释放所分配的内存单元。 因此,形参只有在该函数内有效。调用结束,返回调用函数后,则不能再使用该形参变量。 (3)实参对形参的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 (4)实参和形参占用不同的内存单元,即使同名也互不影响。,函数的传值调用,C+中变量值有两种:变量本身值和变量地址值。而形参的类型也就分为两种:一般类型和指针类型。 则传值调用的方式也有两种:传值调用和传址调用 有时也称:直接调用与间接调用,1、传值调用的实现机制和特点,实现机制是:调用函数中的数据只是在调用之初通过实参向形参传递,之后各占有不同的空间,并且不再发生联系,互不干扰。 特点是:形参值的改变不影响实参。 实参本身的值在调用前后和调用过程中都不发生变化。,函数swap本想实现交换a和b值的功能,但调用结果却不如意。原因就在于这种调用是一种值调用。 例:#include void swap(int,int); void main( ) int a=3,b=5; cout“before swap :a=“a“,b=“bendl; swap(a,b); cout“after swap :a=“a“,b=“bendl; void swap(int a,int b) int temp; temp=a; a=b; b=temp; cout“in swap :a=“a“,b=“bendl; ,调用swap时,内存单元分配情况:,3,5,3,temp=a; a=b; b=temp;,实现机制是:实参为某变量地址值,形参为指针类型。在调用时,将地址值赋给形参,使形参指针指向该变量。则以后可直接通过形参指针来访问该变量。 特点是:可通过改变形参所指向的变量值来影响实参所对应的变量值。,2、传址调用的实现机制和特点,函数swap实现了交换a和b值的功能。原因就在于这种调用是一种传址调用。 例:#include void swap(int *,int *); void main( ) int a=3,b=5; cout“before swap :a=“a“,b=“bendl; swap( ,调用swap时,内存单元分配情况:,3,5,3,temp=*a; *a=*b; *b=temp;,函数的引用调用,使用引用作为函数形参时,调用函数的实参要用变量名。将实参变量名赋给形参的引用,则在被调函数中,对引用的操作,实质就是直接通过引用来操作实参的变量值。 显然,引用传递方式具有传址调用的效果,又有传值调用的语法简单性和可读性。 注意: 若只从调用语句观察,是无法区别采用的是传值调用还是引用调用。,函数swap实现了交换a和b值的功能。这里调用是采用了引用调用。 例:#include void swap(int ,调用swap时,内存单元分配情况:,3,5,3,temp=a; a=b; b=temp;,4.3 函数的参数,函数参数的求值顺序 设置函数参数的默认值 使用数组作函数参数,函数参数的求值顺序,由于使用不同求值顺序的编译器而造成二义性。 #include int add(int x,int y); /函数声明 void main( ) int a=4,b=6; int z=add(+a,a+b); /从左至右为5 11 coutzendl; /从右至左为5 10 int add(int x,int y) /函数定义(实现) return x+y; ,设置函数参数的默认值,在函数说明语句中预先初始化一些参数的值,从而使调用语句中相应的参数可以缺省。 如: int max(int, int, int=-32768); 则在函数调用时可以不给最后一个参数传递值,而采用缺省值。如: max(i1,i2); / 参数缺省调用 max(i1,i2,i3); /正常调用 允许函数默认参数值,是为了让编程简单。 设置函数参数的默认值是按从右至左的方式 当又有声明又有定义时,定义中不允许默认参数。若只有定义,则默认参数才可出现在函数定义中。,注意:若一个函数中有多个默认参数,则参数缺省按从后向前的顺序进行, 如: void fun(int a=3,int,char c=a); /error 当调用函数时,也只能从右向左匹配参数。 如: 声明函数为: void func(int a,int b=2,int c=3,int d=5); 则调用方法: func(2,15, ,3); / error,使用数组作函数参数,1、形参和实参都用数组 这种调用的机制是:形参和实参共用内存中的同一个数组空间。因此,在被调用函数中改变了数组中某个元素的值,对调用函数该数组的该元素值也被改变,因为是共用同一个数组。 #include int a8=1,3,5,7,9,11,13; void fun(int b ,int n) for (int I=0;In-1;I+) b7+=bI; void main( ) int m=8; fun(a,m); couta7endl; ,#include void invert(char b,int n); void main() char s60; int n; couts; cout“字符串原始顺序:“sendl; n=strlen(s); invert(s,n); cout“字符串反转后的顺序:“sendl; void invert(char b,int n) for(int i=0;in/2;i+) char c=bi; bi=bn-1-i; bn-1-i=c; ,输入一字符串个数,反序存放并输出,不能写成invert(sn,n),有问题吗?,2、形参和实参都用对应数组的指针 实际,也可一个使用指针,另一个使用数组。在使用指针时,可以直接用数组名,也可用另外定义的指向数组的指针。 #include int a8=1,3,5,7,9,11,13; void fun(int *pa,int n) /函数声明与定义 for (int I=0;In-1;I+) *(pa+7)+=*(pa+I); void main( ) int m=8; fun(a,m); couta7endl; ,#include void invert(char *b,int n); void main() char s60; int n; /char *p=s; couts; cout“字符串原始顺序:“sendl; n=strlen(s); invert(s,n); / invert(p,n); cout“字符串反转后的顺序:“sendl; void invert(char *b,int n) for(int i=0;in/2;i+) char c=*(b+i); *(b+i)=*(b+n-1-i); *(b+n-1-i)=c; ,输入一字符串个数,反序存放并输出,3、形参用引用实参用数组名 其中要先用类型定义语句定义一个int型的数组类型。 typedef int aref8; /类型定义 int a8=1,3,5,7,9,11,13; void fun(aref ,#include typedef char arr60; /先用类型定义一个char型的数组类型 void invert(arr ,输入一字符串个数,反序存放并输出,4.4 内联函数,引入的原因 定义方法 注意事项,引入原因,目的是为了解决程序中函数调用的效率问题。 函数调用时需要建立栈内存环境,进行参数传递,并产生程序执行转移,则都要有时间和空间的代价。 而有时一些函数代码很短(行),却有高使用频率,造成处理现场的开销巨增。 这时若将函数体嵌入函数调用处,则可避免每次调用函数的开销。大大提高效率。,通过将该函数声明为inline来实现。 即在函数声明和定义中用inline来修饰。如: inline int isnumber(char); void main( ) char ch; for (int I=1; Ich; int p=isnumber(ch); cout=0 编译器在看到inline后,为该函数创建一段代码,以便以后每次碰到该函数的调用都用相应代码来替代。,定义方法,注意事项,内联函数可以在一开始仅声明一次,但必须在被调用之前声明或定义为inline。因为内联函数的代码必须在被替换前生成被替换的代码。否则编译器同一般函数对待,产生函数调用。 内联函数还有限制: 函数内不能含有循环结构或switch结构; 不能含有任何静态数据及数组声明。 不能是递归函数。,注 意,4.5 函数重载,引入原因 重载方式 注意事项,引入原因,重载指用同一个函数名对应着多个函数实现的情况。 如: 求两个数的较大值,若数的类型不同,则返回值类型不同。 int max(int,int); float c=max(3.14,3.5); /error 因此一个定义了实现整型数比较功能的函数无法用于实现实型数的比较,尽管其功能一致。 这时就可采用重载。,在C+中,允许两个或两个以上的函数采用相同的名字,只要使用不同类型、不同数目的参数或不同的返回值,编译器便知道在什么情况下该调用哪个函数,这就叫函数重载。 如:int max (int x, int y) return ( (xy) ? x: y); double max (double x , double y) return ( (xy) ?x: y); #include void main( ) coutmax(10,20)endl; coutmax(1.23,2.34)endl; ,重载方式,靠将实参的个数及类型和所被调用的f()函数的形参的个数及类型一一比较来判断。,()作为重载函数至少在参数个数、参数类型上有所不同。若仅在返回类型上不同,编译器是无法区别的。 如:void func(int); int func(int); /错误 int min(int ,int); int min(int,int,int); /正确 int add(int,int); double add(double,double);/正确 ()typedef定义的类型只是给已有类型取另外一个名字,编译器不能将其同原类型区分。如: typedef INT int; void func(int x) . void func (INT x) (3)重载函数一般应具有相同的功能,否则会破坏程序的可读性。,注意事项,4.6 函数的嵌套调用和递归调用,嵌套调用 递归调用,嵌套调用,函数的嵌套调用是指在执行被调用函数时,被调用函数又调用了其它函数。其关系可表示如图所示:,例4.15 计算s=1k+2k+3k+N k #include const int k=4; const int n=6; /符号常量定义 int power(int,int); /求幂的函数说明 int sum(int,int); /求和的函数说明 void main( ) cout“Sum of 1k+2k+3k+N k = “; coutsum(k,n)endl; /调用sum函数 ,int sum(int k,int n) /*计算1到n的k次方之累加和*/ int s=0; for(int i=1;i=n;i+) s+= power(i, k); /累加 return s; /调用f1函数 int power(int m,int n) /*计算m的n次方*/ int p=1, i; for(i=1;in;i+) p*= m; /累乘算法 return p; ,递归调用,函数的递归调用是指,一个函数在它的函数体内,直接或间接地调用它自身。 如:求n!的程序 long fac(int n) if (n= =1) return 1; else return fac(n-1)*n; /出现函数自调用 注意两个递归条件: (1) 初始条件:n=1时 ,n!=1 (2) 递归公式: n1时 ,n!= n * ( n-1 ) !,三、调用函数本身,但参数值趋于 结束条件,一、有一个使递归结束的条件,二、当条件成立时,不再调用自己,而有确定值,fac:=6,fac:=2,fac:=1,4.7 作用域,标识符的作用域规则 作用域的种类 局部变量和全局变量,标识符的作用域规则,规则为: 标识符只能在说明它或定义它的范围内是可见的,而在该范围之外是不可见的。 说明有: 1、大多数标识符的说明与定义是一致的,只有少数例外。如:函数等。 2、范围有大有小。最大为整个程序,最小为块,中间有文件和函数。,作用域的种类,不同的标识符定义在不同的范围内有不同的作用域。按作用域的大小可分为: 程序级:包含着组成该程序的所有文件。 文件级:仅在定义它的文件内。 函数级:在它所定义的函数体内。 块级: 定义在分程序中、语句块内。 注意:作用范围一般是从定义时开始,到相应范围结束为止。,如:#include void main() int x(3),y(5); /double x(3.5); error for (;x0;x-) int x(4); coutx“t“y“t“; coutendlx“t“yendl; ,在不同作用域中允许同名。,在同一作用域中不允许同名。,蓝色为函数作用域,绿色为块作用域,不为x的原因是局部优先,4 5 4 5 4 5 0 5,局部变量和全局变量,1、局部变量 是指作用域在函数级和块级的变量。 2、全局变量 是指作用域在程序级和文件级的变量。,#include int i(5); /外部变量extern static int j(6); /外部静态变量 void min(int x,int y ) /形参 int i(3); /自动变量 auto register int j(2); /寄存器变量 static int k(1); /内部静态变量 void main( ) ,局部变量,全局变量,蓝色为文件作用域,绿色为函数作用域,注意:同名时是局部优先,分析程序结果,int a(5),b(7),c(10); cout“atbtc“endl; couta“t“b“t“cendl; int b(8);double c(8.8); couta“t“b“t“cendl; a=b; int c;c=b; couta“t“b“t“cendl; couta“t“b“t“cendl; couta“t“b“t“cendl;,5 7 10,5 8 8.8,8 8 8,8 7 10,8 8 8.8,4.8 C+的系统函数,概述,C+系统将所提供的系统函数的说明分类放在不同的.h文件(头文件)中。如: 数学处理函数(math.h)、字符串处理函数(string.h)、屏幕处理函数(conio.h)、图形处理函数(graph.h)。 conconsole,控制台、安慰 具体使用需要看所用的C+版本的帮助。注意: 1、了解系统中有哪些系统函数。 2、了解函数位于哪个头文件中。 3、调用一个函数,须了解函数功能、参数和返回值。,如: #include #include void main( ) double I(30*3.1415/180); double x=sin(I); double y=cos(I); coutxyendl; ,输入15个数,用函数求和与平均值,void main() double a15,s(0),ave(0); for(int i=0;iai; for(i=0;i15;i+) s+=ai; ave=s/15; cout“和:“sendl; cout“平均值:“aveendl; ,sum(15),ave(15),double sum(int x); /函数声明 double ave(int x); /函数声明 double a15,s(0); /全局变量 void main() for(int i=0;iai; cout“和:“sum(15)endl; /函数调用 cout“平均值:“ave(15)endl; ,double sum(int x)/求和函数的定义 for(i=0;ix;i+) s+=ai; return s; double ave(int x) /求平均值函数的定义 return s/x; ,数组排序冒泡法,第一轮 B D C A E,原数据 B E D C A,第三轮 B A C D E,第二轮 B C A D E,第四轮 A B C D E,4 3 2 1,结论:轮数(外循环)为:n-1,每轮次数(内循环)为:n-i,void mai

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论