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

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

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

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

Q&A

解決済

8回答

18778閲覧

文字列strの中に、文字cが含まれている個数

reotantan

総合スコア295

C

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

0グッド

0クリップ

投稿2015/09/05 11:17

文字列strの中に、文字cが含まれている個数(含まれていなければ0とする)を返すという問題でした。

自分でコードを書いてみたのですが、
どこかに問題があるみたいで、問題点を指摘してください。よろしくお願いします

#include <stdio.h> int str_chnum(const char *str, int c) { int count=0; while(*str++) { if(*str==c) { count++; } } if(count>=0) return count; else return 0; } int main(void) { char str[100]; int c; puts("type"); scanf("%s",str); puts("type c"); scanf("%d",&c ); str_chnum(str,c); return 0; }

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

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

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

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

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

guest

回答8

0

ベストアンサー

c は文字を読み込むので char 型の方が良いです。
合わせて scanf と str_chnum の引数も直してください。

1回目の scanf でエンターを入力した事により、
2回目の scanf が改行コードを読み込んでしまっています。
2回目の scanf の前で getchar(); して改行コードを読み飛ばしてください。

結果を出力していませんので、main 関数の return の手前で表示しましょう。

while の条件内で *str++ としていますので、
*str が '\0' でなければ、str のアドレス操作が行われます。

while の動作的には正しいのですが、
これでは、while 内の if 文で先頭の文字の分がカウント出来ません。

while の最後で str++; をしましょう。

str_chnum の戻り値は、単に count を返すだけで良いです。

修正例です。

c

1#include <stdio.h> 2 3int str_chnum(const char *str, char c) // 1 4{ 5 int count = 0; 6 7 while (*str) { // 4 8 if (*str == c) { 9 count++; 10 } 11 str++; // 4 12 } 13 14 return count; // 5 15} 16 17int main(void) 18{ 19 char str[100]; 20 char c; // 1 21 22 puts("type"); 23 scanf("%s", str); 24 25 getchar(); // 2 26 27 puts("type c"); 28 scanf("%c", &c ); // 1 29 30 printf("%d\n", str_chnum(str, c)); // 3 31 return 0; 32}

投稿2015/09/05 13:04

編集2015/09/05 17:01
umeaji

総合スコア101

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

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

0

ほかの方の指摘以外に・・・
str_chnum()でカウントを返しているようですが、main()で受け取っていませんね?
カウンタを表示させるような処理は要りませんか?
あと、最初のscanf()で読み込んだ後、バッファには改行が残ってしまうので、2回目のscanf()で改行が読み込まれます。なのでfgets()で文字列を読み込んでからscanf()で文字を読み込んだほうがいいと思います。
scanf()の注意については
scanf()の注意事項を参考に。
1文字読み込みならgetchar()かgetc()を使ったほうが確実です。

投稿2015/09/05 12:53

編集2015/09/05 13:03
cateye

総合スコア6851

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

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

reotantan

2015/09/05 13:24

returnで返された値をなにかに代入する必要があるという事ですか? if(count>=0) return count;のcountはmain関数にあるが、変数にはいっていないという事でしょうか?
cateye

2015/09/06 09:36

>countはmain関数にあるが ・・・がよく分からないのですが・・・ int count=0;は、関数int str_chnum(const char *str, int c)の中で宣言されているので、main()からは不可視です。 従って、main()側で受け取る変数(int ansなど)を宣言してans= str_chnum(str,c);という風に受け取らなければ、カウント値は分かりません。
philomagi

2015/09/06 10:13

>reotantanさん 「スコープ」の概念を確認してみてはいかがでしょう。 int型変数countは、str_chnum()でのみ使用される、言わば「使い捨ての変数」です。countという変数名およびその値は、main()では使用できません(=「main()からは不可視」)。 その使い捨ての値を他所でも利用するには、returnで呼び出し元(ここではmain())に教えてあげる必要があります。
reotantan

2015/09/06 13:07

説明が下手くそですいません、解説は理解できました。ありがとうございます
guest

0

horohoroさんの指摘に加えて、main関数内で

C

1puts("type c"); 2scanf("%d",&c );

としていますが、ここのscanfの書式指定子が誤っています。ここでは文字列中から探し出す文字を入力させるので、書式指定子は"%d"ではなく、"%c"にします。

投稿2015/09/05 12:26

archiver

総合スコア1557

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

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

reotantan

2015/09/06 13:08

書式指定子も理解が浅いようです、ご指摘ありがとうございます
guest

0

まずは、やりたい事をもう一度整理してみると良いと思います。

パッと見で矛盾していると思われる点をいくつか上げると

  • 「文字c」と言いつつ、プログラムでは「int型」で定義している
  • せっかく文字数のカウント結果を関数から「return」しているのに利用していない
  • 関数内の「while」ループでは、終了条件である「文字列の終端」の判定が無い

といった点がうまく動かない原因だと思います。

それと、これは経験を積むうちに身に着く事かも知れませんが、コーディングスタイルがあまり c的 ではないと思いました。

(これは個人的解釈ですが)コーディングスタイルには大きく分けて**フォーマット(書式、見た目)に関する部分とイディオム(変数や式の使い方、定石)**の2種類があります。

フォーマットについては、優れたフォーマッターがあるのでそちらに任せれば良く、おまり気にする必要はありません。

一方、イディオムを正しく身に着けることは、コーディング力を磨く上で非常に重要です。
いわゆる習慣の問題なので、可能な限り早い段階から意識的に身に着ける努力が必要です。

ここで イディオム と言っているのは、たとえば文字列を先頭から末尾まで1文字ずつ処理する仕方とか文字の比較の仕方などのことで、これらにはいわば定石とも言える書き方があり、それらはどれも洗練されていて無駄がありません。

ですので、まずは自分で一から考えてみるということ自体はとても重要なのですが、一旦動くようになったら、優れた実装例をよく読み(コードリーディング)、単に暗記するのではなく、なぜその書き方が適切なのかという センス を磨くようにお勧めします。

と言いつつ、実は私自身もまだ素人なので…
あまり参考にはならないと思いますが、なるべく元のソースを変えずに修正し、明らかに不要な行を削除したものを記載しておきます。

c

1#include <stdio.h> 2 3int str_chnum(const char *str, const char c) 4{ 5 int count=0; 6 7 while(*str != '\0') // 誤記訂正。cateyeさんありがとうございます! 8 { 9 if(*str++ == c) count++; 10 } 11 12 return count; 13} 14 15 16 17int main(void) 18{ 19 char str[100]; 20 char c; 21 22 puts("type"); 23 scanf("%[^\n]s",str); 24 puts("type c"); 25 scanf(" %*c%c", &c); 26 27 printf("Result : %d\n", str_chnum(str, c)); 28 29 return 0;

主な変更点は
0. 変数c を char型 へ変更
0. whileループは文字列の「終端文字 '\0' を検出するまで」に変更
0. カウント結果を返す前のif文による判定は実質的に意味がないので削除
0. 関数からの戻り値を表示するように変更

なお、scanf関数のフォーマット指定子スキャン集合指定子を追加している意味は下記の通りです。(ご参考)
変数str:"%s""%[¥\n]s":改行以外は読み込む(つまり空白を含めて入力可)
変数c :"%c""%*c%c" :直前で実行したscanfのためにバッファに残った改行を読み飛ばす
このようにすると、空白を含む文字列の中に、指定した文字(空白も可)がいくつ含まれているかを検索可能になります。

以上、ご参考になれば幸いです。

投稿2015/09/05 16:08

編集2015/09/06 10:24
pi-chan

総合スコア5936

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

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

cateye

2015/09/06 09:49 編集

while(*str++ != '\0')←ここで先頭の1文字を読み飛ばしているので・・・ if(*str==c) count++;←2文字目からの検索になっている? # あと・・・好みの問題もあるでしょうが{}はたとえ実行行が一行でもつけておいたほうがいいです。
pi-chan

2015/09/06 10:24

cateyeさん、ご指摘ありがとうございます!回答内容を、修正しておきました。
reotantan

2015/09/06 13:04

なるほど、確かに定石を学ぶ事はあらゆるものにおいても重要ですね。 ありがとうございます
guest

0

c

1#include <stdio.h> 2#include <string.h> 3 4int str_chnum(const char *str, const char c) { 5 int count = 0; 6 if (str != NULL) { 7 const char * p = NULL; 8 for (p = str; *p; p++) { 9 if (c == *p) { 10 count++; 11 } 12 } 13 } 14 return count; 15} 16 17int main(void) { 18 char str[100] = ""; 19 char c = '\0'; 20 21 puts("type str"); 22 // 一行を読み込む 23 fgets(str, sizeof(str) - 1, stdin); 24 // 末尾の改行を削除する。 25 char * p = strchr(str, '\n'); 26 if (p) { 27 *p = '\0'; 28 } 29 30 puts("type c"); 31 scanf(" %c",&c); 32 33 printf("%d 個の '%c' が \"%s\" に含まれています。\n", str_chnum(str, c), c, str); 34 return 0; 35}

scanf では 空白を含む行全体をよむことができないので、
fscanf をつかっています。また str[] のサイズを超えて文字が書き込まれてしないようにもしています。

参考

また、fgets(), scanf() 時に CTRL-D が入れられた時でも動作するように、str, c は初期値を入れています。

実行例:

$ ./a.out type str 1234 5678 1234 type c 1 2 個の '1' が "1234 5678 1234" に含まれています。 $ ./a.out type str <--- CTRL-D を押下する type c 0 個の '' が "" に含まれています。

投稿2015/09/05 13:32

katoy

総合スコア22324

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

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

reotantan

2015/09/06 13:05

なるほど,fscanf使ってみます。 ありがとうございました
guest

0

「作ったプログラムが意図通りに動かない。」

こういう時こそデバッガの出番です。
プログラムを書く上でデバッグ作業は避けて通れない面倒な作業と言えます。
ただ、この作業が出来ないようではプログラマとしてはちょっと問題ありです。

参考:
Eclipseでデバッグする
Xcodeでデバッグする
VisualStudioでデバッグする

参考:
EclipseにCDTを追加する手順

XCodeやVisualStudioでのデバッグは特に環境を用意する必要がないので、WindowsやMacの場合は楽に導入できると思います。
Linuxの場合にはEclipseを使うかgdbを使うかで状況が異なりますが、ちょっと手間に感じるとしてもEclipseを用意した方が実際のデバッグ作業が楽になります。

デバッガを使わない方法としては、printfデバッグのように途中の状況を表示させるデバッグ方法があります。
例えば、入力された結果が意図した内容か、関数に渡す値が意図した内容か、ということを実際にprintfで表示して確認するデバッグ方法です。
逐一動作を止めて確認できる状況ばかりではないので、場合によってはこういった方法も必要になります。

C

1int main(void) 2{ 3 char str[100]; 4 int c; 5 puts("type"); 6 scanf("%s",str); 7 puts("type c"); 8 scanf("%d",&c ); 9 10 //(debug print) 11 printf("debug: str = %s\n", str); 12 printf("debug: c = %x\n", c); 13 14 str_chnum(str,c); 15 return 0; 16}

実際にそういった方法を使い作業目的を覚えるのも勉強なので、いろいろ実践してみることをお勧めします。

投稿2015/09/05 12:25

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

reotantan

2015/09/06 13:10

デバッグ大切ですね、やり方をしっかり覚えます。 ありがとうございました。
guest

0

最初のwhileで、条件判断した後すぐにインクリメントしているので、whileの中のブロックではインクリメントしたあとの値になってしまいます。whileではなくforで条件判断とインクリメントをわければいいかなと思います。

C

1for (; *str; str++) { 2 // ... 3}

あと、見つからなければcountは0のままなので、最後のif分岐はいらないと思います。

投稿2015/09/05 12:23

raccy

総合スコア21735

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

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

reotantan

2015/09/06 13:10

はい、確かにそうですね。ありがとうございます
guest

0

while内でインクリメントしていますよね。
ってことは、最初のcとの比較は1文字目からでなく...(略)

投稿2015/09/05 12:11

horohoro

総合スコア490

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

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

reotantan

2015/09/06 13:11

そうですね、間違ったコードを書いてしまいました。 ご指摘ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問