Handbook C
Handbook C
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
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
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.
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:
See also Section 1.3.2 of the Practical Course Handbook for more details on keeping a logbook.
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.
Typewriter font is used for anything typed, e.g. C code or Unix commands.
New terms and jargon are written in italics the first time they are used.
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.
#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.
• 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”).
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.
#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
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!
2 There are a few instances in C where newlines are important: eg. they are illegal within strings (as already mentioned); they are also
);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:
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 .
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.
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)
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.
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;
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 */
exit(0);
}
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!
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,
Which prints
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;
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:
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):
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.
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.
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.
4 0.00451
-------------------
/* Code fragment to use to input data of the above format */
int x;
double 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.
#include <stdio.h>
#include <stdlib.h>
int
main()
{
int i;
float f;
double d;
char strng[21];
exit(0);
}
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.
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).
int p,q;
double d;
p=1; q=2;
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).
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!
int p,q;
p = 8;
+= -= *= /= %=
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.
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.
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:
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;
}
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”.
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:
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;
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)!
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:
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 */
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;
Notice that we can count backwards in a for loop because, as mentioned above, the update expression doesn’t have
to be an increment.
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.
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.
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 */
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]
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.
int p, q;
p=2; q=4;
/*....Another example....*/
#include <math.h>
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.
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]
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.
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];
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.
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:
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:
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);
}
int main()
{
int i;
double a, 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.
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.
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*/
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)
fclose(file pointer)
#include<stdlib.h>
#include<stdio.h>
int main()
{
FILE *fin, *fout; /* Declare file pointers*/
double x, y; /* Other variables */
fclose(fin);
fclose(fout);
exit(0);
}
#include<stdlib.h>
#include<stdio.h>
int main()
{
FILE *fout;
int i;
fclose(fout);
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.
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);
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.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int
main()
{
FILE *fout;
int i;
double s,d;
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.
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;
x 0 1 2 3 4
y
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):
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!):
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;
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:
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.
"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>
/* 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];
/* 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);
exit(0);
}
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;
exit(0);
}
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.
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");
}
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.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().
#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.
#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
48 Chapter 4. Further C
i = i + 1;
return;
}
int main()
{
counter();
counter();
counter();
exit(0);
}
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’.
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 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 */
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;
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;
exit(0);
}
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
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") ;
gnuplot_resetplot(g1) ;
gnuplot_resetplot(g1) ;
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
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) ;
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
A useful function is gnuplot cmd which executes a gnuplot command. The function prototype is
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
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
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!
#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
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.
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