[英]C Linked List pointer understanding
我试图了解 C 链表指针的工作原理。 我知道指向变量的指针是指向地址存储器的“链接”,而指向指针的指针有时是对指针本身的引用。
我担心的是,例如,节点引用如何修改原始列表值,而不是列表本身。
我会更好地解释自己:
void insertNode(struct node** head, int value) {
struct node* new = malloc(sizeof(struct node*));
struct node* ref = (*head); //this is a reference. -> same address.
//base case
if((*head) == NULL) {
//do things
} else { // not null
while(ref->next != null) {
ref = ref->next; //THIS: how can this not modify the head itself?
}
//null spot found, set up node
new->value = 10; //some int here
new->next = NULL;
ref->next = new; //Instead, how can this modify the head? and why?
}
}
这是一些代码片段,我的问题是:是的,我正在持有一个参考来通过ref 。 但为什么
ref = ref->next;
只修改 ref 本身,而
ref->next = new
还修改头?
通过 GDB,我看到两者在开始时共享相同的地址内存,但 ref 仅修改新插入的引用列表。
有人可以解释一下吗?
ref
只是一个指针; 修改ref
不会修改ref
指向的内容。
while
循环实际上只是在寻找列表的最后一个元素。 在while
循环之后, ref
将简单地指向列表的最后一个元素。
第一个“神秘”线:
ref = ref->next; //THIS: how can this not modify the head itself?
这里我们只读取了ref->next
,所以 head 不能修改。
第二个“神秘”线:
ref->next = new; //Instead, how can this modify the head? and why?
这里我们修改了ref
指向的内容。 在这一行, ref
要么指向列表的最后一个元素,要么指向头部(如果列表中只有一个元素,它也是列表的最后一个元素,或者是新创建的头部(待完成) in //do things
) 如果列表为空。
也许一些图片会有所帮助。
在调用insertNode
之前,您有一个链接的节点序列,如下所示:
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
您有一个指向列表第一个元素的指针(称为h
):
+---+
| h |
+---+
|
V
+-------+------+ +-------+------+ +-------+------+
| value | next | ---> | value | next | ---> | value | next | ---|||
+-------+------+ +-------+------+ +-------+------+
当你调用insertNode
,你传递一个指向h
in的指针作为参数,我们称之为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
),将其设置为指向从堆分配的新节点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.