Ciao e benvenuto in questo articolo dove parleremo degli errori più comuni che possiamo incontrare nella programmazione, la loro tipologia e quali strumenti possono aiutarci a risolverli e ad evitarli.

Partiamo con il capire quali sono gli errori che comunemente si fanno nella programmazione.

Quando si realizza un programma il programmatore informatico deve stare attento a non introdurre errori.

Gli errori di programmazione possono essere distinti in due categorie fondamentali:

  1. Categoria degli errori che il compilatore è in grado di riconoscere ed evidenziare.
  2. Categoria degli errori che il compilatore NON è in grado di riconoscere e rilevare.

Alla prima categoria appartengono gli errori formali o di sintassi

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

Gli errori di sintassi o anche chiamati errori formali possono essere distinti in:

  • errori lessicali: cioè l’uso di parole chiave del linguaggio non esistenti o scritte in maniera errata, analoghi agli “errori ortografici” del linguaggio naturale
  • errori sintattici: gli errori sintattici si hanno quando utilizziamo delle parole chiave del linguaggio di programmazione scritte correttamente ma utilizzate in maniera errata nella scrittura dell’istruzione

Cerchiamo di capire meglio cosa sono questi errori lessicali e errori sintattici usando degli esempi…

Esempi di errori lessicali:

  • In italiano un errore lessicale potrebbe essere per esempio la frase “o fame” dove “o” è scritto senza “h” quindi la frase corretta sarebbe “ho fame”.
  • l’uso di una parola chiave che non esiste nel linguaggio per esempio usare come parola chiave “per” al posto della parola chiave “for” oppure scrivere una parola chiave in maniera errata per esempio scrivere la parola chiave “while” senza la “i” cioè “whle”.

Esempio di errori sintattici:

  • aprire un blocco con la parentesi graffa e dimenticarsi di chiuderla

if(…..){

……..

  • aprire una parentesi tonda e dimenticarsi di chiuderla

Fra tutti gli errori quelli di sintassi sono quelli che destano meno preoccupazione.

Questo Perché nella maggior parte dei linguaggi il compilatore prima di generare il file eseguibile del programma effettua un controllo lessicale e sintattico dei sorgenti e solo se NON sono presenti errori di questo tipo compila il codice sorgente e crea l’eseguibile del programma. Perciò il compilatore rileva questo tipo di errori.

Se per programmare si utilizza un ambiente di sviluppo integrato (IDE – Integrated Development Environment) come per esempio Eclipse, questo tipo di errore addirittura viene evidenziato dall’editor in tempo reale già in fase di scrittura del codice del programma.

Scopriamo ora quali sono invece gli errori di semantica.

Gli errori di semantica 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 determinano degli output diversi da quelli previsti.

In particolare è presente un errore di semantica quando un programma viene eseguito (cioè è quindi privo di errori di sintassi), ma fornisce un output incoerente o si comporta in un modo inaspettato o in generale in modo non previsto dal programmatore.

Poiché questi errori di semantica derivano da un’errata logica di stesura del programma da parte del programmatore, sono anche detti errori di logica.

Questi errori sono insidiosi in quanto il codice 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 il risultato (l’output) coincida con quello atteso.

Come ultima tipologia di errori vediamo gli Errori di Runtime cioè gli errori a tempo di esecuzione del programma.

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.

Visto che si manifestano solo in situazioni eccezionali e non prevedibili al momento della scrittura del codice Java, queste situazioni causa di errore vengono anche chiamate per comodità eccezioni.

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 zero
  • l’uso di un percorso file non valido
  • il verificarsi di un overflow in una variabile da parte di un’operazione aritmetica. Quindi l’inserimento di un valore troppo grande rispetto alla massima capienza che può avere quella variabile

Si tratta di errori critici, perché se non gestiti dall’applicazione possono determinare anomalie di funzionamento del programma, fino a bloccarlo inaspettatamente (cioè generare quello che viene chiamato il crash dell’applicazione).

Prevedere e gestire questi tipi di errori non è semplice ed è per questo che in molti linguaggi di programmazione esistono dei meccanismi di gestione strutturata delle eccezioni e quando si verifica un evento di errore di runtime, l’esecuzione del programma interrompe il suo normale flusso e, sfruttando questo meccanismo di gestione delle eccezioni, riprende il controllo a partire da un altro punto del programma dove si trovano tutte le procedure di gestione degli errori di runtime.

In altri termini il programma prova a gestire l’eccezione che è stata “lanciata”, cioè viene eseguito un blocco di codice che cerca di rimediare all’errore che si è verificato, in modo tale da permettere al programma di riprendere correttamente il flusso di esecuzione del programma.

Detto ciò….avendo visto tutte queste tipologie di errori…..

Adesso la domanda nasce spontanea….

COS’È UN BUG?

Questo termine si sente tanto in giro.

Per esempio nelle affermazioni “è stato trovato un bug nel programma X”

Oppure “È stato risolto un bug nel programma Y”

Sicuramente puoi intuire che riguarda degli errori che vengono trovati o corretti all’interno di un programma.

Però quali tipi di errori vengono identificati con il termine bug?

Riepiloghiamo tutti gli errori che abbiamo visto fino adesso:

  • gli errori formali che bloccano il compilatore che non riesce a produrre il file eseguibile del programma
  • gli errori di runtime che bloccano l’esecuzione del programma anche se il programma è corretto e di solito questo blocco è dovuto ad alcune circostanze particolari come per esempio l’inserimento di un input sbagliato da parte dell’utente.
  • Infine abbiamo parlato degli errori logici, che non bloccano nulla, ma l’output del programma non è corretto. Cioè dato un particolare input, l’output non è quello che si aspetta l’utente

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 e la sua introduzione nel gergo informatico deriva da un curioso aneddoto risalente al 1947.

Il 9 settembre 1947 il tenente Grace Hopper ed il suo gruppo stavano cercando la causa del malfunzionamento di un computer che creò non pochi problemi ai programmatori e progettisti che non riuscivano a comprenderne la causa.

Quando, con stupore, si accorsero che una falena si era incastrata tra i circuiti.

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, perché possono essere sfruttati da utenti malintenzionati per aggirare i controlli di accesso, al fine di ottenere privilegi e accesso ad informazioni non autorizzati.

In generale nessun software, anche quelli prodotti dalle software house più blasonate, è immune da bug.

Man mano che questi BUG vengono scoperti, anche dopo il rilascio e la commercializzazione del software, vengono corretti attraverso il rilascio periodico di aggiornamenti oppure attraverso nuove versioni del software.

Quello che il programmatore può fare per ridurre al minimo l’introduzione di bug nei suoi programmi può riassumersi in due punti:

  • 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. Quindi fare una fase preliminare allo sviluppo vero e proprio dove si progetta l’intera applicazione utilizzando alcuni strumenti che sono spiegati nell’ingegneria del software. Questi strumenti ti permettono di progettare il software nella maniera più accurata possibile prevedendo e gestendo anche i vari casi di fallimento del software.
  • sfruttare al meglio le funzionalità messe a disposizione dai linguaggi di programmazione per la gestione delle eccezioni e quindi degli errori.

Abbiamo visto cosa sono i bug. Adesso andiamo a capire come il programmatore può trovare questi bug all’interno del proprio codice.

Un potente strumento usato dai programmatori per trovare un bug nel codice è il DEBUG

Vediamo come funziona…

La rimozione degli errori di semantica e di runtime in un programma può essere svolta in primo luogo dal programmatore che può esaminare il proprio sorgente e capire dove risieda il problema.

Per applicazioni più grandi, in cui sono coinvolti più componenti, sono invece necessari degli strumenti più avanzati.

Questi strumenti sono chiamati DEBUGGER.

Il debugger consente di interrompere l’esecuzione di un programma in un punto preciso (chiamato break point o punto di arresto).

Questa interruzione permette l’analisi dei valori correnti delle variabili e di scorrere in maniera sequenziale e controllata dal programmatore ogni riga di codice a caccia di anomalie logiche di comportamento.

Quindi è il programmatore che prende il controllo e interrompe il programma su una particolare riga di codice e dice al programma quando andare avanti e di quante righe o istruzioni andare avanti.

Potenzialmente potrebbe bloccare l’esecuzione dopo ogni istruzione per controllarne il risultato.

In un certo senso il debug può essere paragonato al lavoro investigativo: sei messo di fronte agli indizi e devi ricostruire i processi e gli enti che hanno portato ai risultati che hai ottenuto.

Il Debug è una scienza sperimentale: dopo che hai avuto un’idea di ciò che può essere andato storto, modifichi il programma e lo provi ancora.

Se la tua ipotesi era corretta allora puoi predire il risultato della modifica e puoi avvicinarti di un ulteriore passo all’avere un programma funzionante.

Se la tua ipotesi era sbagliata devi ricercarne un’altra. Cioè continuare a cercare fino a che non trovi la causa del problema.

Come disse Sherlock Holmes:

“Quando hai eliminato l’impossibile ciò che rimane, per quanto improbabile, deve essere la verità”

Per fare debug del codice sorgente alla ricerca di un Bug solitamente si può fare in due modi:

  • L’inserimento di messaggi di tracciamento: cioè per esempio inserire delle stampe intermedie all’interno del codice dove andiamo a stampare e visualizzare il valore delle variabili durante tutto il processo dell’algoritmo.
  • Oppure l’utilizzo di un debugger: che come detto sono programmi speciali che vengono utilizzati per eseguire un altro programma e analizzare il suo stato e comportamento durante l’esecuzione delle singole istruzioni.

L’inserimento di messaggi di tracciamento comporta tipicamente una grossa perdita di tempo (perché devi decidere dove e quali messaggi inserire, analizzare l’output, attivarli e disattivarli, e non dimenticarsi di disattivarli o eliminarli).

Questa procedura io ti sconsiglio vivamente perché al termine del debug ti tocca ripulire l’intero codice da tutte le stampe che hai inserito, rischiando di dimenticarne qualcuna in giro!!!

I programmatori professionisti si affidano invece ai cosiddetti debugger perché sono più potenti e permettono di eseguire passo passo il programma permettendo in ogni momento di monitorare e anche modificare lo stato delle variabili mentre lo stesso è in esecuzione

I moderni ambienti di sviluppo (come per esempio Eclipse, IntelliJ o NetBeans) contengono debugger integrati che permettono di:

  • arrestare l’esecuzione del programma in punti precisi
  • ispezionare la pila di esecuzione
  • ispezionare il contenuto di variabili locali,
  • ispezionare lo stato degli oggetti creati
  • e molto altro ancora.

Ispezionare la pila di esecuzione significa che se un metodo per esempio chiama altri metodi usando il debug possiamo vedere per ogni metodo da chi è stato chiamato e a sua volta vedere tutta la pila delle chiamate fatte per arrivare alla chiamata di quel singolo metodo e tutto ciò live cioè mentre il programma è in esecuzione.

Se vuoi conoscere come applicare tutti questi strumenti ad esempi e casi pratici entra nel mio corso GRATUITO dove potrai capire come imparare a programmare in maniera semplice e divertende (senza imparare nessun codice a memoria) anche se stai partendo adesso ad imparare la programmazione o parti completamente da zero e non hai nessuna conoscenza di base. E per le prime 30 persone ci sarà anche una sorpresa!

Ok adesso concludiamo l’articolo con questa citazione:

Fare il debug è come essere il detective in un film giallo in cui tu sei anche l’assassino!

Filipe Fortes

P.S. Se vuoi imparare a programmare in maniera semplice e divertente seguendo soli 3 STEP CLICCA QUI e iscriviti al mio Corso GRATUITO.

P.P.S. Se ti è piaciuto questo articolo lascia un commento qui sotto   clicca MI PIACE e CONDIVIDI sulla mia pagina Facebook.