web-dev-qa-db-fra.com

Pointeurs de fonction et adresse d'une fonction

J'ai donc pensé que lors de la création de pointeurs de fonction, vous n'avez pas besoin du operator & pour obtenir l'adresse de la fonction initiale:

#include <stdio.h>

double foo (double x){
    return x*x;
}

int main () {

    double (*fun1)(double) = &foo;
    double (*fun2)(double) =  foo;

    printf("%f\n",fun1(10));
    printf("%f\n",fun2(10));

    printf("fun1 = %p \t &foo = %p\n",fun1, &foo);
    printf("fun2 = %p \t  foo = %p\n",fun2,  foo);       

    int a[10];

    printf("  a = %p \n &a = %p  \n",a,&a);

    return 0;
}

sortie:

>./a.out 
100.000000
100.000000
fun1 = 0x4004f4      &foo = 0x4004f4
fun2 = 0x4004f4       foo = 0x4004f4
  a = 0x7fff26804470 
 &a = 0x7fff26804470 

Ensuite, j'ai réalisé que cela est également vrai pour les tableaux, ce qui signifie que si vous avez int a[10] les deux a et &a pointe vers le même emplacement. Pourquoi cela avec des tableaux et des fonctions? L'adresse est-elle enregistrée dans un emplacement mémoire qui a la même adresse que la valeur (adresse) qui y est enregistrée?

47
GradGuy

Étant donné int a[10], a et &a Renvoient la même adresse, oui, mais leurs types sont différents.

a est de type int[10]. Lorsqu'il est implicitement converti en type de pointeur, le pointeur est de type int* Et pointe vers l'élément initial du tableau. &a Est de type int (*)[10] (c'est-à-dire un pointeur vers un tableau de dix entiers). Parce qu'il ne peut y avoir de remplissage dans un tableau, ils produisent tous deux des pointeurs avec la même valeur , mais les pointeurs ont des valeurs différentes types .

Les fonctions sont similaires aux tableaux, mais pas entièrement identiques. Votre fonction foo est de type double(double). Chaque fois que foo est utilisé dans une expression et n'est pas l'opérande de l'opérateur unaire &, Il est implicitement converti en un pointeur sur lui-même, qui est de type double(*)(double).

Ainsi, à toutes fins pratiques, le nom d'une fonction et un pointeur vers la même fonction sont interchangeables. Il y a quelques subtilités, dont je discute toutes dans une réponse à "Pourquoi toutes ces définitions de pointeurs de fonctions folles fonctionnent-elles toutes? Que se passe-t-il vraiment?" (Cette question a été posée à propos de C++, mais le les règles pour les fonctions non membres en C++ sont les mêmes que pour les fonctions en C.)

66
James McNellis

Non, il n'y a pas de stockage supplémentaire dédié au pointage vers la fonction/tableau.

Avec la plupart des variables variable_name a une signification autre que l'obtention de l'adresse de cette variable, vous devez donc utiliser &variable pour obtenir l'adresse.

Avec une fonction ou un tableau, function_name (en soi, non suivi de parenthèses) n'a pas d'autre sens, donc il n'y avait aucun problème à l'interpréter comme prenant l'adresse de la fonction.

De même en sens inverse: un pointeur normal doit être explicitement déréférencé, mais pas un pointeur vers une fonction (encore une fois, car il n'y a pas d'autre interprétation raisonnable), donc donné un pointeur vers une fonction comme:

int (*func)(param_list);

Les éléments suivants sont équivalents les uns aux autres - les deux appellent la fonction que func pointe vers:

(*func)(params);

func(params);
8
Jerry Coffin

Fondamentalement, puisque le nom de la fonction est "connu" pour être une fonction, le & n'est pas strictement nécessaire. Ce comportement est le même pour les tableaux. Rappelez-vous qu'une fonction elle-même n'est pas une variable, elle se comporte donc un peu différemment que vous pourriez vous y attendre parfois. Si vous avez la 2e édition de K&R, vous pouvez consulter la section 5.11 sur les pointeurs vers les fonctions, ou le manuel de référence à la fin,

Section A7.1 Génération de pointeur: si le type d'une expression ou d'une sous-expression est "tableau de T" pour un type T, la valeur de l'expression est un pointeur vers le premier objet du tableau et le type de l'expression est modifié en "pointeur vers T." Cette conversion n'a pas lieu de l'expression est l'opérande de l'opérateur unaire &, ... De même, une expression de type "fonction retournant T", sauf lorsqu'elle est utilisée comme l'opérande de l'opérateur &, est convertie en "pointeur vers fonction renvoyant T. "

Section A7.4.2 Opérateur d'adresse: L'opérateur unaire & prend l'adresse de son opérande .... Le résultat est un pointeur vers l'objet ou la fonction référencée par la valeur l. Si le type de l'opérande est T, le type du résultat est "pointeur vers T."

Pour autant que je sache, c'est la même chose pour C99.

1
Cameron

fun et &fun sont exactement les mêmes (sauf que sizeof (f) est illégal). a et &a sont les mêmes jusqu'à l'arithmétique du pointeur: a + 10 == &a + 1, car 10*sizeof(*a) == sizeof(a) (où sizeof(*a) == sizeof(int)).

1
Loic

printf ("fun1 =% p\t & foo =% p\n", fun1, foo);

Ici, vous appelez foo en passant le pointeur de fonction avec pass by value et

printf ("fun2 =% p\t foo =% p\n", fun2, & foo)

Ici, vous appelez &foo en passant la fonction Pointer avec pass by reference

dans les deux cas, vous appelez le printf avec le pointeur de fonction uniquement.

Rappelez-vous que foo lui-même est function pointer value et `pas une variable.

La même chose se produit avec le tableau. int arr[10] se traduit par obtenir un bloc continu de 10 entiers et l'adresse du premier élément est stockée dans arr. donc arr est aussi un pointeur.

0
rahul maindargi