一、项目名称与来源
上海海洋大学C语言期末大作业
二、原项目运行
运行环境:
系统:Windows11 24H2
cpu:i7-10750H
编译器:Dev c++ 5.11
运行结果:
点击查看代码
#include <stdio.h>
#include <stdlib.h>// 定义每个月的天数,第一行为平年,第二行为闰年
int dateofmonth[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};// 英文月份名称数组
char *month1[] = {"January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"
};// 中文月份名称数组
char *month2[] = {"一月", "二月", "三月", "四月", "五月", "六月","七月", "八月", "九月", "十月", "十一月", "十二月"
};// 中文星期名称数组
char *week[7] = {"日", "一", "二", "三", "四", "五", "六"};// 判断是否为闰年
int isLeapYear(int year) {return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}// 计算指定日期是星期几
int getWeekday(int year, int month, int date) {int days = 0;int leap = isLeapYear(year);for (int i = 0; i < month - 1; i++) {days += dateofmonth[leap][i];}days += date;return ((year - 1) + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400 + days) % 7;
}// 打印指定年份的日历
void printCalendar(int year, int month_a, int month_b) {printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", year);for (int i = month_a - 1; i < month_b; i++) {int firstWeekday = getWeekday(year, i + 1, 1);int daysInMonth = dateofmonth[isLeapYear(year)][i];// 打印月份名称printf("\t%-8s\t\t\t\t\t%6s\n", month1[i], month2[i]);printf("\t______________________________________________________\n\t ");// 打印星期名称for (int j = 0; j < 7; j++) {printf("%s\t", week[j]);}printf("\n\n");printf("\t# ");// 打印空白以对齐第一个日期if (firstWeekday > 0) {for (int n = 0; n < firstWeekday; n++) {printf(" \t");}printf(" ");}// 打印日期for (int k = 1; k <= daysInMonth; k++) {if (firstWeekday == 7) {printf("\n\t# ");firstWeekday = 0;}if (firstWeekday == 6) {printf("%2d #", k);} else {printf("%2d\t ", k);}firstWeekday++;}// 补齐行尾if (firstWeekday < 6) {while (firstWeekday++ != 6) {printf(" \t");}printf(" #");} else if (firstWeekday == 6) {printf(" #");}printf("\n\t______________________________________________________\n");printf("\n\n\n\n\n");}
}// 查询指定日期是星期几
void queryWeekday() {int year, month, day;printf("请输入要查询的日期 年 月 日(2024 1 1):");scanf("%d %d %d", &year, &month, &day);printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n\n\n\n\n", year, month, day, week[getWeekday(year, month, day)]);
}// 查询指定年份是否为闰年
void queryLeapYear() {int year;printf("请输入要查询的年份(2024):");scanf("%d", &year);printf("\n\n查询结果如下:\n\n");if (isLeapYear(year)) {printf("%d是闰年\n", year);} else {printf("%d不是闰年\n", year);}
}// 查询指定月份的最大天数
void queryDaysInMonth() {int year, month;printf("请输入要查询的月份(2024 1):");scanf("%d %d", &year, &month);printf("\n\n查询结果如下:\n\n");int leap = isLeapYear(year);printf("%d年%d月有%d天\n\n", year, month, dateofmonth[leap][month - 1]);
}// 主菜单函数
void showMenu() {printf(" 万年历查询系统\n\n");printf("**************************************\n");printf("1:查询某年某月某日是星期几\n");printf("2:查询某年是否是闰年\n");printf("3:打印某年的日历\n");printf("4.查询某月的最大天数\n");printf("5:退出\n");printf("**************************************\n");printf("\n请选择: ");
}int main() {int choice, year;while (1) {showMenu();scanf("%d", &choice);printf("\n");switch (choice) {case 1:queryWeekday();break;case 2:queryLeapYear();break;case 3:printf("请输入要打印的年份(2024):");scanf("%d", &year);printCalendar(year, 1, 12);break;case 4:queryDaysInMonth();break;case 5:return 0;default:printf("\n\n\t输入错误,请重新输入\n\n\n\n");}}return 0;
}
三、主要问题
- 代码冗余与重复
逻辑重复:在多个函数中都存在对闰年判断的逻辑调用,如 getWeekday、printCalendar、queryDaysInMonth 等函数,虽然调用的是同一个 isLeapYear 函数,但多次调用会造成一定的代码冗余。
2.错误处理
输入验证:代码中缺乏对用户输入的验证,例如在 queryWeekday、queryLeapYear 等函数中,用户输入的日期可能不符合实际情况(如月份超出 1 - 12 的范围),但代码没有进行相应的验证和处理,可能会导致程序出现异常。
3.可读性与可维护性
变量命名:部分变量命名不够清晰,例如在 getWeekday 函数中,days 变量虽然能表达其存储的是天数,但没有明确说明是从哪个时间点开始计算的天数,降低了代码的可读性。
注释不足:虽然代码中有一些注释,但整体注释不够详细,特别是一些关键逻辑部分,如计算星期几的公式,没有详细解释,不利于后续的维护和扩展。
代码结构:代码整体结构较为复杂,函数内部的逻辑较多,例如 printCalendar 函数中包含了打印月份名称、星期名称、日期以及处理节假日等多个功能,代码过长,可将一些功能拆分成更小的函数,提高代码的可维护性。
四、优化
1.添加节假日显示
点击查看代码
// 打印指定年份的日历
void printCalendar(int year, int month_a, int month_b) {printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", year);for (int i = month_a - 1; i < month_b; i++) {int firstWeekday = getWeekday(year, i + 1, 1);int daysInMonth = dateofmonth[isLeapYear(year)][i];// 打印月份名称printf("\t%-8s\t\t\t\t\t%6s\n", month1[i], month2[i]);printf("\t______________________________________________________\n\t ");// 打印星期名称for (int j = 0; j < 7; j++) {printf("%s\t", week[j]);}printf("\n\n");printf("\t# ");// 打印空白以对齐第一个日期if (firstWeekday > 0) {for (int n = 0; n < firstWeekday; n++) {printf(" \t");}printf(" ");}// 打印日期for (int k = 1; k <= daysInMonth; k++) {if (firstWeekday == 7) {printf("\n\t# ");firstWeekday = 0;}int isHoliday = 0;for (int h = 0; h < sizeof(holidays) / sizeof(holidays[0]); h++) {if (holidays[h][0] == i + 1 && holidays[h][1] == k) {isHoliday = 1;printf("\033[31m%2d(%s)\033[0m\t ", k, holiday_names[holidays[h][2] - 1]);break;}}if (!isHoliday) {if (firstWeekday == 6) {printf("%2d #", k);} else {printf("%2d\t ", k);}}firstWeekday++;}// 补齐行尾if (firstWeekday < 6) {while (firstWeekday++ != 6) {printf(" \t");}printf(" #");} else if (firstWeekday == 6) {printf(" #");}printf("\n\t______________________________________________________\n");printf("\n\n\n\n\n");}
}
2.添加生肖和星座查询
点击查看代码
// 查询指定日期是星期几
void queryWeekday() {int year, month, day;printf("请输入要查询的日期 年 月 日(2024 1 1):");scanf("%d %d %d", &year, &month, &day);printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n", year, month, day, week[getWeekday(year, month, day)]);// 显示星座int constellation_index;if ((month == 1 && day >= 20) || (month == 2 && day <= 18)) {constellation_index = 1;} else if ((month == 2 && day >= 19) || (month == 3 && day <= 20)) {constellation_index = 2;} else if ((month == 3 && day >= 21) || (month == 4 && day <= 19)) {constellation_index = 3;} else if ((month == 4 && day >= 20) || (month == 5 && day <= 20)) {constellation_index = 4;} else if ((month == 5 && day >= 21) || (month == 6 && day <= 20)) {constellation_index = 5;} else if ((month == 6 && day >= 21) || (month == 7 && day <= 22)) {constellation_index = 6;} else if ((month == 7 && day >= 23) || (month == 8 && day <= 22)) {constellation_index = 7;} else if ((month == 8 && day >= 23) || (month == 9 && day <= 22)) {constellation_index = 8;} else if ((month == 9 && day >= 23) || (month == 10 && day <= 22)) {constellation_index = 9;} else if ((month == 10 && day >= 23) || (month == 11 && day <= 21)) {constellation_index = 10;} else if ((month == 11 && day >= 22) || (month == 12 && day <= 21)) {constellation_index = 11;} else {constellation_index = 0;}printf("星座是:%s\n", constellations[constellation_index]);// 显示生肖int zodiac_index = (year - 1900) % 12;printf("生肖是:%s\n\n\n\n", zodiacs[zodiac_index]);
}
3.倒计时功能
点击查看代码
// 倒计时功能
void countdown() {int year1, month1, day1, year2, month2, day2;printf("请输入当前日期 年 月 日(2024 1 1):");scanf("%d %d %d", &year1, &month1, &day1);printf("请输入目标日期 年 月 日(2024 1 1):");scanf("%d %d %d", &year2, &month2, &day2);int days1 = 0, days2 = 0;for (int y = 1900; y < year1; y++) {days1 += isLeapYear(y) ? 366 : 365;}int leap1 = isLeapYear(year1);for (int m = 0; m < month1 - 1; m++) {days1 += dateofmonth[leap1][m];}days1 += day1;for (int y = 1900; y < year2; y++) {days2 += isLeapYear(y) ? 366 : 365;}int leap2 = isLeapYear(year2);for (int m = 0; m < month2 - 1; m++) {days2 += dateofmonth[leap2][m];}days2 += day2;int diff = days2 - days1;if (diff > 0) {printf("\n距离 %d年%d月%d日 还有 %d 天\n\n\n", year2, month2, day2, diff);} else if (diff < 0) {printf("\n %d年%d月%d日 已经过去 %d 天\n\n\n", year2, month2, day2, -diff);} else {printf("\n今天就是 %d年%d月%d日\n\n\n", year2, month2, day2);}
}
4.对代码的优化和精简
点击查看代码
#include <stdio.h>// 每月天数,[0]平年 [1]闰年
int dateofmonth[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
const char *month1[] = {"January", "February", "March", "April", "May", "June","July", "August", "September", "October", "November", "December"};
const char *month2[] = {"一月", "二月", "三月", "四月", "五月", "六月","七月", "八月", "九月", "十月", "十一月", "十二月"};
const char *week[7] = {"日", "一", "二", "三", "四", "五", "六"};
const int holidays[][3] = {{1, 1, 1}, {2, 14, 2}, {3, 8, 3}, {4, 1, 4}, {5, 1, 5},{6, 1, 6}, {9, 10, 7}, {10, 1, 8}, {12, 25, 9}};
const char *holiday_names[] = {"元旦", "情人节", "妇女节", "愚人节", "劳动节","儿童节", "教师节", "国庆节", "圣诞节"};
const char *constellations[] = {"摩羯座", "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座","巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"};
const int constellation_bounds[12] = {20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22};
const char *zodiacs[] = {"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};// 判断闰年
int isLeapYear(int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }// 计算星期
int getWeekday(int y, int m, int d) {int days = d;for (int i = 0; i < m - 1; i++) days += dateofmonth[isLeapYear(y)][i];return (y - 1 + (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + days) % 7;
}// 打印日历
void printCalendar(int y) {printf("\n\n\n\t—————————————%d 年日历—————————————\n\n\n", y);for (int m = 0; m < 12; m++) {int first = getWeekday(y, m + 1, 1), days = dateofmonth[isLeapYear(y)][m];printf("\t%-8s\t\t\t\t\t%6s\n", month1[m], month2[m]);printf("\t______________________________________________________\n\t ");for (int i = 0; i < 7; i++) printf("%s\t", week[i]);printf("\n\n\t# ");for (int i = 0; i < first; i++) printf(" \t");for (int d = 1; d <= days; d++) {if ((first + d - 1) % 7 == 0) printf("\n\t# ");int isHoliday = 0;for (int h = 0; h < sizeof(holidays) / sizeof(holidays[0]); h++)if (holidays[h][0] == m + 1 && holidays[h][1] == d) {isHoliday = 1;printf("\033[31m%2d(%s)\033[0m\t ", d, holiday_names[holidays[h][2] - 1]);break;}if (!isHoliday) printf("%2d\t ", d);}printf("\n\t______________________________________________________\n\n\n\n\n");}
}// 查询星期、星座、生肖
void queryWeekday() {int y, m, d;printf("请输入要查询的日期 年 月 日(2024 1 1):");scanf("%d %d %d", &y, &m, &d);int w = getWeekday(y, m, d), c = (m - 1 + (d < constellation_bounds[m - 1] ? 0 : 1)) % 12, z = (y - 1900) % 12;printf("\n\n查询结果如下:\n\n%d年%d月%d日是星期%s\n星座是:%s\n生肖是:%s\n\n\n\n", y, m, d, week[w], constellations[c], zodiacs[z]);
}// 查询闰年
void queryLeapYear() {int y;printf("请输入要查询的年份(2024):");scanf("%d", &y);printf("\n\n查询结果如下:\n\n%d %s闰年\n\n", y, isLeapYear(y) ? "是" : "不是");
}// 查询月最大天数
void queryDaysInMonth() {int y, m;printf("请输入要查询的月份(2024 1):");scanf("%d %d", &y, &m);printf("\n\n查询结果如下:\n\n%d年%d月有%d天\n\n", y, m, dateofmonth[isLeapYear(y)][m - 1]);
}// 倒计时
void countdown() {int y1, m1, d1, y2, m2, d2;printf("请输入当前日期 年 月 日(2024 1 1):");scanf("%d %d %d", &y1, &m1, &d1);printf("请输入目标日期 年 月 日(2024 1 1):");scanf("%d %d %d", &y2, &m2, &d2);int days1 = 0, days2 = 0;for (int y = 1900; y < y1; y++) days1 += isLeapYear(y) ? 366 : 365;for (int m = 0; m < m1 - 1; m++) days1 += dateofmonth[isLeapYear(y1)][m];days1 += d1;for (int y = 1900; y < y2; y++) days2 += isLeapYear(y) ? 366 : 365;for (int m = 0; m < m2 - 1; m++) days2 += dateofmonth[isLeapYear(y2)][m];days2 += d2;int diff = days2 - days1;printf("\n%s %d年%d月%d日 %s %d 天\n\n\n",diff > 0 ? "距离" : diff < 0 ? "" : "今天就是",y2, m2, d2,diff > 0 ? "还有" : diff < 0 ? "已经过去" : "",diff > 0 ? diff : -diff);
}// 主菜单
void showMenu() {printf(" 万年历查询系统\n\n");printf("**************************************\n");printf("1:查询某年某月某日是星期几\n");printf("2:查询某年是否是闰年\n");printf("3:打印某年的日历\n");printf("4:查询某月的最大天数\n");printf("5:倒计时功能\n");printf("6:退出\n");printf("**************************************\n");printf("\n请选择: ");
}int main() {int choice, year;while (1) {showMenu();scanf("%d", &choice);printf("\n");switch (choice) {case 1: queryWeekday(); break;case 2: queryLeapYear(); break;case 3: printf("请输入要打印的年份(2024):"); scanf("%d", &year); printCalendar(year); break;case 4: queryDaysInMonth(); break;case 5: countdown(); break;case 6: return 0;default: printf("\n\n\t输入错误,请重新输入\n\n\n\n");}}return 0;
}
五、实现效果
6、总结
难点
1.节假日显示处理
难点:要在日历中准确显示节假日,需要对每个日期进行判断,并且要考虑到不同年份的情况。同时,为了增强显示效果,使用了 ANSI 转义序列来设置节假日日期的颜色,这涉及到对控制台输出格式的控制。
耗时原因:需要设计合理的数据结构来存储节假日信息,并且要编写循环逻辑来遍历每个日期并检查是否为节假日。此外,还要处理不同操作系统和终端对 ANSI 转义序列的支持情况,确保显示效果的一致性。
2.代码优化与精简
难点:在保证代码功能完整的前提下,要对代码进行优化和精简,减少代码冗余,提高代码的可读性和可维护性。这需要对代码结构有深入的理解,并且要掌握一些优化技巧,如函数封装、变量命名规范等。
耗时原因:需要反复审查代码,找出可以优化的部分,并进行多次修改和测试,以确保优化后的代码仍然能够正常工作。
思考
1.代码重构和优化
逆向工程的结果可以为代码重构和优化提供依据。通过分析代码的结构和逻辑,可以找出可以改进的部分,并进行相应的重构和优化。例如,可以将一些重复的代码提取成独立的函数,减少代码冗余;可以优化循环逻辑,提高代码的执行效率。
2.发现潜在问题
逆向工程可以帮助发现代码中存在的潜在问题。例如,在分析代码时,可以发现代码中缺乏输入验证的问题,这可能会导致程序在接收到非法输入时出现异常。此外,还可以发现代码中存在的性能问题,如重复计算、循环嵌套过深等,这些问题可能会影响程序的运行效率。