I have plenty of previous experience with linked lists in Java, but I seem to have confused myself with this simple attempt in C++. I am getting a segmentation fault at runtime, which from what I understand has to do with assigning a null pointer, but I am at a loss for a solution.
Edit: Thank you all for the very helpful responses. The code is now working, but trying to use delete p; at the end of linkedList::addNode results in a segmentation fault at runtime. Just curious if anyone knew why that is?
Here is my updated code:
#include <iostream>
using namespace std;
class Node{
public:
int data;
Node * next;
Node(int x){
data = x;
next = NULL;
}
Node(int x, Node * y){
data = x;
next = y;
}
};
class linkedList{
Node *head;
public:
linkedList(){
head = NULL;
}
void addNode(int value){
Node *p;
if(head == NULL)
head = new Node (value, NULL);
else{
p=head;
while(p->next !=NULL)
p=p->next;
p->next = new Node (value, NULL);
}
}
void print(){
Node * p;
p = head;
while(p != NULL){
cout << p->data << "\n";
p = p->next;
}
}
};
int main(void){
linkedList test;
test.addNode(4);
test.addNode(76);
test.addNode(12);
test.print();
return(0);
}
First, in linkedList::addNode method, you have the construction if (head = NULL), which will wind up assigning to head; you want the == operator.
Second, about the line:
head = &(Node (value, NULL));
For somewhat unintuitive reasons, this won't work. You'll get a reference to a Node, but that node will go out of scope as soon as the method ends, and attempts to reference it will lead to a segmentation fault. You need to use the new operator (same with the other similar line):
head = new Node(value, NULL);
If you add a method for removing a node, make sure to delete the node then—it won't get automatically garbage-collected like it will in Java.
Sidebar: Think of what happens like this: when you do Node(value, NULL), you're using a temporary variable that's declared like this:
Node hiddenTempNode(value, NULL);
This doesn't allocate space for an object anywhere except on the stack—it's very similar to allocating space for an int and a Node * on the stack as separate variables. As a result, as soon as you leave the method, the object disappears and the pointer to it will do weird things when used.
Third, beware: you may want to set next = NULL in your single-parameter constructor, to ensure that it always has a value. Similarly for your default constructor.
Fourth: your linkedList::print method is looping until p->next is NULL and printing the value of p->next; those occurrences of p->next should probably be changed to just p if you want to get the first and last items.
you are taking the address of variables on the stack
head = &(Node (value, NULL));
should be changed to
head = new Node(value, NULL);
same for the p->next code. Then you will want to delete these nodes in your destructor.
As for the printing try
while(p != NULL)
{
cout << p->data << "\n";
p = p->next;
}
You are allocating space for nodes on stack and grabbing its address, which will go away as soon as the block ends and consequently, the address will be rendered invalid. You should allocate nodes using new operator on the heap instead:
Node* node = new Node(value, NULL);
You should free everything you allocate on the heap as soon as you don't need it to prevent memory leak:
delete node;
For starters
if(head = NULL)
is an assignment, not a check for equality. Change it to
if(head == NULL)
Secondly,
head = &(Node (value, NULL));
Doesn't make sense* change this to
head = new Node (value, NULL);
*this actually creates a temporary object, gives you the address, then destroys that newly created object.
Thirdly,
Node(int x) { data = x; }
Leave next without a value, change this line to
Node(int x) { data = x; next = NULL; }
You are not allocating memory.You should use new to allocate it.
One more error in if(head = NULL) , it should be if(head == NULL)
void addNode(int value){
Node *p;
if(head == NULL)
head = new Node (value, NULL);
else{
p=head;
while(p->next !=NULL)
p=p->next;
p->next = new Node (value, NULL);
}
}
I'd like to add two issues that were not mentioned, yet:
when you 'new' objects, you must 'delete' them at some point.
all three of your constructors should initialize both member variables.
Your delete statement is not actually doing any cleanup. By the time you call it p==null. If you want to cleanup the list, you will need to implement a separate method to iterate through, and delete each and every node.
Something like this:
void ClearList ()
{
Node * c = head;
Node * n;
while (c != NULL)
{
n = c->next;
delete c;
c = n;
}
}
The code is now working, but trying to
use
delete p;
at the end of linkedList::addNode
results in a segmentation fault at
runtime. Just curious if anyone knew
why that is?
Well this is an issue because the purpose of the add node function was to dynamically allocate a new node at the end of the LinkedList. So you do that correctly, now by putting 'delete p;' you are deleting the newly added node (something I imagine you don't actually want). However, to answer your question which is why this causes a segmentation fault:
You add a node, you tell head to point to this new node. Now you delete this new node without telling head that it should once again point to NULL. Thus next time you add a node, or try to print your list, it will immediately attempt to look at what head is pointing to, which is in fact released (deleted) memory, kaboom?
The correct (or at least one correct) usage of delete in your list is in a destructor, remember in C++ we always want to clean up the dynamic memory that we have allocated, your destructor might look like:
~linkedList()
{
Node* p = head;
while ( p!=NULL )
{
Node* nextNode = p->next;
delete p;
p = nextNode;
}
}
By using a destructor like this you guarantee that your linkedList will be cleaned up appropriately when it goes out of scope, or is deleted.
Solution: Don't implement your own linked list. Use the one supplied by the standard library.
Related
I am having serious trouble cleaning up the memory of my circular linked list. I think I understand the issue though. My head pointer has, say, N allocations and my build pointer has N-1 allocations. The addback function can only have an integer and I can't use containers or vectors.
void Queue::addBack(int passed_Val)
{
if (head == nullptr) //takes care of first node when list is empty
{
head = new QueueNode;//head is my building now
cout<<"head is "<<sizeof(head)<<" bytes"<<endl;
head->val = passed_Val;
head->next = head;//this make an infinite loop
head->prev = head;
cout<<"node[0] mem allocated "<<head<<endl;
}
else
{
//build next will always have N-1 address, where N = number of addresses
//head points to. I don't want build to point to head when N=1
// have tempPtr point to old address
QueueNode* tempPrev = head->prev;//this will always point to the last node added!
QueueNode* build = new QueueNode;
build->next=head;
cout<<"build is "<<sizeof(build)<<" bytes"<<endl;
cout<<"node[1] mem allocated "<< build <<endl;
cout<<"node[1] "<< head <<endl;
build->val=passed_Val;
build->next = head;
head->prev = build;
tempPrev->next = build;
build->prev = tempPrev;
}
and my destructor looks like this
Queue::~Queue()
{
if(head !=nullptr)//needed because i don't want to deallocate head if it was never called
{
QueueNode* deletePtr = head->next;
cout<<"deallocating node[0] "<<head<<endl;
delete head;
while(deletePtr !=head)
{
delete deletePtr;
cout<<"deallocating pointer "<<deletePtr<<endl;
deletePtr =deletePtr->next;
}
}
}
I've thought about having the build pointer equal to the head pointer in the (head == nullptr) conditional but I am running into even more problems. Here is the result of my valgrind
I am thinking this line is my primary problem
==30923== Address 0x5a225e0 is 0 bytes inside a block of size 24 free'd
So if I understand correctly, I am trying to deallocate memory that has already been freed? How can I fix this in my destructor? I've tried messing around with my addback function but I end up losing nodes or more memory leaks :(
delete deletePtr;
This destroys whatever object deletePtr was referencing. Immediately afterwards:
deletePtr =deletePtr->next;
This attempts to reference deletePtr->next. As we've just determined, deletePtr's object has been destroyed, and dereferencing it becomes undefined behavior.
This may or may not be the only problem with the shown code. It's not possible to conclusively determine this without a minimum reproducible example, there may be other problems; but this is definitely one of them.
To add to the answer given by #SamVarshavchick, the code is flawed here:
delete head;
while(deletePtr != head) // <-- You are comparing deletePtr to a deleted pointer?
Given all that, the Queue destructor seems to bes more complex than it should be. The following is what I would have expected:
Queue::~Queue()
{
QueueNode* deletePtr = head;
QueueNode* nextPtr = nullptr;
while ( deletePtr )
{
nextPtr = deletePtr->next;
delete deletePtr;
deletePtr = nextPtr;
}
}
Note that there is no need to explicitly check for head being null at the beginning of the function, as the while loop will only be entered on a non-null value.
I was the destructor! Here is how I solved it
Queue::~Queue()
{
int i=1;
if(head !=nullptr)//needed because i don't want to deallocate head if it was never called
{
QueueNode* stop = head->prev;
QueueNode* deletePtr = head->next;//tractor node[1]
QueueNode* temp = head;// cutter node[0]
if(deletePtr ==head)// N=1
{
delete deletePtr;
}
else if(deletePtr == stop)//N=2
{
deletePtr = stop;
delete deletePtr;
delete head;
}
else//N >=3
{
while (temp != stop)//last node
{
delete temp; //delete node[0]
cout<<"temp "<<temp<<endl;
cout<<"deletePtr "<<deletePtr<<endl;
temp= deletePtr;// node[1]
deletePtr = deletePtr->next;//node[2]
cout<<"temp "<<temp<<endl;
cout<<"deletePtr "<<deletePtr<<endl;
}
delete stop;
}//end N>=3
}
}
I have created a function called 'Insert' for inserting a new node in a Linked List. It takes the value and the head node for insertion.
When I manually add the nodes myself, the program runs as expected however
I get a segmentation fault when I use the function for adding a node.
I am able to make the function work with a couple of little tweaks but there's another catch, I lose the head node's property of just being a pointer, it now contains some garbage data in it which gets printed when I print the LinkedList.
The tweak I perform is:
Change Line 26 to: A->next = NULL;
Change Line 17 to: while(temp->next != NULL)
The 'segmentation fault' occurs at Line 20 (when the tweak is not done):
Line 20 -----> temp->next = addTo;
I've already tried passing the arguments by reference, using global variables for the head node and checking the logic of the function.
The logic works for manually adding a node.
I have attached the complete code below:
#include <bits/stdc++.h>
using namespace std;
struct ListNode {
int data;
ListNode *next;
};
void Insert(int x , ListNode* head)
{
ListNode* addTo = new ListNode();
addTo->data = x;
addTo->next = NULL;
ListNode* temp;
temp = head;
while(temp != NULL)
temp = temp->next;
temp->next = addTo;
}
int main()
{
ListNode* A;
A = NULL;
//Inserting A Node Manually
// ListNode* first = new ListNode();
// first->data = 9;
// first->next = NULL;
// while(A != NULL)
// A = A->next;
// A = first;
//Inserting using Insert function.
Insert(2,A);Insert(3,A);Insert(6,A);Insert(7,A);
//Printing
ListNode* temp = A;
while(temp != NULL)
{
cout << temp->data << " ";
temp = temp->next;
}
return 0;
}
I expected the node to be added to the list as the logic seems to be correct, however I am getting a segmentation fault.
Any help/insight into this would help a lot.
Thank You.
Problem 1:
while(temp != NULL)
temp = temp->next;
temp->next = addTo;
Guarantees that temp will be NULL when while(temp != NULL) exits. that means there is no temp to get the next from.
Rather than solve this here, I'm going to move on to problem 2 and kill two birds with one stone.
Problem 2:
void Insert(int x , ListNode* head)
leaves you with no way to update the caller if head is changed in the Insert function. You can change the object pointed at by head, but head itself is just a copy of an address. If you change this copy to point at another address, the caller does not know.
This means every time you call Insert(<number>, A);, A will always be NULL.
Solution:
Pass head into Insert by reference so that it can be updated.
void Insert(int x , ListNode*& head)
^ reference to pointer
head's job is to point at the first item in the list. This makes means it does the same thing as any next pointer: It points at the next item. The only difference is the name. We can get rid of this difference by adding an extra indirection, a pointer to head.
ListNode ** temp = &head;
Note that we cannot use a reference (ListNode *& temp) here because once you initialize a reference to refer to an object, it cannot be changed to refer to a different object. A pointer you can change, allowing us to iterate through the list and always point temp at the next next.
Now head or any next is simply temp. This makes head exactly the same as every other next variable and no special cases are required.
void Insert(int x , ListNode*& head)
{
ListNode* addTo = new ListNode();
addTo->data = x;
addTo->next = NULL; // consider making a smarter ListNode constructor that makes
// it impossible to forget to set next.
ListNode ** temp = &head; // we can now manipulate the address in head through temp
while(*temp != NULL) // note the dereference to get the pointed at pointer
// temp won't be null, but it could be pointing at a pointer
// that is null, and that is the end of the list
{ // I always use the optional braces. They prevent confusion.
temp = &(*temp)->next; //get pointer to the next next pointer
// we can now manipulate it exactly the same as head.
}
// we exit the loop with a pointer to the next pointer that we wish to point at
// the new list item regardless of whether it's head or the next of some later
// link in the list
*temp = addTo; // update the pointed at pointer with the new Node.
}
The Community Addition the first answer of How do I properly delete nodes of linked list in C++ demonstrates how to use the same pointer-to-pointer trick to make removing nodes easy.
I am writing a function that inserts a new element in a singly linked linked list.
The function accepts the *head to the list, the data to be put in the new element and the position in the l-list should be inserted at.
This is the link to the programming exercise in case I've not been clear enough.
The following code works perfectly -
/*
Insert Node at a given position in a linked list
head can be NULL
First element in the linked list is at position 0
Node is defined as
struct Node
{
int data;
struct Node *next;
}
*/
Node* InsertNth(Node *head, int data, int position)
{
//Takes head of list as input and returns it after inserting a new element
//with `data` at `position` in the l-list
Node *temp = new Node;
temp = head;
Node *newn = new Node;
newn->data = data;
if(position == 0){
newn->next = head;
return newn;
}
while(--position){
temp = temp->next;
}
newn->next = temp->next;
temp->next = newn;
return head;
}
However, I don't understand why I had to use Node *temp = new Node; temp = head;.
Why doesn't simply using Node *temp = head; work?
I'm only using the temp pointer to traverse the l-list so that I can preserve the location of the head when I return the final list.
Edit - I understand that Node *temp = head; is the way to go. This was how I originally programmed it too, but I forgot to mention that this is what's giving me a segfault. When I change it to Node *temp = new Node; temp = head; it works for all the testcases(including the ones where head is NULL).
Why must this seemingly absurd mem allocation seem to make it work is what I want to know.
The code you post above leaks.
Node *temp = new Node; temp = head;
This is no good.
Node *temp = head;
This is better.
There are other problems in your code; but your analysis that it was silly to new then immediately reassign the pointer is correct. Well spotted.
The answers that were posted before mine are both wrong, sure they Point out that your code leaks, but they didn´t check the rest of the code to see if it actually does what it is supposed to do. Your code is prone to Errors, because you don´t account for the head being NULL, which is clearly stated.
// Returns the new node inserted at the given Position inside the l-list
Node* InsertNth(Node *head, int data, int position)
{
Node *newn = new Node;
newn->data = data;
newn->next = 0;
// No head, return node right away
if(!head)
{
return newn;
}
// Exception - only case where head is not returned
else if(!position)
{
newn->next = head;
return newn;
}
// Create ´temp´ which is a pointer to the next node in the list
Node *temp = head;
// The function allows passing of a signed int, make sure we stay above 0
// as the previously while(--position) would go into an endless loop
// if a signed integer were passed on to the function
while(--position > 0)
{
// Just incase the input is bad, and position exceeds the size of the list
if(!temp->next)
{
break;
}
temp = temp->next;
}
// Now that we found the place in line, insert it between the temp and the next item
// in the list. Which may also be NULL
newn->next = temp->next;
temp->next = newn;
return head;
}
Now as you can see a few changes were made, the code earlier on didn´t account for head being NULL, and it didn´t properly set the member ´next´ to 0, which would lead to a Crash eventually if someone iterated over the supposedly null-terminated single-linked list.
Use of
Node *temp = new Node;
temp = head;
causes a memory leak. Memory allocated in the first line is lost. You need to use:
Node *temp = head;
I've checked the boards and could not find any help with this. I find it easy to implement recursive functions given base and general cases, but this doesn't work the way I do it. I'm supposed to iterate down a list until I reach the tail of a linked list. If the next node is NULL, then I have to store the value at the last node, remove that node, and return the value. So it's similar to a dequeue method, except it's performed recursively. What am I doing wrong?
int LinkedList::removeTailRec(Node *n)
{
// check for the base case(s)
if(n->next == NULL)
{
Node *tmp = new Node();
tmp = n;
int val = n->value;
tmp = NULL;
return val;
}
else
return removeTailRec(n->next);
// else call the recursive method
}
First, I recommend you use nullptr instead of NULL.
Then, onto your code. You're actually not removing anything from your list.
if(n->next == NULL)
{
Node *tmp = new Node();
^^^^^^^^^^
//Useless, and dangerous. This memory is never free'd
tmp = n;
int val = n->value;
tmp = NULL;
^^^^^^^^^^
//You just set a local variable to NULL, you're not deleting anything
return val;
}
If you want to remove the node, you'll have to keep a reference to the previous node (e.g. having a doubly linked list, that is, having a pointer to the next element and a pointer to the previous element in each node, or working on the previous node directly).
Set this previous node's next to nullptr, store the node's value and then delete the Node pointer.
One way to do this is to work with the pointer to the next node :
int LinkedList::removeTailRec(Node *n)
{
//EDIT: Adding a check for n validity
if(!n){
//Here, you should have a way of detecting
//a call to your method with a null pointer
return 0;
}
Node* nextNode = n->next;
// check for the base case(s)
if(nextNode->next == nullptr)
{
//Get the next node value
int val = nextNode->value;
//Set the current node next member to nullptr
n->next = nullptr;
//Free the last node
delete nextNode;
return val;
}
else{
return removeTailRec(n->next);
}
// else call the recursive method
}
You are storing the result but not deleting it from linked list. You can return result in another variable (pointer : result).
Node* getTail(Node *n,int *result){
//u can even free the memory
if(!n->next)
{
result=n->value;
return NULL;
}
n->next=getTail(n->next,result);
}
or you can do it other way
int getTail(Node *n)
{
if(!n) return 0;
if(n->next)
{
if(!n->next->next)
{
Node *frnode=n->next;
int result=n->next->value;
n->next=NULL;
delete frnode;
return result;
}
getTail(n->next);
}
You are not removing last node in your code, and you leak another (temporary) node here.
To remove last node you have to zero the link in the previous node.
Your code should look like
...
if (n == NULL || n->next == NULL)
throw std::out_of_range("node");
if(n->next->next == NULL)
{
int val = n->next->value;
delete n->next;
n->next = NULL;
return val;
}
else ...
Be aware of the fact that c++ is not a functional language and has no optimizations for tail recursion, so in real application as your lists grow big enough you'll eventually have failure with stack overflow =) use Haskell or Erlang for this style of programming, in c++ use for or while.
You should set the Node n's previous Node's next field to NULL when n is the tail Node.
I have problems understanding why when I create two or more nodes (as shown below), the function void del_end()will only delete the char name[20] and not the whole node . How do I fix this problem without memory leak?
#include <iostream>
using namespace std;
struct node
{
char name[20];
char profession[20];
int age;
node *nxt;
node *prv;
};
node *start_ptr = NULL;
void del_end()
{
node *temp, *temp2;
temp = start_ptr;
if (start_ptr == NULL)
cout << "Can't delete: there are no nodes" << endl;
else if (start_ptr != NULL && start_ptr->nxt == NULL)
{start_ptr=NULL;}
else
{
while (temp->nxt != NULL)
{
temp = temp->nxt;
}
temp2=temp->prv;
delete temp;
temp->nxt= NULL;
}
}
Your code has some problems, the worst being here:
temp2=temp->prv;
delete temp2;
temp->nxt= NULL;
You're deleting the next-to-last node, leaving any pointers to it dangling, and losing the last node.
But if you post more of the real code, we can tell you more.
EDIT:
Here's a slightly cleaned-up version of del_end (and there's still plenty of room for improvement).
void del_end()
{
if (start_ptr == NULL)
{
cout << "Can't delete: there are no nodes" << endl;
return;
}
if (start_ptr->nxt == NULL)
{
delete start_ptr;
start_ptr = NULL;
return;
}
node *nextToLast = start_ptr;
node *last = start_ptr->nxt;
while(last->nxt != NULL)
{
nextToLast = last;
last = last->nxt;
}
delete last;
nextToLast->nxt = NULL;
return;
}
Note that this does not assume that the prev links are correct, which seems prudent here.
delete temp2;
WILL delete whole node.
The problem is that you appear to be trying to delete the last node, but you are in fact deleting the one right before it.
while (temp->nxt != NULL)
{
temp = temp->nxt;
}
temp2=temp->prv;
delete temp2;
temp->nxt= NULL;
This is your issue. Change it to this:
while (temp->nxt != NULL)
{
temp = temp->nxt;
}
temp2=temp->prv;
delete temp;
temp2->nxt= NULL;
And I believe it will work as intended. This saves off the next to last node, deletes the end and then sets the next to last node's nxt pointer to null, making it the last one.
If you're concerned about memory, I highly recommend learning to work with Valgrind http://valgrind.org/. It is a great tool. Valgrind divides memory leaks into 3 categories:
"definitely lost" - pointer to dynamically allocated memory is lost and there is no way to recover it
"possibly lost" - pointer to the dynamically allocated memory is pointing to the interior of a block and may be unrelated
"still reachable" - pointer to the dynamically allocated memory still exists, but the memory was never freed at the end of the programs execution
Running Valgrind is also very simple. Here's a link to the User Manual http://valgrind.org/docs/manual/manual.html. Some useful flags when running valgrind:
--leak-check=<no|summary|yes|full>
--show-reachable=<no|yes>
Now, the way I would remove a node in a doubly-linked list is:
// if the node to be removed is the head node
if (nodeToRemove->prev == NULL) {
// reassign the head node pointer
start_ptr = nodeToRemove->next;
} else {
// correct previous node pointer
nodeToRemove->prev->next = nodeToRemove->next;
}
// if the node to be removed node is the tail node
if (nodeToRemove->next == NULL) {
// reassign the tail node pointer
end_ptr = nodeToRemove->prev;
} else {
// correct next node pointer
nodeToRemove->next->prev = nodeToRemove->prev;
}
// deallocate memory
delete(nodeToRemove);
nodeToRemove = NULL;
Just declare node *end_ptr = NULL; after you declare start_ptr and when you append a node to the list, make sure the end_ptr is always pointing to the end of the list... and if you're adding to the end of the list, its easy... just point the end_ptr to the node being added.
You might as well keep a tail pointer, if you always have to delete the last node. So once you have the node you want to delete, I just check if its the head/tail node, reassign the next/prev pointers, and free the memory.
Btw... I took this from my C implementation, so if the syntax is off I apologize... but the logic is there.
Hope that helps.