What happens internally when passing a pointer by value? - c

Suppose I have a following linked list structure:
struct linked_list
{
struct linked_list *next;
int data;
};
typedef struct linked_list node;
And the following function to print the linked list:
void print(node *ptr)
{
while(ptr!=NULL)
{
printf("%d ->",ptr->data);
ptr=ptr->next;
}
}
Now in the main() function when I write this:
print(head); // Assume head is the pointer pointing to the head of the list
This is essentially call-by-value. Because ptr in print will receive a copy of head. And we can't modify head from the print() function because its call-by-value.
But my doubt is, since ptr receives a copy of head but it's able to print the value of linked list. So does that means the print() function receives whole copy of linked list? If it does not receives the whole copy of linked list how its able to print the list?

Your function receives a copy of the pointer. The copy of the pointer points to the same place as the original pointer.
A pointer is like an address. Here's an analogy. Imagine writing your address down on a piece of paper. When you want to give it to a friend you copy the address: that is, you write the same address on a new piece of paper and give that paper to your friend. But if they go to the address written on their copy, they'll go to the exactly same place as if they had gone to the address on the original piece of paper.

print receives a copy of the address of head, which means that the data representing head isn't copied: the function is using the actual head and hence the actual linked list, not a copy.
The implications are that you can change head, e.g. head->data, and you can modify the rest of the linked list. The only thing that you can't do is change which node your passed-in pointer points to, i.e. you can't do head = NULL in print and expect that to be reflected outside print.
So, any of the following changes are all reflected outside the function:
// pick one:
ptr->data = 20;
ptr->next = NULL;
ptr->next = malloc(sizeof(node));
ptr->next->data = 20;
// etc.
The following aren't:
ptr = NULL;
ptr = malloc(sizeof(node));

Related

what does this struct node **p is doing?

I am learning data structure, and here is a thing that I am unable to understand...
int end(struct node** p, int data){
/*
This is another layer of indirection.
Why is the second construct necessary?
Well, if I want to modify something allocated outside of my function scope,
I need a pointer to its memory location.
*/
struct node* new = (struct node*)malloc(sizeof(struct node));
struct node* last = *p;
new->data = data;
new->next = NULL;
while(last->next !=NULL){
last = last ->next ;
}
last->next = new;
}
why we are using struct node **p?
can we use struct node *p in place of struct node **p?
the comment which I wrote here is the answer I found here, but still, I am unclear about this here is the full code...
please help me
thank you
Short answer: There is no need for a double-pointer in the posted code.
The normal reason for passing a double-pointer is that you want to be able to change the value of a variable in the callers scope.
Example:
struct node* head = NULL;
end(&head, 42);
// Here the value of head is not NULL any more
// It's value was change by the function end
// Now it points to the first (and only) element of the list
and your function should include a line like:
if (*p == NULL) {*p = new; return 0;}
However, your code doesn't !! Maybe that's really a bug in your code?
Since your code doesn't update *p there is no reason for passing a double-pointer.
BTW: Your function says it will return int but the code has no return statement. That's a bug for sure.
The shown function (according to its name) should create a new node and apend it at the end of the list represented by the pointer to a pointer to a node of that list. (I doubt however, that it actually does, agreeing with comments...)
Since the list might be empty and that pointer to node hence not be pointing to an existing node, it is ncessary to be able to potentially change the pointer to the first elemet of that list away from NULL to then point to the newly created node.
That is only possible if the parameter is not only a copy of the pointer to the first node but instead is a pointer to the pointer to the first node. Because in the second case you can dereference the pointer to pointer and actually modify the pointer to node.
Otherwise the list (if NULL) would always still point to NULL after the function call.

Understanding code for creating a singly linked list using double pointer in C

I am trying to understand how the code below for creating a singly linked list works using a double pointer.
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void push(struct Node** headRef, int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = *headRef;
*headRef = newNode;
}
//Function to implement linked list from a given set of keys using local references
struct Node* constructList(int keys[], int n) {
struct Node *head = NULL;
struct Node **lastPtrRef = &head;
int i, j;
for(i = 0; i < n; i++) {
push(lastPtrRef, keys[i]);
lastPtrRef = &((*lastPtrRef)->next); //this line
if((*lastPtrRef) == NULL) {
printf("YES\n");
}
}
return head;
}
int main() {
int keys[] = {1, 2, 3, 4};
int n = sizeof(keys)/sizeof(keys[0]);
//points to the head node of the linked list
struct Node* head = NULL;
head = constructList(keys, n); //construct the linked list
struct Node *temp = head;
while(temp != NULL) { //print the linked list
printf(" %d -> ", temp->data);
temp = temp->next;
}
}
I understand the purpose of using the double pointer in the function push(), it allows you to change what the pointer headRef is pointing to inside the function. However in the function constructList(), I don't understand how the following line works:
lastPtrRef = &((*lastPtrRef)->next);
Initially lastPtrRef would be pointing to head which points to NULL. In the first call to push(), within the for loop in constructList(), the value that head points to is changed (it points to the new node containing the value 1). So after the first call to push(), lastPtrRef will be pointing to head which points to a node with the value of 1. However, afterwards the following line is executed:
lastPtrRef = &((*lastPtrRef)->next);
Whereby lastPtrRef is given the address of whatever is pointed to by the next member of the newly added node. In this case, head->next is NULL.
I am not really sure what the purpose of changing lastPtrRef after the call to push(). If you want to build a linked list, don't you want lastPtrRef to have the address of the pointer which points to the node containing 1, since you want to push the next node (which will containing 2) onto the head of the list (which is 1)?
In the second call to push() in the for loop in constructList, we're passing in lastPtrRef which points to head->next (NULL) and the value 2. In push() the new node is created, containing the value 2, and newNode->next points to head->next which is NULL. headRef in push gets changed so that it points to newNode (which contains 2).
Maybe I'm understanding the code wrong, but it seems that by changing what lastPtrRef points to, the node containing 1 is getting disregarded. I don't see how the linked list is created if we change the address lastPtrRef holds.
I would really appreciate any insights as to how this code works. Thank you.
This uses a technique called forward-chaining, and I believe you already understand that (using a pointer-to-pointer to forward-chain a linked list construction).
This implementation is made confusing by the simple fact that the push function seems like it would be designed to stuff items on the head of a list, but in this example, it's stuffing them on the tail. So how does it do it?
The part that is important to understand is this seemingly trivial little statement in push:
newNode->next = *headRef
That may not seem important, but I assure you it is. The function push, in this case, does grave injustice to what this function really does. In reality it is more of a generic insert. Some fact about that function
It accepts a pointer-to-pointer headRef as an argument, as well as some data to put in to the linked list being managed.
After allocating a new node and saving the data within, it sets the new node's next pointer to whatever value is currently stored in the dereferenced headRef pointer-to-pointer (so.. a pointer) That's what the line I mentioned above accomplishes.
It then stores the new node's address at the same place it just pulled the prior address from; i.e. *headRef
Interestingly, it has no return value (it is void) further making this somewhat confusing. Turns out it doesn't need one.
Upon returning to the caller, at first nothing may seem to have changed. lastPtrRef still points to some pointer (in fact the same pointer as before; it must, since it was passed by value to the function). But now that pointer points to the new node just allocated. Further, that new node's next pointer points to whatever was in *lastPtrRef before the function call (i.e. whatever value was in the pointer pointed to by lastPtrRef before the function call).
That's important. That is what that line of code enforces, That means if you invoke this with lastPtrRef addressing a pointer pointing to NULL (such as head on initial loop entry), that pointer will receive the new node, and the new node's next pointer will be NULL. If you then change the address in lastPtrRef to point to the next pointer of the last-inserted node (which points to NULL; we just covered that), and repeat the process, it will hang another node there, setting that node's next pointer to NULL, etc. With each iteration, lastPtrRef addresses the last-node's next pointer, which is always NULL.
That's how push is being used to construct a forward linked list. One final thought. What would you get for a linked list if you had this:
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node* next;
};
void push(struct Node** headRef, int data)
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = *headRef;
*headRef = newNode;
}
int main()
{
//points to the head node of the linked list
struct Node* head = NULL;
push(&head, 1);
push(&head->next, 2);
push(&head->next, 3);
for (struct Node const *p = head; p; p = p->next)
printf("%p ==> %d\n", p, p->data);
}
This seemingly innocent example amplifies why I said push is more of a generic insert than anything else. This just populates the initial head node.
push(&head, 1);
Then this appends to that node by using the address of the new node's next pointer as the first argument, similar to what your constructList is doing, but without the lastPtrRef variable (we don't need it here):
push(&head->next, 2);
But then this:
push(&head->next, 3);
Hmmm. Same pointer address as the prior call, so what will it do? Hint: remember what that newNode->next = *headRef line does (I droned on about it forever; I hope something stuck).
The output of the program above is this (obviously the actual address values will be different, dependent to your instance and implementation):
0x100705950 ==> 1
0x10073da90 ==> 3
0x100740b90 ==> 2
Hope that helps.

simple linked list failing to print

I am learning how to make a linked list, but its failing to print out anything at all, and I cant figure out why??? please help. I believe it has something to do with my pointers but I don't know what it is.
#include <stdio.h>
#include <stdlib.h>
// typedef is used to give a data type a new name
typedef struct node * link ;// link is now type struct node pointer
/*
typedef allows us to say "link ptr"
instead of "struct node * ptr"
*/
struct node{
int item ;// this is the data
link next ;//same as struct node * next, next is a pointer
};
void printAll(link head); // print a linked list , starting at link head
void addFirst(link ptr, int val ); // add a node with given value to a list
link removeLast(link ptr); // removes and returns the last element in the link
//prints the link
void printAll(link head){
link ptr = head;
printf("\nPrinting Linked List:\n");
while(ptr != NULL){
printf(" %d ", (*ptr).item);
ptr = (*ptr).next;// same as ptr->next
}
printf("\n");
}
//adds to the head of the link
void addFirst(link ptr, int val ){
link tmp = malloc(sizeof(struct node));// allocates memory for the node
tmp->item = val;
tmp->next = ptr;
ptr = tmp;
}
// testing
int main(void) {
link head = NULL;// same as struct node * head, head is a pointer type
//populating list
for(int i = 0; i<3; i++){
addFirst(head, i);
}
printAll(head);
return 0;
}
output:
Printing Linked List:
Process returned 0 (0x0) execution time : 0.059 s
Press any key to continue
It's because you're passing a null pointer to your function and the condition for exiting the loop is for that pointer to be null, so nothing happens.
Your addFirst function takes a pointer's value, but it cannot modify the head that you declared inside of main().
To modify head you need to pass a pointer to link, then you can dereference that pointer to access your head and you can then change it.
void addFirst(link *ptr, int val ){
link tmp = malloc(sizeof(struct node));// allocates memory for the node
tmp->item = val;
tmp->next = *ptr;
*ptr = tmp;
}
Now you can change the head pointer. Just remember to pass the address to it when calling the function. addFirst(&head,i)
In the for loop
for(int i = 0; i<3; i++){
addFirst(head, i);
}
you create a bunch of pointers which all point to NULL. head is never changing since pointer itself is passed "by value". E.g. head is copied and all modifications to the pointer itself in addFirst are not visible outside.
This is the same as with say int. Imagine void foo(int x);. Whatever this function does to x is not visible outside.
However changes to the memory which link ptr points to are visible of course.
E.g. this line does nothing:
tmp->next = ptr;
ptr = tmp; <=== this line
}
You can fix this in several ways. One is to return new node from addFirst and another one is to make link ptr to be a pointer to pointer: link *ptr. Since in this case you want to change pointer value (not pointee value):
//link *ptr here a pointer to pointer
void addFirst(link * ptr, int val ){
link tmp = malloc(sizeof(struct node));// allocates memory for the node
tmp->item = val;
tmp->next = *ptr; //<<changed
*ptr = tmp; //<<changed
}
Do not forget to update declaration above also. And the call:
void addFirst(link * ptr, int val ); // add a node with given value to a list
...
for(int i = 0; i<3; i++){
addFirst(&head, i);
}
Then this code produces:
Printing Linked List:
2 1 0
Added:
It's important to understand that working with linked list requires working with two different types of data.
First is struct node and you pass around this type of data using links.
Second is head. This is a pointer to the very first node. When you would like to modify the head you find it is not a "node". It is something else. It's a "name" for the first node in the list. This name by itself is a pointer to node. See how memory layout for head is different from the list itself.
head[8 bytes]->node1[16 bytes]->node2[16 bytes]->...->nodek[16 bytes]->NULL;
by the way - the only thing which have lexical name here is head. All the nodes do not have name and accessible through node->next syntax.
You can also imagine another pointer here, link last which will point to nodek. Again this will have different memory layout from nodes itself. And if you would like to modify that in a function you will need to pass to function pointer to that (e.g.pointer to pointer).
Pointer and data it points to are different things. In your mind you need to separate them. Pointer is like int or float. It is passed "by value" to functions. Yes link ptr is already pointer and that permits you to update the data it points to. However the pointer itself is passed by value and updates to pointer (in your case ptr=tmp) are not visible outside.
(*ptr).next=xxx will be visible of course because data is updated (not pointer). That means you need to do one extra step - make changes to your pointer visible outside of function, e.g. convert the pointer itself (head) into data for another pointer, e.g. use struct node **ptr (first star here says this is pointer to a node, and the second star converts that pointer to data for another pointer.

How to compare a pointer value with NULL in C

I was writing a function which removes a node in a linked list, whose input is a pointer to a linked list. If the function removes a linked list that has only one node, the function will make the pointer point to NULL. Here's part of the code:
void remove(dlinkNode_t *start){
//some previous code
if(start->next==NULL){//meaning we're removing the head of the linked list
dlinkNode_t current=start; //get a temp pointer to point at this node
start=NULL; //make start point to null
free(current); //free the head
return;
}
// More code
In main I created a linked list with one node, and passed this linked list to remove function to free it. Here's the code:
int main(){
dlinkNode_t *node1=create(); //creates a node and make node1 point at it
remove(node1); //now node1 should point at NULL
if(node1==NULL)
printf("hi");
return 0;
}
But I didn't see the hi printed. I don't know why the if statement didn't pass. Any ideas?
A new copy of the pointer is made in the local scope of remove. Any changes you make to the pointer will only be visible in that scope. Any changes that you make to the value being pointed to by a pointer will return to the calling scope.
You can solve this problem in one of two ways :
Return the edited pointer
node1 = remove(node1);
and make the change in remove as well.
dlinkNode_t * remove(dlinkNode_t *start){
//some previous code
//Function code
return start;
Or you can pass a pointer to the pointer start and then manipulate that pointer.
Function call :
remove(&node1);
Function definition :
void remove(dlinkNode_t **start){
//some previous code
if((*start)->next==NULL){ // meaning we're removing
// the head of the linked list
dlinkNode_t current=**start; //get a temp pointer
// to point at this node
**start=NULL; //make start point to null
free(current); //free the head

Modifying head pointer in a linked list

I am having trouble understanding this code. All I really need is to modify the head pointer to point to the first element. So why won't *head work ? Changing the value of *head changes where this pointer points to and that should work, right ? I have read the pass by reference/pass by value, but am finding it hard to understand. Can someone help clarify this ?
Appreciate your help. Thanks.
In C/C++ it’s easier to make mistakes with pointer misuse. Consider this C/C++ code for inserting an element at the front of a list:
bool insertInFront( IntElement *head, int data ){
IntElement *newElem = new IntElement;
if( !newElem ) return false;
newElem->data = data;
head = newElem; // Incorrect!
return true;
}
The preceding code is incorrect because it only updates the local copy of the head pointer. The correct version passes in a pointer to the head pointer:
bool insertInFront( IntElement **head, int data ){
IntElement *newElem = new IntElement;
if( !newElem ) return false;
newElen->data = data;
*head = newElem; // Correctly updates head
return true;
}
You need help understanding the difference right?
Imagine the caller of the function in the first case:
IntElement *head;
int data;
...
insertInFront (head, data);
Now, in this case, the address pointed to by head is placed on the stack and passed in as an argument to insertInFront. When insertInFront does head = newElement; only the argument (on the stack) is modified.
In the second case, the caller would be:
IntElement *head;
int data;
...
insertInFront (&head, data);
In this case, the address of head is placed on the stack and passed in as an argument to insertInFront. When you do *head = newElement, this passed in address is de-referenced to get the address of the original list head, and that is modified.
Its fairly simple when you get your head around what a pointer is. In the first code IntElement *head, head is a pointer to the existing head of the linked list. So the caller is passing in the address of the head element of the list. Changing the value of head in the insert-in-front function doesn't change ANYTHING back at the caller. The value of that address was passed to your function - not what was holding that address back at the caller.
You need to pass your function 'the address of the address of the head' - or IntElement **head. This will allow this function to modify the address held by the caller - i.e. update the linked list to point to the new head.
You don't want to change the value head points to, you want to change the pointer that is stored in head itself, so don't use *head, use a pointer to head itself. Head is of type IntElement *, so the parameter should be a pointer to such a type: IntElement **
Whenever you have a value T x somewhere and you want some other function to modify it, you pass a pointer to x:
T x; // set to some value
modify_me(&x); // will change x
/* ... */
void modify_me(T * x)
{
*x = new_value;
}
Now just apply this mechanic to T = IntElement*. The values that you want to modify are themselves pointers!
(Maybe using a typedef would make things look less confusing: typedef IntElement * NodePtr;.)
Also note that your linked list is broken because you never set the "next" pointer of the new element to point to the old head, and similarly for the "previous" pointer if the list is doubly-linked.

Resources