[? DAN ARMADARAS:?] Ciao, Sono [? Dan Armadaras?]. Oggi, stiamo andando a essere guardando il debug. Non solo abbiamo intenzione di parlare di alcune tecniche, ma anche noi stiamo andando a guardare alcune delle caratteristiche contenute all'interno dell'IDE CS50 che permettono di eseguire il debug di un programma facilmente. Un esempio di qualcosa che può andare storto e in realtà è qualcosa che abbiamo già visto in precedenza. In questo caso, questo è un programma C che accetta un numero intero da parte dell'utente, divide per due, e fornisce l'uscita di nuovo all'utente. Ora da quello che abbiamo visto in precedenza in lezioni, sappiamo che questo sarà effettivamente causare specifici tipi di problemi di divisione quando abbiamo i numeri dispari. In particolare, ci limiteremo a buttiamo nulla dopo il punto decimale. Ora, sappiamo che questo sembra essere il caso. E se corriamo, possiamo confermare i nostri sospetti, primo, mediante la compilazione. E poi, correndo e l'inserimento di un numero dispari. Non è una novità. Ma questo è in realtà un esempio di un bug che può esistere all'interno di un programma più ampio che diventa più difficile da rintracciare. Anche se sappiamo che cosa l'edizione è, il vero nocciolo della questione potrebbe essere cercando di identificare in particolare in cui si verifica l'errore, identificazione di ciò che problema è, e successivo fissaggio. Quindi fornire questo come un esempio di ciò che potrebbe essere qualcosa che già sappiamo, ma possiamo essere sepolti all'interno di altri elementi del codice. Quindi, l'apertura di questo altra fonte file di codice come esempio, questo problema di divisione è ora parte di un programma più grande. Ancora potrebbe essere un po bit artificiosa, e noi potrebbe essere in grado di facilità identificare, soprattutto dato che siamo solo a discutere di questo. Ma possiamo capire che questo problema può esistere su una scala più ampia. Se compilo questo e ora eseguirlo, immettere un numero dispari, possiamo vedere che non otteniamo esattamente l'uscita che forse abbiamo aspettato. In questo caso particolare, potremmo dire che siamo vogliono contare tutti i numeri da uno fino a qualche numero specifico. E possiamo vedere che abbiamo hanno una varietà di questioni qui se stiamo producendo, semplicemente, 0 e 1 quando mettiamo a disposizione un ingresso di 5. Così sappiamo già che c'è un problema qui. Ma non possiamo sapere con precisione dove questo problema esiste realmente. Ora, uno dei modi in cui possiamo cercare di risolvere questo problema è qualcosa che abbiamo già stato introdotto a. Possiamo solo utilizzare su una scala più ampia. Sulla linea 14, abbiamo questa funzione printf, che ci permette di stampare lo stato dei vari pezzi di informazioni. E questo è qualcosa che si dovrebbe sfruttare all'interno del programma per cercare di capire esattamente che cosa è accadendo in varie linee di codice. Quindi, anche se questo non è il output finale che in realtà vogliono produrre fuori questo programma, abbiamo ancora potrebbe avere qualche di debug dichiarazioni dove siamo può cercare di capire esattamente ciò che sta accadendo all'interno del nostro codice. Quindi, in questo caso, lo farò printf con il tag di debug. In questo caso, si tratta solo una stringa di debug che sono up-putting in modo che diventi molto chiaro nel uscita del mio codice che cosa è che voglio mostrare. E l'uscita qui il numero che abbiamo calcolato. In questo caso, potrei vogliono sapere con precisione quello che sta accadendo, prima e dopo qualche calcolo specifico. Così potrei usare un printf prima e dopo quella linea di codice. In questo caso, ho potuto anche renderlo un po 'più chiaro dicendo di debug prima ed eseguire il debug dopo così che io non confondo con me stesso più linee che sembrano identici. Ora, se questo ricompila e corsa esso, immettere un numero come cinque di nuovo, possiamo vedere che abbiamo Ora uscita prima e dopo e scopriamo che non abbiamo fatto un chiaro divisione o chiaro con il numero che in realtà vogliamo fare. Ora, in questo caso, si tratta non proprio una uscita chiara. Non è proprio un risultato chiaro che vogliamo da questo particolare programma. E questo è, ancora, un pò forzato. Ma, forse, una delle cose che potremmo fare se la specifica detto che vogliamo dividere questo per 2 e aggiungere 1-- così in altre parole, vogliamo arrotondare up-- poi potremmo sapere che abbiamo potuto fare quella cosa particolare, in questo caso. Ora qui sappiamo che saremo in grado di aggiungere 1 al nostro numero dimezzato. Cerchiamo di ricompilare questo e confermare che questo si sta comportando il modo in cui vogliamo. Possiamo vedere che ora prima avendo, abbiamo il numero 5. Dopo aver, abbiamo il numero 3, che secondo la nostra specifica, è quello che volevamo fare. Ma se guardiamo al uscita qui, possiamo vedere che potremmo avere un altro bug del tutto, che è che stiamo iniziando il nostro conteggio da 0. Ora di nuovo, questo è qualcosa che abbiamo visto in passato e siamo in grado di risolvere abbastanza prontamente. Ma in questo caso, ci anche avuto il vantaggio di utilizzare l'istruzione printf direttamente all'interno del ciclo for sapere esattamente dove tale errore stava accadendo. Dichiarazioni così printf sono molto utile per aiutare a determinare dove, precisamente nel codice sorgente, un errore specifico si sta verificando. Ed è anche importante rendersi conto che, come stiamo scrivendo codice, potremmo avere assunzioni sullo stato di un programma. Oppure potremmo avere assunzioni su quale parte del programma in realtà è giusta o sbagliata quando in seguito come si costruisce su quel programma e renderlo parte di un complesso e programma più ampio che ci rendiamo conto che alcuni aspetti di che in realtà è bacato. Usando printf può davvero aiutare restringere e identificare le regioni di un programma che non può comportarsi esattamente il modo in cui aspettarsi, in base alle nostre supposizioni. Ma ci sono altri strumenti disponibile, così, che ci permettono di cercare di capire dove un errore si verifica e anche, in particolare, ciò che le cose sono accadendo all'interno del programma. Quindi, utilizzando printf è molto quando utile vogliamo di individuare specifiche aree di un programma che ha qualche bug. Ma diventa anche noioso dopo un po '. In questo caso, si tratta di un relativamente semplice programma con solo uno o due variabili. E diventa molto facile per noi stampare il valore di tali variabili nell'ambito del programma più grande. Ma potremmo avere un diverso programma che ha molte variabili. E non può essere abbastanza così facile da usare printf per cercare di valutare ciò che sta accadendo a ciascuno di tali variabili come il programma sta eseguendo. C'è un programma che esiste chiamato un programma di debugger. In questo caso, quella che ci sarà utilizzo è il debugger GNU, o GDB, che ci permette di ispezionare l'interno funzionamento di un programma in modo molto più maniera dettagliata. Siamo in grado di eseguire in realtà GDB dalla riga di comando qui semplicemente digitando GDB e la comando che vogliamo eseguire il debug. In questo caso, contare. Ora, in questo caso, si può vedere che ci porta a un messaggio che dice GDB. E possiamo davvero eseguire comandi a GDB di iniziare effettivamente esecuzione del programma, fermati in alcuni punti, valutare le variabili e ispezionare le variabili che esistere nello stato del programma in quel momento, e così via. Esso fornisce un sacco di potenza a noi. Ma si dà il caso che l'IDE CS50 anche fornisce una GUI o un utente interfaccia per GDB che ci permette di fare questo senza bisogno l'interfaccia a riga di comando di sorta o addirittura a tutti. Il modo in cui posso accedere a tale è quello di utilizzare il pulsante di debug in cima dell'IDE CS50. Ora, in passato, quello che abbiamo visto è che usiamo il comando Linea per compilare ed eseguire un programma. Il pulsante di debug fa entrambi questi passaggi. Ma anche si apre la scheda debugger all'estrema destra che ci permette di ispezionare una varietà di proprietà del programma come è in esecuzione. Se clicco il debug, in questo caso, si aprirà una nuova scheda nella console finestra al fondo. E si può vedere che questa scheda ha alcune informazioni in cima. E possiamo in gran parte ignorare questo. Ma una delle cose che vogliamo notare è che emette la stessa cosa che abbiamo otterrebbe se abbiamo cercato di fare eseguire su il programma C nella finestra del terminale. Qui, possiamo vedere che è in esecuzione clang, e ha una varietà di bandiere, e di compilare il nostro file count.c, che era la scheda selezionata al momento che mi ha colpito di debug. Quindi questo è molto utile perché ora utilizzando il pulsante di debug, possiamo contemporaneamente compilare e poi eseguire il programma che in realtà desidera eseguire. Uno dei flag che è importante, in questo caso, abbiamo effettivamente stati utilizzando per più tempo ma anche appena fatto un po 'di mano agitando [incomprensibile], che è questo qui. In clang, dice -ggdb3. In questo caso, ciò che siamo raccontando clang, il nostro compilatore, è che vogliamo compilare il nostro programma. Ma anche fornire quali sono chiamato informazioni sui simboli in modo che il compilatore ha effettivamente accesso a un sacco di informazioni di base contenuta all'interno del programma. Più in particolare, il numero di funzioni che ho, i nomi di queste funzioni, le variabili, i tipi che tali variabili sono, e una varietà di altre cose che aiutano il debugger eseguire il suo funzionamento. Ora c'è un'altra cosa questo è importante ricordare quando stiamo discutendo in esecuzione un programma in questo modo. Notare che ci sono effettivamente portato una nuova scheda nella nostra console lungo il fondo. Non abbiamo più interagire direttamente con la finestra di terminale. Ma questa nuova scheda è in realtà una finestra di terminale. È solo specifico per la gestione programma che abbiamo creato. Si noti che al fondo, in combinazione con qualche uscita dal clang compilatore e GDB, che possiamo in gran parte ignorano, in realtà mostra l'output di il nostro programma in fondo. Ora è importante rendersi conto che questo in realtà una finestra vi mostrerà il Uscita dal programma ma anche in grado di accettare l'input per tale programma, pure. Così nota che indica inserisci un numero, che è la stessa uscita che avevamo aveva nella finestra terminale prima. Ma è ora mostrata in questa nuova scheda. Posso inserire un numero. E sarà davvero funzione di come ci aspettiamo ci mostra la nostra di debug, di uscita, l'uscita che potrebbe essere bacato, come abbiamo visto prima. E in fondo, è in realtà ha un po 'di output aggiuntivo dal PIL solo dicendo che questo programma è stato completato. Ora, come si è visto in questo particolare corsa attraverso, non era particolarmente utile perché anche anche se abbiamo avuto il menu del debugger venire up, questo era ancora un programma in esecuzione. In nessun punto ha fatto in realtà sospendere l'esecuzione per noi per poter ispezionare tutte le variabili contenute all'interno. C'è un'altra cosa che dobbiamo fare per per arrivare GDB a riconoscere che vogliamo per sospendere l'esecuzione del programma e non solo permettono di procedere normalmente come si farebbe in qualsiasi altro caso. Per sospendere l'esecuzione, ad un certo linea specifica, abbiamo bisogno di creare ciò che è chiamato un punto di interruzione. E un punto di rottura è molto facilmente creati in questo CS50 IDE prendendo il mouse e cliccando direttamente alla sinistra da un certo numero di linea specifico. Una volta che lo faccio, un puntino rosso appare, che indica che quella linea è ormai un punto di rottura. E la prossima volta che ho eseguito GDB, è si fermerà l'esecuzione a quel punto di rottura quando raggiunge quella riga di codice. Ora, questo è un importante cosa da realizzare che non è necessariamente la caso che ogni riga di codice è in realtà accessibile. Se dovessi creare una funzione qui, per example-- F-- vuoto e basta fare una linea di stampa qui-- ciao world-- se non ho mai chiamare questa funzione, sarà il caso che, se ho impostato un punto di rottura qui, la funzione non verrà mai chiamato. E quindi, questo particolare punto di rottura non sarà mai realmente in pausa esecuzione del programma. Quindi diciamo che creo correttamente un punto di interruzione su alcuni riga di codice che sarà effettivamente eseguito. Ora, in questo caso, questo è il prima riga nella funzione principale. Così sarà certamente il caso che, non appena comincio esecuzione, la prima linea sarà raggiunto. GDB si sospendere l'esecuzione. E poi, sarò in grado di interagire con il debugger. È possibile impostare linee multiple come punti di interruzione, se si desidera. Possiamo anche creare una line up qui in questo segmento di codice che non verrà mai raggiunto. E possiamo anche impostare un ulteriore seguito. Il motivo per cui ci sarebbe vuole fare questo faremo andare in un po 'di più dettaglio in un attimo. Quindi per ora, vorrei solo disattivare questi punti di interruzione aggiuntivi in modo che possiamo guardare a quello che succede quando ho una sola pausa punto nel mio programma. Ho fatto un po ' modifiche a questo programma. Quindi ho bisogno di salvarlo. Farò clic di debug in modo che possa iniziare la compilazione e poi esecuzione del debugger. Vedremo che, dopo i momenti, i linea che abbiamo scelto come la rottura punto è evidenziato in giallo. Possiamo anche notare che nel superiore destro del pannello di debug che l'icona di pausa si è trasformata in un piccola icona di gioco. Ciò significa che abbiamo la pausa esecuzione, in questo caso particolare. E premendo il pulsante Play sarebbe ci permettono di riprendere l'esecuzione a quel punto specifico. Si noti che ci sono un paio di altri pulsanti disponibili in questo pannello di debug, anche. Scavalcare, che mi permette di eseguire tale una riga di codice e passo verso quella linea al successiva, che, in questo caso, significherebbe che il printf istruzione viene eseguita. E sarà poi una pausa esecuzione sulla linea 13, in questo modo. E c'è anche un passo in funzione, che è utile se ho creato altri funzioni in altre parti del codice sorgente. E voglio entrare in quelle funzioni, piuttosto che eseguire tale funzione nel suo complesso. Ma vedremo più al passo in funzione in un attimo. Ora notate alcune altre cose che in realtà esistere all'interno di questo pannello di debug. Abbiamo questo pannello chiamato Stack di chiamate, che ci mostra dove esattamente siamo. In questo caso, siamo dentro della funzione principale. Il nostro script si chiama count.c. E ci capita di essere su linea 13, prima colonna, che è esattamente ciò che la regione evidenziata del codice sorgente indica, pure. Ora si noti che questo dimostra anche nella sezione variabile locale tutte le variabili che esistere all'interno di questa funzione. E 'importante notare che tutte le variabili appariranno in questa variabile locale sezione all'interno di una funzione, ancor prima che siano definite. Possiamo vedere qui che abbiamo una variabile chiamato num, ha un valore di default di 0, ed è di tipo int. Ora, prima abbiamo effettivamente inizializzare tutte queste variabili, non siamo necessariamente garantito a vedere il valore 0. E a seconda di altre esecuzioni che sia stata eseguita e lo stato della vostra memoria quando in realtà esegue questo programma, si potrebbe scoprire che si non vedono valori di 0 e, invece, alcuni altri numeri folli. Ma non ti preoccupare di questo. Non sarà rilevante fino effettivamente inizializzare il valore. Ora, in questo caso, possiamo vedere che Ho effettuato alcune uscite. E sto, in questo momento, una pausa di esecuzione. Ma in questo caso, ciò che Ho molta voglia di fare è per ora un passo su questa linea di codice in modo che io possa realmente chiedere all'utente che int che vogliamo usare nel nostro programma. Ora, in questo caso, quando Mi ha colpito scavalcare, avviso che la pausa, o meglio il Resume tasto è cambiato a questo pulsante Pausa perché questo codice è effettivamente in esecuzione. Che cosa sta succedendo in questo momento è che si tratta ci aspetta di inserire i dati come possiamo vedere dal nostro testo di output al fondo. Così adesso, questo è in realtà non in pausa, anche se, una sorta di, appare di essere, perché non succede niente. Ma si dà il caso che, in il mio caso specifico sulla linea 13, Sto aspettando l'input dell'utente. E così GDB non è in grado di ispezionare un programma è in esecuzione. Ora la prossima volta che entro in un po ' input-- quindi mi inserisco quel numero 5, come abbiamo visto nella past-- premere Invio, e noi notare che, immediatamente, pause GDB e, ancora una volta, mette in evidenza la riga successiva. Ma si noti che ora, come un risultato della nostra introduzione di un valore, abbiamo aggiornato il valore interno delle nostre variabili locali, che è molto utile per sapere con precisione cosa che numero era in memoria. Ora posso permettere che questo programma di continuare giocare fino alla fine della sua esecuzione colpendo Riprendi. Possiamo vedere che molto rapidamente fa la finitura programma in esecuzione con la stessa uscita che avuto prima, il debugger si chiude, e ora questo programma si è fermato completamente. Mostro che solo per il Ai fini di vedere cosa succede quando abbiamo effettivamente colpito Riprendi. Ma in realtà stiamo per voglia di tornare in questo programma in modo da poter cercare di eseguire il debug esattamente ciò che sta accadendo. Ora che sto utilizzando il debugger, posso non hanno bisogno di queste dichiarazioni di debug printf. Così ho potuto rimuoverli come farò ora solo per tornare al nostro codice più semplice che abbiamo avuto un momento fa. Ora, quando salvo la programmare ed eseguirlo, sarà, ancora una volta, andare a quella iniziale punto che ho avuto sulla linea 11 rompere. E sarò in grado di ispezionare le mie variabili voglio fare. Si dà il caso che questo parte non è molto interessante, E so che sto andando stampare questa dichiarazione. Si prega di inserire un numero. E poi, so che sto andando chiedere all'utente per quella intero. Quindi, forse, io in realtà voglio spostare la mia punto rompere un po 'più in basso. È possibile rimuovere punti di interruzione cliccando, ancora una volta, direttamente a fianco di tale numero di riga. Quel punto rosso scompare, a indicare che quel punto di rottura è ormai andato. Ora, in questo caso, esecuzione è stata sospesa. E quindi non è in realtà sta per riprendere in quel caso particolare. Ma posso impostare una pausa puntare un po 'più tardi. E quando mi riprendo la mia ora codice, riprenderà e raccontare il punto di che punto di rottura. Ancora una volta, mi ha colpito Riprendi. Non sembra nulla sta accadendo. Ma questo è perché il mio codice è in attesa di input. Io entrerò un numero 5, premere Invio, e ora il prossimo punto di interruzione sarà colpito. Ora, in questo caso, questo è la linea di codice che, prima, sapevamo è capitato di essere buggy. Quindi cerchiamo di valutare ciò che accade in questo particolare momento. Quando una riga viene evidenziata, questo linea non è ancora stata eseguita. Quindi, in questo caso, possiamo vedere che ho un numero, che Ho un intero chiamato num che ha un valore 5. E ho intenzione di essere l'esecuzione po 'di matematica su quel numero. Se faccio un passo su quella, possiamo notare che il valore per i num è cambiato secondo la aritmetica che abbiamo effettivamente fatto. E ora che siamo all'interno di questo ciclo for o adesso che il ciclo for si è evidenziato, vediamo che abbiamo un nuovo variabile i chiamati che sta per essere utilizzato in tale ciclo for. Ora ricordate prima che io ha detto che a volte sei andando a vedere un qualche tipo di folle numeri come predefinito prima che il numero o che variabile è realmente inizializzato. Possiamo vedere che proprio qui in questa variabile chiamato i, che non ha ancora stato inizializzato al momento di evidenziare. Ma possiamo vedere che ha qualche numero che non avremmo fatto aspettare. Va bene. Non ti preoccupare perché non abbiamo in realtà inizializzato tale numero fino a quando ho passo su questa linea, il valore i è stato inizializzato al valore 1. Quindi, per vedere che che in realtà il caso, facciamo un passo sopra. Ora possiamo vedere che quella linea è stata eseguita. Ed ora stiamo evidenziando questa linea printf. Ed ora possiamo vedere come i nostri valori di I e 3 sono cambiate nel tempo. Ciò è molto utile per fare, infatti, è quello di scavalcare le linee più volte. E si può trovare ciò che realmente accade all'interno del vostro ciclo for e cosa accade al variabili all'interno di quel ciclo for come che l'esecuzione del programma si verifica un passo alla volta. Ora, a questo punto, io scavalcato appena sufficiente che ora sono alla fine del mio programma. Se faccio un passo oltre che, lo farà in realtà cessare esecuzione come abbiamo visto in passato. Permettetemi di riavviare questo, ancora una volta, in modo da che posso indicare qualcos'altro fuori, anche. In questo caso, è ora mi chiede, ancora una volta, per un numero, che Io, di nuovo, entrare. Ma questa volta, ho intenzione di entrare in un numero più grande in modo che il ciclo for sarà iterare più volte. In questo caso, ho intenzione per immettere un valore di 11. Ora di nuovo perché avevo impostato un punto di interruzione alla linea 15, sta andando a evidenziare quella linea. Possiamo vedere che il nostro numero 11 è correttamente rappresentata nelle nostre variabili locali. Scavalcando che, possiamo ora vedere cosa succede al nostro valore di i come si procede all'interno di questo per ciclo. Esso viene incrementato ogni volta che raggiungere la cima di quella per ciclo. Ora, una delle cose che potrebbero essere utile per fare durante l'esecuzione di questo programma è per me realmente cambiare le variabili midstream per vedere cosa accade al mio programma. In questo caso, posso effettivamente doppio clic sul valore. Notare che diventa un campo di testo. Ora posso entrare diverso valorizzare tutto per vedere come si comporta il mio programma quando ho cambiato quella variabile. Ora, in questo caso, la variabile Io ora contiene il valore 10. Ma il programma è ancora pausa in esecuzione. Quando faccio un passo oltre, vedo che il valore i, che sono entrato come 10, non è maggiore del valore di num, che provoca subito il ciclo for per fermare l'esecuzione. Ora che non è l'unico motivo per cui si farebbe voler modificare la variabile in luogo. Oppure potresti tentare di modificarlo così che si può continuare esecuzione di un ciclo oppure in modo che è possibile modificare un valore prima che raggiunge un insieme specifico di aritmetica che si sta per eseguire. Quindi, ora che abbiamo effettivamente cambiare il valore di i come il programma era in esecuzione, ha causato il ciclo for per uscire prematuramente a causa, tutto ad un tratto, i successo di essere maggiore del valore di num, il che significa che quella per il ciclo non è più necessario da eseguire. Inoltre, è successo di essere il caso che abbiamo cambiato il valore di i quando la linea 17 è stata evidenziata, che era il punto nel tempo che il ciclo di esecuzione era in realtà in corso di valutazione. Se avessi cambiato il valore di i su una linea diversa, dico 19, avremmo visto diverso comportamento perché la linea 19 sarebbe hanno eseguito prima del ciclo condizione è stata rivalutata. Ora, a questo punto, sono, ancora una volta, alla fine di questo programma. E posso permettere che questo procedere alla consentire il mio programma per uscire in modo naturale. Ma ci sono un paio di cose che sono importanti per portare via da questa particolare discussione. È necessario valutare le proprie ipotesi su come il codice deve essere comportarsi. Ogni volta che si pensa che qualche pezzo di codice si sa capita di lavorare, che potrebbe essere una bandiera rossa per andare indietro e valutare, ed essere sicuri che la vostra assunzione di come quel codice è in funzione in realtà è vero per come è espressi nel codice sorgente. Ma ancora più a punto è stato, quando stiamo usando il debugger, si può mettere i punti di interruzione a diverse linee di codice, che farà sì che il debugger sospendere l'esecuzione in ciascuna di queste linee in modo da poter valutare la memoria o addirittura cambiare in posizione. E ancora una volta, ricordate che è possibile creare più punti di interruzione in modo che si possono anche riprendere l'esecuzione, saltare su ampie porzioni di codice, e sarà in pausa automaticamente al successivo punto di interruzione. Ci sono in realtà più avanzate caratteristiche del debugger, pure. Ma dovremo fare riferimento per alcuni video successive al fine di prendere in giro davvero a parte come utilizzare tali funzioni particolari. Per ora, grazie molto per la visione. E buona fortuna debug.