- Classificare gli errori: sintassi, warning, runtime e logici.
- Leggere e interpretare i messaggi di gcc.
- Usare -Wall e -Wextra per individuare codice sospetto.
- Applicare un metodo ordinato per correggere un programma.
I tipi di errore
Concetto: bug | Obiettivo: riconoscere e correggere
Chi programma sbaglia di continuo: è normale. Conta saper riconoscere il tipo di errore, perché da quello dipende dove cercare.
| Tipo | Quando si manifesta | Esempio |
|---|---|---|
| Errore di compilazione | Il compilatore si ferma e non crea l'eseguibile. | punto e virgola mancante, variabile non dichiarata |
| Warning | Il programma si compila, ma il codice è sospetto. | formato %d usato con un float |
| Errore del linker | Dopo la compilazione, durante il collegamento. | funzione usata ma mai definita (undefined reference) |
| Errore a runtime | Il programma parte e poi si interrompe. | divisione per zero |
| Errore logico | Il programma funziona ma dà risultati sbagliati. | precedenza degli operatori sbagliata |
Leggere i messaggi del compilatore
Formato: file:riga:colonna: tipo: messaggio | Regola: si parte dal primo
Un punto e virgola dimenticato:
#include <stdio.h>
int main(void)
{
int x = 10
printf("%d\n", x);
return 0;
}err1.c: In function 'main':
err1.c:6:5: error: expected ',' or ';' before 'printf'
6 | printf("%d\n", x);
| ^~~~~~Il messaggio indica file (err1.c), riga (6), colonna (5), il tipo (error) e la spiegazione. Nota che il compilatore segnala la riga 6, mentre il punto e virgola manca alla riga 5: l'errore si scopre solo quando arriva l'istruzione successiva. Se la riga indicata sembra corretta, guarda quella precedente.
Un nome scritto male:
#include <stdio.h>
int main(void)
{
int numero = 4;
printf("Il doppio vale %d\n", nmero * 2);
return 0;
}err2.c: In function 'main':
err2.c:6:35: error: 'nmero' undeclared (first use in this function); did you mean 'numero'?
6 | printf("Il doppio vale %d\n", nmero * 2);
| ^~~~~
| numeronmero non è stato dichiarato; gcc propone anche il nome simile numero. I messaggi qui riportati sono quelli di gcc 13 in inglese; il testo può variare leggermente tra versioni.
- Leggi il primo errore: gli altri possono esserne una conseguenza.
- Vai alla riga indicata e a quella precedente.
- Correggi un solo problema e ricompila.
- Ripeti finché non restano errori.
I warning e le opzioni -Wall -Wextra
Opzioni: -Wall -Wextra | Regola: mai ignorare un warning
#include <stdio.h>
int main(void)
{
float prezzo = 9.5f;
printf("Prezzo: %d\n", prezzo);
return 0;
}err3.c: In function 'main':
err3.c:6:22: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
6 | printf("Prezzo: %d\n", prezzo);
| ~^ ~~~~~~
| | |
| int double
| %fIl programma si compila, ma %d con un float stampa un valore privo di senso. Il warning ti ha avvisato prima ancora di eseguire il programma: per questo si compila sempre con -Wall -Wextra e si tratta ogni warning come un errore da correggere.
Errori a runtime ed errori logici
Runtime: il programma si interrompe | Logici: risultato sbagliato
Errore a runtime. Questo programma si compila senza problemi, ma se l'utente inserisce 0 come divisore si interrompe:
#include <stdio.h>
int main(void)
{
int a, b;
printf("Dividendo: ");
scanf("%d", &a);
printf("Divisore: ");
scanf("%d", &b);
printf("Risultato: %d\n", a / b);
return 0;
}Il C non controlla i dati al posto tuo: la divisione intera per zero termina il programma (su Linux la shell mostra un messaggio come Floating point exception). Imparerai a prevenire il problema con le condizioni.
Errore logico. Qui la media di 8 e 6 dovrebbe valere 7:
#include <stdio.h>
int main(void)
{
int a = 8, b = 6;
float media = a + b / 2;
printf("Media: %.1f\n", media);
return 0;
}Media: 11.0Nessun avviso, ma il risultato è sbagliato: b / 2 viene calcolato prima della somma. La correzione è (a + b) / 2.0.
Come si cercano gli errori logici:
- inserisci dati di cui conosci già il risultato e confronta;
- aggiungi
printfdi controllo che mostrino i valori intermedi delle variabili; - spiega il codice a voce, riga per riga (rubber duck debugging);
- usa il debugger di VS Code: un breakpoint ferma il programma e ti fa vedere le variabili passo dopo passo.
Laboratorio guidato — Caccia agli errori
Obiettivo: correggere un programma con quattro errori di tipo diverso, un passo alla volta.
Software: VS Code, gcc, terminale. Tempo previsto: 30 minuti.
Prerequisiti: lezioni 1-5 completate.
Procedura passo passo
- Crea il file
da_correggere.ce copia il programma seguente, con gli errori:#include <stdio.h> int main(void) { int a, b float media; printf("Primo numero: "); scanf("%d", &a); printf("Secondo numero: "); scanf("%d", &b); mdia = a + b / 2.0; printf("Media: %d\n", media); return 0; } - Compila con
gcc -Wall -Wextra -std=c11 da_correggere.c -o da_correggeree leggi i messaggi: parti dal primo. - Correggi l'errore della riga 5 (manca il punto e virgola) e ricompila: i messaggi cambiano.
- Correggi il nome della variabile (
mdiadiventamedia) e ricompila: ora si compila, ma compare un warning. - Correggi il formato in
printf(%ddiventa%.1f) e ricompila: nessun avviso. - Esegui con i valori
7e8: il risultato non è la media attesa (7.5). È un errore logico. - Correggi l'espressione con le parentesi,
(a + b) / 2.0, ricompila ed esegui di nuovo.
Dopo il punto 2 (quattro errori; il primo è il più importante):
da_correggere.c: In function 'main':
da_correggere.c:6:5: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'float'
6 | float media;
| ^~~~~
da_correggere.c:11:18: error: 'b' undeclared (first use in this function)
11 | scanf("%d", &b);
| ^
da_correggere.c:11:18: note: each undeclared identifier is reported only once for each function it appears in
da_correggere.c:13:5: error: 'mdia' undeclared (first use in this function)
13 | mdia = a + b / 2.0;
| ^~~~
da_correggere.c:14:27: error: 'media' undeclared (first use in this function)
14 | printf("Media: %d\n", media);
| ^~~~~Dopo il punto 3:
da_correggere.c: In function 'main':
da_correggere.c:13:5: error: 'mdia' undeclared (first use in this function); did you mean 'media'?
13 | mdia = a + b / 2.0;
| ^~~~
| media
da_correggere.c:13:5: note: each undeclared identifier is reported only once for each function it appears in
da_correggere.c:14:21: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
14 | printf("Media: %d\n", media);
| ~^ ~~~~~
| | |
| int double
| %fDopo il punto 4 (si compila, ma con un warning):
da_correggere.c: In function 'main':
da_correggere.c:14:21: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
14 | printf("Media: %d\n", media);
| ~^ ~~~~~
| | |
| int double
| %fDopo il punto 5, eseguendo con 7 e 8 (risultato sbagliato):
Primo numero: 7
Secondo numero: 8
Media: 11.0Dopo il punto 7 (risultato corretto):
Primo numero: 7
Secondo numero: 8
Media: 7.5I messaggi di gcc possono differire leggermente per versione e lingua.
Verifica del risultato
| Controllo | Che cosa devi riconoscere |
|---|---|
| Punto e virgola | Il primo errore scompare quando lo aggiungi alla riga 5. |
| Nome della variabile | Il compilatore smette di segnalare mdia. |
| Formato | Il warning su %d scompare. |
| Risultato | Con 7 e 8 la media vale 7.5. |
- Dopo la prima correzione compaiono altri messaggi: è normale, ora il compilatore riesce a leggere più righe del programma.
- Il numero di riga non corrisponde all'errore: guarda anche la riga precedente.
- Il risultato continua a essere 11.0: hai corretto il formato ma non l'espressione, servono le parentesi.
Esercizio autonomo
Scrivi un programma sbagliato di proposito con tre errori di tipo diverso (compilazione, warning, logico) e fallo correggere a un compagno, che deve dirti di che tipo è ciascun errore.
Soluzione (da aprire solo dopo aver provato)
#include <stdio.h>
int main(void)
{
int a, b;
float media;
printf("Primo numero: ");
scanf("%d", &a);
printf("Secondo numero: ");
scanf("%d", &b);
media = (a + b) / 2.0;
printf("Media: %.1f\n", media);
return 0;
}Verifica dei concetti
Tipo: autovalutazione | Argomento: Errori e debug
- Qual è la differenza tra un errore di compilazione e un errore logico?
- Perché conviene correggere prima il primo errore segnalato?
- Perché il compilatore può segnalare una riga diversa da quella in cui hai sbagliato?
- A che cosa servono le opzioni
-Walle-Wextra? - Quali tecniche puoi usare per trovare un errore logico?
- Gli errori possono essere di compilazione, di collegamento, a runtime o logici; i warning segnalano codice sospetto.
- I messaggi di gcc indicano file, riga, colonna e causa.
- Si corregge un errore alla volta, partendo dal primo, e si ricompila.
- Gli errori logici si scoprono confrontando il risultato con quello atteso.
- Hai classificato gli errori per tipo.
- Hai letto i messaggi del compilatore.
- Hai usato i warning per prevenire errori.
- Hai corretto un programma con un metodo ordinato.
Parole chiave
I termini principali introdotti in questa guida sono: