🧰 函数
函数就像一个「自动加工的黑盒子」:把材料从一边塞进去,它咔咔一加工,从另一边吐出成品。把经常做的事包成函数,主程序就能又短又清楚,这叫做模块化。
考纲知识点:函数的定义、调用、声明(不包含匿名函数、类的括号运算符重载)
形参、实参
全局作用域、局部作用域
值传递、引用传递
形参、实参
全局作用域、局部作用域
值传递、引用传递
📖
考纲 · 知识点详述
摘自《CCF编程能力等级认证 C++&Python 认证标准》C++ 四级
- (3)理解模块化编程思想,掌握函数(不包含匿名函数、类的括号运算符重载)的声明、定义及调用,掌握形参与实参的概念及区别。
- (4)掌握变量作用域的概念,理解全局变量与局部变量的区别。
- (5)掌握函数参数的传递方式:C++值传递、引用传递、指针传递。
📚
先学一学
花 5 分钟读完下面 5 课,再去闯关就不慌啦
1
🧃 函数像榨汁机:进水果,出果汁
函数像一个黑盒子:你只关心「塞进去什么、吐出来什么」,不用管里面怎么转。塞进去的叫参数,吐出来的叫返回值。做加法的小函数长这样:
🧪 读代码:定义 add 并调用它
看得懂就好,不用背:
int add(int a, int b) {
return a + b;
}
int main() {
cout << add(3, 4); // 调用函数,输出 7
}
return a + b;
}
int main() {
cout << add(3, 4); // 调用函数,输出 7
}
int add(...) 前面的 int 说「吐出来的是整数」;return a+b 是「把答案递出去」。在 main 里写 add(3,4) 就是调用它,输出 7。
💡 口诀:先定义(造机器)→ 再调用(用机器);return 把结果递出来。
📜 声明 vs 定义
定义是把函数完整写出来(造机器);声明是只提前说一句「待会有个这样的函数」,写 int add(int a, int b);(末尾有分号),这样就算把定义放到 main 后面,程序也知道怎么调用。
💡 口诀:声明像「先报个名」,定义像「把本领练出来」。
2
📋 形参 vs 实参:菜谱和真菜
菜谱上写着「鸡蛋 2 个、糖 1 勺」,可你还没去买——这些只是写在纸上的名字。等你真的买了鸡蛋和糖放进锅里,才是真东西。C++ 里:
🧪 认一认:哪个是形参、哪个是实参
看得懂就好,不用背:
int add(int a, int b) { return a + b; }
// ↑↑↑↑↑↑↑↑↑↑↑ a、b 是形参(纸上的名字)
int main() {
cout << add(3, 4);
// ↑↑↑↑ 3、4 是实参(真值)
}
// ↑↑↑↑↑↑↑↑↑↑↑ a、b 是形参(纸上的名字)
int main() {
cout << add(3, 4);
// ↑↑↑↑ 3、4 是实参(真值)
}
定义函数括号里的是形参(相当于菜谱上的名字);调用时填进去的是实参(相当于真买来的材料)。实参 3 传给形参 a,实参 4 传给形参 b。
💡 口诀:形参是「菜谱上写下的名字」,实参是「真交给函数的材料」。
3
🚪 作用域:房间里的东西 vs 楼道的公用柜
每个函数就像一个小房间。在函数里声明的变量是局部变量,只在「这个房间」里有效,出了房间就找不到了。在所有函数外面(main 之外)声明的变量是全局变量,像楼道的公用柜,谁都能用。
🧪 读代码:猜输出
看得懂就好,不用背:
int total = 0; // 全局变量(楼道公用柜)
void addOne() {
int x = 1; // 局部变量(addOne 房间的)
total += x;
}
int main() {
addOne();
cout << total; // 输出 1
// cout << x; // 报错!x 在房间外看不到
}
void addOne() {
int x = 1; // 局部变量(addOne 房间的)
total += x;
}
int main() {
addOne();
cout << total; // 输出 1
// cout << x; // 报错!x 在房间外看不到
}
total 是全局的,addOne 改它、main 读它都行;x 是 addOne 房间里的,main 想用 x 就会报错。
💡 口诀:房间里的东西不出房间;楼道公用的东西全楼能用。
⚠️ 重名时听谁的?
如果全局变量和局部变量同名,在函数里先用「房间自己的」(局部优先),像房间里自己买了台电视,就不会先看楼道的。出了房间,才轮到全局那台。
💡 口诀:同名撞车,近的优先——局部盖过全局,出房间全局才现身。
4
📄 传值 / 传引用 / 传指针:复印 vs 原文件
把作业交给老师有两种方式:复印一份交上去(老师改复印件,原件不动);或者直接把原文件给老师(老师在原件上改,你的也变了)。函数传参也一样:
🧪 读代码:值传递改不动外面
看得懂就好,不用背:
void f(int b) { b = 99; } // 值传递:收到复印件
int main() {
int a = 5;
f(a);
cout << a; // 还是 5!
}
int main() {
int a = 5;
f(a);
cout << a; // 还是 5!
}
形参 b 是 a 的复印件,函数里把 b 改成 99,原件 a 不受影响,输出 5。
💡 口诀:值传递 = 复印;函数怎么改复印件,原件都不变。
✏️ 引用传递:直接把原件递进去
看得懂就好,不用背:
void f(int& b) { b = 99; } // 引用传递:b 就是 a 的别名
int main() {
int a = 5;
f(a);
cout << a; // 变成 99!
}
int main() {
int a = 5;
f(a);
cout << a; // 变成 99!
}
参数类型写成 int& b(多一个 &),b 就成了 a 的「另一个名字」,改 b 就是改 a,输出 99。
💡 口诀:引用传递 = 给原件起小名;函数用原文件,改了外面也变。
📍 指针传递:告诉函数「东西在哪个门牌号」
看得懂就好,不用背:
void f(int* p) { *p = 99; } // 指针传递:传地址
int main() {
int a = 5;
f(&a); // 把 a 的门牌号交给函数
cout << a; // 变成 99!
}
int main() {
int a = 5;
f(&a); // 把 a 的门牌号交给函数
cout << a; // 变成 99!
}
参数是 int* p,调用时传 &a(门牌号)。函数用 *p 进屋改货,外面 a 也变。引用和指针都能改外面,区别是:引用直接用 b,指针要写 *p。
💡 口诀:值=复印、引用=小名、指针=门牌号;后两个能改外面,值不能。
5
🧱 模块化:把大任务拆成小积木
🕹️ 做游戏的例子
做游戏要「显示血条、检查碰撞、放音乐」三件事。与其全塞在 main 里写得又长又乱,不如做成三个函数:showHp()、checkHit()、playMusic()。main 里只要一行行调用它们,哪里出问题就去修哪个小函数——这就叫模块化编程。
💡 口诀:一个函数干一件事,main 只当「总指挥」点名开工。
🚫 不考的边角
四级考纲的函数不包含匿名函数、类的括号运算符重载,也就是说:函数都要有名字,先别管那些绕口的进阶写法。把「定义、调用、声明、形参实参、作用域、三种传参」掌握好就稳稳的。
💡 口诀:先做有名字的小积木,别碰匿名函数和括号重载。
🎯
闯关小锦囊 · 考点提醒
- 函数 = 黑盒:塞参数、return 返回值;定义写完整、声明先报名、调用用起来。
- 形参是定义括号里写下的名字(菜谱),实参是调用时填的真值(真菜)。
- 作用域:局部变量只在函数房间内有效;全局变量全楼能用;同名时局部优先。
- 值传递改不动外面(复印件);引用传递 int& b 和指针传递 int* p 都能改外面。
- 不考匿名函数、类的括号运算符重载;一个函数干一件事就是模块化。
🎮
学完了?来闯关!
下面 16 个小挑战,点一点就能玩
Demo 原型 · 每个知识点独立页面 · 暂不含真实编译与进度存储。