2010-09-14 7 views
30

Ich stieß auf dieses Stück Code und verlor völlig die Bedeutung.Was bedeutet dieses Codeelement? void (* Signal (int sig, void (* Funktion) (int))) (int);

Was ist eine detaillierte Erklärung für den Code in Zeile 2?

Ich weiß, dass void und int sind Typen, die * func ist ein Zeiger für eine Funktion, und die Klammern sind für die Priorität. Aber ich bekomme immer noch nicht das (* Signal ...), das (Int) und das Ganze zusammen. Je detaillierter, desto besser.

Wahrscheinlich habe ich die Bedeutung/Wirkung dieser Erklärung gekannt. Aber ich hatte noch einige Versuche, um mir zu helfen, zu verstehen, was los ist, wie folgt:

1 #include <signal.h> 
    2 void (*signal)(int sig, void (*func)(int)); 
    3 void (*signal)(int); // then void (signal)(int) again. 
    4 //void (*signal(int sig, void (*func)(int)))(int); //break this line into two lines above 
    5 
    6 int main(){} 

In dem obigen Code, ich brach void (*signal(int sig, void (*func)(int)))(int) in zwei Linien. Für die Linie 3, habe ich versucht, sowohl void (*signal)(int) und void (signal)(int), mit dem gleichen Fehler Ergebnis, das zeigte, dass ich versuche, signal zu deklarieren:

TestDeclaration.c:2: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here
TestDeclaration.c:3: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here

Jetzt weiß ich, beiden Versuche falsche Wege der Erklärung sind, aber warum sind sie falsch ? Warum ist die ursprüngliche Art der Erklärung KEINE Neudeklaration?

+3

+1 für die zeigen, dass Sie wirklich verstehen * einige * davon im Gegensatz zu * none * davon. – BoltClock

+3

Versuchen Sie http://www.cdecl.org –

+2

Lustig, cdecl.org gibt einen Syntaxfehler auf diesem. Kann jemand das erklären? –

Antwort

36

Es ist die Erklärung einer Funktion ein int und einen Zeiger auf eine Funktion (unter Rückkehr int void) und Zurückkehren einen Zeiger auf eine Funktion (unter int und Zurückkehren void) nehmen.


Erklärung, oder führen zu Interpretation

können Sie interpretieren, indem sie alles in Klammern als Einheit zu behandeln und dann nach innen arbeiten mit der „Erklärung folgt usage“ Regel.

void (* signal (int sig, void (* func) (int))) (int); Die Einheit in den Klammern sieht wie eine Funktion aus, die int und void zurückgibt.

Abisolieren des äußeren Teils weg:

*signal(int sig, void (*func)(int)) 

So nimmt signal einige Parameter und gibt etwas, das dereferenziert werden kann (aufgrund der führenden *) eine Funktion zur Bildung int nehmen und wiederkehr void.

Das bedeutet, dass signal eine Funktion ist, die einen Zeiger auf eine Funktion zurückgibt (unter int und void).

Betrachtet man die Parameter nimmt es eine int (d.h. sig) und void (*func)(int), die einen Zeiger auf eine Funktion (unter int und Zurückkehren void).

+9

+1. Sehr kurz. Es hilft, die Clockwise-/Spiralregel zu kennen, wenn komplexe C-Deklarationen analysiert werden sollen. - http://c-faq.com/decl/spiral.anderson.html –

+1

Persönlich bin ich kein Fan der Regel im Uhrzeigersinn/sprial. Ich bevorzuge es, von außen zu arbeiten. Ich weiß, dass es kein beliebter Ansatz ist, aber ich bin viel glücklicher, dass ich die Grammatikregeln richtig mit meinem Ansatz anwende. –

+0

Es ist witzig, dass die oben verlinkte Regelblattseite im Uhrzeigersinn dieselbe Deklaration enthält, nach der das OP fragt. –

0

Zurückgeben Zeiger auf eine Funktion, die nimmt ein:

  • ganze Zahl als erstes Argument Argument und
  • einen Zeiger auf eine Funktion (die eine int nimmt und void) als Argument als zweites Argument .

Und nimmt ein Integer-Argument.

7

Dies ist eines der klassischen Beispiele dafür, wie verschachtelte C-Deklarationen werden können.
diese Erklärung zu verstehen, hilft es in der Regel eine typedef vorstellen:

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

Der typedef deklariert einen Zeiger auf eine Funktion (einen int-Parameter zu nehmen und nichts zurückkehrt). Die Funktion signal kann nun als eine Funktion betrachtet werden, die zwei Parameter (einen int und einen Zeiger auf eine Funktion) und einen Zeiger auf eine Funktion zurückgibt.

Dies kann auch aus der ursprünglichen Deklaration abgeleitet werden, aber es erfordert ein wenig Übung. Der üblicher Weg ist die äußerste Einheit an der Kennung, die Namen zu starten (signal ist dieser Fall):

signal is a ...

Dann lesen Sie rechts, bis Sie eine unvergleichliche schließende Klammer oder das Ende der Erklärung finden: void (*signal(int sig, void (*func)(int))(int)

signal is a function taking ... returning ...

Jetzt können Sie wählen, ob zuerst die Parameter oder der Rückgabewert zuerst analysiert werden soll. Ich werde den Rückgabewert zuerst tun. Dazu lesen Sie rückwärts die passende öffnende Klammer zu finden: void (signal(/ ... */))(int)

`signal is a function taking ... returning a pointer to ...

hin und her auf diese Weise Lesen Sie bei aufeinanderfolgenden Schritten erhalten:

`signal is a function taking ... returning a pointer to a (function taking ... returning ...)

`signal is a function taking ... returning a pointer to a (function taking ... returning void)

`signal is a function taking ... returning a pointer to a (function taking an int and returning void)

`signal is a function taking two parameters: (an int) and (a pointer to a function taking an int and returning void), and returning a pointer to a (function taking an int and returning void)

0

Gedächtnisstütze ich vor vielen Jahren erstellt, das ist Unschätzbar beim Versuch, komplizierte Typen zu verstehen:

Remember these rules for C declares 
And precedence never will be in doubt 
Start with the Suffix, Proceed with the Prefix 
And read both sets from the inside, out. 

Außer, wo Klammern diesen Vorrang ändern, natürlich.

es auf diesen Fall Anwendung:

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

signal is: 
    [inside parentheses] 
    [suffix()] a function, whose arguments are 
    sig, which is [prefix int] an integer, and 
     func, which is: 
     [inside parentheses] 
      [no suffix within these parens] 
      [prefix *] a pointer to 
     [suffix()] a function, whose argument is 
      an int 
     [no more suffixes] 
     [prefix void] and which returns void 
     [no more prefixes] 
     [no more arguments] 
    [prefix *] And which returns a pointer to 
    [no more prefixes within these parens] 
    [suffix()] a function, whose argument is 
     an int 
    [no more suffixes] 
    [prefix void] and which returns void. 

Mit etwas Übung werden Sie zu dem Punkt, wo Sie alles, was im laufenden Betrieb tun können:

"Signal is function, whose arguments are: 
    sig, an integer, 
    and func, a pointer to a function whose argument is an int and which returns void 
... which returns a pointer to a function that takes int as an argument and returns void. 

(Tut mir leid der Fehler erstes Mal - ich bin aus der Übung.)

Ja, diese mnemonic (mit der implizierten "außer für Klammern, natürlich) funktioniert für alle C-Deklarationen, egal wie stark die Zeiger, Arrays, ein Die Funktionen sind gemischt.

Dies ist eine wirklich nützliche Fähigkeit zu haben, wenn Sie herausfinden möchten, wie der Code eines anderen funktioniert ... oder sogar etwas Eigenes herauszufinden, das Sie schon lange nicht mehr gesehen haben.

Aber, ja, der bessere Weg, mit allem fertig zu werden, von dem Sie nicht glauben, dass die Leute es auf einen Blick lesen können, ist, es in Schichten mit typedefs aufzubauen. Die Komponententypen sind wahrscheinlich selbst nützlich, und wenn man einen Schritt nach dem anderen nimmt, werden die Leute davon abgehalten, herauszufinden, welche Klammern mit welchen übereinstimmen. Sei nett zur nächsten Person, die deinen Code berührt!

Wenn Sie die Mnemonik nützlich finden, fühlen Sie sich frei, sie an anderer Stelle zu zitieren - geben Sie mir bitte Kredit als ihr Autor, bitte.

Übrigens gibt es auch "C Explainer" Werkzeuge, die C Delactions analysieren und die Konvertierung in englische Beschreibung für Sie übernehmen. Meine hieß CEX, aus offensichtlichen Gründen, aber viele andere existieren und Sie sollten in der Lage sein, einen zu finden, wenn Sie diese Fähigkeit nicht zu Wetware begehen wollen oder wenn Ihnen jemand etwas aushändigt, das wirklich zu hässlich ist.

+0

Und ja, die Tatsache, dass Start und Suffix, und Fortfahren und Präfix, erste Konsonanten teilen, ist absichtlich; das hilft dir, dich an die richtige Reihenfolge zu erinnern. – keshlam

1

Lassen Sie uns ein Beispiel nehmen, wie diese fiesen Erklärung verwendet werden könnte:

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

Ohne zu viel Ausführlichkeit, könnten wir sagen, dass „Signal“ ist eine Funktion mit zwei Parametern, die eine Funktion zurückgibt.

#include <stdio.h> 

// First function that could be returned by our principal function 
// Note that we can point to it using void (*f)(int) 
void car_is_red(int color) 
{ 
    printf("[car_is_red] Color %d (red) is my favorite color too !\n", color); 
} 

// Second function that could be returned by our principal function 
// Note that we can point to it using void (*f)(int) 
void car_is_gray(int color) 
{ 
    printf("[car_is_gray] I don't like the color %d (gray) either !\n", color); 
} 

// The function taken as second parameter by our principal function 
// Note that we can point to it using void (*func)(int) 
void show_car_type(int mod) 
{ 
    printf("[show_car_type] Our car has the type: %d\n",mod); 
} 

/* Our principal function. Takes two parameters, returns a function. */ 
void (* show_car_attributes(int color, void (*func)(int)))(int) 
{ 
    printf("[show_car_attributes] Our car has the color: %d\n",color); // Use the first parameter 

    int mod = 11; // Some local variable of our function show_car_attributes() 
    func(mod); // Call the function pointed by the second parameter (i.e. show_car_type()) 

    // Depending on color value, return the pointer to one of two functions 
    // Note that we do NOT use braces with function names 
    if (color == 1) 
     return car_is_red; 
    else 
     return car_is_gray; 
    } 


//main() function 
int main() 
{ 
    int color = 2; // Declare our color for the car 
    void (*f)(int); // Declare a pointer to a function with one parameter (int) 

    f = show_car_attributes(color, show_car_type); // f will take the return 
      // value of our principal function. Stated without braces, the 
      // parameter "show_car_types" is a function pointer of type 
      // void (*func)(int). 

    f(color); // Call function that was returned by show_car_attributes() 

    return 0; 
} 

Mal sehen, was ausgegeben wird:

Wenn color = 1

[show_car_attributes] Our car has the color: 1 
[show_car_type] Our car has the type: 11 
[car_is_red] Color 1 (red) is my favorite color too ! 

Wenn color = 2

[show_car_attributes] Our car has the color: 2 
[show_car_type] Our car has the type: 11 
[car_is_gray] I don't like the color 2 (gray) either !