Error handling is a problem that needs to be solved in any language, and there is a mechanism for handling errors only if 100% is not guaranteed to operate correctly. Exception handling is one of the error handling methods.
1 Process activity record (active record)
Each time a function call is made in C, a structure called AR is prepared on the stack, leaving aside the specifics of the specifics of the compiler implementation, as shown in the basic structure of the AR.
Whenever a statement of a function call is encountered, the C compiler generates assembly code to allocate the AR on the stack. For example, the following C code:
void a(int i){ if(i==0){ 1; } else { printf("i = %d \n", i); }}int main(intchar** argv){ a(1);}
When the program executes to the printf () statement, the AR layout on the stack is as follows:
2 using setjmp and longjmp to manipulate AR, complete any jump
So how to manipulate AR, one possible way is to calculate according to the address of the local variable, for example, the above a function, the current AR address when executing a function is the address of the parameter I offset 8 bytes, that is ((char*) &i)-8. However, different C compilers, as well as different hardware platforms, will have different AR structure layouts, and even on some platforms, AR will not be stored in the stack at all. So this way of manipulating AR is not universal.
For this reason, the C language provides a unified method for manipulating AR through library functions, which is the setjmp and LONGJMP functions.
int setjmp(jmp_buf jb);voidint r);
SETJMP is used to save the current AR to JB variable;
Instead, longjmp is used to set the current AR to JB, and jumps to call setjmp (), followed by the first statement. The result is the equivalent of returning to SETJMP () just after execution, but secretly modified the setjmp return value.
SETJMP () always returns 0 on the first call, and the return value after jumping through longjmp (JB,R) is always modified to r, and R cannot be 0. In this way, it is easy to determine whether longjmp () is causing the jump to execute to this based on the return value of setjmp ().
The setjmp/longjmp is mostly popped out of nested function calls.
#include <stdio.h>#include <setjmp.h>JMP_BUF JB;voidA ();voidb ();voidC ();intMain () {if(setjmp (JB) = =0) {a (); }printf("after a (); \ n ");return 0;}voidA () {B ();printf("A () is called\n");}voidB () {C ();printf("B () is called\n");}voidC () {printf("C () is called\n"); LONGJMP (JB,1);}
In C () you can jump directly to main (), in fact longjmp does not limit the destination of the jump, you can jump to any location and restore the current stack environment (stack balance).
3 implementing exception Handling in C language
Exception handling is a way of error handling, and the more common method of error handling in C is to detect function return values.
#include <stdio.h>intF1 () {if(1/ * Execute correctly * /) {return 1; }Else{return-1; }}intF2 () {if(0/ * Execute correctly * /) {return 1; }Else{return-1; }}intMain () {if(F1 () <0){printf("error handling 1\n");Exit(1); }if(F2 () <0){printf("error handling 2\n");Exit(2); }return 0;}
The code above shows the common C-language error handling methods. In rigorous software development, it is necessary to detect the errors that can occur with each function call and do the corresponding processing. The result is a lengthy and cumbersome code. In order to deal with errors uniformly, c++,c#,java and other modern languages introduced the exception handling mechanism. The same functionality of C + + code is probably as follows:
#include <stdio.h>classex1{};classex2{};voidF1 () {printf("Enter F1 () \ n");if(0/ * Execute correctly * /){ }Else{ThrowEx1 (); }printf("Exit F1 () \ n");}voidF2 () {printf("Enter f2 () \ n");if(1/ * Execute correctly * /) { }Else{ThrowEX2 (); }printf("Exit f2 () \ n");}intMain () {Try{F1 (); F2 (); }Catch(Ex1 &ex) {printf("Handling Error 1\n");Exit(1); }Catch(Ex2 &ex) {printf("Handling Error 2\n");Exit(2); }return 0;}
Program output:
进入f1()处理错误1
As you can see, exception handling makes your code look neater, logically coded together, and error-handling code together. The statement after the throw is no longer executed, and the execution stream jumps directly to the catch block corresponding to the nearest try.
It can be speculated that
- Throw is responsible for two things: (1) complete jump; (2) Restore stack ar;
- Try is responsible for saving the current AR
It can be seen that this is basically equivalent to setjmp/longjmp. So it can be approximated in C.
#include <stdio.h>#include <stdlib.h>#include <setjmp.h>JMP_BUF JB;voidF1 () {printf("Enter F1 () \ n");if(0/ * Execute correctly * /){ }Else{longjmp (JB,1); }printf("Exit F1 () \ n");}voidF2 () {printf("Enter f2 () \ n");if(1/ * Execute correctly * /) { }Else{longjmp (JB,2); }printf("Exit f2 () \ n");}intMain () {intr = setjmp (JB);if(r==0) {F1 (); F2 (); }Else if(r==1){printf("Handling Error 1\n");Exit(1); }Else if(r==2){printf("Handling Error 2\n");Exit(2); }return 0;}
Of course, the complete exception handling is far more complex than the code here, you need to consider the nesting of exceptions, here just give the simplest idea.
4 Do not use setjmp and longjmp in C + +
C + + provides direct support for exception handling. Unless you have a very special need, do not re-implement your own exception mechanism, in particular, it is necessary to note that simple invocation of setjmp/longjmp may cause problems. Such as
#include <stdio.h>#include <stdlib.h>#include <setjmp.h>classmyclass{ Public: MyClass () {printf("Myclass::myclass () \ n");} ~myclass () {printf("Myclass::~myclass () \ n");}}; JMP_BUF JB;voidF1 () {MyClass obj;printf("Enter F1 () \ n");if(0/ * Execute correctly * /){ }Else{longjmp (JB,1); }printf("Exit F1 () \ n");}voidF2 () {printf("Enter f2 () \ n");if(1/ * Execute correctly * /) { }Else{longjmp (JB,2); }printf("Exit f2 () \ n");}intMain () {intr = setjmp (JB);if(r==0) {F1 (); F2 (); }Else if(r==1){printf("Handling Error 1\n");Exit(1); }Else if(r==2){printf("Handling Error 2\n");Exit(2); }return 0;}
g++ compile, program output:
MyClass::MyClass()进入f1()处理错误1
VC + + compilation, program output:
MyClass::MyClass()进入f1()MyClass::~MyClass()处理错误1
LONGJMP () before jumping local objects may not be destructor (g++), but also may be destructor (VC + +), the C + + standard does not explicitly require this. This code that relies on a specific compiler version should be avoided.
The Throw keyword of C + + itself can strictly guarantee the construction of local object and the paired call of the destructor.
5 dialectical view of exception handling
In order to implement exception handling, the C + + compiler must do more work for this purpose, and it will inevitably lead to more information being stored directly or indirectly in AR, and generate the assembly code to manipulate the information, resulting in a decrease in operational efficiency.
On the other hand, there are already a large number of non-strict use of exception handling C + + function library and class library, compatible with C library is no exception concept, the burden of history makes C + + difficult to fully use exception handling. In this respect, Java and C # start from the beginning, the important libraries have implemented the standard exception handling specification, completely using the exception mechanism is feasible.
Interestingly, C++11 removes the exception specification in the standard and adds the NOEXCEPT keyword to declare that a function does not throw an exception, and the visible exception is not so popular.
The C + + compiler also provides an option to disable exceptions, the following is the method of disabling exceptions in VC + +.
However, C + + STL widely used exceptions, so in fact, the use of STL C + + program is not possible to disable the exception, if there is no stl,c++ and what advantages? C + + moves forward in a constant conflict.
Using setjmp and longjmp to do exception handling in C language