[Résolu] Matrice et fonctions

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

Marsh Posté le 09-01-2007 à 00:18:53    

Bonsoir
 
Voilà je veux faire une fonction qui lit les élements d'une matrice mais le code suivant refuse de se compiler :  
 

Code :
  1. #include <stdio.h>
  2. void lire_tab (int **m, int l, int c)
  3. {
  4. int i, j;
  5. for (i = 0; i < l; i++)
  6.  for (j = 0; j < c; j++)
  7.  {
  8.   printf ("M[%d,%d] = ", i + 1, j + 1);
  9.   scanf ("%d", &m[i][j]);
  10.  }
  11. }
  12. int main (void)
  13. {
  14. int a[10][10], l, c, i, j;
  15. printf ("Nombre de lignes : " );
  16. scanf ("%d", &l);
  17. printf ("Nombre de colonnes : " );
  18. scanf ("%d", &c);
  19. lire_tab (a, l, c);
  20. for (i = 0; i < l; i++)
  21. {
  22.  for (j = 0; j < c; j++)
  23.   printf ("\t%d", a[i][j]);
  24.  printf ("\n" );
  25. }
  26. return 0;
  27. }


 
et le problème je sais pas pourquoi .
 
en essayant de le corriger, j'arrive à ça :

Code :
  1. #include <stdio.h>
  2. void lire_tab (int m[10][10], int l, int c)
  3. {
  4. int i, j;
  5. for (i = 0; i < l; i++)
  6.  for (j = 0; j < c; j++)
  7.  {
  8.   printf ("M[%d,%d] = ", i + 1, j + 1);
  9.   scanf ("%d", &m[i][j]);
  10.  }
  11. }
  12. int main (void)
  13. {
  14. int *a[10], l, c, i, j;
  15. printf ("Nombre de lignes : " );
  16. scanf ("%d", &l);
  17. printf ("Nombre de colonnes : " );
  18. scanf ("%d", &c);
  19. lire_tab (a, l, c);
  20. for (i = 0; i < l; i++)
  21. {
  22.  for (j = 0; j < c; j++)
  23.   printf ("\t%d", a[i][j]);
  24.  printf ("\n" );
  25. }
  26. return 0;
  27. }


 
ça à le mérite de fonctionner  :sarcastic: , mais bon le problème c'est que j'aimerais une fonctions standard, et ne pas être obligé de spécifier [10 = nombre de colonne], voilà si quelqu'un pouvait m'expliquer où est l'erreur ça serait sympa
 
merci
 
PS : je veux pas utiliser de fonction int** lire_tab (int l, int c) mais bien une fonction void ;)

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

Marsh Posté le 09-01-2007 à 00:18:53   

Reply

Marsh Posté le 09-01-2007 à 04:34:42    

exhortae a écrit :

Voilà je veux faire une fonction qui lit les élements d'une matrice mais le code suivant refuse de se compiler :  
 

Code :
  1. #include <stdio.h>
  2. void lire_tab (int **m, int l, int c)
  3. {
  4. int i, j;
  5. for (i = 0; i < l; i++)
  6.  for (j = 0; j < c; j++)
  7.  {
  8.   printf ("M[%d,%d] = ", i + 1, j + 1);
  9.   scanf ("%d", &m[i][j]);
  10.  }
  11. }



 
Bienvenue dans le monde des tableaux à plusieurs dimensions !!!
Le pb du débutant, c'est qu'il a tendance à croire que [] <=> * donc [][] <=> **
L'équivalence n'est vrai que pour une seule dimension. Pourquoi ? parce que la mémoire n'est définie, en mémoire, que sur une seule dimension. Les cases mémoires sont alignées sur une seule et immense ligne.
Donc le code suivant

void init(int tab[], int max)
{
    int i;
    for (i=0; i < max; i++)
        tab[i]=0;      // Ou *(tab + i)=0
}


 
Peut être sans problème remplacé par

void init(int *tab, int max)
{
    int i;
    for (i=0; i < max; i++)
        tab[i]=0;      // Ou *(tab + i)=0
}


 
Avec le main unique

int main()
{
    int tablo[10];
    init(tablo, 10);
}


 
Avec 2 dim, ce n'est plus du tout vrai. Parce que, pour un tableau de 5 lignes sur 10 colonnes, style "int t[5][10]", lorsque tu fais "t[i][j]", le compilo remplace par "la case située à i * 10 + j". Il en ressort que si tu passes "t" à une fonction, il faut que la fonction connaisse quand-même la largeur du tableau pour pouvoir calculer la position de ses cases.
 
Donc le code suivant

void init(int tab[5][10], int lig, col)
{
    int i,j;
    for (i=0; i < lig; i++)
    {
        for (j=0; j < col; j++)
             tab[i][j]=0;      // Ou *(*(tab + i)) + j=0
    }
}


 
Peut être sans problème remplacé par

void init(int *tab[10], int lig, col)
{
    int i,j;
    for (i=0; i < lig; i++)
    {
        for (j=0; j < col; j++)
             tab[i][j]=0;      // Ou *(*(tab + i)) + j=0
    }
}


 
Mais ne peut absolument pas être remplacé par

void init(int **tab, int lig, col)
{
    int i,j;
    for (i=0; i < lig; i++)
    {
        for (j=0; j < col; j++)
             tab[i][j]=0;      // Ou *(*(tab + i)) + j=0
    }
}


Car là, dans la fonction "init", le compilo ne connait pas la largeur du tableau et ne peut donc pas calculer la position de l'élément demandé.
 
Le main, lui, reste unique

int main()
{
    int tablo[5][10];
    init(tablo, 5, 10);
}


 
En fait, l'équivalence [] <=> * ne marche que pour la première dimension du tableau. Et dès qu'on travaille en plusieurs dim, t'es obligé de conserver les autres.
 
 

exhortae a écrit :

en essayant de le corriger, j'arrive à ça :

Code :
  1. int main (void)
  2. {
  3. int *a[10], l, c, i, j;
  4. printf ("Nombre de lignes : " );
  5. scanf ("%d", &l);
  6. printf ("Nombre de colonnes : " );
  7. scanf ("%d", &c);
  8. lire_tab (a, l, c);
  9. for (i = 0; i < l; i++)
  10. {
  11.  for (j = 0; j < c; j++)
  12.   printf ("\t%d", a[i][j]);
  13.  printf ("\n" );
  14. }
  15. return 0;
  16. }


 
ça à le mérite de fonctionner


C'est le pire danger qui pouvait t'arriver: En ligne 3 tu déclares "a" un tableau de 10 pointeurs. Puis, sur ces pointeurs qui pointent on ne sais-où, tu vas y remplir des trucs. Mais tu ne sais absolument pas si tu ne pointes pas sur des zones allouées à d'autres variables (il n'y a qu'une mémoire). Le résultat te donne un comportement indéfini, c'est à dire où tout peut arriver (j'aime bien cette définition donnée par Emmanuel Delahaye), y compris que cela fonctionne parce que, par chance, tu tapes dans des zones libres. Puis, un jour, tu rajoutes un peu de code banal, style un autre tableau, un printf(), une fonction, un truc totalmeent bateau et là ça plante. Et évidement tu chercheras la cause du plantage dans les quelques lignes que t'auras rajouté alors qu'elle sera en ligne 3.
 
Pour éviter ce danger, il faut toujours se rappeler d'un truc très simple: quand tu as un pointeur style "int *t", tu n'as pas le droit d'aller taper dans "t[i]" (qui est aussi "*(t + i)" ) si t'as pas écrit avant "t=<qqchose>". Ca peut être n'importe quoi mais faut que ce soit valide. Evidemment les deux seuls trucs qu'on peut mettre dans "<qqchose>" sont

  • une adresse valide (par exemple celle d'un tableau déjà alloué)
  • une réservation mémoire (malloc)

Exemples

int tablo[10];
int *t;
t=tablo;      // Ou bien t=&tablo[0]
t[5]=0;


int var;
int *t;
t=&var;
t[0]=12;      // Alors là, évidemment, on n'a pas le droit de mettre un autre indice que "0" !!!


int *t;
t=malloc(10 * sizeof(int));
t[5]=0;
free(t);


 
La règle est valable pour "n" dimensions. T'as pas le droit de taper dans "t[x][y][z]" si "t" ou t[x]" ou "t[x][y]" n'ont pas été alloués auparavant. Or toi, tu vas taper dans "a[i][j]" (équivalent à "*(*(a + i) + j)" ) alors que "a[i]" n'a pas été alloué !!!
 

exhortae a écrit :

mais bon le problème c'est que j'aimerais une fonctions standard, et ne pas être obligé de spécifier [10 = nombre de colonne]


Pas de miracle. Tu déclares "a" comme un pointeur sur un pointeur sur un int (int **a), puis tu alloues à "a" l'espace pour ranger "l" pointeurs sur des int (a=malloc(l * sizeof(int*))) puis tu fais une boucle de 0 à l et tu alloues à chaque "a[i]" l'espace pour ranger c int (a[i]=malloc(c * sizeof(int))).
Là, ce n'est plus un beau carré que tu as mais une pelote de laine où tu peux suivre chaque fil de façon indépendante. Tu pourras donc à loisir aller taper dans "a[i][j]". Le compilo partira de "a" pour aller "i" pointeurs plus loin (tous les éléments de "a" se suivent en mémoire). Ce pointeur ayant été rempli par le a[i]=malloc(...), le compilo pourra aller sur "*a[i]" (<=> a[i][0]) puis, de là, se déplacer sur la case "j" qui se trouve à "j * int" octets plus loin.
N'oublie pas en final, de refaire une boucle de 0 à l et de libérer les "a[i]" avec des "free()" puis, enfin, de libérer "a" par un "free()" final.

Message cité 1 fois
Message édité par Sve@r le 09-01-2007 à 05:12:58

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

Marsh Posté le 09-01-2007 à 19:36:43    

exhortae a écrit :

Bonsoir
 
Voilà je veux faire une fonction qui lit les élements d'une matrice mais le code suivant refuse de se compiler :  
 

Code :
  1. void lire_tab (int **m, int l, int c)
  2. {
  3. }
  4. int main (void)
  5. {
  6. int a[10][10], l, c, i, j;
  7. lire_tab (a, l, c);
  8. }




Mauvais type.  
 
http://mapage.noos.fr/emdel/notes.htm#param_tab


---------------
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 10-01-2007 à 15:29:31    

Sve@r a écrit :


C'est le pire danger qui pouvait t'arriver: En ligne 3 tu déclares "a" un tableau de 10 pointeurs.

 

oui une erreur de précipitation, mais là c'est bon je sais que je dois y faire attention pour les fois prochaines

 


Sve@r a écrit :


Pas de miracle. Tu déclares "a" comme un pointeur sur un pointeur sur un int (int **a), puis tu alloues à "a" l'espace pour ranger "l" pointeurs sur des int (a=malloc(l * sizeof(int*))) puis tu fais une boucle de 0 à l et tu alloues à chaque "a[i]" l'espace pour ranger c int (a[i]=malloc(c * sizeof(int))).
Là, ce n'est plus un beau carré que tu as mais une pelote de laine où tu peux suivre chaque fil de façon indépendante. Tu pourras donc à loisir aller taper dans "a[i][j]". Le compilo partira de "a" pour aller "i" pointeurs plus loin (tous les éléments de "a" se suivent en mémoire). Ce pointeur ayant été rempli par le a[i]=malloc(...), le compilo pourra aller sur "*a[i]" (<=> a[i][0]) puis, de là, se déplacer sur la case "j" qui se trouve à "j * int" octets plus loin.
N'oublie pas en final, de refaire une boucle de 0 à l et de libérer les "a[i]" avec des "free()" puis, enfin, de libérer "a" par un "free()" final.

 

là ça marche, mais moi ce que je voudrais c'est que ça soit la fonction qui se charge de faire le tout (allocation et saisie), j'ai donc essayer ça :

 
Code :
  1. #include <stdio.h>
  2. void lire_tab (int ***m, int l, int c)
  3. {
  4. int i, j;
  5.     *m = (int**) malloc (l * sizeof (int*));
  6.     if (*m == NULL)
  7.         exit (0);
  8.     else
  9.     {
  10.         for (i = 0; i < l; i++)
  11.         {
  12.             *m[i] = (int*) malloc (c * sizeof (int));
  13.             if (*m[i] == NULL)
  14.             exit (0);
  15.         }
  16.     for (i = 0; i < l; i++)
  17.      for (j = 0; j < c; j++)
  18.      {
  19.       printf ("M[%d,%d] = ", i + 1, j + 1);
  20.       scanf ("%d", (*m)[i][j]);
  21.      }
  22.     }
  23. }
  24. int main (void)
  25. {
  26. int **a, l, c, i, j;
  27. printf ("Nombre de lignes : " );
  28. scanf ("%d", &l);
  29. printf ("Nombre de colonnes : " );
  30. scanf ("%d", &c);
  31.     lire_tab (&a, l, c);
  32. for (i = 0; i < l; i++)
  33. {
  34.  for (j = 0; j < c; j++)
  35.   printf ("\t%d", a[i][j]);
  36.  printf ("\n" );
  37. }
  38.     for (i = 0; i < l; i++)
  39.         free (a[i]);
  40.     free (a);
  41. return 0;
  42. }
 

mais ça ne marche pas (je pense à cause d'une erreur dans le scanf de la fonction)

Message cité 1 fois
Message édité par exhortae le 10-01-2007 à 15:30:49
Reply

Marsh Posté le 10-01-2007 à 15:36:14    


 
mon prof n'était pas au courant de ça :D
 

Reply

Marsh Posté le 10-01-2007 à 16:35:18    

exhortae a écrit :


là ça marche, mais moi ce que je voudrais c'est que ça soit la fonction qui se charge de faire le tout (allocation et saisie), j'ai donc essayer ça :
 

Code :
  1. #include <stdio.h>
  2. void lire_tab (int ***m, int l, int c)
  3. {
  4. int i, j;
  5.     *m = (int**) malloc (l * sizeof (int*));
  6.     if (*m == NULL)
  7.         exit (0);
  8.     else
  9.     {
  10.         for (i = 0; i < l; i++)
  11.         {
  12.             *m[i] = (int*) malloc (c * sizeof (int));
  13.             if (*m[i] == NULL)
  14.             exit (0);
  15.         }
  16.     for (i = 0; i < l; i++)
  17.      for (j = 0; j < c; j++)
  18.      {
  19.       printf ("M[%d,%d] = ", i + 1, j + 1);
  20.       scanf ("%d", (*m)[i][j]);
  21.      }
  22.     }
  23. }
  24. int main (void)
  25. {
  26. int **a, l, c, i, j;
  27. printf ("Nombre de lignes : " );
  28. scanf ("%d", &l);
  29. printf ("Nombre de colonnes : " );
  30. scanf ("%d", &c);
  31.     lire_tab (&a, l, c);
  32. for (i = 0; i < l; i++)
  33. {
  34.  for (j = 0; j < c; j++)
  35.   printf ("\t%d", a[i][j]);
  36.  printf ("\n" );
  37. }
  38.     for (i = 0; i < l; i++)
  39.         free (a[i]);
  40.     free (a);
  41. return 0;
  42. }


 
mais ça ne marche pas (je pense à cause d'une erreur dans le scanf de la fonction)


Tout à fait. Tu jongles remarquablement bien avec les pointeurs... mais tu as fait une minuscule erreur d'indirection probablement due à tous ces niveaux d'étoiles que tu manipules (au delà de "**" n'importe qui commence à avoir du mal)
C'est scanf("%d", &(*m)[i][j]) qu'il faut mettre, ou plus simplement scanf("%d", m[i][j]). Pourquoi ? Parce que "m" est déjà un pointeur. D'ailleurs, un simple raisonnement aurait dû te montrer ton erreur:

  • m est de type "int ***" donc "m[i]" est de type "int **" donc "m[i][j]" est de type "int *" donc "(*m)[i][j]" est de type "int". Or, scanf veut un "int *" et non un "int" donc il suffit de mettre "&" devant...


D'ailleurs t'as pensé à ce que j'avais suggéré au début, à savoir regrouper ton tableau et son nombre d'éléments dans une structure ? 1) ça te simplifierait la vie au niveau des paramètres que tu passes à "lire_tab" et 2) ça réduirait toutes tes jongleries avec les "***" et cie...
 

exhortae a écrit :

mon prof n'était pas au courant de ça :D


Ben change de prof parce que c'est quand-même une des bases du C !!!

Message cité 1 fois
Message édité par Sve@r le 10-01-2007 à 16:36:56

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

Marsh Posté le 10-01-2007 à 17:17:11    

Sve@r a écrit :

D'ailleurs, un simple raisonnement aurait dû te montrer ton erreur:

  • m est de type "int ***" donc "m[i]" est de type "int **" donc "m[i][j]" est de type "int *" donc "(*m)[i][j]" est de type "int". Or, scanf veut un "int *" et non un "int" donc il suffit de mettre "&" devant...


jla prends comme méthode pour tester si ça va marcher à partir de maintenant ;)
 
donc voilà j'ai suivi tes indications mais à l'exécution j'obtient l'erreur suivante :
 
http://img232.imageshack.us/img232//patwl5.jpg
 
voilà le code
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. void lire_tab (int ***m, int l, int c)
  4. {
  5. int i, j;
  6.     *m = (int**) malloc (l * sizeof (int*));
  7.     if (*m == NULL)
  8.         exit (0);
  9.     else
  10.     {
  11.         for (i = 0; i < l; i++)
  12.         {
  13.             *m[i] = (int*) malloc (c * sizeof (int));
  14.             if (*m[i] == NULL)
  15.             exit (0);
  16.         }
  17.     for (i = 0; i < l; i++)
  18.      for (j = 0; j < c; j++)
  19.      {
  20.       printf ("M[%d,%d] = ", i + 1, j + 1);
  21.       scanf ("%d", m[i][j]);
  22.      }
  23.     }
  24. }
  25. int main (void)
  26. {
  27. int **a, l, c, i, j;
  28. printf ("Nombre de lignes : " );
  29. scanf ("%d", &l);
  30. printf ("Nombre de colonnes : " );
  31. scanf ("%d", &c);
  32.     lire_tab (&a, l, c);
  33. for (i = 0; i < l; i++)
  34. {
  35.  for (j = 0; j < c; j++)
  36.   printf ("\t%d", a[i][j]);
  37.  printf ("\n" );
  38. }
  39.     getchar ();
  40.     for (i = 0; i < l; i++)
  41.         free (a[i]);
  42.     free (a);
  43. return 0;
  44. }


 
 

Sve@r a écrit :


D'ailleurs t'as pensé à ce que j'avais suggéré au début, à savoir regrouper ton tableau et son nombre d'éléments dans une structure ? 1) ça te simplifierait la vie au niveau des paramètres que tu passes à "lire_tab" et 2) ça réduirait toutes tes jongleries avec les "***" et cie...


 
si j'y ai pensé mais ma connaissance des structure actuellement me permet pas d'être à l'aise avec ça, même si ça a l'air facile je préfère appréhender les choses une par une (et en ce moment c'est les pointeurs ;))
 

Sve@r a écrit :


Ben change de prof parce que c'est quand-même une des bases du C !!!


 
pas possible de changer (pour l'instant) :(  

Reply

Marsh Posté le 10-01-2007 à 22:11:20    

exhortae a écrit :

jla prends comme méthode pour tester si ça va marcher à partir de maintenant ;)


Ca marche à tous les coups
 

exhortae a écrit :

donc voilà j'ai suivi tes indications mais à l'exécution j'obtient une erreur


Oui, en écrivant le post précédent je me disais "est-ce qu'il ne va pas y avoir un pb ici" et finallement, il y a bien eu les pb auxquels je pensais.
1)

exhortae a écrit :

*m[i] = (int*) malloc (c * sizeof (int))



Pas bon. La priorité des opérateurs fait que le compilo commencera par faire "m[i]" c'est à dire aller à la case "i" puis faire "ce qui est pointé par cette case" c'est à dire n'importe quoi.
Toi, ce que tu veux, c'est aller d'abord au pointé de "m", c'est à dire "*m" puis aller à la case "i" à partir de ce pointé, c'est à dire

(*m)[i] = (int*) malloc (c * sizeof (int));


 
2)

exhortae a écrit :

scanf ("%d", m[i][j])



Idem (mais là, c'est ma faute). Il faut commencer par faire "*m" pour aller au pointé, puis se déplacer à la case [i][j] puis demander l'adresse de cette case, c'est à dire  

scanf ("%d", &((*m)[i][j]))


Là j'ai testé.
 
Donc l'astuce que j'ai expliquée marche à tous les coups... à condition que les priorités soient bien de notre coté sinon dommage !!!
 

exhortae a écrit :

si j'y ai pensé mais ma connaissance des structure actuellement me permet pas d'être à l'aise avec ça, même si ça a l'air facile je préfère appréhender les choses une par une (et en ce moment c'est les pointeurs ;))


Pas de pb - A chaque jour suffit sa peine...


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

Marsh Posté le 11-01-2007 à 18:35:50    

Oui je me disais bien qu'il y avait un truc avec les parenthèses mais j'avais pas réussi à trouver, en tout cas les choses commencent à se mettre en place dans ma tête niveau pointeurs et fonctions, beaucoup grâce à toi, merci pour ton aide :jap:

Reply

Sujets relatifs:

Leave a Replay

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