云计算百科
云计算领域专业知识百科平台

C++ 第 15 课:指针 * 和地址 &

上一课标准答案:

  • 最终输出:
  • 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
    房间内容:5


    4. 怎么知道 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;

    输出:

    5


    13. 现在我们有三个不同的东西

    假设:

    int x = 5;
    int* p = &x;

    那么:

    x

    表示:

    x 里面的值

    也就是:

    5


    &x

    表示:

    x 的地址

    例如假设:

    1000


    p

    表示:

    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 = 100


    15. 图示理解

    开始:

    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。

    最后输出:

    100


    17. 指针和引用是不是很像?

    非常像。

    引用:

    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 = 100


    25. 引用参数和指针参数对比

    引用

    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 = ?

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » C++ 第 15 课:指针 * 和地址 &
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!