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

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

ただいまの
回答率

88.04%

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

受付中

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,701

score 5

前提・実現したいこと

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

発生している問題・エラーメッセージ

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

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

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

ソースコード

#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>

//プロトタイプ宣言
int uru_cal(int u_y);
int dow(int wy, int wm);
void writeblank(int b_dos);
int writedays(int eom, int res_dow);
int writedays2(int eom, int res_dow, int now);


struct result{ //構造体result宣言
    int res_month;
    int res_dow;
    int eom;
    int now;
};

int main(void){

    int year = 0;
    int month = 0;
    int uru = 0;
    int i = 0;
    int j = 0;
    int res_uru = 0;
    int res_dow = 0;
    int now = 0;
    int ent = 0;

    printf("表示したい年を入力してください(1900年~) → \n");
    scanf("%d", &year);

    if (1900 >= year){ //入力チェック
        printf("数値が正しくありません。");
        exit(1);
    }


    res_uru = uru_cal(year); //うるう年の計算

    struct result month_a[12] = { //構造体の初期化
        { 1, res_dow, 31, now }, //[0]
        { 2, res_dow, res_uru, now },//[1]うるう関数からの戻り値を代入
        { 3, res_dow, 31, now }, //[2]
        { 4, res_dow, 30, now }, //[3]
        { 5, res_dow, 31, now }, //[4]
        { 6, res_dow, 30, now }, //[5]
        { 7, res_dow, 31, now }, //[6]
        { 8, res_dow, 31, now }, //[7]
        { 9, res_dow, 30, now }, //[8]
        { 10, res_dow, 31, now }, //[9]
        { 11, res_dow, 30, now }, //[10]
        { 12, res_dow, 31, now }, //[11]
    };

    for (i = 0, month = 0; i < 11; i++, month++){ //配列にres_dowを代入
        month_a[i].res_dow = dow(year, month); //dow関数

    }

    printf("=============%d年のカレンダー=============\n", year);


    /*1月~3月*/
    for (j = 0; j < 3; j++){
        printf("         [%d月]         ", month_a[j].res_month);
    }

    printf("\n");


    printf(" 日 月 火 水 木 金 土    日 月 火 水 木 金 土    日 月 火 水 木 金 土\n");
    for (j = 0; j < 3; j++){ 

        /*繰り返し使っている部分↓*/
        writeblank(month_a[j].res_dow); //月の始めのブランクを出力

        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("   ");
        }
        /*繰り返し使っている部分↑*/
    }

    for (i = 0; i < 5; i++){

        /*繰り返し使っている部分↓*/
        for (j = 0; j < 3; j++){ 

            month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力

            ent++;

            if (ent == 3){
                printf("\n");
                ent = 0;
            }
            else{
                printf("   ");
            }
        }
        /*繰り返し使っている部分↑*/
    }

    /*4月~5月*/
    for (j = 3; j < 6; j++){
        printf("         [%d月]          ", month_a[j].res_month);
    }

    printf("\n");


    printf(" 日 月 火 水 木 金 土    日 月 火 水 木 金 土    日 月 火 水 木 金 土\n");

    for (j = 3; j < 6; j++){ 

        /*第1週目*3か月分*/
        writeblank(month_a[j].res_dow); //月の始めのブランクを出力

        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("   ");
        }
    }

    for (i = 0; i < 5; i++){

        for (j = 3; j < 6; j++){ 

            month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力

            ent++;

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

    /*7月~9月*/
    for (j = 6; j < 9; j++){
        printf("         [%d月]          ", month_a[j].res_month);
    }

    printf("\n");

    printf(" 日 月 火 水 木 金 土    日 月 火 水 木 金 土    日 月 火 水 木 金 土\n");

    for (j = 6; j < 9; j++){ 

        /*第1週目*3か月分*/
        writeblank(month_a[j].res_dow); //月の始めのブランクを出力

        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("   ");
        }
    }

    for (i = 0; i < 5; i++){

        for (j = 6; j < 9; j++){ 

            month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力

            ent++;

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

    /*10月~12月*/
    for (j = 9; j < 12; j++){
        printf("         [%d月]         ", month_a[j].res_month);
    }

    printf("\n");


    printf(" 日 月 火 水 木 金 土    日 月 火 水 木 金 土    日 月 火 水 木 金 土\n");

    for (j = 9; j < 12; j++){ 

        /*第1週目*3か月分*/
        writeblank(month_a[j].res_dow); //月の始めのブランクを出力

        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("   ");
        }
    }

    for (i = 0; i < 5; i++){

        for (j = 9; j < 12; j++){ 

            month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); //eomとnowを使って日付を出力

            ent++;

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


} //main終わり

//関数宣言
int uru_cal(int u_y){ //関数うるう計算

    int res_uru = 0;

    if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ //うるう年の判定
        res_uru = 29; //月の終わりの日付を29に設定

    }
    else{
        res_uru = 28; //月の終わりの日付を28に設定
    }

    return res_uru;
};

int dow(int wy, int wm){ //dow関数

    int dayofweek = 0;
    wm += 1;


    if (wm == 1 || wm == 2) {
        wy--;
        wm += 12;
    }

    return dayofweek = (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7; //毎月1日の曜日を計算;
}


void writeblank(int b_dow){ //writeblank関数

    

    int blank = 0;

    for (blank = 0; blank < b_dow; blank++){ //先頭曜日まで空白
        printf("   ");
    }
}


int writedays(int eom, int res_dow){

    int day = 0;
    int nextday = 0;

    for (day = 1; day <= 7 - res_dow; day++){
        printf(" %2d", day);
        nextday = day;
    }

    return nextday; //nextdayを返す
}

int writedays2(int eom, int res_dow, int now){
    int day = 0;
    int r = 0;
    int e = 0;
    int nextday = now;

    

    for (day = nextday + 1, r = 0; r <= 6; day++, r++){

        if (day <= eom){
            printf(" %2d", day);
            nextday = day;
        }
        else{
            printf("   ");
        }
    }


    return nextday;
}

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


※課題として構造体をあえて使っています
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 6

+1

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

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

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

#include <stdio.h>
#include <stdlib.h>

//プロトタイプ宣言
int uru_cal(int u_y);
int dow(int wy, int wm);
int writedays(int eom, int now);


struct result{ //構造体result宣言
    int res_month;
    int res_dow;
    int eom;
    int now;
};

int main(void){

    int year = 0;
    int month = 0;
    int uru = 0;
    int i = 0;
    int j = 0;
    int m = 0;
    int res_uru = 0;
    int res_dow = 0;
    int now = 0;

    printf("表示したい年を入力してください(1900年~) → \n");
    scanf("%d", &year);

    if (1900 >= year){ //入力チェック
        printf("数値が正しくありません。");
        exit(1);
    }


    res_uru = uru_cal(year); //うるう年の計算

    struct result month_a[12] = { //構造体の初期化
        { 1, res_dow, 31, now }, //[0]
        { 2, res_dow, res_uru, now },//[1]うるう関数からの戻り値を代入
        { 3, res_dow, 31, now }, //[2]
        { 4, res_dow, 30, now }, //[3]
        { 5, res_dow, 31, now }, //[4]
        { 6, res_dow, 30, now }, //[5]
        { 7, res_dow, 31, now }, //[6]
        { 8, res_dow, 31, now }, //[7]
        { 9, res_dow, 30, now }, //[8]
        { 10, res_dow, 31, now }, //[9]
        { 11, res_dow, 30, now }, //[10]
        { 12, res_dow, 31, now }, //[11]
    };

    for (i = 0, month = 0; i < 11; i++, month++){ //配列にres_dowを代入
        month_a[i].now = -dow(year, month); //dow関数
    }

    printf("=============%d年のカレンダー=============\n", year);

    for(m=0; m<12; m+=3){

        for (j = 0; j < 3; j++){
            printf("         [%d月]         ", month_a[m+j].res_month);
        }

        printf("\n");

        printf(" 日 月 火 水 木 金 土    日 月 火 水 木 金 土    日 月 火 水 木 金 土\n");

        for (i = 0; i < 6; i++){
            for (j = 0; j < 3; j++){ 

                month_a[m+j].now = writedays(month_a[m+j].eom, month_a[m+j].now);

                if (j == 2){
                    printf("\n");
                }else{
                    printf("   ");
                }
            }
        }
    }    

} //main終わり

int uru_cal(int u_y){ //関数うるう計算

    int res_uru = 0;

    if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ //うるう年の判定
        res_uru = 29; //月の終わりの日付を29に設定

    }
    else{
        res_uru = 28; //月の終わりの日付を28に設定
    }

    return res_uru;
};

int dow(int wy, int wm){ //dow関数

    int dayofweek = 0;
    wm += 1;


    if (wm == 1 || wm == 2) {
        wy--;
        wm += 12;
    }

    return dayofweek = (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7; //毎月1日の曜日を計算;
}

int writedays(int eom, int now){
    int day = 0;
    int r = 0;
    int e = 0;
    int nextday = now;   

    for (day = nextday + 1, r = 0; r <= 6; day++, r++){

        if (1 <= day && day <= eom){
            printf(" %2d", day);
            nextday = day;
        }
        else{
            printf("   ");
        }
    }

    return nextday;
}

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/06/13 15:15

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

    キャンセル

0

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

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

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



投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/06/13 15:17

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

    キャンセル

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/13 15:16

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

    キャンセル

0

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

// #pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>

// プロトタイプ宣言
int main(void);
void drow_calendar(int year);
int uru_cal(int u_y);
int day_of_week_firstday(int wy, int wm);
void writeblank(int b_dos);
int writedays(int eom, int res_dow);
int writedays2(int eom, int res_dow, int now);

int COL_M = 3;  // 横に並べる 月の数 (1, 2, 3, 4, 6, 12 のいずれか)

struct result{ // 構造体result宣言
  int res_month;
  int res_dow;
  int eom;
  int now;
};

int main(void) {
  int year = 0;

  printf("表示したい年を入力してください(1900年~) → \n");
  scanf("%d", &year);

  if (1900 >= year) { // 入力チェック
    printf("数値が正しくありません。");
    exit(1);
  }

  drow_calendar(year);
} // main終わり

//  カレンダーを表示する。
void drow_calendar(int year) {
  int month = 0;
  int uru = 0;
  int i = 0;
  int j = 0;
  int res_uru = 0;
  int res_dow = 0;
  int now = 0;
  int ent = 0;

  res_uru = uru_cal(year); // うるう年の計算

  struct result month_a[12] = { // 構造体の初期化
    { 1, res_dow, 31, now }, //[0]
    { 2, res_dow, res_uru, now }, // [1]うるう関数からの戻り値を代入
    { 3, res_dow, 31, now }, //[2]
    { 4, res_dow, 30, now }, //[3]
    { 5, res_dow, 31, now }, //[4]
    { 6, res_dow, 30, now }, //[5]
    { 7, res_dow, 31, now }, //[6]
    { 8, res_dow, 31, now }, //[7]
    { 9, res_dow, 30, now }, //[8]
    { 10, res_dow, 31, now }, //[9]
    { 11, res_dow, 30, now }, //[10]
    { 12, res_dow, 31, now }, //[11]
  };

  for (i = 0, month = 0; i < 11; i++, month++) {   // 配列の res_dow を設定
    month_a[i].res_dow = day_of_week_firstday(year, month); // dow 関数
  }

  printf("============= %d 年のカレンダー =============\n", year);
  for (int k = 0; k < (12 / COL_M) ; k++) {
    int start_m = COL_M * k;
    //  (start_m + 1) 月 ~  (atart_m + 3) 月
    for (j = start_m; j < start_m + COL_M; j++) {
      printf("         [%2d月]        ", month_a[j].res_month);
    }
    printf("\n");

    for (j = start_m; j < start_m + COL_M; j++) {
      printf(" 日 月 火 水 木 金 土");
      if (j != start_m + COL_M -1) {
    printf("   ");
      }
    }
    printf("\n");

    for (j = start_m; j < start_m + COL_M; j++) {
      writeblank(month_a[j].res_dow); // 月の始めのブランクを出力
      month_a[j].now = writedays(month_a[j].eom, month_a[j].res_dow); // eom と res_dow を使って日付を出力
      ent++;

      if (ent == COL_M) {
    printf("\n");
    ent = 0;
      } else {
    printf("   ");
      }
    }
    for (i = 0; i < 5; i++) {
      for (j = start_m; j < start_m + COL_M; j++) {
    month_a[j].now = writedays2(month_a[j].eom, month_a[j].res_dow, month_a[j].now); // eomとnow を使って日付を出力
    ent++;
    if (ent == COL_M) {
      printf("\n");
      ent = 0;
    } else {
      printf("   ");
    }
      }
    }
  }
}

// 関数宣言
int uru_cal(int u_y){ // うるう計算
  int res_uru = 0;

  if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)){ // うるう年の判定
    res_uru = 29; // 月の終わりの日付を29に設定
  } else {
    res_uru = 28; // 月の終わりの日付を28に設定
  }
  return res_uru;
};

// 毎月1日の曜日を計算
// ツェラーの公式
//  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
int day_of_week_firstday(int wy, int wm) {
  wm += 1;
  if (wm == 1 || wm == 2) {
    wy--;
    wm += 12;
  }
  return (wy + wy / 4 - wy / 100 + wy / 400 + (13 * wm + 8) / 5 + 1) % 7;
}

// 先頭曜日まで空白を出力する。
void writeblank(int b_dow) {
  for (int blank = 0; blank < b_dow; blank++) {
    printf("   ");
  }
}

int writedays(int eom, int res_dow) {
  int nextday = 0;
  for (int day = 1; day <= 7 - res_dow; day++) {
    printf(" %2d", day);
    nextday = day;
  }
  return nextday; // nextday を返す
}

int writedays2(int eom, int res_dow, int now) {
  int day = 0;
  int r = 0;
  int e = 0;
  int nextday = now;

  for (day = nextday + 1, r = 0; r <= 6; day++, r++) {
    if (day <= eom) {
      printf(" %2d", day);
      nextday = day;
    } else {
      printf("   ");
    }
  }
  return nextday;
}
横に3月分出力する部分を関数として括り出しても良いかもしれません。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/06/13 15:15

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

    キャンセル

0

次の方針でつくってみました。
  広い2次元の char の配列をつくる。 
  1 月分のカレンダーを配列上の指定の位置から置く という関数をつくる。 
  1 〜 12 月をこの配列上に置く。 
  最後に 2 次元の配列の内容を画面に表示する。
// #pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>

#define  COL_M       3  // 横に並べる 月の数 (1, 2, 3, 4, 6, 12 のいずれかに設定する)
#define  WIDTH       (((COL_M) * 24) * 2)
#define  HEIGHT      ((12 / (COL_M)) * 8 + 2)
#define  CAL_LOCALE  "ja_JP.UTF-8"
#define  LINE_SIZE   300

// プロトタイプ宣言
int main(void);
void drow_calendar(int year);
void write_month(int x, int y, int m);
int uru_cal(int u_y);
int day_of_week_firstday(int wy, int wm);
int write_blank(wchar_t* str, rsize_t strsize, int b_dos);
int write_days(wchar_t* str, rsize_t strsize, int eom, int res_dow);
int write_days2(wchar_t* str, rsize_t strsize, int eom, int res_dow, int now);

void clear_canvas(void);
int get_len_on_canvas(wchar_t* line);
void show_canvas(void);
void print_to_canvas(int x, int y, wchar_t* str);

struct result{ // 構造体 result 宣言
  int res_month;
  int res_dow;
  int eom;
  int now;
};

struct result month_a[12] = { // 構造体の初期化
  { 1,  0, 31, 0 },
  { 2,  0, 28, 0 }, // [1] うるう関数からの戻り値を代入 *\/ */
  { 3,  0, 31, 0 },
  { 4,  0, 30, 0 },
  { 5,  0, 31, 0 },
  { 6,  0, 30, 0 },
  { 7,  0, 31, 0 },
  { 8,  0, 31, 0 },
  { 9,  0, 30, 0 },
  { 10, 0, 31, 0 },
  { 11, 0, 30, 0 },
  { 12, 0, 31, 0 },
};

// カレンダー表示を組み立てる2次元配列
wchar_t canvas[HEIGHT][WIDTH];

int main(void) {
  int year = 0;

  /* set locale */
  setlocale(LC_CTYPE, CAL_LOCALE);

  printf("表示したい年を入力してください(1900年~) → \n");
  scanf("%d", &year);

  if (1900 >= year) { // 入力チェック
    printf("数値が正しくありません。");
    exit(1);
  }

  clear_canvas();
  drow_calendar(year);
  show_canvas();
} // main終わり

//  カレンダーを表示する。
void drow_calendar(int year) {
  int uru = 0;
  int res_dow = 0;
  int now = 0;

  for (int i = 0; i < 12; i++) {   // 配列の res_dow を設定
    month_a[i].res_dow = day_of_week_firstday(year, i); // dow 関数
  }
  month_a[1].eom = uru_cal(year); // うるう年の計算

  wchar_t  line[LINE_SIZE];
  swprintf(line, LINE_SIZE, L"%d 年のカレンダー", year);
  print_to_canvas(24 * COL_M /2 - 10, 0, line);

  for (int m = 0; m < 12; m++) {
    int x = (m % COL_M) * 24;
    int y = (m / COL_M) * 8 + 1;
    write_month(x, y, m);  // (m+1) 月のカレンダーを canvas の [x, y] の位置に書く。
  }
}

// 一ヶ月分のカレンダーを canvas に書く。
void write_month(int x, int y, int m) {
  wchar_t  line[LINE_SIZE];
  wchar_t  buf[50];
  line[0] = L'\0';
  swprintf(line, LINE_SIZE, L"        [%d月]", month_a[m].res_month);
  print_to_canvas(x, y, line);

  line[0] = '\0';
  swprintf(line, LINE_SIZE, L" 日 月 火 水 木 金 土");
  print_to_canvas(x, y + 1, line);

  line[0] = '\0';
  int len = write_blank(line, LINE_SIZE, month_a[m].res_dow); // 月の始めのブランクを出力
  month_a[m].now = write_days(line + len, LINE_SIZE - len, month_a[m].eom, month_a[m].res_dow); // eom と res_dow を使って日付を出力
  print_to_canvas(x, y + 2, line);

  for (int j = 0; j < 5; j++) {
    line[0] = '\0';
    month_a[m].now = write_days2(line, LINE_SIZE, month_a[m].eom, month_a[m].res_dow, month_a[m].now); // eomとnow を使って日付を出力
    print_to_canvas(x, y + 3 + j, line);
  }
}

// うるう計算
int uru_cal(int u_y) {
  int res_uru = 28;
  if (u_y % 4 == 0 && (u_y % 100 != 0 || u_y % 400 == 0)) { // うるう年の判定
    res_uru = 29; // 月の終わりの日付を29に設定
  }
  return res_uru;
};

// 毎月1日の曜日を計算
// ツェラーの公式
//  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
int day_of_week_firstday(int wy, int wm) {
  wm += 1;
  return (wy + (wy / 4) - (wy / 100) + (wy / 400) + ((13 * wm + 8) / 5) + 1) % 7;
}

// 先頭曜日まで空白を出力する。
int write_blank(wchar_t* str, rsize_t strsize,int b_dow) {
  for (int blank = 0; blank < b_dow; blank++) {
    swprintf(str + blank * 3, strsize - blank * 3, L"   ");
  }
  return b_dow * 3;
}

int write_days(wchar_t* str, rsize_t strsize, int eom, int res_dow) {
  int nextday = 0;
  wchar_t buff[10];
  for (int day = 1; day <= 7 - res_dow; day++) {
    swprintf(str + (day - 1) * 3, strsize - (day -1) * 3, L" %2d", day);
    nextday = day;
  }
  return nextday;
}

int write_days2(wchar_t* str, rsize_t strsize, int eom, int res_dow, int now) {
  int day = 0;
  int r = 0;
  int nextday = now;
  for (day = nextday + 1, r = 0; r <= 6; day++, r++) {
    if (day <= eom) {
      swprintf(str + r * 3, strsize - r * 3, L" %2d", day);
    } else {
      swprintf(str + r * 3, strsize - r * 3, L"   ");
    }
    nextday = day;
  }
  return nextday;
}

//  canvas を ' ' で梅める。
void clear_canvas() {
  for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
      canvas[y][x] = L' ';
    }
  }
}

// 末尾の " " を取り除いた場合の長さを返す。
int get_len_on_canvas(wchar_t* line) {
  for (int i = WIDTH - 1; 0 <= i; i--) {
    if (line[i] != L' ') {
      return i + 1;
    }
  }
  return 0;
}

// canvas の内容を stdout へ出力する。
void show_canvas() {
  for (int y = 0; y < HEIGHT; y++) {
    int line_len = get_len_on_canvas(canvas[y]);
    for (int x = 0; x < line_len; x++) {
      wchar_t wc = canvas[y][x];
      if (wc != '\0') {
    putwchar(wc);
      }
    }
    printf("\n");
  }
}

// canvas の指定位置に string を置く。
void print_to_canvas(int x, int y, wchar_t* str) {
  int len = wcslen(str);
  int xp = x;
  for (int p = 0; p < len; p++) {
    if (xp >= WIDTH) {
      break;
    }
    canvas[y][xp] = str[p];

    // 日本語文字の次には "\0" を置く。
    // 日本語文字は表示幅が 2 なので 次の文字を 潰して、
    // コンソールに出力した時に 文字の位置がずれないようにする。
    if ((0 < str[p] && str[p] < 255)) {
      xp++;
    } else {
      if (xp + 1 < WIDTH) {
    canvas[y][xp + 1] = L'\0';
    xp += 2;
      }
    }
  }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

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

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 256

int check_leap(int);

int main(void)
{
    char buf[BUFSIZE];
    char week[7][10] = {"日","月","火","水","木","金","土"};
    int month_limit[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int year,month;
    int week_number;
    int i,j;
    int num = 1;
    int start;

    fgets(buf,sizeof(buf),stdin);
    year = strtol(buf,NULL,10);
    month_limit[1] += check_leap(year);
    year--;
    week_number = (5 * year / 4 - year / 100 + year / 400 + 36) % 7;
    start = week_number;
    printf("\n");
    for(month = 1; month <= 12; month++){
        printf("%d月\n",month);
        for(i = 0; i < 7; i++){
            printf("%s ",week[i]);
        }
        printf("\n");
        for(i = 0; i < 6; i++){
            for(j = 0; j < 7; j++){
                if(j >= start && num <= month_limit[month - 1]){
                    printf("%2d ",num);
                    num++;
                }
                else{
                    printf("   ");
                }
            }
            printf("\n");
            start = 0;
        }
        week_number += month_limit[month - 1] % 7;
        week_number = week_number % 7;
        start = week_number;
        num = 1;
        printf("\n");
    }
    return 0;
}

int check_leap(int year)
{
    int type = 0; //0が平年,1が閏年
    if(year % 400 == 0){
        type = 1;
    }
    else if(year % 100 == 0){
        type = 0;
    }
    else if(year % 4 == 0){
        type = 1;
    }
    return type;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る