您的位置:首页 > 其它

exit abort return 区别

2013-01-06 22:59 447 查看
Exit abort return 三者区别

exit() 结束当前进程/程序,在整个程序中,只要调用 exit ,就结束。return() 是当前函数返回,当然如果是在主函数main, 自然也就结束当前进程了,如果不是,那就是退回上一层调用。在多个进程时.如果有时要检测某个进程是否正常退出的.就要用到这个进程的返回值。exit(0)表示进程正常退出. 返回 0; exit(1)表示进程非正常退出.返回 1。

如果是C++的话,还是尽量避免使用exit。用exit 的时候任何自动变量和临时变量的析构函数都不会被调用。如果是C 的话,关系不大。对于这种析构你可以用atexit 来指定一个退出时资源清理。也就是说可以利用atexit 函数为exit 事件"挂接"另外的函数,这种"挂接"有点类似Windows 编程中的"钩子"(Hook)。程序输出"atexit 挂接的函数"后即终止,即便是我们不调用exit 函数,当程序本身退出时,atexit 挂接的函数仍然会被执行。atexit 可以被多次行,并挂接多个函数,这些函数的执行顺序为后挂接的先执行。

当然,如果不想在出错情况下处理错误信息的话,你可以用abort()来退出(例如在断言中),而不能用exit,否则程序会产生一个系统错误提示,用了ABORT,退出当前处理过程时不会有错误提示出现。

注意:在多线程程序中,不要在某个线程使用exit abort,这样会导致整个进程退出。

C++ 中exit 的使用

1. 进程的结束

对于一个用C++写的程序,被加载至内存后运行,最终走向死亡。程序的死亡大致有三种:

• 自然死亡,通常就是main()中的一个return 0;

• 自杀,即请求OS 将自己结束。有两种方式:void exit(int status)和void abort(void)。

• 他杀,程序家族中的他杀行径往往是由自己至亲完成的,通常这个至亲就是有亲缘关系的进程。凶器(例如SIGKILL 信号)往往是由OS 直接或者间接提供的。

2. 程序死亡方式对对象析构的影响

C++程序中大致有三种对象:全局对象、局部静态对象、局部非静态对象(自动对象)。例如:

#include <iostream>

using namespace std;

struct Foo

{

Foo(){ cout<<"Foo"<<endl; }

~Foo(){ cout<<"~Foo"<<endl; }

/* some other sources here */

};

Foo Global;

void quit();

int

main()

{

static Foo StaticLocal;

Foo Local;

// quit();

// abort();

return 0;

}

void quit()

{

Foo AnotherLocal;

exit(1);

}

编译运行这个程序,程序将正常退出。运行过程中,Global 对象在进入main之前首先被构造,其次是StaticLocal 和Local。main 函数退出之前,Local 和

StaticLocal 被析构,main 退出后Global 也将被析构。

如果将17 行处quit()的注释去掉,我们将会看到4 个对象被构造,但却只有两个对象被析构,分别是Global 和StaticLocal 对象,其他两个对象Local 和AnotherLocal 对象的析构函数将不会被调用。如果将18 行处abort()的注释去掉(quit()被注释),3 个对象对象被构造,但在程序退出之前没有任何一个对象的析构函数被调用。

也就是说,正常情况下,所有类型的对象都将被析构;由exit 退出时只有非自动对象被析构;abort 被调用时,程序将直接退出,任何对象的析构函数都不会调用。

3. exit 做了什么

介绍exit 之前,不得不提void atexit(void (*f)(void) )函数。atexit,描述了exit 里面要做些什么。使用atexit 可以向exit 注册一系列的函数,这些函数在exit 中被调用,调用的顺序与它们被注册的顺序相反。可以使用下面的代码来验证:

#include <iostream>

using namespace std;

void f1(){ cout<<"f1"<<endl; }

void f2(){ cout<<"f2"<<endl; }

int

main()

{

atexit(f1);

atexit(f2);

exit(1);

return 0;

}

void exit(int status)被调用时,它首先调用全局的或者静态的对象的析构函数,然后调用atexit 所注册的函数。如果这些函数中的某一个再次调用exit,想想会有什么后果?最后,exit 会将代表程序执行状态的status“返回”(确切的说应该叫做传递,因为exit 永远不会返回调用方)给当前程序的父进程。值得一提的是,执行exit 结束程序,虽然自动对象的析构函数不被调用,但当程序结束时,OS 会将该程序占用的资源全部释放。这些资源包括该程序申请的内存(堆)、打开的文件句柄、管道(Unix/Linux)、socket
等。这样一来,似乎那些析构函数不被调用并不会有什么问题。但可惜这只适用于单线程的程序。对于多线程程序来说,只有当整个进程结束时,它占用的资源才会被OS释放,这时某个线程的exit 就有可能带来麻烦(比如内存泄露)。

4. 使用 c++异常来取代exit()函数

使用C++提供的异常机制,可以很好的解决上面的问题。我们可以在需要exit 的地方抛出(throw)异常,然后在捕获(catch)异常处调用exit(或者干脆不用exit),这样,所有需要的析构函数都将被调用。代码:

例一:

#include <iostream>

#include <cstdlib>

using namespace std;

struct Foo

{

Foo(){ cout<<"Foo"<<endl; }

~Foo(){ cout<<"~Foo"<<endl; }

/* some other sources here */

};

struct except: public exception

{

const char* what() const throw()

{

return "except";

}

};

Foo Global;

void quit();

int

main()

{

try

{

static Foo StaticLocal;

Foo Local;

quit();

}

catch(const exception& e)

{

cerr<<e.what()<<endl;

exit(0);

}

return 0;

}

void quit()

{

Foo AnotherLocal;

// exit(1);

throw except();

}

输出:

Foo

Foo

Foo

Foo

~Foo

~Foo

except

~Foo

~Foo

例二:

void main()

{

//初始化

...

try

{

processmail(...);

}

catch (int ret)

{

switch (ret) {

case e_initialization_failure: ...

case e_irrecoverable: ...

...

}

}

}

void processmail(...)

{

//初始化

...

if ( initializationerror )

{

throw(e_initialization_failure);

}

while ( !shutdown ) {

try

{

readmail(...)

}

catch (int ret)

{

switch (ret) {

case e_read_error:

//记录错误信息

...

//试图恢复

...

if ( recovered )

{

continue;

} else {

throw(e_irrecoverable);

}

break;

case ...

}

}

//继续处理

...

}

//throw()可以用来取代缺少的返回码

//但也要注意由此带来的性能损失

throw(s_ok);

} // processmail()

void readmail(...)

{

...

//在此无须捕捉异常

nbytesavailable = readbytes(...)

...

}

int readbytes(...)

{

//读取数据

if ( error )

{

throw(e_read_error);

}

return nbytesread;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: