Lab 03: intro to arrays and structs

Work to complete these exercises in lab today. Whatever you do not finish in the lab, try to complete by the start of lecture on Wednesday.


This repository contains a few sample C++ files that you will modify for this lab assignment. The work will give you practice with two primitive data structures in C++, arrays and structs. These each can be used as the low-level building blocks of other data structures. They are the low-level components of the library object classes that come with C++, and of ones ones that we will invent ourselves.

Arrays

Arrays allow you to construct a fixed-size sequence of data values, all of the same type. Each item of an array can be accessed by its index, numbered from 0 up to, but excluding, the size of that array. For example, the statement below builds an array of 5 items, each of type int. It also gives that array the name values.

int values[5];

The contents of the array are allocated, but the actual values of each are indeterminant. So we need to then explicitly give each array item a value. The for statement below sets the items to be the first 10 integer squares starting at 0:

for (int i=0; i < 5; i++) {
    values[i] = i * i;
}   

The array built above is a stack-based array. That means that its storage lives alongside the local variables of the function that uses it. That also means that the array’s lifetime is the lifetime of that particular function’s call.

Because it is allocated on the stack, we require that the size of the array be an integer constant. Though C++ allows arbitary-sized stack-allocated arrays, most expert programmers discurage such use of them.

There is an alternate syntax to the above that allocates the array, initializes its contents, and names its variable in a single statement. Below is an example

int values[5] = {0,1,4,9,16};

The info on the right hand side of = is an initializer list, and it tells C++ what the 5 items should be set to with the array’s allocation.

It is possible to pass arrays as parameters to functions and procedures, but we need to limit that idea for this lab. In full generality, it requires an understanding of pointers, something we’ll learn in Wednesday’s lecture. We’ll also talk about more complex allocation of arrays, including using the heap memory instead of the stack memory. This will give us longer lifetimes for arrays, but it also requires us to understand pointers.

So here we’ll mostly work on arrays within main, or at least only allocated within main. Exercises 3 and 4 happen to also have you write functions that take arrays as arguments.

Structs

Structs are like primitive object classes. They are just a collection of named data items—the struct’s fields. Since different structs have different collections of field names and field types, then each struct has to have its own type, describing those. For example, below is a definition of a new CS2Student struct type:

struct CS2Student {
  std::string name;
  int year;
  bool isTA;
};

The above definition tells C++ that a CS2Student consists of three named fields, name, year, and isTA, each with that specified type.

Once defined, you can then have variables of type CS2Student in your code. For example, a function could have a variable s declared as below:

CS2Student s;

This allocates space for a string, followed by an integer, followed by a boolean, onto that function’s stack frame. The contents of each are indeterminate since there is no initializer, and so the code might then set each of the fields with assignment statements, like so:

s.name = "Rory Gluthy";
s.year = 1;
s.isTA = false;

It is also possible to use initializer lists for structs, too. Below we do that for a new struct variable t:

CS2Student t = {"Dom Kalemba", 2, true};

Structs can be passed as values to functions, and returned as values from functions. When a struct is passed to a function, C++ copies each field of the struct into another struct, the parameter struct of the called function. This means that a function like

void outputInfo(CS2Student u) { ... }

called with outputInfo(s) can be thought of as acting just like the procedure

void outputInfoParts(std::string nm, int yr, bool ta) {
  CS2Student u;
  u.name = nm;
  u.year = yr;
  u.isTA = ta;
  ...
}

called with outputInfoParts(s.name,s.year,s.isTA). Just as those three values of the arguments are copied into the new storage for parameters nm, yr, and ta, for the latter call, so are each of the fields of s copied into u for the former.


Array Exercises


Exercise 1. the mean value

Modify the code stats.cc that we just examined in class. It creates an array of double of size 10, and then “fills” it with a (possibly smaller) set of values. Modify main so that it computes and outputs the mean of the data entered. This is just the sum of the data values divided by the size of the data set.

So, if the user enters the three items 5, 12, and 4, then the program should report

The mean value of that data is 7.0. 

because that is the average (5+12+4)/3.

Since the size of the data is an int, you’re code needs to convert that integer value to a value of type double. In C++ terminology this conversion is called a “cast”. To do so use the expression

static_cast<double>(size)

This yields a value of type double equal to the integer value held in size.


Exercise 2. the minimum value

Now add a section of the code that computes the minimum value of the data in the array and reports it, like so:

The minimum value of that data is 4.0. 

Exercise 3. mean and minimum functions

Restructure the code that you just wrote so that it instead computes the mean value of an array of data using a function named meanValue. The type signature for this should be the code

double meanValue(double* values, int length) { ... }

For now, don’t worry about the type double* used for values. it is something called a pointer type; we’ll talk about this in class tomorrow. Copy the code verbatim. Within the body of meanValue you can access the array components of values with the bracket notation.

Now modify main so that it uses this function. It should say something like

std::cout << "The mean value of that data is "; 
std::cout << meanValue(data,size);
std::cout << ".\n"

Do the same for the minimum value calculation: Write a similar function named minValue and have it used in main.


Struct Exercises


Exercise 4. car

In the source file named car.cc, invent a new struct named car that holds these pieces of information:

  • make and model: two strings like “VW” and “Beetle”, or “Mazda” and “RAV4”, etc.

  • odometer: the number of miles the car has been driven. Have its type be double.

  • fuel: the number of gallons in the car’s gas tank, also of type double.

  • mpg: this is the gas mileage of the car, in miles per gallon, and is of type double.

Write a C++ function drive(double d, car c) that takes a distance d in miles and a car struct c. It should modify c assuming that the car attempts to drive that distance d, using the fuel that it holds. If it does not have enough gas to drive that distance, then its fuel amount goes to 0.0, and it drives an appropriate fraction of those miles. The odometer should reflect the distance driven. The updated c struct should be returned, so that the line below excerpted from main compiles without complaint:

aCar = drive(distance, aCar);

In the end, you should have a working main that asks for info about the car, asks for a distance it should be driven, and then reports the status of the car after that drive call was made.

Exercise 5. complex product

In lab we just demonstrated a program cmpx.cc that computed with complex numbers using a struct we invented called cmpx. We wrote additon to report the sum of two complex numbers.

Add a function and its use in main that computes the product of two complex numbers. Call it times.

Note that the product of a + bi with c + di (because of “FOIL”) has a real part equal to ac - bd and an imaginary part equal to ad + bc.