同学们好!本期C++程序设计将介绍非成员函数的定义与调用、函数原型、递归调用、函数重载、作用域和编译预处理等内容。
01
函数的基本概念
一、库函数与用户自定义函数
库函数是原型说明在特定的头文件中的预定义的函数。例如,头文件中处理输入输出的函数,头文件中数学计算的函数。
用户自定义函数是根据需要自己定义的函数。
二、函数的参数
函数在定义时定义形式参量,调用时提供实际参量。函数形参表可为空,即()或(void)。
三、函数的返回类型
函数在定义时确定了返回值类型。如果为void,则函数结束时不会返回值;否则,要返回特定类型的值。
例如,平方根函数sqrt(2)返回一个double类型的值,则sqrt(2)可以作为double变量参与运算。
四、main函数
一个C++程序从main函数开始执行,最后由main函数返回以结束程序。main函数可以调用其他的函数,其他的函数完成后返回main函数。程序中有且仅有一个main函数。
02
函数的定义与调用
函数的定义与调用有两种方法。
一、定义函数在前,调用函数在后。
例:
#includeusing namespace std;bool isPrime(unsigned n){ //函数定义 if(n<2) return false; //以下略}int main(){ int n; cin>>n; if(isPrime(n)) //函数调用 //以下略 return 0;}
二、先说明函数原型,再调用函数,并在其他地方定义函数。一个函数只能定义一次,但函数说明可以多次出现。
#includeusing namespace std;bool isPrime(unsigned n); //函数说明int main(){ int n; cin>>n; if(isPrime(n)) //函数调用 //以下略 return 0;}bool isPrime(unsigned n){ //函数定义 if(n<2) return false; //以下略}
函数的定义中的注意事项:
1、函数的名字与其形参表作为一个整体,称为函数的基调。不可出现多个基调相同的函数。
2、return语句用于返回某个值,且结果须与函数返回类型相同。可以有多条return语句,但每次调用只能有一个return语句执行。
3、形参可以指定缺省值,但是须为形参表中最右边的形参。
4、无返回函数体可为空,但花括号{ }不可省。
5、main函数应返回int类型,return 0; 结束。
函数的调用中的注意事项:
1、一般函数调用时的实参与被调用函数的形参的类型和数量一致。若形参带缺省值,则实参个数可能少于形参个数。
2、函数调用执行时,每个形参和局部变量都分配独立的存储单元,用于接收实参传递的数据。函数调用结束时,这些存储单元将被回收。
3、以值传递是指调用函数时实参给形参赋值的参数传递方式。形参值在函数体内的变化对实参本身没有影响。(这一点须牢牢掌握,后面在学习指针时会遇到其他的参数传递方式,要能够区分。)
03
函数重载
函数重载是指多个函数有相同的名称,但有不同的形参。重载的多个函数根据函数的基调进行区分。函数调用时,根据提供的实参类型决定调用哪一个函数。
若多个函数基调相同,仅返回类型不同,则不可以作为函数重载,编译报错。
函数重载的调用过程如下:
有函数重载:
void print(char);void print(int);void print(double);
1、尝试在函数重载的定义中寻找严格匹配。若有,则调用。
例如:
print('a'); //调用void print(char)
2、若不能,则通过整型提升转换寻求最佳匹配。
例如:
unsigned char a=3;print(a); //调用void print(int)
3、若不能,再通过标准转换寻求最佳匹配。
例如:
print(1.0f); //调用void print(double)
4、若不能,再通过用户定义类型转换寻求最佳匹配。
用户自定义类型包括结构体和类等,暂不讨论。
5、若不能,则编译报错。
注意函数调用时不可产生二义性,否则编译报错。
04
函数的嵌套调用
函数不允许嵌套定义,但函数在定义时,可以调用另外的函数。嵌套的函数结果逐层返回。
例如:
int mian(){ int a = f(); //下略 return 0;}int f(){ int a = g(); //下略 return 1;}int g(){ //略 return 2;}
main函数中调用函数f(),函数f()中调用g(),结果层层返回。
05
函数的递归调用
函数的递归调用是指在定义函数中嵌套调用自己。需要注意:递归函数一般先判断递归结束条件,结束条件确定何时不需要进一步递归调用,而是直接return结果结束本层函数,返回上一层;然后再进行递归调用。
例:
void original(int n){ if(n==0) //A return; else original(n/10); //B cout<10<<}
函数original(int n)的作用是从高位到地位依次输出形参n的各位数。
A行首先判断递归结束的条件。
B行继续执行递归调用。
C行是:在里面一层递归调用结束之后,出来继续执行下面的C行,输出本位数。
06
变量的作用域
对于一个块(由同一对花括号{}括起来的部分)内说明的标识符,其作用域始于其说明,终于块的结尾,可被内存块访问。
内层块中可以说明与外侧块中定义的同名的标识符,在内层块中优先访问内层块中的。
具有块作用域的变量称为局部变量,存储在栈中。按照“先进后出”的规则,外层块中的变量先入栈,内层块中的变量后入栈;内层块结束时,内层的变量先出栈,因此此后外层块中不可再访问内层中的变量。
07
特殊的函数
一、带缺省值的形参
1、形参的缺省值必须再形参表的最后面。从第一个带缺省值的形参开始,其右边所有形参都应指定缺省值。
2、调用函数时,若没有提供相应的实参,形参就使用指定的缺省值。
3、重载函数时同名函数形参缺省值可能会发生二义性错误。
例如:
void f(int a, int b=1, int c=0);void f(int a, int b);int main(){ int x=0,y=1,z=2; f(x,y); //报错 return 0;}
二、inline函数
用关键字inline修饰的函数称为内联函数,该函数的所有调用都被替换位其函数体。
内联函数使得目标代码变得更长,以换取更高的执行效率,用空间换时间。
08
编译预处理
一、包含头文件
#include 是包含已有的头文件。
#include"文件名" 是包含自己写的头文件。
二、无参宏
宏是一个命名的字符串。用名称代替所要表示的一串内容。仅对宏名作简单串替换,不作计算,也不作语法检查。宏定义时不以分号; 结尾,可以以分号; 结尾,但分号会算作宏内容的一部分。
例如:
#define PI 3.1415926
在编程过程中,我们只需PI即可代替小数3.1415926。
double p = PI;
等价于
double p = 3.1415926;
宏名在字符串中不展开,即
cout<<"PI";
输出的是PI,不是3.1415926。
三、有参宏
有参宏的定义格式为
#define (形参表)
宏名与左圆括号之间不可有空格。形参表中仅给出形参名称,例如:
#define V(a,b,c) a*b*cvolumn=V(3+5,8-3,6)
需注意,这里展开后并不是(3+5)*(8-3)*6,而是3+5*8-3*6。即宏展开时不作运算,只替换对应的内容,注意与函数区分开。
编辑 | 牟育生
审核 | 李天意