C++大学基础教程 第4章 函数 北京邮电大学电信工程学院 计算机技术中心 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -2- 程序设计中,把具有一定功 能的程序模块用函数或类来 实现 第4章 函 数 4.1 函数概述 4.2 函数定义 4.3 函数调用 4.4 内联函数 4.5 重载函数 4.6 默认参数值的函数 4.7 全局变量与局部变量 4.8 变量的存储类型 4.9 编译预处理 4.1 函数概述 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -5- 4.1 函数概述 1.函数简介 一般是将整个程序分为若干个程序模块 每个模块用来实现一个特定的的功能 C++中模块的实现 ? 函数 ?库函数 ?自定义函数 ? 类 ? 技巧:要熟悉C++标准库提 供的类和函数集合。不要 事事从头做起,要尽可能 利用C++标准库提供的函数 而不是生成新函数,以便 减少程序开发的时间。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -6- #include <iostream> #include <cmath> using namespace std; int main() { cout << "Enter Quadratic coefficients: "; double a, b, c; cin >> a >> b >> c; if ( (a != 0) && (b*b - 4*a*c > 0) ) { double radical = sqrt(b*b - 4*a*c); double root1 = (-b + radical) / (2*a); double root2 = (-b - radical) / (2*a); cout << "Roots: " << root1 << " " << root2; } else { cout << "Does not have two real roots"; } return 0; } 调用函数 或主调函数 被调函数 库函数 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -7- #include <iostream> using namespace std; float CircleArea(float r); // main(): manage circle computation int main() { cout << "Enter radius: "; float MyRadius; cin >> MyRadius; float Area = CircleArea(MyRadius); cout << "Circle has area " << Area; return 0; } // CircleArea(): compute area of radius r circle float CircleArea(float r) { const float Pi = 3.1415; return Pi * r * r; } 自定义函数 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -8- 2.数学库函数 C++语言提供的库函数中有一些是专门完成特定的数 学运算的,称为数学库函数。 实现常见的数学计算 例如: 求绝对值、平方根等。 调用数学函数: 函数名(参数1, … ,参数n) 例如: cout<<sqrt(900.0); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -9- 2.数学库函数 数学函数库中的多数函数都返回double类型结果。 使用数学库函数,需要在程序中包含m ath.h头文 件,这个头文件在新的C++标准库中称为cmath。 函数参数可取常量、变量或表达式。 例: 如果c=13.0、d=3.0和f=4.0,则下列语句: cout<<sqrt(c+d*f); 计算并显示13.0+3.0*4.0=25.0的平方根,即5.0。 4.2 函数定义 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -11- 4.2 函数定义及使用 main() function1() function2() function3() function4() function5() 图4 -1 层次化的函数关系 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -12- 4.2 函数定义及使用 函数定义 函数原型 return语句 函数使用的三种方式 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -13- 1.函数的定义 包括接口和函数体 ? 接口 ?函数类型 函数名 形 式参数表 ? 函数体 ?完成函数功能的语句集合 ?返回值 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -14- 函数定义语法形式 函数类型 函数名 (形式参数表) { 函数体(变量声明和语句) return 表达式; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -15- 函数定义 float CircleArea (float r) { const float Pi = 3.1415; return Pi * r * r; } 函数体 返回值语句 局部变 量定义 形式参数 函数类型 函数名 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -16- 函数名 函数名是这个独立代码段(函数体)的外部标识符 函数定义之后,即可通过函数名调用函数(函数体代 码段)。 例: cout << CircleArea(MyRadius) << endl; 函数名的构成可以是任何有效标识符 (一般多以反映函数功能的单词组合命名,以增 强程序的可读性) 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -17- Sum() // Sum(): compute sum of integers in a ... b int Sum(int a, int b) { int Total = 0; for (int i = a; i <= b; ++i) { Total += i; } return Total; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -18- 形式参数表 函数的形式参数表,简称形参表 形式: (类型1 形式参数1, …,类型n 形式参数n) 形式参数表示主调函数和被调函数之间需要交换的信息 (1)传给被调函数的待处理的数据; (2)控制被调函数操作执行的信息; (3)被调函数执行的结果。 形式参数表从参数的类型、个数、排列顺序上规定了主调函 数和被调函数之间信息交换的形式。 如果函数之间没有需要交换的信息,也可以没有形参,形参 表内写void或空着。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -19- Sum() // Sum(): compute sum of integers in a ... b int Sum(int a, int b) { int Total = 0; for (int i = a; i <= b; ++i) { Total += i; } return Total; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -20- PromptAndRead() // PromptAndRead(): prompt and extract next // integer int PromptAndRead() { cout << "Enter number (integer): "; int Response; cin >> Response; return Response; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -21- 函数返回值 函数返回值类型 规定了函数返回给主调函数的值的 类型,也称为 函数类型 。 当需要函数向主调函数返回一个值时,可以使用 return语句,将需要返回的值返回给主调函数,故 称之为 返回值 。 需要注意的是由return语句返回的值的类型必须与 函数定义时指定的函数返回值类型一致。 如果不需要向主调函数返回值,函数可以定义成无 类型的,函数类型写成void,函数结束时也不必用 return语句。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -22- Sum() // Sum(): compute sum of integers in a ... b int Sum(int a, int b) { int Total = 0; for (int i = a; i <= b; ++i) { Total += i; } return Total; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -23- 函数体 函数体 是实现函数功能的代码部分 ? 变量声明 ? 完成函数功能的语句两部分 从组成结构看,函数体是由程序的三种基本控 制结构即顺序、选择、循环结构组合而成的。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -24- Sum() // Sum(): compute sum of integers in a ... b int Sum(int a, int b) { int Total = 0; for (int i = a; i <= b; ++i) { Total += i; } return Total; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -25- 函数是由函数名、函数类型、形参表 和函数体四部分组成的,使用时通过 函数名和参数表调用函数. 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -26- 例 4.1 编写一个函数cube,计算整数的立方。调用函数 cube计算从1到10相邻整数的立方差。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -27- //计算整数的立方 #include <iostream> using namespace std; int cube( int ); // 函数原型声明 void main() { int last,nowcb; last=1; cout <<"the difference of cube: "<<endl; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -28- for ( int x = 2; x <= 10; x++ ) { nowcb=cube( x ); cout <<nowcb-last << " "; last=nowcb; } cout << endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -29- //函数定义 int cube( int y ) { return y*y*y; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -30- 例 4.2 在三个浮点中确定最大值,使用自定义函数 maximum完成。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -31- // 在三个浮点中找出最大值 #include <iostream> using namespace std; float maximum(float, float, float ); // 函数原型声明 void main() { float a, b, c; cout << "Enter three floating numbers: "; cin >> a >> b >> c; //调用 maximum函数, a,b,c为实际参数 cout << "Maximum is: " << maximum( a, b, c ) << endl; //函数调用 } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -32- // maximum函数定义 // 函数的形式参数 x, y , z float maximum(float x, float y, float z) { float max; max = x>=y?x:y; max = max>=z?max:z; return max; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -33- 2.函数原型 引用函数之前,要先指定函数的接口形式 ? 函数原型 ? 函数定义 函数原型声明格式: 函数类型 函数名(形式参数表); 例: int Max(int a, int b) 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -34- 函数原型 函数原型声明 使编译器获得关于函数名称、函 数类型、函数形参个数、形参类型和形参顺序 的信息。 函数调用时,编译器根据函数原型声明验证函 数调用正确与否。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -35- 函数原型 ? 库函数的声明在相应的库的头文件中,使用库函数 时要包含相应的头文件。 例: #include <cmath> 调用数学库函数: sqrt(…) sin(…) abs(…) …… fstream ? File stream processing assert ? C-based library for assertion processing iomanip ? Formatted input/output (I/O) requests ctype ? C-based library for character manipulations math ? C-based library for trigonometric and logarithmic functions 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -36- 函数原型 ? 程序中,如果调用自定义的函数,且函数定义在后, 调用在先,则必须在调用函数之前有函数原型声明。 void subfun1( …); //原型声明 main() { ┆ sunfun1( …); //函数调用 ┆ } void subfun1( …) //函数定义 { … } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -37- 函数原型 ? 如果是函数定义在先,调用在后,则不必进行函数原 型声明。因为编译器已经从函数定义得到关于函数的 信息。 void subfun1( …) //函数定义 { … } main() { ┆ sunfun1( …); //函数调用 ┆ } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -38- 函数原型 ? 源文件中,如果在所有函数定义体之外声明函数 原型,则该函数可被位于其原型声明之后的所有 函数调用。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -39- void subfun1( …);//原型声明 main() { ┆ sunfun1( …);//函数调用 ┆ } void sunfun2() { ┆ sunfun1( …);//函数调用 ┆ } void subfun1( …)//函数定义 { … } main() { void subfun1( …); //函数原型 声明 ┆ sunfun1( …); //函数调用 ┆ } void sunfun2() { ┆ sunfun1( …); //函数调用,┆ } void subfun1( …) { … } 错误,编译 器不识别 sunfun1标识 符。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -40- 3.return语句 return语句使程序执行流程从被调函数返 回主调 函数,有两种形式: (1) 不 返回值的形式: return; (2) 返 回值的形式 return 表达式; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -41- 例4.3 从键盘输入三角形的三个边长,计算三角形 的面积。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -42- //给定三角形的三个边长,计算三角形的面积。 #include<iostream> #include <cmath> using namespace std; void TriangleAreabySide(float a, float b, float c); void main() { float a,b,c; cout<<"input three numbers of the triangle sides:"; cin>>a>>b>>c; TriangleAreabySide(a, b, c); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -43- //利用边长计算三角形的面积 void TriangleAreabySide(float a, float b, float c) { float area; float s; if (a+b<=c || a+c<=b || b+c<=a ) { cout<<"Not a triangle!"<<endl;; return; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -44- else { s=(a+b+c)/2; area=sqrt(s*(s-a)*(s-b)*(s-c)); cout<<"area of the triangle("<<a<<","<<b<<","<<c<<"): "<<area<<endl; return; } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -45- 例 4.4 利用随机数产生函数rand()产生的随机数模拟 考试成绩,统计成绩的平均值。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -46- //统计分数均值 #include<iostream> #include<cstdlib> #include <ctime> using namespace std; int CalMean(int count); void main() { int count;//数据个数 int mean;//均值 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -47- //输入数据 cout<<"input number:"; cin>>count; mean=CalMean(count); cout<<"mean= "<<mean<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -48- //计算均值 int CalMean(int count) { int score;//分数 int sum(0), mean; srand( (unsigned)time( NULL ) );//种子 //累积求和 int k(0); cout<<"the scores:"<<endl; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -49- while(k<=count) { score=rand()%100; if (score<10) continue;//假设没有低于 10分的,舍弃此数据。 else { cout<<score<<" "; sum+=score;//累积分数 k++;//累积个数 } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -50- cout<<endl; //计算平均值 if(count>0) mean=sum/count; else mean=0; return mean; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -51- ? 当被调函数只 需 要 把 一 个 数 值 结 果 返 回 给 主 调 函 数时,使用return语句返回比较合适。 ? 如果使用return语句给主调 函数返回一个值,则 returnu语 句 必 须 返 回 一 个 与 所 在 函 数 的 函 数 类 型一致的表达 式 。 若 表 达 式 的 结 果 与 函 数 类 型 不 一致,不能通 过 编 译 , 需 要 作 强 制 类 型 转 换 , 将 表达式的类型强制成与函数类型。 例如,如果mean是float型,则return语句应为: return (float)mean; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -52- 4.函数使用的三种方式 (1) 函数语句 函数语句形式: 函数名(实参数表); 例如: TriangleAreabySide(a, b, c); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -53- 实际参数表 实际参数表 ,简称为 实参表 实 参 表 是 按与被调函数形参表一一对应的格式组织的参数 表,即参数的类型、个数和排列顺序必须与被调函数声明的 形参数表严格一致。 实 际 参 数 表的各实际参数以逗号间隔,实际参数可以是常 量、变量和表达式(常量和变量都是最简单的表达式)。 在 执 行 到 函数调用语句时,由主调函数提供给被调函数的 数据和控制信息的参数(视为输入参数)必须具有确定值。 如果被调函数无形参,则实参表也是空的。 实际参数以数据值(值传递)或实际存储空间(地址传 递)提供了形式参数所需的内容。 实际编程中,从可读性考虑,一般使用变量(普通变量或 指针变量)作实际参数。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -54- 调用无参数的函数 #include<iostream> using namespace std; void DisplayMessage(); void main() { DisplayMessage (); //函数调用语句 } void DisplayMessage () { cout<<"只显示确定信息的简单函数不带参数 "<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -55- ( 2)函数表达式 ?函数调用出现在一个表达式中,其形式: 函数名(实际参数表) ?这种表达式称为函数表达式,由函数名和实参表组 成。 ?此时函数要使用return语句向主调函数返回一个确 定的值,参与它所在的表达式的运算。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -56- 例4.5 编写程序,实现坐标旋转公式: θθ θθ cossin sincos 00 00 yxy yxx += ?= 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -57- //实现坐标旋转公式 #include<iostream> #include <cmath> using namespace std; void main() { const double PI=3.14; int x,y;//旋转后坐标 int x0,y0;//原始坐标 int angle; //旋转角度 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -58- //输入数据 cout<<"input point(x,y):"; cin>>x0>>y0; cout<<"input angle of rotation:"; cin>>angle; //计算旋转后的坐标 double theta=angle*PI/180; x=x0*cos(theta)-y0*sin(theta); y=x0*sin(theta)+y0*cos(theta); //输出结果 cout<<"x="<<x<<endl; cout<<"y="<<y<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -59- ?注意: 当函数调用出现在表达式中时,函数一定要通过 return语句返回一个与函数类型一致的值,作为 这个函数表达式的值参与相关计算。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -60- (3)函数参数 利用函数的返回值作实际参数,再作函数调 用。 例如: m=max( a, max( b, c) ); 实质上也是函数表达式形式调用的一种 。 4.3 函数调用 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -62- 4.3 函数调用 函数调用的执行机制 值调用 嵌套调用 递归调用 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -63- 1.函数调用的执行机制 系统在调用函数时,在特定内存空间中为函数 建立一个 活动记录 。 函数的活动记录存储 ? 在函数内定义的每个变量的值 ? 主调函数的断点地址 ? 被调函数的返回值 (保存信息的具体内容与所使用的编译器有关 ) 活动记录是函数正常执行、调用和返回的物理 基础。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -64- ?在程序执行过程中,如果遇到调用其它函数,则系 统暂停当前函数的执行,将下一条指令的地址( 返回 地址 , 当流程从被调函数返回当前函数后,从此地址 继续执行当前函数的语句 。亦称 断点地址 )存入活动 记录,流程转去执行被调函数。 系统为被调函数建立 一个活动记录,被调函数的形参和在被调函数内定义 变量将存入它的活动记录。 当执行完被调函数时,系 统撤销它的活动记录,流程重新回到主调函数,恢复 其断点处的运行状态,继续执行程序的后序语句,直 至结束。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -65- void fun(…); void main() { ┆ fun(…); ┆ } void fun(…) { ┆ return; } main() 调f un() 结束 fun() 返回 保存: 返回地址 当前现场 恢复: 主调函数现场 返回地址 1 2 3 4 5 6 7 图4 -2 函 数调用和返回的过程 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -66- 例 4.6 从键盘输入屏幕上两点的坐标(x, y),计算两点 之间的距离。 (分析函数调用时活动记录 ) 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -67- //计算两点之间的距离 #include<iostream> #include <cmath> using namespace std; float CalDistance(int x1, int y1, int x2, int y2); int square(int x); void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -68- //输入数据 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; //计算距离 dist=CalDistance(x1,y1,x2,y2); //输出结果 cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2 <<","<<y2<<"): "<<dist<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -69- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx2,dy2; dx2=square(xx2-xx1); dy2=square(yy2-yy1); float dist=sqrt(dx2+dy2); return dist; } //计算一个数的平方 int square(int x) { return x*x; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -70- 运行结果: input point 1 (x1,y1):10 20 input point 2 (x2,y2):110 120 distance between point (10,20) and point (110,120): 141.421 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -71- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -72- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 x1 x2 y2 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -73- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 x1 x2 y2 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -74- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 x1 x2 y2 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -75- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 10 x1 x2 20 y2 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -76- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 10 x1 x2 20 y2 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -77- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 10 x1 x2 20 110 y2 120 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -78- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x* x; } 10 xx1 20 110 yy1 120 xx2 yy2 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -79- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x* x; } 10 xx1 20 110 yy1 120 xx2 yy2 dx dy 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -80- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x*x; } 10 xx1 20 110 yy1 120 xx2 yy2 dx dy 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -81- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 yy2 dx dy 100 x 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -82- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 yy2 dx dy 100 x 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -83- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 yy2 dx dy 100 x 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -84- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 dx dy 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -85- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 dx dy 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -86- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 dx dy 100 x 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -87- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 10000 dx dy 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -88- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 10000 dx dy 141.421 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -89- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx,dy; dx=square(xx2-xx1); dy=square(yy2-yy1); float dist=sqrt(dx+dy); return dist; } //计算一个数的平方 int square(int x) { return x**x; } 20 110 yy1 120 xx2 10000 yy2 10000 dx dy 141.421 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -90- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } y1 10 x1 x2 20 110 y2 120 141.421 dist 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -91- void main() { int x1,y1,x2,y2;//两点坐标 float dist; //两点间距离 cout<<"input point 1 (x1,y1):"; cin>>x1>>y1; cout<<"input point 2 (x2,y2):"; cin>>x2>>y2; dist=CalDistance(x1,y1,x2,y2); cout<<"distance between point ("<<x1<<","<<y1<<") and point ("<<x2<<","<<y2<<"): "<<dist<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -92- //计算距离 float CalDistance(int xx1, int yy1, int xx2, int yy2) { int dx2,dy2; dx2=square(xx2-xx1); dy2=square(yy2-yy1); float dist=sqrt(dx2+dy2); return dist; } //计算一个数的平方 int square(int x) { return x* 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -93- 返回值 函数输入数据流 输出数据流 函数能够从输入数据或参数 获得信息 参数 函数处理完的信息用 返回值或输出数据流 输出 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -94- 2. 函数的参数传递(值调用) 函 数 之 间 的 信 息 交 换 的 一种重要形式是函数的 参数传递,由函数的形式参数和实际参数实现。 函 数 在 没 有 被 调 用 时 , 函数的形式参数并不占 有实际的内存空间,也没有实际的值。C ++语言 中函数的参数传递方式分为两种: ·值传递 ·地址传递 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -95- 值传递 如果函数的形式参数为普通 变量,当函数被调用 时 , 系统为这些形式参数分配内存空间,并用实际 参 数 值初始化对应的形式参数,形式上实际参数的 值 传 递给了形式参数。这就是函数调用时参数的值 传递。 值传递方式,实际参数和形 式参数各自占有自己 的内存空间; 参数传递方向只能由实际参数到形式 参 数 ;不论函数对形式参数作任何修改,对相应的 实际参数都没有影响。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -96- 例4.7 如果一个数的所有真因子(包括1,但不包括这 个数本身)之和正好等于这个数本身,则称此数 为完美数。例如: 6=1×2×3,而 1+2+3=6; 28=1×4×7=1×2×14, 而 1+2+4+7+14=28。 如何确定完美数,欧几里得发现,只要是一个素 数,则 一定是一个完美数 。编写程序找出最小 的5个完美数。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -97- //寻找最小的五个完美数 #include <iostream> #include<cmath> using namespace std; bool DecidePrime(unsigned int number); unsigned int power(unsigned int x, unsigned int y); void main() { unsigned int perfect_number; unsigned int num,temp; short n(2); short counter(0);//计数器 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -98- while (counter<5) { temp=power(2,n); num=temp-1; if(DecidePrime(num)) { perfect_number=temp/2*num; cout<<"n="<<n<<","<<"perfect number="<<perfect_number<<endl; counter++; } n++; } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -99- //计算指数 unsigned int power(unsigned int x, unsigned int y) { unsigned int mul(1); for(int i=1;i<=y;i++) mul*=x; return mul; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -100- //判别素数 bool DecidePrime(unsigned int number) { unsigned int i, k; k=sqrt(number); for(i=2; i<=k; i++) //找 number的因数 { if(number%i==0) break; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -101- if(i>=k+1) //判断 number是否被小于 number的数整除 return true; else return false; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -102- 例4.8 从键盘输入两整数,交换次序后输出。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -103- //演示函数参数值传递单向性的例程 #include<iostream.h> void swap(int a, int b); int main() { int x(5), y(10); cout<<"x="<<x<<" y="<<y<<endl; swap(x,y); cout<<"x="<<x<<" y="<<y<<endl; return 0; } 5 10 x 1024 y 1028 运行结果: x=5 y=10 x=5 y=10 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -104- void swap(int a, int b) { int t; t=a; a=b; b=t; } 5a2048 10b252 t2065 10 5 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -105- 3. 嵌套调用 C++的函数不能 嵌套定义 C++的函数可以 嵌套调用 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -106- 例4.9 编程计算一个空心圆柱体的体积。用函数 CylinderVolume()计算一个半径为r、高度为h 的圆柱体的体积: hr 2 π 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -107- //计算空心圆柱体的体积 #include <iostream> #include <string> using namespace std; float DonutSize(float Outer, float Inner, float Width); float CylinderVolume(float Radius, float Width); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -108- void main( ) { cout<<"Outer edge donut radius:"; float OuterEdge; cin>>OuterEdge; cout<<"Hole radius: "; float InnerEdge; cin>>InnerEdge; cout<<"Donut thickness:"; float Thickness; cin>>Thickness; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -109- //计算空心圆柱体的体积 cout<<endl<<"Size of donut with radius "<< OuterEdge<<endl; cout<<" hole radius "<<InnerEdge <<endl; cout<<" thickness "<<Thickness<<endl; cout<<" is "<<DonutSize(OuterEdge, InnerEdge, Thickness)<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -110- //计算空心圆柱体体积 float DonutSize(float Outer, float Inner, float Width) { float OuterSize = CylinderVolume(Outer, Width); float HoleSize = CylinderVolume(Inner, Width); return OuterSize-HoleSize; } //计算圆柱体体积 float CylinderVolume(float r, float h) { const float pi=3.1415f; return pi*r*r*h; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -111- 函 数 main () 调用函数 DonutSize 结束 函数 DonutSize(...) 函 数 CylinderVolume(...) 的第一次执行 1 2 3 4 5 6 10 图4-4 函数嵌套调用示意图 调用函数 CylinderVolum e 调用函数 CylinderVolum e 7 函 数 CylinderVolume(...) 的第二次执行 8 9 11 12 13 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -112- 例4.10 用弦割法求方程 在x=1.5 附近的根。 01 23 =?? xx 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -113- 弦割法 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -114- 解: 分析:设 ,1)( 23 ??= xxxf )()( )()( 01 0110 xfxf xfxxfx x ? ? = 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -115- //用弦割法求方程的根。 #include<iostream> #include<cmath> using namespace std; float function(float x); float xIntersection(float x1, float x2); float eqRoot(float x1, float x2); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -116- void main() { float x0,x1,x; float y0, y1; do { cout<<"enter x0(1.5),x1:"<<endl; cin>>x0>>x1; y0=function(x0); y1=function(x1); } while(y0*y1>=0); x=eqRoot(x0,x1); cout<<"root :"<<x<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -117- float function(float x) { float y= (x -1)*x*x -1; return y; } float xIntersection(float x0, float x1) { float y=(x0*function(x1)-x1*function(x0))/(function(x1)- function(x0)); return y; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -118- float eqRoot(float x0, float x1) { float x, y, y0; y0=function(x0); do { x=xIntersection(x0,x1); y=function(x); if( y*y0>0) { y0=y; x0=x; } else x1=x; }while(fabs(y)>=0.000001); return x; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -119- 4. 递归调用 递归函数 是函数体内有调用函数自己的语句或 通过其它函数间接调用函数自己的函数。 递归调用 是调用递归函数而形成的一种函数调 用方式。 递归函数用于实现数学中的递归问题比较直观 方便。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -120- 经典递归调用问题 阶乘定义式: ? ? ? ?× = )!1( 1 ! nn n 0n 0n若 > = 若 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -121- int factorial( int n) { int fn if(n==0) fn = 1; else fn=n*factorial(n-1); return fn; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -122- #include <iostream> using namespace std; void main() { cout<<"enter a position integer:"; int n, n_fact; cin>>n; n_fact=factorial(n); cout<<"the factorial of "<<n<<"is: "<<n_fact<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -123- main() 调用函数 factorial(3) 1 n = 3 n_fact=? 17 结束 n = 3 n_fact=6 第一次调用函数 factorial(int n) 2 调用函数 factorial(2) 3 n = 3 fn=? 第二次调用函数 factorial(int n) 4 5 n = 2 fn=? 调用函数 factorial(1) 第三次调用函数 factorial(int n) 第四次调用函数 factorial(int n) 调用函数 factorial(0) 7 n = 1 fn=? 6 8 n = 0 fn=1 9 10 11 n = 1 fn=1 14 12 16 1315 n = 2 fn=2 n = 3 fn=6 n_fact=返回值 fn=3*返回值 fn=2*返回值 fn=1*返回值 return fn return fnreturn fnreturn fn 图4.5 factorial函数递归调用过程示意图 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -124- 递归问题的求解实际分成两个阶段: ?化简问题的递推阶段; ?达到递归终止条件得到基本情况的结果,并逐 步回推结果阶段。 递归函数的主要两部分 ?具有更简单参数的递归调用 ?停止递归的终止条件(递归终止条件) 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -125- 例4.11 整数x和y的最大公约数是x和y能够整除的最 大整数。编写一个递归函数GcommonDivisor,返 回整数x和y的最大公约数。 整数x和y的最大公约数的递归定义如下:如 果y等于0,则GcommonDivisor(x,y)为x,否则 GcommonDivisor(x,y)为GcommonDivisor(y, x%y),其中%是求模运算符。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -126- //计算 x和 y的最大公约数 (递归函数 ) #include<iostream> using namespace std; int GcommonDivisor(int x, int y); void main() { int x,y; cout<<"input x,y:"; cin>>x>>y; cout<<"the greatest common divisor of "<<x<<"and "<<y<<": " <<GcommonDivisor(x,y)<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -127- int GcommonDivisor(int x, int y) { if (y==0) return x; else return GcommonDivisor(y, x%y); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -128- ABC 例4.12 汉诺塔问题 汉诺塔问题是著名的经典问 题之 一。 传说 远东 地区 有一 座庙,僧人要把盘子(64个)从第一个柱(A )移到第三个 柱(C ),这64个盘子从上至下逐渐增大。僧人移动盘子的 规则是:每次只能移动一个 盘子 ;柱 子上 任何 时候 都要 保持大盘在下,小盘在上的 放置 方式 ;移 动过 程中 ,可 以借助于第二个柱(B ) , 暂时 放置盘子。据传说,僧人 们完成这个任务时,世界的 末日 就来 临了 。十 九世 纪法 国数学家鲁卡斯指出,完成 这个 任务 ,僧 人们 移动 盘子 的 总 次 数 为 : 。假设一秒钟移动一个,每天24小 时不停地移动,需要5800亿年。 12 64 ? 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -129- 解: 分析: 这个问题适合用递归法解决,递归函数为hanoi()。设A柱上 需要移动的盘子的个数为n,移动n个盘子可以利用递归法降阶 为移动n-1个盘子,具体步骤为: 步 骤1. 如 果 n=1, 则 盘 子 直 接 从 A柱 到 C柱 : A-->C; 执 行 步 骤 3; 否则执行步骤2。 步 骤2. 如 果 n>1, 则 将 盘 子 分 成 最 大 的 1个 和 其 它 ( n-1) 个 , 先 把(n -1)移到B柱上,再把最大的盘直接 移到 C柱 上, 然后 把B 柱上的n-1个盘移到C柱上。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -130- 即: ①将A柱上( n-1)个盘子:借助C柱,由A柱移到B柱: hanoi(n- 1,A,C,B); ②将最大的一个盘子由A柱移到C柱:move(A,C); ③再将B柱上( n-1)个盘子借助A柱移到C柱上: hanoi(n- 1,B,A,C)。 步骤3. 结束。 其中,函数move()为直接移动盘子函数。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -131- //汉诺塔问题程序 #include"iostream" using namespace std; void move(char x,char z); void hanoi(int n,char x,char y,char z); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -132- void main() { int num; cout<<"请输入盘子数: "; cin>>num; cout<<"按 \"汉诺塔 \"的规则,把 "<<num <<"个盘子从 a针搬到 c针的步骤是: "<<endl; hanoi(num,'A','B','C'); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -133- void move(char x, char z) { static int i; i++; cout<<i<<" : "<<x<<" → "<<z<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -134- void hanoi(int n,char x,char y,char z) (1){ (2) if(n==1) (3) move(x,z); (4) else { (5) hanoi(n-1,x,z,y); (6) move(x,z); (7) hanoi(n-1,y,x,z); (8) } (9)} 递归运 行的层 次 运行语 句行号 递归工作栈状态 (返址、n值、 x值、y值、z值) 1 1,2,4,5 0,2,a,b,c 2 1,2,3,9 6,1,a,c,b 0,2,a,b,c 1 6 0,2,a,b,c 2 1,2,3,9 8,1,b,a,c 0,2,a,b,c 1 8,9 0,2,a,b,c 0栈空 4.4 内联函数 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -136- 4.4 内联函数 函数调用时,系统首先要保存主调函数的相 关信息,再将控制转入被调函数,这些操作增加 了程序执行的时间开销。 C++提供的内联函数形式可以减少函数调用 的额外开销(时间空间开销),特别是一些常用的 短小的函数适合采用内联函数形式。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -137- 内联函数 内联函数的定义形式: inline 函数类型 函数名(形式参数表) { 函数体 } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -138- 例4.13 使用内联函数求三个整数中的最大值。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -139- //内联函数例 #include<iostream> using namespace std; inline int max(int x, int y, int z) { return ((x>=y) ? (x>=z ? x : z) : (y>=z ? y : z)); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -140- void main() { int a,b,c; cout<<"enter three integers:" cin>>a>>b>>c; cout<<"Maximum is "<<max(a,b,c)<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -141- 内联函数之所以能够减少函数调 用时 的系 统 空 间 和时间开销,是因为系统在编译程序时就已经 把内联 函数的函数体代码插入到相应的函数调用位置 ,成为 主调函数内的一段代码,可以直接执行,不必 再转换 流程控制权。 这样的结构,自然节省了时间和空间开销,但 使得主 调函数代码变长。 一般是只把短小的函数写成内联函数。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -142- 注意: (1)内联函数体不能包含循环 语句 、s witch语 句 和 异常接口声明。 (2)内联函数要先定义,后调用。因为编译 器 需 要 用内联函数的函数体代码替换对应的函数调用。 如果内联函数不符合要求,编译器就将内联函 数当一 般函数处理。 4.5 重载函数 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -144- 4.5 重载函数 重载函数也是函数的一种特殊情况。 C++允许几个功能类似函数同名,但这些同名 函数的形式参数必须不同,称这些同名函数为 重载函数 。 例: int max(int x, int y){return x>y?x:y; } float max(float x, float y) {return x>y?x:y; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -145- ?各重载函数形式参数的不同是指参数的 个数、类型或 顺序彼此不同,不包括参数标识符的不同。如: ① int max(int a, int b){return a>b?a:b;} ② int max(int x, int y){return x>y?x:y; } ③ int max(int x, int y, int z) {return (x>y?x:y)>z? (x>y?x:y):z; } ①②实际是一个函数,如果写在同一个文件中,编译 时会出现编译错误。若①③或②③在同一个文件中可 形成重载函数。编译器将以形式参数个数的不同来认 定和区分重载函数。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -146- #include<iostream> using namespace std; int min(int x, int y) { return x<y?x:y; } double min(double x, double y) { return x<y?x:y; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -147- void main() { int ia(10),ib(20); double da(0.1), db(0.5); cout<<"minimum of integer . is "<<min(ia,ib)<<endl; cout<<"minimum of double is "<<min(da,db)<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -148- 在使用重载函数时需要注意下面三点: (1) 编译器不以形式参数的标识符区分重载函数。例 int max(int a, int b); int max(int x, int y); 编译器认为这是同一个函数声明两次,编译时出错。 (2) 编译器不以函数类型区分重载函数。 float fun(int x,int y); int fun(int x,int y); 如果函数名和形式参数表相同,只是函数类型不同,编译器同 样认为它们是同一个函数声明两次,编译出错。 (3) 不应该将完成不同功能的函数写成重载函数,破坏程序 的可读性。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -149- 重载函数常用于实现功能类似而所处理的数据 类型不同的问题, 4.6 默认参数值的函数 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -151- 4.6 默认参数值的函数 具有默认参数值的函数是一种特殊的函数形 式, C++允许函数的形式参数有默认值。 例如:计算圆面积的函数: double CircleArea(double radius=1.0) { const double PI=3.14; return PI*radius*radius; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -152- 调用具有默认参数值的函数时,如果提供 实际参数 值,则函数的形参值取自实际参数;如果不提 供实际 参数值,函数的形参采 用默认参数值。例如调用 CircleArea函数: #include<iostream> using namespace std; void main() { cout<<CircleArea(10.0)<<endl; //提供实际参数值 cout<<CircleArea()<<endl; //不提供实际参数值 } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -153- 默认参数值函数如果有多个 参数 ,而其中只有部分参数具有默认 值,则这些具有默认值的参 数值 应该位于形参表的最右端。或者 说,形参表中具有默认参数 值的 参数的右边,不能出现没有默认 值的参数。例如: int CuboidVolume(int length=1, int width=1, int height=1); //正确 int CuboidVolume(int length, int width=1, int height=1); //正确 int CuboidVolume(int length, int width, int height=1); //正确 int CuboidVolume(int length=1, int width, int height=1); //错误 int CuboidVolume(int length, int width=1, int height); //错误 int CuboidVolume(int length=1, int width=1, int height); //错误 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -154- ?如果默认参数值函数是先声明,后定义的,则在 声明函数原型时就指定默认参数值。 ?如果函数定义在先(无需原型声明),则在函数定 义的形参表中指定默认值。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -155- 例4.14 编写具有默认函数值的函数,计算直角三角形的 面积。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -156- //使用默认形式参数值的函数编写计算直角三角形面积的程序 #include <iostream> using namespace std; float areaRATriangle( int side1 = 3, int side2 = 4); void main() { cout<< "The area of default right-angled triangle(3,4) is: “ << areaRATriangle()<<endl; cout << "The area of right-angled triangle(6,4) is:“ << areaRATriangle(6)<<endl; cout<< "The area of right-angled triangle(6,8) is:" << areaRATriangle(6, 8)<< endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -157- // 计算直角三角形面积 float areaRATriangle( int side1, int side2) { return side1* side2 /2.0; } 4.7 全局变量和局部变量 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -159- 4.7 全局变量与局部变量 在程序中,根据变量定义的位置编译器把它 们分为局部变量和全局变量。在函数内部定义的 变量、函数的形式参数为 局部变量 ,在函数外部 定义的变量为 全局变量 。 由于变量定义的位置不同,所以变量起作用 的范围也不同。我们把程序中一个标识符起作用 的范围称为其 作用域 。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -160- 局部变量 局部变量 包括在函数体内定义的变量和函数的 形式参数,它们的作用域就在函数体内,只能在 本函数内使用,不能被其它函数直接访问。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -161- #include <iostream> using namespace std; int fun1(int x, int y); int fun2(int x, int y); void main() { int a,b; cout<<"input a,b:" cin>>a>>b; cout<<fun1(a,b) <<endl; cout <<fun2(a,b) <<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -162- int fun1(int x, int y) { int n; n=x*x+y*y; return n; } int fun2(int x, int y) { int m; m=x*x-y*y; return m; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -163- 局部变量能够随其所在的函数被调用而被分配 内存空 间,也随其所在的函数调用结束而消失(释放 内 存 空 间),所以使用这种局部变量能够提高内存利 用 率 。 同时,由于局部变量只能被其所在的函数访问 ,所以 这种变量的数据安全性也比较好(不能被其它 函 数 直 接读写)。局部变量在我们实际编程中使用 频 率 最 高。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -164- 全局变量 一个C++的源文件(.cpp)可以由多个函数组 成,我们可以在函数外部定义变量,即全局变 量。全局变量能够被位于其定义位置之后的所 有函数(属于本源文件的)共用。也就是说全 局变量起作用的范围是从它定义的位置开始至 源文件结束。 全局变量的作用域是整个源文件。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -165- #include<iostream> using namespace std; int maximum; int minimum; void fun(int x,int y int z) { int t; t=x>y?x:y; maximum=t>z?t:z; t=x<y?x:y; minimum=t<z?t:z; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -166- void main() { int a,b; cout<<"input data a,b:"; cin>>a>>b; fun(a,b); cout<<"maximum="<<maximum<<endl; cout<<"minimum="<<minimum<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -167- 全 局 变 量 在程序执行的整个过程中,始终位于全 局 数 据 区 内固定的内存单元;如果程序没有初始 化全局变量,系统会将其初始化为0。 在 定 义 全 局变量的程序中,全局变量可以被位于 其 定 义 之 后的所有函数使用(数据共享),这给 编 程 者 带 来很大方便;但也因此带来数据安全性 和 程 序 可 读性不好的缺点。在我们实际编程时一 般不要随意使用全局变量。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -168- 作用域 程序中标识符的作用域也就是标识符起作用的 范围,标识符只能在其起作用的范围内被使用。 从标识符起作用的范围上划分,作用域主要分为 全局作用域和局部作用域两种。从标识符在程序 中所处的位置,又可区分作用域为块作用域、函 数作用域、类作用域和文件作用域。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -169- 块作用域 块作用域是指标识符起作用的范围为块内范 围,在块内定义的标识符具有块作用域。这个 块,可以是复合语句的块,也可以是函数定义的 函数体块。块内定义的局部变量的作用域是从变 量定义起至本块结束。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -170- #include<iostream> using namespace std; void main() { int a, b; cout<<"input a,b:" cin>>a>>b; if (a<b) { int t; t=a; a=b; b=t; } int c=a*a-b*b; cout<<a<<"*"<<a" - "<<b<<"*"<<b <<"="<<c<<endl; } t的作用域 a和 b的 作用域 c的作用域 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -171- 函数作用域 函数作用域 ? 语句标号标识符的作用域为函数范围 ? 函数体内定义的语句标号标识符具有函数作用域, 即语句标号的作用域是其所在的函数范围 。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -172- 文件作用域 文件作用域 也即全局作用域,指标识符的作用 域为文件范围。 在源文件所有函数之外声明或定义的标识符具 有文件作用域,全局变量和函数名(不包括在 其它函数内部声明原型的函数名)具有全局作 用域,起作用的范围是从声明或定义点开始, 直至其所在文件结束。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -173- #include<iostream> using namespace std; int counter1, counter2, counter3; char color; inline void getColor() { cout<<"input color of ball(r-red, y-yellow, g-green):"; cin>>color; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -174- void displayResult () { switch(color) { case 'r': counter1++; cout<<"The number of red balls : "<<counter1<<endl; break; case 'y': counter2++; cout<<"The number of yellow balls : "<<counter1<<endl; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -175- case 'g': counter3++; cout<<"The number of green balls : "<<counter1<<endl; break; default: break; } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -176- void main() { for(int k=0;k<10;k++) { getColor(); displayResult(); } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -177- 4.可见性 标识符的 可见性 ,是研究标识符在其作用域内 能否被访问到的问题。 标识符在其作用域内,能被访问到的位置称其 为 可见的 , 不能被访问到的位置称其为 不可见的 。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -178- #include<iostream> using namespace std; double pi=3.1415926; double BallVolume(double radius) { double volume=pi*radius*radius*radius*4/3; //此处使用的是全局变量 pi return volume; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -179- double CircleArea(double radius) { double area=pi*radius*radius; //此处使用的是全局变量 pi return area; } float CircleArea( float radius) { float pi=3.14f; float area=pi*radius*radius; //此处使用的是局部变量pi,全局变量pi在此处不可见。 return area; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -180- void main() { float r; cout<<"input radius: "; cin>>r; double dr=(double) r; cout<<"Ball volume: "<<BallVolume(dr)<<endl; cout<<"Circle area: "<<CircleArea(dr)<<"(double)"<<endl; cout<<"Circle area: "<<CircleArea(r)<<"(float)"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -181- C++规定:内层标识符与外层标识符同名时,内层标 识符可见,外层标识符不可见。 对 于 变 量 也 即 内 层 变 量屏蔽外层同名变量。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -182- 如果函数内的局部变量与全局变量同名,且在函数 内一定要使用这个同名全局变量,可以用全局作用域 符号(::)指定要访问的全局变量。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -183- #include<iostream> using namespace std; double pi=3.1415926; void BallVolume(double radius) { double volume=pi*radius*radius*radius*4/3; //此处使用的是全局变量 pi cout<<"Ball volume: "<<volume<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -184- void CircleArea( float radius) { float pi=3.14f; float area=pi*radius*radius; //此处使用的是局部变量 pi,全局变量 pi在此处不可见。 cout<<"Circle area: "<<area<<"(float)"<<endl; area=::pi*radius*radius; //此处使用的全局变量 pi cout<<"Circle area: "<<area<<"(double)"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -185- void main() { float r; cout<<"input radius: "; cin>>r; BallVolume(r); CircleArea(r); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -186- 关于标识符的使用 (1)标项识符应该先声明,后使用。 (2)在同一作用域中,不能声明同名的标识符。 (3)对于两个嵌套的作用域,如果某个标识符在外层 中声明,且在内层中没有同一标识符的声明,则该标 识符在内层可见;如果在内层作用域内声明了与外层 作用域中同名的标识符,则外层作用域的标识符在内 层不可见。 4.8 变量的存储类型 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -188- 4.8 变量的存储类型 一 个 变 量 在 内 存 中 存 在 的时间取决于变量的存 储类型 C++程序中使用的变量可分为四种存储类型: ?auto ? register ?Extern ?static 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -189- auto型变量 auto型变量包括函数体内部定义的局部变量、 函数的形式参数,称为自动变量。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -190- #include<iostream> using namespace std; int max(int x, int y) { return x>y?x:y; } void main() { int a,b; cout<<"input a,b:"; cin>>a>>b; cout<<max(a,b)<<endl; } auto int a,b; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -191- 自动变量因其所在的函数被调用而产 生,随其所在的函数调用结束而消失。因为 自动变量存于动态存储区,不长时间占据固 定内存,有利于内存资源的动态使用,故程 序中大量使用的都是自动变量。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -192- register型变量 寄存器型变量 定义格式: register 类型标识符 变量标识符 ; 例: register int counter; 访 问 寄 存 器 中 的 变 量 要 比访问内存中的变量速 度快,但由于寄存器数量有限,如果设置过多的 register型变量,编译器将把这些变量按普通局 部变量处理,依然放在内存空间 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -193- extern关键字 多个源文件程序结构 在 多 文 件 程 序 结 构 中 , 如果一个文件中的函数 需要使用其它文件里定义的全局变量,可以用 extern关键字声明所要用的全局变量。 //file1.cpp int x,y; void main() { … } //file2.cpp extern int x,y; void fun() { … } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -194- extern关键字 关键字e xtern提供了多文件程序结构中不同源文 件共享数据的一个途径。但实际编程中,共享数 据时要注意数据的安全性问题。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -195- 静态变量 声明变量时加上关键字 static,则 该变量为静态变 量,定义格式: static 类型标识符 变量标识符 ; 静态变量 ? 静态局部变量 static加在局部变量的定义前,则生成静态局部变量 ? 静态全局变量 static加在全局变量定义前,则形成静态全局变 量。 静态变量在程序运行期间一直在 静 态 存 储 区 占 有 固 定的存储空间。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -196- #include<iostream> using namespace std; int squareMean(int data) { static int sum(0); static int counter(0); sum+=data*data; counter++; return sum/counter; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -197- void main() { int number(1); while(number!=-1) { cout<<"input number:"; cin>>number; cout<<"the mean of square is:"<< squareMean(number)<<endl; } } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -198- 静态局部变量在其所在的函数第一次被调用时, 被初始化为一定的值,系统仅对它们作一次初始化。 如果程序中指定初始化值,则初始化为程序指定值; 如果程序在定义它们时未指定初始值,则系统将静态 局部变量初始化为0。此后静态局部变量能够保持其 在前一次函数调用结束时所获得的值,直到下次函数 调用时被修改。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -199- 静态全局变量只能在其定义文件 中 使 用 , 不 能 被 多 文 件程序结构的其它文件访问。除 此 之 外 , 静 态 全 局 变 量在定义它的文件中的用法与前面 介绍的不加static 的全局变量一样使用。静态全局 变 量 的 数 据 安 全 性 好 于普通全局变量,但不便于多文 件 程 序 结 构 不 同 文 件 之间的数据共享,实际编程时要 根 据 具 体 问 题 决 定 是 否加static。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -200- 生存期 一个变量在内存中存在的时间为变量的 生存 期。 不 同 的 存 储 类 型 的 变 量 的生存期不同,按生存 期可以将变量分为两种: ? 静态生存期变量 ? 全局变量,静态变量 ? 动态生存期变量 ? auto型变量,register型变量 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -201- 生存期 具有静态生存期的变量在程序运行期间一直存在。 具有动态生存期的变量的取决于所在的函数是否被调 用,函数被调用,动态生存 期的 变量 存在 ;函 数调 用结 束,动态生存期变量消失。 具有静态生存期的变量,如果定义时未指定初始值, 则 系统将它们初始化为0; 具有动态生存期的变量,如果未作初始化,则为随机 值。在循环结构中,使用具 有动 态生 存期 的变 量时 ,要 特别注意是否需要先赋值的 问题 ,例 如迭 代求 和( 和变 量初始化为0)或乘积(积变量初始化为1)等。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -202- 多文件结构 用C ++编写处理比较复杂的问题的程序,一般 采 用多文件结构程序,即由多个源程序分别完成不 同的子功能,这样的程序组织便于管理和维护。 C++既支持面向过程的程序设计,也支持面向对 象的程序设计。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -203- 多文件结构 在面向过程的程序设计中,为方便开发和维护程 序,将程序的功能分成相对独立的子功能,然后 用不同的源程序分别实现各个子功能。在实现每 个子功能时,一般可使用两个源文件:一个是包 含程序自定义类型、符号常量定义和函数的声明 等的头文件(*.h文件),一 个是 由实 现算 法 的 函 数构成的.c pp文件(即由函数定义构成的文 件)。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -204- 加入多文件结构图 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -205- 例4.15 模拟投币的程序,每次结果应为正面或反 面,打印HEADS或TAILS。让程序投币100次,计 算每面出现的次数并打印结果。程序应调用一个 Flip函数,该函数无参数,返回0表示正面,1表 示反面。如果程序真实,模拟投币,则每一面出 现的次数应近似相等。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -206- //Mainprog.cpp //模拟投币程序 #include<iostream> #include <cstdlib> #include <cstdio> #include <ctime> #include"flip.h" using namespace std; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -207- void main() { cout<<"now let's begin:"<<endl; int k(1); int Hcounter(0),Tcounter(0); srand( (unsigned)time( NULL ) ); 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -208- while(k<=100) { if(!Flip()) { cout<<"HEADS(k="<<k<<") "; Hcounter++; } else { cout<<"TAILS(k="<<k<<") "; Tcounter++; } if(k%4==0) cout<<endl; k++; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -209- cout<<"total:"<<k-1<<endl; cout<<"HEADS:"<<Hcounter<<endl; cout<<"TAILS:"<<Tcounter<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -210- //flip.h int Flip(); //Flip.cpp //投币 #include <cstdlib> #include <cstdio> #include <ctime> #include"flip.h" using namespace std; int Flip() { return rand()%2; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -211- 在多文件结构程序中,函数的声明和函数定 义、使用分别放在*.h文件和*.cpp文件中,使用 时要在cpp文件的最开始使用include将要用的头 文件包含进来。 4.9 编译预处理 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -213- 4.9编译预处理 编译器在编译源程序之前,先由预处理器处 理预处理指令 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -214- 预处理命令 #include 格式 #include<头文件名> #include“头文件名 ” 功能 将一个头文件嵌入(包含)到当前文件 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -215- 预处理命令 #define 格式 #define 标识符 字符串 功能 把字符串命名为标识符(用标识符代表字符串) 标识符可以表示符号常量或宏名,编写源程序时 代替 ”字符串 ”出现在程序中,编译时又被替换 为 ”字符串 ”内容。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -216- 预处理命令 #undef 格式 #undef 标识符 功能 撤销前面用#define定义的标识符 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -217- 预处理命令 #ifdef 格式 #ifdef 标 识符 语句 #endif 功能 如果已定义了 “标识符 ”,则编译 “语句 ” 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -218- 预处理命令 #ifndef 格式 #ifndef 标识符 语句 #endif 功能 如果未定义了 “标识符 ”,则编译 “语句 ” 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -219- 预处理命令 #if #elif #else #endif 功能 条件编译指令,如果某个表达式成立,则编译相应 的语句。几种形式是: #if-#endif、 #if-#else-#endif、 #if-#elif-#elif-…#endif 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -220- 预处理命令 #if #elif #else #endif 格式 #if 常量表达式 语句 #endif 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -221- 预处理命令 #if #elif #else #endif 格式 或: #if 常量表达式1 语句1 #else 语句2 #endif 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -222- 预处理命令 #if #elif #else #endif 格式 或: #if 常量表达式1 语句1 #elif常量表达式2 语句2 ┇ #else 语句n #endif 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -223- 例4 .16 从键盘输入数据完成复数运算(加、减、乘、 除、模、辐角)。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -224- //myFile.cpp #include<iostream> using namespace std; #include "complex.h" void main() { char ch; do { displayMessage(); cin>>ch; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -225- switch(ch) { case '1': Add(); break; case '2': Minus(); break; case '3': Multiply(); break; case '4': Divide(); break; case '5': Modul(); break; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -226- case '6': amplitude(); break; default: break; } cout>>"continue?(y/n)" cin>>ch; }while(ch=='y'); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -227- inline void displayOperation() { cout<<"Enter your choice:">>endl; cout<<"add(1)">>endl; cout<<"minus(2)">>endl; cout<<"multiply(3)">>endl; cout<<"divide(4)">>endl; cout<<"modul(5)">>endl; cout<<"amplitude(6)">>endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -228- //complex.h #ifndef COMPLEX_H //防止此文件被多次嵌入 #define COMPLEX_H #define DEBUG_COMPLEX //调试时为显示某些中间结果; //在最终版 本,如果不需要,可将此句注释掉。 #define PI 3.14 //定义符号常量 PI,用 PI表示常量 3.14 #define ANGLE180 180 //定义符号常量 void Add(); void Minus(); void Multiply(); void Divide(); void Modul(); void amplitude(); #endif 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -229- //complex.cpp #include<iostream> #include<cmath> #include"complex.h" using namespace std; void Add() { double real1,real2,imag1,imag2; cout<<"input real part and imaginary part of 1rd complex number:"; cin>>real1>>imag1; cout<<"input real part and imaginary part of 2rd complex number:"; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -230- cin>>real2>>imag2; double real,imag; real=real1+real2; imag=imag1+imag2; cout<<"sum:"<<real<<"+"<<imag<<"i"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -231- void Minus() { double real1,real2,imag1,imag2; cout<<"input real part and imaginary part of 1rd complex number:"; cin>>real1>>imag1; cout<<"input real part and imaginary part of 2rd complex number:"; cin>>real2>>imag2; double real,imag; real=real1-real2; imag=imag1-imag2; cout<<" difference:"<<real<<"+"<<imag<<"i"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -232- void Multiply() { double real1,real2,imag1,imag2; cout<<"input real part and imaginary part of 1rd complex number:"; cin>>real1>>imag1; cout<<"input real part and imaginary part of 2rd complex number:"; cin>>real2>>imag2; double real,imag; real=real1*real2-imag1*imag2; imag=imag1*real2+imag2*real1; cout<<" product:"<<real<<"+"<<imag<<"i"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -233- void Divide() { double real1,real2,imag1,imag2; cout<<"input real part and imaginary part of 1rd complex number:"; cin>>real1>>imag1; cout<<"input real part and imaginary part of 2rd complex number:"; cin>>real2>>imag2; double real,imag; double temp; temp=real2*real2+imag2*imag2; 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -234- #ifdef DEBUG_COMPLEX cout<<"temp="<<temp; //调试版本显示 temp,便于分析错误 #endif if(temp>0.000001) { real=(real1*real2+imag1*imag2)/temp; imag=(imag1*real2-imag2*real1)/temp; cout<<"quotient:"<<real<<"+"<<imag<<"i"<<endl; } else cout<<"divided by 0"<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -235- void Modul() { double real,imag; cout<<"input real part and imaginary part:"; cin>>real>>imag; double modual=sqrt(real*real+imag*imag); cout<<"modual:"<<modual<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -236- void amplitude() { double real,imag; cout<<"input real part and imaginary part:"; cin>>real>>imag; double amp; if(fabs(real)>0.000001) { amp=atan(imag/real); } else amp=PI/2; amp=ANGLE180*AMP/PI;//弧度转换为度显示 cout<<"amplitude:"<<amp<<endl; } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -237- #define指令还可以定义带参数的宏 ,例如 : #include<iostream> using namespace std; #define MIN(a,b) (a<b?a:b) void main() { int x,y; cout<<”input x,y:”; cin>>x>>y; cout<<”minimum:”<<MIN(x,y); } 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -238- 总结 本 章 详 细 介 绍 了 关 于 函数的知识,重点介绍了对函数的 引 入 、 定 义、原型声明、函数的参数及函数调用,函数是 实 现 算 法 的基本单位,函数的设计和使用是学习程序设计 必须掌握的基本知识。 函数还有一些特殊的形式,如递归函数、内联函数、 具 有 默 认 参 数值的函数等。递归函数便于递归问题的实现, 尤 其 适 合 数学上的一些递归问题。用递归函数实现算法, 优 点 是 思 路简单;缺点是函数执行效率一般不高,因为频 繁 的 递 归 调用造成的进栈出栈很花时间。内联函数等函数 的特殊形式,使我们在利用函数实现算法时更方便灵活。 本章介绍的变量的存储类型以及标识符的作用域等概 念,也是我们必须掌握的基本知识。 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -239- 作业 2005-4-28 北京邮电大学电信工程学院计算机技术中心 -240-