[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Dette er CS50. CS50.TV]
Den CS50 biblioteket er et nyttig verktøy som vi har installert på maskinen
å gjøre det enklere for deg å skrive programmer som ber brukerne for innspill.
I denne videoen, vil vi trekke tilbake gardin og se på hva er i CS50 biblioteket.
>> I videoen på C-biblioteker, snakker vi om hvordan du # include overskrifter filer
av biblioteket i kildekoden,
og deretter koble med en binær bibliotekfil under knytte fase
av innsamlingsprosessen.
Header-filene spesifisere grensesnittet av biblioteket.
Det vil si, de detalj alle de ressursene som biblioteket har tilgjengelig for deg å bruke,
som funksjon erklæringer, konstanter og datatyper.
Den binære bibliotekfil inneholder gjennomføringen av biblioteket,
som er satt sammen av bibliotekets header-filer og bibliotekets. c kildekodefiler.
>> Den binære bibliotekfilen er ikke veldig interessant å se på siden det er, vel, i binær.
Så, la oss ta en titt på header filer for biblioteket i stedet.
I dette tilfellet er det bare én header fil som heter cs50.h.
Vi har installert den i brukerens inkluderer katalogen
sammen med de andre systemene bibliotekenes header filer.
>> En av de første tingene du vil legge merke til er at cs50.h # inkluderer header-filer fra andre bibliotek -
float, grenser, standard bool og standard lib.
Igjen, etter prinsippet om ikke gjenoppfinne hjulet,
Vi har bygget CS0 bibliotek ved hjelp av verktøy som andre gitt til oss.
>> Den neste tingen du vil se i biblioteket er at vi definerer en ny type som heter "string".
Denne linjen egentlig bare skaper et alias for char * type,
slik at den ikke tilegner magisk den nye strengen type med attributter
vanligvis forbindes med streng objekter i andre språk,
for eksempel lengden.
Grunnen til at vi har gjort dette på er å skjerme nye programmerere fra blodig detaljer
av pekere til de er klare.
>> Den neste delen av topptekstfilen er erklæringen av funksjonene
at CS50 biblioteket gir sammen med dokumentasjon.
Legg merke til detaljnivået i kommentarfeltet her.
Dette er super viktig, slik at folk vet hvordan de skal bruke disse funksjonene.
Vi erklærer i sin tur fungerer å spørre brukeren og retur tegn, dobbeltrom, flyter, ints,
lang lengter og strenger, med vår egen streng type.
Etter prinsippet om informasjon skjule,
Vi har satt vår definisjon i en egen c implementering fil -. cs50.c--
ligger i brukerens kilde katalogen.
Vi har forutsatt at filen slik at du kan ta en titt på det,
lære av det, og rekompilere det på forskjellige maskiner hvis du ønsker,
selv om vi tror det er bedre å jobbe på apparatet for denne klassen.
Uansett, la oss ta en titt på det nå.
>> Funksjonene getchar, GetDouble, GetFloat, GetInt, og GetLongLong
er alle bygget på toppen av GetString funksjon.
Det viser seg at de alle følger i hovedsak samme mønster.
De bruker en stund loop å be brukeren om en linje av input.
De kommer tilbake en spesiell verdi hvis brukeren skriver inn en tom linje.
De forsøker å analysere brukerens input som den aktuelle typen,
det være seg en røye, en dobbel, en flåte, etc.
Og så de enten returnere resultatet hvis input var vellykket analysert
eller de reprompt brukeren.
>> På et høyt nivå, det er ingenting virkelig vanskelig her.
Du har kanskje skrevet tilsvarende strukturert koden selv i det siste.
Kanskje den mest kryptiske utseende delen er sscanf samtalen som analyserer brukerens input.
Sscanf tilhører inndataformatet konvertering familie.
Den lever i standard io.h, og dens oppgave er å analysere en C streng,
ifølge et bestemt format, lagring parse fører variabel
gitt av den som ringer.
Siden inndataformat konvertering funksjoner er svært nyttige, mye brukte funksjoner
som ikke er super ved første øyekast,
vi vil gå over hvordan sscanf fungerer.
>> Det første argumentet til sscanf er en char * - en peker til et tegn.
For funksjonen skal fungere skikkelig,
at karakter skal være det første tegnet i en C streng,
avsluttet med null \ 0 karakter.
Dette er strengen til å analysere
Det andre argumentet til sscanf er et format streng,
typisk gått inn som en streng konstant,
og du har kanskje sett en streng som dette før når du bruker printf.
En prosent skilt i formatstrengen viser en konvertering Specifier.
Tegnet umiddelbart etter et prosenttegn,
indikerer C-typen som vi ønsker sscanf å konvertere til.
I GetInt, ser du at det er en% d og% c.
Dette betyr at sscanf vil prøve å en desimal int - den% d - og røye - den% c.
For hver konvertering Specifier i formatstrengen,
sscanf forventer en tilsvarende argument senere i sin argumentasjon listen.
Som argument må peke til et hensiktsmessig skrevet beliggenhet
der du vil lagre resultatet av konverteringen.
>> Den typiske måten å gjøre dette på er å opprette en variabel på stakken før sscanf samtalen
for hvert element du ønsker å analysere fra strengen
og deretter bruke adressen operatør - tegnet - å passere pekere
til disse variablene til sscanf samtalen.
Du kan se at i GetInt gjør vi akkurat dette.
Rett før sscanf samtalen, erklærer vi en int kalt n og en røye samtale c på stakken,
og vi passerer pekere til dem i sscanf samtalen.
Sette disse variablene på stakken er å foretrekke fremfor å bruke plass tildelt
på haugen med malloc, siden du unngå overhead av malloc samtalen,
og du trenger ikke å bekymre deg for lekker minne.
Tegn som ikke innledes med et prosenttegn ikke be konvertering.
Snarere er de bare legge til filformatet.
>> For eksempel, hvis formatstreng GetInt var% d i stedet,
sscanf ville se etter bokstaven a etterfulgt av en int,
og mens det ville forsøke å konvertere int, ville det ikke gjøre noe annet med en.
Det eneste unntaket til dette er mellomrom.
Hvite mellomrom i formatstrengen matche ethvert mengde blanktegn -
selv ingen.
Så, det er grunnen til at kommentaren nevner muligens med ledende og / eller etterfølgende mellomrom.
Så vil på dette punktet ser det ut som vår sscanf samtale forsøker å analysere brukerens input streng
ved å se etter mulige ledende mellomrom,
etterfulgt av en int som vil bli konvertert og lagret i int variabel n
etterfulgt av en viss grad av blanke, og etterfulgt av et tegn
lagret i char variabelen c..
>> Hva om returverdien?
Sscanf vil analysere inndataene linje fra start til slutt,
stopper når den når slutten eller når et tegn i input
samsvarer ikke et format tegn eller når det ikke kan gjøre en konvertering.
Det er returverdi brukes til å skille når det stoppet.
Hvis det stoppet, fordi det nådd slutten av inndatastrengen
før du gjør noen konverteringer og før unnlate å søke på en del av formatstrengen,
deretter spesielt konstant EOF er returnert.
Ellers returnerer den antall vellykkede konverteringer,
som kan være 0, 1 eller 2, siden vi har bedt om to konverteringer.
I vårt tilfelle ønsker vi å sørge for at brukeren har skrevet i en int og bare en int.
>> Så ønsker vi sscanf å returnere en. Se hvorfor?
Hvis sscanf tilbake 0, så vil ingen konverteringer ble gjort,
så brukeren har skrevet noe annet enn en int på begynnelsen av input.
Hvis sscanf returnerer 2, så brukeren gjorde riktig skriv det i ved begynnelsen av tilførselen
men de da skrevet i noen ikke-mellomrom karakter etterpå
siden% c konvertering lyktes.
Wow, det er ganske lang forklaring for én funksjon samtale.
Uansett, hvis du ønsker mer informasjon om sscanf og sine søsken,
sjekk ut mannen sidene, Google, eller begge deler.
Det er mange strengformateringsangrep alternativer,
og disse kan spare deg for mye manuelt arbeid når du prøver å analysere strenger i C.
>> Den siste funksjon i biblioteket for å se på er GetString.
Det viser seg at GetString er en vanskelig funksjon å skrive riktig,
selv om det virker som en enkel, felles oppgave.
Hvorfor er det slik?
Vel, la oss tenke på hvordan vi skal lagre linjen som brukeren skriver i.
Siden en streng er en sekvens av tegn,
vi kanskje ønsker å lagre den i en matrise på stakken,
men vi trenger å vite hvor lenge rekken kommer til å være når vi erklærer den.
Likeledes, hvis vi ønsker å sette det på haugen,
Vi må passere for å malloc antall byte vi ønsker å reservere,
men dette er umulig.
Vi har ingen anelse om hvor mange tegn brukeren vil skrive
før brukeren faktisk ikke skriver dem.
>> En naiv løsning på dette problemet er å bare bestille en stor del av plassen, sier
en blokk på 1000 tegn for brukerens inngang,
forutsatt at brukeren aldri ville skrive i en streng så lenge.
Dette er en dårlig idé for to grunner.
Først, forutsatt at brukerne vanligvis ikke skriver i strenger så lenge,
du kan kaste bort en masse minne.
På moderne maskiner, kan dette ikke være et problem hvis du gjør dette
i en eller to isolerte tilfeller,
men hvis du tar brukerens input i en sløyfe og lagring for senere bruk,
du raskt kan suge opp massevis av minne.
I tillegg, hvis programmet du skriver er for en mindre maskin -
en enhet som en smarttelefon eller noe annet med begrenset minne -
denne løsningen vil skape problemer mye raskere.
Den andre, mer alvorlig grunn til å ikke gjøre dette er at det etterlater programmet sårbar
til det som kalles en buffer overflow angrep.
I programmering, er et bufferminne som brukes til å midlertidig lagre inn-eller utgang data,
som i dette tilfellet er vår 1000-char blokk.
En buffer overflow oppstår når data skrives forbi slutten av blokken.
>> For eksempel, hvis en bruker faktisk gjør type i mer enn 1000 tegn.
Du har kanskje opplevd dette ved et uhell når du programmerer med arrays.
Hvis du har en rekke 10 ints, stopper ingenting deg fra å prøve å lese eller skrive
15. int.
Det er ingen kompilatoren advarsler eller feil.
Programmet bare tabber rett fram og åpner minne
hvor den tror den 15. int vil bli, og dette kan overskrive de andre variablene.
I verste fall kan du overskrive noen av programmets interne
kontrollmekanismer, forårsaker programmet faktisk utføre forskjellige instruksjoner
enn du hadde tenkt.
>> Nå er det ikke vanlig å gjøre dette ved et uhell,
men dette er en ganske vanlig teknikk som skurkene bruker til å bryte programmer
og sette ondsinnet kode på andres datamaskiner.
Derfor kan vi ikke bare bruke vår naiv løsning.
Vi trenger en måte å hindre at våre programmer fra å være sårbar
til en buffer overflow angrep.
For å gjøre dette, må vi sørge for at våre buffer kan vokse som vi leser
mer input fra brukeren.
Løsningen? Vi bruker en haug tilordnede bufferen.
Siden vi kan endre størrelsen med resize den RealLOC funksjon,
og vi holde styr av to tall - indeksen for neste tomme sporet i bufferen
og lengden eller kapasiteten på bufferen.
Vi leser i chars fra brukeren en om gangen ved hjelp av fgetc funksjonen.
Argumentet den fgetc funksjonen tar - stdin - er en referanse til standard input strengen,
som er en forhåndskobles inngangskanal som brukes til å overføre brukerens input
fra terminalen til programmet.
>> Når brukeren skriver inn et nytt tegn, sjekker vi for å se om indeksen
av den neste ledige sporet pluss 1 er større enn kapasiteten av bufferen.
Den en kommer inn fordi om den neste gratis indeksen er 5,
da vår buffer lengde må være 6 takket være 0 indeksering.
Hvis vi har kjørt tom for plass i bufferen, så vi forsøker å endre størrelsen,
dobling det slik at vi kutte ned på antall ganger vi størrelsen
hvis brukeren er å skrive i en veldig lang streng.
Hvis strengen har blitt for lang, eller hvis vi går tom for heap minne,
vi frigjøre vår buffer og avkastning null.
>> Endelig, føyer vi char til bufferen.
Når brukeren treff Enter eller Retur, signaliserer en ny linje,
eller spesielle tegn - kontroll d - som signaliserer en slutt inngang,
vi gjør en sjekk for å se om brukeren faktisk skrevet i noe som helst.
Hvis ikke, går vi tilbake null.
Ellers, fordi vår buffer er trolig større enn vi trenger,
i verste fall er det nesten dobbelt så stor som vi trenger
siden vi doble hver gang vi endrer størrelsen,
vi gjør et nytt eksemplar av strengen ved hjelp av bare hvor mye plass som vi trenger.
Vi legger til en ekstra 1 til malloc samtalen,
slik at det er plass til den spesielle null terminator tegn - \ 0,
som vi føyer til strengen når vi kopierer i resten av tegnene,
hjelp strncpy stedet for strcpy
slik at vi kan spesifisere nøyaktig hvor mange tegn vi ønsker å kopiere.
Strcpy kopier før den treffer en \ 0.
Da vi frigjøre vår buffer og returnere kopi til den som ringer.
>> Hvem visste for eksempel en enkel-tilsynelatende funksjon kan være så komplisert?
Nå vet du hva som går inn i CS50 biblioteket.
>> Mitt navn er Nate Hardison, og dette er CS50.
[CS50.TV]