Funktion void signal()



  • Von innen nach außen unter Berücksichtigung der Operatorenrangfolge und -assoziativität:

    signal : signal wird deklariert

    signal**(int sig, void (func)(int))* : ... als eine Funktion mit 2 Parametern, sig und func

    *****signal(int sig, void (*func)(int)) : ... die einen Zeiger zurückliefert

    (*signal(int sig, void (*func)(int)))(int) : ... der auf eine Funktion mit einem int-Parameter zeigt

    void (*signal(int sig, void (*func)(int)))(int) : ... die void zurückliefert


  • Mod

    signal ist eine Funktion, die als Rückgabewert einen Zeiger auf eine Funktion zurück gibt, die void zurück gibt und einen int als Parameter hat. signal selber hat zwei Parameter: Einen int und einen Zeiger auf eine Funktion, die void zurück gibt und einen int als Parameter hat.

    Mit einem geschickten Typedef:

    typedef void(*funcptr_t)(int);
    funcptr_t signal(int sig, funcptr_t func);
    

    So ist es lesbar, oder? Aber wie konnte ich das an dem Originalausdruck ablesen (ich muss zugeben, für den habe ich ein paar Minuten gebraucht)? So:

    void (*signal(int sig, void (*func)(int)))(int);
           signal                                  ;   // Deklaration von "signal".
           signal(                          )      ;   // signal ist eine Funktion,
          *signal(                          )      ;   // die einen Zeiger zurück gibt.
         (*signal(                          ))(   );   // Genauer: Einen Zeiger auf eine Funktion.
    void (*signal(                          ))(   );   // Eine Funktion, die void zurück gibt
    void (*signal(                          ))(int);   // und int als Parameter hat.
    void (*signal(       ,                  ))(int);   // signal hat 2 Parameter:
    void (*signal(int sig,                  ))(int);   // Der Erste ist ein int, der den Namen "sig" erhält
    void (*signal(int sig,        func      ))(int);   // Der zweite Parameter hat den Namen "func"
    void (*signal(int sig,       *func      ))(int);   // func ist ein Zeiger
    void (*signal(int sig,      (*func)(   )))(int);   // auf eine Funktion,
    void (*signal(int sig, void (*func)(   )))(int);   // die void zurück gibt
    void (*signal(int sig, void (*func)(int)))(int);   // und einen int als Parameter hat
    

    edit: Da war ich der Langsamste. Die Formatierung des Codes hat zu lange gedauert 🙂 .

    Bashar schrieb:

    Von innen nach außen unter Berücksichtigung der Operatorenrangfolge und -assoziativität:

    Das wollte ich zunächst auch so schreiben, aber ich finde das äußerst missverständlich. "Innen" ist hier letztlich func. Und das ist ein schlechter Anfangspunkt, um den Ausdruck zu entziffern.

    Und noch ein Nachtrag: Die Seite http://cdecl.ridiculousfish.com/ kann zwar nur Variablen und keine Funktionssignaturen entziffern, aber ist hier wohl trotzdem relevant.



  • SeppJ schrieb:

    Bashar schrieb:

    Von innen nach außen unter Berücksichtigung der Operatorenrangfolge und -assoziativität:

    Das wollte ich zunächst auch so schreiben, aber ich finde das äußerst missverständlich. "Innen" ist hier letztlich func. Und das ist ein schlechter Anfangspunkt, um den Ausdruck zu entziffern.

    Du hast es doch auch von innen nach außen gemacht, volkard auch. Geht auch nicht anders. Vom Ausgangspunkt hab ich nichts geschrieben, der fällt bei mir genauso wie bei dir vom Himmel.

    Und noch ein Nachtrag: Die Seite http://cdecl.ridiculousfish.com/ kann zwar nur Variablen und keine Funktionssignaturen entziffern, aber ist hier wohl trotzdem relevant.

    Stimmt nicht. Was die nicht können sind benannte Funktionsparameter. Damit gehts: void (*signal(int, void (*)(int)))(int)


  • Mod

    Bashar schrieb:

    SeppJ schrieb:

    Bashar schrieb:

    Von innen nach außen unter Berücksichtigung der Operatorenrangfolge und -assoziativität:

    Das wollte ich zunächst auch so schreiben, aber ich finde das äußerst missverständlich. "Innen" ist hier letztlich func. Und das ist ein schlechter Anfangspunkt, um den Ausdruck zu entziffern.

    Du hast es doch auch von innen nach außen gemacht, volkard auch. Geht auch nicht anders. Vom Ausgangspunkt hab ich nichts geschrieben, der fällt bei mir genauso wie bei dir vom Himmel.

    Nein, er fällt nicht vom Himmel. Es ist der erste Bezeichner. Bei dir klingt das so, als solle man ganz innen anfangen, da habe ich dich wohl missverstanden. Aber ich bin immer erst nach außen gegangen und dann nach innen. Das ist genau so gut zu rechtfertigen, wie anders herum. Das allerletzte was ich zugefügt habe ist der int mit den meisten Klammern drumrum.

    Und noch ein Nachtrag: Die Seite http://cdecl.ridiculousfish.com/ kann zwar nur Variablen und keine Funktionssignaturen entziffern, aber ist hier wohl trotzdem relevant.

    Stimmt nicht. Was die nicht können sind benannte Funktionsparameter. Damit gehts: void (*signal(int, void (*)(int)))(int)

    Gut zu wissen. Darum hat das bei mir nie funktioniert. Wie ironisch, da doch C im Gegensatz zu C++ vorschreibt, dass man Funktionsparameter benennen muss.



  • SeppJ schrieb:

    Bashar schrieb:

    SeppJ schrieb:

    Bashar schrieb:

    Von innen nach außen unter Berücksichtigung der Operatorenrangfolge und -assoziativität:

    Das wollte ich zunächst auch so schreiben, aber ich finde das äußerst missverständlich. "Innen" ist hier letztlich func. Und das ist ein schlechter Anfangspunkt, um den Ausdruck zu entziffern.

    Du hast es doch auch von innen nach außen gemacht, volkard auch. Geht auch nicht anders. Vom Ausgangspunkt hab ich nichts geschrieben, der fällt bei mir genauso wie bei dir vom Himmel.

    Nein, er fällt nicht vom Himmel. Es ist der erste Bezeichner.

    Das stimmt hier zwar, kann aber nur eine Daumenregel und nicht die tatsächliche Regel sein, da der Bezeichner fehlen kann, z.B. in einem Cast-Ausdruck:

    (void (*(int, void (*)(int)))(int)) p
    


  • ridiculousfishs cdecl kommt mit mehr nicht zurecht.

    Z.B. Ellipsen, oder um mal beim Thema zu bleiben, einer alternativen Funktionszeigerschreibweise ohne Sternchen:

    void f(void (void));
    // oder anhand des Beispiels
    void (*signal(int, void (int)))(int);
    

    Oder C99. Und damit meine ich nicht mal so etwas exotisches:

    void g(int [static 4]);
    void h(int [const]);
    

  • Mod

    Bashar schrieb:

    SeppJ schrieb:

    Nein, er fällt nicht vom Himmel. Es ist der erste Bezeichner.

    Das stimmt hier zwar, kann aber nur eine Daumenregel und nicht die tatsächliche Regel sein, da der Bezeichner fehlen kann, z.B. in einem Cast-Ausdruck:

    (void (*(int, void (*)(int)))(int)) p
    

    Das ist schon die Regel für den Startpunkt, sofern sie anwendbar ist. Dahinter steckt natürlich irgendwo die Syntax der Sprache C, aus der man diese Regel herleiten kann. Wenn wir streng nach der Syntax, wie sie im Standard definiert wird, vorgehen, dann haben wir auch die Wahl, von Innen oder von Außen anzufangen, je nachdem, welche Art von Parser wir für die Grammatik nutzen wollen. Hier ist dann sogar wirklich von ganz Innen bzw. ganz außen gemeint:
    (void ((int, void ()(int)))(int)) // Wir erkennen an den umschließenden Klammern, dass es um einen Cast geht, die Klammern selber sind aber nicht weiter von Belang

    void                               // irgendwas void-artiges
     void (*                    )(   )  // Aha: Ein Zeiger auf eine void-Funktion (Dies könnte prinzipiell auch eine Funktion sein, die eine Funktion zurück gibt. Da das aber nicht erlaubt ist, lese ich den Stern gleich hier.)
     void (*                    )(int)  // Die void-Funktion nimmt einen int
     void (*(                  ))(int)  // Der Zeiger ist selber Rückgabe einer Funktion
     void (*(int, void         ))(int)  // Letztere Funktion nimmt einen int und etwas void-artiges
     void (*(int, void (*)(   )))(int)  // Siehe oben: Das void-Ding ist ein Zeiger auf eine void-Funktion
     void (*(int, void (*)(int)))(int)  // mit int als parameter
    

    oder

    int          // ein int
                          (int)         // der Argument von etwas ist
                       (*)(int)         // Der int ist Argument eines Funktionszeigers
                  void (*)(int)         // Diese Funktion gibt void zurück
             int, void (*)(int)         // auf gleicher Ebene ist auch ein int
            (int, void (*)(int))        // Beide sind Argumente einer Funktion
           *(int, void (*)(int))        // auf die gezeigt wird
          (*(int, void (*)(int)))(   )  // von einer anderen Funktion
     void (*(int, void (*)(int)))(int)  // mit void als Rückgabe und int als Argument
    

    Wobei mir die erste Methode intuitiver ist. Man sieht auch bei der letzten Zeile des zweiten Beispiels, dass ist im Prinzip zum int-Argument wieder abgestiegen bin. Außerdem ändert sich ständig der Basistyp. Oben ist von vornherein klar, dass am Ende irgendwie ein void (mit massiven Modifikationen) raus kommt, das bleibt auch die ganze Zeit so. Beim zweiten Beispiel erfährt man das erst ganz am Ende. Und man kann prinzipiell Mehrdeutigkeiten haben, wo "Innen" ist. Nein, die zweite Methode mag ich nicht.

    Ich kenne mich nicht gut genug mit Informatik aus, um zu sagen, ob es noch gemischte Parser für diese Art von Grammatik gibt. Ich kann nur von oben nach unten oder von unten nach oben durch die Ausdrücke gehen 🙂 .



  • SeppJ schrieb:

    Das ist schon die Regel für den Startpunkt, sofern sie anwendbar ist.

    Was genau meinst du mit die Regel? Eine die zufällig für alle anwendbaren Fälle zutrifft, oder gibt es noch andere Kriterien?


  • Mod

    Eine die zufällig für alle anwendbaren Fälle zutrifft.



  • OK, dann haben wir ja nichts inhaltliches zu diskutieren.


Anmelden zum Antworten