OVH Cloud OVH Cloud

pointeur de fonction de type perso

29 réponses
Avatar
Nicolas aunai
salut


j'ai ce cas là :

typedef union pointeurs{

HANDLE (*fct_handle)();//oui je c, pas standard...
void (*fct_void)();
device (*fct_device)();

}pointeurs;

avec device défini ainsi :

typedef struct{

char cdrom[3];
char path[15];

}device;


ensuite, je fais :

pointeurs tab_fct[4];

tab_fct[0].fct_handle=opendevice;
tab_fct[1].fct_void=EjectVolume;
tab_fct[2].fct_void=load;
tab_fct[3].fct_device=Get_Drives;


là ça compile bien, sauf que.... pour la ligne :

tab_fct[3].fct_device=Get_Drives;

j'ai le warning :
[Warning] assignment from incompatible pointer type

je sais pourquoi, mais ne sais pas trop comment résoudre ce problème....

en fait Get_Drives ne retourne pas une structure de type device, mais un
pointeur sur une structure de type device...
donc je pense que l'erreur vient du fait que j'ai mal défini mon pointeur de
fonction...

bon, j'aif ait un truc qui marche, mais c'est un peu de la chance :

device *(*fct_device)();

qqn peut-il m'expliquer cette notation un peu compliquée ?


--
nico,
http://astrosurf.com/nicoastro
messenger : nicolas_aunai@nospam@hotmail.com

10 réponses

1 2 3
Avatar
Nicolas aunai
Anthony a écrit:

Tu dois donc écrire device* (*fct)();

En fait, tu as déja tout compris et je ne vois pas vraiment ton
problème...


ouais bah ché pas... c'est la notation :

type *(*fonction)();

qui me semblait louche.....


--
nico,
http://astrosurf.com/nicoastro
messenger : @hotmail.com

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "Nicolas aunai" @free.fr> wrote:

typedef union pointeurs{

HANDLE (*fct_handle)();//oui je c, pas standard...
void (*fct_void)();
device (*fct_device)();


Ca m'étonnerait qu'une fonction retourne une structure ...

}pointeurs;

avec device défini ainsi :

typedef struct{

char cdrom[3];
char path[15];

}device;


... surtout une grosse comme ça...


ensuite, je fais :

pointeurs tab_fct[4];

tab_fct[0].fct_handle=opendevice;
tab_fct[1].fct_void=EjectVolume;
tab_fct[2].fct_void=load;
tab_fct[3].fct_device=Get_Drives;


Ben oui. Quel est le prototype de 'Get_Drives' ?

Je parie pour

device *Get_Drives (<des parametres>);

là ça compile bien, sauf que.... pour la ligne :

tab_fct[3].fct_device=Get_Drives;

j'ai le warning :
[Warning] assignment from incompatible pointer type

je sais pourquoi, mais ne sais pas trop comment résoudre ce problème....
Il suffit de rajouter l'*' qui va bien.


en fait Get_Drives ne retourne pas une structure de type device, mais un
pointeur sur une structure de type device...


Ben, tient!

donc je pense que l'erreur vient du fait que j'ai mal défini mon
pointeur de fonction...


Tu te réponds toi même?

bon, j'aif ait un truc qui marche, mais c'est un peu de la chance :

device *(*fct_device)();


C'est pas de la chance, c'est tout à fait logique.

qqn peut-il m'expliquer cette notation un peu compliquée ?


Bon, ben finalement tu aimes le gore... Je t'avais pourtant montré une façon
de coder qui évite ce genre de lourdeur...

Je propose ça :

typedef struct
{
char cdrom[3];
char path[15];
}
device_s;

typedef HANDLE handle_f ();
typedef void_f ();
device_s *device_f ();

typedef union
{
handle_f fct_handle();
void_f fct_void();
device_f fct_device();
}
pointeurs_u;

pointeurs_u tab_fct[4];

tab_fct[0].fct_handle = opendevice;
tab_fct[1].fct_void = EjectVolume;
tab_fct[2].fct_void = load;
tab_fct[3].fct_device = Get_Drives;


--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/

Avatar
Marc Boyer
Emmanuel Delahaye wrote:
In 'fr.comp.lang.c', "Nicolas aunai" @free.fr> wrote:
typedef union pointeurs{

HANDLE (*fct_handle)();//oui je c, pas standard...
void (*fct_void)();
device (*fct_device)();


Ca m'étonnerait qu'une fonction retourne une structure ...


Heuh, pourquoi ?
C'est mal ?

... surtout une grosse comme ça...


En quoi est-ce pire qu'une autre version ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


Avatar
Marc Boyer
In article <3f02ab02$0$26624$, Nicolas aunai wrote:
bon, j'aif ait un truc qui marche, mais c'est un peu de la chance :
device *(*fct_device)();
qqn peut-il m'expliquer cette notation un peu compliquée ?


Si je l'écris:

(device*) (*fct_device)();

Cela te parait-il plus simple ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Avatar
AG
Ca m'étonnerait qu'une fonction retourne une structure ...



Heuh, pourquoi ?
C'est mal ?
Quand une fonction renvoie une valeur, il y a recopie de la valeur

quelque part (dans la pile je pense) au moment ou on sort de la
fonction. Si la valeur est une structure, grosse qui plus est, le
mécanisme devient lourd (les recopies prennent du temps). C'est pour
cela qu'on préfère passer des pointeurs sur les structures plutot que
les structures elles-même, et de la même manière, on préfère renvoyer un
pointeur sur une structure plutôt que la structure elle même, même si
c'est faisable.

Mais ça m'étonne que tu poses la question.

Alexandre.


Avatar
Marc Boyer
In article , AG wrote:
Lors de l'appel d'une fonction, le contexte de la fonction est sauvée,
(ce qui est dans la pile est copié en mémoire) et les paramètres de la
fonction sont copiés systématiquement dans la pile. (c'est pour ce la
qu'on dit qu'on travaille sur des copies locales). On travaille ensuite
avec ces paramètres.


Hormis le fait (que j'ai appris récemment ici) que toutes les
implémentations n'ont pas de pile, oui, cela se passe souvent
comme cela.

Pour faire
ensuite une opération avec une valeur à l'intérieur de la structure, il
faut la charger dans le registre. Si on peut la charger depuis la
mémoire directement (et pas depuis la pile), ça économise une copie.
D'aucun argumenteront que le chargement depuis la pile doit être plus
rapide : Mais dans le cas de gros chargement, la différence doit être
négligeable par rapport à l'économie d'une copie...


Je ne vois pas pourquoi un chargement depuis la mémoire serait plus
couteux que depuis la pile ? Si pile il y a, elle sera surement
en mémoire ou dans un cache plus rapide d'accès encore.

Je ne sais pas si je suis assez clair, mais je pense que c'est comme ça
que ça fonctionne.


Je crois que le surcout vient surtout de la copie de la valeur dans
la pile vers la valeur de stockage, que tu as oublié.

Et puis bon, les compilateurs savent souvent assez
bien supprimer les copies mémoires inutiles.
Certes, mais c'est pas pour rien qu'on a inventé le mot clef inline (qui

n'existe pas en C): c'est pour dire si l'appelle de fonction est un
mécanisme qui peut faire perdre du temps.


Les compilateurs faisaient de l'inline avant que le mot-clef
n'apparaisse.
L'apparition du mot clef en C++ s'accompagne d'une contrainte
(visibilité du code dans les codes appelant la fonction) qui
facilite le travail du compilateur (et qui était peut-être plus
nécessaire en C++, avec les fonctions membres de 2 lignes,
qu'en C)

L'introduction de pointeur est une source d'erreur (oui, je sais,
les bons programmeurs ne font pas d'erreur de manipulation
de pointeur)
Je pense que c'est discutable à ton niveau.



Tu sais, la dernière erreur qui m'a tenue en aleine pendant
quasiment 2h, c'était
it1 != end(vect1) && it2 != end(vect1)
qui aurait du être
it1 != end(vect1) && it2 != end(vect2)
Les questions de niveau ne protègent pas de l'étourderie.
Au mieux, l'experience nous apprend à éviter certaines
constructions sources d'erreur.
Moi, j'évite les pointeurs quand je peux.

Se pose la question de "qui doit libérer la mémoire" ?
Dans ce cas là, il faut le préciser, c'est vrai, et c'est source d'oublis.



C'est pour ça que la majorité de mes fonctions de
création retourne des structures, ou prennent un pointeur
de structure en paramêtre.
Mais quand je retourne un pointeur de structure, c'est
généralement après un typedef qui cache complètement l'aspect
pointeur de la donnée manipulée.

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(


Avatar
AG
Oui, mais de toute façon, la copie doit avoir lieu à un moment
ou un autre, non ? Il faut recopier le résultat calculé à l'endroit
ou on veut qu'il soit. Sans compter qu'avec les mécanismes
de cache, on peut vouloir préférer travailler avec la pile
(espace contigue en mémoire, facilement dans le cache) qu'avec
de la mémoire éparpillée partout.


Je ne suis pas non plus un expert, mais je pense que ça fonctionne comme
cela :

Lors de l'appel d'une fonction, le contexte de la fonction est sauvée,
(ce qui est dans la pile est copié en mémoire) et les paramètres de la
fonction sont copiés systématiquement dans la pile. (c'est pour ce la
qu'on dit qu'on travaille sur des copies locales). On travaille ensuite
avec ces paramètres.

Lorsqu'on ne passe qu'un pointeur sur une structure, seule l'adresse du
pointeur est copié dans la pile. Si ce pointeur sert (par exemple) à ce
qu'on puisse renvoyer une valeur mais qu'on n'est pas intéressé par la
valeur de la structure (à l'appel de la fonction), il y a déjà une copie
dans la pile qui ne sert à rien. Mais oublions ce cas. Pour faire
ensuite une opération avec une valeur à l'intérieur de la structure, il
faut la charger dans le registre. Si on peut la charger depuis la
mémoire directement (et pas depuis la pile), ça économise une copie.
D'aucun argumenteront que le chargement depuis la pile doit être plus
rapide : Mais dans le cas de gros chargement, la différence doit être
négligeable par rapport à l'économie d'une copie...


Je ne sais pas si je suis assez clair, mais je pense que c'est comme ça
que ça fonctionne.



Et puis bon, les compilateurs savent souvent assez
bien supprimer les copies mémoires inutiles.
Certes, mais c'est pas pour rien qu'on a inventé le mot clef inline (qui

n'existe pas en C): c'est pour dire si l'appelle de fonction est un
mécanisme qui peut faire perdre du temps.

Si la valeur est une structure, grosse qui plus est, le
mécanisme devient lourd (les recopies prennent du temps).



C'est quoi une "grosse" structure ?
Oui, c'est flou. Mais ton approche "sémantique d'abord, puis

optimisation après mesure du code" te permet d'avoir une idée de ce
qu'est une structure "grosse".

Peut-être parce que je n'aime pas trop les règles de programmation
simpliste "on ne retourne pas une structure".
certes.



L'introduction de pointeur est une source d'erreur (oui, je sais,
les bons programmeurs ne font pas d'erreur de manipulation
de pointeur)
Je pense que c'est discutable à ton niveau.



Se pose la question de "qui doit libérer la mémoire" ?
Dans ce cas là, il faut le préciser, c'est vrai, et c'est source d'oublis.


Alexandre.


Avatar
Marc Lasson
AG a écrit:
< gna gna gna ...>

Certes, mais c'est pas pour rien qu'on a inventé le mot clef inline (qui
n'existe pas en C): c'est pour dire si l'appelle de fonction est un
mécanisme qui peut faire perdre du temps.


inline est dans C99.

--
Marc.

Avatar
Olivier Aumage
Marc Boyer writes:

In article , AG wrote:
Quand une fonction renvoie une valeur, il y a recopie de la valeur
quelque part (dans la pile je pense) au moment ou on sort de la
fonction.


Oui, mais de toute façon, la copie doit avoir lieu à un moment
ou un autre, non ? Il faut recopier le résultat calculé à l'endroit
ou on veut qu'il soit.


Il vaut mieux laisser la fonction appelante décider où et quand faire
la ou les éventuelles copies si nécessaire (ou ne pas en faire si
c'est inutile).

Aussi étonnant que cela puisse paraître, écrire une fonction de copie
optimale pour un processeur donné est particulièrement difficile sur
des processeurs récents. Ce qui signifie que les codes de copie
insérés par le compilateurs pour réaliser la copie vers et depuis la
pile est seront dans l'un des trois cas suivants:
. générique mais peu efficace.
. efficace mais peu 'portable' sur d'autres processeurs de la même
famille
. intermédiaire, et dans ce cas très similaire à 'memcpy'

Donc, il est à mon avis préférable de ne pas risquer l'un des deux
premiers cas en utilisant memcpy explicitement.

Sans compter qu'avec les mécanismes
de cache, on peut vouloir préférer travailler avec la pile
(espace contigue en mémoire, facilement dans le cache) qu'avec
de la mémoire éparpillée partout.


Si la structure a été remplie/initialisée/modifiée avant l'appel de la
fonction, elle sera sans doute également dans le cache (au moins le
second niveau).

Les caches fonctionnent d'autant mieux que les zones de mémoire
utilisées varient peu. Or le fait de traiter de grosses structures sur
la pile va précisément à l'encontre de ce principe.

Et puis bon, les compilateurs savent souvent assez
bien supprimer les copies mémoires inutiles.


Je ne parierais pas trop sur ça pour des structures... :-)

Si la valeur est une structure, grosse qui plus est, le
mécanisme devient lourd (les recopies prennent du temps).


C'est quoi une "grosse" structure ?


sizeof(struct ma_structure)) > sizeof(struct ma_structure *))

C'est pour
cela qu'on préfère passer des pointeurs sur les structures plutot que
les structures elles-même, et de la même manière, on préfère renvoyer un
pointeur sur une structure plutôt que la structure elle même, même si
c'est faisable.

Mais ça m'étonne que tu poses la question.


Peut-être parce que je n'aime pas trop les règles de programmation
simpliste "on ne retourne pas une structure".
L'introduction de pointeur est une source d'erreur (oui, je sais,
les bons programmeurs ne font pas d'erreur de manipulation
de pointeur).


"L'introduction de pointeur est une source d'erreur" n'est-elle donc
pas également une règle un peu simpliste ? ;-)

L'usage montre qu'avec un minimum de rigueur, l'utilisation de
pointeurs n'apporte pas particulièrement plus de problème qu'autre
chose. Le pointeur est sans doute l'élément central du C. Pourquoi
vouloir le contourner ? :-)

Se pose la question de "qui doit libérer la mémoire" ?


Celui qui l'alloue, autant que possible.

Autant je veux bien conseiller le passage de paramêtre par
pointeur, autant pour le retour de fonction...

Pour ma part, je préfère réfléchir en terme de sémantique,
puis optimiser après mesure du code.


En réalité, c'est moins un problème d'optimisation qu'un problème de
cohérence et d'homogénéité. En utilisant systématiquement des
références, tu n'as plus à alterner entre des niveaux d'indirection
différents (le pointeur vers ta structure devient l'unique point
d'accès à cette structure), tu n'as plus à alterner entre des opérateurs
d'accès différents ('->' devient l'unique opérateur d'accès aux
membres), tu n'as plus à alterner les 'struct s' et les 'struct s *'
dans les déclarations de variables, d'arguments et de valeur de retour.
Il n'y a d'ailleur plus besoin de 'retourner' la structure
puisqu'elle est passée par référence, ce qui libère la valeur de
retour pour indiquer un code d'erreur par exemple, alors qu'en
retournant directement une structure, il est difficile d'indiquer
une situation d'echec.

Bref, voila quelques points de reflexions qui n'engagent que moi. Les
questions de style sont de toute façon purement personnelles.

--
Olivier


Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', Marc Boyer wrote:

In 'fr.comp.lang.c', "Nicolas aunai" @free.fr> wrote:
typedef union pointeurs{

HANDLE (*fct_handle)();//oui je c, pas standard...
void (*fct_void)();
device (*fct_device)();


Ca m'étonnerait qu'une fonction retourne une structure ...


Heuh, pourquoi ?
C'est mal ?


Ca peut l'être.

... surtout une grosse comme ça...


En quoi est-ce pire qu'une autre version ?


Plus la structure est grosse, plus la copie est longue.

--
-ed- [remove YOURBRA before answering me]
The C-language FAQ: http://www.eskimo.com/~scs/C-faq/top.html
<blank line>
FAQ de f.c.l.c : http://www.isty-info.uvsq.fr/~rumeau/fclc/



1 2 3