Warum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit der Anwendung des address-of-Operators auf den Funktionsnamen?

Lesezeit: 5 Minuten

Warum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit
WiSaGaN

Das ist interessant Die Verwendung des Funktionsnamens als Funktionszeiger entspricht der Anwendung des address-of-Operators auf den Funktionsnamen!

Hier ist das Beispiel.

typedef bool (*FunType)(int);
bool f(int);
int main() {
  FunType a = f;
  FunType b = &a; // Sure, here's an error.
  FunType c = &f; // This is not an error, though. 
                  // It's equivalent to the statement without "&".
                  // So we have c equals a.
  return 0;
}

Die Verwendung des Namens ist etwas, das wir bereits in Array kennen. Aber so etwas kann man nicht schreiben

int a[2];
int * b = &a; // Error!

Es scheint nicht mit anderen Teilen der Sprache übereinzustimmen. Was ist die Begründung für dieses Design?

Diese Frage erklärt die Semantik eines solchen Verhaltens und warum es funktioniert. Aber mich interessiert, warum die Sprache so entworfen wurde.

Interessanter ist, dass der Funktionstyp implizit in einen Zeiger auf sich selbst konvertiert werden kann, wenn er als Parameter verwendet wird, aber nicht in einen Zeiger auf sich selbst konvertiert wird, wenn er als Rückgabetyp verwendet wird!

Beispiel:

typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function 
                    // that has the type of bool(int).

1646266208 496 Warum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit
Michael Burr

Da Sie ausdrücklich nach der Begründung für dieses Verhalten fragen, ist hier das Nächste, was ich finden kann (aus dem ANSI C90-Begründungsdokument – http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):

3.3.2.2 Funktionsaufrufe

Zeiger auf Funktionen können entweder als verwendet werden (*pf)() oder als pf(). Das letztere Konstrukt, das im Basisdokument nicht sanktioniert ist, taucht in einigen aktuellen Versionen von C auf, ist eindeutig, macht keinen alten Code ungültig und kann eine wichtige Abkürzung sein. Die Abkürzung ist nützlich für Pakete, die nur einen externen Namen präsentieren, der eine Struktur voller Zeiger auf Objekte und Funktionen bezeichnet: Elementfunktionen können aufgerufen werden als graphics.open(file) anstatt
(*graphics.open)(file). Die Behandlung von Funktionsbezeichnern kann zu einigen merkwürdigen, aber gültigen syntaktischen Formen führen. Angesichts der Erklärungen:

int f ( ) , ( *pf ) ( ) ; 

dann sind alle folgenden Ausdrücke gültige Funktionsaufrufe:

( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();

Der erste Ausdruck in jeder Zeile wurde im vorherigen Absatz besprochen. Der zweite ist der konventionelle Gebrauch. Alle nachfolgenden Ausdrücke nutzen die implizite Umwandlung eines Funktionsbezeichners in einen Zeigerwert in fast allen Ausdruckskontexten. Das Komitee sah keinen wirklichen Schaden darin, diese Formulare zuzulassen; Verbotsformulare wie (*f)()während noch erlaubt *a (zum int a[])schien einfach mehr Mühe zu geben , als es wert war .

Grundsätzlich wurde die Äquivalenz zwischen Funktionsbezeichnern und Funktionszeigern hinzugefügt, um die Verwendung von Funktionszeigern etwas bequemer zu machen.

  • Ich nehme an, das lag daran, dass Funktionsbezeichner für menschliche Zwecke wirklich syntaktischer Zucker sind. Ein Funktionsname ist eigentlich nur eine Bezeichnung auf einer Speicheradresse. Die Umgebung sieht eigentlich keine separaten Funktionen und Zeiger darauf – sie sieht nur eine Anweisungsverzweigung zu einer Adresse. Dies unterscheidet sich von Objekten, für die eine Umgebung unterschiedliche Adressierungsmodi verwendet (direkt oder indirekt).

    – Tobi

    21. September 2016 um 10:45 Uhr


Warum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit
Jerry Sarg

Es ist eine Funktion, die von C geerbt wurde.

In C ist es vor allem deshalb erlaubt, weil es nicht viel mehr gibt, was der Name einer Funktion an sich bedeuten könnte. Alles, was Sie mit einer tatsächlichen Funktion tun können, ist sie aufzurufen. Wenn Sie es nicht anrufen, die nur was Sie tun können, ist die Adresse zu nehmen. Da es keine Mehrdeutigkeit gibt, folgt auf einen Funktionsnamen kein a ( Um einen Aufruf der Funktion anzuzeigen, wird der Name als Adresse der Funktion ausgewertet.

Das ist tatsächlich etwas ähnlich zu einem anderen Teil der Sprache – der Name eines Arrays ergibt die Adresse des ersten Elements des Arrays, außer unter einigen ziemlich begrenzten Umständen (als Operand von verwendet & oder sizeof).

Da C dies erlaubte, tut C++ dies auch, vor allem, weil das Gleiche gilt: Das einzige, was Sie mit einer Funktion tun können, ist, sie aufzurufen oder ihre Adresse zu nehmen, also wenn dem Namen kein a folgt ( um einen Funktionsaufruf zu kennzeichnen, wird der Name ohne Mehrdeutigkeit zur Adresse ausgewertet.

  • The only things you can do with a function are call it or take its address — Sie könnten es auch verwenden, um eine Referenz auf eine Funktion zu initialisieren.

    – Kubbi

    28. August 2012 um 3:44 Uhr

  • @Cubbi: C hat keine Referenzen, und der springende Punkt dieser Antwort ist, dass das Verhalten von C geerbt wird.

    – jamesdlin

    21. September 2016 um 12:01 Uhr

  • Technisch gesehen ergibt ein Funktionsname in C die Adresse der Funktion sogar wenn es wird in einem Funktionsaufrufausdruck verwendet; Der “Ausdruck, der die aufgerufene Funktion bezeichnet” muss einen Zeiger-auf-Funktion-Typ haben, und um dies zu berücksichtigen, wird ein bloßer Funktionsname als Zeiger auf die Funktion ausgewertet, “außer wenn es der Operand von ist sizeof oder unär &” (C99 §§ 6.5.2.2p1 bzw. 6.3.2.1p4).

    – zol

    21. September 2016 um 12:52 Uhr


Warum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit
Jess gut

Bei Arrays gibt es keinen Zeigerabfall, wenn der address-of-Operator verwendet wird:

int a[2];
int * p1 = a;      // No address-of operator, so type is int*
int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]

Dies ist sinnvoll, da Arrays und Zeiger unterschiedliche Typen sind und es beispielsweise möglich ist, Referenzen auf Arrays zurückzugeben oder Referenzen auf Arrays in Funktionen zu übergeben.

Welcher andere Typ könnte jedoch bei Funktionen möglich sein?

void foo(){}
&foo; // #1
foo;  // #2

Stellen wir uns vor, dass nur #2 den Typ angibt void(*)()was wäre die Art von &foo sein? Es gibt keine andere Möglichkeit.

  • Können Sie bitte den Typ erklären (*)[2]. Ich weiß nicht, ob ich es mir richtig vorstellen kann, aber ist es wie der dritte Wert des Arrays (Zeiger) p2? Und sein Wert ist auch Adresse? Als (*)[2] = &. Korrigieren Sie meine Vorstellung vom Wert des Zeigers (*)[2]aber im Grunde ist es so, nicht wahr?

    – Hirte

    10. Januar 2020 um 21:36 Uhr

  • @Hirt: int (*)[2] ist ein Zeiger auf ein Array von 2 intS. Diese Seite gibt eine ausführliche Erklärung.

    – Jess Gut

    11. Januar 2020 um 1:04 Uhr

  • zweite oder dritte? Sie haben nicht vom Nullindex aus gezählt

    – Hirte

    11. Januar 2020 um 10:43 Uhr


  • Hast du meinen Link gelesen? int (*)[2] ist nicht Indizierung in ein Array. Es sagt, dass das Array a hat Größe von 2.

    – Jess Gut

    11. Januar 2020 um 10:57 Uhr

917580cookie-checkWarum ist die Verwendung des Funktionsnamens als Funktionszeiger gleichbedeutend mit der Anwendung des address-of-Operators auf den Funktionsnamen?

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

Privacy policy