2011-09-16 11 views
8

C /ブロック:スタックベースのブロックが範囲外になる

Appleの libdispatchqueue.hのヘッダーファイルの1つに、次の警告が表示されます。

// The declaration of a block allocates storage on the stack. 
// Therefore, this is an invalid construct: 

dispatch_block_t block; 

if (x) { 
    block = ^{ printf("true\n"); }; 
} else { 
    block = ^{ printf("false\n"); }; 
} 
block(); // unsafe!!! 

// What is happening behind the scenes: 

if (x) { 
    struct Block __tmp_1 = ...; // setup details 
    block = &__tmp_1; 
} else { 
    struct Block __tmp_2 = ...; // setup details 
    block = &__tmp_2; 
} 

// As the example demonstrates, the address of a stack variable is 
// escaping the scope in which it is allocated. That is a classic C bug. 

この問題を例示するテストケースはありません。スタック上にインスタンス化されたブロックを作成することはできますが、スタック上の一意のアドレスに常に現れるように見えます。

私はこれに対する答えが簡単だと思っていますが、それは私を逃れます。誰かが私の(限られた)理解のギャップを埋めることができますか?

EDITthisレスポンスを見たことがありますが、そのインスタンスが上に掲載された私の例にどのように変換できるかはよく分かりません。誰か私にif構造を使った例を教えてもらえますか?

+0

あなたが投稿したリンクは、別の問題、すなわち、変数が変化してもクロージャーが変わったように見えます。 JavaScriptのまったく同じ問題である["Javascript:closure of loop?"](http://stackoverflow.com/questions/5555464/javascript-closure-of-loop)の質問を参照してください。しかし、彼らはこのコメントで間違いを警告したようだ。最近、ブロックは自動的に自分自身をコピーしますか?私も知りたいのですが。 –

+0

私はちょっと試しましたが、いつも同じ結果を得ています。ブロック構造体は関数スコープにあるようです。おそらく、たくさんの人が噛まれ、そのように変わったでしょうか? –

答えて

5

  • あなたはクロージャが実際にスタックの閉鎖であることを確認する必要があります。 Apple Clang 2.1以降、現在のコンテキスト(queue.hのものなど)で変数を参照しないクロージャは、グローバルクロージャとして実現されています。これは、コンパイラ/コンパイラのバージョンによって異なる実装の詳細です。

  • コンパイラは、クロージャが一度存在していたスタック領域を効果的に再利用/書き換えするコードを発行する必要があります。それ以外の場合は、その関数内のすべてのオブジェクトが関数スタックフレーム内の別のアドレスに存在します。つまり、その関数内でクラッシュすることはありません。 Apple Clang 2.1はスタックメモリアドレスを再利用しないようです。 GCC 4.6はそれらを再利用できますが、クロージャをサポートしていません。

アップルクラン2.1は、関数のスタックフレーム内のアドレスを再利用していないと私はそれを伝えることができることは、この特定の例を作成することはできませんから、GCC 4.6は、クロージャをサポートしていないので - 関数内、呼び出しますスコープスタッククロージャから外れる - クラッシュします。

私はmy blogについてこれについての詳細な文章を書いた。

+0

Bavariousにお返事ありがとうございます。あなたは私の質問に応えたいと望んでいた人々の一人でした。私はブロックが状態をキャプチャしなければならないことに気付きました(そうでなければそれらはグローバルにレンダリングされます)がスタックに置かれるようにしましたが、スタックアドレスはクロージャのために再利用されません。 –

+0

@Sedもっと一般的です - スタックアドレスはどんな種類のオブジェクトでも再利用されません。クロージャは含まれています。これは、関数スタックフレームで作成されたすべてが、関数が終了するまで有効であることを意味します。私は、Clangが故意にそれを避けるのか、将来それが実施されるのかどうかはわかりません。 –

+0

スタックアドレスが実際に 'long'型のために再利用されていたのを見て、" aha! "を使ってそれを続けようと計画していましたが、コンパイラをGCC 4.2に設定しました。 D'oh。 –

関連する問題