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

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

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

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

Q&A

6回答

11154閲覧

C言語で12ヶ月表示カレンダー【もっとスッキリさせたい】

lago_sakmof

総合スコア5

C

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

0グッド

0クリップ

投稿2015/06/11 13:24

編集2015/06/11 13:26

###前提・実現したいこと
先日は質問への回答ありがとうございました。
続いてC言語で、入力された年の1年カレンダーを表示するプログラムを作成しています。

###発生している問題・エラーメッセージ
入力値からカレンダーの出力もうまくいっているのですが、
日数の書き出し部分あたりに同じコードを何度も使ってしまっています。
この繰り返しの部分をもっとスッキリさせる方法はないでしょうか。

下記コード上に記述した部分が繰り返し使っているので関数にしてしまいたいのですが、
すでに関数を使ってしまっているので関数内関数?になってしまうためかうまくいきません。
(C言語で関数内関数は使えないとどこかのサイトで見ました)

関数の中で関数を使う方法や、
それ以外の方法がありましたらアドバイスいただけると幸いです。

###ソースコード

lang

1#pragma warning(disable:4996) 2#include <stdio.h> 3#include <stdlib.h> 4 5//プロトタイプ宣言 6int uru_cal(int u_y); 7int dow(int wy, int wm); 8void writeblank(int b_dos); 9int writedays(int eom, int res_dow); 10int writedays2(int eom, int res_dow, int now); 11 12 13struct result{ //構造体result宣言 14 int res_month; 15 int res_dow; 16 int eom; 17 int now; 18}; 19 20int main(void){ 21 22 int year = 0; 23 int month = 0; 24 int uru = 0; 25 int i = 0; 26 int j = 0; 27 int res_uru = 0; 28 int res_dow = 0; 29 int now = 0; 30 int ent = 0; 31 32 printf("表示したい年を入力してください(1900年~) → \n"); 33 scanf("%d", &year); 34 35 if (1900 >= year){ //入力チェック 36 printf("数値が正しくありません。"); 37 exit(1); 38 } 39 40 41 res_uru = uru_cal(year); //うるう年の計算 42 43 struct result month_a[12] = { //構造体の初期化 44 { 1, res_dow, 31, now }, //[0] 45 { 2, res_dow, res_uru, now },//[1]うるう関数からの戻り値を代入 46 { 3, res_dow, 31, now }, //[2] 47 { 4, res_dow, 30, now }, //[3] 48 { 5, res_dow, 31, now }, //[4] 49 { 6, res_dow, 30, now }, //[5] 50 { 7, res_dow, 31, now }, //[6] 51 { 8, res_dow, 31, now }, //[7] 52 { 9, res_dow, 30, now }, //[8] 53 { 10, res_dow, 31, now }, //[9] 54 { 11, res_dow, 30, now }, //[10] 55 { 12, res_dow, 31, now }, //[11] 56 }; 57 58 for (i = 0, month = 0; i < 11; i++, month++){ //配列にres_dowを代入 59 month_a[i].res_dow = dow(year, month); //dow関数 60 61 } 62 63 printf("=============%d年のカレンダー=============\n", year); 64 65 66 /*1月~3月*/ 67 for (j = 0; j < 3; j++){ 68 printf(" [%d月] ", month_a[j].res_month); 69 } 70 71 printf("\n"); 72 73 74 printf(" 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土\n"); 75 for (j = 0; j < 3; j++){ 76 77 /*繰り返し使っている部分↓*/ 78 writeblank(month_a[j].res_dow); //月の始めのブランクを出力 79 80 month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); //eomとres_dowを使って日付を出力 81 82 ent++; 83 84 if (ent == 3){ 85 printf("\n"); 86 ent = 0; 87 } 88 else{ 89 printf(" "); 90 } 91 /*繰り返し使っている部分↑*/ 92 } 93 94 for (i = 0; i < 5; i++){ 95 96 /*繰り返し使っている部分↓*/ 97 for (j = 0; j < 3; j++){ 98 99 month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力 100 101 ent++; 102 103 if (ent == 3){ 104 printf("\n"); 105 ent = 0; 106 } 107 else{ 108 printf(" "); 109 } 110 } 111 /*繰り返し使っている部分↑*/ 112 } 113 114 /*4月~5月*/ 115 for (j = 3; j < 6; j++){ 116 printf(" [%d月] ", month_a[j].res_month); 117 } 118 119 printf("\n"); 120 121 122 printf(" 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土\n"); 123 124 for (j = 3; j < 6; j++){ 125 126 /*第1週目*3か月分*/ 127 writeblank(month_a[j].res_dow); //月の始めのブランクを出力 128 129 month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); //eomとres_dowを使って日付を出力 130 131 ent++; 132 133 if (ent == 3){ 134 printf("\n"); 135 ent = 0; 136 } 137 else{ 138 printf(" "); 139 } 140 } 141 142 for (i = 0; i < 5; i++){ 143 144 for (j = 3; j < 6; j++){ 145 146 month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力 147 148 ent++; 149 150 if (ent == 3){ 151 printf("\n"); 152 ent = 0; 153 } 154 else{ 155 printf(" "); 156 } 157 } 158 } 159 160 /*7月~9月*/ 161 for (j = 6; j < 9; j++){ 162 printf(" [%d月] ", month_a[j].res_month); 163 } 164 165 printf("\n"); 166 167 printf(" 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土\n"); 168 169 for (j = 6; j < 9; j++){ 170 171 /*第1週目*3か月分*/ 172 writeblank(month_a[j].res_dow); //月の始めのブランクを出力 173 174 month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); //eomとres_dowを使って日付を出力 175 176 ent++; 177 178 if (ent == 3){ 179 printf("\n"); 180 ent = 0; 181 } 182 else{ 183 printf(" "); 184 } 185 } 186 187 for (i = 0; i < 5; i++){ 188 189 for (j = 6; j < 9; j++){ 190 191 month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力 192 193 ent++; 194 195 if (ent == 3){ 196 printf("\n"); 197 ent = 0; 198 } 199 else{ 200 printf(" "); 201 } 202 } 203 } 204 205 /*10月~12月*/ 206 for (j = 9; j < 12; j++){ 207 printf(" [%d月] ", month_a[j].res_month); 208 } 209 210 printf("\n"); 211 212 213 printf(" 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土\n"); 214 215 for (j = 9; j < 12; j++){ 216 217 /*第1週目*3か月分*/ 218 writeblank(month_a[j].res_dow); //月の始めのブランクを出力 219 220 month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); //eomとres_dowを使って日付を出力 221 222 ent++; 223 224 if (ent == 3){ 225 printf("\n"); 226 ent = 0; 227 } 228 else{ 229 printf(" "); 230 } 231 } 232 233 for (i = 0; i < 5; i++){ 234 235 for (j = 9; j < 12; j++){ 236 237 month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力 238 239 ent++; 240 241 if (ent == 3){ 242 printf("\n"); 243 ent = 0; 244 } 245 else{ 246 printf(" "); 247 } 248 } 249 } 250 251 252 253} //main終わり 254 255//関数宣言 256int uru_cal(int u_y){ //関数うるう計算 257 258 int res_uru = 0; 259 260 if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ //うるう年の判定 261 res_uru = 29; //月の終わりの日付を29に設定 262 263 } 264 else{ 265 res_uru = 28; //月の終わりの日付を28に設定 266 } 267 268 return res_uru; 269}; 270 271int dow(int wy, int wm){ //dow関数 272 273 int dayofweek = 0; 274 wm += 1; 275 276 277 if (wm == 1 || wm == 2) { 278 wy--; 279 wm += 12; 280 } 281 282 return dayofweek = (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7; //毎月1日の曜日を計算; 283} 284 285 286void writeblank(int b_dow){ //writeblank関数 287 288 289 290 int blank = 0; 291 292 for (blank = 0; blank < b_dow; blank++){ //先頭曜日まで空白 293 printf(" "); 294 } 295} 296 297 298int writedays(int eom, int res_dow){ 299 300 int day = 0; 301 int nextday = 0; 302 303 for (day = 1; day <= 7 - res_dow; day++){ 304 printf(" %2d", day); 305 nextday = day; 306 } 307 308 return nextday; //nextdayを返す 309} 310 311int writedays2(int eom, int res_dow, int now){ 312 int day = 0; 313 int r = 0; 314 int e = 0; 315 int nextday = now; 316 317 318 319 for (day = nextday + 1, r = 0; r <= 6; day++, r++){ 320 321 if (day <= eom){ 322 printf(" %2d", day); 323 nextday = day; 324 } 325 else{ 326 printf(" "); 327 } 328 } 329 330 331 return nextday; 332} 333

###補足情報(言語/FW/ツール等のバージョンなど)

※課題として構造体をあえて使っています

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

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

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

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

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

guest

回答6

0

C言語では関数内に関数を定義することはできませんが関数内でほかの関数を呼ぶことはいくらでもできます。

今回の場合、関数化するよりも重複処理をまとめるので十分です。
ループの値と配列の組み合わせ方、月の最初の日までの扱いがポイントです。

もっと短くできますが、趣旨がずれるので考えてみてください。
あと12月が常に日曜から始まるのもあえて残してあります。

lang

1#include <stdio.h> 2#include <stdlib.h> 3 4//プロトタイプ宣言 5int uru_cal(int u_y); 6int dow(int wy, int wm); 7int writedays(int eom, int now); 8 9 10struct result{ //構造体result宣言 11 int res_month; 12 int res_dow; 13 int eom; 14 int now; 15}; 16 17int main(void){ 18 19 int year = 0; 20 int month = 0; 21 int uru = 0; 22 int i = 0; 23 int j = 0; 24 int m = 0; 25 int res_uru = 0; 26 int res_dow = 0; 27 int now = 0; 28 29 printf("表示したい年を入力してください(1900年~) → \n"); 30 scanf("%d", &year); 31 32 if (1900 >= year){ //入力チェック 33 printf("数値が正しくありません。"); 34 exit(1); 35 } 36 37 38 res_uru = uru_cal(year); //うるう年の計算 39 40 struct result month_a[12] = { //構造体の初期化 41 { 1, res_dow, 31, now }, //[0] 42 { 2, res_dow, res_uru, now },//[1]うるう関数からの戻り値を代入 43 { 3, res_dow, 31, now }, //[2] 44 { 4, res_dow, 30, now }, //[3] 45 { 5, res_dow, 31, now }, //[4] 46 { 6, res_dow, 30, now }, //[5] 47 { 7, res_dow, 31, now }, //[6] 48 { 8, res_dow, 31, now }, //[7] 49 { 9, res_dow, 30, now }, //[8] 50 { 10, res_dow, 31, now }, //[9] 51 { 11, res_dow, 30, now }, //[10] 52 { 12, res_dow, 31, now }, //[11] 53 }; 54 55 for (i = 0, month = 0; i < 11; i++, month++){ //配列にres_dowを代入 56 month_a[i].now = -dow(year, month); //dow関数 57 } 58 59 printf("=============%d年のカレンダー=============\n", year); 60 61 for(m=0; m<12; m+=3){ 62 63 for (j = 0; j < 3; j++){ 64 printf(" [%d月] ", month_a[m+j].res_month); 65 } 66 67 printf("\n"); 68 69 printf(" 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土\n"); 70 71 for (i = 0; i < 6; i++){ 72 for (j = 0; j < 3; j++){ 73 74 month_a[m+j].now = writedays(month_a[m+j].eom, month_a[m+j].now); 75 76 if (j == 2){ 77 printf("\n"); 78 }else{ 79 printf(" "); 80 } 81 } 82 } 83 } 84 85} //main終わり 86 87int uru_cal(int u_y){ //関数うるう計算 88 89 int res_uru = 0; 90 91 if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ //うるう年の判定 92 res_uru = 29; //月の終わりの日付を29に設定 93 94 } 95 else{ 96 res_uru = 28; //月の終わりの日付を28に設定 97 } 98 99 return res_uru; 100}; 101 102int dow(int wy, int wm){ //dow関数 103 104 int dayofweek = 0; 105 wm += 1; 106 107 108 if (wm == 1 || wm == 2) { 109 wy--; 110 wm += 12; 111 } 112 113 return dayofweek = (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7; //毎月1日の曜日を計算; 114} 115 116int writedays(int eom, int now){ 117 int day = 0; 118 int r = 0; 119 int e = 0; 120 int nextday = now; 121 122 for (day = nextday + 1, r = 0; r <= 6; day++, r++){ 123 124 if (1 <= day && day <= eom){ 125 printf(" %2d", day); 126 nextday = day; 127 } 128 else{ 129 printf(" "); 130 } 131 } 132 133 return nextday; 134}

プログラミングを始めたばかりの頃、僕もこんなんだったなぁ……

投稿2015/06/11 16:47

toki_td

総合スコア2850

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

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

lago_sakmof

2015/06/13 06:15

お返事が遅くなりました。 アドバイス有難うございます! じっくり読んで参考にさせていただきます。
guest

0

かなり昔の質問ですが、短く書けたので回答します.

最初にZellerの公式を用いて入力された年の1月1日の曜日を求めます。コンピューターでカレンダーを表示するときはこの公式を使っています。
それをもとにfor文で日にちを表示します。
次の月に行くとき、その月の日にちをもとに次の月は何曜日から始まるか計算しています。

3重for文を使っていますが
1個目:1月~12月までのループ
2個目:その月の行数 (最大値は6)
3個目:その月の列数 (曜日の7)

また、繰り返し使うもの(例えば、曜日など)は配列にまとめました。
閏年の判定による2月の日にちの計算のところも少し工夫しました。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#define BUFSIZE 256 6 7int check_leap(int); 8 9int main(void) 10{ 11 char buf[BUFSIZE]; 12 char week[7][10] = {"日","月","火","水","木","金","土"}; 13 int month_limit[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; 14 int year,month; 15 int week_number; 16 int i,j; 17 int num = 1; 18 int start; 19 20 fgets(buf,sizeof(buf),stdin); 21 year = strtol(buf,NULL,10); 22 month_limit[1] += check_leap(year); 23 year--; 24 week_number = (5 * year / 4 - year / 100 + year / 400 + 36) % 7; 25 start = week_number; 26 printf("\n"); 27 for(month = 1; month <= 12; month++){ 28 printf("%d月\n",month); 29 for(i = 0; i < 7; i++){ 30 printf("%s ",week[i]); 31 } 32 printf("\n"); 33 for(i = 0; i < 6; i++){ 34 for(j = 0; j < 7; j++){ 35 if(j >= start && num <= month_limit[month - 1]){ 36 printf("%2d ",num); 37 num++; 38 } 39 else{ 40 printf(" "); 41 } 42 } 43 printf("\n"); 44 start = 0; 45 } 46 week_number += month_limit[month - 1] % 7; 47 week_number = week_number % 7; 48 start = week_number; 49 num = 1; 50 printf("\n"); 51 } 52 return 0; 53} 54 55int check_leap(int year) 56{ 57 int type = 0; //0が平年,1が閏年 58 if(year % 400 == 0){ 59 type = 1; 60 } 61 else if(year % 100 == 0){ 62 type = 0; 63 } 64 else if(year % 4 == 0){ 65 type = 1; 66 } 67 return type; 68}

投稿2018/07/02 12:46

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

次の方針でつくってみました。
広い2次元の char の配列をつくる。
1 月分のカレンダーを配列上の指定の位置から置く という関数をつくる。
1 〜 12 月をこの配列上に置く。
最後に 2 次元の配列の内容を画面に表示する。

lang

1// #pragma warning(disable:4996) 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5#include <locale.h> 6#include <wchar.h> 7 8#define COL_M 3 // 横に並べる 月の数 (1, 2, 3, 4, 6, 12 のいずれかに設定する) 9#define WIDTH (((COL_M) * 24) * 2) 10#define HEIGHT ((12 / (COL_M)) * 8 + 2) 11#define CAL_LOCALE "ja_JP.UTF-8" 12#define LINE_SIZE 300 13 14// プロトタイプ宣言 15int main(void); 16void drow_calendar(int year); 17void write_month(int x, int y, int m); 18int uru_cal(int u_y); 19int day_of_week_firstday(int wy, int wm); 20int write_blank(wchar_t* str, rsize_t strsize, int b_dos); 21int write_days(wchar_t* str, rsize_t strsize, int eom, int res_dow); 22int write_days2(wchar_t* str, rsize_t strsize, int eom, int res_dow, int now); 23 24void clear_canvas(void); 25int get_len_on_canvas(wchar_t* line); 26void show_canvas(void); 27void print_to_canvas(int x, int y, wchar_t* str); 28 29struct result{ // 構造体 result 宣言 30 int res_month; 31 int res_dow; 32 int eom; 33 int now; 34}; 35 36struct result month_a[12] = { // 構造体の初期化 37 { 1, 0, 31, 0 }, 38 { 2, 0, 28, 0 }, // [1] うるう関数からの戻り値を代入 *\/ */ 39 { 3, 0, 31, 0 }, 40 { 4, 0, 30, 0 }, 41 { 5, 0, 31, 0 }, 42 { 6, 0, 30, 0 }, 43 { 7, 0, 31, 0 }, 44 { 8, 0, 31, 0 }, 45 { 9, 0, 30, 0 }, 46 { 10, 0, 31, 0 }, 47 { 11, 0, 30, 0 }, 48 { 12, 0, 31, 0 }, 49}; 50 51// カレンダー表示を組み立てる2次元配列 52wchar_t canvas[HEIGHT][WIDTH]; 53 54int main(void) { 55 int year = 0; 56 57 /* set locale */ 58 setlocale(LC_CTYPE, CAL_LOCALE); 59 60 printf("表示したい年を入力してください(1900年~) → \n"); 61 scanf("%d", &year); 62 63 if (1900 >= year) { // 入力チェック 64 printf("数値が正しくありません。"); 65 exit(1); 66 } 67 68 clear_canvas(); 69 drow_calendar(year); 70 show_canvas(); 71} // main終わり 72 73// カレンダーを表示する。 74void drow_calendar(int year) { 75 int uru = 0; 76 int res_dow = 0; 77 int now = 0; 78 79 for (int i = 0; i < 12; i++) { // 配列の res_dow を設定 80 month_a[i].res_dow = day_of_week_firstday(year, i); // dow 関数 81 } 82 month_a[1].eom = uru_cal(year); // うるう年の計算 83 84 wchar_t line[LINE_SIZE]; 85 swprintf(line, LINE_SIZE, L"%d 年のカレンダー", year); 86 print_to_canvas(24 * COL_M /2 - 10, 0, line); 87 88 for (int m = 0; m < 12; m++) { 89 int x = (m % COL_M) * 24; 90 int y = (m / COL_M) * 8 + 1; 91 write_month(x, y, m); // (m+1) 月のカレンダーを canvas の [x, y] の位置に書く。 92 } 93} 94 95// 一ヶ月分のカレンダーを canvas に書く。 96void write_month(int x, int y, int m) { 97 wchar_t line[LINE_SIZE]; 98 wchar_t buf[50]; 99 line[0] = L'\0'; 100 swprintf(line, LINE_SIZE, L" [%d月]", month_a[m].res_month); 101 print_to_canvas(x, y, line); 102 103 line[0] = '\0'; 104 swprintf(line, LINE_SIZE, L" 日 月 火 水 木 金 土"); 105 print_to_canvas(x, y + 1, line); 106 107 line[0] = '\0'; 108 int len = write_blank(line, LINE_SIZE, month_a[m].res_dow); // 月の始めのブランクを出力 109 month_a[m].now = write_days(line + len, LINE_SIZE - len, month_a[m].eom, month_a[m].res_dow); // eom と res_dow を使って日付を出力 110 print_to_canvas(x, y + 2, line); 111 112 for (int j = 0; j < 5; j++) { 113 line[0] = '\0'; 114 month_a[m].now = write_days2(line, LINE_SIZE, month_a[m].eom, month_a[m].res_dow, month_a[m].now); // eomとnow を使って日付を出力 115 print_to_canvas(x, y + 3 + j, line); 116 } 117} 118 119// うるう計算 120int uru_cal(int u_y) { 121 int res_uru = 28; 122 if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)) { // うるう年の判定 123 res_uru = 29; // 月の終わりの日付を29に設定 124 } 125 return res_uru; 126}; 127 128// 毎月1日の曜日を計算 129// ツェラーの公式 130// http://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F 131int day_of_week_firstday(int wy, int wm) { 132 wm += 1; 133 return (wy + (wy / 4) - (wy / 100) + (wy / 400) + ((13 * wm + 8) / 5) + 1) % 7; 134} 135 136// 先頭曜日まで空白を出力する。 137int write_blank(wchar_t* str, rsize_t strsize,int b_dow) { 138 for (int blank = 0; blank < b_dow; blank++) { 139 swprintf(str + blank * 3, strsize - blank * 3, L" "); 140 } 141 return b_dow * 3; 142} 143 144int write_days(wchar_t* str, rsize_t strsize, int eom, int res_dow) { 145 int nextday = 0; 146 wchar_t buff[10]; 147 for (int day = 1; day <= 7 - res_dow; day++) { 148 swprintf(str + (day - 1) * 3, strsize - (day -1) * 3, L" %2d", day); 149 nextday = day; 150 } 151 return nextday; 152} 153 154int write_days2(wchar_t* str, rsize_t strsize, int eom, int res_dow, int now) { 155 int day = 0; 156 int r = 0; 157 int nextday = now; 158 for (day = nextday + 1, r = 0; r <= 6; day++, r++) { 159 if (day <= eom) { 160 swprintf(str + r * 3, strsize - r * 3, L" %2d", day); 161 } else { 162 swprintf(str + r * 3, strsize - r * 3, L" "); 163 } 164 nextday = day; 165 } 166 return nextday; 167} 168 169// canvas を ' ' で梅める。 170void clear_canvas() { 171 for (int y = 0; y < HEIGHT; y++) { 172 for (int x = 0; x < WIDTH; x++) { 173 canvas[y][x] = L' '; 174 } 175 } 176} 177 178// 末尾の " " を取り除いた場合の長さを返す。 179int get_len_on_canvas(wchar_t* line) { 180 for (int i = WIDTH - 1; 0 <= i; i--) { 181 if (line[i] != L' ') { 182 return i + 1; 183 } 184 } 185 return 0; 186} 187 188// canvas の内容を stdout へ出力する。 189void show_canvas() { 190 for (int y = 0; y < HEIGHT; y++) { 191 int line_len = get_len_on_canvas(canvas[y]); 192 for (int x = 0; x < line_len; x++) { 193 wchar_t wc = canvas[y][x]; 194 if (wc != '\0') { 195 putwchar(wc); 196 } 197 } 198 printf("\n"); 199 } 200} 201 202// canvas の指定位置に string を置く。 203void print_to_canvas(int x, int y, wchar_t* str) { 204 int len = wcslen(str); 205 int xp = x; 206 for (int p = 0; p < len; p++) { 207 if (xp >= WIDTH) { 208 break; 209 } 210 canvas[y][xp] = str[p]; 211 212 // 日本語文字の次には "\0" を置く。 213 // 日本語文字は表示幅が 2 なので 次の文字を 潰して、 214 // コンソールに出力した時に 文字の位置がずれないようにする。 215 if ((0 < str[p] && str[p] < 255)) { 216 xp++; 217 } else { 218 if (xp + 1 < WIDTH) { 219 canvas[y][xp + 1] = L'\0'; 220 xp += 2; 221 } 222 } 223 } 224}

投稿2015/06/14 04:13

katoy

総合スコア22324

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

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

0

3 月分を横に並べて出力する部分を 共通にして、それを 4 回くりかえすようにしてみました。

lang

1// #pragma warning(disable:4996) 2#include <stdio.h> 3#include <stdlib.h> 4 5// プロトタイプ宣言 6int main(void); 7void drow_calendar(int year); 8int uru_cal(int u_y); 9int day_of_week_firstday(int wy, int wm); 10void writeblank(int b_dos); 11int writedays(int eom, int res_dow); 12int writedays2(int eom, int res_dow, int now); 13 14int COL_M = 3; // 横に並べる 月の数 (1, 2, 3, 4, 6, 12 のいずれか) 15 16struct result{ // 構造体result宣言 17 int res_month; 18 int res_dow; 19 int eom; 20 int now; 21}; 22 23int main(void) { 24 int year = 0; 25 26 printf("表示したい年を入力してください(1900年~) → \n"); 27 scanf("%d", &year); 28 29 if (1900 >= year) { // 入力チェック 30 printf("数値が正しくありません。"); 31 exit(1); 32 } 33 34 drow_calendar(year); 35} // main終わり 36 37// カレンダーを表示する。 38void drow_calendar(int year) { 39 int month = 0; 40 int uru = 0; 41 int i = 0; 42 int j = 0; 43 int res_uru = 0; 44 int res_dow = 0; 45 int now = 0; 46 int ent = 0; 47 48 res_uru = uru_cal(year); // うるう年の計算 49 50 struct result month_a[12] = { // 構造体の初期化 51 { 1, res_dow, 31, now }, //[0] 52 { 2, res_dow, res_uru, now }, // [1]うるう関数からの戻り値を代入 53 { 3, res_dow, 31, now }, //[2] 54 { 4, res_dow, 30, now }, //[3] 55 { 5, res_dow, 31, now }, //[4] 56 { 6, res_dow, 30, now }, //[5] 57 { 7, res_dow, 31, now }, //[6] 58 { 8, res_dow, 31, now }, //[7] 59 { 9, res_dow, 30, now }, //[8] 60 { 10, res_dow, 31, now }, //[9] 61 { 11, res_dow, 30, now }, //[10] 62 { 12, res_dow, 31, now }, //[11] 63 }; 64 65 for (i = 0, month = 0; i < 11; i++, month++) { // 配列の res_dow を設定 66 month_a[i].res_dow = day_of_week_firstday(year, month); // dow 関数 67 } 68 69 printf("============= %d 年のカレンダー =============\n", year); 70 for (int k = 0; k < (12 / COL_M) ; k++) { 71 int start_m = COL_M * k; 72 // (start_m + 1) 月 ~ (atart_m + 3) 月 73 for (j = start_m; j < start_m + COL_M; j++) { 74 printf(" [%2d月] ", month_a[j].res_month); 75 } 76 printf("\n"); 77 78 for (j = start_m; j < start_m + COL_M; j++) { 79 printf(" 日 月 火 水 木 金 土"); 80 if (j != start_m + COL_M -1) { 81 printf(" "); 82 } 83 } 84 printf("\n"); 85 86 for (j = start_m; j < start_m + COL_M; j++) { 87 writeblank(month_a[j].res_dow); // 月の始めのブランクを出力 88 month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); // eom と res_dow を使って日付を出力 89 ent++; 90 91 if (ent == COL_M) { 92 printf("\n"); 93 ent = 0; 94 } else { 95 printf(" "); 96 } 97 } 98 for (i = 0; i < 5; i++) { 99 for (j = start_m; j < start_m + COL_M; j++) { 100 month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); // eomとnow を使って日付を出力 101 ent++; 102 if (ent == COL_M) { 103 printf("\n"); 104 ent = 0; 105 } else { 106 printf(" "); 107 } 108 } 109 } 110 } 111} 112 113// 関数宣言 114int uru_cal(int u_y){ // うるう計算 115 int res_uru = 0; 116 117 if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ // うるう年の判定 118 res_uru = 29; // 月の終わりの日付を29に設定 119 } else { 120 res_uru = 28; // 月の終わりの日付を28に設定 121 } 122 return res_uru; 123}; 124 125// 毎月1日の曜日を計算 126// ツェラーの公式 127// http://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F 128int day_of_week_firstday(int wy, int wm) { 129 wm += 1; 130 if (wm == 1 || wm == 2) { 131 wy--; 132 wm += 12; 133 } 134 return (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7; 135} 136 137// 先頭曜日まで空白を出力する。 138void writeblank(int b_dow) { 139 for (int blank = 0; blank < b_dow; blank++) { 140 printf(" "); 141 } 142} 143 144int writedays(int eom, int res_dow) { 145 int nextday = 0; 146 for (int day = 1; day <= 7 - res_dow; day++) { 147 printf(" %2d", day); 148 nextday = day; 149 } 150 return nextday; // nextday を返す 151} 152 153int writedays2(int eom, int res_dow, int now) { 154 int day = 0; 155 int r = 0; 156 int e = 0; 157 int nextday = now; 158 159 for (day = nextday + 1, r = 0; r <= 6; day++, r++) { 160 if (day <= eom) { 161 printf(" %2d", day); 162 nextday = day; 163 } else { 164 printf(" "); 165 } 166 } 167 return nextday; 168}

横に3月分出力する部分を関数として括り出しても良いかもしれません。

さらに共通部分を括り出すなら、次のような案が考えられます。
広い2次元の char の配列をつくる。
1 月分のカレンダーを配列上の指定の位置から置く という関数をつくる。
1 〜 12 月をこの配列上に置く。
最後に 2 次元の配列の内容を画面に表示する。

投稿2015/06/13 04:29

katoy

総合スコア22324

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

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

lago_sakmof

2015/06/13 06:15

お返事が遅くなりました。 アドバイス有難うございます! じっくり読んで参考にさせていただきます。
guest

0

このへんを関数にまとめるといいかもですね。
ちなみに、関数の中でも関数は使えますよ。
そもそもmainも関数ですからね。
void monthPrint() {
month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); //eomとres_dowを使って日付を出力

ent++; if (ent == 3){ printf("\n"); ent = 0; } else{ printf(" "); }

}

もう1つ方法としまして、置換型・関数マクロて検索して頂けるといいです。
#define MONTH_PRINT() {ent++;if(ent==3)...略}

void main() {
MONTH_PRINT(); // このように使うと、コンパイル時にコードを置いてくれます。
}

投稿2015/06/11 15:13

MasaakiIrie

総合スコア1021

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

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

lago_sakmof

2015/06/13 06:16

お返事が遅くなりました。 アドバイス有難うございます! 関数の中でも関数は使えるのですね。置換型・関数マクロで検索してみます。
guest

0

>入力値からカレンダーの出力もうまくいっているのですが、
>日数の書き出し部分あたりに同じコードを何度も使ってしまっています。
>この繰り返しの部分をもっとスッキリさせる方法はないでしょうか。

>下記コード上に記述した部分が繰り返し使っているので関数にしてしまいたいのですが、
>すでに関数を使ってしまっているので関数内関数?になってしまうためかうまくいきません。
>(C言語で関数内関数は使えないとどこかのサイトで見ました)

100万回位、御自分の投稿を見直して、
どこかのサイトで見た事ではなく、
御自分で、手を動かして、確認しましたか?
プログラムでは、耳学問は意味を持ちません。
仕様に従って、正しく、過不足無く(異論はあるはず)、
動作するコードが正しいコードです。
~個人的には仕様通りで済ますのは嫌い~

投稿2015/06/11 15:03

daive

総合スコア2028

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

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

lago_sakmof

2015/06/13 06:17

お返事が遅くなりました。 アドバイス有難うございます! 実践で覚えるのが一番ですね。甘えた質問にご回答いただきありがとうございました。 もっと勉強したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問