In C language development, seinterfaces Fault segment errors must be old acquaintances, errors free memory, access to dangling Pointers, memory out of line, there will always be one for you. Typically, when a segment error occurs, the application throws an exception and then crashes completely, the user crashes, and the developer seems to be embarrassed to say “It’s my fault, SORRY”.

In fact, there are some things you can do to make things better than just watching them happen. For example, you can make things less awkward.

Two functions

In fact, SIGSEGV signals are thrown when a memory exception occurs, and the operating system stops the process by default. If we capture the SIGSEGV signal of the process, we can decide for ourselves what to do when a segment error occurs. If you want result signal processing, you just need to use the signal function to do so. However, this only allows the process to exit without embarrassment. If you want the process to recover from an error, you need two helpers:

  • int sigsetjmp(sigjmp_buf env, int savesigs)
  • void siglongjmp(sigjmp_buf env, int savesigs)

Sigsetjmp saves all register states to env at the time of the call, and you can use siglongjmp and env to jump back to where you were at some point. Sigsetjmp takes a snapshot of the CPU, and siglongjMP restores this snapshot to the CPU as if it had never been interrupted. The effect is very similar to the goto statement, goto can only jump within functions, siglongjmp can “goto” between functions.

Note that sigsetjmp is executed twice (if siglongjmp is called), we can determine which execution is currently performed by returning the value:

  • The value 0 is returned for the first snapshot execution.
  • “Jump” back from siglongJMP, return value! 0;

Recovering from an exception

For a piece of code that might have an exception:

char * ptr = NULL;
char c = *ptr;
Copy the code

Obviously, execution to the second line triggers the SIGSEGV signal.

We can register SIGSEGV signal processing callbacks before these two lines of code to take over the system default behavior; Recovery from an exception can be achieved by calling sigsetjMP in front of the exception code and cleverly arranging siglongJump into the signal processing callback function.

The first time flag = sigsetjmp(env, 1), flag is 0, the exception code is executed, when accessing the NULL pointer triggers a segment error, the signal processing callback takes over, Flag = sigsetjmp(env, 1), flag is not 0, so we know that we should execute the recovery code printf(“exception! \n”) and continue with the following code.

A little tricks

To summarize, how to implement try-catch in C:

  • Before the exception code block, the CPU register state is reserved and the path after it is determined according to flag.
  • If flag is 0, the abnormal code is run for the first time.
  • If flag is not 0, an exception has occurred and the signal processing callback is returned to run the recovery code.

Let’s rewrite some of the previously mentioned functions into macros that can look like this:

    TRY
        char * a = NULL;
        *a = 'x';
    /*catch segement falut*/
    CATCH_ALL
        /* segement fault catch */
        printf("exception! \n");
    ENDTRY
Copy the code

There is only segment error catching implemented here, and actually more signal types can be added to support more exception catching.

You can see the complete implementation in C_Exception, which also implements a division by zero exception and a user-thrown exception.