課題です

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ten

課題です

#1

投稿記事 by ten » 12年前

次のようなプログラムを作れ。

1. 数値を順番に読み込んで二次元の配列に格納する。
2. その配列を画面に出力する。
3. 次に、2つの整数を読み込む。
4. 二次元配列のうち、読み込んだ整数に対応する二つの行の内容を交換する。
5. 配列を画面に出力する。配列の読み込み、交換、出力はそれぞれ関数にすること。

という課題をやってます。
コンパイしても、エラーいっぱい困ってます。
どうかアドバイス下さい。

ソースです。

#include <stdio.h> 

void reve(int *); 

int read(int *);

void write(int *); 

int main () 
{ 
     int x[5][5]; 

     read(x);

     write(x);

     reve(x); 

     write(x); 

     return 0; 
} 


int read(int x[/url]) /* 読み込み関数 */
{ 
     int i, j;
     i=0;
     j=0;
     while(i <= 5) {

           while(j <= 5){
           scanf("%d\n", &x[j]);                  /*  配列読み込み */
           j += 1;
           }

                i += 1;
                }

          return 0;
 
} 


void reve(int x[/url])  /* 交換関数 */
{ 
int n, i, j, y;
printf("何行目と何行目を入れ替えますか?\n");
scanf("%d", &n);
scanf("%d", &j);

      for(n=0; n <=5 ; n++) { 
      y = x[j][n]; 
      x[j][n] = x[n];
      x[n] = y; 
      }

      return;

} 

void write(int x[/url]) /* 出力関数  */
{ 
int i, j;

j=0; 
     for(i = 0; i <= 5; i++) { 

           while(j <=5){
           printf("x[%d][%d]=%d\n", i, j, x[j]);
           j += 1;
           }
      }

return;

}

管理人

Re:課題です

#2

投稿記事 by 管理人 » 12年前

このサンプルは、改良前のものです。

もっと下の投稿に改良版を投稿したので、そちらを参考にしてください。


#include<stdio.h>

void read(int x[5][5]){
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			printf("%d-%d(残り%d個) -> ",i+1,j+1,25-(i*5+j));
			scanf("%d",&x[j]);
		}
	}
	return;
}

void write(int x[5][5]){
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			printf("x[%d][%d]=%d\n",i,j,x[j]);
	return;
}

void reve(int x[5][5]){
	int i,y,n,m;
	printf("何行目と何行目を入れ替えますか?\n");

	printf("1行目 -> ");
	scanf("%d",&n);
	printf("2行目 -> ");
	scanf("%d",&m);
	
	n=n-1;		m=m-1;
	
	for(i=0;i<5;i++)
		y=x[n] , x[n]=x[m] , x[m]=y;

	return;
}

int main(){
	int x[5][5];
	read(x);
	write(x);
	reve(x);
	write(x);
	return 0;
}


悪いとこなおしておきました。
説明は後書きますね。
これでやりたいことあってるか、実際にコンパイルして確かめてみてください。

Justy

Re:課題です

#3

投稿記事 by Justy » 12年前

 int x[5][5]がいっぱい出てきてますが、(ちょっと難しいかもしれませんが)こうすると楽ですよ。
[color=#d0d0ff" face="monospace]typedef int      array_type[5][5];
int read(array_type x);
.....
int main()
{
    array_type x;
    read(x); 
    .....
}
[/color]

 

Justy

Re:課題です

#4

投稿記事 by Justy » 12年前

@@@tenさんへ

 順番に行きます。

1 read() / reve() / write()各関数の引数の型

 それぞれの関数の宣言では引数が[color=#d0d0ff" face="monospace"> int x[/url][/color]のように一次元配列だったり[color=#d0d0ff" face="monospace] int *[/color]のようにポインタで宣言されており、宣言と実装で型が違う上、更に実際に main関数から呼ばれている時は intの二次元配列の型を指定しています。つまり、プロトタイプ宣言、実装、使用時の3つ全てで全部型が異なっています。

 型はプロトタイプ宣言と実装は統一を、使用時とは互換性のある型を指定しなければなりません。
 ここでは型は全て intの[5, 5]の二次元配列なので、判りやすくするために typedefした型に置き換えて行きましょう。
[color=#d0d0ff" face="monospace]typedef int      array_type[5][5];
int read(array_type x);
.....
int main()
{
    array_type x;
    read(x); 
    .....
}
[/color]
 ここまででコンパイルは通るかと思います。


3 read()のアルゴリズム

 5x5の二次元配列なので 25回処理を繰り返す必要があるのですが、このアルゴリズムではそうはなりません。
 問題点は2つ。

 1つはループ条件です。
 配列の要素は5x5しかないので、アクセスできるのは x[4][4]までです。
 が、各ループ終了条件が [color=#d0d0ff" face="monospace]i <= 5[/color]と5以下なループするになっている為、x[5][5]までアクセスしてしまっています。

 2つ目は jの初期化です。
 最初に jのループが終わった後、i+=1されて、もう一度 jのループに入りますが、このとき jの値は前回のループを抜けたときの値・・・ここでは 6になっています。
 なので、i+=1の処理の後に j=0と jの値を初期化してあげてください。

 
3 write()のアルゴリズム

 こちらも read()同様、ループ条件に5が含まれています。
 0~4になるように修正してください。

 さらに iの値が変更されていません。
 jのループが終わった後 iを加算しないと iを使った終了条件のループが終わらないので、無限ループになってしまいます。



4 reve()のアルゴリズム

 行を入れ替えるとありますが、あまり意味がわからないので仕様面で合っているかどうかはよくわかりませんが、こちらも read()同様、ループ条件に5が含まれています。
 2つの値を入力するところで、n と jの変数に入れていますが、これは iと jの間違いでしょう。

管理人

Re:課題です

#5

投稿記事 by 管理人 » 12年前

間違いを注釈として書きました。
#include <stdio.h> 

void reve(int *);//最上位のみ省略出来る。 つまり(int (*)[5])このように書く必要がある

int read(int *);//int型関数にする必要が無いので、void関数でいい

void write(int *); 

int main () { 
     int x[5][5]; 

     read(x);
     write(x);
     reve(x); 
     write(x); 
     return 0; 
} 


int read(int x[/url]) //上で説明したように、最下位の[5]は省略不可。(int (*)[5])と書く
{ 
     int i, j;
     i=0;
     j=0;
     while(i <= 5) {//while文にせず、for文にするほうが楽 i<5でなければならない。

           while(j <= 5){//[5]で宣言したのに、5含め、0,1,2,3,4,5で回すと計6回、まわる。
           scanf("%d\n", &x[j]);//\nは要らない。今何個目入力かわからない。
           j += 1;
           }

                i += 1;
                }//jが0で初期化していないせいで2周目以降条件にはいらない、0~4の繰り返しにならない。

          return 0;
 } 


void reve(int x[/url])  //同様の間違い
{ 
int n, i, j, y;
printf("何行目と何行目を入れ替えますか?\n");
scanf("%d", &n);
scanf("%d", &j);

      for(n=0; n <=5 ; n++) { //せっかくnで入力を受けたのに、n=0で0で初期化してしまっている。
      y = x[j][n]; 
      x[j][n] = x[n];// iの値は何も入っていない。
      x[n] = y; 
      }

      return;

} 

void write(int x[/url]) //同様の間違い
{ 
int i, j;

j=0; 
     for(i = 0; i <= 5; i++) { //for文とwhile文の混在 i<5まででなければ6回まわってしまう

           while(j <=5){
           printf("x[%d][%d]=%d\n", i, j, x[j]);
           j += 1;
           }//jを初期化していないので、2周目以降jは6となり条件に入らない。
      }

return;

}



私が書いたプログラムで、main文を最初に書きたければ、プロトタイプ宣言は


void read (int (*)[5]);
void write(int (*)[5]);
void reve (int (*)[5]);


このようになります。
私の書いたプログラムでどこかわからない部分があれば聞いてください。


>Justyさん、

3、のwhite関数の中のiはforでまわっているので、無限ループの危険性は無いのではないでしょうか。

Justy

Re:課題です

#6

投稿記事 by Justy » 12年前

 あ、その通りですね。
 消しておきます。

管理人

Re:課題です

#7

投稿記事 by 管理人 » 12年前

(int (*)[5])と書く、といいましたが、
int *
と似たような書き方をするなら・・という意味なので、
最初のうちは
(int x[5][5])
と書いておいたほうがわかりやすくていいかもしれません。
5という数は最初からわかっているなら#defineで定義するのが一般的です。

#define A1 5
#define A2 5

(int x[A1][A2])

こんな感じがいいかもしれません。

管理人

Re:課題です

#8

投稿記事 by 管理人 » 12年前

先ほど私が書いたプログラム、
「1行目」

「2行目」
という表示はおかしいですね。
行列らしく表示の方法を変えて、再度書き直しました。


こちらが改良版です


アルゴリズムは全く同じです。

実際にコンパイルして、実行してみて、自分がやりたいこととあっているか確認してください。
#include<stdio.h>

void read(int x[5][5]){
	for(int i=0;i<5;i++){//このfor文を2つ組合わせることで5*5=25回ループできる。
		for(int j=0;j<5;j++){
			printf("%d-%d(残り%d個) -> ",i+1,j+1,25-(i*5+j));//入力を促す
			scanf("%d",&x[j]);//入力を格納
		}
	}
	return;
}

void write(int x[5][5]){
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			printf("%4d ",x[j]);//%4dというのは4桁表示するという意味。
		}
		printf("\n");//各行が表示できたら改行する
	}
	return;
}

void reve(int x[5][5]){
	int i,y,n,m;
	printf("何行目と何行目を入れ替えますか?\n");

	printf("入れ替える行1 -> ");
	scanf("%d",&n);
	printf("入れ替える行2 -> ");
	scanf("%d",&m);
	
	n=n-1;		m=m-1;//1行目といわれたら[0][/url]を交換する為、1を引く
	
	for(i=0;i<5;i++)
		y=x[n] , x[n]=x[m] , x[m]=y;//交換作業

	return;
}

int main(){
	int x[5][5];
	read(x);
	write(x);
	reve(x);
	write(x);
	return 0;
}


実行結果

・・・(略)
5-3(残り3個) -> 23
5-4(残り2個) -> 24
5-5(残り1個) -> 25
   1    2    3    4    5
   6    7    8    9   10
  11   12   13   14   15
  16   17   18   19   20
  21   22   23   24   25
何行目と何行目を入れ替えますか?
入れ替える行1 -> 2
入れ替える行2 -> 3
   1    2    3    4    5
  11   12   13   14   15
   6    7    8    9   10
  16   17   18   19   20
  21   22   23   24   25


これで入れ替わったかどうか確認しやすくなったと思います。

1行目というのは配列でいうx[0][/url]ここの0の事ですから、お間違いないよう。

%4dというのは「少なくとも4桁で表示せよ」という意味です。
1桁でも4マススペース使いますし、3桁でも4スペース使います。
ですから、4桁までの数値がはいっていれば、キレイにならんで表示されます。

もし5桁の数字が入力されることがあるなら5にしましょう。

ten

Re:課題です

#9

投稿記事 by ten » 12年前

Justyさん、管理人さんホントにありがとうございます。
凄く分かり安かったです!
アルゴリズムの細かいところまで解説していただき
スッキリしました!

管理人

Re:課題です

#10

投稿記事 by 管理人 » 12年前

理解していただけてよかったです^^

少しでも疑問が残っていたら気軽に聞いてくださいね☆

バグ

Re:課題です

#11

投稿記事 by バグ » 12年前

行を入れ替えるだけならば、指定列の先頭アドレスを渡してコピーを作って、丸々入れ替えてしまうようなやりかたもできそうですね(^-^)

閉鎖

“C言語何でも質問掲示板” へ戻る