在FreeBSD的/bin和/usr/bin中,return和exit有什么区别?

5
我注意到在/bin/usr/bin中的FreeBSD代码有一些修复,使用exit而不是return,这是什么意思?
我所想到的是return语句可能会导致vfork(2)破坏堆栈帧,这是唯一的原因吗?如果是这样,为什么只有部分命令在/bin/usr/bin中得到了修复,而不是全部?

3
您能指出更具体的内容吗?比如一个提交记录,一份源代码清单或其他什么东西?没有更多上下文的情况下,这个问题很难理解。 - unwind
1
freeBSD的/bin/df最近有一个提交,使用exit()代替main()中的return,但/bin/cat仍然在main()中使用return - oxnz
2
@oxnz:也许可以试试这个?https://dev59.com/A3RB5IYBdhLWcg3w77on#5856935 - Matteo Italia
2
抱歉,不行。这是让我困惑的提交。https://github.com/freebsd/freebsd/commit/5ec82107b61b159ab874773644b3df4880e4874e - oxnz
找到了一个类似的问题:https://dev59.com/A3RB5IYBdhLWcg3w77on - oxnz
在任何意图终止程序的情况下,即使在main()中也要使用exit()是良好风格和健壮实践。提交作者的推理和证明混乱而且过于复杂,并充满了未经证实的假设。returnexit()main()中的区别,在这个特定的例子中完全不相关。 - Greg A. Woods
3个回答

1

根据5.1.2.2.3p1,如果主函数的返回类型与int兼容,则从主函数的初始调用返回相当于使用主函数返回的值作为参数调用exit函数。

这排除了你“破坏堆栈帧”的理论;对于符合C实现(至少对于非递归的main入口点),return 0;在功能上与exit(0);相同。

我认为这个更改仅仅是风格上的,或者可能是由于无知而引导的。另一个可能性是作者想将main转换为递归函数(或者main已经被转换为递归函数;我只是快速地进行了一次grep,似乎不是立即递归)。最后,作为最后的可怕选择,也许FreeBSD使用的C实现是不符合标准的(我真的希望不是这样!)...

编辑:通过阅读这个答案,我突然想到这可能是绕过编译器错误的一个解决方法,但是很遗憾,我检查了使用atexit的源代码,并没有找到进一步追究这条路线的理由。


1

让我们看看能否将其他人提供的各个链接融合成一个恰当的答案。


就 C 语言而言,没有任何区别。

exit() 的调用会:

  1. 调用使用 atexit() 注册的函数;
  2. 刷新并关闭输出流;
  3. 删除使用 tmpfile() 打开的文件;
  4. 将控制返回给环境。

main 函数中的 return <n> 相当于 exit( <n> )


一些有缺陷的静态代码分析器认为不同。

就C语言而言,main()分配的内存如果没有被free()释放,则会发生泄漏。Unix在进程终止时清理内存,但并非所有操作系统都这样做(!)。

显然,一些静态代码分析器认为在exit()点仍然分配的内存未泄漏(而对于从main()返回则是),这就是为什么进行了该提交(以消除警告)。

这是对代码分析器中的一个错误的解决方法


就 C++ 语言而言,有很大的区别。

当你从 main() 返回时,你离开了函数的作用域,这意味着局部对象被销毁。

由于 C++ 程序员享受确定性销毁的好处(与例如 Java 不同,即使在 VM 终止时也可能不执行您的析构函数...),他们倾向于使其析构函数做更多事情,而不仅仅是释放内存。网络连接、临时文件、被 ncurses 锁定的终端窗口,所有这些好处,C 程序员必须手动关心或使用 atexit()

当你从 main() 调用 std::exit() 函数时,该函数直接将控制权转移到运行时main() 函数永远不会返回,进程被终止而不调用 main() 的本地对象的析构函数。

此外,虽然 std::exit() 被定义为刷新和关闭 C 输出流,但是对于 C++ 输出流却没有这样的规定。

你想太多了并且做出了一些假设。(1) returnexit()main()的区别在于自动存储期内在main()中定义的对象的生命周期(以及它们可能被在注册atexit()的回调函数引用的罕见情况)。(2) 在exit()之前分配内存但未释放的任何内存都将泄漏,main()没有任何特殊之处。没有实际的静态分析器会混淆(作者也在做出假设并感到困惑)。(3) C++在这里是不相关的。 - Greg A. Woods

0

在我看来,没有非常好的理由这样做。

这个特定的更改在freebsd邮件列表中的这篇文章中有讨论。

简而言之,提交日志应该是:

"在main()中使用exit()代替return以解决静态分析器的问题"

因此,这是为了帮助静态分析器和内存分析器,否则它们会将在main()中分配的内容报告为内存泄漏 - 大多数简单的实用程序不会打扰去释放它们,因为进程无论如何都将退出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接