void

void - C - Programmation

Marsh Posté le 26-04-2007 à 16:04:43    

Bonjour,
 
j'aimerai savoir si quand on fait une fonction
 

Code :
  1. int ma_fonction(void * toto)


 
ca veut dire qu'on peut faire des traitement sur les adresses, mais par exemple est ce qu'on peut faire des traitements sur les valeurs ??? Par exemple si on passe à la fonction :
 

Code :
  1. int i = 0;
  2. ma_fonction(&i);


 
est-ce possible ? est ce possible que la fonction programmée fasse des opérations sur la valeur *toto, étant donnée que c'est un type void  :heink: . Je sens bien que non ....mais heu oui j'aime me faire flageller ?  :sweat:  :lol:  
 
 
 

Reply

Marsh Posté le 26-04-2007 à 16:04:43   

Reply

Marsh Posté le 26-04-2007 à 16:08:46    

Non, on ne peut pas faire d'opération sur le contenu d'une adresse pointée par un void*, c'est un pointeur générique. Si tu veux pouvoir en faire quelque chose (du contenu de l'adresse blabla), il faut au minimum un second paramètre, pour donner la taille unitaire d'un élément. Et si ton void* est un tableau d'éléments, il te faut aussi donner à la fonction le nombre d'éléments...


Message édité par Elmoricq le 26-04-2007 à 16:09:06
Reply

Marsh Posté le 26-04-2007 à 16:09:12    

t'as essayé de compiler ça ?


---------------
Töp of the plöp
Reply

Marsh Posté le 26-04-2007 à 16:11:03    

_darkalt3_ a écrit :

t'as essayé de compiler ça ?

 

Euh, aucun problème :

Code :
  1. #include <stdio.h>
  2.  
  3. int machin(void *truc)
  4. {
  5.   return 0;
  6. }
  7.  
  8. int main(void)
  9. {
  10.   int i = 42;
  11.  
  12.   return machin(&i);
  13. }
 


$ gcc -Wall -O2 -pedantic taiste.c
                                                                                                                                                     
$

 


Juste, en l'état, ça ne sert à rien.


Message édité par Elmoricq le 26-04-2007 à 16:12:10
Reply

Marsh Posté le 26-04-2007 à 16:12:53    

(donc la réponse à sa question aurait été "oui c'est possible techniquement", avant développements).


---------------
Töp of the plöp
Reply

Marsh Posté le 26-04-2007 à 16:16:20    

_darkalt3_ a écrit :

(donc la réponse à sa question aurait été "oui c'est possible techniquement", avant développements).


(j'comprends pas bien, je réponds à sa question, non ? [:petrus dei])


Message édité par Elmoricq le 26-04-2007 à 16:17:19
Reply

Marsh Posté le 26-04-2007 à 16:23:21    

(oui bien sûr, mais je pars du principe que le gars comprends mieux en manipulant lui même)


---------------
Töp of the plöp
Reply

Marsh Posté le 26-04-2007 à 17:08:57    

C'est tout à fait possible, mais tu ne sera pas capable de déréférencer toto. Pour accéder à ce qui est pointé par toto, il faudra le caster avant de le déréfencer, ce suppose que tu connais pas ailleurs le type pointé par toto.

Code :
  1. int a = *toto; /* Pas bon */
  2. int b = *((int *)toto); /* Bon */

Reply

Marsh Posté le 26-04-2007 à 17:13:25    

Par contre les cas sont rares où l'ont peut écrire une fonction admettant un void* comme argument, et "connaissant" le type du paramètre (on peut par exemple imaginer une structure avec un void* dedans, et un champ de la structure permettant de déduire le type du void* selon les cas).

Message cité 1 fois
Message édité par Elmoricq le 26-04-2007 à 17:15:04
Reply

Marsh Posté le 26-04-2007 à 18:04:44    

Elmoricq a écrit :

Par contre les cas sont rares où l'ont peut écrire une fonction admettant un void* comme argument, et "connaissant" le type du paramètre (on peut par exemple imaginer une structure avec un void* dedans, et un champ de la structure permettant de déduire le type du void* selon les cas).


 
Pas tant que ça, regarde du coté des pthread ou d'autres callbakc géénriques.

Reply

Marsh Posté le 26-04-2007 à 18:04:44   

Reply

Marsh Posté le 26-04-2007 à 21:05:42    

les void* sont souvent utiliser comme pointeur vers des fonctions. Sinon c'est vrai que je n'en vois pas l'interet non plus.  
 
On peut le comparer à Object dans les languages orientés objets tel que Java ou C#...  ça n'a que peux d'interet...

Reply

Marsh Posté le 26-04-2007 à 21:19:15    

moi23372 a écrit :

les void* sont souvent utiliser comme pointeur vers des fonctions.

 

Surement pas !

 

Un pointeur de fonction, c'est ça : <type>(*fonctionquelconque)(paramètres, ...)

 

exemple : int *pointeurDeFonction(int machin, double bidule, const char * const * truc);

 
moi23372 a écrit :

Sinon c'est vrai que je n'en vois pas l'interet non plus.

 

Y a plein d'intérêt au void*, on discutait juste ici de la possibilité d'exploiter les données à l'adresse pointée par un void*, avec juste ce pointeur à disposition. [:mlc]
Le type void* est un outil comme un autre, il a donc plein d'intérêts.

 
moi23372 a écrit :

On peut le comparer à Object dans les languages orientés objets tel que Java ou C#...  ça n'a que peux d'interet...

 

Euh... non. [:marc]

Message cité 1 fois
Message édité par Elmoricq le 26-04-2007 à 21:22:16
Reply

Marsh Posté le 28-04-2007 à 13:41:53    

Elmoricq a écrit :

Un pointeur de fonction, c'est ça : <type>(*fonctionquelconque)(paramètres, ...)
 
exemple : int *pointeurDeFonction(int machin, double bidule, const char * const * truc);


T'as bien mis les parenthèses dans ta description... alors pourquoi tu les oublies ici ?
Là, on a une fonction nommé "pointeurDeFonction" qui renvoie un pointeur sur un int  :D  
 

Elmoricq a écrit :

Y a plein d'intérêt au void*, on discutait juste ici de la possibilité d'exploiter les données à l'adresse pointée par un void*, avec juste ce pointeur à disposition.


Une fois, à mes débuts, j'ai utilisé un pointeur de ce type. J'avais besoin d'afficher le login et le nom du groupe de l'utilisateur du programme. Et comme c'était vraiment ponctuel, j'ai utilisé la même variable qui recevait successivement

  • un pointeur sur une structure de type "struct passwd"
  • un pointeur sur une structure de type "struct group"

Les deux structures étant différentes, je n'ai pas eu d'autre solution que de déclarer ma variable de type "void*". Ensuite, lorsque je voulais me servir de ce pointeur en tant que "struct passwd*", je faisais un cast. Idem pour l'autre.
Bon, c'était horrible j'admets.
 

in_your_phion a écrit :

ca veut dire qu'on peut faire des traitement sur les adresses, mais par exemple est ce qu'on peut faire des traitements sur les valeurs ???


 
Ben il y a qsort() qui peut trier un tableau de n'importe quoi (il y a donc forcément traitement sur les valeurs). Toi tu passes en premier paramètre à cette fonction l'adresse de ton tableau. Comme cette fonction est universelle (tu peux lui passer l'adresse d'un tableau de n'importe quel type), elle stockera cette adresse dans un pointeur de type "void *".
Bien évidemment, comme le dit Elmoricq, il faut passer à la fonction d'autres infos pour qu'elle puisse manipuler correctement ce pointeur...

Message cité 1 fois
Message édité par Sve@r le 28-04-2007 à 13:50:11

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 28-04-2007 à 19:35:49    

Sve@r a écrit :

T'as bien mis les parenthèses dans ta description... alors pourquoi tu les oublies ici ?


Parce que je les ai oubliées dans l'exemple, rien de plus.  [:cerveau arf]

Reply

Marsh Posté le 29-04-2007 à 12:41:39    

Elmoricq a écrit :

Parce que je les ai oubliées dans l'exemple, rien de plus.  [:cerveau arf]


Arf brain failure... [:ddr555]


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 29-04-2007 à 15:39:13    

moi23372 a écrit :

les void* sont souvent utiliser comme pointeur vers des fonctions.


Non. Merci d'éviter de sortir des aneries pareilles. void * est un type "pointeur générique" sur objet. Certainement pas sur fonction (comportement indéfini).

 

Quand à l'utilité, elle concerne évidemment la programmation générique. Par exemple :

 

http://mapage.noos.fr/emdel/complog.htm

 

ou

 

http://mapage.noos.fr/emdel/clib.htm
Modules GLL, GFIFO etc.

 



Message édité par Emmanuel Delahaye le 29-04-2007 à 15:40:42

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 29-04-2007 à 15:44:18    

in_your_phion a écrit :

Bonjour,
 
j'aimerai savoir si quand on fait une fonction
 

Code :
  1. int ma_fonction(void * toto)


 
ca veut dire qu'on peut faire des traitement sur les adresses, mais par exemple est ce qu'on peut faire des traitements sur les valeurs ??? Par exemple si on passe à la fonction :
 

Code :
  1. int i = 0;
  2. ma_fonction(&i);


 
est-ce possible ? est ce possible que la fonction programmée fasse des opérations sur la valeur *toto, étant donnée que c'est un type void  :heink: . Je sens bien que non ....mais heu oui j'aime me faire flageller ?  :sweat:  :lol:


Le but n'est pas de faire des traitements sur des adresses, mais de passer de façon 'anonyme' (ou générique) l'adresse d'un objet à une fonction, dans le but de faire des traitements indépendamment du type.
 
Bien sûr, en fonction du traitement à faire une indication sur la taille de l'objet pointé peut être utile (qsort(), par exemple).
 
Mais dans le cas d'un callback, c'est le programmeur qui doit passer l'adresse d'un type bien défini au moment de l'appel, et c'est lui qui sait donc quel est le type attendu. (le plus simple étant de gérer un pointeur local typé selon les désirs de l'utilisateur dans le callback).


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 29-04-2007 à 15:56:17    

Emmanuel Delahaye a écrit :

Mais dans le cas d'un callback, c'est le programmeur qui doit passer l'adresse d'un type bien défini au moment de l'appel


Je ne connais pas trop les callback (ou alors je les connais sous un autre nom). Tu pourrais me donner un exemple STP ?
En fait, j'arrive pas trop à m'imaginer pourquoi le programmeur doit programmer une fonction qui recevra un "void *" puis dans la fonction il devra faire le branchement sur le bon type alors que manifestement il connaît le type du paramètre qu'il lui envoie => autant mettre directement le bon type (ou alors j'ai rien pigé)...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 29-04-2007 à 16:15:14    

Sve@r a écrit :

Je ne connais pas trop les callback (ou alors je les connais sous un autre nom). Tu pourrais me donner un exemple STP ?
En fait, j'arrive pas trop à m'imaginer pourquoi le programmeur doit programmer une fonction qui recevra un "void *" puis dans la fonction il devra faire le branchement sur le bon type alors que manifestement il connaît le type du paramètre qu'il lui envoie => autant mettre directement le bon type (ou alors j'ai rien pigé)...


As-tu lu mon article sur les composants logiciels cité au-dessus ?
 
C'est comme ça qu'on code les 'évènements' (idem windows, gtk+ etc.)
 
As-tu déjà utilisé qsort() ?
 
Le principe de la programmation générique est que le maximum de code est unique et placé en bibliothèque, mais que ce qui est lié à l'application. Ici, la fonction de comparaison est confiée au programmeur. Les paramètres de la fonction de comparaison (adresse des éléments) sont évidemment de type void * (en l'occurence, void const *), car la fonction appelante, ne connait pas le type, par définition.


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 29-04-2007 à 18:40:08    

Emmanuel Delahaye a écrit :

As-tu lu mon article sur les composants logiciels cité au-dessus ?


Non, et je ne vois pas trop où il peut être sur ton site...
 

Emmanuel Delahaye a écrit :

As-tu déjà utilisé qsort() ?


Tout à fait. Mais cette fonction ne travaille absolument pas comme tu as décrits le callback ("une fonction qui sait quel est le type attendu et qui choisit le type qu'il faut au moment de l'appel" si je lis bien)
qsort() reçoit un tableau générique, le nombre d'éléments, la taille d'un élément. Et grace à cette taille, elle peut déplacer les éléments en interne pour les trier. Mais en interne, elle ne bosse que sur des caractères (probablement à coup de memcpy). Elle ne "choisit" donc pas le type. elle utilise le type le plus primitif qui puisse exister...
 

Emmanuel Delahaye a écrit :

Le principe de la programmation générique est que le maximum de code est unique et placé en bibliothèque, mais que ce qui est lié à l'application. Ici, la fonction de comparaison est confiée au programmeur. Les paramètres de la fonction de comparaison (adresse des éléments) sont évidemment de type void * (en l'occurence, void const *), car la fonction appelante, ne connait pas le type, par définition.


Oui, je connais ce principe et je me suis moi-même amusé à programmer un "tri par arbre" universel sur le même principe que qsort(). Mais là aussi il est impératif que ma fonction connaisse la taille d'un élément
 

Emmanuel Delahaye a écrit :

Bien sûr, en fonction du traitement à faire une indication sur la taille de l'objet pointé peut être utile... mais dans le cas d'un callback...


En fait, quand je lis cette phrase, j'en déduis qu'une fonction peut utiliser un type générique sans connaître la taille de l'élément qu'on lui passe et j'arrive pas à m'imaginer comment elle peut faire...

Message cité 1 fois
Message édité par Sve@r le 29-04-2007 à 18:56:33

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 29-04-2007 à 19:27:31    

Sve@r a écrit :

Non, et je ne vois pas trop où il peut être sur ton site...


http://mapage.noos.fr/emdel/complog.htm

Citation :


Tout à fait. Mais cette fonction ne travaille absolument pas comme tu as décrits le callback ("une fonction qui sait quel est le type attendu et qui choisit le type qu'il faut au moment de l'appel" si je lis bien)
qsort() reçoit un tableau générique, le nombre d'éléments, la taille d'un élément. Et grace à cette taille, elle peut déplacer les éléments en interne pour les trier. Mais en interne, elle ne bosse que sur des caractères (probablement à coup de memcpy). Elle ne "choisit" donc pas le type. elle utilise le type le plus primitif qui puisse exister...


Ca, c'est en interne, mais regarde bien le prototype de la fonction de comparaison, qui est une fonction de callback.

Citation :


Oui, je connais ce principe et je me suis moi-même amusé à programmer un "tri par arbre" universel sur le même principe que qsort(). Mais là aussi il est impératif que ma fonction connaisse la taille d'un élément


Tout à fait. Mais encore une fois, je parle de la fonction de comparaison (le callback), et non de la fonction de tri.

Citation :


En fait, quand je lis cette phrase, j'en déduis qu'une fonction peut utiliser un type générique sans connaître la taille de l'élément qu'on lui passe et j'arrive pas à m'imaginer comment elle peut faire...


Elle se contente de passer l'adresse. Il ne faut pas confondre 2 usages des void * illustrés par qsort.  
 
1 - la programmation générique. Un objet est défini par un couple adresse (anonyme) / taille (en char). : void* / size_t. S'y ajoute le nombre d'éléments, puis qu'il s'agit d'un tableau. Exemple courants : qsort(), fwrite() etc.  
 
2 - le callback qui reçoit l'adresse de la ou des données à traiter (ici, 2 données en lecture seule à fin de comparaison). Cette adresse est évidemment anonyme, puisque le callback est lui-même générique. C'est dans la fonction de callback que l'on 'spécialise' en mettant le bon type grâce à des pointeurs intermédiaires (il existe des méthodes plus gores, mais je n'en parle pas) :  

Code :
  1. int cb_compare(void const *a, void const *b)
  2. {
  3.    T *pa = a;
  4.    T *pb = b;
  5.    /* maintenant, on a accès aux données pointées par pa et pb  
  6.        (simples, structure, tableau etc.) */
  7.    return ...; /* -xxx, 0, +xxx */
  8. }



---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 29-04-2007 à 20:58:53    

Emmanuel Delahaye a écrit :

Cette adresse est évidemment anonyme, puisque le callback est lui-même générique. C'est dans la fonction de callback que l'on 'spécialise' en mettant le bon type grâce à des pointeurs intermédiaires (il existe des méthodes plus gores, mais je n'en parle pas) :  

Code :
  1. int cb_compare(void const *a, void const *b)
  2. {
  3.    T *pa = a;
  4.    T *pb = b;
  5.    /* maintenant, on a accès aux données pointées par pa et pb  
  6.        (simples, structure, tableau etc.) */
  7.    return ...; /* -xxx, 0, +xxx */
  8. }



EVIDEMMENT !!!
Le callback c'est le pointeur sur la fonction de comparaison qu'on passe à qsort(). Et le prototype de ce pointeur de fonction ne peut être que "int (*ptFct)(void *, void *)" puisque le programmeur de qsort() ne connait pas à l'avance les types qu'elle devra traiter !!!  :love:  
 
Merci de l'exemple  :jap:  
 
Ce qui m'empêchait de bien piger, c'est que si c'est moi qui avait écrit "cb_compare()", je l'aurais écrit directement

Code :
  1. int cb_compare(T *pa, T *pb)
  2. {
  3.    /* ici, on a aussi accès aux données pointées par pa et pb  
  4.        (simples, structure, tableau etc.) */
  5.    return ...; // -xxx, 0, +xxx
  6. }


En me disant << le prototype de ptFct() dans qsort() reçoit deux pointeurs universels, je peux donc lui passer une fonction traitant deux pointeurs sur "T" sans crainte >>. Mais je présume que c'est peut-être pas forcément la meilleure façon d'écrire "cb_compare()" n'est-ce pas ???


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 29-04-2007 à 21:20:18    

Sve@r a écrit :

Ce qui m'empêchait de bien piger, c'est que si c'est moi qui avait écrit "cb_compare()", je l'aurais écrit directement

Code :
  1. int cb_compare(T *pa, T *pb)
  2. {
  3.    /* ici, on a aussi accès aux données pointées par pa et pb  
  4.        (simples, structure, tableau etc.) */
  5.    return ...; // -xxx, 0, +xxx
  6. }




Tu aurais eu un 'type incompatible' au moment de l'appel de qsort, car il attend l'adresse d'une fonction dnt le prototype est clairement défini :  

Code :
  1. void qsort (void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));


C'est donc :  

Code :
  1. int (*compar)(const void *, const void *)


si on met autre chose, le comportement est indéfini.


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 29-04-2007 à 22:44:43    

Emmanuel Delahaye a écrit :

car il attend l'adresse d'une fonction dnt le prototype est clairement défini :  
C'est donc :  

Code :
  1. int (*compar)(const void *, const void *)


si on met autre chose, le comportement est indéfini.


C'est là qu'on voit le minuscule détail qui fait la différence. J'ai toujours cru qu'un pointeur de type "void *" étant "universel" c'est à dire qu'on pouvait y mettre un pointeur sur n'importe quoi, j'en ai conclu qu'un prototype attendant un "void*" pouvait recevoir un "<truc> *"...
 
PS: J'aime pas quand on me dit que le comportement est indéfini parce que, dans 99,9% des cas, le comportement est tel qu'on s'y attend. Ex:

  • pas de return dans le main => comportement indéfini => j'ai toujours vu une sortie à "0"
  • définir une fonction avec un paramètre "(<type> *)" alors qu'elle est prototypée "(void *)" => comportement indéfini => dans 99% des cas, il y aura cast implicite

Message cité 2 fois
Message édité par Sve@r le 29-04-2007 à 23:16:40

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 29-04-2007 à 22:58:25    

Sve@r a écrit :

C'est là qu'on voit le minuscule détail qui fait la différence. J'ai toujours cru qu'un pointeur de type "void *" était "universel" c'est à dire qu'on pouvait y mettre un pointeur sur n'importe quoi. Et donc ce n'est pas le cas ???
 
Mais à l'inverse, le pointeur renvoyé par "malloc" est "void *" et on peut le mettre dans n'importe quel pointeur. Donc, si je comprends bien, on peut mettre un pointeur "void *" dans n'importe quel pointeur mais on ne peut pas mettre n'importe quel pointeur dans un "void *" ???


 
On le peut, sauf erreur de ma part:
 

Code :
  1. typedef void (*funcType1Arg)(char);
  2. typedef void (*funcType2Arg)(char, char);
  3. typedef void (*funcType3Arg)(char, char, char);
  4. void badStuff(int typeFunc, void* func, char* data)
  5. {
  6.   switch(typeFunc)
  7.   {
  8.     case 1:
  9.     {
  10.       funcType1Arg fun = (funcType1Arg)func;
  11.       fun(data[0]);
  12.     } break;
  13.     case 2:
  14.     {
  15.       funcType2Arg fun = (funcType2Arg)func;
  16.       fun(data[0], data[1]);
  17.     } break;
  18.     case 3:
  19.     {
  20.       funcType3Arg fun = (funcType3Arg)func;
  21.       fun(data[0], data[1], data[2]);
  22.     } break;
  23.     default:
  24.       break;
  25.   }
  26. }


Pas testé la compile... [:naughty]


Message édité par schnapsmann le 29-04-2007 à 22:59:11
Reply

Marsh Posté le 29-04-2007 à 23:46:21    

Sve@r a écrit :

C'est là qu'on voit le minuscule détail qui fait la différence. J'ai toujours cru qu'un pointeur de type "void *" étant "universel" c'est à dire qu'on pouvait y mettre un pointeur sur n'importe quoi,


exact (enfin objet, OK ? pas fonction).

Citation :


 j'en ai conclu qu'un prototype attendant un "void*" pouvait recevoir un "<truc> *"...


Oui. Mais ici il s'agit de la définition d'un pointeur de fonction. Si le prototype de la fonction n'est pas strictement identique, tous les compilateurs que je connais signalent une erreur ou un warning.

 
Citation :

PS: J'aime pas quand on me dit que le comportement est indéfini parce que, dans 99,9% des cas, le comportement est tel qu'on s'y attend. Ex:

  • pas de return dans le main => comportement indéfini => j'ai toujours vu une sortie à "0"



QoI : (Quality of Implementation) en C90, repris en C99. (le return 0 de main() est devenu facultatif, c'est pas la peine de le répéter...)

 
Citation :

  • définir une fonction avec un paramètre "(<type> *)" alors qu'elle est prototypée "(void *)" => comportement indéfini => dans 99% des cas, il y aura cast implicite

Ben c'est normal. Le prototype et la fonction doivent être identiques. Sinon, c'est le b*rd*l

 



Message édité par Emmanuel Delahaye le 29-04-2007 à 23:50:15

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 09-05-2007 à 17:45:44    

bonjour,
 
merci pour toutes les explications .... J'aurais encore une question si vous le voulez bien :
 
Quand on fait une fonction avec une liste d'arguments, et que la liste des arguments comporte pleins de types différents, comment fait-on ? On créer une structure ou y'a mieux à faire (void ??) ??
 
 
par exemple une fonction avec une liste d'arguments int, double et char :
 

Code :
  1. struct largs {
  2.   int a;
  3.   double b;
  4.   char c;
  5. };
  6. //et avec un prottype :
  7. int ma_fonction (struct * liste_arguments);


 
ou bien ??

Reply

Marsh Posté le 09-05-2007 à 17:57:11    

in_your_phion a écrit :

Quand on fait une fonction avec une liste d'arguments, et que la liste des arguments comporte pleins de types différents, comment fait-on ?

Code :
  1. struct largs {
  2.   int a;
  3.   double b;
  4.   char c;
  5. };
  6. //et avec un prottype :
  7. int ma_fonction (struct * liste_arguments);




C'est une solution. Le regroupement par structure doit avoir une logique liée à l'application elle-même. C'est une question de conception.

 

On peut aussi laisser les arguments un par un, surtout si il n'y a pas de pointeurs, mais ça multiplie les copies... Et si il y a 30 paramètres, ça va être facile à gérer. Bref, c'est un peu au cas par cas. Que veux-tu faire exactement ?

 


Message cité 1 fois
Message édité par Emmanuel Delahaye le 09-05-2007 à 17:57:40

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 10-05-2007 à 13:09:29    

Emmanuel Delahaye a écrit :

C'est une solution. Le regroupement par structure doit avoir une logique liée à l'application elle-même. C'est une question de conception.
 
On peut aussi laisser les arguments un par un, surtout si il n'y a pas de pointeurs, mais ça multiplie les copies... Et si il y a 30 paramètres, ça va être facile à gérer. Bref, c'est un peu au cas par cas. Que veux-tu faire exactement ?


 
salut,
 
en fait c'était en référence à la question su les pointeurs de fonctions. Si par exemple on veut un poiteur sur une fonction qui fait une opération, mais que cette opération peut prendre en arguments des doubles, ou des float, etc. Dans ce cas comment fait-on ?

Reply

Marsh Posté le 10-05-2007 à 13:24:18    

Je serais tenté de dire qu'il faudrait architecturer ton appli différemment de manière à ce que ta fonction prennent toujours des arguments du même type (l'utilisation d'un enum + struct entre dans ce cas de figure). Si ce n'est pas possible, je vois deux solutions :
 

  • Tu passes à ta fonction un void *, justement, ainsi qu'un paramètre supplémentaire qui permet de connaître le type référencé. Evidemment tu ne peux pas passer tes paramètre par valeur de cette façon.


  • Tu utilises les va_arg comme le fait printf

Message cité 1 fois
Message édité par matafan le 10-05-2007 à 13:26:16
Reply

Marsh Posté le 10-05-2007 à 14:27:18    

matafan a écrit :

Je serais tenté de dire qu'il faudrait architecturer ton appli différemment de manière à ce que ta fonction prennent toujours des arguments du même type (l'utilisation d'un enum + struct entre dans ce cas de figure). Si ce n'est pas possible, je vois deux solutions :

 
  • Tu passes à ta fonction un void *, justement, ainsi qu'un paramètre supplémentaire qui permet de connaître le type référencé. Evidemment tu ne peux pas passer tes paramètre par valeur de cette façon.


  • Tu utilises les va_arg comme le fait printf



  • Tu utilises une structure avec un sélecteur et une union. C'est plus clair que les variadics ou qu'un void*. C'est déterministe.
 


Message édité par Emmanuel Delahaye le 10-05-2007 à 14:27:55

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 10-05-2007 à 15:49:43    

Oui, c'était ma parenthèse ;)

Reply

Marsh Posté le 10-05-2007 à 16:45:31    

matafan a écrit :

Oui, c'était ma parenthèse ;)


Ah, oui, en effet. Elle méritait un  



---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 11-05-2007 à 16:24:22    

que pensez vous de l'exemple suivant, qui utilise un void ? C'est ce à quoi je pensais ...

 

C'est un bout de code d'exmple d'une librairie mathématique (gsl). Dans l'exemple, on initialise une fonction avec une liste de paramètres  pointé par un void *. Y'a des cast un peu partouts, et je ne comprend pas bien l'interêt....Pourriez vous m'éclairer ?

 

le lien http://www.gnu.org/software/gsl/ma [...] o-minimize

 

l'exemple :

 
Code :
  1. //The following example function defines a simple paraboloid with two parameters,
  2.      /* Paraboloid centered on (dp[0],dp[1]) */
  3.    
  4.      double
  5.      my_f (const gsl_vector *v, void *params)
  6.      {
  7.        double x, y;
  8.        double *dp = (double *)params;
  9.    
  10.        x = gsl_vector_get(v, 0);
  11.        y = gsl_vector_get(v, 1);
  12.    
  13.        return 10.0 * (x - dp[0]) * (x - dp[0]) +
  14.                 20.0 * (y - dp[1]) * (y - dp[1]) + 30.0;
  15.      }
  16.    
  17.      /* The gradient of f, df = (df/dx, df/dy). */
  18.      void
  19.      my_df (const gsl_vector *v, void *params,
  20.             gsl_vector *df)
  21.      {
  22.        double x, y;
  23.        double *dp = (double *)params;
  24.    
  25.        x = gsl_vector_get(v, 0);
  26.        y = gsl_vector_get(v, 1);
  27.    
  28.        gsl_vector_set(df, 0, 20.0 * (x - dp[0]));
  29.        gsl_vector_set(df, 1, 40.0 * (y - dp[1]));
  30.      }
  31.    
  32.      /* Compute both f and df together. */
  33.      void
  34.      my_fdf (const gsl_vector *x, void *params,
  35.              double *f, gsl_vector *df)
  36.      {
  37.        *f = my_f(x, params);
  38.        my_df(x, params, df);
  39.      }
  40. //The function can be initialized using the following code,
  41.      gsl_multimin_function_fdf my_func;
  42.    
  43.      double p[2] = { 1.0, 2.0 }; /* center at (1,2) */
  44.    
  45.      my_func.f = &my_f;
  46.      my_func.df = &my_df;
  47.      my_func.fdf = &my_fdf;
  48.      my_func.n = 2;
  49.      my_func.params = (void *)p;

Message cité 1 fois
Message édité par in_your_phion le 11-05-2007 à 16:25:20
Reply

Marsh Posté le 11-05-2007 à 18:24:17    

in_your_phion a écrit :

Y'a des cast un peu partouts, et je ne comprend pas bien l'interêt....Pourriez vous m'éclairer ?


Je présume que tu parles de ces cast là...

Citation :

Code :
  1. double  my_f (const gsl_vector *v, void *params)
  2. {
  3.        double *dp = (double *)params;
  4.        ......
  5. }



C'est obligatoire. La variable "params" est un pointeur sur un truc indéfini (un truc qui peut être char, short, long, etc... d'où le terme "pointeur universel" ).
 
Si tu commences à vouloir manipuler "*params" (ce qu'il y a à l'adresse stockée dans "params" ); le compilo ne saura pas si, à partir de cette adresse de départ, il faut récupérer un octet (char), 2 octets (int), 4 octets (long/float) ou 8 octets (double) puisque "*param" est indéfini
 
Donc le programmeur, qui connaît ce qu'il reçoit, stocke ce qu'il reçoit dans la variable "dp" qui est un type connu. Ensuite, il pourra utiliser "*dp" autant qu'il voudra.
Le cast c'est juste pour éviter les warning. Un pointeur étant toujours un pointeur, on peut très bien écrire "dp=params" mais le compilo préviendra en disant "attention, êtes-vous sûr de ce que vous faites" donc cast

Message cité 1 fois
Message édité par Sve@r le 11-05-2007 à 18:25:50

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 11-05-2007 à 18:38:23    

Sve@r a écrit :

Je présume que tu parles de ces cast là...

Citation :

Code :
  1. double  my_f (const gsl_vector *v, void *params)
  2. {
  3.        double *dp = (double *)params;
  4.        ......
  5. }



C'est obligatoire.<...>


 :ouch: C'est pas du tout obligatoire en C...

Code :
  1. double  my_f (const gsl_vector *v, void *params)
  2. {
  3.        double *dp = params;
  4.        ......
  5. }


est du C parfaitement valide et standard...

Message cité 1 fois
Message édité par Emmanuel Delahaye le 11-05-2007 à 18:39:07

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 17-05-2007 à 10:01:58    

Emmanuel Delahaye a écrit :

:ouch: C'est pas du tout obligatoire en C...


Exact - C'est écrit à la page 199 du K&R (1er paragraphe). En fait, c'est obligatoire pour tout autre pointeur que "void *" d'où ma confusion.
Comme d'hab, tu maîtrises l'extrème soucis du minuscule détail qui font et feront toujours la différence. Ptet un brin agaçant tout de même  ;)  


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed