530 likes | 686 Views
C++ 语言程序设计 第五章 C++ 函数 (第一讲). 学习目标. . 掌握函数定义和函数调用的方法,会使用函数原形和头文件; 了解参数传递的原理,熟练掌握通过传值方式向函数传递数据的方法,掌握使用数组参数和可选参数; 掌握函数重载; 了解函数和变量作用域;. 第一讲主要内容. C++ 函数的概念 函数的定义 函数的调用 函数原形与头文件 函数调用中的参数传递 内联( inline )函数 函数重载与名字混成 函数和变量的作用域 函数模板. . x. 0. 0. -1. y. 1. 1. -2. 4. 2. x 2.
E N D
学习目标 • 掌握函数定义和函数调用的方法,会使用函数原形和头文件; • 了解参数传递的原理,熟练掌握通过传值方式向函数传递数据的方法,掌握使用数组参数和可选参数; • 掌握函数重载; • 了解函数和变量作用域;
第一讲主要内容 C++函数的概念 函数的定义 函数的调用 函数原形与头文件 函数调用中的参数传递 内联(inline)函数 函数重载与名字混成 函数和变量的作用域 函数模板
x 0 0 -1 y 1 1 -2 4 2 x2 C++函数的概念.. • —— 函数的概念源于数学,是一个集合到另一个集合的映射。例如对于数学中的函数 y=f(x)=x2:
C++函数的概念.. • 前例用C++函数表示就是:int y(int x){ return x*x;} 对应因变量y 对应自变量x 对应映射规则X2
… (5,9) (7,5) (5,5) … (7,8) (9,7) … 5 7 C++函数的概念.. { • —— 再如,对于数学中的函数 • m (m<=n) • min=f(m,n)= • n (m>n)
C++函数的概念.. 此例用C++函数表示就是: int min(int m,int n){ if(m<=n) return m; else return n;}
函数的定义.. • — 定义格式: • 类型修饰符函数名(形式参数表)函数体 一个变量表,用来接收调用程序传来的数据。 形式参数简称形参。 规定函数值(函数返回值)的类型,如 int、double 等。 用{}括起来的语句序列,形同一个复合语句。
函数的定义.. —— 一般情况下,函数体中应以return 表达式;结束函数的运行。 —— 无参函数:形式参数表为空(或为 void):double PI(void){ return 3.1415926;}
函数的定义.. • —— 无返回值的函数:返回值类型规定为 void 的函数。例如: • void Hi(void){ cout<<"Hi!"<<endl; return;} 可以不要 无返回值的函数通过副作用体现其功能
函数的定义 —— 主函数(main 函数)可以声明为有参函数,也可以声明为无参函数;可以声明为有返回值函数,也可以声明为无返回值函数。
函数的调用.. —— 调用格式:函数名(实在参数表) 实在参数简称实参,每一个实参就是一个表达式; 实参的个数必须与形参的个数相同; 实参表达式的类型应当与对应形参变量的类型相同; 调用时,实参表达式的值被传送给函数,为对应的形参变量所接收。
函数的调用.. —— 作为表达式的函数调用其本身就是一个表达式,或者是某个表达式中的一个子表达式,它的值将参与整个表达式的求值过程。例如:s=min(5,7)*10; 无返回值函数不能作为表达式加以调用
函数的调用.. —— 作为语句的函数调用 在一个语句中单独使用函数调用,通过函数的副作用发挥其作用。例如:Hi(); //显示 Hi!min(5,7);//无意义
函数的调用.. • 若调用的是有返回值的函数,其返回值被舍弃不用; • 函数的作用通过其副作用体现 • 对于无副作用的函数,作为语句调用是没有意义的。
函数的调用.. —— 递归调用 函数直接或间接地调用自身。递归调用是实现递归算法的手段。
函数的调用.. —— 递归算法及递归调用举例: long fact(long n) { if(n<=1L) return 1L; return n*fact(n-1L); }
函数的调用.. • —— 递归算法都必须满足的的三个条件: • 1.有明确的结束递归的条件:n=0或n=1,此条件下可以直接得出结果:1 ; • 2.要解决的问题可以转化为相对简单的同类型的问题:n!可转化为 n·(n-1)!,而 (n-1)! 就是比 n! 稍简单的同类型的问题; • 3.随着问题的逐次转换,最终能达到结束递归的条件:算法中的参数 n 在递归过程中的逐次减少,必然会到达 n=0 或 n=1 的时刻。
函数原形与头文件.. ——函数原形 格式:类型修饰符函数名(形式参数表) 作用:提供完成函数调用所必需的接口 信息 使用位置:函数调用之前(程序首部) 什么情况下必须使用?函数的定义出现在函数调用之后,或在别的文件中。
函数原形与头文件.. —— 头文件 是扩展名为 .h 的含有函数原形 (或 其他声明)的纯文本文件; 通过使用#include命令可把头文件中的内容包含(插入)到程序文件中; 头文件有利于维护函数原形的一致性;
函数原形与头文件.. 通过使用头文件可使函数的实现(定义)和函数的使用(调用)分离,从而提高函数的重用性 通过使用头文件可把系统分解成多个程序文件,以便于多人分工合作 使用C/C++标准函数必须包含相应的头文件,如: #include<math.h>
函数调用中的参数传递.. —— C++参数传递的基本方式——“传值” double DOUBLE(double n){ n*=2; //形参变量n的值被改变return n;}void main(){ double m=7.0; cout<<endl<<m; cout<<endl<<DOUBLE(m); cout<<endl<<m;} 运行
函数调用中的参数传递.. —— 数组参数 可以把整个数组作为参数传递给函数。 在说明一个一维数组参数时,可以不限定数组元素的个数;在说明一个二维数组参数时,可以不限定数组的行数(数组参数的第一维可不限定)。 在调用具有数组参数的函数时,须以单独的数组名作为实在参数。
函数调用中的参数传递.. • 例5.5:设计函数sum,它计算并返回作为参数的数组中所有元素的合计值: • int sum(int array[],int size);
函数调用中的参数传递.. (例5.5) 运行 int sum(int array[],int size) { int s=0; for(int i=0;i<size;i++) s+=array[i]; return s; } void main() { int v1[]={1,2,3,4,5}; cout<<sum(v1,5)<<endl; int v2[]={1,2,3,4,5,6,7,8}; cout<<sum(v2,8)<<endl; }
函数调用中的参数传递.. • 例5.6:设计函数sumAll,计算 5 列二维数组 data 的每一行元素的合计,并把结果存入一维数组 result 的对应元素中:void sumAll(int data[][5], int result[], int rows);
函数调用中的参数传递.. (例5.6) void sumAll(int data[][5], int result[], int rows) { for(int i=0;i<rows;i++){ result[i]=0; for(int j=0;j<5;j++) result[i]+=data[i][j]; } }
函数调用中的参数传递.. (例5.6续): 运行 void main() { int v[][5]={{1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7}}; int s[3]; sumAll(v,s,3); for(int i=0;i<3;i++) cout<<s[i]<<‘ ’; }
函数调用中的参数传递.. 注意:仅用行下标引用一个二维数组时,所引用的是对应于该行的那个一维数组。用此方法,可把二维数组的某一行作为一维数组使用。如例5.7: void sumAll(int data[][5], int result[], int rows) { for(int i=0;i<rows;i++) result[i]=sum(data[i],5); }
函数调用中的参数传递.. ——可选参数 通过设定默认值,可把一个形参说明为可选参数,说明格式是:类型修饰符变量名=默认值例如:int f(int a,char b,char c='Z', char *s=”READY”);
函数调用中的参数传递.. 只能把形参表中最后的若干形数说明为可选参数; 在调用具有可选参数的函数时,可以不给出对应于可选参数的实参,编译系统会自动以设定的默认值作为实参; 被省略的实参必须是最后若干个可选参数所对应的实参;
函数调用中的参数传递.. 例如对于 int f(int a,char b,char c='Z',char *s=”READY”); f(3,'a','b') f(3,'a','b',”READY”) f(3,'a') f(3,'a','Z', ”READY”) f(3,'a',”NOT_READY”) ?
函数调用中的参数传递.. • 可选参数的说明只与函数调用的代码生成有关,而对函数定义及其代码生成毫无影响。因此:可选参数应该在函数原形中进行说明
函数调用中的参数传递.. 例5.8:设计函数area_of_fan,它根据给出的半径和角度计算扇形面积。
函数调用中的参数传递.. • #define PI 3.1415926 • double area_of_fan(double radius, double angle=360.0); • void main(){ • cout<<endl<<area_of_fan(5.0,90.0); • cout<<endl<<area_of_fan(5.0); • cout<<endl<<area_of_fan(5.0,360.0); • } • double area_of_fan(double radius, double angle){ • return PI*radius*radius*angle/360.0; • } 运行
内联(inline)函数.. • 在一般函数定义前面加上保留字inline,该函数即被说明为内联函数。例如:inline int add2(int n){ return n+2;} • C++有可能直接用函数体代码来替代对函数的调用,这一过程称为函数体的内联展开。
内联(inline)函数 • 在函数定义前加inline,只是向C++编译系统提出内联展开的请求,编译系统有可能不理睬这种请求。 • 内联函数的说明属于参考性说明,因此应该放置在头文件中。 • 内联函数的作用:提高运行效率。 • 内联函数适合于简单的、使用频繁的小函数。
函数重载与名字混成.. • —— 关于函数重载 • C++允许定义同名的函数,称为函数重载。 • 重载的函数必须在参数的数量上或类型上与其他同名函数有所不同。 • 函数重载的作用:使若干密切相关的函数有一个统一的调用接口。
函数重载与名字混成.. —— 关于名字混成(name mangling) 编译系统为区分同名函数而用来生成内部函数名的一种特殊方法; 内部函数名不但包含了外部函数名本身,还包含了函数的参数信息。
函数重载与名字混成.. • 例如,对于重载函数int fun(double n);double fun(double n,int m);void fun(int m,char n[]); C++Builder中的内部名:@fun$qd@fun$qdi@fun$qipc Visual C++中的内部名: ?fun@@YAHN@Z?fun@@YANNH@Z?fun@@YAXHQAD@Z
函数重载与名字混成.. — 用 extern "C" 可阻止编译系统进行名字混成: extern "C" void Cfunc(int); extern "C"{ void Cfunc1(int); void Cfunc2(int); void Cfunc3(int);}; extern "C"{ #include"locallib.h"};
函数重载与名字混成.. ——要注意可选参数对函数重载的影响。假定已经定义了: int f(int k,int m=0, double d=0.0); 合法的调用:f(3,5,6.7)、f(3,5)、f(3)不能重载: int f(int); int f(int,int); int f(int,int,double); int f(int,double=0.0);等
函数和变量的作用域.. • —— 函数的作用域 • 全局作用域(跨文件作用域):同一应用系统的任何程序文件中的任何函数都可以调用。 • 文件作用域:只允许同一程序文件中的函数调用,函数定义前须加上 static 修饰。
函数和变量的作用域.. —— 变量的作用域 跨文件作用域 文件作用域 块作用域 • —— 变量的生存期 • 静态(初值0) • 自动 • 动态
函数和变量的作用域.. —— 全局变量 定义位置:函数外 作用域: 跨文件(未加 static 修饰) 文 件(加 static 修饰) 生存期:静态 作用:记录应用系统的全局信息,是函数之间交换数据信息的媒介。
函数和变量的作用域.. extern 说明:要访问另一个文件中定义的跨文件作用域的全局变量,必须进行extern 说明,如:extern int var;
函数和变量的作用域.. —— 局部变量 定义位置:函数内的某一个块 (复合语句)中 作用域:块 生存期: 静态(加 static 修饰) 自动(加 auto 修饰, 可省略) 作用:作为所在块的临时变量。
函数和变量的作用域.. —— 寄存器变量 一种用 register 修饰的局部变量 这种变量中的数据是存储于寄存器中的,在使用过程中不用访问内存,从而大大提高了变量的存取速度; 但编译系统在判断不可能时,仍会将之处理为一般的自动变量。 48
函数和变量的作用域.. • — for语句中定义的变量的作用域 • 请判断下面语句序列的正误:for(int i=0;i<=10;i++) cout<<i<<','; cout<<i; • 在旧标准中这是正确的,即 for 语句中定义的 i 的作用域在 for 语句所在的块中。 • 在新标准中这是错误的,即 for 语句中定义的 i 的作用域只局限于该 for 语句。
函数和变量的作用域.. —— 静态自动变量应用实例(例5.9) #define RESET true unsigned counter(bool reset=!RESET ){ static unsigned cnt=0; if(reset) return cnt=0; return ++cnt; } 静态自动变量可用于在同一函数的不同调用之间交换数据信息