函数与运算符的重(2).ppt_第1页
函数与运算符的重(2).ppt_第2页
函数与运算符的重(2).ppt_第3页
函数与运算符的重(2).ppt_第4页
函数与运算符的重(2).ppt_第5页
已阅读5页,还剩101页未读 继续免费阅读

下载本文档

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

文档简介

1,第5章 函数与运算符的重载,5.1 5.2 5.3 函数的嵌套与递归 5.4 函数与运算符的重载 5.5 函数与C+程序结构 5.6 程序实例,2,问题,1,为什么要用函数 2,使用函数的程序和顺序程序有什么区别?,3,5.3.1函数的嵌套,函数的嵌套 一个函数的函数体中包含一个或多个函数调用语句,即称为函数嵌套 。 嵌套的含义是,如果函数A 要调用函数B,也就是说,函数A 的定义要依赖于函数B 的定义。因此函数B 的定义或函数B 的原型必须出现在函数A 的定义语句之前。 另一方面,函数A 调用函数B,在调用A 的过程中,即执行A 的函数体过程中,调用B,也就是中途把程序控制转到B 的函数体,在执行结束后再返回到A 的函数体中。 函数嵌套调用所占用的空间(如赋值参数的创建等等)用堆栈(stack)的方式管理。一般这种堆栈所分配的空间是有限的,因此函数互相嵌套的层数也是有限的,依编译系统不同,其允许的嵌套层数也可能不同。,4,函数调用的堆栈情况,堆栈,Main(),cuberoot( x ),参数传递、返回值,保护现场、恢复现场,调用,返回,5,实例,# include void f1 (int,int); void f2 (int); void main () int a, b ; cout a ; cout b; f1 (a, a+b); cout endl a+b endl; ,void f1 (int x, int y) int m = 2; x *= m ; y+; f2 (x+y)/m); void f2 (int p) if (p = 100) cout endl p; ,6,函数调用过程中的栈结构,7,C+允许函数自己调用自己(如A函数可以调用A函数本身,称为直接递归)。也允许A函数调用B函数,而后B函数又调用A函数(从而形成间接递归)。但不论使用哪种递归,程序员都应保障递归函数在执行若干次后能够“退出”递归(不再进行递归调用,也即能够实现递归出口)。,5.3.2 函数的递归,8,函数递归的程序实例,1 使用递归求累乘积与累加和 2 反序输出从键盘输入的10个整数 3 反序输出一个整数,9,1 使用递归求累乘积与累加和,1. 求n的阶乘的递归函数prod - 使程序执行后的输出结果为: Input a positive integer:6 p=1*.*6=720 在数学中,可按如下方式对n!进行递归定义: 当n=1时, n!=1; 当n1时, n!=n*(n-1)! 这正是我们编写求n的阶乘的递归函数prod的基础。,10,#include long prod(int n) /注意用的是long,课本p131(3) if ( n=1 ) return 1; /n等于1时,递归出口(“退出”递归) else return n * prod(n-1); / n大于1时的返回值(n!)为n乘以n-1的阶乘 / ( 使用自递归调用“prod(n-1)”来求出(n-1)! ),11,void main( ) int n; coutn; /输入的正整数放入n中 long p=prod(n); /求出n的阶乘放入p中 cout“p=1*.*“n“=“pendl; /输出结果p ,12,上述求阶乘的递归函数中,当主函数通过prod(3)对递归函数prod进行调用时,它的返回值为3*prod(2),此时系统将再一次对prod本身进行调用而形成递归调用。 但注意后一次调用的实参为2,比上一次的实参3“下降”了1。而计算prod(2)时,它的返回值为2*prod(1),此时系统将再一次对prod本身进行递归调用,但此时的实参又“下降”了1。,13,正是通过这种实参的逐次“下降”,可保障递归函数在执行若干次后(此时的求阶乘问题当“下降”到1时),能够“退出”递归(不再进行递归调用,也即实现了递归出口)。 由于prod(1)的返回值为1,系统进一步算出2*prod(1)的值(即prod(2)的值)为2*1=2,再进一步算出3*prod(2)的值为3*2=6,这正是prod(3)的调用返回值(也即求出了3!=3*2*1=6)。,14,虽然上述执行过程是由系统自动完成的,但程序员要理解并熟知这种调用机制与系统实现方法,从而才能编写出逻辑正确且简明易懂的递归处理程序。 另外注意,递归处理程序的执行速度通常要比非递归处理方法慢。 问题:为什么会慢?,15,求出1到n之累加和的递归函数sum,使程序执行后的输出结果为: Input a positive integer:100 s=1+.+100=5050,16,#include int sum(int n) /递归函数sum if ( n=1 ) /n等于1时,递归出口 return 1; else return n + sum(n-1); /n大于1时的返回值(累加和)为n加上“从1累加到 /n-1的和”(要使用递归调用求出前n-1个数的累加和),17,void main( ) int n; coutn; int s=sum(n); /求出从1累加到n的和放s中 cout“s=1+.+“n“=“sendl; ,18,2 反序输出从键盘输入的10个整数,反序输出:从键盘输入10 个int 型数,而后按输入的相反顺序输出它们。 例如: 输入:1 2 3 4 5 6 7 8 9 10 输出:10 9 8 7 6 5 4 3 2 1,19,2 反序输出从键盘输入的10个整数,# include void inv (int n) int i; cini;/输入 if (n1) inv (n-1); /递归 else cout“- The result -”endl; /递归出口 cout i “ ”; /输出 ,void main(void) cout“Input 10 integers:“endl; inv(10); coutendl; ,20,2 反序输出从键盘输入的10个整数,可以有下面的执行结果: Input 10 integers: 1 2 3 4 5 6 7 8 9 10 - The result - 10 9 8 7 6 5 4 3 2 1,21,3 反序输出一个整数,在许多情形下递归函数易写易读,像解著名的Hanoi 塔问题的递归函数,其递归程序很短,也极易理解(见5.6 节),但是,如果不用递归方法,程序将十分复杂,很难编写。 要求 反序输出一个正整数的各位数值, 如输入231,应输出132。,22,3 反序输出一个整数,# include int conv(int n) if (n10) coutn; return; /递归出口 cout n%10; conve(n/10); /递归 ,void main(void) int t; cout t; cout endl; conv(t); ,23,3 反序输出一个整数,输出结果为: Input a positive number: 4781 1874 如果不用递归函数设计,程序不如递归形式清晰:,int conv(int n) if (n0) cout “Please input a positive number!”; else do cout n%10; n=/10; while (n!=0); ,24,总结,(1) 无论是直接递归还是间接递归都必须保证在有限次调用之后能够结束。例如函数fac 中的参数n 在递归调用中每次减1,总可达到2 的状态而结束。 (2) 函数调用时系统要付出时间和空间代价,在环境条件相同的情形下,总是非递归程序效率较高。,25,5.4 函数和运算符的重载,5.4.1 函数重载 5.4.2 可重载运算符 5.4.3 运算符重载函数的定义,26,5.4.1 函数的重载,函数重载实际上是函数名重载,即支持多个不同的函数采用同一名字。 例如: int abs(int n)return(n0?-n:n; float abs(float f)if(f0)f-f; return f; double abs(double d)if(d0)return-d;return d; 三个函数都是求绝对值,采用同一个函数名,更符合人们的习惯 . 例如在程序中经常出现这样的情况:对若干种不同的数据类型求和,虽然数据本身差别很大(例如整数求和,向量求和,矩阵求和),具体的求和操作差别也很大,但完成不同求和操作的函数却可以取相同的名字(例如sum,add 等)。打印函数print,显示函数display等也是同样。,27,5.4.1 函数的重载,函数名的重载并不是为了节省标识符(标识符的数量是足够的),而是为了方便程序员的使用,这一点很重要。实现函数的重载必须满足下列条件之一: (1)参数表中对应的参数类型不同; (2)参数表中参数个数不同; (3)参数表中不同类型参数的次序不同。,28,5.4.1 函数的重载,例如: void print(int); /整型 void print(point); /类point 的对象 int sum(int ,int); int sum(int ,int ,int); int get(int n,float a ); int get(int n,float a ,int n);,29,5.4.1 函数的重载,在定义同名函数时应注意: (1)返回类型不能区分函数, float add(int float); int add(int float);/错误 (2)采用引用参数不能区分函数, void print(double); void print(double); /错误 (3)有些派生基本类型的参数虽然可以区分同名函数,但在使用中必须注意 (4)包含可缺省参数时,可能造成二义性,,30,5.4.1 函数的重载,C+对函数重载的处理过程: (1)通过数组名与指针变量,函数名与函数指针,某类型变量与const 常量之间的转换,再查是否可实现匹配; (2)把实参类型从低到高(按字长由短到长)进行基本类型及其派生类型的转换,再检查是否可匹配; (3)查有无已定义的可变个数参数的函数,如有把它归为该函数。 在进行上述尝试性的处理之后可能出现仍无匹配或匹配不唯一的情况,这时可能输出出错信息或错误地运行。,31,5.4.2 可重载运算符,C+语言中的运算符实际上是函数的方便表示形式, 例如,算术运算符”+” 也可以表示为函数形式: int add (int a, int b) return a+b; 这时,a+b 和 add (a,b) 的含义是一样的。 C+语言规定,大多数运算符都可以重载, 单目运算符: -,!,+,-,new,delete 双目运算符: +,-,*, , , =,!=,= , , 等,32,5.4.2 可重载运算符,(1)可重载运算符几乎包含了C+的全部运算符集,例外的是:限定符.,:,条件运算符?:,取长度运算符sizeof 它们不可重载 (2)在可重载的运算符中有几种不同情况: 算术运算符,逻辑运算符,位运算符等与基本数据类型有关,通过运算苻重载函数的定义,使它们可以用于某些用户定义的数据类型,这是重载的主要目的。 赋值运算符,关系运算符,!等所涉及的数据类型按C+程序规定,并非只限于基本数值类型。因此,这些运算符可以自动地扩展到任何用户定义的数据类型,一般不需作重载定义就可“自动”地实现重载。 单目运算符+和-实际上各有两种用法,前缀增(减)量和后缀增(减)量。其运算符重载函数的定义当然是不同的,对两种不同的运算无法从重载函数的原型上予以区分:函数名(operator +)和参数表完全一样。为了区别前缀+和后缀+,C+语言规定,在后缀+的重载函数的原型参数表中增加一个int 型的无名参数。,33,5.4.3 运算符重载函数的定义,运算符的重载是一个特殊函数定义过程,这类函数总是以operator作为函数名。其实例在第七章以后引进 假设程序中定义了一个枚举类型的bool 类型: enum boolFALSE,TRUE; 用运算符(双目),*(双目),(单目)来表示或、与、非运算是十分方便的:,34,5.4.3 运算符重载函数的定义,bool operator + (bool a ,bool b) if(aFALSE)(bFALSE) return FALSE; return TRUE; bool operator*(bool a,bool b) if(aTRUE)(bTRUE) return TRUE; return FALSE; bool operator-(bool a) if(aFALSE) return TRUE; return FALSE; ,我们可以在程序中方便的表示其运算: b1b1+b2; b1-b3; b1(b1+b3)* FALSE;,35,5.4.3 运算符重载函数的定义,运算符重载函数的调用可有两种方式: 1 与原运算符相同的调用方式,如上例中的b1+b2,b1*b2,等等。 2 一般函数调用方式,如b1+b2,也可以写为operator+(b1,b2)被重载的运算符的调用方式,优先级和运算顺序都与原运算符一致,其运算分量的个数也不可改变。 3 运算符重载主要用于用类的形式定义的用户定义类型,例如,复数类型,集合类型,向量类型等等,通过运算符重载把人们习惯的运算符引入到计算操作之中,会收到很好的效果。这样的实例将在第七章介绍。,36,5.5 函数与C+程序结构,5.5.1库函数的使用 库函数又称标准函数,是C+语言编译系统为用户提供的内部函数,其编写与一般用户定义的函数相同,程序员可在程序中直接使用,但是要在程序开头说明库函数所在的头文件名,例如: #include #include C+系统中有一个很大的标准函数库(和标准类库),包括许多在各种程序中常用的基本任务处理函数,这些库函数被分成不同的组,例如,数学计算、字符处理、字符串处理、I/O操作、图形处理等等,,37,5.5.2 SP框架结构,按结构程序设计(SP)思想设计的程序结构称为SP框架。函数是SP框架的核心。 一个SP框架的完整C+程序由下面几部分组成: 1)一个主函数。它可调用其它函数,但不能被调用。 2)任意多个用户定义函数。都处于同一“等级”,可以互相调用。 3)全局说明。在所有函数定义之外的变量说明和函数原型。 4)预处理命令。在进行预处理后,这部分被取代。 5)注释。只起方便阅读的作用,编译后被删除。,38,5.5.2 SP框架结构,对于比较大的程序,可以把它们划分为几个程序文件,这些程序模块可能由一个或多个程序员编写,最简单而有效的划分方法是: 根据主函数和各用户定义的函数的功能及相互关系,把它们划分成若干个.CPP文件。 按与每个.CPP 程序文件中的函数有关的全局说明组成一个或多个.h(头)文件。 程序中使用的库函数组成的若干.CPP 文件和对应的.h 文件。在预处理命令的帮助下,一个C+程序被划分为若干.CPP和.h程序文件。在包含命令的帮助下,这些文件形成了一个有机的整体。 在这样的模块结构中,各个.CPP文件是全部函数的划分,它们组成了程序代码的主体。,39,5.5.3 函数间的数据传递,1. 通过赋值参数和返回语句(单向) 2通过全局变量(双向) 3.通过指针类型参数和引用参数(双向) 4.函数的数组类型参数(双向),40,1 通过赋值参数 (“单向传递”方式),传值方向只是:“上层” =“下层”。也即,可从主调函数A中通过赋值参数所对应的实参将值“传入”到被调函数B内(去使用),但不可将被调函数B内改变后的参数值“传出”到主调函数A中(去接着使用)。系统处理方式为: 被调函数中对形参值的改变不影响主调函数处的任一变量的值(形参分配有自己的局部于被调函数的存储空间,调用入口处将实参表达式的值赋给该局部变量)。,41,通过函数返回值(“单向传递”方式),通过函数内使用的return语句, 可将被调函数B内计算出的最终值“传出”到主调函数A的“调用点”处(去接着使用)。也即,传值方向只是:“下层” =“上层”。,42,全局变量的定义域可延续到整个程序执行结束,因此,只要在函数中没有把该全局变量名说明为其它变量,在所有的函数中都可以直接访问它,也就是说,函数间的数据传递还可以通过全局变量实现,这种传递可以是双向的 可从主调函数A中通过全局变量将值“传入”到被调函数B内(在A中赋值, 进入B内后使用该值),又可将被调函数B内改变后的全局变量值“传出”到主调函数A中(去接着使用)。也即,传值方向可为:“上层” =“下层”,“下层” =“上层”。,2 通过全局变量(“双向传递”方式),43,3 通过引用参数(“双向传递”方式, 有关引用的其它使用方法详见第6章),传值方向可为:“上层” =“下层”,“下层” =“上层”。即是说,它不仅可向被调函数的形参“传入”值(调用时的实参值),而且还可通过该形参“传出”值。系统处理方式为: 被调函数中对形参值的使用与改变,就是对主调函数中调用语句处所对应实参变量值的直接使用与改变(形参不具有自己的局部于被调函数的存储空间,它只是实参变量的一个“替身”)。,44,4 通过数组参数或指针参数(“双向传递” 方式,指针参数的具体使用方法见第6章),传值方向可为:“上层” =“下层”,“下层” =“上层”。 数组作形参,且在被调函数内使用或改变数组元素的值。系统处理方式为: 对形参数组元素的使用与改变,就是对实参数组元素的直接使用与改变。 指针作形参,且在被调函数内使用或改变指针所指变量的值。系统处理方式为: 被调函数中对形参指针所指变量值的使用与改变,就是对实参指针所指变量值的直接使用与改变。,45,数组可作为函数参数,从而把主调函数中的整个数组(实际上是数组的首地址)传给了被调函数。而后可在被调函数中使用或改变传来的那些数组元素的值。但注意,若在被调函数内改变数组元素值的话,返回主调函数后,相应实参数组元素的值也进行了相同的改变(“双向传值”功能)。,数组参数,46,例1. 读如下程序,看执行 后会显示出什么结果?,#include void ProcessingFunc(int b, int num); /函数原型 /int型数组形参b,通常省去对元素个数的指定 /(当然也可以进行指定!) void main() int A10=0,1,2,3,4,9,8,7,66,88; cout“- before calling, ai= -n“; for(int i=0; i10; i+) coutAi“ “; coutendl;,47,ProcessingFunc(A, 10); /函数调用 cout“- after calling, ai= -n“; for(i=0; i10; i+) coutAi“ “; coutendl; ,48,void ProcessingFunc(int b, int num) /数组形参b,省去了对元素个数的指定! cout“- in ProcessingFunc, bi= -n“; for(int i=0; inum; i+) coutbi“ “; coutendl; int tmp=b0; b0=bnum-1; bnum-1=tmp; b1+=100; ,49,程序执行后的显示结果如下:,- before calling, ai= - 0 1 2 3 4 9 8 7 66 88 - in ProcessingFunc, bi= - 0 1 2 3 4 9 8 7 66 88 - after calling, ai= - 88 101 2 3 4 9 8 7 66 0,50,例2. 求a数组中前n个整数 累加和的递归函数sum,其中使用了数组形参与递归函数。 程序执行后的输出结果为: Input 6 integers: 22 4 -2 9 100 3 s=136,51,#include int sum(int a, int n) if ( n=1 ) return a0; else return ( an-1 + sum(a, n-1) ); ,52,void main() const int n=6; int An; coutAi; int s = sum(A, n); cout“s=“sendl; ,53,程序中出现的所有名字(标识符)都必须说明,每个名字(变量名、常量名、参数名、函数名、类名、对象名等)都在程序的一定范围内有意义,就是该名字的作用域。 1. 外部存储属性与静态存储属性 2名字的生存期与作用域,5.5.4 变量与函数的作用域,54,一个C+程序由一个主函数和若干用户定义函数(或类)组成,当程序的规模较大时,整个程序可能被划分为几个程序文件,关键字extern(外部存储属性)和static(静态存储属性)可以规定所说明的变量名或函数名的作用域在一个程序文件范围内还是扩展到程序文件之外。,外部存储属性与静态存储属性,55,C+的存储类别,存储类别主要是针对变量而言的。变量不仅具有数据类型(存储空间大小), 而且还具有存储类别(所占存储空间的期限,即生命期)。 变量的存储类别可分为以下四种:自动(auto)型、寄存器(register)型、外部(extern)型、静态(static)型。,56,使用显式存储类别时,变量说明的一般格式变为: , . , ; 其中的“”可以是auto、register、extern、static四个关键字之一。 C+程序的数据主要存放在如下两个数据区之中,一个称为静态数据区(也称全局数据区 - “一旦分配,一直拥有”),另一个称为动态数据区(也称堆栈数据区 - “入时分配并拥有,出时归还两手空”)。,57,具有程序级作用域以及文件级作用域的那些变量被分配在静态数据区之中。另外,具有static存储类别的变量也均被分配在静态数据区之中。在程序执行时,就为这种变量分配空间并进行隐式初始化(将数值量初始化为0,将字符量初始化为空格),直到程序执行结束时才释放这些存储空间。 具有其它局部作用域的那些变量被分配在动态数据区之中。在程序执行时,每遇这种变量的作用域开始时就为其分配一次存储空间(但并不进行隐式初始化),而后, 在每遇这种变量的作用域结束时就立即释放它们所占据的存储空间。,58,变量的存储类别,(1) 自动(auto)变量与寄存器(register)变量 这两类变量都是局部变量。均被分配在动态数据区之中。它们的“生命期”仅在其作用域之内。即是说,这两类变量的作用域与“生命期”具有一致性。 由于C+编译器默认所有在函数或块内说明的局部变量均为自动(auto)变量, 所以实用程序中根本不需要使用关键字auto。 说明寄存器(register)变量时,必须使用存储类别register。如果没有足够多的通用寄存器,则编译器将它们按自动(auto)变量来处理。,59,(2) 外部(extern)变量 外部(extern)变量在所有的函数、类和名字空间之外说明的变量的作用域从被说明点开始,到所在的程序文件结束具有程序级作用域。它们被分配在静态数据区之中。即是说,它们是作用域最大且“生命期”最长的一种变量。在另一个程序文件中如果需要使用同一个变量的话,必须把这个变量说明为外部(extern)的,表示这个变量的说明不在本程序文件中,而在原文件之中。,60,(3) 静态(static)变量 又可分为局部静态变量和全局静态变量。局部静态变量是指在函数或块的内部说明的静态变量,它的作用域是局部的;对于局部变量:增加static说明,使其生存期扩展到整个程序。而全局静态变量指的是在所有函数的外部说明的具有单文件级全局性的静态变量。静态全局变量的生存期为整个程序,作用域为本程序文件,不可扩展。 两种静态变量都被分配在静态数据区之中,即是说,它们的“生命期”都与整个程序的执行期相同。 对于函数:一般(类外)函数的生存期和作用域为整个程序,静态属性的函数的作用域被限于所在的程序文件,这时,该函数不能在其它程序文件中使用。,61,函数的存储类别,函数也分为两种存储类别,一种是外部(extern)存储类别,另一种是静态(static)存储类别。 (1) 外部(extern)函数 具有外部(extern)存储类别的函数称为外部(extern)函数。这种函数具有程序级作用域。当函数定义时没给出存储类别时,系统默认它为外部(extern)存储类别,所以实用程序中几乎从不使用extern来说明外部函数。,62,例如,如下的两个函数说明是完全等价的。 int func() extern int func() (2) 静态(static)函数 具有静态(static)存储类别的函数称为静态(static)函数(有时也称为内部函数)。这种函数只具有文件级作用域,即是说, 这样的函数只能在本文件的内部被调用,在其它文件中均不可见。,63,存储类别相关实例,1. 存储类别实例1 - 外部(extern)变量 本实例程序由两个文件构成,下面分别叙述它们。 (1)程序文件1 本文件中对外部变量x及ch进行定义,并通过调用处于另一文件中的函数来“传递”与使用这些外部变量的具体值。,64,/file_1.cpp(源程序文件1,含有2个函数),#include int x=11; char ch=A; void func1(); /函数原型 void func2(); /函数原型,具体定义在另一个文件中 void main() func1(); func2(); cout“In file1_main: x, ch=“x“, “chendl; void func1() cout“In file1_func1: x, ch=“x“, “chendl; ,65,(2)程序文件2 本文件通过extern关键字来对外部变量x及ch进行说明(不再分配存储空间,与另一文件定义处的那一同名变量共享同一存储空间),并通过使用与修改这些外部变量的值来达到文件间相互通讯的目的。,66,/file_2.cpp(源程序文件2,含有1个函数),#include extern int x; extern char ch; void func2() cout“In file2_func2: x, ch=“x“, “chendl; x=22; ch=B; 程序执行后的显示结果如下: In file1_func1: x, ch=11, A In file2_func2: x, ch=11, A In file1_main: x, ch=22, B,67,存储类别实例2 - 局部静态(static)变量,局部静态(static)变量与局部自动(auto)变量的使用区别是: 它们的作用域都是局部的,作用域外都是不可见的;但局部静态(static)变量在其作用域外仍是存在的,而局部自动(auto)变量在其作用域外则不复存在。,68,#include void f1(); void main() int i; for (i=1; i=3; i+) f1(); void f1() int a=1; /局部自动变量a static int s=1; /局部静态变量s cout“In f1 - pos1: a(auto)=“a“ s(static)=“sendl; a+=2; s+=2; cout“In f1 - pos2: a(auto)=“a“ s(static)=“s“nn“; ,69,程序执行后的显示结果如下: In f1 - pos1: a(auto)=1 s(static)=1 In f1 - pos2: a(auto)=3 s(static)=3 In f1 - pos1: a(auto)=1 s(static)=3 In f1 - pos2: a(auto)=3 s(static)=5 In f1 - pos1: a(auto)=1 s(static)=5 In f1 - pos2: a(auto)=3 s(static)=7,70,名字的生存期和作用域,名字(变量名、函数名等等)的生存期与其对应的语法实体被分配的存储空间相关全局的、静态的、外部的语法实体被分配到全局数据区 ,生存期为整个程序 局部的(在函数内,程序块内说明的)语法实体被分配到局部数据区(栈区等) ,这种分配是临时的,一旦该函数体或程序块这些结束,所分配的空间被撤销,局部名字的生存期从被说明开始,到程序块结束。从这里也可看到,生存期与一个程序被划分为一个或多个程序文件是没有关系的。 名字的作用域与程序文件有关,作用域的概念是从这个名字能够被引用的,角度考虑的,一个名字在某处可以被引用,也称它在这里是可见的,它可被引用的范围,就是该名字的作用域。 简单说作用域是指标识符(如变量名、参数名、函数名等)在程序正文中的有效范围。,71,(1) 程序级作用域(也称多文件级作用域) 属于程序级作用域的有通过extern存储类别进行说明的外部变量以及外部函数等。 (2) 文件级作用域(也称单文件级作用域) 其有效范围为定义该标识符的那一个文件内。属于此种作用域的有静态函数以及在各函数之外的某一位置进行说明的那些变量等。 (3) 类级作用域 有效范围为所定义的那一个类的类体内。类中的私有成员的作用域仅在其类体内,公有成员以及保护成员的作用域有所不同。关于类级作用域将在后面的章节再进一步讨论。,72,(4) 函数级作用域(也称单函数级作用域) 有效范围为所处的那一个函数的函数体内。属于此种作用域的有函数的形参、在函数体内说明的变量、以及语句标号等。 (5) 块级作用域 块是程序正文中被一对花括号括起来的那一块区域(如, 函数内的某一个复合语句)。C+允许在块中说明局部于该块的变量。,73,(6) 函数原型级作用域,仅指出在函数原型声明时形式参数的作用范围。例如,假设有如下的函数原型声明: double myfunc(int ipara); 此时的形式参数ipara的有效范围(作用域)仅局限于说明该参数的那一对圆括号之间,在程序的其他任何地方都无法引用该标识符。,74,关于重名标识符的作用域,重名标识符指的是在程序中被重复定义的同名标识符。在相同的作用域内,标识符不能被重复定义。但在不同的作用域内,允许对标识符进行重复定义。 重名标识符的作用域遵循如下的规则: (1) 没有包含关系的两个不同作用域 在其中说明的标识符尽管名字相同,但二者毫不相干。,75,(2) 具有包含关系的两个不同作用域 将它们看成是互不相同的名字;进入子范围后, 将屏蔽其父范围的名字。即是说,进入子范围后, 原父范围处定义的那一同名标识符将是不可见的,但它仍然存在;当退出了子范围后,原父范围处定义的那一同名标识符将又成为可见的了。,76,与作用域有关的程序实例,1. 作用域实例1 本实例出现了三个不同的作用域:函数级作用域,被函数级作用域所包含的外层块级作用域,被外层块级作用域所包含的内嵌块级作用域。,77,#include void main() int i=10; /整型变量i, 具有函数级作用域 char ch=1; cout“in main - i,ch=“i“, “chendl; int i=20; /另一整型变量i, 外层块级作用域 char ch=2; cout“in local1 - i,ch=“i“, “chendl;,78,if(i0) double i=30.3; /双精度变量i,内嵌块级作用域 int ch=33; cout“in local2 - i,ch=“i“, “chendl; cout“in local1 - i,ch=“i“, “chendl; cout“in main - i,ch=“i“, “chendl; ,79,程序执行后的显示结果如下: in main - i,ch=10, 1 in local1 - i,ch=20, 2 in local2 - i,ch=30.3, 33 in local1 - i,ch=20, 2 in main - i,ch=10, 1,80,2. 作用域实例2 本实例主要用于说明文件级作用域(全局变量),函数级作用域(局部变量),以及函数原型级作用域的相互关系及其使用。其中还出现了两个具有“平行”关系的函数作用域。,81,#include int x=11; / x具有文件级作用域 char ch=1; void func1(int ipara1); /ipara1仅具有函数原型级作用域 void func2() int i=22222; /函数级i,与func1中i重名但不相干 double ch=202.2; /函数级ch,与文件级ch同名 cout“in func2 - x,ch=“x“, “chendl; cout“in func2 - i=“iendl; ,82,void main() cout“in main - x,ch=“x“, “chendl; func1(x); func2(); void func1(int ii) int i=21111; /函数级i,与func2中i重名但不相干 int x=201; cout“in func1 - ii=“iiendl; cout“in func1 - x,ch=“x“, “chendl; cout“in func1 - i=“iendl; ,83,程序执行后的显示结果如下: in main - x,ch=11, 1 in func1 - ii=11 in func1 - x,ch=201, 1 in func1 - i=21111 in func2 - x,ch=11, 202.2 in func2 - i=22222,84,5.6.1 “三色冰激凌”程序 -,由冰激凌商提出的问题:有28种颜色的原料,可以组合成多少种3色冰激凌。问题归结为计算排列数与组合数。 本示例计算排列数A(elements,selections)及组合数C(elements,selections)。 如:A(3,2)=6, C(3,2)=3; A(28,3)=19656, C(28,3)=3276。,85,由于排列数A(ele,sel)与组合数C(ele,sel)间有如下的关系: C(ele,sel)=A(ele,sel)/sel! 程序中编制了一个求阶乘的递归函数factorial,可用于求出sel的阶乘。 使程序执行后的显示结果如下: Number of selections:3 Out of how many elements:28 A(28,3)=19656 C(28,3)=3276,86,#include long factorial(int number); /函数原型 void main( ) int i,selections,elements; /计算A(elements,selections) /以及C(elements,selections) coutselections; /输入整数selections coutelements; /输入整数elements double answer = elements; int ele = elements;,87,/求排列数A:共进行sel-1次乘法 /A(ele,sel)=ele*(ele-1)*.*(ele-sel+1) for(i=1; iselections; i+) /循环sel-1次 answer*=-ele; /最终的answer值即为所要求的A(ele,sel) cout“A(“elements“,“selections“)=“; coutanswerendl; /输出排列数A(ele,sel)之结果,88,/组合数C的求法: / C(ele,sel)=A(ele,sel)/sel! answer/=factorial(selecti

温馨提示

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

评论

0/150

提交评论