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;
	}
}

輸出結果如下圖,可以多次輸入,一次性查找多個日期:
萬年曆運行結果

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