Book

Recommendation

The recommended C book for this unit is the wikibooks one at wikibooks.org/wiki/C_Programming/

These slides provide a few general notes, then notes on some of the sections of that book - if these notes don't answer your questions, ask Ian

All of C?

Does this unit cover all of the C language?

Yes, except for some of the more obscure features (union, register, restrict, volatile) and its use in device drivers or embedded systems

Pro C: total control over efficiency, processor, devices at the lowest level, base language on all computers

Con C: undefined, unspecified, platform dependent and locale dependent behaviour

C Family

The unit doesn't cover C++ or C# etc. from the C family

C++ is C with no features removed, and many more features added to support object oriented programming, so it is too complex (and some things are harder than C!)

But it has some niche uses (gaming, crypto, ...) and if you stick to a well-chosen subset, C++ can be fine, but maybe it is better to jump straight to Java

C# is technically an open language but, in practice, has far too much of a Windows bias to be thought of as platform independent (except perhaps for Unity)

Universal

According to computer science theory, C is universal, i.e. capable of carrying out any computation

So there is nothing in principle you can do in C that you can't do in other languages, and vice versa

In practice, some things seem more convenient in one language than the other

You will meet other languages in the future, but C provides a universal jargon which you will be expected to understand

Index (part 1)

Index (part 2)

Why learn C? 1

This section of the book isn't very good: our reasons for teaching C (which is old) are:

Why learn C? 2

You may well find C useful for:

History

It is important to realise that every historical summary is inaccurate, misleading and biased

This section of the wiki book is no exception, and it particularly misses out what has happened to C since the earliest days, but it is a reasonable starting point

What you need...

The only compilers you should consider are clang and gcc, not Tiny C or Visual Studio which are far too non-standard for cross-platform work

Our recommendation for a C programming environment for Windows is Cygwin or Bash (see aside: computers)

The section mentions IDEs, but says not to use them unless confident - so don't use them (unless confident)

Obtaining a compiler

The section recommends Dev-C++ for Windows

But that is an IDE, and IDEs should be avoided (unless you are confident)

And it suggests MinGW for Windows, but that is old and causes lots of problems; Cygwin or Bash is recommended

Intro exercise: 1

Many examples in the book have no program comment, which makes them rubbish programs

The compiler commands in the book do not include the option -std=c11, which you must use in this unit

In fact they don't specify a standard at all, which is a disaster for cross-platform programming

The essential option -Wall is barely mentioned, and other recommended options such as -pedantic or -g are not mentioned

Intro exercise: 2

The book uses the Allman indent style rather than the K&R indent style (see Wikipedia)

Either style, or any other, is OK, but set up your editor to make sure your indenting is consistent

The K&R style is used in this unit, partly because programs fit better on slides

In team programming, it is important for all members of a team to (a) follow company policy if any or (b) agree on the same style and (c) not argue about it

Preliminaries: 1

The book uses int main(void) - the void does no harm, but is arguably technically incorrect

It means that no calls to main should have any arguments, whereas main() means any number of arguments

But (a) main is called by the system with two arguments (b) there aren't going to be any calls to main from your own code (c) in general, calling functions with too many arguments isn't a common source of bugs (d) across modules, it is the declaration (i.e. signature, or prototype) where void matters, not the definition

Preliminaries: 2

The book uses end-of-line comments, which sets a bad example for real programs - never use them (they are usually a sign that your code isn't readable enough, and keeping them lined up and consistent with the code, is unnecessary maintenance effort)

There are also comments inside function definitions - you should hardly ever use them - comments should come before functions

A global variable is used (to illustrate scope) - global variables are banned in this unit

Basics of Compilation

These days, you hardly ever need to compile modules separately, so the details of how to do it can be ignored

Programming Structure/Style

The book shows some multi-line comments with decorative asterisks - this isn't recommended unless your editor maintains them neatly with no effort

For this unit, we recommend using the multi-line style for a program comment at the top of a program or module, and the single-line style (even if you need two or three lines of comment) in front of a function - that allows you to use a multi-line comment to comment out large sections of a program during debugging

Variables: 1

This section seems to recommend against newer standards because "some compilers still don't support them", and "it is uncommon so may irritate other programmers"

But using no standard is stupid for cross-platform programs, the newer standards have been around a long time now, C11 is technically the only C standard, and C11 is supported by decent compilers. It is time for all C programmers to use C11 (otherwise you will irritate forward-looking programmers).

Variables: 2

Variable names should be as readable as possible, and also as compact as possible as long as that doesn't make them less readable - many bugs are caused by poor or confusing choices of variable name

Short, unabbreviated, single words are often best - one-letter names can be OK for short-lived generic variables

For multi-word variable names, there is the underscore style (some_number) and the camel case style (someNumber) - use whichever you like

Variables: 3

The const keyword is mentioned - use it for constants

The #define mechanism is mentioned - don't use it, because it is a death trap

Don't use auto - it is never necessary

Don't use register - let the compiler optimise

Don't use volatile - except in a device driver

The static and extern keywords are not needed until we talk about programs with separate modules

Variables: 4

Why is #define dangerous?

#define plus1(n) n+1

With this definition, 2*plus1(3) becomes 2*3+1 which is wrong

So, put brackets round the entire replacement text:

#define plus1(n) (n+1)

Variables: 5

Why is #define still dangerous?

#define twice(n) (2*n)

With this definition, twice(3+4) becomes (2*3+4) which is wrong

So, put brackets round each argument appearance:

#define twice(n) (2*(n))

Variables: 6

Why is #define still dangerous?

#define max(m,n) ((m)>(n)?(m):(n))

With this definition, max(2,in()) becomes (2>in() ? 2 : in()) which is wrong if in is some function which reads input

So, avoid arguments with side effects or, better, just don't use #define at all (use the inline keyword)

Variables: 7

Is there any downside to using const for constants?

Technically, const means "read-only" rather than "constant", so values could be read in, or changed indirectly via pointers

So you can't use const variables in switch statements (and the compiler isn't able to optimize code by knowing the values at compile time)

Variables: 8

Is there a 'nice' way of defining constants which allows their use in switch statements?

Yes, but only for integers and integer-compatible values

enum day { Mon, Tue, Wed, ... };
enum { width=100, newline='\n' };

When you define an enumerated type, it usually defines constants 0, 1, ... but you can give values to the constants, and the enumeration can be anonymous

Variables: 9

Is there a 'nice' way of defining functions with full compiler optimisation?

Yes, use the inline keyword

inline int max(int m, int n) {
    return m>n ? m : n;
}

Across modules, add static and put the whole definition in a header file or, better, use the -flto compiler option for cross-module optimization

Simple input and output: 1

The book has examples of printf without the final \n in the string

On some systems, that causes printing to be delayed until a \n is used in a later call, which means that the output may not appear when you expect it, which can make debugging exceptionally difficult

Always use a final \n except when you are deliberately building up a line of output in pieces

Simple input and output: 2

The section mentions printing out tabs using \b

This is extremely unreliable, being heavily dependent on the operating system and other environment issues

Any use of the actual tab character ('hard tab') is ambiguous and unreliable, so it is best to avoid them altogether, and use spaces instead

For example, you should set your editor to 'soft tabs'

Simple input and output: 3

You have to be very careful when using the scanf function, in fact it is best to avoid it

It was designed (a) to treat spaces and newlines the same (b) to be used non-interactively (c) without regard for security loopholes

If you want to play safe, or you care about the line structure of the input, or if you want your program to respond to each line typed in, use fgets to read a line in and then sscanf to extract the items from it

Simple input and output: 4

The section has an example of using scanf("%s")

There are at least three problems with this:

So use fgets/sscanf and avoid scanf

Simple input and output: 5

One example shown is:

printf("Please input an integer value: ");

The idea is to print out a prompt without a newline, and for the user to type their response on the same line

But on some systems, the text won't appear until the next printf, so the user won't see it in time

For safety, add fflush(stdout); after the printf

Operators and type casting: 1

This section contains a lot of grammar jargon

Also, it is like a reference manual, listing all the operators, regardless of whether you need them yet

Operators and type casting: 2

Precedence refers to the rules which the compiler uses to decide what order to do things in

For example, in 2+4*10 the multiplication is done first giving 42, not the addition which would give 80, because * has a higher precedence

If in any doubt, add brackets, e.g. write 2+(4*10) or (2+4)*10 to make it clear which you mean

Operators and type casting: 3

A cast in C is something like (int) pi which changes a value to a given type

Casts are very unsafe, because any basic type can be cast to any other, whether it makes sense or not, and information can easily be lost or mangled

It is usually best to regard using a cast as a failure to find a better and safer way of expressing what you want, and you should add a comment to explain

Operators and type casting: 4

The book suggests that the shifting operations can be very fast, e.g. n<<1 might be faster than n*2

But you shouldn't write n<<1 when you mean n*2

That's because (a) it is less readable and (b) the compiler can optimize better than you can and will automatically convert your n*2 into n<<1 only if it is genuinely better (just use the -O2 compiler option)

Operators and type casting: 5

The book says that C doesn't have true and false, and uses 1 and 0 instead

But it doesn't say that you can use #include <stdbool.h> which provides the type bool as a synonym for a suitable integer type, and true and false as synonyms for 1 and 0

Use these to make your programs more readable

Arrays and strings

This section seems reasonably accurate

Program flow control

This section fails to mention the stdbool.h header

It mentions goto - never use it

Otherwise, it is reasonably good

See the end of the statements chapter of the lecture notes on how to avoid uncontrolled jumps

Procedures and functions: 1

The early examples in this section have printfs without newlines again - don't do it

There are functions with no arguments defined with argument list (void) - as before, that's only necessary in declarations not definitions

Procedures and functions: 2

The book has some brief information about recursion

For most problems in the early parts of the unit, you can use either recursion or loops - feel free to use either

In branching problems, e.g. divide-and-conquer algorithms like quicksort or mergesort, or traversal of tree data structures, recursion comes into its own as the most readable, convenient and efficient approach

Procedures and functions: 3

The static keyword is mentioned again

It is not needed until we start on multiple modules

Just for the record, it makes sure that a function is local to the module it appears in, and isn't visible to the rest of the program

Procedures and functions: 4

C's standard library consists of a collection of functions, summarised in the book

To check the official full set of library headers and functions, see the C11 standard (chapter 7)

To find other documentation for a function, type C and its name into Google (a C++ version is often OK) or use the man command

Procedures and functions: 5

Variable length argument lists (varargs) are an advanced topic

printf is the main common example

You hardly ever need to define a varargs function for yourself

Standard Libraries

The standard library of support modules has already been covered, so this section just gives extra advanced or background information

It mentions the POSIX library, which you can use with care, but it is very badly described

Pointers and arrays

The section is reasonably accurate, but not very clearly explained

Memory management

The section is reasonably accurate, but not very clearly explained

Error handling 1

Error handling needs to be taken seriously if you want to produce robust production programs

The main thing is to read the conventions for each standard function you use, test its return value or whatever to see if there was an error, and do something sensible (e.g. call perror and exit)

Other issues in this section are advanced

Error handling 2

A useful technique that the book doesn't mention is wrapper functions:

FILE *fp = notNull(fopen(...));

The notNull function checks that the call returns a non-null result:

void *notNull(void *p) {
    if (p != NULL) return p;
    ... report error and exit ...
}

File I/O

Newlines are mentioned: see aside: characters

Redirection is mentioned: see aside: unix commands

The section is written like a reference manual - it doesn't highlight the simple things first

You will get a better impression of simple approaches from the lecture notes: chapter: input & output

Composite data types

The section is reasonably accurate, but not very clearly explained

String manipulation

Don't use "wide character" strings, use ordinary strings containing UTF-8

Further math: 1

This section doesn't make it very clear exactly what to do to use the maths library

Suppose you want to use the sqrt function

At the top of the program, put #include <math.h>

On some systems, you must compile with:

clang -std=c11 ... prog.c -lm

The -lm (include maths library) option must come after prog.c

Further math: 2

Most standard library modules are linked with your program automatically

Others may need a -l option, e.g. -lm for maths

The compiler deals with each program file or library on the compiling line in left-to-right order

When it reaches a library, it extracts from the library all the functions which have outstanding calls so far, so if you write clang ... -lm prog.c nothing will be included from the library

Libraries

It may be interesting to read this section, but it is advanced, and the modules chapter of the lecture notes will provide a simpler approach to some of the issues

Exercises

The exercises are worth working through, if you have time

The advanced sections after this are also worth browsing in, if and when you have time