C++函数详细讲解


第4章 函数与预处理 4.1 概述 4.2 定义函数的一般形式 4.3 函数参数和函数的值 4.4 函数的调用 *4.5 内置函数 *4.6 函数的重载 *4.7 函数模板 *4.8 有默认参数的函数 4.9 函数的嵌套调用 4.10 函数的递归调用 4.11 局部变量和全局变量 4.12 变量的存储类别 4.13 变量属性小结 4.14 关于变量的声明和定义 4.15 内部函数和外部函数 4.16 预处理命令 一个较大的程序不可能完全由一个人从头至尾地完 成,更不可能把所有的内容都放在一个主函数中。 为了便于规划、组织、编程和调试,一般的做法是 把一个大的程序划分为若干个程序模块(即程序文 件),每一个模块实现一部分功能。不同的程序模 块可以由不同的人来完成。在程序进行编译时,以 程序模块为编译单位,即分别对每一个编译单位进 行编译。如果发现错误,可以在本程序模块范围内 查错并改正。在分别通过编译后,才进行连接,把 各模块的目标文件以及系统文件连接在一起形成可 执行文件。 4.1 概述 在一个程序文件中可以包含若干个函数。无论把一 个程序划分为多少个程序模块,只能有一个main函 数。程序总是从main函数开始执行的。在程序运行 过程中,由主函数调用其他函数,其他函数也可以 互相调用。在C语言中没有类和对象,在程序模块 中直接定义函数。可以认为,一个C程序是由若干 个函数组成的,C语言被认为是面向函数的语言。 C++面向过程的程序设计沿用了C语言使用函数的 方法。在C++面向对象的程序设计中,主函数以外 的函数大多是被封装在类中的。主函数或其他函数 可以通过类对象调用类中的函数。无论是C还是 C++,程序中的各项操作基本上都是由函数来实现 的,程序编写者要根据需要编写一个个函数,每个 函数用来实现某一功能。因此,读者必须掌握函数 的概念以及学会设计和使用函数。 “函数”这个名词是从英文function翻译过来的,其 实function的原意是“功能”。顾名思义,一个函数 就是一个功能。 在实际应用的程序中,主函数写得很简单,它的作 用就是调用各个函数,程序各部分的功能全部都是 由各函数实现的。主函数相当于总调度,调动各函 数依次实现各项功能。 开发商和软件开发人员将一些常用的功能模块编写 成函数,放在函数库中供公共选用。程序开发人员 要善于利用库函数,以减少重复编写程序段的工作 量。 图4.1是一个程序中函数调用的示意图。 图4.1 例4.1 在主函数中调用其他函数。 #include using namespace std; void printstar(void) //定义printstar函数 { cout<<″****************************** ″<y?x:y; //将x和y中的大者的值赋给整型变量z return (z); //将z的值作为函数值返回调用点 } C++要求在定义函数时必须指定函数的类型。 4.2.2 定义有参函数的一般形式 在调用函数时,大多数情况下,函数是带参数的。 主调函数和被调用函数之间有数据传递关系。前面 已提到:在定义函数时函数名后面括号中的变量名 称为形式参数(formal parameter,简称形参), 在主调函数中调用一个函数时,函数名后面括号中 的参数(可以是一个表达式)称为实际参数(actual parameter,简称实参)。 4.3 函数参数和函数的值 4.3.1 形式参数和实际参数 例4.2 调用函数时的数据传递。 #include using namespace std; int max(int x,int y) //定义有参函数max {int z; z=x>y?x:y; return(z); } int main( ) {int a,b,c; cout<<″please enter two integer numbers:″; cin>>a>>b; c=max(a,b); //调用max函数,给定实参为a,b。函数值赋给c cout<<″max=″< using namespace std; int main( ) {float add(float x,float y); //对add函数作声明 float a,b,c; cout<<″please enter a,b:″; cin>>a>>b; c=add(a,b); cout<<″sum=″< using namespace std; inline int max(int,int, int); //声明函数,注意左端有inline int main( ) {int i=10,j=20,k=30,m; m=max(i,j,k); cout<<″max=″<a) a=b; //求a,b,c中的最大者 if(c>a) a=c; return a; } 由于在定义函数时指定它为内置函数,因此编译系 统在遇到函数调用“max(i,j,k)”时,就用max函数体 的代码代替“max(i,j,k)”,同时将实参代替形参。这 样,程序第6行 “m=max(i,j,k);”就被置换成 if (j>i) i=j; if(k>i) i=k; m=i; 注意: 可以在声明函数和定义函数时同时写 inline,也可以只在其中一处声明inline,效果相 同,都能按内置函数处理。 使用内置函数可以节省运行时间,但却增加了目标 程序的长度。因此一般只将规模很小(一般为5个语 句以下)而使用频繁的函数(如定时采集数据的函数) 声明为内置函数。 内置函数中不能包括复杂的控制语句,如循环语句 和switch语句。 应当说明: 对函数作inline声明,只是程序设计者 对编译系统提出的一个建议,也就是说它是建议性 的,而不是指令性的。并非一经指定为inline,编 译系统就必须这样做。编译系统会根据具体情况决 定是否这样做。 归纳起来,只有那些规模较小而又被频繁调用的简 单函数,才适合于声明为inline函数。 在编程时,有时我们要实现的是同一类的功能,只 是有些细节不同。例如希望从3个数中找出其中的 最大者,而每次求最大数时数据的类型不同,可能 是3个整数、3个双精度数或3个长整数。程序设计 者往往会分别设计出3个不同名的函数,其函数原 型为: int max1(int a,int b, int c); //求3个整数中的最大者 double max2(double a,double b,double c); //求3个双精度数中最大者 long max3(long a,long b,long c); //求3个长整数中的最大者 C++允许用同一函数名定义多个函数,这些函数的 参数个数和参数类型不同。这就是函数的重载 (function overloading)。即对一个函数名重新赋予 它新的含义,使一个函数名可以多用。 *4.6 函数的重载 对上面求最大数的问题可以编写如下的C++程序。 例4.5 求3个数中最大的数(分别考虑整数、双精度 数、长整数的情况)。 #include using namespace std; int main( ) {int max(int a,int b,int c); //函数声明 double max(double a,double b,double c); //函数声明 long max(long a,long b,long c); //函数声明 int i1,i2,i3,i; cin>>i1>>i2>>i3; //输入3个整数 i=max(i1,i2,i3); //求3个整数中的最大者 cout<<″i_max=″<>d1>>d2>>d3; //输入3个双精度数 d=max(d1,d2,d3); //求3个双精度数中的最大者 cout<<″d_max=″<>g1>>g2>>g3; //输入3个长整数 g=max(g1,g2,g3); //求3个长整数中的最大者 cout<<″g_max=″<a) a=b; if(c>a) a=c; return a; } double max(double a,double b,double c) //定义求3个双精度数中的最大 者的函数 {if(b>a) a=b; if(c>a) a=c; return a; } long max(long a,long b,long c) //定义求3个长整数中的最大者的函 数 {if(b>a) a=b; if(c>a) a=c; return a; } 运行情况如下: 185 -76 567↙ (输入3个整数) 56.87 90.23 -3214.78↙ (输入3个实数) 67854 -912456 673456↙ (输入3个长整数) i_max=567 (输出3个整数的最大值) d_max=90.23 (输出3个双精度数的最大值) g_max=673456 (输出3个长整数的最大值) 上例3个max函数的函数体是相同的,其实重载函 数并不要求函数体相同。重载函数除了允许参数类 型不同以外,还允许参数的个数不同。 例4.6 编写一个程序,用来求两个整数或3个整数中 的最大数。如果输入两个整数,程序就输出这两个 整数中的最大数,如果输入3个整数,程序就输出 这3个整数中的最大数。 例4.6 编写一个程序,用来求两个整数或3个整数中 的最大数。如果输入两个整数,程序就输出这两个 整数中的最大数,如果输入3个整数,程序就输出 这3个整数中的最大数。 #include using namespace std; int main( ) {int max(int a,int b,int c); //函数声明 int max(int a,int b); //函数声明 int a=8,b=-12,c=27; cout<<″max(a,b,c)=″<a) a=b; if(c>a) a=c; return a; } int max(int a,int b) //此max函数的作用是求两个整数中的最大者 {if(a>b) return a; else return b; } 运行情况如下: max(a,b,c)=27 max(a,b)=8 两次调用max函数的参数个数不同,系统就根据参 数的个数找到与之匹配的函数并调用它。 参数的个数和类型可以都不同。但不能只有函数的 类型不同而参数的个数和类型相同。例如: int f(int); //函数返回值为整型 long f(int); //函数返回值为长整型 void f(int); //函数无返回值 在函数调用时都是同一形式,如“f(10)”。编译系统 无法判别应该调用哪一个函数。重载函数的参数个 数、参数类型或参数顺序3者中必须至少有一种不 同,函数返回值类型可以相同也可以不同。 在使用重载函数时,同名函数的功能应当相同或相 近,不要用同一函数名去实现完全不相干的功能, 虽然程序也能运行,但可读性不好,使人莫名其妙。 C++提供了函数模板(function template)。所谓函数 模板,实际上是建立一个通用函数,其函数类型和 形参类型不具体指定,用一个虚拟的类型来代表。 这个通用函数就称为函数模板。凡是函数体相同的 函数都可以用这个模板来代替,不必定义多个函 数,只需在模板中定义一次即可。在调用函数时系 统会根据实参的类型来取代模板中的虚拟类型,从 而实现了不同函数的功能。看下面的例子就清楚了。 例4.7 将例4.6程序改为通过函数模板来实现。 *4.7 函数模板 #include using namespace std; template //模板声明,其中T为类型参数 T max(T a,T b,T c) //定义一个通用函数,用T作虚拟的类型名 {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); //调用模板函数,此时T被int取代 d=max(d1,d2,d3); //调用模板函数,此时T被double取代 g=max(g1,g2,g3); //调用模板函数,此时T被long取代 cout<<″i_max=″< 或 template 通用函数定义 通用函数定义 在建立函数模板时,只要将例4.5程序中定义的第一 个函数首部的int改为T即可。即用虚拟的类型名T 代替具体的数据类型。在对程序进行编译时,遇到 第13行调用函数max(i1,i2,i3),编译系统会将函数 名max与模板max相匹配,将实参的类型取代了函 数模板中的虚拟类型T。此时相当于已定义了一个 函数: int max(int a,int b,int c) {if(b>a) a=b; if(c>a) a=c; return a; } 然后调用它。后面两行(14,15行)的情况类似。 类型参数可以不只一个,可以根据需要确定个数。 如 template 可以看到,用函数模板比函数重载更方便,程序更 简洁。但应注意它只适用于函数的参数个数相同而 类型不同,且函数体相同的情况,如果参数的个数 不同,则不能用函数模板。 一般情况下,在函数调用时形参从实参那里取得 值,因此实参的个数应与形参相同。有时多次调用 同一函数时用同样的实参,C++提供简单的处理办 法,给形参一个默认值,这样形参就不必一定要从 实参取值了。如有一函数声明 float area(float r=6.5); 指定r的默认值为6.5,如果在调用此函数时,确认r 的值为6.5,则可以不必给出实参的值,如 area( ); //相当于area(6.5); 如果不想使形参取此默认值,则通过实参另行给出。 如 *4.8 有默认参数的函数 area(7.5); //形参得到的值为7.5,而不是6.5 这种方法比较灵活,可以简化编程,提高运行效率。 如果有多个形参,可以使每个形参有一个默认值, 也可以只对一部分形参指定默认值,另一部分形参 不指定默认值。如有一个求圆柱体体积的函数,形 参h代表圆柱体的高,r为圆柱体半径。函数原型如 下: float volume(float h,float r=12.5); //只对形参r指定默认值12.5 函数调用可以采用以下形式: volume(45.6); //相当于volume(45.6,12.5) volume(34.2,10.4) //h的值为34.2,r的值为10.4 实参与形参的结合是从左至右顺序进行的。因此指 定默认值的参数必须放在形参表列中的最右端,否 则出错。例如: void f1(float a,int b=0,int c,char d=′a′); //不正确 void f2(float a,int c,int b=0, char d=′a′); //正确 如果调用上面的f2函数,可以采取下面的形式: f2(3.5, 5, 3, ′x′) //形参的值全部从实参得到 f2(3.5, 5, 3) //最后一个形参的值取默认值′a′ f2(3.5, 5) //最后两个形参的值取默认值,b=0,d=′a′ 可以看到,在调用有默认参数的函数时,实参的个 数可以与形参的个数不同,实参未给定的,从形参 的默认值得到值。利用这一特性,可以使函数的使 用更加灵活。例如例4.7求2个数或3个数中的最大数。 也可以不用重载函数,而改用带有默认参数的函数。 例4.8 求2个或3个正整数中的最大数,用带有默认 参数的函数实现。 #include using namespace std; int main( ) {int max(int a, int b, int c=0);//函数声明,形参c有默认值 int a,b,c; cin>>a>>b>>c; cout<<″max(a,b,c)=″<a) a=b; if(c>a) a=c; return a; } 运行情况如下: 14 -56 135↙ max(a,b,c)=135 max(a,b)=14 在使用带有默认参数的函数时有两点要注意: (1) 如果函数的定义在函数调用之前,则应在函数 定义中给出默认值。如果函数的定义在函数调用之 后,则在函数调用之前需要有函数声明,此时必须 在函数声明中给出默认值,在函数定义时可以不给 出默认值(如例4.8)。 (2) 一个函数不能既作为重载函数,又作为有默认 参数的函数。因为当调用函数时如果少写一个参 数,系统无法判定是利用重载函数还是利用默认参 数的函数,出现二义性,系统无法执行。 C++不允许对函数作嵌套定义,也就是说在一个函 数中不能完整地包含另一个函数。在一个程序中每 一个函数的定义都是互相平行和独立的。 虽然C++不能嵌套定义函数,但可以嵌套调用函 数,也就是说,在调用一个函数的过程中,又调用 另一个函数。见图4.6。 图4.6 4.9 函数的嵌套调用 在程序中实现函数嵌套调用时,需要注意的是: 在 调用函数之前,需要对每一个被调用的函数作声明 (除非定义在前,调用在后)。 例4.9 用弦截法求方程f(x)=x3-5x2+16x-80=0的根。 这是一个数值求解问题,需要先分析用弦截法求根 的算法。根据数学知识,可以列出以下的解题步骤: (1) 取两个不同点x1,x2,如果f(x1)和f(x2)符号相 反,则(x1,x2)区间内必有一个根。如果f(x1)与f(x2) 同符号,则应改变x1,x2,直到f(x1), f(x2)异号为止。 注意x1、x2的值不应差太大,以保证(x1,x2)区间内只 有一个根。 (2) 连接(x1, f(x1))和(x2, f(x2))两点,此线(即弦) 交x轴于x,见图4.7。 图4.7 x点坐标可用下式求出: x=x1·f(x2)-x2·f(x1) f(x2)-f(x1) 再从x求出f(x)。 (3) 若f(x)与f(x1)同符号,则根必在(x, x2)区间内,此 时将x作为新的x1。如果f(x)与f(x2)同符号,则表示 根在( x1,x)区间内,将x作为新的x2。 (4) 重复步骤 (2) 和 (3), 直到 |f(x)|<ξ为止, ξ 为一个很小的正数, 例如10-6。此时认为 f(x)≈0。 这就是弦截法的算法,在程序中分别用以下几个函 数来实现以上有关部分功能: (1) 用函数f(x)代表x的函数:x3-5x2+16x-80。 (2) 用函数xpoint (x1,x2)来求(x1,f(x1))和(x2,f(x2))的连 线与x轴的交点x的坐标。 (3) 用函数root(x1,x2)来求(x1,x2)区间的那个实根。 显然,执行root函数的过程中要用到xpoint函数,而执 行xpoint函数的过程中要用到f函数。 根据以上算法,可以编写出下面的程序: #include #include #include using namespace std; double f(double); //函数声明 double xpoint(double, double); //函数声明 double root(double, double); //函数声明 int main( ) { double x1,x2,f1,f2,x; do {cout<<″input x1,x2:″; cin>>x1>>x2; f1=f(x1); f2=f(x2); } while(f1*f2>=0); x=root(x1,x2); cout<0) {y1=y; x1=x; } else x2=x; }while(fabs(y)>=0.00001); return x; } 运行情况如下: input x1,x2:2.5 6.7↙ A root of equation is 5.0000000 对程序的说明: (1) 在定义函数时,函数名为f,xpoint和root的3个 函数是互相独立的,并不互相从属。这3个函数均 定为双精度型。 (2) 3个函数的定义均出现在main函数之后,因此在 main函数的前面对这3个函数作声明。 习惯上把本程序中用到的所有函数集中放在最前面 声明。 (3) 程序从main函数开始执行。函数的嵌套调用见 图4.8。 图4.8 (4) 在root函数中要用到求绝对值的函数fabs,它是 对双精度数求绝对值的系统函数。它属于数学函数 库,故在文件开头用#include 把有关的头 文件包含进来。 在调用一个函数的过程中又出现直接或间接地调用 该函数本身,称为函数的递归(recursive)调用。 C++允许函数的递归调用。例如: int f(int x) {int y,z; z=f(y); //在调用函数f的过程中,又要调用f函数 return (2*z); } 以上是直接调用本函数,见图4.9。 图4.10表示的是间接调用本函数。在调用f1函数过 程中要调用f2函数,而在调用f2函数过程中又要调 用f1函数。 4.10 函数的递归调用 图4.9 图4.10 从图上可以看到,这两种递归调用都是无终止的自 身调用。显然,程序中不应出现这种无终止的递归 调用,而只应出现有限次数的、有终止的递归调 用,这可以用if语句来控制,只有在某一条件成立 时才继续执行递归调用,否则就不再继续。 包含递归调用的函数称为递归函数。 例4.10 有5个人坐在一起,问第5个人多少岁?他说 比第4个人大两岁。问第4个人岁数,他说比第3个 人大两岁。问第3个人,又说比第2个人大两岁。问 第2个人,说比第1个人大两岁。最后问第1个人, 他说是10岁。请问第5个人多大? 每一个人的年龄都比其前1个人的年龄大两岁。即 age(5)=age(4)+2 age(4)=age(3)+2 age(3)=age(2)+2 age(2)=age(1)+2 age(1)=10 可以用式子表述如下: age(n)=10 (n=1) age(n)=age(n-1)+2 (n>1) 可以看到,当n>1时,求第n个人的年龄的公式是相 同的。因此可以用一个函数表示上述关系。图4.11 表示求第5个人年龄的过程。 图4.11 可以写出以下C++程序,其中的age函数用来实现 上述递归过程。 #include using namespace std; int age(int); //函数声明 int main( ) //主函数 { cout<1) 例4.11 用递归方法求n!。 求n!可以用递推方法,即从1开始,乘2,再乘 3……一直乘到n。 求n!也可以用递归方法,即5!=4!×5,而4!= 3!×4,…,1!=1。可用下面的递归公式表示: n!= 1 (n=0,1) n·(n-1)!(n>1) 有了例4.10的基础,很容易写出本题的程序: #include using namespace std; long fac(int); //函数声明 int main( ) {int n; //n为需要求阶乘的整数 long y; //y为存放n!的变量 cout<<″please input an integer :″; //输入的提示 cin>>n; //输入n y=fac(n); //调用fac函数以求n! cout<1时,进行递归调用 return f; //将f的值作为函数值返回 } 运行情况如下: please input an integer:10↙ 10!=3628800 许多问题既可以用递归方法来处理,也可以用非递 归方法来处理。在实现递归时,在时间和空间上的 开销比较大,但符合人们的思路,程序容易理解。 #include using namespace std; int main() {int fac(int); int a,b,c,sum=0; cout<<"enter a,b,c:"; cin>>a>>b>>c; sum=sum+fac(a)+fac(b)+fac(c); cout< using namespace std; int main() {int f(int); int n,s; cout<<"input the number n:"; cin>>n; s=f(n); cout<<"The result is "< using namespace std; int f(int a) //定义f函数,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< using namespace std; int fac(int); //函数声明 int main( ) {int i; for(i=1;i<=5;i++) cout< using namespace std; int max(int,int); //函数声明 void main( ) {extern int a,b; //对全局变量a,b作提前引用声明 cout<y?x:y; return z; } 运行结果如下: 15 在main后面定义了全局变量a,b,但由于全局变量 定义的位置在函数main之后,因此如果没有程序的 第5行,在main函数中是不能引用全局变量a和b的。 现在我们在main函数第2行用extern对a和b作了提 前引用声明,表示a和b是将在后面定义的变量。这 样在main函数中就可以合法地使用全局变量a和b了。 如果不作extern声明,编译时会出错,系统认为a和 b未经定义。一般都把全局变量的定义放在引用它 的所有函数之前,这样可以避免在函数中多加一个 extern声明。 2. 在多文件的程序中声明外部变量 如果一个程序包含两个文件,在两个文件中都要用 到同一个外部变量num,不能分别在两个文件中各 自定义一个外部变量num。正确的做法是:在任一 个文件中定义外部变量num,而在另一文件中用 extern对num作外部变量声明。即 extern int num; 编译系统由此知道num是一个已在别处定义的外部 变量,它先在本文件中找有无外部变量num,如果 有,则将其作用域扩展到本行开始(如上节所述), 如果本文件中无此外部变量,则在程序连接时从其 他文件中找有无外部变量num,如果有,则把在另 一文件中定义的外部变量num的作用域扩展到本文 件,在本文件中可以合法地引用该外部变量num。 分析下例: file1.cpp file2.cpp extern int a,b; int a=3,b=4; int main( ) ┆ {cout< using namespace std; int main( ) {extern int max(int,int); //声明在本函数中将要调用在其他文件中定义 的max函数 int a,b; cin>>a>>b; cout<y?x:y; return z; } 运行情况如下: 7 -34↙ 7 在计算机上运行一个含多文件的程序时,需要建立 一个项目文件(project file),在该项目文件中包含 程序的各个文件。详细情况请参阅本书的配套书 《C++程序设计习题解答与上机指导》。 通过此例可知:使用extern声明就能够在一个文件 中调用其他文件中定义的函数,或者说把该函数的 作用域扩展到本文件。extern声明的形式就是在函 数原型基础上加关键字extern。由于函数在本质上 是外部的,在程序中经常要调用其他文件中的外部 函数,为方便编程,C++允许在声明函数时省写 extern。例4.16程序main函数中的函数声明可写成 int max(int,int); 这就是我们多次用过的函数原型。由此可以进一步 理解函数原型的作用。用函数原型能够把函数的作 用域扩展到定义该函数的文件之外(不必使用 extern)。只要在使用该函数的每一个文件中包含 该函数的函数原型即可。函数原型通知编译系统: 该函数在本文件中稍后定义,或在另一文件中定义。 利用函数原型扩展函数作用域最常见的例子是 #include命令的应用。在#include命令所指定的头文 件中包含有调用库函数时所需的信息。例如,在程 序中需要调用sin函数,但三角函数并不是由用户在 本文件中定义的,而是存放在数学函数库中的。按 以上的介绍,必须在本文件中写出sin函数的原型, 否则无法调用sin函数。sin函数的原型是 double sin(double x); 本来应该由程序设计者在调用库函数时先从手册中 查出所用的库函数的原型,并在程序中一一写出 来,但这显然是麻烦而困难的。为减少程序设计者 的困难,在头文件cmath中包括了所有数学函数的 原型和其他有关信息,用户只需用以下#include命 令: #include 即可。这时,在该文件中就能合法地调用各数学库 函数了。 可以在C++源程序中加入一些“预处理命 令”(preprocessor directives),以改进程序设计环 境,提高编程效率。预处理命令是C++统一规定 的,但是它不是C++语言本身的组成部分,不能直 接对它们进行编译(因为编译程序不能识别它们)。 现在使用的C++编译系统都包括了预处理、编译和 连接等部分,因此不少用户误认为预处理命令是 C++语言的一部分,甚至以为它们是C++语句,这 是不对的。必须正确区别预处理命令和C++语句, 区别预处理和编译,才能正确使用预处理命令。 C++与其他高级语言的一个重要区别是可以使用预 处理命令和具有预处理的功能。 4.16 预处理命令 C++提供的预处理功能主要有以下3种: (1) 宏定义 (2) 文件包含 (3) 条件编译 分别用宏定义命令、文件包含命令、条件编译命令 来实现。为了与一般C++语句相区别,这些命令以 符号“#”开头,而且末尾不包含分号。 还可以用#define命令定义带参数的宏定义。其定义 的一般形式为 #define 宏名(参数表) 字符串 如 #define S(a,b) a*b //定义宏S(矩形面积),a、b为宏的参数 4.16.1 宏定义 使用的形式如下: area=S(3,2) 用3、2分别代替宏定义中的形式参数a和b,即用 3*2代替S(3,2)。因此赋值语句展开为 area=3*2; 由于C++增加了内置函数(inline),比用带参数的宏 定义更方便,因此在C++中基本上已不再用#define 命令定义宏了,主要用于条件编译中。 1.“文件包含”的作用 所谓“文件包含”处理是指一个源文件可以将另外一 个源文件的全部内容包含进来,即将另外的文件包 含到本文件之中。C++提供了#include命令用来实 现“文件包含”的操作。如在file1.cpp中有以下 #include命令: #include ″file2.cpp″ 它的作用见图4.18示意。 4.16.2 “文件包含”处理 图4.18 “文件包含”命令是很有用的,它可以节省程序设计 人员的重复劳动。 #include命令的应用很广泛,绝大多数C++程序中 都包括#include命令。现在,库函数的开发者把这 些信息写在一个文件中,用户只需将该文件“包含” 进来即可(如调用数学函数的,应包含cmath文 件),这就大大简化了程序,写一行#include命令的 作用相当于写几十行、几百行甚至更多行的内容。 这种常用在文件头部的被包含的文件称为“标题文 件”或“头部文件”。 头文件一般包含以下几类内容: (1) 对类型的声明。 (2) 函数声明。 (3) 内置(inline)函数的定义。 (4) 宏定义。用#define定义的符号常量和用const声 明的常变量。 (5) 全局变量定义。 (6) 外部变量声明。如entern int a; (7) 还可以根据需要包含其他头文件。 不同的头文件包括以上不同的信息,提供给程序设 计者使用,这样,程序设计者不需自己重复书写这 些信息,只需用一行#include命令就把这些信息包 含到本文件了,大大地提高了编程效率。由于有了 #include命令,就把不同的文件组合在一起,形成 一个文件。因此说,头文件是源文件之间的接口。 2. include命令的两种形式 在#include命令中,文件名除了可以用尖括号括起 来以外,还可以用双撇号括起来。#include命令的 一般形式为 #include <文件名> 或 #include ″文件名″ 如 #include 或 #include ″iostream″ 都是合法的。二者的区别是: 用尖括号时,系统到 系统目录中寻找要包含的文件,如果找不到,编译 系统就给出出错信息。 有时被包含的文件不一定在系统目录中,这时应该 用双撇号形式,在双撇号中指出文件路径和文件名。 如果在双撇号中没有给出绝对路径,如#include ″file2.c″则默认指用户当前目录中的文件。系统先 在用户当前目录中寻找要包含的文件,若找不到, 再按标准方式查找。如果程序中要包含的是用户自 己编写的文件,宜用双撇号形式。 对于系统提供的头文件,既可以用尖括号形式,也 可以用双撇号形式,都能找到被包含的文件,但显 然用尖括号形式更直截了当,效率更高。 3. 关于C++标准库 在C++编译系统中,提供了许多系统函数和宏定 义,而对函数的声明则分别存放在不同的头文件中。 如果要调用某一个函数,就必须用#include命令将 有关的头文件包含进来。C++的库除了保留C的大 部分系统函数和宏定义外,还增加了预定义的模板 和类。但是不同C++库的内容不完全相同,由各 C++编译系统自行决定。不久前推出的C++标准将 库的建设也纳入标准,规范化了C++标准库,以便 使C++程序能够在不同的C++平台上工作,便于互 相移植。新的C++标准库中的头文件一般不再包括 后缀.h,例如 #include 但为了使大批已有的C程序能继续使用,许多C++ 编译系统保留了C的头文件,即提供两种不同的头 文件,由程序设计者选用。如 #include //C形式的头文件 #include //C++形式的头文件 效果基本上是一样的。建议尽量用符合C++标准的 形式,即在包含C++头文件时一般不用后缀。如果 用户自己编写头文件,可以用.h为后缀。 一般情况下,在进行编译时对源程序中的每一行都 要编译。但是有时希望程序中某一部分内容只在满 足一定条件时才进行编译,也就是指定对程序中的 一部分内容进行编译的条件。如果不满足这个条 件,就不编译这部分内容。这就是“条件编译”。 有时,希望当满足某条件时对一组语句进行编译, 而当条件不满足时则编译另一组语句。 条件编译命令常用的有以下形式: 4.16.3 条件编译 (1) #ifdef 标识符 程序段1 #else 程序段2 #endif 它的作用是当所指定的标识符已经被#define命令定 义过,则在程序编译阶段只编译程序段1,否则编 译程序段2。#endif用来限定#ifdef命令的范围。其 中#else部分也可以没有。 (2) #if 表达式 程序段1 #else 程序段2 #endif 它的作用是当指定的表达式值为真(非零)时就编 译程序段1,否则编译程序段2。可以事先给定一 定条件,使程序在不同的条件下执行不同的功能。 例4.16 在调试程序时,常常希望输出一些所需的信 息,而在调试完成后不再输出这些信息。可以在源 程序中插入条件编译段。下面是一个简单的示例。 #include using namespace std; #define RUN //在调试程序时使之成为注释行 int main( ) { int x=1,y=2,z=3; #ifndef RUN //本行为条件编译命令 cout<<″x=″<<x<<″,y=″<<y<<″,z=″<<z; //在调试程序时需要输出 这些信息 #endif //本行为条件编译命令 cout<< ″x*y*z=″ *y*z<
还剩68页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

bnwe

贡献于2016-01-31

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf