版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 在结构化程序设计中,函数是将任务进行模块划分的基在结构化程序设计中,函数是将任务进行模块划分的基本单位。本单位。 在面向对象的程序设计中,对数据的操作总是封装在在面向对象的程序设计中,对数据的操作总是封装在函数中,一个函数描述一种操作。不要完全按模块思想讲函数中,一个函数描述一种操作。不要完全按模块思想讲。也是让学生处于面向对象程序设计的氛围中。这样第。也是让学生处于面向对象程序设计的氛围中。这样第5 5章章的教学会顺利一些。的教学会顺利一些。 要掌握函数的使用,必须理解函数调用时的内部实现机要掌握函数的使用,必须理解函数调用时的内部实现机制,以及与此相关的内存分配机制、变量生命期和作用域。
2、制,以及与此相关的内存分配机制、变量生命期和作用域。 本章还将介绍关于函数重载的概念,介绍递归算法本章还将介绍关于函数重载的概念,介绍递归算法、内联函数、默认参数函数以及多文件组织、编译预处、内联函数、默认参数函数以及多文件组织、编译预处理、工程文件的概念和运行库函数。理、工程文件的概念和运行库函数。41 函数的定义与调用函数的定义与调用 4 5 作用域与存储类型作用域与存储类型 44 函数调用机制函数调用机制 43 全局变量和局部变量全局变量和局部变量 42 函数的参数传递函数的参数传递,返回值及函数原型说明返回值及函数原型说明 410 编译预处理编译预处理 4 9 头文件与多文件结构头文件
3、与多文件结构 4 8 C+的系统库函数的系统库函数 4 7 函数的一些高级议题函数的一些高级议题 4 6 函数的递归调用函数的递归调用 4.1.1 函数概述函数概述4.1.2 函数的定义函数的定义4.1.3 函数的调用函数的调用 函数是函数是C+C+程序的基本组成模块。程序的基本组成模块。通过函数,可以把一个复杂任务分解成为若干通过函数,可以把一个复杂任务分解成为若干个易于解决的小任务。充分体现结构化程序设计由个易于解决的小任务。充分体现结构化程序设计由粗到精,逐步细化的设计思想。粗到精,逐步细化的设计思想。组成组成C+C+程序的若干函数中,有一个称为程序的若干函数中,有一个称为main()m
4、ain()(Winmain()Winmain())函数,是程序执行的入口,它可以调)函数,是程序执行的入口,它可以调用其他函数。而其他一般函数既可以调用也可以被调用其他函数。而其他一般函数既可以调用也可以被调用。函数之间的调用关系见下图:用。函数之间的调用关系见下图:main ( )fun2( )fun1( )fun3( )fun1_1( )fun2_1( )fun2_2( )图图4.1 4.1 函数调用层次关系函数调用层次关系函数函数按是否带有参数,分为按是否带有参数,分为: : 无参函数无参函数和和有参函数有参函数4.1.1结束结束 函数按其是否系统预定义分为两类:函数按其是否系统预定义分
5、为两类:一类是编译系统预定义的,称为一类是编译系统预定义的,称为库函数库函数或或标准函数标准函数,如一些常用的数学计算函数、字符串处理函数、图如一些常用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。这些形处理函数、标准输入输出函数等。这些库函数都库函数都按功能分类,集中说明在不同的头文件中按功能分类,集中说明在不同的头文件中。用户只。用户只需在自己的程序中包含某个头文件,就可直接使用需在自己的程序中包含某个头文件,就可直接使用该文件中定义的函数。该文件中定义的函数。另一类是用户另一类是用户自定义函数自定义函数,用户可以根据需要将某,用户可以根据需要将某个具有相对独立功能的程
6、序定义为函数。个具有相对独立功能的程序定义为函数。1. 无参函数无参函数2. 有参函数有参函数 定义格式为:定义格式为:数据类型数据类型函数名函数名( (voidvoid)函数体函数体 例例: 下面函数的功能是打印一个表头下面函数的功能是打印一个表头void TableHead ( ) cout*endl;cout* example *endl;cout*=b?a:b); 定义函数时可能会涉及若干个变量,究竟哪些变量应当定义函数时可能会涉及若干个变量,究竟哪些变量应当作为函数的参数?哪些应当定义在函数体内?这有一个原则:作为函数的参数?哪些应当定义在函数体内?这有一个原则:作为一个相对独立的模
7、块,作为一个相对独立的模块,函数在使用时完全可以被看成函数在使用时完全可以被看成 “黑匣子黑匣子”,除了输入输出外,其他部分可不必关心,除了输入输出外,其他部分可不必关心。从函。从函数的定义看出,函数头正是用来反映函数的功能和使用接口,数的定义看出,函数头正是用来反映函数的功能和使用接口,它所定义的是它所定义的是“做什么做什么”,在这部分必须明确,在这部分必须明确“黑匣子黑匣子”的的输入输出部分,输入输出部分,输出就是函数的返回值,输入就是参数输出就是函数的返回值,输入就是参数。因。因此,只有那些功能上起自变量作用的变量才必须作为参数定此,只有那些功能上起自变量作用的变量才必须作为参数定义在参
8、数表中;函数体中具体描述义在参数表中;函数体中具体描述“如何做如何做”,因此除参数,因此除参数之外的为实现算法所需用的变量应当定义在函数体内。之外的为实现算法所需用的变量应当定义在函数体内。 C+C+中不允许函数的嵌套定义,即在一个函数中定义另一中不允许函数的嵌套定义,即在一个函数中定义另一个函数。个函数。在在C+C+中,除了主函数外,其他任何函数都不能单独作为程中,除了主函数外,其他任何函数都不能单独作为程序运行。任何函数功能的实现都是通过被主函数直接或间接序运行。任何函数功能的实现都是通过被主函数直接或间接调用进行的。所谓函数调用,就是使程序转去执行函数体。调用进行的。所谓函数调用,就是使
9、程序转去执行函数体。 无参函数的调用格式为:无参函数的调用格式为: 函数名函数名( )( ) 有参函数的调用格式为:有参函数的调用格式为: 函数名函数名( (实际参数表实际参数表) )其中实际参数简称实参,用来将实际参数的值传递给形参,其中实际参数简称实参,用来将实际参数的值传递给形参,因此可以是常量、具有值的变量或表达式。因此可以是常量、具有值的变量或表达式。main( )函数函数调用调用max(2.5,4.7 )函数函数max(2.5,4.7 )return 4.7 主程序后主程序后续语句续语句【例【例41】 输入两个实数,输出其中较大的数。其中求两个实输入两个实数,输出其中较大的数。其中
10、求两个实数中的较大数用函数完成。数中的较大数用函数完成。程序如下程序如下:#includefloat max(float x,float y)return(x=y?x:y);void main()float x,y;cout输入两个实数:输入两个实数:xy;coutx和和y中较大数为中较大数为max(x,y)endl; 421 函数的参数传递及传值调用函数的参数传递及传值调用 423 函数原型说明函数原型说明422 函数返回值函数返回值 函数调用首先要进行参数传递,参数传递的方向是由实参函数调用首先要进行参数传递,参数传递的方向是由实参传递给形参。传递过程是,传递给形参。传递过程是,先计算实参
11、表达式的值,再将先计算实参表达式的值,再将该值传递给对应的形参变量该值传递给对应的形参变量。一般情况下,。一般情况下,实参和形参的实参和形参的个数和排列顺序应一一对应,并且对应参数应类型匹配个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容)(赋值兼容), ,即实参的类型可以转化为形参类型。而对即实参的类型可以转化为形参类型。而对应参数的参数名则不要求相同。应参数的参数名则不要求相同。按照参数形式的不同,按照参数形式的不同,C+有两种调用方式:有两种调用方式:传值调用传值调用和和引用调用引用调用。顾名思义,传值调用传递的是实参的值,。顾名思义,传值调用传递的是实参的值,本章主要介绍传值
12、调用。关于引用调用,将在第五章类本章主要介绍传值调用。关于引用调用,将在第五章类与对象中介绍。与对象中介绍。调用调用power(4.6,3 )函数函数power(4.6,3 )return 97.336 主程序后续语主程序后续语句句n= 3x= 4.6c= a【例【例42】 说明实参和形参对应关系的示例。说明实参和形参对应关系的示例。#include #includefloat power(float x,int n)/求求x x的的n n次幂次幂float pow=1;while(n-) pow*=x;return pow; void main()int n=3;float x=4.6;ch
13、ar c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x)endl; 调用调用power(a,3 )函数函数power(a,3 )return 912673 主程序后续语主程序后续语句句n= 3x= 4.6c= a【例例42】 说明实参和形参对应关系的示例。说明实参和形参对应关系的示例。#include #includefloat power(float x,int n) /求求x x的的n n次幂次幂float pow=1;while(n-) pow*=x;retur
14、n pow; void 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;调用调用power(3,4.6 )函数函数power(3,4.6 )return 81 主程序后续语主程序后续语句句n= 3x=4.6c= a【例例42】 说明实参和形参对应关系的示例。说明实参和形参对应关系的示例。#include #includefloat power(float x,int n) /求求x x的的n n
15、次幂次幂float pow=1;while(n-) pow*=x;return pow; void 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语句的一般格式为:语句的一般格式为: return 表达式;表达式;函数的计算结果通过该语句传递回主调函数。函数的计算结果通过该语句传递回主调函数。【例【例43】设计函数,根据三角形的三边长求面积。如】设计函数,根据三角形的三边长求
16、面积。如果不能构成三角形,给出提示信息。果不能构成三角形,给出提示信息。分析:函数为计算三角形面积,一般三角形返回面积值,分析:函数为计算三角形面积,一般三角形返回面积值,若不能构成三角形则返回若不能构成三角形则返回-1。设计一个主函数完成函数。设计一个主函数完成函数测试。根据返回值情况输出相应结果。测试。根据返回值情况输出相应结果。程序见下页:程序见下页:#include#includefloat 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
17、; return sqrt(s*(s-a)*(s-b)*(s-c);void 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;函数可以有返回值,也可以没有返回值。对函数可以有返回值,也可以没有返回值。对于没有返回值的函数,功能只是完成一定操作,应于没有返回值的函数,功能只是完成一定操作,应将返回值类型定义为将返回
18、值类型定义为void ,函数体内可以没有,函数体内可以没有return语句,当需要在程序指定位置退出时,可语句,当需要在程序指定位置退出时,可以在该处放置一个:以在该处放置一个:return ;4.2.2结束结束 函数原型是一条以分号结束的语句,实际上函数原型是一条以分号结束的语句,实际上就是所定义函数的函数头,形如:就是所定义函数的函数头,形如:函数返回值类型函数返回值类型函数名函数名 ( (形参表形参表) ) 语法上对程序文件中函数的排列次序是没有固定要求的,语法上对程序文件中函数的排列次序是没有固定要求的,只要满足只要满足先定义后使用先定义后使用即可。但从结构化程序设计的角度,即可。但从
19、结构化程序设计的角度,通常是先调用后定义。使用函数原型,则既符合由粗到精通常是先调用后定义。使用函数原型,则既符合由粗到精的思维方式,又满足了语法要求。的思维方式,又满足了语法要求。 其中形参表可以逐个列出每个参数的类型和参数名,其中形参表可以逐个列出每个参数的类型和参数名,也可以列出每个形参的类型,也可以列出每个形参的类型,参数名可省略参数名可省略,各形参之间,各形参之间以逗号分隔。函数原型和所定义的函数必须在返回值类型、以逗号分隔。函数原型和所定义的函数必须在返回值类型、函数名、形参个数和类型及函数名、形参个数和类型及次序次序等方面完全对应一致,否等方面完全对应一致,否则将导致编译错误。则
20、将导致编译错误。 下面是一个使用结构化程序设计思想开发的企业管理下面是一个使用结构化程序设计思想开发的企业管理报表程序的框架。它使用了函数原型说明。报表程序的框架。它使用了函数原型说明。#include void menu_print();void account_report();void engineering_report();void marketing_report();void main() int choice; do menu_print();cinchoice; while(choice=4); switch(choice) case 1: account_report();
21、 break; case 2: engineering_report(); break; case 3: marketing_report(); break; void menu_print() cout”系统功能:系统功能:”endl; cout”1 财务报表财务报表”endl; cout”2 工程报表工程报表”endl; cout”3 市场报表市场报表”endl; cout”选择业务序号:选择业务序号:”; void account_report() /生成财务报表生成财务报表void engineering_report() /生成工程报表生成工程报表 void marketing_re
22、port() /生成市场报表;生成市场报表;【例【例44】 输出所有满足下列条件的正整数输出所有满足下列条件的正整数m:10m1000且且m、m2、m3均为回文数。均为回文数。分析:分析:回文指左右对称的序列。如回文指左右对称的序列。如121、353等就是回等就是回文数。判断整数是否回文数用函数实现,其思想是将文数。判断整数是否回文数用函数实现,其思想是将该数各位拆开后反向组成新的整数,如果该整数与原该数各位拆开后反向组成新的整数,如果该整数与原数相等则为回文数。数相等则为回文数。程序如下:程序如下:#include#includebool palindrome(int); /函数原型函数原型
23、void main()coutsetw(10)msetw(20)m*m“setw(20)m*m*mendl;for(int m=11;m1000;m+)if(palindrome(m)&palindrome(m*m) &palindrome(m*m*m)coutsetw(10)msetw(20)m*msetw(20)m*m*0);for(intj=0;ji;j+) n=n*10+digitj;return (n=m);m m*m m*m*m11 121 1331101 10201 1030301111 12321 1367631 运行结果:运行结果: 堆区堆区 ( (动态数据动态数据) )栈区
24、(函数局部数据)栈区(函数局部数据)(main()函数局部数据)函数局部数据)全局数据区全局数据区( (全局、静态全局、静态) )代码区(程序代码)代码区(程序代码) 操作系统为一个操作系统为一个C+C+程序的运行所分配的内程序的运行所分配的内存分为四个区域,如图存分为四个区域,如图4.34.3 程序在内存中的程序在内存中的区域区域所示:所示:(1)代码区()代码区(Code area):存放程序代码,):存放程序代码,即程序中各个函数的代码块;即程序中各个函数的代码块;(2)全局数据区()全局数据区(Data area):存放全局数):存放全局数据和静态数据;分配该区时内存全部清零。据和静态
25、数据;分配该区时内存全部清零。(3)栈区()栈区(Stack area):存放局部变量,如):存放局部变量,如函数中的变量等;分配栈区时内存不处理。函数中的变量等;分配栈区时内存不处理。(4)堆区()堆区(Heap area):存放与指针相关的):存放与指针相关的动态数据。分配堆区时内存不处理。参见第七动态数据。分配堆区时内存不处理。参见第七章。章。在所有函数之外定义的变量称为在所有函数之外定义的变量称为全局变量全局变量。全局变量在编译时建立在全局数据区,在未给全局变量在编译时建立在全局数据区,在未给出初始化值时系统自动出初始化值时系统自动初始化为全初始化为全0。全局变量可定义在程序开头,也可
26、定义在中间全局变量可定义在程序开头,也可定义在中间位置,该全局变量位置,该全局变量在定义处之后在定义处之后的任何位置都是可的任何位置都是可以访问的,称为可见的。以访问的,称为可见的。请看下例:请看下例:打印打印200调用调用func( )函数函数func( )200*2=400打印打印400n=100n=100*2=200【例【例4 45 5】 多个函数使用全局变量的例子。多个函数使用全局变量的例子。#includeint n=100;void func()n*=2; void main()n*=2;coutnendl;func();coutnendl; 定义在函数内或块内的变量称为定义在函数
27、内或块内的变量称为局部变量局部变量。程序中使用的绝大多数变量都是局部变量。程序中使用的绝大多数变量都是局部变量。局部变量在程序运行到它所在的块时建立在栈中,局部变量在程序运行到它所在的块时建立在栈中,该块执行完毕局部变量占有的空间即被释放。该块执行完毕局部变量占有的空间即被释放。局部变量在定义时可加修饰词局部变量在定义时可加修饰词auto,但通常省略。但通常省略。局部变量在定义时若未初始化,其值为随机数。局部变量在定义时若未初始化,其值为随机数。打印打印main()中的中的t=3.5调用调用fun( )函数函数fun( )打印打印fun()中的中的t=5 打印打印main()中的中的t=3.5
28、t= 3.5t = 5【例【例49】 使用局部变量的例子。使用局部变量的例子。#includevoid fun()auto int t=5;/ fun()中的局部变量,中的局部变量,auto可省略可省略coutfun()中的中的t=tendl;void main()float t=3.5;/main()函数中的局部变量函数中的局部变量coutmain()中的中的t=tendl;fun();coutmain()中的中的t=tendl;局部变量占用的内存是在程序执行过程中局部变量占用的内存是在程序执行过程中“动态动态”地建立地建立和释放的。这种和释放的。这种“动态动态”是通过栈由系统自动管理进行的
29、。当是通过栈由系统自动管理进行的。当任何一个函数调用发生时,系统都要作以下工作:任何一个函数调用发生时,系统都要作以下工作:(1)建立栈空间;)建立栈空间;(6)恢复现场:取主调函数运行状态及返回地址,释放栈空间;)恢复现场:取主调函数运行状态及返回地址,释放栈空间;(7)继续主调函数后续语句。)继续主调函数后续语句。(5)释放被调函数中局部变量占用的栈空间;)释放被调函数中局部变量占用的栈空间;(4)执行被调函数函数体;)执行被调函数函数体;(3)为被调函数中的局部变量分配空间,完成参数传递;)为被调函数中的局部变量分配空间,完成参数传递;(2)保护现场:主调函数运行状态和返回地址入栈;)保
30、护现场:主调函数运行状态和返回地址入栈;void fun1(int, int);void fun2(float);void main() int x=1;y=2; fun1(x, y);void fun1(int a,int b) float x=3; fun2(x);void fun2(float y) int x; x栈顶栈顶栈底栈底y3fun2()fun1()运行状态及返回地址运行状态及返回地址x3b2a1fun1()main()运行状态及返回地址运行状态及返回地址y2x1main()操作系统运行状态及返回地址操作系统运行状态及返回地址此图例说明在程序执行过程中怎样通过栈此图例说明在程序
31、执行过程中怎样通过栈“动态动态”地建立和地建立和释放局部变量占用的内存的释放局部变量占用的内存的4.5.1 作作 用用 域域4.5.2 变量的存储类型变量的存储类型4.5.3外部存储类型与静态存储类型外部存储类型与静态存储类型 4.5.4 生命期与可见性生命期与可见性 1 块作用域块作用域 3 文件作用域文件作用域 2 函数原型作用域函数原型作用域 作用域作用域指标识符能够被使用的范围。只有在作用指标识符能够被使用的范围。只有在作用域内标识符才可以被访问(称为可见)。域内标识符才可以被访问(称为可见)。本节只讨论本节只讨论局部域局部域和文件域(全局域),其中局和文件域(全局域),其中局部域包括
32、部域包括块域块域和和函数原型域函数原型域。任何。任何标识符标识符作用域的起作用域的起始点均为始点均为标识符说明标识符说明处。处。下面分别介绍下面分别介绍:参和函数体中定义的局部变量,作用域都在该函数内,参和函数体中定义的局部变量,作用域都在该函数内,也称作也称作函数域函数域。块块指一对大括号括起来的程序段。块中定义的标指一对大括号括起来的程序段。块中定义的标识符,作用域在块内。识符,作用域在块内。复合语句是一个块。复合语句是一个块。函数也是一个块。函数也是一个块。复合语句中定义的标识符,复合语句中定义的标识符,作用域仅在该复合语句中。作用域仅在该复合语句中。函数中定义的标识符,包括形函数中定义
33、的标识符,包括形a= 3 b= 535a=3 b=5a=5 b=3【例【例47】 输入两数,按从大到小的顺序保存,并输出结果。输入两数,按从大到小的顺序保存,并输出结果。结果结果栈栈t = 3 #includevoid main() int a,b; /具有函数域具有函数域 cout输入两整数:输入两整数:ab; cout“a=atb=b=a)int t; /具有块域具有块域 t=a; a=b; b=t; /交换交换a,b的值的值 couta=atb=bendl;【例【例48】设计函数完成两数交换,用主函数进行测试。】设计函数完成两数交换,用主函数进行测试。#includevoid swap(
34、int,int);void main() int a,b; /a,b作用域为作用域为main() cout输入两整数:输入两整数:ab; cout调用前:实参调用前:实参a=a, b=bendl; swap(a,b); /传值传值 cout调用后:实参调用后:实参a=a,b=bendl;void swap(int a,int b) /a,b作用域为作用域为swap() cout调用中调用中endl; cout交换前:形参交换前:形参a=“ a,b=bendl; int t; t=a; a=b; b=t;/交换交换swap()中的中的a,b的值的值 cout交换后:形参交换后:形参a=a,b=b
35、endl;由由VC+平台运行,结果如下:平台运行,结果如下:输入两整数:输入两整数:3 5调用前:实参调用前:实参a=3,b=5调用中调用中交换前:形参交换前:形参a=3,b=5交换后:形参交换后:形参a=5,b=3调用后:实参调用后:实参a=3,b=5 交换失败交换失败局部变量具有局部作用域使得程序在不同块中可以局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些同名变量各自在自己的作用域使用同名变量。这些同名变量各自在自己的作用域中可见,在其它地方不可见。中可见,在其它地方不可见。 对于块中对于块中嵌套嵌套其它块的情况,如果嵌套块中有同其它块的情况,如果嵌套块中有同名局部变量,服
36、从局部优先原则,即在内层块中名局部变量,服从局部优先原则,即在内层块中屏屏蔽蔽外层块中的同名变量,换句话说,内层块中局部外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用变量的作用域为内层块;外层块中局部变量的作用域为外层除去包含同名变量的内层块部分。域为外层除去包含同名变量的内层块部分。如果块内定义的局部变量与全局变量同名,块内仍如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符可以通过域运算符“:”访问同名的全局变量。访问同名的全局变量。全局全局n= 100
37、 200 300内内 i= 500内内 j= 600内内n=500+600 =11001100 500 600100200+300=500500 500 200 300外部外部 i=200外部外部 j=300【例【例49】 显示同名变量可见性。显示同名变量可见性。int n=100;#includevoid main() int i=200,j=300;cout ntitjendl; /内部块内部块 int i=500,j=600,n; n=i+j; cout ntitj endl; /输出局部变量输出局部变量n cout:nendl;/输出全局变量输出全局变量n n=i+j;/修改全局变量修
38、改全局变量cout ntitj endl; 函数原型不是定义函数,在作函函数原型不是定义函数,在作函数原型声明时,其中的形参作用域数原型声明时,其中的形参作用域只在原型声明中,即只在原型声明中,即作用域结束于作用域结束于右括号右括号。正是由于形参不能被程序。正是由于形参不能被程序的其他地方引用,所以通常只要声的其他地方引用,所以通常只要声明形参个数和类型,明形参个数和类型,形参名可省略形参名可省略。文件作用域文件作用域也称全局作用域。定义在所有函数之外的也称全局作用域。定义在所有函数之外的标识符,具有文件作用域,作用域为从定义处到整个标识符,具有文件作用域,作用域为从定义处到整个源文件结束。文
39、件中定义的全局变量和函数都具有文源文件结束。文件中定义的全局变量和函数都具有文件作用域。件作用域。如果某个文件中说明了具有文件作用域的标识符,该如果某个文件中说明了具有文件作用域的标识符,该文件又被另一个文件包含,则该标识符的作用域延伸文件又被另一个文件包含,则该标识符的作用域延伸到新的文件中。如到新的文件中。如cincin和和coutcout是在头文件是在头文件iostream.hiostream.h中说中说明的具有文件作用域的标识符,它们的作用域也延伸明的具有文件作用域的标识符,它们的作用域也延伸到嵌入到嵌入iostream.hiostream.h的文件中。的文件中。存储类型决定了变量的存
40、储类型决定了变量的生命期生命期,变量生命期指从,变量生命期指从获得空间到空间释放之间的时期。获得空间到空间释放之间的时期。存储类型的说明符有四个存储类型的说明符有四个:auto, register, static和和extern。前两者称为自动类型,后两者分别为静态和外。前两者称为自动类型,后两者分别为静态和外部类型。部类型。本节重点掌握本节重点掌握static和和extern这两种类型的使用和这两种类型的使用和区别。具体说,区分区别。具体说,区分局部变量和静态局部变量局部变量和静态局部变量,全局全局变量和静态全局变量变量和静态全局变量。auto:前面提到的局部变量都是自动类型。其空间分配前面
41、提到的局部变量都是自动类型。其空间分配于块始,空间释放于块终,且由系统自动进行。自于块始,空间释放于块终,且由系统自动进行。自动变量保存在栈中,且是在程序运行过程中获得和动变量保存在栈中,且是在程序运行过程中获得和释放空间,未初始化时值为随机数。释放空间,未初始化时值为随机数。register:为提高程序运行效率,可以将某些变量保存在为提高程序运行效率,可以将某些变量保存在寄存器中,即说明为寄存器变量,但不提倡使用。寄存器中,即说明为寄存器变量,但不提倡使用。static:静态变量。根据被修饰变量的位置不同,分为静态变量。根据被修饰变量的位置不同,分为局部(内部)静态变量和全局(外部)静态变量
42、。局部(内部)静态变量和全局(外部)静态变量。所有静态变量均存放在全局数据区所有静态变量均存放在全局数据区,编译时获得存,编译时获得存储空间,未初始化时自动全储空间,未初始化时自动全0 0,且只初始化一次。,且只初始化一次。局部静态变量的局部静态变量的作用域作用域为为块域块域,但,但生命期生命期为为整个整个文件文件。即当块结束时,局部静态变量空间仍然保持,。即当块结束时,局部静态变量空间仍然保持,直到整个程序文件结束时该局部静态变量空间才释放,直到整个程序文件结束时该局部静态变量空间才释放,生命期结束。生命期结束。【例【例410】 自动变量与局部静态变量的区别。(演自动变量与局部静态变量的区别
43、。(演示)示)#include st() static int t=100; /局部静态变量局部静态变量 t+; return t;at() int t=100; /自动变量自动变量 t+;return t;void main() int i; for(i=0;i5;i+) coutat()t; coutendl; for(i=0;i5;i+) coutst()t; coutendl;i= 0t= 10012 34 5101101101101101i= 0t=1001 21013 4 5102103104105#include st() static int t=100; /局部静态变量局部静
44、态变量 t+; return t;at() int t=100; /自动变量自动变量 t+;return t;void main() int i; for(i=0;i5;i+) coutat()t; coutendl; for(i=0;i5;i+) coutst()t; coutendl;全局静态变量是指用全局静态变量是指用static修饰的全局修饰的全局变量。有关内容在下节静态存储类型中介绍。变量。有关内容在下节静态存储类型中介绍。1. 外部存储类型外部存储类型2. 静态存储类型静态存储类型一个一个C+C+程序可以由多个源程序文件组成,编译系统程序可以由多个源程序文件组成,编译系统将这若干个
45、文件连接在一起,产生可执行程序。外部将这若干个文件连接在一起,产生可执行程序。外部存储类型和静态存储类型确定了变量和函数在多文件存储类型和静态存储类型确定了变量和函数在多文件程序中的联络关系。程序中的联络关系。 外部存储类型包括外部变量和外部函数。在由多个源程序外部存储类型包括外部变量和外部函数。在由多个源程序文件组成的程序中,如果一个文件要使用另一个文件中定文件组成的程序中,如果一个文件要使用另一个文件中定义的全局变量或函数,这些源程序文件之间通过外部类型义的全局变量或函数,这些源程序文件之间通过外部类型的变量和函数进行沟通。的变量和函数进行沟通。在一个文件中定义的全局变量和函数都缺省为外部
46、的,即在一个文件中定义的全局变量和函数都缺省为外部的,即其作用域可以延伸到程序的其他文件中。但其他文件如果其作用域可以延伸到程序的其他文件中。但其他文件如果要使用这个文件中定义的全局变量和函数,必须在使用前要使用这个文件中定义的全局变量和函数,必须在使用前用用“extern”extern”作外部声明,外部声明通常放在文件的开头作外部声明,外部声明通常放在文件的开头。变量定义时编译器为其分配存储空间,而变量声明指明该变量定义时编译器为其分配存储空间,而变量声明指明该全局变量已在其他地方说明过,编译系统不再分配存储空全局变量已在其他地方说明过,编译系统不再分配存储空间,直接使用变量定义时所分配的空
47、间。间,直接使用变量定义时所分配的空间。函数声明缺省为外部的,因此修饰词函数声明缺省为外部的,因此修饰词externextern通常省略。通常省略。【例【例4.11】外部存储类型的例子。假定程序包含两个源程序文件】外部存储类型的例子。假定程序包含两个源程序文件Ex4_11_1.cpp和和Ex4_11_2.cpp,程序结构如下:,程序结构如下:/* Ex4_11_1.cpp ,由,由main()组成组成*/# include void fun2(); /外部函数声明,等价于外部函数声明,等价于extern void fun2();int n; /全局变量定义全局变量定义void main() n
48、=1; fun2(); / fun2()定义在文件定义在文件Ex4_11_2.cpp中中 coutn=nendl;/* Ex4_11_2.cpp,由,由fun2()组成组成*/extern int n; /外部变量声明,外部变量声明,n定义在文件定义在文件Ex4_11_1.cpp中中void fun2() /fun2()被文件被文件Ex4_11_1.cpp中的函数调用中的函数调用n=3; 运行结果:运行结果:n=3静态存储类型包括静态全局变量和静态函数。在定义全局静态存储类型包括静态全局变量和静态函数。在定义全局变量或函数时加说明符变量或函数时加说明符staticstatic,就成为静态变量或
49、静态函,就成为静态变量或静态函数。静态存储类型的作用域与外部存储类型相反,一旦定数。静态存储类型的作用域与外部存储类型相反,一旦定义为静态存储类型,就限制该变量或函数只能在定义它的义为静态存储类型,就限制该变量或函数只能在定义它的文件中使用。静态全局变量在编译时分配存储空间,如果文件中使用。静态全局变量在编译时分配存储空间,如果定义时不指定初值,则编译系统将其初始化为全定义时不指定初值,则编译系统将其初始化为全0 0。一个全局变量和一个静态全局变量在使用上是不同的,一个全局变量和一个静态全局变量在使用上是不同的,其他文件通过外部变量声明可以使用一个全局变量,但其他文件通过外部变量声明可以使用一
50、个全局变量,但却无法使用静态全局变量,却无法使用静态全局变量,静态全局变量只能被定义它静态全局变量只能被定义它的文件所独享的文件所独享。函数与静态函数之间的区别是相同的。函数与静态函数之间的区别是相同的。1. 生命期生命期2. 可见性可见性 生命期(生命期(Life timeLife time)也叫生存期。生命期与存储区域相关,)也叫生存期。生命期与存储区域相关,存储区域分为代码区、静态数据区、栈区和堆区,相应地,存储区域分为代码区、静态数据区、栈区和堆区,相应地,生命期分为生命期分为静态生命期、局部生命期和动态生命期静态生命期、局部生命期和动态生命期。 静态生命期静态生命期指的是标识符从程序
51、开始运行时指的是标识符从程序开始运行时存在,即具有存储空间,到程序运行结束时消亡,存在,即具有存储空间,到程序运行结束时消亡,即释放存储空间。具有静态生命期的标识符存放即释放存储空间。具有静态生命期的标识符存放在静态数据区,属于静态存储类型,如全局变量、在静态数据区,属于静态存储类型,如全局变量、静态全局变量、静态局部变量。静态全局变量、静态局部变量。具有静态生命期具有静态生命期的标识符在未被用户初始化的情况下,系统会自的标识符在未被用户初始化的情况下,系统会自动将其初始化为全动将其初始化为全0 0。 函数驻留在代码区,也具有静态生命期。所函数驻留在代码区,也具有静态生命期。所有具有文件作用域
52、的标识符都具有静态生命期。有具有文件作用域的标识符都具有静态生命期。 在函数内部或块中定义的标识符具有局部生命在函数内部或块中定义的标识符具有局部生命期,其生命期开始于执行到该函数或块的标识符声期,其生命期开始于执行到该函数或块的标识符声明处,结束于该函数或块的结束处。具有静态生命明处,结束于该函数或块的结束处。具有静态生命期的标识符存放在栈区。期的标识符存放在栈区。具有局部生命期的标识符具有局部生命期的标识符如果未被初始化,其内容是随机的,不可用如果未被初始化,其内容是随机的,不可用。 具有局部生命期的标识符必定具有局部作用域;具有局部生命期的标识符必定具有局部作用域;但反之不然,但反之不然
53、,静态局部变量具有局部作用域,但却静态局部变量具有局部作用域,但却具有静态生命期具有静态生命期。 具有动态生命期的标识符由特定的函数调具有动态生命期的标识符由特定的函数调用或运算来创建和释放用或运算来创建和释放,如调用,如调用malloc()malloc()或用或用newnew运算符为变量分配存储空间时,变量的生命运算符为变量分配存储空间时,变量的生命期开始,而调用期开始,而调用free()free()或用或用deletedelete运算符释放空间运算符释放空间或程序结束时,变量生命期结束。具有动态生或程序结束时,变量生命期结束。具有动态生命期的变量存放在堆区。关于命期的变量存放在堆区。关于n
54、ewnew运算和运算和deletedelete运算将在指针一章中介绍运算将在指针一章中介绍。 可见性可见性从另一个角度说明标识符的有效性,可见性与从另一个角度说明标识符的有效性,可见性与作用域具有一定的一致性。作用域具有一定的一致性。标识符的作用域包含可见标识符的作用域包含可见范围,可见范围不会超过作用域范围,可见范围不会超过作用域。可见性在理解同名。可见性在理解同名标识符的标识符的作用域嵌套作用域嵌套时十分直观。对于外层块与内层时十分直观。对于外层块与内层块定义了同名标识符的,在外层作用域中,内层所定块定义了同名标识符的,在外层作用域中,内层所定义的标识符是不可见的,即外层引用的是外层所定义
55、义的标识符是不可见的,即外层引用的是外层所定义的标识符;同样,的标识符;同样,在内层作用域中,外层的标识符将在内层作用域中,外层的标识符将被内层的同名标识符屏蔽,变得不可见被内层的同名标识符屏蔽,变得不可见,即外层中同,即外层中同名标识符的可见范围为作用域中挖去内层块的范围。名标识符的可见范围为作用域中挖去内层块的范围。图图4.64.6显示下面程序段中变量的作用域与可见性。显示下面程序段中变量的作用域与可见性。下面的程序段和图示显示作用域与下面的程序段和图示显示作用域与可见性。可见性。 int m=1;float x; float m=3.5; X=5.5;m+; int m, float x
56、作用域作用域int m可见可见float m不可见不可见x可见可见float m作用域作用域float m可见可见int m不可见不可见x可见可见1n 1)!-(n*n1n 1 0n 1 n 递归是一种描述问题的方法,或称算法。递递归是一种描述问题的方法,或称算法。递归的思想可以简单地描述为归的思想可以简单地描述为“自己调用自己自己调用自己”。例如用如下方法定义阶乘:例如用如下方法定义阶乘:可以看出是用阶乘定义阶乘,这种自可以看出是用阶乘定义阶乘,这种自己定义自己的方法称为递归定义。己定义自己的方法称为递归定义。 在函数调用中,有这样两种情况,一种是在函数在函数调用中,有这样两种情况,一种是在
57、函数A的定的定义中有调用函数义中有调用函数A的语句,即自己调用自己;另一种是函数的语句,即自己调用自己;另一种是函数A的定义中出现调用函数的定义中出现调用函数B的语句,而函数的语句,而函数B的定义中也出的定义中也出现调用函数现调用函数A的语句,即相互调用。前者称的语句,即相互调用。前者称直接递归直接递归,后者,后者称称间接递归间接递归。本节只介绍直接递归。递归函数必须定义递。本节只介绍直接递归。递归函数必须定义递归归终止条件终止条件(Stopping condition),避免无穷递归(),避免无穷递归(Infinite Recursion)。)。递归定义的阶乘算法用函数描述为:递归定义的阶乘
58、算法用函数描述为:fac(int n)if (n=0|n=1) return 1;else return n*fac(n-1);只要设计主函数调用阶乘函数,即可实现计算阶乘。只要设计主函数调用阶乘函数,即可实现计算阶乘。【例【例412】 求求4!#include int fac(int n)int y; coutnt;if(n=0|n=1) y=1; else y=n*fac(n-1);coutyt;return y;void main()coutn4!=fac(4)endl; n=4cout4;y=4*fac(3);fac(4)=cout2;y=2*fac(1); n=2cout1;y=1;
59、cout1;return 1; n=1 n=3cout3;y=3*fac(2);cout24;return 24;cout6;return 6;cout2;return 2;24递归函数的执行分为递归函数的执行分为“递推递推”和和“回归回归”两个过程两个过程,这两个过程由递归终止条件控制,即,这两个过程由递归终止条件控制,即逐层递推逐层递推,直至直至递归终止条件递归终止条件,然后,然后逐层回归逐层回归。每次调用发生。每次调用发生时都首先判断递归终止条件。递归调用同普通的函时都首先判断递归终止条件。递归调用同普通的函数调用一样,每当调用发生时,在栈中分配单元保数调用一样,每当调用发生时,在栈中分
60、配单元保存返回地址以及参数和局部变量;而与普通的函数存返回地址以及参数和局部变量;而与普通的函数调用不同的是,由于递推的过程是一个逐层调用的调用不同的是,由于递推的过程是一个逐层调用的过程,因此存在一个逐层连续的参数入栈过程,直过程,因此存在一个逐层连续的参数入栈过程,直至遇到递归终止条件时,才开始回归,这时才逐层至遇到递归终止条件时,才开始回归,这时才逐层释放栈空间,返回到上一层,直至最后返回到主调释放栈空间,返回到上一层,直至最后返回到主调函数。函数。 【例【例413】 汉诺塔问题。汉诺塔问题。有有A、B、C三根柱子,三根柱子,A柱柱上有上有n个大小不等的盘子,大盘在下,小盘在上。要求个大
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 青帮内部等级制度
- 2025年涟源市市直医疗卫生机构公开招聘专业技术人员69人备考题库含答案详解
- 中央团校(中国青年政治学院)2026年度社会人员公开招聘10人备考题库及完整答案详解一套
- 2025年许岭镇消防站招聘9人备考题库及答案详解(新)
- 2025年成都东部新区第四中学校教师招聘备考题库及答案详解(新)
- 2025年合肥国家实验室持续招聘备考题库(含答案详解)
- 2025年中山大学孙逸仙纪念医院深汕中心医医务科病案室合同医技岗位招聘备考题库带答案详解
- 2025年中原细胞和免疫治疗实验室公开招聘劳务派遣人员备考题库有完整答案详解
- 2025年复旦大学附属妇产科医院招聘超声科主任备考题库及一套完整答案详解
- 2025年绍兴理工学院人才引进126人备考题库及完整答案详解一套
- 2025-2026学年赣美版(新教材)初中美术八年级下册(全册)教学设计(附目录P134)
- 2025年度济南水务集团有限公司员工招聘160人笔试参考题库附带答案详解
- 2026年春人教PEP版(新教材)四年级下册英语教学计划(含进度表)
- 2026届新高考政治三轮热点复习+订约履约 诚信为本
- 2026中国大唐集团有限公司校园招聘笔试参考题库及答案解析
- 2026年南京铁道职业技术学院单招职业技能测试题库及答案详解(各地真题)
- 光影的进化:电影技术发展史【课件文档】
- 2026年通辽职业学院高职单招职业适应性测试模拟试题及答案详解
- 基于可穿戴设备的运动员训练负荷优化策略
- 2026年杭州科技职业技术学院单招综合素质考试题库附答案解析
- 2026年江西工业工程职业技术学院单招综合素质考试题库参考答案详解
评论
0/150
提交评论