C 语言 <code>&lt;setjmp.h&gt;</code> 详解:setjmp、longjmp 的用法、场景与限制

Nora
Nora
发布时间:2026-08-22| 169人浏览过
原创

前言

在 C 语言里,&lt;setjmp.h&gt; 提供了一种少见但很强的控制流能力:程序可以不按常规返回路径退出,而是直接跳回先前保存的执行点。它常被拿来处理错误恢复,但真正难点不在函数怎么写,而在什么时候该用、什么时候该避开,这也是本文重点要说明的判断依据。

<setjmp.h> 在 C 标准库里属于比较特殊的一组接口:它不按函数一层层返回,而是允许程序直接跳回先前保存过的执行点。很多人第一次接触它时,容易把它理解成“C 语言版异常处理”,但真正适不适合用,关键在于你是否清楚它保存了什么、会跳到哪里,以及会带来哪些限制。

这篇文章按“机制是什么、代码怎么跑、适合放在哪、有哪些坑”四个问题来梳理 <setjmp.h>。看完之后,你可以判断它是否适合当前项目中的错误恢复场景,而不是只记住两个函数名。

什么是 <setjmp.h> 的非局部跳转

<setjmp.h> 主要用于实现非局部跳转(non-local jump)。和普通的 return 不同,它可以让程序跳回到之前某个已经保存好的执行位置,而不需要沿着当前调用栈逐层返回。

这套机制通常由三个部分组成:

  • jmp_buf:用于保存跳转上下文。
  • setjmp:在当前位置记录上下文。
  • longjmp:在后续某处恢复上下文并跳回。

源文里提到,setjmp 保存的信息通常包括 CPU 寄存器状态、栈指针等执行现场。可以把它理解成“给程序当前状态打一个可回退的标记”。

setjmp、longjmp 和 jmp_buf 是怎么配合的

setjmp 负责记录当前执行点

setjmp 一般接收一个 jmp_buf 变量作为参数,用来保存当前上下文。第一次执行到这里时,程序会继续向下运行;如果以后通过 longjmp 跳回这里,那么 setjmp 会再次“返回”,但这次返回值不再是首次执行时的结果,而是来自 longjmp 的第二个参数。

展示 setjmp、longjmp 与 jmp_buf 三者配合关系的白底流程信息图
非局部跳转的执行路径用流程图梳理 setjmp 首次返回与。
// www.ja vascriptcn.com code example
#include <setjmp.h>

jmp_buf env;
int x = 3;

void func(void) {
    x *= 10;
    if (x == 30)
        longjmp(env, 1);  // 跳转到 setjmp 处
}

int main() {
    int ret = setjmp(env);
    if (ret == 0) {
        func();
    } else {
        printf("longjmp occurredn");
    }
    return 0;
}

这段代码里,setjmp(env) 首次执行后,ret == 0,于是进入 func()。当 func() 中调用 longjmp(env, 1) 后,程序不会继续从 longjmp 的下一行执行,而是直接回到 setjmp(env) 所在的位置,此时 ret 变成 1。

longjmp 负责恢复上下文并返回指定值

longjmp 的两个参数分别是目标上下文和一个整型返回值。它的效果是恢复之前由 setjmp 保存的执行现场,并让对应的 setjmp 重新返回。

// www.ja vascriptcn.com code example
#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void func(void) {
    longjmp(env, 1);  // 跳转到 setjmp 处
}

int main() {
    int ret = setjmp(env);
    if (ret == 0) {
        func();
    } else {
        printf("longjmp occurredn");
    }
    return 0;
}

这个例子更直接地展示了流程:

  1. 先在 main() 中执行 setjmp(env)。
  2. 首次返回时 ret == 0,调用 func()。
  3. func() 内执行 longjmp(env, 1)。
  4. 程序跳回 main() 中的 setjmp(env),这次 ret == 1。

因此,判断“当前是首次执行,还是从 longjmp 跳回来”,最常见的方法就是检查 setjmp 的返回值。

它适合放在哪些场景里

1. 模拟异常处理

在没有原生异常机制的 C 语言里,setjmp 和 longjmp 常被用来做简化版的异常处理。思路是:先在上层函数设置一个“错误返回点”,一旦下层函数遇到严重错误,就直接跳回这个位置,统一处理。

// www.ja vascriptcn.com code example
#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void func(void) {
    int x = 1 / 0;  // 引发除零错误
    longjmp(env, 1);  // 跳转到 setjmp 处
}

int main() {
    int ret = setjmp(env);
    if (ret == 0) {
        func();
    } else {
        printf("Exception occurredn");
    }
    return 0;
}

不过这里需要单独提醒:示例中的 int x = 1 / 0; 本身就是未定义或异常行为,实际项目里不应该用它作为常规演示方式。真正可借鉴的是“检测到错误后执行 longjmp 跳回上层”这个思路,而不是制造除零操作本身。

2. 做有限度的错误恢复

在一些资源受限的嵌入式系统或实时系统里,开发者有时会用它快速跳出多层调用,回到某个已知状态,再执行恢复逻辑。这比层层返回错误码更直接,但前提是控制流清晰,而且跳转边界可控。

// www.ja vascriptcn.com code example
#include <stdio.h>
#include <setjmp.h>

jmp_buf env;

void func(void) {
    // 模拟错误
    longjmp(env, 1);  // 跳转到 setjmp 处
}

int main() {
    int ret = setjmp(env);
    if (ret == 0) {
        func();
    } else {
        printf("Error recoveredn");
    }
    return 0;
}

这类用法的重点不是“跳得快”,而是“跳回去以后还能不能安全收尾”。如果项目对资源释放、状态一致性要求很高,就不能只看到它省掉了多少层 return。

使用前必须知道的限制和风险

有额外性能开销

由于 setjmp 和 longjmp 需要保存、恢复执行上下文,它们天然会带来额外开销。在性能敏感路径里,尤其是高频调用代码中,应当谨慎使用。

展示 <setjmp.h> 适用场景与风险边界的白底对比信息图
适用场景与使用边界把常见使用场景和几个关键限制放在同一张图里,方便读者快速判断该不该在项目中使用这组接口。

不适合当成通用多线程方案

源文明确提到,setjmp 和 longjmp 不适用于多线程环境。原因并不只是“线程多了更复杂”,而是每个线程都有独立的执行栈和上下文,非局部跳转一旦跨越错误边界,就很容易进入不可预期状态。

不能跨不同调用栈随意使用

这组接口只能在对应的调用栈范围内工作,不能把它理解成任意位置都能回跳的“全局传送门”。一旦脱离原本那段有效的栈环境,再去恢复旧上下文,结果就可能失控。

错误使用会触发未定义行为

源文还提到:在调用 setjmp 后,如果在返回前执行了任何非本地跳转,结果是未定义的。对于这类接口,最大的风险往往不是“编译不过”,而是代码表面能运行、实际行为却不可靠。

换句话说,<setjmp.h> 适合做受控场景下的跳转恢复,不适合拿来替代清晰、可维护的常规控制流设计。

怎么判断项目里该不该用

如果你的目标只是跨越多层调用、集中处理某一类严重错误,<setjmp.h> 确实能提供一种直接手段;它在某些底层库、嵌入式场景或历史 C 项目中仍然有现实价值。

但如果你希望的是可维护、可推理、便于多人协作的错误处理流程,那么优先考虑明确的返回值、状态码设计和资源清理路径,通常会比引入非局部跳转更稳妥。是否使用 setjmp/longjmp,关键不在于“能不能跳”,而在于“跳完之后程序状态是否仍然可控”。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。