【问题标题】:C Linked List pointer understandingC链表指针理解
【发布时间】:2018-10-22 12:05:57
【问题描述】:

我试图了解 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?
    }
}

这里有一点 sn-ps 代码,我的问题是: 是的,我通过 ref 持有对 head 的引用。 但是为什么

ref = ref->next;

只修改 ref 本身,而

ref->next = new

还修改头部?

通过 GDB,我看到一开始,两者共享相同的地址内存,但 ref 只修改了新插入的引用列表。

谁能解释一下?

【问题讨论】:

标签: c list pointers data-structures


【解决方案1】:

ref 只是一个指针;修改ref 不会修改ref指向 的内容。

while 循环实际上只是在寻找列表的最后一个元素。在 while 循环之后,ref 将简单地指向列表的最后一个元素。

第一个“神秘”行:

ref = ref->next; //THIS: how can this not modify the head itself?

这里我们只是读取ref->next,所以头部不能修改。

第二个“神秘”行:

ref->next = new; //Instead, how can this modify the head? and why?

这里我们修改ref所指向的内容。在这一行ref 要么指向列表的最后一个元素,要么指向头部(如果列表中只有一个元素,它也是列表的最后一个元素,或者是新创建的头部(到如果列表为空,请在 //do things) 中完成。

【讨论】:

    【解决方案2】:

    也许一些图片会有所帮助。

    在调用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 值的东西都不会影响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->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),将其设置为指向从堆中分配的新节点。

    【讨论】:

    • 这绝对帮助我以清晰和简单的方式理解所有指针背景到列表中。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多