函数:定义、参数与返回值
这是什么:函数 = 给一段逻辑起个名字、打个包,以后随取随用。
为什么用:代码一多就该学会"打包"——把一段功能装进函数,随取随用。C++ 的函数比 Python 多了点仪式感,但每个仪式都有用处:编译器从上往下读,函数用之前得先"报名"。
如何用:先写一个 add 函数体验"先声明、后定义",再试试同名不同参的重载(overload),最后看参数为什么默认只能拿到复印件。
声明与定义:先报名,后干活
C++ 编译器从上往下读文件,函数在被用到之前,得让它"认识"这个函数:
#include <iostream>
int add(int a, int b); // 声明:告诉编译器"有这么个函数"
int main() {
std::cout << add(3, 4) << std::endl;
return 0;
}
int add(int a, int b) { // 定义:真正的实现
return a + b;
}
你会看到
7
| 组成 | 例子 | 含义 |
|---|---|---|
| 返回类型 | int |
函数交回来的数据类型 |
| 函数名 | add |
调用时喊的名字 |
| 参数列表 | (int a, int b) |
进厂原料,每个参数都要标类型 |
return |
return a + b; |
把结果交回去,类型必须和返回类型一致 |
自己练手时,把函数定义直接写在 main 上面、省掉声明也完全可以;声明和定义分开写是多文件工程里的标准姿势,先混个脸熟。
参数是"传值":改了也白改
#include <iostream>
void tryChange(int x) { // void = 不返回任何东西
x = 100; // 只改了自己手里那份"复印件"
}
int main() {
int a = 1;
tryChange(a);
std::cout << a << std::endl;
return 0;
}
你会看到
1
C++ 默认传值:函数收到的是原件的复印件,怎么改都烧不到外面的 a。(想让函数真改外面的值?下一章的"引用"就是干这个的。)void 表示"什么都不交回来",相当于 Python 函数不写 return。
函数重载:同名不同参(C++ 特色)
Python 里定义两个同名函数,后者会覆盖前者;C++ 却允许同名函数共存——只要参数不一样:
#include <iostream>
int area(int w, int h) { // 两个 int:算矩形
return w * h;
}
double area(double r) { // 一个 double:算圆
return 3.14 * r * r;
}
int main() {
std::cout << area(3, 4) << std::endl;
std::cout << area(2.0) << std::endl;
return 0;
}
你会看到
12
12.56
编译器看你传的参数类型和个数,自动帮你挑对的那一个函数——这叫重载(overload)。
注意:仅凭返回类型不同不能构成重载。下面这段是错误的:
int area(int w, int h); // 返回 int
double area(int w, int h); // 返回 double,参数一样,不构成重载,编译报错
常用标准库函数预习
C++ 标准库自带很多好用函数,后面章节会反复用到:
| 功能 | 函数 | 头文件 | 例子 |
|---|---|---|---|
| 绝对值 | std::abs() |
<cstdlib> / <cmath> |
std::abs(-5) 得 5 |
| 幂运算 | std::pow() |
<cmath> |
std::pow(2, 3) 得 8 |
| 平方根 | std::sqrt() |
<cmath> |
std::sqrt(16) 得 4 |
| 最大/最小 | std::max() / std::min() |
<algorithm> |
std::max(3, 7) 得 7 |
| 交换 | std::swap() |
<utility> / <algorithm> |
std::swap(a, b) |
❓ 常见问题
Q1:函数定义一定要写在 main 下面吗?
不一定。如果定义写在 main 上面,就不需要单独声明;如果写在下面,必须先声明,否则编译器在 main 里看到 add(3,4) 时"不认识"这个函数,会报错。
Q2:void 函数能用 return 吗?
可以,但只能单独写 return; 用来提前结束函数,不能带返回值(如 return 5; 在 void 函数里会报错)。
Q3:参数可以省略类型吗?
不行。C++ 的每个函数参数都必须显式标类型,如 int a, int b,不能像 Python 那样只写 a, b。
⚠️ 新手翻车现场
翻车一:在 main 下面定义函数,却忘了前面声明
// 错误:main 里调用了 add,但 add 的定义在 main 下面,编译器不认识
int main() {
std::cout << add(3, 4);
return 0;
}
int add(int a, int b) {
return a + b;
}
问题:编译器从上往下读,看到 add(3,4) 时还没读到 add 的定义,报错"'add' was not declared in this scope"。
修正:在 main 上面加一行声明 int add(int a, int b);,或者把 add 的完整定义挪到 main 上面。
翻车二:函数声明和定义的参数类型不一致
int add(int a, int b); // 声明:两个 int
double add(int a, int b); // 定义:返回 double,参数一样,不构成重载,冲突
问题:参数列表完全一样,只返回类型不同,编译器视为重复定义,报错。
修正:要么让参数不同(构成合法重载),要么让返回类型也一致。
🎯 什么时候用函数
- 一段代码要被用两次及以上
- 需要给代码起个有意义的名字,让主程序更易读
- 想把复杂逻辑拆成小块,逐个测试
📝 本章小结
- 函数由:返回类型、函数名、参数列表(每个都要标类型)、函数体组成
- C++ 编译器从上往下读,用到函数前要先声明(或先定义)
- 默认传值:函数收到原件的复印件,改不了外面的变量
- 函数重载:同名不同参可共存,编译器按参数类型和个数挑选
void表示不返回任何东西- 记忆口诀:返回类型打头阵,参数个个标类型;传值是复印件,重载看参数。
动手改
- 写
double celsiusToFahrenheit(double c),把 37 度转成华氏度(公式c * 9 / 5 + 32),验证结果是 98.6 - 写
void sayHi(std::string name),打印"你好,××"(记得在文件头加#include <string>) - 给
area再加一个重载:int area(int side),算正方形面积 - 写一个函数
int maxOfThree(int a, int b, int c),返回三个数中的最大值
下一章:指针与引用,正面硬刚 C++ 的灵魂难关 🐸