Verwirrung bei Pointer und References



  • Seit kurzem beschäftige ich mich näher mit Pointer und References. Die Grundlagen sind mir soweit klar. Problematisch ist es, wenn komplexere Konstrukte auftauchen wie z.B.:

    void ** (*d) (int &, char **(*)(char *, char **));
    

    Nach ein wenig googeln, war ich auf folgenden Artikeln gestoßen: http://www.codeproject.com/KB/cpp/complex_declarations.aspx#right_left_rule. Der Artikel ist mir zu oberflächlich und für mich nicht sehr verständlich.

    Was für weitere Artikel könnt ihr mir zum Thema empfehlen?


  • Mod



  • SeppJ schrieb:

    http://cdecl.org/

    😞
    http://cdecl.ridiculousfish.com/?q=void++%28*d%29+%28int+%26%2C+char+**%28*%29%28char+*%2C+char+%29%29

    langsam nervt es, wie viele URLs falsch abgeschnippelt werden.



  • Also das Konstrukt ist ja noch einfach. Ein Zeiger namens d auf eine Funktion, die void** zurückgibt und int&, sowie einen Zeiger auf eine Funktion, die char** zurückgibt und einen char*, sowie einen char** nimmt, nimmt



  • Vielleicht haste daran mehr Freude?
    /home/volkard/src/testcpp/main.cpp|14|Fehler: ungültige Umwandlung von »void (* (* (& ()())[5])(void ()()))()« in »int«|
    Also mir bereitet das Lesen erhebliche Schwierigkeiten.



  • volkard schrieb:

    Vielleicht haste daran mehr Freude?
    /home/volkard/src/testcpp/main.cpp|14|Fehler: ungültige Umwandlung von »void (* (* (& ()())[5])(void ()()))()« in »int«|
    Also mir bereitet das Lesen erhebliche Schwierigkeiten.

    http://cdecl.org/ schrieb:

    syntax error

    😃



  • Zeiger auf Funktion(), die Referenz auf Array[5] von Zeigern auf Funktionen(void (*) ()), die Zeiger auf void Funktion() zurückgeben, zurückgibt.



  • Jo.

    typedef void voidVoidFunctionType();
    typedef voidVoidFunctionType* setNewHandlerType(voidVoidFunctionType*);
    typedef setNewHandlerType* arrayOf5PointersToSetNewHandlerType[5];
    typedef arrayOf5PointersToSetNewHandlerType& referenceToArrayOf5PointersToSetNewHandlerType;
    typedef referenceToArrayOf5PointersToSetNewHandlerType (*pointerToVoidFunctionReturningReferenceToArrayOf5PointersToSetNewHandlerType)();
    
    int main()
    {
        pointerToVoidFunctionReturningReferenceToArrayOf5PointersToSetNewHandlerType c;
        int i=c;
    }
    

    http://ideone.com/992y9



  • inc7 schrieb:

    Nach ein wenig googeln, war ich auf folgenden Artikeln gestoßen: http://www.codeproject.com/KB/cpp/complex_declarations.aspx#right_left_rule. Der Artikel ist mir zu oberflächlich und für mich nicht sehr verständlich.

    Was für weitere Artikel könnt ihr mir zum Thema empfehlen?

    Kein Artikel. Es ist eigentlich ganz einfach zu verstehen. Eine Deklaration besteht immer aus einem Typ und einem Deklarator: T D; (den Initialisierer lass ich mal weg). Der Typ ist dabei ein einfacher Typname (also primitiver Typ, Klasse, Enum oder typedef). Das heißt, bei int*& pref; ist der Typ int und der Deklarator * & pref (was man als Argument dafür, es als int *&pref zu schreiben nehmen kann, aber nicht muss). Die Frage ist also, was bedeutet ein Deklarator?

    Ein Deklarator hat einen rekursiven Aufbau, eine Syntax.
    *D bedeutet: D ist Zeiger auf irgendwas (das T in der Deklaration), &D bedeutet: D ist Referenz auf irgendwas, D[] bedeutet: D ist Array von irgendwas. Das kann man jetzt ineinander schachteln:
    *(D[]) heißt: D[] ist Zeiger auf irgendwas und D ist Array von irgendwas, also zusammen: D ist Array von Zeiger auf irgendwas.
    (*D)[] ist andersrum kombiniert: D ist Zeiger auf Array.

    Wenn man von kleineren Abweichungen absieht (wie z.B. dem Referenzzeichen), dann ist das gerade die Ausdruckssyntax. Diese Lesart funktioniert folgendermaßen. Wir nehmen uns mal irgendeinen komplizierten Typ, ich persönlich bin ja ein Fan der signal-Funktion.

    void (*signal(int , void (*)(int)))(int)

    Der Deklarator ist (*signal(int , void (*)(int)))(int) . Wenn man das als eine Art Ausdruck interpretiert (Rangfolge zwischen * und () beachten!), sieht man sofort: signal wird als Funktion mit zwei Parametern aufgerufen: signal(int, void (*) (int)) . Der eine Parameter ist int, den anderen müsste man auch analysieren, aber das schenk ich mir mal, das ist ein Zeiger auf eine Funktion, die einen int-Parameter annimmt und void zurückgibt.
    Was passiert mit dem Rückgabewert von signal? Er wird dereferenziert: *signal(int , void (*)(int)). Zusammenfassung bisher: signal ist eine Funktion, die einen Zeiger zurückgibt.
    Was passiert mit dem dereferenzierten Wert? Er wird als Funktion aufgerufen. (*signal(int , void (*)(int)))
    (int)
    . Zusammenfassung: signal ist eine Funktion, die einen Zeiger zurückgibt, der auf eine Funktion zeigt.
    Man geht also einfach von innen nach außen vor: Wenn etwas dereferenziert ist, muss es ein Zeiger sein, wenn etwas aufgerufen wird eine Funktion, wenn es indiziert wird ein Array. So hab ich volkards komischen Typ analysiert.

    Man muss sich also eigentlich nur einen Satz merken: Declaration mirrors use. Wenn man das einmal verstanden hat, hat man auch keine Probleme mehr mit den Abweichungen von der Regel, wie const und Referenzen.

    Oder man vergisst das ganze und bastelt sich übersichtliche typedefs 😉



  • Es ist eigentlich ganz einfach, ha. Das ist auf eine gewisse Weise schon richtig, und einem Compiler kann man das dementsprechend leicht beibringen, aber wenn man vor einem drei bis vier Ebenen tief geschachtelten Ausdruck steht, ist man als Mensch ohne viel Übung schon ein bisschen verloren.

    Es ist ein bisschen wie mit dem Spiel Go: Die Regeln hat man in fünf Minuten verstanden, aber das Spiel ist trotzdem komplexer als Schach. Und sogar komplexer als Funktions- und Arrayzeigerdeklarationen. 😉



  • seldon schrieb:

    Es ist eigentlich ganz einfach, ha. Das ist auf eine gewisse Weise schon richtig, und einem Compiler kann man das dementsprechend leicht beibringen, aber wenn man vor einem drei bis vier Ebenen tief geschachtelten Ausdruck steht, ist man als Mensch ohne viel Übung schon ein bisschen verloren.

    Es ist ein Unterschied, ob man aufgibt, weil man einfach prinzipiell nicht durchblickt, oder ob man nur keine Lust hat, die ganzen Verschachtelungen aufzudröseln.

    Es ist ein bisschen wie mit dem Spiel Go

    Überhaupt nicht. Wenn man die Regeln verstanden hat, kann man sie mechanisch anwenden, was bei Spielen generell nicht funktioniert.


Anmelden zum Antworten