为什么 setjmp/longjmp

Why does setjmp/longjmp

我想使用 setjmp/longjmp 在主函数中重用一些代码(注意:这只是一个练习,并不是我在现实世界中认真计划做的事情)。 以下代码是我想出的:

#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;
    
void func(void)
{
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
}

int main()
{
    int x = 0;
    if (setjmp(jmp_body) == 1) {
        printf("Body %d\n", ++x);
        longjmp(jmp_ret, 1);
    }
    func();
    func();
    func();
    return 0;
}

我期望这段代码的工作方式如下:

  1. main() 函数将记住 'body' 部分的位置并使用 if (setjmp(jmp_body) == 1) 跳过它。
  2. 在使用 if (setjmp(jmp_ret) == 0)
  3. 记住正文应该 return 的位置后,func() 调用将使用 longjmp(jmp_body) 暂时跳转到正文
  4. 正文将执行并使用 longjmp(jmp_ret, 1)
  5. 跳回 func() 调用
  6. func() 正按预期从 return 到 main()

因此,我希望代码打印的内容如下:

Body 1
Body 2
Body 3

相反,它会永远循环不断地执行正文,这向我表明 func() 调用没有 return 在它应该在的地方,而是可能 return 在它本身之上一遍又一遍地执行自己。

相比之下,下面的代码打印出我所期望的:

#include <stdio.h>
#include <setjmp.h>
jmp_buf jmp_body, jmp_ret;

int main()
{
    int x = 0;
    if (setjmp(jmp_body) == 1) {
        printf("Body %d\n", ++x);
        longjmp(jmp_ret, 1);
    }
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    if (setjmp(jmp_ret) == 0)
        longjmp(jmp_body, 1);
    return 0;
}

if (setjmp(jmp_ret) == 0) longjmp(jmp_body, 1) 放入使原始方法无效的函数调用中是什么意思?

您试图 longjmp 将堆栈从 main() 退回到 func()。这没有定义。由于 longjmp 本身是一个函数,您很可能最终 longjmp 进入 func,return 地址是 longjmp 调用自身,因此是一个无限循环。

TL/DR - 你不能跳回你跳出的函数。

7.13.2.1 The longjmp function
...
2     The longjmp function restores the environment saved by the most recent invocation of the setjmp macro in the same invocation of the program with the corresponding jmp_buf argument. If there has been no such invocation, or if the invocation was from another thread of execution, or if the function containing the invocation of the setjmp macro has terminated execution248) in the interim, or if the invocation of the setjmp macro was within the scope of an identifier with variably modified type and execution has left that scope in the interim, the behavior is undefined.
248) For example, by executing a return statement or because another longjmp call has caused a transfer to a setjmp invocation in a function earlier in the set of nested calls.
C 2011 Online Draft

当你在func中执行longjmp(jump_body, 1);时,你无效 jump_ret.

longjmp 不是双向的 - 它展开堆栈,就好像 setjmplongjmp 之间的任何函数调用从未发生过一样.