[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Dit is CS50. CS50.TV]
De CS50 bibliotheek is een handig hulpmiddel dat we hebben geïnstalleerd op het apparaat
om het gemakkelijker maken voor u om programma's te schrijven die prompt gebruikers om input.
In deze video gaan we trekken het gordijn en kijken naar wat er precies in de CS50 bibliotheek.
>> In de video op C bibliotheken, we praten over hoe je # include headers bestanden
van de bibliotheek in uw broncode,
en dan heb je te verbinden met een binaire bibliotheek bestand tijdens het koppelen fase
van de compilatie-proces.
De header-bestanden geeft u de interface van de bibliotheek.
Dat wil zeggen, ze detail alle middelen die de bibliotheek heeft voor u beschikbaar te gebruiken,
zoals functie declaraties, constanten en gegevenstypen.
De binaire bibliotheek bestand bevat de uitvoering van de bibliotheek,
die is samengesteld uit header van de bibliotheek bestanden en de bibliotheek. c broncode-bestanden.
>> Het binaire bibliotheek bestand is niet erg interessant om naar te kijken want het is, nou ja, in binaire.
Dus, laten we een kijkje nemen op de header-bestanden voor de bibliotheek in plaats daarvan.
In dit geval is er maar een header bestand genaamd cs50.h.
We hebben geïnstalleerd in de gebruiker zijn directory
samen met het andere systeem bibliotheken 'header-bestanden.
>> Een van de eerste dingen die je zal opvallen is dat cs50.h # header-bestanden van andere bibliotheken omvat -
vlotter, grenzen, standaard bool, en standaard lib.
Nogmaals, volgens het principe van niet het wiel opnieuw uitvinden,
we hebben opgebouwd de CS0 bibliotheek met behulp van tools die andere voorzien voor ons.
>> Het volgende wat je ziet in de bibliotheek is dat we definiëren van een nieuw type genaamd "string".
Deze lijn eigenlijk alleen maar creëert een alias voor de char * type,
zodat het niet op magische wijze doordringen van de nieuwe string type met attributen
vaak geassocieerd met String-objecten in andere talen,
zoals lengte.
De reden dat we dit gedaan hebt is om nieuwe programmeurs te beschermen tegen de bloedige details
van pointers totdat ze klaar zijn.
>> Het volgende deel van de header-bestand is de verklaring van de functies
dat de CS50 bibliotheek biedt, samen met documentatie.
Let op de mate van detail in de comments hier.
Dit is super belangrijk, zodat mensen weten hoe deze functies te gebruiken.
Wij verklaren op hun beurt functioneert om de gebruiker vragen en terug te keren tekens, doubles, drijvers, ints,
lang naar verlangt, en strijkers, met behulp van onze eigen string type.
Volgens het principe van informatie verbergen,
hebben we onze definitie in een apart c implementatie-bestand -. cs50.c--
gelegen in de gebruiker bron directory.
We hebben op voorwaarde dat bestand, zodat u kunt een kijkje nemen op het,
leren, en het op verschillende machines hercompileren als je wilt,
hoewel we denken dat het beter is om met het apparaat werken voor deze klasse.
Hoe dan ook, laten we nu een kijkje nemen op het.
>> De functies getchar, GetDouble, GetFloat, GetInt en GetLongLong
zijn gebouwd bovenop de GetString functie.
Het blijkt dat ze allemaal in wezen hetzelfde patroon volgen.
Zij maken gebruik van een while-lus aan de gebruiker voor een lijn van invoer te vragen.
Zij keren een bijzondere waarde als de gebruiker ingangen een lege regel.
Ze proberen de gebruiker ingang ontleden als het juiste type,
of het nu een char, een dubbele, een float, enz.
En dan zijn ze ofwel terug te keren het resultaat als de ingang werd succesvol geparsed
of ze reprompt de gebruiker.
>> Op een hoog niveau, er is niets echt lastig hier.
Je zou kunnen hebben geschreven op soortgelijke wijze gestructureerde code zelf in het verleden.
Misschien wel het meest cryptische ogende deel is de sscanf gesprek dat invoer van de gebruiker parseert.
Sscanf maakt deel uit van de input conversie familie.
Het leeft in standaard io.h, en zijn werk is te ontleden een C string,
volgens een bepaald formaat, opslaan van de resultaten in variabele parse
die door de beller.
Omdat de ingang conversie functies zijn zeer nuttig, op grote schaal gebruikte functies
die niet zijn super intuïtief op het eerste,
we gaan over hoe sscanf werkt.
>> Het eerste argument om sscanf is een char * - een pointer naar een karakter.
Voor de functie goed te laten werken,
dat karakter zal het eerste teken van een C string zijn,
beëindigd met de nul \ 0 karakter.
Dit is de string te parsen
Het tweede argument om sscanf is een format string,
meestal doorgegeven als een string constante,
en u zou kunnen hebben gezien een string als dit eerder bij het gebruik van printf.
Een procent teken in de format string geeft een conversie specificatie.
Het karakter onmiddellijk na een procent teken,
geeft het C-type dat we willen sscanf converteren naar.
In GetInt, zie je dat er een% d en een% c.
Dit betekent dat sscanf zal proberen om een ​​decimaal int - de% d - en een char - de% c.
Voor elke conversie specificatie in de format string,
sscanf verwacht een corresponderende argument later in de lijst met argumenten.
Dit argument moet verwijzen naar een voldoende getypte locatie
waarin het resultaat van de conversie slaan.
>> De typische manier om dit te doen is om een ​​variabele te maken op de stapel voor de sscanf oproep
voor elk item dat u wilt parseren op basis van de string
en gebruik dan de adres-operator - de ampersand - om pointers geven
variabelen die de sscanf gesprek.
Je kunt zien dat in GetInt we precies doen.
Vlak voor de sscanf oproep, verklaren we een int genoemd n en een char oproep c op de stapel,
en passeren we verwijzingen naar hen in de sscanf gesprek.
Putting deze variabelen op de stack heeft de voorkeur boven het gebruik van toegewezen ruimte
op de heap met malloc, omdat je de overhead van de malloc oproep te voorkomen,
en je hoeft geen zorgen te maken over het lekken geheugen.
Tekens niet voorafgegaan door een procentteken niet vragen conversie.
In plaats van ze alleen maar toe te voegen aan het formaat specificatie.
>> Als bijvoorbeeld de opmaakreeks in GetInt waren% d plaats,
sscanf zou kijken naar de letter een, gevolgd door een int,
en terwijl het zou proberen om de int te zetten, zou het niet iets anders doen met de een.
De enige uitzondering hierop is witruimte.
Witruimte tekens in de format string passende hoeveelheid witruimte -
zelfs helemaal geen.
Dus, dat is de reden waarom de opmerking vermeldt eventueel met voor-en / of eindigen met spaties.
Dus, zal proberen op dit punt lijkt ons sscanf oproep aan de gebruiker input string parsen
door te controleren op mogelijke toonaangevende witruimte,
gevolgd door een int die wordt omgezet en opgeslagen in de variabele n int
gevolgd door een hoeveelheid witruimte en gevolgd door een teken
opgeslagen in de char variabele c.
>> Hoe zit het met de return waarde?
Sscanf zal ontleden de invoerregel van begin tot eind,
stoppen wanneer zij tot het einde of bij een karakter in de input
komt niet overeen met een format of wanneer het niet kan maken een conversie.
Het rendement van waarde wordt gebruikt om enkel wanneer het gestopt.
Indien gestopt, omdat het einde van de invoertekenreeks
voordat u conversies en alvorens het als mislukt om een ​​deel van de format string overeenkomt,
dan is de speciale constante EOF wordt geretourneerd.
Anders wordt het aantal succesvolle conversies,
waardoor 0, 1 of 2, want heeft gevraagd twee conversies.
In ons geval willen we ervoor zorgen dat de gebruiker heeft ingevoerd in een int en alleen een int.
>> Dus willen we sscanf tot 1 terug te keren. Zie waarom?
Als sscanf leverde 0, dan is er geen conversies werden gemaakt,
zodat de gebruiker heeft ingevoerd iets anders dan een int begin van de ingang.
Als sscanf retourneert 2, dan is de gebruiker heeft goed typt u deze in het begin van de input,
maar ze vervolgens getypt in een aantal niet-scheidingsteken achteraf
omdat de% c conversie gelukt.
Wow, dat is nogal een uitgebreide uitleg voor een functie oproep.
Hoe dan ook, als je wilt meer informatie over sscanf en haar broers en zussen,
check out de man pagina's, Google, of beide.
Er zijn tal van format string opties,
en deze kunnen besparen u een hoop handwerk wanneer het proberen om strings te ontleden in C.
>> De laatste functie in de bibliotheek om te kijken is GetString.
Het blijkt dat GetString is een lastige functie om goed te schrijven,
ook al lijkt zo'n eenvoudige, gemeenschappelijke taak.
Waarom is dit het geval?
Nou, laten we nadenken over hoe we gaan de lijn op te slaan dat de gebruiker typt inch
Omdat een string is een opeenvolging van tekens,
we zouden willen op te slaan in een array op de stapel,
maar we zouden moeten weten hoe lang de array zal zijn wanneer we het te verklaren.
Evenzo, als we willen het op de hoop,
moeten we doorgeven aan malloc het aantal bytes dat we willen reserveren,
maar dit is onmogelijk.
We hebben geen idee hoeveel tekens de gebruiker typt in
voordat de gebruiker doet eigenlijk typt.
>> Een naïeve oplossing voor dit probleem is om gewoon behouden een groot deel van de ruimte, bijvoorbeeld
een blok van 1000 tekens voor invoer van de gebruiker,
in de veronderstelling dat de gebruiker nooit zou typen in een string zo lang.
Dit is een slecht idee om twee redenen.
Ten eerste, in de veronderstelling dat gebruikers meestal niet typen in snaren die lange,
je zou verspillen veel geheugen.
Op moderne machines, zou dit niet een probleem als je dit doet
in een of twee afzonderlijke gevallen
maar als je het nemen van gebruikers input in een lus en opslaan voor later gebruik,
kunt u snel opzuigen van een ton van het geheugen.
Bovendien, als het programma dat u aan het schrijven bent is voor een kleinere computer -
een apparaat zoals een smartphone of iets anders met een beperkt geheugen -
deze oplossing problemen veel sneller.
De tweede, meer serieuze reden om dit niet doen is dat het uw programma kwetsbaar verlaat
aan wat heet een buffer overflow aanval.
Bij het programmeren, een buffer is het geheugen wordt gebruikt voor de tijdelijke opslag in-of uitgang van gegevens,
in dit geval is onze 1000 char blok.
Een buffer overflow optreedt wanneer gegevens geschreven voorbij het einde van het blok.
>> Als bijvoorbeeld een gebruiker daadwerkelijk soort doet in meer dan 1000 tekens.
Je zou kunnen hebben ervaren per ongeluk bij het programmeren met arrays.
Als u een array van 10 ints, niets houdt je tegen probeert te lezen of te schrijven
15 int.
Er zijn geen compiler waarschuwingen of fouten.
Het programma gewoon blunders rechtdoor en toegang tot het geheugen
waar hij denkt dat de 15e int zal zijn, en dit kan overschrijven uw andere variabelen.
In het ergste geval kunt u overschrijven enkele van interne je programma's
controlemechanismen, het veroorzaken van uw programma daadwerkelijk uit te voeren verschillende instructies
dan je bedoeling was.
>> Nu, het is niet gebruikelijk om per ongeluk doen,
maar dit is een vrij algemeen techniek die slechteriken gebruiken om programma's te doorbreken
en zet kwaadaardige code op andermans computer.
Daarom kunnen we niet alleen gebruik maken van onze naïeve oplossing.
We moeten een manier vinden om onze programma's te voorkomen dat kwetsbare
voor een buffer overflow aanval.
Om dit te doen, moeten we ervoor zorgen dat onze buffer kan groeien als we lezen
meer input van de gebruiker.
De oplossing? We gebruiken een hoop toegewezen buffer.
Aangezien wij kunt de grootte van het gebruik van de de grootte van de realloc functie,
en we houden van twee getallen - de index van de volgende lege sleuf in de buffer
en door de lengte van de buffer.
We lezen in tekens van de gebruiker een voor een met de fgetc functie.
Het argument van de fgetc functie neemt - stdin - is een verwijzing naar de standaard input string,
die een voorgeschakelde ingangskanaal waarmee invoer van de gebruiker overbrengen
van de terminal naar het programma.
>> Wanneer de gebruiker een nieuw karakter, we controleren of de index
van de volgende vrije slot plus 1 groter is dan de capaciteit van de buffer.
De +1 komt in, want als de volgende vrije index is 5,
dan is onze buffer lengte moet 6 dankzij 0 indexering.
Als we geen ruimte meer in de buffer, dan proberen we hem te vergroten of verkleinen,
een verdubbeling van het zo dat we bezuinigen op het aantal keren dat we de grootte van
als de gebruiker in te typen een heel lange string.
Als de string heeft gekregen te lang is of als we opraken van heap-geheugen,
bevrijden we onze buffer en terug te keren null.
>> Tenslotte voegt de char we de buffer.
Zodra de gebruiker op Enter of Return, het signaleren van een nieuwe lijn,
of de speciale tekens - controle d - die een einde van de ingangssignalen,
we doen een controle om te zien of de gebruiker daadwerkelijk getypt in helemaal niets.
Zo niet, dan keren we terug null.
Anders, omdat onze buffer is waarschijnlijk groter dan we nodig hebben,
in het ergste geval bijna tweemaal zo groot als we
omdat we verdubbelen elke keer als we vergroten of verkleinen,
maken we een nieuwe kopie van de string met behulp van alleen de hoeveelheid ruimte die we nodig hebben.
We voegen een extra 1 tot en met de malloc oproep,
zodat er ruimte is voor de speciale null-terminator karakter - de \ 0,
die we toevoegen aan de string een keer kopiëren we in de rest van de personages,
met strncpy plaats van strcpy
zodat wij precies aan te geven hoeveel tekens we willen kopiëren.
Strcpy kopieert totdat het een \ 0.
Dan bevrijden we onze buffer en zendt het exemplaar naar de beller.
>> Wie had gedacht dat een dergelijke eenvoudige uitziende functie kan zo ingewikkeld?
Nu weet je wat er in de CS50 bibliotheek.
>> Mijn naam is Nate Hardison, en dit is CS50.
[CS50.TV]