1 00:00:00,000 --> 00:00:02,490 [Powered by Google Translate] [CS50 Library] 2 00:00:02,490 --> 00:00:04,220 [Nate Hardison] [Harvard University] 3 00:00:04,220 --> 00:00:07,260 [Questo è CS50. CS50.TV] 4 00:00:07,260 --> 00:00:11,510 La biblioteca CS50 è un utile strumento che abbiamo installato l'apparecchio 5 00:00:11,510 --> 00:00:15,870 per rendere più facile per voi a scrivere programmi che richiede agli utenti per l'ingresso. 6 00:00:15,870 --> 00:00:21,670 In questo video, ci tira la tenda e guardare a ciò che esattamente si trova nella libreria CS50. 7 00:00:21,670 --> 00:00:25,520 >> Nel video di librerie C, si parla di come si # include file di header 8 00:00:25,520 --> 00:00:27,570 della biblioteca nel codice sorgente, 9 00:00:27,570 --> 00:00:31,150 e poi si collega con un file di libreria binario durante la fase di collegamento 10 00:00:31,150 --> 00:00:33,140 del processo di compilazione. 11 00:00:33,140 --> 00:00:36,440 I file di intestazione specificare l'interfaccia della libreria. 12 00:00:36,440 --> 00:00:41,280 Cioè, dettaglio tutte le risorse che la biblioteca ha a disposizione per l'uso, 13 00:00:41,280 --> 00:00:45,250 come dichiarazioni di funzioni, costanti e tipi di dati. 14 00:00:45,250 --> 00:00:48,890 Il file di libreria binario contiene l'implementazione della biblioteca, 15 00:00:48,890 --> 00:00:54,580 che viene compilato dal file di intestazione della biblioteca e della biblioteca. c file di codice sorgente. 16 00:00:54,580 --> 00:00:59,820 >> Il file di libreria binario non è molto interessante da guardare dal momento che è, beh, in binario. 17 00:00:59,820 --> 00:01:03,300 Quindi, diamo uno sguardo ai file header per la libreria, invece. 18 00:01:03,300 --> 00:01:07,710 In questo caso, c'è solo un file header chiamato cs50.h. 19 00:01:07,710 --> 00:01:11,040 Lo abbiamo installato nella directory utente includono 20 00:01:11,040 --> 00:01:15,150 insieme ai file di intestazione di librerie di sistema degli altri. 21 00:01:15,150 --> 00:01:21,530 >> Una delle prime cose che noterete è che cs50.h # include file di intestazione di altre biblioteche - 22 00:01:21,530 --> 00:01:25,670 galleggiante, limiti, serie bool, e lib standard. 23 00:01:25,670 --> 00:01:28,800 Anche in questo caso, secondo il principio di non reinventare la ruota, 24 00:01:28,800 --> 00:01:33,490 abbiamo costruito la libreria CS0 utilizzando strumenti che hanno fornito altri per noi. 25 00:01:33,490 --> 00:01:38,690 >> La prossima cosa che vedrete in biblioteca è che si definisce un nuovo tipo di "stringa". 26 00:01:38,690 --> 00:01:42,330 Questa linea in realtà solo crea un alias per il tipo char *, 27 00:01:42,330 --> 00:01:46,000 in modo che non magicamente infondere il nuovo tipo di stringa con gli attributi 28 00:01:46,000 --> 00:01:49,650 comunemente associati con gli oggetti stringa in altre lingue, 29 00:01:49,650 --> 00:01:50,850 come lunghezza. 30 00:01:50,850 --> 00:01:55,180 Il motivo per cui abbiamo fatto è quello di proteggere i nuovi programmatori dai dettagli scabrosi 31 00:01:55,180 --> 00:01:57,580 di puntatori fino a quando sono pronti. 32 00:01:57,580 --> 00:02:00,130 >> La parte successiva del file di intestazione è la dichiarazione delle funzioni 33 00:02:00,130 --> 00:02:04,410 che il CS50 libreria fornisce insieme alla documentazione. 34 00:02:04,410 --> 00:02:06,940 Si noti il ​​livello di dettaglio nei commenti qui. 35 00:02:06,940 --> 00:02:10,560 Questo è super importante in modo che la gente sappia come usare queste funzioni. 36 00:02:10,560 --> 00:02:19,150 Si dichiara, a sua volta, funziona per richiedere all'utente e caratteri di ritorno, doppie, galleggianti, interi, 37 00:02:19,150 --> 00:02:24,160 lungo anela, e archi, usando il nostro proprio tipo di stringa. 38 00:02:24,160 --> 00:02:26,260 Secondo il principio di information hiding, 39 00:02:26,260 --> 00:02:31,640 abbiamo messo la nostra definizione in un apposito file di implementazione c -. cs50.c-- 40 00:02:31,640 --> 00:02:35,110 si trova nella directory di origine dell'utente. 41 00:02:35,110 --> 00:02:38,040 Abbiamo messo a disposizione il file in modo da poter dare un'occhiata a esso, 42 00:02:38,040 --> 00:02:41,490 imparare da esso, e ricompilarlo su macchine diverse se lo si desidera, 43 00:02:41,490 --> 00:02:45,510 anche se pensiamo che sia meglio lavorare sulla macchina per questa classe. 44 00:02:45,510 --> 00:02:47,580 In ogni caso, diamo un'occhiata ora. 45 00:02:49,020 --> 00:02:54,620 >> Le funzioni getchar, GetDouble, getFloat, GetInt, e GetLongLong 46 00:02:54,620 --> 00:02:58,160 sono tutti costruiti sulla sommità della funzione GetString. 47 00:02:58,160 --> 00:03:01,510 Si scopre che tutti seguono sostanzialmente lo stesso schema. 48 00:03:01,510 --> 00:03:04,870 Usano un ciclo while per richiedere all'utente per una riga di input. 49 00:03:04,870 --> 00:03:08,430 Essi restituiscono un valore speciale se l'utente immette una riga vuota. 50 00:03:08,430 --> 00:03:11,750 Essi cercano di analizzare l'input dell'utente come il tipo appropriato, 51 00:03:11,750 --> 00:03:15,010 che si tratti di un char, un doppio, un float, ecc 52 00:03:15,010 --> 00:03:18,710 E poi restituirà il risultato se l'ingresso è stato analizzato correttamente 53 00:03:18,710 --> 00:03:21,330 oppure reprompt dell'utente. 54 00:03:21,330 --> 00:03:24,230 >> Ad alto livello, non c'è nulla di davvero difficile qui. 55 00:03:24,230 --> 00:03:28,760 Si potrebbe avere scritto il codice allo stesso modo strutturato te in passato. 56 00:03:28,760 --> 00:03:34,720 Forse la parte più criptico di aspetto è la chiamata sscanf che analizza l'input dell'utente. 57 00:03:34,720 --> 00:03:38,160 Sscanf fa parte della famiglia di conversione di formato di input. 58 00:03:38,160 --> 00:03:42,300 Vive in io.h standard, e il suo compito è quello di analizzare una stringa C, 59 00:03:42,300 --> 00:03:46,520 secondo un particolare formato, la memorizzazione dei risultati di analisi a variabili 60 00:03:46,520 --> 00:03:48,720 fornito dal chiamante. 61 00:03:48,720 --> 00:03:53,570 Poiché le funzioni di conversione del formato di input sono molto utili, funzioni ampiamente utilizzati 62 00:03:53,570 --> 00:03:56,160 che non sono super intuitiva in un primo momento, 63 00:03:56,160 --> 00:03:58,300 andremo su come sscanf funziona. 64 00:03:58,300 --> 00:04:03,330 >> Il primo argomento a sscanf è un char * - un puntatore a un carattere. 65 00:04:03,330 --> 00:04:05,150 Per la funzione per funzionare correttamente, 66 00:04:05,150 --> 00:04:08,340 quel personaggio dovrebbe essere il primo carattere di una stringa C, 67 00:04:08,340 --> 00:04:12,270 terminato con il nulla carattere \ 0. 68 00:04:12,270 --> 00:04:15,120 Questa è la stringa da analizzare 69 00:04:15,120 --> 00:04:18,269 Il secondo argomento a sscanf è una stringa di formato, 70 00:04:18,269 --> 00:04:20,839 tipicamente passato come una costante stringa, 71 00:04:20,839 --> 00:04:24,040 e si potrebbe aver visto una stringa come questa prima di quando si utilizza printf. 72 00:04:24,040 --> 00:04:28,650 Un segno di percentuale nella stringa di formato indica un indicatore di conversione. 73 00:04:28,650 --> 00:04:30,850 Il carattere immediatamente dopo un segno di percentuale, 74 00:04:30,850 --> 00:04:35,430 indica il tipo C che vogliamo convertire in sscanf. 75 00:04:35,430 --> 00:04:40,090 In GetInt, si vede che c'è un d% e% c. 76 00:04:40,090 --> 00:04:48,690 Ciò significa che sscanf cercherà di un decimale int - la% d - e un char - il c%. 77 00:04:48,690 --> 00:04:51,510 Per ogni indicatore di conversione nella stringa di formato, 78 00:04:51,510 --> 00:04:56,620 sscanf si aspetta un argomento corrispondente successiva nella lista degli argomenti. 79 00:04:56,620 --> 00:05:00,850 Questo argomento deve puntare a una posizione adeguatamente tipizzato 80 00:05:00,850 --> 00:05:04,000 in cui memorizzare il risultato della conversione. 81 00:05:04,000 --> 00:05:08,910 >> Il modo tipico di fare questo è quello di creare una variabile sullo stack prima della chiamata sscanf 82 00:05:08,910 --> 00:05:11,440 per ogni elemento che si desidera analizzare dalla stringa 83 00:05:11,440 --> 00:05:15,520 e quindi utilizzare l'operatore di indirizzo - la e commerciale - di passare puntatori 84 00:05:15,520 --> 00:05:19,100 a quelle variabili alla chiamata sscanf. 85 00:05:19,100 --> 00:05:22,720 Si può vedere che in GetInt facciamo esattamente questo. 86 00:05:22,720 --> 00:05:28,240 Poco prima della chiamata sscanf, dichiariamo un int chiamato n e c char chiamata sullo stack, 87 00:05:28,240 --> 00:05:32,340 e passiamo puntatori a loro nella chiamata sscanf. 88 00:05:32,340 --> 00:05:35,800 Mettendo queste variabili nello stack è preferito rispetto all'uso di spazio allocato 89 00:05:35,800 --> 00:05:39,350 sul mucchio con malloc, dal momento che si evita il sovraccarico della chiamata malloc, 90 00:05:39,350 --> 00:05:43,060 e non dovete preoccuparvi di perdite di memoria. 91 00:05:43,060 --> 00:05:47,280 I caratteri non preceduti da un segno di percentuale non richiede la conversione. 92 00:05:47,280 --> 00:05:50,380 Piuttosto è sufficiente aggiungere alla specifica di formato. 93 00:05:50,380 --> 00:05:56,500 >> Ad esempio, se la stringa di formato in GetInt erano un d% invece, 94 00:05:56,500 --> 00:05:59,800 sscanf sarebbe per la lettera a seguita da un int, 95 00:05:59,800 --> 00:06:04,360 e mentre si cerca di convertire il int, non avrebbe fatto qualsiasi altra cosa con la a. 96 00:06:04,360 --> 00:06:07,440 L'unica eccezione a questo è spazio vuoto. 97 00:06:07,440 --> 00:06:11,030 Spazi vuoti nella stringa di formato corrisponde a nessuna quantità di spazi - 98 00:06:11,030 --> 00:06:12,890 anche del tutto assenti. 99 00:06:12,890 --> 00:06:18,100 Quindi, è per questo che il commento menziona eventualmente con i più importanti e / o di spazio bianco in coda. 100 00:06:18,100 --> 00:06:22,910 Quindi, a questo punto sembra che il nostro appello sscanf cercherà di analizzare la stringa di input dell'utente 101 00:06:22,910 --> 00:06:25,380 controllando eventualmente preceduto da spazio bianco, 102 00:06:25,380 --> 00:06:29,300 seguito da un int che verrà convertito e memorizzato nella variabile n int 103 00:06:29,300 --> 00:06:33,090 seguita da una certa quantità di spazio bianco, e seguito da un carattere 104 00:06:33,090 --> 00:06:35,810 memorizzato nella variabile c char. 105 00:06:35,810 --> 00:06:37,790 >> E il valore di ritorno? 106 00:06:37,790 --> 00:06:41,560 Sscanf analizzerà la linea di ingresso dall'inizio alla fine, 107 00:06:41,560 --> 00:06:44,860 fermandosi quando raggiunge la fine o quando un personaggio in ingresso 108 00:06:44,860 --> 00:06:49,320 non corrisponde un carattere di formato o quando non è possibile effettuare una conversione. 109 00:06:49,320 --> 00:06:52,690 E 'il valore restituito viene utilizzato per individuare quando si è fermato. 110 00:06:52,690 --> 00:06:55,670 Se si è fermato, perché ha raggiunto la fine della stringa di input 111 00:06:55,670 --> 00:07:00,630 prima di effettuare qualsiasi conversione e prima di fallire per abbinare parte della stringa di formato, 112 00:07:00,630 --> 00:07:04,840 allora la costante EOF speciale viene restituito. 113 00:07:04,840 --> 00:07:08,200 In caso contrario, restituisce il numero di conversioni di successo, 114 00:07:08,200 --> 00:07:14,380 che potrebbe essere 0, 1, o 2, dal momento che abbiamo chiesto due conversioni. 115 00:07:14,380 --> 00:07:19,000 Nel nostro caso, si vuole fare in modo che l'utente ha digitato in un int e solo un int. 116 00:07:19,000 --> 00:07:23,370 >> Quindi, vogliamo tornare sscanf 1. Scopri perché? 117 00:07:23,370 --> 00:07:26,850 Se sscanf restituito 0, allora nessun conversioni sono state fatte, 118 00:07:26,850 --> 00:07:31,690 così l'utente ha digitato qualcosa di diverso da un int all'inizio dell'ingresso. 119 00:07:31,690 --> 00:07:37,100 Se sscanf restituisce 2, allora l'utente ha correttamente lo digitare all'inizio dell'ingresso, 120 00:07:37,100 --> 00:07:41,390 ma poi trascritte in qualche non-spazio bianco carattere dopo 121 00:07:41,390 --> 00:07:44,940 poiché la conversione% c riuscito. 122 00:07:44,940 --> 00:07:49,570 Wow, che è piuttosto una lunga spiegazione per una chiamata di funzione. 123 00:07:49,570 --> 00:07:53,460 In ogni caso, se volete maggiori informazioni su sscanf e dei suoi fratelli, 124 00:07:53,460 --> 00:07:57,130 controllare le pagine man, di Google, o entrambi. 125 00:07:57,130 --> 00:07:58,780 Ci sono un sacco di opzioni di stringa di formato, 126 00:07:58,780 --> 00:08:03,830 e questi possono risparmiare un sacco di lavoro manuale quando si cerca di analizzare le stringhe in C. 127 00:08:03,830 --> 00:08:07,180 >> L'ultima funzione nella libreria da guardare è GetString. 128 00:08:07,180 --> 00:08:10,310 Si scopre che GetString è una funzione difficile da scrivere correttamente, 129 00:08:10,310 --> 00:08:14,290 anche se sembra così semplice, compito comune. 130 00:08:14,290 --> 00:08:16,170 Perché è questo il caso? 131 00:08:16,170 --> 00:08:21,380 Bene, cerchiamo di pensare a come stiamo andando a memorizzare la linea che l'utente digita poll 132 00:08:21,380 --> 00:08:23,880 Poiché una stringa è una sequenza di caratteri, 133 00:08:23,880 --> 00:08:26,430 si potrebbe desiderare di conservarlo in un array sullo stack, 134 00:08:26,430 --> 00:08:31,250 ma avremmo bisogno di sapere quanto tempo l'array sarà quando ci si dichiara. 135 00:08:31,250 --> 00:08:34,030 Allo stesso modo, se vogliamo metterla sul mucchio, 136 00:08:34,030 --> 00:08:38,090 abbiamo bisogno di passare a malloc il numero di byte che vogliamo riserva, 137 00:08:38,090 --> 00:08:39,730 ma questo è impossibile. 138 00:08:39,730 --> 00:08:42,760 Non abbiamo idea di quanti caratteri l'utente dovrà digitare 139 00:08:42,760 --> 00:08:46,590 prima che l'utente in realtà non li digita. 140 00:08:46,590 --> 00:08:50,720 >> Una soluzione banale a questo problema è quello di prenotare solo una grossa fetta di spazio, ad esempio, 141 00:08:50,720 --> 00:08:54,540 un blocco di 1000 caratteri per l'input dell'utente, 142 00:08:54,540 --> 00:08:57,980 supponendo che l'utente non avrebbe mai digitare una stringa che a lungo. 143 00:08:57,980 --> 00:09:00,810 Questa è una cattiva idea per due motivi. 144 00:09:00,810 --> 00:09:05,280 In primo luogo, partendo dal presupposto che gli utenti in genere non digitare stringhe così a lungo, 145 00:09:05,280 --> 00:09:07,610 si rischia di sprecare un sacco di memoria. 146 00:09:07,610 --> 00:09:10,530 Sulle macchine moderne, questo potrebbe non essere un problema se si esegue questa operazione 147 00:09:10,530 --> 00:09:13,890 in uno o due casi isolati, 148 00:09:13,890 --> 00:09:17,630 ma se si sta prendendo l'input dell'utente in un ciclo e la conservazione per uso futuro, 149 00:09:17,630 --> 00:09:20,870 si può rapidamente aspirare una tonnellata di memoria. 150 00:09:20,870 --> 00:09:24,450 Inoltre, se il programma che si sta scrivendo è per un computer più piccolo - 151 00:09:24,450 --> 00:09:28,100 un dispositivo come uno smartphone o altro con memoria limitata - 152 00:09:28,100 --> 00:09:32,060 questa soluzione può causare problemi molto più veloce. 153 00:09:32,060 --> 00:09:36,450 La seconda, la ragione più grave di non farlo è che lascia il programma vulnerabile 154 00:09:36,450 --> 00:09:39,710 a quello che si chiama un attacco di tipo buffer overflow. 155 00:09:39,710 --> 00:09:45,840 In programmazione, un buffer di memoria è utilizzata per memorizzare temporaneamente i dati di ingresso o di uscita, 156 00:09:45,840 --> 00:09:48,980 che in questo caso è la nostra 1000-char blocco. 157 00:09:48,980 --> 00:09:53,370 Un buffer overflow si verifica quando i dati vengono scritti oltre la fine del blocco. 158 00:09:53,370 --> 00:09:57,790 >> Ad esempio, se un utente fa effettivamente tipo in più di 1000 caratteri. 159 00:09:57,790 --> 00:10:01,570 Si potrebbe avere sperimentato questo errore durante la programmazione con gli array. 160 00:10:01,570 --> 00:10:05,620 Se si dispone di un array di 10 interi, nulla vi impedisce di cercare di leggere o scrivere 161 00:10:05,620 --> 00:10:07,810 l'int 15. 162 00:10:07,810 --> 00:10:10,000 Non ci sono avvisi del compilatore o errori. 163 00:10:10,000 --> 00:10:13,250 Il programma solo errori dritto e accede alla memoria 164 00:10:13,250 --> 00:10:18,150 dove si pensa che la int 15 sarà, e questo può sovrascrivere le altre variabili. 165 00:10:18,150 --> 00:10:22,040 Nel peggiore dei casi, è possibile sovrascrivere alcuni interna del programma 166 00:10:22,040 --> 00:10:26,820 meccanismi di controllo, causando il programma da eseguire in realtà diverse istruzioni 167 00:10:26,820 --> 00:10:28,340 di quanto previsto. 168 00:10:28,340 --> 00:10:31,360 >> Ora, non è comune a fare questo errore, 169 00:10:31,360 --> 00:10:35,150 ma questa è una tecnica abbastanza comune che i cattivi usano per rompere i programmi 170 00:10:35,150 --> 00:10:39,080 e inserire il codice dannoso sul computer di altre persone. 171 00:10:39,080 --> 00:10:42,910 Pertanto, non possiamo utilizzare la nostra soluzione ingenua. 172 00:10:42,910 --> 00:10:45,590 Abbiamo bisogno di un modo per evitare che i nostri programmi di essere vulnerabile 173 00:10:45,590 --> 00:10:47,880 ad un attacco di tipo buffer overflow. 174 00:10:47,880 --> 00:10:51,430 Per fare questo, abbiamo bisogno di fare in modo che il buffer può crescere come si legge 175 00:10:51,430 --> 00:10:53,850 più input dall'utente. 176 00:10:53,850 --> 00:10:57,440 La soluzione? Usiamo un heap buffer allocato. 177 00:10:57,440 --> 00:10:59,950 Dal momento che siamo in grado di modificarne le dimensioni utilizzando il ridimensionamento della funzione realloc, 178 00:10:59,950 --> 00:11:04,580 e tenere traccia dei due numeri - l'indice dello slot vuota successiva nel buffer 179 00:11:04,580 --> 00:11:08,390 e la lunghezza o la capacità del buffer. 180 00:11:08,390 --> 00:11:13,210 Leggiamo in caratteri da parte dell'utente una alla volta utilizzando la funzione fgetc. 181 00:11:13,210 --> 00:11:19,360 L'argomento della funzione fgetc prende - stdin - è un riferimento alla stringa di input standard, 182 00:11:19,360 --> 00:11:23,810 che è un canale di ingresso preconnesso che viene utilizzato per trasferire input dell'utente 183 00:11:23,810 --> 00:11:26,270 dal terminale al programma. 184 00:11:26,270 --> 00:11:29,890 >> Ogni volta che l'utente digita in un nuovo personaggio, controlliamo per vedere se l'indice 185 00:11:29,890 --> 00:11:35,810 del prossimo slot libero più 1 è maggiore della capacità del buffer. 186 00:11:35,810 --> 00:11:39,690 Il 1 è disponibile in, perché se il primo indice libero è 5, 187 00:11:39,690 --> 00:11:44,150 poi la lunghezza il buffer deve essere di 6 a 0 grazie indicizzazione. 188 00:11:44,150 --> 00:11:48,350 Se abbiamo esaurito lo spazio nel buffer, poi si cerca di ridimensionare, 189 00:11:48,350 --> 00:11:51,690 raddoppiando così che ridurre il numero di volte che ridimensionare 190 00:11:51,690 --> 00:11:54,760 se l'utente sta scrivendo in una stringa molto lunga. 191 00:11:54,760 --> 00:11:57,950 Se la stringa è diventato troppo lungo o se siamo a corto di memoria heap, 192 00:11:57,950 --> 00:12:01,350 liberiamo il nostro buffer e nullo ritorno. 193 00:12:01,350 --> 00:12:04,170 >> Infine, aggiungere il carattere al buffer. 194 00:12:04,170 --> 00:12:08,200 Una volta che l'utente preme Invio, segnalando una nuova linea, 195 00:12:08,200 --> 00:12:12,050 o il carattere speciale - controllo d - che segnala la fine di input, 196 00:12:12,050 --> 00:12:16,240 facciamo un controllo per verificare se l'utente ha effettivamente digitato nulla. 197 00:12:16,240 --> 00:12:18,820 In caso contrario, restituire null. 198 00:12:18,820 --> 00:12:22,280 In caso contrario, perché il nostro buffer è probabilmente più grande di cui abbiamo bisogno, 199 00:12:22,280 --> 00:12:24,830 nel caso peggiore è quasi due volte più grande che dobbiamo 200 00:12:24,830 --> 00:12:27,830 dal momento che il doppio ogni volta che ridimensionare, 201 00:12:27,830 --> 00:12:31,840 facciamo una nuova copia della stringa utilizzando solo la quantità di spazio di cui abbiamo bisogno. 202 00:12:31,840 --> 00:12:34,220 Aggiungiamo un ulteriore 1 per la chiamata malloc, 203 00:12:34,220 --> 00:12:37,810 in modo che ci sia spazio per lo speciale carattere null di terminazione - il \ 0, 204 00:12:37,810 --> 00:12:41,990 che aggiungere alla stringa una volta copiare il resto dei personaggi, 205 00:12:41,990 --> 00:12:45,060 utilizzando strncpy invece di strcpy 206 00:12:45,060 --> 00:12:48,830 in modo da poter specificare esattamente il numero di caratteri che vogliamo copiare. 207 00:12:48,830 --> 00:12:51,690 Strcpy copia fino a quando non colpisce un \ 0. 208 00:12:51,690 --> 00:12:55,740 Poi liberare il nostro buffer e restituire la copia al chiamante. 209 00:12:55,740 --> 00:12:59,840 >> Chi sapeva così semplice apparentemente funzione potrebbe essere così complicato? 210 00:12:59,840 --> 00:13:02,820 Ora sai cosa succede nella libreria CS50. 211 00:13:02,820 --> 00:13:06,470 >> Il mio nome è Nate Hardison, e questo è CS50. 212 00:13:06,470 --> 00:13:08,350 [CS50.TV]