C++ 第 13 课:值传递 —— 为什么函数里改了,外面却没变?
上一课标准答案:
- 不能。 因为
a是test()里的局部变量,只能在test()的作用域里使用。 - 输出:
6
6
因为每次调用 test(),都会重新创建一个新的 a = 5,然后 a++ 变成 6。
3. 可以。 因为 a 定义在外层 main() 里,内层 if 可以访问外层变量。
4. b 定义在 if 的 {} 里面,离开这对花括号后,它的作用域就结束了,所以外面不能直接使用。
第 13 课:值传递 —— 为什么函数里改了,外面却没变?
这一课非常重要,因为它直接通向后面的:
引用 &
指针 *
函数参数
数组
对象
机器人状态修改
先看一个很奇怪的例子。
#include <iostream>
void change(int a)
{
a = 100;
}
int main()
{
int x = 5;
change(x);
std::cout << x << std::endl;
return 0;
}
你可能会直觉地觉得:
change(x)里面把a改成了 100,那x应该也变成 100。
但实际输出是:
5
为什么?
1. 关键:a 和 x 不是同一个变量
看调用:
change(x);
假设:
x = 5;
调用函数时,C++ 会把 x 的值:
5
复制一份,交给函数参数:
int a
所以可以想象成:
main 里面:
x
┌─────┐
│ 5 │
└─────┘
调用 change(x)
复制一个 5
↓
change 里面:
a
┌─────┐
│ 5 │
└─────┘
注意:
x和a是两个不同的盒子。
只是开始时里面都放着 5。
2. 函数里面改的是 a
执行:
a = 100;
于是变成:
main:
x
┌─────┐
│ 5 │
└─────┘
change:
a
┌─────┐
│ 100 │
└─────┘
所以改的是:
a
不是:
x
函数结束以后,a 消失。
而 x 从头到尾还是:
5
3. 这就叫“值传递”
专业名字:
pass by value,值传递
意思是:
把变量里的“值”复制一份传进去。
不是把原变量本身交进去。
4. 再看一个更明显的例子
void addOne(int number)
{
number++;
}
然后:
int main()
{
int count = 10;
addOne(count);
std::cout << count << std::endl;
}
调用时:
count = 10
复制给:
number = 10
然后函数里:
number++;
变成:
number = 11
但是:
count
依然:
10
所以最后输出:
10
5. 用“复印件”理解最简单
你可以把值传递理解成:
你把文件复印一份给别人。
原文件:
x = 5
复印件:
a = 5
别人把复印件改成:
a = 100
你的原文件:
x = 5
不会自动变化。
这就是值传递最好的直觉。
6. 那为什么之前 calculateError 没问题?
之前我们写:
double calculateError(double target, double current)
{
return target - current;
}
调用:
double result = calculateError(50.0, 20.0);
这里我们根本不需要修改外部变量。
函数只是:
拿到两个值
↓
计算
↓
返回结果
所以值传递非常合适。
7. 值传递什么时候特别常见?
例如:
double square(double x)
{
return x * x;
}
调用:
double result = square(3.0);
我们只是想让函数计算:
3 × 3
并不希望外面的变量被改掉。
所以值传递非常安全。
8. 一个机器人例子
double limitAngle(double angle)
{
if (angle > 90.0)
{
angle = 90.0;
}
return angle;
}
调用:
double joint_angle = 120.0;
double safe_angle = limitAngle(joint_angle);
函数收到:
angle = 120
然后改成:
angle = 90
返回:
90
所以:
safe_angle = 90
但原来的:
joint_angle
仍然是:
120
因为传进去的是副本。
9. 这其实有好处
值传递的一个优点就是:
函数内部乱改参数,不会影响外面的原变量。
例如:
void test(int x)
{
x = 999;
}
你在外面:
int motor_id = 2;
test(motor_id);
motor_id 不会突然变成 999。
这能减少很多意外修改。
10. 但是,有时候我们就是想改外面的变量
比如:
int speed = 10;
我写一个函数:
void setSpeed(int speed)
{
speed = 100;
}
然后:
setSpeed(speed);
你可能希望外面的 speed 变成 100。
但值传递下,它不会变。
这时候就需要下一阶段的重要概念:
引用 reference
也就是:
&
不过我们这一课先把“值传递”彻底吃透。
11. 再看一遍完整程序
#include <iostream>
void change(int a)
{
a = 100;
std::cout << "Inside: " << a << std::endl;
}
int main()
{
int x = 5;
change(x);
std::cout << "Outside: " << x << std::endl;
return 0;
}
执行过程:
x = 5
↓
调用 change(x)
↓
把 5 复制给 a
↓
a = 5
↓
a 改成 100
↓
输出 Inside: 100
↓
函数结束
↓
a 消失
↓
回到 main
↓
x 还是 5
↓
输出 Outside: 5
所以:
Inside: 100
Outside: 5
12. 参数名不需要和外面一样
例如:
void printValue(int number)
{
std::cout << number << std::endl;
}
外面:
int age = 20;
printValue(age);
调用时:
age 的值 20
↓
复制给 number
所以:
number = 20
变量名字不需要一样。
13. 就算名字一样,也还是两个变量
看:
void change(int x)
{
x = 100;
}
int main()
{
int x = 5;
change(x);
std::cout << x << std::endl;
}
这里函数参数也叫:
x
外面也叫:
x
但是它们仍然不是同一个变量。
可以想象成:
main 房间里的 x = 5
change 房间里的 x = 5
名字相同,不代表同一个东西。
最终还是输出:
5
14. 参数本质上也是函数内部的局部变量
这和上一课正好接上。
void change(int a)
这里:
a
就是 change() 内部的一个局部变量。
只不过它的初始值由调用者传进来。
例如:
change(20);
相当于函数开始时:
a = 20
函数结束后,这个 a 的生命周期结束。
15. 一个容易误解的机器人例子
假设:
void stopMotor(double speed)
{
speed = 0.0;
}
然后:
int main()
{
double motor_speed = 100.0;
stopMotor(motor_speed);
std::cout << motor_speed << std::endl;
}
你可能觉得:
stopMotor()不是把速度改成 0 了吗?
但这里只改了函数里的:
speed
所以输出还是:
100
这就是为什么理解值传递非常重要。
以后你看到真实机器人代码时,如果一个函数“明明改了参数,但外面没变”,首先就要检查:
是不是值传递?
16. 值传递的核心图
把这个牢牢记住:
外部变量
x = 5
│
│ 复制值
▼
函数参数
a = 5
│
│ 修改
▼
a = 100
外面的 x 仍然 = 5
不是:
x 和 a 指向同一个盒子
这个“同一个盒子”的效果,下一课的引用才会出现。
本课小练习
第 1 题
void change(int a)
{
a = 20;
}
int main()
{
int x = 5;
change(x);
std::cout << x << std::endl;
}
最后输出什么?
第 2 题
void addTen(int number)
{
number += 10;
std::cout << number << std::endl;
}
int main()
{
int x = 3;
addTen(x);
std::cout << x << std::endl;
}
依次输出什么?
第 3 题
为什么:
void change(int a)
{
a = 100;
}
不能直接改变外面的:
int x = 5;
第 4 题
一句话解释:
什么是值传递?
更多推荐


所有评论(0)