Sie sind auf Seite 1von 104

LECTURE NOTES:

EEE1003W - Computing For Electrical


Engineers: Part II - C Programming

M. S. Toeu.
s
Electrical Engineering Department
Control and Instrumentation
University of Cape Town.

ii

Contents

1 Introduction

1.1

Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.2

Why C? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3

Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4

A Short Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4.1

C Program Structure - Hello, EEE1003W!! . . . . . . . . . . . . .

1.4.2

Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4.3

Variables, Declarations, Initializations, Assignment,


Operators and I/O . . . . . . . . . . . . . . . . . . . . . . . . . .

1.4.4

Control Flow and Conditional Compilation . . . . . . . . . . . . .

10

1.4.5

Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11

2 Introduction to Software

13

2.1

What is Programming? . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

2.2

What is Software?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

2.3

Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13

2.4

Software Development . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

2.5

Software Completeness . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

iii

iv

CONTENTS
2.5.1

Semantic Correctness . . . . . . . . . . . . . . . . . . . . . . . . .

15

2.5.2

Syntactic Correctness . . . . . . . . . . . . . . . . . . . . . . . . .

15

Software Development Cycles . . . . . . . . . . . . . . . . . . . . . . . .

18

2.6.1

Software Life Cycle stages . . . . . . . . . . . . . . . . . . . . . .

18

2.6.2

The Waterfall Life Cycle . . . . . . . . . . . . . . . . . . . . . . .

20

2.6.3

The Iterative Life Cycle . . . . . . . . . . . . . . . . . . . . . . .

20

2.6.4

Rapid Prototyping Life Cycle . . . . . . . . . . . . . . . . . . . .

21

2.7

Flow Charts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

2.8

Additional Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

2.6

3 Standard C Data Types

23

3.1

Variable Naming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

3.2

Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

3.3

Formatting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

3.4

Declaration and Initialization and Assignment . . . . . . . . . . . . . . .

26

3.5

Type Qualiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

3.6

Variable Resolution and Operators . . . . . . . . . . . . . . . . . . . . .

28

3.7

Type Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

3.8

Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

4 C Operators and Expressions

31

4.1

Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

4.2

Assignment Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . .

32

4.3

Relational, Equality and Increment/Decrement Operators . . . . . . . . .

33

CONTENTS

4.4

Logical Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

4.5

Bitwise Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

4.6

Functions in Standard Libraries . . . . . . . . . . . . . . . . . . . . . . .

36

4.7

Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36

5 Execution Flow Control


5.1

39
39

5.1.1

Statement or Instruction . . . . . . . . . . . . . . . . . . . . . . .

39

5.1.2

Block of Instructions . . . . . . . . . . . . . . . . . . . . . . . . .

40

5.1.3

A Source File . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

Conditional Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

5.2.1

if(){ } else{ } . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

5.2.2

switch(){ } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

5.2.3

5.2

Program Statements and Blocks . . . . . . . . . . . . . . . . . . . . . . .

Conditional Value Use . . . . . . . . . . . . . . . . . . . . . . . .

44

5.3

Nested Conditionals

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

5.4

Repeated Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

5.4.1

do{ } while() . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5.4.2

while(){ } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5.4.3

for(){ } . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

5.4.4

label, goto label; . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

5.5

Nested Loops and Conditionals . . . . . . . . . . . . . . . . . . . . . . .

47

5.6

Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

6 Input Output

49

vi

CONTENTS
6.1

Output Redirection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49

6.2

Direct File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

6.3

Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . .

52

6.4

Other Forms of I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

6.5

Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54

7 Functions

55

7.1

Function Denitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55

7.2

Function Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

7.3

Return Types and Input Variables . . . . . . . . . . . . . . . . . . . . . .

57

7.4

Calling Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

7.5

Variables and Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

7.5.1

Local Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

7.5.2

Global Variables . . . . . . . . . . . . . . . . . . . . . . . . . . .

59

7.6

Recursive Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

7.7

The C Preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

60

7.7.1

Including Files . . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

7.7.2

Dening Constants, Constant Expressions and Macros . . . . . .

61

7.7.3

Conditional Denitions . . . . . . . . . . . . . . . . . . . . . . . .

62

Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

7.8

8 The C Software Structure

67

8.1

Simple Program: Single Source File . . . . . . . . . . . . . . . . . . . . .

67

8.2

The Header (.h) File . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

CONTENTS

vii

8.3

Compiling and Running a Module . . . . . . . . . . . . . . . . . . . . . .

69

8.4

Structure of C Software . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

9 Arrays

73

9.1

What is an Array? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

9.2

Declaration and Initialization . . . . . . . . . . . . . . . . . . . . . . . .

74

9.3

Characters and Character Strings . . . . . . . . . . . . . . . . . . . . . .

74

9.4

Multidimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

9.5

Use of Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

9.6

Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77

10 Pointers

79

10.1 What is a Pointer? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

79

10.2 Declaration and Initialization . . . . . . . . . . . . . . . . . . . . . . . .

79

10.3 Using Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

80

10.4 Arrays Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

10.5 Character Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82

10.6 Variable Use by Value and Reference . . . . . . . . . . . . . . . . . . . .

84

10.7 Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

86

11 Composite Data Types - Structures

87

11.1 What is a Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87

11.2 Structure Properties and Functions . . . . . . . . . . . . . . . . . . . . .

87

11.2.1 Denition, Members and Type Dening . . . . . . . . . . . . . . .

88

viii

CONTENTS
11.2.2 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

11.2.3 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

88

11.3 Using Structrures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

11.4 Pointers to Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91

11.4.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91

11.4.2 Use of Pointers to Structures . . . . . . . . . . . . . . . . . . . . .

92

11.5 Arrays of Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

11.6 Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

12 Advanced Concepts in C

95

12.1 Make Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.2 Function Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.3 Callback Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.4 User Dened Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.5 Abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.6 Bug-Free C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

12.7 Extra Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

Chapter 1
Introduction
One of the largest set of real life problems are computing problems. Traditionally these
problems were solved using mechanical devices. The most basic computer is a mechanical
abacus used to introduce arithmetic since around 2300 BC.

(a) ABACUS

(b) ENIAC

Analogue electronics using vacuum tubes were later used for computing, followed by the
rst announced computer in 1946 Electronic Numerical Integrating And Computer
(ENIAC). Word is a single arithmetic operation would take the electricity of the entire
city. The disadvantages of mechanical and analog computers were that once made, they
are very dicult to modify for a dierent problem. The ENIAC could be reprogrammed
(by very privileged people) to perform dierent computing tasks. This introduced some
exibility in computing and marked the beginning or the digital computing era.
Since then, computers have become the main platform for solving a large majority of
computing problems and have increased processing speed, lower power consumption and
sizes that t the palm. These computers provide a hardware platform for implementations
1

CHAPTER 1. INTRODUCTION
of software that solves computing problems. Loosely stated, hardware is the tangible
part of a computer and software is the non-tangible component behind the nice graphics.
Figure:1.1 below illustrates the organization of computer hardware and software.

Figure 1.1: Computer System Organization

At the lowest level is the computer hardware. Such hardware has some interface buttons
to human users but it is largely hidden away from the user for security and safety reasons.
The hardware is controlled by a layer of software called system software. System software
is an interface between the hardware and user level software. One often needs privileges
and passwords to modify such software, as it may also make or break the computer.
The top layer of software is the User software. This software runs on top of the system
software and is not protected since it can not harm the hardware (most of the time).

1.1

Programming

Unfortunately, digital computers process information in the form of binary 1 or 0.


Streams of such ones and zeros intended for communication with computers are called
machine code. In order for human beings to communicate with computers, they have
to be able to interpret binary information (Machine Language), or computers be able
to interpret spoken languages such as English, Sotho, Zulu etc. Computers can not
understand spoken language and even though humans can understand binary, it becomes
tedious and impossible to process it at the speed of computers. As a result, compromise
languages that are understood by humans and can easily be converted to machine language
are used. These are called programming languages. Programming languages dier in the
way they are written (Syntax), and understood (Semantics). Furthermore, the ease to
which they can be converted to machine language diers, as well as the way they are
converted. Figure:1.2 below is a typical chart showing how close some programming
languages are to and from machine language to spoken language.
2

1.2. WHY C?

Figure 1.2: Programming Language Chart

1.2

Why C?

In all honesty, all programming languages have their fair share of GOOD and BAD, and
some are more suited to some tasks than others. From the chart that illustrates how
far the languages are from machine code and human language, it is clear to see that
C is closer to machine language than most languages while still being human readable.
Furthermore, C is a compiled language which combined with its ease of conversion to
machine language gives it a speed advantage over most programming languages. This
makes it ideal for Electrical engineers whose programming problems are close to hardware
and strict on timing. A number of computer programs and programming languages such
as Python are developed using C/C++ to show just how advantageous it is. Lastly, C
has a very small diction (vocabulary) therefore it is easy to learn.
However, this beauty does not come without cost. The most important of the disadvantages
of C is that it allows careless programming which often lends the uninitiated in hot water.
Furthermore, the small vocabulary of C is also a curse since it does not have many of
the nice functions that other languages have (its a D.I.Y). In addition to the above C is
not object oriented, so it is not easy to use with the modern programming concepts. It
is also worth mentioning that C is a strongly typed language. This means that the type
of each variable should be specied before it can be used, or there will be problems.
The choice of which programming language to use depends on the application/problem
at hand. For time and mission critical software such as an Aeroplane autopilot one would
opt for a faster programming language such as C. For applications that are not critical,
such as word processing, email, etc. slow languages with more functionality are good,
such as Python.

1.3

Terminology

1. Algorithm: A well dened sequence of steps needed to solve a problem.


2. Syntax: This is a dened set of words, phrases or instructions that form a language.
If missused or spelled wrongfully, these loose meaning and result in errors.
3. Program: A set of computer instructions that perform a dened task (implements
3

CHAPTER 1. INTRODUCTION
an algorithm).
4. Code: A group of programming language instruction.
5. Interpreter: A computer program that translates instructions into machine code
and runs them.
6. Compiler: A computer program that converts instructions into object les.
7. Linker: A computer program that converts object les into executable les that
can be run to perform a task.
8. Interpretation: Conversion of computer instructions, where each instruction can
be converted independently. Languages, such as Python, MATLAB, Java, Perl,
Tcl etc. are interpreted. Interpreted languages are portable since each machine
interprets them when it gets them. However, these languages are slow, since they
have to be interpreted each time they are run. Figure:1.3 shows a programming
cycle for interpreted languages.

Figure 1.3: Programming Cycle for Interpreted Languages


9. Compilation: A once-o conversion of computer instructions. Most of compiled
instructions need to be in some format and compiled as a group not independently.
Examples of such languages are C/C++. These can not be ported to other computers
after compilation and have to be recompiled on each machine. These languages are
however fast once they have been compiled. Figure:1.4 shows the programming
cycle for compiled languages.

Figure 1.4: Programming Cycle for Compiled Languages


10. Documentation: These are les and sections of programs that are intended at
explaining the operation and intention of programs or instructions in programs. It
is good practice to always document programs to enable others (including yourself)
to understand and improve them at a later stage.
11. Software: Software is a group of programs, documentation and conguration les
that collectively perform a specic task.
4

1.4. A SHORT TUTORIAL


12. Low level/High level Languages: Low level languages are those that are close to
machine language such as Assembler language and C, they often contain only the
basic (necessary) functions. High level languages are closer to spoken language,
they are mostly slow and have more nice functions that simplify programmers
lives. Examples can be made of Python, MATLAB, Java etc.

1.4

A Short Tutorial

In this section a presentation some of the concepts of C programming are presented for
individuals who are familiar with programming in some other language such as Python
:-) to start having fun. If however, the reader is not confortable with programming, in
any other languages, all of what is presented here will be covered more elaborately in the
rest of the text.

1.4.1

C Program Structure - Hello, EEE1003W!!

As mentioned earlier in the text, C instructions can not be run one-by-one in the manner
of Python and other languages. In order to run them they have to be put in a given
format for the compiler to understand. Some consider this a problem, but hey! there is
no rose without thorns :-). In order to study the structure of a C program we will use a
simple program that prints Hello EEE1003W! and quits.

The rst line in the program #include<stdio.h> includes a library stdio.h. Libraries
are a collection of functions that have already been dened and can be used by other
programs. This will become apparent in the later chapters. For now let us just consider
a library as a bag full of tools that we need to achieve a task. This particular library
has functions needed to get data from the user, les, storage etc into our program and
functions used to give data back to the user (on the screen), store data on disk, or les.
There are several of these libraries and will be covered in detail later in the text.
On the same line we see text that is preceded by "//" this double forward slash tells the
compiler to ignore whatever follows (only on that line). An alternative way of telling the
compiler to ignore a section of the program is to enclose it as "/* ...text...*/". This
is seen on some of the lines in the program. Text lines that are ignored by the compiler
5

CHAPTER 1. INTRODUCTION
are used comments and are used to explain what that code section or instruction does.
This is a good practice to remind ourselves what we were doing and to explain to other
programmers who might work on our programs.
Line 6 contains an instruction printf("Hello, EEE1003W!");. An instruction is any
line in the program that is terminated by a semicolon (;). The instruction contains a
function that printf("...string...") which prints whatever string is placed between
the double quotes. This function comes from the library that we included, meaning that
if we remove line 1 the program will not be compiled successfully.
The most important part of a C program is the one specied main(){ }. When a C
program is compiled and executed, the rst instruction that appears within the { } of
main() are executed. This means every runnable C program(s) should have the main(){
} somewhere. Otherwise, the computer will not know where to start.

1.4.2

Getting Started

The rst thing to do is to have a compiler installed on your computer. Compilers come
as programs that once installed can be run on the command prompt, or through some
Integrated Development Environment (IDE). For our purpose as beginners, we will only
consider the IDE route. Most IDEs have similar structures and functions, so we will
introduce only one called Bloodshed Dev-C++, and others can be used similarly.
Install Dev-C++ by running a le Dev-C++.exe from our Vula site, which can also be
downloaded free from the internet, and follow the instructions that come up.
Once Dev-C++ is installed, go to
Start > All Programs > Bloodshed Dev C + + > Dev C + +, and a window
similar to the one Figure:1.5 will open.

Figure 1.5: Opening an IDE and new le

1.4. A SHORT TUTORIAL


To open a new program, go to File > New > Source File or press Ctrl+N on the
keyboard and an empty text space will appear on which you can start typing your
program. Type the program discussed earlier and go to File > Save As or Ctrl+F12
on your keyboard. In a small window that opens as, look for the File Name: eld and
type the name of your le, which should be hello.c for our previous program, then click
on Save. You can choose the folder in which to save your le by selecting the desired
folder from the Save in: drop down menu. This is illustrated in Figure:1.6.

Figure 1.6: Typing program and saving it


Move your mouse pointer over all the buttons of your IDE and read the little messages
that appear next to the mouse pointer. These messages tell you what happens when
you click on that menu item. Pay special attention to where the Compile (Ctrl+F9),
Run (Ctrl+F10) and Compile & Run (F9) come up. The texts in the () tells you
which buttons to hit (simultaneously, e.g press the Ctrl and F9 keys and see) on your
key board to achieve the same task.
Once the le has been saved, click on the Compile button on the IDE, which you should
have already located. At the bottom of the IDE window, some text is printed if there
are problems (Errors) in your program. If the program was compiled successfully a small
window will open telling you how many errors and warnings were found in your program.
Our program should not contain any errors. Then we can click on the Run button on the
IDE menu and this will link and run our program. The Compile and Run functions can
also be found by going to Execute > Compile and Execute > Run. A command
window will appear and show Hello, EEE1003W! printed on it as shown in Figure:1.7.
Now remove the semicolon at the end line 6 and repeat the compiling and running, check
the text that is printed at the bottom of the IDE and look for what the error is??. Note
that in order to run the program again you need to close the command window rst.
If when you run the program, no command window opens, go back to the program and
insert the line system("PAUSE"); just before the closing } of main(){ }. This simply
tells the compiler to halt at this point and wait for user action which can be a click of
any key on the keyboard. Repeat the compile and run procedure.
7

CHAPTER 1. INTRODUCTION

Figure 1.7: Compiling and Running a program


Congratulations!! you have compiled your rst program :-)
Exercise: 1.4-1
Modify the hello.c program so that it prints out Congratulations to you !!.

1.4.3

Variables, Declarations, Initializations, Assignment,


Operators and I/O

The major importance of programs, in any programmig language is to manipulate data.


This includes getting data from a data source (keyboard, storage device, le, microphone,
etc.) operating on and transforming the data and returning the results to a data sink
(screen, le, storage device, speakers, etc.). In order to operate on the data a program
needs to store the data somewhere temporarily where it can access it. The device for
temprarily storing data is called memory, and comes in dierent forms; Random Access
Memory (RAM), Flash memory etc.
A variable is location in memory reserved for storing data. The size of the reserved
storage space is determined by the type of data that is going to be stored in it. The
variable has a Data Type such as int, float, double, bool for reserving space for
integers, oating point numbers and binary, char, char*, for storing a single character
and a string of characters. In C the compiler has to be told about the type of a variable
before such a variable can be used. The process of telling the compiler about the variable
types and reserving space for them is called declaring variables.
If a variable is declared, space enough to accommodate a variable of its type is reserved,
but nothing meaningful is stored in it. In fact, a random value may be in there and
this value is called garbage. If two integer variables int a and int b are declared and
followed by an addition stored in the third integer int c (i.e. c = a + b;), the result
8

1.4. A SHORT TUTORIAL


is unpredictable and may be dangerous to the operation of the software. To solve this
problem variables are usually initialized to some values such as a = 2;.
The data to be stored in the reserved space can be written into the program (assigned),
or it can come from some external (to the program) source, such as the keyboard, le,
storage device, microphone etc. The functions that enable the program to acquire data
from external sources can be found in the standard input and output library (stdio.h)
which should be included at the beginning of the C program. Examples of such functions
are scanf(); getc(); gets(); for reading data the last two functions read a character
and string of characters respectively, printf(); putc(); puts(); which are the output
counter parts of the previous input functions.
C provides a number of operators that operate in the data, these operators, work on
specic data types. Examples are arithmetic operators (+, -, *, /,) and others which
follow the famous BODMAS precedence rules. Other operators are binary and logic
operators which will be covered in detail in the next chapter. The program below
demonstrates some of the concepts presented above.

The int before main() and the return 0; at line 18, will be explained later in the
text, for now we have a choice of leaving them out like in the hello.c program. In this
program the only parts of the program that need explaining are line 5 which declares
three integer variables a, b, c. The int is a key word that tells the compiler to reserve
space for an integer. The %d, in scanf() on line 8 is a formating directive that tells the
compiler fo format the read data as an integer. The function then stores the read data at
the address of the integer variable a specied as &a. If the data was a character we would
use %c and %f for oating point number. The same %d directive used inside printf()
tells C to print the integer value stored in variable c at the place of the directive lines 14
and 16. The \n in the printf() instruction tells C to print whatever follows the directive
on the newline. Run the program and observe the output. Note that you might need to
add the system("PAUSE"); so that the output does not just ash and dissapear.

CHAPTER 1. INTRODUCTION
Exercies:1.4-2
(a) Enter a value of a = 1, smaller than b = 2. Why is the quotient zero? Modify the
program so that it gives the correct output quotient.
(b) Enter values of a and b as oating point numbers 9.9 and 3.0. Why are the answers
rounded o?
(c) Modify the program so that it can allow us to enter oating point numbers.

1.4.4

Control Flow and Conditional Compilation

Sometimes, to solve a problem we need to repeat a similar task for a number of times.
In that case we can repeat the instructions that perform the task over and over, or write
the program so that it loops to the start of the repeated instructions for the number of
times needed. This behavior is called looping. There are several types of loops; for,
while and do while loops.
Another situation occurs when we perform certain tasks only when a certain condition is
met, such as if the value of a variable is greater than a number 15. The program below
repeatedly requests the students to enter their age. If their age is less than 15 it prints a
message for them to go home, if they are older it prints a welcome message (if() else
parts). The program repeats this process until 10 students have been checked (for();
loop). Run the program and observe the output.

Note that the loop structure has a format for(START, END, INCREMENT){INSTRUCTIONS},
and can be interpreted as folows, ... while CONDITION is true, repeatedly run the
INSTRUCTIONS and each time add INCREMENT to START... . The conditional
compilation on line 11 has format:
if(CONDITION){IN ST RU CT ION S1 } else {IN ST RU CT ION S2 }, and means: run
IN ST RU CT ION S1 if CONDITION is true, or else run IN ST RU CT ION S2 .
10

1.4. A SHORT TUTORIAL


Exercise:1.4-3
(a) Modify the program so that it welcomes students from age 20 and above.
(b) Change the program so that it only addresses 5 students.
(d) Change the program so that it species the age of the student in the welcome or
rejection message.

1.4.5

Functions

In some problems we nd out that we need to compute some given function at dierent
parts of the program (not necessarily repeatedly). In that case, one solution is to rewrite
the code instructions to perform the function at every point where we need to perform it.
A better alternative is to write the set of instructions as a collection called a function.
Having dened the function all we need to do is call the function at every point where
we would have rewritten the respective instructions. The program below illustrates the
use of a function, which takes the value of resistance R and current I passing through the
resistance as its inputs, and gives out the potential drop V across the resistor.

The function has a format:


DataType FunctionName(DataType1 Input1, DataType1 Input2)INSTRUCTIONS; return
Variable;
11

CHAPTER 1. INTRODUCTION
This means, that the function takes input variables Input1 and Input2 of data types
DataType1 and DataType2 respectively, manipulates the variables and return a value
stored in variable V to the user. The DataType before FunctionName tells us that the
function will give back a value of that Type.
All of the concepts presented briey in this section will be addressed in more depth
in the rest of the text. The reader is encouraged to experiment and play around with
the programs and any other simple programs that can be obtained on internet tutorials
(Careful!!) in order to raise awareness and questions that will help their understanding.
Exercise:1.4-4
(a) Modify the program so that it computes the power dissipated by the resistor P = I 2 R.
(b) Comment out line 4 and check the output again. Why is there an error?
(c) Why is the function dened as a oat? and what will happen if we remove the return
V; in line 31?

12

Chapter 2
Introduction to Software

2.1

What is Programming?

In the most general sense, programming can be thought of as solving a problem by


following a well dened set of procedures (steps). In Computing, this therefore means
that programming involves coming up with ecient algorithms to solve a computing
problem and writing out programs to perform such algorithms. Therefore a programmer
is not someone who can memorize programming language instructions, but an individual
who can use such instructions to solve computing problems.

2.2

What is Software?

While there are many acceptable denitions of software, for the purpose of this text
we will understand software to mean a collection of computer programs, documentation
and conguration les needed to solve a computing problem. This is mainly due to the
fact that programs that are not properly congured are useless, and this is also true for
programs that users dont know how to use because there is not enough documentation.

2.3

Documentation

Documentation is a collection of documents, and comments in programs and conguration


les that explain the functionality of programs and conguration les. Such documention
is put in place by the author(s) of the programs and conguration les or a third party who
understands them. Documentation is a vital part of software, that simplies upgrading,
maintaining , and reverse engineering the software if the need arises. It is therefore
13

CHAPTER 2. INTRODUCTION TO SOFTWARE


good practice to document all programs and conguration les well, since upgrading and
maintaining software can be the most expensive phases of a software life cycle.

2.4

Software Development

In most cases, individuals jump onto writing programs even before they understand
the problem at hand and form an algorithmic solution for it. This can lead to lots of
time spend on unnecessary programs that need not be part of the solution. Essentially,
computers can not do any magic that a human being can not do, all they achieve is doing
the human derived algorithms faster and involving large and lots of numbers that may
be tedious and cumbersome for humans. As a result, a program implementing a well
thought of algorithm will give meaningful results while a program done in a rush with
no planning may give results that do not make sense. There are fundamental steps that
are taken by software designers in order to minimize development time and increase the
chances of correct outcomes. The steps given below do not in anyway represent a specic
software design model but are easy to understand.

1. Understand the Problem: It is vital that the problem at hand be well understood
before any form of programs can be written. This includes answering the questions:
What is the problem?, Can it be solved?, Who is going to use the software? Does
it take inputs from somewhere? Does it give outputs, and in what format?
2. Design an Algorithm: If all of these questions and others can be answered, a solution
can then be investigated. This solution should be well thought of and analyzed
before it can be programmed. Common factors that should be considered are: the
time available for developing the solution, what technology options are there for
solving the problem (e.g programming languages), which algorithm(s) can be used,
how many people are working on the problem. The end result is a sequence of
steps that can be followed by a human being (to some extend) aimed at solving the
problem. At this stage the developer(s) has to answer amongst others the following
questions: Are we solving the right problem? and Are we solving the problem
correctly?
3. Design and Implement Software: At this stage the designed algorithms can now be
organized to suit the technology chosen to implement them. An example is that of
a programming language: if an algorithm is implemented in Python, the design can
use object oriented techniques, which can not be used if for instance C was chosen.
The end result of the software design is a well planned structure for programs, how
many programs, what does each program solve, what are its inputs and outputs,
how many functions, their purpose and inputs and outputs.
When the structure of the software has been designed, and well explained it should
be in such a way that a dierent individual(s) can pick it up and implement it
without diculty. It is this structure that is taken and implemented in a given
programming language (writting programs).
14

2.5. SOFTWARE COMPLETENESS


4. Test and Commission Software: Once the programs and conguration les have
been written, it is possible that some of the programs can be tested on their own,
and some of the functions can also be tested on their own. This testing is done,
and the individual functions and programs are then combined to solve the overall
program, and tested. If all is well then we can give coee a break and get some
sleep.

2.5

Software Completeness

Having tried to follow the guidelines to the letter, a million dollar question is then: How
do we know when the software is complete? A couple of things can be checked and are
listed below.
Does the software run (work) without errors?
Does the software solve the right problem?
Is the software solving the problem correctly?
Does the software handle illegal actions from the user without crashing?
Can the software handle more data/operations without loss of speed or crashing?
Is the software fast enough?
Is the Client/Boss happy? ;-)

The check list above can be summarized by checking the software for correctness in two
aspects:

2.5.1

Semantic Correctness

In solving problems some solutions give acceptable results through legitimate means, and
these are good. Some solutions will give acceptable results through wrong manipulations,
if this is the case, some of the results will be wrong even if other aspects of the software
are correct. In simple terms, the programs or algorithms do not make sense, then the
program is Semantically incorrect.

2.5.2

Syntactic Correctness

As stated earlier in the text each language has its own key words, phrases, instruction that
should be used in a prescribed manner in order to maintain meaning. In programming
15

CHAPTER 2. INTRODUCTION TO SOFTWARE


languages, if the instructions are not used in the way they were meant to, the compiler/interpreter
gives an error or warning message. If a program(s) does not run (work) due to misused
instructions, the program is said to be Syntactically incorrect.
Example: Square Root
Write software that can be used to compute the square root of real numbers and give a
the answer as a real number.
Solution 01: Careless
Our rst solution to this problem is one which many people would follow, without
thoroughly understanding, analyzing the problem, formulating an algorithm, choosing
a suitable technology (programming language) and designing the program(s).

After completing this program, then the questions arise from the client/boss: Who said
the real numbers come from the user? What if the user enters a negative real number?
What if we enter a large real number and you used float not double? I want my answers
rounded to 2 decimal places :-(
Solution 02: Well planned
In this solution we take the time to ask ourselves what is a real number? (positive and
negative, can be large). Where will they come from? (Keyboard). How large are they
expected to be? (float size). What precision should the answers be? (2 decimal
places).
After asking all the questions that help us understand the problem and deciding to use C,
we note that C already has a function that computes square root for positive real numbers
in the library math.h, and we use it and add extra instructions to handle negative numbers
and round o the answers. The second program is given below.

16

2.5. SOFTWARE COMPLETENESS

Exercise:2.5-1
(a) Determine if the program below is correct semantically or syntactically, point out any
instructions that are not correct in both regards.

(b) Why is it important to document software?


(c) Rewrite the square root program so that if negative numbers are entered it converts
them to their positive equivalents and computes their square root.
17

CHAPTER 2. INTRODUCTION TO SOFTWARE

2.6

Software Development Cycles

We introduced earlier an informal set of steps that can be followed in developing software.
In literature, there are well dened software development procedures called Software
Life Cycle Models. A brief account of the three commonly known and used is given in
the text that follows.

2.6.1

Software Life Cycle stages

1. Gathering Requirements
This is the process of gathering understanding of what the problem that needs
solving is, and what functionality the software needs to provide. This task answers
the question of what needs to be done, and not how it should be done. This involves
meeting with the end user of the software to gure out what it is they expect from
the nished product.
2. Feasibility Study
After understanding the requirements, considering the time allocated for the software
to be completed, the amount of manpower and skill available, available budget and
the technology, it is essential to determine if a solution to the problem is feasible.
If it is, can it be realized within the given time frame or by the available labour,
through the technology at our disposal. You dont want to miss a deadline as that
can be very costly.
3. Requirements Analysis & Specication
The requirements given by the end user are in human language (English, Sotho,
Tswana, etc) and often, do not factor in any technical implications. Furthermore,
it is often not easy to analyze such requirements to uncover conicts, dependencies,
resource requirements. In order to achieve the aforementioned, the user requirements
need to be analyzed and specied in a way that makes it possible to analyze them
to uncover technical implications.
Several formal specication methods exist, with the most common being ow
charts, state machines and Unied Modeling Language (UML). The list
is by no means all inclusive. Flow charts will be introduced here, but the other
alternatives will be encountered withing more relevant texts. In specifying software
it is important to distinguish between two types of requirements:
Functional Requirements: These are requirements that determine the core
functionality of the software, such as how the inputs are processed to give
outputs, how fast some operations need to be done and for how many times,
the way in which data needs to be represented and the sources and sinks of
inputs and outputs respectively.
Non-functional Requirements: These are light weight tasks for displaying
greetings, helping the user to navigate the software, and tips. They have no
inuence on the critical functions of the software and can be missed without
negative eects.

18

2.6. SOFTWARE DEVELOPMENT CYCLES


4. Design
Based on the technology used to develop the software (such as programming language),
the software can be broken down into smaller more manageable and independently
testable programs, functions and modules. It is the aim of the design phase to
establish such decompositions, and how the dierent components of the software
will interact with each other. The chosen technology dictates the software structure.
The common ways in which software is designed are:
Monolithic design: This is where everything is done in one program, without
any separation of functionality or simplication of tasks. Most new comers
use this approach, and end up in trouble because if a part of the program
is not working, the entire software fails. Furthermore if some functionality is
changed, it may require the entire program to be modied. Bad idea!!!
Modular design: The software is broken down into groups of functions that
can be implemented and tested independently, and are called modules. These
modules are then combined to form the whole software. This design approach
will be covered and used later in the text.
Object Oriented design: Some programming languages allow for software
to be thought of as a system of objects, with attributes, such attributes can be
manipulated and these objects interact with each other and can inherit each
others attributes just like in real life. Examples of such programming languages
are C++, Java, Python, etc. Modular and Object Oriented designs are very
good in that parts of the software can continue functioning even when others
are not. Moreover, it is easy to modify some objects, functions and modules
without having to change others.

5. Coding & Unit Testing


Once the design has been completed, the individual components (programs, functions)
of the software can be implemented (coding) and tested independently. This divide
and conquer approach makes it easy to identify problems. Furthermore, the rest
of the software components can continue to function even if one component is not.
The task of establishing if individual software components function properly is
called Unit Teesting.
There are several types of tests perforemed on the software components and some
of these are: code inspection, which involves manually going through the programs
line by line to verify their correctness (semantic, and syntax) and black box testing,
which involves giving inputs to the programs or functions and checking if they give
expected results. A rst round of such tests are often performed by the developers,
while successive tests may be assigned to trusted outsiders. The advantage of
outsiders is that they are unfamiliar with the programs and are more critical.
6. Integration, System Testing & Commissioning
When the components of the software have all been individually implemented and
tested successfully they can then be combined to collectively realize the solution
to the original problem and tested for proper functionality. If there are problems,
some of the earlier tasks are repeated to rectify mistakes. Otherwise, the software
is commissioned and handed over to the end user.
There are three phases of testing performed here and they are:
19

CHAPTER 2. INTRODUCTION TO SOFTWARE


-Testing: This is a stage of software perforemed by the development team
in a controlled environment using well chosen test scenarios and data. There
are several automatic software programs that can be used to do such testing.
-Testing: The software is subjected to tests by trusted groups of people
besides the development team. They subject the software to the likely sorts
of useage that can be expected from the end user, including illegal operations,
overloading of functionality and abrupt terminations.
Acceptance Testing: If the software passes the rst two testing stages, it
is released to the user(s) for acceptance testing. This stages reveals if the
software meets the users expectations, solves the right problems, can handle
the users load, and common illegal situations.

7. Maintenance This is perhaps the most expensive stage of commercial software. It


involves xing of errors and software defects (bugs) discovered by the end user after
the software has been handed over to the user(s).
8. Retirement/Decommissioning After some time the software becomes obsolete, and
has to be thrown out. This arises if the problem is no longer of importance, the
technology used for implementing the software becomes out dated, or any other
reasons.

2.6.2

The Waterfall Life Cycle

The dierent between the software life cycle models is mainly in the sequence in which
the stages of the life cycle are executes. In the waterfall model the stages are done one
after the other from requirements gathering to retirement as shown by the diagram below.

2.6.3

The Iterative Life Cycle

For the iterative model, the life cycle stages are executed repeatedly and each time
producing a rened product. This is illustrated in the gure below.
20

2.7. FLOW CHARTS

2.6.4

Rapid Prototyping Life Cycle

In this model a prototype of the nal product is developed and demonstrated to the
user, further work is done on the prototype developing it towards the nal product by
continually adding functionality. Most software developers employ this model without
knowing.

2.7

Flow Charts

Flow charts are a graphical representation of the set of steps taken to solve a problem
(algorithm). Flow charts are drawn as blocks connected by arrows. Blocks can represent,
a task, decision, input, output, storage, start and end. Arrows join blocks indicating
what has to be done next from one block. Few of the most commonly used blocks are
demonstrated below.

21

CHAPTER 2. INTRODUCTION TO SOFTWARE

2.8

Additional Exercises

(a) What is a complete software?


(b) How does chosen technology inuence software design?
(c) What are the advantages of breaking software into smaller programs and functions?
(d) Why is documentation so important?
(e) Draw a ow chart showing an algorithm for computing the factorial of a number.
(f) Why is monolithic design not a good practice?

22

Chapter 3
Standard C Data Types
Most C programs are written to manipulate data in some way to produce outputs. The
manipulated data is read from the input devices such as keyboard, microphone inputs,
web camera, or storage devices like CDROM, usb ash device, and others. In order to
work with such data the program needs to store it somewhere where it can be easily
accessed and modied. The device that is used by programs to store data temporarily is
called memory. An oversimplied way is to think of memory as a storage space which
can be partitioned into section of dierent sizes, as required. It is important to note
that there is only a limited amount of memory space on any computer and part of good
programming is to use such memory eciently.
A space in memory reserved for storing data is called a Variable. In order to know
where the data is stored a label is placed on the storage location, and this label is called
a variable name. Data that is used by programs takes several forms, such as real numbers,
alphabetic characters, strings of characters, boolean (1 or 0) data, and many others. Each
form of data has a specic size, that it occupies in memory.

3.1

Variable Naming

Variable names can contain alphanumeric literals such as EEE1003W. However, in C


variable names are not allowed to start with an number e.g 32Rose. Names can also
contain an underscore such as student name, but the underscore should not be used
at the beginning of the name as C reserves such for standard macros.
It is common practice to use variable names that make sense in order to make the
programs easy for humans to read e.g StudentNumber, and while at it we should note that
variable names are case sensitive, so studentnumber does not refer to the same memory
location as our previous variable.
23

CHAPTER 3. STANDARD C DATA TYPES


The C programming language has syntax which contains special keywords that are
reserved, such as int, float, double, if, for, while, do, TRUE, FALSE, etc..
The list of keywords is long, but it is easy to note them in a program because the
assume a dierent color from ordinary words when typed onto an IDE (for most IDEs).
Such keywords should never be used as variable names.

3.2

Data Types

Programming languages each have a set of standard data types and sizes that are dened
to represent numbers, alphabetic characters, strings of numbers, strings of characters.
These data types are dened to cover as much of the real world data representations as
possible or atleast enable some real world data representation formats to be represented
by combinations of such standard data types. Table:3.1 lists some of the standard data
types of the C programming language, their size (in number of bits) and what they
represent.

3.3

Formatting

Even though these data types are standard to C, the C compiler has to be told how to
deal with them (not very clever really but has its uses). The way to tell the compiler is
through directives, which are put inside double quotes and preceded by a % sign (recall
the tutorials). These are called formatting directives and are used mostly in Input and
Output functions. Table:3.1

24

3.3. FORMATTING

Table 3.1: Standard C data types


C name

Size (bits)

Formatting

Data Type

int

16/32

%d

Integer, whose size depends on computer e.g 2, 4, -5

float

%f

Single precision oating point number e.g 5.709, -2.445

double

%lf

Double precision oation point number

char

%c

Alphabetic character e.g a, c, D

32

%X

A pointer variable

bool

A boolean (binary) variable e.g 0, 1, TRUE, FALSE

u8

An unsigned 8 bit integer

u16

16

Unsigned 16 bit integer

u32

32

Unsigned 32 bit integer

u64

64

Unsigned 64 bit integer

It is very important to note that the sizes of the data types in the table depend on the
computer on which C is being used except for the last 4 types. This is why C programs
written to directly control hardware (Device drivers) on one computer may not work on
another computer if the variable size data types are used. The last four types come from
a special header le types.h. In cases where the data being dealt with comes from the
user, it is possible to establish what the data is by suing functions found in ctype.h.
Furthermore the maximum and minimum numbers values that can be represented using
a given data type on a computer can be found in the float.h and limits.h libraries.
The program below should print the maximum and minimum values represented by the
int and float data types on the computer running the program. Other limits are also
available under these header les.

25

CHAPTER 3. STANDARD C DATA TYPES

3.4

Declaration and Initialization and Assignment

In all C programs before any memory space to be used for storing data has to be reserved
before use. This process of telling the C to reserve memory space for data of a given type
is called declaration. A declaration starts with the data type, then the names of variables
of that type separated by commas, and ends with a semicolon.
DataType name1,name2,name3; e.g float temperature, height, weight;
Once the space has been reserved the the variable can be used in some operations.
However, if nothing had been stored in the reserved space yet, the value in there is
unknown and is called garbage. Using this variable in an operation gives unpredictable
results. For this reason it is important to set the values inside variables to known values
before using them, and this process is called initialization. For most of the standard C
data types, we can store a value in the variable using the equal sign =. This excludes
strings of characters (char *), for which a special function is used (strcp()). Functions
that deal with character strings can be found in string.h. We can now initialize our
previous variables as,
temperature = 5.6;
or during declaration as,
float temperature = 5.6;

Variable values can be changed after initialization. The process of changing initial values
stored in variables is called assignment and is also done using the equality operator =.
Care has to be taken that in assignment and initialization, the variable name should be
on the left and assigned value on the right of the assignment operator.
e.g temperature = 5.6 not 5.6 = temperature

3.5

Type Qualiers

The data types discussed earlier are called type speciers. There are other keywords that
are used with the type speciers in the declaration of variables in order to qualify the
type or sizes of values that the variables can take, and these are called type qualiers.
Some of the common type qualiers are given in Table:3.2.
An example of how type qualiers are used can be given of decalring two integers; one
with a constant value (constantInt) and the other is expected to change value very
frequently in the program (fastInt).
const int constantInt = 40; or equivalently int const constantInt = 40;
26

3.5. TYPE QUALIFIERS

Table 3.2: Standard C data types


C name

Size (bits)

Data Type

short

16

Short integer value

long

32

Long integer value

long-long

64

Very long integer value

unsigned

A value whose sign is always positive

signed

A value which can be negative or positive

const

Stores the initialization value forever.

volatile

Frequently changing value

volatile int fastInt;


The program below illustrates how variables are declared, initialized and used in simple
operations.
Exercise:3.5-1
(a) Why are the quotients dierent even though we are dividing the same values?
(b) Modify the program so that the initializations are done on the same lines as the
declarations.
(c) Modify the program so that it takes the divisors and dividends from the user.

27

CHAPTER 3. STANDARD C DATA TYPES

3.6

Variable Resolution and Operators

The size of variables in number of bits (n) determines the number of distinct values the
variable can store, called the variable resolution (r).
r = 2n
e.g an unsigned 16 bit integer will have 216 = 65536 dierent values ranging from 0 to
65535. Now it is worth noting that if the integer were signed, it would take both negative
and positive values and hence the resolution is spread across both sides (polarities). An
example can be made of a signed 8 bit integer, which will have 256 dierent values from
-128 to 127.
Furthermore, the type of variables dictate the operations that can be performed on its
values. Logically we can not perform any arithmetic operations on characters, and we can
not convert numbers from small case to block case. That being said, each variable type has
operators and functions that operate on them. Numeric variables can be operated on by
arithmetic operators +, , /, and functions found in math.h. Characters and character
strings can be operated on by functions found (not only) in string.h and ctypes.h,
and boolean of logic (bool) variables by logic and bitwise operators. Operators will be
covered in details in the next chapter.
Exercise:3.6-1
How many distinct integers can be represented by a long-long integer?
28

3.7. TYPE CONVERSIONS

3.7

Type Conversions

C allows the types of some data or variables to be changed from one form to the other
after declaration, as they are used. This process is called typecasting. If a variable was
declared as a float var1 = 5.876; and we want to use this variable as an integer, all
we need to do is use it as (int)var1 instead of just var1.
e.g c = 3 + (int)var1; not c = 3+var1;
It should be noted that in doing some type changes such as the one we just did we loose
data because the typecasting throws away the fractal part of the number and uses it as
just 5. The program below illustrates typecasting.

3.8

Exercises

(a) Write a program that computes the product of two oating point numbers, and
prints out the answer as an integer without making the original product an integer (do
not declare the product variable as an int).
(b) Write a program that converts the weight of entered in grams (g) to pounds (lbs) and
prints out the answer.
(c) Write a function that reads character from the user and if it is in small case prints it
out as a block letter or as a small case letter if it was block.
(d) Write a program that converts an integer number between 0 and 26 entered by the
user to a character and prints it out, or converts a character entered by the user to a
number and prints it out.
29

CHAPTER 3. STANDARD C DATA TYPES


(e) Write a program that computes the mathematical function y = cos(A) + sin(A) and
displays the output. The value of A comes from the user in degrees. (Try to use functions).

(f) Write a program that prints out the largest positive and negative exponential values
to which a double variable can be raised to.

30

Chapter 4
C Operators and Expressions
The manipulation that programs perform on data to produce results is achieved through
operators. These operators mostly take two data items and are called binary operators.
This however, does not exclude the existence of unary operators that operate on one data
item.
The type of data determines its size, in memory. Furthermore, the type determines the
operations that can be performed on the data, such as arithmetic operations on numerical
data. C operators can be grouped into ve main groups and are discussed below.

4.1

Arithmetic Operators

Arithmetic operators are binary ones that include; multiplication (*), division (/), addition
(+), subtraction (-), and modulo (%). The rst four operators are common and are
dened on all simple numerical data types. The last operator evaluates the fractal part
resulting from a division of two numbers, and operates on integer values.
When the rst four operators are applied together in an expression the Brackets, Order,
Division, Multiplication, Addition and Subtraction (BODMAS) rules of precedence
are followed. The order in which the operations should be performed should always be
specied by proper use of brackets ().
Example-4.1-1
(a) 3 + 6/2 = 6
(b) (3 + 6)/2 = 4.5
(c) 15%2 = 1
31

CHAPTER 4. C OPERATORS AND EXPRESSIONS

4.2

Assignment Operators

Assignment operators use the equality operator to store values in variables. Such vales
usually come from an arithmetic manipulation of other variables, input data from external
sources, or manipulating the value stored in the same variable and storing the updated
result back into the same variable.
The program below illustrates the use of the arithmetic and assignment operators and
brackets to force the order of execution.

In this program there are a couple of new notations that should be noted. Line 18 shows
an arithmetic operation (a + b) c whose result is assigned or stored in a variable d. It
is to be noted that the addition is perforemed rst because of the brackets even though
it has lower priority to multiplication. The expression for the printed value in line 20 is
similar to the one discussed except for the brackets, and this results in dierent outcomes.
Furthermore, it is worth noting that the result of the expression in line 20 is not stored
anywhere, and hence gets lost immediately after that line. Line 21 takes the vale stored
in d and increments it by 10, hence the expressions below are equivalent.
d+ = 10 d = d + 10
This notation can be used with most of the binary operators discussed in this chapter.
It has the advantage of being more compact, but can be confusing to individuals who
are not very familiar with the C programming language. An example can be made with
32

4.3. RELATIONAL, EQUALITY AND INCREMENT/DECREMENT OPERATORS


multiplication, and a bitwise shift operation to be discussed later.
d = 10 d = d 10
d <<= 5 d = d << 5, meaning the binary value in d is shifted to the left by 5 bits
and then stored back in d.

4.3

Relational, Equality and Increment/Decrement


Operators

Relational operators are binary operations used to compare (relate) two values. The
compared values can be variables, constants, or evaluated expressions. The operators
are >=, <=, <, <, and have the same precedence and a higher priority to equality ones
==, ! =.
These operators are mainly used in conditional execution, or loop structures which are
subject of the next chapter. The program given in subsection 1.4.4 illustrates the use
of these relational operators. Increment operators are the ++, for decrementing
and incrementing a value by 1 respectively. It is important to note the dierent use of
these increment operators as pre-increment/decrement and post-increment/decrement.
For instance,
int a = 5, b;
b = ++a; increments a and stores result in b
b = a++; stores the value of a in b and then increments a

4.4

Logical Operators

Logical operators are the logical AND (&&), and OR ( ) used as conjectures for checking
multiple conditions. Such conditions are specied using relational operators. An example
can be given of a situation where we want to check if the value of an expression x2 + 2
is between values 5 and 10 including 10 itself, this can be specied as: ((5 < (x2 +
2))&&(10 >= (x2 + 2))). Evaluation of this expression will give a TRUE (1) result if the
conditions are true of FALSE (0) if they are not. The program below illustrates the use
of relational, assignment, increment/decreemnt, equality and logical operators.

33

CHAPTER 4. C OPERATORS AND EXPRESSIONS

4.5

Bitwise Operators

Computers process data in binary format. This involves data storage, manipulation and
transmission between devices inside and outside the computer system. Sometimes it is
more convenient to deal with the data in binary format in our programs. C provides
operators for performing most of the boolean operations and other operators needed to
manipulate binary numbers, and these operate on all data types that have integer values.
These are listed in the Table:4.1 and explained.
The rst three operators are binary operators, while the last three are unary. These
operators are used mostly in programming hardware registers, for toggling certain bits
in a register. C provides conventions in which these operators can be used to manipulate
only certain bits and leave others as they are. The program below illustrates the use of
some of these operators.
Example:4.5-1
The results of some boolean operations are given below. The subscripts on the numbers
represent the base to which we are counting (2 for base 2 or binary, 10 for base 10 or
decimal).
(a) 101101002 |000110012 = 101111012
34

4.5. BITWISE OPERATORS

Table 4.1: Bitwise Operators


Operator

Purpose

Bitwise OR

&

Bitwise AND

Exclusive OR

>>

Shift right

<<

Shift left

Ones complement inverse

(b) 101101002 &000110012 = 000100002


(c) 101101002 000110012 = 101011012
(d) 101101002 >> 3 = 000101102
(e) 101101002 << 2 = 110100002
(f) 101101002 = 010010112
(g) 510 |710 = 000001012 |000001112 = 000001112

The example and program above operate on all the bits in the numbers. It is possible to
apply an operation only on one bit of a given binary number. This is done by knowing
remembering that taking an OR of any number with zeros leaves the number unchanged,
and AND of a number with ones leaves the number unchanged. Therefore, the operations
below will only change one bit in the rst number.
(a) 10112 &11012 = 10012 changes bit 1 (counting from 0 right to left).
(b) 10112 |01002 = 11112 changes bit 2.
This demonstrates that the AND operator can be used to turn bits OFF and the OR
35

CHAPTER 4. C OPERATORS AND EXPRESSIONS


operator to turn them ON.
Exercise:4.5-1
(a) Compute the results of the owing boolean operation
( (1310 |910 ))&710
(b) Write a program that takes a positive input decimal (base 10) integer from the user
and converts it to binary form and prints it.
(c) Modify your program in (b) to take two positive decimal integers, convert them to
binary, multiply them and convert the answer back to decimal and print both the binary
and decimal answer
(d) Write a program that takes an input integer from the user and classies it as either
an odd or even number

4.6

Functions in Standard Libraries

To complete the picture, some operations on data are performed through standard functions
dened in the C Standard Library. To use these functions the appropriate header les have
to be included in our program. For manipulating numerical data int, double, float,
etc, the math.h, float.h header on one of the libraries, and for characters and character
strings string.h, ctype.h, stdio.h. The reader is urged to familiarize themselves with
functions found in the dierent C libraries. Converting between, data types can also be
achieved by some functions found in stdlib.h.
Exercise:4.6-1
(a) Search the standard library to nd a function that generates random numbers and
write a program that prints 10 random numbers.

4.7

Extra Exercises

(a) Write a program that takes an integer 4 bit from the user, sets the most signicant
bit (MSB) or bit 3 to 1 and prints the resulting integer.
(b) Write a program that counts the number of ones in an 8 bit integer entered by the
user and prints them.
(c) Write a program that allows the user to enter two complex numbers, then adds,
divides them, and computes their magnitudes and displays them.
(d) Write a program that takes two integers a and b and computes the function
c = (a) + b100
36

4.7. EXTRA EXERCISES


Be very careful about the completeness of your program.

37

CHAPTER 4. C OPERATORS AND EXPRESSIONS

38

Chapter 5
Execution Flow Control
So far we have established that every set of C programs should contain the main(){ }
function in order for it to be able to do some productive work. The reason for this is
that the compiler looks for this function rst when the program is run and executes the
rst instruction found in it. If there are no special control structures in the program, the
compiler will run all instructions inside the main() function one after the other from the
rst to the last one before the closing } and then stop. In order to determine or change
the way the C programs execute the instructions in a program special control instruction
and structures are used, and these are, instructions to quit, pause, continue, return,
break, loops, conditions, and functions. Some of these with the exception of functions
are discussed in this chapter in details.

5.1

Program Statements and Blocks

For ease of explanation and understanding, programs contents can be classied using the
terms dened in the subsections that follow.

5.1.1

Statement or Instruction

In C an instruction or statement is any line of text in a program that is terminated by a


semicolon (;) and not preceded by // or enclosed within /* */. Examples of statements
are:
int a, b, c;
c = a + b;
printf ();
return c;
39

CHAPTER 5. EXECUTION FLOW CONTROL

5.1.2

Block of Instructions

In the tutorial chapter we dened and used some functions which we dened as a group
of instructions that are grouped together to perform a task. A function is an example
of a block, another block is formed by if(){ }else { } conditionals and other loop
structures. More formally a block is group of instructions grouped together (with { })
for a purpose, not would not function in the same manner if not grouped.

5.1.3

A Source File

When we type our programs, we call the resulting text les source les. These are the
.c, .c++, .py les. In C these are the les that contain the denitions of functionality.
There is another form of C le called a header le, some of which we have already used
in our #include<.h> statements, and will be discussed at a later stage.

5.2

Conditional Execution

Apart from the normal execution procedure explained in earlier. It is possible to decide
how many times a block of instructions is executed, and under what condition. Some
of the execution conditions can be decided before hand such as the number of times to
request input from the user. However, some conditions can only be asserted while the
program is already running. C provides us with a means to specify all these execution
patterns using standard programming constructs discussed below.

5.2.1

if(){ } else{ }

This programming construct is specied as: if(CONDITION){BLOCK1} else{BLOCK2} and


understood to mean that BLOCK1 should be executed if CONDITION is true or else
BLOCK2 should be executed. The CONDITIONs are specied using Logical statements
such as: age>65. It is possible to check multiple conditions, with the expectation that
one of them be true if(CONDITION1
CONDITION2
...){BLOCK1} else{BLOCK2}, or
all of them be true in order to execute BLOCK1 and this is specied as if(CONDITION1
&& CONDITION2 && CONDITION3 && ...){BLOCK1} else{BLOCK2}.
Alternative to the aforementioned uses of the conditional if(){ } else{ }, several if()
checks can be performed before the else{ } default. When this happens the computer
checks each option and executes every BLOCK associated with a true CONDITION.
Sometimes it is required that if one condition is true all others not be checked. In this
case, all if() checks after the rst one should be used as else if(). Check this eect
40

5.2. CONDITIONAL EXECUTION


by removing the else with the second if() in line 13 and check the output when the
age is 12.

5.2.2

switch(){ }

A commonly arising situation is the need to check multiple conditions. An example of


this is taken from a program that assigns a grade letter to students given a percentage
mark below. This program is long and can get potentially convoluted.

41

CHAPTER 5. EXECUTION FLOW CONTROL

A compact alternative to writing out multiple if() statements is to use the


switch(VALUE){case(EXPRESSION): { }} conditional. A program for classifying animals
is written as shown below, to demonstrate the use of a this construct. The break;
instruction forces the program to stop checking the rest of the conditions if the current
condition is true. Otherwise, if a condition without an associated break; instruction is
found, the program will continue to check and execute any other true conditions.Usually,
the compiler will give an error if more than one case: statements share the same
condition. In the use of if() conditionals the default action taken is specied within
a else { } environment. For the switch() conditional the default action is specied
using the default: { } environment. It should be noted that the VALUE used in the
switch() conditional should and the EXPRESSION in the case() should have integer
value, otherwise the compiler will complain.

42

5.2. CONDITIONAL EXECUTION

Note that multiple cases can lead to the same action being taken, and these are specied
as:
case(EXPRESSION1): case(EXPRESSION1) : ... :{ }
Exercise:5.2-1
Write the program that takes a single upper case letter fro A to E and prints a single
word starting with such a letter, using the switch() conditional.

43

CHAPTER 5. EXECUTION FLOW CONTROL

5.2.3

Conditional Value Use

An alternative use of conditions is specied as (CONDITION ? b : c). This simply


means; if CONDITION is true, use a otherwise use b. This can be used in assignments,
printing and other operations. An example is given in the program below. Remember
that the %s is a formating directive for character strings.

5.3

Nested Conditionals

Conditional programming environments can be used inside other conditionals, when this
happens, care has to be taken to make sure that the if() else blocks are well matched.
An else { } block will always associate with the if(){ } after which it immediately
comes. Referring back to the program in section 4.4, we can see that the else { } on
line 23 is associated with the if() in line 20, and not line 19 which is matched to line 27.
The program is a typical use of nested conditionals and the switch(){ } can be applied
in a similar manner.

5.4

Repeated Compilation

It is sometimes necessary to repeat a task for a specic number of times or while a given
condition is true. When such a situation arises C provides programming environments
that enable the program to repeat the same actions, and check conditions to see if they
are still true, and these environments are called loops. Under normal circumstances,
loops should have conditions, which when violated (false), the looping should stop. This
condition is called an exit condition. Otherwise, if there is no exit condition, the repetition
will go on forever, forming an innite loop.
44

5.4. REPEATED COMPILATION

5.4.1

do{ } while()

A do{ACTIONS}while(CONDITION) tells the computer to keep performing the ACTIONS,


and at the end of each run of the ACTIONS check if the condition is still true. If the exit
condition is true, repeat ACTIONS, else terminate the loop. This being the case it is easy
to see that this loop will run atleast once, which is the rst run before the condition is
checked. Most of the conditions used in these loops require a question-answer interaction
with the user to determine if the loop should continue or stop. In such cases, and others
the loop structure does not need the variables in the condition to be initialized before
the loop, because they can be set inside the loop during the rst unchecked run.

5.4.2

while(){ }

If it is required that the ACTIONS not be taken at all if the condition is false, then the
rst thing that needs to be done is to check the condition, then if it is true, carry out the
ACTIONS, otherwise skip the actions. This is the ow of events taken by a while(){}
loop, specied as:
while(CONDITION){ ACTIONS }
In this loop structure, the variables used in the CONDITION need to be initialized
rst in order to ensure predictable behavior. The do{}while() and while(){} loop
environments can function on a counter, or non-conting based condition. These will be
clear after the next loop structure.

5.4.3

for(){ }

A direct equivalent of a while(){} loop is the for(){} loop, which is mainly used in
counter based repetitions where, each time the loop runs a counter value is incremented
or decremented until it reaches a given maximum or minimum, at which the loop quits.
The structure of this loop is:
for(VALUE;CONDITION;UPDATE){ACTIONS}
It says; start at VALUE, check CONDITION, and if true, perform the ACTIONS or skip
if false, and each time UPDATE the VALUE, and check CONDITION. If condition is still
true repeat, otherwise terminate the loop. This loop requires that VALUE be initialized
to some known value at the beginning of the loop.
45

CHAPTER 5. EXECUTION FLOW CONTROL

5.4.4

label, goto label;

The last form of repetitive execution structure is provided by the LABEL: goto LABEL;
construct. This looping method is not very popular among C programmers. In it, labels
are placed at desired locations in the program terminated by a colon (:). These labels
can take any name, following similar rules to those of variable naming. At a desired
location in the program, another line instruction is placed that says got LABEL;, and
forces the program execution to jump back and start executing an instruction that comes
immediately after the LABEL.
The program below demonstrates the use of these loops structure. Careful attention
should be paid to the way they are written in a program.

46

5.5. NESTED LOOPS AND CONDITIONALS

5.5

Nested Loops and Conditionals

In the same manner that we did with conditionals, loop structures can be placed inside
other loops. When this happens, each single time the outer loop is run, the inner loop is
repeated until it terminates. This is demonstrated in the program below that adds the
sums of all the numbers smaller than the even numbers in the range n-0 where n is a
positive integer entered by the user. For example if n = 5

sum =

[j]
i=0,ieven numbers j=0

sum = {0} + {0 + 1 + 2} + {0 + 1 + 2 + 3 + 4} = 13

47

CHAPTER 5. EXECUTION FLOW CONTROL

5.6

Extra Exercises

(a) Write a program that takes a positive integer n from the user and computes its
factorial, then displays the answer on screen.
(b) Write a program that can lists all the odd integer numbers between the positive
integers numbers M IN and M AX entered by the user.
(c) Write a program that continually takes the weight in kilograms [kg] and height in
meters [m] of users, computes and displays their Body-Mass-Index (BMI) and recommends,
how much weight they should gain or loose in order to be in the normal BMI range of
18.5[kg/m2 ] to 24.9[kg/m2 ]. The equation for BMI is:
BM I[kg/m2 ] =

weight[kg]
(height)2 [m2 ]

(d) Write a function that prints a circle of n symbols where n is an even integer entered
by the user and is greater than 2.

48

Chapter 6
Input Output
In most practical computing problems, one hardly nds stand alone software that does
not need to take input data from some other sources other than the keyboard and mouse.
Most software applications get input data from text les, disk storages, and external
sensing devices such as microphones, thermocouples, and give outputs to other external
actuating devices examples of which are DC motors and loudspeakers.
To this end we have only written programs that take input from a keyboard, and give
outputs to the computer screen (monitor). A good thing is that computers treat most
Input and Output (IO) devices similarly as just les that can be read and/or written
to. In this chapter we will introduce the concept of reading and writting data from and
to text les. This is done using two methods; redirecting and direct le access. In
addition to these we will introduce the concept of command line argument passing.

6.1

Output Redirection

Using redirection, computer programs are written as usual, without any special constructs,
and compiled. However, the running of the resulting executable has to be done on a
command window, and the outputs or inputs redirected to or from a text le. This
redirecting is achieved using the > and < command line operators for output and input
redirecting respectively. The operators function similarly on Windows and Linux systems
and use a format given below.
executable.exe > textFile.txt
As an example we look at a program that writes Hello EEE1003W!! into a text le
called OutputFile.txt. The command window clip below shows how the executable is
run and the created text le.

49

CHAPTER 6. INPUT OUTPUT

The program below demonstrates how a string can be redirected from a text le as an
input to an executable and printed on the command window.

The command window clip below shows the results of the redirection. Note that the text
le used is InputFile.txt and was manually created before running the program using
a text editor (notepad in this case).

6.2

Direct File I/O

The redirection method has a problem of writting every output of the program into the
text le. In cases where the output that is being logged into the le is used by other
programs, we need to write only the useful data and not the rest of the compiler messages
50

6.2. DIRECT FILE I/O


such as Press any key to continue . . . . C allows us to directly access text les and other
IO devices in the following procedure.

Open the le: we get a le discreptor (le pointer).


Give the necessary settings (congurations).
Use the le (Read, Write and Append).
Close the le.

When opening a le, C gives us information about the le (address, writing/reading


status, etc). This information is stored in a le discreptor (le pointer). Therefore,
before opening a le, a pointer of type FILE is declared.
FILE *fd; // Declaration of a file pointer
When opening a le the result of the operation (a le pointer) is assigned to fd.
fd = fopen(fileName, access method); // Open a file
The le name can have any text extension (.txt, .dat etc) or no extension. The access
methods eld species if the le will be used for writting (w) reading (r) or appending
(a). It is to be noted here that if a le by the le name already exists it will be opened,
failing which, a new le will be created. If the le is being opened for reading only r
then, it has to exist already. Otherwise C will not create it, but give a segmentation fault
error. An example is made here of a le opened for reading and writting.
fd = fopen(myTextFile.txt, wr);
Once the le has been opened with the right permissions and settings, we can read from
it, write into it or append according to the permissions. These are done with similar les
to those used before but prexed with f: fprintf(); fscanf(); fgets(); fputs();
etc. The dierence with these functions is that in addition to the usual parameters given
to them, we need to provide the le discreptor of the le to be used for IO. The program
below illustrates the operations of direct le IO. Note that at the end of our operations
the le is closed by a function that takes the le pointer as its input.
fclose(fd);
If we do not close the le, other programs that need to use it will think our program is
still busy with it and not open it to avoid conicts.

51

CHAPTER 6. INPUT OUTPUT

When using the keyboard and the screen for IO, the compiler automatically puts the le
discreptors for us if we do not do so manually. The le discreptor for these are stdin
and stdout respectively. Therefore the instructions below are equivalent:
fprntf(stdout, Hello EEE1003W!); printf(Hello EEE1003W!);
and
fscanf(stdin, %s, &name); scanf(%s, &name);

6.3

Command Line Arguments

In addition to the input output methods explained so far, there is another way of giving
inputs to a program. This method is called command line passing of arguments. A
program is written and compiled, when the executable is run on the command window
it is followed by arguments in the manner shown below:
hello.exe Sammy
52

6.4. OTHER FORMS OF I/O


When this happens, the main() function is responsible for capturing such inputs and
giving them to the program. In order to do so the main() function is used as:
int main(int argc, char *argv[])
The rst integer input (argc) stores the number of command line arguments passed, and
the second one (argv[]) stores each argument in a compartment of the array. These
arguments can be accessed and used in the main le as demonstrated in the program
below.

It should be noted that the rst element of argv[] contains the name of the executable
itself. That is why the rst argument is accessed from location 1 of the array.

6.4

Other Forms of I/O

The other forms of input/output forms, not addressed in this manuscript are through
data acquisition devices which can communicate with the outside world of the computer.
Such devices have specic functions provided to manipulate them called device drivers.
However if the devices are custom then the designer needs to write such functions. The
second method is data access from a database, which is a software storage of data on
a computer in a non volatile manner. Dierent database technologies are available
such as SQL, mySQL, Oracle and others and these form enough content for a series
of undergraduate courses.

53

CHAPTER 6. INPUT OUTPUT

6.5

Extra Exercises

(a) Write a program that reads and displays the number of characters and words in text
le created by the user. The program must allow the user to enter the name of the text
le.
(b) A sudoku game is presented by a matrix of data forming 9 rows and 9 columns in a
text le. Write a function that reads the game entries from the le and stores the game
in a global multidimensional array. The program should take the name of the text le
containing the game from the user.
(c) Write a program that prints out a solved sudoku game onto a text le. There should
be a single space between each row entry and one empty line between lines.
(d) Write a simple mathematical calculator program that works on the command line.
This program should take three command line arguments: operation, operand1 and
operand2 and print the answer to each of the following operations: add, subtract, divide
and multiply, for two arguments, e.g. program.exe add 2 4 should give 6.
(e) Write a program that scans a text le to nd a word or phrase provided by the user
in the le. It should print each text line in the le containing the phrase.
(f) Write a program that scans a text le and checks that every word after a period (.)
begins with a capital letter and prints out each sentence containing a violation of this
requirement.

54

Chapter 7
Functions
Sometimes a large task can be broken into smaller sub-tasks that can be performed
independently and their results used in other sub-tasks. If such tasks are implemented
as programs, the sub-tasks can be implemented as functions. In short, a function is a set
of instructions grouped in a block to perform a well dened task.
Decomposition of large programs into functions hides away the unnecessary complex
implementation details that make it dicult to understand the program. Furthermore,
if a task is performed by a function, its implementation can be changed without having
to change the entire program.
It is good practice to implement frequently performed tasks as functions. In this way
the task is implemented once and used at many places, and by many other programmers.
Examples of functions that have been dened and we use frequently are the input/output
functions found in stdio.h, and some mathematic functions found in math.h. C allows
programmers to dene custom functions and use them as well as share them with other
developers. Implementation and use of such functions involves four steps: prototyping,
declaration, denition and function calling.

7.1

Function Denitions

An example of how a function is dened in C was provided in the short tutorial, and is
repeated here. In essence a function can be considered as a black-box that takes some
inputs (variables) and may give out an output. Functions also have names by which they
can be referred to during use. Consider a function that takes two float input variables
named; I for current and R for resistance, uses them by Ohms Law to compute the
potential drop across the resistance V = I*R; and then gives the computed voltage (V)
as the output.

55

CHAPTER 7. FUNCTIONS

The function has the format:


ReturnType FunctionName(DataType1 inputName1, DataType2 inputName2){
DECLARATIONS;
INSTRUCTIONS;
return RESULT;
}
The ReturnType tells us the data type of the result from the function e.g float. The
FunctionName gives us a way to refer to the function. This functions takes two inputs
of data types DataType1 and DataType2. Functions can take more input variables or
none. The last line in the function is a return RESULT line that passes the result of the
function to the external world. The data type of RESULT should always be the same as
ReturnType. If the ReturnType is not specied, the function is taken to return an integer
by default in C.
Some functions do not return any value to the outside world and are dened with a return
type void. An example of a function with no return result and taking no inputs would
be stated as:
void FunctionName(){

}
The C programming language does not allow functions to be dened inside other functions.
This therefore means that in the simplest case, no function should be dened inside the
main(){} function.
56

7.2. FUNCTION PROTOTYPE

7.2

Function Prototype

A function prototype is a way to tell the compiler that such a function exists and therefore,
no error should be raised when the function is used. At this point the compiler does not
need to know what the function does. The structure of a C function prototype is provided
and explained in the text that follows. A function prototype is needed if the function
denition does not appear before the function is used.
ReturnType FunctionName(DataType1 inputName1, DataType1 inputName2, ...);
What the format means is that, if we give inputs inputName1, inputName1, ... of
specic data types, to a function called FunctionName, the function will give a result
of data type ReturnType when it is done computing. A function prototype is always
terminated by a semicolon. The names of the input variables need not be specied in the
prototype, but just their type.
ReturnType FunctionName(DataType1, DataType2, ...);
If a function is implemented in a dierent source le than the one we are working in, to
use such a function, there is a need to declare it, in order to tell the compiler what the
return type of such a function is, otherwise the compiler my take it to return an integer
by default. A function whose return type is not specied in C is assigned an int return
type by default.

7.3

Return Types and Input Variables

Instead of giving variables to functions, and using variables in rueturn statements, we


can replace such with expressions. When this is done such expressions should evaluate
to a result of the same data type as the variable that would have been used there. Using
the example of the voltage computing function the following return statements would be
equivalent.
return V; return (I*R);
The () brackets around the expression can be omitted, but it is always advised that they
be used. The same format can be used when the function is used.
57

CHAPTER 7. FUNCTIONS

7.4

Calling Functions

Once a function has been dened, it can be used inside other functions or itself. The most
obvious way is to use it inside main() { }. The use of a function is termed a function
call. When a function is called it should be provided with the inputs it needs (correct
data types and cardinality), and the result returned from the function can be stored in
some variable in the calling function or used directly in an expression or print function.
A complete example of functions demonstrating the prototype, denition, and calling is
provided below. The reader is advised to pay attention to the syntax.

58

7.5. VARIABLES AND SCOPE

7.5

Variables and Scope

One advantage of using functions in C is that the rest of the program does not know
what happens inside a specic function. A function is a black-box which may give some
outputs when given inputs. The variables used in programs are of two types.

7.5.1

Local Variables

If a variable is declared inside a function, such a variable is not visible and can not be
used outside the scope of such a function. Variables declared inside functions are called
local variables. An advantage of local variables is that they are created when the function
is run and freed when it stops running, therefore saving memory space. Examples of local
variables are float V; in the compute voltage() function. Since this variable is local
we can use the same variable name in another function such as main(){} without any
conicts.

7.5.2

Global Variables

If on the other hand, a variable is declared outside all functions, such a variable is called
a global variable. Everytime that variable is referred to in any function, it refers to the
same value. The advantage of external variables is that if functions need to exchange
lots of data, such data can be stored in global variables instead of giving a long list of
inputs to functions. The down side is that as the amount of shared data and hence global
variables increase, the program can get potentially messy. The program below illustrates
the use of global and local variables. The reader is advised to apply these concepts in
the voltage computation problem as an alternative to giving inputs to the function and
letting it return a value of the voltage.

59

CHAPTER 7. FUNCTIONS

7.6

Recursive Functions

As stated earlier in this chapter, after a function has been dened it can be called or used
in other functions. The second alternative is to call the function within itself. When this
happens the function is called recursive because it is dened by calling itself. Recursive
functions nd application in incremental tasks in which one starts at some value and
keeps applying the same operation on the previous result until some condition is met.
The most important aspects of recursive functions is that they must have an exit condition,
which stops the execution when it is true. The second component is that the function
should be called somewhere within its body. An example illustrating an incremental task
is the computation of a factorial function on a positive integer. This is illustrated by
example of an integer n = 5.

n! =

and

0! = 1

5! = 5 4 3 2 1 = 120
= 5 4! = 5 (4 3 2 1)
= 5 4 3! = 5 4(3 2 1)
= 5 4 3 2! = 5 4 3 (2 1)
= 5 4 3 2 1! = 5 4 3 2 (1)
A recursive function takes the general format:
ReturnType FunctionName(Inputs){
if (EXIT CONDITION){
return VALUE;
}
else{
INSTRUCTIONS;
return (function self call);
}
}

7.7

The C Preprocessor

The building of C programs happens in two main steps; the rst step is the building of
the components or instructions specied by #include statements such as the included
header les and the #define statements which are used to dene constants, constant
60

7.7. THE C PREPROCESSOR


expressions and macros. All these components are then integrated into the parts of the
main programs which are built second.
The preprocessor serves to include functions that have already been dened in some
libraries such as ctype.h or user dened libraries. The second function is the denition
of frequently used program constructs. These are discussed below.

7.7.1

Including Files

Two forms of les can be included in C programs. These are standard header les from
the C standard library, included using angle brackets #include<>. We have used some
of these in our programs. The second types of les are header les for libraries dened
by the user, and these are included using double quotes as #include"user defined.h".
User dened libraries and header les are subject of later chapters.

7.7.2

Dening Constants, Constant Expressions and Macros

If a constant value is used frequently in a program such as = 3.14159265358, re-typing


this long number can be cumbersome, leading to numerical mistakes. It is often a good
idea to just assign a symbolic name to the value. One way of dening a symbolic constant
was discussed in chapter 3 using the const float pi = 3.14159265358; type qualier.
This is done in the program. An alternative is to dene such a constant in the preprocessor
as #define pi 3.14159265358;.
By the same token, constant values computed from constants can also be dened in the
preprocessor as:
#define A 2;
#define B 5;
#defined C A+B;
here C is dened as the constant expression A + B. If the expression made up of variables
instead, then it is implemented as a macro. A macro is not a function, but a description
of text to be substituted in the place of the macro name. Macros are used similar to a
function as demonstrated below. The program below illustrates the use of a macro to
print a message telling the user if a user entered character is a small or block letter, and
another one for determining the minimum of three numbers.
The format of a macro denition is:
#define MIN(a,b) ((a>b)?b:a)
61

CHAPTER 7. FUNCTIONS
This can then be used in a similar fashion as a function, by giving it the inputs a and
b in the program and assigning its result to some variable. Note that no data types are
specied for the variables used in the macro denition.

7.7.3

Conditional Denitions

The danger in dening new constants, macros, and header les is that the names we choose
for them may have already been used in our C implementation. When this happens our
new denitions may replace the other existing ones, and this is undesirable. In order to
avoid such conict, conditional denition is used. This uses done as follows:
#ifndef MY HEADER H
#define MY HEADER H

PROTOTYPES, VARIABLES, NEW DATA TYPES etc.

#endif
All the rst line does is check if similar named constants and macros exist. If they do
our new ones are not dened, otherwise our denitions are accepted, and the last line
closes the conditional environment. It is advisable that this conditional denition be used
always to avoid any potential conicts.

7.8

Exercises

(a) Write a program that takes two fractions (numerator and denominator), and implement
functions that implement addition and subtraction for these functions and give an answer
out. Hint: global variables will be handy.

(b) Write a function that asks the user how many numbers the user wants to sort, allows
the user to enter as many numbers, then sorts them out and prints them starting with
the smallest to the largest. Test your function

(c) Without using the library complex.h, write a set of functions that can be used for
complex number arithmetic (add, subtract, multiply, divide), and test them. Hint: global
variables may be handy.

62

7.8. EXERCISES
(d) Write a function that takes two integers and swaps them around and prints them to
conrm the swap.

(e) Dene a macro that implements the absolute value (positive) of any real number and
test it.

63

CHAPTER 7. FUNCTIONS

64

7.8. EXERCISES

65

CHAPTER 7. FUNCTIONS

66

Chapter 8
The C Software Structure
Up to this point we have explored some basics of C programming; variables, operators
and expressions, controlling the ow of execution as well as dening and using functions.
All of the above have been done in a single C source le (.c), in what is called monolithic
programming. Real life problems can become so large and complex that implementing
them in a single C program can be a very daunting task, and a very bad software
development practice. Furthermore, we may be required to implement some functions
that are going to be used by some other programmers software, in which case these
functions need to be accessible for calling outside our own programs. In this chapter we
are going to introduce the concepts of header les and modular programming.

8.1

Simple Program: Single Source File

Single le .c programs have been studied since the beginning of this text. All in all, they
have been observed to have the structure outlined below.
The rst section of the le contains inclusions of standard library header les. The
second involves including user dened library header les, which are subject of the this
chapter. Third are the preprocessor denitions of constants and macros, followed by
global declarations of variables and function prototypes (or even function denitions).
The main(){} function follows and other function denitions can be found after it.
The only problem is that the functions and variables declared and dened in this program
can only be used inside the program. What happens when we want to share such functions
with our colleagues?
67

CHAPTER 8. THE C SOFTWARE STRUCTURE

8.2

The Header (.h) File

A header le is more formally dened as a public interface for other .c les to gain access
to some functions and variables declared and dened in a given .c le(s). It is a mechanism
by which we can share the functions and data we dene in or .c les with others, while
keeping some data and functions secret.
In order to achieve this sharing, all the function prototypes, declarations of global variables
and denition of constants and macros that we want to share are moved from our .c le
to a separate le (header) with an extension .h. This le is then included in our .c le
where the denition of the prototypes are done, and other hidden functions. All other .c
les that want to use our shared functions and data should also include our .h le. This
is illustrated by the programs that follow.

68

8.3. COMPILING AND RUNNING A MODULE

8.3

Compiling and Running a Module

In order to compile multiple source les with header les in most IDEs such les have to
be part of the same project. This means that instead of our usual steps of creating new
les, we follow the steps below.
1. File->New->Project, then select Empty Project, the C programming language
option and name our project (no need to specify the extension).
2. File->New->Source File, the IDE will ask if you want to add the le to the project,
click yes. Save the le as a .c or .h according to your need.
3. Type the contents of your programs, then compile and run as usual.

69

CHAPTER 8. THE C SOFTWARE STRUCTURE

8.4

Structure of C Software

C software can be decomposed according to the tree diagram below. The focus of this
chapter is in dividing C programs into modules. These modules consist of a header le
and source le(s). An example of a module is given in the previous subsections.

70

8.4. STRUCTURE OF C SOFTWARE

A Software development approach in which complex tasks are broken down into groups of
functions that are implemented, tested and packaged independently is called a modular
design. This is a good design due to its divide-and-conquer simplicity, ease of modifying
one module without having tho change all others, hiding of implementation details of
each module, and ability to share functionality.

71

CHAPTER 8. THE C SOFTWARE STRUCTURE

72

Chapter 9
Arrays
Variables have been declared and used in our C programs, and we understood them as
reserved spaces in memory to store data values. The size of the reserved space (variable)
depends on the data type to be stored in it, specied during declaration. So far we have
dealt with individual data storage allocations. If contiguous memory space is reserved
for 5 integer variables, it is easy to note that, starting from the rst integer location and
skipping space enough to store one integer we end up at the second integer location and
so on. This is depicted by an illustrative memory diagram below.

Figure 9.1: Illustration of a block of indexed contiguous memory (array)

9.1

What is an Array?

C provides a way in which these integers can be seen as indexed sections in a block of
contiguous memory called an array. In order to access one section we need to specify
the array name and the section number (numbers run from 0 to n-1). This is illustrated
in Figure:9.1 above. An array is not a variable but a collection of variables sharing the
same name. An array can only contain data elements of the same type, and can have any
arbitrary size. Arrays can be quite important in making cleaner programs, manipulation
of character strings and enabling programs to return multiple values as shall be seen.
The objective of this chapter is to give a detailed treatment of arrays and their uses.
73

CHAPTER 9. ARRAYS

9.2

Declaration and Initialization

The declaration of an array has to specify three pieces of information; the type of data
to be stored (DataType), the name of the array (ArrayName) and the number of data
elements to be stored in the array (ArraySize).
DataType ArrayName[ArraySize] e.g int A[5];
Depending on the type of data to be stored in the array several methods of initialization
exist. These range from a list of comma separated values that initialize each compartment
of the array.
int intArray[3] = {3,5,7};
char charArray[5] = "Sammy"; or char charArray[5] = {S,a,m,m,y};
After declaration and initialization, the values of the array compartments can be set
individually by using the array name and index. It is to be noted that C uses indices
running from 0 to n 1, where n is the size of the array. This is illustrated below for our
previously declared and initialized integer array.
printf("%d",intArray[1]); //prints the second element of the array 5
intArray[2] = 2; //assigns a value 2 into the third compartment of the array
intArray[0] = intArra[1]+intArray[2];
If all elements of an array need to be set, a loop structure such as a for() loop can be used.
Alternatively, there are special functions for assigning values to certain types of arrays.
Character arrays can be manipulated with functions found in string.h. This include
functions for copying, comparing, computing the length for character arrays (strings) and
others.

9.3

Characters and Character Strings

Text and character strings manipulation forms a big part of computing, from simple
character and word counting, spell checking to complicated speech to text converters. One
way of handling strings in C is by representing them as arrays of characters. Input/output
formatting of strings is the %s, which is used with the printf(); and scanf(); functions.
Other functions that are used for string input/output are the gets(); and puts();, and
they are by no means the only ones.
In this representation, characters in a string can be accessed simply as array elements,
manipulated and stored back. The only problem with this approach is that the size of the
array (string) has to be known at declaration or a conservatively large array is used to
avoid running out of storage. It is to be mentioned here that the character set does not
74

9.4. MULTIDIMENSIONAL ARRAYS


constitute only alphabetic letters but also constant characters used to mark white space,
end of line, null character and many others. The reader is encouraged to do an internet
search on the ASCII table to familiarize themselves with the character set. Each string
is terminated by a null character \O, hence we can write a simple function that takes
a string and determines its length, including white spaces.

A similar function is dened and called strlen(); and found with others such as
strcpy(); for copying a string into another, strcmp(); for comparing two strings and
strcat(); for concatenating strings as well as others in the C standard library string.h.

9.4

Multidimensional Arrays

If instead of one block of variables, the array is formed from several (say 5) blocks of
equal size, the array becomes a multidimensional array or a matrix. In order to index
such an array we need three pieces of information; the array name, row number and
column number. These array types are declared and initialized as shown below.
float floatArray[3][3] = {{2.4, 0.5, 2},{1.6, 3.8, 0},{5.1, 4.4, 3}};
Each row of the matrix is initialized by a comma separated list of values in { } and
the rows initializations are separated by commas too. The declaration and initializations
75

CHAPTER 9. ARRAYS
above result in.

2.4 0.5 2
f loatArray = 1.6 3.8 0
5.1 4.4 3

In order to access an element of the matrix for manipulation or printing, the array name,
row and column need to be specied. An example is given below.
floatArray[1][1] = 10; //sets the element on row 2, column 2 to a value 10
Manipulations of multidimensional arrays involve nested loop structures.

9.5

Use of Arrays

In using arrays it is to be noted that an array is not a variable, but a group of variables.
Therefore, simple operations can be performed on the individual elements depending on
their type but, these simple operators studied in previous chapters can not be applied
directly to arrays. This is mainly due to the fact that arrays can have dierent types and
sizes, that my cause problems in both unary and binary operations. The program below
illustrates the use of arrays.

76

9.6. EXTRA EXERCISES

9.6

Extra Exercises

(a) Write a program that counts the number of words in a string of 50 characters entered
by the user.
(b) Modify your program in (a) to replace all the rst letters in the words with a block
letter if it is a small letter and a small letter if it is a block letter.
(c) Write a program that takes two, 2 by 2 matrices from the user, adds them, multiplies
them and displays their sum and products (use multidimensional arrays).
(d) Write a protram that takes a string from the user of less than 20 letters and prints it
vertically. e.g. John as:
J
o
h
n

77

CHAPTER 9. ARRAYS

78

Chapter 10
Pointers
In the previous chapter we dealt with arrays. The manipulations done on arrays depend
heavily on the address of the rst element of such an array. In actual fact, the name of
an array is mapped onto the address of the rst element of the array, i.e &intArray[0].
When an element at index 2 is specied intArray[2], the computer starts from the
address of the rst element and skips two integer spaces after that address and we get
to the desired location intArray[2]. The exact details of how this happens and the
equivalence of arrays and pointers are subject of this chapter.

10.1

What is a Pointer?

A pointer can be dened as a variable that stores the address of another variable. A
pointer is in the format of an integer (often 16 bits). This is perhaps the most interesting
subject of the C programming language and the most used in advanced programs. While
pointers are very good to use in our programs, they can often lead to disaster if used
carelessly. The idea of a pointer is illustrated pictorially in Figure:10.1
An analogy here can be made of the postal service; in order for items to be delivered to
the correct destination, the address of the destination needs to be known. An address
directory book can be seen as our pointer which contains such addresses. Furthermore, a
street can be considered as an array of homes; we only need to know where the street starts
(address of rst home) and we can go from one house to the next by simply indexing.

10.2

Declaration and Initialization

A pointer is declared with a data type, name, and a leading asterix:


int *myPointer;
79

CHAPTER 10. POINTERS

Figure 10.1: Illustration of a variable that stores the address of (points to) another
variable (a POINTER)

What should be noted is that the data type is not the type of the address but the type
of the data to be stored at the address stored in the pointer. From the illustration in
Figure:10.1 the int would be the type of the variable A. Initialization of a pointer uses
the & unary operator which, in this context means address of.
int A = 10; //Declare and initialize an integer variable
int *P; //Declare a pointer .
P = &A; //Initialize the pointer with the address of an integer A
After the declaration and initialization the pointer P now points to an integer variable
A, or the variable P now stores the address of variable A. Note that we are calling the
pointer P a variable.

10.3

Using Pointers

Dened more formally there are two important pointer operators, and these are:
Address Operator: This is a unary operator that is used to get the address of a variable
in memory. It is used as & which should be read as address of. We have already seen
the use of this operator in the function scanf();, and recently for initializing a pointer,
80

10.4. ARRAYS POINTERS


and we will see it in use again later in a program.
Value Operator: If we now have a pointer to a variable, to get the value stored in that
variable using the pointer, the unary value operator * is used. This therefore means
that from our previous declarations and initializations printf("%d",*P); would print 10
which is the value stored in A. Note that now the data type of the pointer is not included,
otherwise int *P would mean a declaration.
Apart from the above emphasized operators, we mentioned earlier that addresses are
stored as integer values. As a result arithmetic operators are allowed on such addresses.
These include adding/subtracting an integer from P, incrementing and decrementing.
Division and other operators that result in non-integral results are however not valid. An
example can be given for incrementing the value of a pointer (variable address) by 2.
P = P+2;//equivalent to skipping 2*16 bits
This simply means that we should replace the address stored in P by the address that is
two integer spaces away from the current address. So, incrementing an integer address by
two will give a dierent answer to incrementing a long int address by the same value,
since a long integer space is about twice the size of the integer space (skip 2*32 bits). We
can apply the P++; P--; --P; and ++P; operators on the address stored in P.
Since we said that *P gives the value of A, and A is an integer, this means that we can do
to (*P) anything we could legally do to A. It is recommended that the brackets always be
used to avoid confusion with the order of execution. For instance printf("%",*P++);
would print the value in A and then increment the address, while printf("%d",*(P++));
would print the value stored in the next integer space from A.

10.4

Arrays Pointers

While not exactly the same, the ideas of arrays and pointers are equivalent. The mapping
is basically established in that the name of an array is a pointer to the rst element of
such an array. Therefore, if a pointer is declared and initialized to the rst element of
our array the pointer name and the array name can be used almost seamlessly.
char *decoy, name[5] = {S,a,m,m,y};
decoy = &name[0]; // or decoy = name;

The following expressions are equivalent:


decoy[0] = T; name[0] = T; // replace S with T
*(decoy+2) = r; name[2] = r; // replace m with r
*(name+3) = r; decoy[3] = r;
81

CHAPTER 10. POINTERS


If however, the pointer int *decoy was initialized to a dierent location in name[], say
decoy = &name[2] then the following would apply;
decoy[0] = T; name[2] = T; // replace m with T
*(decoy+2) = r; name[4] = r; // replace y with r
name[0] = r; *(decoy-2) = r; // replace S with r
since the decoy pointer is now initialized at the middle of the name array. With this
initialization of decoy it is however illegal to try to access *(decoy+4) or *(decoy-4)
because these memory locations have not been reserved before, so we are not allowed to
use them. Furthermore, even if the compiler allows it we do not know what is stored in
these spaces and tempering with them can crash other computer programs by modifying
their data. The program below illustrates these concepts and is well commented.

10.5

Character Strings

In the previous chapter it was illustrated, how character strings can be represented
as character arrays of nite length. A second notation is presented in this section for
declaring and using character strings. In this approach the idea is that we can declare a
character string as a pointer to a character, and the other characters that follow can be
found by indexing from the rst character. Some compilers handle memory allocation for
the string while others require such memory to be explicitly allocated by the program.
char *myString = "Jack"; //String declaration and initialization
Note that a similar initialization can be done to array strings. In addition to declaring
a pointer to the rst character J, the compiler creates space for the remaining three
characters of the assignment. However trying to access memory locations outside the
82

10.5. CHARACTER STRINGS


span of the initialization string will crash the compiler. In the short program below the
commented line tries to store a character at location 10 of the string and the compiler
will crash because such space was not reserved prior to storage. Reading from memory
locations outside the allocated area my work but it will give unpredictable results.

C allows us to allocate the right amount of space to pointer variables. This is done using
the functions malloc(); and free();, other functions for this task calloc();. The
dierence is that calloc(); initializes the newly reserved space while malloc(); does
not. These functions are found in stdlib.h.
As an equivalent to declaring an array of 10 characters one can declare a pointer to a
memory block of 10 character spaces. An example is made here of declaring a pointer
that points to the rst of ten characters.
char *myString = malloc(10*sizeof(char)); char myString[10];
C also makes it possible to resize the initial space allocated to a pointer and this is done
using the function realloc(). For instance if we wanted to increase the space allocated
to myString to 20 spaces, we would write:
myString = realloc(VOID, 20); // increasing memory allocation
If there is not enough contiguous space immediately after the initially allocated space,
this function will nd a large enough contiguous space, copy the original data in the
original pointer to to this new extended location and free the original pointer.
It is important that all manually allocated memory space be freed at the end of its use.
This is done with the use of a free(); function that takes as an input the name of the
allocated pointer, so we would have to write:
free(myString); //free allocated memory
Otherwise, we will eventually run out of memory because we committed all of it. This
is called memory leakage and is a very serious problem with careless use of pointers and
manual memory allocation. The program below illustrates the use of the above concepts.
83

CHAPTER 10. POINTERS

10.6

Variable Use by Value and Reference

In the preceding chapters, we wrote functions that took some variables as inputs and
returned some variable as the output. In all that we have done even if the function
changed the values in the input variables, such a change would not remain after the
function is done executing. The values of the input variables remain as they were before
the function was called, because the function is only given copies of the variables. When
this happens the input variables are said to passed by value. Consider the program
below that is written to swap the values of two integers.

84

10.6. VARIABLE USE BY VALUE AND REFERENCE


If on the other hand we give to the function, addresses of the variables as inputs, then
we enable the function to modify the original variables and hence, the change stays even
after the function is done executing. This is now called passign variables by reference
to the function. The swap program is now written using addresses of the variables and
their values remain swapped even after the function execution.

This can be illustrated by an example of a bird and its reection in watter. To a child
who only sees the birds reection, they can not do anything permanent to the bird by
playing with the reection, because when the water settles again the bird will still be
there at the same position. However, if they now have access to the bird itself, they can
move it to another location or paint its feathers to a dierent color.

85

CHAPTER 10. POINTERS

10.7

Extra Exercises

(a) Write a program that takes the names and surname of an individual, all in one input
string and prints out their initials and surname, e.g Mohohlo Samuel Tsoeu, and prints
MS Tsoeu.

(b) Write and test a function that takes an input string of less than 20 characters converts
the case of all alphabetic letters and prints it out in the main() function. do not use
global variables. e.g takes SamMy and prints sAMmY.

(c) Write a program that takes 6 real numbers from the user, sotres them in an array,
then write a function that takes the array as an input, sorts the numbers from largest to
smallest and prints the them out in that order.

(d) Write a program that counts the number of white spaces in a string of less than 50
characters and prints the number out.

86

Chapter 11
Composite Data Types - Structures
One of the challanges regarding functions that have been encountered so far is that they
can only return one variable or value. Should one need to return multiple values from a
function, golobal variables and passing variables by reference have been our only solutions.
In addition to that most programming languages provide basic standard variable types,
which do not cover all forms of real life data encountered in computing.

11.1

What is a Structure

In the event that a function needs to return multiple values or variables, and/or the
basic data types do not give the programmer a convenient form of representation, then
structures come to use. A structure is a group of variables that can be refered collectively
as one structure. Once a structure has been dened, the new compound data type can
be used more or less in a similar manner as other data types.

11.2

Structure Properties and Functions

An example of a scenario in which the basic data type do not provide a covenient for is that
or representing points in an n-dimensional space (vectors). To illustrate the application
real 2-dimensional points (in R2 ) with real x and y coordinates are used. Therefore, a
point can be represented as a group of two float or double variables.
Having formed a new data type (structure), it is necessary to dene functions and
operators that can be performed on the data. Without these, the only way to manipulate
such structures is by manipulating their constituent variables individually. An example
of this is addition of two points or computing the Eucledian distance between two points.
87

CHAPTER 11. COMPOSITE DATA TYPES - STRUCTURES


It should be stated clearly that C is not an object oriented language, therefore some of
the fancy concepts of C++, Java, and other object oriented languages such as; private,
public, protected member variables, private functions, inheritence and polymorphism are
not available.

11.2.1

Denition, Members and Type Dening

Below is denition of a structure representing a point.

The struct keyword tells the compiler that we are dening a new structure, named
point, and within the opening and closing brackets are the variables that are grouped
together (members). After the closing bracket, the typedef keyword tells the compiler
that the new structure is dened into a new data type called point. The name of the
structure and the new type name chosen need not be the same.

11.2.2

Operators

In order to access the individual members of a group (structure) a notation using the
. notation is used e.g if a point is declared as point A;, we can assign a value to the
x-coordinate of the point by using a notation similar to:
A.x = 3.4;
which means the x-member of the point structure. The use of this notation will be
demonstrated in code segments in the subsections that follow.

11.2.3

Methods

Below we have dened two functions; one for displaying points such as A = (3,6.5),
and the second for adding two points.
In order to avoid forgetting to initialize members of structures, it is often necessary to
dene functions that declare and initialise structures to some base values. To illustrate
this we can dene a function that creates a point variable and initializes it to coordinates
(0,0).
In addition to this some structures do not have xed sizes, such as a matrix or vector
88

11.2. STRUCTURE PROPERTIES AND FUNCTIONS

structure. In order to enable the user to give the size of the structre upon creating it it
is necessary to dene a function that creates such a structure.

89

CHAPTER 11. COMPOSITE DATA TYPES - STRUCTURES

11.3

Using Structrures

The declarations of structures take the same format as all other basic data types, and
the initialization follows the array format of a comma separated list of values. The code
segment below illustrates the declaration, initialization, and use of newly dened functions
that can take point(s) data as inputs and return a point. Note that by returning a point,
we have eectively returned two variables of type double (x and y).

90

11.4. POINTERS TO STRUCTURES

11.4

Pointers to Structures

Structures have been presented as a way of grouping basic data types and dening their
combination as new composite data types. These can then be used in a similar manner
to the standard C data types, besides the fact that ordinary oprators are not dened on
them. By a similar token, pointers to the newly dened data types can be declared and
used. An example is made here of a pointer to a struter of type point dened earlier.
point A, *P; //Declare a variable and a pointer
P = &A; //Initilize the pointer to address of A

11.4.1

Syntax

As with ordinary poiters, there are two most inportant unary operators. If we wanted
to obtain the address of a point variable say A, then we could use the unary address
operator &A.
If on the other hand, following from the declaration and initialization seen earlier we,
want to access the x-coordinate of the point A by using the pointer P , the syntax would
be as shown below,
(*P).x = 3.4; //Note the use of brackets
The syntax above has a less cumbersome equivalent given below,
P->x = 3.4; //Note the -> notation
91

CHAPTER 11. COMPOSITE DATA TYPES - STRUCTURES

11.4.2

Use of Pointers to Structures

A simple use of the pointer concepts and syntax is demonstrated by adding some functionality
to our previous main() function.

A program example that uses the concepts presented above is given in a program that
takes two point variables A and B and swaps their coordinates.

11.5

Arrays of Structures

In a similar manner that pointers are used in conjunction or alternatively with arrays the
same principles apply with newly dened data types. An array of 5 data points can be
declared as:
92

11.5. ARRAYS OF STRUCTURES


point myPoints[5]; point *Points = malloc(5*sizeof(point)); // remember
to use free(Points);
It is mentioned here again that in the same way that the pointer and array syntax formats
were used interchangeably before, nothing has changed. The program below demonstrates
the interchangeable array and pointer syntax.

93

CHAPTER 11. COMPOSITE DATA TYPES - STRUCTURES

11.6

Extra Exercises

(a) Write the a function that computes the distance between two points.

(b) Using the point structure and the distance functions, dene a new structure that
represents a triangle. Dene functions for computing the area covered by a triangle

(c) Dene a structure to represent student entity with the ollowing members: Name,
gender, age, student number. Write a function that takes the attributes of ten students
and stores them in a global array.

(d) Write a function that takes an input of type vect and prints it out as a row vector,
e.g 1 2 3 4 . Note that the input vectors will be of dierent sizes. Hint: use the
sizeof() function and the knowledge that the data member of a vector stores values of
double precision.

94

Chapter 12
Advanced Concepts in C

12.1

Make Files

12.2

Function Pointers

12.3

Callback Functions

12.4

User Dened Libraries

12.5

Abstraction

12.6

Bug-Free C

95

CHAPTER 12. ADVANCED CONCEPTS IN C

12.7

Extra Exercises

96

Das könnte Ihnen auch gefallen