Home / Articles / Type Qualifiers
Type Qualifiers
Type Qualifiers
In C (and C++) variables and objects can be defined with type qualifiers that indicate special properties of the data. There are three type qualifiers:
constvolatilerestrict
const
The const keyword tells the compiler that the data contained by a variable or object cannot change. Once a constant variable has been initialised, its value cannot be modified.
Constant Variables
A variable of basic data type, structure, or enum can be declared as constant:
const int code = 203;
Any attempt to change the value after initialisation produces a compilation error:
#include <stdio.h>
int main()
{
const int code = 203;
code = 101; /* Error */
return 0;
}
$ gcc const.c
const.c: In function 'main':
const.c:5: error: assignment of read-only variable 'code'
Both of the following declarations are equivalent:
const int code = 203;
int const code = 203; /* Same as above */
Constant Pointers
Pointers can be declared as constant in three different ways:
1. Constant pointer, mutable data — the pointer cannot be redirected, but the data it points to can be modified. Specify const before the pointer name:
char data1[] = "Hello World";
char data2[] = "BloodAxe";
char * const ptr = (char *)data1;
ptr[0] = 'A'; /* Ok */
ptr = (char *)data2; /* Error: assignment of read-only variable 'ptr' */
2. Mutable pointer, constant data — the pointer can be redirected, but the data it points to cannot be changed. Specify const before the data type:
char data1[] = "Hello World";
char data2[] = "BloodAxe";
const char * ptr = (char *)data1;
ptr = (char *)data2; /* Ok */
ptr[0] = 'A'; /* Error: assignment of read-only location */
3. Both constant — neither the pointer nor the data can be changed. Specify const twice:
const char * const ptr = (char *)data1;
ptr = (char *)data2; /* Error */
ptr[0] = 'A'; /* Error */
Constant Objects (C++)
In C++ a class object can be declared as constant. A const object cannot modify its member data, and cannot call any member function that modifies member data:
#include <iostream>
class myClass
{
public:
int a;
myClass() { a = 20; }
void set(int value) { a = value; }
};
int main()
{
const myClass m;
m.a = 10; /* Error */
m.set(20); /* Error */
return 0;
}
const.cc: In function 'int main()':
const.cc:18: error: assignment of data-member 'myClass::a' in read-only structure
const.cc:19: error: passing 'const myClass' as 'this' argument of 'void myClass::set(int)' discards qualifiers
Initialising Constant Members in C++
If a const member is declared inside a class, it must be assigned a value in the constructor's initialisation list:
class myClass
{
private:
const int a;
public:
myClass() : a(1) { }
myClass(int value) : a(value) { }
};
volatile
The volatile qualifier tells the compiler that the value of a variable may be modified by something outside the visible program flow (such as hardware, an OS signal handler, or a concurrent thread). It instructs the compiler not to apply optimisations that assume the value is unchanged between accesses.
A volatile variable is declared by placing volatile before the type:
volatile int code;
There are three main scenarios where volatile is needed:
Memory-Mapped I/O
A pointer can be directed at a hardware device register. Writing to that address sends data to the device. Without volatile, the compiler may optimise away intermediate writes that it considers redundant:
char far *ptr = (char far *) 0xB0008000L; /* Points to video card buffer */
ptr[0] = 'a';
ptr[0] = 'b';
ptr[0] = 'c';
The compiler might only generate code for the last assignment, discarding the first two. Declaring ptr as volatile forces all three writes to be emitted.
setjmp and longjmp
When setjmp saves program context, it saves register contents. If the compiler has stored a local variable in a register and longjmp restores that register, the variable's value will also be restored to its pre-setjmp value — which may not be what the programmer intended.
Example: without volatile, a local variable modified between setjmp and longjmp may revert to its old value after the jump when optimisation is enabled:
$ gcc jmp.c -O3 -o jmp
$ ./jmp
Before jump: local_var:10
After jump: local_var:10 /* Unexpected: should be 20 */
Declaring the variable volatile forces it to be stored in memory rather than a register, so it retains its modified value after the jump.
Signal Handlers
A signal handler may modify a global variable that the main program is testing in a loop. Without volatile, the compiler may optimise away the loop condition check:
volatile int transmit_data_status = CONTINUE_DATA_TRANSFER;
void my_signal_handler(int sig)
{
transmit_data_status = STOP_DATA_TRANSFER;
}
void send_loop()
{
while (CONTINUE_DATA_TRANSFER == transmit_data_status)
{
send_data();
sleep(1);
}
}
Without volatile, the compiler might conclude that transmit_data_status never changes inside the loop and convert the condition to an infinite loop.
restrict
The restrict qualifier is used with pointers. Declaring a pointer as restrict is a promise to the compiler that the memory it points to will not be accessed through any other pointer. This allows the compiler to generate more efficient code.
Example without restrict — the compiler must reload *offset after each assignment because destination1 or destination2 might alias offset:
int add(int *destination1, int *destination2, int *offset)
{
(*destination1) += (*offset);
(*destination2) += (*offset); /* Compiler must re-load *offset */
}
With restrict, the compiler knows offset cannot overlap with destination1 or destination2, so *offset only needs to be loaded once:
int add(int *destination1, int *destination2, int * restrict offset)
{
(*destination1) += (*offset);
(*destination2) += (*offset); /* Compiler can reuse the cached *offset */
}
Important: if a pointer is incorrectly declared as restrict but actually does alias another pointer, the resulting behaviour is undefined. The programmer is responsible for ensuring correctness.
restrict was introduced in C99. On older compilers, or when targeting earlier standards, you may need to enable C99 mode explicitly:
gcc -std=c99 restrict.c