選ばなかったカードの識別

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

選ばなかったカードの識別

#1

投稿記事 by きょう » 12年前

この問題に関して質問です。

http://judge.u-aizu.ac.jp/onlinejudge/d ... id=10015です

http://judge.u-aizu.ac.jp/onlinejudge/r ... 自分で作ってみました

コード:

#include<stdio.h>
int main(void)
{
	int a[4][13],i,j,n,k;
	char c,gara[4]={'S','H','C','D'};
	for(i=0;i<4;i++)
	{
		for(j=0;j<13;j++)
		{
			a[i][j]=0;/*一旦すべて0に*/
		}
	}
	printf("nは?");
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%c",&c);
		scanf("%d",&k);
		if(c=='S')
		{
			a[0][k-1]=1;
		}
		else if(c=='H')
		{
			a[1][k-1]=1;
		}
		else if(c=='C')
		{
			a[2][k-1]=1;
		}
		else if(c=='D')
		{
			a[3][k-1]=1;
		}
	}
	for(i=0;i<4;i++)
	{
		for(j=0;j<13;j++)
		{
			if(a[i][j]==0)
			{
				printf("%c %d\n",gara[i],j+1);
			}
		}
	}
	return 0;
}
考え方としては、二次元配列で、自分が手に取ったカードは1とし、あとで0であるものをprintf関数で呼び出そうとしました。

しかし、例えばn=2と打っても一つのカードしか入力できません。 どこが間違えているのでしょうか?

また、参考にした回答を見ると、scanf("%d",&n);を2回繰り返しているのですが、どういう意味があるのでしょうか?

よろしくお願いします。

とっち
記事: 56
登録日時: 13年前
住所: 岡山

Re: 選ばなかったカードの識別

#2

投稿記事 by とっち » 12年前

答えをそのまま言ってもいいのですがせっかくなんでヒントだけにします

17行目のscanf関数の前に

コード:

printf("c=");
18行目のscanf関数の前に

コード:

printf("k=");
というのを入れてみてください
どうなっているかもしかしたらわかるかもしれません
(これはprintfデバッグというものです。処理が望むところに来ているかどうかチェックできます)

この先も説明してもいいのですがきょうさんが自力で解くほうが勉強になるかなと思い
最初のヒントとしてはこの辺にしておきます

わからなければどんどん質問してください

きょう

Re: 選ばなかったカードの識別

#3

投稿記事 by きょう » 12年前

遅くなり、すみません。

回答ありがとうございます!

早速実行してみたのですが、結果は

c=k=になりました。

理由はわかりませんが、一回打ち込むことで2回分打ち込んでいることになるから、n=2のとき実行したとき変な結果になったのだとわかりました。

とっち
記事: 56
登録日時: 13年前
住所: 岡山

Re: 選ばなかったカードの識別

#4

投稿記事 by とっち » 12年前

>一回打ち込むことで2回分打ち込んでいることになるから、n=2のとき実行したとき変な結果になったのだとわかりました。
考え方はあってるんだけどこの場合はちょっと違うかな

つまりどういうことかというと

まず、理想の動作としては
1.nは?と表示される -> nを入力
2.c=と表示される -> cを入力
3.k=と表示される -> kを入力

ですよね
しかし今2.がない状態です

理由はscanf関数にあります
ググってもらうと答え出るかも・・・

一応ヒントとしてですが最初に提示してもらったプログラムの19行目に

コード:

printf("c=%ck=%d",c,k);
というのを入れてみてください
もしかしたらわかるかも・・・

きょう

Re: 選ばなかったカードの識別

#5

投稿記事 by きょう » 12年前

やってみました。 そうすると、Cの値は出ず、kしかでないことになっていました。

すみません、まだこれだとどうしてかわかりません・・・

ただ、nを入れないと、二回打ち込むことになっているので、scanf関数はしっかり動作していると思います。

きょう

Re: 選ばなかったカードの識別

#6

投稿記事 by きょう » 12年前

http://www.aa.alpha-net.ne.jp/freeh/min ... scanf.html
のサイトが言っていることと似ています。

ただ、そうして起こるのかはページを見てもよく理解できませんでした。

かずま

Re: 選ばなかったカードの識別

#7

投稿記事 by かずま » 12年前

プログラムは、書いたとおりに動きます。
動きを追ってみましょう。

scanf("%d", &n);
標準入力からの入力待ちになります。
キーボードから 2 と Enter を入力すると、
標準入力バッファに "2\n" が入ります。
scanf はそこから '2' と '\n' を読み込みますが、
'\n' は "%d" という書式に合わないので、これを
標準入力バッファに戻します。そして、読み込んだ
char の '2' を int の 2 に変換し、変数 n に入れます。

scanf("%c",&c);
標準入力に文字が入っていますから、入力待ちになりません。
標準入力に文字が入っている文字 '\n' を読み込んで
変数 c に入れます。標準準入力バッファはカラになりました。

scanf("%d",&k);
標準入力がカラですから入力待ちになります。
キーボードから 「S スペース 1 Enter」 を入力すると、
標準入力バッファには "S 1\n" が入ります。
scanf はそこから 'S' を読み込みますが、"%d" の書式に
合わないので、これを標準入力バッファに戻します。

ということで、
 n = 2
 c = '\n'
 k = 不定
となります。

scanf("%c",&c); でやりたいことは、'\n' を読み飛ばして
次に入力される 'S' を読み込みたいということです。
scanf のほとんどの書式 %d, %s, %f, %x などは、それ自身が
0個以上の空白類文字(' ', '\t', '\n' など) を読み飛ばす
という仕様になっています。
ところが、%c と %[ だけは、空白類文字を読み飛ばしません。
scanf には空白類文字を読み飛ばすだけの機能を持つ書式があります。
それは、" " です("\t" や "\n" でも同じ)。
したがって、scanf(" %c%d", &c, &k); と書くことで、
c と k に望みどおりの値を入力できます。

きょう

Re: 選ばなかったカードの識別

#8

投稿記事 by きょう » 12年前

とても詳しくありがとうございます。


scanf関数は、打ち込んだ値+¥nが入力されるということがわかりました。そして何も打たなくても¥nというのは打ち込んだということになります。こういう解釈で正しいでしょうか?

また、いくつか質問があるのですが、よいでしょうか?

>scanf("%c",&c);
標準入力に文字が入っていますから、入力待ちになりません。

文字が入ってるということはどういうことでしょうか?

あと、
> n = 2
 c = '\n'
 k = 不定

のcにある¥nはどこから出てきたのでしょうか?


よろしくお願いします。

かずま

Re: 選ばなかったカードの識別

#9

投稿記事 by かずま » 12年前

きょう さんが書きました: scanf関数は、打ち込んだ値+¥nが入力されるということがわかりました。そして何も打たなくても¥nというのは打ち込んだということになります。こういう解釈で正しいでしょうか?
いいえ、その解釈は完全に間違っています。
キーボートから打ち込んだキーの値は、標準入力バッファに入ります。
scanf が直接キーボードから文字を読み込むわけではありません。
scanf は、標準入力バッファから 1文字ずつ読み込んで、書式と合う文字だけを
変換して、指定の変数に入れます。書式と合わない文字は、標準入力バッファに
戻して、その文字を読まなかったことにします。
その文字は、次回の scanf (または他の入力関数) が読み込みます。
きょう さんが書きました: >scanf("%c",&c);
標準入力に文字が入っていますから、入力待ちになりません。

文字が入ってるということはどういうことでしょうか?
標準入力バッファに '\n' という文字が入っているということです。
きょう さんが書きました: > n = 2
 c = '\n'
 k = 不定

のcにある¥nはどこから出てきたのでしょうか?
キーボードから入力した Enter が、標準入力バッファに '\n' として
入ったのを scanf("%d",%n) が一度は読み込みますが、'\n' が "%d" の書式
に合わないので、標準入力バッファの先頭に戻されます。
次に、scanf("%c",&c) が標準入力バッファから 1文字を読み込んだら、
それが '\n' であって、それを c に入れます。

きょう

Re: 選ばなかったカードの識別

#10

投稿記事 by きょう » 12年前

初歩的な質問すみません。

標準入力バッファとはどういうものなのでしょうか?

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 選ばなかったカードの識別

#11

投稿記事 by みけCAT » 12年前

自分はこのような問題を回避する為に、cをchar c[4];として取り、%cではなく%sで読み込み、
値を使うときにはcの代わりにc[0]を使用します。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 選ばなかったカードの識別

#12

投稿記事 by softya(ソフト屋) » 12年前

きょう さんが書きました:初歩的な質問すみません。

標準入力バッファとはどういうものなのでしょうか?
正確に書くと標準入力のストリームバッファと言うものです。
標準入力(stdin)はコンソールからの入力ですね。
scanfやgetcなどは受け付けた入力を一度標準ライブラリ内にある文字列バッファに貯めこむ設計になっています。
scanfだと、そこから入力書式で指示されたものだけ取り出してくるのです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

きょう

Re: 選ばなかったカードの識別

#13

投稿記事 by きょう » 12年前

ご回答ありがとうございます。

標準ライブラリ内にある文字列バッファに貯めこんで、そこからたとえば%dだったら数値のみを取り出すという解釈であっていますでしょうか?

例えば、 scanf("%c",&c)だったらバッファから文字列のみを取り込むということでしょうか?

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 選ばなかったカードの識別

#14

投稿記事 by みけCAT » 12年前

%cはバッファから「1文字」(1バイト?)を取り込みます。文字列ではありません。
文字列を取り込むのは%sです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

きょう

Re: 選ばなかったカードの識別

#15

投稿記事 by きょう » 12年前

そうでした。文字列は%sですよね。

こういうミスも気を付けないといけないと思いました。

%sが取り込んで、変数cに取り込まれるわけですね。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 選ばなかったカードの識別

#16

投稿記事 by みけCAT » 12年前

%sで変数c(char c[4];で宣言)に文字列を入れるときは、scanf内の変数cに&をつけてはいけません。
一応気をつけてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

きょう

Re: 選ばなかったカードの識別ーNo: 7 の理解ー

#17

投稿記事 by きょう » 12年前

scanf("%d", &n);
標準入力からの入力待ちになります。
キーボードから 2 と Enter を入力すると、
標準入力バッファに "2\n" が入ります。
scanf はそこから '2' と '\n' を読み込みますが、
'\n' は "%d" という書式に合わないので、これを
標準入力バッファに戻します。そして、読み込んだ
char の '2' を int の 2 に変換し、変数 n に入れます。

scanf("%c",&c);
標準入力に文字が入っていますから、入力待ちになりません。
標準入力に文字が入っている文字 '\n' を読み込んで
変数 c に入れます。標準準入力バッファはカラになりました。

scanf("%d",&k);
標準入力がカラですから入力待ちになります。
キーボードから 「S スペース 1 Enter」 を入力すると、
標準入力バッファには "S 1\n" が入ります。
scanf はそこから 'S' を読み込みますが、"%d" の書式に
合わないので、これを標準入力バッファに戻します。

ということで、
 n = 2
 c = '\n'
 k = 不定
となります。

までは皆様のおかげで理解できました。

ただ、ここからがわかりません。
scanf("%c",&c); でやりたいことは、'\n' を読み飛ばして
次に入力される 'S' を読み込みたいということです。
scanf のほとんどの書式 %d, %s, %f, %x などは、それ自身が
0個以上の空白類文字(' ', '\t', '\n' など) を読み飛ばす
という仕様になっています。
ところが、%c と %[ だけは、空白類文字を読み飛ばしません。
scanf には空白類文字を読み飛ばすだけの機能を持つ書式があります。
それは、" " です("\t" や "\n" でも同じ)。
したがって、scanf(" %c%d", &c, &k); と書くことで、
c と k に望みどおりの値を入力できます。[/quote]

どういう場合が¥nを読み飛ばすんですか?

かずま

Re: 選ばなかったカードの識別

#18

投稿記事 by かずま » 12年前

きょう さんが書きました: どういう場合が¥nを読み飛ばすんですか?
"%d" は、先行する空白類文字を読み飛ばしたあと、
10進整数を読み取り、int の変数に変換結果を入れます。
"%s" は、先行する空白類文字を読み飛ばしたあと、
空白類類文字でない文字の列を読み取り、char配列にその文字列を入れます。
"%f" は、先行する空白類文字を読み飛ばしたあと、
浮動小数点数を読み取り、float の変数に変換結果を入れます。
"%x" は、先行する空白類文字を読み飛ばしたあと、
16進整数を読み取り、unsigned int の変数に変換結果を入れます。

"%c" は、先行する空白類文字列を読み飛ばさず、
1文字だけ読み取り、その文字の値を char 変数に入れます。
"%[abc]" は、先行する空白類文字列を読み飛ばさず、
連続するa b c の文字だけの列を読み取り、その文字列を char 配列に入れます。

" " は、空白類文字列を読み飛ばします。
"\n" は、空白類文字列を読み飛ばします。
"\t" は、空白類文字列を読み飛ばします。

だから、
" %c" は、先行する空白類文字を読み飛ばしたあと、
1文字だけ読み取り、その文字の値を char 変数に入れます。
"\n%c" は、先行する空白類文字を読み飛ばしたあと、
1文字だけ読み取り、その文字の値を char 変数に入れます。
"\t%c" は、先行する空白類文字を読み飛ばしたあと、
1文字だけ読み取り、その文字の値を char 変数に入れます。

きょう

Re: 選ばなかったカードの識別

#19

投稿記事 by きょう » 12年前

詳しい解説ありがとうございます。

理解できました。

%dや%cでもこんなに違いがあるんですね。

ありがとうごうざいます。

閉鎖

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