本篇博客有更新!!!更新后效果圖如下:

文章末尾的完整代碼如不能在Dev-C++上完好運行,出現如下問題:
E:\Dev-Cpp\源代碼\萬年歷.c [Error] 'for' loop initial declarations are only allowed in C99 or C11 mode

原因及解決辦法:http://bbs.bccn.net/thread-436527-1-1.html
回歸正題:更新內容為第三問中第2小問的代碼實現(超詳細注釋)
最近幫朋友做一些C語言的練習題,期間遇到了個比較有意思的題目,意在考察模塊化程序設計,大致要求如下:
電子萬年歷:
1、編寫函數int isleapyear(int year);判某年是否為閏年,如該年為閏年返回1,否則返回0。編寫主函數輸入年份給出該年是否為閏年。
2、編寫函數int dayofmonth(int year ,int month);計算某年某月有幾天。主函數中輸入任意的年和月,給出此年該月有幾天。
3、編寫萬年歷。請利用上述1、2已編寫的函數和下邊已提供的函數來完成:
(1)輸入年打印出此年的日歷。
(2)輸入年和月打印給出此年該月的日歷。
1 /*參考代碼*/ 2 int firstdayofyear(int year) /*求某年的第一天是星期幾*/ 3 4 { int i; 5 6 long n,days=year; 7 8 days=days*365; /* printf("\n%ld",days);*/ 9 10 for(i=1;i<year;i++)days=days+isleapyear(i); /*printf("\n%ld",days);*/ 11 12 n=days%7; 13 14 return n; 15 16 } 17 18 int firstdayofmonth(int year, int month) /* 求某年某月的第一天是星期*/ 19 20 { int i,days=0, weekdays; 21 22 weekdays=firstdayofyear(year); 23 24 for(i=1;i<month;i++)days+=dayofmonth(yeari); 25 26 return (days+weekdays)%7; 27 28 }
這道題並不算困難(給出了一些參考代碼),而且根據上面給出的要求也不難得到如下思路:
第一問:根據閏年計算的規則編寫一個閏年判斷函數。
這里我采用的是格里高利閏年規則:
1.公元年份非4的倍數,為平年。
2.公元年份為4的倍數但非100的倍數,為閏年。
3.公元年份為100的倍數但非400的倍數,為平年。
4.公元年份為400的倍數,為閏年。
轉換為C語言代碼形式就成了下面這樣子:
1 int isLeapyear(int year) 2 {//判斷否為閏年 3 //滿足上述規則2或4,返回1,否則返回0 4 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 5 }
第二問:編寫函數計算某年某月有幾天。
由於每一年的天數差別只在二月,而二月份天數的多少又受閏年的影響,所以我們只需要對二月進行特殊判斷即可。可以利用第一問已經寫過的函數,完成第二問,代碼如下:
1 int dayOfMonth(int year, int month) 2 {//計算某年某月的天數 3 int x;//天數 4 switch(month){ 5 case 1:case 3:case 5:case 7:case 8:case 10:case 12: 6 x=31; 7 break; 8 case 2: 9 //閏年2月29天,平年28天 10 x=isLeapyear(year) ? 29 : 28; 11 break; 12 case 4:case 6:case 9:case 11: 13 x=30; 14 break; 15 default: break; 16 } 17 return x; 18 }
第三問:利用已知函數,根據輸入打印出相應的年或月的日歷。
重點在於第一小問,只要我們能正確輸出某一月的日歷,那么輸出某年的日歷就相當於依次輸出1~12月的日歷。參考代碼中已經給出了兩個非常有用的函數,分別是:
- int firstdayofyear(int year) /*求某年的第一天是星期幾*/
- int firstdayofmonth(int year, int month) /* 求某年某月的第一天是星期*/
這里我的想法是先利用firstdayofmonth這個函數計算出某年某月第一天是星期幾,然后計算出該月有多少天,最后利用循環輸出,為了美觀每七天換行一次。代碼如下:
1 void displayMonth(int year, int month) 2 { 3 int start,days;//該月第一天是周幾 該月的天數 4 start=firstdayofmonth(year,month); 5 days=dayOfMonth(year,month); 6 if(start==0)//如果等於零說明第一天剛好是星期天,要轉換一下 7 start=7; 8 printf("一\t二\t三\t四\t五\t六\t日\n"); 9 for(int i=1;i<days+start;++i){ 10 if(i<start)//沒有到該月第一天之前,輸出空白 11 printf(" \t"); 12 else//從1開始依次輸出日期 13 printf("%2d\t",i-start+1); 14 if(i%7==0)//滿七天換行 15 printf("\n"); 16 } 17 }
第二小問就簡單了,循環依次打印每月的日歷就是某年的日歷,代碼如下:
1 void displayYear(int year) 2 {//循環調用打印月份的函數輸出某年的日歷 3 for(int i=1;i<=12;++i){ 4 printf("%d月:\n",i); 5 displayMonth(year,i); 6 printf("\n"); 7 } 8 }
由於初版整體打印效果比較low,這次又進行了改進。思路為每次輸出一行的內容,即第i月和第i+6月(0<i<7)的日期,代碼實現如下(效果圖見文章開頭):
1 void displayYear(int year) { 2 //左邊月的天數 右邊月的天數 左邊月份的第一天 右邊月份的第一天 3 int daysl,daysr,sl,sr; 4 //二乘六的日歷表外循環共執行6次 5 for(int i=1; i<=6; ++i) { 6 //打印星期欄 7 printf(" |%2d Sun Mon Tue Wen Thu Fri Sat %2d Sun Mon Tue Wen Thu Fri Sat |\n",i,i+6); 8 //初始上述變量,m和n分別記錄該月當前應輸出的日期 9 int m=1, n=1; 10 sl=firstdayofmonth(year,i); 11 daysl=dayOfMonth(year,i); 12 sr=firstdayofmonth(year,i+6); 13 daysr=dayOfMonth(year,i+6); 14 //對於不是第一天不是星期天的日期要做加一變化 15 sl = sl > 0 ? sl+1 : 0; 16 sr = sr > 0 ? sr+1 : 0; 17 //該循環每次輸出具體的兩個月份的日歷,如1/7月,2/8月…… 18 while(m < sl+daysl || n < sr+daysr) { 19 //每次循環都使用一個數組記錄要輸出的內容,這個大小只是為了輸出美觀設置的 20 //初始化值為0 21 int out[40]= {0}; 22 //-1代表要輸出表示邊界的"|"符號 23 out[1] = -1; 24 out[39] = -1; 25 //輸出每一行的循環 ,p,q記錄應輸出的位置 26 for(int p=5, q=24; p<18; p+=2, q+=2, ++m, ++n) { 27 //左邊的月份 28 if(m < sl+daysl) { 29 if(m<sl)//未到該月第一天,令值為-2,表示輸出連續的三個空格 30 out[p]=-2; 31 else //記錄正確日期的值 32 out[p]=m-sl+1; 33 } else 34 out[p]=-2;//如果該月日期輸出完畢,其余的部分為了格式一致,要用空格補全 35 36 //右邊的月份,原理同上 37 if(n < sr+daysr) { 38 if(n<sr) 39 out[q]=-2; 40 else 41 out[q]=n-sr+1; 42 } else 43 out[q]=-2; 44 } 45 //按照上述數組保存值所代表的意義打印出來 46 for(int t=0; t<40; ++t) { 47 if(out[t]==-1) 48 printf("|"); 49 else if(out[t]==-2) 50 printf(" "); 51 else if(out[t]==0) 52 printf(" "); 53 else 54 printf("%3d",out[t]); 55 } 56 printf("\n"); 57 } 58 } 59 }
最后把他們整合到成一個程序,代碼如下:
1 #include<stdio.h> 2 3 /** 4 * 目前使用的格里高利歷閏年規則如下: 5 * 1.公元年份非4的倍數,為平年。 6 * 2.公元年份為4的倍數但非100的倍數,為閏年。 7 * 3.公元年份為100的倍數但非400的倍數,為平年。 8 * 4.公元年份為400的倍數,為閏年。 9 */ 10 11 //函數聲明 12 int isLeapyear(int year); //判斷是否為閏年 13 int dayOfMonth(int year, int month); //計算某年某月的天數 14 int firstdayofyear(int year); //求某年的第一天是星期幾 15 int firstdayofmonth(int year, int month);//求某年某月的第一天是星期幾 16 void displayYear(int year); //打印某年的日歷 17 void displayMonth(int year, int month); //打印某年某月的日歷 18 19 int main() { 20 int year,month; 21 printf("請依次輸入年份和月份:\n"); 22 scanf("%d%d",&year,&month); 23 if(year<=0 || month<1 || month>12) 24 printf("輸入不合法!"); 25 else { 26 if(isLeapyear(year)) 27 printf("是閏年,該月有%d天",dayOfMonth(year, month)); 28 else 29 printf("是平年,該月有%d天",dayOfMonth(year, month)); 30 printf("\n%d年的日歷如下:\n",year); 31 printf("\n +---------------------------%d年的年歷--------------------------+\n",year); 32 displayYear(year); 33 printf(" +-----------------------------------------------------------------+\n"); 34 } 35 //displayMonth(year,month); 36 return 0; 37 } 38 39 int isLeapyear(int year) { 40 //判斷否為閏年 41 //滿足上述規則2或4,返回1,否則返回0 42 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 43 } 44 45 int dayOfMonth(int year, int month) { 46 //計算某年某月的天數 47 int x;//天數 48 switch(month) { 49 case 1: 50 case 3: 51 case 5: 52 case 7: 53 case 8: 54 case 10: 55 case 12: 56 x=31; 57 break; 58 case 2: 59 //閏年2月29天,平年28天 60 x=isLeapyear(year) ? 29 : 28; 61 break; 62 case 4: 63 case 6: 64 case 9: 65 case 11: 66 x=30; 67 break; 68 default: 69 break; 70 } 71 return x; 72 } 73 74 int firstdayofyear(int year) { 75 //求某年的第一天是星期幾 76 int i; 77 long days; //天數 78 days=year*365;//從0年到year年一共有多少天 79 for(i=1; i<year; i++) //加上閏年多出的天數 80 days=days+isLeapyear(i); 81 return days%7; //取余得出year年的第一天是星期幾 82 } 83 84 int firstdayofmonth(int year, int month) { 85 //求某年某月的第一天是星期幾 86 int i,days=0,weekdays; //天數 星期幾 87 weekdays=firstdayofyear(year); //得出year年的第一天是weekdays 88 for(i=1; i<month; i++) //計算month月之前的天數days 89 days+=dayOfMonth(year, i); 90 return (days+weekdays)%7; //取余得出year年month月的第一天是星期幾 91 } 92 93 void displayYear(int year) { 94 //左邊月的天數 右邊月的天數 左邊月份的第一天 右邊月份的第一天 95 int daysl,daysr,sl,sr; 96 //二乘六的日歷表外循環共執行6次 97 for(int i=1; i<=6; ++i) { 98 //打印星期欄 99 printf(" |%2d Sun Mon Tue Wen Thu Fri Sat %2d Sun Mon Tue Wen Thu Fri Sat |\n",i,i+6); 100 //初始上述變量,m和n分別記錄該月當前應輸出的日期 101 int m=1, n=1; 102 sl=firstdayofmonth(year,i); 103 daysl=dayOfMonth(year,i); 104 sr=firstdayofmonth(year,i+6); 105 daysr=dayOfMonth(year,i+6); 106 //對於不是第一天不是星期天的日期要做加一變化 107 sl = sl > 0 ? sl+1 : 0; 108 sr = sr > 0 ? sr+1 : 0; 109 //該循環每次輸出具體的兩個月份的日歷,如1/7月,2/8月…… 110 while(m < sl+daysl || n < sr+daysr) { 111 //每次循環都使用一個數組記錄要輸出的內容,這個大小只是為了輸出美觀設置的 112 //初始化值為0 113 int out[40]= {0}; 114 //-1代表要輸出表示邊界的"|"符號 115 out[1] = -1; 116 out[39] = -1; 117 //輸出每一行的循環 ,p,q記錄應輸出的位置 118 for(int p=5, q=24; p<18; p+=2, q+=2, ++m, ++n) { 119 //左邊的月份 120 if(m < sl+daysl) { 121 if(m<sl)//未到該月第一天,令值為-2,表示輸出連續的三個空格 122 out[p]=-2; 123 else //記錄正確日期的值 124 out[p]=m-sl+1; 125 } else 126 out[p]=-2;//如果該月日期輸出完畢,其余的部分為了格式一致,要用空格補全 127 128 //右邊的月份,原理同上 129 if(n < sr+daysr) { 130 if(n<sr) 131 out[q]=-2; 132 else 133 out[q]=n-sr+1; 134 } else 135 out[q]=-2; 136 } 137 //按照上述數組保存值所代表的意義打印出來 138 for(int t=0; t<40; ++t) { 139 if(out[t]==-1) 140 printf("|"); 141 else if(out[t]==-2) 142 printf(" "); 143 else if(out[t]==0) 144 printf(" "); 145 else 146 printf("%3d",out[t]); 147 } 148 printf("\n"); 149 } 150 } 151 } 152 153 void displayMonth(int year, int month) { 154 int i,start,days;//自增變量 該月第一天是周幾 該月的天數 155 start=firstdayofmonth(year,month); 156 days=dayOfMonth(year,month); 157 start = start > 0 ? start+1 : 0; 158 printf("\nSUN MON TUE WEN THU FRI SAT\n"); 159 for(i=1; i<days+start; ++i) { 160 if(i<start) { //沒有到該月第一天之前,輸出空白 161 printf(" "); 162 } else //從1開始依次輸出日期 163 printf("%3d ",i-start+1); 164 if(i%7==0)//滿七天換行 165 printf("\n"); 166 } 167 }
1 #include<stdio.h> 2 3 /** 4 * 目前使用的格里高利歷閏年規則如下: 5 * 1.公元年份非4的倍數,為平年。 6 * 2.公元年份為4的倍數但非100的倍數,為閏年。 7 * 3.公元年份為100的倍數但非400的倍數,為平年。 8 * 4.公元年份為400的倍數,為閏年。 9 */ 10 11 //函數聲明 12 int isLeapyear(int year); //判斷是否為閏年 13 int dayOfMonth(int year, int month); //計算某年某月的天數 14 int firstdayofyear(int year); //求某年的第一天是星期幾 15 int firstdayofmonth(int year, int month);//求某年某月的第一天是星期幾 16 void displayYear(int year); //打印某年的日歷 17 void displayMonth(int year, int month); //打印某年某月的日歷 18 19 int main() 20 { 21 int year,month; 22 printf("請依次輸入年份和月份:\n"); 23 scanf("%d%d",&year,&month); 24 if(year<=0 || month<1 || month>12) 25 printf("輸入不合法!"); 26 else{ 27 if(isLeapyear(year)) 28 printf("是閏年,該月有%d天",dayOfMonth(year, month)); 29 else 30 printf("是平年,該月有%d天",dayOfMonth(year, month)); 31 printf("\n%d年的日歷如下:\n",year); 32 displayYear(year); 33 printf("%d年%d月的日歷如下:\n",year,month); 34 displayMonth(year,month); 35 } 36 return 0; 37 } 38 39 int isLeapyear(int year) 40 {//判斷否為閏年 41 //滿足上述規則2或4,返回1,否則返回0 42 return ((year%100!=0&&year%4==0) || year%400==0) ? 1 : 0; 43 } 44 45 int dayOfMonth(int year, int month) 46 {//計算某年某月的天數 47 int x;//天數 48 switch(month){ 49 case 1:case 3:case 5:case 7:case 8:case 10:case 12: 50 x=31; 51 break; 52 case 2: 53 //閏年2月29天,平年28天 54 x=isLeapyear(year) ? 29 : 28; 55 break; 56 case 4:case 6:case 9:case 11: 57 x=30; 58 break; 59 default: break; 60 } 61 return x; 62 } 63 64 int firstdayofyear(int year) 65 {//求某年的第一天是星期幾 66 long days; //天數 67 days=year*365;//從0年到year年一共有多少天 68 for(int i=1;i<year;i++) //加上閏年多出的天數 69 days=days+isLeapyear(i); 70 return days%7; //取余得出year年的第一天是星期幾 71 } 72 73 int firstdayofmonth(int year, int month) 74 {//求某年某月的第一天是星期幾 75 int days=0,weekdays; //天數 星期幾 76 weekdays=firstdayofyear(year); //得出year年的第一天是weekdays 77 for(int i=1;i<month;i++) //計算month月之前的天數days 78 days+=dayOfMonth(year, i); 79 return (days+weekdays)%7; //取余得出year年month月的第一天是星期幾 80 } 81 82 void displayYear(int year) 83 { 84 for(int i=1;i<=12;++i){//循環調用打印月份的函數輸出某年的日歷 85 printf("%d月:\n",i); 86 displayMonth(year,i); 87 printf("\n"); 88 } 89 } 90 91 void displayMonth(int year, int month) 92 { 93 int start,days;//該月第一天是周幾 該月的天數 94 start=firstdayofmonth(year,month); 95 days=dayOfMonth(year,month); 96 if(start==0)//如果等於零說明第一天剛好是星期天,要轉換一下 97 start=7; 98 printf("一\t二\t三\t四\t五\t六\t日\n"); 99 for(int i=1;i<days+start;++i){ 100 if(i<start)//沒有到該月第一天之前,輸出空白 101 printf(" \t"); 102 else//從1開始依次輸出日期 103 printf("%2d\t",i-start+1); 104 if(i%7==0)//滿七天換行 105 printf("\n"); 106 } 107 }
運行效果見下圖(舊版):

雖然說是萬年歷,但實際有點過於簡單了(只顯示公歷),而且這個輸入范圍也有限制(不能超過int的范圍),不過還是有點參考價值的。如果對你有幫助的話不妨支持一下,非常感謝🙏
