C++程序设计 湖南大学 杜四春、银红霞
第 4章 函数
4.1 函数概述
4.2 函数的定义与声明
4.3 函数的调用
C++程序设计 湖南大学 杜四春、银红霞
C++语言认为函数是一个能完成某一独立
功能的子程序,也就是程序模块。
函数就是对复杂问题的一种“自顶向下,
逐步求精”思想的体现。
编程者可以将一个大而复杂的程序分解为
若干个相对独立而且功能单一的小块程序(函
数)进行编写,并通过在各个函数之间进行调
用,来实现总体的功能。
C++程序设计 湖南大学 杜四春、银红霞
4.1 函数概述
使用函数的优点:
( 1) 可读性好;
( 2) 易于查错和修改;
( 3) 便于分工编写, 分阶段调试;
( 4) 各个函数之间接口清晰, 便于相互间交换
信息和使用;
( 5) 节省程序代码和存储空间;
( 6) 减少用户总的工作量;
( 7) 成为实现结构程序设计思想的重要工具;
( 8) 扩充语言和计算机的原设计能力;
( 9) 便于验证程序正确性 。
C++程序设计 湖南大学 杜四春、银红霞
设计 C++程序的过程,实际上就是编写函
数的过程,至少也要编写一个 main()函数。
执行 C++程序,也就是执行相应的 main()
函数。即从 main()函数的第一个左花括号
,{”开始,依次执行后面的语句,直到最后一
个右花括号,}”为止。如果在执行过程中遇到
其他的函数,则调用其他函数。调用完后,返
回到刚才调用函数的下一条语句继续执行。而
其他函数也只有在执行 main()函数的过程中
被调用时才会执行。
C++程序设计 湖南大学 杜四春、银红霞
函数可以被一个函数调用,也可以调用另
一个函数,它们之间可以存在着调用上的嵌套
关系。但是,C++不允许函数的定义嵌套,即
在函数定义中再定义一个函数是非法的。
C++函数是一个独立完成某个功能的语句
块,函数与函数之间通过输入和输出来联系。
C++程序设计 湖南大学 杜四春、银红霞
4.2 函数的定义与声明
在 C++程序中调用函数之前,首先要对函数
进行定义。如果调用此函数在前,函数定义在后,
就会产生编译错误。
为了使函数的调用不受函数定义位置的影响,
可以在调用函数前进行函数的声明。这样,不管
函数是在哪里定义的,只要在调用前进行函数的
声明,就可以保证函数调用的合法性。
C++程序设计 湖南大学 杜四春、银红霞
4.2.1 函数的定义
C++中的每一个函数都是从四个方面来进
行定义:类型, 函数名, 形式参数表, 函数体 。
定义一个函数的语法格式为:
类型 函数名 ( 形式参数表 )
{
函数体;
}
C++程序设计 湖南大学 杜四春、银红霞
例如:
int max(int a,int b)
{
int t;
if(a>b) t=a;
else t=b;
return t;
}
C++程序设计 湖南大学 杜四春、银红霞
类型就是该函数的类型,也就是该函数的返回值
的类型,此类型可以是 C++中除函数、数组类型之外
的任何一个合法的数据类型,包括普通类型、指针类
型和引用类型等。
函数的返回值通常指明了该函数处理的结果,由
函数体中的 return语句给出。一个函数可以有返回值,
也可以无返回值(称为无返回值函数或无类型函数)。
此时需要使用保留字 void作为类型名,而且函数体中
也不需要再写 return语句,或者 return的后面什么也没
有。
每个函数都有类型,如果在函数定义时没有明确
指定类型,则默认类型为 int。
C++程序设计 湖南大学 杜四春、银红霞
函数名是一个有效的 C++标识符,遵循一般的命
名规则。在函数名后面必须跟一对小括号“()”,
用来将函数名与变量名或其他用户自定义的标识符区
分开来。在小括号中可以没有任何信息,也可以包含
形式参数表。 C++程序通过使用这个函数名和实参表
可以调用该函数。
主函数的名称规定取编译器默认的名称 main()。
C++程序设计 湖南大学 杜四春、银红霞
形式参数表又称参数表,写在函数名后面的一对
圆括号内。它可包含任意多个(含 0个,即没有)参数
说明项,当多于一个时其前后两个参数说明项之间必
须用逗号分开。
每个参数说明项由一种已定义的数据类型和一个
变量标识符组成,该变量标识符称为该函数的形式参
数,简称形参,形参前面给出的数据类型称为该形参
的类型。每个形参的类型可以为任一种数据类型,包
括普通类型、指针类型、数组类型、引用类型等。
一个函数定义中的 <参数表 >可以被省略,表明该
函数为无参函数,若 <参数表 >用 void取代,则也表明
是无参函数,若 <参数表 >不为空,同时又不是保留字
void,则称为带参函数。
C++程序设计 湖南大学 杜四春、银红霞
<函数体 >是一条复合语句,它以左花括号开始,
到右花括号结束,中间为一条或若干条 C++语句,用
于实现函数执行的功能。
注意:在一个函数体内允许有一个或多个 return语
句,一旦执行到其中某一个 return语句时,return后面
的语句就不再执行,直接返回调用位置继续向下执行。
C++程序设计 湖南大学 杜四春、银红霞
函数形参也可以在函数体外说明 。 如下例:
func1(int a,int b)
{

}
也可写成:
func1(a,b)
int a;
int b;
{

}
C++程序设计 湖南大学 杜四春、银红霞
#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;
}
例 4-1:给出以下程序的运行结果。
C++程序设计 湖南大学 杜四春、银红霞
此程序的运行结果为:
Please input n:
2
the result:1
Please input n:
-2
the result:-1
C++程序设计 湖南大学 杜四春、银红霞
请注意,C++中不允许函数定义嵌套,即在函数
定义中再定义一个函数是非法的。一个函数只能定义
在别的函数的外部,函数定义之间都是平行的,互相
独立的。
例如:下面的代码在主函数中非法嵌套了一个 f()
函数定义:
void main()
{
void f()
{
//…
}
}
C++程序设计 湖南大学 杜四春、银红霞
4.2.2 函数的声明
函数声明也称函数模型 ( 或函数原型 ) 。 在主调
函数中, 如果要调用另一个函数, 则须在本函数或本
文件中的开头将要被调用的函数事先作一声明 。 声明
函数, 就是告诉编译器函数的返回类型, 名称和形参
表构成, 以便编译系统对函数的调用进行检查 。
函数声明的一般格式为:
函数类型 函数名 ( 形式参数表 ) ;
除了需在函数声明的末尾加上一个分号“;”之
外,其他的内容与函数定义中的第一行(称函数头)
的内容一样。
C++程序设计 湖南大学 杜四春、银红霞
例如:设有一函数的定义为:
double func1(double a,int b,float c)
{
函数体
}
正确完整的函数声明应为:
double func1(double x,int y,float z);
//末尾要加上分号
C++程序设计 湖南大学 杜四春、银红霞
也可以写为如下形式:
double func1(double,int,float);
//函数声明中省略了形参名
或写为如下形式:
double func1(double a,int b,float c);
//函数声明中的形参名与
//函数定义中的形参名不同
C++程序设计 湖南大学 杜四春、银红霞
不能写为如下形式:
double func1(x,y,z);
//函数声明中省略了形参类型
或:
func1(double x,int y,float z);
//函数声明中省略了函数类型
或:
double func1(int y,float z,double x);
//函数声明中形参顺序调换了
C++程序设计 湖南大学 杜四春、银红霞
4.3 函数的调用
4.3.1 函数调用的格式:
在 C++中, 除了主函数 main由系统自动调用外,
其他函数都是由主函数直接或间接调用的 。 函数调用
的语法格式为:
函数名 (实际参数表);
实参应该与函数定义中的形参表中的形参一一对
应,即个数相等、次序一致且对应参数的数据类型相
同或相容 。每个实参是一个表达式,并且必须有确定
的值。
C++程序设计 湖南大学 杜四春、银红霞
如:
g1(25) //实参是一个整数
g2(x) //实参是一个变量
g3(a,2*b+3) //第一个为变量, 第二个运算表
//达式
g4(sin(x),’@’) //第一个为函数调用表达式, 第二
//个为字符常量
g5(&d,*p,x/y-1) //分别为取地址运算、间接访问和
//一般运算表达式
C++程序设计 湖南大学 杜四春、银红霞
常见的函数调用方式有下列两种:
? 方式一:将函数调用作为一条表达式语句使用,
只要求函数完成一定的操作, 而不使用其返回值 。 若
函数调用带有返回值, 则这个值将会自动丢失 。 例如:
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);
C++程序设计 湖南大学 杜四春、银红霞
#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<<”Please input x y z:”
<<endl;
cin>>x>>y>>z;
int m=max(x,y,z);
cout<<”The max is:”<<m
<<endl;
}
例:函数调用。
C++程序设计 湖南大学 杜四春、银红霞
4.3.2 函数调用的过程
当调用一个函数时,整个调用过程分为三步进行,
第一步是参数传递,第二步是函数体执行,第三步是
返回,即返回到函数调用表达式的位置。
C++程序设计 湖南大学 杜四春、银红霞
4.3.3 函数调用时的参数传递
参数传递称为“实虚结合”,即实参向形
参传递信息,使形参具有确切地含义(即具有
对应的存储空间和初值)。这种传递又分为两
种不同的方式,一种是按值传递,另一种是地
址传递或引用传递。
C++程序设计 湖南大学 杜四春、银红霞
1,按值传递
以按值传递方式进行参数传递的过程为:首先计
算出实参表达式的值,接着给对应的形参变量分配一
个存储空间,该空间的大小等于该形参类型的长度,
然后把已求出的实参表达式的值一一存入到为形参变
量分配的存储空间中,成为形参变量的初值,供被调
用函数执行时使用。这种传递是把实参表达式的值传
送给对应的形参变量,故称这种传递方式为“按值传
递”。这种方式被调用函数本身不对实参进行操作,
也就是说,即使形参的值在函数中发生了变化,实参
的值也完全不会受到影响,仍为调用前的值。
C++程序设计 湖南大学 杜四春、银红霞
#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
例 4-3:按值传递。
C++程序设计 湖南大学 杜四春、银红霞
2,地址传递
如果在函数定义时将形参的类型说明成指针,对
这样的函数进行调用时就需要指定地址值形式的实参。
这时的参数传递方式即为地址传递方式。这种地址传
递与上述的按值传递不同,它把实参的存储地址传送
给对应的形参,从而使得形参指针和实参指针指向同
一个地址。因此,被调用函数中对形参指针所指向的
地址中内容的任何改变都会影响到实参。
C++程序设计 湖南大学 杜四春、银红霞
#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
例 4-4:地址传递。
C++程序设计 湖南大学 杜四春、银红霞
3,引用传递
按值传递方式容易理解,但形参值的改变不能对
实参产生影响;地址传递方式虽然可以使得形参的改
变对相应的实参有效,但如果在函数中反复利用指针
进行间接访问,会使程序容易产生错误且难以阅读。
如果以引用作为参数,则既可以使得对形参的任何操
作都能改变相应的实参的数据,又使函数调用显得方
便、自然。引用传递方式是在函数定义时在形参前面
加上引用运算符,&”。
C++程序设计 湖南大学 杜四春、银红霞
#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
例 4-5:引用传递。
C++程序设计 湖南大学 杜四春、银红霞
4.3.4 内联函数
内联扩展( inline expansion)简称为内联
( inline),内联函数也称为内嵌函数。当在一个函数
的定义或声明前加上关键字 inline则就把该函数定义为
内联函数,它主要是解决程序的运行效率。
计算机在执行一般函数的调用时,无论该函数多
么简单或复杂,都要经过参数传递、执行函数体和返
回等操作,这些操作都需要一定的时间开销。若把一
个函数定义为内联函数后,在程序编译阶段,编译器
就会把每次调用该函数的地方都直接替换为该函数体
中的代码,由此省去函数的调用及相应的保存现场、
参数传递和返回操作,从而加快整个程序的执行速度。
C++程序设计 湖南大学 杜四春、银红霞
例 4-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
C++程序设计 湖南大学 杜四春、银红霞
内联函数可以在一开始仅定义或声明一次, 但必
须在函数被调用之前定义或声明 。 否则, 如下例, 编
译器不认为那是内联函数, 仍然如同对普通函数那样
处理该函数的调用过程
从用户的角度看, 调用内联函数和一般函数没有
任何区别 。
请注意:内联函数的函数体有一些限制:内联函
数中不能含有任何循环以及 switch和 goto语句;内联函
数中不能说明数组;递归函数(自己调用自己的函数)
不能定义为内联函数。
C++程序设计 湖南大学 杜四春、银红霞
4.3.5 重载函数
函数重载又称为函数的多态性, 是指同一个函数
名对应着多个不同的函数 。 所谓, 不同, 是指这些函
数的形参表必须互不相同, 或者是形参的个数不同,
或者是形参的类型不同, 或者是两者都不相同, 否则
将无法实现函数重载 。 例如, 下面是合法的重载函数:
int func1(int,int);
int func1(int);
double func1(int,long);
double func1(long);
C++程序设计 湖南大学 杜四春、银红霞
重载函数的类型,即函数的返回类型可以相同,
也可以不同。但如果仅仅是返回类型不同而函数名相
同、形参表也相同,则是不合法的,编译器会报“语
法错误”。如:
int func1(int a,int b);
double func1(int a,int b);
除形参名外都相同的情况,编译器不认为是重载
函数,只认为是对同一个函数原型的多次声明 。
在调用一个重载函数 func1()时,编译器必须判
断函数名 func1到底是指哪个函数。它是通过编译器,
根据实参的个数和类型对所有 func1()函数的形参一
一进行比较,从而调用一个最匹配的函数。
C++程序设计 湖南大学 杜四春、银红霞
4.3.6 带默认形参值的函数
在 C++语言中调用函数时,通常要为函数的每个
形参给定对应的实参。若没有给出实参,则按指定的
默认值进行工作。
当一个函数既有定义又有声明时,形参的默认值
必须在声明中指定,而不能放在定义中指定。只有当
函数没有声明时,才可以在函数定义中指定形参的默
认值。
C++程序设计 湖南大学 杜四春、银红霞
默认值的定义必须遵守从右到左的顺序,如果某
个形参没有默认值,则它左边的参数就不能有默认值。
如,
void func1(int a,double b=4.5,int c=3); //合法
void func1(int a=1,double b,int c=3); //不合法
在进行函数调用时,实参与形参按从左到右的顺
序进行匹配,当实参的数目少于形参时,如果对应位
置形参又没有设定默认值,就会产生编译错误;如果
设定了默认值,编译器将为那些没有对应实参的形参
取默认值。
C++程序设计 湖南大学 杜四春、银红霞
注意:形参的默认值可以是全局常量, 全局变量,
表达式, 函数调用, 但不能为局部变量 。
例如, 下例不合法:
void func1()
{
int k;
void g(int x=k); //k为局部变量
}
C++程序设计 湖南大学 杜四春、银红霞
4.3.7 函数的嵌套调用
由前述可知,C++函数不能嵌套定义,即一个函
数不能在另一个函数体中进行定义。但在使用时,允
许嵌套调用,即在调用一个函数的过程中又调用另一
个函数。 例如:
func1(int a,float b)
{ float c;
c=func2(b-1,b+1);
}
int func2(float x,float y)
{
函数体
}
func1和 func2是分别独立定义的函数,互不从属。
C++程序设计 湖南大学 杜四春、银红霞
4.3.8 函数的递归调用
一个函数直接或间接地调用自身,这种现象就是
函数的递归调用。
递归调用有两种方式:直接递归调用和间接递归
调用。直接递归调用即在一个函数中调用自身,间接
递归调用即在一个函数中调用了其他函数,而在该其
他函数中又调用了本函数。
利用函数的递归调用,可将一个复杂问题分解为
一个相对简单且可直接求解的子问题(“递推”阶
段);然后将这个子问题的结果逐层进行回代求值,
最终求得原来复杂问题的解(“回归”阶段)。
C++程序设计 湖南大学 杜四春、银红霞
#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<<"please input n:“
<<endl;
cin>>n;
cout<<"n!="<<f(n)<<endl;
}
此程序的运行结果为:
please input n:
5
n!=120
例 4-12:求 n的阶乘。(函数递归调用的例程。)