簡體   English   中英

為什么setjmp / longjmp在MinGW中沒有時會在MSVC上崩潰?

[英]Why does the setjmp/longjmp crash on MSVC when it didn't in MinGW?

我想使用setjmp() / longjmp()來實現一個協程系統。 然后我決定編寫一個.c文件來測試它。 在MinGW,沒關系; 我得到了我想要的結果。 但是當我在MSVC ++中編譯它時,程序崩潰:“訪問沖突”

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

    jmp_buf a;
    int is_invoke=0;

    void 
    action_1()
    {
        for ( ;; ) {
          printf("hello~~~A\n");
          if(!setjmp(a)) {
            is_invoke=1;
            return;
          }  
        }
    }

    void 
    func()
    {
      if (is_invoke) {
        longjmp(a,1);
      }
      action_1();
      printf("end\n");
    }

    void 
    dummy()
    {
      ;
    }

    int 
    main(int argc, char *argv[])
    {
        for ( ;; )  {
          func();
          dummy();
        }
        return 0;
    }

setjmp的手冊頁說:

setjmp()將堆棧上下文/環境保存在env供以后使用longjmp() 如果調用setjmp()的函數返回,則堆棧上下文將無效。

在一個簡單的實現中,你可能會認為jmp_buf包含一個地址來重置堆棧指針和一個跳轉到的地址。 只要你從保存在函數返回jmp_buf ,棧幀指向的jmp_buf不再有效,並且可以立即被破壞。

換句話說,你只能依靠longjmp作為一種超級return聲明 - 永遠不要深入。

我認為這對你來說在mingw(以及我在Linux上)的作用的原因是特定於實現的,可能還有運氣。 還有另外一種方法 - 你讀過西蒙塔特姆的邪惡的協程宏文章嗎?

由於您正在調用未定義的行為,因此一個編譯器崩潰並且另一個編譯器似乎可以正常工作。 兩者都是正確的 - 這是未定義行為的美妙。

問題是保存的上下文 - jmp_buf - 只有在調用setjmp()來設置它的函數沒有返回時才會保持有效。

C99標准(不再是現行標准,但這種措辭不太可能發生重大變化)說:

§7.13.2.1的longjmp功能

longjmp函數使用相應的jmp_buf參數恢復最近調用setjmp宏所保存的環境。 如果沒有這樣的調用,或者包含setjmp宏的調用的函數在過渡期間終止執行208) ,或者如果setjmp宏的調用在具有可變修改類型的標識符的范圍內並且執行具有在過渡期間將該范圍留下,行為未定義。

208)例如,通過執行return語句或因為另一個longjmp調用導致在嵌套調用集合中較早的函數中轉移到setjmp調用。

您的代碼幾乎立即退出action_1() ,使得setjmp()保存的jmp_buf毫無價值。


幾年前我創建了setjmp()longjmp()的這個小演示。 它可能會幫助你。

/*
@(#)File:           $RCSfile: setjmp.c,v $
@(#)Version:        $Revision: 1.1 $
@(#)Last changed:   $Date: 2009/10/01 16:41:04 $
@(#)Purpose:        Demonstrate setjmp() and longjmp()
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 2009
*/


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

static jmp_buf target_location;

static void do_something(void)
{
    static int counter = 0;
    if (++counter % 10 == 0)
        printf("---- doing something: %3d\n", counter);
    if (counter % 1000 == 0)
    {
        printf("||-- doing_something: calling longjmp() with value -1\n");
        longjmp(target_location, -1);
    }
}

static void do_something_else(int i, int j)
{
    printf("-->> do_something_else: (%d,%d)\n", i, j);
    do_something();
    if (i > 2 && j > 2 && j % i == 2)
    {
        printf("||-- do_something_else: calling longjmp() with value %d\n", (i + j) % 100);
        longjmp(target_location, (i + j) % 100);
    }
    printf("<<-- do_something_else: (%d,%d)\n", i, j);
}

static void doing_stuff(void)
{
    int i;
    printf("-->> doing_stuff()\n");
    for (i = rand() % 15; i < 30; i++)
    {
        int j;
        do_something();
        for (j = rand() % 10; j < 20; j++)
        {
            do_something_else(i, j);
        }
    }
    printf("<<-- doing_stuff()\n");
}

static void manage_setjmp(void)
{
    printf("-->> manage_setjmp()\n");
    switch (setjmp(target_location))
    {
    case 0:
        /* Initial return - get on with doing stuff */
        doing_stuff();
        break;
    case -1:
        /* Error return - terminate */
        printf("<<-- manage_setjmp() - error return from setjmp()\n");
        return;
    default:
        /* NB: not officially possible to assign the return from setjmp() */
        printf("---- manage_setjmp() - non-error return from setjmp()\n");
        doing_stuff();
        break;
    }
    printf("<<-- manage_setjmp()\n");
}

int main(void)
{
    printf("-->> main()\n");
    manage_setjmp();
    printf("<<-- main()\n");
    return(0);
}

您不能將setjmp / longjmp用於協同程序。 在POSIX上使用makecontext / swapcontext,或在Windows上使用光纖(CreateFiber等)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM