【问题标题】:ASAN: heap-use-after-free when flattening a binary tree to a linked listASAN:将二叉树展平为链表时的 heap-use-after-free
【发布时间】:2020-01-08 20:17:56
【问题描述】:

这是一个 Leetcode 的问题,Flatten Binary Tree to Linked List。

我的解决方案非常简单。对于节点root,将root->right 推入堆栈。将root->left 设置为root->right 并将root->left 设置为NULL。

我得到一个运行时错误:

AddressSanitizer: heap-use-after-free on address 0x603000000108 at pc 0x00000046bf70 bp 0x7ffc5d6c5f70 sp 0x7ffc5d6c5f68

是什么导致了错误?

这是我的代码:

class Solution {
public:
    void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s){ 
        if(root == NULL){
            if(s.empty()) return;
            else{
                TreeNode* newroot = s.top();  
                s.pop();
                pre->right = newroot;
                flat_tree(pre, newroot,s);
            }
        }else{
            if(root->left == NULL) {
                flat_tree(root, root->right, s); 
            }else if(root->right == NULL){
                root->right = root->left;
                flat_tree(root, root->right, s); 
            }else{
                TreeNode* right = root->right;
                root->right = root->left;
                s.push(right);
                flat_tree(root, root->right, s); 
            }
        }
    }   

    void flatten(TreeNode* root) {
        stack<TreeNode*> s;
        flat_tree(NULL ,root,s);  
    }
};

【问题讨论】:

    标签: c++ recursion stack binary-tree


    【解决方案1】:

    这非常接近。左节点指针在其子节点移动到根的右侧后需要清空,以防止当测试套件跟随两个指向同一内存位置的指针时发生的双重空闲堆损坏。

    这是工作代码:

    void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s){ 
        if(root == NULL){
            if(s.empty()) return;
            else{
                TreeNode* newroot = s.top();  
                s.pop();
                pre->right = newroot;
                flat_tree(pre, newroot,s);
            }
        }else{
            if(root->left == NULL) {
                flat_tree(root, root->right, s); 
            }else if(root->right == NULL){
                root->right = root->left;
                root->left = NULL; /* set the left pointer to null */
                flat_tree(root, root->right, s); 
            }else{
                TreeNode* right = root->right;
                root->right = root->left;
                root->left = NULL; /* do the same here */
                s.push(right);
                flat_tree(root, root->right, s); 
            }
        }
    }   
    

    我还建议重新组织分支以避免重复的逻辑:

    void flat_tree(TreeNode* pre, TreeNode* root, stack<TreeNode*>& s) { 
        if (root) { 
            if (root->right) {
                s.push(root->right);
            }
    
            root->right = root->left;
            root->left = NULL;
            flat_tree(root, root->right, s); 
        }
        else if (!s.empty()) {
            pre->right = s.top();
            s.pop();
            flat_tree(pre, pre->right, s);
        }
    }
    

    【讨论】:

    • 非常感谢!有用。在实际项目中,有没有可以快速告诉我们问题出在哪里的方法?
    • 没问题。如果“在实际项目中”,您的意思是在 LC 接口上,您得到的只是 ASAN 输出。但话又说回来,ASAN 是我能想到的检测这类问题的好工具,在这种情况下,错误消息非常明确。当然,使用高级语言编码通过完全避免内存问题并提供堆栈跟踪来解决问题,因此您始终可以用效率换取更轻松的编码和调试。
    猜你喜欢
    • 2019-08-03
    • 1970-01-01
    • 2020-07-23
    • 2017-12-12
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多