函数的定义、声明和调用
上一节正式地介绍了什么是函数,并简单了解了形式参数和返回值。这一节主要从语法出发来讲解如何使用函数。
函数定义
当我们像前两节所述的那样写出一个函数的时候,这个函数就可以使用了。同时,这个函数还拥有它的名字,叫做函数名。因此,这既是一个声明又是一个定义。一般地,函数定义的写法是这样的:
返回值类型 函数名(形参列表) 复合语句
这样的语法引入了一个名字叫 函数名
的函数。它接收一些形参,这些形参会写在 形参列表
中。形参列表
是一列由逗号 ,
分隔的
形参类型说明符 形参名
组成(类似不带初始化器的声明)。同时,函数的 返回值类型
也需明确给出。若没有返回值,需将其写为 void
。一般地,函数所代表的那段过程由 复合语句
给出。称这段 复合语句
为函数的函数体(Function body)。
举个例子:
其中,max
是函数名,是一个被引入的名字,因此这个名字遵从和变量名相同的命名规则。形参列表为 int x1, int x2
,它是由逗号 ,
分隔的两个形参声明 int x1
和 int x2
。这两个形参的类型都是 int
,一个叫 x1
,一个叫 x2
。除此之外,函数的返回值类型指定为 int
,写在了整个定义的最前面。
函数的函数体为从第 1 行 {
开始,第 9 行 }
结束的复合语句。这个函数所代表的的过程即执行这个复合语句。因此在使用函数的时候,从第 2 行开始从上到下依次执行这些子语句。
return 语句
函数中可以使用一种特别的语句称为 return 语句。return 语句的写法如下:
return 返回值;
执行 return 语句需要做两件事情。第一,立即结束函数的执行;第二,将 返回值
传回使用这个函数的地方(若有)。
若函数的返回值类型为 void
,则无需在 return 语句中写出 返回值
,此时 return 语句只用于结束函数执行。若函数的返回值类型不是 void
,则必须写出 返回值
,否则编译错误。
return 语句不一定只能出现一次。但一次函数执行过程中最多执行一次 return 语句。请看下例:
这个函数的定义与之前的定义是完全等价的。同样,当 a > b
时,只会执行第 3 行的 return 语句,此时返回值为 a
的值;反之只会执行第 5 行的 return 语句,此时返回值为 b
的值。
函数调用运算符
使用一个函数需要通过表达式来使用。使用函数的表达式被称为函数调用表达式,这种表达式的运算符为函数调用运算符。
运算符 | 名称 | 作用 |
---|---|---|
f(a) | 函数调用运算符 | 以实际参数 a 调用函数 f |
正如上表所述,函数表达式写作
函数名(实参列表)
其中,实参列表
是以逗号 ,
分隔的一系列值。这些值被称为实际参数(Argument),简称实参。比如函数调用表达式
max(42, 56);
中,42
和 56
就是两个实参。实参列表中实参的个数和类型需要与 函数名
这个函数的形参列表中的形参相吻合。(这里吻合的意思是相同或者可以发生隐式转换。)比如刚刚定义的 max
函数需要两个 int
类型的形参,这里提供了两个 int
类型的值作为实参,这就是吻合的。
在执行表达式运算的时候,会执行以下步骤:
- 将实参“代入”
函数名
这个函数的形参; - 随后执行
函数名
这个函数的函数体; - 执行过程中若遇到 return 语句,则结束函数执行,将返回值作为整个调用表达式的值。
还是用最初的例子
,则运算 max(42, 56)
这个函数调用表达式的时候,
- 用实参代入形参:变量
x1
的值初始化为42
,变量x2
的值初始化为56
; - 执行函数体:运算得到变量
y
的值为56
; - 执行
return y
:将56
作为函数返回值,函数执行结束,得到函数调用表达式的值为56
。
再比如下面的赋值语句
a = max(42, 56);
中,赋值运算符的右侧操作数是一个函数调用表达式,而函数调用表达式的值为 56
,这就相当于将 56
赋给了变量 a
。
像这样,通过函数调用表达式来使用一个函数的方法称为函数的调用(Call)。
函数的声明
看看这段代码。
我稍稍挪动了一下 printNum
函数定义的位置,把它放在了最下面。然而仅仅这样一个改动就不行了,编译会报错。报的什么错误呢?编译器告诉我们:“第 4 行名字 printNum
未声明。”
那么问题就很显然了:名字 printNum
的作用域不包括第 4 行。因为它的首次声明(声明点)在第 6 行,只有第 6 行后面才是 printNum
的作用域。所以第 4 行无法使用这个名字,从而就无法调用这个函数。那怎么解决呢?
当然我们仍然可以把函数定义写在最前面——除此之外,还可以这样做:在前面写上函数的声明,但不定义。因为 C++ 事实上允许多次声明一个名字(但不允许多次定义一个东西)。
像这样,第 3 行就是函数 printNum
的声明了。函数的声明和定义非常相似,只不过把作为函数体的复合语句改成了分号 ;
:
返回值类型 函数名(形参列表);
如果没有函数体,那么执行这个函数的时候计算机根本不知道要做什么,从而无法正常使用这个函数。所以这种写法只是一个声明而不是一个定义。
你也许注意到了 形参列表
中,每个形参的名字是可选的。这是因为,如果形参的名字用不上的话, 形参列表
中就不需要给出这个形参的名字。而函数声明中没有函数体,故没有形参名字存在的必要。所以一般人们写函数声明但不定义时,都不给出形参的名字:
函数也是 C++ 中的实体,故它也拥有自己的类型标识。比如上例中,
printNum
的类型标识为void(int)
,而max
的类型标识为int(int,int)
。