Si creo una variable dentro de un nuevo conjunto de llaves, ¿esa variable aparece en la pila en la llave de cierre o se cuelga hasta el final de la función? Por ejemplo:
void foo() {
int c[100];
{
int d[200];
}
//code that takes a while
return;
}
¿Va d
a tomar memoria durante la code that takes a while
sección?
Respuestas:
No, las llaves no actúan como un marco de pila. En C, las llaves solo denotan un ámbito de nomenclatura, pero nada se destruye ni se extrae nada de la pila cuando el control se sale de ella.
Como un programador que escribe código, a menudo puede pensarlo como si fuera un marco de pila. Los identificadores declarados dentro de las llaves solo son accesibles dentro de las llaves, por lo que desde el punto de vista del programador, es como si fueran empujados a la pila a medida que se declaran y luego se abren cuando se sale del alcance. Sin embargo, los compiladores no tienen que generar código que empuje / extraiga nada en la entrada / salida (y, en general, no lo hacen).
También tenga en cuenta que las variables locales pueden no utilizar ningún espacio de pila en absoluto: podrían mantenerse en registros de CPU o en alguna otra ubicación de almacenamiento auxiliar, o optimizarse por completo.
Entonces, la
d
matriz, en teoría, podría consumir memoria para toda la función. Sin embargo, el compilador puede optimizarlo o compartir su memoria con otras variables locales cuyas vidas de uso no se superponen.fuente
El tiempo durante el cual la variable realmente está ocupando memoria obviamente depende del compilador (y muchos compiladores no ajustan el puntero de la pila cuando se ingresan y salen bloques internos dentro de las funciones).
Sin embargo, una pregunta estrechamente relacionada pero posiblemente más interesante es si el programa puede acceder a ese objeto interno fuera del alcance interno (pero dentro de la función que lo contiene), es decir:
(En otras palabras: ¿se le permite al compilador desasignar
d
, incluso si en la práctica la mayoría no lo hace?).La respuesta es que el compilador está permitido cancelar la asignación
d
, y el accesop[0]
en el que el comentario indica es un comportamiento indefinido (el programa está no permitió el acceso a la parte exterior del objeto interno del ámbito interno). La parte relevante del estándar C es 6.2.4p5:fuente
Su pregunta no es lo suficientemente clara como para ser respondida sin ambigüedades.
Por un lado, los compiladores normalmente no realizan ninguna asignación de asignación de memoria local para ámbitos de bloque anidados. La memoria local normalmente se asigna solo una vez en la entrada de la función y se libera en la salida de la función.
Por otro lado, cuando finaliza la vida útil de un objeto local, la memoria ocupada por ese objeto puede reutilizarse para otro objeto local más tarde. Por ejemplo, en este código
ambas matrices generalmente ocuparán la misma área de memoria, lo que significa que la cantidad total de almacenamiento local que necesita la función
foo
es lo que sea necesario para la mayor de las dos matrices, no para las dos al mismo tiempo.d
Usted debe decidir si este último califica para continuar ocupando la memoria hasta el final de la función en el contexto de su pregunta.fuente
Depende de la implementación. Escribí un programa corto para probar lo que hace gcc 4.3.4, y asigna todo el espacio de la pila a la vez al comienzo de la función. Puede examinar el ensamblaje que produce gcc utilizando el indicador -S.
fuente
No, d [] no estará en la pila por el resto de la rutina. Pero alloca () es diferente.
Editar: Kristopher Johnson (y Simon y Daniel) tienen razón , y mi respuesta inicial fue incorrecta . Con gcc 4.3.4.on CYGWIN, el código:
da:
¡Vive y aprende! Y una prueba rápida parece mostrar que AndreyT también tiene razón sobre las asignaciones múltiples.
Se agregó mucho más tarde : la prueba anterior muestra que la documentación de gcc no es del todo correcta. Durante años ha dicho (énfasis agregado):
fuente
alloca
función. Estoy realmente sorprendido de que cygwin gcc haría eso. Ni siquiera es una matriz de longitud variable, así que IDK por qué lo mencionas.Podrían. Puede que no. La respuesta que creo que realmente necesitas es: nunca asumas nada. Los compiladores modernos hacen todo tipo de arquitectura y magia específica de implementación. Escriba su código de manera simple y legible para los humanos y deje que el compilador haga lo bueno. Si intenta codificar alrededor del compilador, está buscando problemas, y los problemas que generalmente tiene en estas situaciones suelen ser terriblemente sutiles y difíciles de diagnosticar.
fuente
Su variable
d
generalmente no se saca de la pila. Las llaves no indican un marco de pila. De lo contrario, no podría hacer algo como esto:Si las llaves causan un verdadero empuje / pop de pila (como lo haría una llamada a la función), entonces el código anterior no se compilaría porque el código dentro de las llaves no podría acceder a la variable
var
que vive fuera de las llaves (como un sub- La función no puede acceder directamente a las variables en la función de llamada). Sabemos que este no es el caso.Las llaves se utilizan simplemente para determinar el alcance. El compilador tratará cualquier acceso a la variable "interna" desde fuera de los corchetes como no válidos, y puede reutilizar esa memoria para otra cosa (esto depende de la implementación). Sin embargo, no se puede quitar de la pila hasta que vuelva la función de cierre.
Actualización: Esto es lo que la especificación C tiene que decir. Con respecto a los objetos con duración de almacenamiento automático (sección 6.4.2):
La misma sección define el término "vida útil" como (el énfasis es mío):
La palabra clave aquí es, por supuesto, 'garantizada'. Una vez que abandona el alcance del conjunto interno de llaves, la vida útil de la matriz ha terminado. El almacenamiento puede o no estar asignado (el compilador puede reutilizar el espacio para otra cosa), pero cualquier intento de acceder a la matriz invoca un comportamiento indefinido y produce resultados impredecibles.
La especificación C no tiene noción de marcos de pila. Solo habla de cómo se comportará el programa resultante y deja los detalles de la implementación al compilador (después de todo, la implementación se vería bastante diferente en una CPU sin pila que en una CPU con una pila de hardware). No hay nada en la especificación C que indique dónde terminará o no un marco de pila. La única forma real de saber es compilar el código en su compilador / plataforma particular y examinar el ensamblaje resultante. El conjunto actual de opciones de optimización de su compilador probablemente también jugará un papel en esto.
Si desea asegurarse de que la matriz
d
ya no consume memoria mientras se ejecuta el código, puede convertir el código entre llaves en una función separada o explícitamentemalloc
yfree
la memoria en lugar de utilizar el almacenamiento automático.fuente
Creo que está fuera de alcance, pero no se saca de la pila hasta que la función regrese. Por lo tanto, seguirá ocupando memoria en la pila hasta que se complete la función, pero no será accesible después de la primera llave de cierre.
fuente
Ya se ha dado mucha información sobre el estándar que indica que, de hecho, es específico de la implementación.
Entonces, un experimento podría ser de interés. Si probamos el siguiente código:
Usando gcc obtenemos aquí dos veces la misma dirección: Coliro
Pero si probamos el siguiente código:
Usando gcc obtenemos aquí dos direcciones diferentes: Coliro
Entonces, no puedes estar realmente seguro de lo que está sucediendo.
fuente