Teniendo en cuenta que la memoria se divide en cuatro segmentos: datos, montón, pila y código, donde las variables globales, variables estáticas, tipos de datos constantes, variables locales (definidas y declaradas en funciones), variables (en función principal), punteros y el espacio asignado dinámicamente (usando malloc y calloc) se almacena en la memoria
Creo que se asignarían de la siguiente manera:
- Variables globales -------> datos
- Variables estáticas -------> datos
- Tipos de datos constantes -----> código
- Variables locales (declaradas y definidas en funciones) --------> stack
- Variables declaradas y definidas en la función principal -----> montón
- Punteros (por ejemplo
char *arr
,int *arr
) -------> montón - Espacio asignado dinámicamente (usando malloc y calloc) --------> stack
Me refiero a estas variables solo desde la perspectiva C.
Corríjame si me equivoco, ya que soy nuevo en C.
c
memory
memory-management
types
starkk92
fuente
fuente
main
Es solo otra función. Las variables van a la pila a menos que seamalloc
como en cualquier otro lado.Respuestas:
Has acertado algunos de estos, pero quien escribió las preguntas te engañó en al menos una pregunta:
main
función ----->montóntambién se apilan (el profesor intentaba engañarte)char *arr
,int *arr
) ------->montónde datos o pila, dependiendo del contexto. C le permite declarar unstatic
puntero global o , en cuyo caso el puntero en sí mismo terminaría en el segmento de datos.malloc
,calloc
,realloc
) -------->pilamontónVale la pena mencionar que "stack" se llama oficialmente "clase de almacenamiento automático".
fuente
alloca
que funciona de manera similarmalloc
, pero sí la asignación de la pila.Para aquellos futuros visitantes que puedan estar interesados en conocer esos segmentos de memoria, estoy escribiendo puntos importantes sobre 5 segmentos de memoria en C:
Algunos jefes:
5 segmentos de memoria en C:
1. Segmento de código
printf("Hello, world")
, la cadena "Hola, mundo" se crea en el segmento de código / texto. Puede verificar esto usando elsize
comando en el sistema operativo Linux.Segmento de datos
El segmento de datos se divide en las dos partes siguientes y, por lo general, se encuentra debajo del área de almacenamiento dinámico o en algunas implementaciones por encima de la pila, pero el segmento de datos nunca se encuentra entre el área de almacenamiento dinámico y la pila.
2. Segmento de datos no inicializado
int globalVar;
o variable local estáticastatic int localStatic;
se almacenarán en el segmento de datos no inicializados.0
oNULL
entonces, iría al segmento de datos no inicializado o bss.3. Segmento de datos inicializado
int globalVar = 1;
o la variable local estáticastatic int localStatic = 1;
se almacenarán en el segmento de datos inicializado.4. Segmento de pila
5. Segmento del montón
malloc
,calloc
orealloc
métodos.int* prt = malloc(sizeof(int) * 2)
se asignarán ocho bytes en el montón y la dirección de memoria de esa ubicación se devolverá y almacenará enptr
variable. Laptr
variable estará en la pila o en el segmento de datos dependiendo de la forma en que se declare / use.fuente
Corregí tus oraciones incorrectas
variables constantes locales -----> stack
variable constante global inicializada -----> segmento de datos
variable constante global no inicializada -----> bss
variables declaradas y definidas en la función principal -----> stack
punteros (ej .: char * arr, int * arr) -------> el tamaño de esa variable de puntero estará en la pila.
Considere que está asignando memoria de n bytes (usando
malloc
ocalloc
) dinámicamente y luego haciendo una variable de puntero para apuntarlo. Ahora que losn
bytes de memoria están en el montón y la variable de puntero requiere 4 bytes (si la máquina de 64 bits es de 8 bytes) que estarán en la pila para almacenar el puntero inicial de losn
bytes de la porción de memoria.Nota: Las variables de puntero pueden apuntar la memoria de cualquier segmento.
espacio asignado dinámicamente (usando malloc, calloc) --------> montón
fuente
Una arquitectura de escritorio popular divide la memoria virtual de un proceso en varios segmentos :
Segmento de texto: contiene el código ejecutable. El puntero de instrucción toma valores en este rango.
Segmento de datos: contiene variables globales (es decir, objetos con enlace estático). Subdividido en datos de solo lectura (como constantes de cadena) y datos no inicializados ("BSS").
Segmento de pila: contiene la memoria dinámica para el programa, es decir, la tienda libre ("montón") y los marcos de pila locales para todos los hilos. Tradicionalmente, la pila C y la pila C solían crecer en el segmento de pila desde los extremos opuestos, pero creo que la práctica se ha abandonado porque es demasiado insegura.
El programa AC generalmente coloca objetos con una duración de almacenamiento estático en el segmento de datos, objetos asignados dinámicamente en el almacén gratuito y objetos automáticos en la pila de llamadas del hilo en el que vive.
En otras plataformas, como el antiguo modo real x86 o en dispositivos integrados, las cosas obviamente pueden ser radicalmente diferentes.
fuente
Desde la perspectiva del lenguaje C , todo lo que importa es la extensión, el alcance, la vinculación y el acceso; exactamente cómo se asignan los elementos a diferentes segmentos de memoria depende de la implementación individual, y eso variará. El lenguaje estándar no habla sobre segmentos de memoria en absoluto . La mayoría de las arquitecturas modernas actúan principalmente de la misma manera; las variables de alcance de bloque y los argumentos de función se asignarán desde la pila, el alcance de archivo y las variables estáticas se asignarán desde un segmento de datos o código, la memoria dinámica se asignará desde un montón, algunos datos constantes se almacenarán en segmentos de solo lectura etc.
fuente
Una cosa que hay que tener en cuenta sobre el almacenamiento es la regla como si . No se requiere que el compilador coloque una variable en un lugar específico; en su lugar, puede colocarla donde lo desee, siempre que el programa compilado se comporte como si se ejecutara en la máquina C abstracta de acuerdo con las reglas de la máquina C abstracta. Esto se aplica a todas las duraciones de almacenamiento . Por ejemplo:
42
en el código de ensamblado generado pero sin signos de404
.const
oconst
no necesita estar en la memoria. Ejemplo : el compilador puede demostrar quefoo
es efectivoconst
e integra su uso en el código.bar
tiene enlace externo y el compilador no puede probar que no se cambiaría fuera del módulo actual, por lo tanto, no está en línea.malloc
no necesita residir en la memoria asignada desde el montón! Ejemplo : observe cómo el código no tiene una llamadamalloc
y tampoco se almacena el valor 42 en la memoria, ¡se guarda en un registro!malloc
y la referencia se pierde sin desasignar el objeto sinfree
necesidad de perder memoria ...malloc
no necesita estar dentro del montón debajo del salto de programa (sbrk(0)
) en Unixen ...fuente
No, pueden estar en la pila o en el segmento de datos. Pueden apuntar a cualquier parte.
fuente
main
y variables dinámicamente asignados son demasiado malfuente
Ejemplos ejecutables mínimos de Linux con análisis de desmontaje
Dado que este es un detalle de implementación no especificado por los estándares, echemos un vistazo a lo que está haciendo el compilador en una implementación particular.
En esta respuesta, vincularé a respuestas específicas que hacen el análisis, o proporcionaré el análisis directamente aquí, y resumiré todos los resultados aquí.
Todos ellos están en varias versiones de Ubuntu / GCC, y los resultados son probablemente bastante estables en todas las versiones, pero si encontramos alguna variación, especifiquemos versiones más precisas.
Variable local dentro de una función
Ya sea
main
o cualquier otra función:Como se muestra en: ¿Qué significa <valor optimizado fuera> en gdb?
-O0
: apilar-O3
: registra si no se derraman, apilar de otra maneraPara obtener motivación sobre por qué existe la pila, consulte: ¿Cuál es la función de las instrucciones push / pop utilizadas en los registros en el ensamblaje x86?
Variables globales y
static
variables de función0
o no inicializa (y, por lo tanto, se inicializa implícitamente0
):.bss
sección, vea también: ¿Por qué se requiere el segmento .bss?.data
secciónchar *
ychar c[]
Como se muestra en: ¿Dónde se almacenan las variables estáticas en C y C ++?
¿TODO también se colocarán literales de cadena muy grandes en la pila? O
.data
? ¿O falla la compilación?Argumentos de la función
Debe pasar por la convención de llamada relevante, por ejemplo: https://en.wikipedia.org/wiki/X86_calling_conventions para X86, que especifica registros específicos o ubicaciones de pila para cada variable.
Entonces, como se muestra en ¿Qué significa <valor optimizado fuera> en gdb? ,
-O0
luego sorbe todo en la pila, mientras-O3
trata de usar registros tanto como sea posible.Sin embargo, si la función se alinea, se tratan como locales normales.
const
Creo que no hay diferencia porque puedes descartarlo.
Por el contrario, si el compilador puede determinar que algunos datos nunca se escriben, en teoría podría colocarlos en
.rodata
incluso si no son constantes.TODO análisis.
Punteros
Son variables (que contienen direcciones, que son números), igual que todas las demás :-)
malloc
La pregunta no tiene mucho sentido para
malloc
, ya quemalloc
es una función, y en:*i
es una variable que contiene una dirección, por lo que corresponde al caso anterior.En cuanto a cómo funciona malloc internamente, cuando lo llama, el kernel de Linux marca ciertas direcciones como grabables en sus estructuras de datos internas, y cuando el programa las toca inicialmente, ocurre una falla y el kernel habilita las tablas de páginas, lo que permite el acceso suceder sin segfaul: ¿Cómo funciona la paginación x86?
Sin embargo, tenga en cuenta que esto es básicamente exactamente lo que hace el
exec
syscall debajo del capó cuando intenta ejecutar un ejecutable: marca las páginas en las que desea cargar y escribe el programa allí, vea también: ¿Cómo obtiene el núcleo un archivo binario ejecutable que se ejecuta bajo linux? Excepto queexec
tiene algunas limitaciones adicionales sobre dónde cargar (por ejemplo, si el código no es reubicable ).La syscall exacta utilizada
malloc
es parammap
implementaciones modernas de 2020, y en el pasadobrk
se usaba: ¿malloc () usa brk () o mmap ()?Bibliotecas dinámicas
Básicamente,
mmap
vaya a la memoria: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710variables del entorno y
main
'sargv
Arriba de la pila inicial: /unix/75939/where-is-the-environment-string-actual-stored TODO ¿por qué no en .data?
fuente