Ricerca…


Osservazioni

I qualificatori di tipo sono le parole chiave che descrivono una semantica aggiuntiva su un tipo. Sono parte integrante delle firme dei tipi. Possono apparire sia al livello più alto di una dichiarazione (che influisce direttamente sull'identificatore) sia a sottolivelli (rilevanti solo per i puntatori che influiscono sui valori puntati):

Parola chiave Osservazioni
const Impedisce la mutazione dell'oggetto dichiarato (apparendo al livello più alto) o impedisce la mutazione del valore puntato (apparendo accanto a un sottotipo di puntatore).
volatile Informa il compilatore che l'oggetto dichiarato (al livello più alto) o il valore puntato (nei sottotipi del puntatore) può cambiare il suo valore come risultato di condizioni esterne, non solo come risultato del flusso di controllo del programma.
restrict Un suggerimento per l'ottimizzazione, pertinente solo ai puntatori. Dichiara l'intento che per tutta la durata del puntatore, non verranno utilizzati altri puntatori per accedere allo stesso oggetto puntato.

L'ordinamento di tipo qualificatori rispetto a specificatori della classe di memorizzazione ( static , extern , auto , register ), tipo di modificatori ( signed , unsigned , short , long ) e specificatori di tipo ( int , char , double , ecc) non è applicata, ma la buona pratica è metterli nell'ordine di cui sopra:

static const volatile unsigned long int a = 5; /* good practice */
unsigned volatile long static int const b = 5; /* bad practice */

Qualifiche di alto livello

/* "a" cannot be mutated by the program but can change as a result of external conditions */
const volatile int a = 5;

/* the const applies to array elements, i.e. "a[0]" cannot be mutated */    
const int arr[] = { 1, 2, 3 };

/* for the lifetime of "ptr", no other pointer could point to the same "int" object */
int *restrict ptr;

Qualifiche del sottotipo di puntatore

/* "s1" can be mutated, but "*s1" cannot */
const char *s1 = "Hello";

/* neither "s2" (because of top-level const) nor "*s2" can be mutated */
const char *const s2 = "World";

/* "*p" may change its value as a result of external conditions, "**p" and "p" cannot */
char *volatile *p;

/* "q", "*q" and "**q" may change their values as a result of external conditions */
volatile char *volatile *volatile q;

Variabili (const) non modificabili

const int a = 0; /* This variable is "unmodifiable", the compiler
                    should throw an error when this variable is changed */
int b = 0; /* This variable is modifiable */

b += 10; /* Changes the value of 'b' */
a += 10; /* Throws a compiler error */

La qualifica const significa solo che non abbiamo il diritto di modificare i dati. Ciò non significa che il valore non può cambiare alle nostre spalle.

_Bool doIt(double const* a) {
   double rememberA = *a;
   // do something long and complicated that calls other functions

   return rememberA == *a;
}

Durante l'esecuzione delle altre chiamate *a potrebbe essersi verificato un cambiamento, e quindi questa funzione potrebbe tornare false o true .

avvertimento


Le variabili con qualifica const possono essere modificate usando i puntatori:

const int a = 0;

int *a_ptr = (int*)&a; /* This conversion must be explicitly done with a cast */
*a_ptr += 10;          /* This has undefined behavior */

printf("a = %d\n", a); /* May print: "a = 10" */

Ma farlo è un errore che porta a comportamenti non definiti. La difficoltà qui è che questo può comportarsi come previsto in semplici esempi come questo, ma poi andare male quando il codice cresce.

Variabili volatili

La parola chiave volatile indica al compilatore che il valore della variabile può cambiare in qualsiasi momento come risultato di condizioni esterne, non solo come risultato del flusso di controllo del programma.

Il compilatore non ottimizzerà tutto ciò che ha a che fare con la variabile volatile.

volatile int foo; /* Different ways to declare a volatile variable */
int volatile foo;

volatile uint8_t * pReg; /* Pointers to volatile variable */
uint8_t volatile * pReg;

Ci sono due ragioni principali per utilizzare variabili volatili:

  • Interfaccia con hardware con registri I / O mappati in memoria.
  • Quando si utilizzano variabili che vengono modificate al di fuori del flusso di controllo del programma (ad esempio, in una routine di servizio di interrupt)

Vediamo questo esempio:

int quit = false;

void main() 
{
    ... 
    while (!quit) {
      // Do something that does not modify the quit variable
    } 
    ...
}

void interrupt_handler(void) 
{
  quit = true;
}

Il compilatore può notare che il ciclo while non modifica la variabile quit e converte il ciclo in un ciclo while (true) senza fine. Anche se la variabile quit è impostata sul gestore di segnale per SIGINT e SIGTERM , il compilatore non lo sa.

Dichiarare quit come volatile dirà al compilatore di non ottimizzare il ciclo e il problema sarà risolto.

Lo stesso problema si verifica quando si accede all'hardware, come vediamo in questo esempio:

uint8_t * pReg = (uint8_t *) 0x1717;

// Wait for register to become non-zero 
while (*pReg == 0) { } // Do something else

Il comportamento dell'ottimizzatore è di leggere il valore della variabile una volta, non c'è bisogno di rileggerlo, poiché il valore sarà sempre lo stesso. Quindi finiamo con un ciclo infinito. Per forzare il compilatore a fare ciò che vogliamo, modifichiamo la dichiarazione in:

uint8_t volatile * pReg = (uint8_t volatile *) 0x1717;


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow