Search code examples
cpass-by-referencesingly-linked-listpass-by-valuefunction-definition

I don't understand fully how lists in c work


So I'm trying to add an element to the beginning of a list. The function prepend_list is at line 27. I'm trying to make it in two different ways, one returns a list back and one is a void function.

#include "base.h"
#include "string.h"

typedef struct Node Node;
struct Node{
    int value;
    Node* next;
};

Node* new_node(int value, Node* next){
    Node* new = xmalloc(sizeof(Node));
    new->value = value;
    new->next = next;
    return new;
}

void free_list(Node* list){
    Node* currentHead = list;
    while(list != NULL){
        currentHead = list;
        list = list->next;
        free(currentHead);
    }
}

Node* prepend_list(int value, Node* list){
    return new_node(value, list);
}

void test(void) {
    Node* n = NULL;
    n = new_node(1, new_node(3, NULL));
    n = prepend_list(2, n);
    for(Node* print = n;print != NULL;print = print->next){
        printi(print->value);
        if(print->next != NULL){
            prints(", ");
        }
    }
    printsln("");
    free_list(n);
}

int main(void) {
    test();
    report_memory_leaks(true);
    return 0;
}

But I don't understand why I can't do this instead

void prepend_list(int value, Node* list){
    list = new_node(value, list);
}

When I tried this, the list n in the test function didn't change at all. Instead I got memory leaks. Can somebody tell me why?


Solution

  • The function deals with a copy of the value of the expression passed to the function as an argument.

    You can imagine the function

    void prepend_list(int value, Node* list){
        list = new_node(value, list);
    }
    

    and its call

    Node* n = NULL;
    prepend_list(2, n);
    

    the following way

    prepend_list(2, n);
    //...
    void prepend_list(/* int value, Node* list */){
        int value = 2;
        Node* list = n;
        list = new_node(value, list);
    }
    

    As you can see the local variable list of the function is initialized by the value of the passed pointer n and within the function it is the local variable list that is changed. The pointer n stays unchanged. That is the function accepts its argument by value.

    There are two approaches to resolve the problem and to change the original pointer.

    The first one is return from the function a new pointer and assign it to the original pointer as it is done in your program.

    n = prepend_list(2, n);
    

    The second one is to pass the original pointer by reference indirectly through a pointer to it. And dereferencing the pointer to the original pointer the function can have an access to the original pointer and can change it. For example

    prepend_list(2, &n);
    //...
    void prepend_list(int value, Node ** list){
        *list = new_node(value, *list);
    }