【问题标题】:Different output depending on whether or not I print the return value不同的输出取决于我是否打印返回值
【发布时间】:2021-08-20 00:15:41
【问题描述】:

所以我有一个简单的 C++ 代码 sn-p,它应该将一个节点插入到二叉搜索树中。如果值被成功插入,则返回 true,如果值已经在树中,则返回 false。

struct Node {
  int data;
  Node* parent = nullptr;
  Node* left = nullptr;
  Node* right = nullptr;
};

bool insert(Node& root, int data) {
  if (data > (root.data)) {
    if ((root.right) == nullptr) {
      Node newNode = {data, &root};
      root.right = &newNode;
      return true;
    } else {
      return insert(*root.right, data);
    }
  }
  else if (data < (root.data)) {
    if ((root.left) == nullptr) {
      Node newNode = {data, &root};
      root.left = &newNode;
      return true;
    } else {
      return insert(*root.left, data);
    }
  }
  else {
    return false; // if both values are equal 
  }
}

在测试我的功能时,我发现了一些奇怪的东西。当我不打印函数的返回值时,它给出了正确答案(20):

  Node root = {50};
  insert(root, 20);
  cout << (root.left->data) << endl;

然而,当我做打印返回值时,它给出了不正确的结果(0):

  Node root = {50};
  cout << insert(root, 20) << endl;
  cout << (root.left->data) << endl;

我无法理解为什么会发生这种情况,但我最好的选择是因为一些奇怪的内存问题,也许没有为结构分配新内存?我来自 Python,内存管理是自动处理的,所以我仍然习惯这样的情况。

【问题讨论】:

  • Node newNode = {data, &amp;root}; 创建一个局部变量,当它超出范围时被销毁。保留指向该变量的指针是未定义的行为。
  • 不同的输出取决于我是否打印返回值 这可能意味着某种类型的未定义行为。编译器是否发出任何警告?编辑:@RetiredNinja 找到了 UB
  • 奇怪的是,它没有产生警告或错误,至少在这种设置下没有:godbolt.org/z/fr857M1j9
  • 我无法理解为什么会发生这种情况 -- C++ 并没有按照你认为的方式工作。这:Node newNode = {data, &amp;root}; root.right = &amp;newNode; 不会创建某种对newNode 的永久引用,其中一些垃圾收集器看到它仍在使用中,因此不理会它。您需要重写您的代码,以便它在某种意义上使用动态内存分配实际创建节点。目前,在代码中的任何地方都没有发布过new 或delete,也没有使用任何智能指针。
  • 但是是的,这是您使用动态分配的情况。 std::unique_ptr 非常适合这种情况。这是一个关于使用智能指针的精彩演示:youtube.com/watch?v=JfmTagWcqoE 如果你想不必要地折磨自己,你可以使用new。

标签: c++ memory-management binary-search-tree nodes


【解决方案1】:

你在那里调用未定义的行为:

  Node newNode = {data, &root};
  root.right = &newNode;

这会在您的树中存储堆栈变量的地址。一旦函数返回,取消引用这个Node 的孩子就不再合法了。从那里开始,任何事情都可能发生。

你可能想要这样的东西:

  Node* newNode = new Node;
  newNode.data = data;
  newNode.root = root;
  ...
  root.right = newNode;

编辑:请记住,每当您将new 放入代码中时,您都需要一个匹配的delete。为了避免这种麻烦,现代方法是使用unique_ptr。你应该调查一下。在您的情况下,由于您保留指向根的反向指针,因此您需要 shared_ptr 和/或 weak_ptr。

【讨论】:

  • 这些问题已经存在,只是规模较小。我坚信所有 OP 都需要专门学习当前的问题才能成长。如果我们将它们一直推到 unique_ptr 和其他高级构造,IMO,它们就无法了解原因。但好点,我会用方向编辑。
  • 指向根的反向指针可以只是一个原始指针,因为它不拥有。
猜你喜欢
  • 2021-10-16
  • 1970-01-01
  • 2020-01-23
  • 2019-06-09
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多