犯了不该犯的错误。。在main函数里使用return 0和exit(0)是不一样的

时间:2008-05-22 08:59:17   来源:论坛整理  作者:  编辑:chinaitzhe
看来还是刚入门。

一个大意,导致BUG找了大半天


网友回复:up 接分
网友回复:DB
网友回复:怎么说
网友回复:jf
网友回复:不一样吗?
return多做了?
网友回复:一样的啊,有什么不同?
网友回复:??
网友回复:貌似exit会销毁所以static对象,清空所有缓冲区,关闭所有i/o通道,但是局部对象的析构函数不会被调用,我的理解异常或者main函数的尾部调用exit的,不知道是否是这样,lz能不能介绍下碰到了什么样的bug?
网友回复:具体症状是什么?
网友回复:google了下,都说是一样的
网友回复:
引用 1 楼 Peterry 的回复:
up 接分

网友回复:似乎很有意思,你说一下哪里的不一样吧。我想看一下。

return 0 可以退出一个程序和一个函数
exit(0) 是退出一个程序。
网友回复:在windows下面是:
return 会调用全局变量和局部变量的析构函数
exit 只会调用全局变量的析构函数

引用楼主 lin_style 的帖子:
看来还是刚入门。

一个大意,导致BUG找了大半天

网友回复:up接分
网友回复:不一样的
1、abort 直接终止进程不做任何动作
2、exit 销毁所有static对象,清空所有缓冲区,关闭所有i/o通道,但是局部对象的析构函数不会被调用
3、return 0 应该是正常退出

lz是不是写了类似
if (xxx)
{
exit(0);
}
的代码啊?
我比较好奇的是lz碰到了什么样的bug
网友回复:什么样的bug?
网友回复:不一样是肯定的
假如是在其他函数里执行return 和exit(0),大家肯定会说不一样
而main对操作系统来说也只是个普通的函数,所以return多做了点全局变量的销毁工作

大家可以试试这样

在main函数里,创建一个socket,设置好超时时间,执行recv,
然后直接return,看看这个程序是不是还在。再用exit试试。

我是在用第三方库写代码的时候碰到这样情况,return无法退出,资源仍然存在。就算是有超时,也是卡在那里。
当然,随着平台不同, 情况略有差异。


说到这,我又想起昨天唐僧和飞雪讨论的问题,生命周期和作用域(参见这个地址 http://topic.csdn.net/u/20080519/14/a83b3fe1-6d65-4aef-b403-419ed8c1fd06.html)

class C
{
//有个socket变量m_sock;
fun
{
返回一个操作句柄
}
}
main
{
C c
handle h=c.返回一个句柄
//执行m_sock操作

析构这个句柄
//假设这里m_sock仍处在工作状态
return 0;
}

这样的话,m_sock资源是不会释放的。
既,他的生命周期却扩充到整个系统中(这里环境针对系统而言)。而这时,他的主进程已经正常返回了,所以操作系统对他的处理也就不了了知。

网友回复:学习学习
网友回复:以上是自己的一点理解
还请拍砖
网友回复:
引用 17 楼 lin_style 的回复:
不一样是肯定的
假如是在其他函数里执行return 和exit(0),大家肯定会说不一样
而main对操作系统来说也只是个普通的函数,所以return多做了点全局变量的销毁工作

大家可以试试这样

在main函数里,创建一个socket,设置好超时时间,执行recv,
然后直接return,看看这个程序是不是还在。再用exit试试。

我是在用第三方库写代码的时候碰到这样情况,return无法退出,资源仍然存在。就算是有超时,也是卡在那里。
当然…


看来我弄错了,return 0;应该不是最安全的退出,最安全的做法应该是在main的最后调用exit(EXIT_SUCCESS);是不是这样的?
网友回复:
没有研究过这个不同, 但是我认为应该没什么区别,
网友回复:靠,不许叫卫亭“唐僧”!
网友回复:DESCRIPTION
The exit() function causes normal program termination and the the value
of status & 0377 is returned to the parent (see wait(2)). All func-
tions registered with atexit() and on_exit() are called in the reverse
order of their registration, and all open streams are flushed and
closed. Files created by tmpfile() are removed.


to 20楼
int main()
{
return 0;
}
我的认为:return 0只是个规范的模式。return只是表示调用的返回
比如
while()
{
return
}

你说最安全做法是调用exit.
其实不然,有两点, 一、规范的设计上。做为一个完整的程序来说,应该是从运行开始到运行结束,资源都要自己释放和调配,这不应该由系统来做。
二、exit不调用析构和构造函数,所以在C 规范里,有了return 0这么一句

。。。
因为要吃饭。所以先闪了。。
网友回复:
引用 22 楼 fetag 的回复:
靠,不许叫卫亭“唐僧”!


。。我也是跟着几位星星叫着。。
总之,肯定不是我先叫的。
网友回复:学习
网友回复:假如在只有一个函数的情况下,使用return 0;或是 exit (0);的效果都是一样的,假如程序中使用了递归那么,return 0;将控制权返回到递归的上一级,而不是退出整个程序,假如使用exit(0);则是将退出整个程序.
网友回复:关于结束程序的几种情形,我的一点个人理解:
1、abort() 最不负责任的函数,直接终止进程,应该是尽量避免使用
2、exit() 被调用的时候,会销毁所有static对象,清空所有缓冲区,关闭所有i/o通道,
这个函数看上去很美,但是他有一个致命的弱点,不会调用局部对象的析构函数,所以应该
是确保局部对象的析构函数都被调用之后才能调用exit,这样是最安全的,但是什么时候局部对
象会确定被析构呢?首先在c 中有两种情况析构函数会被调用,第一:delete,第二,栈展开的
时候,上面第一点是针对堆上的对象,所以重点应该是在第二点,exit应该在栈展开的时候被调
用是最合适的,栈展开在异常处理中会被执行,所以exit()是配合异常处理一起使用的。
3、main函数最后的return EXIT_SUCCESS;
正如楼主所碰到的这也不一定安全,因为某些i/o操作没有被关闭的情形下,进程直接结束了,
所以之前一定要确保所有的i/o操作已经被关闭的情形下再reutrn EXIT_SUCCESS;

PS:上面是个人理解,不一定正确,欢迎拍砖


网友回复:楼主,真的是一样的,主线程(main)之所以能结束就是因为调用了exit(0);
网友回复:main 之外还有至少一层调用栈。。可以做的事情太多了。
www.xfocus.net/articles/200109/269.html
有趣的东西多了。。
网友回复:感谢废人大哥让小弟的知识面又拓宽了。(不知道明天爬得起来否。。)

引用 28 楼 jackzhhuang 的回复:
楼主,真的是一样的,主线程(main)之所以能结束就是因为调用了exit(0);


先说说我理想的看法.在我们自己写的一个程序里,假如对一个函数进行了return,那么没人会认为他执行了exit(0)吧。当然他会进行其他的一些销毁工作;同理在main这个函数里,对系统这个环境说也只是一个普通的函数调用,return后,销毁工作做得更多。这只是我理想的看法。

按废人大哥给的文章反推,main结束后可能做了这些工作。动态连接器的卸除,共享库的卸除,段表某些位置的数值的清空。以上这些可能都由__libc_start_main函数中之前设定的destructor函数来完成。
我私下认为。这个destructor函数不包括对main函数里变量的删除工作。

所以流程是return 后,做销毁工作,调用__libc_start_main函数中之前设定的destructor函数
exit后,做更多的额外销毁工作(不包括调用析构等),调用__libc_start_main函数中之前设定的destructor函数

所以。我不赞同28楼兄弟的说。两者的走向是不一样的

网友回复:赶紧睡觉。 。

再次感谢废人大哥。。
网友回复:觉得在Linux下编程经常用exit(0)和exit(1)晕哦~
网友回复:
引用 30 楼 lin_style 的回复:
感谢废人大哥让小弟的知识面又拓宽了。(不知道明天爬得起来否。。)

引用 28 楼 jackzhhuang 的回复:
楼主,真的是一样的,主线程(main)之所以能结束就是因为调用了exit(0);


先说说我理想的看法.在我们自己写的一个程序里,假如对一个函数进行了return,那么没人会认为他执行了exit(0)吧。当然他会进行其他的一些销毁工作;同理在main这个函数里,对系统这个环境说也只是一个普通的函数调用,return后,销毁工…


你说的是,不过我的意思就是在return后,主线程依靠exit(0)退出。
对一个函数进行return——这个函数不是main,当然不会调用exit了,main是主线程才会调用。

网友回复:
引用 30 楼 lin_style 的回复:
我私下认为。这个destructor函数不包括对main函数里变量的删除工作。


不包含。C 的局部变量析构是由C 编译器保证的,而 libc 库的"destructor"函数是C库的功能,与 C 无关。
另,假如有看过 APUE 的,里面有一段介绍 exit, _exit, abort 的区别的,那才叫一个复杂,呵呵。

引用 33 楼 jackzhhuang 的回复:
你说的是,不过我的意思就是在return后,主线程依靠exit(0)退出。
对一个函数进行return——这个函数不是main,当然不会调用exit了,main是主线程才会调用。

跟“主线程”无关,跟“main”函数本身也无关。
网友回复:
C/C code





Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/







#include <iostream>

#include <assert.h>



class Point 

{

public:

    Point()

    {

        std::cout << "co\n";

    }



    ~Point()

    {

        std::cout << "de\n";

        assert(0);

    }

};



int main()

{

    Point  p ;//= new Point;



    //delete p;



    //return 0;

        exit(0);

}







看来我错了,不管是从堆还是栈分配内存,exit(0)都不会析构,他只会析构全局数据。

换成return就会析构栈和全局的数据。

学到东西了,呵呵,以前一直没注重。
网友回复:进程结束后,资源自然被系统回收,这是毫无疑问的,不论是从main中return还是exit。
网友回复:学习
网友回复:up接分
网友回复:学习了,接分!
网友回复:
引用 36 楼 mymtom 的回复:
进程结束后,资源自然被系统回收,这是毫无疑问的,不论是从main中return还是exit。



不然。我这次BUG就是从SOKET里面产生的


对32-38楼表示崇高的敬意(这么迟还在。。)

网友回复:
引用 39 楼 Crazy_hand 的回复:
学习了,接分!

网友回复:#include <iostream>
#include <assert.h>

class Point
{
public:
Point()
{
std::cout < < "co\n";
}

~Point()
{
std::cout < < "de\n";
assert(0);
}
};
Point d;
int main()
{
Point p ;//= new Point;

//delete p;

//return 0;
exit(0);
}
不管是return还是exit,似乎都不调用全局变量析构函数,但是return会调用局部变量的析构函数!

网友回复:
引用 24 楼 lin_style 的回复:
引用 22 楼 fetag 的回复:
靠,不许叫卫亭“唐僧”!


。。我也是跟着几位星星叫着。。
总之,肯定不是我先叫的。

我可以很负责任的告诉你,“活唐僧”这个称号是我最先叫的,嘿嘿...
网友回复:这个是轻易忽略啊,昨天还在看exit和_exit这两个东东呢

网友回复:学习了
网友回复:
引用 22 楼 fetag 的回复:
靠,不许叫卫亭“唐僧”!

顶过儿。开开玩笑没问题,形成制度可就不好啦。我没想过要出家诶。

其实从main中return 0之后会做一些必要的工作,最后也会调用exit的。
楼主的问题是资源没有正常释放,所以被阻塞在return 0之后、exit之前了。

直接调用exit的话,就跳过了那些必要的工作了。这会造成多大的问题呢?因编译器而异吧。
至少,在楼主的例子中,将无法知道程序存在一个潜在的错误了——这个错误,原本是可以由精心设计的socket库暴露出来的。有时候,容错并非最好的选择,有问题就暴露出来,比跳过它要好得多。
关键字:不该,错误,main,函数,使用,

文章评论

共有 0 位网友发表了评论 此处只显示部分留言 点击查看完整评论页面