C Programming Compress
C Programming Compress
Othertitles ofrelatedinterest
Tony Royce
*
© A. P. Royce 1996
All rights reserved. No reproduction, copy or transmission of this
publication may be made without written permission.
No paragraph of this publication may be reproduced, copied or transmitted
save with written permission or in accordance with the provisions of the
Copyright, Designs and Patents Act 1988, or under the terms of any licence
permitting limited copying issued by the Copyright Licensing Agency, 90
Tottenham Court Road, London W1T 4LP.
Any person who does any unauthorised act in relation to this publication
may be liable to criminal prosecution and civil claims for damages.
The author has asserted his right to be identified as the author of this work
in accordance with the Copyright, Designs and Patents Act 1988.
Published by
PALGRAVE MACMILLAN
Houndmills, Basingstoke, Hampshire RG21 6XS and
175 Fifth Avenue, New York, N.Y. 10010
Companies and representatives throughout the world
PALGRAVE MACMILLAN is the global academic imprint of the Palgrave
Macmillan division of St. Martin's Press, LLC and of Palgrave Macmillan Ltd.
Macmillan® is a registered trademark in the United States, United Kingdom
and other countries. Palgrave is a registered trademark in the European
Union and other countries.
This book is printed on paper suitable for recycling and made from fully
managed and sustained forest sources.
A catalogue record for this book is available from the British Library.
12 11 10 9 8 7
08 07 06 OS 04
Contents
Preface viii
vi
Section 4 - Modular Programming
vii
Preface
This text has been written for students with little or no previous programming
experience. It should be useful on a wide range of courses - in particular - degree ,
BTEC, City and Guilds and various professional courses in the C language.
The text substantially covers the C language (ANSI standard) and introduces program
design and testing.
As far as possible, each lesson is short and covers a single topic. There are numerous
examples and exercises. Topics have been arranged in order of difficulty and
usefulness. Easy, and more commonly used, subjects are at the beginning and
harder, or less used, topics are near the end.
It is important to try out all the practical examples on a computer and to attempt all
the exercises. Just as with any language, programming languages become easy with
constant use .
I should like to thank students and colleagues for their help and encouragement in the
development of this book.
viii
1.1 Introduction to Programming
So - for example - if you want it to work out the pay figures for your employees
then you will have to give it a set of instructions telling it how it can do this.
Likewise if you want to do word-processing on a computer - then it will have to
be given a suitable set of instructions telling it how it can pretend to be a
typewriter for you!
These sets of instructions can frequently be bought ready made - in the same way
that you can buy a set of instructions (a recipe) telling you how to make a
particular type of cake. If so then they will normally come stored on a tape or
disk so that you can load them straight onto the machine and let it use them.
Often there is not a set of instructions which exactly matches what is needed. For
example, a very large company which wants its computer to keep records of all its
stock will probably have different requirements from those of another company
so it will need a set of instructions written specially - and if so it will need to
employ people (called programmers) to write these instructions.
All these languages are similar to English or to mathematical expressions and are
therefore fairly easy to learn. However they all have very small vocabularies and
the main problem involved in programming is expressing a complicated task as a
set of simple steps .
Most languages have specialist uses. For example FORTRAN and ALGOL are
used for mathematics and scientific applications, BASIC was originally intended
as a begirmers' language for training (although it has since become popular for a
wide range of jobs), and COBOL is intended for use in business.
C has a wide range of uses. It can be used to write applications programs for
business or industrial tasks or it can be used as a system programming language -
to write operating systems and various utilities. It is used on computers of various
sizes and types. Historically, it has.a close association with the UNIX operating
system which still provides an excellent environment for C progranuners.
The computer will not be able to follow any of these languages directly but will
first have to convert anything written in them into its own language - a set of
difficult-to-follow numbers - called machine code (each different type of
computer can have its own machine code). This translation is carried out by a
program called a compiler - which you would normally be able to buy to convert
your chosen language to your own computer's particular machine code.
1.2 Programming in C
The method of writing and testing C programs will vary from system to system -
and you will need to look at the reference manual for your own C compiler to
know exactly how to do this. (Appendix A gives an introduction to using C on a
UNIX system.)
Sometimes one program will allow you to do all these things - sometimes you
will need a separate program to do each of these tasks - particularly an editor (or
word processor), a compiler and a linker.
If you make any mistakes. they will generally show up at two points :
1. when translating the program from C into machine code (compiling) -
because the compiler does not recognize something you have written (e.g .
a misspelling, or incorrect punctuation);
or
2. when running the program you may find that it does not do what you
hoped it would do or simply stops.
To put right compilation errors - check first for simple mistakes such as leaving
out punctuation or spelling words incorrectly. After that. check that what you
have written is all correct C grammar (compare with the examples given in the
book).
When trying to put right errors which occur when trying to run your program -
you have to remember that the computer will try to do exactly what you have told
it to do.
Sometimes it will come across something that it finds impossible - such as trying
to get information from a file that does not exist, or trying to divide by zero - in
which case you will have what is called a run-time error. In these cases you have
to alter the program as necessary or make sure that the files you told it were
available for it to use are in fact on the disk .
At other times the program will work but will not do what you wanted it to do
because you have given it incorrect instructions (e.g, you have told it to ADD
when you meant SUBTRACT). This is called a logic error and will mean that
you will have to alter the program so that it will do what you intended.
2
1.3 Structure ofa C Program
Library files have one or more headerfiles associated with them. These contain
certain information needed to be able to use groups of commands contained in the
library .
For example, a header file called 'stdio.h' contains information needed to allow
many input and output commands contained in the standard library to be used.
1. a note of the relevant header file associated with the library from which
commands will be drawn - in the form:
mainO
3. the actual program instructions enclosed within braces { }. Each C
instruction statement finishes with a semi-colon.
Example Program
#include <stdio.h>
main()
(
printf("Hello\n") ;
return 0;
Notes:
1. The printf command is drawn from the standard library and is made
available by including stdio.h in the program; it instructs the computer to display
a message or value on the screen . (The \n moves the cursor to a new line).
2. The main program function usually ends with the command return 0 -
which, when the program is carried out, tells the operating system that it has
finished running without problems .
Exercises
Write programs:
1. to display the message Goodbye.
2. to display a line of asterisks : **********
3. to display a four by four square of asterisks.
3
1.4 Layout ofa C Program
Example Program
main()
(
/*Display sails and mast*/
printf (U I \n H
) ;
printf(U * *1 *\n");
printf(U * * I *\n U) ;
printf(U * * I *\n U ) ;
printf( U * * I *\n U ) ;
printf( U * * I *\n U) ;
printf(U * * I- ----- -- ---*\n") ;
print:f (U I \n U ) ;
/*Display boat*/
printf(U *** '******************\n") ;
printf(U *********************\n");
/*Finish*/
return 0;
Exercises
1. List the comments in the example program.
Ensuring that each program is well spaced, and includes suitable comments -
write programs to draw:
2. a house - including a roof, walls, and ground;
3. a ship - including superstructure, mast and funnels and hull;
4. a wine glass - made up of bowl, stem and base.
4
1.5 Vocabulary
Notes
a) These keywords are always in lower case (i.e. small letters).
b) Some compilers may use additional keywords - check the manual.
2. Words drawn from the standard library or from standard header files.
These words - e.g, printf, scan! - are usually reserved for a specific
purpose - but may be redefined by the programmer to have a different
meaning. Usually, however, the programmer should keep these words for
their standard meanings - for clarity.
Exercises
Which of the following are valid and suitable as names for variables or functions
defined by the programmer? Explain your answer in each case .
a) overall_mark b) printf
c) Overall_mark d) print
e) 2nd_exam_mark 0 average mark
g) second_exam_mark h) _mark
i) exam_mark_2 j) _mark
5
1.6 Variables
A computer has to store any data it is about to use in spaces in its Central
Memory . Because the data placed in these memory locations will vary each time
the program is used - they are known as variables.
For example. a program written to add two numbers together and display a total
would need to use three memory spaces for the data - one for each of the numbers
used (including the answer).
When you are writing a program - you have to tell the machine in advance how
many spaces it will have to set aside for items of data and the type of data that a
space should be able to hold (e.g. integer data. character data . floating point data
etc). You will also have to give each space a name.
This is done by listing the names near the beginning of the program .
sets aside three spaces in memory to hold integer (i.e. whole number) data - and
gives each space a label.
sets aside three spaces - each one capable of holding a single character - such as a
letter of the alphabet.
sets aside three spaces capable of storing floating-point numbers (i.e numbers
containing fractions - e.g. 5.25).
main()
{
int mathsmark, englishrnark , averagemark;
char overallgrade;
Note: This example program makes use of four storage locations - three for
integers (the marks) and one for a character (the grade).
Exercises
Write suitable definitions for the variables that will be used by each of the
following programs :
I. accepts maths mark and English mark and calculates and displays the
average mark;
2. accepts exam mark and displays grade (A, B, C or F);
3. accepts price in pounds and converts to dollars (Allow for fractions);
6
1.7 Using scan! and print!
Both of these commands are drawn from the standard library and accessed by
including the header file stdio.h in the program.
scanf
The scanf command instructs the computer to wait for the user to key in data in a
specified form then store it in a named storage location.
tells the computer to accept an ordinary integer number ("%d" indicates that input
should be a whole denary number) from the user, then store it in a space called
mathsmark.
tells the computer to accept a single character ("%c" indicates a character), and
store it in a space called mathsgrade.
When %c is specified, the scanf command will normally accept the first character
it finds, including any space or carriage return still in the buffer. To tell the
computer to ignore these, put a space directly before the % sign, so %c".
II
.1\
' !KJU
Note the use of the & before the names of data items, when using scanf. The
meaning of this will be made clear in a later lesson.
printf
The print! command can be used simply to display a simple message - e.g.
printf("Hello") · or to display the contents of a named memory location (or both).
tells the computer to display the contents of the space called mathsmark in the
form of an ordinary denary integer number. (Note that. as with scanf, the form in
which the data should be displayed has to be enclosed within double quotation
marks).
e.g.5. printf ("%c" I mathsgrade)
tells the computer to display the contents of the space called mathsgrade in the
form of a single character.
e.g.6. printf ("%f" I weight)
tells the computer to display the contents of the space called weight in the form of
a decimal fraction.
7
It is also possible to ask the computer to display a message together with the
value stored in a memory location:
tells the computer to display the message "Your average mark is " followed
immediately by the contents of the storage location averagemark - in the form of
an ordinary integer number.
Control characters such as newline (\n) or tab (\1) may be included within the
message to be displayed.
e.g.B. printf ("Your wage is %d\n", wage)
tells the computer to display the message followed by the value of wage - then
move the cursor to a new line ready for the next display command .
causes the computer to move the cursor down two lines then display the message
"Your grade is ", followed immediately by the value of grade - then move the
cursor down two lines.
Example Program
#include <stdio .h>
main( )
{
int mathsmark, englishmark, totalmark;
I *Ge t marks*1
printf ("Please key in your maths mark\n ");
scanf ("%d", &mathsmark) ;
I*Finish*1
return 0 ;
Exercises
1. Write a program to ask for an employee's wage , accept the response and
display the message "Your wage is: " followed by the actual wage that
was keyed in.
2. Write a program to ask for a student's grade, accept the response, then
repeat the grade with an appropriate message.
3. Write a program to ask a user to key in the number of seats in a room,
accept the response and display the number keyed in.
8
1.8 Strings
Many items of data - for example names, addresses, etc. - consist of several
characters grouped together to form a piece of text. These are known as strings.
allocates enough space to store a ten character student name - together with an
end-or-string marker.
To ask the computer to accept a string keyed in by a user, scan! is used with %s to
specify that a string will be input.
Note that NO ampersand (&) is used whenscan!is used with %s (unlike %c, %d,
%!- as seen in the previous lesson).
The scanf command assumes that a space, tab or newline character marks the end
of the string being keyed in. This means that scan! cannot be used to accept a
string of characters which might include spaces (e.g. an address). Where a string
may contain a space, the gets command can be used instead . The gets command
takes a newline character as the end of the input string.
will accept any string (including one containing a space) and store it in
studentname.
In either case, the user should take care to key in no more than the number of
characters set aside for the variable - any more may be accepted and stored in
memory spaces not allocated, perhaps overwriting important information and
even 'crashing' the system.
Note, however, that there may be problems if gets is carried out at any point in a
program after scan! has been used,' this will be explainedin Lesson 1.38.
To tell the computer to display a string, print! can be used - again with %s to
indicate the type of output.
Exerci...ies
1. Define variables to hold:
a) a name (up to 20 letters); b) an address (up to 60 characters);
2. Write a program to ask for and accept a student's name, address and mark,
then display the information again on the screen.
9
1.9 Assignment
e.g .1 num1 = 5;
places 5 in the integer storage location numl,
e.g.Z grade = I AI ;
places the electronic code for 'A' (see Appendix E) in the character location
grade . Note that when referring to a character - e.g. a letter of the alphabet -
single quotation marks' , are placed around it.
It is possible to initialise (i.e, give a starting value to) a variable at the same time
as it is defined.
sets aside a character storage location called grade and initialises it to the value
'C'.
sets aside a floating point location called wageJate and places to.50 in it.
Exercises
Write instructions to:
1. place 5 in the integerlocation numberl;
2. set up a character variable a and initialise it to 'A';
3. copy the contents of x to y.
10
1.10 Arithmetic
instructs the computer to add the contents of the memory locations basicpay and
overtime-pay and place the answer in total wage.
The normal rufes apply as to the order in which arithmetic operations are carried
out: MULTIPLICATION, DIVISION and MODULUS are performed first
followed by ADOmON and SUBTRACTION.
Exercises
1. What value will be placed in the memory space called x as a result of each
of the following statements ?
a) x = 3 +5 b) =
x 5/2 c) x=5%2
d) x = (3 + 5)/2 e) x = 4/2 + 8/4 1) x=3*2
2. Write programs:
a) to ask for a student's name, maths mark. English mark and
computing mark and then display his name and average mark.
b) to ask for an employee's name and annual pay; calculate tax for
year at 30% of pay; work out net annual pay and finally display a payslip
showing name, gross weekly pay, tax for week and net weekly pay.
c) to ask for an employee's name and wage; then give him an increase
of 20% and print out the name and new wage.
d) to ask for the figure for the total bonus to be divided among staff,
and the number of staff; then display the amount of bonus for each
employee.
e) to ask for the length and width of a room; then calculate the floor
area and display all the room's measurements.
11
1.11 Arithmetic using Assignment Operators
If you want to carry out arithmetic on the contents of a memory location - placing
the answer back in the same location - then it is convenient to use assignment
operators.
Those available for arithmetic are:
+= -= *= 1= %=
Exercises
1. If x starts off as 20, calculate its value after each of the following
commands :
a) x += 5; b) x -= 2;
c) x "'=2; d) x/=5;
e) x %= 3; e) x += y; (assume y contains 10)
2. Using assignment operators, write instructions to:
a) increase pay by 100;
b) increase pay by 20% (Note - to increase pay by 20% - simply
multiply by 1.20).
c) halve price;
d) find the remainder after sharing bonus among 20 people .
e) reduce amount. due by 200.
3. Write a program to ask for an employee's pay, increase it by 25%
(multiply by 1.25) and display the new pay.
4. Write a program to ask for the price of an item - increase it by £10 • and
display the new price.
5. Write a program to ask for the price of an item and the price increase , then
calculate and display the new price.
12
1.12 Incrementing and Decrementing Variables
The most efficient way to add I to or subtract I from the contents of a variable is
to use the increment (++) and decrement (--) operators.
Both of these operators can be used as prefixes (i.e. attached to the front) or
suffixes (i.e. attached to the end) to the name of a data item in order to change its
value.
So count++ and ++count
both have the effect of adding I to the contents of the memory space called count.
While the effect of using these operators as prefixes or suffixes is exactly the
same in simple commands such as the examples shown - they have different
effects in more complicated statements.
For example x = count++ means copy the contents of count to x, then
increment count. So if count started off as 10, then after the above statement, x
would contain the old value of count (10) and count would contain I I.
13
1.13 Using if.. else (1)
Sometimes you might wish to program the computer to take one course of action
in a certain case - and a different course of action in another case. To do this the
if .. else statement is used.
For example, you may wish the computer to display "PASS" if a student gets 50
or more in an exam, and "FAn:' if the mark is less than 50 - this could be written
as follows:
Note
I. Brackets are placed around the condition.
2. A semi-colon finishes each statement.
(i.e, anyone who gets less than 80 or less will not get a prize - so no action is
needed .)
The following Relational Operators may be used with the if .. else statement:
is greater than >
is less than <
is greater than or equal to >=
is less than or equal to <=
equal to
is not equal to !=
Do not confuse the relational operator == (which compares two items to check
=
whether they are equal) with the assignment operator (which makes a variable
equal some value - see Lesson 1.9).
Exercises
I. Write a program which will ask an employee for his/her length of service
(in years); if the length of service is more than 5 years, the computer
should display the words "bonus payable £100" - otherwise the words
"bonus payable £20" should appear.
2. Write a program which will ask a student for his/her marks in an exam. If
the mark is less than 10, the message "You must resit the exam" should
appear, otherwise the computer should congratulate the student on his/her
mark.
14
1.14 Using if.. else (2)
A compound if .. else may be used where there are several possible options.
e.g.I
i f (mark >= 85)
printf("Distinction\n"l;
else if (mark >= 65)
printf("Merit\n");
else if (mark >= 50)
printf("Pass\n");
else
printf("Fail\n");
Each condition will be tested in tum until one is found to be true - whereupon the
relevant command will be carried out, and then control will drop to the statement
after the group of if .. else statements (in this case the instruction to display the
actual mark).
e.g.2
i f (age >= 30)
if (length_of_service >= 10)
bonus = 5000 ;
else
bonus = 3000;
else
if (length_of_service >= 10)
bonus = 4000;
else
bonus = 1000;
In this case, employees are divided first into two groups according to age. Once
this has been done, then their bonus depends on length of service. So, for
example, someone of 35 with 5 years' service with the firm gets a bonus of 3000.
NOTE THAT THE INDENTATION HAS NO EFFECT - (It just makes the
program easier to understand). THE EFFECT OF THE SENTENCE IS
GOVERNED BY THE RULE - each else is paired with the most recent if that
has not already been paired with an else.
Exercise
1. Write a program to accept a student's mark and display a grade. (If the
student got 85 or more - then Grade A should be displayed ; if 65 or more -
then Grade B; if 50 or more - then Grade C; ifless than 50 - then grade F.)
2. Write a program which will ask a student for his/her grade ('P' or 'F') in
maths and English. If a student passed in both subjects - then he/she is
given a "Distinction". If the student passed in one subject - then he/she
gets a "Pass" . Otherwise "Fail" is displayed.
15
1.15 Using if.. else (3)
For any mark of 80 or over, the computer will display "Distinction" and ask what
prize the student would like; marks lower than this are divided into passes and
fails. The statement printj("%d",mark) is outside the if.. else groups of
statements and so applies to all students and every mark is printed.
Exercises
1. In Example 2 above. what would be displayed on the screen for each of
the following marks?
a) 40 b) 41 c) 70 d) 75 e) 10 f) 60
2. Write instructions which will carry out the instructions in the following
decision table:
CONDITIONS :
English Mark >= 50 y y N N
French Mark >= 50 y N Y N
ACTIONS :
Display "c r e d i t" X
Display "pass" X X
pisplay " f a il " X
16
1.16 Using Logical Operators (&&, II, !)
(i.e. the student has to get 50 or more in either one of the exams to pass.)
In some cases - a particular condition must not be true for an action to be taken -
this can be expressed using! (meaning not) - placed before the condition it must
negate.
(i.e. the essay is a pass if the number of spelling errors is not greater than 10.)
Sometimes it is more complicated - perhaps a student takes two exams and can
pass by getting 50 or more in both exams or by getting 80 or more in either one.
This can be expressed using a combinations of &&s and lis.
If there are several conditions, it is usually clearer to use brackets to show which
ones should be evaluated first - otherwise the assumption will be that ls are
carried out before &&s, which in tum are carried out before lis.
Exercises
Express the following conditions:
1. a student will pass if he/she gets 50 or more in all three examinations;
2. a student will pass if he/she gets 50 or more in anyone of three
examinations;
3. a student will pass if he/she gets 50 or more in both exam 1 and exam 2 or
in exam 3 alone;
4. a student will pass if he/she gets 50 or more in any two of three exams.
17
1.17 Using switch
The switch command may be used instead of if..else and is particularly useful
whenever there is a long list of possible courses of action which could be taken,
depending on some value stored in a variable.
In the example above, the variable coursecode will be checked and action taken
accordingly (e.g. when course-code = I, then "Engineering" will be displayed,
etc) , If the, course_code has any value not specified, then the message "Invalid
Course Code" will be displayed.
The break command causes the computer to exit from the switch group of
statements immediately it has dealt with the relevant case. If the break command
were not included, the computer would go on to carry out the actions relating to
the cases which followed. The last option - in this case the default - does not
really need a break at the end, but it avoids errors if you later add statements and
forget to place a break here.
Exercises
Using switch
1. Write a program which will ask a student for his/her mark (out of ten) and
display "Grade A" for a mark of 8 or more : "Grade B" for 6 or 7: "Grade
Coo for 4 or 5: "Grade D" for any other mark.
2. Write a program which will ask an employee to key in his/her job grade
('A', 'B', 'C' or 'D') and which will then display the appropriate wage
(£500. £300, £200, £100 respectively).
18
1.18 Repetition using while
e.g.I. count = 0;
while (count < 10)
(
printf ("Hello\n");
count++;
)
This will display "Hello" ten times down the screen. Note that the group of
statements which have to be repeated are enclosed in braces {} .
This will repeatedly allow students to key in their marks-out-of-ten and tum them
into percentages - until'N' is typed in reply to "Any more ...".
Note that· with a while loop ('loop' means 'repetiJion') - the condition for
continuing is checked before starting each repetition.
It is important to ensure that the variable being checked has a suitable value
before starting the loop first of all. Setting the variable to some suitable starting
value is called 'initialising' it.
Exercises
1. Write a program which will repeatedly ask for a number. double it and
display the answer until a 0 is keyed.
2. Write a program which will display the numbers from 5 to 15 across the
screen.
3. Write a program which will repeatedly ask a user for an employee's annual
wage - then display the weekly wage and ask the user if there are any
more to calculate - stopping when 'N' is keyed.
19
1.19 Repetition using do .. while
e.g.l do
{
printf ("Key in your maths mark\n");
scanf ("%d", &mathsJl\ark);
printf("Key in your English mark\n");
scanf( "%d", &englishJl\ark);
average_mark = (english_mark + maths_mark)/2;
printf("Your average is %d\n" , average_mark);
printf("Any more to calculate ?\n");
scanf ( " %c", &carry_on) ;
while (carry_on != 'N') ;
The instructions will be carried out once and then repeatedly until the user keys in
'N'. Note that as the test does not come until the end (by which time the user will
have keyed in a response), there is no need to initialise the variable carry_on .
e.g.2 count = 1;
do
(
printf("%d\n",count) ;
count++ ;
while (count <= 100);
The computer will display numbers from I to tOO down the screen.
Exercises
I. What might be the advantages of using do .. while in example l above .
(Consider the alternative. )
2. Do again the Exercise Programs from the previous lesson - using do ..
while instead of while.
20
1.20 Repetition using for
If you want a certain part of your program to be repeated a given number of times
the for statement is particularly useful as, unlike the while and do .. while
commands, it can increment a count automatically.
The computer will repeatedly display "Hello" on the screen • adding 1 to the
variable count after each repetition and continuing as long as the condition (count
<= 10) is true. So the message will be displayed 10 times.
e.g.2 for (number = 0; number <= 100; number +=10)
printf ("%d\n", number);
The variable being used to keep count is incremented by 10 each time - with the
result that the numbers O. 10. 20. 30. etc. will be displayed.
Exercise
1. Write a program which will display the numbers 1 to 7 on the screen.
2. Write a program which will display a short message (make up your own)
15 times on the screen.
3. Write a program which will display the numbers from 100 to 1000 in steps
of 100.
21
1.21 Functions
It is usual to divide a program into junctions. Each function will be given a name
- made up by the programmer - and will contain commands telling the computer
how to carry out a particular section of the job.
The main program function can then delegate work to the other functions simply
by calling them.
For example, the task of drawing a sailing boat could be divided into two parts:
l. draw the sails and mast;
2. draw the hull.
The commands telling the computer how to do these jobs could therefore be split
into two separate functions and the main function will simply call these functions
in the correct order.
This makes the program clearer to follow as the main function will only contain a
small number of instructions - the detail of how to do each job will be contained
in the function definitions.
This example prototype states that one function called drawline will be defined
later in the program. The two voids indicate that no data will be passed to or from
the function.
These Function Prototypes are used by the compiler so that it knows what to
expect when checking the rest of the program and can flag an error if the function
is used incorrectly later on.
e.g.3 main ( )
(
main instructions
)
4. definitions for the other functions used: each definition consists of the
function's name and other details (compare with the prototype) - followed by the
instructions which make up the function - enclosed within braces { }.
22
Example Program
tinclude <stdio.h>
void draw_sails(void) ;
void draw_hull(void);
main()
(
Each function is called as it is required - i.e. work is delegated to the function.
draw_sails() ;
draw_hull ( ) ;
return 0;
void draw_hull(void)
(
printf(" **********************\n");
printf(" *********************\n");
When the program starts, the computer will follow the instructions in the main
function until it comes to a call to another function - in this case, draw_sails .
It will then refer to the function definition for draw_sails and carry out the
commands contained within it - (in this case all the prinifstatements).
The computer will return to the main function at the place where it left off, and
will carry on through main until it sees another call to a function - in this case this
comes immediately with a call to draw_hull - and the same process will happen
again with this function.
Eventually, the program will reach the return 0 statement at the end of main - and
will then finish .
Exercises
1. Write a program to draw a house - using separate functions for the roof
and walls .
2. Write a program to draw a desktop computer - using separate functions to
draw the screen, computer casing and keyboard.
23
1.22 Functions - Local Variables
A function will often need memory locations to hold data that it is processing.
These will normally be defined locally within the function which will use them
and are known as local variables. One function cannot directly see or modify the
contents of local variables belonging to any other function .
For example, in the following layout, the variable x belongs to main and cannot
be used for storing data by either function] or function2 . Similarly, y can only be
used by function], and z by function2 .
void functionl(void) ;
void function2(void);
main()
(int X;
Main program instructions
void functionl(void)
(int y;
Function] instructions
void function2(void)
(int z;
Function2 instructions
The main function of the following program checks which course a student
wishes to take. If the student chooses the beginners' course, main immediately
accepts the student onto the course but if he/she chooses the advanced course,
main then delegates to function check_qualifications the task of deciding whether
the applicant is sufficiently qualified.
Example Program]
#include <stdio.h>
void check_qualifications(void);
main ( )
{int course_applied_for;
printf ("Which course do you wish to join ?\n");
printf( "Key 1 for Beginners or 2 for Advanced\n");
scanf ( "%d", &course_applied_for);
24
if lcourse_applied_for == 1)
printfl "Accepted for Beginners course") ;
else if lcourse_applied_for == 2)
check_qualificationsl);
else
printf l "Error - wrong course code");
return 0;
void check_qualificationslvoid)
(char c_studie~before;
printfl"Have you studied C before ?");
scanf (" %c", &c_s tudied_before) ;
if lc_studied_before == 'yO)
printf l "Accepted for Advanced course\n");
else
printf ("Try the Beginners course\n");
Note that two variables in different functions may be called by the same name -
they will still be two entirely separate storage locations.
Exercises
I. Set out a program structure with four functions:
main - which uses a character variable called salary grade; function]
which uses an integer location called tax_allowance; functions which
uses two floating point variables called salary and tax; and functions
which uses no storage locations.
2. Write a program which asks a user whether he/she wants to draw a square
or a triangle and stores the reply ('S' or 'T') in a character variable called
shape required - local to main. The task of drawing the chosen shape is
then delegated either to function square or function triangle. The
individual function called then asks the user what character (e.g. ''''')
should be used for the shape's border - stores the reply in a suitably named
local variable and then draws the shape as requested.
25
1.23 Functions - Passing Parameters
Two separate functions do not automatically have access to each other's data area
in memory. However, when one function calls another, it may need to pass across
information for the latter to use.
This is done, when a function is called, by specifying the data which has to be
transferred in the brackets following the function's name.
The function definition must have corresponding entries telling it what sort of
data to expect and allocating temporary locations where it can store the
information that it receives.
The data items passed across in this way are known as parameters. In particular,
the data actually passed across are known as actual parameters or arguments -
while the definitions of the data expected by the function are known as formal
parameters.
The following example main function delegates the work of displaying a number
of asterisks on the screen to a function called showstars - passing across the
number of stars required as a parameter.
Example Program J
#include <stdio.h>
void shows tars (int) ; The function prototype alerts the compiler
that function showstar« will expect to receive an item of
integer data and will return no data (indicated by 'void'
before the function name) to the calling function (main in
this case).
main()
{
shows tars (5) ; Function showstars is called - passing
across 5 as the number ofstars that are to be displayed.
return 0;
Note that if the function needs any working storage locations these will need to be
defined within the function - e.g count in the example.
When the example program runs, the computer will start working through the
main function and will find that the first command is a call to showstars. It will
then refer to the function definition of showstars for further instructions.
26
Temporary memory spaces will be allocated for use by showstars - as specified 10
the function definition . The first of these, called number_of-stars, will receive
the value (5) passed across from function main. The second, count, will be used
as a working location to keep track of how many stars have been displayed.
Showstars then uses afor loop to display the requisite number of stars.
When showstars has finished its task the computer refers back to the main
function where it left off. As this happens, any temporary storage locations set up
within function showstars are deallocated and the data in them is effectively lost.
The computer would carry on through main if there were any further commands.
In this case, it reaches the end of main and the program finishes.
So, the previous example could be adapted to aUow the user to key in the number
of stars required . (Only the main function will need to be changed - as showstars
is already designed to display any number of stars passed across.)
main()
(int number_required;
Exercises
1. Write a program which asks the user how many greetings should be
displayed then - using a function called greet - displays "Hello" on the
screen the requisite number of times.
2. Write a program which asks the user to key in 'P' (for 'Pass') or 'F' (for
'Fail') in answer to the question - "What was your result in the Computing
Exam?"
The character keyed in should be passed to a function which will display
an appropriate message (e.g. "Well Done !" or "Work harder next time"
depending on the result.
3. Write a program which asks the user to key in a grade ('A' to 'E').
The main function should then pass the grade to a function which will
display a suitable message as follows (e.g. "Excellent", "Good", "Fairly
Good", "Satisfactory", etc).
27
1.24 Functions» Using return
A function often has to pass information back to the main funct ion. One way to
do this is to use the return command at the end of the function.
The following example delegates the work of adding two numbers to function
total, which then passes the answer back to the main program .
Example Program J
*include <stdio .h>
int total (int, int); The prototype indicates that two integer
values will be passed to junction totol which will then
return an integer value to main. (The type of return value
is indicated by the inJ before the junction name.)
main ()
{
When the example program runs the . computer will start to carry out the
instructions in main. So, it will start the printf command - prepare, as instructed,
to display a decimal integer number and then realise that the last part of the
statement makes a call to function total to find the number that must be displayed.
As the function is called, three memory locations will be set aside for it - numl ,
num2 and ans (as stated in the function definition) - and the values (10 and 20)
passed across as arguments are placed in the first two of these locations. The
variable ans will be used as a working location to store the result of the
calculation.
The function total then adds the contents of numl and num2, places the answer in
ans and then returns the value of ans to the calling function (main).
The function then ends and control passes back to the main function at the same
point it was left. So, the number returned by the function is displayed on the
screen by the printf command.
28
To avoid the need for the extra working location ans, the calculation could be
carried out within the brackets following the return command:
return(numl + num2);
Example Program 2
iinclude <stdio.h>
char grade ( int) ; The prototype indicates that one integer value
should be passed to function grade which will then return a
character value to the callingfunction mai".
main()
{int mathsJllark;
printf ("Please key in your maths mark ");
scanf ("!tid" I &mathsJllark);
printf ("Your grade is !tic" I grade (maths_mark) );
return 0;
Exercise
1. Using a function to perform the calculations in each case, write programs
to display :
a) the sum of three numbers (10,20.30);
b) the average of 2 integer numbers keyed in by the user;
c) the average of three floating point numbers keyed in by the user;
2. Write a program which asks the user to key in hislher mark, then (using a
function which accepts the mark and returns the grade) decides on and
displays a grade on the basis: a mark of 85+ gives grade 'A'; 65+ gives
'B'; 50+ gives 'C'; otherwise the grade will be 'F'.
29
1.25 Functions» Repetitive Calls
A function may be called several times. For example, in the following program,
function draw_square is called twice (to display two squares) .
Example Program J
#include <stdio.h>
void draw_square(void);
main( )
{
draw_square ( ) ;
draw_square();
return 0 ;
It is also possible 10 call a function repeatedly by placing the call within a loop.
The following example asks the user how many triangles he/she wants displayed
and then uses a for loop to call draw_triangle repeatedly .
Example Program 2
#include <stdio.h>
void draw_triangle(void);
main()
lint count, num;
printf("How many triangles do you want displayed?");
scanf ( "%d" I &num);
return 0;
void draw_triangle(void)
{
printf ( " * \n" ) ;
printf("* *\n");
printf("* *\n") ;
printf("* * * *\n");
30
A function requiring parameters may be called several times with different values
each time. The following program calls greet twice - the first time passing across
5 as the number of "Hello"s to display and JO the second time.
Example Program 3
#include <stdio.h>
void greet(int);
main()
(
greet(5);
greet(lO);
return 0;
Example Program 4
#include <stdio.h>
char grade(int);
main()
{int maths_mark, english_mark;
printf ( "Please key in your maths mark ");
scanf ("%d", &maths_mark);
return 0;
31
1.26 Functions· Passing Work on to Other Functions
A function may delegate work to other functions. For example, in the following
program, main delegates the task of drawing a vase on a table to two functions -
draw_vase and draw_table. However, draw_table in tum passes on the work to
two other functions - draw_table_top and draw_table_legs.
Example Program I
.include <stdio .h>
void draw_vase(void);
void draw_table(void) ;
void draw_table_top(void);
void draw_table_legs(void);
main ( )
(
draw_vase () ;
draw_table ( ) ;
return 0;
void draw_vase(void)
(
printf(" **********\n");
printf(" * *\n") ;
printf (" * * \n" ) ;
printf(" ****\n");
void draw_table(void)
(
draw_table_top();
draw_table_legs();
void draw_table_top(void)
(
printf(" \n");
printf(" ************************\n");
void draw_table_legs(void)
(
printf ( " ** **\n") ;
printf (" ** **\n") ;
printf(" ** **\n");
printf(" ** **\n") ;
printf(" ** **\n") ;
printf(" ** **\n") ;
32
A student's grade on a course depends on hislher average mark over the two final
exams . In the following program, the main function obtains the student's details,
then displays a report - delegating the work of deciding the grade to function
studentgrade - which in tum passes on the task of calculating the average mark
to function averagemark,
Example Program 2
Exercises
1. Write a program which will draw a multi-storey office block. The main
function should ask the user for the number of storeys required, then call
functions roof, walls and ground. Function walls should call function
storey (which will draw one storey of the building) the appropriate number
of times using afor loop.
2. Write a program which will calculate an employee's wage - which is made
up of basic pay and commission. The main function should obtain the
employee's name , age, number of GCSEs and sales for the year. It should
display a payslip, delegating to salary the task of working out the pay.
Function salary should use functions basic-pay (which will calculate
basic pay on the basis that every employee gets £l()()()() plus £100 for
every year of their age) and bonus (which is £5 for every £100 of sales).
33
1.27 Recursion
Example Program
#include <stdio.h>
main()
{int num;
/ *Fi n i s h */
return 0;
Exercise s
I. In maths - any number followed by an exclamation mark means multiply
all the numbers up to that number together - so 5f (pronounced 5 factorial)
means 5 x 4 x 3 x 2 x 1. Write a program which calculates x! - where x is
any whole number keyed in by the user.
2. Drawing a line of 5 asterisks can be thought of as being made up of
drawing one asterisk followed by a line of 4 asterisks. Write a program
which uses a recursive function to tackle this problem for any given length
of line.
34
1.28 Global Variables
The contents of global variables (also called external variables because they are
defined outside any function) can be read or modified by any function in the
program (or program module).
This can be useful where several functions need to be able to alter the values
stored in a variable - and avoids excessive parameter passing. However, when
global variables are used, it is important to check the whole program with great
care to ensure that no function modifies the data by mistake.
A variable can be made global by defining it outside any function - usually near
the beginning of the program.
For example. in the following program layout - the variable a can be seen and
used by main.functionl andfunction2.
void functionl(void);
void function2(void);
int a;
main()
{
Main program instructions
}
void functionl(void)
{
Functionl instructions
}
void function2(void)
{
Function2 instruct ions
}
Note that a local variable and a global variable may have the same name - in
which case the local variable is assumed to be referenced whenever it is
mentioned within the function where it is defined.
35
Example Program
iinclude <stdio.h>
void get_student_details(void);
void display_report(void) ;
/ftSet up global variables to hold student's name and
mark ft/
char student_name[l6] ;
int student-mark ;
main ( )
(
get_student_details();
display_report();
return 0;
Exercises
Using global variables and functions - write programs to:
1. ask for an employee's name and annual salary - then display a payslip
showing the name and weekly wage.
2. ask for a student's name and grade. then display a report.
36
1.29 Static Local Variables
Most local variables are automatic - i.e, they are allocated when the function is
called and vanish when the function ends. Sometimes a function must be able to
hold data between calls . In this case, the relevant local variables must be classed
as static.
A static variable may be set to a starting value - so that the contents is known the
first time the function is called.
The following example program allows a bank clerk to allocate unused account
numbers to new customers.
Example Program
iinclude <stdio.h>
main ()
{char any_more;
do
{set_up_account_number() ;
printf ( "\nAny more customers ?");
scanf I" %c", &any_more) ;
} while (any_more !='N ') ;
return 0;
}
void set_up_account_numberlvoid)
{/*Set up static variable and initialise it to 0*/
static int account_number = 0;
/*Give new customer an account number*/
printf("\nNext account number :%d",account_number);
/*Increment account_number ready for the next
customer*/
account_number++;
Exercises
Write a program which calls a function called greet which displays "Hello" and
the number of greetings so far.
37
1.30 Using #define
Some values in a program are constants - i.e. they remain the same every time the
program is run (unless the program is changed to cope with new situations). For
example, in a program that decides whether a student has passed an exam on the
basis of what mark he/she achieved, the mark needed to get a pass is constant - it
will remain the same for every student (unless the exam rules change and the
program is then changed to correspond to the new rules) .
To make the program more readable (and more easily modified), the Hdefine
directive can be used to give any constant a name. Such a named constant is
called a symbolic constant - because a symbol (name) stands for a constant value .
Once the definition has appeared in the program (this is usually placed near the
top) the programmer can write the word PASS_MARK instead of the number 50.
So, the command from e.g. I can now be written as follows -
Just before the program is compiled , the C Preprocessor will check through the
text for any names that have been Hdefined and will substitute their actual value
into the program. So, for example, the preprocessor would look at the code for
e.g.3 above - note that PASS_MARK had been defined as 50 - and replace the
name every time it occurred with the value 50. So, the code for e.g.3 would
become as in e.g. I. before compilation.
Any item in the program which remains constant every time the progam is run
can be given a name. So, for example a message could be given a name.
So, a command to display the word "Hello" could then be written as:
Before the program is compiled the preprocessor will substitute the message
"Hello" wherever it finds the name GREETING in the program text.
Note that it is usual (although not essential) to use capital letters for the names
given to constants.
38
Example Program
tinclude <stdio.h>
main ( )
(char employee_grade, employee_name(21l ;
int wage;
/*Calculate pay*/
switch (employee_grade)
(
case MANAGER: wage = 500; break;
case SUPERVISOR: wage = 300; break;
case ASSISTANT: wage = 200 ; break;
/*Display Payslip*/
printf (COMPANY_NAME) ;
printf ("%s\n", employee_name);
printf ("Wage: %d\n", wage);
/*Finish*/
return 0;
}
Exercises
1. Modify the Example Program so that A & B Stores can use it.
2. A & B Stores introduce a new grade of employee Senior Supervisor
(Wage £400 per week). Alter the program to take this into account.
3. Making use of #define to give names to constants where appropriate -
write a program to ask for a student's name and percentage mark, decide
on a grade, and display a report. The grades are: Pass (for a mark of 50 -
64); Merit (for a mark of 65 - 84); and Distinction (for a mark of 85 -
1(0).
39
1.31 Using enum
The programmer may use enum to set up a group of symbolic constants - each
having some integer value.
e.g.1 enum {PASS_MARK = 50, MERIT_MARK = 65}
Where no specific values are given to the constants, values are assigned
automatically - starting with 0 for the first name, J for the next, and so on.
defines the names ZERO, ONE,TWO ...NINE as representing the integer values 0,
J, 2, ..9 automatically .
If a value is specified for one identifier, then those that follow it receive the next
numbers in sequence.
gives the values 1,3,3,4,5 to the names of the chess pieces listed.
Any symbolic constant defined in an enum expression may be used within the
program instead of the value it represents - just as with symbolic constants set up
using #define. The identifier will always be replaced with its defined numeric
value before compilation.
e.g.5 if (mark >= MERIT_MARK)
printf ( "Merit\n") ;
else if (mark >= PASS_MARK)
printf ("Pass\n");
else
printf ("Fail \n") ;
40
Enumeration may be used to set up an integer data type (or a memory space)
restricted to the values represented in the list.
sets up a new data type called days_oJ-week which consists of any of the numbers
1 to 7 (represented by the symbolic constants listed).
This data type may then be used to allocate storage space to hold any of these
values.
e.g.9 enum days_ot_week today;
sets up a memory space called today which should be able to hold any number
within those defined by enum days_oJ-week (i.e. 1 to 7).
Within the program, a value may be placed in today, either using a symbolic
constant or using an actual number.
So, today = 2; and today = MON; are equivalent.
sets up a data type called boolean which may take the values 0 or 1, a variable
called end_o/Jile which should hold one of these values - and defines symbolic
constants FALSE and TRUE which may be used to represent the numbers and
thereby make the program easier to understand.
Again. a value may be assigned to the variable using either the number or the
identifier. So, end_o/Jile = 1; is equivalent to end offile = TRUE;
Note that a name defined as a symbolic constant using enum must not be defined
more than once within the same scope. (It will be all right if a name is defined
with different values in separate functions - but not within the same fun ction)
Exercises
1. Salaries in a firm are - managers - 25000, supervisors - 18000, clerks -
15000. Use enum to define suitable symbolic constants to represent these
figures; set up a data type - salaries and a variable salary and use these in
a program which will allow a user to key in a code for the type of
employee and display the wage for the week.
2. Use enum to define the measures of distance YARD = 1, CHAlN = 22,
FURLONG = 220, MILE = 1760; set up a data type called
measure_oJ-distance and a variable called length_oJ-road; the length of a
particular road is 1 mile. 2 furlongs, 4 chains and 5 yards- assign this sum
to the variable length_oJ-road and display the total.
3. Use enum to define the measures of weight - POUND = I, STONE = 14,
HUNDREDWEIGHT = 112, TON = 2240; set up a data type
units_oCweight and a storage location called weighcoCgoods; write a
program which will ask a user to key in an item's weight in pounds then
display the answer in tons, hundredweight, stones and pounds - make. usc
of the symbol ic constants to make the program easier to follow.
41
1.32 String Functions
The standard library includes several functions to manipulate and check strings.
The header file string.b must be #included in your program to allow these
functions to be used.
The function strcmp compares two strings. It returns 0 if the two are the same; a
value greater than 0 if the first string is the higher (alphabetically); and a value
less than 0 if the first string is the lower.
Exercises
1. Write a program which will ask an employee to key in his/her job title (in
upper-case letters) and then display the salary - on the basis that managers
are paid £30000, deputy managers are paid £25000, supervisors - £17000
and clerks - £14000.
2. Write a program which will ask two employees for their names, then
display them in the correct alphabetic order.
42
1.33 Blocks
The for loop will apply to everything in the braces that follow it - so the block of
two commands (to display Hello and Goodbye) will be repeated 20 times.
If the braces are removed, the for loop only applies to the first print! and the
message Hello will be displayed 20 times - but the message Goodbye will only be
displayed once - (when the Hellos have all been displayed).
e.g.2 i f (mark>=50)
{
printf ("Pass\n");
printf("Collect a Prize \n") ;
Here the if will govern the whole block that follows it - and if the student's mark
is 50 or more then the messages Pass and Collect a Prize will both be displayed
(and if the mark is less than 50 - neither message will be shown) .
If the braces are removed, then the if will only govern the statement that
immediately follows (i.e. printf("Pass\n");) - so those students who get a mark of
50 or more will have Pass displayed but all students will be told to collect a prize
whether they passed or not!
Any block of code may contain local variables which are only accessible to
instructions within that block. A local variable should be defined at the top of a
block. These can be useful for working locations and counts which are not
needed outside the block.
uses the local variable count to measure the length of a line of asterisks. Note that
the variable length is not local to this block.
Exercises
1. Write a program which will display a square of asterisks to the size
specified by the user. Use local variables within blocks where
appropriate.
2. Write a program which will display a triangle to the user's requirements.
Again use local variables where appropriate.
43
1.34 Using breaklcontinue/exit/returnlgoto
Each of the commands break, continue, return, exit and goto offers a way to leave
the processing being carried out at the time.
break - tells the machine to leave a switch statement, or a while, do..while, or for
loop - and start the next section of program.
The machine will let the user key in ten characters - displaying each letter - unless
an 'A' is found - when it will break out of the loop and start the next piece of code.
continue - tells the computer to leave the current repetition of a while, do .. while,
or for loop and start the next one.
The computer will accept and display ten characters keyed in by the user one by
one - except that when an 'A' is encountered it will not display it but instead go
back to the for command - so that count is incremented and so on to the next letter
- until all the letters (apart from the 'A's) have been displayed.
return - tells the computer to leave the current function and go back to the point
it left in the calling function. The return command may be followed by a value or
expression. A return encountered in the main function tells the computer to leave
the program. In this case, it is usual to follow return with the value 0 - to indicate
that the program finished with no problems - or 1 to indicate that it was
unsuccessful. See Lesson 1.24.
error_routine:
/*Deal with problem*/
44
exit is a function available from the standard library (by #including the header file
stdlib.h in the program). It tells the computer to exit from the program
immediately - wherever it is encountered. It is therefore a useful way of reacting
to major problems (once any vital processing has been handled) . It is usual to
follow exit with the argument 1 if there has' been an error - or 0 if there is no
problem.
/*Leave Program*/
exit(l);
Exercises
1. Write a program which will ask ten students for their names and grades
(P' or 'F') - displaying an appropriate message in each case - but will stop
if any student keys in 'Q' instead of the grade.
2. Modify the program so that it will, instead of stopping, just go on to the
next student.
45
1.35 Using printj• Formatting Output
The print! command can include detailed instructions regarding how a particular
data item should be displayed.
These layout indicators are included within the formatting string - between the %
sign and the conversion character (i.e. c, d.f, etc) for the relevant item.
will display the contents of number within a field ten spaces wide. This is useful
for setting data out in columns. Note that if the field specified is not large enough
for the value displayed - then a larger field will be used automatically.
will display the contents of number within a field whose width is governed by the
value stored infield_widthJor_number.
The precision required for display can be specified as a decimal point followed by
a number.
Again an asterisk * may be used in the formatting string to tell the command to
refer to a variable named in the argument list for the precision.
Precision may also be applied to a string - in which case it is used to limit the
number of characters displayed from the string .
Left Alignment
A hyphen (-) included in the formatting string will ensure that the corresponding
data item will be aligned on the left of its display field. This is important where a
minimum field width has been specified which is larger than the data item ; if left
alignment is not specified the item will be right justified.
46
e.g.S printf ("%-lOd", number);
will display the contents of the variable number aligned to the left of its ten space
field .
Any combination of these formatting indicators may be used - but must be in the
order: left alignment field-size precision.
will display the contents of wage. left aligned within a 10 space field, showing
two digits after the decimal point.
Exercises
1. Write a program which will tryout all the printf statements in the above
table.
2. Write a program which will display the names and wages for five
employees in a table. (Initialise five suitable string variables and five float
variables to suitable values to test the instructions.)
3. In the table above printf("%dvt", numl ) and printf("%- /Odvt", numl ) give
the same result. What is the difference and when would this be important?
47
1.36 Using scan!- Specifying Input Format
The scan! command can include instructions indicating the format in which data
should be expected to be keyed in.
The computer may be told to expect and disregard specific characters - by placing
these within the formatting string in their expected positions.
The user is expected to type in the date in the form - e.g. 3/3/95. The scan!
command will read the first number and store it in day, then read the / keyed in by
the user - see that it matches the character indicated within its formatting string
and discard it and go on to read the next number and so on.
The machine may also be told to expect and discard input of a particular type - by
placing an asterisk (*) before the conversion character.
This can be a useful facility to discard unwanted newline characters in the input
(see Lesson 1.38).
will read and store an integer - then read and discard the next character - which
would probably be the newline caused by the user pressing the <enter> key.
Likewise it could be used to give the user a choice as to how data will be entered.
will allow the user to key in the date with any single character separating the day,
month and year. So, either 2/3/95 or 2-3-95 would be acceptable.
will accept up to 10 characters and store them in name . (The string name should
have heen defined as at least ten characters long.) Any extra characters keyed
will be held in the buffer ready to be read by the next input command.
Exercises
I. Write a program which will ask the user to key in his/her name and marks
for Engli sh, Maths and Computing - in the form JONES /20/40/50 -
storing the marks in appropriate variables.
2. Write a program which will ask a user to key in the dimensions of a room
in the form - width 20/length 40/height 60 - storing the data in appropriate
variables.
48
1.37 Using putchar/getchar
displays the contents of the storage location grade. Note that grade may be
defined either as a cluu location or as an int - (in which case only the contents of
the lowest byte will be displayed as a character • and the remainder of the
variable ignored).
will get one character and place it in the variable grade. Again. grade may be
defined either as a char or into H the end of file (which 'will be denoted on a
UNIX system if <ctrl d» is typed) or an error occurs, getchar will return EOF (a
constant defined in stdio.h), which is represented by a bit pattern too large to store
in a char. So, if this is likely to be of concern, the variable should be defined as
into
Example Program
main ( )
{char letl;
return 0;
}
The behaviour of this program may vary between systems - because input from
the keyboard may be buffered (i.e. stored and not processed until <enter> is
pressed) or unbuffered (i.e. each character is processed immediately it is typed).
With unbuffered input - getchar will accept a character. echo it on the screen and
store it in letl; then putchar will display it again immediately. This process will
be repeated - until ,*, is keyed.
On most systems getchar deals with buffered input. The user might type several
characters - which will all echo to the screen and be stored in the buffer as they
are typed ; but nothing will be processed until the <enter> key is pressed . Then
getchar will fetch the first character from the buffer and place it in letl; putchar
will display it; this will repeat until an asterisk is found in the buffer.
Exercise
Write a program which will accept a message from the user - store it as a string -
then display the message on the screen. Make use of putchar and getchar.
49
1.38 Buffered Keyboard Input
When a program is running - every time the computer reaches a buffered input
command, it checks the buffer. If it can find suitable data, it will pick it up and
store it in the variable specified in the program statement, leaving any extra data
still stored in the buffer. If not, the computer will pause for the user to key in data
- which will be stored in the buffer. The computer will not go on with the
program until the user presses the <enter> key. Thereupon, the computer will
again look in the buffer for data to satisfy the input command. Note that whenever
the user presses the <enter> key - the special newline character \II will be stored
in the buffer.
scanf - in general , reads past any white space characters that have been typed
into the buffer (newline, spaces , tabs) to fmd the data it needs .
If the buffer contents is as above, the command scanft"%d", &numl) will ignore
the first \n - pick up the digits 210, convert them into pure binary and store the
value in numl, then leave the final \n in the buffer.
Similarly, the command scanf("%s", accountc ode} will ignore the first \n - then
pick up each of the digits (treating them as individual characters) and store them
in the string accountcode - again leaving the final \n in the buffer. Note that
when scanf is used with %s, any white space character marks the end of the
string.
However, if scan! is used with %c, the command will not read past any white
space characters. So, the command scanf("%c", &reply) will just pick up the first
character in the buffer - in this case \n - and store it in reply. To avoid this, the
%c can be preceded by a space - which again tells the command to read past any
white space characters - and then pick up the first character. So, scanfi" %c",
&reply) will pick up the 2 and store it in reply.
gets - does not read past any white space characters to find a string. It will simply
start with the first character in the buffer. If the first character is \n, the gets
command picks this up - but then, because gets sees \n as the end of an input
string, it stops. So, if the buffer has the contents as above, the digits will be left in
the buffer. Note that, unlike scanf, gets picks up the \II at the end of the string. It
does not, however, store it in the variable.
getchar - just picks up the first character in the buffer (including white space
=
characters). So, if the buffer's contents is as above, letter getchar() will pick up
the newline character and store it in the variable letter.
50
This various treatment of white space characters has to be considered when
writing a program.
When the computer reaches the first scant command, it will look in the buffer -
and if there is nothing there 'will stop and wait for the user to key in data. At this
point - the user can key in anything he/she wants - the computer will not stop the
input until the user presses <enter> . If we assume that the user just keys in an
answer to the first question and presses <enter> - then the buffer might contain
(if the employee's number is 1000) :
The scant command will then pick up the 1000 and store it in pure binary in
payroll_number - leaving the \n in the buffer.
The next input command gets will look in the buffer - find there is already data in
there and pick it up (without waiting for the user to key in any data). As it is \n,
the gets command will just store an end of string marker in employee name .
This can be avoided by inserting %*c after the %d (leaving no space between
them) in the previous scanf. This will tell the computer to pick up - but not store
one character after the number - which in this case will be \no The machine will
then pause after the message Name ? and allow the user to type the employee's
name - which will then be found by the gets and stored.
Exercises
1. Assuming the following buffer contents -
explain what will happen when each of the following pieces of code are run.
a) scanf ( " %d" &id_nurnber);
I
gets (name) ;
e) x= getchar () ;
scanf ("%s" name);
I
2. Write a program which will ask a teacher for a student's name (in full),
address, marks in English, maths and computing, grades in the same
subjects ('A' to 'E'), and a conunent (up to 80 characters).
51
1.39 Evaluating a Condition
Firstly, the contents of the brackets following the if, while or do..while command
is evaluated to a numeric value. A C program evaluates any relational expression
(e.g. mark >= 50) to the value 1 if it is TRUE; or to 0 if it is FALSE.
For example, in the following instruction, the relational expression (mark> 50)
will first be evaluated. It will be worth 1 if the mark is 50 or more - and 0 if the
mark is less than 50.
So, suppose the variable mark contains 70 - then the contents of the brackets will
be worth 1 and the whole statement will be equivalent to :
e.g.2 i f (1)
printf ( "Pass" ) ;
The if command then simply checks the value assigned to the brackets instead of
the conditional expression. If it is I, as in this case (or some other non-zero
value) then the command following the ifwill be carried out. Otherwise (i.e. if the
value is 0) it will not (and the else portion of the command will be carried out if
present) .
The programmer can take advantage of how ifworks by writing an actual value in
brackets after if - rather than a relational expression. If the value is non-zero the
command following will always be carried out - whereas if the value is zero the
command will not be carried out.
c.g.3 if (1)
printf ( "Pass" ) i
Every student will get a "Pass" message. Conversely, if the value in brackets
were 0, then no student would pass . In general, of course, this is not particularly
useful - as there is no point using if where the same action should always be
taken.
The value of mark /50 is calculated and the answer placed in exam.result. The
contents of exam.result will then be taken as the value of the expression and the
action taken will depend on this value. IT exam result has been defined as int, a
mark of 50 or more will result in a value of I or more being placed the variable
examresult. A mark of less than 50 will result in a value less than 1, which - if
examgrade is an integer variable - will be truncated to O. So, in that case the
message "Fail" will be displayed.
Notice the we of the assignment operator (=) in the above expression - NOT the
relational operator ( ) because the intention is to place a value in a variable -
not to compare two values to see if they are equal. Some compilers will issue a
warning asking you to check that this ls reallyyour intention.
e.g.6 i f (functl ( ) )
printf("Successful");
else
printf ("Unsuccessful") i
So - if functl is successful, the value in brackets will not be zero, so the command
following the ifwill be carried out and the message "Successful" will be displayed
- otherwise, the else branch of the command will be carried out and
"Unsuccessful" will be shown .
Alternatively, the return value from the function can be reversed using the NOT
operator!
e.g.? i f (! functl ( ) )
printf ( "Unsuccessful II) ;
If the value returned by functl is 0 - it will be turned into I by the ! and so the
message will be displayed.
The while and do..while commands work in a similar way .
So,
e.g.8 while ( 1 )
(
printf ("Hello\n") ;
}
e.g.9 num = 1 i
while (num)
{
printf("Key in a number vnv I :
scanf (" %d", &num) i
The user will be allowed to key in numbers until he/she types 0 - then the loop
will stop .
As long as functl returns a non-zero value the computer will keep displaying
"Success".
One might need to repeat a list of instructions until a function is successful. For,
example, assume a function gecaccno_and_check asks a customer for his/her
account number - checks that it is valid - and returns 0 if it is not.
The customer will be asked repeatedly to type in an account number until a valid
one is keyed. If a valid number is keyed then the computer will look past the
while loop - to see what to do next (e.g. ask the customer what they want to
order).
54
Example Program
iinclude <stdio.h>
main()
{int stock_code;
while(! (get_acc_no_and_check()))
{
printf ("Account number is not correct\n");
printf ("Please retype");
}
int get_acc_no_and_check(void)
(int account_num;
printf("\nPlease key in your account number");
scanf ("%d" &account_nwn);
I
Exercises
1. Write a function called pass which will accept a student's mark as a
parameter - and then return 1 if the mark is 50 or over; and 0 if the mark is
less than 50. Use this function in a program which will ask the student to
key in hislher mark and display the message "Well done" or "Try again"
depending on the return value from function pass .
2. Write a function valid accountnumber which will carry out a modulus
11 check on an account number - consisting of 5 digits plus the check digit
- and return 1 if the number is valid and 0 if not. Use this in a program
which asks a user to type in a valid account number and displays an
appropriate message if the number is not valid.
55
1.40 Data Types
Several standard data types exist apart from char. int and float. This allows the
programmer to specify suitably sized memory spaces for a wide variety of data.
It is important to note that - for the most part - the various data types in C simply
denote the size of the memory space. So, for example, if a variable is defined as
char it simply tells the compiler to set up a small memory space - just large
enough to hold the electronic code for a character; there is actually no restriction
on the space only being used to hold a character, and the space can be used to
hold the binary code for a small number if required. Likewise an int which sets
up a rather larger memory space may be used to hold the code for a character (but
this will only take up a part of the space available).
The programmer can choose how the data stored should be interpreted - for
example - by using %c or %d in a print! statement. So, if - for example - the
char variable Zener held the ASCII code for 'A', then the statement printj("%c",
letter) would display the character 'A' on the screen - whereas printj("%d". letter).
would display 65 (assuming the characters are held as ASCII Codes).
char/signed/unsigned
When defining the variable, the signed or unsigned modifier is placed before the
word char.
int/signed/unsignedRong/short
An int may also be specified as signed or unsigned - but in this case, if neither is
stated, the integer is assumed to be signed.
56
value from 0 to 65535, and a signed short int to hold a value from -32767 to
+32767 - the same as the minimum guaranteed for an into A short int is assumed
to be signed if this is not specified.
jIoaJIdoublello", double
A floating point number may be held more precisely as a double or long double -
which both guarantee 10 digits of precision as against the 6 digits guaranteed for a
float. All the floating point data types are guaranteed to hold a value between I E-
37to IE+37.
e.g.9 float num12;
double numl3;
long double numl4
When using print! or scanf, %/ should be used in the formatting string with both
float and double data types, and %If with long doubles. However, the standard
displayed precision is only 6 decimal places - so a higher precision may need to
be specified to take advantage of the double or long double data types.
e.g.to printf(I% .10f", numl2);
57
The following table lists the types and summarises the minimum requirements of
the ANSI C standard in relation to the data each type will hold.
As has already been mentioned, some systems will improve upon these minima,
and the actual ranges, etc., allowed by a particular system • are defined as
symbolic constants (see Lesson 1.30) in the header files limits.h and float.h, So,
.for example, (provided that the appropriate header file is #included in your
program) the command printft"%d". [NT_MAX) will display the value of the
largest number that the system will hold in a variable defined as into
Exercises
I. Write a program which will display all the data types with the maxima and
minima for your system - plus the precision for thejloat and double types .
2. Write a program which will display all the whole numbers from 0 to 4
million.
3. Write a program which will calculate 22f1 - and display the answer correct
to 8 decimal places.
58
2.1 Introduction to Program Design
In the same way, before writing a program it is important to plan it. This will
normally include listing:
1. the main tasks that your program will carry out;
2. the variables that your program will need to use to store data.
Example
Write a program which will take data about a student (name and marks for
English and Maths) then calculate an average mark and produce a report.
Tasks
This program could be split into 3 main tasks:
1. ask the user for the student's data and accept it;
2. calculate the average mark;
3. display a report .
The practice of breaking one large task into a number of smaller ones is known as
top-down design . It has the advantage that the programmer can concentrate on
one small task at a time, when he/she writes the program - making the work
easier.
Rather than making a list of tasks (such as the one above), Structure Diagrams are
often used to show the breakdown. These are considered in detail in the
following lessons.
Variables
The program will need string variables to store the student's names - so consider
how long these need to be - and integer variables to store the three marks
(English, maths and average). Make up names for these.
Exercise
For each of the following jobs, suggest a suitable division into smaller tasks and
write data definitions:
1. ask an employee for his/her name and annual salary, calculate weekly pay,
then display a payslip;
2. ask a student for hislher name, group and exam mark (out of 10), convert
the mark to a percentage and display a report;
3. ask a shopkeeper for the name of an item of stock, its price and how many
items have been sold that week - calculate the value of the sales and
display a report.
59
2.2 ProgramDesign - Sequences
A job may be broken into a number of smaller tasks carried out one after the
other. This is called a sequence.
PRODUCE
STUDENT
REPORT
Note
The overall name for the job is shown in the rectangle at the top of the diagram -
the smaller tasks it splits into are shown under this in the order that they are
carried out (from left to right).
Exercises
1. Produce structure diagrams for the exercises in Lesson 2.1.
2. What is meant by a sequence .,
60
2.3 Program Design «Iteration
If a main job can be broken down into a single simple task repeated a number of
times, this is called an iteration (means repetition).
PRODUCE
MESSAGE
SCREEN
24 times
Produce *
message
line
Note
The asterisk in the top right comer of the lower rectangle indicates that the
smaller task is repeated - and the number of times is shown above the rectangle.
GET NUMBERS
FROM
KEYBOARD
Exercises
Produce structure diagrams for each of the following tasks.
1. Clearing the screen consists of displaying 24 blank lines .
2. Homework consists of writing 100 lines saying 'I must work harder in
class'.
61
2.4 Program Design» Selection
If a task requires the computer to choose between two or more options - this is
called a selection.
Example 1
The process of awarding an exam grade consists of either awarding a pass or
awarding a fail.
AWARD EXAM
GRADE
I
I i f mark >= 50
0
I i f mar k < 50
0
Award pass Award fail
Notes:
1. The letter '0' in a box indicates that the task enclosed is an Option - only
one of these boxes will be carried out.
2. The condition for carrying out a particular task is shown above the
relevant box.
Example 2
The task of awarding a grade consists of carrying out one of 4 options:
I. award a Distinction;
2. award a Merit;
3. award a Pass;
4. award a Fail.
AWARD EXAM
GRADE
I cl
0
I c2
0
I 0
c3 I c4
0
Award Award Award Award
distinction merit pass fail
Note: The abbreviation 'c' plus a number may be used for conditions rather than
writing them in full above the option boxes.
62
Some processes consist of carrying out a particular task if a certain condition is
true, but doing nothing if it is not true.
Example 3
A computer has to write a letter to all customers who are overdue with payments.
This can be shown as follows:
PROCESS
CUSTOMER ACCOUNT
I
I cl I c2
I Send letter
0
.> 0
Note:
The box for the second option contains no task as nothing has to be done if
nothing is due- so a line is put through the box - meaning 'do nothing'.
Exercises
Produce structure diagrams for the following jobs.
1. Processing a student application for a course consists of printing either an
acceptance letter or a rejection letter - depending upon whether the student
has passed or failed GCSE English.
2. Calculating pay for an employee consists of paying either £300 per week
(managers) or £200 (other staff).
3. Processing Certificates consists of printing a Certificate if grade is 'P' .
63
2.5 Program Design· Combining Structures
Most designs will involve a combination of various structures (sequence, iteration
and selection). Each design win be built up by working downwards - gradually
breaking each task down into smaller tasks.
The task of producing reports for the whole class win break down into the simpler
task of producing a report for one student - repeated 20 times. On a structure
diagram this is shown as:
PRODUCE
REPORTS
FOR CLASS
I 20 times
Produce *
one
student's
report
The task of producing one student's report can itself be broken down into a
sequence of smaller tasks as was done in Lesson 2.2 To do this you should cover
up the top box and look at the lower task as if it were the only one; so producing
a report for a single student might break down into a sequence of three tasks:
1. get name and marks;
2. calculate average mark;
3. display report.
These will be shown on the structure diagram by adding a sequence of three
rectangles - so the structure diagram will now appear as: .
PRODUCE
REPORTS
FOR CLASS
I 20 times
Produce *
one
student's
report
I
II I
Get Calculate Display
student average report
details
64
Example 2 - Design a program to do bonus calculations for all employees in a
firm (each employee gets a bonus of 10% of hislher annual salary) .
This task will break down into the simpler task of calculating the bonus for one
employee - repeated until all have been done. (The user will be asked after each
employee whether he/she wishes to carry on.)
PROCESS
BONUSES
FOR FIRM
The job of processing an employee's bonus will break down into a sequence:
I. get details;
2. calculate bonus;
3. produce bonus payslip;
and
4. ask if there are any more to do.
PROCESS
BONUSES
FOR FIRM
65
Example 3 - Design a program which will accept a student's name and percentage
marks for English and maths, calculate the average, award a grade (Pass for 50 or
more, Merit for 65 or more, Distinction for 85 or more and Fail if below 50) and
display a report.
Process student's
result
r T I I
Get name Calculate Decide Display
and marks average grade report
However, the third task - awarding the grade - breaks down further as it consists
of making a choice between awarding a Distinction, Merit, Pass or Fail.
The whole diagram will therefore be shown as:
Process student's
result
r
1
I I I
fGet name fcalculate Decide Display
and marks average grade report
I
I cl
0
I c2 I c3 I c4
Award Award 0 Award 0 Award 0
Distinction Merit Pass Fail
66
The program in the previous example has the disadvantage that it deals with only
one student. To be more useful, it should allow the user to key in details for a
number of students - producing a report for each one.
Example 4 - Design a program which for each member of a class of students will
accept the student's name and percentage marks for English and maths, calculate
the average. award a grade (Pass for 50 or more. Merit for 65 or more. Distinction
for 85 or more and Fail if below 50) and display a report.
The overall job is to process the results for the whole class and this breaks down
into a repetition - processing each student's results in tum .
Process
Results for
class
I cl
Process *
Results for
one student
The task of processing the results for one student will break down into a sequence
- as in example 3 - except that at the end of processing a student, the user must be
asked whether there are any more students to enter.
Process
Results for
class
cl
Process *
Results for
one student
I I I I
Get Calculate Decide Display Check i f
data average grade report any more
67
The remainderof the tasks breakdownexactlyas in Example3 - so the final
structurediagram will be as follows:
Process
Results for
class
cl
Process *
Results for
one student
I I I I
Get Calculate Decide Display Check if
data average grade report any more
0
I
c2 c3 I I c4
0 Award
I c5
0
Award Award 0 Award
Distinction Merit Pass Fail
68
Exercises
Produce structure diagrams for each of the following tasks.
I. Processing the payroll for a firm consists of repeatedly dealing with one
employee's pay - asking for hislher name and annual pay. calculating
hislher monthly pay and producing a payslip.
Assume that there are IS employees in the firm.
2. Processing the stock file consists of repeatedly (for each item of stock)
asking for the stock item's name and old stock level, calculating new stock
level and displaying a report Ask the user if there are any more to do
after each item.
3. Employees in a firm are given a bonus according to their length of service:
I year bonus £100;
2 years bonus £200;
3 years or more bonus £500.
Your design should allow for getting information from the keyboard,
making a decision and displaying the name of each member of staff and
hislher bonus.
The program should terminate when the user indicates that all employees
have been dealt with.
4. Students who get more than 90% in an exam are to be awarded a prize.
Your design should allow for getting the data from the keyboard, making a
decision and producing a report showing the name and whether or not
he/she is to get a prize.
Assume that there are 20 students in the class.
69
2.6 Implementing a Design in C
When a program is small, any of the above structures might be suitable. A large
program will normally be split into functions .
Advantagesojin-linecode
In general, in-line code will run faster because there are no calls to, and returns
from, functions - which take up processing time.
Advantagesojout-of-linecode
1. Using functions will often result in a smaller program • as work which
needs to be done in several parts of a program can just be coded once as a
function and then called each time it is needed . If the code were in-line, it
would need to be coded each time it was used.
This means :
a) a programmer may use functions from a library or written by
another programmer without knowing anything about their internal
working - only what parameters are needed, what will be returned and the
effect of the function.
70
2.7 In-line Codingfrom a Structure Diagram - Sequences
Example
Tum the following structure diagram into C instructions.
PRODUCE
STUDENT
REPORT
I I
Get Calculate Display
student average report
details
1. Write a comment relating to each task in the sequence (ignoring for the
moment the overall job denoted by the top box in the diagram) .
Each of the tasks in the sequence above will be represented by a comment in the
program.
/*Calculate average*/
/*Display report*/
2. Fill the space after each comment with appropriate C instructions to carry
out the task.
/*Calculate average*/
average_mark = (english_mark + maths_mark)/2;
/*Display report*/
printf ("Report for %s\n\n" I surname);
printf ("English : %d\n" english_mark);
I
71
3. If Produce Student Report represents the whole program - then all that is
necessary is to place the code within the main function, add variable definitions, a
Finish section - with a return 0 command ; and #include any necessary header
files. The overall name of the job can be used as a comment for the whole
program.
/*Calculate average*/
average_mark = (english_mark + maths_mark)/2;
/*Display report*/
printf ("Report for %s\n\n", surname);
printf ("English: %d\n", english_mark);
printf ("Maths : %d\n", maths_mark);
printf("Average: %d\n", average_mark);
/*Finish*/
return 0;
If the sequence of tasks represents only part of the program - then the code will
simply fit into the appropriate place in the program .
72
Exercises
Write a program from each of the following structure diagrams (Follow the
method used in the above example).
1.
PROCESS
EMPLOYEE'S
PAY
I
I I I
Get Calculate Calculate Display
employee weekly pay weekly pay pays lip
details before tax after tax
Assume that the employee details keyed in will be name and annual salary and
that the pays lip has to show name and weekly wage after deducting 30% tax.
2.
PRODUCE
STOCK
REPORT
I I
Get Calculate Display
stock item new stock report
details level
Assume that the name of the stock-item, the old stock level (number of items at
the beginning of the week) and the number sold that week are keyed in. The
report has to show the name of the stock-item and the new stock level.
73
3.
PRODUCE
NEW
PRICE LABEL
I I
Get Calculate Display
sales item new price price label
details
Assume that the name of the item is keyed in together with its old price. The
computer has to increase the price by 20% and display a suitable label showing
the name of the item and its new price.
4.
PROCESS
LOAN
REPAYMENTS
I I I I
Get Calculate Work Calculate Display
loan balance out balance account
details after interest with
repayment interest
Assume that the borrower's name is keyed in - together with the amount of money
outstanding from the previous month and the amount repaid this month. The
computer should then calculate the new amount still owed, work out interest at
2%, and add this to the balance to produce a figure for the new amount owed -
then produce an account statement showing the name, old balance, amount repaid,
new balance, interest for month and new balance after interest is added.
74
2.8 In-line Coding from a Structure Diagram «Iteration
Example
Tum the following structure diagram into C instructions.
PRODUCE
MESSAGE
SCREEN
24 times
Produce *
message
line
1. Write a comment relating to the repeated task (again ignoring the overall
name for the whole job) .
e.g.I /"'Produce message line"'/
4. Place the appropriate instruction in the braces to carry out the task
specified by the comment.
As there is only one instruction to be repeated the braces may be removed but it
may be clearer to leave them in place.
75
5. Again, if the structure diagram represents the whole program - then the
code may be surrounded by braces. the function heading mainO placed at the top.
variable definitions added and any necessary header files #included.
Again. the overall name of the job can be used as a comment for the whole
program.
/*Finish*/
return 0;
If the task represented by the structure diagram is only a part of the whole job
then again these instructions will fit into the program at an appropriate place.
Exercises
Write programs for the structure diagrams you produced for Lesson 2.3. For
Question 2. make the computer display the 100 lines on the screen.
76
2.9 In-line Codingfrom a Structure Diagram - Selection
Example Problem
Tum the following structure diagram into C instructions.
AWARD EXAM
I GRADE
I
I i f mark >== 50
0
I i f mark < 50
0
Award pass Award fail
1. Write a comment relating to each optional task (again ignoring the overall
name of the job).
/*Award Fail*/
else
/*Award Fail*/
3. Place braces after each comment to take the instructions which belong
with each comment (again. these may be deleted if there will be only one
instruction for each comment).
}
else
/*Award Fail*/
{
77
4. Fill in the appropriate instructions in each section.
As there is only one instruction dependent on each selection, the braces can be
removed.
5. It is unlikely that a selection will form the whole of a program (unless data
has been passed into the program from the command line - see Lesson 5.18). So,
these instructions will fit into the program at an appropriate place. Perhaps they
might form a function called by main - once a student has had the opportunity to
key in his/her mark. Alternatively, they might be part of the main function -
again after suitable instructions have been included to obtain the student's mark.
Exercises
1. Why is a Selection unlikely to form the basis of a whole program?
2. Write suitable code from the structure diagrams you produced for Lesson
2.4 and incorporate the instructions within a program.
78
2.10 Implementing a Complex Program Design using In-line Code
Example
Tum the following structure diagram into C instructions.
Produce
Reports for
class
cl
Produce *
Report for
one student
I I I I
Get Calculate Decide Display Check i f
data average grade report any more
Ic2 c3 I
0 Award 0 Award
I c4
0 Award
I c5
0
Award
Distinction Merit Pass Fail
2. Write the data definitions at the top of function main. In this case, a single
character space will be needed to hold the grade. a string to hold a name, three
integer spaces for the marks. and another character space to hold the answer to the
question 'Any more students ?'.
e.g.2 main ( l
(char grade, any_more_students, surname[16];
int maths~ark, english_mark, average_mark;
)
3. Tum the name of the top box in the structure diagram into a comment
describing the work of the whole program and place this at the very top of the
program text.
79
e.g.3 / ... PROGRAM - produce reports for class .../
main( )
{char grade, any~ore_students, surname[16]i
int maths-mark, englis~rk, average~rk;
4. Work down the rest of the structure diagram - one level at a time - using
the rules shown in Lessons 2.7, 2.8 and 2.9 to convert the diagram into C program
code.
The first level below the top is a single repeated task. So, the appropriate loop
control command do .. while (assuming that there will be at least one student),
together with a comment and braces for the instructions which will be repeated,
can be placed in function main.
The job of producing a student report in tum breaks down into a sequence of five
smaller tasks, so a comment relating to each one can be placed - slightly indented
- within the braces that follow the Produce studentreport comment.
main()
{char grade, any~ore_students, surname[16];
int maths~rk, englis~rk, average~rk;
do
/*Produce report for student*/
{/*Get data*/
/*Calculate average mark*/
/*Decide Grade*/
/*Display Report*/
/*Check if any more students*/
} while (any~ore_students == 'y') ;
}
80
The job of deciding on a grade breaks down into a selection of four options . This
can be shown by appropriate if, else if or else commands. Again place braces after
each comment to hold the actual instructions.
}
else if (average~rk >= 65)
/"'Award Merit"'/
{
}
else if (average~ark >= 50)
/"'Award Pass"'/
{
}
else
/"'Award Fail"'/
{
/"'Display Report"'/
/"'Check if any more students"'/
} while (any~ore_students == 'Y');
}
Notes
J. Each box on the structure diagram is now represented by a comment in
the program.
2. The lower the level the box occupies on the diagram - the more indented
will be the related comment from the margin 0/ the program. Boxes at the same
level will be represented by comments with the same amount of indentation.
5. Write suitable C instructions under each comment to carry out the action
indicated and add afinish section. Include any necessary header file names.
81
e.g.? iinclude <stdio.h>
/*PROGRAM - produce reports for class */
main()
{char grade, any_more_students, surname[16];
int maths~ark, english~ark, average_mark;
do
/*Produce report for student*/
(/*Get data*/
printf("\nSurname 1");
scanf ( "%s", surname);
/*Decide Grade*/
if (average~ark >=85)
/*Award Distinction*/
{
grade = 'Do ;
}
else if (average_mark >= 65)
/*Award Merit*/
{
grade = 'M' ;
}
else i f (average_mark >= 50)
/*Award Pass*/
{
grade = "P 0 ;
}
else
/*Award Fail*/
{
grade = IF' ;
/*Display Report*/
printf ("Report for %s\n\n", surname);
printf ("English : %d\n" , english~ark);
printf ("Maths: %d\n", maths~ark);
printf'( "Grade: %c\n", grade);
/*Finish*/
return 0;
82
2.11 Hierarchical Coding from a Structure Diagram» Sequences
Example
Tum the following structure diagram into C instructions.
PRODUCE
STUDENT
REPORT
I I
Get Calculate Display
student average report
details
So. assuming that the example structure diagram represents the whole job. the
following function names might be used.
e.g.I main
get_student_details
calculate_average
display_report
2. Create outline functions . Add definitions for any variables and parameters
that will be needed and give a return type to the function definitions.
There are various possibilities for arranging the data in the example. We will use
global variables in this example - as all functions need to be able to access most
of the variables. This means that no data will be passed between functions. so no
parameters or return values will be needed and these items can be defined as void.
e.g.2 int maths_mark , english_mark, average_mark;
char surname[20];
main()
{
}
void get_student_details(void)
{
}
83
void calculate_average(void)
{
}
void display_report(void)
{
}
3. Fill the function (in this case main) corresponding to the top box in the
structure diagram with a function call for each box in the sequence immediately
below the top box - and joined directly to it (three in this case).
If the top box in the diagram corresponds to function main add return 0 to
indicate the finish of the program.
e.g.3 main ( )
(
get_student_details();
calculate_average();
display_report();
return 0;
Now, by looking at this function you have a complete summary of how the
program will carry out the job required. To get more detail , it will be necessary to
look at the individual functions that the computer is being told to perform.
4. So, the next stage is to fill in the program statements in the other
functions.
void calculate_average(void)
{
average_mark = (english_mark + maths~ark)/2;
void display_report(void)
{
printf ("Report for %s\n\n", surname);
printf ("English : %d\n", english_mark);
printf (HMaths: %d\n" , maths_mark);
printf ("Average: %d\n", average_mark);
84
5. If Produce Student Report represents the whole program - then all that is
necessary is to #include any necessary header files and add prototypes for the
functions. The overall name of the job can be used as a comment for the whole
program.
void get_student_details(void)
{
printf ( "\nSurname ?");
scanf ( "%s", surname);
printf ( "\nEnglish mark ?");
scanf ("%d", &english_mark);
printf("\nMaths mark ?");
scanf (" %d", &maths_mark);
)
void calculate_average(void)
{
average_mark = (english_mark + maths_mark)/2;
)
void display_report(void)
{
printf ("Report for %s\n\n", surname);
printf ("English: %d\n", english_mark);
printf ("Maths: %d\n" maths_mark);
I
If the sequence of tasks represents only part of the program - then the code will
simply fit into the appropriate place in the program.
Exercises
l. Consider other ways the data could be organised in the example program.
2. Using a hierarchical structure, write a program from each of the structure
diagrams from the exercises in Lesson 2.2.
85
2.12 Hierarchical Codingfrom a Structure Diagram -Iteration
Example
Tum the following structure diagram into C instructions.
PRODUCE
MESSAGE
SCREEN
24 t.imes
Produce *
message
line
So, assuming that the program comprises only the tasks shown - the following
functions could be used.
e.g.l main
produce_message_line
2. Create outline functions. Add definitions for any variables and parameters
which will be needed and add the return type to the function definitions.
In the example, the only variable used is one to keep count of how many
messages have been displayed . This only needs to be accessed by function main -
so can be defined as a local variable in that function. No data will be passed
between functions - so both parameter definitions, and the return type for the
called function producemessageline will be void.
e.g.2 main ( )
{int count;
}
void produce_message_line(void)
{
}
3. Fill the function corresponding to the top box in the diagram - with
instructions to carry out the task represented by the box immediately below it the
required number of times.
From the structure diagram above, the main task (producing a screen of "Hello"s)
consists of repeating the task of producing a single line 24 times. So, decide
which command - for, while .. do, do .. while - should be used to carry out the
86
repetition and place this - with the appropriate condition - in the function which
corresponds to the main task (in this case function ma;n) ; add the name of the
function which is to be repeated.
As the top box in the example corresponds to main and the whole job has now
been summarised - add return 0 to indicate the end of the program.
e.g.3 main ( )
{int count ;
for (count = 1; count <= 24; count++)
produce~essage_line();
return 0;
4. Fill the function corresponding to the smaller, repeated task (in this case
producemessage.line) with suitable C statementsso that it will carry out the task
required.
e.g.4 void produce~essage_line( )
(
printf("Hello\n");
}
return 0;
void produce~essage_line()
(
printf ("Hello\n") ;
}
Exercises
Write programs for the structure diagrams you produced for Lesson 2.3. For
Question 2, make the computer display the 100 lines on the screen.
87
2.13 Hierarchical Coding from a Structure Diagram· Selection
Example Problem
Tum the following structure diagram into C instructions.
AWARD EXAM
GRADE
I
I i f mark >= 50 I i f mar k < 50
I Award pass
0 0
Award fail
It is very unlikely that the structure diagram will represent the whole program - as
there is no facility for obtaining the marks upon which to base the decision. So
the main task here is unlikely to correspond to function main in the program.
e.g.I awarcLexanLgrade
award....Pass
awarcLfail
char award....Pass(void)
{
}
char award....fail(void)
{
}
88
3. Decide which command will be used to control the selection - if.else or
switch - and place appropriate instructions in the function corresponding to the
main task in the structure diagram - in this case awardgrade.
char award-pass(void)
(
return ( 'P') i
char award.-fail(void)
{
return ( , F I ) i
)
5. These functions will then fit into the program at an appropriate place.
Exercises
I. Incorporate the example functions into a program which will ask for a
mark and display 'P' or 'F'.
2. Write suitable code from the structure diagrams you produced for Lesson
2.4 and incorporate the instructions within a program.
89
2.14 Implementing a Complex Program Design using Hierarchical
Code
Example
A program has been designed to draw a block of offices (six storeys).
Task (2) is an iteration of drawing one floor- 5 times (as each floor is the same).
The only variable needed is an integer to hold a count of how many floors are
drawn .
DISPLAY
OFFICE
BLOCK
I I
Draw Draw Draw
roof first to ground
fifth floors floor
5 times
Draw *
one floor
e.g.1 main ( )
{
void draw_roof(void)
{
}
void draw_first_to_fifth_floors(void)
{
}
90
void draw_ground_floor(void)
{
}
void draw_floor(void)
{
}
4. Starting at the top of the structure diagram and working down, fill each
function with appropriate C instructions. The higher level functions will just
contain calls to lower level functions.
The main task, DISPLAY OFFICE BLOCK, breaks down into a sequence of
three tasks - draw roof, draw first to fifth floors and draw ground floors - so main
should contain calls to the three corresponding functions to carry out these tasks -
together with a return O.
e.g.2 main ( )
{
draw_roof ( ) ;
draw_first_to_fifth_floors() ;
draw_ground_floor();
return 0;
The functions draw jroof, draw groundfloor and draw-floor can now be filled
with suitable C instructions.
void draw_ground_floor(void)
(
printf(" * *****************************\n") ;
printf( "
printf ("
printf("
* ------
*
* ------
I I
------ ------
------
*\n");
* \n" ) ;
*\n");
printf(" ******************************\n");
91
void draw_floor(void)
{
printf(" ******************************\n");
printf(" * ---- -- ------ ------ *\n");
printf (" * *\n") ;
printf(" * ------ ------ ------ *\n");
printf(" ******************************\n");
5. Finally, prototypes can be listed and any necessary header files #included .
e.g.5 #include <stdio. h>
void draw_roof(void);
void draw_first_to_fifth_floors(void);
void draw_9round_floor(void);
void draw_floor(void);
main()
{
draw_roof();
draw_first_to_fifth_floors()i
draw_ground_floor()i
return 0;
void draw_first_to_fifth_floors(void)
(int count;
void draw_roof(void)
{
~rintf("********************************\n")i
void draw_9round_floor()
(
printf(" ******************************\n")i
printf("
printf("
printf("
* - -----
*
* ------
I I
------ ------
------
*\n")i
*\n");
*\n")i
printf(" ******************************\n");
}
void draw_floor(void)
{
printf(" ******************************\n")i
printf(" * ------ ------ ------ *\n")i
printf(" * *\n")i
printf(" * ------ ------ ------ *\n")i
printf(" ******************************\n");
92
2.15 Implementing a Program Design using In-line and
HlerarchicaCCode
Generally a program will be a mixture of in-line and hierarchical code. The
overall structure diagram can be broken down to show how the program splits
into functions.
Example
The job (shown in Lesson 2.10) of producing student reports might be split up so
that the tasks of calculating an average mark and deciding a grade are delegated to
functions calculatcaverage and decide grade respectively. The design can
therefore be modified to show this.
Function IIUIm
Local variables: int maths_mark, english_mark
char any_more_students, sumame[l6];
Global variables affected: none
Process
Results for
class
I cl
Process *
Results for
one student
I I
I
I I I
Get Calculate Decide Display Check if
data average grade report any more
93
Function decideJrade
Prototype: char decide_grade(int)
Description: Accepts a percentage mark (stored in int parameter mark) and
returns a grade.
Local variables (apart from parameters): none
Global variables affected by function : none
Decide
grade
I
I c2
0
I c3 I c4 I c5
Award Award 0 Award 0 Award 0
Distinction Merit Pass Fail
Function calculate_average
The design can then be implemented using the same rules used in Lesson 2.10,
but treating each function as if it were a separate program.
So function main will implemented in the same way as in Lesson 2.10. except that
it will not contain the selection or the actual calculation of the average - but will
instead call the relevant functions .
94
do
/*Produce report for student*/
{/*Get data*/
printf ( " \nSurname ?");
scanf( "%s", surname);
printf ( "\nEnglish Mark ?");
scanf ("%d", &english_mark) ;
printf("\nMaths Mark ?") ;
scanf("%d", &maths_mark);
/*Calculate average mark*/
average_mark =
calculate_average (english_mark , maths_mark) ;
/*Decide Grade*/
grade = decide_grade(average_mark);
/*Display Report*/
printf ("Report for %s\n\n", surname);
printf ("English: %d\n", english_mark) ;
printf ("Maths: %d\n", maths_mark);
printf ("Grade: %c\n", grade);
/*Check i f any more students*/
printf("\nAny more students? - YIN");
scanf(" %c", &any_more_students);
} while (any~ore_students == 'Y') ;
/*Finish */
return 0;
/*FUNCTION - calculate_average
Accepts two marks and returns the average*/
int calculate_average(int markl, int mark2)
(
return((markl+ mark2)/2) ;
}
95
2.16 Testing Software
Program testing involves trying out various inputs to a program, working out what
the output should be and then running the program to check if the actual output
corresponds.
There are two approaches to selecting test data - black box testing (also called
functional testing) and white box testing (also called logical testing).
This involves looking at what the program is supposed to do and then checking if
it does it correctly - without being concerned with how the program works.
It could be compared to test driving a new car to check that it meets its
specifications - speed, braking power, road holding, etc - without at any time
taking into consideration the various parts and how the car is designed.
This involves looking at the program design to see how the software works and
then ensuring that every part of the program is tested - paying particular attention
to those areas where difficulties are likely to arise.
It could be compared to testing a car after looking at the design and the parts to
ensure that all components get tested and to see which parts are likely to be
weakest and need most testing.
Using both methods should result in a reasonably full test of the program.
The first step in either case is to draw up a test plan. Consider all the things you
want to check, then make up some sets of data which will do this.
Example
Test a program which has been written to grade all the students in a class.
For each student, the computer should ask the teacher for the student's maths
mark and English mark, calculate the average, decide on a grade on the basis that
50 to 64 is a Pass, 65 to 84 is a Merit, 85 or more is a Distinction and less than 50
is a Fail, then display a report showing the name, average mark and grade. After
each student has been keyed in, the teacher should be asked if there are any more
students in the class - and the program should stop if the teacher keys 'N' or 'n',
96
Bklck Box Testing
To test the program - test data will have to be found (or invented) which checks
all aspects of the specification.
A typical range of marks should be used - ensuring that some students will fall
into each grade category and that very low and very high marks are tried out. In
addition it is necessary to check that the program stops as required (and not
otherwise).
Test Plan
Name
INPUT
mallu eDgluh oy more I Name
HXPECI1!D OUTPUT
Avera!e Grade
The test plan has been shown in ascending order of mark (to show that it covers
every grade). However, it might be better to reorganise it in random order - to
ensure that the order that it is keyed has no effect on the program working .
The program will have to be run twice to ensure that it stops when either 'N' or 'n'
are keyed .
To check which processes have been tested, the structure diagram can be
numbered (ignoring the top box) and a grid can be set up showing which data
record tests which task . :Every task should process at least one data record.
97
Process
Results for
class
cl
1 Process *
Results for
one student
I I I I
2 Get 3Calculate 4De cide 5 Display 6Check i f
data average grade report any more
I c2 I c3 I
9 Award 0
c4 I c5
7 Award 0 8 Award 0 10 Award 0
Distinction Merit Pass Fail
Test Plan
Name
INPUT
malhs eDgliJh anymore I Name
BXPECfBD OUTPUT
A_aco Gtado
98
Each record can then be ticked on a grid against the processes tested. So, for
example, Record 1 will test Tasks I - 6 and 10 while Record 9 will test Tasks I -
7.
Ta.rk
1 2 j 4 5 6 7 8 9 10
Record
1 / / / / / / /
2 / / / / / / /
j / / / / / / /
4 / / / / / / /
5 / / / / / / /
6 / / / / / / /
7 / / / / / / /
8 / / / / / / /
9 / / / / / / /
Check that at least one tick appears under each task. In the case of tasks which
form part of a selection and any other complicated tasks, several ticks should
appear. If not, then more test data will be needed.
Finally, a test plan may be produced combining both white box and black box
approaches .
Test Plan
I
INPUT HXPECfBDOUTPUT
Name maths english anymore Nome Average Grade
Again, it will probably be desirable to reorder the data randomly to ensure that the
order does not affect the test.
99
If the data is more complicated the test plan may be set out with each individual
record separate.
Test Plan
I.
Inpul
Name:PHD..IPS
MaIhs: 0
Boglish: 0
AnyMore Y
£%peeled OUlpUI
Name: PHD..IPS
Average: 0
Grade: Pail
2.
Inpul
Name:JONBS
MaIhs: 0
Boglish 50
AnyMore Y
ExpfCled OUlpUI
Namo:JONBS
Average: 25
Grade: Pail
It can also be useful to include a note of what is being tested in each case -
e.g. Tests calculation and c5 (average mark < 50).
The actual output should be checked against the expected output and the result
logged. This could involve ticking off the items on a printout or writing down the
actual output on a table similar to the test plan .
Exercises
Write test plans for some of the programs from earlier exercises and test the
programs against them.
100
3.1 Defining Structures
For example, a student's surname and mark could be grouped together and the
whole item could be referred to as studenCdetails.
<---------------------------------stude"t_details------------------------------->
<-------------------------- ------surname----------------------------><-mark->
Exercises
I. Sketch the layout in memory of e.g.2 (above) - as has already been done
for e.g. I.
2. Set up the following structures in memory - and sketch the memory
layout:
a) employee_details which is made up of name, address, department
and salary.
b) course_details made up of coursename, course iiescription,
course_code, course_tutor.
c) book_title_details made up of isbn, title, author.
101
3.2 UsingStructures
To refer to any part of a structure in memory, the overall name of the memory
space is used· followed by a dot and the name of the individual data item within
it.
So from the structure definition for studenCdetails (e.g.l in the previous lesson),
the student's mark would be referred to as studentdetails.mark.
So,
or
would allow the user to key in a word and store it the memory location surname.
102
Example Program
/*Program asks user for an employee's name, department and annual pay -
calculates the monthly pay - and displays a payslip*/
tinclude <stdio.h>
main ()
{struct
{char surname[21];
char first_name[21];
char department[21] ;
float annual_salary;
float monthly-pay;
} employee_details;
/*Finish*/
return 0;
Exercises
l. Write a program which will ask the user for a student's name, address and
mark then display all the details in a report.
2. Write a program which will ask the user for an employee's details, store
them in a structure called employecdetaiis, consisting of name,
department name, annuaCsalary and then display a payslip showing the
name, department and weekly wage.
3. Modify the above programs • so that they will repeatedly request and
display information until the user indicates that he/she has finished.
103
3.3 Defining Structure Types
For example, we might wish to hold the same type of personal information for
both students and lecturers. So, we could set up a type (i.e. a layout or pattern)
and give it a name (placed straight after struct and before the brace { beginning
the definition ).
It is also possible to set up a structure type and memory spaces at the same time.
Exercises
1. Define a structure type called stock_details_type consisting of item_code,
description, cost price and sales-price - then use this type to define three
memory spaces called stock_ordered, stockreceived and stock_sold.
2. Define a structure called customer_typ e consisting of name, address,
accountbalance - and use this pattern to allocate memory spaces called
customer_a ccouncdetails and customersales iletails.
104
3.4 Using Structure Types
Where a structure has been set up using a type definition before allocation of
memory (previous lesson) - the data items are still referred to by the variable
names, NOT the type name - followed by a dot and the relevant item variable
names - as listed within the structure definition.
So, looking at the example structures from the previous lesson - the two memory
locations set up are called lecturer_details and studentsietoils, and they each
break down into surname, firstname, and address (as described in the definition
for struct person_type).
Example Program
iinclude <stdio.h>
main ( )
{/*Define a structure type called sta~~_ty'pa*/
struct staff_type
{char surname[21Ji
float annual_salarYi
} i
/*Finish*/
return 0;
}
Exercises
Rewrite the programs from the exercise in Lesson 3.2 - making use of structure
type definitions.
105
3.5 Operations on Structures
Where two structures are of the same type (i.e. have the same layout). the data in
one of the memory structures can be copied to the other memory structure using
the assignment command (=).
So, where a student has finished his/her course and is subsequently offered a
lecturing post. all the data can be transferred from studentdetails to
lecturer_details in one go.
Alternatively, where - for example - a maths lecturer enrols for, say, a language
course at the college, then his/her personal information can be quickly copied
from one part of the system to another.
The user is asked to key in details of a student and these will be stored within the
fields of studentjdetails . The whole contents of studentdetails is then passed to
function produce report which receives them in studentrecord - and uses the
data to display the report.
Note that placing a string within a structure allows the contents (rather than just
the address - see Lesson 3.21) to be passed as a parameter.
Example Program I
#include <stdio.h>
struct student_type
{
char name [20] ;
int english_mark;
int maths_mark;
};
106
main()
(struct student_type student_details;
/*Get student details*/
printf("\nName ?");
scanf ("%S", student_details . name) ;
printf("\nEnglish mark? ");
scanf ( "%d", &student_details. english_mark) ;
printf("\nMaths mark ? ");
scanf("%d", &student_details.maths~ark);
/*Produce report*/
produce_report(student_details);
/*Finish*/
return 0;
Example Program 2 makes use of this by delegating the task of getting the
student's details from the user to the function gecstudent_details which collects
all the data into the structure studentd ata - then returns it to main which copies
the information into studencdetaiis and in tum passes it as a parameter to
produce report.
Example Program 2
#include <stdio.h>
struct student_type
{
char name [20] ;
int english_mark;
int maths_mark;
} ;
107
struct student_type get_student_details(voidl;
void produce_report(struct student_typel;
main(l
{struct student_type student_details;
/*Get student details*/
student_details = get_student_details(l;
/*Produce report*/
produce_report(student_detailsl;
/*Finish*/
return 0;
printf("\nEnglish mark?"l;
scanf (" %d" &student_data . english_mark I ;
I
return(student_datal;
printf("************************\n");
)
Note that passing large structures to and from functions slows a program down.
Exercises
1. Write a program which will ask the user for a student's marks for English.
maths and French - then pass a structure containing these marks to
function decidegrade which will return another structure containing the
grades for each subjects ('P' if the mark was 50 or above - otherwise 'F').
The grades should then be displayed in a report.
2. Write a program which will use a function gecemployecdetails to ask
an employee for his/her name and annual salary. This information will be
passed as a structure to function produce payslip which will display the
name and weekly wage.
108
3.6 Defining Nested Structures
For example, a student's details may consist of his/her surname and grades. The
grades may be subdivided into maths, English and computing.
student_details
I I
surname gyades
I I
maths english
e.g.I struct
(char surname[lO);
, grade.,
}
However, we cannot give a simple type for grades in the above structure
definition - because it is in itself a structure - including maths and english:
e.g.2 struct
{char maths;
char english;
} grades;
We can then substitute this structure definition for grades into the structure for
studencdetails.
e.g.3 struct
(char surname[lO);
atruct
(char math.,
char engli.h,
) grade.,
} student_details;
This allocates a memory space with the overall name studencdetails as follows:
<----------------------------------student_details------------------------------->
moths english
<-------------------------------surname----------------------------><--grades-->
109
An alternative way to define a nested structure is to set up types for the lower
level structures - then use those in the type definition for the overall structure.
So
Next, the type(s) for the next level up are defined (in this case the overall
structure of studentd etails) - making use of any names already defined as
appropriate.
Exercises
Define a structure for each of the following - and sketch the memory layout (see
above example). Use two methods.
1. An employee's record consists of his/her name, address, department and
wage details. The name breaks down into surname, first name and title;
the address subdivides into line 1, line 2 and post code; the wage details
consist of annual salary and tax code.
2. A student's record consists of name, course and marks. Name consists of
surname, first name and title. Marks consists of the percentage mark for
assignment I, assigment 2 and the exam.
110
3.7 Using Nested Structures
To access a specific item in a nested structure, the overall name of the memory
space is used followed by a dot and the name of the lower level structure which
contains the data item, followed by another dot and the name of the item required.
For example, to access the maths grade in the studencdetails structure specified
in Lesson 3.6, the overall name of the memory space studencdetails would be
followed by a dot and grades (the name of the lower level structure containing the
item) and another dot - then finally maths (the name of the actual data item). So
studenCdetails.grades.maths refers to the item required.
allows the user to key in a character and stores it as the english grade.
Example Program
iinclude <stdio.h>
main ( )
{struct
{struct
{char surname[21J;
char first_name[21J ;
} name;
struct
{int maths;
int english;
int average;
} marks;
} student_details;
111
/*Display report */
printf("%s ", student_details.name.first_name);
printf ("%s\n ", student_details . name . surname) ;
printf ("%d\n", student_details . marks . average) ;
/ *Fi n i s h */
return 0;
Exercises
1. Write a program which will request information on an employee - name
(surname, first names, title), address (line I, line 2, line 3), annual salary -
storing these details in a suitable structure; then calculate weekly wage,
and display a payslip.
2. Write a program which will ask for a student's name (surname, first
names, title), course details (name, number), marks (subject I, subject 2,
subject 3, subject 4); then calculate an average mark and a grade based on
the average mark (80+ gives J\'; 60+ gives 'B'; 50+ gives 'C"; less than 50
gives 'F').
112
3.8 Defining Arrays - Single Dimensional
If a number of similar items of data (e.g. the marks for ten students) have to be
stored in Central Memory at the same time, these would usually be stored in an
array (i.e. a table of data).
In a similar way, space can be set aside to store a table of floating point numbers
(i.e. numbers containing decimal fractions):
NOTE: you have already met an array of characters - as a string (Lesson 1.8) is
just a special type of character array (used to store characters which are seen as
linked together to form a word or message and, in C, always finishing with a
special end-of-string marker character).
Exercises
Write suitable definitions for arrays to store:
1. a list of marks (whole numbers only) for 20 students;
2. a list of wages (pounds and pence) for 25 employees;
3. a list of grades ('A' to 'E') for a class of ten students.
113
3.9 Using Arrays. Single Dimensional
Any item in an array is referred to by the name of the array followed by a number
in square brackets []. This number is known as the Index .
Note that the first item in a C array is numbered 0 - so the first mark in the array
studentmark would be studenunark[OI, the second mark would be
studentmarkl1I, and the tenth would be studenunark[91.
accepts a mark from the keyboard and stores it in the first space in the
studentmark table.
e.gA student_no = 1;
printf ("%d", studentJl\ark[student_no]);
will display the mark for the second student (i .e. studencmark[1I).
will ask the user for hislher student number - then display that student's mark.
Thefor loop will display the contents of every item in the array student mark .
It is important to ensure that you do not refer to a space that has not been
allocated. So, for example, if you have set up an array as: int mathsmark]101;
you should not then try to access maths_mark[ 10/ (i.e. the eleventh space) or
maths_mark[ 11/ (the twelfth space), as these do not exist. C does not check that
you only refer to items within the array - i.e. it does not carry out bounds checking
114
- so you could end up displaying rubbish or overwriting an area of memory
allocated for something else - even part of your program instructions.
Example Program
iinclude <stdio.h>
main ( )
(
int emp_number;
float employee_wage[5] ;
emp_number) ;
scanf ( "%f" &employee_wage [emp_number] ) ;
I
/*Display payslips */
for (emp_number = 0; emp_number <= 4; emp_number++)
/*Display one payslip*/
(printf("***********************************\n");
printf ("Employee number : %d\n" I emp_number);
printf ("Wage = %f\n" I employee_wage [emp_number] ) ;
printf("***********************************\n\n");
}
/*Finish*/
return 0 ;
Exercises
I. As the Example Program deals with the wages for five employees, why is
only one memory space needed for the employee number?
2. Explain what is meant by the index to an Array? Is a floating point value
valid for an Index'!
3. Write a program to ask the user for the marks for ten students, store them
in an array called studentmark; then display a report for each student.
4. Modify your program from Question 2 so that after accepting all the
marks, it will ask the user to key in a student number and then display the
mark for that student only.
5. Modify your program from Question 3 so that after displaying the required
mark it will ask the user if he/she wants to see another and so on until the
user keys 'N'.
6. Modify your programs from Question 4 so that it will deal with alphabetic
grades rather than numeric marks.
115
3.10 Defining Arrays· Two Dimensional
Suppose that there are five students in a class, each of whom takes 3 exams
during the year. One way to hold this infonnation would be in a two-dimensional
array (i.e. a table with two axes).
exam number o 1 2
student o
1
2
3
4
sets aside ten lots of twelve spaces to hold floating point numbers - e.g, to hold
details of twelve monthly payments (pounds and pence) for each of ten
employees.
Note that while it is convenient for the programmer to think of the data as being
held in a table - it is actually held sequentially (row after row) in memory - so an
array defined as int mark[4][2] would be stored as:
I I
(4 lots of 2)
I I
(2 lots of 4)
Exercises
Define tables to hold:
1. five percentage marks for each of ten students;
2. six months' sales figures for each of 20 salespeople;
3. two exam grades and a final grade for each of twenty students.
Sketch how the data for each of the above arrays would be stored in memory.
116
3.11 Using Arrays - Two Dimensional
displays the first student's third exam mark. (The indexing still starts with 0 on
both axes.)
accepts a 'number from the user and stores it as the fifth student's second exam
mark.
Again. rather than actual numbers, integer variables may be used as indexes.
e.g.4 s'tudent = 2; exam = 0 ;
printf ("%d" student_mark[student] [exam]);
I
asks the user for details of the student and exam then displays that student's mark
for the particular exam.
will display the contents of the array in a tabular form - one row per student. The
first/or loop steps through the students - and for each student the second/or loop
steps through that individual's exam results. Each student row is finished with a
new line (\n) .
117
Example Program
_include <stdio.h>
main()
{int student, exam;
int student_grade [10] [3];
Exercises
1. What is a nested loop? (See examples of this in e.g.6 above and in the Get
grades... section of the Example Program above.) Explain how it works.
2. Sketch out how the grades data is held in Central Memory for the
Example Program above.
3. A company has ten branches. Write a program which will ask the user for
the monthly sales figures (£ only - i.e integers) for each branch over a
period of six months and store the figures in a two-dimensional array -
then allow the user to ask for the figures for any particular branch for any
month .
4. Modify the above program (Question 3) so that once the sales figures have
been keyed in the user will be asked to key a branch number and
immediately see all the sales figures for that branch displayed in a row.
5. Modify the program for Question 4 so that the sales figures for the
selected branch are displayed in a column with a total at the bottom.
6. Modify the program for Question 5 so that once the data has been entered
all the sales figures are displayed in a table with suitable headings .
7. Modify the program for Question 6 so that Monthly Totals for the Firm
appear at the bottom of each month's column .
8. Modify the program for Question 7 so that it handles pounds and pence
(i.e, floating point).
118
3.12 Arrays of Strings
So, a name - for example - would be stored one letter per space, followed by an
end-of-string character (which in C is represented by VJ).
If for example the name "JULIA" were keyed into a string defined as char
studencname[lOj it would be held as:
So, space to store four 9-character names (plus end-of-string markers for each
name) would be defined as charstudencname[4J[10j.
J U L I A \0
A N N E \0
J 0 N A T H 0 N \0
D A V I D \0
As, previously mentioned, although we might visualise the two dimensional array
as a table similar to the one above - the data would actually be stored
sequentially in memory. (See Lesson 3.10)
Once an array of strings has been defined - one particular string can simply be
referred to by the array name and the first index number only .
will allow the user to key in a new name which would overwrite "DAVID"
119
e.g.4 gets (student_name [3));
will have the same effect.
will display the first letter of the first name - i.e. 'J'.
Example Program
'include <stdio.h>
main()
{int student;
char student_name [10] [21];
I *Finish *1
return 0;
Exercises
I. Write a program which asks the user for the names of ten employees, then
displays all the names in list.
2. Write a program which asks a teacher to key in the name of ten students in
a class - together with their overall percentage marks - then displays the
names and marks in a table. (Use an array of strings and an array of
integers.)
3. Write a program which asks for the names of ten students· together with
their marks for each of two assessments. (Use an array of strings and a
two-dimensional array ofintegers.)
4. Write a program which asks for the names of twenty cities, then displays
the first letter of each.
5. Write a program which asks the user for the names of twenty cities - then
searches for any that begin with the letter 'A' and displays the total.
6. Write a program which asks the user for the names of twenty cities - then
searches for any that contain the letter 'A' and displays the total of 'A's.
120
3.13 Defining Arrays of Structures
e.g.I struct
{char student_name[lOl i
int eXaIl\Jtlarki
} student_details i
allocates a memory space with the overall name studentdetails to hold
information about one student - e.g.
An array of such structures could be used to hold the data for several students:
E L. I Z A B E T H \0 60
T E S F A I \0 65
A N D R 0 U L L A \0 80
e.g.2 struct
{char student_name[lOli
int exam...marki
} student_details [101
sets up an array called student-details suitable for storing details of ten students.
Alternatively, the structure type can be set up as a template with a type name.
The memory space for the array can then be set up later .
Exercises
Define arrays of structures suitable for holding:
1. the names, addresses and wages for ten employees;
2. the names, maths marks, English marks and grades for five students.
121
3.14 Using Arrays of Structures
To refer to a specific item of data in an array of structures, use the array name
followed by the relevant number in square brackets [ ] - followed by a dot and the
name of the data item required.
So, in the example array from the previous lesson, studenrdetailsltil.exam jnark
refers to the first student's mark.
accepts a number from the keyboard and stores it as the first student's
exam_mark. Note the position of the & - before the array name, not the item
name.
Note that as usual when scan! or gets are used to read a string, no & is used.
122
Example Program
/*Program obtains name and marks for ten students from
user - stores details in memory - tnen allows user to
display a particular student's details*/
tinclude <stdio.h>
main()
{int student;
struct
(char student_name[ll] ;
int student_mark;
) student_details[lO];
/*Finish*/
return 0;
}
Exercises
Use arrays of structures to store the main data for each of the following programs.
1. Write a program which will ask a teacher to key in the name, English
mark and maths mark for five students - then display all the details in a
table.
2. Write a stock database program which will ask the user to key in the
details of twenty items of stock (item-code, description, cost price, selling
price) - then repeatedly allow the user to choose one stock item and
display the information for that item.
3. Modify the program for Question 2 so that once the data has been keyed in
it will allow individual items of data to be modified - before finally being
displayed as a table.
123
3.15 Central Memory
For more information on formats for storing numbers and characters, refer to
Appendices C & E).
Figure 1 A s1TUJll section ofCentral Memory
H E L L 0
1 2 3
123
Note that it takes up one byte of storage to hold the letter 'H' and five bytes to
hold the characters 'HEUO', three bytes to hold the number 123 in an electronic
format which a screen or printer will recognise and one byte to store the same
number in a compressed form suitable only for computer arithmetic - but
unsuitable for sending to the screen without conversion.
Groups of bytes in memory are further organised into words. A word is the
amount of data that a particular type of .computer is able to fetch from memory
and process in one go. The number of bytes per word depends on the type of
machine being used. In general, a more powerful computer will have a longer
word-length (i.e. more bytes per word). 2 bytes or 4 bytes per word is typical.
124
Certain types of data (e.g, characters) conveniently fit into one byte of storage .
So whenever the programmer defines a char variable, the compiler will, in
general, allocate one byte of memory.
However, for integer data, which is going to be used for arithmetic - a byte will
only hold a small number (between -128 and +127, although if you do not store
the + or - sign it is possible to squeeze in a number up to 255).
A word. however, will allow a much larger number to be held. For example, a
machine with a word-length of 2 bytes would be able to hold an integer between
-32768 and +32767. or up to 65535 if you do not store the sign.
The exact amount of space allocated to any particular type of variable depends on
the compiler- which in tum will have been developed to make the most efficient
use of a particular type of computer.
Exercises
1. Explain : a) byte; b) word.
2. What is the minimum number of bytes that would be needed to hold
variables of type: a) long int b) short int
125
3.16 Pointers
1~~ tQQ:Q§~
The computer, however, refers to every location by a number - called the address
of the location - as shown below (Figure 2).
Figure 2
I
/
<student 9N&J -- -< student mark I
/
/'
0105
So, in this example, the address of studentgrade is 0100 and the address of
studentmark is 0102.
Note that for a variable which takes up a word. the address of the first byte is
used as the address ofthe whole word.
126
It is possible to store this address in another location - for later reference - but first
a suitable memory location must be allocated.
The • before a variable name in a data definition indicates that the variable will
be suitable for holding an address. Such a variable is referred to as a pointer
variable. Note that you usually have to state what type of field (e.g. char, int, etc)
the variable will point to. So markpointer in e.g, I is a pointer to an integerfield.
Figure 3
/
/
-< studenL9!QikJ /
,/
-- --<5ttLdent mark I
So far, the location called markpointer is empty, but the address of another
location can now be stored there:
Exercises
1. Set up a memory location - called studentgradeptr - suitable for holding
the address of studentgrade in the example.
2. Give an instruction which will assign the address of student grade to
studentgrade.ptr. What number will now be stored in
student....grade..,ptr?
127
3.17 Using Pointers
So, ·mark-pointer will now refer to the contents of the memory location whose
address is stored in mark-pointer (i.e. studentmark: - see previous lesson).
Note that. in the example (Figure 4 in the previous lesson). tlUU'k.../lointer refers
to location 0104 . while ·ltUJrk.../lointer and stude"'-ltUJrk both refer to location
0102.
So,
as both statements will wait until the user keys in a number - then store it in
studentmark.
An address can be displayed on the screen by using printf with the %p conversion
character.
will display the address stored in markpointer (i.e, the address of studencmark).
128
Example Program
main()
/*Display grade*/
if (*addrl >= 50)
printf( "Pass\n") ;
else'
printf( ");
lFail\n
/*Finish*/
return 0;
Exercises
1. Write a program which will ask a student for his/her grade and store the
grade in the variable studentgrade - then display a suitable message
depending on the grade. A pointer variable studentgradeptr should be
set up to contain the address of studentgrade. Use this pointer to refer to
student-grade throughout the program.
2. Write a program which will ask an employee for his/her wage and store
the answer in the variable wage (floating point) - then calculate an
increase of 20% and display the answer. Set up a pointer variable
wage-pointer containing the address of wage and use this to refer to the
wage throughout the program.
129
3.18 Passing Pointers as Parameters
One function does not usually have direct access to a local variable defined in
another function. However, it may be given access by passing the variable's
address as a parameter. This allows the function to view and modify the
variable's contents directly.
For example, the task of adding I to the contents of a variable may be delegated
to a function caned increment.
calls function increment and passes across the address of the storage location
count - thus allowing the function to modify the contents of this storage location
directly.
The function must be defined with a suitable formal parameter to accept and store
an address passed across when the function is called. (e.g. int *number-ptr -
would set up a space called number-ptr to store the address of an integer
variable.)
The contents of the variable whose address is passed across (count in this case)
may then be referred to indirectly - in the body of the function - as *number-ptr.
ExampleProgram
iinclude <stdio.h>
main()
{int count;
/*Get a number from user*/
printf (" \nKey in a number") ;
scanf ("%d" &count) ;
I
/*Add 1*/
increment(&count);
/*Display answer*/
printf ("%d\n" I count);
/*Finish*/
return 0;
}
130
3.19 Pointers to Structures
allocates a memory space called studentptr - suitable to hold the address of any
memory area - such as studentdetails - conforming to struct studenttype.
Alternatively, the pattern, the memory space for the student data and the pointer
variable to hold its address can all be set up at the same time.
Either way, the address of the structure studentdetails can then be assigned to the
pointer variable, studentptr.
The individual items in the structure can now be referred to by the name of the
pointer variable followed by -> and the name of the item in the structure.
So, studentdetails.mark would be referred to as student.ptr-z-mark if accessed
via the pointer variable rather than directly by name.
Here, the computer is told to access the address specified in studentptr, then
display the contents of the mark data item within the structure found at that
address.
131
Note that when we are using a pointer to access a structure - NO • is used before
the name ofthe pointer variable (the job ofindicating that we are referring to the
contents ofa location addressed by the pointer is effectively performed by ->.).
The same effect can be achieved by referring to the structure directly by name:
As usual, when a string is to be read from the keyboard using scan/. no & is used
before the name of the string - as it is in itself a pointer (see Lesson 3.20).
Example Program
.include <stdio.h>
main ( )
{/*Set up space for student data and a pointer variable
to hold its address*/
struct student_type
{char surname[20)i
int mark;
} student_details, *student...,ptr;
I*Get student details from user*/
printf("\nPlease key in surname g);
scanf("%s", student...,ptr->surname);
printf( "\nPlease key in mark H);
scanf("%d", &student...,ptr->mark)i
I*Display student report*1
print£("\n***************************")i
printf("\n%s", student...,ptr->surname)i
printf("\nMark: %d ", student...,ptr->mark)i
printf("\n***************************\n");
132
3.20 Pointers and A"ays
Pointers can be used to access the elements in an array (as an alternative to using
an index) .
Unlike the name of most variables, the overall name of an array is in itself a
pointer (to the beginning of the array). So. if an array of ten student grades is set
up in memory -
e.g.l char exam.....grade [10] ;
then the.name of the array. examgrade (without any index) gives the address of
the beginning of the array. (This is why it is not necessary to place & before the
name of a string - i.e, an array ofchar - when using scanf)
A pointer variable can be set up. ready to hold the address of some part of the
array.
The address of the start of the array can then be assigned to (i.e, placed in)
grade-J1lr.
e.g.3 grade....ptr = exam_grade;
So. the pointer variable. gradeptr, now contains the address of the beginning of
the array - in other words the address of the first item in the array. (So.
exam_grade[Oj can be referred to as *grade-J1tr.)
Addressarithmetic
Address arithmetic can be used to move between the items in the array.
The +••• ++ and •• signs may be used to modify the contents of a pointer
variable. However. they have a slighly different effect from their general
arithmetic usage.
e.g.4 grade....ptr++ i
increments grade-J1tr so that it contains the address of the next element in the'
array - in this case the second item (i.e. grade[1J).
This command can be repealed to point to each item in the array in tum.
133
The •• operator decrements the address in the pointer variable so that it points to
the previous item .
e.g .5 grade-ptr--;
If we are starting back at the beginning of the array - with grade..ptr holding the
address of grade{O/:
So, if we are starting with gradeptr holding the address of grader 4/:
modifies the address so that gradeptr now points to the beginning of the array
again (i.e, grade{OJ).
Note that address arithmetic automatically adjusts for the different size of
different types of variable.
For example the items in a character array will each occupy a single byte - so
when ++ is used on the pointer it will add 1 to the address so as to point to the
next item.
However, the items in an integer array will each take up two bytes (or possibly
four). So. ++ will add the requisite number (i.e. 2 or 4) to the address in order to
hold the address of the next item in the array. Compare this with ++ used in
general arithmetic - which always adds J to a number.
The other operators similarly adjust for the size of the data type.
This is why the type of data a pointer will address has to be stated when it is
defined. All addresses are alike in form and size - whatever the type of data to
which they point. However, the system needs to know whether an address is for a
character, an integer, a floating point number, etc. so that it knows what
adjustment to make when address arithmetic is carried out.
134
Comparing Pointers
If the address held in ptr 1 is the same as that contained in ptr2 then a suitable
message will be displayed .
e.g.9 while (ptrl <= ptr2)
{
Main Instructions
ptrl++;
}
Subscripting Pointers
will accept a number keyed in by the user and store it in the first element of the
array whose address is stored inmark-ptr.
Example Program]
tinclude <stdio.h>
main()
(int mark[lO);
int *mark...,ptr, *last_mark...,ptr ;
135
I*Get marks from user and store in array*1
while (mark-ptr <= last~ark-ptr)
(
printt ( "\nKey in mark ") ;
scant ( "%d", mark-ptr);
mark-ptr++ ;
I*Finish*1
return 0;
ExampleProgram2
tinclude <stdio.h>
main()
{int mark[lO] i
int *mark-ptri
int student ;
I*Assign address of beginning of array to mark-Ptr*1
mark-ptr = mark;
I*Get marks from user and store in array*1
for (student = 0 ; student <= 9; student++)
{
printf ( "\nKey in mark ");
scant ( %d", &mark-ptr [s tuden t] ) ;
II
I*Finish*1
return 0;
Exercises
1. Rewrite the Example Programs from Lessons 3.9 and 3.14 - using a
pointer variable and address arithmetic.
2. Rewrite the Example Programs from Lessons 3.9 and 3.14 - using a
pointer variable and a subscript to the pointer variable .
136
3.21 Passing Arrays as Parameters
For example, suppose the list of marks for 10 students is held in an array defined
as int maths_mark[lO] - the work of displaying the marks might be delegated to
function display_marks .
The function itself will be defined as expecting one parameter, which can be
specified as - for example - int mark[] or int "'mark. Both have the same effect -
of setting up a memory space suitable to hold the address of an integer variable.
The former may be clearer for documentation purposes - to indicate that the
address of (the first element of) an integer array is expected. Note that the use of
array notation for a formal parameter does not set up an array - it is simply an
alternativenotationfor a pointer variable.
e.g.2 void display.....marks (int mark [ ])
{int count;
for (count = 0; count <=9; count++)
printf ("%d\t%d\n", count , mark [count] );
}
Alternatively, the notation *mark may be used to refer to the items in the array
maJhs_mark - so the printf statement becomes
e.g .3 printf ( %d\ t%d\n·, count, *mark++);
II
Note that the ++ will increment the address after the contents of the 11remory
space it addresses has been displayed.
The fact that the address of an array is passed means that the function which
receives it can directly modify the original data - not just a copy (or place new
data into the array if it presently contains none) .
So the work of filling the array could be delegated to function getmarks - which
could be.called and defined in a similar way to displaymarks.
Example Program 1
iinclude <stdio.h>
iinclude <stdio.h>
void get_marks (int []);
void display_rnarks(int []);
137
main()
(int maths_mark[lO) ;
get_marks(maths_mark);
display_marks(maths-mark) ;
return 0;
}
void get_marks (int mark[))
(int count;
for (count = 0; count <= 9 ; count++)
(
printf ( "\nKey in mark for student %d" count) ;
scanf ( " %d", &mark [count) ) ;
Example Program2
iinclude <stdio .h>
iinclude <stdio .h>
void get_marks (int [)) ;
void display_marks (int [));
main()
(int maths_mark[lO);
get_marks(maths-mark) ;
display_marks(maths-mark);
return 0;
}
void get_marks (int mark[])
(int count;
for (count = 0 ; count <= 9; count++)
(
printf (" \nKey in mark for student %d" count) ;
scanf ( "%d", mark++);
Exercises
l. Write a program which delegates, to function sort, the task of sorting an
array of 10 numbers.
2. Write a program which delegates. to function loCaps, the task of
converting all the letters in a name into capitals.
138
3.22 Arrays ofPointers
An array of pointers may be set up in the same way as any other array.
e.g.l int *ptr_to_mark[5];
Of course, the memory spaces for the actual data have to be set up separately.
The addresses of each of these variables can then be assigned to the elements of
the array ptr_to-,no.rk.
The contents of any specific variable can then be referred to either directly (by the
variable name) or indirectly via its address held in th.e array.
So,
(display the contents of the variable whose address is held in the first element of
the array ptr_to-,no.rk)
Using an array of pointers rather than an array of strings can be an efficient way
to store a number of messages.
e.g.6 char *ptr_to_msg [3] ;
The messages can be set up in memory and the elements of the pointer array
initialised to point to the messages - as follows.
e.g.? ptr_to_msg [0] = "Welcome to the Computing
Course\n" ;
ptr_to_msg[l] = "Welcome to the Science Course\n";
ptr_to_msg[2] = "Welcome to the Language
Course\n" ;
So. the address of the first character of each message will be placed in the
relevant element in the array ptr_to_msg. Note that the address of a string is
139
always given directly - either by the name of the string (if it is stored in a named
character array) - or as aboveby the actualstring literal.
Alternatively, the array could be initialised as follows:
Again, the messages are set up in memory, an array called ptr_to_msg is set up
and the addresses of the first character of each message are stored in the relevant
element of the pointer array. There is no need to specify the number of spaces in
the array ptr_to_msg as the compiler will obtain this figure (3) from the actual
number of strings . .
Example Program
iinclude <stdio .h>
main()
{char course_code;
char *ptr_to..,msg[] =
{"Welcome to the Computing Course\n",
"Welcome to the Science Course\n",
"Welcome to the Language Course\n",
};
/*Finish*/
return 0;
140
Exercises
1. Write a program which will ask for a student's mark out of five in an exam
then display a suitable message.
2. Write a program which will ask for a student's grade in an exam ('A' to 'F')
and display a suitable message. Note that the letter j\' in the ASCII code
is represented by 65. So subtracting 65 from the character code for 'A'
will reduce it to O.
3. Write a program which will display the name of the day of the week when
the user keys in the day 's number (e.g. 1 produces "Sunday").
4. Write a program which will display the name of the month of the year
when the user keys in the month number.
141
3.23 Pointers to Pointers
A pointer variable may be set up to hold the address of another pointer variable.
allocates memory space for a variable mark (to hold an integer number), a pointer
variable ptr_to.mark (to hold the address of an integer variable - such as mark)
and a pointer variable ptr_to-ptr_to_mark (to hold the address of another pointer
variable - such as ptr_to_mark).
places the value 50 in mark; the address of mark in ptr_to_mark; and the address
of ptr_to_mark in ptr_to-ptr_to_mark- so that the memory locations are linked
as below.
The value of mark can now be accessed directly by referring to the variable by
name:
or by telling the machine to access the variable whose address is stored in the
pointer variable whose address is held inptr_to-ptr_to_mark.
Exercises
1. Write a program which will allow a teacher to key in a student's mark and
which will then display a grade ("Pass" for a mark of 50+ or "Fail" if the
mark is less than 50). Refer to the variable mark throughout your program
via the pointer to pointer variable ptr_to-ptr_to_mark.
2. Modify the program so that it deals with marks which might include
decimal fractions.
142
3.24 Pointers to Arrays ofPointers
sets up three messages in central memory together with an array of pointers - each
element of which is initialised to hold the address of the beginning of one
message.
It is now possible to set up a variable to hold the address of the first element in
this array.
The address of the beginning of the array may be placed in this variable (Note
that the name ofan array represents its address).
ptr_to_array
address - > P a s s \n \0
address - > M e r i t \n \0 I
This could be defined using the ** notation shown above - e.g. char **array-ptr
or the notation char *array-ptr{J- which means a pointer to the beginning of an
array ofpointers to char . These are just two different ways of specifying exactly
the same sort of variable.
So, the function definition could take either of the following forms .
143
e.g.5 void functionl (char *array-ptr [] ) ;
(
Function instructions
)
Within the function the messages can be referred to either by subscripting the
pointer variable - for example - array-p1r{OJ (no * needed because a string is
referred to by quoting its address) to display the first message; or using the
*
normal pointer notation - for example - *array-p1r (only one again because a
string is involved) which will display the message whose address is currently held
in arrayptr.
Example Program
iinclude <stdio.h>
void display_countries(char *country_table-ptr[]);
main()
{/*Set up names of four countries in memory and array
of pointers referencing them*/
char *ptr_to_country[] =
("United Kingdom", "United States of America",
"Australia", "Fr an c e " ) ;
/*Call function passing across address of beginning of
array of pointers (address given by name of
array) */
display_countries(ptr_to_country);
/*Finish*/
return 0;
Exercises
Each of the following programs should hold the main data and the arrays of
pointers as local variables within function main.
1. Write a program which will set up the names of ten capital cities in
memory together with an array of pointers to reference them. The user
should then be allowed to key in a number (0 to 9) and the work of
displaying the name should be delegated to a function .
2. Write a program which will set up the verbal form of the digits 0 to 9 in
memory together with an array of pointers to reference them. The user
should then be able to key in a single digit and the work of displaying the
verbal form should be delegated to a function.
3. Write a program which will hold the Roman numerals I, II, III, IV, V, VI,
VII, Vlll, IX, X in memory - with an array of pointers to reference them.
When the user keys in a number (1 to 10) the task of displaying the
Roman equivalent should be delegated to a function .
144
3.25 Dynamic Memory Allocation - Requesting and Releasing
Space
So far, we have set aside memory spaces for any items of data that the program
might use - when writing the program. So, if the program might need to store a
student's grade then we have defmed a suitable variable. Likewise, if the program
is likely to have to hold in memory the grades for a whole class of students then
we have set up an array large enough to store the grades for the likely maximum
number of students in a class.
asks malloc to find a vacant l-byte space, uses the cast (char *) to indicate that
the value returned by malloc will be treated as a pointer to a space containing
character data, and stores the address in grade ptr (which should have been
previously defined as ehar *grade,.ptr).
displays an error message and exits from the program if malloc cannot find space.
The same instructions can be written more concisely (see Lesson 1.39) as:
e.g.3 if (! (grade....,ptr = (char *) malloc (l) ) )
{
printf ("No space available for data \n ") i
exit(l);
145
It is usual to use sizeof and the data type, when requesting space to let the system
work out for itself how much space to allocate - as some data types will differ in
size between machines. For example, an integer might be two bytes on one
machine and four bytes on another. So, sizeoj(int) will give the correct number of
bytes for the machine - so maUoe(sizeoj(int)) will return the address of a space big
enough to hold an integer.
asks malloc for a space large enough to hold an integer, uses a cast (int "') to
indicate that the address returned will be treated as a pointer to a memory space
containing an integer, then places the address in markpointer (which should
have been declared previously as int "'mark-pointer).
Similarly, malloc can be asked for a space large enough to store a structure.
e.g.5 student...,ptr = (struet student_type *)
malloe(sizeof(struet student_type));
Exercises
I. Write commands to request:
a. one byte of memory and store the address in letter-ptr;
b. a space large enough to hold an integer and store its address in
numptr;
c. a space large enough to hold a structure of type struet
employee_type;
2. Write commands to release each of the spaces allocated above.
146
3.26 Dynamic Memory Allocation - Using Space
A newly allocated space can be accessed by using the pointer to refer to it. Note
that a new space allocated by mlIUOC has no name - we only know it by its
address.
e.g.l *grade...,ptr = I DI ;
assigns the grade 'D' to the memory location whose address is stored in
grade-ptr.
displays the contents of the memory space whose address is stored in grade-ptr.
e.g.3 scanf ( " %c", grade...,ptr);
allows the user to key in a character then stores it in the memory space whose
address is held in grade-ptr.
allows the user to key in an integer then stores it in the memory space whose
address is held in mark-pointer.
assigns 50 to the mark field of the structure whose address is currently held in
studentptr.
e.g.8 printf l " %d", student...,ptr->mark);
displays the contents of the mark item within the structure whose address is stored
in studentptr,
e.g.9 scanf ("%d", &student...,ptr->mark);
allows the user to key in an integer then stores it in the mark space of the structure
whose address is held in studentptr.
147
Example Program 1
iinclude <stdio.h>
iinclude <stdlib .h>
main()
{char *grade-ptr;
I*Request space to store a grade*1
if (! (grade-ptr = (char *) malloc(l)))
{
printf ("No space available for data\n");
exit (1) ;
I*Finish*1
return 0;
Example Program 2
iinclude <stdio.h>
iinclude <stdlib .h>
main()
{struct student_type
( char surname(20);
int mark;
*student-ptr;
I*Request space to store student data*1
if (! (student-ptr = (struct student_type *)
malloc(sizeof(struct student_type))))
printf ("No space available for data\n");
exit(!) ;
I*Finish*1
return 0;
148
3.27 Linked Lists
An alternative approach is to allocate no space for the list of data items when the
program is written - but include commands which will ask the system to find
space as it is needed. So, as each student's grade is keyed in, the program will
request a space to store it.
One way of storing data using dynamic memory allocation is in a linked list.
Each item of data is stored together with the address of the next item in the list.
The address of the first item is kept in a pointer variable which was set aside for
the purpose when the program was written. The items may be stored near each
other - or far apart - depending on where the system can find free memory spaces
when each request is made.
jirscstudent-ptr
~
cF E}->IBill EJ-+ulial~
Starting a new list
A pattern will be needed for a structure capable of holding the data plus the
address of the memory space holding the next structure in the list.
A memory space set up with this structure type would hold a student's name and
mark and the address of the space in memory which held the next student's data.
Note that there is no need to set up any actual storage space at this stage (just a
pattern) as space will be allocated for each student as needed when the program
is running.
149
Three pointer variables will be needed to hold:
a) the address of the first data structure in the list;
b) the address of the data structure currently being processed ;
c) the address of the previous data item processed .
e.g.2 struct student_type *first_student-ptr,
*current_student-ptr, *previous_student-ptr;
The pointer to the beginning of the list should be initialised to NUll to show that
it is not pointing to any data record as yet.
Adding an item
In any case - it will be necessary to get space to hold the item (using malfoe)- and
store its address in the pointer variable set aside to hold the address of the current
item.
e.gA if (! (current_student-ptr = (struct student_type*)
malloc(sizeof(struct student_type))))
printf("Error - insufficient memory\n") ;
exit(l);
gets space via malloc, places the address in currenrstudent ptr and if malloc was
unable to find space displays an error message and exits from the program.
Data can then be accepted from the user and placed in the memory space set
aside.
e.g.5 printf (" \nName ?");
scanf("%s", current_student-ptr->name);
printf (" \nMark ?");
scanf ("%d", ¤t_student...,ptr->mark);
gets data from the user and stores it in the space just allocated (whose address is
held in currenrstudentptr).
150
The item may be inserted at the beginning of the list - by setting its next record
pointer to point to the current first record (which then becomes the second item in
the list) and setting the first item pointer to point to the new item.
e.g.6 current_student-ptr->next_student-ptr =
first_student-p t r;
first_student-ptr = current_student-ptr;
8-> 8J
jirsUtudent../1tr (New item)
lAde
8-> I Bill
If the list was previously empty (indicated by the fact that the pointer to the
beginning of the list contained NULL) - e.g.6 will leave the list as shown below.
jirsCstudent../1tr (New item)
8-> lAde I~
The item may be inserted in the middle of the list (once a suitable position is
found) by placing the address of the next item in the next record pointer of the
new item.
e.g.? current_student-ptr->next_student-ptr =
previous_student-ptr->next_student-ptr;
jirstJtudent../1tr
and setting the next record pointer of the prevous item to point to the new item.
e.g.8 previous_student-ptr->next_student-ptr =
current_student-ptr;
jirscstudent../1tr (New item)
8->~ IJUlial~
~
151
To add an item to the end of the list consists of indicating that this is presently the
last item in the list (until the next one is added) by setting the next item pointer
variable within the structure to NUU.
~
cF EJ-+ill
and changing the next item field of the previous item from NUll to the address
of the current item - to form a link .
e.g.1O previous_student-J;>tr->next_student-J;>tr =
current_student-J;>tr;
jirsCSludelll..,plr
I
> TOM
Removing an item
To remove an item from the beginning of the list, the pointer to the first item
should be changed to point to the second item in the list or to the value NUll if
there was only one item. This can be achieved by copying the contents of the
next item pointer from the item that is to be removed - into the first item pointer.
e.g.ll first_student-J;>tr =
current_student-J;>tr->next_student-J;>tr
jirs/Jludefll..,plr
~ LI
~I~e-----·Ann~>
. ~> Bill
152
The memory space can then be released.
jirst-student-ptr
>IBill 13->IJUliaE]
To remove an item from the middle of the list, the item before the one to be
removed should be linked to the item after it.
IBill
The unwanted memory space can then be released.
jirscstudent-ptr
L>..-__
IJulia 8
To remove an item from the end of the list (indicated by the fact that its next item
pointer holds NUU), the previous item's next item pointer should be set to NUU.
This can be achieved by using the same command as in the previous example.
jirscstudent-ptr
153
Again the memory can then be released.
jirscsludencplr
B
cF EJ->IBill EJ
Moving to the next iJem
Moving to the next item in the list consists of copying the next item pointer of the
current item into the current item pointer.
e.g.14 current_student...,ptr =
current_student...,ptr->next_student...,ptr;
Generally, it will also be useful to adjust the previous item pointer so that it
always holds the address of the item before the current item. This has to be done
before the above instruction alters the contents of the current pointer.
Example Program
154
do
{/*Display Menu and get choice of action*/
printf (" \fMENU\n\n") ;
printf ("Add student to class list Lvn " ) ;
printf ("Display list - allow deletions 2\n") ;
printf ("Exit O\n") ;
scanf("%d", &choice);
/*Carry out required action*/
switch(choice)
(
case 1: add_student(); break;
case 2: display_list(); break;
case 0: break;
default: printf("Error - please rekey\n ");
}
while (choice != 0);
/*Finish*/
return 0;
void add_student(void)
{
/*Request memory space*/
if (! (current_student-ptr = (struct student_type*)
malloc(sizeof(struct student_type))))
printf ("Error - insufficient memory\n");
exit (1);
155
}
else
/*Add new student in middle or at end of list*/
{
current_student-ptr->next_student-ptr =
previous_student-ptr->next_student-ptr;
previous_student-ptr->next_student-ptr =
current_student-ptr;
void display_list(void)
{char option;
do
{
if (option == 'D')
remove_student();
else if (option != ' N ' )
{
printf ("Error - please rekey\n");
seanf (" te", &option);
}
} while ((option != 'N') && (option != 'D'));
/*Next student*/
previous_student-ptr = eurrent_student-ptr;
eurrent_student-ptr =
eurrent_student-ptr->next_student-ptr;
156
/*FUNCTION remove_student - should be called with
current_student-ptr pointing to the record to be
removed and previous_student-ptr pointing to the
previous record (unless first or only record to be
removed)"'/
void remove_student(void)
{/*Unlink record from list"'/
if (current_student-ptr == first_student-ptr)
/"'Remove first (or only) item from list*/
first_student-ptr =
current_student-ptr->next_student-ptr;
else
/"'Remove item from middle or end of list"'/
previous_student-ptr->next_student-pt r =
current_student-ptr->next_student-pt r;
/"'Release Memory"'/
free(current_student-ptr);
}
157
3.28 Circular Linked Lists
A circular linked list may be useful for holding data items which are dealt with in
rotation - with the first item being used or I processed again as soon as all the
others have had their tum - and so on.
A tutor in a college makes regular appointments to talk to students in his/her
group individually about their work -,so that one student is seen each lesson. As
soon as all the students have been seen (after some weeks), it is time to start
again with the first. If the records for the interviews are computerised, then the
data might usefully be held in a circular linked list.
Each data item in a circular linked list holds a pointer to the next one Gust as in
any linked list) but there is no end to the list as the 'last' item points to the first.
EJ L
[> AIm I a - = > ''''''-Bi-ll-Ej-> Julia I 8=J
As usual, it is likely that a program will need facilities to add an item to the list,
delete an item, go to the next item, go through the whole list, search for an item
and process an item in some way (e.g modify the data, display the contents, or
save the record on disk).
As usual with linked lists a pattern for a suitable structure will be needed.
sets up a structure type consisting of the student's name, and a pointer to the next
record in the list.
158
A pointer to hold the address of the record currently being processed will be
needed and will usually be initialised to NULL (i.e, pointing to nothing).
Adding an item
If the list already contains records, then the position-ptr must be set to hold the
address of the record before the position where the new record will be inserted.
(In some cases this might involve searching for the correct position. In the
example, the position ptr will already be set to the address of the last record
processed - which might be a suitable place to add a new student so that he/she
automatically goes to the 'end' of the rota.)
Then the pointer_to_nexcrecord of the new record (whose address is still in
temp-ptr) will have to be set to the address of the next record in the list. (This
address is currently held in the record whose address is stored in posttion ptr.;
159
e.g.6 temp..,ptr->pointer_to_next_record =
position-ptr->pointer_to-next_record;
and finally, in the example application, the new record's address will be copied
from temp-ptr into position ptr (although this will vary according to processing
requirements).
The user can then be asked for basic details to store in the record - in this case the
name.
e.g.9 printf ( " \nKey in student s name"); I
gets(temp-J?tr->name) i
So, if the new student's name (e.g. David) is keyed in the list will now be as
follows.
160
Removing an ilem
It may be necessary to search the list for the item that is to be removed. H so, the
user can be asked for the name of the student who is leaving the class - then a
function can becalled to search for the record.
If the item is the only one in the list (determined by the fact that the
pointer_to_nexcrecord points back to the record itself) then the memory area
can be released and position-ptr set to NUll.
Otherwise, if there are several items, the position-ptr should beset to the address
of the record before the one that is to be removed. (This can easily be done by
referencing each record via the pointer contained in the previous one - when
searching.) In this way position-ptr will automatically be pointing to the
previous record when the required one is found.
To remove the record, its address (available from the pointer_to_nexcrecord of
the previous record) should be stored temporarily, so that it can later be passed as
a parameter to free.
e.g.I I temp....ptr =
position""ptr->pointer_to_next_record;
Then, the previous record's pointer_lO_nexcrecord should be set to point to the
next record after the one which is being removed.
e.g.l2 position""ptr->pointer_to_next_record =
position....ptr->pointer_to_next_record->
pointer_to_next_record;
position-ptr
16J
Finally. the space occupied by the record removed from the list can be released
using free .
e.g.l3 free (temp-ptr) ;
posi/ion-p"
L> __-,
IJulia 8=]
Moving to the next item
The address of the next item in the list is stored in the current item's
to
p o i m e r record, so this can be copied into the
n e x t thus
p o s i t i o a p t r -
The data can then be processed (e.g, displayed. modified. or compared with some
search value).
e.g.l5 printf (" \n%s", position-ptr->name);
will display the name of the student currently being dealt with.
If an item is to be displayed. then the program will need the address of that item;
if however. it is to be deleted. the program will need the address of the previous
item in the list. so that its pointer_Io_nexcrecord can be altered to point past the
item to be removed and on to the next item.
So. a search might conveniently be carried out by a function which can return
either the address of the item required or the address of the item before the one
required.
Such a function will need three parameters - the start address for the search. the
name of the student whose data is required. and an indicator stating whether the
required item's own address or the previous item's address should be returned.
Searching for an item involves moving onto the next item repeatedly until the
required record is found, or until the search gets back to where it started (if the
item sought turns out not to be in the list).
The function can then return the address of the required record. or the address of
the previous record - or if the record was not found - an indicator (e.g. NULL) to
show this.
163
Example Program
iinclude <stdio.h>
iinclude <stdlib.h>
iinclude <string .h>
void add_student(void);
void remove_student(void);
void display_next_student(void);
void display_require~student(void);
struct student_type *seek(struct student_type * char
[], char );
void error_routine(void);
struct student_type
{char name[15];
struct student_type *pointer_to_next_record;
} ;
/*Finish*/
return 0;
void add_student(void)
{/*Request space*/
if (! (temp-ptr = (struct student_type *)
malloc(sizeof (struct student_type»))
error_routine();
164
/*Link new record space into list*!
if (position-ptr == NULL)
{/*Add first record to list*!
position-ptr =
temp-ptr;
position-ptr->pointer_to_next_record
position-ptr;
}
else
{/*Add record to others already in list*/
temp-ptr->pointer_to_next_record
position-ptr->pointer_to_next_record;
position-ptr->pointer_to_next_record = temp-ptr;
position-ptr = temp-ptr;
scanf("%s", temp-ptr->name);
}
void remove_student(void)
{char student-name[lS];
!*Get name of student to remove from class list*!
printf ("Name of student to remove ? \n" ) ;
scanf ("%S", student_name);
!*Search list for name*/
if (! (temp-ptr = seek (position-ptr, student_name,
o PO) ) )
position-ptr = temp-ptr;
if (position-ptr ==
position-ptr->pointer_to_next_record)
(/*Remove only record from list*!
free(position-ptr);
position-ptr = NULL;
}
else
(
/*Remove record i f others in list*/
temp-ptr =
position-ptr->pointer_to_next_record;
position-ptr->pointer_to_next_record =
temp-ptr-> pointer_to_next_record;
free ( temp-ptr) ;
}
}
void display_next_student(void)
{
position-ptr =
position-Ptr->pointer_to_next_record;
165
printf ( \n%s ", position-ptr->name);
II
void display_require~student(void)
{char student_name[15];
/*Get name of student to display*/
printf ("Name of student to display ?\n");
scanf ("%s", student_name);
/*Search list for student*/
if (! (temp-ptr = seek (position-ptr, student_name,
'C')) )
/*Display Record*/
printf ("%s\n", temp-ptr->name);
void error_routine(void)
(
printf("Insufficient Memory\n");
166
3.29 Queues
Customers in a shop might form a queue waiting to be served. In the same way, a
queue can be set up in the computer's memory holding - for example - details of
jobs that a manager must attend to, or the names of students waiting to see their
tutor. A computer might use a queue to keep details of programs waiting to be
run.
A queue can be set up as a type of linked list using pointers. When the queue has
been formed, one pointer variable will hold the memory address of the item at the
front of the queue, another will hold the address of the item at the back of the
queue and each item will include (in addition to its own data) the address of the
next item in the queue.
~
~ EJ-+m
A program which processes a queue will need - at least - a function to add items
to the end of the queue, and another function to process and remove items from
the front 'of the queue .
The program will need a structure type (pattern) to create suitable memory spaces
as they are needed to hold the data items.
is a suitable format to set up memory spaces to hold a student's name and the
address of the memory space holding the details of the next student in the queue.
Pointer variables will be needed to hold the memory addresses of the front and
back of the queue .
167
Adding new items to the queue
When the program runs, it will need to request space for each new item added to
the queue.
If the value returned is NULL then an appropriate message must be displayed and
action taken .
Otherwise, the data can be obtained from the user and stored in the new memory
space .
Elf---------------->I David D
Otherwise (if there are already one or more students in the queue), the previous
student's next in queue pointer should now be set to point to the address of the
new student's details . Note that the address of the previous student's details will
currently be held inend_of-queue..plr.
at:
fronl_of-queue-pl r enlCo!-queue-plr (New ilem)
~ I
> DaVidD
I
cF EJ-+"
r--..,-----,
a - = > Julia
168
The end of queue pointer can now be updated to address the new student's data.
Iaddr~~ I D David
The new student's next in queue pointer should be set to NULL to indicate that
he/she is the last in the queue.
~
cF 8->IBill
Processing items at the front ofthe queue
First of all, it is important to check whether there are any items in the queue. This
can be ascertained by checking whether the front of queue pointer is set to NULL.
If froniofqueue contains NULL (i.e, points to nothing) then the computer will
display a suitable message and return to the calling function without proceeding
any further .
The front of queue pointer will give the address of the first item in the queue for
processing (e.g. display on the screen).
e.g.I1 printf ("%s\n", front_of_queue-ptr->namel;
The item can then be removed from the queue. First, set the temporary pointer to
address the item to be removed.
e.g.12 temp-ptr = front_of_queue-ptr;
169
Then, if this is the only item in the queue (which can be ascertained by checking
whether the first item's next in queue pointer is NULL), the front of queue pointer
and end of queue pointer should be set to NULL. Otherwise, if there is a second
item in the queue set the front of queue pointer to address this.
IAnn
Finally, the space occupied by the removed item can befreed to the system .
froncoLqueue~tr
170
Example Program
*include <stdio.h>
*include <stdlib.h>
struct student_type
(char name[20);
struct student_type *next_in_queue-ptr;
};
void add_student_to_queue(void);
void deal_with_first_in_queue(voiq);
void main ( )
{int choice;
front_of_queue-ptr = en~of_queue-ptr = NULL;
do
(/*Display menu and get choice*/
printf ("Add student to queue 1 \n") ;
printf("Deal with first in queue 2\n") ;
printf (" Finish O\n") ;
!*Finish*!
return 0;
void add_student_to_queue(void)
{struct student_type *temp-ptr;
!*Request space for new addition to queue - i f no space
available display message and return to function main*!
if ( ! (temp-ptr = (struct student_type *)
malloc(sizeof(struct student_type»»
printf ("No space available\n") ;
return ;
171
/*Set front of queue or previous student's next in
queue pointers as appropriate to point to new addition
to queue*/
if (front_of_queue-ptr == NULL)
frorit_of_queue-ptr = temp-ptr;
else
end_of_queue-ptr->next_in_queue-ptr = ternp-ptr;
/*Set end of queue pointer to point to new addition to
queue*/
end_of_queue-ptr = ternp-ptr;
/*Set new student's next in queue pointer to indicate
that he/she is last in queue*/
ternp-ptr->next_in_queue-ptr = NULL;
void deal_with_first_in_queue(void)
{struct student_type *ternp-ptr;
char remove ;
/*Check if any students in queue*/
if (front_of_queue-ptr ==NULL)
{printf ("No students in queue\n") ;
return; }
/*Display first student in queue and check if OK to
remove*/
printf ("%s\n", front_of_queue-ptr->narnel;
printf ("OK to remove from queue?\n ") ;
scanf (" %c", &remove);
if (remove != 'YO)
return;
/*Remove item from queue*/
ternp-ptr = front_of_queue-ptr;
if (ternp-ptr->next_in_queue-ptr == NULL)
front_of_queue-ptr = en~of_queue-ptr = NULL;
else
front_of_queue-ptr =
ternp-ptr->next_in_queue-ptr;
free (ternp-ptr) ;
Exercises
1. Write a program which will allow a business person to keep a list of jobs
to do. Jobs are processed in order - and removed from the list as they are
done. New tasks are added to the end of the list The information on each
task should include a title and a brief (up to 80 characters) description.
2. Write a program which will allow a salesperson to keep a list of orders.
These will be processed in the order they were received and inquiries
taken off the list as soon as the goods are sent. (Asswne that all goods
ordered are in stock.) The data stored should include the name of the
customer, a description of the goods ordered, the stock item code, the
quantity, the price per item and the total cost (calculated by the computer).
172
3.30 Trees
A tree is a useful way to store a hierarchy in memory - e.g. the organisation chart
a
for a business. The simplest type of tree is binary tree - where each node (i.e.
item in the tree) has up to two branches . Such a tree might be used to store - for
example - a chart of someone's parents, grandparents, etc. It can also be used as
an efficient way to store data so that it can easily be retrieved in order (or so that
any individual item can quickly be found) .
Each node in a binary tree consists of data together with (generally) two pointers -
the first addressing the left branch and the second the right. Other pointers may
be added - such as one to hold the address of the parentnode.
A pointer to address the root of the tree will be needed. If the tree is empty - then
this will be set to NUll.
The following example binary tree holds the names of a group of people - in
alphabetical order. The leftmost node contains Andy and the rightmost Jane.
rootptr
B---------
>IANN ~
l->-IAND--Y-~-NU.-L-L-INU.-L-; ~Ea
To process the example tree - facilities would probably be needed to add a new
item, display the data in order, remove an item , or search for a specific item.
A pointer variable can then be set up to hold the address of the tree's root node.
e.g.2 struct node *root-ptr;
173
A number of other pointer variables will be needed - how many and where they
are defined will depend on the program design.
Adding an item
First of all the program will have to request space to store the new data.
The data can then be obtained from the user and stored in the space allocated by
malloc.
e.gA printf (" \nName ? ") ;
scanf{"%s", temp-ptr->name)i
The left and right pointers for the new node should then be set to NUll to
indicate that as yet there are no branches from the new item.
If tree is currently empty (indicated by the pointer to the root node being set to
NULL) - then the root pointer will have to be set to point to the new node.
rootptr
81---------> IDAVID~
Otherwise, a search will have to be made for the proper place to insert the new
item .
This will be done by comparing the new item with each node - starting at the root
- until the correct position is found. The search will move leftwards or rightwards
depending on whether the key (name) of the new item is lower or higher than the
key for the existing item currently being compared. The new item will be
inserted as soon as an empty branch is found .
174
For example. the record for a new student - BOB - will first be compared with the
root node (i.e. DAVID).
rootptr (compare)
81---------
>IANN ~ ----'~
[r->-I-AND--Y-~-NUL--L-INu.-'L-; ~Ea
BOB is lower in the alphabet than DAVID - so the search will move to the left and
BOB will be compared to the left branch node of the root - i.e, ANN.
rootptr (compare)
81---------
I
>~~
L-IAND--Y-~-;-L-~~Ea
175
As BOB is higher alphabetically than ANN - the search will move to ANNs right
branch node - COliN.
rootptr
8f---------:
>IANN ~ ___Ea
l->-IAND--Y-EEJ--L-l-NUL-;-~INUL+=I
(compare)
~OB I I I
NULL NULL
BOB is lower alphabetically than COliN - so the search move rightwards again -
but there is currently no left branch node from COUN. So, the record can be
inserted here by setting COUNs left...ptr to address the new record.
rootptr
8--------:
>IANN_~
__
~-~--r----r---,
176
Removing an item
First the addresses of the unwanted node and its parentnode must be found.
The parent node should be unlinked from the unwanted node and the relevant
pointer altered so that it points past the unwanted item to one of its child nodes
(e.g. the left branch).
e.g.? parent_node->left...,ptr =
unwanted-node->left...,ptr;
or
parent_node->right...,ptr =
unwanted-node->left...,ptr;
(Depending on which pointer contains the address of the node which is to be
removed.)
So, in the following example the record for ANN is unlinked from its parent node
- whose left pointer is altered to point to the record for ANDY.
root-ptr
8r---------
__IANN_~ddr
addr
L-I
> ANDY I~
~---,-----,
> '-C_O_L_I_N..L.-T""'--'-_----'
>1 8 0 8 ~
177
The right branch from the unwanted node can then be inserted in a suitable place
in the tree - using the same process as for inserting a new node .
root-p1r
8t---------
> ,.---,---,----,
>
> ,.---,----,----,
>
rool-ptr
81---------
>IBOB ~
If the root node itself is to be removed, then the method is similar - except that the
rootptr will be set to point to the left branch node from the unwanted node;
again the right branch will be inserted in a suitable place in the tree.
178
Displllying the tree
A recursive function call can be used here. The tree is considered as being made
up of lots of smaller and smaller trees - with the simplest being a leaf node (i.e.
having no branches) .
So. looking at the example tree below. if display_tree is called (first call) with
rootptr (i.e. the address of the node holding data on David) as the parameter. it
will then carry out the Process left branch section and call itself (second call)
passing on the address of Ann and then again (third call) passing on the address of
Andy and then again (fourth call) passing the left...jJtr from Andy - which is NULL
- each time storing the address passed across in a new formal parameter (variable)
called current.nodeptr - (there is one variable of this name for each call). So
far. nothing has been displayed .
At this point. the the fourth call of the function will note that the address passed
was NULL and return to the previous call to carry on processing the node for the
third call - Andy - which will be displayed by the printf statement.
The Process right branch node part of the third call will then be carried out - and
display_tree will be called with the right...jJtr from Andy. As this node is NULL
the function will immediately return to processing Andy. The end of the third call
to function is reached and control returns to the second call of the function and
the Process current node part of dealing with Ann is carried out - displaying the
name.
This process continues until the whole tree has been displayed in order.
rootptr
8:----------
>IANN ~
[r->-I-AND--Y-~-L-L-INu.-L-;- ~Ea
179
Example Program
tinclude <stdio.h>
tinclude <stdlib.h>
tinclude <string .h>
struct node
{char name[20];
struct node *left-ptr ;
struct node *right-ptr;
};
!*Finish*!
return 0;
}
180
exit(l);
181
void display_tree(struct node *current_node-ptr)
{/*Check if subtree is empty*!
if (current_node-ptr == NULL)
return;
!*Process left branch*!
display_tree(current_node-ptr->left-ptr);
!*Process current node*!
printf ( "%s\n", current_node-ptr->name);
!*Process right branch*!
display_tree(current_node-ptr->right-ptr);
void remove_item(void)
(char name [20) ;
struct node *current_node-ptr, *parent_node-Ptr;
int found = 0;
!*Check if tree is empty*!
if (root-ptr == NULL)
{
printf ("No data in tree\n");
return;
182
/*Itern not in tree*/
{
printf ("Not found\n");
return;
}
else
/*Search to right for itern*/
{
parent_node-ptr = current_node-ptr ;
current_node-ptr =
current_node-ptr->right-ptr;
}
} while (! found);
/*Unlink node*/
if (current_node-ptr == root-ptr)
/*Rernove current root node and set current root' s
left branch node as root*/
root-ptr = root-ptr->left-ptr;
else if (parent_node-ptr->left-ptr --
current_node-ptr)
/ *Adjust parent node's left pointer to address left
branch node of unwanted node*/
parent_node-ptr->left-ptr =
current_node-ptr->left-ptr;
else
/*Adjust parent node 's right pointer to address left
branch node of unwanted node*/
parent_node-ptr->right_ptr =
current_node-ptr->left-ptr;
/*Release rnerno~*/
free(current_node-ptr);
Exercises
1. Explain fully the steps in displaying the example tree using the recursive
function shown .
2. The present find and insert function works iteratively. Write an
alternative recursive function to do the same job.
3. Write a program which will allow a user to keep data on employees in a
tree. Each node should hold the employee's surname. department and
salary. The tree,should be kept so that employees can quickly be located
by their surname. Your program should cater for new staff and for staff
leaving. It should also allow an existing employee's department and salary
to be modified.
4. What would be the advantages of including other pointers - e.g. to address
a node's parent node as well as its left and right branch nodes? Write a
program which will process a tree (of staff details) constructed in this way.
183
3.31 Files
So far, all the programs in this book have stored any data that they have used
(numbers, names , etc) in the computer's central memory. This is only temporary
storage and the data remains available only as long as the program is running (or
a shorter time than that if new data is keyed in to overwrite the old contents of a
storage location) . This means that the next time the program is run - aU the data
has to be typed in again.
If the data needs to be kept for another time then the program will need to include
instructions telling the computer to copy relevant items from central memory onto
a backing-storage medium, such as a disk, before it is lost.
In a manual system, an office worker would organise data that was going to be
stored for future use into files before placing it in a filing cabinet.
In the same way, in a computer system, any data that is going to be stored on disk
will be organised into files of related data.
Alternatively, a file might break down into several data records - each one about
a particular individual person or item and consisting of a group of fields
(individual items of data - such as name, address or wage). A payroll program or
a stock control program would use a file organised in this way.
In C. the streams of data output from the computer to a disk file or elsewhere can
be divided into text streams and binary streams.
In general, binary files are more efficient for storing data - while text files are
used for information that is going to be displayed or printed without further
editing.
184
3.32 Files - Storing Data on Disk using /write
To store data in a disk file , a C program must go through the following steps.
1. Set aside memory space to hold the address of some file information
which will be given out by the computer's operating system . The memory space
should be declared as type FILE - which is a special structure defined in stdio .h .
2. Set aside working space(s) to hold one set of data items temporarily while
they are waiting to be written to disk. The most convenient way to do this might
be to allocate a structure.
3. Ask the operating system to set up an empty disk file. This process is
usually known as opening or creating a file and involves the system in placing a
file name in the disk directory and setting aside some disk space for the start of
the me. At this point the operating system passes across to the program some
data about the me.
4. Data can then be written (i.e. copied) from the central memory to the disk
file. This is nonnally done a small quantity at a time. For example, the data on
one student may be keyed in and held temporarily in the central memory spaces
set aside (see 2 above) - then written to disk. This process is repeated for the next
student and so on - until all the data is on disk.
There are various alternative commands for writing to a file. The command
fwrite copies the contents of a designated area of memory to disk - in exactly the
same format as it was stored in central memory (i.e, not necessarily in a readable
text form),
185
Thefwrite command must specify:
a. the address of the memory area where the data is stored;
b. the size in bytes of the memory area (rather than working this out , the
sizeof command can be used - followed by the type of the memory area);
c. the number of areas to be written;
and
d. the name of the file pointer associated with the file.
e.g.4 fwrite (&student_details, sizeof (struct
student_type), 1,
student_fi1e-pointer);
This command will write the contents of the central memory location
studentjdetails to the disk file associated with the file pointer student....file...,pointer
(i.e, the file "student"). The type name struct studenctype is used with the sizeof
directive - as a guide to the size of the memory area. The machine is told to write
one of these storage areas.
5. Finally, the file should be closed. This instructs the operating system to
carry out various tasks (depending on the system) but which might include
writing the final file size to the disk directory and ensuring that any data stored
temporarily in system buffers is written correctly to disk.
186
ExampleProgram
tinclude <stdio.h>
main()
(/*space for address of file information */
FILE *student_file-pointer;
/*space to store a student's details temporarily -
before being written to disk */
struct student_type
(char surname[16];
int mark;
) student_details;
/*space to store user's answer to question Finished?*/
char finished_indicator;
/*Finish*/
return 0 ;
}
Exercises
1. Write a program to ask for the name and wage of each employee in a firm
and store the data in a file called "staff'.
2. Write a program to ask for the surname, first name and marks for English,
maths and French for each student in a class - storing them in a file called
"students".
3. Write a program to ask for the item code, description, quantity in stock,
cost price and sales price for each type of stock held by a shop · storing in
a file called "stock".
187
3.33 Files - Reading Data/rom Disk usingIrefUl
To retrieve data from a disk file, a program must go through a similar set of steps.
1. As when writing to a file, set aside a pointer variable to hold the address
of file information.
2. Set aside working space to hold a set of data items temporarily as they are
read from disk and awaiting processing. This space must conform to the type of
data to be read from disk - i.e. should generally be of the same type as the
variable(s) used as working space in the program that wrote the file originally.
4. Data can then be read (i,e. copied) from the disk file to central memory.
As with writing, this is normally done a small quantity at a time. For example,
the data on one student (i.e. one record) may be read from disk and stored
temporarily in Central Memory - then processed in some way - e.g. displayed on
screen . This process is repeated for the next student and so on - until all the data
from the disk has been read.
The format of the fread command - which reads data from disk is the same as
fwrite.
e.g.4 fread (&student_details, sizeof (struct
student_type), 1,
student_file...pointer);
reads one record from the disk and places it in the central memory location
student.sietails.
The fread would need to be included in a loop together with commands to process
data (e.g. display it on the screen) - so that each student's information could be
dealt with in turn. (See Example Program below .)
188
It is necessary to check - after each attempt to read the file - whether data was
retrieved, or whether the end of the file has been reached. The feof function is
used for this purpose. (The feoffunction returns 0 if it is the end of the file.)
a. (attempt to) read the file and check if any data has been read or if it is end-
of-file (it is possible for a file to be empty - so we want to check for that
possibility right away);
b. start a while loop which begins by processing the data that has just been
read then (attempts to) read another record from disk - stopping when end-of-file
is encountered.
189
Example Program
#include <stdio.h>
main()
{
I*space for address of file information *1
FILE *student_file-pointer;
I*space to store a student 's details temporarily -
after being read from disk *1
struct student_type
{char surname[16];
int mark;
) student_details;
I *Finish *1
return 0;
Exercises
1. Write a program to read the "staff' file - created in the last lesson
(Exercises Question 1) and display the data on the screen - one employee
per line.
2. Write a program to read the "students" file - created in the last lesson
(Exercises Question 2) - calculate the average mark and display a well set-
out report for each student.
3. Write a program to read the file called "stock" created in the last lesson
(Exercises Question 3) • to calculate the profit on each item and display all
the information on the screen.
190
3.34 Files Writing Formatted Text to Disk using!printf
»
The command !printf can be used to write formatted textual data to a file. It
would, for example, be suitable for writing in'formation to a file which could then
be printed out as a report.
The program would still need to go through a similar series of steps to those
shown in Lesson 3.32.
2. Set aside working space(s) to hold data items temporarily while they are
waiting to be written to disk.
holds the details for one student while they are typed in and ready to be written to
disk. Note that when using fprintf there is no need to group the items together
into a structure - although this can be done ifrequired.
The fopen function opens a disk me which will be known as "studtext" on the
disk directory. The "w" indicates that this is a new text me and that data will be
written to it.
4. Data can then be written (i.e. copied) from the central memory to the disk
me.
writes the contents of the central memory locations students urname and
studentmark to disk. This command will store the data on disk in the same form
that printfwould display it on the screen - i.e. in a readable text form. This is not
particularly efficient for storage as, for example, an integer variable might take up
only two bytes of storage to hold - say - the number 30200. As text, this number
will take up five bytes on disk (one byte to hold each character).
As with fwrite - fprintf would need to be included in a loop together with any
other processing commands - so that the task of getting data and writing it to a file
could be repeated for each student.
191
Example Program
_include <stdio.h>
main()
{/*space for address of file information *1
FILE *student_text_file-pointerj
I*space to store a student's details temporarily -
before being written to disk *1
char student_surname[16]j
int student~rk;
I*space to store user's answer to question Finished?*1
char finishe~indicator;
I*Finish*1
return 0;
Exercises
1. Write a program to ask for the name and wage of each employee in a firm
and write the data to a report file called "emprep".
2. Write a program to ask for the surname, rust name and marks for English,
maths and French for each student in a class - writing them to a file called
"strep".
3. Write a program to ask for the item code , description, quantity in stock,
cost price and sales price for each type of stock held by a shop - storing in
a file called "stockcat".
192
3.35 Files - Reading Formatted Text from Disk using fscanf
Although a formatted text file is most useful for producing reports ready for
printing, it is still possible to retrieve data from such a file. Again, the program
must go through a similar set of steps to those shown in Lesson 3.33.
2. Set aside working space(s) to hold a set of data items temporarily as they
are read from disk and awaiting processing. Again, this space must conform to
the type of data to be read from disk.
3. Ask the operating system to open the disk file for reading .
4. Data can then be read (i.e. copied) from the disk file to central memory.
reads data from the disk and places it in the central memory locations
student surname and student mark. This command assumes that the data on disk
is in the same form that scan! would read it from a keyboard - i.e, in a readable
text form .
193
Again, it is usual to carry out one attempt to read the file before going into the
loop to check if there is any data to be processed (see Lesson 3.33).
e.g.S e_o_f_ind =
fscanf (student_text_file...pointer, "%s %d",
student_surname, &student_mark);
while(e_o_f_ind ! = EOF)
{
/*Process data (e.g. display on screen)*/
194
Example Program
*include <stdio.h>
main()
(/*space for address of file information */
FILE *student_text_file-pointer;
/*space to store a student's details temporarily - as
they are being read from disk */
char student_surname[16];
int student-mark;
/*space to store end-of-rile flag*/
int e_o_f_ind;
/*Open student file for reading text*/
student_text_file-pointer = fopen("studtext","r");
/*Finish*/
return 0;
Exercises
1. Write a program to read the "emprep" file - created in the last lesson
(Exercises Question I) and display the data on the screen - one employee
per line.
2. Write a program to read the "strep" file - created in the last lesson
(Exercises Question 2) - calculate the average mark and display a well set-
out report for each student.
3. Write a program to read the file called "stockcat" created in the last lesson
(Exercises Question 3) - to calculate the profit on each item and display all
the information on the screen.
195
3.36 Files - Writing Data to Disk - one byte at a time - using pute
Data can be written from central memory to disk - one byte at a time using putc or
fputc (they carry out the same task).
3. Open an empty file. This may be a text or a binary file - depending on its
purpose.
The/open function opens a new file and labels it "poem". The " W " indicates that
this is a new text file and that data will be written to it. For some types of data
" wb " (new binary file) would be suitable.
writes the contents of the variable letter to the file associated with
poemJile-pointer.
As with previous commands that write data to a file - pute would need to be
included in a loop together with any other processing commands (e.g, instructions
to get a character from the user) - so that, for example, a whole piece of text could
be keyed in and saved -letter by letter.
Clearly, when keying in a text, the user would not wish to have to reply to a
repealed question after each letter - asking whether there were any more to type -
so the programmer must decide on another way for the user to indicate that he or
she has finished. Perhaps, the simplest way would be to set aside a specific
character which the user could type when he/she wished to end the input
Anything that the user is unlikely to wish to use as part of a normal piece of text
would be suitable. (In the example program, an asterisk is used.)
196
Example Program
iinclude <stdio.h>
main ( )
(
/*file pointer variable*/
FILE *poe~file-pointer;
/*Close file */
fclose(poe~file-pointer);
/*Finish*/
return 0;
Note that on many systems, the function getchar does not start to pick up data
that has been keyed in until the <ENTER> key is pressed (see Lesson 1.37),-
characters are simply stored in a buffer and read when <ENTER> is pressed.
The size of the buffer then limits the amount of text that can be keyed in on each
line - forcing the user to go to a new line - even if this is not appropriate.
Exercises
1. The example program saves the * at the end of the input. Modify the
program so that the asterisk is not written to disk.
2. If the above note, regarding how getchar works, applies on your system -
modify the program so that the newline character is not saved to disk
when <ENTER> is pressed merely to empty the buffer. Instead, the
program should save a newline character if the user keys in a 'I' character.
197
3.37 Files» Reading Data/rom Disk one byte at a time using getc
Similarly, data can be read from disk into central memory - one byte at a time -
using getc or fgetc.
The same steps have to be followed as with other methods of reading data from
files.
1, Define afile pointer variable.
e.g.l FILE *poeI\Lfile-pointer;
2. Set aside workingspace to hold a byte of data as it is read from disk. The
working area is generally set up as int rather than char . This is because getc can
either return a single byte value actually read from disk or a longer (integer)
value indicating that it has encountered the end of the file or an error - so the
working space needs to be large enough to store either.
e.g.2 int letter;
3. Open a file which already contains some data. Again, the file may be
opened as a text or a binary file - depending on the program's purpose, and how
the file was originally created.
e.g.3 poeI\Lfile-pointer = fopen( "poem", lOr");
Thefopen function opens the "poem" file created by the Example Program in the
previous lesson - using mode "r" (read a text file) . Under certain circumstances
"rb" (read a binary file) would be suitable.
4. Data can then be read from the file - one byte at a time.
e.g.4 getc (letter, poeI\Lfile-pointer);
reads one byte from the file and stores it in the variable letter. When getc reaches
the end of the file, or is unable to read data for any reason, getc returns EOF
(which is an integer value - often -J - taking up the whole of the integer variable
letter).
To allow the whole file to be processed - getc would need to be included in a loop
together with other processing commands (e.g. instructions to display a character
on the screen). As each byte is read, the return value from getc will have to be
checked to see whether it is the end of the file.
A typical sequence for reading and checking for end of file would be:
e.g.S /*Read the first byte from disk file*/
letter = getc(poeI\Lfile-pointer);
198
If you prefer, you can use/eo/to check for end of file - replacing while(letter !=
EOF) with while(!/eoj(poemJile...pointer). In this case there is no need to leave
an integer working space as there will be no need to store the EOF value returned
by getc - a char variable will be adequate.
Example Program
tinclude <stdio .h>
main()
(
/*file pointer variable*/
FILE *poellLfile....,pointer;
/*space to store character read from file and awaiting
processing or integer EOF value*/
int letter;
/*Close file */
fclose(poellLfile....,pointer);
/*Finish*/
return 0;
Exercises
I. Write a program to read a text file and display the contents on the screen-
converting all lower case letters to upper case as it does so.
2. Write a program to read a text file and count the number of words -
displaying the text and the total at the end.
3. Write a program to read a text file and count the number of occurrences of
each letter of the alphabet- displaying the text and the totals at the end.
199
3.38 Random Files» using fseek
Data in a file which has been opened as a binary file may be accessed at random
by using fseek to set the position within the me for the next read or write
command. The fseek command may be still be used if the me is opened as a text
file but in this case it is limited to setting the position to the beginning of the file
or to the current position.
sets the position for the next read or write of the file as the lOOth byte (Byte 0 is
the first) measured from the beginning of the file whose details are pointed to by
student.filepointer.
If a file is made up of records then it is easiest to use sizeofto make the computer
work out the displacement.
sets the position to the number of bytes (found by multiplying the size of struct
studenttype by I) from the beginning of the file necessary to access the second
record. (The first record of course starts at byte 0.)
sets the position to the number of bytes from the beginning of the file appropriate
to access any required student record (assuming that the file is organised by
studentnumbers.
Thefseek command may be used to set the position for the next data record to be
written somewhere within the current extent of the file - or to a totally new
position anywhere after the end of the file.
writes a record at the IOOOth record position in the file. If the file is empty or only
contains a few records. then the record will be written in the correct position -
200
leaving space for the earlier records to written later. If the 1000th record already
exists - it will be overwritten.
Likewise, fseek will set the position for the next read - which again may be
anywhere within the me - or after the end of the file (but of course as no data has
been saved there the information read will not be valid).
reads the record at the 1000th record position in the file. This does not necessarily
mean that there are 999 records before this - there may just be blank spaces or a
mixture of blank spaces - with records every so often.
If no actual details have been saved at the position - the computer will still read
whatever is there (which is probably just rubbish).
If the file has been opened for both reading and writing - then it may be useful to
read a record, display it, then allow the user to key in a modification and write
that back to the same position. In this case fseek will have to be used before both
the read and write commands , as the position after the fread will automatically
move to the next record and we do not want the change to affect the wrong data.
The fseek command may of course be used together with other suitable read or
write commands such as gete and pute.
e.g.? fseek (text_file-pointer, 0, SEEK-SET) ;
getc(letter , text_file-pointer};
201
Example Program1
*include <stdio.h>
main ( )
{FILE *student_file-pointer;
char choice;
struct student_type
(int id.....number;
char surname[15];
char firstname[15];
int mark;
) student_details;
/*Finish*/
return 0;
202
Example Program2
iinclude <stdio.h>
main ( )
{FILE *student_file-pointer;
int student_i~number_required;
char choice;
struct student_type
{int i~number;
char surname[15];
char firstname[15];
int mark;
} student_details ;
/*Open student file tor reading*/
student_file-pointer = fopen( "BACLASS", "rb");
/*Get and display details tor group ot students*/
do
{/*Get student number*/
printf ( "\nStudent Number ?");
scanf ( "%d", &student_i~number_required);
/*Set tile position and read record*/
fseek(student_file-pointer ,
student_i~number_required * sizeof(struct
student_type) , SEE~SET);
fread(&student_details, sizeof(struct
student_type), 1, student_file-pointer) ;
/*Check that the data read is valid*/
if (student_detai1s.i~number==
student_id_number_required)
{/*Display Student Details*/
printf ("%d\n", student_details. id_number) ;
printf ("%s\n", student_details. surname);
printf ("%s\n", student_details . firstname);
printf ("Mark: %d\n", student_details .mark) ;
}
else
/*Display message - no data held tor student*/
printf ("No details held\n");
/*Check it user wishes to view any more student
data*/
printf("Any more ?\n");
scanf (" %c", &choice);
while (choice != 'N') ;
/*Close student tile*/
fc1ose(student_fi1e-pointer) ;
/*Finish*/
return 0;
203
Note that in order to avoid processing invalid data, the program checks the data
that has been read from disk into the studencdetails.id../lumber, against the
studenUd_numberJequested. If data has been saved at the location read, then
they should be equal; if not the number read will just be whatever happened to be
on disk and is highly unlikely to equal the expected id number. To further reduce
the risk ofprocessing invalid data. an estimate should be made of the number of
students whose data will need to be held - and a file should be written
(sequentially) just made up ofblank records (Negative values are likely to be best
- as both NULL and spaces have binary representations (0 and 32 respectively)
which could equal real student ids. Any records written and read by subsequent
programs should then be checked as being within the valid range for the file.
Exercises
1. Write a program which will format a suitable number of dummy records
in a new file to hold stock records for a small store. Write a program
which will allow the user the option of writing or reading a current record.
Include validation to ensure that blank records are not processed - and that
no data is written beyond the valid range designed for the file.
2. Write a program which will allow the options of writing a new record,
reading and displaying an existing record or modifying an existing record
(Note that the program should not be able to alter the stock number -
which is the key field). Again, include suitable validation .
204
3.39 Files» Handling Errors
So far, all the file-handling programs in this book have assumed that there are no
problems when a file is opened or closed, or when an attempt is made to write
data to disk - or read data from disk.
Each file handling command has a different way of signalling that an error has
occurred.
The fopen function returns the address of file information if it can open the file
satisfactorily - but returns NUU if there is an error and the file cannot be opened.
So, it is usual, immediately after trying to open a file, to check the file pointer to
ensure that the value is not NUU before trying any more processing.
If the new "student" file cannot be opened (perhaps there is insufficient room - or
a read only file exists with the same name) a message will be displayed on the
screen and the program will stop immediately. The same check can be carried out
after attempting to open a file for reading.
e.g .2 if « student_file...,pointer =
fopen (" student H , "wb") ) == NULL)
(
printf("\nUnable to open file\n");
exit(l) ;
}
The effect is the same as with e.g.l . The expression in the inside brackets (i.e.
studentJile..pointer = fopen( "student", "w") is carried out first. Then the value
of this expression (i.e. the value placed in the file pointer) is checked to see if it is
NULL. If so the commands in the braces are carried out - displaying a message
and exiting . Otherwise. these commands are ignored and the program carries on
to the next instruction.
205
The fclose function returns 0 if there is no error and some other value if there is a
problem closing the file. (An error is likely to occur if there is insufficient space
to finish writing the file.) So, the return value from fclose should be checked to
ensure that it is O.
The functions which write data to disk and read data from disk have various ways
of signalling an error.
The fwrite function returns the number of items written. This can be checked
against the number that were requested.
The fwrite command is asked here to write one item (the contents of
studencdetails). So, in this case, it would be expected to return the value 1. If it
fails to do so, then there is an error (e.g. insufficient disk space).
The fread function, similarly, returns the number of items read. However, if it
retums a number less than the number of items requested , it can mean either that
there was an error in the attempt to read the file (e.g. through corrupted data) or
that the end of the file has been reached. The functionferror can then be used to
check if there is an error (it returns 0 if there is no error) .
If the figure returned by fread is lower than the number of items requested (1) -
then the function ferror is used to check whether this was caused by an error; if
so, an error message wiu be displayed.
206
Alternatively, ferror can simply be used after every attempt to read the file -
without checking the return value fromjread.
If the return value is negative, the program displays an error message and exits.
The fscanf command returns the number of characters read and assigned to
variables, if it is successful. Otherwise, it returns EOF, whether end of file has
been reached or whether the problem is caused by an error trying to read the file.
Again , to check whether an error has occurred ferror can be used.
If, as in the Example Program in Lesson 3.35, the return value from fscanf is
placed in a variable, then this variable can be tested.
e.g.9 i f (e_o_f_ind =
(fscanf(student_text_fi1e-pointer, "%s %d",
student_surname, &student~ark) ) == EOF)
{
if (ferror(student_text_fi1e-pointer))
{
printf ("Error in reading file\n");
exit(l) ;
)
207
The putc function normally returns a copy of the byte that it has written - as
confirmation of success. If there is an error it returns EOF instead.
The getc function returns EOF if is the end of the file or if there is an error in
reading the file. So, againjerror should be used to check if there is an error.
The expression letter =getc(poemJile-pointer) is carried out first, and the result
is checked to see if it equals EOF. If it does, then ferror is used to confirm
whether there is an error. Otherwise. the program continues to process letter.
The fseek function returns 0 when it is successful - and a non-zero value if there is
an error (such as an attempt to find a record before the beginning ofthe me). So
the return value can be tested and appropriate action taken.
printf("Error\n");
exit (1)
So far, we have assumed that if an error occurs, then the appropriate course of
action is to display a message and exit the program. In practice this might not be
the best course of action - as there may be useful data in memory which will be
lost when the program ends. In such cases. a better response to an error might be
to call a function which gives the user an opportunity to choose an appropriate
course of action to save the data - perhaps on another disk.
208
Example Program J
iinclude <stdio.h>
iinclude <stdlib.h>
main()
{/*space for address of file information */
FILE * student_file-pointer ;
/*space to store a student's details temporarily -
before being written to disk */
struct student_type
(char surname[16);
int mark;
} student_details;
/*space to store user's answer to question Finished?*/
char finishe~indicator;
/*Finish*/
return 0 ;
}
209
Example Program 2
while(!feof(student_file-pointer»)
{/*Display report for student*/
printf (" \n%s\n", student_details. surname) ;
printf ("Mark: %d\n", student_details . mark) ;
/*Read next student's record from disk*/
if ((fread(&student_details, sizeof(struct
student_type), 1, student_file-pointer» < 1)
{
if (ferror(student_file-pointer»)
(
printf("Error in readingfile\n");
exit(1) ;
210
/*Close student file*/
if (fclose(student_file-pointer) != 0)
{
printf ( \nError closing file\n ") ;
II
exit(!) ;
);
/*Finish*/
return 0;
)
Exercises
Modify the Example Programs from Lessons 3.34 - 3.38 to include checking for
file-handling errors.
211
4.1 Introduction to Modular Programming
A program may be divided into a number of modules- each one in a separate file.
These modules may be compiled (and tested) separately - then linked together to
form one program.
This has many advantages when large programs are being developed .
1. The work can be shared between several programmers working fairly
independently.
2. It is easier to find errors in a number of small program modules rather than
one large one.
3. Program modules can be produced which may be used in more than one
program - thus reducing the overall amount of work involved.
When working in this way, careful consideration must be given to the overall
design and how best to split the program so that modules are cohesive. It is
essential to produce clear module specifications - stating exactly what each
module should do and how it will connect to the other modules. Particular
consideration must be given to the data that will be passed between modules and
how it will be transferred.
212
4.2 Using Functions from other Modules
So, suppose a program is divided into two modules • as shown below - Module I
containing the main function, and Module 2 containingjUnctionl (a function with
no return value and no parameters). As Module I contains a call to functionl , it
contains a declaration of this function near the top of the file.
Module 1
/*Declaration of functionl*/
void functionl(void)j
I*Definition of main*/
main ( )
{
/*Instructions (including call to functionl)*/
)
Module 2
/*Definition of functionl*/
void functionl(void)
{
/*Instructions*/
}
If either of the modules contain calls to routines from the standard library, then
the appropriate header file (e.g. stdio.h) must be #included in that module.
Names (of functions or data items) which are defined in one module and
referenced in another are said to have external linkage. On some systems the
compiler will pay attention only to the first six characters of such names (as
opposed to thirty one characters for names which are used only within one
module). In addition the compiler might not be case-sensitive over names with
extemallinkage - so that capital letters and small letters are treated as equivalent.
To ensure that your programs are portable, it is safest to ensure that the first six
characters of all names with external linkage are unique and do not rely on
different case to distinguish them.
213
Example Program
Module 1
iinclude <stdio.h>
/*Declare functions that module will call*/
void display_square(void)j
void display_triangle(void)j
main()
{char choicej
/*Get users choice of shape*/
printf("To display a square - key S\n")j
printf("To display a triangle - key T\n");
scanf %c
(II &:choice) j
II I
/*Finish*/
return OJ
Module 2
tinclude <stdio.h>
void display_square(void)
(
printf("* * * *\n") ;
printf("* *\n") ;
printf ("* *\n")j
printf("* * * *\n") ;
void display_triangle(void)
(
printf("*\n");
printf("* *\n")j
printf("* *\n");
printf("* * * *\n")j
214
Compiling and Unking Modules
To create a single working program from the above modules the programmer(s)
must:
The method for doing this will vary between compilers and systems.
On a standard UNIX system the command cc followed by the two filenames will
compile the modules and arrange for them to be linked automatically.
So, if the modules had been saved as - for example - shapesl,c and shapes2.c then
the command:
would result in shapesl,c and shapes2.c being compiled - creating object files
called shapesl.o and shapes2.0 - and then linked (together with the standard
library) - to produce a usable machine code program called display (which can be
run simply by typing its name).
cc -c shapesl.c
cc -c shapes2.c
which will create object files called shapesl.o and snapesz.o. The -c options
before the C filenames indicates that the modules should be compiled only
without any attempt at this stage to link them.
215
4.3 Using Variables from other Modules
One module may access global variables defined in another module - again
provided that it contains a declaration of the variable (the name and type of the
variable preceded by the word extern). A declaration does not allocate any
memory space - it simply indicates that the declared variables have been set up
elsewhere in the program - probably in another module.
In the example below, x is a global integer variable defined (i.e, set up with
memory space) in Module 1 - but which also needs to be accessed by Module 2 -
which therefore contains a declaration of this variable (sufficient details - name
and type - for Module 2 to be able to use the variable correctly).
Module /
/*Declaration of functionl*/
void functionl(void)i
/*Definition of global variable*/
int Xi
/*Definition of main*/
main! )
{
/*Instructions (including call to Eunctionl)*/
}
Module 2
/*Declaration of variable x*/
extern int Xi
/*DeEinition of functionl*/
void functionl(void)
{
/ * Ins tructions"* /
}
216
Example Program
Module I
/*Declare functions that will be called (defined in
Module 2)*/
void get_student_details(void);
void display_report(void);
/*Main Program*/
main ( )
(
get_student_details();
display_report();
return 0;
Module 2
finclude <stdio.h>
void get_student_details(void)
{
printf ( \nKey in name ");
II
gets(student_name);
Module 3
iinclude <stdio.h>
void display_report(void)
{
printf ("%s\n", student_name);
printf ("%d\n", student_mark);
}
217
4.4 Static Global VarUlbles
The tenn static applied to a global variable indicates that the memory space may
only be accessed from within the module where it is defined. Note that the term
static applied to a local variable has a totally differentmeaning (Lesson 1.29).
This facility is particularly useful when a variable can be grouped together with
all the functions which use it in one module - as it ensures that other functions
cannot use or modify the data.
So, in the example below, the variable y can be accessed by all the functions in
Module 2 - but not from Module 1.
Module I
/*Declaration of functions used in this Module*/
void functionl(void)i
void function2(void)i
/*Definition of main*/
main()
{
/*Instructions (including calls to functionl and
function2) */
Module 2
static int Yi
/*Definition of functionl*/
void functionl(void)
{
/*Instructions*/
}
/*Definition of function2*/
void function2(void)
{
/*Instructions*/
}
The following example program allows the user the choice of converting a
numeral between 0 and 9 to its equivalent in words (e.g. zero, one, two, etc) - or
the other way around. It delegates this work as appropriate to the functions
toWords and toNumerals. These both make use of a table of the verbal fonn of
the numbers from 0 - 9, so it is sensible to place both these functions in a module
together with the table held as a global variable. No other module needs access to
the table, so it can be made accessible only within the module by defining it as
static.
218
Example Program
Module 1
iinclude <stdio.h>
/*Declaration of functions this Module will call*/
int toNumerals(char []);
char *toWords(int);
main()
{int choice, num;
char num-in_words[6];
/*Ask if user wants to translate words to numerals
or numerals to words*/
printf("Translate numerals to words - Key l\n");
printf ("Translate words to numerals - Key 2\n");
scanf ( "%d", &choice);
/*Carry out choice*/
if (choice == 1)
{
printf ("Key in a number between 0 and 9\n");
scanf I "%d", &num);
printf ("%s\n", tOWords Inum));
}
else if(choice == 2)
(
printf("Type a number (in words) between zero and
nine\n") ;
scanf I "%s", num-in_words);
printf("%d\n", toNumerals(nUffiLin_words));
}
else printf("Error\n");
/*Finish*/
return 0;
}
219
Module 2
#include <string.h>
/*Array of pointers to numbers in verbal forms*/
static char *number_table[] =
( "zero", "one", " two" Ithree" , "four" ,
"
220
4.5 Object Libraries
Object files may be grouped together into library files. This is a good way to
organise object files which contain functions likely to be useful in more than one
program. In this way readily usable libraries of additional commands can be set
up which any programmer may use in his/her programs.
The commands for carrying out these steps will vary between systems and
compilers.
On a stondard UNIX system this will involve using the cc compiler to produce
object files, then the command ar to place the object files in a library.
If, for example the various functions to draw shapes (Lesson 4.2) are likely to be
useful in a number of different programs then these could be placed in a library as
follows.
1. Create separate C source files for each individual shape function - using vi
or some other editor.
2. Use cc (with the -c option) to compile each one independently. So. if the
files were called square.c, and triangle.c then they could be compiled
(without linking) using the commands:
cc -c square. c
cc -c triangle.c
221
To check the contents of a library use ar t followed by the library name. So, ar t
libshapes.a should now result in a list of the three program files wliich have been
added to it.
Before a program module may use functions defined elsewhere - it must contain a
declaration of that function (as mentioned in Lesson 4.2). When a library is
created, a header file of declarations for all the functions in that library should be
written (and amended whenever the library is changed). Any programmer may
then #include the appropriate header file in a program which will use the library
to ensure that it contains correct declarations (in the same way that header files
such as stdio.h must be #included in a program to be able to use several functions
from the standard library).
So, for the libshapes .a library, a header file should be made - containing
declarations of displaYJquare and display_triangle. Note that a header file is
simply text and does not have to be processed in any way before use.
I
void display_square(void) ;
void display_triangle(void);
Example Program
iinclude <stdio.h>
iinclude "shapes .h"
main ()
lint num, count;
/*Get number of squares required*/
printf (" \nHow many squares do you wish to draw? ") i
scanf ( "%d", &num);
/*Draw required number of squares*/
for (count = 0; count <num; count++)
draw_square()i
/*Finish*/
return Oi
Note that if the required header file has been copied into the standard C header
file directory for the system, then its name in the #include directive should be
surrounded by < > - otherwise, the name (or the full pathname) should be
surrounded by" ".
222
On a standard UNIX system the cc command can be used to link a program with
a library .
So if the example program above has been saved as draw.c, then the command:
cc -cdraw.c
will produce an object file called draw.o which can then be linked to the library
with the command:
Documentation
Exercises
1. Create a library of functions to draw a variety of shapes (square, rectangle,
different types of triangle, diamond, etc). Produce a suitable header file .
Then write a program which will make use of some of the functions in the
library.
2. Create pairs of functions - to convert the numerals 0-9 into the verbal
equivalent - and vice versa - in the following languages:
i) French (zero, un, deux, trois, quatre , cinq, six, sept, huit, neut);
ii) Italian (zero, uno, due, tre, quattro, cinque, sei, sette, otto, nove);
iii) Spanish (cero, uno, dos, tres, cuatro, cinco, seis, siete, ocho,
nueve).
Create a library made up of these object files and a suitable header file .
3. Write a program which will allow a user to key in a number in French and
have it translated into Spanish - using the library functions from Question
2.
4. Write a program which will allow a user to choose to convert anyone of
French, Italian or Spanish numbers to anyone of the others - using a
menu.
5. Add another language to the library.
223
5.1 Macros
Once it has been defined, the macro DISPLAY_GREETING can then be used
anywhere in the program instead of the command printj("Hello\n"). Before the
program compiles - the preprocessor will go through the program finding each
occurrence of the word DISPLAY_GREETING and replacing it with the command
printj("Hello\n").
Once defined, the macro DRAW..,SQUARE may then be used within the program
text and wili be replaced when the program is compiled by the set of printf
statements which make up its definition.
A macro may be given its own local variables - by making use of the fact that a
block of code (i.e, instructions between { and }) can have variables which are
local to the block (see Lesson 1.33).
224
So. in general, a program using macros will take up more memory space than the
same program usingjimctions - but will run faster (because no time is lost passing
control over to the function and back again).
Example Program
iinclude <stdio.h>
idefine DRAW_SAILS printf (" I \n"); \
printf (II * *1 *\n"); \
printf( II * * I * \n ") ; \
printf(" * * I *\n II) ; \
printf( II * * I * \n "); \
printf(" * * I * \n ") ; \
printf(" * * I---------*\n"); \
printf (II I \n");
printf("*********************\n") ;\
printf(" ********************\n");
main()
{
DRAW_SAILS ;
DRAW_HULL;
return 0;
Before the Example Program is compiled the preprocessor will substitute the
definitions for DRAW_SAILS and DRAW.J/UU each time they occur in the
program text. So function main will be the only function - but will be enlarged by
all the printf statements that make up the two macro definitions.
(Compare this with the Example Program in Lesson 1.21 which carries out the
same job using functions .)
Exercises
1. Write a program to draw a house - using separate macros for the roof and
walls.
2. Write a program to draw a desktop computer - using separate macros to
draw the screen, computer casing and keyboard.
3. Write a program to draw a 5 by 5 square made up of asterisks (make use
of e for loops and a local variable for the block of code that makes up the
macro).
4. Compare your programs to Questions 1 and 2 with the similar programs
from the Exercises to Lesson 1.21. Explain the differences in how they are
compiled and how they work when they are running.
225
5.2 Macros - usingArguments
Arguments may be placed in brackets after macro names . These arguments can
then be replaced by specified text which may be different each time the macro is
used in the program.
This example defines a macro which will add two values together (represented by
x and y and place the answer in a variable (represented by ans).
When the macro is used in the program - three arguments must be specified to
conform to those in the macro definition . Note that in the example the first actual
argument must be the name of a variable - because it will receive information (i.e.
the answer).
e.g.2 ADDUP (totalJllark, 10, 20)
When the program is compiled, the actual arguments totai mark, J0 and 20 will
be used in place of the corresponding formal arguments in the definition. So, this
line (e.g.2) will be replaced in the program text by totalmark =10 + 20; and
then this instruction will be compiled .
Before compilation. the preprocessor will convert this line to - total mark =
maths_mark + english_mark;
So,
e.g.5 DRAW_LINE ( 5 ) ;
Do not confuse this use of arguments with their use in functions - where data is
actually transferred into temporary memory locations when parameters are
passed. Macros are purely concerned with textual substitution.
226
Example Program
main ( )
{char student_name(20];
int maths_mark, maths_ex~mark,
maths_coursework-mark,
english-mark, english_ex~mark,
english_coursework-mark,
overall-mark;
/*Display report*/
printf("Report for %s\n" , student_name};
printf ("Maths %d\n", maths_mark);
printf ("English %d\n", english~ark);
printf ("Overall %d\n", overall_mark) ;
/*Finish*/
return 0 ;
}
Exercises
1. Write a macro which will calculate a student's grade if given his/her mark
- on the basis that 85% or more gives a 'D', 65 - 84 gives 'M', 50 - 64 gives
'P' and lower than 50 will gives 'F'. Use the macro in a program which
asks a student for hislher name and marks for Computing and Maths - then
works out a grade for each and displays a report.
2. Write a macro which will display a square of asterisks - taking its size
from an argument. (Use a block with a local count variable to make up
the body of the macro.) Use the macro in a program which asks the user
for the size of square required - then displays the shape in the chosen size.
227
5.3 Using typedef
A typedef allows the programmer to give a name of his/her own choice to a data
type.
For example, another name may be given to any of the standard data types:
gives the alternative name whole_number to the standard data type into
From then on, the type wholenumber may be used to set up memory spaces to
hold integer values.
So,
sets up variables, numl, num2 and answer- each able to hold an integer number.
Giving alternative names in this way, is more useful for complex types as a
program can be made more readable.
228
A typedef for a pointer type may be set up.
is equivalent to
e.g.9 char *ptr_to_grade;
A type name for a pointer to a particular type of function may be set up.
e.g.1O typedef void (*ptr_to_void_funct_no...,params) (void)
Exercises
l. Give alternative type names to the standard data types: char, float, long,
double.
2. Give a typenames to pointer types to hold the addresses of each of the
above data types.
3. Give a typename to the structure type structstudenrtype
4. Give a typename to a pointer type suitable to hold the address of a
function with one integer parameter and returning a character.
5. Set up variables for each of the above types.
229
5.4 ConditionalExpressions
A conditional expression may take one of two values - depending on the result of
the condition which comprises the first part of the expression.
The condition mark >= 50 is checked and. if it is true, then the expression will
take the value of the first item after the ? (i.e. 'P~; otherwise the expression will
take the value of the seconditem- after the: (i.e. 'F).
So. if the mark is 50 or more then the character 'P' will be placed in grade;
otherwise, if the mark is less than 50 - 'F' will be placed in grade.
The program displays whichever is the student's better mark out of maths_mark
and english_mark.
A conditional expression may also be used as the return value from a function.
The function calc_bonus returns the value of the bonus (200 or 1(0) on the basis
of the number of years' service.
Exercises
1. Write a program which will ask the user to key in two numbers. then
display the higher.
2. Write a program which will ask the user to key in his/her salary - then if
the salary is over 3000• calculate tax at 25% on the excess; otherwise- if
the salary is 3000 or less - setting tax at zero. In either case the program
should assign the result to the variable tax and display it.
230
5.5 Defining Unions
A union allows a particular memory location to be defined in more than one way.
For example. a location might be set up to hold an integer or a char.
e.g.l union
{int percentage;
char grade ;
} eXarl\....result;
This sets up a memory space which would allow a teacher to store a student's
exam_result as either an integer number - or as a character grade.
The actual space allocated will be enough to hold the largest data type mentioned
(in this case int) • which means that when the shorter type is stored. some space
may be unused.
The union may be given a tag to set up a type - just as with a structure.
This template can then be used to set up memory spaces conforming to this type.
As with structures. a union type and memory spaces can be set up at the same
time.
Exercises
Write definitions for the following structures:
I. to hold an employee's actual salary or salary grade;
2. to hold a student's result as a grade or whole number percentage or a mark
out of ten (which may include decimal fractions);
3. to hold an employee's surname or his/her employee number.
231
5.6 Using Unions
The individual items within the union are named in a similar way to structures -
so in the e.g.I from the previous lesson examresult.percentage refers to the
space when it is holding a number and examresult.grade when it is holding a
character.
The programmer has to keep track of what is actually stored in the location at any
particular time.
Example Program
/*Finish* /
return 0;
232
5.7 Unions and Structures
For example, a structure might contain details of a student name, course code,
and result - but the form of the result might vary according to which course was
being taken; e.g. Maths students might be given a mark out of 100 - while
Computing students might be given a grade ('A' - 'F').
e.g.l struct
{char surnarne[l51;
int course_code;
union
{int maths_mark;
char english_grade ;
};
} student_details ;
sets up a memory space large enough to hold the student's surname, course code
and either a mark for maths or a grade for English.
A student might study several subjects on his/her course - e.g. a science student
might study maths, physics and chemistry - while a languages student might study
French and German.
e.g.2 struct
{char surnarne[l51;
int course_code;
union
{ struct
{int maths_mark;
int physics_mark;
int chemistry_mark;
} science;
. struct
{int french_mark ;
int german_mark;
} language;
} result;
} student_details;
sets up a space called studentjdetails to store the student's surname, course code
and results either for science (maths, physics and chemistry marks) or for
languages (French and German marks). The details stored will depend on the
course code - which enables the program to track what is stored at any particular
time.
The individual items within such a structure are referred to in the same way as
items in a nested structure - so the French mark, for example - is referred to as
studencdetails.result.language·french_mark.
233
Example Program
#include <stdio.h>
main()
{int average-mark;
struct
{char surname[15];
int course_code;
union
{int computing;
struct
{char french;
char german;
} languages;
} result;
student_details;
/*Get student details*/
printf (" \nSurname ?");
scanf ("%s", student_details. surname) ;
printf("\nCourse Code 7 - 1 for Computing");
printf ( "\n - 2 for Languages");
scanf ( "%dH, &student_details. course_code) ;
if (student_details.course_code == 1)
( printf("\nComputing mark (%) 7");
scanf(H%d", &student_details.result.computing);
}
else if (student_details.course_code == 2)
{
printf (" \nFrench grade (A - F) 7");
scanf (" %c",
&student_details.result.languages.french)i
printf (" \nGerman grade (A - F) ? H) ;
scanf (H %c",
&student_details.result.languages.german)i
)
else
( printf("Error\n");
return( 1) ;
}
/*Display Report*/
printf("%s\n", student_details.surname);
if (student_details.course_code == 1)
printf ("Computing Mark is : %d",
student_details.result.computing)i
else
{ printf ("French mark: %c\n",
student_details.result.languages.french);
printf ("French mark: %c\n H,
student_details.result.languages.german);
/*Finish*/
return 0;
234
Exercises
1. Sketch the memory space used by the Example Program.
2. Write a program which will ask for a student's name and course code -
then hislher maths, physics and chemistry marks for a Science student - or
French and German marks for a Languages student; finally displaying the
name and average mark.
235
5.8 Register Variables
This tells the compiler to try to allocate a register in the processor for the variable
rather than a space in central memory. The computer can access a register much
faster than it can a memory space - so this can appreciably speed up a program.
However, the number of registers is usually quite small, so the compiler will not
always be able to allocate every variable specified as register to an actual register
(particularly if many have been specified in the program); in this case a normal
location in memory will be set up.
A register is specified by adding the word register before the variable's definition.
Note that a register variable does not have an address in memory - so &count
would be invalid.
Example Program
#include <stdio.h>
enurn (NO, YES);
main()
(register int count, divisor;
int prime;
/*Check all numbers from 1 to 32000 for Primeness*/
for (count = 1; count <= 32000; count ++)
(/*Check i f count is a Prime Number*/
prime = YES;
for (divisor = 2; divisor < count; divisor++)
(
if «count % divisor) == 0)
(
prime = NO;
break;
)
i f (prime)
/*DisplayPrime Number*/
printf ("%d\n", count);
/*Finish*/
return 0;
Exercises
Write a program to sum all the numbers from I to a given keyed in number. Use
a register for the running total.
236
5.9 Using const and volatile
A memory location may be given the type qualifier const to indicate that its
contents should not be changed by the. program. A value may be given when the
location is set up.
e.g.l canst int pay_rate = 5;
sets up the location payrate - with the contents 5 - and indicates that this value
should not be changed .
The term const may be applied to an array - in which case it signifies that the
items in the array may not be altered.
sets up an array containing the marks necessary to obtain a Pass, Merit and
Distinction in an exam and indicates that these values will not change.
The qualifier const may be applied to a pointer - including an array name - used
as a formal parameter to a function, to indicate that the item the pointer addresses
may not be changed. This is a useful way of protecting arrays which have to be
read by a function.
Here, the parameter course_name expects the address of an array - but the
function will only be allowed to read the array data without modifying it.
The qualifier volatile is used to show that the contents of a memory location may
be changed by some factor outside the program (e.g. by the system, or by some
piece of equipment which needs to send data to the computer).
A memory location may be both const (indicating that the program may not
change its contents) and volatile (indicating that a device or another program may
alter it).
e.g.5 volatile canst int machine_status_in;
sets up a space called machinejtatus_in which can be used by a piece of
equipment to leave data for the program - but not the other way round.
It would, of course, be possible (and desirable) to give an initial value.
e.g.6 volatile canst int machine_status_in = 0;
237
Exercises
1. Write a program which asks an employee for the number of hours he/she
has worked that week • then multiplies the hours by the wage rate
(contents 10.50) to calculate the pay for the week.
2. Write a program which holds the names of the months of the year in an
array of strings (set up so that the program may not modify them) and
displays the appropriate month name when the month number is keyed in.
3. Write a program which asks the user for the name of the capital of
England - then passes hislher answer to a function which checks if the
answer is correct (but may not alter it).
4. Write a program which - after initialising a location called machinestatus
to 0 - continuously checks whether a device has changed it while the
program is running (displaying a message if so). The program itself may
not modify the contents. Of course, for this to work fully you would have
to arrange for a piece of equipment to be able to modify the location's
contents - but there is no need to attempt this for the current exercise.
238
5.10 Using Bitwise Operators (&, I, ", », «)
The & (Bitwise AND) operator is used to check the bits in a particular memory
location against another binary pattern (called a mask) specified by the
programmer - and modify them accordingly: if the corresponding bits in both
patterns are 1 (ON) - the result will be 1 (ON); otherwise the result will be 0
(OFF).
e.g.l. int x = 9;
x = x & 17;
The variable x starts off containing the bit pattern for 9: 0000000000001001
(assuming that integers are 2 bytes long)
This would be checked against the binary pattern for 17: 0000000000010001
Only the bits which are ON in both patterns will be left ON - so the result will be:
0000000000000001
Whatever number is keyed in - all the bits except the last four - will be reset to 0
(i.e. switched OFF).
Only where a bit in the variable numl , AND the corresponding bit in the mask
are equal to 1 (ON) will the result be 1 (ON).
Using & is, therefore, a useful way of turning any particular bit position OFF-
as by making a particular bit (or bits) in the mask O. we can ensure that the
resulting bit(s) at the corresponding position(s) will be O.
Suppose that the single-byte variable (i.e. declared as char) studentstatus byte
uses the individual bits to show whether or not a student is on a particular course.
If the course represented by bit 2 (i.e the third bit from the right) closes - we can
ensure that each student's status reflects the fact that they can no longer be on this
course by setting the relevant bit to 0 - by ANDing against the mask 11111011
(i.e. 251).
239
The I (Bitwise OR) operator is used in a similar way . However, in this case, if the
corresponding bits in either pattern are 1 (ON) - the result will be 1 (ON); if both
bits are 0 (OFF), the result will also be 0 (OFF).
e.g.1 int x = 7;
x = x I 10;
The memory location x starts off containing the pattern: 0000000000000111
This would be checked against the binary pattern for 10: 0000000000001010
Any bits that are ON in either pattern will be left ON - so the result will be:
0000000000001111
So, the value of x will now be 15.
=
Whatever number is keyed in - the command numl numl I 1 will set bit 0 to 1
(i.e. switched ON). - ensuring that all even numbers input are turned into odd
numbers.
Wherever a bit in the variable OR in the mask is 1 - the result will also be 1.
Using I is, therefore, a useful way of turning any particular bit position ON - as by
making a particular bit (or bits) in the mask equal to I, we can ensure that the
resulting bit(s) at the corresponding position(s) will be 1.
240
The 1\ (Bitwise XOR) operator is also used in a similar way. However, in this
case if the corresponding bits in ONE pattern OR the other pattern but NOT
BOrn are 1 (ON) - the result will be 1 (ON); if both bits are 1 (ON) , or if both
bits are 0 (OFF) , the result will be 0 (OFF).
Any bits that are ON in one pattern but not both will be left ON - so the result is
0000000000000110
So, the value of x will now be 6.
Wherever a bit in the variable differs from the corresponding bit in the mask (i.e.
one is 1 and the other 0) the result will be 1. Where the corresponding bits are
equal (i.e both 1 or both 0) the result will be O.
Using 1\ is, therefore, a useful way of reversing the status of any particular bit
position - as by making a particular bit (or bits) in the mask 1, we can ensure that
the resulting bit(s) at the corresponding position(s) will be reversed from what
they were in the variable.
241
The - (Bitwise NOT) operator simply reverses the status of each bit in the
designated variable. So each 1 is reset to 0, and each 1 is set to O.
e.g.l =
int x 15;
x=-x;
The variable x starts off with the binary pattern for 15: 0000000000001111
numl = -numl;
printf ( %d", numl);
II
Again, the status of each bit in the binary pattern for whatever number is entered
will be reversed.
242
The « (Left Shift) operator shifts the whole pattern a specified number of places
to the left. The blanks created on the right of the pattern will be filled with Os.
The variable x starts off with the binary pattern for 15: 0000000000001111
After the left shift (2 places), the pattern will be: 0000000000111100
After the left shift (2 places), the pattern will be: 1111111111000000
The » (Right Shift) operator shifts the whole pattern a specified number of
places to the right. For example, numl >> 4 shifts the bits four places to the
right. If the number is declared as unsigned, any blanks created on the left of the
pattern will be filled with Os. If the number is declared as signed, the way blanks
are filled varies according to the system being used: in some cases an arithmetic
shift will be carried out (i.e. the blanks will be filled with a 1 or 0 depending on
whether the number is positive or negative respectively); on other systems, a
logical shift is used - just filling with Os.
The variable x starts off with the binary pattern for 255: 0000000011111111
After the right shift (2 places), the pattern will be: 0000000000111111
The variable x starts off with the binary pattern for -16: 1111111111110000
On systems where a logical shift takes place (or ifx is declared as unsigned):
after the right shift (2 places), the pattern will be: 0011111111111100
244
5.11 Defining Bit-fields
A structure is used to define the allocation of storage space. Within the structure
each bit-field is defined as an integer - either signed or unsigned - given a name
and the size of the field in bits is stated after a colon (:).
e.g.l struct
{unsigned int on_off_switch:1;
signed int speed: 3;
} machine_status;
This structure allocates two fields. The first, on_offJWitch is only one bit long -
and can be set to 1 (to switch the machine on) - or 0 (to switch it off). Note that
any single-bit field must be declared as unsigned (as there is no room for a sign
bit). The second field, speed, is 3 bits long and is signed - so it can hold a number
between -4 and +3 (if two's complement notation is used). (This might be used
to indicate four reverse speeds and three forward speeds for the machine.)
So, an 8-bit byte of storage could hold the results of four modules .
e.g .2 s truc t
{unsigned int maths :2;
unsigned int C-programming:2;
unsigned int:COBOL-programming :2;
unsigned int:systems_analysis :2;
} student_grades ;
So, a storage location called studentgrades is divided into four 2_bit fields.
On another course, a student is awarded only a Pass or Fail in each module. The
result of each subject could be held as a single-bit field with 1 representing Pass,
and 0 representing Fail.
e.g.3 struct
{unsigned int maths:1;
unsigned int english:1;
unsigned int C-programming :1;
unsigned int COBOL-programming:1;
} student_results;
This structure takes up four bits (i.e half a byte) - with each bit-field comprising
one bit.
245
Bit-fields and ordinary variables may be mixed within a structure. So a memory
area could be set up to hold a student's name as a string and his/her grades in bit-
fields.
e.g.4 struct
(char name[20];
unsigned int maths_grade :2;
unsigned int english_grade :2;
unsigned int computing_grade : 2;
) student_details;
This structure takes up slightly less than 21 bytes (whereas if each grade were
represented by a character, the same data would take up 23 bytes) .
e.g.5 struct
(unsigned int a_bit:l;
unsigned int b_bit :1;
unsigned int c_bit :1;
unsigned int d_bit:1;
unsigned int e_bit:1;
unsigned int f_bit:1;
unsigned int g_bit:1;
unsigned int h_bit :1;
) status_byte;
On some systems this structure would be set up with a_bit as Bit 7 (i.e the most
significant bit) and h_bit as Bit 0 (i.e the least significant bit): on other systems it
could be the other way around.
This means that if you wish to use bit-fields to process an area of memory set up
outside your program - perhaps a signal coming in from a device, you will first
have to check how your C compiler arranges bit-fields - before mapping a bit-
field to the memory in question.
Exercises
1. A computing test consists of 10 multiple choice questions - each with four
possible answers. Set up a suitable space to store these answers using bit
fields.
2. A student's results consist of grades ('A' to 'F') for 5 exams . Set up space
to hold this data.
3. A signal to be sent to a machine consists of: on/off indicator, desired
operating temperature (6 possible settings), desired speed (8 possible
settings). Set up a suitable memory space to hold the signal before it is
sent.
4. A status byte received from a device consists of:
Bit 0 - if ON indicates needs reloading with material ;
Bit I - if ON indicates overheating;
Bit 2 - if ON indicates due for service;
Bit 3 - if ON indicates pressure too high;
Bits 4 - 7 are unused.
What problems will you face when setting up a space with bit-fields to
handle this signal? Suggest two possible definitions.
246
5.12 Using Bit-fields
Values may be assigned to a bit-field in the same way that they are assigned to a
normal integer variable which is part of a structure.
So, looking at e.g.l from the previous lesson, the machinestatus can be set to
switched-on and forward speed 3 by the following commands.
e.g.l machine_s ta tus . on_o f f_swi tch = 1;
machine_status.speed = 3;
A bitfield may be the subject of a condition. So, looking at e.g.2 from the
previous lesson - a student's grades may be displayed in full.
e.g.2 if (student_grade.maths == 3)
printf("Distinction\n");
else if (student_grade .maths - - 2)
printf ( "Merit\n") ;
else if (student_grade .maths 1)
printf( "Pass\n") ;
else
printf("Fail\n");
Bearing in mind that any non-zero value in brackets after ifis evaluated as TRUE
and that zero is evaluated as FALSE (Lesson 1.39) - the results from e.g.3 in the
previous lesson can be efficiently displayed.
e.g.3. if (student_result .maths)
printf("Pass in Maths\n");
else
printf ("Fail in Maths\n") ;
Exercises
1. Write a program which will display 10 multiple choice questions (four
possible answers to each question) - storing the answers in bit-fields - then
displaying the total of correct answers.
2. Write a program which will ask a student to key in his/her grades ('A' - 'F')
for each of five exams - store the data using bit-fields - then display the
average grade.
247
5.13 Standard Character Type Functions
Each of the commands returns a non-zero value if the result of the check is
positive and zero if negative.
displays a suitable message if the variable ch contains the character code for a
numeric digit,
e.g.3 i f (isalnum(chll
printf (II%C is alphanumeric \n ", ch l ,
displays a suitable message if the variable ch contains the character code for a
letter of the alphabet or for a numeric digit.
isupper - checks whether a character is an upper case letter of the alphabet (i.e. a
capitallctter).
displays a message if the variable ch contains the code for an upper case letter.
islower - checks whether a character is a lower case letter of the alphabet (i.e. a
small letter).
e.g.S if (islower(chll
printf ( II %c is a small letter \n ", chj ,
displays a message if the variable ch contains the code for a lower case letter.
248
isspace - checks whether a character is a white space character (i.e. space,
horizontal tab, veritical tab, new line).
displays a message if the variable ch contains the code for a white space
character.
~g~ if (isprint(ch))
printf("%c is printable\n ", ch l r
displays a message if the variable ch contains the code for a printable character.
Commands also exist to convert a lower case letter to upper case and vice versa.
converts the character stored in ch_lower to upper case and places it in ch_upper.
converts the character stored in ch_upper to lower case and places it in ch_lower.
Exercises
1. Write a program which checks every character in a piece of text typed in
by the user and at the end prints a total of all the characters and sub-totals
for the various types - alphabetic, numeric, etc.
2. Write a program which converts all the lower case letters in a piece of text
to upper case.
249
5.14 Using atoi, atof, atol
The commands alai, atof, atol are used to convert numeric digits held in a
character code form in a string into a pure biliary form suitable for arithmetic. To
use these commands, the header file stdlib.h must be #included in the program.
will convert the numeric digits stored in the string num to pure binary and place
the result in the integer variable num1. So, if num held the string "720" - (coded
in ASCII as 0011000111 00110010 00110000) it would be turned into the pure
binary form 0000001011010000 for storing in num1 (assuming that integers take
up two bytes).
will convert the numeric digits in num to pure binary and place the result in num2
(which should have been defined as long int).
atof - converts a string of numeric digits (which may include a decimal point) to a
double . :
will convert the contents of nUn! to a floating point number and place it in num3
(which should have been defined as a double) .
In each case (working from the beginning of the string), the first character which
cannot be part of a number of the appropriate type (i.e. integer or floating point)
marks the end of the number to be converted.
Suppose a student's mark has been typed in as 20/100 and is held in that form in a
character array mark_string.
Exercises
1. Write a program which will accept a number as a string and convert it to
an integer before adding I and displaying it.
2. Write a program which will accept an employee's salary in a string of the
form - e.g. £20000 and convert the number into an integer. (Note that the
address of the second byte of the string should be passed to the conversion
function .)
250
5.15 Pointers to Functions
A program is stored in memory in the same way as data is stored . This means
that particular (machine code) instructions occupy specific storage locations and
therefore have addresses. It is possible to obtain the address of a function (or
rather the address of the start of the function), store it in a pointer variable and
then use this address rather than the name of the function - in much the same way
as the address of a variable can be used instead of its name.
The address of a function is given directly by its name (without any brackets or
parameters).
A suitable pointer variable may be set up to hold the address of a function. This
variable must specify the details of any parameters or return values that the
function may have.
defines a variable called ptr_toJunctionl which is able to hold the address of any
function which has no parameters and no return value.
defines a variable called ptr_toJunction2 which is able to hold the address of any
function which has two integer parameters and no return value.
defines a variable called ptr_toJunction3 which is able to hold the address of any
function which has two integer parameters and an integer return value.
The address of the function may then be placed in the appropriate pointer
variable.
251
Calling afunction via a pointer variable
Once the address of the function has been assigned to the variable - the function
may be called via this pointer.
c.g.6 (*ptr_to_function1) ( ) ;
calls the function whose address is stored in ptr_toJunctionl (in this case
display_square) .
Any parameters may be listed in brackets after the pointer variable name - in the
same way as they would after the function name if it were called directly.
The return value from the function whose address is held may be used in the
normal way - just replacing the function name by (*ptr_variablcname) .
252
Example Program 1
iinclude <stdio.h>
iinclude <stdlib .h>
void display_square(void);
void display_triangle(void);
main()
{void (*ptr_to_shape_function) ();
char shape_chosen;
/*Get choice of shape from user*/
printf ("Display Square (S) or Triangle (T) \n");
scanf (" !fic" &shape_chosen);
I
exit(!)
/*Finish*/
return 0;
}
void display_square(void)
{
printf("* * * *\n") ;
printf ( "* *\n") ;
printf ("* *\n") ;
printf("* * * *\n") ;
)
void display_triangle(void)
(
printf ("*\n");
printf("* *\n");
printf("* *\n");
printf("* * * *\n");
}
253
Example Program 2
#include <stdio.h>
#include <stdlib.h>
int multiply_by_two(int) ;
int multiply_by_three(int);
main()
{int (*ptr_to_calc_function) (int);
char calculation_chosen;
int number;
!*Get number and calculation required*/
printf ("Key in a nurnber\n");
scanf ( " %d", &number);
printf ("Key 0 to double the number\n");
printf("Key T to treble the number\n");
scanf (" %c" I &calculation_chosen);
!*Assign address of relevant function to pointer
variable*!
if (calculation_chosen == '0')
ptr_to_calc_function = multiply_by_two;
else if (calculation_chosen == 'T')
ptr_to_calc_function = multiply_by_three;
else
(
printf("Error\n");
exit(l);
!*Finish*/
return 0;
Exercises
1. Write a program which will give the user the option of drawing a square,
triangle or diamond shape.
2. Write a program which will ask a user for a number - then give the option
of dividing it by 2 or by 3 (use a separate function for each).
254
5.16 Arrays ofPointers to Functions
sets up an array of pointers able to hold the address of three functions (each
function returning no value and expecting no parameters).
A function can then be called either by name or via its address held in the relevant
element of the array.
e.g .3 (*ptr_to_function[O]) ();
ExampleProgram
#include <stdio.h>
void display_square(void);
void display_triangle(void);
void display_diamond(void);
main()
/*Finish*/
return 0;
255
void display_square(void)
(
printf("* * '" *\n") ;
printf("* "'\n ") ;
printf("* "'\n") ;
printf("* * * "'\n O ) ;
)
void display_triangle(void)
{
printf (" *\n");
printf("'" "'\n O);
printf("* "'\n");
printf("* '" '" *\n");
)
void display_diarnond(void)
(
printf(O *\n") ;
printf(" * *\n") ;
printf("* *\n") ;
printf(" * *\n") ;
printf(" "'\n") ;
)
Exercises
1. What will happen if an invalid choice is keyed in when the example
program runs? Why does this happen? Add some instructions to validate
the choice keyed in to avoid this happening.
2. Write a program that will ask the user for a number, then give the choice
of doubling, trebling or halving the number.
256
5.17 Passing Function Addresses as Parameters
The function which will receive the pointer as an argument must have a suitable
formal parameter in its definition.
calls function draw_offices passing across the value 5 and the address of the
function draw..floor_style_I.
Example Program
*include <stdio.h>
main()
{char style_choice;
int num-of_floors;
257
/*Get number of floors required*/
printf( "\nNurnber of floors ");
scanf (n%d n, &nWILof_floors) ;
/*Finish*/
return 0;
}
/*Draw ground*/
printf("****************************\n");
}
void draw_floor_stylel(void)
(printf("* *\n") ;
printf("* *\n") ;
printf ("*. *\n") ;
printf("* * vn " ) ;
printf( "* *\n") ;
)
void draw_f1oor_style2(void)
* \n " ) ;
o o o
{printfl"*
printf("* *\n") ;
printfl"* * \n" ) ;
printf("* *\n") ;
printfl"* *\n") ;
}
Exercises
1. Add another floor style to the Example Program.
2. Write a program which will ask the user whether he wishes to add two
numbers together or subtract them. The program should then pass the two
numbers together with the name of function add or function subtract as
appropriate to function calculate which will return the answer ready for
display .
258
5.18 Command Line Arguments
A user may be allowed (or required) to type extra information for a program to
use, on the command line when starting a program. These extra items of data -
such as filenames, etc - are known as command linearguments.
Many operating system utilities work in this way. For example under MSDOS the
PRINT utility is nonnally followed by the name of the file which should be
printed (e.g. PRINT STUDENT.TXT will print the me called STUDENT.TX1).
Likewise, under UNIX, if the cat utility is followed by one filename - it will
display the contents of that file on the screen (e.g. cat student.txt will display the
contents of student.txt).
This facility can be arranged for any program by defining two parameters for
function main. These are traditionally named argc and argv (although any name
could be used). The parameter argc should be set up as an integer and argv as a
pointer to a pointer to char. The system can then use these parameters to allow
the program to access any command line arguments.
e.g. main (int argc, char * argv [ ) )
{
Main instructions
}
When a user types a program name - followed by one or more arguments - the
system counts the number of items typed (including the program name) and,
when the program starts, stores that information in argc. Each of the items keyed
in is stored as a string of characters - and the address of each string is stored as an
element in an array of pointers - with the last item in the array set to null. The
system then places the address of the beginning of this pointer array into the
parameter argv.
For example, suppose a program called greet has been designed to receive
someone's name as a command line argument - whereupon it should display
"Hello" and the name. If the user keys in greet Julia, then, at the start of the
program, the contents of the various storage locations will be as follows.
argc
address I - - -> J u 1 i a \0
NULL
(Array a/pointers)
259
Example Program I
iinclude <stdio.h>
main (int argc, char *argv())
{
/*Display message*/
printf ("Hello %s\n", argv(l));
/*Finish*/
return 0;
Note that Example Program 1 could receive any number (within system limits) of
arguments from the user- but only displays the first.
It must be remembered that any numbers keyed in are treated as character strings
(i.e. each digit coded in a charactercode - e.g. ASCII - and occupying a separate
byte of memory) - so they must be converted to a suitable numeric form (e.g.
integer) before they can be used in arithmetic (This can be done using the
function atoi from the standard library - stdlib.k should be included in the
program to make this function available.)
Example Program 2
iinclude <stdio.h>
iinclude <stdlib.h>
/*Display total*/
printf ("The total is %d\n", total) i
/*Finish*/
return Oi
}
Note that when running programs which accept several arguments, these
arguments must be separated by spaces.
Exercises
1. Write a program whichwillfind the arithmetic mean (average) of any
number of values typedin as command line arguments.
2. Write a program whichdraws a triangleor a square - according to whether
the user types s or t as a command line argument.
260
5.19 Conversion ofData Types
Integral Variables - Signed Char. Integer, Short Int and Long Int Variables -
converting from smaller to larger.
In general, if a signed whole number value is assigned to a signed variable of the
same or a larger whole number type (e.g, the contents of a signed char converted
into a signed int variable, or the contents of an im copied into a long int space)
there should be no problem - and the binary pattern will be padded appropriately
to represent the same positive or negative value.
The principle will be the same if the contents of a signed char or int location are
converted to a long into
Note that both int and long int are signed unless they are specified as unsigned -
whereas a char may be signed or unsigned - depending on the compiler - so if a
signed char is required it must be specifically stated.
261
Signed Char, Integer, Short Int and Long Int Variables - converting from larger
to smaller
When the contents of a whole number type (signed or unsigned) is copied into a
variable of a smaller whole number type - the most significant (i.e. leftmost)
byte(s) will be removed to make the pattern fit.
The variable num5 starts holding the binary pattern for 5000 -
0001001110001000. To fit this pattern into a single byte location num6. the
leftmost byte is removed so num6 will hold 10001000 - which represents 136 if
the char is taken as unsigned or -120 if the char is signed.
Floatand Double- converting to andfrom Signed Char, Int. Short and Long
When a value of some integral type is copied into afloat or double variable. then
if the value is within the range allowed on the particular system - it will be
represented (approximating to the next higher or lower if necessary). If the range
for the float or double is not large enough to take the figure - then the result will
depend on the system.
When a value of type float or double is copied into a variable of integral type . the
fractional part will be truncated. then the result will depend on whether the
memory space is large enough to take the number; if not. the result will depend on
the system.
262
Mixing data types in arithmetic and comparisons
263
5.20 Using a Cast
It is possible to force a value to take a certain type by using a cast. This consists
of the desired type in brackets preceding the value (or variable name).
converts the value held in numl into a long int version before it is used in
arithmetic. (Note that the size of the storage location numl - and the value
actually held there - is not affected.)
Using a cast is useful if a function must take a parameter of a certain type
(although a prototype for the function wiu ensure automatically that a parameter
is converted to the correct type).
e.g.2 functionl( (double)numl) ;
calls functionl converting the data copied from numl into a double format -
before passing it to the function.
It can also be useful to make it clear that a conversion is intended. For example,
while a return will automatically convert any data returned to the type of the
defined return value for the function, in certain cases this will produce a compiler
warning - to suggest that the programmer should check that he/she really intends
to do this. Carrying out an explicit cast will avoid the warning .
e.g.3 char function! (int x) ;
{
return (char) (x) ;
}
returns the lowest byte of the integer parameter x; it would do the same without
the cast - but the compiler might display a warning.
Exercises
1. Write a cast which will convert the total of numl and num2 to a float -
before passing it as a parameter to function double.
2. Write a cast which will convert the value copied from numl to a char data
type. What might the effect of this be?
264
Appendix A • Developing a C Program on the UNIX system
1. Key in and save the program using vi - see Appendix B - (or some other
editor) - ensure that you give the program file a name ending with .c - to
indicate that it is a C program file.
e.g. cc test.c
265
Appendix B - Using vi
One of the standard editors on the UNIX system is called vi. It is not particularly
friendly, but you will quickly learn to use it - and can usually rely on it being
available on most UNIX systems.
Starting the vi Editor:
vi <program name>
Make up your own program name - but finish it with a suitable extension for the
language you are using - which for C will be .c.
Using vi
Unlike a word processor you cannot simply start typing - but must tell the editor
that you intend to do so by keying <escape> i.
Every time you want to change what you are doing - say go from typing in text, to
deleting text or saving the file - you will have to tell the editor that you intend to
do so by pressing <escape> followed by the appropriate code.
Note that for the last four commands, when the colon (:) is pressed after the
<escape> key, a colon will appear at the bottom of the screen ready for the w, wq
or q! to be typed.
Note that UNIX treats capital letters and small letters as different.
The vi editor has a wide range of additional commands which will generally be
found in one of the manuals for your system.
266
Appendix C - The Binary System
Our normal system of counting is denary. When we count past nine, we cannot
represent 10 in a single digit so we 'carry' and use an additional digit.
You could place column headings above a figure to help with additions, etc.
(young children would probably do so).
The column headings for the denary system would be: ... 1000s..100s..lOs..Is - so
that, for example, the number 271 would be arranged in columns as:
1005 105 15
2 7 1
In other words, 271 means 2 lots of 100 plus 7lots of 10 plus 1 unit.
We use this number system (also called Base 10 because the column headings
grow larger - as they move leftwards - by being multiplied by 10) because we
have 10 fingers.
A computer, therefore, uses Base 2 (i.e the Binary System) to count. It is possible
to place column headings above a binary number - these are multiplied by 2 as
they move leftwards: ..... 25612864 32168421
0 1 I 0 0 0 I 1
64 + 3 2 + 2 + 1 = 99
267
Converting Binary numbersto Denary
Step 1. Arrange the numbers under binary column headings - so that the number
finishes in the rightmost (Is) column. (To make the headings - start on the
right with 1 - then keep multiplying by 2 as you move to the left - until
there are enough headings to cover the whole number.)
32 16 8 4 2 1
1 100 1 1
Step 2. Add all the headings from those columns which contain Is to get the final
answer.
Step 1. Write down the binary column headings finishing on the left with the
largest binary column heading which is less than the denary number that
you are converting. In this case the largest heading which will go into 39
is 32.
32 16 8 4 2 1
Step 2. Subtract the largest heading from the number and write a I in that column
(i.e. the 32 column in this case). Keep a note of the remainder.
32 16 8 4 2 1
1
Step 3. Repeat Step 2 with the remainder (in this case 7) and keep doing so until
the whole number has been used up.
32 16 8 4 2 1
1 0 0 1 1 1
So, 39 Denary can be written as 1001I 1 Binary.
268
Signed Binary Numbers
If a variable is set up as signed (so that it can hold positive or negative values) the
most significant bit (i.e, the largest column heading) is used to denote whether the
number is positive or negative.
Possibly the most common notation is two's complement. The most significant
bit's column heading is made negative (all the other columns remain positive).
So for a one-byte location, the headings will be: -128, 64,32, 16,8,4,2, 1.
As for unsigned numbers, the value of a number is found by adding all the
column headings that are switched 0".
1 000 1 0 10
- 128 + 8 + 2 -1 18
269
Appendix D - The Hexadecimal System
The Hexadecimal System (i.e. base 16) is a convenient shorthand for Binary and
is used - for example - in machine code programming.
ConvertingBinary to Hexadecimal
It is far simpler to convert binary to hexadecimal than to tum it into decimal.
Step I Startingfrom the right, split the binary number into groups of four digits
(quartets):
lllOOlll
Step 2 Again starting from the right place column headings above the binary
digits - treating each quartetas separate.
8421 8421
1110 0111
8421 8421
1 I 10 01 I 1
= 14 Decimal =7 Decimal
=E Hexadecimal =7 Hexadecimal
Step 4 Place the hexadecimal digits together.
270
Appendix E· Character Codes
Letters of the alphabet, punctuation marks, control codes - such as carriage return,
and numeric digits ready to be sent immediately for display are represented by a
character code. One of the most common is ASCIT (American Standard Code for
Information Interchange) .
So, the command printJ("%d", numl} will convert the value in numl from pure
binary to a suitable character code (one code for each digit of the number) before
sending it to the screen for display. However, the command printJ("%c". numl)
will simply send the binary pattern stored in numl to the screen - on the
assumption that it contains a recognisable code for a single digit (or other
character).
Note that ASCII is a seven bit code, so the most significant bit of a byte (e.g. a
character location) is not used.
271
ASCllCodes
272
Index
#dejine 38 comments 4
actual parameters 26 Comparing Pointers 135
addition 11 compilation errors 2
address 130 Compile 265
Address arithmetic 133 compiler 1
ampersand 9 Compiling and Linking Modules 215
and 17 Conditional Expressions 230
ar 221 const 237
argc 259 continue 44
arguments 26, 226 Conversion of Data Types 261
argv 259 ctype.h 248
Arithmetic 11, 12 Data Types 56
arithmetic shift 243 declaration 213
arithmetic signs 11 Decrementing 13
array 133,137,139,255 default 18
Array of pointers 259 division II
Arrays - Single Dimensional 113, 114 do..while 20, 52
Arrays - Two Dimensional 116, 117 Dynamic Memory Allocation 145,
Arrays of Pointers 139 147
Arrays of Pointers to Functions 255 end-of-string character 119
Arrays of Strings 119 enum 40
Arrays of Structures 121, 122 EOF 193, 194, 198
ASCn 271 Evaluating a Condition 52
Assignment 10, 106 executable file 265
Assignment Operators 12 exit 45
atof 250 extern 216
atoi 250 extemallinkage 213
atol 250 extemal variables 35
automatic 37 fclose 186, 189, 191, 194, 196, 199,
binary stream 184 206
Binary System 267 feof 189, 199
Bit-fields 245, 247 ferror 206
Bitwise AND 239 fgetc 198
Bitwise NOT 242 field 184
Bitwise Operators 239 FILE 185, 188, 191, 193, 196, 198
Bitwise OR 240 Files 184
Bitwise XOR 241 Files - Handling Errors 205
block 21,43,226 Files - Reading Data from Disk one
bounds checking 114 byte at a time using getc 198
braces 3, 16, 19,21,43 Files - Reading Data from Disk using
break 18,44 fread 188
buffer 50 Files - Reading Formatted Text from
buffered input 49 Disk using fscanf 193
Buffered Keyboard Input 50 Files - Storing Data on Disk using
byte 124 fwrite 185
case 18 Files - Writing Data to Disk - one byte
cast 145,264 at a time - using putc 196
cc 215,265 Files - Writing Formatted Text to Disk
Central Memory 124 using fprintf 191
char 6,56 float 6
character 6, 10 float.h 58
Character Codes 271 floating point 6
Character Type Functions 248 fopen 185, 188, 191. 193, 196, 198,
Circular Linked Lists 158 205
Command Line Arguments 259 for 21
273
formal parameters 26 logical shift 243
Formatting Output 46 long 56
fprintf 191, 207 long int 57
fputc 196 loop 19
fread 188,206 machine code 1
free 146, 153, 161, 170, 183 macro 224, 226
fseanf 193,207 Macros - using Arguments 226
fseek 200, 208 maUoe 145, 147, ISO, 159, 168, 174
Functions 22,24,26,28,30,213 Mixing data types 263
Functions - 32 Modular Programming 212
fwrlte 186, 206 module 212
getc 198,208 modulus 11
getehar 49, 50 multiplication 11
gets 9,50 Nested Structures 109, 111
Global Variables 35,216 newline 8
goto 44 not 17
header file 222 object file 265
header files 3, 5, 22 Object Libraries 221
Hexadecimal 270 Operations on Structures 106
hierarchical 70, 83 or 17
Hierarchical Coding 83, 86, 88 out-of-line 70, 83
if 52 parameter 106, 130, 137,257
if .. else 14, 15, 16 Parameters 26
Implementing a Complex Program Passing Arrays as Parameter 137
Design using Hierarchical Code Passing Function Addresses as
90 Parameter 257
Implementing a Complex Program Passing Pointers as Parameters 130
Design using In-line Code 79 pointer to a function 257
Implementing a Design 70 pointer variable 127, 128, 135
Implementing a Program Design 93 Pointers 126, 133, 139
in-line 70 Pointers and Arrays 133
In-line Coding 71, 75, 77 Pointers to Arrays of Pointers 143
Incrementing 13 Pointers to Functions 251, 255
initialise 10 Pointers to Pointers 142
initialising 19 Pointers to Structures 131
Input Format 48 preprocessor 224
int 6,56 print! 3,7,9,46,57
integer 6 processor 236
isalnum 248 Program Design 59
isalpha 248 prototype 22
isdigit 248 putc 196, 208
islower 248 putchar 49
isprint 249 Queues 167
isspace 249 Random Files 200
isupper 248 record 184, 188
iteration 61,75,86 Recursion 34
Keywords 5 recursive 179
label 44 register 236
Layout of a C Program 4 Register Variables 236
Left Shift 243 relational expression 52
library 221 Relational Operators 14
library files 3, 265 remainder 11
limits.h 58 reserved 5
link 265 return 3, 28, 44, 107
Linked Lists 149 Right Shift 243
Local Variables 24,226 run-time error 2
logic error 2 scanf 7,9,48,50,57
Logical Operators 17 selection 62,77,88
274
semi-colon 3 volatile 237
sequence 60, 71 while 19,52
short 56 white space characters 50
short int 57 word 124
signed 56
Signed Binary Numbers 269
signed char 56
signed int 56
signed short int 57
sizeof 146
standard library 5
static 37,218
Static Local Variables 37
stdio.h 3
stdlib.h 45, 145,250
storage location 124
strcmp 42
strcpy 42
stream 184
string 106,113,119
String Functions 42
string.h 42
Strings 9, 10
strlen 42
struct 101, 104
structure 111,121,122,131,233,245
Structure Diagrams 59
Structure Types 104, 105
Structures 101, 102, 109
Subscripting Pointers 135
subtraction 11
switch 18
symbolic constant 38, 40
symbolic constants 58
tab 8
Test Plan 97, roo
Testing Software 96
text format 184
text stream 184
tolower 249
toupper 249
Trees 173
typedef 228
unbuffered input 49
union 231,232,233
union type 231
Unions and Structures 233
UlVlX 1,215,265,266
unsigned 56
unsigned char 56
unsigned int 56
unsigned long int 57
unsigned short int 57
Using Pointers 128
Variables 6
vi 265,266
Vocabulary 5
void 22
275