Reputation: 9590
Let's say I have a linked list node like the following:
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
The goal is to write a function to delete a node in a singly-linked list. One efficient way to do it in constant time is something like this:
void deleteNode(ListNode* node) {
auto *tmp = node->next;
*node = *tmp;
delete tmp;
}
This works, but why do we need to dereference the pointers?
If node
is a pointer and tmp
is a pointer, why does it need to dereferenced? Why can't I do node = tmp
?
Upvotes: 1
Views: 637
Reputation: 238311
but why do we need to dereference the pointers?
Let's explore what happens if we don't indirect through the pointers:
auto *tmp = node->next;
node = tmp;
delete tmp;
This would be equivalent to just
delete node->next;
// resulting structure
previous node next (deleted) next next (leaked)
1---------->2----dangling--->_ 4
// desired structure that we get from the correct code
previous node next (deleted) next next
_
1-----------3---------------------------------->4
So, we end up with wrong node being deleted, and with a dangling pointer in the node that was supposed to be deleted.
Note that even the correct inirecting version is broken when attempting to delete the last node.
Upvotes: -1
Reputation: 22074
What your function really does, is that it doesn't delete the node from the parameter, but the next node, overwriting the current node with the follower.
The dereferencing of the pointer acts like a memcpy()
and moves the data from the next node to the current. You are not copying the pointers but the data it points to.
This way you can repeatedly call the function with the same node pointer, and it will move down the chain. However, since you are not checking the pointer, the last node probably has a NULL pointer and will crash on derefencing.
So you need to do
if (tmp)
*node = *tmp;
Example:
typedef struct list
{
struct list *next;
int value;
} List;
void deleteNext(List* node)
{
auto *tmp = node->next;
if(tmp)
*node = *tmp;
delete tmp;
}
int main(int argc, char *argv[])
{
List *l0 = new List;
List *l1 = new List;
l0->value = 0;
l0->next = l1;
l1->value = 1;
l1->next = NULL;
deleteNext(l0);
deleteNext(l0); // Without the 'if' it will crash here.
return 0;
}
Upvotes: 0
Reputation: 2152
node = tmp
just changes the argument(local variable)*node = *tmp
is to copy the content of ListNode
which is equivalent to
node.val = tmp.val; node.next = tmp.next
node->next
as a pointer, it is now a dangling pointer)Upvotes: 0
Reputation: 51825
Let's break down the three lines of your deleteNode
function:
auto *tmp = node->next;
This creates a local variable, tmp
which will be a copy of the next
field of the passed node
parameter. This is a pointer to the next structure in the list and, once we've made a copy of it, we can erase or overwrite that member.
*node = *tmp;
This copies the actual data of the structure pointed to by tmp
(that is, the next node in the list) to the current node, overwriting the next
field as it does so. We need to dereference both pointers in order to copy the values of the structures pointed to.
delete tmp;
This deletes the 'next' node in the given list. However, we have already copied all its data (including its next
member) into our current node, so our modified list now starts with (effectively) the second one in the original list (the passed parameter); notably, the next
field of *node
will now be the address originally stored in node->next->next
– thus, we have 'skipped' an entry (the second) in the list and deleted it.
Upvotes: 1
Reputation: 91
If you do node = tmp
and after that delete tmp you will be deleting the ListNode
, which node
points to.
Upvotes: 0
Reputation: 16805
When performing *node=*tmp
you copy all the bytes of *tmp
into *node
thus
node->val
now holds tmp->val
and node->next
now holds tmp->next
.
The old content of node
has been forgotten (it's normal since you want to get rid of this node) but you saved the content of the next node at this same place.
Then if you delete the next node (known through tmp
) you don't lose its content (it has been saved in the previous node).
Upvotes: 1
Reputation: 122133
Why can't I do
node = tmp
?
You can do that, but it won't do anything useful. node
is a local variable in deleteNode
. As it is a pointer you can use that local pointer to modify what it points to, but modfying the pointer itself has no effect outside of the function.
Actually pointers are not different with respect to that. You also cannot observe any effect from outside when you have
void foo(int x) {
x = 42;
}
Passing a reference is different:
void bar(int& x) {
x = 42;
}
int a = 0;
bar(a); // now a == 42
Same with pointers:
void bar_ptr(int*& x) {
x = nullptr;
}
int* b = &a;
bar_ptr(b); // now b == nullptr
Upvotes: 0
Reputation: 41
The reason you can't just write node = tmp
is because that wouldn't change anything outside of your function.
Given this linked list
node0 -> node1 -> node2 -> node3
If you want to delete node1, the desired outcome would be
node0 -> node2 -> node3
If you don't want to actively modify the pointer value (that is, the address next
) in node0
, you have to move the value inside node2
to where node1
was.
Upvotes: 0