Ваша функция ожидала указатель на указатель на узел. Вы даете ему указатель на узел три раза в своих рекурсивных вызовах. Кроме того, вы не проверяете, что указатель на указатель, и указатель, на который он указывает, ненулевые; вы проверяете только первое.
Короче говоря, ваша функция должна выглядеть так:
void free_tree (node_t ** root)
{
if(root && *root)
{
free_tree(&(*root)->left);
free_tree(&(*root)->mid);
free_tree(&(*root)->right);
free(*root);
*root = NULL;
}
}
Последняя функциональная строка является необязательной, но, честно говоря, бессмысленно делать это с указателями на указатели, если вы не собираетесь делать это в любом случае, так как она устанавливает указатель вызывающей стороны на NULL после уничтожения дерева. При правильном построении дерева ваш вызывающий должен сообщить адрес корня дерева при уничтожении всего дерева, как:
node_t *root = NULL;
// ... build tree ...
free_tree(&root);
// root is now NULL; tree is destroyed