【问题标题】:Why does setjmp/longjmp为什么 setjmp/longjmp
【发布时间】:2021-07-15 14:07:07
【问题描述】:

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

#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. func() 调用将在使用 if (setjmp(jmp_ret) == 0) 记住身体应该返回的位置后使用 longjmp(jmp_body) 临时跳转到身体
  3. 主体将执行并使用longjmp(jmp_ret, 1) 跳转回func() 调用
  4. func() 将按预期返回到main()

因此,我期望打印的代码如下:

Body 1
Body 2
Body 3

相反,它永远循环执行主体,这向我表明 func() 调用没有返回它应该返回的位置,而是可能会返回到自身之上,一遍又一遍地执行自身。

相比之下,以下代码打印的正是我所期望的:

#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) 放在函数调用中会导致原始方法无效是什么原因?

【问题讨论】:

  • “暂时跳跃”是一个有趣的表达方式。没有什么是暂时的。当您跳出func 时,jump_rec 不再有效。所以后续的长跳尝试是未定义的。
  • 是的,我认为您误解了 longjmp 是什么以及做什么。您只能跳上调用堆栈,进入尚未返回的函数。当你这样做时,你会隐式地从当前函数以及介于两者之间的所有函数返回;然后你不能 longjmp 回到他们身边。这不是为了在整个程序中跳转到随机的代码块;相反,它会强制堆栈上的多个函数同时返回,例如在支持它的语言中抛出异常。
  • longjmp() 确实展开堆栈,展开部分中的所有内容都不再有效,您可以了解堆栈展开以及 setjmp()longjmp() 的实际工作方式。请使用一致的意图风格。

标签: c setjmp


【解决方案1】:

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

7.13.2.1 longjmp 函数
...
2     longjmp 函数恢复最近一次调用保存的环境 setjmp 宏在程序的同一调用中具有相应的 jmp_buf 参数。如果没有这样的调用,或者调用来自 另一个执行线程,或者 如果函数包含对 setjmp 的调用 宏已在此期间终止执行248),或者如果调用 setjmp 宏在具有可变修改类型的标识符的范围内,并且执行具有 在此期间离开该范围,行为未定义
248) 例如,通过执行 return 语句或 因为另一个 longjmp 调用导致了 转移到嵌套调用集中较早的函数中的setjmp 调用。
C 2011 Online Draft

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

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

【讨论】:

  • 有人需要告诉the Ruby developersRuby 完全滥用 setjmp()/longjmp()
  • @AndrewHenle:天哪。
【解决方案2】:

您尝试将longjmpmain() 退回到func()。这没有定义。由于longjmp 本身就是一个函数,因此您很可能以longjmp 结束func,返回地址是longjmp 调用本身,因此是一个无限循环。

【讨论】:

    猜你喜欢
    • 2011-12-21
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-21
    相关资源
    最近更新 更多