[C] Allocation dynamique - Tableau et structu

Résolu/Fermé
Hylpoz - 13 mars 2008 à 23:38
Hylpoz Messages postés 1 Date d'inscription vendredi 14 mars 2008 Statut Membre Dernière intervention 14 mars 2008 - 14 mars 2008 à 13:59
Bonjour,
J'ai cherche sur le forum les réponses à mes questions, mais certaines restent sans réponses ou un peu floues... :(

Je vous explique mon problème:

Je dois faire un projet un C, dans lequel j'ai décidé d'utiliser une structure d'arbre pour représenter des mots/phrases.
Voici la structure de mes noeuds:


typedef struct t_Noeud t_Noeud;
struct t_Noeud{
    char C;                //Caractère du noeud
    unsigned int Nb_fils;
    t_Noeud* *Fils;        //Tableau dynamique de liens vers la suite de l'arbre.
};


J'appelle "fils" les liens vers la suite de l'arbre.

Voici un exemple d'arbre (fait sous paint :euh: ) pour que vous voyez ce que je veux modéliser:
http://img232.imageshack.us/img232/4151/exemplearbreah3.jpg

Donc en gros chaque noeud peut avoir un nombre variable de fils de 0 à 255.

Voici la fonction que j'utilise pour rajouter un fils et la fonction d'initialisation d'un noeud:


void Initialiser_Noeud(t_Noeud *i_Ptr_noeud, char i_Car)
{
      i_Ptr_noeud->C = i_Car;
      i_Ptr_noeud->Nb_fils = 0;
      i_Ptr_noeud->Fils = NULL;
}


void Ajouter_nouvelle_chaine(t_Noeud *i_Ptr_noeud, char i_Car)
{
      size_t Taille_alloc_fils = i_Ptr_noeud->Nb_fils + 1;
      size_t Taille_alloc_noeud = sizeof(t_Noeud);

      /*Agrandir tableau de "fils de 1 case
       *fonctionne comme malloc(Taille_alloc_fils) si i_Ptr_noeud->Fils == NULL
       */
      i_Ptr_noeud->Fils = realloc(i_Ptr_noeud->Fils,Taille_alloc_fils);
 
      //Ajouter un nouveau noeud en dernière position
      i_Ptr_noeud->Fils[i_Ptr_noeud->Nb_fils] = malloc(Taille_alloc_noeud);

      //Initialisation du nouveau noeud.
      Initialiser_Noeud(i_Ptr_noeud->Fils[i_Ptr_noeud->Nb_fils],i_Car);
   
      //Le nombre de fils est incrémenté.
      i_Ptr_noeud->Nb_fils++;
}


Ce code marche si mes souvenirs d'hier sont bons mais le fait est que j'ai du mal avec les passages de paramètres en langage C...
Alors voici la liste de question auxquelles j'espère vous pourrez répondre :)
Question 3:
Nos professeurs nous ont conseillé d'éviter d'utiliser la fonction realloc().
Donc j'aimerai à partir de malloc() et free() agrandir mon tableau de 1 case et le recopier, et le rendre dans le paramètre "t_Noeud *i_Ptr_noeud".

Que faut-il que je modifie dans la signature de ma fonction pour que cela soit possible?


Question 2:
Dans la fonction void Ajouter_nouvelle_chaine(t_Noeud *i_Ptr_noeud, char i_Car)
je modifie le noeud repéré par le pointeur que je passe en paramètre. Mais ce pointeur est censé être seulement une copie du vrai pointeur (celui de la fonction appelante).
Alors comment ce fait-il que mes modifications soient prises en compte?

Les modifications étant:
-Agrandissement du tableau.
-Ajout d'un nouveau noeud à la fin de ce même tableau
-Incrémentation du champ: Nb_fils

Je me pose la même question lorsque j'initialise mon noeud...

Question 3:
A la fin de mon algorithme, il faut que je libère mon arbre. Dans Mon cas, le code suivant suffit-il?



void main()
{

//Début du programme
   t_Noeud *Racine = malloc(sizeof(t_Noeud)), *P = NULL;
   Initialiser_Noeud(Racine,'@');  

//Corps du programme 
//...
//...

//Fin du programme
   free(Racine);
}



ou faut-il que je parcours mon arbre complètement et libérer 1 à 1 chaque noeud de l'arbre?
Si c'est le cas, la signature suivante convient-elle? (La fonction étant une fonction récursive) Car la le pointeur sera une recopie du pointeur de la fonction appelante...

void Liberer_noeud(t_Noeud *i_Ptr_Noeud);



Merci à ceux qui auront eu la patience et le courage de lire ce long post...
Si vous avez besoin de plus de précision ou explications n'hésitez pas.
A voir également:

4 réponses

size_t Taille_alloc_fils = (i_Ptr_noeud->Nb_fils + 1) * sizeof(t_Noeud*);
tu as raison, c'est moi qui m'étais trompé

Pour ce qui est d'affecter NULL aux pointeurs désalloués, ça n'est demandé ni par la syntaxe, ni par la logique. On entre dans le domaine des goûts et des couleurs. L'aspect pratique, c'est que tu est sûr que tu n'as plus rien à désallouer, et que tu peux tester la valeur d'un pointeur avant de t'en servir, ce qui peut être utile pour le debogage.
Dans ton cas précis, affecter NULL aux fils ne sert à rien, ils ne seront plus accessibles de toutes manières dès que tu auras affecté NULL au parent.
3
Bonjour

Remarque préliminaire :
size_t Taille_alloc_fils = i_Ptr_noeud->Nb_fils + 1;

ce ne serait pas plutôt :
size_t Taille_alloc_fils = (i_Ptr_noeud->Nb_fils + 1) * sizeof(unsigned int);


Question 1 :
Non, la signature de la fonction n'a pas à changer. Elle a toujours besoind des mêmes paramètres en entrée, ni plus ni moins et elle reste de type void.

Question 2 :
Tu passes une copie d'un pointeur. Mais ce que tu modifies dans ta fonction, ce n'est pas le pointeur, c'est la variable pointée. Or un pointeur et sa copie contiennent l'adresse de la même zone mémoire : toute modification de la zone mémoire pointée par l'un modifie la zone mémoire pointée par l'autre, puisque c'est la même !
Le problème aurait été tout différent si dans ta fonction tu faisais un i_Ptr_noeud=malloc(..) ou realloc(..)

Question 3 :
un Free à la racine de l'arbre ne suffit pas : la fonction free ne connaît rien de ta structure et est donc incapable de parcourir tous les noeuds. Tu es effectivement obligé de les libérer toi-même. Une fonction récursive, qui bien sûr libère les fils avant le noeud parent, est le meilleur moyen de le faire.
La signature que tu proposes me semble tout à fait convenir : là encore, tu ne cherches pas à récupérer une valeur modifiée du pointeur (tu en ferais quoi ?)
1
Bonjour le père, et merci pour tes réponses.

J'ai encore quelques questions pour toi suite à ton intervention...
Remarque préliminaire :

size_t Taille_alloc_fils = i_Ptr_noeud->Nb_fils + 1;


ce ne serait pas plutôt :

size_t Taille_alloc_fils = (i_Ptr_noeud->Nb_fils + 1) * sizeof(unsigned int);



Merci je vois que j'ai juste mis le nombre de case mémoire que je veux réserver mais pas leur taille...
Je fais un tableau de pointeur en gros, donc
size_t Taille_alloc_fils = (i_Ptr_noeud->Nb_fils + 1) * sizeof(unsigned int);

doit être

size_t Taille_alloc_fils = (i_Ptr_noeud->Nb_fils + 1) * sizeof(t_Noeud*);

non? Ou est-ce peut être la même chose?


Question 1 :
Non, la signature de la fonction n'a pas à changer. Elle a toujours besoin des mêmes paramètres en entrée, ni plus ni moins et elle reste de type void. 


Oui je comprends maintenant, j'ai confondu le pointeur que je passe en paramètre et mon tableau de pointeur sur la suite de l'arbre.
Donc si j'ai compris, puisque je modifie le noeud repéré les paramètres sont bons.

Question3:
[...]


En fait, je pensais que si je passais par recopie le pointeur, la libération n'aurait pas eu lieu...

Une dernière question concernant ma fonction de libération mémoire:
Après la libération d'un noeud, est-il conseillé d'affecter le pointeur à null?

Voila la fonction en question:

void Liberer_noeud(t_Noeud *i_Ptr_Noeud)
{
   unsigned short i;

   //libération des noeuds fils
   for ( i = 0 ; i < i_Ptr_Noeud->Nb_fils ; i++ )
   {
      Liberer_noeud(i_Ptr_Noeud->Fils[i]);
      i_Ptr_Noeud->Fils[i] = NULL;
   }
   free(i_Ptr_Noeud);
}
1
Hylpoz Messages postés 1 Date d'inscription vendredi 14 mars 2008 Statut Membre Dernière intervention 14 mars 2008
14 mars 2008 à 13:59
Ok c'est parfait tu as répondu à toutes mes questions :)

En tout cas, je te remercie pour ta patience et ton efficacité!
0