0% found this document useful (0 votes)
11 views61 pages

Handbook C

The Handbook of the Physics Computing Course at the University of Oxford outlines the structure and content of the course for the 2004-2005 academic year. It includes sections on the C programming language, system usage, and various programming concepts such as variables, loops, and file I/O. The document serves as a comprehensive guide for students to navigate the course and develop their computing skills.

Uploaded by

Janet Pam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views61 pages

Handbook C

The Handbook of the Physics Computing Course at the University of Oxford outlines the structure and content of the course for the 2004-2005 academic year. It includes sections on the C programming language, system usage, and various programming concepts such as variables, loops, and file I/O. The document serves as a comprehensive guide for students to navigate the course and develop their computing skills.

Uploaded by

Janet Pam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 61

Handbook of the Physics Computing Course

2004-2005

University of Oxford
Sub-faculty of Physics
CRW/AOH
2
Contents

1 Introduction 5
1.1 The Computing Course and Laboratory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 The C course . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 The marking system and use of your logbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.4 Hilary term . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.5 The C language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.6 Typographical conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2 Learning to use the system 9


2.1 Logging in and getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2 Terminal and the Unix command-line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 The C programming environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.1 Creating a new program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.2 Compiling, running, and debugging your programs . . . . . . . . . . . . . . . . . . . . . 11
2.3.3 Opening and Closing existing programs . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4 Case sensitivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5 Logging out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3 The elements of C 13
3.1 Program Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1.1 Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1.2 Code layout and style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.1 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.2 Declaration and Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.2.3 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3 Output using printf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4 Input using scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.5 Arithmetic Expressions and Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.5.1 Type conversions and casts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.5.2 Some other operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.6 Expressions, statements, and compound statements . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.7 if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.7.1 Relational operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.7.2 Logical operators and else-ifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.8 Loops and Iteration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.8.1 for loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3
3.8.2 while loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.8.3 do...while loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.9 Mathematical functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.10 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.10.1 Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.10.2 Array elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.10.3 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.11 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.12 File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.12.1 Writing to Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.12.2 Reading from Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4 Further C 41
4.1 Random Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.2 Multi-dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.3 String manipulation functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.4 switch statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.5 More about variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.5.1 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.5.2 Global Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.5.3 Storage classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.6 The C Pre-Processor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
4.7 struct, typedef and Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.8 Taking your interest further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

A Graphical output using Gnuplot from within your program 53

B Programming from the Unix command-line 57


B.1 Command-line editing and compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
B.2 Advanced Compilation using make . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

C Reserved Words and Program Filenames 59

4 Contents
Introduction 1
1.1 The Computing Course and Laboratory
Being able to program a computer is a vital skill for the modern physicist. The first year computing course is
designed to teach computer programming in a scientific context, assuming no prior knowledge of computing. The
programming language you will learn during this course is called C.
All undergraduates are required to attend the computing course in their first year. You will spend four half-day
sessions in the Computing Lab during Michaelmas term. Sessions take place either in the morning (10:00–13:00)
or the afternoon (14:00–17:00). People from either session may also work during the lunch-hour.
You will work singly, not in pairs as you will do in other parts of the Practical Course. Computing will
contribute the equivalent of 4 days to your Practical Course record: 2 days in Michaelmas and 2 in Hilary.
During your four sessions in the Computing Lab, you will be using a Sun computer system called rayleigh.
The computer runs Solaris, which is Sun’s version of the Unix operating system. Unix differs in several ways from
the operating system that you are most likely to be familiar with, Microsoft’s Windows. However, Physicists often
use Unix in preference to Windows because, once you get used to it, it is a more powerful and flexible environment
for doing scientific and technical computing; it also tends to be more reliable (and is not prone to viruses!). If you
have encountered Linux, then you have effectively already seen a Unix system. Apple’s new operating system,
Mac OS X, is also based on Unix.

1.2 The C course


In addition to acquainting yourself with the system, you will also learn the basics of computer programming in C.
You will do this by working your way through to the end of Chapter 3 of this handbook, reading the explanations
and attempting most of the exercises (some exercises have parts (a) and (b): in these cases (b) is optional and
intended for students with previous programming experience or those wanting more of a challenge). If you have
any trouble, please don’t hesitate to speak to a demonstrator: they are here to help.
We anticipate that you will finish the exercises in this book towards either the end of your second session or the
beginning of your third. Don’t worry if you finish earlier or later. If you think you have finished the handbook then
talk to a demonstrator who will assess your progress and then record your completion of the handbook’s exercises
as CO00 (gaining you a day’s practical course credit). The demonstrator will then select one of the following
problems for you to solve over the remaining sessions:

CO11 Quadratic Equation; straight line fitting by least squares

CO12 Nuclear decay and the Doppler effect

CO13 Numerical Integration by Simpson’s rule and by a Monte Carlo method

CO14 Solution of non-linear equations; solution of a differential equation

CO15 Polynomial curve fitting by least squares

CO16 Graph plotting; Fourier Analysis

1.3 The marking system and use of your logbook


You are encouraged to use your logbook whilst you are in the Computing Lab. Keeping a logbook whilst
programming is just as important as when doing experimental work. When you have completed the programming
problem (CO11–CO16), you will be marked by a demonstrator, who will expect you to explain in detail how your

5
program works; they will also test your program by running it. So it is in your own interests to keep a logbook as
it will help you to explain your work to the demonstrator.
The kind of things that could be recorded in your logbook include:

• The outline design of your program;


• Steps you took which aren’t obvious from the problem script;
• Notes on any problems encountered as you tried to make your program work, and your solutions;
• Hints from demonstrators;
• Input data and output results, including graphs.

See also Section 1.3.2 of the Practical Course Handbook for more details on keeping a logbook.

1.4 Hilary term


In Hilary term you will be expected to solve a more challenging programming problem. By the end of Michaelmas
Term you should have received an email informing you of which problem you will have to do. You can then
collect the appropriate script from the foyer of the Practical Course. The problem must be completed and an
account written up and submitted to the Practical Course technicians’ office (DWB Room 206). You will be
informed about the submission deadline when you are assigned the problem. Section 1.3.5 of the Practical Course
Handbook explains what to include in an account.
The Computing Lab will be available for you to work on your problem throughout Hilary term. There will
be a demonstrator present on Thursdays and Fridays to give assistance and advice to first years working on
programming problems. First years will have priority on these days; other years nominally have priority on
Mondays and Tuesdays. No-one has priority on Wednesdays when the Lab may be used freely. However, at
all times, people wishing to do serious work have priority over those web-browsing or using email.
On Thursdays and Fridays in Hilary term access to the computers will be subject to availability rather than by
formally-booked sessions. In practice, though, there are always likely to be machines available in the Computing
Lab during Hilary.
You will also be free to use your own or college computing facilities, if you have access to them. Further advice
about installing suitable software to allow you to program in C on your own computer will be made available in
due course.

1.5 The C language


This course will only teach you a subset of the C programming language. Nevertheless, in doing the course you
will acquire a solid foundation of skills required for programming in general, as well as those applicable to C itself.
C is a very powerful and versatile language which was originally developed in conjunction with the Unix
operating system, more than 30 years ago. It is still a popular and widely used language. For instance, many of the
physicists working in the department use C (or C++) as their primary programming language. More importantly,
though, its compact efficiency and versatility make C highly-suited not only to scientific programming, but also for
writing systems software, applications and general-purpose programs. In addition to being a useful and popular
language in its own right, C has also given rise to two1 other widely-used languages: C++ and Java. So another
benefit of learning C is that a good foundation is gained for subsequently learning C++, Java, or indeed any of the
derived languages2 .
Whilst C is not the easiest language for a beginner to grasp, nor is it the most difficult. For those with
previous programming experience, it should certainly be straightforward to learn. In any case, once it has been
1 There are several other direct C derivatives including Objective-C and the new Microsoft language C#; C has also had an influence on

more modern languages such as Perl and Python and it is intimately connected with Unix operating systems (including Linux and Mac OS X).
Knowing about C therefore gives an added insight into many aspects of modern computing.
2 If you already know any C++ (or Java) please do not use any constructs that are not part of Standard C. This course deliberately only

considers the international standard (ISO) version of C. Therefore you should also not use any platform-specific extensions such as those found
in Windows C compilers (eg. Turbo-C or Visual-C).

6 Chapter 1. Introduction
mastered, C constitutes an extremely powerful programming tool and one that is standard across a wide range of
computing platforms. C is typically the first (and sometimes the only) language made available on a platform:
from the smallest embedded micro-processors, to Windows PCs and Macs, Linux and Unix workstations, right up
to mainframes and super-computers. Your efforts in learning the language will be well-rewarded.

1.6 Typographical conventions


This handbook uses the following typographical conventions:

Most of the document appears in the Times font.

Typewriter font is used for anything typed, e.g. C code or Unix commands.

Sans serif is used for menu and application names

New terms and jargon are written in italics the first time they are used.

1.6. Typographical conventions 7


8 Chapter 1. Introduction
Learning to use the system 2
This chapter contains essential information that you will need to be able to do the course, such as how to login and
logout, and how to use the system to write and run C programs. There are also some other generally-useful pieces
of advice and information. To gain experience quickly and learn how to use the system you will write a simple
program. Don’t worry if you don’t understand all of it, it will be explained in section 3.1, for now just follow the
instructions.

2.1 Logging in and getting started


Sit at one of the Sun terminals. Move the mouse: the screen should come on; if it doesn’t after several seconds,
press the power button in the bottom-right corner of the monitor, so the green LED is lit.
You should see a login panel: using lower-case, enter your username where indicated and then press Return
but before you enter your password, ensure that the graphic on the right says “Solaris Common Desktop
Environment”. If it does not, then click and hold the Options button, and select Common Desktop Environment
from the Session menu. Now enter your password and press Return. Ask a demonstrator if you need help.
The login panel should disappear and after a few seconds, you should see various windows appear on the
screen. You will need to use the Netscape web browser, so if its License Panel pops up click the Accept button.
A Netscape browser window will appear displaying the Computing Lab web page. A collection of useful links are
on the left hand side of this page; click on the link labelled ’Introduction for First Year Students’: work through
this web document NOW (you can return to this manual when you have completed it).
If, for any reason, the browser window has been closed you can access the Computing Lab page again at:
http://www-teaching.physics.ox.ac.uk/computing/

2.2 Terminal and the Unix command-line


Minimise the Netscape window by clicking on the “dot” button at the top-right of its window. You can also
minimise the Help Viewer and File Manager windows in the same manner.
You should now be left with a single Terminal window. If you do not see a Terminal window, then you can
get a new one at any time by clicking once on the Terminal icon towards the right of the front panel at the bottom
of the screen (it should be the third icon from the right and depicts a computer monitor with keyboard).
Click in the Terminal window so its border turns dark pink (this indicates that you can type things into it).
Terminals provide access to what is known as the Unix command-line, or shell. (You will also be using the
command-line should you remotely login to the system). A Unix shell is a bit like the DOS-prompt in Microsoft
Windows, but it is far more powerful and flexible: it makes available hundreds of commands, utilities and programs.
The shell indicates that it is ready to receive commands by displaying the following prompt:

rayleigh%

Some basic help and information about Unix commands is available by typing help in a Terminal window.
Initially, though, you will not need to use many Unix commands because we have provided an integrated system
for editing, compiling and running your C programs called SciTE. However, SciTE isn’t available on all Unix
systems and you may not be able to use it if you want to work via a remote login to this system; hence it will
be useful to know a little bit about how to write and run programs from the Unix command-line: instructions are
given in Appendix B.

9
2.3 The C programming environment
The environment in which you will be writing, compiling and running your C programs in this course is called
SciTE (Scintilla Text Editor) 1 .
A useful way in which the SciTE editor makes writing C programs easier is that it helps to lay out your code
for you. C is a free-format language which means that you have great freedom over how you lay out your code.
However, it is still important to lay out your code logically; this is a particularly necessary habit to acquire when
programming in C because it renders code much more readable (this is discussed further in Section 3.1.2).
SciTE will indent lines of code that appear in a compound statement. If you don’t like the way SciTE lays out
your code you can use the Backspace key to remove any indentations.
SciTE will also highlight your program to assist you as you write it: C keywords it recognises will be coloured
blue while other words will be black; quoted text will appear dark pink and comments in green; unmatched brackets
are red and numerical constants cyan. This may help you to spot mistakes in your program.
To start up SciTE type scite at the shell prompt and press the Return key:

rayleigh% scite

Note: scite is typed entirely in lower-case, this is essential and will be explained in section 2.4.
At this point, you may wish to minimise the Terminal window (by clicking on the “dot” button at the top right
of the window); do NOT close it or SciTE will be killed!
After a few seconds a new window called “(Untitled) — SciTE” will appear. The window has two panes: the
upper one is the most important because it contains SciTE’s editor. (An editor is an application, rather like a simple
word-processor, which can be used to write any kind of text including programs. You will be using the editor to
create and save new programs, and then to open, modify and save existing stored programs).
SciTE’s editor has been customised to make it particularly useful for writing C programs, and it should be fairly
intuitive to use. Documents are opened, closed and saved by selecting commands in the File menu. This manual
will explain the use of SciTE in terms of its menu commands. There is also a graphical “Tool Bar” which can be
activated from the View menu and clicking on the Tool Bar button; it lets you perform many of the operations
described below and some people may find it more convenient.

2.3.1 Creating a new program


Selecting New from the File menu will display an empty file in the current window, called “(Untitled)”. You can
go back to a previous file by selecting its name in the list of open files near the bottom of the File menu (there can
only ever be one “Untitled” file open: to get another one, you must first save the current one).
Type the following program into the SciTE editor.

#include <stdio.h>
#include <stdlib.h>

int
main()
{
printf("Hello world\n");
exit(0);
}

Save the file by selecting Save from the File menu. The first time you save a file, Save will actually behave
like Save As; i.e. the Save File As panel will appear. Enter hello.c in the text field at the bottom of the panel
and then click OK. It is important to know that C programs must end with “ .c”. On subsequent occasions, Save
will simply save the file. Once saved, the program remains on-screen for further editing if you wish.
1 If you are already familiar with Unix or Linux you may alternatively use your favourite text-editor to write your C programs (remember

to save them with a .c suffix), and then compile and run them yourself using the GNU C compiler, gcc (see also Appendix B). However, we
recommend you try using SciTE because it has some nice features which make C programming a little easier.

10 Chapter 2. Learning to use the system


Note: Do NOT save a program with the name test.c – it will not work! (There are some other program
names to avoid, such as if.c, which the demonstrators will be able to advise you about – see Appendix C).
You should also avoid using spaces in your program names (you can use the underscore character instead, “ ”); in
general it’s best to use just letters, numbers, and underscores.

2.3.2 Compiling, running, and debugging your programs


A compiler is a program which translates the source code of programs you create, into machine code which the
computer can understand and run. So, before the system can run your program it must compile it. In fact, the
life-cycle of every program involves the following general stages:

• The source code is written to a file using an editor (you have already done this)
• The source is translated into machine code (usually with a compiler)
• The machine code is run or “executed”

During development most programs will be found to have errors or bugs and so the programmer must pursue
the edit-compile-debug cycle, testing and debugging their program until a satisfactory result is achieved.
To compile a program select Compile from the Tools menu, or press Control and F7 together (SciTE will
automatically save any changes you’ve made prior to compilation). You will see a line appear in the bottom panel
of SciTe that looks like:
> gcc -Wall -O filename.c -o filename -lm

If your compilation was successful, this will be followed by “ Exit code: 0”. In the above command, gcc
invokes the C compiler and -Wall -O are options passed to the compiler: to report all warnings and to use code
optimisation. filename.c is the name of the file containing the program to be compiled (the source code) and
is followed by -o and the program name but without the .c. This tells the compiler to create an executable (ie.
a version of the program that the computer can run) with the same name as your program less the .c. Finally the
-lm tells the compiler to link in the mathematical library. This library isn’t always needed, but it is safe to include
and is essential for programs which use mathematical functions.
Depending on whether your program has any mistakes (bugs), either the compilation or the run may fail,
constituting compile-time or run-time errors respectively. If you see error messages in the output pane at the
bottom of the main window at compile-time, you can double-click on one, and the line containing that error will
be identified with a yellow circle and selected for you. If there are several errors, select and correct just the first
one and then try recompiling. Quite often a single bug will be the cause of a series of errors, which will disappear
when it is corrected.
Run-time bugs (ie. bugs that don’t appear until you run the program) are generally harder to spot: you will
need to check carefully through your code. If you can’t find a bug, do ask a demonstrator for help.
To run a program that you have successfully compiled, select Go in the Tools menu (or press F5). The results
of running the program will appear in a new Terminal window. Once you have finished running your program you
should press the Return key to close the Terminal window and return to SciTE. You can also run a program from
the command line (from a Terminal window) by typing “ ./” followed by the executable name at the command
prompt; eg. ./filename
Sometimes running programs can stop responding: if this happens click Stop Executing in the Tools menu.
Try compiling and running your program now. If you have any difficulty ask a demonstrator. (Note that the
parenthesised character after the word “ exit” in the example program is a zero, “0”, not a capital oh, “O”).

2.3.3 Opening and Closing existing programs


You can retrieve a previously saved program by choosing Open from the File menu and selecting the file to open
in the panel which appears. To select a different directory, double-click on its name. To go back up a level, either
use the pull-down menu towards the top of the Open File panel, or double-click on the directory called “ ../”.
If you have finished editing your program then choose Close from the File menu. If there are unsaved changes
you will be prompted to save them.

2.3. The C programming environment 11


If you are satisfied that the program you wrote in the last section worked you may close your SciTE session.

2.4 Case sensitivity


An operating system or programming language is said to be case-sensitive if it differentiates between words which
contain the same letters but which vary in terms of whether they are typed in upper-case or lower-case (even if only
a single letter is in a different case).
You have probably used Microsoft Windows: in general it is not case-sensitive. Solaris, in common with all
Unix operating systems, is essentially always case-sensitive. For example, see what happens if we capitalise the
scite command:

rayleigh% SCITE
SCITE: Command not found
rayleigh% Scite
Scite: Command not found

Unix commands tend to be written entirely in lower-case (though sometimes upper-case characters are used as
well). Most modern programming languages (eg. C, C++, Java, Python, Perl, etc.) are case-sensitive too. When
writing programs in these languages you have to use lower-case much of the time.
In C there are a few exceptions to this rule. The most important examples you will encounter in this course
are: “ FILE”; and #define constants which are written in upper-case by convention. The basic rule is that you
should type commands in exactly as they appear in this manual.

2.5 Logging out


Whenever you have finished using the system, you should ensure that you logout to make your Sun Ray terminal
available for someone else to use. Before you do so, make certain that you have saved any changes you have been
making in any editor windows or other applications; you should also Exit from Netscape (see the bottom of its File
menu).
To logout, click on the Exit button which is under the small globe icon near the centre of the icons along the
bottom of the screen. You will be asked to confirm that you wish to logout: click OK to confirm logout. As
part of the logout process, all applications you have running will be quit for you. The CDE environment will
also remember which applications you were running in your session, and where their windows were situated on
the screen: the next time you login, it will try to recreate the session; however, it will not remember any Unix
commands, such as scite, which you may have been running from within a Terminal.
(Note: Netscape is automatically launched every time you login; if you leave it running when you logout you
will get two copies the next time you login which will provoke a Netscape panel to pop-up, warning you that
someone else may already be running Netscape in your account: if this ever happens just click on the Cancel
button in the panel).

12 Chapter 2. Learning to use the system


The elements of C 3
These notes give a basic introduction to programming using C and should contain all the information you need for
the first-year course. Since C is such a popular and widely-used programming language there are many sources of
information about it, both online and in book form. A list of online C information sources is available on the first
year course web page.

3.1 Program Structure


3.1.1 Hello world
You have already written, compiled and run a simple C program:

#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("Hello world\n");
exit(0);
}

Though very short, this requires some explanation. C programs are organised into structural units known as
functions. Functions will be discussed in detail in Section 3.11, for now it is sufficient to know that functions are
used to perform tasks. We define functions by stating

• its return type, the type of data the function will return once it’s finished its task,
• its name, in this case “ main”,
• followed by parentheses ( ), which may or may not contain arguments,
• followed by a series of one or more statements enclosed within braces { }.

Statements are the instructions which tell the computer what you actually want it to do. We have stated that
main() will return an int (ie. an integer) when it finishes. This is not strictly essential because all functions are
assumed to return ints unless specified otherwise, but it is nevertheless more correct to be explicit. Don’t worry
if you don’t understand what this all means now: it will make sense later once you’ve tried a few examples.
In principle, there is no upper limit on how many functions a C program may contain, but there is a minimum
constraint: every program must include a function called main(). The main() function is special because C
programs start running at the beginning of it and finish once they reach its end. Interestingly, the main() function
need not be at the start of a program: indeed it is not unusual to find it at or near the end.
The first two lines,

#include <stdio.h>
#include <stdlib.h>

contain a special kind of statement known as a pre-processor directive1 : these ones tell the compiler to include
at that point in the program information about the libraries named inside the <> brackets. stdio.h contains
information about the standard input/output library, whilst stdlib.h does the same for the standard C library.
1 Any line which begins with a hash “ #” character is a pre-processor instruction or directive: the C pre-processor is a useful if slightly

eccentric feature which is covered later in section 4.6.

13
These two libraries are so significant and the things they contain so frequently used that you should always include
these two lines in your programs.
Moving on to the first line of code inside the main() function: printf() is a standard library function
(ie. a function which has already been defined and made available as part of the C language) which is used to print
formatted output to the screen. Once a function has been defined it is used, or called, by typing its name followed
by parentheses. Pieces of information, known as arguments, may be passed to a function – this is done by placing
the arguments inside the parentheses.
In this case we created a sequence or string of characters by enclosing them in double-quotes, and told
printf() that we wanted it to print them by passing the string as an argument. The “\n” is known as an
escape sequence and is used to indicate a newline character. Though it may at first seem a bit clumsy and cryptic,
this way of formatting output is actually very powerful and precise, giving complete control over exactly what gets
printed. printf() is used a great deal and will be described in more detail in Section 3.3.
Finally, rather than just letting the program end by reaching main()’s closing brace, }, a call to the standard
library function exit() has been used. Calling exit() means that the system does a bit of tidying up and
returns the exit code it is passed (in this case 0) to whatever ran the program. exit() may be called from
anywhere within a C program, and provides a useful way of quitting a program that has encountered an error
or other problem. It also allows your program a means of reporting whether or not its run was successful: by
convention exit code 0 means “successful completion”; non-zero codes (typically -1) usually denote an abnormal
termination of some sort, but you can choose whichever values you wish to use and assign them whatever meaning
you like.
Notice also the punctuation: semi-colons, “ ;”, are used to terminate each statement within the function. This
does not mean that every line of a C program must end in a semi-colon (for example it would be a mistake to
terminate the line “ int main()” with one). By following the examples in this handbook, and with practice it
will become apparent when they are required; forgetting them is a common mistake made by beginners, so don’t
get too frustrated if you do.

Exercise 3.1
Use SciTE to modify your hello.c program to print out a short message, perhaps ”Hello”
followed by your name.

If you get errors when you try to compile or run a program, then check it carefully for small mistakes like
missing punctuation marks (especially the “;”s on the ends of lines). Even seemingly trivial mistakes can cause
big problems in computer programs!

3.1.2 Code layout and style


C is a free-format language which means that you have great freedom over the layout of your code. White space
(ie. spaces and tabs) are generally ignored by the compiler, and even newlines are mostly2 ignored. In particular,
you have the freedom to insert blank lines to space out logical sections of your code and to split long statements
over several lines. It is imperative that you use spaces or tabs to indent blocks of statements within braces { }. In
practice we recommend 2 to 4 spaces, because tabs can end up pushing you too far over to the right. As mentioned
in Chapter 2, SciTE will automatically help you with your code layout, including indentation. The demonstrators
can also guide you on good practice.
As an example of how unimportant layout is to the compiler, the Hello world program could perfectly legally
be written as follows (the C pre-processor statements are not free-format: they must still be written one per line):

2 There are a few instances in C where newlines are important: eg. they are illegal within strings (as already mentioned); they are also

required to terminate pre-processor statements.

14 Chapter 3. The elements of C


#include <stdlib.h>
#include <stdio.h>
int
main(){printf
("Hello world - this program is a complete mess!\n"

);exit

( 0 )

;
}

As you can see from the chaos above, whilst layout is mostly irrelevant to the compiler, it is vital for humans!
It can and must be used to emphasize the structure of your programs to enhance their readability and improve
understanding. Bad layout will often lead to incomprehensible code and hence increase the number of mistakes
likely to be made: especially if you (or someone else) subsequently has to modify your program. It cannot be
over-emphasized how important it is to get into the habit of using good layout: if acquired as a beginner it will be
a habit that will remain with you always and help to make you a better programmer.
Ultimately, though, code layout is a stylistic aspect of programming and so to an extent a matter for personal
taste. C’s exceptionally free-format has lead to a variety of different styles for laying out programs; however the
good ones are always clear and logical. The examples in this handbook (apart from the one above!) demonstrate a
particularly clear and simple style which we recommend. You may follow another recognised style as long as it is
clear and you are consistent.

3.2 Variables
Variables are named places in the computer’s memory that are used to store data. Conceptually they are much like
variables in algebra and so you should find them fairly intuitive. However, unlike mathematical variables C (like
many other languages) requires you to state the name and type of the variables you will be using at the beginning
of your program.

3.2.1 Types
There are several different types of variable which occupy different amounts of memory and which can store
different kinds of data. The most important types are as follows:

Type Kind of data stored — amount of memory occupied Example values


int Integers (ie. whole numbers) in the range -123456789, 0, 1,
-2147483648 to 2147483647 — 4 bytes (32 bits) 17, -23, 1000000

float Real numbers stored to only about 6 significant figures in the 0.0013, 3.14159,
range of about 1.2 × 10−38 to 3.4 × 1038 — 4 bytes (32 bits) 3e+8, 1.6e-19, -2.0

double Real numbers stored to 15 significant figures in the range 0.0, -1.087e-102,
of about 2.2 × 10−308 to 1.79 × 10308 — 8 bytes (64 bits) -2.0, 3.141592653589

char A special integer type with a range of -128 to 127. Hold the ’a’, ’A’, ’z’, ’0’,
numeric code of a single text character — 1 byte (8 bits) ’?’, ’;’, ’\n’,
’9’, -1, 127

Some of the examples in the table use exponential notation. This is very straightforward: for example “ 3e8”
means 3 × 108 and “ -1.087e-102” means −1.087 × 10−102 .
The sizes and ranges of the different types may vary depending on the computer system you’re using. However,
chars are always 8 bits and are guaranteed to be able to store values from 0 to 127 (they aren’t guaranteed to store

3.2. Variables 15
negative values, though they usually do in practice). In addition, ints are almost always 4 bytes and doubles
8 bytes.
floats (so-called because they implement floating-point arithmetic) are much too imprecise for most
scientific computing and hence doubles (which are double-precision floats) should nearly always be used
instead. Surprisingly, doubles can be less accurate than ints. Although doubles can store fractional values
and hence store numbers to greater precision, ints store exact values (though they are limited to whole numbers).
As an example of the difference between accuracy and precision: “g=10” is less precise than “g=8.80665324”
but it is a much more accurate value of the mean acceleration due to gravity at the Earth’s surface. Even better,
“59.998” is more precise than “60” but it is less accurate as a count of the number of seconds in a minute!
The values stored by chars are integer numbers; however, because chars are usually used to store the
numeric codes of characters, character constants like ’A’, ’5’ or ’\n’ are normally used with them. For
instance, the code on this system3 for the letter ‘a’ is 97, the digit ‘1’ is 49, and the newline character ‘\n’ is 10!
So be careful not to confuse things like ’2’ with 2, as they have quite distinct meanings. The numeric values
of character codes are unmemorable and may vary from system to system, so it is always best to use character
constants; they are also easier to understand!
Note that forward-quotes, ’ ’, delimit character constants. Double quotes, " ", are used to define strings
(which are sequences of characters – chars can only store individual characters): do not confuse the two! We
have already encountered strings with printf(), in fact they were string constants4 .

"Here is a string constant"

There are several other types available in C including short and long ints and long doubles which
have varying sizes and ranges; and there are also unsigned versions of all the integer types (which can only
store positive values). However, you can safely restrict yourself to ints, doubles, and chars for this course.

3.2.2 Declaration and Names


C (like many other languages) requires you to declare all variables before they are used, so that the right amount
of space can be allocated for them by the compiler and so that you have a record of the variable names you have
used. In C a declaration specifies the type of variable and then one or more variable names (if there are multiple
names they are comma-separated). Here are some examples of valid declarations:

int a;
int i, j, level;
double x, y3, voltage, epsilon0;
char c, answer;
double hubble_constant;

Variable names must start with a letter (the underscore character, “ ”, counts as a letter) and then may consist
only of alphanumeric characters (i.e. letters and numbers). Spaces and punctuation characters are not allowed!
If you are a beginner you will avoid making mistakes if you restrict yourself to using lower-case for the names of
your variables (and functions). See also Section 2.4 on case-sensitivity.
Where relevant you should give your variables a descriptive name, such as time, height, or z0. If you are
coding a formula, try to match the letters or symbol-names used for the algebraic variables to the variable names
you choose in your program. There are various reserved words which you cannot use for variable names because
C uses them for other things; these are listed in Appendix C.
Within functions, declarations must always occur before any other statements at the start of a block of code (ie.
immediately after a “ {”).

3.2.3 Assignment
Data is stored in a variable by assigning a value to the variable. Assignments are made by putting the variable
name on the left, followed by a single =, followed by what is to be stored. eg.
3 If you want to see a full list of the ASCII character codes used by this system, type the Unix command: man ascii
4 Unfortunately, there is no simple string variable type in C: char arrays are used instead (arrays will be explained later in section 3.10)

16 Chapter 3. The elements of C


x = 5;

To draw an analogy, you can think of variables as named boxes. What we have done above is to label a box
with an “ x”, and then put the number 5 in that box.
In addition to simple constants (such as 5, 2.6, or ’a’) the thing being stored may be the value of another
variable or a more complicated expression (we will say more about arithmetic expressions in Section 3.5).

y = x + 4;

You also have the option of assigning variables an initial value when you declare them (although in these cases,
you can usually only assign constants); eg.

int p=3, q=4;


double sum=0.0;
char c=’y’;

Note that although 0 is an integer, by writing it above as 0.0 we indicate that it is a floating-point constant:
there are certain situations where it can be important to be explicit in this way.
There are some differences between the syntax 5 of C and normal algebra which are important. Assignment
statements work from right to left only: x = 5 is fine, but 5 = x doesn’t make sense and will cause an
assignment error. If you like, you can think of the equals sign as an arrow pointing from the value on the right, to
the variable name on the left: x ← 5 and read the expression as “assign 5 to x” (or, if you prefer, as “ x becomes
5”). However, C will still accept many expressions that might be used in algebra, although in some cases the exact
meanings may differ:

a = b = c = 0;

The assignment above reads as: “assign 0 to c, assign c to b, assign b to a”.


There are also statements that are algebraically nonsense, that are perfectly valid in C (and indeed in most other
programming languages). The most common example is incrementing a variable:

i = 2;
i = i + 1;

The second line in this example is nonsensical in maths, but makes sense in C if you think of the equals as
an arrow pointing from right to left. To describe the second statement in words: on the right-hand side we have
looked at what is in the box labelled i, added 1 to it, then stored the result back in the same box. In this way i is
incremented from 2 to 3. Having described what variables are and how they work we’re now going to have a look
at them in action.

5 The syntax of a language refers to its grammar and structure: ie. the order and way in which things are written.

3.2. Variables 17
Exercise 3.2-1
Type in, compile and run the following example program.
/*
* A comment to explain that this is a
* simple program to add up two numbers
*/
#include <stdlib.h>
#include <stdio.h>

int main()
{
int i, j, sum; /* declare 3 variables */

/*** Assign initial values to variables ***/


i = 3;
j = 5;
/*Add them up
*/
sum = i+j;

/*Output the results*/


printf("i is %d and j is %d\n", i, j);
printf("Their sum is %d\n", sum);

exit(0);
}

Remember: save your program with a .c extension.

Now do exercise 3.2-1. Compile and run the program. It should be fairly obvious what it does, though perhaps
not what every bit of the code means. What would have been immediately apparent is that whenever we referred
to a variable in the program, C replaced its name with the value we’d assigned to it.
You will also have noticed a new feature in the program not related to variables: the use of comments. If
the compiler sees the character pair “ /*” it completely ignores everything that follows until the characters “
*/” are read. (The only exception is that comments cannot occur inside strings, other comments or character
constants: in those cases /* has no special meaning whatsoever). The example shows several different ways in
which comments can be used: as with code layout they have no significance to the compiler and how you insert
them is largely a matter of personal taste—just ensure they are clearly placed. Comments should be liberally used
throughout programs to explain what the code is doing, but keep them succinct.
We have also used printf() in a new way to output not just text but the values stored in our int variables.
The use of printf() is described further in the next section.

Exercise 3.2-2
(a) Change the program so it subtracts the two numbers, rather than adds them up. Try using different
numbers.
(b) Experiment with the program to prove to yourself that C is case-sensitive; eg. check whether a
variable named sum is the same as one called SUM. You could also try capitalising the names of
one of the functions: this may cause some strange problems!

3.3 Output using printf()


Computer programs generally interact with the user or, in some cases, with other programs. These interactions
occur as a result of inputs and outputs. The most common form of output (which we’ve already encountered)
involves printing things to the screen; however, as we shall see later, it can also involve writing data to files,
sending plots to a printer, etc.
We have already seen that the printf() function can print more than just a text message. The last example
program included:

18 Chapter 3. The elements of C


printf("i is %d and j is %d\n", i, j);
printf("Their sum is %d\n", sum);

When you ran the program you’ll have noticed that the %d’s got replaced in the output by the values of i,
j, and sum. printf() always requires a string (called the format string) as its first argument, and it may
then be followed by one or more additional arguments (which can be variables, constants, or expressions) that you
want it to format and print for you. The format string can contain three kinds of text: ordinary characters which
are printed exactly as they appear; escape sequences for special characters like “\n” for newline; and conversion
specifications which begin with a “ %” and end with a conversion character such as “ d”.
There are several escape sequences defined in C (we have already encountered \n several times). The most
useful ones are:
Escape Sequence Character represented
\n newline
\t tab
\\ backslash itself
\’ quote “ ’”
\" double quote “ "”
For example,

printf("\t\"My name is \’%s\’\"\n", "Jim");

Which prints

"My name is ’Jim’"

Escape sequences may be used in strings or in character constants, eg. "%d\t%d" or ’\t’, and they count
as a single character.
A conversion specification like “ %d” tells printf() to insert the value of the next argument at that point in
the output: how the value is printed depends on which conversion character is used: eg. “ %d” means format an
integer value as a decimal number. Here is an example:

int a=4;
double x=2.5;

printf("Here is an integer: %d\t", a);


printf("and here is a double: %f\n", x);

Note that we used %f for the double: different conversion codes work with different types of value (see the
table on the next page). You must be careful to match the type of the argument to an appropriate conversion. For
example, you will get a strange result if you use %d with a double instead of an int! Our C compiler will
warn you if you get the types wrong, but it doesn’t actually prevent you from doing it and will still compile your
program.
The example code above would produce the following output:

Here is an integer: 4 and here is a double: 2.500000

You can see that, as requested, we got a tab after the 4, but the value of x has been printed to 6 decimal places:
this is simply because %f always prints to 6 decimal places by default. You can override this default precision by
using %.Nf where N is the number of decimal places you want. For example, using the same values of a and x,
here’s another version of the example modified to specify the precision (its output is shown beneath it):

printf("Here is an integer: %d\t", a);


printf("and here is a double: %.2f\n", x);

3.3. Output using printf() 19


-----------------------------------------------------------------
Here is an integer: 4 and here is a double: 2.50

In addition to specifying the precision, you can also specify a field-width; ie. the minimum number of characters
that will be printed by a conversion. If there are fewer characters, spaces are generally6 used to pad out to the field-
width; if there are more, then the field-width is exceeded. Be aware that the width includes any decimal point, +
or - signs, and the letter “ e” (or “ E”) if using an exponential format (eg. “ -4.1e+03” occupies 8 characters).
Field-widths can be used with all the conversion codes. They are essential for ensuring that columns are aligned
when outputting tables of data. See the following example and its output:

int a=1;
double x=-0.004;

printf("%3d\t%8.2f\n", a, x);

a=10;
x=-40.009;
printf("%3d\t%8.2f\n", a, x);

a=100;
x=-4001;
printf("%3d\t%8.2f\n", a, x);
-----------------------------
1 -0.00
10 -40.01
100 -4001.00

The padding is normally placed to the left of the characters printed, so that they line up on the right (right-
justification). You can get left-justification if you use the syntax %-3d, %-8.2f, etc. Notice also that
printf() has rounded -40.009 to the correct precision, ie. -40.01.
Below is a table of the common printf() conversion codes. Note that the codes which expect an int will
work with any integer type, including chars. However, they will give very strange results if you pass them a
floating point type. Likewise, the double codes will happily accept a float but may produce strange output if
you pass them an integer type.

Conversion code Argument type Formatted as


%d int Ordinary number (eg. 365)
%c int Prints the character with this numeric code (see below)
%f double Real number, including a decimal point, (by default)
a precision of 6 decimal places (eg. 365.256000)
%e,%E double Alternative to %f which formats using exponential notation
eg. 3.652560e+02, or 3.652560E+02
%g,%G double An intelligent combination of %f and %e: it uses the %f format
if the exponent of the argument <-4 or >= the precision!
In other words it tries to choose a sensible format.
%s string Print a string.
%% This is how you output the “ %” character itself!

There is no simple string type in C; for now it’s enough to know that you may print a string constant with
printf() by using %s.
Three more examples:

char c=’A’;
printf("%s %c is %d\n", "The code for", c, c);
6 If the field-width is written with a leading 0 (eg. %03d) then zeros are used for padding instead of spaces.

20 Chapter 3. The elements of C


----------------------------------------------
The code for A is 65

double x=365.256;
printf("%.1f\t%8.1e\t%.10E\n", x, x, x);
----------------------------------------
365.3 3.7e+02 3.6525600000E+02

double y=1.25e-5;
printf("%12.4f %8.4g\n%12.4f %8.4g\n", y, y, y*1000, y*1000);
printf("%12.4f %8.4g\n", y*1e8, y*1e8);
printf("%12.4f %8.4g\n", y*1e12, y*1e12);
---------------------------------------------------------------
0.0000 1.25e-05
0.0125 0.0125
1250.0000 1250
12500000.0000 1.25e+07

It is up to you to ensure that the number of arguments after the format string matches the number of conversion
specifications: compilers react differently to such mismatches, some will produce warnings others will not, some
will compile and allow you to run your program where bizarre values will get printed. In all cases it is a mistake
to have a different number of arguments and conversion specifications.

3.4 Input using scanf()


So far, we have only output data. This section describes how to input data using the counterpart of printf(),
scanf(). scanf() can be thought of as working like printf() in reverse: it inputs values into variables
rather than outputting the values from variables. Like printf(), it expects a format string as its first argument,
and then a series of arguments which should be references to variables into which values are to be input.
A scanf() format string looks essentially identical to a printf() one. The two differences are that:
1. Spaces in the format string match any number of spaces and tabs in the input;
2. The conversion specifications for floats and doubles are not quite the same: floats still use %f, but
doubles must use %lf [Note: this is the letter ell, l, not the digit one, 1; think of %lf as ”long float”];
Rather than use the ordinary name of the variable, you must prefix it with the & operator which gives a
reference to the variable rather than the contents of the variable. scanf() needs to be able to write new values
into variables, so it needs to know where the variable is, rather than what value it presently contains.
An example of using scanf to input an x-y pair consisting of an int and a double:

Values input are:

4 0.00451

-------------------
/* Code fragment to use to input data of the above format */

int x;
double y;

scanf("%d %lf", &x, &y);

The exception to this is strings (ie. arrays of characters). You will encounter character arrays later in the course,
for now remember that they do not require the & operator.

3.4. Input using scanf() 21


scanf() ignores blanks and tabs in its format string, it also skips over white spaces (blanks, tabs and
newlines7 ) as it looks for input values. You do not need to put \n in your format string.

#include <stdio.h>
#include <stdlib.h>

int
main()
{
int i;
float f;
double d;
char strng[21];

printf("type in an int: ");


scanf("%d", &i);
printf("value entered: %d\n\n", i);

printf("type in a float: ");


scanf( "%f", &f);
printf("value entered: %f\n\n", f);

printf("type in a double: ");


scanf("%lf", &d);
printf("value entered: %f\n\n", d);

printf("type in a string (up to 20 characters long): ");


scanf("%s", strng); /***Strings do NOT require the & operator***/
printf("string entered: %s\n\n", strng);

exit(0);
}

3.5 Arithmetic Expressions and Operators


The programs you write will nearly always involve numerical calculations. The rules for arithmetic expressions
are largely common sense: anything that looks right mathematically usually is, with some minor changes (for
example, the multiplication operator must always be included explicitly). In fact, C performs arithmetic in much
the same way as an electronic calculator. Basic arithmetic calculations are expressed using C’s mostly obvious
arithmetic operators.

Operation Operator
Addition +
Subtraction -
Multiplication *
Division /
Remainder %
Grouping ()

C generally evaluates expressions from left to right, but things enclosed in brackets are calculated first, followed
by multiplications and divisions, followed by additions and subtractions. If in doubt use parentheses around the
expressions you want evaluated first. As you can see below, parentheses may be nested, in which case the innermost
expressions are evaluated first.

7 There is a situation where scanf() does not skip a newline: this will be explained in Section 3.8.3.

22 Chapter 3. The elements of C


Expression Result Comments
2+3*4 14 Multiplication (and division) are evaluated before addition (or subtraction)
(2+3)*4 20 Parentheses over-ride normal operator precedence
2+(3*4) 14 The same as the default behaviour
6/2*3 9 Equal precedence operators are evaluated strictly left-to-right
14%5 4 The remainder of 14/5
4*(6/(3-1)) 12 Innermost parentheses are evaluated first
1/2 0 Integer divisions discard fractional parts
1.0/2 0.5 The presence of a non-integer constant or variable
causes an expression to be calculated using floating-point arithmetic

Unlike some other programming languages, there is no built-in power operator to express xy . For raising x
to small integer powers, the most effective method is to use x*x, or x*x*x, etc. Otherwise, the library function
pow(x,y) is available: it expects two double arguments and returns a double result (see also Section 3.9).

3.5.1 Type conversions and casts


From the table above, you can see that when only integer quantities are involved, integer arithmetic is used, with the
effect that divisions can give unexpected results! This is often not what is desired and hence you should normally
ensure that floating-point arithmetic is used in calculations, especially those involving divisions. If either operand
of an operator is, say, a double, then the other operand will be converted to double and evaluated. Otherwise,
a good way of doing this is to specify whole number constants as doubles8 by appending .0 on the end of
them; eg. 1.0, 0.0, etc.
If there are no constants involved, however, you can if necessary force the value of any variable or expression
to another type using a special operator called a cast. A cast is simply the name of the type you want the variable
to be converted into (eg. “ int” or “ double”) surrounded by parentheses and placed in front of the thing you
want to convert. eg. “ (int)floatvar” converts the value in floatvar to an int: it does not permanently
change the type of floatvar itself; it merely converts the type of its value in that expression.
An example of when a cast is essential:

int p,q;
double d;

p=1; q=2;

d=p/q; /* d will equal 0.0 because p and q are both ints */

/* Now use a cast */

d = (double)p / q; /* d will now equal 0.5 because the value of p


* (i.e. 1) was cast to a double forcing the
* division to be done with floating point
* arithmetic */

/* Be careful exactly when you do a cast, though */

d = (double) (p/q); /* d will again equal 0.0 because the cast was
* done too late--after the division was
* evaluated */

Casts may seem odd, and perhaps a bit confusing, but they are actually very useful and straightforward. For
instance, most of the mathematical functions in C (see Section 3.9) expect double arguments, and so you will
often need to cast variables that you pass to them to double (some examples will be given later).
Happily, except in cases like the above, casts are often not essential, because C will implicitly convert different
types involved in expressions. The full rules that C uses to decide how and when to convert values from one type to
8 If you ever need to force a constant to be a float rather than a double, you can use f or F as a suffix (eg. 1.05f).

3.5. Arithmetic Expressions and Operators 23


another are fairly involved, but the most important ones are summarised below. In general, if an operation is being
performed involving two quantities which have different types, the “smaller” type is promoted to the “bigger” type
before the operation proceeds, and the result is that of the bigger type. This is the case whether the operation is
arithmetic, or an assignment, or any other sort.
1. If either quantity is double convert the other to double.

double answer, d = 2.0;


int i = 4;
answer = d/i; /* The value of i gets converted to double
* because d is double */

2. Otherwise, if either quantity is float convert the other to float.

float answer, f = 2.0;


int i = 4;
answer = f+i; /* The value of i is converted to float */

3. Otherwise convert char to int.

char c = ’a’;
int answer, i = 4;
answer = c + i;

Casts are one example of how C is in general very permissive about what it allows you to do. Many languages
are strongly-typed and are restrictive about which types they allow to be converted to which others. For experienced
programmers, C’s flexible approach is extremely useful, but for beginners it can sometimes cause unexpected
problems (the above being good examples).
Most languages will allow you to assign integer types to floating types, and to assign “smaller” types to “bigger”
ones (eg. chars to ints or floats to doubles). But another example of C’s permissiveness is the way that
‘bigger” types can also be assigned to “smaller” ones. For instance, C won’t stop you from assigning an 8-byte
double to a 1-byte char (though it may give you a warning)! This can lead to problems if you’re not aware of
what you’re doing, and so in general you should avoid doing so. If you do assign a floating point type to an integer
type the fractional part will be discarded. Also, attempting to assign a number beyond the range of a smaller type
will obviously cause problems and no guarantees can be made about what will then happen: if you do do this, you
have again made a mistake and your program is faulty!

3.5.2 Some other operators


C is renowned for having a very rich set of operators. Whereas most languages are content with the usual arithmetic
operators, assignments, and little else, C provides a range of useful and esoteric operators.
Increment and Decrement Operators
In particular, a group of operators that are widely seen in C programs are the increment and decrement operators,
“ ++” and “ --”. Very simply, the increment operator, ++, adds 1 to a variable, whilst the decrement operator,
--, subtracts 1. Although they are nearly always used with integer types, it is perfectly legal to use them with
floating-point types as well. Interestingly, they can be written either before or after the variable they’re operating
upon, and this changes their behaviour subtlely.

int p,q;

p = 8;

q = p++; /* q is now 8; p is now 9 */

q = ++p; /* q and p are now both 10 */

24 Chapter 3. The elements of C


So the difference between “ p++” and “ ++p” is that p++ increments p but not until after its value has been
used, whereas ++p increments p before its value is used. The decrement operator, --, works in exactly the same
way. All forms of these operators can only be applied to variables, not constants (eg. “ 3++” is illegal). They can
however be used simply to perform the increment or decrement operation, without the value being used. ie. this is
a legal statement: “ p++;” and is effectively the same as doing “ p=p+1;”.
Assignment Operators
Another group of operators which C introduced to the world are the compound assignment operators. An
expression like “ i=i+3” can be written instead as “ i+=3”. There are a number of such assignment operators
available, including one for each of the basic arithmetic operations:

+= -= *= /= %=

These operators are not essential but they may produce more efficient code, and can even reduce the potential
for mistakes. It should be stated that if there is an expression to the right of an assignment operator, then it is
considered to be parenthesised; ie.

a *= z / x + 4;

is equivalent to
a = a * (z / x + 4);

Exercise 3.5
Write a program which asks the user for a value for the radius of a circle (use printf and scanf) and
calculates the area and circumference of the circle and then prints them together with the radius to
the screen. You can use an approximate value for π. Don’t forget comments, and make sure the
program’s output is descriptive, ie. the person running the program is not just left with two numbers
but with some explanatory text.

3.6 Expressions, statements, and compound statements


So far we have concentrated on how C stores, manipulates and outputs data. These are all essential aspects of
programming. Equally important, however, is the ability to be able to specify the order in which computations are
performed. The programming jargon for this aspect of a language is control flow. We have already encountered
C’s most fundamental control flow features: statements and blocks.
Formally, an expression such as “ x=0”, or “ printf()”, or “ a+1” becomes a statement when it is followed
by a semi-colon9 . Because of this, in C the semi-colon is known as the statement terminator.

/* Here are two statements */


x = 4;
x = x + 2;

A group of statements surrounded by braces { } is known as a block or compound statement. A compound


statement is legal in C anywhere that a single statement can be used. Re-read that last sentence, it is very significant.
As we shall see in the next few sections, it is also extremely useful.

/* Here is a single compound statement (or block)


which contains 2 statements. Always remember to indent! */
{
x = 4;
x = x + 2;
}

9 This distinction between expressions and statements is a subtle one which many other languages don’t make. It’s helpful for being able to

define things formally, but is not something you need worry about too much.

3.6. Expressions, statements, and compound statements 25


One important note about the legal equivalence of single statements and blocks: the body of a function must
always be a compound statement even if the function only contains one statement. For example it would not have
been legal to have written our first Hello world program as follows:

/* THIS IS NOT A VALID C PROGRAM! */


main()
printf("Hello world\n");

So, if in doubt, use a compound statement, because they are always valid.

3.7 if statements
It is often essential in programming to be able to control the flow of a program according to whether some
condition is true or false. The C construct for doing this is the if statement. It allows a statement to be executed
conditionally. Its form is:

if(condition)
statement

Where statement is only executed if condition is true. Note that an if statement only has a single conditional
statement. However, as described above, you can arrange for a block of statements to be executed if the condition
is true by using a compound statement.
There is an extended form of the if statement which allows alternative statements to be executed:

if(condition)
statement1
else
statement2

In this case, statement1 is executed if condition is true whereas statement2 is executed if condition is false.
What do “true” and “false” mean in this context? Computers, and their programming languages, depend heavily
on a simple mathematical system known as Boolean logic. Boolean logic is based on a two-value, or binary, system
of truth and falsehood, usually represented by 1 and 0.
Unlike many languages, C has no special boolean type to represent TRUE and FALSE values. ints are used
instead with the rule that 0 is FALSE and any non-zero value (though normally 1) means TRUE.
The condition in an if statement can be any C expression, however it is almost always in practice a relational
expression, ie. a comparison of the relative values of two quantities. For example:

int a=2, b=3;

if(a < b)
{
printf("a is less than b\n");
a=a+2;
}
else
{
printf("a is not less than b\n");
a=a-1;
}

26 Chapter 3. The elements of C


3.7.1 Relational operators
C has a set of relational operators which take two arguments and yield an int result: 0 if the comparison is false
and 1 if it is true.
Relational operator Meaning
< is less than
<= is less than or equal to
> is greater than
>= is greater than or equal to
== is equal to
!= does not equal
The “ ==” is not a mistake: a single “ =” is used for assignment, which is completely different to testing for
equality, so a different symbol must be used.
This is a major source of confusion and error in C! We’ve already said that the condition expression can
be any valid C expression, and that includes an assignment. Hence if you do the following you will get very
unexpected results:

int a = 4;

if (a = 5)
/* ALMOST CERTAINLY A MISTAKE IT SHOULD BE: if (a == 5)*/
printf("a is 5\n");

In this code fragment, 4 is assigned to a and then it is intended to test whether a equals 5 and if so, output
an appropriate message. Unfortunately, the condition expression of the if is another assignment (not an equality
test) and so 5 is assigned to a and the result of that operation is also 5 (because in C, every expression has a
numeric value, and the value of an assignment operation is the quantity assigned). 5 also happens to be non-zero
and hence the condition is considered to be true and the printf() statement is executed.
It is almost inevitable that you will make this mistake at first, and it can be a very difficult one to spot because
it is perfectly valid C code (and can cause serious bugs in some situations). You will greatly reduce your chances
of making this mistake if you get into the habit early on of reading to yourself things like “ a = 5” as “a becomes
5” or “5 assigned to a”; and “ a == 5” as “a is equal to 5”.

3.7.2 Logical operators and else-ifs


Several if-else statements can be concatenated together to form an if-else-if statement

if(condition1)
statement1
else if(condition2)
statement2
else
statement3

Another important requirement is the ability to combine several conditions in one expression. For instance to
be able to do something if one thing is true AND simultaneously another thing is true. There are three logical
operators for doing this
Logical operator Meaning
&& AND: only TRUE if both operands are TRUE
|| OR: TRUE if either operand is TRUE (or both are)
! NOT: inverts its sole operand so that:
TRUE becomes FALSE; FALSE becomes TRUE
For example:

/* Example of logically combining many conditions */

3.7. if statements 27
if (a>b && a>c)
biggest = a;
else if (b>a && b>c)
biggest = b;
else if (!(c==a || c==b))
/* ie. The opposite of "c equals a or b" */
biggest = c;

printf("The biggest is %d\n", biggest);

Relational operators have a higher precedence than logical operators; however, if ever in doubt, or to make
things clearer you may parenthesise (eg. “ (a==b) || (a!=0)”).
The above example also illustrates that an else can contain another if statement (as indeed can an if
statement). This is important because it allows a series of conditional tests to be chained together, although it can
be the cause of confusing code. It also raises the issue of “to which if does an else belong?”. eg. in the
following example:

if (a!=0)
if (b==c)
printf("a is non-zero and b equals c\n");
else
printf("err....which if do I belong to?\n");

The indentation suggests the right answer: the rule is that an else belongs to the first if above that hasn’t
already got an else. If we want to force an else to belong to a different if we can do so using braces thus:

if (a!=0)
{
if (b==c)
printf("a is non-zero and b equals c\n");
}
else
printf("Now I know!\n");

Exercise 3.7
(a) Write a program to read two numbers and print them in numerical order.
Consult a demonstrator before attempting part (b)
(b) Write a program to read three numbers and print them in increasing order. It should be able to
correctly cope with: (2, 6, 1); (1, 6, 2); (6, 1, 2); (4, 6, 4); (6, 4, 4); (4, 4, 6); and even (4, 4, 4)!

3.8 Loops and Iteration


There are many situations where it is necessary to be able to repeatedly execute a statement or a series of statements
(for example when performing an iterative calculation). This control flow feature is known as a loop. Strictly, in
C, only one statement can be used in a loop; however, as with if statements, if you need several statements to be
repeated you simply have to group them together into a compound statement.
There are three types of loop in C, for loops, while loops and do...while loops (the latter are used less
often than the other two). We will look at each one in turn.

3.8.1 for loops


When a given number of repetitions are required (for instance if the number of iterations needed for a calculation
is already known) it is easiest to control a loop by using a variable as a counter. Three things need to occur in such
loops: a counter must be initialised to a starting value before the loop is entered; its value must be checked every

28 Chapter 3. The elements of C


time around the loop (including prior to the first time) to see when to finish; and the value of the counter must be
updated at the end of each loop iteration. In C this type of loop is implemented using a for statement.
The general format of a for loop is

for(initialise ; condition ; update)


statement

The initialise part is an expression which is only done once before the start of the loop. It is virtually always an
assignment used to initialise a loop counting variable (e.g. i=0).
The condition is an expression (nearly always a comparison to check the value of the loop variable) which is
evaluated before the start of each iteration of the loop: if it is non-zero the statement is executed (e.g. i<10).
After the statement has been executed, the update expression is evaluated. This usually increments the value of
the loop variable (eg. i=i+1, or i+=1, or most often i++), though any expression is valid. The sequence then
restarts at the condition. If the condition is false (zero), the loop ends.
Some examples will make this all a lot clearer. Suppose we want a program to print out the squares and the
sum of the squares of the integers in the range 1 to 10. We could write:

printf("%d\t%d\t%d\n", 1, 1*1, 1*1);


printf("%d\t%d\t%d\n", 2, 2*2, 1*1+2*2);
printf("%d\t%d\t%d\n", 3, 3*3, 1*1+2*2+3*3); etc.

but this is a particularly bad way of doing this. Imagine we wanted to change our program to print out the squares
and the sum of the squares from 1 to 100! It is much easier to write this in a for loop as:

int i;
int sumsq=0; /* Always set summation variables to 0 before use */

for(i=1; i<=10; i++)


{
sumsq = sumsq + i*i;
printf("%d\t%d\t%d\n", i, i*i, sumsq);
}

Here we set i as 1 initially. The first time through the loop sumsq will have the value 0+1*1=1, and this is printed
to the screen. The condition ( i<=10) is checked next and since it is true (i is 1) i is incremented by one. The
second time through the loop sumsq is 1+2*2=5 and this is again printed to the screen. Can you see how easy it
would be to change this to print all squares and their cumulative squares up to 100? All you need to do is change
the condition to i<=100!
We’ll discuss one more example before looking at while loops. Suppose we want to print a conversion table
between degrees and radians, counting down from 360 to 0 degrees in steps of 5 degrees. Before looking at the
code fragment below, see if you can anticipate the initial value, test condition and update expression that will be
required.

int degree;
double radian;

for(degree=360; degree>=0; degree=degree-5)


{
radian = degree * 2.0 * 3.1415926 / 360.0;
printf("%3d\t%.4f\n", degree, radian);
}

Notice that we can count backwards in a for loop because, as mentioned above, the update expression doesn’t have
to be an increment.

3.8. Loops and Iteration 29


Exercise 3.8.1
(a) Write a program to calculate and output the sum of k −2 for k from 1 to 10 in increments of 1.
Remember that integer division can give undesired results.
(b) More confident students may like to modify their part (a) program to calculate the same sum for
k from 1 to N (where N should itself be varied from 5 to 50 in steps of 5). This will require you to
use a loop within a loop (known as nesting). Think carefully about how to output the results neatly.

3.8.2 while loops


Sometimes, rather than executing a loop a fixed number of times it is necessary to continue executing it while a
condition is true. This is done with a while loop which has a very simple and obvious structure:

while(condition)
statement

The condition is tested first and the loop is only entered if it is true (non-zero). It is then re-tested each time around
at the start of the loop. Once again, statement can be a compound block.

int ok=1;
while(ok == 1)
{
/* Do some stuff ... */
if(the_end == nigh)
ok=0;
}

Because the condition expression is checked before the loop is entered, it is worth noting:
1. that any variables (such as ok) which are used in the condition must already contain a value; and
2. that it is possible that the loop statement will never be executed.
It is also worth pointing out that if nothing in the loop affects the value of the conditional expression the loop
will probably continue for ever! This is usually a bug. If you find that your program is not stopping this is more
than likely the cause.
An interesting aside: since in C a conditional expression is true if it’s non-zero, we could have written “
while(ok)” instead of “ while(ok == 1)”.
A useful example is to rewrite our example from the section on for loops as a while loop.

int sumsq=0; /* Always set summation variables to 0 before use */


int i=1;
while (i<=10)
{
sumsq = sumsq + i*i;
printf("%d\t%d\t%d\n", i, i*i, sumsq);
i++;
}

Notice that the initial condition ( i=1), the condition ( i<=10) and the increment ( i++) are still present. In this
case, where the number of iterations is known before we enter the loop, a for loop is more compact and should
be used.

30 Chapter 3. The elements of C


3.8.3 do...while loops
There is a variation on while called do...while. It is not so widely used in C. It exists because it is
occasionally useful to be able to guarantee at least one execution of the statement in a conditional loop.

do
statement
while(condition);

Note the semi-colon at the end: it is not optional. The difference here is that the statement is executed before
condition is evaluated, thus guaranteeing at least one iteration of the loop.
One case where the do...while loop is useful is when you want to repeat a calculation until a user indicates
via keyboard input that the program should stop.
The following piece of code will perform some calculation then ask the user if they want to repeat the
calculation. If the user types n or N the loop will exit.

char cont;
do
{
/* perform task */

printf("Do you want to repeat the calculation? (y/n) ");


scanf("%c", &cont); /**This may cause a problem!**/

} while (cont != ’n’ && cont != ’N’);

Normally, when you are asked for keyboard input you indicate to the system that you have finished typing
by pressing the Return (or Enter) key. This appends a newline character to the characters you typed, and
everything is placed by the system into what is called a buffer. The system then makes this input buffer available
to the program, which in the above case is using scanf() to read one character from the buffer placing it in a
variable called cont. If the value of cont is not ’ n’ or ’ N’ then the loop will execute a second time.
However, an unfortunate quirk of using scanf() to read a single character in this way is that the newline
character that was used to indicate the end of keyboard input is also left in the input buffer! (Note that this only
happens when using a %c conversion). The second time through the loop the buffer is not empty, as it still has
a newline character in it, which precedes any subsequent input placed in the buffer by the next invocation of
scanf(). scanf() places this (newline) character in cont and tests it against ’ n’ and ’ N’, which clearly
gives the wrong outcome!
Happily, there is a simple way to work around this issue: you can force scanf() to read and discard any
unwanted whitespace (including newline characters) prior to a %c conversion by using a leading space. ie. Instead
of "%c" use " %c".10

Exercise 3.8.2
Find the largest factor of a number, n: test your program with different values of n. [Remember: a
number’s largest factor is the largest integer that divides into the number with no remainder]

3.9 Mathematical functions


To keep the language small, the designers of C decided to build in only the most essential features, leaving
everything else to be provided in one of a series of function libraries. We’ve already encountered exit()
from the standard library, and of course printf() from the standard input/output library. The advantage of
this approach is that a rich and wide collection of C library functions have been developed over the years, and
mathematical functions are no exception.
To gain access to most of the mathematical functions, you must include the following header file in your
program:
10 More experienced students may like to know that the buffer may alternatively be cleared, or flushed, using the fflush() library function.

3.9. Mathematical functions 31


#include <math.h>

The only exception is the abs() function which is an integer function supplied by the standard library,
stdlib.h.
The most commonly-used maths functions are as follows (angles for the trigonometric functions must be in
radians; 1◦ = 2π/360 radians):
Function name Description
sin(x) sine of x
cos(x) cosine of x
tan(x) tangent of x
asin(x) arcsin of x
acos(x) arccos of x
atan(x) arctan
√ of x
sqrt(x) x, x>=0
pow(x,y) xy
exp(x) ex
log(x) natural log, ln(x), x>0
log10(x) base 10 log(x), x>0
erf(x) error function of x
fmod(x,y) floating point remainder of x/y with same sign as x
ceil(x) smallest integer not less than x (result is still double)
floor(x) largest integer not greater than x (result is still double)
fabs(x) absolute value (ie. modulus) of x
abs(i) as above for an int i
The math.h functions all expect double arguments and return a double value hence remember to use
( double) casts for the arguments (as in the first example). Once again abs is the exception.
Notice that the arguments to some of the maths functions must be greater than zero! In particular, on this
system if you try to pass a negative argument to sqrt() you will see the string NaN when you try to print the
return value. NaN means Not a Number. Similarly, attempting to do things like log(0.0) or log10(-1.0)
will yield -Inf (Minus Infinity), whilst attempting to divide by zero will of course give you Inf.

/*The next line must always be used with maths functions*/


#include <math.h> /* NB. "math.h" not "maths.h"! */

int p, q;
p=2; q=4;

/* We must cast both arguments passed to pow()*/


d = pow((double)p,(double)q);

/*....Another example....*/

#include <math.h>

double x=16.0, s, cuberoot;

s = sin(x);
cuberoot = pow(x, 1.0/3.0); /*Note we’ve used 1.0/3.0 because 1/3 is */
/*an int expression (in fact equal to 0!)*/

The math.h header file also defines several constants, including “ M PI” for π, and “ M E” for e (i.e. the base
of natural logarithms).
Be aware that the floor() function returns surprising results for negative arguments. eg. floor(-3.01)
returns the value -4.0.

32 Chapter 3. The elements of C


Exercise 3.9
(a) Output a table of x, sin(x), cos(x), tan(x), and sec(x) for 0 - 360 degrees in steps of 5 degrees.
Check that your results are sensible.
Only attempt parts (b) and (c) if you have programmed before and are well ahead of schedule.
(b) Find out what happens if you try to divide by zero, or take the log or square root of a negative
number. The results may vary on different computer systems, and even with different compilers.
(c) Compute the cube root of a number to an accuracy of 10−6 given that if a is an approximate cube
root of x then (2a + xa−2 )/3 is a better one.

3.10 Arrays
If a variable is a data structure that can hold one piece of data of a particular type (a single box), then an array is a
structure that can hold several pieces of data all of the same type. Continuing with the box analogy (from Section
3.2.3) an array is a large box (or perhaps a rack) into which a series of smaller boxes can be put (figure 3.1). The
smaller boxes, known as elements of the array, are numbered sequentially from zero so that they can be identified.
The number, known as the array index, starts at zero, and goes up in increments of one.

xx
1 5 6.5 -11
xx[0] xx[1] xx[2] xx[3]

Figure 3.1: Arrays can be thought of as boxes in boxes

Arrays have types in exactly the same way as ordinary variables, such as int, double, or char. The
individual elements of an array of ints behave exactly like ordinary int variables: they can store the same kind
of data and can be used in exactly the same ways. In other words, the small boxes inside, say, an int “array box”
are the same size and shape as individual int variable boxes.
In addition to providing an easy way of automatically creating lots of variables of the same type, arrays are
especially useful because they allow their elements to be accessed by a numerical index.

3.10.1 Declaration
Arrays are declared in almost the same way as simple variables (and indeed declarations of arrays and variables
can be mixed together): The declaration consists of the name of the type followed by the name of the array with a
number in square brackets: the number specifies the number of elements the array will have.

char cc[10], c; /* Declares an array of 10 chars called cc,


* and an ordinary char variable, c */
int xx[4];
int i, j, readings[200];
double y[25], sum;

3.10.2 Array elements


Since array indices start at 0, an array declared as “ array[N]” will have elements called
array[0],...,array[N-1]. Once we have declared an array we can use its individual elements like
ordinary variables of the same type. Array elements are referred to by the name of the array followed by their
index number in square brackets; eg.

y[2] = 6.5;

3.10. Arrays 33
i = i * y[2];

However, normally arrays are processed en-masse, usually inside loops. For instance:

sum=0.0;
for (i=0; i<4; i++)
sum += xx[i]*yy[i];

3.10.3 Initialisation
C provides a way to initialise an array (ie. to assign initial values to all the elements) when it is declared. The
syntax is as follows:

int a[5] = { 5, 10, 15, 20, 25 };

/* The above is equivalent to */

int a[5];
a[0]=5; a[1]=10; a[2]=15; a[3]=20; a[4]=25;

In fact, if you are using this initialisation method, you may omit specifying the size of the array: the compiler
will calculate the size to use by counting the number of initialisation values.

int a[] = {5, 10, 15, 20, 25, 30, 35 }; /* Compiler will automatically
know to create int a[7] */

This is useful if you need to keep changing the amount of initial data you’re putting into an array but don’t
want to have to keep matching the size of the array to the amount of data. On the other hand, it makes it harder for
someone reading your code to know the size that the array will be, so be explicit with the size if you can.

Exercise 3.10
(a) Using a for loop, construct two 100 element arrays, x and y, such that element i of x stores
the value sin(2πi/100)) and the corresponding element of y stores cos((2πi/100)). Print the values
stored in the elements of x and y as you calculate them.
Consult a demonstrator before attempting part (b).
(b) Compute the scalar (i.e. dot) products x.x, y.y, and x.y, to check that sin and cos are orthogonal.
The scalar, or dot, product is defined as:
X
x.y = xi .yi
i

3.11 Functions
Functions are the constructs C uses to separate logically distinct portions of a program into self-contained units.
We’re already familiar with the main() function and have used many library functions: this section explains how
to create your own functions.
There are several reasons why you might create a function.

1. A common one is to encapsulate a group of statements which might need to be executed more than once
in different parts of a program (e.g. perhaps you need to be able to perform a particular task every time a
certain situation arises): rather than having to type in the same group of statements multiple times, you can
put them into a function, and then every time they need to be used, you simply call the function.

34 Chapter 3. The elements of C


2. Another reason could be that your program comprises a series of logically distinct parts: rather than writing
it as one long series of statements, it can be clearer to divide the code into sections, using a different function
for each one. These can then be called in sequence.
3. A third reason could be that you are writing a portion of code that you are going to want to use again in other
programs: if written properly, it is easy to copy a function into a new program and use it straight away.

In all of these scenarios, functions are being used to structure a program into sub-programs; ie. modular
sub-sections of the main program.
The form of a function definition is:

return-type function_name(argument declarations)


{
declarations and statements
}

The arguments of a function are also sometimes referred to as its parameters. The formal parameters are the
arguments used in the definition of the function, whilst the actual parameters are the arguments actually passed to
the function when it is called.
Example of a function:

int addup(int x, int y)


{
int sum;

sum = x + y;
return sum;
}

Most functions return a value: as you can see from the example, this is done with a return statement. A
function should always have at least one return, but may have several (this can be useful if you want to be able
to pass back different values according to circumstances). The value passed back by return should be of the same
type as the function (ie. int in the example above). It need not be the value of a variable, however; it could be a
constant or even an expression. For instance, we could have written: return x+y; and dispensed with sum
altogether.
Functions may return any of the C data types (eg. int, double, etc). By default, functions are assumed to
return ints, but you should get into the habit of always explicitly declaring what type a function returns (even if it
is int). Surprisingly, functions may even return no actual value at all! To get around the fact that, like variables,
all functions must have a type, C uses a special type, called “ void”, for functions with no return value.
Many programming languages call sub-programs that return no value “procedures” or “sub-routines” and they
consider them to be distinct from other things they call functions, which have exactly the same properties but do
return a result. C takes the view that this is an artificial distinction and so uses the term function for all sub-
programs, regardless of whether they return a value. A “void function” then, is essentially a procedure: a program
module that executes and returns without yielding a value.
Once a function has been defined (or declared; see below) it can be called from another part of the program. If
the function was defined with arguments then the number and type of arguments in the function call must match
the arguments in the function definition.
An example of a program containing a couple of functions:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double my_function(double x)
{
double answer;

3.11. Functions 35
answer = exp(-x) / (x*x);

return (answer);
}

void print_it_out(int a, double b)


{
printf("First number is: %d\nSecond number is: %.4g\n", a, b);
return;
}

int main()
{
int i;
double a, z;

for (i=1; i<=10; i++)


{
a = i/10.0; /* Use 10.0 to force floating-point division */
z = my_function(a);
print_it_out(i, z);
}

exit(0);
}

Note that the variable a in main() which is a double is completely separate from the integer parameter a
in the function print it out().
In the example we have used two double variables in main(), a and z, to store values that are then passed
to the functions. We could have dispensed with these variables by using the expressions themselves as arguments.
In other words, the second argument to print it out() could have been the call of my function(), which
in turn could have had i/10.0 as its argument. ie.

print_it_out(i, my_function(i/10.0));

This is perfectly legal because my function() will be called and return a double value, which will then
be passed as the second parameter to print it out().
The functions have been defined before main(). This is a good way of writing the code because before a
function can be called, it must already have been declared or defined. A function declaration is the same as the
definition but without the body of the function. ie.

double my_function(double x);


void print_it_out(int, double);

Note that in a declaration you don’t have to name the formal parameters, merely specify their types 11 . It would
have been legal to declare both functions before main() and then put their definitions after main(): it’s up to
you whether you declare first and then define later, or just define first.
Type the program in and run it for yourself to see what it does.

11 Such declarations are sometimes known as function prototypes

36 Chapter 3. The elements of C


Exercise 3.11
The following function computes ex by summing the Taylor series expansion to n terms. Write a
program to print a table of ex using both this function and the exp() function from the math.h
library, for x = 0 to 1 in steps of 0.1. The program should ask the user what value of n to use.

double taylor(double x, int n)


{
int i;
double sum = 1.0;
double term = 1.0;
for (i=1; i<=n; i++)
{ /*Or we could have written: */
term = term * x / i; /* term *= x / i; */
sum = sum + term; /* sum += term; */
}
return sum;
}

3.12 File I/O


C communicates with files using a special type of variable called a file pointer, written FILE *. Don’t worry
about the syntax which looks worse than it actually is. Just follow the simple recipes given in this chapter for
reading from and writing to files and you can forget about the details of what is going on and why.
A file pointer variable, called fout, is declared like this:
FILE *fout;

A file pointer is just like any other variable in C, you declare it at the top of your program and then assign a
value to it. File pointers are associated with an actual file by using the fopen() function.
FILE *fopen(char *filename, char *access mode)

( char * is another way of denoting a string). If the file cannot be opened fopen() will return a special
value called NULL. You should check for this when you open a file otherwise you may try to write to a file using
a pointer that doesn’t refer to the file which will give you errors. An easy way of doing this is
FILE *fin;
fin = fopen("input.data", "r");
if (fin == NULL)
{
/* oh dear, we can’t access the file...
* do something suitable here */
printf("Cannot open %s\n", "input.data");
exit(-1); /*It’s nearly always necessary to*/
} /*exit if you fail to open a file*/

/* Note that a more compact way of opening


* and testing the file pointer above is */
if ((fin = fopen("input.data", "r")) == NULL)

It may not look the easiest method of opening a file but if you use this method you will rarely go wrong. The access
mode is a way of telling the compiler what we want to use the file for (e.g. reading or writing). The commonest
access modes are:
r Open a file for reading
w Open a file for writing, if file exists destroy current contents
r+ Open a file for reading and writing
a Open a file for appending (i.e. adding to the end of the file)

3.12. File I/O 37


If files are still open when a program exits, the system will close them for you. However it is good practice to
close files explicitly when you’ve finished with them (it also means you can then open another file using the same
file pointer). We can do this using the fclose() function.

fclose(file pointer)

Let’s look at a proper example.

#include<stdlib.h>
#include<stdio.h>
int main()
{
FILE *fin, *fout; /* Declare file pointers*/
double x, y; /* Other variables */

if ((fin = fopen("input.data", "r")) == NULL)


{
printf("Cannot open %s\n", "input.data");
exit(-1);
}

if ((fout = fopen("output.data", "w")) == NULL)


{
printf("Cannot open %s\n", "output.data");
exit(-2);
}

fclose(fin);
fclose(fout);
exit(0);
}

3.12.1 Writing to Files


The example we have studied so far is not particularly interesting. We have only opened and closed two files.
Writing to a file is not very difficult, you can do it with a modified version of the printf() function you have
already seen. The function we use to write to files is fprintf(). fprintf() expects a file pointer as its first
argument and then expects a format string similar to that for printf().
An example will make things clear.

#include<stdlib.h>
#include<stdio.h>
int main()
{
FILE *fout;
int i;

if ((fout = fopen("output.data", "w")) == NULL)


{
printf("Cannot open %s\n", "output.data");
exit(-1);
}

for (i=0; i<10; i++)


{
fprintf(fout,"i = %d\n", i);
}

fclose(fout);

38 Chapter 3. The elements of C


exit(0);
}

Try typing this program in and run it. You should get a file called ”output.data” in your directory which contains
the values 0...9. Note we are using fprintf with a file pointer and not printf.

3.12.2 Reading from Files


We have already seen the fprintf version of printf. It will be little surprise to learn there is a fscanf
version of the scanf function for formatted file input. Like fprintf, fscanf expects a file pointer as its first
argument. The second argument is a format string similar to that for scanf().
EOF is a character which indicates the end of a file. It is returned by read commands when they try to read
beyond the end of a file. It is possible to check for the EOF character using the feof() function which takes a
file pointer and returns true if the file pointer is at the end of the file. A common way of reading to the end of a file
is simply to use a while loop until feof returns true.

FILE *fp;
if ((fp = fopen("data.txt", "r")) == NULL)
{
printf("Cannot open %s\n", "data.txt");
exit(-1);
}
while (!feof(fp))
{
/* read data from the file data.txt */
}
fclose(fp);

Expanding on the above example, read from the file data.txt which contains a column of ints and a columns
of floats.

#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
int i;
float x;
if ((fp = fopen("data.txt", "r")) == NULL)
{
printf("Cannot open %s\n", "data.txt");
exit(-1);
}
while (!feof(fp))
{
fscanf(fp, "%d %f\n", &i,&x);

/* Do something with i and x */


}

fclose(fp);
exit(0);
}

Note, you MUST use \n in the format string of an fscanf function to read over new-line characters when
reading data from a file. This is different to the way scanf is used to read data input from the keyboard, where
\n should NOT be used.

3.12. File I/O 39


Exercise 3.12
(a)Recreate the one hundred element arrays of sin and cos you created in Exercise 3.10. Print these
arrays out to a file in the form of a table. The first line of the table should contain the first element
of both arrays, the second line the second element, an so on.
Consult a demonstrator before attempting part (b).
(b)Create a program to read in the values from the file you have just created and print them to the
screen.

40 Chapter 3. The elements of C


Further C 4
This chapter contains information about various supplementary topics that do not belong in the main chapter.
Some may be needed later in the course, but are not directly connected with learning the basic language (such as
the section on generating random numbers). Others are considered additional to the basic C course because they
cover information about the language that is either of a reference nature, or is more obscure or advanced than that
in the main part of the course.
If you have reached this point before the end of the second session, you should read through some or all of the
sections in this chapter, and perhaps attempt the exercises. Otherwise, you should probably just skim through to
familiarise yourself with the information included so you can refer back to any of it later if necessary. You may
ask a demonstrator to assess you now for CO00 and assign and book one of the problems for you.

4.1 Random Numbers


In order to generate a random double in the range 0.0 to 1.0 you can use the drand48()1 function
which takes no parameters, and returns a random number in the above interval. It is part of the standard library
stdlib.h.
Ideally, before calling drand48() for the first time, you should call the function srand48() once to seed
the random number generator. This does not return any value but does expect an integer seed value. The same seed
value will produce the same sequence of random numbers (hence the tag “pseudo-random”!). (Although it is not
recommended practice, constant default initializer values will be supplied automatically if drand48() is called
without seeding the random number generator).
An example of using random numbers is

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int
main()
{
FILE *fout;
int i;
double s,d;

if ((fout = fopen("random.dat", "w")) == NULL)


{ printf("Cannot open random.dat\n"); exit(-1); }

/* Seed the random number generator */


srand48((unsigned int) time(NULL));

for (i=0; i<1000; i++)


{
/* get two random numbers */
s = drand48();
d = drand48();

fprintf(fout, "%f\t%f\n", s, d);


}
fclose(fout);
exit(0);
}
1 For those interested, drand48() generates pseudo-random numbers with a uniform distribution using the well-known linear congruential

algorithm and 48-bit integer arithmetic.

41
If you have time run the program above and plot the points in the file random.dat with gnuplot. You should see
a random distribution in the graph.
If you are not familiar with gnuplot, type gnuplot at the command prompt. This will start gnuplot and the
prompt will change to gnuplot > at this prompt type plot "random.dat". To quit from gnuplot simply
type quit at the command prompt.

Optional Exercise 4.1


Write a program that simulates the UK National lottery by selecting six different whole numbers in
the range 1 - 49.

The function drand48 is a Unix function and not available on Windows platforms. If you are compiling your
program on a PC you should use the related rand function. This returns an integer between 0 and RAND MAX,
so if you want a double between 0 and 1, like drand48() creates, do:

double random_number;
random_number=rand()/(double)RAND_MAX;

4.2 Multi-dimensional Arrays


Although less common in other forms of programming, it is often the case in scientific programming that data
which is 2-, 3- (or even higher-) dimensional in character needs to be manipulated. Image data is a classic example
as it effectively consists of an x-y grid of brightness values. Or, as another example, imagine an experiment where
you are measuring a field-strength over a surface at x,y positions. You might end up with a matrix of data as
follows:

x 0 1 2 3 4
y

0 0.32 0.45 0.56 0.42 0.24

1 0.46 0.75 0.93 0.67 0.39

2 0.17 0.31 0.46 0.38 0.22

To be able to store and process this data, it is natural to want to use a 2-dimensional array of a floating-point
type. You could declare such an array as follows:

double V[3][5];

This creates a 3-row by 5-column array of doubles comprising the following elements (each of which,
remember, is the same as a simple double variable):

V[0][0], V[0][1], V[0][2], V[0][3], V[0][4], V[1][0], V[1][1], ...


... V[2][2], V[2][3], V[2][4]

As with simple arrays, it is also possible to initialise a 2-D array; here’s how we could input the above example
data into our array (take careful note of where the semi-colon and all the commas go!):

double V[][] = { {0.32, 0.45, 0.56, 0.42, 0.24},


{0.46, 0.75, 0.93, 0.67, 0.39},
{0.17, 0.31, 0.46, 0.38, 0.22} };

42 Chapter 4. Further C
Even though we think of the array as two-dimensional, the computer stores the array in the linear sequence
shown above (ie. element V[1][0] follows immediately after V[0][4]). Pay attention to the fact that the
right-hand index (ie. the column number) changes fastest in the sequence: you should always try to process 2-
D arrays so that you access the elements sequentially along the rows, rather than down the columns. This is
particularly important if you are using large arrays: doing it the other way will make your program run many
times more slowly, because the system will be continually having to access the array elements out of sequence! To
illustrate this, here’s an example of the correct way to access our array:

/* .... */
int x, y;

for (y=0; y<=2; y++)


{
for (x=0; x<=4, x++)
something = V[y][x] * or_other;
}

It is natural to want to think in terms of (x,y), but in C you should always think (y,x) or “row, column”. Another
somewhat non-intuitive feature that is especially noticeable with multi-dimensional arrays is that the indices run
from 0 ... N-1: ie. we had to declare V[3][5] but the last element of the array is V[2][4]. This always
requires careful thought to avoid making mistakes!
Finally, it is possible to have 3- and higher-dimensional arrays in C; for instance, the syntax for a 3-D
declaration would be:

int threeD[2][5][10]; /* This creates a 2x5x10 int array with


* elements threeD[0][0][0] ....
* threeD[1][4][9] */

As with 2-D, it is the right-most index which changes fastest, then the middle one, and the left one changes
slowest. One could keep extending the principle to many higher dimensions. However, 3-D arrays are already
unwieldy and in practice one rarely uses more than 2-D arrays, and even those are avoided when simple arrays can
be used. The general rule, as always in programming, is to keep things as simple as possible.

4.3 String manipulation functions


A string is a sequence of zero or more characters surrounded in double quotes, for example

"I am a string"
"Hello World!"
"" /* The empty string */

The double quotes are not part of the string, they only serve to delimit it. The C programming language does
not have a string data type, instead a string is stored as an array of characters with a NULL ( ’\0’) character at the
end. When you need a character array to hold a string you need to declare the array to be one element larger than
the string so that there is room for the NULL character. For example, if you are declaring an array called answer
to hold the value ”false” you should declare it as

char answer[6];

Even though there is no string type in C, strings are so useful that there is an extensive set of library functions
for manipulating strings.
To gain access to the string functions, you must include the following header file in your program:

#include <string.h>

4.3. String manipulation functions 43


The most commonly-used string manipulation functions are as follows:
Function name Description
strcmp(s1, s2) Compares s1 to s2, returns 0 if s1 == s2
strlen(s1) Get the length of the string s1
strcpy(s1, s2) Copies string s2 into s1
strncpy(s1, s2, int n) Copies n characters from string s2 into s1
strcat(s1, s2) Concatenates string s2 onto the end of s1
strncat(s1, s2, int n) Append n characters from string s2 to string sl
For example:

/* Example 1 */
#include <stdio.h>
#include <stdlib.h>
/*The next line must always be used with string functions*/
#include <string.h>

int main()
{
char reply[4];

printf("Do you want to run this program? [yes/no] ");


scanf("%s", reply);

if(strcmp(reply, "no") == 0) /* 0 means they are the same */


exit(1);
else
/* The rest of the program... */
}

/* Example 2 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
char str[100];

printf("Enter a string\n");
scanf("%s", str);
printf("The length of (%s) is %d \n", str, strlen(str) );

exit(0);
}

/* Example 3 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
char firstname[20], surname[20];
char name[42], name_official[42];

strcpy(firstname, "Tom");
strncpy(surname, "Jones", strlen("Jones");

strcpy(name, firstname);
strcpy(name_official, surname);

44 Chapter 4. Further C
strcat(name, surname);
strcat(name_official, surname);

printf("name = %s\n", name);


printf("official name = %s\n", name_official);

exit(0);
}

Optional Exercise 4.3


Write a function that returns true if an input string is a palindrome. A palindrome is a word that
reads the same backwards as it does forwards e.g. ABBA.

Another very useful pair of string manipulation functions are sprintf() and sscanf() which are in the
<stdio.h> library. They are similar to printf and scanf but instead of printing a formatted string to the
screen (which we refer to as standard output or stdout) or reading a formatted string from the keyboard (standard
input or stdin) they print and read formatted strings to/from strings.
For example:

#include <stdio.h>

int main()
{
char str[20];
int i;

printf("Enter an integer: ");


scanf("%d", &i);

sprintf(str, "You entered %d\n", i);


printf("%s", str);

exit(0);
}

4.4 switch statements


A switch statement allows a single variable to be compared with several possible integer constants. It can often
be used instead of a sequence of the if-else-if statements we have seen before. The syntax for the switch
statement is

switch ( variable )
{
case integer_constant1:
statements...;
case integer_constant2:
statements...;
default:
statements...;
}

Each case is checked in turn and if the variable matches the value of a case constant those statements are executed
and then all the statements in the subsequent blocks are executed until execution reaches the end of the switch
statement. It is a common mistake to think that execution of the switch statement ends once the statements
matching the correct case are executed. For this reason a break statement is usually put at the end of each block
of statements. An example will make this clear.

4.4. switch statements 45


Note: the default case is a special one that is executed if no other constant in the switch statement
matches the variable. The default case is optional.
For example, suppose we have an integer which is read, say, from the command line. Until now we would
check it as a series of if- else- ifs.

if (q==1)
{
printf("q equals 1\n");
}
else if (q==2)
{
printf("q equals 2\n");
}
else if (q==3)
{
printf("q equals 3\n");
}
else
{
printf("q does not equal 1, 2 or 3\n");
}

However, using a switch statement can be a neater solution.

switch (q)
{
case 1:
printf("q equals 1\n");
break;
case 2:
printf("q equals 2\n");
break;
case 3:
printf("q equals 3\n");
break;
default:
printf("q does not equal 1, 2 or 3\n");
}

The break statement is used to stop executing the switch statement at the end of each case. If you forget
the break statement, execution will continue with the next case. Sometimes, though, it actually is useful to be
able to execute a block of statements for a range of cases, e.g. if (letter==’A’ || letter==’E’ ||
letter==’I’) etc if we are counting vowels. The next example leaves out the break statement to show you
how to match cases with a switch statement.

char letter;
/*Some code which inputs something into letter*/

switch (letter)
{
case ’A’:
case ’E’:
case ’I’:
case ’O’:
case ’U’:
numberofvowels++;
break;
case ’ ’:
numberofspaces++;
break;

46 Chapter 4. Further C
default:
numberofconsonants++;
}

If letter contains ’E’ then we start executing the first block of statements under the ’E’ option. You’ll notice
that there is no block under the ’E’ case. Since there is no break statement execution continues with the ’I’ case,
and then the ’O’ case and then the ’U’ case where we increment numberofvowels. Since there is a break
statement here we ’break’ out of the switch statement.
The default option is a special option that is executed if no other option in the switch statement is
executed. In our last example, if letter was ’t’ then none of our conditions (’A’,’E’,’I’,’O’,’U’,’ ’) are met so the
statements after the default case are executed.
Remember: switch statements can ONLY be used with integer types (ie. int, char, short, long,
etc) and that the values after the case’s must evaluate to integer constants.

4.5 More about variables

4.5.1 Scope
The scope of a variable is the region of the program in which it is accessible. Variables declared inside functions
(which are the only sort we have so far encountered) are accessible only from within the function in which they are
declared. They are thus sometimes referred to as local variables.
This also means that it is possible to have local variables with the same name in different functions. In terms of
the box analogy, you can have different boxes in different parts of the program with the same label. The contents
of these different boxes are entirely separate and cannot be mixed up. For example:

#include <stdlib.h>
#include <stdio.h>

void afunction()
{
int i;
i = 4;
printf("The value of i inside afunction() is: %d\n", i);
return;
}

int main()
{
int i;
i = 1;
printf("The value of i inside main() before is: %d\n", i);
afunction();
printf("The value of i inside main() after is: %d\n", i);
exit(0);
}

Running the above program should generate the following output; as you can see, the value of i in main()
is unaffected by the value of i in afunction().

The value of i inside main() before is: 1


The value of i inside function afunction() is: 4
The value of i inside main() after is: 1

4.5. More about variables 47


4.5.2 Global Variables
Up until now all the variables used have been local. C does allow variables which are external to any function and
thus globally available to all functions. This is done quite simply by putting the variable declaration outside all
your functions. In other resepects, they look the same. Try entering and running this program

#include <stdlib.h>
#include <stdio.h>

int a_global_variable;

void afunction()
{
printf("%d\n", a_global_variable);
return;
}

int main()
{
a_global_variable = 5;
afunction();
exit(0);
}

Global variables are useful because they allow different functions direct access to the same data; however,
they are also potentially dangerous because they allow bugs in one part of a program to affect the data used by a
different part of the program. They also reduce the modularity of your code: for example, if you have written a
particularly useful function you may want to re-use it in other programs2 . However, if the function you want to
re-use depends on global variables it makes it much harder to use the function in a different program, because you
also have to ensure that the same global variables are made available and that they have meaningful values. For
both these reasons, it is best to avoid using global variables except when they make your program much clearer or
more efficient. In general you should pass data between functions using arguments and return values.
Note that global variables are only in scope for functions which appear after the global variable has been
declared. Hence, it is normal for global variable declarations to occur at the top of a program, usually after header
file includes.

4.5.3 Storage classes


We have seen that the scope of local variables is limited to the function they are declared in. The effect of this is
that when the function returns, the variables are no longer used and are destroyed, we say the variable has gone
’out of scope’. What happens if you actually want to remember the value of a variable between function calls?
One option is to use a global variable but as we have seen this is not always wise. Instead we can tell the compiler
to remember the value of a variable in a function between calls to the function by using the keyword static
before declaring the variable.
Compile and run the following piece of code as an example.

#include <stdlib.h>
#include <stdio.h>

void counter()
{
int i=0;
static int j=0;
printf("The value of i is: %d\n", i);
printf("The value of j is: %d\n", j);
j = j + 1;
2 Such code re-use is a very good aim; it saves time and tends to make programs more reliable, because the bugs in re-used code are more

likely to have already been found and fixed.

48 Chapter 4. Further C
i = i + 1;

return;
}

int main()
{
counter();
counter();
counter();
exit(0);
}

4.6 The C Pre-Processor


As its name suggests, the C Pre-Processor processes the code of your program before the compiler-proper sees it.
It is essentially a fairly simple utility which performs textual substitutions on your code according to directives you
give it. Pre-processor directives are identified as lines which start with commands prefixed by the hash character,
“ #”, and can occur at any point in your program. In practice, however, they are normally confined to the top of
a program. There are several directives available, however two of them are far more widely used than any of the
others: #include and #define.
We have already encountered #include on numerous occasions. What it literally does is to replace itself
with the contents of the file specified. Normally it is used to include standard library header files, and header files
written by the programmer. In principle, though, it could be used to include any file.
#define is largely used as a means of being able to define easily-identifiable constants which may be used
throughout a program, and which can be modified easily in one place. By convention, #define constants all
have upper-case names to help differentiate them from variables and other names used in a program. You should
definitely stick to this convention in your programs. The values of such constants can also be over-ridden at
compile-time which can be a convenient way of altering some default setting for a program without having to edit
it. In its simplest mode, it is used as follows:

#define CONSTANT 1.0


#define NAME "Boris" /* They can be any text you like, not just
#define TRUE 1 numerical values or strings */

/* Then later in your program you might do things like */

y = CONSTANT * x + c;

if (y == TRUE)
printf("Hello %s\n", NAME);

Note, however, that #define’d constants do not get substituted inside strings.
It is important not to put a semi-colon at the end of a #define directive because the value of the #define
is substituted straight into your code (semi-colon as well) which is generally not what you want. Remember the
rule ‘only put a semi-colon at the end of a statement’.

4.6. The C Pre-Processor 49


4.7 struct, typedef and Complex Numbers
C has no built in complex number type but does allow us to define our own types using a typedef command.
The typedef command ’defines’ a new variable’s type. The typedef keyword can be used to create any definition
you like for example, to declare a new data type called ’digits’ (which we’ll make integers)

typedef int digits;

Which we could use to declare variables.

digits a,b,c;

Now a,b,c are variables of type digits (which in turn are ints). This is not particularly useful for redefining data
types as we’ve done here but a very useful feature for defining new types such as complex numbers as we shall
soon see.
Another useful feature of the C language is the struct, which implements complicated data structures. A
data structure is a user-defined data type which, like an array, can be used to group a collection of data elements
under a single name. Unlike an array, in a data structure these elements may be of different types. In fact, the
elements of a structure can be themselves be arrays or even other structures! Hence, arbitrarily complicated data
structures can be represented in C.
In scientific C programming, structs are useful for storing and manipulating complex numbers, and we will
deal with this topic in detail. First, a more general example of the definition of a struct:

struct Person /* "Person" is the "tag" for this type of structure*/


{
char gender; /* ’m’ or ’f’ */
int age; /* in years */
float height, weight; /* in metres and kg */
char name[64]; /* allow for very long names! */
} John, Janet; /* When defining a struct you may optionally also */
/* include declarations of structs of that type */

struct Family
{
char surname[64];
struct Person members[20]; /* Allow for a lot of children*/
/* Note that this is an array of struct Person’s!*/
} ; /* The semi-colon is mandatory, even when there are no declarations */

/* Instead/in addition, once a struct has been defined,


further declarations can be made thus */

struct Family Robinson; /*Declares the first struct Family structure */


struct Person Peter, Jane; /*Declares two further "struct Person" structures*/

/* Elements are manipulated as follows */


John.age = 55; /* This is an element of type int */
John.gender = ’m’;
strcpy(John.name, "John");

Robinson.members[0] = John; /* Robinson.members[0] is of type struct Person */


/* so we can assign John to it */
Robinson.members[1] = Janet;
Robinson.members[2] = Peter; Robinson.members[3] = Jane;

printf("%s’s age = %d\n", Robinson.members[0].name, Robinson.members[0].age);


/* Equivalent to John.name and John.age */

50 Chapter 4. Further C
As you can see, using structs is very powerful, but can also get quite confusing until you get used to it!
Fortunately, the sorts of data structures typically found in scientific code are very simple, such as complex numbers.
Since complex numbers are composed of a real and imaginary part we can define a structure that defines a basic
complex number. We will use complex numbers as an example of using structures. A struct for a complex number
could be defined as:

struct ComplexNumber {
double real;
double imag;
};

The defines a structure, of type struct ComplexNumber, which in this example contains two doubles
called real and imag. It is a common convention that the names of structures begin with an uppercase letter.
Using this definition, variables of type ComplexNumber can be declared as
float a, b;
int c;
struct ComplexNumber d, e;

We can access the elements of the complex number using the dot (.) operator for example
d.real = 4.0;
d.imag = 6.5; /* this defines d = 4.0 + 6.5i */
printf("%f + %fi\n", d.real, d.imag);

As an alternative, to avoid having to declare every complex number using “ struct ComplexNumber” we
could use typedef to define a new datatype called complex:
typedef struct {
double real;
double imag;
} complex;

We now have a new datatype called ’complex’. We can declare new variables as ’complex’ by
complex d, e;

and use these variables as we’ve just seen above.


Since we have defined a new complex type we will also need to define a set of operations on complex
numbers such as complex addition and mutliplication.
Two files ( complex.h and complex.c) are available from the computing course homepage3 that contain
the definition of a complex number type and contain all the complex number operations. You can #include
"complex.h" at the top of your program and then use the following functions defined in complex.h for
complex addition, subtraction, multiplication, division and modulus. A function print complex exists for
printing complex numbers.
complex sum(complex x, complex y);
complex diff(complex x, complex y);
complex prod(complex x, complex y);
complex quot(complex x, complex y);
double modulus(complex x);
char *toString(complex x);
You will need to compile the complex.c file with your own source code. You can use the gcc command
described in Appendix B.
3 http://www-teaching.physics.ox.ac.uk/computing/

4.7. struct, typedef and Complex Numbers 51


gcc -o MyCode complex.c MyCode.c -lm

Here is an example using complex numbers (you can find it with the complex.c and complex.h codes
on the computing course homepage). Save this file as testComplex.c, compile and run it as described in
Appendix B.

#include "complex.h"

int
main()
{
complex z,w;

printf("Enter first complex number:\n");


printf("Real part: ");
scanf("%lf",&z.real);
printf("Imaginary part: ");
scanf("%lf",&z.imag);

printf("Enter second complex number:\n");


printf("Real part: ");
scanf("%lf",&w.real);
printf("Imaginary part: ");
scanf("%lf",&w.imag);

printf("Sum is: ");


print_complex(sum(z,w));

printf("Difference is: ");


print_complex(diff(z,w));

printf("Product is: ");


print_complex(prod(z,w));

if(w.real != 0 && w.imag != 0)


{
printf("z/w is: ");
print_complex(quot(z,w));
}

exit(0);
}

4.8 Taking your interest further


If you’ve enjoyed learning C and would like to know more about the language then some online resources are
available on the Physics Computing Laboratory website where you will also find an online version of this manual.

52 Chapter 4. Further C
Graphical output using Gnuplot
from within your program
A
In this course you will use the Gnuplot package to generate graphical output. It is an extremely powerful and
flexible program, although it does have a rather steep learning curve for beginners.
The online ’Useful Information about the System’ link has a section on how to use gnuplot from the Unix
command line. In many cases, it will be easiest for you to output any results you want to plot into a file as x-y data
pairs. You can then use gnuplot from the command line to generate graphs.
However, more ambitious students may like to attempt to plot directly from their C programs. In which case,
you should read this section.
You will need to download two files (gnuplot i.c and gnuplot i.h) from the Downloads link on the course
website (http://www-teaching.physics.ox.ac.uk/computing/Downloads/downloads.html). These files should be
saved to the same directory as the program that you are writing. You will need to compile gnuplot i.c with your
program on the command line. We suggest you read appendix B for instructions on how to do this. Briefly, on the
command line, you will need to run the command

gcc -O -Wall -o myprogram gnuplot_i.c myprogram.c -lm

where you can replace myprogram.c and myprogram with the name of your program with and without the .c
extension respectivey.
In any program from which you would like to produce graphical output you must include the file gnuplot i.h
in the same way as you include the stdio and stdlib libraries to have access to the standard I/O and libraries
respectively. However, instead of the angle brackets used to include stdio and stdlib you will need to use
double quotes i.e.

#include "gnuplot_i.h"

Then you will need to create a variable for the gnuplot ctrl * type and initialise it using the
gnuplot init() function in much the same way we had to initialise a FILE * type with fopen.

gnuplot_ctrl *g;
g = gnuplot_init();

g is merely a suggested name for your variable—you are free to name it like any other variable.
The construct gnuplot ctrl *g; should look familiar to you, you will have seen something similar in the
section on file I/O. In the same way we ignored pointers in that section we will ignore them here also. If you just
follow the instructions in this section you will be able to think of g as an ordinary gnuplot ctrl * type.
Your program is now ready to plot graphs. There are several functions available for plotting equations and data
points, it is probably easiest to study some examples.

#include <stdio.h>
#include <stdlib.h>
#include "gnuplot_i.h"

#define NPOINTS 50
#define SLEEP_LGTH 2
int main(int argc, char *argv[])
{
gnuplot_ctrl *g1;
double x[NPOINTS] ;
double y[NPOINTS] ;

53
int i ;

g1 = gnuplot_init() ;

gnuplot_setstyle(g1, "lines") ;

/* Plot a straight line ax+b */


printf("Plotting y = x\n") ;
gnuplot_plot_slope(g1, 1.0, 0.0, "unity slope") ;
sleep(SLEEP_LGTH) ;

gnuplot_resetplot(g1) ;

/* Plot a function (sin(x)) */


printf("Plotting y = sin(x)\n") ;
gnuplot_plot_equation(g1, "sin(x)", "sine") ;
sleep(SLEEP_LGTH) ;

gnuplot_resetplot(g1) ;

/* Plot an array of variables */


printf("Plotting y = xˆ2\n") ;
for (i=0 ; i<NPOINTS ; i++)
{
x[i] = (double)i ;
y[i] = (double)i * (double)i ;
}
gnuplot_setstyle(g1, "points") ;
gnuplot_plot_xy(g1, x, y, NPOINTS, "y = x*x") ;
sleep(SLEEP_LGTH) ;

gnuplot_close(g1) ;
}

There are probably many new functions in this piece of code that you will not recognise, don’t worry, there are
always new functions to find even after years of programming.
The #define NPOINTS 50 and #define SLEEP LGTH 2 ’define’ the constants NPOINTS and
SLEEP LGTH to be 50 and 2 respectively. This means that wherever the pre-processor finds these names in
the code it substitutes their values. This was explained in section 4.6. The sleep() function literally sends the
program to sleep for the number of seconds given to it as an argument. This is useful for when, like in our case,
you want to be able to see some results before the program does more calculations.
The first plotting command we see in our program is the gnuplot plot slope function. This plots a
slope on our graph. (Remember the generalised equation for a straight line is y=ax+b). The function prototype for
gnuplot plot slope can be found in the gnuplot i.h file and is

void gnuplot_plot_slope(
gnuplot_ctrl *handle,
double a,
double b,
char *title) ;

where we have already seen the gnuplot ctrl * variable, the title is the title of the graph and the a and
b variables are the values in the equation we are plotting. Don’t be put off by the char *. This again is a pointer
but it can be treated here as a char[].
Before we can plot another graph using our g1 variable we need to ’reset’ it. Hence we see the
gnuplot resetplot(g1) ; This should be used before reusing any gnuplot ctrl variable.
The next plotting command we see in our example is the gnuplot plot equation. This function does
”exactly what it says on the tin” and the function prototype is

54 Appendix A. Graphical output using Gnuplot from within your program


void gnuplot_plot_equation(
gnuplot_ctrl *handle,
char *equation,
char *title) ;

the handle and title arguments are the same as we have seen already and the equation argument is any
equation that is recognised by gnuplot. In our example this was sin(x). It would be useful to consult the gnuplot
documentation before using this function to see the types of equations gnuplot can plot.
The final plotting command we see is the gnuplot plot xy which will plot a range of xy points. The
function prototype is

void gnuplot_plot_xy(
gnuplot_ctrl *handle,
double *x,
double *y,
int n,
char *title) ;

where the handle and title arguments are the same as the previous commands. You can see our old friends
’pointers’ again. This time think of the double * type as an array of doubles. The int n; argument is the
number of points in the x and y array to plot (the arrays x and y should contain the same number of elements).
It is important that every session opened with the gnuplot init() function is closed using the
gnuplot close() function. Otherwise your program may leave memory locked after the program has finished
running, this unwanted side effect is called memory leaking and in extreme cases it can cause the computer to run
out of memory and crash.
There are a number of useful functions that you can use on your graph. We have seen one already, the
gnuplot setstyle function,

void gnuplot_setstyle(
gnuplot_ctrl *handle,
char *style) ;

Where the style argument can be one of:

lines
points
linespoints
impulses
dots
steps
errorbars
boxes
boxeserrorbars

The gnuplot set xlabel and gnuplot set ylabel functions are used to print labels on the x and y
axes of your graph. The function prototype for both is

void gnuplot_set_xlabel(gnuplot_ctrl * h, char * label);


void gnuplot_set_xlabel(gnuplot_ctrl * h, char * label);

A useful function is gnuplot cmd which executes a gnuplot command. The function prototype is

void gnuplot_cmd(gnuplot_ctrl * handle, char * cmd, ...);

55
which looks more difficult than it actually is. Basically, it is the same prototype as the fprintf function we are
already familiar with. For example, the gnuplot command for setting a title (in this case ’temperature’) on the x
axis is set xlabel ’temperature’. Suppose we have a string called x title that contains the title for
the x axis then we can use the gnuplot cmd function in the following way

gnuplot_cmd(gp, "set title %s", x_title);

This function is especially useful when you want to save your graph. The gnuplot i.h header has no
prototype for saving your graph to a file so we must use gnuplot cmd. Saving graphs in gnuplot requires
several steps, first we must set the terminal type, then set the name of the file where our graph will be saved and
then plot the graph again. The following code will save a graph in a file called mygraph.ps

gnuplot_cmd(gp, "set terminal postscript color");


gnuplot_cmd(gp, "set output \"mygraph.ps\"");
gnuplot_cmd(gp, "replot");
gnuplot_cmd(gp, "set terminal x11");

Optional Exercise A
Write a program that populates a large array (about 20,000 elements) of doubles (x and y) as follows:
x[0] = 0.5 and y[0] = 0.0
Now create a loop that goes from i=1 to 20000, for each i in the loop pick a random number, r, in
the range [0,1] and set the values of x[i], y[i] according to:
½
x[i] = 0.05x[i − 1]
if r ≤ 0.1
y[i] = 0.6y[i − 1]

½
x[i] = 0.05x[i − 1]
if 0.1 < r ≤ 0.2
y[i] = −0.5y[i − 1] + 1.0

½
x[i] = 0.46x[i − 1] − 0.32y[i − 1]
if 0.2 < r ≤ 0.4
y[i] = 0.39x[i − 1] + 0.38y[i − 1] + 0.6

½
x[i] = 0.47x[i − 1] − 0.15y[i − 1]
if 0.4 < r ≤ 0.6
y[i] = 0.17x[i − 1] + 0.42y[i − 1] + 1.1

½
x[i] = 0.43x[i − 1] + 0.28y[i − 1]
if 0.6 < r ≤ 0.8
y[i] = −0.25x[i − 1] + 0.45y[i − 1] + 1.0

½
x[i] = 0.42x[i − 1] + 0.26y[i − 1]
if 0.8 < r ≤ 1.0
y[i] = −0.35x[i − 1] + 0.31y[i − 1] + 0.7

Plot the values of x and y and comment on the graph. You may be surprised by what you plot!

56 Appendix A. Graphical output using Gnuplot from within your program


Programming from
command-line
the Unix B
B.1 Command-line editing and compilation
As you worked through this manual it was assumed that you were compiling your programs within the SciTE
environment (section 2.3.2). It is a good idea to know how to compile your programs from the command line. 1
As an example we will write another simple hello program, hello.c. As we are not using SciTE here (and
indeed it cannot be used unless you have logged in graphically; eg. from a Sun Ray), you will need to use one
of the command-line editors. More experienced Unix or Linux users may use their favourite Unix editor, such
as emacs or vi. For other students we recommend the pico editor. So, on the command line, type: pico
hello.c and enter the code as below.

#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("Hello\n");
exit(0);
}

To save (or “write Out”) the program to the file from pico you need to type Control and O together (this is
abbreviated as ˆO). (To exit pico you type ˆX).
To compile the program, use the following command:
gcc -o hello hello.c
Before continuing we will take a few moments to explain this command. The compiler we are using is the
GNU C compiler ( gcc) so this is the first command we run. The gcc command needs to know the name of the
files to compile otherwise it will complain that it has no input files, so we give it our program ( hello.c) as an
argument. The -o flag tells the compiler to send the output of the compilation process to the file hello (you
could type any filename you like after the -o flag, but it’s conventional to always use the same name you used for
the source code, minus the .c suffix). If you leave out the -o hello then the compiler will automatically save
the compiled program in a file called a.out.
If the compilation was successful an executable file will be created which you can run by typing:

./hello

An important thing to remember: if you include the math.h header in your program then you will need to link
the maths library during the compilation process. This is done by adding -lm at the end of the compile command
but it can be easily forgotton, so be careful.
gcc -o mymath mymath.c -lm
If you have written your program in several files that need to be compiled into one executable or if you use the
code in another file then you will have to compile all the programs at the same time. A common example of this is
when you are using the gnuplot extensions (gnuplot i.c & gnuplot i.h).
gcc -o mygraph mygraph.c gnuplot i.c -lm
This tells the compiler to compile the programs mygraph.c and gnuplot i.c into a single executable called
mygraph and to use the mathematical library.

1 For those who are more familiar with another editor and have decided not to use SciTE this is how you will have to compile your programs

57
B.2 Advanced Compilation using make
This section is only intended for students who are already experienced at programming on Unix or Linux.
For larger projects a Unix utility called make may be used. make uses a file in the current directory, by
default called makefile or Makefile, which must be written by the programmer, that contains instructions
on how to compile a project that may contain several files.
A fairly simple Makefile might be:

all: myproject

myproject: myfile1.o myfile2.o myfile3.o myfile4.o


gcc -o myproject myfile1.o myfile2.o myfile3.o myfile4.o -lm

clean:
rm *.o myproject

Makefiles are not free-format. The lines starting gcc and rm must each start with a Tab character, not spaces.
After creating the Makefile, to build the project simply type make on the command line. The make program will
look for a valid target in the Makefile. A target is a user defined keyword at the beginning of a line and which ends
with a colon.
In our simple Makefile the targets are all, myproject and clean. To be useful, at least some of the
Makefile’s targets must contain rules. rules are simply a list of Unix commands (often compilation commands)
that should be executed if that target is invoked. As stated above, the command lines in a rule must be prefixed by
a Tab character.
By default simply running the make command will invoke the first target, in this case all. If we want to
pick a specific target to run then we can type make targetname eg. make myproject.
On the same line as the target is a list of dependencies. These are files or other targets that this target ’depends’
on.
In our example the target all only depends on the target myproject2 which in turn depends on the files
myfile1.o myfile2.o myfile3.o and myfile4.o. Note that these end in .o and not .c. This is because
they refer to the object files which a compiler generates when compiling a program with multiple source files.
Since all only depends on myproject we would achieve the same result by typing: make myproject
as we would by typing make.
During compilation every source ( .c) file gets compiled into an object ( .o) file and then any necessary
libraries, e.g. the math ( -lm) library, are linked in to produce the final executable. During the make process
make will compare the time and date of the object files to the source files and recompile any object files that
are out of date and then build the target using the target’s rules (the command lines following the target and its
dependencies). You will note also that the files here are also object files. This is done for speed since the .c files
are already compiled into .o files we only need to combine the object files with the libraries. If we had put .c files
here then each .c file would be recompiled!
The more observant of you will note that there is no rule to compile .c files into .o files. This is because
make has built-in rules that do this for you so you don’t have to worry about it. Note: The command gcc -c
myfile.c will create myfile.o.
In our example, the clean target is special because it does not do any compilation. By typing:
make clean
you can clean up the project i.e. remove the object and executable files. This is a nice simple way of tidying up
your working area.

2 In this case the all target isn’t particularly useful, because it does nothing more than its dependency myproject. However, in more

complicated Makefiles the default target will typically depend on several other targets and/or files.

58 Appendix B. Programming from the Unix command-line


Reserved Words and Program
Filenames
C
The C language reserves special words, which we call keywords, you must not use any of C’s keywords as your
variable names. The words reserved by C are:
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
Do not use any of the following words either (although they are not C reserved words, they conflict with the
names of commonly-used C library functions):
abort clock getenv rand srand
abs close labs read strcmp
atof div malloc remove strcpy
atoi exit open rename system
atol free printf scanf time
calloc getchar putchar signal write
You should also avoid all the names defined in the math.h library (you must avoid them if you
#include <math.h>):
acos cos floor sin tanh
asin cosh log sinh
atan exp log10 sqrt
ceil fabs pow tan
The above two lists of standard library function names are far from exhaustive; however they do include the
majority of commonly-used functions and/or functions with names that might conceivably be chosen unwittingly.
Finally, the Solaris Unix system has a large number of built-in commands and standard system utilities. If you
give your program the same name as some of these utilities you can get peculiar problems or very unexpected
results! In particular, do NOT give your programs any of the following names:
break.c end.c if.c set.c time.c
case.c exit.c jobs.c source.c wait.c
continue.c for.c limit.c stop.c while.c
default.c goto.c nice.c switch.c
else.c history.c repeat.c test.c
Again, this is not an exhaustive list, and it can vary from system to system; however, by avoiding these filenames
you should avoid the most common problems.

59
Index

=, 27 hello world, 13
==, 27
if statement, 26
abs(), 32 if-else-if statement, 45
acos(), 32 if-else-if statements, 27
arithmetic operators, 22 increment operator, 24
arrays, 33 int, 15
arrays, declaration, 33
arrays, initialisation, 34 log(), 32
arrays, multidimensional, 42 log10(), 32
asin(), 32 logical operator, 27
assignment, 27
atan(), 32 main() function, 13
make, 58
C Pre-processor, 49 makefile, 58
case sensitivity, 12 math.h, 32
ceil(), 32 mathematical functions, 31
char, 15
code layout, 15 pow(), 32
compile-time errors, 11 pre-processor directive, 13
Compiling programs, 11 printf(), 14, 18
complex numbers, 50
cos(), 32 random numbers, 41
relational operators, 27
Debugging programs, 11 reserved Words, 59
decrement operator, 24 return type, 13
do...while loop, 31 run-time errors, 11
double, 15 Running programs, 11
drand48(), 41
scanf(), 21
erf(), 32 SciTE, 10
escape sequence, 19 sin(), 32
exit(), 14 sleep(), 54
exp(), 32 sprintf(), 45
sqrt(), 32
fabs(), 32 srand48(), 41
file I/O, 37 sscanf(), 45
file pointer, 37 stdio.h, 13
float, 15 stdlib.h, 13
floor(), 32 strcat, 44
fmod(), 32 strcmp, 44
for loop, 28, 29 strcpy, 44
function parameters, 35 strlen, 44
Functions, 13 strncat, 44
functions, 34 strncpy, 44
struct, 50
gcc, 11, 57 switch statement, 45
gnuplot, 42, 53
gnuplot i.h, 53 tan(), 32

60
typedef, 50

variables, assignment, 16
variables, casting, 23
variables, declaration, 16
variables, global, 48
variables, local, 47
variables, scope, 47
variables, static, 48

while loop, 30

Index 61

You might also like