4

I have this code which creates new node at the very front:

void push(struct Node** head_ref, int new_data)
{
    struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
   
    new_node->data  = new_data;
   
    new_node->next = (*head_ref);
   
    (*head_ref)    = new_node;
}

And I have this code which creates a new node after another one:

void insertAfter(struct Node* prev_node, int new_data) 
{ 
    if (prev_node == NULL) 
    { 
       printf("the given previous node cannot be NULL");     
       return; 
    } 
          
    struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 
  
    new_node->data = new_data; 
  
    new_node->next = prev_node->next; 
  
    prev_node->next = new_node; 
}

I understand that if we pass a single pointer a.k.a. head in the first case, the changes will take effect only in the push function, rather than the whole program. That's why we use reference to head to change what it points to.

In the second case, though, we pass a single pointer. I just don't understand why when we are not using reference to the previous node, the code will still work even though we are changing next to point to the new node.

Shouldn't these changes remain only in the insertAfter function?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
zaro
  • 75
  • 5
  • You _could_ write `insertAfter` with `(struct Node** prev_node, ...`, no problem. But when you call `InsertAfter` you don't actually need to modify the `prev_node`.pointer on the caller's side. – Jabberwocky Apr 12 '21 at 13:42

3 Answers3

3

The explanation for both cases lies in the fact that pointers, as any other variable in C, are passed by value.

When you change the value of a pointer passed as parameter, within a function, you need to pass a pointer to that pointer because you will be changing the pointer itself.

If you want to change the value of the variable where it points to you can just pass a copy of the pointer. It will still work because you will be changing the value stored in the address stored in that pointer, a copy of the address will do just fine.

Example:

void func(int *ptr)
{
    static int a = 20;
    *ptr = 10; // changing the value stored in the address pointed by ptr
    ptr = &a;  // changing ptr itself, (spoiler, won't reflect in the original)
}
int main()
{
    int var;
    int *ptr = &var;

    func(ptr);
    printf("%d", *ptr);
}

You'll see that the variable where ptr is pointing to is still var, not a, and the output will be 10, because, again, you are able to change the value of the variable to which the pointer is pointing to.


Whereas if you pass a pointer to the pointer:

void func(int **ptr)
{
    static int a;
    *ptr = &a;  // changing the value of the pointer
    **ptr = 20; // changing the value of the variable stored int the address stored in ptr
}
int main()
{
    int var = 10;
    int *ptr = &var;
  
    func(&ptr);
    printf("%d", *ptr);
}

Now ptr will be pointing to a, not to var and the output will be 20, we were able to change both the pointer value and the value stored in the address stored in the pointer.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • That's the first case, yes. But what about the second one? – zaro Apr 12 '21 at 13:26
  • @zaro, that was the point I was trying to make, the second case, when you pass a pointer, you are passing a copy, when you change its value it will not reflect in the original argument, I'll edit with an example. – anastaciu Apr 12 '21 at 13:29
3

In the first function

void push(struct Node** head_ref, int new_data)
{
    struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
   
    new_node->data  = new_data;
   
    new_node->next = (*head_ref);
   
    (*head_ref)    = new_node;
}

there is changed the pointer itself to the head node. So the function needs to deal with the original pointer not with a copy of its value.

So you need to pass the pointer to th head node by reference.

In C passing by reference means passing an object indirectly through a pointer to it. In this case dereferencing the pointer like in this statement of the function

    (*head_ref)    = new_node;

the function has a direct access to the original passed object.

In the second function

void insertAfter(struct Node* prev_node, int new_data) 
{ 
    if (prev_node == NULL) 
    { 
       printf("the given previous node cannot be NULL");     
       return; 
    } 
          
    struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 
  
    new_node->data = new_data; 
  
    new_node->next = prev_node->next; 
  
    prev_node->next = new_node; 
}

the pointer prev_node itself is not being changed. It is the node pointed to by the pointer that is being changed. So there is no need to pass the original pointer to the function by reference. On the other hand the node pointed to by the pointer prev_node is passed by reference due to the pointer. And using the pointer you can change data members of the pointed node.

halfer
  • 19,824
  • 17
  • 99
  • 186
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Great explanation, thanks! One more thing - " (*head_ref) " refers to the pointer "head", so it can point to the new node? – zaro Apr 12 '21 at 13:41
  • @zaro Imagine that you declared a pointer to the head node like struct Node *head = NULL; Then passing the pointer by reference like push( &head, new_data)l the function has a direct access to the pointer head by means of dereferencing the parameter (pointer) *head_ref that now points to the original pointer head. – Vlad from Moscow Apr 12 '21 at 15:17
2

In the first case, you are modifying what is pointed at by the pointer head_ref.

In the second case, you are modifying what is pointed at by the pointer prev_node. (A->B means (*A).B)

Both function takes pointers to what should be modified and modifying what are pointed at.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70