6

次のコード:

  • gcc バージョン 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits) でコンパイルすると問題なく動作します。
  • MSVC10 (Win7/32 ビット) でコンパイルすると正常に動作します。
  • gcc バージョン 4.5.2 (Win7/32 ビットの MinGW) で実行するとクラッシュする

main.cpp :

# include <iostream>
# include <csetjmp>
# include <stdexcept>

using namespace std ;

void do_work(jmp_buf context)
{
    try
    {
        throw runtime_error("Ouch !") ;
    }
    catch(exception & e)
    {
    }

    longjmp(context, -1) ;                        //BP1
}

int main(int, char *[])
{
    jmp_buf context ;

    try
    {
        if( setjmp(context) != 0 )
        {
            throw runtime_error("Oops !") ;       //BP2
        }

        do_work(context) ;
    }
    catch(exception & e)
    {
        cout << "Caught an exception saying : " << e.what() << endl ;
    }
}

デバッグしてみましたが、プログラムの動作がおかしくなりました。場合によっては、最初のブレークポイント (BP1) を通過して BP2 でクラッシュし、プログラムが無限ループに陥った場合のように、制御が BP1 に到達しないこともあります。私のデバッグスキルではこれ以上は言えません。

このコードは、MinGW 4.5 で奇妙な動作を示す最小限のものです。また、次のことに気付きました。

  • do_work関数呼び出しをその内容で置き換えると、プログラムは正常に実行されます。
  • try{ ... } catch(...){ }内のブロックを削除するdo_workと、プログラムは正常に実行されます。
  • 最適化フラグは効果がありません (常にクラッシュします)。

C++ コードの問題は認識してsetjmp/longjmpいますが、一部のレガシー C コードとのインターフェイスに使用せざるを得ません。

私の質問 :

  • これは欠陥/バグ/エラーコードですか? または、MinGW 4.5 がコードを誤って処理していますか? (ツールのせいにするのは辛辣でおこがましいですが、いくつかの設定に問題があるのではないかと思います)。

アドバイスをありがとう。

必要に応じてタグを付け直してください。

4

3 に答える 3

3

Unix の longjmp(3) man ページには次のように書かれています。

longjmp() ルーチンは、setjmp() ルーチンを呼び出したルーチンが戻った後に呼び出すことはできません。

「制御がBP1に到達しない場合がある」という懸念を説明していると思います。「問題なく動く」というのは信頼できる判断ではないと思います。ランダムに正常に実行され、一般的にスタックが台無しになっていることを期待したいと思います。

longjmp/setjmp と C++ の例外を混在させて作業しているときに、クラッシュや未定義の動作を回避するために考慮すべき明確な推奨事項がいくつかあります。

  • C++ プログラムでは setjmp/longjmp を使用しないでください。
  • 例外が発生する可能性のあるプログラムで setjmp/longjmp 関数を使用する場合、それらが相互作用しない限り安全です。
  • try 句と catch 句の内外で longjmp を使用しないでください。
  • 自動オブジェクトの初期化ポイントを越えて決して longjmp しないでください。
  • 特にデストラクタが自明でない場合は、自動オブジェクトの破壊ポイントを決して longjmp しないでください。
  • シグナル ハンドラからスローしないでください。
  • ネストされたシグナル ハンドラから longjmp を呼び出さないでください。
  • 場所 X から場所 Y への longjmp の動作は、X でスローされた例外と X でキャッチされた例外が同じ効果を持つ限り、予測可能で有効なままです。
  • setjmp/longjmp と例外を混在させると、移植性は期待できません。
  • 代わりに、使用しているドキュメンテーション コンパイラの関連する詳細を参照してください。たとえば、Visual C++ を使用する場合は、setjmp/longjmp の使用を参照してください。

この質問では、C++ で記述されたプログラムでレガシー C コードを処理することに言及しています。Boost ライブラリの 1 つをレビューしているときに、jpeg ライブラリの sjlj の問題に関する興味深い議論がありました。議論は長くなりましたが、ここにエッセンスと推奨オプションがあります

于 2011-10-29T13:00:54.123 に答える
1

C++ は、 の使用に関して 1 つの追加の制限のみを行いますlongjmp()

プログラム内の別の (宛先) ポイントに制御を転送するスローされた例外によって自動オブジェクトが破棄される場合、同じ (宛先) ポイントに制御を転送するスロー ポイントで longjmp(jbuf, val) を呼び出すと、未定義の動作が発生します。 . (18.7)

あなたはこれを認識しているようですが、あなたのプログラムはそれを行いません。私はコンパイラの欠陥に投票します。

于 2011-10-29T13:19:20.790 に答える
0

longjmpおよびsetjmpC関数であり、C++例外処理セマンティクスおよびオブジェクト破棄セマンティクスでそれらを呼び出すことは未定義の動作です。つまり、機能するかどうかは実装次第です(テストではこれが示され、SEHスタックアンワインドセマンティクスはタイプに応じて物事を壊します使用済み、作業中の iirc は dwarf2 を使用しています)

于 2011-10-29T12:45:11.613 に答える