Müssen Pointer Pointer immer auf Pointer zeigen ?



  • Hi, gerade dabei mir die Zeiger einzuprägen und ich habe extreme Schwierigkeiten wenn ich mir die Frage stelle, ob ich das verstanden habe oder nur auswendig gelernt habe.

    int i = 10;
    int* p1 = &i; // p1 wird Adresse von i zugewissen. Man sagt p1 zeigt auf i ?
    int** p2 = &p1; // p2 wird Adresse von p1 zugewissen. Man sagt p2 zeigt auf p1 ?
    

    p2 steht irgendwo im Speicher und hat als Wert gespeichert die Adresse von p1.
    p1 steht an der Stelle die p2 als Wert hat und als Wert wiederum eine Adresse wo i gespeichert ist.

    p2 -> p1 -> i ...

    soda bis hierher glaube ich passt. Aber jetzt fangen wir mal mit probieren an.

    int** p3 = &i; // Pointer Pointer auf Variablen

    Fall 1.) *p3 = 20; // i = 20 ? 1 mal referenziert dürfte doch funktionieren ?
    Fall 2.)**p3 = Er nimmt den Wert 10 her und dereferenziert ihn und zeigt dann auf die Stelle 10 im speicher ?

    Frage3.) Warum gibt es überhaupt Pointer Pointer ? Würde es nicht reichen nur ( einfache Pointer anzugeben )

    char* p4

    und dem Programmierer selber die Entscheidung lassen wieoft er referenziert ?

    Frage4.) Die wichtigste Frage: Siehe Thema. Ist der einzige Grund das mein einen **p baut wirklich das er irgendwie auf einen *p zeigt oder gibt es da " mehr " ?



  • int** p3 = &i; // Pointer Pointer auf Variablen
    Passt nicht, da p3 ja ein int** ist, &i aber nur ein int*

    Bullz schrieb:

    Warum gibt es überhaupt Pointer Pointer ?

    Wo setzt du denn Zeiger ein?
    Z.B. wenn du mehrere Werte aus einer Funktion heraus ändern willst.
    Was ist , wenn du den Inhalt (die Adresse) von einem Pointer ändern willst.

    Z.B bei Arrays. Wenn du ein Array asu Zeigern hast, bekommst du bei Funktionaufrufen einen Doppelzeiger.

    Bullz schrieb:

    Würde es nicht reichen nur ( einfache Pointer anzugeben )

    char* p4

    und dem Programmierer selber die Entscheidung lassen wieoft er referenziert ?

    Nein. Der Compiler muss doch wissen, wann er den eigentlichen Variablentyp nehmen muss.
    Denn *p4 ist das char. p4 ist der Pointer. Da kannst du keine Sterne mehr weglassen um eine char** zu bekommen.



  • Bullz schrieb:

    Aber jetzt fangen wir mal mit probieren an.

    int** p3 = &i; // Pointer Pointer auf Variablen

    Das ist ein Typfehler: &i hat den Typ Zeiger auf int, p3 ist ein Zeiger auf Zeiger auf int. C ist ziemlich lax was Zeiger auf verschiedene Typen angeht, und lässt das durchgehen, aber wirklich richtig ist das nicht. Insbesondere sind deine Schlüsse nicht korrekt:

    Fall 1.) *p3 = 20; // i = 20 ? 1 mal referenziert dürfte doch funktionieren ?

    Nein, das geht schon schief. In der Praxis fährst du gut, solange Zeiger und int gleich repräsentiert sind, beispielsweise als 32-bit-Wort, aber das ist halt nicht immer so.

    Fall 2.)**p3 = Er nimmt den Wert 10 her und dereferenziert ihn und zeigt dann auf die Stelle 10 im speicher ?

    Ja, das wird ungefähr passieren. Sinnvoll ist das in den wenigsten Fällen.

    Frage3.) Warum gibt es überhaupt Pointer Pointer ? Würde es nicht reichen nur ( einfache Pointer anzugeben )

    Weil sie sich natürlich ergeben. Wenn man eine Variable eines beliebigen Typs T hat, dann hat diese eine Adresse, die dann den Typ "Zeiger auf T" hat. Insbesondere ist das natürlich so, wenn T bereits ein Zeigertyp ist. Warum sollte man das künstlich einschränken?

    char* p4

    und dem Programmierer selber die Entscheidung lassen wieoft er referenziert ?

    Dass C ein schwaches Typsystem hat, heißt nicht, dass es keins hat. Bei deinem Vorschlag wäre *p4 ja ein char, und das darf ich jetzt nochmal zu **p4 dereferenzieren, versteh ich das richtig? Das heißt, ich muss auch *'A' schreiben können. Macht das Sinn? Ich glaube kaum.

    Frage4.) Die wichtigste Frage: Siehe Thema. Ist der einzige Grund das mein einen **p baut wirklich das er irgendwie auf einen *p zeigt oder gibt es da " mehr " ?

    Ich versteh die Frage nicht. Das eine ist die Notation für das andere, wie kann jenes dann ein Grund sein? Aber falls es dich beruhigt, man braucht manchmal Zeiger auf Zeiger. Ab 3 Sternen wird es esoterisch, mehr als 3 hab ich noch nie ernsthaft irgendwo gesehen.

    PS: In B, einer der Vorgängersprachen von C, konnte man tatsächlich alles dereferenzieren. Alle Variablen waren 36-Bit-Worte, die je nach Operation als Ganzzahl, als 4 Zeichen oder als Zeiger interpretierbar waren. Aber man hat daraus gelernt...


  • Mod

    Bullz schrieb:

    soda bis hierher glaube ich passt.

    Ja, alles korrekt bis dahin.

    Aber jetzt fangen wir mal mit probieren an.

    int** p3 = &i; // Pointer Pointer auf Variablen

    Ja. Du nutzt hier aus, dass Zeigertypen in C implizit in andere Zeigertypen (Daten- und Funktionszeiger jeweils separat) umgewandelt werden können. Das Ergebnis kann aber durchaus undefiniert sein, wenn die Datentypen, auf die die Zeiger zeigen, nicht zueinander passen (z.B. falsches Alignment).

    Fall 1.) *p3 = 20; // i = 20 ? 1 mal referenziert dürfte doch funktionieren ?

    Unter der Annahme, dass int und int* zueinander kompatibel sind (was nicht unbedingt der Fall sein muss, z.B. auf 64-Bit Systemen könnte i an einer durch 4 aber nicht durch 8 teilbaren Adresse stehen, während ein int* aber ein 8 Byte Alignment haben könnte), dann ja.

    Fall 2.)**p3 = Er nimmt den Wert 10 her und dereferenziert ihn und zeigt dann auf die Stelle 10 im speicher ?

    Wieder unter der Annahme, dass die Typen überhaupt kompatibel sind: Ja.

    Frage3.) Warum gibt es überhaupt Pointer Pointer ? Würde es nicht reichen nur ( einfache Pointer anzugeben )

    1. Siehe oben, der Typ auf den gezeigt wird ist durchaus wichtig, sowohl technisch für das Alignment der Typen als auch für korrekte Konvertierungen, als auch für Pointerarithmetik.
    2. Warum überhaupt ein Typensystem? Man könnte auch alles untypisiert lassen und der Programmierer muss selber auf alles aufpassen. Aber es vermeidet Fehler und hat keine Nachteile. Und ein Teil eines Typensystems ist auch, dass Zeiger da auf einen bestimmten Datentyp zeigen. Und man braucht auch Zeiger, die auf Zeiger zeigen können.

    Frage4.) Die wichtigste Frage: Siehe Thema. Ist der einzige Grund das mein einen **p baut wirklich das er irgendwie auf einen *p zeigt oder gibt es da " mehr " ?

    Man braucht Zeiger auf Zeiger zum Aufbau komplexerer Datenstrukturen und Algorithmen. Zum Beispiel fast immer, wenn die Zeiger selbst zum Objekt eines Algorithmus werden, dann muss man auch Zeiger auf Zeiger zulassen.



  • SeppJ schrieb:

    Du nutzt hier aus, dass Zeigertypen in C implizit in andere Zeigertypen (Daten- und Funktionszeiger jeweils separat) umgewandelt werden können.

    Sicher? IIRC geht das nur von/nach void*, nicht zwischen beliebigen Zeiger-Typen.



  • Ein an den Haaren herangezogenes Beispiel

    #include <stdlib.h>
    #include <stdio.h>
    
    //Textarrays (wie z.B argv von main)
    char *deutsch[] = { "Hallo", "Welt", "!", NULL};
    char *english[] = { "Hello", "World!", NULL};
    
    void textout(char **ppc)  // Doppelzeiger
    { while (*ppc != NULL)
      { printf("1.Zeichen: %c. Text: %s\n", *ppc[0], *ppc);
        ++ppc;
      }
    }
    
    void settext(char ***pppc, int i)
    // einen Doppelzeiger aus einer Funktion heraus ändern
    // das würde man anders lösen, ist ja nur ein Beispiel
    { 
      if (i == 0) 
        *pppc = deutsch;
      if (i == 1) 
        *pppc = english;
    }
    
    int main (int argc, char ** argv)
    {
      char **ppc;
    
      ppc = deutsch;
      textout(ppc);
    
      settext(&ppc,1);
      textout(ppc);
    
      return 0;
    }
    

    http://ideone.com/fnA4KX


  • Mod

    SG1 schrieb:

    SeppJ schrieb:

    Du nutzt hier aus, dass Zeigertypen in C implizit in andere Zeigertypen (Daten- und Funktionszeiger jeweils separat) umgewandelt werden können.

    Sicher? IIRC geht das nur von/nach void*, nicht zwischen beliebigen Zeiger-Typen.

    Doch, sicher. C(99)-Standard, 6.3.2.3, 5-6. Leider weigert sich der pdf-Reader hier am Rechner mir den Text aus meiner Fassung des Dokuments auszuschneiden. 😞
    Es wird dort jedenfalls explizit Daten- zu Datenzeigerkonvertierung erlaubt, sofern das Alignment passt (ansonsten UB). Der nächste Absatz erlaubt das gleiche für Funktionszeiger.



  • Interessant, wieder was dazu gelernt.



  • SeppJ schrieb:

    Doch, sicher. C(99)-Standard, 6.3.2.3, 5-6. Leider weigert sich der pdf-Reader hier am Rechner mir den Text aus meiner Fassung des Dokuments auszuschneiden. 😞
    Es wird dort jedenfalls explizit Daten- zu Datenzeigerkonvertierung erlaubt, sofern das Alignment passt (ansonsten UB). Der nächste Absatz erlaubt das gleiche für Funktionszeiger.

    Ja sicher ist die Konvertierung erlaubt, aber da steht nichts davon, dass sie implizit durchgeführt wird. Im Gegenteil, in 6.5.4 steht "Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast."

    6.5.16.1 ist auch nicht sehr ergiebig, dort werden folgende impliziten Konvertierungen zugelassen:
    * zwischen Zeigern auf kompatible Typen
    * wenn ein void-Zeiger dabei ist
    * Nullzeigerkonstanten nach Zeigertyp
    * Zeiger nach _Bool

    Kompatibilität zwischen Typen ist eine sehr eng gestrickte Äquivalenzrelation, auf keinen Fall sind alle Typen kompatibel.



  • Also ich gestern schlafen gegangen bin dachte ich wüsste wenigstens irgendwas. Ich weiß gar nichts. Möchte aber wenigstens die ultra Basics können das ich in meinem Übungen weiter komme.

    1.) Das einzige was ich vielleicht verstanden habe

    int i;
    // Ein facher Zeiger sollte immer auf Variablen zeigen
    int* p1 = &1;
    // Wenn ich jetzt so blöd bin 😉 und mir nen Pointer Pointer bauen will, dann muss // sollte ? ich diesen auf einen normalen Pointer zeigen lassen
    int** p2 = &p1;
    // Und zum letzten mal weil so schön ist. Ich baue mir einen *** und dieser muss ? auf einen ** zeigen
    int***p3 = &p2

    Ich hoffe wenigstens das passt... wenn man das in ein Schema reinbringen.

    * zeigt auf Adresse von Variablen
    ** zeigt auf Adresse von *
    *** zeigt auf Adresse von ** usw

    Die kleine Zwischenfrage drängt sich auf warum man wirklich ** braucht. Sobald ich eine Variable ändern will ( keinen Pointer) schreibe ich
    Typ foo* = &Zu_veränderende_Variable ... und zack kann ich sie ändern ? Warum also den Aufwand antun einen ** zu erstellen auf einen * zeigen zu lassen und über diesen dann die Variable zu ändern ? Ganz kurze Antwort für sau blöde User bitte.

    Und hier ein kleines Beispiel von mir:

    char* p = "Hello";
    char** pp = &p;
    
    *p = "Funktioniert"; //klappt
    pp[0] = "Funktioniert"; //klappt auch
    //Hier der Versuche auf einzelne Elemente des Feldes zuzugreifen.
    printf("%c",**p); // Fehler Erhoffte mir 'F'
    printf("%c",p[0][1]); // Fehler Erhoffte mir 'u'
    

    p.s Schaue mir das Beispiel von oben noch genauer an. Aber habe wenig Hoffnung 😉



  • p ist nur ein einfacher Zeiger.

    Und du benutzt ihn bei printf wie einen Doppelzeiger.
    Schreib da pp hin und es passt.

    Das *p = "Funktioniert"; ist auch falsch

    char *p = "Funktioniert"; // ist richtig, da es die Varialendefinition ist
    .. 
    *p  = "Funktioniert nicht"; // ist falsch
    p = "Richtig" ; // ist richtig
    


  • ** sind natürlich gerechtfertigt, da reicht ein Blick auf

    int main(int argc,char**argv)
    

    ** repräsentiert hier eine String-Liste.
    Das kannst du dann endlos weiterführen:
    wie sieht ein Zeiger auf eine Stringliste aus? (z.B. für die Übergabe einer Stringliste an eine ändernde Funktion)

    char ***
    

    Und schon hast du sogar 3 Ebenen.



  • [quote="DirkB"]
    Schreib da pp hin und es passt.
    /quote

    Danke, hab den falschen Zeiger genommen.2 Mal nämlich. Habe mich bisschen rumgespielt mit den Pointern. Danke soweit gehts eigentlich bis auf zwei Sachen.

    char* p = "Hello";
    char** pp = &p;
    
    //Erste Frage
    printf("%c\n",**pp); //Gibt erstes Zeichen Haus.
    pp++; // Möchte den Pointer um eine Speicherstelle erhöhen um auf 'e' zu kommen Fehler
    printf("%c\n",**pp); //Hier sollte 'e' rauskommen
    
    //2 Frage
    printf("%c\n",**pp+1) //Erhöht den ASCII Wert um 1ns ... anstatt das er die nächste Stelle ausgeben würde :(
    

  • Mod

    1. Pointerarithmetik ist dir bekannt?

    int werte[] = {1,2,3};
    printf("%d", *(werte+1));
    

    Ergibt "2", anstatt, so wie du es vielleicht vermuten würdest, irgendwelchen Datenmüll.
    2. Operatorpriorität.



  • //2 Frage
    printf("%c\n",**pp+1) //Erhöht den ASCII Wert um 1ns ... anstatt das er die nächste Stelle ausgeben würde 😞
    **pp ist ja auch ein char. So wie es bei der Definition steht.
    Und was erwartest du bei c+1?

    char c, *p, **pp; // c, *p und **pp sind jeweils char
    p und *p sind Zeiger auf char

    Noch als Anmerkung:
    Wenn du schreibst
    char* p, c; dann ist p ein Zeiger und c ein char, weil es das gleiche ist wie
    char *p, c;



  • SeppJ schrieb:

    1. Pointerarithmetik ist dir bekannt?

    int werte[] = {1,2,3};
    printf("%d", *(werte+1));
    

    Ergibt "2", anstatt, so wie du es vielleicht vermuten würdest, irgendwelchen Datenmüll.

    Ja ist mir Bekannt sonst würde ich nicht auf die Idee kommen das sowas überhaupt geht. Dein Beispiel hilft mir bei meinem Problem nicht weiter.

    char* p = "Hello";
    char** pp = &p;
    
    printf("%c\n",**pp); 
    //Was muss hierher damit das unten das Zeichen e ausgeben wird  ?
    // **(pp+1); oder *(pp+1) geht schon mal nicht :(.   
    printf("%c\n",**pp); // hier soll e gedruckt werden
    

    thx für 2 🙂


  • Mod

    Bullz schrieb:

    SeppJ schrieb:

    1. Pointerarithmetik ist dir bekannt?

    int werte[] = {1,2,3};
    printf("%d", *(werte+1));
    

    Ergibt "2", anstatt, so wie du es vielleicht vermuten würdest, irgendwelchen Datenmüll.

    Ja ist mir Bekannt sonst würde ich nicht auf die Idee kommen das sowas überhaupt geht. Dein Beispiel hilft mir bei meinem Problem nicht weiter.

    Wenn ein Zeiger auf Integer bei Erhöhung auf den nächsten Integer gesetzt wird, auf was wird dann wohl ein Zeiger auf Zeiger auf char bei Erhöhung gesetzt?



  • Wie in der Schule 😉

    Den nächsten Char was er bei mir aber nicht tut.

    Ich könnte die scheiß Pointer vom 10 Stock gerade werfen.

    **(pp+1); schaut doch eh recht gut aus... warum macht der Compiler nicht das was ich will ?


  • Mod

    Bullz schrieb:

    Den nächsten Char was er bei mir aber nicht tut.

    Was daran liegt, dass diese Antwort falsch ist.

    int *i;
    char **c;
    ++i;  // Zeigt auf den nächsten int
    ++c;  // Du sagst: Zeigt auf den nächsten char
    

    Ist das nicht inkonsequent? Ich schreibe es mal anders:

    typedef int my_int_type;
    typedef char* my_char_ptr_type;
    
    my_int_type *pointer_to_my_int_type;
    my_char_ptr_type *pointer_to_my_char_ptr_type;
    
    ++pointer_to_my_int_type;  // Zeigt auf den nächsten my_int_type
    ++pointer_to_my_char_ptr_type; // Worauf zeigt dies?
    
    **(pp+1); schaut doch eh recht gut aus... warum macht der Compiler nicht das was ich will ?
    

    Du musst dich wieder fragen, welchen Wert du um 1 erhöhen willst. **p wolltest du wohl nicht um 1 erhöhen, denn das war der erste char selbst. pp willst du wohl auch nicht um eins erhöhen, denn das ist der Zeiger, der auf den anderen Zeiger zeigt, der auf den ersten char zeigt.

    warum macht der Compiler nicht das was ich will ?

    Der Computer macht ganz genau das, was du ihm sagst. Wenn du ihm etwas anderes sagst, als du willst, dann macht er folglich nicht das, was du willst. Die Schuld liegt aber da drin, dass du nicht richtig ausdrückst, was du möchtest.



  • Bullz schrieb:

    **(pp+1); schaut doch eh recht gut aus... warum macht der Compiler nicht das was ich will ?

    Weil der Compiler macht, was du ihm sagst. 😃
    Mit *(pp+1) schaltest du auf den nächsten Zeiger und greifst dann auf das Zeichen zu.
    pp zeigt dann nicht mehr auf p, sondern daneben. Und da ist kein gültiger char
    .

    JEtzt fehlt noch die dritte Möglichkeit: *(*p+1)


Anmelden zum Antworten