Dynamic memory Allocation in Linked list insert function - c++

I was going through a tutorial on Linked Lists in C++. I found the following code for the implementation for inserting an element in the Linked List:
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(Node** head_ref, int new_data)
{
/* 1. allocate node */
Node* new_node = new Node();
Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be
the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty,
then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
In step 1, we are declaring a pointer with name new_node and it is pointing to a dynamically created block.
What I couldn't understand is that, if the function is called 4 times then how can a new pointer variable with the same name be created on each call? Since we are using dynamic memory allocation, therefore it won't be dumped when we return from function.
So, how is this piece of code working?

Variable names don't exist at runtime, only at compile-time.
The new_node variable represents a chunk of memory that is local to the append() function. Each time append() is called, a new memory chunk is created when it enters scope, and that chunk is released when it goes out of scope.
Each call to new allocates a new block of dynamic memory. In this case, the memory address of that block is being stored inside that local memory chunk that new_node represents.
append()
+----------------+
| new_node |
| +------------+ | +-------------+
| | 0xABCDABCD |-|------| Node object |
| +------------+ | +-------------+
+----------------+

Related

Remove node in a singly linked list

Suppose you have a pointer p to a node in a singly linked list that is
not on the last node in the list. You have no other pointer to the
list except the following links in each node.
Describe an O(1) algorithm that logically removes the value stored in
the node pointed to by p (suggestion: use the next node).
The idea to do it is to transfer information from the next node to the current node pointed to by p and the next node is removed from the list. My question is why don't we remove the node pointed by our pointer instead of removing the next node. I am a bit confused.
You have a pointer to the node, but no way to change the previous node's reference to it; therefore you must copy and remove the next node.
Your pointer p only references the current node. Since you can't modify prevous->next to point at the node with value 3, you must copy next node into the current one, both value and next pointer.
My question is why don't we remove the node pointed by our pointer instead of removing the next node.
You can't remove the current node.
The question states:
You have no other pointer to the list except the following links in each node.
This means that there are nodes prior to yours and, in particular, there is an immediately prior node that points to yours. You cannot delete your node because you have no way of changing the immediately prior node.
You can't remove the next node.
The question states that you need to remove:
the value stored in the node pointed to by p.
That's definitely not the value of the next node.
Summary
You will replace the node at the address of p with the next node in the list. When you start you have:
+----+ +----+
| | | |
-->| p |-->|next|-->
| | | |
+----+ +----+
0x1234
After you replace the node at the current address you will have:
+----+
| |
-->|next|-->
| |
+----+
0x1234
Nothing prior to the address of p in the list changes. When you are done, you simply:
delete p;
Detail
Your question setup tells you you have a pointer p to a node that is not the last node in your list and you need to remove the value at that node. Great, most of the work is already done, no need to iterate to find the node to remove. So how to make use of the current pointer to remove the value (actually replace the node at that address with the next node)?
Here you can make use of the address of the pointer p (&p) and the pointer to the next node (p->next) to copy the content of the next node to the present address, overwriting the current node contents with it. Since you haven't changed where the original pointer p points, it still points to the memory for the original node that was linked at the current address allowing you to free the memory using pointer p to complete the operation.
So what you want to do is first, replace the node at the current address in the list, e.g.
node **ppn = &p; /* a pointer to pointer holding current address */
*ppn = p->next; /* replace node at address of p with p->next */
(where p points to the current node, &p is the address for the pointer that is linked in your list, and p->next is a separate pointer to the next node in the list)
Since the previous next pointer still points to this address, and you have replaced the node at the current address with the next node in the list (whose next pointer still points to the correct following node), this is all that is required to remove the node that was original linked at p in the list.
To complete the process and avoid a memory leak, you need to free (delete) the memory for the node you removed. Since you have not changed where p itself points, it still points to the node you have removed from your list. So you can simply:
delete p;
and you are done.
It will take a minute to wrap your head around how you have used the address of p &p as a placeholder in your list, and you have simply replaced the node that was originally at that address in your list, with the next node in your list. This removes the node pointed to by p from the list. prev->next still points to the address of p and the new node you have assigned to that address still has a valid ->next pointer to the node that follows it in the list, so your list is complete sans one node. The pointer p still points to the memory holding the node that was removed, so you simply delete p; to tidy up.
That is what is explained in Linus on Understanding Pointers, though I never really loved that explanation as I found it a bit thin and awkwardly written.
Now you can take this knowledge and look at the del_node() and delnode() functions I pointed you to and begin digesting how it all works. It may take an hour or a day for the light-bulb to wink on and it all fall into place. That's fine. Thinking about how you can use both the address of a pointer as well as where the pointer points (e.g. the address it holds as its value) takes a bit of wrestling with to make peace with.
Full Example
A full example using:
*ppn = p->next; /* replace node at address with next */
free (p); /* delete current */
to remove the 4th node in a list of 1 - 10 using only p and its address:
#include <stdio.h>
#include <stdlib.h>
typedef struct node_t {
int data;
struct node_t *next;
} node_t;
/** add node at end of list, update tail to end */
node_t *add (node_t **head, int v)
{
node_t **ppn = head, /* pointer to pointer to head */
*pn = *head, /* pointer to head */
*node = malloc (sizeof *node); /* allocate new node */
if (!node) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
node->data = v; /* initialize members values */
node->next = NULL;
while (pn) {
ppn = &pn->next;
pn = pn->next;
}
return *ppn = node; /* add & return new node */
}
/** print all nodes in list */
void prn (node_t *l)
{
if (!l) {
puts ("list-empty");
return;
}
for (node_t *n = l; n; n = n->next)
printf (" %d", n->data);
putchar ('\n');
}
/** delete all nodes in list */
void del_list (node_t *l)
{
node_t *n = l;
while (n) {
node_t *victim = n;
n = n->next;
free (victim);
}
}
int main (void) {
node_t *list = NULL, *p = NULL, **ppn = NULL;
int node_to_rm = 4;
for (int i = 0; i < 10; i++)
if (!add (&list, i + 1))
return 1;
prn (list);
/* iterate to find 4th node (with data = 5) */
for (ppn=&list, p=list; p && node_to_rm; ppn=&p->next, p=p->next)
node_to_rm--;
*ppn = p->next; /* replace node at address with next */
free (p); /* delete current */
prn (list);
del_list (list);
}
Example Use/Output
$ ./bin/ll_rm_single_node_given_p
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
Memory Use/Error Check
$ valgrind ./bin/ll_rm_single_node_given_p
==22684== Memcheck, a memory error detector
==22684== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22684== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==22684== Command: ./bin/ll_rm_single_node_given_p
==22684==
1 2 3 4 5 6 7 8 9 10
1 2 3 4 6 7 8 9 10
==22684==
==22684== HEAP SUMMARY:
==22684== in use at exit: 0 bytes in 0 blocks
==22684== total heap usage: 11 allocs, 11 frees, 1,184 bytes allocated
==22684==
==22684== All heap blocks were freed -- no leaks are possible
==22684==
==22684== For counts of detected and suppressed errors, rerun with: -v
==22684== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Insert a new node in an ordered linked list in C++

I am writing some code in visual studio with c++, it's an ordered linked list but I am having a bit of trouble with the pointers.
I have three different methods / functions that carry out this task.
/*
* insert_head: Insert a node at the beginning of the list.
*/
book *inserta_head(book *head, book *newNode){
newNode->next = head;
return newNode;
}
/*
* insert_after: Insert a new node after another one.
*/
void insert_after(book *node, book *newNode){
newNode->next = node->next;
node->next = newNode;
}
/*
* insert: Adds a new node (ordered by code) to the list.
*/
void insert(book* head, int code, char name[40], char lastName[40], char title[40], int year, int lend) {
book* newNode = crear_libro(code, name, lastName, title, year, lend);
book* aux = head;
// If the list is empty.
if (head == NULL){
head = insert_head(head, newNode);
} else {
// If the new node goes before the head.
if (aux->code > newNode->code){
head = insert_head(head,newNode);
} else {
while (aux != nullptr && aux->code < newNode->code)
aux = aux->next;
// Verify the code isn't repeated
if (aux != nullptr && aux->code == newNode->code){
printf("Error: Verify the code. \n");
} else {
insert_after(aux,newNode);
}
}
}
}
I've tried running the code. Every time I try to print the list it says it's empty. I've checked my printing method and the method that creates nodes, both of them are working so I'm pretty sure it is related to the pointers but I can't find the error.
Your insert function changes the head pointer. But that pointer is a copy of the head pointer you called the function with. So the head pointer outside the insert function is unchanged. That's why nothing gets added to the list.
One simple fix is to make the head parameter a reference.
void insert(book*& head, int code, ...
The problem is how you handle the head.
After this line:
head = insert_head(head, newNode);
head in the function should be correct (double check with a debugger).
However, the head in the caller will remain unchanged. This is because you don't change the data in the existing head, but you create a new one.
A simple fix is to take the pointer to the pointer to head. book** head This way you can change the pointer in the caller as well (after fixing all the compilation errors).

What faulty logic is causing this push_back(...) function of my linked list to fail?

For some reason
template <typename T> void SinglyLinkedList<T>::push_back ( T v )
{
node * newNode = new node;
newNode->val = v;
newNode->next = NULL;
if (_root != NULL)
{
node * thisNode = _root;
while (thisNode->next != NULL) thisNode = thisNode->next;
thisNode->next = newNode;
}
else
{
_root = newNode;
}
}
has incorrect logic and I need some help figuring out what it is. When I tested
int myArray [] = { 1, 69, -23942, 69, 56, 67 };
SinglyLinkedList<int> myList(myArray, sizeof(myArray)/sizeof(int));
myList.push_back(33);
myList.print();
it printed
1->69->-23942->69->56->33->33->33->33->33-> ...
until the program crashed.
I'll explain my logic so that you can pinpoint where I'm wrong:
node * newNode = new node;
newNode->val = v;
newNode->next = NULL;
creates a new node object on the heap and initializes its value to v and its pointer to the next node as NULL. This is the node that will be added on to the end of the list. The end of the list will either be
(1) a final element whose next is NULL
or
(2) non-existent, meaning there are no elements, hence no final element
For micro-optimability, because I expect case (1) to occur more often I put it inside the if clause that is to follow and put case (2) inside the else clause.
Case (2) occurs if and only if the _root node is NULL, meaning the list is empty, and the handling of it is simply to make that _root be newNode:
else
{
_root = newNode;
}
Case (1) requires finding the final node and setting its next to newNode, which should be accomplished very simply with the 3 lines
node * thisNode = _root;
while (thisNode->next != NULL) thisNode = thisNode->next;
thisNode->next = newNode;
What is the flaw in that logic?
The error is in your constructor. This line:
delete lastNode;
is deleting memory that your are still using (the node that lastnode points to has just been put in your list).
Remove that line.
Edit: further explanation. Once the memory that lastnode points to is freed, the runtime can then use that memory again for another call to new. In your your case you have probably ended up with newNode and thisNode pointing to the same location, causing the node to point to itself.
when you delete last node, the node which its next is null deleted and the previous one next pointer is not null so when you parse the linked list you directed to unknown memory places!!!

Copy constructor for a doubly linked list, issue with head initalization

I'm trying to write a copy constructor for a templated, double-linked list...
Here's what I have so far:
// Copy Constructor
template <typename Item>
LinkedList<Item>::LinkedList(const LinkedList<Item> &s){
// Create an iterator for the other list
ListNode<Item> *node = s.head;
// Create a reference to use as an iterator for the new list
// Set it to the address of head of the new list
ListNode<Item> **curr_node = &head;
// Create a placeholder for the address of the previous node
ListNode<Item> *prev_node = NULL;
while(node != NULL){
*curr_node = new ListNode<Item>;
// If a prev_node address has been saved, initialize the new node's prev value
// If a prev_node hasn't been saved yet, this means it is the head and prev should
// be initialized to NULL
(*curr_node)->prev = prev_node;
// Set the new node data fields to that of the node in the other list
(*curr_node)->data = node->data;
// -------- Set up for next iteration -------
// Save the address of the current node to be used in the next node's prev_node
prev_node = *curr_node;
// Set the curr_node pointer to the address of the next node in the new list
curr_node = &((*curr_node)->next);
// Set the node pointer to the next node in other list
node = node->next;
}
// Set the tail after all the nodes have been copied
tail = prev_node;
}
When I call this code in my tester:
LinkedList<int> ll;
ll.insert_back(5);
ll.insert_back(10);
ll.insert_back(15);
LinkedList<int> ll1 = ll;
cout << "Printing contents of copied list: "; ll1.print();
I get this error in valgrind:
Contents of list to be copied: [5] -> [10] -> [15]
==4624== Conditional jump or move depends on uninitialised value(s)
==4624== at 0x401077: LinkedList<int>::print() (LinkedList.cc:159)
==4624== by 0x400D7B: main (tester.cpp:54)
==4624==
==4624== Conditional jump or move depends on uninitialised value(s)
==4624== at 0x40109E: LinkedList<int>::print() (LinkedList.cc:155)
==4624== by 0x400D7B: main (tester.cpp:54)
Line 153, 155, 159 of my print() function:
153: ListNode<Item> *end = head;
155: while(end != NULL){
159: if(end->next != NULL)
So I draw the conclusion that end is never initialized meaning that head is NULL or set to some junk value... Anyone have any thoughts on this? Where am i going wrong?
It seems like you would want to replace the line
curr_node = &((*curr_node)->next);
with
*curr_node = &(node->next);
since curr_node was set to a new ListNode<Item> earlier inside the while loop.
Just curious, why is curr_node a pointer to a pointer to a node?

Memory not freed up after deleting linked list's last node

I coded the following. Insert, delete at beginning, insert at beginning and end are all working fine. The memory marked in bold is not freed up. Should cout<<temp give an error? Please comment on the correctness of this code.
void del(node** head)
{
node* temp = (*head)->next;
node* prev = *head;
while(temp ->next!= NULL)
{
prev = temp;
temp = temp -> next;
}
cout<<endl<<temp;
cout<<endl<<prev;
//delete prev ->next;
prev -> next = 0;
delete temp;
cout<<endl<<"temp after free"<<temp;
cout<<endl<<prev;
}
void main()
{
node* head = NULL;
int x = 5;
head = insert(head,x);
insert(head,6);
insert(head,7);
insert(head,8);
print(head);
del(&head);
print(head);
getch(); }
Output:
Empty List
|5| at 00673BF0
-------------------
|6| at 00673C30
-------------------
|7| at 00673FB8
-------------------
|8| at 00673FF8
-------------------
00673FF8
00673FB8
temp after free00673FF8
00673FB8
|5| at 00673BF0
-------------------
|6| at 00673C30
-------------------
|7| at 00673FB8
-------------------
delete does not set the value of the pointer to NULL, but the memory pointed to is no longer valid (does not contain a live node)
This means that cout << temp will print the value of the pointer it still has (and was the address of the node before the delete), but dereferencing temp (e.g. *temp or temp->next) is undefined behavior
Note: nowehere in del do you modify the pointer pointed to by head, so you either don't need the double indirection (node**) or you should assign the new head to *head.
As Attila already pointed out it's undefined behavior to dereference a pointer after deleting it. To prevent accidentally doing that it's good practice to assign NULL to the pointer directly after the delete. Dereferencing a NULL pointer immediately causes an error pointing you to the right cause.
In the code its good to free the temp node first. Then assign the prev->next to NULL. So that it will be very easy to understand that temp node is not available, thats way making prev->next NULL.