《函数是C程序的基本模块可将一些功能相对独立的或经常课件》由会员分享,可在线阅读,更多相关《函数是C程序的基本模块可将一些功能相对独立的或经常课件(92页珍藏版)》请在金锄头文库上搜索。
1、 函数是函数是C+C+程序的基本模块。可将一些功能相对独立的或程序的基本模块。可将一些功能相对独立的或经常使用的操作或运算抽象出来,定义为函数。使用时只要经常使用的操作或运算抽象出来,定义为函数。使用时只要考虑其功能和使用接口即可。考虑其功能和使用接口即可。 在结构化程序设计中,函数是将任务进行模块划分的基在结构化程序设计中,函数是将任务进行模块划分的基本单位。本单位。 在面向对象的程序设计中,类中所封装的操作是用函数在面向对象的程序设计中,类中所封装的操作是用函数进行描述的,因此函数在进行描述的,因此函数在C+C+程序中具有非常重要的意义。程序中具有非常重要的意义。 要掌握函数的使用,必须理
2、解函数调用时的内部实现机要掌握函数的使用,必须理解函数调用时的内部实现机制,以及与此相关的内存分配机制、变量生命期和作用域。制,以及与此相关的内存分配机制、变量生命期和作用域。 本章还将介绍关于函数重载的概念,介绍递归算法、内本章还将介绍关于函数重载的概念,介绍递归算法、内联函数、默认参数函数以及多文件组织、编译预处理、工程联函数、默认参数函数以及多文件组织、编译预处理、工程文件的概念和运行库函数。文件的概念和运行库函数。第四章第四章 函数函数 函数是C程序的基本模块可将一些功能相对独立的或经常第四章第四章 函数函数 41函数的定义与调用函数的定义与调用45作用域与存储类型作用域与存储类型 4
3、4函数调用机制函数调用机制 43全局变量和局部变量全局变量和局部变量 42函数的参数传递函数的参数传递,返回值及函数原型说明返回值及函数原型说明 410编译预处理编译预处理49头文件与多文件结构头文件与多文件结构48C+的系统库函数的系统库函数47函数的一些高级议题函数的一些高级议题 46函数的递归调用函数的递归调用 函数是C程序的基本模块可将一些功能相对独立的或经常 4.1 函数的定义与调用函数的定义与调用4.1.1 函数概述函数概述4.1.2 函数的定义函数的定义4.1.3 函数的调用函数的调用 函数是C程序的基本模块可将一些功能相对独立的或经常4.1.1 函数概述函数概述函数是函数是C+
4、C+程序的基本组成模块。程序的基本组成模块。通通过过函函数数,可可以以把把一一个个复复杂杂任任务务分分解解成成为为若若干干个个易易于于解解决决的的小小任任务务。充充分分体体现现结结构构化化程程序序设设计计由由粗粗到精,逐步细化的设计思想。到精,逐步细化的设计思想。组组 成成 C+C+程程 序序 的的 若若 干干 函函 数数 中中 , 有有 一一 个个 称称 为为main()main()(Winmain()Winmain())函函数数,是是程程序序执执行行的的入入口口,它它可可以以调调用用其其他他函函数数。而而其其他他一一般般函函数数既既可可以以调调用用也也可可以被调用。函数之间的调用关系见下图
5、:以被调用。函数之间的调用关系见下图:函数是C程序的基本模块可将一些功能相对独立的或经常4.1.1 函数概述函数概述main ( )fun2( )fun1( )fun3( )fun1_1( )fun2_1( )fun2_2( )图图4.1 4.1 函数调用层次关系函数调用层次关系函数是C程序的基本模块可将一些功能相对独立的或经常4.1.1 函数概述函数概述函数函数按是否带有参数,分为按是否带有参数,分为: : 无参函数无参函数和和有参函数有参函数4.1.1结束结束 函数按其是否系统预定义分为两类,一类是编译函数按其是否系统预定义分为两类,一类是编译系统预定义的,称为系统预定义的,称为库函数库函
6、数或或标准函数标准函数,如一些常,如一些常用的数学计算函数、字符串处理函数、图形处理函用的数学计算函数、字符串处理函数、图形处理函数、标准输入输出函数等。这些数、标准输入输出函数等。这些库函数都按功能分库函数都按功能分类,集中说明在不同的头文件中类,集中说明在不同的头文件中。用户只需在自己。用户只需在自己的程序中包含某个头文件,就可直接使用该文件中的程序中包含某个头文件,就可直接使用该文件中定义的函数。另一类是用户定义的函数。另一类是用户自定义函数自定义函数,用户可以,用户可以根据需要将某个具有相对独立功能的程序定义为函根据需要将某个具有相对独立功能的程序定义为函数。数。函数是C程序的基本模块
7、可将一些功能相对独立的或经常4.1.2 函数的定义函数的定义1.无参函数无参函数2.有参函数有参函数函数是C程序的基本模块可将一些功能相对独立的或经常1 无参函数无参函数 定义格式为:定义格式为:数据类型函数名数据类型函数名( (voidvoid)函数体函数体 例例:下面函数的功能是打印一个表头下面函数的功能是打印一个表头voidTableHead()cout*endl;cout*example*endl;cout*=b?a:b);函数是C程序的基本模块可将一些功能相对独立的或经常 定义函数时可能会涉及若干个变量,究竟哪些变量应当作定义函数时可能会涉及若干个变量,究竟哪些变量应当作为函数的参数
8、?哪些应当定义在函数体内?这有一个原则:作为函数的参数?哪些应当定义在函数体内?这有一个原则:作为一个相对独立的模块,为一个相对独立的模块,函数在使用时完全可以被看成函数在使用时完全可以被看成 “ “黑黑匣子匣子”,除了输入输出外,其他部分可不必关心,除了输入输出外,其他部分可不必关心。从函数的定。从函数的定义看出,函数头正是用来反映函数的功能和使用接口,它所定义看出,函数头正是用来反映函数的功能和使用接口,它所定义的是义的是“做什么做什么”,在这部分必须明确,在这部分必须明确“黑匣子黑匣子”的输入输出的输入输出部分,部分,输出就是函数的返回值,输入就是参数输出就是函数的返回值,输入就是参数。
9、因此,只有那。因此,只有那些功能上起自变量作用的变量才必须作为参数定义在参数表中;些功能上起自变量作用的变量才必须作为参数定义在参数表中;函数体中具体描述函数体中具体描述“如何做如何做”,因此除参数之外的为实现算法,因此除参数之外的为实现算法所需用的变量应当定义在函数体内。所需用的变量应当定义在函数体内。 C+ C+中不允许函数的嵌套定义,即在一个函数中定义另一中不允许函数的嵌套定义,即在一个函数中定义另一个函数。个函数。提示提示函数是C程序的基本模块可将一些功能相对独立的或经常4.1.3 函数的调用函数的调用在在C+C+中中,除除了了主主函函数数外外,其其他他任任何何函函数数都都不不能能单单
10、独独作作为为程程序序运运行行。任任何何函函数数功功能能的的实实现现都都是是通通过过被被主主函函数数直直接接或或间间接接调调用进行的。所谓函数调用,就是使程序转去执行函数体。用进行的。所谓函数调用,就是使程序转去执行函数体。 无参函数的调用格式为:无参函数的调用格式为: 函数名函数名( )( ) 有参函数的调用格式为:有参函数的调用格式为: 函数名函数名( (实际参数表实际参数表) )其其中中实实际际参参数数简简称称实实参参,用用来来将将实实际际参参数数的的值值传传递递给给形形参参,因此可以是常量、具有值的变量或表达式。因此可以是常量、具有值的变量或表达式。函数是C程序的基本模块可将一些功能相对
11、独立的或经常4.1.3 函数的调用函数的调用main()函数函数调用调用max(2.5,4.7 )函数函数max(2.5,4.7 )return4.7 主程序后主程序后续语句续语句【例例41】输输入入两两个个实实数数,输输出出其其中中较较大大的的数数。其其中中求求两两个个实实数中的较大数用函数完成。数中的较大数用函数完成。程序如下程序如下:#includefloat max(float x,float y)return(x=y?x:y);void main()float x,y;cout输入两个实数:输入两个实数:xy;coutx和和y中较大数为中较大数为max(x,y)endl;函数是C程序
12、的基本模块可将一些功能相对独立的或经常4.2 函数的参数传递、返回值及函数的参数传递、返回值及函数原型说明函数原型说明 421 函数的参数传递及传值调用函数的参数传递及传值调用 423 函数原型说明函数原型说明422 函数返回值函数返回值 函数是C程序的基本模块可将一些功能相对独立的或经常函数调用首先要进行参数传递,参数传递的方向是由实参函数调用首先要进行参数传递,参数传递的方向是由实参传递给形参。传递过程是,传递给形参。传递过程是,先计算实参表达式的值,再将先计算实参表达式的值,再将该值传递给对应的形参变量该值传递给对应的形参变量。一般情况下,。一般情况下,实参和形参的实参和形参的个数和排列
13、顺序应一一对应,并且对应参数应类型匹配个数和排列顺序应一一对应,并且对应参数应类型匹配(赋值兼容)(赋值兼容), ,即实参的类型可以转化为形参类型。而对即实参的类型可以转化为形参类型。而对应参数的参数名则不要求相同。某些特殊情况下也允许参应参数的参数名则不要求相同。某些特殊情况下也允许参数不对应,这将在函数高级议题中讨论。数不对应,这将在函数高级议题中讨论。按照参数形式的不同,按照参数形式的不同,C+C+有两种调用方式:有两种调用方式:传值调用传值调用和和引用调用引用调用。顾名思义,传值调用传递的是实参的值,本章。顾名思义,传值调用传递的是实参的值,本章主要介绍传值调用。关于引用调用,将在第五
14、章类与对象主要介绍传值调用。关于引用调用,将在第五章类与对象中介绍。中介绍。 4.2.1 函数的参数传递及传值调用函数的参数传递及传值调用 函数是C程序的基本模块可将一些功能相对独立的或经常 4.2.1 函数的参数传递及传值调用函数的参数传递及传值调用 调用调用power(4.6,3)函数函数power(4.6,3)return97.336 主程序后续语主程序后续语句句n= 3x= 4.6c= a【例【例42】 说明实参和形参对应关系的示例。说明实参和形参对应关系的示例。#include #includefloat power(float x,int n)/求求x x的的n n次幂次幂floa
15、t 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; 函数是C程序的基本模块可将一些功能相对独立的或经常 4.2.1 函数的参数传递及传值调用函数的参数传递及传值调用 调用调用power(a,3)函数函数power(a,3)return912673 主程序后续语主程序后续语句句n= 3x= 4.6c= a【例例42】
16、 说明实参和形参对应关系的示例。说明实参和形参对应关系的示例。#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;char c=a;coutpower(x,n)=power(x,n)endl;coutpower(c,n)=power(c,n)endl;coutpower(n,x)=power(n,x)endl;函数是C程序的基本模块可将一些功能相对独立的或经常 4.2.1 函数的参数传递
17、及传值调用函数的参数传递及传值调用 调用调用power(3,4.6)函数函数power(3,4.6)return81主程序后续语主程序后续语句句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;char c=a;coutpower (x,n)=power(x,n)endl;coutpow
18、er (c,n)=power(c,n)endl;coutpower (n,x)=power(n,x)endl;函数是C程序的基本模块可将一些功能相对独立的或经常4.2.2 函数返回值函数返回值return语句的一般格式为:语句的一般格式为:return表达式;表达式;函数的计算结果通过该语句传递回主调函数。函数的计算结果通过该语句传递回主调函数。【例【例43】设计函数,根据三角形的三边长求面积。如】设计函数,根据三角形的三边长求面积。如果不能构成三角形,给出提示信息。果不能构成三角形,给出提示信息。分析:函数为计算三角形面积,一般三角形返回面积值,分析:函数为计算三角形面积,一般三角形返回面积
19、值,若不能构成三角形则返回若不能构成三角形则返回-1。设计一个主函数完成函数测。设计一个主函数完成函数测试。根据返回值情况输出相应结果。试。根据返回值情况输出相应结果。程序见下页:程序见下页:函数是C程序的基本模块可将一些功能相对独立的或经常4.2.2 函数返回值函数返回值#include#includefloatTriangleArea(floata,floatb,floatc)if(a+b=c)|(a+c=b)|(b+c=a)return-1;floats;s=(a+b+c)/2;returnsqrt(s*(s-a)*(s-b)*(s-c);voidmain()floata,b,c,are
20、a;cout输入三角形三边输入三角形三边a,b,c:abc;area=TriangleArea(a,b,c);if(area=-1)cout(a,b,c)不能构成三角形!不能构成三角形!endl;elsecout三角形三角形(a,b,c)面积为:面积为:areaendl;函数是C程序的基本模块可将一些功能相对独立的或经常4.2.2 函数返回值函数返回值函函数数可可以以有有返返回回值值,也也可可以以没没有有返返回回值值。对对于于没没有有返返回回值值的的函函数数,功功能能只只是是完完成成一一定定操操作作,应应将将返返回回值值类类型型定定义义为为void ,函函数数体体内内可可以以没没有有retur
21、n语语句句,当当需需要要在在程程序序指指定定位位置置退退出出时时,可可以以在该处放置一个:在该处放置一个:return;4.2.2结束结束函数是C程序的基本模块可将一些功能相对独立的或经常4.2.3 函数原型说明函数原型说明 函函数数原原型型是是一一条条以以分分号号结结束束的的语语句句,实实际际上上就是所定义函数的函数头,形如:就是所定义函数的函数头,形如:函数返回值类型函数名函数返回值类型函数名 ( (形参表形参表) ) 语语法法上上对对程程序序文文件件中中函函数数的的排排列列次次序序是是没没有有固固定定要要求求的的,只只要要满满足足先先定定义义后后使使用用即即可可。但但从从结结构构化化程程
22、序序设设计计的的角角度度,通通常常是是先先调调用用后后定定义义。使使用用函函数数原原型型,则则既既符符合合由由粗粗到到精精的的思维方式,又满足了语法要求。思维方式,又满足了语法要求。 其中形参表可以逐个列出每个参数的类型和参数名,其中形参表可以逐个列出每个参数的类型和参数名,也可以列出每个形参的类型,也可以列出每个形参的类型,参数名可省略参数名可省略,各形参之间,各形参之间以逗号分隔。函数原型和所定义的函数必须在返回值类型、以逗号分隔。函数原型和所定义的函数必须在返回值类型、函数名、形参个数和类型及函数名、形参个数和类型及次序次序等方面完全对应一致,否等方面完全对应一致,否则将导致编译错误。则
23、将导致编译错误。 函数是C程序的基本模块可将一些功能相对独立的或经常 下下面面是是一一个个使使用用结结构构化化程程序序设设计计思思想想开开发发的的企企业业管管理理报表程序的框架。它使用了函数原型说明。报表程序的框架。它使用了函数原型说明。#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)
24、 case 1: account_report(); break; case 2: engineering_report(); break; case 3: marketing_report(); break; 函数是C程序的基本模块可将一些功能相对独立的或经常voidmenu_print()cout”系统功能:系统功能:”endl;cout”1财务报表财务报表”endl;cout”2工程报表工程报表”endl;cout”3市场报表市场报表”endl;cout”选择业务序号:选择业务序号:”;voidaccount_report()/生成财务报表生成财务报表voidengineering_re
25、port()/生成工程报表生成工程报表voidmarketing_report()/生成市场报表;生成市场报表;函数是C程序的基本模块可将一些功能相对独立的或经常4.2.3 函数原型说明函数原型说明【例例44】 输输出出所所有有满满足足下下列列条条件件的的正正整整数数m:10m1000且且m、m2、m3均为回文数。均为回文数。分分析析:回回文文指指左左右右对对称称的的序序列列。如如121、353等等就就是是回回文文数数。判判断断整整数数是是否否回回文文数数用用函函数数实实现现,其其思思想想是是将将该该数数各各位位拆拆开开后后反反向向组组成成新新的的整整数数,如如果果该该整整数数与与原原数数相相
26、等等则则为为回文数。回文数。程序如下:程序如下:#include#includeboolpalindrome(int);/函数原型函数原型函数是C程序的基本模块可将一些功能相对独立的或经常voidmain()coutsetw(10)msetw(20)m*m“setw(20)m*m*mendl;for(intm=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
27、);函数是C程序的基本模块可将一些功能相对独立的或经常4.2.3 函数原型说明函数原型说明mm*mm*m*m111211331101102011030301111123211367631运行结果:运行结果:函数是C程序的基本模块可将一些功能相对独立的或经常4.3 全局变量和局部变量全局变量和局部变量4 43 31 1 变量的存储机制与变量的存储机制与变量的存储机制与变量的存储机制与C+C+的内存布局的内存布局的内存布局的内存布局 4 43 32 2 全局变量全局变量全局变量全局变量 4 43 33 3 局部变量局部变量局部变量局部变量 函数是C程序的基本模块可将一些功能相对独立的或经常4.3.
28、1 变量的存储机制与变量的存储机制与C+的内存布局的内存布局堆区堆区 ( (动态数据动态数据) )栈区(函数局部数据)栈区(函数局部数据)(main()函数局部数据)函数局部数据)全局数据区全局数据区 ( (全局、静态变量全局、静态变量) )代码区(程序代码)代码区(程序代码) 操操作作系系统统为为一一个个C+C+程程序序的的运运行行所所分分配配的的内内存存分分为为四四个个区区域域,如如图图4.34.3 程程序序在在内内存存中中的的区区域域所示:所示:函数是C程序的基本模块可将一些功能相对独立的或经常(1)代码区()代码区(Codearea):存放程序代码,):存放程序代码,即程序中各个函数的
29、代码块;即程序中各个函数的代码块;(2)全局数据区()全局数据区(Dataarea):存放全局数):存放全局数据和静态数据;分配该区时内存全部清零。据和静态数据;分配该区时内存全部清零。(3)栈区()栈区(Stackarea):存放局部变量,如):存放局部变量,如函数中的变量等;分配栈区时内存不处理。函数中的变量等;分配栈区时内存不处理。(4)堆区()堆区(Heaparea):存放与指针相关的):存放与指针相关的动态数据。分配堆区时内存不处理。参见第七章。动态数据。分配堆区时内存不处理。参见第七章。4.3.1 变量的存储机制与变量的存储机制与C+的内存布局的内存布局函数是C程序的基本模块可将一
30、些功能相对独立的或经常 4.3.2 全局变量全局变量 在所有函数之外定义的变量称为在所有函数之外定义的变量称为全局变量全局变量。全全局局变变量量在在编编译译时时建建立立在在全全局局数数据据区区,在在未未给给出初始化值时系统自动出初始化值时系统自动初始化为全初始化为全0。全全局局变变量量可可定定义义在在程程序序开开头头,也也可可定定义义在在中中间间位位置置,该该全全局局变变量量在在定定义义处处之之后后的的任任何何位位置置都都是是可可以访问的,称为可见的。以访问的,称为可见的。请看下例:请看下例:函数是C程序的基本模块可将一些功能相对独立的或经常4.3.2 全局变量全局变量打印打印200调用调用f
31、unc()函数函数func()200*2=400打印打印400n=100n=100*2=200【例【例4 45 5】 多个函数使用全局变量的例子。多个函数使用全局变量的例子。#includeintn=100;voidfunc()n*=2;voidmain()n*=2;coutnendl;func();coutnendl;函数是C程序的基本模块可将一些功能相对独立的或经常4.3.3 局部变量局部变量 定义在函数内或块内的变量称为定义在函数内或块内的变量称为局部变量局部变量。程序中使用的绝大多数变量都是局部变量。程序中使用的绝大多数变量都是局部变量。局局部部变变量量在在程程序序运运行行到到它它所所
32、在在的的块块时时建建立立在在栈栈中中,该块执行完毕局部变量占有的空间即被释放。该块执行完毕局部变量占有的空间即被释放。局局部部变变量量在在定定义义时时可可加加修修饰饰词词auto,但但通通常常省省略略。局部变量在定义时若未初始化,其值为随机数。局部变量在定义时若未初始化,其值为随机数。函数是C程序的基本模块可将一些功能相对独立的或经常4.3.3 局部变量局部变量 打印打印main()中的中的t=3.5调用调用fun()函数函数fun()打印打印fun()中的中的t=5打印打印main()中的中的t=3.5t= 3.5t=5【例【例49】使用局部变量的例子。使用局部变量的例子。#includev
33、oidfun()autointt=5;/fun()中的局部变量,中的局部变量,auto可省略可省略coutfun()中的中的t=tendl;voidmain()floatt=3.5;/main()函数中的局部变量函数中的局部变量coutmain()中中的的t=tendl;fun();coutmain()中中的的t=tendl;函数是C程序的基本模块可将一些功能相对独立的或经常4.4 函数调用机制函数调用机制 局局部部变变量量占占用用的的内内存存是是在在程程序序执执行行过过程程中中“动动态态”地地建建立立和和释释放放的的。这这种种“动动态态”是是通通过过栈栈由由系系统统自自动动管管理理进进行行的
34、的。当当任何一个函数调用发生时,系统都要作以下工作:任何一个函数调用发生时,系统都要作以下工作:(1)建立栈空间;)建立栈空间;(6)恢复现场:取主调函数运行状态及返回地址,释放栈空间;)恢复现场:取主调函数运行状态及返回地址,释放栈空间;(7)继续主调函数后续语句。)继续主调函数后续语句。(5)释放被调函数中局部变量占用的栈空间;)释放被调函数中局部变量占用的栈空间;(4)执行被调函数函数体;)执行被调函数函数体;(3)为被调函数中的局部变量分配空间,完成参数传递;)为被调函数中的局部变量分配空间,完成参数传递;(2)保护现场:主调函数运行状态和返回地址入栈;)保护现场:主调函数运行状态和返
35、回地址入栈;函数是C程序的基本模块可将一些功能相对独立的或经常4.4 函数调用机制函数调用机制 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()操作系统运行状态及返回地址操作系统运行状态及返回
36、地址此图例说明此图例说明在程序执行过程中怎样通过栈在程序执行过程中怎样通过栈“动态动态”地建立和地建立和释放局部变量占用的内存的释放局部变量占用的内存的函数是C程序的基本模块可将一些功能相对独立的或经常 4.5 作用域与存储类型作用域与存储类型4.5.1 作作用用域域4.5.2变量的存储类型变量的存储类型4.5.3外部存储类型与静态存储类型外部存储类型与静态存储类型 4.5.4生命期与可见性生命期与可见性 函数是C程序的基本模块可将一些功能相对独立的或经常4.5.1 作用域作用域 1块作用域块作用域 3文件作用域文件作用域 2函数原型作用域函数原型作用域 作作用用域域指指标标识识符符能能够够被
37、被使使用用的的范范围围。只只有有在在作作用用域内标识符才可以被访问(称为可见)。域内标识符才可以被访问(称为可见)。本本节节只只讨讨论论局局部部域域和和文文件件域域(全全局局域域),其其中中局局部部域域包包括括块块域域和和函函数数原原型型域域。任任何何标标识识符符作作用用域域的的起起始点均为始点均为标识符说明标识符说明处。处。下面分别介绍下面分别介绍:函数是C程序的基本模块可将一些功能相对独立的或经常参参和和函函数数体体中中定定义义的的局局部部变变量量,作作用用域域都都在在该该函函数数内内,也称作也称作函数域函数域。1.块域块域块块指指一一对对大大括括号号括括起起来来的的程程序序段段。块块中中
38、定定义义的的标标识符,作用域在块内。识符,作用域在块内。复合语句是一个块。复合语句是一个块。函数也是一个块。函数也是一个块。复合语句中定义的标识符,复合语句中定义的标识符,作用域仅在该复合语句中。作用域仅在该复合语句中。函数中定义的标识符,包括形函数中定义的标识符,包括形函数是C程序的基本模块可将一些功能相对独立的或经常1.块域块域a= 3 b= 535a=3 b=5a=5 b=3【例【例47】输入两数,将两数按从大到小的顺序保存,并输出结果。输入两数,将两数按从大到小的顺序保存,并输出结果。结果结果栈栈t=3#includevoidmain()inta,b;/具有函数域具有函数域cout输入
39、两整数:输入两整数:ab;cout“a=atb=b=a)intt;/具有块域具有块域t=a;a=b;b=t;/交换交换a,b的值的值couta=atb=bendl;函数是C程序的基本模块可将一些功能相对独立的或经常【例【例48】设计函数完成两数交换,用主函数进行测试】设计函数完成两数交换,用主函数进行测试,看结果如何,看结果如何。#includevoids);voidmain()inta,b;/a,b作用域为作用域为main()cout输入两整数:输入两整数:ab;cout调用前:实参调用前:实参a=a,b=bendl;swap(a,b);/传值传值cout调用后:实参调用后:实参a=a,b=
40、bendl;voidsa,intb)/a,b作用域为作用域为swap()cout调用中调用中endl;cout交换前:形参交换前:形参a=“a,b=bendl;intt;t=a;a=b;b=t;/交换交换swap()中的中的a,b的值的值cout交换后:形参交换后:形参a=a,b=bendl;局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些局部变量具有局部作用域使得程序在不同块中可以使用同名变量。这些同名变量各自在自己的作用域中可见,在其它地方不可见同名变量各自在自己的作用域中可见,在其它地方不可见。函数是C程序的基本模块可将一些功能相对独立的或经常1.块作用域块作用域由由VC+
41、平台运行,结果如下:平台运行,结果如下:输入两整数:输入两整数:35调用前:实参调用前:实参a=3,b=5调用中调用中交换前:形参交换前:形参a=3,b=5交换后:形参交换后:形参a=5,b=3调用后:实参调用后:实参a=3,b=5交换失败交换失败 对于块中对于块中嵌套嵌套其它块的情况,如果嵌套块中有同名局部变量,服从局其它块的情况,如果嵌套块中有同名局部变量,服从局部优先原则,即在内层块中部优先原则,即在内层块中屏蔽屏蔽外层块中的同名变量,换句话说,内层块外层块中的同名变量,换句话说,内层块中局部变量的作用域为内层块;外层块中局部变量的作用域为外层除去包中局部变量的作用域为内层块;外层块中局
42、部变量的作用域为外层除去包含同名变量的内层块部分。含同名变量的内层块部分。 如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,如果块内定义的局部变量与全局变量同名,块内仍然局部变量优先,但与块作用域不同的是,在块内可以通过域运算符但与块作用域不同的是,在块内可以通过域运算符“:”访问同名的全访问同名的全局变量。局变量。函数是C程序的基本模块可将一些功能相对独立的或经常全局全局n= 100100 200 300内内i=500内内j=600内内n=500+600=11001100 500 600100200+300=500500 500 200 300外部外部 i=200外部外部 j=3
43、00【例【例49】显示同名变量可见性。显示同名变量可见性。intn=100;#includevoidmain()inti=200,j=300;coutntitjendl;/内部块内部块inti=500,j=600,n;n=i+j;coutntitjendl;/输出局部变量输出局部变量ncout:nendl;/输出全局变量输出全局变量nn=i+j;/修改全局变量修改全局变量coutntitjendl;函数是C程序的基本模块可将一些功能相对独立的或经常2函数原型作用域函数原型作用域 函函数数原原型型不不是是定定义义函函数数,在在作作函函数数原原型型声声明明时时,其其中中的的形形参参作作用用域域只只
44、在在原原型型声声明明中中,即即作作用用域域结结束束于于右右括括号号。正正是是由由于于形形参参不不能能被被程程序序的的其其他他地地方方引引用用,所所以以通通常常只只要要声声明明形形参参个个数和类型,数和类型,形参名可省略形参名可省略。函数是C程序的基本模块可将一些功能相对独立的或经常3 文件作用域文件作用域 文文件件作作用用域域也也称称全全局局作作用用域域。定定义义在在所所有有函函数数之之外外的的标标识识符符,具具有有文文件件作作用用域域,作作用用域域为为从从定定义义处处到到整整个个源源文文件件结结束束。文文件件中中定定义义的全局变量和函数都具有文件作用域。的全局变量和函数都具有文件作用域。如如
45、果果某某个个文文件件中中说说明明了了具具有有文文件件作作用用域域的的标标识识符符,该该文文件件又又被被另另一一个个文文件件包包含含,则则该该标标识识符符的的作作用用域域延延伸伸到到新新的的文文件件中中。如如cincin和和coutcout是是在在头头文文件件iostream.hiostream.h中中说说明明的的具具有有文文件件作作用用域域的的标标识识符符,它它们们的的作作用用域域也也延延伸伸到到嵌入嵌入iostream.hiostream.h的文件中。的文件中。函数是C程序的基本模块可将一些功能相对独立的或经常存存储储类类型型决决定定了了变变量量的的生生命命期期,变变量量生生命命期期指指从从
46、获得空间到空间释放之间的时期。获得空间到空间释放之间的时期。4.5.2 变量的存储类型变量的存储类型 存存储储类类型型的的说说明明符符有有四四个个:auto, register, static和和extern。前前两两者者称称为为自自动动类类型型,后后两两者者分分别别为为静静态态和和外外部类型。部类型。本本节节重重点点掌掌握握static和和extern这这两两种种类类型型的的使使用用和和区区别别。具具体体说说,区区分分局局部部变变量量和和静静态态局局部部变变量量,全全局局变量和静态全局变量变量和静态全局变量。函数是C程序的基本模块可将一些功能相对独立的或经常auto:前前面面提提到到的的局局
47、部部变变量量都都是是自自动动类类型型。其其空空间间分分配配于于块块始始,空空间间释释放放于于块块终终,且且由由系系统统自自动动进进行行。自自动动变变量量保保存存在在栈栈中中,且且是是在在程程序序运运行行过过程程中中获获得得和和释放空间,未初始化时值为随机数。释放空间,未初始化时值为随机数。4.5.2 变量的存储类型变量的存储类型 register:为为提提高高程程序序运运行行效效率率,可可以以将将某某些些变变量量保保存存在在寄存器中,即说明为寄存器变量,但不提倡使用。寄存器中,即说明为寄存器变量,但不提倡使用。static:静静态态变变量量。根根据据被被修修饰饰变变量量的的位位置置不不同同,分
48、分为为局局部部(内内部部)静静态态变变量量和和全全局局(外外部部)静静态态变变量量。所所有有静静态态变变量量均均存存放放在在全全局局数数据据区区,编编译译时时获获得得存存储储空间,未初始化时自动全空间,未初始化时自动全0 0,且只初始化一次。,且只初始化一次。函数是C程序的基本模块可将一些功能相对独立的或经常局局部部静静态态变变量量的的作作用用域域为为块块域域,但但生生命命期期为为整整个个文文件件。即即当当块块结结束束时时,局局部部静静态态变变量量空空间间仍仍然然保保持持,直直到到整整个个程程序序文文件件结结束束时时该该局局部部静静态态变变量量空空间间才才释释放放,生命期结束。生命期结束。局部
49、静态变量局部静态变量 【例例410】自自动动变变量量与与局局部部静静态态变变量量的的区区别别。(演演示)示)函数是C程序的基本模块可将一些功能相对独立的或经常#includest()staticintt=100;/局部静态变量局部静态变量t+;returnt;at()intt=100;/自动变量自动变量t+;returnt;voidmain()inti;for(i=0;i5;i+)coutat()t;coutendl;for(i=0;i5;i+)coutst()t;coutendl;4.5.2 变量的存储类型变量的存储类型 i= 0t= 10012 34 5101101101101101函数是
50、C程序的基本模块可将一些功能相对独立的或经常4.5.2 变量的存储类型变量的存储类型 i= 0t=1001 21013 4 5102103104105#includest()staticintt=100;/局部静态变量局部静态变量t+;returnt;at()intt=100;/自动变量自动变量t+;returnt;voidmain()inti;for(i=0;i5;i+)coutat()t;coutendl;for(i=0;i5;i+)coutst()t;coutendl;函数是C程序的基本模块可将一些功能相对独立的或经常全局静态变量全局静态变量全全局局静静态态变变量量是是指指用用stati
51、c修修饰饰的的全全局局变量。有关内容在下节静态存储类型中介绍。变量。有关内容在下节静态存储类型中介绍。函数是C程序的基本模块可将一些功能相对独立的或经常4.5.3 外部存储类型与静态存储类型外部存储类型与静态存储类型1.外部存储类型外部存储类型2.静静态态存存储储类类型型一个一个C+C+程序可以由多个源程序文件组成,编译系统程序可以由多个源程序文件组成,编译系统将这若干个文件连接在一起,产生可执行程序。外部将这若干个文件连接在一起,产生可执行程序。外部存储类型和静态存储类型确定了变量和函数在多文件存储类型和静态存储类型确定了变量和函数在多文件程序中的联络关系。程序中的联络关系。 函数是C程序的
52、基本模块可将一些功能相对独立的或经常1 外部存储类型外部存储类型 外外部部存存储储类类型型包包括括外外部部变变量量和和外外部部函函数数。在在由由多多个个源源程程序序文文件件组组成成的的程程序序中中,如如果果一一个个文文件件要要使使用用另另一一个个文文件件中中定定义义的的全全局局变变量量或或函函数数,这这些些源源程程序序文文件件之之间间通通过过外外部部类类型型的变量和函数进行沟通。的变量和函数进行沟通。在一个文件中定义的全局变量和函数都缺省为外部的,即在一个文件中定义的全局变量和函数都缺省为外部的,即其作用域可以延伸到程序的其他文件中。但其他文件如果其作用域可以延伸到程序的其他文件中。但其他文件
53、如果要使用这个文件中定义的全局变量和函数,必须在使用前要使用这个文件中定义的全局变量和函数,必须在使用前用用“extern”“extern”作外部声明,外部声明通常放在文件的开头。作外部声明,外部声明通常放在文件的开头。变量定义时编译器为其分配存储空间,而变量声明指明该变量定义时编译器为其分配存储空间,而变量声明指明该全局变量已在其他地方说明过,编译系统不再分配存储空全局变量已在其他地方说明过,编译系统不再分配存储空间,直接使用变量定义时所分配的空间。间,直接使用变量定义时所分配的空间。函数声明缺省为外部的,因此修饰词函数声明缺省为外部的,因此修饰词externextern通常省略。通常省略。
54、 函数是C程序的基本模块可将一些功能相对独立的或经常1 外部存储类型外部存储类型 【例【例4.11】外部存储类型的例子。假定程序包含两个源程序文件】外部存储类型的例子。假定程序包含两个源程序文件Ex4_11_1.cpp和和Ex4_11_2.cpp,程序结构如下:,程序结构如下:/*Ex4_11_1.cpp,由,由main()组成组成*/#includevoidfun2();/外部函数声明,等价于外部函数声明,等价于externvoidfun2();intn;/全局变量定义全局变量定义voidmain()n=1;fun2();/fun2()定义在文件定义在文件Ex4_11_2.cpp中中cout
55、n=nendl;/*Ex4_11_2.cpp,由,由fun2()组成组成*/externintn;/外部变量声明,外部变量声明,n定义在文件定义在文件Ex4_11_1.cpp中中voidfun2()/fun2()被文件被文件Ex4_11_1.cpp中的函数调用中的函数调用n=3;运行结果:运行结果:n=3函数是C程序的基本模块可将一些功能相对独立的或经常2 静态存储类型静态存储类型 静态存储类型包括静态全局变量和静态函数。在定义全局静态存储类型包括静态全局变量和静态函数。在定义全局变量或函数时加说明符变量或函数时加说明符staticstatic,就成为静态变量或静态函,就成为静态变量或静态函数
56、。静态存储类型的作用域与外部存储类型相反,一旦定数。静态存储类型的作用域与外部存储类型相反,一旦定义为静态存储类型,就限制该变量或函数只能在定义它的义为静态存储类型,就限制该变量或函数只能在定义它的文件中使用。静态全局变量在编译时分配存储空间,如果文件中使用。静态全局变量在编译时分配存储空间,如果定义时不指定初值,则编译系统将其初始化为全定义时不指定初值,则编译系统将其初始化为全0 0。一个全局变量和一个静态全局变量在使用上是不同的,其一个全局变量和一个静态全局变量在使用上是不同的,其他文件通过外部变量声明可以使用一个全局变量,但却无他文件通过外部变量声明可以使用一个全局变量,但却无法使用静态
57、全局变量,法使用静态全局变量,静态全局变量只能被定义它的文件静态全局变量只能被定义它的文件所独享所独享。函数与静态函数之间的区别是相同的。函数与静态函数之间的区别是相同的。函数是C程序的基本模块可将一些功能相对独立的或经常4.5.4 生命期与可见性生命期与可见性1.生命期生命期2.可见性可见性函数是C程序的基本模块可将一些功能相对独立的或经常 1 生命期生命期 (1 1)静态生命期)静态生命期)静态生命期)静态生命期 (2 2)局部生命期)局部生命期)局部生命期)局部生命期 (3 3)动态生命期)动态生命期)动态生命期)动态生命期 生命期(生命期(Life timeLife time)也叫生存
58、期。生命期与存储区域相关,)也叫生存期。生命期与存储区域相关,存储区域分为代码区、静态数据区、栈区和堆区,相应地,存储区域分为代码区、静态数据区、栈区和堆区,相应地,生命期分为生命期分为静态生命期、局部生命期和动态生命期静态生命期、局部生命期和动态生命期。函数是C程序的基本模块可将一些功能相对独立的或经常(1)静态生命期静态生命期 静静态态生生命命期期指指的的是是标标识识符符从从程程序序开开始始运运行行时时存存在在,即即具具有有存存储储空空间间,到到程程序序运运行行结结束束时时消消亡亡,即即释释放放存存储储空空间间。具具有有静静态态生生命命期期的的标标识识符符存存放放在在静静态态数数据据区区,
59、属属于于静静态态存存储储类类型型,如如全全局局变变量量、静静态态全全局局变变量量、静静态态局局部部变变量量。具具有有静静态态生生命命期期的的标标识识符符在在未未被被用用户户初初始始化化的的情情况况下下,系系统统会会自自动将其初始化为全动将其初始化为全0 0。 函函数数驻驻留留在在代代码码区区,也也具具有有静静态态生生命命期期。所所有具有文件作用域的标识符都具有静态生命期。有具有文件作用域的标识符都具有静态生命期。函数是C程序的基本模块可将一些功能相对独立的或经常(2)局部生命期局部生命期 在在函函数数内内部部或或块块中中定定义义的的标标识识符符具具有有局局部部生生命命期期,其其生生命命期期开开
60、始始于于执执行行到到该该函函数数或或块块的的标标识识符符声声明明处处,结结束束于于该该函函数数或或块块的的结结束束处处。具具有有静静态态生生命命期期的的标标识识符符存存放放在在栈栈区区。具具有有局局部部生生命命期期的的标标识识符符如果未被初始化,其内容是随机的,不可用如果未被初始化,其内容是随机的,不可用。 具具有有局局部部生生命命期期的的标标识识符符必必定定具具有有局局部部作作用用域域;但但反反之之不不然然,静静态态局局部部变变量量具具有有局局部部作作用用域域,但但却却具有静态生命期具有静态生命期。函数是C程序的基本模块可将一些功能相对独立的或经常(3)动态生命期动态生命期 具有动态生命期的
61、标识符由特定的函数调具有动态生命期的标识符由特定的函数调用或运算来创建和释放用或运算来创建和释放,如调用,如调用malloc()malloc()或用或用newnew运算符为变量分配存储空间时,变量的生命运算符为变量分配存储空间时,变量的生命期开始,而调用期开始,而调用free()free()或用或用deletedelete运算符释放运算符释放空间或程序结束时,变量生命期结束。具有动空间或程序结束时,变量生命期结束。具有动态生命期的变量存放在堆区。关于态生命期的变量存放在堆区。关于newnew运算和运算和deletedelete运算将在指针一章中介绍。运算将在指针一章中介绍。 函数是C程序的基本
62、模块可将一些功能相对独立的或经常2可见性可见性 可见性可见性从另一个角度说明标识符的有效性,可见性与作从另一个角度说明标识符的有效性,可见性与作用域具有一定的一致性。用域具有一定的一致性。标识符的作用域包含可见范围,标识符的作用域包含可见范围,可见范围不会超过作用域可见范围不会超过作用域。可见性在理解同名标识符的。可见性在理解同名标识符的作用域嵌套作用域嵌套时十分直观。对于外层块与内层块定义了同时十分直观。对于外层块与内层块定义了同名标识符的,在外层作用域中,内层所定义的标识符是名标识符的,在外层作用域中,内层所定义的标识符是不可见的,即外层引用的是外层所定义的标识符;同样,不可见的,即外层引
63、用的是外层所定义的标识符;同样,在内层作用域中,外层的标识符将被内层的同名标识符在内层作用域中,外层的标识符将被内层的同名标识符屏蔽,变得不可见屏蔽,变得不可见,即外层中同名标识符的可见范围为,即外层中同名标识符的可见范围为作用域中挖去内层块的范围。图作用域中挖去内层块的范围。图4.64.6显示下面程序段中显示下面程序段中变量的作用域与可见性。变量的作用域与可见性。函数是C程序的基本模块可将一些功能相对独立的或经常2可见性可见性 下下面面的的程程序序段段和和图图示示显显示示作作用用域域与与可见性。可见性。intm=1;floatx;floatm=3.5;X=5.5;m+; int m, flo
64、at x作用域作用域int m可见可见float m不可见不可见x可见可见float m作用域作用域float m可见可见int m不可见不可见x可见可见函数是C程序的基本模块可将一些功能相对独立的或经常4.6 函数的递归调用函数的递归调用 递递归归是是一一种种描描述述问问题题的的方方法法,或或称称算算法法。递递归归的的思思想想可可以以简简单单地地描描述述为为“自自己己调调用用自自己己”。例例如用如下方法定义阶乘:如用如下方法定义阶乘:可以看出是用阶乘定义阶乘,这种自己定义自己的方法称为可以看出是用阶乘定义阶乘,这种自己定义自己的方法称为递归定义。递归定义。函数是C程序的基本模块可将一些功能相
65、对独立的或经常在函数调用中,有这样两种情况,一种是在函数在函数调用中,有这样两种情况,一种是在函数A的定的定义中有调用函数义中有调用函数A的语句,即自己调用自己;另一种是函数的语句,即自己调用自己;另一种是函数A的定义中出现调用函数的定义中出现调用函数B的语句,而函数的语句,而函数B的定义中也出的定义中也出现调用函数现调用函数A的语句,即相互调用。前者称的语句,即相互调用。前者称直接递归直接递归,后者,后者称称间接递归间接递归。本节只介绍直接递归。递归函数必须定义递。本节只介绍直接递归。递归函数必须定义递归归终止条件终止条件(Stoppingcondition),避免无穷递归),避免无穷递归(
66、InfiniteRecursion)。)。递归定义的阶乘算法用函数描述为:递归定义的阶乘算法用函数描述为:fac(intn)if(n=0|n=1)return1;elsereturnn*fac(n-1);只要设计主函数调用阶乘函数,即可实现计算阶乘。只要设计主函数调用阶乘函数,即可实现计算阶乘。4.6 函数的递归调用函数的递归调用函数是C程序的基本模块可将一些功能相对独立的或经常【例【例412】求求4!#includeintfac(intn)inty;coutnt;if(n=0|n=1)y=1;elsey=n*fac(n-1);coutyt;returny;voidmain()coutn4!=
67、fac(4)endl; n=4cout4;y=4*fac(3);fac(4)=cout2;y=2*fac(1); n=2cout1;y=1;cout1;return 1; n=1 n=3cout3;y=3*fac(2);cout24;return 24;cout6;return 6;cout2;return 2;24函数是C程序的基本模块可将一些功能相对独立的或经常递归函数的执行分为递归函数的执行分为“递推递推”和和“回归回归”两个过程,两个过程,这两个过程由递归终止条件控制,即这两个过程由递归终止条件控制,即逐层递推逐层递推,直,直至至递归终止条件递归终止条件,然后,然后逐层回归逐层回归。每
68、次调用发生时。每次调用发生时都首先判断递归终止条件。递归调用同普通的函数都首先判断递归终止条件。递归调用同普通的函数调用一样,每当调用发生时,在栈中分配单元保存调用一样,每当调用发生时,在栈中分配单元保存返回地址以及参数和局部变量;而与普通的函数调返回地址以及参数和局部变量;而与普通的函数调用不同的是,由于递推的过程是一个逐层调用的过用不同的是,由于递推的过程是一个逐层调用的过程,因此存在一个逐层连续的参数入栈过程,直至程,因此存在一个逐层连续的参数入栈过程,直至遇到递归终止条件时,才开始回归,这时才逐层释遇到递归终止条件时,才开始回归,这时才逐层释放栈空间,返回到上一层,直至最后返回到主调函
69、放栈空间,返回到上一层,直至最后返回到主调函数。数。 4.6 4.6 函数的递归调用函数的递归调用函数是C程序的基本模块可将一些功能相对独立的或经常4.6 函数的递归调用函数的递归调用【例例413】汉汉诺诺塔塔问问题题。有有A、B、C三三根根柱柱子子,A柱柱上上有有n个个大大小小不不等等的的盘盘子子,大大盘盘在在下下,小小盘盘在在上上。要要求求将将所所有有盘盘子子由由A柱柱搬搬动动到到C柱柱上上,每每次次只只能能搬搬动动一一个个盘盘子子,搬搬动动过过程程中中可可以以借借助助任任何何一一根根柱柱子子,但但必必须须满满足足大大盘盘在在下,小盘在上。打印出搬动的步骤。下,小盘在上。打印出搬动的步骤。
70、A柱柱B柱柱C柱柱函数是C程序的基本模块可将一些功能相对独立的或经常分析:分析:1 A1 A柱只有一个盘子的情况:柱只有一个盘子的情况: A A柱柱C C柱;柱;2 2 A A柱柱有有两两个个盘盘子子的的情情况况:小小盘盘A A柱柱B B柱柱,大大盘盘A A柱柱C C柱柱,小盘小盘B B柱柱C C柱。柱。3 3 A A柱柱有有n n个个盘盘子子的的情情况况:将将此此问问题题看看成成上上面面n-1n-1个个盘盘子子和和最最下下面面第第n n个个盘盘子子的的情情况况。n-1n-1个个盘盘子子A A柱柱B B柱柱,第第n n个个盘盘子子A A柱柱C C柱柱,n-1n-1个个盘盘子子B B柱柱C C柱
71、柱。问问题题转转化化成成搬搬动动n-1n-1个个盘盘子子的的问问题题,同同样样,将将n-1n-1个个盘盘子子看看成成上上面面n-2n-2个个盘盘子子和和下下面面第第n-1n-1个个盘盘子子的的情情况况,进进一一步步转转化化为为搬搬动动n-2n-2个个盘盘子子的的问问题题,类类推推下下去去,一一直直到到最最后后成成为为搬搬动动一一个个盘子的问题。盘子的问题。这是一个典型的递归问题,递归结束于只搬动一个盘子。这是一个典型的递归问题,递归结束于只搬动一个盘子。4.6 4.6 函数的递归调用函数的递归调用函数是C程序的基本模块可将一些功能相对独立的或经常4.6 函数的递归调用函数的递归调用算法可以描述
72、为:算法可以描述为:1n-1个盘子个盘子A柱柱B柱,借助于柱,借助于C柱;柱;2第第n个盘子个盘子A柱柱C柱;柱;3n-1个盘子个盘子B柱柱C柱,借助于柱,借助于A柱;柱;其中步骤其中步骤1和步骤和步骤3继续递归下去,直至搬动一个盘继续递归下去,直至搬动一个盘子为止。由此,可以定义两个函数,一个是递归函子为止。由此,可以定义两个函数,一个是递归函数,命名为数,命名为hanoi(intn,charsource,chartemp,chartarget),实现将,实现将n个盘子从源柱个盘子从源柱source借助中间柱借助中间柱temp搬到目标柱搬到目标柱target;另一;另一个命名为个命名为mov
73、e(charsource,chartarget),用,用来输出搬动一个盘子的提示信息。来输出搬动一个盘子的提示信息。函数是C程序的基本模块可将一些功能相对独立的或经常程序如下:程序如下:#includevoidmove(charsource,chartarget)coutsourcetargetendl;voidhanoi(intn,charsource,chartemp,chartarget)if(n=1)move(source,target);elsehanoi(n-1,source,target,temp);/将将n-1个盘子搬到中间柱个盘子搬到中间柱move(source,target
74、);/将最后一个盘子搬到目标柱将最后一个盘子搬到目标柱hanoi(n-1,temp,source,target);/将将n-1个盘子搬到目标柱个盘子搬到目标柱voidmain()intn;cout输入盘子数:输入盘子数:n;hanoi(n,A,B,C);函数是C程序的基本模块可将一些功能相对独立的或经常【例【例414】输入一个整数,用递归算法将整数倒序输出。输入一个整数,用递归算法将整数倒序输出。分析:分析:在递归过程的递推步骤中用求余运算将整数的各个位分在递归过程的递推步骤中用求余运算将整数的各个位分离,并打印出来。离,并打印出来。#includevoidbackward(intn)cout
75、n%10;if(n10)return;elsebackward(n/10);voidmain()intn;cout输入整数:输入整数:n;cout原整数:原整数:nendl反向数:反向数:;backward(n);coutendl;函数是C程序的基本模块可将一些功能相对独立的或经常4.6 函数的递归调用函数的递归调用n=247cout7;backward(24);n=2cout2;return;n=24cout4;backward(2);backward(247) return; return; coutendl; 求余总是取当前整数的最右一位,所以先输出余数后递归求余总是取当前整数的最右一位
76、,所以先输出余数后递归可实现倒序输出。如果先递归后输出余数,则是在回归的过程可实现倒序输出。如果先递归后输出余数,则是在回归的过程中输出,实现的就是正序输出。中输出,实现的就是正序输出。从以上几例可以看出,递归算法一般不需要借助循环,但通从以上几例可以看出,递归算法一般不需要借助循环,但通过不断过不断递推递推和和回归回归的过程实现了其他算法用循环完成的功能。的过程实现了其他算法用循环完成的功能。因此,递归的终止条件非常重要,否则将会无休止地递归下因此,递归的终止条件非常重要,否则将会无休止地递归下去,陷入死循环状态。去,陷入死循环状态。函数是C程序的基本模块可将一些功能相对独立的或经常【例例4
77、.15】在【例】在【例3.11】中采用递推法求解】中采用递推法求解Fibonacii数列,本例数列,本例用递归法求解。本例的递归调用过程参见图用递归法求解。本例的递归调用过程参见图4.11。#include#includeintfib(intn)if(n=0)return0;elseif(n=1)return1;elsereturnfib(n-1)+fib(n-2);voidmain()for(inti=0;i=19;i+)/将将19改为改为69,可以看出计算到后面越来越缓慢。,可以看出计算到后面越来越缓慢。if(i%5=0)coutendl;coutsetw(6)fib(i);coutend
78、l;4.6 函数的递归调用函数的递归调用函数是C程序的基本模块可将一些功能相对独立的或经常图图4.11 递归求解斐波那契数列调用树递归求解斐波那契数列调用树同其他算法相比,用递归算法编制的程序同其他算法相比,用递归算法编制的程序非常简洁易读非常简洁易读,但,但缺点是增加了内存的开销,在递推的过程中会缺点是增加了内存的开销,在递推的过程中会占用大量栈空占用大量栈空间间,且连续的调用返回操作,且连续的调用返回操作占用较多占用较多CPU时间时间。因此是否选。因此是否选择使用递归算法取决于所解决的问题及应用的场合。择使用递归算法取决于所解决的问题及应用的场合。4.6 函数的递归调用函数的递归调用函数是
79、C程序的基本模块可将一些功能相对独立的或经常4.7 函数的一些高级议题函数的一些高级议题 471 函数重载函数重载 472缺省变元缺省变元 473内联函数内联函数 函数是C程序的基本模块可将一些功能相对独立的或经常4.7.1 函数重载函数重载 在在C+中,如果需要定义几个功能相似,而参数类型不同的函数,那中,如果需要定义几个功能相似,而参数类型不同的函数,那么这样的几个函数可以使用相同的函数名,这就是么这样的几个函数可以使用相同的函数名,这就是函数重载函数重载。例如求。例如求和函数,对应不同的参数类型,可以定义如下几个重载函数:和函数,对应不同的参数类型,可以定义如下几个重载函数:sum(in
80、ta,intb)/不写返回类型,返回整型不写返回类型,返回整型doublesum(doublea,doubleb)floatsum(floata,floatb,floatc)当某个函数中调用到重载函数时,编译器会根据实参的类型去对应地当某个函数中调用到重载函数时,编译器会根据实参的类型去对应地调用相应的函数。匹配过程按如下步骤进行:调用相应的函数。匹配过程按如下步骤进行:(1)如果有严格匹配的函数,就调用该函数;)如果有严格匹配的函数,就调用该函数;(2)参数内部转换后如果匹配,调用该函数;)参数内部转换后如果匹配,调用该函数;(3)通过用户定义的转换寻求匹配。)通过用户定义的转换寻求匹配。因
81、此在定义重载函数时必须保证参数类型不同,仅仅返回值类型不同因此在定义重载函数时必须保证参数类型不同,仅仅返回值类型不同是不行的。函数重载的好处在于,可以用相同的函数名来定义一组功是不行的。函数重载的好处在于,可以用相同的函数名来定义一组功能相同或类似的函数,程序的可读性增强。能相同或类似的函数,程序的可读性增强。函数是C程序的基本模块可将一些功能相对独立的或经常4.7.1 函数重载函数重载 3+5=调用调用sum(3,5)函数函数sum(3,5)return82.2+5.6=调用调用sum(2.2,5.6)函数函数doublesum(2.2,5.6)return7.83.5+4+8=调用调用s
82、um(3.5,4,8)函数函数floatsum(3.5,4,8)return15.5结束结束87.815.5【例【例416】重载函数的应用。重载函数的应用。#includesum(inta,intb)returna+b;Doublesum(doublea,doubleb)returna+b;floatsum(floata,floatb,floatc)returna+b+c;voidmain()cout3+5=sum(3,5)endl;cout2.2+5.6=“sum(2.2,5.6)endl;cout3.5+4+8=“sum(3.5,4,8)endl;函数是C程序的基本模块可将一些功能相对独立
83、的或经常 一般情况下,函数调用时的实参个数应与形参相同,一般情况下,函数调用时的实参个数应与形参相同,但为了更方便地使用函数,但为了更方便地使用函数,C+C+也允许定义具有缺省参数的也允许定义具有缺省参数的函数,这种函数调用时实参个数可以与形参不相同。函数,这种函数调用时实参个数可以与形参不相同。 缺省参数指在定义函数时为形参指定缺省值(默认值)缺省参数指在定义函数时为形参指定缺省值(默认值)。这样的函数在调用时,对于缺省参数,可以给出实参值,也这样的函数在调用时,对于缺省参数,可以给出实参值,也可以不给出参数值。如果给出实参,将实参传递给形参进行可以不给出参数值。如果给出实参,将实参传递给形
84、参进行调用,如果不给出实参,则按缺省值进行调用。调用,如果不给出实参,则按缺省值进行调用。 缺省参数的函数调用:缺省参数的缺省参数的函数调用:缺省参数的值值或或表达方式表达方式在编译在编译时确定,即其值通过时确定,即其值通过表达式表达式给出,但表达式必须有意义;缺给出,但表达式必须有意义;缺省值还可以是全局常量、全局变量,甚至可以省值还可以是全局常量、全局变量,甚至可以通过函数调用通过函数调用给出给出;但不能通过局部变量给出,因为局部变量的值在执行;但不能通过局部变量给出,因为局部变量的值在执行时才有意义。时才有意义。4.7.2 缺省变元缺省变元 函数是C程序的基本模块可将一些功能相对独立的或
85、经常【例【例420】缺省变元。缺省变元。/文件名:文件名:Ex4_20.cpp#includevoiddelay(intloops=5)/延时函数延时函数,默认延时默认延时5个时间单位个时间单位for(;loops0;loops-);voidmain()delay(3);cout“延时延时3个时间单位个时间单位endl;delay();/等同于等同于delay(5)cout“延时延时5个时间单位个时间单位endl;4.7.2 缺省变元缺省变元 123延时延时3个时间单位个时间单位12345延时延时5个时间单位个时间单位函数是C程序的基本模块可将一些功能相对独立的或经常缺省参数通过表达式给出,所
86、以可以使用缺省参数通过表达式给出,所以可以使用函数调用函数调用,如:,如:intfun1(inta=rand();参数参数a缺省时,可由随机数发生函数当场产生,缺省时,可由随机数发生函数当场产生,编译时定的是编译时定的是调什么函数调什么函数。缺省参数可以有多个,但所有缺省参数必须放在参数表的右缺省参数可以有多个,但所有缺省参数必须放在参数表的右侧,即先定义所有的非缺省参数,再定义缺省参数。这是因为侧,即先定义所有的非缺省参数,再定义缺省参数。这是因为在函数调用时,在函数调用时,参数自左向右逐个匹配参数自左向右逐个匹配,当实参和形参个数不,当实参和形参个数不一致时只有这样才不会产生二义性。一致时
87、只有这样才不会产生二义性。一个参数只能在一个文件被指定一次缺省实参一个参数只能在一个文件被指定一次缺省实参,习惯上,缺,习惯上,缺省参数在公共头文件包含的函数声明中指定,否则缺省实参只省参数在公共头文件包含的函数声明中指定,否则缺省实参只能用于包含该函数定义的文件中的函数调用。例如:能用于包含该函数定义的文件中的函数调用。例如:intfun2(int,int=10,int=20);/函数原型中给出缺省值。参数名也可省略函数原型中给出缺省值。参数名也可省略voidfun1()intfun2(inta,intb,intc)/定义中不再给出缺省值定义中不再给出缺省值4.7.2 缺省变元缺省变元 函数
88、是C程序的基本模块可将一些功能相对独立的或经常4.7.3 内联函数内联函数 当程序执行函数调用时,系统要建立栈空间,保护现场,传当程序执行函数调用时,系统要建立栈空间,保护现场,传递参数以及控制程序执行的转移等等,这些工作需要系统时递参数以及控制程序执行的转移等等,这些工作需要系统时间和空间的开销。有些情况下,函数本身间和空间的开销。有些情况下,函数本身功能简单功能简单,代码很,代码很短,但使用短,但使用频率频率却很却很高高,程序频繁调用该函数所花费的时间程序频繁调用该函数所花费的时间却很多,从而使得程序执行效率降低。为了提高效率,一个却很多,从而使得程序执行效率降低。为了提高效率,一个解决办
89、法就是不使用函数,直接将函数的代码嵌入到程序中。解决办法就是不使用函数,直接将函数的代码嵌入到程序中。但这个办法也有缺点,一是相同代码重复书写,二是程序可但这个办法也有缺点,一是相同代码重复书写,二是程序可读性往往没有使用函数的好。读性往往没有使用函数的好。为了协调好效率和可读性之间为了协调好效率和可读性之间的矛盾的矛盾,C+提供了另一种方法,即定义提供了另一种方法,即定义内联函数内联函数,方法是,方法是在定义函数时用修饰词在定义函数时用修饰词inline。函数是C程序的基本模块可将一些功能相对独立的或经常4.7.3 内联函数内联函数 请看如下程序段,读入一行字符串,逐个判断是否为数字字符:请
90、看如下程序段,读入一行字符串,逐个判断是否为数字字符:#includeinlineIsNumber(charch)returnch=0&ch=9?1:0;voidmain()charch;while(cin.get(ch),ch!=n)if(IsNumber(ch)cout是数字字符是数字字符endl;elsecout不是数字字符不是数字字符endl;因使用频度很高,说明为内联函数。因使用频度很高,说明为内联函数。函数是C程序的基本模块可将一些功能相对独立的或经常4.8 C+的系统库函数的系统库函数 C+提供了一个很大的常用函数库,该函数库本身并不是提供了一个很大的常用函数库,该函数库本身并不
91、是C+语言的组成部分,所有库中的函数用户都可以自己定义,语言的组成部分,所有库中的函数用户都可以自己定义,但直接使用库函数能给编程带来很大方便。但直接使用库函数能给编程带来很大方便。系统函数库实际系统函数库实际上是一系列源程序文件上是一系列源程序文件,每个文件中定义了若干常用函数及,每个文件中定义了若干常用函数及标识符,具有相同或相似功能的函数和标识符集中放在一个标识符,具有相同或相似功能的函数和标识符集中放在一个文件中。这些文件均以文件中。这些文件均以.h的形式命名,存放在系统目录的的形式命名,存放在系统目录的include子目录子目录下。例如文件下。例如文件iostream.h中定义了与控
92、制台中定义了与控制台输入输出和文件输入输出相关对象和成员函数,输入输出和文件输入输出相关对象和成员函数,math.h中中定义了大量数学函数,定义了大量数学函数,string.h中定义了大量与字符串操作中定义了大量与字符串操作相关的函数。相关的函数。 函数是C程序的基本模块可将一些功能相对独立的或经常49 头文件与多文件结构头文件与多文件结构 491 头文件头文件 在将一个程序分解成若干个文件时,需要考虑标识符在其他文件中的在将一个程序分解成若干个文件时,需要考虑标识符在其他文件中的可见性。使用头文件是一个很有效的方法。如:可见性。使用头文件是一个很有效的方法。如:#include其中的其中的i
93、ostream.h是系统定义的一个文件,这种以是系统定义的一个文件,这种以“.h”命名的文件称为命名的文件称为头文件,头文件,系统定义的头文件系统定义的头文件中定义了一些常用的公用标识符和函数,用户中定义了一些常用的公用标识符和函数,用户只要将头文件包含进自己的文件,就可使头文件中定义的标识符在用户文只要将头文件包含进自己的文件,就可使头文件中定义的标识符在用户文件中变得可见,也就可以直接使用头文件中定义的标识符和函数。件中变得可见,也就可以直接使用头文件中定义的标识符和函数。除了系统定义的头文件外,用户还可以除了系统定义的头文件外,用户还可以自定义头文件自定义头文件。对于具有外部。对于具有外
94、部存储类型的标识符,可以在其他任何一个源程序文件中经声明后引用,因存储类型的标识符,可以在其他任何一个源程序文件中经声明后引用,因此用户完全可以将一些具有外部存储类型的标识符的声明放在一个头文件此用户完全可以将一些具有外部存储类型的标识符的声明放在一个头文件中。具体地说,头文件中可以包括:用户构造的数据类型(如枚举类型),中。具体地说,头文件中可以包括:用户构造的数据类型(如枚举类型),外部变量,外部函数、常量和内联函数等具有一定通用性或常用的量,而外部变量,外部函数、常量和内联函数等具有一定通用性或常用的量,而一般性的变量和函数定义不宜放在头文件中。一般性的变量和函数定义不宜放在头文件中。函
95、数是C程序的基本模块可将一些功能相对独立的或经常492 多文件结构多文件结构 在开发较大程序时,通常将其分解为多个源程序文件,在开发较大程序时,通常将其分解为多个源程序文件,每个较小的程序用一个源程序文件建立。程序经过建立、每个较小的程序用一个源程序文件建立。程序经过建立、编译、连接,成为一个完整的可执行程序。编译、连接,成为一个完整的可执行程序。多文件结构通多文件结构通过工程进行管理,在工程中建立若干用户定义的头文件过工程进行管理,在工程中建立若干用户定义的头文件.h和源程序文件和源程序文件.cpp。头文件中定义用户自定义的数据类型,。头文件中定义用户自定义的数据类型,所有的程序实现则放在不
96、同的源程序文件中所有的程序实现则放在不同的源程序文件中。编译时每个。编译时每个源程序文件单独编译,如果源程序文件中有编译预处理指源程序文件单独编译,如果源程序文件中有编译预处理指令,则首先经过编译预处理生成临时文件存放在内存,之令,则首先经过编译预处理生成临时文件存放在内存,之后对临时文件进行编译生成目标文件后对临时文件进行编译生成目标文件.obj,编译后临时文,编译后临时文件撤销。所有的目标文件经连接器连接最终生成一个完整件撤销。所有的目标文件经连接器连接最终生成一个完整的可执行文件的可执行文件.exe。图图4.12是一个多文件系统的开发过程。是一个多文件系统的开发过程。函数是C程序的基本模
97、块可将一些功能相对独立的或经常492 多文件结构多文件结构 编译 预编译编译 预编译 预编译编译图图4.6C+程序开发过程程序开发过程临时文件临时文件1临时文件临时文件2临时文件临时文件n.libC+标准类库标准类库连接连接运行运行函数是C程序的基本模块可将一些功能相对独立的或经常4.10 编译预处理编译预处理 4101宏定义指令宏定义指令4102文件包含指令文件包含指令 4103条件编译指令条件编译指令 函数是C程序的基本模块可将一些功能相对独立的或经常4.10.1 宏定义指令宏定义指令#define 1 1 不带参宏定义不带参宏定义用来产生与一个字符串对应的常量字符串,格式为:用来产生与一
98、个字符串对应的常量字符串,格式为:#define #define 宏名宏名 常量串常量串 预处理后文件中凡出现该字符串处均用其对应的常量预处理后文件中凡出现该字符串处均用其对应的常量串代替。替换过程称为宏替换或宏展开。例如,如果串代替。替换过程称为宏替换或宏展开。例如,如果使用指令使用指令#define PI 3.1415926#define PI 3.1415926则程序中可以使用标识符则程序中可以使用标识符PIPI,编译预处理后产生一个,编译预处理后产生一个中间文件,文件中所有中间文件,文件中所有PIPI被替换为被替换为3.14159263.1415926。宏替换只是字符串和标识符之间的简
99、单替换,预处理宏替换只是字符串和标识符之间的简单替换,预处理本身不做任何数据类型和合法性检查,也不分配内存本身不做任何数据类型和合法性检查,也不分配内存单元单元。函数是C程序的基本模块可将一些功能相对独立的或经常4.10.1 宏定义指令宏定义指令#define 2带参数的宏定义带参数的宏定义带参宏定义的形式很象定义一个函数,格式为:带参宏定义的形式很象定义一个函数,格式为:#define宏名宏名(形参表形参表)表达式串表达式串例如作如下宏定义:例如作如下宏定义:#defineS(a,b)(a)*(b)/2程序中可使用程序中可使用S(a,b),预处理后产生中间文件,其中,预处理后产生中间文件,其
100、中S(a,b)被被替换成替换成(a)*(b)/2。注意,宏定义时形参通常要用括号括起来,。注意,宏定义时形参通常要用括号括起来,否则容易导致逻辑错误。例如,如果定义:否则容易导致逻辑错误。例如,如果定义:#defineS(a,b)a*b/2那么程序中的那么程序中的S(3+5,4+2)就会被宏展开为就会被宏展开为3+5*4+2/2,不,不符合定义的真正的意图。符合定义的真正的意图。带参宏定义形式上象定义函数,但它与函数的本质不同,宏定带参宏定义形式上象定义函数,但它与函数的本质不同,宏定义仍然只是产生字符串替代,不存在分配内存和参数传递。义仍然只是产生字符串替代,不存在分配内存和参数传递。函数是
101、C程序的基本模块可将一些功能相对独立的或经常4.10.2 文件包含(嵌入)指令文件包含(嵌入)指令#include 文文件件包包含含用用#include指指令令,预预处处理理后后将将指指令令中中指指明明的的源源程程序序文件嵌入到当前源程序文件的指令位置处。格式为:文件嵌入到当前源程序文件的指令位置处。格式为:#include或或#include文件名文件名第第一一种种方方式式称称为为标标准准方方式式,预预处处理理器器将将在在include子子目目录录下下搜搜索索由由文文件件名名所所指指明明的的文文件件。这这种种方方式式适适用用于于嵌嵌入入C+提提供供的的头头文文件件,因因为为这这些些头头文文件
102、件一一般般都都存存在在C+系系统统目目录录的的include子子目目录录下下。而而第第二二种种方方式式编编译译器器将将首首先先在在当当前前文文件件所所在在目目录录下下搜搜索索,如如果果找找不不到到再再按按标标准准方方式式搜搜索索。这这种种方方式式适适用于嵌入用户自己建立的头文件。用于嵌入用户自己建立的头文件。函数是C程序的基本模块可将一些功能相对独立的或经常一个被包含的头文件中还可以有一个被包含的头文件中还可以有#include指令,即指令,即include指令可以嵌套指令可以嵌套,但是,如果同一个头文件,但是,如果同一个头文件在同一个源程序文件中被在同一个源程序文件中被重复包含重复包含,就会
103、出现,就会出现标识标识符重复定义的错误符重复定义的错误。例如:头文件。例如:头文件f2.h中包含了中包含了f1.h,如果文件,如果文件f3.cpp中既包含中既包含f1.h,又包含,又包含f2.h,那么编译将提示错误,原因是,那么编译将提示错误,原因是f1.h被包含了两次,被包含了两次,那么其中定义的标识符在那么其中定义的标识符在f3.cpp中就被重复定义。中就被重复定义。避免重复包含可以用避免重复包含可以用条件编译指令条件编译指令。4.10.2 文件包含(嵌入)指令文件包含(嵌入)指令#include 函数是C程序的基本模块可将一些功能相对独立的或经常4.10.3 条件编译指令条件编译指令 1
104、用用宏名宏名作为编译的条件作为编译的条件格式为:格式为:#ifdef#else#endif2表达式的值表达式的值作为编译条件作为编译条件格式为:格式为:#if#else#endif当希望在不同条件下编译程序的不同部分。这种情况就要使当希望在不同条件下编译程序的不同部分。这种情况就要使用条件编译指令。用条件编译指令。 其中程序段可以是程序也可以是编译预处理指令。可以通过其中程序段可以是程序也可以是编译预处理指令。可以通过在该指令前面安排宏定义来控制编译不同的程序段。在该指令前面安排宏定义来控制编译不同的程序段。函数是C程序的基本模块可将一些功能相对独立的或经常例:在调试程序时常常要输出调试信息,
105、而调试完后不需要例:在调试程序时常常要输出调试信息,而调试完后不需要输出这些信息,则可以把输出调试信息的语句用条件编译指输出这些信息,则可以把输出调试信息的语句用条件编译指令括起来。形式如下:令括起来。形式如下:#ifdefDEBUGcouta=atx=xendl;#endif在程序调试期间,在该条件编译指令前增加宏定义:在程序调试期间,在该条件编译指令前增加宏定义:#defineDEBUG调试好后,删除调试好后,删除DEBUG宏定义,将源程序重新编译一次。宏定义,将源程序重新编译一次。条件编译指令包括:条件编译指令包括:#if、#else、#ifdef、#ifndef、#endif、#undef等。等。#ifndef与与#ifdef作用一样,只是选择的条件相反。作用一样,只是选择的条件相反。#undef指令用来取消指令用来取消#define指令所定义的符号,这样可指令所定义的符号,这样可以根据需要打开和关闭符号。以根据需要打开和关闭符号。函数是C程序的基本模块可将一些功能相对独立的或经常