¿Qué significa realmente "Memoria asignada en tiempo de compilación"?


159

En lenguajes de programación como C y C ++, las personas a menudo se refieren a la asignación de memoria estática y dinámica. Entiendo el concepto, pero la frase "Toda la memoria fue asignada (reservada) durante el tiempo de compilación" siempre me confunde.

La compilación, según tengo entendido, convierte el código C / C ++ de alto nivel al lenguaje de máquina y genera un archivo ejecutable. ¿Cómo se "asigna" la memoria en un archivo compilado? ¿No se asigna siempre la memoria en la RAM con todas las cosas de administración de memoria virtual?

¿No es la asignación de memoria por definición un concepto de tiempo de ejecución?

Si hago una variable asignada estáticamente de 1 KB en mi código C / C ++, ¿aumentará eso el tamaño del ejecutable en la misma cantidad?

Esta es una de las páginas donde se usa la frase bajo el encabezado "Asignación estática".

Volver a lo básico: asignación de memoria, un recorrido por el historial


El código y los datos están totalmente segregados en la mayoría de las arquitecturas modernas. mientras que los archivos fuente contienen ambos datos de código en el mismo lugar, el contenedor solo tiene referencias a datos. Esto significa que los datos estáticos en la fuente solo se resuelven como referencias.
Cholthi Paul Ttiopic

Respuestas:


184

La memoria asignada en tiempo de compilación significa que el compilador resuelve en tiempo de compilación donde ciertas cosas se asignarán dentro del mapa de memoria del proceso.

Por ejemplo, considere una matriz global:

int array[100];

El compilador sabe en tiempo de compilación el tamaño de la matriz y el tamaño de una int, por lo que conoce el tamaño completo de la matriz en tiempo de compilación. Además, una variable global tiene una duración de almacenamiento estático por defecto: se asigna en el área de memoria estática del espacio de memoria de proceso (sección .data / .bss). Dada esa información, el compilador decide durante la compilación en qué dirección de esa área de memoria estática estará la matriz .

Por supuesto que las direcciones de memoria son direcciones virtuales. El programa asume que tiene su propio espacio de memoria completo (desde 0x00000000 hasta 0xFFFFFFFF por ejemplo). Es por eso que el compilador podría hacer suposiciones como "Bien, la matriz estará en la dirección 0x00A33211". En tiempo de ejecución, la MMU y el sistema operativo traducen las direcciones a direcciones reales / de hardware.

Las cosas de almacenamiento estático inicializadas de valor son un poco diferentes. Por ejemplo:

int array[] = { 1 , 2 , 3 , 4 };

En nuestro primer ejemplo, el compilador solo decidió dónde se asignará la matriz, almacenando esa información en el ejecutable.
En el caso de cosas con valor inicializado, el compilador también inyecta el valor inicial de la matriz en el ejecutable, y agrega código que le dice al cargador del programa que después de la asignación de la matriz al inicio del programa, la matriz se debe llenar con estos valores.

Aquí hay dos ejemplos del ensamblaje generado por el compilador (GCC4.8.1 con destino x86):

Código C ++:

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

Ensamblaje de salida:

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

Como puede ver, los valores se inyectan directamente en el ensamblaje. En la matriz a, el compilador genera una inicialización cero de 16 bytes, porque el Estándar dice que las cosas almacenadas estáticas deben inicializarse a cero por defecto:

8.5.9 (Inicializadores) [Nota]:
Cada objeto de la duración del almacenamiento estático se inicializa en cero al inicio del programa antes de que tenga lugar cualquier otra inicialización. En algunos casos, la inicialización adicional se realiza más tarde.

Siempre sugiero a las personas que desmonten su código para ver qué hace realmente el compilador con el código C ++. Esto se aplica desde las clases de almacenamiento / duración (como esta pregunta) hasta las optimizaciones avanzadas del compilador. Puede indicarle a su compilador que genere el ensamblado, pero existen herramientas maravillosas para hacerlo en Internet de manera amigable. Mi favorito es GCC Explorer .


2
Gracias. Esto aclara mucho. Entonces, el compilador genera algo equivalente a "reservar memoria desde 0xABC hasta 0xXYZ para la matriz variable [] etc." y luego el cargador usa eso para realmente asignarlo justo antes de ejecutar el programa?
Talha Sayed

1
@TalhaSayed exactamente. Vea la edición para ver el ejemplo
Manu343726

2
@Secko he simplificado las cosas. Es solo una mención sobre el programa que funciona a través de la memoria virtual, pero como la pregunta no es sobre la memoria virtual, no he extendido el tema. Solo apuntaba que el compilador puede hacer suposiciones sobre las direcciones de memoria en tiempo de compilación, gracias a la memoria virtual.
Manu343726

2
@Secko sí. mmm "generado" es un término mejor, creo.
Manu343726

2
"Se asigna en el área mamaria estática del espacio de memoria del proceso" Lectura que asignó algunas áreas mamarias estáticas en mi espacio de memoria del proceso.
Radiodef

27

La memoria asignada en tiempo de compilación simplemente significa que no habrá más asignaciones en tiempo de ejecución, no habrá llamadas a malloc, nuevos u otros métodos de asignación dinámica. Tendrá una cantidad fija de uso de memoria incluso si no necesita toda esa memoria todo el tiempo.

¿No es la asignación de memoria por definición un concepto de tiempo de ejecución?

La memoria no está en uso antes del tiempo de ejecución, pero el sistema maneja inmediatamente antes de que se inicie su asignación.

Si hago una variable asignada estáticamente de 1 KB en mi código C / C ++, ¿aumentará eso el tamaño del ejecutable en la misma cantidad?

Simplemente declarar la estática no aumentará el tamaño de su ejecutable más de unos pocos bytes. Declararlo con un valor inicial que no sea cero lo hará (para mantener ese valor inicial). Por el contrario, el vinculador simplemente agrega esta cantidad de 1 KB al requisito de memoria que el cargador del sistema crea para usted inmediatamente antes de la ejecución.


1
si escribo static int i[4] = {2 , 3 , 5 ,5 }, aumentará en tamaño ejecutable en 16 bytes. Dijiste "Simplemente declarar la estática no aumentará el tamaño de tu ejecutable más que unos pocos bytes. Declararlo con un valor inicial que no sea cero" Declararlo con un valor inicial será lo que significa.
Suraj Jain

Su ejecutable tiene dos áreas para datos estáticos: una para las estadísticas no inicializadas y otra para las estáticas inicializadas. El área no inicializada es realmente solo una indicación de tamaño; cuando se ejecuta su programa, ese tamaño se utiliza para aumentar el área de almacenamiento estático, pero el programa en sí no tuvo que contener nada más que la cantidad de datos no inicializados que se utilizan. Para las estadísticas inicializadas, su programa debe contener no solo el tamaño de (cada) estático, sino también el que se inicializa. Por lo tanto, en su ejemplo, su programa tendrá 2, 3, 5 y 5.
mah

Su implementación se define en cuanto a dónde se coloca / cómo se asigna, pero no estoy seguro de entender la necesidad de saber.
mah

23

La memoria asignada en tiempo de compilación significa que cuando cargue el programa, una parte de la memoria se asignará inmediatamente y el tamaño y la posición (relativa) de esta asignación se determinarán en tiempo de compilación.

char a[32];
char b;
char c;

Esas 3 variables están "asignadas en tiempo de compilación", significa que el compilador calcula su tamaño (que es fijo) en tiempo de compilación. La variable aserá un desplazamiento en la memoria, digamos, apuntando a la dirección 0, bapuntará a la dirección 33 y ca la 34 (suponiendo que no haya optimización de alineación). Entonces, asignar 1Kb de datos estáticos no aumentará el tamaño de su código , ya que solo cambiará un desplazamiento dentro de él. El espacio real se asignará en el momento de la carga. .

La asignación de memoria real siempre ocurre en tiempo de ejecución, porque el kernel necesita realizar un seguimiento y actualizar sus estructuras de datos internas (cuánta memoria se asigna para cada proceso, páginas, etc.). La diferencia es que el compilador ya conoce el tamaño de cada dato que va a usar y esto se asigna tan pronto como se ejecuta su programa.

Recuerde también que estamos hablando de direcciones relativas . La dirección real donde se ubicará la variable será diferente. En el momento de la carga, el núcleo reservará algo de memoria para el proceso, digamos en la dirección x, y todas las direcciones codificadas contenidas en el archivo ejecutable se incrementarán en xbytes, de modo que la variable aen el ejemplo estará en la dirección x, b en la dirección x+33y pronto.


17

Agregar variables en la pila que ocupan N bytes no (necesariamente) aumenta el tamaño del contenedor en N bytes. De hecho, agregará solo unos pocos bytes la mayor parte del tiempo.
Comencemos con un ejemplo de cómo agregar 1000 caracteres a su código voluntad aumentar el tamaño de la papelera de forma lineal.

Si el 1k es una cadena, de mil caracteres, que se declara así

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

y luego vim your_compiled_binlo harías, en realidad podrías ver esa cadena en el contenedor en alguna parte. En ese caso, sí: el ejecutable será 1 k más grande, porque contiene la cadena en su totalidad.
Sin embargo, si asigna una matriz de ints, charso longs en la pila y la asigna en un bucle, algo en esta línea

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

entonces, no: no aumentará el contenedor ... por 1000*sizeof(int)
Asignación en tiempo de compilación significa lo que ahora ha llegado a entender que significa (según sus comentarios): el contenedor compilado contiene información que el sistema requiere para saber cuánta memoria qué función / bloque necesitará cuando se ejecute, junto con información sobre el tamaño de pila que requiere su aplicación. Eso es lo que el sistema asignará cuando ejecute su bin, y su programa se convierta en un proceso (bueno, la ejecución de su bin es el proceso que ... bueno, entiende lo que digo).
supuesto, no estoy pintando la imagen completa aquí: la papelera contiene información sobre qué tan grande será la pila que la papelera realmente necesitará. Basado en esta información (entre otras cosas), el sistema reservará una porción de memoria, llamada pila, sobre la cual el programa tendrá una especie de reinado libre. El sistema todavía asigna memoria de pila cuando se inicia el proceso (el resultado de la ejecución de su bin). El proceso gestiona la memoria de la pila por ti. Cuando se invoca / ejecuta una función o bucle (cualquier tipo de bloque), las variables locales de ese bloque se envían a la pila y se eliminan (la memoria de la pila se "libera", por así decirlo) para que otras personas las usen. funciones / bloques. Declarandoint some_array[100]solo agregará unos pocos bytes de información adicional al contenedor, que le indica al sistema que la función X requerirá 100*sizeof(int)+ un poco de espacio adicional de contabilidad.


Muchas gracias. Una pregunta más, ¿las variables locales para las funciones también se asignan de la misma manera durante el tiempo de compilación?
Talha Sayed

@TalhaSayed: Sí, eso es lo que quise decir cuando dije: "información que el sistema requiere para saber cuánta memoria requerirá qué función / bloque". En el momento en que llame a una función, el sistema asignará la memoria requerida para esa función. En el momento en que la función regrese, esa memoria se liberará nuevamente.
Elias Van Ootegem

En cuanto a los comentarios en su código C: Eso no es realmente / necesariamente lo que sucede. Por ejemplo, lo más probable es que la cadena se asigne solo una vez, en tiempo de compilación. Por lo tanto, nunca se "libera" (también creo que la terminología generalmente solo se usa cuando se asigna algo dinámicamente), itampoco se "libera". Si iresidiera en la memoria, simplemente sería empujado a la pila, algo que no se libera en ese sentido de la palabra, ignorando eso io cse mantendrá en registros todo el tiempo. Por supuesto, todo esto depende del compilador, lo que significa que no es tan blanco y negro.
phant0m

@ phant0m: nunca dije que la cadena se asigna en la pila, solo el puntero también sería, la cadena misma residiría en la memoria de solo lectura. Sé que la memoria asociada con las variables locales no se libera en el sentido de las free()llamadas, pero la memoria de pila que usaron es gratuita para que otras funciones la usen una vez que la función que enumeré regresa.
Eliminé

Ah, ya veo. En ese caso, tome mi comentario como "me confundió su redacción".
phant0m

16

En muchas plataformas, el compilador consolidará todas las asignaciones globales o estáticas dentro de cada módulo en tres o menos asignaciones consolidadas (una para datos no inicializados (a menudo llamados "bss"), una para datos de escritura inicializados (a menudo llamados "datos" ), y uno para datos constantes ("const")), y todas las asignaciones globales o estáticas de cada tipo dentro de un programa serán consolidadas por el vinculador en una global para cada tipo. Por ejemplo, suponiendo que intson cuatro bytes, un módulo tiene lo siguiente como sus únicas asignaciones estáticas:

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

le diría al enlazador que necesitaba 208 bytes para bss, 16 bytes para "datos" y 28 bytes para "const". Además, cualquier referencia a una variable se reemplazaría con un selector de área y desplazamiento, por lo que a, b, c, d, y e, se reemplazarían por bss + 0, const + 0, bss + 4, const + 24, datos +0 o bss + 204, respectivamente.

Cuando se vincula un programa, todas las áreas bss de todos los módulos se concatenan juntas; igualmente los datos y las áreas constantes. Para cada módulo, la dirección de cualquier variable relativa a bss se incrementará en el tamaño de todas las áreas bss de los módulos anteriores (de nuevo, de la misma manera con datos y const). Por lo tanto, cuando se realiza el enlazador, cualquier programa tendrá una asignación bss, una asignación de datos y una asignación constante.

Cuando se carga un programa, generalmente sucederá una de cuatro cosas dependiendo de la plataforma:

  1. El ejecutable indicará cuántos bytes necesita para cada tipo de datos y, para el área de datos inicializada, donde se pueden encontrar los contenidos iniciales. También incluirá una lista de todas las instrucciones que usan una dirección relativa a bss, data o constante. El sistema operativo o el cargador asignarán la cantidad adecuada de espacio para cada área y luego agregarán la dirección inicial de esa área a cada instrucción que lo necesite.

  2. El sistema operativo asignará una porción de memoria para contener los tres tipos de datos y le dará a la aplicación un puntero a esa porción de memoria. Cualquier código que use datos estáticos o globales lo desreferenciará en relación con ese puntero (en muchos casos, el puntero se almacenará en un registro durante la vida útil de una aplicación).

  3. Inicialmente, el sistema operativo no asignará ninguna memoria a la aplicación, excepto lo que contiene su código binario, pero lo primero que hará la aplicación será solicitar una asignación adecuada del sistema operativo, que siempre mantendrá en un registro.

  4. El sistema operativo inicialmente no asignará espacio para la aplicación, pero la aplicación solicitará una asignación adecuada al inicio (como se indicó anteriormente). La aplicación incluirá una lista de instrucciones con direcciones que deben actualizarse para reflejar dónde se asignó la memoria (como con el primer estilo), pero en lugar de tener la aplicación parcheada por el cargador del sistema operativo, la aplicación incluirá suficiente código para parchearse .

Los cuatro enfoques tienen ventajas y desventajas. Sin embargo, en todos los casos, el compilador consolidará un número arbitrario de variables estáticas en un pequeño número fijo de solicitudes de memoria, y el vinculador consolidará todas ellas en un pequeño número de asignaciones consolidadas. Aunque una aplicación tendrá que recibir una porción de memoria del sistema operativo o del cargador, es el compilador y el enlazador los responsables de asignar las piezas individuales de esa porción grande a todas las variables individuales que lo necesitan.


13

El núcleo de su pregunta es: "¿Cómo se" asigna "la memoria en un archivo compilado? ¿No se asigna siempre la memoria en la RAM con todas las cosas de administración de memoria virtual? ¿No es la asignación de memoria por definición un concepto de tiempo de ejecución?"

Creo que el problema es que hay dos conceptos diferentes involucrados en la asignación de memoria. Básicamente, la asignación de memoria es el proceso mediante el cual decimos "este elemento de datos se almacena en esta porción específica de memoria". En un sistema informático moderno, esto implica un proceso de dos pasos:

  • Algunos sistemas se utilizan para decidir la dirección virtual en la que se almacenará el artículo.
  • La dirección virtual se asigna a una dirección física.

El último proceso es puramente en tiempo de ejecución, pero el primero se puede hacer en tiempo de compilación, si los datos tienen un tamaño conocido y se requiere un número fijo de ellos. Así es básicamente cómo funciona:

  • El compilador ve un archivo fuente que contiene una línea que se parece un poco a esto:

    int c;
  • Produce una salida para el ensamblador que le indica que reserve memoria para la variable 'c'. Esto podría verse así:

    global _c
    section .bss
    _c: resb 4
  • Cuando se ejecuta el ensamblador, mantiene un contador que rastrea las compensaciones de cada elemento desde el inicio de un 'segmento' de memoria (o 'sección'). Esto es como las partes de una 'estructura' muy grande que contiene todo en el archivo completo, no tiene memoria real asignada en este momento y podría estar en cualquier lugar. Se observa en una tabla que _ctiene un desplazamiento particular (digamos 510 bytes desde el inicio del segmento) y luego incrementa su contador en 4, por lo que la siguiente variable estará en (por ejemplo) 514 bytes. Para cualquier código que necesite la dirección de _c, solo pone 510 en el archivo de salida y agrega una nota de que la salida necesita la dirección del segmento que contiene _cmás tarde.

  • El vinculador toma todos los archivos de salida del ensamblador y los examina. Determina una dirección para cada segmento para que no se superpongan, y agrega las compensaciones necesarias para que las instrucciones aún se refieran a los elementos de datos correctos. En el caso de la memoria no inicializada como la ocupada porc(se le dijo al ensamblador que la memoria no se inicializaría por el hecho de que el compilador la puso en el segmento '.bss', que es un nombre reservado para la memoria no inicializada), incluye un campo de encabezado en su salida que le indica al sistema operativo cuánto necesita ser reservado. Puede ser reubicado (y generalmente lo es), pero generalmente está diseñado para cargarse de manera más eficiente en una dirección de memoria en particular, y el sistema operativo intentará cargarlo en esta dirección. En este punto, tenemos una idea bastante buena de cuál es la dirección virtual que utilizará c.

  • La dirección física no se determinará en realidad hasta que se ejecute el programa. Sin embargo, desde la perspectiva del programador, la dirección física es realmente irrelevante: nunca descubriremos qué es, porque el sistema operativo generalmente no se molesta en decirle a nadie, puede cambiar con frecuencia (incluso mientras el programa se está ejecutando), y El objetivo principal del sistema operativo es abstraer esto de todos modos.


9

Un ejecutable describe qué espacio asignar para las variables estáticas. El sistema realiza esta asignación cuando ejecuta el ejecutable. Por lo tanto, su variable estática de 1kB no aumentará el tamaño del ejecutable con 1kB:

static char[1024];

A menos que, por supuesto, especifique un inicializador:

static char[1024] = { 1, 2, 3, 4, ... };

Entonces, además del 'lenguaje de máquina' (es decir, instrucciones de la CPU), un ejecutable contiene una descripción del diseño de memoria requerido.


5

La memoria se puede asignar de muchas maneras:

  • en el montón de aplicaciones (el sistema operativo asigna el montón completo para su aplicación cuando se inicia el programa)
  • en el montón del sistema operativo (para que pueda obtener más y más)
  • en el montón controlado por el recolector de basura (igual que los dos anteriores)
  • en la pila (para que pueda obtener un desbordamiento de la pila)
  • reservado en el segmento de código / datos de su binario (ejecutable)
  • en lugar remoto (archivo, red, y recibe un identificador, no un puntero a esa memoria)

Ahora su pregunta es qué es "memoria asignada en tiempo de compilación". Definitivamente es solo un dicho incorrectamente redactado, que se supone que se refiere a la asignación de segmento binario o la asignación de pila, o en algunos casos incluso a una asignación de montón, pero en ese caso la asignación está oculta a los ojos del programador por una llamada invisible del constructor. O probablemente la persona que dijo que solo quería decir que la memoria no está asignada en el montón, pero que no sabía acerca de las asignaciones de pila o segmento (o no quería entrar en ese tipo de detalles).

Pero en la mayoría de los casos, la persona solo quiere decir que la cantidad de memoria asignada se conoce en tiempo de compilación .

El tamaño binario solo cambiará cuando la memoria esté reservada en el segmento de código o datos de su aplicación.


1
Esta respuesta es confusa (o confusa) en el sentido de que habla sobre "el montón de aplicaciones", "el montón del sistema operativo" y "el montón del GC" como si todos estos fueran conceptos significativos. Infiero que con el n. ° 1 estabas tratando de decir que algunos lenguajes de programación podrían (hipotéticamente) usar un esquema de "asignación de montón" que asigna memoria de un búfer de tamaño fijo en la sección .data, pero eso parece poco realista para ser dañino a la comprensión del OP. Re # 2 y # 3, la presencia de un GC realmente no cambia nada. Y re # 5, omitió la distinción relativamente MUCHO más importante entre .datay .bss.
Quuxplusone

4

Tienes razón. La memoria se asigna (paginado) en el momento de la carga, es decir, cuando el archivo ejecutable se lleva a la memoria (virtual). La memoria también se puede inicializar en ese momento. El compilador solo crea un mapa de memoria. [Por cierto, ¡los espacios de pila y montón también se asignan en el momento de la carga!]


2

Creo que necesitas retroceder un poco. Memoria asignada en tiempo de compilación ... ¿Qué puede significar eso? ¿Puede significar que la memoria en chips que aún no se han fabricado, para computadoras que aún no se han diseñado, se está reservando de alguna manera? No. No, viaje en el tiempo, no hay compiladores que puedan manipular el universo.

Por lo tanto, debe significar que el compilador genera instrucciones para asignar esa memoria de alguna manera en tiempo de ejecución. Pero si lo miras desde el ángulo correcto, el compilador genera todas las instrucciones, entonces, ¿cuál puede ser la diferencia? La diferencia es que el compilador decide, y en tiempo de ejecución, su código no puede cambiar ni modificar sus decisiones. Si decidió que necesitaba 50 bytes en tiempo de compilación, en tiempo de ejecución, no puede hacer que decida asignar 60, esa decisión ya se ha tomado.


Me gustan las respuestas que usan el método socrático, pero aún así te rechacé por la conclusión errónea de que "el compilador genera instrucciones para asignar esa memoria de alguna manera en tiempo de ejecución". Consulte la respuesta más votada para ver cómo un compilador puede "asignar memoria" sin generar "instrucciones" de tiempo de ejecución. (Tenga en cuenta que "instrucciones" en un contexto de lenguaje ensamblador tiene un significado específico, es decir, códigos de operación ejecutables. Es posible que haya estado usando la palabra coloquialmente para significar algo como "receta", pero en este contexto eso confundirá al OP. )
Quuxplusone

1
@Quuxplusone: leí (y voté) esa respuesta. Y no, mi respuesta no aborda específicamente el problema de las variables inicializadas. Tampoco aborda el código auto modificable. Si bien esa respuesta es excelente, no abordó lo que considero un tema importante: poner las cosas en contexto. De ahí mi respuesta, que espero ayude al OP (y a otros) a detenerse y pensar en lo que está o puede estar sucediendo cuando tienen problemas que no entienden.
jmoreno

@Quuxplusone: Perdón si estoy haciendo acusaciones falsas aquí, pero supongo que usted fue una de las personas que también respondió mi respuesta. Si es así, ¿le importaría señalar qué parte de mi respuesta fue la razón principal para hacerlo, y también le gustaría revisar mi edición? Sé que me he saltado algunos bits sobre los aspectos internos reales de cómo se gestiona la memoria de la pila, por lo que ahora agregué un poco sobre mi no ser 100% precisa a mi respuesta ahora de todos modos :)
Elias Van Ootegem

@jmoreno El punto que señaló sobre "¿Puede significar que la memoria en chips que aún no se han fabricado, para computadoras que aún no se han diseñado, de alguna manera se está reservando? No." Es exactamente el falso significado que implica la palabra "asignación" lo que me confundió desde el principio. Me gusta esta respuesta porque se refiere exactamente al problema que estaba tratando de señalar. Ninguna de las respuestas aquí realmente tocó ese punto en particular. Gracias.
Talha Sayed

2

Si aprende la programación de ensamblaje, verá que tiene que tallar segmentos para los datos, la pila y el código, etc. El segmento de datos es donde viven sus cadenas y números. El segmento de código es donde vive su código. Estos segmentos están integrados en el programa ejecutable. Por supuesto, el tamaño de la pila también es importante ... ¡no querrás un desbordamiento de pila !

Entonces, si su segmento de datos es de 500 bytes, su programa tiene un área de 500 bytes. Si cambia el segmento de datos a 1500 bytes, el tamaño del programa será 1000 bytes más grande. Los datos se ensamblan en el programa real.

Esto es lo que sucede cuando compila lenguajes de nivel superior. El área de datos real se asigna cuando se compila en un programa ejecutable, lo que aumenta el tamaño del programa. El programa también puede solicitar memoria sobre la marcha, y esta es una memoria dinámica. Puede solicitar memoria de la RAM y la CPU se la dará para que la use, puede soltarla y su recolector de basura la devolverá a la CPU. Incluso puede ser cambiado a un disco duro, si es necesario, por un buen administrador de memoria. Estas características son las que le proporcionan los lenguajes de alto nivel.


2

Me gustaría explicar estos conceptos con la ayuda de unos pocos diagramas.

Esto es cierto que la memoria no se puede asignar en tiempo de compilación, seguro. Pero, entonces, ¿qué sucede de hecho en tiempo de compilación?

Aquí viene la explicación. Digamos, por ejemplo, un programa tiene cuatro variables x, y, z y k. Ahora, en tiempo de compilación, simplemente hace un mapa de memoria, donde se determina la ubicación de estas variables entre sí. Este diagrama lo ilustrará mejor.

Ahora imagine, ningún programa se está ejecutando en la memoria. Esto lo muestro con un gran rectángulo vacío.

campo vacío

A continuación, se ejecuta la primera instancia de este programa. Puedes visualizarlo de la siguiente manera. Este es el momento en que realmente se asigna la memoria.

primera Instancia

Cuando se ejecuta la segunda instancia de este programa, la memoria se vería de la siguiente manera.

segunda instancia

Y el tercero ...

tercera instancia

Y así sucesivamente.

Espero que esta visualización explique bien este concepto.


2
Si esos diagramas mostraran la diferencia entre memoria estática y dinámica, serían más útiles en mi humilde opinión.
Bartek Banachewicz

Esto había sido deliberadamente evitado por mí para mantener las cosas simples. Mi objetivo es explicar esta funda con claridad sin mucho desorden técnico. En la medida en que esto está destinado a la variable estática ... Este punto se ha establecido bien en respuestas anteriores. Así que me salté esto.
user3258051

1
Eh, este concepto no es particularmente complicado, por lo que no veo por qué hacerlo más simple de lo que debe ser, pero dado que se entiende solo como una respuesta complementaria, está bien.
Bartek Banachewicz 01 de

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.