OVH Cloud OVH Cloud

passage de parametres

6 réponses
Avatar
yves
Bonjour
Je me pose quelques questions sur l 'allocation memoire :
A priori il n 'est pas possible créer un pointeur (non initialisé) et de
l'utiliser comme paramètre d'une fonction qui elle, l 'alloue.
Le retour est NULL.
Vu que le pointeur est une variable globale je ne vois pas bien pourquoi
cela ne fonctionne pas?
On est donc obligé de passer une valeur, un int et c' est OK

typedef struct
{
int a;
int b;...
}t_essai;
///////////////////////CAS KO//////////////////////////////
t_essai *essai; => pointeur non initialisé,variable globale
main()
{
//envoi d'un pointeur NULL
fnc_essai(essai);
//au retour de la fonction essai est mauvais(toujours NULL)
}

void fnc_essai(t_essai *essai)
{
essai = malloc(......); //ici essai est bon
//inits valeurs
essai->x =....
........
}

///////////////////////CAS OK//////////////////////////////
//le code suivant fonctionne (passage d' un int par valeur pour mémoriser
l'adresse de la structure)

int adresse;
main()
{
fnc_essai(&adresse);
//cast
essai = (t_essai*)adresse;
essai->a = .....
}

void fnc_essai(int *adr)
{
t_essai *essai;
essai = malloc(......);
*adr = (int)essai;

//inits valeurs
essai->x =....
........
}
///////////////////////CAS KO//////////////////////////////
Si l'on passe une variable de type t_essai(pas un pointeur) pour forcer
l'adresse de l'appelant sur l'allocation c' est HS.
t_essai essai;
main()
{
fnc_essai(&essai);
}
void fnc_essai(t_essai adr)
{
t_essai *essai;
essai = malloc(......);
*adr = essai; => plante a la compil (types incompatibles)normal
adr = essai => ne retourne pas l' adresse en sortie de la fonction

//inits valeurs
essai->x =....
........
}

//enfin j' ai un cas qui fonctionne. tout cela car j' ai besoin
//d 'allouer de la memoire dans une DLL, de retourner au main
// mais la memoire est elle bien desallouee si:
Il semble que oui mais comment s'en assurer
////////////////////////////MAIN///////////////////////////

fnc_essai(&adresse);
//utlilisation de adresse
desalloue();


////////////////////////////////////DLL////////////////////////
t_essai *essai; //global dans la DLL

void fnc_essai(int *adr)
{
essai = malloc(......);
*adr = (int)essai;
//inits valeurs
essai->x =....
........
}

desalloue()
{
free(essai)
}


Merçi d'avance pour vos conseils.

6 réponses

Avatar
Yves ROMAN

Bonjour
Je me pose quelques questions sur l 'allocation memoire :
A priori il n 'est pas possible créer un pointeur (non initialisé) et de
l'utiliser comme paramètre d'une fonction qui elle, l 'alloue.
Le retour est NULL.


Il faut le passer par adresse pour pouvoir remonter sa valeur

Vu que le pointeur est une variable globale je ne vois pas bien pourquoi
cela ne fonctionne pas?
On est donc obligé de passer une valeur, un int et c' est OK

typedef struct
{
int a;
int b;...
}t_essai;
///////////////////////CAS KO//////////////////////////////
t_essai *essai; => pointeur non initialisé,variable globale
main()
{
//envoi d'un pointeur NULL
fnc_essai(essai);
//au retour de la fonction essai est mauvais(toujours NULL)
}

void fnc_essai(t_essai *essai)
{
essai = malloc(......); //ici essai est bon


Mais c'est le parametre qui est modifie, pas la variable globale
Les 2 portent le meme nom, c'est la variable/parametre local qui prend le
dessus.

//inits valeurs
essai->x =....
........


Et au retour, le pointeur passé par l'appelant reste inchangé car passé par
valeur.

}

[...]


En fait tu as fait un melange de 2 façons :
1) par variable globale
2) par adresse de pointeur
Dans les cas qui marchent tu passent l'adresse d'une variable qui va contenir le
pointeur.

Les bonnes facons dans chaque cas sont :

1) :
t_essai *essai = NULL ;

// La variable est globale : pas besoin de parametre
void fnc_essai(void)
{
essai = malloc(......);
//inits valeurs
essai->x =....
}

main()
{
fnc_essai();
free(essai) ;
}

2) :

// Tu veux modifier le pointeur : il faut passer son adresse
void fnc_essai(t_essai ** essai)
{
*essai = malloc(......);
//inits valeurs
(*essai)->x =....
}

main()
{
t_essai * essai
fnc_essai(&essai);
free(essai) ;
}

Avatar
Emmanuel Delahaye
In 'fr.comp.lang.c', "yves" wrote:

Je me pose quelques questions sur l 'allocation memoire :
A priori il n 'est pas possible créer un pointeur (non initialisé) et de
l'utiliser comme paramètre d'une fonction qui elle, l 'alloue.


Ta terminologie est floue...

"A priori il n'est pas possible de définir un pointeur (non initialisé) et de
l'utiliser comme paramètre d'une fonction qui elle, l'initialise."


Le retour est NULL.
Vu que le pointeur est une variable globale je ne vois pas bien pourquoi
cela ne fonctionne pas?


Que le pointeur soit global ou non ne change rien à l'affaire. Le passage de
paramètre s'effectue par valeur, donc modifier cette valeur n'a pas
d'incidence sur la valeur originale.

Le fait que le pointeur soit global (AMA, mauvaise conception) permettrait de
l'initialiser directement sans paramètre, mais bon, ici, on fait du C, pas du
BASIC...

On est donc obligé de passer une valeur, un int et c' est OK


Là, j'ai rien compris. Qu'est-ce qui est OK? La suite sera peut être plus
claire...

typedef struct
{
int a;
int b;...
}t_essai;
///////////////////////CAS KO//////////////////////////////
t_essai *essai; => pointeur non initialisé,variable globale


En fait, étant global, il vaut NULL par défaut.

main()
{
//envoi d'un pointeur NULL
fnc_essai(essai);
//au retour de la fonction essai est mauvais(toujours NULL)


Ok. Tu as démontré le passage par valeur, rien à dire.

}

void fnc_essai(t_essai *essai)
{
essai = malloc(......); //ici essai est bon


Certes, mais comme je le dis souvent, modifier un paramètre est souvent le
signe d'une mauvaise conception. La preuve! La valeur originelle est
inchangée.

//inits valeurs
essai->x =....
........
}

///////////////////////CAS OK//////////////////////////////
//le code suivant fonctionne (passage d' un int par valeur pour mémoriser
l'adresse de la structure)

int adresse;
main()
{
fnc_essai(&adresse);
//cast
essai = (t_essai*)adresse;


Ah, je vois! Tu as compris ce qu'il fallait faire, mais tu le fais mal.
L'utilisation d'un cast est souvent (aussi) le signe d'une mauvaise
conception.

essai->a = .....
}

void fnc_essai(int *adr)
{
t_essai *essai;
essai = malloc(......);
*adr = (int)essai;

//inits valeurs
essai->x =....
........
}


Ok. La bonne solution est de réfléchir à ton besoin.

Tu voudrais définir un pointeur d'un certain type, disons dans main() (tu ne
me feras pas définir de globale autrement que sous la torture).

int main (void)
{
t_essai *p;
}

Puis appeler une fonction qui initialiserait ce pointeur via un paramètre (il
y a d'autres solutions)

int main (void)
{
t_essai *p;

init(<???>p);
}

Le rôle de la fonction est de donner une valeur à une variable externe. La
seule solution connuee est de passer son adresse via un pointeur du même
type.

Quelle est le type de 'p' ? 't_essai *'
Quelle est l'adresse de 'p' ? '&p'.
Quelle est le type de l'adresse de 'p' ? Un pointeur de type 't_essai *',
'soit t_essai **'.

Le paramètre capable de recevoir l'adresse de p est donc de ce type.

static void init (t_essai **pp)
{
}

int main (void)
{
t_essai *p;

init(&p);
}

Je te laisse continuer.

//enfin j' ai un cas qui fonctionne. tout cela car j' ai besoin
//d 'allouer de la memoire dans une DLL, de retourner au main
// mais la memoire est elle bien desallouee si:
Il semble que oui mais comment s'en assurer
////////////////////////////MAIN///////////////////////////

fnc_essai(&adresse);
//utlilisation de adresse
desalloue();


////////////////////////////////////DLL////////////////////////
t_essai *essai; //global dans la DLL

void fnc_essai(int *adr)
{
essai = malloc(......);
*adr = (int)essai;
//inits valeurs
essai->x =....
........
}

desalloue()
{
free(essai)
}


C'est de la programmation à la barbare... Ces globales sont hideuses...

Je ferais comme ça :

Dans la bibiothèque :

/* xxx.c */
#include "xxx.h"

xxx_s *xxx_create(void)
{
xxx_s * this = malloc (sizeof *p);
if (this)
{
/* Par defaut, tout a 0 */
static const xxx_s z = {0};
*this = z;
}
return this;
}

void xxx_create(xxx_s *this)
{
free (this);
}

Commun :

/* xxx.h */
#ifndef H_XXX
#define H_XXX

typedef struct
{
int i;
char s[123];
}
xxx_s;

xxx_s *xxx_create(void);
void xxx_create(xxx_s *this);

#endif /* guard */


Application :

/* main.c */
#include "xxx.h"
int main (void)
{
xxx_s *p = xxx_create();

if (p != NULL)
{
/* utilisation,
...
puis quand c'est fini :
*/

xxx_delete (p);
p = NULL;
}
return 0;
}

--
-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
yves
Merçi beaucoup, je voyais que je mélangeais
donnée et adresse, avec un int c'était facile mais...,
je n'aurais jamais pensé au pointeur double
honte à moi.
Avatar
fluminis
Bonjour,

Je n'ai pas de compilateur c sous la main pour tester ce que je vais
te dire mais je pense que ca marche.

Tu veux modifier un pointeur dans ta fonction:
si tu as declaré un type

typedef struct t_essai * ptr_essai;

tu es d'accord avec moi qu'il faut faire :

void fnc_essai(ptr_essai * variable);

l'etoile voulant dire que tu veux modifier la variable
donc si tu n'utilises pas de type predefini tu devras faire :

void fnc_essai(t_essai ** essai);

=> deux etoiles

Voila !

en esperant que ca marche!
Avatar
Erwan David
Emmanuel Delahaye écrivait :

Que le pointeur soit global ou non ne change rien à l'affaire. Le passage de
paramètre s'effectue par valeur, donc modifier cette valeur n'a pas
d'incidence sur la valeur originale.

Le fait que le pointeur soit global (AMA, mauvaise conception)
permettrait de l'initialiser directement sans paramètre, mais bon,
ici, on fait du C, pas du BASIC...


Par contre on peut passer un pointeur sur le pointeur.

--
Monde de merde

Avatar
Bertrand Mollinier Toublet
Erwan David wrote:
Emmanuel Delahaye écrivait :


Que le pointeur soit global ou non ne change rien à l'affaire. Le passage de
paramètre s'effectue par valeur, donc modifier cette valeur n'a pas
d'incidence sur la valeur originale.

Le fait que le pointeur soit global (AMA, mauvaise conception)
permettrait de l'initialiser directement sans paramètre, mais bon,
ici, on fait du C, pas du BASIC...



Par contre on peut passer un pointeur sur le pointeur.

Oui.


Sauf que:

1) c'est pas beau (mais ce n'est que mon avis :-D)
2) c'est porteur de plus de problemes que ca n'en vaut la peine: que
faire si le pointeur (vers le pointeur) n'est pas correctement
initialise, etc.
3) il y a une alternative simple et elegante: retourner le nouveau
pointeur. Si tu objectes que la fonction prefererais retourner un code
d'erreur, je voudrais faire remarquer que dans le cas d'une allocation,
il n'y a pas a se prendre la tete: ou bien l'allocation a reussi et tu
retournes le pointeur initialise, ou bien l'allocation a foire, et tu
retournes NULL. Quant a savoir pourquoi elle a foire, que celui qui a
deja essaye de couvrir exhaustivement les raisons de l'echec de la
fonction appelee dans la fonction appelante me jette la premiere pierre!

--
Bertrand Mollinier Toublet
"Uno no se muere cuando debe, sino cuando puede"
-- Cor. Aureliano Buendia