函数
函数的定义与使用
函数的定义与使用
完整的函数定义包括函数原型声明和函数声明的实现两个部分,但是很多程序中,将函数声明和函数实现写在一起。从程序语言编写规范上,先提倡写函数声明,再写函数实现。
一般情况下,函数定义格式如下:
类型标识符 函数名(形式参数列表){
声明部分
语句
}
函数名右侧小括号中的是形式参数列表,{ }中的是函数体部分。在形参表中给出的参数称为形式参数,他们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值。形参既然是变量,就必须在形参表中给出形参的类型说明;同时C++支持参数表为空的定义,即函数没有形式参数。
其中类型标识符说明函数值的返回类型。返回类型可以是简单的数据类型,也可以是复合数据类型,也可以是空类型。而先写函数声明,再写函数实现的一般格式如下:
......
类型标识符 函数名(形式参数列表); //函数声明
......
类型标识符 函数名(形式参数列表)
{
声明部分;
语句;
}
例如,定义一个函数,用于求两个数中的最大数,可以写为:
int max(int a,int b){
if(a>b){
return a;
}else{
return b;
}
}
第一行说明,max函数是一个整形函数,其返回的函数值是一个整数。形参为a和b,均为整形量。a和b的具体值是由主调函数在调用时传送过来的。
比如,在main()中,可以这样调用:
int main(void)
{
int i = 2,j = 3;
cout<<max(i,j)<<endl;
return 0;
}
在max函数中的{ }的函数体内,除形参外没有使用其他变量,因此只有语句而没有声明部分。在max函数体中的return语句是把a或b的值作为函数的值返回给主调函数。有返回值的函数中至少有一条return语句。
函数的参数传递
大多数情况下,函数是带参数的。主调函数和被调用函数之间有数据传递关系。前面已经提到:在定义函数时函数名后面小括号中的变量名称为形式参数,在主调函数中调用一个函数时,函数名后面小括号中的参数(也可以是一个表达式)称为实际参数。
C++的参数传递规则基本上和C是一样的。在主调函数调用被调函数时,要根据被调函数的形参列表传入参数类型,个数一致的实际参数。
/*调用函数时的数据传递*/
#include <iostream>
using namespace std;//使用标准名空间
int max(int x,int y){//定义有参函数
int z;
z = x>y?x:y;
return z;
}
int main(void)
{
int a,b,c;
cout<<"Please enter two integrate numbers";
cin>>a>>b;
c = max(a,b);//调用max函数,给定实参为a,b
cout<<"max = "<<c<<endl;
}
运行结果:
有关形参和实参的说明:
(1) 在定义函数时指定的形参,在未出现函数调用时,他们并不占内存中的存储单元。因此称它们是形式参数,表示他们并不是实际存在的数据。只有在发生函数调用时,函数max中的形参才会被分配内存单元,以便接收从实参传来的数据。在调用结束后,形参所占的内存单元也被释放。
(2) 实参可以是常量,变量或表达式,如max(3,a+b);但要求a和b有确定的值,以便在调用函数时将实参的值赋给形参。
(3) 在定义函数时,必须在函数首部指定形参的类型。
(4) 实参与形参的类型应相同或赋值兼容。
(5) 一般情况下,除了引用形式参数外,实参变量对形参变量的数据传递是"值传递",即单向传递,只有实参传给形参,而不能由形参返回实参。在调用函数时,编译系统临时给形参分配存储单元。请注意:实参单元和形参单元是不同的单元。
调用结束后,形参单元被释放,实参单元仍保留并维持原值。因此在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中实参的值。
引用参数
引用参数是C++新引入的一种参数形式。首先,引用是被引用对象的一个别名,引用形式传递参数是C++中唯一的传地址参数方式。传递引用即传递实参本身,是通过对被引用对象地址的直接传递实现的,对引用的更新或者赋值将直接操作在被引用代表的对象身上。
引用的定义形式如下:
类型名& 引用名 = 被引用对象;
例如:
int i= 3;
int& reif = i;
对于阴影来说,引用一旦定义好了,在整个程序运行期间就不允许更改,一个引用名只能引用一个对象。
例如下面的引用就是非法的:
int i = 3,j = 5;
int& refi = i;
int& refi = j;
下面例子说明了引用作为函数参数时值的变化情况!
/*引用参数的数据传递*/
#include <iostream>
using namespace std;
void refreshfunc(int& ref){
ref++;
cout<<"ref = "<<ref<<endl;
}
int main(void)
{
int i=0;
cout<<"i = "<<i<<endl;
refreshfunc(i);
cout<<"i = "<<i<<endl;
rerturn 0;
}
运行结果如下:
使用引用时要注意,引用是一种特殊类型的变量,如果不是作为函数参数,定义一个引用就必须进行初始化,使它引用一个已经存在的对象。
函数的返回值
(1) 函数的返回值是通过函数中的return语句获得的。return语句将被调用函数中的一个确定值带回主调函数中去;return 语句后面的括号可以要,也可以不要。return 后面的值可以是一个表达式。
(2) 函数值的类型。函数返回值属于某一个确定的类型,应在定义函数时指定函数值得类型。
(3) 如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。
函数调用形式
1.函数调用的一般形式
函数调用的一般形式如下:函数名([实参列表]) //[]中的内容表示可选项
如果是调用无参函数,则"实参列表可以没有",但是小括号不可以省略。如果实参列表包含多个实参,各参数之间要用逗号隔开。实参与形参个数应相等,类型匹配(相同或赋值兼容)。实参与形参按顺序对应,一对一的传递数据。
按函数在语句中的作用来分,可以有一下3种函数调用方式:
(1)函数语句
把函数调用单独作为一个语句,函数调用只是完成一定的操作,无需返回值。例如:
printf("hello!World\n");
(2)函数表达式
函数出现在一个表达式中,这时要求函数带回一个确定的值以参加表达式的运算。例如:c = 2*max(a,b);
(3)函数参数
函数调用作为一个函数的实参。例如:
m = max(a,max(b,c)); //max(b,c)是函数调用,其值作为外层max函数调用的一个实参
2.被调用函数的声明和函数原型
在一个函数中,调用另一个函数(即被调用函数)需要具备以下条件:
(1) 首先被调用的函数必须是已经存在的函数
(2) 如果使用库函数,一般需要在本文件开头使用#include
命令将有关头文件嵌入到本文件中。
(3) 如果使用用户自己定义的函数,如果该函数与调用它的函数在同一个程序单位中,且位置在主调函数之后,则必须在调用此函数之前对调用的函数做声明。
所谓函数声明,就是在函数尚未被定义的情况下,事先将该函数有关信息通知编译系统,以便使编译能够正常运行。
/*对被调用函数做声明*/
#include <iostream>
using namespace std;
int main(void)
{
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 = "<<c<<endl;
return 0;
}
float add(float x,float y){
float z;
z = x+y;
return z;
}
运行结果如下:
对函数的定义和声明不是同一件事情。定义是指对函数功能的确立,包括指定函数名,函数类型,形参及其类型,函数体等,它是一个完整的,独立的函数单位。而声明的作用,则是把函数的名字,函数的类型以及形参的个数,类型和顺序(不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(如函数名是否正确,实参与形参的类型和个数是否一致)。
其实,在函数声明中,也可以不写形参名,而只写形参的类型。
float add(float,float);
这种函数声明称为函数原型(function prototype)。使用函数原型是C和C++的一个重要特点。它的作用主要是:根据函数原型在程序编译阶段对调用函数的合法性进行全面检查。如果发现与函数原型不匹配的函数调用就报告编译出错,属于语法错误。
函数原型的一般形式为:
(1) 函数类型 函数名(参数类型1,参数类型2…);
(2) 函数类型 函数名(参数类型1 参数名1,参数类型2,参数名2…);
第(1)种形式是基本的形式,为了便于阅读程序,也允许在函数原型中加上参数名,就成了第(2)种形式。但是编译系统并不检查参数名。因此参数名可以任意写。上面程序中的声明也可以写成:
float add(float a,float b);//参数名不用x,y;而用a,b
应当保证函数原型与函数首部写法上一致,即函数类型,函数名,参数个数,参数类型和参数顺序必须相同。在函数调用时,函数名,实参类型和实参个数应与函数原型一致。
前面已说过,如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经事先知道了已定义的函数类型,会根据函数首部提供的信息对函数的调用做出正确性检查。
有经验的程序员一般将main()函数写在最前面,这样对整个程序的结构和作用一目了然,纵览全局,然后再具体了解各个函数的细节。此外,用函数原型来声明函数,能减少编写程序时可能出现的错误,由于函数声明的位置和函数调用语句的位置比较近,因此在写程序时便于参照函数原型来书写函数调用,不易出错吧,所以应养成对所用用到的函数做声明的习惯。这是保证程序正确性和可读性的重要环节。
函数声明的位置可以在调用函数所在的函数中,也可以在函数之外。如果函数声明放在函数体的外部,在所有函数定义之前,则在各个主调函数中不必对所调用函数再做声明。例如:
char letter(char,char);
float max(float,float);
int main()
{
}
char letter(char,char){
}
float max(float,float){
}
如果一个函数被多个函数调用,用这种方式比较好,可以避免在每个主调函数中重复声明。