547 votes

Pourquoi le volatile est-il nécessaire en c ?

Pourquoi est-ce que volatile nécessaire en C ? A quoi sert-il ? Que fera-t-il ?

597voto

Nils Pipenbrinck Points 41006

Volatile indique au compilateur de ne pas optimiser tout ce qui a trait à la variable volatile.

Il n'y a qu'une seule raison de l'utiliser : Quand vous vous interfacez avec le matériel.

Disons que vous avez un petit morceau de matériel qui est mappé dans la RAM quelque part et qui a deux adresses : un port de commande et un port de données :

typedef struct
{
  int command;
  int data;
  int isbusy;
} MyHardwareGadget;

Maintenant, vous voulez envoyer une commande :

void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isbusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

Cela semble facile, mais cela peut échouer car le compilateur est libre de changer l'ordre dans lequel les données et les commandes sont écrites. Ainsi, notre petit gadget émettrait des commandes avec la valeur de données précédente. Jetez également un coup d'œil à la boucle wait while busy. Celle-ci sera optimisée. Le compilateur essaiera d'être malin, de lire la valeur de isbusy une seule fois et ensuite d'entrer dans une boucle infinie. Ce n'est pas ce que vous voulez.

Le moyen de contourner ce problème est de déclarer le gadget pointeur comme volatile. De cette façon, le compilateur est obligé de faire ce que vous avez écrit. Il ne peut pas supprimer les affectations de mémoire, il ne peut pas mettre en cache les variables dans les registres et il ne peut pas non plus changer l'ordre des affectations :

C'est la version correcte :

   void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }

60 votes

Personnellement, je préférerais que la taille des entiers soit explicite, par exemple int8/int16/int32, lorsqu'on parle au matériel. Belle réponse cependant ;)

31 votes

Oui, vous devriez déclarer des choses avec une taille de registre fixe, mais hé - c'est juste un exemple.

77 votes

Volatile est également nécessaire dans le code threadé lorsque vous jouez avec des données qui ne sont pas protégées contre la concurrence. Et oui, il y a des moments valables pour faire cela, vous pouvez par exemple écrire une file d'attente de messages circulaire thread safe sans avoir besoin d'une protection explicite de la concurrence, mais elle aura besoin de volatiles.

247voto

Manoj Doubts Points 2080

volatile en C est en fait apparu dans le but de ne pas mettre automatiquement en cache les valeurs de la variable. Il va dire à la machine de ne pas mettre en cache la valeur de cette variable. Elle prendra donc la valeur de la variable volatile de la mémoire principale à chaque fois qu'il la rencontre. Ce mécanisme est utilisé car à tout moment, la valeur peut être modifiée par le système d'exploitation ou par une interruption. Ainsi, en utilisant volatile nous aidera à accéder à la valeur à chaque fois.

1 votes

Entré dans l'existence ? Le terme "volatile" n'était-il pas à l'origine emprunté au C++ ? Eh bien, je crois me souvenir...

2 votes

Ce n'est pas volatile tout à fait - il interdit également certains réordonnancement si spécifié comme volatile .

6 votes

@FaceBro : Le but de volatile était de permettre aux compilateurs d'optimiser le code tout en permettant aux programmeurs d'obtenir la sémantique qui serait obtenue sans ces optimisations. Les auteurs de la norme s'attendaient à ce que des implémentations de qualité prennent en charge toute sémantique utile compte tenu de leurs plates-formes et domaines d'application cibles, et ne s'attendaient pas à ce que les auteurs de compilateurs cherchent à offrir la sémantique de qualité la plus basse qui soit conforme à la norme et qui ne soit pas 100% stupide (notez que les auteurs de la norme reconnaissent explicitement dans la justification...

222voto

CesarB Points 18048

Une autre utilisation de volatile est le traitement des signaux. Si vous avez un code comme celui-ci :

quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

Le compilateur est autorisé à remarquer que le corps de la boucle ne touche pas à l'élément quit et convertir la boucle en une while (true) boucle. Même si le quit est définie dans le gestionnaire de signaux pour SIGINT et SIGTERM ; le compilateur n'a aucun moyen de le savoir.

Toutefois, si le quit la variable est déclarée volatile le compilateur est obligé de le charger à chaque fois, car il peut être modifié ailleurs. C'est exactement ce que vous voulez dans cette situation.

1 votes

Quand vous dites "le compilateur est obligé de la charger à chaque fois, est-ce que c'est comme quand le compilateur décide d'optimiser une certaine variable et que nous ne déclarons pas la variable comme volatile, au moment de l'exécution cette certaine variable est chargée dans les registres du CPU et non dans la mémoire ?

3 votes

@AmitSinghTomar Ça veut dire ce que ça veut dire : Chaque fois que le code vérifie la valeur, elle est rechargée. Sinon, le compilateur est autorisé à supposer que les fonctions qui ne prennent pas une référence à la variable ne peuvent pas la modifier, donc en supposant comme CesarB l'a prévu que la boucle ci-dessus ne fixe pas quit le compilateur peut l'optimiser en une boucle constante, en supposant qu'il n'y a aucun moyen pour quit à modifier entre les itérations. N.B. : Ceci n'est pas nécessairement un bon substitut à la programmation threadsafe réelle.

0 votes

Si quit est une variable globale, alors le compilateur ne doit pas optimiser la boucle while, correct ?

69voto

Chris Jester-Young Points 102876

volatile indique au compilateur que votre variable peut être modifiée par d'autres moyens que le code qui y accède. Par exemple, il peut s'agir d'un emplacement mémoire mappé en entrée/sortie. Si cela n'est pas spécifié dans de tels cas, certains accès aux variables peuvent être optimisés, par exemple, son contenu peut être maintenu dans un registre, et l'emplacement mémoire n'est pas relu à nouveau.

33voto

Robert S. Barnes Points 17244

Voir cet article d'Andrei Alexandrescu, " volatile - le meilleur ami du programmeur multithreadé "

Le site volatile mot-clé a été pour empêcher les optimisations du compilateur compilateur qui pourraient rendre le code incorrect en présence de certains événements asynchrones. Par exemple, si vous déclarez une variable primitive comme volatile le compilateur n'est pas autorisé à le mettre en cache dans un registre -- une optimisation commune qui serait désastreuse si cette variable était partagée entre plusieurs threads. Ainsi, la règle générale est que, si vous avez des variables de type primitif qui doivent être partagées entre plusieurs threads, déclarez ces variables volatile . Mais vous pouvez faire beaucoup plus avec ce mot-clé : vous pouvez l'utiliser pour attraper du code qui n'est pas thread safe, et vous pouvez le faire au moment de la compilation. Cet article montre comment faire ; la solution implique un simple pointeur intelligent qui qui permet également de sérialiser facilement sections critiques du code.

L'article s'applique à la fois C et C++ .

Voir aussi l'article " C++ et les dangers du verrouillage à double vérification " par Scott Meyers et Andrei Alexandrescu :

Ainsi, lorsque l'on traite certains emplacements de mémoire (par exemple, les ports mappés en mémoire ou la mémoire référencée par les ISR [ Interrupt Service Routines ] ), certaines optimisations doivent être suspendues. volatile existe pour spécifier un traitement spécial pour de tels emplacements, en particulier : (1) le contenu d'une variable volatile est "instable" (peut changer par des moyens inconnus du compilateur), (2) toutes les écritures sur les données volatiles sont "observables", elles doivent donc être exécutées religieusement, et (3) toutes les opérations sur les données volatiles sont exécutées dans l'ordre dans lequel elles apparaissent dans le code source. Les deux premières règles garantissent une lecture et une écriture correctes. La dernière permet la mise en œuvre de protocoles d'E/S qui mélangent entrée et sortie. C'est de manière informelle ce que garantit la volatilité du C et du C++.

1 votes

La norme précise-t-elle si une lecture est considérée comme un "comportement observable" si la valeur n'est jamais utilisée ? J'ai l'impression qu'elle devrait l'être, mais lorsque j'ai affirmé qu'elle l'était ailleurs, quelqu'un m'a demandé une citation. Il me semble que sur toute plate-forme où la lecture d'une variable volatile pourrait avoir un effet, un compilateur devrait être tenu de générer un code qui effectue chaque lecture indiquée précisément une fois ; sans cette exigence, il serait difficile d'écrire un code qui génère une séquence prévisible de lectures.

0 votes

@supercat : Selon le premier article, "Si vous utilisez le modificateur volatile sur une variable, le compilateur ne mettra pas cette variable en cache dans les registres - chaque accès touchera l'emplacement mémoire réel de cette variable." De plus, dans la section §6.7.3.6 de la norme c99, il est dit : "Un objet qui a un type qualifié de volatile peut être modifié d'une manière inconnue de l'implémentation ou avoir d'autres effets secondaires inconnus." Cela implique en outre que les variables volatiles ne peuvent pas être mises en cache dans des registres et que toutes les lectures et écritures doivent être exécutées dans l'ordre relatif aux points de séquence, qu'elles sont en fait observables.

0 votes

Ce dernier article indique en effet explicitement que les lectures sont des effets secondaires. Le premier indique que les lectures ne peuvent pas être effectuées hors séquence, mais ne semble pas exclure la possibilité de les éluder complètement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X