02-函数与作用域:把代码封装成工具¶
目录页:见 C++ 小白教程目录
目标¶
- 把一段重复代码封装成函数并正确调用
- 分清“声明/定义”“形参/实参”“返回值/返回类型”
- 彻底搞懂作用域:变量在哪能用、什么时候消失
先决知识¶
- 会写
int main()并能用std::cin/std::cout输入输出(见 01 章 0.1、0.4、1.1、1.2) - 会用 if/for/while 写基础逻辑(见 01 章 4、5)
1. 为什么需要函数:把重复劳动交给“机器”¶
当你的程序开始出现重复代码时,函数就是“把重复劳动装进一个按钮”:
- 专业名称:函数(function)
- 类比:像一台机器,你把材料放进去(参数),按下开关(调用),机器吐出结果(返回值)
- 作用:减少重复、让代码更好读、更好改、更不容易出错
- 规则/坑点:函数要么定义在使用之前,要么提前写声明(函数原型)
2. 函数的 4 个关键点:名字、输入、输出、实现¶
先看一个完整可运行的例子:写一个 max2,返回两个整数里的较大值。
#include <iostream>
int max2(int a, int b) {
if (a > b) {
return a;
}
return b;
}
int main() {
int x = 0;
int y = 0;
std::cin >> x >> y;
int ans = max2(x, y);
std::cout << "max=" << ans << '\n';
return 0;
}
把 int max2(int a, int b) { ... } 这行拆开:
int:返回类型(return type)max2:函数名(function name)(int a, int b):参数列表(parameter list)a、b是形参(parameter):函数“内部用的名字”x、y是实参(argument):调用时“传进去的具体值”{ ... }:函数体(function body):机器内部怎么加工
return 的含义:
- 专业名称:返回语句(return statement)
- 类比:像“出货口把成品交给调用者”
- 作用:结束函数并把一个值带出去
- 规则/坑点:返回类型是
int就必须返回一个int;漏掉 return 会导致编译错误或未定义行为(取决于情况)
3. 声明 vs 定义:先挂招牌还是先开店¶
你会遇到一种常见写法:main 写在上面,函数实现写在下面。此时需要“先声明”。
- 专业名称:函数声明/函数原型(function declaration / function prototype)
- 类比:像先挂招牌:告诉别人“这家店叫什么、提供什么服务、输入输出是什么”,实现细节以后再写
- 作用:让编译器在看到调用时,已经知道函数的签名(signature)
- 规则/坑点:声明要和定义一致(返回类型、参数个数、参数类型)
可运行例子(函数定义放在 main 后面):
#include <iostream>
int max2(int a, int b);
int main() {
int x = 0;
int y = 0;
std::cin >> x >> y;
std::cout << "max=" << max2(x, y) << '\n';
return 0;
}
int max2(int a, int b) {
if (a > b) {
return a;
}
return b;
}
这里 int max2(int a, int b); 就是声明:末尾有分号 ;,没有 {}。
4. 参数传递(入门版):值传递 = 传“复印件”¶
这章先只讲你最常用也最安全的:值传递(pass by value)。
- 专业名称:值传递(pass by value)
- 类比:把实参复印一份交给函数,函数改的是复印件,不会影响原件
- 作用:简单、安全、不容易改坏外面的变量
- 规则/坑点:如果参数很大(比如很长的字符串或大结构体),复制会有成本,后面再讲引用/指针优化
看一个“新手最容易误解”的例子:你以为函数能把外面的 x 改掉,其实改不掉。
#include <iostream>
void setToZero(int x) {
x = 0;
}
int main() {
int x = 123;
setToZero(x);
std::cout << "x=" << x << '\n';
return 0;
}
输出还是 x=123,因为函数里拿到的是一份复印件。要想真正修改外面的变量,需要引用/指针(放到第 04 篇讲)。
5. 作用域(scope):变量能活在哪些地方¶
你写的每个变量,都有“能用的范围”。这就是作用域。
- 专业名称:作用域(scope)
- 类比:变量像“房间里的物品”,你出了房间就看不见、也用不了
- 作用:控制名字可见范围,避免到处乱用、互相撞名
- 规则/坑点:C++ 里最常见的是块作用域:一对
{}就是一个范围
5.1 块作用域:{} 里的变量,外面不能用¶
#include <iostream>
int main() {
if (true) {
int inside = 10;
std::cout << "inside=" << inside << '\n';
}
return 0;
}
如果你在 if 外面写 std::cout << inside;,会编译失败:名字在外面不可见。
5.2 for 的计数器作用域¶
#include <iostream>
int main() {
for (int i = 0; i < 3; i++) {
std::cout << i << '\n';
}
return 0;
}
i 只在 for 的小括号和循环体里可见。循环结束后 i 就“消失”了。
5.3 变量生命周期(lifetime):什么时候创建、什么时候销毁¶
- 专业名称:生命周期(lifetime)
- 类比:变量像“临时工”,进入代码块上班,离开代码块下班
- 作用:理解为什么变量会“消失”,也能理解后面指针为什么会危险
- 规则/坑点:局部变量(local variable)离开作用域就销毁,再去用它会出大问题(后面会专门讲)
常见坑¶
-
错误:在调用之前没有声明/定义函数
结果:编译器找不到函数
正确:把函数定义写在main前,或写函数声明(见第 3 节) -
错误:
int函数少写了return
结果:编译错误或行为不确定
正确:保证所有路径都有return(或重构逻辑) -
错误:想在函数里改外面的变量,但用了值传递
结果:外面的变量不变
正确:需要引用/指针(第 04 篇再做) -
错误:把变量定义在
{}里,出了{}还想用
结果:编译失败
正确:把变量定义到需要使用的共同外层作用域
小练习¶
练习 1:写 3 个小工具函数¶
- 题目:实现
max2、min2、absInt - 输入/输出:自行在
main里读入两个整数并输出三个结果 - 约束:只用
if和return,先别用三目运算符 - 提示:
absInt(x):如果 x<0 返回 -x,否则返回 x - 目标:练函数定义、返回值、调用
练习 2:求和函数¶
- 题目:实现
int sum1ToN(int n),返回1+2+...+n - 输入/输出:输入 n,输出
sum=... - 约束:n 为正整数
- 提示:函数里用 for/while 都行
- 目标:练“函数里写循环”
练习 3:判断素数函数(复用上一章思路)¶
- 题目:实现
bool isPrime(int n) - 输入/输出:输入 n,输出
prime=1或prime=0 - 约束:n >= 2
- 提示:先用最朴素的试除写法即可;想优化可以做到
i*i <= n - 目标:练返回
bool、函数逻辑分支
练习 4:做一个“计算器”程序(综合)¶
- 题目:写一个循环菜单:1 加 2 减 3 乘 4 除 0 退出
- 输入/输出:用户先输入选择,再输入两个数,输出结果
- 约束:除法要处理除数为 0(提示错误并继续循环)
- 提示:把加减乘除分别写成 4 个函数;
main只负责菜单与调度 - 目标:练“把大程序拆成小函数”,感受可读性提升
小结¶
你现在应该掌握:
- 函数的基本结构:返回类型、函数名、参数列表、函数体
return的意义与“必须返回”的规则- 声明/定义的区别,以及为什么需要函数原型
- 作用域与生命周期:变量能用的范围、什么时候消失
下一篇会把“一组数据”放进数组和字符串里,让你能处理更真实的输入。