580 likes | 718 Views
第 五 章 函数 调用. 5.1 void 函数 5. 2 传值调用与传引用调用 5. 3 使用过程抽象 5. 4 测试和调试函数 5.5 常规调试技术 作业. 5.1 void 函数. 函数声明. // 习题 4_10… double clothing_size(double height ,double weight,int age); // 返回用户服装(上衣)的尺码 ( 英寸 ) int main(){ … blouse_size=clothing_size( height,weight,age+10);
E N D
第五章 函数调用 • 5.1 void 函数 • 5.2 传值调用与传引用调用 • 5.3 使用过程抽象 • 5.4 测试和调试函数 • 5.5 常规调试技术 • 作业
5.1 void 函数 函数声明 • //习题 4_10… • double clothing_size(double height ,double weight,int age); • //返回用户服装(上衣)的尺码(英寸) • int main(){ … • blouse_size=clothing_size( height,weight,age+10); • pants_size=clothing_size(weight,age+10); … • } • double clothing_size(double height ,double weight,int age) • {//返回用户服装(上衣)的尺码(英寸) • double size=weight*height/288; • if(age>30) //30岁以后,每增加10岁尺码加1/8 • size=size+(1/8.0)*(age-30)/10; • return (size);} 函数调用 函数定义
返回一个值的函数特征: • 1. 它是完成子任务的一个小程序; • 2. 传给这个小程序的实参是它的输入;它的传值调用形参是局部于函数的变量(局部变量),它接收主调程序传送的实参的值。 • 3. 它生成一个值作为它惟一的结果,这个返回值相当于这个小程序的输出。 • 总之,这样的函数总是返回一个值,它是黑盒函数定义的理想模式。 • 事实上,一个子任务有三种情况: • (1)产生一个值; • (2)不产生值或其它子任务不需要它产生的值; • (3)产生多个值; • 不返回值的函数称为void 函数
5.1.1 void函数的定义 • //已知华氏温度及相应的摄氏温度,输出这两个温度值。 • void show_results(double f_degree,double c_degree) • { • cout.setf(ios::fixed); • cout.setf(ios::showpoint); • cout.precision(1); • cout<<f_degree<<“华氏温度相当于" • <<c_degree<<"摄氏温度。"<<endl; • return; • }
5.1.1 void函数的定义 • (1)声明部分: • void function_name(parameter_list); • (2) 调用部分:一个独立的可执行语句 • Void main() • { … • function_name(real_parameter_list); • } • (3) void函数定义: • void function_name(parameter_list); • { • … • return;//可以包含一个或多个return语句,也可以没有return语句 • }
5.1.2 void函数编程实例 • 例 5_1 将华氏温度转换为摄氏温度 • 转换公式为:C=(5/9)(F-32) • 华伦海托(1714),德国物理学家,制定了华氏温度,他发现水银气压表的指示刻度会随着温度的变化而变化 。 • 摄尔西乌斯(1742),瑞典天文学家。依据冰水混合(0oC)和与水的沸点(100oC)制定了摄氏温度。 • 如:104oF=40oC
将华氏温度转换为摄氏温度 主函数完成数据的输入 屏幕初始化 温度转换函数 温度输出函数 任务分解 • 将一个总任务分解成四个子任务,每个子任务都有一个独立的函数来完成:
函数的完整声明 • 四个子任务由四个函数来实现,我们首先写出这三个函数的完整声明(主函数不用声明): • void initialize_screen(); • //将当前输出与上个程序分开或清屏; • double celsuis(double fahrenheit); • //前条件:形参fahrenheit是华氏温度。 • //后条件:将华氏温度转换为摄氏温度; • void show_results(double f_degree,double c_degree); • //显示华氏温度及相应的摄氏温度。
函数的实现 这个return是必须的 • double celsuis(double fahrenheit) • { • return ((5.0/9.0)*(fahrenheit-32)); • } • void show_results(double f_degree,double c_degree) • { • cout.setf(ios::fixed); • cout.setf(ios::showpoint); • cout.precision(1); • cout<<f_degree<<"华氏温度等价于"; • cout<<c_degree<<"摄氏温度。"<<endl; • return; • } 这个return是可选的,并可多次出现
void 函数中的 return语句 • void show_results(double f_degree,double c_degree) • { • if(c_degree>0) • { • cout<<f_degree<<"华氏温度等价于" • <<c_degree<<"摄氏温度。"<<endl; • return; • } • cout<<“数据错误!"<<endl; • return; • } 这个return是可选的,并可多次出现,它标志着函数运行的终止。
Void 函数的调用是一个独立的可执行语句 在主函数中调用自定义函数 • int main() • { • double f,c; • initialize_screen(); • cout<<"我们将华氏温度转换为摄氏温度!"<<endl; • cout<<"请输入华氏温度:"; • cin>>f; • c=celsuis(f); • show_results(f,c); • return 0; • }
关于return语句 • return语句(称为返回语句)具有如下三种使用格式: • return; • return <表达式>; • return ( <表达式> ); • 第一种格式的return用于立即从被调函数中返回, 当函数类型为void时,应使用这种格式的返回语句。 • 当函数类型为非void型时,应使用第二或第三种格式的return语句,此两种格式的语句效果完全相同(可将第二种格式看成是第三种格式的省略形式),系统此时都将计算出表达式的值,并“携带”该值立即从被调函数中返回。
5.2 值调用与引用调用 • 一个子任务的功能有三种情况: • (1)产生一个值; • (2)产生多个值; • (3)不产生值或其它子任务不需要它产生的值,但需要完成一项任务(如显示、存储文件等)。 • 引用形参能将函数产生的多个值返回
5.2.1 引用参数的概念 • 若参数表中某参数的类型被指定为“<类型> &”,则称该参数为引用参数,否则为赋值参数。如: • void show_results(double f_degree,double c_degree); • //它所用的两个参数均为赋值参数。 • void swap(int& a, int& b); • //它所用的两个参数均为引用参数。 • 语法要求:调用函数时,引用参数对应的实参必须是指定类型的变量。
对函数进行调用的执行过程(步骤): • (1) 将对应实参表达式的值赋给赋值形参(若参数为赋值参数的话); • (2) 用实参变量替换相应的形参(若参数为引用参数的话); • (3) 按各形参的“当前值”(或已被“赋值”,或已被“换名”)去执行一遍函数体并返回调用处。 • f(1,2)___void f(int x,int y) • f(a,b)___void f(int &x,int &y)
结论: • (1) 通过赋值参数来传值的方式是一种“单向传值”方式,它只可向被调函数的形参“传入”值,而不可通过该形参“传出”值。 • 即函数中对形参的各种修改不改变实参。 • (2) 通过引用参数来传值的方式是一种“双向传值”方式,它不仅可向被调函数的形参“传入”值,而且还可通过该形参“传出”值。 • 即函数中对形参的任何修改都直接改变了实参。
a b a b 3 4 4 3 5.2.2 编程实例:swap函数 • 例5_2 交换内存中两个变量之值。 • 函数描述即函数声明: • void swap (int& a,int& b); • //交换形参a与b的值 交换前: 交换后:
函数算法实现: • void swap(int &x,int &y)//引用参数 • //void swap(int a ,int b )//值参 • { • int t=a; • a=b; • b=t; • }
例程编码(实例演示“5_2交换两个变量的值.cpp”)例程编码(实例演示“5_2交换两个变量的值.cpp”) • #include<iostream.h> • void swap(int &,int &);//引用参数 • //void swap(int ,int );//值参 • void main(){ • int a=3,b=4; • cout<<"交换前:a,b="; • cout<<a<<" ,"<<b<<endl; • swap(a,b); • cout<<"交换后:a,b="; • cout<<a<<" ,"<<b<<endl; • }
b a b a a b 4 3 4 4 3 3 //函数体 • void swap(int &x,int &y)//引用参数 • //void swap(int a ,int b )//值参 • { int t=a;a=b;b=t; } 实参 y 形参 x 引用调用 按值传送
思考 • 1.在什么情况下选用赋值参数?在什么情况下选用引用参数? 2.比较赋值参数与引用参数各自的优缺点 赋值参数功能上的缺陷 引用参数的副作用
5.3 使用过程抽象 • 5.3.1 函数的嵌套调用与递归调用 • 5.3.2 前条件与后条件 • 5.3.3 案例分析:超市定价系统
main() func1(x) func2(x) 5.3.1 函数的嵌套调用 • 1.函数嵌套的概念 • 一个函数体中又包含了一个或多个函数的调用,称为函数的嵌套。
2. 一个函数调用另一个函数举例 • 例5_3 将变量值按从小到大的顺序排列。(见例程演示) • void swap(int &x,int &y)//引用参数 • { • int t=x;x=y;y=t; • } • void order(int &a,int&b,int&c) • { • if(a>b) swap(a,b); • if(a>c) swap(a,c); • if(b>c) swap(b,c); • return; • }
main() order(x) swap(x) • 函数嵌套调用关系
main() function(x) function(x) • 3. 函数的递归调用 • 一个函数体中又调用了它自已,称为函数的递归调用。
//递归函数的定义 • double factor(int number) • { • double f; • if(number<=1) • f=1; • else • { • f=number*factor(number-1); • cout<<f<<endl;; • } • return f; • } • 见例程“5_5递归法求n!.cpp”
5.3.2 前条件与后条件 • 为函数声明写注释信息时,可将说明信息分为两种。 • 前条件: • 指出函数调用时要满足的条件,称为前条件; • 后条件: • 对函数调用结果的描述,即在满足前条件的前提下,指出函数执行之后将满足什么条件(得到什么结果),称为后条件。 • 具体的说就是对于要返回一个值的函数,后条件是对该返回值的描述;对于产生多个返回值的函数后条件是对这批值的描述,实际是对引用参数的修改描述。
如:void swap (int& a,int& b); • //前条件:a,b已被赋值 • //后条件:交换a与b的值 • double celsuis(double fahrenheit); • //前条件:形参fahrenheit是华氏温度。 • //后条件:将华氏温度转换后的摄氏温度;
5.3.3 案例分析:超市定价系统 • 1.问题定义 • Quick_shop连锁超市的定价策略: • 预计当7天之内售出时(<=7): • 商品零售价=批发价+批发价*5% • 预计当7天之后才能售出时(>=8): • 商品零售价=批发价+批发价*10% • 输入:商品批发价(进价)、预计售出的天数 • 输出:商品零售价
2.问题分析 • 将这个问题分成三个子任务: • (1)输入数据; • (2)计算商品零售价; • (3)输出结果。 • 三个任务由三个函数来完成: • 1)输入函数 • void get_input(double &cost,int &turnover); • //前条件:用户准备好从键盘输入二个正确数据至内存 • //后条件:cost设为一商品的批发价,rurnover为商品预计售出的天数
2)计算函数 • double price(double &cost,int &turnover); • //前条件:cost设为一商品的批发价, • rurnover为商品预计售出的天数 • //后条件:返回商品的零售价 • 3)输出函数 • void give_results(double cost,int turnover,double price); • //前条件:cost设为一商品的批发价, • // rurnover为商品预计售出的天数 • // cost为商品的零售价 • //后条件:在屏幕上显示cost、turnover、price的值
3.算法设计 • //返回商品的零售价 • double price(double &cost,int &turnover) • { • if(turnover>THRESHOLD) • return cost+cost*LOW_MARKUP; • else • return cost+cost*HIGH_MARKUP; • }
4.程序编码(完整编码见例程5_5) • void main() • { • double wholesale_cost,retail_price; • //批发价和零售价 • int sheft_time;//估计出售的时间 • introduction(); • get_input(wholesale_cost,sheft_time); • retail_price=price(wholesale_cost,sheft_time); • give_results(wholesale_cost,sheft_time,retail_price); • }
5.程序测试 • (1)输入的种类 • 低涨幅:5% • 高涨幅:10% • (2)边界值测试 • 预计售出天数 • THRESHOLD=7 • 相差1错误: • THRESHOLD=6 • THRESHOLD=8
5.4 测试和调试函数 • 1.驱动程序测试法 • 写一个特殊的程序,以尽可能简单的方法为函数获取参数值,然后执行函数,并显示结果。
函数get_input( )的驱动程序 • void main() • { … char ans; • do { • get_input(wholesale_cost,sheft_time); • cout<<"商品批发价: $"<<wholesale_cost<<endl; • cout<<"商品预计售出天数: "<<sheft_time<<"天"<<endl; • cout<<"继续吗?(y or n)"<<endl; • cin>>ans; cout<<endl; • }while(ans=='y'||ans=='Y'); • … • }//见例程“5_6函数的驱动程序.cpp”
2. stub(占位函数)测试法 • 在测试一个函数A时,要用到另一个还没有被测试的函数B,用B的简化版替代B,则称此B的简化版为stub.
这是一个占位函数(stub) • double price(double &cost,int &turnover) • { • if(turnover<=7) • return cost+cost*0.05; • else • return cost+cost*0.10; • return 23.9; • } • 见见例程“5_7测试超市定价系统.cpp”
5.5 常规调试技术 • 1. 常见错误 • (1)未初始化的变量; • (2)相差1错误;//<——<== • (3)超过数据边界;//35! • (4)自动类型转换;//5/9,重载函数形参 • (5)该用“==”的地方用了 “=”。//逻辑表达式中 • 2.定位错误 • (1)利用cout语句设置跟踪标志 • (2)利用调试器(debugger)设置断点。
3.利用调试器调试程序 • (1)利用调试器(debugger)设置断点 • 1)设置固定断点 • 鼠标左单击某个有效语句行首,单击【F9】键,该行左端出现圆点标记,说明该行设置了断点,调试到该行就会临时中断,单击【F5】继续调试. • 2) 设置与条件相关的断点 • 使用【edit】菜单下的【Breakpoint】命令可以设置与某个条件有关的断点。 • (2)调试 • 执行调试命令,单击【F5】键即可,继续执行,仍单击【F5】键。
5.6 案例分析:长度的换算 • 1.问题定义 • 5_8 写一个程序,要求它读入一个用成米和厘米表示的长度,换算英尺和英寸。或它读入一个用英尺和英寸成表示的长度,换算米和厘米,采用那种换算由用户选择。 • 1英尺=0.3048米,1米=100厘米, • 1英尺=12英寸。
main() menu_select() introduction() Britesh_metric() metric_British() input_British(…) British_metric(…) give_results(…) input_British(…) metric_British (…) give_results(…) 2.问题分析 • 将这个问题分成四个层次的子任务(共11个小任务)
//函数声明部分 • void introduction(); • //本系统的说明 • void menu_select(); • //后条件:根据用户的选择,执行相应的换算 • void British_metric(); • //执行英制到公制的转换过程 • void metric_British(); • //执行英制到公制的转换过程 • void input_metric(double &meter,double ¢imeter); • //前条件:用户准备好从键盘输入二个正确数据至内存 • //后条件:meter和centimetetr为一个长度的米数和厘米数 • void input_British(double &food,double &inch); • //前条件:用户准备好从键盘输入二个正确数据至内存 • //后条件:food和inch设为一个长度的英尺数和英寸数
void British_metric(double food,double inch,double &meter,double ¢imeter); • //前条件:food和inch为给定的一个长度的英尺数和英寸数 • //后条件:meter和centimetetr为返回的一个长度的米数和厘米数 • void metric_British(double meter,double centimeter,double&food,double&inch); • //前条件:meter和centimetetr为给定的一个长度的米数和厘米数 • //后条件:food和inch为返回的一个长度的英尺数和英寸数 • void give_results(double food,double inch,double meter,double centimeter); • //前条件:food、inch、meter、centimeter分别为给定的一个长度的英尺数、英寸数、米数和厘米数 • //后条件:在屏幕上显示food、inch、meter、centimeter的值
3.算法设计 • void menu_select() • { • char select; • do • { • cout<<"---------------------------------------\n"; • cout<<"\t1--将一个英制长度换算成公制长度\n"; • cout<<"\t2--将一个公制长度换算成英制长度\n"; • cout<<"---------------------------------------\n"; • cout<<"请选择:"; • cin>>select; • }while(select!='1'&&select!='2');