必备技能5.11:函数原型
在本章开始的时候我们对函数的原型进行了简单的讨论。现在我们要对函数的原型进行更深入的讨论。在C++中,函数在使用之前是必须声明的。通常,这个是通过函数原型来完成的。函数原型中说明了函数的三部分内容:
1. 函数的返回类型
2. 函数参数的类型
3. 函数参数的数量
函数原型使得编译器可以进行以下三个重要的操作:
1. 函数原型告诉编译器在调用函数的时候应该生成什么样子的代码。编译器必须针对不同的返回类型进行不同的处理。
2. 函数原型使得C++能够报告任何在调用函数时实参和形参之间的非法的类型转换。
3. 函数的原型使得编译器能够检测出调用函数时传入实参的数量和函数原型需要的参数数量的差别。
函数原型的通用形式如下。除了不需要函数体外,它和函数的定义是一样的。
类型 函数名称(类型1 参数1,类型2 参数2,... ...,类型N 参数N);
在函数原型中,参数名称是可选项。然而,明确地写上参数名称确实可以使得编译器在出现错误的时候通过参数名称来确定任何的类型不匹配,因此我们最好还是写上参数的名称。
为了能更好地理解函数原型的作用,我们可以看看下面的程序。如果我们编译一下下面的程序,就会看到编译错误。这是因为程序中函数sqr_it()需要的是一个整型指针,而程序在调用这个函数的时候传入的是一个整型数。(不存在从整型数到整形指针的自动转换。)
/*
该程序中使用了函数原型来强制进行强类型检查
*/
void sqr_it(int *i);
int main()
{
int x;
x= 10;
sqr_it(x); //错误,参数类型不匹配。
return 0;
}
void sqr_it(int *i)
{
*i = *i * *i;
}
如果函数的定义在函数的使用之前,那么函数的定义也可以起到函数原型的作用。例如,下面的程序是完全合法的:
//如果函数的定义在函数的使用之前,那么定义也可以起到函数原型的作用。
#include <iostream>
using namespace std;
//用来判断一个数字是不是偶数
bool isEven(int num)
{
if ( !(num%2) ) return true; //偶数
return false;
}
int main()
{
if(isEven(4)) cout << "4 is even\n";
if(isEven(3)) cout << "this won't display";
return 0;
}
在这里,由于函数isEven()的定义是在main()函数之前,所以函数的定义就可以起到函数原型的作用,因此也不需要单独的函数原型。
通常,和确保函数定义都是在函数调用之前相比,声明一下程序中用到的函数的原型显得更简单。特别针对大型程序来说,有时候是很难确定函数之间调用关系。更进一步来说,还存在两个函数相互调用的情况,这时我们必须使用函数的原型。
包含函数原型的头文件
在本书的前面,我们学习过了标准的C++头文件。我们知道这些头文件中含有程序所需要的信息。这种解释只能说是部分是正确的,因为这种解释并不全面。C++的头文件中含有标准库中的函数原型。(其中还含有这些函数需要用到的不同的值和定义。)和我们自己写的函数一样,标准库函数在使用之前也必须声明其原型。我们可以查阅编译器的库函数文档来明确使用库函数是所需要的头文件。在库函数文档中除了有对函数的描述之外,我们还可以看到使用这个函数是必须包含的头文件。
练习:
1. 什么是函数的原型?函数的原型有什么作用?
2. 除了main()函数之外,其它所有的函数都必须声明其原型吗?
3. 当我们使用标准库函数的时候,为什么必须包含其头文件?