采用自顶向下、逐步求精的设计方法。这种分解问题的思想很重要,可以帮助我们更好地分析疑难问题,特别是对于工程项目,较多的功能和耦合会让问题无从下手,只有采用自顶向下、逐步求精、分而治之的思想才能很好地解决这类问题!

题目:根据用户输入年份,显示该年12个月的日历,并对应具体的星期几。

分析:首先根据问题处理的思路,可以分为三个过程:UserInput,Process,Print。UserInput主要用于接收用户输入的年份,并将输入传给Process。Process接收用户输入的年份,根据基准时间(1990年1月1日是星期一)计算输入年份中每一月中每一天对应的是星期几。这里面存在这样几个问题,第一个,需要计算当前年份每个月的日历,然后循环12次即可,每个月的月历需要计算本月之前的月份到1990年之间的天数,再加上本月的天数。由于2月天数的特殊性,所以还需要进行闰年的判断。第二个问题是如何计算一个月的月历。每月的第一天可以通过对7取余数得到对应的是周几,然后依次往后计算,以该月份天数为循环次数。值得注意的一点是每到周六,要进行换行。

程序设计过程:

1.      自顶向下

我们从主程序开始,考虑程序要做什么?一般来说,用户只需要输入年份,并按回车即可,就可以显示该年的日历。在用户输入之前,最好能给用户一些提示性的语句作为Instruction。

所以,void GiveInstruction(void)函数就会得到,函数语句如下:

voidGiveInstruction(void)         //tell theuser how to do{printf("This program displays a calendarfor a full\n");printf("year. The year must not be before1900.\n");}

这种方式会让用户觉得程序更为友好。下面就需要逐步精化,逐步精化的原则是:一旦你有了某个程序的概要描述,就应该在此结束,并把它写下来,调用其他函数或者过程,以此来表示那些还要继续细化的程序。

这里需要注意的是“过程”指的是不返回值而是起到某些作用的函数,我们称之为过程。

由于函数的一般写法是:

返回值的类型或者无返回值函数名称(形参类型1 形参名1,形参类型2 形参名2……)

所以可以根据函数的一般写法确定过程函数的写法,由于过程函数没有返回值,则void。过程函数的写法如下:

void 函数名称(形参类型1 形参名1,形参类型2 形参名2……)

我们继续回到自顶向下的设计中,根据刚才的描述,主程序可以写成:

main(){intYear;GiveInstruction();Year=GetYearFromUser();PrintCalendar(Year);}

主函数中包含的三个子函数很容易理解,其中核心函数是PrintCalendar(Year),该函数要能根据用户输入的年份,输出日历。下面采用逐步精化来分解PrintCalendar(Year)函数。

2.      逐步精化PrintCalendar(Year)

虽然上述三个函数都需要编写,但是我们直接进入问题的核心,根据核心函数来布置或者安排其他附属函数的功能。从main的调用来看,PrintCalendar(Year)的原型是

void PrintCalendar(int Year);

我们在抽象级别考虑这个函数,考虑它需要做什么。它需要打印12个月的日历,好了,到了这里,下一步的想法有了,使用for循环再加上每个月的日历函数就可以实现。代码如下:

voidPrintCalendar(int year){int month;for(month=1;month<=12;month++){PrintCalendarMonth(month,year);//print the every month is the essence of programprintf("\n");}}

下面的问题就转化成了PrintCalendarMonth(month,year),我们注意这个函数具有两个输入month,year。year来自于用户输入,而month表示一年的12个月。此时显然闰年的问题出现了,在考虑二月份天数的时候,就必须考虑闰年。闰年函数我们已经非常熟悉,如下:

int IsLeapYear(int year) //judge the inputnumber is leapyear or not{return((year%400==0)||((year%4==0)&&(year%100!=0)));}

闰年的问题考虑清楚之后,就要回头注意日历的输出格式。

由于每个月我们都需要这样输出,所以只要能实现一个,下面的就只是“量”的问题了。这个日历的前两行也相当容易,比较麻烦的是数字和上面的weekday要对上。这里面存在三个问题,第一个,每个月的1号前面的空格要正好合适;第二个,每个月的周六要换行;第三个,每个数字之间有间隔。

先解决第一个问题,由于每个月的1号对应的weekday可能不同,则前面的空格数也不同。所以,我们就要知道每个月1号对应的weekday。假设我们知道每个月的1号对应的weekday,则空格函数就可以写出来了。如下:

voidIndentFirstLine(int weekday) //The spacebar of the first row of every month{int i;for(i=0;i<weekday;i++){printf("   ");}}

下面就需要知道每个月的1号对应的是哪个weekday,即intFirstDayOfMonth(month,year)函数。这里就需要一个具体的参照,比如我们知道历史上某年某月某日对应的是周几,就可以根据天数和月数推算出来用户输入的年份里面某个月的第一天是哪个weekday。例如,1900年1月1日是星期一。从这天开始,按是否是闰年,每年加365或者366天。对于当年要处理的月份之前的月,加上这个月的天数。用取模运算计算。比如,我们要计算2018年2月,就可以先按照年计算到2018年1月的第一天是周几,只需要(weekday+365/366)%7,然后再按照月计算到2月,只需要(weekday+month(I,year))%7。由于1900年1月1日是星期一,所以weekday初始值是Monday。intFirstDayOfMonth(month,year)函数代码如下:

intFirstDayOfMonth(month,year) //know first day of every month is which one ofweekday{int weekday,i;weekday=Monday;for(i=1900;i<year;i++) //firstaccording to the year{weekday=(weekday+365)%7; // %can get the weekdayif(IsLeapYear(i))  weekday=(weekday+1)%7;}for(i=1;i<month;i++) //secondaccording to the month{weekday=(weekday+MonthDays(i,year))%7;}return weekday;}

这里有一个技巧,由于我们使用取模运算,因此可以设定weekday代表的数字,这样进行取模运算时会非常方便。如下:

#defineSunday 0      //easy to qiumo(%)#defineMonday 1#defineTuesday 2#defineWednesday 3#defineThursday 4#defineFriday 5#defineSaturday 6

由于intFirstDayOfMonth(month,year)函数用到了统计每个月天数的函数MonthDays(i,year),这个很简单,因此我们也顺便把这个函数写在下面:

intMonthDays(int month,int year){switch(month){case 2:if(IsLeapYear(year))return 29;return 28;case 4: case 6: case 9: case11:return 30;default:return 31;}}

这样的话,第一个问题——每个月的1号前面的空格要正好合适,就可以通过回调每月1号的weekday解决。

下面就要解决换行和间距的问题。换行的问题并不难,以每个月的天数作为for条件,让每个月1号的weekday加1(define的好处是让取模之后0~6范围内的数字和代表的weekday可以随时转换)然后对7取模。当weekday==Saturday时,换行。这里Saturday也可以换成6,define的好处在这里展现无遗!这里由于每个月的日历之间也要换行,因此换行的逻辑以一定要弄清楚。显然当没有到本月的最后一天时,每逢周六要换行,换完行之后weekday加1变成Sunday;当本月最后一天是Saturday时,则由于已经执行加1操作,故下个月1号的Sunday自然会换行;当本月最后一天不是Saturday时,则由于仍在本月的日历中,不能换行,但由于下个月的日历不能贴在一起,则应该在本月日历程序的外面实行换行操作。这部分需要细心调试。

voidPrintCalendarMonth(int month,int year){int weekday,nDays,day;printf("   %s %d\n",MonthName(month),year); //thehead of the monthly calendarprintf(" Su Mo Tu We Th FrSa\n");nDays=MonthDays(month,year); //get thedays of month,which will call IsLeapYear functionweekday=FirstDayOfMonth(month,year);//know first day of every month is which one of weekdayIndentFirstLine(weekday); //print thespacebar before the first dayfor(day=1;day<=nDays;day++){printf(" %2d",day);//printf the dayif(weekday==Saturday)printf("\n"); // saturday can be placedwith 6,this will fully display the advantages of "define"//if saturday,line feed;ifnot,the final weekday will line feedweekday=(weekday+1)%7;//calculate next day}if (weekday!=Sunday)printf("\n");//if not sunday,line feed;if sunday,theformer weekday is saturday,has already line feed}

这里MonthName(month)函数需要注意,由于该函数返回的是字符串,C语言没有返回字符串的string function(int month),好像C++有,处理这个问题通常的做法是使用函数参数传递指针或者申请堆空间,使用完后free掉。这里,使用的是指针的方法。

char*MonthName(int month) //transmit the string of months{switch(month){case 1:return("January");case 2:return("February");case 3:return("March");case 4:return("April");case 5:return("May");case 6:return("June");case 7:return("July");case 8:return("August");case 9:return("September");case 10:return("October");case 11:return("November");case 12:return("December");default:return ("Illegalmonth");}}

3.      组装

现在函数的主体已经有了,下面介绍写注释、修改和测试了。完整代码如下:

输出1900年之后的日历

“ 输出1900年之后任意一年的日历 ”完整编程思路!相关推荐

  1. 输出1900年之后任意一年的日历

    题目:输出1900年之后任意一年的日历.原题出自<C语言的科学与艺术>P132 /* File name:calendar.cFunction:generate a calendar fo ...

  2. 输出1900至2000年(包含1900年和2000年)间的所有闰年

    #include<stdio.h> //输出1900至2000年(包含1900年和2000年)间的所有闰年 int main() {int year; for(year=1900;year ...

  3. 【C语言】输出1900至2000年(包含1900年和2000年)间的所有闰年

    前言 输出1900至2000年(包含1900年和2000年)间的所有闰年 闰年的特点: (1)能被4整除,而不能被100整除,则是闰年 (2)能被400整除也是闰年. 程序源码 #include< ...

  4. 【面试练习题】使用编程语言输出1900年~2000年的全部闰年

    使用编程语言输出1900年~2000年的全部闰年 闰年的判断规则: 普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年.2020年等就是闰年). 世纪闰年:公历年份是整百数的,必 ...

  5. 利用固定输出的三端稳压器得到任意电压

    利用固定输出的三端稳压器得到任意电压

  6. 从键盘任意输入一个整数n,编程计算并输出1-n之间的所有素数之和。

    从键盘任意输入一个整数n,编程计算并输出1-n之间的所有素数之和.要求: 1)编写函数 int IsPrime(int x),该函数功能是判断x是否为素数,若函数返回0,则表示不是素数,若返回1,则代 ...

  7. 【C语言基础练习】有红、绿、蓝三种颜色的球各3个。现在将着9个球混合放在一个盒子中,从中任意摸出6个,编程计算摸出球的各种颜色搭配。

    有红.绿.蓝三种颜色的球各3个.现在将着9个球混合放在一个盒子中,从中任意摸出6个,编程计算摸出球的各种颜色搭配. 1 #include<stdio.h>2 int main()3 {4 ...

  8. 已知鸡和兔的总数量n,总脚数为m。输入n和m,依次输出鸡和兔的数目。如果无解,输出“no answer”。 将下面的代码填写完整。

    撰写人--软工二班--陈喜平 题目描述 已知鸡和兔的总数量n,总脚数为m.输入n和m,依次输出鸡和兔的数目.如果无解,输出"no answer". 将下面的代码填写完整. #inc ...

  9. 1.已知本原多项式,利用Matlb中的simulink构成m序列产生器。2.已知任意本原多项式,利用matlb软件编程求解其对应的m序列以及m序列产生过程。

    1. 已知本原多项式,利用Matlb中的simulink构成m序列产生器.2.已知任意本原多项式,利用matlb软件编程求解其对应的m序列以及m序列产生过程. m序列是最长线性反馈移位寄存器的简称,他 ...

最新文章

  1. 在leangoo里怎么复制列表,删除列表,插入列表?
  2. linux 查找某个库文件属于哪个rpm包
  3. 关于inet_ntop、inet_pton中的n和p分别代表的意义
  4. “ 70后”作家:从尴尬自省到扬眉吐气
  5. 判断sem信号量为零_将信号量递减为零的进程崩溃时,如何恢复信号量?
  6. [vue] 从0到1自己构架一个vue项目,说说有哪些步骤、哪些重要插件、目录结构你会怎么组织
  7. servlet,springmvc,springboot转发时页面静态资源404问题
  8. cocos creator 判断双击_Creator | 编辑器中可操作顶点的多边形遮罩
  9. 运营实操:亚马逊运营的顶级思维
  10. 【转载】mac读取ntfs硬盘方法
  11. Openstack1 云计算与虚拟化概念
  12. Java应届生面试必备考题(附答案)
  13. 世界由物质、能量、信息
  14. dubbo工程结构分析
  15. 更聪明地学习,而不是苦读——《如何高效学习》
  16. std__vector介绍
  17. [MSDN版本][32bit/64bit]Windows 2003 R2 With Sp2[中/英][标准/企业/数据中心]
  18. 长尾序列用户行为建模中可转移参数的学习(KDD-2020)
  19. 关于错误: [Error] ld returned 1 exit status
  20. Linux常用指令(命令)超级无敌全合集加图文说明

热门文章

  1. 计算机学科专业考研总分,2021计算机专业考研备考常识与考试科目分值
  2. VMware Workstation Pro虚拟机命令行安装图形界面
  3. 西南科技大学数据库实验二(Oracle 11g)
  4. 智慧企业的基础——知识中台
  5. 【Unity】Asset Pipeline Version 2(Asset Database v2)内部细节
  6. 在人工智能的角逐中,仿真数据是重要的平衡力量吗?
  7. 发布订单平台小程序开发制作
  8. 云开发山楂岛留言小程序带审核实现教程及源码
  9. 程序人生(创)一个新手程序员的两三事(未完待续...)
  10. jq常用过滤器_jquery 过滤器界别