file 'NonexistentFile.h'(No such file or directory)!
?6 Open file failed: No such file or directory
?7 [wangxiaoyuan_@localhost test1]$ ./GlbErr NonexistentFile.h > test
?8 Open file failed: No such file or directory
?9 [wangxiaoyuan_@localhost test1]$ ./GlbErr NonexistentFile.h 2> test
10 Cannot open file 'NonexistentFile.h'(No such file or directory)!
复制代码
? ? ?也可仿照errno的定义和处理,定制自己的错误代码:
?
复制代码
?1 int *_fpErrNo(void)
?2 {
?3 ? ?static int dwLocalErrNo = 0;
?4 ? ?return &dwLocalErrNo;
?5 }
?6?
?7 #define ErrNo (*_fpErrNo())
?8 #define EOUTOFRANGE ?1
?9 //define other error macros...
10?
11 int Callee(void)
12 {
13 ? ? ErrNo = 1;
14 ? ? return -1;
15 }
16 ?
17 int main(void)
18 {
19 ? ? ErrNo = 0;
20 ? ? if((-1 == Callee()) && (EOUTOFRANGE == ErrNo))
21 ? ? ? ? printf("Callee failed(ErrNo:%d)!\n", ErrNo);
22 ? ? return 0;
23 }
复制代码
? ? ?借助全局状态标志,可充分利用函数的接口(返回值和参数表)。但与返回值一样,它隐含地要求调用者在调用函数后检查该标志,而这种约束同样脆弱。
?
? ? ?此外,全局状态标志存在重用和覆盖的风险。而函数返回值是无名的临时变量,由函数产生且只能被调用者访问。调用完成后即可检查或拷贝返回值,然后原始的返回对象将消失而不能被重用。又因为无名,返回值不能被覆盖。
?
2.3 局部跳转(goto)
? ? ?使用goto语句可直接跳转到函数内的错误处理代码处。以除零错误为例:
?
复制代码
?1 double Division(double fDividend, double fDivisor)
?2 {
?3 ? ? return fDividend/fDivisor;
?4 }
?5 int main(void)
?6 {
?7 ? ? int dwFlag = 0;
?8 ? ? if(1 == dwFlag)
?9 ? ? {
10 ? ? RaiseException:
11 ? ? ? ? printf("The divisor cannot be 0!\n");
12 ? ? ? ? exit(1);
13 ? ? }
14 ? ? dwFlag = 1;
15?
16 ? ? double fDividend = 0.0, fDivisor = 0.0;
17 ? ? printf("Enter the dividend: ");
18 ? ? scanf("%lf", &fDividend);
19 ? ? printf("Enter the divisor : ");
20 ? ? scanf("%lf", &fDivisor);
21 ? ? if(0 == fDivisor) //不太严谨的浮点数判0比较
22 ? ? ? ? goto RaiseException;
23 ? ? printf("The quotient is %.2lf\n", Division(fDividend, fDivisor));
24?
25 ? ? return 0;
26 }
复制代码
? ? ?执行结果如下:
?
复制代码
1 [wangxiaoyuan_@localhost test1]$ ./test
2 Enter the dividend: 10
3 Enter the divisor : 0
4 The divisor cannot be 0!
5 [wangxiaoyuan_@localhost test1]$ ./test
6 Enter the dividend: 10
7 Enter the divisor : 2
8 The quotient is 5.00
复制代码
? ? ?虽然goto语句会破坏代码结构性,但却非常适用于集中错误处理。伪代码示例如下:
?
复制代码
?1 CallerFunc()
?2 {
?3 ? ? if((ret = CalleeFunc1()) < 0);
?4 ? ? ? ? goto ErrHandle;
?5 ? ? if((ret = CalleeFunc2()) < 0);
?6 ? ? ? ? goto ErrHandle;
?7 ? ? if((ret = CalleeFunc3()) < 0);
?8 ? ? ? ? goto ErrHandle;
?9 ? ? //...
10?
11 ? ? return;
12 ? ??
13 ErrHandle:
14 ? ? //Handle Error(e.g. printf)
15 ? ? return;
16 }
复制代码
2.4 非局部跳转(setjmp/longjmp)
? ? ?局部goto语句只能跳到所在函数内部的标号上。若要跨越函数跳转,需要借助标准C库提供非局部跳转函数setjmp()和longjmp()。它们分别承担非局部标号和goto的作用,非常适用于处理发生在深层嵌套函数调用中的出错情况。“非局部跳转”是在栈上跳过若干调用帧,返回到当前函数调用路径上的某个函数内。
?
#include
?
int setjmp(jmp_buf env);
?
void longjmp(jmp_buf env,int val);
?
? ? ?函数setjmp()将程序运行时的当前系统堆栈环境保存在缓冲区env结构中。初次调用该函数时返回值为0。longjmp()函数根据setjmp()所保存的env结构恢复先前的堆栈环境,即“跳回”先前调用setjmp时的程序执行点。此时,setjmp()函数返回longjmp()函数所设置的参数val值,程序将继续执行setjmp调用后的下一条语句(仿佛从未离开setjmp)。参数val为非0值,若设置为0,则setjmp()函数返回1。
?
? ? ?可见,setjmp()有两类返回值,用于区分是首次直接调用(返回0)和还是由其他地方跳转而来(返回非0值)。对于一个setjmp可有多个longjmp,因此可由不同的非0返回值区分这些longjmp。
?
? ? ?举个简单例子说明 setjmp/longjmp的非局部跳转:
?
复制代码
?1 jmp_buf gJmpBuf;
?2 void Func1(){
?3 ? ? printf("Enter Func1\n");
?4 ? ? if(0)longjm