Types can be qualified with one or more of the following: const
, volatile
,
and restrict
. Each of these qualifiers changes behaviors when accessing
objects of the qualified type. Leaving these out you're left with "unqualified
types". The qualified and unqualified versions of types can be used
interchangably as arguments to functions, return values from functions, and
members of unions.
const
Objects declared with the const
qualifier are not modifiable. In particular,
they're not assignable but can have constant initializers. This means objects
with the const-qualified types can be placed in "read-only" memory by the
compiler, and any attempt to write will result in a runtime error:
const int i = 1;
i = 2; // error: i is const-qualified
Both gcc and clang seems to pick this error up at build time!
C allows you to modify an object that is pointed to by a const
pointer by
casting the const
away, provided that the original object was not also
declared as const
:
int i = 12;
const int j = 12;
const int *ip = &i;
const int *jp = &j;
*(int *)ip = 42; // ok
*(int *)jp = 42; // undefined behavior
volatile
Objects of volatile-qualified types server a special purpose. Static volatile-qualified objects are used to model memory mapped input/output (I/O) ports, and static constant volatile-qualified objects model memory-mapped input ports such as a real-time clock.
The values stored in these objects may change without the knowledge of the compiler. For example, every time the value from a read-time clock is read, it may change, even if the value has not been written to by the C program. Using a volatile-qualified type lets the compoler know that the value may change, and ensures that every access to the real-time clock that occures (otherwise, and access could be optimized away or replaces by a previously read and cached value.
In this example the compiler must generate instructions to read the value from
port
and then write it back to port
:
volatile int port;
port = port;
Without the volatile
qualifification, the compiler would see this as a no-op
(operation that does nothing) and probably eliminate both the read and the
write.
restrict
A restrict-qualified pointer is used to promote optimization. Objects indirectly accessed through a pointer frequently cannot be fully optimized because of potential aliasing, which occurs when more than one pointer refers to the same object. Aliasing can inhibit optimizations, because the compiler can't tell if portions of an object can change values when another apparently unrelated object is modified. So, it's only a good idea to use restrict if you're absolutely sure there will not be more than one pointer accessing your object.