Как использовать malloc для двумерного массива структур? (ошибка шины: 10)
Я пытаюсь реализовать решение судоку. Для этого я использую структуру, показанную ниже, для представления ячейки на доске судоку. Затем я объявляю массив этих структур размером 9x9, чтобы представить доску.
структура клетки:
struct cell{
char value;
unsigned int possible;
};
затем я объявляю массив sctructs как:
struct cell board[9][9];
Моя проблема в том, что, когда я пытаюсь ввести значение в массив (т.е. board[2][2].value = getchar();), иногда это работает, а в других случаях я получаю эту ошибку:
Bus error: 10
Я не совсем уверен, что это значит... Чем "Ошибка шины: 10" отличается от ошибки сегментации?
Я использую gcc и просто редактирую в vim. По моим ощущениям, мне нужно динамически распределять память для этого массива. Теперь я понимаю, как использовать malloc для выделения памяти для двумерного массива, что-то вроде этого:
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));
Но у меня возникают проблемы с реализацией части выделения памяти для двумерного массива структур.
Будет ли что-то вроде этого?
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));
Я опасаюсь, что этот " sizeof(struct cell) " неправильно распределяет объем памяти, который должен быть.
Любая помощь будет принята с благодарностью! Я довольно новичок в C (C++ - мой родной язык), я много использовал встроенный C, но я пытаюсь лучше понять язык в целом.
Бонусные баллы за подробные \ подробные объяснения!
Спасибо!
РЕДАКТИРОВАТЬ ОК, так что спасибо всем за отличные предложения, я до сих пор не реализовал динамическое распределение памяти, но, как было запрошено, вот код, который вызывает ошибку шины:
/* 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 }
Как видите, я изменил тип данных value на int, чтобы он соответствовал типу возвращаемого значения getchar (). Но мой код все еще выдает странные ошибки / результаты во время выполнения. Например, на первой итерации цикла while в setup_board я могу ввести, скажем, Row:1, Col:1, Value:5, затем, когда я введу 'n' для выхода, доска должна быть напечатана с 5 в верхнем левый угол, однако это не так. Печатная матрица все еще находится в своем состоянии после вызова initialize_board().
Выход:
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
Кроме того, если я введу другие координаты матрицы, то получу ошибку шины: 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
Любой совет о том, как убрать этот уродливый двойной getchar () бизнес будет также оценен.
Спасибо всем!
РЕДАКТИРОВАТЬ НОМЕР ВТОРОЙ Проблема была с этими getchar ().... Я не знал, что они возвращают целочисленный код ASCII, чтобы представлять числа вместо самих фактических чисел. Вот что я сделал, чтобы это исправить:
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 }
Спасибо всем за вашу помощь и вклад, я буду реализовывать многие предложения, которые вы все сделали и многому научились только из этого вопроса!
РЕДАКТИРОВАТЬ НОМЕР ТРИ
ОДИН ЗАКЛЮЧИТЕЛЬНЫЙ ВОПРОС!
Хотя моя первоначальная проблема решена, есть ли у кого-нибудь твердые мнения или рассуждения о том, будет ли лучше реализовать матрицу путем динамического распределения памяти?
Я полагаю, я оставлю это как есть сейчас, так как он работает, но так как матрица довольно большая, лучше ли будет программировать динамическое распределение?
5 ответов
getchar
возвращает код, который представляет символ; Вы должны добавить код, который преобразует его в число. Рассмотрим, например, следующий код:
printf("\nRow: ");
row = getchar();
printf("Column: ");
getchar();
Что происходит, когда пользователь печатает "хе-хе" вместо 1
а также 1
что ожидает программа? Программа получит коды ASCII для h
а также e
назначьте их row
а также col
и, таким образом, получить доступ к элементам массива вне диапазона.
На самом деле, то же самое происходит с обычным вводом! Пользователь вводит 1
программа получает код ASCII (49
) и выполняет некоторое переполнение памяти:
board[49-1][49-1].value = 53;
Чтобы исправить это, преобразуйте коды символов в числа:
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;
Сначала несколько замечаний по идиомам. Есть идиома для массивов malloc в C:
Type *ptr;
ptr = malloc (n * sizeof(*ptr));
sizeof
Оператор может получить не только тип, но и переменную с этим типом. Обратите внимание на звездочку перед ptr
, что означает, что мы выделяем что-то с размером Type
не Type*
, Нет необходимости разыгрывать возврат, как void*
Указатель может быть назначен любому указателю. Таким образом, вы можете определить макрос для размещения любого массива следующим образом:
#define ALLOC(p, n) p = malloc(n * sizeof(*p))
Кроме того, при распределении двумерных или многомерных матриц обычно выбирают всю необходимую вам память сразу, как показано ниже:
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;
}
Таким образом, мы делаем только два выделения: одно для извлечения указателей заголовка строки, а другое для извлечения всей необходимой памяти. После этого мы указываем все указатели заголовка строки на правильное место в матрице, поэтому мы можем использовать обычные идиомы как matrix[i][j]
, Некоторым также нравится выделять один вектор и matrix[i*col + j]
Но я нахожу первое гораздо более понятным.
Наконец, это не решенный вопрос, но я считаю, что проще определить структуру как тип, и тогда не нужно постоянно напоминать, что это действительно структура
typedef struct Cell Cell;
struct Cell{
...
};
Cell board[9][9];
Наконец, я проверил вашу статическую плату Cell и не обнаружил странную ошибку шины. Это может быть связано с заполнением символов, но я нахожу это маловероятным. Может ли это быть что-то из-за getchar? Он будет выбирать новые строки и пробелы.
getchar()
возвращает int
, Возможно, возвращаемое значение превышает диапазон char
, Смотрите getchar()
Кроме вопроса, как я вижу, на него уже дан ответ, но относительно ваших комментариев malloc
и т.п.
malloc
вернуть указатель на неинициализированное вновь выделенное пространство для объекта размером с размер.
Вы не получаете пробел, например, int, но пробел размера N. Вот почему вы можете взять символ и прочитать байты в int.
Теперь в некоторых пунктах, которые являются субъективными и, возможно, немного не так, но давайте попробуем.
Кастинг malloc
является избыточным и только загромождает код. Это похоже на то, что вместо того, чтобы наполнять бутылку водой, вы наполняете бутылку водой, чтобы наполнить бутылку водой. Выделенная память не становится тем, к чему она приведена. Это кусок памяти. Период. В некотором смысле это смотрит назад.
Int * указывает на определенную часть виртуальной памяти и обрабатывает эту память на основе кусков по размеру int. В зависимости от порядка байтов, который будет первым или последним LSB в 4-байтовой последовательности в конкретной системе, имеющей 4-байтовые целые числа.
Когда дело доходит до макросов, я бы не рекомендовал использовать макросы для таких функций, как malloc
, Это делает код трудным для чтения, а файловые операции, такие как grep и т. Д., Становятся бесполезными / более сложными.
Когда кто-то сталкивается с макросом, он должен проверить, что делает этот макрос. Один добавляет еще один слой кода, подверженного ошибкам и т. Д. Если вы через 6 месяцев или кто-то другой, то читаете код, вам придется проверить, что на самом деле делает этот макрос. Если вы читаете malloc
Вы знаете, что это делает, когда вы читаете MYMALLOC
ты понятия не имеешь
Это основные строительные блоки, которые лучше всего хранить как есть. Безуспешно, учитывая боль.
Иногда можно встретить код вроде:
BLAH(k, v);
while (--i)
BOFF(mak, VED(uu));
if (FOO != k)
BAR;
Теперь, чтобы расшифровать, вам придется читать макросы, которые часто являются определением мрака, и это быстро превращается в беспорядок. Вы знаете примитивы и стандартные функции. Не скрывай, что происходит.
И никогда не меняйте поток управления программой с помощью макросов.
Когда дело доходит до typedefs, я вообще их ненавижу. Я никогда не печатаю определение структуры.
Скажи то есть struct foo bar;
против BLAH bar;
, Теперь в последнем баре может быть что угодно. Функция, неподписанный символ, структура, указатель на примитив и т. Д. По мере роста кода это становится другой вещью, которую нужно отслеживать. Бывший, используя struct
является кратким и кратким, и любой, кто читает его, знает, что это структура, а не указатель.
В целом это часто делает больше, чтобы запутать код, а затем сделать его более понятным. Не скрывай, что происходит.
Typedefs могут быть полезны, например, для указателей на функции, но с другой стороны, указатели на функции также следует использовать с осторожностью.
То же самое касается #define
, Используйте это едва ли.
Я не знаю, почему вы получаете Bus 10:
ошибка, она должна нормально работать со статическим массивом.
Но для динамического распределения памяти для board
Вы могли бы использовать:
cell ** board = (cell **) malloc(x_size * sizeof(cell *));
for (int i = 0; i < Size_X; i++)
{
board[i] = (cell *) malloc (y_size * sizeof (cell));
}
Вы также можете выделить board
как простой массив из 81 cell
:
cell * board = (cell *) malloc (x_size * y_size (cell));
Я проверил статическое распределение под Windows, и он работает нормально, как я тестировал выше с malloc
, но мой компилятор установлен на C++, поэтому приведенный выше код может не работать на 100% с чистым компилятором C.
Надеюсь, поможет.