質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

208閲覧

[c]文字を一文字ずつ・・・

andor

総合スコア17

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/07/16 02:52

前提・実現したいこと

次の問題でつまづいています。

問題

700円のラーメンにトッピングをのせる種類は、3つである。どのトッピングも100円である。トッピングの有無は、"o"が有り、"x"が無しである。入力は、以下の形式で与えられる。(要約済)
(例)oxo
(解)900
(例)xxx
(解)700

発生している問題

上の文字列を一つずつ読み込んで配列に入れたいのですが、ここが分かりません。

該当のソースコード

C言語 (理想)

if (s[0] == "o") { cost = cost + 100; } if (s[1] == "o") { cost = cost + 100; } if (s[2] == "o") { cost = cost + 100; }

最終は、このように判定したいです。

お願い

文字列として入力を読み込む程でよろしくお願いします。
初心者なので、出来れば丁寧に教えを乞いたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

入力を受け取る領域を確保する。

まずどうやって入力するにせよ、それを保存する領域が必要ですね。これには配列を使います。今は 3 文字と決まっているので、少なくとも 4 つ分の領域 を確保します。

4 つというのはどういうことか。C 言語では、「文字列の終わり」を指す ヌル文字 と呼ばれる存在があります。数字としてはゼロ 0 で、文字としては '\0' で表します。これが文字列の最後にくっついて、「ここで文字列は終わりですよ」ということを表すのです。いろいろな関数はこれによって文字列の区切りを認識しているので、これがなければ、たとえば printf("%s", ...); とかしたときに本来の文字列の範囲を越えて「メモリ上にたまたまあったヌル文字にあたる値」のところまで止まらず出力されちゃいます (つまり後ろにゴミが残ってしまいます) 。

たとえば、"oxo" という文字列は{'o', 'x', 'o', '\0'}というふうにできています。なので、文字数 + 1 の領域が必要です。

ここまでのコード:

c

1char s[4];

実際に入力をとるというときはもっとざっくり適当に 100 とか用意しておいて、必要な分だけ使うということも多いです。が、まあ、ここでは 3 文字と決まっているので、これでよいと思います。

入力を受け取る。

さて、入力をとるにはいろいろな関数がありますが、この場合一行に 3 文字と決まっているので fgets() を簡単に使えます。
この関数は、キーボードから入力するときは fgets(受け取る配列, 配列のサイズ, stdin); とします。実は fgets() 関数はファイルなどからも入力をうけることができ、最後の引数でどこから受け取るかを指定します。stdin を指定するとキーボードになります。ちなみに、入力が配列サイズを越えた場合は後ろは無視されます(標準入力に取り残されます)。文字列の終わりには少なくともヌル文字を入れないといけないので、実際に受け取れる文字数は 配列サイズ - 1 です。
あふれた入力は次の入力にまわされます。ですから、このプログラムをループで複数回回すときは、前のゴミが悪さをしないように標準入力を空読みして捨てなければなりません。とりあえずここではそれは無視します。

ここまでのコード:

c

1char s[4]; 2fgets(s, 4, stdin);

ox を判定する。

「理想」のところでは if 文を 3 つ使って判定されています。別にそれでも全く問題はないのですが、せっかく配列で用意したのでループを使って判定していきましょう。でないと、たとえばトッピングが 100 種類とかあったとき手書きするの辛くないですか?

for ループを使いましょう。次のような構文です。

c

1for (初期条件; ループ継続条件; ループ終了後処理) { 2 処理; ... 3}

それで、何回か繰り返す処理の場合、少し応用性が落ちますが、それは後々やればいいと思うので、次のように定型文的に覚えてもよい気がします。

c

1for (int ループカウンタ = 0; ループカウンタ < 回数; ループカウンタ++) { 2 処理; ... 3} 4

さて、ここではトッピングは 3 種類なので、 3 回ループしたいですね。ということで

for (int i = 0; i < 3; i++) { }

というふうになります。ここで、i はループごとに 0 -> 1 -> 2 と変化していきます。0 を含むので、 3 までは行かないことに注意してください。しかし配列の添字は 0 から始まるので結構都合がいいですよね。

では次に中の処理を考えます。やりたいことは、i 文字目が o なら 100 円を足すということですね。これは次のようにできます。

c

1if (s[i] == 'o') { 2 cost += 100; 3}

はい、理想で書いてくださったものとほとんど同じです。注意としては、「1 文字の o 」を表すのはシングルクオートで 'o' です。ダブルクオートで "o" としてしまうと 文字列 を表しますので、ヌル文字がついて {'o', '\0'} という構造になります。 (しかも、それに対するポインタであって、とりあえず一文字の o とは全く異なるものだと考えてください。)

また、cost = cost + 100; というのは cost += 100; と略記できます。これは別に使っても使わなくてもよいです。

なお、入力が 3 文字に満たない場合、先のヌル文字が手前に入り、それ以降の要素は初期化されません。なので、それ以降の要素にアクセスするとよく分からないゴミを読み込んでしまいます。たまたまゴミが 'o' の形をしていたら誤判定してしまうので、読み込み前に if 文でヌル文字であるかどうかを判定してループを抜けるということをしてもよいと思います。それには

c

1if (s[i] == '\0') { 2 break; 3}

というふうにできます。

さあ、cost を使ったのですが、まだ cost を準備していませんね。適当に int でよいでしょう。ベースは 700 円でそれに追加料金がかかるという設定なので、ループの前に int cost = 700; というのを追加するとよさそうです。

ここまでのコード:

c

1char s[4]; 2fgets(s, 4, stdin); 3 4int cost = 700; 5for (int i = 0; i < 3; i++) { 6 if (s[i] == '\0') { 7 break; 8 } 9 if (s[i] == 'o') { 10 cost += 100; 11 } 12}

もうこれでロジックは終了ですね。

表示

あとは表示でもしましょうか。これは printf() を使えますね。

完成したコード:

c

1#include <stdio.h> 2 3int main() { 4 char s[4]; 5 fgets(s, 4, stdin); 6 7 int cost = 700; 8 for (int i = 0; i < 3; i++) { 9 if (s[i] == '\0') { 10 break; 11 } 12 if (s[i] == 'o') { 13 cost += 100; 14 } 15 } 16 printf("%d\n", cost); 17}

ところで、今はトッピングが 3 種類ですが、もし 4 種類や 5 種類に増えたらどう対応しましょうか。今は配列の数、 fgets() の引数、ループの条件の 3 箇所も変更しなければなりません。面倒ですし、うっかりどこかだけ変更を忘れてしまうとバグの原因ともなります。

そこで、トッピングの種類の数に名前をつけるということがよくされます。これに関しては #define というものを使うのですが、

#define 名前 数

と覚えればよいと思います。define はプリプロセッサディレクティブと言って、他にもいろいろできることがあるのですが、それはまたおいおい学べばよいと思います。ここでは単に定数に名前を付けると思って (これもあまり正確な表現ではないのですが) 。

たとえば NUM_TOPPING という名前を付けるとしましょう。今は 3 種類なので

c

1#define NUM_TOPPING 3

とすればよいです。これを使って全体を書き換えると、

c

1#define NUM_TOPPING 3 2 3#include <stdio.h> 4 5int main() { 6 char s[NUM_TOPPING + 1]; 7 fgets(s, NUM_TOPPING + 1, stdin); 8 9 int cost = 700; 10 for (int i = 0; i < NUM_TOPPING; i++) { 11 if (s[i] == '\0') { 12 break; 13 } 14 if (s[i] == 'o') { 15 cost += 100; 16 } 17 } 18 printf("%d\n", cost); 19}

となります。変更に強いということもありますが、読み易さの観点から見ても、 4 とか 3 とか唐突にでてくるより、このように名前を付けた方が意味がすっと通るようになるのではないかと思います。

これでもしトッピングの数が 4 種類、 5 種類、 100 種類となったとしても、一番上の NUM_TOPPING を変更するだけで済みます。

投稿2018/07/16 03:59

Eki

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

andor

2018/07/16 04:52

丁寧なご回答誠にありがとうございました。ご回答のおかげで、無事に問題を解決することができました。 一つ一つ段階を追ってわかりやすく解説してくれたので、初心者の僕にでもすぐに理解でき、ためになりました。本当にありがとうございました。
andor

2018/07/16 04:52

また機会があればよろしくお願いします。
guest

0

まず、文字列を入れる配列を定義しておきます

char bff[80];

これで、最大80文字の文字を入れることができます
文字列としては、終端符号'\0'が入るので、最大79文字となります

次に、キーボードから文字列を入力する関数です

fgets(bff,80,stdin);

これで、キーボードからタイプした文字列が bff に入って返されます

投稿2018/07/16 03:11

y_waiwai

総合スコア87784

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

andor

2018/07/16 04:53 編集

丁寧なご回答誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問