Votre question

Aide liste chainé langage C

Tags :
  • Programmation
Dernière réponse : dans Programmation
12 Octobre 2012 19:35:07

Bonjour a tous,

j'ai un tp sur les listes chainée mais je n'arrive pas a m'en sortir au niveau de l'affichage.
Voici mon code:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "main.h"
  4.  
  5. void traiterPremierChoix(int premierChoix);
  6. void traiterDeuxiemeChoix(int deuxiemeChoix);
  7. ficheEtudiant* creerFicheEtudiant(ficheEtudiant *tete);
  8. void afficherListe(ficheEtudiant *tete);
  9.  
  10. int main(void)
  11. {
  12.  
  13. int premierChoix=0;
  14. do
  15. {
  16. printf("1 : Creation d'une liste chainee \n");
  17. printf("2 : Tri de la liste\n");
  18. printf("3 : Affichage de la liste\n");
  19. printf("4 : Recherche de la note d'un etudiant\n");
  20. printf("5 : Ajout d'un nouvel element\n");
  21. printf("6 : Copie de la liste dans un fichier\n");
  22. printf("7 : Lecture du fichier\n");
  23. printf("8 : Quitter\n");
  24. printf("\t Faites votre choix: ");
  25. scanf("%d",&premierChoix);
  26. printf("\n");
  27. }while(premierChoix<1 && premierChoix<9);
  28. traiterPremierChoix(premierChoix);// on passe le choix de l'utilisateur et
  29.  
  30. return 0;
  31. }
  32.  
  33.  
  34. void traiterPremierChoix(int premierChoix)
  35. {
  36. int deuxiemeChoix=0;
  37. switch(premierChoix)
  38. {
  39. case 1:
  40. do
  41. {
  42. printf("1 : Par une insertion en tete de liste\n");
  43. printf("2 : Par un ajout en queue de liste\n");
  44. printf("\t Faites votre choix: ");
  45. scanf("%d",&deuxiemeChoix);
  46. }while(deuxiemeChoix<1 && deuxiemeChoix>2);
  47. traiterDeuxiemeChoix(deuxiemeChoix);
  48.  
  49.  
  50. }
  51. }
  52.  
  53. void traiterDeuxiemeChoix(int deuxiemeChoix)
  54. {
  55. ficheEtudiant *tete=NULL;
  56. switch(deuxiemeChoix)
  57. {
  58. case 1:
  59. {
  60.  
  61.  
  62. tete=creerFicheEtudiant(tete);
  63. printf("la valeur de tete est : tete=%d",tete);
  64. afficherListe(tete);
  65. }
  66.  
  67. }
  68. }
  69.  
  70. ficheEtudiant* creerFicheEtudiant(ficheEtudiant *tete)
  71. {
  72.  
  73. char reponse='o';
  74.  
  75.  
  76. do{
  77. ficheEtudiant* etudiant;
  78. etudiant = (ficheEtudiant *)malloc(sizeof(ficheEtudiant));
  79. printf("Entrez le nom de l'etudiant: ");
  80. scanf("%s",etudiant->nom);
  81. printf("Entrez le prenom de l'etudiant: ");
  82. scanf("%s",etudiant->prenom);
  83. printf("Entrez la note de l'etudiant: ");
  84. scanf("%lf",&etudiant->note);
  85. etudiant->suivant=tete;
  86. tete=etudiant;
  87.  
  88. printf("Voulez vous continuer?(o/n): ");
  89. reponse=getche();
  90. printf("\n");
  91. }while(reponse!='n');
  92. printf("la valeur de tete a la sorti est : tete=%d",tete);
  93. }
  94.  
  95. void afficherListe(ficheEtudiant *tete)
  96. {
  97. ficheEtudiant *etudiant=tete;
  98. while(etudiant->suivant!=NULL);
  99. {
  100.  
  101. printf("%s %s %lf \n",etudiant->nom, etudiant->prenom, etudiant->note);
  102.  
  103. etudiant=etudiant->suivant;
  104.  
  105. }
  106. }



ma structure est défini dans main.h

  1. typedef struct ficheEtudiant ficheEtudiant;
  2. struct ficheEtudiant
  3. {
  4. char nom[20];
  5. char prenom[20];
  6. double note;
  7. ficheEtudiant *suivant; // contiendra l'adresse de la fiche suivante
  8. };


Mon probleme est le suivant: Lorsque je test 'tete' juste avant de sortir de la fonction creerFicheEtudiant j'ai bien une adresse, mais quand je le teste une fois sorti 'tete' est NULL.
Je n'arrive pas a trouver pourquoi.

Autres pages sur : aide liste chaine langage

a b L Programmation
12 Octobre 2012 23:26:20

Je ne lis pas ton code car il n'est pas indenté (mais merci Omar pour les tags ;)  ), mais en regardant le prototype de te fonction, j'ai vu ton problème (le passage en paramètre se fait toujours par une copie directe ou une copie de pointeur), alors je t'explique comment ton programme fonctionne une fois compilé.

Lorsque ton programme fonctionne, tu as 2 type de mémoires:
- le tas : c'est une zone mémoire qui contient tout ce qui est créé avec malloc + variables globales
- la pile : c'est tout ce qui peut s'effacer à la sortie d'un bloc (variables locales d'une fonction, paramètres de fonctions)

Une fois compilé, à l'exécution:
Lorsque tu appelles ta fonction, le programme copie les paramètres à envoyer à la fonction, sur la pile.
Dans ta fonction, le programme accède au paramètre en accédant directement à l'adresse mémoire où se site la copie sur la pile.
A la fin de la fonction, le programme dépile tout le contexte (toutes les variables locales + paramètres).

L'avantage du tas est que ça reste en mémoire (pas de mécanisme d'empilement).
Donc dans ta fonction, pour modifier des données extérieures, tu dois:
- soit modifier des variables allouées dans le tas
- soit modifier des variables mis sur la pile par des fonctions appelantes : tu n'y as pas directement accès car ce n'est pas le même contexte, mais tu peux y accéder car c'est encore dans la pile.
Dans les 2 cas, il faut faire passer un pointeur (qui n'est qu'un entier de 4 octets indiquant une adresse mémoire).

Jusque là, je pense que tu as bien compris car c'est ce que tu fais (je peux éclaircir si besoin).

Pour ton problème (bon je suis obligé de regarder un peu le code non indenté pour être clair :(  ):
Avant de faire appel à la fonction, tu as une variable "tete" qui pointe sur une structure "*tete" en mémoire. Disons par exemple que ta structure se trouve à l'adresse 0x40004000.
"tete" est un pointeur mais c'est aussi une variable locale déclarée en début de fonction. Donc, c'est sur la pile. Disons par exemple, que la variable "tete" se trouve à l'adresse 0x60006000.
Donc, à l'adresse 0x60006000, tu as un entier de 4 octets dont la valeur est 0x40004000, et à l'adresse 0x40004000, tu as une cinquantaine d'octet contenant 20 octets pour le nom, 20 pour le prénom, 8 octets pour le double et 4 octets pour un entier contenant une adresse mémoire (puisque c'est un pointeur).

A l'appel de ta fonction, tu passes "tete" en paramètre de ta fonction. Donc, le programme compilé va copier le paramètre sur la pile. Dans mon exemple, le programme compilé va récupérer la valeur à l'adresse 0x60006000, c'est-à-dire 0x40004000 et le copie sur la pile, par exemple à l'adresse 0x60006100.
Donc, sur la pile, à l'adresse 0x60006100, le programme a mis 0x40004000.
Maintenant le programme exécute la fonction.
Dans ta fonction, le paramètre "tete" est maintenant un pointeur qui pointe sur ta structure (en 0x40004000), mais cette variable se situe à l'adresse 0x60006100.

Donc:
- lorsque tu modifies "*tete" (ou "tete->quelquechose"), tu modifies les données qui se trouvent à l'adresse indiquée par la variable tete, soit 0x40004000
- lorsque tu modifies "tete" (c'est-à-dire que tu modifies l'adresse mémoire de la variable), tu modifies une adresse mémoire stockée à l'adresse 0x60006100.
Par exemple, si dans ta fonction, tu crées une structure sur le tas à l'adresse 0x40004100, et que tu l'assigne à "tete", alors à l'adresse 0x60006100, tu auras la valeur 0x40004100.

Enfin, lorsque l'exécution de la fonction se termine, le programme dépile toute la partie de la pile qui a été créé pour la fonction. Donc, ta variable située à l'adresse 0x60006100 est détruite.
Une fois sorti de la fonction, la variable paramètre de fonction "tete" (0x60006100) n'existe plus, et tu retrouves la variable "tete" initiale (0x60006000), qui elle, n'a jamais été modifiée et contient logiquement toujours la valeur 0x40004000.

Au final, tout paramètre est passé par copie. Pour éviter, le problème de la copie automatique, l'astuce est d'utiliser un pointeur qui lui, est copié, mais pas les données sur lequel il pointe. Ceci permet d'aller modifier directement les données dans la bonne zone mémoire.
Dans ton programme, tu veux aussi modifier la valeur de l'adresse mémoire indiqué dans le pointeur. Donc, tu dois appliquer la même astuce : copier un pointeur sur la pile pour éviter la copie de donnée (ta donnée étant l'adresse mémoire).
Tout ça pour dire que tu dois gérer un pointeur sur ton pointeur : (ficheEtudiant *) * pTete, soit ficheEtudiant ** pTete.
En paramètre tu indique l'adresse de ta variable "tete" en envoyant "&tete", comme ça, ce n'est plus 0x40004000 qui est empilé, mais 0x60006000 qui est copié sur la pile. En paramètre tu auras une variable pTete (par exemple situé à l'adresse 0x60006200) qui contiendra l'adresse 0x60006000, et ton tu pourras modifier l'adresse mémoire du pointeur "tete" qui est en dehors de la fonction.

ps: je ne me relis pas, j'ai fais un trop gros pâté. :) 
28 Octobre 2012 21:25:54

Tu as oublié de retourner ton pointeur sur ficheEtudiant dans ta fonction creerFicheEtudiant non ? :) 
Du coup il n'affecte pas de valeur à tête en sortant de ta fonction.

N'oublies pas de désallouer la mémoire a la fin de ton programme pour chaque malloc. Et n'hésites pas à faire des fonctions séparées pour la création, l'affectation, l'affichage et la destruction.
La quantité de code sera plus importante mais ça facilitera la lecture, la modification pour toi et surtout pour une personne extérieure :) .
(J'dis ça parce que parfois j'aide des potes qui ont 3 grosses fonction de 300 lignes de codes chacune, bah ça donne pas envie d'aider XD... Puis quand vous bosserez à plusieurs ce sera vachement plus sympa de savoir quoi fait quoi juste en lisant les prototypes des fonctions).
Tom's guide dans le monde
  • Allemagne
  • Italie
  • Irlande
  • Royaume Uni
  • Etats Unis
Suivre Tom's Guide
Inscrivez-vous à la Newsletter
  • ajouter à twitter
  • ajouter à facebook
  • ajouter un flux RSS