The four basic types available to the C programmer are:
Each data type occupies a number of bytes in memory, dependent on the type of machine. In C, variables are allocated sufficient memory to hold the type the programmer specifies when they are declared. Declaration can happen at the beginning of a file, or in a compound statement before any executable code.
It is up to the programmer to choose the correct type appropriate to the problem in hand. With modern computers, memory is not usually a limiting factor nor in most cases is the speed of the calculations crucial. The compiler is built with the concept of a "natural size" of variable appropriate to the machine: for a most modern machines, integers ("int") occupy 32 bits or 4 bytes, and floating point numbers ("double") occupy 64 bits or 8 bytes.
There are further distinctions between signed and unsigned, and short and long data types which are included for completeness. One should be aware of these in case it is necessary to represent very large values, or to reduce memory usage at the expense of execution efficiency, but they will not be necessary for the implementation of the minefield game. The keywords which change the size or signedness of a type are referred to as qualifiers.
Declaring a variable of type char indicates that it can store one ASCII character.
| Type | Storage | Range of Values |
|---|---|---|
| char | 1 byte | -128 to 127 |
| signed char | 1 byte | -128 to 127 |
| unsigned char | 1 byte | 0 to 255 |
ASCII codes 32-127 include the printable character set; codes 0-31 provide control characters such as tab and line feed; and codes above 128, although not defined in the ASCII standard, provide access to non-printable characters.

A simple int is the most common numerical declaration. It indicates that integers (whole numbers) will be stored in the variable. The size of an integer is not defined in the C standard: it is guaranteed to be so shorter than a short int, and no longer than a long int! On most modern computers, the length of an integer is the same as a long integer, namely 4 bytes.
| Type | Storage | Range of Values |
|---|---|---|
| long signed long int signed int |
4 bytes | -2147483648 to 2147483647 |
| unsigned long unsigned int |
4 bytes | 0 to 4294967295 |
| short signed short |
2 bytes | -32768 to 32767 |
| unsigned short | 2 bytes | 0 to 65535 |
The default type for storing floating point data is double: "double precision floating point". The C standard says that all calculations done on floating point values will be done in double precision, so using the single precision float type is ill-advised unless memory space is at an absolute premium because it will actually involve an additional type conversion and actually slow the program down.
Double precision floating point is accurate to roughly 15 digits and single precision to roughly six. An additional type, long double, is available, with even higher precision.
| Type | Storage | Range of Values |
|---|---|---|
| float | 4 bytes | 3.4E ±38, c. 6 sig figs |
| double | 8 bytes | 1.7E ±308 c. 15 sig figs |
| long double | 10 bytes | 1.2E ±4932 c. 19 sig figs. |
The keyword void has three uses:

Type void expressions are evaluated for side effects. You cannot use the (non-existent) value of an expression that has type void in any way, nor can you convert a void expression (by implicit or explicit conversion) to any type except void. If you do use an expression of any other type in a context where a void expression is required, its value is discarded. To conform to the ANSI specification, void ** cannot be used as int ** . Only void * can be used as a pointer to an unspecified type.
Any of these types can be prefixed by the modifer const, which "fixes" the value of a variable and makes it illegal for C code to modify it. For example, one could declare const int one = 1; and it would be illegal for the program to change it later with the code one = 2.
One might be tempted to use a constant whereever the C preprocessor's #define directive was used previously. However, they work in very different ways. If the same variable was declared at the top of two different source files making up the same program, this would be an error even if they were the same value because the compiler wouldn't know which one was "the right one" when the object files were linked together. Preprocessor macros on the other hand (e.g. #define one 1) work by simply substituting "1" every time "one" appears in the source code. Use the one appropriate for the circumstances.
The modifer appears just before the type declarator when a variable is declared.

It is possible to cause the compiler to assoiciate values with words to form a type, referred to as an enumerated type. For example, consider the international colour code used to label resistors, where a particular colour represents a particular component value.
The compiler normally allocates 1 to the first word, 2 to the second and so on, but black indicates a zero in resistor colour codes so this type overrides the default behavior. It is possible to allocate numbers for each of the words arbitrarily, so long as the same value is not given to more than one different word. The compiler will silently ignore such an inconsistency, choosing a number of its own instead.
It is important to realise that using an enumerated type does not tell the computer how to print out a value of that type. If asked to print the value of r after executing the above code fragment, the computer will simply print out 2, not red.




In fact, it is a bad idea to declare variables as register when using a modern compiler like gcc. The compiler is suprisingly intelligent, and makes use of the on-chip registers for storing intermediate results in a way which is designed to speed up execution. It is actually far better than most humans at deciding how the microprocessor's resources should be used. By hinting that a register is used to store a particular variable, the code might actually run slower because the compiler now has fewer on-chip registers available for other uses. If storing a variable in a register would have speeded up execution, the chances are the compiler would have done that anyway without the hint. register is therefore best thought of as a throw-back to the days of inefficient C compilers, which has been left in for the sake of comatibililty and the ability to compile old C programs without having to modify them.
Because register variables aren't stored in memory, you can't take their address with the & operator.
Don't bother with register variables in your programs: it's just more to type when entering the code, and rarely results in any improvement in performance.
Converting between types in C is automatic, so if x is declared as a double, writing an assignment like x = 2 is fine because 2 is implicitly cast from an integer to a double precision floating point number before it is assigned. Occasionally, it is useful to force the issue, and instruct the compiler to consider a value as a particular type. This is achieved as a type cast, by preceding the term to be cast with the target type enclosed in (parentheses).
The simple types of the C language permit the storage and manipulation of floating point and integer numbers. In addition to the default format for such data, qualifiers permit storage at greater or lesser precision, or as signed or unsigned values. In general, qualifers should be omitted, and the compiler will "do the right thing" according to the machine for which the code is being produced.
Any variable can be declared const. Once initialiased, such a variable can then no longer be changed.
The void type is a special valueless type which can be used to declare, for example, functions which return and/or accept no values. A pointer-to-void can be used to pass the address of a variable of any type.
The tools upon which this course relies are Copyright the Free Software Foundataion where they are made available under the GPL (GNU Public Licence).
The content of this course was derived from that generated by many ex-colleagues at the University of Leeds, Department of Electronics and Electrical Engineering. Much of the content has been reworked, and substantially augmented, but Dr N J Bailey, Centre for Music Technology, The University of Glagsow. This manifestation is Copyright N J Bailey; some of the content is Copyright The University of Leeds.
Diagrams on this resource are drawn in XFig and are rendered by the browser using The University of Hamburg's Simple FIG viewer applet which is Copyright (C) 1996-2002 F.N.Hendrich, hendrich@informatik.uni-hamburg.de.
The source code, programming examples and exercises are all specific to this course, and are Copyright, Dr N J Bailey.
The applet for viewing and demonstrating C programs is Copyright Dr N J Bailey, and is to be found documented and with its source code on the Centre for Music Technology website under Software