【发布时间】:2011-03-28 16:08:38
【问题描述】:
从 C 程序中的任何位置调用时,C 编程中的 return 和 exit 语句有什么区别?
【问题讨论】:
从 C 程序中的任何位置调用时,C 编程中的 return 和 exit 语句有什么区别?
【问题讨论】:
for 或 break。两者(几乎)做同样事情的唯一情况是在 main() 函数中,因为 main 的返回执行 exit()。
在大多数 C 实现中,main 是一个真正的函数,由一些启动代码调用,执行类似于 int ret = main(argc, argv); exit(ret); 的操作。 C 标准保证如果 main 返回时会发生与此等效的事情,但是实现会处理它。
以return 为例:
#include <stdio.h>
void f(){
printf("Executing f\n");
return;
}
int main(){
f();
printf("Back from f\n");
}
如果你执行这个程序,它会打印:
Executing f Back from f
exit() 的另一个例子:
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
如果你执行这个程序,它会打印:
Executing f
你永远不会得到“从 f 回来”。还要注意调用库函数exit() 所必需的#include <stdlib.h>。
还要注意exit()的参数是一个整数(它是launcher进程可以获得的进程的返回状态,常规用法是0表示成功,其他任何值表示错误)。
return 语句的参数是函数的返回类型。如果函数返回void,可以省略函数末尾的return。
最后一点,exit() 有两种口味 _exit() 和 exit()。形式之间的区别在于exit()(并从main 返回)在真正终止进程之前调用使用atexit() 或on_exit() 注册的函数,而_exit()(来自#include <unistd.h>,或其同义词_Exit from #include <stdlib.h> ) 立即终止进程。
现在还有一些 C++ 特有的问题。
在退出函数时,C++ 比 C 执行更多的工作 (return-ing)。具体来说,它调用超出范围的本地对象的析构函数。在大多数情况下,程序员不会在乎进程停止后程序的状态,因此不会有太大的区别:分配的内存将被释放,文件资源关闭等等。但是你的析构函数是否执行 IO 可能很重要。例如,在调用 exit 时不会刷新本地创建的自动 C++ OStream,并且您可能会丢失一些未刷新的数据(另一方面,静态 OStream 将被刷新)。
如果您使用旧的 C FILE* 流,则不会发生这种情况。这些将在exit() 上刷新。实际上,规则与注册退出函数的规则相同,FILE* 将在所有正常终止时刷新,包括exit(),但不会调用_exit() 或 abort()。
您还应该记住,C++ 提供了第三种退出函数的方法:抛出异常。这种退出函数的方式会调用析构函数。如果在调用者链中的任何地方都没有捕获到它,则异常可以向上传递到 main() 函数并终止进程。
如果您在程序中的任何位置从main() 或exit() 调用return,则将调用静态C++ 对象(全局)的析构函数。如果程序使用_exit() 或abort() 终止,则不会调用它们。 abort() 在调试模式下最有用,目的是立即停止程序并获取堆栈跟踪(用于事后分析)。它通常隐藏在 assert() 宏后面,仅在调试模式下有效。
exit() 什么时候有用?
exit() 表示您想立即停止当前进程。当我们遇到某种无法恢复的问题时,它可能对错误管理有一些用处,这些问题不允许您的代码再做任何有用的事情。当控制流很复杂并且错误代码必须一直向上传播时,它通常很方便。但请注意,这是不好的编码习惯。在大多数情况下,静默结束进程是更糟糕的行为,应该首选实际的错误管理(或者在 C++ 中使用异常)。
如果在库中直接调用exit() 尤其糟糕,因为它会使库用户陷入困境,并且应该由库用户选择是否实施某种错误恢复。如果你想举例说明为什么从库中调用 exit() 不好,它会导致例如人们询问 this question。
在支持它的操作系统上,exit() 作为结束由 fork() 启动的子进程的方式是无可争议的合法使用。回到 fork() 之前的代码通常是个坏主意。这就是解释为什么 exec() 系列的函数永远不会返回给调用者的理由。
【讨论】:
main()中使用return。当然,我在main() 的末尾使用return 0; — 我有时在函数体中使用exit();。我不喜欢 C99 规则关于从 main() 的末尾脱落等于 return 0; 的末尾;这是一个愚蠢的特殊情况(尽管 C++ 率先造成损害)。
at_quick_exit()、_Exit()(也在C99 中)和quick_exit()。 _exit() 函数来自 POSIX,但本质上与 _Exit() 相同。
return语句退出当前函数,exit()退出程序
they are the same when used in main() function
return 也是一个语句,而 exit() 是一个需要 stdlb.h 头文件的函数
【讨论】:
我写了两个程序:
int main(){return 0;}
和
#include <stdlib.h>
int main(){exit(0)}
执行gcc -S -O1后。这是我发现的
组装时(仅重要部件):
main:
movl $0, %eax /* setting return value */
ret /* return from main */
和
main:
subq $8, %rsp /* reserving some space */
movl $0, %edi /* setting return value */
call exit /* calling exit function */
/* magic and machine specific wizardry after this call */
所以我的结论是:尽可能使用return,并在需要时使用exit()。
【讨论】:
ret 指令返回到可能完成一些额外工作的点,但最后无论如何都会调用 exit() 函数 - 或者如何避免做什么 exit() 做什么?这意味着前者只是做了一些额外的“重复”工作,对第二种解决方案没有任何好处,因为无论如何在最后肯定会调用 exit()。
在C语言中,用于程序的启动函数(可以是main()、wmain()、_tmain()或者你的编译器使用的默认名称)没有太大区别。
如果您在main() 中使用return,则控制权会返回到最初启动您的程序的C 库中的_start() 函数,然后无论如何都会调用exit()。因此,您使用哪一个并不重要。
【讨论】: