C-Syntax für Funktionen, die Funktionszeiger zurückgeben

Lesezeit: 7 Minuten

kevemans Benutzeravatar
Kevemann

Betrachten Sie die folgenden Typedefs:

typedef int (*f1)(float);
typedef f1 (*f2)(double);
typedef f2 (*f3)(int);

f2 ist eine Funktion, die einen Funktionszeiger zurückgibt. Das selbe mit f3sondern der Typ der Funktion, der Zeiger auf die f3 Rückkehr, ist f2. Wie kann ich definieren f3 ohne die typedefs? Ich weiß, dass Typedefs die sauberere und einfacher zu verstehende Art der Definition sind f3. Meine Absicht hier ist jedoch, die C-Syntax besser zu verstehen.

  • C oder C++? Weil C++ einige einfachere Möglichkeiten hat, solche Dinge auszudrücken.

    – Welpe

    25. Mai 2012 um 17:27 Uhr


  • cdecl ist dein Freund

    – Robᵩ

    25. Mai 2012 um 17:43 Uhr

  • cdecl.org übersetzt nur von der c-Syntax ins Englische. Es ist nicht sehr nützlich, um den anderen Weg zu gehen, da Sie Ihre Erklärung perfekt in der erwarteten Form formulieren müssen, und so funktioniert Englisch oder eine andere natürliche Sprache nicht.

    – Benjamin Lindley

    25. Mai 2012 um 17:48 Uhr


  • Einverstanden. Ich verwende eine bestimmte Strategie, um dabei zu helfen. Ich beginne mit einer einfachen Erklärung, von der ich weiß, dass sie gültig ist. explain erstellt dann ein englischsprachiges Formular für diese Erklärung. Ich kopiere und füge diese englischsprachige Form sorgfältig ein und erweitere sie, füge das Verb hinzu declareund, voilaich habe die Antwort, die ich suche.

    – Robᵩ

    25. Mai 2012 um 18:20 Uhr

  • Sie sollten verwenden std::function stattdessen … den gleichen Grund, den Sie verwenden sollten std::vector Anstatt von new/delete.

    – Invers

    28. Mai 2012 um 5:15 Uhr


Beginnen Sie mit Ihrer Erklärung für f1:

int (*f1)(float);

Sie wollen f2 ein Zeiger auf eine zurückkehrende Funktion sein f1also ersetzen f1 in der Erklärung oben mit der Erklärung für f2:

int (*      f1     )(float);
            |
      +-----+-----+
      |           |
      v           v
int (*(*f2)(double))(float);

Die Deklaration lautet wie

        f2                   -- f2
       *f2                   -- is a pointer
      (*f2)(      )          -- to a function
      (*f2)(double)          --   taking a double parameter
     *(*f2)(double)          --   returning a pointer
    (*(*f2)(double))(     )  --   to a function
    (*(*f2)(double))(float)  --     taking a float parameter
int (*(*f2)(double))(float)  --     returning int

Sie wiederholen den Vorgang für f3:

int (*(*    f2    )(double))(float);
            |
        +---+----+
        |        |
        v        v
int (*(*(*f3)(int))(double))(float);

was sich liest wie

          f3                           -- f3
         *f3                           -- is a pointer
        (*f3)(   )                     -- to a function
        (*f3)(int)                     --   taking an int parameter
       *(*f3)(int)                     --   returning a pointer
      (*(*f3)(int))(      )            --   to a function
      (*(*f3)(int))(double)            --     taking a double parameter
     *(*(*f3)(int))(double)            --     returning a pointer
    (*(*(*f3)(int))(double))(     )    --     to a function
    (*(*(*f3)(int))(double))(float)    --       taking a float parameter
int (*(*(*f3)(int))(double))(float);   --       returning int

  • +1, da dies wirklich die ursprüngliche Frage beantwortet, bei der es um die korrekte Syntax in C ging …

    – Macmade

    25. Mai 2012 um 18:30 Uhr


  • Diese Antwort sollte massenhaft gedruckt und in Informatikschulen verteilt werden. Dies ist der einfachste und beste Weg, um die Deklarationssyntax von Funktionszeigern zu verstehen. Vielen Dank, John Bode!

    – Mouradif

    22. Juli 2016 um 13:37 Uhr

  • Danke, Johannes! Ich frage mich, wo du das gelernt hast? Gibt es ein gutes Buch, das das erklärt?

    – Tim

    11. August 2017 um 20:07 Uhr

  • @Tim: Es geht hauptsächlich darum, die Deklarationssyntax zu verstehen, die in den meisten Referenzen leider nicht sehr gut erklärt wird. Es hat eines Tages einfach „Klick“ gemacht, und plötzlich machten haarige Erklärungen Sinn.

    – Johannes Bode

    12. August 2017 um 3:13 Uhr

  • @rplgn: Dies fällt aus dem Paradigma der Verwendung von Deklarationsmimics heraus. Die Struktur eines Deklarators entspricht der Struktur eines Ausdrucks desselben Typs im Code. Wenn Sie beispielsweise ein Array von Zeigern auf haben int und Sie möchten den Wert von erhalten i‘ten Element indizieren Sie in das Array und referenzieren das Ergebnis – x = *a[i];. Die Art der Ausdruck *a[i] ist intso die Deklaration von a ist geschrieben int *a[N];. Die Array- und Zeiger-Eigenschaften sind Funktionen des Deklarators, nicht des Typbezeichners.

    – Johannes Bode

    29. November 2021 um 16:30 Uhr


Benutzeravatar von Puppy
Welpe

In C++ kann dies durch das Wunder der Templates etwas einfacher werden.

#include <type_traits>

std::add_pointer<
    std::add_pointer<
        std::add_pointer<
            int(float)
        >::type(double)
    >::type(int)
>::type wow;

  • Die Frage bezog sich auf die C-Syntax, auch wenn der Beitrag mit C ++ getaggt war, also meiner Meinung nach nicht relevant ist … Aber übrigens eine nette Antwort. 🙂

    – Macmade

    25. Mai 2012 um 18:31 Uhr

  • Das ist vielleicht sauberer, als auf jegliche Hilfsmittel zu verzichten (weder noch typedef oder add_pointer), aber die typedef Ansatz ist immer noch sauberer (IMHO).

    – David Hamm

    25. Mai 2012 um 19:31 Uhr

  • @Macmade: Sie markieren C++, Sie erhalten eine C++-Antwort.

    – Welpe

    28. Mai 2012 um 20:17 Uhr

Dasselbe wie bei der Typedef, nur dass Sie Ihre Funktionsdefinition anstelle ihres Namens setzen.

Hier ist wie f2 würde aussehen wie:

typedef int (*(*f2)(double))(float);

Du kannst tun f3 als Übung, da ich davon ausgehe, dass es sich um eine Hausaufgabe handelt 😉

  • Die Antwort, die Sie geben, ist über eine Google-Suche leicht verfügbar. Allerdings ist die Syntax für mehr als eine Ebene (Funktion gibt Funktionszeiger zurückgibt Funktionszeiger zurück) nicht ganz einfach zu finden. Und die Hausaufgabenreferenz war ungerechtfertigt.

    – Kevemann

    25. Mai 2012 um 17:38 Uhr


  • Es ist genau das gleiche, ersetzen f2 in meiner Form mit deiner f3 Typdef. Ich bin mir nicht sicher, was Sie mit der Google-Suche meinen, es ist die grundlegende C-Syntax.

    – Blind

    25. Mai 2012 um 17:38 Uhr


  • Suchen Sie nach “Funktion, die Funktionszeiger zurückgibt”, und Sie werden die Antwort finden, die Sie gerade gepostet haben. Suchen Sie jedoch nach “Funktion, die den Funktionszeiger zurückgibt, der den Funktionszeiger zurückgibt”, und Sie würden die gesuchte Antwort nicht finden.

    – Kevemann

    25. Mai 2012 um 17:40 Uhr

  • @keveman: Er hat Ihnen eine sehr einfache Regel gegeben, die es Ihnen ermöglichen sollte, jede gewünschte Form zu konstruieren. Warum beschweren Sie sich, dass Google Ihnen auch einen Teil der Antwort gibt?

    – Benjamin Lindley

    25. Mai 2012 um 17:41 Uhr


  • @keveman- Diese Definition stimmt nicht mit Ihrer ursprünglichen Beschreibung überein. Geben Sie es bei cdecl.org ein und Sie erhalten eine detaillierte Beschreibung. Ich denke, Ihre Funktionsargumente müssen in der Reihenfolge sein int->double->float.

    – bta

    25. Mai 2012 um 17:50 Uhr


Einfach nicht. Es kann getan werden, aber es wird sehr verwirrend sein. Typedefs sind da, um das Schreiben und Lesen dieses kurzen Codes zu erleichtern.

Eine Funktion f die keine Argumente akzeptiert und einen Funktionszeiger zurückgibt int (*)(float) wäre wahrscheinlich so etwas wie (ungetestet):

int (*f())(float);

Dann müssen Sie für den Rest nur noch Klammern hinzufügen, bis es wie Lispeln aussieht.

Lerne das die Rechts-Links-Regel:

Die “Rechts-Links”-Regel ist eine völlig reguläre Regel zur Entschlüsselung von C-Deklarationen. Es kann auch nützlich sein, sie zu erstellen.

Inspiriert von John Bodes Antwort möchte ich sie grafisch darstellen. Es sollte Ihnen helfen zu verstehen, wie der Compiler es in einen AST umwandeln würde:

Funktion im Funktionsrückgabewert, rekursiv

Wir beginnen zunächst (1) mit dem gepackten Typ f3, wie durch “typedef f2 (*f3)(int);” bezeichnet. Es ist ein eigener Funktionstyp. Dann wird es einen Schritt weiter in (2) entpackt, wodurch die einschließenden geschweiften Klammern gesetzt werden, was im Wesentlichen die CFG-Erzeugung der “Ist-Funktion” der C-Sprache durchführt (ich gehe davon aus, dass der Leser eine grundlegende Vorstellung von der lexikalischen Analyse von Programmiersprachen hat). Wenn Sie sich (3) ansehen, sehen Sie, dass wir den „Ist-Funktion“-Lexierungsschritt dreimal rekursiv durchgeführt haben, wobei die drei Male durch die „Funktions“-Knoten im Diagramm sichtbar sind.

Wenn ich mich an den erforderlichen (aber vereinfachten) CFG-Produktionen in benutzerdefinierter Notation versuche, könnten sie so aussehen …

declaration -> underlying_type:specifier sp ( func-ptr-decl | specifier )
func-ptr-decl -> '(' sp '*' sp ( func-ptr-decl | specifier ) sp ')' sp '(' sp param-list sp ')'

specifier ist eine Zeichenfolge, die am besten als Variablennamen in der Programmiersprache C erklärt wird, sp ist eine optionale Zeichenfolge mit Leerzeichen, param-list vereinfacht als (möglicherweise leere) kommagetrennte Liste von Deklarationen.

In C wird jede Anweisung, die eine Variable oder einen Parameter einführt, als Deklaration bezeichnet. Deklarationen bestehen aus einem Ort/Namen, dem Typ der Daten und einem Initialisierer. In der gegebenen Frage haben wir Deklarationen, deren Typen Zeiger auf Funktionen sind. Der Zeiger-auf-Funktion-Typ ist bis zu dreimal rekursiv verschachtelt. Das Diagramm zeigt es so, wie die Pfeile auf Typen zeigen, was bedeutet, dass sie in anderen Typen verschachtelt sind.

Benutzeravatar von Inverse
Umgekehrt

Verwenden std::function:

typedef std::function<int(float)> f1;
typedef std::function<f1(double)> f2;
typedef std::function<f2(int)>    f3;

oder

typedef std::function<std::function<std::function<int(float)>(double)>(int)> f3;

1394460cookie-checkC-Syntax für Funktionen, die Funktionszeiger zurückgeben

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy