You are on page 1of 41

Lab Manual for Computer Science I

Terence Soule
January 11, 2008

Contents
Introduction 2

Expectations 3

1 Introduction to Unix 4

Exercise 1.1 6

2 Simple I/O, variables, and expressions 7

Exercise 2.1 (I/O and Variables) 8

Exercise 2.2 (Variables and Memory) 9

3 Conditionals: if, else, switch 10

Exercise 3.1 11

4 Iteration and Looping 12

Exercise 4.1 13

Exercise 4.2 14

Exercise 4.3 15

5 Algorithmic Complexity and Big-O Notaiton 16

Exercise 5.1 17

6 Functions 19

Exercise 6.1 20

7 Scope 22

1
Exercise 7.1 23

8 Arrays 25

Exercise 8.1 26

9 2-Dimensional Arrays 27

Exercise 9.1 28

10 Character Strings 30

Exercise 10.1 31

11 Pointers 32

Exercise 11.1 33

12 Structures 34

Exercise 12.1 36

13 Recursive Functions 37

Exercise 13.1 38

A Pico 39

B The Curses Library 40

2
Introduction
This laboratory manual was written to accompany the Computer Science I course (CS120) for the University
of Idaho Computer Science program. Lab sections meet once a week for 2 hours. Each section of this manual
contains a brief introduction to the material and one or more labs. The labs parallel and complement the
material being covered the in the lecture portion of the course.
The emphasis in this manual is learning through practice and experimentation. Learning a new pro-
gramming language requires a lot of practice writing programs and these labs are designed to provide that
practice. These labs are also designed to reinforce experimentation and debugging skills, both of which are
important to the successful programmer.
This manual is divided into a number of labs. Each lab begins with some introductory material for
that week’s lab. Each lab is followed by one or more exercises. It is not expected that all of the exercises
associated with a given lab will be assigned every week. The lab or course instructor will choose each week’s
exercises(s) from the appropriate section. Exercise(s) will be assigned during the lecture before the lab meets
to allow students to read the appropriate section and to begin the exercise(s) early if they chose. Assigned
exercise(s) must be turned in at the end of the laboratory period.
The lab manual does not need to be followed in the order it is written. Instructors may choose to skip
some sections or otherwise vary the order in which the material is covered to better fit their lectures.

3
Expectations
All students must show up at the beginning of the lab period. All work must be complete and turned in by
the end of the lab period. Late work will not be accepted. If the exercise(s) are assigned before the lab meets
students may start, and possibly complete, the exercise(s) before the lab meets. In these cases students may
turn in their work at the beginning of the lab period, but they still must show up for lab.
All work should be typed. In exceptional cases it may be acceptable to annotate printed work by hand.
Any programs written as part of a lab exercise must be turned in along with a copy of the output showing
the results.
All work must include the student’s name, the date and the lab number. Individual exercises should be
clearly marked.
It is up to the student to show that their programs function properly. Every program should be run mul-
tiple times with different inputs or parameters to show the it functions properly under a range of conditions.
It is acceptable to request help from or to discuss problems with other students. However, any work you
turn in must be your own work. You must understand it and be able to explain it. Copying another students
code is strictly forbidden.
There are a number of expectations that apply to any programs turned in:
• All programs should be adequately commented.
• All programs should begin with a comment containing the student’s name, date and assignment num-
ber.
• Variable names should be descriptive.
• Programs should be broken into functions where appropriate.
• Output should be clear.
• Requests for input from the user should be clear.
• Programs should be adequately tested to show that they function properly under ‘reasonable’ condi-
tions.

4
Lab 1: Introduction to Unix
The goal of the exercises in this section is to familiarize the reader with basic Unix operations.
Unix is an operating system originally designed in the 60’s and 70’s to be multi-user, multi-taking
operating system. It is one of the most, if not the most, influential operating system ever developed.
Unix must have a way of organizing the users’ files and of allowing multiple programs to run simulta-
neously without interfering with each other. Files are organized hieratically in what is known as a ‘tree’
structure. The root of the tree is called ‘/’. Each ‘branch’ of the tree is a subdirectory. Each subdirectory
has its own name. (Subdirectories are equivalent to folders in a Windows or Macintosh environment.)
Every user is assigned their own subdirectory whose name is the same as the user’s login. Thus, the user
tsoule, keeps all of his files in a subdirectory called tsoule or in subdirectories within the tsoule subdirectory.
Each subdirectory is protected so that only certain users are allowed access to it. In addition, there are
several levels of access, for example you might be allowed to read the files in a certain subdirectory, but not
to modify those files. (This is common in subdirectories containing programs that every user wants to use,
such as the c++ program, but that users shouldn’t be allowed to change.)
There are a number of basic commands that are used to negotiate Unix’s file structure:
• ls Short for LiSt, this command lists the files and subdirectories in the current subdirectory. It does
not list ‘hidden’ files; ones whose names starts with a ‘.’.
• ls -a This command lists all of the files and subdirectories in the current subdirectory including the
hidden files.
• ls -l This command lists all of the files and subdirectories in the current directory in the ‘long’ format
- it lists information other than just the names.
• pwd Short for Print Working Directory, this command tells the used where they are in the files
structure. A typical output would be /users/faculty/tsoule/ showing that the user is currently in the
subdirectory tsoule, which is in the subdirectory faculty, which is in the subdirectory users, which is
in the root directory /.
• cd directoryname Short for Change Directory the command changes which directory the user is cur-
rently in.
• cp filename1 filename2 Short for CoPy, this command copies filename1 into filename2. If filename2
already exists, it is replaced with the new file. The file names can include directories.
• mv filename1 filename2 Short for MoVe, this command copies filename1 into filename2 and removes
filenmae1. If filename2 already exists, it is replaced with the new file. The file names can include
directories.
• mkdir directoryname Short for MaKe DIRectory, this command creates a new subdirectory.
• rm filename Short for ReMove, this command removes (deletes) a file.
• rmdir directoryname Short for ReMove DIRectory, this command removes (deletes) a directory. A
directory must be empty before you can remove it.
• script filename The script command makes a record of everything printed on the screen by writing it
to the file filename. (If no filename is given it is written to a file called typescript.) To use the script

5
command type script filename, run the program, then type exit. Typing exit stops the script. The
script file can be printed and turned in as the sample output of a program.
There are two errors to avoid when using the script command. First, if you fail to type exit everything
you do will continue to be dumped into the script file. In particular, if you attempt to open the script
file before exiting the script command you will set up an infinite loop. Second, make sure that the
filename you use is not the same as the name of a program you wrote; otherwise the script file will
overwrite (erase) the program and you will have to rewrite the program.
• man commandname Short for MANual, this command presents help information on the given com-
mand.
All Unix directories include two special subdirectories called ‘.’ and ‘..’ The directory called ‘.’ is the
current directory. Thus, for example, the command cd . moves the user to the current directory, which has
no effect. The directory called ‘..’ refers to the directory above the current directory.

6
Exercise 1.1
This exercise tests some of the commands in Unix and provides some practice with text editors in Unix.

1. Use ssh to log into the UI Unix system.


2. In your home directory use a text editor to create a file called ‘lab1.txt’. (You may want to begin with
pico, a fairly easy to use text editor found on most Unix systems. Help with pico can be found in
appendix A.) Record your name, section number, the assignment number (Lab #1) and current date
in the file. Save and exit the file.
3. Create a new directory in your home directory called ‘Lab1’.
4. Move the file ‘lab1.txt’ into the directory called ‘Lab1’.
5. Change your current directory to be the new directory ‘Lab1’.
6. Use the pwd command to determine where your current directory is in the directory hierarchy. Describe
the results in the ‘lab1.txt’ file.
7. Create a new file called ‘program1.cpp’.
8. Enter the following program into the file:

/* Your name
** Lab #1
** your section number
** the date
**/
#include<iostream> // include the library with I/O commands
using namespace std;
int main(){ // beginning of the program
cout << "Hello, world." << endl; // print some output
}

9. Use the g++ command to compile the file ‘program1.cpp’.


10. Run the compiled program. Describe the results of running the program in the ‘lab1.txt’ file.
11. Rerun the program using the script command to record the output in a file called ‘lab1.output’. Don’t
forget to turn off script before doing anything else.
12. Use the ls command to determine what files are in the current directory. Record the results in the
‘lab1.txt’ file.
13. Turn in the file ‘lab1.txt’, the file ’program1.cpp’, and the ‘lab1.output’.

7
Lab 2: Simple I/O, variables, and expressions
I/O
Input and output (or I/O) in C++ is handled via ‘streams’. (Plain C handles input and output very
differently. For now we will not worry about C’s approach.) Streams can be thought of as sources of or
destinations for values. For example, for your program to receive input it must receive values from some
source, typically the keyboard. So, in C++ we would set up a ‘stream’ to receive data from the keyboard.
Each stream has to have a name so the program can refer to it. The two most common streams are
called cin and cout. cin ‘streams’ input from the keyboard. cout ‘streams’ data to the screen. Later we will
learn how to create streams that direct values to and from files (file stream). The double less than (<<) and
greater than (>>) commands are used in conjunction with the I/O streams. For example, the command:
cout << ‘‘Hello, please enter an integer.’’;
sends the string in quotes to the (standard) output stream, typically the computer screen.

Varaibles and Expressions


Variables are used to store values. Each variable must be declared before it can be used. Declaring a
variable means giving it a name and a type. A variable’s type determines how much space is set aside in
the computer’s memory to store the value and how the value being stored is interpreted. At the most basic
level all values are stored as binary numbers; a variable’s type determines whether that binary number is
interpreted as an integer, a character or as some other type of value. For example, to declare a variable of
type integer called sampleVariable we would write:
int sampleVariable;
You can also initialize a variable with a value (that is assign it a value) when you declare it:
int sampleVariable = 15;
For the most part expressions in C/C++ are handled in the way we would expect. For example, the
command:
sampleVariable = 5 + 2 * anotherVariable;
assigns the value of 2 times the value of anotherVariable plus 5 to the variable sampleVariable. The most
difficult feature of C/C++ expressions is what happens when different types are mixed together in one
expression (for example, trying to divide integers with floating point numbers). This is explored in some of
the following exercises.

8
Exercise 2.1 (I/O and Variables)
This exercise introduces the ideas of simple input and output (I/O), variable types and expressions. We
begin with a simple program that appears to perform some simple calculations. However, a little testing
shows that the program does not function properly and needs to be improved. Pay attention to the tests
that are being made, in the future you will be expected to perform similar tests to make sure that your
program is functioning correctly.

1. Create a new program file and enter the following program (Don’t forget to add comments to the
beginning of the program giving your name, the date and the assignment number. This is required for
all programs.):

#include<iostream>
using namesapce std;
int main(){
int x,y; // declare two variables
int sum = 0, average; // declare two more variables
cout <<‘‘Please enter a number.’’ << endl;
cin >> x;
cout <<‘‘Please enter another number.’’ << endl;
cin >> y;
sum = x+y; // Calculate the sum
cout <<‘‘The sum of ’’ << x << ‘‘ plus ’’ << y;
cout << ‘‘ is ’’ << sum << ‘‘.’’;
// Notice how the spaces are placed in the cout command.
average = sum/2; // Calculate the average
cout <<‘‘The average of your numbers is ’’;
cout << average << ‘‘.’’ << endl;
}

2. Create a separate file (include your name, the date and the assignment number) to record the answers
to the following questions:
• What does the program print for the sum and the average if you enter 5 and 17?
• What does the program print for the sum and the average if you enter 6 and 17?
• What does the program print for the sum and the average if you enter 4.5 and 5.5?
3. Try fixing the program by changing the types of x and y to float.
4. Repeat the previous questions and record the answers.
5. If the program is still not giving the correct answer for the average of 6 and 17. Fix the program so
that it does.
6. Turn in the corrected program and the answers to the questions.

9
Exercise 2.2 (Variables and Memory)
This exercise explores what happens in memory when variables are defined and the size limits of variables.
Both of these frequently lead to programming errors.

• Write a program or programs to help answer the following questions (you may use a previous program
as a starting point):
1. What happens if you declare a variable of type integer and print it’s value before assigning it a
value?
2. What happens if you declare a variable of type float and print it’s value before assigning it a
value?
3. What is the difference (if any) between the answers for questions 1 and 2 and how would you
explain the results?
4. Is it possible to try to store a value that is too large for an integer variable to hold? What happens
if you try? (Hint: to test this with code you can write a series of commands like:

int largeInt = 1000;


cout << largeInt<<endl;
largeInt = largeInt*largeInt;
cout << largeInt<<endl;
largeInt = largeInt*largeInt;
cout << largeInt<<endl;
. . . repeat the multiplication and output
as many times as necessary to get an error
and see what happens.)
5. Is it possible to try to store a value that is too large for a float variable to hold? What happens
if you try?
6. Try to find the largest value that an integer variable can correctly hold.
• Type your answers and turn them in. There is no code to turn in.

10
Lab 3: Conditionals: if, else, switch
Conditionals are commands that allow a program to take different actions depending on specific conditions.
For example, most programs do different things depending on what the user inputs. Without conditionals
every program would do exactly the same thing every time it ran.
The most common conditionals in C/C++ are the if, the if-else, and the switch constructs.
The structure of the if command is:
if(predicate){
// commands to be
// executed if the
// predicate is true.
}
The rest of the program.
This is always executed.
(Note the indenting that follows the if and the else commands. As with most formatting in C/C++ this
is not required for the program to compile correctly, but it does make the program more readable; without
proper indenting it is almost impossible for a person to understand what a program does. You will lose
points if you don’t correctly indent your programs.)
The structure of the if-else structure is:
if(predicate){
// commands to be
// executed if the
// predicate is true.
}
else{
// commands to be
// executed if the
// predicate is false.
}
The rest of the program.
This is always executed.
The curly brackets defining the block after the if and the else can be omitted if only a single command
is used after the if or the else. For example,
if(predicate)
\\single command to be executed
The rest of the program.
This is always executed.
If the predicate in the if statement is zero it is treated as false, otherwise it is treated as true. Thus,
the predicate can be anything that C/C++ can interpret as an integer. However, to make your programs
readable the predicate should generally be a test of some kind, such as X < Y, which is true if X is less than
Y and false otherwise.

11
Exercise 3.1
This experiment is a simple introduction to if and if-else. As always, reuse as much of the code from previous
labs as would be helpful.

1. Write a program that allows the user to enter two integer and then tells the user which of the two
values is smaller. For example, if the user entered the integers 5 and 6 the output should be something
like:
5 is the smaller number.

As always include your name, date, and the lab number as a comment at the beginning of the program.
As always test the program with several different inputs to make sure that it runs properly.
2. What happens if the user enters two numbers that are the same?

3. If necessary fix the program so that it gives a ‘reasonable’ output if the user enter two numbers that
are the same.
4. If you used only simple if commands in your program rewrite it to use if-else constructs. If you already
used if-else constructs, rewrite your program to only use if commands.
5. You should turn in two programs and output showing the test cases for both programs. The two
programs are the program that uses if’s and the program that uses if-else’s.

12
Lab 4: Iteration and Looping
Loops allow a program to execute the same set of commands multiple times. Without them writing programs
that did repetitive tasks would be extremely tedious (even with cut and paste).
The three common types of loops in C/C++ are for, while, and do-while. As an example, the basic
structure of the for loop is:
for(command 1 ; command 2 ; command 3){
// commands to be
// executed
// during the loop.
}
// rest of the program.
// Not part of the loop.
As is usual in C/C++ the curly brackets defining the block after the for and can be omitted if only a single
command is used after the loop. For example,
for(command 1 ; command 2 ; command 3)
// Only command that is in the loop.
// The rest of the program.
(Note the indenting that follows the for command. As with most formatting this is not required for the
program to compile correctly, but it does make the program more readable; without proper indenting it is
almost impossible for a person to understand what a program does. You will lose points if you don’t correctly
indent your programs.)
command 1 is executed once at the beginning of the loop. It is used to initialize the loop and commonly
defines a ‘counter’ - a variable that is used to count the number of times a loop has been executed. A typical
command is:
int i = 0
which defines a new variable i and sets its value to 0.
command 2 is tested at the end of every loop. If it is false (zero) the loop ends. Thus it should be a
predicate. A typical command is:
i < 100
which ends the loop when i is equal to or greater than 100.
command 3 is executed at the end of every loop, before the conditional (command 2) is tested. It is
usually used to increment the counter. A typical command is:
i++;
which increases i by one.
For example, the following loop repeats exactly 100 times.
for(int j = 0; j<100 ; j++){ // loop 100 times
// commands to be
// executed
// during the loop.
}

13
Exercise 4.1
This exercise illustrates the importance of looping for highly repetitive tasks. Only the for loop is used.
1. Without using any kind of loop, write a program that has the user enter exactly five integers, and
calculates and prints the sum of the integers.
2. Write a program that will do the same thing using a for loop.
3. Write a new program, still using a for loop, that allows the user to enter up to 100 integers and again
prints the sum. However, if at any point the user enters a zero then the loop halts and the sum of the
numbers already entered is printed. You can end the loop early by setting the counter variable equal
to any value that will cause the loop to halt. (Imagine how hard it would be to write this program
without the ability to loop.)

4. Turn in all three programs with sample output showing test cases, etc.

14
Exercise 4.2
This exercise explores the use of the other looping constructs.
1. Using a for loop write a program that has the user can enter up to 10 integers, but if they enter a zero
the loop stops.
2. Rewrite the program using a while loop.
3. Rewrite the program using a do-while loop.
4. Remember to make sure that each type of loop exits properly for both conditions (the user enters all
10 numbers or the user enters a zero).
5. Which version was easiest to write, why?
6. Turn in all three programs with sample output showing test cases and your answer to the last question.

15
Exercise 4.3
Nested loops are simply programs that have one loop within another. They are often used for printing tables
of data. In using nested loops don’t forget to indent properly to make it clear what sections of code are part
of which loop.
In this exercise we will look at the ASCII values for different characters by building a table of the ASCII
values.
The table should roughly look like:
0 1 2 3 4 5 6 7 8 9
40 ( ) ...
50 ...
60
70
80
90
100
110
This table shows, for example, that the ASCII value 40 (40+0) represents the character ‘(’ and the ASCII
value 41 (40+1) represents the character ‘)’. We will not worry about the values below 40 (many of which
don’t print) or above 119.
To create this table you will need to nested loops. For the rows you may want to count from 40 to 110
in steps of 10. The other loop, for the columns, will need to count from 0 to 9.
The value for each entry in the table will be the character corresponding to the ASCII value of the sum
of the two loop variables. For example, if the loop variables have values 100 and 5 respectively then the table
entry will be the character corresponding to the ASCII value 105. To print this character use the static cast
command:

cout << static_cast<char>(x+y) ;


where x and y are the two loop variables.
You will need to be a little careful about where you put spaces and new lines, to get the table to look
correct.
As always turn in the code and the output. In this case, because there is no input, there is only one
output file.

16
Lab 5: Algorithmic Complexity and Big-O Notaiton
The algorithmic complexity of an algorithm is a measure of how many operations an algorithm requires as a
function of the input size. The Big-O notation is used to describe the upper bounds of an algorithm’s com-
plexity. The big-O value of an algorithm can be determined by first generating an equation that determines
the number of steps an algorithm will require to complete and then by reducing that equation to its most
significant term. For an example, let’s look at a program that will print out a list of factorials. We assume
that each operation takes one time step to complete.

for (int i = 1; i <= n; ++i) {

// This happens ‘n’ times.


int factorial = 1;

for (int j = i; j >= 1; --j) {

// This happens 1 + 2 + 3 + ... + ‘n’ times.


factorial *= j;
}

// This happens ‘n’ times.


cout << i << "! = " << factorial << endl;
}

In evaluating algorithms, it is often helpful to remember the following property:


X
n
n(n + 1)
1 + 2 + 3 + ...+ n = i=
i=1
2

Using this formula, we can see that the code above takes

n(n + 1) n2 n
+ 2n = + + 2n
2 2 2
steps to complete. To determine the Big-O complexity, we ignore any constants and coefficients, because
they will have a relatively small impact in the limit of large n. Similarly, we only include the largest factor,
because it dominates the complexity as n becomes very large. In this example, the largest term is n2 , and
so the Big-O is O(n2 ).

17
Exercise 5.1
In this exercise you will evaluate four programs to try and determine the Big-O complexity of each. You
will not have access to the C++ source code for the applications, but only the compiled executables. The
four applications do the same thing, but each has as different Big-O complexity. The complexities are O(n),
O(n log n), O(n2 ), and O(n3 ). It will be your job to match the programs with the correct complexity.

Obtaining the Executables


You will have to download the executables off of the course syllabus website, These programs will only run
on CS machines or the school Unix machines. The four programs are called max1, max2, max3, and max4.
After transferring the programs to the Unix machines (which you can do with the SSH program file
transfer functionality), you will have to type
chmod u+x max?
to tell the computer that it’s OK to treat these files as applications.

File Input
All four of the programs do the same thing; they read a file consisting of a sequence of positive and negative
integers and find the consecutive subsequence with the highest sum. For example in the series: -5 3 6 -2 7
-5 3 -2 the italicized values form the subsequence with the highest value.
To determine the complexity of the four test programs (they all do the same thing, but they are not all
equally efficient) you will need to test them on sequences of different sizes. The easiest way to do this is to
automatically generate files of random positive and negative integers for the programs to work on.
A program to create such files is below. We have not covered everything that is used in the program, but
it will work if typed in exactly as shown.

1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 #include <ctime>
5 using namespace std;
6
7 int main()
8 {
9 const int MAX_ABSOLUTE_VALUE = 1000;
10
11 // Seed the random number generator, so that it will
12 // output different values each time it’s run.
13 srand(time(0));
14
15 cout << "Please enter a save filename (no spaces): ";
16 string outputFilename;
17 cin >> outputFilename;
18
19 ofstream outstream(outputFilename.c_str());

18
20 if (outstream.fail()) {
21 cout << "Unable to open " << outputFilename << endl;
22 } else {
23 int numInts;
24 cout << "How many integers would you like? ";
25 cin >> numInts;
26
27 for (int i = 0; i < numInts; ++i) {
28 // Get a random number, and scale
29 // it to [0, MAX_ABSOLUTE_VALUE * 2)
30 int randomInteger = rand() % (MAX_ABSOLUTE_VALUE * 2);
31
32 // Shift number to
33 // [-MAX_ABSOLUTE_VALUE, MAX_ABSOLUTE_VALUE)
34 randomInteger -= MAX_ABSOLUTE_VALUE;
35
36 outstream << randomInteger << endl;
37 }
38 }
39
40 outstream.close();
41 }

Testing and Timing the Programs


The four test programs take the name of a file to read as an argument when run, meaning the file name
simply follows the program name when run. To measure the time a program takes to evaluate a file, you
will use the Unix time command.
time ./max4 gen.dat

This runs max4 on the file gen.dat. You will get output similar to below:
The maximum segment sum is 12993

real 0m0.047s
user 0m0.010s
sys 0m0.020s
The user time (0.010 seconds in this example) is the time the computer spent running the program, and is
the one to which you should pay attention.

Turn In
Come up with a method of determining the Big-O complexity of each program. Remember that there is one
each of O(n), O(ns ), O(n log n), and O(n3 ). Turn in your categorizations of the four programs, and a one
or two paragraphs explaining how you came to your conclusions.

19
Lab 6: Functions
Loops are simple programming constructs that make it easy for the programmer to make a program do the
same thing repetitively. Functions have a similar advantage; they allow the programmer to define a set of
commands that can be ‘called’ by a name. Anytime you want those commands to run you simply use the
function name. Without functions you would need to retype the commands over and over again.
You can send information, via ‘arguments’, to a function and receive results, via ‘return value’, from a
function. One way to think of a function is as a mini-program that does part of the full program’s job for
you.
Functions can make a program shorter and easier to read, but more importantly, they can make a program
easier to write. Functions make programs easier to write because they make it easy to design and write a
long program as a series of smaller, simpler functions. Functions also make it easier for programs to be
written by a team. Each team member can write separate functions according to some specific criteria and
those functions are put together to form the whole program.
Functions must be declared before they can be defined or used. The declaration of a function defines the
function’s name, return type, and arguments. For example, the statement:
float sampleFunction(int, float);
declares a function named sampleFunction that returns a floating point value and that takes one integer
value and one floating point value as arguments.
The definition of a function gives the code that is executed when the function is called. For example, the
code:

float sampleFunction(int arg1, float y){


float product;
product = arg1*y;
return product;
}
defines the function called sampleFunction. (The function happens to return the product of its two argu-
ments.)
A common way to format programs is to put function declarations before main and function definitions
after main:
float sampleFunction(int, float); // declaration
...
int main(){
...
sampleFunction used one or more times
...
}
...

float sampleFunction(int arg1, float y){ // definition


float product;
product = arg1*y;
return product;
}

20
Exercise 6.1
For this experiment you will need to divide into teams of three members. Each of the three members will be
responsible for writing part of the entire program. The program will calculate the product of two natural
numbers.

• The first team member needs to write a function that asks the user for a natural number (an integer
larger than zero). If the user doesn’t enter a natural number the function should print a message and
request another number. This should repeat until the user does enter a natural number. (Hint: a
do-while loop will work well for this function.) The natural number is returned by the function.
• The second team member needs to write a function that accepts two integers as arguments; calculates
their product and returns it.
• The third team member needs to write a function that accepts one integer (the product) as an argument
and prints it.
• The main program needs to be written to use these three functions. Note: the second two functions
are easier to write so the team members in charge of those functions should write the main program
as well.
• Put the names of all three team members in the comment block at the beginning of the program.
• Each function should include a comment block giving the name of the team member who wrote that
function.
• To put the program together you will need to place all of the functions in one directory and then
combine them into one file. Unix users may copy files from each other if those files are: in a publicly
accessible directory and are publicly readable.
– Create a new directory in your home directory called ‘pub’. (Use the mkdir command to create
the directory.)
– Use the command chmod a+x pub to make the directory publicly accessible.
– Copy the file containing your portion of the program into the pub directory.
– Use the command chmod a+r filename to make the file publicly readable.
– Now any team member (or anyone else) should be able to change their current directory to your
pub directory and make a copy of the file.
– Remeber that you can use the pwd command to determine your current directory.
• In pico you can use the Read File command (control r) to read one file into another. Use this command
to read the separate function files into the main program file. Don’t forget the function declarations
at the beginning of the program.
• Compile and run the program.

21
Hand In
Hand in one copy of the program and sample output showing the program working. Remember to test
several cases, including the user entering illegal values.

22
Lab 7: Scope
Scope rules determine where in program a variable is available for use and resolves ambiguities if there are
more than one variable with the same name. Used properly scope rules make it much easier to write programs
in isolated parts; the scope rules avoid having variables ‘conflict’, which would lead to unpredictable results.
Variables in C/C++ are inherently local; meaning they are only defined within the set of curly braces
{} in which they are defined - its scope. For example,

{
int x;
// x can be used properly here.
}
// x should not be used here.
In particular, this means that each variable is only accessible within the function (including main) where it
is defined (because every function is defined using curly braces).
For example,

int g = 1;
int main(){
int x = 2;
int y = 3;
// x and y can be used here; they have the values 2 and 3 respectively.
// g can be used here; it has the value 1.
}

void sampleFunction(){
int x = 6;
int z = 9;
// x and z can be used here; they have the values 6 and 9 respectively.
// changing x here will NOT change the value of x in main; they are different x’s.
// g can be used here; it has the value 1.
// changing g here WILL change the value of g in main.
}
In the above example y, z, and both x’s are local variables. They can only be used within the curly braces
where they are defined and changing one x will not effect the other. Because g is not declared within any
set of braces it has global scope - the ‘same’ z is accessible anywhere in the program.

23
Exercise 7.1
The following program is an example of very poorly used scope rules. The mixture of local variables, global
variables, pass-by-reference, pass-by-value, and repeated variable names makes it difficult to predict what
will be printed.
Enter the program, skipping the line numbers, and run the program. Save the output to a file using the
script command. Edit the output file, by adding text that explains for each line of the output where the
variable was declared and where it’s value was assigned, using the line numbers to help. For example, part
of the explanation of the first line of output would be: “x was declared on line 9 and it was assigned the
value 0 on line 9”.
Print and turn in your annotated output.

1 #include<iostream>
2 using namespace std;
3 int function1(int, int &);
4 int function2(int, int &);
5 int g = 5;
6 int main(){
7 int a = 1;
8 int b = 5;
9 int x = 0;
10 cout << "Main 1: a = " << a << " b = " << b;
11 cout << " g = " << g << " x = " << x;
12 cout << endl << endl << endl << endl << endl;
13 {
14 int a = 7;
15 g = 10;
16 cout << "Main 2: a = " << a << " b = " << b;
17 cout << " g = " << g << " x = " << x;
18 cout << endl << endl << endl << endl << endl;
19 }
20 x = function1(a,b);
21 cout << "Main 3: a = " << a << " b = " << b;
22 cout << " g = " << g << " x = " << x;
23 cout << endl << endl << endl << endl << endl;
24 x = function2(a,b);
25 cout << "Main 4: a = " << a << " b = " << b;
26 cout << " g = " << g << " x = " << x;
27 cout << endl << endl << endl << endl << endl;
28 }
29
30 int function1(int x, int &y){
31 int a;
32 a = x+y;
33 g = x+y;
34 y = a+g;
35 cout << "Function1: a = " << a << " x = " ;

24
36 cout << x << " g = " << g;
37 cout << endl << endl << endl << endl << endl;
38 return a;
39 }
40
41 int function2(int b, int &x){
42 int temp;
43 temp = b;
44 b = x;
45 x = temp;
46 temp = function1(b,x);
47 g = temp+x;
48 return temp;
49 }

25
Lab 8: Arrays
So far we have only dealt with simple variables that can hold only a single value, e.g. x = 7. Arrays allow
us to store a list of values and to easily access members of that list.
Like variables (and functions) arrays have to be declared before they can be used. The declaration of
an array defines both the name of the array and how many values it can store. (Like variables arrays have
to be declared before they are used so that when the program is executing the computer knows how much
memory to set aside for storing the values and what type the values are to be treated like.)
The declaration of an array to hold 100 integer values would be:
int LongArray[100];
where the name of the array could be any valid variable name. One tricky feature of C/C++ arrays is
that the values are numbered from 0 to one less than the array size. So, the 50th element of the array is
LongArray[49]. For example you would print the 50th element with the command:
cout << LongArray[49];
or you would set the value with the command:
LongArray[49] = 7;

You can also use an integer variable in the place of the value in the above commands. For example the
command:
cout << LongArray[j];
prints the jth element of the array; where j should be an integer variable that was defined and given a value
earlier in the program.
When you ask for the jth element of an array the computer actually begins at the start of the array and
then takes j steps through memory to find the element you asked for. (The size of the steps depends on the
type of the array, an array of floats requires larger steps than an array of integers, but we don’t need to worry
about that now because it happens automatically.) However, C and C++ don’t do any bounds checking
with arrays. If you define an array with 50 elements and then ask for element number 67 the computer will
start at the beginning of the array and take 67 steps and return the value it finds - even though there’s only
50 elements in the array. The value you get back will not be what you expected.
You can also ‘step’ beyond the memory set aside for your program. For example, if you ask for element
1,000,000 from an array with only 10 elements you may exceed the memory that the operating system has
set aside for your program to use. In this case the operating system steps in, because you’re effectively trying
to access the memory of a different program, and halts your program - printing a cryptic error message in
the process.

26
Exercise 8.1
This is a simple exercise to introduce the use of arrays and some of the common errors that occur when
programming with them.

• Write a simple program that allows the user to enter 10 integers into an array, then prints the array
and calculates, stores and prints the sum and average of the values in the array. Print this program to
be turned in, along with sample output.
• Add a line to the end of the program that prints the 11th element of the array. What gets printed
when you run the program? Try running the program a few times. Is there any consistency in what
gets printed for the 11th element?
• Add a line at the end of the program to print the 10,000th element of the array. What happens now?
What is the exact error message you get?
• Turn in you original program and the results of the last two experiments.

27
Lab 9: 2-Dimensional Arrays
2-dimensional arrays are very common in computer programming. They are often used to store tables of
data and (as in this lab) to represent data that will be displayed on the screen. 2 (or more) dimensional
arrays are declared in a manner similar to 1-dimensional arrays: you must give the type of the data, the
name of the array and the size of each dimension of the array. For example, a 10x20 array of floats would
be defined as:
float sampleArray[10][20];
The size of the array must be declared using a size that the compiler can determine, either a value, a constant
variable, or a ‘defined’ value. Thus, either of the following would be correct:
const int maxx = 10;
const int maxy = 20;
float sampleArray[maxx][maxy];
or
#define maxx 10
#define maxy 20
float sampleArray[maxx][maxy];
It’s better programming technique to use a constant variable or a defined value rather than a value (like
10 or 20). With a constant variable or defined value if you want to change the size of the array in the
program there is only one place that the program needs to be changed. It also means that you don’t have to
remember arbitrary numbers (the array sizes) instead you can remember more meaningful variable names.
To access or to set the value of an element in an N dimensional array you have to give all N index values
using the standard square bracket notation:
x = sampleArray[3][4];
or
sampleArray[3][4] = 3.1415;
As with any array the index values can be integer values or integer variables or expressions that generate
integer values. And, as always with arrays, it is the programmer’s responsibility to make sure that the array
bounds are not exceeded. E.g. don’t try to access element sampleArray[12][25] in an array that was defined
to be 10x20.

28
Exercise 9.1
Below is a small program that roughly represents a robot in a room. The room is defined in a 2 dimensional
array of characters. Objects in the room are stored as characters in the array. For example, the location of
the robot is stored as an ‘R’ in the array. The walls of the room are stored in the array as ‘-’s for the top
and bottom walls and as ‘|’s for the side walls.
The function called setup() puts all of the initial characters into the 2 dimensional array.
For this lab you need to use the curses library (see appendix B) to write a function that prints the room.
The function should take the room array as an argument (look at the syntax of the setup function to help
with passing the array). The function should loop through the entire array, moving to the correct location
of the screen and printing the character stored in the array. E.g. the function should ‘move’ to location r,c
and print the [r][c]’th element of the array for each r and c within the dimensions of the room.

#include<curses.h> // include the curses library


using namespace std;
// define the maximum size of the ’room’
#define MAXX 40
#define MAXY 20

void setup(char[][MAXY],int,int); // sets up the room

int main(){
WINDOW *wnd;
int x=MAXX/2,y=MAXY/2; // robot’s initial location
char room[MAXX][MAXY]; // stores the room
wnd = initscr(); // ’initializes’ the window
clear(); // clears the window
refresh(); // reprints the window
setup(room,x,y); // setup the room with the robot
print(room); // write this function!!
endwin(); // frees the screen for normal use
}

/*This function sets up the room, adding walls, and the robot*/
void setup(char r[][MAXY], int x, int y){
for(int i = 0; i < MAXX; i++){
for(int j = 0; j < MAXY; j++){
r[i][j] = ’ ’; // empty the room
}
}
for(int i = 0; i < MAXY; i++){
r[0][i] = ’|’; // left wall
r[MAXX-1][i] = ’|’; // right wall
}
for(int i = 0; i < MAXX; i++){
r[i][0] = ’-’; // top wall
r[i][MAXY-1] = ’-’; // bottom wall

29
}
r[x][y] = ’R’; // place the robot
}

Turn in your program and sample output showing the room drawn on the screen.

30
Lab 10: Character Strings
In C++ there are two ways to store sequences of characters (“character strings”): as an array of chars or as
a variable of the type ‘string’. For both types of character strings there are a number of useful functions.

Arrays of characters
Arrays of characters are just like any other array. They are declared with commands like:
char a string[10];
Elements of the array (that is any individual character in the array) can be accessed using its index just
like any other array. A number of functions that are used to manipulate arrays of characters are defined in
the library cstring, which must be included to use them. Because C++ doesn’t keep track of the lengths
of arrays a special character is placed at the end of character arrays so the functions know when they’ve
reached the end of a string. The character is called the NULL character. It’s denoted by ‘\0’.

Strings
To use the string type you need to include the library called, simply enough, string. Variables are declared
just like variables of any type:
string another string;
Many of the function that apply to variables of the type string are accessed using operators. For example,
the ‘+’ operator is used to concatenated two strings. Other functions in the string library are defined in the
textbook.
In this lab you will be experimenting with different functions for both types of character strings: arrays
of chars and ‘strings’.

31
Exercise 10.1
Write short pieces of code that demonstrate how each of the following functions work. Both the code and
the output should clearly show what the functions do. For example, the following code could be used to
demonstrate the len() function:

#include<iostream>
#include<cstring>
using namespace std;
main(){
char test_string[] = ‘‘Test String’’;
cout << ‘‘The strlen() function returns ’’;
cout << ‘‘the length of a string’’<< endl;
cout << ‘‘E.g. the length of the string: *’’;
cout << test_string << ‘‘* is ’’ << strlen(test_string);
cout << ‘‘.’’ << endl;
}
(The *’s are put in to make it clear where the string begins and ends.)
Turn in your code and the output demonstrating the function’s use.
Demonstrate the following functions from the cstring library that apply to character arrays:
1. strlen
2. strcpy
3. strcat
4. strcmp
5. strstr
Demonstrate the following functions from the string library that apply to variables of type string:
1. length
2. = (string copy)
3. + (string concatenation)

4. == (string comparison)
5. find

32
Lab 11: Pointers
Pointers are variables that store the address of a memory location. They are called pointers because they
‘point to’ the memory location whose address they store. For example, if the pointer variable p stores the
value B1A2 then it ‘points to’ memory location B1A2.
(Memory addresses are usually labeled using hexidecimal. Hexidecimal is simply a base 16 counting
system. In a base 16 system we need symbols to stand for the values 10 through 15 and the letters A
through F are used. So, B in hexidecimal = 11. For example, B1A2 = 11 ∗ 163 + 1 ∗ 162 + 10 ∗ 161 + 2 ∗ 160 ,
which is 11*4096 + 1*256 + 10*16 + 2 = 45056 + 256 + 160 + 2 = 45474.)
Pointers are declared using a *. For example, the command:
int *p;
creates a pointer called p that can point to a memory location storing an integer (it can also point to
memory locations storing other types, but that will lead to big problems). In this case we haven’t made p
point anywhere yet.
The following code illustrates using pointers:
int *p; // create a pointer
int i = 5; // create an integer variable
p = &i; // make p ‘point to’ i
*p = 6; // change the value in the location p points to
cout << i; // prints _6_; i has ‘magically’ changed
cout << *p; // also prints 6
To make a pointer point to a regular variable we used the & symbol (p = &i). To access the value a
pointer is pointing to we used the * symbol (*p = 6). You can also change where a pointer points to, that is
change what memory location it stores, by using operators like ++ (p++;). This would increase the memory
location stored in the pointer, which makes the pointer point to a later memory location.
(The amount that ++ adds to a pointer’s location depends on the type the pointer is declared to point
to. For example, if a pointer p points to chars then ++ typically changes the memory value stored by p by
1 byte because chars take up 1 byte in memory.)

33
Exercise 11.1
The following code demonstrates some of the different ways pointers can manipulate values in memory. When
you compile and run the code you will see that the array is initially “abcdef” and ends up as “xxxxxx”. You
need to figure out on which line each of the original letters is changed to an x.

(1) #include<iostream>
(2) using namespace std;
(3) void function1(char *);

(4) int main(){


(5) char array[] = "abcdef";
(6) char *p;
(7) cout << "initial array: " << array << endl;
(8) p = array;
(9) *p = ’x’;
(10) p = &array[1];
(11) p++;
(12) *p = ’x’;
(13) p = &array[3];
(14) p[2] = ’x’;
(15) p--;
(16) function1(p);
(17) function1(array);
(18) function1(&array[3]);
(19) cout << "final array: " << array << endl;
(20) }

(21) void function1(char *q){


(22) q++;
(23) *q = ’x’;
(24) }

You only need to turn in a list saying the line number that changed a given letter. For example, ‘the a
was changed on line number 6.’ (which clearly isn’t correct). For letters changed in the function give the
line number in main where the function was called.

34
Lab 12: Structures
Structures are a way to create new types that combine 2 or more existing types. A simple example would
be a new type that consists of two integers (the existing types). Such a type might be useful for fractions:
one integer holds the numerator, the other holds the denominator.
Structures are defined using the struct keyword, and typical using the typedef keyword. For example,
the code:
typedef struct fraction{
int numerator;
int denominator;
} fraction;
defines the fraction type described above; and by using the typedef keyword renames the type from “struct
fraction” to just “fraction”. The numerator and denominator are variables of type integer; they are often
refered to as members of the structure.
So we can now create variables, pointers and arrays using the fraction type and the normal syntax. For
example, the code:
fraction f1;
fraction arrayOfFractions[10];
fraction *fracP;

creates a variable called f1 of the fraction type, an array of ten fractions called arrayOfFractions, and a
pointer to a fraction called fracP.
However, we can not (yet) do things with fractions that we can do with standard types. For example,
commands like:
cout << f1 << endl;
f1 = array_of_fractions[0] + array_of_fractions[1];
cin >> f1;
are all illegal because c++ doesn’t know how we want fractions printed, added or entered. Instead we need
to tell c++ exactly how to do these things. For example, if we wanted fractions to be printed like n/d we
could write:
cout << f1.numerator << "/" << f1.denominator << endl;
in this case we are asking c++ to print f1.numerator and f1.denominator, both of which are integers and
c++ know how to print integers.
Note the use of the dot (.) notation to access the members of the structure. The dot notation is used
for both accessing and setting the values of the members. For example, to have the user enter a fraction we
could write:
cout << "Please enter the numerator";
cin >> f1.numerator;
cout << "Please enter the denominator";
cin >> f1.denominator;

35
This works because c++ knows how to use cin with integers.
Structures can be passed to and returned from functions just like any other type of variable. The structure
name (in these examples fraction) is used as the argument type. So, for example:
fraction func(fraction, int);
defines a function called func that takes a fraction structure and an integer as arguments and returns a
fraction.
Because we can not directly use common c++ elements like cin and cout with structures, it is common
to write functions that do their job for us. For example, writing a function to print a fraction or a function
that adds two fractions together and returns their sum as a fraction.

36
Exercise 12.1
1. Write a structure that will store a name and ID number. The structure should have two members: a
character array or string to store the name and an integer to store the ID.
2. Write a function that allows the user to enter a name and ID number a structure.
3. Write a function that prints the structure. You can chose how the name and ID are printed.
4. Write a program that creates an array of 5 of these name and ID structures, allows the user to enter up
to 5 names and IDs, and then prints the list of names and IDs. The names and IDs should be entered
and printed using the two functions written in steps two and three.

Turn in the program and sample output.

37
Lab 13: Recursive Functions
Recursive functions are functions that call themselves. (You can also have a series of functions that form
a recursive loop - function A calls function B, function B calls function C, ... , until the last function calls
function A again.) To avoid an infinite series of function calls recursive functions must have a ‘stopping
condition’ in addition to a ‘recursive call’. The stopping condition makes sure that eventually the series of
function calls halts. Without a stopping condition the program will run out of memory, typically resulting
in a segmentation fault.

38
Exercise 13.1
The factorial of a natural number N (written as N!) is
N! = N * (N-1) * (N-2) * . . . * 1
it can also be defined recursively as:
1! = 1
N! = N * (N-1)!
Write two functions to calculate factorials. The first function should be recursive and should not use a
loop; the second function should use a loop and should not be recursive.
Use the time command (time ./a.out) to determine which function is faster. You may need to calculate
the factorial of a fairly large number to detect a difference. To determine which function is faster you will
need to ‘hard code’ N, not allow the user to input it. If the program waits for user input it will throw off
the time measurement.
Turn in your code and output showing that both functions correctly calculate factorials. In addition,
report which of the functions is faster and which of the functions is shorter.

39
A Pico
Pico is a simple text editor that we will use for writing programs and occasionally other documents. Pico
can be found on almost all UNIX machines. We begin with Pico only because it is one of the simplest text
editors to learn and use. Most computer science majors eventually chose to use a more advanced and more
powerful editor such as vi or emacs. Tutorials on the use of vi and emacs are available on the web and
if you do chose to learn one of these text editors (or some other editor) please feel free to use it for class
assignments.
To begin using pico simply type:
pico filename
at the Unix prompt. This will begin pico and either open an existing file or create a new file depending on
whether the given file already exists. Once the file is open you can begin typing.
Note that pico does not use the mouse. You use the arrow keys to move the cursor.
Once pico is started you will see the file’s current contents (possibly nothing if it is a new file) plus two
rows of commands listed across the bottom of the screen. For example the first command listed is ^G Get
Help. The ^ symbol stands for the control key in pico. So, to ‘get help’ hold down the control key (labeled
Ctrl on the keyboard) and simultaneously press the g key (capitals not required). This opens a help page.
You can exit the help page by pressing ctrl and x simultaneous as noted at the bottom of the help page or
you can skip to the next page of help by pressing ctrl and v simultaneously.
Other common and useful commands are:
^o (press ctrl and o simultaneously) this saves your current work. Pico will ask for a filename (all of
pico’s prompts are at the bottom of the screen) and prompt you with the current filename. So, if you just
hit enter the file is saved under the current name.
^x exits pico. If you haven’t saved the latest changes to the file pico will ask you whether you want to
save, and if so what file name to use, just as with ^o.
^k cuts the current line of text deleting from the file.
^u uncuts the last set of cut lines restoring them to the file at the cursors current location. ^k and ^u
can be used as a primitive cut and paste to move one or more lines of text.
^w allows you to search for a string. After typing ^w (remember that’s ctrl and w simultaneously) pico
will ask for the string you want to search for. Again the prompt for the search string appears at the bottom
of the screen.
^c tells you the current line number. Useful for finding errors in your code.
^v jumps down a whole page. Useful for navigating large documents.
^y jumps up a whole page.

40
B The Curses Library
The curses library defines a number of functions that give you much more precise control over displaying
characters on the screen. The name curses was apparently derived from the term cursor by someone with a
crude sense of humor. (There is also a newer version of the library called ncurses, short for new curses, that
is available, but it is not currently installed.)
To use the curses library you need to include it:
#include<curses.h>
and explicitly link it during the compiling process. Common libraries are linked automatically, but because
the curses library is not always used it has to be explicitly linked. So, to compile a program that includes
the curses library type:
g++ -lcurses programname
There are a few basic commands that are a part of any program using the curses library:

• WINDOW *variable name; - This declares a variable of type ‘window’. For now ignore the *.
• variable name = initscr(); - This connects the WINDOW variable to the screen
• clear(); - This function clears the screen.
• refresh(); - This function redraws the screen. It must be used every time you want something new to
be displayed.
• endwin(); - This function ends curses control of the screen. It should go at the end of the program. If
you forget to include it at the end of the program, when the program exits the screen will no longer
respond properly and you will have to exit and log back on to the system.

Note how each of these commands is used in the sample program given below.
There are three simple commands that help you place and remove characters from the screen:
• move(r,c); - This function moves the cursor to row r and column c. r and c can be integers, integer
variables or integer expressions.
• insch(ch); - This function places the character ch at the cursor’s current position. ch can be a chracter
such as ‘R’ or a character variable.
• delch(); - This function deletes the character at the cursor’s current location.
For example, the code:
int row = 5, column = 10;
move(row,column);
delch();
insch(’X’);
refresh();
removes whatever character is currently at row 5 and column 10 and replaces it with the character X.
Note that without the refresh command the changes wouldn’t show up on the screen. Also note that the
preliminary commands (defining a variable of type WINDOW, initializing the screen, etc.) would need to
be done first.

41

You might also like