I have the following following somewhat convoluted data structures: a binary tree made up of nodes, and a linked list of items, each item storing tree nodes. If a Node is created, then it will certainly be used in a Tree, in a List, or both. Here are the data structures:
typedef struct Node Node;
typedef struct Item Item;
struct Node {
int key;
Node *left;
Node *right;
};
typedef struct {
Node *root;
} Tree;
struct Item {
Node *value;
Item *next;
};
typedef struct {
Item *head;
} List;
I am having trouble free'ing my tree and list. Logically, I would first free the Tree, and then the List. My idea was to set the Node pointers to NULL after freeing them in order for the List to decide whether the current Item has non-NULL nodes which must be free'd.
Here is how the Tree is free'd:
void tree_free_helper(Node *node)
{
if (node) {
tree_free_helper(node->left);
tree_free_helper(node->right);
free(node);
node = NULL;
}
}
void tree_free(Tree *tree)
{
if (tree) {
tree_free_helper(tree->root);
free(tree);
}
}
This works fine if Nodes only belong to a Tree. However, when a Node is also stored in a List Item, I get one extra free call = number of mallocs + 1. Here is how I free a List:
void list_free(List *list)
{
Item *p, *q;
if (!list) return;
p = list->head;
while (p) {
q = p;
p = p->next;
if (q->value) // trying to free only non-free'd nodes
free(q->value);
free(q);
}
free(list);
}
Valgrind dutifully informs me that Address 0x52040e0 is 0 bytes inside a block of size 24 free'd, and where in my code the block 0x52040e0 was alloc'd at. Here's a simple example for which I get this valgrind report, where I create 3 nodes for a Tree (n1 being the root and having n0 as left child and n2 as right child); the problem comes from appending one of the nodes (n0 in this example) to a List:
int main() {
List *list = list_create();
Tree *tree = tree_create();
Node *n0 = node_create(0);
Node *n1 = node_create(1);
Node *n2 = node_create(2);
tree->root = n1;
n1->left = n0;
n1->right = n2;
list_append(list, n0);
tree_free(tree);
list_free(list);
}
Note: list_create, tree_create, and node_create respectively create a List, a Tree, and a Node. list_append creates an Item that is added to the list, and the item stores the Node passed as argument.
So my question is: seeing that setting the node pointer to NULL after I free it does not seem to have any effect, as if (q->value) always evaluates to true in list_free, what am I doing wrong? Thank you for any insight.
NULLin yourfree()function if you ignore that allocation might actually produce aNULL.main, it seems so. If so, you need to decide which "owns" them (the tree or the list). The one that doesn't shouldn't be freeing theirnode, but should still be removing theirList(orTree) matching the correspondingNodefrom their respective formal structures.mallocin my..._createfunctions. The check forNULLinlist_freeis meant to check whether the current item in the list still contains a valid reference to a node, and in this case free the node (i.e. the node was never in the tree).