1

According to my current knowledge of how C works, if I use the function List_int_add(linklist, 50) in this way without having to assign linklist = List_int_add(linklist, 50), the value of linklist would've been updated to the return value of List_int_add(linklist, 50), since in place of linklist is the argument Node_int * head, and the function returns head. Since the argument is a pointer, shouldn't List_int_add(linklist, 50) be sufficient to update linklist?

When using List_int_add(linklist, 50), the output of List_int_print(linklist) would start at 100, and not 50. But it works fine if I assign to linklist=List_int_add(linklist, 50).

(please ignore the typecasting at return, it is due to how I write the code previously, and I am in the midst of editing the code, I know it's not necessary)

Thanks in advance.

In 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;
}

In 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;
    } 
}

in 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
Yunnosch
  • 26,130
  • 9
  • 42
  • 54
Sunny Hill
  • 21
  • 4
  • Unrelated to your problem, but in the `List_int_add` function the variable `head` is of type `Node_int *`, which is the same type that the function is declared to return. That means you don't need to cast `head`, as you cast it to the type it already is. As much as possible, avoid casts. And if the compiler complains then examine the code first to see if you have made a mistake, and if you're 100% sure you haven't then you cast. – Some programmer dude Dec 20 '17 at 06:53
  • 1
    As for your problem, you pass the pointer *by value*. To *emulate* pass-by-reference you need to pass a pointer to the variable, which in your case is a pointer to the pointer, of type `Node_int **`. And use the address-of operator `&` when passing the variable. – Some programmer dude Dec 20 '17 at 06:55
  • [Don't cast the result of `malloc` in C](http://stackoverflow.com/q/605845/995714) – phuclv Dec 20 '17 at 07:13
  • 1
    If you call this function `void foo(int bar) {bar = 42;}` like this `int x = 0; foo(x);` what will be the value of `x` after the call to `foo`? – Jabberwocky Dec 20 '17 at 08:07
  • @LưuVĩnhPhúc Thanks! – Sunny Hill Dec 24 '17 at 15:55

2 Answers2

0

To make sure nothing is misunderstood I'm going to state a few things that I think you already know, but it's just for the sake of the clarity of the answer.

  1. Everything in C is pass-by-value. This means that whenever an argument is given to a function, what is really given is a copy of this argument (an automatic variable having the value of the argument).
  2. To be able to modify the value of a variable x using a function without affecting the return value to x (eg. x=modify_x_please(x)), we would pass the reference of x. This way, the function receives a copy of the address of x, which enables it to modifyxdirectly by dereferencing it.

For example :

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

which will be used this way:

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

So all of the above you already know but let's imagine one last example:

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

usage:

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

And here, it's becoming more related to your question: if you want to modify a pointer, you pass a reference to the pointer.

This means changing your functions signature to Node_int * List_int_add(Node_int ** head, int val), and using (*head) instead of head etc...

You can even have a typedef Node_int * Linked_List_int_t, so the idea becomes clearer. This way you can think: If I want to modify a list, I pass the address of this list. The fact that the list is just an address to a node is a completely different matter. But I know that there are arguments against "overusing" typedef too, so I think you should think about the downsides of using the typedef I proposed if it interests you.

One last thing, if the add and remove functions get the good references and modify them well, you probably won't even need to have a return. You could just make their return type void.

0

Your notes are not wrong, but they do not apply the linked list implementation you have shown.
At the end of this answer (after discussion) you find a slightly changed one, for which your notes do apply.

You observe that the output of the shown code starts with 100.

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

This is because of the List_int_remove(linklist,50);.
For a discussion of why the 50 is not visible, focussing on the use of pointers as arguments, note that the starts at 50 if that line is deleted.

//linklist = List_int_remove(linklist,50);  

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

Deleting all but the first linklist = still starts at 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

The first is needed because of Node_int * linklist = NULL; and the fact that the list does not use an unprinted dummy element as anchor. That method of implementing a linked list is what your notes apply to, see below.

If that first one is also removed the output is empty.

It is possible to use a linked list according to your notes, i.e. consistently only use

List_int_add(linklist, 50)

without linklist =, if you change it to using an anchor.

With the slightly different implementation below (changes labeled "// Note this change!").

Note that I kept the return values, they are actually not needed, except for Node_int_construct(...). If the return values are strictly ignored, the use of NULL as a linklist parameter is an error condition, which I handledd simply by returning directly after an error message.
I put everything into one file, as a convenient 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;
    }
}
Yunnosch
  • 26,130
  • 9
  • 42
  • 54