2011-01-04 5 views
61

Si nous déclarons une variable comme volatile chaque fois que la nouvelle valeur est mis à jour
Si nous déclarons une variable const la valeur de cette variable ne sera pas modifiéDifférence entre volatile const & const

Puis const volatile int temp;
Quelle est l'utilité de déclarer la variable temp comme ci-dessus? Que se passe-t-il si nous déclarons comme const int temp
?

+0

Vous ne voudriez pas utiliser 'const volatile int temp; 'à la portée du bloc (c'est-à-dire à l'intérieur de' {} '), il ne sert à rien ici. –

Répondre

21

Ce n'est pas parce que la variable est constante qu'elle n'a peut-être pas changé entre deux points de séquence. Constante est une promesse que vous faites de ne pas changer la valeur, pas que la valeur ne sera pas changée.

+5

Plus un pour souligner que les données 'const' ne sont pas" constantes ". –

24
  • volatile va dire au compilateur de ne pas optimiser le code lié la variable, généralement quand on sait qu'il peut être modifié de « l'extérieur », par exemple par un autre fil.
  • const indiquera au compilateur qu'il est interdit au programme de modifier la valeur de la variable.
  • const volatile est une chose très spéciale que vous verrez probablement utilisé exactement 0 fois dans votre vie (tm). Comme on peut s'y attendre, cela signifie que le programme ne peut pas modifier la valeur de la variable, mais que la valeur peut être modifiée de l'extérieur, donc aucune optimisation ne sera effectuée sur la variable.
+8

J'aurais pensé que les variables 'volatiles' sont généralement ce qui se passe quand on commence à jouer avec du matériel, pas avec d'autres threads. Où j'ai vu 'const volatile' utilisé dans des choses comme les registres d'état mappés en mémoire ou similaire. –

+2

Bien sûr, vous avez absolument raison, le multithreading n'est qu'un exemple, mais pas le seul :). – mingos

+13

Si vous travaillez avec des systèmes embarqués, vous le verrez très souvent. –

95

Un objet marqué comme const volatile ne sera pas autorisé à modifier le code (une erreur sera augmentée en raison de la qualification const) - au moins par ce nom particulier/pointeur.

La partie volatile du qualificatif signifie que le compilateur ne peut pas optimiser ou réorganiser l'accès à l'objet.

Dans un système embarqué, il est généralement utilisé pour accéder aux registres matériels qui peuvent être lus et mis à jour par le matériel, mais cela n'a aucun sens d'écrire (ou d'écrire).

Un exemple peut être le registre d'état d'un port série. Divers bits indiquent si un caractère attend d'être lu ou si le registre de transmission est prêt à accepter un nouveau caractère (c'est-à-dire, - il est vide). Chaque lecture de ce registre d'état peut entraîner une valeur différente en fonction de ce qui s'est passé dans le matériel du port série.

Cela n'a aucun sens d'écrire dans le registre d'état (selon la spécification matérielle particulière), mais vous devez vous assurer que chaque lecture du registre entraîne une lecture réelle du matériel - en utilisant une valeur mise en cache d'un lecture précédente ne vous dira pas des changements dans l'état du matériel.

Un exemple rapide:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg; // correct hardware addresses 


#define UART_CHAR_READY 0x00000001 

int get_next_char() 
{ 
    while ((*status_reg & UART_CHAR_READY) == 0) { 
     // do nothing but spin 
    } 

    return *recv_reg; 
} 

Si ces pointeurs ne sont pas marqués comme étant volatile, quelques problèmes peuvent se produire:

  • le test de boucle while peut lire le registre d'état qu'une seule fois, depuis le compilateur pourrait supposer que tout ce qu'il pointe ne changera jamais (il n'y a rien dans le test de boucle while ou la boucle elle-même qui pourrait le changer).Si vous avez entré la fonction alors qu'aucun caractère n'était en attente dans le matériel UART, vous pourriez vous retrouver dans une boucle infinie qui ne s'est jamais arrêtée même lorsqu'un caractère a été reçu. La lecture du registre de réception pourrait être déplacée par le compilateur avant la boucle while - encore une fois parce qu'il n'y a rien dans la fonction qui indique que *recv_reg est modifié par la boucle, il n'y a aucune raison pour qu'il ne puisse pas être lu avant d'entrer dans le boucle.

Les qualificateurs volatile garantissent que ces optimisations ne sont pas effectuées par le compilateur.

+0

+1 pour explication. Et j'ai une question: qu'en est-il des méthodes const volatiles? Si j'ai une classe à laquelle beaucoup de threads accèdent (bien que l'accès soit synchronisé avec mutex) mes méthodes const doivent aussi être volatiles (puisque certaines variables peuvent être changées par d'autres threads) – Sasa

6

J'avais besoin de l'utiliser dans une application embarquée où certaines variables de configuration se trouvent dans une zone de mémoire flash qui peut être mise à jour par un bootloader. Ces variables de configuration sont « constante » lors de l'exécution, mais sans le qualificatif volatile le compilateur d'optimiser quelque chose comme ça ...

cantx.id = 0x10<<24 | CANID<<12 | 0; 

... par précalcul la valeur constante et en utilisant une instruction de montage immédiate ou le chargement de la constante à partir d'un emplacement proche, de sorte que toute mise à jour de la valeur CANID d'origine dans la zone de configuration flash soit ignorée. CANID doit être const volatile.

3

const signifie que la variable ne peut pas être modifiée par le code c, pas qu'il ne peut pas changer. Cela signifie qu'aucune instruction ne peut écrire dans la variable, mais sa valeur peut encore changer. Signifie que la variable peut changer à tout moment et que, par conséquent, aucune valeur mise en cache ne peut être utilisée; chaque accès à la variable doit être exécuté à son adresse mémoire.

Puisque la question est étiquetée « embedded » et en supposant temp est une variable déclarée utilisateur, et non un registre concernant le matériel (puisque ceux-ci sont généralement traitées dans un fichier .h séparé), pensez à:

Un processeur embarqué qui possède à la fois une mémoire de données en lecture/écriture volatile (RAM) et une mémoire de données en lecture seule non volatile, par exemple FLASH dans l'architecture von-Neumann, où les données et l'espace programme partagent un bus de données et d'adresses commun. Si vous déclarez const temp pour avoir une valeur (au moins si elle est différente de 0), le compilateur affectera la variable à une adresse dans l'espace FLASH, car même si elle était affectée à une adresse RAM, elle a toujours besoin d'une mémoire FLASH pour stocker la valeur initiale de la variable, en faisant de l'adresse RAM une perte d'espace puisque toutes les opérations sont en lecture seule.

En conséquence:

int temp; est une variable stockée dans la RAM, initialisé à 0 au démarrage (cstart), les valeurs mises en mémoire cache peuvent être utilisés.

const int temp; est une variable stockée dans (en lecture seule) FLASH, initialisée à 0 au moment du compilateur, des valeurs en mémoire cache peuvent être utilisées.

volatile int temp;volatile int temp; est une variable stockée dans la RAM, initialisée à 0 au démarrage (cstart), les valeurs mises en cache NE seront PAS utilisées

const volatile int temp; est une variable stockée dans (lecture ony) FLASH, initialisé à 0 au moment du compilateur, les valeurs mises en cache ne seront pas utilisées

VIENT Voici la partie utile:

De nos jours, les processeurs les plus intégrés ont la Possibilité d'apporter des modifications à leur mémoire non volatile en lecture seule au moyen d'un module de fonction spécial, auquel cas const int temp peut être modifié à l'exécution, mais pas directement. Dit d'une autre manière, une fonction peut modifier la valeur à l'adresse où temp est stockée.

Un exemple pratique consisterait à utiliser temp pour le numéro de série du périphérique. La première fois que le processeur embarqué fonctionne, temp sera égal à 0 (ou la valeur déclarée) et une fonction peut utiliser ce fait pour effectuer un test pendant la production et si possible, demander à recevoir un numéro de série et modifier la valeur de temp au moyen d'une fonction spéciale. Certains processeurs ont une plage d'adresses spéciale avec une mémoire OTP (programmable une seule fois) juste pour cela.

Mais voici la différence:

Si const int temp est un ID modifiable au lieu d'un numéro de série programmable une seule fois et ne déclare pas volatile, une valeur mise en cache peut être utilisé jusqu'à ce que le prochain démarrage, ce qui signifie la nouvelle ID peut ne pas être valide jusqu'au prochain redémarrage, ou pire encore, certaines fonctions peuvent utiliser la nouvelle valeur alors que d'autres peuvent utiliser une ancienne valeur mise en cache jusqu'à redémarrage. Si const int temp est déclaré voltaile, le changement d'ID prend effet immédiatement.

1

Nous utilisons mot-clé « const » pour une variable lorsque nous ne voulons pas le programme pour le changer. Alors que lorsque nous déclarons une variable 'const volatile', nous disons au programme de ne pas la modifier et au compilateur que cette variable peut être modifiée de manière inattendue à partir d'une entrée provenant du monde extérieur.

+0

Cette réponse ne prend pas en compte le ' spécificateur volatile »du tout. –

4

En C, const et volatile sont des qualificatifs de type et ces deux sont indépendants.

Fondamentalement, const signifie que la valeur n'est pas modifiable par le programme. Et volatile signifie que la valeur est sujette à un changement soudain (peut-être de l'extérieur du programme).

En fait, le standard C mentionne un exemple de déclaration valide qui est à la fois const et volatile. L'exemple est

« extern const volatile int real_time_clock; »

où real_time_clock peut être modifiable par le matériel, mais ne peut pas être attribué à, incrémentée ou décrémentée.

Nous devrions donc déjà traiter séparément const et volatile. En outre, ces qualificatifs de type s'appliquent également à struct, union, enum et typedef.

2

Vous pouvez utiliser const et volatile ensemble.Par exemple, si 0x30 est supposée être la valeur d'un port qui est modifié par les conditions extérieures seulement, la déclaration suivante empêcherait toute possibilité d'effets secondaires accidentels:

const volatile char *port = (const volatile char *)0x30; 
Questions connexes