Retour d'une fonction

Retour d'une fonction - C - Programmation

Marsh Posté le 10-01-2006 à 15:52:01    

Bonjour  :hello: ,
 
Je me posais la question suivante :
 
lorsque l'on crée une fonction qui renvoi un résultat structurellement complexe (pointeur sur une structure par exemple...), il y a deux solutions pour renvoyer le résultat :
- soit on passe un parametre par adresse et on y stocke le résultat de la fonction (du coup on peut se garder le retour de la fonction pour y mettre un code d'erreur par exemple)
- soit on renvoi un pointeur sur la structure (créée alors avec de l'allocation dynamique dans le corps de la fonction )
 
Quoi qu'est le mieux et le plus utilisé ?

Reply

Marsh Posté le 10-01-2006 à 15:52:01   

Reply

Marsh Posté le 10-01-2006 à 15:54:50    

Si ma fonction ne retourne pas d'autre information qu'une structure (ou NULL si erreur), je retourne la structure directement.
Ca évite de faire mumuse avec des pointeurs de pointeurs de pointeurs de ...
 
Si je dois renvoyer plusieurs informations, en plus de savoir si ça s'est bien passé ou non (par exemple un nombre de structures créées ou autre), je passe par adresse.
 
Après, j'imagine que chacun sa manière de faire. [:spamafote]

Reply

Marsh Posté le 10-01-2006 à 16:17:09    

J'aurais pensé qu'il existait une sorte de convention ...  
Genre strcpy demande bien un pointeur pour le résultat (j'imagine que c pour laisser à l'utilisateur le soin de gérer la mémoire) alors qu'il pourrait très bien retourner une copie directement ...
Je pense qu'il doit y avoir aussi une histoire de : si création d'un nouvel objet alors retour direct sinon passage de l'objet en parametre par adresse...
 
en tout cas merci de ta réponse (c'est aussi un peu comme cela que je pense le truc, mais comme je voudrais encore m'améliorer je cherche la petite bête ;)

Reply

Marsh Posté le 10-01-2006 à 16:19:32    

julien_54 a écrit :


Genre strcpy demande bien un pointeur pour le résultat (j'imagine que c pour laisser à l'utilisateur le soin de gérer la mémoire) alors qu'il pourrait très bien retourner une copie directement ...


ben les string sont deja des pointeurs, alors...

Reply

Marsh Posté le 10-01-2006 à 16:26:38    

julien_54 a écrit :

J'aurais pensé qu'il existait une sorte de convention ...  
Genre strcpy demande bien un pointeur pour le résultat (j'imagine que c pour laisser à l'utilisateur le soin de gérer la mémoire) alors qu'il pourrait très bien retourner une copie directement ...


 
strcpy() ne crée rien, il écrit dans une zone mémoire ; dans ma réponse je suis parti du principe que je créais la structure, parce que dans les autres cas de figure, la question ne se pose pas.
Même s'il y a de (bien petites) subtilités, comme dans le cas de fgets() par exemple.
 

julien_54 a écrit :

Je pense qu'il doit y avoir aussi une histoire de : si création d'un nouvel objet alors retour direct sinon passage de l'objet en parametre par adresse...


 
Ca dépend de ce que tu dois retourner. Un passage par adresse ne sert que rarement à la création d'un nouvel objet, je ne trouve cela justifié que si plusieurs données doivent être retournées. Et encore, y a souvent un problème de conception derrière (parce que ça suppose de passer en paramètre une variable non encore définie, ou nulle, moi ça me pose un problème).
Le passage par adresse sert surtout à modifier la valeur d'une variable déjà créée. Comme dans le cas de strcpy(), justement.


Message édité par Elmoricq le 10-01-2006 à 16:36:03
Reply

Marsh Posté le 10-01-2006 à 16:50:26    

D'accord avec toi si ce n'est le fait que pour strcpy le passage par adresse ne se justifie pas par "modifier la valeur d'une variable déjà créée" puisque justement à part "écrire dans une zone mémoire", strcpy ne modifie rien (en terme de valeur, pas en terme de mémoire) au mieux il écrase, contrairement à strcat qui a bien comme objectif de modifier la valeur ...

Reply

Marsh Posté le 10-01-2006 à 16:52:59    

[:pingouino]
 
Dans le fond, aucune différence entre strcpy() et strcat().  
Le premier écrit depuis le début, le second écrit à partir du premier caractère NULL qu'il trouve.
Point barre.

Message cité 1 fois
Message édité par Elmoricq le 10-01-2006 à 16:53:39
Reply

Marsh Posté le 10-01-2006 à 17:05:46    

Pas au niveau conceptuel (l'un crée l'autre modifie...)
En tout cas ce n'est pas le sujet de mon post...  
N'y a t'il pas un avantage d'optimisation au niveau du retour de fonction avec parametre par adresse ...?

Reply

Marsh Posté le 10-01-2006 à 17:10:45    

julien_54 a écrit :

Pas au niveau conceptuel (l'un crée l'autre modifie...)
En tout cas ce n'est pas le sujet de mon post...


 
Non. Le concept est le même, c'est une écriture dans une zone mémoire préalablement allouée.
L'écriture ne débute pas au même endroit, c'est tout.
Si tu veux deux concepts différents, étudie strdup() et strcpy().
 

julien_54 a écrit :

N'y a t'il pas un avantage d'optimisation au niveau du retour de fonction avec parametre par adresse ...?


 
Optimisation n°1 : ne pas optimiser.
 
Surtout à ce niveau-là. Les optimisations de taille se situent au niveau des algorithmes employés.
Si tu as besoin faire de la micro-optimisation, tu as de sérieux ennuis.

Reply

Marsh Posté le 10-01-2006 à 17:22:13    

Lorsque l'on manipule des programmes dont la partie critique recouvre 50 % du temps de l'execution et que cette partie est un algorithme mathématique déjà connues et reconnues par des mathématiciens experts, on peut avoir à optimiser, voir utiliser des routines d'appel de code assembleur directement dans le code C.  
 
Mais encore une fois ce n'est pas le sujet, je cherche juste des idées fraiches et judicieuses provenant d'expériences riches d'autres programmeurs, pour mettre à jour mes connaissances...

Reply

Marsh Posté le 10-01-2006 à 17:22:13   

Reply

Marsh Posté le 10-01-2006 à 19:18:00    

On fait comme on veut, suivant les cas.
 
Il y a des cas ou tu es oblige de passer un parametre par reference, plutot que d'utiliser la valeur de retour : par exemple si la fonction doit retourner plusieurs structures (car on ne peut evidemment retourner qu'une seule valeur), ou si tu preferes utiliser la valeur de retour pour indiquer differents cas d'erreur (bref au lieux d'avoir simplement NULL <=> erreur, tu consideres que 1 indique un type d'erreur, 2 un autre type d'erreur...). Apres, a partir du moment ou dans une API tu as un cas ou tu a besoin de passer par reference, il peut etre judicieux, par souci de consistence, d'utiliser systematiquement des references plutot que les valeurs de retour.
 
Je ne vois pas en quoi passer par la valeur de retour serait plus rapide que passer par une reference.
 
Si on peut passer par la valeur de retour, alors on peut aussi passer par une reference. Le contraire n'est pas vrai. La solution par reference est donc plus generale est plus soupe.
 
Ca ne veut pas dire pour autant qu'il faut systematiquement passer par une reference. Il y a des cas ou utiliser la valeur de retour peut sembler plus "naturel". Ca peut etre le cas par exemple si ta fonction a principalement un role de "constructeur", comme strdup par exemple. Il peut sembler plus naturel de faire c = new_circle(center, radius) que new_circle(&c, center, radius) car l'output (c) est clairement separe de l'input (center et radius).

Reply

Marsh Posté le 10-01-2006 à 20:11:09    

julien_54 a écrit :

Lorsque l'on manipule des programmes dont la partie critique recouvre 50 % du temps de l'execution et que cette partie est un algorithme mathématique déjà connues et reconnues par des mathématiciens experts, on peut avoir à optimiser, voir utiliser des routines d'appel de code assembleur directement dans le code C.  
 
Mais encore une fois ce n'est pas le sujet, je cherche juste des idées fraiches et judicieuses provenant d'expériences riches d'autres programmeurs, pour mettre à jour mes connaissances...


 
Idée à exploiter : plutôt que de micro-optimiser, ce qui en dehors de cas très précis ne sert pas à grand chose d'autre qu'à rendre le code difficilement compréhensible, il vaut mieux utiliser un logiciel de profiling.
A partir de ça on peut déterminer quelle partie du programme est la plus consommatrice, et pourquoi. On peut très facilement améliorer les performances sans circonvenir à la lisibilité du code avec ce type d'outil.

Reply

Marsh Posté le 11-01-2006 à 00:06:20    

Elmoricq a écrit :

à partir du premier caractère NULL qu'il trouve.


nul ou null !


---------------
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-01-2006 à 01:50:42    

julien_54 a écrit :

Lorsque l'on manipule des programmes dont la partie critique recouvre 50 % du temps de l'execution et que cette partie est un algorithme mathématique déjà connues et reconnues par des mathématiciens experts, on peut avoir à optimiser, voir utiliser des routines d'appel de code assembleur directement dans le code C.  
 
Mais encore une fois ce n'est pas le sujet, je cherche juste des idées fraiches et judicieuses provenant d'expériences riches d'autres programmeurs, pour mettre à jour mes connaissances...


 
Dans le cas où tu passes à ta fonction l'adresse d'un objet existant, tu dois d'abord t'assurer que l'objet existe bien avant d'appeler la fonction. C'est le cas de "strcpy()"
 
Dans le cas où ta fonction renvoie un pointeur sur un objet qu'elle aura créé elle-même, tu t'affranchis de ta préoccupation de création mais tu es obligé de t'assurer toi-même de la libération de la mémoire allouée. C'est le cas de "strdup()" (merci Elmoricq pour ces exemples bien choisis)
 
Chaque solution a donc ses avantages et ses inconvénients. Personnellement (je sais pas pourqoui) je préfère la première solution. Peut-être parce que j'associe inconsciemment "free" avec "malloc" et que j'aime pas l'utiliser si j'ai pas fait moi-même le malloc...
Par ailleurs, je trouve que c'est plus facile de porter une fonction de ce style en C++.
 
Exemple en C

struct s_travail {
    <tititi>
    <tatata>
    <tututu>
};
 
void Action(struct s_travail *pt, ..., ...)
{
    pt-><tititi>=...
    pt-><tatata>=...
    pt-><tututu>=...
}


 
Le même en C++

class c_travail {
    public:
    <tititi>
    <tatata>
    <tututu>
    void Action(..., ...);
};
 
void c_travail::Action(..., ...)
{
    this-><tititi>=...
    this-><tatata>=...
    this-><tututu>=...
    // Et encore, le mot "this" peut être omis...
}

Message cité 1 fois
Message édité par Sve@r le 11-01-2006 à 01:54:09

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

Marsh Posté le 11-01-2006 à 08:38:51    

Sve@r a écrit :

Chaque solution a donc ses avantages et ses inconvénients. Personnellement (je sais pas pourqoui) je préfère la première solution. Peut-être parce que j'associe inconsciemment "free" avec "malloc" et que j'aime pas l'utiliser si j'ai pas fait moi-même le malloc...


C'est pour ça que je préfère implémenter clairement des 'constructeurs/destructeurs' (xxx_create() / xxx_delete()) :


   xxx_s *p = xxx_create();
 
   if (p != NULL)
   {
      xxx_usage (p, ...);
 
      xxx_delete(p), p = NULL;
   }


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


Message édité par Emmanuel Delahaye le 11-01-2006 à 08:41:27

---------------
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-01-2006 à 08:40:26    

Merci pour tous les messages constructifs !

Reply

Sujets relatifs:

Leave a Replay

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