volatile Typqualifizierer
Jeder einzelne Typ im C-Typensystem hat mehrere qualifizierte Versionen dieses Typs, entsprechend einem, zwei oder allen drei der Qualifizierer const, volatile und für Zeiger auf Objekttypen restrict. Diese Seite beschreibt die Auswirkungen des volatile-Qualifizierers.
Jeder Zugriff (sowohl Lese- als auch Schreibzugriff), der über einen lvalue-Ausdruck vom volatile-qualifizierten Typ erfolgt, gilt für die Zwecke der Optimierung als beobachtbarer Nebeneffekt und wird streng nach den Regeln der abstrakten Maschine ausgewertet (d. h. alle Schreibvorgänge werden zu einem bestimmten Zeitpunkt vor dem nächsten Sequenzpunkt abgeschlossen). Das bedeutet, dass innerhalb eines einzelnen Ausführungsthreades ein volatiler Zugriff nicht wegoptimiert oder relativ zu einem anderen sichtbaren Nebeneffekt neu geordnet werden kann, der durch einen Sequenzpunkt von dem volatilen Zugriff getrennt ist.
Das Umwandeln eines nicht-volatilen Werts in einen volatilen Typ hat keine Auswirkung. Um auf ein nicht-volatiles Objekt mit volatilen Semantiken zuzugreifen, muss seine Adresse in einen Zeiger-auf-volatile umgewandelt werden und dann muss der Zugriff über diesen Zeiger erfolgen.
Jeder Versuch, über einen nicht-volatilen lvalue auf ein Objekt zuzugreifen oder es zu schreiben, dessen Typ volatile-qualifiziert ist, führt zu undefiniertem Verhalten.
volatile int n = 1; // object of volatile-qualified type int* p = (int*)&n; int val = *p; // undefined behavior
Ein Mitglied eines volatile-qualifizierten Struktur- oder Union-Typs übernimmt die Qualifizierung des Typs, zu dem es gehört (sowohl beim Zugriff über den Operator . als auch über den Operator ->).
struct s { int i; const int ci; } s; // the type of s.i is int, the type of s.ci is const int volatile struct s vs; // the types of vs.i and vs.ci are volatile int and const volatile int
|
Wenn ein Array-Typ mit dem volatile-Typqualifizierer 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 of array of volatile int int* pi = a[0]; // Error: a[0] has type volatile int* void *unqual_ptr = a; // OK until C23; error since C23 // Notes: clang applies the rule in C++/C23 even in C89-C17 modes
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-volatilen Typ kann implizit in einen Zeiger auf die volatile-qualifizierte Version desselben oder eines kompatiblen Typs umgewandelt werden. Die umgekehrte Umwandlung erfordert einen Cast-Ausdruck.
int* p = 0; volatile int* vp = p; // OK: adds qualifiers (int to volatile int) p = vp; // Error: discards qualifiers (volatile int to int) p = (int*)vp; // OK: cast
Beachten Sie, dass Zeiger auf Zeiger auf T nicht in Zeiger auf Zeiger auf volatile T umwandelbar sind; damit zwei Typen kompatibel sind, müssen ihre Qualifizierungen identisch sein.
char *p = 0; volatile char **vpp = &p; // Error: char* and volatile char* are not compatible types char * volatile *pvp = &p; // OK, adds qualifiers (char* to char*volatile)
Inhalt |
[bearbeiten] Verwendung von volatile
static volatile Objekte modellieren speicherabgebildete E/A-Ports, und static const volatile Objekte modellieren speicherabgebildete Eingabeports, wie z. B. 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 zur Kommunikation mit Signalhandlern verwendet.volatile Variablen, die lokal zu einer Funktion sind, die eine Invokation des Makros setjmp enthält, sind die einzigen lokalen Variablen, deren Werte nach der Rückkehr von longjmp garantiert erhalten bleiben.Beachten Sie, dass volatile Variablen nicht für die Kommunikation zwischen Threads geeignet sind; sie bieten keine Atomarität, Synchronisation oder Speicherordnung. Ein Lesezugriff auf eine volatile Variable, die von einem anderen Thread ohne Synchronisation modifiziert wird, oder eine gleichzeitige Modifikation durch zwei nicht synchronisierte Threads, führt aufgrund eines Datenrennens zu undefiniertem Verhalten.
[bearbeiten] Schlüsselwörter
[bearbeiten] 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
[bearbeiten] Referenzen
- C17-Standard (ISO/IEC 9899:2018)
- 6.7.3 Typqualifizierer (S. 87-90)
- C11-Standard (ISO/IEC 9899:2011)
- 6.7.3 Typqualifizierer (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
[bearbeiten] Siehe auch
| C++-Dokumentation für cv-Typqualifizierer (
const und volatile) |