简体   繁体   English

从函数返回指针如何工作?

[英]How does returning pointer from a function work?

Consider the following code snippet: 考虑以下代码片段:

#include <iostream>
#include <queue>

struct node {
    int key;
    node *l;
    node *r;

    node(int key)
        :key(key), l(nullptr), r(nullptr) {
    }
};

struct bst {
    node *root;

    bst() :root(nullptr) {

    }

    node* find(int key) {
        return find(root, key); 
    }

    node* find(node *root, int key) {
        if (!root) {
            return nullptr;
        } else {
            if (root->key < key) {
                return find(root->l, key);
            }
            else if (root->key > key) {
                return find(root->r, key);
            } else {
                return root;
            }
        }
    }

    void insert(int key) {
        insert(root, key);
    }

    void insert(node *&root, int key) {
        if (!root) {
            root = new node(key);
        } else {
            if (root->key < key) {
                insert(root->r, key);
            } else if (root->key > key) {
                insert(root->l, key);
            } else {
                return;
            }
        }
    }

    void print_by_level(std::ostream &o) {
        if (!root) {
            o << "(empty)";
            return;
        }
        std::queue<node*> q;
        int curr_lv = 1;
        int next_lv = 0;
        q.push(root);
        while (!q.empty()) {
            auto p = q.front();
            q.pop();
            curr_lv--;
            o << p->key << ' ';
            if (p->l) {
                q.push(p->l);
                next_lv++;
            }
            if (p->r) {
                q.push(p->r);
                next_lv++;
            }
            if (curr_lv == 0) {
                o << '\n';
                curr_lv = next_lv;
                next_lv = 0;
            }
        }
        o << '\n';
    }

};

int main() {
    bst t;
    t.insert(5);
    t.insert(10);
    t.insert(15);
    t.print_by_level(std::cout);

    // return pointer to 5 which is root
    node *p = t.find(5);
    // modify it, ok
    p->key = 100;
    t.print_by_level(std::cout);

    // now try to delete or change where p is pointing to
    delete p;
    p = nullptr;
    // then it's not happy :(
    t.print_by_level(std::cout);

    return 0;
}

I would expect p returning from find() is root , however it's not the case! 我希望从find()返回的proot ,但是事实并非如此! It seemed like it only returned a copy of that pointer. 似乎它只返回了该指针的副本。 However p->key = 100 actually changed the value of root . 但是p->key = 100实际上更改了root的值。 Could anyone help me explain this? 有人可以帮我解释一下吗?

On the other hand, if I delete t.root manually then it worked as expected. 另一方面,如果我手动删除t.root ,则它按预期工作。

int main() {
    bst t;
    t.insert(5);
    t.insert(10);
    t.insert(15);
    t.print_by_level(std::cout);

    // return pointer to 5 which is root
    node *p = t.find(5);
    // modify it, ok
    p->key = 100;
    t.print_by_level(std::cout);

    delete t.root;
    t.root = nullptr;
    // ok happy now
    t.print_by_level(std::cout);
    return 0;
}

Now try to change where p points to and return node*& : (please forgive me for a dirty hack nullp :(). 现在尝试更改p指向的位置并返回node*& :(请原谅我一个肮脏的hack nullp :()。

#include <iostream>
#include <queue>

struct node {
    int key;
    node *l;
    node *r;

    node(int key)
        :key(key), l(nullptr), r(nullptr) {
    }
};

struct bst {
    node *root;
    node *nullp;

    bst() :root(nullptr), nullp(nullptr) {

    }

    node*& find(int key) {
        return find(root, key); 
    }

    node*& find(node *root, int key) {
        if (!root) {
            return nullp;
        } else {
            if (root->key < key) {
                return find(root->l, key);
            }
            else if (root->key > key) {
                return find(root->r, key);
            } else {
                return root;
            }
        }
    }

    void insert(int key) {
        insert(root, key);
    }

    void insert(node *&root, int key) {
        if (!root) {
            root = new node(key);
        } else {
            if (root->key < key) {
                insert(root->r, key);
            } else if (root->key > key) {
                insert(root->l, key);
            } else {
                return;
            }
        }
    }

    /**
     *          p               q
     *         / \             / \
     *        q   x3     =>  x1   p
     *      /  \                 / \
     *     x1   x2              x2  x3
     */
    void rotate_w_left_child(node *&p) {
        node *q = p->l;
        p->l = q->r;
        q->r = p;
        p = q;
    }

    void print_by_level(std::ostream &o) {
        if (!root) {
            o << "(empty)";
            return;
        }
        std::queue<node*> q;
        int curr_lv = 1;
        int next_lv = 0;
        q.push(root);
        while (!q.empty()) {
            auto p = q.front();
            q.pop();
            curr_lv--;
            o << p->key << ' ';
            if (p->l) {
                q.push(p->l);
                next_lv++;
            }
            if (p->r) {
                q.push(p->r);
                next_lv++;
            }
            if (curr_lv == 0) {
                o << '\n';
                curr_lv = next_lv;
                next_lv = 0;
            }
        }
        o << '\n';
    }

};

int main() {
    bst t;
    t.insert(5);
    t.insert(4);
    t.insert(1);
    t.print_by_level(std::cout);
    node *p = t.find(5);
    // using root, happy
    // t.rotate_w_left_child(t.root);
    // t.print_by_level(std::cout);

    // using p, not happy :(
    t.rotate_w_left_child(p);
    t.print_by_level(std::cout);
    return 0;
}

In main when you delete p you are actually deleting the root pointer in the t object! maindelete p时,实际上是在删除t对象中的root指针! Don't do that, or you will have undefined behavior, which is what you experience here. 请勿这样做,否则您将遇到不确定的行为,这就是您在此处所遇到的。

When you return a pointer, you return a copy of the pointer , both pointers will point to the same object. 当您返回一个指针,返回指针的副本,两个指针会指向同一个对象。 If you then delete one of the pointers, the other pointer will now point to a deleted object. 如果然后delete其中一个指针,则另一个指针现在将指向已删除的对象。

[This is not necessarily answer, but a comment with code] [这不一定是答案,而是带有代码的注释]

Your code may be suffering from Variable Shadowing, the member "root" clearly clashes with the function parameter "root". 您的代码可能受可变阴影的影响,成员“ root”显然与函数参数“ root”冲突。

A common practice to help avoid issues like this is to prefix members and other special cases with a prefix, such as "m_" for "member", "s_" for static, "g_" for "global". 避免此类问题的常见做法是为成员和其他特殊情况加上前缀,例如,“ m_”代表“ member”,“ s_”代表静态,“ g_”代表“ global”。 Don't confuse it with the dreaded hungarian - at least, not systems . 不要把它与可怕的匈牙利人混为一谈,至少不要与系统混为一谈。

struct bst {
    node *m_root;
    node *m_nullp;

    bst() : m_root(nullptr), m_nullp(nullptr) {
    }

    node*& find(int key) {
        return find(m_root, key); 
    }

    node*& find(node *root, int key) {
        if (!root) { // definitely means the parameter
            return nullp;
        } else {
            if (root->key < key) {
                return find(root->l, key);
            }
            else if (root->key > key) {
                return find(root->r, key);
            } else {
                return root;
            }
        }
    }

    void insert(int key) {
        insert(m_root, key);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM