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

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

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

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

Q&A

解決済

3回答

2196閲覧

C言語で関数の再起がうまくいかないのですがどこを修正したらいいでしょうか?

sonozaki_SZ

総合スコア28

C

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

0グッド

0クリップ

投稿2017/05/31 14:43

編集2017/06/01 10:44

下記のコードで適値以外の入力は再起させて数値の再入力を行いたいのですがうまくいきません

C言語

1calendar.h 2 3/* 関数プロトタイプ宣言 */ 4void read_year_month(int *y, int *m); /* 年と月の入力と入力された値の判定後に値を引数に代入 */ 5void d_days(int *ds, int y, int m); /* 日数の判定 */ 6void d_week_fday(int *w, int y, int m, int d); /* 月の初日の曜日を判定 */

C言語

1main.c 2 3#include <stdio.h> 4#include "calendar.h" 5 6int main(void) 7{ 8 int year, month, days, week_fday; 9 10 puts("年と月を入力後にその月のカレンダーを表示します"); 11 12 read_year_month(&year, &month); 13 14 printf("%d\n", year); 15 printf("%d\n", month); 16 getchar(); 17}

C言語

1read_year_month.c 2 3#include <stdio.h> 4 5#define STR_LEN 8 6 7void read_year_month(int *y, int *m) 8{ 9 char str[STR_LEN]; 10 int i; 11 12 fflush(stdin); /* 入力バッファのクリア */ 13 14 puts("年を2016〜2099の範囲、月を1〜12の範囲で入力して下さい"); 15 puts("年と月の間を半角スペースで区切って下さい"); 16 puts("例:2016 1/2099 12"); 17 printf("年と月の入力:"); 18 fgets(str, sizeof(str), stdin); 19 20 /* 数字以外の値が入力されたら再起 */ 21 for(i = 0; i < STR_LEN-1; i++) 22 { 23 if(i != 4) 24 { 25 if(str[i] < '0' && '9' < str[i]) 26 { 27 puts("入力された値は不正です"); 28 puts("入力し直して下さい"); 29 read_year_month(y, m); 30 } 31 else 32 { 33 /* NOP */ 34 } 35 } 36 else 37 { 38 /* NOP */ 39 } 40 } 41 sscanf(str, "%d %d", y, m); /* 文字型を整数型に変換して変数*y*mに代入 */ 42 /* 範囲外の数値が入力されたら再起 */ 43 if((*y < 2016 && 2099 < *y) || (*m < 1 && 12 < *m)) 44 { 45 puts("入力された値は不正です"); 46 puts("入力し直して下さい"); 47 read_year_month(y, m); 48 } 49 else 50 { 51 fflush(stdin); 52 } 53}
実行結果_1 年と月を入力後にその月のカレンダーを表示します 年を2016〜2099の範囲、月を1〜12の範囲で入力して下さい 年と月の間を半角スペースで区切って下さい 例:2016 1/2099 12 年と月の入力:abcdefghi 1408891440 32767
実行結果_2 年と月を入力後にその月のカレンダーを表示します 年を2016〜2099の範囲、月を1〜12の範囲で入力して下さい 年と月の間を半角スペースで区切って下さい 例:2016 1/2099 12 年と月の入力:1234 56 1234 56

2017/06/01 19:44 追記
皆様の意見を参考にしてread_year_month関数を下記のように修正したら再起がうまく機能するようになりました

C言語

1#include <stdio.h> 2#include <stdbool.h> 3#include "calendar.h" 4 5void read_year_month(int *y, int *m) 6{ 7 char str[STR_LEN]; 8 int i; 9 bool err_flg; 10 11 /* 入力バッファのクリア */ 12 fflush(stdin); 13 14 puts("年を2016~2099の範囲、月を1~12の範囲で入力して下さい"); 15 puts("年と月の間を半角スペースで区切って下さい"); 16 puts("例:2016 1/2099 12"); 17 printf("年と月の入力:"); 18 fgets(str, sizeof(str), stdin); 19 fflush(stdin); 20 21 /* 数字以外の値が入力された場合にエラー処理 */ 22 for (i = 0; i < STR_LEN-1; i++) 23 { 24 if (('0' < str[i] && str[i] < '9') || (str[i] == ' ' || str[i] == '\n')) 25 { 26 err_flg = false; 27 } 28 else 29 { 30 err_flg = true; 31 } 32 } 33 sscanf(str, "%d %d", y, m); /* 文字型を整数型に変換して変数y, mに値を代入 */ 34 /* 範囲外の値が入力されたらエラー処理 */ 35 if ((*y < 2016 || 2099 < *y) || (*m < 1 || 12 < *m)) 36 { 37 err_flg = true; 38 } 39 else 40 { 41 err_flg = false; 42 } 43 if (err_flg == true) 44 { 45 puts("入力された値は不正です"); 46 puts("入力し直して下さい"); 47 read_year_month(y, m); 48 } 49 else 50 { 51 /* NOP */ 52 } 53} 54

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

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

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

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

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

guest

回答3

0

ベストアンサー

入力が正常か異常か管理するフラグの変数を作って、最後にフラグが正常だったらreturn、異常だったら再帰呼び出しとすればいいと思います。イメージとしては下記のような感じです。

C

1include <stdbool.h> 2void read_year_month(int *y, int *m) 3{ 4 bool flag = true; 5 // ...色々処理 6 if (/* 駄目なのかチェック */) { 7 flag = false; 8 } 9 if (flag) { 10 // ...色々処理 11 if (/* 駄目なのかチェック */) { 12 flag = false; 13 } 14 } 15 if (flag) { 16 // ...色々処理 17 if (/* 駄目なのかチェック */) { 18 flag = false; 19 } 20 } 21 // ... 22 if (flag) return; 23 read_year_month(y, m); 24}

こつは、再帰呼び出しを最後の一回のみにすると言うことです。そうすることで。

  • 再帰呼び出し側での処理が完了した後に余計な処理が走ることがなくなる。
  • 末尾呼び出し最適化でスタックが消費されなくなる。

という、利点が生まれます。

その他、fflush(stdin)の迷信を信じている点が気になりますが、処理系によってはうまく動く場合もありますので、気にしないでおきます。

投稿2017/05/31 21:47

raccy

総合スコア21735

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

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

sonozaki_SZ

2017/06/01 11:03

fflush(stdin)の代わりにどういう処理をしたら良いでしょうか?
raccy

2017/06/01 11:56

リンク先のページに解説があるとおり、Cの標準だけでfflush(stdin)相当の動作させることはできません。本当に標準入力のバッファクリアを行いたいのであれば、処理系によってマクロで分岐していくしかないと思います。ただ、fflush(stdin)に対応した処理系もおおいようですので、手元で試す程度であれば、問題ありません。Cの標準ではないということだけ、覚えておけばいいと思います。 もし、移植性を考えて、標準の動作をさせたいのであれば、空読みをするぐらいしかありません。標準入力の空読みは検索するといくつかサンプルが出てくると思います。ただ、空読みとバッファクリアでは意味が異なり、動作も異なります。実際に動かして比べてみるといいでしょう。
guest

0

本件の例でいえば「関数を呼び出してそこから帰ってきたら、その関数の機能は完遂されている」点に注意しないといけない・・・といったところでしょうか。

上の関数は「入力とそのチェックをする」機能を持っていますね?チェック途中にNGを検出したら再帰呼び出しをしていますが、その再帰呼び出しから帰ってきた時点では「入力もチェックも完遂されている」のでそれ以上なにもすることがないと考えるべきです。

実際のコードでは再帰呼び出しから返ってきた後も呼び出し元の関数の処理(ループによるチェック)を継続してしまっていますので、そこが問題です。再帰呼び出しから返ってきた後は即座にreturnすべきです。

投稿2017/05/31 15:03

KSwordOfHaste

総合スコア18394

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

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

0

月入力は01~12としました。2016 01/2099 12

#include <stdio.h> #define STR_LEN 9 /* 関数プロトタイプ宣言 */ int read_year_month(int *y, int *m); /* 年と月の入力と入力された値の判定後に値を引数に代入 */ void d_days(int *ds, int y, int m); /* 日数の判定 */ void d_week_fday(int *w, int y, int m, int d); /* 月の初日の曜日を判定 */ int main(void) { int year, month, days, week_fday; puts("年と月を入力後にその月のカレンダーを表示します"); read_year_month(&year, &month); printf("%d\n", year); printf("%d\n", month); getchar(); } int read_year_month(int *y, int *m) { char str[STR_LEN]; int i; fflush(stdin); /* 入力バッファのクリア */ puts("年を2016〜2099の範囲、月を1〜12の範囲で入力して下さい"); puts("年と月の間を半角スペースで区切って下さい"); puts("例:2016 1/2099 12"); printf("年と月の入力:"); fgets(str, sizeof(str), stdin); /* 数字以外の値が入力されたら再起 */ for(i = 0; i < STR_LEN-2; i++) { if(i != 4) { if(str[i] < '0' || '9' < str[i]) { puts("入力された値は不正です"); puts("入力し直して下さい"); return (read_year_month(y, m)); } else { /* NOP */ } } else { /* NOP */ } } sscanf(str, "%d %d", y, m); /* 文字型を整数型に変換して変数*y*mに代入 */ /* 範囲外の数値が入力されたら再起 */ if((*y < 2016 || 2099 < *y) || (*m < 1 || 12 < *m)) { puts("入力された値は不正です"); puts("入力し直して下さい"); return (read_year_month(y, m)); } else { fflush(stdin); } return 0; }

投稿2017/06/01 02:51

編集2017/06/01 22:33
A.Ichi

総合スコア4070

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問