上一课标准答案:
-
Hi会输出 2 次 -
输出:
5
-
有 2 个参数:
int joint
double angle
其中 joint 是 int,angle 是 double。
moveJoint(2, 30.5);
表示调用 moveJoint 函数,并把:
2 → joint
30.5 → angle
传进去。
第 11 课:函数返回值与 return
上一课我们学的函数主要是:
void moveJoint()
{
}
这种函数只负责:
做事情
但有时候我们希望函数:
算出一个结果,然后把结果交回来。
例如机械臂控制里:
目标角度 = 50°
当前角度 = 20°
误差 = ?
我们可以让一个函数专门计算误差。
1. 先看第一个有返回值的函数
double calculateError(double target, double current)
{
double error = target - current;
return error;
}
先别急,我们一点点拆。
2. 为什么这里不是 void?
上一课:
void stopRobot()
void 表示:
这个函数不需要返回结果。
现在:
double calculateError(...)
前面是:
double
意思变成:
这个函数执行完后,会返回一个
double类型的结果。
也就是:
double calculateError(...)
↑
这个函数最终要交回来一个小数
3. return 是什么?
看:
return error;
可以理解成:
把
error的值作为函数结果交回去。
例如:
double calculateError(double target, double current)
{
double error = target - current;
return error;
}
如果调用:
calculateError(50.0, 20.0);
函数内部:
target = 50.0
current = 20.0
于是:
double error = target - current;
就是:
error = 50 - 20
= 30
最后:
return error;
相当于:
把
30.0交回调用函数的位置。
4. 返回到哪里?
这是非常重要的一点。
看:
double result = calculateError(50.0, 20.0);
你可以把右边:
calculateError(50.0, 20.0)
想象成最终会变成:
30.0
所以整句话最终相当于:
double result = 30.0;
于是:
result
┌──────┐
│ 30.0 │
└──────┘
5. 用"机器"理解函数返回值
你可以把:
calculateError
想成一台计算机器。
输入:
target = 50
current = 20
经过:
50 - 20
输出:
30
所以:
50 ─────┐
├──→ calculateError ───→ 30
20 ─────┘
这就是:
输入参数 → 函数处理 → 返回结果
6. 完整程序
#include <iostream>
double calculateError(double target, double current)
{
double error = target - current;
return error;
}
int main()
{
double result = calculateError(50.0, 20.0);
std::cout << result << std::endl;
return 0;
}
最终输出:
30
7. 程序到底怎么跳来跳去?
这个要理解。
程序先进入:
main()
执行:
double result = calculateError(50.0, 20.0);
这时程序发现:
我要调用
calculateError。
于是:
main
↓
调用 calculateError
↓
target = 50
current = 20
↓
计算 error = 30
↓
return 30
↓
回到 main
↓
result = 30
然后继续:
std::cout << result << std::endl;
输出:
30
8. 其实可以直接 return
刚才写:
double calculateError(double target, double current)
{
double error = target - current;
return error;
}
也可以直接写:
double calculateError(double target, double current)
{
return target - current;
}
因为:
target - current
本身就已经能算出结果。
例如:
50 - 20 = 30
直接:
return 30;
完全可以。
9. 为什么有时候还要先创建 error?
因为复杂程序里,中间变量往往更容易看懂。
例如:
double error = target - current;
return error;
比直接写:
return target - current;
稍微长一点,但:
调试时更容易观察
error到底是多少。
初学阶段你可以多用中间变量。
10. 返回类型必须对应
比如:
double calculateError(...)
说明:
这个函数承诺返回
double。
所以:
return error;
最好也是一个能表示为 double 的值。
类似:
int getJointNumber()
{
return 3;
}
这里:
int
对应:
return 3;
11. 再看 bool 返回值
函数不一定返回数字。
例如我们想判断机械臂角度是否合法:
bool isAngleValid(double angle)
{
return angle >= -90.0 && angle <= 90.0;
}
这里返回类型:
bool
所以最后返回:
true
或者:
false
例如:
bool valid = isAngleValid(45.0);
里面判断:
45 >= -90
并且
45 <= 90
都成立。
所以:
valid = true
12. 这在机器人代码里非常常见
以后你可能看到:
bool isConnected()
意思大概率是:
检查设备是否连接,并返回
true / false。
例如:
if (isConnected())
{
std::cout << "Robot connected" << std::endl;
}
你现在已经能理解这段代码的核心:
调用 isConnected()
↓
它返回 true 或 false
↓
if 根据这个结果决定是否进入
13. 函数返回值可以直接参加运算
比如:
double getAngle()
{
return 30.0;
}
那么:
double result = getAngle() + 10.0;
相当于:
double result = 30.0 + 10.0;
所以:
result = 40.0
这点以后非常重要。
因为函数调用看起来是一串文字:
calculateIK(...)
但它最终可能代表一个计算结果。
14. return 后面的代码通常不会再执行
看:
int test()
{
return 5;
std::cout << "Hello" << std::endl;
}
执行到:
return 5;
时,函数已经结束并返回了。
所以下面的:
std::cout << "Hello" << std::endl;
不会执行。
可以理解成:
return
=
交出结果 + 离开当前函数
15. 所以 return 有两个作用
例如:
return error;
可以理解为:
- 把
error作为结果交回去 - 当前函数执行结束
这点很重要。
16. void 里能不能有 return?
可以出现:
return;
但后面没有返回值。
例如:
void checkAngle(double angle)
{
if (angle > 90.0)
{
return;
}
std::cout << "Angle OK" << std::endl;
}
如果:
angle > 90
就:
return;
直接结束函数。
但因为这是:
void
函数,所以不返回具体数据。
这个现在先认识即可。
17. 一个机械臂 PID 雏形
以后 PID 会反复使用:
误差 = 目标值 - 当前值
现在我们已经可以封装成函数:
double calculateError(double target, double current)
{
return target - current;
}
然后:
int main()
{
double target = 90.0;
double current = 65.0;
double error = calculateError(target, current);
std::cout << error << std::endl;
return 0;
}
结果:
25
你以后学 PID 时可能会看到:
double error = target - current;
double output = kp * error;
现在这些基础已经开始接上了。
18. 再进一步:函数调用函数
函数里面还可以调用其他函数。
例如:
double calculateError(double target, double current)
{
return target - current;
}
void controlJoint(double target, double current)
{
double error = calculateError(target, current);
std::cout << error << std::endl;
}
然后:
int main()
{
controlJoint(50.0, 20.0);
return 0;
}
逻辑是:
main
↓
controlJoint
↓
calculateError
↓
返回30
↓
controlJoint得到error=30
↓
输出30
↓
回到main
真实机器人程序就是这样一层一层组织起来的。
19. 现在总结函数的三个部分
例如:
double calculateError(double target, double current)
{
return target - current;
}
可以拆成:
double
↓
返回值类型
calculateError
↓
函数名字
(double target, double current)
↓
参数
return target - current;
↓
返回结果
所以一个函数可以想象成:
返回类型 函数名(输入参数)
{
处理过程
return 结果;
}
本课小练习
第 1 题
int add(int a, int b)
{
return a + b;
}
调用:
int result = add(3, 4);
result 是多少?
第 2 题
double getAngle()
{
return 45.5;
}
这里函数:
- 返回值类型是什么?
- 返回的值是多少?
第 3 题
bool isPositive(int number)
{
return number > 0;
}
调用:
bool result = isPositive(-3);
最后 result 是 true 还是 false?
第 4 题
用自己的话解释:
double error = calculateError(80.0, 50.0);
下一课我们继续函数,但会讲一个以后非常容易混淆、也非常关键的问题: