Key syntax elements for lab 4
The C++ feature we will focus on for lab 4 covers pass-by-reference, the bool datatype,
loops, and random number generation. (Refer back to the course notes or to the
lab 3 syntax examples for refreshers
on the syntax used earlier.)
Pass-by-reference
Remember that regular parameter passing simply passes a copy of a value,
whereas pass-by-reference allows the called function to access/change the
original variable passed to it.
This is done by adding the & symbol before the parameter name in the
formal parameter list (in the prototype and declaration of the function).
For example, in the program below the negate function switches
the passed parameter between positive and negative.
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
void negate(float &orig);
int main()
{
int x = 3;
negate(x);
// x has now been changed to -3
cout << x << endl;
}
void negate(float &orig)
{
// replace the original value with the opposite
orig = - orig;
}
|
The boolean datatype
Here we introduce a new datatype, bool, that can be used to hold true/false values, e.g.
bool subtitlesOn; // will set to true if user wants subtitles, false otherwise
// use the variable to remember if a particular condition evaluated to true or false
char userChoice;
cout << "Do you wish to have subtitles turned on? Enter Y for yes or N for no: ";
cin >> userChoice;
if (userChoice == 'Y') {
subtitlesOn = true;
} else {
subtitlesOn = false;
}
We can test if the variable currently holds value 'true' using if (thevariablename) or
we can test if it holds value false using if (!thevariablename), e.g.:
if (subtitlesOn) {
// .... code here to turn on subtitles ...
} else {
// .... code here to turn off subtitles ...
}
Functions can have bool as a return type, e.g.
// return true if min < x and x < max
bool isXbetween(int x, int min, int max)
{
if (x <= min) { // too small
return false;
} else if (x >= max) { // too big
return false;
} else {
return true; // x is in between min and max
}
}
Loops
The other C++ syntax we will focus on covers while loops,
do-while loops, and for loops.
- while loops
these are top tested loops, and must include appropriate
instructions inside the loop to update the condition being checked
// print the values from 10 through 20, one per line
int x = 10;
while (x <= 20) // enter the body of the loop if x is less than or equal to 20, otherwise
{ // skip the body of the loop and go on with the rest of the program
cout << x << endl; // do the loop actions (in this case just a single output statement)
x++; // update x for the next potential pass through the loop
} // upon reaching the bottom of the loop, go back up to the test condition
// rest of the program would be down here
|
- do while loops
these are bottom tested loops, so always run the body of the loop at least once
(since you don't encounter the test condition until after the first pass through the loop)
int x = 10;
do
{
cout << x << endl;
x++;
} while (x <= 20); // if x is <= 20 go back and do the loop body again,
// otherwise leave the loop now
|
- for loops
for loops are top tested, but allow you to initiaze a loop counter
as part of the loop header, as well as the counter update statement
for loop | equivalent while loop |
for (int x = 1; x < 10; x++)
{
cout << x << endl;
}
|
int x = 1;
while (x < 10)
{
cout << x << endl;
x++;
}
|
Note the condition is checked at the top of the for loop, and the update
is applied at the bottom.
- nested loops
If we put one loop inside another, each time we go through the "outer" loop
once we might go through the "inner" loop many times. For example, in the code
below the outer loop controls which row we're working on, the inner loop controls
which column we're printing in the current row:
code | output |
for (int row = 1; row < 8; row++)
{
cout << "Row " << row << ":";
for (int column = 1; column < 5; column++) {
cout << column;
}
cout << endl;;
}
|
Row 1: 1234
Row 2: 1234
Row 3: 1234
Row 4: 1234
Row 5: 1234
Row 6: 1234
Row 7: 1234
|
Some notes on characters and ASCII
- Each char is represented by a unique integer, from 0 to 127. (If you're
interested, a quick search
for ASCII tables on any search engine will show you which values are associated with
which specific characters.)
When we want to, we can treat chars like ints to work with their ascii value,
though it's safest to explicitly tell the compiler that we want to
convert between the data types,
e.g.
char ch = 'Q';
int x = (int)(ch); // get integer (ascii) value of this char
cout << "The ascii value for " << ch << " is " << x; // Q is 81
x = x - 10;
ch = (char)(x); // get char equivalent of this integer value
cout << "The char with ascii value " << x << " is " << ch; // 71 is G
|
Pseudo-random number generation
There are many different pseudo-random number generators out there, with varying
properties in terms of how well they emulate true randomness. The one we'll
use for now is a simple one called rand, found in the cstdlib library.
The rand function returns a random integer in the range 0..LONG_MAX,
so we need to do a little manipulation if we want to generate values
in a specific range:
- For a real number in the range 0..1
// take rand's result (as a float) and divide it by the biggest possible rand value
float r;
r =
- For an integer in the range 0..N
// take the remainder after dividing rand's result by N+1
long r;
r = rand() % (N+1);
- For an integer in the range M..N
// generate a random value in the range 0..(1+N-M) then add M
long r;
r = M + (rand() % (1+N-M));
Special note: initializing (seeding) the random number generator
Because random number generators are just computer programs, they actually have
fixed behaviour. If they were run over and over, by themselves, they would always
produce the same sequence of values (so from run to run it wouldn't seem very random).
To get around this, each time we start a program that uses a random number generator
we initialize, or seed, the generator with a difficult-to-predict value, which the
generator then uses as the basis for that run's generation sequence.
One possible seed value is the value returned by the time function from the ctime library.
(Trivia: this function returns the number of seconds since the start of Jan. 1st 1970.)
Thus a simple program to generate 50 random numbers in the range 1-100 might look like:
#include <iostream>
#include <cstdlib>
#include <ctime>
int main()
{
// seed the rng with the current time, just needs to be done once
srand(time(NULL));
// generate the 50 random numbers in the range 1..100
for (int i = 0; i < 50; i++) {
long r = 1 + (rand() % 100);
cout << r << endl;
}
return 0;
}
One weakness with using time(NULL) as the seed is that the time is measured in seconds,
so if you run the program twice in the same second then it will use the same seed both times,
and hence the same RNG sequence both times.