Votre question

C : pointeurs et structures [les étudiants et la guerre des ********]

Tags :
  • Programme
  • Programmation
Dernière réponse : dans Programmation
7 Mai 2009 22:51:41

Bonsoir, J'ai un problème lors de la compilation de mon sous programme (ci-dessous) et je n'ai rien trouvé sur le web pour m'aider... si vous pouvez y jeter un coup d'oeil vous m'aiderez bcp pour la suite de mon TP :) 

déclaration des structures :
  1. // definition structure : Cellule
  2. struct cellule
  3. {
  4. int info;
  5. struct cellule *lien;
  6. };
  7. typedef struct cellule TCellule;
  8. <a href="http://www.infos-du-net.com/forum/270963-21-pointeurs-structures-etudiants-guerre#t139684" target="_blank">http://www.infos-du-net.com/forum/270963-21-pointeurs-s...</a>
  9. // definition structure : File
  10. struct File
  11. {
  12. TCellule *premier ;
  13. TCellule *dernier ;
  14. };
  15. typedef struct File TFile;


sous-programme :
  1. TFile creerFile ()
  2. {
  3. TFile F;
  4. *(F.premier) = NULL;
  5. *(F.dernier) = NULL;
  6. return F;
  7. }


qu'est ce qui cloche ? :??: 

Autres pages sur : pointeurs structures etudiants guerre

8 Mai 2009 11:08:39

personne ne peut me débloquer svp ? :) 
a b L Programmation
8 Mai 2009 15:07:53

Quel est ton erreur de compilation ?
quand tu fais *(quelque chose), tu accède au contenu pointé par le pointeur (quelque chose).
Toi, tu veux assigner un pointeur NULL, donc, met directement F.premier = NULL

Une bonne méthode de programmation, c'est que lorsqu'on utilise *(quelque chose), on s'assure toujours que le pointeur est valide:
  1. if (!(quelque chose))
  2. return error;
  3.  
  4. faire quelque chose avec *(quelquechose)


Un pointeur n'est qu'une adresse mémoire codée sur 4 octets. Le pointeur NULL est l'adresse mémoire 0x00000000. Quand on fait (*pointeur), on va ailleurs en mémoire à l'adresse indiqué dans pointeur.
Contenus similaires
Pas de réponse à votre question ? Demandez !
8 Mai 2009 16:49:42

Merci cricky !!

l'erreur de compilation c'était : incompatible types in assignment, mais grace a ton bout de cours j'y voit plus clair :) 

sans vouloir abuser de ton savoir j'ai une deuxième question :) 

pour accéder à l' 'info' de la cellule pointée par *premier, comment dois-je mi prendre ?...

moi j'ai fais ca (je sais que c'est faux malheureusement) :
  1. int premier (TFile pF)
  2. {
  3. return (*(pF.premier).info);
  4. }

a b L Programmation
8 Mai 2009 21:36:58

L'opérateur "point" est prioritaire sur l'opérateur d'indirection "étoile".
Dans dans ce que tu as écrit:
*(pF.premier).info est équivalent à *((pF.premier).info)
Et du coup, c'est pas bon car tu fais .info sur le pointeur pF.premier.
Donc ce que tu dois mettre, c'est:
(*(pF.premier)).info
Faut pas hésiter à mettre des parenthèses partout. ;) 
Après, tu as une autre possibilté, car avec ".", tu accèdes à un membre d'une structure, et bien tu peux faire presque la même chose avec un pointeur. l'opérateur "->" permet d'accéder à un membre d'un pointeur de structure.
Donc, (*(pF.premier)).info est équivalent à (pF.premier)->info, c'est plus facile à lire.
mais bon la règle que je disais pour le * vaut aussi pour le ->. Dès qu'on utilise * ou ->, on s'assure que le pointeur n'est pas NULL, pour éviter le crash si le pointeur est NULL. :) 
Donc, au final, je modifierais le corps de ta fonction:
  1. int premier(TFile pF)
  2. {
  3. if (!pF.premier)
  4. return INFO_ERREUR; // une valeur permettant d'identifier l'erreur
  5.  
  6. return pF.premier->info;
  7. }


Voilà, une dernière remarque concernant les paramètres de fonctions. Une fois ton programme compilé, tout ce qui est passé en paramètre est copié sur la pile (chaque processus possède sa propre pile, mais tu ne la vois pas quand tu programme en C).
Bref, tout ça pour dire que dans ta fonction premier, pF n'est qu'une copie de ce que tu lui a donné à l'appel.
Je prends l'exemple d'une variable de type TFile2 nommé monFile.
Je complexifie pour bien expliquer. Je définis ma struture TFile2 comme ceci:
  1. typedef struct
  2. {
  3. int infoFile;
  4. celulle * premier;
  5.  
  6. } TFile2;

monFile est, par exemple, situé à l'adresse 0x00112233. Donc, en mémoire, à l'adresse 0x00112233, tu as 8 octets dont les 4 premiers est le champ infoFile (qui contient par exemple 0xFFEEDDCC), et les 4 suivants sont le pointeur lien, donc une adresse mémoire, par exemple 0x00114455. Donc, en mémoire à l'adresse 0x00114455, tu as le contenu d'une structure cellule qui contient elle même un pointeur, etc.
Donc, à l'adresse 0x00112233, tu as: [0xFFEEDDCC, 0x00114455].
Le paramètre de ta fonction premier2(TFile2 pF) est TFile2 pF, donc lorsque tu appelles premier2(monFile); la structure monFile (situé à l'adresse 0x00112233) est copiée sur la pile, par exemple à l'adresse 0x00222200. La copie est une copie binaire (un PC c'est très bête). Donc, dans la fonction premier2(), pF est une nouvelle structure situé à l'adresse 0x00222200 et contenant [0xFFEEDDCC, 0x00114455]. Tu remarqueras que le champ infoFile est bien copié, le pointeur aussi mais sa valeur est toujours 0x00114455 ! Donc ta structure est copiée, mais pas les objets qui sont pointés: monFile.premier et pF.premier pointent sur la même structure en mémoire !
Les conséquences:
- si dans la fonction premier2(), tu fais pF.infoFile = 0;, tu modifies la variable copiée sur la pile et pas la variable d'origine monFile qui gardera donc sa valeur d'origine à la sortie de la fonction.
- si dans la fonction premier2(), tu fais pF.premier->info = 0;, tu modifies le membre pointé par la variable copié sur la pile, mais comme le pointeur pointe sur la même structure en mémoire, en modifiant pF.premier->info, tu modifies aussi monFile.premier->info.
Comme ta structure TFile, ne contient que des pointeurs, si tu modifies les structures pointées de pF, tu vas modifier le contenu de ta variable passée à premier(), mais attention, car si tu modifies la valeur du pointeur (si tu veux que premier pointe sur un autre élément par exemple pour la création ou suppression d'une cellule en tête de liste), alors ça ne marchera pas puisque tu modifierais l'adresse dans la structure copiée sur la pile. Dans mon exemple, si je mets pf.premier = 0x00118899 (l'adresse d'un nouvel élément créé avec malloc), je vais avoir à l'adresse 0x00222200: [0xFFEEDDCC, 0x00118899], mais à l'adresse 0x00112233, j'aurais toujours [0xFFEEDDCC, 0x00114455].
Donc, attention si tu fais des fonction d'ajout et de suppression d'éléments en début ou fin de liste. ;) 

La solution pour pouvoir tout modifier dans ta structure, c'est de passer un pointeur en paramètre. Par exemple, premier3(TFile2 * pF).
Dans ce cas, j'ai toujours monFile situé à l'adresse 0x00112233, mais cette fois-ci je ne copie pas la structure mais le pointeur. Donc, sur la pile, il va y avoir à l'adresse 0x00222200: [0x00112233]. Le pointeur est une copie, mais en utilisant *pF, on va accéder à la même structure que monFile puisqu'il est situé à l'adresse 0x00112233. Donc pour les ajout et supression en fin et début de liste, pense bien à utiliser des pointeur pour modifier toute la structure.

Désolé pour cette longue explication, mais je pense qu'il faut bien comprendre comment le programme compilé fonctionne pour éviter de se prendre la tête après sur un programme qui ne fait que la moitié des choses. :) 

Une dernière astuce pour ne pas se mélanger à savoir si c'est un pointeur ou pas, je mets toujours un p dans le nom de la variable pour indiquer que c'est un pointeur : premier => pPremier, comme ça au simple coup d'œil, on sait qu'il faudra utiliser * ou -> (et faire un test de pointeur NULL).
9 Mai 2009 00:12:48

Waoww waoww waoow .... génial super extra :D 
alors la un gros merci !! c'est vraiment super sympa d'avoir écris tout ca !!!

Citation :

Désolé pour cette longue explication, mais je pense qu'il faut bien comprendre comment le programme compilé fonctionne pour éviter de se prendre la tête après sur un programme qui ne fait que la moitié des choses. :) 


Au contraire je te remercie! cette discussion va rester longtemps en 'Favoris' le temps que je maitrise tout ca!! (elle est plus complète que mes cours ;) )

bon par contre ca ma pris du temps à lire hein ^^ je l'avoue!

donc pour l'appel je fais :
  1. // appel fonction
  2. fonction (struct);
  3.  
  4. // fonction
  5. fonction (Tstruct *struct)
  6. {
  7. stuct.pPointeur->donnee; // ca me plait l'astuce du 'p' :)
  8. }


Donc en fait on envoie une adresse à la fonction, puis ensuite on pointe sur cette adresse lors du déroulement de la fonction c'est ca ?

pour un int, float il aurait fallu mettre '&variable' (dans l'appel de la fonction)
pour un tableau de caractère ou une structure c'est déjà une adresse, donc on a pa besoin du '&

un truc du genre non..........???........me tapez pas professeur si j'ai faux....
parce que ca je maitrise pas très bien en fait...
a b L Programmation
9 Mai 2009 12:02:42

Déjà, n'appelle pas ta variable struct parce que c'est un mot-clé et ça va pas plaire au compilateur. Dans ta fonction, comme maintenant ton paramètre est un pointeur et pas directement une structure, tu dois utiliser struct->pPointer->donnee;
Et pour éviter les bugs de crash, comme il y a 2 ->, on teste avant si struct n'est pas à NULL et si struct->pPointer n'est pas à NULL non plus.

Citation :
Donc en fait on envoie une adresse à la fonction, puis ensuite on pointe sur cette adresse lors du déroulement de la fonction c'est ca ?

Oui, on manipule le pointeur pour modifier la structure envoyée.

Citation :
pour un int, float il aurait fallu mettre '&variable' (dans l'appel de la fonction)
pour un tableau de caractère ou une structure c'est déjà une adresse, donc on a pa besoin du '&

Oui, pour un tableau, effectivement, la variable tableau est en fait un pointeur sur le premier élément du tableau.
Le & permet d'obtenir l'adresse mémoire d'une variable (pour int, float et struct). Donc "&variable" est un pointeur de type int* (si variable est un int).
Donc, quand tu écriras ta fonction, il faudra mettre en paramètre un int *, et du coup tester dans la fonction si le pointeur envoyé n'est pas NULL.
Mais bon, tu ne fais ça que si tu veux modifier l'entier envoyé. Si tu ne fais que le lire (idem pour les structures), tu peux te contenter de lire une valeur copiée, alors pas besoin de passer par un pointeur dans ce cas (comme c'est le cas j'imagine pour ta fonction premier()).
9 Mai 2009 15:32:30

ok génial, je comprends mieux dorénavant ...

Citation :
Donc pour les ajout et supression en fin et début de liste, pense bien à utiliser des pointeur pour modifier toute la structure.


alors justement ^^ je dois faire une fonction enfiler, pour rajouter une cellule en tête de File, donc je te montre mon code (qui ne marche pas biensur, sinon c'est pas drole)
==> promis après je t'embête plus <==

rem : j'ai pas fait de test sur les pointeurs parce que j'y suis pas arrivé :( 

rappel des définitions de structures
  1. // definition structure : Cellule
  2. struct cellule
  3. {
  4. int info;
  5. struct cellule *lien;
  6. };
  7. typedef struct cellule TCellule;
  8.  
  9. // definition structure : File
  10. struct File
  11. {
  12. TCellule *premier ;
  13. TCellule *dernier ;
  14. };
  15. typedef struct File TFile;


l'appel de la fonction (dans le main)
  1. int i;
  2. printf("saisir l'entier a enfiler : ");
  3. scanf("%i",&ele);
  4. F = enfiler (&F,ele);


et la fonction
  1. TFile enfiler (TFile *pF,int pele)
  2. {
  3. TCellule C;
  4. C.info=pele;
  5. C.lien=NULL;
  6. if (fileVide(*pF)==0)
  7. {
  8. (*pF).premier=&C;
  9. (*pF).dernier=&C;
  10. }
  11. else
  12. {
  13. (*((*pF).dernier)).lien=&C;
  14. (*pF).dernier=&C;
  15. }
  16. return (*pF);
  17. }


ca passe à la compilation mais après à l'affichage ca passe pas...

j'ai d'ailleurs fais une boucle pour l'affichage :
  1. TCellule *affichage;
  2.  
  3. printf("\n\n----- FILE -----\n");
  4. if ( fileVide(F) != 0 )
  5. {
  6. affichage=F.premier;
  7. printf("==> %i\n",(*affichage).info);
  8. while ((*affichage).lien != NULL)
  9. {
  10. affichage=(*affichage).lien;
  11. printf("==> %i\n",(*affichage).info);
  12. }
  13. }
  14. printf("----------------\n");

a b L Programmation
9 Mai 2009 19:09:55

Déjà ici:
  1. TFile enfiler (TFile *pF,int pele)

Pour le retour de fonction, c'est pareil que les paramètre, ça fait une copie, mais ce n'est pas un problème ici.
Bon, ça sert à rien de retourner ton TFile, puisque tu le modifies en paramètre, un "void enfiler(TFile *pF,int ele)" suffit, et il suffit de l'appeler en faisant "enfiler(&F, ele);"

Bref, ce n'est pas ton problème.
Ton problème vient de là:
  1. TCellule C;

quand tu crées une variable locale, tu la crée sur la pile, et n'est valable que dans le contexte où tu la déclares, c'est à dire dans ta fonction (à la fin de la fonction, tout le contexte est dépilé). Donc, à la sortie de ta fonction, ton TCellule est détruit.
Pour que ça reste en mémoire dans tout le programme, il te faut la créer sur le tas avec malloc(). Du coup, il faudra pendre à la libérer avec un free() lorsque tu n'en as plus besoin (il te faudra faire une fonction qui supprime toute la liste pour libérer la mémoire proprement.
10 Mai 2009 00:39:06

Ok merci beaucoup, tu m'as déjà bien trop aidé :) 

Je vais faire tout ca promis :) 

Encore merci pour ton aide tu m'as fait avancer énormément @+++
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