¿Por qué los libros dicen, "el compilador asigna espacio para variables en la memoria"?

18

¿Por qué los libros dicen: "el compilador asigna espacio para variables en la memoria". ¿No es el ejecutable el que hace eso? Quiero decir, por ejemplo, si escribo el siguiente programa,

#include <iostream>
using namespace std;

int main()
{
   int foo;
   return 0;
}

y compilarlo, y obtener un ejecutable (que sea program.exe), ahora, si ejecuto program.exe, este archivo ejecutable ordenará asignar un espacio para la variable foo. ¿No? Explique por qué los libros siguen diciendo: "el compilador hará esto ... hará eso".

El codificador pacífico
fuente
11
¿De qué libros estás hablando?
wirrbel
44
Su "pregunta relacionada" debería ser una pregunta separada.
SShaheen
El compilador genera código que hace esto o aquello, es lo que está diciendo. directa o indirectamente.
old_timer
FYI stackoverflow.com/questions/7372024/… y tenga en cuenta que un compilador puede decidir cambiar la ubicación de una variable percibida en la memoria en aras de la alineación, por ejemplo: stackoverflow.com/questions/17774276/…
NoChance

Respuestas:

20

Tienes razón en que el compilador como tal desaparece cuando tu programa realmente se ejecuta. Y si se ejecuta en una máquina diferente, el compilador ya no está disponible.

Supongo que esto es para hacer una distinción clara entre la memoria realmente asignada por su propio código. El compilador insertará algún código en su programa que haga la asignación de memoria (como usar comandos nuevos, malloc o similares).

Entonces, los libros usan "el compilador hace esto o aquello" a menudo para decir que el compilador agregó algún código que no se menciona explícitamente en sus archivos de código. Es cierto que esto no es exactamente lo que está sucediendo. Desde este punto de vista, muchas cosas mencionadas en los tutoriales estarían equivocadas, pero necesitarían explicaciones bastante elaboradas.

Thorsten Müller
fuente
Sí, eso es lo que yo creía. Gracias por una respuesta rápida!
The Peaceful Coder
12
el compilador asigna la variable foo en la pila sustituyéndola por un desplazamiento al puntero de la pila durante la compilación. No está en absoluto relacionado con la asignación del montón que se realiza por mallocet. Alabama.
wirrbel
@holger: Su objeción es técnicamente correcta, por supuesto. Pero el espacio de la pila como tal aún debe asignarse cuando el programa se inicia antes de que pueda usarse (lo que puede suceder de varias maneras, a veces dependiendo de la arquitectura de la CPU). Traté de encontrar algunos detalles de cómo sucede esto, pero con poco éxito.
thorsten müller
2
Creo que el tamaño de la pila para el hilo principal está reservado por el vinculador y luego manejado por el sistema operativo. Para subprocesos personalizados, es más similar a la asignación de almacenamiento dinámico, es decir, la persona que llama puede corregir el tamaño en tiempo de ejecución.
wirrbel
4

Depende de la variable. El sistema operativo asigna el montón, el programa asignará pila y el compilador asignará espacio para globales / estáticos, es decir, están integrados en el propio exe. Si asigna 1 MB de memoria global, el tamaño de su archivo exe aumentará en al menos 1 MB

James
fuente
1
De eso no se trata esta pregunta.
Philipp
2
En realidad, está más cerca de la pregunta que las otras respuestas enumeradas aquí.
wirrbel
@ James Ah, esta no es mi experiencia. Por ejemplo, int test[256][1024]; int main(){ test[0][0]=2; return 0; } este pequeño programa tiene 1 MB asignado pero solo me genera un archivo de objeto de 1.4 Kb y un ejecutable de 8.4 Kb. Sin embargo, debería usar la cantidad correcta de RAM.
Garet Claborn
1
¿No deberían ser solo los comandos de asignación almacenados para globales? Si ha codificado todos los valores utilizando las primitivas como int o char, el tamaño del ejecutable definitivamente aumentará en más de la cantidad de variables agregadas. Tales como int a1=1,a2=2,... todo el camino hasta ... , a1048576=1048576;Solo entonces definitivamente obtendrías algo más grande que 1mb, creo.
Garet Claborn
2
Es lo que sea que ponga datos en la sección BSS del exe
James
4

lo que hará el compilador es tomar su código y compilarlo en código máquina. Lo que mencionas es un buen ejemplo donde un compilador solo necesita traducir.

Por ejemplo, cuando escribes

int foo;

Puede ver eso como 'Le estoy diciendo al compilador que [ en la salida que genera ] solicite que la computadora reserve suficiente memoria RAM para un int al que pueda hacer referencia más tarde. El compilador probablemente usará una identificación de recursos o algún mecanismo para rastrear foo en el código de máquina, ¡puedes usar foo en un archivo de texto en lugar de escribir el ensamblaje! Hurra !

Por lo tanto, también puede ver esto ya que el compilador está escribiendo una carta ( o tal vez una novela / enciclopedia ) a todos los procesadores y dispositivos específicos. La carta está escrita en señales binarias que (generalmente) pueden traducirse a diferentes procesadores cambiando el objetivo. Cualquier 'carta' y / o combo puede enviar todo tipo de solicitudes y / o datos; por ejemplo, asigne espacio para esta variable que utilizó el programador.

Garet Claborn
fuente
3

Decir "el compilador asigna memoria" puede no ser fácticamente exacto en el sentido literal, pero es una metáfora que sugiere de la manera correcta.

Lo que realmente sucede es que el compilador crea un programa que asigna su propia memoria. Excepto que no es el programa el que asigna memoria, sino el sistema operativo.

Entonces, lo que realmente sucede es que el compilador crea un programa que describe sus requisitos de memoria y el sistema operativo toma esa descripción y la usa para asignar memoria. Excepto que el sistema operativo es un programa y los programas en realidad no hacen nada, describen un cálculo que realiza la CPU. Excepto que la CPU es realmente solo un circuito electrónico complicado, no un pequeño homónimo antropomorfizado.

Pero tiene sentido pensar en los programas, compiladores y CPU como personas pequeñas que viven dentro de una computadora, no porque realmente lo sean, sino porque es una metáfora que se adapta bien al cerebro humano.

Algunas metáforas funcionan bien para describir cosas en un nivel de abstracción, pero no funcionan tan bien en otro nivel. Si piensa en el nivel del compilador, tiene sentido describir el acto de generar código que dará como resultado que se asigne memoria cuando el programa que se está compilando realmente se ejecuta como "asignación de memoria". Es lo suficientemente cercano como para que cuando pensemos en cómo funciona un compilador, tengamos la idea correcta, y no sea tan largo que olvidemos lo que estábamos haciendo. Si tratamos de usar esa metáfora en el nivel del programa compilado en ejecución, es engañoso de una manera extraña, que es lo que notó.

Michael Shaw
fuente
0

Es el compilador quien decide dónde almacenar una variable: puede estar en la pila o en un registro gratuito. Cualquiera sea la decisión de almacenamiento realizada por el compilador, el código de máquina correspondiente para acceder a esa variable se generará y no se puede cambiar en tiempo de ejecución. En este sentido, el compilador se encarga de asignar espacio para las variables y el program.exe final solo actúa ciegamente como un zombie en tiempo de ejecución.

Ahora, no confunda esto con una administración de memoria dinámica diferente como malloc, nueva o puede ser su propia administración de memoria. Los compiladores se ocupan del almacenamiento y acceso variable, pero no le importa lo que significa un valor real en otro marco / biblioteca. Por ejemplo:

byte* pointer = (byte*)malloc(...);

En tiempo de ejecución, malloc puede devolver un número arbitrario, pero al compilador no le importa, lo único que le importa es dónde almacenar ese número.

Codismo
fuente
0

Una redacción más precisa sería: - "el compilador le dice al cargador que reserve espacio para las variables"

En un entorno C-ish habrá tres tipos de espacio para variables: -

  • un bloque fijo para variables estáticas
  • Un bloque grande para variables "automáticas", generalmente denominado "pila". Las funciones toman un trozo en la entrada y lo liberan al regresar.
  • Un bloque grande llamado "montón", que es desde donde se asigna la memoria administrada por el programa (usando malloc () o una API de administración de memoria similar.

En un sistema operativo moderno, la memoria de almacenamiento dinámico no se reservará sino que se asignará según sea necesario.

James Anderson
fuente
0

Sí, tiene razón, en este caso (declarando una variable en una función), la oración de su libro probablemente sea incorrecta: cuando declara una variable en una función, se asigna en la pila al ingresar a la función. De todos modos, un compilador debe optimizar la situación: si la función no es recursiva ( main()es un buen candidato para ello), está bien "asignarla" en tiempo de compilación (en el BSS).

(Si tiene curiosidad acerca de dónde residen sus variables, puede verificarlo de una manera sucia (si no desea examinar la estructura del archivo obj, de todos modos, ¿por qué no?), Para que pueda declarar algún tipo diferente de variables: constante, estática, dinámica, malloc()asignada , etc., y muestra sus direcciones (use el %Xformateador printf()para una mejor legibilidad). Las variables que residen en la pila tendrán direcciones de memoria muy diferentes).

ern0
fuente
0

Lo único que se hará en tiempo de ejecución será aumentar el puntero de la pila en una cierta cantidad. Entonces el compilador decide de antemano:

  • cuánto espacio de pila se necesitará para la función.
  • A qué desplazamiento del puntero de la pila se ubicará cada variable individual.

Este puede llamarse "asignación", pero, por supuesto, durante el tiempo de compilación solo se coloca en el modelo que tiene el compilador del programa en ejecución.

Ingo
fuente