自己解決のコメント欄
scanfで入力された文字は一度バッファに保存され、Enterキーを入力するとバッファをクリアし、再度そのメモリ領域が使用できる状態になります。
しかし、scanfでは最後の改行文字(\n)はクリアされずにバッファに残ってしまうため、上記のソースコードを例に挙げれば、変数sumで最後に入力した改行文字(\n)が変数cpに代入されてしまいます。
そのため、scanf(" %c", &cp)の空白はバッファに残った改行文字の入力分であると思われます。
バッファ(標準入力)まわりの話題というのはそうなんだけど、解釈はちょっと違います。
C++
1#include <cstdio>
2#include <iostream>
3
4int main(void) {
5 int a;
6 char cp;
7 int sum;
8
9 std::cout << "input a number > ";
10 std::cin >> sum;
11 cp=std::cin.get();//(A)
12 std::cout << (int)cp;
13 return 0;
14}
こうすると、10とか13とかの改行文字が(A)で取得出来ているのがわかるはずです。つまり、「scanfでは最後の改行文字(\n)はクリアされずにバッファに残ってしまう」= cinでは数値を入力したあと\nがクリアされる、という想定は間違っています。
scanf()だろうとcin << だろうと、どちらも「入力指示があったデータを可能な範囲で取り込み、変換出来なかったデータはバッファ(標準入力)に残る」という動作をしています。どちらも、数値データを取り込んだあと、改行文字に出会ったらそれは変換不可能なのでバッファに残して動作を終了します。(つまり、「Enterキーを入力するとバッファをクリアし」という動作も存在しません)
char cp;
scanf("%c",&cp);
は、問答無用に、バッファの先頭から一文字を取り込んでcpに格納しろ、という指示です。なので、バッファの先頭に改行文字があればそれを取り込んで処理を完了します。(これが期待しない動作になっていたわけですが)
ここで、書式指定を" %c"とすると、先頭の空白は0文字以上の空白文字にマッチします。空白文字というのは、空白、水平タブ、改行等、isspace()関数が真を返す文字たち。そうすると、改行は空白文字の一種なので、最初の空白に(あれば)マッチし、その後最初に出てくる空白文字ではない一文字が%cにマッチして取り込まれるということになります。ここはTAKAHIRO_24さんの仰るとおり。(でも、文字の前に空白文字が複数あっても" %c"の空白が全て引き受けてくれる、というのは認識してたかしら?)
cinだと期待の動きになったのは、cinでcharを取り込むときの動作が" %c"に相当するもの、と決められていたから、です。ちょっとずるいような回答になりますけど、そういうことなので。直前の整数取り込みがscanfなのかcin <<だったのかは関係ありません。
ちなみに、scanf()の書式指定"%d"とか"%s"とかも、バッファ先頭の0個以上の空白文字を読み飛ばす、ということになっています。だから、
scanf("%d",&a);
scanf("%d",&b);
などというのは、バッファに残っているEnterを読み飛ばして期待通りに動くわけです。
このついでに言えば、複数の整数を取り込むときに
scanf("%d %d",&a,&b);
とする人が時々いますけれど、実はこれ
scanf("%d%d",&a,&b);
と書いても動作は同じ。
ところで、質問のプログラムは(scanf版でも)"12+34-56=[Enter]"と連続して入力してもちゃんと動作する、というのはわかってましたか? (一応の理屈はこの回答中に書かれてますけど)
追記
scanf版で"1[Enter]+[Enter]2[Enter]=[Enter]"と入力したときの動作を、コンピュータになったつもりでトレースしてみましょう。
説明のため、一応プログラムに行番号をつけておきます。
C++
1 1: scanf("%d", &sum);
2 2: while(true){
3 3: scanf("%c", &cp);
4 4: if(cp == '=') break;
5 5: scanf("%d", &a);
6 6: switch (cp) {
7 7: case '+':
8 8: sum += a;
9 9: break;
1010: case '-':
1111: sum -= a;
1212: break;
1313: case '*':
1414: sum *= a;
1515: break;
1616: case '/':
1717: sum /= a;
1818: break;
1919: }
2020: }
2121: printf("%d\n", sum);
これに、"1[Enter]+[Enter]2[Enter]=[Enter]"を喰わせます。
1:最初は標準入力は空なので入力待ち。"1[Enter]"を入力するのでsumに1を取り込みます。標準入力には\nが残っています。
2:のwhile(true)は素通しで
3:これまでの話で、cpには'\n'が入ります。標準入力は空になって
4:ifの条件は偽で次へ
5:ここでscanfは入力バッファが空なので入力待ちになります。ここに入ってくるデータは"+[Enter]"。"%d"は整数を要求します。'+'はもしかしたら整数の一部かもしれないので取り込みますが、次が'[Enter]'だと整数として解釈出来ないので、scanfは失敗。aの値は変更されないまま、また標準入力には"[Enter]"が入ってまま次に進みます。
6:cpには'\n'が入ってますから、どのcaseラベルともマッチしません。
20:switch文を抜けて、whileの終わりに到達。2:のwhileのところまで戻りましょう。
3:標準入力の先頭は'[Enter]'ですから、cpに'\n'を取り込みます。
4:cpは'\n'なので偽
5:標準入力が空で入力待ち、"2[Enter]が入力されますので、aには2が入って標準入力には[Enter]が残ります。
6:cpは'\n'なのでcaseにマッチなし
20:while終わり、2:に戻って
3:標準入力は'[Enter]'で'\n'をcpに取り込み
4:ifの条件は偽で次へ
5:標準入力は空なので入力待ち。"=[Enter]"が入力されますが、数値には変換不可で標準入力には"=[Enter]"が入った状態で次に
6:cpは'\n'なのでマッチなし
20:while終わり、2:に戻って
3:標準入力の先頭は'='なので、cpに取り込みます。標準入力には"[Enter]"が残っています(もう関係ないけど)
4:cpが'='なのでif文の判定式が真でbreak; 21:へ
21:さて、sumは...最初に設定された1から変更なし。なので1を表示します。
以上、終わり。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/26 08:24 編集
2020/04/26 11:49
2020/04/26 14:49
2020/04/26 22:05
2020/04/27 11:17