Monday 25 April 2016

c++ - Trie double free or corruption in destructor



I am writing a Prefix Tree class in C++. Each node in the tree has an array of length 27 of Node*. These are to hold letters and spaces. I sanitize any input to the tree by casting all letters to lowercase and all symbols to spaces. In the destructor I call a function called clear which accepts a node (root node first). When writing unit tests, It only makes it past one test successfully, and the second test that calls a destructor on the Prefix class fails with a double free or corruption error. I have ran across this before and have been able to detect the problem and remedy it, however I cannot figure out what is causing this, here is some code:




Node struct:



const unsigned int B_FACTOR = 27;  // a..z plus space

struct Node_t {
bool word;
Node_t *links[B_FACTOR];
Node_t(): word(false) {}
};



Insert (public and private):



bool Prefix::insert(string thing) {
sanitize(thing);
if(!root) root = new Node_t();
if(thing == "") return true;
insertPrivate(thing, root);
return true;
}


void Prefix::insertPrivate(string input, Node_t *node) {
if(input == "") {
node->word = true;
return;
}
int idx = (int(input[0])-97) >= 0 ? (int(input[0])-97) : 26;
if(!node->links[idx]) node->links[idx] = new Node_t();
insertPrivate(input.substr(1, input.length()), node->links[idx]);
}



Destructor:



Prefix::~Prefix() {
clear(root);
}


Clear:




void Prefix::clear(Node_t *node) {
if(!node) return;
cout << "On Node " << endl;
for (int i = 0; i < 26; ++i) {
clear(node->links[i]);
}
delete node;
}



Unit Tests:



void testInsert0() {
cout << "testInsert0" << endl;
Prefix a;
a.insert("a");
TS_ASSERT(a.isStored("a"));
}


void testInsert1() {
cout << "testInsert1" << endl;
Prefix b;
b.insert("dd");
TS_ASSERT_EQUALS(b.isStored("d"), false);
}

void testInsert2() {
cout << "testInsert2" << endl;
Prefix a;

a.insert("abcdef");
TS_ASSERT(!a.isStored("abcd"));
}


Output to making the tests is this:



Running cxxtest tests (4 tests).testInsert0
On Node
On Node

.testInsert1
Post test
On Node
On Node
On Node
On Node
On Node
*** Error in `./testrunner': double free or corruption (out): 0x00000000019652a0 ***
make: *** [test] Aborted (core dumped)



When I comment out the code in the destructor that calls the clear function, it all works (of course with plenty of memory errors) but when the destructor is active, it cannot make it past 2 full unit tests that exercise its ability.


Answer



This is probably a case where not all the variables are initialised, which is causing a spurious call to delete. Ensure that each link the Node_t struct is set to nullptr before it's used.



Also check out Valgrind, which runs your program in a virtual machine to check for common memory issues - it's very useful for this sort of thing.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...