退出此函数后,为什么没有传递给指针参数的指针变量值发生变化?

时间:2017-12-20 06:48:35

标签: c pointers linked-list

根据我目前对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

2 个答案:

答案 0 :(得分:0)

为了确保没有任何误解,我要陈述一些我认为你已经知道的事情,但这只是为了澄清答案。

  1. C中的所有内容都是按值传递的。这意味着每当给函数赋一个参数时,实际给出的是该参数的副本(具有参数值的自动变量)。
  2. 为了能够使用函数修改变量x的值而不影响x 的返回值(例如x=modify_x_please(x),我们会通过x的引用。这样,该函数就会收到x地址的副本,这使其能够通过解除引用来直接修改x
  3. 例如:

    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,因此这个想法会变得更加清晰。通过这种方式,您可以认为:如果我想修改列表,我会传递此列表的地址。列表只是节点的地址这一事实是完全不同的事情。但我知道有人反对&#34;过度使用&#34; typedef也是如此,所以我认为如果你感兴趣的话,你应该考虑使用我提出的typedef的缺点。

    最后一点,如果添加和删除功能获得良好的引用并很好地修改它们,您可能甚至不需要返回。你可以让他们的返回类型为void。

答案 1 :(得分:0)

您的笔记没有错,但它们不适用您显示的链接列表实施 在这个答案的最后(讨论之后)你会发现一个稍微改变过的答案,你的笔记就适用了。

您观察到所示代码的输出以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作为linklist参数是一个错误条件,我只是通过在错误消息后直接返回来处理。 我将所有内容都放在一个文件中,作为一个方便的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;
    }
}