programa pilas pila parentesis llaves dinamica corchetes contar con codigo balanceo analizador c memory stack

pilas - En C, ¿las llaves actúan como un marco de pila?



pilas en c codigo (8)

Si creo una variable dentro de un nuevo conjunto de llaves, ¿esa variable sale de la pila en el corchete de cierre o 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 a tomar memoria durante el code that takes a while sección code that takes a while ?


Creo que sale fuera del alcance, pero no se elimina de la pila hasta que la función vuelva. Por lo tanto, seguirá ocupando la memoria en la pila hasta que se complete la función, pero no será accesible después de la primera llave de cierre.


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 conjunto que gcc produce utilizando la bandera -S.


El tiempo durante el cual la variable está realmente ocupando memoria es obviamente dependiente 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 tiene permiso para acceder a ese objeto interno fuera del alcance interno (pero dentro de la función que lo contiene), es decir:

void foo() { int c[100]; int *p; { int d[200]; p = d; } /* Can I access p[0] here? */ return; }

(En otras palabras: ¿se permite al compilador desasignar d , incluso si en la práctica la mayoría no lo hace?).

La respuesta es que el compilador puede desasignar d y acceder a p[0] donde el comentario indica un comportamiento indefinido (el programa no puede acceder al objeto interno fuera del alcance interno). La parte relevante del estándar C es 6.2.4p5:

Para dicho objeto [uno que tiene una duración de almacenamiento automática] que no tiene un tipo de matriz de longitud variable, su duración se extiende desde la entrada en el bloque con el que está asociado hasta que la ejecución de ese bloque termina de alguna manera . (Ingresar un bloque cerrado o llamar a una función suspende, pero no termina, la ejecución del bloque actual.) Si el bloque se ingresa recursivamente, se crea una nueva instancia del objeto cada vez. El valor inicial del objeto es indeterminado. Si se especifica una inicialización para el objeto, se realiza cada vez que se alcanza la declaración en la ejecución del bloque; de lo contrario, el valor se vuelve indeterminado cada vez que se alcanza la declaración.


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.en CYGWIN, el código:

void foo(int[]); void bar(void); void foobar(int); void foobar(int flag) { if (flag) { int big[100000000]; foo(big); } bar(); }

da:

_foobar: pushl %ebp movl %esp, %ebp movl $400000008, %eax call __alloca cmpl $0, 8(%ebp) je L2 leal -400000000(%ebp), %eax movl %eax, (%esp) call _foo L2: call _bar leave ret

¡Vive y aprende! Y una prueba rápida parece mostrar que AndreyT también tiene razón sobre las asignaciones múltiples.

Agregado mucho más tarde : la prueba anterior muestra que la documentación de gcc no es del todo correcta. Por años ha dicho (énfasis agregado):

"El espacio para una matriz de longitud variable se desasigna en cuanto finaliza el alcance del nombre de la matriz".


No, las llaves no actúan como un marco de pila. En C, las llaves solo denotan un alcance para nombrar, pero nada se destruye ni se saca nada de la pila cuando el control pasa de ella.

Como programador que escribe código, a menudo puede pensarlo como si fuera un marco de pila. Los identificadores declarados dentro de los corchetes solo son accesibles dentro de los corchetes, por lo que desde el punto de vista de un programador, es como si fueran insertados en la pila a medida que se declaran y luego aparecen cuando se sale del alcance. Sin embargo, los compiladores no tienen que generar código que empuje / revele cualquier cosa en la entrada / salida (y generalmente, no lo hacen).

También tenga en cuenta que las variables locales pueden no utilizar ningún espacio de pila en absoluto: pueden mantenerse en registros de la CPU o en otra ubicación de almacenamiento auxiliar, o pueden optimizarse por completo.

Entonces, la matriz d , 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 cuyos tiempos de vida de uso no se superponen.


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 la implementación. Escribe tu código simple y legiblemente a los humanos y deja que el compilador haga las cosas buenas. Si intenta codificar el compilador, está buscando problemas, y los problemas que suele tener en estas situaciones suelen ser terriblemente sutiles y difíciles de diagnosticar.


Su pregunta no es lo suficientemente clara como para responderla sin ambigüedades.

Por un lado, los compiladores normalmente no hacen ninguna asignación de memoria local-desasignación para los ámbitos de bloques 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 posteriormente para otro objeto local. Por ejemplo, en este código

void foo() { { int d[100]; } { double e[20]; } }

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 la necesaria para la mayor de dos matrices, no para las dos al mismo tiempo.

Si el último califica como d continuar ocupando la memoria hasta el final de la función en el contexto de su pregunta es para que usted decida.


Su variable d lo general no sale de la pila. Las llaves no indican un marco de pila. De lo contrario, no podrías hacer algo como esto:

char var = getch(); { char next_var = var + 1; use_variable(next_char); }

Si las llaves forzaran una verdadera pila push / pop (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 var variable que vive fuera de las llaves (como un sub -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 usan simplemente para determinar el alcance. El compilador considerará que cualquier acceso a la variable "interna" desde el exterior de las llaves adjuntas es inválido, y puede reutilizar esa memoria para otra cosa (esto depende de la implementación). Sin embargo, es posible que no salte de la pila hasta que regrese la función envolvente.

Actualización: esto es lo que la especificación C tiene para decir. En cuanto a objetos con duración de almacenamiento automático (sección 6.4.2):

Para un objeto que no tiene un tipo de matriz de longitud variable, su duración se extiende desde la entrada en el bloque con el que está asociado hasta que la ejecución de ese bloque termina de todos modos.

La misma sección define el término "vida" como (énfasis mío):

La duración de un objeto es la porción de ejecución del programa durante la cual se garantiza que el almacenamiento está reservado para él. Existe un objeto, tiene una dirección constante y conserva su último valor almacenado a lo largo de su vida útil. Si se hace referencia a un objeto fuera de su tiempo de vida, el comportamiento no está definido.

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 asignarse (su compilador puede volver a usar 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. Habla solo de cómo se comportará el programa resultante y deja los detalles de implementación al compilador (después de todo, la implementación serí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 terminará un marco de pila. La única manera 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 tu compilador también jugará un rol en esto.

Si desea asegurarse de que la matriz d ya no consuma memoria mientras se ejecuta su código, puede convertir el código entre llaves en una función separada o explícitamente malloc y free la memoria en lugar de utilizar el almacenamiento automático.