3 votes

Comment utiliser malloc pour un tableau bidimensionnel de structures ? (erreur de bus : 10)

J'essaie d'implémenter un solveur de sudoku. Pour ce faire, j'utilise une structure, illustrée ci-dessous, pour représenter une cellule sur un tableau de sudoku. Je déclare ensuite un tableau 9x9 de ces structures pour représenter le tableau.

la structure cellulaire :

struct cell{
     char value;
     unsigned int possible;
};

Ensuite, je déclare un tableau de structures comme :

struct cell board[9][9];

Mon problème est que lorsque j'essaie d'entrer une valeur dans le tableau (c'est-à-dire board[2][2].value = getchar() ;), parfois cela fonctionne, et d'autres fois j'obtiens cette erreur :

Bus error: 10

Je ne sais pas exactement ce que cela signifie... En quoi "Bus error : 10" diffère d'une erreur de segmentation ?

J'utilise gcc et j'édite avec vim. Mon sentiment est que j'ai besoin d'allouer dynamiquement la mémoire pour ce tableau. Maintenant, je comprends comment utiliser malloc pour allouer de la mémoire pour un tableau à deux dimensions, quelque chose comme ça :

int ** Array;  
Array = (int**) malloc(x_size*sizeof(int*));  
for (int i = 0; i < x_size; i++)  
    Array[i] = (int*) malloc(y_size*sizeof(int)); 

Mais j'ai du mal à mettre en œuvre la partie allocation de mémoire pour un tableau de structs à 2 dimensions.

Est-ce que cela ressemblerait à ceci ?

struct cell** board;
board = (struct cell**) malloc(x_size*sizeof(struct cell**));
for(int i=0; i< x_size; i++)
    board[i] = (struct cell*) malloc(y_size*sizeof(struct cell));

Je crains que cette " sizeof(struct cell) " n'alloue pas correctement la quantité de mémoire qu'elle devrait.

Toute aide serait grandement appréciée ! Je suis assez novice en C (le C++ est ma langue maternelle), j'ai beaucoup utilisé le C intégré, mais j'essaie de mieux comprendre le langage dans son ensemble.
Points bonus pour les détails \in des explications approfondies !

Gracias.

EDIT Je n'ai toujours pas mis en place d'allocation dynamique de mémoire, mais, comme demandé, voici le code qui produit l'erreur de bus :

 /* only code being used in solver.h*/
 29 /* structure to describe a cell */
 30 struct cell{
 31     int value;
 32     unsigned int possible;
 33 };

   /*solver.c*/
 4 #include <ctype.h>
 5 #include <stdio.h>
 6 #include "solver.h"
 7 
 8 
 9 struct cell board [9][9];
 10 
 11 
 12 int main(){
 13     initialize_board();
 14     print_board();
 15     setup_board();
 16     print_board();
 17 return 0;
 18 }
 19 
 20 void print_board(){
 21     int i=0, j=0;
 22     for(i = 0; i<9; i++){
 23         for(j = 0; j<9; j++)
 24             printf(" %d",board[i][j].value);
 25         printf("\n");
 26     }
 27 }
 28 
 29 void initialize_board(){
 30     int i = 0, j = 0;
 31 
 32     for(i = 0; i<9; i++)
 33         for(j = 0; j<9; j++){
 34             (board[i][j]).value = 0;
 35             (board[i][j]).possible = 0x1FF;
 36         }
 37 }
 38 
 39 void setup_board(){
 40     int row=0, col=0, val = 0;
 41     char another = 'Y';
 42 
 43     printf("Board Initial Setup.\nEnter the row and column number of the value to be entered into the board.");
 44     printf("\nRow and Column indexes start at one, from top left corner.");
 45     while(another == 'Y'){
 46         printf("\nRow: ");
 47         row = getchar();
 48         printf("Column: ");
 49         getchar();
 50         col = getchar();
 51         printf("Value: ");
 52         getchar();
 53         (board[row-1][[col-1]).value = getchar();
 54         printf("Enter another value? (y/n): ");
 55         getchar();
 56         another = toupper(getchar());
 57         getchar();
 58     }
 59 }

Comme vous pouvez le voir, j'ai changé le type de données de la valeur en int pour correspondre au type de retour de getchar(). Mais mon code produit toujours des erreurs d'exécution/résultats étranges. Par exemple, dans la première itération de la boucle while dans setup_board, je peux entrer Row:1, Col:1, Value:5, puis lorsque j'entre 'n' pour quitter, le tableau devrait être imprimé avec 5 dans le coin supérieur gauche, mais ce n'est pas le cas. La matrice imprimée est toujours dans l'état où elle se trouvait après l'appel à initialize_board().

Sortie :

Board Initial Setup.
Enter the row and column number of the value to be entered into the board.
Row and Column indexes start at one, from top left corner.
Row: 1
Column: 1
Value: 4
Enter another value? (y/n): n
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0

De plus, si j'entre d'autres coordonnées de matrice, c'est là que j'obtiens l'erreur de bus : output :

Board Initial Setup.
Enter the row and column number of the value to be entered into the board.
Row and Column indexes start at one, from top left corner.
Row: 5
Column: 5
Value: 5
Bus error: 10

Tout conseil sur la façon de nettoyer cette affreuse affaire de double getchar() serait également apprécié.
Merci à tous !

DEUXIÈME MODIFICATION Le problème venait de ces getchar()'s.... Je n'avais pas réalisé qu'ils retournaient un code ASCII entier pour représenter les nombres au lieu des nombres eux-mêmes. Voici ce que j'ai fait pour résoudre ce problème :

 47     while(another == 'Y'){
 48         valid=0;
 49         while(!valid){
 50             printf("\nRow: ");
 51             row = getchar() - '0';  /* convert ASCII character code to actual integer value */
 52             getchar();              /*added to remove extra newline character from stdin */
 53             printf("Column: ");
 54             col = getchar() - '0';
 55             getchar();              /*remove \n */
 56             printf("Value: ");
 57             val = getchar() - '0';
 58             getchar();              /*remove \n */
 59             if(val >9 || val<1 || col>9 ||col<1 || row>9 || row<1)
 61                 printf("\nInvalid input, all values must be between 1 and 9, inclusive");
 62             else
 63                 valid = 1;  
 64         }
 65         board[row-1][col-1].value = val;
 66         printf("Enter another value? (y/n): ");
 67         another = toupper(getchar());
 68         getchar();                  /*remove \n */
 69     }

Merci à tous pour votre aide et vos commentaires, je vais mettre en œuvre un grand nombre des suggestions que vous avez faites et j'ai beaucoup appris rien qu'en répondant à cette question !

TROISIÈME MODIFICATION

UNE DERNIÈRE QUESTION !

Bien que mon problème initial soit résolu, quelqu'un a-t-il une opinion ou un raisonnement solide sur la question de savoir s'il serait préférable d'implémenter la matrice en allouant dynamiquement la mémoire ?

Je me dis que je vais laisser les choses en l'état puisque ça marche, mais comme la matrice est assez grande, est-ce que ce ne serait pas une meilleure pratique de programmation que d'allouer dynamiquement ?

2voto

gjcamann Points 170

getchar() renvoie un int . Il se peut que la valeur renvoyée dépasse la plage de char . Voir getchar()

2voto

Bruno Kim Points 608

Tout d'abord, quelques remarques sur les expressions idiomatiques. Il existe un idiome pour mallocer les tableaux en C :

Type *ptr;
ptr = malloc (n * sizeof(*ptr));

Les sizeof peut recevoir non seulement le type, mais aussi une variable de ce type. Notez l'astérisque devant ptr ce qui signifie que nous allouons quelque chose de la taille de Type , pas Type* . Il n'est pas nécessaire de lancer le retour, car un void* peut être assigné à n'importe quel pointeur. Ainsi, vous pouvez définir une macro pour allouer n'importe quel tableau comme suit :

#define ALLOC(p, n) p = malloc(n * sizeof(*p))

De même, lors de l'allocation de matrices bi- ou multidimensionnelles, il est habituel de récupérer toute la mémoire dont vous avez besoin en une seule fois, comme suit :

Type **matrix;
matrix = malloc(row * sizeof(*matrix));
matrix[0] = malloc(row * col * sizeof(*matrix[0]))
for (i=0; i < row; i++){
    matrix[i] = matrix[0] + i*col;
}

De cette façon, nous n'effectuons que deux allocations, l'une pour récupérer les pointeurs d'en-tête de ligne, et l'autre pour récupérer toute la mémoire nécessaire. Ensuite, nous faisons en sorte que tous les pointeurs d'en-tête de ligne pointent vers le bon endroit dans la matrice, de sorte que nous puissions utiliser les idiomes habituels tels que matrix[i][j] . Certains préfèrent également allouer un seul vecteur et y accéder à l'aide de la fonction matrix[i*col + j] mais je trouve le premier beaucoup plus clair.

Enfin, ce n'est pas une question réglée, mais je trouve plus facile de définir la structure comme un type, et de ne pas avoir à rappeler tout le temps qu'elle l'est. en effet a struct

typedef struct Cell Cell;
struct Cell{
    ...
};

Cell board[9][9];

Enfin, j'ai testé votre carte Cellule statique et je n'ai pas trouvé cette erreur de bus bizarre. Ce n'est pas le cas. peut être due au remplissage des caractères, mais je trouve cela peu probable. Est-ce que cela peut être dû au getchar ? Il prend les nouvelles lignes et les espaces.

2voto

anatolyg Points 8076

getchar renvoie un code qui représente un caractère ; vous devez ajouter du code qui le convertit en nombre. Considérons, par exemple, le code suivant :

printf("\nRow: ");
row = getchar();
printf("Column: ");
getchar();

Que se passe-t-il lorsque l'utilisateur tape "hehe" au lieu de 1 y 1 que le programme attend ? Le programme obtiendra les codes ASCII pour h y e , les assigner à row y col et d'accéder ainsi à des éléments de tableau hors plage.

En fait, la même chose se produit avec une entrée normale ! L'utilisateur saisit 1 le programme obtient le code ASCII ( 49 ) et effectue un dépassement de mémoire :

board[49-1][49-1].value = 53;

Pour y remédier, convertissez les codes de caractères en nombres :

if (row >= '1' && row <= '9')
    row -= '0'; // a common C hack for converting a character-code to a number
else
    printf("Bad input"); // not a proper error handling, just an example

col -= '0'; // 1 line for brevity; you must do the same code as above

value = getchar();
value -= '0'; // same as above

board[row-1][col-1].value = value;

1voto

Morpfh Points 2288

En dehors de la question, à laquelle je vois qu'il a été répondu, mais en ce qui concerne vos commentaires sur les malloc etc.

malloc pointeur de retour vers non initialisé l'espace nouvellement alloué pour un objet de taille taille .

Vous ne recevez pas un espace de par exemple int, mais un espace de taille N. C'est pourquoi vous pouvez prendre un char et lire les octets dans un int.

Voici maintenant quelques points subjectif et peut-être un peu hors SO, mais essayez.


Casting malloc est redondant et ne fait qu'encombrer le code. C'est comme si, au lieu de remplir une bouteille d'eau, on remplissait une bouteille d'eau pour remplir une bouteille d'eau. La mémoire allouée ne devenir ce vers quoi il est lancé. Il s'agit d'un morceau de mémoire. C'est un morceau de mémoire. D'une certaine manière, il s'agit d'un regard à l'envers.

Un int * pointe vers une partie spécifique de la mémoire virtuelle et traite cette mémoire sur la base de morceaux de la taille de l'int. En fonction de l'ordre des octets, ce sera le premier ou le dernier LSB d'une séquence de 4 octets sur un système spécifique ayant des int de 4 octets.


En ce qui concerne les macros, je ne recommande pas d'utiliser des macros pour des fonctions telles que malloc . Cela rend le code pénible à lire et les opérations sur les fichiers comme grep etc. deviennent inutiles/difficiles.

Lorsque l'on rencontre une macro, il faut vérifier ce qu'elle fait. On ajoute une couche supplémentaire de code sujet à l'erreur, etc. Lorsque vous lisez le code après 6 mois ou si quelqu'un d'autre que vous le lit, vous devez vérifier ce que fait réellement cette macro. Si vous lisez malloc vous savoir ce que cela fait, lorsque vous lisez MYMALLOC vous n'en aurez aucune idée.

Il s'agit d'éléments de base qu'il est préférable de conserver tels quels. Il n'y a rien à gagner si l'on tient compte de la douleur.

Il arrive que l'on rencontre des codes tels que :

BLAH(k, v);
while (--i)
     BOFF(mak, VED(uu));
if (FOO != k)
      BAR;

Maintenant, pour déchiffrer, il faut lire les macros, ce qui est souvent la définition de l'obscurité, et cela devient rapidement un désordre. Vous connaissez les primitives et les fonctions standard. Ne cachez pas ce qui se passe.

Et jamais modifier le flux de contrôle d'un programme à l'aide de macros.


En règle générale, je déteste les définitions de types. Je ne typifie jamais les structures.

Dire i.e. struct foo bar; vs BLAH bar; . Dans ce dernier cas, la barre peut être n'importe quoi. Une fonction, un char non signé, une structure, un pointeur sur une primitive, etc. Au fur et à mesure que le code se développe, cela devient une autre chose à suivre. Dans le premier cas, l'utilisation de struct est concise et courte et toute personne qui la lit sait qu'il s'agit d'une structure et non d'un pointeur.

En général, il contribue davantage à obscurcir le code qu'à le rendre plus clair. Ne cachez pas ce qui se passe.

Les typedefs peuvent être utiles, par exemple, pour les pointeurs de fonction, mais là encore, les pointeurs de fonction doivent être utilisés avec précaution.


Il en va de même pour #define . Ne l'utilisez que rarement.

0voto

George Points 307

Je ne sais pas pourquoi vous obtenez le Bus 10: il devrait fonctionner correctement avec le tableau statique.

Mais pour l'allocation dynamique de la mémoire pour le board que vous pourriez utiliser :

cell ** board = (cell **) malloc(x_size * sizeof(cell *));

for (int i = 0; i < Size_X; i++)
{
    board[i] = (cell *) malloc (y_size * sizeof (cell));
}

Vous pouvez également attribuer le board comme un simple tableau de 81 cell :

cell * board = (cell *) malloc (x_size * y_size (cell));

J'ai testé l'allocation statique sous Windows et cela fonctionne bien, tout comme j'ai testé ce qui précède avec malloc Mais mon compilateur est réglé sur C++, donc le code ci-dessus pourrait ne pas fonctionner à 100% avec un compilateur C pur.

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X