函数介绍

函数的基本概念

在上一章中,我们定义了一个函数,它是一个按顺序执行的语句集合。虽然这确实是真的,但这个定义并没有提供多少关于为什么函数有用的洞见。让我们更新我们的定义:函数是一个可重用的语句序列,旨在完成特定的工作。 你已经知道每个可执行程序必须有一个名为main()的函数(这是程序运行时开始执行的地方)。然而,随着程序变得越来越长,将所有代码都放在main()函数中变得越来越难以管理。函数提供了一种将我们的程序分割成小的、模块化的块的方法,这些块更容易组织、测试和使用。大多数程序使用许多函数。C++标准库附带了许多已经编写好的函数供你使用——然而,编写你自己的函数也同样常见。你自己编写的函数被称为用户定义函数。 考虑一个可能在现实生活中发生的情况:你正在读书,突然想起你需要打个电话。你在书上放一个书签,打个电话,当你完成电话后,你回到你书签标记的地方,继续你的书,就在你离开的地方。 C++程序可以以相同的方式工作(并且借用一些相同的术语)。当程序在执行一个函数中的语句时遇到函数调用,它会按顺序执行。函数调用告诉CPU中断当前函数并执行另一个函数。CPU本质上在当前执行点"放一个书签",执行函数调用中命名的函数,然后返回到它书签标记的点并恢复执行。

术语

发起函数调用的函数是调用者,被调用(执行)的函数是被调用者。函数调用有时也被称为调用,调用者调用被调用者。

用户定义函数的一个例子

首先,我们从定义用户定义函数的最基本语法开始。在接下来的几课中,所有用户定义的函数都将采取以下形式:

returnType functionName() // 这是函数头(告诉编译器函数的存在)
{
    // 这是函数体(告诉编译器函数的作用)
}

第一行是非正式地称为函数头,它告诉编译器一个函数的存在,函数的名称,以及我们将在以后课程中介绍的其他信息(比如返回类型)。

在这节课中,我们将使用int(对于函数main())或void(否则)作为returnType。

就像变量有名称一样,用户定义的函数也有名称。functionName是你的用户定义函数的名称(标识符)。

标识符后的括号告诉编译器我们正在定义一个函数。

花括号和它们之间的语句被称为函数体。这就是决定你的函数将做什么的语句将去的地方。

函数调用与执行流程

要调用一个函数,我们使用函数的名称后跟一组括号(例如functionName()调用名称为functionName的函数)。按照惯例,括号紧贴函数名称放置(它们之间没有空格)。 目前,函数必须在可以调用之前定义。我们将在课程2.7 – 前向声明和定义中讨论解决这个问题的方法。

这是一个示例程序,说明了用户定义函数的定义和调用:

#include <iostream> // 用于std::cout

// 用户定义函数doPrint()的定义
// 在这个例子中,doPrint()是被调用的函数
void doPrint()
{
    std::cout << "In doPrint()\n";
}

// 用户定义函数main()的定义
int main()
{
    std::cout << "Starting main()\n";
    doPrint();                        // 通过函数调用来中断main()。main()是调用者。
    std::cout << "Ending main()\n";   // 这个语句在doPrint()结束后执行

    return 0;
}

这个程序产生以下输出:

Starting main()
In doPrint()
Ending main()

程序执行顺序

这个程序从函数main()的顶部开始执行,第一行执行的代码打印Starting main()。 main()中的第二行是对函数doPrint()的函数调用。我们知道它是一个函数调用,因为后面的括号。

警告 调用函数时,不要忘记在函数名称后加上括号()。如果你忘记了括号,你的程序可能无法编译(如果它确实编译了,函数将不会被调用)。

因为进行了函数调用,main()中语句的执行被暂停,执行跳转到被调用函数doPrint()的顶部。doPrint()中的第一行(也是唯一的一行)打印In doPrint()。当doPrint()终止时,执行返回到调用者(main())并从函数调用刚超出的点继续。因此,接下来在main()中执行的语句打印Ending main()。

多次调用函数

函数的一个有用之处在于它们可以被多次调用。以下是一个程序,演示了这一点:

#include <iostream> // 用于std::cout

void doPrint()
{
    std::cout << "In doPrint()\n";
}

// 函数main()的定义
int main()
{
    std::cout << "Starting main()\n";
    doPrint(); // doPrint()第一次被调用
    doPrint(); // doPrint()第二次被调用
    std::cout << "Ending main()\n";

    return 0;
}

这个程序产生以下输出:

Starting main()
In doPrint()
In doPrint()
Ending main()

由于doPrint()被main()调用了两次,doPrint()执行了两次,In doPrint()被打印了两次(每次调用一次)。

函数调用链

函数可以调用调用其他函数的函数

你已经看到,函数main()可以调用其他函数(比如上面例子中的函数doPrint())。被main()调用的函数也可以调用其他函数(以及那些函数也可以调用函数,等等…)。在以下程序中,函数main()调用函数doA(),它调用函数doB():

#include <iostream> // 用于std::cout

void doB()
{
    std::cout << "In doB()\n";
}


void doA()
{
    std::cout << "Starting doA()\n";

    doB();

    std::cout << "Ending doA()\n";
}

// 函数main()的定义
int main()
{
    std::cout << "Starting main()\n";

    doA();

    std::cout << "Ending main()\n";

    return 0;
}

这个程序产生以下输出:

Starting main()
Starting doA()
In doB()
Ending doA()
Ending main()

嵌套函数的限制

不支持嵌套函数 定义放在另一个函数内部的函数是嵌套函数。与其他一些编程语言不同,在C++中,函数不能被嵌套。以下程序是非法的:

#include <iostream>

int main()
{
    void foo() // 非法:这个函数嵌套在函数main()内部
    {
        std::cout << "foo!\n";
    }

    foo(); // 函数调用foo()

    return 0;
}

编写上述程序的正确方式是:

#include <iostream>

void foo() // 不再在main()内部
{
    std::cout << "foo!\n";
}

int main()
{
    foo();

    return 0;
}

术语解释

“foo"是一个无意义的词,经常用作函数或变量的占位符名称,当名称对某个概念的演示不重要时。这样的词被称为元语言变量(尽管在日常语言中,它们通常被称为"占位符名称”,因为没有人能记住"元语言变量"这个术语)。C++中其他常见的元语言变量包括"bar",“baz”,以及以"oo"结尾的3个字母的词,比如"goo",“moo"和"boo”)。 对于对词源学(单词如何演变)感兴趣的人,RFC 3092是一个有趣的阅读。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"