Qualifiers | Type Hierarchy |
|
const | volatile |
The keyword const can used to qulaify a variable declaration. It is used to tell the compiler that a variable's value cannot be changed after its original initialization.
const float pi=3.14;
The variable may be a pointer in which case the value that cannot be changed is an address (but the thing it points to can still be changed), e.g.
int z; const int *zp; ... *zp=5;
The keyword volatile is use to to qualify a declaration to mean that the value of that variable may change at any time (e.g. by the hardware) so the compiler cannot count on it being constant between reads without a write. This tells it that no optimizations are possible.
The classification of the different data types can be viewed in a hierarchical way, as illustrated by the diagram which follows. This is one way (among others) to organize the data types.
/-----------------------------------------------------> void | +-----+ | +--------+ +----------+ +----------+ |Data |-+-->| Simple |-+-->|Elementary|--+-->| Whole |--+--> char |Types| | +--------+ | +----------+ | | Numbers | | +-----+ | | | +----------+ \--> int | | | | | | +----------+ | | +----------+ \-->| Floating |--+--> float | \-->| Pointers | | Numbers | | | +----------+ +----------+ \--> double | | +--------+ +----------+ \-->|Compound|-+-->|Same Type |-----------------------> array[] +--------+ | +----------+ | +----------+ | +----------+ /-->| Spatial |-----> struct | |Different | | +----------+ \-->| Types |--+ +----------+ | +----------+ \-->| Temporal |-----> union +----------+
There is one special data type called "void", which will be discussed below. The other data types can be split into the categories of:
Simple and Compound
Simple data, is data which consists of a single indivisible data item. Such kinds of data entities are usually the direct object of the instructions of the computer.
Compound data, is a data entity which consists of, or can consist of, more than one data item - each of which itself can be simple or compound. The data items comprising a compound data type can be of either the same type or of different types. When the type is the same it is called an 'array'.
When the type of the constituting data items are different they can be spatially different (i.e. have fields of different types) and hence simultaneously present, or the type can change over time as the program executes. The first is called a structure, and uses the keyword "struct", while the latter is called a "union". These will be discussed last.
Simple data types can be either:
Elementary or Pointer
An elementary data type is a single data item which is directly used by the operators in the 'C' language. The computer will have instructions which directly manipulate operands of all or most of the elementary types.
An elementary data item's value is used by referring directly to it by its name. The name of an elementary data item, when appearing in an expression, implies its value (called an 'rvalue') when it appears on the right side of an assignment operator and implies its memory address (called an 'lvalue') to write its new value when the name appears on the left side of an assignment operator. For example:
x=y+2;
causes the value of "x" to be replaced by 2 more than the value of "y".
A Pointer is a data item whose value is an address. This address can point to any of the possible data types - including another pointer. Every variable has an address, i.e. a location in memory.
There are two classes of the elementary data types. The integer class and the floating class of numbers.
Integers are variables which can only take on values which are whole numbers, such as: 99, 0, or -42. The integers come in different sizes, each considered to be a separate type. This size, in memory bytes, determines the maximum and minimum number that can be represented by that type. An integer whose size is 2 bytes, i.e. 16 bits, has a range of values from: -32768 to +32767.
Floating variables can take on values which include or can lie in between the whole numbers, such as: 0.5, -3.1459, 67.00000015, -.299E-45. These floating numbers are only approximations to the mathematical Real Numbers because of the limited dynamic range of values and the limited precision in terms of the number of the significant digits.
By the recursive use of:
it is possible to build any possible data structure of any complexity. One can have: arrays of arrays, pointers to structures, structures with fields that are arrays, arrays of pointers to unions, arrays of pointers to functions returning pointers to double, arrays of structures, etc.
As a slightly advanced subject on data types, there is the use of the two keywords: "const" and "volatile" which are both type modifiers. That is they may appear as part of a declaration for elementary or compound data types. Note that is possible for both of these modifiers to be used in the same declaration. When they are applied to a compound data type they apply to all of its parts, elements or fields.
Normally the order of appearance of type specifiers and their modifiers does not matter. The one exception is for pointers. Here, these two keywords have different meaning in two different positions. These two positions are before and after the asterisk, '*'. They may in fact appear in one or both of these two positions. When they appear before the asterisk, they apply to the value of the pointer. When they appear after the asterisk they apply to the value of the thing being pointed to.
The "const" type specifier tells the compiler that the data object it is applied to in the declaration cannot be modified by the program. The compiler will enforce this, to the best of its ability. Here is an example:
const int x=7;
In the above example, the use of the "const" keyword tells the compiler that the value of "x" is not allowed to be changed by the program. If the variable "x" is used as an 'lvalue', i.e. it appears to the left of an equal sign, the compiler will give a fatal error message to that effect.
This is different than using the preprocessor statement:
#define x 7
Because in this case "x" is not a "variable" at all, i.e. it does not have a location nor a size. "x" is a manifest constant which is symbolically replaced by 7 wherever it appears. See the appropriate lessons on the preprocessor for more details.
In the following three examples:
char z; char const * p1=&z; char * const p2=&z; char const * const * p3=&z;
all three pointer variables "p1", "p2" and "p3" have been initialized to point to the variable "z". The three pointers have a difference in the placement of the "const" modifier. Variable "p1" cannot be modified to point to another variable, but the value of what it points to can be changed. Variable "p2" can be changed to point to another "char" but the value of what it points to cannot be changed. For variable "p3" neither what it points to or the value pointed to can be changed. Be sure that you are clear about this difference.
The meaning of the "volatile" type specifier is that the hardware may modify the data object in some asynchronous way independent of the execution of the program. This may be via a hardware interrupt or via a totally parallel path independent of the central processing unit (CPU).
The "volatile" keyword has no semantic effect. It is only accepted syntactically. But it does have a pragmatic effect in that it may restrict possible optimizations on variables which have been so marked.
Note that a declaration consisting of only the keywords "const" or "volatile", or the two together, alone implies type "int".