Differenza tra const e const volatile

Se dichiariamo una variabile come volatile ogni volta che viene aggiornato il valore fresco
Se dichiariamo una variabile come const il valore di quella variabile non verrà modificato

Quindi const volatile int temp;
A cosa serve dichiarare la variabile temp come sopra?
Cosa succede se dichiariamo come const int temp ?

Un object contrassegnato come const volatile non potrà essere modificato dal codice (verrà generato un errore a causa del qualificatore const ) – almeno attraverso quel particolare nome / puntatore.

La parte volatile del qualificatore significa che il compilatore non può ottimizzare o riordinare l’accesso all’object.

In un sistema embedded, questo viene in genere utilizzato per accedere ai registri hardware che possono essere letti e aggiornati dall’hardware, ma non ha senso scrivere su (o potrebbe essere un errore scrivere in).

Un esempio potrebbe essere il registro di stato per una porta seriale. Vari bit indicheranno se un personaggio è in attesa di essere letto o se il registro di trasmissione è pronto ad accettare un nuovo carattere (es., – è vuoto). Ogni lettura di questo registro di stato potrebbe dare un valore diverso a seconda di cosa si è verificato nell’hardware della porta seriale.

Non ha senso scrivere nel registro di stato (a seconda delle particolari specifiche hardware), ma è necessario assicurarsi che ogni lettura del registro si traduca in una lettura effettiva dell’hardware – utilizzando un valore memorizzato nella cache da una precedente lettura vinta ‘ Ti spiegherò i cambiamenti nello stato dell’hardware.

Un rapido esempio:

 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; } 

Se questi indicatori non sono stati contrassegnati come volatile , potrebbero verificarsi alcuni problemi:

  • il test del ciclo while poteva leggere il registro di stato solo una volta, dal momento che il compilatore poteva assumere che qualsiasi cosa indicasse non sarebbe mai cambiato (non c’è nulla nel test del ciclo while o loop stesso che potrebbe cambiarlo). Se hai inserito la funzione quando non c’era nessun carattere in attesa nell’hardware UART, potresti finire in un ciclo infinito che non si è mai fermato nemmeno quando è stato ricevuto un personaggio.
  • la lettura del registro di ricezione potrebbe essere spostata dal compilatore prima del ciclo while – di nuovo perché non c’è nulla nella funzione che indica che *recv_reg è cambiato dal ciclo, non c’è motivo per cui non possa essere letto prima di entrare nel ciclo.

I qualificatori volatile assicurano che queste ottimizzazioni non vengano eseguite dal compilatore.

  • volatile dirà al compilatore di non ottimizzare il codice relativo alla variabile, di solito quando sappiamo che può essere modificato da “esterno”, ad esempio da un altro thread.
  • const dirà al compilatore che è vietato al programma modificare il valore della variabile.
  • const volatile è una cosa molto speciale che probabilmente vedrai usata esattamente 0 volte nella tua vita ™. Come ci si può aspettare, significa che il programma non può modificare il valore della variabile, ma il valore può essere modificato dall’esterno, quindi non verranno eseguite ottimizzazioni sulla variabile.

Non è perché la variabile è const che potrebbe non essere cambiata tra due punti di sequenza.

Constness è una promise che fai di non cambiare il valore, non che il valore non sarà cambiato.

Ho avuto bisogno di usarlo in un’applicazione embedded dove alcune variabili di configurazione si trovano in un’area di memoria flash che può essere aggiornata da un bootloader. Queste variabili di configurazione sono “costanti” durante il runtime, ma senza il qualificatore volatile il compilatore ottimizzerà qualcosa del genere …

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

... precalcolando il valore costante e utilizzando un'istruzione di assemblaggio immediata o caricando la costante da una posizione vicina, in modo tale che qualsiasi aggiornamento del valore CANID originale nell'area di configurazione flash venga ignorato. CANID deve essere const volatile.

In C, const e volatile sono qualificatori di tipo e questi due sono indipendenti.

In sostanza, const significa che il valore non è modificabile dal programma.

E volatile significa che il valore è sobject a cambiamenti improvvisi (possibilmente al di fuori del programma).

In effetti, lo standard C menziona un esempio di dichiarazione valida che è sia const sia volatile. L’esempio è

“Extern const volatile int real_time_clock;”

dove real_time_clock può essere modificabile dall’hardware, ma non può essere assegnato a, incrementato o decrementato.

Quindi dovremmo trattare separatamente const e volatile. Inoltre, questo tipo di qualificatore vale anche per struct, union, enum e typedef.

const significa che la variabile non può essere modificata dal codice c, non che non può essere modificata. Significa che nessuna istruzione può scrivere sulla variabile, ma il suo valore potrebbe ancora cambiare.

volatile significa che la variabile può cambiare in qualsiasi momento e quindi non possono essere usati valori memorizzati nella cache; ogni accesso alla variabile deve essere eseguito nel suo indirizzo di memoria.

Poiché la domanda è contrassegnata come “incorporata” e supponiamo che temp sia una variabile dichiarata dall’utente, non un registro relativo all’hardware (poiché questi sono generalmente gestiti in un file .h separato), considerare:

Un processore embedded che dispone sia di memoria volatile di dati in lettura e scrittura (RAM) che di memoria dati non volatile di sola lettura, ad esempio memoria FLASH nell’architettura von-Neumann, in cui i dati e lo spazio programma condividono un bus dati e indirizzi comune.

Se dichiari const temp valore per avere un valore (almeno se diverso da 0), il compilatore assegnerà la variabile a un indirizzo nello spazio FLASH, perché anche se fosse assegnato a un indirizzo RAM, ha ancora bisogno di memoria FLASH per memorizzare il valore iniziale della variabile, rendendo l’indirizzo RAM uno spreco di spazio poiché tutte le operazioni sono di sola lettura.

Conseguentemente:

int temp; è una variabile memorizzata nella RAM, inizializzata a 0 all’avvio (cstart), i valori memorizzati possono essere usati.

const int temp; è una variabile memorizzata in FLASH (read-ony), inizializzata a 0 al momento del compilatore, possono essere usati valori memorizzati nella cache.

volatile int temp; è una variabile memorizzata nella RAM, inizializzata a 0 all’avvio (cstart), i valori memorizzati nella cache NON verranno utilizzati.

const volatile int temp; è una variabile memorizzata in FLASH (read-ony), inizializzata a 0 al momento del compilatore, i valori memorizzati nella cache NON saranno usati

Ecco la parte utile:

Oggigiorno la maggior parte dei processori embedded ha la possibilità di apportare modifiche alla propria memoria non volatile di sola lettura tramite un modulo funzione speciale, nel qual caso const int temp può essere modificato in fase di runtime, anche se non direttamente. Detto in altro modo, una funzione può modificare il valore all’indirizzo in cui è memorizzato il temp .

Un esempio pratico potrebbe essere l’utilizzo di temp per il numero di serie del dispositivo. La prima volta che viene eseguito il processore incorporato, temp sarà uguale a 0 (o il valore dichiarato) e una funzione può utilizzare questo fatto per eseguire un test durante la produzione e, se riesce, chiedere di ricevere un numero seriale e modificare il valore di temp per mezzo di una funzione speciale. Alcuni processori hanno uno speciale intervallo di indirizzi con memoria OTP (programmabile una sola volta) solo per quello.

Ma qui arriva la differenza:

Se const int temp è un ID modificabile invece di un numero seriale programmabile una sola volta e NON è dichiarato volatile , è ansible utilizzare un valore memorizzato nella cache fino al prossimo avvio, il che significa che il nuovo ID potrebbe non essere valido fino al successivo riavvio, o addirittura peggio, alcune funzioni potrebbero utilizzare il nuovo valore mentre altre potrebbero utilizzare un valore memorizzato nella cache precedente fino al riavvio. Se const int temp IS dichiarato voltaile , la modifica dell’ID avrà effetto immediato.

In questo articolo vengono descritti gli scenari in cui si desidera combinare qualificatori const e volatili.

http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/

Puoi usare const e volatile insieme. Ad esempio, se si assume che 0x30 sia il valore di una porta modificata solo da condizioni esterne, la seguente dichiarazione impedirebbe qualsiasi possibilità di effetti collaterali accidentali:

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

Usiamo la parola chiave ‘const’ per una variabile quando non vogliamo che il programma lo cambi. Mentre quando dichiariamo una variabile ‘const volatile’ stiamo dicendo al programma di non cambiarlo e al compilatore che questa variabile può essere cambiata inaspettatamente da input provenienti dal mondo esterno.

In termini semplici, il valore nella variabile ‘const volatile’ non può essere modificato a livello di codice ma può essere modificato dall’hardware. Volatile qui è quello di prevenire l’ottimizzazione del compilatore.