volatile type qualifier
Chaque type individuel dans le
système de types
du C possède plusieurs versions
qualifiées
de ce type, correspondant à un, deux ou l'ensemble des trois qualificateurs
const
,
volatile
, et, pour les pointeurs vers des types objet,
restrict
. Cette page décrit les effets du qualificateur
volatile
.
Chaque accès (en lecture et en écriture) effectué via une expression lvalue de type qualifié volatile est considéré comme un effet secondaire observable à des fins d'optimisation et est évalué strictement selon les règles de la machine abstraite (c'est-à-dire que toutes les écritures sont terminées à un moment donné avant le point de séquence suivant). Cela signifie qu'au sein d'un seul thread d'exécution, un accès volatile ne peut pas être optimisé ou réordonné par rapport à un autre effet secondaire visible qui est séparé par un point de séquence de l'accès volatile.
Une conversion d'une valeur non volatile en un type volatile n'a aucun effet. Pour accéder à un objet non volatile en utilisant une sémantique volatile, son adresse doit être convertie en un pointeur-vers-volatile, puis l'accès doit être effectué via ce pointeur.
Toute tentative de lecture ou d'écriture d'un objet dont le type est qualifié volatile via une lvalue non volatile entraîne un comportement indéfini :
volatile int n = 1; // objet de type qualifié volatile int* p = (int*)&n; int val = *p; // comportement indéfini
Un membre d'une structure ou d'un type union qualifié volatile acquiert la qualification du type auquel il appartient (à la fois lorsqu'il est accédé en utilisant l'opérateur
.
ou l'opérateur
->
):
struct s { int i; const int ci; } s; // le type de s.i est int, le type de s.ci est const int volatile struct s vs; // les types de vs.i et vs.ci sont volatile int et const volatile int
|
Si un type tableau est déclaré avec le qualificatif de type volatile (via l'utilisation de
|
(jusqu'à C23) |
|
Un type tableau et son type d'élément sont toujours considérés comme étant identiquement qualifiés volatile. |
(depuis C23) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // tableau de tableau de volatile int int* pi = a[0]; // Erreur : a[0] a le type volatile int* void *unqual_ptr = a; // OK jusqu'à C23 ; erreur depuis C23 // Notes : clang applique la règle en C++/C23 même dans les modes C89-C17
Si un type de fonction est déclaré avec le qualificatif de type volatile (via l'utilisation d'un
typedef
), le comportement est indéfini.
|
Dans une déclaration de fonction, le mot-clé
Les deux déclarations suivantes déclarent la même fonction : void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(depuis C99) |
Un pointeur vers un type non volatile peut être implicitement converti en un pointeur vers la version qualifiée volatile du même type ou compatible . La conversion inverse nécessite une expression de cast.
int* p = 0; volatile int* vp = p; // OK : ajoute des qualificateurs (int vers volatile int) p = vp; // Erreur : supprime des qualificateurs (volatile int vers int) p = (int*)vp; // OK : cast
Notez qu'un pointeur vers un pointeur vers
T
n'est pas convertible en pointeur vers un pointeur vers
volatile T
; pour que deux types soient compatibles, leurs qualifications doivent être identiques :
char *p = 0; volatile char **vpp = &p; // Erreur : char* et volatile char* ne sont pas des types compatibles char * volatile *pvp = &p; // OK, ajoute les qualificateurs (char* vers char*volatile)
Table des matières |
Utilisations de volatile
static
volatile
les objets modélisent les ports d'E/S mappés en mémoire, et
static
const
volatile
les objets modélisent les ports d'entrée mappés en mémoire, tels qu'une horloge temps réel :
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
objets de type
sig_atomic_t
sont utilisés pour la communication avec les
gestionnaires de signaux
.
volatile
Les variables
volatile
qui sont locales à une fonction contenant un appel de la macro
setjmp
sont les seules variables locales garanties de conserver leurs valeurs après le retour de
longjmp
.
Notez que les variables volatiles ne conviennent pas à la communication entre threads ; elles n'offrent pas l'atomicité, la synchronisation ou l'ordonnancement de la mémoire. Une lecture d'une variable volatile qui est modifiée par un autre thread sans synchronisation ou une modification concurrente par deux threads non synchronisés est un comportement indéfini en raison d'une course aux données.
Mots-clés
Exemple
démontre l'utilisation de volatile pour désactiver les optimisations
#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); }
Sortie possible :
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
Références
- Norme C17 (ISO/IEC 9899:2018):
-
- 6.7.3 Qualificateurs de type (p: 87-90)
- Norme C11 (ISO/CEI 9899:2011) :
-
- 6.7.3 Qualificateurs de type (p: 121-123)
- Norme C99 (ISO/IEC 9899:1999) :
-
- 6.7.3 Qualificateurs de type (p: 108-110)
- Norme C89/C90 (ISO/IEC 9899:1990) :
-
- 6.5.3 Qualificateurs de type
Voir aussi
|
Documentation C++
pour
les qualificateurs de type cv (
const
et
volatile
)
|