CIS 1057. Computer Programming in C

Chapter 10. Structure Types

 

1. User-defined structure types

Very often a computer system needs to keep records, each of which contains information regarding a specific object or event, where each data item may has a different type.

Therefore, there is a need for a data structure that can contain components with different types (which means an array cannot be directly used).

In C, a structure can be defined by specifying its components by type and name.

For example, a planet can be described by its five attributes: name, diameter, number of moons, orbit time, and rotation time. To organize these data together, the following structure type planet_t can be defined:

#define STRSIZ 10

typedef struct {
  char name[STRSIZ];
  double diameter;
  int moons;
  double orbit_time, rotation_time;
} planet_t;
When a variable is declared as of a structure type, the variable will get the memory for storing values for its components. For example,
  planet_t current_planet, previous_planet,
           blank_planet = {"", 0, 0, 0, 0};
The last declaration comes with an initialization of the components, according to their defining order in the structure.

A component in a structure type can be a (previously defined) structure type or array, so as to form a hierarchical structure.

In general, a structure type is defined in the following format:

typedef struct {
  type_1 name_list_1;
  type_2 name_list_2;
     ... ...
  type_n name_list_n;
} type_name;
where a name list consists of component names separated by comma (",").

Please note the similarity and difference between this definition and the definition of an enumerated type, as well as an array.

After a structured variable is declared as belonging to a structure type, a component in it can be accessed using the variable name followed by the direct component selection operator, which is a period ("."). A component used in this way is just like a variable of the corresponding type, as in FIGURE 10.1:

Then, the following statement

  printf("%s's equatorial diameter is %.1f km.\n",
         current_planet.name, current_planet.diameter);
displays the sentence
Jupiter's equatorial diameter is 142800.0 km.
The direct component selection operator has the highest precedence (when used with other operators) and is left associative (when occurring more than once). See Table 10.1 (page 572), or this webpage, for a review of precedence and associativity of operators.

When the name of a structure type variable is used with no component selection operator, it refers to the entire structure. A new copy of a structure's value can be made by an assignment, as the following:

  previous_planet = current_planet;

 

2. Structure type data as parameters

When a structured variable is passed as an input argument to a function, all of its component values are copied into the components of the function's corresponding formal parameter. When such a variable is used as an output parameter, the address-of operator must be used.

For example, see the programs in FIGURE 10.2, FIGURE 10.3, and FIGURE 10.4.

Please note that in the last example, "&(*plnp).diameter" actually means "&((*plnp).diameter)", and it cannot be further simplified into "&*plnp.diameter" — here "." has higher precedence than "&" and "*", and the other two are right associative.

FIGURE 10.5 shows the data areas of functions main and scan_planet during execution of the following statement in main:

  status = scan_planet(& current_planet);

C also provides an indirect component selection operator, "->", as a combination of a direct component selection operator and a pointer reference, that is, when structp is a pointer to a structure, the following two expressions are equivalent:

  (*structp).component   // referencing, then direct component selection
  structp->component     // indirect component selection
Consequently, the assignment in FIGURE 10.4 can be rewritten as
      result = scanf("%s%lf%d%lf%lf",  plnp->name,
                                       &plnp->diameter,
                                       &plnp->moons,
                                       &plnp->orbit_time,
                                       &plnp->rotation_time;
Here "->" has a higher precedence than "&".

 

3. Structured return values

In C, a function can return a structure value, with copies of all its components.

Example: FIGURE 10.6 and FIGURE 10.7, and in the latter the following structure is used:

typedef struct {
  int hour, minute, second;
} time_t;
The following statement uses the function to update a time record:
  time_now = new_time(time_now, secs);
as shown by FIGURE 10.8:

 

4. Problem solving with structure types

Combining a user-defined type with a set of basic operations creates an abstract data type (ADT). For example, FIGURE 10.9 shows the operations defined on structure type planet_t:

ADT provides a higher-level description about the data and the operations.

CASE STUDY: A user-defined type for complex numbers

A complex number is a number with a real part and an imaginary part. For example, the complex number a + bi has a real part a and an imaginary part b, where the symbol i represents the square root of -1. We will need to define functions for the I/O of these numbers, as well as for arithmetic operations and finding absolute value on them.

We use the following structure type for complex numbers:

  typedef struct {
      double real, imag;
  } complex_t;
The operations defined on complex numbers are specified by the following function prototypes:
  int scan_complex(complex_t *c);
  void print_complex(complex_t c);
  complex_t add_complex(complex_t c1, complex_t c2);
  complex_t subtract_complex(complex_t c1, complex_t c2);
  complex_t multiply_complex(complex_t c1, complex_t c2);
  complex_t divide_complex(complex_t c1, complex_t c2);
  complex_t abs_complex(complex_t c);
A partial implementation of these functions, as well as a demo driver, are in FIGURE 10.10.

 

5. Parallel arrays and arrays of structures

An array of structures are usually equivalent to parallel arrays of the components.

For example, if each student record consists of an ID number and a GPA value, then a list of students can be represented as an array of structure:

  typedef struct {
    int     id;
    double  gpa;
  } student_t;

  student_t stulist[50];
and the array is shown in FIGURE 10.11:

The same data can be stored as two arrays that "parallel" to each other, in the sense that the same index in them indicate data for the same student:

  int     id_list[50];
  double  gpa_list[50];
Usually the first way is more natural and convenient.

CASE STUDY: Universal measurement conversion

The problem is to write a program to convert a measurement from one unit into another unit. The relations among the units are loaded into the program from a file, where each unit is a structure:
  typedef struct {              /* unit of measurement type           	*/
      char   name[NAME_LEN];    /* character string such as "milligrams"	*/
      char   abbrev[ABBREV_LEN];/* shorter character string such as "mg"	*/
      char   class[CLASS_LEN];  /* character string such as "pressure",
                                   "distance", "mass"                  	*/
      double standard;          /* number of standard units equivalent
                                   to this unit                       	*/
  } unit_t;
All the units are put into an array of unit_t. Each time the user is asked to input a measurement value, the old (source) unit, and the new (destination) unit. The result is the same measurement in the new unit.

The complete program is in FIGURE 10.12. The data file and sample run are shown in FIGURE 10.13: