[Powered by Google Translate] [§ 4 - komfortabler] [Rob Bowden - Harvard University] [Dies ist CS50. - CS50.TV] Wir haben ein Quiz morgen bei euch nicht wusste, dass. Es ist im Grunde auf alles, was Sie in der Klasse gesehen haben könnte oder sollte in der Klasse gesehen haben. Dazu gehören Zeiger, obwohl sie ein sehr aktuelles Thema sind. Sie sollten zumindest verstehen die hohe ihnen. Alles, was über der Klasse gegangen war Sie für die Quiz verstehen sollten. Also, wenn Sie Fragen zu ihnen haben, können Sie diese nun fragen. Aber das wird ein sehr Studenten geleiteten Sitzung sein, wo euch Fragen zu stellen, so hoffentlich Menschen haben Fragen. Hat jemand Fragen? Ja. >> [Schüler] Können Sie über Zeiger wieder los? Ich werde über Zeiger gehen. Alle Ihre Variablen unbedingt leben in Erinnerung, aber in der Regel müssen Sie sich darüber keine Sorgen und du nur sagen, x + 2 und y + 3 und der Compiler wird herauszufinden, wo die Dinge sind für Sie leben. Sobald Sie mit Zeigern zu tun haben, jetzt bist du explizit mit dieser Memory-Adressen. So dass eine einzelne Variable wird immer nur bei einer einzigen Adresse zu jeder gegebenen Zeit zu leben. Wenn wir einen Zeiger deklarieren möchten, ist das, was die Art aussehen würde? Ich möchte einen Zeiger p zu erklären. Was macht der Typ aussehen? [Schüler] int * p. >> Ja. So int * p. Und wie mache ich es deuten auf x? >> [Schüler] Ampersand. [Bowden] So kaufmännisches wörtlich heißt die Adresse des Betreibers. Also, wenn ich sage & x es wird immer die Speicheradresse der Variablen x. So jetzt habe ich den Zeiger p, und überall in meinem Code kann ich * p verwenden oder ich könnte x und es wird genau die gleiche Sache. (* P). Was ist dieses Tun? Was bedeutet, dass Sterne bedeuten? [Schüler] Es bedeutet, einen Wert an diesem Punkt. >> Ja. Also, wenn wir es betrachten, kann es sehr nützlich sein, ziehen Sie die Diagramme wenn dies eine kleine Box des Speichers für x, der um den Wert 4 weisen passiert, dann haben wir eine kleine Schachtel mit Speicher für p, und so p Punkte x, so zeichnen wir einen Pfeil von p x. Wenn wir also sagen * p wir selbstverständlich an die Box, p. Star ist folgen Sie den Pfeil und dann tun, was Sie wollen mit diesem Kasten rechts gibt. So kann ich sagen, * p = 7; und das wird an die Box, die x und Veränderung, die bis 7 ist zu gehen. Oder ich könnte sagen, int z = * p * 2; Das ist verwirrend, weil es Sterne, Stern. Der eine Stern Dereferenzierung p, der andere Stern 2 multipliziert. Beachten Sie, ich könnte genauso gut ersetzt die * p mit x. Sie können sie in der gleichen Weise zu nutzen. Und dann später kann ich p deuten auf eine völlig neue Sache zu haben. Ich kann nur sagen, p = &z; So, jetzt P nicht mehr Punkte auf x; es zeigt auf z. Und jedes Mal wenn ich zu tun * p es ist das dasselbe wie z. So die nützliche Sache über dieses ist, wenn wir immer in Funktionen zu starten. Es ist eine Art von nutzlosen, um einen Zeiger zu erklären, dass auf etwas und dann bist du nur Dereferenzierung es wenn Sie könnten die ursprüngliche Variable verwendet haben, um mit zu beginnen. Aber wenn man in Funktionen zu bekommen - so sagen wir, wir haben eine Funktion, int foo, das dauert einen Zeiger und tut einfach * p = 6; Wie wir zuvor mit Swap gesehen haben, können Sie nicht eine effektive Swap-und eine separate Funktion nur durch Weitergabe Zahlen, weil alles in C immer vorbei ist nach Wert. Selbst wenn Sie vorbei Zeigern Sie nach Wert vorbei. Es passiert einfach so, dass diese Werte Speicheradressen sind. Wenn ich also sage foo (p), ich bin das Bestehen der Zeiger in der Funktion foo und dann foo tut * p = 6; So innerhalb der genannten Funktion ist noch * p entspricht x, aber ich kann nicht x innerhalb dieser Funktion, weil es nicht scoped innerhalb dieser Funktion. So * p = 6 ist die einzige Art, wie ich eine lokale Variable von einer anderen Funktion zugreifen können. Oder, na ja, sind Zeiger die einzige Art, wie ich eine lokale Variable von einer anderen Funktion zugreifen können. [Schüler] Lassen Sie uns sagen, Sie wollten einen Zeiger zurückgeben. Wie genau macht man das? [Bowden] Gibt einen Zeiger als in so etwas wie int y = 3; return & y? >> [Schüler] Yeah. [Bowden] Okay. Sie sollten niemals tun. Das ist schlecht. Ich glaube, ich sah in diesen Vorlesungsfolien Sie begann sehen diese ganze Diagramm des Speichers wo hier hast du Speicheradresse 0 und hier unten haben Sie Speicheradresse 4 Konzerte oder 2 zum 32. So dann hast du ein paar Sachen und ein paar Sachen und dann haben Sie Ihren Stack und du hast deine Haufen, die Sie gerade erst begonnen Lernen über, aufgewachsen. [Schüler] Ist das nicht der Haufen über dem Stapel? Yeah. Der Heap ist oben, nicht wahr? >> [Student] Nun legte er 0 an der Spitze. [Schüler] Oh, legte er 0 an der Spitze. >> [Schüler] Oh, okay. Disclaimer: Anywhere mit CS50 du wirst sehen, es auf diese Weise. >> [Schüler] Okay. Es ist nur so, dass, wenn man gerade sehen Stacks, wie wenn man von einem Stapel halten Sie von Stapeln Dinge übereinander denken. So neigen wir dazu, dies um kippen, so dass die Stapel wächst wie ein Stapel gewohnt Statt des Stapels nach unten hängt. >> [Schüler] nicht haufenweise technisch wachsen auch, obwohl? Es hängt davon ab, was Sie unter aufwachsen. Der Stack und Heap immer in entgegengesetzte Richtungen wachsen. Ein Stapel wird immer das Aufwachsen in dem Sinne, dass es wächst bis zu höheren Speicheradressen, und der Haufen wächst nach unten in, dass es zu niedrigeren Speicheradressen wächst. So die Spitze ist 0 und der Boden ist High Memory-Adressen. Sie sind beide wachsen, nur in entgegengesetzter Richtung. [Schüler] Ich meinte nur, weil Sie sagten, Sie setzen Stapel auf dem Boden denn es scheint intuitiver weil für den Stapel an der Oberseite eines Haufens beginnen, Heap ist auf sich selbst zu, so that's - >> Ja. Sie denken auch an den Heap wächst und größer, aber der Stapel so mehr. So der Stapel ist diejenige, die wir irgendwie wollen zeigen, aufwachsen. Aber wohin man sieht sonst wird die Adresse 0 an der Spitze zeigen und die höchste Speicheradresse an der Unterseite, so ist dies Ihre übliche Sicht der Erinnerung. Haben Sie eine Frage? [Schüler] Kannst du uns mehr über die Haufen? Yeah. Ich werde das in einer Sekunde. Zunächst geht zurück, warum Rückgabe und y ist eine schlechte Sache, auf dem Stapel, den Sie haben eine Reihe von Stack-Frames, die alle Funktionen darstellen welche genannt worden. So ignorieren früheren Dinge, ist der obere Teil Ihres Stacks immer gehen die wichtigste Funktion sein denn das ist die erste Funktion, die aufgerufen wird. Und dann, wenn Sie eine andere Funktion aufrufen, wird der Stapel wird wachsen nach unten. Also, wenn ich eine Funktion, foo, rufen und es bekommt seinen eigenen Stack-Frame, es so nennen eine Funktion, eine Bar, es bekommt seinen eigenen Stack-Frame. Und bar sein könnte rekursive und es könnte sich nennen, und so, dass der zweite Aufruf von bar wird seine eigenen Stack-Frame zu bekommen. Und so was geht in diesen Stack-Frames sind alle lokalen Variablen und alle der Funktionsargumente dass - Alle Dinge, die lokal scoped dieser Funktion sind in diesen Stack-Frames gehen. Das heißt also, wenn ich etwas gesagt wie bar ist eine Funktion, Ich werde einfach einen Integer zu deklarieren und dann wieder einen Zeiger auf diese integer. Woher kommt also y leben? [Schüler] y lebt in bar. >> [Bowden] Yeah. Irgendwo in diesem kleinen Platz der Erinnerung ist ein littler Platz, y in sich hat. Wenn ich & Return y, ich einen Zeiger auf diesem kleinen Block des Speichers. Aber dann, wenn eine Funktion zurück, wird seinen Stack-Frame aus dem Stapel geholt. Und das ist, warum es heißt Stack. Es ist wie der Stack Datenstruktur, wenn du weißt, was das ist. Oder auch wie ein Stapel von Tabletts ist immer das Beispiel, Haupt wird sich auf den Grund zu gehen, dann ist die erste Funktion rufen Sie wird oben auf, dass zu gehen, und Sie können nicht zurück zur Hauptseite, bis Sie wieder aus allen Funktionen, die genannt worden Das haben oben drauf gelegt. [Schüler] Also, wenn Sie haben wieder die & y, dieser Wert ist freibleibend ohne vorherige Ankündigung. Ja, es ist - >> [Schüler] Es könnte sein, überschrieben. >> Ja. Es ist völlig - Wenn Sie versuchen, und - Dies wäre auch ein int * bar sein, weil es wieder ist ein Zeiger, so seine Rückkehr Typ int *. Wenn Sie den Rückgabewert dieser Funktion zu verwenden, ist es nicht definiertes Verhalten weil diese Zeiger auf schlechtes Gedächtnis. >> [Schüler] Okay. So was, wenn, zum Beispiel, erklärte sie int * y = malloc (sizeof (int))? Das ist besser. Ja. [Schüler] Wir sprachen darüber, wie wenn wir die Dinge ziehen, um unseren Papierkorb sie nicht wirklich gelöscht, wir nur verlieren ihre Zeiger. Also in diesem Fall wissen wir eigentlich löschen Sie den Wert oder ist es immer noch da in Erinnerung? Zum größten Teil, es wird noch da sein. Aber lassen Sie uns sagen, dass wir gerade eine andere Funktion baz nennen. Baz wird seinen eigenen Stack Frame auf hier. Es wird zu überschreiben all dieses Zeug, und dann, wenn Sie später versuchen, und verwenden Sie den Zeiger, die Sie vor haben, es wird nicht auf den gleichen Wert sein. Es wird gerade geändert haben, weil Sie die Funktion baz genannt. [Schüler] Aber hatten wir nicht, würden wir immer noch 3? [Bowden] Aller Wahrscheinlichkeit nach würden Sie. Aber man kann nicht darauf verlassen. C sagt nur undefiniertem Verhalten. [Schüler] Oh, tut es. Okay. Also, wenn Sie einen Zeiger zurückgeben wollen, ist dies, wo malloc kommt in Gebrauch. Ich bin eigentlich schriftlich nur zurück malloc (3 * sizeof (int)). Wir werden über malloc mehr gehen in eine zweite, aber die Idee von malloc ist alle Ihre lokalen Variablen immer auf den Stapel. Alles, was ist malloced geht auf den Haufen, und es wird für immer und ewig auf dem Heap bis Sie ihn explizit freigibt. So bedeutet dies, dass, wenn Sie malloc etwas, es wird nach der Rückgabe der Funktion überleben. [Schüler] Wird es überleben, nachdem das Programm nicht mehr läuft? >> Nr. Okay, also wird es da sein, bis das Programm ist den ganzen Weg gemacht läuft. >> Ja. Wir können gehen über Details von dem, was passiert, wenn das Programm beendet wird. Möglicherweise müssen Sie mich daran erinnern, aber das ist eine separate Sache völlig. [Schüler] So malloc erzeugt einen Zeiger? >> Ja. Malloc - >> [Schüler] Ich denke, malloc bezeichnet einen Block des Speichers, dass ein Zeiger verwenden können. [Bowden] Ich möchte, dass die Darstellung wieder. >> [Schüler] So arbeitet diese Funktion, obwohl? [Schüler] Yeah, malloc bezeichnet einen Block von Speicher, die Sie verwenden können, und dann gibt die Adresse des ersten Blocks jener Speicher. [Bowden] Yeah. Also, wenn Sie malloc, Sie packte einige Speicherblock das ist derzeit in dem Haufen. Wenn der Haufen zu klein ist, dann ist die Heap gerade dabei, zu wachsen, und sie wächst in diese Richtung. Also sagen wir mal der Haufen ist zu klein. Dann wird es ein wenig wachsen und liefern einen Zeiger auf diesem Block, die gerade gewachsen. Wenn Sie free stuff, du machst mehr Platz im Heap, so dann später malloc aufrufen können wiederverwendet werden, dass der Speicher, die Sie zuvor befreit. Das Wichtigste über malloc und free ist, dass Sie die volle Kontrolle über die Lebensdauer dieser Speicherblöcke. Globale Variablen sind immer lebendig. Lokale Variablen sind lebendig in ihren Anwendungsbereich. Sobald Sie an einem geschweiften Klammer gehen, sind die lokalen Variablen Toten. Malloced Speicher ist lebendig, wenn man es am Leben zu sein möchten und dann freigegeben wird, wenn man es zu sagen, freigesetzt werden. Das sind eigentlich die einzigen 3 Arten von Speicher, wirklich. Es gibt eine automatische Speicherverwaltung, die der Stapel. Dinge geschehen automatisch für Sie. Wenn Sie int x sagen, wird Speicher für int x zugeordnet. Wenn x den Bereich verlässt, wird Speicher für x zurückgefordert. Dann gibt es noch dynamische Speicherverwaltung, das ist, was malloc ist, was ist, wenn Sie die Kontrolle haben. Sie dynamisch entscheiden, wenn der Speicher sollte und was nicht zugeordnet werden. Und dann gibt es statisch, das bedeutet nur, dass es für immer lebt, das ist, was globale Variablen sind. Sie sind einfach immer in Erinnerung. Haben Sie Fragen? [Schüler] können Sie einen Block nur mit geschweiften Klammern aber nicht mit zu haben, eine if-Anweisung oder eine while-Anweisung oder so etwas? Sie können eine Block nach einer Funktion zu definieren, aber das hat geschweiften Klammern zu. [Schüler] So kann man nicht einfach wie eine zufällige Paar von geschweiften Klammern haben in Ihrem Code , die lokale Variablen haben? >> Ja, das können Sie. Innerhalb von int bar könnten wir {int y = 3;}. Das soll hier genau richtig. Aber das ganz bestimmt den Anwendungsbereich der int y. Nach diesem zweiten geschweiften Klammer kann y nicht mehr verwendet werden. Sie haben fast nie tun, though. Kommen wir zurück zu dem, was geschieht, wenn ein Programm beendet ist, es ist eine Art Missverständnis / halbe Lüge, die wir geben, um nur die Dinge einfacher. Wir sagen euch, dass, wenn Sie Speicher Sie Zuweisung einige Brocken RAM für diese Variable. Aber du bist nicht wirklich direkt berühren RAM jemals in Ihren Programmen. Wenn Sie daran denken, wie ich zog - Und tatsächlich, wenn Sie durch in GDB gehen sehen Sie die gleiche Sache. Unabhängig davon, wie oft Sie Ihr Programm ausführen oder welches Programm Sie laufen, der Stapel wird immer zu starten - Sie immer zu Variablen rund Adressbuch oxbffff etwas zu sehen. Es ist in der Regel irgendwo in dieser Region. Aber wie können 2 Programme möglicherweise haben Zeiger auf denselben Speicher? [Schüler] Es gibt einige willkürliche Bezeichnung, wo oxbfff soll auf der RAM sein das kann tatsächlich an verschiedenen Orten je nachdem, wann die Funktion aufgerufen wurde sein. Yeah. Der Begriff ist virtueller Speicher. Die Idee ist, dass jeder einzelne Prozess, jedes einzelne Programm, das auf Ihrem Computer ausgeführt wird hat seine eigene - nehmen wir an, 32 Bit - völlig unabhängig Adressraum. Dies ist der Adressraum. Es hat seine eigene völlig unabhängig 4 Gigabyte zu bedienen. Also, wenn Sie 2 Programme gleichzeitig laufen, sieht dieses Programm 4 Gigabyte an sich selbst, Dieses Programm sieht 4 Gigabyte an sich selbst, und es ist unmöglich, dass dieses Programm Dereferenzierung eines Zeigers und am Ende mit dem Gedächtnis von diesem Programm. Und was virtueller Speicher ist eine Abbildung von einem Prozesse Adressraum die tatsächlichen Dinge auf RAM. So ist es bis zu Ihrem Betriebssystem zu wissen, dass, hey, wenn dieser Kerl dereferences Zeiger oxbfff, das wirklich bedeutet, dass er will RAM byte 1000, während, wenn dieses Programm dereferences oxbfff er wirklich will RAM Byte 10000. Sie können beliebig weit voneinander entfernt sein. Dies gilt sogar der Dinge in einem einzigen Verfahren Adressraum. So wie es sieht alle 4 Gigabyte an sich selbst, aber sagen wir mal - [Schüler] Hat jeden einzelnen Prozess - Angenommen, Sie haben einen Computer mit nur 4 Gigabyte RAM. Hat jeder einzelne Prozess sehen, die ganzen 4 Gigabyte? >> Ja. Aber die 4 Gigabyte er sieht, ist eine Lüge. Es ist einfach es denkt, es hat alle diese Erinnerung, weil sie nicht wissen, alle anderen Verfahren existiert. Es wird nur so viel Speicher, wie es braucht eigentlich. Das Betriebssystem wird nicht RAM zu diesem Prozess geben wenn es nicht mit jedem Speicher in der gesamten Region. Es wird nicht, um ihm Speicher für diese Region. Aber die Idee ist, dass - ich bin versucht zu denken - ich kann nicht von einer Analogie zu denken. Analogien sind hart. Eines der Themen des virtuellen Speichers oder eines der Dinge, es ist die Lösung ist, dass Prozesse sollen völlig unbekannt voneinander. Und so können Sie schreiben ein Programm, dass nur dereferences keine Zeiger, wie einfach ein Programm schreiben, * (ox1234) sagt, und dass die Dereferenzierung Speicheradresse 1234. Aber es ist bis auf das Betriebssystem dann übersetzen, was 1234 bedeutet. Also, wenn 1234 zufällig eine gültige Speicheradresse für diesen Prozess, wie es auf dem Stack oder etwas ist, dann wird dies den Wert von dieser Speicheradresse soweit das Verfahren kennt. Aber wenn 1234 ist keine gültige Adresse, wie es an Land passiert in einigen Stückchen Erinnerung hier die über dem Stapel ist und darüber hinaus dem Heap und Sie haben nicht wirklich genutzt, dass dann, wenn Sie Dinge wie segfaults bekommen ist weil Sie berühren Erinnerung, die Sie sollten sich nicht berühren. Dies gilt auch - Ein 32-Bit-System, bedeutet 32 ​​Bit haben Sie 32 Bit, um einen Speicher-Adresse zu definieren. Es ist, warum Zeiger 8 Byte sind, weil 32 Bit 8 Byte sind - oder 4 Bytes. Zeiger sind 4 Byte. Also, wenn Sie einen Zeiger wie oxbfffff sehen, ist, dass - Innerhalb einer bestimmten Programm können Sie einfach konstruieren beliebige Zeiger, überall von ox0 zu Ochs 8 f's - ffffffff. [Schüler] Hast du nicht sagen, dass sie 4 Byte? >> Ja. [Schüler] Dann wird jedes Byte haben - >> [Bowden] Hexadezimal. Hexadezimal - 5, 6, 7, 8. So Zeigern, du wirst immer in hexadezimaler sehen. Es ist nur, wie wir Hinweise zu klassifizieren. Alle 2 Ziffern der hexadezimalen beträgt 1 Byte. So es geht um 8 Hexadezimalziffern für 4 Bytes. Also jedes einzelnen Zeiger auf einem 32-Bit-System wird 4 Bytes, was bedeutet, dass in Ihrem Prozess können Sie eine beliebige 4 Byte zu konstruieren und einen Zeiger aus ihm heraus, was bedeutet, dass, soweit es bekannt ist, kann ein gesamtes 2 an die 32 Bytes zu adressieren. Auch wenn es nicht wirklich Zugang zu, dass Auch wenn Ihr Computer nur über 512 Megabyte, denkt er es hat so viel Speicher. Und das Betriebssystem ist schlau genug, dass es nur zuordnen, was Sie wirklich brauchen. Es ist nicht einfach, oh, ein neues Verfahren: 4 Konzerte. Yeah. >> [Schüler] Was bedeutet der Ochse das? Warum schreiben Sie das? Es ist nur das Symbol für hexadezimal. Wenn Sie eine Zahl Start sehen, mit Ochsen, sind die aufeinanderfolgenden Dinge hexadezimal. [Schüler] Sie wurden über das, was geschieht, wenn ein Programm beendet erklären. >> Ja. Was geschieht, wenn ein Programm endet, ist das Betriebssystem nur löscht die Zuordnungen, dass es für diese Adressen, und das ist es. Das Betriebssystem kann jetzt nur noch geben, dass der Speicher zu einem anderen Programm zu verwenden. [Schüler] Okay. Also, wenn Sie etwas zuzuweisen auf dem Heap oder den Stapel oder globale Variablen oder nichts, sie alle nur so schnell wie das Programm beendet verschwinden da das Betriebssystem ist nun frei, um diesen Speicher zu einem anderen Prozeß geben. [Schüler] Auch wenn es wohl noch Werte geschrieben? >> Ja. Die Werte sind wahrscheinlich immer noch da. Es ist einfach, es wird schwierig sein, zu ihnen zu bekommen. Es ist viel schwieriger, sie zu bekommen, als es auf einer gelöschten Datei zu erhalten, ist weil die gelöschte Datei Art sitzt dort für eine lange Zeit und die Festplatte ist viel größer. Es wird also auf verschiedene Teile des Speichers überschreiben , bevor es geschieht, um den Block arbeiten, dass diese Datei an früher zu überschreiben. Aber Hauptspeicher, RAM, radeln Sie durch viel schneller, also wird es sehr schnell überschrieben. Fragen zum Thema oder etwas anderes? [Schüler] Ich habe Fragen zu einem anderen Thema. >> Okay. Hat jemand Fragen zu diesem Thema? Okay. Anderes Thema. >> [Schüler] Okay. Ich ging durch einige der Praxis-Tests gehen, und einer von ihnen wurde über die sizeof reden und der Wert, den es gibt oder verschieden Variablentypen. >> Ja. Und sie sagte, dass sowohl int und long sowohl Rückkehr 4, so dass sie beide 4 Byte lang sind. Gibt es einen Unterschied zwischen einem int und einem langen, oder ist es die gleiche Sache? Ja, es gibt einen Unterschied. Der C-Standard - Ich bin wahrscheinlich zu vermasseln. Der C-Standard ist wie, was C, ist die offizielle Dokumentation von C. Dies ist, was es sagt. So ist die C-Standard sagt nur, dass ein char wird für immer und immer 1 Byte sein. Alles danach - eine kurze ist immer genau so größer ist als oder gleich einem Zeichen definiert. Das könnte streng größer als, aber nicht positiv. Ein int ist ebenso größer als oder gleich einem kurzen definiert. Und eine lange ist ebenso größer als oder gleich einem int definiert. Und einer langen langen größer oder gleich einer Länge. Also das einzige, was die C-Standard definiert die relative Anordnung von allem. Die tatsächliche Größe des Speichers, dass die Dinge nehmen ist in der Regel bis zur Umsetzung, aber es ist ziemlich gut an diesem Punkt definiert. >> [Schüler] Okay. So Shorts sind fast immer zu 2 Bytes sein. Ints sind fast immer zu 4 Bytes. Lange longs sind fast immer zu 8 Bytes sein. Und sehnt, es hängt davon ab, ob Sie mit einem 32-Bit-oder eine 64-Bit-System. So lange wird auf die Art des Systems entsprechen. Wenn Sie eine 32-Bit-System sind wie die Appliance, es geht um 4 Bytes. Wenn Sie eine 64-Bit sind wie eine Menge von aktuellen Computern, es geht um 8 Bytes sein. Ints sind fast immer 4 Bytes an dieser Stelle. Lange longs sind fast immer 8 Byte. In der Vergangenheit verwendeten ints um nur 2 Bytes sein. Aber beachten Sie, dass diese vollständig erfüllt all diese Beziehungen größer und gleich. So lange wird tadellos darf die gleiche Größe wie eine ganze Zahl sein, und es auch erlaubt, die gleiche Größe wie eine lange lang sein. Und es passiert einfach so zu sein, dass in 99,999% der Systeme, es wird gleich entweder ein int oder ein long long. Es hängt nur von 32-Bit-oder 64-Bit. >> [Schüler] Okay. In Schwimmern, wie ist das Komma in Bezug auf Bits bezeichnet? Wie als binäre? >> Ja. Sie brauchen nicht zu, dass für CS50 kennen. Sie haben nicht einmal erfahren, dass in 61. Sie wissen nicht, dass wirklich lernen in einem Kurs. Es ist nur eine Darstellung. Ich vergesse die genauen Bit Kleingärten. Die Idee der Gleitkomma ist, dass man eine bestimmte Anzahl von Bits zur Darstellung zuordnen - Grundsätzlich ist alles, was in der wissenschaftlichen Schreibweise. So ordnen sie eine bestimmte Anzahl von Bits, um die Zahl selbst, wie 1,2345 zu repräsentieren. Ich kann nie eine Zahl mit mehr Stellen als 5. Dann haben Sie auch Zuweisung einer bestimmten Anzahl von Bits, so dass es wie sein tendiert Sie können nur bis zu einer bestimmten Anzahl, wie das ist der größte Exponent man haben kann, und man kann nur hinunter bis zu einem gewissen Exponenten wie das ist der kleinste Exponent man haben kann. Ich erinnere mich nicht die genaue Art und Weise Bits alle diese Werte zugeordnet sind, aber eine gewisse Anzahl von Bits werden auf 1,2345 gewidmet eine weitere bestimmte Anzahl von Bits dem Exponenten gewidmet und es ist nur möglich, ein Exponent einer bestimmten Größe repräsentieren. [Schüler] Und ein double? Ist das wie ein extra langer float? >> Ja. Es ist das gleiche wie ein Schwimmer, außer jetzt bist du mit 8 Bytes statt 4 Bytes. Sie werden nun in der Lage zu 9 Ziffern oder 10 Ziffern verwenden, und dies wird in der Lage sein zu gehen bis zu 300 statt 100. >> [Schüler] Okay. Und schwimmt auch 4 Byte. >> Ja. Nun, wieder, es hängt wahrscheinlich insgesamt auf generelle Umsetzung, sondern schwebt 4 Bytes sind, sind Doppel-8. Doubles sind doppelt, weil sie doppelt so groß wie Schwimmer sind genannt. [Schüler] Okay. Und gibt es doppelte verdoppelt? >> Es gibt nicht. Ich denke - >> [student] Wie lange sehnt? >> Ja. Ich glaube nicht. Ja. [Schüler] Auf der letztjährigen Test gab es eine Frage der Hauptfunktion mit den Teil des Programms sein. Die Antwort war, dass es nicht Teil des Programms sein. In welcher Situation? Das ist, was ich sah. [Bowden] Es scheint - >> [Schüler] Welche Situation? Haben Sie das Problem? >> [Schüler] Ja, ich kann auf jeden Fall nach oben ziehen. Es muss nicht sein, technisch, aber im Grunde ist es sein wird. [Schüler] Ich sah ein auf einem anderen Jahr. Es war wie Richtig oder falsch: Eine gültige - >> Oh, a c-Datei.? . [Schüler] Jede c-Datei muss - [beide sprechen auf einmal - unverständlich] Okay. Also das ist getrennt. A. C-Datei muss nur Funktionen enthalten. Sie kompilieren eine Datei in Maschinen-Code, Binär-, was auch immer, ohne dass ausführbare noch. Eine gültige ausführbare muss eine Hauptfunktion. Sie können schreiben, 100 Funktionen in 1-Datei, aber keine wesentlichen und dann, dass sich kompilieren binär, dann schreiben Sie eine andere Datei, die nur Haupt aber es ruft eine Reihe von diesen Funktionen in dieser Binärdatei hier. Und so, wenn du machst die ausführbare Datei, das ist, was die Linke tut wird es kombiniert diese zwei binäre Dateien in eine ausführbare Datei. Also ein. C Datei muss nicht eine Hauptfunktion zu haben. Und auf große Code-Basen sehen Sie Tausende von. C Dateien und 1 Hauptdatei. Noch Fragen? [Schüler] Es war eine andere Frage. Er sagte zu machen ist ein Compiler. Richtig oder falsch? Und die Antwort war falsch, und ich verstand, warum es nicht wie Clang. Aber was wir nennen können, wenn sie es nicht ist? Machen, ist im Grunde nur - ich kann genau sehen, was sie es nennt. Aber es läuft einfach Befehle. Machen. Ich kann ziehen diese auf. Yeah. Oh, yeah. Achten Sie auch tut. Das sagt der Zweck der Marke Dienstprogramm zur automatischen Bestimmung welche Teile eines großen Programms neu kompiliert werden und geben die Befehle, um sie neu zu kompilieren. Sie können make-Dateien, die absolut riesig sind. Machen Sie befasst sich mit den Zeitstempeln von Dateien und, wie wir schon sagten, Sie kompilieren einzelne Dateien ab, und es ist nicht, bis Sie an den Linker dass sie zusammen in einem Programm zu schreiben. Also, wenn Sie 10 verschiedene Dateien und Sie eine Änderung vornehmen, um ein von ihnen, was dann make tun wird, ist nur kompilieren, dass 1 Datei und dann erneut linken alles zusammen. Aber es ist viel dümmer als die. Es ist an Ihnen, vollständig zu definieren, dass das ist, was sie tun sollte. Es hat standardmäßig die Möglichkeit, diese Zeitstempel Zeug zu erkennen, aber Sie können eine Make-Datei nichts zu tun. Sie können eine Datei machen so. Dass, wenn Sie es nur CDs in ein anderes Verzeichnis eingeben Ich war frustriert, weil ich tack alles in meiner Appliance und dann habe ich die PDF aus dem Mac. Also habe ich Finder gehen und ich kann nicht gehen, eine Verbindung zum Server, und der Server ich eine Verbindung zu ist mein Appliance, und dann öffne ich die PDF Das geht von LaTeX erstellt. Aber ich war frustriert, weil jedes einzelne Mal, wenn ich brauchte, um das PDF zu aktualisieren, Ich musste es in ein bestimmtes Verzeichnis, dass es Zugriff auf kopieren und es wurde immer ärgerlich. Anstatt also schrieb ich eine Make-Datei, die Sie haben, zu definieren, wie sie die Dinge macht. Wie Sie in machen dies PDF LaTeX. Genau wie jede andere Marke Datei - oder ich glaube, Sie haben nicht die Make-Dateien gesehen, aber wir haben in der Appliance eine globale Marke Datei, die nur sagt, Wenn Sie die Erstellung einer C-Datei, verwenden Clang. Und hier in meinem Make-Datei, die ich mache, sage ich, Diese Datei, die Sie gehen zu wollen, um mit PDF LaTeX kompilieren. Und so ist es PDF LaTeX, die immer wieder compiling ist. Machen Sie nicht kompilieren. Es ist einfach laufen diese Befehle in der Reihenfolge I angegeben. So ist es PDF LaTeX läuft, kopiert es in das Verzeichnis Ich will es kopiert werden, es CDs in das Verzeichnis und tut andere Dinge, aber alles was man tut, ist zu erkennen, wenn eine Datei geändert wird, und wenn es ändert, dann wird die Befehle, die sie ausführen soll ist wenn die Datei ändert. >> [Schüler] Okay. Ich weiß nicht, wo die globale Marke-Dateien sind für mich, check it out. Weitere Fragen? Alles aus den vergangenen Quiz? Alle Zeiger Dinge? Es gibt subtile Dinge mit Zeigern wie - Ich bin nicht in der Lage sein, eine Quizfrage auf sie zu finden - aber wie diese Art der Sache. Stellen Sie sicher, Sie verstehen, dass, wenn ich sage int * x * y - Dies ist nicht gerade hier nichts, denke ich. Aber wie * x * y, das sind 2 Variablen, die auf dem Stapel sind. Wenn ich sage, x = malloc (sizeof (int)), ist x immer noch eine Variable auf dem Stack, malloc gibt einige Blocks über in den Haufen, und wir mit x-Punkt auf dem Heap. So etwas auf den Stapel Punkte auf dem Heap. Wenn Sie malloc nichts, du unweigerlich Speicherung innerhalb eines Zeigers. So dass Zeiger auf dem Stack ist die malloced Block in dem Haufen. Eine Menge Leute verwirren und sagen int * x = malloc, x auf dem Heap. Nein, was x, um Punkte auf dem Heap. x selbst ist auf dem Stack, es sei denn, aus welchem ​​Grund Sie x eine globale Variable haben sein, In diesem Fall kommt es, um in einem anderen Bereich des Speichers zu sein. So Verfolgung sind diese box Pfeil und Diagrammen ziemlich häufig für das Quiz. Oder, wenn es nicht von Quizfragen 0, wird es auf quiz 1 sein. Sie sollten wissen, alle diese, die Schritte bei der Erstellung da musste man Fragen über die zu beantworten. Ja. [Schüler] Könnten wir über diese Schritte zu gehen - >> Sure. Vor Schritte und Zusammenstellung haben wir Vorverarbeitung, Erstellung, Montage und Vernetzung. Vorverarbeitung. Was bedeutet das? Es ist der einfachste Schritt in - na ja, nicht wie - das bedeutet nicht, es sollte klar sein, aber es ist der einfachste Schritt. You guys umsetzen konnte es selber. Yeah. [Schüler] Nehmen Sie, was Sie in Ihrem enthält wie diese und kopiert und dann auch definiert. Es sieht für Dinge wie # include und # define, und es nur Kopien und Pasten, was die eigentlich bedeuten. Also, wenn Sie sagen, # include cs50.h wird der Präprozessor Kopieren und Einfügen cs50.h in dieser Linie. Wenn Sie sagen, # define x bis 4 sein, geht der Präprozessor durch das gesamte Programm und ersetzt alle Instanzen von x mit 4. So der Präprozessor dauert eine gültige C-Datei und gibt eine gültige C-Datei wo die Dinge wurden kopiert und eingefügt. So, jetzt kompilieren. Was bedeutet das? [Schüler] Es geht von C nach binär. [Bowden] Es muss nicht den ganzen Weg gehen, um binäre. [Schüler] in Maschinencode dann? >> Es ist nicht Maschinencode. [Schüler] Assembly? >> Versammlung. Es geht um Assembly, bevor sie den ganzen Weg geht in C-Code, und die meisten Sprachen etwas tun. Wählen Sie einen beliebigen Hochsprache, und wenn du gehst, es zu kompilieren, es ist wahrscheinlich in Schritten zu kompilieren. Zuerst, es wird Python C kompilieren, dann wird es nach C nach Assembly zu kompilieren, und dann Assembly wird in binäre übersetzt werden. So kompilieren wird es von C auf Assembly bringen. Das Wort kompilieren bedeutet in der Regel bringt es von einer höheren Ebene auf ein niedrigeres Niveau Programmiersprache. Also das ist der einzige Schritt in der Zusammenstellung, wo Sie beginnen mit einem High-Level-Sprache und am Ende in einer Low-Level-Sprache, und das ist, warum der Schritt nennt kompilieren. [Schüler] Beim Übersetzen, sagen wir mal, dass du getan hast # include cs50.h. Will der Compiler kompilieren Sie das cs50.h, wie die Funktionen, die dort sind, und dass in Assembler-Code als auch übersetzen, oder wird es kopieren und fügen Sie etwas, das pre-Versammlung? cs50.h wird so gut wie nie enden in Assembly. Sachen wie Funktionsprototypen und die Dinge sind nur für Sie, vorsichtig zu sein. Es garantiert, dass der Compiler Dinge zu überprüfen, wie Sie den Aufruf von Funktionen sind mit den richtigen Return-Typen und die richtigen Argumente und solche Sachen. So cs50.h wird in die Datei vorverarbeitet werden, und dann, wenn es Kompilieren ist es ist im Grunde weg, nachdem es sicher, dass alles korrekt genannt macht geworfen. Aber die Funktionen in der CS50-Bibliothek definiert, die getrennt von cs50.h, die werden nicht separat kompiliert werden. Das wird tatsächlich kommen, die in der Verknüpfung Schritt, so dass wir, dass in einer Sekunde. Aber zuerst, was ist die Montage? [Schüler] Versammlung Binary? >> Ja. Montage. Wir nennen es nicht kompilieren, weil Montage ist so ziemlich eine reine Übersetzung von binären. Es gibt sehr wenig Logik geht von der Montage bis binär. Es ist genau wie Nachschlagen in einer Tabelle, oh, wir haben diese Anweisung; das entspricht binäre 01110. Und so werden die Dateien, die Montage in der Regel Ausgänge sind. O-Dateien. Und. O-Dateien sind das, was wir vor dem Ausspruch, wie eine Datei braucht nicht eine Hauptfunktion haben. Jede Datei kann bis zu einer. O-Datei kompiliert werden, solange es eine gültige C-Datei ist. Es kann bis zu. O kompiliert werden. Nun ist die Verknüpfung, was wirklich bringt einen Haufen. O-Dateien und bringt sie in eine ausführbare. Und so was linking tut, ist man der CS50-Bibliothek als. O file denken. Es ist eine bereits kompilierte Binärdatei. Und so, wenn Sie kompilieren Sie die Datei, Ihre hello.c, die GetString nennt, hello.c wird auf hello.o zusammengestellt, hello.o ist jetzt in binär. Es nutzt GetString, so muss es gehen über die cs50.o, und der Linker smooshes sie zusammen und kopiert GetString in diese Datei und kommt mit einer ausführbaren Datei, alle Funktionen er benötigt. So cs50.o ist nicht wirklich eine O-Datei, aber es ist nah genug, dass es keinen grundsätzlichen Unterschied. So Verknüpfung bringt nur ein paar Dateien zusammen die separat enthalten alle Funktionen, die ich brauche, um zu verwenden und erstellt die ausführbare Datei, die tatsächlich ausgeführt werden soll. Und damit ist auch das, was wir vor dem Ausspruch wo man 1000 haben. c Dateien, kompilieren Sie sie alle. o Dateien, was wahrscheinlich eine Weile dauern, dann ändern Sie 1. c-Datei. Sie brauchen nur, dass 1. C-Datei und dann relink alles neu kompilieren, verlinken alles wieder zusammen. [Schüler] Wenn wir die Verknüpfung schreiben wir LCS50? Yeah, so LCS50. Diese Flagge Signale an den Linker, dass Sie in dieser Bibliothek werden sollte Verlinkung nicht erkennbar. Haben Sie Fragen? Haben wir über binäre außer dass 5 Sekunden in der ersten Vorlesung gegangen? Ich glaube nicht. Sie sollten wissen, alle großen Os, dass wir gegangen sind vorbei, und Sie sollten in der Lage sein, wenn wir Ihnen gab eine Funktion, Sie sollten in der Lage sein zu sagen, es ist groß O, grob. Oder auch, ist groß O rau. Also, wenn Sie sehen, für Schleifen Schleife über die gleiche Anzahl von Dingen verschachtelt, wie int i, i > [Schüler] n Quadrat. >> Es tendenziell n Quadrat. Wenn Sie triple genistet haben, neigt sie dazu, n gewürfelt. So diese Art von Sache, die Sie sollten in der Lage sein, darauf hinzuweisen, sofort. Sie müssen Insertion Sort und Bubble sort kennen und Mergesort und alle von denen. Es ist einfacher zu verstehen, warum sie die n quadriert und n log n und all das sind weil ich glaube, es war an einem Quiz 1 Jahr, wo wir im Grunde gab dir eine Implementierung von bubble sort und sagte: "Was ist die Laufzeit dieser Funktion?" Also, wenn Sie es erkennen, wie bubble sort, dann kann man sofort sagen, n Quadrat. Aber wenn Sie schaut ihn an, die Sie nicht sogar brauchen, es bubble sort zu realisieren; Sie können nur sagen, dass dies tut dies und jenes. Dies ist n quadriert. [Schüler] Gibt es irgendwelche harten Beispiele, die Sie mit oben kommen kann, wie eine ähnliche Idee herauszufinden? Ich glaube nicht, dass wir Ihnen keine harten Beispiele. Der Bubble-Sort Sache ist ungefähr so ​​schwer, wie wir gehen, und sogar, dass, solange Sie verstehen, dass Sie über das Array iterieren für jedes Element in dem Array, welche sich auch etwas, n Squared sein. Es gibt allgemeine Fragen, wie hier haben wir - Oh. Erst neulich, Doug behauptete: "Ich habe einen Algorithmus, der ein Array sortieren kann erfunden "Von n Zahlen in O (log n) Zeit!" So wie wir wissen, das ist unmöglich? [Unverständlich Student Response] >> Ja. Zumindest, müssen Sie jedes Element im Array zu berühren, so ist es unmöglich, eine Reihe von sortieren - Wenn alles in unsortierter Reihenfolge, dann wirst du zu berühren alles im Array, so ist es unmöglich, sie in weniger als O n tun. [Schüler] Du hast uns gezeigt, dass beispielsweise der Lage, es zu tun in O n wenn Sie eine Menge Speicher. >> Ja. Und that's - ich vergesse, was that's - Ist es das Zählen Art? Hmm. Das ist eine ganze Zahl Sortieralgorithmus. Ich wurde für die speziellen Namen dafür suchen, dass ich konnte mich nicht erinnern, letzte Woche. Yeah. Dies sind die Arten von Arten, die Dinge in großen O von n erreichen können. Jedoch gibt es Beschränkungen, wie Sie können nur ganze Zahlen bis zu einer bestimmten Anzahl. Plus, wenn Sie versuchen, etwas that's sortieren - Wenn Ihr Array 012, -12, 151, 4 Millionen, dann, dass einzelne Element wird völlig ruinieren das gesamte Sortierung. Haben Sie Fragen? [Schüler] Wenn Sie eine rekursive Funktion und es macht die rekursive Aufrufe innerhalb einer return-Anweisung, das ist endrekursiv, und so wäre das nicht mehr Speicher während der Laufzeit oder es würde zumindest verwenden vergleichbaren Speicher als eine iterative Lösung? [Bowden] Ja. Es wäre wahrscheinlich etwas langsamer, aber nicht wirklich. Endrekursiv ist ziemlich gut. Betrachtet man die Stack-Frames, sagen wir mal haben wir wichtigsten und wir haben int bar (int x) oder so etwas. Dies ist nicht eine perfekte rekursive Funktion, sondern Rückkehr bar (x - 1). So offensichtlich ist dies fehlerhaft. Sie müssen base Fällen and stuff. Aber die Idee ist, dass diese endrekursiv ist, was bedeutet, wenn der Haupt-Anrufe bar es geht um seinen Stack-Frame zu bekommen ist. In diesem Stack-Frame es geht um ein kleines Block des Speichers werden das entspricht dem Argument x. Und so sagen wir wichtigsten passiert bar (100) zu nennen; So x wird beginnen als 100. Wenn der Compiler erkennt, dass dies ein Schwanz rekursive Funktion ist, dann, wenn bar macht seine rekursive Aufruf von bar, anstatt eine neue Stack-Frame, die denen der Stapel weitgehend wachsenden gestartet wird, schließlich wird es in den Haufen laufen und dann bekommst du segfaults weil das Gedächtnis beginnt kollidieren. So anstatt eigene Stack-Frame, kann es zu realisieren, hey, ich habe nie wirklich brauchen, um wieder zu diesem Stack-Frame, so dass anstelle Ich werde einfach ersetzen Sie dieses Argument mit 99 und starten Sie dann bar alles vorbei. Und dann wird es wieder tun, und es wird Return bar erreichen (x - 1), und anstatt einen neuen Stack-Frame, wird es nur ersetzen die aktuelle Auseinandersetzung mit 98 und springen dann zurück an den Anfang der bar. Diese Operationen ersetzt, dass ein Wert auf dem Stack und springt zurück an den Anfang, sind ziemlich effizient. Also nicht nur das ist das gleiche Speichernutzung als separate Funktion, die iterativen weil du nur mit 1 Stack-Frame, aber du bist nicht leiden die Schattenseiten der mit den Funktionen aufrufen. Funktionen aufrufen kann etwas teuer, weil es alle diese Einstellung zu tun hat und Abbau und all dieses Zeug. Also das Endrekursion ist gut. [Schüler] Warum dauert es keine neuen Schritte? Weil es erkennt, dass es nicht nötig ist. Der Aufruf von bar ist nur wieder den rekursiven Aufruf. So ist es nicht erforderlich, dass Sie den Rückgabewert zu tun. Ist es nur geht, um sofort zurückgeben. So ist es nur geht, um ihr eigenes Argument ersetzen und neu anzufangen. Und auch, wenn Sie nicht über den Schwanz rekursive Version, dann bekommst du alle diese Bars, in denen, wenn diese bar zurück es hat seinen Wert auf diesen einen zurück, dann ist das bar sofort wieder und es seinen Wert wieder auf dieser ein, dann ist es nur geht, um sofort wieder und kehren ihren Wert auf dieses ein. Sie sind also spart dies knallen all diese Dinge vom Stapel da der Rückgabewert wird nur gehen bestanden den ganzen Weg zurück werden sowieso. Also warum nicht einfach ersetzen unsere Auseinandersetzung mit der aktualisierten Argument und von vorne anfangen? Wenn die Funktion nicht endrekursiv, wenn Sie etwas tun - [Schüler], wenn bar (x + 1). >> Ja. Also, wenn Sie es in Zustand, dann machst du etwas mit dem Rückgabewert. Oder auch wenn Sie gerade tun return 2 * bar (x - 1). So, jetzt bar (x - 1) muss zurückgeben, damit es 2 mal diesen Wert zu berechnen, so jetzt ist es nicht braucht einen eigenen separaten Stack-Frame, und jetzt, egal wie sehr man es versucht, Sie gehen zu müssen, um - Dies ist nicht endrekursiv. [Schüler] Würde ich versuchen, eine Rekursion zu bringen für eine Endrekursion zielen - [Bowden] In einer idealen Welt, aber in CS50 Sie nicht zu haben. Um Endrekursion bekommen, in der Regel richten Sie ein zusätzliches Argument wo Balken int x in y zu nehmen und y entspricht der ultimative, was Sie wollen, um zurückzukehren. So dann wirst du zurückkehren bar (x - 1), 2 * y. Also das ist nur ein High-Level wie Sie die Dinge zu sein endrekursiv verwandeln. Aber das zusätzliche Argument - Und dann am Ende, wenn Sie Ihre Basis Fall erreichen, man muss nur wieder y weil du angesammelt die ganze Zeit die Rückkehr Wert, den Sie wollen. Sie Art getan haben, es iterativ aber mit rekursiven Aufrufen. Haben Sie Fragen? [Schüler] Vielleicht über Pointer-Arithmetik, wie bei der Verwendung von Strings. >> Sure. Pointer-Arithmetik. Bei der Verwendung von Strings ist es einfach, weil Strings char Sterne sind, Zeichen sind für immer und ewig ein einzelnes Byte, und so Zeigerarithmetik ist das Equivalent zum regulären Arithmetik, wenn Sie mit Strings zu tun haben. Sagen wir einfach, char * s = "Hallo". So haben wir einen Block im Speicher. Es muss 6 Byte denn man braucht immer die Null-Terminator. Und char * s wird zu Beginn dieser Anordnung zeigen. So s zeigt es. Nun, dies ist im Grunde wie jedes Array funktioniert, unabhängig davon, ob es eine Rückkehr von malloc oder ob es auf dem Stapel. Jede Array ist im Grunde ein Zeiger auf den Anfang des Arrays, und dann alle Array-Betrieb, jede Indizierung ist nur los in diesem Array ein bestimmter Offset. Also, wenn ich so etwas wie s [3] zu sagen, dies ist s los und Zählen 3 Zeichen in. So s [3], haben wir 0, 1, 2, 3, so s [3] wird zu dieser l beziehen. [Schüler] Und wir könnten den gleichen Wert, indem Sie s + 3 und dann Klammern Stern zu erreichen? Ja. Dies ist gleichbedeutend mit * (n + 3); und das ist immer und immer gleich, egal was du tust. Sie müssen nie die Klammer-Syntax verwenden. Sie können immer die * (s + 3) Syntax. Menschen neigen dazu, die Klammer-Syntax möchten, though. [Schüler] Also alle Arrays sind eigentlich nur Zeiger. Es ist eine leichte Unterscheidung, wenn ich sage, int x [4]; >> [Schüler] Heißt das schaffen die Erinnerung? [Bowden] Das wird 4 ints auf dem Stack zu schaffen, so 16 Bytes insgesamt. Es wird zu 16 Bytes auf dem Stack zu schaffen. x ist nicht irgendwo gespeichert. Es ist nur ein Symbol mit Bezug auf den Start der Sache. Weil Sie erklärte das Array innerhalb dieser Funktion was der Compiler zu tun ist, ersetzen Sie einfach alle Instanzen der Variablen x mit, wo es passiert ist zu wählen, um diese 16 Byte setzen. Es kann das nicht mit char * s, da s ist eine tatsächliche Zeiger. Es ist frei, um dann auf andere Dinge zu zeigen. x eine Konstante ist. Sie können es nicht haben Punkt zu einem anderen Array. >> [Schüler] Okay. Aber diese Idee, diese Indizierung ist die gleiche, unabhängig davon, ob es sich um eine traditionelle Array ist oder wenn ein Zeiger auf etwas oder wenn es ist ein Zeiger auf einen malloced Array. Und in der Tat ist es so entspricht, dass also auch die gleiche Sache. Es ist eigentlich nur übersetzt, was drin ist in der Halterung und was von den Klammern links, fügt sie zusammen, und dereferenziert. Also das ist genauso gültig wie * (s + 3) oder s [3]. [Schüler] Kann man Zeigern auf 2-dimensionale Arrays? Es ist schwieriger. Traditionell Nr. Ein 2-dimensionales Array ist nur ein 1-dimensionales Array mit einigen bequeme Syntax weil wenn ich sage, int x [3] [3], das ist wirklich nur ein Array mit 9 Werten. Und so, wenn ich Index, der Compiler weiß, was ich meine. Wenn ich sage, x [1] [2], weiß es Ich will in die zweite Reihe zu gehen, also wird es die ersten 3 überspringen, und dann will die zweite Sache, dass, so werde diese eins zu bekommen ist. Es ist aber immer noch nur ein eindimensionales Array. Und so, wenn ich wollte, um einen Zeiger auf dieses Array zuweisen, Ich würde sagen, int * p = x; Der Typ von x ist gerade - Es ist hart zu sagen Art von x, da es nur ein Symbol, und es ist nicht eine tatsächliche variable, aber es ist nur ein int *. x ist nur ein Zeiger auf den Anfang davon. >> [Schüler] Okay. Und so werde ich nicht in der Lage sein [1] [2] zugreifen. Ich denke, es ist eine spezielle Syntax zur Deklaration eines Zeigers, etwas Lächerliches wie int (* p [-. etwas absolut lächerlich ich gar nicht wissen. Aber es gibt eine Syntax für die Deklaration Zeigern wie mit Klammern und Dinge. Es kann auch nicht tun lassen. Ich konnte Rückblick auf etwas, das mir sagen würde, die Wahrheit. Ich werde für später zu sehen, ob es eine Syntax für Punkt. Aber du wirst nie sehen. Und auch die Syntax ist so archaisch, dass, wenn Sie es verwenden, Menschen verwirrt werden. Mehrdimensionale Arrays sind ziemlich selten, wie es ist. Sie ziemlich viel - Nun, wenn du Matrix Dinge sind es nicht selten zu sein, aber in C Sie selten gehen zu mit mehrdimensionalen Arrays. Yeah. >> [Schüler] Angenommen, Sie haben eine wirklich lange Reihe. So im virtuellen Speicher scheint es zu sein alle aufeinander, wie die Elemente direkt neben einander, aber in dem physikalischen Speicher, wäre es möglich, daß zur aufgeteilt werden? >> Ja. Wie virtuellen Speicher funktioniert, ist es nur trennt - Die Einheit der Aufteilung ist eine Seite, die zu 4 Kilobyte werden neigt, und so, wenn ein Prozess sagt, hey, ich möchte diesen Speicher verwenden, das Betriebssystem wird ordnen es 4 Kilobyte für den kleinen Block des Speichers. Auch wenn Sie nur einen einzigen kleinen Byte im gesamten Block des Gedächtnisses, das Betriebssystem werde es die vollen 4 Kilobyte. Also, was bedeutet, ich hätte - sagen wir mal das ist mein Stack. Dieser Stapel getrennt werden konnten. Mein Stack konnte Megabyte und Megabyte betragen. Mein Stack kann enorm sein. Aber der Stapel selbst muss in einzelne Seiten aufgeteilt werden, die, wenn wir hier sehen wir sagen, das ist unser RAM, wenn ich 2 Gigabyte RAM haben, ist dies der tatsächlichen Adresse 0 wie die 0. Byte meinem RAM, und dies ist 2 Gigabyte den ganzen Weg hier unten. So dass diese Seite könnte dieser Block hier entsprechen. Diese Seite könnte dieser Block hier entsprechen. Dies könnte man auf diese eine hier entsprechen. So das Betriebssystem ist kostenlos auf den physikalischen Speicher zuweisen jedem einzelnen Seite beliebig. Und das bedeutet, dass, wenn diese Grenze passiert, um ein Array überspannen, ein Array geschieht dies links und rechts von dieser Reihenfolge von einer Seite, dann das Array wird im physischen Speicher aufgeteilt werden. Und dann, wenn Sie das Programm beenden, wenn der Prozess beendet ist, diese Zuordnungen zu löschen und dann ist es frei, diese kleinen Blöcke für andere Dinge verwenden. Noch Fragen? [Schüler] Die Zeigerarithmetik. >> Oh yeah. Streicher waren leichter, aber wenn man so etwas wie ints, Also zurück zu int x [4]; Ob dies ist eine Matrix oder ob es sich um einen Zeiger auf einen malloced Bereich von 4 Ganzzahlen, es wird gleich behandelt werden. [Schüler] So Arrays sind auf dem Heap? [Bowden] Arrays sind nicht auf dem Heap. >> [Schüler] Oh. [Bowden] Diese Art der Anordnung neigt dazu, auf dem Stapel es sei denn, Sie erklärt es - ignoriere globalen Variablen. Verwenden Sie keine globalen Variablen. Innerhalb einer Funktion, die ich sagen, int x [4]; Es wird eine 4-Ganzzahl-Block auf dem Stapel für diese Reihe zu erzeugen. Aber das malloc (4 * sizeof (int)); wird auf dem Heap zu gehen. Aber nach diesem Punkt kann ich x und p in so ziemlich die gleiche Weise verwenden, andere als die Ausnahmen habe ich schon sagte über Sie p kann zuweisen. Technisch sind die Größen etwas anders, aber das ist völlig irrelevant. Man kann nie wirklich nutzen ihre Größen. Die p, ich könnte sagen p [3] = 2 oder x [3] = 2; Sie können sie in genau der gleichen Weise zu verwenden. So Zeigerarithmetik jetzt - Ja. [Schüler] Haben Sie nicht p * tun, wenn man die Klammern haben? Die Klammern sind eine implizite Dereferenzierung. >> Okay. Eigentlich auch das, was Sie mit dem Spruch können Sie mehrdimensionale Arrays mit Zeigern, was Sie tun können, ist so etwas wie, sagen wir, int ** pp = malloc (sizeof (int *) * 5); Ich werde nur schreiben alles zuerst. Ich wollte nicht, dass man. Okay. Was ich tat, hier ist - Das sollte pp [i] sein. Pp so ist ein Zeiger auf einen Zeiger. Sie mallocing pp auf ein Array von 5 int Sterne zeigen. So in Erinnerung haben Sie auf dem Stack S. Es wird auf ein Array von Blöcken, die alle 5 sind selbst Zeiger verweist. Und wenn ich dann malloc hier unten, malloc ich, dass jede dieser einzelnen Zeiger sollte zu einem separaten Block von 4 Bytes auf dem Heap verweisen. So deutet dies auf 4 Byte. Und das ein auf eine andere 4 Byte. Und sie alle verweisen auf ihre eigenen 4 Byte. Das gibt mir einen Weg zu tun multidimensionale Dinge. Ich könnte sagen, pp [3] [4], aber jetzt ist dies nicht das Gleiche wie mehrdimensionale Arrays weil mehrdimensionalen Arrays es übersetzt [3] [4] in einer einzigen in den x-Offset. Diese dereferences p, greift die dritte Index, dann dereferenziert, dass und Zugänge - 4 ungültig wäre - der zweite Index. Während, wenn wir hatten das int x [3] [4] vor, wie ein mehrdimensionales Array und wenn du Halterung verdoppeln, es ist wirklich nur ein einziges dereferenzieren, Sie folgen einem einzigen Zeiger und dann einen Offset das ist wirklich 2D Referenzen. Sie folgen 2 separate Zeiger. So auch technisch können Sie haben mehrdimensionalen Arrays wobei jede einzelne Anordnung ist Größen. Also ich denke, gezackte mehrdimensionalen Arrays ist, was es heißt da wirklich das erste, was könnte, etwas, das 10 Elemente hat zeigen, die zweite Sache könnte, etwas, das 100 Elemente hat zeigen. [Schüler] Gibt es eine Grenze für die Anzahl von Zeigern können Sie , die auf andere Zeiger? >> Nr. Sie können int ***** p. Zurück zur Zeigerarithmetik - >> [Schüler] Oh. >> Ja. [Schüler] Wenn ich int *** p und haben dann mache ich eine Dereferenzierung und ich sage p * ist gleich diesem Wert, ist es nur noch ein Niveau von Dereferenzierung tun? >> Ja. Also, wenn ich will die Sache, dass der letzte Zeiger gerade befindet zugreifen - Dann tun Sie *** p. >> Okay. Das ist also p Punkte 1 Block, weist auf einen weiteren Block, der auf einem anderen Block. Dann, wenn Sie tun * p = etwas anderes, dann sind Sie, dies zu ändern Bisher auf einen anderen Block zu zeigen. >> Okay. [Bowden] Und wenn diese wurden malloced, dann haben Sie jetzt Speicher verloren es sei denn, Sie zufällig verschiedene Referenzen von ihnen haben da kann man nicht zurück zu denen diejenigen, die Sie gerade weggeworfen. Pointer-Arithmetik. int x [4]; wird einen Bereich von 4 Ganzzahlen zuzuweisen wobei x wird auf den Anfang des Arrays weisen. Also, wenn ich so etwas sagen wie x [1], ich will es in die zweite Zahl im Array meine, was würden diese sein. Aber wirklich, das ist 4 Byte in das Array, da diese Zahl belegt 4 Bytes. So einem Offset von 1 wirklich bedeutet einem Offset von 1 Mal so groß, was die Art der Anordnung ist. Dies ist eine Anordnung von ganzen Zahlen, so dass es weiß bis 1-fachen Größe des int tun, wenn es zum Ausgleich will. Die andere Syntax. Beachten Sie, dass dies äquivalent zu * (x + 1) ist; Wenn ich sage, Zeiger + 1, was das wieder ist die Adresse, dass der Zeiger speichert plus 1 mal so groß wie der Art des Zeigers. Also, wenn x = OX100, dann x + 1 = ox104. Und Sie können diese missbrauchen und so etwas sagen wie char * c = (char *) x; und jetzt c wird die gleiche Adresse wie x. c sein wird gleich OX100, aber c + 1 wird um gleich ox101 Da Zeigerarithmetik hängt von der Art des Zeigers, dass Sie mit Zugabe. So c + 1, sieht es bei c, es ist ein Zeiger auf char, so es wird 1 mal Größe char hinzuzufügen, das wird immer 1 sein, so erhalten Sie 101, wohingegen, wenn I x, die auch noch 100 tun, wird x + 1 geht bis 104 sein. [Schüler] Kann man c + +, um den Zeiger um 1 voranzubringen? Ja, können Sie. Sie können das nicht mit x, weil x ist nur ein Symbol, es ist ein konstanter, man kann es nicht ändern x. Aber c geschieht nur ein Zeiger werden, deshalb c + + ist durchaus möglich und sie wird um 1 erhöht. Wenn c waren einfach ein int *, dann c + + würde 104 werden. + + Macht Zeigerarithmetik wie c + 1 wäre Zeigerarithmetik getan haben. Das ist eigentlich wie eine Menge Dinge wie merge sort - Anstatt die Erstellung von Kopien der Dinge, können Sie stattdessen passieren - Wie, wenn ich diese Hälfte des Array übergeben wollte - lasst uns etwas davon zu löschen. Sagen wir, ich wollte diese Seite des Arrays in eine Funktion übergeben. Was würde ich für diese Funktion übergeben? Wenn ich x passieren, bin ich vorbei diese Adresse. Aber ich will dieses bestimmte Adresse weitergeben. Also, was soll ich weiter? [Schüler] Pointer + 2? [Bowden] So x + 2. Ja. Das wird diese Adresse. Sie werden auch sehr häufig sehen es als x [2] und dann die Adresse dafür. So müssen Sie die Adresse zu nehmen, weil die Halterung ist eine implizite Dereferenzierung. x [2] bezieht sich auf den Wert, der in diesem Feld ist, und dann wollen Sie die Adresse dieser Box, so dass Sie sagen, & x [2]. Also das ist, wie etwas in Mergesort, wo Sie wollen die Hälfte der Liste etwas passieren Sie wirklich nur passieren & x [2], und jetzt so weit wie der rekursive Aufruf betrifft, my new Array beginnt dort. Last-Minute-Fragen. [Schüler] Wenn wir nicht setzen ein kaufmännisches oder - was heißt das? >> Star? [Schüler] Star. >> Technisch Dereferenzierungsoperator, aber - >> [Schüler] Dereference. Wenn wir nichts dagegen unternehmen, einen Stern oder ein kaufmännisches, was passiert, wenn ich nur sagen, y = x und x ist ein Zeiger? Was die Art der y? >> [Schüler] Ich werde einfach sagen, dass es Zeiger 2. Also, wenn Sie nur sagen, y = x, jetzt x und y auf die gleiche Sache. >> [Schüler] Zeigen Sie auf die gleiche Sache. Und wenn x ist eine int-Zeiger? >> Es würde sich beschweren, weil Sie nicht zuordnen können Zeigern. [Schüler] Okay. Beachten Sie, dass Zeiger, obwohl wir sie ziehen, wie Pfeile, wirklich alles, was sie store - int * x - wirklich alle x speichert ist so etwas wie OX100, welches wir zufällig als ein Hinweis auf den Block bei 100 gespeicherten Daten darstellen. Wenn ich also sage int * y = x; Ich bin nur das Kopieren OX100 in y, der wir gerade dabei, als y darstellen, auch deutete auf OX100. Und wenn ich sage int i = (int) x, dann i wird speichern, was den Wert der OX100 ist Innenseite, aber jetzt wird es als integer statt eines Zeigers interpretiert werden. Aber Sie müssen die Besetzung sonst wird es zu beschweren. [Schüler] Also meinst du zu werfen - Wird es zu Gießen int von x oder Gießen int y? [Bowden] Was? [Schüler] Okay. Nach diesen Klammern wird es zu einem x-oder ay dort sein? [Bowden] Entweder. x und y sind äquivalent. >> [Schüler] Okay. Weil sie beide Zeiger. >> Ja. [Schüler] Also wäre die hexadezimale 100 in ganzzahligen Form zu speichern? >> [Bowden] Yeah. Aber nicht der Wert dessen, was sie zeigt. [Bowden] Yeah. >> [Schüler] Also einfach die Adresse in ganzzahligen Form. Okay. [Bowden] Wollte man aus irgendeinem bizarren Grund, Sie könnten nur mit Zeigern umgehen und nie mit Zahlen umgehen und genau wie int * x = 0 sein. Dann wirst du wirklich verwirrt, wenn Zeigerarithmetik beginnt passiert. So die Zahlen, die sie speichern sind bedeutungslos. Es ist nur, wie Sie am Ende zu interpretieren. Also ich bin frei, OX100 aus einem int * kopieren und in ein int, und ich bin frei zuweisen - du bist wahrscheinlich auf für nicht Gießen erhalten schrie - Ich bin frei, so etwas wie (int *) ox1234 in dieser willkürlichen int * zuweisen. So ox123 ist genauso gültig eine Speicheradresse wie & y. & Y passiert etwas, das ziemlich viel ist ox123 zurückzukehren. [Schüler] Wäre das eine wirklich coole Art zu dezimal oder hexadezimal Form zu gehen, wie wenn Sie einen Zeiger haben und warf es als int? [Bowden] Sie können wirklich nur Drucken mit wie printf. Lassen Sie uns sagen, ich habe int y = 100. So printf (% d \ n - wie Sie bereits wissen sollten - drucken, die als integer,% x. Wir müssen nur druckt sie, als hexadezimal. So ein Zeiger nicht als hexadezimale gespeichert, und eine ganze Zahl ist nicht als Dezimalzahl gespeichert. Alles wird als binäre gespeichert. Es ist nur, dass wir auf Zeiger als hexadezimale zeigen, neigen weil wir denken, der Dinge in dieser 4-Byte-Blöcke, und Speicher-Adressen sind in der Regel vertraut. Wir sind wie, wenn es mit bf beginnt, dann ist es auf dem Stapel passiert. So ist es nur unsere Interpretation von Zeigern als hexadezimal. Okay. Irgendwelche letzten Fragen? Ich werde hier sein für ein wenig nach, wenn Sie etwas anderes haben. Und das ist das Ende davon. [Schüler] Yay! [Applaus] [CS50.TV]