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