[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Dette er CS50. CS50.TV]
Det CS50 Biblioteket er et nyttigt værktøj, som vi har installeret på apparatet
at gøre det lettere for dig at skrive programmer, der ansporer brugere til input.
I denne video, vil vi trække sig tilbage gardinet og se på, hvad der præcist er i CS50 bibliotek.
>> I videoen på C-biblioteker, taler vi om, hvordan du # include headers filer
af biblioteket i din kildekode,
og derefter du linker med en binær biblioteksfil i sammenkædningen fase
af fastlæggelsen processen.
De header-filer angive interfacet af biblioteket.
Det vil sige, at de detaljer alle de ressourcer, biblioteket har til rådighed for dig at bruge,
ligesom funktionserklæringer, konstanter og datatyper.
Den binære bibliotek fil indeholder gennemførelsen af ​​biblioteket,
som er udarbejdet på grundlag af bibliotekets header-filer og bibliotekets. C kildekodefiler.
>> Den binære biblioteksfil er ikke meget interessant at se på, da det er, ja, i binær.
Så lad os tage et kig på header-filer til biblioteket i stedet.
I dette tilfælde er der kun en header fil kaldet cs50.h.
Vi har installeret det i brugerens omfatter biblioteket
sammen med de andre systemkomponenter bibliotekernes header-filer.
>> En af de første ting, du lægger mærke til, er, at cs50.h # inkluderer header-filer fra andre biblioteker -
float, grænser, standard bool og standard lib.
Igen, efter princippet om ikke at genopfinde hjulet,
vi har opbygget den CS0 biblioteket ved hjælp af værktøjer som andre, der er fastsat for os.
>> Den næste ting du vil se i biblioteket er, at vi definere en ny type kaldet "streng".
Denne linje virkelig bare skaber et alias for den char * type,
så det ikke magisk gennemsyre den nye streng type med attributter
ofte forbundet med strengeobjekter på andre sprog,
såsom længde.
Grunden til at vi har gjort dette, er at beskytte nye programmører fra de blodige detaljer
af pegepinde, indtil de er klar.
>> Den næste del af header filen er erklæringen af ​​funktionerne
at CS50 Biblioteket sammen med dokumentation.
Læg mærke til detaljeringsgraden i kommentarerne her.
Det er super vigtigt, så folk ved, hvordan man bruger disse funktioner.
Vi erklærer til gengæld fungerer til at bede brugeren og retur chars, doubler, flåd, int'er,
lang længes, og strygere, ved hjælp af vores egen streng type.
Efter princippet om information skjul,
Vi har sat vores definition i en separat c implementering fil -. cs50.c--
placeret i brugerens kildebiblioteket.
Vi har forudsat at fil, så du kan tage et kig på det,
lære af det, og kompilere den på forskellige maskiner, hvis du ønsker,
selv om vi synes, det er bedre at arbejde på apparatet for denne klasse.
Anyway, lad os tage et kig på det nu.
>> Funktionerne getchar, GetDouble, GetFloat, GetInt, og GetLongLong
er alle indbygget oven på GetString funktion.
Det viser sig, at de følger alle væsentlige samme mønster.
De bruger en while-løkke til at bede brugeren om en linje af input.
De vender tilbage en særlig værdi, hvis brugeren indtaster en tom linje.
De forsøger at parse brugerens input som den relevante type,
det være sig en char, en dobbelt, en float osv.
Og så de enten returnere resultatet, hvis inputtet er fuldført parset
eller de reprompt brugeren.
>> På et højt niveau, er der ikke noget virkelig svært her.
Du har måske skrevet tilsvarende strukturerede koden selv i fortiden.
Måske den mest kryptiske udseende del er den sscanf opkald, der analyserer brugerens indlæsning.
Sscanf er en del af input formatkonvertering familien.
Den lever i standard io.h, og dens opgave er at parse en C streng,
efter et bestemt format, at lagre parse resulterer i variable
tilvejebringes af den opkaldende.
Da det indtastede format konvertering funktioner er meget nyttige, almindeligt anvendte funktioner
der er ikke super intuitive i starten,
vi vil gå over, hvordan sscanf fungerer.
>> Det første argument til sscanf er en char * - en pegepind til et tegn.
For at funktionen skal fungere korrekt,
denne karakter bør være det første tegn på en C-streng,
termineres med nul \ 0 tegn.
Dette er streng at parse
Det andet argument til sscanf er en formatstreng,
typisk bestået i som en streng konstant,
og du måske har set en streng som dette før, når du bruger printf.
Et procenttegn i formatet streng viser en konvertering Projekteringsvejledning.
Tegnet umiddelbart efter et procenttegn,
angiver type C, som vi ønsker sscanf at konvertere til.
I GetInt, se dig, at der er en% d og en% c.
Det betyder, at sscanf vil forsøge at en decimal int - den% d - og en char - den% c.
For hver konvertering Projekteringsvejledning i det format string,
sscanf forventer en tilsvarende argument senere i sin argumentation liste.
Dette argument må pege på en passende indtastet placering
hvor at lagre resultatet af omdannelsen.
>> Den typiske måde at gøre dette på er at oprette en variabel på stakken før sscanf opkald
for hvert element, du ønsker at tolke fra strengen
og derefter bruge den adresse operatør - tegnet - at passere pointers
disse variabler til sscanf opkaldet.
Du kan se, at i GetInt vi gøre netop dette.
Lige før sscanf opkald, erklærer vi en int kaldet n og en char opkald c på stakken,
og vi passerer henvisninger til dem i sscanf opkald.
Sætte disse variabler på stakken er foretrukket frem for hjælp tildelt plads
på heapen med malloc, da du undgå overhead af malloc opkald,
og du behøver ikke at bekymre dig om utæt hukommelse.
Tegn ikke indledt med et procenttegn ikke bede konvertering.
Snarere de bare føje til formatet specifikationen.
>> For eksempel, hvis det format string i GetInt var% d stedet
sscanf ville kigge efter bogstavet a efterfulgt af en int,
og mens det vil forsøge at konvertere int, ville det ikke gøre noget andet med en.
Den eneste undtagelse til dette er mellemrum.
Blanktegn i formatstreng matche enhver mængde whitespace -
selv slet ingen.
Så det er grunden til, at kommentaren nævner muligvis med førende og / eller afsluttende blanke.
Så vil på dette tidspunkt det ligner vores sscanf opkald forsøge at parse brugerens input streng
ved at kontrollere for eventuelle ledende mellemrum,
efterfulgt af en int, der konverteres og gemmes i int Variablen n
efterfulgt af en vis mængde af mellemrum, og efterfulgt af et tegn
lagret i den forkullede masse variable ca.
>> Hvad med den returnerede værdi?
Sscanf vil parse input linje fra start til slut,
stopper, når den når slutningen, eller når et tegn i input
matcher ikke et format forandringer, eller når det ikke kan lave en konvertering.
Det er returværdien bruges til at fremhæve, når det stoppet.
Hvis det stoppes, da den nåede enden af ​​input string
før du foretager nogen konverteringer og før undlade at matche en del af formatstreng,
så den særlige konstant EOF returneres.
Ellers er det returnerer antallet af gennemførte konverteringer,
som kunne være 0, 1 eller 2, da vi har bedt om to konverteringer.
I vores tilfælde vil vi sørge for, at brugeren har indtastet i en int, og kun en int.
>> Så vi ønsker sscanf at returnere 1. Se hvorfor?
Hvis sscanf returnerede 0, så ingen konverteringer blev foretaget,
så brugeren har indtastet noget andet end en int ved begyndelsen af ​​den indgående.
Hvis sscanf returnerer 2, så brugeren ikke korrekt skrive det i begyndelsen af ​​input,
men de så skrevet i nogle ikke-hvidt tegn bagefter
eftersom% c omdannelse lykkedes.
Wow, det er en ganske lang forklaring til én funktion opkald.
Anyway, hvis du ønsker mere information om sscanf og sine søskende,
tjek de man-siderne, Google, eller begge dele.
Der er masser af formatstrengssårbarheder muligheder,
og disse kan spare dig for en masse manuelt arbejde, når de forsøger at parse strenge i C.
>> Den endelige funktion i biblioteket for at se på, er GetString.
Det viser sig, at GetString er en vanskelig funktion til at skrive ordentligt,
selvom det virker som sådan en simpel, fælles opgave.
Hvorfor er det sådan?
Nå, lad os tænke over, hvordan vi skal gemme den linje, som brugeren skriver i.
Da en streng er en sekvens af tegn,
vi måske ønsker at gemme det i et array på stakken,
men vi ville få brug for at vide, hvor længe sættet vil være, når vi erklære den.
Ligeledes, hvis vi ønsker at sætte det på bunke,
vi er nødt til at passere til malloc antallet af bytes vi ønsker at reservere,
men dette er umuligt.
Vi har ingen idé om, hvor mange tegn brugeren skal indtaste
før brugeren rent faktisk skriver dem.
>> En naiv løsning på dette problem er at bare reservere en stor luns af plads, siger,
en blok på 1000 tegn for brugerens indlæsning,
forudsætning af, at brugeren aldrig ville skrive i en snor så længe.
Det er en dårlig idé af to grunde.
Først antager, at brugerne typisk ikke skrive strenge, der længe,
du kunne spilde en masse hukommelse.
På moderne maskiner, kan dette ikke være et problem hvis du gør dette
i en eller to isolerede tilfælde,
men hvis du tager brugerens input i en løkke og oplagring til senere brug,
du kan hurtigt suge op et væld af hukommelse.
Desuden, hvis det program, du skriver, er en mindre computer -
en enhed som en smartphone eller noget andet med begrænset hukommelse -
denne løsning vil skabe problemer meget hurtigere.
Den anden, mere alvorlig grund til ikke at gøre dette er, at det efterlader dit program sårbar
til, hvad der kaldes en buffer overflow angreb.
I programmering, er en buffer hukommelse, der bruges til midlertidigt at gemme input eller output data,
som i dette tilfælde er vores 1000-char blok.
Et bufferoverløb opstår, når data skrives forbi enden af ​​blokken.
>> For eksempel. Hvis en bruger rent faktisk type i mere end 1000 chars
Du har måske oplevet dette ved et uheld, når du programmerer med arrays.
Hvis du har en vifte af 10 int'er, intet stopper dig fra at forsøge at læse eller skrive
den 15. int.
Der er ingen compiler advarsler eller fejl.
Programmet bare brølere ligeud og tilgår hukommelse
hvor den mener den 15. int vil blive, og dette kan overskrive dine andre variabler.
I værste fald kan du overskrive nogle af dit program interne
kontrolmekanismer, at forårsage dit program faktisk at gennemføre forskellige instruktioner
end du havde tænkt dig.
>> Nu er det ikke almindeligt at gøre dette ved et uheld,
men dette er en ret almindelig teknik, som skurkene bruge til at bryde programmer
og sætte skadelig kode på andre folks computere.
Derfor kan vi ikke bare bruge vores naive løsning.
Vi har brug for en måde at forhindre vores programmer fra at være sårbar
til en buffer overflow angreb.
For at gøre dette, er vi nødt til at sikre, at vores buffer kan vokse, når vi læser
more input fra brugeren.
Løsningen? Vi bruger en bunke allokeret buffer.
Da vi kan ændre størrelsen ved hjælp af resize den realloc funktion,
og vi holde styr på to numre - indekset for den næste tomme slot i bufferen
og længden eller kapacitet af bufferen.
Vi læser i chars fra brugeren én ad gangen ved hjælp af fgetc funktionen.
Argumentet den fgetc funktion tager - stdin - er en reference til den standard input strengen,
som er en preconnected indgangskanal, der anvendes til at overføre brugerens indlæsning
fra terminalen til programmet.
>> Når brugeren skriver i en ny karakter, tjekker vi for at se, om indekset
Den næste ledige plads plus 1 er større end kapaciteten af ​​pufferen.
Da +1 kommer ind, fordi hvis det næste fri-indekset er 5,
så vores buffer længde skal være 6 takket være 0 indeksering.
Hvis vi har kørt ud af rummet i bufferen, så vi forsøger at ændre dens størrelse,
fordobling så vi skære ned på det antal gange, som vi størrelsen
hvis brugeren er at skrive i en virkelig lang snor.
Hvis strengen har fået for lang, eller hvis vi løber tør for heap-hukommelse,
vi befri vores buffer og tilbagevenden null.
>> Endelig vil vi tilføje trækullet til bufferen.
Når brugeren hits indtaste eller vende tilbage, signalerer en ny linje,
eller den særlige char - kontrol d - som signalerer en ende af input,
vi gør en check for at se, om brugeren rent faktisk har skrevet i noget som helst.
Hvis ikke, vender vi null.
Ellers fordi vores buffer er sandsynligvis større, end vi har brug for,
i værste fald er det næsten dobbelt så stort som vi har brug for
da vi fordoble hver gang vi ændrer størrelse,
vi laver en ny kopi af strengen ved blot at bruge den mængde plads, som vi har brug for.
Vi tilføjer et ekstra 1 til malloc opkaldet,
så der er plads til den særlige null terminator karakter - \ 0,
som vi tilføje til strengen, når vi kopierer i resten af ​​figurerne,
ved hjælp strncpy stedet for strcpy
så vi kan specificere præcis hvor mange tegn vi ønsker at kopiere.
Strcpy kopier, indtil den rammer en \ 0.
Så vi befri vores buffer og returnere kopien til den, der ringer.
>> Hvem vidste sådan en simpel-tilsyneladende funktion kunne være så kompliceret?
Nu ved du, hvad der går ind i CS50 biblioteket.
>> Mit navn er Nate Hardison, og dette er CS50.
[CS50.TV]