Lecture Set 10 – Structured Data Types (Part II) - Structs

 

Required Reading – HK (Hanly/Koffman), Chapter 11 (Sections 1-3, 5)

Required Assignments – Lab 08 – Rework of Lab 07 using Lookup Table

 

A.     Introduction to Structs

 

Review

Thus far, the only aggregate or structured data type we have used was the array.

 

We used arrays to store (model) collections of data of the same type (and a specified size) using a single name.

 

     

 double x[10];   

 

 #define maxItem = 10

    double x[maxItem];

Text Box: double x[10];    x     

 

We learned to reference individual elements of an array using subscripts such as x[1] or x[i].

 

 

 


Text Box: [0] [1] [2]  [3] [4] [5] [6] [7] [8] [9]

                    

We say:

The contents of x[1] is 2.0

The contents of x[6] is –1.0 (the decimal point would not fit in the picture).

 

 

Modeling a Collection of Data of Different Types

Often, we have a need to create models of related data of different types.  For example, if I owned a music store I wanted to write a program for tracking my inventory of rock ‘n roll CDs I might want to know

 

-- The title of the CD (string of characters)

-- The name of the artist (string of characters)

-- The number of CDs on hand (integer)

-- The cost of the CD (double)

 

Most computer programming languages now provide a way to model such a collection of data.  In C, the feature we need to use is another structured data type called a struct.

 

 

1.      We must first define the new data type, for example

 

#define STRSIZ 15

 

typedef struct { // defining a new type to model R&R CDs

   char title[STRSIZ];  // a string: CD title

   char artist[STRSIZ]; // a string: Artist name

   int onhand;          // Inventory

   double price;        // CD Price

} rockCDType

 

which defines the type (model) for R&R CDs.

 

2.      Once we have defined the data type we can then define variables (sometimes called objects) of that type, just as we would define variables of other types.

 

 rockCDType rockCD;

 

 


      the new              the object or

user-defined type     variable of the new type

 

We often depict such user-defined structured types either horizontally or vertically as shown below.

 

               rockCD

 

 

title

 

Hold Out

 
 


 

artist

 

Jackson Browne

 
 


32

 

onhand

 
 

 

18.75

 

price

 
  

 

 

 

 

onhand

 

artist

 

price

 

title

 

18.75

 

32

 

Jackson Browne

 

Hold Out

 
                                rockCD

 

 

 

 

 

 

B.     Referencing  elements (components) of a struct

 

We now have the same problem with structs as we had with arrays.  How do we reference individual elements (called components) of a struct.  With arrays we used subscripts.  With structs, we use a period (.), also know as the direct component selection operator (or, more simply, the selection operator, for short).

For example:

 

            The contents of rockCD.price is 18.75.

            The contents of rockCD.title is the character string “Hold Out”

 

 

 

C.     Arrays of Structs

 

Just as we can declare arrays of integers, floats, doubles, etc., we can also declare arrays of structured types.  For example,

 

     #define maxSize 1000

     rockCDType rockCD [maxSize];

 

declares an array of size 1000 of structs where each component is a struct of type rockCDType.   This array might be illustrated as follows.

 

title

 

artist

 

onhand

 

price

 
 

 


Text Box: [0]

 


Text Box: [1]

…

 

 


Text Box: [2]

 

 

 

 

 


Text Box: [999]

 

 

 

 


Here,

                        The value of rockCD[999].artist is The Beatles.

                        The value of rockCD[2].price is 15.00

 

 

Note carefully, that entire structs can be manipulated, but only in limited ways.

  1. A struct of one type may be assigned to a struct of the same type.
  2. A struct may be passed by value or address to a function
  3. A struct may be returned from a function (using the return statement)

 

BUT – you cannot do things like displaying an entire struct or scanning data into an entire struct without writing a function to do the work.

 

This is not unlike writing separate functions to read and display a fraction, or for that matter to add two fractions together.  Below are some examples to illustrate how this works.  The examples were taken from a small, complete fractions test program that is a reference document for Lecture Set 10..

 

In main we define a fraction using a struct, and then declare three variables of the new type, fractType.  This is followed by sample calls to functions to scan in a fraction, display a fraction, and add together two fractions to obtain a third fraction.

 

typedef struct {    // defines a fraction type

   int numer;       // numerator

   int denom;       // denominator

} fractType;

 

fractType f1, f2, f3;

 

      ...

 

int flag = scanFract (&f2);  // scan in numer and denom of f2

      ...

 

displayFract (f1);           // display numer and denom of f1

      ...

 

addFract (&f3, f1, f2);      // add f1 to f2 and save in f3

 

 

 

Definitions for the described functions might appear as follows.

 

// Function to display a fraction

void displayFract

   (fractType f)  // IN: fraction to be displayed

{

   printf("The fraction is %d / %d : \n", f.numer, f.denom);

   // OR you could write the fraction in math notation --

   // printf("The fraction is (%d, %d): \n", f.numer, f.denom);

}  // end displayFract

 

 

// Function to add two fractions and return the sum

void addFract

   (fractType *fResult, // OUT: result of adding two fractions

    fractType fLeftop,  // IN: left operand for addition

    fractType fRightop) // IN: right operand for addition

 

{

   (*fResult).numer =

      fLeftop.numer * fRightop.denom

         + fLeftop.denom * fRightop.numer;

   (*fResult).denom = fLeftop.denom * fRightop.denom;

   // simplifies result to lowest terms

   (*fResult) = simplify (*fResult); 

   return;

}  // end addFract  

 

 

 

Why is the * required in from of fResult in the add function?

Why is the *fResult enclosed in parenthesis?

Rewrite the addFract function to return the sum fraction using the return statement, as in fractType addFract (fractType f1, fractTypef2);

 

 

 

// Function to scan in a fraction f in form a / b

// Stores results in numer and denom components of actual arg

//    Returns -1 if / not present

//    Returns 0 if b = 0

//    Returns 1 if ok

int scanFract

   (fractType *f)   // OUT: fraction to be scanned in

{

   char symbol;     // used to store the symbol between a and b

   int tempdenom;   // temp cell for denom - simplifies code

   printf("Enter a fraction in the form a / b:");

   scanf("%d", &(*f).numer);   // or just use f.numer

   scanf("%c", &symbol);

   if (symbol != '/') return (-1);

   scanf("%d", &tempdenom);

   if (tempdenom == 0) return (0);

   (*f).denom = tempdenom;

   return (1);

}  // end scanFract

 

 

To understand the reason for the notation &(*f).numer, refer to the picture below.  Note that f in the function is not a fraction.  Rather, it represents a fraction.  This is not

a new concept.  It was first introduced in Lecture Set 7, when we first discussed output arguments and then again in Lecture Set 8, where passing arrays to functions (passed by address) was discussed. 

 

 

 

 

 

 

If we had written &f.numer, we would have gotten a compiler error.

-- Unary operators are processed right to left (unlike binary operators)

-- Thus the compiler would try to apply the selection operator . to f

-- But f is NOT a fraction. It is a pointer to a fraction and the .operator cannot be applied to an address.

 

 

 

 

 

Step 1 (*f) –

dereference the

pointer to get to the

struct f2

 

Address of f2.numer passed to scanf

 

Note that f is NOT a fraction but the address of a fraction.  We say that f represents a fraction.

 
Step 3

& … - applies the address of operator to

(*f).numer

 
Step 2

.numer – select the numer component of f2

 

f

 

Address

Of f2

 

 

 

 

                   f2

 

numer

 

 

denom

 
                     Main                                                                   scanFract