Department of Engineering

IT Services

Keywords, Operators and Declarations


You can't use the following reserved words for variable names, etc.

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

A few of these haven't yet been described.

:- This is the default Storage Class for variables so it's not explicitly used. static, which you've already met, is an alternative class.

:- If a variable isn't meant to change you can define it as const. E.g., If you create an integer using `const int i = 6;' then a later `i = 7;' will be illegal. However, if you create a pointer to i and use this to change the value, you'll probably get away with it. The main purpose of const is to help optimisers. volatile is the opposite of const.

:- C has enumerated types, like pascal. E.g.
enum color {Red, Green, Blue};
They're not as useful as in pascal because C doesn't check if you set an enumerated type to a valid value.

:- You can suggest to the compiler that a variable should be kept in a register for faster access. E.g. `register int i' might help if i is a much-used indexing variable. An optimising compiler should use registers efficienty anyway. Note that you can't use the `&' operator on a register variable.


At last, here is a table of operators and precedence.

The lines of the table are in order of precedence, so `a * b + 6' is interpreted as `(a * b) + 6'. When in doubt put brackets in!

The Associativity column shows how the operators group. E.g. `' groups left to right, meaning that is equivalent to rather than . Both are pretty useless expressions.

Associativity Operator
left to right () [], ->, .
right to left ! (negation), ~ (bit-not)
++, --, - (unary) , * (unary), & (unary), sizeof
right to left cast (type)
left to right *, /, % (modulus)
left to right - +
left to right <<, >>
left to right <, <=, >, >=
left to right ==, !=
left to right & (bit-and), | (bit-or)
left to right ^ (bit-xor)
left to right && (logical and)
left to right || (logical or)
right to left ?:
right to left =, +=, -=, /=, %=, >>=, &=
left to right ,

Bit operations

C can be used to operate on bits. This is useful for low-level programming though the operations are also used when writing graphics applications.

Setting a bit :-
Suppose you wanted to set bit 6 of i (a long, say) to 1. First you need to create a mask that has a 1 in the 6th bit and 0 elsewhere by doing `1L<<6' which shifts all the bits of the long 1 left 6 bits. Then you need to do a bit-wise OR using `i = i | (1L<<6)'.

Unsetting a bit :-
Suppose you wanted to set bit 6 of i (a long, say) to 0. First you need to create a mask that has a 0 in the 6th bit and 1 elsewhere by doing `1L<<6' then inverting the bits using the ~ operator. Then you need to do a bit-wise AND using the & operator. The whole operation is `i =i & ~(1<<6)' which can be contracted to `i &= ~(1<<6)'.

Creating a mask for an call :-
In graphics, masks are often created each of whose bits represent a option that is to be selected in some way. Each bit can be referred to using an alias that has been set up in an include file. E.g. a mask which could be used in a call to make a window sensitive to key presses and buttonpresses could be set up by doing
unsigned int mask = KeyPressMask | ButtonPressMask;


First, a note on terminology. A variable is defined when it is created, and space is made for it. A variable is declared when it already exists but needs to be re-described to the compiler (perhaps because it was defined in another source file). Think of declaring in C like declaring at customs - admitting to the existence of something.

C declarations are not easy to read. Any good book on C should explain how to read complicated C declarations ``inside out'' to understand them, starting at the variable name and working outwards back to the base type. You shouldn't need to use complicated declarations so don't worry too much if you can't `decode' them. Keep a cribsheet of useful typedefs and play with cdecl (see section 16.1).

ANSI C introduced the use of the `void' keyword in various contexts.

  • `routine(void)' - the routine takes no arguments.
  • `void routine (int i)' - the routine returns no value.
  • `void *ptr' - ptr is a generic pointer which should be cast into a specific form before use.

The following examples show common declarations.

int *p pointer to an int
int x[10] an array of 10 ints
int (*x)[10] a pointer to an array of 10 ints
int *x[10] array of 10 pointers to ints
int (*f)(int) pointer to a function taking and returning an int
void (*f)(void)    pointer to a function taking no args and returning nothing
int (*f[])(int) An array of pointers to a functions taking and returning an int

Note the importance of the brackets in these declarations. If a declaration gets too complex it should be broken down. For example, the last example could be rewritten as

typedef int (*PFI)(int) /* declare PFI as pointer to function that 
                           takes and returns an int.*/
PFI f[];