Appendice F Utilizzare il debugger di Visual Studio Obiettivi Essere in grado di impostare i breakpoint e di eseguire un programma nel debugger. Essere in grado di utilizzare il comando Continue per continuare l'esecuzione. ■ Essere in grado di utilizzare la finestra Locals per visualizzare e modificare i valori delle variabili. ■ Essere in grado di utilizzare la finestra Watch per valutare le espressioni. ■ Essere in grado di utilizzare i comandi Step Into, Step Out e Step Over per controllare l'esecuzione. ■ Essere in grado di utilizzare la finestra Autos per visualizzare le variabili che vengono utilizzate nei comandi prossimi a quello corrente. ■ ■ F.1 Introduzione Nel Capitolo 2 avete imparato che esistono due tipi di errori, ovvero, errori di compilazione ed errori logici ed avete imparato come eliminare i primi dal vostro codice. Gli errori logici (denominati anche bug) non impediscono che un programma venga compilato con successo, ma possono far sì che il programma stesso produca dei risultati erronei al momento dell'esecuzione. Molti produttori di compilatori C forniscono anche un software noto come debugger, che vi permette di controllare l'esecuzione dei vostri programmi per individuare ed eliminare gli errori logici. Il debugger si rivelerà come uno dei più importanti strumenti per lo sviluppo di programmi. Questa appendice illustra le caratteristiche chiave del debugger del Visual Studio. L'Appendice H illustra le caratteristiche e le possibilità offerte dal debugger GNU. F.2 I breakpoint ed il comando Continue Iniziamo il nostro studio del debugger investigando i breakpoint, che sono dei marcatori che possono essere impostati in corrispondenza di ogni riga eseguibile del codice. Quando l'esecuzione del programma raggiunge un breakpoint, viene effettuata una pausa, consentendovi di esaminare i valori delle variabili per aiutarvi a determinare se ci sia un errore logico. Per esempio, potete esaminare il valore di una variabile che memorizza il risultato di un calcolo per determinare se quest'ultimo sia stato eseguito correttamente. Si noti che tentare di inserire un breakpoint in corrispondenza di una linea di codice che non sia eseguibile (come un commento) imposterà il breakpoint alla prossima linea eseguibile di codice nella relativa funzione. Per illustrare le caratteristiche del debugger, utilizziamo il programma in Figura F.1, che trova il massimo di tre interi. L'esecuzione inizia in corrispondenza del main (righe 8–22). I tre interi vengono inseriti con la scanf (riga 15). In seguito, gli interi sono passati a maximum (riga 19), che ne determina il massimo. Quest'ultimo è il valore restituito al main dal comando return in maximum (riga 38). Il valore restituito viene poi visualizzato per mezzo del comando printf (riga 19). 1 /* Fig. F.1: figG_01.c 2 Trovare il massimo di tre interi */ 3 #include <stdio.h> 4 5 int maximum( int x, int y, int z ); /* prototipo di funzione */ 6 7 /* l’esecuzione del programma inizia dalla funzione main */ 8 int main( void ) 9{ 10 int number1; /* primo intero */ 11 int number2; /* secondo intero */ 12 int number3; /* terzo intero */ 13 printf( "Enter three integers: " ); 14 Copyright (C) 2010 Apogeo 15 scanf( "%d%d%d", &number1, &number2, &number3 ); 16 17 /* number1, number2 e number3 sono gli argomenti 18 della chiamata di funzione a maximum */ 19 printf( "Maximum is: %d\n", maximum( number1, number2, number3 ) ); 20 21 return 0; /* indica che il programma è terminato con successo */ 22 } /* fine della funzione main */ 23 24 /* Definizione della funzione maximum */ 25 /* x, y e z sono i parametri */ 26 int maximum( int x, int y, int z ) 27 { 28 int max = x; /* si assuma che x sia il massimo */ 29 if ( y > max ) { /* se y è maggiore di max, assegna y a max */ 30 31 max = y; 32 } /* end if */ 33 if ( z > max ) { /* se z è maggiore di max, assegna z a max */ 34 35 max = z; 36 } /* end if */ 37 return max; /* max è il valore massimo */ 38 39 } /* fine della funzione maximum */ Fig. F.1 | Determinare il massimo di tre interi. Creare un progetto in Visual C++ 2008 Express Nei passi seguenti creerete un progetto che includa il codice della Figura F.1. 1. Selezionate File > New > Project... in Visual C++ 2008 Express per visualizzare la finestra di dialogo New Project. 2. Nell'elenco Project types:, selezionate Win32 e nell'elenco Templates, selezionate Win32Console Application. 3. Inserite un nome per il vostro progetto nel campo Name: e specificate dove volete salvarlo nel vostro computer nel campo Location:, poi fate clic su OK. 4. Nella finestra di dialogo Win32 Application Wizard fate clic su Next >. 5. In Applicationtype: selezionate Console application e in Additional options: selezionate Empty project, poi fate clic su Finish. 6. Nella finestra Solution Explorer fate clic con il tasto destro sulla cartella Source Files del vostro progetto e selezionate Add > Existing Item... per visualizzare la finestra di dialogo Add Existing Item. 7. Individuate la cartella contenente il codice degli esempi dell'Appendice H, selezionate il file con il codice e fate clic su Add. Abilitare la modalità di debug (Debug Mode) e inserire i breakpoint Nei passi seguenti utilizzerete i breakpoint e diversi comandi del debugger per esaminare il valore della variabile number1 dichiarata in Figura F.1. 1. Abilitare il debugger. Il debugger normalmente è abilitato per default. Nel caso in cui non lo sia, dovrete cambiare le impostazioni del menu a tendina Solution Configurations (Figura F.2) situato nella barra degli strumenti. Per far ciò, fate clic sulla freccia verso il basso del menu a tendina e poi selezionate Debug. Fig. F.2 | Abilitare il debugger. Copyright (C) 2010 Apogeo 2. Inserire i breakpoint. Aprite il file figG_01.c facendo doppio clic su di esso nel Solution Explorer. Per inserire un breakpoint, fate clic dentro la barra grigia alla sinistra della finestra del codice (margin indicator bar in Figura F.3) in prossimità della riga in cui volete effettuare l'interruzione oppure fate clic con il tasto destro su quella linea di codice e selezionate Break- point > Insert Breakpoint. Potete impostare tanti breakpoint quanti ne saranno necessari. Impostate i breakpoint in corrispondenza delle righe 14 e 19 del vostro codice. Un circoletto rosso apparirà sul barra che indica il margine nel punto in cui avete fatto clic, in modo da indicare che un breakpoint è stato impostato (Figura F.3). Quando il programma viene eseguito, il debugger effettua una pausa nell'esecuzione in corrispondenza di ogni linea che contiene un breakpoint. Si dice che il programma si trova in break mode quando il debugger lo mette in pausa. I breakpoint possono essere impostati prima di far girare un programma, mentre si trova in break mode e mentre è in corso di esecuzione. Fig. F.3 | Impostare due breakpoint. 3. Iniziare il debug. Dopo aver impostato i breakpoint nell'editor del codice, selezionate Build>Build Solution per compilare il programma, poi selezionate Debug > Start Debugging per iniziare il processo di debugging. [Nota: Se non compilate prima il programma, esso sarà comunque compilato nel momento in cui selezionerete Debug > Start Debugging.] Quando eseguite il debug di un'applicazione console, appare una finestra Command Prompt (Figura F.4) in cui potete specificare gli input del programma e vederne l'output. Il debugger entra in break mode nel momento in cui l'esecuzione raggiunge il breakpoint in riga 14. Fig. F.4 | Il programma Maximum Numbers in esecuzione. 4. Esaminare l'esecuzione del programma. In seguito all'ingresso in break mode in corrispondenza del primo breakpoint (riga 14), l'IDE diventa la finestra attiva (Figura F.5). La freccia gialla alla sinistra della riga 14 indica che tale linea contiene il prossimo comando da eseguire. Fig. F.5 | Esecuzione del programma sospesa in corrispondenza del primo breakpoint. Copyright (C) 2010 Apogeo 5. Utilizzare il comando Continue per riprendere l'esecuzione. Per riprendere l'esecuzione, selezionate Debug > Il comando Continue riprende l'esecuzione del programma fino al prossimo breakpoint o fino al raggiungimento della fine del main, a seconda di quale evento si verifichi per primo. Il programma continua ad eseguire ed effettua una pausa in corrispondenza della riga 15. Inserite i valori 22, 85, e 17 per i tre interi. Il programma esegue finché non si ferma al prossimo breakpoint (riga 19). Notate che, quando posizionate il vostro puntatore del mouse sul nome della variabile number1, il valore in essa contenuto viene visualizzato in un Quick Info box (Figura F.6). Come potete constatare, ciò può aiutarvi ad individuare degli errori logici nei vostri programmi. Continue. Fig. F.6 | Quick Info box che visualizza il valore di una variabile. 6. Impostare un breakpoint in corrispondenza della parentesi di chiusura del main. Impostate un breakpoint in riga 22 del codice sorgente facendo clic sulla barra indicante il margine alla sinistra della riga 22. Questo fatto impedirà al programma di chiudersi immediatamente dopo aver visualizzato il suo risultato. Quando non ci sono più breakpoint in corrispondenza dei quali sospendere l'esecuzione, il programma eseguirà fino al completamento e la finestra del Command Prompt verrà chiusa. Se non impostate questo breakpoint, non sarete in grado di vedere l'output del programma prima che la finestra della console venga chiusa. 7. Continuare l'esecuzione del programma. Utilizzate il comando Debug > Continue per eseguire il codice fino al prossimo breakpoint. Il programma visualizza il risultato del suo calcolo (Figura F.7). Fig. F.7 | Output del programma. 8. Eliminare un breakpoint. Fate clic sul breakpoint nella barra indicante il margine. 9. Completare l'esecuzione del programma. Selezionate Debug > Continue per eseguire il programma fino al suo completamento. In questa sezione, avete imparato ad attivare il debugger e ad impostare i breakpoint in modo da esaminare i risultati del codice mentre il programma è in esecuzione. Avete anche imparato come continuare l'esecuzione in seguito alla sospensione di quest'ultima in corrispondenza di un breakpoint e come rimuovere i breakpoint. F.3 Le finestre Locals e Watch Nella sezione precedente avete imparato che la caratteristica Quick Info vi permette di esaminare il valore di una variabile. In questa sezione imparerete ad usare la finestra Locals per assegnare nuovi valori alle variabili mentre il vostro programma è in esecuzione. Userete la finestra Watch anche per esaminare il valore di espressioni più complesse. Copyright (C) 2010 Apogeo 1. Inserire i breakpoint. Cancellate i breakpoint esistenti selezionando Debug > Delete All Breakpoints. Poi impostate un breakpoint in corrispondenza della riga 19 del codice sorgente facendo clic nella barra che indica il margine alla sinistra della riga 19 (Figura F.8). Fig. F.8 | Impostare i breakpoint in corrispondenza delle righe 25 e 28. 2. Iniziare il debugging. Selezionate Debug > Start. Inserite i valori 22, 85, e 17 in corrispondenza del prompt Enter three integers: e premete Enter in modo che il vostro programma legga i valori che avete appena inserito. 3. Sospendere l'esecuzione del programma. Il debugger entra in break mode in corrispondenza della riga 19 (Figura F.9). A questo punto la riga 15 ha letto i valori che avete inserito per number1 (22), number2 (85) e number3 (17). La riga 19 rappresenta il prossimo comando da eseguire. Fig. F.9 | L'esecuzione del programma è sospesa quando il debugger raggiunge il breakpoint della riga 19. 4. Esaminare i dati. In break mode potete esaminare il valore delle vostre variabili locali utilizzando la finestra Locals del debugger, normalmente visibile nella parte inferiore sinistra dell'IDE quando siete in modalità di debugging. Se non è visibile al momento, potete esaminare la finestra Locals, selezionando Debug > Windows > Locals. La Figura F.10 mostra i valori delle variabili locali del main, ovvero, number1 (22), number2 (85) e number3 (17). Fig. F.10 | Esaminare le variabili number1, number2 e number3. 5. Valutare le espressioni aritmetiche e booleane. Potete valutare le espressioni aritmetiche e booleane utilizzando la finestra Watch. Potete tenere visibili fino a quattro finestre Watch. Selezionate Debug > Windows > Watch > Watch 1. Nella prima riga della colonna Name digitate (number1 + 3) * 5, poi premete Enter. Il valore di questa espressione (125 Copyright (C) 2010 Apogeo in questo caso) viene mostrato nella colonna Value (Figura F.11). Nella riga successiva della colonna Name digitate number== 3, poi premete Enter. Questa espressione determina se il valore di number1 sia 3. Le espressioni contenenti l'operatore == (o ogni altro operatore relazionale o di uguaglianza) vengono considerate delle espressioni di tipo bool. Il valore dell'espressione in questo caso è false (Figura F.11), in quanto number1 al momento contiene 22, non 3. Fig. F.11 | Esaminare i valori delle espressioni. 6. Modificare i valori. In base ai valori inseriti dall'utente (22, 85 e 17), il numero massimo mandato in output dal programma dovrebbe essere 85. Tuttavia, potete utilizzare la finestra Locals per cambiare i valori delle variabili durante l'esecuzione del programma. Ciò può rivelarsi utile per sperimentare diversi valori e per individuare gli errori logici. Nella finestra Locals, fate clic sul campo Value nella riga corrispondente a number1 per selezionare il valore 22. Digitate 90, poi premete Enter. Il debugger cambierà il valore di number1 e visualizzerà il suo nuovo valore in rosso (Figura F.12). Fig. F.12 | Modificare il valore di una variabile. 7. Impostare un breakpoint in corrispondenza della parentesi di chiusura del main. Impostate un breakpoint in riga 22 del codice sorgente per impedire al programma di chiudersi immediatamente dopo aver visualizzato il suo risultato. Se non impostate questo breakpoint, non sarete in grado di vedere l'output del programma prima che la finestra della console si chiuda. 8. Riprendere l'esecuzione e vedere il risultato del programma. Selezionate Debug > Continue per continuare l'esecuzione del programma. La funzione main eseguirà fino al comando return statement in riga 21 e visualizzerà il risultato. Notate che il risultato sarà 90 (Figura F.13). Questo dimostra che il Passo 7 ha modificato il valore di number1 a 90 dal valore originale (85). Fig. F.13 | L'output visualizzato dopo la modifica del valore della variabile number1. 9. Interrompere la sessione di debugging. Selezionate Prompt. Rimuovete tutti i breakpoint rimanenti. Debug > Stop Debugging. Ciò chiuderà la finestra Command In questa sezione avete imparato ad utilizzare le finestre Watch e Locals per valutare le espressioni aritmetiche e booleane. Avete anche imparato come modificare il valore di una variabile durante l'esecuzione del programma. F.4 Controllare l'esecuzione utilizzando i comandi Step Into, Step Over, Step Out e Continue A volte eseguire un programma riga per riga vi può aiutare a verificare che il codice di una funzione venga eseguito correttamente ed a individuare e correggere gli errori logici. I comandi che imparerete in questa sezione vi permetteranno di eseguire una funzione riga per riga, di eseguire tutte le istruzioni di una funzione in un colpo solo o di eseguire solamente le istruzioni rimanenti di una funzione (nel caso in cui abbiate già eseguito alcune istruzioni dentro la funzione). Copyright (C) 2010 Apogeo 1. Impostare un breakpoint. Impostate un breakpoint in corrispondenza della riga 19 facendo clic sulla barra che indica il margine alla sinistra della riga. 2. Far partire il debugger. Selezionate Debug > Start. Inserite i valori 22, 85 e 17 al prompt Enter three integers:. L'esecuzione si fermerà quando il programma raggiungerà il breakpoint in corrispondenza della riga 19. 3. Utilizzare il comando Step Into. Il comando Step Into esegue la prossima istruzione nel programma (riga 19), poi si arresta immediatamente. Se quell'istruzione contiene una chiamata di funzione (come avviene in questo caso), il controllo viene trasferito all'interno della funzione chiamata. Ciò vi permette di eseguire singolarmente ogni comando all'interno della funzione per confermare l'esecuzione della funzione. Selezionate Debug > Step Into per entrare nella funzione maximum. In seguito selezionate nuovamente Debug > Step Into in modo che la freccia gialla si posizioni in corrispondenza della riga 28 come mostrato in Figura F.14. 4. Utilizzare il comando Step Over. Selezionate Debug > Step Over per eseguire il comando corrente (riga 28 in Figura F.14) e trasferire il controllo alla riga 30 (Figura F.15). Il comando Step Over si comporta come il comando Step Into quando la prossima istruzione da eseguire non contiene una chiamata di funzione. Vedrete come il comando Step Over differisca dal comando Step Into nel Passo 10. Fig. F.14 | Eseguire passo per passo la funzione maximum. Fig. F.15 | Eseguire il comando corrente nella funzione maximum. 5. Utilizzare il comando Step Out. Selezionate Debug > Step Out per eseguire le istruzioni rimanenti nella funzione e restituire il controllo alla prossima istruzione eseguibile (riga 21 in Figura F.1). Spesso, nelle funzioni più lunghe, vorrete controllare alcune linee chiave di codice per poi continuare nel debugging del codice del chiamante. Il comando Step Out vi permette di continuare l'esecuzione del programma nel chiamante senza dover eseguire riga per riga l'intera funzione chiamata. 6. Impostare un breakpoint. Impostate un breakpoint alla fine del main in corrispondenza della riga 22 della Figura F.1. Farete uso di questo breakpoint nel passo successivo. 7. Utilizzare il comando Continue. Selezionate Debug > Continue per continuare l'esecuzione fino al raggiungimento del prossimo breakpoint in corrispondenza della riga 22. Usare il comando Continue si rivela utile quando volete eseguire tutto il codice fino al prossimo breakpoint. Copyright (C) 2010 Apogeo 8. Arrestare il debugger. Selezionate Debug > Stop Debugging per terminare la sessione di debugging. Ciò provocherà la chiusura della finestra del Command Prompt. 9. Far partire il debugger. Prima di poter illustrare la prossima caratteristica del debugger, fate partire nuovamente il debugger. Avviatelo, come avete fatto nel Passo 2, ed inserite 22, 85 e 17 in risposta al prompt. Il debugger entrerà in break mode in corrispondenza della riga 19. 10. Utilizzare il comando Step Over. Selezionate Debug > Step Over (Figura F.16). Ricordatevi che questo comando si comporta come il comando Step Into quando la prossima istruzione da eseguire non contiene una chiamata di funzione. Nel caso in cui la prossima istruzione da eseguire contenga una chiamata di funzione, la funzione chiamata verrà eseguita nella sua interezza (senza effettuare pause nell'esecuzione di nessuna istruzione all'interno della funzione) e la freccia gialla avanzerà alla prossima linea eseguibile (dopo la chiamata di funzione) nella funzione corrente. In questo caso il debugger eseguirà la riga 19, situata nel main (Figura F.1). La riga 19 richiamerà la funzione maximum. In seguito il debugger effettuerà una pausa in corrispondenza della riga 21, ovvero, la prossima linea eseguibile nella funzione corrente, main. Fig. F.16 | Utilizzare il comando Step Over. 11. Arrestare il debugger. Selezionate tutti i breakpoint rimanenti. Debug > Stop Debugging. Ciò chiuderà la finestra Command Prompt. Rimuovete In questa sezione avete imparato come usare il comando Step Into del debugger per correggere le funzioni richiamate durante l'esecuzione del vostro programma. Avete visto come il comando Step Over possa essere utilizzato per oltrepassare una chiamata di funzione. Avete utilizzato il comando Step Out per continuare l'esecuzione fino alla fine della funzione corrente. Avete imparato anche che il comando Continue continua l'esecuzione fino al raggiungimento di un altro breakpoint o all'uscita dal programma. F.5 La finestra Autos La finestra Autos visualizza le variabili utilizzate nell'istruzione precedentemente eseguita (includendo il valore di ritorno di una funzione, se è il caso) e le variabili nella prossima istruzione da eseguire. 1. Impostare i breakpoint. Impostate i breakpoint in corrispondenza delle righe 14 e 19 nel main facendo clic sulla barra che indica il margine. 2. Utilizzare la finestra Autos. Avviate il debugger selezionando Debug>Start. Quando il debugger entra in break mode in corrispondenza della riga 14, aprite la finestra Autos (Figura F.17) selezionando Debug > Windows > Autos. Dato che abbiamo appena iniziato l'esecuzione del programma, la finestra Autos elencherà soltanto le variabili coinvolte nel prossimo comando da eseguire, ovvero in questo caso, number1, number2 e number3. Visualizzare il valore memorizzato in una variabile vi permette di verificare che il vostro programma stia manipolando le variabili correttamente. Notate che number1, number2 e number3 contengono valori negativi molto grandi. Tali valori, che possono differire ogni volta che il programma viene eseguito, sono i valori non inizializzati delle variabili. Questi valori imprevedibili (e spesso indesiderabili) sono la dimostrazione dell'importanza di inizializzare tutte le variabili in C prima del loro utilizzo. Copyright (C) 2010 Apogeo Fig. F.17 | La finestra Autos mentre visualizza i valori di number1, number2 e number3. 3. Continuare l'esecuzione. Selezionate Debug > Continue per eseguire il programma fino al raggiungimento del secondo breakpoint in corrispondenza della riga 19. Al prompt di input del programma, inserite i valori dei tre interi. La finestra Autos aggiornerà i valori di number1, number2 e number3 dopo la loro inizializzazione. I loro valori appariranno in rosso per indicare che sono stati appena modificati. (Figura F.18) Fig. F.18 | La finestra Autos mentre visualizza le variabili locali number1, number2 e number3. 4. Inserire dei dati. Selezionate Debug > Step ritorno della funzione maximum (Figura F.19). Over per eseguire la riga 19. La finestra Autos visualizza il valore di Fig. F.19 | La finestra Autos mentre visualizza il valore di ritorno della funzione maximum. 5. Arrestare il debugger. Selezionate Debug > Stop Debugging per terminare la sessione di debugging. Rimuovete tutti i breakpoint rimanenti. F.6 Conclusione In questa appendice avete imparato come inserire, disabilitare ed eliminare i breakpoint nel debugger di Visual Studio. I breakpoint vi permettono di effettuare delle pause nell'esecuzione del programma in modo da esaminare i valori delle variabili. Questa caratteristica vi aiuterà ad individuare e correggere gli errori logici nei vostri programmi. Avete visto come utilizzare le finestre Locals e Watch per esaminare il valore di un'espressione e per cambiare il valore di una variabile. Avete anche imparato i comandi Step Into, Step Over, Step Out e Continue che possono essere utilizzati per determinare se una funzione venga eseguita correttamente. Infine avete imparato come usare la finestra Autos per esaminare le variabili usate in particolare nel comando precedente ed in quello successivo. Esercizi di autovalutazione F.1 Completate gli spazi in ognuna delle seguenti frasi: a) Quando il debugger sospende l'esecuzione del programma in corrispondenza di un breakpoint, si dice che il programma sia in __________. b) La caratteristica __________ di Visual Studio 2008 vi permette di esaminare il valore di una variabile posizionando il mouse sul nome della variabile nel codice. c) Potete esaminare il valore di un'espressione usando la finestra __________ del debugger. Copyright (C) 2010 Apogeo d) Il comando __________ si comporta come il comando Step Into quando la prossima istruzione da eseguire non contiene una chiamata di funzione. F.2 Stabilite quali delle seguenti affermazioni sono vere e quali false. Nel caso siano false, spiegatene il motivo. a) Quando l'esecuzione del programma viene sospesa in corrispondenza di un breakpoint, la prossima istruzione da eseguire è quella dopo il breakpoint. b) Quando il valore di una variabile viene modificato, diventa giallo nelle finestre Autos e Locals. c) Durante il debugging il comando Step Out esegue le istruzioni rimanenti nella funzione corrente e restituisce il controllo del programma al punto in cui la funzione era stata richiamata. Risposte agli esercizi di autovalutazione F.1 a) break mode. b) Quick Info box. c) Watch. d) Step Over. F.2 a) Falso. Quando l'esecuzione del programma viene sospesa in corrispondenza di un breakpoint, il prossimo comando da eseguire è il comando corrispondente al breakpoint. b) Falso. Una variabile diventa rossa se il suo valore è stato modificato. c) Vero. Copyright (C) 2010 Apogeo