[Résolu] Pointeurs et fonctions

Pointeurs et fonctions [Résolu] - C - Programmation

Marsh Posté le 07-01-2007 à 12:06:22    

Bonjour,
 
voilà je suis tjs avec mes problèmes de pointeurs  :sarcastic:  et là j'en ai un que j'arrive pas à résoudre:
 
je dois faire une fonction qui lit les élements d'un tableau, avec comme paramètre un nombre maximum d'elements, si le nombre d'éléments saisis est > au nbre d'elements max autorisé la fonction doit me permettre de changer le nombre d'elements saisie (que j'utiliserais ensuite dans le main pour afficher les éléments)
 
le soucis c'est au niveau de l'allocation mémoire du tableau, quand je la fait en dynamique j'ai n'importe quoi qui sort dans l'affichage  :heink:  
 

Code :
  1. #include <stdio.h>
  2. #include <conio.h>
  3. #include <stdlib.h>
  4. void lire_tab (int *t, int *n, int nmax)
  5. {
  6. int i;
  7. while (nmax < *n)
  8. {
  9.  printf ("Dimension du tableau : " );
  10.  scanf ("%d", n);
  11. }
  12.     t = (int*)  malloc ((*n) * sizeof (int));
  13. for (i = 0; i < *n; i++)
  14. {
  15.  printf ("Element[%d] : ", i + 1);
  16.  scanf ("%d", &t[i]);
  17. }
  18. }
  19. int main (void)
  20. {
  21. int *t, n, i;
  22. printf ("Dimension du tableau : " );
  23. scanf ("%d", &n);
  24. lire_tab (t, &n, 10);
  25. printf ("\n\n" );
  26. for (i = 0; i < n; i++)
  27.  printf ("element[%d] : %d\n", i + 1, t[i]);
  28. getch ();
  29. return 0;
  30. }

Message cité 2 fois
Message édité par exhortae le 11-01-2007 à 18:40:50
Reply

Marsh Posté le 07-01-2007 à 12:06:22   

Reply

Marsh Posté le 07-01-2007 à 12:23:58    

exhortae a écrit :

voilà je suis tjs avec mes problèmes de pointeurs <...> le soucis c'est au niveau de l'allocation mémoire du tableau, quand je la fait en dynamique j'ai n'importe quoi qui sort dans l'affichage


C'est normal. Ceci :

Project   : Forums
Compiler  : GNU GCC Compiler (called directly)
Directory : C:\dev\forums2\
--------------------------------------------------------------------------------
Switching to target: default
Compiling: main.c
main.c: In function `main':
main.c:26: warning: 't' might be used uninitialized in this function
Linking console executable: console.exe
Process terminated with status 0 (0 minutes, 6 seconds)
0 errors, 1 warnings


Révèle un problème de conception :

 

En effet, tu passes une valeur non initialisée à une fonction. Je rappelle qu'en C, les passages de paramètres se font exclusivement par copie de la valeur, et que modifier la valeur d'un paramètre n'affecte pas la valeur initiale.

 

Si tu veux qu'une fonction modifie la valeur d'une variable, il y a 2 solutions :

 

- Passer l'adresse de la variable
- Retourner la valeur et la stocker dans la variable.

 

NOTA : Si ton compilateur de dit rien, c'est qu'il est probablement mal réglé :

 

http://mapage.noos.fr/emdel/codage.htm#cfg_compilo

Message cité 1 fois
Message édité par Emmanuel Delahaye le 07-01-2007 à 12:25:08

---------------
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 08-01-2007 à 18:53:57    

exhortae a écrit :

Code :
  1. void lire_tab (int *t, int *n, int nmax)
  2. {
  3.    ...
  4.     t = (int*)  malloc ((*n) * sizeof (int));
  5. }
  6. int main (void)
  7. {
  8. int *t, n, i;
  9. ...
  10. lire_tab (t, &n, 10);
  11. }



 
Emmanuel a bien résumé la chose, voire peut-être de façon trop sybilline pour un débutant...
En fait, tu passes à "lire_tab" une copie de "t", c'est à dire une copie de ton pointeur. Cette copie est modifiée dans la fonction "lire_tab" et récupère l'adresse allouée puis la copie est effacée une fois la fonction finie et quand tu reviens dans le "main", "t" n'a jamais été modifié (et ne contient certainement pas l'adresse allouée qui a été perdue)
2 solutions

  • tu passes à ta fonction "lire_tab" l'adresse de "t". Cette fonction recevant une adresse (de type "int **" puisque "t" est de type "int*" ) pourra aller modifier l'élément pointé ("*t=malloc(...)" ) => pour résumer, t'as parfaitement compris que comme "n" sera modifié dans "lire_tab" (lors du "scanf()" ) il faut lui passer l'adresse de "n" dans le "main"; donc tu dois appliquer le même raisonnement pour "t" => tu peux aussi te rappeler que dans "lire_tab()" tu peux nommer tes paramètres avec un nom différent des variables du "main()" et tu réaliseras qu'il n'y a aucun rapport avec le "t" du main() et le "t" de lire_tab()


  • tu fais renvoyer par "lire_tab" le pointeur de la zone allouée (renvoyé lui-même par malloc) et, dans ton main, tu écrits "t=lire_tab(...)" => dans ce cas plus la peine de passer "t" à "lire_tab" car il sera modifié par l'affectation de retour

Message cité 1 fois
Message édité par Sve@r le 08-01-2007 à 19:33:55

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

Marsh Posté le 08-01-2007 à 19:58:48    

Emmanuel Delahaye a écrit :


 
 Je rappelle qu'en C, les passages de paramètres se font exclusivement par copie de la valeur, et que modifier la valeur d'un paramètre n'affecte pas la valeur initiale.
 
Si tu veux qu'une fonction modifie la valeur d'une variable, il y a 2 solutions :
 
- Passer l'adresse de la variable
- Retourner la valeur et la stocker dans la variable.
 


 
Oui en faite le pointeur que je passais comme paramètre dans la fonction m'a induit en erreur, j'ai pas pensé qu'il fallait passer comme paramètre un pointeur sur un pointeur.
 

Sve@r a écrit :

Emmanuel a bien résumé la chose, voire peut-être de façon trop sybilline pour un débutant...
En fait, tu passes à "lire_tab" une copie de "t", c'est à dire une copie de ton pointeur. Cette copie est modifiée dans la fonction "lire_tab" et récupère l'adresse allouée puis la copie est effacée une fois la fonction finie et quand tu reviens dans le "main", "t" n'a jamais été modifié (et ne contient certainement pas l'adresse allouée qui a été perdue)
2 solutions

  • tu passes à ta fonction "lire_tab" l'adresse de "t". Cette fonction recevant une adresse (de type "int **" puisque "t" est de type "int*" ) pourra aller modifier l'élément pointé ("*t=malloc(...)" ) => pour résumer, t'as parfaitement compris que comme "n" sera modifié dans "lire_tab" (lors du "scanf()" ) il faut lui passer l'adresse de "n" dans le "main"; donc tu dois appliquer le même raisonnement pour "t" => tu peux aussi te rappeler que dans "lire_tab()" tu peux nommer tes paramètres avec un nom différent des variables du "main()" et tu réaliseras qu'il n'y a aucun rapport avec le "t" du main() et le "t" de lire_tab()


  • tu fais renvoyer par "lire_tab" le pointeur de la zone allouée (renvoyé lui-même par malloc) et, dans ton main, tu écrits "t=lire_tab(...)" => dans ce cas plus la peine de passer "t" à "lire_tab" car il sera modifié par l'affectation de retour


Merci grâce à ça j'ai pu comprendre mon erreur
 
voilà le programme modifié
 

Code :
  1. #include <stdio.h>
  2. #include <conio.h>
  3. #include <stdlib.h>
  4. void lire_tab (int **t1, int *n, int nmax)
  5. {
  6. int i;
  7. while (nmax < *n)
  8. {
  9.  printf ("Dimension du tableau : " );
  10.  scanf ("%d", n);
  11. }
  12.     *t1 = (int*)  malloc ((*n) * sizeof (int));
  13. for (i = 0; i < *n; i++)
  14. {
  15.  printf ("Element[%d] : ", i + 1);
  16.  scanf ("%d", *t1 + i);
  17. }
  18. }
  19. int main (void)
  20. {
  21. int *t, n, i;
  22. printf ("Dimension du tableau : " );
  23. scanf ("%d", &n);
  24. lire_tab (&t, &n, 10);
  25. printf ("\n\n" );
  26. for (i = 0; i < n; i++)
  27.  printf ("element[%d] : %d\n", i + 1, t[i]);
  28. getch ();
  29. return 0;
  30. }


 
 
si tu pouvais me dire si maintenant il est correct (chez moi il tourne mais je suis pas sur pour la partie du scanf), ça me permettra ensuite d'expliquer ce que j'ai compris pour voir si je ne me trompe pas.
 
Merci à vous deux
 
PS : je viens de voir que t'as editer ton post pour ajouter une précision, mais j'avais trouver avant (cherché pendant 5 bonnes minutes quand même :D)

Message cité 1 fois
Message édité par exhortae le 08-01-2007 à 20:03:58
Reply

Marsh Posté le 08-01-2007 à 21:09:50    

exhortae a écrit :

voilà le programme modifié
 

Code :
  1. #include <stdio.h>
  2. #include <conio.h>
  3. #include <stdlib.h>
  4. void lire_tab (int **t1, int *n, int nmax)
  5. {
  6. int i;
  7. while (nmax < *n)
  8. {
  9.  printf ("Dimension du tableau : " );
  10.  scanf ("%d", n);
  11. }
  12.     *t1 = (int*)  malloc ((*n) * sizeof (int));
  13. for (i = 0; i < *n; i++)
  14. {
  15.  printf ("Element[%d] : ", i + 1);
  16.  scanf ("%d", *t1 + i);
  17. }
  18. }
  19. int main (void)
  20. {
  21. int *t, n, i;
  22. printf ("Dimension du tableau : " );
  23. scanf ("%d", &n);
  24. lire_tab (&t, &n, 10);
  25. printf ("\n\n" );
  26. for (i = 0; i < n; i++)
  27.  printf ("element[%d] : %d\n", i + 1, t[i]);
  28. getch ();
  29. return 0;
  30. }


 
si tu pouvais me dire si maintenant il est correct (chez moi il tourne mais je suis pas sur pour la partie du scanf


D'un point de vue "C", tes scanf sont excellents. T'as parfaitement maîtrisé la relation d'égalité entre "&t[i]" et "t + i" qu'on retrouve aussi entre "t[i]" et "*(t + i)".
 
Cependant, d'un point de vue "humain", utiliser un "scanf" pour faire saisir qqchose à une personne c'est horrible. Car "scanf" sous-entend que l'entrée est formatée alors que ce que tape le type derrière son clavier est tout sauf formaté. Que se passe-t-il si le type tape "azerty" là où on lui demande un nombre ? 1) ton nombre est à 0 et 2) le buffer d'entrée reste chargé avec "azerty" => à la saisie suivante d'une chaîne, c'est "azerty" qui sera utilisé
 
La meilleure solution utilisable est de tout accepter via fgets() car au-moins ton buffer est toujours clean et ensuite de traiter ton entrée via sscanf().
 
Exemple: remplacer

int i;
scanf("%d", &i);


 
Par

int i;
char saisie[1024];
fgets(saisie, 1024, stdin);
sscanf(saisie, "%d", &i);


Pour plus de détail, aller voir http://forum.hardware.fr/hfr/Progr [...] 9666_1.htm
 
Ensuite, étant donné que ton tableau "t" est intrinsèquement lié à "n", tu peux regrouper les deux éléments dans une structure. Tu passes ensuite l'adresse de la structure à la fonction qui peut grâce, à cet unique paramètre, aller taper dans le tableau "t" ou le nombre "n" comme elle a envie.
1) ça t'économise un paramètre
2) ça t'évite de jongler avec les "*t + i" et "*n"
 
Enfin ne pas oublier de  
1) vérifier que le malloc a réussi sinon ce n'est pas la peine de continuer
2) libérer "t" en fin d'utilisation


Message édité par Sve@r le 08-01-2007 à 21:15:34

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

Marsh Posté le 08-01-2007 à 21:57:26    

Pour la saisie on m'a fait la remarque plusieurs fois, le soucis c'est que en cours on utilise les scanf, et le jour ou j'ai essayé d'utiliser les fgets, strtol ... je me suis senti en déphasage par rapport au cours, donc pour l'instant (et malgré moi) j'utilise ce que l'ont fait en cours avec dans l'idée de corriger ces erreurs de saisie pendant les grandes vacances (j'ai déjà achetéle livre de ritchie et kernighan pour y arriver).
 
pour les structures je note ;)
 
quand à la vérification de l'allocation et à la liberation, oui c'est un oubli de ma part.
 
Merci :)

Reply

Sujets relatifs:

Leave a Replay

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