Stack overflow with unique_ptr linked list [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I've converted the following linked list struct
struct node {
node* next;
int v;
};
into a c++11 version - that is not using the pointers.
struct node {
unique_ptr<node> next;
int v;
};
Adding, removing elements and traversing works fine, however when I insert roughly 1mil elements, I get a stack overflow when when the destructor of the head node is called.
I'm not sure what I'm doing wrong.
{
node n;
... add 10mill elements
} <-- crash here

As explained in the other answers, you segfault because of the recursive implicit destructor. It is possible to fix this without resorting to raw pointers, having to trust the compiler or writing a custom allocator:
~node() {
for (std::unique_ptr<node> current = std::move(next);
current;
current = std::move(current->next));
}
Here you iteratively go through the chain of pointers. This will, one at a time, unchain one pointer and change ownership std::move(current->next) to current. At the same time the previous unchained pointer, owned by current, will be released while being overwritten by the move assignment.
You may find the explicit variant more straightforward:
current.reset(current->next.release()));
Is effectively the same as:
current = std::move(current->next));
I prefer the move version, because it does at no time leave you with a raw pointer. But in that case it does not make a difference.

You are making nothing wrong here.
When you create your list of 10 millions elements, allocation each node with make_unique everything is fine (Of course the data is not on the stack, except perhaps the first node !).
The problem is when you you get rid of the head of your list: unique_ptr will take care of the deleting the next node that it owns, which also contains a unique_ptr that will take care of deleting the next node... etc...
So that in the end the 10 millions elements get deleted recursively, each recursive call taking some space on the stack.

By default std::unique_ptr calls the operator function of structure std::default_delete that just executes operator delete.
So each operator function of the structure std::default_delete recursively calls itself for data member next of structure node.
As result you get the stack overflow.
You would get the same result if you used ordinary pointers instead of the pointers of type std::unique_ptr but added a destructor to the structure node the following way
struct node {
node* next;
int v;
~node() { delete next; }
};
Or even like
struct node {
node* next;
int v;
~node() { if ( next ) delete next; }
};
for a list with a big number of nodes because the destructor will be called recursively

Because when you destroy the head node element, it calls destructor oа unique_ptr, which destroys the second element that calls destructor of the 3rd element which calls ... etс 1mil times.
So, you have 1 mil nested funtion calls (of destructors). Each function call takes memory in stack at least to store return address (and parameters and local variables as well, if needed). Naturally, stack can not provide such amount of memory. You should redesing code to resolve it. For instance, rewrite destructor of Node class so that it finds the last list element and then destroys it and all other nodes from the end in a cicle, not recursievely.

Related

Can we implement a link list without using the head pointer means by using a simple variable of the head instead of the pointer of the head?

Can we implement a link list without using the head pointer means by using a simple variable of the head instead of the pointer of the head ?
Yes. If you are implementing a circular linked list with a sentinel node, the sentinel node can be the simple variable that also serves as the head.
Alternatively, you could use a std::optional instance to serve as the head.
In specific cases you could, but in general not. And why would you want to? Here are some reasons, I could think of now. Take for example this code:
template<class T>
class Node
{
private:
T value;
Node<T> *next;
};
class MyLinkedList
{
private:
bool isEmpty; // indicates wether the list is empty or not
Node head; // Head as member
};
But there are several major flaws with this code:
You would always need to care about isEmpty when adding or deleting, or doing anything with the list
You can't initialize head if T has no default constructor
When deleting the last element you have to call the destructor of object that technically remains in scope.
When deleting the last element and then deleting the empty list the destructor of Node::value will be called twice
Don't know if those are all reasons, but I think, just #2 is a big enough problem to not consider this.
Of course you could use std::optional, but that's just a pointer with a wrapper. which even works, without a default constructor, so could be an alternative. Alltough it would be used in the same way as a (smart) pointer, so it's not "a simple variable of the head".

May I know why the parameter to this function isn't of int type? I don't understand the whole concept of the pointer here [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am learning data structures and algorithms and came across Linked list, which uses pointers. However, I don't understand how they are being used in the codes below. Can anyone guide me on this?
int Linkedlist :: addatEnd(node* n){
if(head == NULL){
head = n;
n->next = NULL;
}else{
node* n2 = getlastnode();
n2->next = n;
}
}
Shouldn't the parameter to the function addatEnd() be of int type? But if it really is a pointer, does it mean it will have to point to a certain memory address?
Shouldn't the parameter to the function addatEnd() be of int type?
No.
A linked list consists of nodes. Each node will indirectly link (i.e. refer, i.e. point) to the next node, until the last node which terminates the list. An integer cannot indirectly point to the next node, so nodes of a linked list cannot be an integers.
But if it really is a pointer, does it mean it will have to point to a certain memory address?
Yes. It will have to point to some node instance (because the function indirects through the pointer). That instance will be stored in some memory address.
P.S. The function is broken.
It sets n->next to null only if it is the only element of the list. It fails to set n->next when there are other nodes.
Furthermore, it is declared to return int but fails to do so. The behaviour of the program is undefined.
Shouldn't the parameter to the function addatEnd() be of int type?
It could be implemented in a different way, but as it currently is, it makes sense for it to take a node* instead.
But if it really is a pointer, does it mean it will have to point to a
certain memory address?
Generally, pointers that are passed as a parameter don't have to point to a valid memory address. They can also be null pointers, and the function could detect that and proceed accordingly.
In this case, a valid pointer has to be passed, however, because the function is trying to modify the object, at least if head == NULL:
n->next = NULL;
If n doesn't point to a valid node, this is undefined behavior and will likely crash the program, or do even worse.
node is a separate data structure. And the node* n means, whoever is calling addatEnd(node* n) is passing the node's address as an argument. Callee(the one calling your function) is doing :
Node n;//Instaniating node data structure
addatEnd(&node);//Passing the address of the node data structure to your function
The address of node is passed so that it does not make a separate copy the node structure and do operations on it, and whatever operations we do on the copy of the node will not be reflected on the original node.
We want to do operations on the original node data structure, hence passing the address of node so we can do operations on the memory location where the node was originally instantiated.

How to "completely" delete all nodes in a linked list?

I got curious about how pointers and deleting pointers worked in C++ so I set up an experiment. I made a very simple singly linked list and the following recursive function that deletes all nodes in a list:
void deleteList(Node *node) {
if (!node)
return;
deleteList(node->next);
cout << "deleting node " << node->data << endl;
delete node;
node = nullptr;
}
I suppose that this successfully deletes all nodes in a list. However, after calling this function in main, I check to see if the head node still exists:
List list;
// appending a bunch of numbers to the list...
list.deleteList(list.head);
if (list.head)
cout << true;
this will print 1 to the console, meaning that the head does indeed still exist. I would expect the head, and all other nodes after it, to be null and hence that the if condition fails, since setting pointers to null is the last thing I do in the recursive function. So why does the program report that the head still exists?
edit: changed List list(); to List list;
You freed the memory, but the assignment to nullptr only affected the copy of the pointer passed to the function, not the original pointer in the caller.
If you declared the function as receiving the pointer by reference:
void deleteList(Node *&node) {
then the assignment of node = nullptr; would affect the caller as well.
Mind you, since you tagged this C++11, it's usually much simpler to just define the linked list as a series of std::unique_ptr<Node>s in the forward direction (raw pointers in the reverse direction if it's bidirectional to avoid reference cycles), so you can avoid the need for a special deleter function, and just set the head pointer to nullptr and let C++ do the work of cascading the deletion.
Edit: There is a flaw to letting std::unique_ptr do the work; as pointed out in the comments, this means the list size is effectively limited by the stack, and too large lists will cause stack overflow when you delete them. So explicitly clearing one by one (the simplest approach being to implement popping properly, and have clearing simply be popping until head is converted to a nullptr by the popleft method) would be safer. I left the original suggestion in place for posterity, so this explanation makes sense.

C++ vector implementation - removing elements

I'm implementing a vector type. I'm not troubled by the algorithms or the data structure at all but I am unsure about a remove method. for instance:
bool Remove(Node* node)
{
/* rearrange all the links and extract the node */
delete node;
}
where node is a pointer to the current node that we are at. But if I delete node then how do I prevent this from happening:
Node* currentNode = MoveToRandNode();
Remove(currentNode);
cout << currentNode->value;
If currentNode were a pointer to a pointer it would be easier but...it's not.
You could add another level of abstraction to your iterator (which now is a raw pointer)
If you do not handle raw pointers, but create some sort of iterator class instead of a pointer, it is possible to invalidate the iterator, and thus failing controlled if anyone tries to access the iterator after it has been removed.
class Iterator {
Node operator*() {
if (node) return *node;
else throw Something();}
private:
Node* node;
}
Of course this wrapping of a pointer will come at a cost of some overhead (checking the pointer on each deref). So you will have to decide how safe you want to play. Either document as suggested by others or wrap for safety.
Step back first. You need to define who "owns" the memory pointed to by the vector. Is it the vector itself, or the code that uses the vector? Once you define this, the answer will be easy - either Remove() method should always delete it or never.
Note that you've just scratched the surface of the possible bugs and you answer to "who owns it" will help with other possible issues like:
If you copy a vector, do you need to copy the items within it, or just the pointers (e.g. do a shallow or deep copy
When you destroy a vector, should you destroy the items within it?
When you insert an item, should you make a copy of the item, or does the vector take ownership of it?
well, you cannot do that, but some modifications to your code can improve safety.
Add ref
bool Remove(Node*& node)
{
/* rearrange all the links and extract the node */
delete node;
node = nullptr;
}
check for nullptr
if(currentNode)
cout << currentNode->value;
probably you need to try std::shared_ptr
This is similar to "iterator invalidation". E.g., if you have a std::list l and a std::list::iterator it pointing into that list, and you call l.erase(it), then the iterator it is invalidated -- i.e., if you use it in any way then you get undefined behavior.
So following that example, you should include in your documentation of the Remove method something along the lines: "the pointer node is invalidated, and may not be used or dereferenced after this method returns."
(Of course, you could also just use std::list, and not bother to re-invent the wheel.)
For more info on iterator invalidation, see: http://www.angelikalanger.com/Conferences/Slides/CppInvalidIterators-DevConnections-2002.pdf
In addition what innochenti wrote.
I think you have to decide what is expected/desired behavior of cout << currentNode->value;:
Error - (as innochenti wrote node = nullptr)
Default Value - create node devault_value (which has some default value for its value), and after delete node; do node=default_value

Correct way of erasing a linked list

Suppose, I have a singly linked list and its basic building block is,
struct Node {
Data d;
Node *pNext;
// methods
~Node();
};
The head of the linked list is stored as,
Node *m_Head; // member of some class
When, I am done with the list, I will clean it by deleting each node as,
void Erase()
{
Node *pIter, *pTemp = m_Head;
while((pIter = pTemp) != 0)
{
pTemp = pIter->pNext;
delete pIter;
pIter = pTemp;
}
}
I thought, if I can simplify this. So I came up with an idea where I can clean this whole linked list with just a single instruction !
delete m_Head;
and destructor will look like:
Node::~Node() { delete this->pNext; }
Here my concern is, will it cause recursion (implicitly due to delete) ? If yes, then it's definitely a concern for bigger linked lists. Will compiler be able to help in any way for optimizing that ?
[Note: Not using any library facility like std::list or other.]
I think the question that you have to ask is, does each Node in the list own its pNext Node? If not, then it has no business deleting its pNext node in its destructor.
In most linked list implementations all the nodes are owned by the list, a node doesn't own all the nodes after it in the list. It makes more sense to keep the nodes as dumb (POD-structs) and let all of the logic reside in the list.
It's definitely a design "smell" that your node has a destructor but no copy constructor or copy assignment operator. I think this approach will cause more complexity when you come to code implementing insert, splice and erase single element functions as you will have to manually manage the pNext pointers in any case to avoid unintentional destruction of the entire tail of a list.
Of course: Only do this for learning purposes or when you are sure that your own List is really better for your use case
It depends. Possibly your compiler will detect a tail recursion and emit code that is conceptually equivalent to using a loop.
If not, then yes, it will recurse. Usually, some thousands of recursions should be possible on commodity boxes, if stack pressure is small (like in your case). However, there is no guarantee, and indeed, for really large lists, this can be a problem.
Also, I think that recursion is indeed not entirely appropriate for the concept of sibling nodes. A node hierarchy, like with quadtrees, cries for recursion, but I have a not so good time thinking in recursion (which forms a call hierarchy) when the list-concept is about sibling-nodes.
You may also consider the manual loop as a easy-to-achieve optimization over the recursion that will make your code more robust in a guaranteed way.
Btw, you could also should rip out the deletion of nodes into a holder class:
class List {
public:
~List() {
for-each-node
delete-node
}
private:
class Node {
Node *node_;
...
};
...
};
This is basically how the standard library's list is usually implemented. It makes the whole implementation easier to achieve and conceptually more correct (Nodes don't own their sibling logically)
Most compiler do tail call elimination in default setting. Some smarter one can convert non-tail calls to tail calls.
So, this method okay as long as you have some optimization turned on.
Raw pointers ? Manual calls to delete ?
Why not, simply:
struct Node {
Data d;
std::unique_ptr<Node> next;
};
Then you don't even have to worry about memory management at all, it's automatic!