Pointeur de fonction Typedef?

460

J'apprends à charger dynamiquement des DLL mais ce que je ne comprends pas c'est cette ligne

typedef void (*FunctionFunc)();

J'ai quelques questions. Si quelqu'un est en mesure de leur répondre, je vous en serais reconnaissant.

  1. Pourquoi est-il typedefutilisé?
  2. La syntaxe semble étrange; après voidne devrait-il pas y avoir de nom de fonction ou quelque chose? Cela ressemble à une fonction anonyme.
  3. Un pointeur de fonction est-il créé pour stocker l'adresse mémoire d'une fonction?

Je suis donc confus en ce moment; pouvez-vous clarifier les choses pour moi?

Jack Harvin
la source
5
Jetez un oeil au lien (dernière section) learncpp.com/cpp-tutorial/78-function-pointers
enthousiastegeek
9
Il convient de noter que puisque c ++ 11 using FunctionFunc = void (*)();peut être utilisé à la place. Il est un peu plus clair que vous
déclarez
juste pour ajouter à @ user362515, une forme un peu plus claire pour moi est:using FunctionFunc = void(void);
topspin
@topspin IIRC ces deux ne sont pas les mêmes. L'un est un type de pointeur de fonction, l'autre est un type de fonction. Il y a une conversion implicite, c'est pourquoi cela fonctionne, IANA (C ++) L donc, on peut intervenir et me corriger. Dans tous les cas, si l'intention est de définir un type de pointeur, je pense que la syntaxe avec le *est un peu plus explicite.
user362515

Réponses:

463

typedefest une construction de langage qui associe un nom à un type.
Vous l'utilisez de la même manière que vous utiliseriez le type d'origine, par exemple

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

les utiliser comme

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

Comme vous pouvez le voir, vous pouvez simplement remplacer le nom typé par sa définition donnée ci-dessus.

La difficulté réside dans le pointeur vers la syntaxe des fonctions et la lisibilité en C et C ++, et typedefpeut améliorer la lisibilité de ces déclarations. Cependant, la syntaxe est appropriée, car les fonctions - contrairement à d'autres types plus simples - peuvent avoir une valeur de retour et des paramètres, d'où la déclaration parfois longue et complexe d'un pointeur vers la fonction.

La lisibilité peut commencer à être vraiment délicate avec des pointeurs vers des tableaux de fonctions, et quelques autres saveurs encore plus indirectes.

Pour répondre à vos trois questions

  • Pourquoi typedef est-il utilisé? Pour faciliter la lecture du code - en particulier pour les pointeurs vers des fonctions ou des noms de structure.

  • La syntaxe semble étrange (dans le pointeur vers la déclaration de fonction) Cette syntaxe n'est pas évidente à lire, du moins au début. L'utilisation d'une typedefdéclaration facilite la lecture

  • Un pointeur de fonction est-il créé pour stocker l'adresse mémoire d'une fonction? Oui, un pointeur de fonction stocke l'adresse d'une fonction. Cela n'a rien à voir avec la typedefconstruction qui facilite seulement l'écriture / lecture d'un programme; le compilateur développe simplement la définition de typedef avant de compiler le code réel.

Exemple:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456
e2-e4
la source
6
dans le dernier exemple, «carré» ne ferait pas simplement référence à la même chose, c'est-à-dire pointeur sur la fonction au lieu d'utiliser & square.
pranavk
2
Question, dans votre premier exemple typedef que vous avez de la forme , typedef type aliasmais avec des pointeurs de fonction , il semble seulement être 2 arguments, typedef type. L'alias a-t-il par défaut le nom spécifié dans l'argument type?
dchhetri
2
@pranavk: Oui, squareet &square(et, en effet, *squareet **square) font tous référence au même pointeur de fonction.
Jonathan Leffler
6
@ user814628: Ce que vous demandez n'est pas clair. Avec typedef int newname, vous devenez newnameun alias pour int. Avec typedef int (*func)(int), vous transformez funcen un alias pour int (*)(int)- un pointeur pour fonctionner en prenant un intargument et en retournant une intvaleur.
Jonathan Leffler
10
Je suppose que je suis juste confus au sujet de la commande. Avec typedef int (*func)(int), je comprends que func est un alias, juste un peu confus car l'alias est enchevêtré avec le type. En passant par typedef int INTl'exemple, je serais plus à l'aise si le pointeur de la fonction typedef était de forme typedef int(*function)(int) FUNC_1. De cette façon, je peux voir le type et l'alias dans deux jetons distincts au lieu d'être maillés en un seul.
dchhetri
188
  1. typedefest utilisé pour alias les types; dans ce cas , vous êtes aliasing FunctionFuncà void(*)().

  2. En effet, la syntaxe semble étrange, jetez un œil à ceci:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
    
  3. Non, cela indique simplement au compilateur que le FunctionFunctype sera un pointeur de fonction, il n'en définit pas un, comme ceci:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
    
Jacob Relkin
la source
27
typedefne déclare pas un nouveau type. vous pouvez avoir plusieurs typedefnoms définis par le même type, et ils ne sont pas distincts (par exemple wrt. surcharge des fonctions). il y a des circonstances dans lesquelles, en ce qui concerne la façon dont vous pouvez utiliser le nom, un typedefnom -defined n'est pas exactement équivalent à ce qu'il est défini, mais plusieurs typedefnoms -defined pour le même, sont équivalents.
Bravo et hth. - Alf
Ah je comprends maintenant. Merci.
Jack Harvin
4
+1 pour la réponse à la question 2 - très clair. Les autres réponses étaient claires aussi, mais celle-ci se démarque pour moi :-)
Michael van der Westhuizen
33

Sans le typedefmot, en C ++, la déclaration déclarerait une variable FunctionFuncde type pointeur pour fonctionner sans argument, en retournant void.

Avec le typedefil définit à la place FunctionFunccomme un nom pour ce type.

Santé et hth. - Alf
la source
5
C'est une très bonne façon d'y penser. Si vous lisez attentivement les autres réponses, elles ne résolvent pas vraiment la confusion du PO concernant l'intrication de l'alias et du nom. J'ai appris quelque chose de nouveau en lisant ce post.
Mad Physicist
9

Si vous pouvez utiliser C ++ 11, vous voudrez peut-être utiliser le mot std::function- usingclé et .

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;
Halil Kaskavalci
la source
1
sans le usingmot clé C ++ 11 serait typedef std::function<void(int, std::string)> FunctionFunc;, juste au cas où quelqu'un voudrait un autre wrapper autour des fonctions sans C ++ 11
Top-Master
2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}
Amjad
la source
1
Dans quelles circonstances le «&» est-il nécessaire? Par exemple, «func_p = & findDistance» et «func_p = findDistance» fonctionnent-ils aussi bien?
Donna
1
Un nom de fonction est un pointeur, vous n'avez donc pas besoin d'utiliser '&' avec lui. En d'autres termes, «&» est facultatif lorsque les pointeurs de fonction sont pris en compte. Cependant, pour le type de données primitif, vous devez utiliser le «&» pour obtenir son adresse.
Amjad
1
Il a toujours semblé étrange que le «&» puisse être facultatif. Si un typedef est utilisé pour créer un pointeur vers une primitive (c. typedef (*double) p-à- d. , Le '&' est-il facultatif?
Donna
Je pense que vous vouliez taper typedef double* ppour définir un pointeur sur un double. Si vous souhaitez remplir le ppointeur à partir d'une variable primitive, vous devrez utiliser le '&'. Une note latérale, nous vous * * nom du pointeur> pour déréférencer un pointeur. Le *est utilisé dans le pointeur de fonction pour déréférencer le pointeur (nom de la fonction) et aller au bloc de mémoire où se trouvent les instructions de fonction.
Amjad
2

Pour le cas général de la syntaxe , vous pouvez consulter l' annexe A de la norme ANSI C .

Dans la forme Backus-Naur à partir de là, vous pouvez voir qui typedefa le type storage-class-specifier.

Dans le type, declaration-specifiersvous pouvez voir que vous pouvez mélanger de nombreux types de spécificateurs, dont l'ordre n'a pas d'importance.

Par exemple, il est correct de dire:

long typedef long a;

pour définir le type acomme alias pour long long. Donc, pour comprendre le typedef sur l'utilisation exhaustive dont vous avez besoin, consultez une forme backus-naur qui définit la syntaxe (il existe de nombreuses grammaires correctes pour ANSI C, pas seulement celle d'ISO).

Lorsque vous utilisez typedef pour définir un alias pour un type de fonction, vous devez placer l'alias au même endroit où vous placez l'identifiant de la fonction. Dans votre cas, vous définissez le type FunctionFunccomme un alias pour qu'un pointeur fonctionne, dont la vérification de type est désactivée à l'appel et ne renvoie rien.

alinsoar
la source