pointeur de tableau 2D d'entiers...

pointeur de tableau 2D d'entiers... - C++ - Programmation

Marsh Posté le 16-03-2012 à 21:10:52    

Bonjour, (je créé un nouveau topic pour pas encombrer le précédent)
J'ai un souci avec un passage de fonction en paramètre j'ai beau retourner le problème dans tout les sens ça veut pas compiler...
 
J'ai un fichier Map.h dans lequel je créé une class avec en private : (ligne 30)

Code :
  1. private:
  2. int terrain[12][12];


 
Et en public la fonction qui renvoi ce tableau :

Code :
  1. public
  2. int** RenvoitTerrain();


 
Avec comme code dans le Map.cpp

Code :
  1. int** Map::RenvoitTerrain()
  2. {
  3.   return terrain;
  4. }


 
Ma fonction utilisant cette class comporte #include "Map" et après avoir créé un objet maCarte de type Map demande :

Code :
  1. Rect_source.x = (*maCarte.RenvoitTerrain())[j][i]*LARGEUR_TILE;


 
 
L'erreur que renvoi le compilateur :
map.cpp:30:10: error: cannot convert ‘int (*)[12]’ to ‘int**’ in return
make: *** [map.o] Erreur 1
 
J'ai beau changer int** en int[12]* il me lève une autre erreur, et ainsi de suite  :pt1cable:  
 
Merci d'avance.
 

Reply

Marsh Posté le 16-03-2012 à 21:10:52   

Reply

Marsh Posté le 17-03-2012 à 16:39:13    

Un tableau C et un pointeur sont deux choses différentes : int[] et int* sont différents, int[][] et int** sont différents aussi. Quand tu écris return terrain; tu ne dois pas t'attendre à une conversion magique entre ton int terrain[12][12] et ton type de retour int**.  
 
Je vais tenter de dresser un portrait de la situation mais tu vas vite voir que C++ est un langage (très complexe) qui n'aime pas trop les tableaux C, qui sont du coup difficiles à manipuler surtout quand ils ont plusieurs dimensions.
De base C++ "dégrade" le type tableau (à une dimension) qu'on lui passe pour en faire un pointeur (ce comportement vient du C).  
On peut donc écrire ce genre de chose :

Code :
  1. int* getArray(int array[]) { // array est dégradé en int*
  2.     return array;
  3. }


Note que je suis censé retourner un int* et que visiblement mon return array; ne pose pas de problème. C'est pour ça que je parle de dégradation, en fait c'est une autre écriture pour :

Code :
  1. int* getArray(int* array) {
  2.     return array;
  3. }


mais ce n'est pas un "vrai" passage de tableau en argument qu'on fait là si je puis dire. D'ailleurs si on veut passer un tableau d'int à 2 dimensions en paramètre, on ne peut pas écrire :

Code :
  1. void foobar(int array[][]) { ... }   // declaration of ‘array’ as multidimensional array must have bounds for all dimensions except the first


(le message d'erreur nous indique qu'une chose comme "void foobar(int array[][37]) { ... }" marcherait soit dit en passant).  
 
De même si on veut retourner un tableau de ce genre, on ne peut pas écrire des trucs du genre :

Code :
  1. int[][] getArray() { ... } //  error: expected unqualified-id before ‘[’ token


 
 
Comment on fait alors ? Ben c'est compliqué :/
 
C'est pour ça que la plupart du temps :

  • soit les gens utilisent les collections de la stl comment std::vector<int>, qui sont les tableaux de C++ (par opposition à int[] qui sont les tableaux de C)
  • soit ils abandonnent l'idée d'utiliser "proprement" les types tableaux C et se rabattent sur des pointeurs ou des pointeurs de pointeurs. Mais comme type de retour pour une méthode publique d'une classe c'est franchement pas top. Quand on utilise ce genre de raccourcis il faut que ça reste en private mais il faut éviter d'exposer ce genre de cuisine interne publiquement.


 
 
Un exemple de méthode qui bosse vraiment avec un tableau de taille 5 (et uniquement 5), sans dégrader son type en pointeur :

Code :
  1. int ( &fillArray( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
  2.     return arr;
  3. }
  4. int foo[5];
  5. fillArray(foo);


Je ne pense pas que ce genre de choses soit une bonne idée (syntaxe complexe, taille fixée, etc).  
Dans la version la plus récente de C++ on peut faire la même chose avec une syntaxe moins cryptique, en utilisant des templates :

Code :
  1. #include <array>
  2. using namespace std;
  3. array<int,5>& fillArray( array<int,5> &arr) {
  4.     return arr;
  5. }
  6. array<int,5> foo;
  7. array<int,5> bar = fillArray(foo);


 
 
Mon conseil : fait toi une classe Terrain, qui n'utilise qu'en interne des pointeurs genre int** pour manipuler ton int[][]. Dans le reste de ton code après tu utilises cet objet Terrain, mais pas un int**, c'est trop casse-gueule :o


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Marsh Posté le 17-03-2012 à 18:30:41    

Pourquoi tu fais plutôt un accesseur sur le tableau ?
 
genre :  
 

Code :
  1. int Map::GetValueAt(int xx,int yy)
  2. {
  3.   // Accès hors du tableau :
  4.   if (xx>12 || xx<0 || yy>12 || yy<0)
  5.      return -1;
  6.  
  7.     return tableau[yy][xx]; // ou return tableau[xx][yy];
  8. }


---------------
Perhaps you don't deserve to breathe
Reply

Marsh Posté le 17-03-2012 à 23:54:55    

@Terminator : Nan en fait il me faut vraiment la valeur de tout le tableau pour pouvoir le manipuler "une couche au dessus".

 

@Mathieux : Merci pour ce cours détaillé :)
Je cerne mieux le problème maintenant, en fait comme que je le dis à Terminator, je voudrais utiliser ce tableau "une couche au dessus" dans la fonction qui utilise ma class Map. (Je suis donc obligé de faire mes magouilles sur ces deux étages)
En suivant ton idée, mettons que je déclare dans ma class (en private) mon tableau comme un int **, je fais ensuite une fonction qui retourne simplement tableau (int**).
J'ai deux questions :
- Comment j'initialise mon tableau ? (un malloc est nécessaire non ?)
- Comment j'appelle ma fonction RetourneTableau une couche au dessus? (simplement avec un maCarte.RetourneTableau() ?)


Message édité par beuted le 18-03-2012 à 10:58:34
Reply

Marsh Posté le 18-03-2012 à 08:26:55    

ne pas oublier boost::array (ou std::array) qui permet de manipuler de stableaux C ave cune semantique de valeur correct.

Reply

Marsh Posté le 18-03-2012 à 10:57:24    

@Mathieux je retire ma question j'ai trouvé un tuto très bien à ce sujet. http://h-deb.clg.qc.ca/Sujets/Dive [...] ux-2D.html
Le problème c'est que maintenant j'ai une belle seg fault PROBLEME RESOLU je n'avais juste pas compris le principe du destructeur en fait, je ne pensais pas qu'il s'appliquait directement après le constructeur.

 

Voila mon constructeur et mon destructeur (tableau de type int**) :

Code :
  1. #define LARGEUR_MAP 10
  2. #define HAUTEUR_MAP 10
  3. Map::Map()
  4. {
  5.   terrain  = new int* [LARGEUR_MAP];
  6.   for (int i = 0; i < LARGEUR_MAP; i++) {
  7.     terrain[i] = new int[HAUTEUR_MAP];
  8.   }
  9. }
  10. Map::~Map()
  11. {
  12.   for (int i = 0; i < LARGEUR_MAP; i++)
  13.     delete [] terrain[i];
  14.   delete [] terrain;
  15. }
 

Et ma fonction qui affiche mon terrain en utilisant le tableau d'entier, c'est la que ca seg fault)

 
Code :
  1. void Map::afficherTerrain(SDL_Surface* surface, SDL_Surface* screen,int nombre_blocs_largeur,int nombre_blocs_hauteur)
  2. {
  3. int i,j;
  4. SDL_Rect Rect_dest;
  5. SDL_Rect Rect_source; // # recréé a chaque fois ?
  6. Rect_source.w = LARGEUR_TILE;
  7. Rect_source.h = HAUTEUR_TILE;
  8. for(i=0;i<nombre_blocs_largeur;i++)
  9. {
  10.  for(j=0;j<nombre_blocs_hauteur;j++)
  11.  {
  12.   Rect_dest.x = i*LARGEUR_TILE;
  13.   Rect_dest.y = j*HAUTEUR_TILE;
  14.   Rect_source.x = terrain[j][i]*LARGEUR_TILE;
  15.   Rect_source.y = 0;
  16.   SDL_BlitSurface(surface,&Rect_source,screen,&Rect_dest);
  17.  }
  18. }
  19. }
 

Lorque j'appelle maCarte.afficherTerrain(tile,ecran,10,10) ca seg fault ...


Message édité par beuted le 18-03-2012 à 13:28:50
Reply

Marsh Posté le 18-03-2012 à 13:47:53    

T'as pas initialisé les valeurs du tableau, je me trompe ?


---------------
Perhaps you don't deserve to breathe
Reply

Marsh Posté le 18-03-2012 à 16:25:57    

Si si, dans mon constructeur.
Mais mon problème est résolu, j'ai juste laissé mon code au cas ou quelqu'un ai le même problème que moi tombe sur cette page.
Mon problème venait en fait de mon destructeur. Je l'ai supprimé et je l'ai remplacé par une fonction "libererTerrain" que j'appelle à la fin de mon programme.

Reply

Marsh Posté le 18-03-2012 à 22:48:28    

Ton destructeur est appelé lorsque ton instance est supprimée, tu dois mal manipuler une instance quelque part alors :??:


---------------
Perhaps you don't deserve to breathe
Reply

Marsh Posté le 18-03-2012 à 23:02:59    

Citation :

   le destructeur d'un objet créé de façon statique est appelé de façon implicite dès que le programme quitte la portée dans lequel l'objet existe
    le destructeur d'un objet créé de façon dynamique doit être appelé grâce au mot clé delete, qui permet de libérer la mémoire occupée par l'objet.


 
Mon destructeur se déclenchait quand je ne le souhaitais pas apparemment. Je l'ai simplement supprimé et remplacé par une fonction libererTableau.

Reply

Marsh Posté le 18-03-2012 à 23:02:59   

Reply

Marsh Posté le 19-03-2012 à 07:49:18    

beuted a écrit :

Citation :

   le destructeur d'un objet créé de façon statique est appelé de façon implicite dès que le programme quitte la portée dans lequel l'objet existe
    le destructeur d'un objet créé de façon dynamique doit être appelé grâce au mot clé delete, qui permet de libérer la mémoire occupée par l'objet.


 
Mon destructeur se déclenchait quand je ne le souhaitais pas apparemment. Je l'ai simplement supprimé et remplacé par une fonction libererTableau.


 
Les destructeurs ne se déclenchent pas de manière aléatoire :o Utilise ton debugger pour mettre un point d'arrêt dans le destructeur et une fois dedans regarde la pile d'appel, pour comprendre d'où ça part. Comme dit Terminapor c'est une erreur de manipulation.


---------------
Il y a autant d'atomes d'oxygène dans une molécule d'eau que d'étoiles dans le système solaire.
Reply

Sujets relatifs:

Leave a Replay

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