Namensräume
Varianten
Aktionen

volatile Typqualifizierer

Von cppreference.com
< c‎ | Sprache

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 typedef), ist der Array-Typ nicht volatile-qualifiziert, aber sein Elementtyp ist es.

(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 volatile innerhalb der eckigen Klammern erscheinen, die zur Deklaration eines Array-Typs eines Funktionsparameters verwendet werden. Es qualifiziert den Zeigertyp, in den der Array-Typ umgewandelt wird.

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

1) 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
2) static volatile Objekte vom Typ sig_atomic_t werden zur Kommunikation mit Signalhandlern verwendet.
3) 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.
4) Darüber hinaus können volatile Variablen verwendet werden, um bestimmte Formen von Optimierungen zu deaktivieren, z. B. um Dead Store Elimination oder Constant Folding für Micro-Benchmarks zu deaktivieren.

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

volatile

[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)