How can I get rid of this Heisenbug in my doubly-linked list data structure? - c++

I am implementing a modified doubly-linked data structure and while I was unit testing the functions, I encountered a Heisenbug that I can't get rid of.
Specific piece of code that I get the random segmentation fault is below:
case CURSOR_LIST_COPY_CONSTRUCTOR:
{
// Test to construct a cursor list by using the copy constructor.
if (verbose) {
os << "\nCURSOR_LIST_COPY_CONSTRUCTOR:" << std::endl <<
"Starting the construction operation:" <<
std::endl;
}
anil::cursor_list my_cursor_list;
my_cursor_list.append(1);
anil::cursor_list my_copied_cursor_list = my_cursor_list;
if (&my_copied_cursor_list == nullptr &&
my_copied_cursor_list.index() != -1 &&
my_copied_cursor_list.size() != 1 &&
my_copied_cursor_list.front_data() != 1 &&
my_copied_cursor_list.back_data() != 1) {
if (verbose) {
os << "Copy construction unsuccessful!" << std::endl;
}
return false;
} else {
if (verbose) {
os << "Copy construction successful!" << std::endl;
}
return true;
}
return false;
break;
}
Two runs out of ten give me a segmentation fault at my_copied_cursor_list.front_data() != 1. Initially I couldn't get to replicate this bug inside gdb but after using the command set disable-randomization off, which I learned from an answer to the question segfault only when NOT using debugger, inside gdb, I was able to replicate the same bug.
The source code for the front data is as following:
int anil::cursor_list::front_data() {
if (this != nullptr && this->is_empty() == false) {
return this->front->data;
}
}
I tried to put return this->front->data; into an if statement in order to test if this->front was somehow nullptr; however, even when I did it, it still somehow caused a segmentation fault. This was what I tried and failed:
int anil::cursor_list::front_data() {
if (this != nullptr && this->is_empty() == false) {
if (this->front != nullptr) {
return this->front->data;
}
}
}
The code for the append() function is as following:
void anil::cursor_list::append(int new_data) {
if (this != nullptr) {
cursor_list_node* new_node = new cursor_list_node;
new_node->data = new_data;
new_node->next = nullptr;
new_node->previous = this->back;
if (this->is_empty() == false) {
this->back->next = new_node;
} else {
this->front = new_node;
}
this->back = new_node;
++this->m_size;
}
}
The code for the class declaration is as following:
#ifndef ANIL_CURSOR_LIST_H
#define ANIL_CURSOR_LIST_H
#include <cstddef>
#include <iostream>
namespace anil {
class cursor_list_node {
private:
int data;
cursor_list_node* next;
cursor_list_node* previous;
friend class cursor_list;
};
class cursor_list {
private:
// Data:
int m_index;
int m_size;
cursor_list_node* front;
cursor_list_node* back;
cursor_list_node* cursor;
// Functions:
void delete_list();
public:
cursor_list() : m_index(-1), m_size(0), front(nullptr), back(nullptr),
cursor(nullptr) {}
cursor_list(cursor_list& copied_list);
bool is_empty();
int size();
int index();
int front_data();
int back_data();
int cursor_data();
bool operator==(cursor_list& rhs); // rhs = right hand side
cursor_list& operator= (cursor_list& rhs); // rhs = right hand side
friend std::ostream& operator<<(std::ostream& out, const cursor_list& rhs); // rhs = right hand side
void clear();
void move_cursor_front();
void move_cursor_back();
void move_cursor_prev();
void move_cursor_next();
void prepend(int new_data);
void append(int new_data);
void insert_before_cursor(int new_data);
void insert_after_cursor(int new_data);
void delete_front();
void delete_back();
void delete_cursor();
~cursor_list();
};
}
#endif /* ANIL_CURSOR_LIST_H */
The segmentation fault message inside gdb is as following:
Program received signal SIGSEGV, Segmentation fault.
0x00000000004018c8 in anil::cursor_list::front_data (this=0x7ffe055100c0)
at anil_cursor_list.cpp:82
82 return this->front->data;
My operating system is as following:
Distributor ID: Ubuntu
Description: Ubuntu 16.04.6 LTS
Release: 16.04
Codename: xenial
Lastly, the entire source code for the data structure can be found at https://github.com/Karipso/Karipso-Public/blob/master/cpp_practice_codes/data_structures/cursor_list/anil_cursor_list.cpp and https://github.com/Karipso/Karipso-Public/blob/master/cpp_practice_codes/data_structures/cursor_list/anil_cursor_list.h.
Similarly, the source code for the unit tests can be found at: https://github.com/Karipso/Karipso-Public/blob/master/cpp_practice_codes/data_structures/cursor_list/main.cpp
Finally, the makefile is located at https://github.com/Karipso/Karipso-Public/blob/master/cpp_practice_codes/data_structures/cursor_list/Makefile
Can someone help me determine what this bug is caused by?
Update: I tried to run the program using gdb line by line using next and step and printed this and this->front before the seg fault. This is the result:
Breakpoint 1, run_tests (os=..., bst_test=1, verbose=false) at main.cpp:121
121 anil::cursor_list my_copied_cursor_list = my_cursor_list;
(gdb) next
138 if (my_copied_cursor_list.index() != -1) {}
(gdb)
139 if (my_copied_cursor_list.size() != 1) {}
(gdb)
140 if (my_copied_cursor_list.front_data() != 1) {}
(gdb) step
anil::cursor_list::front_data (this=0x7ffc683c4500) at anil_cursor_list.cpp:79
79 if (this != nullptr && this->is_empty() == false) {
(gdb) next
80 return this->front->data;
(gdb) print this
$7 = (anil::cursor_list * const) 0x7ffc683c4500
(gdb) print this->front
$8 = (anil::cursor_list_node *) 0xe92cb976bc5a2900
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401956 in anil::cursor_list::front_data (this=0x7ffc683c4500)
at anil_cursor_list.cpp:80
80 return this->front->data;
Update-2: I think, I was able to get rid of the Heisenbug. After adding the initializations for all member variables of the cursor_list class, the program stopped giving seg-faults. So, I updated the copy constructor to be as following:
this->m_index = -1;
this->m_size = 0;
this->front = nullptr;
this->back = nullptr;
this->cursor = nullptr;
if (copied_cursor_list.is_empty() == false) {
for (cursor_list_node* it = copied_cursor_list.front; it != nullptr;
it = it->next) {
this->append(it->data);
}
}
I will do some experimentation to figure out why append() function wasn't properly initializing the front pointer and update the post if I can figure out the exact cascade of events that lead to that faulty initialization or lack of initialization.

if (this != nullptr)
You aren't allowed to check that. Compilers can and will change this to if(true).
Calling nullptr->blah() is already UB, so within blah the compiler is free to assume nullptr is not null.
int anil::cursor_list::front_data
Not all return paths return a value. Falling off the end is UB.
The compiler is free to remove your if checks guarding the return path, because segfaulting is allowed in UB.
Fixing this might not fix your crash, but with UB in your code, compilers are permitted to generate code that crashes in nonsense ways.
To fix your problem, you should make a much much simpler linked list with fewer operations.
Then unit test that.
Then wrap that simpler linked list in your rich API. And unit test your rich API.
Then when you have a crash in your rich API unit tests, you can write up a minimal unit test in your simple API.

Related

Error when trying to deallocate binary search tree (only with primitive data types)

I am trying to write a BST which performs common operations, Insertion, Deletion, Search, and traversals.
The problem I have found is that the created tree works perfectly when it is assigned to use non-primitive data types, For exmple: user defined struct, classes and C++ stl classes (as std::string)
But when I use int, double_t, int64_t and so, it throws an exception.
~BinaryNode()
{
if (left_ != NULL)
Deleting(left_);
if (right_ != NULL)
Deleting(right_);
//The exception occurs here when the primitive data type
//is attempted to be deallocated
}
//Using preorder to delete this tree recursively
void Deleting(BinaryNode<T> *node)
{
if (node == NULL) return;
if (node->left_ != NULL)
delete node->left_;
if (node->right_ != NULL)
delete node->right_;
node->left_ = node->right_ = NULL;
delete node;
}
I had made many tests, checking the integrity between nodes and that's not the problem. Because, As I said, the problem is when the destructor reaches its end.
When I used the debugger I saw that a std::string calls its destructor and is correctly deallocated. But with primitive types it throws an exception of type:
Exception has occurred.
Trace/breakpoint trap
Thanks Beforehand.
Edit:
I am testing it as:
int main()
{
BinarySearchTree<uint64_t> bst(
[](const uint64_t &t1, const uint64_t &t2) -> int8_t {
if (t1 == t2) return 0;
if (t1 > t2) return 1;
else return -1;
});
//performance slow down because there are (20000^2)/2 operations
//to do. This is not a Self Balanced tree, so it is expected
//it ends having worst performance than a singly linked list
for (size_t i = 0; i <= 200; i++)
// i <= 20000 causes error
// i <= 200 or i <= 2000 works perfect
{
bst.Insert(i);
std::cout << "i:" << i << std::endl;
}
std::cout << bst.Size() << std::endl;
bst.Clear();//this generates the error. [When i <= 20000]
}
Definition of Insert
virtual bool Insert(const T &t)
{
if (this->root_ == NULL)
{
this->root_ = new BinaryNode<T>(t);
return true;
}
return Insert(t, this->root_);
}
virtual bool Insert(const T& t, BinaryNode<T> *node)
{
if (this->comparing_(t, node->t_) > 0)
if (node->left_ == NULL)
{
node->left_ = new BinaryNode<T>(t);
this->size_++;
}
else Insert(t, node->left_);
else if (this->comparing_(t, node->t_) < 0)
if (node->right_ == NULL)
{
node->right_ = new BinaryNode<T>(t);
this->size_++;
}
else Insert(t, node->right_);
else return false; //if there is such element
return true; //if it was successfully inserted
}
This is the definition of Clear
virtual void Clear()
{
delete root_;
root_ = NULL;
}
Note: comparing_ is a lambda.
I think that this is a recursion error.
I found that the error was produced due to a stack overflow error. Why? Because the stack of calls was adding as many calls as nodes were at an unbalanced side of the tree.
Due the nature of an unbalanced binary search tree this happens, there is a limit for recursion. I tried adding 20.000 and 200.000 elements into a AVLTree and it worked fine due the self balancing procedure, because it ensures the tree to have a height O(ln(n))
As I can't see the node class so according to my understanding:
The data types like int can't be assigned NULL.

Question about segmentation fault on std::stack push

I am working on a octree traversal algorithm. The current implementation uses a std::queue for such purpose, working flawlessly. However, I would like to use for such traversal a std::stack, as a depth first search will give better performance, avoiding testing non needed nodes.
However, when changing from one structure to another, I start getting segmentation faults on the push() function. Here is the stack report from gdb:
0x00005555555ae28d in __gnu_cxx::new_allocator<voxelizer::Node*>::construct<voxelizer::Node*, voxelizer::Node* const&> (this=0x7fffffffd7f0, __p=0x5555559abde8, __args#0=<error reading variable>)
at /usr/include/c++/7/ext/new_allocator.h:136
136 { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
(gdb) up
#1 0x00005555555acd1c in std::allocator_traits<std::allocator<voxelizer::Node*> >::construct<voxelizer::Node*, voxelizer::Node* const&> (__a=..., __p=0x5555559abde8, __args#0=<error reading variable>)
at /usr/include/c++/7/bits/alloc_traits.h:475
475 { __a.construct(__p, std::forward<_Args>(__args)...); }
(gdb) up
#2 0x00005555555ab63e in std::deque<voxelizer::Node*, std::allocator<voxelizer::Node*> >::push_back (this=0x7fffffffd7f0, __x=<error reading variable>) at /usr/include/c++/7/bits/stl_deque.h:1547
1547 _Alloc_traits::construct(this->_M_impl,
(gdb) up
#3 0x00005555555aa29f in std::stack<voxelizer::Node*, std::deque<voxelizer::Node*, std::allocator<voxelizer::Node*> > >::push (this=0x7fffffffd7f0, __x=<error reading variable>)
at /usr/include/c++/7/bits/stl_stack.h:226
226 { c.push_back(__x); }
I could not get my head around why, so I created a minimal, verifiable example where I could get rid of possible errors caused by any other part of the system. I reproduced the cotree Node structure, and created a small tree to traverse:
#include <iostream>
#include <stack>
#include <utility>
using namespace std;
// ==============================================================
class TestClass
{
public:
// Default constructor
TestClass()
: d(0)
, children(nullptr)
{
}
// Depth based constructor
TestClass(int d_)
: d(d_)
, children(nullptr)
{
if(d > 0)
{
children = new TestClass*[8];
for(int i = 0; i < 8; i++)
{
children[i] = new TestClass(d - 1);
}
}
}
// Copy constructor
TestClass(const TestClass & other_)
: d(0)
, children(nullptr)
{
_copy(other_);
}
// Move constructor
TestClass(TestClass && other_)
: d(0)
, children(nullptr)
{
_move(std::move(other_));
}
// Destructor
~TestClass()
{
_clearChildren();
}
// Copy assignment operator
TestClass & operator= (const TestClass & other_)
{
_copy(other_);
return *this;
}
// Move assignment operator
TestClass & operator= (TestClass && other_)
{
_move(std::move(other_));
return *this;
}
int depth()
{
return d;
}
TestClass ** getChildren()
{
return children;
}
private:
void _clearChildren()
{
if(children != nullptr)
{
for(int i = 0; i < 8; i++)
{
delete children[i];
}
delete[] children;
children = nullptr;
}
}
void _copy(const TestClass & other_)
{
d = other_.d;
_clearChildren();
if(other_.children != nullptr)
{
children = new TestClass*[8];
for(int i = 0; i < 8; i++)
{
children[i] = new TestClass(*(other_.children[i]));
}
}
}
void _move(TestClass && other_)
{
d = other_.d;
_clearChildren();
children = std::move(other_.children);
}
private:
int d;
TestClass ** children;
};
// ==============================================================
typedef TestClass * TestClassPtr;
// ==============================================================
int main(int argc, char ** argv)
{
TestClassPtr test = new TestClass(5);
stack<TestClassPtr> s;
s.push(test);
while(!s.empty())
{
TestClassPtr & next = s.top();
s.pop();
cout << "On depth " << next->depth() << endl;
if(next->getChildren() != nullptr)
{
std::cout << "Adding children" << std::endl;
for(int i = 0; i < 8; i++)
{
if(next->getChildren()[i]->getChildren() != nullptr)
{
s.push(next->getChildren()[i]);
}
}
}
}
cout << "Done" << endl;
return 0;
}
By running it I was able to reproduce the problem, in the push() method as well:
On depth 5
Adding children
On depth 3
Adding children
On depth 1
Adding children
Segmentation fault
So I went on to revising the documentation. Note that I'm using C++11. The requirements for a default std::stack can be inherited from the requirements of using a std::deque, as it is the default container used. Since C++11, the main requirement is to be a complete type and Erasable I made sure the destructor was accessible. Also, for the sake of safe proofing, I implemented a default constructor, copy constructor, move constructor, copy assignment, and move assignment.
So I believe my class is Erasable, but perhaps not complete. By modifying the traverse loop in the example and adding the "SAFE PROOF LINE" if:
if(next->getChildren() != nullptr)
{
std::cout << "Adding children" << std::endl;
for(int i = 0; i < 8; i++)
{
// SAFE PROOF LINE
if(next->getChildren()[i]->getChildren() != nullptr)
{
s.push(next->getChildren()[i]);
}
}
}
I was able to get rid of the segmentation fault. The nodes which this line discard are the leaf nodes, which does not have children and, thus, their children variable is a nullptr.
My questions:
Does this means a nullptr pointer makes a type incomplete?
The point of using this raw memory double pointer is to safe as much
memory as possible, is there anyway I can make it work without having
to substitute it for a stack array or a std::vector?
Thanks.
Seems to go wrong right from the start
while(!s.empty())
{
TestClassPtr & next = s.top();
s.pop();
next is a reference to the object on the top of the stack, but the very next line removes that object, so the reference becomes invalid.
Simple answer is to not use a reference and just copy the top of the stack.
while(!s.empty())
{
TestClassPtr next = s.top();
s.pop();
gdb says that the push argument is invalid:
push (this=0x7fffffffd7f0, __x=<error reading variable>)

How to delete a node pointer

This is schoolwork. I haven't seen anything that really answers this directly, so I'm having a hard time fixing it. I have to create a linked node implementation of a max heap and I'm having difficulty with the deletion of a node after removing a value.
My Code:
template<class ItemType>
BinaryHeapNode<ItemType>* LinkedMaxHeap<ItemType>::getLastNode()
{
BinaryHeapNode<ItemType>* lastNode = rootPtr->getRightSiblingPtr();
BinaryHeapNode<ItemType>* prevLastNode = rootPtr;
while(lastNode != nullptr)
{
prevLastNode = lastNode;
lastNode = lastNode->getRightSiblingPtr();
}
return prevLastNode;
}
template<class ItemType>
bool LinkedMaxHeap<ItemType>::removeValue(ItemType value)
{
BinaryHeapNode<ItemType>* tempNode = rootPtr;
for (int i = 0; i < itemCount; i++)
{
if(tempNode->getItem() == value)
{
tempNode->setItem(getLastNode()->getItem());//set item
delete getLastNode(); //delete last node
getLastNode() = nullptr; //set last node null
getLastNode()->setRightSiblingPtr(nullptr); //last node should be different
itemCount--; //set it's sibling to null
heapRebuild(tempNode);
}
tempNode = tempNode->getRightSiblingPtr();
}
return true;
}
My issue is with getLastNode() = nullptr. VS is telling me that getLastNode() isn't an lvalue. That doesn't make sense to me because getLastNode is returning a pointer to a BinaryHeapNode, but it can't set that pointer to nullptr?
I thought this might be a problem with my logic of pointers (which is shaky at best) so I thought changing getLastNode() to return just a node would help. That did not. So I tried messing with the & operator and returning an address of the last node. Needless to say I haven't found the solution yet. If anyone can provide some sort of direction it would be appreciated. I'm just not entirely sure why it doesn't work.
EDIT:
Edited the code based on what arynaq mentioned. The errors went away, but now I have a bunch of linker errors I have to fix before I can test it. Will this code do what I want? I feel like it is just going to delete nodeToDelete and not get rid of the node in the heap.
template<class ItemType>
bool LinkedMaxHeap<ItemType>::removeValue(ItemType value)
{
BinaryHeapNode<ItemType>* tempNode = rootPtr;
BinaryHeapNode<ItemType>* nodeToDelete = getLastNode();
for (int i = 0; i < itemCount; i++)
{
if(tempNode->getItem() == value)
{
tempNode->setItem(nodeToDelete->getItem());
delete &nodeToDelete;
nodeToDelete = nullptr;
getLastNode()->setRightSiblingPtr(nullptr);
itemCount--;
heapRebuild(tempNode);
}
tempNode = tempNode->getRightSiblingPtr();
}
return true;
}
Ok, I'll try to help by explaining some things about pointers. Hopefully this will clarify some misconceptions and help you with your assignment.
When you get a copy of the pointer like so: mypointer* p = get_pointer(); and then you delete that, you are deleting the memory. But when you assign nullptr to this local variable, it wont affect the "source" of your pointer.
Here is a detailed example, showing where things can go wrong. If you never set v[0] to nullptr.
#include <iostream>
#include <vector>
struct Object {
~Object() {
std::cout << "Object destructor." << std::endl;
}
int val = 42;
};
struct OtherObj {
int val = 322;
};
void print_vec(const std::vector<Object*>& v) {
for (const auto& x : v) {
std::cout << x << std::endl;
}
}
int main(int, char**) {
// Init vector and print addresses.
std::vector<Object*> v(2);
print_vec(v);
// Init objects in vector and printit.
for (auto& x : v) {
x = new Object();
}
print_vec(v);
// Get a copy of a pointer and delete that. All good so far.
Object* pointer_to_delete = v[0];
delete pointer_to_delete;
// Assign nullptr to the temporary local pointer.
// Does nothing to the pointer in the vector.
pointer_to_delete = nullptr;
// Print the vector to prove it.
print_vec(v);
// On a non debug build, the memory will still have the last value.
// Careful! Cause of headaches here. This should be set to nullptr.
std::cout << v[0]->val << std::endl; // "No problem", certainly not nullptr.
// Now that we allocate a new object, v[0] will be overwritten.
OtherObj* bad_bad_boy = new OtherObj();
// Print the address of the new object, to show it was created at
// the old v[0] address.
std::cout << bad_bad_boy << std::endl;
// Bad things ensue...
std::cout << v[0]->val << std::endl;
return 0;
}
The output on clang is :
0x0
0x0
0x7ffa21c026c0
0x7ffa21c026d0
Object destructor.
0x7ffa21c026c0
0x7ffa21c026d0
42
0x7ffa21c026c0
322
As you can see, setting the local pointer to nullptr is not enough! I hope this clears up some things for you :)
Online version

Code exhibiting different behaviour on different platforms, requesting explanation

When trying to answer a question on stackexchange, I tried to review this piece of code:
#include <iostream>
using namespace std;
struct Node {
int key;
Node *leftnode;
Node *rightnode;
string value;
Node(int tkey, const std::string& tvalue) : leftnode(nullptr), rightnode(nullptr), key(tkey), value(tvalue) {}
};
Node root_node(1, "Node #1"); // Binary search tree
string inline query_bst(const int key) {
Node *cur_node = &root_node;
while (cur_node != NULL) {
if (key == cur_node->key) {
return cur_node->value;
}
if (key < cur_node->key) { /* value already returned, no need for else */
cur_node = cur_node->leftnode;
} else {
cur_node = cur_node->rightnode;
}
}
return ""; // Return empty string if not found
}
void inline insert_bst(int key, string value) {
Node *cur_node;
Node *next_node = &root_node;
// Search through bst for key
while (next_node != NULL) {
cur_node = next_node;
if (key < cur_node->key) {
next_node = cur_node->leftnode;
} else {
next_node = cur_node->rightnode;
}
}
Node new_node(key, value);
next_node = &new_node;
if (key < cur_node->key) {
cur_node->leftnode = next_node;
} else {
cur_node->rightnode = next_node;
}
}
int main() {
root_node.key = 1;
insert_bst(2, "Node #3");
insert_bst(3, "Node #4");
cout << query_bst(3) << '\n' << query_bst(4);
}
For me, this program compiles, but crashes. I searched for the cause of this, and deduced (hopefully correctly) that in function "insert_bst()" a variable named "new_node" is created, and later a pointer is assigned this variable's address. However, the new_node var has an automatic duration, thus it is destroyed at the end of the function's execution. Therefore, during the second call of insert_bst(), when the program tries to access the key/value of the inserted node, trash values are retrived (this I confirmed using a debugger), which ruins the program.
Then why would this piece of code work properly on some other platform?
My tests were done on Windows 7 x64, on Code::Blocks 16.01 and CLion, using g++.
The platform on which the code works: Mac OS X Yosemite Clion, also g++
As you deduced correctly the function is creating a local object and adding that to the BST. When the local object is destroyed when the function returns we now have a dangling pointer and using it is undefined behavior.
Since it is undefined behavior that means that the behavior of the program is undefined. It may run, crash, become self aware and name itself Skynet or anything in between.
As MikeCAT pointed out we can get rid of this undefined behavior by using new to make the node persistent and that gets rid of the dangling pointer issue.

C++ vector and segmentation faults

I am working on a simple mathematical parser. Something that just reads number = 1 + 2;
I have a vector containing these tokens. They store a type and string value of the character. I am trying to step through the vector to build an AST of these tokens, and I keep getting segmentation faults, even when I am under the impression my code should prevent this from happening.
Here is the bit of code that builds the AST:
struct ASTGen
{
const vector<Token> &Tokens;
unsigned int size,
pointer;
ASTGen(const vector<Token> &t) : Tokens(t), pointer(0)
{
size = Tokens.size() - 1;
}
unsigned int next()
{
return pointer + 1;
}
Node* Statement()
{
if(next() <= size)
{
switch(Tokens[next()].type)
{
case EQUALS
:
Node* n = Assignment_Expr();
return n;
}
}
advance();
}
void advance()
{
if(next() <= size) ++pointer;
}
Node* Assignment_Expr()
{
Node* lnode = new Node(Tokens[pointer], NULL, NULL);
advance();
Node* n = new Node(Tokens[pointer], lnode, Expression());
return n;
}
Node* Expression()
{
if(next() <= size)
{
advance();
if(Tokens[next()].type == SEMICOLON)
{
Node* n = new Node(Tokens[pointer], NULL, NULL);
return n;
}
if(Tokens[next()].type == PLUS)
{
Node* lnode = new Node(Tokens[pointer], NULL, NULL);
advance();
Node* n = new Node(Tokens[pointer], lnode, Expression());
return n;
}
}
}
};
...
ASTGen AST(Tokens);
Node* Tree = AST.Statement();
cout << Tree->Right->Data.svalue << endl;
I can access Tree->Data.svalue and get the = Node's token info, so I know that node is getting spawned, and I can also get Tree->Left->Data.svalue and get the variable to the left of the =
I have re-written it many times trying out different methods for stepping through the vector, but I always get a segmentation fault when I try to access the = right node (which should be the + node)
Any help would be greatly appreciated.
There's plenty more code that we haven't seen, so I can't tell you precisely what's going on, but I see a few things that are reasons for concern. One is that the Statement() method doesn't always return a value. If the first if test doesn't pass, then we call advance() and fall off the bottom of the routine without an explicit return. The caller will try to get the return value of the function but it'll get garbage. This could lead to all sorts of problems, including things like double free() calls, etc, which can easily cause segfaults.
Expression() has the same problem.