5

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;
    }
4

3 に答える 3

8

setjmp の man ページには次のように書かれています。

setjmp()envで後で使用するために スタック コンテキスト/環境を保存しますlongjmp()。呼び出した関数が戻ると、スタック コンテキストは無効になりsetjmp()ます。

単純な実装では、ajmp_bufにはスタック ポインターをリセットするアドレスとジャンプするアドレスが含まれていると想定できます。を保存した関数から戻るとすぐに、 がjmp_buf指すスタック フレームはjmp_buf無効になり、すぐに破損する可能性があります。

言い換えれば、longjmp に頼ることができるのは、一種のスーパーreturnステートメントとして機能することだけであり、決して深くはなりません。

これがmingwで(そしてLinuxで私のために)機能する理由は、実装固有であり、おそらく運次第だと思います。別の方法があります。Simon Tatham の邪悪なコルーチン マクロのエッセイを読んだことがありますか?

于 2011-12-25T05:00:51.180 に答える
4

未定義の動作を呼び出しているため、1 つのコンパイラがクラッシュし、別のコンパイラが動作しているように見えても問題ありません。どちらも正しい - それが未定義の動作の美しさです。

問題は、保存されたコンテキスト (つまり、コンテキストを設定するために呼び出された関数が返されないjmp_buf限り) が有効であるということです。setjmp()

C99 標準 (現在の標準ではなくなりましたが、この文言は大幅に変更された可能性は低いです) は次のように述べています。

§7.13.2.1longjmp関数

この関数は、対応する引数を使用したプログラムの同じ呼び出しで longjmp、マクロの最新の呼び出しによって保存された環境を復元します。そのような呼び出しがなかった場合、またはマクロの呼び出しを含む関数が暫定的に実行を終了した場合208) 、またはマクロの呼び出しが可変的に変更された型を持つ識別子のスコープ内にあり、実行が終了した場合動作は未定義です。setjmpjmp_bufsetjmpsetjmp

208)たとえば、returnステートメントを実行することによって、または別の呼び出しによって、ネストされた呼び出しのセットの前にある関数の呼び出しにlongjmp転送されたためです。setjmp

あなたのコードはaction_1()ほとんどすぐに終了し、jmp_buf保存されたものはsetjmp()無価値になります。


数年前に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);
}
于 2011-12-25T07:42:34.203 に答える
1

コルーチンに setjmp/longjmp を使用することはできません。POSIX では makecontext/swapcontext を使用し、Windows ではファイバー (CreateFiber など) を使用します。

于 2011-12-25T07:11:02.503 に答える