C++ 第 11 课:函数返回值与 return

上一课标准答案:

  1. Hi 会输出 2 次

  2. 输出:

    5

  3. 有 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;

可以理解为:

  1. 把 error 作为结果交回去
  2. 当前函数执行结束

这点很重要。


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);

下一课我们继续函数,但会讲一个以后非常容易混淆、也非常关键的问题:

相关推荐
不会就选b16 小时前
C++补充部分(一)--lambda 表达式
开发语言·c++
Pniubi19 小时前
HashMap的扩容和树化边界
java·开发语言
gCode Teacher 格码致知21 小时前
Pandas教学-17:Series与Dataframe的关系
开发语言·python·pandas
发量惊人的中年网工21 小时前
分公司访问总部ERP慢怎么办?SD-WAN组网的排查与验收要点
服务器·开发语言·php
下页、再停留21 小时前
【C#桌面客户端系列学习-15】生成可视化图表之折线图、柱状图、饼状图
开发语言·c#
ShineWinsu21 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
罗西的思考1 天前
【Agentic RL】Dressage Staleness 控制深度分析
人工智能·算法·机器学习
quaer1 天前
弗洛伊德算法--判圈法寻找环的入口O(n) O(1)
数据结构·算法
益莱储中国1 天前
深度解析:RF 实验室从 5G迈向 6G 的过程中保持技术领先与设备升级的路径
开发语言·php
丰色木夕1 天前
Ubuntu22.04下源码编译安装USD-Brep
c++·ubuntu·nvidia·usd-brep