[Powered by Google Translate] [CS50 Library] [Nate Hardison] [Universitatea Harvard] [Acest lucru este CS50. CS50.TV] Biblioteca CS50 este un instrument util, care ne-am instalat pe aparat pentru a face mai ușor pentru tine de a scrie programe pe care utilizatorii prompt pentru intrare. În acest videoclip, vom trage înapoi cortina si uita-te la ceea ce este exact în bibliotecă CS50. În video de pe bibliotecile C, vorbim despre modul în care fișierele header # include de bibliotecă în codul sursă, și apoi vă legați cu un fișier binar bibliotecă în timpul fazei de legătură din procesul de compilare. Fișierele antet specifica interfata a bibliotecii. Asta este, ei detaliu toate resursele pe care biblioteca are disponibile pentru tine de a utiliza, cum ar fi declarațiile funcțiilor, constante, tipuri de date și. Fișierul binar Biblioteca conține punerea în aplicare a bibliotecii, care este compilat din fișierele header bibliotecii și despre biblioteca C fișiere. codul sursă. Fișier binar Biblioteca nu este foarte interesant să se uite la, deoarece e, bine, în binar. Deci, haideți să aruncăm o privire la fișierele header pentru bibliotecă în loc. În acest caz, nu există decât un singur fișier header numit cs50.h. L-am instalat în directorul de utilizare includ împreună cu fișierele de alte biblioteci de sisteme de antet. Unul dintre primele lucruri pe care le veți observa este faptul că cs50.h include # fișiere antet de la alte biblioteci - float, bool limite, de iarnă, și lib standard. Din nou, ca urmare a principiului de a nu reinventa roata, am construit biblioteca CS0 folosind unelte pe care alte prevăzute pentru noi. Următorul lucru pe care îl veți vedea în bibliotecă este ca putem defini un nou tip numit "string". Această linie de fapt doar creează un alias pentru tipul char *, astfel încât să nu satura magic de tip sir nou cu atribute frecvent asociate cu obiecte de coarde în alte limbi, cum ar fi lungimea. Motivul pentru care am făcut acest lucru este de a proteja programatori de noi de la detaliile gory de pointeri până când sunt gata. Următoarea parte a fișierului header este declarația de funcții că biblioteca CS50 ofera împreună cu documentația. Observați nivelul de detaliere în comentariile de aici. Acest lucru este foarte important pentru ca oamenii să știe cum să folosească aceste funcții. Ne pronunțăm, la rândul lor, funcționează pentru a solicita utilizatorului și caractere de returnare, duble, flotoare, Ints, lungă doreste, si siruri de caractere, folosind tipul nostru șir proprie. În urma principiul de ascundere de informații, ne-am pus definiția noastră într-un fișier separat, punerea în aplicare c -. cs50.c-- situat în directorul sursă de utilizator. Am condiția ca fișier, astfel încât să puteți lua o privire la ea, învăța de la el, și recompilați-l pe mașini diferite, dacă doriți, chiar dacă credem că e mai bine pentru a lucra la aparat pentru această clasă. Oricum, haideți să aruncăm o privire la ea acum. Funcțiile getchar, GetDouble, GetFloat, GetInt, și GetLongLong sunt toate construite pe partea de sus a funcției getString. Se pare că ei vor urma în esență, același model. Ei folosesc o buclă în timp ce pentru a cere utilizatorului pentru o linie de intrare. Ei se întorc o valoare deosebită în cazul în care utilizatorul introduce o linie goală. Ei încearcă pentru a analiza intrarea utilizatorului ca tipul de caz, fie el un char, un dublu, un float, etc Și apoi se vor întoarce fie rezultatul dacă intrarea a fost cu succes analizat sau ei reprompt utilizator. La un nivel ridicat, nu este nimic adevarat complicat aici. S-ar putea fi scris codul structurate în mod similar vă în trecut. Poate că partea cea mai criptic-aspect este apelul sscanf care analizează intrarea utilizatorului. Sscanf face parte din familia de conversie de intrare format. Ea locuiește în io.h de iarnă, iar treaba lui este de a interpreta un șir C, în conformitate cu un anumit format, stocarea rezultatelor în analiza variabilei furnizate de către apelant. Deoarece formatul funcții de conversie de intrare sunt foarte utile, funcțiile utilizate pe scară largă care nu sunt super intuitiv la început, vom trece peste modul în care funcționează sscanf. Primul argument a sscanf este un char * - un pointer la un caracter. Pentru funcția să funcționeze corect, caracterul ar trebui să fie primul caracter al unui șir C, încheiată cu nul \ 0 caractere. Aceasta este șir pentru a analiza Al doilea argument pentru a sscanf este un șir format, de obicei, a trecut într-o constantă șir ca, și este posibil să fi văzut un șir ca acest lucru înainte atunci când se utilizează printf. Un semn la sută în șir format indică un specificator de conversie. Caracterul imediat după un semn la sută, indică tipul C pe care ne-o dorim pentru a converti la sscanf. În GetInt, veți vedea că există o d% și o c.%. Acest lucru înseamnă că sscanf va încerca să o int zecimal -% d - și un char - C%. Pentru fiecare specificatorul de conversie în șir format, sscanf așteaptă un argument corespunzătoare mai târziu în lista de argumentul său. Acest argument trebuie să indice într-o locație adecvată tastat în care se păstrează rezultatul conversiei. Modul tipic de a face acest lucru este de a crea o variabilă pe stivă înainte de apelul sscanf pentru fiecare element pe care doriți să-parsa din șirul și de a folosi apoi operatorul adresa - ampersand - pentru a trece pointeri pentru aceste variabile la apelul sscanf. Puteți vedea că, în GetInt facem exact acest lucru. Chiar înainte de apelul sscanf, ne declara un int n numit și un apel c char pe stivă, și trecem pointeri la ele în apel sscanf. Punerea acestor variabile pe stiva este preferat de peste folosind spațiul alocat pe morman cu malloc, din moment ce evita aeriene de apel malloc, și nu trebuie să vă faceți griji cu privire la scurgeri de memorie. Caractere care nu sunt prefixate de un semn de procent, nu cere de conversie. Mai degrabă ei trebuie doar să adăugați la caietul de sarcini formatul. De exemplu, dacă șirul formatul în GetInt au fost o d%, în loc, sscanf s-ar uita pentru o scrisoare urmat de un int, și în timp ce-l va încerca să convertească int, aceasta nu ar face nimic altceva cu o. Singura excepție de la acest lucru este un spațiu. Caractere spațiu alb în șir format meciul orice cantitate de spații albe - chiar deloc. Deci, de aceea comentariul menționează, eventual, cu conducerea și / sau la sfârșitul numelui spațiu. Deci, în acest moment se pare că apelul nostru va încerca sscanf pentru a analiza șir de utilizator de intrare prin verificarea pentru posibile de conducere spații albe, urmat de un int care vor fi transformate și stocate în variabila int n urmată de o anumită cantitate de spații albe, și urmată de un caracter stocate în variabila char c. Ce zici de valoarea de returnare? Sscanf va analiza linia de intrare de la început până la sfârșit, oprire atunci când ajunge la capăt sau atunci când un personaj din intrare nu se potrivește cu un caracter format sau atunci când nu se poate face o conversie. Valoarea ei de retur este utilizat pentru a unic atunci când sa oprit. În cazul în care sa oprit, pentru că a ajuns la sfârșitul șirului de intrare înainte de a face orice conversie și înainte de faptul că nu pentru a se potrivi o parte din stringul de format, apoi EOF specială constanta este returnat. În caz contrar, returnează numărul de conversii de succes, care ar putea fi 0, 1, sau 2, când ne-am rugat pentru două conversii. În cazul nostru, vrem să ne asigurăm că utilizatorul tastat într-un int si numai un int. Deci, vrem să se întoarcă sscanf 1. Vezi de ce? Dacă sscanf returnat 0, atunci nu s-au făcut conversii, astfel încât utilizatorul tastat altceva decât o int de la începutul intrare. Dacă sscanf returnează 2, apoi utilizatorul sa scrie corect în la începutul anului de intrare, dar apoi introdus într-un caracter non-spațiu după aceea întrucât% c conversie a reușit. Wow, asta e destul de o explicație lungă pentru apelul funcției unul. Oricum, dacă doriți mai multe informații despre sscanf și frații săi, a verifica afară de paginile man, Google, sau ambele. Există o mulțime de opțiuni șir format, și acestea pot economisi o mulțime de muncă manuală atunci când încearcă să elimine siruri de caractere în C Funcția finală în bibliotecă să se uite la este getString. Se pare că este o funcție getString dificil de a scrie corect, chiar daca se pare ca o astfel de sarcină simplă, comună. De ce este acest caz? Ei bine, hai să ne gândim cum vom stoca linia pe care utilizatorul tipuri de inch Din moment ce un șir este o secvență de caractere, am putea dori să-l stocați într-o matrice pe stivă, dar ne-ar trebui să știe cât timp matrice va fi atunci când l-am declara. De asemenea, dacă vrem să-l pună pe heap, avem nevoie pentru a trece la malloc numărul de octeți vrem să rezerve, dar acest lucru este imposibil. Nu avem nici o idee cât de multe caractere utilizatorul va introduce în înainte de utilizare, de fapt nu le tastați. O soluție naiv la această problemă este de a rezerva doar o bucată mare de spațiu, să zicem, un bloc de 1000 de caractere de intrare pentru utilizator, presupunând că utilizatorul nu ar tastați într-un șir atât de mult. Aceasta este o idee rea pentru două motive. În primul rând, presupunând că utilizatorii de obicei nu tastați în siruri de caractere atât de mult, ai putea deșeuri o mulțime de memorie. Pe mașinile moderne, acest lucru nu ar putea fi o problemă dacă faci acest lucru în una sau două cazuri izolate, dar dacă luați datele introduse de utilizator într-o buclă și depozitarea pentru utilizare ulterioară, vă poate suge rapid o tona de memorie. În plus, în cazul în care programul scrii este pentru un computer mai mic - un dispozitiv cum ar fi un smartphone sau altceva cu memorie limitată - această soluție va provoca probleme mult mai repede. Al doilea motiv, mult mai grav să nu facă acest lucru este că lasă programul dumneavoastră vulnerabil la ceea ce se numește un overflow atac buffer. În programare, un buffer de memorie este utilizată pentru stocarea temporară a datelor de intrare sau de ieșire, care în acest caz este nostru 1000-char bloc. Un buffer overflow apare atunci când datele sunt scrise trecut sfârșitul blocului. De exemplu, dacă un utilizator face de fapt de tip în mai mult de 1000 de caractere. Este posibil să fi experimentat acest lucru accidental la programare cu matrice. Dacă aveți o serie de 10 Ints, nimic nu te opreste de la încercarea de a citi sau a scrie 15 int. Nu există avertizări sau erori de compilare. Programului doar gafe drept înainte și accesează memoria în cazul în care se crede că va fi int cincisprezecelea, iar acest lucru poate suprascrie variabilele alte. În cel mai rău caz, puteți suprascrie o parte din program intern dvs. mecanisme de control, provocând programul dvs. pentru a executa efectiv instrucțiuni diferite decât ați intenționat. Acum, nu e obișnuit să facă acest lucru accidental, dar aceasta este o tehnica destul de comună că băieții răi utilizați pentru a sparge programe și a pus cod malitios pe computerele altor persoane. De aceea, nu putem folosi doar soluția noastră naiv. Avem nevoie de o modalitate de a preveni programele noastre de a fi vulnerabil la un buffer overflow atac. Pentru a face acest lucru, avem nevoie să ne asigurăm că buffer-ul nostru poate creste asa cum am citit intrare mai mult de la utilizator. Soluția? Noi folosim un tampon alocat heap. Din moment ce se poate redimensiona folosind funcția de redimensionare realloc, și ne ține evidența două numere - indicele de slotul gol următor în tampon și lungimea sau capacitatea de a tampon. Citim în caractere de la utilizator la un moment dat folosind funcția fgetc. Argumentul funcția fgetc ia - stdin - este o trimitere la șirul de intrare standard, care este un canal de intrare preconnected, care este utilizat pentru a transfera de intrare utilizatorului de la terminal la program. Ori de câte ori utilizatorul tastează într-un nou personaj, vom verifica pentru a vedea dacă indexul din slot liber următoarea plus 1 este mai mare decât capacitatea de tampon. The 1, deoarece vine în cazul în care indicele următor liber este de 5, atunci lungimea buffer nostru trebuie să fie mulțumită 6 la 0 indexare. Dacă ne-am alerga afară de spațiu în tampon, apoi am încerca să-l redimensiona, dublarea în așa fel încât am tăiat în jos, pe numărul de ori pe care le redimensiona dacă utilizatorul este tastarea într-un șir foarte lung. Dacă șirul a devenit prea mult timp sau dacă ne alerga afară de memorie heap, ne eliberăm tampon noastră și nul retur. În cele din urmă, am adăuga la char buffer. Odată ce hit-uri de utilizator intra sau reveni, de semnalizare o linie nouă, sau speciale char - de control d - care semnalează un sfârșit de intrare, facem o verificare pentru a vedea dacă utilizatorul de fapt tastat nimic, la toate. Dacă nu, ne vom întoarce null. În caz contrar, pentru ca tampon nostru este, probabil, mai mare decât avem nevoie, în cel mai rău caz e aproape de două ori mai mare ca avem nevoie de când ne-am dubla fiecare dată când ne redimensiona, vom face o nouă copie a șirului folosind doar cantitatea de spațiu de care avem nevoie. Am adăuga un extra de la 1 la apelul malloc, astfel că există spațiu pentru construcții nul caracterul terminator - pentru \ 0, care să anexeze la șirul odată ce vom copia in restul personajelor, folosind strncpy loc de strcpy astfel încât să putem specifica exact cât de multe caractere dorim să copiați. Strcpy copiază până când lovește un \ 0. Apoi ne-am elibera tampon noastră și a reveni copie apelantului. Cine a știut astfel o funcție de simplu, aparent ar putea fi atat de complicat? Acum știi ce se întâmplă în bibliotecă CS50. Numele meu este Nate Hardison, iar acest lucru este CS50. [CS50.TV]