CPSC170
Fundamentals of Computer Science II

Lab 11

Structures

Introduction

Today we will learn how to design a complex data type called a struct. A struct is a collection of data and function members. For example, suppose we want to make a type to represent a student. We will need to store the student’s first name, last name, and age. A character array will be a good choice to represent both first and last names. An unsigned integer will be used to represent age. As you might have noticed, structs can be composed of multiple data types, unlike arrays that contain only a single data type. The following struct can be used to represent a student.


#define MAX_NAME_SIZE 50

// The name of this struct is Student. Note the struct keyword.
struct Student {

  // The first name is represented by a sequence of characters.
  char firstName[MAX_NAME_SIZE];
	
  // The second name is represented by a sequence of characters.
  char lastName[MAX_NAME_SIZE];
	
  // The students age is represented by an unsigned integer.
  unsigned age;

}; // Note the semicolon that marks the end of the definition of a struct.


int main() {

  // Creates a student struct called newStudent.
  Student newStudent;

  char firstName[] = "Millicent";
  char lastName[] = "Bystander";

  // Sets the member variable firstName to Millicent.
  int i;
  for (i = 0; firstName[i] != '\0'; i++) {
    newStudent.firstName[i] = firstName[i];
  }
  
  // Null character to mark the end of the string.
  newStudent.firstName[i] = '\0';

  // Sets the member variable lastName to Millicent.
  for (i = 0; lastName[i] != '\0'; i++) {
    newStudent.lastName[i] = lastName[i];
  }
  
   // Null character to mark the end of the string.
  newStudent.lastName[i] = '\0';

  // Sets the member variable age to 21.
  newStudent.age = 21;

  // Prints out the member variables of newStudent.
  std::cout << "Student name: " << newStudent.firstName << " "
            << newStudent.lastName << std::endl;
  std::cout << "Student age: " << newStudent.age << std::endl;
}

Try running the above code. Your output should be:

Student name: Millicent Bystander
Student age: 21

Why don't we add a print function to the Student struct. Its job is to print the student's data. Our Struct will now look like this.


// The name of this struct is Student. Note the struct keyword.
struct Student {

  // The first name is represented by a sequence of characters.
  char firstName[MAX_NAME_SIZE];
	
  // The second name is represented by a sequence of characters.
  char lastName[MAX_NAME_SIZE];
	
  // The students age is represented by an unsigned integer.
  unsigned age;
  
  //prints the student's data to the standard output.
  //PRE: The arrays firstName and lastName contain the null character.
  //POST: The student's data has been printed to the standard output.
  void print() {
    std::cout << "First name: " << firstName << std::endl;
    std::cout << "Last name: " << lastName << std::endl;
    std::cout << "Age: " << age << std::endl;
  }

}; // Note the semicolon that marks the end of the definition of a struct.

As you can see, functions can also be added to structs. Notice that print doesn't take any arguments, this is because it has access to the data members (firstName, lastName, and age) of the struct.

You can test this function in main with the statement newStudent.print()

A a constructor is a special member of struct. It's responsible for creating a struct. We will add a constructor that takes no arguments (it is called a default constructor). Its job is to set the data members to acceptable temporary values. Our struct now becomes.


// The name of this struct is Student. Note the struct keyword.
struct Student {

  // The first name is represented by a sequence of characters.
  char firstName[MAX_NAME_SIZE];
	
  // The second name is represented by a sequence of characters.
  char lastName[MAX_NAME_SIZE];
	
  // The students age is represented by an unsigned integer.
  unsigned age;
  
  
  // Creates a default student.
  Student() {
    firstName[0] = '\0';
    lastName[0] = '\0';
    age = 0;
  }
  
  //prints the student's data to the standard output.
  //PRE: The arrays firstName and lastName contain the null character.
  //POST: The student's data has been printed to the standard output.
  void print() {
    std::cout << "First name: " << firstName << std::endl;
    std::cout << "Last name: " << lastName << std::endl;
    std::cout << "Age: " << age << std::endl;
  }

}; // Note the semicolon that marks the end of the definition of a struct.

The default constructor is called when a Student struct is created with the following line of code.

Student newStudent;

Put a print statement in the default constructor and see that this is true.

The struct Student can be treated just like any other type. We can create an array of students. Try creating an array of students. It's just like creating an array for any of the fundamental types.

Task 1: Add a new data member to represent a student's ID. Use the type unsigned.

Task 2: Update the print function to output the student's ID.

Setting a students name is tedious. Let's add a function that sets the first name to a given string.


// The name of this struct is Student. Note the struct keyword.
struct Student {

  // The first name is represented by a sequence of characters.
  char firstName[MAX_NAME_SIZE];
	
  // The second name is represented by a sequence of characters.
  char lastName[MAX_NAME_SIZE];
	
  // The students age is represented by an unsigned integer.
  unsigned age;
  
  
  //Creates a default student.
  Student() {
    firstName[0] = '\0';
    lastName[0] = '\0';
    age = 0;
  }
  
  //PRE: name is a character array of length at most MAX_NAME_SIZE and contains
  //     the null character '\0'.
  //POST: Starting from the first position, all the characters in name up till
  //      and including the null character are written to firstName starting from
  //      index 0.
  void setFirstName(const char name[]) {
    int i;
	
    for (i = 0; name[i] != '\0'; i++) {
      firstName[i] = name[i];
    }
  
    firstName[i] = '\0';
  }
  
  //prints the student's data to the standard output.
  //PRE: The arrays firstName and lastName contain the null character.
  //POST: The student's data has been printed to the standard output.
  void print() {
    std::cout << "First name: " << firstName << std::endl;
    std::cout << "Last name: " << lastName << std::endl;
    std::cout << "Age: " << age << std::endl;
  }

}; // Note the semicolon that marks the end of the definition of a struct.

Task 3: Add a similar function to set the last name.

Fraction

Create a structure that represents fractional numbers. It should have two unsigned data members and one function member for printing the fraction. One for the numerator the other for the denominator. It should have a default constructor that creates the fraction 0/1.

Now, create the following non-member functions (i.e.functions defined outside the struct for Fraction):


// Prints a fraction in the form "numerator / denominator".
void print_fraction(const Fraction& fraction)

// Returns a new fraction that is the sum of lhs and rhs.
// Note, the returned fraction is not necessarily the most
// simple fraction.
Fraction add(const Fraction& lhs, const Fraction& rhs)

// Returns a new fraction that is equivalent to the input
// fraction reduced to its lowest terms.
Fraction reduce(const Fraction& fraction)

For each function, other than print_fraction, include a function that tests it.

Add a fraction data member called grade to the Student struct. Update the print function to also display the student's grade.