Working with Structures in C: Functions and Structures

Now, you can return to the problem that was discovered in the previous program.Your program thinks that February always has 28 days, so naturally when you ask it for the day after February 28, it always displays March 1 as the answer.You need to make a spe- cial test for the case of a leap year. If the year is a leap year, and the month is February, the number of days in that month is 29. Otherwise, the normal lookup inside the daysPerMonth array can be made.

A good way to incorporate the required changes into Program 9.2 is to develop a function called numberOfDays to determine the number of days in a month. The func- tion would perform the leap year test and the lookup inside the daysPerMonth array as required. Inside the main routine, all that has to be changed is the if statement, which compares the value of today.day to daysPerMonth[today.month – 1]. Instead, you could now compare the value of today.day to the value returned by your numberOfDays function.

Study Program 9.3 carefully to determine what is being passed to the numberOfDays function as an argument.

Program 9.3   Revising  the Program to Determine Tomorrow’s Date

// Program to determine tomorrow’s date

#include <stdio.h>

#include <stdbool.h>

struct date

{

int month;

int day;

int year;

};

int main (void)

{

struct date today, tomorrow;

int numberOfDays (struct date d);

printf (“Enter today’s date (mm dd yyyy): “);

scanf (“%i%i%i”, &today.month, &today.day, &today.year);

if ( today.day != numberOfDays (today) ) {

tomorrow.day = today.day + 1;

tomorrow.month = today.month;

tomorrow.year = today.year;

}

else if ( today.month == 12 ) {   // end of year

tomorrow.day = 1;

tomorrow.month = 1;

tomorrow.year = today.year + 1;

}

else {                        // end of month

tomorrow.day = 1;

tomorrow.month = today.month + 1;

tomorrow.year = today.year;

}

printf (“Tomorrow’s date is %i/%i/%.2i.\n”,tomorrow.month,

tomorrow.day, tomorrow.year % 100);

return 0;

}

// Function to find the number of days in a month

int numberOfDays (struct date d)

{

int  days;

bool isLeapYear (struct date d);

const int  daysPerMonth[12] =

{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

if ( isLeapYear (d) == true && d.month == 2 )

days = 29;

else

days = daysPerMonth[d.month – 1];

return days;

}

// Function to determine if it’s a leap year

bool isLeapYear (struct date d)

{

bool leapYearFlag;

if ( (d.year % 4 == 0 && d.year % 100 != 0) ||

d.year % 400 == 0 )

leapYearFlag = true;  // It’s a leap year

else

leapYearFlag = false; // Not a leap year

return leapYearFlag;

}

Program 9.3   Output

Enter today’s date (mm dd yyyy): 2 28 2004

Tomorrow’s date is 2/29/04.

Program 9.3   Output (Rerun)

Enter today’s date (mm dd yyyy): 2 28 2005

Tomorrow’s date is 3/1/05.

The first thing that catches your eye in the preceding program is the fact that the defini- tion of the date structure appears first and outside of any function. This makes the definition known throughout the file. Structure definitions behave very much like variables—if a structure is defined within a particular function, only that function knows of its existence. This is a local structure definition. If you define the structure outside of any function, that definition is global.A global structure definition allows any variables that are subsequently defined in the program (either inside or outside of a function) to be declared to be of that structure type.

Inside the main routine, the prototype declaration

int numberOfDays (struct date d);

informs the C compiler that the numberOfDays function returns an integer value and takes a single argument of type struct date.

Instead of comparing the value of today.day against the value daysPerMonth[today.month – 1], as was done in the preceding example, the statement

if ( today.day != numberOfDays (today) )

is used. As you can see from the function call, you are specifying that the structure today is to be passed as an argument. Inside the numberOfDays function, the appropriate decla- ration must be made to inform the system that a structure is expected as an argument:

int numberOfDays (struct date d)

As with ordinary variables, and unlike arrays, any changes made by the function to the values contained in a structure argument have no effect on the original structure. They affect only the copy of the structure that is created when the function is called.

The numberOfDays function begins by determining if it is a leap year and if the month is February. The former determination is made by calling another function called isLeapYear.You learn about this function shortly. From reading the if statement

if ( isLeapYear (d) == true  && d.month == 2 )

you can assume that the isLeapYear function returns true if it is a leap year and returns false if it is not a leap year. This is directly in line with our discussions of Boolean vari- ables back in Chapter 6, “Making Decisions.” Recall that the standard header file <stdbool.h> defines the values bool, true, and false for you, which is why this file is included at the beginning of Program 9.3.

An interesting point to be made about the previous if statement concerns the choice of the function name isLeapYear. This name makes the if statement extremely readable and implies that the function is returning some kind of yes/no answer.

Getting back to the program, if the determination is made that it is February of a leap year, the value of the variable days is set to 29; otherwise, the value of days is found by indexing the daysPerMonth array with the appropriate month. The value of days is then returned to the main routine, where execution is continued as in Program 9.2.

The isLeapYear function is straightforward enough—it simply tests the year con-tained in the date structure given as its argument and returns true if it is a leap year and false if it is not.

As an exercise in producing a better-structured program, take the entire process of determining tomorrow’s date and relegate it to a separate function.You can call the new function dateUpdate and have it take as its argument today’s date. The function then cal- culates tomorrow’s date and returns the new date back to us. Program 9.4 illustrates how this can be done in C.

Program 9.4   Revising  the Program to Determine  Tomorrow’s  Date, Version 2

// Program to determine tomorrow’s date

#include <stdio.h>

#include <stdbool.h>

struct date

{

int month;

int day;

int year;

};

// Function to calculate tomorrow’s date

struct date dateUpdate (struct date today)

{

struct date tomorrow;

int numberOfDays (struct date d);

if ( today.day != numberOfDays (today) ) {

tomorrow.day = today.day + 1;

tomorrow.month = today.month;

tomorrow.year = today.year;

}

else if ( today.month == 12 ) {  // end of year

tomorrow.day = 1;

tomorrow.month = 1;

tomorrow.year = today.year + 1;

}

else {                        // end of month

tomorrow.day = 1;

tomorrow.month = today.month + 1;

tomorrow.year = today.year;

}

return tomorrow;

}

// Function to find the number of days in a month

int numberOfDays (struct date d)

{

int days;

bool isLeapYear (struct date d);

const int daysPerMonth[12] =

{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

if ( isLeapYear && d.month == 2 )

days = 29;

else

days = daysPerMonth[d.month – 1];

return days;

}

// Function to determine if it’s a leap year

bool isLeapYear (struct date d)

{

bool leapYearFlag;

if ( (d.year % 4 == 0 && d.year % 100 != 0) ||

d.year % 400 == 0 )

leapYearFlag = true;  // It’s a leap year

else

leapYearFlag = false; // Not a leap year

return leapYearFlag;

}

int main (void)

{

struct date dateUpdate (struct date today);

struct date thisDay, nextDay;

printf (“Enter today’s date (mm dd yyyy): “);

scanf (“%i%i%i”, &thisDay.month, &thisDay.day, &thisDay.year);

nextDay = dateUpdate (thisDay);

printf (“Tomorrow’s date is %i/%i/%.2i.\n”,nextDay.month,

nextDay.day, nextDay.year % 100);

return 0;

}

Program 9.4   Output

Enter today’s date (mm dd yyyy): 2 28 2008

Tomorrow’s date is 2/29/08.

Program 9.4   Output (Rerun)

Enter today’s date (mm dd yyyy): 2 22 2005

Tomorrow’s date is 2/23/05.

Inside main, the statement

next_date = dateUpdate (thisDay);

illustrates the ability to pass a structure to a function and to return one as well. The dateUpdate function has the appropriate declaration to indicate that the function returns a value of type struct date. Inside the function is the same code that was included in the main routine of Program 9.3. The functions numberOfDays and isLeapYear remain unchanged from that program.

Make certain that you understand the hierarchy of function calls in the preceding program: The main function calls dateUpdate, which in turn calls numberOfDays, which itself calls the function isLeapYear.

1. A Structure for Storing the Time

Suppose you have the need to store values inside a program that represents various times expressed  as hours, minutes, and seconds. Because you have seen how useful our date structure has been in helping you to logically group the day, month, and year, it seems only natural to use a structure that you could call appropriately enough, time, to group the hours, minutes, and seconds. The structure definition is straightforward enough, as follows:

struct time

{

int  hour;

int  minutes;

int  seconds;

};

Most computer installations choose to express the time in terms of a 24-hour clock, known as military time. This representation avoids the hassle of having to qualify a time with a.m. or p.m. The hour begins with 0 at 12 midnight and increases by 1 until it reaches 23, which represents 11:00 p.m. So, for example, 4:30 means 4:30 a.m., whereas 16:30 represents 4:30 p.m.; and 12:00 represents noon, whereas 00:01 represents 1 minute after midnight.

Virtually all computers have a clock inside in the system that is always running. This clock is used for such diversified  purposes  as informing the user of the current time, causing certain events to occur or programs to be executed at specific times, or recording the time that a particular event occurs. One or more computer programs are usually associated with the clock. One of these programs might be executed every second, for example, to update the current time that is stored somewhere in the computer’s memory.

Suppose you want to mimic the function of the program described previously— namely, to develop a program that updates the time by one second. If you think about this for a second (pun intentional), you realize that this problem is quite analagous to the problem of updating the date by one day.

Just as finding the next day had some special requirements, so does the process of updating the time. In particular, these special cases must be handled:

  1. If the number of seconds reaches 60, the seconds must be reset to 0 and the min- utes increased by 1.
  2. If the number of minutes reaches 60, the minutes must be reset to 0 and the hour increased by 1.
  3. If the number of hours reaches 24, the hours, minutes, and seconds must be reset to 0.

Program 9.5 uses a function called timeUpdate, which takes as its argument the current time and returns a time that is one second later.

 

Program 9.5   Updating the Time  by One Second

// Program to update the time by one second

#include <stdio.h>

struct time

{

int hour;

int minutes;

int seconds;

};

int main (void)

{

struct time timeUpdate (struct time now);

struct time currentTime, nextTime;

printf (“Enter the time (hh:mm:ss): “);

scanf (“%i:%i:%i”, &currentTime.hour,

&currentTime.minutes, &currentTime.seconds);

nextTime = timeUpdate (currentTime);

printf (“Updated time is %.2i:%.2i:%.2i\n”, nextTime.hour,

nextTime.minutes, nextTime.seconds );

return 0;

}

// Function to update the time by one second

struct time timeUpdate (struct time now)

{

++now.seconds;

if ( now.seconds == 60 ) {    // next minute

now.seconds = 0;

++now.minutes;

if ( now.minutes == 60 ) { // next hour

now.minutes = 0;

++now.hour;

if ( now.hour == 24 ) // midnight

now.hour = 0;

}

}

return now;

}

Program 9.5   Output

Enter the time (hh:mm:ss): 12:23:55

Updated time is 12:23:56

Program 9.5   Output (Rerun)

Enter the time (hh:mm:ss): 16:12:59

Updated time is 16:13:00

Program 9.5   Output (Second  Rerun)

Enter the time (hh:mm:ss): 23:59:59

Updated time is 00:00:00

The main routine asks the user to enter in the time. The scanf call uses the format string

“%i:%i:%i”

to read the data. Specifying a nonformat character, such as ‘:’, in a format string signals to the scanf function that the particular character is expected as input. Therefore, the format string listed in Program 9.5 specifies that three integer values are to be input— the first separated from the second by a colon, and the second separated from the third by a colon. In Chapter 16, “Input and Output Operations in C,” you learn how the scanf function returns a value that can be tested to determine if the values were entered in the correct format.

After the time has been entered, the program calls the timeUpdate function, passing along the currentTime as the argument. The result returned by the function is assigned to the struct time variable nextTime, which is then displayed with an appropriate printf call.

The timeUpdate function begins execution by “bumping” the time in now by one second. A test is then made to determine if the number of seconds has reached 60. If it has, the seconds are reset to 0 and the minutes are increased by 1. Another test is then made to see if the number of minutes has now reached 60, and if it has, the minutes are reset to 0 and the hour is increased by 1. Finally, if the two preceding conditions are sat- isfied, a test is then made to see if the hour is equal to 24; that is, if it is precisely midnight. If it is, the hour is reset to 0. The function then returns the value of now, which contains the updated time, back to the calling routine.

Source: Kochan Stephen G. (2004), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.

Leave a Reply

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