1、本章内容本章内容u在结构化程序设计中,函数是将任务进行在结构化程序设计中,函数是将任务进行模块划分的基本单位。一个函数实现一项模块划分的基本单位。一个函数实现一项功能。功能。u在面向对象程序设计中,函数是对数据的在面向对象程序设计中,函数是对数据的一项操作,也是实现一项功能。一项操作,也是实现一项功能。3.1 函数的定义与调用函数的定义与调用 3.5 作用域与标识符的可见性作用域与标识符的可见性 3.4 函数调用机制函数调用机制 3.3 全局变量和局部变量全局变量和局部变量 3.2 函数的参数传递函数的参数传递,返回值及函数声明返回值及函数声明 3.9 头文件与多文件结构头文件与多文件结构 3
2、.6 存储类型与标识符的生命期存储类型与标识符的生命期 3.8 函数的一些高级议题函数的一些高级议题 3.7 函数的递归调用函数的递归调用 本章内容本章内容3.1.1 函数概述函数概述3.1.2 函数定义函数定义3.1.3 函数调用函数调用函数是函数是C+C+程序的基本组成模块。程序的基本组成模块。通过函数,可以把一个复杂任务分解成为若干个易于解通过函数,可以把一个复杂任务分解成为若干个易于解决的小任务。充分体现逐步细化的设计思想。决的小任务。充分体现逐步细化的设计思想。组成组成C+C+程序的若干函数中,程序的若干函数中,有一个称为有一个称为main()main()(WinmainWinmai
3、n()())函数,是程序执行的入口)函数,是程序执行的入口,它可以调,它可以调用其他函数,但不可以被调用。而其他用其他函数,但不可以被调用。而其他一般函数既可一般函数既可以调用也可以被调用以调用也可以被调用。函数概念的引入:函数概念的引入:入口函数:入口函数:3.1.1 函数概述函数概述图图3.1 3.1 函数调用层次关系函数调用层次关系main()fun2()fun1()fun3()funa()funb()func()3.1.1 函数概述函数概述程序执行的入口程序执行的入口3.1.1 函数概述函数概述 u库函数和自定义函数:库函数和自定义函数:库函数库函数或或标准函数标准函数,是由编译系统预
4、定义的,是由编译系统预定义的,如一些常用的数学计算函数、字符串处理函数、如一些常用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。图形处理函数、标准输入输出函数等。库函数都按功能分类,集中说明在不同的头文库函数都按功能分类,集中说明在不同的头文件中件中。用户只需在自己的程序中包含某个头文。用户只需在自己的程序中包含某个头文件,就可直接使用该文件中定义的函数。件,就可直接使用该文件中定义的函数。用户根据需要将某个具有相对独立功能的程序用户根据需要将某个具有相对独立功能的程序定义为函数,称定义为函数,称自定义函数自定义函数。函数头函数头函数体函数体 语句序列语句序列 定义函数返回
5、值的定义函数返回值的数据类型。若无返数据类型。若无返回值,回值,void函数返回值由函数返回值由 return 语句给出语句给出3.1.2 函数的定义函数的定义函数运行时需要的数函数运行时需要的数据,由主调函数提供。据,由主调函数提供。若无,若无,void或者省略或者省略函数体可以为空,函数体可以为空,称为空函数。称为空函数。3.1.2 函数的定义函数的定义u 无参函数无参函数/打印一个表头打印一个表头void TableHead()cout*endl;cout*example *endl;cout*endl;函函数数头头int input()/输入满足要求的数据输入满足要求的数据int n;
6、cout输入一个大于输入一个大于5的整数:的整数:n;while(n=b?a:b);有参函数的参数表中列出所有有参函数的参数表中列出所有形式参数形式参数的类型和参数名的类型和参数名称。各参数即使类型相同也必须分别加以说明。称。各参数即使类型相同也必须分别加以说明。形式参数简称形参,只能是变量名形式参数简称形参,只能是变量名,不允许是常量或表,不允许是常量或表达式。达式。void delay(long n)int i;for(i=0;in;i+);/延时一段时间延时一段时间数据类型数据类型 函数名函数名(参数类型参数类型1 1形式参数形式参数1,1,参数类型参数类型2 2形式参数形式参数2,2,
7、)函数体函数体u有参函数有参函数3.1.2 函数的定义函数的定义问题:问题:定义函数时究竟哪些变量应当作为函数的参数?哪些定义函数时究竟哪些变量应当作为函数的参数?哪些应当定义在函数体内?应当定义在函数体内?原则原则:函数在使用时被看成函数在使用时被看成“黑匣子黑匣子”,除了输入输出外,除了输入输出外,其他部分可不必关心其他部分可不必关心。从函数的定义看出,函数头正是用来。从函数的定义看出,函数头正是用来反映函数的功能和使用接口,它所定义的是反映函数的功能和使用接口,它所定义的是“做什么做什么”。即。即明确了明确了“黑匣子黑匣子”的输入输出部分,的输入输出部分,输出就是函数的返回值,输出就是函
8、数的返回值,输入就是参数输入就是参数。因此,只有那些功能上起自变量作用的变量。因此,只有那些功能上起自变量作用的变量才必须作为参数定义在参数表中;函数体中具体描述才必须作为参数定义在参数表中;函数体中具体描述“如何如何做做”,因此除参数之外的为实现算法所需用的变量应当定义,因此除参数之外的为实现算法所需用的变量应当定义在函数体内。在函数体内。C+C+中不允许函数的嵌套定义,即不允许在一个函数中定中不允许函数的嵌套定义,即不允许在一个函数中定义另一个函数。义另一个函数。提示3.1.3 函数的调用函数的调用u函数调用函数调用所谓所谓函数调用,就是使程序转去执行函数体。函数调用,就是使程序转去执行函
9、数体。无参函数的调用格式:无参函数的调用格式:函数名函数名()有参函数的调用格式:有参函数的调用格式:函数名函数名(实际参数表实际参数表)实际参数简称实际参数简称实参实参,用来将实际参数的值传递,用来将实际参数的值传递给形参,因此给形参,因此可以是常量、具有值的变量或表可以是常量、具有值的变量或表达式达式。在在C+中,除了主函数外,其他任何函数都不中,除了主函数外,其他任何函数都不能单独作为程序运行。任何函数功能的实现都能单独作为程序运行。任何函数功能的实现都是通过被主函数是通过被主函数直接或间接直接或间接调用进行的。调用进行的。【例【例3.1】输入两个实数,输出其中较大的数。其中求两个实输入
10、两个实数,输出其中较大的数。其中求两个实数中的较大数用函数完成。数中的较大数用函数完成。程序如下程序如下:#include using namespace std;float max(float a,float b)return(a=b?a:b);int main()float x,y;cout输入两个实数:输入两个实数:xy;coutx和和y中较大数为中较大数为max(x,y)endl;return 0;main()函数函数调用调用max(2.5,4.7)函数函数max(2.5,4.7)return 4.7 主程序后主程序后续语句续语句3.2.1 函数的参数传递及传值调用函数的参数传递及传值
11、调用 3.2.2 函数返回值函数返回值 3.2.3 函数声明函数声明 3.2.1 函数的参数传递及传值调用函数的参数传递及传值调用 u传值调用和引用调用:传值调用和引用调用:按照参数形式的不同,按照参数形式的不同,C+有两种调用方式:有两种调用方式:传值调用传值调用和和引用调用引用调用。传值调用传递的是实参的值传值调用传递的是实参的值,本章介绍传值调,本章介绍传值调用用 3.2.1 函数的参数传递及传值调用函数的参数传递及传值调用 注意:注意:【例【例1.3】中调用函数】中调用函数strcpy(s3,s2),却实现了字符,却实现了字符数组数组s2的内容复制到字符数组的内容复制到字符数组s3中。
12、这是因为中。这是因为数组名实数组名实际上代表存储数组的内存的首地址际上代表存储数组的内存的首地址,复制给形参的是实参,复制给形参的是实参数组的首地址,结果参加运算的是实参数组。数组作为参数组的首地址,结果参加运算的是实参数组。数组作为参数,定义时形参用数组名加一对方括号,调用时实参只用数,定义时形参用数组名加一对方括号,调用时实参只用数组名数组名 u传值调用:传值调用:将实参的值复制给形参,在函数中参加运算的将实参的值复制给形参,在函数中参加运算的是形参,而实参不会发生任何改变。传值调用是形参,而实参不会发生任何改变。传值调用起了一种隔离作用。起了一种隔离作用。【例【例3.2】实参和形参对应关
13、系的示例。实参和形参对应关系的示例。调用调用power(4.6,3)函数函数power(4.6,3)return 97.336 主程序后续语主程序后续语句句n=3x=4.6c=afloat power(float x,int n)/求求x x的的n n次幂次幂float p=1;while(n-)p*=x;return p;int main()int n=3;float x=4.6;char c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x)endl;return 0
14、;【例【例3.2】实参和形参对应关系的示例。实参和形参对应关系的示例。调用调用power(a,3)函数函数power(97,3)return 912673 主程序后续语主程序后续语句句n=3x=4.6c=afloat power(float x,int n)/求求x x的的n n次幂次幂float p=1;while(n-)p*=x;return p;int main()int n=3;float x=4.6;char c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x
15、)endl;return 0;【例【例3.2】实参和形参对应关系的示例。实参和形参对应关系的示例。调用调用power(3,4.6)函数函数power(3,4)return 81主程序后续语主程序后续语句句n=3x=4.6c=afloat power(float x,int n)/求求x x的的n n次幂次幂float p=1;while(n-)p*=x;return p;int main()int n=3;float x=4.6;char c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)
16、=power(n,x)endl;return 0;3.2.2 函数返回值函数返回值u return语句的语句的作用:作用:返回函数值返回函数值 不再执行后续语句,程序控制返回调用点不再执行后续语句,程序控制返回调用点 一个函数体内可以有多个一个函数体内可以有多个return 语句语句 表达式返回值的类型表达式返回值的类型与与函数类型函数类型不相同时,自不相同时,自动强制转换成函数的类型动强制转换成函数的类型ureturn语句的格式:语句的格式:return 表达式;表达式;【例【例3.3】设计函数,根据三角形的三边长求面积。】设计函数,根据三角形的三边长求面积。如果不能构成三角形,给出提示信息
17、。如果不能构成三角形,给出提示信息。u分析:函数为计算三角形面积,一般三角形返分析:函数为计算三角形面积,一般三角形返回回面积值面积值,若不能构成三角形则返回,若不能构成三角形则返回-1。设计。设计一个主函数完成函数测试。根据返回值情况输一个主函数完成函数测试。根据返回值情况输出相应结果。出相应结果。【例【例3.33.3】程序:程序:float TriangleArea(float a,float b,float c)if(a+b=c)|(a+c=b)|(b+c=a)return-1;float s;s=(a+b+c)/2;return sqrt(s*(s-a)*(s-b)*(s-c);int
18、 main()float a,b,c,area;cout输入三角形三边输入三角形三边a,b,c:abc;area=TriangleArea(a,b,c);if(area=-1)cout(a,b,c )不能构成三角形!不能构成三角形!endl;else cout三角形三角形(a,b,c )面积为:面积为:areaendl;return 0;3.2.2 函数返回值函数返回值讨论:讨论:-函数可以有返回值,也可以没有返回值。对于没有返回值函数可以有返回值,也可以没有返回值。对于没有返回值的函数,功能只是完成一定操作,应将返回值类型定义为的函数,功能只是完成一定操作,应将返回值类型定义为void,函数
19、体内可以没有,函数体内可以没有return语句,当需要在程序指定语句,当需要在程序指定位置退出时,可以在该处放置一个:位置退出时,可以在该处放置一个:return;3.2.2结束结束 返回值是如何返回到调用处的?返回值是如何返回到调用处的?实际上,在函数返回时,系统会在实际上,在函数返回时,系统会在内存建立内存建立一个临时变量一个临时变量,函数返回时将函数值保存在,函数返回时将函数值保存在该临时变量中,然后由主调函数中包含调用该临时变量中,然后由主调函数中包含调用的表达式语句从该临时变量中取得值,表达的表达式语句从该临时变量中取得值,表达式语句执行后该临时变量撤销。式语句执行后该临时变量撤销。
20、3.2.2 函数返回值函数返回值3.2.3 函数声明函数声明语法上对程序文件中函数的排列次序要求满足语法上对程序文件中函数的排列次序要求满足先定义后先定义后使用使用。对于函数,只要在调用之前作函数声明(。对于函数,只要在调用之前作函数声明(Function Declaration),则函数定义放在任何位置程序都能正确编),则函数定义放在任何位置程序都能正确编译运行。译运行。函数声明的引入:函数声明的引入:函数声明的格式:函数声明的格式:函数声明是函数声明是一条一条以以分号结束分号结束的语句:的语句:例如【例例如【例3.33.3】中求三角形面积的】中求三角形面积的函数声明函数声明为:为:floa
21、t TriangleArea(float a,float b,float c);或或float TriangleArea(float,float,float);但下面的函数原型是错误的:但下面的函数原型是错误的:int TriangleArea(float,float,float);/错误,返回值类型不同错误,返回值类型不同float TriangleArea(int,int,int);/错误,参数类型不同错误,参数类型不同float TriangleArea(float,float);/错误,参数个数不同错误,参数个数不同float TriangleArea(float a,float b,
22、float c)if(a+b=c)|(a+c=b)|(b+cchoice;while(choice=4);switch(choice)case 1:account_report();break;case 2:engineering_report();break;case 3:marketing_report();break;return 0;void menu_print()cout”系统功能:系统功能:”endl;cout”1 财务报表财务报表”endl;cout”2 工程报表工程报表”endl;cout”3 市场报表市场报表”endl;cout”选择业务序号:选择业务序号:”;void a
23、ccount_report()/生成财务报表生成财务报表void engineering_report()/生成工程报表生成工程报表 void marketing_report()/生成市场报表;生成市场报表;3.2.3 函数声明函数声明【例【例3.4】输出所有满足下列条件的正整数输出所有满足下列条件的正整数m:10m1000且且m、m2、m3均为回文数。均为回文数。分析:分析:回文指左右对称的序列。如回文指左右对称的序列。如121、353等就是回等就是回文数。判断整数是否回文数用函数实现,其思想是将文数。判断整数是否回文数用函数实现,其思想是将该数各位拆开后反向组成新的整数,如果该整数与原该
24、数各位拆开后反向组成新的整数,如果该整数与原数相等则为回文数。数相等则为回文数。m m*m m*m*m11 121 1331101 10201 1030301111 12321 1367631 运行结果:运行结果:bool palindrome(int);/函数声明函数声明int main()int m;coutsetw(10)msetw(20)m*m“setw(20)m*m*mendl;for(m=11;m1000;m+)if(palindrome(m)&palindrome(m*m)&palindrome(m*m*m)coutsetw(10)msetw(20)m*msetw(20)m*m*
25、0);for(j=0;ji;j+)n=n*10+digitj;return(n=m);【例【例3.43.4】输出回文数输出回文数3.3.1 变量的存储机制与变量的存储机制与C+的内存布局的内存布局 3.3.2 全局变量全局变量 3.3.3 局部变量局部变量3.3.1 变量的存储机制与变量的存储机制与C+的内存布局的内存布局自由存储区自由存储区 (动态数据动态数据)操作系统为一个操作系统为一个C+C+程序的运行所分配的内程序的运行所分配的内存分为四个区域,如图存分为四个区域,如图3.33.3 所示:所示:栈区(函数局部数据)栈区(函数局部数据)全局数据区全局数据区(全局、静态全局、静态)代码区(
26、程序代码)代码区(程序代码)(main()函数局部数据)函数局部数据)自由存储区自由存储区 (动态数据动态数据)存储区域说明:存储区域说明:(1)代码区()代码区(Code area):存放程序代码,即程序):存放程序代码,即程序中各个函数的代码块;中各个函数的代码块;(2)全局数据区()全局数据区(Data area):存放全局数据和静):存放全局数据和静态数据;态数据;分配该区时内存全部清零分配该区时内存全部清零,结果变量的所有字,结果变量的所有字节节等效初始化为全等效初始化为全。(3)栈区()栈区(Stack area):存放局部变量,如函数中):存放局部变量,如函数中的变量等;的变量等
27、;分配栈区时不处理内存分配栈区时不处理内存,即变量取随机值。,即变量取随机值。(4)自由存储区(自由存储区(Free store area):):存放与指针相存放与指针相关的动态数据。关的动态数据。分配分配自由存储自由存储区时不处理内存区时不处理内存。参见第。参见第七章。七章。3.3.1 变量的存储机制与变量的存储机制与C+的内存布局的内存布局 3.3.2 全局变量全局变量 在所有函数之外定义的变量称为在所有函数之外定义的变量称为全局变量全局变量。全局变量存放在全局数据区,因编译器自动将全局变量存放在全局数据区,因编译器自动将该区清为全该区清为全0,如果用户在定义时不显式给出初始,如果用户在定
28、义时不显式给出初始化值,则化值,则等效初始化为全等效初始化为全0。全局变量可定义在程序开头,也可定义在中间全局变量可定义在程序开头,也可定义在中间位置,该全局变量位置,该全局变量在定义处之后在定义处之后的任何位置都是可的任何位置都是可以访问的,称为以访问的,称为可见的可见的。全局变量引入:全局变量引入:打印打印200调用调用func()函数函数func()200*2=400打印打印400n=100n=100*2=200【例【例3.53.5】多个函数使用全局变量的例子。多个函数使用全局变量的例子。int n=100;void func()n*=2;int main()n*=2;coutnendl
29、;func();coutnendl;return 0;3.3.3 局部变量局部变量 定义在函数内或块内的变量称为定义在函数内或块内的变量称为局部变量局部变量。程序中使用的绝大多数变量都是局部变量。程序中使用的绝大多数变量都是局部变量。局部变量在程序运行到它所在的块时建立在栈中,局部变量在程序运行到它所在的块时建立在栈中,该块执行完毕局部变量占有的空间即被释放。故亦称该块执行完毕局部变量占有的空间即被释放。故亦称为为自动变量自动变量。局部变量在定义时可加修饰词局部变量在定义时可加修饰词auto,但通常省略。但通常省略。局部变量在定义时若未初始化,其值为局部变量在定义时若未初始化,其值为随机数随机
30、数。局部变量引入:局部变量引入:打印打印main()中的中的t=3.5调用调用fun()函数函数fun()打印打印fun()中的中的t=5 打印打印main()中的中的t=3.5t=3.5t=5void fun()auto int t=5;/fun()中的局部变量,中的局部变量,auto可省略可省略coutfun()中的中的t=tendl;int main()float t=3.5;/main()函数中的局部变量函数中的局部变量coutmain()中的中的t=tendl;fun();coutmain()中的中的t=tendl;return 0;【例【例3.6】使用局部变量的例子。使用局部变量的
31、例子。局部变量占用的内存是在程序执行过局部变量占用的内存是在程序执行过程中程中“动态动态”地建立和释放的地建立和释放的局部变量占用的内存是在程序执行过程中局部变量占用的内存是在程序执行过程中“动态动态”地建立地建立和释放的。这种和释放的。这种“动态动态”是通过栈由系统是通过栈由系统自动自动管理进行的。管理进行的。函数调用机制函数调用机制 u函数调用过程:函数调用过程:(1)建立栈空间;)建立栈空间;(2)保护现场:主调函数运行状态和返回地址入栈;)保护现场:主调函数运行状态和返回地址入栈;(3)为被调函数中的局部变量分配空间,完成参数传递;)为被调函数中的局部变量分配空间,完成参数传递;(4)
32、执行被调函数函数体;)执行被调函数函数体;(5)释放被调函数中局部变量占用的栈空间;)释放被调函数中局部变量占用的栈空间;(6)恢复现场:取主调函数运行状态及返回地址,释放栈空间;)恢复现场:取主调函数运行状态及返回地址,释放栈空间;(7)继续主调函数后续语句。)继续主调函数后续语句。void fun1(int,int);void fun2(float);int main()int x=1;y=2;fun1(x,y);return o;void fun1(int a,int b)float x=3;fun2(x);void fun2(float y)int x;x栈顶栈底y3fun2()fun
33、1()运行状态及返回地址x3b2a1fun1()main()运行状态及返回地址y2x1main()操作系统运行状态及返回地址3.4 函数调用机制函数调用机制 此图例说明在程序执行过程中怎样通过栈此图例说明在程序执行过程中怎样通过栈“动态动态”地建立和地建立和释放局部变量占用的内存的释放局部变量占用的内存的3.5.1 块域块域3.5.2 函数声明域函数声明域3.5.3 文件域文件域 作用域:作用域:指标识符能够被使用的范围。只有在指标识符能够被使用的范围。只有在作用域内标识符才可以被访问(称为可见)作用域内标识符才可以被访问(称为可见)。本节重点讨论本节重点讨论局部域局部域和和文件域(全局域),
34、文件域(全局域),其其中局部域包括中局部域包括块域块域和和函数声明域函数声明域。任何。任何标识符标识符作用作用域的起始点均为域的起始点均为标识符说明标识符说明处。处。作用域与标识符的可见性作用域与标识符的可见性 函数中定义的标识符,包括形参和函数体中定义函数中定义的标识符,包括形参和函数体中定义的局部变量,作用域都在该函数内,也称作的局部变量,作用域都在该函数内,也称作函数域函数域。块块指一对大括号括起来的程序段。块中定义的标指一对大括号括起来的程序段。块中定义的标识符,作用域在块内。识符,作用域在块内。复合语句是一个块。复合语句是一个块。函数也是一个块。函数也是一个块。复合语句中定义的标识符
35、,复合语句中定义的标识符,作用域仅在该复合语句中。作用域仅在该复合语句中。块的引入:块的引入:3.5.1 块域块域a=3 b=535a=3 b=5a=5 b=3【例【例3.7】输入两数,按从大到小的顺序保存,并输出结果。输入两数,按从大到小的顺序保存,并输出结果。结果结果栈栈t =3 int main()int a,b;/具有函数域具有函数域 cout输入两整数:输入两整数:ab;cout“a=atb=b=a)int t;/具有块域具有块域 t=a;a=b;b=t;/交换交换a,b的值的值 couta=atb=bendl;return 0;上述程序若在最后一个上述程序若在最后一个cout语句处
36、增加:语句处增加:couttendl;则编译时会提示错误,因为变量则编译时会提示错误,因为变量t的作用域只在的作用域只在if语句中,其它地方语句中,其它地方不可见不可见。3.5.1 块域块域由由VC+运行,结果如下:运行,结果如下:输入两整数:输入两整数:3 5调用前:实参调用前:实参a=3,b=5调用中调用中交换前:形参交换前:形参a=3,b=5交换后:形参交换后:形参a=5,b=3调用后:实参调用后:实参a=3,b=5 交换失败交换失败操作系统运行状态及返回地址main()3a5bmain()运行状态及返回地址swap()3a5b3t35 3.5.1 块域块域u局部变量具有局部作局部变量具
37、有局部作用域,使得程序在不用域,使得程序在不同块中可以使用同名同块中可以使用同名变量。这些同名变量变量。这些同名变量各自在自己的作用域各自在自己的作用域中可见,在其它地方中可见,在其它地方不可见不可见【例【例3.8】设计函数完成两数设计函数完成两数交换,用主函数进行测试。交换,用主函数进行测试。100 200 3001100 500 600100200+300=500 500 200 300【例【例3.9】显示同名变量可见性。显示同名变量可见性。int n=100;#include int main()int i=200,j=300;cout ntitjendl;/内部块内部块 int i=5
38、00,j=600,n;n=i+j;cout ntitj endl;/输出局部变量输出局部变量n cout:nendl;/输出全局变量输出全局变量n n=i+j;/修改全局变量修改全局变量cout ntitj endl;return 0;对于块中对于块中嵌套嵌套其它块的情况,如果嵌套块中有同其它块的情况,如果嵌套块中有同名局部变量,服从局部优先原则,即在内层块中名局部变量,服从局部优先原则,即在内层块中屏屏蔽蔽外层块中的同名变量,换句话说,内层块中局部外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用变量的作用域为内层块;外层块中局部变量的作用域为外层除去包含
39、同名变量的内层块部分。域为外层除去包含同名变量的内层块部分。如果块内定义的局部变量与全局变量同名,块内仍如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符可以通过域运算符“:”:”访问同名的全局变量。访问同名的全局变量。3.5.1 块域块域 函数声明不是定义函数,在作函数声明时,其中的形参作用域只在声明中,即作用域结束于右括号。正是由于形参不能被程序的其他地方引用,所以通常只要声明形参个数和类型,形参名可省略。3.5.2 函数声明域函数声明域 3.5.3 文件域文件域文件域也称全局域。定义在所有函
40、数之外也称全局域。定义在所有函数之外的标识符作用域为从定义处到整个源文件的标识符作用域为从定义处到整个源文件结束,即文件域结束,即文件域 。文件中定义的全局变量。文件中定义的全局变量和函数的作用域为文件域。和函数的作用域为文件域。3.6.1 存储类型存储类型3.6.2 生命期生命期3.6 存储类型与标识符的生命存储类型与标识符的生命期期 u存储类型(存储类型(storage class)决定标识符的存储区)决定标识符的存储区域,即编译系统在不同区域为不同存储类型的标域,即编译系统在不同区域为不同存储类型的标识符分配空间。识符分配空间。u由于存储区域不同,标识符的生命期也不同。由于存储区域不同,
41、标识符的生命期也不同。u所谓所谓生命期生命期,指的是,指的是标识符从获得空间到空间释标识符从获得空间到空间释放之间的期间放之间的期间,标识符只有在生存期中、并且在,标识符只有在生存期中、并且在其自己的作用域中才能被访问。其自己的作用域中才能被访问。u用用auto和和register修饰的称为自动存修饰的称为自动存储类型储类型u用用static修饰的称为静态存储类型修饰的称为静态存储类型u用用extern修饰的称为外部存储类型。修饰的称为外部存储类型。3.6.1 存储类型存储类型 u存储类型的说明符(存储类型的说明符(storage class specifier)autoregistersta
42、ticexternu自动存储类型自动存储类型自动变量为用自动变量为用auto说明的变量,通常说明的变量,通常auto缺省。缺省。局部变量都是自动变量,生命期开始于块的执局部变量都是自动变量,生命期开始于块的执行,结束于块的结束,其原因是自动变量的空行,结束于块的结束,其原因是自动变量的空间分配在栈中,块开始执行时系统自动分配空间分配在栈中,块开始执行时系统自动分配空间,块执行结束时系统自动释放空间。故间,块执行结束时系统自动释放空间。故自动自动变量的生命期和作用域是一致的变量的生命期和作用域是一致的。3.6.1 存储类型存储类型 为提高程序运行效率,可以将某些变量保存在寄为提高程序运行效率,可
43、以将某些变量保存在寄存器中,即用存器中,即用register说明为寄存器变量,说明为寄存器变量,但不提但不提倡使用倡使用。3.6.1 存储类型存储类型 u静态存储类型静态存储类型static说明的变量称为静态变量。说明的变量称为静态变量。位置不同位置不同区别区别局部静态变量局部静态变量和和全局静态变量全局静态变量,也称内,也称内部静态变量和外部静态变量。部静态变量和外部静态变量。静态变量均存储在全局数据区静态变量均存储在全局数据区,如果程序未显式给出,如果程序未显式给出初始化值,则等效初始化为全初始化值,则等效初始化为全0。静态变量占有的空间要到整个程序执行结束才释放,静态变量占有的空间要到整
44、个程序执行结束才释放,故故静态变量具有整个程序执行期间的生命期静态变量具有整个程序执行期间的生命期。局部静态变量是定义在块中的静态变量,编译局部静态变量是定义在块中的静态变量,编译系统在全局数据区为其开辟空间并保存数据,系统在全局数据区为其开辟空间并保存数据,如果显式给出初始化值,则在该块第一次执行如果显式给出初始化值,则在该块第一次执行时完成,且只进行一次。时完成,且只进行一次。【例【例3.10】自动变量与局部静态变量的区别自动变量与局部静态变量的区别3.6.1 存储类型存储类型【例【例3.10】自动变量与局部静态变量的区别自动变量与局部静态变量的区别 int st()static int
45、t=100;/局部静态变量局部静态变量 t+;return t;int at()int t=100;/自动变量自动变量 t+;return t;int main()int i;for(i=0;i5;i+)coutat()t;coutendl;for(i=0;i5;i+)coutst()t;coutendl;return 0;i=0t=10012 34 51011011011011013.6.1 存储类型存储类型 i=0t=1001 21013 4 5102103104105【例【例3.10】自动变量与局部静态变量的区别自动变量与局部静态变量的区别 int st()static int t=10
46、0;/局部静态变量局部静态变量 t+;return t;int at()int t=100;/自动变量自动变量 t+;return t;int main()int i;for(i=0;i5;i+)coutat()=t;coutendl;for(i=0;i5;i+)coutst()t;coutendl;return 0;3.6.1 存储类型存储类型 3.6.2 生命期生命期u静态生命期静态生命期静态生命期静态生命期(Static extent或或Static storage duration)指的是标识符从程序开始运行时就存在,)指的是标识符从程序开始运行时就存在,具有存储空间,到程序运行结束时
47、消亡,释放存储空具有存储空间,到程序运行结束时消亡,释放存储空间。间。具有静态生命期的标识符存放在全局数据区,如全局具有静态生命期的标识符存放在全局数据区,如全局变量、静态全局变量、静态局部变量。变量、静态全局变量、静态局部变量。具有静态生命期的标识符在未被用户初始化的情况下,具有静态生命期的标识符在未被用户初始化的情况下,系统会系统会等效等效将其初始化为全将其初始化为全0。函数驻留在代码区,也具有静态生命期。函数驻留在代码区,也具有静态生命期。所有具有文所有具有文件作用域的标识符都具有静态生命期件作用域的标识符都具有静态生命期 3.6.2 生命期生命期 在函数内部或块中定义的标识符具有在函数
48、内部或块中定义的标识符具有局部生命期局部生命期(Automatic extent或或Automatic storage duration),其生命期开始于执行到该函数或块的标),其生命期开始于执行到该函数或块的标识符定义处,结束于该函数或块的结束处。具有局部识符定义处,结束于该函数或块的结束处。具有局部生命期的标识符存放在栈区。具有局部生命期的标识生命期的标识符存放在栈区。具有局部生命期的标识符如果未被初始化,其内容是随机的,不可引用。符如果未被初始化,其内容是随机的,不可引用。具有局部生命期的标识符必定具有局部作用域;但反具有局部生命期的标识符必定具有局部作用域;但反之不然,静态局部变量具有
49、局部作用域,但却具有静之不然,静态局部变量具有局部作用域,但却具有静态生命期。态生命期。3.6.2 生命期生命期u动态生命期动态生命期具有具有动态生命期动态生命期(dynamic extent或或dynamicstorage duration)的标识符存放在自由存储区,由特定的函数)的标识符存放在自由存储区,由特定的函数调用或运算来创建和释放调用或运算来创建和释放 如用如用new运算符(或调用运算符(或调用malloc()函数)为变量分配存储空间时,函数)为变量分配存储空间时,变量的生命期开始变量的生命期开始 而用而用delete运算符(或调用运算符(或调用free()函数)释放空间或程序结束
50、时,函数)释放空间或程序结束时,变量生命期结束。变量生命期结束。关于关于new运算符和运算符和delete运算符将在第七章中介绍运算符将在第七章中介绍 自己调用自己自己调用自己1n 1)!-(n*n1n 1 0n 1 n!可以看出是用阶乘定义阶乘,可以看出是用阶乘定义阶乘,这种自己定义自己的方法称这种自己定义自己的方法称为递归定义。为递归定义。递归的引入递归的引入u递归是一种描述问题的方法,或称算法。递归是一种描述问题的方法,或称算法。u递归的思想可以简单地描述为递归的思想可以简单地描述为“自己调用自己自己调用自己”。u例如用如下方法定义阶乘:例如用如下方法定义阶乘:递归定义的阶乘函数:递归定