[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Questo è CS50. CS50.TV]
La biblioteca CS50 è un utile strumento che abbiamo installato l'apparecchio
per rendere più facile per voi a scrivere programmi che richiede agli utenti per l'ingresso.
In questo video, ci tira la tenda e guardare a ciò che esattamente si trova nella libreria CS50.
>> Nel video di librerie C, si parla di come si # include file di header
della biblioteca nel codice sorgente,
e poi si collega con un file di libreria binario durante la fase di collegamento
del processo di compilazione.
I file di intestazione specificare l'interfaccia della libreria.
Cioè, dettaglio tutte le risorse che la biblioteca ha a disposizione per l'uso,
come dichiarazioni di funzioni, costanti e tipi di dati.
Il file di libreria binario contiene l'implementazione della biblioteca,
che viene compilato dal file di intestazione della biblioteca e della biblioteca. c file di codice sorgente.
>> Il file di libreria binario non è molto interessante da guardare dal momento che è, beh, in binario.
Quindi, diamo uno sguardo ai file header per la libreria, invece.
In questo caso, c'è solo un file header chiamato cs50.h.
Lo abbiamo installato nella directory utente includono
insieme ai file di intestazione di librerie di sistema degli altri.
>> Una delle prime cose che noterete è che cs50.h # include file di intestazione di altre biblioteche -
galleggiante, limiti, serie bool, e lib standard.
Anche in questo caso, secondo il principio di non reinventare la ruota,
abbiamo costruito la libreria CS0 utilizzando strumenti che hanno fornito altri per noi.
>> La prossima cosa che vedrete in biblioteca è che si definisce un nuovo tipo di "stringa".
Questa linea in realtà solo crea un alias per il tipo char *,
in modo che non magicamente infondere il nuovo tipo di stringa con gli attributi
comunemente associati con gli oggetti stringa in altre lingue,
come lunghezza.
Il motivo per cui abbiamo fatto è quello di proteggere i nuovi programmatori dai dettagli scabrosi
di puntatori fino a quando sono pronti.
>> La parte successiva del file di intestazione è la dichiarazione delle funzioni
che il CS50 libreria fornisce insieme alla documentazione.
Si noti il ​​livello di dettaglio nei commenti qui.
Questo è super importante in modo che la gente sappia come usare queste funzioni.
Si dichiara, a sua volta, funziona per richiedere all'utente e caratteri di ritorno, doppie, galleggianti, interi,
lungo anela, e archi, usando il nostro proprio tipo di stringa.
Secondo il principio di information hiding,
abbiamo messo la nostra definizione in un apposito file di implementazione c -. cs50.c--
si trova nella directory di origine dell'utente.
Abbiamo messo a disposizione il file in modo da poter dare un'occhiata a esso,
imparare da esso, e ricompilarlo su macchine diverse se lo si desidera,
anche se pensiamo che sia meglio lavorare sulla macchina per questa classe.
In ogni caso, diamo un'occhiata ora.
>> Le funzioni getchar, GetDouble, getFloat, GetInt, e GetLongLong
sono tutti costruiti sulla sommità della funzione GetString.
Si scopre che tutti seguono sostanzialmente lo stesso schema.
Usano un ciclo while per richiedere all'utente per una riga di input.
Essi restituiscono un valore speciale se l'utente immette una riga vuota.
Essi cercano di analizzare l'input dell'utente come il tipo appropriato,
che si tratti di un char, un doppio, un float, ecc
E poi restituirà il risultato se l'ingresso è stato analizzato correttamente
oppure reprompt dell'utente.
>> Ad alto livello, non c'è nulla di davvero difficile qui.
Si potrebbe avere scritto il codice allo stesso modo strutturato te in passato.
Forse la parte più criptico di aspetto è la chiamata sscanf che analizza l'input dell'utente.
Sscanf fa parte della famiglia di conversione di formato di input.
Vive in io.h standard, e il suo compito è quello di analizzare una stringa C,
secondo un particolare formato, la memorizzazione dei risultati di analisi a variabili
fornito dal chiamante.
Poiché le funzioni di conversione del formato di input sono molto utili, funzioni ampiamente utilizzati
che non sono super intuitiva in un primo momento,
andremo su come sscanf funziona.
>> Il primo argomento a sscanf è un char * - un puntatore a un carattere.
Per la funzione per funzionare correttamente,
quel personaggio dovrebbe essere il primo carattere di una stringa C,
terminato con il nulla carattere \ 0.
Questa è la stringa da analizzare
Il secondo argomento a sscanf è una stringa di formato,
tipicamente passato come una costante stringa,
e si potrebbe aver visto una stringa come questa prima di quando si utilizza printf.
Un segno di percentuale nella stringa di formato indica un indicatore di conversione.
Il carattere immediatamente dopo un segno di percentuale,
indica il tipo C che vogliamo convertire in sscanf.
In GetInt, si vede che c'è un d% e% c.
Ciò significa che sscanf cercherà di un decimale int - la% d - e un char - il c%.
Per ogni indicatore di conversione nella stringa di formato,
sscanf si aspetta un argomento corrispondente successiva nella lista degli argomenti.
Questo argomento deve puntare a una posizione adeguatamente tipizzato
in cui memorizzare il risultato della conversione.
>> Il modo tipico di fare questo è quello di creare una variabile sullo stack prima della chiamata sscanf
per ogni elemento che si desidera analizzare dalla stringa
e quindi utilizzare l'operatore di indirizzo - la e commerciale - di passare puntatori
a quelle variabili alla chiamata sscanf.
Si può vedere che in GetInt facciamo esattamente questo.
Poco prima della chiamata sscanf, dichiariamo un int chiamato n e c char chiamata sullo stack,
e passiamo puntatori a loro nella chiamata sscanf.
Mettendo queste variabili nello stack è preferito rispetto all'uso di spazio allocato
sul mucchio con malloc, dal momento che si evita il sovraccarico della chiamata malloc,
e non dovete preoccuparvi di perdite di memoria.
I caratteri non preceduti da un segno di percentuale non richiede la conversione.
Piuttosto è sufficiente aggiungere alla specifica di formato.
>> Ad esempio, se la stringa di formato in GetInt erano un d% invece,
sscanf sarebbe per la lettera a seguita da un int,
e mentre si cerca di convertire il int, non avrebbe fatto qualsiasi altra cosa con la a.
L'unica eccezione a questo è spazio vuoto.
Spazi vuoti nella stringa di formato corrisponde a nessuna quantità di spazi -
anche del tutto assenti.
Quindi, è per questo che il commento menziona eventualmente con i più importanti e / o di spazio bianco in coda.
Quindi, a questo punto sembra che il nostro appello sscanf cercherà di analizzare la stringa di input dell'utente
controllando eventualmente preceduto da spazio bianco,
seguito da un int che verrà convertito e memorizzato nella variabile n int
seguita da una certa quantità di spazio bianco, e seguito da un carattere
memorizzato nella variabile c char.
>> E il valore di ritorno?
Sscanf analizzerà la linea di ingresso dall'inizio alla fine,
fermandosi quando raggiunge la fine o quando un personaggio in ingresso
non corrisponde un carattere di formato o quando non è possibile effettuare una conversione.
E 'il valore restituito viene utilizzato per individuare quando si è fermato.
Se si è fermato, perché ha raggiunto la fine della stringa di input
prima di effettuare qualsiasi conversione e prima di fallire per abbinare parte della stringa di formato,
allora la costante EOF speciale viene restituito.
In caso contrario, restituisce il numero di conversioni di successo,
che potrebbe essere 0, 1, o 2, dal momento che abbiamo chiesto due conversioni.
Nel nostro caso, si vuole fare in modo che l'utente ha digitato in un int e solo un int.
>> Quindi, vogliamo tornare sscanf 1. Scopri perché?
Se sscanf restituito 0, allora nessun conversioni sono state fatte,
così l'utente ha digitato qualcosa di diverso da un int all'inizio dell'ingresso.
Se sscanf restituisce 2, allora l'utente ha correttamente lo digitare all'inizio dell'ingresso,
ma poi trascritte in qualche non-spazio bianco carattere dopo
poiché la conversione% c riuscito.
Wow, che è piuttosto una lunga spiegazione per una chiamata di funzione.
In ogni caso, se volete maggiori informazioni su sscanf e dei suoi fratelli,
controllare le pagine man, di Google, o entrambi.
Ci sono un sacco di opzioni di stringa di formato,
e questi possono risparmiare un sacco di lavoro manuale quando si cerca di analizzare le stringhe in C.
>> L'ultima funzione nella libreria da guardare è GetString.
Si scopre che GetString è una funzione difficile da scrivere correttamente,
anche se sembra così semplice, compito comune.
Perché è questo il caso?
Bene, cerchiamo di pensare a come stiamo andando a memorizzare la linea che l'utente digita poll
Poiché una stringa è una sequenza di caratteri,
si potrebbe desiderare di conservarlo in un array sullo stack,
ma avremmo bisogno di sapere quanto tempo l'array sarà quando ci si dichiara.
Allo stesso modo, se vogliamo metterla sul mucchio,
abbiamo bisogno di passare a malloc il numero di byte che vogliamo riserva,
ma questo è impossibile.
Non abbiamo idea di quanti caratteri l'utente dovrà digitare
prima che l'utente in realtà non li digita.
>> Una soluzione banale a questo problema è quello di prenotare solo una grossa fetta di spazio, ad esempio,
un blocco di 1000 caratteri per l'input dell'utente,
supponendo che l'utente non avrebbe mai digitare una stringa che a lungo.
Questa è una cattiva idea per due motivi.
In primo luogo, partendo dal presupposto che gli utenti in genere non digitare stringhe così a lungo,
si rischia di sprecare un sacco di memoria.
Sulle macchine moderne, questo potrebbe non essere un problema se si esegue questa operazione
in uno o due casi isolati,
ma se si sta prendendo l'input dell'utente in un ciclo e la conservazione per uso futuro,
si può rapidamente aspirare una tonnellata di memoria.
Inoltre, se il programma che si sta scrivendo è per un computer più piccolo -
un dispositivo come uno smartphone o altro con memoria limitata -
questa soluzione può causare problemi molto più veloce.
La seconda, la ragione più grave di non farlo è che lascia il programma vulnerabile
a quello che si chiama un attacco di tipo buffer overflow.
In programmazione, un buffer di memoria è utilizzata per memorizzare temporaneamente i dati di ingresso o di uscita,
che in questo caso è la nostra 1000-char blocco.
Un buffer overflow si verifica quando i dati vengono scritti oltre la fine del blocco.
>> Ad esempio, se un utente fa effettivamente tipo in più di 1000 caratteri.
Si potrebbe avere sperimentato questo errore durante la programmazione con gli array.
Se si dispone di un array di 10 interi, nulla vi impedisce di cercare di leggere o scrivere
l'int 15.
Non ci sono avvisi del compilatore o errori.
Il programma solo errori dritto e accede alla memoria
dove si pensa che la int 15 sarà, e questo può sovrascrivere le altre variabili.
Nel peggiore dei casi, è possibile sovrascrivere alcuni interna del programma
meccanismi di controllo, causando il programma da eseguire in realtà diverse istruzioni
di quanto previsto.
>> Ora, non è comune a fare questo errore,
ma questa è una tecnica abbastanza comune che i cattivi usano per rompere i programmi
e inserire il codice dannoso sul computer di altre persone.
Pertanto, non possiamo utilizzare la nostra soluzione ingenua.
Abbiamo bisogno di un modo per evitare che i nostri programmi di essere vulnerabile
ad un attacco di tipo buffer overflow.
Per fare questo, abbiamo bisogno di fare in modo che il buffer può crescere come si legge
più input dall'utente.
La soluzione? Usiamo un heap buffer allocato.
Dal momento che siamo in grado di modificarne le dimensioni utilizzando il ridimensionamento della funzione realloc,
e tenere traccia dei due numeri - l'indice dello slot vuota successiva nel buffer
e la lunghezza o la capacità del buffer.
Leggiamo in caratteri da parte dell'utente una alla volta utilizzando la funzione fgetc.
L'argomento della funzione fgetc prende - stdin - è un riferimento alla stringa di input standard,
che è un canale di ingresso preconnesso che viene utilizzato per trasferire input dell'utente
dal terminale al programma.
>> Ogni volta che l'utente digita in un nuovo personaggio, controlliamo per vedere se l'indice
del prossimo slot libero più 1 è maggiore della capacità del buffer.
Il 1 è disponibile in, perché se il primo indice libero è 5,
poi la lunghezza il buffer deve essere di 6 a 0 grazie indicizzazione.
Se abbiamo esaurito lo spazio nel buffer, poi si cerca di ridimensionare,
raddoppiando così che ridurre il numero di volte che ridimensionare
se l'utente sta scrivendo in una stringa molto lunga.
Se la stringa è diventato troppo lungo o se siamo a corto di memoria heap,
liberiamo il nostro buffer e nullo ritorno.
>> Infine, aggiungere il carattere al buffer.
Una volta che l'utente preme Invio, segnalando una nuova linea,
o il carattere speciale - controllo d - che segnala la fine di input,
facciamo un controllo per verificare se l'utente ha effettivamente digitato nulla.
In caso contrario, restituire null.
In caso contrario, perché il nostro buffer è probabilmente più grande di cui abbiamo bisogno,
nel caso peggiore è quasi due volte più grande che dobbiamo
dal momento che il doppio ogni volta che ridimensionare,
facciamo una nuova copia della stringa utilizzando solo la quantità di spazio di cui abbiamo bisogno.
Aggiungiamo un ulteriore 1 per la chiamata malloc,
in modo che ci sia spazio per lo speciale carattere null di terminazione - il \ 0,
che aggiungere alla stringa una volta copiare il resto dei personaggi,
utilizzando strncpy invece di strcpy
in modo da poter specificare esattamente il numero di caratteri che vogliamo copiare.
Strcpy copia fino a quando non colpisce un \ 0.
Poi liberare il nostro buffer e restituire la copia al chiamante.
>> Chi sapeva così semplice apparentemente funzione potrebbe essere così complicato?
Ora sai cosa succede nella libreria CS50.
>> Il mio nome è Nate Hardison, e questo è CS50.
[CS50.TV]