Domanda

Che cosa fa l' volatile parola chiave fare?In C++ che problema si risolve?

Nel mio caso, non ho mai consapevolmente bisogno.

È stato utile?

Soluzione

volatile è necessario se si sta leggendo da un punto di memoria che, diciamo, completamente separato processo/dispositivo/qualsiasi cosa possa scrivere.

Ho usato per lavorare con doppia porta di ram in un sistema multiprocessore, in rettilineo C.Abbiamo utilizzato un hardware riuscito a 16 bit come un semaforo per sapere quando l'altro ragazzo è stato fatto.In sostanza abbiamo fatto questo:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

Senza volatile, l'ottimizzatore vede il loop inutile (Il ragazzo non tramonta mai il valore!Ha noci, sbarazzarsi di quel codice!) e il mio codice dovrebbe procedere senza aver acquisito il semaforo, causando problemi in seguito.

Altri suggerimenti

volatile è necessario durante lo sviluppo di sistemi embedded o driver di periferica, in cui è necessario per leggere o scrivere un mappati in memoria dei dispositivi hardware.Il contenuto di un particolare dispositivo di registrazione potrebbe cambiare in qualsiasi momento, quindi è necessario il volatile parola chiave per garantire che tali accessi non ottimizzati di distanza dal compilatore.

Alcuni processori hanno virgola mobile registri che hanno più di 64 bit di precisione (es.32-bit x86 senza SSE, vedi di Pietro, commento).In questo modo, se si esegue diverse operazioni su numeri a precisione doppia, effettivamente ottenere una maggiore precisione e risposta che se si dovesse troncare ogni risultato intermedio a 64 bit.

Questo di solito è grande, ma significa che a seconda di come il compilatore ricevuto registri e fatto ottimizzazioni avrai risultati diversi per lo stesso esatto operazioni la stessa ingressi.Se avete bisogno di coerenza è quindi possibile imporre ogni operazione di tornare alla memoria utilizzando la parola chiave volatile.

E ' anche utile per alcuni algoritmi che non algebrica senso, ma ridurre l'errore in virgola mobile, come Kahan sommatoria.Algebraicly è un nop, così spesso in modo errato ottimizzato a meno che alcune variabili sono volatili.

Da un "Volatile come una promessa" articolo di Dan Saks:

(...) un volatile oggetto è uno il cui valore può cambiare spontaneamente.Che è, quando si dichiara un oggetto per essere volatile, si indica al compilatore che l'oggetto potrebbe cambiare stato, anche se non dichiarazioni in programma sembrano cambiare."

Qui ci sono i link a tre dei suoi articoli riguardanti la volatile parola chiave:

È NECESSARIO utilizzare volatile quando attuano il blocco strutture di dati.In caso contrario il compilatore è libero di ottimizzare l'accesso alla variabile, che cambia la semantica.

Per dirla in altro modo, volatile indica al compilatore che accede a questa variabile deve corrispondere ad una memoria fisica operazione di lettura/scrittura.

Per esempio, questo è come InterlockedIncrement è dichiarata nel Win32 API:

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);

Una grande applicazione che ho usato per lavorare nei primi anni 1990 contenuti C-base di gestione delle eccezioni utilizzando setjmp e longjmp.La parola chiave volatile è stato necessario su variabili i cui valori necessari per essere conservato nel blocco di codice, che è servita come "catturare" la clausola, per paura di coloro vars essere memorizzati in registri e spazzata via dal longjmp.

In C Standard, uno dei luoghi di utilizzare volatile è con un gestore di segnale.Infatti, in C Standard, il tutto si può tranquillamente fare in un gestore di segnale è modificare un volatile sig_atomic_t variabile, o di uscire in fretta.Infatti, per quanto ne so, è l'unico posto in C Standard che l'uso di volatile è necessario per evitare comportamenti indefiniti.

ISO/IEC 9899:2011 §7.14.1.1 Il signal funzione

¶5 Se il segnale si presenta diversa da come risultato della chiamata abort o raise funzione, il il comportamento è indefinito se il gestore di segnale si riferisce a qualsiasi oggetto statico o thread durata di conservazione che non è un blocco atomico, senza oggetto, diverso da assegnare un valore ad un oggetto dichiarata come volatile sig_atomic_t, o il gestore di segnale chiamate qualsiasi funzione nella libreria standard diverso abort funzione, il _Exit funzione, il quick_exit funzione, o il signal funzione con il primo argomento pari al numero di segnale corrispondente al segnale che ha causato l'invocazione del gestore.Inoltre, se una tale chiamata per il signal risultato di una funzione in un SIG_ERR ritorno, la valore di errno è indeterminato.252)

252) Se il segnale è generato da un asincrono gestore di segnale, il comportamento è indefinito.

Il che significa che in C Standard, si può scrivere:

static volatile sig_atomic_t sig_num = 0;

static void sig_handler(int signum)
{
    signal(signum, sig_handler);
    sig_num = signum;
}

e non molto altro.

POSIX è molto più indulgente su cosa si può fare in un gestore di segnale, ma ci sono comunque delle limitazioni (e uno dei limiti è che lo Standard di I/O biblioteca — printf() et al — non può essere utilizzato in modo sicuro).

In via di sviluppo per un embedded, ho un ciclo che controlli una variabile che può essere cambiato in un gestore di interrupt.Senza "volatile", il ciclo diventa un noop - per quanto riguarda il compilatore può dire, la variabile non cambia mai, così si ottimizza il controllo di distanza.

Stessa cosa si applica ad una variabile che può essere cambiato in un altro thread più in un ambiente tradizionale, ma ci siamo spesso fare le chiamate di sincronizzazione, in modo che il compilatore non è in modo libero, con l'ottimizzazione.

Io l'ho utilizzato build di debug quando il compilatore insiste sull'ottimizzazione di distanza una variabile che voglio essere in grado di vedere come io passo attraverso il codice.

Oltre a usarlo come previsto, volatile è utilizzato nel template metaprogramming.Può essere usato per prevenire il sovraccarico accidentale, come il volatile attributo (come const) prende parte la risoluzione dell'overload.

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

Questo è legale;i sovraccarichi sono potenzialmente callable e di fare quasi lo stesso.Il cast volatile il sovraccarico è legale come sappiamo bar non passerà un non-volatile T comunque.Il volatile la versione è strettamente peggio, però, quindi mai scelto risoluzione di sovraccarico se non volatile f è disponibile.

Si noti che il codice non dipenda in realtà volatile l'accesso alla memoria.

  1. è necessario utilizzare per implementare spinlock così come alcune (tutte?) blocco strutture di dati
  2. utilizzare con operazioni atomiche/istruzioni
  3. mi ha aiutato una volta a superare compilatore bug (erroneamente il codice generato durante la fase di ottimizzazione)

Il volatile la parola chiave è destinato per impedire che il compilatore di applicare eventuali ottimizzazioni su oggetti che possono cambiare in modi che non possono essere determinati dal compilatore.

Oggetti dichiarati come volatile sono esclusi dall'ottimizzazione perché i loro valori possono essere cambiati dal codice al di fuori dell'ambito di applicazione del codice corrente in qualsiasi momento.Il sistema legge il valore corrente di un volatile oggetto dalla posizione di memoria piuttosto che mantenere il suo valore nel registro temporaneo presso il punto è richiesto, anche se una precedente istruzione richiesto, per un valore dall'oggetto stesso.

Considerare i seguenti casi

1) le variabili Globali modificato da una routine di servizio di interrupt al di fuori del campo di applicazione.

2) le variabili Globali all'interno di un multi-threaded applicazione.

Se non usiamo il qualificatore volatile, si verifichino i seguenti problemi

1) il Codice potrebbe non funzionare come previsto quando ottimizzazione è acceso.

2) il Codice potrebbe non funzionare come previsto quando gli interrupt sono attivata e utilizzata.

Volatile:Un programmatore migliore amico

https://en.wikipedia.org/wiki/Volatile_(computer_programming)

Al di là del fatto che la parola chiave volatile è utilizzato per indicare al compilatore di non ottimizzare l'accesso ad alcune variabili (che può essere modificato da un filo o una routine di interrupt), può essere anche utilizzato per rimuovere alcuni bug del compilatore -- SÌ, si può essere ---.

Per esempio, ho lavorato su di una piattaforma embedded sono stati il compilatore stava facendo del male, assuptions per quanto riguarda il valore di una variabile.Se il codice non è stato ottimizzato il programma sarebbe ok.Con le ottimizzazioni (che sono stati davvero bisogno, perché è stato un critico di routine) il codice non funziona correttamente.L'unica soluzione (anche se non molto corretto) era di dichiarare il 'guasto' variabile come volatile.

Il programma sembra funzionare anche senza volatile parola chiave?Forse questo è il motivo:

Come accennato in precedenza, il volatile parola chiave aiuta in casi come

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

Ma sembra che ci sia quasi nessun effetto una volta che un esterno o non in linea funzione viene chiamata.E. g.:

while( *p!=0 ) { g(); }

Quindi con o senza volatile quasi lo stesso risultato viene generato.

Fintanto che g() può essere completamente sostituite, il compilatore è in grado di vedere tutto quello che sta succedendo e quindi può ottimizzare.Ma quando il programma effettua una chiamata a un luogo in cui il compilatore non può vedere cosa sta succedendo, non è sicuro per il compilatore di fare qualsiasi ipotesi più.Quindi il compilatore genera il codice che legge sempre dalla memoria direttamente.

Ma attenzione a quando un giorno, la funzione g() diventa in linea (sia a causa esplicito o modifiche a causa del compilatore/linker bravura) quindi il tuo codice potrebbe rompere se hai dimenticato la volatile parola chiave!

Quindi vi consiglio di aggiungere il volatile parola chiave, anche se il programma sembra funzionare senza.Rende l'intenzione chiara e più robusto rispetto ai futuri cambiamenti.

Nei primi giorni di C, i compilatori avrebbero interpretare tutte le operazioni di lettura e scrittura lvalues come le operazioni di memoria, per essere eseguita nella stessa sequenza in cui la legge e scrive apparso nel codice.L'efficienza può essere migliorata notevolmente, in molti casi, se i compilatori hanno dato una certa libertà di ri-ordine e consolidare le operazioni, ma c'era un problema con questo.Anche le operazioni sono state spesso specificato in un certo ordine, semplicemente perché è stato necessario specificare nella alcuni ordine e, quindi, il programmatore ha scelto uno dei molti altrettanto buone alternative, che non era sempre il caso.A volte sarebbe importante che certe operazioni si svolgono in una particolare sequenza.

Esattamente che i dettagli di sequenziamento sono importanti varierà a seconda della piattaforma di destinazione e campo di applicazione.Piuttosto che fornire particolarmente dettagliata controllo, Standard optato per un modello semplice:se una sequenza di accessi sono fatto con lvalues che non sono qualificati volatile, un compilatore può riorganizzare e consolidare la loro, come si vede in forma.Se un'azione è fatto con un volatilequalificati lvalue, una implementazione di qualità dovrebbe offrire qualsiasi ulteriore ordine le garanzie possono essere richieste dal codice di targeting per le sue inteso piattaforma e campo di applicazione, senza dover richiedere l'uso di non-standard di sintassi.

Purtroppo, invece di identificare ciò che garantisce i programmatori avrebbero bisogno, molti compilatori hanno, invece, scelto di offrire il minimo di garanzie imposti dalla Norma.Questo rende volatile molto meno utile di quanto dovrebbe essere.Su gcc o clang, per esempio, un programmatore di dover implementare una base di "hand-off mutex" [quella in cui un compito che ha acquisito e pubblicato un mutex non farlo di nuovo fino a quando l'operazione ha fatto in modo che] deve fare quattro cose:

  1. Mettere l'acquisizione e il rilascio del mutex in una funzione che il compilatore non è in linea, e per i quali non è possibile applicare a Tutto il Programma di Ottimizzazione.

  2. Qualificare tutti gli oggetti custoditi da mutex come volatile- qualcosa che non dovrebbe essere necessario se tutti gli accessi si verificano dopo aver acquisito il mutex e prima di rilasciarla.

  3. Utilizzare il livello di ottimizzazione da 0 a forzare il compilatore a generare il codice come se tutti gli oggetti che non sono qualificati register sono volatile.

  4. Utilizzare gcc-direttive specifiche.

Al contrario, quando si utilizza una qualità superiore compilatore che è più adatto per i sistemi di programmazione, come l'icc, sarebbe un'altra opzione:

  1. Assicurarsi che un volatilequalificati per la scrittura viene eseguita everyplace un'acquisizione o di rilascio è necessario.

L'acquisizione di una base di "hand-off mutex" richiede un volatile leggere (per vedere se è pronto), e non dovrebbe essere necessario un volatile scrivere così (l'altro lato non provare a ri-acquisire esso fino a quando è tornata), ma di dover eseguire un significato volatile scrivere è sempre meglio di una qualsiasi delle opzioni di gcc o clang.

Un uso devo ricordare è che, il gestore del segnale funzione, se si desidera accedere/modificare una variabile globale (ad esempio, il marchio di uscita = true) dichiarare che la variabile 'volatili'.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top