The C and C++ language include the standard set of repetition control statements: for loops, while loops, and do-while loops (called repeat-until loops in several other high-level languages). You may be surprised, however, by the ways a program can leave a repetition loop. C and C++ provide four methods for altering the repetitions in a loop. All repetition loops can naturally terminate based on the expressed test condition. In C and C++, however, a repetition loop can also terminate because of an anticipated error condition by using either a break or exit statement. Repetition loops can also have their logic control flow altered by a break statement or a continue statement.
The basic difference between a for loop and a while or do-while loop has to do with the “known” number of repetitions. Typically, for loops are used whenever there is a definite predefined required number of repetitions, and while and do-while loops are reserved for an “unknown” number of repetitions.
When the for loop statement is encountered, theinitialization_exp is executed first. This is done at the start of the loop, and it is never executed again. Usually, this statement involves the initialization of the loop controlvariable. Following this, test_exp, which is called the loop terminating condition, is tested. Whenever test_exp evaluates to TRUE, the statement or statements within the loop are executed. If the loop was entered, then after all of the statements within the loop are executed, increment_exp is executed. However, if test_exp evaluates to FALSE, the statement or statements within the loop are ignored, along with increment_exp, and execution continues with the statement following the end of the loop. The indentation scheme applied to for loops with several statementsto be repeated looks like this:
In the case where several statements need to be executed, a pair of braces is required to tie their execution to the loop control structure. Let’s examine a few examples of for loops.
The following example sums up the first five integers. It assumes that isum and ivalue have been predefined as integers:
After isum has been initialized to zero, the for loop is encountered. First,ivalue is initialized to 1 (this is done only once); second, ivalue‘s value is checked against the loop terminating condition, <= 5. Since this is TRUE, a 1 is added to isum. Once the statement is executed, the loop control variable (ivalue) is incremented by 1. This process continues four more times until ivalue is incremented to 6 and the loop terminates.
In C++, the same code segment could be written as follows. See if you can detect the subtle difference:
C++ allows the loop control variable to be declared and initialized within the for loop. This brings up a very sensitive issue among structured programmers, which is the proper placement of variable declarations. In C++, you can declare variables right before the statement that actually uses them. In the preceding example, sinceivalue is used only to generate an isum, with isum having a larger scope than ivalue, the local declaration for ivalue is harmless. However, look at the following code segment:
This would obscure the visual “desk check” of the variable isum because it was not declared below the function head. For the sake of structured design and debugging, it is best to localize all variable declarations. It is the rare code segment that can justify the usefulness of moving a variable declaration to a nonstandard place, in sacrifice of easily read, easily checked, and easily modified code.
The value used to increment for loop control variables does not always have to be 1 or ++. The following example sums all the odd numbers up to 9:
In this example, the loop control variable iodd_value is initialized to 1 and is incremented by 2.
Another unique feature is that for loops don’t always have to go from a smaller value to a larger one. The following example uses a for loop to read into an array of characters and then print the character string backward:
// // forlp.cpp // A C++ program that uses a for loop to input a character array // Copyright (c) Chris H. Pappas and William H. Murray, 1998 //
#include <stdio.h>
#define CARRAY_SIZE 10
int main( ) { int ioffset; char carray[CARRAY_SIZE];
In this application, the first for loop initializedioffset to zero (necessary since all array indexes are offsets from the starting address of the first array element), and while there is room in carray, reads characters in one at a time. The second for loop initializes the loop control variableioffsetto the offset of the last element in the array and, while ioffset contains a valid offset, prints the characters in reverse order. This process could be used to parse an infix expression that was being converted to prefix notation.
When combining or nesting for loops, as in the next example, take care to include the appropriate braces to make certain the statements execute properly:
/* * nslop1.c * A C program demonstrating * the need for caution when nesting for loops. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
However, suppose the outer for loop had been written without the braces, like this:
/* * nslop2.c * A C program demonstrating what happens when you nest * for loops without the logically required braces {}. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
Without the braces surrounding the first for loop, only the first printf( ) statement is associated with the loop. Once the printf( ) statement is executed four times, the second for loop is entered.The inner loop uses the last value stored iniouter_val, or 5, to generate the values printed by its printf( ) statement.
The need to include or not include braces can be a tricky matter at best that needs to be approached with some thought to readability. Look at the next two examples and see if you can figure out if they would produce the same output.
Here is the first application:
/* * lpdmo1.c * Another C program demonstrating the need * for caution when nesting for loops. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
Compare the preceding program with the following example:
/* * lpdmo2.c * A comparison C program demonstrating the need * for caution when nesting for loops. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
In these last two examples, the only statement associated with the outer for loop is the inner for loop. The inner for loop is considered a single statement. This would still be the case even if the inner for loop had multiple statements to execute. Since braces are needed only around code blocks or multiple statements, the outer for loop does not need braces to execute the program properly.
while
The C and C++ while loop is apretest loop just like the for loop. This means that the program evaluates test_exp before entering the statement or statements within the body of the loop. Because of this, pretest loops may be executed from zero to many times. The syntax for a C while loop is
while(test_exp) statement;
For while loops with several statements, braces are needed:
Typically, while loop control structures are used whenever an indefinite number of repetitions is expected. The following C program uses a while loop to control the number of timesivalueis shifted to the right. The program prints the binary representation of a signed integer.
/* * while.c * A C program using a pretest while loop with flag * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
#include <stdio.h>
#define WORD 16 #define ONE_BYTE 8
int main( ) { int ivalue = 256, ibit_position=1; unsigned int umask = 1;
printf(“The following value %d,\n”,ivalue); printf(“in binary form looks like: ”);
This application begins by defining two constants, WORD and ONE_BYTE, that can be easily modified for different architectures. WORD will be used as a flag to determine when the while loop will terminate. Within the while loop,ivalue is shifted, compared toumask, and printed from most significant bit to least. This allows the algorithm to use a simple printf( ) statement to output the results.
In the next example, the application prompts the user for an input filename and an output filename. The program then uses a while loop to read in and echo print the input file of unknown size.
/* * dowhile.c * A C program using a while loop to echo print a file * The program demonstrates additional file I/O techniques * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
Here, the while loop contains two executable statements, so the brace pair is required. The program also illustrates the use of several file I/O statements like fgetc( ) and fputc( ), along with feof( ) (discussed in Chapter 11).
do-while
The do-while loop differs from the for and while loops. The do-while loop is apost-test loop. In other words, the loop is always entered at least once, with the loop condition being tested at the end of the first iteration. In contrast, for loops and while loops may execute from zero to many times, depending on the loop control variable. Since do-while loops always execute at least one time, they are best used whenever there is no doubt you want the particular loop entered. For example, if your program needs to present a menu to the user, even if all the user wants to do is immediately quit the program, he or she needs to see the menu to know which key terminates the application.
The syntax for a do-while loop is
do action; while(test_condition);
Braces are required for do-while statements that have compound actions:
do { action1; action2; action3; actionn; } while(test_condition);
The following application uses a do-while loop to calculate some statistics for a user-entered sentence:
// // dowhile.cpp // A C++ program demonstrating the usefulness of a // do-while loop to process user-defined sentence. // Copyright (c) Chris H. Pappas and William H. Murray, 1998 //
#include <iostream.h>
#define LENGTH 80 #define NULL_TERM 1
int main( ) { const int LENGTH 80; char cSentence[LENGTH + NULL_TERM]; int iNumChars = 0, iNumWords = 1;
do { cout << “Please enter your sentence : ”; cin.getline(cSentence,LENGTH); } while (cSentence[0] == ‘\0’;
while (cSentence[iNumChars] != ‘\0’) { if (cSentence[iNumChars] == ‘ ‘) iNumWords++; iNumChars++; }
The do-while loop in this program repeats the prompt and input statements for the user-requested sentence until the user enters at least one character. Simply pressing the enter key causes the getline( ) function to store the null character in the first array element position, repeating the loop. Once the user enters the sentence, the program jumps out of the do-while loop printing the calculated statistics.
break
The break statement can be used to exit a loop before the test condition becomes FALSE. The break statement is similar in many ways to a goto statement, only the point jumped to is not known directly. When breaking out of a loop, program execution continues with the next statement following the loop itself. Look at this simple application:
/* * break.c * A C program demonstrating the use of the break statement. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
Use the integrated debugger to trace through the preceding program. Trace the variables isum and itimes. Pay particular attention to which statements are executed after isum reaches the value 21. What you should have noticed is that when isum reached the value 21, the break statement was executed. This caused the increment ofitimes to be jumped over, itimes++, with program execution continuing on the line of code below the loop. In this example, the next statement executed was the return.
continue
There is a subtle difference between the break statement and the continue statement. As you have already seen from the last example program, break causes the loop to terminate execution altogether. In contrast, the continue statement causes all of the statements following the continue statement to be ignored but doesnotcircumvent incrementing the loop control variable or the loop control test condition. In other words, if the loop control variable still satisfies the loop test condition, the loop will continue to iterate.
The following program demonstrates this concept, using a number guessing game:
/* * contnu.c * A C program demonstrating the use of the continue * statement. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
#include <stdio.h>
#define TRUE 1 #define FALSE 0
int main( ) { int ilucky_number=77, iinput_val, inumber_of_tries=0, iam_lucky=FALSE;
while(!iam_lucky){ printf(“Please enter your lucky guess: ”); scanf(“%d”,&iinput_val); inumber_of_tries++; if(iinput_val == ilucky_number) iam_lucky=TRUE; else continue; printf(“It only took you %d tries to get lucky!”, inumber_of_tries); }
return(0); }
Debugger Trace
As an exercise, enter the preceding program and trace the variables iinput_val, inumber_of_tries, and iam_lucky. Pay particular attention to which statements are executed after iinput_val is compared to ilucky_number.
The program uses a while loop to prompt the user for a value, increments theinumber_of_tries for each guess entered, and then determines the appropriate action to take based on the success of the match. If no match was found, the else statement is executed. This is the continue statement. Whenever the continue statement is executed, the printf( ) statement is ignored. Note, however, that the loop continues to execute. Wheniinput_val matches ilucky_number, the iam_lucky flag is set to TRUE and the continue statement is ignored, allowing the printf( ) statement to execute.
Combining break and continue
The break and continue statements can be combined to solve some interesting program problems. Look at the following C++ example:
// // bracntg.cpp // A C++ program demonstrating the usefulness of combining // the break and continue statements. // Copyright (c) Chris H. Pappas and William H. Murray, 1998 //
Before seeing how the program functions, take a look at the input to the program:
word control ^B exclamation! apostrophe’ period. ^Z
Also examine the output produced:
word control B exclamation
apostrophe
period
This application continues to read character input until the EOF character ^Z is typed. It then examines the input, removing any nonprintable characters, and places each “word” on its own line. It accomplishes all of this by using some very interesting functions defined in CTYPE.H, including isascii( ), ispunct( ), isspace( ), andisprint( ). Each of the functions is passed a character parameter and returns either a zero or some other value indicating the result of the comparison.
The function isascii( ) indicates whether the character passed falls into the acceptable ASCII value range,ispunct( ) indicates whether the character is a punctuation mark,isspace( ) indicates whether the character is a space, and functionisprint( ) reports whether the character parameter is a printable character.
By using these functions, the application determines whether to continue the program at all and, if it is to continue, what it should do with each of the characters input.
The first test within the while loop evaluates whether the file is even in readable form. For example, the input data could have been saved in binary format, rendering the program useless. If this is the case, the associated if statements are executed, printing a warning message and breaking out of the while loop permanently.
If no errors are encountered, the second if statement is encountered; it checks whether the character input is either a punctuation mark or a blank space. If either of these conditions is TRUE, the associated if statements are executed. This causes a blank line to be skipped in the output and executes the continue statement. The continue statement efficiently jumps over the remaining test condition and output statement but does not terminate the loop. It merely indicates that the character’s form has been diagnosed properly and that it is time to obtain a new character.
The third if statement asks whether the character is printable or not, but only if the file is in an acceptable format and the character input is not punctuation or a blank. This test takes care of any control codes. Notice that the example input to the program included a control ^B. Since ^B is not printable, this if statement immediately obtains a new character and then executes a continue statement. In like manner, this continue statement indicates that the character in question has been diagnosed, the proper action has been taken, and it is time to get another character. The continue statement also causes the putchar( ) statement to be ignored while not terminating the while loop.
exit( )
Under some circumstances, it is possible for a program to terminate long before all of the statements in the program have been examined and/or executed. For these specific circumstances, C incorporates the exit( ) library function. The functionexit( ) expects one integer argument, called astatus value. The UNIX and MS-DOS operating systems interpret a status value of zero as signaling a normal program termination, while any nonzero status values signify different kinds of errors.
The particular status value passed to exit( ) can be used by the process that invoked the program to take some action. For example, if the program were invoked from the command line and the status value indicated some type of error, the operating system might display a message. In addition to terminating the program, exit( ) writes all output waiting to be written and closes all open files.
The following application averages a list of up to 30 grades. The program will exit if the user requests to average more than SIZE number of integers.
// // exit1.cpp // A C++ program demonstrating the use of the exit function // Copyright (c) Chris H. Pappas and William H. Murray, 1998 //
#include <iostream.h> #include <process.h>
#define LIMIT 30
int main( ) { int irow,irequested_qty,iscores[LIMIT]; float fsum=0,imax_score=0,imin_score=100,faverage;
cout << “\nEnter the number of scores to be averaged: ”; cin >> irequested_qty; if(irequested_qty > LIMIT) { cout << “\nYou can only enter up to ” << LIMIT << \ “ scores” << “ to be averaged.\n”; cout << “\n >>> Program was exited. <<<\n”; exit; }
cout << “\nThe maximum grade is ” << imax_score; cout << “\nThe minimum grade is ” << imin_score; cout << “\nThe average grade is ” << faverage;
return(0); }
The application starts by including the PROCESS.H header file. Either PROCESS.H or STDLIB.H can be included to prototype the function exit( ). The constantLIMIT is declared to be 30 and is used to dimension the array of integers, iscores. After the remaining variables are declared, the program prompts the user for the number of iscores to be entered. For this program, the user’s response is to be typed next to the prompt.
The application inputs the requested value into the variable irequested_qty and uses this for the if comparison. When the user wants to average more numbers than will fit iniscores, the two warning messages are printed and then the exit( ) statement is executed. This terminates the program altogether.
Examine the following listing and see if you can detect the two subtle differences between the preceding program and this one:
// // exit2.cpp // A C++ program demonstrating the use of the exit function // in relation to the difference between the process.h // and stdlib.h header files. // Copyright (c) Chris H. Pappas and William H. Murray, 1998 //
#include <iostream.h> #include <stdlib.h>
#define LIMIT 30
int main( ) { int irow,irequested_qty,iscores[LIMIT]; float fsum=0,imax_score=0,imin_score=100,faverage;
cout << “\nEnter the number of scores to be averaged: ”; cin >> irequested_qty; if(irequested_qty > LIMIT) { cout << “\nYou can only enter up to ” << LIMIT << \ “ scores” << “ to be faveraged.\n”; cout << “\n >>> Program was exited. <<<\n”; exit(EXIT_FAILURE); }
cout << “\nThe maximum grade is ” << imax_score; cout << “\nThe minimum grade is ” << imin_score; cout << “\nThe average grade is ” << faverage;
return(0); }
Here, by the inclusion of the STDLIB.H header file instead of PROCESS.H, two additional definitions became visible: EXIT_SUCCESS (which returns a value of zero) andEXIT_FAILURE (which returns an unsuccessful value). This program used theEXIT_SUCCESS definition for a more readable parameter to the functionexit( ).
atexit( )
Whenever a program invokes theexit( ) function or performs a normal program termination, it can also call any registered “exit functions” posted withatexit( ). The following C program demonstrates this capability:
/* * atexit.c * A C program demonstrating the relationship between the * function atexit and the order in which the functions * declared are executed. * Copyright (c) Chris H. Pappas and William H. Murray, 1998 */
The atexit( ) function uses the name of a function as its only parameter and registers the specified function as an exit function. Whenever the program terminates normally, as in the preceding example, or invokes theexit( ) function, allatexit( ) declared functions are executed.
Technically, each time the atexit( ) statement is encountered in the source code, the specified function is added to a list of functions to execute when the program terminates. When the program terminates, any functions that have been passed toatexit( ) are executed, with thelast function added being the first one executed. This explains why the atexit_fn3 output statement was printed before the similar statement in atexit_fn1. atexit( ) functions are normally used as cleanup routines for dynamically allocated objects. Since one object (B) can be built upon another (A),atexit( ) functions execute in reverse order. This would delete object B before deleting object A.