Related
The two code examples below both add a node at the top of a linked list.
But whereas the first code example uses a double pointer the second code example uses a single pointer
code example 1:
struct node* push(struct node **head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data = data;
newnode->next = *head;
return newnode;
}
push(&head,1);
code example 2:
struct node* push(struct node *head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data = data;
newnode->next = head;
return newnode;
}
push(head,1)
Both strategies work. However, a lot of programs that use a linked list use a double pointer to add a new node. I know what a double pointer is. But if a single pointer would be sufficient to add a new node why do a lot of implementations rely on double pointers?
Is there any case in which a single pointer does not work so we need to go for a double pointer?
Some implementations pass a pointer to pointer parameter to allow changing the head pointer directly instead of returning the new one. Thus you could write:
// note that there's no return value: it's not needed
void push(struct node** head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=*head;
*head = newnode; // *head stores the newnode in the head
}
// and call like this:
push(&head,1);
The implementation that doesn't take a pointer to the head pointer must return the new head, and the caller is responsible for updating it itself:
struct node* push(struct node* head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=head;
return newnode;
}
// note the assignment of the result to the head pointer
head = push(head,1);
If you don't do this assignment when calling this function, you will be leaking the nodes you allocate with malloc, and the head pointer will always point to the same node.
The advantage should be clear now: with the second, if the caller forgets to assign the returned node to the head pointer, bad things will happen.
Edit:
Pointer to pointer(Double pointers) also allows for creation for multiple user defined data types within a same program(Example: Creating 2 linked lists)
To avoid complexity of double pointers we can always utilize structure(which works as an internal pointer).
You can define a list in the following way:
typedef struct list {
struct node* root;
} List;
List* create() {
List* templ = malloc(sizeof(List));
templ->root = NULL;
return templ;
}
In link list functions use the above List in following way: (Example for Push function)
void Push(List* l, int x) {
struct node* n = malloc(sizeof(struct node));
n->data = x;
n->link = NULL;
printf("Node created with value %d\n", n->data);
if (l->root == NULL) {
l->root = n;
} else {
struct node* i = l->root;
while (i->link != NULL){
i = i->link;
}
i->link = n;
}
}
In your main() function declare the list in follow way:
List* list1 = create();
push(list1, 10);
Although the previous answers are good enough, I think it's much easier to think in terms of "copy by value".
When you pass in a pointer to a function, the address value is being copied over to the function parameter. Due to the function's scope, that copy will vanish once it returns.
By using a double pointer, you will be able to update the original pointer's value. The double pointer will still be copied by value, but that doesn't matter. All you really care is modifying the original pointer, thereby bypassing the function's scope or stack.
Hope this answers not just your question, but other pointer related questions as well.
As #R. Martinho Fernandes pointed out in his answer, using pointer to pointer as an argument in void push(struct node** head, int data) allows you to change the head pointer directly from within push function instead of returning the new pointer.
There is yet another good example which shows why using pointer to pointer instead a single pointer may shorten, simplify and speed up your code. You asked about adding a new node to the list which probably typically doesn't need pointer-to-pointer in contrast to removing the node from the singly-linked list. You can implement removing node from the list without pointer-to-pointer but it is suboptimal. I described the details here. I recommend you also to watch this YouTube video which addresses the problem.
BTW: If you count with Linus Torvalds opinion, you would better learn how to use pointer-to-pointer. ;-)
Linus Torvalds: (...) At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I've seen too many people who delete a singly-linked list entry by keeping track of the "prev" entry, and then to delete the entry, doing something like
if (prev)
prev->next = entry->next;
else
list_head = entry->next;
and whenever I see code like that, I just go "This person doesn't understand pointers". And it's sadly quite common.
People who understand pointers just use a "pointer to the entry pointer", and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a "*pp = entry->next". (...)
Other resources that may be helpful:
C double pointers
Pointers to Pointers
Why use double pointer? or Why use pointers to pointers?
In your particular example there is no need for the double pointer. However it can be needed, if, for example, you were to do something like this:
struct node* push(struct node** head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=*head;
//vvvvvvvvvvvvvvvv
*head = newnode; //you say that now the new node is the head.
//^^^^^^^^^^^^^^^^
return newnode;
}
Observation and Finding, WHY...
I decided to do some experiments and make some conclusion,
OBSERVATION 1- If the linked list is not empty then we can add the nodes in it (obviously at the end) by using a single pointer only.
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p = root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
return 0;
}
int main(){
int m;
struct LinkedList *A=(struct LinkedList*)malloc(sizeof(struct LinkedList));
//now we want to add one element to the list so that the list becomes non-empty
A->data=5;
A->next=NULL;
cout<<"enter the element to be inserted\n"; cin>>m;
insert(A,m);
return 0;
}
Its simple to explain (Basic). We have a pointer in our main function which points to the first node (root) of the list. In the insert() function we pass the address of the root node and using this address we reach the end of the list and add a node to it. So we can conclude that if we have address of a variable in a function (not the main function) we can make permanent changes in the value of that variable from that function which would reflect in the main function.
OBSERVATION 2- The above method of adding node failed when the list was empty.
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p=root;
if(p==NULL){
p=temp;
}
else{
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int m;
struct LinkedList *A=NULL; //initialise the list to be empty
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
If you keep on adding elements and finally display the list then you would find that the list has undergone no changes and still it is empty.
The question which struck my mind was in this case also we are passing the address of the root node then why modifications are not happening as permanent modifications and list in the main function undergoes no changes. WHY? WHY? WHY?
Then I observed one thing, when I write A=NULL the address of A becomes 0. This means now A is not pointing to any location in memory. So I removed the line A=NULL; and made some modification in the insert function.
some modifications,(below insert() function can add only one element to an empty list, just wrote this function for testing purpose)
int insert(struct LinkedList *root, int item){
root= (struct LinkedList *)malloc(sizeof(struct LinkedList));
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A;
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
the above method also fails because in the insert() function root stores same address as A in the main() function but after the line root= (struct LinkedList *)malloc(sizeof(struct LinkedList)); the address stored in root changes. Thus now , root (in insert() function) and A (in main() function) store different addresses.
So the correct final program would be,
int insert(struct LinkedList *root, int item){
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A = (struct LinkedList *)malloc(sizeof(struct LinkedList));
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
But we dont want two different functions for insertion, one when list is empty and other when list is not empty. Now comes double pointer which makes things easy.
One thing I noticed which is important is that pointers store address
and when used with '*' they give value at that address but pointers
themselves have their own address.
Now here is the complete program and later explain the concepts.
int insert(struct LinkedList **root,int item){
if(*root==NULL){
(*root)=(struct LinkedList *)malloc(sizeof(struct LinkedList));
(*root)->data=item;
(*root)->next=NULL;
}
else{
struct LinkedList *temp=(struct LinkedList *)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p;
p=*root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int n,m;
struct LinkedList *A=NULL;
cout<<"enter the no of elements to be inserted\n";
cin>>n;
while(n--){
cin>>m;
insert(&A,m);
}
display(A);
return 0;
}
following are the observations,
1. root stores the address of pointer A (&A) , *root stores the address stored by pointer A and **root stores the value at address stored by A. In simple language root=&A, *root= A and **root= *A.
2. if we write *root= 1528 then it means that value at address stored in root becomes 1528 and since address stored in root is the address of pointer A (&A) thus now A=1528 (i.e. address stored in A is 1528) and this change is permanent.
whenever we are changing value of *root we are indeed changing value at address stored in root and since root=&A ( address of pointer A) we are indirectly changing value of A or address stored in A.
so now if A=NULL (list is empty) *root=NULL , thus we create the first node and store its address at *root i.e. indirectly we storing the address of first node at A. If list is not empty , everything is same as done in previous functions using single pointer except we have changed root to *root since what was stored in root is now stored in *root.
Let's take this simple eg:
void my_func(int *p) {
// allocate space for an int
int *z = (int *) malloc(sizeof(int));
// assign a value
*z = 99;
printf("my_func - value of z: %d\n", *z);
printf("my_func - value of p: %p\n", p);
// change the value of the pointer p. Now it is not pointing to h anymore
p = z;
printf("my_func - make p point to z\n");
printf("my_func - addr of z %p\n", &*z);
printf("my_func - value of p %p\n", p);
printf("my_func - value of what p points to: %d\n", *p);
free(z);
}
int main(int argc, char *argv[])
{
// our var
int z = 10;
int *h = &z;
// print value of z
printf("main - value of z: %d\n", z);
// print address of val
printf("main - addr of z: %p\n", &z);
// print value of h.
printf("main - value of h: %p\n", h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// change the value of var z by dereferencing h
*h = 22;
// print value of val
printf("main - value of z: %d\n", z);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
my_func(h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// print value of h
printf("main - value of h: %p\n", h);
return 0;
}
Output:
main - value of z: 10
main - addr of z: 0x7ffccf75ca64
main - value of h: 0x7ffccf75ca64
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffccf75ca64
my_func - make p point to z
my_func - addr of z 0x1906420
my_func - value of p 0x1906420
my_func - value of what p points to: 99
main - value of what h points to: 22
main - value of h: 0x7ffccf75ca64
we have this signature for my_func:
void my_func(int *p);
If you look at the output, in th end, the value that h points to is still 22 and the value of h is the same, altough in my_func it was changed. How come ?
Well, in my_func we are manipulating the value of p, which is just a local pointer.
after calling:
my_func(ht);
in main(), p will hold the value that h holds, which represents the address of z variable, declared in main function.
In my_func(), when we are changing the value of p to hold the value of z, which is a pointer to a location in memory, for which we have allocated space, we are not changing the value of h, that we've passed in, but just the value of local pointer p. Basically, p does not hold the value of h anymore, it will hold the address of a memory location, that z points to.
Now, if we change our example a little bit:
#include <stdio.h>
#include <stdlib.h>
void my_func(int **p) {
// allocate space for an int
int *z = (int *) malloc(sizeof(int));
// assign a value
*z = 99;
printf("my_func - value of z: %d\n", *z);
printf("my_func - value of p: %p\n", p);
printf("my_func - value of h: %p\n", *p);
// change the value of the pointer p. Now it is not pointing to h anymore
*p = z;
printf("my_func - make p point to z\n");
printf("my_func - addr of z %p\n", &*z);
printf("my_func - value of p %p\n", p);
printf("my_func - value of h %p\n", *p);
printf("my_func - value of what p points to: %d\n", **p);
// we are not deallocating, because we want to keep the value in that
// memory location, in order for h to access it.
/* free(z); */
}
int main(int argc, char *argv[])
{
// our var
int z = 10;
int *h = &z;
// print value of z
printf("main - value of z: %d\n", z);
// print address of val
printf("main - addr of z: %p\n", &z);
// print value of h.
printf("main - value of h: %p\n", h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// change the value of var z by dereferencing h
*h = 22;
// print value of val
printf("main - value of z: %d\n", z);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
my_func(&h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// print value of h
printf("main - value of h: %p\n", h);
free(h);
return 0;
}
we have the follwoing output:
main - value of z: 10
main - addr of z: 0x7ffcb94fb1cc
main - value of h: 0x7ffcb94fb1cc
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffcb94fb1c0
my_func - value of h: 0x7ffcb94fb1cc
my_func - make p point to z
my_func - addr of z 0xc3b420
my_func - value of p 0x7ffcb94fb1c0
my_func - value of h 0xc3b420
my_func - value of what p points to: 99
main - value of what h points to: 99
main - value of h: 0xc3b420
Now, we actually have changed the value which h holds, from my_func, by doing this:
changed function signature
calling from main(): my_func(&h); Basically we are passing the address of h pointer to double pointer p, declared as a parameter in function's signature.
in my_func() we are doing: *p = z; we are dereferencing the double pointer p, one level. Basically this got translated as you would do: h = z;
The value of p, now holds the address of h pointer. h pointer holds the address of z.
You can take both examples and diff them.
So, getting back to your question, you need double pointer in order to make modifications to the pointer that you've passed in straight from that function.
Think of memory location for head like [HEAD_DATA].
Now in your second scenario, the calling function's main_head is the pointer to this location.
main_head--->[HEAD_DATA]
In your code, it sent the value of the pointer main_head to the function(i.e the address of the memory location of head_data)
You copied that to local_head in the function.
so now
local_head---> [HEAD_DATA]
and
main_head---> [HEAD_DATA]
Both point to the same location but are essentially independent of each other.
So when you write local_head = newnode;
what you did is
local_head--/-->[HEAD_DATA]
local_head-----> [NEWNODE_DATA]
You simply replaced the memory address of previous memory with new one in local pointer.
The main_head (pointer) still points to the old [HEAD_DATA]
The standard way to handle linked lists in C is to have the push and pop functions automatically update the head pointer.
C is "Call by value" meaning copies of parameters are passed into functions. If you only pass in the head pointer any local update you make to that pointer will not be seen by the caller. The two workarounds are
1) Pass the address of the head pointer. (Pointer to head pointer)
2) Return a new head pointer, and rely on the caller to update the head pointer.
Option 1) is the easiest even though a little confusing at first.
The answer is more obvious if you take the time to write a working node insertion function; yours isn't one.
You need to be able to write over the head to move it forward, so you need a pointer to the pointer to the head so you can dereference it to get the pointer to the head and change it.
Imagine a case where you have to make certain changes and those changes should reflect back in the calling function.
Example:
void swap(int* a,int* b){
int tmp=*a;
*a=*b;
*b=tmp;
}
int main(void){
int a=10,b=20;
// To ascertain that changes made in swap reflect back here we pass the memory address
// instead of the copy of the values
swap(&a,&b);
}
Similarly we pass the Memory Address of the Head of the List.
This way, if any node is added and the Value of Head is Changed, then that change Reflects Back and we don't have to manually reset the Head inside of the calling function.
Thus this approach reduces the chances of Memory Leaks as we would have lost the pointer to the newly allocated node, had we forgot to update the Head back in the calling function.
Beside this, the second code will Work Faster since no time is wasted in copying and returning since we work directly with the memory.
When we pass pointer as a parameter in a function and want update in the same pointer we use double pointer.
On the other hand if we pass pointer as a parameter in a function and catch it in single pointer then will have to return the result to calling function back in order to use the result.
I think the point is that it makes it easier to update nodes within a linked list. Where you would normally have to keep track of a pointer for previous and current you can have a double pointer take care of it all.
#include <iostream>
#include <math.h>
using namespace std;
class LL
{
private:
struct node
{
int value;
node* next;
node(int v_) :value(v_), next(nullptr) {};
};
node* head;
public:
LL()
{
head = nullptr;
}
void print()
{
node* temp = head;
while (temp)
{
cout << temp->value << " ";
temp = temp->next;
}
}
void insert_sorted_order(int v_)
{
if (!head)
head = new node(v_);
else
{
node* insert = new node(v_);
node** temp = &head;
while ((*temp) && insert->value > (*temp)->value)
temp = &(*temp)->next;
insert->next = (*temp);
(*temp) = insert;
}
}
void remove(int v_)
{
node** temp = &head;
while ((*temp)->value != v_)
temp = &(*temp)->next;
node* d = (*temp);
(*temp) = (*temp)->next;
delete d;
}
void insertRear(int v_)//single pointer
{
if (!head)
head = new node(v_);
else
{
node* temp = new node(v_);
temp->next = head;
head = temp;
}
}
};
Lets say I noted down your house address on a card-1. Now if I want tell your house address to somebody else, I can either copy the address from card-1 to card-2 and give card-2 OR I can give card-1 directly. Either ways the person will know the address and can reach you. But when I give card-1 directly, the address can be changed on card-1 but if I gave card-2 only the address on card-2 can be changed but not on card-1.
Passing a pointer to pointer is similar to giving the access to card-1 directly. Passing a pointer is similar to creating a new copy of the address.
I think your confusion might come from the fact that both functions have a parameter named head. The two head are actually different things. head in the first code stores the address of of the head node pointer(which itself stores an address of the head node structure). Whereas the second head stores an address of the head node structure directly. And since both function returns the newly created node(which should be the new head), I think there is no need to go for the first approach. Callers of this function is responsible to update the head reference they have. I think the second one is good enough and simple to look at. I'd go with the second one.
The naming convention- Head is the cause of the confusion.
The Head is the Tail and the Tail is the Head. The Tail wags the Head.
The Head is just a Pointer,Data is Null - and the Tail is just Data, Pointer is Null.
So you have a pointer to a struct pointer. the Struct pointer points to the 1st node struct in the Linked list.
This pointer to the 1st struct node pointer is called Head. It can better be called startptr or headptr.
When you catch hold of the startptr you have caught hold of the linkedlist. then you can traverse all the struct nodes.
The two code examples below both add a node at the top of a linked list.
But whereas the first code example uses a double pointer the second code example uses a single pointer
code example 1:
struct node* push(struct node **head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data = data;
newnode->next = *head;
return newnode;
}
push(&head,1);
code example 2:
struct node* push(struct node *head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data = data;
newnode->next = head;
return newnode;
}
push(head,1)
Both strategies work. However, a lot of programs that use a linked list use a double pointer to add a new node. I know what a double pointer is. But if a single pointer would be sufficient to add a new node why do a lot of implementations rely on double pointers?
Is there any case in which a single pointer does not work so we need to go for a double pointer?
Some implementations pass a pointer to pointer parameter to allow changing the head pointer directly instead of returning the new one. Thus you could write:
// note that there's no return value: it's not needed
void push(struct node** head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=*head;
*head = newnode; // *head stores the newnode in the head
}
// and call like this:
push(&head,1);
The implementation that doesn't take a pointer to the head pointer must return the new head, and the caller is responsible for updating it itself:
struct node* push(struct node* head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=head;
return newnode;
}
// note the assignment of the result to the head pointer
head = push(head,1);
If you don't do this assignment when calling this function, you will be leaking the nodes you allocate with malloc, and the head pointer will always point to the same node.
The advantage should be clear now: with the second, if the caller forgets to assign the returned node to the head pointer, bad things will happen.
Edit:
Pointer to pointer(Double pointers) also allows for creation for multiple user defined data types within a same program(Example: Creating 2 linked lists)
To avoid complexity of double pointers we can always utilize structure(which works as an internal pointer).
You can define a list in the following way:
typedef struct list {
struct node* root;
} List;
List* create() {
List* templ = malloc(sizeof(List));
templ->root = NULL;
return templ;
}
In link list functions use the above List in following way: (Example for Push function)
void Push(List* l, int x) {
struct node* n = malloc(sizeof(struct node));
n->data = x;
n->link = NULL;
printf("Node created with value %d\n", n->data);
if (l->root == NULL) {
l->root = n;
} else {
struct node* i = l->root;
while (i->link != NULL){
i = i->link;
}
i->link = n;
}
}
In your main() function declare the list in follow way:
List* list1 = create();
push(list1, 10);
Although the previous answers are good enough, I think it's much easier to think in terms of "copy by value".
When you pass in a pointer to a function, the address value is being copied over to the function parameter. Due to the function's scope, that copy will vanish once it returns.
By using a double pointer, you will be able to update the original pointer's value. The double pointer will still be copied by value, but that doesn't matter. All you really care is modifying the original pointer, thereby bypassing the function's scope or stack.
Hope this answers not just your question, but other pointer related questions as well.
As #R. Martinho Fernandes pointed out in his answer, using pointer to pointer as an argument in void push(struct node** head, int data) allows you to change the head pointer directly from within push function instead of returning the new pointer.
There is yet another good example which shows why using pointer to pointer instead a single pointer may shorten, simplify and speed up your code. You asked about adding a new node to the list which probably typically doesn't need pointer-to-pointer in contrast to removing the node from the singly-linked list. You can implement removing node from the list without pointer-to-pointer but it is suboptimal. I described the details here. I recommend you also to watch this YouTube video which addresses the problem.
BTW: If you count with Linus Torvalds opinion, you would better learn how to use pointer-to-pointer. ;-)
Linus Torvalds: (...) At the opposite end of the spectrum, I actually wish more people understood the really core low-level kind of coding. Not big, complex stuff like the lockless name lookup, but simply good use of pointers-to-pointers etc. For example, I've seen too many people who delete a singly-linked list entry by keeping track of the "prev" entry, and then to delete the entry, doing something like
if (prev)
prev->next = entry->next;
else
list_head = entry->next;
and whenever I see code like that, I just go "This person doesn't understand pointers". And it's sadly quite common.
People who understand pointers just use a "pointer to the entry pointer", and initialize that with the address of the list_head. And then as they traverse the list, they can remove the entry without using any conditionals, by just doing a "*pp = entry->next". (...)
Other resources that may be helpful:
C double pointers
Pointers to Pointers
Why use double pointer? or Why use pointers to pointers?
In your particular example there is no need for the double pointer. However it can be needed, if, for example, you were to do something like this:
struct node* push(struct node** head, int data)
{
struct node* newnode = malloc(sizeof(struct node));
newnode->data=data;
newnode->next=*head;
//vvvvvvvvvvvvvvvv
*head = newnode; //you say that now the new node is the head.
//^^^^^^^^^^^^^^^^
return newnode;
}
Observation and Finding, WHY...
I decided to do some experiments and make some conclusion,
OBSERVATION 1- If the linked list is not empty then we can add the nodes in it (obviously at the end) by using a single pointer only.
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p = root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
return 0;
}
int main(){
int m;
struct LinkedList *A=(struct LinkedList*)malloc(sizeof(struct LinkedList));
//now we want to add one element to the list so that the list becomes non-empty
A->data=5;
A->next=NULL;
cout<<"enter the element to be inserted\n"; cin>>m;
insert(A,m);
return 0;
}
Its simple to explain (Basic). We have a pointer in our main function which points to the first node (root) of the list. In the insert() function we pass the address of the root node and using this address we reach the end of the list and add a node to it. So we can conclude that if we have address of a variable in a function (not the main function) we can make permanent changes in the value of that variable from that function which would reflect in the main function.
OBSERVATION 2- The above method of adding node failed when the list was empty.
int insert(struct LinkedList *root, int item){
struct LinkedList *temp = (struct LinkedList*)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p=root;
if(p==NULL){
p=temp;
}
else{
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int m;
struct LinkedList *A=NULL; //initialise the list to be empty
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
If you keep on adding elements and finally display the list then you would find that the list has undergone no changes and still it is empty.
The question which struck my mind was in this case also we are passing the address of the root node then why modifications are not happening as permanent modifications and list in the main function undergoes no changes. WHY? WHY? WHY?
Then I observed one thing, when I write A=NULL the address of A becomes 0. This means now A is not pointing to any location in memory. So I removed the line A=NULL; and made some modification in the insert function.
some modifications,(below insert() function can add only one element to an empty list, just wrote this function for testing purpose)
int insert(struct LinkedList *root, int item){
root= (struct LinkedList *)malloc(sizeof(struct LinkedList));
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A;
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
the above method also fails because in the insert() function root stores same address as A in the main() function but after the line root= (struct LinkedList *)malloc(sizeof(struct LinkedList)); the address stored in root changes. Thus now , root (in insert() function) and A (in main() function) store different addresses.
So the correct final program would be,
int insert(struct LinkedList *root, int item){
root->data=item;
root->next=NULL;
return 0;
}
int main(){
int m;
struct LinkedList *A = (struct LinkedList *)malloc(sizeof(struct LinkedList));
cout<<"enter the element to be inserted\n";
cin>>m;
insert(A,m);
return 0;
}
But we dont want two different functions for insertion, one when list is empty and other when list is not empty. Now comes double pointer which makes things easy.
One thing I noticed which is important is that pointers store address
and when used with '*' they give value at that address but pointers
themselves have their own address.
Now here is the complete program and later explain the concepts.
int insert(struct LinkedList **root,int item){
if(*root==NULL){
(*root)=(struct LinkedList *)malloc(sizeof(struct LinkedList));
(*root)->data=item;
(*root)->next=NULL;
}
else{
struct LinkedList *temp=(struct LinkedList *)malloc(sizeof(struct LinkedList));
temp->data=item;
temp->next=NULL;
struct LinkedList *p;
p=*root;
while(p->next!=NULL){
p=p->next;
}
p->next=temp;
}
return 0;
}
int main(){
int n,m;
struct LinkedList *A=NULL;
cout<<"enter the no of elements to be inserted\n";
cin>>n;
while(n--){
cin>>m;
insert(&A,m);
}
display(A);
return 0;
}
following are the observations,
1. root stores the address of pointer A (&A) , *root stores the address stored by pointer A and **root stores the value at address stored by A. In simple language root=&A, *root= A and **root= *A.
2. if we write *root= 1528 then it means that value at address stored in root becomes 1528 and since address stored in root is the address of pointer A (&A) thus now A=1528 (i.e. address stored in A is 1528) and this change is permanent.
whenever we are changing value of *root we are indeed changing value at address stored in root and since root=&A ( address of pointer A) we are indirectly changing value of A or address stored in A.
so now if A=NULL (list is empty) *root=NULL , thus we create the first node and store its address at *root i.e. indirectly we storing the address of first node at A. If list is not empty , everything is same as done in previous functions using single pointer except we have changed root to *root since what was stored in root is now stored in *root.
Let's take this simple eg:
void my_func(int *p) {
// allocate space for an int
int *z = (int *) malloc(sizeof(int));
// assign a value
*z = 99;
printf("my_func - value of z: %d\n", *z);
printf("my_func - value of p: %p\n", p);
// change the value of the pointer p. Now it is not pointing to h anymore
p = z;
printf("my_func - make p point to z\n");
printf("my_func - addr of z %p\n", &*z);
printf("my_func - value of p %p\n", p);
printf("my_func - value of what p points to: %d\n", *p);
free(z);
}
int main(int argc, char *argv[])
{
// our var
int z = 10;
int *h = &z;
// print value of z
printf("main - value of z: %d\n", z);
// print address of val
printf("main - addr of z: %p\n", &z);
// print value of h.
printf("main - value of h: %p\n", h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// change the value of var z by dereferencing h
*h = 22;
// print value of val
printf("main - value of z: %d\n", z);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
my_func(h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// print value of h
printf("main - value of h: %p\n", h);
return 0;
}
Output:
main - value of z: 10
main - addr of z: 0x7ffccf75ca64
main - value of h: 0x7ffccf75ca64
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffccf75ca64
my_func - make p point to z
my_func - addr of z 0x1906420
my_func - value of p 0x1906420
my_func - value of what p points to: 99
main - value of what h points to: 22
main - value of h: 0x7ffccf75ca64
we have this signature for my_func:
void my_func(int *p);
If you look at the output, in th end, the value that h points to is still 22 and the value of h is the same, altough in my_func it was changed. How come ?
Well, in my_func we are manipulating the value of p, which is just a local pointer.
after calling:
my_func(ht);
in main(), p will hold the value that h holds, which represents the address of z variable, declared in main function.
In my_func(), when we are changing the value of p to hold the value of z, which is a pointer to a location in memory, for which we have allocated space, we are not changing the value of h, that we've passed in, but just the value of local pointer p. Basically, p does not hold the value of h anymore, it will hold the address of a memory location, that z points to.
Now, if we change our example a little bit:
#include <stdio.h>
#include <stdlib.h>
void my_func(int **p) {
// allocate space for an int
int *z = (int *) malloc(sizeof(int));
// assign a value
*z = 99;
printf("my_func - value of z: %d\n", *z);
printf("my_func - value of p: %p\n", p);
printf("my_func - value of h: %p\n", *p);
// change the value of the pointer p. Now it is not pointing to h anymore
*p = z;
printf("my_func - make p point to z\n");
printf("my_func - addr of z %p\n", &*z);
printf("my_func - value of p %p\n", p);
printf("my_func - value of h %p\n", *p);
printf("my_func - value of what p points to: %d\n", **p);
// we are not deallocating, because we want to keep the value in that
// memory location, in order for h to access it.
/* free(z); */
}
int main(int argc, char *argv[])
{
// our var
int z = 10;
int *h = &z;
// print value of z
printf("main - value of z: %d\n", z);
// print address of val
printf("main - addr of z: %p\n", &z);
// print value of h.
printf("main - value of h: %p\n", h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// change the value of var z by dereferencing h
*h = 22;
// print value of val
printf("main - value of z: %d\n", z);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
my_func(&h);
// print value of what h points to
printf("main - value of what h points to: %d\n", *h);
// print value of h
printf("main - value of h: %p\n", h);
free(h);
return 0;
}
we have the follwoing output:
main - value of z: 10
main - addr of z: 0x7ffcb94fb1cc
main - value of h: 0x7ffcb94fb1cc
main - value of what h points to: 10
main - value of z: 22
main - value of what h points to: 22
my_func - value of z: 99
my_func - value of p: 0x7ffcb94fb1c0
my_func - value of h: 0x7ffcb94fb1cc
my_func - make p point to z
my_func - addr of z 0xc3b420
my_func - value of p 0x7ffcb94fb1c0
my_func - value of h 0xc3b420
my_func - value of what p points to: 99
main - value of what h points to: 99
main - value of h: 0xc3b420
Now, we actually have changed the value which h holds, from my_func, by doing this:
changed function signature
calling from main(): my_func(&h); Basically we are passing the address of h pointer to double pointer p, declared as a parameter in function's signature.
in my_func() we are doing: *p = z; we are dereferencing the double pointer p, one level. Basically this got translated as you would do: h = z;
The value of p, now holds the address of h pointer. h pointer holds the address of z.
You can take both examples and diff them.
So, getting back to your question, you need double pointer in order to make modifications to the pointer that you've passed in straight from that function.
Think of memory location for head like [HEAD_DATA].
Now in your second scenario, the calling function's main_head is the pointer to this location.
main_head--->[HEAD_DATA]
In your code, it sent the value of the pointer main_head to the function(i.e the address of the memory location of head_data)
You copied that to local_head in the function.
so now
local_head---> [HEAD_DATA]
and
main_head---> [HEAD_DATA]
Both point to the same location but are essentially independent of each other.
So when you write local_head = newnode;
what you did is
local_head--/-->[HEAD_DATA]
local_head-----> [NEWNODE_DATA]
You simply replaced the memory address of previous memory with new one in local pointer.
The main_head (pointer) still points to the old [HEAD_DATA]
The standard way to handle linked lists in C is to have the push and pop functions automatically update the head pointer.
C is "Call by value" meaning copies of parameters are passed into functions. If you only pass in the head pointer any local update you make to that pointer will not be seen by the caller. The two workarounds are
1) Pass the address of the head pointer. (Pointer to head pointer)
2) Return a new head pointer, and rely on the caller to update the head pointer.
Option 1) is the easiest even though a little confusing at first.
The answer is more obvious if you take the time to write a working node insertion function; yours isn't one.
You need to be able to write over the head to move it forward, so you need a pointer to the pointer to the head so you can dereference it to get the pointer to the head and change it.
Imagine a case where you have to make certain changes and those changes should reflect back in the calling function.
Example:
void swap(int* a,int* b){
int tmp=*a;
*a=*b;
*b=tmp;
}
int main(void){
int a=10,b=20;
// To ascertain that changes made in swap reflect back here we pass the memory address
// instead of the copy of the values
swap(&a,&b);
}
Similarly we pass the Memory Address of the Head of the List.
This way, if any node is added and the Value of Head is Changed, then that change Reflects Back and we don't have to manually reset the Head inside of the calling function.
Thus this approach reduces the chances of Memory Leaks as we would have lost the pointer to the newly allocated node, had we forgot to update the Head back in the calling function.
Beside this, the second code will Work Faster since no time is wasted in copying and returning since we work directly with the memory.
When we pass pointer as a parameter in a function and want update in the same pointer we use double pointer.
On the other hand if we pass pointer as a parameter in a function and catch it in single pointer then will have to return the result to calling function back in order to use the result.
I think the point is that it makes it easier to update nodes within a linked list. Where you would normally have to keep track of a pointer for previous and current you can have a double pointer take care of it all.
#include <iostream>
#include <math.h>
using namespace std;
class LL
{
private:
struct node
{
int value;
node* next;
node(int v_) :value(v_), next(nullptr) {};
};
node* head;
public:
LL()
{
head = nullptr;
}
void print()
{
node* temp = head;
while (temp)
{
cout << temp->value << " ";
temp = temp->next;
}
}
void insert_sorted_order(int v_)
{
if (!head)
head = new node(v_);
else
{
node* insert = new node(v_);
node** temp = &head;
while ((*temp) && insert->value > (*temp)->value)
temp = &(*temp)->next;
insert->next = (*temp);
(*temp) = insert;
}
}
void remove(int v_)
{
node** temp = &head;
while ((*temp)->value != v_)
temp = &(*temp)->next;
node* d = (*temp);
(*temp) = (*temp)->next;
delete d;
}
void insertRear(int v_)//single pointer
{
if (!head)
head = new node(v_);
else
{
node* temp = new node(v_);
temp->next = head;
head = temp;
}
}
};
Lets say I noted down your house address on a card-1. Now if I want tell your house address to somebody else, I can either copy the address from card-1 to card-2 and give card-2 OR I can give card-1 directly. Either ways the person will know the address and can reach you. But when I give card-1 directly, the address can be changed on card-1 but if I gave card-2 only the address on card-2 can be changed but not on card-1.
Passing a pointer to pointer is similar to giving the access to card-1 directly. Passing a pointer is similar to creating a new copy of the address.
I think your confusion might come from the fact that both functions have a parameter named head. The two head are actually different things. head in the first code stores the address of of the head node pointer(which itself stores an address of the head node structure). Whereas the second head stores an address of the head node structure directly. And since both function returns the newly created node(which should be the new head), I think there is no need to go for the first approach. Callers of this function is responsible to update the head reference they have. I think the second one is good enough and simple to look at. I'd go with the second one.
The naming convention- Head is the cause of the confusion.
The Head is the Tail and the Tail is the Head. The Tail wags the Head.
The Head is just a Pointer,Data is Null - and the Tail is just Data, Pointer is Null.
So you have a pointer to a struct pointer. the Struct pointer points to the 1st node struct in the Linked list.
This pointer to the 1st struct node pointer is called Head. It can better be called startptr or headptr.
When you catch hold of the startptr you have caught hold of the linkedlist. then you can traverse all the struct nodes.
Do pointers to structures in C work differently than other pointers?
For example in this code:
typedef struct node {
int data;
struct node *next;
} node;
void insert(node **head, int data) {
node *new_node = malloc(sizeof(node));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
int main() {
node *head = NULL;
insert(&head, 6);
Why do I have to use a pointer to a pointer and can't use the variable head in the insert function like in this example with arrays:
void moidify(int *arr) {
*arr = 3;
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(array);
}
Here I don't have to pass &array to the function.
There is no difference. If you want to change the value of the variable you send in to function in such a way that the change is visible in the function that called function, you need to supply its address to function, which is what you do when taking the address of head.
In moidify(array) you send in a pointer to the first element in array which is why modifying the array data works. If you would like to modify the array variable itself (by making it potentially point somewhere else), you would have to take its address too. Example:
void moidify(int **arr) {
*arr = realloc(*arr, 128);
if(*arr == NULL) {
perror(__func__);
exit(1);
}
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(&array);
}
You must understand how pointers works to get this one.
Here, the variable array is not properly speaking, an array. It's a pointer toward a memory space, of size 8 * sizeof(int). It contains only an address. From this address you can access the values of the array, you move using this address, to the rightfully memory space you want to fill or read.
Once that understood, when you call the moidify function, you are not passing the array. Nor the memory space. You are passing, the address of the memory space. The function gets a copy of the given address, in the argument variable int *arr.
Hence, you can use it the same way you use it from the main function.
If you wanted to change the address toward which the array variable would go, you would need to specify &array to the receiving function, which would then use an int ** argument variable.
Your example with struct is similar to this last part I just described, you want to change toward which address head is pointing, so, you need to give &head to the function. To get the address of head, and be able to modify the contained address.
You use an address, to access the memory space called head, to modify the address inside the memory space called head, which point toward another memory space, where your struct truly belongs.
I have a question. Why does the output for the 2 pointers different? I did not assign pointees to either but one does not return NULL while one returns NULL.
typedef struct node
{
bool word;
struct node* children[27];
}
node;
int main(void)
{
node header;
node* header_2 = malloc(sizeof(node));
printf("%p %p\n", header.children[1], header_2->children[1]);
}
OUTPUT: 0xbfba76d4 (nil). Shouldn't both be NULL? Thanks a lot!
Consider the following case:
int i;
int *j = malloc(sizeof(int));
printf("%d, %d", i, (*j)) ;
(You cannot guarantee that i=0 and *j=0 because a memory has been allocated to both but their values may be garbage value which is what that memory location had previously occupied)
In order to have a defined value, always initialize the allocation/initialization with 0.
node a; // Everything default-initialized
void foo()
{
static nodeb; // Everything default-initialized
node c; // Nothing initialized
node d = { 0 }; // Everything default-initialized
node *p = malloc(sizeof(*p)); // Nothing initialized
node *q = calloc(1, sizeof(*q)); // Everything zero-initialized
}
Everything default initialized means they are initialized with the default value which is zero.
Nothing initialized means they will persist the value of the location which may be a garbage value or zero.
Ref link: C struct with pointers initialization
this line: node header;
will contain whatever trash happens to be on the stack at the address of the header variable.
this line: node* header_2 = malloc(sizeof(node));
will contain whatever is returned by the call to malloc
(which, if malloc is successful will be a pointer to somewhere in the 'heap' and if malloc fails will be NULL)
void push(stack *head, int valuee)
{
if(head->next==NULL && head->value==-1)
{
head->value = valuee;
printf("First element %d inserted\n",valuee);
}
else
{
stack *temp = new stack;
temp->value = valuee;
temp->next = head;
head = temp;
printf("Element %d inserted\n",valuee);
}
}
First element is inserted properly but when i continue inserting elements, none of the elements are inserted after the first one.
Read somewhere that i have to pass pointer to pointer of stack but I did this same thing during a postfix infix question and it was working over there.
Help me with this problem here.
Many thanks for any help in advance
Previous example of infix postfix which was working fiine
void push(char c, stack *node)
{
stack *B = (stack *)malloc(sizeof(stack));
if (node->next == NULL)
{
node->next = B;
B->value = c;
B->next =NULL;
}
else
{
B->next = node->next;
node->next = B;
B->value = c;
}
}
You can change the function like this
stack* push(stack *head, int valuee)
//return void to stack *
return head;
//In the end return the new head
and it will work.
Call push like this
head = push(head,value);
Why u need pointer to pointer ?
You want to modify the contents of a pointer so if you pass only a pointer to the function it is pass by copy so you can not modify it actually.
In the case of infix-prefix scenario you must have not modified the string you would have read it only, so a pointer to pointer is not required.
For this line of code in the else portion of the if statement:
head = temp;
Your intention is to mutate head, in other words, change what head is pointing to. However, pointers are passed in as values, just like other variables. In other words, suppose I call the push function somewhere else. For simplicity, suppose I call it in the main function, something like this:
int main()
{
stack *headOfStack = new stack;
// suppose this next push triggers the else portion of the push code
push(headOfStack, 6);
}
Now, after the push(headOfStack, 6); statement has been executed, your intention is to expect headOfStack to point to a new "stack node" which contains the value 6. Now, headOfStack is a pointer to a variable of type stack. It stores a memory address. You can think of a memory address as some integer. When we call push, we are copying the content of headOfStack (the content of headOfStack is a memory address) into the local variable head of the push function. Therefore, when:
head = temp;
is executed, we are assigning the contents of temp to head. What is temp? It is a pointer to a variable of type stack. In other words, the value of temp is a memory address. So head = temp; simply assigns the memory address contained in temp to the local variable head. The local variable head in the push function and our headOfStack variable in the main function are two completely different variables with different memory addresses. If my explanation has been clear so far, this means that when we modify head in the push function, the headOfStack variable in main is totally unchanged.
What you want to do in this case is:
void push(stack **headPtr, int valuee)
{
// this will get the actual pointer we are interested in
stack *head = *headPtr;
if(head->next==NULL && head->value==-1)
{
head->value = valuee;
printf("First element %d inserted\n",valuee);
}
else
{
stack *temp = new stack;
temp->value = valuee;
temp->next = head;
// mutation is done here
*headPtr = temp;
printf("Element %d inserted\n",valuee);
}
}
And its usage, using our fictional main function:
int main()
{
stack *headOfStack = new stack;
// notice the use of &headOfStack instead of headOfStack
push(&headOfStack, 6);
}
Just remember that pointers store memory addresses, and that pointers are just variables, and they have memory addresses as well. To mutate a pointer (change what a pointer is pointing to), just pass in its memory address to the function.
Hope that helps!
EDIT for new edit in question
void push(char c, stack *node)
{
stack *B = (stack *)malloc(sizeof(stack));
if (node->next == NULL)
{
node->next = B;
B->value = c;
B->next =NULL;
}
else
{
B->next = node->next;
node->next = B;
B->value = c;
}
}
For this version of push, what it's doing is essentially:
If node->next == NULL, so node has no successor, then set its successor to a newly allocated node with value c
Otherwise, node->next != NULL and node has some successor. Then we would set the newly allocated node B to node's successor, and set node's original successor to be B's successor. Or in other words, it splices a new node B (with value c) in between node and its successor.
I am finding it quite hard to explain this, but a simple explanation is, this push does not change what node is pointing to. At nowhere did we show intention to mutate node. I think the stuff involving B should be quite understandable, so let's focus on the node->next assignments.
I am assuming that the stack is a struct that looks something like this:
struct stack {
char value;
struct stack *next;
};
Now, suppose in our main function, we have a stack:
stack x;
Notice that x is not a pointer. I think we all agree that doing x.value = something and x.next = something will mutate those fields.
Now, let's look at this:
stack *y = malloc(sizeof(struct stack));
We know that y stores an address to an actual struct stack (the actual struct stack is at *y). So y->value = something and y->next = something will mutate those fields.
So hopefully you can see why the node->value assignments work. Essentially node contains an address to an actual struct stack, whose value is *node. By pointer syntax, node->value and node->next assignments will change the contents of node.
Not a very good explanation I know. But just write more code. Pointers confused the hell out of me when I was first starting out with C. I think that these days, I can still be confused by 2 or 3 layers of indirection, and I've encountered some very nasty pointer bugs. Just practice more... some day you will really get it. I know it's what they all say, but it's true.