基础
C++ 函数:完整图景
一句话答案:函数是一段有名字的代码,带类型的输入(参数)和一个类型的输出(返回值)。调用它,就是带着具体输入跑一遍这段代码,然后把结果交回来。
结构
int add(int a, int b) { // 返回类型、名字、参数表
return a + b; // 函数体,以返回一个 int 结束
}
这一行有四处要读:int 是交出什么,add 是名字,int a, int b 是输入,return 产生输出。没有返回值的函数声明为 void。
调用就是代入
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int square(int x) {
return x * x;
}
int main() {
cout << add(2, 3) << endl; // 5
cout << square(add(1, 1)) << endl; // (2)^2 = 4 —— 调用里套调用
return 0;
}
第二行才是重点:函数调用本身就是一个值,所以它出现在任何能放字面量的地方。函数可组合性就来自这里。
参数是拷贝
这是关于 C++ 函数最重要的一个事实:
#include <iostream>
using namespace std;
void tryToChange(int n) {
n = 999; // 只改了本地拷贝
}
int main() {
int x = 5;
tryToChange(x);
cout << x << endl; // 还是 5
return 0;
}
真要改调用方的变量,必须传地址(指针)或引用。三种传法的完整对比(带图)在传值、指针还是引用。
返回 vs 打印
返回值的函数能参与表达式,打印的不行:
#include <iostream>
using namespace std;
int doubled(int n) { return n * 2; }
void printed(int n) { cout << n * 2 << endl; }
int main() {
int total = doubled(10) + 1; // 可以:doubled() 是一个值
cout << total << endl;
printed(10); // 可以,但没法复用
return 0;
}
家规:在函数里算(return),在 main 里打印。这样函数既可测又可复用。
作用域:名字住在哪
#include <iostream>
using namespace std;
int counter = 0; // 全局:到处可见
int bump() {
int local = 5; // 局部:只在这次调用中存在
counter++;
return counter + local;
}
int main() {
cout << bump() << endl; // 6
cout << bump() << endl; // 7 —— 全局活下来了
// cout << local; // 错误:local 不在此作用域
return 0;
}
优先用局部变量:函数需要什么就作为参数传进去,产出什么就返回出来。全局变量很方便,却会悄悄让代码难以推理。
到底为什么要拆函数
- 一处定义,多处使用。规则改一处,所有调用点跟着变。
- 名字变成句子。
isPrime(n)比十二行试除好读。 - 可测性。有参数有返回值的函数,看输出就能验证;打印并修改全局的代码块不能。
- 递归才有可能。函数能调用自己 —— 这是递归的基础。
三个 bug
- 走到末尾却没返回。声明返回
int,但某条路径没有return—— 未定义行为,不是编译错误。 - 指望按值传参能改值。新手最常见的困惑:函数改的是拷贝。
- 先用后声明。C++ 需要在调用前知道签名 —— 这就是声明(或把定义写在
main之前)的作用。