Control Structures

Aims

To enable students to:

  1. use relational operators

  2. understand logical operators

  3. make decisions

  4. write loops

  5. scope variables

Use relational operators

Relational and logical operators are all binary operations that evaluate two operands, producing a result of TRUE (non-zero) or FALSE (zero).

C++ supports six relational operators which permit the comparison of two values; < (less than), <= (less than or equal to), == (equal to), != (not equal to), >= (greater than or equal to) and > (greater than).

The following code will display the result of testing to see if two numbers are equal:

cout << (1 == 1);

In this case they are and a 1 will appear on screen. Changing the code to

cout << (1 == 0);

results in a 0 being shown.

The relational operators will happily compare variables of different types, for example a float can be compared to an int with the correct results (i.e. if the float is 1.0 and the int is 1 an equality test will produce an answer of TRUE).

Relational operators can not be used to compare arrays, instead it is necessary to compare each individual element of the array in turn.

Be careful when using the equality operator ==. It is a common mistake to use the assignment operator = in its place, this will also yield an answer of TRUE or FALSE, but this time based on whether the computer successfully assigned a new value to the variable (the purpose of =) - normally the result will be TRUE.

Understand logical operators

C++ provides three logical operators && (and), || (or) and ! (not). As with relational operators, logical operators return TRUE or FALSE depending on the result of the operation. The && and || are binary operators (i.e. take two operands), while the ! takes only one. In all cases the operands can be very simple (such as the contents of a variable), or complex (the result of other logical or relational operations).

The && operator will return TRUE only if both operands are TRUE. For example

cout << (1 && 1);

writes 1 to the screen, while

cout << (1 && 0);

writes 0 to the screen, and

int iSmall = 5;

int iBig = 9;

cout << (1 && (iSmall <= iBig));

writes 1 to the screen.

The || operator returns TRUE if either of the operands are TRUE. In the above examples, if && were replaced by || all would display 1 on the screen.

The ! operator only returns TRUE if its operand is FALSE - i.e. it inverts the state of the operand. For example,

cout << !(1 && 1);

writes 0 to the screen, while

cout << !(1 && 0);

writes 1 to the screen, and

int iSmall = 5;

int iBig = 9;

cout << !(1 && (iSmall <= iBig));

writes 0 to the screen.

Make decisions

In order for a program to do anything really useful it needs to be able to make decisions. A system controlling a power station needs to be able to detect the temperature of the boilers and if they go over a pre-set value turn off the gas supply. To achieve this C++ provides the if keyword which protects a block of code from being executed unless some entry expression is TRUE.

For example,

if (1)
{
    cout << "Here!\n";
}

writes Here! to the screen, but

int iSmall = 5;

int iBig = 9;

if (!(1 && (iSmall <= iBig)))
{
    cout << "Here!\n");  
}

does nothing, while

if (1 == 1)
{
    cout << "Here!\n");
}

writes Here! to the screen.

The if key word can test any expression that produces a zero (FALSE) or non-zero (TRUE) value - essentially anything!

Frequently a program will need to perform some other action if the test expression failed, this is achieved via the else keyword as in:

int iSmall = 5;

int iBig;

cin >> iBig;

if (iBig > iSmall)  
{  
    cout << "Your number is too big.\n";  
}  
else  
{  
    cout << "Your number is just right.\n";  
}

The above code takes a number from the user, if it is bigger than 5 they get the message "Your number is too big.", otherwise their told "Your number is just right."

It is permissible for the block of code executed by the else statement to contain another if statement, as in:

int iSmall = 5;

int iBig;

cin >> iBig;

if (iBig > iSmall)  
{  
    cout << "Your number is too big.\n";  
}  
else if (iBig == iSmall)  
{  
    cout << "Your number is just right.\n";  
} 
else  
{  
    cout << "Your number is too small.\n";  
}

Now, the program will only display "Your number is just right." if the user entered 5.

For a program that needs to take many different actions depending on the outcome of a expression the if...else construct can become unwieldy. To meet this need use the switch statement.

The switch statement executes one or more of a series of code blocks depending on the integer value obtained from executing a test condition, for example:

char cUserResponce;

cin >> cUserResponce;

switch (cUserResponce)  
{  
    case 'a':  
        cout << "Hello\n";  
        break;

    case 'b':      
    case 'c':
          cout << "Goodbyean";
          break;

    default:
          cout << "Please wait\n";      
}

The above code displays Hello if the user enters an a, Goodbye on b or c and Please wait in all other cases. The program begins executing code at the case statement, matching the integer value being switched on (in this case a character variable, such as a), execution continues until the end of the switch block is reached or a break statement is encountered.

The default case is optional, if present it is executed for any values that have no matching case statement.

Write loops

Computers are capable of executing tirelessly for indefinite periods of time. The example code presented so far does no utilise this ability - they execute once and come to an end. To make programs repeatedly execute a block of code it is necessary to make use of a program construct termed a loop. C++ supports three types of loop - while, for and do.

The while loop is constructed as follows:

while (_expression_)

    _statement_;

_next statement_;

At the start of the loop the expression is evaluated and if found to be TRUE the statement is executed before control is returned back to the start of the loop. If the expression is found to be FALSE control is passed on to the next statement. It is perfectly possible for the statement within the loop to never be executed if the expression evaluates to FALSE on the first test. The following code will repeatedly ask the user for a character until they end 'n'.

char cInput = 'y';

while (cInput != 'n')
{  
    cout << "Enter a charactern";

    cin >> cInput;  
}

The for loop is constructed as follows:

for (_expression1_; _expression2_; _expression3_)

    _statement_;

_next statement_;

which is equivalent to:

_expression1_;

while (_expression2_)  
{  
    _statement_;

    _expression3_;  
}

_next statement_;

First expression1 is evaluated, typically this is used to initialise the loop. Then expression2 is evaluated, if it is TRUE statement is executed before expression3 is evaluated and control passed back to the start of the loop, otherwise control is passed on to next statement. The for loop is typically used to execute a section of code a fixed number of times, such as

for (int iCounter = 24; iCounter > 0; iCounter--)  
{   
    cout << "n";  
}

which will clear the screen by printing 24 new lines. Many compilers provide routines in their libraries that will clear the screen for you, but as this is not a standard feature of C++ you may prefer to use something like this example to ensure your source code is portable across computer platforms. As with all loops it is possible to get the for loop to execute a section of code infinitely, as the following demonstrates:

for ( ; ; )  
{
    cout << "Infinityn";  
}

As with the while loop, the for loop executes statement zero or more times, depending on the result of evaluating expression2.

The do statement is a variant of the while statement that makes its test at the bottom of the loop, not the top. It is constructed as follows:

do

    _statement_;

while (_expression_);

_next statement_;

The following code forces the user to enter a positive integer before continuing on with the rest of the program:

int iNumber;

int iError;

do
{  
    cout << "Input a positive integer: ";

    cin >> iNumber;

    if (iError = (iNumber <= 0))      
    {      
        cout << "nERROR: Try again!nn";      
    }  
} while (iError);

Scope variables

Two forms of variables exist, global and local. Global locals are declared outside of a function, typically at the top of a source file. These variables are available to all functions within the source file, for example in the code below will result in 2 being written to the screen:

int iGlobal;

void vModifyGlobal (void)  
{  
    iGlobal++;  
}

void main (void) 
{
    iGlobal = 1;

    vModifyGlobal ();

    cout << iGlobal;  
}

The other form of variable is termed local. This is only available to the block of code in which it is defined. Typically this is a function, as in the following code where the value 1 is displayed on the screen:

void vManipulate (void)
{
    int iLocal = 10;

    iLocal ++;  
}

void main (void)
{
    int iLocal = 0;

    vManipulate ();

    cout << iLocal;  
}

Local variables can have more restricted levels of definition. The following code will display 10 0 despite the same variable name being used in the two cout statements. This is because the two instances of cout are in different blocks of code (delimited by {}), each with their own local copies of iLocal.

void main ()
{
    int iLocal = 0;

    if (iLocal == 0)      
    {      
        int iLocal = 10;

        cout << iLocal << " ";      
    }

    cout << iLocal;  
}

The two local variables of iLocal can be independently manipulated, changes to the copy defined in the inner code block have no effect on that belonging to the outer.

Although global variables can be extremely useful, enabling values to be passed easily between functions, they are also very dangerous - in a large file it can be difficult to track down all the places where a global variable is used so it can be difficult to predict the changes to its value made by one function may have on others. Instead of using global variables it is safer to pass the values of local variables between functions via their parameters.

For example, the first example given in this section can be re-written as:

void vModify (int *ptrValue)  
{
    *ptrValue = *ptrValue + 1;  
}

void main (void)
{
    int iLocal = 1;

    vModify (&iLocal);

    cout << iLocal;  
}

This will result in the value 2 being written tot he screen. The main function passes a pointer to the local variable iLocal to the vModify function. This function takes the pointer, de-references it via the * operator and adds one to the integer at that location. When control is passed back to the main function the local variable now has the value 2.

Download