在C++中我们可以使用try/catch来捕获异常,但是C语言中没有这样的关键字,只有setjmp和longjmp函数,它们的常规用法是先设置信号捕获函数,再调用setjmp,在信号捕获函数中调用longjmp,如果出现异常则会跳到setjmp后面:
#include<setjmp.h>#include<signal.h>#include<stdio.h>jmp_buf buf;voidsig(ints){switch(s){caseSIGSEGV:caseSIGINT:caseSIGFPE:(void)signal(s,sig);longjmp(buf,1);break;default:break;}}intmain(intargc,char*argv[]){(void)signal(SIGINT,sig);(void)signal(SIGFPE,sig);(void)signal(SIGSEGV,sig);if(setjmp(buf)==0){int*p=nullptr;*p=0;}else{printf("exception\n");}我们可以使用宏来模拟try,catch:
#defineTRYif(setjmp(buf)==0){#defineCATCH\}\else这样就可以写成下面的形式了:
TRY{}CATCH{}目前这种形式不支持嵌套捕获,比如:
TRY{TRY{}CATCH{}//如果这里出现异常,就不能捕获}CATCH{}要支持捕获,也很简单,把jmp_buf设置成数组,再使用一个变量来记录深度,使用一次TRY则加1,CATCH时减1即可:
jmp_buf buf[256]={};uint8_tdepth=0;#defineTRYif(setjmp(buf[depth++])==0){#defineCATCH\--depth;\}\elsevoidsig(ints){switch(s){caseSIGSEGV:caseSIGINT:caseSIGFPE:(void)signal(s,sig);longjmp(buf[--depth],1);break;default:break;}}测试代码:
#include<setjmp.h>#include<signal.h>#include<stdint.h>#include<stdio.h>jmp_buf buf[256]={};uint16_tdepth=0;#defineTRYif(setjmp(buf[depth++])==0){#defineCATCH\--depth;\}\elsevoidsegv(){int*v=nullptr;*v=1;printf("%p\n",v);}voiddiv0(){intv=0;printf("%d\n",1/v);}voidsig(ints){switch(s){caseSIGSEGV:caseSIGINT:caseSIGFPE:(void)signal(s,sig);longjmp(buf[--depth],1);break;default:break;}}voidtest(){TRY{TRY{segv();}CATCH{printf("SIGSEGV exception\n");}div0();}CATCH{printf("SIGFPE exception\n");}}intmain(intargc,char*argv[]){(void)signal(SIGINT,sig);(void)signal(SIGFPE,sig);(void)signal(SIGSEGV,sig);TRY{test();}CATCH{printf("test exception\n");}printf("ok\n");return0;}如果是使用的VC编译器和调试器,在除0的地方会直接断下来,并不会继续执行,所以触发不了SIGFPE信号,这是由于SEH异常机制导致,如果想要实现与GCC一样的行为,即POSIX行为,则需要拦截SEH,手动触发SIGFPE信号:
#if_MSC_VER#include<windows.h>LONG WINAPIseh_handler(EXCEPTION_POINTERS*e){if(e->ExceptionRecord->ExceptionCode==EXCEPTION_INT_DIVIDE_BY_ZERO){raise(SIGFPE);returnEXCEPTION_CONTINUE_EXECUTION;}returnEXCEPTION_CONTINUE_SEARCH;}#endif再调用:
#if_MSC_VERAddVectoredExceptionHandler(1,seh_handler);#endif完整代码:
#include<setjmp.h>#include<signal.h>#include<stdint.h>#include<stdio.h>#if_MSC_VER#include<windows.h>LONG WINAPIseh_handler(EXCEPTION_POINTERS*e){if(e->ExceptionRecord->ExceptionCode==EXCEPTION_INT_DIVIDE_BY_ZERO){raise(SIGFPE);returnEXCEPTION_CONTINUE_EXECUTION;}returnEXCEPTION_CONTINUE_SEARCH;}#endifjmp_buf buf[256]={};uint16_tdepth=0;#defineTRYif(setjmp(buf[depth++])==0){#defineCATCH\--depth;\}\elsevoidsegv(){int*v=0;*v=1;printf("%p\n",v);}voiddiv0(){intv=0;printf("%d\n",1/v);}voidsig(ints){switch(s){caseSIGSEGV:caseSIGINT:caseSIGFPE:(void)signal(s,sig);longjmp(buf[--depth],1);break;default:break;}}voidtest(){TRY{TRY{segv();}CATCH{printf("SIGSEGV exception\n");}div0();}CATCH{printf("SIGFPE exception\n");}}intmain(intargc,char*argv[]){#if_MSC_VERAddVectoredExceptionHandler(1,seh_handler);#endif(void)signal(SIGINT,sig);(void)signal(SIGFPE,sig);(void)signal(SIGSEGV,sig);TRY{TRY{TRY{segv();}CATCH{printf("SIGSEGV exception\n");}div0();}CATCH{printf("SIGFPE exception\n");}test();}CATCH{printf("test exception\n");}printf("ok\n");return0;}如果对你有帮助,欢迎点赞收藏!