Wo werden Ausdrücke und Konstanten gespeichert, wenn nicht im Arbeitsspeicher?

Lesezeit: 10 Minuten

Benutzeravatar von Aquarius_Girl
Wassermann_Mädchen

Aus Programmiersprache C von Brian W. Kernighan

&-Operator gilt nur für Objekte im Speicher: Variablen und Array-Elemente. Sie kann nicht auf Ausdrücke, Konstanten oder Registervariablen angewendet werden.

Wo werden Ausdrücke und Konstanten gespeichert, wenn nicht im Arbeitsspeicher? Was bedeutet dieses Zitat?

Z.B:
&(2 + 3)

Warum können wir seine Adresse nicht nehmen? Wo wird es gespeichert?
Wird die Antwort auch für C++ dieselbe sein, da C ihr übergeordnetes Element ist?

Diese verknüpfte Frage erklärt, dass solche Ausdrücke sind rvalue Objekte und alles rvalue Objekte haben keine Adressen.

Meine Frage ist, wo werden diese Ausdrücke gespeichert, sodass ihre Adressen nicht abgerufen werden können?

  • Ausdrücke werden nirgendwo gespeichert, deshalb können Sie ihre Adresse nicht abrufen. Dasselbe gilt für numerische Literalkonstanten.

    – Irgendein Programmierer-Typ

    19. Dezember 2017 um 9:56 Uhr


  • Der Wert des Ausdrucks wird direkt in den Registern des Prozessors gespeichert, sodass er keine Speicheradresse hat, die vom &-Operator adressiert werden kann

    – rauben_

    19. Dezember 2017 um 9:56 Uhr

  • Mögliches Duplikat von Warum nimmt die Adresse eines vorübergehenden illegalen? oder die Adresse eines temporären Objekts nehmen oder Wo werden temporäre Objekte gespeichert? oder etc. Wie auch immer Sie es schneiden, dies wurde viele Male gefragt und beantwortet. Und zu verstehen, wie High-Level-Code zu Maschinencode zusammengesetzt wird, würde dies beantworten.

    – Unterstrich_d

    19. Dezember 2017 um 9:58 Uhr


  • Sie sind im Speicher gespeichert, nur nicht adressierbarer Speicher. CPUs haben Arbeitsregister, Flags und sogar Anweisungen mit implizierten Konstanten.

    – Lee Daniel Crocker

    19. Dezember 2017 um 18:59 Uhr

  • C++ trübt das Wasser ein wenig, indem das Ergebnis eines Ausdrucks kann an eine Funktion übergeben werden, die eine konstante Referenz nimmt const T& oder eine Rvalue-Referenz T&&, und dann kann diese Funktion die Adresse dieses Arguments übernehmen, wenn sie möchte. Selbst wenn T ist ein einfacher eingebauter Typ wie int!

    – Daniel Schepler

    19. Dezember 2017 um 19:52 Uhr

Benutzeravatar von aaaaaa123456789
aaaaa123456789

Betrachten Sie die folgende Funktion:

unsigned sum_evens (unsigned number) {
  number &= ~1; // ~1 = 0xfffffffe (32-bit CPU)
  unsigned result = 0;
  while (number) {
    result += number;
    number -= 2;
  }
  return result;
}

Lassen Sie uns nun das Compiler-Spiel spielen und versuchen, dies von Hand zu kompilieren. Ich gehe davon aus, dass Sie x86 verwenden, da dies von den meisten Desktop-Computern verwendet wird. (x86 ist der Befehlssatz für Intel-kompatible CPUs.)

Lassen Sie uns eine einfache (nicht optimierte) Version durchgehen, wie diese Routine aussehen könnte, wenn sie kompiliert wird:

sum_evens:
  and edi, 0xfffffffe ;edi is where the first argument goes
  xor eax, eax ;set register eax to 0
  cmp edi, 0 ;compare number to 0
  jz .done ;if edi = 0, jump to .done
.loop:
  add eax, edi ;eax = eax + edi
  sub edi, 2 ;edi = edi - 2
  jnz .loop ;if edi != 0, go back to .loop
.done:
  ret ;return (value in eax is returned to caller)

Nun, wie Sie sehen können, sind die Konstanten im Code (0, 2, 1) erscheinen tatsächlich als Teil der CPU-Anweisungen! In der Tat, 1 taucht überhaupt nicht auf; der Compiler (in diesem Fall nur ich) rechnet bereits ~1 und verwendet das Ergebnis im Code.

Während Sie die Adresse eines CPU-Befehls nehmen können, macht es oft keinen Sinn, die Adresse eines Teils davon zu nehmen (in x86 können Sie das manchmal, aber in vielen anderen CPUs können Sie das einfach überhaupt nicht), und Codeadressen sind unterscheidet sich grundlegend von Datenadressen (weshalb Sie einen Funktionszeiger (eine Codeadresse) nicht als regulären Zeiger (eine Datenadresse) behandeln können). In einigen CPU-Architekturen sind Codeadressen und Datenadressen vollständig inkompatibel (obwohl dies bei x86 nicht der Fall ist, wie es die meisten modernen Betriebssysteme verwenden).

Merken Sie sich das while (number) ist äquivalent zu while (number != 0). Dass 0 taucht im kompilierten Code überhaupt nicht auf! Es wird durch die impliziert jnz Anweisung (springen, wenn nicht Null). Dies ist ein weiterer Grund, warum Sie die Adresse davon nicht nehmen können 0 – es hat keine, es ist buchstäblich nirgendwo.

Ich hoffe, das macht es für Sie klarer.

  • (@aaa: Fügen Sie irgendwo einen Verweis auf GodBolt ein: godbolt.org) (und du brauchst int result)

    – Jongware

    19. Dezember 2017 um 10:14 Uhr

  • Dasselbe gilt auch für viele benannte Variablen – sie existieren möglicherweise auch nicht im Hauptspeicher. Das heißt, nicht bis der Compiler gezwungen ist, sie dort abzulegen, weil Sie ihre Adresse nehmen. Es ist also unklar, warum dasselbe nicht für Ausdrücke gilt.

    – Oliver Charlesworth

    19. Dezember 2017 um 10:18 Uhr


  • @OliverCharlesworth, das gilt in diesem Beispiel eindeutig als result hat auch keine adresse. Wenn der Compiler jedoch gezwungen war, ihm eine zu geben, ist die Semantik der Behandlung der Variablen klar. Konstanten sind in dieser Hinsicht umständlicher. Aber du kann tun ((unsigned []) {3}) Wenn Sie beispielsweise einen Zeiger auf eine Konstante 3 benötigen, ist dies durch die Sprache gut definiert. (Zumindest in C; bei C++ bin ich mir hier nicht sicher.)

    – aaaaaa123456789

    19. Dezember 2017 um 10:20 Uhr

  • Ich denke, was ich meine, ist, dass diese Antwort mehr oder weniger rückwärts gerichtet ist – Sie sehen sich den Weg des Compilers an tut verhalten sich angesichts der tatsächlich Semantik der Sprache, und dies dann als Begründung für diese Semantik angeben. Aber der Compiler wird (und könnte) jeden Maschinencode generieren, der notwendig war, um die Semantik der Sprache zu implementieren – es gibt keinen intrinsischen/physikalischen Grund dafür konnte nicht Nehmen Sie die Adresse von Ausdrücken usw.

    – Oliver Charlesworth

    19. Dezember 2017 um 11:33 Uhr

  • Allerdings wäre es eine bessere Antwort (IMO;)

    – Oliver Charlesworth

    19. Dezember 2017 um 11:37 Uhr

Benutzeravatar von Useless
Nicht zu gebrauchen

Wo werden diese Ausdrücke gespeichert, sodass die Adressen nicht abgerufen werden können?

Ihre Frage ist nicht wohlgeformt.

  • Konzeptionell

    Es ist, als würde man fragen, warum Menschen über den Besitz von Substantiven diskutieren können, aber nicht über Verben. Substantive beziehen sich auf Dinge die (potenziell) besessen werden können, und Verben beziehen sich auf Aktionen die durchgeführt werden. Sie können eine Aktion nicht besitzen oder eine Sache ausführen.

  • In Bezug auf die Sprachspezifikation

    Ausdrücke sind es nicht gelagert an erster Stelle sind sie ausgewertet. Sie können vom Compiler zur Kompilierzeit oder vom Prozessor zur Laufzeit ausgewertet werden.

  • In Bezug auf die sprachliche Umsetzung

    Betrachten Sie die Aussage

    int a = 0;
    

    Dies macht zwei Dinge: Erstens deklariert es eine Integer-Variable a. Das ist definiert etwas zu sein, dessen Adresse man nehmen kann. Es liegt am Compiler, alles zu tun, was auf einer bestimmten Plattform sinnvoll ist ermöglichen Sie nehmen die Adresse von a.

    Zweitens setzt es den Wert dieser Variablen auf Null. Das macht nicht bedeutet, dass irgendwo in Ihrem kompilierten Programm eine ganze Zahl mit dem Wert Null existiert. Es könnte allgemein als implementiert werden

    xor eax,eax
    

    das heißt, XOR (exklusiv-oder) die eax bei sich registrieren. Das ergibt immer Null, was auch immer vorher da war. Es gibt jedoch keinen festen Wertgegenstand 0 im kompilierten Code, um dem Ganzzahlliteral zu entsprechen 0 Du hast in der Quelle geschrieben.

Nebenbei, wenn ich das sage a Oben ist etwas, dessen Adresse Sie nehmen können – es ist erwähnenswert, dass es möglicherweise nicht wirklich eine Adresse hat wenn nicht du nimmst es. Zum Beispiel die eax Das in diesem Beispiel verwendete Register hat keine Adresse. Wenn der Compiler beweisen kann, dass das Programm noch korrekt ist, a kann sein ganzes Leben in diesem Register verbringen und niemals im Hauptspeicher existieren. Umgekehrt, wenn Sie den Ausdruck verwenden &a Irgendwo sorgt der Compiler dafür, dass adressierbarer Speicherplatz zum Speichern erstellt wird a‘s Wert in.


Beachten Sie zum Vergleich, dass ich problemlos eine andere Sprache auswählen kann, in der ich kann Nehmen Sie die Adresse eines Ausdrucks.

Es wird wahrscheinlich interpretiert, da die Kompilierung diese Strukturen normalerweise verwirft, sobald die maschinenausführbare Ausgabe sie ersetzt. Zum Beispiel hat Python Laufzeit-Introspektion und code Objekte.

Oder ich kann von LISP ausgehen und es erweitern, um eine Art Adressoperation für S-Ausdrücke bereitzustellen.

Das Wichtigste, was sie beide gemeinsam haben, ist, dass sie sind nicht Cwas eine Frage des Designs und der Definition ist bietet keine diese Mechanismen.

  • Dies spielt etwas locker mit der Terminologie – in den Augen des Sprachstandards “Objekt” ! = “Variable”. Ein Temporär ist auch ein Objekt.

    – Oliver Charlesworth

    19. Dezember 2017 um 11:35 Uhr

  • Abgesehen davon denke ich, dass dies bisher die beste Antwort hier ist – es ist die einzige, die (mehr oder weniger) sagt “weil der Standard dies sagt”, anstatt Implementierungsdetails so zu betrachten, als wären sie ein Beweis für eine intrinsische / körperliche Einschränkung.

    – Oliver Charlesworth

    19. Dezember 2017 um 11:42 Uhr

Solche Ausdrücke werden Teil des Maschinencodes. Ein Ausdruck 2 + 3 wird wahrscheinlich in die Maschinencodeanweisung “Lade 5 in Register A” übersetzt. CPU-Register haben keine Adressen.

  • Theoretisch sollten sie, wenn sie in Maschinencode übersetzt werden, Platz im .text-Abschnitt einnehmen. Aber sie tun es nicht! Wieso ist es so?

    – Gaurav Pathak

    19. Dezember 2017 um 10:02 Uhr


  • Dasselbe gilt für benannte Variablen, daher bin ich mir nicht sicher, ob dies eine gute Erklärung ist.

    – Oliver Charlesworth

    19. Dezember 2017 um 10:08 Uhr

  • @ Gaurav: was “sie”? In Lundins Beispiel die Zahl 5 kann als Literaloperand als Teil einer größeren Maschinencodeanweisung erscheinen. Teile einer Anweisung haben ebenfalls keine Adressen. (Wenn Sie bei der Semantik pingelig sein wollen. Sie tun aber Sie können nicht darauf zugreifen.) (Nitpick Nr. 2: Einige Architekturen speichern die tatsächliche Zahl 5 möglicherweise nicht als eigenes Byte.) (Nitpick Nr. 3: Abhängig von den Umständen kann die Zahl 5 darf nicht erscheinen überhaupt in der Anleitung selbst. In Betracht ziehen a = 5*b; die kompiliert werden können lea eax,[ebx+4*ebx].)

    – Jongware

    19. Dezember 2017 um 10:09 Uhr

  • Ich war nicht pingelig. Es ist nur aus Neugier und Unwissenheit, die Sie sagen können.

    – Gaurav Pathak

    19. Dezember 2017 um 10:14 Uhr

  • @Gaurav Sie nehmen Platz in .text ein, aber nicht als einziges Literal, sondern als Anweisungs-Op-Code “Lade Register A”, gefolgt von der Zahl 5. Wenn Sie diese Adresse in .text lesen würden, würden Sie erhalten die ganze Anweisung.

    – Ludin

    19. Dezember 2017 um 10:31 Uhr

Benutzeravatar von klutt
klutt

Es ist nicht wirklich sinnvoll, die Adresse in einen Ausdruck zu bringen. Das Nächstliegende, was Sie tun können, ist ein Funktionszeiger. Ausdrücke werden nicht im gleichen Sinne wie Variablen und Objekte gespeichert.

Ausdrücke werden im eigentlichen Maschinencode gespeichert. Natürlich könnten Sie die Adresse finden, an der der Ausdruck ausgewertet wird, aber es macht einfach keinen Sinn, dies zu tun.

Lesen Sie etwas über die Montage. Ausdrücke werden im Textsegment gespeichert, während Variablen in anderen Segmenten wie Daten oder Stack gespeichert werden.

https://en.wikipedia.org/wiki/Datensegment

Eine andere Möglichkeit, es zu erklären, ist, dass Ausdrücke CPU-Anweisungen sind, während Variablen reine Daten sind.

Noch etwas zu beachten: Der Compiler optimiert oft Dinge weg. Betrachten Sie diesen Code:

int x=0;
while(x<10)
    x+=1;

Dieser Code wird wahrscheinlich optimiert auf:

int x=10;

Also, was würde die Adresse zu (x+=1) meinst du in diesem Fall? Es ist nicht einmal im Maschinencode vorhanden, hat also per Definition überhaupt keine Adresse.

Benutzeravatar von Basile Starynkevitch
Basile Starynkevitch

Wo werden Ausdrücke und Konstanten gespeichert, wenn nicht im Speicher

In einigen (eigentlich vielen) Fällen ist ein konstanter Ausdruck nicht gelagert überhaupt. Denken Sie insbesondere an Compiler optimierenund siehe CppCon 2017: Matt Godbolt’s sich unterhalten „Was hat mein Compiler in letzter Zeit für mich getan? Entriegeln des Deckels des Compilers“

In Ihrem speziellen Fall von etwas C-Code mit 2 + 3hätten die meisten optimierenden Compiler konstant gefaltet das in 5, und diese 5-Konstante könnte gerecht sein Innerhalb etwas Maschinensprache Anweisung (wie ein Bitfeld) Ihrer Codesegment und nicht einmal einen genau definierten Speicherort haben. Wenn diese Konstante 5 ein Schleifenlimit wäre, hätten einige Compiler es tun können Schleife abrollenund diese Konstante erscheint nicht mehr im Binärcode.

Siehe auch diese Antwort usw. …

Beachten Sie, dass C11 ist ein Spezifikation in Englisch geschrieben. Lesen Sie es n1570 Standard. Lesen Sie auch die viel umfangreichere Spezifikation von C++11 (oder höher).

Das Nehmen der Adresse einer Konstanten ist durch die verboten Semantik von C (und von C++).

1408660cookie-checkWo werden Ausdrücke und Konstanten gespeichert, wenn nicht im Arbeitsspeicher?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy