
VisualStudioを使ってC言語でコンソールアプリケーションのカレンダーを作っています。
やりたいことは以下の通りです。
プログラムを起動すると、現在の月を中央に三ヶ月分のカレンダーを表示。
Lキーでカレンダー全体を一ヶ月前に変更
Rキーで一ヶ月後に変更
Dキーで三ヶ月前に変更
Uキーで三ヶ月後に変更
Aキーで一年前に変更
Bキーで一年後に変更
起動時にカレンダー表示するのはできているんですが、Rでカレンダーを進めていくと12月の次が13月、次が0月、その次が1月となってしまい、日付も上手いこと表示されません。
L,D,U,でも同じようになります。
また、A,Bで年を変えると日付が正確に表示されないです。
これらを正しく表示させる方法をご教授いただけますでしょうか。
#include <stdio.h>
#include <conio.h>
#include <time.h>
#define KEY_ESC(0x1b)// エスケープ
#define KEY_L(0x6c)// L
#define KEY_D(0x64)// D
#define KEY_R(0x72)// R
#define KEY_U(0x75)// U
#define KEY_A(0x61)// A
#define KEY_B(0x62)// B
time_t timer;// 現在の時刻
struct tm local;// 地方時
int curr_year;// 現在の年
intcurr_mon;// 現在の月
int input;// どのキーが入力されたか
int calendar[12][6][7];// 月、最大の週、1週間
int year;// 年
int month;// 月
int day;// 日
int nweek;// その月が何周あるか
int nday;// その月が何日まであるか
int day_of_week;// その月が何曜日から始まるか
void CalendarDisplay(void);
void CalendarSet(void);
void DayOfWeek(int, int, int);
void NumOfDay(int);
void KeyOperat(int);
int LaepYear(int, int);
int Zeller(int, int, int);
int main(void)
{
CalendarDisplay();
while (1)
{
printf("ESCキーで終了\n\n");
input = _getch();
if(input == KEY_ESC) {
break;
}
else if(input == KEY_L || input == KEY_D ||
input == KEY_R || input == KEY_U ||
input == KEY_A || input == KEY_B) {
KeyOperat(input);
}
}
return 0;
}
// カレンダーの表示
void CalendarDisplay(void)
{
// 現在時刻を取得
timer = time(NULL);
// 地方時に変換
localtime_s(&local, &timer);
// 現在の年
curr_year = local.tm_year + 1900;
// 現在の月
curr_mon = local.tm_mon + 1;
CalendarSet();
}
// 当月とその±1の月を設定
void CalendarSet(void)
{
for (month = curr_mon-1; month <= curr_mon+1; month++)
{
// 12月を超えたら次の年に
if(curr_mon > 12) {
curr_mon = 1;
month = 1;
curr_year += 1;
}
// 1月を超えたら前の年に
else if(curr_mon < 1) {
curr_mon = 12;
month = 12;
curr_year -= 1;
}
// その月が何日まであるか求めて代入
nday = LaepYear(curr_year, month);
// その月が何曜日から始まるか
day_of_week = Zeller(curr_year, month, 1);
nweek = 0;
for (day = 1; day <= nday; day++)
{
calendar[month-1][nweek][day_of_week] = day;
//printf("day-> %d ", day);
if (++day_of_week == 7) {
day_of_week = 0;
nweek++;
}
}
}
DayOfWeek(month, curr_mon, curr_year);
}
// 月と曜日の表示
void DayOfWeek(int m, int cur_m, int cur_y)
{
for (m = cur_m-1; m <= cur_m-1; m++)
{
printf("%d%6d月%16d%6d月%16d%6d月\n" "日 月 火 水 木 金 土 "
"日 月 火 水 木 金 土 " "日 月 火 水 木 金 土\n",
cur_y, m, cur_y, m + 1, cur_y, m + 2);
NumOfDay(&calendar[m-1]);
}
}
// 日数の表示
void NumOfDay(int calendar[][6][7])
{
int max_w = 0, nmon = 0, w = 0;
// その月の最大週の間
for (max_w = 0; max_w < 6; max_w++)
{
// 3ヶ月分表示
for (nmon = 0; nmon < 3; nmon++)
{
for (w = 0; w < 7; w++)
{
if (calendar[nmon][max_w][w]) {
printf("%2d ", calendar[nmon][max_w][w]);
}
else {
printf(" ");
}
}
printf(" ");
}
printf("\n");
}
}
// キー操作でカレンダーの表示を変える
void KeyOperat(int input)
{
do {
switch(input)
{
case KEY_L:
curr_mon -= 1;
break;
case KEY_R:
curr_mon += 1;
break;
case KEY_D:
curr_mon -= 3;
break;
case KEY_U:
curr_mon += 3;
break;
case KEY_A:
curr_year -= 1;
break;
case KEY_B:
curr_year += 1;
break;
default :
input = !input;
break;
}
} while(!input);
CalendarSet();
}
// うるう年の判定
int LaepYear(int y, int m)
{
const int num_day[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (m == 2 && y % 4 == 0 &&
(y % 100 != 0 || y % 400 == 0)) {
return 29;
}
return num_day[m - 1];
}
// ツェラーの公式から曜日を求める
int Zeller(int y, int m, int d)
{
int dow;
// 1月と2月の場合、それぞれ前年の13月、14月とする
if(m == 1 || m == 2) {
y--;
m = m + 12;
}
dow = (y + y/4 - y/100 + y/400 + (13*m+8)/5 + d) % 7;
return dow;
}
No.2ベストアンサー
- 回答日時:
とりあえず、CalenderSet()の月判定が間違っていますね。
if( curr_mon > 12 ) は、if( month > 12 ) ではないですか?
1月未満か、の判定も同じく。
また、ループの終了条件はループする毎に評価されますから、このfor( month = curr_mon-1; month <= curr_mon+1; month++ )の終了条件を作る材料である、curr_monをループ内で変更してしまうと、終了条件も変わりますよ?
そして、ループカウンタでもあるmonth=1や=12とやってしまうと、今度は1や12からカウントを始めますので、意図しない月数表示されたり、前後分合わせて3ヶ月のうち1~2月分しか表示しない、等の動作を行う気もします。
for()ループのカウンタ(ここではmonth)と終了条件に参照する変数(ここではcurr_mon)は、変更しないで、テンポラリを作成したほうが良いと思います。
void CalenderSet( void )
{
int dispmon; //monthの代わり
int dispyear;
for( month = curr_mon-1; month <= curr_mon+1; month++ ){
dispmon = month; //表示したい月をdispmonにセット
dispyear = curr_year;
if( dispmon > 12 ){
dispmon -= 12; //今回は+-1月なので、dispmon = 1でもOK。2月以上前後に対応したかったら12を引く
dispyear = curr_year + 1;
}
if( dispmon < 1 ){
dispmon += 12; //上と同じ
dispyear = curr_year - 1;
}
// その月が何日まであるか求めて代入
nday = LaepYear( dispyear, dispmon ); //変更前→curr_year, month);
// その月が何曜日から始まるか
day_of_week = Zeller(dispyear, dispmon, 1 ); //変更前→ curr_year, month, 1);
nweek = 0;
以降省略。必要に応じて、変更してください。
変な動作をする原因は、このCalenderSet()で、中央の年月を記憶する変数(curr_year, curr_mon)を変更してしまっているということではないでしょうか?
curr_monやcurr_yearは、中央に表示したい年月であり、キー操作によって”のみ”移動させたいのでしょうから、キー操作以外で変更してはいけません。
表示するために前後の年月が必要なのであれば、それを記憶するためのローカル変数(dispyear, dispmon)を作って、それにコピーした後で、前後の年月にするための変更をローカル変数に行うべきです。
これ以外の部分は見てないのですが、とりあえずここを変更してみて、デバックを進めてみてはどうでしょう?
遅くなって申し訳ありません。
丁寧で分かりやすいご回答ありがとうございました。
テンポラリを作成してソースを改造してみたところ、思い通りの動きにすることができました!
普通に考えれば分かることでしたが、自分だけで考えてた時はテンポラリを作成するという事を全く思いつかなかったので非常に助かりました。
No.3
- 回答日時:
グローバル変数使わずに1月分のカレンダー表示から始めた方がいいと思う。
現在の月から始めたかったんですけど、1月分からの方が考え方は簡単ですよね。
それと、たまに日数がきちんと表示されないバグがグローバル変数をやめることで直りました!
ありがとうございました。
No.1
- 回答日時:
症状を正確に書いてほしいんだけどなぁ....
ひょっとして表示される 3か月の中央が 12月だと「その次の月」が「13月」になってたりしませんか?
変数の使い分けが変だと思う.
この回答への補足
すみません、字数制限の関係で詳しく書けませんでした。
具体的には一ヶ月ずつ進めていくと、おっしゃる通り以下のような感じになります。
2012 11月 2012 12月 2012 13月
日 月 火 水 木 金 土 日 月 火 水 木 金 土 月 火 水 木 金 土
1 2 3 1
4 5 6 7 8 9 10 2 3 4 5 6 7 8
11 12 13 14 15 16 17 9 10 11 12 13 14 15
18 19 20 21 22 23 24 16 17 18 19 20 21 22
25 26 27 28 29 30 23 24 25 26 27 28 29
30 31
ESCキーで終了
2013 0月 2013 1月 2013 2月
日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土
114 4 57 13 24 4 1 2 3 4 5 1 2
112 4 144 6 7 8 9 10 11 12 3 4 5 6 7 8 9
13 14 15 16 17 18 19 10 11 12 13 14 15 16
20 21 22 23 24 25 26 17 18 19 20 21 22 23
27 28 29 30 31 24 25 26 27 28
ESCキーで終了
-----------------------------------------------------------
一年後にすると
2010 4月 2010 5月 2010 6月
日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土
1 2 3 4 1 2 3 1 2 3 4 5 6 1 1 2 3 4 5
4 5 6 7 8 9 10 2 3 4 5 6 7 8 6 7 8 9 10 11 12
11 12 13 14 15 16 17 9 10 11 12 13 14 15 13 14 15 16 17 18 19
18 19 20 21 22 23 24 16 17 18 19 20 21 22 20 21 22 23 24 25 26
25 26 27 28 29 30 30 23 24 25 26 27 28 29 27 28 29 30 30 29 30
30 31
ESCキーで終了
と、表示されてしまいます。
>変数の使い分けが変だと思う.
具体的にどこの変数の使い方がおかしいのでしょうか?
ご教授くだされば嬉しいです。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# C 言語の Gauss Jordan 法について 2 2022/12/28 11:16
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- WordPress(ワードプレス) wordpressでphpを読み込みたい 1 2022/10/30 23:40
- C言語・C++・C# プログラミングペーパーテスト 次の問題の実行結果を答えろ #include int x[ ] = { 1 2022/06/16 21:49
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・昔のあなたへのアドバイス
- ・字面がカッコいい英単語
- ・許せない心理テスト
- ・歩いた自慢大会
- ・「I love you」 をかっこよく翻訳してみてください
- ・ゆるやかでぃべーと タイムマシンを破壊すべきか。
- ・はじめての旅行はどこに行きましたか?
- ・準・究極の選択
- ・この人頭いいなと思ったエピソード
- ・「それ、メッセージ花火でわざわざ伝えること?」
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・【お題】甲子園での思い出の残し方
- ・【お題】動物のキャッチフレーズ
- ・人生で一番思い出に残ってる靴
- ・これ何て呼びますか Part2
- ・スタッフと宿泊客が全員斜め上を行くホテルのレビュー
- ・あなたが好きな本屋さんを教えてください
- ・かっこよく答えてください!!
- ・一回も披露したことのない豆知識
- ・ショボ短歌会
- ・いちばん失敗した人決定戦
- ・性格悪い人が優勝
- ・最速怪談選手権
- ・限定しりとり
- ・性格いい人が優勝
- ・これ何て呼びますか
- ・チョコミントアイス
- ・単二電池
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・ゴリラ向け動画サイト「ウホウホ動画」にありがちなこと
- ・泣きながら食べたご飯の思い出
- ・一番好きなみそ汁の具材は?
- ・人生で一番お金がなかったとき
- ・カラオケの鉄板ソング
- ・自分用のお土産
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
「指定されたキャストは有効で...
-
C言語での引数の省略方法
-
複数桁10進数の*桁目だけを抽出...
-
if と配列の組み合わせ
-
#define _CRT_SECURE_NO_WARNIN...
-
C言語 エラーの原因がわからな...
-
数字列を3桁ごとにカンマで区切...
-
実数の整数部,小数部の取得
-
初項a_0=aとし、漸化式 a_n+1=(...
-
このプログラミング誰か教えて...
-
商と剰余を同時に求める(C言語)
-
system関数がうまくいかない
-
ColorをRGBで指定する方法
-
【C++】関数ポインタの使い方
-
構造体のすべての変数を文字列...
-
Win32APIで作るコンボボックス...
-
C 言語の Gauss Jordan 法について
-
C言語のポインタの問題の解答が...
-
(int *)の意味
-
線形補間 2次元テーブル C言語...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
「指定されたキャストは有効で...
-
C言語での引数の省略方法
-
複数桁10進数の*桁目だけを抽出...
-
#define _CRT_SECURE_NO_WARNIN...
-
C言語 エラーの原因がわからな...
-
ラップ関数とはどんなものですか?
-
【C++】関数ポインタの使い方
-
実数の整数部,小数部の取得
-
read関数をノンブロッキングで...
-
(int *)の意味
-
ColorをRGBで指定する方法
-
std::set<int> で、ある値が何...
-
PowerShellがうまくいかない
-
int16_t の _t は何?
-
「{ } で囲むだけ」は正しい?
-
int型の変数値をバイト列として...
-
商と剰余を同時に求める(C言語)
-
return 1L
-
acceptをalarmでタイムアウトさ...
-
(マルチスレッド)_beginthrea...
おすすめ情報