ページ 1 / 1
difftime関数が0を返す
Posted: 2014年11月30日(日) 09:06
by 担麺々
年・月・日を入力させ、答えがそれより前か後かを表示して、ランダムな年・月・日を当てさせるゲームを作っています。
ソースコードは下記のとおりなのですが、プログラムを実行すると、どんな年・月・日を入力しても「正解です。」と表示されてしまいます。
difftime関数の返却値を代入した、変数diftmの値が0になっていることが問題だと思われますが、なぜ変数diftmの値が0になるのでしょうか。ご教示いただければ幸いです。
コード:
#include <stdio.h>
#include <time.h>
int is_leap(int year);
int monthdays(int year, int month);
int main(void)
{
time_t crtt;
struct tm *crtlt;
struct tm anslt = {0};
time_t anst;
struct tm inlt = {0};
int diftm = 1;
srand(time(&crtt));
crtlt = localtime(&crtt);
anst = mktime(&anslt);
anslt.tm_year = 1 + rand() % crtlt->tm_year;
anslt.tm_mon = rand() % 12;
anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1900年から現在の年までです。\n");
do {
printf("年:"); scanf("%d", &inlt.tm_year);
printf("月:"); scanf("%d", &inlt.tm_mon);
printf("日:"); scanf("%d", &inlt.tm_mday);
if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
printf("入力が不正です。再入力してください。\n");
else if (inlt.tm_year < 1900 || inlt.tm_year > crtlt->tm_year + 1900)
printf("答えは1900年から現在の年までです。再入力してください。\n");
else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
printf("入力が不正です。再入力してください。\n");
else {
time_t intm;
inlt.tm_year -= 1900;
inlt.tm_mon--;
printf("%d\n", diftm); /*デバッグ用*/
intm = mktime(&inlt);
diftm = (int)difftime(anst, intm);
printf("%d\n", diftm); /*デバッグ用*/
if (diftm > 0)
printf("それよりも後です。\n");
else if (diftm < 0)
printf("それよりも前です。\n");
}
printf("%d\n", diftm); /*デバッグ用*/
} while (diftm);
printf("正解です。\n");
return 0;
}
int is_leap(int year)
{
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
int monthdays(int year, int month)
{
int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month-- != 2)
return mday[month];
return (mday[month] + is_leap(year));
}
Re: difftime関数が0を返す
Posted: 2014年11月30日(日) 09:48
by みけCAT
試しにperrorを追加したコード
コード:
#include <stdio.h>
#include <time.h>
#include <stdlib.h> /* srand()とrand()を使うので、あった方がいい */
int is_leap(int year);
int monthdays(int year, int month);
int main(void)
{
time_t crtt;
struct tm *crtlt;
struct tm anslt = {0};
time_t anst;
struct tm inlt = {0};
int diftm = 1;
srand(time(&crtt));
crtlt = localtime(&crtt);
perror("1-localtime"); /* 追加 */
anst = mktime(&anslt);
perror("2-mktime"); /* 追加 */
anslt.tm_year = 1 + rand() % crtlt->tm_year;
anslt.tm_mon = rand() % 12;
anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1900年から現在の年までです。\n");
do {
printf("年:"); scanf("%d", &inlt.tm_year);
printf("月:"); scanf("%d", &inlt.tm_mon);
printf("日:"); scanf("%d", &inlt.tm_mday);
if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
printf("入力が不正です。再入力してください。\n");
else if (inlt.tm_year < 1900 || inlt.tm_year > crtlt->tm_year + 1900)
printf("答えは1900年から現在の年までです。再入力してください。\n");
else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
printf("入力が不正です。再入力してください。\n");
else {
time_t intm;
inlt.tm_year -= 1900;
inlt.tm_mon--;
printf("%d\n", diftm); /*デバッグ用*/
intm = mktime(&inlt);
perror("3-mktime"); /* 追加 */
diftm = (int)difftime(anst, intm);
perror("4-diftm"); /* 追加 */
printf("%d\n", diftm); /*デバッグ用*/
if (diftm > 0)
printf("それよりも後です。\n");
else if (diftm < 0)
printf("それよりも前です。\n");
}
printf("%d\n", diftm); /*デバッグ用*/
} while (diftm);
printf("正解です。\n");
return 0;
}
int is_leap(int year)
{
/* 警告されたのでカッコを追加 */
/* return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; */
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int monthdays(int year, int month)
{
int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month-- != 2)
return mday[month];
return (mday[month] + is_leap(year));
}
を実行すると、
コード:
YUKI.N>gcc -g3 -Wall -Wextra -o difftime_returns_0_mod1.exe difftime_returns_0_m
od1.c
YUKI.N>difftime_returns_0_mod1.exe
1-localtime: No error
2-mktime: Invalid argument
日付当てゲームを開始します。
年月日を当ててください。
なお、答えは1900年から現在の年までです。
年:1999
月:1
日:1
1
3-mktime: Invalid argument
4-diftm: Invalid argument
0
0
正解です。
YUKI.N>
と表示されました。
したがって、45行目のmktimeがうまくいっていないことがわかります。
原因は調査中です。
【訂正】うまくいっていないのは18行目のmktimeの方でした。
Re: difftime関数が0を返す
Posted: 2014年11月30日(日) 10:02
by みけCAT
18行目のmktimeで、20~22行目でansltにデータを設定する前にansltを使って時刻データを生成しようとしているのが問題だと思います。
さらに、少なくとも自分の環境(GCC 4.8.1)では、mktimeでだいたい1971年より前の日付は作れないようです。
このことは以下のコードで確認しました。
コード:
#include <stdio.h>
#include <time.h>
#include <errno.h>
int main(void) {
struct tm t={0};
time_t mkedtime;
int i;
t.tm_mon = 0;
t.tm_mday = 1;
for(i=0;i<100;i++) {
t.tm_year = i;
errno = 0;
mkedtime = mktime(&t);
printf("%d/%d/%d -> %.0f\n",t.tm_year,t.tm_mon,t.tm_mday,(double)mkedtime);
perror("mktime");
}
return 0;
}
出力の一部
コード:
67/0/1 -> -1
mktime: Invalid argument
68/0/1 -> -1
mktime: Invalid argument
69/0/1 -> -1
mktime: Invalid argument
70/0/1 -> -1
mktime: Invalid argument
71/0/1 -> 31503600
mktime: No error
72/0/1 -> 63039600
mktime: No error
73/0/1 -> 94662000
mktime: No error
74/0/1 -> 126198000
mktime: No error
Re: difftime関数が0を返す
Posted: 2014年11月30日(日) 11:05
by 担麺々
>みけCAT さん
ありがとうございます。変数ansltにデータを設定した後にmetime関数で時刻データを生成するよう修正しました。また、私もGCC 4.8.1を使用しているので、答えが1971年以降になるよう修正しました。
コード:
/* main関数以外は同じ*/
int main(void)
{
time_t crtt;
struct tm *crtlt;
struct tm anslt = {0};
time_t anst;
struct tm inlt = {0};
int diftm = 1;
srand(time(&crtt));
crtlt = localtime(&crtt);
anslt.tm_year = 71 + rand() % (crtlt->tm_year - 71);
anslt.tm_mon = rand() % 12;
anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
anst = mktime(&anslt);
printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1971年から現在の年までです。\n");
do {
printf("年:"); scanf("%d", &inlt.tm_year);
printf("月:"); scanf("%d", &inlt.tm_mon);
printf("日:"); scanf("%d", &inlt.tm_mday);
if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
printf("入力が不正です。再入力してください。\n");
else if (inlt.tm_year < 1971 || inlt.tm_year > crtlt->tm_year + 1900)
printf("答えは1971年から現在の年までです。再入力してください。\n");
else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
printf("入力が不正です。再入力してください。\n");
else {
time_t intm;
inlt.tm_year -= 1900;
inlt.tm_mon--;
intm = mktime(&inlt);
diftm = (int)difftime(anst, intm);
/* ---デバッグ用--- */
printf("%d\n%d\n%d\n\n", diftm, (int)anst, (int)intm);
printf("%d\n%d\n%d\n", anslt.tm_year, anslt.tm_mon, anslt.tm_mday);
printf("%d\n%d\n%d\n\n", inlt.tm_year, inlt.tm_mon, inlt.tm_mday);
/* --------------*/
if (diftm > 0)
printf("それよりも後です。\n");
else if (diftm < 0)
printf("それよりも前です。\n");
}
} while (diftm);
printf("正解です。\n");
return 0;
}
それが上記のコードなのですが、実行すると、どのような年・月・日を入力しても、diftm == anst となります。
変数intmの値が0だとそのようになりますが、実行結果から、変数intmの値やtm構造体のメンバの値は正常であるように思いました。
どの点が間違っているのでしょうか。度々すみません。
Re: difftime関数が0を返す
Posted: 2014年11月30日(日) 11:27
by みけCAT
Re: difftime関数が0を返す
Posted: 2014年11月30日(日) 12:36
by 担麺々
>みけCAT さん
ありがとうございます。ご助言いただいたように、difftime関数を用いずに、変数anstとintmを直接比較する方法をとったところ、正常に動作しました。ありがとうございました。
コード:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int is_leap(int year);
int monthdays(int year, int month);
int main(void)
{
time_t crtt;
struct tm *crtlt;
struct tm anslt = {0};
time_t anst;
struct tm inlt = {0};
time_t intm;
srand(time(&crtt));
crtlt = localtime(&crtt);
anslt.tm_year = 71 + rand() % (crtlt->tm_year - 71);
anslt.tm_mon = rand() % 12;
anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
anst = mktime(&anslt);
printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1971年から現在の年までです。\n");
do {
printf("年:"); scanf("%d", &inlt.tm_year);
printf("月:"); scanf("%d", &inlt.tm_mon);
printf("日:"); scanf("%d", &inlt.tm_mday);
if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
printf("入力が不正です。再入力してください。\n");
else if (inlt.tm_year < 1971 || inlt.tm_year > crtlt->tm_year + 1900)
printf("答えは1971年から現在の年までです。再入力してください。\n");
else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
printf("入力が不正です。再入力してください。\n");
else {
inlt.tm_year -= 1900;
inlt.tm_mon--;
intm = mktime(&inlt);
if (anst > intm)
printf("それよりも後です。\n");
else if (anst < intm)
printf("それよりも前です。\n");
}
} while (anst != intm);
printf("正解です。\n");
return 0;
}
int is_leap(int year)
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int monthdays(int year, int month)
{
int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month-- != 2)
return mday[month];
return (mday[month] + is_leap(year));
}