Qualificatori di Tipo in Linguaggio C
I Qualificatori di Tipo in linguaggio C sono utilizzati per specificare come un valore possa modificarsi nel tempo.
In Linguaggio C ne esistono quattro:
const
volatile
restrict
_Atomic
Il qualificatore restrict
lo abbiamo già studiato quando abbiamo affrontato i puntatori limitati o puntatori restricted e, infatti, si applica solo ai puntatori.
Il qualificatore volatile
si usa per la programmazione di basso livello e lo studieremo nelle prossime lezioni. Mentre, il qualificatore _Atomic
si usa per la programmazione concorrente e anche il suo studio è rimandato a lezioni successive.
In questa lezione ci concentreremo sul qualificatore const
e sul suo utilizzo.
Qualificatore const
Il qualificatore const
posto davanti la dichiarazione di una variabile consente di definire entità che rassomigliano ad una variabile ma che in realtà possono essere accedute in sola lettura. In altre parole, un programma può accedere al valore di un oggetto const
ma non ne può modificare il contenuto.
Prendiamo l'esempio che segue:
const int numero = 10;
In questo caso abbiamo creato un oggetto const
di tipo int
chiamato numero
e gli abbiamo assegnato il valore 10
. Questo significa che il valore di numero
non può essere modificato nel corso dell'esecuzione del programma.
Analogamente, possiamo creare un array di const
:
const int numeri[] = {1, 2, 3, 4, 5};
In questo caso abbiamo creato un array di const
di tipo int
chiamato numeri
e gli abbiamo assegnato i valori {1, 2, 3, 4, 5}
. Anche in questo caso, il contenuto dell'array numeri
non può essere modificato.
Dichiarare un oggetto come const
fornisce una serie di vantaggi:
-
Leggibilità del codice:
il qualificatore
const
indica che il valore di un oggetto non cambierà nel corso del programma, rendendo il codice più leggibile e più facile da manutenere. Inoltre, uno sviluppatore che legge il codice può capire immediatamente che un oggetto è destinato a rimanere invariato. -
Prevenzione di errori:
il qualificatore
const
previene la modifica accidentale di un oggetto. Se un programma tenta di modificare un oggettoconst
, il compilatore genererà un errore. -
Ottimizzazione del codice:
il qualificatore
const
consente al compilatore di effettuare ottimizzazioni del codice. Ad esempio, il compilatore può memorizzare un oggettoconst
in una posizione di memoria diversa rispetto ad un oggetto nonconst
, poiché sa che il valore di un oggettoconst
non cambierà. -
Programmazione Embedded:
il qualificatore
const
è particolarmente utile nella programmazione embedded, dove la memoria è limitata e la prevenzione di errori è fondamentale. Inoltre, il qualificatoreconst
può essere utilizzato per memorizzare costanti in memoria di sola lettura (ROM). Ad esempio, quando si sviluppa per piattaforme come Arduino, è comune utilizzare il qualificatoreconst
per definire costanti che vengono memorizzate nella EEPROM o nella memoria Flash piuttosto che nella RAM.
Ricapitolando:
Qualificatore const
in Linguaggio C
Il qualificatore const
in linguaggio C consente di definire entità che possono essere accedute in sola lettura. Le variabili const
non possono essere modificate nel corso dell'esecuzione del programma.
La sintassi per adoperare il qualificatore const
è la seguente:
const tipo nome = valore;
dove tipo
è il tipo di dato dell'oggetto, nome
è il nome dell'oggetto e valore
è il valore iniziale dell'oggetto.
Un oggetto const
deve essere sempre inizializzato
Un oggetto const
deve essere sempre inizializzato al momento della dichiarazione:
const int numero = 10; // Corretto
In realtà, è possibile dichiarare un oggetto const
senza inizializzarlo. Tuttavia, questa pratica non solo ha poco senso, ma la conseguenza è che l'oggetto non può essere modificato. Per cui, il valore che conterrà sarà completamente casuale.
Ad esempio, se scriviamo un programma simile:
#include <stdio.h>
int main() {
/* Non Inizializzato */
const int numero;
printf("Numero: %d\n", numero);
return 0;
}
Il programma potrebbe stampare un valore casuale, come 0
, 1
, -1
, ecc. Questo perché l'oggetto numero
non è stato inizializzato e il suo valore è indefinito.
Differenza tra const
e #define
Abbiamo visto in precedenza che nel codice scritto in C possiamo definire delle costanti attraverso la direttiva di precompilazione #define
.
Sorge spontanea a questo punto la domanda: perché fornire due meccanismi diversi per definire delle costanti?
Il fatto è che esistono delle sostanziali differenze tra l'uso del qualificatore const
e la direttiva #define
:
- La direttiva
#define
può essere adoperata per creare costanti di tipo numerico, carattere o stringa. Mentre, il qualificatoreconst
può essere utilizzato per creare costanti di qualunque tipo: array, strutture dati, puntatori e union. -
Le costanti definite con
#define
sono sostituite dal preprocessore con il valore definito. Mentre, le costanti definite conconst
sono memorizzate in memoria e possono essere trattate come variabili normali.La conseguenza è che gli oggetti
const
sono soggetti alle stesse regole di visibilità a cui sono soggette le variabili. Ad esempio, un oggettoconst
dichiarato all'interno di una funzione è visibile solo all'interno di quella funzione. Mentre, una costante definita con#define
è visibile in tutto il file sorgente. -
Il valore di un oggetto
const
può essere visto dal debugger, mentre il valore di una costante definita con#define
no. - Le costanti definite con
#define
non sono tipizzate. Mentre, le costanti definite conconst
sono tipizzate e il compilatore può effettuare controlli di tipo. - Si può applicare l'operatore indirizzo
&
per ottenere l'indirizzo di un oggettoconst
in quanto esso occupa effettivamente spazio in memoria. Mentre, non si può applicare l'operatore indirizzo a una costante definita con#define
in quanto essa non occupa spazio in memoria.
Oltre queste differenze, ne esiste una fondamentale che riguarda l'uso degli oggetti const
in espressioni costanti.
Nello standard C89, non si può adoperare un oggetto const
in un'espressione costante. Per cui, ad esempio, il codice che segue non è valido:
#define N 10
const int M = 10;
/* Valido */
int a[N];
/* NON Valido in C89 */
int b[M];
In questo esempio, N
è una costante definita con #define
e M
è un oggetto const
. In C89, N
può essere utilizzato per definire la dimensione di un array, mentre M
no.
Nello standard C99 questo limite è stato rilassato. Quindi si può adoperare un oggetto const
in un'espressione costante purché l'oggetto const
abbia come classe di storage auto
, ossia abbia durata di vita automatica.
Quindi, in C99, il seguente codice diventa valido:
static const int N = 10;
void funzione() {
const int M = 10;
/* Valido in C99 */
int a[M];
/* NON Valido in C99 */
int b[N];
}
In questo esempio, N
è un oggetto const
con classe di storage static
, mentre M
è un oggetto const
con classe di storage auto
. In C99, M
può essere utilizzato per definire la dimensione di un array, mentre N
no.
In generale, non esiste una regola definitiva da seguire per scegliere se usare const
oppure #define
per definire delle costanti. La scelta dipende dal contesto e dalle esigenze specifiche del programma.
Una regola di massima è quella di adoperare costanti definite con la direttiva #define
quando si tratta di costanti numeriche o di stringhe semplici. Questo perché, così facendo, possiamo usare le costanti nella definizione della dimensione di array, nelle istruzioni switch
e in tutti quei casi in cui sono richieste espressioni costanti.
In Sintesi
In questa lezione abbiamo visto come utilizzare il qualificatore const
in linguaggio C per definire costanti. Le costanti definite con const
sono oggetti che possono essere acceduti in sola lettura e non possono essere modificati nel corso dell'esecuzione del programma.
Abbiamo anche confrontato l'uso del qualificatore const
con la direttiva #define
per definire costanti e abbiamo visto le differenze tra i due approcci.
Il qualificatore const
è uno strumento potente che può migliorare la leggibilità del codice, prevenire errori e ottimizzare il codice. È particolarmente utile nella programmazione embedded e in situazioni in cui è necessario definire costanti di qualunque tipo.
Inoltre, abbiamo visto che in C99 è possibile utilizzare un oggetto const
in un'espressione costante purché l'oggetto const
abbia come classe di storage auto
.
In conclusione, il qualificatore const
è uno strumento fondamentale per la scrittura di codice C robusto e manutenibile.