user3043746
user3043746

Reputation: 39

why and when is a double-pointer required?

I have been a sysadmin most of my life, but recently decided to practice some my dev knowledge and attempt a devops position. I have as such been practicing some C and Python skills and wrote some code for inserting a number into a linked list.

void list_insert(struct list *h, int d)
{
        struct list *elem = malloc(sizeof(struct list));

        elem->data = d;
        elem->next = NULL;

        if (!h) {
                h = elem;
        } else {
                elem->next = h;
                h = elem;
        }
}           

I noticed that this function doesn't seem to alter the outside view of the variable h (i.e whatever is passed to list_insert) and I noticed that printing at the end of the insert function seems to work. So having tried to look for answers online, I couldn't find anything, obvious but I found most list implementations would have double-pointers. I changed my function to use double-pointers and then it suddenly started working. Could someone help me understand what's happening here as I know pointer management is an important concept and I think I understand what a pointer is and how it relates to memory, but I don't think I get why a single pointer does not get changed, whereas a double-pointer does.

Thanks!

Upvotes: 1

Views: 2104

Answers (3)

user1814023
user1814023

Reputation:

In C, arguments to function are passed by values. Even pointers are passed by values.

For example:

#include<malloc.h>
#include<stdio.h>
int allocatingMemory(int* ptr)
{
    ptr = malloc(sizeof(int)); 
    if(ptr==NULL)
        return -1;
    else 
        return 0;
}// We are not returning the pointer to allocated memory

int main(void)
{
    int* ptr;
    int allocated = allocatingMemory(ptr);
    if(allocated == 0)
    {
        *ptr = 999;// Boom!!! 
        free(ptr);
    }

    return 0;
}

To overcome this issue, we use

int allocatingMemory(int** ptr)
{
    *ptr = malloc(sizeof(int));
    if(*ptr == NULL)
        return -1;
    else 
        return 0;
}

int main(void)
{
    int* ptr;
    int isAllocated = allocatingMemory(&ptr);
    if(isAllocated == 0)
    {
        *ptr = 999;
        free(ptr);
    }

    return 0;
}

If you are working with linked lists and say for example, you want to modify the head. You will pass a pointer to pointer (Note that, it is not called as double pointer) to head node.

Upvotes: 6

unwind
unwind

Reputation: 399949

To change memory in the caller's context, a function needs to have a pointer to that memory.

If the caller of your function has an empty list in a variable, and does an insert on that list like so:

struct list *numbers = NULL;

list_insert(numbers, 4711);

then of course inside list_insert() all we have is the NULL pointer, so we can't change the value of the variable numbers in the caller's context.

If, on the other hand, we're given a pointer to the variable, we can change the variable.

That said, it's much cleaner (in my opinion) to return the new head of the list, i.e. make the function's signature be struct list * list_insert(struct list *head, int x);.

Upvotes: 4

azmuhak
azmuhak

Reputation: 974

h is actually a copy of the original pointer, so your original pointer doesnot get modified. That is why you should use a double pointer.

There are numerous questions related to that on SO. for example Using single versus double pointers in Linked lists implemented in C

Upvotes: 1

Related Questions