flüchtig vs. veränderlich in C ++

85

Ich habe eine Frage zum Unterschied zwischen flüchtig und veränderlich. Ich bemerkte, dass beide bedeuten, dass es geändert werden könnte. Was sonst? Sind sie dasselbe? Was ist der Unterschied? Wo sind sie anwendbar? Warum werden die beiden Ideen vorgeschlagen? Wie benutzt man sie auf unterschiedliche Weise?

Vielen Dank.

skydoor
quelle

Antworten:

112

Ein mutableFeld kann auch in einem Objekt geändert werden, auf das über einen constZeiger oder eine Referenz zugegriffen wird , oder in einem constObjekt, sodass der Compiler weiß, dass es nicht im R / O-Speicher gespeichert werden soll. Ein volatileSpeicherort kann durch Code geändert werden, den der Compiler nicht kennt (z. B. einen Treiber auf Kernelebene), sodass der Compiler z. B. die Registerzuweisung dieses Werts unter der ungültigen Annahme, dass der Wert "möglicherweise nicht haben kann", nicht optimieren kann geändert "seit es zuletzt in dieses Register geladen wurde. Dem Compiler werden sehr unterschiedliche Arten von Informationen gegeben, um sehr unterschiedliche Arten ungültiger Optimierungen zu stoppen.

Alex Martelli
quelle
13
volatileObjekte können auch durch Prozesse geändert werden, an denen die CPU überhaupt nicht beteiligt ist. Beispielsweise kann sich ein von Bytes empfangenes Register in einem Kommunikationsperipheriegerät beim Empfang eines Bytes inkrementieren (und dies kann sogar einen Interrupt auslösen). Ein weiteres Beispiel ist ein Register für anstehende Interrupt-Flags in einem Peripheriegerät.
Mike DeSimone
55
Dies volatilebedeutet nicht nur, dass sich das Objekt außerhalb des Wissens des Compilers ändern kann, sondern auch, dass Schreibvorgänge in das Objekt vom Compiler nicht entfernt werden können, selbst wenn diese Schreibvorgänge nutzlos erscheinen. Beispiel: x = 1; x = 0; Wenn xes flüchtig ist, muss der Compiler beide Schreibvorgänge ausführen (die auf Hardwareebene von Bedeutung sein können). Für ein nichtflüchtiges Objekt könnte sich der Compiler jedoch dafür entscheiden, das nicht zu schreiben, 1da es nie verwendet wird.
Michael Burr
14
Ein Objekt kann sowohl markiert werden constund volatile! Sie können das Objekt nicht ändern, aber es kann hinter Ihrem Rücken geändert werden.
CTMacUser
2
@Destructor: Die übliche Situation ist das Schreiben in ein Hardware-Geräteregister.
Michael Burr
5
@Destructor Nehmen wir an, Sie steuern den Status einer LED. Schreiben 0 schaltet es aus, Schreiben 1 schaltet es ein. Wenn ich die LED blinken muss, um einen Fehlerstatus zu kommunizieren, der Compiler jedoch beschließt, alle Schreibvorgänge außer dem letzten zu optimieren, da keiner der Werte verwendet wird, blinkt die LED nie und das gewünschte Verhalten wird nicht realisiert .
Iheanyi
28

mutable: Das veränderbare Schlüsselwort überschreibt alle einschließenden const-Anweisungen. Ein veränderbares Mitglied eines const-Objekts kann geändert werden.

volatile: Das Schlüsselwort volatile ist ein implementierungsabhängiger Modifikator, der beim Deklarieren von Variablen verwendet wird und den Compiler daran hindert, diese Variablen zu optimieren. Volatile sollte mit Variablen verwendet werden, deren Wert sich auf unerwartete Weise ändern kann (z. B. durch einen Interrupt), was zu Konflikten mit Optimierungen führen kann, die der Compiler möglicherweise durchführt.

Quelle

xian
quelle
Sie sagten, Volatile should be used with variables whose value can change in unexpected wayssollten wir es vorziehen, es zufällig zu verwenden?
Asif Mushtaq
@AsifMushtaq keine Werte. Wege. veränderbare Effekte wirken sich auf die Berechtigungen aus, die der von Ihnen geschriebene Code hat. Sie können also über eine const ptr- oder const-Referenz auf die Variable zugreifen. Was ist, wenn es nicht Ihr Code ist, der es ändert? Etwas, von dem der Compiler den ptr- oder Referenztyp nicht überprüfen kann? Das ist flüchtig. Und flüchtig erzwingt auch das Zurückschreiben des Caches in den Hauptspeicher. Dies wird also viel mit Multithread-Code verwendet. :)
Dan
22

Sie sind definitiv nicht dasselbe. Mutable interagiert mit const. Wenn Sie einen const-Zeiger haben, können Sie normalerweise keine Mitglieder ändern. Mutable bietet eine Ausnahme von dieser Regel.

Volatile hingegen hat nichts mit Änderungen zu tun, die vom Programm vorgenommen wurden. Dies bedeutet, dass sich der Speicher aus Gründen ändern kann, die außerhalb der Kontrolle des Compilers liegen. Daher muss der Compiler die Speicheradresse jedes Mal lesen oder schreiben und kann den Inhalt nicht in einem Register zwischenspeichern.

Ben Voigt
quelle
"Volatile hingegen hat nichts mit Änderungen zu tun, die vom Programm vorgenommen wurden ..." - hmmm, machen Sie ein Mitglied flüchtig und sehen Sie, was während des Kompilierens kaputt geht. Der Versuch, flüchtig nachträglich hinzuzufügen, ähnelt dem Versuch, const nachträglich hinzuzufügen ... Schmerzhaft.
JWW
@jww: Es hat nichts mit dem Schreiben des Programms zu tun. Sie können die Adresse eines Objekts vom Typ nehmen T, in einem speichern const T*und daraus lesen. Wenn Sie dieses Objekt volatileerstellen, schlägt das Speichern seiner Adresse const T*fehl, obwohl Sie nie versuchen zu schreiben. volatileund Änderungen / Modifikationen / Speicherschreibvorgänge aus dem Programmcode sind vollständig orthogonal.
Ben Voigt
17

Eine grobe, aber effektive Art, über den Unterschied nachzudenken, ist:

  • Der Compiler weiß, wann sich ein veränderbares Objekt ändert.
  • Der Compiler kann nicht wissen, wann sich ein flüchtiges Objekt ändert.
Jonathan Leffler
quelle
1
In diesem Sinne: volatilebytes_received, mutablereference_count.
Mike DeSimone
11

Eine markierte Variable mutableermöglicht das Ändern in einer deklarierten Methodeconst .

Eine markierte Variable volatileteilt dem Compiler mit, dass er die Variable jedes Mal lesen / schreiben muss, wenn Ihr Code dies ebenfalls mitteilt (dh er kann den Zugriff auf die Variable nicht optimieren).

Kyle Lutz
quelle
4

Ich möchte hinzufügen, dass flüchtig auch sehr nützlich ist, wenn es um Multithreading-Anwendungen geht, dh Sie haben Ihren Hauptthread (wo main () lebt) und Sie erzeugen einen Arbeitsthread, der sich weiter dreht, während eine Variable "app_running" wahr ist. main () steuert, ob "app_running" wahr oder falsch ist. Wenn Sie also das flüchtige Attribut nicht zur Deklaration von "app_running" hinzufügen, optimiert der Compiler den Zugriff auf "app_running" im Code, der vom sekundären Thread main ( ) ändert möglicherweise "app_running" in "false", der sekundäre Thread wird jedoch weiterhin ausgeführt, da der Wert zwischengespeichert wurde. Ich habe das gleiche Verhalten mit gcc unter Linux und VisualC ++ gesehen. Ein "flüchtiges" Attribut in der "app_running" -Deklaration löste das Problem. So,

BinCoder
quelle
1
Nein! Dies ist ein häufiges Missverständnis. C ++ 11 und C11 führten Atomics zu diesem Zweck ein. Stackoverflow.com/questions/8819095/…
KristianR