An elegant Exception Handling Mechanism in the C Language

Source: Internet
Author: User
Tags signal handler
In the previous article, the Goto statements in C language were elaborated in depth. In fact, The Goto statements are the most primitive support for exception handling programming in process-oriented and structured programming languages. Later, in order to better and more easily support the exception handling programming mechanism, programmers can write more efficient and friendly code modules with the exception handling mechanism in C language programs. Therefore, there is a more elegant Exception Handling Mechanism in the C language, namely, the setjmp () function and the longjmp () function.

In fact, this exception handling mechanism is not part of the C language, but two very skillful library functions implemented in the C standard library, maybe most C programmers are familiar with it, and the exception handling mechanism for programs is provided by combining the setjmp () function and longjmp () function, it has been widely used in many library systems developed in C language, such as JPG Parsing Library and encryption and decryption library.

Maybe this exception handling mechanism in C language is more thorough in concept than that in GOTO statements. The hero who has always been rigorous in style and prefers to answer all questions will certainly not put it
Abandon the comprehensive and in-depth research on this exception handling mechanism. Let's take a look.

What is the function of setjmp?

As mentioned above, setjmp is a function provided in the C standard library. It is used to save the current running status of the program. Its function prototype is as follows:

Int setjmp (jmp_buf env );

This is a comment on it in msdn, as follows:

The setjmp function is used to save the runtime stack environment of the program. You can call the longjmp function to restore the previously saved program stack environment. When setjmp and longjmp are used together, they provide a mechanism to implement "non-local jump" ("non-local goto") in the program. In addition, this mechanism is often used for implementation to pass the control flow of the program to the error processing module; or the normal return statement is not used in the program, or the normal call of the function, so that the program can be restored to the previous call routine (that is, the function.

When the setjmp function is called, the current stack environment of the program is saved to the Env parameter. When longjmp is called, the previous environment is restored based on the previously saved variable, in addition, the current program control flow is returned to the execution point of the previously called setjmp. In this case, in the following control flow routine, all the variables that can be accessed (except the register type variables) include the variables owned by the longjmp function during call.

Setjmp and longjmp do not support object-oriented semantics in C ++. Therefore, in the C ++ program, use the exception handling mechanism provided by C ++.

Now, I have a very emotional understanding of setjmp. I will not comment too much on it for the time being, so I will continue to look at the longjmp function.

What is the function of longjmp?

Similarly, longjmp is a function provided in the C standard library. It is used to restore the stack environment of program execution. Its function prototype is as follows:

Void longjmp (jmp_buf ENV, int value );

This is a comment on it in msdn, as follows:

The longjmp function is used to restore the stack environment stored when the setjmp function called in the previous program is called. When setjmp and longjmp are used together, they provide a mechanism to implement "non-local jump" ("non-local goto") in the program. In addition, this mechanism is often used for implementation. It transfers the control flow of the program to the error processing module, or does not use normal return statements or normal function calls, enables the program to be restored to a previous call routine (that is, a function.

When the setjmp function is called, the current stack environment of the program is saved to the Env parameter. When longjmp is called, the previous environment is restored based on the previously saved variable, therefore, the current program control flow is returned to the execution point when setjmp was previously called. In this case, the value is returned by the setjmp function, and the program continues to be executed. In addition, in the following control flow routine, all the variables that can be accessed by it (except for register variables) include the variables owned by the longjmp function during call; variables of the register type are unpredictable. The value returned by the setjmp function must be a non-zero value. If the value transmitted by longjmp is 0, the value actually returned by setjmp is 1.

Call longjmp before calling the setjmp function to return the result. Otherwise, the result is unpredictable.

Observe the following rules or restrictions when using longjmp:
· Do not assume that variables of the Register Type will always remain unchanged. After longjmp is called, variables of the Register Type in the routine will not be restored in the control flow returned by setjmp.
· Do not use the longjmp function to output the control flow from an interrupt processing routine unless the caught exception is a floating point number exception. In the latter case, if the program first initializes the floating point package by calling the _ fpreset function, it can return data from the Interrupt Processing Routine Through longjmp.
· In the C ++ program, be careful with the use of setjmp and longjmp. The use of setjmp and longjmp should not support object-oriented semantics in C ++ well. Therefore, in C ++ programs, the exception handling mechanism provided by C ++ is more secure.
Combine setjmp and longjmp. It turns out to be so powerful!
Now you have a very perceptual knowledge of setjmp and longjmp. Next, let's look at an example and analyze it from this example. The sample code is as follows (from msdn ):

/* Fpreset. C: This program uses signal to set up
* Routine for handling floating-point errors.
*/

# I nclude <stdio. h>
# I nclude <signal. h>
# I nclude <setjmp. h>
# I nclude <stdlib. h>
# I nclude <float. h>
# I nclude <math. h>
# I nclude <string. h>

Jmp_buf mark;/* address for long jump */
Int fperr;/* Global error number */

Void _ cdecl fphandler (INT Sig, int num);/* prototypes */
Void fpcheck (void );

Void main (void)
{
Double N1, N2, R;
Int jmpret;
/* Unmask all floating-point exceptions .*/
_ Control87 (0, _ mcw_em );
/* Set up floating-point error handler. The Compiler
* Will generate a warning because it expects
* Signal-handling functions to take only one argument.
*/
If (signal (sigfpe, fphandler) = sig_err)

{
Fprintf (stderr, "couldn't set sigfpe/N ");
Abort ();}

/* Save stack environment for return in case of error. First
* Time Through, jmpret is 0, so true conditional is executed.
* If an error occurs, jmpret will be set to-1 and false
* Conditional will be executed.
*/

// Note that the following statement is used to save the current running state of the program.
Jmpret = setjmp (Mark );
If (jmpret = 0)
{
Printf ("Test for invalid operation -");
Printf ("enter two numbers :");
Scanf ("% lf", & N1, & N2 );

// Note that the following statement may be abnormal,
// If the 2nd variables input from the terminal are 0
R = N1/N2;
/* This won't be reached if error occurs .*/
Printf ("/n % 4.3g/% 4.3g = % 4.3g/N", N1, N2, R );

R = N1 * N2;
/* This won't be reached if error occurs .*/
Printf ("/n % 4.3g * % 4.3g = % 4.3g/N", N1, N2, R );
}
Else
Fpcheck ();
}
/* Fphandler handles sigfpe (floating-point error) interrupt. Note
* That this prototype accepts two arguments and that
* Prototype for signal in the run-time library expects a signal
* Handler to have only one argument.
*
* The second argument in this signal handler allows processing
* _ Fpe_invalid, _ fpe_overflow, _ fpe_underflow, and
* _ Fpe_zerodivide, all of which are Microsoft-specific symbols
* That augment the information provided by sigfpe. The Compiler
* Will generate a warning, which is harmless and expected.

*/
Void fphandler (INT Sig, int num)
{
/* Set global for outside check since we don't want
* To Do I/O in the handler.
*/
Fperr = num;
/* Initialize floating-point package .*/
_ Fpreset ();
/* Restore calling environment and jump back to setjmp. Return
*-1 so that setjmp will return false for conditional test.
*/
// Note that the function of the following statement is to restore the state of the program saved by the previous setjmp.
Longjmp (mark,-1 );
}
Void fpcheck (void)
{
Char fpstr [30];
Switch (fperr)
{
Case _ fpe_invalid:
Strcpy (fpstr, "Invalid Number ");
Break;
Case _ fpe_overflow:
Strcpy (fpstr, "overflow ");

Break;
Case _ fpe_underflow:
Strcpy (fpstr, "underflow ");
Break;
Case _ fpe_zerodivide:
Strcpy (fpstr, "divide by zero ");
Break;
Default:
Strcpy (fpstr, "other floating point error ");
Break;
}
Printf ("error % d: % s/n", fperr, fpstr );
}

The program running result is as follows:
Test for invalid operation-enter two numbers: 1 2

1/2 = 0.5

1*2 = 2

The program running result above is normal. In addition, the running result of the program is as follows:
Test for invalid operation-enter two numbers: 1 0
Error 131: divide by zero

Haha! An exception occurred while running the program (Division by 0), and this exception was caught by the program's predefined Exception Handling Module. Amazing! Never underestimate it. It can be a C language program.

Analyze setjmp and longjmp

Now, let's analyze the execution process of the above program. Of course, this article mainly analyzes the transfer control process of the program running in case of exceptions. Due to the limited length of the article, we simplify irrelevant code during analysis, which makes it easier to understand the execution process of the control flow. As shown in.

Haha! Whether the execution process of the program is clear at a glance, the most important of which is the call processing of setjmp and longjmp functions. Let's analyze them separately.

When the program runs to step 2, call the setjmp function. This function will save some status information about the current running of the program, mainly the values of some system registers, such as SS, Cs, EIP, eax, registers such as EBX, ECx, EDX, and eflags, especially the EIP value, because it stores the execution point of a program. This information is stored in the mark variable, which is a variable of the special struct type defined in the C standard library.

After the setjmp function is called to save the program status, the function returns 0, and the program is executed in step 3 and step 4. When the variable N2 is 0 during statement execution in Step 4, a floating point number calculation exception occurs, causing the control flow to be transferred to the fphandler function, that is, to Step 5.

Then, run to step 6 and call the longjmp function. This function is internally saved from the previous setjmp program status, that is, in the mark variable, to restore the value of the previous system register. So we entered Step 7. Note that this is very interesting. In fact, after calling the longjmp function, the program control flow (especially the EIP value) once again, it entered the internal processing of the setjmp function, but this time the value returned by setjmp is the 2nd parameters passed in when the longjmp function is called, that is,-1, therefore, the program is executed in step 1.

Summary

Unlike the GOTO statement, the combined call of setjmp () and longjmp () in C language provides a more elegant Exception Handling Mechanism for programmers. It has the following features:

(1) goto can only achieve local jump, while the combination of setjmp () and longjmp () can effectively implement non-local (remote) Jump of program control flow;

(2) Unlike the GOTO statement, the combined use of setjmp () and longjmp () provides a true exception handling mechanism. For example, it can effectively define the module area protected by monitoring (similar to the region defined by the try keyword in C ++ ); at the same time, it can also effectively define the Exception Handling Module (similar to the region defined by the catch keyword in C ++). In addition, it can call the longjmp function during program execution, easily throw an exception (similar to the throw keyword in C ++ ).

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.