Dichiarazioni e Loro Sintassi in Linguaggio C
Le Dichiarazioni ricoprono un ruolo fondamentale nel linguaggio C. Dichiarando una variabile o una funzione, si forniscono delle informazioni fondamentali al compilatore che le può usare per controllare potenziali errori e per generare codice più efficiente.
Nelle lezioni precedenti abbiamo visto esempi di dichiarazioni di variabili e di funzioni ma senza entrare troppo nel dettaglio. A partire da questa lezione, vedremo più in dettaglio le dichiarazioni e la loro sintassi.
Utilizzeremo i concetti di questa lezione come punto di partenza per affrontare, poi, concetti più avanzati come le classi di storage e i qualificatori di tipo.
Sintassi delle Dichiarazioni
Una dichiarazione in linguaggio C può essere vista come un'istruzione non eseguibile che fornisce informazioni al compilatore riguardo ad un particolare identificatore.
Ad esempio, quando scriviamo:
float x;
stiamo in sostanza dicendo al compilatore che, all'interno dello scope corrente, il nome x
rappresenta una variabile di tipo float
.
Analogamente, quando scriviamo ad esempio:
int somma(int a, int b);
stiamo dichiarando una funzione di nome somma
che accetta due parametri di tipo int
e restituisce un valore di tipo int
.
In generale, una dichiarazione ha la forma seguente:
specificatori dichiaratori;
Dove:
specificatori
, chiamati anche specificatori della dichiarazione, sono una lista di parole chiave che descrivono le proprietà della variabile o funzione che stiamo dichiarando;dichiaratori
, chiamati anche dichiaratori di base, sono una lista di identificatori separati da virgole. I dichiaratori possono fornire anche informazioni aggiuntive circa le proprietà.
Analizziamoli separatamente.
Specificatori di Dichiarazione
Gli specificatori della dichiarazione possono essere suddivisi in tre tipi:
-
Classi di Storage:
chiamati anche classi di memorizzazione, specificano la durata di vita e la visibilità di un'entità.
In Linguaggio C esistono cinque classi di storage. Inoltre, solo una classe di storage può essere specificata per ogni dichiarazione:
auto
register
static
extern
thread_local
-
Qualificatori di Tipo:
specificano come il valore di una certa variabile può modificarsi nel tempo. Ne esistono quattro ed una dichiarazione ne può contenere da zero a più di uno:
const
volatile
restrict
_Atomic
In particolare, il terzo,
restrict
, è stato introdotto nello standard C99, mentre l'ultimo,_Atomic
, è stato introdotto nello standard C11. -
Specificatori di Tipo:
specificano il tipo di dato della variabile o della funzione che stiamo dichiarando. Ne esistono diversi, tra cui:
void
char
int
float
double
short
long
signed
unsigned
Inoltre, è possibile definire tipi di dato personalizzati utilizzando le strutture, le unioni e le enumerazioni.
Gli specificatori di tipo possono essere combinati tra di loro come già abbiamo visto in precedenza. Inoltre l'ordine in cui appaiono non è rilevante, ad esempio le dichiarazioni:
int unsigned long x; unsigned long int x;
sono equivalenti.
Inoltre, possiamo anche usare nomi di tipo specificati con
typedef
.
Nello standard C99 è stato aggiunto anche uno speciale specificatore di dichiarazione che viene adoperato esclusivamente per le funzioni: inline
. Lo approfondiremo nelle prossime lezioni.
In generale, i qualificatori del tipo e gli specificatori del tipo devono essere inseriti sempre dopo la classe di storage e prima dei dichiaratori di base, per cui la sequenza diventa:
classe_di_storage qualificatori_di_tipo specificatori_di_tipo dichiaratori_di_base;
Tuttavia, tra qualificatori del tipo e specificatori del tipo, l'ordine non è rilevante.
Dichiaratori
Per quanto riguarda i dichiaratori di base, essi possono essere:
-
nomi semplici: nel qual caso stiamo dichiarando una variabile;
int x;
in tal caso il dichiaratore è semplicemente
x
, ossia il nome della variabile. -
nomi seguiti da parentesi tonde: nel qual caso stiamo dichiarando una funzione;
int somma(int a, int b);
in tal caso il dichiaratore è
somma(int a, int b)
. -
nomi seguiti da parentesi quadre: nel qual caso stiamo dichiarando un array;
int numeri[10];
in tal caso il dichiaratore è
numeri[10]
. -
nomi preceduti da un asterisco: nel qual caso stiamo dichiarando un puntatore;
int *p;
in tal caso il dichiaratore è
*p
.
In una singola dichiarazione possiamo avere più dichiaratori separati da virgole:
specificatori dichiaratore1, dichiaratore2, ..., dichiaratoreN;
Ogni singolo dichiaratore può essere seguito da un inizializzatore, ossia un'espressione che assegna un valore iniziale alla variabile. Ciò, ovviamente, vale solo per le variabili.
La sintassi generale è la seguente:
specificatori dichiaratore = espressione;
Riepilogo della sintassi delle dichiarazioni
Riepiloghiamo, adesso, la sintassi generale delle dichiarazioni in linguaggio C:
Sintassi delle Dichiarazioni in linguaggio C
La sintassi di una dichiarazione in linguaggio C è la seguente:
classe_di_storage qualificatori_di_tipo specificatori_di_tipo dichiaratore1, dichiaratore2, ..., dichiaratoreN;
Dove:
-
classe_di_storage
è una classe di storage, ossia una delle parole chiaveauto
,register
,static
oextern
;La classe di storage può essere assente oppure ne può comparire solo una.
-
qualificatori_di_tipo
è una lista di qualificatori di tipo, ossiaconst
,volatile
orestrict
;I qualificatori di tipo possono essere assenti oppure ne possono comparire più di uno.
-
specificatori_di_tipo
è una lista di specificatori di tipo, ossiavoid
,char
,int
,float
,double
,short
,long
,signed
,unsigned
o tipi definiti contypedef
; -
dichiaratore1, dichiaratore2, ..., dichiaratoreN
sono dichiaratori di base, ossia nomi di variabili, funzioni, array o puntatori;I dichiaratori possono essere seguiti da un inizializzatore tranne nel caso di funzioni:
specificatori dichiaratore1 = espressione1, dichiaratore2 = espressione2, ..., dichiaratoreN = espressioneN;
L'inizializzatore è un'espressione che assegna un valore iniziale alla variabile.
Esempi
Mettiamo in pratica le regole viste sopra esaminando qualche esempio.
Osserviamo la figura che segue:
In questo esempio abbiamo:
- Una classe di storage:
static
; - Uno specificatore del tipo:
double
; - Tre dichiaratori:
a
eb
che sono variabili semplici e*p
che è un puntatore.
Non abbiamo nessun qualificatore di tipo.
Esaminiamo questo secondo esempio:
In questo esempio non abbiamo classe di storage. In compenso abbiamo:
- Un qualificatore del tipo:
const
; - Uno specificatore del tipo:
char
; - Un dichiaratore:
esempio[]
; - Un inizializzatore:
= "Prova"
;
In questo caso, esempio
è un array di caratteri costante.
Analizziamo un altro esempio che ha sia classe di storage che qualificatore del tipo:
In questo esempio abbiamo:
- Una classe di storage:
extern
; - Un qualificatore del tipo:
const
; - Due specificatori del tipo:
unsigned int
; da notare che il loro ordine è ininfluente. Avremmo potuto scrivere ancheint unsigned
; - Un dichiaratore:
x
.
Per concludere esaminiamo un ultimo esempio di dichiarazione di una funzione:
In questo caso abbiamo dichiarato una funzione usando:
- Una classe di storage:
extern
; - Uno specificatore del tipo che ne indica il tipo di ritorno:
double
; - Un identificatore:
quadrato(double)
. Questo identificatore indica che la funzionequadrato
accetta un parametro di tipodouble
.
In Sintesi
In questa lezione abbiamo approfondito la sintassi delle dichiarazioni in linguaggio C. Nelle lezioni precedenti l'avevamo soltanto accennata.
In particolare abbiamo visto che:
- Una dichiarazione è composta da specificatori e dichiaratori;
- Gli specificatori possono essere di tre tipi: classi di storage, qualificatori di tipo e specificatori di tipo;
- I dichiaratori possono essere nomi di variabili, funzioni, array o puntatori;
- Una dichiarazione può contenere più dichiaratori separati da virgole;
- Ogni dichiaratore può essere seguito da un inizializzatore.
Sebbene abbiamo già studiato gli specificatori di tipo nelle lezioni precedenti, specialmente quando abbiamo studiato i tipi di dato in C, non abbiamo, tuttavia, ancora affrontato il significato e l'utilizzo delle classi di storage e dei qualificatori di tipo. Questi argomenti saranno trattati nelle prossime lezioni.
In particolare, nella prossima lezione affronteremo il concetto di classi di storage.