【问题标题】:How is the data returned to main?数据如何返回到main?
【发布时间】:2018-05-16 16:40:47
【问题描述】:

我写了这段代码,它似乎可以工作,但是当我重新分析它时,我想知道数据(最小)是如何返回到主函数的?
给定这个二叉树:

           17
       14      19
    12       18  20
       13          21

还有这段代码:

struct Node {
   int data;
   Node* left;
   Node* right;
};

int min(Node* root) {
   if(root == NULL)
      return 0;
   else if(root->left == NULL)
      return root->data;
   else
      min(root->left);
}

最后一次调用min(Node*) 将返回root->data,但调用者在min(root->left) 中没有return。所有的returns 已经在之前的min(Node*) 中被跳过了。

【问题讨论】:

  • "我写了这段代码,它可以工作" -> "我写了这段代码,它似乎可以工作"。

标签: c++ data-structures return binary-search-tree


【解决方案1】:

代码有未定义的行为,所以它是偶然的。在 C++ 中,控制流到达非void 函数的末尾而不返回值是非法的(main 是唯一的例外)。

请注意,您应该始终在启用警告的情况下编译代码,在这种情况下,编译器会标记出来。


可能发生的事情是返回值通过最嵌套的调用存储在寄存器中(我相信通常是eax),并且由于其他调用实际上并没有执行return,他们不会覆盖它。但是,请注意,这纯粹是推测,可能会随着不同的编译器、不同的编译标志等而改变。未定义的行为是未定义的,永远不能依赖。

【讨论】:

    【解决方案2】:

    此代码包含未定义的行为,因此从语言律师的角度来看,它能够正常工作真是太幸运了。

    实际上,最有可能发生的事情(在 X86 架构上)是 min() 函数将结果存储在 eax 寄存器中。

    由于函数在递归调用自身后什么都不做,eax 寄存器会处于被调用函数离开它的任何状态,并且这隐含地成为函数本身的结果。

    简而言之,min(root->left); 完全像 return min(root->left); 一样被编译,这完全是运气。

    编辑

    附加说明:您的函数以允许称为尾递归的方式编写,这可能会或可能不会在这里发挥作用。在任何情况下,你都不应该依赖这种行为。

    【讨论】:

    • 所以,解决方案之一是在min(root->left) 中添加return
    • @NarutoUzumaki 这正是你应该做的。就个人而言,我也会摆脱那个决赛。这样所有的代码路径都有一个返回语句。官方规则是“非 void 函数的每个 可达 代码路径必须导致有效的返回语句”,但将规则应用于所有代码路径,无论是否可达,都更简洁。
    【解决方案3】:

    您的意思是“它 [意外地] [在我尝试的测试数据上] 有效吗?”因为,为了让它工作,最后一个分支应该是else return min(root->left); 你的编译器是否发出了任何关于没有return 的可能控制路径的警告?

    【讨论】:

    • 编译器没有对此发出任何警告。我使用这个命令编译它:g++ file.cpp -o main。我正在使用适用于 Linux (Ubuntu) 的 Windows 子系统。
    • @NarutoUzumaki:你应该总是在编译时添加-Wall,即g++ -Wall file.cpp -o main——否则你不会得到有用的编译器警告。用上面的代码试试,你会看到这可能为你节省了多少时间!
    • 但其实很奇怪,默认的gcc根本没有报错。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 2014-06-20
    • 2021-01-05
    • 1970-01-01
    • 2015-10-03
    • 2021-03-27
    • 2018-11-12
    相关资源
    最近更新 更多