[Powered by Google Translate] [Sezione 5 - più confortevole] [Rob Bowden - Harvard University] [Questo è CS50. - CS50.TV] Come ho detto nella mia e-mail, ci sono un sacco di cose che si possono utilizzare oltre l'apparecchio di fare effettivamente i set problema. Si consiglia di farlo in macchina solo perché allora possiamo più facilmente aiutare e sappiamo quanto tutto sta andando a lavorare. Ma, come un esempio di dove si possono fare le cose se, per esempio, non si dispone di accesso ad un apparecchio o si desidera lavorare nel seminterrato Science Center - che in realtà hanno la macchina troppo - se si desidera lavorare ovunque. Un esempio è hai visto / sentito parlare di SSH? SSH è fondamentalmente solo come connettersi a qualcosa. In realtà, in questo momento sto SSHed nell'apparecchio. Non ho mai lavorare direttamente nell'apparecchio. Ecco l'apparecchio, e se si guarda qui si vede l'indirizzo IP. Non ho mai lavorare l'apparecchio stesso; Ho sempre venire a un iTerm2 finestra / finestra terminale. È possibile SSH a tale indirizzo IP, ssh jharvard@192.168.129.128. Ricordo che il numero molto facilmente perché è un bel modello. Ma che mi chiede la password, e ora sono in macchina. Fondamentalmente, a questo punto, se si apre un terminale all'interno dell'apparecchio stesso, questa interfaccia, ma lo si dovrebbe utilizzare, è esattamente lo stesso come l'interfaccia che sto usando qui, ma ora si sta SSHed. Non c'è bisogno di SSH per la macchina. Un esempio di un altro luogo si potrebbe SSH è che io sono abbastanza sicuro di avere di default - Oh. Più grande. Tutti si dovrebbe avere di account di default sui server FAS FAS. Per quanto mi riguarda, vorrei SSH al rbowden@nice.fas.harvard.edu. E 'intenzione di chiedere che la prima volta, e tu dici di sì. La mia password è solo andare a essere la mia password FAS. E così ora, sto SSHed ai server belle, e posso fare tutto quello che voglio qui. Un sacco di classi che si possono adottare, come 124, stanno per avere di caricare roba da qui effettivamente presentare le serie di problemi. Ma dire che non si ha accesso al vostro apparecchio. Poi si può fare le cose, come qui si dirà - Questo è solo la nostra sezione di domande. Ti verrà chiesto di farlo in dell'apparecchio. Invece mi limiterò a fare sul server. Io vado a decomprimere questo. Il problema sarà che siete abituati ad usare qualcosa di simile a gedit o qualunque interno dell'apparecchio. Tu non stai andando ad avere che sul server FAS. E 'tutto solo sarà questa interfaccia testuale. Così si potrebbe uno dei due, cercare di imparare un editor di testo che essi hanno. Hanno Nano. Nano di solito è abbastanza facile da usare. È possibile utilizzare le frecce e digitare normalmente. In modo che non è difficile. Se si vuole ottenere davvero fantasia si può usare Emacs, che probabilmente non avrebbe dovuto avviare, perché io non so nemmeno come chiudere Emacs. Controllo X, Controllo C? Gia '. Oppure si può usare Vim, che è quello che uso. E così quelli che sono le vostre opzioni. Se non si vuole fare questo, si può anche, se si guarda al manual.cs50.net-- Oh. Su un PC, è possibile utilizzare SSH PuTTY, che si sta andando ad avere per scaricare separatamente. Su un Mac, si può solo da terminale predefinito l'uso o è possibile scaricare iTerm2, che è come un bel terminale fantasia. Se andate a manual.cs50.net vedrai un link per Notepad + +, che è quello che si può usare su un PC. Esso consente di SFTP da Notepad + +, che è fondamentalmente SSH. Ciò vi permetterà di fare è modificare i file in locale, e poi ogni volta che si desidera salvare loro, sarà salvare nice.fas, dove si può poi eseguire. E l'equivalente per Mac sta per essere TextWrangler. Quindi ti permette di fare la stessa cosa. Ti permette di modificare i file in locale e salvarli nice.fas, dove si può poi eseguire. Quindi, se siete mai bloccati senza un apparecchio, ci sono queste opzioni a fare ancora le serie di problemi. L'unico problema sarà che non si sta andando ad avere la libreria CS50 nice.fas perché non ha che per impostazione predefinita. È possibile scaricare la libreria CS50 - Io non credo di aver bisogno che a questo punto. È possibile scaricare la libreria CS50 e copiarlo nel nice.fas, o credo che a questo punto non ne fanno uso più comunque. Oppure, se lo facciamo, è possibile per il momento non viene sostituito da le implementazioni delle funzioni della libreria CS50 comunque. In modo che non dovrebbe essere molto più di una restrizione. E questo è tutto. Torno alla macchina ora, faremo tutto l'apparecchio. Guardando la nostra sezione di domande, all'inizio, come ho detto nella mia e-mail, dobbiamo parlare di quello corto si dovevano guardare. Abbiamo il riorientamento & Tubi e queste tre domande. Per quale flusso non funzioni come printf scrivere di default? Così flusso. Che cosa è un flusso? Un flusso è fondamentalmente come è solo un po '- Non è nemmeno una fonte di 1 e 0. Il flusso è chiedere qui è fuori standard. E in modo standard è un torrente che quando si vada a scrivere, appare sullo schermo. Fuori standard, dal torrente, vuol dire che basta scrivere 1 e 0 ad esso, e l'altra estremità out standard legge semplicemente da tale flusso. E 'solo una serie di 1 e 0. È possibile scrivere nei flussi o si può leggere dai flussi seconda di ciò che il flusso è in realtà. Gli altri due flussi predefiniti sono standard e standard error. È standard in ogni volta che si fa GetString, è in attesa di cose ingresso. Così è in attesa per voi, in realtà è in attesa di standard, che è davvero quello che si ottiene quando si digita sulla tastiera. Stai scrivendo in standard da Errore standard è sostanzialmente equivalente a fuori standard, ma è specializzata in che quando si stampa su standard error, si suppone di stampare solo i messaggi di errore a quella in modo da poter distinguere tra messaggi normali visualizzati sullo schermo contro i messaggi di errore a seconda che sono andati a fuori standard o errore standard. File troppo. Fuori standard, standard, e lo standard error sono flussi solo speciali, ma in realtà qualsiasi tipo di file, quando si apre un file, diventa un flusso di byte dove si può solo leggere da tale flusso. Ti, per la maggior parte, può solo pensare di un file come un flusso di byte. Quindi, ciò che i flussi si scrivono per default? Fuori standard. Qual è la differenza tra> e >>? Qualcuno guarda il video in anticipo? Va bene. > Sta per essere come si reindirizza in file, e >> è anche andare a reindirizzare l'output in file, ma è invece andare da aggiungere al file. Per esempio, diciamo che mi capita di avere dict proprio qui, e la roba solo all'interno di dict è il gatto, gatto, cane, pesce, cane. Un comando che permette di avere a riga di comando è cat, che è solo andare a stampare il contenuto di un file. Quindi, quando dico dict gatto, sta andando per la stampa gatto, gatto, cane, pesce, cane. Questo è tutto cat fa. Ciò significa che è stampato sullo standard output gatto, gatto, cane, pesce, cane. Se invece desidera reindirizzare che a un file, posso usare> e destinarli a tutto ciò che il file è. Chiamo il file file. Così ora se I ls, vedrò ho un nuovo file chiamato file. E se aprirlo, che sta per avere esattamente quello che gatti, condizionati dalla riga di comando. Così ora se lo faccio di nuovo, poi va a reindirizzare l'output in file, e ho intenzione di avere la stessa cosa esatta. Tecnicamente, quindi, completamente calpestato quello che abbiamo avuto. E vedremo se cambio dict, ho tirato fuori il cane. Ora, se gatto dict in file di nuovo, stiamo per avere la nuova versione con il cane rimosso. Quindi sostituisce completamente. Al contrario, se usiamo >>, che sta per aggiungere file. Ora, l'apertura del file, si vede che abbiamo proprio la stessa cosa stampato due volte perché era una volta, poi allegata all'originale. Quindi questo è quello che> e >> fare. L'prossima chiedere - Non chiedere al riguardo. L'altra che abbiamo è <, che se> reindirizza fuori standard, fare 2>, che sta reindirizzando errore standard. Quindi, se qualcosa è andato a standard error, che non si sarebbe messo in txt2. Ma bando se lo faccio 2>, quindi è ancora la stampa Ciao, Rob! alla riga di comando perché io sono solo il reindirizzamento errore standard, non sto reindirizzando standard output. Errore standard e fuori standard sono diversi. Se si voleva scrivere effettivamente errore standard, quindi ho potuto cambiare questo sia fprintf per stderr. Così printf, per impostazione predefinita, viene stampato fuori standard. Se voglio stampare su standard error manualmente, quindi devo usare fprintf e specificare quello che voglio stampare. Se invece ho fatto fprintf stdout, allora questo è sostanzialmente equivalente a printf. Ma fprintf standard error. Così ora, se mi reindirizzare questo in txt2, Ciao, Rob! è ancora ottenere stampato nella riga di comando dal momento che è ottenere stampato errore standard e sono solo il reindirizzamento standard output. Se ora ridirige lo standard error, ora non vengono stampati, e txt2 sarà Ciao, Rob! Così ora, è possibile stampare i vostri errori effettivi standard error e stampare i messaggi regolari fuori standard. E così, quando si esegue il programma, è possibile eseguirlo come. / Ciao questo tipo con il 2> in modo che il programma sta andando a funzionare normalmente, ma gli eventuali messaggi di errore che si ottiene è possibile controllare più avanti nel log degli errori, così gli errori e poi guardare più tardi e il file errori avrà eventuali errori che sono successe. Domande? L'ultimo è del tubo, che si può pensare come prendere standard fuori da un comando e che lo rende lo standard in del comando successivo. Un esempio qui è eco è una cosa riga di comando che è solo andare a eco ciò che ho messo come argomento. Non voglio mettere le virgolette. Echo bla, bla, bla è solo andare a stampare bla, bla, bla. Prima, quando ho detto che ho dovuto mettere Rob in un file txt perché posso solo reindirizzare i file txt, invece, / se io eco Rob e poi tubo in esso. / ciao, che farà anche lo stesso tipo di cosa. Questo sta prendendo l'output di questo comando, echo Rob, e usarlo come input per. / ciao. Si può pensare ad esso come primo redirect eco Rob in un file e poi ingresso in. / ciao il file che è stato appena emesso. Ma ci vuole il file temporaneo fuori dal quadro. Domande su questo? La domanda successiva sta per coinvolgere questo. Cosa gasdotto potrebbe utilizzare per trovare il numero di nomi univoci in un file chiamato names.txt? I comandi che andremo a voler usare qui sono unici, così uniq, e wc. Si può fare uniq l'uomo a guardare effettivamente a ciò che fa, ed è solo andando a filtrare adiacenti righe corrispondenti dall'ingresso. E l'uomo wc sta per stampare la nuova riga, parola, e il numero di byte per ogni file. E l'ultimo che stiamo andando a voler utilizzare è sorta, che sta per ordinare solo righe di file txt. Se faccio un po 'di file txt, names.txt, ed è Rob, Tommy, Joseph, Tommy, Joseph, RJ, Rob, quello che voglio fare è trovare il numero di nomi univoci in questo file. Quindi, quello che dovrebbe essere la risposta? >> [Studente] 4. Sì >>. Dovrebbe essere 4 da Rob, Tommy, Joseph, RJ sono i nomi univoci solo in questo file. Il primo passo, se mi limito a fare il conteggio delle parole names.txt, questo è in realtà mi sta dicendo tutto. Questo è in realtà la stampa - Vediamo un po ', man wc - newline, parole e numero di byte. Se mi interessa solo le linee, allora posso solo fare wc-l names.txt. Così che è passaggio 1. Ma io non voglio wc-l names.txt names.txt perché contiene solo tutti i nomi, e voglio filtrare alcun quelli non univoci. Quindi, se faccio names.txt uniq, che non tutto mi dà quello che voglio perché i nomi duplicati sono ancora lì. Perché è così? Perché non uniq fare ciò che voglio? [Studente] I duplicati non sono [incomprensibile] >> Si '. Ricordate la pagina di manuale di uniq dice filtri corrispondenti linee adiacenti. Non sono adiacenti, in modo che non li filtra. Se ordinarli prima names.txt ordinamento sta per mettere tutte le linee duplicate insieme. Così ora names.txt tipo è che. Ho intenzione di voler utilizzare tale come input per uniq, che è | uniq. Questo mi dà Giuseppe, RJ, Rob, Tommy, e voglio utilizzarlo come input per wc-l, che sta per darmi 4. Come si dice qui, che cosa gasdotto potrebbe usare? Si può fare un sacco di cose come l'utilizzo di una serie di comandi in cui si utilizza l'output di un comando come input per il comando successivo. Si può fare un sacco di cose, un sacco di cose intelligenti. Domande? Va bene. Questo è tutto per tubi e reindirizzamento. Ora andiamo al roba vera, la roba di codifica. All'interno di questo PDF, vedrete questo comando, e ti consigliamo di eseguire questo comando nel vostro apparecchio. wget è il comando solo per ottenere qualcosa da Internet, in fondo, così wget e questo URL. Se si è andato a questo URL nel browser, sarebbe scaricare il file. Ho appena cliccato su di esso, in modo che il file scaricato per me. Ma scrivere wget di quella cosa all'interno del terminale è solo andare a scaricarlo nel vostro terminale. Ho section5.zip, e ti consigliamo di decomprimere section5.zip, che sta per darvi una cartella denominata section5, che sta per avere tutti i file che andremo a utilizzare oggi all'interno di esso. Poiché i nomi dei file di questi programmi suggeriscono, sono un po 'buggy, così la vostra missione è quella di capire perché con gdb. Ha tutti li hanno scaricato / sa come farli scaricare nel loro apparecchio? Va bene. Correre ./buggy1, si dirà Segmentation fault (core dumped), che ogni volta che si ottiene un segmentation fault, è una brutta cosa. In quali circostanze si ottiene un segfault? [Studente] Dereferenziare un puntatore nullo. Sì >>. Così che è un esempio. Dereferenziare un puntatore nullo che si vuole ottenere un segfault. Che segfault significa che stai toccando la memoria non si deve toccare. Così dereference un puntatore nullo sta toccando l'indirizzo 0, e in fondo, tutti i computer al giorno d'oggi dire che 0 è l'indirizzo di memoria non si deve toccare. Ecco perché dereferenziazione un risultato puntatore null in un segfault. Quando vi capita di non inizializzare un puntatore, allora ha un valore di spazzatura, e così quando si tenta di dereferenziarlo, con ogni probabilità si sta toccando la memoria che è in mezzo al nulla. Se vi capita di avere fortuna e il valore dei rifiuti successo per puntare da qualche parte sullo stack o qualcosa del genere, poi quando si dereference quel puntatore che non si è inizializzato, nulla vada storto. Ma se sta puntando, per esempio, da qualche parte tra lo stack e l'heap, o è solo per indicare da qualche parte che non è stata utilizzata dal programma ancora, allora stai toccando la memoria non si deve toccare e segfault. Quando si scrive una funzione ricorsiva e ricorsiva troppe volte e il vostro stack diventa troppo grande e si scontra pila in cose che non dovrebbe essere in collisione con, stai toccando la memoria non si deve toccare, in modo da segfault. Questo è ciò che è un segfault. E 'anche lo stesso motivo che, se si dispone di una stringa come - torniamo al programma precedente. In hello.c--Sto solo andando a fare qualcosa d'altro. char * s = "ciao mondo!"; Se uso * s = qualcosa o s [0] = 'X'; in modo da rendere ciao,. / ciao, perché che segfault? Perché questo segfault? Che cosa vi aspettate che succeda? Se ho fatto printf ("% s \ n", s); ci si può aspettare da stampare? [Studente] X ciao. Sì >>. Il problema è che quando si dichiara una stringa come questa, s è un puntatore che sta per andare in pila, e ciò s punta a questa stringa è contenuta in memoria di sola lettura. Quindi, solo per il nome, memoria di sola lettura, si dovrebbe ottenere l'idea che se si tenta di modificare ciò che è in memoria di sola lettura, si sta facendo qualcosa che non dovrebbe fare con la memoria e si segfault. Questo è in realtà una grande differenza tra char * s e char s []. Così char s [], ora questa stringa sta per essere messa in pila, e lo stack non è di sola lettura, il che significa che questo dovrebbe funzionare perfettamente. E lo fa. Ricordate che quando faccio char * s = "ciao mondo!", S è di per sé in pila ma i punti s per qualche altra parte, e che da qualche altra parte sembra essere di sola lettura. Ma char s [] è solo qualcosa in pila. Ecco, questo è un altro esempio di un segfault accadendo. Abbiamo visto che ./buggy1 provocato un segfault. In teoria, non si dovrebbe guardare buggy1.c immediatamente. Invece, vedremo attraverso gdb. Si noti che quando si arriva Segmentation fault (core dumped), si ottiene il file sopra il centro qui chiamato. Se ls-l, vedremo che il nucleo di solito è un file abbastanza grande. Questo è il numero di byte del file, in modo che appaia come se fosse qualcosa da 250 kilobyte. La ragione di questo è che cosa il core dump è effettivamente è quando il programma va in crash, lo stato della memoria del programma viene semplicemente copiato e incollato in questo file. Essa viene scaricato in quel file. Questo programma, mentre era in esecuzione, è capitato di avere un utilizzo di memoria di circa 250 kilobyte, e così questo è quello che ho scaricato in questo file. Ora si può guardare a quel file, se facciamo gdb nucleo buggy1. Possiamo solo fare gdb buggy1, e che semplicemente avviare gdb regolarmente, utilizzando buggy1 come file di input. Ma se lo fai gdb nucleo buggy1, allora è specificamente sta per avviare gdb cercando in quel file core. E dicendo buggy1 gdb significa sa che il file principale viene dal programma buggy1. Così gdb buggy1 nucleo sta per portarci subito al punto in cui il programma è successo a terminare. Vediamo qui Programma terminato con segnale 11, Segmentation fault. Ci capita di vedere una linea di montaggio, che probabilmente non è molto utile. Ma se si digita bt o backtrace, che sta per essere la funzione che ci dà la lista dei nostri stack frame corrente. Così backtrace. Sembra che abbiamo solo due stack frame. Il primo è il nostro stack frame principale, e il secondo è la stack frame per questa funzione che ci capita di essere in, che sembra non ci resta che il codice assembly per. Quindi cerchiamo di tornare nella nostra funzione principale, e per fare ciò che possiamo fare frame 1, e penso che possiamo anche fare verso il basso, ma io quasi mai verso il basso - o verso l'alto. Gia '. Su e giù. Porta in primo piano in su uno stack frame, giù ti porta giù uno stack frame. Io tendo ad usare mai che. Ho appena specificamente dire telaio 1, che è andare al fotogramma 1. Fotogramma 1 sta per portarci in stack frame principale, e si dice che proprio qui la riga di codice ci capita di essere a. Se volevamo un altro paio di righe di codice, possiamo dire elenco, e questo ci darà tutte le righe di codice che lo circondano. La linea che era segfaulted a 6: if (strcmp ("CS50 rocce", argv [1]) == 0). Se non è ancora chiaro, si può ottenere direttamente da qui semplicemente pensando perché segfaulted. Ma possiamo fare un ulteriore passo avanti e dire: "Perché argv [1] segfault?" Stampa Facciamo argv [1], e sembra come se fosse 0x0, che è il puntatore nullo. Stiamo strcmping CS50 rocce e nulli, e così che sta andando a segfault. E perché è argv [1] null? [Studente] Perché non le ha dato della riga di comando argomenti. Gia '. Non l'abbiamo dato nessun comando argomenti della riga. Così ./buggy1 è solo andare per avere argv [0] è ./buggy1. E non ha intenzione di avere un argv [1], in modo che sta per segfault. Ma se, invece, lo faccio solo CS50, è andare a dire È possibile ottenere una D perché questo è quello che dovrebbe fare. Guardando buggy1.c, si suppone di stampa "Si ottiene una D" - Se argv [1] non è "CS50 rocce", "Si ottiene una D", altrimenti "Si ottiene una A!" Quindi, se vogliamo una A, abbiamo bisogno di questo per confrontare come vero, il che significa che paragona a 0. Quindi, argv [1], deve essere "CS50 rocce". Se si vuole fare che sulla riga di comando, è necessario utilizzare \ per sfuggire allo spazio. Così CS50 \ rocce e si ottiene una A! Se non si esegue la barra rovesciata, perché non questo lavoro? [Studente] Sono due argomenti diversi. Sì >>. Argv [1] sta per essere CS50, e argv [2] sarà rocce. Va bene. Ora ./buggy2 sta per segfault di nuovo. Invece di aprire con il suo file core, dobbiamo solo aprire buggy2 direttamente, così gdb buggy2. Ora, se basta eseguire il nostro programma, quindi sta andando a dire Programma segnale ricevuto SIGSEGV, che è il segfault segnale, ed è qui che è successo a succedere. Guardando alla nostra backtrace, vediamo che eravamo in oh_no funzione, che è stato chiamato dalla Dinky funzione, che è stato chiamato dal Binky funzione, che è stato chiamato da principale. Possiamo anche vedere gli argomenti di queste funzioni. L'argomento di Dinky e Binky è stato di 1. Se riportiamo la funzione oh_no, vediamo che oh_no sta solo facendo char ** s = NULL; * S = "BOOM"; Perchè dovrebbe fallire? [Studente] Non è possibile dereference il puntatore nullo? Sì >>. Questo è solo per dire s è NULL, indipendentemente se questo sembra essere un char **, che, a seconda di come lo si interpreta, potrebbe essere un puntatore a un puntatore a una stringa o un array di stringhe. E 's è NULL, per cui s * è dereference un puntatore nullo, e quindi questo sta andando in crash. Questo è uno dei modi più veloci si può eventualmente segfault. E 'solo che dichiara un puntatore nullo e subito va in segfault. Questo è ciò che sta facendo oh_no. Se andiamo su un fotogramma, quindi stiamo per entrare nella funzione che ha chiamato oh_no. Ho bisogno di fare che verso il basso. Se non si immette un comando e basta premere Invio di nuovo, sarà solo ripetere il comando precedente che è stato eseguito. Siamo nel frame 1. La vendita di questo telaio, vediamo qui è la nostra funzione. Si può colpire di nuovo elenco, oppure si può fare la lista 20 e verranno mostrati più. La funzione di dinky dice che se i è 1, quindi andare alla funzione oh_no, altrimenti andare alla funzione slinky. E sappiamo che i è 1 perché ci capita di vedere qui dinky che è stato chiamato con l'argomento 1. Oppure si può semplicemente faccio a stampare e dirà che è 1. Siamo attualmente in dinky, e se andiamo su un altro fotogramma, sappiamo che finirai in Binky. Up. Ora siamo in Binky. Listato questa funzione - l'elenco da prima di metà mi ha interrotto - ha iniziato come se i è 0, allora andremo a chiamare oh_no, altrimenti chiamare dinky. Sappiamo che ero 1, quindi ha chiamato dinky. E ora siamo di nuovo in main, e principale è solo andare a essere int i = rand ()% 3; Questo è solo per darvi un numero casuale che è 0, 1 o 2. E 'intenzione di chiamare binky con quel numero, e verrà restituito 0. Guardando a questo, solo a piedi attraverso il programma manualmente senza correre immediatamente, è necessario impostare un punto di interruzione alla centrale, il che significa che quando si esegue il programma l'esecuzione del programma fino fino a quando non colpisce un punto di rottura. Quindi, l'esecuzione del programma, verrà eseguito e poi colpirà la funzione principale e interrompere l'esecuzione. Ora siamo all'interno del principale, e il passo successivo o sta per portarci alla riga successiva di codice. È possibile eseguire il passaggio o successivo. Colpire successivo, ora mi è stato impostato su rand ()% 3, in modo da poter stampare il valore di i, e dirà che è 1. Ora non importa se usiamo o successivo passo. Credo che importava in quello precedente, ma ci vorrebbe usare successivo. Se usiamo passo, entrare dentro la funzione, il che significa che un'occhiata alla cosa reale che sta accadendo dentro di Binky. Se usiamo prossimo, significa andare oltre la funzione e basta andare alla riga successiva di codice nella nostra funzione principale. Proprio qui, su questa linea, sono stato a dove si dice rand ()% 3; se l'ho fatto passo, sarebbe andare in attuazione di rand e guardare a ciò che sta succedendo lì, e ho potuto passare attraverso la funzione rand. Ma non mi interessa la funzione rand. Voglio solo passare alla riga successiva di codice in main, per cui uso successivo. Ma ora mi importa la funzione Binky, quindi voglio fare un passo in quella. Ora sono in Binky. La prima riga di codice sta per dire se (i == 0), faccio un passo, vediamo finiamo a Dinky. Se noi le cose della lista, vediamo che controllare i è = 0. i non è uguale a 0, quindi è andato a la condizione else, che sta per chiamare Dinky (i). Si potrebbe confondersi. Se si basta guardare queste righe direttamente, si potrebbe pensare che se (i == 0), ok, allora ho fatto un passo e ora sono a Dinky (i), si potrebbe pensare che deve significare i = 0 o qualcosa del genere. No. Significa solo che sa di poter attaccare direttamente alla Dinky linea (i). Perché non è 0, il passo successivo non sta per finire in altro. Altrimenti non è una linea che sta per fermarsi a. E 'solo per andare alla riga successiva si può effettivamente eseguire, che è dinky (i). Entrare nel Dinky (i), vediamo se (i == 1). Ci faccio a sapere = 1, quindi quando facciamo un passo, sappiamo che stiamo andando a finire in oh_no perché i = 1 chiama la oh_no funzione, che si può entrare in, che sta per impostare char ** s = a NULL e subito "BOOM". E poi in realtà guardando la realizzazione di buggy2, questo, i è solo ottenere un numero casuale - 0, 1, o 2 - chiamata Binky, che se i è 0 si chiama oh_no, altrimenti chiama dinky, di cui si parla qui. Se i è 1, chiamata oh_no, altrimenti chiamare slinky, che venire qui, se i è 2, chiamare oh_no. Non so nemmeno che ci sia un modo - Qualcuno vedere un modo di rendere questo un programma che non si segfault? Perché a meno che non mi manca qualcosa, se i è 0, verrà immediatamente segfault, altrimenti si va a una funzione che se i è 1 si segfault, altrimenti si va a una funzione in cui se i è 2 si segfault. Quindi, non importa quello che fai, segfault. Credo che un modo per risolverlo sarebbe invece di fare char ** s = NULL, si potrebbe malloc spazio per quella stringa. Potremmo fare malloc (sizeof) - sizeof cosa? [Studente] (char) * 5? >> Vi sembra giusto? Sto assumendo questo funziona se io in realtà ha funzionato, ma non è quello che sto cercando. Guardate il tipo di s. Lasciatemi aggiungere int *, in modo int * x. Vorrei fare malloc (sizeof (int)). Oppure, se volevo un array di 5, farei (sizeof (int) * 5); Che cosa succede se ho un int **? Quello che mi sarebbe malloc? [Studente] Dimensione del puntatore. Sì >>. (Sizeof (int *)); Stessa cosa qui. Voglio (sizeof (char *)); Questo sta per allocare lo spazio per il puntatore che punta a "BOOM". Non ho bisogno di allocare lo spazio per "BOOM" stesso perché questo è sostanzialmente equivalente a quello che ho detto prima di char * x = "BOOM". "BOOM" esiste già. Succede esistere in sola lettura regione di memoria. Ma esiste già, il che significa che questa riga di codice, se s è un char **, allora * s è un char * e si sta impostando questo char * per puntare a "BOOM". Se io volessi copiare "BOOM" in s, allora avrei bisogno di allocare lo spazio per s. Farò * s = malloc (sizeof (char) * 5); Perché 5? Perché non 4? Sembra che "BOOM" è di 4 caratteri. >> [Studente] Il carattere null. Gia '. Tutte le corde si sta per necessità il carattere null. Ora posso fare qualcosa di simile strcat - Qual è la funzione per la copia di una stringa? [Studente] cpy? Strcpy >>. uomo strcpy. Così strcpy o strncpy. strncpy è un po 'più sicuro in quanto è possibile specificare esattamente il numero di caratteri, ma qui non importa perché sappiamo. Così strcpy e guardare negli argomenti. Il primo argomento è la nostra destinazione. Il secondo argomento è la nostra fonte. Stiamo andando a copiare nella nostra destinazione * s il "BOOM" puntatore. Perché potrebbe voler fare questo con un strcpy invece di quello che avevamo prima di * s = "BOOM"? C'è una ragione si potrebbe desiderare di fare questo, ma quello che è che la ragione? [Studente] Se si vuole cambiare qualcosa in "BOOM". Sì >>. Ora posso fare qualcosa di simile s [0] = 'X'; perché i punti s al mucchio e che lo spazio sul mucchio che s sta puntando è un puntatore a un maggior spazio sul mucchio, che memorizza "BOOM". Quindi questa copia di "BOOM" viene memorizzato nel mucchio. Ci sono tecnicamente due copie di "BOOM" nel nostro programma. C'è il primo che ha appena dato da questa costante "BOOM" stringa, e la seconda copia del "BOOM", strcpy creato la copia di "BOOM". Ma la copia di "BOOM" viene memorizzato sul mucchio, e il mucchio sei libero di cambiare. L'heap non è di sola lettura, in modo che significa che s [0] sta per ti permette di cambiare il valore di "BOOM". E 'intenzione di farvi cambiare i caratteri. Domande? Va bene. Passando alla buggy3, andiamo gdb buggy3. Ci basta eseguirlo e vediamo otteniamo un segfault. Se si backtrace, ci sono solo due funzioni. Se saliamo nella nostra funzione principale, vediamo che segfaulted a questa linea. Quindi, solo guardando questa linea, per (int riga = 0; fgets questa roba non è uguale a NULL; linea + +). Il nostro quadro precedente si chiamava _IO_fgets. Vedrete che un sacco con built-in funzioni C, che quando si ottiene il segfault, ci saranno i nomi delle funzioni davvero criptici come questo _IO_fgets. Ma che sta per riferirsi a questa chiamata fgets. Da qualche parte qui dentro, ci va in segfault. Se guardiamo gli argomenti di fgets, ci permette di stampare buffer. Facciamo stampare come - Oh, no. Stampa non è andare a lavorare esattamente come voglio io. Vediamo il programma vero e proprio. Buffer è un array di caratteri. Si tratta di un array di caratteri di 128 caratteri. Quindi, quando dico buffer di stampa, sta andando a stampare le 128 caratteri, che credo sia quello che ci si aspetta. Quello che stavo cercando è stampare l'indirizzo del buffer, ma che in realtà non mi dice molto. Così, quando mi capita di dire qui tampone x, mi mostra 0xbffff090, che, se ti ricordi di prima o un certo punto, Oxbffff tende ad essere uno stack-ish regione. Lo stack tende a cominciare da qualche parte appena sotto 0xc000. Solo vedendo questo indirizzo, so che sta accadendo sul buffer di stack. Riavvio il mio programma, eseguito, su, tampone che abbiamo visto era questa sequenza di caratteri che sono praticamente senza senso. Poi la stampa di file, file di che cosa assomiglia? [Studente] Null. Sì >>. File è un di tipo * FILE, quindi è un puntatore, e che il valore di puntatore è nullo. Così fgets sta per provare a leggere da tale puntatore in modo indiretto, ma per accedere a tale puntatore, deve dereferenziarlo. Oppure, per poter accedere a quello che dovrebbe essere rivolto a, lo dereferenziazioni. Quindi è dereference un puntatore nullo e si segfault. Avrei potuto riavviato lì. Se si spezza al nostro punto principale ed eseguire, la prima riga di codice è di tipo char * filename = "nonexistent.txt"; Questo dovrebbe dare un suggerimento abbastanza grande perché questo programma non riesce. Digitando accanto mi porta alla riga successiva, in cui ho aperto questo file, e poi ho subito entrare nella nostra linea, dove una volta mi ha colpito prossimo, sta andando a segfault. Qualcuno ha voglia di buttare via un motivo per cui potrebbe essere andare in segfault? [Studente] Il file non esiste. Sì >>. Questo dovrebbe essere un suggerimento che ogni volta che si sta aprendo un file è necessario controllare che il file esiste realmente. Quindi, ecco, "nonexistent.txt"; Quando abbiamo filename fopen per la lettura, abbiamo quindi bisogno di dire if (file == NULL) e dire printf ("Il file non esiste!" o - meglio ancora - filename); return 1; Così ora controlliamo per vedere se è NULL prima di poter realmente continua e cercando di leggere da quel file. Possiamo rifare solo per vedere che funziona. Avevo intenzione di inserire una nuova riga. Così ora nonexistent.txt non esiste. Si consiglia di controllare sempre per questo genere di cose. Si dovrebbe sempre verificare se fopen restituisce NULL. Si dovrebbe sempre assicurarsi che malloc non ritorna NULL, altrimenti si segfault. Ora buggy4.c. Esecuzione. Sto indovinando questo è in attesa di ingresso o eventualmente loop infinito. Sì, è loop infinito. Così buggy4. Sembra che siamo loop infinito. Siamo in grado di rompere al principale, eseguire il nostro programma. In gdb, purché l'abbreviazione si utilizza è inequivocabile o abbreviazioni speciali che essi forniscono per voi, quindi è possibile utilizzare n per uso successivo, invece di dover digitare a fianco tutta la strada. E ora che ho colpito n una volta, posso solo premere Invio per andare avanti dopo invece di dover premere Invio n, n Inserisci, n Invio. Sembra come se fossi in una sorta di ciclo for che è l'impostazione di matrice [i] a 0. Sembra che io non sono mai la rottura di questo ciclo for. Se i stampa, così ho è 2, poi me ne andrò dopo. Io i stampa, i è 3, poi me ne andrò dopo. Io ho stampare e i è 3. Quindi, stampare i, i è 4. In realtà, stampa sizeof (array), quindi dimensione della matrice è 20. Ma sembra che ci sia qualche comando gdb speciale per andare fino a quando succede qualcosa. E 'come la creazione di una condizione sul valore della variabile. Ma non mi ricordo quello che è. Quindi, se andare avanti - Che cosa ha detto? Che cosa hai portato su? [Studente] non visualizza i add - >> Si '. Quindi visualizzare posso aiutare. Se solo i visualizzazione, sarà inserire qui ciò che il valore di i è quindi non c'è bisogno di stamparlo ogni volta. Se solo continuare prossima, vediamo 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5. Sta succedendo qualcosa di terribilmente sbagliato, e mi viene reimpostato a 0. Guardando buggy4.c, si vede tutto ciò che accade è array int [5]; for (i = 0; i <= sizeof (array), i + +) array [i] = 0; Che cosa si vede che c'è di sbagliato? Come un suggerimento, quando stavo facendo il gdb buggy4 - rompiamo principale di esecuzione - Ho fatto stampare sizeof (array) solo per vedere cosa la condizione è dove dovrei finalmente scoppiare. Dove sono? Ho eseguito? Io non ha dichiarato ancora. Quindi stampare sizeof (array) e che è 20, che si prevede in quanto la mia matrice è di dimensione 5 ed è su 5 numeri interi, così l'intera cosa dovrebbe essere 5 * sizeof (int) byte, dove sizeof (int) tende ad essere 4. Quindi, sizeof (array) è 20. Che cosa dovrebbe essere? [Studente] Diviso per sizeof (int). Sì >>, / sizeof (int). Sembra che ci sia ancora un problema qui. Credo che questo dovrebbe essere solo < dal momento che è più o meno sempre > [Bowden] Sì. Quando stiamo andando al di là della fine del nostro array, in qualche modo quello spazio che stiamo prevalente è prevalente il valore di i. E così, se guardiamo in buggy4, rompere principale, corsa, cerchiamo di stampare l'indirizzo di i. Sembra come se fosse bffff124. Ora stampare l'indirizzo di array [0]. 110. Che dire [1]? 114. [2], 118. 11c, 120. array [5] è bfff124. Così array [5] ha lo stesso indirizzo di i, il che significa che array [5] i è. Se hanno lo stesso indirizzo, sono la stessa cosa. Così, quando abbiamo fissato array [5] a 0, ci sono i impostazione a 0. E se pensi di questo in termini di stack, int i è dichiarato prima, il che significa che si po 'di spazio nello stack. Poi array [5] è assegnata, in modo quindi 20 byte sono allocati sullo stack. Così i vengono assegnate in primo luogo, allora questi 20 byte vengono assegnati. Così ho succede a destra prima la matrice, e per il modo in cui, come ho detto la scorsa settimana, ove tecnicamente lo stack cresce verso il basso, quando si indice in un array, abbiamo la garanzia che la posizione 0 nella matrice succede sempre prima della prima posizione nella matrice. Questa è una specie di come ho disegnato la settimana scorsa. Si noti che in fondo abbiamo indirizzo 0 e superiormente abbiamo Max indirizzo. Lo stack è sempre in crescita verso il basso. Diciamo che i allocare. Abbiamo allocare intero i, il che significa che diciamo solo che qui intero i viene assegnato. Poi allocare il nostro array di 5 interi, il che significa che al di sotto che, in quanto lo stack cresce verso il basso, quei 5 numeri interi vengono assegnati. Ma a causa di come gli array funzionano, abbiamo la garanzia che la prima posizione nella matrice ha sempre indirizzo inferiore alla seconda cosa nella matrice. Così la posizione di 0 array deve sempre avvenire prima in memoria, considerando che la posizione matrice 1 deve avvenire dopo che e la posizione di matrice 2 deve avvenire dopo che, il che significa che la posizione 0 dell'array sarebbe accaduto da qualche parte qui, posizione matrice 1 sarebbe accaduto in precedenza che perché salendo significa indirizzi superiori in quanto l'indirizzo massimo è qui. Così array [0] qui, array [1] qui, array [2] qui, array [3] qui. Notate come prima assegnato intero i tutta la strada fino qui, come ci muoviamo sempre più nel nostro array, ci stiamo avvicinando sempre di più al nostro intero i. Si dà il caso che la matrice [5], che è una posizione oltre la nostra matrice, è esattamente dove mi è capitato intero da assegnare. Ecco, questo è il punto in cui ci capita di essere colpendo lo spazio sullo stack che è stato assegnato per intero i, e che stiamo impostando a 0. È così che funziona. Domande? Gia '. [Studente] Non importa. Va bene. [Studente] Come si fa a evitare questo tipo di errori? Questi tipo di errori? Non utilizzare C come linguaggio di programmazione. Utilizzare un linguaggio che ha limiti di matrice di controllo. Finché si sta attenti, non vi resta che evitare di andare oltre i confini della vostra matrice. [Studente] Così qui quando siamo andati oltre i limiti della matrice - [Bowden] Questo è dove le cose iniziano ad andare male. >> [Studente] Oh, va bene. Fino a quando si rimane all'interno della memoria allocata per l'array, che stai bene. Ma C non fa il controllo degli errori. Se faccio array [1000], che sarà lieto di solo modificare qualunque cosa accada - Va all'inizio della matrice, poi va 1000 posizioni dopo e lo imposta a 0. Non fa alcun controllo che oh, questo in realtà non ha 1000 cose in esso. 1000 è ben oltre quello che deve cambiare, che Java o qualcosa avrai serie di limiti dell'indice o indice di eccezione limiti. È per questo che un sacco di linguaggi di livello superiore hanno queste cose dove se si va oltre i limiti della matrice, non si riesce in modo che non è possibile cambiare le cose da sotto di voi e poi le cose vanno molto peggio di un semplice ottenere un'eccezione dicendo che sei andato oltre la fine della matrice. [Studente] E così dovrebbe abbiamo appena cambiato il <= a solo > [Bowden] Si '. Dovrebbe essere > [Studente] destro. Altre domande? Va bene. [Studente] Ho una domanda. Sì >>. [Studente] Qual è la variabile di matrice reale? [Bowden] Come quello che è array? Matrice stessa è un simbolo. È solo l'indirizzo iniziale dei 20 byte che stiamo riferimento. Si può pensare ad esso come un puntatore, ma è un puntatore costante. Non appena le cose si compilato, l'array variabile non esiste più. [Studente] Così come si trova la dimensione della matrice? Dimensione della matrice si riferisce alla dimensione del blocco che quel simbolo si riferisce. Quando faccio qualcosa di simile a printf ("% p \ n", array); cerchiamo di farlo funzionare. Cosa ho appena fatto di sbagliato? 'Allineamento' Array dichiarato qui. Oh, qui. Clang è intelligente, e capita di notare che ho dichiarato l'array come 5 elementi ma io sono l'indicizzazione in posizione 1000. Si può farlo perché questi sono solo costanti. Si può solo andare così lontano a meno di notare che sto andando oltre i limiti della matrice. Ma notate prima, quando abbiamo avuto i non essere corretto, non è in grado di determinare il numero di valori che ho potuto assumere, quindi non può determinare che andavo oltre la fine della matrice. Questo è solo Clang di essere intelligente. Ma ora fare buggy4. Quindi, che cosa sto facendo di sbagliato? Implicitamente dichiarando la funzione di libreria 'printf'. Ho intenzione di voler includere # . Va bene. Ora esecuzione buggy4. Stampa il valore della matrice come ho fatto qui, la stampa come un puntatore stampe qualcosa che assomiglia a questo - bfb8805c - che è un po 'di indirizzo che è nello stack-ish regione. Matrice stessa è come un puntatore, ma non è un puntatore effettivo, da un normale puntatore possiamo cambiare. Array è solo una costante. I 20 blocchi di memoria partono 0xbfb8805c indirizzo. Quindi bfb8805c attraverso questo indirizzo +20--o credo -20 - è tutta la memoria allocata per questa matrice. Array, la variabile stessa non viene memorizzata da nessuna parte. Quando si sta compilando, il compilatore - onda mano a questo - ma il compilatore, basta usare dove sa di essere di matrice. Si sa dove tale array inizia, e in modo che possa sempre e solo fare le cose in termini di offset da quell'inizio. Non ha bisogno di una stessa variabile per rappresentare array. Ma quando faccio qualcosa di simile int * p = array, ora p è un puntatore che punta a tale matrice, e ora p in realtà non esistono in pila. Sono liberi di cambiare p. Posso fare p = malloc. Quindi, in origine indicava matrice, ora punta a un pò di spazio sul mucchio. Non posso fare di matrice = malloc. Se Clang è intelligente, sarà urlarmi destra fuori del blocco. A dire il vero, sono abbastanza sicuro che gcc avrebbe fatto anche questo. Quindi tipo di matrice 'int [5]' non è cedibile. Non è possibile assegnare qualcosa a un tipo di matrice perché array è solo una costante. E 'un simbolo che fa riferimento a quei 20 byte. Non si può cambiare. [Studente] E dove è la dimensione della matrice memorizzata? [Bowden] Non è memorizzato da nessuna parte. E 'quando si sta compilando. Allora, dove è la dimensione di array memorizzato? È possibile utilizzare solo sizeof (array) all'interno della funzione che l'array è si è dichiarata. Quindi, se faccio qualche funzione, foo, e lo faccio (int array []) printf ("% d \ n", sizeof (array)); e poi qui io chiamo foo (array); all'interno di questa funzione - cerchiamo di farlo funzionare. Questo è Clang essere intelligente ancora. Mi sta dicendo che sizeof il parametro di matrice funzione tornerà dimensione del 'int *'. Questo sarebbe un errore se non è quello che volevo per accadere. Andiamo in realtà spegnere Werror. Attenzione. Avvertenze vanno bene. Sarà comunque compilare purché dotato di un allarme. . / A.out sta per stampare 4. L'avviso che è stato generato è un chiaro indicatore di ciò che è andato storto. Questa matrice int è solo andare a stampare sizeof (int *). Anche se ho messo array [5] qui dentro, è ancora solo andando a stampare sizeof (int *). Quindi, non appena si passa in una funzione, la distinzione tra array e puntatori è inesistente. Questa sembra essere una matrice che è stata dichiarata in pila, ma non appena si passa tale valore, che 0xBF blah, blah, blah in questa funzione, allora questo puntatore punta a tale matrice nello stack. Quindi questo significa che sizeof vale solo per la funzione che l'array è stato dichiarato, il che significa che quando si compila questa funzione, Clang quando passa attraverso questa funzione, vede array è un array di int di dimensione 5. Allora vede sizeof (array). Beh, e '20. Che in realtà è come sizeof funziona praticamente per quasi tutti i casi. Sizeof non è una funzione, è un operatore. Non si chiama la funzione sizeof. Sizeof (int), il compilatore sarà solo tradurlo a 4. Capito? Va bene. [Studente] Allora, qual è la differenza tra sizeof (array) in main e in foo? Questo è dovuto al fatto che stiamo dicendo sizeof (array), che è di tipo int *, mentre la matrice quaggiù è non di tipo int *, è un array int. [Studente] Quindi, se si ha il parametro in array [] invece di matrice * int, potrebbe significare che si potrebbe ancora cambiare matrice perché ora si tratta di un puntatore? [Bowden] Ti piace questa? >> [Studente] Si '. Potete cambiare matrice all'interno della funzione ora? [Bowden] Si potrebbe cambiare matrice in entrambi i casi. In entrambi i casi si è liberi di dire array [4] = 0. [Studente] Ma si può fare il punto matrice a qualcos'altro? [Bowden] Oh. Gia '. In entrambi i casi - >> [studente] Si '. [Bowden] La distinzione tra array [] e un array int *, non c'è nessuno. È inoltre possibile ottenere un po 'di array multidimensionale in qui per un po 'la sintassi conveniente, ma è ancora solo un puntatore. Questo significa che sono libero di fare array = malloc (sizeof (int)), e ora puntare da qualche altra parte. Ma proprio come come questo funziona sempre e sempre, modificare questa matrice facendolo puntare a qualcosa d'altro non modifica l'array qui perché è una copia della tesi, non è un puntatore a tale argomento. E in realtà, proprio come più indicazione che è esattamente la stessa cosa - abbiamo già visto che cosa stampe di matrice di stampa - cosa succederebbe se stampare l'indirizzo della matrice o l'indirizzo dell'indirizzo della matrice a uno di questi? Ignoriamo questo. Va bene. Questo va bene. E 'ora in esecuzione. / A.out. Matrice di stampa, quindi stampando l'indirizzo della matrice, sono la stessa cosa. Array semplicemente non esiste. Si sa quando si stampa matrice, si stampa il simbolo che si riferisce a quei 20 byte. Stampa l'indirizzo dell'array, beh, di matrice non esiste. Non ha un indirizzo, quindi viene stampato solo l'indirizzo di questi 20 byte. Non appena si compila il basso, come nel vostro buggy4 compilato. / A.out, array è inesistente. Puntatori esistono. Array non lo fanno. I blocchi di memoria che rappresentano la matrice esistono ancora, ma la matrice variabile e variabili di tale tipo non esistono. Quelli sono come le principali differenze tra vettori e puntatori sono appena si effettua una chiamata di funzione, non c'è differenza. Ma all'interno della funzione che la stessa matrice viene dichiarata, sizeof funziona in modo diverso poiché si sta stampando la dimensione dei blocchi invece della dimensione del tipo, e non si può cambiare perché è un simbolo. Stampa la cosa e l'indirizzo della cosa stampa la stessa cosa. E questo è più o meno di esso. [Studente] Puoi dire che ancora una volta? Potrei aver perso qualcosa. Matrice di stampa e l'indirizzo della matrice di stampa la stessa cosa, mentre se si stampa un puntatore contro l'indirizzo del puntatore, l'unica cosa che viene stampato l'indirizzo di quello che stai puntando, l'altra stampa l'indirizzo del puntatore in pila. È possibile modificare un puntatore, non è possibile modificare un simbolo di matrice. E sizeof puntatore sta per stampare la dimensione di quel tipo puntatore. Così int * p sizeof (p) sta per stampare 4, ma int array [5] stampa sizeof (array) è andare in stampa 20. [Studente] Così int array [5] viene stampato 20? Sì >>. Ecco perché all'interno di buggy4 quando è usato per essere sizeof (array) questo è stato fatto i <20, che non è quello che volevamo. Vogliamo che i <5. >> [Studente] Ok. [Bowden] E poi, non appena si inizia il passaggio nelle funzioni, se abbiamo fatto int * p = array; all'interno di questa funzione, si può sostanzialmente usare p e matrice in esattamente gli stessi metodi, tranne per il problema sizeof e il problema cambiando. Ma p [0] = 1; è come dire array [0] = 1; E non appena si dice foo (array), oppure foo (p); all'interno della funzione foo, questa è la stessa chiamata due volte. Non vi è alcuna differenza tra queste due chiamate. Tutti bene su questo? Va bene. Abbiamo 10 minuti. Cercheremo di ottenere attraverso questo programma Hacker Typer, questo sito, che è uscito l'anno scorso o qualcosa del genere. E 'solo dovrebbe essere come si digita in modo casuale e stampata - Qualsiasi file, capita di aver caricato è quello che sembra si sta digitando. Sembra una specie di codice del sistema operativo. Questo è quello che vogliamo implementare. Si dovrebbe avere un binario eseguibile di nome hacker_typer che prende in un singolo argomento, il file in "tipo di hacker." L'esecuzione del file eseguibile deve pulire lo schermo e poi stampare un carattere dalla passata nel file ogni volta che l'utente preme un tasto. Quindi, qualsiasi tasto premuto, si dovrebbe buttare via e invece stampare un carattere dal file che è l'argomento. Io più o meno quello che ti dico le cose che stiamo andando ad avere bisogno di sapere sono. Ma noi vogliamo controllare la libreria termios. Non ho mai usato questa libreria in tutta la mia vita, quindi ha scopi molto minimale. Ma questo sta andando essere la biblioteca che possiamo usare per buttare via il personaggio che ha colpito quando si sta digitando in standard da Così hacker_typer.c, e stiamo andando a voler includere # . Guardando la pagina di manuale di termios - Sto indovinando che terminale di sistema operativo o qualcosa del genere - Io non so come leggerlo. Guardando questa, si dice per includere questi 2 file, quindi lo faremo. Per prima cosa, vogliamo prendere in un singolo argomento, che è il file che deve aprire. Allora, cosa voglio fare? Come posso controllare per vedere che ho un solo argomento? [Studente] Se argc è uguale. >> [Bowden] Si '. Quindi, se (argc = 2!) Printf ("Uso:% s [file per aprire]"). Così ora se corro questo senza fornire un secondo argomento - oh, mi serve la nuova linea - vedrete che dice di utilizzo:. / hacker_typer, e poi il secondo argomento deve essere il file che voglio aprire. Ora cosa devo fare? Voglio leggere da questo file. Come faccio a leggere da un file? [Studente] Lo aprire prima. Sì >>. Così fopen. Che cosa significa fopen assomiglia? [Studente] Nome file. >> [Bowden] Nome del file sta per essere argv [1]. [Studente] E poi che cosa si vuole fare con essa, in modo che il - >> [Bowden] Si '. Quindi, se non si ricordava, appena poteva fare fopen uomo, dove sta andando ad essere un percorso const char * dove percorso è il nome file, const char * mode. Se vi capita di non ricordare ciò che modo è, allora si può guardare per la modalità. All'interno delle pagine man, il carattere barra è quello che è possibile utilizzare per la ricerca di cose. Così ho tipo / modalità per la ricerca di modalità. n e N sono ciò che è possibile utilizzare per scorrere le partite di ricerca. Qui si dice che i punti argomento mode di una stringa iniziando con una delle seguenti sequenze. Quindi r, file di testo aperto per la lettura. Questo è quello che vogliamo fare. Per la lettura, e voglio conservare questo. La cosa sta andando essere un * FILE. Ora, che cosa voglio fare? Dammi un secondo. Va bene. Ora, che cosa voglio fare? [Studente] Controllare se è NULL. >> [Bowden] Si '. Ogni volta che si apre un file, assicurarsi che siete successo in grado di aprirlo. Ora voglio fare quella roba termios dove voglio prima leggere le mie impostazioni correnti e salvare quelli in qualcosa, allora voglio cambiare le mie impostazioni buttare via qualsiasi carattere di tipo I, e poi voglio aggiornare tali impostazioni. E poi alla fine del programma, voglio tornare ai miei impostazioni originali. Così la struttura sta per essere di termios tipo, e ho intenzione di voler due di questi. Il primo sta per essere i miei current_settings, e poi si sta andando ad essere i miei hacker_settings. In primo luogo, ho intenzione di salvare le impostazioni correnti, allora ho intenzione di voler aggiornare hacker_settings, e poi via, alla fine del mio programma, voglio ripristinare le impostazioni correnti. Quindi il salvataggio delle impostazioni correnti, il modo in cui funziona, noi termios uomo. Vediamo che abbiamo questo tcsetattr int, int tcgetattr. Passo in una struttura termios dal suo puntatore. Il modo in cui questo aspetto è - ho già dimenticato quello che la funzione è stata chiamata. Copia e incolla. Così tcgetattr, allora voglio passare nella struttura che sto salvando le informazioni, che sta per essere current_settings, e il primo argomento è il descrittore di file per la cosa che desidera salvare gli attributi di. Quello che il descrittore di file è è come ogni volta che si apre un file, si ottiene un descrittore di file. Quando ho fopen argv [1], si ottiene un descrittore di file, che si fa riferimento ogni volta che si vuole leggere o scrivere su di esso. Questo non è il descrittore di file che voglio usare qui. Ci sono tre descrittori di file che avete per impostazione predefinita, che sono standard, fuori standard, e di errore standard. Per impostazione predefinita, penso che sia standard è 0, fuori standard è 1, e l'errore standard è 2. Allora, cosa voglio cambiare le impostazioni di? Voglio cambiare le impostazioni ogni volta che mi ha colpito un personaggio, Voglio che buttare via quel carattere invece di stamparlo sullo schermo. Che acqua - standard, fuori standard o errore standard - risponde alle cose quando si digita sulla tastiera? >> [Studente] Standard trovi >> Gia '. Così può fare 0 o posso fare stdin. Sto ricevendo il current_settings di standard da Ora voglio aggiornare tali impostazioni, quindi prima mi copiare nel hacker_settings quello che i miei current_settings sono. E come il lavoro di strutture è sarà solo copiare. Questo consente di copiare tutti i campi, come ci si aspetterebbe. Ora voglio aggiornare alcuni dei campi. Guardando termios, si dovrà leggere un sacco di questo solo per vedere quello che si desidera cercare, ma le bandiere si sta andando a voler cercare sono eco, così ECHO caratteri di input Echo. Prima di tutto voglio impostare - ho già dimenticato quello che i campi sono. Questo è ciò che la struttura assomiglia. Così modalità di ingresso penso che vogliamo cambiare. Vedremo la soluzione per fare in modo che è quello che vogliamo cambiare. Vogliamo cambiare lflag per evitare la necessità di guardare attraverso tutti questi. Vogliamo cambiare le modalità locali. Si dovrebbe leggere tutta questa cosa per capire dove tutto appartiene che si desidera modificare. Ma è all'interno di modalità di locali dove stiamo andando a voler cambiare la situazione. Quindi hacker_settings.cc_lmode è come si chiama. c_lflag. Questo è dove si riesce a mettere gli operatori bit per bit. Siamo un po 'fuori dal tempo, ma andremo in fretta reale. E 'qui che entriamo in operatori bit a bit, dove credo di aver detto una volta tempo fa che ogni volta che iniziare a trattare con le bandiere, avete intenzione di utilizzare l'operatore bit a bit molto. Ogni bit nella bandiera corrisponde a una sorta di comportamento. Ecco, questo flag ha un sacco di cose diverse, dove tutti significano qualcosa di diverso. Ma quello che voglio fare è semplicemente spegnere il bit che corrisponde a ECHO. Quindi, per spegnerlo faccio & = ¬ ECHO. A dire il vero, penso che sia come techo o qualcosa del genere. Sto solo andando a controllare di nuovo. Posso termios. E 'solo ECHO. ECHO sta per essere un po 'sola. ¬ ECHO sta a significare tutti i bit sono impostati a 1, il che significa che tutti i parametri sono impostati su true tranne che per il bit di ECHO. Per finire i miei bandiere locali con questo, vuol dire tutti i flag che sono attualmente impostata su true sarà ancora impostata su true. Se la mia bandiera ECHO è impostato su true, allora questo è necessariamente impostato su false sulla bandiera ECHO. Quindi, questa riga di codice si appena fuori la bandiera ECHO. Le altre linee di codice, mi limiterò a copiarli nell'interesse di tempo e poi spiegarle. Nella soluzione, ha detto 0. E 'probabilmente meglio dire esplicitamente stdin. Si noti che sto facendo anche ECHO | icanon qui. Icanon si riferisce a qualcosa di separato, il che significa che la modalità canonica. Che cosa significa la modalità canonica è di solito quando si sta digitando la riga di comando, standard non elabora nulla fino a colpire newline. Così, quando si fa GetString, si digita un sacco di cose, allora si colpisce newline. Questo è quando è inviato a standard da Questo è il valore predefinito. Quando ho disattivare la modalità canonica, ora ogni singolo carattere che si preme è ciò che viene elaborato, che di solito è tipo di male, perché è lento per elaborare queste cose, è per questo che è bene di tamponare in linee intere. Ma voglio che ogni carattere da elaborare dal momento che non ne vogliono sapere di attendere per me per colpire newline prima di elaborare tutti i personaggi che ho scrivendo. Questo disattiva la modalità canonica. Questa roba significa solo durante l'elaborazione in realtà caratteri. Ciò significa elaborare immediatamente, non appena li sto scrivendo, la loro elaborazione. E questa è la funzione che sta aggiornando le mie impostazioni per la standard, e mezzi TCSA farlo adesso. Le altre opzioni sono attendere che tutto ciò che è attualmente il flusso viene elaborato. Questo non ha molta importanza. Proprio in questo momento modificare le impostazioni di essere tutto ciò che è attualmente in hacker_typer_settings. Credo che ho chiamato hacker_settings, quindi cerchiamo di cambiare la situazione. Cambiare tutto per hacker_settings. Ora, alla fine del nostro programma che stiamo andando a voler tornare a ciò che è attualmente all'interno di normal_settings, che sta a guardare, proprio come e normal_settings. Avviso Non ho cambiato nessuno dei miei normal_settings quanto originariamente sempre. Poi, per semplicemente cambiare di nuovo, li passare di nuovo alla fine. Questo era l'aggiornamento. Va bene. Ora, all'interno di qui mi limiterò a spiegare il codice nell'interesse del tempo. Non è che molto codice. Vediamo si legge un carattere dal file. Lo abbiamo chiamato f. Ora è possibile fgetc uomo, ma come fgetc sta andando a lavorare è solo sta andando a restituire il carattere che hai appena letto o EOF, che corrisponde alla fine del file o qualche evento di errore. Stiamo looping, continuando a leggere un singolo carattere dal file, finché non avremo finito di caratteri da leggere. E mentre lo stiamo facendo, siamo in attesa di un singolo carattere da standard da Ogni volta che si digita qualcosa nella riga di comando, che è la lettura di un carattere da standard da Poi putchar è solo andare a mettere il char si legge qui dal file di standard out. È possibile putchar uomo, ma è solo mettendo sullo standard output, è la stampa di quel personaggio. Si potrebbe anche fare solo printf ("% c", c); stessa idea. Quello sta andando a fare la maggior parte del nostro lavoro. L'ultima cosa che stiamo andando a voler fare è solo fclose il nostro file. Se non si fclose, che è una perdita di memoria. Vogliamo fclose il file che originariamente aperto, e penso che sia. Se facciamo questo, ho già avuto problemi. Vediamo un po '. Che cosa ha lamentarsi? Previsto 'int', ma l'argomento è di tipo 'struct _IO_FILE *'. Staremo a vedere se funziona. Consentito solo in C99. Augh. Ok, fare hacker_typer. Ora abbiamo descrizioni più utili. Quindi, l'uso di identificatore non dichiarato 'normal_settings'. Io non chiamo normal_settings. Ho chiamato current_settings. Quindi cerchiamo di cambiare tutto questo. Ora passa argomento. Farò questo 0 per ora. Va bene. . / Hacker_typer cp.c. Ho anche non pulire lo schermo all'inizio. Ma si può guardare indietro al set ultimo problema per vedere come cancellare lo schermo. E 'solo la stampa di alcuni caratteri mentre questo è fare quello che voglio fare. Va bene. E pensare perché questo doveva essere 0 invece che da stdin, che dovrebbe essere # define 0, questo si lamenta che - Prima quando ho detto che non c'è descrittori di file, ma poi hai anche il tuo * FILE, un descrittore di file è solo un numero intero, mentre un * FILE ha un sacco di cose ad esso associati. La ragione per cui abbiamo bisogno di dire 0 invece che da stdin è che stdin è un FILE * che punta alla cosa che fa riferimento descrittore di file 0. Così, anche qui quando faccio fopen (argv [1], sto diventando un FILE * indietro. Ma da qualche parte in quel * FILE è una cosa corrispondente al descrittore di file per quel file. Se guardate la pagina man per open, quindi penso che dovrete fare man 3 aperto - No. - man 2 open - si '. Se guardate la pagina per open, aperto è come un livello inferiore fopen, ed è restituire il descrittore di file effettivo. fopen fa un po 'di cose su di aperto, che invece di restituire solo che descrittore di file restituisce un puntatore FILE intero * all'interno del quale è il nostro piccolo descrittore di file. Così standard si riferisce alla cosa * FILE, mentre 0 si riferisce al solo standard di descrittore di file in sé. Domande? [Ride] si è allargato attraverso quella. Bene. Abbiamo finito. [Ride] [CS50.TV]