Was ist der Unterschied zwischen einem statischen globalen und einem statischen flüchtigen Variable?
Frage
Ich habe eine statische globale Variable und eine statische volatile Variable in Dateigültigkeitsbereich verwendet wird,
beide werden von einer ISR und einer Hauptschleife und Hauptschleife überprüft den Wert der Variablen aktualisiert.
hier bei der Optimierung weder die globalen Variable noch die flüchtigen Variable optimiert. Anstatt also eine flüchtige Variable verwendet eine globale Variable löst das Problem.
So ist es gut, globalen Variable zu verwenden, anstatt von flüchtigem?
Jeder besonderer Grund statische volatil zu verwenden ??
Jedes Beispiel Programm würde spürbar sein.
Vielen Dank im Voraus ..
Lösung
Sie sind verschiedene Dinge. Ich bin kein Experte in volatiler Semantik. Aber ich denke, es macht Sinn, was hier beschrieben wird.
Globale
Globale bedeuten nur die Kennung in Frage zu Datei-Gültigkeitsbereich deklariert wird. Es gibt verschiedene Bereiche, genannt Funktion (wobei goto-Etiketten definiert sind), fi le (wobei globals befinden), Block (wo normale lokale Variablen befinden), und Funktionsprototyp (wo Funktionsparameter befinden). Dieses Konzept nur existieren, um die Sichtbarkeit von Identifikatoren zu strukturieren. Es hat nichts mit Optimierungen zu tun hat.
Static
static
ist eine Speicherdauer (wir werden nicht zu dieser hier) und eine Möglichkeit, einen Namen in der Datei Umfang interne Bindung erklärt zu geben. Dies kann für Funktionen oder Objekte nur innerhalb einer Übersetzungseinheit erforderlich erfolgen. Ein typisches Beispiel wäre eine help
Funktion sein, die akzeptierten Parameter auszudrucken, und die nur von der main
Funktion definiert in der gleichen .c
-Datei bezeichnet.
6.2.2 / 2 in einem C99-Entwurf:
Wenn die Deklaration eines fi le Umfang Kennung für ein Objekt oder eine Funktion enthält die Speicherklasse spezifischen ER statisch, weist die interne Kennung Verknüpfung.
Interne Verknüpfung bedeutet, dass die Kennung außerhalb der aktuellen Übersetzungseinheit nicht sichtbar ist (wie die help
Funktion von oben).
Volatile
Flüchtige ist eine andere Sache: ( 6.7.3 / 6 )
Ein Objekt, das flüchtige-quali fi zierte hat Typ kann in einer Weise modifiziert werden nicht bekannt die Umsetzung oder andere unbekannte Nebenwirkungen. daher ist jede Expression unter Bezugnahme auf ein solches Objekt ist streng bewertet werden gemäß die Regeln der abstrakten Maschine, wie in 5.1.2.3 beschrieben. Außerdem, den Wert bei jedem Sequenzpunkt letzten in dem Objekt gespeichert wird zustimmen dass durch die abstrakten vorgeschrieben Maschine, mit Ausnahme des Ande des ED unbekannte Faktoren erwähnt zuvor.
Die Norm stellt ein hervorragendes Beispiel für ein Beispiel, wo volatile
überflüssig wäre ( 5.1.2.3/8 ):
Eine Implementierung könnte de fi nieren eines Eins-zu-Eins-Entsprechung zwischen abstrakt und tatsächliche Semantik: bei jeder Sequenzpunkt, die Werte von die tatsächlichen Objekte würden zustimmen diejenigen spezi fi zierten von der abstrakten Semantik. Das Schlüsselwort
volatile
wäre dann überflüssig.
Sequence Punkte sind Punkte, bei denen die Wirkung von Nebenwirkungen in Bezug auf die abstrakte Maschine abgeschlossen sind (das heißt äußeren Bedingungen wie Speicherzellenwerte sind nicht enthalten). Zwischen dem rechten und der linken Seite von &&
und ||
nach ;
und von einem Funktionsaufruf zurückkehrt sind Sequenzpunkte zum Beispiel.
Die abstrakte Semantik ist, was der Compiler nicht sieht nur die Folge von Codes innerhalb eines bestimmten Programms ableiten kann. Auswirkungen von Optimierungen sind hier irrelevant. tatsächliche Semantik enthält die Auswirkung von Nebenwirkungen erfolgt durch, um Objekte zu schreiben (zB Wechsel von Speicherzellen). ein Objekts als flüchtiges Qualifikations bedeutet ein immer den Wert eines Objektes wird direkt aus dem Speicher ( „wie durch die unbekannten Faktoren modifizierte“). Die Norm legt keine Fäden überall erwähnen, und wenn Sie in der Größenordnung von Änderungen oder auf der Unteilbarkeit der Operationen verlassen müssen, sollten Sie plattformabhängige Weise verwenden, um sicherzustellen.
Für einen einfachen Überblick zu verstehen, hat Intel einen großen Artikel über das hier .
Was soll ich jetzt tun?
Halten Sie Ihre Datei-Umfang (global) Daten als flüchtig erklärt. Globale Daten an sich nicht die Variablenwert bedeuten wird gleich dem Wert sim Speicher Tored. Und statisch nicht nur Ihre Objekte lokal auf die aktuelle Übersetzungseinheit (die aktuellen .c
Dateien und alle anderen Dateien # include'ed durch sie) machen.
Andere Tipps
Lassen Sie mich zunächst erwähnen, dass eine statische globale Variable, das gleiche wie eine globale Variable ist, mit der Ausnahme, dass Sie die Variable auf den Umfang der Datei begrenzen. D. h Sie können diese globale Variable in anderen Dateien über das extern
Schlüsselwort verwenden.
So können Sie Ihre Frage auf globale Variablen vs flüchtigen Variablen reduzieren.
Jetzt auf volatile:
Wie const
, volatile
ist ein Typ Modifikator.
Das volatile
Schlüsselwort erstellt wurde Compiler-Optimierungen zu verhindern, dass Code falsch machen kann, insbesondere, wenn es asynchrone Ereignisse.
Objekte als volatile
erklären möglicherweise nicht in bestimmten Optimierungen verwendet werden.
Das System liest immer den aktuellen wahren Wert eines flüchtigen Objekts an dem Punkt, es verwendet wird, auch wenn eine vorherige Anweisung für einen Wert von demselben Objekt gefragt. Außerdem wird der Wert des Objekts geschrieben sofort auf Zuordnung. Das heißt, es gibt keine Zwischenspeicherung eines flüchtigen Variable in ein CPU-Register.
Dr. Jobb das hat einen großen Artikel auf volatile .
Hier ist ein Beispiel aus dem Dr. Jobb des Artikel:
class Gadget
{
public:
void Wait()
{
while (!flag_)
{
Sleep(1000); // sleeps for 1000 milliseconds
}
}
void Wakeup()
{
flag_ = true;
}
...
private:
bool flag_;
};
Wenn der Compiler sieht, dass Sleep()
ist ein externer Anruf, wird es, dass Sleep()
annehmen kann unmöglich die Variable Flag_ Wert ändern. So kann der Compiler speichert den Wert von flag_
in einem Register. Und in diesem Fall wird es sich nie ändern. Aber wenn ein anderer Thread Anrufe aufzuwecken, ist der erste Thread lesen noch aus dem Register der CPU. Wait()
wird nie aufwachen.
Warum also nicht einfach nie Cache-Variablen in die Register und das Problem vollständig vermeiden?
Es stellt sich heraus, dass diese Optimierung wirklich können Sie insgesamt eine Menge Zeit sparen. So C / C ++ können Sie explizit über das volatile
Schlüsselwort deaktivieren.
Die Tatsache, über diesem flag_
war eine Membervariable, und nicht eine globale Variable (noch statisch global) keine Rolle spielt. Die Erklärung nach dem Beispiel gibt die richtige Argumentation, auch wenn Sie mit globalen Variablen (und statischen globalen Variablen) zu tun hat.
Ein verbreiteter Irrtum ist, dass eine Variable volatile
erklärt ausreicht Thread-Sicherheit zu gewährleisten. Operationen auf die Variable noch nicht atomar, obwohl sie in den Registern nicht „zwischengespeichert“ werden
flüchtig mit Zeigern:
Flüchtige mit Zeigern, funktioniert wie const mit Zeigern.
Eine Variable vom Typ volatile int *
bedeutet, dass die Variable, die der Zeiger zeigt zu flüchtig ist.
Eine Variable vom Typ int * volatile
bedeutet, dass der Zeiger selbst flüchtig ist.
Das „flüchtig“ Schlüsselwort schlägt den Compiler nicht bestimmte Optimierungen auf Code zu tun, dass die Variable beteiligt ist; wenn Sie nur eine globale Variable verwenden, nichts hindert den Compiler zu Unrecht Ihren Code zu optimieren.
Beispiel:
#define MYPORT 0xDEADB33F
volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';
Ohne „flüchtig“, der erste Schreib optimiert werden kann.
Das Schlüsselwort volatile teilt den Compiler, um sicherzustellen, dass Variable wird nicht zwischengespeichert werden. Alle Zugriffe auf es in einer konsistenten Art und Weise vorgenommen werden muß einen konsistenten Wert zwischen allen Threads haben. Wenn der Wert der Variablen von einem anderen Thread geändert werden soll, während Sie eine Schleife Prüfung auf Veränderung haben, möchten Sie die Variable volatil sein, da es keine Garantie dafür gibt, dass ein regulärer Variablenwert wird nicht an einem bestimmten Punkt und die Schleife zwischengespeichert werden wird nur annehmen, dass es gleich bleibt.
Sie können nicht in verschiedenen in der aktuellen Umgebung, aber subtile Veränderungen könnten das Verhalten beeinflussen.
- Verschiedene Hardware (mehr Prozessoren, verschiedene Speicherarchitektur)
- Eine neue Version des Compilers mit einem besseren Optimierung.
- Random Variation Timing zwischen Threads. nur ein Problem kann einmal in 10 Millionen auftreten.
- Verschiedene Compiler Optimierungseinstellungen.
Es ist viel sicherer auf lange Sicht der richtigen Multithreading-Konstrukte von Anfang an zu verwenden, auch wenn die Dinge scheinen, ohne sie jetzt zu arbeiten.
Natürlich, wenn Ihr Programm ist nicht multi-threaded dann spielt es keine Rolle.
I +1 friol Antwort. Ich möchte einige Präzisierungen hinzufügen, wie es scheint eine Menge Verwirrungen in verschiedenen Antworten auf sein. C ist flüchtig ist nicht Java flüchtigen
So zuerst, Compiler kann eine Menge Optimierungen tut auf, basierend auf dem Datenfluss des Programms, flüchtig in C verhindert, dass macht es sicher, dass es jedes Mal, Lade- / Speicher an den Ort, der Sie wirklich (anstelle der Verwendung von Registern des Wisch aus zB). Es ist nützlich, wenn Sie einen Memory Mapped IO-Port haben, wie friol des aufgezeigt.
Volatile in C hat nichts mit Hardware-Caches oder Multithreading zu tun. Es macht Sie keine Speicher Zäune, und Sie haben absolut keine Garantie, in der Größenordnung von Operationen, wenn zwei Threads, es zu tun zugreift. Java flüchtiges Schlüsselwort tut genau das, obwohl:. Einsetzen von Speichern Zäunen bei Bedarf
volatile Variable bedeutet, daß der Wert assinged es nicht konstant ist, das heißt, wenn eine Funktion ein flüchtiges Variable „a = 10“ und die Funktion enthält, ist die Zugabe von 1 in jedem Aufruf dieser Funktion, dann wird es immer dann aktualisierte Wert zurück.
{
volatile int a=10;
a++;
}
wenn die obige Funktion aufgerufen wird immer wieder dann die Variable a auf 10 nicht neu initialisiert wird, wird es immer den aktualisierten Wert zeigen, bis das Programm ausgeführt wird.
1. Ausgang = 10
dann 11
dann 12
und so weiter.