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