3

数独ソルバーを実装しようとしています。これを行うには、以下に示す構造体を使用して、数独ボードのセルを表します。次に、これらの構造体の 9x9 配列を宣言して、ボードを表します。

セル構造:

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

次に、structs の配列を次のように宣言します。

struct cell board[9][9];

私の問題は、配列に値を入力しようとすると (つまり、board[2][2].value = getchar();)、うまくいくこともあれば、次のエラーが発生することもあります。

Bus error: 10

これが何を意味するのかよくわかりません... "Bus error: 10" はセグメンテーション違反とどう違うのですか?

私はgccを使用していて、vimで編集しています。私の気持ちは、この配列にメモリを動的に割り当てる必要があるということです。これで、malloc を使用して 2 次元配列にメモリを割り当てる方法がわかりました。次のようになります。

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

しかし、構造体の 2 次元配列のメモリ割り当て部分を実装するのに問題があります。

こんなものだろうか。

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 をかなり使用しましたが、言語全体をよりよく理解しようとしています。
詳細\詳細な説明のボーナス ポイント!

ありがとう!

編集 OK、素晴らしい提案をしてくれたみんなに感謝します。私はまだ動的メモリ割り当てを実装していませんが、要求に応じて、バスエラーを生成しているコードは次のとおりです。

 /* 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 }

ご覧のとおり、getchar() の戻り値の型と一致するように、値のデータ型を int に変更しました。しかし、私のコードは依然として奇妙な実行時エラー/結果を生成しています。たとえば、setup_board の while ループの最初の反復で、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

また、他のマトリックス座標を入力すると、バスエラーが発生します: 出力:

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

その醜い double getchar() ビジネスをクリーンアップする方法についてのアドバイスもいただければ幸いです。
みんな、ありがとう!

EDIT NUMBER 2 問題は、これらの 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     }

皆さんのご協力とご意見に感謝します。皆さんが行った提案の多くを実装し、この質問から多くのことを学びました!

編集番号 3

最後に 1 つの質問です。

私の最初の問題は解決されましたが、メモリを動的に割り当てることによってマトリックスを実装する方が良いかどうかについて、強い意見や推論を持っている人はいますか?

動作するのでそのままにしておきますが、マトリックスがかなり大きいので、動的に割り当てる方がプログラミングの練習に適しているでしょうか?

4

5 に答える 5

2

getchar文字を表すコードを返します。それを数値に変換するコードを追加する必要があります。たとえば、次のコードを考えてみましょう。

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

ユーザーが代わりに「hehe」1と入力1し、プログラムが期待しているとどうなりますか? プログラムは および の ASCII コードを取得し、それらを およびhe割り当て、範囲外の配列要素にアクセスします。rowcol

実際、通常の入力でも同じことが起こります! ユーザーがを入力する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 に答える
2

getchar()を返しますint。返される値が の範囲を超えている可能性がありcharます。 getchar() を参照してください

于 2012-05-08T17:54:55.033 に答える
2

まず、慣用句に関するいくつかの注意事項。C で配列を malloc するイディオムがあります。

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

sizeof演算子は型だけでなく、その型の変数も受け取ることができます。の前のアスタリスクに注意してください。これは、 ではなくptrのサイズで何かを割り当てていることを意味します。ポインターは任意のポインターに割り当てることができるため、戻り値をキャストする必要はありません。そうすれば、次のように任意の配列を割り当てるマクロを定義できます。TypeType*void*

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

また、2 次元または多次元の行列を割り当てる場合、次のように、必要なすべてのメモリを一度にフェッチするのが通常です。

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

1 つは行ヘッダー ポインターをフェッチするため、もう 1 つは必要なすべてのメモリをフェッチするためです。その後、すべての行ヘッダー ポインターがマトリックス内の正しい場所を指すようにするため、通常のイディオムを として使用できます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 に答える
1

Q以外にも、回答されているようですが、mallocその他のコメントについて。

mallocサイズsizeのオブジェクトに新しく割り当てられた、初期化されていない領域のポインタを返します。

たとえば int のスペースを受け取るのではなく、サイズ N のスペースを受け取ります。これが、char を取得して int のバイトを読み取ることができる理由です。

いくつかの点は主観的で、おそらく少しずれていますが、試してみてください。


キャスティングmallocは冗長で、コードが煩雑になるだけです。ボトルに水を入れるのではなく、ボトルに水を入れてボトルに水を入れるようなものです。割り当てられたメモリは、キャストされたものにはなりません。記憶の塊です。限目。ある意味、後ろ向きです。

int * は仮想メモリの特定の部分を指し、そのメモリを int のサイズのチャンクに基づいて扱います。4 バイトの int を持つ特定のシステムで、4 バイト シーケンスの LSB が最初または最後になるバイト順によって異なります。


マクロに関しては、 のような関数にマクロを使用することはお勧めしませんmalloc。コードを読むのが面倒になり、grep などのファイル操作が役に立たなくなったり、難しくなります。

マクロに出くわしたら、そのマクロが何をするかをチェックする必要があります。エラーが発生しやすいコードなどのレイヤーをもう1つ追加します。6か月後または他の誰かがコードを読んだ場合、そのマクロが実際に何をするかを確認する必要があります。読めmallocば、それが何をするかはわかりますが、読んだときには何もわかりMYMALLOCません。

そのままにしておくのが最適な基本的な構成要素です。痛みを考慮した利益はありません。

次のようなコードに出くわすことがあります。

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

さて、解読するにはマクロを読まなければなりませんが、これはしばしばあいまいさの定義であり、すぐに混乱してしまいます。プリミティブと標準関数を知っています。起こっていることを隠さないでください。

また、マクロを使用してプログラムの制御フローを決して変更しないでください。


typedefに関しては、私は一般的に嫌いです。私は決して構造体を型定義しません。

ie struct foo bar;vs と言ってBLAH bar;ください。後者のバーでは何でもかまいません。関数、unsigned char、構造体、プリミティブへのポインターなど。コードが大きくなるにつれて、これは追跡する別のものになります。前者の usingstructは簡潔で短く、それを読んだ人なら誰でもそれが構造体であり、ポインターではないことを知っています。

全体として、コードを難読化するよりも明確にするために多くのことを行うことがよくあります。起こっていることを隠さないでください。

Typedef は関数ポインタなどに役立ちますが、関数ポインタも注意して使用する必要があります。


についても同様です#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));
}

boardを 81 の単純な配列として割り当てることもできますcell

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

Windows で静的割り当てをテストしましたが、上記を でテストしたように正常に動作しますmallocが、私のコンパイラは C++ に設定されているため、上記のコードは純粋な C コンパイラで 100% 動作しない可能性があります。

それが役に立てば幸い。

于 2012-05-08T18:08:33.127 に答える