#DebuggingIl debugging, o in italiano “correzione degli errori”, è il processo di rimozione di errori logici o concettuali (in inglese, bug) dal proprio codice. Durante la fase di stesura del codice è molto probabile che vengano inseriti inavvertitamente dei bug; perciò familiarizzare e padroneggiare le tecniche di debugging è essenziale. Elenchiamo qui sotto le tecniche e gli strumenti più efficaci per fare debugging:
#Usare std::cout o std::cerrIl metodo più immediato (ma spesso il più efficace) per scovare i bug è quello di stampare informazioni intermedie durante l’esecuzione del codice.
Individuare un overflow con std::cout
Dati due interi 𝑎,𝑏 tali che 1≤𝑎,𝑏≤14, calcolare 𝑎𝑏.
Consideriamo il seguente codice che dovrebbe risolvere il problema:
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
int risposta = 1;
for (int i = 1; i <= b; i++)
risposta = risposta * a;
cout << risposta << endl;
}
Compiliamo e testiamo la soluzione con qualche input:
3 4
81
81 è la risposta corretta. Proviamo con un altro input:
14 14
-1282129920
Qualcosa è andato storto; proviamo a capire meglio cosa inserendo un std::cout nel ciclo:
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
int risposta = 1;
for (int i = 1; i <= b; i++) {
cout << i << ": " << risposta << endl;
risposta = risposta * a;
}
cout << risposta << endl;
}
14 14
1: 1
2: 14
3: 196
4: 2744
5: 38416
6: 537824
7: 7529536
8: 105413504
9: 1475789056
10: -813789696
11: 1491846144
12: -588990464
13: 344068096
14: 521986048
-1282129920
Come possiamo vedere, alla decima iterazione del ciclo la risposta diventa negativa. Questo è dovuto a un overflow: la variabile risposta di tipo int può contenere numeri fino a 231−1=2147483647, ma il numero che stiamo cercando di calcolare è più grande! Per risolvere questo problema, usiamo il tipo long long al posto di int, che contiene numeri fino a 263−1≈9.22×1018.
#include <iostream>
using namespace std;
int main() {
long long a, b;
cin >> a >> b;
long long risposta = 1;
for (int i = 1; i <= b; i++) {
risposta = risposta * a;
}
cout << risposta << endl;
}
14 14
11112006825558016
#Usare gli avvisi del compilatoreIl compilatore può segnalare alcune situazioni sospette durante la compilazione. Per abilitare i vari controlli possiamo usare le opzioni -Wall, -Wextra e -Wshadow:
g++ -Wall -Wextra -Wshadow programma.cpp
Rispettivamente:
•-Wall attiva molti warning generici, ad esempio quelli per una funzione non-void senza return o per una variabile dichiarata ma mai usata;•-Wextra attiva altri warning non inclusi in -Wall, come quello per un parametro della funzione mai utilizzato;•-Wshadow segnala quando una variabile dichiarata in uno scope interno oscura una variabile con lo stesso nome in uno scope esterno.Ricordiamo che gli avvisi non indicano necessariamente un errore, ma è buona pratica leggerli e correggerli quando possibile.
Leggere un warning sul valore restituito
Una funzione con tipo di ritorno diverso da void deve restituire un valore. In questo codice manca il return, quindi il compilatore può segnalare che l’esecuzione raggiunge la fine della funzione senza restituire un valore:
int doppio(int x) {
x * 2;
}
programma.cpp: In function ‘int doppio(int)’:
programma.cpp:2:9: warning: statement has no effect [-Wunused-value]
2 | x * 2;
| ~~^~~
programma.cpp:3:1: warning: no return statement in function returning non-void [-Wreturn-type]
3 | }
| ^
La correzione è:
int doppio(int x) {
return x * 2;
}
Individuare una variabile shadowed
Consideriamo il seguente codice, in cui vogliamo calcolare il massimo di un vettore:
int massimo = a[0];
for (int i = 1; i < n; i++)
int massimo = max(massimo, a[i]);
cout << massimo << "\n";
programma.cpp:32:9: warning: declaration of ‘int massimo’ shadows a previous local [-Wshadow]
32 | int massimo = max(massimo, a[i]);
| ^~~~~~~
programma.cpp:1:5: note: shadowed declaration is here
29 | int massimo = a[0];
| ^~~~~~~
La variabile massimo dichiarata nel ciclo nasconde quella esterna. Inoltre, nella sua inizializzazione viene usata prima di essere inizializzata. Il massimo esterno non viene aggiornato.