|
~ Introduction to Programming: C ~ Session 18 - Program Design and Split |
In This Session...
In this session we will be covering the following topics:-
In the last session, we covered:-
The course has, up to now, concentrated on getting you familiar with the
various techniques that are using whilst writing a program.
When designing computer programs that perform a specific task, we need to
understand fully what we wish to achieve by writing the program.
This may seem obvious, but its surprising the number of people who will skip
this planning process and go straight to the 'coding' part.
The consequences of this can be that you end up with a finished product
that does not perform the task that was originally intended – and thus needs
to be thrown away. This scenario is
frighteningly common in everyday programming practice. Common reasons for
skipping the design stage are:-
You
think you've got the design in your head
The
person who asked for the program needs it yesterday, so it seems that
skipping to the 'coding' will save some time
It
seems boring, and is unnecessary paperwork
You
might find out things you’d rather not know about – for example that the
problem is more complex than originally anticipated
Somebody
sent you an email giving a rough idea of what they want, so that's a design
isn't it?
You can see that these are all really psychological reasons – they are
excuses!
The
process that you should go through when designing a program is
generically known as the software development life-cycle. There are
different models used to represent this, but typically it looks something like
this:-
These can be broken down as follows:-
Initial
Request
You may have an idea for a program based on something you have seen, or in a
business setting, a system user may have identified some information that he
or she would like to store and extract for a particular purpose (for
example).
The initial request is inevitably vague and lacking in details. If you
created a program based on these details, you would probably end up with
something that did not fulfil the real requirements, which may not be
an accurate reflection of the initial request.
Key: Don't take the initial request as gospel. It is only a
starting point.
Investigation
/ Analysis
Your task is to take that initial request, and try to determine the reason
why it is required, and thus what is really needed. There are several
ways of doing this:-
1
to 1 interview. Talk to the person who made the request, and ask them
what they want, what they have seen that made them decide this, and
whether it could be given to them in stages – e.g. a few details /
screens first, and lots of whiz-bang features later, when they are happy
with the 'first draft'.
Questionnaire.
If a number of people will be using the system, it may be worth getting an
initial understanding of what is required, and then asking a number of
questions that will help to determine if this is an accurate reflection of
the requirements, or whether other things need to be taken into account.
Similar
Systems. It may be worth looking at existing systems that do the same
job, even if they are not quite what is required. They may give you some
ideas, or may be fit for the task themselves – why reinvent the wheel?
Data
requirements. Sometimes it is worth looking at the documents that are
currently used in the process, and look at what information is kept in
those documents. These will give you a good idea of what information will
need to be recorded in your system.
Processes.
When computerising something, it is often useful to look at how the
information is being used, and where it is being passed. This gives you an
idea of how the information is likely to be stored and retrieved, and when
particular information becomes available – e.g. in a library book
logging system, the return date will not become available until the book
is returned!
From
this investigation, you would produce a Specification document. This
details what the system should do (it's purpose), what it should store, and
possibly the information that should be retrieved from the system (e.g.
reports). Once you have produced this document, you should check with the
user that this is an accurate reflection of what is required before
proceeding.
Now
that we are reasonably certain of what we want the program to do, we are
ready to investigate the best way of doing it.
Sometimes, a change in the current manual process (i.e. way of doing
things) is sufficient, and a computerized solution is unnecessary. One
solution may be to use a pre-existing application, and fit the requirements
to the product (although this usually results in a certain amount of
compromise). You may decide that it would be best to develop the system in
Visual Basic, or maybe Access if it is a database application, or Java if it
is a web-server application.
Once you have decided how you will implement the solution, you need to
design your program the fit the specifications. This tends to be the hardest
part of the process when you start in programming, although becomes
second-nature after a while.
We will look at various techniques for doing this later.
Implementation
– This means taking the design and turning it into source code – i.e.
actually writing the programs in the language / computer that you have
decided is most appropriate. Surprisingly, if you have performed the other
stages of the life-cycle properly, this part can be the easiest to do.
Testing
– This means ensuring that the program operates as it should. Typically,
you would create a test plan to list everything that needs to be tested,
giving the expected results, and the actual results. If these tally, your
application has been validated to be fit for its purpose. This is known as a
test plan. It is vital that this should be accurate, and cover all
areas that need to be tested:-
White
Box Testing – This happens whilst the programmer is developing the
program. It tends to be part of the debugging process – i.e. looking
through the program line-by-line to see what is happening, if the program
is not operating as expected. Modern development environments allow the
programmer to set breakpoints at certain lines in the source code,
so that execution stops at that point, and the programmer can check to see
(for example) the values of variable at that point, and perhaps step
through the program line-by-line to see what is happening on a
step-by-step basis as the program is executing.
Black
Box Testing
This is a more structured testing method. The idea is that having written
the program, it needs to be proved that the finished product matches the
original specification – i.e. it does its job properly.
We will be looking at this in more detail later.
User
Testing
This is an extension of black-box testing, but often simplified. It gives
selected users an opportunity to try out the program before it is
generally released, to take into account any last-minute changes, or to
avoid the cost of distributing something that is not yet ready for general
use.
Review:
After the system has been in use for a period of time, it is useful to
reassess the original specification to see how successful the program has
been in fulfilling its task, and whether the requirements have changed, or
been re-evaluated through using the program. In this way, the whole
life-cycle starts again, but this time in a 'maintenance' phase, where the
foundation is already laid for making changes to the program.
When designing how to turn a specification into a
design, it is useful to divide the specification into the following elements:-
1.
Outputs
What should the system produce – i.e. documents, graphs, screens, emails, web
pages etc.
2.
Inputs
The inputs required will usually be dictated by the outputs required. So, you
would need to look at:-
a.
How data is collected and put into the system
b.
How to check the data put into the system to make sure it is in the
correct form (e.g. post code is a standard format, National Insurance numbers
should never be duplicated etc.)
c.
The quantity of information that is put into the system
d.
The design of the 'input forms' – e.g. screen layouts for data entry
3.
Files
This is sometimes linked to databases too – i.e. how information that is input
on the system should be stored, and how to retrieve information from this
storage too.
Linked to this is the consideration of how to limit access to the data according
to who needs to see the information – essential by law, in order to comply
with the data protection act.
Information is also generally grouped into standard 'records' – the design of
these records is very important when dealing with large amounts of information.
4.
Procedures
You will need to work out the various tasks that need to be performed by the
system in order to fulfil the requirements of the system. Each task will tend to
deal with a specific requirement – e.g. one procedure for data entry, one for
producing a report, one for storing the information, one for retrieving it etc.
Breaking up functions of the program into logical chunks is a fundamental part
of the design process.
As part of your design, it is important to take into
accounts any exceptions – i.e. circumstances where it is not possible or
infeasible to fulfil the requirements. For example, it may be possible to change
an original order for some goods, after the goods are all paid for, but it may
not be desirable for accountancy and audit trail purposes, so this would be a
valid and intentional restriction.
There are various techniques you can use to
represent how your program will work in the form of diagrams. This can help you
to visualise the function of the program.
One technique is called JSP or Jackson
Structured Programming – not to be
confused with another modern use of this acronym, Java Server Pages.
This is a way of designed programs in a 'top-down' way. This means that you start with an idea for a program, and split it up into sections - e.g. "set up program", "main loop" and "closing messages", and then take each section, and split it up into smaller tasks. This continues until you have enough detail to work from in order to write a program in your chosen language. It also helps model structures such as loops and branches (i.e. going one of a number of routes depending on a decision made).
A square box describing what the procedure does, represents each step in a program. For example:-
![]()
If this
action can be subdivided into further actions, a line is drawn at the bottom of
the box, and a line going to each of the actions that make up this one action is
drawn to any further boxes.
For
example, to get a name from the keyboard, we might first display a message
asking for the name, and the get the name into a variable:-

Notice
that the steps that make up a procedure not only appear under that procedure,
but the steps move logically from the left of the page to the right – i.e. the
request message gets displayed first, and then the name is read from the
keyboard, and then the name is placed into a variable.
The C
source code equivalent to this might be:-
printf("What is your name: ");
gets(name_var);
Notice that
although we have split the process into three steps, we only have two lines of
source code from this – the gets
command both stops to read text from the keyboard, and when the Enter key is pressed,
places that text into a variable.
When you
are designing your program, you will usually have your program as a single box
on its own, and the various actions that take place in your program are a
'subdivision' of this – i.e. appear as boxes underneath it.
To
signify a loop, place an asterisk (*) character in
the top right-hand corner of a box, and write the condition just to the right of
the box.
Everything inside the loop will be a subdivision of
this box.
Take the following example:-

This says that the three processes that are
required to get the name from the keyboard should be repeated until the name
returns is not a blank – seems reasonable!
The C code to do this would might be as follows:-
do
{
printf("What is your name: ");
gets(name_var);
} while (strlen(name_var)==0);
Just say we wanted to add a statement to say that if
the person's name is Simon that you want to marry them, otherwise show that you
aren't interested. In C terms, this is an If statement (or if there
were many options, it could be a switch statement) – a condition to do differing
tasks depending on the condition.
In JSP a circle in the top-right hand corner of the
box represents this. The top box explains the reason for the condition, and all
boxes branching off underneath this box represent the different conditions. Each
of these conditions may have boxes branching underneath them to show the
processes to be taken under each of these conditions.
So, in the above example, our JSP might look like this:-
Exercise 57 - Create a program from JSP
Take the JSP diagram described above, and turn it into a C program. Try to do it without looking at the solution located at the end of these session notes first!
Splitting a program using functions
In the last session, we used functions to create "calculating machines" which take data in, perform a specific calculation, and give an answer - for example, a volume calculating function which takes the length, breadth and height dimensions in, performs a calculation (i.e. multiplies them together) inside the function, and then produces a result that replaces the call to the function in the original formula - e.g. calc_volume(2,2,2) would be replaced by 8. These functions can then be used again and again throughout your program.
You can also use functions as a way of splitting up your program into logical chunks, making each of those chunks smaller in side (as they generally will just list the function names in the order required to perform the required tasks ).
To do this, we can build up our program into "stubs". These are functions that do nothing in their own right - i.e. the actual C code is left out. This allows you to build up the structure of the program before concentrating on how to do it. Once you come to do the actual C coding, you only have to concentrate on small chunks of code at a time, making even complex programs much easier to tackle.
We start at the top of our JSP diagram. This represents the main() function. The next level down lists all the tasks inside the main() function. We could give each of these boxes a function name and hide the detail of the boxes inside each function. Typically, these do not need any data passing into or out of them. Thus we prefix them with void to show that nothing is coming out of them, and put nothing in the parenthesis - ( and ) - to show that nothing is going into them:-
#include <stdio.h>
#include <conio.h>
#include <string.h>
/* Prototypes */
void get_name();
void check_name();
main()
{
char name_var[20];
get_name();
check_name();
getch();
}
void get_name()
{
/*stub*/
}
void check_name()
{
/*stub*/
}
You can see that the main() function now mirrors the three boxes underneath it in the JSP diagram. We could split check_name further, but there's really no benefit in doing this. Splitting up a program too much can make it as unreadable as having it all in one large program in the main() function!
Exercise 58 - Create a program from a JSP diagram; Global Variables
You can now fill in the stubs with C code to perform the tasks described in the function. Take the program you created in exercise 57, and use cut and paste to copy chunks of code from the main() function to the two new functions, replacing them in the main() function with calls to each of the functions.
When you try to execute this, you will get an error indicating the the variable name_var is not available. This is because it is defined inside the main() function, and the get_name() and check_name() functions which need to work on this variable, are in separate functions, which do not have access to this variable.
The easiest solution is to make name_var a global variable, which means that it will be available from any function in the program. To do this, move the line:-
char name_var[20];
above the
main()
line, so that the top of the program now looks like this:-
#include <stdio.h>
#include <conio.h>
#include <string.h>
/* Prototypes */
void get_name();
void check_name();
char name_var[20];
main()
{
get_name();
...
Now that name_var is not inside the main() function, it is available to all functions. This practice is not generally encouraged in programming, as it can lead to problems where the same variable is used for different purposes in different functions, and they interfere with each other.
Examples where you almost always need to avoid global variables, and define a variable inside a function (i.e. as a local variable) include defining a variable for a loop, and anything where the variable will not need to be shared with other functions.
A better way to pass information between functions is to use parameters, as we did last session with our volume calculator example. However, we have not yet learned how to pass strings into a function, so we'll use a global variable instead.
Now test your program to make sure it works as before.
Finally, trace through the program using the F7 trace option and follow the red highlighter through your listing to see the order in which lines are being executed. This should help you understand how splitting up a program does not really change the order in which things are executed - rather it makes it easier to follow what is happening.
#include <stdio.h>
#include <conio.h>
#include <string.h>
main()
{
char name_var[20];
do
{
printf( "What is your name: " );
gets( name_var );
}
while ( strlen(name_var)==0 );
if ( strcmp( name_var, "Simon" )==0 )
printf( "Marry Me!\n" );
else
printf( "Go away!" );
getch();
}
Here's an example execution or three. Notice the case-sensitivity of strcmp() function:-
What is your name: George Go away!
What is your name: SIMON Go away!
What is your name: Simon Marry Me!
#include <stdio.h>
#include <conio.h>
#include <string.h>
/* Prototypes */
void get_name();
void check_name();
char name_var[20];
main()
{
get_name();
check_name();
getch();
}
void get_name()
{
do
{
printf( "What is your name: " );
gets( name_var );
}
while ( strlen(name_var)==0 );
}
void check_name()
{
if ( strcmp( name_var, "Simon" )==0 )
printf( "Marry Me!\n" );
else
printf( "Go away!" );
}
In the next session we will be covering the following topics:-
Indenting functions
Locals vs. Global variables
Passing parameters by reference
Passing strings and arrays
Scope rules
![]()
(c) Copyright
2003-4 Simon Huggins. All Rights Reserved.
If you have any issues or questions regarding the content of this web
site, please contact the
author by clicking here.
Alternatively, you can leave a voice message on 00 44 (0)7050-618-297 or fax
on 00 44 (0)7050-618-298
This Page was last updated: 29 January 2004 13:07