Istruzione Nulla in Linguaggio C

In questa lezione esploriamo il concetto di istruzione nulla in linguaggio C.

Analizziamo come e quando utilizzare questa istruzione, evidenziando sia le situazioni in cui può semplificare il codice sia gli errori tipici che si possono commettere.

Istruzione Nulla

In linguaggio C un'istruzione può anche essere vuota o nulla.

In pratica si tratta di un'istruzione priva di qualsiasi operazione, che non esegue nulla. Per introdurre un'istruzione nulla si utilizza semplicemente un punto e virgola (;).

Ad esempio:

int x = 5;
int y = 10;

x += 3;
; // Istruzione nulla
y -= 2;

In questo esempio, l'istruzione nulla (;) non ha alcun effetto sul programma.

Definizione

Istruzione Nulla

Un'Istruzione Nulla in C è un'istruzione che non esegue alcuna operazione, rappresentata da un punto e virgola:

;

Impieghi dell'Istruzione Nulla

Sorge a questo punto la domanda: perché utilizzare un'istruzione nulla?

Questo tipo di istruzione viene principalmente utilizzata per realizzare cicli i cui corpi siano vuoti, nel senso che non necessitano di eseguire alcuna operazione all'interno del ciclo stesso.

Vediamo un esempio. Supponiamo di voler realizzare un programma che verifichi se un numero intero inserito dall'utente sia un numero primo o meno.

Per implementare questo programma possiamo sfruttare un test di primalità che verifica se il numero è divisibile per altri numeri. In particolare, possiamo verificare se il numero è divisibile per tutti i numeri compresi tra 2 e se stesso meno 1.

Un primo modo di implementare questo test in C potrebbe essere il seguente:

#include <stdio.h>

int main() {
    printf("Inserisci un numero intero: ");
    int numero;
    scanf("%d", &numero);

    int d;
    for (d = 2; d < numero; d++)
        if (numero % d == 0)
            break; // Se il numero è divisibile, esci dal ciclo

    if (d == numero) {
        printf("%d è un numero primo.\n", numero);
    } else {
        printf("%d non è un numero primo.\n", numero);
    }

    return 0;
}

In questa prima implementazione, il cuore dell'algoritmo è il ciclo for:

for (d = 2; d < numero; d++)
    if (numero % d == 0)
        break; // Se il numero è divisibile, esci dal ciclo

Questo ciclo itera su tutti i numeri da 2 fino a numero - 1, verificando se il numero inserito è divisibile per ciascuno di essi. Se trova un divisore, esce dal ciclo con l'istruzione break.

Possiamo, però, modificare il ciclo in modo da non eseguire alcuna operazione all'interno del suo corpo, utilizzando un'istruzione nulla.

Per prima cosa, spostiamo la condizione if all'interno della dichiarazione del ciclo for, in modo da verificare la divisibilità direttamente durante l'iterazione. Poi, possiamo utilizzare un'istruzione nulla per il corpo del ciclo:

for (d = 2; d < numero && numero % d != 0; d++)
    ; // Istruzione nulla

In questo modo, ad ogni iterazione del ciclo, viene dapprima verificata la condizione d < numero. Se questa condizione è falsa, poiché l'operatore && funziona in corto circuito, la seconda condizione numero % d != 0 non viene valutata, e il ciclo termina. Se invece la condizione è vera, viene verificata la divisibilità del numero, e se questa è falsa, il ciclo continua senza eseguire alcuna operazione grazie all'istruzione nulla.

Alla fine del ciclo, una delle due condizioni sarà falsa: o d sarà uguale a numero, oppure numero % d sarà uguale a 0, indicando che il numero non è primo.

Consiglio

Inserire un'istruzione nulla su di una riga separata

Facciamo una piccola osservazione sul codice dell'esempio precedente.

Abbiamo inserito l'istruzione nulla su di una riga separata per conto suo:

for (d = 2; d < numero && numero % d != 0; d++)
    ; // Istruzione nulla

Avremmo potuto benissimo scrivere l'istruzione nulla sulla stessa riga del ciclo for, come segue:

for (d = 2; d < numero && numero % d != 0; d++) ;

Tuttavia, questa pratica è altamente sconsigliata, poiché rende il codice meno leggibile e può confondere chi lo legge.

Ad esempio, riprendendo il codice precedente, se scrivessimo:

for (d = 2; d < numero && numero % d != 0; d++) ;
if (d == numero) {
    printf("%d è un numero primo.\n", numero);
} else {
    printf("%d non è un numero primo.\n", numero);
}

Ad un primo sguardo, potrebbe sembrare che l'istruzione if sia parte del ciclo for, il che non è corretto. In realtà, l'istruzione if viene eseguita una volta dopo che il ciclo for è terminato.

Per cui, il nostro consiglio è di inserire sempre l'istruzione nulla su una riga separata.

Un'ultima osservazione riguarda il fatto che trasformare un ciclo dotato di corpo in un ciclo con istruzione nulla non necessariamente rende il codice più efficiente. Si tratta di una questione di stile e leggibilità del codice, piuttosto che di prestazioni. Esistono casi, tuttavia, in cui l'uso di un'istruzione nulla può semplificare la logica del programma e renderlo più chiaro come vedremo quando studieremo le stringhe.

Istruzione Nulla ed Errori di Programmazione

L'utilizzo dell'istruzione nulla può portare a errori di programmazione se non viene utilizzata correttamente risultando spesso in enormi confusioni per gli sviluppatori.

In questa sezione vediamo quali sono i più comuni.

  1. Inserire un'istruzione nulla al termine di istruzione if:

    Prendiamo l'esempio che segue:

    if (x > 0); /* ERRORE */
        printf("x è positivo.\n");
    

    In questo caso abbiamo inserito un punto e virgola (;) alla fine dell'istruzione if ossia un'istruzione nulla. Questo significa che l'istruzione printf non è parte del blocco if, ma viene eseguita sempre, indipendentemente dal valore di x. Il risultato sarà che il messaggio verrà stampato anche quando x non è positivo.

  2. Creazione accidentale di un ciclo infinito con while:

    Consideriamo il seguente codice:

    int i = 0;
    while (i < 10); /* ERRORE */
        i++;
    

    Qui abbiamo un ciclo while che sembra corretto, ma l'istruzione nulla alla fine del ciclo (ossia il punto e virgola messo dopo il while) significa che il corpo del ciclo è vuoto. Di conseguenza, i non viene mai incrementato e il ciclo diventa infinito, bloccando l'esecuzione del programma.

  3. Esecuzione del corpo di un ciclo una volta sola:

    Un altro errore comune è quello di inserire un punto e virgola dopo un'istruzione for o while. La conseguenza è che il corpo del ciclo viene eseguito una sola volta. Ad esempio:

    for (int i = 0; i < 5; i++); /* ERRORE */
        printf("i: %d\n", i);
    

    In questo caso, l'istruzione nulla alla fine del ciclo for significa che sebbene la variabile i venga incrementata, la funzione printf, che dovrebbe essere il vero corpo del ciclo, viene eseguita solo una volta dopo che il ciclo è terminato. Questo può portare a risultati inaspettati.

In base a questi esempi, è chiaro che l'uso dell'istruzione nulla richiede attenzione e cautela. È importante essere consapevoli di come e dove viene utilizzata per evitare errori logici nel codice.