您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 销售管理 > 性的最有效方法之一C语言实现出错处理的方法是
第九章异常处理程序的错误有两大类:①编译链接错误:这类错误是由程序的语法错误(例如关键字错误、变量未定义、语句结束缺分号、括号失配、结构失配等)和其他错误(函数只声明未定义、缺少库的链接配置等)引起的。这类程序错误发生在程序的编译链接过程中,对于一个具有一定经验的编程人员是容易解决的。②运行错误:这类程序错误发生在程序的运行期间,主要表现在计算过程中的被0除、内存空间不足、数据的输入输出错误等。这类程序错误只靠编程人员的经验是难以避免的。错误修复技术是解决程序运行错误,提高代码健壮性的最有效方法之一。C语言实现出错处理的方法是出错与错误处理的紧耦合,即检查被调函数的返回值或输出信息,以便确定是否发生错误,作出相应的处理。这种出错处理存在两个主要问题:出错处理的繁琐和错误检查引起的代码膨胀将不可避免地降低程序的执行效率,增加程序的阅读困难。被调用函数只清楚出错原因而不清楚被调用环境,因此缺乏处理错误的依据。因此这种将用户函数与出错处理紧密结合的方法将造成使用出错处理的不方便和难以接受。正是因为上述原因,使得不少程序设计人员在实际设计中常常“忽略”出错处理,似乎是在“不会出错”的状态下编程,这会严重地降低程序代码的健壮性。异常处理是C++语言的一个重要特征,它提出了出错处理更加完美的方法。出错处理代码的编写不再繁琐,也不须将出错处理代码与功能代码紧密结合。在可能发生错误的函数中加入出错代码,并在后面调用该函数的程序中加入错误处理代码。如果程序中多次调用一个函数,可以在程序中加入一个专门用于被调函数的出错处理函数。错误发生是不会被忽略的。如果被调用函数需发送一条出错信息给调用函数,它可向调用环境发送一个描述错误信息的对象。如果调用环境没有捕获该错误信息对象,则该错误信息对象会被自动向上一层的调用环境发送;如果调用环境无法处理该错误信息对象,则调用环境可以将该错误信息对象主动发送到上一层的调用环境中;直到该错误信息对象被捕捉和处理。9.1C语言的出错处理在通过对被调用函数的返回或对断言宏assert()的判断结果的检查能够确切定后续操作的情况下,出错处理就变得十分明确和容易了,因为可以通过程序执行的当前运行环境得到所有必要的信息。然而能够这样处理的错误都是环境一般都是简单的普通错误。如果错误问题发生时,在程序当前运行环境中无法获得足够的错误发生和处理的信息,则需要从更大的运行环境中获取出错处理信息。C语言处理这类错误情况的典型方法有三种:⑴出错信息可以通过函数的返回值获得。如果返回值不足以描述出错信息,则可设置全局错误判断标志(标准C语言中全局变量errno以及系统运行库函数perror(constchar*string)、strerror(interrnum)支持这一方法)。由于这种方法要对每个函数调用都进行错误检查,这将十分繁琐并增加程序的混乱度。另外,偶然出现异常的函数返回值可能并不反映什麽问题。⑵可使用C信号处理库中的signal函数设置中断信号处理,和使用raise函数向正在运行的程序发送信号。这两个函数的原型如下:void(*signal(intsig,void(__cdecl*func)(intsig[,intsubcode])))(intsig);intraise(intsig);信号处理库的使用者必须清楚地了解、恰当地定义和设置中断信号处理。同时对于大型项目,不同库之间的信号可能会产生冲突。因此,信号处理库的使用有一定的难度。⑶使用C标准库中的设置跳转函数setjmp和非局部跳转函数longjmp实现出错和错误处理。这两个函数的原型如下:setjmp(jmp_bufenv);longjmp(jmp_bufenv,intvalue);调用setjmp函数在程序中存储一典型的正常状态,如果进入错误状态,longjmp可恢复由setjmp函数所设定的状态,并且状态被恢复时的存储地点与错误发生地点紧密联系。在较早的VisualC++版本(例如VC++6.0)中C语言的信号处理技术和setjmp/longjmp函数被调用时不能正确地调用类对象的析构函数,所以一个描述出错信息的对象不能被正确地清除。如果出错对象不能被清除,则该对象将被保留下来且不能再次被正确地存取,因此,实际上是不可能有效、正确地从异常情况中恢复。所以在C++编程中不推荐使用C语言中这种处理出错的方法。例9-1描述了setjmp/longjmp函数的这一特点。在VC++6.0中的运行结果:tornado,witch,munchkins...there'snoplacelikehomethere'snoplacelikehomethere'snoplacelikehomerainbow()iscalled.AuntieEm!Ihadthestrangestdream...分析:①由于main函数中setjmp的调用(保存当前运行点环境)后返回0,导致if分支的执行,显示了前5行执行信息。②全局函数OZ被调用,其中longjmp的调用结果使得程序的运行恢复到由setjmp保存的运行点,导致使setjmp再次被调用。由于保存运行点环境的缓冲区kansas被setjmp的第一次调用设置,使得setjmp的第二次调用返回值为非0,导致else分支执行,显示第6行运行信息,但rainbow对象RB未被析构。结论:①程序中调用setjmp和longjmp的方法提供了修复程序运行错误的典型结构和方法。②使用setjmp和longjmp可能无法析构错误发生前创建的对象,不能满足面向对象程序的错误处理。9.2C++语言的出错处理虽然C语言的出错处理技术和方法在C++语言的程序设计中仍然可以使用,但更重要的是C++提供了一套符合面向对象程序设计的出错处理技术——异常处理技术。异常处理的特点主要表现在:①异常的产生和异常的处理分离。②异常的信息数据和行为被封装成独立的类对象。③结构化的异常处理为异常对象的捕获、处理、传递提供了有效、方便的编程手段。④能确保残留对象在异常处理时被彻底析构。9.3异常对象的创建和抛出如果程序发生了异常情况,而在当前运行环境中无法获取处理异常的足够信息,就可以创建一个包含异常信息和行为的对象,并将该对象从发生异常的运行环境中抛出,发送到上一层运行环境中,这称为异常的创建和抛出,例如:throwmyerror(somethingbadhappened);分析:①关键字throw的作用是抛出异常对象,被抛出的对象可以是包括系统预定义类型在内的任何类型,当然多数情况为自定义异常类型,如本例中的myerror就是一个以字符串为参数创建的自定义异常对象。②在函数中使用关键字throw抛出异常类对象都是函数正常执行中不存在的。抛出异常类对象的结果是导致函数退出执行,但不是函数设计的正常返回。因此,异常类型可以视为是函数异常退出作用域的返回值类型。③函数的正常返回和对函数返回的检查和处理是处于同一作用域的,而对异常的处理作用域与异常抛出作用域可能相距很远。注意,只有完整创建的异常对象才能在异常被处理时被清除,而函数返回(包括异常返回)时,函数作用域内的所有对象均被清除。④函数可以根据错误发生的原因不同,抛出不同类型的异常对象,使函数的调用者可以在更大范围的程序上下文环境中考虑对异常的处理。9.4异常的捕获和处理函数在发生错误时能以抛出异常对象的方式结束函数执行是建立在假定该异常对象能被捕获和处理的前提下的。这一假定在C++中是成立的,这也是异常处理的一个优点。完成函数调用时的异常测试,异常对象的捕获和处理是由try-catch结构实现的,使得处理程序运行错误的编码变得方便、有效,并具有完全的结构化和良好的可读性。该结构的一般形式如下:try{被测试的程序代码}catch(异常类型异常对象名){异常处理的程序代码}9.4.1测试块try测试块try的作用是使处于该块中的程序代码执行可能抛出的异常对象能在后续的异常处理器中被捕获,从而确定如何处理。因此,调用一个函数,并期望在函数调用者所在程序运行环境中使用异常处理的方法解决函数可能发生的错误,就必须将函数调用语句置于测试块try中。否则函数所抛出的异常对象就不能被后续的异常处理器捕获,从而使异常对象被自动传递到上一层运行环境,直至被操作系统捕获和处理,导致程序被终止执行。9.4.2异常处理器异常发生后,被抛出的异常对象一旦被随后的异常处理器捕获到,就可以被处理。根据在当前运行环境中能否解决引起异常的程序运行错误,对异常对象的处理有两种:①尝试解决程序运行错误,析构异常对象。②无法解决程序运行错误,将异常对象抛向上一层运行环境。为此,异常处理器应该具备捕获一个以上任何类型的异常对象的能力,每个异常对象的捕获和处理由关键字catch引导。例如:try{//codethatmaygenerateexception}catch(type1id1){//handleexceptionsoftype1}catch(type2id2){//handleexceptionsoftype2}//etc…⑴每个catch语句相当于一个以特定的异常类型为单一参数的小型函数;⑵标识符id1、id2等如同函数中的参数名,如果对引起该异常对象抛出的程序运行的错误处理中无须使用异常对象,则该标识符可省略;⑶异常处理器部分必须紧跟在测试块try之后;⑷catch语句与switch语句不同,即每个case(情况)引起的执行需要加入break实现执行的结束;⑸测试块try中不同函数的调用可能会抛出相同的异常对象,而异常处理器中对同一异常对象的处理方法只需要一个。异常处理的两种模式——终止与恢复:⑴终止模式如果引起异常的是致命错误,即表明程序运行进入了无法恢复正常运行的状态,这时必须调用终止模式结束程序运行的异常状态,而不应返回异常抛出之处。⑵恢复模式恢复意味着期望对异常的处理能够修复异常状态,然后再次对抛出异常对象的函数进行测试调用,使之能够成功运行。如果希望程序具有恢复运行的能力,就需要程序在异常处理后仍能继续正常执行,这时异常处理就更像一个被调用的函数。在程序需要进行恢复运行的地方,可以将测试块try和异常处理器放在while循环中,直到测试调用得到满意的结果。9.4.3异常规格说明编写异常处理器必须知道被测试调用的函数能抛出哪些类型的异常对象。C++提供了异常规格说明语法,即在函数原型声明中,位于参数表列之后,清晰地告诉函数的使用者:该函数可能抛出的异常类型,以便使用者能够方便地捕获异常对象进行异常处理。带有异常规格说明的函数原型说明的一般形式:返回类型函数名(参数表列)throw(异常类型名[,…])使用异常规格说明的函数原型有三种:①抛出指定类型异常对象的函数原型:voidfunction()throw(toobig,toosmall,divzero);②能抛出任何类型异常对象的函数原型:voidfunction();注意,该形式与传统的函数原型声明形式相同。③不抛出任何异常对象的函数原型:voidfunction()throw();为了实现对函数的安全调用和对函数执行中可能产生的错误进行有效的处理。应该在编写每个有可能抛出异常的函数时都应当加入异常规格说明。需要特别注意的是:如果函数的执行错误所抛出的异常对象类型并未在函数的异常规格说明中声明,则会导致系统函数unexpected()被调用,以便解决未预见错误引起的异常。unexpected()是由函数指针实现函数调用的,因此我们可通过改变函数指针所指向的函数执行代码的入口地址来改变相对应的处理操作。这就意味着用户定义自己特定的对未预见错误的处理方法(系统的缺省处理操作将最终导致程序终止运行)。实现自定义处理方法设定是调用系统函数set_unexpected(…)完成的。该函数的原型如下:typedefvoid(*unexpected_function)();unexpected_functionset_unexpected(unexpected_functionunexp
本文标题:性的最有效方法之一C语言实现出错处理的方法是
链接地址:https://www.777doc.com/doc-2437803 .html