Pointeurs de fonctions en programmation C avec exemples
Les pointeurs donnent de grandes possibilités aux fonctions 'C' qui sont limitées à renvoyer une seule valeur. Avec les paramètres de pointeur, nos fonctions peuvent désormais traiter des données réelles plutôt qu'une copie de données.
Afin de modifier les valeurs réelles des variables, l'instruction appelante transmet les adresses aux paramètres de pointeur dans une fonction.
Exemple de pointeurs de fonctions
Par exemple, le programme suivant échange deux valeurs sur deux :
void swap (int *a, int *b); int main() { int m = 25; int n = 100; printf("m is %d, n is %d\n", m, n); swap(&m, &n); printf("m is %d, n is %d\n", m, n); return 0;} void swap (int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp;} }
Sortie :
m is 25, n is 100 m is 100, n is 25
Le programme échange les valeurs réelles des variables car la fonction y accède par adresse en utilisant aiguille. Nous discuterons ici du processus du programme :
- Nous déclarons la fonction responsable de l'échange des deux valeurs de variables, qui prend deux pointeurs entiers comme paramètres et renvoie n'importe quelle valeur lorsqu'elle est appelée.
- Dans la fonction principale, nous déclarons et initialisons deux variables entières ('m' et 'n') puis nous imprimons respectivement leurs valeurs.
- Nous appelons la fonction swap() en passant l'adresse des deux variables comme arguments à l'aide du symbole esperluette. Après cela, nous imprimons les nouvelles valeurs échangées des variables.
- Ici, nous définissons le contenu de la fonction swap() qui prend deux adresses de variables entières comme paramètres et déclarons une variable entière temporaire utilisée comme troisième boîte de stockage pour enregistrer l'une des variables de valeur qui sera placée dans la deuxième variable.
- Enregistrez le contenu de la première variable pointée par 'a' dans la variable temporaire.
- Stockez la deuxième variable pointée par b dans la première variable pointée par a.
- Mettez à jour la deuxième variable (pointée par b) par la valeur de la première variable enregistrée dans la variable temporaire.
Fonctions avec paramètres de tableau
En C, on ne peut pas passer un tableau par valeur à une fonction. Alors qu'un nom de tableau est un pointeur (adresse), nous passons donc simplement un nom de tableau à une fonction, ce qui signifie passer un pointeur vers le tableau.
Par exemple, nous considérons le programme suivant :
int add_array (int *a, int num_elements); int main() { int Tab[5] = {100, 220, 37, 16, 98}; printf("Total summation is %d\n", add_array(Tab, 5)); return 0;} int add_array (int *p, int size) { int total = 0; int k; for (k = 0; k < size; k++) { total += p[k]; /* it is equivalent to total +=*p ;p++; */} return (total);}
Sortie :
Total summation is 471
Ici, nous expliquerons le code du programme avec ses détails
- Nous déclarons et définissons la fonction add_array() qui prend une adresse de tableau (pointeur) avec son numéro d'éléments comme paramètres et renvoie la somme totale accumulée de ces éléments. Le pointeur est utilisé pour itérer les éléments du tableau (en utilisant la notation p[k]), et nous accumulons la somme dans une variable locale qui sera renvoyée après avoir itéré l'ensemble du tableau d'éléments.
- Nous déclarons et initialisons un tableau d'entiers avec cinq éléments entiers. Nous imprimons la somme totale en passant le nom du tableau (qui fait office d'adresse) et la taille du tableau au add_array()fonction appelée comme arguments.
Fonctions qui renvoient un tableau
En C, on peut renvoyer un pointeur vers un tableau, comme dans le programme suivant :
#include <stdio.h> int * build_array(); int main() { int *a; a = build_array(); /* get first 5 even numbers */ for (k = 0; k < 5; k++) printf("%d\n", a[k]); return 0;} int * build_array() { static int Tab[5]={1,2,3,4,5}; return (Tab);}
Sortie :
1 2 3 4 5
Et ici, nous discuterons des détails du programme
- Nous définissons et déclarons une fonction qui renvoie une adresse de tableau contenant une valeur entière et ne prend aucun argument.
- Nous déclarons un pointeur entier qui reçoit le tableau complet construit après l'appel de la fonction et nous imprimons son contenu en itérant l'intégralité du tableau de cinq éléments.
Notez qu'un pointeur, et non un tableau, est défini pour stocker l'adresse du tableau renvoyée par la fonction. Notez également que lorsqu'une variable locale est renvoyée par une fonction, nous devons la déclarer comme statique dans la fonction.
Pointeurs de fonction
Comme nous savons par définition que les pointeurs pointent vers une adresse dans n'importe quel emplacement mémoire, ils peuvent également pointer vers le début du code exécutable en tant que fonctions en mémoire.
Un pointeur vers une fonction est déclaré avec le * , l'instruction générale de sa déclaration est :
return_type (*function_name)(arguments)
Vous devez vous rappeler que les parenthèses autour de (*function_name) sont importantes car sans elles, le compilateur pensera que function_name renvoie un pointeur de return_type.
Après avoir défini le pointeur de fonction, nous devons l'attribuer à une fonction. Par exemple, le programme suivant déclare une fonction ordinaire, définit un pointeur de fonction, attribue le pointeur de fonction à la fonction ordinaire et appelle ensuite la fonction via le pointeur :
#include <stdio.h> void Hi_function (int times); /* function */ int main() { void (*function_ptr)(int); /* function pointer Declaration */ function_ptr = Hi_function; /* pointer assignment */ function_ptr (3); /* function call */ return 0;} void Hi_function (int times) { int k; for (k = 0; k < times; k++) printf("Hi\n");}
Sortie :
Hi Hi Hi
- Nous définissons et déclarons une fonction standard qui imprime un texte Hi k fois indiqué par le paramètre times lorsque la fonction est appelée
- Nous définissons une fonction pointeur (avec sa déclaration spéciale) qui prend un paramètre entier et ne renvoie rien.
- Nous initialisons notre fonction pointeur avec la Hi_function ce qui signifie que le pointeur pointe vers la Hi_function().
- Plutôt que d'appeler la fonction standard en enregistrant le nom de la fonction avec des arguments, nous appelons uniquement la fonction pointeur en passant le chiffre 3 comme arguments, et c'est tout !
Gardez à l'esprit que le nom de la fonction pointe vers l'adresse de début du code exécutable comme un nom de tableau qui pointe vers son premier élément. Par conséquent, des instructions telles que function_ptr = &Hi_function et (*funptr)(3) sont correctes.
REMARQUE : Il n'est pas important d'insérer l'opérateur d'adresse & et l'opérateur d'indirection * lors de l'affectation de fonction et de l'appel de fonction.
Tableau de pointeurs de fonction
Un tableau de pointeurs de fonction peut jouer un rôle de commutateur ou d'instruction if pour prendre une décision, comme dans le programme suivant :
#include <stdio.h> int sum(int num1, int num2); int sub(int num1, int num2); int mult(int num1, int num2); int div(int num1, int num2); int main() { int x, y, choice, result; int (*ope[4])(int, int); ope[0] = sum; ope[1] = sub; ope[2] = mult; ope[3] = div; printf("Enter two integer numbers: "); scanf("%d%d", &x, &y); printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: "); scanf("%d", &choice); result = ope[choice](x, y); printf("%d", result); return 0;} int sum(int x, int y) {return(x + y);} int sub(int x, int y) {return(x - y);} int mult(int x, int y) {return(x * y);} int div(int x, int y) {if (y != 0) return (x / y); else return 0;}
Enter two integer numbers: 13 48 Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2 624
Ici, nous discutons des détails du programme :
- Nous déclarons et définissons quatre fonctions qui prend deux arguments entiers et renvoie une valeur entière. Ces fonctions ajoutent, soustraient, multiplient et divisent les deux arguments concernant la fonction appelée par l'utilisateur.
- Nous déclarons 4 entiers pour gérer respectivement les opérandes, le type d'opération et le résultat. De plus, nous déclarons un tableau de quatre pointeurs de fonctions. Chaque pointeur de fonction d'un élément du tableau prend deux paramètres entiers et renvoie une valeur entière.
- Nous attribuons et initialisons chaque élément du tableau avec la fonction déjà déclarée. Par exemple, le troisième élément qui est le troisième pointeur de fonction pointera vers la fonction d'opération de multiplication.
- On recherche les opérandes et le type d'opération de l'utilisateur tapé au clavier.
- Nous avons appelé l'élément de tableau approprié (pointeur de fonction) avec des arguments et nous stockons le résultat généré par la fonction appropriée.
L'instruction int (*ope[4])(int, int); définit le tableau de pointeurs de fonction. Chaque élément du tableau doit avoir les mêmes paramètres et le même type de retour.
Le résultat de l'instruction = ope[choice](x, y); exécute la fonction appropriée selon le choix fait par l'utilisateur. Les deux entiers saisis sont les arguments passés à la fonction.
Fonctions utilisant des pointeurs vides
Les pointeurs vides sont utilisés lors des déclarations de fonctions. Nous utilisons un type de retour void * permettant de renvoyer n'importe quel type. Si nous supposons que nos paramètres ne changent pas lors du passage à une fonction, nous le déclarons comme const.
Par exemple :
void * cube (const void *);
Considérez le programme suivant :
#include <stdio.h> void* cube (const void* num); int main() { int x, cube_int; x = 4; cube_int = cube (&x); printf("%d cubed is %d\n", x, cube_int); return 0;} void* cube (const void *num) { int result; result = (*(int *)num) * (*(int *)num) * (*(int *)num); return result;}
Résultat:
4 cubed is 64
Ici, nous discuterons des détails du programme :
- Nous définissons et déclarons une fonction qui renvoie une valeur entière et prend une adresse de variable immuable sans type de données spécifique. Nous calculons la valeur cubique de la variable de contenu (x) pointée par le pointeur num, et comme il s'agit d'un pointeur vide, nous devons la convertir en un type de données entier en utilisant un pointeur de notation spécifique (* type de données), et nous retournons la valeur du cube.
- Nous déclarons l'opérande et la variable résultat. De plus, nous initialisons notre opérande avec la valeur « 4 ».
- Nous appelons la fonction cube en passant l'adresse de l'opérande, et nous gérons la valeur renvoyée dans la variable résultat
Pointeurs de fonction comme arguments
Une autre façon d'exploiter un pointeur de fonction en le passant en argument à une autre fonction parfois appelée « fonction de rappel » car la fonction réceptrice « le rappelle ».
Dans le fichier d'en-tête stdlib.h, la fonction Quicksort « qsort() » utilise cette technique qui est un algorithme dédié au tri d'un tableau.
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void *base : pointeur void vers le tableau.
- size_t num : Le numéro de l'élément du tableau.
- size_t width La taille de l'élément.
- int (*compare (const void *, const void *) : pointeur de fonction composé de deux arguments et renvoie 0 lorsque les arguments ont la même valeur, <0 lorsque arg1 vient avant arg2, et >0 lorsque arg1 vient après arg2.
Le programme suivant trie un tableau d'entiers du petit au grand nombre à l'aide de la fonction qsort() :
#include <stdio.h> #include <stdlib.h> int compare (const void *, const void *); int main() { int arr[5] = {52, 14, 50, 48, 13}; int num, width, i; num = sizeof(arr)/sizeof(arr[0]); width = sizeof(arr[0]); qsort((void *)arr, num, width, compare); for (i = 0; i < 5; i++) printf("%d ", arr[ i ]); return 0;} int compare (const void *elem1, const void *elem2) { if ((*(int *)elem1) == (*(int *)elem2)) return 0; else if ((*(int *)elem1) < (*(int *)elem2)) return -1; else return 1;}
Résultat:
13 14 48 50 52
Ici, nous discuterons des détails du programme :
- Nous définissons une fonction de comparaison composée de deux arguments et renvoie 0 lorsque les arguments ont la même valeur, <0 lorsque arg1 vient avant arg2 et >0 lorsque arg1 vient après arg2. Les paramètres sont un type de pointeurs vides convertis en type de données de tableau approprié. (entier)
- Nous définissons et initialisons un tableau d'entiers. La taille du tableau est stockée dans le num variable et la taille de chaque élément du tableau est stockée dans la variable width en utilisant sizeof() prédéfini Opérateur C.
- Nous appelons l' qtrier et transmettez le nom du tableau, la taille, la largeur et la fonction de comparaison définis précédemment par l'utilisateur afin de trier notre tableau par ordre croissant. La comparaison sera effectuée en prenant à chaque itération deux éléments du tableau jusqu'à ce que l'ensemble du tableau soit trié.
- Nous imprimons les éléments du tableau pour être sûr que notre tableau est bien trié en itérant tout le tableau en utilisant pour la boucle.