You are on page 1of 35

Chapter XV

Program Design

Chapter XV Topics
15.1 Introduction

15.2 Steps of Program Development

15.3 Understanding the Problem

15.4 Creating an Algorithm

15.5 Coding the Algorithm

15.6 Testing and Debugging the Program

15.7 Updating and Enhancement

15.8 The Need to Compromise

15.1 Introduction

Chapter XV Program Design 15.1


Chapter VIII introduced program modularity and set the stage for proper program
development. We will now devote an entire chapter to the steps of program design.
Most textbooks start with program design in chapter one. Bad habits are to be
avoided at all costs, and the student needs to be designing correct programs from day
one. This idea sounds good in theory, but experience has shown that students have
little interest in top-down design, step-wise refinements, modular programming
and other equally foreign sounding phrases in the beginning.

Who cares about step-wise refinement when you have no idea how to use a text
editor, save a program, or for that matter boot the computer. I have vivid memories
of attending a lecture on proper program design. It was the first lecture of the first
computer science course I took back in the Seventies. At the end of the lecture a
program assignment was handed to the students, to be turned in one week later. Not
one detail was given about using the computer. In other words, students marched to
the computer lab, their heads buzzing with program design terminology, and not a
clue how to log-on to the mainframe computer, or even write a program that would
compute the sum of two numbers.

This book has taken those experiences into account. Now that you know enough
about computer hardware and software, and you have a foundation knowledge of
one particular programming language, you are ready for some firm guidance in
proper program design.

Why Bother With Program Design?

With the popularity and availability of micro computers it is not unusual for students
to start any program by planting themselves behind a computer and hope that the
fingers on the keyboard will be manipulated by cosmic guidance... or at least the
guidance of a fellow student of your teacher.

Many times, neither a fellow student nor the teacher is available and cosmic
guidance has not always proven reliable with computer assignments. There comes a
time when the burden of solving the problem falls on YOU.

Keep in mind that there is no substitute for actual practice when you wish to acquire
problem solving skills, but this chapter hopes to give some directions that may be
useful. It is suggested that even the skilled programmers who have finished every
assignment ahead of time take the time to read this chapter.

It is difficult to justify program design principles when your programs are very
small. Frankly, it matters little how you write a short program. It is precisely with

Chapter XV Program Design 15.2


the programs of the real world that attention to program design becomes extremely
significant. Real world programs involve both money and people’s lives.

In the short history of computer science sad chapters have already accumulated. A
suspension bridge in Idaho collapsed in heavy cross winds, killing many people.
The cause was traced to a faulty program used by construction engineers. Hospitals
have used sophisticated medical equipment with faulty programs that has resulted in
unnecessary injury or loss of lives to patients. At the same time correct or incorrect
programs mean huge losses or profits for the business world. The new Denver
International airport lost untold millions when the airport opened much later than
planned due to program problems in its luggage handling system. In the years
approaching 2000, programmers have been struggling to fix the headache of old
programs that cannot digest four digit years. In these old programs, both 1900 and
2000 is recorded as 00. The impact of this problem can be very profound in the
business world and companies have spent millions of dollars fixing old programs.
One of the biggest problems in fixing the old programs is poor program design that
makes the Y2K update both difficult to find and fix.

Even as a high school student you will find that proper program design principles
will make program writing simpler and quicker. Students who continue with the
second year course will especially need these principles as program assignments
grow in length and complexity.

15.2 Steps of Program Development

What are the steps of program development? Ten textbooks may list ten different
sets of steps, but most of the popular textbooks will likely adhere closely to
statements that sound like the five steps listed below.

These steps will probably surprise you. The average student has the tendency to
start with step 3 and often wonders why the program has so many problems. Check
out these steps. Where have you been starting with your program assignments?

Steps of Program Development

1. Understand the problem

Chapter XV Program Design 15.3


2. Create an Algorithm
3. Code the Algorithm
4. Test and Debug the Program
5. Update and Enhance the Program

Let us be honest now. How often did you sit behind the computer and start typing in
the program immediately? Step 1 certainly gets skipped a lot. Are you one of
many students who does not even bother to read the specifications of the program
assignment? Well, do not feel alone. Many students are too impatient to read
specifications, even if it saves time on the long run. With this chapter let us start
writing programs correctly. We will start by explaining each step briefly and then
return and looks at the program development steps in considerable detail.

Understand the Problem

The first step in computer program development is ignored more than anything else.
Can a program expert with twenty years computer science experience write a
program that will play chess, unless such a person understands the game of chess?
Can somebody write a program which will multiply two matrices unless he or she
knows how to multiply matrices? You know the answer to these questions.
Therefore, before anything else happens, you need to understand the problem. This
is particularly important, since the real situation is often such that problem
originators are usually not programmers.

Create an Algorithm

An algorithm is a step-by-step solution to a given problem. Of the five steps, this


will often be the most difficult and time-consuming step, when program assignments
start becoming complex. Entire textbooks are devoted to important algorithms.
There will be several future chapters devoted entirely to several popular computer
algorithms.

Code the Algorithm

At this stage you need to determine the programming language that you will use.
C++ may not always be the best solution. As you become a more experienced
programmer, you will be able to select from different languages for different

Chapter XV Program Design 15.4


applications. If the algorithm is well designed, it should comfortably translate into
most programming languages. Furthermore, keep in mind that coding a program is
not just accepted because it works. Serious considerations need to be given to
efficiency in execution time, in memory usage, and in program readability.

Test and Debug the Program


After a short introduction in C++, you have learned to become dependent upon the
compiler to point out errors. Keep in mind that the compiler is only equipped to
detect syntax errors. Both RUNTIME and LOGIC errors can only be detected
when the program is executed and tested properly.

Update and Enhance the Program

Even programs that are very well planned and developed properly require updating
and enhancing. Situations arise that were not considered in the early planning stages
and the actual process of interacting with a working copy of the program often
motivates improvement ideas.

15.3 Understand the Problem

In most cases a programmer does not originate the specifications for a program. If
he or she is a student in a computer science course, the program assignment will be
given by the class instructor, and in the business world it is usually the customer
who determines what needs to be programmed. Large programs are written by a
team of programmers who each have a specific assignment to complete. Even if you
write a program strictly for your own sake, you will need to start at this point. What
exactly is this point?

In an oversimplified manner we can say that all programs can be reduced to


digesting a given set of input that produces a desired output. Somebody may want a
payroll program. The program needs to enter a variety of data about employees.
The program should then compute the regular pay, overtime pay, gross pay,
deductions, net pay, print out paychecks, and update year-to-date files. This
program example, and most others use the following pattern:

Chapter XV Program Design 15.5


INPUT

PROCESS

OUTPUT

How do you start if you have understood the problem? This is not always as easy as
it sounds, but before you even touch a key on the computer, solve the problem
yourself. In other words, act like the computer yourself. This simple piece of
instruction works very well. Your very first step in writing the payroll program
above is not to ponder about how to write this or that program segment. First, get
out some paper, write down some typical input data and proceed to process the data
until you get the required output.

Very Important Note

If you cannot solve the problem yourself, on paper, then


you won't be able to write a program that solves the
problem on a computer

Also keep in mind that if you are stumped at the starting point, then no amount of
computer-busy-work will help you out. Arno Penzias, famous computer scientist,
astronomer, and 1959 Nobel Prize winner, makes an interesting quote about such
stumped conditions.

If you are lost, speeding up does not help.


If you have difficulty getting started, it is time to go to the program originator to get
help, or to go to some source to find out how to solve the problem. Perhaps you
have to write a program which determines the Standard Deviation for a given set of
numbers. This is wonderful, but you do not know a Standard Deviation from a hot
water bottle. In such a case, get out some reference books, or find a person familiar
with your problem.

Chapter XV Program Design 15.6


As you are gaining a better understanding of the problem you will also need to
determine any special limitations that need to be taken into account. Ignoring
special cases can result in unfortunate program crashings later on. A program which
solves second degree equations with the quadratic formula will need to handle the
cases for which there are no real solutions to the problem. Ignoring this will result in
the computer attempting to take the square root of a negative number. Most
language systems will fail in such instances.

Get Clear Program Specifications


If you feel that you have gained a solid understanding of the problem, it is time to
get program specifications.

How much data, and what type of data will be entered?


Is speed important, or is memory efficiency more important?
Must the program be very user-friendly?
What format does the output need to be in?

The questions go on. Many computer science teachers avoid students’ program
specifications dilemmas. Teachers usually pass out a detailed program assignment
handbook at the start of the course or individual specifications prior to each required
program assignment.

Getting Program Specifications

1. Program Assignment Directions


2. Sample Program Executions
3. Teacher Instructions
4. Always Use Common Sense

15.4 Creating an Algorithm

You are happy with step ONE. You know the problem. You have acquired the
program specifications, and now you are ready to get down to some serious
business. What is next? Typing in the program on the computer? One may think
that this is the logical choice, if you watch the approach that many students use with
their assignments.

Chapter XV Program Design 15.7


Now is the time to design a problem solution that will solve the problem in a logical
step-by-step approach. This stage is called Creating an Algorithm.

When you are writing short programs, this step seems totally useless. A program
which adds and subtracts two entered numbers does not require too much thought.
On the other hand if your assignment is to write a program that plays chess, then you
have a major problem on your hand. It is precisely with the larger, and more true-to-
life, kind of programs that hacking away at the computer - without much prior
thought - simply does not work. Who can write a chess program without a large
amount of planning?

If an algorithm is a step-by-step solution to a problem, is there any method to


creating algorithms? This is a sticky question because algorithms, and program
solutions in general, can be very creative. People come up with a surprising number
of different, yet correct, solutions to any given program assignment.

The first approach is not very clever, but quite effective for small programs or
procedures. It consists of neatly writing down the exact steps that are performed in
solving a given problem. In many cases whether you are averaging a set of
numbers, computing somebody's net pay, or a similar short problem, you have the
algorithm in front of you. You solved the problem. Closely observing these steps
and writing them in a logical order will often yield a workable algorithm.

However, there are problems which are totally beyond such a method. No attempt is
made here to develop a chess program, but how would you develop an algorithm for
chess anyway? Where would you start?

The commonly accepted method in the computer science community is to use the
Top-Down-Design-and-Step-Wise-Refinements approach. Top-down means to
work from the top to the bottom. From general to specific. Step-wise refinements
indicate the levels in the design that refine the problem to greater detail at every step
"down" to the next, more detailed, level.

Let us apply this top-down approach to the chess program. Keep in mind this is
over-simplified, but you should understand the general idea of building a program
design from general to specific.

Write a
Chess Program

Chapter XV Program Design 15.8


Display Move Chess Check If
Chess Board Pieces Checkmate

Display Display Decide Legal Update


Board Pieces Next Move Move? Board

King King

Queen Queen

Rooks Rook

Bishops Bishop

Knights Knight

Pawns Pawn

The entire chess program is so mind-boggling that you cannot comprehend it all
together. With a top-down design you can refine each level to a step that is
manageable, such as teaching the computer to display a knight, or check if a queen is
moved properly, or display the 64 squares of the board. The main point is that you
develop your algorithm by starting at the top in a very general manner and determine
the main modules. This is the first level of refinement. A very rough refinement,
but at each level the algorithm becomes more and more specific. The changes from
one level to the next level are relatively small. These small step-wise refinements
are easier to manage than plunging in immediately to some small detail.

You should write your C++ programs in such a manner that the main function is like
looking at the first level of your top-down design algorithm, as is shown below, with
the main function of a chess program.

// main function of CHESS.CPP

void main()
{
bool Finished;

Chapter XV Program Design 15.9


DisplayChessBoard();
do
{
GetChessMove();
MoveChessPiece();
DisplayChessBoard();
CheckIfCheckmate(Finished);
}
while (!Finished);
}

This second step will also increase your computer lab efficiency. Students do not
always have access to a computer. Not every student has a computer at home, and
students who do have a computer at home do not necessarily have every opportunity
to use it. Parents are very strange, frequently they want to use the computer they
have paid for. In such situations you can take out some paper and start thinking
about designing your program. Remember the following:

Creating an Algorithm Advise

After the program specifications are clear, make sure that you
can solve the problem yourself. Take sample input and go
through the steps required to process the desired output.

Keep in mind that an algorithm does not require the use of


programming code. The entire algorithm can be in English.

Algorithm Example

The algorithm example that follows computes the Greatest Common Factor (GCF)
of two integers. This algorithm can be used not only for writing a program, but also
for teaching a math class.

Algorithm Steps Sample Problem


Step 1:
Start with two integers Integer1 is 120
Integer2 is 108

Chapter XV Program Design 15.10


Step 2:
Divide Integer1 by Integer2 and 120 / 108 = 1
compute the remainder. The remainder = 12
Step 3:
If the remainder equals 0, you are The remainder is not 0
finished. The GCF is Integer2. You are not finished.

Step 4:
If the remainder is not 0 then Integer1 is now 108
Integer1 becomes Integer 2 and Integer2 is now 12
Integer2 becomes the remainder
Step 5:
Go to Step2:
Step 2:
Divide Integer1 by Integer2 and 108 / 12 = 9
compute the remainder. The remainder = 0
Step 3:
If the remainder equals 0, you are The remainder is 0
finished. The GCF is Integer2. You are finished and the
GCF = 12

You can take this GCF algorithm and give it to a younger brother or sister in
elementary school. It will help him or her with math homework that involves the
computation of Greatest Common Factors. There is another reason for this precise
step-by-step algorithm approach. Excuse me for saying this, but many people,
students included, think in a scattered fashion. By some brain process, which I do
not comprehend, many scattered thinkers do get desired results in their daily
routines. Unfortunately, the same approach applied to a computer program gets
scattered results. The computer lacks the subconscious intelligence that sorts out the
confusing thought process.

An Important Word About Object Oriented Programming

This chapter in general, and the top-down approach specifically, is very important in
procedural programming. Procedural programming emphasizes program design
with modules, which are implemented with functions in the C++ language. Very
soon you will be introduced to OOP (Object Oriented Programming). In OOP the
approach to program design is not exactly the same as it is in procedural
programming. This leaves authors of computer science books, which teach C++ and
OOP, with a problem. You can use the approach that students need to learn the
procedural, pre-oop programming style first. You can also plunge in and do
everything the OOP way right away or very early. There are merits in both
approaches. My biggest objection with the early OOP approach is that students
know very little about OOP before they are expected to understand program design

Chapter XV Program Design 15.11


with OOP. The problem with OOP later is that it requires some changes in program
development. I personally think it is better to go in two stages.

Program Design and OOP

The program design steps and suggestions in this chapter are


based on procedural programming principles. Object Oriented
Programming (OOP) alters some of these design principles.

Program design with OOP will be emphasized throughout the


chapters on object oriented programming.

Any discussion right now about the influence of object oriented


thinking and design will sound impressive but will probably not
be fully appreciated.

15.5 Coding the Algorithm

You will need to waste precious little time pondering which programming language
your newly designed algorithm should employ. This is a computer science book
titled, Exposure C++, and you are taking a course which is at this moment is
teaching you programming in C++.

However, the coding step does include deciding on the language to use. Little will
be said about that here. You need to gain more knowledge about the advantages and
disadvantages of various languages before you can decide on the best language for a
given application.

A confession needs to be made. So far the impression has been given that the
algorithm was created completely independent of any language choice. This is not

Chapter XV Program Design 15.12


really correct, because it is the knowledge of a specific language's capabilities that
directs a person towards one algorithm or another. The simple reality is that you
have probably already picked your language before you even looked closely at any
particular problem. There is also a real world reality for programmers who do know
multiple languages. Often they pick the language that they like best, which usually
is the language they know best. In the real world there can also be beautiful
simplicity similar to the classroom. Your team leader gives you a particular
assignment and tells you which language to use.

Coding a workable algorithm is not simply a matter of translating step-by-step what


you have worked up in the previous section. You need to be conscious of three
different considerations when you code the program, which pertain to the efficiency
of the program.

It may seem that efficiency means the execution speed of the program, but that is not
always the case. There are memory concerns, as well as program maintenance
(readability) that must be considered in the design of a program.

Efficiency Considerations

 Execution Efficiency
 Memory Efficiency
 Program Maintenance Efficiency (Readability)

What these three steps specify is that it is not good enough that a program works.
Perhaps one of the most tiresome comments made by students is complaining about
points taken off for lack of efficiency, or styling by saying that "it works". That
philosophy was fine in the days of the 1 million dollar computers and the five-
thousand-dollar-a-year programmers. Today's hardware is relatively cheap, while
custom software costs a fortune to develop for businesses. Money is not spent on
software that merely works, but that also considers the three points above. Let us
take a look at each point in turn.

Execution Efficiency
Some years back - in the early eighties - the head football coach at Berkner High
School wanted a computer program to assist him in analyzing data. The coach had a
student who convinced his coach that he was great. Maybe not so great on the
football field, but absolutely terrific with computers. The enterprising student

Chapter XV Program Design 15.13


created a brief file with a small amount of data and proudly demonstrated his
program. The student entered a small amount of data and the coach was impressed.
Required statistics popped up instantly. During the football season more and more
data was entered in the program. By the middle of the season the coaches ordered
pizza and started eating while waiting on the computer’s results. By the end of the
season, data was entered Friday evening and results finally were available Saturday
morning after breakfast.

The data processing package mentioned above did work correctly, but the execution
efficiency was horrible. Several poor decisions had been made in its development
stage. Two factors combined to cause the incredible slow-down. First the program
was written in interpretive BASIC. You should remember that interpretive
languages execute much slower than compiled languages. This happens especially
when nested loops are involved. The second factor was the choice of the Bubble
Sort for the sorting algorithm. You may not be familiar yet with many different
sorting routines, but the Bubble Sort is one of the slowest sorts available.

Execution efficiency is controlled by the language you use, and the algorithms you
create. It is assumed that little choice can be made about hardware. Do keep in
mind that some computers operate much faster than others. However, unless you
have unlimited financial resources, you will need to use the equipment that you own
or have to use at school or work. Regardless of the available hardware, strictly at the
coding level you can make a big difference by watching closely what you code.

For example, in mathematics there is a method for solving equations called:


Cramer's Rule.

Cramer's Rule is a method for solving equations with more than one unknown. It is
not important to understand the details of Cramer's Rule. The formula is shown in a
general form below. The main point being made is that every value computed
requires division by the same general determinant. The determinant is a value that
takes a considerable amount of computations, especially when the equations have
many unknowns. Solving equations with many unknowns is a very common
requirement in the engineering and business world. The general example shown
below is for three equations with three unknowns.

Cramer’s Rule

X Value
Determinant

Chapter XV Program Design 15.14


General
Determinant

Y Value
Determinant

General
Determinant

Z Value
Determinant

General
Determinant

In the illustration about Cramer's Rule the values of X, Y and Z are computed, and
in each case it is necessary to use the same general determinant. Many students
write this program using two separate formulas. This is fine, but what is not so fine
is when each formula includes the computation of the same general determinant.
Execution efficiency is increased considerably if the determinant is first computed,
ONE TIME, and then its value is used in the formulas that follow. The example
showed the required computations for three equations with three unknowns. In the
case where ten equations need to be solved with ten unknowns the same general
determinant will need to be computed ten times. In such a case the unnecessary
computations will make a very noticeable difference in execution speed.

Memory Efficiency
Every character in your program occupies memory space in the computer. Small,
simple programs are of little concern here, but the bigger programs will reach a point
where they will not function properly. Either the program needs to be condensed or
additional memory needs to be added. Usually, memory usage is unavoidable, but at
other times the program can be designed to save a considerable amount of memory.

Using program modularity with functions, introduced earlier, is significant here.


Your algorithm may require that certain data is displayed at three different places in
the program. The output format is the same in each case. Writing code for this three
times is inefficient time-wise, and it will also use up unnecessary space in the
primary memory (RAM), as well as your secondary external memory (disk). One
function which handles the output three times is a memory saving approach.

Chapter XV Program Design 15.15


It is not only the program that takes up space in memory, but the variables that the
program uses take up memory as well. In other words, the manner in which you
define your variables makes a difference. Right now you know that an int takes two
bytes, a float takes four bytes and a double takes eight bytes. Single digit bytes are
not a memory concern. Do realize that soon you will be using data types that will
stores hundreds and thousands of numbers. A data type that has 5000 integers will
require 10,000 bytes. The same data type with 5000 doubles will require 40,000
bytes. You see that the difference is now significant.

This memory business is of no consequence with the program assignments that you
have completed so far. But it becomes very real when the programs become larger
in the future. You will find that memory gets gobbled up very rapidly. This is
especially true when your program uses graphics. For instance a modest VGA
graphics screen with 320 X 200 pixels is a total of 64,000 pixels. Each pixel is
stored as one byte and storing a graphics screen requires 64,000 bytes. Keep in
mind that we are only talking about a relatively low resolution of 320 X 200 pixels.
Many graphics screens use 800 X 600 or 1024 X 768 pixels.

Documentation will be addressed shortly, but keep in mind that you have some
decisions to make. Single-letter variables and totally undocumented programs take
less memory. However, as the next section explains, that may create an inefficiency
of a different type.

As will be emphasized at the end of this chapter the development of a program is a


compromise that can never be resolved perfectly. Something will have to give, no
matter what you do.

Program Maintenance Efficiency (Readability)


Maintenance efficiency sounds fancy, but actually what is meant is readability.
Does readability have something to do with the efficiency of a program? It certainly
does. A poorly structured program without any comments is difficult to read, harder
to understand, and probably impossible to alter at some later date.

That is what is meant by program maintenance. I am talking about the efficiency of


maintaining a program. This means fixing small and large bugs in the beginning of
program development. More importantly it means the updating of the program as
situations change in the future when considerable additions, deletions, or program
alterations are required. These future changes may happen when either the original
programmer is no longer present or at best when the original programmer no longer
has a current memory of the program design.

Chapter XV Program Design 15.16


Let us look at a program example. Two versions will be presented. The first has no
acceptable program style, no self-documentation, and no comments of any kind.
The second illustrates the recommended style and comments that need to be used in
a C++ program.

Keep in mind that C++ is a free-style language. Provided the appropriate


semicolons are in place, and other syntax is correct, your program will compile, no
matter how you have it structured. In the earlier algorithm section, you saw the
algorithm steps for computing the Greatest Common Factor. Now you will see an
actual C++ program that follows the steps of this algorithm, and at the same time
observe some lessons about program readability.

// PROG1501.CPP
#include <iostream.H>
#include <conio.h>
void main() {clrscr();int A,B,C,D;cout<<"Enter";
cin>>A;cout<<"Enter";cin>>B;do{C=A
%B;if(C==0)D=B;else{A=B;B=C;
}}while(C!=0);cout<<endl<<endl;cout<<D<<endl;getch();}

Program PROG1501.CPP is not meant to demonstrate the appearance of a typical


student’s program. Many students use poor, inconsistent styling with their program
and the same students frequently make a fuzz when they lose points. This program
example is intentionally exaggerated. I hope every student agrees that this is a very
unreadable, unpleasant program. Consistent style, program comments, and readable
variable identifiers are not for the glorification of your teacher. Proper style assists
you in making program writing, debugging and updating simpler.

// PROG1502.CPP
/********************************************************************/
/* This program computes the Greatest Common Factor (GCF) of two */
/* entered integers. The method used was devised by Euclid over */
/* two thousands years ago, and today it is still one of the most */
/* efficient methods for finding the GCF. */
/********************************************************************/

#include <iostream.H>
#include <conio.h>

void main()
{
clrscr();
int Number1, Number2;
int Remainder;

Chapter XV Program Design 15.17


int GCF;
cout << "Enter 1st number ===>> ";
cin >> Number1;
cout << "Enter 2nd number ===>> ";
cin >> Number2;
do
{
Remainder = Number1 % Number2;
if (Remainder == 0)
GCF = Number2;
else
{
Number1 = Number2;
Number2 = Remainder;
}
}
while (Remainder != 0);
cout << endl << endl;
cout << "The greatest common factor is " << GCF <<
endl;
getch();
}

To a large degree, styling is subjective and opinions vary considerably. You have
already seen many program examples in this book that demonstrate one particular
C++ style that is used by many people. It is by no means the only appropriate style
for writing a C++ program. There are many styles and some styles are more useful
for certain purposes. For instance, the GCF program will be shown again with a
different style that is popular because it reduces the number of indentations used in a
program, and the number of lines on the screen. This kind of style is popular for
large programs.

// PROG1502.CPP
//
// This program computes the Greatest Common Factor
(GCF) of two
// entered integers. The method used was devised by
Euclid over
// two thousands years ago, and today it is still one
of the most
// efficient methods for finding the GCF.
// This example uses a different program style.

Chapter XV Program Design 15.18


#include <iostream.H>
#include <conio.h>

void main() {
clrscr();
int Number1, Number2;
int Remainder;
int GCF;
cout << "Enter 1st number ===>> ";
cin >> Number1;
cout << "Enter 2nd number ===>> ";
cin >> Number2;
do {
Remainder = Number1 % Number2;
if (Remainder == 0)
GCF = Number2;
else {
Number1 = Number2;
Number2 = Remainder;
}
}while (Remainder != 0);
cout << endl << endl;
cout << "The greatest common factor is " << GCF <<
endl;
getch();
}

There is certainly disagreement in the computer science community on the correct


program style. For the most part the disagreement is very mild and the loudest
message that is heard from the computer science world is to be consistent. Many
styles that are readable are acceptable as long as the style is consistent. The very
consistency of the style helps to clarify the program’s intention.

But proper consistent styling and self-documenting variable names are not sufficient
for maintenance efficiency. A program require documentation. Documentation is
not just a case of a single piece of paper explaining how to install the program. It is
not even a large, thick book, explaining every tiny detail of the program. Program
documentation is a combination of several items.

Program Documentation

 Online user documentation


 Internal Program Documentation

Chapter XV Program Design 15.19


- Self-documenting identifiers
- Program comments
 External Documentation
- User manual
- Technical reference manual

Online User Documentation


Online user documentation includes all the information that is made available on the
monitor and printer during program execution. There is a wide variety in this
category. There are prompts to guide the program user along the way. There are
special help screens which can be called upon when necessary and frequently hints
are provided when the user makes incorrect entries.

The current emphasis is on more and more online documentation. Experience has
shown that program users are very reluctant to use their reference manuals. Clear,
well placed, online documentation is a major factor in making software packages
popular. Both the Macintosh computers and PCs with windows are designed to give
clear guidance to program users, as it is needed, in multiple pop-up windows.

Internal Documentation
The importance of self-documenting identifiers has been demonstrated earlier with
the condense GCF program. Program comments are comments contained within the
source code to explain variables, program segments and functions. Comments need
to be inserted - where necessary - to clear up confusion or to provide insight in the
logic of a certain function. Good internal documentation is critical in testing,
debugging and in future modification. However, only use comments if additional
information is provided. Do not add comments just to please your teacher.

External Documentation

External documentation usually falls into two categories: User manuals and
technical reference manuals. User manuals are written for the primary program
user, who frequently is not a very technical person, and has no interest in program
modification. The technical reference manual provides detailed information that is

Chapter XV Program Design 15.20


of tremendous help to any programmer who may need to update the program at
some future date.

15.6 Testing and Debugging the Program

When an inexperienced student programmer finishes a program, and it compiles,


there is often little concern given to proper test data. A particular set of data is used
that is often the data specified for the program execution hard copy. Since the
program compiles and executes, a premature conclusion is drawn that it must be
correct. As a matter of fact, it is not unusual for students to turn in programs with
totally illogical outputs. Students recognize compile errors, because the computer
stops and indicates such errors. But there are in fact three kinds of errors.

Three Types of Errors

 Compile or Syntax Errors


 Runtime or Execution Errors
 Logic Errors

Recognizing which one of the three errors occurs is the most important step in
debugging a troubled program. Remember, no matter how stumped you are by your
program’s uncooperative nature, you always should be able to state what type of
error your program has. I can assure you that your teacher will be very pleased if
you can say something like:

Excuse me Mrs. Hromcik, my program has a compile error that says I have an
unknown identifier, or something like Excuse me, Mr. Rosier, my program compiles
and executes, but the output does not make any sense at all.

Please do not impress your teacher with the following dialogue:

Student: Can you help me?

Chapter XV Program Design 15.21


Teacher: What do you need?
Student: I have a problem.
Teacher: What type of problem do you have?
Student: My computer has a problem.
Teacher: What is wrong with your computer?
Student: It does not work?
Teacher: What is it that does not work?
Student: My program does not work.
Teacher: What is working or not working with your program?
Student: I don’t know. It does not work.
Teacher: You must know what is wrong, even if you cannot fix the problem.
Student: If I knew what was wrong, I would not ask for help.

Students often think that they have no responsibility in identifying problems. It is


sufficient to tell a teacher that a problem exists. Having announced the existence of
the problem, transfers ownership of said problem to the teacher. Student can now
relax and teacher gets to work. This theory has some difficulties. First, there are a
variety of teachers who give pitifully little help to students who have put forward
minimal or no effort themselves. Second, you will soon find yourself in college or
on the job where easy ownership transfer of problems is not an available option.

Compile or Syntax Errors


Remember that Syntax means Language Sentence Structure. All languages,
especially computer languages, have rules for proper sentence structure, or syntax. A
program compiler goes through two steps. First, the compiler checks if the
programs has been written with acceptable syntax. Second, it translates the
program. It would be inefficient to start the second step unless the first step can be
completed.

Since the compiler specifically checks syntax errors, and it gives a syntax error
message, the error is also called a compile error. Your C++ compiler rapidly finds
any syntax errors, but you must accept the following three facts about any C++
compiler that you might be using.

Compile Error Facts

 The compiler will catch all syntax errors.


 The error message is not necessarily an accurate
description of the actual syntax error.
 The compiler does not necessarily stop at the correct

Chapter XV Program Design 15.22


error location. The error indicator stops either on or
before the actual error location.

Students usually have little difficulty with cryptic error messages. Even if a message
is totally logical, it is still overlooked in most cases. The real problem is that
students expect the error indicator to be at the error location. I have time, and time,
and time again seen students in total puzzlement. The error message was very
accommodating stating: Semi-colon expected.

My hardworking, motivated students would look and look at the highlighted line
with the error message. Everything looked just fine and all required semi-colons are
in place. Amazingly, the missing semi-colon, at the end of the immediately
preceding program statement was not noticed. Please realize that the compiler does
not have intelligence. Certain syntax rules must be obeyed and failure to obey the
syntax rules will cause problems. However, the compiler does not necessarily
recognize what you did wrong or where. What happens is that your error brings
about something that is not digestible to the compiler. At the moment that it is clear
that a mistake is made, the compiler will let you know. This can be many lines
below the actual mistake.

To make matters worse, it is possible that the mistake is not realized until some
library is included. You will now get an error message that stops with a highlighted
bar inside a library that you did not create. This is a major source of frustration. It is
possible that some include libraries have mistakenly been altered, and it is also
possible that the libraries are corrupted. However, in the majority of cases the
libraries are just fine and you are suffering from a program statement error that is not
detected until much later in the compile sequence.

To illustrate the point that the compiler is easily fooled about the error location look
at program PROG1503.CPP on the next page. The program will not compile. Can
you tell what is wrong?

// PROG1503.CPP
// This program demonstrates how one type of program
error can
// cause a totally different syntax error message
somewhere else

#include <iostream.H>
#include <conio.h>

void main()

Chapter XV Program Design 15.23


{
clrscr();
int Number1, Number2; /* two numbers entered by the
user */
int Remainder; /* the remainder of integer
division /
int GCF; /* the Greatest Common Factor
*/
cout << "Enter 1st number ===>> ";
cin >> Number1;
cout << "Enter 2nd number ===>> ";
cin >> Number2;
do
{
Remainder = Number1 % Number2;
if (Remainder == 0)
GCF = Number2;
else
{
Number1 = Number2;
Number2 = Remainder;
}
}
while (Remainder != 0);
cout << endl << endl;
cout << "The greatest common factor is " << GCF <<
endl;
getch();
}

When you attempt to compile this program, you will get an error message that
specifies Unknown identifier on your screen. Does that make any sense? Variable
identifier GCF is defined, is it not? Well yes, you defined GCF, but the compiler
does not realize this. The problem is that Remainder did not finish with a proper
comment. The result is that GCF becomes part of a comment.

In other words the mistake is not finishing the comment properly. Do not bother me
with statements about using the other comment style that does not require a closing
comment symbol. With this type of mistake the compiler stops at a totally different
location, and gives a message quite different from the mistake made. Actually the
compiler's response and its error message is quite logical, but only to people who
have programmed for quite a while. The main point is, that you can often expect
error messages that are poor indicators of actual mistakes, and the location where the
compilers stops is not always too accurate either. You are not pleased right now. At
this stage of your computer science development you get the distinct feeling that the

Chapter XV Program Design 15.24


compiler will tell you that you goofed, indicate this goof at any location except
where it occurred, and to make matters worse treat you to a cryptic message
unrelated to the mistake you made.

It really is not all that bad. With some practice the messages do make sense, but you
can code your programs in such a way that you pretty well know what and where the
mistakes must have been made. The secret is in using STUBS when you write your
programs, and following a logical sequence of coding your program. You are no
stranger to using stubs. This excellent program development feature was already
introduced in the earlier Program Modularity chapter. A brief stub review is given
here. Furthermore, the first introduction to stubs did not include the issue of
parameter passing. Stubs and parameters work closely together to help writing
programs that are easy to debug.

Stub Explanation Review

A STUB is a dummy function, which consists of a heading followed by an


empty program statement block. A STUB compiles for testing purposes, but
it does not execute anything.

Stub Example:

void DisplayData(int X, int Y)


{
}

Every step of the proper stub sequence will not be shown. I will only show you two
programs. First, the main function with all the initial stubs. This is the so-called
first stub stage, which does compile. Second, the completed program.

The biggest source of errors, for students at this stage, are errors caused by using
parameters incorrectly. It certainly makes a lot of sense to insure that your program
uses proper parameter passing at the early stages of program development. The very
first stub stage, which should compile, will catch any of the more common errors
made with parameters.

// PROG1504.CPP
// This program computes employee wages.
// The first stub stage is demonstrated.

#include <iostream.h>
#include <conio.h>

Chapter XV Program Design 15.25


#include <iomanip.h>
void EnterData(double &PR, int &HW);
void ProcessData(double PR, int HW, double &GP, double
&NP);
void DisplayData(double GP, double NP);

void main()
{
double PayRate; // amount paid per hour
int HoursWorked; // number of hours worked per week
double GrossPay; // total amount earned in one week
double NetPay; // weekly take-home pay after
deductions
clrscr();
EnterData(PayRate,HoursWorked);
ProcessData(PayRate,HoursWorked,GrossPay,NetPay);
DisplayData(GrossPay,NetPay);
getch();
}

void EnterData(double &PR, int &HW)


{

void ProcessData(double PR, int HW, double &GP, double


&NP)
{

void DisplayData(double GP, double NP)


{
}

Notice how nice and compact this program is. You can see the main function. You
can see the prototypes and you can see the function stubs. If this stage does not
compile, you are able to count the actual and formal parameters. You can also easily
check the data types to make sure that they match. Make sure that you do more at
this initial stage. Compiling is only part of the total program package. The program
needs to be logically correct. Check carefully and make sure that you have correctly

Chapter XV Program Design 15.26


identified all the reference parameters and placed an ampersand (&) in front of each
formal, reference parameter.

The intermediate stages, of completing each one of the stubs in turn, is not shown.
This is identical to the early stub explanation, introduced in chapter 8. The next
page shows the completed program.

// PROG1505.CPP
// This program computes employee wages.
// This is the final stage after every module is completed.

#include <iostream.h>
#include <conio.h>
#include <iomanip.h>

void EnterData(double &PR, int &HW);


void ProcessData(double PR, int HW, double &GP, double &NP);
void DisplayData(double GP, double NP);

void main()
{
double PayRate; // amount paid per hour
int HoursWorked; // number of hours worked per week
double GrossPay; // total amount earned in one week
double NetPay; // weekly take-home pay after deductions
clrscr();
EnterData(PayRate,HoursWorked);
ProcessData(PayRate,HoursWorked,GrossPay,NetPay);
DisplayData(GrossPay,NetPay);
getch();
}

void EnterData(double &PR, int &HW)


{
cout << endl << endl;
cout << "ENTER DATA FUNCTION" << endl;
cout << endl;
cout << "Enter your hourly payrate ===>> ";
cin >> PR;
cout << "Enter your weekly hours ===>> ";
cin >> HW;
}

void ProcessData(double PR, int HW, double &GP, double &NP)


{
double Deductions; // federal tax and social security withholdings
cout << endl << endl;
cout << "PROCESS DATA FUNCTION" << endl;
GP = PR * HW;
Deductions = GP * 0.28;
NP = GP - Deductions;
}

void DisplayData(double GP, double NP)

Chapter XV Program Design 15.27


{
cout << endl << endl;
cout << "DISPLAY DATA FUNCTION" << endl;
cout << endl;
cout << setiosflags(ios::fixed);
cout << setiosflags(ios::showpoint);
cout << setprecision(2);
cout << "Gross Pay: " << GP << endl;
cout << "Net Pay: " << NP << endl;
}

Runtime or Execution Errors


Runtime errors are much tougher to correct than syntax errors. Your program may
compile just fine, but when you attempt to run it, the program "crashes." You may
or may not get any useful message, but it will be very clear that something is wrong.
There are many different causes for runtime errors, but in general let us define this
error as follows:

Runtime Error Definition

A runtime error or execution error is an error that interrupts


program execution. This is more popularly known as the
program "crashes".

In the greater majority of cases you will receive some kind of error message. Your
first step with execution errors is to see if the message makes sense. C++ will also
take the cursor to the location where it suspects the error has been made. As was
pointed out with syntax errors, it is quite possible that the computer is fooled. The
execution error may be at some other location then indicated.

Even more uncomfortable are the errors that cause a computer crash and there is no
message at all. Sometimes you may even have to re-boot the system. At other times
the computer is so distraught by the nature of your error that a courtesy reboot is
automatically performed. Whether you get a message or not, your first task is to
isolate the program segment that causes the runtime error.

All the modern C++ compilers have some type of debugging tool. These tools are
often a good help in identifying problems. The use of such tools is not covered here.

Chapter XV Program Design 15.28


There are too many different debugging features. Please consult the online help and
the reference manual of your C++ compiler for more information.

However, there is something that can be done with every compiler without the use of
any special debugging help. If you are using the stub method, you should already be
able to identify the offending function. Make sure that you not only compile the
program after completing each function, but also run the program. This approach
will help to pinpoint the function location of both syntax and runtime errors.
Another problem may occur when some of your functions become quite large.
Further isolation of the problem can be done by using CHECK statements. The
statements are placed at intervals in your program and help to locate the error. You
need to use something like the program segment below.

cout << ”CHECK 1” << endl; getch();


program statement;
......
......
program statement;
cout << ”CHECK 2” << endl; getch();
program statement;
......
......
program statement;
cout << ”CHECK 3” << endl; getch();
program statement;
......
......
program statement;
cout << ”CHECK 4” << endl; getch();
program statement;

Initially you may have the CHECK statements pretty far apart. After you run the
program you will find a rough “crash” location. At that point you might decide to
place the CHECK statements closer together. Sometimes you need to narrow down
the checking to a single statement.

You might wonder what causes a runtime error? The compiler has checked the
syntax and the syntax is fine. An executable code is created and the program does
execute. So what can be wrong now? There are a variety of problems that can be
handled by the computer. The more common runtime error is division by zero.
Another common problem is taking the square root of a negative number. I hope
you realize that checking syntax is not at all the same as checking something like a
division by zero.

Chapter XV Program Design 15.29


When a “Crash” is not a “Crash”

Students often think that a computer with a totally black screen


has “crashed.” Nothing is showing and nothing is happening.
Frequently, the program is stuck in an infinite loop without a
means to exit. This is not a runtime error. The computer is
busily doing what it is told. This is a logic error.

Logic Errors

We arrive now at the toughest error of them all, the logic error. Why are logic
errors toughest? Simply because the computer gives zero help in the logic error
department. This baby is strictly your affair and if you are not careful you will get
bitten severely. But first things first, what is a logic error?

Logic Error Definition

A logic error occurs when a program's output is not logically


correct, even though the program compiles, and the program
executes without crashing. The careful use of well-chosen test
data catches logic errors.

One of the biggest problems with logic errors is that many programmers, especially
beginning students, do not realize that a logic error exists. How do you know that
you have this problem? The answer is proper TEST DATA. Consider a simple
example first. Your program assignment involves computing the payroll for a
number of employees. Every employee works 20 or more hours and earns at least
$7.50 per hour. You run your program and look at the results. Some of the
employees earn negative amounts of money. It does not require a programming
genius to figure out that something is wrong. Ironically, there are students who do
not catch such errors. Avoid surprises by using the following steps:

Program Testing Steps

1. Test the program first with easy to tell test data. For

Chapter XV Program Design 15.30


example, a program that averages student's grades should
start with all grades of 100. Your average has to be 100.

2. Test the program with a set of test data for which the correct
output is known. If you average a set of peculiar numbers,
check the answer first on a calculator.

3. Test the program with a wide variety of data, for every known
path that is possible in the program.

4. Test the program carefully with test data at borderline cases.

Types of Test Data

Minimal Test Data is a set of data that checks every possible


path in a program, at least once.

Thorough Test Data is a set of data that checks every possible


path in a program, and checks the border cases as well, at least
once.

Consider program PROG1506.CPP, which is a payroll program that uses control


structures for computing over time pay and the federal income tax bracket. In this
case the whole program will not be shown. The main focus is on the control
structures.

// PROG1506.CPP

cout << "ENTER YOUR HOURLY PAYRATE ===>> ";


cin >> PayRate;
cout << "ENTER YOUR WEEKLY HOURS ===>> ";
cin >> HoursWorked;
if (HoursWorked > 40)
{
OverTime = HoursWorked - 40;
RegularPay = HoursWorked * 40;
ExtraPay = OverTime * PayRate * 1.5;
GrossPay = RegularPay + ExtraPay;
}
else
{
GrossPay = HoursWorked * PayRate;

Chapter XV Program Design 15.31


}
if (GrossPay > 500)
Deductions = GrossPay * 0.29;
else
Deductions = GrossPay * 0.15;
NetPay = GrossPay - Deductions;

Minimum test data requires testing in the four different paths through the program.
This program can have regular pay and 15% deductions, regular pay and 29%
deductions, overtime pay and 15% deductions and overtime pay and 29%
deductions. Thorough testing requires more test data. It is not sufficient to test the
four possible execution paths in the program. You also need to check the border
cases. It is precisely at the borderline situations that many programs produce logic
errors. Thorough testing of the border cases is vital to prevents OBOBs. This,
recently popular, acronym stands for Of By One Bug. It is not unusual that a
program uses a conditional expression such as (X > Y) when the, close but not the
same, expression (X >= Y) should have been used. Unintentionally leaving out
the equal ( = ) sign can only be detected with test data that checks the border cases.

Whether you try to solve your own logic errors, or you are trying to get help from
somebody else, A hard copy of the program listing is vital. Then you can do a
variable trace. This means that you trace the values of the variables as the program
executes. In other words, you become the computer.

If you methodically trace through the program in the proper sequence you should
end up with the same results as the computer, and catch your logic error. You may
also use the technique shown for execution errors. Display values of variables at
intermediate stages to isolate the location where the logic goes haywire.

Carburetor Logic
Every year I meet students who do some really bizarre things to their programs.
These students are motivated, they are hard working and they have good intentions
about completing their program assignments. They also recognize that some things
are wrong with their programs. It is with logic errors that this unique group of
students start using very interesting logic. All of a sudden previously, correct
program segments are gone, only to be replaced with new strange looking code. I
call this Carburetor Logic. This can be explained by the following conversation
between a customer and a mechanic.

Mechanic: What is wrong with your car?

Chapter XV Program Design 15.32


Customer: My car does not start.
Mechanic: Let me check. Wow, your carburetor is gone. No wonder
your car does not start. What happened to your carburetor?
Customer: I removed my carburetor.
Mechanic: Why?
Customer: My car would not start.
Mechanic: Your car cannot start without a carburetor.
Customer: My car did not start with the carburetor, so I removed it.

Please do not ever expose your teacher to carburetor logic. Strange things may
happen to your teacher. Eye ticks - long under control - return and Vietnam
flashbacks - assumed cured - suddenly return.

15.7 Updating and Enhancement

If a program is worth keeping then it is worth improving. Look at any software


package on the market. How many software packages can you find, older than two
years that is titled Version 1.0. Often it is not until software is actually used that
improvement becomes natural. Many times improvements are considered by the
person or company who originated the software and at other times program users
give ideas for improvement.

In 1985 Lynn Rosier, of Richardson High School, Texas developed a very fine
gradebook program for teachers. This program became an instant success with
teachers, but almost immediately the requests for enhancements started to come in
such as:

Progress reports that can have custom remarks on them

Class grades that are automatically curved

Attendance records that use the same class lists as the grade book

Book records that use the same class lists as the grade book

A choice of waited, percent and point grade computations

Numerous other reports that are useful for teachers

This is only a partial list and you will not be bored with numerous examples of other
programs that have been improved with later versions. This brief section on
program enhancement does not exist in an effort to explain how to enhance a

Chapter XV Program Design 15.33


program. Such a topic is too general and can easily require a book to handle
adequately.

The point here is that program enhancement is the most natural thing to follow
regular program development. It is never sufficient to finish a program without
regard to proper structure, readability, comments and other important program
design concepts. Any person who finishes a meaningful program with the idea that
it will not be looked at in the future is kidding himself or herself.

It is bad when you cannot understand somebody else's program. It is far worse when
you are totally confused by your very own program design. And this happens
frequently to inexperienced programmers.

15.8 The Need for Compromise

If you have paid attention so far in this chapter you may feel very frustrated. Your
programs are supposed to be efficient, which is fine with you. Prior to this chapter
you thought you understood efficiency, but now there happen to be three types of
efficiency.

It does not take a genius to figure out that nice self-documenting identifiers, clear
internal documentation (comments), and one-task-one-module type of programming
gobbles up memory. Well that is great, you either make a program readable, or you
use up a lot of memory. Calling a function is nice for structure and readability, but it
slows down program execution. You can't win. First accept the following fact
about programming.

The Efficiency Dilemma

It is not possible to develop a program which achieves the


maximum possible efficiency, simultaneously, in the following
three efficiency areas:
 Execution Efficiency
 Memory Efficiency
 Program Maintenance Efficiency (readability)

Chapter XV Program Design 15.34


Which is more important? That question can only be answered by looking at an
individual program and its environment. The main point is that you have to accept
that each program is a compromise. The manner in which you compromise is
dictated by your hardware and the purpose for which the program is designed. In
the past, memory was extremely scarce and expensive. This usually meant that
program readability was sacrificed. Today memory is plentiful and readability and
program appearance is very important. Many windows style programs run much
slower - even on modern computers - than old-style text programs did on older
computers.

Chapter XV Program Design 15.35

You might also like