[Powered by Google Translate] [SEZIONE 5: meno confortevole]
[Nate Hardison, Harvard University]
[Questo è CS50.] [CS50.TV]
Quindi bentornato, ragazzi.
Benvenuti nella sezione 5.
A questo punto, dopo aver completato quiz 0 e aver visto come hai fatto,
potete sentire veramente bene, perché mi ha colpito molto per i punteggi in questa sezione.
Per i nostri spettatori online, abbiamo avuto un paio di domande
circa gli ultimi due problemi sul set problema - o per il quiz, piuttosto.
Così abbiamo intenzione di andare su quelli molto in fretta in modo che ognuno vede ciò che è accaduto
e come passare attraverso la soluzione reale piuttosto che la visualizzazione della soluzione stessa.
Stiamo per andare oltre l'ultimo paio di problemi molto in fretta, 32 e 33.
Così, ancora una volta, in modo che gli spettatori on-line può vedere questo.
>> Se si attiva al problema 32, che si trova a pagina 13,
13 su 16, problema 32 è tutto di swap.
Si trattava di scambiare due numeri interi.
E 'il problema che avevamo superato un paio di volte a lezione.
E qui, quello che ci chiede di fare è una traccia di memoria veloce.
Per inserire i valori delle variabili come sono in pila
come codice passa attraverso questa funzione swap.
In particolare, quello che stiamo guardando - Sto andando a mettere questo iPad verso il basso -
in particolare, quello che stiamo guardando è la linea numero 6 proprio qui.
Ed è numero 6 solo per contiguità con il problema precedente.
Quello che vogliamo fare è visualizzare o etichettare lo stato della memoria
come è al momento in cui si esegue questa linea 6,
che è di fatto un ritorno dalla nostra funzione di scambio proprio qui.
Se scorrere verso il basso qui, abbiamo visto che gli indirizzi di tutto nella memoria è stato fornito per noi.
Questo è molto fondamentale; torneremo ad esso in un attimo.
E poi qui in basso, abbiamo avuto un piccolo diagramma di memoria che stiamo andando a fare riferimento.
Ho effettivamente fatto questo sul mio iPad.
Quindi ho intenzione di alternare avanti e indietro tra l'iPad e il codice solo per riferimento.
>> Cominciamo. In primo luogo, concentriamoci sul primo paio di righe di principale proprio qui.
Per iniziare, abbiamo intenzione di inizializzare a 1 x e y a 2.
Quindi abbiamo due variabili intere, sono entrambi sta per essere inseriti nello stack.
Stiamo andando a mettere un 1 e un 2 in loro.
Quindi, se ho capovolto per il mio iPad, si spera, vediamo -
Apple TV mirroring, e ci andiamo. Va bene.
Quindi, se ho capovolto per il mio iPad,
Voglio inizializzare x a 1 e y 2.
Lo facciamo semplicemente scrivendo un 1 nella casella contrassegnata x
e 2 nella casella contrassegnata y. Abbastanza semplice.
Così ora torniamo al computer portatile, vedere cosa succede dopo.
Così questa linea successiva è dove le cose si fanno difficili.
Passiamo l'indirizzo di x e l'indirizzo di y come i parametri a e b per la funzione di scambio.
L'indirizzo di x e l'indirizzo di y sono cose che non possiamo calcolare
senza fare riferimento a questi punti proiettile fino qui.
E per fortuna, i primi due punti ci dicono esattamente quali siano le risposte.
L'indirizzo di x in memoria è 10, e l'indirizzo di y in memoria è 14.
Quindi questi sono i valori che vengono passati come a e b sulla cima nella nostra funzione di scambio.
Quindi, di nuovo, tornando al nostro schema, posso scrivere un 10 in un
e 14 in b.
Ora, questo punto è dove si procede con lo swap.
Quindi, lanciando indietro al computer portatile nuovo,
vediamo che il modo in cui funziona è di swap ho dereference prima una e memorizzare il risultato in tmp.
Così l'operatore dereference dice: "Ehi. Trattare il contenuto della variabile a come indirizzo.
Vai a tutto ciò che è memorizzato a quell'indirizzo, e lo carica. "
Che si carica fuori della variabile sta per essere memorizzato nella nostra variabile tmp.
Flipping nuovo al iPad.
Se andiamo ad affrontare 10, sappiamo che l'indirizzo 10 è il x varible
perché ci hanno detto che il nostro punto di proiettile che l'indirizzo di x in memoria è di 10.
Così possiamo andare lì, ottenere il valore di esso, che è 1, come si vede sul nostro iPad,
e caricare che in tmp.
Ancora una volta, questo non è il contenuto finale.
Stiamo andando a piedi attraverso e arriveremo al nostro stato finale del programma alla fine.
Ma in questo momento, abbiamo il valore 1 memorizzato in tmp.
>> E c'è una domanda veloce qui.
[Alexander] è l'operatore dereference - questo è solo il diritto stella di fronte alla variabile?
Sì >>. Così l'operatore dereference, come lanciamo di nuovo al nostro computer portatile, ancora una volta,
questa stella è proprio di fronte.
In questo senso, si tratta - si in contrasto con l'operatore di moltiplicazione
che richiede due cose: l'operatore dereference è un operatore unario.
Appena applicato ad un valore al contrario di un operatore binario,
in cui si applica a due valori diversi.
Ecco, questo è ciò che accade in questa linea.
Abbiamo caricato il valore 1 e memorizzato nella nostra variabile temporanea intero.
La riga successiva, si memorizzare il contenuto di b in -
o, piuttosto, di memorizzare i contenuti che b si punta a nel luogo in cui una sta puntando.
Se analizziamo questo da destra a sinistra, stiamo andando a b dereference,
ci accingiamo ad affrontare 14, ci accingiamo a prendere il numero intero che è lì,
e poi abbiamo intenzione di aprire l'indirizzo 10,
e stiamo andando a gettare il risultato del nostro dereferenziamento di b in quello spazio.
Lanciando di nuovo al nostro iPad, dove si può fare questo un po 'più concreto,
potrebbe essere utile se scrivo i numeri su tutti gli indirizzi qui.
Così sappiamo che a y, siamo a indirizzo 14, x è all'indirizzo 10.
Quando cominciamo a b, si dereferenziare b, stiamo andando a prendere il valore 2.
Abbiamo intenzione di prendere questo valore perché questo è il valore che vive all'indirizzo 14.
E abbiamo intenzione di metterlo nella variabile che vive all'indirizzo 10,
che è proprio lì, corrispondente alla nostra variabile x.
Così siamo in grado di fare un po 'di sovrascrivere qui
in cui ci liberiamo del nostro 1 e invece si scrive un 2.
Quindi tutto è bene e il bene nel mondo, anche se abbiamo x ora sovrascritti.
Abbiamo conservato il vecchio valore di x nella nostra variabile tmp.
Così siamo in grado di completare lo scambio con la linea successiva.
Lanciando di nuovo al nostro portatile.
Ora tutto ciò che rimane è quello di prendere il contenuto dal nostro intero variabile temporanea
e memorizzarle nella variabile che vive presso l'indirizzo che b è in possesso.
Quindi stiamo andando a risolvere il riferimento b efficacemente per ottenere l'accesso alla variabile
che è l'indirizzo che b tiene in esso,
e stiamo andando a riempire il valore che è in possesso di tmp in esso.
Flipping torna l'iPad una volta di più.
Posso cancellare il valore qui, 2,
e invece si copierà l'1 a destra in esso.
Poi la riga successiva che esegue, naturalmente -
Se lanciamo torna al computer portatile - è questo il punto 6,
che è il punto in cui abbiamo voluto avere il nostro schema completamente compilata.
Quindi, lanciando indietro al iPad una volta di più, solo così è possibile vedere lo schema completato,
si può vedere che abbiamo un 10 in una, a 14 in b, un 1 in tmp, a 2 in x, e un 1 in y.
Ci sono delle domande su questo?
Questo ha più senso, dopo aver camminato attraverso di essa?
Fai meno senso? Speriamo di no. Va bene.
>> I puntatori sono un argomento molto delicato.
Uno dei ragazzi con cui lavoriamo è un detto molto comune:
"Per capire i puntatori, è necessario prima capire i puntatori."
Che credo sia molto vero. Ci vuole un po 'per abituarsi ad esso.
Disegno un sacco di foto, sorteggio di diagrammi di memoria come questo sono molto utili,
e dopo si cammina attraverso esempio dopo esempio dopo esempio,
si inizierà ad avere un senso un po 'più e un senso un po' più e un po 'più senso.
Infine, un giorno, avrai tutto completamente padroni.
Tutte le domande prima di passare al problema successivo? Bene.
Quindi, capovolgere torna al computer portatile.
Il problema successivo che abbiamo è il problema numero 33 il file di I / O.
Zoom su questo un po '.
Problema 33 - Sì?
>> [Daniel] Ho appena avuto una domanda veloce. Questa stella, o l'asterisco,
si chiama dereferenziazione quando si utilizza un asterisco prima.
Come si chiama quando si utilizza la e commerciale prima?
>> La e commerciale è prima l'operatore di indirizzo.
Quindi cerchiamo di scorrere verso l'alto.
Oops. Sono in modalità zoom quindi non posso davvero scorrimento.
Se guardiamo a questo codice molto velocemente proprio qui,
ancora una volta, la stessa cosa accade.
Se guardiamo a questo codice proprio qui, su questa linea, dove facciamo la chiamata a scambiare,
la e commerciale è solo dire "ottenere l'indirizzo presso il quale vive x variabili".
Quando il compilatore compila il codice,
deve fisicamente tracciare un posto nella memoria per tutte le variabili di vivere.
E così ciò che il compilatore può fare una volta che è compilato tutto,
lo sa: "Oh, ho messo x all'indirizzo 10. ho messo y all'indirizzo 14."
Si può quindi inserire questi valori per voi.
Così si può allora - si può poi passare questo e passare & y pure.
Questi ragazzi ottenere l'indirizzo, ma anche, quando si passa alla funzione di scambio,
queste informazioni tipo, questo * int proprio qui, dice al compilatore,
"Va bene, stiamo andando a essere interpretare questo indirizzo come un indirizzo di una variabile intera."
Come un indirizzo di un int, che è diverso dall'indirizzo di una variabile carattere
perché un int occupa, su una macchina a 32 bit, occupa 4 byte di spazio,
considerando che un personaggio prende solo fino 1 byte di spazio.
Quindi è importante sapere anche che cosa è - ciò che vive, che tipo di valore
vive presso l'indirizzo che e 'stato superato trovi
O l'indirizzo che hai a che fare con.
In questo modo, si sa quanti byte di informazioni per caricare dalla vostra RAM.
E poi, sì, questo operatore dereferenziare, come se stessi chiedendo,
si accede alle informazioni e ad un indirizzo particolare.
Così si dice, con questa variabile qui, trattare il contenuto di una forma di indirizzo,
vai a questo indirizzo, ed estrarre, caricare nel processore, il carico in un registro
i valori reali o il contenuto che vivono a questo indirizzo.
Altre domande? Queste sono buone domande.
E 'un sacco di nuova terminologia troppo.
E 'anche un po' funky, e vedere * e in luoghi diversi.
>> Bene.
Ma torniamo al problema 33, file I / O.
Questo è stato uno di quei problemi che credo un paio di cose sono successe.
Uno, è un argomento abbastanza nuovo.
E 'stata presentata molto presto prima che il quiz,
e poi penso che sia stata un po 'come uno di quei problemi di parola in matematica
dove ti danno un sacco di informazioni, ma che in realtà non finiscono per dover utilizzare una tonnellata di esso.
La prima parte di questo problema è descrivere ciò che è un file CSV.
Ora, un file CSV, secondo la descrizione, è un file CSV valori.
La ragione per cui questi sono tutti interessanti, e la ragione per cui li uso mai,
è, perché, come molti di voi hanno mai usato roba come Excel?
Figura molti di voi hanno, probabilmente, o userà ad un certo punto della tua vita.
Potrai utilizzare qualcosa di simile a Excel.
Al fine di ottenere i dati di un foglio di calcolo Excel o fare qualsiasi tipo di trattamento effettuato con esso,
se si vuole scrivere un programma C o di un programma Python, programma Java,
a che fare con i dati memorizzati in là,
uno dei modi più comuni per farlo fuori è in un file CSV.
E si può aprire Excel e quando si va a 'Save As' dialogo,
si può uscire da un file vero e CSV.
>> Comodo per sapere come affrontare queste cose.
Il modo in cui funziona è che è simile a - Voglio dire, e 'essenzialmente imitando un foglio di calcolo,
dove, come si vede qui, nel pezzo molto più a sinistra,
abbiamo tutti i cognomi.
Così abbiamo Malan, quindi Hardison, e poi Bowden, MacWilliam, e poi Chan.
Tutti i cognomi. E poi una virgola separa i cognomi dai nomi.
David, Nate, Rob, Tommy, e Zamyla.
Ho sempre mescolare Robby e Tom.
E poi, infine, la terza colonna sono gli indirizzi email.
Una volta capito questo, il resto del programma è abbastanza semplice da implementare.
Quello che abbiamo fatto per imitare la stessa struttura del nostro programma C
si abbiamo usato una struttura.
Si comincerà a giocare con questi un po 'più pure.
Li abbiamo visti per il primo bit piccolo problema nel set 3, quando abbiamo a che fare con i dizionari.
Ma questa struttura personale memorizza un cognome, un nome, e una e-mail.
Proprio come il nostro file CSV è stata l'archiviazione.
Quindi questa è solo la conversione da un formato ad un altro.
Dobbiamo convertire, in questo caso, una struttura del personale in una linea,
separati da virgole linea, proprio così.
Ha senso? Voi ragazzi hanno preso il quiz,
quindi immagino che hai almeno avuto il tempo di pensare a questo.
>> Nella funzione di noleggio, il problema ci chiede di prendere in - zoom we'll su questo un po '-
prendere in una struttura di staff, una struttura del personale, con il nome di s,
e aggiungere il suo contenuto al nostro file staff.csv.
Si scopre che questo è abbastanza semplice da usare.
Ci sorta di giocare con queste funzioni un po 'di più oggi.
Ma in questo caso, la funzione fprintf è davvero la chiave.
Quindi, con fprintf, siamo in grado di stampare, proprio come voi ragazzi hanno utilizzato printf questo termine tutto.
È possibile printf una riga in un file.
Quindi, invece di fare la solita chiamata printf in cui si dà la stringa di formato
e poi di sostituire tutte le variabili con i seguenti argomenti,
con fprintf, il primo argomento è invece il file che si desidera scrivere.
Se dovessimo guardare a questa all'interno dell'apparecchio, per esempio, l'uomo fprintf,
siamo in grado di vedere la differenza tra printf e fprintf.
Io ingrandire qui un po '.
Quindi, con printf, diamo una stringa di formato, e quindi gli argomenti successivi
sono tutte le variabili per la sostituzione o la sostituzione nella nostra stringa di formato.
Considerando che con fprintf, il primo argomento è davvero questa * file chiamato un flusso.
>> Tornando qui al nostro noleggio,
abbiamo già ottenuto il nostro flusso * file aperto per noi.
Questo è ciò che la prima riga fa, ma apre il file staff.csv,
essa si apre in modalità append, e tutto quello che ci resta da fare è
scrivere la struttura del personale al file.
E, vediamo, non voglio usare l'iPad?
Userò l'iPad. Abbiamo vuoto - cerchiamo di mettere questo sul tavolo in modo da poter scrivere un po 'meglio -
annullare noleggio e prende in un argomento, una struttura di staff denominata s.
Hai nostri parentesi graffe, abbiamo il nostro * file chiamato file,
abbiamo la nostra linea fopen dato a noi,
e mi limiterò a scrivere sotto forma di punti dal momento che è già in pedia.
E poi la nostra linea successiva, stiamo andando a fare una chiamata a fprintf
e abbiamo intenzione di passare il file che si desidera stampare,
e poi la nostra stringa di formato, che -
Lascio voi ragazzi mi dicono quello che sembra.
E tu, Stella? Sapete cosa la prima parte della stringa di formato simile?
[Stella] Non sono sicuro. >> Non esitate a chiedere Jimmy.
Sai, Jimmy?
[Jimmy] Sarebbe solo l'ultimo? Non lo so. Io non sono del tutto sicuro.
Va bene >>. Che ne dici, qualcuno ha avere questo corretto l'esame?
No. Va bene.
Si scopre che qui tutto quello che dobbiamo fare è che vogliamo ogni parte della nostra struttura personale
da stampare come una stringa in nostro file.
Abbiamo appena utilizzare il carattere di sostituzione di stringhe tre momenti diversi perché abbiamo un cognome
seguito da una virgola, quindi un nome seguito da una virgola,
e poi finalmente l'indirizzo email a cui fa seguito - che non è
montaggio sul mio schermo - ma è seguito da un carattere di nuova riga.
Quindi ho intenzione di scrivere solo laggiù.
E poi, seguendo la nostra stringa di formato,
dobbiamo solo le sostituzioni, che si accede usando la notazione punto
che abbiamo visto nella serie problema 3.
Possiamo usare s.last, s.first e s.email
di sostituire in quei tre valori nella nostra stringa di formato.
Allora, come e 'andata? Ha senso?
Sì? No? Forse? Va bene.
>> L'ultima cosa che facciamo dopo che abbiamo stampato e dopo abbiamo aperto il nostro file:
ogni volta che abbiamo aperto un file, dobbiamo sempre ricordare per chiuderla.
Perché altrimenti finiremo per perdita della memoria,
utilizzando fino descrittori di file.
Quindi, per chiuderla, quale funzione usiamo? Daniel?
[Daniel] fclose? >> Fclose, esattamente.
Quindi l'ultima parte di questo problema è stato quello di chiudere correttamente il file, utilizzando la funzione fclose,
che sembra proprio così.
Non troppo pazzo.
Cool.
Ecco, questo è un problema 33 sulla quiz.
Avremo sicuramente più file di I / O in arrivo.
Faremo un po 'di più a lezione di oggi, o al punto di oggi,
perché è quello che sta andando a formare la maggior parte di questo pset imminente.
Passiamo dal quiz a questo punto. Sì?
>> [Charlotte]] Perché fclose (file) invece di fclose (staff.csv)?
Ah >>. Perché si scopre che - così la questione, che è un grande,
Ecco perché, quando scriviamo fclose, stiamo scrivendo fclose (file) stella variabile
a differenza del nome del file, staff.csv? E 'corretto? Gia '.
Quindi, diamo uno sguardo. Se dovessi tornare al mio portatile,
e diamo un'occhiata alla funzione fclose.
Quindi la funzione fclose chiude un flusso e lo prende nel puntatore al flusso che si desidera chiudere,
in contrasto con il nome effettivo del file che si desidera chiudere.
E questo è dovuto al fatto dietro le quinte, quando si effettua una chiamata a fopen,
quando si apre un file, si sta effettivamente l'allocazione di memoria per memorizzare le informazioni relative al file.
In modo da avere puntatore del file che contiene le informazioni sul file,
come ad esempio è aperto, le sue dimensioni, dove siete attualmente nel file,
in modo da poter rendere la lettura e la scrittura le chiamate a quel luogo particolare all'interno del file.
Si finisce per chiudere il puntatore, invece di chiudere il nome del file.
>> Sì? [Daniel] Quindi, al fine di utilizzare al noleggio, vuoi dire - come fa a ottenere l'input dell'utente?
Ha fprintf agire come GetString, nel senso che ti basta attendere l'input dell'utente
e vi chiedo di scrivere questo - o attendere per poter digitare queste tre cose?
Oppure avete bisogno di usare qualcosa per implementare noleggio?
Sì >>. Quindi non siamo - la questione è stata, come si fa a ottenere l'input dell'utente
al fine di attuare a noleggio? E quello che abbiamo qui è il chiamante del noleggio,
passata in questo struct personale con tutti i dati memorizzati nella struct già.
Così è fprintf in grado di scrivere solo i dati direttamente al file.
Non c'è nessuna attesa per l'input dell'utente.
L'utente ha già dato l'input con la corretta mettendo in questa struttura del personale.
E le cose, ovviamente, si rompono se uno qualsiasi di questi puntatori erano nulle,
quindi scorrere verso l'alto qui e guardiamo la nostra struttura.
Abbiamo ultima stringa, prima stringa, e-mail stringa.
Ora sappiamo che tutti coloro in realtà, sotto il cofano, sono variabili char *.
Questo può o non può essere che punta a null.
Essi possono essere che punta alla memoria nello heap,
forse memoria sullo stack.
Noi in realtà non so, ma se uno qualsiasi di questi puntatori sono nulle, o non valide,
che questo sarà sicuramente in crash la nostra funzione noleggio.
Era una cosa che era un po 'oltre lo scopo di sostenere l'esame.
Non stiamo preoccuparsi di questo.
Grande. Va bene. Quindi, passando dal quiz.
>> Chiudiamo questo ragazzo, e stiamo andando a guardare pset 4.
Quindi, se voi ragazzi guardano le specifiche pset, una volta che vi si possa accedere, cs50.net/quizzes,
ci accingiamo a passare attraverso alcuni dei problemi della sezione di oggi.
Sto scorrendo verso il basso - sezione di domande inizia sulla terza pagina della specifica pset.
E la prima parte si chiede di andare a vedere il corto di riorientare e tubi.
Che era una specie di breve fresco, si mostra alcune nuove, interessanti trucchi della riga di comando che è possibile utilizzare.
E poi abbiamo alcune domande per voi.
La prima domanda sui flussi, a cui scrive printf per impostazione predefinita,
abbiamo tipo toccato solo un po 'un momento fa.
Questo fprintf che stavamo solo discutendo prende in un flusso di file * come argomento.
fclose prende in un flusso di file * pure,
e il valore di ritorno di fopen ti dà un flusso di file * pure.
Il motivo per cui non abbiamo visto quelli che prima, quando abbiamo avuto a che fare con printf
è perché ha printf un flusso predefinito.
E il flusso predefinito a cui si scrive
potrete scoprire a breve.
Quindi, dare un'occhiata a questo.
>> Nella sezione di oggi, stiamo andando a parlare un po 'di GDB,
poiché il più familiare si è con essa, la pratica più si ottiene con esso,
il meglio che tu possa essere in grado di dare la caccia effettivamente dei bug nel codice.
Questo accelera il processo di debug fino tremendamente.
Quindi, utilizzando printf, ogni volta che fai che devi ricompilare il codice,
devi correre di nuovo, a volte è necessario spostare la chiamata a printf in giro,
commentare il codice, ci vuole solo un po '.
Il nostro obiettivo è quello di cercare di convincervi che con GDB, è possibile essenzialmente
printf nulla in qualsiasi punto nel codice e non avete mai ricompilarlo.
Non hai mai avviare e mantenere indovinare dove printf successivo.
La prima cosa da fare è copiare questa linea e ottenere il codice sezione fuori del web.
Sto copiando questa riga di codice che dice: "http://cdn.cs50.net wget".
Ho intenzione di copiarlo.
Ho intenzione di andare oltre al mio apparecchio, l'ingrandimento in modo da poter vedere quello che sto facendo,
incollarlo in là, e quando ho premere Invio, il comando wget è letteralmente una rete ottenere.
E 'intenzione di tirare giù il file da Internet,
e sta andando a salvarlo nella directory corrente.
Ora, se io la mia lista directory corrente si può vedere che ho questo file section5.zip lì dentro.
Il modo di affrontare quel tipo è quello di decomprimerlo,
che si può fare nella riga di comando, proprio come questo.
Section5.zip.
Che te lo decomprimere, creare la cartella per me,
gonfiare tutti i contenuti, metterli in là.
Così ora posso andare nella mia sezione 5 directory usando il comando cd.
Pulisce lo schermo utilizzando chiaro. Quindi pulire lo schermo.
Adesso ho un terminale bello e pulito da affrontare.
>> Ora, se io elencare tutti i file che vedo in questa directory,
si vede che ho quattro file: buggy1, buggy2, buggy3 e buggy4.
Ho anche avuto i loro file corrispondenti. C.
Non stiamo andando a guardare i files. C per ora.
Invece, abbiamo intenzione di usarli quando ci apriamo GDB.
Li abbiamo tenuti in giro in modo da avere accesso al codice sorgente effettiva in cui stiamo usando GDB,
ma l'obiettivo di questa parte della sezione è quello di armeggiare intorno con GDB
e vedere come si può utilizzare per capire cosa c'è che non va con ciascuno di questi quattro programmi buggy.
Quindi stiamo solo andando a giro per la stanza molto velocemente,
e ho intenzione di chiedere a qualcuno di eseguire uno dei programmi buggy,
e poi ce ne andiamo come un gruppo con GDB, e vedremo cosa possiamo fare per risolvere questi programmi,
o di individuare almeno cosa c'è di sbagliato in ciascuno di essi.
Cominciamo qui con Daniel. Vuoi eseguire buggy1? Vediamo cosa succede.
[Daniel] E dice che c'è un errore dell'applicazione. Sì >>. Esattamente.
Quindi, se corro buggy1, ottengo un errore seg.
A questo punto, potrei andare e aprire buggy1.c,
cercare di capire cosa c'è di sbagliato,
ma una delle cose più sgradevoli su questo errore di errore seg
è che non ti dice su quale linea delle cose del programma in realtà è andato storto e si è rotto.
Si tipo di dover guardare il codice
e capire con ipotesi e verificare o printf per vedere cosa c'è di sbagliato.
Una delle funzioni più interessanti di GDB è che è molto, molto facile
per capire la linea con cui il programma va in crash.
E 'del tutto vale la pena di usarlo, anche se solo per questo.
Quindi, per avviare GDB, tipo I GDB, e poi dare il percorso del file eseguibile che voglio correre.
Qui sto scrivendo gdb ./buggy1.
Hit Enter. Mi dà tutte queste informazioni sul copyright,
e qui vedrete questa linea che dice: "i simboli di lettura da / home /
jharvard/section5/buggy1. "
E se tutto va bene, vedrete che stampare un messaggio che assomiglia a questo.
Sarà leggere i simboli, si dirà "Sto leggendo i simboli dal file eseguibile,"
e poi avrà il messaggio "done" qui.
Se vedi qualche altra variante di questo, o si vede che non riusciva a trovare i simboli
o qualcosa del genere, che cosa significa è che proprio non hanno compilato l'eseguibile corretto.
Quando compilare i programmi per l'utilizzo con GDB, dobbiamo usare quella speciale-g,
e che è fatto per impostazione predefinita se si compilare i programmi, semplicemente digitando make
o fare buggy o fare recuperare, una di queste.
Ma se si sta compilando manualmente con Clang, allora dovrete entrare e comprendere il fatto che-g.
>> A questo punto, ora che abbiamo la nostra GDB prompt
è abbastanza semplice per eseguire il programma.
Siamo in grado di digitare run, oppure possiamo semplicemente digitare r.
Maggior parte dei comandi possono essere abbreviati GDB.
Di solito ad un solo o un paio di lettere, che è piuttosto bella.
Così Saad, se si digita r e premere Invio, cosa succede?
[Saad] Ho SIGSEGV, errore di segmentazione, e poi tutto questo incomprensibile.
Sì >>.
Come stiamo vedendo sullo schermo in questo momento, e come ha detto Saad,
quando digita RUN o R e premere Invio, abbiamo ancora ottenere lo stesso difetto seg.
Quindi, utilizzando GDB non risolve il nostro problema.
Ma ci dà un po 'incomprensibile, e si scopre che questo incomprensibile
in realtà ci dice dove sta accadendo.
Per analizzare questo un po ', il primo bit è la funzione in cui tutto è andato storto.
C'è questa __ strcmp_sse4_2, e ci dice che sta accadendo in questo file
chiamato sysdeps/i386, tutto questo, ancora una volta, un po 'un pasticcio - ma la linea 254.
Questo è un po 'difficile da analizzare. Di solito quando si vede cose come questa,
che significa che è seg fault in una delle librerie di sistema.
Quindi, qualcosa a che fare con strcmp. Voi ragazzi avete visto prima di strcmp.
Non troppo folle, ma questo significa che strcmp è rotto o che c'è un problema con strcmp?
Cosa ne pensi, Alexander?
[Alexander] E 'questo - è di 254 la linea? E il - non il binario, ma non è loro soffitti,
e poi c'è un altro linguaggio per ogni funzione. E '254 in quella funzione, o -?
>> E 'la linea 254. Sembra che in questo file. S, quindi è probabilmente il codice assembly.
>> Ma, credo che la cosa più urgente è, perché abbiamo ottenuto un guasto seg,
e sembra che sta arrivando dalla funzione strcmp,
questo implica, quindi, che strcmp è rotto?
Non dovrebbe, si spera. Quindi, solo perché si ha un errore di segmentazione
in una delle funzioni del sistema, in genere ciò significa che proprio non lo hanno chiamato correttamente.
La cosa più veloce da fare per capire cosa sta realmente succedendo
quando si vede qualcosa di pazzo come questo, ogni volta che vedete un errore seg,
soprattutto se si dispone di un programma che, utilizzando più di un semplice principale,
è quello di utilizzare un backtrace.
I abbreviare backtrace scrivendo bt, in contrapposizione alla parola backtrace completo.
Ma Charlotte, cosa succede quando si digita bt e premere Invio?
[Charlotte] Mi mostra due linee, 0 linea e la linea 1.
Sì >>. Così linea 0 e la linea 1.
Questi sono gli stack frame reali che erano attualmente in gioco quando il programma si è bloccato.
A partire dal telaio più in alto, il frame 0, e andare più in basso, che è frame 1.
Il nostro telaio più in alto è il frame strcmp.
Si può pensare a questo come un problema simile a quello che stavamo solo facendo il quiz con i puntatori,
dove avevamo scambiare stack frame in cima alla stack frame principale,
e abbiamo avuto le variabili che utilizzano swap è stato in cima alle variabili che principale è stato utilizzato.
Qui il nostro incidente è accaduto nella nostra funzione strcmp, che è stato chiamato dalla nostra funzione principale,
backtrace e ci sta dando non solo le funzioni in cui le cose non riuscite,
ma è anche ci dice dove tutto era chiamato da.
Quindi, se ho scorrere sopra un po 'più a destra,
possiamo vedere che sì, eravamo in linea 254 di questo strcmp-sse4.s file.
Ma la chiamata è stata fatta a buggy1.c, linea 6.
Quindi questo significa che possiamo fare - è che può solo andare a controllare e vedere che cosa stava succedendo
a buggy1.c, linea 6.
Anche in questo caso, ci sono un paio di modi per farlo. Uno è quello di uscire da GDB
o hanno il codice aperto in un'altra finestra e riferimenti incrociati.
Questo, in sé e per sé, è piuttosto comodo perché ora, se siete in orario d'ufficio
e hai un guasto e il seg TF sta chiedendo dove tutto è stata la rottura,
si può solo dire: "Oh, la linea 6. Non so cosa sta succedendo,
ma qualcosa linea 6 sta causando il mio programma di rompere. "
L'altro modo per farlo è che si può utilizzare questo comando chiamato elenco GDB.
È anche possibile abbreviare con l.
Quindi, se abbiamo raggiunto l, che cosa siamo arrivati ​​qui?
Abbiamo un sacco di cose strane.
Questo è il codice montaggio vero e proprio
che è in strcmp_sse4_2.
Questo sembra un po 'funky,
e il motivo per cui stai ricevendo questo è perché in questo momento,
GDB noi ha nel frame 0.
>> Così ogni volta guardiamo le variabili, ogni volta che guardiamo il codice sorgente,
stiamo guardando il codice sorgente che riguarda lo stack frame siamo attualmente;
Quindi, al fine di ottenere qualcosa di significativo, dobbiamo
passare a un fotogramma stack che ha più senso.
In questo caso, lo stack frame principale avrebbe senso un po 'più,
perché questo era in realtà il codice che abbiamo scritto.
Non il codice strcmp.
Il modo in cui è possibile spostarsi tra i fotogrammi, in questo caso, perché abbiamo due,
abbiamo 0 e 1,
hai fatto con il su e giù i comandi.
Se mi muovo su un fotogramma,
ora sono in stack frame principale.
Posso spostare verso il basso per tornare a dove mi trovavo,
risalire, scendere di nuovo, e salite ancora.
Se mai fare il programma in GDB, si ottiene un crash, si ottiene il backtrace,
e si vede che è in qualche file che non si sa cosa sta succedendo.
Si tenta lista, il codice non sembra familiare a voi,
dare un'occhiata alle cornici e capire dove ti trovi.
Probabilmente siete nello stack frame sbagliato.
O almeno sei in uno stack frame che non è uno che si può veramente eseguire il debug.
Ora che siamo nello stack frame del caso, siamo in main,
Ora possiamo usare il comando list per capire cosa la linea era.
E lo si può vedere, ma è stampato per noi qui.
Ma siamo in grado di colpire elencare tutti uguali, e la lista ci dà questa bella stampa
del codice sorgente effettivo che sta succedendo qui.
>> In particolare, possiamo guardare alla riga 6.
Siamo in grado di vedere quello che sta succedendo qui.
E sembra che stiamo facendo un confronto di stringhe
tra la stringa "CS50 rocce" e argv [1].
Qualcosa su questo faceva andare in crash.
Quindi Missy, hai qualche idea su quello che potrebbe essere succedendo qui?
[Missy] Non so perché è crash. >> Non sai perché è crash?
Jimmy, qualche idea?
[Jimmy] Io non sono del tutto sicuro, ma l'ultima volta che abbiamo usato stringa di confrontare,
o strcmp, abbiamo avuto come tre diversi casi sotto di essa.
Non abbiamo avuto un ==, non credo che, proprio in quella prima linea.
Invece è stato suddiviso in tre, e uno era == 0,
uno era <0, credo, e uno era> 0.
Quindi forse una cosa del genere? Sì >>. Quindi c'è questo problema
delle stiamo facendo il confronto corretto?
Stella? Qualche idea?
[Stella] Non sono sicuro. >> Non sono sicuro. Daniel? Pensieri? Va bene.
Si scopre quello che sta succedendo qui è quando abbiamo finito il programma
e abbiamo ottenuto il guasto seg, quando è stato eseguito il programma per la prima volta, Daniel,
hai dato tutti gli argomenti della riga di comando?
[Daniel] No. No. >> In tal caso, qual è il valore di argv [1]?
>> Non esiste un valore. >> Destra.
Beh, non vi è alcun valore stringa appropriata.
Ma c'è un certo valore. Qual è il valore che viene memorizzato in là?
Un valore >> spazzatura? >> E 'un valore immondizia o, in questo caso,
la fine della matrice argv è sempre terminata con null.
Quindi, ciò che effettivamente ha memorizzato nel non vi è nulla.
L'altro modo per risolvere questo problema, invece di pensare fino in fondo,
è quello di provare a stampare fuori.
Questo è dove dicevo che l'utilizzo di GDB è grande,
perché è possibile stampare tutte le variabili, tutti i valori che si desidera
utilizzando questo pratico-dandy comando p.
Quindi, se di tipo I p e poi digitare il valore di una variabile o il nome di una variabile,
dire, argc, vedo che argc è 1.
Se voglio stampare argv [0], posso farlo proprio così.
E come abbiamo visto, argv [0] è sempre il nome del programma,
sempre il nome del file eseguibile.
Qui sotto potete vedere è ottenuto il percorso completo.
Posso anche stampare argv [1] e vedere cosa succede.
>> Qui abbiamo avuto questo tipo di valore mistico.
Abbiamo ottenuto questo 0x0.
Ricordate all'inizio del termine, quando abbiamo parlato di numeri esadecimali?
O che piccola domanda alla fine del pset 0 su come rappresentare il 50 in esadecimale?
Il nostro modo di scrivere i numeri esadecimali in CS, giusto per non confondere noi stessi
con i numeri decimali, è che abbiamo sempre con prefisso 0x.
Quindi questo prefisso 0x sempre significa solo interpretare il numero come un numero esadecimale,
non come una stringa, non come un numero decimale, non come un numero binario.
Dal momento che il numero di 5-0 è un numero valido in esadecimale.
Ed è un numero decimale, 50.
Quindi questo è solo il modo in cui evitare ambiguità.
Così 0x0 mezzi esadecimale 0, che è anche decimale 0, binario 0.
E 'solo il valore 0.
Si scopre che questo è ciò che nulla è, in realtà, nella memoria.
Null è solo 0.
Qui, l'elemento memorizzato in argv [1] è nullo.
Quindi stiamo cercando di confrontare la nostra "CS50 rocce" stringa in una stringa nulla.
Così dereferencing null, tentando di accedere a nulla le cose,
coloro che sono in genere andando a causare una sorta di errore di segmentazione o di altre cose brutte accadano.
E si scopre che strcmp non controlla per vedere
se non hai passato un valore che è null.
Piuttosto, si va solo avanti, cerca di fare il suo dovere,
e se seg difetti, seg difetti, ed è il tuo problema. Devi andare a risolvere il problema.
Molto velocemente, come potremmo risolvere questo problema? Charlotte?
[Charlotte] È possibile verificare con se.
Quindi, se argv [1] è nullo, == 0, then return 1, o qualcosa del genere [incomprensibile].
Sì >>. Ecco, questo è un ottimo modo per farlo, come si può verificare,
il valore che stiamo per passare in strcmp, argv [1], si è nullo?
Se è null, allora possiamo dire bene, interrompere.
>> Un modo più comune per farlo è quello di utilizzare il valore di argc.
Potete vedere qui all'inizio del principale,
abbiamo omesso che la prima prova che noi di solito facciamo quando utilizzare gli argomenti della riga di comando,
che è quello di verificare se il nostro valore argc è quello che ci aspettiamo.
In questo caso, ci aspettiamo almeno due argomenti,
il nome del programma più altri uno.
Dato che stiamo per usare il secondo argomento proprio qui.
Quindi, avendo una sorta di test in anticipo, prima della nostra chiamata strcmp
che i test o meno argv è almeno 2, anche fare la stessa cosa.
Possiamo vedere se funziona eseguendo nuovamente il programma.
È sempre possibile riavviare il programma all'interno di GDB, che è veramente bello.
È possibile eseguire, e quando si passa gli argomenti al programma,
li passare quando si chiama l'esecuzione, non quando si avvia GDB.
In questo modo è possibile mantenere invocando il programma, con parametri diversi ogni volta.
Correte, o ancora, posso digitare r, e vediamo cosa succede se si digita "ciao".
Sarà sempre chiesto se si desidera avviare fin dall'inizio di nuovo.
Di solito, si vuole avviarlo di nuovo dall'inizio.
E a questo punto, si riavvia di nuovo, esso stampa
il programma che si sta eseguendo, buggy1, con l'argomento ciao,
e stampa questo fuori standard, si dice, "Si ottiene una D," faccia triste.
Ma non abbiamo errore di segmentazione. Ha detto che il processo è terminato normalmente.
In modo che sembra piuttosto buono.
Nessuna anomalia più segmenti, abbiamo fatto passato,
così sembra che il bug era davvero colpa segmento che ci stavamo.
Purtroppo, ci dice che stiamo ottenendo un D.
>> Siamo in grado di tornare indietro e guardare il codice e vedere cosa stava succedendo lì
per capire cosa era - perché ci stava dicendo che abbiamo ottenuto un D.
Vediamo, qui è stato questo printf dicendo che hai un D.
Se digitiamo lista, come si mantiene elenco digitando, mantiene scorrendo verso il basso attraverso il programma,
in modo che vi mostrerò le prime righe del programma.
Poi vi mostrerò le prossime righe, e il blocco successivo e il blocco successivo.
E continuerò cercando di andare verso il basso.
E ora ci arriveremo "linea numero 16 non è compreso nell'intervallo."
Perché ha solo 15 righe.
Se si arriva a questo punto e la tua chiedersi: "Che cosa devo fare?" è possibile utilizzare il comando help.
Utilizzare aiuto e poi dare il nome di un comando.
E si vede il GDB ci dà tutto questo genere di cose.
Dice: "Con nessun argomento, elenca dieci righe più dopo o intorno alla quotazione precedente.
Elenco - elenca le dieci righe prima - "
Quindi cerchiamo di provare a utilizzare meno lista.
E che elenca le 10 righe precedenti, è possibile giocare con un elenco un po '.
È possibile fare la lista, lista -, si può anche dare elencare un numero, come lista 8,
e sarà un elenco delle 10 linee attorno alla riga 8.
E si può vedere quello che sta succedendo qui è che hai un semplice if else.
Se si digita CS50 rocce, esso stampa "Si ottiene una A."
In caso contrario, esso stampa "Si ottiene una D."
Bummer città. Bene. Sì?
>> [Daniel] Così, quando ho provato a fare CS50 rocce senza le virgolette,
si dice "È possibile ottenere una D."
Avevo bisogno le virgolette per farlo funzionare, perché?
Sì >>. Si scopre che, quando - questo è un altro bocconcino po 'di divertimento -
quando si esegue il programma, se lo si esegue e digitare CS50 rocce,
proprio come Daniele stava dicendo che ha fatto, e si preme Invio,
si dice ancora abbiamo un D.
E la domanda è: perché?
E si scopre che sia il nostro terminale e GDB analizzare come due argomenti distinti.
Perché quando c'è uno spazio, che è implicita
il primo argomento chiuso, l'argomento successivo sta per iniziare.
Il modo di combinare quelle in due, o mi dispiace, in un solo argomento,
è quello di utilizzare le virgolette.
Così ora, se lo mettiamo tra virgolette ed eseguirlo di nuovo, si ottiene una A.
Quindi, solo per ricapitolare, senza virgolette, CS50 e rocce vengono analizzati come due argomenti distinti.
Con le virgolette, è analizzato come un argomento del tutto.
>> Possiamo vedere questo con un punto di interruzione.
Finora siamo stati in esecuzione il nostro programma, ed è stato in esecuzione
fino a quando non seg guasti o visite un errore
o fino a quando è uscito e tutto è stato del tutto soddisfacente.
Questo non è necessariamente la cosa più utile, perché a volte
si dispone di un errore nel programma, ma non sta causando un errore di segmentazione.
Non sta causando il vostro programma di fermarsi o di qualcosa di simile.
Il modo per ottenere GDB per mettere in pausa il programma in un punto particolare
è quello di impostare un punto di interruzione.
È possibile eseguire questa operazione impostando un punto di interruzione su un nome di funzione
oppure è possibile impostare un punto di interruzione su una particolare linea di codice.
Mi piace impostare punti di interruzione su nomi di funzione, perché - facile da ricordare,
e se effettivamente entrare e modificare il codice sorgente di un po ',
allora il tuo punto di interruzione sarà effettivamente rimanere nello stesso posto all'interno del codice.
Considerando che, se si sta utilizzando i numeri di riga, e modificare i numeri di riga
perché aggiungere o eliminare un po 'di codice, quindi i punti di interruzione sono tutti completamente sbagliato.
Una delle cose più comuni che fare è impostare un punto di interruzione sulla funzione principale.
Spesso mi avvio GDB, ti tipo b principale, premere Invio, e che verrà impostato un punto di interruzione
la funzione principale che dice solo: "Mettere in pausa il programma non appena si inizia a correre,"
e in questo modo, quando ho eseguito il mio programma con, diciamo, CS50 rocce come due argomenti
e premere Invio, si arriva alla funzione principale e si ferma a destra alla prima riga,
destra prima che valuta la funzione strcmp.
>> Dal momento che sono in pausa, ora posso iniziare a pasticciare in giro e vedere cosa sta succedendo
con tutte le variabili che sono passati nel mio programma.
Qui posso stampare argc e vedere cosa sta succedendo.
Vedere che argc è 3, perché è ottenuto 3 diversi valori in esso.
E 'ottenuto il nome del programma, è ottenuto il primo argomento e il secondo argomento.
Siamo in grado di stampare quelli fuori, cercando in argv [0], argv [1], argv [2].
Così ora si può anche capire perché questa chiamata strcmp sta per fallire,
perché si vede che lo ha fatto dividere la CS50 e le rocce in due argomenti distinti.
A questo punto, una volta che hai raggiunto un punto di interruzione, è possibile continuare a scorrere il programma
riga per riga, al contrario di iniziare nuovamente il programma.
Quindi, se non si desidera avviare di nuovo il programma e solo continuare a partire da qui,
è possibile utilizzare il comando continue e continuare eseguirà il programma fino alla fine.
Proprio come ha fatto qui.
Tuttavia, se riavviare il programma, CS50 rocce, colpisce il mio punto di interruzione di nuovo,
e questa volta, se non si vuole andare solo tutto il percorso attraverso il resto del programma,
Posso usare il comando successivo, che ho anche abbreviare con n.
E questo verrà descritto il programma riga per riga.
Così si può vedere come le cose eseguire, come il cambiamento variabili, come le cose vengono aggiornate.
Il che è piuttosto bella.
L'altra cosa interessante è invece di ripetere lo stesso comando più e più e più volte,
se si premere Invio - ecco vedi che non ho digitato nulla -
se mi limito a premere Invio, si ripeterà il comando precedente,
o il comando precedente GDB che ho appena messo dentro
Posso tenere premendo invio e terrò passo attraverso il mio codice riga per riga.
Vorrei incoraggiare voi ragazzi per andare a controllare i programmi buggy anche altri.
Non abbiamo il tempo di passare attraverso tutti loro oggi in sezione.
Il codice sorgente è lì, quindi è possibile tipo di vedere cosa sta succedendo
dietro le quinte, se vi trovate in panne,
ma per lo meno, basta praticare l'avvio GDB,
l'esecuzione del programma fino a quando si rompe su di voi,
ottenere il backtrace, cercando di capire quale funzione l'incidente era in,
quale linea era su, la stampa di alcuni valori delle variabili,
solo così si ottiene una sensazione di esso, perché questo aiuterà senz'altro a andare avanti.
A questo punto, abbiamo intenzione di smettere di GDB, che si fa utilizzando uscire o semplicemente q.
Se il programma si trova nel mezzo di correre ancora, e non è uscito,
sarà sempre chiedere: "Sei sicuro sicuro di voler uscire?"
Si può solo colpire sì.
>> Ora stiamo andando a guardare il prossimo problema che abbiamo, che è il programma cat.
Se si guarda il corto di reindirizzamento e tubi, vedrai che Tommy usa questo programma
che consente di stampare praticamente tutto l'output di un file sullo schermo.
Quindi, se corro gatto, questo è in realtà un built-in programma per l 'apparecchio,
e se si dispone di Mac si può fare anche sul vostro Mac, se si apre il terminale.
E noi - gatto, diciamo, cp.c, e premere Invio.
Ciò fatto, se si scorre un po 'e vedere dove abbiamo fatto la linea,
o dove abbiamo eseguito il comando cat, è letteralmente appena stampato il contenuto di cp.c al nostro schermo.
Siamo in grado di farlo funzionare di nuovo e si può mettere in più file insieme.
Così si può fare cp.c gatto, e allora possiamo anche concatenare il file Cat. C,
che è il programma che stiamo per scrivere,
e si metterà a stampare entrambi i file back to back al nostro schermo.
Quindi, se si scorre un po ', si vede che quando abbiamo fatto questa cp.c gatto, Cat. C,
prima stampato il file cp, e poi sotto, stampato il file di Cat. C fino qui.
Stiamo andando a utilizzare questo per ottenere solo i nostri piedi bagnati.
Giocare con semplice stampa al terminale, vedere come funziona.
Se voi ragazzi aprono con gedit Cat. C, premere Invio,
si può vedere il programma che stiamo per scrivere.
Abbiamo incluso questo bel piatto della caldaia, in modo da non dover perdere tempo a digitare tutto ciò che fuori.
Abbiamo anche controllare il numero di argomenti passati trovi
Abbiamo stampare un bel messaggio di utilizzo.
>> Questo è il genere di cose che, ancora una volta, come abbiamo parlato,
è quasi come memoria muscolare.
Basta ricordarsi di continuare a fare lo stesso tipo di cose
e sempre stampare una sorta di messaggio utile
in modo che la gente sappia come gestire il vostro programma.
Con il gatto, è abbastanza semplice, stiamo solo andando a passare attraverso tutti i diversi argomenti
che sono stati trasferiti al nostro programma, e abbiamo intenzione di stampare
il loro contenuto fuori allo schermo uno alla volta.
Per stampare i file verso lo schermo, che andremo a fare qualcosa di molto simile
per quello che abbiamo fatto alla fine del quiz.
Alla fine del quiz, che assumere programma, abbiamo dovuto aprire un file,
e poi abbiamo dovuto stampare.
In questo caso, abbiamo intenzione di aprire un file, e stiamo andando a leggere da esso, invece.
Poi andiamo in stampa, invece di un file, che andremo a stampare sullo schermo.
Modo che la stampa sullo schermo tutti voi avete fatto prima con printf.
Quindi non è troppo pazzo.
Ma la lettura di un file è un po 'strano.
Andremo attraverso che un po 'alla volta.
Se voi ragazzi tornare a quella ultimo problema sul tuo quiz, problema 33,
la prima linea che stiamo andando a fare qui, l'apertura del file, è molto simile a quello che abbiamo fatto lì.
Così Stella, che cosa fa quello sguardo linea come, quando si apre un file?
[Stella] Capital * FILE, file - >> Ok. >> - È pari a fopen. Yup >>.
Che in questo caso è? E 'nel commento.
>> E 'nel commento? argv [i] e r?
>> Esattamente. Proprio sulla. Così Stella del tutto giusto.
Questo è ciò che la linea assomiglia.
Stiamo per ottenere una variabile flusso di file, conservarlo in un FILE *, in modo che tutti i tappi,
FILE, * e il nome di questa variabile sarà file.
Potremmo chiamare tutto quello che vogliamo. Potremmo chiamarlo first_file, o file_i, tutto ciò che vuoi.
E poi il nome del file è stato passato sulla riga di comando per questo programma.
Quindi è memorizzato in argv [i,] e poi abbiamo intenzione di aprire il file in modalità di lettura.
Ora che abbiamo aperto il file, qual è la cosa che dobbiamo sempre ricordare di fare
ogni volta che abbiamo aperto un file? Chiuderlo.
Quindi Missy, come facciamo a chiudere un file?
[Missy] fclose (file) >> fclose (file). Esattamente.
Grande. Va bene. Se guardiamo a questo commento da fare qui,
si dice, "Apri argv [i] e stampare il suo contenuto su stdout."
>> Fuori standard è un nome strano. Stdout è solo il nostro modo di dire
si desidera stampare al terminale, vogliamo stampare nel flusso di output standard.
Si può effettivamente sbarazzarsi di questo commento proprio qui.
Ho intenzione di copiarlo e incollarlo dato che è quello che abbiamo fatto.
A questo punto, ora dobbiamo leggere il bit per bit del file.
Abbiamo discusso un paio di modi di lettura dei file.
Quali sono i tuoi preferiti fino ad ora?
Quali modi avete visto o ti ricordi, a leggere i file?
[Daniel] fread? Fread >>? Così fread è uno. Jimmy, sai tutti gli altri?
[Jimmy] No. >> Ok. Nope. Charlotte? Alexander? Tutti gli altri? Va bene.
Così gli altri sono fgetc, è uno che useremo molto.
C'è anche fscanf; voi vedere qualcosa qui?
Tutti iniziano con f. Niente a che fare con un file.
C'è fread, fgetc, fscanf. Queste sono tutte le funzioni di lettura.
Per la scrittura abbiamo fwrite, abbiamo fputc invece di fgetc.
Abbiamo anche fprintf come abbiamo visto sul quiz.
Poiché questo è un problema che coinvolge la lettura da un file,
abbiamo intenzione di usare una di queste tre funzioni.
Non abbiamo intenzione di utilizzare queste funzioni qui.
Queste funzioni si trovano tutti nella norma libreria I / O.
Quindi, se si guarda nella parte superiore di questo programma,
si può vedere che abbiamo già incluso il file di intestazione per l'I / O standard library.
Se vogliamo capire quale si desidera utilizzare,
possiamo sempre aprire le pagine man.
Così possiamo digitare stdio uomo
e leggere tutto l'input stdio e funzioni di uscita in C.
E possiamo già vedere oh, guarda. E 'menzionare fgetc, è menzionare fputc.
Così si può eseguire il drill down un po 'e guardare, per esempio, fgetc
e guardare la sua pagina man.
Si può vedere che va di pari passo con un sacco di altre funzioni:
fgetc, fgets, getc, getchar, ottiene, ungetc, e la sua immissione di caratteri e stringhe.
Quindi questo è come si legge in caratteri e stringhe da file dallo standard input,
che è essenzialmente dall'utente.
E questo è come lo facciamo in effettivo C.
Quindi questo non utilizza il GetString e le funzioni getchar
che abbiamo usato dalla libreria CS50.
Stiamo andando a fare questo problema in un paio di modi
in modo che si può vedere in due modi diversi di farlo.
Sia la funzione fread che Daniel menzionato e fgetc sono buoni modi per farlo.
Penso che fgetc è un po 'più facile, perché ha solo, come vedete,
un argomento, il * file che stiamo cercando di leggere il carattere da,
e il suo valore di ritorno è un int.
E questo è un po 'di confusione, no?
>> Perche 'e' un personaggio, quindi perché non questo ritorno un char?
Voi ragazzi avete qualche idea sul perché questo potrebbe non restituire un char?
[Risposte Missy, incomprensibile] >> Si '. Quindi Missy è totalmente ragione.
Se è ASCII, allora questo numero intero può essere mappato a un carattere vero e proprio.
Potrebbe essere un carattere ASCII, e che è di destra.
Questo è esattamente ciò che sta accadendo.
Stiamo utilizzando un int semplicemente perché ha più bit.
E 'più grande di un char, il nostro char ha solo 8 bit, 1 byte che sulle nostre macchine a 32 bit.
E un int ha un valore "tutti i 4 byte di spazio.
E si scopre che il modo fgetc funziona,
se scorrere verso il basso nella nostra sinossi in questa pagina uomo un po ',
scorrere fino in fondo. Si scopre che usano questo valore speciale chiamato EOF.
E 'una costante speciale come valore di ritorno della funzione fgetc
ogni volta che si ha colpito la fine del file o se si verifica un errore.
E si scopre che per fare questi confronti con EOF correttamente,
si desidera avere tale importo supplementare di informazioni che si hanno in un int
anziché utilizzare un char.
Anche se fgetc è effettivamente ottenere un carattere da un file,
si vuole ricordare che si sta tornando qualcosa che è di tipo int a voi.
Detto questo, è abbastanza facile da usare.
E 'intenzione di darci un personaggio, così tutto quello che dobbiamo fare è continuare a chiedere il file,
"Dammi il carattere successivo, dammi il carattere successivo, dammi il carattere successivo,"
fino ad arrivare alla fine del file.
E che farà rientrare un carattere alla volta da nostro file,
e poi possiamo fare quello che ci pare.
Possiamo memorizzare, possiamo aggiungere una stringa, possiamo stampare.
Fare nulla di tutto ciò.
>> Zoom indietro e tornare al nostro programma di Cat. C,
se abbiamo intenzione di utilizzare fgetc,
come potremmo affrontare questa riga di codice successiva?
Stiamo per usare - fread farà qualcosa di leggermente diverso.
E questa volta, stiamo solo andando a utilizzare fgetc per ottenere un carattere alla volta.
Per elaborare un intero file, cosa potremmo fare?
Quanti personaggi ci sono in un file?
Ci sono un sacco. Quindi, probabilmente avrete bisogno di ottenere uno
e quindi ottenere un altro e ottenere un altro e ottenere un altro.
Che tipo di algoritmo pensi che potrebbe essere necessario usare qui?
Che tipo di -? [Alexander] Un ciclo for? >> Esattamente.
Alcuni tipi di loop.
Un ciclo for è effettivamente grande, in questo caso.
E come dicevi, suona come si desidera un loop sopra l'intero file,
ottenere un carattere per volta.
Qualche suggerimento su ciò che potrebbe apparire come?
[Alexander, incomprensibile]
>> Va bene, dimmi solo in inglese quello che stai cercando di fare? [Alexander, incomprensibile]
Quindi, in questo caso, sembra che stiamo solo cercando di eseguire un loop l'intero file.
[Alexander] Così ho > La dimensione di -?
Credo che la dimensione del file, giusto? Le dimensioni - we'll basta scrivere in questo modo.
Dimensione del file per il momento, i + +.
Così si scopre che il modo in cui si esegue questa operazione utilizzando fgetc, e questa è una novità,
è che non c'è un modo facile per ottenere solo la dimensione di un file
con questo tipo "sizeof" di costrutto che hai visto prima.
Quando si usa questa funzione fgetc, stiamo introducendo una sorta di
nuovo, sintassi funky a questo ciclo for, dove invece di usare solo un contatore di base
andare carattere per carattere, che andremo a tirare un carattere alla volta,
un carattere alla volta, e il modo in cui sappiamo di essere alla fine
non è quando abbiamo contato un certo numero di caratteri,
ma quando il personaggio che tira fuori è che fine speciale di carattere file.
Così siamo in grado di farlo - Io chiamo questo ch, e stiamo andando a inizializzarlo
con la nostra prima chiamata per ottenere il carattere della prima del file.
Quindi questa parte qui, questo sta andando per ottenere un carattere dal file
e memorizzarlo nella variabile ch.
Abbiamo intenzione di continuare a fare questo fino ad arrivare alla fine del file,
che facciamo un test di tipo carattere non essere uguale a quello speciale carattere EOF.
E poi invece di fare ch + +, che sarebbe solo aumentare il valore,
quindi se si legge una A dal file, la A maiuscola, per esempio,
ch + + ci darebbe b, e poi ci piacerebbe avere e poi c d.
Non è chiaro quello che vogliamo. Quello che vogliamo qui
in questo ultimo bit è che vogliamo ottenere il carattere successivo dal file.
>> Così come potremmo ottenere il carattere successivo dal file?
Come ottenere il primo carattere dal file?
[Studente] fgetfile? Fgetc >>, o, mi dispiace, avevi completamente ragione.
L'ho scritto male proprio lì. Quindi sì.
Qui invece di fare ch + +,
stiamo solo andando a chiamare fgetc (file) di nuovo
e memorizzare il risultato nella nostra variabile cap stesso.
[Domanda Studente, incomprensibile]
>> Questo è dove questi ragazzi FILE * sono speciali.
Il loro modo di lavorare che è - quando si apre - la prima volta che fare quella chiamata fopen,
* il file serve efficacemente come puntatore all'inizio del file.
E poi ogni volta che si chiama fgetc, si muove un personaggio attraverso il file.
Così ogni volta che si chiama questo, si sta incrementando il puntatore al file di un carattere.
E quando fgetc ancora una volta, si sta muovendo un altro personaggio
e un altro carattere e un altro personaggio e un altro personaggio.
[Domanda Studente, incomprensibile] >> E that's - yeah.
È un po 'di questa magia sotto il cofano.
Devi solo continuare a incrementare attraverso.
A questo punto, siete in grado di lavorare effettivamente con un carattere.
Così come potremmo stampata allo schermo, ora?
Possiamo usare la stessa cosa printf che abbiamo usato prima.
Che abbiamo usato tutto il semestre.
Possiamo chiamare printf,
e siamo in grado di passare il carattere proprio così.
Un altro modo per farlo è invece di usare printf e di dover fare questa stringa di formato,
possiamo anche utilizzare una delle altre funzioni.
Possiamo usare fputc, che stampa un carattere sullo schermo,
tranne se guardiamo fputc - fammi diminuire un po '.
Vediamo ciò che è bello è che ci vuole nel carattere che abbiamo letto con l'ausilio fgetc,
ma allora dobbiamo dare un flusso di stampare.
Si può anche usare la funzione putchar, che metterà direttamente fuori standard.
Quindi ci sono un sacco di diverse opzioni che possiamo usare per la stampa.
Sono tutti nella norma libreria I / O.
Ogni volta che si desidera stampare - così printf, per impostazione predefinita, verrà stampata la norma speciale fuori corso d'acqua,
che è quello stdout.
Quindi possiamo solo fare riferimento ad esso come una sorta di magia questo valore, stdout qui.
Oops. Mettere il punto e virgola fuori.
>> Questo è un sacco di nuove informazioni funky qui.
Molto di questo è molto idiomatica, nel senso che questo è codice
ciò che è scritto in questo modo solo perché è pulito da leggere, facile da leggere.
Ci sono molti modi per farlo, molte funzioni che si possono usare,
ma si tende a seguire solo questi modelli più e più volte.
Quindi non stupitevi se vedete il codice come il prossimo di nuovo e di nuovo.
Bene. A questo punto, abbiamo bisogno di rompere per la giornata.
Grazie per essere venuto. Grazie per la visione, se sei online. E ci vediamo la prossima settimana.
[CS50.TV]