繁体   English   中英

C链表指针理解

[英]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值的事情都不会影响headh

所以,如果我们执行这个循环

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->nextNULL ,因此循环退出。

然后我们将值分配给new->valuenew->next ,这样new->nextNULL

+------+ +-----+ +-----+      +-------+------+
| 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM