(Zeiger auf Zeiger) Verständnissproblem



  • Hallo,

    ich habe ein Verständnissproblem mit einem Konstukt: (void**) &MyStruct oder (void**) &MyObj.

    Ich weiß das es in C++ Zeiger gibt. Diese besitzen einen Typ. Meistens Zeiger auf etwas wie z.B. int* intPTR; (Zeiger auf einen int, oder char* chrPTR zeiger auf einen CHAR (Byte)).
    Zeiger nehmem Addressen auf, das sind die Speicherstellen an denen Variablen (int , char,..) Werte speichern.
    Also werden sie intialisiert um auf etwas zu zeigen! Z.B. intPTR=&y; (wobei y ein int). Nun kann dereferenziert werden also *intPTR=3; und somit wird y der Wert 3 zugeiesem. Zeiger auf Zeiger bedeutet meiner Meinung dann das int z.B. y eben kein Wert steht sondern eine Addresse ist also auch Zeiger auf etwas.

    Ähnliche Konstrukte tauchen auf z.B. wenn mann die Funktion OleCreatePictureIndirect benutzt mit "VOID** ppvObj".

    Kann mir jemand erklären was ich da eigentlich mache, wenn ich eine solches Konstrukt benutze? Ich kann das in meinem Kopf irgendwie nicht richtig auflösen.
    Kann mir da jemand helfen und es anschaulich erklären.

    (...Ja, ich weiß wieder so'ne Frage vom Newbie....)

    Gruß
    Franky



  • "Zeiger auf irgendwas" aka void* ist auch nur ein ganz normaler Datentyp, von dem du Variablen anlegen kannst. Und natürlich kannst du auch einen Zeiger auf einen "void*" verweisen lassen - das ergibt dann einen void**.

    In C Bibliotheken kommt sowas häufig vor, wenn eine Funktion dynamisch Speicherplatz anfordert und dem Hauptprogramm übergeben will - das Hauptprogramm hat einen Zeiger auf irgendwas und übergibt ihn als "Referenz" (ich weiß, was die C'ler dazu sagen werden) an eine Funktion:

    int get_data(int** pdata,...)
    {
      int anzahl = ...;
      *pdata = malloc(anzahl*sizeof(int));
      ...
      return anzahl;
    }
    
    int main()
    {
      int* mydata;//Zeiger auf einen Speicherbereich
      int datasize = get_data(&mydata/*ich übergebe die Adresse der Variablen*/,...);
      int i;
      for(i=0;i<datasize;++i)
        printf("%d %d\n",i,mydata[i]);
    }
    


  • Zeiger auf int hast du ja schon verstanden. Zeiger auf Chars oder andere Typen auch. So ein Zeiger ist aber selbst ein Typ, hat damit auch eine Adresse (irgendwo muss die Adresse des int ja "aufgeschrieben" werden. Damit kann man also auch Zeiger auf Zeiger haben.

    int i;
    int* pi = &i; //beinhaltet die Adresse von i
    int** ppi = &pi; //beinhaltet die Adresse von pi
    

    bei void pointern ist das nur insofern unterschiedlich, als dass es keien Sinn macht, "Zeiger auf nix" zu haben. ein void* ist daher vielmehr ein "Zeiger auf irgendwas", der, bevor man ihn benutzen kann, meist noch zu einem Zeiger auf eien Konkreten Typen gecastet werden muss. In einem void* steht also irgendeine Adresse, und man weiss ohne den Kontext nicht genau, was man an der Adresse vorzufinden hat. Anders ists wieder mit dem void**, was ein Zeiger auf einen voidpointer ist. Ein void** zeigt also auf eine Stelle im Speicher, an der eine blanko-Adresse steh.



  • Kann mir jemand sagen, warum es bei mir bei folgendem Beispiel keinen compilier Fehler gibt?

    #include <iostream>
    
    int main()
    {
        int i=23123;
    	void *p1=&i;
    	void *p2;
    	p2=&p1;
    	return 0;
    }
    

    MfG
    Stromberg



  • Hallo

    Warum sollte es den einen geben? Du weist in der letzten Anweisung p2 die Adresse von p1 zu. Das heißt p2 zeigt auf p1, nicht auf i.

    bis bald
    akari



  • Aso, ich hab gedacht das "" muss man machen, wenn man die Adresse von einem Zeiger in einem anderen speichert. Aber das "..." muss man nur machen, wenn man die Adresse aus einem Zeiger, überdiesen, an einen anderen Zeiger weitergibt?
    Also:

    int 10;
    *p1=&10;
    **p2=p1;
    

    MfG
    Stromberg



  • Ja, eigentlich müsstest du es machen, aber ein void* kann auf beliebige Daten verweisen, also ohne Probleme auch auf einen anderen void* (oder sogar auf sich selber).



  • Hallo,

    ok ich glaube mein Problem an sich ist dann eher die Schnittstelle zur Funktion zu verstehen:

    Wenn ich weiß das es Zeiger gibt und Variablen (integer, long, char, ...) aud die ich dann zeigen kann, also die Variable besitzt einen Wert (

    int x=2;
    

    ) dann wird der Wert ja irgendwo im Speicher gehalten. Setze ich einen Zeiger drauf

    int *intPTR; und intPTR=&x;
    

    kann ich jetzt Werte nach x schreiben ohne x zu kennen (könnte auch y) heißen.( Praktische Beispiel ist die Swap Funktion , die Werte austauscht und ich Zeiger übergebe, weil ich sonst Schwierigkeiten bekomme die Werte zu tauschen.)

    "int** ppi = &pi;"
    

    habe ich dann eben nur einen Zeiger nämlich (pi)
    Wenn ich aber eine Funktion schreibe oder Aufrufe ist dann (...aber nicht hauen...)

    ...,(int** ppi) &pi,...
    

    dann vom Prinzip das gleiche wie eben sie Initializierung mit int** ppi=.... Weil eben das Konstrukt (void**) &myStruct auch einer Funktion im Paramterbereich übergeben wird und ich nicht auf die Reihe kriege was dort eigentlich genau passiert. Ich kann eine Variable deklarieren und einen Zeiger draufsetzen, aber wenn ich mir Funktionsschnittstellen ansehen, dann klemmt's.

    Gruß
    Franky



  • Einen Zeiger hast du übrigens auch bei Arrays. Dort zeigt der Zeiger auf das erste Element.
    Einen Zeiger auf einen Zeiger kann man so für mehrdimensionale Arrays nutzen, wie viele Grafikbibliotheken es zB benötigen (wenn auch manchmal nur intern oder was auch immer).

    Das hast du zB bei char* meinArray[4]; meinArray ist dann vom Typ char** und verweist auf den Punkt im Speicher in dem die vier Adressen hintereinander stehen, die auf weitere Speicherbereiche verweisen, an denen dann Zeichenketten stehen (wenn man denn was initialisiert) können.



  • void mySwap(int *a, int *b){ int i; i=*a; ...}
    

    wenn dann mySwap aufgerufen wird dann so:

    mySwap(&x, &y)
    

    Ist das also das gleiche wie

    int* a; int i; a=&x; i=*a;
    

    weil dann müßte ja (Aufruf)

    ...myFunc(..., (void**) &myStruct,...)
    

    (wobei das ja nicht weiß, eben nur als Beispiel, zum Verständniss was ich da mache) (mögliche Deklaration)

    ...myFunc(..., void* paramStruct,...)
    

    oder so?

    Gruß
    Franky



  • Es kommt drauf an was das für eine Funktion ist. Willst du selber eine erstellen?

    Wenn du nur den Pointer auf ein Objekt brauchst, dann:
    void myFunc(myStruct *s);

    und Aufruf mit myFunc(&meinStruktOderObjekt);

    Void-Pointer braucht man wirklich nur in seltenen Fällen.



  • int i=23123;
        int *p1=&i;
        int *p2;
        p2=p1;
    
    	cout << *p2 << " " << *p1 << endl;
    // ausgabe: 23123 23123
    

    das hier scheint mit:

    int i=23123;
        int *p1=&i;
        int **p2;
        p2=&p1;
    
    	cout << **p2 << " " << *p1 << endl;
    // ausgabe: 23123 23123
    

    äquivalenz zu sein.

    da wird man wohl nicht erkennen können, wofür ** gut sind?



  • Du kannst ja soviele Pointer auf Pointer auf Pointer etc erstellen wie du willst, solange du die immer wieder alle dereferenzierst, macht das keinen Unterschied (Speicherverbrauch für die einzelnen Pointer mal ignoriert).

    Im Grunde erstellst du jedesmal ein Inhaltsverzeichnis für ein Inhaltsverzeichnis in einem Buch. So als Metapher mal gedacht. Da kannst du fünfhundert verschachtelte Inhaltsverzeichnisse haben, solange du denen immer brav bis zum eigentlichen Text folgst, nehmen die nur Platz weg. 😉



  • Hallo,

    ...sondern nur die Parameterübergabe der Art

    ..., (void**) &myVariable,...
    

    verstehen.
    Bislang dachte ich immer Zeiger verstanden zu haben, aber dann habe ich den oben angegebenen Code gesehen und war verblüfft. Weil eine Addresse übergeben wird und diese konvertiert in einen void Zeiger auf void Zeiger. Also habe ich mich gefragt, was das soll. Weil ich das noch nie gesehen habe und meine Bemühungen zu verstehen was da abläuft ins Leere gelaufen sind.

    Gruß
    Franky



  • Zeig doch mal die ganze Funktionssignatur.

    Also wenn eine Funktion einen Pointer auf einen void* haben will, musst du ihr natürlich auch einen geben. Daher das casten zu void**. Und myVariable ist vermutlich ein typedef, irgend ein Pointer.



  • ..ist aus dem Buch von David Kruglinski, George Sheperd, Scot Wingo
    "Inside Visual C++ 6.0; Kapitel 24, Seite 533 ff."

    Dort wird a)

    eine Struktur definiert

    struct IMotion
    {
      virtual Fly()=0;
      virtual GetPosition()=0;
    };
    

    b) eine Klasse Spaceship

    class Spaceship: public IMotion
    {
      protected:
        m_nPosition;
      public:
        CSpacehip() {m_nPosition=0;}
        void Fly();
        int& GetPosition() {return m_nPosition}
    };
    

    c) eine Schnittstelle

    IMotion *pMot;
    GetClassObject (CLSID_CSpaceship, IID_IMotion, (void**) & pMot);
    

    Das ganze um Konstrukte wie eben...

    STDAPI OleCreatePictureIndirect( 
      PICTDESC* pPictDesc,
                     //Pointer to the structure of parameters for picture
      REFIID  riid,  //Reference to the identifier of the interface
      BOOL fOwn,     //Whether the picture is to be destroyed
      VOID** ppvObj  //Address of output variable that receives the 
                     // interface pointer requested in riid
    );
    

    ...zu verstehen, weil für mich das ganze irgendwie ähnlich aussieht, aber ich mit (void) &...** irgendwie nicht klarkomme. Wei lich nicht wirklich weiß was das dort macht!

    Gruß
    Franky



  • Ich seh das so:

    '*' bedeutet: Pointer (völlig unabhängig von dem Typ auf was der Pointer Zeigt). Es ist ja nur eine Adresse im Speicher - d.h. ein Pointer selbst sieht immer gleich aus (Adresse eben) - egal auf was für einen Typ er zeigt.

    Insofern ist void* ein Pointer auf etwas vom Typ void (also etwas unbekanntes das man halt dann wahrscheinlich noch casten muss um damit weiterzuarbeiten)

    void** ist einfach ein Pointer auf einen Pointer der auf etwas vom Typ void zeigt.

    Könnte z.B. von Nutzen sein wenn man einen ganze Liste von Pointern auf Ergebnisse unterschiedlichen Typs hat und über eine andere Funktion einen Pointer auf eine Stelle in dieser Liste zurückgibt.

    Oder seh ich das jetzt falsch?



  • FrankTheFox schrieb:

    ..ist aus dem Buch von David Kruglinski, George Sheperd, Scot Wingo
    "Inside Visual C++ 6.0; Kapitel 24, Seite 533 ff."

    Dort wird a)

    eine Struktur definiert

    struct IMotion
    {
      virtual Fly()=0;
      virtual GetPosition()=0;
    };
    

    Stehn die Methoden da wirklich so ohne Rueckgabetypen? Ich glaube nicht. Falls doch, dann suche einen Altpapiercontainer in deiner Naehe auf, der das Buch liebevoll aufnehmen wird.

    IMotion *pMot;
    GetClassObject (CLSID_CSpaceship, IID_IMotion, (void**) & pMot);
    

    Die Funktion GetClassObject erwartet als dritten Parameter offenbar einen Zeiger auf einen void-Zeiger (vermutlich um dort eine Adresse zu hinterlassen, man koennte stattdessen auch eine Referenz auf einen Zeiger verwenden). Dein pMot ist aber ein Zeiger auf ein IMotion, damit ist &pMot ein Zeiger auf einen IMotion-Zeiger (IMotion**). Deshalb steht dort das (void**), um den Zeiger-auf-Imotion-Zeiger in einen Zeiger-auf-void-Zeiger zu veraendern.

    Das ganze um Konstrukte wie eben...

    STDAPI OleCreatePictureIndirect( 
      PICTDESC* pPictDesc,
                     //Pointer to the structure of parameters for picture
      REFIID  riid,  //Reference to the identifier of the interface
      BOOL fOwn,     //Whether the picture is to be destroyed
      VOID** ppvObj  //Address of output variable that receives the 
                     // interface pointer requested in riid
    );
    

    ...zu verstehen, weil für mich das ganze irgendwie ähnlich aussieht

    hier ist OleCreatePictureIndirect eine Funktion, die utner anderem einen VOID** erwartet - vom Namen her vermutlich mehr oder weniger das gleiche wie ein void**

    Der Unterschied: bei GetClassObject handelt es sich um einen Funktionsaufruf mit einem cast nach void**. Bei OleOleLangerName um eine Funktionsdeklaration, die dem Anwender sagt dass das letzte Argument ein VOID** zu sein hat.
    Beides (vor allem das letzte) sieht mir etwas arg C-Style aus, vermutlich kann man das auch schicker loesen.



  • ich glaub' ich hab's!!!! Wenn ich falsch liege, dann sagt's mir:

    Also meine Lösung:

    void function mySwap(int *a, int *b)
      {
       .......
      }
    

    wird so aufgerufen...

    mySwap( &a, &b);
    

    die Deklaration von GetClassObject setzt aber

    ..., void** ppvObj,...
    

    voraus. Also einen Zeiger auf einen Zeiger! Ich muß dann nicht nur &...
    schreiben, sondern muß das zu einem Zeiger auf einen Zeiger machen, also dann ein (void)** vor die Addresse, also das &! Also nur ein cast! Ich dachte weil ich das klammere und vor das Addressop setzte, hätte das andere Bedeutung.



  • Ja, da stehen Rückgabewerte.


Log in to reply