0

Yesterday I was trying to implement a linked list and although it worked and I "sort of" understand, it fried my brain a little bit.

What is wrong with function addNode() here?

#include <stdio.h>

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

struct Node *createList();
void addNode(struct Node* head, int value); // Adds Node directly after head
void viewList(struct Node *head); // Outputs list starting from head

int main()
{
    struct Node *head = createList();

    addNode(head, 10);
    addNode(head, 8);
    addNode(head, 23);
    addNode(head, 5);
    addNode(head, 4);
    addNode(head, 4100);


    viewList(head); // I didn't upload here to save space

    return 0;
}

struct Node *createList()
{ 
    struct Node *head = (struct Node *) malloc(sizeof(struct Node));
    head = NULL;
    return head;
}

void addNode(struct Node* head, int value)
{
    if(head == NULL)
    {
        struct Node *tmp = (struct Node *) malloc(sizeof(struct Node));
        tmp->value = value;
        tmp->next = head;
        head = tmp;
    }

    else
    {
        struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
        newNode->value = value;
        newNode->next = head;
        head = newNode;
    }
}

The reason I am confused is because that version of add node did not work whilst this one did...

void addNode(struct Node** head, int value)
{
    if(*head == NULL)
    {
        struct Node *tmp = (struct Node *) malloc(sizeof(struct Node));
        tmp->value = value;
        tmp->next = *head;
        *head = tmp;
    }

    else
    {
        struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
        newNode->value = value;
        newNode->next = *head;
        *head = newNode;
    }
}

and that was called in the main function using an amperand in front of the head node pointer

addNode(&head, 10);

The thing that also baffles me is this. I have written some practise functions that accepts a pointer in the parameter list and within the function, modifies what the pointer is pointing to. I never had to use this **pointer syntax.

3
  • 2
    Your creatList function leaks memory. First you allocate memory, then you discard that pointer (by assigning NULL to it) and return a pointer to NULL. Commented Mar 16, 2013 at 14:23
  • 4
    Duplicate hundreds of times over. c-faq.com/ptrs/passptrinit.html Commented Mar 16, 2013 at 14:23
  • "I never had to use this **pointer syntax". Then its about time you learned how. Read the FAQ link posted by Carl. Commented Mar 16, 2013 at 14:25

4 Answers 4

0

It has to do with that parameters are passed by value. So in the first, non-working version, the pointer to head is passed by value so the variable is a local variable inside the function. Changes to local variables are not visible outside the function when the function returns.

However, in the second version you pass the pointer by reference, so the function knows where in memory the actual pointer is and can store directly in that memory.


ASCII-diagram time:

Lets say you have the following three variables:

int value1;
int *pointer1 = &value1;
int **pointer2 = &pointer1;

The memory for the variables look something like this:

+----------+     +----------+     +--------+
| pointer2 | --> | pointer1 | --> | value1 |
+----------+     +----------+     +--------+

So pointer2 points to pointer1, and pointer1 points to value1.

Using the dereference operator * on pointer2 will get the value of what pointer2 points to, i.e. pointer1.

Sign up to request clarification or add additional context in comments.

6 Comments

I can understand how normal variables need to be passed by reference to a function otherwise a copy is made. But what does a pointer copy do and point to?
@jimbo123 If you use the address-of operator on a pointer, you get a pointer to the pointer. A pointer variable can be seen as a normal integer whose contents is the address of what it points to. So a pointer to a pointer is the address of where the pointer is in memory. If you dereference a pointer-to-pointer with the * operator then you get the actual pointer.
@jimbo123 See my updated answer, it might help a little.
Thanks Joachim, I do already know that you can get pointers that point to pointers using **. Maybe I should have asked my question in a different way. What I did not understand is what happens internally when you pass a pointer by value to a function. With a normal variable a copy is made but what happens if you create a copy of a pointer?
@jimbo123 A Pointer variable is just like any other variable, it's just that its content is an address.
|
0

Stylistic (don't repeat yourself) ::

void addNode(struct Node** head, int value)
{
    if(*head == NULL)
    {
        struct Node *tmp = (struct Node *) malloc(sizeof(struct Node));
        tmp->value = value;
        tmp->next = *head;
        *head = tmp;
    }

    else
    {
        struct Node *newNode = (struct Node *) malloc(sizeof(struct Node));
        newNode->value = value;
        newNode->next = *head;
        *head = newNode;
    }
}

You don't need the if/else ::

#include <stdlib.h>

void addNode(struct Node **head, int value)
{

        struct Node *newNode = malloc(sizeof *newNode);
        if ( !newNode) { error(); return;}
        newNode->value = value;
        newNode->next = *head; // Could be NULL, but we need a NULL anyway in that case
        *head = newNode;

}

1 Comment

Oh yes you are right duh! I wanted to get a skeleton of all the code before I start tidying it up.
0

This here will not work well

struct Node *createList()
{ 
    struct Node *head = (struct Node *) malloc(sizeof(struct Node));
    head = NULL;
    return head;
}

after you allocate a node and have head pointing to it you set head to point to NULL.

Comments

0

Here it is:

You have a bug with createList

struct Node *createList()
{
    struct Node *head = malloc(sizeof(*head));
    head->value = 0;   //< Probably you want this
    head->next = NULL; //< You definitively wanted this
    return head;
}

If you are adding nodes to the tail of the list, then addNode would look like:

void addNodeLast(struct Node* head, int value)
{
    struct Node *tailNode;
    struct Node *newNode;

    newNode = malloc(sizeof(*newNode));
    newNode->value = value;
    newNode->next = NULL;

    // Find the last node in the list
    for (tailNode = head; tailNode->next; tailNode = tailNode->next); 

    tailNode->next = head->next;
}

If you are inserting nodes after the head:

void addNodeAfterHead(struct Node* head, int value)
{
    struct Node *newNode;

    newNode = malloc(sizeof(*newNode));
    newNode->value = value;
    newNode->next = head-next;

    head->next = newNode;
}

If you are changing the head (making new head every time):

Node *addNodeNewHead(struct Node* head, int value)
{
    struct Node *newNode;

    newNode = malloc(sizeof(*newNode));
    newNode->value = value;
    newNode->next = head;

    return newNode;
}

...
Node * head = createList();
head = addNodNewHead(head, 3);
head = addNodNewHead(head, 5);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.