MST
星途 面试题库

面试题:C标准库中setjmp和longjmp函数的深度剖析与应用陷阱

深入解释C标准库中setjmp和longjmp函数的工作原理,包括它们如何保存和恢复程序的执行环境。列举至少两个在使用这两个函数时可能遇到的微妙陷阱,并说明如何避免这些陷阱,同时给出相关代码示例。
34.1万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

setjmp 和 longjmp 函数工作原理

setjmp

setjmp 函数用于保存当前调用环境的上下文,以便后续 longjmp 可以恢复到此位置继续执行。它在调用点记录寄存器的值、栈指针等信息到一个 jmp_buf 类型的缓冲区中。

int setjmp(jmp_buf env);

  • env 是一个 jmp_buf 类型的数组,用于保存上下文环境。
  • 返回值:首次调用 setjmp 时返回 0,通过 longjmp 跳回时返回非零值。

longjmp

longjmp 函数用于恢复由 setjmp 保存的上下文环境,使程序跳回到 setjmp 调用处继续执行。

void longjmp(jmp_buf env, int val);

  • env 是之前由 setjmp 填充的 jmp_buf 缓冲区。
  • val 是传递给 setjmp 的返回值,setjmp 会以这个值作为返回值,除非 val0,此时 setjmp 将返回 1

保存和恢复执行环境

  1. 保存setjmp 保存当前栈指针、通用寄存器等执行环境到 jmp_buf 中。这使得程序状态被记录下来。
  2. 恢复longjmpjmp_buf 中取出保存的执行环境,恢复栈指针和寄存器的值,程序继续从 setjmp 调用处执行,就好像函数调用链被“重置”到了 setjmp 调用点。

使用陷阱及避免方法

陷阱一:局部变量的状态问题

  1. 问题描述:当使用 longjmp 跳回时,局部变量的值可能不是预期的。因为 longjmp 恢复的是 setjmp 调用时的栈状态,自 setjmp 调用后修改的局部变量不会被恢复。
  2. 避免方法:将需要在 longjmp 后保持状态的变量声明为 volatile 或使用全局变量。
  3. 代码示例
#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void second() {
    int a = 5;
    // 如果没有 volatile,a 的值可能不是预期的 10
    volatile int b = 10; 
    longjmp(env, 1);
    a = 7; // 此语句不会执行到
    b = 12; // 此语句不会执行到
}

void first() {
    if (setjmp(env) == 0) {
        second();
    } else {
        // 这里 a 可能仍为 5,因为 longjmp 不会恢复自 setjmp 调用后修改的局部变量
        // 但 b 会是 10,因为 b 是 volatile
        printf("After longjmp: a = %d, b = %d\n", a, b); 
    }
}

int main() {
    first();
    return 0;
}

陷阱二:栈展开和资源清理

  1. 问题描述longjmp 不会像正常函数返回那样进行栈展开,因此可能导致资源(如打开的文件、分配的内存)没有正确清理。
  2. 避免方法:在 setjmp 之前手动管理资源的清理,或者使用 atexit 注册清理函数。
  3. 代码示例
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
#include <unistd.h>

jmp_buf env;
FILE* file;

void cleanup() {
    if (file) {
        fclose(file);
    }
}

void second() {
    file = fopen("test.txt", "w");
    if (!file) {
        longjmp(env, 1);
    }
    // 文件操作
    fclose(file);
}

void first() {
    atexit(cleanup);
    if (setjmp(env) == 0) {
        second();
    } else {
        printf("File open failed, exiting.\n");
    }
}

int main() {
    first();
    return 0;
}