C++ 判断闰年

在日常生活中,很多小伙伴都听说过 “四年一闰” 的说法,于是写代码时第一反应就是只判断 year % 4 == 0。这个写法用来判断 2024 年没有问题,但如果放到 1900 年就会出错:1900 同样能被 4 整除,但它其实是一个平年。

因此,写这道题的关键,就是把公历里关于 4、100、400 的闰年规则准确写成 C++ 的条件判断。

闰年的判断规则

按照公历规定,“四年一闰,百年不闰,四百年再闰”。具体来说,一个年份只要满足以下 2 个条件中的任意一个就是闰年,两个都不满足则是平年:

  • 普通闰年:能被 4 整除,但不能被 100 整除。例如 2024 年、2028 年能被 4 整除且不是整百年,都属于普通闰年。而 2025 年不能被 4 整除,因此是平年。
  • 世纪闰年:整百年份必须能被 400 整除才算闰年。例如 2000 年能被 400 整除,所以是闰年。而 1900 年虽然能被 4 和 100 整除,但不能被 400 整除,因此是平年。

把上面的规则写成 C++ 代码时,常见的有 2 种写法:一种是使用一行逻辑表达式直接算出布尔结果,另一种是使用多个条件语句分步筛选。

使用单行逻辑表达式判断(推荐)

最简洁的做法,是借助 C++ 逻辑运算符中的逻辑与(&&)和逻辑或(||),把前面那两条闰年规则组合成一行布尔表达式:

(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)

在这行表达式中,&& 表示 “并且”,左边的 year % 4 == 0 && year % 100 != 0 对应普通闰年。中间的 || 表示 “或者”,右边的 year % 400 == 0 对应世纪闰年。左右两边只要有一边成立,整个表达式的结果就是 true。

下面我们把这行表达式放进 isLeapYear() 函数中,并在 main() 里配合范围 for 循环测试 4 个典型年份。

示例 1:逻辑表达式判断闰年

#include <iostream>

// 使用单行逻辑表达式判断闰年
bool isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

int main() {
    int years[4] = {2000, 1900, 2024, 2025};

    for (int year : years) {
        if (isLeapYear(year)) {
            std::cout << year << " 年是闰年\n";
        } else {
            std::cout << year << " 年是平年\n";
        }
    }
    return 0;
}

运行结果如下。

2000 年是闰年
1900 年是平年
2024 年是闰年
2025 年是平年

分析:

在 isLeapYear() 函数中,return 语句直接返回逻辑表达式算出来的布尔结果。当我们传入 2000 时,虽然左边的 2000 % 100 != 0 为假,但右边的 2000 % 400 == 0 为真。由于 false || true 的结果为 true,所以 2000 年被判定为闰年。

当我们传入 1900 时,左边因为能被 100 整除而为假,右边又不能被 400 整除也为假,因此返回 false。而传入 2024 时,左边已经满足普通闰年的条件,右边就不用再算了,直接返回 true。

提示 根据 C++ 的运算符优先级,逻辑与(&&)本来就会先于逻辑或(||)计算。不过当一行代码里同时出现 && 和 || 时,我们显式加上小括号把条件分组括起来,能让人一眼看清逻辑层次,在实际开发中更推荐这种写法。

使用 if 语句分步判断

如果刚开始接触多条件组合,觉得把所有条件挤在一行里不太好读,我们也可以配合 if 语句分步判断,按照 400、100、4 的顺序一层层往下筛选。

示例 2:分步筛选判断闰年

#include <iostream>

// 按 400、100、4 的顺序分步判断闰年
bool isLeapYear(int year) {
    if (year % 400 == 0) {
        return true;     // 能被 400 整除,是闰年
    }
    if (year % 100 == 0) {
        return false;    // 其余整百年份,是平年
    }
    return year % 4 == 0;
}

int main() {
    int years[4] = {2000, 1900, 2024, 2025};

    for (int year : years) {
        if (isLeapYear(year)) {
            std::cout << year << " 年是闰年\n";
        } else {
            std::cout << year << " 年是平年\n";
        }
    }
    return 0;
}

运行结果如下。

2000 年是闰年
1900 年是平年
2024 年是闰年
2025 年是平年

分析:

在这个例子中,函数开头的两个 if 专门用来把 “整百年份” 提前筛出来处理掉。为什么这两个 if 后面都不用写 else 呢?这是因为函数一旦执行 return 语句就会立刻结束并返回结果,根本不会继续往下走。

顺着执行顺序来看:如果年份能被 400 整除(如 2000),第 1 个 if 直接返回 true。如果代码能走到第 2 个 if,说明它肯定不能被 400 整除,此时只要满足 year % 100 == 0(如 1900),就直接返回 false。

等跨过前两个 if 走到最后一行时,剩下的肯定都不是整百年份,我们直接通过 return year % 4 == 0; 判断它能否被 4 整除就可以了。

需要特别注意的是,采用这种分步写法时,小伙伴们千万不要把判断的先后顺序颠倒过来。下面这两种写法的判断结果是截然不同的。

// 正确方式
if (year % 400 == 0) {
    return true;
}
if (year % 100 == 0) {
    return false;
}
return year % 4 == 0;

// 错误方式
if (year % 4 == 0) {
    return true;
}
if (year % 100 == 0) {
    return false;
}
return year % 400 == 0;

因为所有能被 100 或 400 整除的年份,本身也一定能被 4 整除。如果把 if (year % 4 == 0) 放在最前面,像 1900 这样的年份在第 1 个 if 处就会满足条件并直接返回 true,后面写的整百年排除规则就完全没有机会执行了。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号