You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

longjmp/setjmp跨平台差异:Windows异常Linux正常运行问题

setjmp/longjmp跨平台行为差异:Windows触发异常的原因

先看你提供的代码:

#include <csetjmp>
#include <iostream>
using namespace std;
jmp_buf jb1;
jmp_buf jb2;
auto bar() {
    if (setjmp(jb2) == 0) {
        longjmp(jb1, 1);
    } else {
        longjmp(jb1, 2);
    }
}
void foo() {
    bar();
}
void testjmp() {
    auto i = setjmp(jb1);
    if (i == 0) {
        foo();
    } else {
        cout << "i = " << i << endl;
    }
    i = setjmp(jb1);
    if (i == 0) {
        longjmp(jb2, -1);
    } else {
        cout << "i = " << i << endl;
    }
}
int main() {
    testjmp();
    return 0;
}

这个代码在Linux能正常运行,但Windows上触发异常,核心问题在于两个平台对longjmp的安全检查逻辑不一样,而你的代码触发了C标准里的未定义行为

具体拆解执行流程和问题点:

  • 第一次进入testjmp(),调用setjmp(jb1)保存当前栈帧,返回0后调用foo()
  • foo()调用bar()bar()里调用setjmp(jb2)保存自己的栈帧,返回0后立刻longjmp(jb1,1)跳回testjmp()的第一个setjmp点。
  • 这时候bar()foo()的函数已经执行完毕,它们的栈帧早就被销毁(弹出调用栈)了。
  • 接下来testjmp()里再次调用setjmp(jb1),返回0后尝试longjmp(jb2, -1)——这里就是关键:jb2保存的是已经被销毁的bar()的栈帧,相当于要跳回一个不存在的栈空间里。

对于这种情况:

  • Linux的glibc实现比较宽松,允许这种未定义行为的执行(虽然结果不可靠,但不会直接崩溃);
  • 而Windows的MSVC runtime会严格检查longjmp的目标栈帧有效性,一旦发现要跳转到已经被弹出的栈帧,就会直接触发异常,避免程序执行到非法内存区域。

本质上,你的代码违反了setjmp/longjmp的核心使用规则:longjmp只能跳转到当前调用栈中仍然有效的setjmp,也就是保存jmp_buf的函数栈帧还未被销毁的时候。

内容的提问来源于stack exchange,提问作者l0n0l

火山引擎 最新活动