《你真的会写闰年判断吗?90%的人都忽略了这些细节》
大家好,今天来聊一个看起来很简单,但处处藏着"门道"的编程问题——如何判断闰年。
先看最原始的代码
很多C语言初学者第一次写的闰年判断代码大概是这样:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int year = 0;
scanf("%d", &year);
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
printf("是闰年");
else
printf("不是闰年");
return 0;
}
这段代码能跑,逻辑也对,但它离"工程级"代码还差得远。
今天我们就从这十几行代码出发,看看如何把一个"玩具级"程序写成"专业级"代码。
先搞懂:为什么闰年的规则这么奇怪?
在看代码之前,先搞懂一个常识问题:为什么闰年是"4年一闰,百年不闰,四百年再闰"?
答案很简单:地球绕太阳转一圈不是刚好365天,而是365天5小时48分46秒(约365.2422天)。
如果每年都按365天算,每4年就少算了约23小时15分,所以加1天补上——这就是"4年一闰"。
但这样每4年又多补了45分钟,累积400年就多算了3天,所以每400年要减去3个闰年——这就是"百年不闰,四百年再闰"。
翻译成代码逻辑就是:
能被4整除 并且 不能被100整除
或者
能被400整除
对应:(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
第一次优化:封装成函数
上面的代码把所有逻辑都堆在main函数里,复用性很差。如果另一个地方也要判断闰年怎么办?复制粘贴?
我们来把核心逻辑抽成独立函数:
#include <stdio.h>
#include <stdbool.h> // 用bool类型比int更语义化
/**
* @brief 判断给定年份是否为闰年
* @param year 待判断的年份
* @return 是闰年返回true,否则返回false
*/
bool is_leap_year(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int main() {
int year;
printf("请输入年份:");
scanf("%d", &year);
if (is_leap_year(year))
printf("%d 是闰年\n", year);
else
printf("%d 不是闰年\n", year);
return 0;
}
✅ 优点:
- 语义清晰:函数名
is_leap_year一眼就知道干啥的 - 可复用:任何地方需要判断闰年,直接调用这个函数就行
- 易测试:可以单独测试这个函数的正确性
第二次优化:加上边界校验
你有没有想过,如果用户输入-2024或者0会怎么样?代码照样跑,但结果毫无意义。
公历(格里高利历)是1582年才开始实行的,所以1582年之前的年份用这套规则判断是不准确的。专业的代码必须处理非法输入:
bool is_leap_year(int year) {
// 边界校验:年份必须大于等于1582年
if (year < 1582) {
printf("⚠️ 警告:公历闰年规则从1582年开始适用\n");
return false;
}
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
很多人写代码只考虑"正确输入",但专业程序员会花50%的精力考虑"错误输入"。
第三次优化:写个测试用例
怎么证明你的代码是对的?靠脑子想几个测试用例?太不专业了。
直接写个自动化测试:
int main() {
// 测试用例:覆盖各种边界情况
int test_years[] = {
2024, // 普通闰年 ✓
2023, // 平年 ✗
2000, // 400年闰年 ✓
1900, // 能被100整除但不能被400整除 ✗
1600, // 400年闰年 ✓
1582, // 公历起始年 ✗
1581, // 公历之前 ❌
0, // 非法输入 ❌
-2024 // 非法输入 ❌
};
int n = sizeof(test_years) / sizeof(test_years[0]);
printf("========== 闰年测试 ==========\n");
for (int i = 0; i < n; i++) {
bool result = is_leap_year(test_years[i]);
printf("%4d -> %s\n", test_years[i],
result ? "是闰年 ✅" : "不是闰年 ❌");
}
printf("==============================\n");
return 0;
}
运行结果:
========== 闰年测试 ==========
2024 -> 是闰年 ✅
2023 -> 不是闰年 ❌
2000 -> 是闰年 ✅
1900 -> 不是闰年 ❌
1600 -> 是闰年 ✅
1582 -> 不是闰年 ❌
⚠️ 警告:公历闰年规则从1582年开始适用
1581 -> 不是闰年 ❌
⚠️ 警告:公历闰年规则从1582年开始适用
0 -> 不是闰年 ❌
⚠️ 警告:公历闰年规则从1582年开始适用
-2024 -> 不是闰年 ❌
==============================
所有边界情况一目了然。
90%的人都会犯的3个错误
❌ 错误1:漏加括号
// 虽然运算符优先级没错,但可读性差
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
括号不只是给编译器看的,更是给人看的。多写一对括号,拯救无数后来人。
❌ 错误2:逻辑写反
// 手滑把 != 写成了 ==,1900年就会被误判为闰年
if ((year % 4 == 0 && year % 100 == 0) || (year % 400 == 0))
❌ 错误3:忘记"百年不闰"
// 这是最常见的错误!1900、2100、2200都会被误判
if (year % 4 == 0)
完整的最终版代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
/**
* @brief 判断给定年份是否为闰年(公历1582年起适用)
* @param year 待判断的年份(必须 >= 1582)
* @return 是闰年返回true,否则返回false
*/
bool is_leap_year(int year) {
// 边界校验:公历从1582年开始实行
if (year < 1582) {
printf("⚠️ 警告:公历闰年规则从1582年开始适用\n");
return false;
}
// 标准闰年判断逻辑
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int main() {
// ========== 测试模式 ==========
int test_years[] = {2024, 2023, 2000, 1900, 1600, 1582, 1581, 0, -2024};
int n = sizeof(test_years) / sizeof(test_years[0]);
printf("========== 闰年测试 ==========\n");
for (int i = 0; i < n; i++) {
bool result = is_leap_year(test_years[i]);
printf("%4d -> %s\n", test_years[i],
result ? "是闰年 ✅" : "不是闰年 ❌");
}
printf("==============================\n\n");
// ========== 交互模式 ==========
int year;
printf("请输入年份:");
scanf("%d", &year);
if (is_leap_year(year))
printf("%d 是闰年\n", year);
else
printf("%d 不是闰年\n", year);
return 0;
}
判断闰年是编程入门最基础的题目,但从"能跑"到"好用"之间隔着好几个层级:
- 功能正确 → 逻辑没写错
- 可读性好 → 别人能看懂
- 边界安全 → 处理非法输入
- 可测试 → 容易验证正确性
所谓"优秀的代码",无非就是把这些小事做到极致。

1万+

被折叠的 条评论
为什么被折叠?



