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];
We learned to reference individual elements of an array using subscripts such as x[1] or x[i].
![]()
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).
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
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”
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
![]()
![]()
…
![]()
![]()
![]()

![]()
![]()
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.
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. & … - applies the address
of operator to (*f).numer .numer – select the numer component of f2 f Address Of f2 f2 numer denom

Step 3
Step 2
![]()
![]()
![]()
![]()
Main scanFract