也许一些图片会有所帮助。
在调用insertNode 之前,您有一系列节点,如下所示:
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
你有一个指针(称为h)指向列表的第一个元素:
+---+
| h |
+---+
|
V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
当您调用insertNode 时,您将指向h 的指针作为参数传入,我们称之为head:
+------+
| head |
+------+
|
V
+---+
| h |
+---+
|
V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
您创建一个名为ref 的指针变量,它的值是*head (h); IOW,ref 最终指向列表的第一个元素:
+------+ +-----+
| head | | ref |
+------+ +-----+
| |
V |
+---+ |
| h | |
+---+ |
| +----+
V V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
然后在堆上创建另一个节点,并将该指针分配给名为 new 的局部变量:
+------+ +-----+ +-----+ +-------+------+
| head | | ref | | new | ---> | value | next |
+------+ +-----+ +-----+ +-------+------+
| |
V |
+---+ |
| h | |
+---+ |
| +----+
V V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
所以,需要注意的是,虽然ref 和*head (h) 具有相同的值(列表中第一个节点的地址),但它们是不同的对象。因此,任何改变ref 值的东西都不会影响head 或h。
所以,如果我们执行这个循环
while(ref->next != null) {
ref = ref->next;
第一次迭代的结果是
+------+ +-----+ +-----+ +-------+------+
| head | | ref | | new | ---> | value | next |
+------+ +-----+ +-----+ +-------+------+
| |
V |
+---+ |
| h | |
+---+ |
| +------------+
V V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
经过另一次迭代,我们得到
+------+ +-----+ +-----+ +-------+------+
| head | | ref | | new | ---> | value | next |
+------+ +-----+ +-----+ +-------+------+
| |
V |
+---+ |
| h | |
+---+ |
| +----------------------------------+
V V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
此时ref->next是NULL,所以循环退出。
然后我们给new->value和new->next赋值,这样new->next就是NULL:
+------+ +-----+ +-----+ +-------+------+
| head | | ref | | new | ---> | value | next | ---|||
+------+ +-----+ +-----+ +-------+------+
| |
V |
+---+ |
| h | |
+---+ |
| +----------------------------------+
V V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
最后,我们将ref->next设置为new的值,从而将new指向的节点添加到列表的末尾:
+------+ +-----+ +-----+ +-------+------+
| head | | ref | | new | ---> | value | next | ---|||
+------+ +-----+ +-----+ +-------+------+
| | ^
V | |
+---+ | +-------------------------------+
| h | | |
+---+ | |
| +----------------------------------+ |
V V |
+-------+------+ +-------+------+ +-------+------+ |
| value | next | ---> | value | next | ---> | value | next | ---+
+-------+------+ +-------+------+ +-------+------+
注意ref->next 不是指向变量 new,它指向的是new 指向的东西。
所以,这就是为什么更新ref 不会影响head(或*head (h))。基本情况,列表为空,将最终写入*head (h),将其设置为指向从堆中分配的新节点。