Es gibt einige Teile, mit denen alle diese Operatorkombinationen auf die gleiche Weise funktionieren können.
Der grundlegende Grund, warum all diese Arbeiten funktionieren, ist, dass eine Funktion (wie foo
) implizit in einen Zeiger auf die Funktion konvertierbar ist. Aus diesem Grund void (*p1_foo)() = foo;
funktioniert works: foo
wird implizit in einen Zeiger auf sich selbst konvertiert und dieser Zeiger wird zugewiesen p1_foo
.
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 gewöhnliche Funktionen ist sie aufgrund der impliziten Umwandlung von Funktion in Funktionszeiger immer redundant. In jedem Fall void (*p3_foo)() = &foo;
funktioniert dies deshalb .
Das Unäre *
ergibt, wenn es auf einen Funktionszeiger angewendet wird, die Funktion, auf die gezeigt wird, genauso wie es das Objekt zeigt, 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
:
- Erstens
foo
wird implizit in einen Zeiger auf sich selbst konvertiert und der erste *
wird auf diesen Funktionszeiger angewendet, wodurch die Funktion foo
erneut erhalten wird.
- Dann wird das Ergebnis wieder implizit in einen Zeiger auf sich selbst konvertiert und das zweite
*
wird angewendet, was wiederum die Funktion ergibt foo
.
- Es wird dann implizit wieder in einen Funktionszeiger konvertiert und der Variablen zugewiesen.
Sie können beliebig viele *
s hinzufügen , das Ergebnis ist immer das gleiche. Je mehr *
s, desto besser.
Wir können auch Ihr fünftes Beispiel betrachten &*foo
:
- Erstens
foo
wird implizit in einen Zeiger auf sich selbst konvertiert; Das Unäre *
wird angewendet und gibt foo
wieder nach.
- Dann wird das auf
&
angewendet foo
, was einen Zeiger auf foo
ergibt, der der Variablen zugewiesen ist.
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 auf einen Zeiger. zu einer Funktion (zum Beispiel könnten Sie Ihrer Liste hinzufügen void (**pp_foo)() = &p7_foo;
).
Deshalb &&foo
funktioniert es nicht: &foo
ist keine Funktion; Es ist ein Funktionszeiger, der ein Wert ist. Dies &*&*&*&*&*&*foo
würde jedoch genauso funktionieren, &******&foo
da in beiden Ausdrücken das &
immer auf eine Funktion und nicht auf einen r-Wert-Funktionszeiger angewendet wird.
Beachten Sie auch, dass Sie das Unary nicht verwenden müssen, *
um den Aufruf über den Funktionszeiger zu tätigen. beide (*p1_foo)();
und (p1_foo)();
haben das gleiche Ergebnis, wiederum aufgrund der Umwandlung von Funktion in Funktionszeiger.
&foo
nimmt die Adresse vonfoo
, was dazu führt, dass ein Funktionszeigerfoo
wie erwartet auf zeigt.&
Operatoren für Objekte verketten : angegebenint p;
,&p
liefert einen Zeiger aufp
und ist ein rWert-Ausdruck; Der&
Operator benötigt einen lvalue-Ausdruck.*
, desto weniger fröhlich .&*
sich gegenseitig aufhebt (6.5.3.2):"The unary & operator yields the address of its operand."
/ - /"If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue."
.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. Eine Funktion in C wird also selbst als Adresse modelliert, was wahrscheinlich dazu führt, dass eine Funktion "dieselbe" ist wie die Adresse, auf die sie zeigt.
quelle
&
und*
sind idempotente Operationen an einem Symbol, das in C als Funktion deklariert ist, was bedeutetfunc == *func == &func == *&func
und daher*func == **func
Es bedeutet , dass der Typ
int ()
der gleiche wieint (*)()
als Funktionsparameter und eine definierte func kann als übergeben werden*func
,func
oder&func
.(&func)()
ist das gleiche wiefunc()
. Godbolt Link.Eine Funktion ist also wirklich eine Adresse
*
und&
hat keine Bedeutung. Anstatt einen Fehler zu erzeugen, interpretiert der Compiler sie als Adresse von func.&
Bei einem als Funktionszeiger deklarierten Symbol wird jedoch die Adresse des Zeigers abgerufen (da er jetzt einen separaten Zweck hat), währendfuncp
und*funcp
identisch istquelle