CSC 161 Grinnell College Spring, 2010
 
Imperative Problem Solving and Data Structures
 

Structures in C — structs

Goals

In this laboratory you will gain experience working with structs in the C programming language.

Prerequisites:

Functions, arrays, and parameters in C.

Steps for this Lab

  1. Getting Started
  2. Representing Time with a Struct
  3. Passing Structs to Functions

Part A: Getting Started

Consider the following struct that might be used in a campus directory:

    #define MAX 20
    struct entry {
        char first[MAX];
        char last[MAX];
        char dorm[MAX];
        int phone;
    };
  1. Write a function equalEntry that takes two entry records as parameters and returns true if the entries contain identical data and false otherwise.

    Reminder: You will need to compare strings with the functions strcmp or strncmp. Type man strcmp in a terminal window for details.

  2. Write a function comesFirst that takes two entry records as parameters and returns true if the name within the first entry comes before the name of the second. In comparing names, comesFirst initially should compare last names. If the last names are different, comesFirst should check dictionary order, ignoring capitalization. If the last names are identical, then comesFirst should compare the first names using dictionary order and ignoring capitalization.

  3. Write a program that reads two entry records, prints whether they are identical (using equalEntry for the test), and prints which of the two entries comes first (using comesFirst).

Part B 2: Representing Time with a Struct

Refer to the reading for this lab for a proposed definition of structure timeinto_t.

Structure types may be used as return types or argument types in functions. A function that converts time values given in seconds (e.g., 12345.67) to time values given in hh:mm:ss.sss format might have the prototype:

    timeinfo_t convertTime( double realTime )

and would look like:

    timeinfo_t convertTime( double realTime )
    {
        timeinfo_t result;
        .
        .
        .
        return result;
    }
  1. Complete the body of this function so that it converts real time values given as a number of seconds to hh:mm:ss.sss format.

    Write a small test program that shows that 12345.67 seconds is equivalent to 3:25:45.67 (3 hours, 25 minutes and 45.67 seconds) by printing out the result.

  2. A function for adding two times given in the hh:mm:ss.sss format might have the prototype

        timeinfo_t addTimes( timeinfo_t one, timeinfo_t two )
    

    Complete the addTimes function so that it may be used to add two times given in the hh:mm:ss.sss format.

    Write a short test program that adds 1:2:3.33 to 2:3:4.44 and verify that you get 3:5:7.77.

If you wrote your addTimes function using the "obvious" algorithm, it will likely produce the result 10:63:12.33 when 5:30:12.33 is added to 5:33:0.00. Note that the result has more than 59 minutes!

For this reason, it would be useful to have another function that takes a single timeinfo_t parameter, and returns a "normalized" version of the parameter. That is, it returns a result with the number of minutes and seconds both being between 0 and 59.

  1. Write a function normalize having prototype

            timeinfo_t normalize( timeinfo_t originalTime )
    

    that returns a normalized representation for originalTime. Then revise your addTimes function so that it returns a normalized time result.

Part C: Passing Structs to Functions

  1. Modify your test program so that it creates an un-normalized timeinfo_t variable (e.g., 10:63:12.33). Note that in C there is no sense of a "private" member, so your main program can initialize the members of a struct directly. For example,

       timeinfo_t time1;
       time1.hours = 10;
       .
       .
       .
    

    Then call your normalize function, passing it the new struct. Finally, check whether the original struct argument was changed by normalize.

    Did you get the result that you expected? (The original should not have been changed, at least not unless you reassigned the return value of the function to the original struct variable.)

  2. Passing and returning struct variables with the technique used above can be inefficient because the whole struct argument is copied to variables local to the function. It may be faster to pass and return pointers to struct variables.

    Modify your addTimes function from the previous exercise, such that it now accepts the addresses of two timeinfo_t variables. (Note that to accept the address of a struct, one must use a parameter which is a pointer to the given struct type.) In addition, to ensure that the function can not modify the original struct arguments unintentionally, you should "const qualify" the parameters. Thus, your function header should read as follows:

      
        timeinfo_t addTimes( const timeinfo_t* one, const timeinfo_t* two )
    

    Within your function, you can no longer access the members of each struct with syntax such as one.hours. Instead, you must use one->hours. Do you understand why?

    Finally, be sure that when you call the function, you pass it addresses. For example,

       timeinfo_t time3 = addTimes(&time1, &time2);
    

    At this point, your program should work smoothly and produce the same results it did prior to this design upgrade.


This document is available on the World Wide Web as

     http://www.cs.grinnell.edu/~walker/courses/161.sp10/lab-structs.shtml

Part A: created 11 April 2008 by Henry M. Walker
revised 11 April 2008
Valid HTML 4.01! Valid CSS!
Parts B, C: created and revised 2001-2004 at the University of Toronto by Alan Rosenthal and Tom Fairgrieve
revised March 2007 by Marge Coahran with permission from Alan and Tom
Full Lab: revised 11 April 2008 by Henry M. Walker
revised 25 January 2009 by Henry M. Walker
last revised 3 April 2010 by Henry M. Walker
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.