Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.09 on page 114

Rewrite the routines day_of_year and month_day with pointers instead of indexing.



Solution by Lars Wirzenius

/*
 * A solution to exercise 5-9 in K&R2, page 114:
 *
 *	Rewrite the routines day_of_year and month_day with pointers
 *	instead of indexing.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#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},
};

/* original versions, for comparison purposes */

int day_of_year(int year, int month, int day)
{
	int i, leap;
	
	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	for (i = 1; i < month; i++)
		day += daytab[leap][i];
	return day;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	
	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	for (i = 1; yearday > daytab[leap][i]; i++)
		yearday -= daytab[leap][i];
	*pmonth = i;
	*pday = yearday;
}


/* pointer versions */

int day_of_year_pointer(int year, int month, int day)
{
	int i, leap;
	char *p;
	
	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;

	/* Set `p' to point at first month in the correct row. */
	p = &daytab[leap][1];

	/* Move `p' along the row, to each successive month. */
	for (i = 1; i < month; i++) {
		day += *p;
		++p;
	}
	return day;
}

void month_day_pointer(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	char *p;
	
	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	p = &daytab[leap][1];
	for (i = 1; yearday > *p; i++) {
		yearday -= *p;
		++p;
	}
	*pmonth = i;
	*pday = yearday;
}


int main(void)
{
	int year, month, day, yearday;
	
	year = 2000;
	month = 3;
	day = 1;
	printf("The date is: %d-%02d-%02d\n", year, month, day);
	printf("day_of_year: %d\n", day_of_year(year, month, day));
	printf("day_of_year_pointer: %d\n", 
		day_of_year_pointer(year, month, day));


	yearday = 61;	/* 2000-03-01 */
	month_day(year, yearday, &month, &day);
	printf("Yearday is %d\n", yearday);
	printf("month_day: %d %d\n", month, day);
	month_day_pointer(year, yearday, &month, &day);
	printf("month_day_pointer: %d %d\n", month, day);
	
	return 0;
}


Solution by Xggggg

/*Exercise 5-9. Rewrite the routines day_of_year and month_day with pointers instead of
indexing.*/

#include <stdio.h>

static char daytab[][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}};

int day_of_year(int year, int month, int day);
void month_day(int year, int yearday, int *pmonth, int *pday);

int main(void)
{
	int a, b;

	printf("%d\n", day_of_year(2000, 9, 15));

	month_day(2000, 259, &a, &b);
	printf("%d %d\n", a, b);

	return 0;
}

int day_of_year(int year, int month, int day)
{
	int i, leap;
	leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
	if (year >= 1582 && month > 0 && month <= 12 && day > 0 && day <= *(*(daytab + leap) + month))
	{
		for (i = 1; i < month; i++)
			day += *(*(daytab + leap) + i);
		return day;
	}
	else
		return -1;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
	if (year >= 1582 && yearday > 0 && (yearday <= 366 && leap || yearday <= 365))
	{
		for (i = 1; yearday > *(*(daytab + leap) + i); i++)
			yearday -= *(*(daytab + leap) + i);
		*pmonth = i;
		*pday = yearday;
	}
	else
		*pmonth = *pday = -1;
}

Solution by Gregory Pietsch

/* Gregory Pietsch */

/* Given the problem, I thought that this would be a better
 * description of daytab.
 */
static int daytab[] = {
    0, 
    31,
    31+28,
    31+28+31,
    31+28+31+30,
    31+28+31+30+31,
    31+28+31+30+31+30,
    31+28+31+30+31+30+31,
    31+28+31+30+31+30+31+31,
    31+28+31+30+31+30+31+31+30,
    31+28+31+30+31+30+31+31+30+31,
    31+28+31+30+31+30+31+31+30+31+30,
    0, 
    31,
    31+29,
    31+29+31,
    31+29+31+30,
    31+29+31+30+31,
    31+29+31+30+31+30,
    31+29+31+30+31+30+31,
    31+29+31+30+31+30+31+31,
    31+29+31+30+31+30+31+31+30,
    31+29+31+30+31+30+31+31+30+31,
    31+29+31+30+31+30+31+31+30+31+30,
};

/* is it a leap year?  (assume it's my calendar, the Gregorian) */
int leap(int year)
{
    return ((year % 4) == 0) 
            && (((year % 100) != 0)
                || (year % 400) == 0)));
}

/* day_of_year:  set day of year from month & day */
int day_of_year(int year, int month, int day)
{
    return *(daytab + ((month - 1) + (leap(year) * 12))) + day;
}

/* month_day: set month, day from day of year */
void month_day(int year, int yearday, int *pmonth, int *pday)
{
    int m, ly;

    ly = leap(year);
    if (yearday < 1 || yearday > (365 + ly))
        return; /* no real error checking */
    m = leap(year) ? 23 : 11;
    while (*(daytab + m) > yearday)
        m--;
    if (pmonth)
        *pmonth = (m % 12) + 1;
    if (pday)
        *pday = yearday - (*(daytab + m));
}


Solution by menonsahab

/* My approach was to create an array of pointers, each element of which 
points to an array of integers not necessarily all of the same length */

#include <stdio.h>

int Zer[] = {0};
int Jan[] = {31};
int Feb[] = {28, 29};
int Mar[] = {31};
int Apr[] = {30};
int May[] = {31};
int Jun[] = {30};
int Jul[] = {31};
int Aug[] = {31};
int Sep[] = {30};
int Oct[] = {31};
int Nov[] = {30};
int Dec[] = {31};

int *daytab[] = { Zer, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};

int day_of_year(int year, int month, int day)
{
	int i, leap;
	leap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
	for(i = 1; i < month; i++)
		day += *( *(daytab + i) + ((leap && i == 2) ? 1 : 0));
	return day;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	leap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
	for(i = 1; yearday > *( *(daytab + i) + ((leap && i == 2) ? 1 : 0)); i++)
		yearday -= *( *(daytab + i) + ((leap && i == 2) ? 1 : 0)); // The cryptic expression in the RHS is nothing but daytab[i][(leap && i == 2) ? 1 : 0]
	*pmonth = i;
	*pday = yearday;
}

int main()
{
	int year, month, day, yearday, retval;

	year = 2017;
	month = 11;
	day = 22;
	yearday = 326;

	retval = day_of_year(year, month, day);
	printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);

	return 0;
}

/* 
Program Output:
2017 11 22 <---> 2017 326
2017 326 <---> 2017 11 22
*/


Solution by voidboy

#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}
};


int day_of_year(int, int, int);
void month_day(int, int, int *, int *);

int main()
{
        int m, d;

        printf("==> %d\n", day_of_year(2020, 1, 1));
        printf("==> %d\n", day_of_year(2020, 4, 14));
        printf("==> %d\n", day_of_year(2020, 12, 31));

        month_day(2020, 1, &m, &d);
        printf("%d %d\n", m, d);
        month_day(2020, 105, &m, &d);
        printf("%d %d\n", m, d);
        month_day(2020, 366, &m, &d);
        printf("%d %d\n", m, d);

        return 0;
}

int day_of_year(int year, int month, int day)
{
        int leap;
        char *p;

        leap = (year%4 == 0) && (year%100 != 0 || year%400 == 0);
        p = &daytab[leap][1];
        while (--month > 0)
                day += *p++;
        return day;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
        int leap;
        char *p;

        leap = (year%4 == 0) && (year%100 != 0 || year%400 == 0);
        p = &daytab[leap][1];
        while (yearday > *p)
                yearday -= *p++;
        *pmonth = 1 + p - &daytab[leap][1];
        *pday = yearday;
}


Solution by Codybartfast (cat 0)

The two functions demonstrate slightly different ways of accessing datetab, i.e., *(daytab[leap] + i) vs *(*(daytab + leap) + i).

static char row0[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static char row1[] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static char row2[] = "Third row of different length!";
static char *daytab[] = { row0, row1, row2 };

int day_of_year(int year, int month, int day)
{
	int i, leap;

	leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
	for (i = 1; i < month; i++)
		day += *(daytab[leap] + i);
	return day;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;

	leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
	for (i = 1; yearday > *(*(daytab + leap) + i); i++)
		yearday -= *(*(daytab + leap) + i);
	*pmonth = i;
	*pday = yearday;
}

Solution by anonymous

I originally posted code that is quite similar to how others completed this exercise. However, I wanted to understand pointers more and decided to go back the next day and come up with a solution using pointers without using the convenient indexing shortcut of *(ptr + #) to make things work. After I got that working in the day_of_year function, I then made a one-liner version of that code in the month_day function that which assigns a pointer to the second dimension of the daytab array. This removed the need for the extra pointer variable.

Also, the way I changed daytab in my original program affects the way the first-dimension pointer is declared and it turns out that two-dimensional arrays require non-intuitive syntax. To save any future readers the hassle of making a pointer to a two-dimensional array, I provided the syntax below:

// two-dimensional array version
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}
};
char (*daytabptr)[13] = daytab;                     // pointer to first dimension
char *p = (leap == 1) ? *++daytabptr : *daytabptr;  // pointer to second dimension (same as as below)
char *p = (leap == 1) ? *(daytab + 1) : *daytab;    // pointer arithmetic method to get to the second dimension  (same as as below)

Below is my revised solution

#include <stdio.h>

/*
    Exercise 5-9. Rewrite the routines day_of_year and month_day with pointers instead of indexing.
*/

int day_of_year(int year, int month, int day);
void month_day(int year, int yearday, int *pmonth, int *pday);

static char normal[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static char leap[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static char *daytab[] = { normal, leap };

int main()
{
    int m, d;
    month_day(2021, 349, &m, &d);
    printf("%d %d\n", m, d);
    printf("%d\n", day_of_year(2021, 12, 15));
    return 0;
}

// set day of year from month and day. Returns 0 if input is invalid
int day_of_year(int year, int month, int day)
{
    int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; // if leap year, value is 1, otherwise 0 since arithmetic value of a logical expression is 0 for false and 1 for true
    if (day < 1 || month < 1 || month > 12 || (month == 2 && leap && day > 29) || (month == 2 && !leap && day > 28) || ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) || (!(month == 4 || month == 6 || month == 9 || month == 11) && day > 31))
        return 0; // invalid input
    char **daytabptr = daytab;                          // pure pointer method
    char *p = (leap == 1) ? *++daytabptr : *daytabptr;  // pure pointer method
    while (month-- > 1)
        day += *++p; // p points to 0 at first, so ++p has it point to month 1 before adding days first time
    return day;
}

// set month, day from day of year. Sets pmonth and pday to zero if input is invalid
void month_day(int year, int yearday, int *pmonth, int *pday)
{
    int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; // if leap year, value is 1, otherwise 0
    if (yearday < 1 || (leap && yearday > 366) || (!leap && yearday > 365))
    {
        *pmonth = *pday = 0;
        return; // invalid input
    }
    char *p = (leap == 1) ? *(daytab + 1) : *daytab, *pOriginal = p; // pointer arithmetic method
    while (yearday > *++p) // p points to 0 at first, so ++p has it point to month 1 before comparing and subtracting days the first time
        yearday -= *p;
    *pmonth = p - pOriginal;
    *pday = yearday;
}

Solution by Timmins

Came up with something a bit more compact. Leap-year calculation also seemed like a good candidate for a macro.

#include <stdio.h>

int daytab[2][13] = {
	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

#define leapcalc(x) (!(x%4) && (x%100)) || !(x%400)

// given the year, month and day, return the day of the year
int dayofyear(int year, int month, int day) {
	int *daysptr = leapcalc(year) ? daytab[1] : daytab[0];

	while (--month)
		day += *daysptr++;

	return day;
}

// given the year and yearday, calculate the month and day
void monthandday(int year, int yearday, int *month, int *day) {
	int *daysptr = leapcalc(year) ? daytab[1] : daytab[0];

	for (*month = 1; yearday > *daysptr; (*month)++)
		yearday -= *daysptr++;

	*day = yearday;
}
Personal tools