1 / 56

第五章 函数与编译预处理

第五章 函数与编译预处理. 河海大学计算机信息学院 丁海军 dinghaijun@webmail.hhuc.edu.cn. 第六周上机题目 P96 2 , 4 , 5 , 6 , 12 第七周上机韪 P96 13 , 14 , 15 P101  例 6.3 P102  例 6.4 P105 例 6.5 P107 例 6.7, 例 6.8. 概述. 一 、模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块 , 作用: 各模块相对独立、功能单一、结构清晰、接口简单 控制了程序设计的复杂性 提高元件的可靠性 缩短开发周期

charo
Download Presentation

第五章 函数与编译预处理

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第五章函数与编译预处理 河海大学计算机信息学院 丁海军 dinghaijun@webmail.hhuc.edu.cn

  2. 第六周上机题目 • P962,4,5,6,12 • 第七周上机韪 • P9613,14,15 • P101 例6.3 • P102 例6.4 • P105 例6.5 • P107 例6.7,例6.8

  3. 概述 一、模块化程序设计 • 基本思想:将一个大的程序按功能分割成一些小模块, • 作用: • 各模块相对独立、功能单一、结构清晰、接口简单 • 控制了程序设计的复杂性 • 提高元件的可靠性 • 缩短开发周期 • 避免程序开发的重复劳动 • 易于维护和功能扩充 • 开发方法: 自上向下,逐步分解,分而治之

  4. 概述 二、复习 • C++是函数式语言 • 必须有且只能有一个名为main的主函数 • C++程序的执行总是从main函数开始,在main中结束 • 函数不能嵌套定义,可以嵌套调用

  5. 概述 二、函数分类 • 从用户角度 • 标准函数(库函数):由系统提供 • 用户自定义函数 • 从函数形式 • 无参函数 • 有参函数 使用库函数应注意: 1、函数功能 2、函数参数的数目和顺序,及各参数意义和类型 3、函数返回值意义和类型 4、需要使用的包含文件

  6. 5.1从一个编程任务谈起(P60第8题) void main() { int n,i,j; double y; cin>>n; y=0 For(i=1;i<=n;i++) { factorial_i=<i的阶乘>; y=y+factorial_i; } cout<<“y=“<<y<<endl; cout<<“n=“<<n<<endl } 例:从键盘输入一个整数n,按下式计算y的值,并输出y和n 的值(y用实数表示) y=1!+2!+3!+…+n! • 分析 • (1) 输入整数n cin>>n • 求整数n的阶乘和y • (3) 输出y和n的值cout<<y<<n Factorial_i=1; for(j=1;j<=I;j++) factorial_i=factorial_i*j;

  7. 5.1概述 #include<iostream.h> void main() { int n,i,j; long y,factorial_i; cin>>n; y=0; for(i=1;i<=n;i++) { factorial_i=1; for(j=1;j<=i;j++) factorial_i=factorial_i*j; y=y+factorial_i; } cout<<"y="<<y<<endl; cout<<"n="<<n<<endl; } 这是一个完整的程序,是可以运行的,但存在一些问题: (1)整个程序看起来结构不是太清晰 (2)求一个数的阶乘是一个很通用的功能,很多地方要使用,应该将其作为一个公用的函数存在。

  8. 函数定义 5.1概述 //求阶乘子程序 long fac (int i) { long x; x=1; for(j=1;i<=I;j++) x=x*j; return x; } //主程序 #include<iostream.h> double factorial(int i); void main() { int n,i,j; long y,factorial_i; cin>>n; y=0; for(i=1;i<=n;i++) { factorial_i=fac (i); y=y+factorial_i; } cout<<"n="<<n<<endl; cout<<"y="<<y<<endl; } • 语法要素 • 函数的定义要素 • 函数调用要素 • 函数的参数传递 • 变量存储类型

  9. 5.2 函数定义和调用 5.2.1 函数的定义 5.2.2 函数的返回值 5.2.3 函数的调用 5.2.4 函数的原型说明

  10. 函数定义风格: 函数类型 函数名(形参类型说明表) { 说明部分 语句部分 } 例 有参函数(错误在哪儿?) int max(int x, y) { int z; z=x>y?x:y; return(z); } 例 空函数 dummy( ) { } 函数体为空 函数返回值类型 缺省int型 无返回值void 5.2.1 函数定义 合法标识符 • 一般格式 函数体 //求阶乘子程序 long factorial(int i) { long x; x=1; for(j=1;i<=I;j++) x=x*j; return x; } 例 无参函数 printstar( ) { cout<<“**********\n”; } 或 printstar(void ) { cout<<“**********\n”; } 例 有参函数(现代风格) int max(int x,int y) { int z; z=x>y?x:y; return(z); }

  11. 返回语句 形式: return(表达式); 或return 表达式; 或return; 功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数 说明: 函数中可有多个return语句 若无return语句,遇}时,自动返回调用函数 若函数类型与return语句中表达式值的类型不一致,按前者为准,自动转换------函数调用转换 void型函数,无返回值,使用 return 例:符号函数(多返回语句) y= int sign(int x) { if (x>0) return 1; if (x= =0) return 0; if (x<0) return -1; } =1 x>0 =0 x=0 =-1 x<0 5.2.2 函数的返回值 例 无返回值函数 void swap(int &x,int &y ) { int temp; temp=x; x=y; y=temp; } return;

  12. 5.2.2 函数的返回值 例 函数带回不确定值 printstar() { cout<<"**********"; } main() { int a; a=printstar(); cout<<a; } void printstar() { printf("**********"); } main() { int a; a=printstar(); cout<<a; } 输出:10 编译错误!

  13. 5.2.2 函数的返回值 main() { float a,b,c; cin>>a>>b; c=max(a,b); cout<<"Max is “<<c<<endl; } ///////////////////////////////// int max(float x, float y) { float z; z=x>y?x:y; return(z); } 例 函数返回值类型转换 输入5.52.3 运行结果: 5.0

  14. 5.2 3 函数的调用 • 调用形式 函数名(实参表); 说明: • 实参与形参个数相等,类型一致,按顺序一一对应 • 实参表求值顺序,因系统而定(一般自右向左)

  15. 5.2 3 函数的调用 int fun(int a, int b); main() { int i=2,p; p=fun(i, i++); cout<<p); } int fun(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); } 例 参数求值顺序 #include<iostream.h> int fun(int a, int b); main() { int i=2,p; p=fun(i,++i); cout<<p; } int fun(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); } 运行结果:0 运行结果:0

  16. 5.2 3 函数的调用 • 调用方式 • 函数作为一条独立语句: 例 {printstar(); cout<<“Hello,World!\n”;} • 函数作为表达式中的一个操作数://必须是有返回值的函数 例 { m=max(a,b)*2;} • 函数作为加一个函数的参数: 例 { cout<<max(a,b); m=max(a,max(b,c)); }

  17. 对被调用函数要求必须是已存在的函数 库函数: #include <*.h> 用户自定义函数: 函数原型说明 函数原型说明 一般形式: 函数类型 函数名(形参类型 [形参名],….. ); 作用:告诉编译系统函数类型、参数个数及类型,以便检验 函数定义与函数说明不同 函数说明位置:程序的数据说明部分(函数内或外) 5.2.4 函数的原型说明 程序员素质: 对任何被调用函数都先作说明

  18. 5.2.4 函数的原型说明 例 函数说明举例 float add(float,float); //function declaration main() { float a,b,c; cin>>a>>b; c=add(a,b); cout<<"sum is “<<c; } float add(float x, float y) { float z; z=x+y; return(z); }

  19. c=max(a,b); (main 函数) 实参 int max(int x, int y) { int z; z=x>y?x:y; return(z); } (max 函数) 形参 5.3 函数参数及其传递方式 • 形参与实参 • 形式参数:定义函数时函数名后面括号中的变量名 • 实际参数:调用函数时函数名后面括号中的表达式 main() { int a,b,c; cin>>a>>b; c=max(a,b); cout<<"Max is “<<c; } int max(int x, int y) { int z; z=x>y?x:y; return(z); }

  20. 5.3 函数参数及其传递方式 • 形参与实参 • 说明: • 实参必须有确定的值 • 形参必须指定类型 • 形参与实参类型一致,个数相同 • 若形参与实参类型不一致,自动按形参类型转换——函数调用转换 • 形参在函数被调用前不占内存;函数调用时为形参分配内存;调用结束,内存释放

  21. 5.3 函数参数及其传递方式 • 值传递方式 • 方式:函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值 • 特点: • 形参与实参占用不同的内存单元 • 单向传递

  22. 调用前: x: 7 y: 11 x: 7 y: 11 调用: a: 7 b: 11 x: 7 y: 11 swap: a: 11 b: 7 temp x: 7 y: 11 调用结束: 例 交换两个数 #include<iostream.h> void swap(int a,int b); main() { int x=7,y=11; cout<<"==original==\n" ; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; swap(x,y); cout<<"==in main==\n"; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; cin>>x; } void swap(int a,int b) { int temp; temp=a; a=b; b=temp; cout<<"==in swap==\n"; cout<<"a="<<a<<" b="<<b<<endl; } ==original== x=7 y=11 ==in swap== a=11 b=7 ==in main== x=7 y=11

  23. 5.3 函数参数及其传递方式 • 地址传递 • 方式:函数调用时,将数据的存储地址作为参数传递给形参 • 特点: • 形参与实参占用同样的存储单元 • “双向”传递 • 实参和形参必须是地址常量或变量

  24. 5.3 函数参数及其传递方式 /*ch7_2.c*/ #include<iostream.h> void swap(int &a,int &b); main() { int x=7,y=11; cout<<"==original==\n" ; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; swap(x,y); cout<<"==in main==\n"; cout<<"x="<<x<<"\t" ; cout<<"y="<<y<<"\n" ; cin>>x; } swap(int &a,int &b) { int temp; temp=a; a=b; b=temp; cout<<"==in swap==\n"; cout<<"a="<<a<<" b="<<b<<endl; } 例 交换两个数 ==original== x=7 y=11 ==in swap== a=11 b=7 ==in main== x=11 y=7

  25. 文件包含编译预处理命令 #include <iostream.h> long sum(int a, int b); long factorial(int n); main() { int n1,n2; long a; cin>>n1>>n2; a=sum(n1,n2); cout<<"a=“<<a; } long sum(int a,int b) { long c1,c2; c1=factorial(a); c2=factorial(b); return(c1+c2); } 函数类型说明 long factorial(int n) { long rtn=1; int i; for(i=1;i<=n;i++) rtn*=i; return(rtn); } 函数调用 函数调用 实参 函数定义 形参 函数返回值 总结

  26. 5.3函数的嵌套调用 C++规定:函数定义不可嵌套,但可以嵌套调用函数 例 求三个数中最大数和最小数的差值

  27. main( ) dif函数 max函数 调用函数dif 调用函数max min函数 调用函数min 输出 结束 int dif(int x,int y,int z) { return max(x,y,z)-min(x,y,z); } int max(int x,int y,int z) { int r; r=x>y?x:y; return(r>z?r:z); } int min(int x,int y,int z) { int r; r=x<y?x:y; return(r<z?r:z); } #include <iostream.h> int dif(int x,int y,int z); int max(int x,int y,int z); int min(int x,int y,int z); void main() { int a,b,c,d; cin>>a>>b>>c; d=dif(a,b,c); cout<<"Max-Min=“<<d; }

  28. 5.4 变量的存储属性 • 基本概念: 变量是对程序中数据的存储空间的抽象 • 变量的属性 • 数据类型:变量所持有的数据的性质(操作属性) • 存储类型:决定变量的作用域和生存期(存储属性) • 变量定义格式: [存储类型] 数据类型 变量表; • 存储器类型:寄存器、静态存储区、动态存储区 • 生存期:变量在某一时刻存在-------静态变量与动态变量 • 作用域:变量在某区域内有效-------局部变量与全局变量 • 与存储类型有关的关键字 • auto -----自动型 • register-----寄存器型 • static ------静态型 • extern -----外部型

  29. 全局作用域 • 局部作用域 • 函数作用域 • 块作用哉 作用域 存储类型 • 静态存储 • 动态存储 5.4 变量的存储属性 变量在程序中的定义位置 变量的存储 修饰符(static) 变量在内存中的存储位置 变量的生存期 内存分布

  30. 5.4 变量的存储属性

  31. 5.4 变量的存储属性

  32. main f2 main f1 f2 f1 main a生存期: b生存期: a作用域 c生存期: b作用域 C作用域 例 文件file1.c int a; main( ) { ……. ……. f2; ……. f1; ……. } f1( ) {int b; ……… f2; …….. } f2( ) { static int c; ……… }

  33. 5.4 变量的存储属性 • 说明: • main中定义的变量只在main中有效 • 不同位置的同名变量,占不同内存单元 • (因此是不同的变量) • 形参属于局部变量 • 可用于局部变量修饰符:auto register static (默认为auto, register很少用) • 变量必须先定义才能使用 • 全局变量如要在多个文件中使用,需要用extern修饰符修饰

  34. float f1(int a) { int b,c; ……. } char f2(int x,int y) { int i,j; …… } main() { int m,n; ……. } a,b,c有效 x,y,i,j有效 m,n有效 5.4 变量的存储属性 例 不同函数中同名变量 main() { int a,b; a=3; b=4; cout<<"main:a=“<<a<<“ b=“<<b; sub(); cout<<"main:a=“<<a<<“ b=“<<b; } /////////////////////////////// sub() { int a,b; a=6; b=7; cout<<“sub:a=“<<a<<“ b=“<<b; } 运行结果: main:a=3,b=4 sub:a=6,b=7 main:a=3,b=4

  35. 5.4 变量的存储属性 int a=3,b=5; max(int a, int b) { int c; c=a>b?a:b; return(c); } main() { int a=8; cout<<"max=“<<max(a,b); } #include<iostream.h> int x=10; int main() { int x=100; for(int i=1;i<=10;i++) { int j=10; j=j*i; } cout<<x; x=100 cout<<i<<j; } 运行结果:max=8 编译错误!

  36. 5.4 变量的存储属性 例 全局变量的副作用 编写程序显示如下的图形: ***** ***** ***** ***** ***** #include<iostream.h> void prt(); int i; main() { for(i=0;i<5;i++) prt(); } /////////////////////// void prt() { for(i=0;i<5;i++) cout<<'*'; cout<<endl; } #include<iostream.h> void prt(); main() { int i; for(i=0;i<5;i++) prt(); cin>>i; } /////////////////////// void prt() { int i; for(i=0;i<5;i++) cout<<'*'; cout<<endl; } ?!!! 运行结果: *****

  37. 变量存储类型总结 局部变量 全局变量 auto register 局部static 全局static 全局 存储类别 动态 静态 存储方式 静态存储区 动态区 寄存器 存储区 生存期 函数调用开始至结束 程序整个运行期间 作用域 定义变量的函数或复合语句内 本文件 其它文件 赋初值 每次函数调用时 编译时赋初值,只赋一次 不确定 未赋初值 自动赋初值0或空字符 5.4 变量的存储属性 • 局部变量默认为auto型 • register型变量个数受限,且不能为long, double, float型 • 局部static变量具有全局寿命和局部可见性 • 局部static变量具有可继承性 • extern不是变量定义,可扩展外部变量作用域

  38. 例 局部静态变量值具有可继承性 void increment(void); main() { increment(); increment(); increment(); } void increment(void) { static int x=0; x++; cout<<x; } void increment(void); main() { increment(); increment(); increment(); } void increment(void) { int x=0; x++; cout<<x; } 运行结果:1 2 3 运行结果:1 1 1

  39. 例 用extern扩展外部变量作用域 main() { void gx(),gy(); extern int x,y; cout<<"1: x="<<x<<"\ty="<<y<<endl; y=246; gx(); gy(); } void gx() { extern int x,y; x=135; cout<<"1: x="<<x<<"\ty="<<y<<endl; } int x,y; void gy() { cout<<"1: x="<<x<<"\ty="<<y<<endl; } 运行结果: 1: x=0 y=0 2: x=135 y=246 3: x=135 y=246

  40. //xx.cpp int a; main() { int power(int n); int b=3,c,d,m; cout<<"Enter the number a and its power:\n"; cin>>a>>m; c=a*b; cout<<a<<b<<c; d=power(m); cout<<a<<“^”<<m<<“=“<<d; } //x1.cpp extern int a; int power(int n) { int i,y=1; for(i=1;i<=n;i++) y*=a; return(y); } 例 引用其它文件中的变量,输出ab和a的m次方

  41. 5,7,10 5,8,8.8 8,8,8 8,8,8.8 8,7,10 • 阅读下面程序,预测程序结果. #include<iostream.h> void main( ) { int a = 5, b = 7, c = 10; cout<<a<<”,”<<b<<”,”<<c<<endl; { int b = 8; double c = 8.8; cout<<a<<”,”<<b<<”,”<<c<<endl; a = b; { int c = b; cout<<a<<”,”<<b<<”,”<<c<<endl; } cout<<a<<”,”<<b<<”,”<<c<<endl; } cout<<a<<”,”<<b<<”,”<<c<<endl; }

  42. 5.5内联函数 float max(float x,float y); void main() {float A,B; cout<<Input A and B”; cin>>A>>B; cout<<”最大值=“<<max(A,B)<<endl; } inline float max(float x,float y); void main() {float A,B; cout<<Input A and B”; cin>>A>>B; cout<<”最大值=“<<max(A,B)<<endl; } • 内联函数也称为内嵌函数,解决程序的运行效率 • 函数调用需要建立调用栈环境,进行参数传递,产生程序执行的转移,这些工作需要时间开销 • 有些函数使用频率高,但代码很短 • 特别注意: • 循环语句 • Switch语句 • 复杂嵌套的 if~else 语句 • 出现在内联函数中无效 float max(float x,float y) { return (x>y? x: y); } inline float max(float x,float y) { return (x>y? x: y); }

  43. 5.6 具有缺省参数值的函数 P81-83 #include<iostream.h> void delay(int n=1000) { for( ; n>0; n- -); } void main() { cout<<"延时500个时间单位…\n"; delay(500); cout<<"延时1000个时间单位…\n"; delay(); } #include<iostream.h> void delay(int n=200); void main() { cout<<"延时500个时间单位…\n"; delay(500); cout<<"延时200个时间单位…\n"; delay(); } void delay(int n) { for( ; n>0; n- -); }

  44. 5.7 函数重载  在C语言中,仅求绝对值的函数就有以下几个 int abs(int x) long labs(long x) float fabs(float x) 这显然不利于人们记忆,为了求一个绝对值,人们要记忆三个函数及其参数要求。 函数重载

  45. //重新定义绝对值函数 int abs(int x) //整形 { return (x<0? -x:x);} long abs(long x)//长整形  { return (x<0? -x:x);} double abs(double x)//浮点 { return (x<0? -x:x);} //主程序 Main() { int x1=10; long x2=-53456; float x3=-153.5 cout<<abs(x1)<<endl; cout<<abs(x2)<<endl; cout<<abs(x3)<<endl; } 5.7 函数重载  • 说明 • 定义的重载函数必须具有不同的参数类型或参数个数 • 仅返回值不同时,不能定义为重载函数

  46. 5.8 编译预处理 5.8.1 包含文件(头文件) 5.8.2 宏定义 • 作用:对源程序编译之前做一些处理,生成扩展C源程序 • 种类 • 文件包含 #include • 宏定义 #define • 条件编译 #if--#else--#endif等(一般不用) • 格式: • “#”开头 • 占单独书写行 • 语句尾不加分号

  47. #include “file2.c” file2.c B A A file2.cpp file1.cpp file1.cpp 5.8.1 包含文件(头文件) < >直接按标准目录搜索 " " 先在当前目录搜索,再搜索标准目录 可指定路径 • 功能:一个源文件可将另一个源文件的内容全部包含进来 • 一般形式: #include “文件名” 或 #include <文件名> • 处理过程:预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译 • 说明: • 理论上讲,#include后面可以包含任可符合C++语法规范的程序语句。 • 实际使用时我们只建议将函数原型说明,常数值定义及其它的一些宏定义放在被包含文件中,对这样的文件我们称为头文件

  48. 5.8.2 宏定义 一、不带参数的宏定义 语法: #define宏名 表达式  例: #define PI 3.141592 #define PROMPT "面积为:" #define AREA PI*r*r 例:宏定义的使用 #include<iostream.h> #define PI 3.141592 #define R=2.8 #define AREA PI*R*R #define CR '\n\ #define PROMPT "面积是" main() { cout<<PROMPT<<AREA<<CR; } • 说明: • 宏定义功能#define是从C语言继承过来,建议大家少使用。 • 宏定义主要用于定义一些符号常数,建议大家使用关键字const来定义常数 #include<iostream.h> main() { cout<<"面积是: "<<3.141592*2.8*2.8<<‘\n'; }

  49. 5.8.2 宏定义 二、带参数的宏定义 语法: #define宏名(参数表) 表达式  例: #defineMAX(a,b) a>b?(a:b #define MIN(a,b) a<b?a:b #define SQR(x) (x)*(x) #define AREA(r) 3.1241592*(r)*(r) #define mult(x,y) (x)*(y)

  50. int f2(int t) { int a,c; …… c=f1(a); ……. return(3+c); } int f1(int x) { int y,z; …… z=f2(y); ……. return(2*z); } 5.9 递归调用 定义:函数直接或间接的调用自身叫函数的递归调用 int f(int x) { int y,z; …… z=f(y); ……. return(2*z); } • 说明 • C++编译系统对递归函数的自调用次数没有限制 • 每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出

More Related