Inizializzatori in Linguaggio C
Gli inizializzatori in linguaggio C consentono di assegnare valori alle variabili fin dalla loro dichiarazione, migliorando la chiarezza del codice e riducendo potenziali errori.
In questa lezione esploreremo i diversi tipi di inizializzatori, le regole che governano la loro validità e le differenze tra variabili con durata di archiviazione automatica e statica.
Inizializzatori
Per comodità, il C ci permette di specificare valori iniziali per le variabili mentre le dichiariamo.
Abbiamo, infatti, visto nelle lezioni precedenti che per inizializzare una variabile, scriviamo il simbolo =
dopo il suo dichiaratore, seguito da un inizializzatore. (Non confondiamo il simbolo =
in una dichiarazione con l'operatore di assegnazione: l'inizializzazione non è la stessa cosa dell'assegnamento.)
Abbiamo già visto vari tipi di inizializzatori nelle lezioni precedenti. L'inizializzatore di una variabile semplice è un'espressione dello stesso tipo della variabile:
int i = 5 / 2; /* i è inizialmente 2 */
Se i tipi non corrispondono, il C converte l'inizializzatore usando le stesse regole dell'assegnamento:
int j = 5.5; /* convertito in 5 */
L'inizializzatore di una variabile puntatore deve essere un'espressione di puntatore dello stesso tipo della variabile o di tipo void *
:
int *p = &i;
L'inizializzatore per un array, una struttura o un'unione di solito è una serie di valori racchiusi tra parentesi graffe:
int a[5] = {1, 2, 3, 4, 5};
Inoltre, abbiamo visto che in C99 possiamo adoperare i cosiddetti inizializzatori designati:
int a[5] = {[2] = 3, [4] = 5};
/* è equivalente a */
int a[5] = {0, 0, 3, 0, 5};
Per completare la panoramica sulle dichiarazioni, esaminiamo ora alcune regole aggiuntive che governano gli inizializzatori:
-
Un inizializzatore per una variabile con durata di archiviazione statica deve essere costante:
#define FIRST 1 #define LAST 100 static int i = LAST - FIRST + 1;
Poiché
LAST
eFIRST
sono macro, il compilatore può calcolarne il valore iniziale (100 - 1 + 1 = 100). SeLAST
eFIRST
fossero state variabili, l'inizializzatore sarebbe stato illegale. -
Se una variabile ha durata di archiviazione automatica, il suo inizializzatore non deve essere necessariamente costante:
int f(int n) { int last = n - 1; ... }
-
Un inizializzatore racchiuso tra parentesi graffe per un array, una struttura o un'unione deve contenere solo espressioni costanti, mai variabili o chiamate di funzione:
#define N 2 int powers[5] = {1, N, N*N, N*N*N, N*N*N*N};
Poiché
N
è una costante, l'inizializzatore perpowers
è valido; seN
fosse una variabile, il programma non compilerebbe. In C99, questa restrizione vale solo se la variabile ha durata di archiviazione statica. -
L'inizializzatore per una struttura o union automatica può essere un'altra struttura o unione:
void g(struct part part1) { struct part part2 = part1; ... }
L'inizializzatore non deve per forza essere il nome di una variabile o di un parametro, ma deve essere un'espressione del tipo corretto. Ad esempio, l'inizializzatore di
part2
potrebbe essere*p
, dovep
è di tipostruct part *
, oppuref(part1)
, dovef
è una funzione che restituisce una struttura di tipopart
.
Variabili non Inizializzate
Nelle lezioni precedenti abbiamo visto che le variabili non inizializzate hanno valori indefiniti. Ciò perché potrebbero avere il valore di un'area di memoria precedentemente occupata, o potrebbero essere allocate in una parte della memoria che non è stata inizializzata. Tuttavia, è un errore comune pensare che le variabili non inizializzate siano sempre impostate a zero.
Non è sempre vero però: il valore iniziale di una variabile dipende dalla sua durata di archiviazione.
-
Le variabili con durata di archiviazione automatica non hanno un valore iniziale predefinito. Il valore iniziale di una variabile automatica non può essere previsto e può variare ogni volta che la variabile entra in esistenza.
-
Le variabili con durata di archiviazione statica assumono il valore zero di default. A differenza della memoria allocata con
calloc
, che semplicemente imposta i bit a zero, una variabile statica viene inizializzata correttamente in base al suo tipo: le variabili di tipo intero sono inizializzate a0
, quelle a virgola mobile a0.0
, mentre i puntatori contengono un puntatore nullo.
Dal punto di vista stilistico, è preferibile fornire inizializzatori per le variabili statiche piuttosto che fare affidamento sul fatto che siano garantite a zero. Se un programma accede a una variabile che non è stata esplicitamente inizializzata, chi legge il programma non può facilmente determinare se la variabile è effettivamente considerata zero o se viene inizializzata in un altro punto del programma.
In Sintesi
In questa lezione abbiamo visto che:
- Un inizializzatore viene specificato scrivendo
=
dopo il dichiaratore, seguito da un'espressione o da un elenco di valori (nel caso di array, strutture e unioni). - Le variabili con durata di archiviazione statica devono avere un inizializzatore costante, mentre quelle automatiche possono essere inizializzate con valori dinamici.
- In C99 è possibile usare gli inizializzatori designati e altre forme avanzate per inizializzare array e strutture in modo più flessibile.
- Variabili non inizializzate hanno comportamenti diversi a seconda della loro durata di archiviazione: quelle statiche sono azzerate, mentre quelle automatiche hanno un valore indeterminato.
- È buona pratica fornire sempre inizializzatori, specialmente per le variabili statiche, per evitare ambiguità e rendere chiaro il comportamento del programma.