Komplexe C-Deklaration

70

Ich habe gerade einen Code im Internet durchgesehen und Folgendes gefunden:

float * (*(*foo())[SIZE][SIZE])()

Wie lese ich diese Erklärung? Gibt es bestimmte Regeln zum Lesen derart komplexer Erklärungen?

Kaunteya
quelle
22
Spiralregel
Luchian Grigore
16
cdecl.org
Juanchopanza
Verwenden Sie cdecl.org
999k
22
Sie finden den Programmierer, der es geschrieben hat, und lassen sich von ihm sagen, was es bedeutet. Dann sagen Sie Ihrem Chef, er solle ihn entlassen, und Sie bestehen darauf, dass Sie niemals an Code arbeiten werden, den er geschrieben hat.
Pete Becker
Möglicherweise duplizieren stackoverflow.com/questions/3706704/…
Alex

Antworten:

119

Ich habe das schon eine Weile nicht mehr gemacht!

Beginnen Sie mit foound gehen Sie nach rechts.

float * (*(*foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente ...

Kann nicht richtig gehen, da es eine schließende Klammer gibt. Geh nach links:

float * (*(* foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente, die einen Zeiger zurückgeben

Ich kann nicht weiter nach links gehen, also kreuzen wir die Klammern und gehen wieder nach rechts

float * (*(* foo())[SIZE][SIZE])() float * (*(* foo())[SIZE][SIZE])() float * (*(* foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente, die einen Zeiger auf ein Array von SIZE-Arrays von SIZE zurückgibt ...

Schließende Klammer erreicht, wieder links, um ein Zeigersymbol zu erreichen:

float * (*(* foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente, die einen Zeiger auf ein Array von SIZE-Arrays von SIZE-Zeigern auf ... zurückgibt.

Wieder die linke Klammer, also kreuzen wir sie und gehen wieder nach rechts:

float *( *(* foo())[SIZE][SIZE])() float *( *(* foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente, die einen Zeiger auf ein Array von SIZE-Arrays mit SIZE-Zeigern auf eine Funktion ohne Argumente zurückgibt ...

Und bis zum Ende gelassen

float * ( *(* foo())[SIZE][SIZE])()

foo ist eine Funktion ohne Argumente, die einen Zeiger auf ein Array von SIZE-Arrays mit SIZE-Zeigern auf eine Funktion zurückgibt, ohne Argumente, die einen Zeiger auf float zurückgeben


Und wer auch immer das geschrieben hat, bitte bringen Sie ihm bei, Folgendes zu verwenden typedef:

// Function that returns a pointer to float
typedef float* PFloatFunc ();

// Array of pointers to PFloatFunc functions
typedef PFloatFunc* PFloatFuncArray2D[SIZE][SIZE];

// Function that returns a pointer to a PFloatFuncArray2D
PFloatFuncArray2D* foo();
Kos
quelle
57
+1 für "Und wer auch immer das geschrieben hat, bitte bringen Sie ihm bei, typedef zu verwenden"
Dan spielt
3
Beachten Sie, dass der Teil "Keine Argumente" nur für C ++ korrekt ist. für C bedeutet es "nicht spezifizierte Argumentliste" (aber es kann keine variable Funktion sein, da diese einen vollständigen Prototyp im Umfang haben müssen, selbst in C).
Jonathan Leffler
100

Standardregel: Finden Sie die Kennung ganz links und arbeiten Sie sich heraus, merken Sie sich das []und ()binden Sie vorher *:

            foo                      -- foo
            foo()                    -- is a function
           *foo()                    -- returning a pointer
          (*foo())[SIZE]             -- to a SIZE-element array
          (*foo())[SIZE][SIZE]       -- of SIZE-element arrays
         *(*foo())[SIZE][SIZE]       -- of pointers
        (*(*foo())[SIZE][SIZE])()    -- to functions
      * (*(*foo())[SIZE][SIZE])()    -- returning pointers
float * (*(*foo())[SIZE][SIZE])();   -- to float

Stellen Sie sich vor, Sie haben eine Reihe von Funktionen, die Zeiger zurückgeben auf float:

float *quux();
float *bar();
float *bletch();
float *blurga();

Angenommen, Sie möchten sie in einer 2x2-Tabelle speichern:

float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};

tabist ein SIZE x SIZE-Array von Zeigern auf Funktionen, auf die Zeiger zurückgegeben werden float.

Nun wollen wir entscheiden, dass eine Funktion einen Zeiger auf diese Tabelle zurückgibt:

float *(*(*foo())[SIZE][SIZE])()
{
  static float *(*tab[SIZE][SIZE])() = {quux, bar, bletch, blurga};
  return &tab;
}

Beachten Sie, dass Sie mehrere Funktionen haben können, die Tabellen mit unterschiedlichen Funktionen erstellen oder dieselben Funktionen unterschiedlich organisieren:

float *(*(*qwerbl())[SIZE][SIZE])()
{
  static float *(*tab[SIZE][SIZE])() = {blurga, bletch, bar, quux};
  return tab;
}

Das ist der einzige Grund, warum ich mir so etwas vorstellen kann. Sie sollten solche Typen nicht sehr oft in freier Wildbahn sehen (obwohl sie gelegentlich auftauchen und ich mich schuldig gemacht habe, etwas ähnlich Abscheuliches geschrieben zu haben).

John Bode
quelle
1
qwerbl? Sie haben fast keine generischen Variablennamen mehr, oder :-) +1 aus Gründen. Und ich bin mir sicher, dass "tief verwandte" Typen ziemlich häufig vorkommen, aber normalerweise auch Strukturen oder Klassen beinhalten, wodurch das Namensproblem auf natürliche Weise verschwindet - wie hier bei der Einführung einiger Typedefs.
Kos
@ Kos: yup. Ich hatte noch keine RDA für Koffein und konnte mir nichts Besseres einfallen lassen.
John Bode
1
Wikipedia hat eine Liste metasyntaktischer Variablen, damit Ihnen nicht die Puste ausgeht: foo, bar, baz, qux, quux, corge, grault, garply, waldo, fred, plugh, xyzzy, thud.
Waleed Khan
1
@WaleedKhan Durchsuchen Sie die Referenzen und Sie landen in der Jargon-Datei. catb.org/jargon/html/M/metasyntactic-variable.html
Kos
6

Laut cdecl.org

Deklarieren Sie foo als Funktionsrückgabezeiger auf Array. GRÖSSE des Arrays GRÖSSE des Zeigers auf Funktionsrückgabezeiger auf Float

Verwenden Sie die Spiralregel von Luchian Grigore, wenn Sie sie von Hand dekodieren möchten.

Clement J.
quelle
4

Am besten konvertieren Sie hier in eine Reihe von Typedefs.

typedef float * fnReturningPointerToFloat();
typedef fnReturningPointerToFloat* fnArray[SIZE][SIZE];
fnArray* foo();
QuentinUK
quelle
2
Ich habe über eine Minute gebraucht, um das zu tun.
QuentinUK
3

Im Allgemeinen könnten Sie cdecl.org ausprobieren, aber Sie müssten es ersetzenSIZE

Angenommen, Sie tauschen SIZEgegen 12, dann erhalten Sie:

deklarieren Sie foo als Funktionsrückgabezeiger auf Array 12 von Array 12 von Zeiger auf Funktionsrückgabezeiger auf float

Ich bin mir nicht sicher, ob dir das wirklich hilft!

Zwei Beobachtungen hier:

  1. Ich vermute, dass dieser Code keinen Kommentar daneben hatte, der den Zweck des Codes erläuterte (dh nicht die technische Erklärung dessen, was er ist, sondern was er aus funktionaler / geschäftlicher Sicht erreicht), wenn ein Programmierer ihn verwenden muss Etwas so Komplexes wie dieses, sollten sie gut genug sein, um zukünftigen Betreuern zu erklären, welchen Zweck es erfüllt.
  2. Sicherlich gibt es in C ++ offensichtlichere und wahrscheinlich sicherere Möglichkeiten, dasselbe zu erreichen.
Komponente 10
quelle
3
Dies liegt an der "GRÖSSE", Sie müssen stattdessen das Literal verwenden (und es selbst durch die Konstante danach ersetzen).
Clement J.
GRÖSSE durch eine Zahl ersetzen !!
Kaunteya
2

Dieses Dokument gibt mir den besten Hinweis darauf, wie ich eine C-Deklaration einfach erstellen kann:

http://c-faq.com/decl/spiral.anderson.html

Es gibt drei einfache Schritte:

  • Beginnen Sie mit dem unbekannten Element und bewegen Sie sich spiralförmig / im Uhrzeigersinn. Wenn Sie auf folgende Elemente stoßen, ersetzen Sie diese durch die entsprechenden englischen Aussagen:

    • [X]oder [] => Array X Größe von ... oder Array undefinierte Größe von ...

    • (type1, type2) => Funktion übergibt Typ1 und Typ2 und gibt ...

    • * => Zeiger auf ...

  • Machen Sie dies spiralförmig / im Uhrzeigersinn, bis alle Token bedeckt sind.

  • Lösen Sie immer zuerst alles in Klammern!

Beispiel:

             +-------+
             | +-+   |
             | ^ |   |
        char *str[10];
         ^   ^   |   |
         |   +---+   |
         +-----------+

Question we ask ourselves: What is str?

``str is an...

- We move in a spiral clockwise direction starting with `str' and the first character we see is a `[' so, that means we have an array, so...
  ``str is an array 10 of...

- Continue in a spiral clockwise direction, and the next thing we encounter is the `*' so, that means we have pointers, so...
  ``str is an array 10 of pointers to...

- Continue in a spiral direction and we see the end of the line (the `;'), so keep going and we get to the type `char', so...
``str is an array 10 of pointers to char''

We have now ``visited'' every token; therefore we are done!
Maël Nison
quelle
2

Obwohl die meisten der obigen Antworten gut genug sind, fehlen vollständige Regeln für die Dekodierung komplexer C-Deklarationen. Ich habe unten einen vollständigen Satz bereitgestellt, um jede komplexe C-Deklaration zu dekodieren. Dieses Regelwerk basiert tatsächlich auf der Priorität von Operatoren. Regeln wie rechte Spiralregeln können als Abkürzung für diese Regeln angesehen werden.

Vor allem müssen wir einige Dinge wissen, um die Deklaration zu entschlüsseln.

'Grundtyp' einer Erklärung

Die AC-Deklaration hat immer nur einen grundlegenden Deklarationstyp. Dies ist die am weitesten links stehende Position einer Erklärung. Zum Beispiel -

  • int a - Grundtyp ist 'int'
  • float *p - Grundtyp ist 'float'
  • char (*p)[3] - Grundtyp ist 'char'

Vorrang und Assoziativität

Als nächstes müssen wir die Rangfolge der wissen (), []und *- Dereferenzierungsoperator

  1. (), []- Assoziativität ist von links nach rechts
  2. * - Assoziativität ist von rechts nach links

Phrase, die jedem der obigen Operatoren entspricht

Als nächstes müssen wir die decodierte Phrase kennen, die jedem Operator entspricht. Beispiele werden diesen Punkt klar machen.

  • () - Funktionsrückgabe
  • [SIZE] - Array von GRÖSSE
  • * - Zeiger auf

Befolgen Sie nun die folgenden Regeln, um die Deklaration zu dekodieren

  1. Schreiben Sie immer zuerst den Variablennamen, gefolgt von einem 'ist'.

    Zum Beispiel -

  • int a- a ist ...
  • float *p- p ist ...
  • char (*p)[3]- p ist ...
  1. Beenden Sie immer mit dem Basistyp

    Zum Beispiel -

  • int a- a ist ... int
  • float *p- p ist ... schweben
  • char (*p)[3]- p ist ... char
  1. Füllen Sie nun den Teil dazwischen mit den folgenden Unterschritten

    • Folgen Sie ausgehend vom Namen der Priorität und Assoziativität des Operators, um den Operator mit der nächsthöheren Priorität auszuwählen, und hängen Sie die entsprechende Phrase an den mittleren Teil der decodierten Zeichenfolge an.

    • Wiederholen Sie den obigen Unterschritt für die verbleibende Deklaration, bis der Dekodierungsprozess abgeschlossen ist

ANMERKUNG 1: Der Einfachheit halber habe ich die Argumente der Funktion ignoriert, sie können jedoch direkt nach der entsprechenden Phrase eingefügt werden ().

ANMERKUNG 2: Parenthesis ( ()) ändert die Prioritätsreihenfolge von Operatoren wie bei jedem arithmetischen Ausdruck.

ANMERKUNG 3: Sie können in der dekodierten Deklaration Klammern verwenden, um die Lesbarkeit zu verbessern (ich habe dies in einigen Beispielen unten getan). Stellen Sie sich jede Menge von () als eine Einheit vor.

ANMERKUNG 4: Ein n-dimensionales Array ist tatsächlich ein Array von Arrays von ... (n-1-mal) Arrays . Zum Beispiel ist A [2] [3] - A ein Array von 2 (Array von 3 int), dh A ist ein Array von 2 Elementen, wobei jedes Element ein Array ist, das 3 ganze Zahlen enthält

Beispiele

  • int a- a ist int
  • float *p- p ist der Zeiger auf float
  • char (*p)[3] - p ist ein Zeiger auf ein Array mit 3 Zeichen

Einige komplexe Deklarationsbeispiele

  1. int **p[10] - p ist ein Array von 10 Zeigern auf Zeiger auf int
  2. int (*p)[10] - p ist ein Zeiger auf ein Array von 10 int
  3. int *p(char *a) - p ist ein Funktionsrückgabezeiger auf int
  4. int (*p(char*a))[10] - p gibt die Funktion zurück (Zeiger auf (Array von 10 int))
  5. int *(*p)() - p ist Zeiger auf (Funktionsrückgabe (Zeiger auf int))
  6. int (*p()[20])[10] - p ist eine Funktionsrückgabe (Array von 20 (Zeiger auf (Array von 10 int)))

Dieser Regelsatz kann auch mit verwendet werden const- const qualifier ändert den Begriff links davon (falls vorhanden), andernfalls ändert er den Begriff rechts davon.

  1. const int *p[10] - p ist ein Array von 10 Zeigern auf int const
  2. int const *p[10] - p ist ein Array mit 10 Zeigern auf const int (dies entspricht dem 7. Beispiel).
  3. int *const p[10] - p ist ein Array von 10 const Zeigern auf int

Nun ein wirklich komplexes Beispiel, das in der Praxis nirgendwo Verwendung findet, aber dennoch zur Demonstration des Dekodierungsprozesses verwendet werden kann

  1. char *(*(**foo[][8])())[] - foo ist ein Array von (Array von 8 (Zeiger auf (Zeiger auf (Rückgabe der Funktion) (Zeiger auf (Array von (Zeiger auf char))))))

Nun endlich Dekodierung für die in der Frage gegebene Erklärung

float * (*(*foo())[SIZE][SIZE])() - foo gibt die Funktion zurück (Zeiger auf (Array von SIZE (Array von SIZE) (Zeiger auf (Funktion gibt Zeiger auf float zurück)))

Das Folgende ist der Link für den Artikel, aus dem ich diesen Dekodierungsprozess gelesen habe

Beispiel 10 wurde diesem Artikel entnommen

http://www.unixwiz.net/techtips/reading-cdecl.html

Abhi
quelle
1

von http://cdecl.org/

Deklarieren Sie foo als Funktionsrückgabezeiger auf Array. GRÖSSE des Arrays GRÖSSE des Zeigers auf Funktionsrückgabezeiger auf Float

David Ranieri
quelle