#Z0301. 讲解自定义函数

讲解自定义函数

No testdata at current.

这一章,我们将向大家介绍 C++ 编程语言中,函数的概念、定义与调用。

大家在数学中已经接触过 函数 这个概念——但在程序设计中,所谓“函数”,指的是一个事先定义好的代码模块,一经定义,就可以在程序的多个位置重复调用。我们之前使用的诸如 size、pow 等等,就是 C++ 各类库中已经定义好的函数。

在程序开发过程中,一个较为复杂的系统往往需要划分为若干个子系统,然后对这些子系统,或者说子程序,分别进行开发和调试。在 C++ 语言中,这种子程序体现为函数。 函数编写好之后,就可以被重复使用——使用的时候用户只需要关心函数的功能和用法,而不用具体考虑怎么实现函数的功能。这样有利于代码重用,可以有效提高开发效率,增强程序的可靠性,也便于分工合作和维护。


为什么要写函数

我们来看下面这段代码:

for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= i; j++) {
        cout << '*';
    }
    cout << endl;
}
for (int i = 1; i <= 4; i++) {
    for (int j = 1; j <= i; j++) {
        cout << '*';
    }
    cout << endl;
}
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= i; j++) {
        cout << '*';
    }
    cout << endl;
}

程序会依次输出三个直角三角形。但是,这个程序看起来很“傻瓜”,写出了三段极为相似的二重循环。我们有没有办法把这段代码精简一下呢?


精简后的程序

void output(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            cout << '*';
        }
        cout << endl;
    }
}
int main() {
    output(3);
    output(4);
    output(5);
    return 0;
}

那么,这里的void又是什么意思呢?为什么要写出void output(int n) {这样的一行代码呢?output(3)被执行的时候发生了什么呢?


函数的定义

我们已经知道,在 C++ 语言中,任何变量在使用之前首先都要进行声明。同样地,任何函数在使用之前也要先完成定义。

简单地说,你要让编译器知道,你定义的这个函数里面究竟有什么代码需要执行,然后编译器才能在你调用函数的时候,知道接下来该做什么。

对于如下的函数定义:

void output(int n) {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            cout << '*';
        }
        cout << endl;
    }
}

其中,output为函数名,int n为形式参数表。形式参数表 说明了函数输入的每个参数的类型和名称,其声明方式与变量的声明类似。内部的两重循环为函数体语句,在这部分语句中,可以用形式参数表中的变量(如变量n)进行各种操作。

我们一般将void output(int n) {}称为函数的头部。


形参和实参

如果有多个形式参数(简称为 形参),则用逗号分隔即可,每个形式参数的类型都要明确写出,不能省略:

void abc(int a, int b, int c) {
}

形参的取值决定于调用这个函数的代码。例如,我们可以在主函数调用刚刚定义的输出三角形的output函数:

output(3);
output(4);
output(5);

每次调用的代码output(3)中的 3 就是 实际参数(简称 实参)。当我们指定实际参数为 3 之后,output函数的函数体语句中就可以认为形参变量n的初始值为 3,也就可以输出一个大小为 3 的直角三角形了。

函数的形参一定要和实参一一对应,参数个数和对应位置的参数类型完全相同。


数组参数

当我们需要使用数组作为参数时,可以这么写:

void sum(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

这个函数会输出一个有 n 个int类型元素的数组,当我们执行如下的调用语句后:

int a[] = {2, 3, 1, 4, 7, 6};
output(a, 4);

会输出:2 3 1 4。在实参中,我们可以传入一个值、一个变量、甚至一个式子(函数的形参有另外一种传递参数的方式,此时的实参必须为一个变量,不在本节课中展开介绍了)。


无参数函数

当一个函数没有任何形参时,可以按照如下的方式定义:

void hello() {
    int x;
    cin >> x;
    cout << "hello " << x + 1 << endl;
}
int main() {
    hello();
    return 0;
}

无论是函数的定义还是调用,函数名后的一对小括号都是必不可少的。如果不加这对小括号,会导致编译错误哦。


函数内修改参数

在函数内修改形参的值,是不一定会对实参的值产生影响的。比如下面这段代码:

void add(int x) {
    x++;
}
int main() {
    int a = 5;
    add(a);  // a == 5
    return 0;
}

执行add(a)之后,a的值仍然为 5 而不是 6。对于intstringdoublechar等类型的形参,都是如此。

只要当形参类型为数组时,修改形参的值会对应地修改实参的值:

void add(int a[]) {
    a[0]++;
}
int main() {
    int x[5] = {2, 3, 4, 5, 6};
    add(x);   // x == {3, 3, 4, 5, 6}
    return 0;
}

一定不要搞混哦。