C++ 函数参数与返回值

在 C++ 中,编写自定义函数时最常打交道的就是 “参数” 和 “返回值”。简单来说,参数负责把外部的数据传进函数,返回值则负责把函数算出来的结果返回给调用处。

C++ 函数参数

在 C++ 中,如果一个函数需要外部提供数据才能完成计算,我们就可以在函数名后面的小括号 () 中定义参数。

1. 形参与实参

在学习函数参数时,小伙伴们经常会遇到 “形参” 和 “实参” 这两个词。结合定义函数和调用函数的语法来看,它们其实非常容易区分。

语法:

// 定义函数
返回类型 函数名(类型1 形参1, 类型2 形参2) {
    // 函数体
}

// 调用函数
函数名(实参1, 实参2);

说明:

在上面的语法中,主要涉及以下 3 个基本概念:

  • 形参:即 “形式上的参数”,指的是定义函数时在小括号里写出来的参数变量,用来接收调用者传进来的数据。至于在函数内部修改形参会不会影响外面的原变量,这关系到函数的值传递与引用传递,后面章节会详细介绍。
  • 实参:即 “实际上的参数”,指的是调用函数时真正传入的具体数据。实参不仅可以是具体的数值,也可以是已有的变量或算术表达式。
  • 参数列表:函数名后面小括号中的全部参数,多个参数之间使用英文逗号(,)隔开。

定义多个形参时有一个初学者很容易踩坑的细节:每一个形参前面都必须单独写出数据类型,不能像声明普通变量那样合并简写,如下所示:

// 正确方式
int getTotal(int price, int count) {
    return price * count;
}

// 错误方式
int getTotal(int price, count) {
    return price * count;
}

示例 1:传入不同形式的实参

#include <iostream>

// 形参:单价和数量
int getTotal(int price, int count) {
    return price * count;
}

int main() {
    int total1 = getTotal(20, 3);                 // 传入具体数值

    int itemPrice = 25;
    int num = 2;
    int total2 = getTotal(itemPrice, num);        // 传入变量
    int total3 = getTotal(itemPrice + 5, num);    // 传入表达式

    std::cout << total1 << '\n';
    std::cout << total2 << '\n';
    std::cout << total3 << '\n';
    return 0;
}

运行结果如下。

60
50
60

分析:

在这个例子中,函数头小括号里的 price 和 count 是形参,相当于函数内部提前准备好的两个变量。每次调用函数时,其实就是把实参的值赋给对应的形参,和普通的变量赋值是一样的道理。

在 main() 中,我们第 1 次调用时传入数值 20 和 3,算出 60。第 2 次调用传入变量 itemPrice 和 num,算出 50。第 3 次传入表达式 itemPrice + 5 时,程序会先算出 30,再传进函数得到 60。

2. 多参数的传递顺序

当一个函数包含多个参数时,调用处传入的实参会严格按照 “从左往右的先后位置” 依次与形参对应。也就是说,第 1 个实参赋给第 1 个形参,第 2 个实参赋给第 2 个形参,以此类推。

示例 2:多参数按位置对应

#include <iostream>

// 原价减去优惠金额
int getFinalPrice(int price, int discount) {
    return price - discount;
}

int main() {
    int price1 = getFinalPrice(100, 20);    // 原价 100,优惠 20
    int price2 = getFinalPrice(20, 100);    // 实参顺序写反

    std::cout << "正常顺序结果:" << price1 << '\n';
    std::cout << "写反顺序结果:" << price2 << '\n';
    return 0;
}

运行结果如下。

正常顺序结果:80
写反顺序结果:-80

分析:

getFinalPrice(100, 20) 表示将第 1 个实参 100 赋给 price,将第 2 个实参 20 赋给 discount,算出来的结果是 80。

如果我们调用时不小心写反成 getFinalPrice(20, 100),由于两个参数都是 int 类型,编译时并不会报错。但此时的含义变成了 “原价 20,优惠 100”,最终算出了 -80。因此调用多参数函数时,小伙伴们要确保实参的个数、顺序和类型都与形参一致。

C++ 函数返回值

在 C++ 中,参数解决的是 “数据怎么传进函数”,而返回值解决的则是 “函数算完以后,怎么把结果返回给调用处”。

1. 使用 return 返回结果

定义函数时,函数名前面写的类型就是该函数的 “返回类型”。当函数内部计算出结果后,我们需要使用 return 语句把对应类型的结果返回给调用位置。

语法:

return 表达式;

说明:

执行 return 语句时,程序会先算出后面表达式的值,再把这个值返回给调用该函数的地方。因此,一次有返回值的函数调用本身就代表一个具体的值:我们既可以把它赋给变量保存起来,也可以直接使用 std::cout 把它打印出来,或者继续作为实参传给另一个函数。

示例 3:接收并使用返回值

#include <iostream>

// 计算商品总价
int getTotal(int price, int count) {
    return price * count;
}

// 计算减去优惠后的应付金额
int applyDiscount(int total, int discount) {
    return total - discount;
}

int main() {
    int total = getTotal(20, 3);
    int finalPrice = applyDiscount(total, 15);

    std::cout << "最终应付:" << finalPrice << '\n';
    return 0;
}

运行结果如下。

最终应付:45

分析:

在这个例子中,调用 getTotal(20, 3) 时,函数算出总价 60 并通过 return 返回给 main(),保存到变量 total 中。接着我们又把 total 传给 applyDiscount(total, 15),减去 15 元优惠后算出应付金额 45。

另外,如果我们单独写一行 getTotal(20, 3); 而不去接收或打印它,语法上也是合法的。只不过这样一来,函数算出来的结果就会被直接丢弃。

注意 只要函数名前面的返回类型不是 void,函数体内部就必须使用 return 语句返回一个对应的值。一旦漏写 return 让函数直接走到末尾结束,就会引发未定义行为,调用处可能拿到随机的垃圾值,甚至直接导致程序崩溃。

2. 返回值与函数返回类型

对于一个函数来说,它最终返回什么类型的数据,是由函数名前面声明的 “返回类型” 决定的,而不是由 return 后面的表达式决定的。如果两者类型不一致,C++ 会统一以函数名前面的返回类型为准。下面我们来看一个例子。

示例 4:返回值类型不一致

#include <iostream>

int getPi() {
    double pi = 3.14;
    return pi;
}

int main() {
    std::cout << getPi() << '\n';
    return 0;
}

运行结果如下。

3

分析:

从运行结果可以看出,pi 的值明明是 3.14,最终输出的却是 3。这是因为 getPi() 声明的返回类型是 int,而 return 后面的 pi 是 double 类型,C++ 会自动触发隐式类型转换,直接截断小数部分只保留整数 3。

因此在实际开发中,我们一定要保证函数名前面的返回类型与 return 返回值的类型一致。比如上面的例子就应该把函数头改为 double getPi(),避免小数部分被意外丢掉。

3. return 提前结束函数

return 除了用来返回计算结果之外,还有一个重要特点:一旦函数执行到了 return 语句,当前这一次函数调用就会立即结束,return 后面尚未执行的代码不会再运行。

示例 5:根据条件提前返回

#include <iostream>

int getTicketPrice(int age) {
    if (age < 12) {
        return 20;
    }
    return 40;
}

int main() {
    std::cout << "10 岁票价:" << getTicketPrice(10) << '\n';
    std::cout << "20 岁票价:" << getTicketPrice(20) << '\n';
    return 0;
}

运行结果如下。

10 岁票价:20
20 岁票价:40

分析:

当我们第 1 次调用 getTicketPrice(10) 时,条件 age < 12 成立,执行 return 20; 后函数立即结束,后面的代码不再运行。第 2 次调用传入 20 时,条件不成立,程序跳过 if 代码块直接执行最后的 return 40;。

编写这类带条件判断的有返回值函数时,大家要确保所有分支路径下都有 return 兜底。如果把上面最后的 return 40; 删掉,一旦传入大于或等于 12 的年龄,函数走到底也没有返回任何结果,就会出错。

再看下面这段代码,调用 testReturn(5) 返回的结果是 10 而不是 100,小伙伴们可以思考一下为什么。

int testReturn(int x) {
    return x * 2;
    x = 100;
    return x;
}

4. 无返回值函数(void)

并不是每个函数都需要返回计算结果。如果某个函数的任务只是完成一个具体动作,比如在屏幕上打印一段提示信息,我们把它的返回类型写成 void 就可以了。

示例 6:带参数的 void 函数

#include <iostream>

void showScore(int score) {
    std::cout << "当前成绩:" << score << '\n';
}

int main() {
    showScore(95);
    return 0;
}

运行结果如下。

当前成绩:95

分析:

void showScore(int score) 前面的 void 表示该函数没有返回值,但它依然可以正常接收 int 类型的参数。调用 showScore(95); 时,函数只负责执行打印动作,并不会返回任何值给 main()。

正因为 void 函数没有返回值,所以我们不能把它的调用结果赋给变量,写成 int res = showScore(95); 会直接编译报错。另外,在 void 函数中虽然不能写 return 具体值;,但可以单独写一个不带值的 return;,用来让函数提前退出:

void showScore(int score) {
    if (score < 0) {
        return;    // 分数小于 0 时提前退出
    }
    std::cout << "当前成绩:" << score << '\n';
}

return 与 std::cout 的区别

很多初学的小伙伴在写函数时,容易把 return 和 std::cout 混为一谈。其实两者的职责完全不同:

return 负责把结果返回给代码:它把函数算出来的值传回调用处,方便我们赋给变量或继续参与运算,但本身不会在屏幕上打印任何文字。如果在 main() 里只写一句 getTotal(20, 3); 而不输出,屏幕上不会有任何显示。

std::cout 负责把内容显示到屏幕:它只把数据打印到控制台给人查看,并不会给调用处返回任何值。只要后面的程序还需要用到函数的计算结果,我们就应该使用 return 返回,而不能只写 std::cout。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号