第3章函数 在开发较大的应用程序时,通常将程序分解为若干功能模块,每一模块实现一个特定的功能。采用模块化程序设计的优点在于可以控制程序的复杂性,便于人员分工,提高开发效率,提升软件的可靠性、可维护性和重用性等。在C++程序中,函数是程序的基本模块,通过组合函数模块的调用实现程序特定的功能,如图3.1所示。 图3.1组合函数模块的调用 在前文的程序中,都是在主函数main()中编写代码逻辑,有时还需要调用标准函数库中的一些函数。本章主要学习如何实现自定义函数,即将代码逻辑或算法编写成一个个相对独立的函数模块,通过组合调用的方式来实现程序。 3.1概述 从用户使用的角度看,函数有两种: 标准函数和自定义函数。标准函数是库函数,由系统提供,用户可以直接调用。自定义函数则是用户根据需求,自己编写用于实现特定功能的代码块。 C++语言提供极为丰富的库函数,这些库函数有数学函数、字符和字符串处理函数、输入输出函数和动态存储分配函数等。库函数的学习可以从掌握一些最基本、最常用的库函数开始,之后再逐步深入学习。 我们来看一个使用C++标准库中的数学函数的示例。 【例31】求整数x的绝对值。 编写程序 1#include 2#include 3using namespace std; 4int main() 5{ 6int x; 7x=-6; 8cout<<"|"< 2using namespace std; 3int sum(int x,int y) 4{ 5int s; 6s=x+y; 7return s; 8} 9int main() 10{ 11int z; 12z=sum(2,6); 13cout<<"2和6两数相加的结果是: "< 2using namespace std; 3int sum(int x,int y); 4int main() 5{ 6int z; 7z=sum(2,6); 8cout<<"2和6两数相加的结果是: "<b) return a; else return b; } 第一行说明getMax函数是一个整型函数,其返回的函数值是一个整数。形参为a,b,均为整型变量。在{}中的函数体内,除形参外没有使用其他变量,因此只有语句而没有声明部分; 若要用到新的变量,需要进行声明和初始化等。在getMax函数体中的return语句是把a(或b)的值作为函数的值返回给主调函数。有返回值的函数中至少有一个return语句。 函数在使用时将被视为“黑匣子”,除输入输出外,其他部分可以不必关心。从函数的定义中可以看出,函数头反映函数的功能和使用接口,它定义用什么输入数据执行一个处理逻辑后产生什么输出。它明确“黑匣子”的输入输出部分,输入就是形参,输出就是函数的返回值。而函数体中具体描述“如何做”,但是对函数的使用者而言,不需要知道其具体是如何实现的。 需要说明的是,函数的定义既可以放在主函数main之前,也可放在主函数main之后。此外,文件中所有的函数定义,包括main函数在内,都是平行的,即不能在一个函数的函数体内再定义另一个函数。 3.2.2定义与声明 函数的定义是指对函数功能的确立,包括为函数命名、指定函数返回值类型和形参类型以及编写函数体等。函数定义之后,需要进行函数声明后才能使用。函数的声明是把函数名、函数类型以及形参类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(如函数名是否正确,实参与形参的类型和个数是否一致)。调用函数之前对被调函数进行声明与使用变量之前先进行变量声明类似。 函数声明遵循如下原则。 (1) 在实践中一般将函数声明放在程序的起始位置,可以使得代码结构清晰。 (2) 若函数定义在先,调用在后,可以省略声明; 若定义在后,调用在先,则必须声明。 函数声明的一般形式如下。 类型说明符 被调函数名(类型 形参,类型 形参…); 或为: 类型说明符 被调函数名(类型,类型…); 括号内给出了形参的类型和形参名,或只给出形参类型,便于编译系统进行检查排错,保证程序正确执行。 例如,对上例中的getMax函数的声明如下。 int getMax (int a,int b); 或写为: int getMax (int,int); 3.3返回语句 调用函数时,主调函数和被调函数之间往往存在数据传递关系。主调函数通过参数将数据传递给被调函数,而被调函数则通过返回语句的返回值向主调函数传递数据。其中函数的返回值是指函数被调用后,执行函数体中的程序段所取得的返回给主调函数的值。如调用正弦函数取得正弦值,调用getMax函数取得的最大值等。 函数的返回值通过return语句返回给主调函数,语句的一般形式如下。 return表达式; return语句的功能是计算表达式的值,并返回主调函数。在函数中允许有多个return语句,但每次调用只能有一个return语句被执行,因此只能返回一个函数值。例如,前文示例中的getMax函数,有两个return语句,但每次有且仅有一个被执行。 既然函数有返回值,这个值就必然有一个确定的类型,应当在定义函数时指定函数值的类型。而对于不返回函数值的函数,可以明确定义为“空类型”,类型说明符为“void”。如前文示例中的printTableHead函数并不向主函数返回函数值,因此定义如下。 void printTableHead ( ){ …} 为了使程序有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型。 3.4函数的参数 函数的参数分为形式参数和实际参数两种。在定义函数时,函数名后面括号中的变量名称为“形式参数”,简称“形参”,在主调函数中调用函数时,函数后面括号中的参数称为“实际参数”,简称“实参”。 形参出现在函数定义中,作用范围是整个函数体,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不起作用。形参和实参的功能是用来进行数据传输。发生函数调用时,主调函数把实参的值传送给被调函数的形参,从而实现主调函数向被调函数的数据传输。 关于函数的形参和实参,有以下几点说明。 (1) 形参变量只有在被调用时才分配内存单元,调用结束时释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。 (2) 实参可以是常量、变量、表达式、函数等,无论实参是何种类型,在进行函数调用时,它们都必须已经具有确定的值,以便把这些值传送给形参。 (3) 实参和形参在数量、类型,以及顺序上应严格一致,否则会出现类型不匹配的错误。 (4) 函数调用中发生的数据传输是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。这意味着在函数调用过程中,形参的值发生改变,而实参中的值不会变化。 【例34】城市有大量出租汽车运营,其按行车里程收费的方式简单易于理解。首先在固定里程内的部分(三千米以内)按起步价10元收取,超出的部分则按每千米1.5元收取。程序要求输入行驶里程,经过计算后输出收费金额。 问题分析 设计子函数CalcFee实现根据里程数进行计费的功能,而在main主函数中实现读取用户输入的里程数,并调用子函数CalcFee进行计费运算,在调用时需要向子函数传递参数: 里程数。子函数根据一定的计算规则进行计算,完成后将结果返回给主函数,主函数输出结果。 编写程序 1#include 2#include 3using namespace std; 4double CalcFee(double miles); 5int main(){ 6double miles; 7cout<<"请输入行驶里程:"<>miles; 9cout<<"应该收取的费用为: "<3){ 17money=10+ceil(miles-3)*1.5; 18} 19return money; 20} 运行结果 请输入行驶里程: 4↙ 应该收取的费用为:11.5 3.5函数的调用 函数的调用,是使程序转而去执行其他函数体。函数不能嵌套定义,但是函数之间允许相互调用,也允许嵌套调用,习惯上把调用者称为主调函数。函数还可以自己调用自己,称为递归调用。 main函数是主函数,它可以调用其他函数,而不允许被其他函数调用。因此,程序的执行总是从main函数开始,完成对其他函数的调用后再返回main函数,最后由main函数结束整个程序。一个C++程序必须有且只有一个主函数main。 在程序中往往通过对函数的组合调用来执行各种具体功能,函数调用的一般形式如下。 无参函数的调用格式: 函数名( ) 有参函数的调用格式: 函数名(实际参数表) 对无参函数调用时不需要传递参数。有参函数中实际参数表的参数可以是常量、具有值的变量或表达式,各实参之间要用逗号进行分隔。 3.5.1函数的调用方式 根据函数调用在程序中出现的位置,函数的调用方式可以分为以下三种。 (1) 函数表达式: 函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数有返回值。 例如: z=getMax (x,y)是一个赋值表达式,把getMax的返回值赋予变量z。 (2) 函数语句: 函数调用的一般形式加上分号即构成函数语句。 例如: printf ("%d",a);和scanf ("%d",&b);都是以函数语句的方式调用函数。 (3) 函数实参: 函数作为另一个函数调用的实参出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须有返回值。 例如: printf("%d",getMax (x,y));把getMax调用的返回值作为printf函数的实参来使用。 使用函数调用语法来解决一个实际问题: 快速计算搬运费用。因为要解决的问题要比前文讲述的函数实例复杂,因此用到多个子函数将其简化。 【例35】某搬家运输公司要求设计一个能快速计算搬运费用的程序。搬家费用由两部分组成: 第一项是人力搬运费,每100千克收费40元; 第二项是运输费用,包含50元的固定费用和每千米10.5元的里程费用。程序首先提示用户输入家具的重量和运输距离,然后程序显示人力费用、运输费用和总费用。 问题分析 可以将程序设计成顺序结构形式,如图3.2所示。 图3.2程序流程 可以在程序的主函数中用输入语句(如cin语句)输入货物重量和距离,然后依次计算人力费用、运输费用、总费用和显示人力费用、运输费用、总费用等。计算人力费用使用函数Calc_Labor()完成; 计算运输费用使用函数Calc_Travel()完成; 显示人力费用、运输费用和总费用等使用函数Display_Charges()完成。 编写程序 1//This program estimates the charge for moving furniture. 2//It uses functions to do calculations and output the results. 3#include 4#include 5using namespace std; 6double Calc_Labor(int); 7double Calc_Travel(int); 8void Display_Charges(double, double, double); 9int main() 10{ 11int weight;//Estimated weight 12int distance; //Estimated distance 13double labor_charge; //Estimated labor charge 14double travel_charge; //Estimated travel charge 15double total_charge; //Estimated total charge 16cout << setprecision(2) 17<< setiosflags(ios::fixed) 18<< setiosflags(ios::showpoint); 19cout << "Enter the weight in kilograms: "; 20cin >> weight; 21cout << endl; 22cout << "Enter the distance in kilometers: "; 23cin >> distance; 24labor_charge = Calc_Labor(weight); 25travel_charge = Calc_Travel(distance); 26total_charge = labor_charge + travel_charge; 27Display_Charges(labor_charge, travel_charge, total_charge); 28cout << endl; 29return 0; 30} 31double Calc_Labor(int weight) 32{ 33const double LABOR_RATE = 40; 34double labor_charge; 35labor_charge = (weight / 100) * LABOR_RATE; 36return labor_charge; 37} 38double Calc_Travel(int distance) 39{ 40const double CHARGE_PER_KM = 10.50; 41const double FLAT_TRAVEL_COST = 50.00; 42double travel_charge; 43travel_charge = FLAT_TRAVEL_COST + distance * CHARGE_PER_KM; 44return travel_charge; 45} 46void Display_Charges(double labor_charge, double travel_charge, double total_charge) 47{ 48cout << endl; 49cout << "The estimated charges are as follows:" << endl << endl; 50cout << "Estimated Labor: " << setw(9) << labor_charge << endl; 51cout << "Estimated Travel: " << setw(9) << travel_charge << endl; 52cout << "---------------------------" << endl; 53cout << "Total Estimate: " << setw(9) << total_charge << endl; 54} 运行结果 Enter the weight in kilograms:1000↙ Enter the distance in kilometers:30↙ The estimated charges are as follows: Estimated Labor:400.00 Estimated Travel:365.00 --------------------------- Total Estimate:765.00 实例首先声明了三个函数: Calc_Labor()函数返回人力费用,返回值声明为双精度(double)类型,声明一个整型参数用于以千克为单位的货物重量作为函数输入。Calc_Travel ()函数返回运输费用,返回值同样声明为双精度(double)类型,声明一个整型参数用于以千米为单位的运输距离作为函数输入。Display_Charges()函数有三个参数,分别是人力费用、运输费用和总费用,类型均为双精度(double)类型,因为该函数没有返回值,所以返回值类型为void。 程序是一个顺序结构程序,首先用cin语句要求用户输入家具的重量和运输距离; 然后赋值语句 labor_charge = Calc_Labor(weight); 调用执行函数Calc_Labor(),计算出基于家具重量作参数的人力费用,该费用作为函数返回值返回到main()函数,并赋值给labor_charge变量; 赋值语句 travel_charge = Calc_Travel(distance); 调用执行函数Calc_ Travel (),计算出基于运输距离作参数的运输费用,该费用作为函数返回值返回到main()函数,并赋值给travel_charge变量; 赋值语句 total_charge = labor_charge + travel_charge; 调用执行函数计算人力费用和运输费用之和并将结果赋值给total_charge变量; 函数调用语句 Display_Charges(labor_charge, travel_charge, total_charge);的 三个输入参数分别是人力费用、运输费用和总费用,用于在显示器分别显示人力费用、运输费用和总费用。 3.5.2嵌套调用 C++语言不允许函数嵌套定义,即在一个函数中不能完整地包含另一个函数。各函数之间是平行和独立的,不存在上一级函数和下一级函数的问题,但是允许在一个函数的定义中出现对另一个函数的调用,这就是函数的嵌套调用,即在被调函数中又调用其他函数。需要注意的是,在调用函数之前,需要对每一个被调用的函数作声明(除非定义在前,调用在后)。其关系如图3.3所示。 图3.3函数的嵌套调用 图3.3表示两层嵌套的情形。其执行过程是: 执行main函数中调用a函数的语句时,即转去执行a函数,在a函数中调用b函数时,又转去执行b函数,b函数执行完毕返回a函数的断点继续执行,a函数执行完毕返回main函数的断点继续执行。 3.5.3递归调用 一个函数在它的函数体内调用其自身称为递归调用,该函数称为递归函数。在递归调用中,主调函数同时也是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。 递归调用有直接递归调用和间接递归调用两种形式。直接递归即在函数中出现调用函数本身。例如,用直接递归求解斐波那契数列第n项。斐波那契数列的第一项和第二项是1,而后面每一项是前二项之和,即1,1,2,3,5,8,13…… long fib(int x) { if(x>2) return(fib(x-1)+fib(x-2));//直接递归 else return 1; } 间接递归调用是指函数中调用了其他函数,而其他函数又调用了原来的函数。例如,下面的代码定义两个函数,它们构成了间接递归调用。 int fnl(int a) { int b; b=fn2(a+1); //间接递归 //... } int fn2(int s) { int c; c=fnl(s-1);//间接递归 //... } 示例中,fn1()函数调用了fn2()函数,而fn2()函数又调用了fn1()函数。它们构成了间接递归调用。 采用递归方法来解决问题,必须符合以下三个条件。 (1) 可以把要解决的问题转化为一个新问题,而这个新问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。即解决问题的方法相同,调用函数的参数不同(有规律地递增或递减),如果没有规律则不能适用递归调用。 (2) 可以应用这个转化过程使问题得到解决。使用其他的方法比较复杂或难以解决,而使用递归的方法可以较好地解决问题。 (3) 必须有一个明确的结束递归的条件,保证能够在适当的地方结束递归调用,否则可能导致系统崩溃。 下面我们应用递归调用的方法来估算一下基金本金的发展速度。 【例36】某基金公司初始资金5000万,预计每年收益是25%,每年固定费用支出150万(年终提取),管理方收益提成20%(也就是年终净收益1000万,基金管理人提取20%),请计算该基金第5年的资金额是多少? 问题分析 可以将程序设计成函数的递归调用形式。第5年的资金额取决于第4年的资金额,而第4年的资金额取决于第3年的资金额,而第3年的资金额取决于第2年的资金额,依次类推,已知第1年的资金额是5000万。 money(5)=money(4)*(1+0.25)-150-money(4)*0.25*0.2 money(4)=money(3)*(1+0.25)-150-money(3)*0.25*0.2 money(3)=money(2)*(1+0.25)-150-money(2)*0.25*0.2 money(2)=money(1)*(1+0.25)-150-money(1)*0.25*0.2 money(1)=5000 可以用式子表述如下。 money (n)=5000(n=1) money (n)= money (n-1) *(1+0.25)-150-money(n-1)*0.25*0.2(n>1) 可以看到,当n>1时,求第n年的资金的公式是相同的,因此可以用一个函数表示上述关系。 编写程序 1//This program estimates the money of the fund after n years. 2//It uses recursion functions to do calculations. 3#include 4using namespace std; 5int money(int n) 6{ 7int m; 8if (n==1) 9m=5000; 10else 11m=money(n-1)*(1+0.25)-150-money(n-1)*0.25*0.2; 12return m; 13} 14int main() 15{ 16int n=5; 17cout <<"The money of the fund after "< using namespace std; int a,b; /*外部变量*/ int fun1(){ /*函数fun1*/ //... } int x,y; /*外部变量*/ int fun2(){ /*函数fun2*/ //... } int main(){ /*主函数*/ //... return 0; } 从上例可以看出a、b、x、y都是在函数外部定义的外部变量,都是全局变量。但x、y定义在函数fun1之后,而在fun1内没有对x、y的说明,所以它们在fun1内无效。a、b定义在源程序最前面,因此在fun1、fun2及main内不加说明也可使用。 我们来看一个计算商店销售额的示例。 【例37】商店中通常进行多种商品的销售,每过一段时间需要进行一次商品单品销售额和总销售额的统计。设计程序进行该项任务的计算,要求输入N种商品,分别计算N种商品的销售额和N种商品的总销售额。 问题分析 定义全局变量salesAmount来累计总销售额,定义子函数calcSingleProductSales来计算单品销售额,该函数需要两个参数,单价price和销售量num来完成计算任务。在main主函数中,完成接收键盘输入的商品数目、每种商品的单价和销售量,之后调用子函数calcSingleProductSales,并将商品单价和销售量传递给子函数,子函数进行计算。计算任务完成后结果返回给main主函数,主函数进行结果的显示输出。 编写程序 1#include 2using namespace std; 3double salesAmount=0; 4double calcSingleProductSales(double price,double num); 5int main(){ 6int N; 7double productSales; 8double price,num; 9cout<<"请输入商品数量:"<>N; 11for (int i=1;i<=N;i++){ 12cout<<"请输入商品单价和销售量:"<>price>>num; 14productSales=calcSingleProductSales(price,num); 15cout<<"商品"< 2using namespace std; 3float circle; 4float getArea(float a,float b) { 5float s; 6circle=2*(a+b); 7s=a*b; 8return s; 9} 10int main(){ 11float l,w,area; 12cout<<"请输入长方形的长和宽:"<>l>>w; 14area=getArea(l,w); 15cout<<"该长方形的周长为: "< using namespace std; int x = 10; int main(){ int x = x; cout< 3using namespace std; 4int main() 5{ 6extern double CalcSalary(double hourPay,double hours); 7//声明调用其他文件中定义的函数 8double a,b; 9cout<<"请输入时薪和工作小时数: "<>a>>b; 11cout<<"该工人的工资为: "< 16using namespace std; 17double CalcSalary(double hourPay,double hours) 18{ 19double salary=hourPay*hours; 20return salary; 21} 运行结果 请输入时薪和工作小时数: 10 12 --------------------------- 该工人的工资为: 120 在需要调用外部函数的源文件中,需要用extern声明所用的函数是外部函数。 3.8综合实例 【例310】编写程序,实现搬家公司计算客户搬家的总费用。搬家总费用由人工费和搬运费两个因素决定。首先,搬运每100千克的家具收取人工费40元。其次,50元为搬家的固定费用,再加上每千米10.50元的搬运费。程序首先提示用户输入家具的重量和搬运的距离,然后计算搬运费,最后显示人工费、搬运费和搬家总费用。 问题分析 采用的算法用伪代码描述如下。 提示用户输入家具的重量weight和搬运的距离distance 计算人工费labor_charge 计算搬运费travel_charge 计算搬家总费用total_charge 显示人工费labor_charge,搬运费travel_charge和搬家总费用total_charge 程序计算人工费labor_charge和搬运费travel_charge,使用Calc_Labor()函数和Calc_Travel()函数来实现。同样,显示结果使用函数Display_Charges()来实现。 编写程序 1#include 2#include 3using namespace std; 4double Calc_Labor(int); 5double Calc_Travel(int); 6void Display_Charges(double, double, double); 7int main() 8{ 9int weight, distance; 10double labor_charge, travel_charge, total_charge; 11cout << "请输入家具的重量(千克): "; 12cin >> weight; 13cout << "请输入搬运的距离(千米): "; 14cin >> distance; 15labor_charge = Calc_Labor(weight); 16travel_charge = Calc_Travel(distance); 17total_charge = labor_charge + travel_charge; 18Display_Charges(labor_charge, travel_charge, total_charge); 19return 0; 20} 21double Calc_Labor(int weight) 22{ 23const double LABOR_RATE = 40; 24double labor_charge; 25labor_charge = (weight / 100) * LABOR_RATE; 26return labor_charge; 27} 28double Calc_Travel(int distance) 29{ 30const double CHARGE_PER_KM = 10.50; 31const double FLAT_TRAVEL_COST = 50.00; 32double travel_charge; 33travel_charge = FLAT_TRAVEL_COST + distance * CHARGE_PER_KM; 34return travel_charge; 35} 36void Display_Charges(double labor_charge, double travel_charge, double total_charge) 37{ 38cout << "计算出的搬家费用如下: " << endl; 39cout << "人工费: " << labor_charge << endl; 40cout << "搬运费: " << travel_charge << endl; 41cout << "-------------------------------------" << endl; 42cout << "搬家总费用: " << total_charge << endl; 43} 运行结果 请输入家具的重量(千克): 7400↙ 请输入搬运的距离(千米): 230↙ 计算出的搬家费用如下: 人工费: 2960.00 搬运费: 2465.00 ------------------------------------- 搬家总费用: 5425.00 首先声明了程序使用的Calc_Labor()、Calc_Travel()和Display_Charges()三个函数。Calc_Labor()计算人工费,所以声明该函数返回一个double类型的值,因为重量是以千克为单位输入,所以其参数是int类型。Calc_Travel()函数也返回一个double类型的值。因为搬运距离是int类型,所以其参数是int类型。最后,Display_Charges()包含三个参数,即人工费labor_charge、搬运费travel_charge和搬家总费用total_charge。因为Display_Charges()没有返回值,所以返回类型为void。 然后程序提示用户输入家具的重量和搬运的距离。赋值语句 labor_charge = Calc_Labor(weight); 调用函数Calc_Labor(),该函数的功能是根据家具的重量计算人工费。在main()函数中家具的重量weight的值,作为实参传递给形参weight计算人工费,并用变量labor_charge的值返回人工费。 下一个赋值语句 travel_charge = Calc_Travel(distance); 调用Calc_Travel(),该函数的功能是根据搬运的距离计算搬运费。同样,在main()中将变量distance的值传递给Calc_Travel()函数的参数distance计算搬运费,并用变量travel_charge的值返回搬运费。变量total_charge计算人工费labor_charge和搬运费travel_charge的总和。 最后程序将变量labor_charge、travel_charge和total_charge的值传递给Display_Charges()函数相应的参数,显示输出。 【例311】编写一个程序,实现银行为使用自动取款机(ATM)的客户提供个人贷款业务,并显示客户个人的贷款利息、贷款总额和每月的还贷金额。 首先,程序应该显示一段问候语,内容如下。 ************************************************************* 欢迎使用个人贷款程序。 本程序将显示个人的贷款利息、贷款总额和每月的还贷金额。 请按以下指令认真操作。 ************************************************************* 显示问候语后,程序要向用户询问贷款金额(本金)、贷款的年利率(百分比)和贷款年数(期限)。因为程序只用于处理个人贷款,因此,贷款金额不能少于1000元,且不能超过20000元。如果贷款金额超出范围,程序则显示相应的提示信息并终止执行。按照我国央行基准利率规定,贷款的年利率不能超过18.7%。如果输入的年利率大于18.7%,程序则显示相应的提示信息并终止执行。最后,贷款的最长期限只能在五年之内。如果输入的贷款期限大于五年,程序则显示相应的提示信息并终止执行。 如果所有的输入数据有效,程序计算贷款利息、贷款的总金额,以及每月的还贷金额,使用以下公式计算。 贷款利息=贷款金额×(年利率/100)×贷款年数 贷款总额=贷款金额+贷款利息 每月的还贷金额=贷款总额/ (12×贷款年数) 由于用户输入的年利率是百分比形式,所以在计算贷款利息的公式中需要将年利率值除100。如果用户输入年利率为10.7,必须使用公式进行换算,即10.7/100 = 0.107。 在计算每月的还贷金额公式中,表达式(12×还贷年数)是贷款的月份数。目的是把贷款总额按月还贷。 最后,程序显示用户输入的数据、贷款利息、贷款总额和每月的还贷金额。 问题分析 程序的设计较为简单,层次结构如图3.5所示。 图3.5程序层次结构 使用Display_Greeting模块实现显示多行问候语,把Obtain_Data模块分为三个子模块,分别是Get_Principal、Get_Rate和Get_Term,每个子模块实现相应的一个功能。这种每个输入值作为一个单独的子模块,是常见的一种编程方法。 因为涉及三个简单的计算公式,我们把Do_Calculations作为一个模块,这三个计算公式将出现在main()中。同时,Display_Results也作为一个模块,也将出现在main()中。 采用的算法用伪代码描述如下。 Display_Greeting Obtain_Data: Get_Principal Get_Rate Get_Term Do_Calculations Display_Results 获取用户的输入数据的三个模块具有相似的结构。因此,只编写Get_Principal的伪代码,其他两个模块类似。 贷款金额不能少于1000或大于20000,使用嵌套if语句实现。 Get_Principal的伪代码描述如下。 提示用户输入贷款金额principal if (贷款金额principal < 1000.00) 显示错误信息 退出程序 else if (贷款金额principal > 20000.00) 显示错误信息 退出程序 else 返回贷款金额 principal endif endif 编写程序 1#include 2#include 3#include 4using namespace std; 5void Display_Greeting(); 6double Get_Principal(); 7double Get_Rate(); 8int Get_Term(); 9int main() 10{ 11double interest, principal, rate, total, monthly_payment; 12int term; 13Display_Greeting(); 14principal = Get_Principal(); 15rate = Get_Rate(); 16term = Get_Term(); 17interest = principal * (rate/100) * term; 18total = principal + interest; 19monthly_payment = total / (12 * term); 20cout << "贷款年利率: " << rate << "%" << endl; 21cout << "贷款期限: " << term << " Years" << endl; 22cout << "贷款金额: " << principal << endl; 23cout << "贷款利息: " << interest << endl; 24cout << "-----------------------------------------------" << endl; 25cout << "贷款总额: " << total << endl; 26cout << "每月的还贷金额: " << monthly_payment << endl; 27return 0; 28} 29void Display_Greeting() 30{ 31int position; 32for (position = 1; position <= 61; ++position) 33cout << "*"; 34cout << endl; 35cout << "\t\t欢迎使用个人贷款程序。" << endl; 36cout << "本程序将显示个人的贷款利息、贷款总额和每月的还贷金额。"; 37cout << endl; 38cout << "请按以下指令认真操作" << endl << endl; 39for (position = 1; position <= 61; ++position) 40cout << "*"; 41cout << endl; 42} 43double Get_Principal() 44{ 45const double MIN_PRINCIPAL = 1000.00; 46const double MAX_PRINCIPAL = 20000.00; 47double principal; 48cout << "请输入贷款金额: "; 49cin >> principal; 50if (principal < MIN_PRINCIPAL) 51{ 52cout << "对不起,我们不提供1000元以下的个人贷款。" << endl; 53exit(1); 54} 55else 56if (principal > MAX_PRINCIPAL) 57{ 58cout << endl; 59cout << "对不起,我们不提供20000元以上的个人贷款。" << endl; 60cout << "请输入合理的贷款金额。\n"; 61exit(1); 62} 63else 64return principal; 65} 66double Get_Rate() 67{ 68const double MAX_RATE = 18.70; 69double rate; 70cout << "请输入当前的年利率(百分比): "; 71cin >> rate; 72if (rate > MAX_RATE) 73{ 74cout << "对不起,年利率超出了我们的最大值。" << MAX_RATE << "%" << endl; 75exit(1); 76} 77else 78return rate; 79} 80int Get_Term() 81{ 82const int MAX_TERM = 5; 83int term; 84cout <<"请输入贷款的期限: "; 85cin >> term; 86if (term > MAX_TERM) 87{ 88cout << endl; 89cout << "对不起,您的贷款期限不能超过" << MAX_TERM << "years" << endl; 90exit(1); 91} 92else 93return term; 94} 运行结果 ************************************************************* 欢迎使用个人贷款程序。 本程序将显示个人的贷款利息、贷款总额和每月的还贷金额。 请按以下指令认真操作。 ************************************************************* 请输入贷款金额: 10000.00↙ 请输入当前的年利率(百分比): 10.7↙ 请输入贷款的期限: 4↙ 贷款年利率: 10.70% 贷款期限: 4Years 贷款金额: 10000.00 贷款利息: 4280.00 ----------------------------------------------- 贷款总额: 14280.00 每月的还贷金额: 297.50 程序开始对main()函数里用到的四个子函数进行了原型说明,由于Display_Greeting()子函数只完成在屏幕显示信息的功能,所以不需要任何参数和返回值。其他三个子函数都不需要参数来完成其功能,只需要获得输入数据。如果用户输入的数据有效,则每个函数都把输入的数据返回给main()函数。因此,每个函数返回的类型和用户输入数据的类型一致。因为贷款金额和年利率是小数,所以Get_Principal()和Get_Rate()子函数的返回值是double型。因为贷款期限是整数,所以Get_Term()子函数的返回值是int型。 main()函数首先调用Display_Greeting()子函数显示问候语,接着调用Get_Principal()、Get_Rate()和Get_Term()子函数得到输入数据,并把相应的值赋值给变量principal、rate和term。注意,用户输入的年利率是百分比年利率。 然后,利用问题描述里给出的计算公式计算贷款利息interest、贷款总额total和每月的还贷金额monthly_payment。 最后,用7条cout输出语句显示结果。 【例312】编写函数,由实参传递主调函数中存放的字符串首地址给该函数,统计此字符串中字母、数字、空格和其他字符的个数,并在主调函数中输入字符串以及输出结果。 问题分析 在主调函数中,某一字符串可以存放在一维字符数组中,该数组名字是存放字符串的一维数组首地址,是地址常量,所编写的处理字符串的函数,其形参定义为数组名,调用该函数时,把主调函数中的存放字符串的一维字符数组首地址,通过实参传递到该函数中,能够处理主调函数的字符串。 编写程序 1#include 2using namespace std; 3void judge(char a[]); 4void main() 5{ 6const int size=100; 7char a[size]; 8cin.getline(a,size); 9judge(a); 10} 11void judge(char a[100])//判断字符类型 12{ 13int letter=0,number=0,others=0,i=0; 14while(a[i]!='\0') 15{ 16if ((a[i]>='a'&&a[i]<='z')||(a[i]>='A'&&a[i]<='z')) letter++; //统计字母个数 17else if (a[i]>='0' && a[i]<='9') number++; //统计数字个数 18else others++; //统计其他字符个数 19i++; 20} 21cout<<"letter="<>x>>y; 10d=cdivisor(x,y); 11m=cmultiple(x,y,d); 12cout<<"common divisor is "< 2#include 3using namespace std; 4void outs(char a[]); 5void main() 6{ 7const int size=10; 8char a[size]; 9cin.getline(a,size); 10outs(a); 11} 12void outs(char a[10]) 13{ 14int i; 15if(strlen(a)<=4) 16{ 17for(i=0;i<4;i++) 18cout< 2#include 3using namespace std; 4void scpy(char *,char *); 5void main() 6{ 7const int size=100; 8char a[size]="Hello world"; 9char b[size]="Net"; 10cout<<"a= "< 2#include //使用数学函数时要包含头文件cmath 3#include //使用I/O流控制符要包含头文件iomanip 4using namespace std; 5int main( ) 6{double f(double a,double b,double c); 7double a,b,c; 8cout<<"输入a,b,c:"; 9cin>>a>>b>>c; 10if (a>0 && b>0 && c>0 && a+b>c && b+c>a && c+a>b) 11{ 12cout< 2using namespace std; 3int main( ) 4{long f(int n); 5long s; 6s=f(3)+f(7)+f(8); 7cout<<"3!+5!+8!="<1) 编写程序 1#include 2#include 3using namespace std; 4int main( ) 5{double p(int n,int x); 6long s=0; 7int i,x,n; 8cout<<"输入n,x:"; 9cin>>n>>x; 10cout< 2#include 3using namespace std; 4int main( ) 5{long f(int n); 6long s=0; 7int i,n; 8cout<<"输入一个正整数n:"; 9cin>>n; 10cout<<"sum="< 2#include 3using namespace std; 4int main() 5{long f(int); 6int n=40,i; 7for(i=1;i<=n;i++) 8{cout<