wake-up-neo.com

Warum funktionieren Funktionszeigerdefinitionen mit einer beliebigen Anzahl von kaufmännischen Und-Zeichen '&' oder Sternchen '*'?

Warum machen die folgenden Arbeiten?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}
201
Jimmy

Es gibt einige Punkte, die es ermöglichen, dass alle diese Kombinationen von Operatoren auf die gleiche Weise funktionieren.

Der Hauptgrund für all diese Arbeiten ist, dass eine Funktion (wie foo) implizit in einen Zeiger auf die Funktion konvertierbar ist. Deshalb funktioniert void (*p1_foo)() = foo;: foo wird implizit in einen Zeiger auf sich selbst konvertiert und dieser Zeiger wird p1_foo Zugewiesen.

Das unäre & Liefert, wenn es auf eine Funktion angewendet wird, einen Zeiger auf die Funktion, genau wie es die Adresse eines Objekts liefert, wenn es auf ein Objekt angewendet wird. Bei Zeigern auf normale Funktionen ist sie aufgrund der impliziten Funktion-zu-Funktion-Zeiger-Konvertierung immer redundant. In jedem Fall funktioniert void (*p3_foo)() = &foo; aus diesem Grund.

Das unäre * Ergibt, wenn es auf einen Funktionszeiger angewendet wird, die Funktion, auf die gezeigt wird, genauso wie es das Objekt ergibt, auf das gezeigt wird, wenn es auf einen gewöhnlichen Zeiger auf ein Objekt angewendet wird.

Diese Regeln können kombiniert werden. Betrachten Sie Ihr vorletztes Beispiel, **foo:

  • Zuerst wird foo implizit in einen Zeiger auf sich selbst konvertiert, und der erste * Wird auf diesen Funktionszeiger angewendet, wodurch erneut die Funktion foo erhalten wird.
  • Dann wird das Ergebnis wieder implizit in einen Zeiger auf sich selbst konvertiert und das zweite * Wird angewendet, was wiederum die Funktion foo ergibt.
  • Es wird dann implizit wieder in einen Funktionszeiger konvertiert und der Variablen zugewiesen.

Sie können beliebig viele * Hinzufügen, das Ergebnis ist immer dasselbe. Je mehr *, Desto besser.

Wir können auch Ihr fünftes Beispiel betrachten, &*foo:

  • Zunächst wird foo implizit in einen Zeiger auf sich selbst konvertiert. Der unäre * wird angewendet und ergibt wieder foo.
  • Dann wird & Auf foo angewendet, was einen Zeiger auf foo ergibt, der der Variablen zugewiesen wird.

Das & Kann jedoch nur auf eine Funktion angewendet werden, nicht auf eine Funktion, die in einen Funktionszeiger konvertiert wurde (es sei denn, der Funktionszeiger ist natürlich eine Variable. In diesem Fall ist das Ergebnis ein Zeiger. to-a-pointer-to-a-function, zum Beispiel könnten Sie Ihrer Liste void (**pp_foo)() = &p7_foo;) hinzufügen.

Deshalb funktioniert &&foo Nicht: &foo Ist keine Funktion; Es ist ein Funktionszeiger, der ein Wert ist. &*&*&*&*&*&*foo Würde jedoch genauso funktionieren wie &******&foo, Da in beiden Ausdrücken der & Immer auf eine Funktion und nicht auf einen rWert-Funktionszeiger angewendet wird.

Beachten Sie auch, dass Sie nicht den unären * Verwenden müssen, um den Aufruf über den Funktionszeiger zu tätigen. Sowohl (*p1_foo)(); als auch (p1_foo)(); führen aufgrund der Konvertierung von Funktion zu Funktionszeiger zu demselben Ergebnis.

208
James McNellis

Ich denke, es ist auch hilfreich, sich daran zu erinnern, dass C nur eine Abstraktion für die zugrunde liegende Maschine ist und dies einer der Orte ist, an denen diese Abstraktion leckt.

Aus der Sicht des Computers ist eine Funktion nur eine Speicheradresse, die, wenn sie ausgeführt wird, andere Anweisungen ausführt. Daher wird eine Funktion in C selbst als Adresse modelliert, was wahrscheinlich dazu führt, dass eine Funktion "dieselbe" wie die Adresse ist, auf die sie verweist.

6
madumlao