Warum sagen Bücher, "der Compiler reserviert Speicherplatz für Variablen im Speicher"?

18

Warum sagen Bücher "der Compiler reserviert Speicherplatz für Variablen im Speicher"? Ist es nicht die ausführbare Datei, die das macht? Ich meine zum Beispiel, wenn ich das folgende Programm schreibe,

#include <iostream>
using namespace std;

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

und kompiliere es und erhalte eine ausführbare Datei (lass es program.exe sein). Wenn ich jetzt program.exe starte, wird diese ausführbare Datei selbst befehlen, etwas Platz für die Variable foo zu reservieren. Wird es nicht Bitte erklären Sie, warum Bücher immer wieder sagen: "Der Compiler wird dies tun ... das tun".

Der friedliche Kodierer
quelle
11
Von welchen Büchern redest du?
Wirrbel
4
Ihre "verwandte Frage" sollte eine separate Frage sein.
SShaheen
Der Compiler generiert Code, der dies oder jenes tut, was er sagt. direkt oder indirekt.
old_timer
FYI stackoverflow.com/questions/7372024/… und beachten Sie, dass ein Compiler möglicherweise beschließt, die Position einer wahrgenommenen Variablen im Speicher zu ändern, um sie auszurichten.
NoChance

Antworten:

20

Sie haben Recht, dass der Compiler als solcher nicht mehr vorhanden ist, wenn Ihr Programm tatsächlich ausgeführt wird. Und wenn es auf einer anderen Maschine läuft, ist der Compiler nicht mehr verfügbar.

Ich vermute, dies ist eine klare Unterscheidung zwischen dem tatsächlich von Ihrem eigenen Code zugewiesenen Speicher. Der Compiler fügt Code in Ihr Programm ein, der die Speicherzuweisung vornimmt (wie bei der Verwendung von new-, malloc- oder ähnlichen Befehlen).

Bücher verwenden daher "der Compiler tut dies oder das" oft, um zu sagen, dass der Compiler Code hinzugefügt hat, der in Ihren Codedateien nicht explizit erwähnt wird. Es stimmt, dass dies nicht genau das ist, was los ist. Unter diesem Gesichtspunkt wären viele Dinge, die in Tutorials erwähnt werden, falsch, müssten aber ausführlich erklärt werden.

thorsten müller
quelle
Ja, das habe ich geglaubt. Danke für eine schnelle Antwort!
Der friedliche Kodierer
12
Der Compiler ordnet die Variable foo im Stapel zu, indem er sie beim Kompilieren durch einen Offset zum Stapelzeiger ersetzt. Es hat überhaupt nichts mit der Heap-Allokation zu tun, die von mallocet. al.
Wirrbel
@holger: Dein Einwand ist natürlich technisch korrekt. Der Stapelspeicher als solcher muss jedoch noch zugewiesen werden, wenn das Programm gestartet wird, bevor es verwendet werden kann (dies kann auf verschiedene Arten geschehen, manchmal abhängig von der CPU-Architektur). Ich habe versucht, einige Details zu finden, wie dies geschieht, aber mit wenig Erfolg.
thorsten müller 06.04.13
2
Ich denke, die Stapelgröße für den Haupt-Thread wird vom Linker reserviert und dann vom Betriebssystem verwaltet. Bei benutzerdefinierten Threads ähnelt dies eher der Heap-Zuweisung, dh der Aufrufer kann die Größe zur Laufzeit festlegen.
Wirrbel
4

Das hängt von der Variablen ab. Das Betriebssystem ordnet Heap zu, das Programm ordnet Stack zu und der Compiler ordnet Speicherplatz für Globals / Statics zu, dh sie sind in die Exe selbst integriert. Wenn Sie 1 MB globalen Speicher zuweisen, erhöht sich Ihre Exe-Größe um mindestens 1 MB

James
quelle
1
Darum geht es in dieser Frage nicht.
Philipp
2
Tatsächlich ist es der Frage näher als den anderen hier aufgeführten Antworten.
Wirrbel
@ James Ah, das ist nicht meine Erfahrung. Zum Beispiel hat int test[256][1024]; int main(){ test[0][0]=2; return 0; } dieses kleine Programm 1 MB zugewiesen, generiert aber nur eine 1,4-KB-Objektdatei und eine 8,4-KB-ausführbare Datei. Es sollte jedoch die richtige Größe des Arbeitsspeichers verwenden.
Garet Claborn
1
Sollten es nicht nur die Zuweisungsbefehle sein, die für Globals gespeichert werden? Wenn Sie alle Werte mit den Primitiven int oder char fest codiert haben, würde sich die Größe der ausführbaren Datei definitiv um mehr als die Anzahl der hinzugefügten Variablen erhöhen. So wie int a1=1,a2=2,... bis ... , a1048576=1048576;Nur dann würdest du definitiv etwas größer als 1 MB bekommen, denke ich.
Garet Claborn
2
Es ist das, was Daten in den BSS-Bereich der Exe stellt
James,
4

was der Compiler wird tun , ist Ihren Code nehmen und es in Maschinencode kompiliert. Was Sie erwähnen, ist ein gutes Beispiel, bei dem ein Compiler nur übersetzen muss.

Zum Beispiel, wenn Sie schreiben

int foo;

Sie können dies als "Ich sage dem Compiler zu" in der Ausgabe, die er generiert ] genügend RAM für ein int zu reservieren, auf das ich später verweisen kann. Der Compiler wird wahrscheinlich eine Ressourcen-ID oder einen Mechanismus verwenden, um foo in der zu verfolgen." Maschinencode, Sie können foo in einer Textdatei verwenden, anstatt Assembly zu schreiben! Hurra !

Sie können dies auch betrachten, wenn der Compiler einen Brief ( oder vielleicht einen Roman / eine Enzyklopädie ) an alle Zielprozessoren und -geräte schreibt . Der Buchstabe ist in Binärsignalen geschrieben, die (im Allgemeinen) durch Ändern des Ziels in verschiedene Prozessoren übersetzt werden können. Jeder "Brief" und / oder jede Kombination kann alle Arten von Anfragen und / oder Daten senden - wie zum Beispiel, weisen Sie dieser Variablen, die der Programmierer verwendet hat, Platz zu.

Garet Claborn
quelle
3

Das Sprichwort "Der Compiler weist Speicher zu" mag im wörtlichen Sinne nicht sachlich korrekt sein, aber es ist eine Metapher, die auf die richtige Weise andeutet.

Was wirklich passiert, ist, dass der Compiler ein Programm erstellt, das seinen eigenen Speicher zuweist. Es ist jedoch nicht das Programm, das den Speicher zuweist, sondern das Betriebssystem.

Was wirklich passiert, ist, dass der Compiler ein Programm erstellt, das seinen Speicherbedarf beschreibt. Das Betriebssystem verwendet diese Beschreibung, um Speicher zuzuweisen. Abgesehen davon, dass das Betriebssystem ein Programm ist und Programme eigentlich nichts tun, beschreiben sie eine Berechnung, die von der CPU durchgeführt wird. Abgesehen davon, dass die CPU nur eine komplizierte elektronische Schaltung ist, kein anthropomorphisierter kleiner Homonculus.

Aber es ist sinnvoll, sich Programme, Compiler und CPUs als kleine Menschen vorzustellen, die in einem Computer leben, nicht weil sie es tatsächlich sind, sondern weil dies eine Metapher ist, die gut zum menschlichen Gehirn passt.

Einige Metaphern eignen sich gut für die Beschreibung von Dingen auf einer Abstraktionsebene, funktionieren auf einer anderen Ebene jedoch nicht so gut. Wenn Sie auf der Ebene des Compilers denken, ist es sinnvoll, den Vorgang des Generierens von Code zu beschreiben, der zur Zuweisung von Speicher führt, wenn das zu kompilierende Programm tatsächlich als "Speicherzuweisung" ausgeführt wird. Es ist nah genug, dass wir, wenn wir darüber nachdenken, wie ein Compiler funktioniert, die richtige Idee haben und es nicht so langwierig ist, dass wir vergessen, was wir getan haben. Wenn wir versuchen, diese Metapher auf der Ebene des kompilierten Programms zu verwenden, das ausgeführt wird, ist dies auf seltsame Weise irreführend, was Ihnen aufgefallen ist.

Michael Shaw
quelle
0

Es ist der Compiler, der entscheidet, wo eine Variable gespeichert werden soll - entweder im Stack oder in einem freien Register. Unabhängig von der vom Compiler getroffenen Speicherentscheidung wird der entsprechende Maschinencode für den Zugriff auf diese Variable generiert und kann zur Laufzeit nicht geändert werden. In diesem Sinne ist der Compiler für die Zuweisung von Speicherplatz für Variablen verantwortlich, und die endgültige program.exe verhält sich zur Laufzeit nur blind wie ein Zombie.

Verwechseln Sie dies nicht mit einer anderen dynamischen Speicherverwaltung wie malloc, new oder möglicherweise Ihrer eigenen Speicherverwaltung. Compiler beschäftigen sich mit variablem Speicher und Zugriff, aber es ist egal, was ein tatsächlicher Wert in einem anderen Framework / einer anderen Bibliothek bedeutet. Beispielsweise:

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

Während der Laufzeit kann malloc eine beliebige Zahl zurückgeben, dem Compiler ist es jedoch egal, wo diese Zahl gespeichert werden soll.

Kodismus
quelle
0

Eine genauere Formulierung wäre: - "Der Compiler weist den Loader an, Platz für die Variablen zu reservieren".

In einer C-ish-Umgebung gibt es drei Arten von Platz für Variablen:

  • ein fester Block für statische Variablen
  • Ein großer Block für "automatische" Variablen, die normalerweise als "Stapel" bezeichnet werden. Funktionen greifen bei der Eingabe nach einem Teil und geben ihn bei der Rückkehr frei.
  • Ein großer Block namens "Heap", in dem programmgesteuerter Speicher zugewiesen wird (mithilfe von malloc () oder einer ähnlichen Speicherverwaltungs-API).

Auf einem modernen Betriebssystem wird der Heap-Speicher nicht reserviert, sondern nach Bedarf zugewiesen.

James Anderson
quelle
0

Ja, Sie haben Recht, in diesem Fall (Deklaration einer Variablen in einer Funktion) ist der Satz Ihres Buches wahrscheinlich falsch: Wenn Sie eine Variable in einer Funktion deklarieren, wird sie beim Aufrufen der Funktion auf dem Stapel zugeordnet. Auf jeden Fall sollte ein Compiler die Situation optimieren: Wenn die Funktion nicht rekursiv ist ( main()ein guter Kandidat dafür ist), ist es in Ordnung, sie zur Kompilierungszeit zuzuweisen (auf dem BSS).

(Wenn Sie neugierig sind, wo sich Ihre Variablen befinden, können Sie dies auf schmutzige Weise überprüfen (wenn Sie die Struktur der obj-Datei sowieso nicht untersuchen möchten, warum nicht?), Damit Sie eine andere Art von Variablen deklarieren können: Konstante, statisch, dynamisch, malloc()-zugeordnet usw. und zeigen ihre Adressen an (verwenden Sie zur besseren Lesbarkeit den %XFormatierer in printf(). Die auf dem Stapel befindlichen Variablen haben sehr unterschiedliche Speicheradressen.)

ern0
quelle
0

Das Einzige, was zur Laufzeit gemacht wird, ist, den Stack-Poinbter um einen bestimmten Betrag anzustoßen. Der Compiler entscheidet also vorher:

  • Wie viel Stapelspeicher wird für die Funktion benötigt.
  • In welchem ​​Abstand vom Stapelzeiger befindet sich jede einzelne Variable.

Diese kann als "Zuordnung" bezeichnet werden, nimmt aber während der Kompilierungszeit natürlich nur in dem Modell Platz, das der Compiler des laufenden Programms hat.

Ingo
quelle