【问题标题】:Why didn't the value of pointer variable passed to pointer argument change after exiting this function?为什么退出此函数后传递给指针参数的指针变量的值没有改变?
【发布时间】:2018-06-02 16:10:00
【问题描述】:

根据我目前对C如何工作的了解,如果我以这种方式使用函数List_int_add(linklist, 50)而不必分配linklist = List_int_add(linklist, 50),则链接列表的值将更新为List_int_add(linklist, 50)的返回值,因为代替linklist 的是参数Node_int * head,并且函数返回head。由于参数是一个指针,List_int_add(linklist, 50) 不应该足以更新linklist 吗?

使用List_int_add(linklist, 50) 时,List_int_print(linklist) 的输出将从 100 开始,而不是 50。但如果我分配给 linklist=List_int_add(linklist, 50),它会正常工作。

(请忽略返回时的类型转换,这是由于我之前编写代码的方式,而我正在编辑代码中,我知道没有必要)

提前致谢。

在 main.c 中:

#include <stdio.h>
#include <stdlib.h>
#include "LinkedList/linkedlist.h"
int main(int argc, char ** argv){
    Node_int * linklist = NULL;
    linklist = List_int_add(linklist, 50);
    linklist = List_int_add(linklist, 100);
    linklist = List_int_add(linklist, 150);
    linklist = List_int_add(linklist, 200);
    linklist = List_int_add(linklist, 250);
    linklist = List_int_add(linklist, 300);
    linklist = List_int_add(linklist, 350);

    linklist = List_int_remove(linklist,50);
    List_int_print(linklist);
    List_int_destroy(linklist);
    if (linklist == NULL)
        List_int_print(linklist);
    return EXIT_SUCCESS;
}

在linkedlist.c中:

#include <stdio.h>
#include <stdarg.h> 
#include <stdlib.h>
#include "linkedlist.h"

/* Node_int_construct: create a Node_int */ 
static Node_int * Node_int_construct(int val); 

/* Node_int_construct: create a Node_int   */ 
static Node_int * Node_int_construct(int val){
    Node_int * nd = (Node_int *) malloc(sizeof(Node_int));
    nd->next = NULL; 
    nd->prev = NULL;
    nd->value = val; 
    return nd;
}

Node_int * List_int_add(Node_int * head, int val){
    Node_int * nd = Node_int_construct(val);
    // insert at the end
    Node_int * ptr = (Node_int *) head;
    if (ptr == NULL){
        head = nd; 
        head -> next = NULL;
        head -> prev = NULL; 
        return (Node_int *) head; 
    }
    while (ptr->next != NULL){
        ptr = ptr->next; 
    }
    nd->prev = ptr; 
    ptr->next = nd;
    return (Node_int *) head;  
}

Node_int * List_int_remove(Node_int * head, int val){
    Node_int * target = List_int_search((const Node_int * const) head, val);
    if (target == NULL){
        return target; 
    }
    if (target == head){
        head = head->next; 
        head->prev = NULL; 
        free(target);
    } 
    else if (target->next == NULL){
        Node_int * ptr = target->prev; 
        ptr->next = NULL;
        free(target); 
    }
    else {
        Node_int * prev = target->prev;
        Node_int * next = target->next;
        prev->next = next; 
        next->prev = prev; 
        free(target);
    }
    return head; 
}

void List_int_destroy(Node_int * head){
    Node_int * ptr = head;
    Node_int * temp; 
    while (ptr != NULL){
        temp = ptr; 
        ptr = ptr->next;
        free(temp); 
        }
}


Node_int * List_int_search(const Node_int * const head, int val){
    Node_int * ptr = (Node_int *) head;
    while (ptr != NULL){
        if (ptr->value == val){
            return ptr; 
        }
        ptr = ptr->next;
    }
    return ptr; 
}

void List_int_print(const Node_int * const head){
    Node_int * ptr = (Node_int*) head;
    while (ptr != NULL){
        printf("> %d \n", ptr->value); 
        ptr = ptr->next;
    } 
}

在linkedlist.h中:

#ifndef LINKEDLIST_H
#define LINKEDLIST_H

typedef struct _node_int {
    struct _node_int * next;
    struct _node_int * prev;
    int value;  
} Node_int; 

/* List_int_add: add Node_int at the end of list. */ 
Node_int * List_int_add(Node_int * head, int val);

/* List_int_remove: remove first item that matches val.
    Returns head if successful, returns NULL if failure */
Node_int * List_int_remove(Node_int * head, int val); 

/* List_int_destroy: Delete entire list. */
void List_int_destroy(Node_int * head);

/* List_int_search: Returns pointer to node of the first matching item in list,
    NULL if node is not found. */ 
Node_int * List_int_search(const Node_int * const head, int val);

/* List_int_print: print int list. */
void List_int_print(const Node_int * const head);
#endif

【问题讨论】:

  • 与您的问题无关,但在List_int_add 函数中,变量head 的类型为Node_int *,与函数声明返回的类型相同。这意味着您不需要转换head,因为您将它转换为它已经是的类型。尽可能避免强制转换。如果编译器抱怨,那么首先检查代码,看看你是否犯了错误,如果你 100% 确定你没有,那么你就投了。
  • 至于你的问题,你通过指针按值。要模拟按引用传递,您需要传递一个指向变量的指针,在您的例子中是指向指针的指针,类型为Node_int **。并在传递变量时使用地址运算符&amp;
  • 如果你像这样调用void foo(int bar) {bar = 42;}这个函数int x = 0; foo(x);在调用foo之后x的值是多少?
  • @LưuVĩnhPhúc 谢谢!

标签: c pointers linked-list


【解决方案1】:

为了确保没有人误解,我将陈述一些我认为你已经知道的事情,但这只是为了让答案更清晰。

  1. C 中的所有内容都是按值传递的。这意味着,无论何时向函数提供参数,真正给出的是该参数的副本(具有参数值的自动变量)。
  2. 为了能够使用函数修改变量x 的值而不影响x 的返回值(例如x=modify_x_please(x),我们将传递x 的引用.这样,该函数会收到x 地址的副本,这使它能够通过取消引用来直接修改x

例如:

void increment_int(int *x){
   (*x)++;
}

将这样使用:

int x=3;
increment_int(&x);
/*here x is equal to 4*/

以上所有内容你都已经知道了,但让我们想象最后一个例子:

void set_pointer_to_null(void **p){
(*p)=NULL;
}

用法:

int * p;
p=&x;
set_pointer_to_null(&p);
/*p is NULL here*/

在这里,它与您的问题变得更加相关:如果您想修改指针,则传递对指针的引用

这意味着将您的函数签名更改为Node_int * List_int_add(Node_int ** head, int val),并使用(*head) 而不是head 等...

你甚至可以有一个typedef Node_int * Linked_List_int_t,这样想法就更清晰了。这样你可以想:如果我要修改一个列表,我传递这个列表的地址。列表只是节点的地址这一事实完全不同。但我知道也有人反对“过度使用” typedef,所以我认为如果你感兴趣的话,你应该考虑一下使用我提出的 typedef 的缺点。

最后一件事,如果 add 和 remove 函数获得良好的引用并很好地修改它们,您可能甚至不需要返回。你可以让他们的返回类型无效。

【讨论】:

    【解决方案2】:

    你的笔记没有错,但它们没有应用你展示的链表实现。
    在这个答案的末尾(经过讨论),您会发现一个略有变化的答案,您的注释确实适用。

    您观察到所示代码的输出以 100 开头。

    > 100
    > 150
    > 200
    > 250
    > 300
    > 350
    

    这是因为List_int_remove(linklist,50);
    有关为什么 50 不可见的讨论,重点是使用指针作为参数,请注意如果删除该行,则从 50 开始。

    //linklist = List_int_remove(linklist,50);  
    
    > 50
    > 100
    > 150
    > 200
    > 250
    > 300
    > 350
    

    删除除第一个 linklist = 之外的所有内容仍从 50 开始。

    linklist = List_int_add(linklist, 50);
    /* linklist = */ List_int_add(linklist, 100);
    /* linklist = */ List_int_add(linklist, 150);
    /* linklist = */ List_int_add(linklist, 200);
    /* linklist = */ List_int_add(linklist, 250);
    /* linklist = */ List_int_add(linklist, 300);
    /* linklist = */ List_int_add(linklist, 350);
    
    
    > 50
    > 100
    > 150
    > 200
    > 250
    > 300
    > 350
    

    第一个是需要的,因为Node_int * linklist = NULL; 以及列表不使用未打印的虚拟元素作为锚点的事实。实现链接列表的方法就是您的注释所适用的方法,见下文。

    如果第一个也被删除,则输出为空。

    可以根据您的笔记使用链表,即始终只使用

    List_int_add(linklist, 50)
    

    没有linklist =,如果您将其更改为使用锚点。

    下面的实现略有不同(标记为“// 注意此更改!”的更改)。

    请注意,我保留了返回值,实际上不需要它们,除了 Node_int_construct(...)。如果严格忽略返回值,则使用 NULL 作为链接列表参数是一种错误情况,我只需在错误消息后直接返回即可处理。
    我把所有东西都放在一个文件中,作为一个方便的 MCVE。

    #include <stdio.h>
    #include <stdlib.h>
    #ifndef LINKEDLIST_H
    #define LINKEDLIST_H
    
    typedef struct _node_int {
        struct _node_int * next;
        struct _node_int * prev;
        int value;
    } Node_int;
    
    /* List_int_add: add Node_int at the end of list. */
    Node_int * List_int_add(Node_int * head, int val);
    
    /* List_int_remove: remove first item that matches val.
        Returns head if successful, returns NULL if failure */
    Node_int * List_int_remove(Node_int * head, int val);
    
    /* List_int_destroy: Delete entire list. */
    void List_int_destroy(Node_int * head);
    
    /* List_int_search: Returns pointer to node of the first matching item in list,
        NULL if node is not found. */
    Node_int * List_int_search(const Node_int * const head, int val);
    
    /* List_int_print: print int list. */
    void List_int_print(const Node_int * const head);
    
    // Note this change!
    /* Node_int_construct: create a Node_int */
     Node_int * Node_int_construct(int val);
    #endif
    
    int main(int argc, char ** argv){
        Node_int * linklist = Node_int_construct(42); // this very first dummy gets ignored
        /* linklist = */ List_int_add(linklist, 50);
        /* linklist = */ List_int_add(linklist, 100);
        /* linklist = */ List_int_add(linklist, 150);
        /* linklist = */ List_int_add(linklist, 200);
        /* linklist = */ List_int_add(linklist, 250);
        /* linklist = */ List_int_add(linklist, 300);
        /* linklist = */ List_int_add(linklist, 350);
    
        //linklist = List_int_remove(linklist,50);
        List_int_print(linklist);
        List_int_destroy(linklist);
        if (linklist == NULL)
            List_int_print(linklist);
        return EXIT_SUCCESS;
    }
    
    
    /* Node_int_construct: create a Node_int   */
    static Node_int * Node_int_construct(int val){
        Node_int * nd = (Node_int *) malloc(sizeof(Node_int));
        nd->next = NULL;
        nd->prev = NULL;
        nd->value = val;
        return nd;
    }
    
    Node_int * List_int_add(Node_int * head, int val){
        Node_int * nd = Node_int_construct(val);
        // insert at the end
        Node_int * ptr = (Node_int *) head;
        if (ptr == NULL){
            printf("Sorry, anchor implementation requires use of head = Node_int_construct(/* dummy */ 42);\n");
            return (Node_int *) head;
        }
        while (ptr->next != NULL){
            ptr = ptr->next;
        }
        nd->prev = ptr;
        ptr->next = nd;
        return (Node_int *) head;
    }
    
    Node_int * List_int_remove(Node_int * head, int val){
        Node_int * target = List_int_search((const Node_int * const) head, val);
        if (target == NULL){
            return target;
        }
        if (target == head){
            head = head->next;
            head->prev = NULL;
            free(target);
        }
        else if (target->next == NULL){
            Node_int * ptr = target->prev;
            ptr->next = NULL;
            free(target);
        }
        else {
            Node_int * prev = target->prev;
            Node_int * next = target->next;
            prev->next = next;
            next->prev = prev;
            free(target);
        }
        return head;
    }
    
    void List_int_destroy(Node_int * head){
        Node_int * ptr = head; // No change, destroy inclusing anchor.
        Node_int * temp;
        while (ptr != NULL){
            temp = ptr;
            ptr = ptr->next;
            free(temp);
            }
    }
    
    
    Node_int * List_int_search(const Node_int * const head, int val){
        Node_int * ptr = (Node_int *) head->next; // Note this change!
        while (ptr != NULL){
            if (ptr->value == val){
                return ptr;
            }
            ptr = ptr->next;
        }
        return ptr;
    }
    
    void List_int_print(const Node_int * const head){
        Node_int * ptr = (Node_int*) head->next; // Note this change!
        while (ptr != NULL){
            printf("> %d \n", ptr->value);
            ptr = ptr->next;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-25
      • 2014-11-26
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      相关资源
      最近更新 更多