Tracce
/
C
C
/
Programma
/
Blocchi
Bl

Blocchi in C

{one: "1 esercizio", many: "%{count} esercizi", other: "%{count} esercizi"}

Informazioni su Blocchi

Un blocco è un insieme di istruzioni e dichiarazioni raggruppate in un'unica unità sintattica. I blocchi sono spesso, ma non sempre, definiti all'interno di parentesi graffe. I blocchi possono essere annidati all'interno di funzioni e di altri blocchi.

Nell'esempio seguente, number è definito all'interno della funzione main. flag è definito all'interno del blocco if, che è annidato nella funzione main. Le variabili definite all'interno di un blocco sono valide solo all'interno di quel blocco. Di conseguenza, provare a compilare il codice produce un errore di compilazione.

#include <stdio.h>

int main() {

  int number = 1;

  if (number == 1) {
      int flag = 1;
      printf("%d", flag);
  }

  // this line generates a compilation error
  // error: 'flag' undeclared (first use in this function)
  printf("%d", flag);
    
}

Il messaggio di errore può confondere, dato che flag viene usato prima nella funzione. Tuttavia, flag è definito all'interno di un blocco dentro la funzione. Il blocco interno riesce a «vedere» la variabile number della funzione che lo racchiude, ma la funzione esterna non riesce a «vedere» la variabile flag definita nel blocco interno. Quando l'esecuzione esce dal blocco, la variabile flag viene distrutta automaticamente e non è più disponibile per il programma. La funzione main stessa vede l'identificatore flag per la prima volta nell'ultima istruzione printf. Se si rimuove l'ultima istruzione printf, il programma verrà compilato e, una volta eseguito, stamperà 1. Questo perché la variabile flag è valida nel blocco che contiene la prima istruzione printf.

A partire da C99, anche le istruzioni if, for, while e do su una sola riga sono considerate blocchi. Nell'esempio seguente, sebbene i non sia definito all'interno di parentesi graffe, esiste solo nel blocco definito dall'istruzione for e non è visibile alla funzione main esterna.

#include <stdio.h>

int main() {

    for (int i = 0, j = 11; i < j; i+=2)
        printf("%d", i);

    // this line generates a compilation error
    // error: 'i' undeclared (first use in this function)
    printf("%d", i);
    
}

Se si rimuove l'ultima istruzione printf, il programma verrà compilato e, una volta eseguito, stamperà 0 2 4 6 8 10 . Alcuni linguaggi, come Go e Rust, stabiliscono esplicitamente che le istruzioni condizionali e i cicli su una sola riga costituiscono un blocco. Richiedono che tali istruzioni su una sola riga siano comunque racchiuse tra parentesi graffe.

Una funzione si comporta in un certo senso come un blocco. L'identificatore di una variabile definita in una funzione non può essere visto dall'esterno della funzione, proprio come l'identificatore di una variabile definita in un blocco non può essere visto dall'esterno del blocco. Due variabili in due funzioni possono avere lo stesso nome, dato che le due funzioni non si vedono l'una dentro l'altra, proprio come due variabili in due blocchi possono avere lo stesso nome, dato che i due blocchi non si vedono l'uno dentro l'altro. Ma funzioni e blocchi non sono identici. Una funzione non può essere definita dentro un'altra funzione, mentre un blocco può essere annidato dentro un altro blocco. E alcuni identificatori di un blocco possono essere visti dall'esterno del blocco. Nell'esempio seguente, il codice non verrà compilato

#include <stdio.h>

int main() {

  {
      int i = 0;
      begin:
      printf("i == %d\n", i);
      if (i == 0) {
          i = 1;
          goto begin;
      }
  }

  {
      int i = 1;
      begin:
      printf("i == %d\n", i);
      if (i == 1) {
          i = 0;
          goto begin;
      }
  }

}

Il codice qui sopra non viene compilato e produce il seguente errore

/tmp/gH2TVYnTSJ.c: In function 'main':
/tmp/gH2TVYnTSJ.c:17:5: error: duplicate label 'begin'
   17 |     begin:
      |     ^~~~~
/tmp/gH2TVYnTSJ.c:7:5: note: previous definition of 'begin' was here
    7 |     begin:
      |     ^~~~~

Sebbene i possa essere definito in ogni blocco, i nomi delle label devono essere univoci all'interno di una funzione. Questo perché le labels hanno lo scope della funzione, cioè ogni label è vista dalla funzione a prescindere da dove sia definita al suo interno. Quindi, a differenza delle variabili, indipendentemente da dove sia definita una label all'interno di una funzione, deve avere un nome univoco in quella stessa funzione. Per far funzionare il codice, occorre cambiare il nome di una delle label in modo che siano entrambe univoche, come nell'esempio seguente

#include <stdio.h>

int main() {

    {
        int i = 0;
        begin:
        // prints
        // i == 0
        // i == 1
        printf("i == %d\n", i);
        if (i == 0) {
            i = 1;
            goto begin;
        }
    }

    {
        int i = 1;
        start:
        // prints
        // i == 1
        // i == 0    
        printf("i == %d\n", i);
        if (i == 1) {
            i = 0;
            goto start;
        }
    }

}

Le istruzioni su una sola riga possono essere «impilate» e restare comunque all'interno di un unico blocco senza l'uso di parentesi graffe. Nell'esempio qui sotto, l'istruzione for contiene un'istruzione if, che a sua volta contiene un'istruzione printf. Sebbene nel blocco for ci siano in tutto due istruzioni, solo la sua istruzione if su una sola riga viene valutata come corpo del ciclo for. L'istruzione printf su una sola riga dell'istruzione if, a sua volta, viene valutata come corpo di quest'ultima. Se i blocchi annidati occupano ciascuno una sola riga, nessuno di essi ha bisogno di parentesi graffe. Nota che il commento non richiede parentesi graffe per racchiuderlo insieme all'istruzione printf dentro il blocco if, dato che il commento non è un'istruzione. L'indentazione chiarisce cosa sta succedendo, ma un team di sviluppo può avere linee guida di codifica proprie su dove collocare le parentesi graffe in situazioni simili, per eliminare ogni ambiguità.

#include <stdio.h>

int main() {

    for (int i = 0, j = 11; i < j; i++)
        if (i & 1 == 1)
            // prints 1 3 5 7 9 
            printf("%d ", i);
}

Abbiamo visto che i valori definiti all'interno di un blocco non sono accessibili dall'esterno del blocco. E abbiamo visto che i valori definiti all'esterno di un blocco di solito sono accessibili dall'interno del blocco. È possibile che una variabile esterna a un blocco venga nascosta all'interno di un blocco, se l'identificatore di quella variabile viene usato per definire una variabile del blocco. Nell'esempio seguente, l'identificatore number viene usato sia per una variabile a livello di funzione sia per una variabile a livello di blocco. Diciamo che la variabile a livello di blocco nasconde, o ombreggia, la variabile a livello di funzione.

#include <stdio.h>

int main() {
    int number = 1;
    // prints number in function is 1
    printf("number in function is %d\n", number);
    
    if (number == 1) {
        // the definition of a block-level variable using the same name
        // shadows the function-level variable
        int number = 2;
        // prints number in function is 2
        printf("number in if block is %d\n", number);
    }
    // prints number in function is 1
    printf("number in function is %d\n", number);
}

I compilatori possono generare un errore se si tenta di ombreggiare un parametro di funzione. L'esempio seguente

#include <stdio.h>

void print_number(int number) {
    // will fail to compile
    int number = 2;
    printf("number in function is %d\n", number);
}

int main() {
    int number = 1;
    printf("number in main is %d\n", number);
    print_number(number);
    printf("number in main is %d\n", number);
}

produce i seguenti errori su due piattaforme

/tmp/jIvhD5UOop.c: In function 'print_number':
/tmp/jIvhD5UOop.c:5:9: error: 'number' redeclared as different kind of symbol
    5 |     int number = 2;
      |         ^~~~~~
/tmp/jIvhD5UOop.c:3:23: note: previous definition of 'number' was here
    3 | void print_number(int number) {
      |                   ~~~~^~~~~~

e

redefinition of formal parameter 'number'
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda