1.4 函数与预处理
1.4.1 概述
程序总是从main函数开始执行。关键字void表示本函数没有返回值,是空类型。函数不能嵌套调用,但互相之间可以调用(main函数不能被调用)。在使用函数前一定要进行声明,除非函数的定义在其使用之前。
1.4.2 定义函数的一般形式
定义无参函数的一般形式 | 定义有参函数的一般形式 |
类型标识符 函数名() { 声明部分; 语句; } | 类型标识符 函数名(形参列表) { 声明部分; 语句; } |
1.4.3 形参和实参
在定义函数时函数名后面括号中的变量名称为形参,在主函数中调用到该函数时,函数名后面括号中的参数称为实参。实参和形参间的数据传递是单向的,只能由实参传给形参。在编译时,二者的存储单元是不同的单元。调用结束后,形参单元释放,实参单元仍保留原值。实参与形参的个数要匹配,它们按照顺序,一对一地传递数据。
函数的返回值通过函数中的return语句获得。
1.4.4 函数的调用和声明
函数名(实参列表); //函数调用的一般格式
在一个函数中调用另一个函数所需要的条件:(1)被调用的函数必须是已经存在的函数(是库函数或者用户自己定义的函数)。(2)当使用自己定义的函数,且该函数与调用它的函数在同一个程序单位内,若函数定义在函数调用之后,则需在调用该函数之前对它做声明。如果被调用的函数的定义出现在主调函数之前,可以不必加声明。函数声明只比函数定义的首部多一个分号。
Eg:
int main()
{
int max(int x, int y);//对max函数作声明
int a,b;
max(a,b)
}
int max(int x,int y)//定义max函数
{……}
其中int max(int x, int y);是对被调用的max函数作声明,在函数声明中也可以不写形参名,而只写形参的类型,如:
int max(int , int );
这种函数声明称为函数原型。函数原型的一般形式有以下几种:
① 函数类型 函数名(参数类型1,参数类型2,…);
② 函数类型 函数名(参数类型1 参数名1,参数类型2 参数名2,…);
由于编译系统不检查参数名,因此参数名是什么都无所谓,但是第一种是基本形式。
1.4.5 内置函数
C++提供一种高效率的方法,即在编译时将锁掉用的函数的代码直接嵌入到主调函数中,而不是将流程转出去。这种嵌入到主调函数中的函数称为内置函数。只需在函数首行左端加一个关键字inline即可。内置函数不能包括复杂的控制语句,如循环语句和switch语句。
Eg:
inline int max(int,int);//声明内置函数max
int main()
{
int a,b;
max(a,b)
}
inline int max(int x,int y)//可以同时加inline,也可以仅在声明时加inline,效果是一样的
{……}
1.4.6 函数的重载(静态多态性、静态关联)
C++允许使用同一个函数名定义多个函数,这些函数的参数个数和参数类型不同,这就是函数的重载。即对一个函数名赋予新的意义,是一个函数名可以多用。运算符重载本文中不再赘述,详情可参考《C++程序设计》第10章。函数的重载还是静态多态性的一种体现,多态性将在后面的章节中讲到。
例 1.4.1
参数类型不同的情况 | 参数个数不同的情况 |
例 1.4.1a 求三个数中的最大数(分别考虑整数、双精度数和长整数的情况) | 例 1.4.1b 写一个程序,用来求2个整数或者3个整数中的最大值。 |
|
|
1.4.7 函数模版
函数的重载在程序中仍然需要分别定义每一个函数,比较麻烦。为了解决这个问题,,可以使用函数模版。所谓函数模版,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。凡是函数体相同的函数均可使用该模版,不必再定义多个函数。在调用时,系统会根据实参类型取代模版中的虚拟类型。函数模版可以很好的解决函数重载中的参数类型不同的情况时的简化。
定义函数模版的一般形式为:
template<typenameT>
例 1.4.1 a可以改写如下:
#include<iostream>
using namespace std;
template <typename T>
T max(T a,T b,T c)
{
if(b>a) a=b;
if(c>a) a=c;
return a;
}
int main()
{
int i1=185,i2=-76,i3=567,I;
double d1=56.87,d2=90.23,d3=-3214.78,d;
long g1=67854,g2=-912456,g3=673456,g;
i=max(i1,i2,i3);
d=max(d1,d2,d3);
g=max(g1,g2,g3);
cout<<”i_max=”<<i<<endl;
cout<<”d_max=”<<d<<endl;
cout<<”g_max=”<<g<<endl;
return 0;
}
1.4.8 有默认参数的函数
C++提供了一个简单的方法,给形参一个默认值,这样形参就不一定要从实参取值了。若例1.4a的函数声明如下:
int max(int a,int b=10,int c=5);//表示指定b的默认值为10,c的默认值为5
若有多个形参,可以使每一个形参都有一个默认值,也可以只对其中一部分形参指定默认值。
当调用函数时:max(8)相当于max(8,10,5),max(11,3)相当于max(11,3,5)。实参与形参的结合是从左至右的,因此指定默认值的参数必须放在形参列表的最右端。
int max(int a=10,int b,int c=5);//错误,默认参数必须在最右端
使用带默认参数的函数时要注意以下2点:①若函数定义在函数调用之前,则在函数定义中需给出默认值。如果函数定义在函数调用之后,则需在函数调用前声明带默认值的函数。②一个函数不能既作为重载函数,又作为有默认参数的函数。
1.4.9 函数的嵌套调用和递归调用
C++不允许在一个函数中完整地包含另一个函数,这个叫嵌套定义。但是C++允许嵌套调用,即在调用一个函数的过程中,又调用另一个函数,如图1.2。
图 1.2 两层嵌套调用
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。包含递归调用的函数称为递归函数。
Eg:
int f(int x)
{
int y,z;
z=f(y); //在调用函数f的过程中,又要调用f函数
return(2*z);
}
程序中出现的递归调用次数应该是邮箱的,这可以通过if语句来控制,当满足某一条件是才执行递归调用,否则不再继续。
例 1.4.2
用递归法求n!。
分析:和,可以得到下面的递推公式
则程序如下:
#include<iostream>
using namespace std;
long fac(int); //函数声明
int main()
{
int n;//为所求的阶乘
long ;//用来存放n的阶乘
cout<<”please input an integer:”;
cin>>n;
y=fac(n);
cout<<”n!=”<<y<<endl;
return 0;
}
long fac(int a) //定义阶乘函数
{
long f;
if (n<0)
{
cout<<”n<0,data error!”<<endl;
f=-1;
}
else if (n==0||n==1) f=1;
else f=fac(n-1)*n;//n>1时,进行递归调用
return f;
}
1.4.10 全局变量、局部变量以及变量的存储类别
局部变量 | 全局变量 |
定义:在一个函数内部定义的变量称为内部变量,它只在本函数范围内有效,在此函数外是无法使用这些变量的。同样,在复合语句中的变量仅在本复合语句范围内有效。 | 定义:在函数内定义的变量叫局部变量,而在函数之外定义的变量称为全局变量。全局变量的有效范围从定义的位置开始到本源文件结束。 |
说明:①主函数中定义的变量仅在主函数中有效,不会在整个文件或程序中有效。②不同函数中可以使用同名变量。③形参也是局部变量。 | 说明:①全局变量的作用是增加函数间数据联系的渠道。②全局变量会降低函数通用性。③在同一个原文件中,若全局变量与局部变量同名,则在局部变量作用范围内屏蔽全局变量。 |
自动变量 | auto | 数据存储在动态存储区中,函数调用结束就释放这些空间,C++默认指定所有变量为auto |
静态局部变量 | static | 局部变量的值在函数调用结束后保留原值,所占存储单元不释放,在下一次调用时,该变量保留上一次函数调用结束时的值。且其他函数不能引用静态局部变量。 |
寄存器变量 | register | 将变量的值存储在CPU寄存器中,可提高执行效率。 |
全局变量(又称外部变量) | extern | 用extern声明全局变量,以便扩展全局变量的作用域。若定义点前的函数想引用该全局变量,则在引用之前需用关键字extern对该变量做外部变量声明,这种声明称为提前引用声明。 |
例 1.4.3 下列程序输出什么。
#include<iostream>
using namespace std;
int f(int a)
{
auto int b=0;//定义b为自动变量
static int c=3;//定义c为静态局部变量
b=b+1;
c=c+1;
return a+b+c;
}
int main()
{
int a=2,i;
for (i=0;i<3;i++)
cout<<f(a)<<” ”;
cout<<endl;
return 0;
}
运行结果为:7 8 9
一个C++程序可以由多个源程序文件组成,若在两个文件中要用到同一个外部变量num,则不能在两个文件中个自定义num。正确的做法是:在一个文件中定义外部变量num,在另一个文件中用extern对num作外部变量声明。若希望变量num仅能被本文件引用而不能被其他文件引用,需在定义外部变量时加static。这样即便另一个文件中有extern int num,它也无法使用num(用static声明静态外部变量)。
1.4.11 内部函数和外部函数
内部函数 | 外部函数 |
定义:如果一个函数仅能被本文件中的其他函数调用,则称它为内部函数。使用内部函数可以使函数只局限于所在文件。若果在不同的文件中有同名函数,也不会相互干扰。 | 定义:如果一个函数能被其它文件中的其他函数调用,则称它为外部函数。若省略关键字extern,则默认为外部函数。 |
格式: static 类型标识符 函数名(形参表) static int fun(int a,int b); | 格式: extern int fun(int a,int b); |
例 1.4.4 输出两个整数的最大值,用外部函数实现。
file1.cpp
| file2.cpp
|
总结一下extern关键字和static关键字的功能:
static:声明静态局部变量,声明静态外部变量,声明内部函数。
extern:声明全局变量,声明外部函数。
1.4.12 预处理命令
一、宏定义
一般形式:
#define 标识符 字符串
#define PI 3.1415926
#define宏名(参数表) 字符串
#define S(a,b) a*b
二、文件包含
#include”file1.cpp”。
#include<文件名>和#include”文件名”的区别:
用尖括号时,系统到系统目录中寻找要包含的文件,如果找不到则编译系统会报错。系统目录指的是存放C++系统的目录。一般情况下,库函数和C++编译系统是存放在同一个目录下的。因此如果要包含的是C++系统提供的头文件,宜采用这种形式。
如果用户自己编写的文件往往存放在自己指定的目录中,此时用双撇号形式,在双撇号中给出文件路径和文件名。这样编译系统会先在该目录下寻找该文件,若果找不到,再到系统目录中去寻找。