Aller au contenu

[C]Pointeurs de pointeurs de pointeurs de...


Don_Angelo

Messages recommandés

Bonjour à tous,

J'ai un petit problème de pointeurs qui me prend le chou depuis un peu moins d'une semaine.

J'ai une fonction dont voici le code qui déclare et initialise des tableaux de structures:

typedef struct
{
   char nom;
   int x;
   int y;
   couleur clan;
}piece;

typedef piece* echiquier[8][8];

void new_game(echiquier ech)
{
   int i,j;
   piece **camp;
   camp=(piece**) malloc(2*sizeof(piece));
   for (i=0;i<16;i++) *(camp+i) = (piece*) malloc(16*sizeof(piece));

   for (i=2;i<6;i++)
   {
       for (j=0;j<8;j++) ech[i][j]=NULL;
   }


   for (i=0;i<16;i++)      //pions
   {
       camp[i/8][i%8+8].nom = (char) ((int) 'P'+ (i/8)*32);
       camp[i/8][i%8+8].clan = (couleur) i/8;
       camp[i/8][i%8+8].x = i%8+1;
       camp[i/8][i%8+8].y = (1-i/8)*5+2;
       ech[(1-i/8)*5+1][i%8] = &camp[i/8][i%8+8];
   }

...

   //roi noir
       camp[1][7].nom = 'r';
       camp[1][7].clan = NOIR;
       camp[1][7].x = 5;
       camp[1][7].y = 1;
       ech[0][4] = &camp[1][7];
}

Ce que j'aimerais c'est que cette fonction retourne un pointeur vers le tableau camp que je pourrais appeler dans le main et manipuler sans avoir de problème de portée de variable.

Le problème c'est que je ne sais plus trop bien combien je dois mettre d'étoiles dans le main pour déclarer mon pointeur, ni même dans le return de ma fonction.

Lien à poster

Bon, ça fait un moment que je n'ai plus fait de C, en ce moment c'est C++ au boulot.

Plusieurs idées me viennent :

Le typedef est inutile pour la déclaration du tableau, par contre il est bien placé pour la structure, c'est bien de ne pas avoir à taper struct à chaque déclaration.

Pourquoi stocker le x,y avec dans une pièce, alros que tu as ton tableau qui donne les coordonnées de la pièce.

Je te conseille d'utiliser plutôt un tableau 1d comme ceci :

piece echiquier[16];

Par rapport à des tableaux alloués dynamiquement, ça te fait seulement 1 malloc et 1 free, au lieu d'avoir à faire une boucle pour chaque. Et c'est plus facile à maintenir et débugger.

En le déclarant avec les [ ], si tu utilises seulement le nom du tableau, il devient équivalent à un pointeur. En fait, c'en est un (mais attention, il reste statique il me semble, tu ne peux pas faire un changement d'adresse là genre echiquier = malloc(blabla....)).

Pour connaitre par exemple la pièce en (row, col), tu peux te faire une chtite fonction comme ça (en supposant que ton tableau soit de largeur width et hauteur height, stocké dans des variables globales, ou une structure : je te donnes le code générique, dans ton cas c'est (8,8)):

piece Get_Piece(int x, int y)
{
return echiquier[y*width + x];
}

ou avec les pointeurs (à vérifier, je le fais en live là) :

piece Get_Piece(int x, int y)
{
return *(echiquier+(y*width + x));
}

Je te laisse le code pour une fonction Set_Piece(int x, int y, piece p)... :D

Tu peux déclarer ton tableau en global ou seulement dans le main, à toi de voir comment tu vas architecturer ton programme.

Si tu veux l'utiliser ailleurs que dans le main (dans des .h ou .c), tu peux faire :

extern piece echiquier[16];

Pour le main :

...
main
{
...
new_game(echiquier);
...
}

avec par exemple :

void new_game(echiquier *ech)
{
...
for(int j=0; j<8; j++)
for(int i=0;i<8; i++)
 printf("Piece n° %i nom : ", i,  ech[j*8+i].nom); // ou printf("Piece n° %i nom : ", i,  Get_Piece(i,j).nom);

Et voilou.

Question : es-tu réellement obligé de d'utiliser un pointeur de pointeur ? Pas très utile dans le cas présent, à mon avis.

Ensuite: est-ce vraiment judicieux de créer un tableau de pièces pour l'échiquier ? Ne serait-il pas plus clair de faire un tableau de pièces pour stocker les pions , avec des fonctions permettant de supprimer les pièces qui sont "mangées", adapter sa taille en fonction du nombre de pièces : stocker la taille dans une structure, qui contient aussi le tableau :

struct S_Pieces
{
piece tabPieces[16];
int size;
};

et parcourir ce tableau pour remplir l'échiquier (moins il y a de pièces plus ça ira vite, bon là c'est assez insignifiant). Cet échiquier serait un tableau de booléens (facile à voir si la case est libre du coup).

Pour récupérer une pièce, il suffit de parcourir le tableau de pièces et trouver les coordonnées correspondantes à la case sur l'échiquier.

Je me rend compte que je me suis mis le cerveau à l'envers pour pas grand chose là :lol .

Lien à poster

Merci LapinGarou pour tes conseils.

En fait on a pas trop le choix pour ce qui est du codage dans le sens ou c'est un projet qui nous est donné par la fac avec des instructions précises. Sur certains points on doit se débrouiller et pour d'autres on a pas le choix.

Concernant mon problème je n'ai pas vu que j'avais typé new_game en void alors que j'aurais du la typer en void* ainsi je récupère bien ce que je veux dans mon main.

Pour le tableau camp il est à mon sens obligatoire qu'il soit à deux dimensions: du 2*16, la première servant à 'classer' les pièces par joueur. ainsi camp[1] nous donne le tableaux de pièce du joueur 2 camp[0] celui du joueur 1.

On nous impose d'utiliser un type échiquier (ce que je trouve idiot d'ailleurs), alors on a chercher à tourner cette contrainte à notre avantage.

Voilà comment je compte modéliser le problème. Stocker les pièces des joueurs dans un tableau camp de façon à ce que ce tableau soit en fait un tableau de pointeurs, ainsi j'initialise l'élément du dit tableau à null quand la pièce correspondante de l'échiquier est mangée. Il est vrai que la maintenance serait plus facile si on définissait une liste chainée. mais je n'ai jamais bien su les manier en C.

Mon échiquier contient des pointeurs de pièce, afin que si une case est occupée elle contient un pointeur vers le tableau camp. Donc il me sert non seulement à savoir si une case de l'échiquier est occupée, mais aussi à retrouver plus vite la pièce qui l'occupe le cas échéant. ça nous facilite le travail pour vérifier, par exemple, si un roi est en échec ou non dans le sens ou dans certains cas il est plus efficace de rechercher sur le tableau camp et dans d'autres sur le tableau échiquier. Le gros inconvénient de cette méthode c'est que l'on doit mettre à jour une pièce dans deux tableaux, mais j'imagine qu'on ne peut pas avoir le beurre et l'argent du beurre.

C'est vrai qu'il est plus simple de mettre mon échiquier et mon tableau camp en variables globales, mais personnellement je suis contre cette solution parce que ça restreint les possibilités du programme. Dans le sens ou les données sont, pour moi, mon bien encapsulées et que le prog ne pourrait gérer qu'une partie à la fois.

Mais quoiqu'il arrive il est clair que je devrais coder des fonctions pour faire la maintenance des tableaux camp et échiquier.

L'inconvénient majeur dans cette histoire c'est que les profs voudraient nous forcer à modéliser le problème d'une façon tellement tordue qu'on a énormément de mal à adapter notre code à leurs exigences. Eux voudraient nous forcer à modéliser un type pour l'échiquier et un type pour les cases de l'échiquier, mais pas de type pour les pièces, ce que je trouve un peu étrange.

Lien à poster

J'avoue que j'ai réfléchi à l'arrache au problème, j'ai dit comment je l'aurai codé sans vraiment y réfléchir plus avant.

C'est un peu nul d'obliger de passer par une façon de résoudre le problème. En même temps, quand on est sous les "ordres" d'un chef de projet, il faut faire ce qu'il dit, de la façon qu'il le dit.

Lien à poster

oui j'imagine que c'est pour nous préparer au métier.

Par contre là où j'ai un gros doute c'est pour le libérer la mémoire allouée par ma fonction parce que depuis mon main() je ne peux pas faire de free(camp), même si camp est bien déclaré car c'est celui du main et il n'a pas été déclaré avec un malloc() donc j'imagine que free() n'apprécierait pas non?

Lien à poster

Si tu n'avais pas alloué ton dynamiquement (donc avec un malloc), pas besoin de faire un free.

Par contre, vu que tu fais des mallocs :

piece **camp;
camp=(piece**) malloc(2*sizeof(piece));
for (i=0;i<16;i++) 
   *(camp+i) = (piece*) malloc(16*sizeof(piece)); 

il faut faire les "free" dans l'ordre inverse : d'abord la boucle "for", puis pour camp.

A partir du moment où tu as fait un malloc, il faut forcément un free quelque part.

Je ne me rappelle plus trop les joies de ce genre en C, mais il me semble qu'il faut libérer la mémoire manuellement, contrairement au C++ qui peut le faire seul suivant comment on a implémenté la construction.

Lien à poster
×
×
  • Créer...