Dr. Roger Ianjamasimanana

The setjmp and longjmp functions in C

By Dr. Roger Ianjamasimanana

1. A rustic mechanism for direct branching in C

The C language provides a somewhat primitive mechanism for performing a “direct branch” between two functions, thereby bypassing the usual function-call-and-return sequence. The most common use of this feature is to “escape” from a series of nested function calls in the event of an abnormal or exceptional situation, without having to return through each intermediate level.

This mechanism relies on the macro setjmp and the function longjmp. The first is used to record a specific location in the program, and the second can, from anywhere in the program, jump directly to that recorded location—ignoring any intermediate function returns. Before discussing these two macros in detail, let’s look at a brief example.

Consider the following statements:

#include <setjmp.h>
    ...
    jmp_buf env; /* A type defined in setjmp.h for saving the environment */
    /* The variable 'env' must be accessible to any longjmp calls */
    
    if (setjmp(env) == 0)
    {
        /* instructions_1 */
    }
    else
    {
        /* instructions_2 */
    }

When setjmp(env) is called, the information stored in the env variable includes:

  1. The address of the call to setjmp (note that it’s not the address of the subsequent instruction). This information is used later if longjmp is called.
  2. The current environment—various details required to resume execution properly at the saved location.

Additionally, the setjmp macro always returns a value of 0 on a direct call. In this example, that means instructions_1 will be executed.

Later, from anywhere in the program, if you encounter:

longjmp(env, 1);

this will cause a return to the address saved in env and a restoration of the current state. In other words, execution will resume again at the evaluation of:

if (setjmp(env) == 0)

But this time, since the call to setjmp was made indirectly (i.e., via longjmp), the standard dictates that its return value will be the second parameter passed to longjmp (in this case, 1). Therefore, instructions_2 will run in this scenario.

Naturally, the variable env must be accessible both from the site of the setjmp call and from wherever you might call longjmp to jump back to that saved point.

2. The setjmp macro and the longjmp function

2.1 Prototypes and their roles

Prototype (from setjmp.h)

int setjmp (jmp_buf env)
  • env: an array of type jmp_buf that is used to save the environment and the address to which you may later return with longjmp.
  • Return value: returns 0 if the call to setjmp is direct, or a nonzero value if the call occurs indirectly via longjmp.

Prototype (from setjmp.h)

void longjmp (jmp_buf env, int state)
  • env: this array provides the environment to restore, as well as the address to which execution will jump.
  • state: if this value is nonzero, it will be used as the return value of the setjmp call. If it is 0, the behavior is as though the value were 1.
  • Return value: this function call does not truly return in the usual sense, since invoking longjmp triggers a branch. Once longjmp executes, everything continues as if setjmp had just returned.

When longjmp is called, it restores the environment previously saved by setjmp (using the contents of env). Execution then resumes at the location recorded earlier, as though state were the return value from setjmp. To prevent an unintended loop, the ANSI standard dictates that if state is 0, longjmp will supply a return value of 1 instead of 0.

✍️
Remark

The type that jmp_buf corresponds to is always some form of array, although the exact type of its elements is not specified. Whenever setjmp and longjmp are not in the same function, the memory location used for storing the environment must not be automatic in storage class. It can be either a static variable or a dynamically allocated location.

2.2 Usage constraints

The mechanism used for these non-local jumps requires comparing the return value of setjmp against an integer. Therefore, the standard naturally imposes conditions on how setjmp can be called. It can only appear in the following contexts:

  1. It can serve as the complete controlling expression of a loop, selection, or switch statement, such as:
  2. if (setjmp(env)) ...
    while (setjmp(env)) ...
  3. It can appear as an operand of the == or != operator, where the other operand is also an integer, and the resulting comparison forms the complete controlling expression of a loop, selection, or switch statement. For example:
  4. if (setjmp(env) == 2) ...
    while (setjmp(env) != 5) ...
  5. It can appear as the sole operand of the logical NOT operator (!), where the resulting expression again constitutes the complete controlling expression of a loop, selection, or switch. For instance:
  6. if (!setjmp(env)) ...
    while (!setjmp(env)) ...
  7. It can stand alone as an expression statement:
  8. setjmp(env);

The last case is of limited interest because in such an expression statement, you cannot distinguish a direct call to setjmp from an indirect one triggered by longjmp.

Note that the following examples are not valid under the standard, even if some implementations might accept them:


if (2*setjmp(env) < 12) ...   /* invalid */
if (setjmp(env) < 3) ...      /* invalid */
if (!(res = setjmp(env))) ... /* invalid */ 

These constraints ensure that the return value of setjmp is only used in straightforward comparisons or control expressions, preventing more complex manipulations that could introduce undefined behavior.


3. When to use setjmp and longjmp?

You might use setjmp and longjmp when you want to:

  • Avoid deep error-checking paths in heavily nested function calls.
  • Implement a simplified exception-handling mechanism in C.
  • Handle critical errors or special exit conditions from a library or system function.

However, most situations can be handled with regular function returns or error-handling strategies. Non-local jumps can skip resource cleanup if not used carefully.


4. Practical use of setjmp and longjmp in C

Below is a simple demonstration of setjmp and longjmp where a function triggers an error condition, and we jump back to handle that error.

#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void do_something_risky(int error_trigger) {
    if (error_trigger) {
        printf("Error encountered! Jumping back...\n");
        longjmp(env, 42); 
        // Execution continues from setjmp in main, returning 42.
    }
    printf("Function completed without error.\n");
}

int main(void) {
    int val = setjmp(env);
    if (val == 0) {
        // First time setjmp is called, val == 0
        printf("Calling do_something_risky with error trigger.\n");
        do_something_risky(1);  // Simulate error
        printf("This line will never be reached if error_trigger==1.\n");
    } else {
        // Non-zero return means we returned via longjmp
        printf("We jumped back with value: %d\n", val);
    }
    return 0;
}

In this example, the call to longjmp(env, 42) interrupts the normal flow, causing setjmp in main to return 42. The line after do_something_risky in main is skipped.


5. Considerations and cautions

  • Stack unwinding: C does not automatically handle resource cleanup (unlike C++ destructors). If you jump past code responsible for releasing resources, you may cause leaks or other issues.
  • Local variables: any local variables (especially in registers) may have unpredictable values after a jump, depending on how the compiler optimizes them. Limit usage to only the needed scenarios.
  • Portability: although setjmp/longjmp are part of the C standard, certain behaviors may differ slightly across compilers or platforms. Always test carefully.

6.Multiple jump points

You can maintain multiple jump points if you store multiple jmp_buf buffers. For instance, you might have:


jmp_buf mainEnv, backupEnv;

int main(void) {
    // ...
    setjmp(mainEnv);
    // ...
    setjmp(backupEnv);
    // ...
    // Some logic
    longjmp(mainEnv, 1);
    // ...
}

However, this pattern can get complicated quickly. It’s generally a good idea to keep the usage of setjmp and longjmp as simple as possible.


feature-top
Readers’ comment
feature-top
Log in to add a comment
🔐 Access