2011-09-16 8 views
8

W jednym z plików nagłówkowych Apple'a dla libdispatch, queue.h następujące ostrzeżenie pojawia się:C w/bloków: bloki na stosie będzie poza zakresem

// 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. 

spróbować, jak mogę, nie mogę wymyślić przypadek testowy, który jest przykładem tego błędu. Mogę tworzyć bloki, które są tworzone na stosie, ale wydają się (wydają się) zawsze pojawiać się na unikatowych adresach na stosie, nawet gdy są poza zasięgiem względem siebie.

Wyobrażam sobie, że odpowiedź na to pytanie jest prosta, ale ucieka mi. Czy ktokolwiek może wypełnić luki w moim (ograniczonym) rozumieniu?

EDIT: Widziałem this odpowiedź, ale ja nie bardzo rozumiem jak to wystąpienie może przełożyć się moim przykładzie zamieszczonych powyżej. Czy ktoś może pokazać mi przykład przy użyciu konstrukcji if?

+0

Link, który wysłałeś, dotyczy innego problemu, a mianowicie, że zamknięcia wydają się działać dziwnie w obecności zmiennych zmiennych. Zobacz pytanie ["JavaScript: zamknięcie pętli?"] (Http://stackoverflow.com/questions/5555464/javascript-closure-of-loop), który jest dokładnie tym samym problemem w JavaScript. Jednak wygląda na to, że popełnili błąd, o którym wspomina ten komentarz. Czy bloki kopiują się automatycznie w te dni? Chciałbym też wiedzieć. –

+0

Próbowałem trochę, ale zawsze uzyskuję taki sam wynik jak ty, struktury bloków wydają się znajdować w zakresie funkcji. Być może wielu ludzi zostało ugryzionych, a oni to zmienili? –

Odpowiedz

5

Aby do odpoczynku zamknięcie stosu wewnątrz funkcji:

  • Musisz upewnić się, że zamknięcie jest rzeczywiście zamknięcia stos. Od wersji Apple Clang 2.1 zamknięcie, które nie odwołuje się do zmiennych w bieżącym kontekście (takim jak w kolejce.h), jest realizowane jako globalne zamknięcie. Jest to szczegół implementacji, który może się różnić w zależności od wersji kompilatora/kompilatora;

  • Kompilator musi emitować kod, który skutecznie ponownie wykorzystuje/przepisuje obszar stosu, w którym kiedyś znajdowało się zamknięcie. W przeciwnym razie każdy obiekt znajdujący się w tej funkcji znajduje się w innym adresie w ramce stosu funkcji, co oznacza, że ​​nie wystąpi awaria wewnątrz tej funkcji. Wygląda na to, że Apple Clang 2.1 nie wykorzystuje ponownie adresów pamięci stosu. GCC 4.6 może je ponownie użyć, ale nie obsługuje zamknięć.

Od Jabłko Clang 2.1 nie ponowne adresy w ramce stosu i funkcja GCC 4.6 nie obsługuje zamknięć, z tego co mogę powiedzieć to nie jest możliwe, aby ten konkretny przykład - wewnątrz funkcji, wywołania zamknięcie stosu poza zasięgiem - awaria.

Napisałem bardziej szczegółowy tekst na ten temat na my blog.

+0

Dzięki za odpowiedź, Bavarious. Byłeś jednym z ludzi, na których miałem nadzieję odpowiedzieć na moje pytanie. :) Zdałem sobie sprawę, że bloki muszą przechwytywać stan (w przeciwnym razie są renderowane globalnie), aby zostać umieszczone na stosie, ale znalazłem to samo, co ty - adresy stosów nie są ponownie wykorzystywane do zamykania. –

+0

@Sed Jest bardziej ogólny - adresy stosów najwyraźniej nie są ponownie wykorzystywane do żadnego rodzaju obiektów, włącznie z zamknięciami. Oznacza to, że wszystko, co zostało utworzone w ramce stosu funkcji, jest żywe do końca funkcji. Nie jestem pewien, czy Clang celowo tego unika, czy też może zostać zaimplementowany w przyszłości. –

+0

Planowałem pójść za tym z "aha!", Stwierdziwszy, że adresy stosów rzeczywiście były ponownie używane dla typów 'long', ale miałem ustawiony mój kompilator na GCC 4.2. D'oh. –