为什么调用 setjmp() 中的函数会发生分段错误?

Why a segmentation fault occurs calling a function inside setjmp()?

我不明白为什么在函数 middleFunc() 中,在 if ( setjmp(middle) ) 语句中调用 entry_point(arg) 时会引发分段错误。

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


    jmp_buf start,middle,end;


    void finalFunc(void *v)
    {
      printf("hello\n");
      return ;
    }


    void middleFunc(void (*entry_point)(void *), void *arg)
    {
     //just debug : this does not cause segmentation fault
     entry_point(arg);

     if ( setjmp(middle) ){
        //this casues the segmentation fault
        entry_point(arg);
        //once the entry point (finalFunc) is executed go to  jmp_buffer end
        longjmp(end,1);
     }
     else {
        longjmp(start,1);
     }
   }

  int main(){

    if (setjmp(end)){
        //exit since finalFunc has been executed
        return 0;
    }

    if (setjmp(start)){
        //the middleFunc has previously set the jmp_buffer middle
        longjmp(middle,1);
    }

    else{
        int  x = 1;
        middleFunc(finalFunc,(void*)&x);
    }

 }

在您的代码中,行为未定义。在 middleFunc 完成执行(通过正常完成或另一个 longjmp)后,您不能长跳转到 middle

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 function containing the invocation of the setjmp macro has terminated execution248) 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.

在您的代码中 middleFunc 设置 middle,然后通过执行 longjmp(start,1) 立即退出到 main。之后跳转 middle 不再有效。您不再被允许从任何地方跳转到 middlesetjmp/longjmp 机制只支持向上跳转调用栈。你不能做侧跳或下跳。仅支持向上跳跃。

从实际的角度来看,您正试图跳转到 "dead" 函数调用并以某种方式期望函数参数值仍然有效(例如,从上次调用中保留下来或其他)。但他们不是。 setjmp/longjmp 不要preserve/restore 参数值。在 "dead" 调用中 entry_point 的值可能是 some garbage。当您尝试通过 entry_point 进行调用时,代码会转储。

P.S。的确,用 setjmp/longjmp 侧跳有时是用来实现协程的。但是,这种用法超出了标准库规范的范围。并且在任何情况下,这种用法永远不会期望保留参数值。