LECTURE 23-24: Structure Types (Chapter 11)

 

 

User-defined structure types

 

Motivation: A database is a collection of information stored in a computer system. A database is subdivided into records, which normally contains information regarding specific data objects. Each record is characterized with a number of attributes. Structures are appropriate for storing and processing of such data.

 

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

 

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:

 

typedef struct {

  char name[10];

  double diameter;

  int moons;

  double orbit_time, rotation_time;

} planet_t;

 

When a variable of structure type is declared, the memory will be reserved for storing values of 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 components names separated by comma (",").

A component in a structure variable can be accessed using 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 11.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 11.1 (page 556) 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;

 

Note that this is very simple, as compared to arrays or strings.

 

 

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 11.2 and FIGURE 11.4.

 

/*

 * Displays with labels all components of a planet_t structure

 */

void

print_planet(planet_t pl) /* input - one planet structure */

{

      printf("%s\n", pl.name);

      printf("  Equatorial diameter: %.0f km\n", pl.diameter);

      printf("  Number of moons: %d\n", pl.moons);

      printf("  Time to complete one orbit of the sun: %.2f years\n", pl.orbit_time);

      printf("  Time to complete one rotation on axis: %.4f hours\n",

pl.rotation_time);

}

 

/*

 * Fills a type planet_t structure with input data. Integer returned as

 * function result is success/failure/EOF indicator.

 *     1 => successful input of one planet

 *     0 => error encountered

 *     EOF => insufficient data before end of file

 * In case of error or EOF, value of type planet_t output argument is

 * undefined.

 */

int

scan_planet(planet_t *plnp) /* output - address of planet_t structure

to fill     */

{

      int result;

 

      result = scanf("%s%lf%d%lf%lf",  (*plnp).name,

                                       &(*plnp).diameter,

                                       &(*plnp).moons,

                                       &(*plnp).orbit_time,

                                       &(*plnp).rotation_time);

      if (result == 5)

            result = 1;

      else if (result != EOF)

            result = 0;

 

      return (result);

}

 

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 11.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 11.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 "&".

 

 

Structured return values

 

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

 

Example (similar to Figure 11.6):

 

/*

 * Gets and returns a planet_t structure

 */

planet_t scan_planet2(void)

{

      planet_t planet;

 

      scanf("%s%lf%d%lf%lf",  planet.name,

                              &planet.diameter,

                              &planet.moons,

                              &planet.orbit_time,

                              &planet.rotation_time);

      return (planet);

}

 

This function has the same effect as function scan_planet from Figure 11.4. The function call would look like:

 

current_planet = scan_planet2();

 

 

Arrays of Structures

 

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 structures. Let us define structure student_t first:

  typedef struct {

    int     id;

    double  gpa;

  } student_t;

 

To declare an array with 50 student records we can write:

 

  student_t stulist[50];

 

and the array is shown in FIGURE 11.11:

 

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

 

  int     id_list[50];

  double  gpa_list[50];

 

Usually the first way is more natural and convenient.