《C语言及程序设计》实践参考——玩转日期和时间

简介: 返回:贺老师课程教学链接【项目2-玩转日期和时间】 定义一个表示时间(包括年、月、日、时、分、秒)的结构体,然后完成下面的功能。 提示:将各个功能分别设计成函数实现,在main函数中调用,进行测试。可以设计一个函数,即刻进行测试,以降低复杂度。 要实现的功能是: (1)输入一个时间(注意各部分数据的取值范围)将输入的时间保存在一个结构体变量中; (2)输出该日在

返回:贺老师课程教学链接

【项目2-玩转日期和时间】
定义一个表示时间(包括年、月、日、时、分、秒)的结构体,然后完成下面的功能。
提示:将各个功能分别设计成函数实现,在main函数中调用,进行测试。可以设计一个函数,即刻进行测试,以降低复杂度。
要实现的功能是:
(1)输入一个时间(注意各部分数据的取值范围)将输入的时间保存在一个结构体变量中;
(2)输出该日在本年中是第几天(注意闰年问题);
(3)输出这是这一天中的第几秒;
(4)输出这是这一年中的第几秒;(不要认为这个数荒唐,在计算中需要取随机数时需要一个不会重复的“种子数”,这个秒数是常用的。)
(5)求你输入的时间d天后是哪年哪月哪日,将结果保存在一个结构体变量中输出。
[参考解答]

整体的考虑

  最后编好的程序的结构应该如下所示,其中所需要设计,一是存储数据所用的数据结构——结构体,二是整个程序的框架,除了main()函数之外,还要有一系列的其他函数作为支撑。在严格的软件工程中,需要提前将所需要的函数(工程中称之为模块)设计出来,而在学习语法阶段,不妨也便宜行事,需要什么写什么,这样做更快一些。在此基础上,main()函数只要按照题目中的要求,或输入,或调用函数,逐个地写出来即可。
  所以,这个程序最后的结构会是:

#include <stdio.h>
struct Time
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
};


//待设计的一大堆函数,是哪些,暂不考虑

int main()
{
    struct Time t;
    //(1)输入一个时间(注意各部分数据的取值范围)将输入的时间保存在一个结构体变量中;
    printf("请输入一个时间(包括年,月,日,时,分,秒):\n");
    getTime(&t);      //输入将不会是很简单的一两条语句,写个函数完成
    outputTime(t);   //马上验证输入,很自地完成


    //(2)输出该日在本年中是第几天(注意闰年问题);
    printf("这是这一年中的第%d天。\n", dayOfYear(t)); //设计专门的函数完成


    //(3)输出这是这一天中的第几秒;
    printf("这是这一天中的第%d秒。\n", secondOfDay(t));//设计专门的函数完成


    //(4)输出这是这一年中的第几秒;
    printf("这是这一年中的第%d秒。\n", dayOfYear(t)*24*3600+secondOfDay(t));


    //(5)求你输入的时间d天后是哪年哪月哪日,将结果保存在一个结构体变量中输出;
    scanf("%d", &d);
    printf("这一天后%d天是:", d);
    outputTime(afterDays(t,d));


    //(6)求你输入的时间s秒后是何日何时,将结果保存在一个结构体变量中输出;
    scanf("%d", &d);
    printf("这一时的后%d秒是:", s);
    outputTime(afterSeconds(t,s));


    //到此任务完成
    return 0;
}

  胖子是一口一口吃出来的,程序得一点一点写出来,做一些,就测试一点,保证局部不要出问题。不要妄图全写完了才去测试,医生说食物摄取过量,老百姓讲是吃撑了。

完成第(1)个要求,测试通过键盘输入进行即可

  在这样一种指导思想下,先完成第(1)个要求,主要是写出自定义函数来。将需要多次用到的功能,例如,输入中要保证范围的要求,也“抽象”一下,做成函数getNum()。不少成员的取值范围是确定的,但每月的天数并不都一样有些麻烦,设计一个函数daysOfMonth()实现,将有利于整个程序的简洁。
  参考代码可以如下:

#include <stdio.h>
struct Time
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
};

//输入一个限定范围内的整型数值
int getNum(char *prompt, int min, int max)
{
    int value=-1;
    printf("输入%s,范围[%d, %d]:", prompt, min, max);
    do
    {
        scanf("%d", &value);
    }
    while(value<min || value>max);
    return value;
}

//返回y年m月的天数
int daysOfMonth(int m,int y)
{
    int days;
    switch(m)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        days=31;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        days=30;
        break;
    case 2:
        if((y%4==0&&y%100!=0)||y%400==0)
            days=29;
        else
            days=28;
    }
    return days;
}

//输入时间,参数为指针,可以影响实参的值
void getTime(struct Time *p)
{
    p->year=getNum("年",0,3000); //getNum()用于输入一定范围内的数
    p->month=getNum("月",1,12);
    p->day=getNum("日",1,daysOfMonth(p->month, p->year));
    p->hour=getNum("时",0,24);
    p->minute=getNum("分",0,59);
    p->second=getNum("秒",0,59);
}

//输出时间
void outputTime(struct Time t)
{
    printf("时间为: %d年%d月%d日%d时%d分%d秒\n",t.year,t.month,t.day,t.hour,t.minute,t.second);
}

int main()
{
    struct Time t;
    //(1)输入一个时间(注意各部分数据的取值范围)将输入的时间保存在一个结构体变量中;
    printf("请输入一个时间(包括年,月,日,时,分,秒):\n");
    getTime(&t);
    outputTime(t);
    return 0;
}

  从做练习的角度,程序就是完成了。要完成测试,关键是保证输入时数据取值范围是否能够得到保证,于是需要多次地启动程序,输入各种不同的时间,看是否能体现设计时的限制。需要考虑到的情形包括:
2013年1月20日3时4分5秒 //中规中矩的输入
2012年3月31日3时4分5秒 //重点考察取日期的“边界值”是否接受,类似地可以用其他月份检查,还可以设计出多种情形
……
2013年2月29日3时4分5秒 //2013不是闰年,这个输入会如何处理
2012年2月29日3时4分5秒 //2012是闰年,这个输入会如何处理
2013年13月45日33时4分5秒 //各种的“捣乱”,这在测试中是必须的
  通过这样的多次运行多次输入查看结果是可行的。但是,如果考虑一次运行程序,可以支持多次输入对不同情形进行测试,那自然是更方便的事了。其实,写一个循环,那也不是难事。事实上,这是工程中更常用的方式。
  在这种思路下,main()函数这样写。

int main()
{
    struct Time t;
    do
    {
        printf("请输入一个时间(包括年,月,日,时,分,秒):\n");
        getTime(&t);
        outputTime(t);
    }while(t.year>0); //输入0年某月某日测试即结束,测试过程由人可控
    return 0;
}

  这只是测试所需要的一部分内容。这样测试出的程序,质量信心必涨!好产品得经过严格的测试,放在程序设计中也是这样。

完成第(2)个要求,测试不再靠键盘输入

  实现功能角度,增加一个函数dayOfYear()即可。涉及到测试时,要靠着键盘输入,那可就烦恼大了:改一点程序,输入若干数据测试,发现错误,改程序,再运行,输入测试数据。这时,可以采用的方法是,用测试用到的数据直接初始化结构体。再进一步,一组测试数据,一起测试,用结构体数组保存数据,测试过程做一个循环。
  实现了的函数和测试用的main()函数如下所示,实现任务(1)中的函数保留不动,它们是整个任务中的一部分,况且(2)任务可能会用到前面的劳动成果,删除不必要。
  参考程序如下:

#include <stdio.h>
struct Time
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
};

//输入一个限定范围内的整型数值
int getNum(char *prompt, int min, int max)
{
    int value=-1;
    printf("输入%s,范围[%d, %d]:", prompt, min, max);
    do
    {
        scanf("%d", &value);
    }
    while(value<min || value>max);
    return value;
}

//返回y年m月的天数
int daysOfMonth(int m,int y)
{
    int days;
    switch(m)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        days=31;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        days=30;
        break;
    case 2:
        if((y%4==0&&y%100!=0)||y%400==0)
            days=29;
        else
            days=28;
    }
    return days;
}

//这天是这一年的第几天
int dayOfYear(struct Time t)
{
    int days=0;
    int m=1;
    while(m<t.month)  //前若干月的天数加起来
    {
        days+=daysOfMonth(m,t.year);  //充分利用已经设计的函数
        ++m;
    }
    days+=t.day; //再加上本月的天数
    return days;
}

//输入时间,参数为指针,可以影响实参的值
void getTime(struct Time *p)
{
    p->year=getNum("年",0,3000); //getNum()用于输入一定范围内的数
    p->month=getNum("月",1,12);
    p->day=getNum("日",1,daysOfMonth(p->month, p->year));
    p->hour=getNum("时",0,24);
    p->minute=getNum("分",0,59);
    p->second=getNum("秒",0,59);
}

//输出时间
void outputTime(struct Time t)
{
    printf("时间为: %d年%d月%d日%d时%d分%d秒\n",t.year,t.month,t.day,t.hour,t.minute,t.second);
}

int main()
{
    //任务(2)输出该日在本年中是第几天(注意闰年问题);
    struct Time t[]=   //测试数据放在结构体数组中
    {
        {1971,1,8,14,25,48},  //最简单的一个测试数据,期望输出8
        {2011,2,27,14,25,48}, //2月,天数要加上1月的31天,期望输出31+27
        {2012,3,2,14,25,48},  //这个输入很敏感,看2月究竟算多少天,期望输出31+29+2=62
        {2013,3,2,14,25,48},  //这个期望输出是31+28+2=61
        {2013,4,10,14,25,48}  //期望结果100,这样的测试数据还可以继续列一些,期望结果要列出来,这是测试的依据
    };
    int i;
    for(i=0; i<5; ++i)   //测试过程用一个循环完成
    {
        outputTime(t[i]);
        printf("这是这一年中的第%d天。\n\n", dayOfYear(t[i]));
    }
    return 0;
}

  体会上面测试数据设计和测试程序(main()函数)设计中的用心,查看运行测试的结果,生产好产品,需要这样做。

第(3)和(4)个任务容易实现,测试过程类似进行

  只需要增加一个自定义函数

//这天是这一天的第几秒  
int secondOfDay(struct Time t)  
{  
    return t.hour*3600+t.minute*60+t.second;  
}  

  相应的测试函数定义为:
[int main()
{
    struct Time t[]=
    {
        {1971,1,8,0,0,2},
        {2011,2,27,0,25,10},
        {2012,3,2,10,20,20},
        {2013,3,2,10,20,20},
        {2013,4,10,10,20,20}
    };
    int i;
    for(i=0; i<5; ++i)
    {
        outputTime(t[i]);
        //3)输出这是这一天中的第几秒;
        printf("这是这一天中的第%d秒。\n", secondOfDay(t[i]));
        //4)输出这是这一年中的第几秒;
        printf("这是这一年中的第%d秒。\n", dayOfYear(t[i])*24*3600+secondOfDay(t[i]));
    }
    return 0;
}

  请体会上面的测试数据的设计,也可以设计出更好的来

再来第(5)个要求,测试数据的设计需要更动动脑子

  关于实现功能而言,主体在Time afterDays(struct Time t,int d)函数,这个求解有些麻烦,但是采用的算法统一从1月0日开始的思路,还是可以将复杂性降下来一些。

#include <stdio.h>
struct Time
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
};

//输入一个限定范围内的整型数值
int getNum(char *prompt, int min, int max)
{
    int value=-1;
    printf("输入%s,范围[%d, %d]:", prompt, min, max);
    do
    {
        scanf("%d", &value);
    }
    while(value<min || value>max);
    return value;
}

//返回y年m月的天数
int daysOfMonth(int m,int y)
{
    int days;
    switch(m)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        days=31;
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        days=30;
        break;
    case 2:
        if((y%4==0&&y%100!=0)||y%400==0)
            days=29;
        else
            days=28;
    }
    return days;
}

//这天是这一年的第几天
int dayOfYear(struct Time t)
{
    int days=0;
    int m=1;
    while(m<t.month)  //前若干月的天数加起来
    {
        days+=daysOfMonth(m,t.year);  //充分利用已经设计的函数
        ++m;
    }
    days+=t.day; //再加上本月的天数
    return days;
}

//输入时间,参数为指针,可以影响实参的值
void getTime(struct Time *p)
{
    p->year=getNum("年",0,3000); //getNum()用于输入一定范围内的数
    p->month=getNum("月",1,12);
    p->day=getNum("日",1,daysOfMonth(p->month, p->year));
    p->hour=getNum("时",0,24);
    p->minute=getNum("分",0,59);
    p->second=getNum("秒",0,59);
}

//输出时间
void outputTime(struct Time t)
{
    printf("时间为: %d年%d月%d日%d时%d分%d秒\n",t.year,t.month,t.day,t.hour,t.minute,t.second);
}

//这天是这一天的第几秒
int secondOfDay(struct Time t)
{
    return t.hour*3600+t.minute*60+t.second;
}

//返回一年有多少天(365或366天)
int daysOfYear(int y)
{
    return ((y%4==0&&y%100!=0)||y%400==0)?366:365;
}

//求你输入的时间d天后是哪年哪月哪日
struct Time afterDays(struct Time t,int d)
{
    struct Time t1=t;
    int d1=d+dayOfYear(t); //dayOfYear(t)求出t是当年第几天
    t1.month=1;
    t1.day=0;  //这样,将问题转换为在当年1月0日基础上加d1天(这个0有意思),避免以后老为2月操心,以及剩余天数一加以后持续进位

    while(d1>daysOfYear(t1.year))  //天数还够一个整年
    {
        d1-=daysOfYear(t1.year);
        ++t1.year;
    }
    //天数不够一整年后,再考虑月,因为从1月1日开始,不用担心Nt.year再加1年
    while(d1>daysOfMonth(t1.month,t1.year))  //天数还够一个整月
    {
        d1-=daysOfMonth(t1.month,t1.year);
        ++t1.month;
    }
    //剩全天数加到日上
    t1.day+=d1;
    return t1;
}

int main()
{
    int d[]= {68,365,366,798} ; //分别在一年内、恰1年(闰或不闰)、798(365+365+68)天时要复杂也最有说服力,考虑跨年、跨月,尤其有闰年时
    struct Time nt,t[]=
    {
        {2001,1,8,0,0,0},   //没有时分秒的事,都0
        {2012,2,20,0,0,0},   //2月是个敏感的月份
        {2014,2,20,0,0,0},   //直接遇到闰年
        {2013,4,20,0,0,0},   //过了2月
        {2013,4,20,0,0,0},   //过了2月
        {2013,12,10,0,0,0},   //近年关,稍加一点就下一年了,也测试下
    };


    //(5)求你输入的时间d天后是哪年哪月哪日
    int i, j;
    for(i=0; i<7; ++i)
        for(j=0; j<4; ++j)
        {
            outputTime(t[i]);
            printf("这是这一年中的第%d天。\n", dayOfYear(t[i]));
            printf("这一天后%d天是:", d[j]);
            nt=afterDays(t[i],d[j]);
            outputTime(nt);
            printf("\n");
        }
    return 0;
}

最后的测试

在以上对函数的详细测试基础上,完成本项目中的工作,只需要下面的mian函数:
int main()
{
struct Time t,nt;
//(1)输入一个时间(注意各部分数据的取值范围)将输入的时间保存在一个结构体变量中;
printf(“请输入一个时间(包括年,月,日,时,分,秒):\n”);
getTime(&t);
outputTime(t);
//(2)输出该日在本年中是第几天(注意闰年问题);
printf(“这是这一年中的第%d天。\n”, dayOfYear(t));
//(3)输出这是这一天中的第几秒;
printf(“这是这一天中的第%d秒。\n”, secondOfDay(t));
//(4)输出这是这一年中的第几秒;
printf(“这是这一年中的第%d秒。\n”, dayOfYear(t)*24*3600+secondOfDay(t));
//(5)求你输入的时间d天后是哪年哪月哪日,将结果保存在一个结构体变量中输出;
int d;
printf(“请输入一个天数: “);
scanf(“%d”, &d);
nt=afterDays(t,d);
printf(“再过%d天, “, d);
outputTime(nt);
return 0;
}
“`

目录
相关文章
|
8月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
358 16
|
8月前
|
算法 C语言
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
201 18
|
8月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
196 3
|
8月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
167 2
|
8月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
141 1
|
2月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
234 15
|
8月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
367 23
|
7月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
171 1
一文彻底搞清楚C语言的函数
|
8月前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
303 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
8月前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
143 24