C中NULL赋值的不同行为

vsnjm48y  于 2023-11-16  发布在  其他
关注(0)|答案(1)|浏览(87)

我写了下面的函数来释放一个二叉树,但是地址消毒器在第二次递归中的标记行报告heap-use-after-free。

void freeTree (tree_t * tree) {
    tree_t * p = tree;
    while (p->left != NULL) {   // <- heap-use-after-free
        freeTree (p->left);
    }
    while (p->right != NULL) {
        freeTree (p->right);
    }
    free (p);
    p = NULL;
}

字符串
但是,在我移动了NULL赋值之后(如下所示),这个问题就消失了。为什么呢?他们不是在做同样的事情吗?

void freeTree (tree_t * tree) {
    tree_t * p = tree;
    while (p->left != NULL) {
        freeTree (p->left);
        p->left = NULL;        // Added
    }
    while (p->right != NULL) {
        freeTree (p->right);
        p->right = NULL;       // Added
    }
    free (p);
    // p = NULL;               // Removed
}

编辑

tree_t类型定义

typedef struct TreeNode {
    int val;
    struct TreeNode * left;
    struct TreeNode * right;
} tree_t;

创建一个新的树节点

tree_t tree = malloc (sizeof *tree);
tree->val = 1;
tree->left = NULL;
tree->right = NULL;

6yt4nkrj

6yt4nkrj1#

你在第一个片段中遇到的直接问题:

  • 你有两个无限循环,你希望p->left在第一个循环中改变,但在第二个循环中没有给它赋值,p->right也是如此。
  • p = NULL;没有做任何事情。它改变了一个永远不会再使用的局部变量。

您通过删除死代码正确修复了第二个问题。
你发现了一个非常复杂的方法来修复第一个问题,正确的解决方案是根本不使用循环。

void freeTree( tree_t *node ) {
   if ( node->left )
      freeTree( node->left );

   if ( node->right )
      freeTree( node->right );

   free( node );
}

字符串
或者,如果您希望允许提供NULL

void freeTree( tree_t *node ) {
   if ( !node )
      return;

   freeTree( node->left );
   freeTree( node->right );

   free( node );
}


在我看来,你似乎期望p = NULL;以某种方式改变调用者中的某个变量。当然,事实并非如此。如果你想要在调用者中使用变量,你需要这样的东西:

void freeTree( tree_t **node_pp ) {
    tree_t *node = *node_pp;

    if ( !node )
       return;

    freeTree( &node->left );
    freeTree( &node->right );

    free( node );

    // Set the variable the caller passed as an argument to NULL.
    *node_pp = NULL;
}


但这对我来说似乎是不必要的复杂。

相关问题