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

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

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

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Windows 7

Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。

Q&A

解決済

2回答

3955閲覧

C言語でカレンダーを作成しているが国民の休日の判定の部分でstack overflowになってしまう

sonozaki_SZ

総合スコア28

C

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Windows 7

Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。

0グッド

0クリップ

投稿2017/06/18 16:54

OS: Windows 7 64bit
開発環境: Visual Studio 2017
ソリューション:空のプロジェクト
デバッガー: ローカル Windowsデバッガー x86

C言語

1 2**calendar.h** 3 4#ifndef CALENDAR_H 5#define CALENDAR_H 6 7#define STR_LEN 8 /* 文字列の長さ */ 8#define P_HOLIDAY 1 /* 祝日 */ 9#define O_HOLIDAY 2 /* 振替休日 */ 10#define N_HOLIDAY 3 /* 国民の休日 */ 11 12void read_year_month(int *y, int *m); /* 年と月の入力と入力値の判定後に適正値を引数に代入 */ 13void print_calendar(int *y, int *m); /* カレンダーの表示 */ 14 15#endif

C言語

1 2**main.c** 3 4#include <stdio.h> 5#include "calendar.h" 6 7int main(void) 8{ 9 int year; /* 年 */ 10 int month; /* 月 */ 11 12 puts("年、月を入力後にその月のカレンダーを表示します"); 13 read_year_month(&year, &month); /* 年と月の入力と入力値の判定後に適正値を引数に代入 */ 14 print_calendar(&year, &month); /* カレンダーの表示 */ 15}

C言語

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

C言語

1 2**print_calendar.c** 3 4#include <stdio.h> 5#include "calendar.h" 6 7int c_lday(int y, int m); /* 末日を求める関数 */ 8int c_dow(int y, int m, int d); /* 任意の日付の曜日を求める関数 */ 9int d_holiday(int y, int m, int d, int w); /* 国民の祝日、振替休日、国民の休日の判定をする関数 */ 10void print_holiday(int y, int m, int d, int w); /* 国民の祝日を表示する関数 */ 11void day_before(int *y, int *m, int *d, int *w);/* 前日を返す関数 */ 12void day_next(int *y, int *m, int *d, int *w); /* 翌日を返す関数 */ 13 14void print_calendar(int *y, int *m) 15{ 16 int day; /* 日にち */ 17 int dow; /* 曜日 */ 18 int const lday = c_lday(*y, *m); /* 末日 */ 19 const char p_dow[7][3] = { "日", "月", "火", "水", "木", "金", "土" }; /* 曜日表示用 */ 20 int hol_flg; /* 祝日、振替休日、国民の休日フラグ */ 21 hol_flg = 0; 22 23 /* 初日の曜日を求める */ 24 day = 1; 25 dow = c_dow(*y, *m, day); 26 /* カレンダーの表示 */ 27 puts("****************************"); 28 printf("%7d年%2d月のカレンダー\n", *y, *m); 29 puts("****************************"); 30 for (day ; day <= lday; day++) /* 初日から末日までループ */ 31 { 32 hol_flg = d_holiday(*y, *m, day, dow); /* 国民の祝日、振替休日、国民の休日の判定 */ 33 printf("%2d日 (%s) ", day, p_dow[dow]); 34 if (hol_flg == P_HOLIDAY) 35 { 36 print_holiday(*y, *m, day, dow); 37 } 38 else if (hol_flg == O_HOLIDAY) 39 { 40 puts("振替休日"); 41 } 42 else if (hol_flg == N_HOLIDAY) 43 { 44 puts("国民の休日"); 45 } 46 else 47 { 48 putchar('\n'); 49 } 50 dow++; 51 if (dow == 7) 52 { 53 dow = 0; 54 } 55 else 56 { 57 /* NOP */ 58 } 59 60 } 61 getchar(); /* コンソールの終了待機処理 */ 62} 63 64int c_lday(int y, int m) 65{ 66 if (m == 2) /* 2月なら */ 67 { 68 /* うるう年の判定 */ 69 if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) /* 400で割り切れるか、4で割り切れるが100で割り切れない年だったら */ 70 { 71 return 29; 72 } 73 else 74 { 75 return 28; 76 } 77 } 78 else if (m == 4 || m == 6 || m == 9 || m == 11) /* 直前までの条件を除く4月、6月、9月、11月だったら */ 79 { 80 return 30; 81 } 82 else 83 { 84 return 31; 85 } 86} 87 88int c_dow(int y, int m, int d) 89{ 90 if (m == 1 || m == 2) 91 { 92 y--; 93 m += 12; 94 return (y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + d) % 7; 95 } 96 else 97 { 98 return (y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + d) % 7; 99 } 100} 101 102int d_holiday(int y, int m, int d, int w) 103{ 104 /* 引数で受け取った値をローカルにコピー */ 105 int buf_y, buf_y2; 106 int buf_m, buf_m2; 107 int buf_d, buf_d2; 108 int buf_w, buf_w2; 109 int flg; 110 buf_y = y; 111 buf_m = m; 112 buf_d = d; 113 buf_w = w; 114 flg = 0; 115 /* 国民の祝日の判定 */ 116 if ((buf_m == 1 && buf_d == 1) /* 1/1 元日 */ 117 || (buf_m == 1 && buf_w == 1 && 8 <= buf_d && buf_d <= 14) /* 1月第2月曜 成人の日 */ 118 || (buf_m == 2 && buf_d == 11) /* 2/11 建国記念の日 */ 119 || (buf_m == 3 && buf_d == (int)(20.8431 + 0.242194 120 * (buf_y - 1980) - (buf_y - 1980) / 4)) /* 3/20付近 春分の日 */ 121 || (buf_m == 4 && buf_d == 29) /* 4/29 昭和の日 */ 122 || (buf_m == 5 && buf_d == 3) /* 5/3 憲法記念日 */ 123 || (buf_m == 5 && buf_d == 4) /* 5/4 みどりの日 */ 124 || (buf_m == 5 && buf_d == 5) /* 5/5 こどもの日 */ 125 || (buf_m == 7 && buf_w == 1 && 15 <= buf_d && buf_d <= 21) /* 7月第3月曜 海の日 */ 126 || (buf_m == 8 && buf_d == 11) /* 8/11 山の日 */ 127 || (buf_m == 9 && buf_w == 1 && 15 <= buf_d && buf_d <= 21) /* 9月第3月曜 敬老の日 */ 128 || (buf_m == 9 && buf_d == (int)(23.2488 + 0.242194 129 * (buf_y - 1980) - (buf_y - 1980) / 4)) /* 9/23付近 秋分の日 */ 130 || (buf_m == 10 && buf_w == 1 && 8 <= buf_d && buf_d <= 14) /* 10月第2月曜 体育の日 */ 131 || (buf_m == 11 && buf_d == 3) /* 11/3 文化の日 */ 132 || (buf_m == 11 && buf_d == 23) /* 11/23 勤労感謝の日 */ 133 || (buf_m == 12 && buf_d == 23)) /* 12/23 天皇誕生日 */ 134 { 135 flg = P_HOLIDAY; /* 国民の祝日が確定 */ 136 } 137 /* 振替休日の判定 */ 138 /* 前日に祝日が続く限り前日を調べ祝日と日曜日が重なったら当日を振替休日にする */ 139 else if (buf_w != 0 && buf_w != 6) /* 当日が祝日でも土日でもなかったら */ 140 { 141 do 142 { 143 day_before(&buf_y, &buf_m, &buf_d, &buf_w); /* 前日 */ 144 if (d_holiday(buf_y, buf_m, buf_d, buf_w) == P_HOLIDAY && buf_w == 0) /* 国民の祝日かつ日曜日だったら */ 145 { 146 flg = O_HOLIDAY; /* 振替休日が確定 */ 147 } 148 else 149 { 150 /* NOP */ 151 } 152 } while (d_holiday(buf_y, buf_m, buf_d, buf_w) == P_HOLIDAY); /* 前日が祝日の間ループ */ 153 /* 国民の休日の判定 */ 154 if (flg == 0) 155 { 156 buf_y = buf_y2 = y; 157 buf_m = buf_m2 = m; 158 buf_d = buf_d2 = d; 159 buf_w = buf_w2 = w; 160 day_before(&buf_y, &buf_m, &buf_d, &buf_w); /* 前日 */ 161 day_next(&buf_y2, &buf_m2, &buf_d2, &buf_w2); /* 翌日 */ 162 if (d_holiday(buf_y, buf_m, buf_d, buf_w) == P_HOLIDAY 163 && d_holiday(buf_y2, buf_m2, buf_d2, buf_w2) == P_HOLIDAY) 164 { 165 flg = N_HOLIDAY; /* 国民の休日が確定 */ 166 } 167 else 168 { 169 /* NOP */ 170 } 171 } 172 else 173 { 174 /* NOP */ 175 } 176 } 177 return flg; 178} 179 180void print_holiday(int y, int m, int d, int w) 181{ 182 if (m == 1 && d == 1) 183 { 184 puts("元日"); 185 } 186 else if (m == 1 && w == 1 && 8 <= d && d <= 14) 187 { 188 puts("成人の日"); 189 } 190 else if (m == 2 && d == 11) 191 { 192 puts("建国記念の日"); 193 } 194 else if (m == 3 && d == (int)(20.8431 + 0.242194 195 * (y - 1980) - (y - 1980) / 4)) 196 { 197 puts("春分の日"); 198 } 199 else if (m == 4 && d == 29) 200 { 201 puts("昭和の日"); 202 } 203 else if (m == 5 && d == 3) 204 { 205 puts("憲法記念日"); 206 } 207 else if (m == 5 && d == 4) 208 { 209 puts("みどりの日"); 210 } 211 else if (m == 5 && d == 5) 212 { 213 puts("こどもの日"); 214 } 215 else if (m == 7 && w == 1 && 15 <= d && d <= 21) 216 { 217 puts("海の日"); 218 } 219 else if (m == 8 && d == 11) 220 { 221 puts("山の日"); 222 } 223 else if (m == 9 && w == 1 && 15 <= d && d <= 21) 224 { 225 puts("敬老の日"); 226 } 227 else if (m == 9 && d == (int)(23.2488 + 0.242194 228 * (y - 1980) - (y - 1980) / 4)) 229 { 230 puts("秋分の日"); 231 } 232 else if (m == 10 && w == 1 && 8 <= d && d <= 14) 233 { 234 puts("体育の日"); 235 } 236 else if (m == 11 && d == 3) 237 { 238 puts("文化の日"); 239 } 240 else if (m == 11 && d == 23) 241 { 242 puts("勤労感謝の日"); 243 } 244 else if (m == 12 && d == 23) 245 { 246 puts("天皇誕生日"); 247 } 248 else 249 { 250 puts("エラー"); 251 } 252} 253 254void day_before(int *y, int *m, int *d, int *w) 255{ 256 if (*m == 1 && *d == 1) /* 1月1日だったら */ 257 { 258 /* 前年の12月31日 */ 259 (*y)--; 260 *m = 12; 261 *d = 31; 262 } 263 else if (*d == 1) /* 1日だったら */ 264 { 265 /* 前月の末日 */ 266 (*m)--; 267 *d = c_lday(*y, *m); 268 } 269 else 270 { 271 (*d)--; 272 } 273 /* 前日の曜日を求める処理 */ 274 if (*w == 0) 275 { 276 *w = 6; 277 } 278 else 279 { 280 (*w)--; 281 } 282} 283 284void day_next(int *y, int *m, int *d, int *w) 285{ 286 if (*m == 12 && *d == 31) /* 12月31日だったら */ 287 { 288 /* 翌年の1月1日 */ 289 (*y)++; 290 *m = 1; 291 *d = 1; 292 } 293 else if (*d == c_lday(*y, *m)) /* 直前までの条件を除く末日だったら */ 294 { 295 /* 翌月の1日 */ 296 (*m)++; 297 *d = 1; 298 } 299 else 300 { 301 (*d)++; 302 } 303 /* 翌日の曜日を求める処理 */ 304 if (*w == 6) 305 { 306 *w = 0; 307 } 308 else 309 { 310 (*w)++; 311 } 312}

C言語

1 2**実行結果** 3 4年、月を入力後にその月のカレンダーを表示します 5年を20162099の範囲、月を112の範囲で入力して下さい 6年と月の間を半角スペースで区切って下さい 7:2016 1/2099 12 8年と月の入力:2020 2 9**************************** 10 20202月のカレンダー 11**************************** 12 1() 13 2() 14 3() 15 4() 16 5() 17 6() 18 7() 19 8() 20 9() 2110() 2211() 建国記念の日 23ここでstack overflow

修正箇所とどのように修正したらいいか教えていただけるとありがたいです。

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

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

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

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

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

guest

回答2

0

ベストアンサー

if (d_holiday(buf_y, buf_m, buf_d, buf_w) == P_HOLIDAY && d_holiday(buf_y2, buf_m2, buf_d2, buf_w2) == P_HOLIDAY)の部分が問題でしょうか...?

12日の祝日判定をするためには、12日は国民の祝日でもなく振替休日でもないので「国民の休日」判定に進み、それには11日と13日の祝日判定が必要で、11日は祝日(建国記念の日)のため13日の祝日判定が行われるが、13日は「国民の祝日」でないため、次に「振替休日」判定に進み、ここで前日(つまり12日)の祝日判定が必要になり...という風に終わらない再帰が起こってしまうのではないでしょうか?

国民の祝日に関するルールを十分調べたわけではないので参考程度ですが、たとえば「国民の休日」判定であれば、前後が「国民の祝日」であるか分かればよい(つまり「休日」を除く)のですから(この認識で合ってましたっけ?)、「国民の休日」判定の際の前後祝日判定の場合は「国民の祝日」判定だけを行うようにする、などはどうでしょう。

※何だかやたらややこしい回答になってしまいました。すみません...

投稿2017/06/18 18:56

Bongo

総合スコア10807

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

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

sonozaki_SZ

2017/06/19 02:24 編集

回答ありがとうございます。 つまり国民の休日判定を行う際は国民の祝日判定のみの関数を作ってそれを呼び出した方が良いということでしょうか。 もし再帰で出来る方法があればもう少しヒントをくださると嬉しいです。 国民の休日の認識はそれであってると思います。 ただ注意しないといけないのは、仮に日曜と火曜が国民の祝日であっても月曜は国民の休日ではなく振替休日になるので先に振替判定を行う必要があるということでしょうか。
Bongo

2017/06/19 02:50 編集

一つの関数(d_holiday)だけで「国民の祝日」以降の判定の要不要に応じて処理を切り替える場合、d_holidayにフラグとなる引数を追加するのが簡単かと思います。 d_holidayを呼び出す際には、「国民の祝日」以降の判定が必要ならTRUE、不要ならFALSEを付けてやれば、d_holiday内部でフラグを見て異なる動作ができるでしょう。 判定の順序は、多分現在のまま(先に振替休日判定)でいいかと思います。 肝になるのは、振替休日判定は注目している日付より過去の日付の情報しか必要としないのに対して、国民の休日判定は未来の日付の情報が必要なため、この時に未来の日付について過去の日付(つまり現在)の情報を得なければならないことになるので、うまくパラドックスを回避するよう実装しなければならないということでしょうね。
sonozaki_SZ

2017/06/19 18:18

Bongo氏の修正案を実践してみたところ正常に動作するようになりました。
guest

0

31日間(あるいはもっと短い日数)を配列で管理した方が何かとトラブルがないと思います。
今回スタックオーバーフローが起きている根本的な原因は、そもそも再帰構造を取り入れていることです。
再帰構造は直感的ですが、実行を追いづらく、またリソースも圧迫しがちです。


コードで書くと、次のような感じでしょうか。突貫で書いてるのでエラーあると思いますが。
あと、デバッグしながらじゃないと作りづらいところはコメントではしょってます。

C

1#define SUNDAY 7 2 3void print_calendar(int *y, int *m) { 4 int weekDayOfThisMonth[32] = {0}; // 曜日を管理する変数 5 int dayOfThisMonth[32] = {0}; 6 int day; 7 int daysLength = c_lday(*y, *m); 8 9 // 曜日・祝日判定 10 for(day=1; day<=daysLength; day++) { 11 if(isSunday(*y, *m, day)) { 12 weekDayOfThisMonth[day] = SUNDAY; 13 } 14 if(isHoliday(*y, *m, day)) { 15 dayOfThisMonth[day] = P_HOLIDAY; 16 } 17 } 18 // 振替休日判定 19 for(day=1; day<=daysLength; day++) { 20 int workY = *y; 21 int workM = *m; 22 int workDay = day; 23 getBeforeDay(&workY, &workM, &workDay); 24 25 // バグだらけになって混乱させそうなので手書きで。 26 // 1. isHoliday(workY, workM, workDay)がfalseになるまで、 27 // getBeforeDayを用いて前日に戻り続ける。 28 // 2. その日から「今日」までの間に日曜があったら、「今日」は振替休日。 29 if(判定結果) { 30 daysOfThisMonth[day] = O_HOLIDAY; 31 } 32 } 33 // 国民の休日判定 34 for(day=1; day<=daysLength; day++) { 35 if(day==1) { 36 if(daysOfThisMonth[day+1]が祝日) { 37 // 前の月も考慮する必要がある 38 int beforeY = *y; 39 int beforeM = *m; 40 int beforeDay = day; 41 getBeforeDay(&beforeY, &beforeM, &beforeDay); 42 43 if(isHoliday(beforeY, beforeM, beforeDay)) { 44 daysOfThisMonth[day] = N_HOLIDAY; 45 } 46 } 47 } 48 else if(day==daysLength) { 49 // 次の月も考慮する必要がある 50 // 1日のパターンと同様なのでパス 51 } 52 else { 53 if( daysOfThisMonth[day-1]が祝日 54 && daysOfThisMonth[day+1]が祝日 ) { 55 56 daysOfThisMonth[day] = N_HOLIDAY; 57 } 58 } 59 } 60}

「今あるエラーを片付ける」ことより、「エラーが出ない構造を考える」ほうが重要です。

投稿2017/06/19 03:41

LouiS0616

総合スコア35660

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

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

sonozaki_SZ

2017/06/19 07:57

回答ありがとうございます。 次に何か作る際にはエラーが出にくく、デバッグしやすい設計を考慮して作ってみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問