Errori di programmazione e bug

Quando realizza un programma il programmatore informatico deve stare attento a non introdurre errori. Gli errori di programmazione possono essere distinti in due categorie fondamentali: quella degli errori che il compilatore è in grado di riconoscere ed evidenziare e quella degli errori che, al contrario, esso non riesce a rilevare.

bug

Alla prima categoria appartengono gli errori formali, che possono essere distinti in errori lessicali (uso di parole chiave del linguaggio non esistenti o scritte in maniera errata, analoghi agli “errori ortografici” del linguaggio naturale) e sintattici (parole chiave scritte correttamente ma utilizzate in maniera errata nella costruzione dell’istruzione). Fra tutti gli errori sicuramente questi sono quelli che destano meno preoccupazione, sia perché vengono rilevati dal compilatore (che, si ricorda, prima di tutto effettua un controllo lessicale e sintattico dei sorgenti e solo successivamente compila l’eseguibile del programma, se non sono presenti errori di questo tipo), sia perché se per programmare si utilizza un ambiente di sviluppo integrato (IDEIntegrated Development Environment) questo tipo di errore addirittura viene evidenziato dall’editor in tempo reale già in fase di scrittura del codice del programma.

Alla seconda categoria, invece, appartengono gli errori logici e gli errori di runtime.

Gli errori logici derivano dagli errori di progettazione dell’algoritmo risolutivo, quindi sono degli errori che si commettono prima ancora della scrittura del programma nel linguaggio di programmazione, e che determinano degli output diversi da quelli previsti. Questi errori sono insidiosi in quanto il codice del sorgente viene compilato ed eseguito senza alcuna segnalazione di errore da parte del compilatore, ma il risultato delle elaborazioni non è quello previsto, ossia il programma non fa quello che dovrebbe fare. La ricerca di tali errori è a carico del programmatore, che può verificare la loro esistenza eseguendo più volte il programma a partire da input diversi e controllando che i risultati (l’output) coincidano con quelli attesi. I casi di prova ovviamente devono essere conosciuti, ossia per essi deve essere noto il risultato.

Gli errori di runtime sono quegli errori che si verificano durante l’esecuzione del programma anche se il programma è corretto e non presenta errori logici, e che hanno la peculiarità di manifestarsi solo al verificarsi di situazioni eccezionali e non prevedibili a priori: le situazioni causa di errore potremmo chiamarle per comodità eccezioni.

P.S. In realtà l’ultima affermazione è solo una semplificazione utile per farsi capire anche da chi non sa che cos’è la programmazione ad oggetti, ma che potrebbe far storcere il naso a chi, invece, la conosce. Per questi ultimi si può precisare che un’eccezione è più propriamente un oggetto appartenente ad una particolare classe dipendente dallo specifico errore di runtime che si verifica e da cui viene creato. In altri termini, il verificarsi di un errore di runtime costituisce l’evento e l’eccezione è l’oggetto che tale evento crea. Tale oggetto può essere utilizzato per avere informazioni circa l’errore che l’ha creato e per rimediare ad esso tramite un gestore delle eccezioni.  Questo vale in generale, poi in particolare il meccanismo di gestione delle eccezioni può leggermente variare a seconda del linguaggio di programmazione OOP considerato.

Anche in un programma “perfetto” gli errori di runtime possono sempre capitare, soprattutto quelli determinati da uno scorretto utilizzo del programma da parte degli utenti utilizzatori (il caso più banale è quello dell’inserimento di un input non valido). Un buon programma dovrebbe essere in grado di prevedere questo tipo di errori e di eseguire le azioni di ripristino, quando è possibile. Alcuni esempi tipici di errori di runtime sono: il verificarsi di una divisione per 0; l’uso di un percorso file non valido o che fa riferimento ad una periferica che in un certo momento risulta scollegata; il verificarsi di un overflow in una variabile da parte di un’operazione aritmetica. Si tratta di errori critici, perché se non gestiti dall’applicazione possono determinare anomalie di funzionamento del programma, fino a bloccarlo inaspettatamente (crash dell’applicazione). Prevedere e gestire questo tipo di errori non è semplice ed è per questo che un po’ tutti i linguaggi di programmazione di ultima generazione supportano dei meccanismi di gestione strutturata delle eccezioni, che consentono di creare dei programmi con gestori di errori efficaci e completi per porre rimedio al verificarsi di queste situazioni di errore.

(Meccanismo delle eccezioni)

Quando si verifica un evento di errore di runtime, l’esecuzione del programma interrompe il suo normale flusso e, sfruttando il meccanismo delle eccezioni messo a disposizione dai linguaggi di programmazione, riprende il controllo a partire da un altro punto del programma dove si trovano le procedure di gestione degli errori di runtime. In altri termini il programma prova a trovare un gestore dell’eccezione che è stata “lanciata”, ossia un blocco di codice che gli dica in che modo rimediare all’errore che si è verificato e che gli permetta così di riprendere correttamente il flusso di esecuzione.

NOTA. Gli errori formali bloccano il compilatore che non riesce a produrre il file eseguibile del programma; gli errori di runtime bloccano l’esecuzione del programma anche se il programma è corretto (in assenza di errori logici), però, solo in alcune circostanze; gli errori logici, invece, non bloccano nulla, ma l’output del programma non è corretto.

Nel gergo informatico si è molto diffuso il termine bug con il significato di errore nella progettazione e realizzazione di un programma. Il significato letterale di questa parola inglese è piccolo insetto (o scarafaggio) e la sua introduzione nel gergo informatico deriva da un curioso aneddoto risalente al 1947, secondo il quale un insetto s’introdusse in un computer, che a quei tempi faceva largo uso di componenti elettromeccanici, come ad esempio i relè (un interruttore azionato da un elettromagnete), causando un malfunzionamento dell’elaboratore che creò non pochi problemi ai programmatori e progettisti che non riuscivano a comprenderne la causa.

I bug software oggi consistono in errori logici e/o di runtime che possono avere effetti più o meno letali. Un bug può rimanere sconosciuto anche per lungo tempo se si tratta di un errore che incide poco sulle funzionalità principali del programma, come ad esempio può essere il caso di un raro errore di runtime. Al contrario, può compromettere seriamente la funzionalità di un programma, come nel caso di un errore logico o di un errore di runtime ricorrente, e in tal caso verrebbe scoperto molto presto. Alcuni bug software possono determinare anche problemi di sicurezza, se quegli errori di programmazione possono essere sfruttati da un utente malintenzionato per aggirare i controlli di accesso, al fine di ottenere privilegi non autorizzati.

In generale nessun software, anche quelli prodotti dalle software house più blasonate, è immune da bug che, man mano che vengono scoperti, anche dopo il rilascio e la commercializzazione del software, vengono corretti o attraverso il rilascio periodico di aggiornamenti (o patch) o attraverso nuove versioni del software.

Quello che il programmatore può fare per ridurre al minimo l’introduzione di bug nei suoi programmi è adottare particolari metodologie di sviluppo (oggetto di studio di una specifica disciplina, denominata Ingegneria del software) allo scopo di eliminare gli errori logici già in fase di progettazione del software e sfruttare al meglio le funzionalità avanzate messe a disposizione dai linguaggi di programmazione per la gestione degli errori di runtime.