質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

Q&A

1回答

640閲覧

迷路探索のプログラムでのSegmentation fault

Tsuruha

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

0グッド

0クリップ

投稿2023/05/19 09:31

編集2023/05/20 01:46

実現したいこと

  • 大きいサイズの迷路でもSegmentation faultが出ずに探索できるようにする。

前提

C言語でスタックを用いて迷路探索のプログラムを書こうとしています。
サイズの小さい迷路では問題なく動作することが確認できています。
また分岐に対応していない(1本道の迷路のみに使える)プログラムでは問題なく動作しています。
最初に迷路のサイズの上限を1024×1024と設定したのですが、それよりも小さい迷路であってもSegmentation faultが出てしまいます。入力となる迷路データは別にtxt形式で保存し実行の際に読み込ませています。メインのソースコードのファイル名がq3-5.cであり読み込ませる迷路データのファイル名がq3-mazeex5.txtとしています。

発生している問題・エラーメッセージ

$ gcc -std=c99 -o q3-5 q3-5.c $ ./q3-5 < q3-mazeex5.txt $ Segmentation fault

該当のソースコード

c

1#include<stdio.h> 2#include<stdlib.h> 3#include<string.h> 4 5#define MAX_MAZE_SIZE 1024 6#define NUMBER_OF_DIRECTIONS 4 // left, right, up, down 7 8enum { FORWARD, BACKWARD }; 9typedef int bool; 10typedef struct { int i, j; } position; 11typedef struct { 12 int size_row, size_col; 13 char map [ MAX_MAZE_SIZE ][ MAX_MAZE_SIZE ]; 14 position start, goal; 15} maze; 16 17/////////////////////////////////////////////////////////////////////////////// 18// Stack definition 19/////////////////////////////////////////////////////////////////////////////// 20#define STACKSIZE MAX_MAZE_SIZE 21typedef position elementtype; 22 23typedef struct { 24 int top; 25 elementtype elements[STACKSIZE]; 26} stack; 27 28void initstack(stack *s) 29{ 30 s->top = -1; 31} 32 33int stackempty(stack *s) 34{ 35 return (s->top < 0); 36} 37 38void printstack(stack *s) 39{ 40 printf("["); 41 for(int i = 0; i <= s->top; i++){ 42 printf("(%d,%d)", s->elements[i].i, s->elements[i].j); 43 if (i < s->top) { 44 printf(", "); 45 } 46 } 47 printf("]\n"); 48} 49 50void push(stack *s, elementtype x, char c) 51{ 52 s->top += 1; 53 if (s->top >= STACKSIZE) { 54 printf("Stack overflow.\n"); exit(1); 55 } else { 56 s->elements[s->top] = x; 57 } 58 printf("%c push (%d,%d): ", c, x.i, x.j); 59 printstack(s); 60 return; 61} 62 63elementtype pop(stack *s, char c) 64{ 65 elementtype x; 66 if (s->top < 0){ 67 printf("%c Stack is empty.\n", c); exit(1); 68 } else { 69 x = s->elements[s->top]; 70 s->top -= 1; 71 printf("%c pop: (%d,%d), ", c, x.i, x.j); 72 printstack(s); 73 return x; 74 } 75} 76/////////////////////////////////////////////////////////////////////////////// 77 78position possible_moves [ 4 ] = { 79 { .i = 0, .j = -1 }, 80 { .i = 0, .j = +1 }, 81 { .i = -1, .j = 0 }, 82 { .i = +1, .j = 0 } }; 83position move ( position p, position d ) { position q = { .i = p.i + d.i, .j = p.j + d.j }; return q; } 84bool equal ( position p, position q ) { return ( p.i == q.i && p.j == q.j ) ? 1 : 0; } 85 86void read_maze ( maze *m ) 87{ 88 char str [ MAX_MAZE_SIZE + 2 ]; // +2 = '\n' + '\0' 89 position p = { .i = 0, .j = 0 }; 90 91 while ( fgets ( str, MAX_MAZE_SIZE + 2, stdin ) ) { 92 for (p.j = 0; str [ p.j ] != '\n'; p.j++ ) { 93 m->map [ p.i ][ p.j ] = str [ p.j ]; 94 if ( str [ p.j ] == 'S' ) { 95 m->start = p; 96 } 97 if ( str [ p.j ] == 'G' ) { 98 m->goal = p; 99 } 100 } 101 p.i += 1; 102 } 103 m->size_row = p.i; 104 m->size_col = p.j; 105} 106 107void print_maze ( maze m ) 108{ 109 position a; 110 111 printf(" "); 112 for ( int j = 0; j < m.size_col; j++ ) { 113 printf("%d", j%10); 114 } 115 printf("\n"); 116 117 for ( a.i = 0; a.i < m.size_row; a.i++ ) { 118 printf("%d", a.i%10); 119 for ( a.j = 0; a.j < m.size_col; a.j++ ) { 120 printf ( "%c", m.map [ a.i ][ a.j ] ); 121 } 122 printf ( "\n" ); 123 } 124} 125 126bool is_in ( maze *m, position p ) { return ( 0 <= p.i && p.i <= m->size_row - 1 && 0 <= p.j && p.j <= m->size_col - 1 ) ? 1 : 0; } 127bool is_goal ( maze *m, position p ) { return ( m->map [ p.i ][ p.j ] == 'G' ) ? 1 : 0; } 128bool is_wall ( maze *m, position p ) { return ( m->map [ p.i ][ p.j ] == '#' ) ? 1 : 0; } 129int next_possible_move ( maze *m, position cur, position prev, position candidate [] ) 130{ 131 int n = 0; 132 for ( int i = 0; i < NUMBER_OF_DIRECTIONS; i++ ) { 133 position next = move ( cur, possible_moves [ i ] ); 134 if ( is_in ( m, next ) && ! is_wall ( m, next ) && ! equal ( next, prev ) ) { candidate[n] = next; n += 1; } 135 } 136 return n; 137} 138 139void solve ( maze *m ) 140{ 141 position start = m->start; 142 position goal = m->goal; 143 stack route, branch; 144 position cur, next, prev; 145 position candidate [ NUMBER_OF_DIRECTIONS ]; // left, right, up, down; 146 bool direction = FORWARD; 147 148 initstack ( &route ); 149 initstack ( &branch ); 150 151 prev = start; 152 cur = start; 153 int n = next_possible_move ( m, cur, prev, candidate ); // 次の可能な移動を検索 154 push ( &route, cur, 'r' ); 155 cur = candidate [ 0 ]; 156 157 printf("START\n"); 158 while(! is_goal ( m, cur ) ) { // ゴールに到達するまで繰り返し 159 printf("CUR: (%d,%d)\n", cur.i, cur.j); 160 n = next_possible_move ( m, cur, prev, candidate ); // 次の可能な移動を検索 161 prev = cur; 162 switch ( n ) { 163 164case 0: // 行き止まり 165 if (stackempty(&branch)) 166 { 167 printf("No solution found.\n"); 168 return; 169 } 170 direction = BACKWARD; 171 cur = pop(&route, 'r'); // 逆戻り 172 printf("%s\n", (direction == FORWARD) ? "FORWARD" : "BACKWARD"); 173 break; 174 175 case 1: // 一本道 176 if (direction == FORWARD) 177 { 178 push(&route, cur, 'r'); 179 cur = candidate[0]; 180 } 181 else 182 { 183 cur = pop(&route, 'r'); 184 } 185 printf("%s\n", (direction == FORWARD) ? "FORWARD" : "BACKWARD"); 186 break; 187 188 default: // 分岐 189 printf("BRANCH\n"); 190 if(direction == FORWARD){ 191 push(&route, cur, 'r'); 192 for (int i = 0; i < n; i++){ 193 next = candidate[i]; 194 if(!is_wall(m, next)){ 195 push(&branch, next, 'b'); 196 } 197 } 198 cur = pop(&branch, 'b'); 199 } 200 else{ 201 cur = pop(&branch, 'b'); 202 direction = FORWARD; 203 } 204 205 printf("%s\n", (direction == FORWARD) ? "FORWARD" : "BACKWARD"); 206 break; 207 } 208 } 209 210 211 // ゴールに到達した 212 // 行儀悪いけど、構造体の内容を書き換えて経路を追加 213 printf("DONE\n"); 214 while ( 1 ) { 215 cur = pop ( &route, 'r' ); 216 if ( equal ( cur, start ) ) { break; } 217 m->map [ cur.i ][ cur.j ] = '.'; 218 } 219 220} 221 222int main(void) 223{ 224 maze m; 225 226 read_maze ( &m ); 227 228 solve ( &m ); 229 230 print_maze ( m ); 231 232 return 0; 233}

txt

1############################# 2##############S############## 3############## ############## 4####### ###### ###### ####### 5###### ###### 6####### ###### ###### ####### 7############## ############## 8####### ###### ###### ####### 9#### ### ### #### 10####### ###### ###### ####### 11####### ###### ###### ####### 12#### ### ### #### 13####### ###### ###### ####### 14####### ###### ###### ####### 15####### ###### ###### ####### 16# # 17####### ###### ###### ####### 18####### ###### ###### ####### 19####### ###### ###### ####### 20#### ### ### G #### 21####### ###### ###### ####### 22####### ###### ###### ####### 23#### ### ### #### 24####### ###### ###### ####### 25############## ############## 26####### ###### ###### ####### 27###### ###### 28####### ###### ###### ####### 29#############################

試したこと

gdbを用いてデバッグしたところ、上のソースコードの93行目に問題があると表示されました。
しかしこの部分のどこに問題があるのかわかりません。

補足情報(FW/ツールのバージョンなど)

gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2023/05/19 09:44

> 上のソースコードの93行目に問題がある クラッシュしたときに p.i と p.j の値はどうなってましたか?
Tsuruha

2023/05/19 10:43

(gdb) p p.i $1 = 28 (gdb) p p.j $2 = 1026248 のように表示されました。当方gdbに慣れておらず質問に答えられているかわかりませんがよろしくお願いいたします。
int32_t

2023/05/19 12:05

p.j の値がおかしいように見えますね。 実行するときに標準入力にどんなデータを与えていますか。
jimbe

2023/05/19 16:35 編集

データもご提示ください。 93行目ということは迷路データの取り込みで失敗しているので迷路を探索するに至っていないということでしょう。 問題が起きる時の p.i が 28 なのだとしたら、迷路データの 29行(?)目に問題があると見るべきでしょう。
Tsuruha

2023/05/20 01:48

申し訳ありません。入力を提示していませんでした。入力も併せて確認できるように更新いたしましたのでよろしくお願いいたします。
jimbe

2023/05/20 02:56 編集

入力の29行目の後に改行は入っているでしょうか。 fgets はストリームが EOF になった場合も戻ります。データの最後に改行が入っていなければ当然配列にも改行は入らず、93行目の書き方ではループが終わりません。 行毎の処理であれば改行コードでデータの終わりを判定するのではなく、基本は ヌル文字('\0')が現れるまで、もしくは strlen 関数でバイト数を得てその数だけ処理するようにし、有ったり無かったりする改行はそのような処理する前に「有ったら削除しておく」ようにするのが妥当ではないでしょうか。
Tsuruha

2023/05/20 08:17

改行は入っていません。改行を入れて実行したところ、スタックがオーバーフローしエラーが出てしまいました。実行例として改行がない場合にも正しく動作するためにはどのようにすれば良いのでしょうか?
guest

回答1

0

c

1 while ( fgets ( str, MAX_MAZE_SIZE + 2, stdin ) ) { 2 for (p.j = 0; str [ p.j ] != '\n'; p.j++ ) {

ファイルの最後の行が \n で終わっていない場合、このコードは読み込んだデータの終端を通り越してクラッシュします。
\0 もチェックするとか、fgets() で読み込んだ直後に最後の \n\0 に書き換えて \0 の存在をチェックするのが常套手段です。

c

1 while (fgets(str, MAX_MAZE_SIZE + 2, stdin)) { 2 for (p.j = 0; str[p.j] != '\n' && str[p.j]; p.j++) {

c

1 while (fgets(str, MAX_MAZE_SIZE + 2, stdin)) { 2 size_t len = strlen(str); 3 if (str[len - 1] == '\n') 4 str[len - 1] = '\0'; 5 for (p.j = 0; str[p.j]; p.j++) {

改行を入れて実行したところ、スタックがオーバーフローしエラーが出てしまいました。

それは当初の質問とは別問題なので、別の質問を立てたほうがよいでしょう。

投稿2023/05/22 03:43

int32_t

総合スコア21695

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問