You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中setjmp/longjmp引发循环计数器异常的原因探究

setjmp/longjmp导致循环计数器跳值的底层原理分析

问题复现

以下是触发异常的最小C代码:

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

jmp_buf routineA, routineB;

void routine_a()
{
    for (int i = 0; i < 10; ++i)
    {
        printf("A %d\n", i);
        if (!setjmp(routineA)) {
            if (i == 0)
              return;
            longjmp(routineB, 1);
        }
    }
}

void routine_b()
{
    for (int i = 0; i < 10; ++i)
    {
        printf("B %d\n", i);
        if (!setjmp(routineB))
          longjmp(routineA, 1);
    }
}

int main()
{
    routine_a();
    routine_b();
    return 0;
}

运行输出:

A 0
B 0
A 1
B 2
A 3
B 4
A 5
B 6
A 7
B 8
A 9

疑问解答

1. 数值跳变的具体原因

核心原因是**setjmp保存的上下文覆盖了循环计数器的状态,且longjmp恢复上下文后,循环的增量逻辑被重复执行**:

  • 每次调用setjmp都会将当前的寄存器、栈帧等上下文保存到jmp_buf中,包括循环计数器i的当前值。
  • 当从longjmp跳回setjmp的返回点时,程序会恢复到setjmp调用时的上下文状态——这意味着i的值会被重置为调用setjmp时的数值。
  • 以routine_a的i=1场景为例:
    1. 执行printf("A 1")后调用setjmp(routineA),此时i=1的状态被保存。
    2. 调用longjmp(routineB,1)跳转到routine_b,routine_b执行完当前循环体后,i从0增量到1,进入下一次循环并调用setjmp(routineB)保存i=1的状态,随后跳回routine_a。
    3. 回到routine_a时,i被恢复为1,循环体执行完毕后执行++i(i变为2),进入下一次循环并调用setjmp(routineA)保存i=2的状态,随后再次跳转到routine_b。
    4. 回到routine_b时,i被恢复为1,循环体执行完毕后执行++i(i变为2),进入循环体执行printf("B 2")——这就跳过了B 1的输出,出现跳变。

2. 调用longjmp时循环计数器会发生什么变化

  • 若循环计数器i是未被volatile修饰的自动变量,longjmp恢复上下文后,i的值是不确定的——这是C标准的明确规定。
  • 在实际编译器实现中:
    • 若i被优化到寄存器中,longjmp会直接恢复setjmp调用时寄存器里的i值。
    • 若i被分配到栈上,longjmp会恢复栈上保存的i值。
  • 恢复i的值后,循环会继续执行循环体剩余代码,随后执行增量操作++i,导致i的实际增量次数超出预期,最终出现跳值。

3. 该行为是C标准定义的还是实现相关的

  • 关于自动变量的不确定性是C标准明确规定的:C17标准§7.13.2.1指出,若在setjmp调用后,未被volatile修饰的自动变量被修改,那么调用longjmp恢复到该setjmp上下文时,该变量的值是不确定的。
  • 具体的跳变表现(比如跳值的步长)是实现相关的,取决于编译器的优化策略(是否将变量分配到寄存器)、setjmp保存的上下文内容等。

如果给循环计数器添加volatile修饰(如for (volatile int i = 0; i < 10; ++i)),编译器会禁止将i优化到寄存器中,强制存储在栈上,此时setjmp保存的是栈上的i值,longjmp恢复后,循环的增量逻辑会按预期执行,不会出现跳值。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:25:54