Blocchi di Codice in linguaggio C

Un blocco di codice in linguaggio C è un insieme di istruzioni e dichiarazioni racchiuse tra parentesi graffe { e }.

In questa lezione studieremo il legame tra blocchi di codice e visibilità delle variabili. Inoltre studieremo il concetto di shadowing o oscuramento delle variabili.

Blocchi di Codice

In precedenza, nella lezione sull'istruzione if abbiamo introdotto il concetto di istruzioni composte. Infatti abbiamo visto che, per eseguire più istruzioni in base alla condizione dell'istruzione if risulta necessario racchiudere tali istruzioni tra le parentesi graffe { e }.

Ad esempio:

if (a > 0) {
    printf("a è maggiore");
    printf("di zero");
}

In questo caso, le istruzioni printf sono racchiuse tra le parentesi graffe { e }. Questo significa che sono eseguite solo se la condizione dell'istruzione if è vera.

In realtà, il costrutto di istruzione composta può contenere al proprio interno anche dichiarazioni di variabili. Ad esempio:

if (a > 0) {
    int b = 10;
    printf("a è maggiore");
    printf("di zero");
}

Per tal motivo piuttosto che chiamare questo costrutto del linguaggio C come istruzione composta, da questo momento in poi lo chiameremo blocco di codice.

Da un punto di vista sintattico anche il corpo di una funzione è a tutti gli effetti un blocco di codice.

Definizione

Blocco di Codice

In linguaggio C un blocco di codice è un insieme di istruzioni e dichiarazioni racchiuse tra le parentesi graffe { e }.

La sintassi è la seguente:

{
    dichiarazione1;
    dichiarazione2;
    ...
    dichiarazioneN;

    istruzione1;
    istruzione2;
    ...
    istruzioneN;
}

Nello standard originale del C le dichiarazioni di variabili devono precedere le istruzioni.

Nel caso del C99 è possibile inframezzare le dichiarazioni con istruzioni:

{
    dichiarazione1;
    istruzione1;
    dichiarazione2;
    istruzione2;
    ...
    dichiarazioneN;
    istruzioneN;
}

Blocchi di Codice e Scope

Dal momento che possiamo dichiarare variabili all'interno di un blocco di codice, è necessario capire come queste variabili vengono gestite dal compilatore.

In particolare, è necessario capire come il compilatore gestisce la visibilità o scope delle variabili dichiarate all'interno di un blocco di codice.

Di default, la Durata di vita di una variabile interna ad un blocco di codice è limitata al blocco stesso. Questo significa che una variabile dichiarata all'interno di un blocco di codice non è visibile all'esterno del blocco stesso. Così come le variabili locali di una funzione, anche le variabili dichiarate all'interno di un blocco di codice sono locali al blocco. Formalmente una variabile interna ad un blocco ha una Durata di vita automatica.

Ad esempio, consideriamo il seguente codice:

int main() {
    int a = 10;
    if (a > 0) {
        int b = 10;
        printf("a è maggiore");
        printf("di zero");
    }
    printf("b vale %d", b);
}

Il compilatore segnalerà un errore di compilazione, perché la variabile b è dichiarata all'interno di un blocco di codice e non è visibile all'esterno del blocco stesso.

b.c: In function ‘main’:
b.c:9:27: error: ‘b’ undeclared (first use in this function)
     printf("b vale %d", b);
                         ^
Definizione

Blocchi di Codice e Variabili locali

Una variabile dichiarata all'interno di un blocco di codice è visibile soltanto all'interno del blocco stesso. Per tal motivo si parla di Variabili locali al blocco di codice.

Blocchi di Codice innestati

Nel linguaggio C è completamente legale dichiarare un blocco di codice all'interno di un altro blocco di codice. In tal caso si parla di blocchi di codice innestati.

Ad esempio consideriamo il seguente codice:

int main() {
    int a = 10;
    printf("a vale %d", a);

    {
        int b = 10;
        printf("b vale %d", b);
    }

    return 0;
}

In questo esempio abbiamo inserito un blocco di codice all'interno del corpo della funzione main. All'interno di esso abbiamo dichiarato la variabile b e stampato il suo valore. La variabile b sarà visibile soltanto all'interno del blocco di codice più interno.

Definizione

Blocchi di Codice Innestati

In linguaggio C è possibile dichiarare un blocco di codice all'interno di un altro blocco di codice. In tal caso si parla di blocchi di codice innestati.

La sintassi è la seguente:

{
    dichiarazione1;
    dichiarazione2;
    ...
    dichiarazioneN;

    istruzione1;
    istruzione2;
    ...
    istruzioneN;

    {
        /* Blocco di codice innestato */
        dichiarazione1;
        dichiarazione2;
        ...
        dichiarazioneN;

        istruzione1;
        istruzione2;
        ...
        istruzioneN;
    }
}

Shadowing di Variabili

In alcuni casi, risulta utile dichiarare una variabile all'interno di un blocco di codice che ha lo stesso nome di una variabile dichiarata all'esterno del blocco stesso.

Questo è consentito dal linguaggio C, ma è necessario fare attenzione perché potrebbe portare a risultati inaspettati.

Infatti il compilatore, quando incontra una variabile all'interno di un blocco di codice, cerca di trovare la variabile con lo stesso nome all'interno del blocco. Se la trova, la variabile all'interno del blocco di codice nasconde la variabile all'esterno. Questo fenomeno viene chiamato shadowing o oscuramento.

Ad esempio, consideriamo il seguente codice:

int main() {
    int a = 10;
    if (a > 0) {
        int a = 20;
        printf("a vale %d", a);
    }
    printf("a vale %d", a);
}

Se proviamo a compilare ed eseguire questo codice, otterremo il seguente risultato:

a vale 20
a vale 10

In questo caso, la variabile a dichiarata all'interno del blocco di codice ha nascosto la variabile a dichiarata all'esterno del blocco di codice. Quindi la prima istruzione printf stampa il valore della variabile a dichiarata all'interno del blocco di codice, mentre la seconda istruzione printf stampa il valore della variabile a dichiarata all'esterno del blocco di codice.

Bisogna fare attenzione allo shadowing delle variabili, tuttavia. L'abuso di tale meccanismo può portare a risultati inaspettati. Quindi è sempre meglio evitare di usare lo stesso nome per variabili dichiarate all'interno e all'esterno di un blocco di codice.

Inoltre, in linguaggio C, quando una variabile è oscurata da una variabile dichiarata all'interno di un blocco di codice, la variabile all'esterno del blocco di codice non è più visibile all'interno del blocco di codice e non vi è modo di accedervi.

Definizione

Oscuramento o Shadowing delle variabili

Lo Shadowing o Oscuramento di una variabile locale avviene quando viene dichiarata una variabile con lo stesso nome all'interno di un blocco di codice più interno.

In tal caso la variabile locale esterna al blocco è oscurata e non più accessibile all'interno del blocco interno.

void f() {
    /* Variabile locale */
    tipo nome;

    /* ... */

    {
        /* Variabile locale del blocco innestato */
        /* Questa variabile oscura la variabile di prima */
        tipo nome;

        /* ... */
    }
}

Shadowing e variabili globali

All'interno di un blocco di codice è anche possibile dichiarare una variabile che ha lo stesso nome di una variabile globale. In questo modo si oscura la variabile globale.

Ad esempio, consideriamo il seguente codice:

int a = 10;

int main() {
    if (a > 0) {
        int a = 20;
        printf("a vale %d", a);
    }
    printf("a vale %d", a);
}

Se proviamo a compilare ed eseguire questo codice, otterremo il seguente risultato:

a vale 20
a vale 10

Anche in questo esempio, la variabile a dichiarata all'interno del blocco di codice ha oscurato la variabile globale a.

Esiste, tuttavia, in questo caso un modo per accedere alla variabile globale a all'interno del blocco di codice. Per comprendere meglio esaminiamo il codice seguente:

#include <stdio.h>

/* Variabile globale */
int a = 10;

int main() {
    if (a > 0) {
        int a = 20;
        printf("a vale %d\n", a);
        /* Blocco di codice innestato */
        {
            extern int a;
            printf("a vale %d\n", a);
        }
    }
    printf("a vale %d\n", a);
}

Se proviamo a compilare ed eseguire questo codice, otterremo il seguente risultato:

a vale 20
a vale 10
a vale 10

Quello che abbiamo fatto in questo caso è dichiarare esplicitamente la variabile globale a all'interno del blocco di codice utilizzando la parola chiave extern. Questo ha permesso di accedere alla variabile globale a all'interno del blocco di codice.

La parola chiave extern, tuttavia, può essere usata soltanto per le variabili globali.

Definizione

Shadowing e Variabili Globali

Lo shadowing di una variabile può essere aggirato esclusivamente nel caso in cui si tratta di una variabile globale.

Per farlo è necessario usare un blocco di codice innestato in cui dichiarare una variabile con lo stesso nome di quella globale e con la parola chiave extern.

/* Variabile globale */
tipo nome;

/* ... */

void f() {
    /* Variabile locale che oscura quella globale */
    tipo nome;

    /* ... */

    {
        /*
         * Dichiarazione esplicita della variabile globale
         * In questo blocco innestato la variabile globale
         * risulta accessibile
         */
        extern tipo nome;

        /* ... */
    }
}

In Sintesi

In questa lezione abbiamo studiato nuovamente il concetto di istruzione composta e lo abbiamo esteso con l'introduzione delle dichiarazioni di variabili. Per questo da adesso in poi parleremo di blocco di codice.

Abbiamo visto che un blocco di codice è un insieme di istruzioni che possono essere eseguite in modo indipendente. Un blocco di codice può essere dichiarato all'interno di un altro blocco di codice.

Abbiamo visto che le variabili dichiarate all'interno di un blocco di codice sono visibili solo all'interno del blocco stesso. Questo significa che non è possibile accedervi all'esterno del blocco di codice.

Infine abbiamo introdotto il concetto di shadowing o oscuramento delle variabili. Questo fenomeno si verifica quando una variabile dichiarata all'interno di un blocco di codice ha lo stesso nome di una variabile dichiarata all'esterno del blocco di codice. In questo caso la variabile dichiarata all'interno del blocco di codice nasconde la variabile dichiarata all'esterno del blocco di codice.

Adesso, avendo studiato i concetti di variabili locali e globali e avendo definito meglio il concetto di blocco di codice possiamo studiare nel dettaglio le regole che governano la visibilità delle variabili.