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)();
}
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
:
foo
implizit in einen Zeiger auf sich selbst konvertiert, und der erste *
Wird auf diesen Funktionszeiger angewendet, wodurch erneut die Funktion foo
erhalten wird.*
Wird angewendet, was wiederum die Funktion foo
ergibt.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
:
foo
implizit in einen Zeiger auf sich selbst konvertiert. Der unäre *
wird angewendet und ergibt wieder foo
.&
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.
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.