Was sind die Hindernisse für das Verstehen von Zeigern und was kann man tun, um sie zu überwinden? [geschlossen]

  • Warum sind Zeiger für viele neue und sogar alte College-Studenten in C oder C ++ ein so führender Verwirrungsfaktor? Gibt es Werkzeuge oder Denkprozesse, mit deren Hilfe Sie die Funktionsweise von Zeigern auf Variablen, Funktionen und darüber hinaus verstehen konnten?

    Welche guten Vorgehensweisen können Sie tun, um jemanden mitzubringen? auf das Niveau von "Ah-hah, ich habe es", ohne dass sie sich in dem Gesamtkonzept festsetzen? Im Grunde bohren Sie ähnliche Szenarien.

    23 May 2010
    6 revs, 4 users 57%David McGraw
28 answers
  • Zeiger ist ein Konzept, das für viele zunächst verwirrend sein kann, insbesondere wenn Zeigerwerte herum kopiert werden und immer noch auf denselben Speicherblock verwiesen wird.

    Ich habe festgestellt, dass die beste Analogie darin besteht, den Zeiger als ein Stück Papier mit einer Hausadresse darauf zu betrachten, und den Speicherblock, auf den er sich bezieht, als das eigentliche Haus. Alle Arten von Operationen können auf einfache Weise erklärt werden.

    Ich habe unten Delphi-Code hinzugefügt und gegebenenfalls einige Kommentare. Ich habe mich für Delphi entschieden, da meine andere Hauptprogrammiersprache C # Dinge wie Speicherlecks nicht auf die gleiche Weise anzeigt.

    Wenn Sie nur das übergeordnete Konzept von Zeigern lernen möchten Dann sollten Sie die Teile mit der Bezeichnung "Speicherlayout" in der nachfolgenden Erläuterung ignorieren. Sie sollen Beispiele dafür geben, wie das Gedächtnis nach Operationen aussehen könnte, sie sind jedoch eher niedriger. Um jedoch genau zu erklären, wie Pufferüberläufe wirklich funktionieren, war es wichtig, dass ich diese Diagramme hinzugefügt habe Beispiel Speicher Layouts sind stark vereinfacht. Es gibt mehr Overhead und viele weitere Details, die Sie wissen müssten, wenn Sie mit Speicher auf niedriger Ebene umgehen müssen. Für die Erklärung von Gedächtnis und Zeigern ist dies jedoch genau genug.


    Nehmen wir das THouse an Die unten verwendete Klasse sieht wie folgt aus:

     type
        THouse = class
        private
            FName : array[0..9] of Char;
        public
            constructor Create(name: PChar);
        end;
     

    Wenn Sie das Hausobjekt initialisieren, erhält der Konstruktor den Namen in das private Feld FName kopiert. Es gibt einen Grund dafür, dass es als Array mit fester Größe definiert ist.

    Im Speicher wird mit der Hauszuweisung ein gewisser Aufwand verbunden sein. Ich werde das im Folgenden so veranschaulichen:

     --- [ttttNNNNNNNNNNN] --- 
     ^ ^ 
     | | 
     | + - das FName-Array 
     | 
     + - Overhead 
     

    Der Bereich "tttt" ist Overhead, normalerweise gibt es morDies gilt für verschiedene Arten von Laufzeiten und Sprachen, z. B. 8 oder 12 Byte. Es ist zwingend, dass die Werte, die in diesem Bereich gespeichert sind, niemals von etwas anderem als dem Speicherzuweiser oder den Kernsystemroutinen geändert werden. Andernfalls besteht die Gefahr, dass das Programm abstürzt.


    Speicher zuweisen

    Bringen Sie einen Unternehmer dazu, Ihr Haus zu bauen, und geben Sie die Adresse des Hauses an. Im Gegensatz zur realen Welt kann der Speicherzuweisung nicht mitgeteilt werden, wo sie zugewiesen werden soll. Sie findet jedoch einen geeigneten Platz mit genügend Platz und meldet die Adresse an den zugewiesenen Speicher.

    In Mit anderen Worten, der Unternehmer wählt die Stelle aus.

     THouse.Create('My house');
     

    Speicherlayout:

     --- [ttttNNNNNNNNNN] --- 
     1234Mein Haus 
     

    Bewahren Sie ein Variable mit der Adresse

    Schreiben Sie die Adresse in Ihr neues Haus auf ein Blatt Papier. Dieses Papier dient als Referenz für Ihr Haus. Ohne dieses Stück Papier sind Sie verloren und können das Haus nicht finden, es sei denn, Sie befinden sich bereits darin.

     var
        h: THouse;
    begin
        h := THouse.Create('My house');
        ...
     

    Speicherlayout:

     h 
     v 
     --- [ttttNNNNNNNNNN] --- 
     1234Mein Haus 
     

    Zeigerwert kopieren

    Schreiben Sie einfach die Adresse in ein neues Stück von Papier. Sie haben jetzt zwei Blätter, die Sie in dasselbe Haus bringen, nicht in zwei separate Häuser. Jeder Versuch, der Adresse aus einem Papier zu folgen und die Möbel in diesem Haus neu anzuordnen, lässt den Anschein erwecken, dass das andere Haus auf dieselbe Weise geändert wurde, es sei denn, Sie können explizit feststellen, dass es sich tatsächlich nur um ein Haus handelt .

    Hinweis: Dies ist normalerweise das Konzept, bei dem ich am meisten Probleme habe, Personen zu erklären. Zwei Zeiger bedeuten nicht zwei Objekte oder Speicherblöcke.

     var
        h1, h2: THouse;
    begin
        h1 := THouse.Create('My house');
        h2 := h1; // copies the address, not the house
        ...
     
     h1 
     v 
     --- [ttttNNNNNNNNNNN] --- 
     1234My house 
     ^ 
     h2 
     

    Befreiung derSpeicher

    Zerstöre das Haus. Sie können das Papier dann später für eine neue Adresse wiederverwenden, wenn Sie möchten, oder es löschen, um die Adresse des Hauses zu vergessen, die nicht mehr existiert.

     var
        h: THouse;
    begin
        h := THouse.Create('My house');
        ...
        h.Free;
        h := nil;
     

    Hier konstruiere ich zuerst das Haus und erhalte seine Adresse. Dann mache ich etwas mit dem Haus (benutze es, den ... Code, der dem Leser als Übung überlassen wurde), und dann gebe ich es frei. Zuletzt lösche ich die Adresse aus meiner Variablen.

    Speicherlayout:

     h & lt; - + 
     v + - vor dem freien 
     --- [ttttNNNNNNNNNN] --- | 
     1234Mein Haus & lt; - + 
     
     h (jetzt zeigt nirgendwohin) & lt; - + 
     + - nach dem freien 
     ---------------------- | (Beachten Sie, dass der Speicher möglicherweise immer noch 
     xx34Mein Haus & lt; - + enthält einige Daten.) 
     

    Dangling Hinweise

    Sie fordern Ihren Unternehmer auf, das Haus zu zerstören, vergessen jedoch, die Adresse von Ihrem Zettel zu löschen. Wenn Sie später das Papier betrachten, haben Sie vergessen, dass das Haus nicht mehr da ist, und es mit fehlgeschlagenen Ergebnissen besucht (siehe auch den Abschnitt über eine ungültige Referenz unten).

     var
        h: THouse;
    begin
        h := THouse.Create('My house');
        ...
        h.Free;
        ... // forgot to clear h here
        h.OpenFrontDoor; // will most likely fail
     

    Die Verwendung von h nach dem Aufruf von .Free könnte funktionieren, aber das ist reines Glück . Höchstwahrscheinlich wird es an einem Kundenplatz während eines kritischen Vorgangs fehlschlagen.

     h & lt; - + 
     v + - vor freiem Eintritt 
     - - [ttttNNNNNNNNNNN] --- | 
     1234Mein Haus & lt; - + 
     
     h & lt; - + 
     v + - nach freiem Eintritt 
     -------- -------------- | 
     xx34Mein Haus & lt; - + 
     

    Wie Sie sehen, zeigt h immer noch auf die Reste der Daten im Speicher, aber ist möglicherweise nicht vollständig, da die Verwendung dieser Daten möglicherweise fehlschlägt.


    < strong> Speicherverlust

    Sie haben das Stück Papier verloren und können das Haus nicht finden. Das Haus steht jedoch immer noch irgendwo und wenn Sie später ein neues Haus bauen möchten, können Sie diese Stelle nicht wiederverwenden.

     var
        h: THouse;
    begin
        h := THouse.Create('My house');
        h := THouse.Create('My house'); // uh-oh, what happened to our first house?
        ...
        h.Free;
        h := nil;
     

    Hier haben wir den Inhalt der Variablen h mit der Adresse eines neuen Hauses überschrieben, aber das alte steht noch ... irgendwo. Nach diesem Code gibt es keine Möglichkeit, dieses Haus zu erreichen, und es wird stehen bleiben. Mit anderen Worten, der zugewiesene Speicher bleibt so lange reserviert, bis die Anwendung geschlossen wird. Anschließend wird er vom Betriebssystem abgebrochen.

    Speicherlayout nach der ersten Zuweisung:

     h 
     v 
     --- [ttttNNNNNNNNNNN] --- 
     1234Mein Haus 
     

    Speicheranordnung nach Sekunde Zuordnung:

     h 
     v 
     --- [ttttNNNNNNNNNN] --- [ttttNNNNNNNNNN] 
     1234My Haus 5678My Haus 
     

    Eine häufigere Methode, um diese Methode zu erhalten, besteht darin, etwas zu vergessen, anstatt es wie oben beschrieben zu überschreiben. In Delphi-Ausdrücken geschieht dies mit der folgenden Methode:

     procedure OpenTheFrontDoorOfANewHouse;
    var
        h: THouse;
    begin
        h := THouse.Create('My house');
        h.OpenFrontDoor;
        // uh-oh, no .Free here, where does the address go?
    end;
     

    Nachdem diese Methode ausgeführt wurde, gibt es keinen Platz mehr In unseren Variablen ist zwar die Adresse des Hauses vorhanden, das Haus ist jedoch immer noch da.

    Speicherlayout:

     h & lt ; - + 
     v + - bevor der Zeiger verloren geht 
     --- [ttttNNNNNNNNNN] --- | 
     1234Mein Haus & lt; - + 
     
     h (jetzt zeigt nirgendwohin) & lt; - + 
     + - nach Verlust des Zeigers 
     --- [ttttNNNNNNNNNNN] --- | 
     1234Mein Haus & lt; - + 
     

    Wie Sie sehen, bleiben die alten Daten im Speicher erhalten und werden vom Speicherzuweiser nicht wiederverwendet. Der Zuweiser verfolgt, welche Speicherbereiche von verwendet wurden, und verwendet sie nicht erneut, wenn Sie ihn nicht freigeben.


    Speicher freigeben, aber einen (jetzt ungültigen) Verweis behalten

    Zerstöre das Haus, lösche ein Stück Papier, aber du hast auch ein anderes Blatt Papier mit der alten Adresse drauf, wenn du zur Adresse gehst Ich werde kein Haus finden, aber Sie könnten etwas finden, das den Ruinen eines Hauses ähnelt.

    Vielleicht finden Sie sogar ein Haus, aber es ist nicht das Haus, das Sie ursprünglich erhalten haben Die Adresse an und somit alle Versuche, sie so zu verwenden, als ob sie zu Ihnen gehört, können schrecklich ausfallen.

    Manchmal finden Sie sogar, dass eine benachbarte Adresse ein ziemlich großes Haus hat Darauf befinden sich drei Adressen (Main Street 1-3), und Ihre Adresse geht in die Mitte des Hauses. Jeder Versuch, diesen Teil des großen 3-Adressen-Hauses als ein einziges kleines Haus zu behandeln, kann auch schrecklich ausfallen.

     var
        h1, h2: THouse;
    begin
        h1 := THouse.Create('My house');
        h2 := h1; // copies the address, not the house
        ...
        h1.Free;
        h1 := nil;
        h2.OpenFrontDoor; // uh-oh, what happened to our house?
     
    < Hier wurde das Haus durch den Verweis in h1 abgerissen, und während h1 ebenfalls gelöscht wurde, hat h2 immer noch die alte, veraltete Adresse. Der Zugriff auf das Haus, das nicht mehr steht, funktioniert oder funktioniert möglicherweise nicht.

    Dies ist eine Variation des baumelnden Zeigers oben. Siehe Speicherlayout.


    Pufferüberlauf

    Sie bringen mehr Sachen in das Haus ein, als Sie möglicherweise hineinpassen, und strömen in das Haus oder den Hof der Nachbarn. Wenn der Besitzer dieses Nachbarhauses später nach Hause kommt, findet er alle möglichen Dinge, die er für seine eigenen hält.

    Aus diesem Grund habe ich mich für eine feste Größe entschieden Array. Nehmen Sie an, dass das zweite Haus, das wir zuweisen, aus irgendeinem Grund vor dem ersten im Speicher abgelegt wird. Mit anderen Worten, das zweite Haus hat eine niedrigere Adresse als das erste. Sie werden auch direkt nebeneinander zugewiesen.

    Daher dieser Code:

     var
        h1, h2: THouse;
    begin
        h1 := THouse.Create('My house');
        h2 := THouse.Create('My other house somewhere');
                             ^-----------------------^
                              longer than 10 characters
                             0123456789 <-- 10 characters
     

    Speicherlayout nach der ersten Zuweisung:

     h1 
     v 
     -------------- --------- [ttttNNNNNNNNNN] 
     5678 Mein Haus 
     

    Speicherlayout nach zweiter Zuweisung:

     h2 h1 
     vv 
     --- [ttttNNNNNNNNNN] - - [ttttNNNNNNNNNN] 
     1234Ihr anderes Haus irgendwo 
     ^ --- + - ^ 
     | 
     + - überschrieben 
     

    Die Ein Absturz ist meistens der Fall, wenn Sie wichtige Teile der gespeicherten Daten überschreiben, die nicht zufällig geändert werden sollten. Zum Beispiel ist es möglicherweise kein Problem, dass Teile des Namens des h1-Hauses geändert wurden, in Bezug auf den Absturz des Programms. Das Überschreiben des Overheads des - Objekts wird jedoch höchstwahrscheinlich abstürzen, wenn Sie dies tun Versuchen Sie, das defekte Objekt <<> zu verwenden, da Verknüpfungen überschrieben werden, die für andere Objekte im Objekt gespeichert werden.


    Verknüpfte Listen

    Wenn Sie einer Adresse auf einem Blatt Papier folgen, gelangen Sie zu einem Haus, und in diesem Haus befindet sich ein weiteres Blatt Papier mit Darauf eine neue Adresse für das nächste Haus in der Kette usw.

     var
        h1, h2: THouse;
    begin
        h1 := THouse.Create('Home');
        h2 := THouse.Create('Cabin');
        h1.NextHouse := h2;
     

    Hier erstellen wir eine Verbindung von unserem Haus zu unserer Kabine. Wir können der Kette folgen, bis ein Haus keine Referenz NextHouse hat, was bedeutet, dass es das letzte ist. Um alle unsere Häuser zu besuchen, könnten wir den folgenden Code verwenden:

     var
        h1, h2: THouse;
        h: THouse;
    begin
        h1 := THouse.Create('Home');
        h2 := THouse.Create('Cabin');
        h1.NextHouse := h2;
        ...
        h := h1;
        while h <> nil do
        begin
            h.LockAllDoors;
            h.CloseAllWindows;
            h := h.NextHouse;
        end;
     

    Speicherlayout (als Link wurde NextHouse hinzugefügt) in dem Objekt mit die vier LLLLs in der folgenden Abbildung):

     h1 h2 
     vv 
     --- [ttttNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL] 
     1234Home + 5678Cabin + 
     | ^ | 
     + -------- + * (keine Verknüpfung) 
     

    In Grundbegriffe, was ist eine Speicheradresse?

    Eine Speicheradresse ist im Grunde nur eine Zahl. Wenn Sie sich den Speicher als großes Byte-Array vorstellen, hat das allererste Byte die Adresse 0, das nächste die Adresse 1 und

    29 June 2010
    adinas
  • In meiner ersten Comp Sci-Klasse haben wir die folgende Übung durchgeführt. Zugegeben, dies war ein Hörsaal mit ungefähr 200 Studenten ...

    Professor schreibt an die Tafel: int john;

    John steht auf

    Professor schreibt: int *sally = &john;

    Sally steht auf und zeigt auf John

    Professor: int *bill = sally;

    Bill steht auf und zeigt auf John

    Professor: int sam;

    Sam steht auf

    Professor: bill = &sam;

    Bill zeigt jetzt auf Sam.

    Ich denke, Sie haben die Idee. Ich denke, wir haben ungefähr eine Stunde damit verbracht, bis wir die Grundlagen der Zeigerzuweisung durchgearbeitet haben.

    21 April 2016
    3 revs, 3 users 78%Tryke
  • Eine Analogie, die ich für die Erklärung von Zeigern hilfreich gefunden habe, sind Hyperlinks. Die meisten Leute können verstehen, dass ein Link auf einer Webseite auf eine andere Seite im Internet verweist und ob Sie & amp; Fügen Sie diesen Hyperlink ein, dann zeigen beide auf dieselbe ursprüngliche Webseite. Wenn Sie die ursprüngliche Seite bearbeiten, folgen Sie einem dieser Links (Zeiger), und Sie erhalten die neue aktualisierte Seite.

    26 July 2013
    2 revs, 2 users 67%Wilka
  • Der Grund, warum Zeiger anscheinend so viele Leute verwirren, ist, dass sie meistens wenig oder gar keinen Hintergrund in der Computerarchitektur haben. Viele scheinen keine Vorstellung davon zu haben, wie Computer (die Maschine) tatsächlich implementiert werden - das Arbeiten in C / C ++ wirkt ein Fremdkörper.

    Eine Übung ist, sie um Implementierung zu bitten Eine einfache, auf Bytecodes basierende virtuelle Maschine (in jeder von ihnen gewählten Sprache ist Python dafür hervorragend geeignet) mit einem Befehlssatz, der auf Zeigeroperationen (Laden, Speichern, direkte / indirekte Adressierung) fokussiert ist. Bitten Sie sie dann, einfache Programme für diesen Befehlssatz zu schreiben.

    Alles, was etwas mehr als eine einfache Hinzufügung erfordert, wird Zeiger beinhalten, und sie werden es sicher bekommen.

    08 August 2008
    JSN
  • Warum sind Zeiger für viele neue und sogar alte College-Studenten in C / C ++ eine so große Verwirrung?

    Das Konzept eines Platzhalters für einen Wert - Variablen - bildet etwas ab, was wir in der Schulalgebra lernen. Es gibt keine existierende Parallele, die Sie zeichnen können, ohne zu verstehen, wie Speicher in einem Computer physisch angeordnet ist, und niemand denkt darüber nach, bis es sich um Dinge mit niedrigem Niveau handelt - auf der Kommunikationsebene C / C ++ / Byte .

    Gibt es Werkzeuge oder Denkprozesse, mit deren Hilfe Sie die Funktionsweise von Zeigern auf Variablen, Funktionen und darüber hinaus verstehen?

    Adressiert Felder. Ich erinnere mich, als ich gelernt hatte, BASIC in Mikrocomputer zu programmieren, da waren diese hübschen Bücher mit Spielen darin, und manchmal musste man Werte in bestimmte Adressen stecken. Sie hatten ein Bild von einer Reihe von Boxen, die schrittweise mit 0, 1, 2 beschriftet waren, und es wurde erklärt, dass nur ein kleines Ding (ein Byte) in diese Boxen passen könnte, und es gab viele - einige Computer hatte sogar 65535! Sie waren nebeneinander und hatten alle eine Adresse.

    Was sind einige gute Praktiken, die gemacht werden können, um jemanden auf das Niveau von zu bringen "Ah-hah, ich habe es verstanden", ohne sich in das Gesamtkonzept einzumischen? Im Allgemeinen bohren Sie ähnliche Szenarien.

    Für eine Übung? Erstellen Sie eine Struktur:

     struct {
    char a;
    char b;
    char c;
    char d;
    } mystruct;
    mystruct.a = 'r';
    mystruct.b = 's';
    mystruct.c = 't';
    mystruct.d = 'u';
    
    char* my_pointer;
    my_pointer = &mystruct.b;
    cout << 'Start: my_pointer = ' << *my_pointer << endl;
    my_pointer++;
    cout << 'After: my_pointer = ' << *my_pointer << endl;
    my_pointer = &mystruct.a;
    cout << 'Then: my_pointer = ' << *my_pointer << endl;
    my_pointer = my_pointer + 3;
    cout << 'End: my_pointer = ' << *my_pointer << endl;
     

    Dasselbe Beispiel wie oben, außer in C:

     // Same example as above, except in C:
    struct {
        char a;
        char b;
        char c;
        char d;
    } mystruct;
    
    mystruct.a = 'r';
    mystruct.b = 's';
    mystruct.c = 't';
    mystruct.d = 'u';
    
    char* my_pointer;
    my_pointer = &mystruct.b;
    
    printf("Start: my_pointer = %c\n", *my_pointer);
    my_pointer++;
    printf("After: my_pointer = %c\n", *my_pointer);
    my_pointer = &mystruct.a;
    printf("Then: my_pointer = %c\n", *my_pointer);
    my_pointer = my_pointer + 3;
    printf("End: my_pointer = %c\n", *my_pointer);
     

    Ausgabe:

     Start: my_pointer = s
    After: my_pointer = t
    Then: my_pointer = r
    End: my_pointer = u
     

    Vielleicht erklärt das einige der Grundlagen anhand eines Beispiels?

    25 February 2015
    Paul Betts
  • Der Grund, warum es mir schwer fiel, Zeiger zu verstehen, war zunächst, dass viele Erklärungen viel Quatsch mit dem Nachgeben von Verweisen enthalten. Dies alles verwirrt das Problem. Wenn Sie einen Zeigerparameter verwenden, übergeben Sie immer noch den Wert. Der Wert ist jedoch eher eine Adresse als etwa ein int.

    Jemand anderes hat bereits mit diesem Tutorial verlinkt, aber ich kann den Moment hervorheben, als ich anfing, Zeiger zu verstehen :

    Ein Tutorial zu Zeigern und Arrays in C: Kapitel 3 - Zeiger und Strings

     int puts(const char *s);
     

    Für die Moment, ignorieren Sie const. Der an puts() übergebene Parameter ist ein Zeiger. ist der Wert eines Zeigers (da alle Parameter in C als Wert übergeben werden) und der Wert eines Zeigers die Adresse an worauf es verweist, oder einfach auf eine Adresse. Wenn wir also, wie wir gesehen haben, puts(strA); schreiben, übergeben wir die Adresse von strA [0].

    In dem Moment, in dem ich diese Wörter las, teilten sich die Wolken und ein Sonnenstrahl umhüllte mich mit Zeigerverständnis.

    Auch wenn Sie ein VB sind. NET oder C # -Entwickler (wie ich bin) und niemals unsicheren Code verwenden, es ist immer noch wo Wenn Sie wissen, wie Zeiger funktionieren, verstehen Sie nicht, wie Objektreferenzen funktionieren. Dann haben Sie die allgemeine, aber falsche Vorstellung, dass das Übergeben einer Objektreferenz an eine Methode das Objekt kopiert.

    12 September 2012
    2 revsKyralessa
  • Ich fand Ted Jensens "Tutorial zu Zeigern und Arrays in C" eine ausgezeichnete Quelle, um mehr über Zeiger zu erfahren. Es ist in 10 Lektionen unterteilt, beginnend mit einer Erklärung, welche Zeiger sind (und wozu sie dienen) und mit Funktionszeigern abgeschlossen. http://home.netcom.com/~tjensen/ptr/cpoint.htm

    Von hier aus lernen Sie die Unix-Sockets-API, mit der Sie wirklich Spaß machen können. http://beej.us/guide/bgnet/

    11 August 2008
    Ted Percival
  • Die Komplexität von Zeigern geht über das hinaus, was wir leicht lehren können. Wenn Schüler aufeinander zuweisen und Zettel mit Hausadressen verwenden, sind beide hervorragende Lernmittel. Sie führen die grundlegenden Konzepte ein. In der Tat ist das Erlernen der grundlegenden Konzepte von entscheidender Bedeutung für die erfolgreiche Verwendung von Zeigern. Im Produktionscode wird jedoch häufig auf viel komplexere Szenarien eingegangen, als diese einfachen Demonstrationen verkapseln können.

    Ich war in Systeme involviert, in denen Strukturen vorhanden waren, die auf andere Strukturen verweisen auf andere Strukturen verweisen. Einige dieser Strukturen enthielten auch eingebettete Strukturen (anstelle von Zeigern auf zusätzliche Strukturen). Hier werden Zeiger wirklich verwirrend. Wenn Sie mehrere Ebenen der Indirektion haben und mit Code wie folgt enden:

     widget->wazzle.fizzle = fazzle.foozle->wazzle;
     

    it kann sehr schnell verwirrend werden (stellen Sie sich viel mehr Zeilen und möglicherweise mehr Ebenen vor). Fügen Sie Arrays von Zeigern und Knoten-zu-Knoten-Zeigern (Bäume, verknüpfte Listen) hinzu, und es wird noch schlimmer. Ich habe gesehen, dass sich einige wirklich gute Entwickler verirrt haben, sobald sie mit solchen Systemen angefangen haben. Sogar Entwickler, die die Grundlagen wirklich gut verstanden haben.

    Komplexe Zeigerstrukturen bedeuten nicht zwangsläufig schlecht Kodierung entweder (obwohl sie es können). Komposition ist ein wichtiges Stück guter objektorientierter Programmierung. In Sprachen mit rohen Zeigern führt dies unweigerlich zu einer mehrschichtigen Umleitung. Des Weiteren müssen Systeme häufig Bibliotheken von Drittanbietern mit Strukturen verwenden, die in Stil und Technik nicht zueinander passen. In solchen Situationen wird natürlich Komplexität auftreten (obwohl wir uns gewiss darum bemühen sollten).

    Ich denke, das Beste, was die Hochschulen tun können, um die Schüler zu lernen Zeiger sollen gute Demonstrationen verwenden, kombiniert mit Projekten, die Zeigerverwendung erfordern. Ein schwieriges Projekt wird mehr zum Zeigerverständnis beitragen als tausend d

    10 August 2008
    Derek Park
  • Ich denke nicht, dass Zeiger als ein Konzept besonders knifflig sind - die meisten mentalen Modelle der Schüler sind so ähnlich und einige Quick-Box-Skizzen können helfen.

    Die Schwierigkeit, zumindest die, die ich in der Vergangenheit erlebt habe und mit der andere Personen zu tun hatten, ist, dass die Verwaltung von Zeigern in C / C ++ unnötig kompliziert sein kann.

    08 August 2008
    2 revsMatt Mitchell
  • Ich dachte, ich würde dieser Liste eine Analogie hinzufügen, die ich als sehr hilfreich bei der Erklärung von Hinweisen als Informatik-Tutor (früher) empfand. Zuerst lassen Sie uns:


    Stellen Sie die Bühne :

    Betrachten Sie einen Parkplatz mit 3 Stellplätzen. Diese Stellplätze sind nummeriert:

     -------------------
    |     |     |     |
    |  1  |  2  |  3  |
    |     |     |     |
     

    In gewisser Weise ist das ähnlich Speicherplätze sind sie sequentiell und zusammenhängend .. eine Art wie ein Array. Im Moment befinden sich keine Autos darin, also ist es wie ein leeres Array (parking_lot[3] = {0}).


    Fügen Sie die Daten hinzu < / strong>

    Ein Parkplatz bleibt nie lange leer ... Wenn dies der Fall wäre, wäre es sinnlos und niemand würde einen bauen. Nehmen wir an, der Tag bewegt sich auf dem Parkplatz mit 3 Autos, einem blauen Auto, einem roten Auto und einem grünen Auto:

        1     2     3
    -------------------
    | o=o | o=o | o=o |
    | |B| | |R| | |G| |
    | o-o | o-o | o-o |
     

    Diese Autos sind alle vom gleichen Typ (Auto). Eine Möglichkeit, dies zu berücksichtigen, besteht darin, dass unsere Autos Daten sind (sagen wir int), aber unterschiedliche Werte haben (blue , red, green; das könnte eine Farbe sein enum)


    Geben Sie den Zeiger

    Wenn ich Sie jetzt auf diesen Parkplatz bringe und Sie ein blaues Auto suchen, strecken Sie einen Finger und zeigen damit auf ein blaues Auto an Stelle 1 Dies ist, als würde man einen Zeiger nehmen und einer Speicheradresse zuweisen (int *finger = parking_lot)

    Ihr Finger (der Zeiger) ist nicht die Antwort auf meine Frage. Wenn Sie auf sehen, sagt Ihr Finger nichts, aber wenn ich sehe, wo Ihr Finger zeigt, zeigt er auf (dereferencing den Zeiger), ich kann es finden das Auto (nach den Daten), nach dem ich gesucht habe.


    Neuer Zeiger

    Jetzt kann ich Sie bitten, stattdessen ein rotes Auto zu finden, und Sie können Ihren Finger auf ein neues Auto umleiten. Nun zeigt Ihr Zeiger (derselbe wie zuvor) neue Daten (den Parkplatz, auf dem sich das rote Auto befindet) desselben Typs (das Auto) an.

    Der Zeiger hat sich nicht physikalisch geändert, er ist immer noch

    05 April 2013
    Mike