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:

Definizione

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 chiave auto, register, static o extern;

    La classe di storage può essere assente oppure ne può comparire solo una.

  • qualificatori_di_tipo è una lista di qualificatori di tipo, ossia const, volatile o restrict;

    I qualificatori di tipo possono essere assenti oppure ne possono comparire più di uno.

  • specificatori_di_tipo è una lista di specificatori di tipo, ossia void, char, int, float, double, short, long, signed, unsigned o tipi definiti con typedef;

  • 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:

Esempio di Dichiarazione 1
Figura 1: Esempio di Dichiarazione 1

In questo esempio abbiamo:

  • Una classe di storage: static;
  • Uno specificatore del tipo: double;
  • Tre dichiaratori: a e b che sono variabili semplici e *p che è un puntatore.

Non abbiamo nessun qualificatore di tipo.

Esaminiamo questo secondo esempio:

Esempio di Dichiarazione 2
Figura 2: Esempio di Dichiarazione 2

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:

Esempio di Dichiarazione 3
Figura 3: Esempio di Dichiarazione 3

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 anche int unsigned;
  • Un dichiaratore: x.

Per concludere esaminiamo un ultimo esempio di dichiarazione di una funzione:

Esempio di Dichiarazione 4
Figura 4: Esempio di Dichiarazione 4

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 funzione quadrato accetta un parametro di tipo double.

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.