3

我正在尝试实现数独求解器。为此,我使用如下所示的结构来表示数独板上的单元格。然后我声明这些结构的 9x9 数组来表示板。

细胞结构:

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

然后,我将一个结构数组声明为:

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() 返回类型。但是我的代码仍然会产生奇怪的运行时错误/结果。例如,在 setup_board 的 while 循环的第一次迭代中,我可以输入 say Row:1, Col:1, Value:5,然后当我输入 'n' 退出时,board 应该在上面打印 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

另外,如果我输入其他矩阵坐标,那就是当我得到总线错误:输出:

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     }

感谢大家的帮助和投入,我将实施你们提出的许多建议,并从这个问题中学到了很多东西!

编辑三号

最后一个问题!

虽然我最初的问题已经解决,但是否有人对通过动态分配内存来实现矩阵是否更好有强烈的意见或推理?

我想,我会保持原样,因为它可以工作,但由于矩阵相当大,动态分配会更好的编程实践吗?

4

5 回答 5

2

getchar()返回一个int. 可能返回的值超出了 的范围char见 getchar()

于 2012-05-08T17:54:55.033 回答
2

首先,关于成语的一些注释。在 C 中有一个 malloc 数组的习惯用法:

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;
}

这样我们只做两次分配,一次获取行头指针,另一次获取所有需要的内存。之后,我们使所有行头指针指向矩阵中的正确位置,因此我们可以使用常用的成语 as matrix[i][j]。有些人还喜欢分配一个向量并使用 访问matrix[i*col + j],但我发现第一个更清楚。

最后,这不是一个解决的问题,但我发现将结构定义为类型更容易,然后不需要一直提醒它确实是一个结构

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

Cell board[9][9];

最后,我测试了你的静态单元板,并没有发现奇怪的总线错误。这可能是由于 char 填充,但我觉得不太可能。可能是由于getchar引起的吗?它将选择换行符和空格。

于 2012-05-08T17:57:42.883 回答
2

getchar返回一个代表一个字符的代码;您必须添加将其转换为数字的代码。例如,考虑以下代码:

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

当用户键入“hehe”而不是1程序1期望的时候会发生什么?该程序将获取hand的 ASCII 代码e,将它们分配给rowandcol并访问超出范围的数组元素。

实际上,正常输入也会发生同样的情况!用户输入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;
于 2012-05-08T20:36:21.160 回答
1

在 Q 旁边,据我所知,它已经得到了回答,但是关于您对malloc等的评论。

malloc为大小为size的对象返回指向未初始化的新分配空间的指针。

您不会收到例如 int 的空间,而是大小为 N 的空间。这就是您可以获取 char 并读取 int 中的字节的原因。

现在在某些方面是主观的,也许有点偏离,但试一试。


强制转换malloc是多余的,只会使代码混乱。这就像不是给一个瓶子装水,而是用一个瓶子装满水来装满一瓶水。分配的内存不会变成它被强制转换的内存。这是一块内存。时期。在某种程度上,它正在向后看。

int * 指向虚拟内存的特定部分,并根据 int 大小的块来处理该内存。根据具有 4 字节整数的特定系统上的 4 字节序列中的 LSB 优先或最后的字节顺序。


当谈到宏时,我不建议将宏用于malloc. 它使代码难以阅读,并且 grep 等文件操作变得无用/更难。

当遇到宏时,必须检查该宏的作用。一个添加一层容易出错的代码等。当您在 6 个月后或其他人阅读代码时,您将不得不检查该宏的实际作用。如果你读了malloc你就知道那是做什么的,当你读的时候你MYMALLOC就不知道了。

它是最好保持原样的基本构建块。没有收获考虑痛苦。

有时会遇到如下代码:

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

现在,要破译你必须阅读通常是晦涩定义的宏,它很快就会变得一团糟。你知道原语和标准函数。不要隐藏正在发生的事情。

永远不要使用宏更改程序的控制流程。


当谈到 typedefs 时,我通常讨厌它们。我从来没有 typedef 结构的。

说 iestruct foo bar;BLAH bar;. 现在在后者酒吧可以是任何东西。一个函数、一个无符号字符、一个结构、一个指向原语的指针等。随着代码的增长,这成为另一个需要跟踪的东西。前者,usingstruct简洁而简短,任何阅读它的人都知道它是一个结构,而不是一个指针。

总的来说,它通常会更多地混淆代码,然后使其更清晰。不要隐藏正在发生的事情。

Typedefs 对函数指针很有用,但是函数指针也应该小心使用。


也一样#define。很少使用它。

于 2012-05-08T22:24:33.063 回答
0

我不知道您为什么会收到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));
}

您还可以将board81 分配为一个简单的数组cell

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

我已经在 Windows 下测试了静态分配,它工作正常,就像我用 测试上面一样malloc,但是我的编译器设置为 C++,所以上面的代码可能不是 100% 使用纯 C 编译器。

希望能帮助到你。

于 2012-05-08T18:08:33.127 回答