ページ 11

自作関数のセグメントエラー

Posted: 2016年4月28日(木) 16:57
by べあ
(投稿に失敗したみたいなので再投稿します)
自作のcount関数でセグメントエラーが起こっているようです。
このコードは30*30のマスに0か1の値が割り振られていて、1のマスの座標情報を書き出し、
ルールにのっとってマスの値を変えるというのを150回繰り返すものです。

count関数で周りのマスの値が1のものをカウントし、その個数によってあるマスの次のターンの値が決まります。
if文で存在しない配列にアクセスしないように気を付けているのですが、不十分でしょうか?
ご教授お願いいたします。

コード:

#include<stdio.h>

#define xgrid 30
#define ygrid 30

int count(int data[xgrid][ygrid]) {
  int x, y, check;
  check = 0;

  if (x > 0 && y < (ygrid - 1)) {
    if (data[x - 1][y + 1] == 1) {
      check += 1;
    }
  }
  if (x > 0 && y > 0) {
    if (data[x - 1][y - 1] == 1) {
      check += 1;
    }
  }
  if (x > 0) {
    if (data[x - 1][y] == 1) {
      check += 1;
    }
  }
  if (y > 0 && x < (xgrid - 1)) {
    if (data[x + 1][y - 1] == 1) {
      check += 1;
    }
  }
  if (y < (ygrid - 1) && x < (xgrid - 1)) {
    if (data[x + 1][y + 1] == 1) {
      check += 1;
    }
  }
  if (x < (xgrid - 1)) {
    if (data[x + 1][y] == 1) {
      check += 1;
    }
  }
  if (y < (ygrid - 1)) {
    if (data[x][y + 1] == 1) {
      check += 1;
    }
  }
  if (y > 0) {
    if (data[x][y - 1] == 1) {
      check += 1;
    }
  }
  return check;
}

int main(void) {
  int data[xgrid][ygrid];
  int i, x, y, check;

  for(x = 0; x < xgrid; x++) {
    for(y = 0; y < ygrid; y++) {
      data[x][y] = 0;
    }
  }

  data[11][8] = data[15][8] = data[16][8] = data[17][8]
	            = data[10][7] = data[11][7] = data[16][6] = 1;

  for(i = 0; i < 151; i++) {
    for(x = 0; x < xgrid; x++) {
      for(y = 0; y < ygrid; y++) {
        if (data[x][y] == 0) {
          check = count(data);
          if (check == 3) {
            data[x][y] = 1;
          } else {
          printf("%d %d\n", x + 1, y + 1);
          check = count(data);
          if (check < 2 || check > 3) {
            data[x][y] = 0;
          }
          }
        }
      }
    }     
    printf("-1 -1\n\n\n");
    }
  return 0;
}


Re: 自作関数のセグメントエラー

Posted: 2016年4月28日(木) 18:12
by can110
count関数内で宣言された未初期化の変数x、yを参照しているのが原因だと思われます。
そもそもcount関数には「どのマスを基準にして」周囲を探すのか引数で指定すべきでは?

ちなみに、以下のように範囲内か判定する関数を用いれば、簡潔に記述できます。

コード:

// 範囲内か
int isRange( int x, int y)
{
	if( (x >= 0) && (x < xgrid) && (y >= 0) && (y < ygrid) ){
		return 1;
	}
	//	printf( "x=%d, y=%d is out of range.\n", x, y);
	return 0;
}

// トーラス補正
void adjustTorus( int *px, int *py)
{
	if(		*px < 0)		*px = xgird-1;
	else if(*px >= xgrid)	*px = 0;
	if(		*py < 0)		*py = ygird-1;
	else if(*py >= ygrid)	*py = 0;
}

int count( int x, int y, int data[][ygrid]) {
	int check = 0;
	// 上下左右1マスずつ走査
	for( xx = x-1; xx <= x+1; xx++){
		for( yy = y-1; yy <= y+1; yy++)
			if( xx == x && yy == y) continue;	// 自身は除く
		//	adjustTorus( &xx, &yy);		// トーラス補正
			if( !isRange( xx, yy)) continue;	// 範囲外
			if( !data[xx][yy]) continue;		// いない
			check++;
		}
	}
	return check;
}
オフトピック
ライフゲームかな?

Re: 自作関数のセグメントエラー

Posted: 2016年4月28日(木) 18:59
by みけCAT
C言語の場合、count関数内で未初期化の自動変数x, yの値は不定であり、その値を使うとundefined behaviorになります。
undefined behaviorになると、何が起こっても文句は言えません。

C++の場合、count関数内の変数x, yは宣言されたブロックのみで利用できる変数であり、staticもexternもついていないので、
automatic storage durationを持ちます。(N3337 3.7.3)
これらの宣言では初期化子が無く、static storage durationでもthread storage durationでもないので、default-initializeされます。
これらの型はクラスでも配列でもないので、default-initializeは何も行わないことであり、値は不定になります。(N3337 8.5)
べあ さんが書きました:if文で存在しない配列にアクセスしないように気を付けているのですが、不十分でしょうか?
はい。
例えばx=10000, y=-12345のときdata[x - 1]は範囲外ですが、10行目の条件式x > 0 && y < (ygrid - 1)は真であり、範囲外へのアクセスが行われてしまいます。

Re: 自作関数のセグメントエラー

Posted: 2016年4月28日(木) 19:06
by みけCAT
can110 さんが書きました:

コード:

int count( int x, int y, int data[][ygrid]) {
	int check = 0;
	// 上下左右1マスずつ走査
	for( xx = x-1; xx <= x+1; xx++){
		for( yy = y-1; yy <= y+1; yy++)
			if( xx == x && yy == y) continue;	// 自身は除く
		//	adjustTorus( &xx, &yy);		// トーラス補正
			if( !isRange( xx, yy)) continue;	// 範囲外
			if( !data[xx][yy]) continue;		// いない
			check++;
		}
	}
	return check;
}
xx, yyが宣言されていない上、もしも「トーラス補正」のコメントアウトを解除することでxxやyyを書き換えて戻さないようにしてしまうとやばいのではないでしょうか?

Re: 自作関数のセグメントエラー

Posted: 2016年4月28日(木) 21:35
by box
べあ さんが書きました: ルールにのっとってマスの値を変えるというのを150回繰り返すものです。
本当ですか?
べあ さんが書きました:

コード:

  for(i = 0; i < 151; i++) {
151回実行してますけど。

Re: 自作関数のセグメントエラー

Posted: 2016年4月28日(木) 21:43
by みけCAT
オフトピック
ライフゲームなら、更新後のデータを混ぜずに全て更新前のデータに基づいて次の状態を決めないといけないのでは?
でも、そもそもマスの値が0のときしか更新しないようになっているから、(今のところ、少なくとも3個で生成、2個か3個で生存の基本ルールの)ライフゲームではないか。