现象描述

链表中常用指针进行操作:

ListNode *slow= head;
ListNode *pre = NULL;
pre = slow;
slow = slow->next;

此时 pre 指向的仍然是 slow 第一次指向的地址,即 head

指针赋值给指针

指针变量存储的是内存地址,当执行 pre = slow 时,是将 slow 当时指向的内存地址复制给了 pre。这意味着:

  1. preslow 一开始指向同一个内存地址(同一个节点)
  2. 当执行 slow = slow->next 时,只是改变了 slow 存储的地址(让它指向了下一个节点)
  3. pre 中存储的地址没有被改变,仍然是 slow 原来指向的那个节点的地址

从地址和值的角度分析

head: 地址0x100
节点1: 地址0x200
节点2: 地址0x300
节点3: 地址0x400
...
  1. 执行 pre = slow 之前
    假设此时:

    • slow 存储的地址是 0x100(指向 vhead)
    • pre 尚未赋值(或存储其他地址)
  2. 执行 pre = slow

    • 这是地址的复制:把 slow 中存储的地址(0x100)复制到 pre
    • 执行后:
      • slow 的值(地址):0x100(不变)
      • pre 的值(地址):0x100(和 slow 相同)
      • 两者指向同一个内存地址(head)
  3. 执行 slow = slow->next

    • slow->nextslow 指向节点(head)的 next 成员,假设是 0x200(节点 1 的地址)
    • 这是修改 slow 自身存储的地址:把 0x200 存入 slow
    • 执行后:
      • slow 的值(地址):0x200(现在指向节点 1)
      • pre 的值(地址):0x100(仍然指向 head,不受 slow 变化影响)

总结

  • 指针变量(pre/slow)存储的是地址
  • pre = slow地址的复制,之后两者的地址值独立
  • slow 被赋值新地址(slow = slow->next)时,只是改变了 slow 自己存储的地址,pre 中复制的旧地址不会同步变化

就像你抄了同学的家庭住址(pre = slow),后来同学搬家了(slow 变了),但你抄的旧地址(pre)不会自动变成新地址。

进一步分析

指针赋值的本质:复制门牌号

当执行 指针A = 指针B 时,本质是把指针 B 上的门牌号抄写到指针 A 上

int a = 10;    // 假设a的地址是0x100
int* p = &a;   // p上写着0x100(指向a)
int* q;        // q是一张空白标签

q = p;         // 把p上的门牌号(0x100)抄到q上

执行后:

  • pq 上的门牌号完全相同(都是 0x100)
  • 它们指向同一块内存(变量 a)
  • pq 是两张独立的标签,各自保存着门牌号

两种不同的修改操作

  1. 修改标签上的门牌号(指针本身)
int b = 20;    // 假设b的地址是0x200
p = &b;        // 把p上的门牌号改成0x200(现在p指向b)

这时 q 上的门牌号还是 0x100(仍然指向 a),因为我们只改了 p 这张标签。

  1. 修改门牌号对应的房间内容(指向的变量)
*q = 30;       // 找到q上的门牌号(0x100),把里面的内容改成30

这时通过 p 访问(如果 p 还指向 0x100 的话)也会得到 30,因为房间里的内容变了。

总结表格

总结

将指针 1 赋值给指针 2,本质是「地址的一次性复制」—— 复制完成后,指针 1 和指针 2 就变成了两个独立存储地址的变量,后续指针 1 的任何改变(无论是指向新地址,还是通过它修改指向变量的值),都不会主动影响指针 2 本身存储的地址值。