本练习以1900年1月1日为基准(该日为星期一),实现一个可计算任意指定月份日期分布的日历程序。主要功能包括:
本次任务采用两种不同编程思路完成,二者在“日期存储与显示方式”上存在本质差异,各有适用场景和优劣点。
该方法通过二维数组模拟实际日历表格布局,利用数组的空间结构来映射每周七天的排布情况,最多六行即可容纳所有可能的日期分布。
6x7
实现原理:
关键代码片段解析:
// 填充日历数组:根据起始星期偏移安排日期
void fill_array(int array[][7], int len, int year, int month) {
int days = getdayofmonth(year, month); // 获取当月总天数
int day1 = form1900days(year, month); // 计算1日对应的星期(1-7)
int count = 0, fill_day = 1;
for(j = 0; j < len; j++){
for(i = 0; i < 7; i++){
if(fill_day > days) continue;
if(count >= day1 - 1) array[j][i] = fill_day++;
count++;
}
}
}
// 打印数组内容:控制对齐与空白显示
void show_array(int a[][7], int len) {
for(j = 0; j < 6; j++){
for(i = 0; i < 7; i++){
if(0 == a[j][i]) printf(" \t");
else printf("%3d\t", a[j][i]);
}
printf("\n");
}
}
6x7=42
优势特点:
不足之处:
此方案摒弃中间存储结构,不依赖数组,而是通过数学计算直接控制输出时机与换行位置,实现实时打印。
y
实现逻辑:
(y + current_day) %7 ==0
核心运算示例:
// 总天数对7取余,得到基础偏移
int x = (leap_count * 366 + normal_count * 365) % 7;
// 加上前几个月的累积天数,确定本月1日星期几(0=周一,6=周日)
int y = (x + pre) % 7;
// 输出前置空格以对齐第一周
for (int k = 0; k < y; k++) printf(" ");
// 循环输出每一天,并在周日结束时换行
while (current_day <= total_days) {
printf("%4d", current_day);
if ((y + current_day) % 7 == 0) printf("\n");
current_day++;
}
%4d
优势特点:
局限性:
| 比较维度 | 方案一:二维数组填充法 | 方案二:直接偏移打印法 |
|---|---|---|
| 内存使用 | 较高(需存储数组) | 极低(无额外存储) |
| 代码可读性 | 高(结构明确) | 中(需理解偏移逻辑) |
| 格式控制难度 | 容易(由数组索引控制) | 较难(依赖空格与宽度手动调节) |
| 功能扩展能力 | 强(便于新增特性) | 弱(修改影响大) |
| 嵌入式适配度 | 一般 | 优秀(资源优化突出) |
本练习涵盖了嵌入式C开发中的多个基础但关键的知识模块,也是工业级项目中常见的技术应用:
遵循公历年规则:年份能被4整除但不能被100整除,或者能被400整除,则为闰年。两套方案均采用一致判断逻辑:
// 方法一:条件完整表达
int isleapyear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
此类判断广泛应用于RTC驱动、日志时间戳、定时任务调度等场景。
通过累计从1900年起至目标年月的总天数,并对7取模,获得星期偏移值,是实现日历对齐的核心数学依据。
支持多种输入格式(如“2024 3”或“2024/3”),并通过正则匹配或字符扫描识别分隔符,提升用户交互体验;同时加入边界检查防止非法输入导致程序异常。
合理使用制表符(\t)、固定宽度输出(%3d、%4d)等方式,保障终端显示整齐美观,尤其在无GUI环境下尤为重要。
两种方案体现了典型的“时间-空间”折衷思想:数组法以空间换清晰结构,偏移法以逻辑复杂度换取极致节省。这种权衡能力是嵌入式开发者必备素养。
// 闰年判断实现方式
// 方法一:传统条件分支写法(逻辑清晰,便于调试)
int is_leap(int year) {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
return 1;
} else {
return 0;
}
}
// 方法二:精简表达式写法(常用于嵌入式环境,节省代码空间)
int is_leap(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
%7
在实时时钟(RTC)驱动、日志时间戳生成等外设模块中,该逻辑是日期处理的基础功能,广泛应用于各类嵌入式系统。
核心思想是计算从基准日“1900年1月1日”到目标月份第一天之间的总天数,再通过取模运算得出星期偏移值:
总天数 = 从1900年至目标年前一年的累积天数 + 当前年份中从1月到目标月前一个月的天数之和; 星期偏移 = 总天数 % 7;
注意:由于1900年1月1日为星期一,因此需确保最终偏移结果与实际星期正确对应。
常见问题点:
不同实现方案对星期的表示范围不一致——例如,一种返回1~7(对应周一至周日),另一种返回0~6。若未统一映射规则,将导致显示错位。
days%7+1y
int *month_days;
if (is_leap(year)) {
month_days = B; // 指向闰年天数数组
} else {
month_days = A; // 指向平年天数数组
}
int *month_days
在嵌入式开发中,串口打印或日志输出必须严格规范格式,本例涉及以下关键点:
printf(" ")%3d%4d增强程序健壮性的关键环节。方案二引入了完整的参数检查流程:
if (year < 1900 || month < 1 || month > 12) {
printf("年份应大于等于1900,月份应在1到12之间\n");
return 0;
}
此类校验在嵌入式环境中尤为重要,如外设信号解析、串口指令接收等场景,可有效防止非法输入引发系统崩溃或硬件异常。
现象:程序显示的星期与真实情况不符(如2025年11月1日应为周六,却显示周五)。
原因:总天数计算存在±1误差,或星期映射关系设置错误。
解决方案:以已知标准日期(如1900年1月1日为周一)进行测试验证,逐步排查累加逻辑是否准确。
现象:日期超出列边界,或前后空格数量不一致。
原因:格式化输出时使用的宽度控制不统一(如部分用4字符宽,部分用其他);
\t%4d现象:闰年2月显示28天,或平年误显29天。
原因:数组索引与月份对应关系混淆(如误将索引2当作2月);
mon[1]mon[2]2025 112025/112025-11尽管本次日历程序看似基础,但涵盖了嵌入式C开发中的多个核心能力维度:
#include <stdio.h>
// 显示星期标题,输出1到7代表周一至周日
void show_title()
{
for (int i = 1; i < 8; i++)
{
printf("%3d\t", i);
}
printf("\n");
}
// 判断是否为闰年:能被4整除但不能被100整除,或能被400整除
int isleapyear(int year)
{
if ((0 == year % 4 && 0 != year % 100) || 0 == year % 400)
{
return 1;
}
else
{
return 0;
}
}
// 获取指定年月的天数,根据是否为闰年调整二月天数
int getdayofmonth(int year, int month)
{
int mon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (isleapyear(year))
{
mon[1] = 29;
}
return mon[month - 1];
}
// 计算从1900年1月1日起到指定年月前的总天数,并返回该月第一天是星期几(1-7)
int form1900days(int year, int month)
{
int days = 0;
int i = 0;
int j = 0;
// 累加完整年的所有月份天数
for (i = 1900; i < year; i++)
{
for (j = 1; j <= 12; j++)
{
days += getdayofmonth(i, j);
}
}
// 累加当前年的前几个月的天数
for (j = 1; j < month; j++)
{
days += getdayofmonth(year, j);
}
return (days % 7) + 1; // 转换为星期1-7表示
}
// 填充二维数组用于显示日历,按行优先方式填入日期
void fill_array(int array[][7], int len, int year, int month)
{
int days = getdayofmonth(year, month); // 当前月的总天数
int day1 = form1900days(year, month); // 当前月第一天对应的星期位置
int count = 0;
int fill_day = 1;
for (int j = 0; j < len; j++)
{
for (int i = 0; i < 7; i++)
{
if (fill_day > days)
continue;
if (count >= day1 - 1)
{
array[j][i] = fill_day++;
}
count++;
}
}
}
// 输出日历内容,空白处用空格代替,其余显示对应日期
void show_array(int a[][7], int len)
{
for (int j = 0; j < 6; j++)
{
for (int i = 0; i < 7; i++)
{
if (0 == a[j][i])
{
printf(" \t");
}
else
{
printf("%3d\t", a[j][i]);
}
}
printf("\n");
}
}
// 主函数:接收用户输入的年份和月份,生成并打印日历
int main(int argc, char **argv)
{
int year = 0, month = 0;
int array[6][7] = {0};
printf("input year month:");
scanf("%d%d", &year, &month);
show_title();
fill_array(array, 6, year, month);
show_array(array, 6);
return 0;
}
6x7
#include "stdio.h"
// 定义平年与闰年各月份天数数组
int A[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // 平年
int B[] = {31,29,31,30,31,30,31,31,30,31,30,31}; // 闰年
// 判断某年是否为闰年
int is_leap(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// 主程序入口
int main()
{
int year = 0, month = 0;
printf("请输入年/月");
scanf("%d/%d", &year, &month);
// 输入合法性检查
if (year < 1900 || month < 1 || month > 12)
{
printf("年份要大于等于1900,月份要在1月到12月\n");
return 0;
}
int leap_count = 0, normal_count = 0;
// 统计从1900年到目标年份前一年之间的闰年与平年数量
for (int i = 1900; i < year; i++)
{
if (is_leap(i))
{
leap_count++;
}
else
{
normal_count++;
}
}
// 总天数计算:每闰年366天,每平年365天
long total_days = (long)leap_count * 366 + (long)normal_count * 365;
// 加上当前年份中已过去的月份的天数
for (int i = 0; i < month - 1; i++)
{
if (is_leap(year))
{
total_days += B[i];
}
else
{
total_days += A[i];
}
}
// 计算该月第一天是星期几(假设1900年1月1日为星期一)
int week_start = (total_days + 1) % 7;
if (week_start == 0) week_start = 7;
// 打印月份标题行
printf("\n 日 一 二 三 四 五 六\n");
// 输出前置空格以对齐星期
for (int i = 1; i < week_start; i++)
{
printf(" ");
}
// 获取当月天数
int days_in_month = is_leap(year) ? B[month - 1] : A[month - 1];
// 输出日期,每行七个
for (int i = 1; i <= days_in_month; i++)
{
printf("%4d", i);
if ((i + week_start - 1) % 7 == 0)
{
printf("\n");
}
}
printf("\n");
return 0;
}
6x7=42
int x = (leap_count * 366 + normal_count * 365) % 7;
int *month_days;
if (is_leap(year)) {
month_days = B;
} else {
month_days = A;
}
int pre = 0;
for (int j = 0; j < month - 1; j++) {
pre += month_days[j];
}
int y = (x + pre) % 7;
printf("------------%d年%d月---------\n", year, month);
printf(" 一 二 三 四 五 六 日\n");
printf("----------------------------\n");
for (int k = 0; k < y; k++) {
printf(" ");
}
int current_day = 1;
int total_days = month_days[month - 1];
while (current_day <= total_days) {
printf("%4d", current_day);
if ((y + current_day) % 7 == 0) {
printf("\n");
}
current_day++;
}
if ((y + total_days) % 7 != 0) {
printf("\n");
}
printf("-------------------------\n");
return 0;
扫码加好友,拉您进群



收藏
