[Powered by Google Translate] [CS50 Library]
[Nate Hardison] [Harvard University]
[Dies ist CS50. CS50.TV]
Das CS50-Bibliothek ist ein hilfreiches Werkzeug, dass wir auf dem Gerät installiert
um es einfacher für Sie, um Programme zu schreiben, dass Benutzer auffordern für die Eingabe.
In diesem Video werden wir ziehen den Vorhang zurück und schauen, was genau in der CS50-Bibliothek.
>> In dem Video auf C-Bibliotheken, wir reden darüber, wie Sie # include Header-Dateien
der Bibliothek in Ihrem Quellcode,
und dann verknüpfen Sie mit einer binären Library-Datei während der Verknüpfung Phase
der Kompilierung.
Die Header-Dateien angeben, die Schnittstelle der Bibliothek.
Das heißt, sie ausführlich alle Ressourcen, die die Bibliothek für Sie zu nutzen,
wie Funktions-Deklarationen, Konstanten und Datentypen.
Die binäre Library-Datei enthält die Implementierung der Bibliothek,
welches aus der Bibliothek Header-Dateien und die Bibliothek. c Quellcode-Dateien kompiliert.
>> Die binäre Library-Datei ist nicht sehr interessant zu sehen, da es ist gut, in binär.
Also, lassen Sie uns einen Blick auf die Header-Dateien für die Bibliothek statt.
In diesem Fall gibt es nur eine Header-Datei namens cs50.h.
Wir haben es in der Benutzer installiert include
Zusammen mit den anderen System-Bibliotheken 'Header-Dateien.
>> Eines der ersten Dinge, die Sie bemerken ist, dass cs50.h # enthält Header-Dateien aus anderen Bibliotheken -
float, Grenzen, Standard bool und Standard-lib.
Auch nach dem Prinzip der nicht das Rad neu erfinden,
wir haben die CS0 Bibliothek mit Werkzeugen, die anderen zur Verfügung gestellt für uns gebaut.
>> Das nächste, was Sie in der Bibliothek zu sehen müssen, ist, dass wir einen neuen Typ namens definieren "string".
Diese Linie wirklich nur erstellt einen Alias ​​für den char *-Typ,
so dass es nicht magisch verleihen dem neuen String-Typ mit Attributen
häufig mit String-Objekten in anderen Sprachen verbunden sind,
wie Länge.
Der Grund warum wir dies getan haben ist es, neue Programmierer aus den blutigen Details abschirmen
von Zeigern, bis sie bereit sind.
>> Der nächste Teil der Header-Datei ist die Erklärung der Funktionen
dass der CS50-Bibliothek bietet zusammen mit einer Dokumentation.
Beachten Sie die Detailstufe in den Kommentaren hier.
Das ist super wichtig, damit Menschen wissen, wie diese Funktionen nutzen.
Wir erklären, die wiederum funktioniert, um den Benutzer-oder Rückgabebelehrung Zeichen-, Doppel-, Schwimmern, ints aufgefordert,
lange sehnt, und Streicher, mit unseren eigenen String-Typ.
Nach dem Prinzip des Information Hiding,
. haben wir unsere Definition in einem separaten c Umsetzung Datei ablegen - cs50.c--
befindet sich in der Benutzer-Quellverzeichnis.
Wir haben diese Datei zur Verfügung gestellt, so dass Sie einen Blick darauf werfen können,
daraus lernen, und kompilieren Sie es auf verschiedenen Maschinen, wenn Sie es wünschen,
obwohl wir denken, es ist besser, um das Gerät in dieser Klasse arbeiten.
Wie auch immer, lassen Sie uns einen Blick auf sie jetzt.
>> Die Funktionen GetChar GetDouble, GetFloat, GetInt und GetLongLong
sind alle auf der Oberseite des GetString-Funktion eingebaut.
Es stellt sich heraus, dass sie alle folgen im Wesentlichen dem gleichen Muster.
Sie verwenden eine while-Schleife, um den Benutzer für eine Zeile einer Eingabe auffordern.
Sie kehren einen besonderen Wert, wenn die Eingaben des Benutzers eine leere Zeile.
Sie versuchen, die Eingabe des Benutzers, wie des entsprechenden Typs analysieren,
sei es ein char, ein Doppelzimmer, ein Schwimmer, etc.
Und dann sind sie entweder wieder das Ergebnis, wenn die Eingabe erfolgreich analysiert wurde
Erneut auffordern oder sie den Benutzer.
>> Auf einem hohen Niveau, es gibt nichts wirklich schwierig hier.
Vielleicht schriftlichen ähnlich strukturierten Code haben sich in der Vergangenheit.
Vielleicht die kryptische wirkende Teil ist der Anruf, der sscanf die Eingabe des Benutzers analysiert.
Sscanf ist Teil des Eingangs-Format Konvertierung Familie.
Er lebt in Standard io.h, und seine Aufgabe ist es, einen C-String parsen,
nach einem bestimmten Format und speichert die parse Ergebnisse in variable
vorgesehen durch den Anrufer.
Da die Eingangs-Format-Konvertierung Funktionen sind sehr nützlich, weithin verwendete Funktionen
die nicht sind super intuitive zunächst
gehen wir darüber, wie sscanf funktioniert.
>> Das erste Argument sscanf ist ein char * - ein Zeiger auf ein Zeichen.
Für die Funktion nicht richtig arbeiten,
dieser Charakter sollte das erste Zeichen eines C-String sein,
beendet mit dem null \ 0 Zeichen.
Dies ist die Zeichenfolge zu analysieren
Das zweite Argument sscanf ist eine Format-String,
typischerweise in als String Konstante übergeben,
und man könnte einen String, wie dies vor, wenn Sie printf gesehen haben.
Ein Prozentzeichen im Format-String zeigt eine Konvertierungsspezifizierer.
Der Charakter unmittelbar nach einem Prozent-Zeichen,
zeigt das C-Typ, die wir wollen sscanf zu konvertieren.
In GetInt, sehen Sie, dass es eine% d und% c.
Dies bedeutet, dass sscanf wird in eine Dezimalzahl int versuchen - das% d - und char - das% c.
Für jede Konvertierung im Format-String,
sscanf erwartet eine entsprechende Argument später in die Liste der Argumente.
Das Argument muss zu einer entsprechend typisierte Speicherort verweisen
bei dem zum Speichern des Ergebnisses der Umwandlung.
>> Der typische Weg dies zu tun ist, um eine Variable auf dem Stack zu erstellen, bevor der sscanf Anruf
für jedes Element, das Sie aus der Zeichenfolge zu analysieren
und verwenden Sie dann die Adresse Betreiber - das kaufmännische - auf Zeiger übergeben
auf diese Variablen in die sscanf Anruf.
Sie können sehen, dass in GetInt wir genau dies tun.
Kurz vor dem sscanf Anruf, erklären wir einen int namens n und ein char Anruf c auf dem Stack,
und wir geben Hinweise, um sie in die sscanf Anruf.
Setzt man diese Variablen auf dem Stack wird über den Weltraum zugeordnet bevorzugt
auf dem Heap mit malloc, da man den Overhead des malloc Aufruf zu vermeiden,
und Sie brauchen sich keine Gedanken über undichte Speicher kümmern.
Zeichen, die nicht von einem Prozent-Zeichen vorangestellt keine Aufforderung Konvertierung.
Vielmehr nur auf die Format-Spezifikation hinzuzufügen.
>> Zum Beispiel, wenn das Format innerhalb GetInt waren% d stattdessen
sscanf würde für das Schreiben einer durch einen int gefolgt aussehen,
und während es versuchen würde, die int konvertieren, wäre es nichts anderes tun, mit der eine.
Die einzige Ausnahme ist Leerzeichen.
Leerzeichen im Formatstring entspricht jede Menge whitespace -
sogar überhaupt keine.
Also, das ist, warum der Kommentar erwähnt, eventuell mit führenden und / oder nachfolgende Leerzeichen.
So wird es an dieser Stelle sieht aus wie unser sscanf Anruf versuchen, die Eingabe des Benutzers String parsen
indem für mögliche führenden Leerzeichen,
gefolgt von einer int, umgewandelt und wird in der Variablen n int gespeichert werden
gefolgt von einer gewissen Menge von Leerzeichen, gefolgt von einem Zeichen
gespeichert in der char-Variable c.
>> Was ist mit dem Rückgabewert?
Sscanf wird die Eingabezeile von Anfang analysieren zu beenden,
stoppt, wenn er das Ende erreicht oder wenn ein Zeichen in der Eingabe
nicht mit einem Format Zeichen oder, wenn es nicht eine Bekehrung.
Es ist Rückgabewert wird verwendet, um einzelne, wenn es gestoppt.
Wenn sie gestoppt werden, weil sie das Ende der Eingabekette erreicht
bevor Sie irgendwelche Umbauten und wartet, bevor er einen Teil der Format-String übereinstimmen,
dann die spezielle Konstante EOF zurückgegeben.
Andernfalls gibt es die Anzahl der erfolgreichen Conversions,
was könnte 0, 1 oder 2 sein, da wir für zwei Konvertierungen gebeten habe.
In unserem Fall wollen wir sicherstellen, dass die Benutzer in einem int und nur einen int eingegeben.
>> So wollen wir sscanf auf 1 zurück. Sehen Sie, warum?
Wenn sscanf ergab 0, dann werden keine Konvertierungen vorgenommen wurden,
so dass der Benutzer etwas anderes als ein int zu Beginn der Eingabe eingegeben.
Wenn sscanf 2 zurückgibt, dann wird der Benutzer hat richtig geben Sie ihn in zu Beginn des Eingangs,
aber sie dann in einigen Nicht-Leerzeichen eingegeben danach
seit% c Konvertierung erfolgreich.
Wow, das ist ein recht langwieriger Erklärung für ein Funktionsaufruf.
Jedenfalls, wenn Sie mehr Informationen über sscanf und seine Geschwister,
Besuche die man pages, Google, oder beides.
Es gibt viele Format-String-Optionen,
und diese können Sie sparen eine Menge Handarbeit, wenn sie versuchen, um Zeichenfolgen in C analysieren
>> Die letzte Funktion in der Bibliothek zu betrachten ist GetString.
Es stellt sich heraus, dass GetString eine heikle Funktion nicht richtig schreiben kann,
obwohl es scheint, wie so eine einfache, gemeinsame Aufgabe.
Warum ist das der Fall?
Nun, lasst uns darüber, wie wir gehen, um die Linie zu speichern, dass der Benutzer Typen in.
Da ein String ist eine Folge von Zeichen,
könnten wir wollen es in einem Array zu speichern auf dem Stack,
aber wir müssten wissen, wie lange das Array sein wird, wenn wir es erklären.
Ebenso, wenn wir wollen es auf dem Heap setzen,
müssen wir auf malloc passieren die Anzahl von Bytes wollen wir zu reservieren,
dies ist jedoch nicht möglich.
Wir haben keine Ahnung, wie viele Zeichen der Benutzer eintippen
bevor der Benutzer eigentlich gar geben Sie sie.
>> Eine naive Lösung für dieses Problem ist, einfach behalten einen großen Teil des Raumes, sagen wir,
ein Block von 1000 Zeichen für die Eingabe des Benutzers,
vorausgesetzt, dass der Benutzer niemals in einer Zeichenfolge, dass lange geben.
Dies ist eine schlechte Idee, aus zwei Gründen.
Erstens, vorausgesetzt, dass die Nutzer in der Regel nicht in Strings so lange geben,
Sie könnten verschwenden viel Speicher.
Auf modernen Maschinen, könnte dies nicht ein Problem sein, wenn Sie dies tun
in ein oder zwei isolierte Instanzen,
aber wenn Sie unter Eingabe des Benutzers in einer Schleife und Speicherung zur späteren Verwendung
Sie können schnell saugen eine Tonne des Gedächtnisses.
Außerdem, wenn das Programm Sie schreiben ist für einen kleineren Computer -
ein Gerät wie ein Smartphone oder etwas anderes mit begrenztem Speicher -
Diese Lösung wird Probleme viel schneller verursachen.
Der zweite, aus wichtigem Grund nicht tun, ist, dass es Ihr Programm lässt anfällig
was heißt ein Pufferüberlauf Angriff.
In der Programmierung ist ein Pufferspeicher zum temporären Speichern oder Ausgangsdaten,
die in diesem Fall ist unsere 1000-char-Block.
Ein Pufferüberlauf tritt auf, wenn Daten über das Ende des Blocks geschrieben wird.
>> Zum Beispiel, wenn ein Benutzer tatsächlich Typs in mehr als 1000 Zeichen.
Vielleicht erlebt versehentlich bei der Programmierung mit Arrays.
Wenn Sie ein Array von 10 ints haben, stoppt nichts, was man von dem Versuch zu lesen oder zu schreiben,
der 15. Int.
Es gibt keine Compiler-Warnungen oder Fehler.
Das Programm gerade Schnitzer geradeaus und greift auf den Speicher
wo es denkt, dass die 15. int wird, und dies kann Ihren anderen Variablen überschreiben.
Im schlimmsten Fall können Sie überschreiben einige Ihrer programm-interne
Kontrollmechanismen, um was Ihr Programm tatsächlich auszuführen andere Anweisungen
als beabsichtigt.
>> Nun, es ist nicht üblich, dies versehentlich tun,
aber dies ist ein recht häufiges Technik, die bösen Jungs nutzen, um Programme zu brechen
und legte Schadcode auf fremden Rechnern.
Deshalb können wir nicht einfach unsere naive Lösung.
Wir müssen einen Weg finden, um unsere Programme von verwundbar zu verhindern
zu einem Pufferüberlauf Angriff.
Um dies zu tun, müssen wir sicherstellen, dass unsere Puffer kann wachsen, wie wir lesen
weitere Eingabe von dem Benutzer.
Die Lösung? Wir verwenden einen Heap-Puffer.
Da wir ändern können, indem Sie die Größe des realloc Funktion
und wir verfolgen zwei Zahlen - den Index der nächsten freien Slot im Puffer
und die Länge oder die Kapazität des Puffers.
Wir lesen in chars vom Benutzer ein zu einer Zeit mit dem fgetc Funktion.
Das Argument der fgetc Funktion nimmt - stdin - ist ein Verweis auf den Standard-Input-Strings,
das ist ein Eingangskanal vorgeschalteten, mit dem die Eingabe des Benutzers übertragen wird
von dem Endgerät an das Programm.
>> Immer wenn der Benutzer einen neuen Charakter, überprüfen wir, ob der Index
des nächsten freien Schlitz plus 1 größer ist als die Kapazität des Puffers.
Die +1 kommt, denn wenn der nächste freie Index 5,
dann werden unsere Puffers Länge muss 6 durch 0 Indexierung sein.
Wenn wir aus dem Raum in dem Puffer ausgeführt haben, dann werden wir versuchen, es zu ändern,
verdoppeln, so daß wir unten geschnitten nach der Anzahl der Male, dass wir die Größe
wenn der Benutzer in einem wirklich lange Zeichenfolge eingeben.
Wenn der String bekommen hat zu lange oder wenn wir laufen aus Heap-Speicher,
befreien wir unsere Puffer und null zurück.
>> Schließlich, fügen wir die char in den Puffer.
Sobald der Benutzer ENTER oder kehren, Signaltechnik eine neue Zeile,
oder die spezielle char - Steuerung d -, die ein Ende der Eingangssignale,
machen wir eine Überprüfung, ob der Benutzer tatsächlich in nichts eingegeben überhaupt.
Wenn nicht, kehren wir null.
Ansonsten, weil unser Puffer ist wahrscheinlich größer, als wir brauchen,
im schlimmsten Fall ist es fast doppelt so groß wie wir brauchen
da wir verdoppeln jedes Mal, wenn wir die Größe,
machen wir eine neue Kopie des Strings mit nur die Menge an Speicherplatz, die wir brauchen.
Wir fügen eine zusätzliche 1 der malloc Anruf
so dass es Raum für den besonderen Nullabschlusszeichen Charakter - das \ 0,
die wir anhängen, um die Zeichenfolge, wenn wir in den Rest der Charaktere zu kopieren,
Verwendung strncpy anstelle von strcpy
so dass wir genau angeben, wie viele Zeichen haben wir kopieren wollen.
Strcpy kopiert, bis es eine \ 0 trifft.
Dann befreien wir unser Puffer und gibt das Exemplar des Anrufers.
>> Wer wusste, wie eine einfache anmutende Funktion, so könnte kompliziert sein?
Jetzt wissen Sie, was in der CS50-Bibliothek.
>> Mein Name ist Nate Hardison, und dies ist CS50.
[CS50.TV]