zoukankan      html  css  js  c++  java
  • 【C++】异常简述(一):C语言中的异常处理机制

      人的一生会遇到很多大起大落,尤其是程序员.

      程序员写好的程序,论其消亡形式无非三种:无疾而终、自杀、他杀.

      当然作为一名程序员,最乐意看到自己写的程序能够无疾而终,因此尽快的学习异常处理机制是非常重要的!

      使自己的程序在遇到错误时能够克服错误,更健壮,而不是遇到错误就愤愤自杀.

      因此,在简述C++的异常机制之前,本文先来简述一下C语言中的异常处理机制.

      在C语言中,传统的错误处理方式有如下几种:

    1.直接终止程序(自杀)

      例如:

    int main(){
    	int a = 10;
    	int b = 20;
    	int c = a/0;
    	return 0;
    }
    

      当用gcc编译完后,执行,会打印“浮点数例外”,然后程序结束.

      这种情况是不允许的,无条件终止程序的库无法运用到不能当机的程序里。

    2.返回一个错误的值,附加错误码

      这种错误处理方式也很常见,比如我们用C语言打开一个文件失败时:

    #include<stdio.h>
    #include<errno.h>
    int main(){
    	FILE* fp = fopen("test.txt","r");
    	if(NULL == fp){
    		printf("文件打开失败,错误码:%d
    ",errno);
    	}
    	return 0;
    }
    

      因为我当前处于Linux系统下,没有GetLastError()函数,所以在Linux下使用全局变量errno来演示.

      这种情况,比较常用,但是有时不合适,例如返回错误码是int,每个调用都要检查错误值,极不方便,也容易让程序规模加倍

    3.返回一个合法的值,让程序处于某种非法的状态

      这种例子,最常见的就是atoi函数,例如:

    #include<stdio.h>
    #include<stdlib.h>
    int main(){
    	int a = atoi("123456789");
    	int b = atoi("dasdasdcs");
    	printf("a = %d
    ",a);
    	printf("b = %d
    ",b);
    	return 0;
    }
    

      虽然b为0,但是有一个全局变量表示了这个0属于非法值,不是由字符串0转过来的.

      这种情况,很容易误导调用者,万一调用者没有去检查全局变量errno或者通过其他方式检查错误,那是一个灾难,而且这种方式在并发的情况下不能很好工作

    4.调用一个预先准备好在出现"错误"的情况下使用的函数.

      这种异常处理情况比较少见,那我就现场直编了一个:

    #include<stdio.h>
    #include<stdlib.h>
    void DealError(){
    	printf("除数为0,老兄你在逗我?
    ");
    }
    typedef void(*fun)();
    int Div(int a,int b,fun callback){
    	if(b==0){
    		callback();
    		return 0;
    	}
    	return a/b;
    }
    
    int main(){
    	printf("正常情况下的4/2 = %d
    ",Div(4,2,DealError));
    	printf("调用错误处理函数的4/0 = %d
    ",Div(4,0,DealError));
    	return 0;
    }
    

     5、通过暴力的方式解决

      暴力的方式有两种,abort()函数和常见exit()函数.例如依然处理除0问题,代码可以这样写:

    #include<stdio.h>
    #include<stdlib.h>
    int Div(int a,int b){
    	if(b==0){
    		//exit(1);	//直接退出
    		abort();	//在Windows下会弹出一个信息框
    	}
    	return a/b;
    }
    
    int main(){
    	printf("正常情况下的4/2 = %d
    ",Div(4,2));
    	printf("调用错误处理函数的4/0 = %d
    ",Div(4,0));
    	return 0;
    }
    

     6、使用goto语句

      虽然,goto语句十分强大,但违背了程序的顺序执行,打乱的程序的执行流,盲目的使用goto语句可能会出现意想不到的错误,因此,并不推荐使用goto语句.

      但,尽管如此,为了探索C语言的异常处理机制,我还是实现一下goto语句处理异常的代码.

    #include<stdio.h>
    #include<stdlib.h>
    int main(){
    	int a = 0;
    	int b = 0;
    	printf("请输入两个值:
    ");
    	printf("a = ");
    	scanf("%d",&a);
    	printf("b = ");
    	scanf("%d",&b);
    	if(b==0){
    		goto Error;
    	}
    	printf("a/b = %d
    ",a/b);
    	return 0;
    Error:
    	printf("除数不能为0,程序异常退出!
    ");
    	exit(-1);
    }
    

     7、使用setjmp()与longjmp()

      goto语句虽然可以跳来跳去,但标记与goto必须处于同一作用域内.

      想从一个函数跳转到另一个函数,就必须使用setjmp与longjmp组合.

      实例如下:

    #include<stdio.h>
    #include<setjmp.h>
    
    jmp_buf mark;
    
    int Div(int a,int b){
    	if(b==0){
    		longjmp(mark,1);  //会使state = 1
    	}
    	return a/b;
    }
    int main(){
    	int State = setjmp(mark);	//保存寄存器相关信息,初始值为0
    	if(State==0){
    		Div(4,0);
    	}else{
    		switch(State){
    			case 1:
    				printf("除0异常!
    ");
    		}
    	}
    	return 0;
    }
    

    注意事项:

      1、setjmp必须先调用,在异常位置通过调用longjmp以恢复先前被保存的程序执行点,否则将导致 不可预测的结果,甚至程序崩溃。

      2、在调用setjmp的函数返回之前调动longjmp,否则结果不可预料。

    setjmp与longjmp存在以下缺陷:

      1、函数的使用者必须非常靠近函数调用的地方编写错误处理代码,无疑使代码变的臃肿笨拙。

      2、setjmp()和longjmp()并不能够很好的支持C++面向对象的语义。

      以上情况,便是C语言中的异常处理常见的机制,C++提供了更为完善的异常处理机制,我将在下文中简述.

  • 相关阅读:
    luogu p1268 树的重量——构造,真正考验编程能力
    luogu p2330[SCOI05] 繁忙的都市——瓶颈生成树
    生成树的个数——基尔霍夫定理(Matrix-Tree Theorem)
    子序列最大和
    有关pascal的填充语句小技巧
    P2320 [HNOI2006]鬼谷子的钱袋
    DP专题——括号序列
    简单的迷宫(bfs)noj1793
    G:献给阿尔吉侬的花束(可能超时)
    ytu 2335: 0-1背包问题
  • 原文地址:https://www.cnblogs.com/qq329914874/p/6734701.html
Copyright © 2011-2022 走看看