ventajas variable una son qué que publicas método metodos memoria locales las hay globales estático estaticos estaticas entre diferencia desventajas como administra c memory gcc x86 stack

una - Orden de asignación de variable local en la pila



variables locales pdf (9)

¿También podría ser un problema de seguridad?

int main() { int array[10]; int i; for (i = 0; i <= 10; ++i) { array[i] = 0; } }

Si la matriz es más baja en la pila que yo, este código se repetirá infinitamente (porque tiene acceso erróneo y pone a cero la matriz [10], que es i). Al colocar la matriz más arriba en la pila, los intentos de acceder a la memoria más allá del final de la pila tendrán más probabilidades de tocar la memoria no asignada y colgarse, en lugar de causar un comportamiento indefinido.

Experimenté con este mismo código una vez con gcc, y no pude hacerlo fallar excepto con una combinación particular de banderas que ahora no recuerdo ... En cualquier caso, colocó la matriz varios bytes de distancia de i.

Eche un vistazo a estas dos funciones:

void function1() { int x; int y; int z; int *ret; } void function2() { char buffer1[4]; char buffer2[4]; char buffer3[4]; int *ret; }

Si rompo en function1() en gdb , e imprimo las direcciones de las variables, obtengo esto:

(gdb) p &x $1 = (int *) 0xbffff380 (gdb) p &y $2 = (int *) 0xbffff384 (gdb) p &z $3 = (int *) 0xbffff388 (gdb) p &ret $4 = (int **) 0xbffff38c

Si hago lo mismo en function2() , obtengo esto:

(gdb) p &buffer1 $1 = (char (*)[4]) 0xbffff388 (gdb) p &buffer2 $2 = (char (*)[4]) 0xbffff384 (gdb) p &buffer3 $3 = (char (*)[4]) 0xbffff380 (gdb) p &ret $4 = (int **) 0xbffff38c

Notarás que en ambas funciones, ret se almacena más cerca de la parte superior de la pila. En function1() , es seguido por z , y , y finalmente x . En function2() , ret es seguido por buffer1 , luego buffer2 y buffer3 . ¿Por qué se cambia la orden de almacenamiento? Estamos usando la misma cantidad de memoria en ambos casos (matrices de 4 bytes frente a matrices de 4 bytes), por lo que no puede ser un problema de relleno. ¿Qué razones podría haber para este reordenamiento, y además, es posible mirando el código C para determinar de antemano cómo se ordenarán las variables locales?

Ahora soy consciente de que las especificaciones de ANSI para C no dicen nada sobre el orden en que se almacenan las variables locales y que el compilador puede elegir su propio orden, pero me imagino que el compilador tiene reglas sobre cómo se ocupa de las mismas. esto, y explicaciones de por qué esas reglas fueron hechas para ser como son.

Como referencia, estoy usando GCC 4.0.1 en Mac OS 10.5.7


Curiosamente, si agrega un int * ret2 extra en función1, entonces el orden es correcto, mientras que está fuera de servicio solo para 3 variables locales. Supongo que está ordenado de esa manera debido a que refleja la estrategia de asignación de registros que se usará. O eso o es arbitrario.


El ISO C no solo no dice nada sobre el orden de las variables locales en la pila, ni siquiera garantiza que exista una pila. El estándar solo habla sobre el alcance y la duración de las variables dentro de un bloque.


El estándar C no dicta ningún diseño para las otras variables automáticas. Sin embargo, específicamente dice, para evitar dudas, que

[...] El diseño del almacenamiento para los parámetros [función] no está especificado. (C11 6.9.1p9)

Se puede entender por eso que el diseño de almacenamiento para cualquier otro objeto tampoco se especifica, excepto para los pocos requisitos que da el estándar, incluyendo que el puntero nulo no puede apuntar a ningún objeto o función válida, y los diseños dentro del agregado objetos.

El estándar C no contiene una sola mención a la palabra "pila"; es muy posible hacer, por ejemplo, una implementación de C sin apilamiento, asignando cada registro de activación del montón (aunque tal vez se podría entender entonces que forman una pila).

Una de las razones para dar al compilador cierto margen de maniobra es la eficiencia. Sin embargo, los compiladores actuales también usarían esto para la seguridad, utilizando trucos como la asignación al azar del diseño del espacio de direcciones y la pila de canarios para tratar de hacer más difícil la explotación del comportamiento indefinido . El reordenamiento de los buffers se hace para que el uso de canary sea más efectivo.


Entonces, hice algo más de experimentación y esto es lo que encontré. Parece estar basado en si cada variable es o no una matriz. Teniendo en cuenta esta entrada:

void f5() { int w; int x[1]; int *ret; int y; int z[1]; }

Termino con esto en gdb:

(gdb) p &w $1 = (int *) 0xbffff4c4 (gdb) p &x $2 = (int (*)[1]) 0xbffff4c0 (gdb) p &ret $3 = (int **) 0xbffff4c8 (gdb) p &y $4 = (int *) 0xbffff4cc (gdb) p &z $5 = (int (*)[1]) 0xbffff4bc

En este caso, los int y los punteros se tratan primero, el último declarado en la parte superior de la pila y el primero declarado más cerca del final. Luego, las matrices se manejan, en la dirección opuesta, cuanto antes la declaración, más arriba en la pila. Estoy seguro de que hay una buena razón para esto. Me pregunto qué es.


No tengo idea de por qué GCC organiza su pila de la forma en que lo hace (aunque supongo que podrías descifrar su fuente o este documento y averiguarlo), pero puedo decirte cómo garantizar el orden de las variables de pila específicas si por alguna razón necesitas. Simplemente ponlos en una estructura:

void function1() { struct { int x; int y; int z; int *ret; } locals; }

Si mi memoria me sirve correctamente, spec garantiza que &ret > &z > &y > &x . Dejé mi K & R en el trabajo, así que no puedo citar el capítulo y el versículo.


Por lo general, tiene que ver con problemas de alineación.

La mayoría de los procesadores son más lentos en la búsqueda de datos que no están alineados con la palabra del procesador. Deben agarrarlo en pedazos y empalmarlo.

Probablemente lo que está sucediendo es que está juntando todos los objetos que son mayores o iguales a la alineación óptima del procesador, y luego ajustando más apretadamente las cosas que pueden no estar alineadas. Sucede que en tu ejemplo todas tus matrices de caracteres son de 4 bytes, pero apuesto a que si los conviertes en 3 bytes, terminarán en los mismos lugares.

Pero si tiene cuatro matrices de un byte, pueden terminar en un rango de 4 bytes, o alinearse en cuatro separadas.

Se trata de lo que es más fácil (se traduce en "más rápido") para que el procesador lo agarre.


Supongo que esto tiene algo que ver con la forma en que se cargan los datos en los registros. Quizás, con las matrices de caracteres, el compilador funciona algo de magia para hacer cosas en paralelo y esto tiene algo que ver con la posición en la memoria para cargar fácilmente los datos en los registros. Intente compilar con diferentes niveles de optimización e intente usar int buffer1[1] lugar.


Todo depende del compilador. Más allá de esto, ciertas variables de procedimiento podrían nunca colocarse en la pila, ya que pueden pasar toda su vida dentro de un registro.