C++语言认为函数是一个能完成某一独立功能的子程序,也就是程序模块。 函数就是对复杂问题的一种“自顶向下,逐步求精”思想的体现。编程者可以将一个大而复杂的程序分解为若干个相对独立而且功能单一的小块程序(函数)进行编写,并通过在各个函数之间进行调用,来实现总体的功能。
1函数概述
1.1函数概述
使用函数的优点:
(1)可读性好;
(2)易于查错和修改;
(3)便于分工编写,分阶段调试;
(4)各个函数之间接口清晰,便于相互间交换信息和使用;
(5)节省程序代码和存储空间;
(6)减少用户总的工作量;
(7)成为实现结构程序设计思想的重要工具;
(8)扩充语言和计算机的原设计能力;
(9)便于验证程序正确性。
设计C++程序的过程,实际上就是编写函数的过程,至少也要编写一个main()函数。执行C++程序,也就是执行相应的main()函数。即从main()函数的第一个左花括号“{”开始,依次执行后面的语句,直到最后一个右花括号“}”为止。
如果在执行过程中遇到其他的函数,则调用其他函数。调用完后,返回到刚才调用函数的下一条语句继续执行。而其他函数也只有在执行main()函数的过程中被调用时才会执行。
函数可以被一个函数调用,也可以调用另一个函数,它们之间可以存在着调用上的嵌套关系。但是,C++不允许函数的定义嵌套,即在函数定义中再定义一个函数是非法的。
C++函数是一个独立完成某个功能的语句块,函数与函数之间通过输入和输出来联系。
5.1.2函数的定义
在C++程序中调用函数之前,首先要对函数进行定义。如果调用此函数在前,函数定义在后,就会产生编译错误。
为了使函数的调用不受函数定义位置的影响,可以在调用函数前进行函数的声明。这样,不管函数是在哪里定义的,只要在调用前进行函数的声明,就可以保证函数调用的合法性。
函数定义的一般形式如下:
返回类型 函数名(参数表列)
{ 语句系列
return 合适类型数值
}
函数的定义包括以下几个部分:
1. 函数名
2. 参数表列
3. 返回类型
4. 函数体
l 函数名: 一个符合C++语法要求的标识符,定义函数名与定义变量名的规则是一样的,但应尽量避免用下划线开头,因为编译器常常定义一些下划线开头的变量或函数。函数名应尽可能反映函数的功能,它常常由几个单词组成。如VC中的按下鼠标左键的响应函数为:OnLButtonDown,这样就较好的反映了函数的功能。
l 参数表列:0个或多个变量,用于向函数传送数值或从函数带回数值,每一个参数都有自己的类型,它不同于变量定义,因为几个变量可以定义在一起,例如?quot;int i,j,k;"。如果参数表列中参数个数为0,我们称之为无参函数,无参函数可以定义为:
"返回类型 函数名( ){…}"或"返回类型 函数名(void){…}"
l 返回类型:指定函数用return返回的函数值的类型,如果函数没有返回值,返回类型应为void。
l 函数体:花括号中的语句称为函数体,一个函数的功能,通过函数体中的语句来完成。
例如: 函数dec求两个数的差值。
int dec(int x,int y)
{ return(x-y); }
例如: 函数out用于输出两个数的差值以及和值。
void out(int x,int y) //用void表示无返回值
{ cout<<x-y;
cout<<x+y;
}
例如:
int max(int a,int b)
{ int t;
if(a>b) t=a;
else t=b;
return t; }
2 函数的定义与声明
在C++程序中调用函数之前,首先要对函数进行定义。如果调用此函数在前,函数定义在后,就会产生编译错误。
为了使函数的调用不受函数定义位置的影响,可以在调用函数前进行函数的声明。这样,不管函数是在哪里定义的,只要在调用前进行函数的声明,就可以保证函数调用的合法性。
2.1 函数的定义
C++中的每一个函数都是从四个方面来进行定义:类型、函数名、形式参数表、函数体。
定义一个函数的语法格式为:
类型 函数名(形式参数表)
{
函数体;
}
例如:
int max(int a,int b)
{
int t;
if(a>b) t=a;
else t=b;
return t;
}
类型就是该函数的类型,也就是该函数的返回值的类型,此类型可以是C++中除函数、数组类型之外的任何一个合法的数据类型,包括普通类型、指针类型和引用类型等。
函数的返回值通常指明了该函数处理的结果,由函数体中的return语句给出。一个函数可以有返回值,也可以无返回值(称为无返回值函数或无类型函数)。此时需要使用保留字void作为类型名,而且函数体中也不需要再写return语句,或者return的后面什么也没有。
每个函数都有类型,如果在函数定义时没有明确指定类型,则默认类型为int。
函数名是一个有效的C++标识符,遵循一般的命名规则。在函数名后面必须跟一对小括号“()”,用来将函数名与变量名或其他用户自定义的标识符区分开来。在小括号中可以没有任何信息,也可以包含形式参数表。C++程序通过使用这个函数名和实参表可以调用该函数。
主函数的名称规定取编译器默认的名称main()。
形式参数表又称参数表,写在函数名后面的一对圆括号内。它可包含任意多个(含0个,即没有)参数说明项,当多于一个时其前后两个参数说明项之间必须用逗号分开。
每个参数说明项由一种已定义的数据类型和一个变量标识符组成,该变量标识符称为该函数的形式参数,简称形参,形参前面给出的数据类型称为该形参的类型。每个形参的类型可以为任一种数据类型,包括普通类型、指针类型、数组类型、引用类型等。
一个函数定义中的<参数表>可以被省略,表明该函数为无参函数,若<参数表>用void取代,则也表明是无参函数,若<参数表>不为空,同时又不是保留字void,则称为带参函数。
<函数体>是一条复合语句,它以左花括号开始,到右花括号结束,中间为一条或若干条C++语句,用于实现函数执行的功能。
注意:在一个函数体内允许有一个或多个return语句,一旦执行到其中某一个return语句时,return后面的语句就不再执行,直接返回调用位置继续向下执行。
函数形参也可以在函数体外说明。如下例:
func1(int a, int b)
{
…
}
也可写成:
func1(a,b)
int a;
int b;
{
…
}
例5-1:给出以下程序的运行结果。
#include "iostream.h"
int func(int n)
{if(n>0)
return 1;
else if(n==0)
return 0;
else return -1;
}
void main()
{
int n;
cout<<"Please input n:"<<endl;
cin>>n;
cout<<"\nthe result:"<<func(n)<<endl;
}此程序的运行结果为:
Please input n:
2
the result:1
Please input n:
-2
the result:-1
请注意:C++中不允许函数定义嵌套,即在函数定义中再定义一个函数是非法的。一个函数只能定义在别的函数的外部,函数定义之间都是平行的,互相独立的。
例如:下面的代码在主函数中非法嵌套了一个f()函数定义:
void main()
{
void f()
{
//…
}
}
5.3 函数原型
函数声明也称函数模型(或函数原型)。在主调函数中,如果要调用另一个函数,则须在本函数或本文件中的开头将要被调用的函数事先作一声明。声明函数,就是告诉编译器函数的返回类型、名称和形参表构成,以便编译系统对函数的调用进行检查。
函数声明的一般格式为:
函数类型 函数名(形式参数表);
除了需在函数声明的末尾加上一个分号“;”之外,其他的内容与函数定义中的第一行(称函数头)的内容一样。
例如:设有一函数的定义为:
double func1(double a, int b, float c)
{
函数体
}
正确完整的函数声明应为:
double func1(double x, int y, float z);
//末尾要加上分号
也可以写为如下形式:
double func1(double,int,float);
//函数声明中省略了形参名
或写为如下形式:
double func1(double a, int b, float c);
//函数声明中的形参名与
函数定义中的形参名不同
不能写为如下形式:
double func1(x,y,z); //函数声明中省略了形参类型
或:
func1(double x, int y, float z);
//函数声明中省略了函数类型
或:
double func1(int y, float z, double x);
//函数声明中形参顺序调换了
5.4 函数的调用
5.4.1 函数调用的格式:
在C++中,除了主函数main由系统自动调用外,其他函数都是由主函数直接或间接调用的。函数调用的语法格式为:
函数名 (实际参数表);
实参应该与函数定义中的形参表中的形参一一对应,即个数相等、次序一致且对应参数的数据类型相同或相容。每个实参是一个表达式,并且必须有确定的值。
如:
g1(25) //实参是一个整数
g2(x) //实参是一个变量
g3(a,2*b+3) //第一个为变量,第二个运算表达式
g4(sin(x),’@’) //第一个为函数调用表达式,第二个为字符常量
g5(&d,*p,x/y-1) //分别为取地址运算、间接访问和一般运算表达式
常见的函数调用方式有下列两种:
方式一:将函数调用作为一条表达式语句使用,只要求函数完成一定的操作,而不使用其返回值。若函数调用带有返回值,则这个值将会自动丢失。例如:
max(3,5);
方式二:对于具有返回值的函数来说,把函数调用语句看作语句一部分,使用函数的返回值参与相应的运算或执行相应的操作。例如:
int a=max(3,5);
int a=max(3,5)+1;
cout<<max(3,5)<<endl;
if(f1(a,b)) cout<<”true”<<endl;
int a=2; a=max(max(a,3),5);
5.4.2 函数调用机制
1.C++的函数调用过程,需要调用初始化和善后处理的环节。函数调用的整个过程就是栈空间操作的过程。
2.函数的栈操作原理是一样的,但具体的实现因编译器不同而不同。
#include<iostream.h>
int max(int a,int b,int c)
{
int t;
t=a;
if(b>t) t=b;
if(c>t) t=c;
return t;
}
void main()
{
int x,y,z;
cout<<"input x y z:"
<<endl;
cin>>x>>y>>z;
int m=max(x,y,z);
cout<<”The max is:”
<<m
<<endl;
}
5.4.3 函数调用的过程
当调用一个函数时,整个调用过程分为三步进行,第一步是参数传递,第二步是函数体执行,第三步是返回,即返回到函数调用表达式的位置。
5.4.4 函数调用时的参数传递
参数传递称为“实虚结合”,即实参向形参传递信息,使形参具有确切地含义(即具有对应的存储空间和初值)。这种传递又分为两种不同的方式,一种是按值传递,另一种是地址传递或引用传递。
1. 按值传递
以按值传递方式进行参数传递的过程为:首先计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的长度,然后把已求出的实参表达式的值一一存入到为形参变量分配的存储空间中,成为形参变量的初值,供被调用函数执行时使用。这种传递是把实参表达式的值传送给对应的形参变量,故称这种传递方式为“按值传递”。这种方式被调用函数本身不对实参进行操作,也就是说,即使形参的值在函数中发生了变化,实参的值也完全不会受到影响,仍为调用前的值。
例5-3:按值传递。
#include "iostream.h"
void swap(int,int);
void main()
{
int a=3,b=4;
cout<<"a="<<a<<",b=“
<<b<<endl;
swap(a,b);
cout<<"a="<<a<<",b="
<<b <<endl;
}
void swap(int x,int y)
{
int t=x;
x=y;
y=t;
}
此程序的运行结果为:
a=3,b=4
a=3,b=4
2. 地址传递
如果在函数定义时将形参的类型说明成指针,对这样的函数进行调用时就需要指定地址值形式的实参。这时的参数传递方式即为地址传递方式。这种地址传递与上述的按值传递不同,它把实参的存储地址传送给对应的形参,从而使得形参指针和实参指针指向同一个地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。
例5-4:地址传递。
#include "iostream.h"
void swap(int *,int *);
void main()
{
int a=3,b=4;
cout<<"a="<<a<<",b=“
<<b<<endl;
swap(&a,&b);
cout<<"a="<<a<<",b=“
<<b<<endl;
}
void swap(int *x,int *y)
{
int t=*x;
*x=*y;
*y=t;
}
此程序的运行结果为:
a=3,b=4
a=4,b=3
3. 引用传递
按值传递方式容易理解,但形参值的改变不能对实参产生影响;地址传递方式虽然可以使得形参的改变对相应的实参有效,但如果在函数中反复利用指针进行间接访问,会使程序容易产生错误且难以阅读。如果以引用作为参数,则既可以使得对形参的任何操作都能改变相应的实参的数据,又使函数调用显得方便、自然。引用传递方式是在函数定义时在形参前面加上引用运算符“&”。
例5-5:引用传递。
#include "iostream.h"
void swap(int &,int &);
void main()
{
int a=3,b=4;
cout<<"a="<<a<<",b=“
<<b<<endl;
swap(a,b);
cout<<"a="<<a<<",b=“
<<b<<endl;
}
void swap(int &x,int &y)
{
int t=x;
x=y;
y=t;
}
此程序的运行结果为:
a=3,b=4
a=4,b=3
5.4.5 函数的嵌套调用
由前述可知,C++函数不能嵌套定义,即一个函数不能在另一个函数体中进行定义。但在使用时,允许嵌套调用,即在调用一个函数的过程中又调用另一个函数。 例如:
func1(int a, float b)
{ float c;
c=func2(b-1,b+1);
}
int func2(float x, float y)
{
函数体
}
func1和func2是分别独立定义的函数,互不从属。
5.4.6 函数的递归调用
一个函数直接或间接地调用自身,这种现象就是函数的递归调用。
递归调用有两种方式:直接递归调用和间接递归调用。直接递归调用即在一个函数中调用自身,间接递归调用即在一个函数中调用了其他函数,而在该其他函数中又调用了本函数。
利用函数的递归调用,可将一个复杂问题分解为一个相对简单且可直接求解的子问题(“递推”阶段);然后将这个子问题的结果逐层进行回代求值,最终求得原来复杂问题的解(“回归”阶段)。
例5-12:求n的阶乘。(函数递归调用的例程。)
#include "iostream.h"
long f(int n)
{if(n<0)
{cout<<“error!“
<<endl;
return(-1);}
else if(n<=1)
return(1);
else
return (n*f(n-1));
}
void main()
{long f(int n);
int n;
cout<<"input n:"
<<endl;
cin>>n;
cout<<"n!="<<f(n)
<<endl;
}
此程序的运行结果为:
please input n:
5
n!=120
5.5变量的作用域与生存期
5.5.1 局部变量与全局变量
1.程序的内存区域。
一个程序将操作系统分配给其运行的内存块分为4个区域。
(1)代码区,存放程序的代码,即程序中各个函数中的代码块。
(2)全局数据区,存放程序全局数据和静态数据。
(3)堆区,存放程序的动态数据。
(4)栈区,存放程序的局部数据,即各个函数中的数据。
2.局部变量。
在一个函数内部说明的变量是内部变量,它只在该函数范围内有效。也就是说,只有在包含变量说明的函数内部,才能使用被说明的变量,在此函数之外就不能使用这些变量了。所以内部变量也称“局部变量”。
P83 ch5_1.cpp
关于局部变量的作用域还要说明以下几点:
1.主函数main()中定义的内部变量,也只能在主函数中使用,其它函数不能使用。同时,主函数中也不能使用其它函数中定义的内部变量。因为主函数也是一个函数,与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。
2.形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量。
3.允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。
4.在复合语句中也可定义变量,其作用域只在复合语句范围内。
3.全局变量
(1)在函数外部定义的变量称为外部变量。以此类推,在函数外部定义的数组就称为外部数组。
(2)外部变量不属于任何一个函数,其作用域是:从外部变量的定义位置开始,到本文件结束为止。
(3)外部变量可被作用域内的所有函数直接引用,所以外部变量又称全局变量。
例:输入长方体的长(l)、宽(w)、高(h),求长方体体积及正、侧、顶三个面的面积。
int s1,s2,s3;
int vs(int a,int b,int c)
{ int v;
v=a*b*c; s1=a*b; s2=b*c; s3=a*c;
return v;
}
main()
{int v,l,w,h;
clrscr();
printf("\ninput length,width and height: ");
scanf("%d%d%d",&l,&w,&h);
v=vs(l,w,h);
printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3);
getch();
}
对于全局变量还有以下几点说明:
(1)外部变量可加强函数模块之间的数据联系,但又使这些函数依赖这些外部变量,因而使得这些函数的独立性降低。
从模块化程序设计的观点来看这是不利的,因此不是非用不可时,不要使用外部变量。
(2)在同一源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量将被屏蔽而不起作用。
(3)外部变量的作用域是从定义点到本文件结束。如果定义点之前的函数需要引用这些外部变量时,需要在函数内对被引用的外部变量进行说明。外部变量说明的一般形式为:
extern 数据类型 外部变量[,外部变量2……];
注意:外部变量的定义和外部变量的说明是两回事。外部变量的定义,必须在所有的函数之外,且只能定义一次。而外部变量的说明,出现在要使用该外部变量的函数内,而且可以出现多次。
5.5.2静态局部变量
1.定义格式: static 数据类型 内部变量表;
2.存储特点
(1)静态局部变量属于静态存储。在程序执行过程中,即使所在函数调用结束也不释放。换句话说,在程序执行期间,静态内部变量始终存在,但其它函数是不能引用它们的。
(2)定义但不初始化,则自动赋以"0"(整型和实型)或'\0'(字符型);且每次调用它们所在的函数时,不再重新赋初值,只是保留上次调用结束时的值!
(3)何时使用静态局部变量
需要保留函数上一次调用结束时的值。
变量只被引用而不改变其值。
5.5.3 外部存储类型
1.构成一个程序的多个源文件之间,通过声明数据或函数为外部的(extern)来进行沟通。
2.所有函数声明一般都放在源文件的开始位置。
3.带extern的是变量声明,不是变量定义。
5.5.4 静态存储类型
1.静态全局变量
(1)在全局变量前加一个static,使该变量只在这个源文件中可用,称之为全局静态变量。全局静态变量就是静态全局变量。
(2)静态全局变量对组成该程序的其他源文件是无效的。
2.静态函数
函数的声明和定义默认情况下在整个程序中是extern的。有时候,你可能需要使某个函数只在一个源文件中有效,不能被其他源文件所用,这时在函数前面加上static。
5.5.5 生命期
1.静态生命期
这种生命期与程序的运行期相同,只要程序一开始运
行,这种生命期的变量就存在,当程序结束时,其生
命期就结束。
2.局部生命期
在函数内部声明的变量或者是块中声明的变量具有局
部生命期。
3.动态生命期
这种生命期由程序中特定的函数调用(malloc()和
free())或操作符(new和delete)来创建和释放。
随机产生1~9之间的整数
#include<iostream.h>
#include<stdlib.h>
#include<iomanip.h>
#include<time.h>
void main()
{
srand(time(0));
for(int i=1;i<=10;i++)
{
cout<<setw(10)<<1+rand()%9;
if(i%5==0)
cout<<endl;
}
}
5.6 函数的作用域
5.6.1 作用域
1.局部作用域
当标识符的声明出现在由一对花括号所括起来的一段程序(块)内时,该标识符的作用域从声明点开始,到块结束处为止,该作用域的范围具有局部性。
2.函数作用域
标号是唯一具有函数作用域的标识符。
3.函数原型作用域
函数原型声明中所作的参数声明在该作用域中。这个作用域开始于函数原型声明的左括号,结束于函数声明的右括号。
4.文件作用域
文件作用域是在所有函数定义之外说明的,其作用域从说明点开始,一直延伸到源文件结束。
5.6.2 可见性
可见性从另一角度表现标识符的有效性,标识符在某个位置可见,表示该标识符可以被引用。可见性与作用域是一致的。作用域指的是标识符有效的范围,而可见性是分析在某一位置标识符的有效性。
5.7 多文件结构
5.7.1 头文件
每个标准库都有相应的“头文件”,头文件中包含对应库中所有函数的函数原型和这些函数所需的各种数据类型和常量的定义。
程序员也可以自己定义头文件,自定义的头文件应以.h结尾。自定义的头文件可以用#include来包含。
5.7.2 多文件结构
大程序倾向于分成多个源文件,其理由为:
(1)避免重复编译函数。
(2)使程序更加容量管理。
(3)把相关函数放到一特定源文件中。
5.7.3 编译预处理
1.#include包含指令
(1)文件包含是指,一个源文件可以将另一个源文件的全部内容包含进来。
(2)文件包含的格式
#include “包含文件名” 或 #include <包含文件名>
两种格式的区别仅在于:
①使用双引号:系统首先到当前目录下查找被包含文件,如果没找到,再到系统指定的“包含文件目录”(由用户在配置环境时设置)去查找。
②使用尖括号:直接到系统指定的“包含文件目录”去查找。一般地说,使用双引号比较保险。
(3)文件包含的优点
一个大程序,通常分为多个模块,并由多个程序员分别编程。有了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。
(4)说明
①编译预处理时,预处理程序将查找指定的被包含文件,并将其复制到#include命令出现的位置上。
②常用在文件头部的被包含文件,称为“标题文件”或“头部文件”,常以“h”(head)作为后缀,简称头文件。在头文件中,除可包含宏定义外,还可包含外部变量定义、结构类型定义等。
③一条包含命令,只能指定一个被包含文件。如果要包含n个文件,则要用n条包含命令。
④文件包含可以嵌套,即被包含文件中又包含另一个文件。
2.#define宏定义指令
(1) #define最常用的方法是建立常量,但已被C++的const定义语句所代替。
(2) #define还可以定义带参的宏,但也已被C++的inline内嵌函数所代替。
(3) #define的一个有效的使用是在条件编译指令中。
3.条件编译指令
条件编译可有效地提高程序的可移植性,并广泛地应用在商业软件中,为一个程序提供各种不同的版本。
(1)一般格式
#ifdef 标识符
程序段1;
[#else
程序段2;]
#endif
(2)功能:当“标识符”已经被#define命令定义过,则编译程序段1,否则编译程序段2。
①在不同的系统中,一个int 型数据占用的内存字节数可能是不同的。
②利用条件编译,还可使同一源程序即适合于调试(进行程序跟踪、打印较多的状态或错误信息),又适合高效执行要求。
(3)关于#ifndef ~ #endif命令
格式与#ifdef ~ #endif命令一样,功能正好与之相反。
5.8 内联函数
内联扩展(inline expansion)简称为内联(inline),内联函数也称为内嵌函数。当在一个函数的定义或声明前加上关键字inline则就把该函数定义为内联函数,它主要是解决程序的运行效率。
计算机在执行一般函数的调用时,无论该函数多么简单或复杂,都要经过参数传递、执行函数体和返回等操作,这些操作都需要一定的时间开销。若把一个函数定义为内联函数后,在程序编译阶段,编译器就会把每次调用该函数的地方都直接替换为该函数体中的代码,由此省去函数的调用及相应的保存现场、参数传递和返回操作,从而加快整个程序的执行速度。
例5-6:
#include "iostream.h"
inline int abs(int x)
{
return x<0?-x:x;
}
void main()
{
int a,b=3,c,d=-4;
a=abs(b);
c=abs(d);
cout<<"a="<<a<<",c="<<c<<endl;
}
此程序的运行结果为:a=3,c=4
5.9 函数重载
函数重载又称为函数的多态性,是指同一个函数名对应着多个不同的函数。所谓“不同”是指这些函数的形参表必须互不相同,或者是形参的个数不同,或者是形参的类型不同,或者是两者都不相同,否则将无法实现函数重载。例如,下面是合法的重载函数:
int func1(int,int);
int func1(int);
double func1(int,long);
double func1(long);
重载函数的类型,即函数的返回类型可以相同,也可以不同。但如果仅仅是返回类型不同而函数名相同、形参表也相同,则是不合法的,编译器会报“语法错误”。如:
int func1(int a, int b);
double func1(int a, int b);
除形参名外都相同的情况 ,编译器不认为是重载函数 ,只认为是对同一个函数原型的多次声明。
在调用一个重载函数func1()时,编译器必须判断函数名func1到底是指哪个函数。它是通过编译器,根据实参的个数和类型对所有func1()函数的形参一一进行比较,从而调用一个最匹配的函数。
5.10 带默认形参值的函数
在C++语言中调用函数时,通常要为函数的每个形参给定对应的实参。若没有给出实参,则按指定的默认值进行工作。
当一个函数既有定义又有声明时,形参的默认值必须在声明中指定,而不能放在定义中指定。只有当函数没有声明时,才可以在函数定义中指定形参的默认值。
默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。
如:
void func1(int a, double b=4.5, int c=3); //合法
void func1(int a=1, double b, int c=3); //不合法
在进行函数调用时,实参与形参按从左到右的顺序进行匹配,当实参的数目少于形参时,如果对应位置形参又没有设定默认值,就会产生编译错误;如果设定了默认值,编译器将为那些没有对应实参的形参取默认值。
注意:形参的默认值可以是全局常量、全局变量、表达式、函数调用,但不能为局部变量。
例如,下例不合法:
void func1()
{
int k;
void g(int x=k); //k为局部变量
}