包装setjmp和longjmp有什么奇怪的?

时间:2010-04-21 20:47:42

标签: c gcc exception-handling

我第一次使用setjmp和longjmp,我遇到了一个问题,当我包装setjmp和longjmp时。我将代码简化为以下示例:

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

jmp_buf jb;

int mywrap_save()
{
  int i = setjmp(jb);
  return i;
}

int mywrap_call()
{
  longjmp(jb, 1);
  printf("this shouldn't appear\n");
}

void example_wrap()
{
  if (mywrap_save() == 0){
    printf("wrap: try block\n");
    mywrap_call();
  } else {
    printf("wrap: catch block\n");
  }
}

void example_non_wrap()
{
  if (setjmp(jb) == 0){
    printf("non_wrap: try block\n");
    longjmp(jb, 1);
  }  else {
    printf("non_wrap: catch block\n");
  }
}

int main()
{
  example_wrap();
  example_non_wrap();
}

最初我认为example_wrap()和example_non_wrap()的行为相同。但是,运行程序的结果(GCC 4.4,Linux):

wrap: try block
non_wrap: try block
non_wrap: catch block

如果我在gdb中跟踪程序,我看到即使mywrap_save()返回1,返回后的else分支也会被奇怪地忽略。谁能解释一下发生了什么?

2 个答案:

答案 0 :(得分:10)

 The longjmp() routines may not be called after the routine which called
 the setjmp() routines returns.

换句话说,你搞砸了你的筹码。

您可以查看装配体,看看您是否可以拼凑出真正发生的事情。

答案 1 :(得分:1)

setjmp()将保存当前的调用堆栈并标记一个点。当调用堆栈增长时,无论距标记点多远,您都可以使用longjmp()转到标记点,就像您从未离开过该点一样。

在你的代码中,当从mywrap_save()返回时,标记的点不再有效,该点周围的堆栈空间是脏的,因此你不能回到脏点。