2009-06-02 20 views
4

Deklariert/Zuweisen einer Variablen in einer höheren Sprache wie C++, eine explizite Anweisung?deklariert eine Variable eine Anweisung

z.B. x = 5;

Es würde vom Loader behandelt werden und als Statusinformationen behandelt werden, richtig?

Es ist keine Anweisung, sondern ein Zustandsobjekt, im Gegensatz zu etwas wie eine for-Schleife, die eine Anweisung ist, die es zur CPU macht?

edit: OK, um ein bisschen mehr zu klären. Ich spreche nicht von Optimierungen. Nehme keine an. Ich spreche über das Endergebnis eines kompilierten Programms in einem ausführbaren Dateiformat. Unter den Umständen, wo der Compiler beschließt, die MOV-Anweisung nicht zu verwenden, werden die Daten 5 innerhalb des Dateisegments der ausführbaren Dateien oder anderswo existieren?

Ist es möglich, dass die 5 als Daten existieren, ohne eine Anweisung zu sein, wobei diese Daten später in den Speicher geladen werden? Oder, im allgemeinen wird x = 5 eine mov-Anweisung ergeben, wenn das Programm ausgeführt wird.

+1

Josh, Sie könnten vielleicht experimentieren, indem Sie diese Anweisung in eine Liste schreiben, die Liste kompilieren, einen Disassembler für die resultierende ausführbare Datei ausführen und die resultierenden Daten untersuchen. –

Antwort

0

Wenn Ihre Variable ist ein primitiver Typ (int, char, etc.):

Für eine globale oder statische Variable, nein. Dies ist nur ein Eintrag im BSS- oder DATA-Segment (abhängig davon, ob es initialisiert ist oder nicht), kein ausführbarer Code ist erforderlich. Außer natürlich, wenn der Initialisierer zur Laufzeit ausgewertet werden muss.

Für eine lokale Variable, wenn sie nicht initialisiert ist, impliziert die erste eine Assembleranweisung, die anderen nicht. Das liegt daran, dass die Speicherplatzzuordnung für sie normalerweise dazu dient, dem Stapelzeiger einen Offset hinzuzufügen (tatsächlich wird subtrahiert - der Stapel wächst rückwärts). Wenn Sie Ihre erste int-Variable deklarieren, wird ein "ADD SP, 4" generiert; Für die Sekunde wurde nur "ADD SP, 8" geändert. Diese Anweisung befindet sich nicht an der Stelle, an der Sie Ihre Variable deklarieren, sondern an der Funktion begin, da dort der gesamte Stack-Platz für lokale Variablen vergeben werden muss.

Wenn Sie eine lokale Variable bei der Erstellung initialisieren, haben Sie eine MOV-Anweisung, um den Wert auf seine Position im Stapel zu laden. Diese Anweisung befindet sich in Bezug auf den Rest des Codes an derselben Stelle wie die Deklaration.

Diese Regeln für lokale Variablen setzen keine Optimierung voraus. Eine übliche Form der Optimierung besteht darin, CPU-Register als Variablen zu verwenden, in diesem Fall ist keine Zuweisung erforderlich, aber die Initialisierung erzeugt einen Befehl. Manchmal müssen diese Register auch ihre Werte beibehalten, so dass Sie am Anfang eine PUSH-Anweisung und am Ende der Funktion eine POP-Anweisung sehen.

Die Regeln für Objekte, wenn kein Konstruktor beteiligt ist (oder der Konstruktor ist inline) sind viel komplizierter, aber eine ähnliche Logik gilt. Wenn Sie einen nicht-inlined Konstruktor haben, benötigen Sie natürlich mindestens eine Anweisung für seinen Aufruf.

3

Es hängt davon ab, wie es verwendet wird.

Im Allgemeinen wird der Compiler versuchen, jede Zeile so wenig störend wie möglich zu machen.

Wenn dies nur an einer Stelle verwendet wird, können Sie darauf wetten, dass es im Maschinencode fest codiert ist, anstatt Platz auf dem Stapel zu verschwenden.

Wenn es für mathematische oder algorithmische Operationen verwendet wird und sich sein Wert ändern kann, kann Platz für die Variable auf dem Stapel zugewiesen werden. Andererseits, wenn es häufig genug verwendet wird, kann der Compiler es einfach in einem Register belassen.

Die Antwort ist: es hängt ab. Kompilieren Sie es und zeigen Sie das Ergebnis mit dem Codefenster eines Debuggers an.

Eine mögliche tatsächliche Übersetzung:

MOV AX, 5 
+0

aber ist die variable Zuweisung selbst eine Anweisung? –

+0

Kommt drauf an. Aber, ja, es könnte sich in eine MOV-Anweisung verwandeln. –

+0

Josh, Sie könnten vielleicht experimentieren, indem Sie diese Anweisung in eine Liste schreiben, die Liste kompilieren und dann einen Disassembler für die resultierende ausführbare Datei ausführen und die resultierenden Daten untersuchen. –

4

Sind Sie fragen, ob eine Variable Deklaration in eine Montageanleitung in gleicher Weise würde ein Add übersetzen wird oder löschen? Wenn ja, dann ist die allgemeine Antwort, dass es keine direkte Übersetzung in Assembly gibt.

Es kann Anweisungen geben, die die Deklaration einer Variablen erleichtern, z. B. die Aktualisierung des Stapelzeigers, um Platz für eine Variable zu schaffen. Aber es gibt keine x86-Assembly-Anweisung, die besagt, dass eine Variable dieses Typs auf dem Stack deklariert werden soll.

Die meisten meiner Erfahrung ist mit x86 und AMD64-Chips, so könnte es Anweisungen auf anderen Prozessoren sein, aber ich bin mir ihrer nicht bewusst (aber wäre neugierig, darüber zu lesen, wenn sie existierten).

Variable Zuweisung ist ein bisschen anders. Der allgemeine Fall von x = 5 wird in eine Art von Assembleranweisung übersetzt (schreibt den Wert zum Beispiel in ein Register). Bei C++ ist es jedoch schwierig, spezifisch zu sein, da es viele Optimierungen und chipspezifische Einstellungen gibt, die dazu führen können, dass eine bestimmte Zeile keine Übersetzung in den Maschinencode hat.

+0

Ich meine nicht, es ist eine bestimmte CPU-Anweisung, ich meine nur, nachdem es kompiliert wurde, ist es ein Zustandsobjekt oder eine Anweisung, die verarbeitet wird. –

1

Nun in Assemblersprache allgemein Die mov-Anweisung wird verwendet, um 5 in das Register zu laden, stack-Variable oder Heap-Variable, die x darstellt.

Aber der Optimierer könnte entscheiden, dies einfach als konstanten Wert zu verwenden (wie #define) oder es könnte entscheiden, es komplett zu entfernen, wenn es nicht verwendet wird und es entscheidet, wo es zu setzen ist (register, stack variable, heap)

Also, ich hoffe, es beantwortet Ihre Frage, aber wie Sie sehen können, eine Variable in C++ zuzuteilen abstrahiert eine Tonne von Dingen, und das ist sehr gut!

FYI: Ich habe Tonnen von Compiler-Assembly-Ausgabe studiert. In diesem Beitrag wird die Zuweisungsoperation in eine Baugruppe ODER-Verknüpfung optimierte, was sehr gut ist: Which is more readable (C++ =)

+0

vorausgesetzt, kein Optimierer ... –

+0

Sind Sie in Compiler schreiben? Ich glaube nicht, dass ich dir mit diesem Josh helfen kann. – toto

1

Es ist auf dem Compiler ausgeführt und auf seinen Optimierungen abhängt. Wenn es die Eliminierung von toten Speichern durchführt, kann es durchaus vorkommen, dass das Assembler-Code-Schreiben an eine Variable nicht ausgegeben wird. Betrachten

for(.....) i+= n; 
i = 1; 
return i; 

leicht auf diese eine optimierte konnte, da die Schriften i ohnehin

durch die spätere Zuordnung überschrieben
i = 1; 
return 1; 

Und wenn die Zuordnung von 1 bis i nicht geschah, und die Schleife laufen würde m mal, könnte der Compiler optimiert es

i += n * m; 
return i; 

Einschließlich Zuwachs von i Optimierung vollständig (ein d im vorherigen Beispiel auch), wenn i lokal ist und keinen globalen Zustand ändern würde. Wenn i einen flüchtigen qualifizierten Typ aufweist, muss der Compiler diese Optimierungen auslassen. Es muss jeden Schritt tun, wie die Sprachspezifikation beschreibt. Aber selbst dann könnten verschiedene Compiler verschiedene Assembler/Instruktionen abhängig von den Fähigkeiten des Zielprozessors erzeugen.

1

Im Allgemeinen können Sie nicht wissen, wie der Compiler Ihre C++ - Anweisungen in Assembleranweisungen übersetzt. Aus einer C++ - Perspektive ist das Deklarieren einer Variablen, die eine Zuweisung enthält (wie "int x = 5;"), eine Anweisung. Wenn Sie Ihr Programm in einem Debugger durchlaufen, wird es in dieser Zeile gestoppt. Aber wer weiß, was der Compiler damit macht. (Die gesamte Variable könnte für alles, was Sie wissen, optimiert werden.)

+0

Wenn Sie in einem Debugger schrittweise vorgehen, kann es in dieser Zeile nicht enden, abhängig davon, welcher Debugger und welche Art von Optimierungen angewendet werden. –

1

Wie viel zur Kompilierungszeit, Verbindungszeit, Ladezeit und Ausführungszeit tatsächlich ausgeführt wird, hängt von allen möglichen Dingen ab. Eine Implementierung ist nicht einmal garantiert, dass etwas im Speicher als x identifizierbar ist. Der C++ - Standard beschreibt einige Speicherlayout- und Laufzeitbeschränkungen, aber dies nur für Spezifität: Er definiert auch beobachtbares Verhalten und sagt ausdrücklich, dass eine Implementierung nach Belieben funktionieren kann, wenn das gesamte beobachtbare Verhalten übereinstimmt (die "Als-ob" -Regel) .

Dies bedeutet, dass die einzig vernünftige Antwort darauf basieren würde, was Implementierungen tatsächlich tun, und die Antwort ist, dass es davon abhängt.

Warum möchten Sie wissen, ob eine bestimmte Codezeile in einen bestimmten Maschinencode übersetzt wird? Wenn Sie das klarstellen, können wir vielleicht eine Frage beantworten, die Ihnen wirklich nützlich ist.

1

Sie haben nicht erwähnt, welcher Typ x ist.Wenn x ist kein Typ-Pod und es hat einen Konstruktor, der einen int akzeptiert, als

x = 5 

können andere Effekte als ‚Speichern 5 irgendwo‘, wie die Syntax zu implizieren scheint. Jetzt

#include <iostream> 

int i; 

class X { 
    int _y; 
public: 
    X(int y) : _y(y) { 
    i ++; // changes a global variable 
    std::cout << "got " << y << std::endl; // does IO 
    }; 
}; 

X x = 5; 

hat eine ganz andere Bedeutung. Also würde ich sagen, dass im Allgemeinen in C++ x = 5 eine Aussage wie jede andere ist, da sie irgendwelche Nebenwirkungen haben kann (wie in meinem Beispiel: einen globalen Zustand ändern oder IO machen).

Verwandte Themen