Was ist der Unterschied zwischen einer statischen globalen und einer statischen flüchtigen Variablen?

Lesezeit: 9 Minuten

Benutzer-Avatar
Manoj Zweifel

Ich habe eine statische globale Variable und eine statische flüchtige Variable im Dateibereich verwendet,

beide werden durch eine ISR und eine Hauptschleife aktualisiert und die Hauptschleife überprüft den Wert der Variablen.

hier werden bei der Optimierung weder die globale noch die flüchtige Variable optimiert. Anstatt also eine flüchtige Variable zu verwenden, löst eine globale Variable das Problem.

Ist es also gut, globale Variablen anstelle von flüchtigen zu verwenden?

Gibt es einen bestimmten Grund, statische Volatilität zu verwenden?

Jedes Beispielprogramm wäre wünschenswert.

Danke im Voraus..

Benutzer-Avatar
Brian R. Bondy

Lassen Sie mich zunächst erwähnen, dass eine statische globale Variable dasselbe ist wie eine globale Variable, außer dass Sie die Variable auf den Bereich der Datei beschränken. Dh Sie können diese globale Variable nicht in anderen Dateien über die verwenden extern Stichwort.

Sie können Ihre Frage also auf globale Variablen im Vergleich zu flüchtigen Variablen reduzieren.

Nun zu flüchtig:

Wie const, volatile ist ein Typmodifikator.

Das volatile Das Schlüsselwort wurde erstellt, um Compileroptimierungen zu verhindern, die zu fehlerhaftem Code führen können, insbesondere bei asynchronen Ereignissen.

Objekte deklariert als volatile dürfen in bestimmten Optimierungen nicht verwendet werden.

Das System liest immer den aktuellen wahren Wert eines flüchtigen Objekts an dem Punkt, an dem es verwendet wird, selbst wenn eine vorherige Anweisung nach einem Wert von demselben Objekt gefragt hat. Außerdem wird der Wert des Objekts sofort bei der Zuweisung geschrieben. Das bedeutet, dass eine flüchtige Variable nicht in einem CPU-Register zwischengespeichert wird.

Dr. Jobb’s hat einen großartigen Artikel über Volatilität.

Hier ist ein Beispiel aus dem Artikel von Dr. Jobb:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

Wenn der Compiler das sieht Sleep() Handelt es sich um ein externes Gespräch, wird es davon ausgegangen Sleep() kann unmöglich den Wert der Variablen flag_ ändern. Der Compiler kann also den Wert von speichern flag_ in einem Register. Und in diesem Fall wird es sich nie ändern. Aber wenn ein anderer Thread Wakeup aufruft, liest der erste Thread immer noch aus dem Register der CPU. Wait() wird niemals aufwachen.

Warum also nicht einfach Variablen in Registern zwischenspeichern und das Problem vollständig vermeiden? Es stellt sich heraus, dass diese Optimierung Ihnen insgesamt wirklich viel Zeit sparen kann. In C/C++ können Sie es also explizit über die deaktivieren volatile Stichwort.

Die Tatsache darüber flag_ war eine Mitgliedsvariable, und keine globale Variable (noch statische globale) spielt keine Rolle. Die Erklärung nach dem Beispiel liefert die richtige Begründung, auch wenn Sie es mit globalen Variablen (und statischen globalen Variablen) zu tun haben.

Ein häufiges Missverständnis ist das Deklarieren einer Variablen volatile ausreicht, um die Fadensicherheit zu gewährleisten. Operationen auf der Variablen sind immer noch nicht atomar, obwohl sie nicht in Registern “zwischengespeichert” werden

flüchtig mit Zeigern:

Volatile mit Zeigern, funktioniert wie const mit Zeigern.

Eine Variable vom Typ volatile int * bedeutet, dass die Variable, auf die der Zeiger zeigt, flüchtig ist.

Eine Variable vom Typ int * volatile bedeutet, dass der Zeiger selbst flüchtig ist.

  • Gute Antwort. Das Einzige, was ich hinzufügen möchte (weil es ein weit verbreitetes Missverständnis ist), ist, dass das Deklarieren einer Variablen als flüchtig nicht ausreicht, um die Thread-Sicherheit zu gewährleisten. Operationen auf der Variablen sind immer noch nicht atomar, obwohl sie nicht in Registern “zwischengespeichert” werden

    – jalf

    6. Dezember 2008 um 16:13 Uhr

Benutzer-Avatar
Johannes Schaub – litb

Sie sind verschiedene Dinge. Ich bin kein Experte für flüchtige Semantik. Aber ich denke, es macht Sinn, was hier beschrieben wird.

Global

Global bedeutet nur, dass der betreffende Bezeichner im Dateibereich deklariert wird. Es gibt verschiedene Geltungsbereiche, genannt Funktion (wo Goto-Labels definiert sind), Datei (wo Globale liegen), Block (wo normale lokale Variablen liegen) und Funktionsprototyp (wo Funktionsparameter liegen). Dieses Konzept existiert nur, um die Sichtbarkeit von Identifikatoren zu strukturieren. Das hat nichts mit Optimierungen zu tun.

Statisch

static ist eine Speicherdauer (auf die wir hier nicht eingehen) und eine Möglichkeit, einen Namen anzugeben, der innerhalb der internen Verknüpfung im Dateibereich deklariert ist. Dies kann für Funktionen oder Objekte erfolgen, die nur innerhalb einer Übersetzungseinheit benötigt werden. Ein typisches Beispiel wäre a help Funktion, die die akzeptierten Parameter ausgibt, und die nur von aufgerufen wird main darin definierte Funktion .c Datei.

6.2.2/2 in einem C99-Entwurf:

Wenn die Deklaration eines Dateibereichsbezeichners für ein Objekt oder eine Funktion den Speicherklassenbezeichner static enthält, hat der Bezeichner eine interne Verknüpfung.

Interne Verknüpfung bedeutet, dass die Kennung außerhalb der aktuellen Übersetzungseinheit nicht sichtbar ist (wie z help Funktion von oben).

Flüchtig

Volatil ist eine andere Sache: (6.7.3/6)

Ein Objekt, das einen flüchtig qualifizierten Typ hat, kann auf eine Weise modifiziert werden, die der Implementierung unbekannt ist, oder andere unbekannte Nebeneffekte haben. Daher muss jeder Ausdruck, der sich auf ein solches Objekt bezieht, streng nach den Regeln der abstrakten Maschine ausgewertet werden, wie in 5.1.2.3 beschrieben. Darüber hinaus muss an jedem Sequenzpunkt der zuletzt im Objekt gespeicherte Wert mit dem von der abstrakten Maschine vorgeschriebenen übereinstimmen, sofern er nicht durch die zuvor erwähnten unbekannten Faktoren modifiziert wird.

Der Standard bietet ein hervorragendes Beispiel für ein Beispiel, wo volatile wäre überflüssig (5.1.2.3/8):

Eine Implementierung könnte eine Eins-zu-Eins-Entsprechung zwischen abstrakter und tatsächlicher Semantik definieren: An jedem Sequenzpunkt würden die Werte der tatsächlichen Objekte mit denen übereinstimmen, die von der abstrakten Semantik spezifiziert werden. Das Schlüsselwort volatile
wäre dann überflüssig.

Sequenzpunkte sind Punkte, an denen die Wirkung von Nebenwirkungen bzgl. der abstrakte Maschine abgeschlossen sind (dh externe Bedingungen wie Speicherzellenwerte sind nicht enthalten). Zwischen rechts und links && und ||nach ; und Rückkehr von einem Funktionsaufruf sind zum Beispiel Sequenzpunkte.

Das abstrakte Semantik kann der Compiler daraus ableiten, dass er nur die Codesequenz innerhalb eines bestimmten Programms sieht. Auswirkungen von Optimierungen spielen hier keine Rolle. eigentliche Semantik beinhalten die Auswirkung von Nebeneffekten, die durch das Beschreiben von Objekten verursacht werden (z. B. das Wechseln von Speicherzellen). Ein Objekt als flüchtig zu qualifizieren bedeutet, dass man den Wert eines Objekts immer direkt aus dem Gedächtnis erhält (“wie durch die unbekannten Faktoren modifiziert”). Der Standard erwähnt Threads nirgendwo, und wenn Sie sich auf die Reihenfolge der Änderungen oder auf die Atomarität von Operationen verlassen müssen, sollten Sie plattformabhängige Methoden verwenden, um dies sicherzustellen.

Für einen leicht verständlichen Überblick hat Intel einen großartigen Artikel darüber hier.

Was sollte ich jetzt tun?

Deklarieren Sie Ihre (globalen) Dateibereichsdaten weiterhin als flüchtig. Globale Daten an sich bedeuten nicht, dass der Wert der Variablen gleich dem im Speicher gespeicherten Wert ist. Und static macht Ihre Objekte nur lokal für die aktuelle Übersetzungseinheit (die aktuelle .c Dateien und alle anderen Dateien, die darin enthalten sind).

Benutzer-Avatar
Friol

Das Schlüsselwort “volatile” schlägt dem Compiler vor, bestimmte Optimierungen an Code, der diese Variable betrifft, nicht vorzunehmen; Wenn Sie nur eine globale Variable verwenden, hindert nichts den Compiler daran, Ihren Code falsch zu optimieren.

Beispiel:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr="A";
*portptr="B";

Ohne “volatile” kann der erste Schreibvorgang optimiert werden.

Das Schlüsselwort volatile weist den Compiler an, sicherzustellen, dass die Variable niemals zwischengespeichert wird. Alle Zugriffe darauf müssen auf konsistente Weise erfolgen, um einen konsistenten Wert zwischen allen Threads zu haben. Wenn der Wert der Variablen von einem anderen Thread geändert werden soll, während eine Schleife nach Änderungen sucht, soll die Variable flüchtig sein, da es keine Garantie dafür gibt, dass ein regulärer Variablenwert nicht irgendwann zwischengespeichert wird und die Schleife werde einfach davon ausgehen, dass es gleich bleibt.

Volatile Variable auf Wikipedia

Sie unterscheiden sich möglicherweise nicht in Ihrer aktuellen Umgebung, aber geringfügige Änderungen können das Verhalten beeinflussen.

  • Andere Hardware (mehr Prozessoren, andere Speicherarchitektur)
  • Eine neue Version des Compilers mit besserer Optimierung.
  • Zufällige Variation des Timings zwischen Threads. Ein Problem kann nur einmal in 10 Millionen auftreten.
  • Unterschiedliche Compiler-Optimierungseinstellungen.

Auf lange Sicht ist es viel sicherer, von Anfang an richtige Multithreading-Konstrukte zu verwenden, auch wenn es vorerst ohne sie zu funktionieren scheint.

Wenn Ihr Programm nicht multithreaded ist, spielt es natürlich keine Rolle.

  • ‘volatile’ in C hat nichts mit Multihreading zu tun. Es verhindert nur, dass der Compiler es optimiert (z. B. Register, das einigen IO-Sachen zugeordnet ist). Aber es fügt KEINE Speicherzäune wie volatile in Java ein.

    – Piotr Lesnicki

    6. Dezember 2008 um 15:15 Uhr

Benutzer-Avatar
Piotr Lesnicki

Ich +1 Friols Antwort. Ich möchte einige Präzisierungen hinzufügen, da es bei verschiedenen Antworten viele Verwirrungen zu geben scheint: C’s volatile ist nicht Java’s volatile.

Erstens können Compiler basierend auf dem Datenfluss Ihres Programms viele Optimierungen vornehmen, flüchtig in C verhindert dies, es stellt sicher, dass Sie wirklich jedes Mal an den Speicherort laden/speichern (anstatt Register zu verwenden, um es z. B. zu löschen). . Es ist nützlich, wenn Sie einen speicherabgebildeten IO-Port haben, wie Friol betonte.

Volatile in C hat NICHTS mit Hardware-Caches oder Multithreading zu tun. Es fügt keine Speicherzäune ein, und Sie haben absolut keine Garantie für die Reihenfolge der Operationen, wenn zwei Threads darauf zugreifen. Javas volatile-Schlüsselwort tut jedoch genau das: Einfügen von Speicherzäunen, wo sie benötigt werden.

  • ‘volatile’ in C hat nichts mit Multihreading zu tun. Es verhindert nur, dass der Compiler es optimiert (z. B. Register, das einigen IO-Sachen zugeordnet ist). Aber es fügt KEINE Speicherzäune wie volatile in Java ein.

    – Piotr Lesnicki

    6. Dezember 2008 um 15:15 Uhr

Benutzer-Avatar
Papun

Flüchtige Variable bedeutet, dass der ihr zugewiesene Wert nicht konstant ist, dh wenn eine Funktion eine flüchtige Variable “a=10” enthält und die Funktion bei jedem Aufruf dieser Funktion 1 hinzufügt, dann wird sie immer einen aktualisierten Wert zurückgeben.
{
volatile int a=10;
a++;
}

Wenn die obige Funktion immer wieder aufgerufen wird, wird die Variable a nicht auf 10 neu initialisiert, sie zeigt immer den aktualisierten Wert, bis das Programm läuft. 1. Ausgang = 10 dann 11 dann 12 und so weiter.

  • Entschuldigung, diese Antwort ist wirklich falsch. Du hast dich verwechselt static und volatile.

    – Klaas van Gend

    12. Februar 2014 um 16:28 Uhr

1369890cookie-checkWas ist der Unterschied zwischen einer statischen globalen und einer statischen flüchtigen Variablen?

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

Privacy policy