2011-09-16 4 views
8

En uno de los archivos de cabecera de Apple para libdispatch, queue.h, aparece la siguiente advertencia:C w/Blocks: bloques basado en pila de salir del alcance

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

mas que trate, no puede llegar a una caso de prueba que ejemplifica este error. Puedo crear bloques que se crean instancias en la pila, pero (parece) siempre aparecen en direcciones únicas en la pila, incluso cuando están fuera del alcance con respecto a la otra.

Imagino que la respuesta a esto es simple, pero se me escapa. ¿Alguien puede llenar los vacíos en mi (limitado) entendimiento?

EDIT: He visto this respuesta, pero no entiendo muy bien cómo esa instancia se puede traducir a mi ejemplo publicado anteriormente. ¿Alguien puede mostrarme un ejemplo usando construcciones if?

+0

El enlace que ha publicado tiene que ver con un problema diferente, es decir, que los cierres parecen actuar de manera extraña en presencia de variables variables. Consulte la pregunta ["Javascript: ¿cierre del ciclo?"] (Http://stackoverflow.com/questions/5555464/javascript-closure-of-loop), que es exactamente el mismo problema en JavaScript. Sin embargo, parece que cometieron el error advertido por este comentario. ¿Los bloques se copian automáticamente en estos días? Me gustaría saber, también. –

+0

Lo he intentado un poco, pero siempre obtengo el mismo resultado que usted, las estructuras de bloques parecen estar en el alcance de la función. Tal vez mucha gente fue mordida y lo cambiaron así? –

Respuesta

5

Con el fin de chocar un cierre pila dentro de una función:

  • usted necesita para asegurarse de que el cierre es de hecho un cierre de pila. A partir de Apple Clang 2.1, un cierre que no hace referencia a las variables en su contexto actual (como el de queue.h) se realiza como un cierre global. Este es un detalle de implementación que puede variar entre diferentes versiones de compiladores/compiladores;

  • El compilador debe emitir un código que efectivamente reutilice/reescriba el área de la pila donde vivió el cierre. De lo contrario, cada objeto dentro de esa función vive en una dirección diferente en el marco de la pila de funciones, lo que significa que no se producirá un bloqueo dentro de esa función. Parece que Apple Clang 2.1 no reutiliza las direcciones de memoria de la pila. GCC 4.6 puede reutilizarlos, pero no admite cierres.

Desde que Apple Clang 2.1 no reutiliza las direcciones en un marco de pila función y GCC 4.6 no es compatible con los cierres, por lo que puedo decir que no es posible hacer este ejemplo en particular - dentro de una función, invocar una cierre de pila fuera del alcance: bloqueo.

Escribí un texto más detallado acerca de esto en my blog.

+0

Gracias por la respuesta, Bavarious. Usted fue una de las personas que esperaba que respondiera a mi consulta. :) Me di cuenta de que los bloques deben capturar el estado (de lo contrario se vuelven globales) para colocarlos en la pila, pero encontré lo mismo que usted: las direcciones de la pila no se vuelven a utilizar para los cierres. –

+0

@Sed Es más general: las direcciones de pila aparentemente no se reutilizan para ningún tipo de objeto, incluidos los cierres. Esto significa que todo lo que se creó en un marco de pila de funciones está vivo hasta el final de la función. No estoy seguro si Clang lo evita intencionalmente o si podría implementarse en el futuro. –

+0

Estaba planeando seguir eso con un "¡Ajá!", Después de haber encontrado que las direcciones de la pila se estaban reutilizando para los tipos "largos", pero mi compilador estaba configurado en GCC 4.2. D'oh. –

Cuestiones relacionadas