Pointers Part 4: const, volatile, restrict
This chapter covers the three type qualifiers as they interact with pointers: const, volatile, and restrict. volatile is one of the most misunderstood features in C, so it receives special attention.
Why This Matters
Qualifiers change what the compiler may assume. const documents and enforces read-only intent. volatile tells the compiler a value can change outside its control (hardware, signal handlers). restrict enables optimization by promising no aliasing. Misusing any of them produces either bugs or missed optimizations.
Prerequisites
c.core.24— pointer basics through pointer-to-pointer.
Core Concept
const and pointers
There are four distinct combinations:
| Declaration | Meaning |
|---|---|
const int *p | pointer to const int (the *int* is read-only) |
int const *p | same as above |
int *const p | const pointer to int (the *pointer* is read-only) |
const int *const p | const pointer to const int |
Read right-to-left: const int *p is "p is a pointer to a const int."
int x = 5;
const int *p = &x; /* ok: can't write through p */
/* *p = 10; ERROR */
p = &y; /* ok: the pointer itself is modifiable */
volatile
volatile tells the compiler that every access to the object must actually happen, in the order written, and must not be optimized away or cached in a register. It is for values that can change or be observed outside the compiler's model: memory-mapped hardware registers, variables shared with a signal handler, or memory modified by other threads (though it is *not* a synchronization primitive for threads — see below).
volatile int status; /* every read/write is a real memory access */
restrict (C99)
restrict is a promise by the programmer that, for the lifetime of the pointer, no other pointer will be used to access the object it points to (with some precise exceptions). It enables the compiler to optimize aggressively because it can assume no aliasing.
void add_arrays(int *restrict dst, const int *restrict a,
const int *restrict b, size_t n);
The compiler may assume dst, a, and b do not overlap, enabling vectorization and reordering.
Syntax
const int *p;
int *const p;
const int *const p;
volatile int *vp;
int *volatile pv;
int *restrict rp;
Examples
const pointer vs. pointer to const
int a = 1, b = 2;
const int *p = &a; /* pointer to const: can rebind, can't write */
p = &b; /* ok */
/* *p = 5; */ /* error */
int *const q = &a; /* const pointer: can write, can't rebind */
*q = 5; /* ok */
/* q = &b; */ /* error */
volatile for a hardware register
#define STATUS_REG ((volatile unsigned *)0x40000000u)
unsigned read_status(void)
{
return *STATUS_REG; /* always performs a real read */
}
(For a real embedded system, the address and width are platform-specific; this is illustrative of the *pattern*.)
restrict for non-overlapping arrays
#include <string.h>
void scale(int *restrict out, const int *restrict in, int factor, size_t n)
{
for (size_t i = 0; i < n; i++)
out[i] = in[i] * factor;
}
The restrict promises out and in do not overlap, so the compiler can vectorize the loop.
How It Works
constis mostly a compile-time check: the compiler rejects writes through a
const-qualified lvalue. (A const object may still be modified through a non-const alias, which is why const is not a guarantee of immutability.)
volatiledisables certain optimizations on accesses to the object.restrictis a contract between programmer and compiler enabling alias
analysis.
Variations
volatile does NOT mean atomic
volatile does not make a variable atomic, does not prevent data races, and does not provide memory ordering for multithreaded code. Use _Atomic and atomics (c.conc.5) for that. VERIFIED
volatile for signal handlers
A variable shared between a signal handler and normal code should be sig_atomic_t (from <signal.h>) and volatile, for the specific case of flag-like communication. c.stdlib.9 covers this.
Common Mistakes
- Thinking
constmakes the underlying object immutable (it only makes that
lvalue read-only).
- Using
volatilefor thread synchronization. - Misreading
int *const pas "pointer to const" when it is "const pointer." - Violating the
restrictpromise (UB).
Undefined Behavior
- Writing to a
constobject through a non-const alias is UB.VERIFIED - Violating the
restrictcontract is UB.VERIFIED - Casting away
constand then modifying a genuinely const object is UB.
Portability
constandvolatileare standard and portable.restrictis C99 and later (and C23); it is not available in C89.- The exact semantics of
volatileare standard, but *what* the compiler
actually does with it is an implementation detail.
Under the Hood
const is usually erased in generated code (it is a compile-time property). volatile forces a load or store instruction with no caching in a register. restrict informs alias analysis, which feeds optimization passes like vectorization and instruction scheduling.
Practical Usage
- Use
conston function parameters that should not be modified, and on
pointers to read-only data.
- Use
volatilefor MMIO registers and (withsig_atomic_t) signal flags. - Use
restricton performance-critical functions where you can guarantee
non-overlap.
Exercises
1. Write each of the four const/pointer combinations and attempt both rebind and write; observe the compiler errors. 2. Write a program with a volatile variable and compile with and without optimization; inspect the generated assembly to see the difference. 3. Write a restrict-annotated function and a non-annotated version, and compare generated code or benchmark the difference. 4. Explain why volatile is not a substitute for atomics.
Deep Challenge
Write a small memory-mapped register abstraction using volatile that reads and writes a device register, and explain exactly what could go wrong if the volatile were omitted when the compiler optimizes. Then discuss the additional measures needed if the register is also accessed from an interrupt handler.
Related Concepts
c.emb.2— MMIO and volatile in embedded.c.conc.5— atomics and the memory model.c.stdlib.9— sig_atomic_t and signal handlers.c.obj.aliasing— aliasing and restrict.
References
- ISO/IEC 9899:2018 §6.7.3 (type qualifiers), §6.7.4 (restrict), §6.2.5.
Verification
- Four const/pointer combinations are standard.
VERIFIED - volatile does not guarantee atomicity or ordering.
VERIFIED - restrict violation is UB.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] const pointer vs. pointer to const
- [ ] const pointer to const
- [ ] volatile semantics
- [ ] volatile is not atomic
- [ ] restrict and aliasing
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.ptr.const | 0 | 6 |
| c.ptr.volatile | 0 | 6 |
| c.ptr.restrict | 0 | 5 |