¿Dónde en la memoria se almacenan mis variables en C?

156

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.

starkk92
fuente
44
Los tipos no se almacenan en la memoria.
55
mainEs solo otra función. Las variables van a la pila a menos que sea malloccomo en cualquier otro lado.
simonc
44
los punteros se almacenan (generalmente) en la pila. La memoria a la que apuntan (generalmente se asigna a través de malloc / calloc) está (generalmente) en el montón.
jpm
3
espacio asignado dinámicamente (usando malloc, calloc) --------> montón
One Man Crew
3
variables declaradas y definidas en la función principal -----> stack
One Man Crew

Respuestas:

217

Has acertado algunos de estos, pero quien escribió las preguntas te engañó en al menos una pregunta:

  • variables globales -------> datos (correcto)
  • variables estáticas -------> datos (correcto)
  • tipos de datos constantes -----> código y / o datos. Considere los literales de cadena para una situación en la que una constante se almacenaría en el segmento de datos, y las referencias a ella se incluirían en el código
  • variables locales (declaradas y definidas en funciones) --------> stack (correcto)
  • variables declaradas y definidas en la mainfunción -----> montón también se apilan (el profesor intentaba engañarte)
  • punteros (ej .: char *arr, int *arr) -------> montón de datos o pila, dependiendo del contexto. C le permite declarar un staticpuntero global o , en cuyo caso el puntero en sí mismo terminaría en el segmento de datos.
  • asignado dinámicamente el espacio (usando malloc, calloc, realloc) --------> pila montón

Vale la pena mencionar que "stack" se llama oficialmente "clase de almacenamiento automático".

dasblinkenlight
fuente
66
También vale la pena mencionar que el montón oficialmente no se llama nada en absoluto. La memoria asignada proviene de algún lugar, no hay nombre en el estándar para ese "algún lugar".
Steve Jessop el
66
En algunos sistemas, (a saber, Linux y * BSD) también existe el allocaque funciona de manera similar malloc, pero sí la asignación de la pila.
Andreas Grapentin el
¿A dónde van las variables const declaradas dentro de un método?
Mahori
@ Ravi El mismo lugar al que van el resto de las constantes (punto # 3 arriba).
dasblinkenlight
Estoy usando GCC 4.8.1 y no parece almacenar la variable constante local a principal en el segmento DATOS. A continuación se muestra el código y el mapa de memoria para 3 de estos programas: Código 1: int main (void) {// char a [10] = "HELLO"; // 1 // const char a [10] = "HOLA"; // 2 devuelve 0; } MAPA DE MEMORIA PARA ARRIBA: datos de texto bss dec nombre hexadecimal 7264 1688 1040 9992 2708 a.exe MAPA DE MEMORIA PARA 2: datos de texto bss dec nombre hexadecimal 7280 1688 1040 10008 2718 a.exe MAPA DE MEMORIA PARA 3: datos de texto bss dec nombre hexadecimal 7280 1688 1040 10008 2718 a.exe
Mahori
124

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:

  1. Cada vez que se ejecuta un programa en C, se asigna algo de memoria en la RAM para la ejecución del programa. Esta memoria se usa para almacenar el código ejecutado con frecuencia (datos binarios), variables de programa, etc. Los segmentos de memoria a continuación se refieren a lo mismo:
  2. Por lo general, hay tres tipos de variables:
    • Variables locales (también llamadas variables automáticas en C)
    • Variables globales
    • Variables estáticas
    • Puede tener variables estáticas globales o estáticas locales, pero las tres anteriores son los tipos principales.

5 segmentos de memoria en C:

1. Segmento de código

  • El segmento de código, también denominado segmento de texto, es el área de memoria que contiene el código ejecutado con frecuencia.
  • El segmento de código a menudo es de solo lectura para evitar el riesgo de ser anulado por errores de programación como desbordamiento de búfer, etc.
  • El segmento de código no contiene variables de programa como la variable local ( también llamada como variables automáticas en C ), variables globales, etc.
  • Según la implementación de C, el segmento de código también puede contener literales de cadena de solo lectura. Por ejemplo, cuando lo hace printf("Hello, world"), la cadena "Hola, mundo" se crea en el segmento de código / texto. Puede verificar esto usando el sizecomando en el sistema operativo Linux.
  • Otras lecturas

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

  • Este segmento también se conoce como bss .
  • Esta es la porción de memoria que contiene:
    1. Variables globales no inicializadas (incluidas las variables de puntero)
    2. Variables globales constantes no inicializadas .
    3. Variables estáticas locales no inicializadas .
  • Cualquier variable local global o estática que no se inicialice se almacenará en el segmento de datos no inicializados
  • Por ejemplo: variable global int globalVar;o variable local estáticastatic int localStatic; se almacenarán en el segmento de datos no inicializados.
  • Si declara una variable global e inicializa como 0oNULL entonces, iría al segmento de datos no inicializado o bss.
  • Otras lecturas

3. Segmento de datos inicializado

  • Este segmento almacena:
    1. Variables globales inicializadas (incluidas las variables de puntero)
    2. Variables globales constantes inicializadas .
    3. Inicializó variables estáticas locales .
  • Por ejemplo: la variable global int globalVar = 1;o la variable local estática static int localStatic = 1;se almacenarán en el segmento de datos inicializado.
  • Este segmento puede clasificarse adicionalmente en área de solo lectura inicializada y área de lectura-escritura inicializada . Las variables globales constantes inicializadas irán en el área de solo lectura inicializada, mientras que las variables cuyos valores pueden modificarse en tiempo de ejecución irán en el área de lectura-escritura inicializada .
  • El tamaño de este segmento está determinado por el tamaño de los valores en el código fuente del programa, y ​​no cambia en tiempo de ejecución .
  • Otras lecturas

4. Segmento de pila

  • El segmento de pila se usa para almacenar variables que se crean dentro de las funciones (la función podría ser la función principal o la función definida por el usuario) ), variables como
    1. Variables locales de la función (incluidas las variables de puntero)
    2. Argumentos pasados ​​a la función
    3. Dirección del remitente
  • Las variables almacenadas en la pila se eliminarán tan pronto como finalice la ejecución de la función.
  • Otras lecturas

5. Segmento del montón

  • Este segmento es para soportar la asignación dinámica de memoria. Si el programador quiere destinar parte de la memoria de forma dinámica a continuación, en C se realiza mediante las malloc, calloco reallocmétodos.
  • Por ejemplo, cuando 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á en ptrvariable. La ptrvariable estará en la pila o en el segmento de datos dependiendo de la forma en que se declare / use.
  • Otras lecturas
hagrawal
fuente
¿No debería ser inicializado en lugar de no inicializado en 3. Segmento de datos inicializado?
Suraj Jain
Re "almacenado en el segmento de datos sin inicializar" (varias instancias): ¿Quiere decir "almacenado sin inicializar en el segmento de datos" ?
Peter Mortensen
@ PeterMortensen Me refiero a ambas cosas. "Cualquier variable local global o estática que no se inicialice se almacenará en el segmento de datos no inicializados"
reunión del
¿Cómo podemos tener una variable estática global en C?
bajo "algunos avisos", encontré este punto "Puede tener variables estáticas globales o estáticas locales, pero las tres anteriores son los tipos principales". en el que se refirió al término "estática global". Mi punto es que una variable estática no puede ser global. es decir, si alguna variable tiene que ser global, entonces debería ser accesible hasta que se complete la ejecución del programa. Por favor explique y ayude si me equivoco.
11

Corregí tus oraciones incorrectas

constant data types ----->  code //wrong

variables constantes locales -----> stack

variable constante global inicializada -----> segmento de datos

variable constante global no inicializada -----> bss

variables declared and defined in main function  ----->  heap //wrong

variables declaradas y definidas en la función principal -----> stack

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

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 malloco calloc) dinámicamente y luego haciendo una variable de puntero para apuntarlo. Ahora que los nbytes 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 los nbytes de la porción de memoria.

Nota: Las variables de puntero pueden apuntar la memoria de cualquier segmento.

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

espacio asignado dinámicamente (usando malloc, calloc) --------> montón

rashok
fuente
los punteros pueden estar en la pila o en el montón (ver especialmente: punteros a punteros)
argentage
@airza: ahora actualizado. En realidad, solo estaba actualizando esos detalles :)
rashok
En el siguiente mapa de memoria, ¿podría indicar dónde está la pila y el montón? No estoy seguro de si esta es la pregunta correcta, ya que la pila y la memoria solo pueden aplicarse en tiempo de ejecución. MAPA DE MEMORIA: "datos de texto bss dec hexadecimal nombre de archivo 7280 1688 1040 10008 2718 a.exe"
Mahori
7

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.

Kerrek SB
fuente
"Creo que la práctica ha sido abandonada porque es demasiado insegura", y hace que sea imposible implementar subprocesos, ya que necesita más de una pila por programa y no pueden estar todos al final :-)
Steve Jessop
@SteveJessop: Sí, yo también estaba pensando eso. Pero los hilos han existido durante mucho tiempo: no sé si todas las pilas de hilos también crecieron hacia atrás, o si crecerían como el montón ... de todos modos, hoy en día todo va en la misma dirección y hay guardia páginas
Kerrek SB
6

Me refiero a estas variables solo desde la perspectiva C.

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.

John Bode
fuente
3

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:

  • Una variable a la que no se accede se puede eliminar por completo, no tiene almacenamiento ... en ninguna parte. Ejemplo : vea cómo hay 42en el código de ensamblado generado pero sin signos de 404.
  • una variable con una duración de almacenamiento automática que no tiene su dirección tomada no necesita almacenarse en la memoria en absoluto. Un ejemplo sería una variable de bucle.
  • Una variable que está consto constno necesita estar en la memoria. Ejemplo : el compilador puede demostrar que fooes efectivo conste integra su uso en el código. bartiene 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.
  • ¡un objeto asignado con mallocno necesita residir en la memoria asignada desde el montón! Ejemplo : observe cómo el código no tiene una llamada mallocy tampoco se almacena el valor 42 en la memoria, ¡se guarda en un registro!
  • por lo tanto, un objeto que ha sido asignado por mallocy la referencia se pierde sin desasignar el objeto sin free necesidad de perder memoria ...
  • el objeto asignado por mallocno necesita estar dentro del montón debajo del salto de programa ( sbrk(0)) en Unixen ...
Antti Haapala
fuente
1

punteros (ej .: char * arr, int * arr) -------> montón

No, pueden estar en la pila o en el segmento de datos. Pueden apuntar a cualquier parte.

Steve Wellens
fuente
Las declaraciones acerca mainy variables dinámicamente asignados son demasiado mal
simonc
No solo en la pila o segmento de datos. Piense en un puntero que apunta a una serie de punteros. En este caso, los punteros de la matriz se almacenan en el montón.
Sebi2020
1
  • Variables / variables automáticas ---> sección de pila
  • Variables asignadas dinámicamente ---> sección de montón
  • Variables globales inicializadas -> sección de datos
  • Variables globales no inicializadas -> sección de datos (bss)
  • Variables estáticas -> sección de datos
  • Constantes de cadena -> sección de texto / sección de código
  • Funciones -> sección de texto / sección de código
  • Código de texto -> sección de texto / sección de código
  • Registros -> registros de CPU
  • Entradas de línea de comando -> sección ambiental / línea de comando
  • Variables ambientales -> sección ambiental / línea de comando
prashad
fuente
¿Qué es la sección de línea de comando / ambiental? ¿Existen en Linux?
Haoyuan Ge
-1

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 maino cualquier otra función:

void f(void) {
    int my_local_var;
}

Como se muestra en: ¿Qué significa <valor optimizado fuera> en gdb?

  • -O0: apilar
  • -O3: registra si no se derraman, apilar de otra manera

Para 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 staticvariables de función

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}

char * y char c[]

Como se muestra en: ¿Dónde se almacenan las variables estáticas en C y C ++?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

¿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

void f(int i, int j);

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? , -O0luego 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 que malloces una función, y en:

int *i = malloc(sizeof(int));

*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 execsyscall 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 que exectiene algunas limitaciones adicionales sobre dónde cargar (por ejemplo, si el código no es reubicable ).

La syscall exacta utilizada malloces para mmapimplementaciones modernas de 2020, y en el pasado brkse usaba: ¿malloc () usa brk () o mmap ()?

Bibliotecas dinámicas

Básicamente, mmapvaya a la memoria: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

variables 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?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente