Come i puntatori a funzionare come membro della struct sono utili in C?

Non sono nuovo alla programmazione in C. Ma non capisco quale sia l’utilità per mantenere il puntatore a funzionare come membro di una struttura in C. es

// 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); } 

I programmatori lo usano per dare una forma Object Oriented (OO) al codice C e fornire oggetti astratti? O per rendere il codice tecnico.

Penso che, nel codice sopra riportato, il secondo modo sia più delicato e anche piuttosto semplice. In prima battuta, dobbiamo ancora passare &var , anche fun() è membro di struct.

Se è utile mantenere il puntatore della funzione all’interno della definizione di struct, aiutaci gentilmente a spiegarne il motivo.

Fornire un puntatore alla funzione su una struttura può consentire di scegliere dynamicmente quale funzione eseguire su una struttura.

 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); } 

Questo ti dà la possibilità di avere una singola interfaccia di chiamata, ma chiamando due funzioni diverse a seconda se il test è valido o meno.

È utile se stai provando a fare una sorta di programmazione “a oggetti”.

Se hai mai visto il codice sorgente del motore di Quake 3, puoi vedere chiaramente che la maggior parte delle “entity framework” hanno attributi che li definiscono e il lavoro che fanno [che sono i puntatori di funzione].

La separazione degli attributi e delle funzioni (tramite i puntatori di funzione in C) definisce gli attributi e le azioni dell’object “struct” che possono eseguire.

Per esempio:

 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; } 

L’ho usato in passato per implementare contenitori generici 1 in C.

Per esempio:

 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 struttura dell’elenco è indipendente dai dati, utilizza void * per rappresentare elementi di dati e delega tutte le operazioni di tipo aware alle funzioni indicate dai puntatori di cui sopra:

 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. Per le definizioni di “generico” e “contenitore” opportunamente allentate

Questo può essere particolarmente utile nel sistema embedded o nella scrittura del driver. Le funzioni sono chiamate usando i puntatori di funzione.

per esempio

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

eccetera

A volte in C è necessario chiamare una funzione, senza conoscerne l’implementazione in anticipo. Ad esempio, se stai sviluppando una libreria autonoma utilizzata in diversi progetti. In tal caso, ha perfettamente senso aggiungere un puntatore a una struttura di contesto e passarlo insieme alle funzioni della libreria. Le funzioni della libreria possono quindi chiamare la funzione in fase di esecuzione senza dover includere i file di intestazione che lo definiscono.

È in effetti simile a ciò che fai quando esegui la programmazione orientata agli oggetti. In C, di solito si passano le variabili di contesto, ad esempio una struct, raggruppando un insieme di variabili e possibilmente funzioni che sono significative per quel contesto. Questo è vicino all’eqvuivalent nella programmazione OO, in cui istanziate una class e impostate le variabili richieste sull’istanza.

Ecco un progetto per aiutare a spiegare l’utilità dei puntatori di funzione. Prova a creare una libreria basata su c che fornisca ereditarietà e polimorfismo. Oppure, prova a ricreare “C with Classes”. I puntatori di funzioni all’interno delle strutture saranno vitali. http://www.cs.rit.edu/~ats/books/ooc.pdf