質問: cost + 100; } ``` 最終は、このように判定したいです。 ### お願い 文字列として入力を読み込む程でよろしくお願いします。 初心者なので、出来れば丁寧に教えを乞いたいです。
回答: ### 入力を受け取る領域を確保する。 まずどうやって入力するにせよ、それを保存する領域が必要ですね。これには配列を使います。今は 3 文字と決まっているので、少なくとも **4 つ分の領域** を確保します。 **4 つ**というのはどういうことか。C 言語では、「文字列の終わり」を指す **ヌル文字** と呼ばれる存在があります。数字としてはゼロ `0` で、文字としては `'\0'` で表します。これが文字列の最後にくっついて、「ここで文字列は終わりですよ」ということを表すのです。いろいろな関数はこれによって文字列の区切りを認識しているので、これがなければ、たとえば `printf("%s", ...);` とかしたときに本来の文字列の範囲を越えて「メモリ上にたまたまあったヌル文字にあたる値」のところまで止まらず出力されちゃいます (つまり後ろにゴミが残ってしまいます) 。 たとえば、`"oxo"` という文字列は`{'o', 'x', 'o', '\0'}`というふうにできています。なので、文字数 + 1 の領域が必要です。 ここまでのコード: ``` char s[4]; ``` 実際に入力をとるというときはもっとざっくり適当に 100 とか用意しておいて、必要な分だけ使うということも多いです。が、まあ、ここでは 3 文字と決まっているので、これでよいと思います。 ### 入力を受け取る。 さて、入力をとるにはいろいろな関数がありますが、この場合一行に 3 文字と決まっているので `fgets()` を簡単に使えます。 この関数は、キーボードから入力するときは `fgets(受け取る配列, 配列のサイズ, stdin);` とします。実は `fgets()` 関数はファイルなどからも入力をうけることができ、最後の引数でどこから受け取るかを指定します。`stdin` を指定するとキーボードになります。ちなみに、入力が配列サイズを越えた場合は後ろは無視されます(標準入力に取り残されます)。文字列の終わりには少なくともヌル文字を入れないといけないので、実際に受け取れる文字数は `配列サイズ - 1` です。 あふれた入力は次の入力にまわされます。ですから、このプログラムをループで複数回回すときは、前のゴミが悪さをしないように標準入力を空読みして捨てなければなりません。とりあえずここではそれは無視します。 ここまでのコード: ``` char s[4]; fgets(s, 4, stdin); ``` ### ox を判定する。 「理想」のところでは if 文を 3 つ使って判定されています。別にそれでも全く問題はないのですが、せっかく配列で用意したのでループを使って判定していきましょう。でないと、たとえばトッピングが 100 種類とかあったとき手書きするの辛くないですか? for ループを使いましょう。次のような構文です。 ``` for (初期条件; ループ継続条件; ループ終了後処理) { 処理; ... } ``` それで、何回か繰り返す処理の場合、少し応用性が落ちますが、それは後々やればいいと思うので、次のように定型文的に覚えてもよい気がします。 ``` for (int ループカウンタ = 0; ループカウンタ < 回数; ループカウンタ++) { 処理; ... } ``` さて、ここではトッピングは 3 種類なので、 3 回ループしたいですね。ということで ``` for (int i = 0; i < 3; i++) { } ``` というふうになります。ここで、`i` はループごとに `0 -> 1 -> 2` と変化していきます。0 を含むので、 3 までは行かないことに注意してください。しかし配列の添字は 0 から始まるので結構都合がいいですよね。 では次に中の処理を考えます。やりたいことは、`i` 文字目が o なら 100 円を足すということですね。これは次のようにできます。 ``` if (s[i] == 'o') { cost += 100; } ``` はい、理想で書いてくださったものとほとんど同じです。注意としては、「1 文字の o 」を表すのはシングルクオートで `'o'` です。ダブルクオートで `"o"` としてしまうと **文字列** を表しますので、ヌル文字がついて `{'o', '\0'}` という構造になります。 (しかも、それに対するポインタであって、とりあえず一文字の `o` とは全く異なるものだと考えてください。) また、`cost = cost + 100;` というのは `cost += 100;` と略記できます。これは別に使っても使わなくてもよいです。 なお、入力が 3 文字に満たない場合、先のヌル文字が手前に入り、それ以降の要素は初期化されません。なので、それ以降の要素にアクセスするとよく分からないゴミを読み込んでしまいます。たまたまゴミが `'o'` の形をしていたら誤判定してしまうので、読み込み前に if 文でヌル文字であるかどうかを判定してループを抜けるということをしてもよいと思います。それには ``` if (s[i] == '\0') { break; } ``` というふうにできます。 さあ、`cost` を使ったのですが、まだ `cost` を準備していませんね。適当に int でよいでしょう。ベースは 700 円でそれに追加料金がかかるという設定なので、ループの前に `int cost = 700;` というのを追加するとよさそうです。 ここまでのコード: ``` char s[4]; fgets(s, 4, stdin); int cost = 700; for (int i = 0; i < 3; i++) { if (s[i] == '\0') { break; } if (s[i] == 'o') { cost += 100; } } ``` もうこれでロジックは終了ですね。 ### 表示 あとは表示でもしましょうか。これは `printf()` を使えますね。 完成したコード: ``` #include int main() { char s[4]; fgets(s, 4, stdin); int cost = 700; for (int i = 0; i < 3; i++) { if (s[i] == '\0') { break; } if (s[i] == 'o') { cost += 100; } } printf("%d\n", cost); } ``` ところで、今はトッピングが 3 種類ですが、もし 4 種類や 5 種類に増えたらどう対応しましょうか。今は配列の数、 `fgets()` の引数、ループの条件の 3 箇所も変更しなければなりません。面倒ですし、うっかりどこかだけ変更を忘れてしまうとバグの原因ともなります。 そこで、トッピングの種類の数に名前をつけるということがよくされます。これに関しては `#define` というものを使うのですが、 ``` #define 名前 数 ``` と覚えればよいと思います。`define` はプリプロセッサディレクティブと言って、他にもいろいろできることがあるのですが、それはまたおいおい学べばよいと思います。ここでは単に定数に名前を付けると思って (これもあまり正確な表現ではないのですが) 。 たとえば `NUM_TOPPING` という名前を付けるとしましょう。今は 3 種類なので ``` #define NUM_TOPPING 3 ``` とすればよいです。これを使って全体を書き換えると、 ``` #define NUM_TOPPING 3 #include int main() { char s[NUM_TOPPING + 1]; fgets(s, NUM_TOPPING + 1, stdin); int cost = 700; for (int i = 0; i < NUM_TOPPING; i++) { if (s[i] == '\0') { break; } if (s[i] == 'o') { cost += 100; } } printf("%d\n", cost); } ``` となります。変更に強いということもありますが、読み易さの観点から見ても、 4 とか 3 とか唐突にでてくるより、このように名前を付けた方が意味がすっと通るようになるのではないかと思います。 これでもしトッピングの数が 4 種類、 5 種類、 100 種類となったとしても、一番上の `NUM_TOPPING` を変更するだけで済みます。