1

I've been doing Linked List for a while now and I've always wondered. Why do some people declare the arguments in any function using double pointers, while some do it using single pointers?

They do not make any difference to the output but they just increase the hard work to be done by the programmer as one needs to add extra '&'s and extra '*'s here and there.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • [All problems in computer science can be solved by another level of indirection](https://stackoverflow.com/q/288623/10077). – Fred Larson Dec 01 '20 at 18:31
  • 2
    i think what you are asking is this : https://stackoverflow.com/questions/7271647/what-is-the-reason-for-using-a-double-pointer-when-adding-a-node-in-a-linked-lis – Zarif Dec 01 '20 at 18:33

1 Answers1

1

Let's assume that you are going to push front a new value to a singly linked list and you declared the function parameters like

push_front( struct Node *head, int value )

In this case the function deals with a copy of the value of the pointer to the head node. Changing the copy does not influence on the original pointer used as an argument.

So you need to return to the caller the changed pointer to the head node from the function. The function declaration in this case will look like

struct Node * push_front( struct Node *head, int value );

The caller of the function shall remember to reassign the pointer to the head node by the returned pointer from the function something like

struct Node *head = NULL;
//...
head = push_front( head, value );  

But what will happen if the memory allocation for a new node will fail within the function?

In this case the function will return a null-pointer. And in this case this statement

head = push_front( head, value );  

will result in memory leaks because the pointer to the head node will be overwritten by the null pointer.

So you need write something like

struct Node *new_node = push_front( head, value );
if ( new_node != NULL ) 
{
    head = new_node;
}
else
{
    puts( "Error: no memory available." );
}

Usually users of such a function forget to check whether the returned pointer is a null pointer.

Here is how the function can be defined

struct Node * push_front( struct Node *head, int value )
{
    struct Node *new_node = malloc( sizeof( struct Node ) );
    
    if ( new_node != NULL )
    {
        new_node->value = value;
        new_node->next  = head;
    }

    return new_node;
} 

An alternative way is to pass the pointer to the head node by reference. In this case the function can return a value that will report whether a new node was added successfully.

In this case the function can be declared and defined like

int push_front( struct Node **head, int value )
{
    struct Node *new_node = malloc( sizeof( struct Node ) );
    int success = new_node != NULL;

    if ( success )
    {
        new_node->value = value;
        new_node->next  = *head;

        *head = new_node;
    }

    return success;
}

And the function can be called like

if ( !push_front( &head, value ) ) 
{
    puts( "Error: no memory available." );
}

As you can see the call of the function looks less confusing without introducing an intermediate pointer as in the case of the call of the previous function.

That is the function interface when the pointer to the head node is passed by reference is more straightforward and clear.

If to use this approach to the function declaration when the pointer to the head node is passed by reference then for example in C++ the function could look even more simpler. For example

void push_front( Node * &head, int value )
{
    head = new Node { value, head };
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335