2011-09-16 14 views
8

In uno dei file di intestazione di Apple per libdispatch, queue.h, compare il seguente avviso:C w/Blocks: blocchi stack-based uscendo dal suo ambito

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

provare come posso, non riesco a trovare una test case che esemplifica questo bug. Posso creare blocchi che vengono istanziati nello stack, ma sembrano (sembrano) sempre a indirizzi univoci nello stack, anche quando sono fuori portata rispetto l'uno all'altro.

Immagino che la risposta a questo sia semplice, ma mi sfugge. Qualcuno può colmare le lacune nella mia (limitata) comprensione?

EDIT: ho visto la risposta this, ma non capisco bene come quell'istanza possa tradurre nel mio esempio pubblicato sopra. Qualcuno può mostrarmi un esempio usando i costrutti if?

+0

Il link che hai postato ha a che fare con un problema diverso, vale a dire che le chiusure sembrano comportarsi in modo strano in presenza di variabili mutabili. Vedi la domanda ["Javascript: chiusura del ciclo?"] (Http://stackoverflow.com/questions/5555464/javascript-closure-of-loop), che è esattamente lo stesso problema in JavaScript. Tuttavia, sembra che abbiano fatto l'errore messo in guardia da questo commento. I blocchi si copiano automaticamente in questi giorni? Mi piacerebbe saperlo anche io. –

+0

Ho provato un po 'ma ottengo sempre lo stesso risultato, le strutture dei blocchi sembrano essere allo scopo della funzione. Forse un sacco di persone sono state morse e loro l'hanno cambiato così? –

risposta

5

Al fine di mandare in crash una chiusura dello stack all'interno di una funzione:

  • È necessario assicurarsi che la chiusura è davvero una chiusura stack. A partire da Apple Clang 2.1, una chiusura che non fa riferimento a variabili nel suo contesto corrente (come quella in queue.h) viene realizzata come chiusura globale. Questo è un dettaglio di implementazione che può variare tra diverse versioni di compilatori/compilatori;

  • Il compilatore deve emettere il codice che riutilizza/riscrive efficacemente l'area di stack in cui la chiusura ha vissuto una volta. Altrimenti, ogni oggetto all'interno di quella funzione risiede in un indirizzo diverso nel frame dello stack delle funzioni, il che significa che non si verificherà un arresto anomalo all'interno di quella funzione. Sembra che Apple Clang 2.1 non riutilizzi gli indirizzi di memoria dello stack. GCC 4.6 può riutilizzarli, ma non supporta le chiusure.

Dal momento che Apple Clang 2.1 non riutilizza gli indirizzi in una cornice funzione di stack e GCC 4.6 non supporta le chiusure, da quello che posso dire che non è possibile fare questo particolare esempio - all'interno di una funzione, richiamare un chiusura dello stack fuori portata - crash.

Ho scritto un testo più dettagliato al riguardo su my blog.

+0

Grazie per la risposta, Bavarious. Tu eri una delle persone che speravo avrebbe risposto alla mia domanda. :) Mi ero reso conto che i blocchi devono catturare lo stato (altrimenti sono resi globali) per essere messi in pila, ma ho trovato lo stesso come te - gli indirizzi stack non vengono riutilizzati per le chiusure. –

+0

@Sed È più generale - gli indirizzi di stack apparentemente non vengono riutilizzati per nessun tipo di oggetto, incluse le chiusure. Ciò significa in effetti che tutto ciò che è stato creato in una cornice stack di funzioni è attivo fino alla fine della funzione. Non sono sicuro che Clang lo eviti intenzionalmente o se potrebbe essere implementato in futuro. –

+0

Stavo pensando di seguirlo con un "aha!", Avendo scoperto che gli indirizzi di stack venivano effettivamente riutilizzati per i tipi 'long', ma avevo il mio compilatore impostato su GCC 4.2. D'oh. –

Problemi correlati