Namespaces
Variants

volatile type qualifier

From cppreference.net

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 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 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 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 transformiert 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-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

1) 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
2) static volatile Objekte vom Typ sig_atomic_t werden für die Kommunikation mit signal -Handlern verwendet.
3) 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.
4) Darüber hinaus können volatile-Variablen verwendet werden, um bestimmte Optimierungsformen 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, 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

volatile

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