K&R C Programs Exercise 5-8

Exercise 5-8. There is no error checking in day_of_year or month_day. Remedy this defect.

K&R’s Section 5.8 defines a 2D array daytab[2][13] for days per month and two functions that convert between day-of-year and (month, day). Neither validates its inputs. Bad values (year 0, month 13, day 367) cause silent wrong results or array overruns. The fix: add bounds checks at the top of each function and return a sentinel value (-1 or 0) on invalid input.

Solution

/* K&R Exercise 5-8 — day_of_year / month_day with error checking
 * Compile: gcc -ansi -Wall ex5-8.c -o ex5-8 */
#include <stdio.h>

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

static int leap(int year) { return year%4==0 && (year%100!=0 || year%400==0); }

/* Returns day of year (1-366), or -1 on invalid input */
int day_of_year(int year, int month, int day)
{
    int i, ly;
    if (year < 1 || month < 1 || month > 12 || day < 1)
        return -1;
    ly = leap(year);
    if (day > daytab[ly][month])
        return -1;
    for (i = 1; i < month; i++)
        day += daytab[ly][i];
    return day;
}

/* Sets *pmonth and *pday; returns 0 on success, -1 on invalid input */
int month_day(int year, int yearday, int *pmonth, int *pday)
{
    int i, ly;
    if (year < 1 || yearday < 1)
        return -1;
    ly = leap(year);
    if (yearday > (ly ? 366 : 365))
        return -1;
    for (i = 1; yearday > daytab[ly][i]; i++)
        yearday -= daytab[ly][i];
    *pmonth = i;
    *pday   = yearday;
    return 0;
}

int main(void)
{
    int m, d;

    printf("2024-03-01: day %d\n",  day_of_year(2024,3,1));  /* 61 (leap) */
    printf("2023-03-01: day %d\n",  day_of_year(2023,3,1));  /* 60 */
    printf("bad month:  day %d\n",  day_of_year(2024,13,1)); /* -1 */
    printf("bad day:    day %d\n",  day_of_year(2024,2,30)); /* -1 */

    if (month_day(2024, 61, &m, &d) == 0)
        printf("day 61 of 2024: %d/%d\n", m, d); /* 3/1 */
    if (month_day(2023, 367, &m, &d) != 0)
        printf("day 367 of 2023: invalid\n");     /* -1 */
    return 0;
}

Compile and Run

gcc -ansi -Wall ex5-8.c -o ex5-8
./ex5-8

Sample Output

2024-03-01: day 61
2023-03-01: day 60
bad month:  day -1
bad day:    day -1
day 61 of 2024: 3/1
day 367 of 2023: invalid

What This Exercise Teaches

  • Input validation at function entry — check all preconditions before touching the data; return a sentinel value rather than silently producing garbage
  • Array bounds and leap yearsdaytab[leap(year)][month] gives the correct day count; validating day > daytab[ly][month] catches Feb 30, Apr 31, etc.
  • Sentinel return vs assertion — returning -1 lets the caller handle the error gracefully; assert would abort; neither approach is wrong, but -1 is better for library functions

Set Up Your C Environment

← Exercise 5-7  | 
Chapter 5 Solutions  | 
Exercise 5-9 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>