Comment les pointeurs pour fonctionner en tant que membre struct utiles en C?

Je ne suis pas nouveau en programmation C. Mais je ne comprends pas ce qu’est l’utilité de garder le pointeur pour fonctionner en tant que membre de structure en C. ex.

// Fist Way: To keep pointer to function in struct struct newtype{ int a; char c; int (*f)(struct newtype*); } var; int fun(struct newtype* v){ return v->a; } // Second way: Simple struct newtype2{ int a; char c; } var2; int fun2(struct newtype2* v){ return v->a; } int main(){ // Fist: Require two steps var.f=fun; var.f(&var); //Second : simple to call fun2(&var2); } 

Les programmeurs l’utilisent-ils pour donner à leur code C une forme orientée object (OO) et fournir un object abstrait? Ou pour donner un aspect technique au code.

Je pense que la deuxième manière du code ci-dessus est plus douce et assez simple aussi. De la première manière, nous devons toujours passer &var , même fun() est membre de struct.

S’il est bon de garder le pointeur de fonction dans la définition de la structure, aidez-nous à en expliquer la raison.

Fournir un pointeur pour fonctionner sur une structure peut vous permettre de choisir dynamicment la fonction à exécuter sur une structure.

 struct newtype{ int a; int b; char c; int (*f)(struct newtype*); } var; int fun1(struct newtype* v){ return v->a; } int fun2(struct newtype* v){ return v->b; } void usevar(struct newtype* v) { // at this step, you have no idea of which function will be called var.f(&var); } int main(){ if (/* some test to define what function you want)*/) var.f=fun1; else var.f=fun2; usevar(var); } 

Cela vous permet d’avoir une seule interface d’appel, mais d’appeler deux fonctions différentes selon que votre test est valide ou non.

Son utile si vous essayez de faire une sorte de programmation “basée sur les objects”.

Si vous avez déjà vu le code source du moteur Quake 3, vous pouvez voir clairement que la plupart des “entités” ont des atsortingbuts qui les définissent, ainsi que le travail qu’elles effectuent [qui sont les pointeurs de fonction].

La séparation des atsortingbuts et des fonctions (via les pointeurs de fonction en C) définit les atsortingbuts et les actions d’un object “struct” qu’ils peuvent effectuer.

Par exemple:

 struct _man{ char name[]; int age; void (*speak)(char *text); void (*eat)(Food *foodptr); void (*sleep)(int hours); /*etc*/ }; void grijesh_speak(char *text) { //speak; } void grijesh_eat(Food *food) { //eat } void grijesh_sleep(int hours) { //sleep } void init_struct(struct _man *man) { if(man == NULL){ man = malloc(sizeof(struct _man));} strcpy(*man.name,"Grijesh"); man->age = 25; man->speak = grijesh_speak; man->eat = grijesh_food; man->sleep = grijesh_sleep; //etc } //so now in main.. i can tell you to either speak, or eat or sleep. int main(int argc, char *argv[]) { struct _man grijesh; init_struct(&grijesh); grijesh.speak("Babble Dabble"); grijesh.sleep(10); return 0; } 

J’ai déjà utilisé cela par le passé pour implémenter les conteneurs génériques 1 en C.

Par exemple:

 typedef struct generic_list_node { void *data; struct generic_list_node *next; } generic_list_node_t; typedef struct generic_list { generic_list_node_t *head; void *(*copy)(void *data); void (*delete)(void *data); int (*compare)(void *lhs, void *rhs); void (*display)(void *data, FILE *stream); } generic_list_t; 

La structure de liste elle-même est agnostique aux données, utilisant void * pour représenter des éléments de données et délègue toutes les opérations sensibles au type aux fonctions indiquées par les pointeurs ci-dessus:

 int insert(generic_list_t l, void *data) { generic_list_node_t *cur = l.head; generic_list_node_t *new = malloc(sizeof *new); if (new) { new->data = l.copy(data); new->next = NULL; if (l.head == NULL) { l.head = new; } else { while (cur->next && l.compare(new->data, cur->data) > 0) cur = cur->next; new->next = cur->next; cur->next = new; printf("Successfully added "); l.display(data, stdout); printf(" to list\n"); } return 1; } return 0; } 

1. Pour des définitions suffisamment lâches de “générique” et de “conteneur”

Cela peut être particulièrement utile dans les systèmes embarqués ou dans l’écriture de pilotes. Les fonctions sont appelées à l’aide de pointeurs de fonction.

par exemple

 struct_my_filesystem.open=my_open; struct_my_filesystem.read=my_read; 

etc

Parfois, en C, vous devez appeler une fonction sans connaître sa mise en œuvre initiale. Par exemple, si vous développez une bibliothèque autonome utilisée dans différents projets. Dans ce cas, il est parfaitement logique d’append un pointeur de fonction à une structure de contexte et de le transmettre aux fonctions de bibliothèque. Les fonctions de bibliothèque peuvent ensuite appeler votre fonction au moment de l’exécution sans avoir à inclure les fichiers d’en-tête qui le définissent.

C’est en fait similaire à ce que vous faites lorsque vous effectuez une programmation orientée object. En C, vous transmettez généralement des variables de contexte, par exemple une structure, en regroupant un ensemble de variables et éventuellement des fonctions significatives pour ce contexte. Ceci est proche de l’équivalent dans la programmation OO, dans lequel vous instanciez une classe et définissez les variables requirejses sur l’instance.

Voici un projet pour aider à expliquer l’utilité des pointeurs de fonction. Essayez de créer une bibliothèque basée sur c fournissant l’inheritance et le polymorphism. Ou bien, essayez de recréer “C avec les classes”. Les pointeurs de fonction à l’intérieur des structures seront essentiels. http://www.cs.rit.edu/~ats/books/ooc.pdf