首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言結構體實戰:日期結構體計算年間第幾天(含閏年修正 Bug)

     編輯:關於C語言

問題描述

定義日期結構體 (年、月、日),輸入日期,計算該日是當年的第幾天。核心考點:結構體定義與使用、閏年判斷、累加月天數。原代碼閏年判斷函數有嚴重 Bug,需重點修正。

原代碼及 Bug 分析


struct date { int year, month, day; };

int checkyear(int n) {
    if ((n%4==0 && n%100!=0) || n%400==0)  // 閏年條件正確
        return 0;  // ❌ Bug: 閏年返回 0
    else
        return 0;  // ❌ Bug: 平年也返回 0
}
// 導致 main 中 if(flag) 永假,閏年 2 月天數永不加 1

Bug 根因

  • 返回值語義混淆:應「閏年返回 1,平年返回 0」或「bool 類型」;
  • 兩個分支都返回 0,編譯器甚至可能優化為直接 return 0;。

修正版完整實現


#include <stdio.h>
#include <stdbool.h>  // C99 bool 類型

struct Date {
    int year;
    int month;
    int day;
};

// 正確閏年判斷:閏年返回 true (1)
bool is_leap_year(int year) {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// 累積每月天數表(平年)
const int DAYS_IN_MONTH[13] = {
    0,  // 0 佔位,月份 1-12 對應下標
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
};

int day_of_year(struct Date d) {
    int day_count = d.day;

    // 累加前 (month-1) 月天數
    for (int m = 1; m < d.month; m++) {
        day_count += DAYS_IN_MONTH[m];
    }

    // 閏年且月份 > 2,補 1 天
    if (is_leap_year(d.year) && d.month > 2) {
        day_count += 1;
    }

    return day_count;
}

int main() {
    struct Date birthday;
    printf("輸入日期 (年 月 日): ");
    if (scanf("%d %d %d", &birthday.year, &birthday.month, &birthday.day) != 3) {
        printf("輸入格式錯誤\n");
        return 1;
    }

    // 簡單合法性檢查
    if (birthday.month < 1 || birthday.month > 12 ||
        birthday.day < 1 || birthday.day > 31) {
        printf("月份或日期超出範圍\n");
        return 1;
    }

    int doy = day_of_year(birthday);
    printf("%d 年第 %d 天\n", birthday.year, doy);
    return 0;
}

關鍵改進點

1. 閏年判斷函數語義清晰化

  • 函數名 is_leap_year 明確語義;
  • 返回 bool (C99) 或 int (1/0);
  • 單一 return 表達式,無分支覆蓋 Bug 風險。

2. 累加邏輯簡化:循環上界 m < d.month

原代碼 i < birthday.month 會多加當月天數,再加 birthday.day 導致重複。正確邏輯:只加前 (month-1) 月,最後加 day。

3. 閏年補償條件:month > 2

閏日是 2 月 29 日,只有 3 月及以後才受影響。1-2 月無需補償。

4. 輸入驗證與錯誤處理

檢查 scanf 返回值、月份/日期基本範圍,防禦性編程。

進階優化:O(1) 查表法(無循環)


// 累積天數表:平年每月 1 日是第幾天 (1月1日=1)
const int CUMULATIVE_DAYS[13] = {
    0,   // 0月
    0,   // 1月1日 = 第1天 → 前0天
    31,  // 2月1日 = 第32天 → 前31天
    59,  90, 120, 151, 181, 212, 243, 273, 304, 334  // 3-12月
};

int day_of_year_fast(struct Date d) {
    int doy = CUMULATIVE_DAYS[d.month] + d.day;
    if (is_leap_year(d.year) && d.month > 2) doy++;
    return doy;
}

查表法將 O(month) 循環降為 O(1),嵌入式/高性能場景首選。

測試用例驗證

日期預期年間天數說明
2023-01-011年初
2023-02-2859平年2月底
2024-02-2960閏年2月29日
2024-03-0161閏年3月1日(閏日後+1)
2023-12-31365平年年尾
2024-12-31366閏年年尾

總結

日期計算是結構體、閏年邏輯、邊界條件的綜合練習。原代碼 Bug 揭示了返回值語義不清導致邏輯失效的典型教訓。關鍵收穫:

  • 閏年判斷:(y%4==0&&y%100!=0) || y%400==0;
  • 累加邏輯:只加前置月份,最後加 day;
  • 閏年補償:month > 2 時才 +1;
  • 工程優化:累積天數查表法 O(1) 替代循環;
  • 防禦性編程:輸入驗證、bool 語義化、錯誤返回碼。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved