volatile type qualifier
Jeder individuelle Typ im C
Typsystem
hat mehrere
qualifizierte
Versionen dieses Typs, entsprechend einem, zwei oder allen drei der
const
,
volatile
, und, für Zeiger auf Objekttypen,
restrict
Qualifizierer. Diese Seite beschreibt die Auswirkungen des
volatile
Qualifizierers.
Jeder Zugriff (sowohl Lese- als auch Schreibzugriff), der über einen Lvalue-Ausdruck eines volatile-qualifizierten Typs erfolgt, wird zu Optimierungszwecken als beobachtbarer Seiteneffekt betrachtet und strikt nach den Regeln der abstrakten Maschine ausgewertet (das heißt, alle Schreibvorgänge werden zu einem Zeitpunkt vor dem nächsten Sequenzpunkt abgeschlossen). Dies bedeutet, dass innerhalb eines einzelnen Ausführungsstrangs ein volatile-Zugriff nicht wegoptimiert oder in Bezug auf einen anderen sichtbaren Seiteneffekt, der durch einen Sequenzpunkt vom volatile-Zugriff getrennt ist, neu angeordnet werden kann.
Eine Umwandlung eines nicht-flüchtigen Werts in einen flüchtigen Typ hat keine Auswirkung. Um auf ein nicht-flüchtiges Objekt mit flüchtiger Semantik zuzugreifen, muss seine Adresse in einen Zeiger-auf-flüchtig umgewandelt werden, und dann muss der Zugriff über diesen Zeiger erfolgen.
Jeder Versuch, ein Objekt, dessen Typ volatile-qualifiziert ist, über einen nicht-volatile L-Wert zu lesen oder zu schreiben, führt zu undefiniertem Verhalten:
volatile int n = 1; // Objekt vom volatile-qualifizierten Typ int* p = (int*)&n; int val = *p; // undefiniertes Verhalten
Ein Mitglied eines flüchtig qualifizierten Struktur- oder Union-Typs erhält die Qualifikation des Typs, zu dem es gehört (sowohl bei Zugriff über den
.
Operator als auch den
->
Operator):
struct s { int i; const int ci; } s; // der Typ von s.i ist int, der Typ von s.ci ist const int volatile struct s vs; // die Typen von vs.i und vs.ci sind volatile int und const volatile int
|
Wenn ein Array-Typ mit dem volatile-Typspezifizierer deklariert wird (durch die Verwendung von
|
(bis C23) |
|
Ein Array-Typ und sein Elementtyp werden immer als identisch volatile-qualifiziert betrachtet. |
(seit C23) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // Array von Array von volatile int int* pi = a[0]; // Fehler: a[0] hat Typ volatile int* void *unqual_ptr = a; // OK bis C23; Fehler seit C23 // Hinweis: clang wendet die Regel in C++/C23 auch in C89-C17 Modi an
Wenn ein Funktionstyp mit dem volatile-Typqualifizierer deklariert wird (durch die Verwendung von
typedef
), ist das Verhalten undefiniert.
|
In einer Funktionsdeklaration kann das Schlüsselwort
Die folgenden beiden Deklarationen deklarieren dieselbe Funktion: void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(seit C99) |
Ein Zeiger auf einen nicht-flüchtigen Typ kann implizit in einen Zeiger auf die volatile-qualifizierte Version desselben oder eines kompatiblen Typs konvertiert werden. Die umgekehrte Konvertierung erfordert einen Cast-Ausdruck.
int* p = 0; volatile int* vp = p; // OK: fügt Qualifizierer hinzu (int zu volatile int) p = vp; // Fehler: verwirft Qualifizierer (volatile int zu int) p = (int*)vp; // OK: Cast
Beachten Sie, dass ein Zeiger auf einen Zeiger auf
T
nicht in einen Zeiger auf einen Zeiger auf
volatile T
konvertierbar ist; damit zwei Typen kompatibel sind, müssen ihre Qualifizierer identisch sein:
char *p = 0; volatile char **vpp = &p; // Fehler: char* und volatile char* sind keine kompatiblen Typen char * volatile *pvp = &p; // OK, fügt Qualifizierer hinzu (char* zu char*volatile)
Inhaltsverzeichnis |
Verwendungen von volatile
static
volatile
Objekte modellieren speichergemappte I/O-Ports, und
static
const
volatile
Objekte modellieren speichergemappte Eingabeports, wie beispielsweise eine Echtzeituhr:
volatile short *ttyport = (volatile short*)TTYPORT_ADDR; for(int i = 0; i < N; ++i) *ttyport = a[i]; // *ttyport is an lvalue of type volatile short
static
volatile
Objekte vom Typ
sig_atomic_t
werden für die Kommunikation mit
signal
-Handlern verwendet.
volatile
Variablen, die lokal in einer Funktion sind, die einen Aufruf des
setjmp
Makros enthält, sind die einzigen lokalen Variablen, die garantiert ihre Werte nach der Rückkehr von
longjmp
beibehalten.
Beachten Sie, dass volatile-Variablen nicht für die Kommunikation zwischen Threads geeignet sind; sie bieten keine Atomarität, Synchronisierung oder Speicherreihenfolge. Ein Lesen einer volatile-Variable, die von einem anderen Thread ohne Synchronisierung geändert wird, oder eine gleichzeitige Änderung von zwei nicht synchronisierten Threads ist undefiniertes Verhalten aufgrund eines Datenwettlaufs.
Schlüsselwörter
Beispiel
demonstriert die Verwendung von volatile zur Deaktivierung von Optimierungen
#include <stdio.h> #include <time.h> int main(void) { clock_t t = clock(); double d = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) d += d * n * m; // reads from and writes to a non-volatile printf("Modified a non-volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); t = clock(); volatile double vd = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) { double prod = vd * n * m; // reads from a volatile vd += prod; // reads from and writes to a volatile } printf("Modified a volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); }
Mögliche Ausgabe:
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
Referenzen
- C17-Standard (ISO/IEC 9899:2018):
-
- 6.7.3 Typqualifizierer (S: 87-90)
- C11-Standard (ISO/IEC 9899:2011):
-
- 6.7.3 Type qualifiers (S.: 121-123)
- C99-Standard (ISO/IEC 9899:1999):
-
- 6.7.3 Typqualifizierer (S: 108-110)
- C89/C90 Standard (ISO/IEC 9899:1990):
-
- 6.5.3 Typqualifizierer
Siehe auch
|
C++ Dokumentation
für
cv (
const
und
volatile
) Typqualifizierer
|