100% found this document useful (1 vote)
99 views

C Programming Compress

This document provides an overview of C programming. It begins with introductory topics such as variables, data types, control structures, and functions. It then covers more advanced topics like complex data structures, pointers, memory allocation, modular programming, and file handling. The document aims to equip readers with the fundamental and advanced skills needed to design and implement programs in C.

Uploaded by

Rudrali Hitech
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
99 views

C Programming Compress

This document provides an overview of C programming. It begins with introductory topics such as variables, data types, control structures, and functions. It then covers more advanced topics like complex data structures, pointers, memory allocation, modular programming, and file handling. The document aims to equip readers with the fundamental and advanced skills needed to design and implement programs in C.

Uploaded by

Rudrali Hitech
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 284

C Programming

Othertitles ofrelatedinterest

Ian O. Angell and Dimitrios Tsoubelis, AdvancedGraphicson VGA and XGA


Cards UsingBorlandC++
N. Frude, A Guideto SPSs/PC+, secondedition
Peter Grossman, Discrete Mathematics for Computing
PercyMett, Introduction to Computing
Tony Royce, COBOL - An Introduction
Tony Royce, Structured COBOL - An Introduction
C Programming

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.

ISBN 978-0-333-63851-4 ISBN 978-1-349-13759-6 (eBook)


DOI 10.1007/978-1-349-13759-6

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

Section 1 - Introductory C Language Topics


1.1 Introduction to Programming 1
1.2 Programming in C 2
1.3 Structure of a C Program 3
1.4 Layout of a C Program 4
1.5 Vocabulary 5
1.6 Variables 6
1.7 Using scanf and printf 7
1.8 Strings 9
1.9 Assignment 10
1.10 Arithmetic II
1.11 Arithmetic using Assignment Operators 12
1.12 Incrementing and Decrementing Variables 13
1.13 Using if .. else (1) 14
1.14 Using if.. else (2) 15
1.15 Using if.. else (3) 16
1.16 Using Logical Operators (&&, II. !) 17
1.17 Using switch 18
1.18 Repetition using while 19
1.19 Repetition using do .. while 20
1.20 Repetition using for 21
1.21 Functions 22
1.22 Functions - Local Variables 24
1.23 Functions - Passing Parameters 26
1.24 Functions - Using return 28
1.25 Functions - Repetitive Calls 30
1.26 Functions - Passing Work on to Other Functions 32
1.27 Recursion 34
1.28 Global Variables 35
1.29 Static Local Variables 37
1.30 Using #define 38
1.31 Using enum 40
1.32 String Functions 42
1.33 Blocks 43
1.34 Using breaklcontinuelexitlretumlgoto 44
1.35 Using printf - Formatting Output 46
1.36 Using scanf - Specifying Input Format 48
1.37 Using putchar/getchar 49
1.38 Buffered Keyboard Input 50
1.39 Evaluating a Condition 52
lAO Data Types 56

Section 2 - ProgramDesign. Implementation and Testing


2.1 Introduction to Program Design 59
2.2 Program Design - Sequences 60
2.3 Program Design - Iteration 61
204 Program Design - Selection 62
2.5 Program Design - Combining Structures 64
2.6 Implementing a Design in C 70
2.7 In-line Coding from a Structure Diagram - Sequences 71
v
2.8 In-line Coding from a StructureDiagram - Iteration 75
2.9 In-line Coding from a StructureDiagram - Selection 77
2.10 Implementing a-Complex ProgramDesign using In-line Code 79
2.11 Hierarchical Coding from a Structure Diagram - Sequences 83
2.12 Hierarchical Coding from a StructureDiagram - Iteration 86
2.13 HierarchicalCoding from a StructureDiagram - Selection 88
2.14 Implementinga ComplexProgramDesign using Hierarchical Code 90
2.15 Implementinga Program Designusing In-line and Hierarchical Code 93
2.16 Testing Software 96

Section 3 - Complex Data Structures

3.1 Defining Structures 101


3.2 Using Structures 102
3.3 Defining StructureTypes 104
3.4 Using StructureTypes 105
3.5 Operations on Structures 106
3.6 Defining Nested Structures 109
3.7 Using Nested Structures III
3.8 Defining Arrays - Single Dimensional 113
3.9 Using Arrays - Single Dimensional 114
3.10 Defining Arrays - Two Dimensional 116
3.11 Using Arrays - Two Dimensional 117
3.12 Arrays of Strings 119
3.13 Defining Arrays of Structures 121
3.14 Using Arrays of Structures 122
3.15 Central Memory 124
3.16 Pointers 126
3.17 Using Pointers 128
3.18 Passing Pointers as Parameters 130
3.19 Pointers to Structures 131
3.20 Pointers and Arrays 133
3.21 Passing Arrays as Parameters 137
3.22 Arrays of Pointers 139
3.23 Pointers to Pointers 142
3.24 Pointers to Arrays of Pointers 143
3.25 Dynamic MemoryAllocation - Requesting and Releasing Space 145
3.26 Dynamic MemoryAllocation - Using Space 147
3.27 Linked Lists 149
3.28 Circular Linked Lists 158
3.29 Queues 167
3.30 Trees 173
3.31 Files 184
3.32 Files - Storing Data on Disk using fwrite 185
3.33 Files - Reading Data from Disk using fread 188
3.34 Files - Writing FormattedText to Disk using fprintf 191
3.35 Files - Reading Formatted Text from Disk using fscanf 193
3.36 Files - Writing Data to Disk - one byte at a time - using pute 196
3.37 Files - Reading Data from Disk one byte at a time using getc 198
3.38 Random Files - using fseek 200
3.39 Files - HandlingErrors 205

vi
Section 4 - Modular Programming

4.1 Introduction to Modular Programming 212


4.2 Using Functions from other Modules 213
4.3 Using Variables from other Modules 216
4.4 Static Global Variables 218
4.5 Object Libraries 221

Section 5 • Advanced and Supplementary C Language Topics

5.1 Macros 224


5.2 Macros - using Arguments 226
5.3 Using typedef 228
5.4 Conditional Expressions 230
5.5 Defining Unions 23/
5.6 Using Unions 232
5.7 Unions and Structures 233
5.8 Register Variables 236
5.9 Using const and volatile 237
5.10 Using Bitwise Operators (&, I, ", », «) 239
5.11 Defining Bit-fields 245
5.12 Using Bit-fields 247
5.13 Standard Character Type Functions 248
5.14 Using atoi, atof, atol 250
5.15 Pointers to Functions 25/
5.16 Arrays of Pointers to Functions 255
5.17 Passing Function Addresses as Parameters 257
5.18 Command Line Arguments 259
5.19 Conversion of Data Types 26/
5.20 Using a Cast 264
Appendices
A - Developing a C Program on the UNIX system 265
B - Using vi 266
C - The Binary System 267
D - The Hexadecimal System 270
E - Character Codes 27/
Index 273

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.

The lessons are grouped into five sections:

Introductory C Language Topics


Program Design. Implementation and Testing
Complex Data Structures (Structures. Arrays. LinkedLists. Queues. Trees. Files)
Modular Programming
Advancedand Supplementary C Language Topics
It is intended that the reader should start practical programming exercises as soon as
possible and should be able to start writing simple programs from Page 1.

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

A computer needs to be given a list of instructions telling it how to do any job


you want it to do.

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!

Such a set of instructions is called a program.

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.

The instructions can be written in anyone or more of a number of languages


designed for the purpose - e.g. - C, COBOL, BASIC, FORTRAN, ALGOL,
Pascal , etc.

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.)

In general - you will need to be able to -


1. key in and edit the lines of C instructions that you want to write - saving
them on disk ;
2. compile them (translate them to the computer's own language);
3. link the compiled program to any other necessary programs and library
files to set up an executable program - i.e, a program which the computer
can load from disk straight into any vacant area in its memory and run -
whenever it is required.
and then
4. tryout your instructions (i.e. run the program) to see that the computer
does what you want.

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

The C language consists of a fairly small number of main commands.

In addition to these, however, additional commands can be drawn from library


files of additional words. Many of these libraries are standard and most C
development packages will include them.

In addition, it is possible to buy extra libraries to further augment the vocabulary


already available, and the programmer can produce hislher own set of commands
and their definitions and include them in a library file.

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.

A simple C program consists of the following:

1. a note of the relevant header file associated with the library from which
commands will be drawn - in the form:

#include <IUIme ofheader file>

2. the main program function heading -

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

A program should be set out clearly - so that it can be understood easily by


anotherprogrammer.
A number of factors will help to make a program easy to follow.
1. Follow a standard layout. Indentationand spacing are used to show that
certain groups of instructions go together.
2. Use comments to make clear what the program does and what each
section of the program is for. Commentsin a C program should be placed
between 1* and *1. Note that comments make no difference to the
way the computer carries out the program - they simply make it easier for
another programmer to read.

Example Program

#include <stdio .h>

/*Program displays a sailing boat*/

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

Words in the C programming language fall into a number of categories.

I. Keywords - words that have a special meaning in the C language. They


may not be used for any other purpose.

auto break case char const


continue default do double else
enum extern float for goto
if int long register return
short signed sizeof static struct
switch typedef union unsigned void
volatile while

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.

3. Words made up by the programmer for functions (commands), variables


(data items), etc . These must follow certain rules.
a) They may contain letters (A - Z, a - z), numbers (0 - 9) and the
underscore character ( _ ) - but must not start with a number, and
should not usually start with an underscore (as this start character
can be reserved for functions used by the library).
b) Upper Case (i.e Capital) letters and lower case (small) letters are
seen as different - so Studencmark, studentmark and
STUDENT_MARK are totally distinct names in C.
c) They may be of any length. However, the ANSI C standard only
requires the first 31 characters to be significant (i.e, serve to
distinguish between different words). So, for example, many
compilers would not distinguish between
studentmarkfor_maths_assignmenCl (35 characters) and
studentmarkfor_maths_assignmenc2 - seeing them both as
referring to the same variable. Some compilers may use more
characters - but it is best to keep within the 31 for portability (i.e.
so that you can transfer your programs between different types of
machine and use different C compilers). For certain identifiers -
only the first 6 characters may be significant on some compilers.
(This will be discussed in Lesson 4.2).

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 .

e.g.I int number1 , number2, answer;

sets aside three spaces in memory to hold integer (i.e. whole number) data - and
gives each space a label.

e.g.2 char mathsgrade, englishgrade, itgrade;

sets aside three spaces - each one capable of holding a single character - such as a
letter of the alphabet.

e.g 3 float roomlength, roomwidth, roomheight;

sets aside three spaces capable of storing floating-point numbers (i.e numbers
containing fractions - e.g. 5.25).

These variable definitions can then be included in the program as follows :

main()
{
int mathsmark, englishrnark , averagemark;
char overallgrade;

Actual program instructions

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.

e.g.l scanf ("%d" I &mathsmark)

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.

e.g.2 scanf ( "%c", &mathsgrade)

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

e.g.3 scanf ("%f" I &weight)

tells the computer to accept an ordinary decimal fraction ("%f" indicates a


floating point number), then store it in a space called weight.

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).

e.g.4. printf ("%d" , mathsmark)

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:

e.g.? printf ("Your average mark is %d", averagemark)

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 .

e.g.9. printf( "\n\nYour grade is %c\n\n",grade)

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) ;

printf ("Please key in your English mark\n") i


scanf ("%d" , &englishmark);

I*Calculate total mark*1


totalmark = mathsmark + englishmark;

I*Display total mark*1


printf ("Your total mark is %d" I totalmark);

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.

To allocate space in memory to store a string, it is necessary to specify char as the


data type and state how much space is required. In fact, one extra character must
be allowed for a special end-of-string marker which is added by the computer.

e.g.l char student_name [11] ;

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.

e.g.2 scanf ( "!lis" I student_name);

instructs the machine to accept a string and then store it as studentname.

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.

e.g.3 gets (student_name);

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.

e.g.4 printf ("!lis" I student_name);

will display the contents of studentname.

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

A value may be placed in a storage location using =. This is called assigning a


value to the variable. Note that this method cannot be usedfor strings.

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.

e.g.3 salary = 100.25;

places 100.25 in the floating point location salary.

It is possible to initialise (i.e, give a starting value to) a variable at the same time
as it is defined.

e.g.4 int mark = 40;


sets aside an integer storage location called mark and immediately gives it the
value 40.

e.g.5 char grade = 'C';

sets aside a character storage location called grade and initialises it to the value
'C'.

e.g .6 float wage_rate = 10 .50;

sets aside a floating point location called wageJate and places to.50 in it.

The contents of one storage location may be copied to another.

e.g.7 num2 = numl;


copies the contents of num I to num2.

A value may be placed in several locations at the same time.

e.g.S numl = num2 = num3 = 10;

places to in numl, num2 and num3.

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

A command such as:

total wage =basic-pay + overtimepay

instructs the computer to add the contents of the memory locations basicpay and
overtime-pay and place the answer in total wage.

The arithmetic signs available are:


+ addition subtraction
* multiplication / division
% modulus (i.e. find remainder after division)
Note that Modulus cannot be used on a floating-pointnumber.

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.

So, x = a + b * c means first multiply b by c and then add a to the answer -


placing the result in x.

This order can be changed by using brackets ( ) around parts of an expression


which are to be calculated first - e.g.-

x = (a + b) * c means first add a and b, then multiply the answer by c.

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= %=

e.g.l numl += 10 adds 10 to the contents ofnuml,

e.g.2 numl - = 5 subtracts 5 from the contents ofnuml,

e.g.3 numl *= 2 multiplies the contents ofnuml by 2.

e.g.4 numl 1= 3 divides the contents ofnuml by 3.

e.g.5 numl %= 3 finds the remainder after dividing the


contents ofnuml by 3.

The name of a variable may be placed after the assignment operator.

e.g.6 numl += num2 increases the contents of numl by the


contents of num2 (and leaves num2
unchanged).

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.

Likewise, count-- and --count

can both be used to subtract 1 from 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.

However, x = ++count means increment count then copy the


contents to x. So, if count started off as 10, then after the statement, both
variables would contain II .

The same rule applies to the decrement operator.


So, x = count -- means copy count into x, then decrement
count.

While x = --count means decrement count, then copy its


contents into x.
Exercises
I. What will be the value of y after each of the following sets of instructions?
a) y=l; b) y=lO; c) y=4;
Y++ ; y-- ; --Y;
d) x = 4; e) x = 4; f) x = 10;
y = ++x; y = x++; y = x--;
2. Write programs:
a) to ask a user for a number, add I and display the answer;
b) to ask the user for a number, subtract 1 and display the answer.

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:

e.g.I if (student_mark >= 50)


printf ( "PASS" ) ;
else
printf ("FAIL") ;

Note
I. Brackets are placed around the condition.
2. A semi-colon finishes each statement.

Sometimes the else is not required:

e.g .2 if (student_mark >= 80)


printf ("You have won a prize") ;

(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");

printf ("Your mark is %d\n" , mark);

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)

Groups of statements enclosed in braces { } (including further if..else statements)


may be carried out following ifor else commands.

e.g.1 i f (mark >= 80)


(
printf("DISTINCTION\n ");
printf ("NAME PRIZE-BOOK REQUIRED ");
scanf("%s", prize);
}
else
(
if (mark >= 40)
printf ("PASS\n");
else
printf ( "FAIL\n") ;
}
printf("%d\n",mark);

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.

e.g.2 if (mark >= 40)


(
printf("PASS ");
if (mark >= 70)
printf ( "GRADE A") ;
else
printf ("GRADE B") ;
}
else
printf (" FAIL ") ;

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, !)

Sometimes a number of conditions all have to be true before a particular action is


taken - this can be expressed using && (meaning and).

e.g.l if (exam_mark_1 >= 50 && exam_mark_2 >= 50)


grade = I P' ;

(i.e. the student has to get 50 or more in both exams to pass.)

Sometimes anyone of a number of possible conditions has to be true for the


action to be taken - this can be expressed using II (meaning or).

e.g.2 if (exam_mark_1 >= 50 II eXCUlLmark_2 >= 50)


grade = I pI ;

(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.

e.g.3 if (! (number_of_spelling_errors > 10))


essay_grade = 'pI ;

(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.

e.gA if ((examLmark_1 >= 50 && ex~mark_2 >= 50)


-I I exam_mark_1 >= 80 I I ex~mark_2 >= 80)
grade = I pI;

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.

e.g.l switch (course_code)


(
case 1 printf ( "ENGINEERING"); break;
case 2 : printf("COMPUTER SCIENCE"); break;
case 3 : printf ("SCIENCE"); break;
de fault : print f ( "INVALID COURSE CODE"); break;

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.

Sometimes, the action to be taken may depend on a character variable.

e.g.2 switch (student_grade)


(
case 'A' :printf ("DISTINCTION"); break;
case 'B' : p r i n t f ("MERIT "); break;
case 'C' :printf("PASS"); break;
default :printf ("FAIL"); break;

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

It is possible to tell the computer to repeat an instruction or a group of instructions


- as long as a certain condition is true.

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 {} .

e.g.2. reply = ' Y I ;


while (reply != 'N')
(
printf ("What is your mark out of ten ?\n") ;
scanf ( "%d" , &mark_out_of_ten) ;

printf ("Percentage is %d\n", percentage_mark);

printf ( "Any more marks to be calculated ?\n ") ;


scanf (" %c", &reply);

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

It is sometimes useful to test at the end of a group of instructions, rather than at


the beginning, whether or not they should be carried out again. The do .. while
command can be used in this case.

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.

startvalue conditionlor continuing aClion aftereach repetition


e.g, I for (count = 1; count <= 10; count++)
printf ("Hello\n") ;

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.

To repeat a group ofinstructions


If you want to make the computer repeat a number of statements - rather than just
a single command as in the example above - it is necessary to surround the
program lines that are to be repeated with braces { and } - thus creating a block
of statements. (This has already been used with while and do .. while in the
previous lessons.)

e.g.3 for (x = 1; x <= 5; x++)


(
printf("hello\n");
printf ("how are you ?\n");
printf ("Message has appeared %d times\n", x) ;

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.

A program which uses functions will normally be structured as follows :

I. a list of header files to be included in the program

e.g.I lIinclude <stdio.h>


2. a prototype for each function: this consists of its name and information as
to what sort of data (if any) will be passed to/from the function .

e.g.2 void draw_line (void) ;

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.

3. the main program function

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 { }.

e.gA void draw_line (void)


(
instructions which make up the junction draw_line
)

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_sails (void)


{
printf(" I \n");
printf(" * *1 *\n");
printf (" * * I *\n") ;
printf(" * * I *\n") ;
printf( " * * I *\n");
printf(" * * I *\n");
printf(" * * I-----------*\n");
printf( II I \n");
)

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 in the example program, the variable course_appliedJor is local to


main. and cJtudkd_before is local to check_quaUfkations. Neither function
can see or modify the other function's variables (and they do not in this case need
to do so).
When the above program runs, the program will first allocate space for any
variable belonging to main (in this case courscappliedJor). It will then work
through the instructions in main.
If/when function check_qualifications is called, the memory space
c_studied_before will be set aside and can be used by the function as the
instructions within it are carried out.
Immediately the computer reaches the end of checkqualifications, and returns to
main, c_studied_before will be deallocated, and any data in it will, effectively.
be lost.

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;

void showstars(int number_ot_stars)


{int count;
for (count = 1 ; count <= number_ot_stars; count++)
printf("··) ;

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.

Rather than passing specific numbers as parameters to a function - it is possible to


ask the computer to take the values from memory locations.

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;

printf ("How many stars do you want displayed ?");


scanf ( " %d " , &number_required);
showstars (number_required); When function showstars is called -
the contents of numberJequired will be copied into the
temporary memory location number_of_stars.
return 0 ;

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 ()
{

printf ( "%d", total (10, 20); Asks the computer to display


the return value from function total. Note that the name of any
junction which gives a return value can be used in a similar way to
the name of a variable.
return 0;

int total (int num1, int nurn2)


lint anSi

ans = numl + nurn2;


return (ans); The computer will return the value ofans so that it can be
used by the calling junction (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);

In general, this is preferable if the calculation is simple - but if the problem is


more complicated , it is better to break it down into stages and use working
locations as necessary.

A function may. of course, return a character or floating point value.

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;

char grade(int mark)


{
if (mark >= 50)
return ( I PI);
else
return ( 'F' ) ;

Note that a function finishes immediately it encountersa return command.

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 ;

void draw_square (void)


(
printf("* * * *\n") ;
printf("* *\n") ;
printf("* *\n") ;
printf("* * * *\n") ;

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);

for (count = 1; count <= num; count++)


draw_triangle ( ) ;

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;

void greet(int nUmber_of_times)


{int count ;
for (count = 1; count <= number_of_times; count++)
printf("Hello "Ii
printf("\n") ;

Similarly. a function returning a value may be called several times - passing


different data each time. The following example asks a student for his/her maths
and English marks, then calls grade twice - the first time to work out a maths
grade, then to decide on the English grade.

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);

printf ("Please key in your English mark") ;


scanf (" %d", &english_mark) ;

printf ("Maths grade: %c\n", grade (maths_mark) );


printf(HEnglish grade: %c\n",grade(english_mark));

return 0;

char grade(int mark)


{
i f (mark >= 50)
return ( 'PI) ;
else
return ( , F ' ) ;

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

iinclude <stdio .h>


char grade (int, intI;
int average (int , intI ;
main()
(int examl, exam2 ;
printf("Please key in your first exam mark?\n");
scanf ( %d", &examl) ;
II

printf ("Please key in your second exam mark?\n") ;


scanf ( "%d", &exam2);
printf ( "Grade: %c\n", grade (examl, exam2));
return 0;
)

char grade(int markl, int mark2)


{
if (average (markl , mark2) >= 85)
return ( 'A');
else if (average (markl, mark2) >= 65)
return ( B' ) ;
I

else if (average (markl, mark2) >= 50)


return ( 'C');
else
return( 'F');

int average(int nurnl, int num2)


{
return «nurnl + num2) /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

A function may call itself - this is called recursion. Th is technique is suitable


when a problem can be seen as breaking down into progress ively simpler
problems.

For example, to sum all the whole numbers up to 5 (i.e. ] + 2 + 3 + 4 + 5) can be


broken down into the simpler problem of summing all the numbers up to 4 then
adding 5 to the answer. In tum - summing all the numbers up to 4 can be seen as
summing all the numbers up to 3 then adding 4 - and so on. So, we just need a
function which sums all the numbers up to a given value - and this can work
recursively.

Example Program

#include <stdio.h>

main()
{int num;

/ *Ge t a number from user* /


printf ("Key in a whole number\n") i
scanf ( " %d " &num) ;
I

/ *Ca l l function Bum_U~_to and display answer*/


printf ("The answer is %d\n". sum_up_to (num) ) ;

/ *Fi n i s h */
return 0;

int sum_up_to(int number)


{
if (number > 0)
return(number + sum_up_to(number - 1));
else
return(O) ;

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.

tinclude <stdio .h>

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;

/ftGet student's name and mark from user and store in


global variables ft/
void get_student_details(void)
(
printf ("Key in student name\n") ;
gets(student_name);
printf ("Key in mark\n");
scanf (" %d" &student_mark) ;
I

/ftDisplays student report ft/


void display_report (void)
(
printf ( "\nReport for %s\n", student_name);
printf ("Mark = %d\n" student-mark);
I

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.

e.g.l static int account_number;


allocates a memory space called accountn umber - which will remain in
existence all the time the program is running - so every time the function which
owns it is called. the contents from the last call will still be stored.

A static variable may be set to a starting value - so that the contents is known the
first time the function is called.

e.g.2 static int account_number = 0;


sets accountnumber to 0 when it is first set up (i.e. when the function to which it
is local is first 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) .

e.g.l if (student_mark >= 50)


printf("Your grade is Pass\n");

So, in this program, the pass mark - 50 - is a constant.

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 .

e.g.2 #define PASS_MARK 50

gives the name PASS_MARK to the number 50 .

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 -

e.g.3. if (student_mark >= PASS_MARK)


printf( "Your grade is Pass\n");

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.

e.gA #define GREETING "He ll o"

defines the word GREETING as meaning the message "Hello".

So, a command to display the word "Hello" could then be written as:

e.g.5 printf (GREETING) ;

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>

tdefine MANAGER 1M'


tdefine SUPERVISOR'S'
tdefine ASSISTANT 'A'

tdefine COMPANY_NAME "X & Y Computer plc\n "

main ( )
(char employee_grade, employee_name(21l ;
int wage;

/*Get employee's details*/


printf ( "Name ?\n");
gets(employee_name);
printf( "Please key in your grade\n") ;
printf(" M for Manager\n");
printf (" S for Supervisor\n") ;
printf(" A for Assistant\n ");

scanf (" %c", &employee_grade);

/*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;
}

In the example program - the use of the symbolic constants MANAGER,


SUPERVISOR and ASSISTANT make the program easier to read - but before the
program compiles, they will be replaced in the text by 'M', 'S' and 'A' respectively,
so that the program can decide the wage on the basis of the grade keyed in.

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}

defines the symbolic constants PASS_MARK and MERIT_MARK as 50 and 65


respectively . Two #define statements would have the same effect here.

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.

e.g.2 enum {ZERO, ONE, TWO , THREE, FOUR, FIVE, SIX,


SEVEN, EIGHT, NINE};

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.

e.g.3 enum {SUN = 1, MON , TUE, WED I THU I FRI I SAT);

gives the values I to 7 to the names of the days.

e.gA enum {PAWN=1 , KNIGHT=3, BISHOP=3, ROOK, QUEEN};

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") ;

MERIT_MARK and PASS_MARK will be replaced with their defined numeric


values before the program is compiled.

e.g.6 for (day = SUN; day <= SAT; day++)


printf( "%d\n", day);

will display the numbers I to 7 down the screen.

e.g.7 total_value_of......pieces 8*PAWN + 2*BISHOP +


2*KNIGHT + 2*ROOK + QUEEN

40
Enumeration may be used to set up an integer data type (or a memory space)
restricted to the values represented in the list.

e.g.S enum days_ot_week {SUN = 1, MON, TUE, WED, THU,


FRI, SAT};

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.

The data type and variable may be set up together.

e.g.lO enum boolean {FALSE , TRUE} encl.-at_tile;

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 strcpy copies the contents of one string to another.

e.g.1 strcpy (namel, name2) i

copies the contents of the character array name2 to namel.

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.

e.g.2 strcmp (namel, name2) i

compares the contents of namel and name2.

One (or both) strings could be literals.

e.g.3 s trcmp (course_name, "COMPUTING") i

compares the contents of course_name to see if it contains "COMPUTING" - if so


it will return O. Otherwise. if the course name contains a name alphabetically
higher - then a value greater than 0 will be returned; if lower then a value less
than 0 will be returned. Note that a word in small letters is not the same as a
word in capitals. The comparison will depend on the character code used - so in
ASCII, 'A'is lower in the scale than 'a'.
The function strlen returns the length of a string.

e.gA s trr Len (namel) i

will return the length of the string contained in namel .

e.g.5 strlen ("JULIA") i

will return 5 (the length of the name "JULIA").

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

In C, a section of code surrounded by braces { } is called a block - and the


instructions within it are treated alike (as if they are one large statement).

~~l for (x=!; x<=20; x++)


(
printf("Hello\n");
printf ("Goodbye\n") ;

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.

e.g.3 printf ("Key in length of line ");


scanf("%d". &length);
{int count;
for (count=!; count<=length; count++)
printf ( *" ) ;
II

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.

e.g. I for (count = 0; count <= 9; count++)


{
scanf(" icY, &letter};
if (letter == 'A')
break;
printf("%c", letter};
}

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.

e.g.2 for (count = 0; count <=9; count++)


{
scanf(" %c" , &letter};
if (letter == 'A')
continue;
printf("%c", letter};

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.

goto - tells the computer to jump to a label (a programmer-defined identifier


terminated with a colon) in the same function. It is generally only used for an
exceptional situation - when it allows the remainder of the code to be bypassed to
avoid complication in dealing - for example - with an error.

e.g.3 if (condition for unusual error)


go to error_routine;

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.

e.gA if (conditionfor major error)


(
/*Deal with important error processing*/

/*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.

Size of Field for Display

A minimum field width can be specified.

e.g.l printf ("%lOd", number) ;

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.

A variable field width may be specified by including an asterisk (*) instead of a


numeric value. The variable name holding the actual width should be included in
the argument list immediately after the data item to which it relates .

e.g.2 printf("%*d", number, field_width_for_number);

will display the contents of number within a field whose width is governed by the
value stored infield_widthJor_number.

Precision for Floating Point Numbers

The precision required for display can be specified as a decimal point followed by
a number.

e.g.3 printf("%.2f", wage);

displays wage showing two figures after the decimal point.

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 .

e.gA printf( "%.5" , name);

will display the first 5 characters of the string name .

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.

e.g.6 printf("%-lO.2f", wage);

will display the contents of wage. left aligned within a 10 space field, showing
two digits after the decimal point.

The following table assumes:

int numl = 200;


char name [ 9] = "Anderson";
float num2 = 2.345678;

Instruction I Output on Screen


printf ( "%d\n", numl); 200
printf ( "%10d\n", numl); 200
printf("%-10d\n", numl); 200
printf("%f\n ", num2); 2 .345678
printf("%10f\n", num2); 2.345678
printf ( " %-10f\n", num2); 2 .345678
printf("%.4f\n", num2); 2.3457
printf ( " %1 0 . 4f\n", num2); 2.3457
printf ("%-10 . 4f\n", num2); 2 .3457
printf ("%s\n", name); Anderson
printf ("%10s\n", name); Anderson
printf ("%-10s\n" , name) ; Anderson
printf("%.2s\n", name); An
printf( "%10.2s\n" , name) ; An

Using appropriate formatting instructions is particularly important when there are


several data items to be arranged in columns.
e.g.? printf("%-lOs %lOf %lOf\n", name,salary,bonus) ;

will display the data neatly set out in columns:

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.

e.g.l scanf ( II %d/ %d/ %d", &day, &month, &year) i

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).

e.g.2 scanf ( II %d%*c ", &num) i

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.

e.g.3 scanf (" %d%*c%d%*c%d", &day, &mon th, &year) i

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.

It is possible to specify the maximum number of characters which may be entered


into a string .

e.gA scanf ( "%10s ", name) i

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

The putchar command displays a single character on the screen .

e.g.I putchar (grade) ;

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).

The getchar command accepts one character typed in by the user.

e.g.2 grade = getchar ( ) ;

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

iinclude <stdio .h>

main ( )
{char letl;

printf ("Type in a message - • to finish\n ") ;


do
{
letl = getchar();
putchar(letl);
while (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

In general, the standard keyboard input commands in C - scanf, getchar, gets,


etc. - operate on buffered input. This means that as the user keys in data , it is not
processed immediately by the program - but instead stored temporarily in a buffer
(i.e. an area of memory set aside as a waiting area) .

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.

Each keyboard input command treats data in the buffer differently.

Assume the buffer contains:

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.

Consider the following instructions .

e.g printf ( "\nPayroll number?");


scanf ("%d" I &payroll_number) ;
printf("\nName ?");
gets(employee_name);

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

scanf ("%s" I name);


b) scanf ("%d" I &id_number);
scanf ("%s" I first_name);
c) scanf ( "%d" I &id_number);
gets (name);
d) scanf ("%d%*c " &id_number);
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

A knowledge of the detailed mechanics of how a conditional instruction works in


C is important - as it allows the programmer to write very concise and efficient
instructions.

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.

Then the value is checked . If it is 1 or any non-zero value (whether positive or


negative) then the command which depends on the condition will be carried out.

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.

e.g.l if (mark >= 50)


printf ( Pass") i
II

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.

However, a variable containing a suitable value can be used in the brackets


instead of a relational expression to control what action to take .

e.gA printf ("Type 1 - if you passed your exam");


printf ( "Type 0 - if you failed") i
scanf( "%d", &result);
if (result)
printf ("Well done");
else
printf( "Better luck next time");
52
Again, the value in result will be checked - and an appropriate message will be
displayed depending on whether it contains zero or some non-zero value.

Alternatively, any sort of expression (e.g. arithmetic) which will produce a


suitable value can be used in the brackets to control what action to take.

e.g.5 if ( eXarlLresul t = mark/ 50 )


printf (" Pass") i
else
printf ( "Fail" ) i

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.

Action may be taken depending on the return value of a function. Suppose


function functl (in common with many standard functions) indicates that it has
been unsuccessful in carrying out its task by returning the value O. Suitable
action could then be taken by getting if to check the return value.

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") ;
}

will display "Hello" repeatedly down the screen without stopping.


53
Again, the contents of a variable can be used.

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 .

The return value from a function may be used.

e.g.If) while (functl)


{
pr intf( "Success")i

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.

e.g.l l while ( ! get_acc_no_and_check ( ) )


{
printf ("Account nwnber is not correct\n")
printf ("Please retype");

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");
}

printf("Type in code for goods you wish to order ");


scanf ("%d", &stock_code);
return 0;

int get_acc_no_and_check(void)
(int account_num;
printf("\nPlease key in your account number");
scanf ("%d" &account_nwn);
I

if (account_nwn > 5000)


return(!);
else
return(O) ;

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

A char may be specified as signed or unsigned (otherwise, if it is just defined as


char. the compiler will decide). This is irrelevant if a character code is being
stored. However, a char variable can be used to store a small number - in which
case the sign may be important and takes up some space. So, if the char is
defined as unsigned - a value from 0 to 255 can be held. IT the variable is defined
as signed - then a value from -127 (or -128 if two's complement notation is used)
to + 127 may be stored.

When defining the variable, the signed or unsigned modifier is placed before the
word char.

e.g.l char numl


signed char num2;
unsigned char numJ;

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.

In ANSI C, an unsigned int is guaranteed to hold a value from 0 to 65535. and a


signed int variable to hold a value from -32767 to +32767 but, on a more
powerful computer, the compiler may provide a larger space for ints and a wider
range of values can be stored .

e.g.2 int num4;


unsigned int num5;
signed int num6;
An int may also be specified as short or long. Using a short int is only relevant if
a system allows more than the standard space for an ordinary int - in which case
the short int saves space , as again the unsigned short int is guaranteed to hold a

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.

e.g.3 short int num6;


unsigned short int num7;
signed short int num8;
An unsigned long int is guaranteed to hold a value from 0 to 4294967295 and a
signed long int to hold a value from -2147483647 to +2147483647. Again a long
int is assumed to be signed if no modifier is specified.

e.g.4 long int num9;


unsigned long int numlO;
signed long int numll;
The word int may be left out when specifying a short or long into

e.g.5 short num6;


unsigned short num7;
signed short num8;
and
e.g.6 long num9;
unsigned long numlO;
signed long numll;
have the same effect as e.g.3 and e.g.4.

When using print! or scan/with an unsigned integer, %u should be used instead


of %d in the formatting string.
e.g.7 printf ( %u·, num4);
U

Both %d and %u should be preceded by h - to indicate a short integer - or 1 to


indicate a long integer.

e.g.S printf ( tld ", numll);


II

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

Datatype Minimum Range SymboUc Consttlnt


signedchar -12710127 SCHAILMIN SCHARjiAX
char ·12710127 CHAR,..MIN CHAR..MAX
or 0102SS (depends on compiler)
unsigned char Ot02SS UCHAR....MIN UCHAR..MAX
signedshort inl -32767 10 32767
shortinl -32767 10 32767 SHRT..MIN SHRT..MAX
unsigned shorlinl -32767 to 32767 USHRT_MIN USHRT_MAX
signedint -32767 to 32767
int -32767 to 32767 INT_MIN INT_MAX
unsigned int 0106SS3S UINT_MIN UINT..MAX
signediong int -2147483647 to 2147483647
longint -2147483647 to 2147483647 LONG..MIN LONG..MAX
unsigned long int oto 4294967295 ULONG..MIN ULONG..MAX
float IE -37to IE +37 FLT..MIN FLT..MAX
(6 digits precision - FLT_DlG gives the ~ecisiOn)
double IE -37to IE +37 DBL..MIN DBL AX
(10digits precision - DBLJ>IG gives the precision)
longdouble IE -37TO IE +37 lDBL..MIN lDBL..MAX
(10digits preclslon- LDBL_DlG givesthe precision)

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

When writing a report or an essay, it is helpful to make a plan - perhaps listing


headings or main topics that you wish to include.

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.

e.g. int maths_rnark, english_mark, average_mark ;


char surnarne[20], first_narne[20];

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.

Example - Produce a Student Report.


Write a program to ask for a student's name and marks in Maths and English.
calculate the average mark and display a report.

This program can be divided into a sequence of three main tasks:


I. get the student's details;
2. calculate the average mark;
3. display the report.

The breakdown can be shown using a structure diagram as follows:

PRODUCE
STUDENT
REPORT

Get Calculate Display


student average report
details

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).

Example 1 - Display a screen of 24 Hellos (in a column).


Displaying a whole screen of Hellos breaks down into the simpler task of
displaying the message line Hello - repeated 24 times. .On a structure diagram
this is shown as:

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.

Example 2 - Ask for a stream of numbers to be keyed in - stopping when zero is


typed.
This job will break down into the simpler task of programming the computer to
ask for a single number - repeated until a zero is keyed.

GET NUMBERS
FROM
KEYBOARD

un til number keyed 0


Get a *
number 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

c I - If mark is greater than or equal to 85


c2 - If mark is greater than or equal to 65 but less than 85
c3 - If mark is greater than or equal to 50 but less than 65
c4 - If mark is less than 50

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

cl - if amount-due is greater than 0


c2 - if amount-due is not greater than 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.

Example 1 - Design a program to produce reports for a class of 20 students.

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

un til any-more-to-do = 'N'


Process *
bonus for
one employee

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.

After adding this sequence under the process-bonus-for-one-employee rectangle,


the complete structure diagram will now be as shown below:

PROCESS
BONUSES
FOR FIRM

until any-more-to-do = 'N'


Process *
bonus for
one employee

Get Calculate Produce Ask i f


employee bonus payslip there are
details for bonus any more

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.

This task breaks down into a sequence of four smaller tasks:


1. get name and mark;
2. calculate average;
3. decide grade;
4. display report.

So, the overall job can be represented as:

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

cI - if average mark is greater than or equal to 85


c2 - if average mark.is greater than or equal to 65 but less than 85
c3 - if average mark is greater than or equal to 50 but less than 65
c4 - if average mark is less than 50

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.

There are two ways this might be done:


I. produce a report as each student's details are keyed in;
or
2. allow details of every student to be entered before producing all the
reports.

For the moment we shall consider the first of these possibilities.

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

c1 - until no more students

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

c 1 - until no more students


c2 - if averagemarkis greater thanor equal to 85
c3 - if averagemarkis greater than or equal to 65 but less than 85
c4 - if averagemarkis greater thanor equal to 50 but less than 65
c5 - if averagemarkis lessthan 50

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 implementing a design (i.e. turning a design into a working C program) -


the programmer must decide whether the code (i.e. program instructions) will be
in-line (i.e, all the instructions are written one after the other with no delegation of
work to functions) or out-of-line - also called hierarchical code - (i.e. with all
important work delegated to functions), or a mixture of both.

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.

2. Functions may be written to work totally independently of the rest of the


program. The use of parameters and local variables means that there is no
need to be concerned that variables used by another part of the program
may be modified accidentally.

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.

b) if a program needs modification, it is generally easy to modify a


single function - without having to read through the whole program and
knowing that there is no need to worry that the rest of the program might
be affected.

3. It is usually simpler to test a large program function by function.

4. A large program is usually easier to read if it is split into functions .


However, too many very small functions can have the opposite effect.

5. Work can easily be divided up between programmers in a team.

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.

e.g.l /*Get student details*/

/*Calculate average*/

/*Display report*/

2. Fill the space after each comment with appropriate C instructions to carry
out the task.

e.g.2 /*Get student details*/


printf (" \nSurname ?");
scanf ( "%s" I surname);

printf (" \nEnglish mark?") ;


scanf ( " %d " &english_mark);
I

printf ( "\nMaths mark ?");


scanf ( "%d" I &maths_mark);

/*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

printf ("Maths: %d\n " maths_mark);


I

pi-intf( "Average: %d\n", average_mark);

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.

e.g.3 #include <stdio .h>

/ *Pr od uc e Student Report*/


main()
{char surname[20);
int english_mark, maths_mark, average_mark;

/*Get student details*/


printf (" \nSurname ?") ;
scanf ( "%s", surname);

printf (" \nEnglish mark?") ;


scanf ("%d", &engl ish_mark) ;

printf ( "\nMaths mark ?") ;


scanf ("%d", &maths_mark);

/*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"'/

2. Decide which command - for, while .. do, do .. while - should be used to


carry out the repetition and place this· with the appropriate condition - before the
comment.

e.g.2 for (count = 1; count <= 24; count++)


/"'Produce message line"'/
3. Place braces after the comments to include the commands which will be
repeated (these may be removed later if only one instruction is to be repeated)

e.g.3 for (count = 1; count <= 24; count++)


/"'Produce message line"'/
(

4. Place the appropriate instruction in the braces to carry out the task
specified by the comment.

e.g.4 for (count = 1; count <= 24; count++)


/"'Produce message line"'/
(
printf("He11o\n");

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.

e.g.5 iinclude <stdio.h>


/*Produce Message Screen*/
main ( )
<int count;
for (count = 1 ; count <= 24; count++)
/*Produce message line*/
<
printf("Hello\n");
}

/*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).

e.g .l /*Award Pass*/

/*Award Fail*/

2. Decide which command will be used to control the selection - if..else or


switch - . and place these with the appropriate conditions before the relevant
comment.

e.g .2 i f (mark >= 50)


/*Award Pass*/

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).

e.g.3 i f (mark >= 50)


/ *Award Pass*/
{

}
else
/*Award Fail*/
{

77
4. Fill in the appropriate instructions in each section.

e.g.4 i f (mark >= 50)


/ *Award Pass */
(
grade = 'p' ;
}
else
/*Award Fail*/
(
grade = 'F' ;

As there is only one instruction dependent on each selection, the braces can be
removed.

e.g.5 if (mark >= 50)


/*Award Pass*/
grade = 'P';
else
/*Award Fail*/
grade = 'F';

However. it is probably clearer to leave them in place, in case later on any


additional instructions need to be added within each grade category (e.g. perhaps
a message saying Well Done' might be required for a Pass.

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

1. Write a skeleton program - just consisting of the outline of the main


function .
e.g.l main ( )
{ -

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.

e.g.4 /*PROGRAM - produce reports for class */


main()
{char grade, any~ore_students, surname[16];
int maths~rk, english_mark, average~rk;
do
/*Produce report for student*/
(

) while (any~ore_students -- 'YO);


}

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.

e.g.5 / * PROGRAM - produce reports for class */

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.

e.g.6 /"'PROGRAM - produce reports for class "'/


main ( )
{char grade, any~ore_students, surname[16];
int maths~rk, englis~rk, average~rk;
do
/"'Produce report for student"'/
{I"'Get data"'/
/"'Calculate average mark"'/
/"'Decide Grade"'/
if (average~rk >=85)
/"'Award Distinction"'/
{

}
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);

printf (" \nEnglish Mark 1");


scanf ( "%d", &english_mark);

printf("\nMaths Mark 1");


scanf ("%d", &maths~ark);

/*Calculate average mark*/


average_mark = (english_mark + maths~ark)/2;

/*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);

/*Check if any more students*/


printf ( " \nAny more students 1 - YIN");
scanf (" %c", &any~ore_students);

while (any_more_students == 'yO);

/*Finish*/
return 0;

82
2.11 Hierarchical Coding from a Structure Diagram» Sequences

Implementing a program design using out-of-line or hierarchical code involves


breaking the design into functions.

Example
Tum the following structure diagram into C instructions.

PRODUCE
STUDENT
REPORT

I I
Get Calculate Display
student average report
details

l. Make up a function name relating to each task in the structure diagram. If


the top box represents the whole program - rather than just a sub-section within
the program - then this box will correspond to function main in the program;
otherwise make up a suitable name describing the actual job.

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.

e.gA 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~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.

e.g.5 #include <stdio. h>


void get_student_details(void);
void calculate_average(void);
void display_report(void);
int maths_mark, english_mark, average_mark;
char surname[20];
main()
{
get_student_details();
calculate_average();
display_report();
return 0;

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

printf ("Average: %d\n" average_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

1. Make up a function name corresponding to each box in the structure


diagram. If the top box in the structure diagram represents the whole program
rather than just a function within the program - call this task function main;
otherwise give it a suitable name relating to the actual task.

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");
}

5. Add any necessary #include statements and function prototypes.


e.g.5 iinclude <stdio. h>
void produce_message_line(void);
main()
lint count;
for (count = 0 ; count < 24; count++)
produce~essage_line();

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

1. Make up a function name relating to each task in the structure diagram.

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

2. Create outline functions, including any necessary parameter definitions,


return types and local variables.
As award_exam~rade is probably a function called by another function (e.g .
main) it would be reasonable to define a formal parameter mark - which could
receive a value passed across from the calling function and a return type of char
for the function· so that it can send back the grade decided. However, this
depends, of course, on the design of the rest of the program. The functions
awardpass and award~rade will not need to receive data - so the parameter
definitions will be void - but they will need to return a grade.

e.g.2 char award....grade (int mark)


{
}

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.

e.g.3 char award.-grade (int mark)


{
if (mark >= 50)
returh(award-pass())i
else
return(award_fail())i
)

(So, awardgrade will return the grade returned by either award-pass or


awardJail) .

4. Fill the other functions with suitable instructions.

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).

It can be split into:


1. draw roof;
2. draw first to fifth floors;
3. draw ground floor (different because it has a door).

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

I. Set up a skeleton program consisting of one function corresponding to


each box in the diagram. The top box will convert to function main.

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)
{
}

2. The count variable can be defined locally within


drawJirsCto..fifth..floors - as this is the only function which will use it.
3. Tum the name of the top box in the structure diagram into a comment
describing the whole program and place this at the top of the program text.

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;

Drawing the first to fifth floors is an iteration of drawing a single floor - so


draw..firsCtoJifth..floors should contain - within a loop - a call to drawfloor.
e.g.3 void draw_firs t_to_fi fth_floors (void)
{lont count;

for (count=!; count<=5 ; count++)


draw_floor () ;

The functions draw jroof, draw groundfloor and draw-floor can now be filled
with suitable C instructions.

e.g.4 void draw_roof (void)


(
printf("*************** *****************\n");

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;

for (count=!; count<=5; count++)


draw_floor ( ) i
}

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

c 1 - until no more students

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

c2 - if mark is greater than or equal to 85


c3 - if mark is greater than or equal to 65 but less than 85
c4 - if mark is greater than or equal to 50 but less than 65
c5 - if mark is less than 50

Function calculate_average

Prototype: int calculateaveragefint, int)


Description: Accepts two marks (stored as formal parameters mark] and mark2)
and returns the arithmetic mean.
Local Variables (apart from parameters) : None
Global Variables affected: none

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 .

Functions calculate_average and decide grade will be coded in a similar way to


main.

e.g. / * PROGRAM - produce reports for class */

#include <stdio .h>


char decide_gradelint) ;
int calculate_average (int, int) ;
main( )
{char grade, any_more_students, surname[16];
int maths_mark, english_mark, average-mark;

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 - decide grade


Accepts a percentage mark and returns a grade*/
char decide_grade(int mark)
{
i f (mark >=85)
/*Award Distinction*/
return ( 'D');
else if (mark >= 65)
/ *Award Meri t */
return ( 'M');
else if (mark >= 50)
/*Award Pass*/
return ( 'PI);
else
/*Award Fail*/
return ( 'F');
}

/*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

It is important to test programs to ensure that they work correctly.

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).

Black Box 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.

White Box Testing

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).

The test plan may be set out as a table.

Test Plan

Name
INPUT
mallu eDgluh oy more I Name
HXPECI1!D OUTPUT
Avera!e Grade

I. PHD..IPS 0 0 y PHD..IPS 0 PAD..


2. JONES 0 50 Y JONES 25 PAD..
3. ROBERTS SO SO Y ROBERTS . 50 PASS
4. JOHNSON 50 60 Y JOHNSON 55 PASS
5. KEBEDB 60 10 Y KEBEDB 65 MERIT
6. SMITH 80 80 Y SMITH 80 MERIT
1. CHAN 85 95 y CHAN 90 DISTINCTION
8. GIBSON 100 100 N GIBSON 100 DISTINCTION

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 .

White Box Testing


Test data should be chosen with reference to the design to ensure that every task
is tried out. The following points should be considered.

I. Every task in the structure diagram should be tested .


2. Selections and iterations need particularly careful testing to ensure that the
conditions are correctly written. So, for example, the boundaries between
the options in a selection should be carefully tested - using values just
below, exactly equal to, and just above the boundary . For example, if a
student should get a Distinction for a mark of 85 or more, then the
condition needs to be tested by keying in details for a student who will get
an average of 84, another who gets 85 and a third who achieves 86.
3. Any tasks which are difficult to program (e.g. complex calculations) need
careful testing.

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

cl - until no more students


c2 - if average mark is greater than or equal to 85
c3 - if average mark is greater than or equal to 65 but less than 85
c4 - if average mark is greater than or equal to 50 but less than 65
c5 - if average mark is less than 50

Test Plan

Name
INPUT
malhs eDgliJh anymore I Name
BXPECfBD OUTPUT
A_aco Gtado

I. HENDERSON 41 51 Y HENDERSON 49 PAIL


2. ROBBRTS 50 50 Y ROBBRTS 50 PASS
3. DAVIES 49 53 Y DAVIES 51 PASS
4. wn..LJAMS 60 68 Y wn..LJAMS 64 PASS
5. KEBBDE 60 10 Y KEBBDE 65 MERIT
6. PATEL 60 12 Y PATEL 66 MERIT
7.0LADBJU 84 84 Y OLADBJU 84 MERIT
8. ROBINSON 80 90 Y ROBINSON 85 DISTINCTION
9. CAl 84 d8 N CAl 86 DISTINCTION

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

I. PHIT.IPS a a y PHIT.IPS a PAn.


2. JONES - a so y JONES 25 PAIT.
3. HENDBRSON 47 51 Y HENDERSON 49 PAn.
4. ROBBRTS SO SO Y ROBERTS SO PASS
5. DAVlBS 49 53 Y DAVIBS 51 PASS
6. JOHNSON SO 60 Y JOHNSON 55 PASS
7. Wll..UAMS 60 68 Y Wll..UAMS 64 PASS
8. KEBBDB 60 70 Y KEBBDB 65 MERIT
9. PATEL 60 72 Y PATEL 66 MERIT
10. SMITH 80 80 Y SMITH 80 MERlT
11.0LADBJU 84 84 Y OLADI!.JU 84 MERlT
12. ROBINSON 80 90 Y ROBINSON 85 DISTINCfION
13. CAl 84 88 Y CAl 86 DISTINCTION
14. CHAN 85 95 Y CHAN 90 DISTINCTION
IS. GIBSON 100 100 N GmSON 100 DISTINCTION

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

It is possible to group a number of related variables together and refer to them by


one overall name.

For example, a student's surname and mark could be grouped together and the
whole item could be referred to as studenCdetails.

In C, this is called a structure.

A space in memory for a stucture suitable for holding information on a student


could be set up as follows.
e.g.1 struct
{char surname[lO]i
int marki
} student_detailsi
This allocates a space in memory called studencdetails • which in tum consists of
two data items: surname (a 10 character string) and mark (an integer).

<---------------------------------stude"t_details------------------------------->

<-------------------------- ------surname----------------------------><-mark->

Information on a department store's account customers might be held as follows:


e.g .2. struct
{char surname[lO]i
char first_name[ll];
char address[60] i
int credit_limit;
float current-balance ;
} customer_account_details;
The details are grouped in memory under the overall variable name
customer_accouncdetails.

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,

e.g.I printf ("%d\n", student_details . ma r k ) ;

would display the mark on the screen.

e.g.2 scanf ( "%d" I &student_details. mark) ;


would allow the user to key in a number, then store it as mark. Note that the &
(where applicable with scanf) is placed before the structure name.

In the case of a string, no ampersand is used, so:

e.g.3 scanf ("%s II I student_details. surname) ;

or

e.g.4. gets (student_details. surname) ;

would allow the user to key in a word and store it the memory location surname.

e.g.5 student_details. mark = 50 ;

sets the mark to 50.

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;

/*Get emPloyee details from user*/


printf ( "Surname ?\n");
scanf("%s·, employee_details .surname) ;
printf ("First name? \n");
scanf ( "%s·, employee_details. first_name) ;
printf("Department? \n") ;
scanf("%s", employee_details.department);
printf ( •Annual Salary ? \n ");
scanf("%f", &employee_details.annual_salary);
/*Calculate monthly pay*/
employee_details.monthly-pay =
employee_details .annual_salary / 12 ;
/*Display payslip*/
printf("\n***********************************\n");
printf ("%s ", employee_details. first_name) ;
printf("%s\n", emp1oyee_detai1s .surname);
printf ("%s\n", employee_details.department);
printf ("Pay: %.2f\n ", employee_details . mon t h l y-pay ) ;
printf("***********************************\n");

/*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

An alternative way to set up a structure - which is particularly useful if the same


type of structure will be used for several different variables - is to define the
layout for the structure and give this layout a name tag, without actually
allocating any memory space at that stage.

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 ).

e.g.I struct person_type


{char surname[ll]i
char first_name[ll] i
char address[60]i
} i

When no name is placed after the structure definition - no memory space is


allocated. The definition above simply sets up a shape or pattern for a data type
called struct person_type (in much the same way as one might make a pattern for
a dress from paper without actually cutting any material.)
This pattern can then be used to describe the amount and layout of memory
required when defining actual memory space later on. So it would now be
possible to set up one or more variables of type struct personj typ e as follows.

e.g.2 struct person_type student_detailsi


sets up a memory space called studentdetails according to the pattern called
person_type.

e.g.3 struct person_type student_details,


lecturer_detailsi
would set 'up two areas of memory - one to hold a student's details and the other to
hold a lecturer's. Both would have the same format.

It is also possible to set up a structure type and memory spaces at the same time.

e.g.4 struct person_type


{char surname[ll]i
char first_name[ll]i
char address[60]i
} student_detailsi
sets up a type (struct person_type) and a memory space tstudenr details] of that
type.

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).

So, a student's surname would be referred to as studencdetails.surname.while a


lecturer's surname would be referred to as lecturer_details.surname.

e.g. printf ( "%s", student_details. surname) i

would display the student's surname.

Example Program
iinclude <stdio.h>

main ( )
{/*Define a structure type called sta~~_ty'pa*/
struct staff_type
{char surname[21Ji
float annual_salarYi
} i

/*Allocate a memo~ space called eaployea_datails -


conforming to structure sta~~_ty'pa*/
struct staff_type employee_details;

/*Get employee details from user*/


printf (·Surname ?\n") ;
scanf ( "%s", employee_details . surname) ;
printf ("Annual Salary? \n");
scanf("%f", &employee_details.annual_salary) ;

/*Calculate new annual pay after 20% pay rise*/


employee_details .annual_salary *= 1.20;

/*Display statement of new details*/


printf ("%s\n", employee_details. surname) ;
printf ("New Salary:% .2f\n",
employee_details .annual_salary);

/*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

In addition to working on the individual elements of a stucture it is possible to


carry out many operations on the structure as a whole.

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.

e.g.1 lecturer_details = student_details;

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.

e.g.2 student_details = lecturer_details;

A structure may be passed as a parameter to a function. It is easiest in this case to


define a structure type as global (i.e. at the top of the program outside any of the
function definitions), then use this type to define actual memory spaces within the
calling and called functions.

In the following example program, the structure pattern studenttype is set up


globally and is then used to allocate a memory space, studentjdetails, within
main, and a formal parameter, studentrecord, within function producereport.

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;
};

void produce_report (struct student_type);

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;

void produce_report(struct student_type student_record)


(int average;
printf("\n*****STUDENT REPORT*****\n");
printf ("Name %s\n", student_record. name) ;
printf (IIEnglish %d\n", student_record. english_mark) i
printf ("Maths %d\n", student_record.maths_mark);
average = (student_record.maths_mark +
student_record.english~ark)/2;
printf ("Average %d\n", average);
printf("************************\n");
}

A structure may also be returned by a function.


e.g.3 struct student_type get_student_details (void) ;
The prototype for gecstudenCdetails indicates that it will return a structure of
type struct studenttype.

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;

struct student_type get_student_details(voidl


{struct student_type student_data;
printf ( "\nName?" I ;
scanf ("%s" ·s t uden t _ da t a . name I ;
I

printf("\nEnglish mark?"l;
scanf (" %d" &student_data . english_mark I ;
I

printf("\nMaths mark ?"l;


scanf ("%d" &student_data.maths_mark);
I

return(student_datal;

void produce_report(struct student_type student_recordl


{int average ;
printf("\n*****STUDENT REPORT*****\n"l;
printf ("Name %s\n" student_record. name I ;
I

printf ("English %d\n", student_record. englishJRarkl ;


printf ("Maths %d\n" student_record.mathsJRark);
I

average = (student_record .maths_mark +


scudent_record.englishJRarkl/2;
printf ("Average %d\n" average);I

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

Structures may be nested (i.e. placed within other 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

So, studencdetails can be defined as a structure - comprising name and grades.

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

e.g.4. struct name_type


{char surname[lOJ;
char first_name[lOJ;
} ;

sets up a type (i.e a pattern) called structname_type;

e.g.S struct address_type


{char line_l[40J;
char line_2 [ 40 J ;
char postcode[7J;
};

sets up a type called structaddressfype ,

Neitherof the above allocates any actualmemory_space.

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.

e.g.6 struct student_details_type


{struct name_type name ;
struct address_type address;
int mark
};

This creates a shape called student_details_type which is made up of: name


(conforming to struct namefype}; address (conforming to struct address_type);
and an integer called mark.

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.

e.g.l printf ("%C", student_details . grades .maths) ;

displays the student's maths grade.

e.g.2 scanf (" %c", &student_details. grades . english) ;

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;

/*Get student details from user*/


printf ("Surname ?\n");
scanf (" %s" , student_details . name . surname) ;
printf ("First name? \n");
scanf ( II %s ", student_details. name . first_name) ;
printf (IIMaths mark ? \n II) ;
scanf("%d", &student_details .marks.maths);
printf ("English mark? \n");
scanf ( "%d", &s tudent_de tails . marks . english) ;

/*Calculate average mark*/


student_details.marks.average =
(student_details .marks.maths +
student_details.marks.englishl/2;

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).

Memory space would be set aside for such an array as follows:

e.g.l int studentJ\ark[lO);

reserves space in Central Memory to store ten whole-number marks.

In a similar way, space can be set aside to store a table of floating point numbers
(i.e. numbers containing decimal fractions):

e.g.2 float employee_wage [5) ;

reserves space for an array of five employee's wages.

Similarly, storage space can be set aside for a table of characters .

e.g.3 char student_grade [10] ;

allocates space for an array of ten student's grades.

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.

e.g.1 studentJllark[2] = 100;

sets the third mark to 100.

e.g.2 printf ("%d", studentJl\ark[6]);

displays the seventh mark.

e.g.3 scanf("%d", &student_mark[O);

accepts a mark from the keyboard and stores it in the first space in the
studentmark table.

Note the &. as usual with scant

To add flexibility to a program, rather than using an actual number as an index, it


is possible to use the name of an integer variable which contains a suitable value.

e.gA student_no = 1;
printf ("%d", studentJl\ark[student_no]);

will display the mark for the second student (i .e. studencmark[1I).

e.g.5 printf ("Key in student number (0 to 9) \n");


scanf ("%d", &student_no) ;
printf("%d", studentJl\ark[student_no]);

will ask the user for hislher student number - then display that student's mark.

e.g.6 for (student_no=O; student_no <=9; student_no++)


printf ("td", student_mark [student_no] ) ;

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] ;

/*Get wage details from user */


for (emp_number = 0; emp-number <= 4; emp_number++)
/*Get details for one employee*/
(
printf ("Key in wage for employee number %d\n" I

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

Space for such a table can be allocated as follows.

e.g.l int student.-mark[S] [3]

sets aside five lots of three spaces to hold integer numbers.

Similarly to store the grades:

e.g.2 char student_graderS] [3];

sets aside five lots of three spaces suitable for characters.

e.g.3 float monthly_wage [10] [12] ;

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)

while one defined as int mark[2][4] would be stored as:

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

To refer to a specific item in a two-dimensional array, the name of the array is


followed by two numbers (index numbers) - each one in separate square brackets
[ ][]. In the first array defined in the previous lesson (studencmark) , the first
index refers to the student and the second to the exam taken.

e.g.l printf ("%d" I student_mark [0] [2]) ;

displays the first student's third exam mark. (The indexing still starts with 0 on
both axes.)

e.g.2 scanf ("%d" I &student_mark[4] [1]);

accepts a 'number from the user and stores it as the fifth student's second exam
mark.

e.g.3 student_mark [0] [0] = 0;

sets the first student's first mark to O.

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

displays the third student's first mark.

e.g.5 printf ("Student number (0-4) ?\n ");


scanf ("%d" I &student);
p:t:intf ( "Exam number ? (0-2) \n" ) ;
scanf ("%d" I &exam);
printf ("The mark is %d\n",
student_mark [student] [exam]);

asks the user for details of the student and exam then displays that student's mark
for the particular exam.

e.g.6 for (student=O; student< =4 ; student++)


{
for (exam=O; exam<=2;exam++)
printf ("%d" student_mark[student] [exam]);
I

printf ( " \n") ;


}

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];

/*Get class grades from user and fill table*/


for (studel.t=O; student<=9; student++)
/*Get exam grades for one student*/
{
printf ("Student : %d\n" , student);
for (exam=O; exam<=2;exam++)
/*Get student's grades for one exam*/
{
printf ("Grade for Exam : %d ?", exam);
scanf (" %c", &student_grade [student] [exam] ) ;

/*Ask user which entry in table he/she wishes to


display*/
printf("Student number (0-9) ?\n");
scanf ("%d", &student) ;
printf ("Exam number? (0-2) \n") ;
scanf ( " %d", &exam) ;

/*Display grade required*/


printf ("The mark is %c\n",
student_grade [student] [exam]);
/*Finish*/
return 0 ;

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

We have already mentioned that a string is a single-dimensional array of


characters.

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:

An array of strings (e.g. a table or list of names) is in effect stored as a two-


dimensional array of characters.

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 .

So, from the above table:

e.g.l printf ("%s", student_name [0] );

will display "JULlA"

e.g.2 printf ("%s", student_name [2] );

will display "JONATHON"


e.g.3 scanf ( " %s", student_name [3] ) ;

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.

To refer to a single character, simply use both index numbers.


e.g.5 printf (II %c", student_name [0] [0] )

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*Get student names from user and fill table*1


for (student=O; student<=9; student++)
{
printf ("Name of student: %d ?\n ll , student);
scanf(" %s", student_name[student]);

I*Ask user which entry in table helshe wishes to


display*1
printf("Student number (0-9) ?\n");
scanf("%d " , &student);

I*Display name required*1


printf ( "The name is %s\n", student_name [student] );

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

We have already mentioned that a structure can be a useful method of organising


related data in Central Memory.

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

To define an array of structures, simply state the number of structures required in


the array. in square brackets directly after the data name for the structure.

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.

e.g .3. struct student


{char student_name[lOli
int eXaIl\Jtlarki
}i

The memory space for the array can then be set up later .

e.gA struct student student_details [10 I i


sets up an array called student-details type struct student - defined above.

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

Each structure in an array is known by the array name followed by a number in


brackets [ ] - e.g. studenrdetails]4J refers to fhe fifth set of details.

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.

e.g.I printf("%d", student_details [0] .exam.....mark) i

displays the first student's exam mark.

e.g.2 printf ("%s", student_details [2] . student_name) i

displays the third student's name.

e.g.3 scanE ( "%d", &student_details [0] . exam.....mark) i

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.

e.g.4 scanE ( "%s", student_details [1] . student_name) i


accepts a word from the keyboard and stores it as the second student's name.

e.g.S gets (student_details [1] . student_name) i

has the same effect.

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];

/*Get class details from user and fill table*/


for (student=O; student<=9; student++)
/*Get one student's details*/
(
printf("Name of student : %d ?\n", student);
scanf("%s", student_details[student].student_name);
printf ("Mark? \n");
scanf ("%d", &student_details [student] . student_mark) ;

/*Ask user which entry in table he/she wishes to


display*/
printf ("Student number (0-9) ?\n") ;
scanf ("%d", &student);

/*Display details required*/


printf (" %5" , student_details [student] . student_name) ;
printf ("%d\n ", student_details [student] . student_mark);

/*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

The computer's Central Memory is divided into millions of small. individual


storage locations. Each storage location is large enough to hold one byte of data -
that is equivalent to the space required to store the electronic code for a single
character (e.g. a letter such as 'A' or a single digit number such as '5') in a format
ready for displaying on screen or printing. Alternatively, a location could be used
to store a small whole number (up to 255) held in a compressed format (suitable
for arithmetic within the computer - but not suitable for printing or display on
screen without conversion). Note that printj used with %d converts a number
stored in an arhhmetlc format into text for display - whereas prim! used with 'foe
assumes that the value stored is a character code ready for display.

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.

Figure 2 - A s1TUJll section of memory for a machine with a word-length of two


bytes.

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

In a high level programming language - such as C - each area of memory


allocated for storing an item of variable data is given a name. So. a space needed
to store the character 'P' or 'F' to indicate a student's grade might be given the
name studentgrade, while the space needed to store the mark (an integer
number) might be called studentmark. (See Figure 1 below.)
Figure 1
/<student 9N&J /' -- < student mark I
It
I /'

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.

In C, it is sometimes useful to be able to refer to a variable by its address rather


than its name. This address can be obtained by placing & before the variable
name. (This has already been used in the scanf command - which always requires
the address of the variables where it must store the data.)
So. &studencmark gives the address of studentmark - which in the figure is
0102.

126
It is possible to store this address in another location - for later reference - but first
a suitable memory location must be allocated.

e.g.!. int *mark-pointer;


sets aside a space called markpointer which can later be used for holding the
address of an integer field. (See Figure 3 below.)

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

--f-~~"""'---"'-""'---+--""-'< mark pointer I

So far, the location called markpointer is empty, but the address of another
location can now be stored there:

e.g.2 mark-pointer = &student_mark;


places the address of studentmark in the memory location mark pointer.

So, markpointer would now contain 0102 (See Figure 4 below.)


Figure 4

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

To refer to the contents of a variable, it is possible simply to place • before the


name of the pointer variable which holds its address.

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,

e.g.l printf ("'d" I *mark-pointer) i

will have the same effect as

e.g.2 printf (·\d" I student_mark) i

Both statements will display the contents (8) of studentmark.

Also note that as nuuk.../loi"ter contains the address of struknCtIUU'k. it can be


used instead of&stude"'-ltUJrk (e.g. in a scanfstatement).
So,

e.g.3 scanf ("%d". mark-pointer) i

will have the same effect as

e.g.4 scanf ( II \d II I &studentJl\ark);

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.

e.g.5. printf (.\p" I mark-pointer);

will display the address stored in markpointer (i.e, the address of studencmark).

e.g.6. printf ("%p" I &student_mark);

will have the same effect.

Note that addresseswill generally be displayedin hexadecimal(SeeAppendix D).

128
Example Program

#include <stdio .h>

main()

{ /*Set up two memory locations :


1. student_mark - which is suitable for storing
an integer number
and
2. addrl - which is suitable for holding the
address of another memory location which in turn
can hold an integer .*/

irit student_mark, *addrl;

/*Place the address of student_mark in addr1*/


addrl = &student_mark;

/*Get mark from user and store it in student_mark*/


printf ("Please key in your mark");
scanf (" %d", addrl);

/*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.

e.g.l increment (&count) ;

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.

e.g .2 void increment (int *number...,ptr )


{/*Add 1 to the contents of the variable whose
address is held in number-ptr*/
(*number...,ptr)++;

ExampleProgram
iinclude <stdio.h>

void increment(int *);

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;
}

void increment(int *number...,ptr)


(
( *number...,ptr) ++;
}

130
3.19 Pointers to Structures

A variable can be set up to hold the address of a structure.

First, the structure type has to be defined,

e.g.l struct student_type


{char surname[20];
int mark;
};

sets up a pattern for a structure suitable for holding data on a student.

Then memory space can be allocated for the structure:.

e.g.2 struct student_type student_details;


sets up a memory space studentjdetails conforming to the pattern set by struct
studenttype.
A pointer variable can be defined to hold the address of a structure.

e.g.3 struct student_type *student-ptr;

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.

e.gA struct student_type


{char surname[20];
int mark;
} _student_details, *student-ptr;

Either way, the address of the structure studentdetails can then be assigned to the
pointer variable, studentptr.

e.g.5 student-ptr = &student_details;

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.

e.g .6 printf (" %d", student-ptr->mark);

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 ->.).

e.g.? printf ("%d", student_details . ma r k ) ;


achieves the same effect as e.g.6 but uses the structure name studem.iletails to
refer to the data directly rather than via a pointer.

e.g.8 scanf ("%d", &student...,ptr->mark);


Again, the variable is accessed via the pointer variable student-ptr rather than
directly. Note that & is used before the name of a pointer variable which
addresses a structure - to indicate that we wish to pass an address to scan!
(which always requires an address as a parameter).

The same effect can be achieved by referring to the structure directly by name:

e.g.9 scanf ( "%d", &student_details. mark) ;

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).

e.g.tO scanf ("%s ", student...,ptr->surname);

which has the same effect as:

e.g.ll scanf ( "%s" , student_details. surname) ;

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.

e.g.2 char * grade....ptr ;


defines a variable. grade-J1lr. suitable for holding the address of a character
variable (such as the address of an item in an array of char).

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.

When the ++ operator is used on an address stored in a pointer variable. it


increments the address to point to the next address for a variable of the relevant
type.

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--;

decrements gradeptr so that it contains the address of the previous element in


the array - in this case moving from the second item back to the first item (i.e.
grade{OJ).

The + operator will allow an address to be added to so that it points to an item a


given number past the present one.

If we are starting back at the beginning of the array - with grade..ptr holding the
address of grade{O/:

e.g.6 grade...,ptr = grade-ptr + 4;

(or grade...,ptr += 4;)

modifies the address so that grade..ptr now points to grade[4/.

The • operator will allow an address to be reduced so that it points to an item a


given number before the present one.

So, if we are starting with gradeptr holding the address of grader 4/:

e.g.7 grade...,ptr = grade...,ptr - 4;

(or grade...,ptr -= 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

Addresses of items in an array may be compared.

e.g.8 i f (ptrl == ptr2)


printf ("Same address");

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++;
}

The instructions in the braces will be repeated (including incrementing ptrl so


that it points to the next element in the array) until the address in ptrl is greater
than the address in ptr2 (which might be set to contain the address of the last
element in the array).

Subscripting Pointers

A pointer variable containing the address of an element of an array may be


subscripted (in the same way as the name of the array) to indicate another
element. So, if markptr is a variable containing the address of the beginning of
the array mark. then mark-ptrl5] will give the contents of the sixth element in the
same way as markiS] or *(mark-ptr + 5).
So,
e.g.lO scanf ("%d", &mark...,ptr[O));

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.

e.g.ll printf ( "%d", mark...,ptr [0] ) ;

will display the first element in the array.

Example Program]

tinclude <stdio.h>
main()
(int mark[lO);
int *mark...,ptr, *last_mark...,ptr ;

/*Assign address of beginning of array to mark-..J)tr*/


mark...,ptr = mark;
/*Assign address of end of array to laBt_mark-..J)tr*/
last_mark...,ptr = mark + 9;

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*Reset mark...,JJtr to contain address of beginning of


array*1
mark-ptr = mark;
I*Display list of marks*1
while (mark-ptr <= last_mark-ptr)
{
printt (" \n%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*Display list of marks*1


for (student = 0 ; student <= 9 ; student++)
{
printt (" \n%d", mark-ptr [student] ) ;
}

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

Whenever an array is specified as a parameter. it is always the address of the


array that is passed not the contents.

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 .

e.g.l display_marks (maths.....mark) ;


calls display_marks specifying the array name maths_mark as an argument -
which passes across the address of the beginning of the array .

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) ) ;

void display_marks(int mark[))


(int count;
for (count = 0 ; count <=9; count++)
printf ("%d\t%d\n", count, 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++);

void display_marks(int mark[))


(int count;
for (count = 0; count <=9; count++)
printf("%d\t%d\n", count, *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];

sets up an array capable of holding the addresses of five integer variables.

Of course, the memory spaces for the actual data have to be set up separately.

e.g.2 int maths_mark, english_mark, french_mark,


physics_mark, computing_mark ;

The addresses of each of these variables can then be assigned to the elements of
the array ptr_to-,no.rk.

e.g.3 ptr_to_mark[O] = &maths_mark;


ptr_to_mark[l] = &english_marki
etc

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,

e.gA printf( "%d" , *ptr_to_mark[O]) ;

(display the contents of the variable whose address is held in the first element of
the array ptr_to-,no.rk)

would then have the same effect as

e.g.5 printf ("%d" , maths_mark);

(display the contents of the variable maths_mnrk)

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] ;

sets up an array capable of holding the addresses of three character variables.


Each character might be the beginning of a message (i.e, three messages in all).

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:

e.g.S char *ptr_to..,msg [] =


{"Welcome to the Computing Course\n" ,
"Welcome to the Science Course\n",
"Welcome to the Language Course\n",
}

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 . .

Any of the messages can then be referred to by its address .

e.g.9 printf ("%s", ptr_to..,msg[O]);


will display the first message "Welcome to the Computing Course". (Note that
there is no need to use • when referring to a string - as they are always referred
to by their address.)

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",
};

/*Get course code from student*/


printf("Please key in your course code\n");
printf ("Key 0 for Computing\n") i
printf ("Key 1 for Science\n");
printf("Key 2 for Languages\n")i
scanf ( "%d", &course_code);
/*Display message welcoming student to appropriate
course*/
printf ( "%s", ptr_to..,msg [course_code] ) i

/*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.

Such a pointer to a pointer is defined by preceding the variable name by two


asterisks.

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).

Each of the variables can now have values assigned to them.

e.g.2 mark = 50;


ptr_to_mark = &marki
ptr_to-ptr_to_mark = &ptr_to_marki

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.

Iaddress 1--> Iaddress 1--> I 50


ptr_to..]Jtr_lo_mark ptr_to_mark mark

The value of mark can now be accessed directly by referring to the variable by
name:

e.g.3 printf ("!lid" I mark) ;


-
or indirectly by telling the computer to access the variable whose address is
contained in ptr_to_mark;

e.g.4 printf ("!lid " I *ptr_to_mark) ;

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

A pointer to a pointer may be useful to refer to an array of pointers.

e.g. I char *ptr_to_grades [] =


{ "Fail\n", "Pass\n", "Merit\n")i

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).

e.g.3 ptr_to_array = ptr_to_grades i

The situation is now as follows:

ptr_to_array

Iaddress ~> address -> F a i 1 \n \0

address - > P a s s \n \0

address - > M e r i t \n \0 I

If the array ptr_to~rades were passed as a parameter to a function - only the


address of that array (i.e. of the first element) would actually be passed. So the
function would need a suitable pointer variable defined as a formal parameter to
receive this address. As the address received would be of an array element which
itself contained an address (of the beginning of a character string), the formal
parameter would need to be defined as a pointer to pointer to char .

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 .

e.g.4 void functionl (char **array-ptr) i


(
Function instructions
)

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;

void display_countries (char *country_table-ptr[])


{int count;
for (count = 0; count <= 4; count++)
printf ("%s\n", country_table-ptr[count]) ;
)

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.

An alternative approach is to include commands in the program which will ask


the system for extra space as it is needed - while the program is running. This is
called dynamic memory allocation.

The malloc function is used to allocate extra space to a program while it is


running. When space is needed, the program can call malloc - asking for a certain
number of bytes ofmemory (e.g. malloc(4) requests 4 bytes of memory). If there
is a suitable free space , malloc will return the address - otherwise it will return
NULL to indicate that insufficient space is available. To be able to use malloe it
is necessary to #include the header file stdlib.h.
The address returned by malloc will be suitable for any sort of data, but it is
necessary to state what type of data will be stored in the new space. This is done
using a cast - e.g. (char *) - placed directly before malloe. The address can then
be stored in a suitable pointer variable.

e.g.l grade....,ptr = (char *) malloc (1) ;

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).

It is important to check whether malloc has been successful in finding a vacant


area of memory before attempting to store any data there I This can be done by
ensuring that the value returned by malloe was notNUU .
e.g.2. grade....,ptr = (char *) malloc (l) ;
if (grade....,ptr == NULL)
{
printf ( "No space available for data\n") i
exit(l);

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.

e.gA mark...,pointer = (int *) malloe (sizeof (int) ) i

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));

The free command is used to release an area of memory which is no longer


needed. Only space previously allocated by malloc (or an associated command)
can be released in this way and the same amount of space is released as was
originally allocated at that address.
e.g.6 free (student...,ptr) ;

releases the space whose address is held in studentptr.

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.

e.g.2 printf ("%c", *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.

e.gA . *mark...,pointer = 50;


assigns the value 50 to the memory location whose address is stored in
mark-pointer.
e.g.5 printf("%d", *mark...,pointer) ;

displays the contents of the memory space whose address is stored in


mark-pointer.

e.g.6 scanf ("%d", mark...,pointer);

allows the user to key in an integer then stores it in the memory space whose
address is held in mark-pointer.

e.g.7 student...,ptr->mark = 50;

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*Set grade to 'D'*I


*grade-ptr = 'D';
I*Display address where grade is stored and grade*1
printf("%p\t%c\n", grade-ptr, *grade-ptr);

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*Get student details from user*1


printf ( "\nSurname 1");
gets(student-ptr->surname);
printf("\nMark 1");
scanf ("%d", &student-ptr->mark);
I*Display student details*1
printf (" \n%s", student-ptr->surname);
printf (" \nMark: %d", student-ptr-> mark) ;

I*Finish*1
return 0;

148
3.27 Linked Lists

When an array will be used to store a number of data items in memory, it is


necessary to know from the outset the maximum number of data items that will
need to be stored - so that the right sized array can be defined. If the programmer
defines an array that is too small, it will cause problems as not all the data items
will be stored: if an array is allocated that is very much too large - then memory
space is wasted.

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.

e.g.l struct student_type


{char name [20] ;
int mark ;
struct student_type *next_student-ptr;
}

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.

e.g.3 first_student-ptr = NULL;

Adding an item

To set up a linked list consists of repeatedly adding an item to the list.

An item might be added:


a) at the beginning of the list;
b) somewhere within the list;
c) at the end of the list.

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", &current_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.

e,g.9 current_student-J;>tr->next_student-J;>tr = NULL;


jirs/Jludefll..,plr

~
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.

e.g.12 free (current_student-ptrl ;

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.

e.g .13 previous_student-ptr->next_student-ptr =


current_student-ptr->next_student-ptri
jirstJtudenl-ptr

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.

e.g.15 previous_student...,ptr = current_student...,ptr;

Example Program

#include <stdio .h>


#include <stdlib .h>
#include <string.h>
void add-student(void) ;
void remove_student(void);
void display_list(void);
void find...,place_for_insertion(void);
struct student_type
(char name[20];
int mark;
struct student_type *next_student-ptr;
);
struct student_type *first_student-ptr,
*current_student...,ptr, *previous_student...,ptr;
main()
{int choice;
/*Initialise first_student-ptr*/
first_student...,ptr = NULL;

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);

/*Get student details from user*/


printf("\nName ?");
scanf("%s" , current_student-ptr -> name);
printf("\nMark ?");
scanf("%d", &current_student-ptr -> mark);
/*Link student into list*/
if (first_student-ptr == NULL)
/*Add first student to list*/
{
current_student-ptr->next_student-ptr
first_student-ptr;
first_student-ptr = current_student-ptr;
}
else
/*Add student to others in list*/
(/*Search list for correct place to add student*/
find-p1ace_for_insertion();
if (previous_student-ptr == NULL)
/*Add new student at beginning of list*/
{
current_student-ptr->next_student-ptr =
first_student-ptr;
first_student-ptr = current_student-ptr;

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;

/*FUNCTION display_list displays full class list


stepping through one record at a time - offering the
user the opportunity to remove the record*/

void display_list(void)
{char option;

/*Start at beginning of list*/


eurrent_student-ptr = first_student-ptr;
while (eurrent_student-ptr !=NULL)
{
/*Display student's details·/
printf ("%s\n", eurrent_student-ptr->name);

/*Display menu and get selection*/


printf ("Carryon N\n") ;
printf("Delete D\n");

seanf (" %e", &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);
}

/"'FUNCTION fin~lace_for_insertion - returns with


previous_student-ptr holding address of previous
student before insertion place for new student; or NULL
if beginning of list ."'/
void find-Place_for_insertion()
{/*Start at beginning of list"'/
if (strcmp(current_student-ptr->name,
first_student-ptr->name) < 0)
/*New student lowest key in list*/
{
previous_student-ptr = NULL;
return;
}
else
{
previous_student-ptr = first_student-p t r;
while ((strcmp(current_student-ptr->name,
previous_student-ptr->next_student-ptr-> name) >0)
&& (previous_student-ptr-> next_student-ptr !=
NULL) )
/*Next student in list*/
(
previous_student-pt r =
previous_student-ptr->next_student-ptr;
)
return ;

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.

A pointer is maintained (position-ptr in the example) to show which item is being


processed at anyone time. (When no processing is taking place, it might be left
pointing to where processing left off last - e.g. the record for the last student
interviewed - or moved on to point to the next record to be processed - the
examples here take the former course of action.)
position-ptr

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).

Starting a new list in memory

As usual with linked lists a pattern for a suitable structure will be needed.

e.g.I struct student_type


{char name [15] ;
struct student_type *pointer_to_next_record;
};

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).

e.g.2 struct student_type *position-ptr = NULL;

A temporary pointer will also be needed for a variety of purposes, including


holding the address of new space allocated by malloc.

e.g.3 struct student_type *temp...,ptr;

Adding an item

This will involve requesting space for the new record.


e.g.4 if (!(temp...,ptr = (struct student_type *)
malloc(sizeof (struct student_type))))
error_routine();
gets space from the system to hold one student record - placing the address in
temp-ptr and if malloc is unable to find space carries out function errorJoutine
(which will have to be defined by the programmer - and which might - for
example - involve displaying an error message, saving the data to disk and
exiting).

If the list is currently empty (indicated by positionptr being equal to NULL) -


then posltionjptr will need to be set to the address of the new record and the
pointerjo-nextrecord within the structure should be set to the record's own
address.
e.g.5 position...,ptr = teInP...,ptr;
position...,ptr->pointer_to_next_record =
position...,ptr;
posit ion ptr

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;

The pointer_to_nexcrecord of the previous record in the list (whose address is


held in position-ptr) will have to be set to point to the new record.

e.g.7 position-J?tr->pointer_to_next_record = temp-ptr;


posilion-ptr

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).

e.g.8 posi tion-J?tr = temp-ptr;

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.

e.g.lO free (position....ptr) ;


position....ptr = 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 -

allowing the next record to be processed.


e.g.14 position-ptr =
position-ptr->pointer_to_next_record;

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.

Searching for an item

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.

e.g.16 struct student_type *seek(struct student_type


* start--ptr , char name_required[), char ind);
162
{struct student_type *item-ptr,
*previous_item-ptri
/*Set item-ptr to start 6f search*/
item-ptr = start-ptri
/*Carry out search*/
do
{/*Move to next record*/
previous_item-ptr = item-ptr i
item-p t r =
item-ptr->pointer_to_next_recordi
/*Check name against required name*/
if (!strcmp(name_required, item-ptr->name))
{/*Item found - return pointer*/
if (ind == 'C')
return(item-ptr)i
else
return(previous_item-ptr);
}
} while (item-ptr != temp-ptr);
/*Item not found* /
return(NULL) i
}

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;
} ;

struct student_type *position-ptr = NULL;


struct student_type *temp-ptr;
main()
{int choice;
do
{/*DisplayMenu and get choice*/
printf ( \nMENU\n" ) ;
II

printf(MAdd student to list l\n") ;


printf("Remove student from list 2\n") ;
printf("Display next student in list 3\n") ;
printf("Display required student 4\n") ;
printf ("Exit O\n ") ;
scanf("'d ", &choice);
/*Carry out chosen task*/
switch (choice)
(case 1: ad~student(); break;
case 2: remove_student(); break;
case 3: display_next_student(); break;
case 4: display_required_student(); break;
case 0: break;
default: printf("Error - please rekey\n");
}
} while (choice != 0);

/*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;

/*Get student details*/


printf ( \nKey in student s name");
II 0

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) ) )

printf("Student not in list\n");


return;

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')) )

printf("student not in list\n");


return;
}

/*Display Record*/
printf ("%s\n", temp-ptr->name);

struct student_type *seek(struct student_type


*start-ptr, char name_required[], char ind)
{struct student_type *item-ptr, *previous_item-ptr;
/*Set itemLPtr to start of search*/
item-ptr = start-ptr;
/*Carry out search*/
do
{/*Move to next record*/
previous_item-ptr = item-ptr;
item-ptr = item-ptr->pointer_to_next_record;
/*Check name against required name*/
if (!strcmp(name_required, item-ptr->name))
{/*Item found - return pointer*/
if (ind == 'C')
return(item-ptr);
else
return(previous_item-ptr);

} while (item-ptr != start-ptr);


/*Item not found*/
return (NULL) ;

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.

e.g.l struct student_type


{char name[20];
struct student_type *next_in_queue-ptr;
} ;-

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 .

e.g.2 struct student_type *front_of_c;rueue-ptr,


*end_of_queue-ptr ;

The pointer variables froncof-queue-ptr and end_of-queue-ptr will be able to


hold addresses of studenttype data structures.
A temporary pointer variable will also be useful.

e.g.3 struct student_type *temp-ptr;

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.

e.g.4 ternp-ptr = (struct student_type *)


malloc(sizeof(struct student_type) ;

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 .

e.g.5 printf("Please key in student's narne\n");


scanf ("%S", temp-ptr->narne);

If there is no student in the queue yet (which can be ascertained by checking


whether the front of queue pointer is NULL), then the front of queue pointer
should be set to point to the new student's details.

e.g.6 front_of_queue-ptr = ternp-ptr;


fronCof-queue-plr (New ilem)

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.

e.g.8 end_of_queue-ptr temp-ptr ;


jTont_of_queue-ptr emLo!-queue...ptr (New item)

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.

e.g.9 temp-ptr->next_in_queue-ptr = NULL;

~
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.

e.g.10 •if (front_of_queue-ptr == NULL)


{
printf ("No students in queue\n") ;
return;

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.

e.g.l3. if (temp~tr->next_in_queue~tr NULL) ==


front_of_queue~tr =
end_of_queue~tr = NULL;
else
front_of_queue~tr =
temp~tr->next_in_queue~tr;

IAnn

Finally, the space occupied by the removed item can befreed to the system .

e.g.14 free (temp~tr);

froncoLqueue~tr

170
Example Program

*include <stdio.h>
*include <stdlib.h>
struct student_type
(char name[20);
struct student_type *next_in_queue-ptr;
};

struct student_type *front_of_queue-ptr,


*en~of_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") ;

scanf ( "%d", &choice);


/*Process choice*!
if (choice == 1)
ad~student_to_queue();
else if (choice == 2)
deal_with_first_in_queue();
else if (choice 1= 0)
printf("Error\n");
} while (choice != 0);

!*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 ;

/*Get students details and store in newly allocated


space*!
printf("Please key in student's name\n");
scanf("%s", temp-ptr->name) ;

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 pattern will be needed for a structure capable of holding an item of data


together with left and right pointers .

e.g.l struct node


(char name (20] ;
struct node *left-ptr;
struct node *right-ptr;
) ;

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;

This will be accessed and modified by several functions - so it is probably easiest


to set this up as as a global variable.

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.

e.g .3 if {! {temp-ptr = (struct node *)


malloc(sizeof{struct node))))
(
printf{"Insufficient memory\n");
exit (1);

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.

e.g.S temp-ptr->left-ptr = temp-ptr->right-ptr = NULL;

Finally, the new node can be inserted in the tree .

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.

e.g.6 root-ptr = temp-ptr i

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---------

> ,.---,---,----,
>

> ,.---,----,----,
>

Finally, the memory space can be released.

e.g.8 free (current_node-ptrl i

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) .

e.g.9 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)i
/*Process current node*/
printf ("%s\n", current_node.....ptr->name) i

/*Process right branch*/


display_tree(current_node.....ptr->right.....ptr)i
}

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;
};

void add_item (void);


void find-place_and_insert(struct node *);
void display_tree(struct node *);
void remove_item(void);
struct node *root-ptr;
main()
{int choice;
!*Initialise root-ptr*!
root-ptr = NULL;
do
{/ *Display menu and get choice*!
printf ("\nMenu\n");
printf("Add item 1 \n") ;
printf("Remove item 2\n") ;
printf ( "Display tree 3 \n") ;
printf ( "Exi t O\n") ;
scanf( "%d", &choice);
!*Carry out choice*!
switch (choice)
(
case 1: ad~item() ; break;
case 2: remove_item(); break;
case 3 : disp1ay_tree(root-ptr); break;
case 0 : break;
default : printf("Error - please rekey\n");
} ;
while (choice != 0);

!*Finish*!
return 0;
}

void add_item (void)


{struct node *temp-ptr;
!*Get space*!
if (! (temp-ptr = (struct node *)
malloc(sizeof(struct node) I»~

printf ("Insufficient memory\n");

180
exit(l);

/*Get details and store in new node*/


printf ("Name\n?\n") ;
scanf("%s", temp-ptr->name);

/*Initialise left and right pointers of new node*/


temp-ptr->left-ptr = temp-ptr->right-ptr = NULL ;
/*Insert node in tree*/
fin~lace_an~insert(temp-ptr);
}

void fin~lace_an~insert(structnode *new_ite~tr)


{int inserted = 0;
struct node *current_node-ptr;

/*Insert first item in tree*/


if (root-ptr == NULL)
{
root-ptr = new_ite~tr;
return;

/*Insert any other item in tree*/


current_node-ptr = root-ptr;
do
{
if (strcmp(new_ite~tr->name,
current_node-ptr->name) <0)
{
if (current_node-ptr->left-ptr -- NULL)
/*Insert new item in tree*/
{
current_node-ptr->left-ptr = new_ite~tr ;
inserted = 1;
}
else
/*Search to left for position for new item*/
current_node-ptr =
current_node-ptr->left-ptr;
}
else
{
if (current_node-ptr->right-ptr -- NULL)
/*Insert new item in tree*/
{
current_node-ptr->right-ptr
inserted = 1;
}
else
/~'Search to right for position for new item*/
current_node-ptr =
current_node-ptr->right-ptr;
}
while (! inserted);

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;

!*Request key for node to remove*!


printf ("Name?") ;
scanf ("%s", name);
!*Find node to remove*!
current_node-ptr = root-ptrj
do
{ if (strcmp(name, current_node-ptr->name) -- 0)
/ *Node found */
found = 1;
else
if (strcmp(name, current_node-ptr->name) < 0)
{
if (current_node-ptr->left-ptr == NULL)
!*Item not in tree*!
{
printf ("Not found\n");
return;
)
else
!*Search to left for item*!
(
parent_node-ptr = current_node-ptr;
current_node-ptr =
current_node-ptr->left-ptr;
)
else
{
if (current_node-ptr->right-ptr NULL)

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;

/*Insert unwanted node's right branch elsewhere in


tree*/
if (current_node-ptr->right-ptr != NULL)
find-place_an~insert(current_node-ptr->
right-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.

A file might consist of a continuous stream of characters. A text editing program


- for example - might create such a file.

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.

A text stream consists (mostly) of readable text format characters - interspersed


with the machine representation of special characters such as \n .

Data written to a binary stream is copied from memory exactly as it is - with no


modification . So, for example. numbers that are held in a pure binary form ready
for arithmetic are copied to disk in the same form. When data is copied from file
to central memory, again there is no modification.

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 .

e.g.l FILE *student_file...,pointer;


sets aside a pointer variable studentJile...,pointer which will in due course be used
to point to some file information,

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.

e.g.2 struct student_type


(char surname[16];
int mark;
) student_details;
This sets up a space in memory called studentsietails to hold the details for one
student while they are typed in and ready to be copied to disk; once the first
student's data has been written to disk - the same space can be used for the next
student's data.

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.

e.g.3 student_file..,pointer = fopen( "student", "wb");


The/open function opens a disk file which will be known as "student" on the disk
directory. The "wb' indicates that this is a new binary file and that data will be
written to it The function also returns the address of some information about the
file. This address should be stored in the pointer variable previously set up (I
above) as it will be needed later.

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.

The fwrite command would need to be included in a loop together with


commands to accept data from the keyboard - so that each student's information
could be processed in tum. (See Example Program below)

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.

e.g.5 fclose (student_file-pointer) ;

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;

/*Open student file for writing binary stream of data*/


student_file-pointer = fopen( "student", "wb");

/*Process data for class of students*/


do
(/*Get data for one student*/
printf ( "\nSurname ? ) ;
scanfl"%s", student_details.surname);
printf ( "\nExam mark ?");
scanf I "%d · , &:student_details. mark) ;
/*Write student's details to disk*/
fwritel&:student_details, sizeoflstruct
student_type), 1,
student_file-pointer);
/*Check if user has finished*/
printfl"\nFinished? - IY/N)\n");
scanf I" %c", &:finished.-indicator);
while (finished_indicator == 'N');
/*Close student file*/
fcloselstudent_file-poi~ter);

/*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.

e.g. 1 FILE * student_file...pointer;

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.

e.g.2 struct student_type


{char student_surname[16];
int student_mark
} student_details;
3. Ask the operating system to prepare a disk file (which must already exist
on disk) for reading . This process is known as opening the file and involves the
system in getting some information about the file from the disk and passing part
of this data across to the C program.

e.g.3 student_file"'pointer = fopen( "student" , "rb");


The fopen function opens the disk file known as "student" on the disk directory.
The "rb" indicates that this is a binary file and that data will be read from it.
Again, the function also returns the address of information about the file for the
program to use - and, as before, this address should be stored in the file pointer
variable.

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.)

The normal sequence is:

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.

e.g.5 /* (Attempt to) read first record from disk*/


fread(&student_details, sizeof(struct
student_type), 1,
student_file-pointer) ;
while(!feof(student_file-pointer))
{
/*Process one student record */

/*(Atternpt to) read next record*/


fread(&student_details, sizeof(struct
student_type), 1,
student_file-pointer);
)

5. Finally, the file should be closed.


e.g.6 fclose (student_file-pointer) ;

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*Open student file for reading (bina~ stream of


data) *1
student_file-pointer = fopen( "student", "rb");
I*Read first student's record from disk*1
fread(&student_details, sizeof(struct student_type),
1, student_file-pointer);
I*Process data for class of students*1
while(!feof(student_file-pointer))
{
I*Display report tor student*1
printf("\n%s\n", student_details.surname);
printf ("Mark : %d\n", student_details. mark) ;
I*Read next student's record from disk*1
fread(&student_details, sizeof(struct
student_type), I,
student_file-pointer) ;
)

I*Close student tile*1


fclose(student_file-pointer);

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.

1. Set aside memory space to hold the address of file information.

e.g.I FILE *student_text_file-pointer ;

2. Set aside working space(s) to hold data items temporarily while they are
waiting to be written to disk.

e.g .2 char student_surname [16] ;


int student_mark;

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.

3. Ask the operating system to open an empty disk me.

e.g.3 s'tudent_text_file-pointer = fopen( "studtext", "w");

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.

e.g.4 fprintf (student_text_file-pointer, "%s %d",


student_surname, student_mark);

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.

5. Finally, the me should be closed.

e.g.5 fclose (student_text_file-pointer) ;

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*Open student file for writing text*1


student_text_file-pointer = fopen ( "studtext " , "w" ) ;
I*Process data for class of students*1
do
(1*Get data for one student from user*1
printf("\nSurname 1");
scanf ("%s", student_surname) j
printf ( "\nExam mark ? ");
scanf("%d", &student~rk);

I*Write student's details to disk*1


fprintf(student_text_file-pointer, "%s %d",
student_surname, student-mark);

I*Check if user has finished*1


printf("\nFinished? - (Y/N)\n");
scanf (" %c" , &finishe~indicator);
}while (finished_indicator == 'N') ;

I*Close student file*1


fclose(student_text_file-pointer);

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.

I, Set aside a pointer variable to hold the address of file information .

e.g.l FILE *student_text_file-pointeri

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.

e.g.2 char student_surname [16] i


int stud~nt_marki

3. Ask the operating system to open the disk file for reading .

e.g.3 student_text_file-pointer = fopen( "studtext", "r ") i


The jopen function opens the disk file known as "studtext" on the disk directory.
The "r" indicates that this is an old text file and that data will be read from it.

4. Data can then be read (i.e. copied) from the disk file to central memory.

e.gA fscanf (student_text_file-pointer, "%s %d" ,


student_surname, &student_mark} i

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 .

As with previous file reading commnds, jscanj 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 tum . (See Example
Program below.)

Thejscanjcommand returns an integer end-of-file value - known as EOF (which


is defined in the stdio.h header file - normally as -1), if it attempts to read from a
file and instead finds the end-of-file marker. To check if the end of the file has
been reached the value returned by fscanf can be copied to an integer variable and
checked each time the processing is repeated.

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)*/

/*Read next student's details*/


e_o_f_ind =
fscanf (student_text_file...pointer, "%s %d",
student_surname , &student_mark);

5. Finally, the file should be closed.

e.g.6 fclose (student_text_file...pointer) ;

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");

/*Process data for class of students*/


/*Read first student's details from disk*/
e_o_f_ind = fscanf(student_text_file-pointer, "%s
%d", student_surname, &student_markl;
while (e_o_f_ind != EOFI
{/*Display student's details on screen*/
printf ("%s", student_surname);
printf (" %d\n", student-mark);

/*Read data for next student from disk*/


e_o_f_ind = fscanf(student_text_file-pointer, "%s
%d", student_surname,
&student_markl ;

/*Close student file*/


fclose(student_text_file-pointerl;

/*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).

The same steps have to be followed as in previous lessons on files .

l. Define ajile pointer variable .

e.g.l FILE *poel'll.-file-pointer;

2. Set aside working space to hold a character temporarily while it is waiting


to be written to disk.

e.g .2 char letter;


sets aside a memory space called letter to hold one character ready to write to the
file.

3. Open an empty file. This may be a text or a binary file - depending on its
purpose.

e.g.3 poeI1Lfile-pointer = fopen ( "poem", "w");

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.

4. Data can then be written to the file - one byte at a time.

e.g.4 putc (letter, poel'll.-file....pointer);

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.)

5. Finally, the file should be closed.

e.g.5 fclose (poel'll.-file-pointer) ;

196
Example Program
iinclude <stdio.h>

main ( )
(
/*file pointer variable*/
FILE *poe~file-pointer;

/*space to store character keyed in and waiting to be


written to file*/
char letter;

/*Display instructions for the user*/


printf (" \nPlease key in a poem\n");
printf ("Type * to finish\n\n ");

/*Qpen text file for writing*/


poe~file-pointer = fopen ( "poem" I "w" ) ;

/*Process text of poem*/


do
(/*Get character typed by user*/
letter = getchar();
/*Write character to disk file*/
putc(letter, poem-file-pointer);

} while (letter != ' * ' ) ;

/*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);

while (letter != EOF)


{/*Process character*/

/*Read next 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.

5. Finally, the file should be closed.

e.g.6 fclose (poellLfile....,pointer) ;

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;

/*D.Pen text file for reading*/


poellLfile....,pointer = fopen( "poem" "r"); I

/*Process text of poem*/


/*Get character from file*/
letter = getc(poellLfile....,pointer) i
while (letter != EOF)
(
/*Display character on screen*/
putchar(letter) ;
/*Get character from file*/
letter = getc(poellLfile....,pointer) ;
}

/*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.

The fseek command must specify:


I. the name of file pointer variable;
2. the required position in the me - made up of:
a) a displacement (in bytes);
and
b) the place from which the displacement should be measured - which
will be one of:
i) the beginning of the file - denoted by SEEK_SET;
ii) the current position - denoted by SEEK_CUR;
iii) the end of the file - denoted by SEEK_END.

e.g.I , fseeklstudent_file-pointer, 99, SEEK_SET)

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.

e.g.2 fseek I student_file-pointer, 1 * sizeof I struct


student_type), SEEK_SET);

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.)

e.g.3 fseek Istudent_file-pointer, student_number *


sizeoflstruct student_type), SEEK-SET);

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.

e.gA fseeklstudent_file-pointer, 999 * sizeoflstruct


student_type), SEEK_SET);
fwritel&student_details, sizeoflstruct
student_type), I, student_file-pointer);

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).

e.g .5 fseek (student_file-pointer, 999 * sizeof (struct


student_type), SEEK_SET} j
fread(&student_details, sizeof(struct
student_type}, 1 , student_file-pointer}j

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.

e.g .6 /*Find position and read record* /


fseek(student_file-pointer, student_number *
sizeof(struct student_type}, SEEK-SET};
fread(&student_details, sizeof(struct
student_type}, 1, student_file-pointer};

/*Display record and get rnoditication* /


printf ("%s\ t", student_details. surname) ;
printf("%d\n", student_details.mark;
printf (" \nNew Mark ?");
scanf ( "%d", &student_details.mark);

/*Find position again and write changed record* /


fseek(student_file-pointer, student_number *
sizeof(struct student_type}, SEEK-SET};
fwrite(&student_details, sizeof(struct
student_type}, 1, student_file-pointer};

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};

reads the first byte from the file.

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;

/*Create a new student file for writing*/


student_file-pointer = fopen( "BACLASS", "wh");
/*Get and save details for group of students*/
do
{/*Get student details*/
printf ( "\nStudent Number 1");
scanf("%d", &student_details.iCLnumber);
printf (" \nSurname") ;
scanf("%s', student_details.surname);
printf ("First name 1");
scanf("%s', student_details.firstname);
printf ("Mark 1");
scanf ("%d", &student_details . ma r k ) ;
/*Set file position and write record*/
fseek(student_file-pointer,
student_details.id.....number * sizeof(struct
student_type), SEE~SET);
fwrite(&student_details, sizeof(struct
student_type), 1, student_file-pointer);
/*Check if there is any more student data to enter*/
printf("Any more students ?\n");
scanf(" lc", &choice);
) while (choice 1= 'N');
/*Close student file*/
fclose(student_file-pointer);

/*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.

In practice, there is always the possibility of a run-time error occurring. For


example, an attempt might be made to create a file or write data to it, when there
is no space left on disk. Alternatively, a program might try to open and read from
a file which does not exist. Problems can occur if part of the disk has been
damaged, or a file corrupted in some way.

It is important to build error-checking into a program, and warning messages


should be displayed if a file-handling error occurs - so that the user will know that
the data has not been saved or correctly read. It might then be possible to sort out
the problem and avoid losing valuable work.

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.

e.g.l student_file...,pointer = fopen( "student", "wb") ;


if (student_file...,pointer == NULL)
( ,

printf (" \nUnable to open file\n");


exit (1) ;

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.

As usual in C, the instructions can be condensed.

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.

e.g.3 i f (fclose (student_file~ointer) ! = 0)


(
printf("\nError closing file\n H);
exit(l);
}

If an error occurs, a message is displayed and the program terminates.

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.

e.g.4 if (( fwrite (&student_details, sizeof (struct


student_type), I, student_file~ointer)) < 1)
printf(HError in writing to file\n")i
exit (1) ;

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) .

e.g.5 if (( fread(&student_details, sizeof (struct


student_typel, I, student_file~ointer)) < 1)
if (ferror(student_file~ointer))
{
printf ("Error in reading file\n") i
exit(l) ;
)

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.

e.g.6 fread(&student_details, sizeof(struct


student_type), 1, student_file-pointer);
if (ferror(student_file-pointer))
{
printf ("Error in reading file\n");
exit (1);

The /printf command, if successful, returns the number of characters written. If


there is an error, it returns a negative value. So, to ensure that the data has been
written, the program should check that the return value is not negative.
e.g.7 if (( fprintf (student_text_file-pointer, "%s %d",
student_surname, student~ark)) < 0)
(
printf("Error in writing to file\n") ;
exit(l);

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.

e.g.8 if (( fscanff (student_text_file-pointer, "%s %d",


student_surname, &student_rnark)) == EOF)
{
if (ferror(student_text_file-pointer))
{
printf ("Error in reading file\n");
exit(l);
)

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.

e.g.lO if (putc (letter, poern......file-pointer) == EOF)


{
printf ("Error in writing to file\n");
exit(l);

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.

e.g.ll i f (( letter = getc (poellLfile-pointer)) == EOF)


{
if (ferror(poern......file-pointer))
{printf ("Error in reading from file\n");
exit(l) ;
}

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.

e.g.12 if (fseek(staff_file-ptr. required_byte,


SEEK_SET) )

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;

/*Open student file for writing binary stream of data*/


if «student_file-pointer = fopen("student", Itwb lt))
== NULL)
(
printf(lt\nUnable to open file\n lt);
exit (1) ;
} ;

/*Process data for class of students*/


do
{/*Get data for one student*/
printf ( "\nSurname ?");
scanf ( "%s", student_details. surname) ;
printf ( "\nExam mark ?") ;
scanf ( "%d", &student_details . mar k ) ;
/*Write student's details to disk*/
if «fwrite(&student_details, sizeof(struct
student_typel, 1, student_file-pointer)) < 1)
{
printf ("Error in writing to file\n");
exit(l);
}

/*Check if user has finished*/


printf ( "\nFinished ? - (YIN) \n H) ;
scanf(" %c" , &finished_indicator) ;
while (finishe~indicator == 'N');
/*Close student file*/
if (fclose(student_file-pointer) != 0)
(
printf (" \nError closing fi1e\n");
exit(l);
};

/*Finish*/
return 0 ;
}

209
Example Program 2

*include <stdio .h>


*include <stdlib.h>
main()
{
/*space for address of file information */
FILE *student_file-pointer;
/*space to store a student's details temporarily after
being read from disk */
struct student_type
{char surname[16];
int mark;
} student_details;
/*Open student file for reading (binary file)*/
if ((student_file-pointer =
fopen( "student", "rb")) == NULL)
printf (" \nUnable to open file\n");
exit (1) ;
};

/*Process data for class of students*/


/*Read first student's record from disk*/
if ((fread(&student_details, sizeof(struct
student_type), l,student_file-pointer»<I)
if (ferror(student_file-pointer»
(
printf( "Error in readingfile\n");
exit (1) ;

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

A program module may call functions belonging to another module - provided


that a declaration (similar to a prototype) is included in the module which calls
the function .

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

/*Call function to draw appropriate shape*/


if (choice == 'S')
display_square()j
else if (choice == 'T')
display_triangle();
else
printf ( "Error\n II ) ;

/*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:

1. compile the separate modules;


2. link them.
They can then be run and tested.

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:

cc -0 display shapesl.c shapes2.c

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).

Alternatively, the two files could be compiled separately.

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.

The two objectfiles can then be linked with the command:

cc -0 display shapesl,o shapes2.o

resulting again in a usable program called display.

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.

e.g. extern int studentJ1larki


indicates that an integer variable studentmark has been defined (and therefore
allocated memory space) somewhere in the program.

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).

Again, as Module I contains a call to function], it must contain a declaration of


this function.

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);

/*Define (set up storage space for) global


variables*/
char student_name[20);
int student_mark;

/*Main Program*/
main ( )
(
get_student_details();
display_report();
return 0;

Module 2
finclude <stdio.h>

/*Declare variables used by this module that have


been defined elsewhere*/
extern char student_name[20);
extern int student-mark;

void get_student_details(void)
{
printf ( \nKey in name ");
II

gets(student_name);

printf ( \nKey in mark ");


II

scanf ( %d", &s tudent_mark) ;


II

Module 3
iinclude <stdio.h>

/*Declare variables used by this module that have


been defined elsewhere/
extern char student_name[20);
extern int student_mark;

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" ,
"

"five", "six", "seven", "eight", "nine");


/*Function toNumerals receives a number in verbal
form and returns the numeral */
int toNumerals(char nllmLin_words[])
{int count;
for (count = Oi count <= 9; count++)
(
if (strcmp(nllmLin_words,number_table[count])
== 0)
break ;
)
return (count) ;
)

/*Function toWords receives a numeral and returns the


number in words*/
char *toWords(int num)
(
return(number_table[num))i
)

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.

Creating object libraries and addingfunctions to existing libraries


1. Create a separate C source file for each function (or group of functions)
which is to be added to a library.
2. Compile each source file to create an object (i.e. machine code) file.
3. Use the librarian program for the system to add the object files to an
existing library file - or to create a new library file made up of the new
functions .

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

This would create files called square.o and triangle.o.

3. Use the librarian program ar to create a new library made up of these


object files:

ar rv libshapes.a square.o triangle.o


This creates a new library called libshapes.a - made up of the files square.o and
triangle.o, (The r option tells the librarian to add the files to the library - but
replacing any that already exist of the same name. The v tells ar to display each
filename as it is processed. Libraryfilenames commonlystart with the letters 'lib'
and finish with the .a extension.)
An object file may be added to an existing library using the same command - so
the file diamond.o containing function display_diamond could be added to library
libshapes.a .
ar rv libshapes.a diamond.o
This command would also allow a modified version of diamond.o to be used to
replace the old version in the library.

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.

Using functions from a library

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.

Example Header File - shapes.h

I
void display_square(void) ;
void display_triangle(void);

So, if a program is written to draw a square by calling function draw_square from


libshapes.a, this function is made available by:
I. #including the header file shapes.h in the program and
2. linking the program with the library libshapes.a (which contains the file
square.o which in tum contains the machine code version of the function
display_square). Note that the linker only selects the object files that are
actually neededfrom the library - so in this case triangle.o (from the same
library) which contains display_triangle would not be linked.

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 -0 draw draw.c libshapes.a


would result in draw.c being compiled into the object file draw.o - which would
then be linked automatically with the required library libshapes.a to produce (as
specified after the -0 option) an executable program called draw.

Alternatively, the program can be compiled and then linked separately.

cc -cdraw.c

will produce an object file called draw.o which can then be linked to the library
with the command:

cc -0 draw draw.o libshapes.a

to produce an executable program called draw.

Documentation

It is important to document a libary - so that anyone using it will know the


functions available - what they will do, what parameters will be expected and
what will be returned and the relevant header file name(s).

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

The #define directive can be used to give a name to an instruction or a group of


instructions. A single - made-up - command word standing in for other
instructions, which will replace it when the program is compiled, is called a
macro.

e.g.I ide fine DISPLAY_GREETING printf ("Hello\n")

defines a macro called DISPLAY_GREETING.

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").

A whole group of instructions may be given a name. Where a definition takes up


several lines, the backslash character (\) is used at the end of each line of the
definition (except the last) to indicate that the definition continues on the next
line.

e.g.2 idefine DRAW_SQUARE printf( "* * * *\n"); \


printf("* * * *\n"); \
printf("* * * *\n"); \
printf("* * * *\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).

e.g.3 ide fine DRAW_LINE \


{int count; \
for (count = 1; count <= 4; count++) \
printf ( ,,* * * *"); \
)

As can be seen, a macro can often be used instead of a function.

However, when a function is used, a single copy of the function instructions is


stored in memory when the program is running. Control of the computer is
passed to the function when it is required to carry out a task - and back to the
main program (or whichever other function called it) when it is has done its work
- until it is needed again.

In the case of a macro, a copy of the set of instructions is stored in memory at


every point in the program that the macro name was mentioned.

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.

e.g.l idefine ADDUP(ans, x , y) ans =x + y

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 .

Alternatively, variables could be used for all the arguments.

e.g.3 ADDUP (total_mark, maths.JT\ark, engllsh.JT\ark)

Before compilation. the preprocessor will convert this line to - total mark =
maths_mark + english_mark;

Using local variables within a block can be useful in a macro definition.

e.gA #define DRAW_LINE (length) \


{lnt count; \
\
for (count = 1; count <= length; count ++) \
printf("*"); \

So,

e.g.5 DRAW_LINE ( 5 ) ;

will now draw a line of five asterisks.

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

iinclude <stdio .h>

idefine AVERAGE (average_mark, markl, mark2) \


average-mark = (markl+ mark2}/2

main ( )
{char student_name(20];
int maths_mark, maths_ex~mark,
maths_coursework-mark,
english-mark, english_ex~mark,
english_coursework-mark,
overall-mark;

/*Get student details*/


printf ( " \nName ?");
gets(student_name};
printf (" \Mark for maths coursework ?");
scanf ("%d", &maths_coursework_mark);
printf ( "\Mark for maths exam ?");
scanf (" %d", &maths_exaIl\Jl\ark) ;
printf("\Mark for English coursework ?"};
scanf ("%d", &english_coursework_mark);
printf ( "\Mark for English exam?");
scanf ("%d", &english_ex~mark);

/*Calculate student's average marks for maths, english


and overall*/
AVERAGE (maths_mark, maths_coursework_mark,
maths_ex~ark};
AVERAGE (english_mark, english_coursework_mark,
english_ex~mark};
AVERAGE (overall-mark,
english~ark, maths_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:

e.g.I typedef int whole_number;

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,

e.g .2 whole_number numl, num2, answer ;

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.

For example, if a structure type is set up:

e.g.3 struct customer_type


{
char surname[15];
char first_name[15];
char address[60] ;
float current_balance;
} ;

It can then be given a typedef - to avoid repeatedly referring to it as struct


customer.;type.
e.g.4 typedef struct customer_type customer;
allows the type name customer to be used in definitions as an alternative to using
struct customer_type.
So,

e.g.5 customer customer_record;


and

e.g .6 struct customer_type customer_record;

are equivalent - both set up a memory space called customerJecord which


conforms to the type set up in e.g.S,

228
A typedef for a pointer type may be set up.

e.g.? typedef char *char...,ptr ;

gives the name char-ptr to the type pointer to char.


The name char-ptr may then be used to set up a pointer variable.
e.g.8 char...,ptr ptr_to_grade;

is equivalent to
e.g.9 char *ptr_to_grade;

in allocating a space called ptr_to-8rade suitable to store the address of a


character variable.

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)

sets up a type called ptr_to_voidJuncCno-params which can then be used to set


up actual function pointer variables.

e.g.l I ptr_to_voiCLfunction_no...,params shape_funct...,ptr;


sets up a pointer called shapefunctptr which can be used to hold the address of
the start of any function with no parameters and no return value.

To set up any Iypedef:


1. decide on a name for the type;
2. place it in a definition as if you were setting up an actual variable of that
name;
3. precede the definition with the word typedef.
The type name can then be used • followed only by simple datanames to set up
variables.
The decision as to whether or not to use typedefs must be taken in relation to
whether they will make the program clearer to follow or not. Complex types
given simple type names may be helpful (and make building up complex data
types easier), but against this must be weighed the question of whether new
names for types will be helpful or otherwise to another programmer modifying
software.

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.

e.g.l (mark >= 50) ? 'P': 'F;

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).

The value of the expression can then be assigned to a variable.

e.g.2 grade = (mark >= 50) ? 'P': '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 value of a conditional expression may be displayed by a printf command.

e.g.3 printf ("Your best mark was td" I (englishJllark >


maths_mark) ? englishJllark: mathsJllark);

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.

e.gA int calc_bonus (int yrs_of_service)


(
return«yrs_of_service >= 5) ? 200:100);
}

The function calc_bonus returns the value of the bonus (200 or 1(0) on the basis
of the number of years' service.

Functions may be used within a conditional expression.

e.g.5 (choice == 'S') ? display_square()


display_triangle();

If choice contains'S' then display_square will be carried out; otherwise


display_triangle. The expression will take the return value (if any) from the
selected function .

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.

e.g.2 union eXarl\....result_type


{int percentage;
char grade ;
};

sets up a template for a union calIed examJesulCtype.

This template can then be used to set up memory spaces conforming to this type.

e.g.3 union eXarl\....result_type rnaths_result,


english_result ;
allocates memory spaces called mathsresult and englishJesult. both of which
have the same structure.

As with structures. a union type and memory spaces can be set up at the same
time.

e.gA union eXarl\....result_type


{int percentage ;
char grade ;
} rnaths_result, english_result;

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

iinclude <stdio .h>


main ( )
{/*Allocate space for salary grade or salary*/
union
{int amount ;
char grade ;
} salary ;
int choice;
/*Get choice from personnel officer of whether he/she
wishes to key in amount or grade*/
printf ("Grade (Key 1) or Amount (Key 2)) ") ;
scanf(" %d", &choice);
/*Get amount or grade and store in memory*/
if (choice == 1)
{
printf("\nPlease key in Grade for employee " );
scanf ( " %c " , &salary. grade) ;
}
else if (choice == 2)
(
printf( "\nPlease key in Salary for employee ");
scanf (" %d ", &salary . amount) ;
}
else
{
printf(IError\n ");
return(l);

/ *Di s p l ay salary amount or grade*/


if (choice == 1)
printf("Grade is %c\n" , salary.grade) ;
else
printf("Sala r y i s %d\n ", salary.amount) ;

/*Finish* /
return 0;

232
5.7 Unions and Structures

A union may be part of a structure - and vice versa.

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

It can be advantageous to specify a variable as a register variable - if it is


accessed repeatedly (thousands or millions of times).

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.

e.g . register int count ;


allocates a register (if available) to hold the variable count.

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.

e.g.2 canst int exam.....grades[] = {50, 65, 85}

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.

e.g.3 void display_course_name (canst course_name [] )


{
Function Instructions

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).

e.g.4 volatile int machine_status;

sets up a memory space called machinestatus which can be used by a computer-


controlled machine to send information back to the program (or by the program to
leave data for the machine).

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

So, the value of x will now be 1.

e.g .2 int num1;


printf ( "Please key in a number\n");
scanf("%d", &numl);
numl = numl & 15;
printf("%d", numl);

Whatever number is keyed in - all the bits except the last four - will be reset to 0
(i.e. switched OFF).

Suppose the user keys in 24.

The variable numl will contain the binary pattern: 0000000000011000


This will be tested against the mask: 0000000000001111

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).

So, the result will be: 0000000000001000


So, the value of numl will be changed to 8.

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.

e.g.2 int num1;


printf(IIPlease key in a number\n") ;
scanf("%d", &num1);
numl = num1 I 1;
printf ("%d" num1);
I

=
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.

Suppose the user keys in 2.

The variable numl will contain the binary pattern: 0000000000000010


This will be tested against the mask: 0000000000000001

Wherever a bit in the variable OR in the mask is 1 - the result will also be 1.

So, the result will be: 0000000000000011


So , the value of numl will be changed to 3.

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.

Using the example of studencstatus_byte again - suppose it becomes compulsory


for every student to take the course represented by bit 4 (i.e, the fifth bit from the
right) - this can easily be set for those students who are not already on the course
by the course - by ORing against the mask 00010000 (i.e 16).

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).

e.g.l int x = 'l ;


x = x " 1j
The memory location x starts off containing the pattern: 0000000000000111

This would be checked against the binary pattern for I : 0000000000000001

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.

e.g.2 int nurn1 j


printf("Please key in a nurnber\n")j
scanf ("%d", &num1) j
nurn1 = nurn1 " 15j
printf (" %d", numll j
The command numl = numl 1\ 15 will cause the status of the rightmost four bits
of the number keyed in to be reversed.

Suppose the user keys in 4.

The variable numl will contain the binary pattern: 0000000000000100


This will be tested against the mask: 0000000000001111

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.

So, the result will be: 0000000000001011

So, the value of numl will be changed to 11.

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.

Again, using the example of studencstatus_byte - let us assume that bits 0, I, 2


and 3 represent single-semester courses that students must study in either their
first or second semester - while bits 4, 5, 6 and 7 are for optional courses that may
be studied over both semesters where appropriate. So, we want to ensure that
students in their second semester at university are automatically enrolled for all
the courses represented by bits 0-3 that they have not yet studied, but do not
repeat any of them. This can be achieved by XORing the status byte against the
mask 00001111 (i.e. 15) - as this will reverse all the bits in the lower half of the
byte but not affect the bits in the upper half.

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

After the - operation, it will contain: 1111111111110000


So, the value of x will now depend on whether it is signed or unsigned. If it is
signed, the value will depend on the method for holding the sign; assuming it is
two's complement, then x will be -16. If x had been declared as unsigned its value
would equal 65520. Note that - regardless of how x is declared - the
interpretation of its contents on display will depend on whether %d (interpret as
signed integer) or %u (interpret as unsigned integer) is used with print/.

e.g.2 int nwnl;


printf ("Please key in a nurnber\n");
scanf ( %d
II numl);
II ,

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.

e.g.l int x = 15;


x = x« 2;

The variable x starts off with the binary pattern for 15: 0000000000001111

After the left shift (2 places), the pattern will be: 0000000000111100

So, the value of x will be 60.

~g2 int x = -16


x = x « 2;
The variable x starts off with the binary pattern for -16: 1111111111110000
(assuming two's complement notation)

After the left shift (2 places), the pattern will be: 1111111111000000

So, the value of x is -64.

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.

e.g.l int x = 255;


x = x» 2;

The variable x starts off with the binary pattern for 255: 0000000011111111

After the right shift (2 places), the pattern will be: 0000000000111111

So, the value of x will be 63.

e.g.2 int x = -16


x = x » 2;

The variable x starts off with the binary pattern for -16: 1111111111110000

On systems where an arithmetic shift takes place:


after the right shift (2 places), the pattern will be: 1111111111111100

So, the value of x will be -4.

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

So, the value of x will be 16380.


243
Exercises
1. What would be the results of applying:
i) &
ii) I
iii) /I

to each of the following pairs of binary patterns:


a) 1111000011110000 0000111100001111
b) 0000010101010101 1111000011110000
c) 0000111000111000 0000000000011111
d) 0000000000110011 0000000000010000
2. What would be the results of applying:
i)
ii) «4
iii) »4
to each of the following binary patterns (assume the variable is an
unsigned integer):
a). 0000000000000011 b) 0000000011111111
c) 1010101010101010 d) 0011111111111100
3. An exam consists of eight questions - to which the candidate is expected
to reply True or False (keying the answers into a computer). A student's
answers are to be stored as bits in a one_byte (i.e, character) variable
called answers.
a) Write a command which will set the bits to the student's answers -
which are: Ql - True; Q2 - False; Q3 - True; Q4 - True; QS -
False; Q6 - True; Q7 - True; Q8- False.
b) Write a command which will change the student's answer to QS to
True.
c) The student decides he/she has answered the last four questions
incorrectly. Write a command which will change all four answers.
d) A teacher suggests that all the answers are wrong. Write a
command which will change all the answers.
e) One student typed in the answers to all the questions in the wrong
place - the answer meant for Ql was typed as the answer to Q3, Q2
was typed as Q4 and so on, and Q7 and Q8 were missed out.
Write a command which will move the answers to the correct
position and another command to allow the student's answers for
Q7 and Q8 to be stored (both True).
f) Another student left out Q1 and typed in the answer meant for Q2
as Ql, the answer for Q3 as Q2 and so on. Write a command to
move all the answers to the correct position. Write another
command to set the answer to Q1 to True.
3. A single byte signal to be sent to a factory machine is held in the variable
machine status. The bits are used as follows, from left to right: Bits 7 and
6 - Not used; Bit 5 - Switch machine ON (1) or OFF(O); Bit 4 - (if ON) Set
production speed to 50 items per minute; Bit 3 - (if ON) Set production
speed to 30 items per minute; Bit 2 (if ON) Set production speed to 10
items per minute; Bit 1 - Set to manual operation.
Sct the bits in machinestatus to switch the machine on and set it to
maximum speed, automatic operation.

244
5.11 Defining Bit-fields

An alternative way to handle bits is to set up bit-fields. These are individual


named fields - consisting of one bit or a small group of bits.

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 (:).

For example, part of a byte may be used to hold information to send to a


computer-controlled machine.

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.)

A student's grade in a course module may be represented by a two-bit binary


= =
code : 11 Distinction, 10 Merit, 01 = Pass and 00 = Fail.

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) .

Memory space for a structure of bit-fields might be allocated differently on


different systems . The bits on one system may be set up from high to low - while
on another they are set up from low to high.

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

A number of standard commands are available which checks the type of a


character - for example whether or not it is alphabetic. whether upper or lower
case and so on. To use these commands the ctype.b should be #included in the
program .

Each of the commands returns a non-zero value if the result of the check is
positive and zero if negative.

isalpha - checks whether a character is a letter of the alphabet.

e.g.l i f ( isalpha (ch 11


printf ( " %c is alphabe tic \n", ch l :

displays a suitable message if the value contained in the storage location ch


represents an alphabetic character.

isdigit - checks whether a character is a numeric digit (i.e, '0' - '9').


~g2 if (isdigit(chll
printf( "%c i s numeric \n", chj ,

displays a suitable message if the variable ch contains the character code for a
numeric digit,

isalnum - checks whether a character is alphanumeric - i.e. alphabetic or numeric.

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).

e.gA i f (i supper (ch 11


printf( "%C is a capital letter \n", chl;

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).

e.g.6 i f (isspace (ch) )


printf("%c is White Spate \n", ch l ,

displays a message if the variable ch contains the code for a white space
character.

isprint - checks whether a character is printable.

~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.

toupper - converts a lower case letter to upper case.

e.g.8 ch_upper = toupper (ch_lower) ;

converts the character stored in ch_lower to upper case and places it in ch_upper.

e.g.9 putchar ( toupper (ch) ) ;

displays the letter stored in ch as a capital letter.

tolower - converts an upper case letter to lower case.


e.g.lO ch_lower = tolower (ch_upper) ;

converts the character stored in ch_upper to lower case and places it in ch_lower.

e.g.Ll putchar (tolower (ch) );


displays the letter stored in ch as a small letter.

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.

atoi - converts a string of numeric digits to an integer.


e.g.l num! = atoi (num) i

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).

atol - converts a string of numeric digits to a long integer.

e.g.2 nurn2 = atol (numl i

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 . :

e.g.3 num3 = atof (num) i

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.

The command studentmark = atoi(mark_string) will convert the digits up to the


first inappropriate character (I) to an integer 20 - and store it in the int variable
studentmark so that it can be used in calculations.

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).

e.g.l printf("%p ", display_square);

will display the address of the start of function display_square.

Defining a pointer variable to hold a function address

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.

I. Think of a name for the pointer variable.


2. Attach an asterisk * to the front of the name to indicate that it will hold an
address.
3. Enclose the whole thing in brackets ().
4. Precede the brackets with the return type for the type of function whose
address will be stored.
5. Follow the brackets with another pair of brackets which include the
parameter types for the function .

e.g .2 void (*ptr_to_functionl) () ;

defines a variable called ptr_toJunctionl which is able to hold the address of any
function which has no parameters and no return value.

e.g.3 void (*ptr_to_function2) (int lint) ;

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.

e.g.4 int (*ptr_to_function3) (int lint) ;

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.

Assigning afunction address to a pointer variable

The address of the function may then be placed in the appropriate pointer
variable.

assigns the address of function display_square to the variable ptr_toJunctionl .

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.

e.g.? (*ptr_to_function2) (10, 20);

calls the function whose address is stored in ptr_toJunction2 - passing across 10


and 20 as parameters.

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) .

e.g.S answer = (*ptr_to_function3) (10, 20);

calls the function whose address is stored in ptr_toJunction3 - passing across 10


and 20 as parameters and placing the value returned in the variable answer.

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

/*Assign address of appropriate function to function


pointer variable*/
if (shape_chosen == 'S')
ptr_to_shape_function = display_square;
else if (shape_chosen == ' T ' )
ptr_to_shape_function = display_triangle;
else
{
printf ( "Error\n II ) ;

exit(!)

/*Display chosen shape*/


(*ptr_to_shape_function) ();

/*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);

!*Carry out calculation and display answer*!


printf (" %d" I (·ptr_to_calc_function) (number) ) ;

!*Finish*/
return 0;

int multiply_by_two(int num)


(
return (num * 2);

int multiply_by_three(int num)


(
return(num * 3);

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

An array of pointers to functions can be set up - by adding the number of


elements in square brackets immediately after the name of the variable.

e.g.I void (*ptr_to_function [3] ) ( ) ;

sets up an array of pointers able to hold the address of three functions (each
function returning no value and expecting no parameters).

The elements might be initialised as follows.

e.g.2 ptr_to_function [0] = draw_square;


ptr_to_function[l] = draw_triangle;
ptr_to_function[2] = draw_diamond;

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]) ();

will then call function draw_square.

ExampleProgram
#include <stdio.h>

void display_square(void);
void display_triangle(void);
void display_diamond(void);

main()

(void (*ptr_to_shape_function[3]) (l;


int shape;

/*Assign addresses of shape functions to elements of


function pointer array*/
ptr_to_shape_function[O] = display_square;
ptr_to_shape_function[l] = display_triangle;
ptr_to_shape_function[2] = display_diamond;

/*Get choice of shape from user*/


printf ("Key 0 to Display Square\n");
printf ("Key 1 to Display Triangle\n");
printf ("Key 2 to Display Diamond\n") ;
scanf ( • %d", &shape);

/*Display chosen shape*/


l*ptr_to_shape_function[shape]) ();

/*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

A pointer to a function can be passed as a parameter to another function. So, for


example function main could pass the job of drawing an office block to another
function draw_officeblock - but instruct it to use another function (e.g.
draw..floor_style I or draw..floorJtyle2) to draw each storey of the block.

The function which will receive the pointer as an argument must have a suitable
formal parameter in its definition.

e.g.I void draw_offices (int height, void


(*draw_floor) () ) ;
{
Instructions
}

Defines a function called draw_offices which expects to receive an integer which


it will store in the integer variable height and a pointer to a function (which has no
return value and no parameters) which it will store in the pointer variable
drawfloor.
To pass a function's address across, it should be named as an argument.

e.g.2 draw_off ices (5, draw_floor_style_l);

calls function draw_offices passing across the value 5 and the address of the
function draw..floor_style_I.

The function whose address has been passed as a parameter (e.g.


draw..floor_styleI ) , can be called using the name of the variable which holds its
address.

e.g.3 ( *draw_f loor) ( ) ;

calls the function whose address is stored in drawfloor (i.e. draw..floor_style_1


in this case). .

Example Program

*include <stdio.h>

void draw_offices lint, void (* ) ());


void draw_floor_stylel(void);
void draw_floor_style2(void);

main()
{char style_choice;
int num-of_floors;

/*Get style required from user*/


printf ("Key A for 2 wide windows per floor\n");
printf ("Key B for 3 narrow windows per floor\n ");
scanf(" %c", &style_choice);

257
/*Get number of floors required*/
printf( "\nNurnber of floors ");
scanf (n%d n, &nWILof_floors) ;

/*Delegate work of drawing offices to function


draw_offices*/
if (style_choice == 'A ')
draw_officeslnum-of_floors, draw_floor_stylel);
else
if lstyle_choice == ' B ' )
draw_offices (nWILof_floors, draw_floor_style2);
else
printf(nError\n");

/*Finish*/
return 0;
}

void draw_offices(int height, void (*draw_f1oor) ())


(int nurn;
/*Draw roof*/
printf("****************************\n");

/*Draw floors of office block (delegate task to


function whose address is held in the pointer variable
draw_floor) */
for (nurn = 1; num <= height; nurn ++)
(*draw_floor) () ;

/*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

argv (Command line items stored in memory)

Iaddress 1---> address r--- > 9 r e e t \0

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>

main (int argc, char *argv())


{int count;
int total = 0;
/*Total the numbers entered as arguments*/
for (count = 1; count < argci count++)
(
total += atoi(argv[count));
}

/*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

When a value (e.g. the contents of a variable) is assigned to a variable of another


type, the binary representation of the data is automatically converted to fit the
new space. However, in many cases the result will not represent the same value.

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.

e.g.l int numl;


signed char n~ = 34;
numl = numz ,
The binary pattern for 34 will be placed in the one byte location num2 -
00100010. The assignment numl =num2 will copy the binary pattern from num2
to numl. As numl is a larger space, the 0 which occupies the leftmost bit (i.e the
'sign' bit) of num2 will be extended to pad out the rest of numl . So (assuming that
integers are two bytes long) the pattern in the integer variable numl will be:
0000000000100010 - which still represents 34.
e.g.2 int num3;
signed char num4 = -25
num3 = num4;
The binary pattern for -25 will be placed in the single byte location num3 - which
in twos complement notation is: 1 1I001I 1. The assignment num3 = num4 will
copy the pattern from num4 to num3 - again using the leftmost bit to pad out the
extra space in the integer location num4. So the contents of num3 will be
1111111111100111 - which still represents -25.

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.

Float, Double, Long Double - converting from smaller to larger


When a value of type float is copied to a variable of type double or long double
the value will remain the same.

Other conversions may result in a loss of data or a change of interpretation -


depending on the size of the numbers involved, whether they are positive or
negative, how the system stores a particular data type and how the system pads a
small unsigned binary pattern.

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.

e.g .3 int num5 = 5000;


char num6;
num6 = num5;

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.

e.g.4 int num7 = 25;


char numB;
numB = num7;

The variable num7. is initialised to the binary pattern for 25 - 0000000000011001 .


This is copied into the char variable numB after removing the high byte. So.
numB will hold OOOlJ001 - which still represents 25.

Signedand Unsigned Integral Types


When a signed value is converted to an unsigned type - or unsigned to signed - of
the same size or larger - the result should remain the same binary pattern.
However. the interpretation may be different - if for example. a negative signed
number is converted to an unsigned number of the same size - it will be seen as a
large positive number - and a large unsigned number may convert to a negative
value on conversion to a signed type.

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.

Float. Double. LongDouble - converting from larger to smaller


When a value of type double is copied into a variable of type float the value will
remain the same - provided that the float type has a large enough range to take the
value - otherwise the result will depend on the system.

262
Mixing data types in arithmetic and comparisons

When different types of numeric data are mixed in an arithmetic or comparison


expression - the smaller types are converted to the largestdata type present before
arithmetic is carried out - on the following basis. (Themethod of conversionis as
stated earlier in the lesson.) The answerwill also be governed by this.

If one item is: Convert othersto


long double long double
double double
float float
unsigned long int unsigned long int
long int and another is unsigned int -
then if long int can hold all valuesof unsigned int -long int
else unsigned long int;
long int long int
unsigned int unsigned int
The remainder will all be converted to type int (including char, short and
enumerated types used in arithmetic- this is called integral promotion) .

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).

e.g. I ( long) numl

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.

Certain conversions of pointers from one type to another need to be done


explicitly. So. the pointer returned by malloc (Lesson 3.25) had to be explicitly
cast to the correct type.

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.

2. Compile and link the program using cc <program name>


This will produce two files:

a) an object file with the same name as your original C program


except that the extension will be .o
b) an executable file (i.e. a program ready to run) called a.out.

e.g. cc test.c

compiles test.c producing an object program called test.o then


automatically links the object program with relevant library files to
produce a runnable program called a.out.

3. Run your program by typing a.out

If you would prefer your executable program to be called something else -


then simply copy a.out to a new name.

e.g . cp a.out test

copies a.out to produce a runnable program called test.

Alternatively, when compiling the program you can specify a particular


name for the runnable program by using the -0 option followed by the
desired name.

e.g. cc -0 test test.c


This will compile the program test.c producing - as before - an object file
called test.o - and then link the program producing an executable file
called test. So, the program can now be run simply by typing test.

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.

e.g. vi test.c (allows you to key in a C program called test.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.

<ESC> I insert text


<ESC> x delete a character (under the cursor) .
<ESC> dd delete a line
<ESC> A add text at the end of the line
<ESC> followed by an arrow key move around in the text

<ESC> :w save the program file


<ESC> :w <program name> save the program file with a new name
<ESC> :wq save the program me and quit
<ESC> :q! quit without saving

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, however, obviously does not have 10 fingers; it counts by switching


electric currents ON (to represent 1) and OFF (to represent 0) . If the computer
needs to count further than one, it has to carryjust as we have to do if we want to
count past 9.

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

So - for example - the binary number 11001 would appear as:


16 8 4 2 1
1 1 001
meaning 1 lot of 16 + 1 lot of 8 + 1 lot of 1 =25 denary.
Think of all the binary digits as being electric lamps - each one representing a
column heading. If a particular lamp's value is needed to help make up a number,
the light is switched ON - otherwise the light is switched OFF. The digit 0 is
used to indicate that the light is off - and 1 to indicate that it is on.

0 1 I 0 0 0 I 1

~ ~g- -g- ~ ~ ! -g--g-


128 64 32 16 8 4 2
ON ON ON ON

64 + 3 2 + 2 + 1 = 99

267
Converting Binary numbersto Denary

e.g. Convert 110011 Binary to 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.

So, 32 + 16 + 2 + 1 = 51. (The 8 and 4 columns do not contain Is - so


they are ignored.

So, 1I 0011 in binary can be written as 51 in denary .

Converting Denarynumbersto Binary


e.g. convert 39 to binary

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.

So, the final answer is:

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.

For a two-byte location (e.g. a typical integer) the headings are:


-32768,16384,8192,4096,2048, 1014,512,256.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

-'g- ~ ! ! -'g-! -'g- ft


- 12 8 6 4 32 16 8 4 2
ON ON ON

- 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.

The column headings for the hexadecimal system are:

........ 4096s 256s 16s Is (multiplying by 16 as you move left)

So - for example - 122 hexadecimal would mean: I x 256


+ 2 x 16
+ 2JU.
= 290 denary
Because it is necessary to be able to count up to 15 in a single column without
carrying - extra characters are needed to represent 10, II, 12, 13 and 14 as single
digits; the letters A, B, C, D, E and F are used for this purpose. The Hexadecimal
Digits are therefore: 0 1 2 34 5 6 78 9 ABC D E F

ConvertingBinary to Hexadecimal
It is far simpler to convert binary to hexadecimal than to tum it into decimal.

e.g. Convert11100111 BinaryintoHexadecimal.

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

Step 3 Now convert each quartet into a hexadecimal digit.

8421 8421
1 I 10 01 I 1

= 14 Decimal =7 Decimal
=E Hexadecimal =7 Hexadecimal
Step 4 Place the hexadecimal digits together.

So the answer is E7 Hexadecimal

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) .

If a character held in ASCII is sent to an output device such as printer or VOU,


the device will automatically output the appropriate characters shape. If, for
example, a numeric digit is not held in a character code such as ASCII, it will
have to be converted first before the device will recognise it.

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

Character Binary Hex Character Binary Hex


<NUL> 00000oo 00 @ 100000o 40
<SOH> 0000001 01 A 1000001 41
<STX> ()()()()()10 02 B 1000010 42
<ETX> ()()()()()II 03 C 1000011 43
<EOT> 0000100 04 0 1000100 44
<ENQ> 0000101 05 E 1000101 45
<ACK> 0000110 06 F 1000110 46
<BEL> 0000111 07 0 1000111 47
<BS> 0001000 08 H 1001000 48
<HT> 0001001 09 1 1001001 49
<VT> 0001010 OA J 1001010 4A
<LF> 0001011 OB K 1001011 4B
<FF> 0001100 OC L 1001100 4C
<CR> 0001101 00 M 1001101 40
<SO> 0001110 OE N 1001110 4E
<SI> 0001111 OF 0 1001111 4F
<DLE> 0010000 10 P 1010000 50
<DCl> 0010001 11 Q 1010001 51
<DC2> 0010010 12 R 1010010 52
<DC3> 0010011 13 S 1010011 53
<DC4> 0010100 14 T 1010100 54
<NAK> 0010101 15 U 1010101 55
<SYN> 0010110 IV 1010110 56
<ETB> 0010111 17 W 1010111 57
<CAN> 0011000 18 X 1011000 58
<EM> 0011001 19 Y 1011001 59
<SUB> 0011010 lA Z 1011010 SA
<ESC> 0011011 IB [ 1011011 5B
<FS> 0011100 lC \ 1011100 5C
<OS> 0011101 10 ) 1011101 50
<RS> 0011110 IE A
1011110 5E
<US> 0011111 IF ~
1011111 SF
<SP> 0100000 20 1100000 60
! 0100001 21 a 1100001 61
" 0100010 22 b 1100010 62
#/£ 0100011 23 c 1100011 63
$ 0100100 24 d 1100100 64
% 0100101 25 e 1100101 65
& 0100110 26 f 1100110 66
0100111 27 g 1100111 67
( 0101000 28 h 1101000 68
) 0101001 29 i 1101001 69
• 0101010 2A j 1101010 6A
+ 0101011 2B k 1101011 68
0101100 2C 1 1101100 6C
0101101 20 m 1101101 60
0101110 2E n 1101110 6E
/ 0101111 2F 0 1101111 6F
0 0110000 30 p 1110000 70
I 0110001 31 q 1110001 71
2 0110010 32 r 1110010 72
3 0110011 33 s 1110011 73
4 0110100 34 t 1110100 74
5 0110101 35 u 1110101 75
6 0110110 36 v 1110110 76
7 0110111 37 w 1110111 77
8 0111000 38 x 1111000 78
9 0111001 39 y 1111001 79
0111010 3A z 1111010 7A
0111011 3B ( 1111011 7B
< 0111100 3C I 1111100 7C
0111101 30 ) 1111101 70
> 0111110 3E 1111110 7E
? 0111111 3F <DEL> 1111111 7F

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

You might also like