Il y a quelques pièces à cela qui permettent à toutes ces combinaisons d'opérateurs de fonctionner de la même manière.
La raison fondamentale pour laquelle tous ces travaux sont qu'une fonction (comme foo
) est implicitement convertible en un pointeur vers la fonction. C'est pourquoi void (*p1_foo)() = foo;
works: foo
est implicitement converti en un pointeur sur lui-même et ce pointeur est affecté à p1_foo
.
L'unaire &
, lorsqu'il est appliqué à une fonction, renvoie un pointeur vers la fonction, tout comme il donne l'adresse d'un objet lorsqu'il est appliqué à un objet. Pour les pointeurs vers des fonctions ordinaires, il est toujours redondant en raison de la conversion implicite de pointeur de fonction en fonction. En tout cas, c'est pourquoi ça void (*p3_foo)() = &foo;
marche.
L'unaire *
, lorsqu'il est appliqué à un pointeur de fonction, produit la fonction pointée, tout comme il renvoie l'objet pointé lorsqu'il est appliqué à un pointeur ordinaire sur un objet.
Ces règles peuvent être combinées. Considérez votre avant-dernier exemple **foo
:
- Tout d'abord,
foo
est implicitement converti en un pointeur sur lui-même et le premier *
est appliqué à ce pointeur de fonction, ce qui donne à foo
nouveau la fonction .
- Ensuite, le résultat est à nouveau implicitement converti en un pointeur sur lui-même et le second
*
est appliqué, ce qui donne à nouveau la fonction foo
.
- Il est ensuite implicitement converti à nouveau en un pointeur de fonction et affecté à la variable.
Vous pouvez ajouter autant de *
s que vous le souhaitez, le résultat est toujours le même. Le plus *
s, le plus joyeux.
Nous pouvons également considérer votre cinquième exemple &*foo
:
- Premièrement,
foo
est implicitement converti en un pointeur sur lui-même; l'unaire *
est appliqué, cédant à foo
nouveau.
- Ensuite, le
&
est appliqué à foo
, ce qui donne un pointeur vers foo
, qui est affecté à la variable.
La &
seule peut être appliqué à une fonction bien, et non à une fonction qui a été converti en un pointeur de fonction ( à moins, bien sûr, le pointeur de fonction est une variable, auquel cas le résultat est un pointeur à un pointeur- à une fonction; par exemple, vous pouvez ajouter à votre liste void (**pp_foo)() = &p7_foo;
).
C'est pourquoi &&foo
ne fonctionne pas: &foo
n'est pas une fonction; c'est un pointeur de fonction qui est une valeur r. Cependant, &*&*&*&*&*&*foo
cela fonctionnerait, tout comme &******&foo
, car dans ces deux expressions, le &
est toujours appliqué à une fonction et non à un pointeur de fonction rvalue.
Notez également que vous n'avez pas besoin d'utiliser l'unaire *
pour effectuer l'appel via le pointeur de fonction; les deux (*p1_foo)();
et (p1_foo)();
ont le même résultat, encore une fois à cause de la conversion de pointeur de fonction en fonction.
&foo
prend l'adresse defoo
, ce qui se traduit par un pointeur de fonction pointant versfoo
, comme on pourrait s'y attendre.&
opérateurs pour des objets: donnésint p;
,&p
donne un pointeur versp
et est une expression rvalue; l'&
opérateur requiert une expression lvalue.*
, le moins joyeux .&*
annulations mutuelles (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."
.Je pense qu'il est également utile de se rappeler que C n'est qu'une abstraction pour la machine sous-jacente et c'est l'un des endroits où cette abstraction fuit.
Du point de vue de l'ordinateur, une fonction n'est qu'une adresse mémoire qui, si elle est exécutée, exécute d'autres instructions. Ainsi, une fonction en C est elle-même modélisée comme une adresse, ce qui conduit probablement à la conception qu'une fonction est "la même" que l'adresse vers laquelle elle pointe.
la source
&
et*
sont des opérations idempotentes sur un symbole déclaré en fonction de C qui signifiefunc == *func == &func == *&func
et donc*func == **func
Cela signifie que le type
int ()
est identique àint (*)()
un paramètre de fonction et qu'une fonction définie peut être passée en tant que*func
,func
ou&func
.(&func)()
est le même quefunc()
. Lien Godbolt.Une fonction est donc vraiment une adresse,
*
et&
n'a donc aucune signification, et au lieu de produire une erreur, le compilateur choisit de l'interpréter comme l'adresse de func.&
sur un symbole déclaré en tant que pointeur de fonction obtiendra cependant l'adresse du pointeur (car il a maintenant un objectif distinct), tandis quefuncp
et*funcp
sera identiquela source