- Mettez le programme suivant dans un fichier toto.c, compilez-le,
et exécutez-le. Qu'affiche-t-il ? Que fait la
fonction fCreation(), étape par étape ?
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int a,b;
} Toto;
void *fCreation(int x, int y) {
Toto *pToto = (Toto *)malloc(sizeof(Toto));
pToto -> a = x;
pToto -> b = y;
return (void*)pToto; // typecast vers void*
}
void fAffichage(void *ptr) {
Toto *pToto = (Toto *)ptr; // typecast vers Toto *
printf("Toto: %d %d\n",pToto->a,pToto->b);
}
int main() {
void *p = fCreation(1,2);
/*printf("TotoMain: %d %d\n",p->a,p->b);*/
fAffichage(p);
free(p);
return(0);
}
- Maintenant, allez dans toto.c et enlevez le commentaire, pour
avoir réelement le printf() dans le main(), donc ainsi
int main() {
void *p = fCreation(1,2);
printf("TotoMain: %d %d\n",p->a,p->b);
fAffichage(p);
free(p);
return(0);
}
- Cela pourra-t-il compiler ? Pourquoi? Est-il permis d'utiliser
l'opérateur * sur un pointeur de void ? Pourquoi ?
- Donc en conclusion, dans la fonction main() pouvons nous faire
quoi que ce soit avec les membres de la structure Toto si on garde
le pointeur vers void ?
- Dans la pratique, on peut mettre le code de définition
de Toto et des fonctions fCreation()
et fAffichage() dans d'autres fichiers source et ne donner que la
bibliothèque compilée. Alors dans le fichier on ne mettra plus
que le main() ainsi
int main() {
void *p = fCreation(1,2);
fAffichage(p);
free(p);
return(0);
}
donc qui ne parle plus de Toto.
Que faudrait-il quand même donner d'autre pour qu'on
puisse compiler et linker un tel main() avec cette
bibliothèque ? (Indication : c'est ce qu'on fait tout le temps
avec stdio.h etc). Attention -- on n'a pas besoin de la
définition de Toto.
- Comment avons-nous séparé les "responsabilités" ? Le
code utilisateur (i.e. le main()) décide quand créer,
et quand libérer, et garde le pointeur. Mais que n'a-t-il pas~?
Quoi d'autre de neuf
Un pointeur de fonction permet d'appeler une fonction parmi plusieurs
préalablement définies -- en la choisissant pendant
l'exécution. On défini d'abord le type, avec typedef, comme une
fonction, mais en mettant une * devant le nom et des paranthès
autour
typedef TYPEDERETOUR (*NOM)(TYPEPARAM1 PARAM1, TYPEPARAM2 PARAM2, ...)
Ici on reprend le calcul de la somme des éléments d'un tableau,
mais on l'écrit de manière générique avec
des void * et pointeurs de fonction.
- Mettez ce programme dans un fichier somme.c, compilez-le et
exécutez-le.
#include <stdio.h>
typedef void (*PAppFunc)(void *dest, void **ppElem);
void accumulerInt(void *acc, void **ppElem) {
int *pAcc = (int *)acc;
int **pElem = (int **)ppElem;
*pAcc += **pElem;
(*pElem)++;
}
void balayerAppliquer(void *tableau, const int nElem, PAppFunc pAppFunc, void *base) {
int k;
for(k = 0; k<nElem; k++) {
pAppFunc(base,&tableau);
}
}
int main() {
int tab[10];
const int nElem = 10;
int k;
int somme = 0;
for(k = 0; k<nElem; k++) {
scanf("%d",tab+k);
}
balayerAppliquer(tab,10,accumulerInt,&somme);
printf("%d\n",somme);
return 0;
}
- Que fait balayerAppliquer() pas-à-pas ? Aux points
précédents nous avons vu comment on cache l'information
avec void*. Dans quelles lignes de ce programme fait-on la
même chose ici~?
- Le profile de la fonction accumulerInt() ne mentionne pas le
type int. Que fait-elle pas-à-pas ?
- Recopiez le fichier somme.c dans un autre
fichier sommeD.c.
Écrivez dans sommeD.c une autre fonction, accumulerDouble(), qui fait la
même chose accumulerInt(), mais pour
des double.
- Toujours dans sommeD.c modifiez le main() pour
que tab soit un tableau de doubles et somme soit un double;
modifiez le scanf()
et au printf() en mettant '%f' à la place de '%d'. Compilez
et testez.
- Pour que ceci fonctionne, avons-nous eu besoin de changer le code
de balayerAppliquer() ? Pourquoi ? Donc ?