一直以来对“异常”以及相关的知识很模糊,这次做个整理,回答几个基本问题以帮助对“异常”的理解:
- C语言的异常
- C语言版的try/catch: setjmp, longjmp
- C++的异常(try/catch)
- Windows 结构化异常,以及与C++标准异常的联系(__try/__except与try/catch)
- 在Windows API 与标准C++代码混用的项目中,如何较好的捕捉所有异常?
C语言的异常
C语言本身对异常处理比较少,当异常发生时,往往有以下方式:
1. exit()与abort()。
使用C库函数exit()和abort() 强行终止程序运行。
exit() - 通常表示程序正常退出。exit()退出之前会做一些清理工作,比如销毁static变量,刷一下缓冲区(不知道是否指文件系统缓冲区?),关闭IO通道,然后结束程序。用户还可通过at_exixt(your_exit_fun)设置自己的退出函数,程序exit()时会调用,通过your_exit_fun()做自定义的清理工作,比如把文件写回磁盘。
abort() - 该函数被调用通常表示出现了无法处理的异常,直接终止程序。它不像exit()那样做清理工作。
2.使用assert(断言)宏调用,位于头文件<assert.h>中,当程序出错时,就会引发一个abort()。
3.使用errno全局变量,由C运行时库函数提供,位于头文件<errno.h>中。
4.使用goto语句,当出错时跳转。
5.使用setjmp, longjmp进行异常处理。
C语言版的try/catch: setjmp, longjmp
不像goto只能实现函数内跳转,setjmp / longjmp一起用可以跨函数跳转。
setjump, longjmp的原理是先通过setjump设置一个跳转点,其实就记录下跳转点的栈信息,然后通过longjmp 来恢复该栈点信息,从而跳转到setjump事先定义的跳转点上执行。
函数定义:
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp() 接受一个jmp_buf类型参数,当显式调用它时,当前栈信息会被记录入该jmp_buf;而longjmp被调用时,并指定同一个jmp_buf作为参数,就会跳转到该栈点上执行,而第二参数val会变成setjmp第二返回时的返回值!这可能是搞晕人的地方:
为什么setjmp会返回两次?
看它的使用,或者就理解了:
#include <cstdio>
#include <cstdlib>
#include <setjmp.h>
jmp_buf jmpbuffer;
void raise_exception(void)
{
printf("raise_exception()\n");
longjmp(jmpbuffer,1);
}
int main(int arg