C语言结构体的应用——万年历

万年历简述

万年历——就是输入一个日期可以查询是星期几,这个功能看起来很普通,但是如果用程序时间的话,还是药费一番周折:

  1. 我们需要保存一个固定的日期,存放它是星期几,输入一个自定义的日期,通过二者的日期差推断出输入日是星期几
  2. 我们需要考虑每一年是闰年还是平年,这个关系我们的日期差到底是几。所以需要创建一个返回bool型变量的函数bool isleapyear(int year)
  3. 我们使用的语言是C语言不是Visual Basic。在VB里面是有日期类型(date)的变量的,加减非常方便,但是C里面没有,我们可以通过结构体定义一个
      typedef struct{int year;typedef struct{int year;
    	int month;
    	int day;
    	enum Weekdays weekday;} Date;

将一个时间点的年、月、日、星期建立联系,方便我们后面的输出和查找。

  1. 每个月的天数是不一样的,而这个日期的分布规律又过于复杂,我们不妨设一个数组来存放月份的天数。2月份的天数初始化的时候可以设为28,如果isleapyear(int year)==true,再将2月份天数改为29,否则设为28。
  2. 计算两个日期的日期差,如果两个日期的年份>2,则需要通过闰年判断函数对每个年份判断,并确定应该分别加多少,而对零头(也就是一年中的哪一天),我们需要另外设一个函数int count_days(int *days,Date *datetime);,通过传递days月份天数数组和一个日期,来计算该日期在该年是哪一天。假设参考日期在所在年的天数是day1,所在年天数为year1,输入日期在所在年的天数是day2,所在年天数为year2,两年所隔的年份总天数为是years,则相隔日期Δ\Deltadays=year1-day1+years+day2;
  3. 我们在程序里面设计一个逻辑性变量bool next,每次运行完一个循环都扫描一下逻辑变量的值,如果用户输入1代表继续(其实非零值均可),输入0代表结束,整个过程使用do…whie循环控制,并且在循环体末尾要加上扫描是否继续的选择到next逻辑变量中的语句,即:
do{
...
printf("\n是否继续?(是:1,否:0): ")
scanf("%d",&next);
fflush(stdin);//在循环中使用scanf必须要清空输入缓存区,否则执行完1次循环会跳过循环体中的第一个scanf
)while(next);

使用一个判断是否继续的变量可以更好地实现人机交互的功能,同时也可以一次性多次测试代码的正确性。
7. 由于日期差值是负的还是整的关系到我们的计算结果,直接不管正负地模7取余是有问题的,所有我们保存7个参考日期,如果相减可以被7整除就代表是星期几。

代码

#include <stdio.h>
#include <stdbool.h>
#include <malloc.h>
#include <math.h>
#define LEN sizeof(Date)
enum Weekdays{
	Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday 
};//定义weekday枚举型变量
typedef struct {
	int year;
	int month;
	int day;
	enum Weekdays weekday;
}Date;//定义日期的结构体变量 
int days[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的日期,可以借助modify_year修改
Date refdate[7];//定义7个参考日期,依次为周一到周日
int datedistract(Date date1,Date date2); //日期相减的函数 
bool isleapyear(int year);//判断闰年的函数 
void modify_days(int *days,int year);//通过闰年对月份日期修改的函数 
void findweekday(Date *datetime);
void weekdayprint(enum Weekdays weekday);
int count_days(int *days,Date *datetime); //计算结构体日期变量在一年中处于哪一天 
int main()
{
	printf("************日期计算实验****************\n");
	bool next;//是否继续
	do{
		Date *datetime=malloc(LEN);//定义Date类型的结构体指针datetime,同时开辟一段内存(不开辟内存会使程序崩溃)
		int i;
		for(i=0;i<7;i++)
		{
			refdate[i].year=2018;
			refdate[i].month=12;
			refdate[i].day=10+i;
		}//refdate结构体数组存放了2018/12/20~2018/12/26的7个日期,他们正好是周一到周日 
		printf("请输入一个日期(year/month/day): ");
		scanf("%d/%d/%d",&datetime->year,&datetime->month,&datetime->day);
		printf("该日期所在年份中的天数为%d\t",count_days(days,datetime)); 
		findweekday(datetime);
		weekdayprint(datetime->weekday);
		printf("\n是否继续?(是:1,否:0):  ");
		//是否继续的判断如下
		scanf("%d",&next); 
	 	fflush(stdin);//刷新标准输入流,否则循环的时候会跳过第一个scanf 
	} while(next);
	return 0; 
}
bool isleapyear(int year)
{
	if(year%4==0)
	{
		if(year%100==0)
		{
			if(year%400==0)return true;
			else return false;
		}
		else return true;
	}
	else return false;
}
//闰年修改函数 
void modify_days(int *days,int year)
{
	if(isleapyear(year)==true)days[1]=29;
	else days[1]=28;
}
int count_days(int *days,Date *datetime)//将该年各月份日期已经结构体变量录入,计算在该年的天数 
{
	int i,count=0;
	modify_days(days,datetime->year);
	for(i=0;i<datetime->month-1;i++)count+=days[i];
	count+=datetime->day;
	return count; 
}
int datedistract(Date date1,Date date2)
{
	int year,month,day,count=0;
	bool cmp;//比较两个日期哪个在前面,方便写循环
	Date temp;//作为交换变量的中间变量
	if(date1.year>date2.year)cmp=true;
	else if(date1.year<date2.year)cmp=false;
	else if(date1.month>date2.month)cmp=true;
	else if(date1.month<date2.month)cmp=false;
	else if(date1.day>date2.day)cmp=true;
	else cmp=false;
	if(cmp)
	{
		temp=date1;
		date1=date2;
		date2=temp;
	}
	//如果date1在date2后面,则交换二者,保证date1始终比date2小
	for(year=date1.year;year<date2.year;year++)count+=isleapyear(year)?366:365;//按照是否为闰年书累加
	count+=count_days(days,&date2)-count_days(days,&date1);//计算零头
	return abs(count);//返回所差日期
}
void findweekday(Date *datetime)
{
	int i;
	for(i=0;i<7;i++)
	{
		if(datedistract(*datetime,refdate[i])%7==0)
		{
			datetime->weekday=i;
			break;
		}
	}
}
void weekdayprint(enum Weekdays weekday)
{
	switch(weekday)
	{
		case 0:printf("星期一");break;
		case 1:printf("星期二");break;
		case 2:printf("星期三");break;
		case 3:printf("星期四");break;
		case 4:printf("星期五");break;
		case 5:printf("星期六");break;
		case 6:printf("星期日");break;
	}
}

输出结果如下图,可以多次输入,一次性查找多个日期:
万年历运行结果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章