ページ 1 / 1
選ばなかったカードの識別
Posted: 2013年4月27日(土) 00:42
by きょう
この問題に関して質問です。
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回繰り返しているのですが、どういう意味があるのでしょうか?
よろしくお願いします。
Re: 選ばなかったカードの識別
Posted: 2013年4月27日(土) 09:50
by とっち
答えをそのまま言ってもいいのですがせっかくなんでヒントだけにします
17行目のscanf関数の前に
18行目のscanf関数の前に
というのを入れてみてください
どうなっているかもしかしたらわかるかもしれません
(これはprintfデバッグというものです。処理が望むところに来ているかどうかチェックできます)
この先も説明してもいいのですがきょうさんが自力で解くほうが勉強になるかなと思い
最初のヒントとしてはこの辺にしておきます
わからなければどんどん質問してください
Re: 選ばなかったカードの識別
Posted: 2013年4月27日(土) 23:07
by きょう
遅くなり、すみません。
回答ありがとうございます!
早速実行してみたのですが、結果は
c=k=になりました。
理由はわかりませんが、一回打ち込むことで2回分打ち込んでいることになるから、n=2のとき実行したとき変な結果になったのだとわかりました。
Re: 選ばなかったカードの識別
Posted: 2013年4月28日(日) 12:49
by とっち
>一回打ち込むことで2回分打ち込んでいることになるから、n=2のとき実行したとき変な結果になったのだとわかりました。
考え方はあってるんだけどこの場合はちょっと違うかな
つまりどういうことかというと
まず、理想の動作としては
1.nは?と表示される -> nを入力
2.c=と表示される -> cを入力
3.k=と表示される -> kを入力
ですよね
しかし今2.がない状態です
理由はscanf関数にあります
ググってもらうと答え出るかも・・・
一応ヒントとしてですが最初に提示してもらったプログラムの19行目に
コード:
printf("c=%ck=%d",c,k);
というのを入れてみてください
もしかしたらわかるかも・・・
Re: 選ばなかったカードの識別
Posted: 2013年4月28日(日) 13:34
by きょう
やってみました。 そうすると、Cの値は出ず、kしかでないことになっていました。
すみません、まだこれだとどうしてかわかりません・・・
ただ、nを入れないと、二回打ち込むことになっているので、scanf関数はしっかり動作していると思います。
Re: 選ばなかったカードの識別
Posted: 2013年4月28日(日) 13:49
by きょう
http://www.aa.alpha-net.ne.jp/freeh/min ... scanf.html
のサイトが言っていることと似ています。
ただ、そうして起こるのかはページを見てもよく理解できませんでした。
Re: 選ばなかったカードの識別
Posted: 2013年4月28日(日) 14:48
by かずま
プログラムは、書いたとおりに動きます。
動きを追ってみましょう。
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: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 01:39
by きょう
とても詳しくありがとうございます。
scanf関数は、打ち込んだ値+¥nが入力されるということがわかりました。そして何も打たなくても¥nというのは打ち込んだということになります。こういう解釈で正しいでしょうか?
また、いくつか質問があるのですが、よいでしょうか?
>scanf("%c",&c);
標準入力に文字が入っていますから、入力待ちになりません。
文字が入ってるということはどういうことでしょうか?
あと、
> n = 2
c = '\n'
k = 不定
のcにある¥nはどこから出てきたのでしょうか?
よろしくお願いします。
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 03:20
by かずま
きょう さんが書きました:
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: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 16:17
by きょう
初歩的な質問すみません。
標準入力バッファとはどういうものなのでしょうか?
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 17:32
by みけCAT
自分はこのような問題を回避する為に、cをchar c[4];として取り、%cではなく%sで読み込み、
値を使うときにはcの代わりにc[0]を使用します。
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 17:33
by softya(ソフト屋)
きょう さんが書きました:初歩的な質問すみません。
標準入力バッファとはどういうものなのでしょうか?
正確に書くと標準入力のストリームバッファと言うものです。
標準入力(stdin)はコンソールからの入力ですね。
scanfやgetcなどは受け付けた入力を一度標準ライブラリ内にある文字列バッファに貯めこむ設計になっています。
scanfだと、そこから入力書式で指示されたものだけ取り出してくるのです。
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 21:46
by きょう
ご回答ありがとうございます。
標準ライブラリ内にある文字列バッファに貯めこんで、そこからたとえば%dだったら数値のみを取り出すという解釈であっていますでしょうか?
例えば、 scanf("%c",&c)だったらバッファから文字列のみを取り込むということでしょうか?
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 21:50
by みけCAT
%cはバッファから「1文字」(1バイト?)を取り込みます。文字列ではありません。
文字列を取り込むのは%sです。
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 22:05
by きょう
そうでした。文字列は%sですよね。
こういうミスも気を付けないといけないと思いました。
%sが取り込んで、変数cに取り込まれるわけですね。
Re: 選ばなかったカードの識別
Posted: 2013年4月29日(月) 22:09
by みけCAT
%sで変数c(char c[4];で宣言)に文字列を入れるときは、scanf内の変数cに&をつけてはいけません。
一応気をつけてください。
Re: 選ばなかったカードの識別ーNo: 7 の理解ー
Posted: 2013年4月30日(火) 00:13
by きょう
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: 選ばなかったカードの識別
Posted: 2013年4月30日(火) 02:20
by かずま
きょう さんが書きました:
どういう場合が¥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: 選ばなかったカードの識別
Posted: 2013年4月30日(火) 08:13
by きょう
詳しい解説ありがとうございます。
理解できました。
%dや%cでもこんなに違いがあるんですね。
ありがとうごうざいます。