2009-09-08 8 views
20

私の質問は、ローカル変数に関するsetjmp/longjmpの動作を目指しています。setjmp/longjmpとローカル変数

例コード:

jmp_buf env; 


void abc() 
{ 
    int error; 

    ... 
    if(error) 
    longjmp(env); 
} 


void xyz() { 
    int v1;   // non-volatile; changed between setjmp and longjmp 
    int v2;   // non-volatile; not changed between setjmp and longjmp 
    volatile int v3; // volatile;  changed between setjmp and longjmp 
    volatile int v4; // volatile;  not changed between setjmp and longjmp 

    ... 

    if(setjmp(env)) { 
    // error handling 
    ... 
    return; 
    } 

    v1++; // change v1 
    v3++; // change v3 

    abc(); 
} 


int main(...) { 
    xyz(); 
} 

のsetjmp/longjmp関数のドキュメント言う:

「すべてのアクセス可能なオブジェクトは、(時間longjmp関数のように値を持っているが)のオブジェクトの値がそれ以外 、と呼ばれていました が対応する のsetjmp()の呼び出しを含む関数に対してローカルであり、volatile修飾された型を持たず、setjmp()呼び出しとlongjmp()呼び出しの間で が変更される自動記憶期間は不定です。

私は2つの可能な解釈、以下を参照してください。

intepretation1:

ローカル変数が両方

  • 非揮発性および
を変更しているものを除き、復元されています

侵入2:

個の

ローカル変数は

  • 非揮発性であるものと
  • longjmpをのみV1は未定義である後interpretation1によれば

を変更されているものを除いて、復元されます。 v2、v3、v4が定義される。 longjmpの後の解釈2によれば、v4のみが定義されています。 v1、v2、v3は未定義です。

どちらが正しいですか?

BTW:すべてのコンパイラに有効な一般的な( "ポータブルな")回答が必要です。つまり、特定のコンパイラを試してみても役に立ちません。

+0

実装上の注意:変更された変数と不揮発性の変数は、longjmpの時と同じか、setjmpの時にコード生成によって復元されます。したがって、「不確定」。したがって、もしそれらが*変更されていなければ、これらの2つの値は同じであり、そのため変更されていないバールは安全です。 – greggo

答えて

10

解釈1が正しいです。解釈2が意図されていた場合、元のテキストは、 "および"の代わりに "または"が使用されていました。

26

setjmp/longjmpは、最初に渡されたときにレジスタ(スタックとコードポインタなどを含む)を保存し、ジャンプするときにそれらを復元することによって実装されます。

自動(別名「ローカル」、スタック割り当て)「揮発性」でない変数はレジスタではなくスタックにを格納することができます。

これらの状況では、longjmpは、setjmp()が最初に呼び出された時点で、これらのレジスタ変数をその値に復元します。

さらに、特に巧妙なコンパイラでは、別の変数の状態から推測できる変数を避け、必要に応じてそれらを計算する可能性があります。しかしながら

、変数が自動であるが、レジスタが割り当てられていない、それはsetjmp関数とlongjmp間コードによって変更することができる場合..

揮発性が明示的にレジスタに変数を格納しないコンパイラを語っています。

変数がvolatileであると明示しない限り、変数をsetjmp/longjmpの間で変更した場合、その値はコンパイラの選択に依存するため、依存する必要はありません( 'indeterminate')。

+0

逆に、 'register' *として宣言された自動変数はレジスタに格納されるかもしれませんが、コンパイラはヒントを無視するかもしれないので、それらが復元されることを保証できません。 – Michael

+0

@Michael実際には何かが "修復"されたという保証さえありません。 「通常の」ケースでは、変数がlongjmpになった関数を呼び出す前に、変数が持つ価値があるということです。前述のように、setjmp/longjmpによって行われたすべてのコールセーブされたレジスタのブラインド復元によって、「clobbered」することがあります。実際には、これはclobberedの場合、おそらく 'setjmp'の値に復元されますが、最後に設定したものではなく、不確定であることのみが示されます。 – greggo