上一课标准答案:
- 最终输出:
20因为:
void change(int& a)中的a是x的引用,修改a就是在修改原来的x。
x = 30 y = 30因为:
int& y = x;表示y是x的别名,它们对应同一个变量。
- 本质区别:
void test(int a)是值传递:
把原变量的值复制一份给
a,修改a不影响原变量。
而:
void test(int& a)是引用传递:
a直接引用原变量,修改a会修改原变量。
- 最后:
joint1 = 0.0因为:
setZero(joint1);把joint1通过引用传进函数,函数里的:
angle = 0.0;直接修改了joint1。
第 15 课:指针*与地址&
这节是 C++ 里第一个真正容易绕晕人的知识点。
但我们不背定义。
我们先回答一个最基础的问题:
变量到底放在哪里?
1.int x = 5;到底发生了什么?
你现在已经知道:
int x = 5;可以粗略理解成:
x ┌─────┐ │ 5 │ └─────┘但这个“盒子”实际上不是凭空存在的。
它要放在电脑的:
内存 RAM
里面。
你可以把内存想象成一排非常非常多的小房间。
内存 地址1000 地址1004 地址1008 地址1012 ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘每一块位置都有自己的:
地址
就像现实中的房子有门牌号。
2. 变量名和地址不是一回事
假设:
int x = 5;电脑可能把x放在某个内存位置。
比如为了理解,我们假设:
变量名:x 内存地址:1000 内容:5那么可以画:
x ↓ 地址 1000 ┌──────────┐ │ 5 │ └──────────┘这里:
x是你给变量起的名字。
而:
1000是它真正位于内存中的地址。
现实程序里的地址不会这么简单,可能长得像:
0x7ffd1234abcd现在先不用管。
3. 地址就像门牌号
这是理解指针最好的类比之一。
假设:
杭州某小区 3栋502室这是一个地址。
房间里面住着一个人。
对应 C++:
地址 ↓ 内存中的位置 值 ↓ 这个位置里面存放的数据所以:
int x = 5;可以想成:
门牌号:1000 房间内容:54. 怎么知道x的地址?
C++ 可以写:
&x注意:
这里的:
&和上一课的:
int& y = x;虽然符号一样,但位置不同,意义也不同。
这里:
&x表示:
取得变量 x 的地址。
5. 看一个程序
#include <iostream> int main() { int x = 5; std::cout << x << std::endl; std::cout << &x << std::endl; return 0; }第一句:
std::cout << x << std::endl;输出的是:
5第二句:
std::cout << &x << std::endl;输出的是:
x在内存中的地址。
你可能看到类似:
0x7ffdf2817a4c每次运行甚至可能不一样。
现在不需要解释为什么。
6. 两种&千万先分开
目前你遇到两种:
第一种
int& y = x;这里:
&表示:
y是一个引用。
第二种
&x这里:
&表示:
取得
x的地址。
你可以先根据位置区分:
int& y ↑ 声明引用 &x ↑ 获取地址以后会逐渐习惯。
7. 那“指针”是什么?
终于到了核心。
假设:
int x = 5;而:
&x能获得x的地址。
那么我们能不能创建一个变量,专门保存这个地址?
可以。
这就是:
指针 pointer
例如:
int* p = &x;第一次看到很怪。
我们一点点拆。
8.int* p = &x;
先看右边:
&x意思:
x的地址。
假设:
&x = 1000然后:
int* p表示:
创建一个指针变量,名字叫
p。
它用来保存:
一个
int类型变量的地址。
所以:
int* p = &x;可以翻译成:
创建一个指向
int的指针p,让它保存x的地址。
9. 画出来就非常直观了
int x = 5; int* p = &x;可以想成:
x 地址1000 ┌─────┐ │ 5 │ └─────┘ ▲ │ │ p里面存着1000 │ p ┌──────┐ │ 1000 │ └──────┘也就是说:
p不是存5。
而是存:
x在哪里。
这就是指针。
10. 一句话理解指针
指针就是一个专门保存内存地址的变量。
普通变量:
int x = 5;里面放:
5指针变量:
int* p = &x;里面放:
x 的地址11.*又是什么?
现在出现第二个核心符号:
*p如果:
p里面存的是x的地址,那么:
*p意思就是:
根据
p保存的地址,找到那个位置里面的数据。
这个操作叫:
解引用 dereference
暂时不用背英文。
你可以把:
*p理解成:
“顺着 p 找过去,看看里面是什么。”
12. 举例
int x = 5; int* p = &x;现在:
p 存着 x 的地址所以:
*p就是:
5例如:
std::cout << *p << std::endl;输出:
513. 现在我们有三个不同的东西
假设:
int x = 5; int* p = &x;那么:
x表示:
x里面的值
也就是:
5&x表示:
x的地址
例如假设:
1000p表示:
p里面保存的内容
而p保存的就是:
1000*p表示:
去
p指向的位置取值
于是得到:
5所以:
x → 5 &x → 1000 p → 1000 *p → 5这是本课最核心的关系。
14. 为什么*p能修改x?
看:
int x = 5; int* p = &x; *p = 100;这里:
*p指的是:
p指向的那个变量。
而p指向:
x所以:
*p = 100;实际上就是:
x = 100;最终:
x = 10015. 图示理解
开始:
p ┌──────┐ │ 1000 │ └──────┘ │ ▼ 地址1000 x ┌──────┐ │ 5 │ └──────┘执行:
*p = 100;意思:
顺着
p找到地址 1000,把那个位置的内容改成 100。
于是:
p ┌──────┐ │ 1000 │ └──────┘ │ ▼ x ┌──────┐ │ 100 │ └──────┘16. 完整程序
#include <iostream> int main() { int x = 5; int* p = &x; std::cout << x << std::endl; std::cout << *p << std::endl; *p = 100; std::cout << x << std::endl; return 0; }输出:
5 5 100为什么?
第一次:
std::cout << x;输出:
5第二次:
std::cout << *p;因为p指向x,所以也是:
5然后:
*p = 100;直接修改x。
最后输出:
10017. 指针和引用是不是很像?
非常像。
引用:
int x = 5; int& ref = x; ref = 100;结果:
x = 100指针:
int x = 5; int* p = &x; *p = 100;结果同样:
x = 100所以你可能问:
那为什么还要两种?
非常好的问题。
现在先不深入,先建立最简单区别。
18. 引用更像“别名”
int& ref = x;可以理解成:
x 和 ref 是同一个东西的两个名字以后正常使用:
ref = 10;不需要特殊符号。
19. 指针更像“地址纸条”
int* p = &x;p自己是一个变量。
里面保存:
x的地址。
如果想操作x:
*p需要先“顺着地址找过去”。
所以:
引用 ≈ 别名 指针 ≈ 保存地址的变量20. 用现实地址类比
假设:
x = 一台机械臂引用:
ref相当于:
给这台机械臂起了另一个名字。
指针:
p相当于你手里有一张纸:
“机械臂在 3号实验室”这个纸条不是机械臂本身。
它只是告诉你:
去哪里找到机械臂。
然后:
*p就相当于:
按纸条上的地址找到机械臂本体。
21.int* p里的*和*p又有区别
这也是初学者容易混的地方。
声明时:
int* p;这里的:
*表示:
p是一个指针变量。
使用时:
*p这里的:
*表示:
访问
p指向的那个变量。
虽然都是*,但是所在位置不同。
先根据上下文区分。
22. 现在把四个东西放在一起
int x = 5; int* p = &x;牢记:
x ↓ 变量的值 5 &x ↓ 变量的地址 p ↓ p 保存的地址 也就是 &x *p ↓ p 指向地址里的值 也就是 x 也就是 5所以可以记成:
p = &x *p = x这是非常重要的一组关系。
23. 指针怎么用于函数?
上一课我们用引用:
void change(int& a) { a = 100; }指针也能做到。
例如:
void change(int* p) { *p = 100; }调用时:
int x = 5; change(&x);注意这里不再传:
x而是传:
&x也就是:
x的地址。
24. 一步一步看
函数:
void change(int* p)表示:
change需要接收一个int的地址。
外面:
int x = 5;然后:
change(&x);把:
x 的地址传进去。
于是函数里的:
p保存:
x 的地址再执行:
*p = 100;就是:
根据地址找到
x,把它改成 100。
所以最终:
x = 10025. 引用参数和指针参数对比
引用
void change(int& a) { a = 100; } change(x);指针
void change(int* p) { *p = 100; } change(&x);两者都能修改外部的x。
但是写法明显不同。
现在先记住结构,不需要判断“哪个更好”。
后面随着工程代码学习,我们会讲使用场景。
26. 机器人里为什么会遇到指针?
非常多。
比如以后你处理:
传感器数据 图像 数组 动态内存 ROS2消息 设备驱动 串口缓存区 CAN数据经常会出现地址。
例如你以后可能见:
uint8_t* data;它大概意思就是:
data是一个指针,指向一块uint8_t数据。
或者 OpenCV、底层驱动程序里经常会涉及:
一块连续内存到底在哪里。
所以指针是 C/C++ 底层编程能力的核心。
27. 但为什么指针容易出问题?
因为指针保存的是地址。
如果地址错误:
找错房间你就可能访问不属于自己的内存。
这会导致:
程序崩溃 数据被破坏 Segmentation fault所以以后我们会专门讲:
nullptr以及:
空指针、野指针、悬空指针
但今天不要一下塞太多。
28. 今天只掌握这一张图
int x = 5; int* p = &x;对应:
p ┌────────┐ │ 地址 │ └───┬────┘ │ ▼ x ┌────────┐ │ 5 │ └────────┘并且:
x → 5 &x → x的地址 p → x的地址 *p → 5如果把这个真正理解,指针就迈过第一道坎了。
本课小练习
第 1 题
int x = 10; int* p = &x;那么:
*p = ?第 2 题
int x = 5; int* p = &x; *p = 20;最后:
x = ?第 3 题
用自己的话分别解释:
&x和:
*p第 4 题
下面代码:
void change(int* p) { *p = 50; } int main() { int x = 3; change(&x); }最后:
x = ?