Corso C avanzato su Raspberry PI: Indentazione del codice

Quest'ultima puntata del corso avanzato sul linguaggio C su Raspberry Pi è dedicata alle tecniche per indentare un listato sorgente. Anche se, a volte, sembra un'operazione superflua, in realtà essa è una delle azioni di formattazione del codice più importanti. Grazie a essa, se eseguita correttamente, è possibile ottenere un codice altamente leggibile e ben organizzato, soprattutto se chi lo consulta sono terze persone.

A cosa serve indentare il codice sorgente

L'indentazione del codice non è una prerogativa solamente del linguaggio C. In pratica, i sorgenti in tutti i linguaggi di programmazione possono essere indentati. Il vecchio Cobol, ad esempio, è un linguaggio che esige un preciso posizionamento dei vari statement, tanto che un errore di semplice tabulazione interrompe la compilazione del software. Nella maggior parte dei linguaggi di programmazione, indentare un programma sorgente non è obbligatorio ma contribuisce tanto a ottenere un listato molto leggibile e chiaro, anche da terze persone o dallo stesso creatore dell'applicazione che consulta il suo lavoro dopo molto tempo. Il linguaggio C è essenzialmente un linguaggio a scrittura libera, pertanto un codice non formattato e non indentato funziona bene lo stesso, benché la sua leggibilità sia più compromessa. Andiamo, innanzitutto, a esaminare le regole d'oro dell'indentazione. E' basilare formattare opportunamente, con dei rientri, le clausole condizionali e i loop. Esaminiamo, ad esempio, il seguente codice:

if ( x>10 ) {
   k++;
   printf( "Elaborazione dei dati in corso.\n" );
} else {
   k--;
   printf( "Elaborazione dei dati bloccata\n" );
}

La leggibilità del listato è estremamente alta al punto che anche un individuo che non conosca il linguaggio C può comprenderne facilmente la sua filosofia. Al contrario, la seguente equivalente codifica, funzionante allo stesso modo, non usa alcun sistema d'indentazione e tutto il codice risulta allineato a sinistra. Ricordiamo che indentazione non è sinonimo di allineamento.

if ( x>10 ) {
k++;
printf( "Elaborazione dei dati in corso.\n" );
} else {
k--;
printf( "Elaborazione dei dati bloccata\n" );
}

Il programma "gira" esattamente nel medesimo modo ma la comprensibilità dello stesso è alquanto compromessa. Il compilatore non fa differenza tra un listato indentato e uno no. Esaminiamo quest'altro programma, che utilizza ben quattro loop nidificati (uno dentro l'altro), stampando tutte le possibili disposizioni (con ripetizione) dei segni "1", "X" e "2" del Totocalcio, a quattro a quattro:

#include <stdio.h>
int main() {
    int a, b, c, d;
    char *ch="1X2";
    for( a=0; a<3; a++)
        for( b=0; b<3; b++)
            for( c=0; c<3; c++)
                for( d=0; d<3; d++)
                    printf( "%c%c%c%c%c  ",ch[a],ch[b],ch[c],ch[d] );
    return 0;
}

Anche in questo caso, l'indentazione contribuisce a fornire, a colpo d'occhio, una rapida schematizzazione dei cicli contenuti nel programma. Ogni ciclo, in questo contesto, ne contiene un altro, un po' come le bambole di una matriosca russa. Proprio per questo motivo essi vengono detti "cicli nidificati". Il risultato della elaborazione finale è il seguente:

 

1111 111X 1112 11X1 11XX 11X2 1121 112X 1122 1X11 1X1X 1X12 1XX1 1XXX 1XX2 1X21 1X2X 1X22 1211 121X 1212 12X1 12XX 12X2 1221 122X 1222 X111 X11X X112 X1X1 X1XX X1X2 X121 X12X X122 XX11 XX1X XX12 XXX1 XXXX XXX2 XX21 XX2X XX22 X211 X21X X212 X2X1 X2XX X2X2 X221 X22X X222 2111 211X 2112 21X1 21XX 21X2 2121 212X 2122 2X11 2X1X 2X12 2XX1 2XXX 2XX2 2X21 2X2X 2X22 2211 221X 2212 22X1 22XX 22X2 2221 222X 2222

 

Il seguente listato, senza rientri, produce lo stesso risultato, ma la sua leggibilità è ridotta, praticamente, a zero:

#include <stdio.h>
int main() {
int a,b,c,d;
char *ch="1X2";
for( a=0; a<3; a++)
for( b=0; b<3; b++)
for( c=0; c<3; c++)
for( d=0;d <3; d++)
printf( "%c%c%c%c%c ",ch[a],ch[b],ch[c],ch[d] );
return 0;
}

A volte, l'indentazione manuale, se eseguita in modo scorretto, può causare una errata contestualizzazione degli statement, come in questo caso:

#include <stdio.h>
int main() {
    int k;
    printf( "Inizio del conteggio\n" );
    for( k=1; k<11; k++ )
        printf( "%d\n", k );
        printf( "Fine del conteggio\n" );
    return 0;
}

In questo caso, la funzione

printf("Fine del conteggio\n");

è indentata male, in quanto il suo dominio di appartenenza non è quello del loop ma si trova fuori da quest'ultimo. La sua collocazione, pertanto dovrebbe essere spostata un po' a sinistra, proprio in corrispondenza del ciclo. Mancando le parentesi graffe di esso, la ripetizione e l'iterazione si riferisce semplicemente al solo statement successivo. Pertanto, la corretta codifica è la seguente:

#include <stdio.h>
int main() {
    int k;
    printf( "Inizio del conteggio\n" );
    for( k=1; k<11; k++)
        printf( "%d\n",k );
    printf( "Fine del conteggio\n" );
    return 0;
}

Parecchi editor mettono a disposizione anche una sorta d'indentazione "grafica", con la quale il programmatore può osservare visivamente, tramite delle linee verticali, il livello di rientro di ciascun blocco di codice. E' anche possibile nascondere le varie parti "meno interessanti" premendo il tasto "-" posto a sinistra di ogni blocco. Si può, ovviamente, mostrarlo con la pressione dell'equivalente tasto "+", come evidenziato in figura 1. Si instaura, in tale modo, una sorta di struttura gerarchica o ad albero, proprio come quella di un file system nell'esplora risorse di un sistema operativo.

Figura 1: l'indentazione grafica di Notepad++

Figura 1: l'indentazione grafica di Notepad++

Usare spazi o tab?

Ci sono parecchie scuole di pensiero che prediligono gli spazi oppure il carattere di tabulazione, per concretizzare l'indentazione o l'allineamento in generale. Molti programmatori usano gli spazi e in questa maniera sanno che il proprio codice può essere letto senza problemi da qualunque programma in qualsiasi sistema operativo (uno spazio è pur sempre uno spazio). Il carattere di tabulazione è anche molto utilizzato e, con la sua pressione, si possono generare due, tre, quattro o più spazi in un unico colpo. Addirittura molti editor danno la possibilità di programmare la larghezza del tabulatore.

La larghezza dell'indentazione

Oltre allo scopo primario del rientro dei vari statements, la larghezza dell'indentazione contribuisce a "disegnare" la forma del codice sorgente. Anche in questo caso ci sono diverse correnti che preferiscono una diversa larghezza della tabulazione. Probabilmente, la maggior parte dei programmatori utilizza una indentazione di tre, quattro oppure otto spazi. I casi estremi, ovviamente, vanno sempre evitati e ciò si può evincere grazie ai tre listati di esempio, qui sotto riportati.

 

Spaziatura d'indentazione: 1

void main() {
 int x = 23;
 for (i=0; i<=5; i++) {
  if (i*i == x) {
   continue;
  }
  MiaFunzione();
 }
}

Spaziatura d'indentazione: 4

void main() {
    int x = 23;
    for (i=0; i<=5; i++) {
        if (i*i == x) {
            continue;
        }
        MiaFunzione();
    }
}

Spaziatura d'indentazione: 16

void main() {
                int x = 23;
                for (i=0; i<=5; i++) {
                                if (i*i == x) {
                                                continue;
                                }
                                MiaFunzione();
                }
}

In ogni caso, ove sia possibile, è sempre da evitare una programmazione estremamente nidificata, ai limiti della comprensione visiva. Il margine di errore, infatti, è molto alto e facilmente il programmatore può perdere la bussola per via delle numerose condizioni o dei cicli presenti nel listato. Il seguente sorgente, privo di ogni utile praticità, con larghezza d'indentazione pari a 4, lo dimostra pienamente:

#include <stdio.h>
int main()
{
    int a,b,c,d,e,f;
    for(a=1;a<77;a++) {
        if(a>50) {
            printf("%d\n,a,a*2");
        }
        for(b=1;b<=100;b++) {
            for(c=1;c<=200;c++) {
                if(a>b && c<b) {
                    for(d=a;d<b;d++) {
                        while(a<c) {
                            printf("%d\n",a+b+c+d);
                            for(e=1;e<150;e++) {
                                printf("%d %d\n",e,c*2);
                            }
                            for(f=1;f<50;f++) {
                                if(f>e) {
                                    f=e;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return 0;
}

Esso, infatti, pur rispettando pienamente la corretta formattazione e la canonica indentazione, risulta poco leggibile e altamente confusionario.

Indentazione con VI e il pacchetto "indent"

Si supponga di aver scritto il codice non indentato di cui alla figura 2. Si tratta di cinque cicli nidificati.

Figura 2: un esempio di codice non indentato all'interno dell'editor "vi"

Figura 2: un esempio di codice non indentato all'interno dell'editor "vi"

[...]

ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2321 parole ed è riservato agli abbonati MAKER. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici MAKER e potrai fare il download (PDF) dell'EOS-Book del mese. ABBONATI ORA, è semplice e sicuro.

Abbonati alle riviste di elettronica

Una risposta

  1. Giovanni Di Maria Giovanni Di Maria 9 luglio 2019

Scrivi un commento