Perché i libri dicono: "il compilatore alloca lo spazio per le variabili in memoria"?


18

Perché i libri dicono "il compilatore alloca lo spazio per le variabili in memoria". Non è l'eseguibile che lo fa? Voglio dire, ad esempio, se scrivo il seguente programma,

#include <iostream>
using namespace std;

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

e compilarlo e ottenere un eseguibile (lascia che sia program.exe), ora, se eseguo program.exe, questo file eseguibile comanderà esso stesso di allocare un po 'di spazio per la variabile pippo. Non è vero? Per favore, spiega perché i libri continuano a dire "il compilatore farà questo ... lo farà".


11
di quali libri stai parlando?
Wirrbel,

4
La tua "domanda correlata" dovrebbe essere una domanda separata.
SShaheen,


Il compilatore genera codice che fa questo o quello che stanno dicendo. direttamente o indirettamente.
old_timer

FYI stackoverflow.com/questions/7372024/...~~V~~singular~~3rd e nota che un compilatore può decidere di modificare la posizione di una variabile percepito nella memoria per il bene di allineamento ad esempio: stackoverflow.com/questions/17774276/...
NoChance

Risposte:


20

Hai ragione a dire che il compilatore in quanto tale è sparito quando il tuo programma viene effettivamente eseguito. E se funziona su una macchina diversa, il compilatore non è più disponibile.

Immagino che questo faccia una chiara distinzione tra la memoria effettivamente allocata dal tuo codice. Il compilatore inserirà del codice nel programma che esegue l'allocazione della memoria (come l'utilizzo di comandi new, malloc o simili).

Quindi i libri usano "il compilatore fa questo o quello" spesso per dire che il compilatore ha aggiunto del codice che non è esplicitamente menzionato nei tuoi file di codice. Abbastanza vero che questo non è esattamente quello che sta succedendo. Da questo punto di vista molte cose menzionate nei tutorial sarebbero sbagliate ma avrebbero bisogno di spiegazioni piuttosto elaborate.


Sì, è quello che credevo. Grazie per una risposta veloce!
The Peaceful Coder

12
il compilatore alloca per la variabile foo sullo stack sostituendolo con un offset al puntatore dello stack durante la compilazione. Non è affatto correlato all'allocazione dell'heap che viene effettuata da mallocet. al.
Wirrbel,

@holger: la tua obiezione è tecnicamente corretta, ovviamente. Ma lo spazio dello stack in quanto tale deve essere ancora allocato all'avvio del programma prima che possa essere utilizzato (che può verificarsi in vari modi, a volte a seconda dell'architettura della CPU). Ho provato a trovare alcuni dettagli su come ciò avvenga, ma con scarso successo.
Thorsten Müller,

2
Penso che la dimensione dello stack per il thread principale sia riservata dal linker e quindi gestita dal sistema operativo. Per i thread personalizzati è più simile all'allocazione dell'heap, ovvero il chiamante può correggere la dimensione in fase di esecuzione.
Wirrbel,

4

Dipende dalla variabile. Il sistema operativo alloca l'heap, il programma alloca lo stack e il compilatore alloca lo spazio per i globals / statica, cioè sono integrati nell'esempio stesso. Se si alloca 1 MB di memoria globale, le dimensioni di exe aumenteranno di almeno 1 MB


1
Non è questa la domanda.
Philipp

2
in realtà è più vicino alla domanda rispetto alle altre risposte elencate qui.
Wirrbel,

@James Ah, questa non è la mia esperienza. Ad esempio, int test[256][1024]; int main(){ test[0][0]=2; return 0; } questo piccolo programma ha allocato 1 MB ma mi genera solo un file oggetto 1,4 Kb e un eseguibile 8,4 Kb. Dovrebbe tuttavia utilizzare la corretta quantità di RAM.
Garet Claborn,

1
Non dovrebbero essere solo i comandi di allocazione memorizzati per i globali? Se hai codificato tutti i valori usando le primitive come int o char, la dimensione dell'eseguibile aumenterebbe sicuramente di più della quantità di variabili aggiunte. Come int a1=1,a2=2,... fino a ... , a1048576=1048576;Solo allora avrai sicuramente qualcosa di più grande di 1 MB, penso.
Garet Claborn,

2
È qualunque cosa metta i dati nella sezione BSS dell'exe
James

4

quello che farà il compilatore è prendere il tuo codice e compilarlo nel codice macchina. Quello che dici è un buon esempio in cui un compilatore deve solo tradurre.

Ad esempio, quando scrivi

int foo;

Puoi vedere che come 'Sto dicendo al compilatore di [ nell'output che genera ] richiedere che il computer riservi abbastanza RAM per un int che posso fare riferimento in seguito Il compilatore probabilmente utilizzerà un ID risorsa o qualche meccanismo per tracciare foo nel codice macchina, puoi usare foo in un file di testo invece di scrivere assembly! Evviva !

Quindi potresti anche guardare questo mentre il compilatore sta scrivendo una lettera ( o forse un romanzo / un'enciclopedia ) a tutti i processori e dispositivi target. La lettera è scritta in segnali binari che (generalmente) possono essere tradotti in processori diversi modificando il target. Qualsiasi "lettera" e / o combo può inviare tutti i tipi di richieste e / o dati, ad esempio allocare spazio per questa variabile utilizzata dal programmatore.


3

Dire "il compilatore alloca la memoria" potrebbe non essere effettivamente accurato in senso letterale, ma è una metafora che suggerisce nel modo giusto.

Quello che succede realmente è che il compilatore crea un programma che alloca la propria memoria. Solo che non è il programma che alloca memoria, ma il sistema operativo.

Quindi quello che succede davvero è che il compilatore crea un programma che descrive i suoi requisiti di memoria e il sistema operativo prende quella descrizione e lo usa per allocare memoria. Tranne che il sistema operativo è un programma e che i programmi in realtà non fanno nulla, descrivono un calcolo che viene eseguito dalla CPU. Solo che la CPU è in realtà solo un circuito elettronico complicato, non un piccolo omoncolo antropomorfizzato.

Ma ha senso pensare a programmi, compilatori e CPU come piccole persone che vivono all'interno di un computer, non perché lo siano realmente, ma perché è una metafora che si adatta bene al cervello umano.

Alcune metafore funzionano bene per descrivere le cose su un livello di astrazione, ma non funzionano altrettanto su un altro livello. Se si pensa a livello del compilatore, ha senso descrivere l'atto di generare codice che comporterà l'allocazione della memoria quando il programma che viene compilato viene effettivamente eseguito come "allocazione della memoria". È abbastanza vicino che quando stiamo pensando a come funziona un compilatore, abbiamo l'idea giusta, e non è così prolisso che dimentichiamo cosa stavamo facendo. Se proviamo a usare quella metafora a livello del programma compilato in esecuzione, è fuorviante in un modo strano, che è quello che hai notato.


0

È il compilatore che decide dove archiviare una variabile - può essere nello stack o in un registro libero. Qualunque sia la decisione di archiviazione presa dal compilatore, il codice macchina corrispondente per accedere a quella variabile verrà generato e non potrà essere modificato in fase di esecuzione. In questo senso, il compilatore si occupa di allocare spazio per le variabili e il finale program.exe si comporta alla cieca come uno zombi in fase di esecuzione.

Ora, non confonderlo con una diversa gestione dinamica della memoria come malloc, new o potrebbe essere la tua gestione della memoria. I compilatori hanno a che fare con l'archiviazione e l'accesso variabili, ma non importa cosa significhi un valore reale in un altro framework / libreria. Per esempio:

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

In fase di esecuzione, malloc può restituire un numero arbitrario ma al compilatore non importa, tutto ciò che importa è dove memorizzare quel numero.


0

Un fraseggio più accurato sarebbe: - "il compilatore dice al caricatore di riservare spazio per le variabili"

In un ambiente C-ish ci saranno tre tipi di spazio per le variabili: -

  • un blocco fisso per variabili statiche
  • Un grande blocco per le variabili "automatiche" di solito indicato come "stack". Le funzioni afferrano un pezzo all'ingresso e lo rilasciano al ritorno.
  • Un blocco di grandi dimensioni chiamato "heap", da cui viene allocata la memoria gestita dal programma (usando malloc () o un'API di gestione della memoria simile.

Su un moderno sistema operativo, la memoria heap non sarà effettivamente riservata ma allocata come richiesto.


0

Sì, hai ragione, in questo caso (dichiarando una variabile in una funzione), la frase del tuo libro è probabilmente errata: quando dichiari una variabile in una funzione, questa viene allocata in pila quando inserisci la funzione. Ad ogni modo, un compilatore dovrebbe ottimizzare la situazione: se la funzione non è ricorsiva ( main()è un buon candidato per essa), è OK "allocarla" in fase di compilazione (sul BSS).

(Se sei curioso di sapere dove risiedono le tue variabili, puoi controllarlo in modo sporco (se non vuoi esaminare la struttura del file obj, comunque, perché no?), Quindi puoi dichiarare alcuni diversi tipi di variabili: costante, statico, dinamico, malloc()allocato , ecc. e visualizza i loro indirizzi (usa il %Xformattatore printf()per una migliore leggibilità). Le variabili che risiedono nello stack avranno indirizzi di memoria molto diversi.)


0

L'unica cosa fatta in fase di esecuzione sarà di aumentare di un certo ammontare lo stack poinbter. Quindi il compilatore decide in anticipo:

  • quanto spazio dello stack sarà necessario per la funzione.
  • A quale offset dal puntatore dello stack verrà individuata ogni singola variabile.

Questo può essere chiamato "allocazione", ma ovviamente, durante il tempo di compilazione, ha luogo solo nel modello che il compilatore ha del programma in esecuzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.