Pointers to pointers: Difference between *a = b->c and a = &b->c - c

Looking back at the two star programming article I can't help but fail to see the significance of the difference between the following two lines:
*curr = entry->next;
curr = &entry->next;
The only difference I can see is that the first line changes *curr to point to the next node, and the second line makes a brand new ** pointing to the node member (which is the previous node in the next loop)
It occurs to me that freeing the entry in the first if block would stop the second line from working properly next loop, but in that case why not just use the first line in both cases? Is it a performance question?
Edit: Please read the second code block in the link above titled "Two star programming"
Edit: So I appear to have explained this poorly (Sorry!) so let me see if I can explain it a bit more verbose.
This is the source code in the article.
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
}
By my reckoning the curr = &entry->next; line isn't necessary, you could use the other twice:
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
*curr = entry->next;
}
}
Then you could move it above the if statement and save yourself a few lines:
void remove_if(node ** head, remove_fn rm)
{
for (node** curr = head; *curr; )
{
node * entry = *curr;
*curr = entry->next;
if (rm(entry))
{
free(entry);
}
}
}
In fact it looks like you don't need a pointer pointer at all and can do this:
void remove_if(node * head, remove_fn rm)
{
for (node* curr = head; curr; )
{
node * entry = curr;
curr = entry->next;
if (rm(entry))
{
free(entry);
}
}
}
So why did they do it the first way? Performance? Something else obscure?

The two are completely different.
curr = &entry->next;
takes the address of the variable entry->next, and assigns it to the pointer variable curr. After this assignment, whatever curr was previously pointing to will not have changed, but will have one fewer reference to it.
*curr = entry->next;
Does not change the value of curr at all, but changes the value of whatever it pointed to, which will have the same number of references as before but a different value.
Yes, both of these will have as one effect that *curr will be equal to entry->next, but they in fact write different values to different memory locations and have other side effects that matter.

The code in the article is correct, and all three of your proposed variants are not correct. Let's look at the correct code first, with some annotations:
void remove_if(node ** head, remove_fn rm)
{
// node ** passed to allow use to modify caller's head pointer
for (node** curr = head; *curr; )
{
// curr is a local variable, that points to a node pointer
// curr points either to the caller's head pointer, or to
// a next pointer within the list
node * entry = *curr;
if (rm(entry))
{
// remove this entry, which means modifying the list
*curr = entry->next;
// modify *curr modifies either caller's head pointer
// or a next pointer
free(entry);
}
else
// did not remove, so do not modify the list
curr = &entry->next;
}
}
The key points to note here:
*curr = entry->next modifies the list.
curr = &entry->next does not modify the list.
Your first two proposed versions are wrong because they do
*curr = entry->next
at every iteration. Assigning to *curr modifies the list. Imagine a scenario where rm() always returned false. In that case you must never modify the list, but you modify it every time round the loop.
And the final variant is wrong in because you are only passing in a node * then there's no way for the caller's head pointer to be modified. In fact, the final variant does not modify the list at all.

Related

Reverse fuction for singly linked list isn't working as it should

we've given a task to reverse a singly linked list and for some reason i struggling with it
its reversing as it should ,but the head that should be the tail just disappears and i can't figure why, even after debugging
'''
void Reverse(struct node *head) {
struct node *last = NULL;
struct node *current = NULL;
struct node *temp = NULL;
current = head;
while (current->next != NULL) { //getting ptr to last item of the list
current = current->next;
last = current;
};
current = head; //resseting the current ptr back to the head of the list
while (current->next->next != NULL) { //getting the current ptr to one before the tail item
current = current->next;
};
temp = last;
while (last != head) {
if (current->next == last) {
last->next = current;
last = current;
current = head;
if (last == head) {
head->next = NULL;;
head->data = temp->data;
head->next = temp->next;
break;
};
};
};
};
'''
I can't completely follow the logic in your code. You find the last and the second last element, and I will assume that head is some dummy element that you always have (because otherwise, not checking if it is NULL is a problem). After that, I am not sure what happens. You "flip" the last two links if you are looking at the head you update it? (You don't need to set head->next to NULL before you update it two lines later; that doesn't do anything).
Did you want to move current from the head and down to the one before last somewhere in the loop? I think that would work, but you would get a quadratic time algorithm.
If we assume that head is a dummy, so it doesn't have any elements to worry about, and we just want its next pointer to point to the reversed links, you should be able to do something like this:
void reverse(struct node *head)
{
struct node *next = head->next;
head->next = 0;
while (next) {
struct node *next_next = next->next;
next->next = head->next;
head->next = next;
next = next_next;
}
}
In the while-loop you can think of the code as pushing next to the front of the list pointed to by head->next and popping it off the current list (by setting next to next_next. When you reach the end of the list, head->next points to all the links, in reversed order. (I haven't tested the code, but I belive it should work).
If head is not a dummy node, you have to handle the special case that it is NULL, and you have to move its value. It makes things trickier if you have to do the latter, especially if you want to use the list generically, where the user is allowed to allocate links, and you might not know what data is in them (beyond that they have a link embedded in them). I would go for having a dummy link if you can, it makes everything simpler, not just reversal.
Of course, with doubly-linked lists it gets simpler still:
#define swap_p(x,y) \
do { struct node *tmp = x; x = y; y = tmp; } while(0)
void reverse(node *dummy)
{
struct link *p = dummy;
do {
swap_p(p->prev, p->next);
p = p->prev;
} while (p != dummy);
}
but that might not be what you want.
Don't know if that helps, but I hope it does a little at least.

Why does my code not insert a new node into this linked list?

I am new to programming. I just want to know why this doesn't work.
My understanding of pointers isn't clear, especially when using pointers across functions.
void append(struct Node** head_ref, float new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
new_node->data = new_data;
new_node->next = NULL;
while (last != NULL)
last = last->next;
last = new_node;
return;
}
void append(struct Node** head_ref, float new_data)
{
while (last->next != NULL)
last = last->next;
last->next = new_node;
return;
}
In the first function the new data doesn't get included, I get only the original linked list.
But the second function works just fine. How does a double pointer work when inserting a new node in the beginning of the linked list? (I have seen answers regarding this question, but I am still confused)
In the first example, you move the pointer last until it points at a NULL location. Then, you set the pointer to new_node. However, at this point, last has no real association to your linked list. It is just a pointer to some memory. In the second example, the correct one, you iterate until you reach the tail of the linked list, where next of that node is NULL. Then, you set that next to new_node. There is now a new tail to the list, that is new_node.
Changing the local variable last does not change the value of the data member next of the previous (last) node.
To be more clear let's assume that the list is empty. Then you have to change the pointer referenced by this double pointer head_ref.
You declared a new pointer
struct Node *last = *head_ref;
The loop
while (last != NULL)
last = last->next;
is skipped because now already last is equal to NULL die to the initialization in the previous declaration. And then you changed this local variable last
last = new_node;
The original value of the pointer pointed by head_ref was not changed because last and *head_ref occupy different extents of memory. You changed the memory occupied by last but not changed the memory occupied by head_ref.
Also you should check whether a memory was successfully allocated.
The function can look the following way
int append( struct Node **head_ref, float new_data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = new_data;
new_node->next = NULL;
while ( *head_ref != NULL ) head_ref = &( *head_ref )->next;
*head_ref = new_node;
}
return success;
}
As for this loop (I think you wanted just to show the loop not a whole function)
while (last->next != NULL)
last = last->next;
last->next = new_node;
then you are changing the data member next of the previous (last ) node.
Though this loop will not work if initially head_ref is equal to NULL.

Destroy a full linked list (c programming)

I am a beginner to C programming and am a bit stuck on pointers. i am trying to create a function that deletes all the elements of the linkes list. However my code deletes all elements except for the head.
I cannot alter the
void destroy(node *h)
parameters due to assignment title.
void destroy(set_element* head){
set_element* temp ;
set_element* curr = head;
if(head){
curr = head->next;
head->next = NULL;
while(curr !=NULL){
temp = curr->next;
free(curr);
curr = temp;
}
head =NULL;
}
}
thanks in advance.
The problem is this line
curr = head->next;
Here you make curr point to the next node. You need to make curr point directly to head.
A simpler version of the function might be e.g. this:
void destroy(set_element **head)
{
set_element *next;
for (set_element *curr = *head; curr; curr = next)
{
next = curr->next;
free(curr);
}
*head = NULL;
}
Note that I pass the head pointer by reference, otherwise the assignment to NULL will only change the local copy of the pointer.
If all you want to do is delete the entire linked list (head node included) and don't care whether the caller is left with a dangling pointer, this gets significantly simpler:
void destroy(set_element* head)
{
set_element* temp;
while (head)
{
temp = head;
head = head->next;
free(temp);
}
}
Invoked on the caller side as:
destroy(head);
That said, if you want to modify the callers passed-in pointer you cannot do it with this function signature. Like all other things in C (arrays not withstanding) parameters are passed by value, and if you need to modify the callers data, the parameter must be a pointer, and the passed-in value an address:
void destroy(set_element** headp)
{
set_element* temp;
while (*headp)
{
temp = *headp;
*headp = temp->next;
free(temp);
}
}
Invoked on the caller side as
destroy(&head);
Both of these assume your list is properly terminated with NULL.

How to clear a linked list using double pointer?

Hi I'm trying to make a function that clears a linked list that *first will point to, then the node **first should be freed and the pointer *first set to NULL.
I'm having trouble grasping double pointers and can't get this to work correctly.
You have to move to the next list element before you delete the node. Otherwise you are accessing memory that has been freed.
while( *first != NULL )
{
temp = *first;
*first = temp->next;
free(temp);
}
Be aware that because you're trashing the whole list, *first is going to eventually be NULL. So you can use a single-pointer (just like temp) to traverse your list, and then set *first = NULL at the end. That saves an extra pointer indirection, which arguably is wasteful of CPU in this case.
[edit] What I mean is:
struct node *curr = *first;
struct node *prev;
while( curr != NULL )
{
prev = curr;
curr = curr->next;
free(prev);
}
*first = NULL;
Generally, I find that the less pointer dereferencing you have going on, the easier the code is to understand at a glance.
node* remove_node(node **double_pointer,int search_value)
//pass head of your node as a parameter to double pointer
{while(*double_pointer && **(double_pointer).value!=search_value)
{
double_pointer=&((**double_pointer)->next);
}
if(**double_pointer!=null)
{//lines below are to delete the node which contains our search value
node* deleted_node=*double_pointer;
*double_pointer=*double_pointer->next;
return deleted node;
}}

swap elements in a linked list without access to it?

if i have a reference to an element in a linked list, how do i swap it with the next element, in c
here's a try,
Node* nRoot, *temp=pNode->next;
nRoot=pNode;
do{
nRoot->next = temp->next;
if(nRoot==pNode) pNode=temp;
temp->next = nRoot;
nRoot=nRoot->next;
}while(nRoot!=NULL)||temp!=NULL);
but it does not work
You can only do this if it double linked list. You need the previous pointer so that you can point it's next to the current's next.
However if you have these then you an do something like this:
Node* next = curr->next;
Nide* prev = curr->prev;
curr->prev = next;
curr->next = next->next;
curr->next->prev = curr;
next->prev = prev;
next->prev->next = next;
next->next = curr;
And the 2 are swapped.
Edit: Of course you can do this with a singly linked list but you do need to know the previous node so that you fix up its next pointer to point to the current's next.
If you have a reference to A and A->next is B, you can do this. I'm assuming they hold a Data* pointer, replace with whatever the data is. Don't actually swap the nodes, just swap the data in the nodes.
void push_forward(Node* curr)
{
Data* currData = curr->data;
curr->data = curr->next->data;
curr->next->data = currData;
}
For the record, I am not a C guy so this might be correct only in algorithm, but not in implementation. I welcome edits, fixes, suggestions, and constructive comments!
If there are no external pointers to the elements that you wish to swap, then you can just swap the data within the list nodes, rather than the nodes themselves, as pointed out in other answers.
If you have external pointers to the list nodes, then you should probably not mess with the node content, unless the rest of your program is find with node contents changing from under its feet.
You will have to swap the nodes, which means that you need to have a pointer to the node that precedes the ones that you need to swap. If you only have the head of the list, then the swap function could be something along these lines:
void swap(Node **list, Node *first) {
Node *i = *list;
Node *p = NULL;
while (i != NULL) {
if (i == first) {
Node *n = i->next;
/* No next node to swap with */
if (n == NULL)
break;
if (p != NULL) {
p->next = n;
} else {
*list = n;
}
i->next = n->next;
n->next = i;
break;
}
p = i;
i = i->next;
}
}

Resources