C++ 函数原型

1.为什么需要原型

原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。

#include <iostream>
void cheers(int);       // prototype: no return value
double cube(double x);  // prototype: returns a double
int main()
{
    using namespace std;
    cheers(5);          // function call
    cout << "Give me a number: ";
    double side;
    cin >> side;
    double volume = cube(side);    // function call
    cout << "A " << side <<"-foot cube has a volume of ";
    cout << volume << " cubic feet.\n";
    cheers(cube(2));    // prototype protection at work
    // cin.get();
    // cin.get();
    return 0;
}

void cheers(int n)
{
    using namespace std;
    for (int i = 0; i < n; i++)
        cout << "Cheers! ";
    cout << endl;
}

double cube(double x)
{
    return x * x * x; 
}

原型是如何影响double volume = cube(side);这段函数运行呢?

首先,原型告诉编译器,cube()有一个double参数。如果程序没有提供这样的参数,原型将让编译器能够捕获这种错误。其次,cube()函数完成计算后,将把返回值放置在指定的位置——可能是CPU寄存器,也可能是内存中。然后调用函数(这里为main())将从这个位置取得返回值。由于原型指出了cube()的类型为double,因此编译器知道应检索多少个字节以及如何解释它们。如果没有这些信息,编译器将只能进行猜测,而编译器是不会这样做的。

读者可能还会问,为何编译器需要原型,难道它就不能在文件中进一步查找,以了解函数是如何定义的吗?这种方法的一个问题是效率不高。编译器在搜索文件的剩余部分时将必须停止对main()的编译。一个更严重的问题是,函数甚至可能并不在文件中。C++允许将一个程序放在多个文件中,单独编译这些文件,然后再将它们组合起来。在这种情况下,编译器在编译main()时,可能无权访问函数代码。如果函数位于库中,情况也将如此。避免使用函数原型的唯一方法是,在首次使用函数之前定义它,但这并不总是可行的。另外,C++的编程风格是将main()放在最前面,因为它通常提供了程序的整体结构。

2.原型的语法

函数原型是一条语句,因此必须以分号结束。获得原型最简单的方法是,复制函数定义中的函数头,并添加分号。如上面一段代码中,对于cube(),就是如此。

然而,函数原型不要求提供变量名,有类型列表就足够了。因此对于cube(),还可以这样声明原型:double cube(double);

3.原型的功能

但它对程序员有什么帮助呢?它们可以极大地降低程序出错的几率。具体来说,原型确保以下几点:

  • 编译器正确处理函数返回值;
  • 编译器检查使用的参数数目是否正确;
  • 编译器检查使用的参数类型是否正确。如果不正确,则转换为正确的类型(如果可能的话)。

假如参数数目不对时将发生的情况。例如,假设进行了如下调用:





如果没有函数原型,编译器将允许它通过。当函数被调用时,它将找到cube()调用存放值的位置,并使用这里的值。这正是ANSIC从C++借鉴原型之前,C语言的工作方式。由于对于ANSIC来说,原型是可选的,因此有些C语言程序正是这样工作的。但在C++中,原型不是可选的,因此可以确保不会发生这类错误。

接下来,假设提供了一个参数,但其类型不正确。在C语言中,这将造成奇怪的错误。例如,如果函数需要一个int值(假设占16位),而程序员传递了一个double值(假设占64位),则函数将只检查64位中的前16位,并试图将它们解释为一个int值。但C++自动将传递的值转换为原型中指定的类型,条件是两者都是算术类型。例如,程序清单7.2将能够应付下述语句中两次出现的类型不匹配的情况:

cheers(cube(2));

首先,程序将int的值2传递给cube(),而后者期望的是double类型。编译器注意到,cube()原型指定了一个double类型参数,因此将2转换为2.0——一个double值。接下来,cube()返回一个double值(8.0),这个值被用作cheer()的参数。编译器将再一次检查原型,并发现cheer()要求一个int参数,因此它将返回值转换为整数8。通常,原型自动将被传递的参数强制转换为期望的类型。(但第8章将介绍的函数重载可能导致二义性,因此不允许某些自动强制类型转换。)

自动类型转换并不能避免所有可能的错误。例如,如果将8.33E27传递给期望一个int值的函数,则这样大的值将不能被正确转换为int值。当较大的类型被自动转换为较小的类型时,有些编译器将发出警告,指出这可能会丢失数据。

仅当有意义时,原型化才会导致类型转换。例如,原型不会将整数转换为结构或指针。

在编译阶段进行的原型化被称为静态类型检查(statictypechecking)。可以看出,静态类型检查可捕获许多在运行阶段非常难以捕获的错误。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇