Wie funktioniert const storage? (Punkt 2, Scott Myers Effective C ++)

8

In Punkt 2 auf Seite 16 (Bevorzugen Sie Konstanten, Aufzählungen und Inlines gegenüber #defines) sagt Scott:

Auch wenn gute Compiler keinen Speicher für const-Objekte vom Typ Integer reservieren ...

Ich verstehe das nicht Wenn ich ein const-Objekt definiere, z

const int myval = 5;

dann muss der Compiler sicher etwas Speicher (von int-Größe) beiseite legen, um den Wert 5 zu speichern?

Oder werden const-Daten auf besondere Weise gespeichert?

Dies ist wohl eher eine Frage des Computerspeichers. Wie speichert der Computer const-Objekte, damit kein Speicher reserviert wird?

user619818
quelle
3
Sie sollten einen eindeutigen Titel angeben. Zum Beispiel hat storage of const objectdie Quelle Ihrer Frage wenig Wert.
Simon Bergot

Antworten:

7

Auch wenn gute Compiler keinen Speicher für const-Objekte vom Typ Integer reservieren ...

Eine etwas korrektere Aussage wäre, dass Compiler den Datenspeicher nicht für const-Objekte vom Integer-Typ reservieren würden: Sie würden ihn gegen den Programmspeicher eintauschen . Es gibt keinen Unterschied zwischen den beiden unter Von Neumann-Architektur, aber in anderen Architekturen wie Harvard ist die Unterscheidung ziemlich wichtig.

Um vollständig zu verstehen, was vor sich geht, müssen Sie sich daran erinnern, wie die Assemblersprache Daten zur Verarbeitung lädt. Es gibt zwei grundsätzliche Möglichkeiten , um die Daten zu laden , - einen Speicher von einem bestimmten Ort lesen (sogenannte direkter Adressierungsmodus) oder eine Konstante als Teil des Befehls spezifizierten Satz selbst (so genannten unmittelbarer Adressierungsmodus). Wenn der Compiler eine const int x = 5Deklaration gefolgt von sieht int a = x+x, hat er zwei Möglichkeiten:

  • Legen Sie 5 in einen Datenspeicher, als wäre es eine Variable, und generieren Sie Anweisungen zum direkten Laden. Behandle Schreibvorgänge in x als Fehler
  • xGenerieren Sie bei jeder Referenzierung eine sofortige Ladeanweisung mit dem Wert 5

Im ersten Fall sehen Sie ein Einlesen xin das Akkumulatorregister , eine Addition des Werts am Speicherort des xAkkumulators und einen Speicher am Speicherort des Akkumulators a. Im zweiten Fall sehen Sie eine sofortige Ladung von fünf, eine sofortige von fünf, gefolgt von einem Geschäft an der Position von a. Einige Compiler können herausfinden, dass Sie hinzufügen , eine Konstante zu sich, optimize a = x+xin a = 10und erzeugen eine einzige Anweisung , dass speichern zehn an der Stelle a.

dasblinkenlight
quelle
1
+1 für die Erwähnung der konstanten Faltung
jk.
10

Nicht unbedingt. Es kann auch entschieden werden, nur den Rohwert 5 anstelle des myvalkompilierten Codes zu verwenden.

Der Unterschied zwischen #define MYVAL 5und const int myval = 5besteht darin, dass der Compiler im ersteren Fall überhaupt keine Wahl hat, da der Präprozessor bereits alle Erwähnungen MYVALim Quellcode 5durch ersetzt hat, bis der Compiler den Quellcode sehen kann. Im letzteren Fall gibt es jedoch eine Wahl. Bei einem nicht optimierten Debug-Build kann der Compiler explizit a zuweisen const int, sodass Sie im Debugger die Konstante myvalanstelle nur des Rohwerts 5 sehen können.

Péter Török
quelle
Ich verstehe den Nutzen der Konstante, anstatt sie zu definieren. Aber auch mit dem const-Beispiel muss der Wert 5 irgendwo in der ausführbaren Datei gespeichert werden?
user619818
3
@ user619818 wird im Code als Anweisungsparameter gespeichert, nicht im Datensegment als reguläre Variable / Konstante.
Péter Török
@ user619818: Die Anweisung hat immer einen Parameter. Wenn also ein separater Speicher zugewiesen wird, gibt es den Wert und die Adresse davon im unmittelbaren Argument der Anweisung. Auf CPUs mit fester Befehlsgröße (wie ARM) wird es noch schlimmer, da kleine Konstanten wie 5 in den 32-Bit-Befehl selbst passen, die Adresse jedoch häufig nicht und die Ausgabe eines zusätzlichen Ladeadressenbefehls bewirkt.
Jan Hudec
3

Ich werde den ersten Satz aus Péter Töröks Antwort stehlen, aber anders ausarbeiten: Nicht unbedingt. Es kann auch entschieden werden, nur den Rohwert 5 anstelle des myvalkompilierten Codes zu verwenden.

Die Behandlung myvalwie eine reguläre Variable durch Zuweisen von Speicherplatz im Speicher kann Auswirkungen auf die Leistung haben, die je nach Architektur und Umgang mit dem Speicher von winzig bis schwerwiegend reichen.

Auf diese Weise würde ein Compiler eine Anweisung ausgeben, die etwas in der Art von "Laderegister R mit dem, was sich an der Speicherstelle für befindet myval" sagt . Die Lage dermyvalals Operand des Befehls kommt er also direkt aus demselben Datenblock wie der Befehl selbst. Auf modernen CPUs ist dieser Wert aufgrund des Befehlsvorabrufs auf dem Chip leicht verfügbar. Mit der vorliegenden Adresse muss die CPU den Wert immer noch aus dem Speicher holen. Das kann schnell gehen, wenn sich der Speicherort in der Nähe im Cache befindet, oder nicht so schnell, wenn dies nicht der Fall ist. Die CPU muss nicht nur vom Chip abweichen, um den Wert zu erhalten, sondern kann auch dazu führen, dass andere, nützlichere Daten aus dem Cache entfernt werden, die später wieder eingefügt werden müssen. Wenn das Programm unter einem Betriebssystem ausgeführt wird, das den Speicher virtualisiert, kann der Zugriff auf diesen Speicherort einen Seitenfehler verursachen, der dazu führt, dass das Programm in den Ruhezustand versetzt wird, bis die erforderliche Seite über periphere (z. B. Festplatten-) E / A in den RAM gebracht wird.

Durch Festverdrahtung des konstanten Werts mit dem Objektcode würde der Compiler eine Anweisung wie "Laderegister R mit dem Wert 5" ausgeben . Wie die oben beschriebene Speicheradresse 5wäre das ein Operand für den Befehl und auf die gleiche Weise verfügbar (dh vorabgerufen). Hier endet die Ähnlichkeit, denn die CPU verfügt nun über alles, was sie benötigt, um das 5Register R zu erstellen und mit ihrem Geschäft fortzufahren. Da Adressen und Register normalerweise die gleiche Größe haben, gibt es keinen Unterschied in der Anzahl der Bytes, die der Befehl belegt, und die tatsächliche Ausführung erfolgt ohne Wahrscheinlichkeit von Cache-Fehlern und Seitenfehlern, die auftreten können, wenn Sie etwas aus dem Speicher fischen.

Der Compiler könnte, wie Péter betonte, Speicherplatz und ein Symbol für myvalDebug-Builds zuweisen . Es würde nicht schaden, dies zu tun und seinen Wert immer noch fest zu verdrahten, da der Wert auf jeden Fall derselbe bleibt und das Symbol wirklich nur für uns Menschen zum Debuggen da ist.

Beachten Sie, dass dies nur für Werte gilt, die in Registern gespeichert werden können, da Register von Natur aus Ganzzahlen sind. Andere Konstanten werden im Speicher gespeichert.

Blrfl
quelle
2

Was das Zitat sagt, ist nicht ganz richtig.

Ein guter Compiler wird keinen Speicher für statische const-Variablen reservieren. Wenn die Variable const nicht statisch ist und sich im Dateibereich befindet, muss der Speicher reserviert werden, da auf die Variable von einer anderen Kompilierungseinheit verwiesen werden kann. Mit Optimierungen der Verbindungszeit kann der Linker möglicherweise die Speicher- und Umschreibanweisungen, die auf die Variable verweisen, entfernen, wenn er nachweisen kann, dass das Programm keinen Zeiger auf diese Variable generiert.

Ein viel besserer Grund für die Verwendung const intanstelle von #defineist, dass Debugger keine Makros "sehen", sodass Sie einen #definedd-Wert im Debugger nicht überprüfen können .

zvrba
quelle
1

Der Compiler ersetzt die Nummer fünf überall dort, wo die Variable 'myval' verwendet wird.

Dibbeke
quelle
0

Der Compiler kann konstante Werte als unmittelbare Operanden betrachten. Sofortige Operanden erfordern keine Datenspeicherung. Der Compiler kann Folgendes behandeln:

int foo = myVal;

das Gleiche wie

int foo = 5;

Der Wert 5 wird nicht im Datenspeicher gespeichert, sondern als Teil der Befehlssequenz.

Der Compiler muss die Datenspeicherung in Fällen reservieren, in denen die Adresse eines Wertes verwendet werden kann. Selbst in diesem Fall verwendet der Compiler weiterhin Sofortoperationen, wenn der Wert von myVal verwendet wird.

Bill Door
quelle