thecodex.expert · The Codex Family of Knowledge
C

const and volatile

const int *p and int * const p look almost identical but mean completely different things — one protects the pointed-to value, the other protects the pointer itself.

C17 / C23 const placement matters Last verified:
Canonical Definition

const declares that a value should not be modified through this particular reference to it, checked and enforced at compile time — attempting to assign through a const-qualified path is a compiler error. With pointers, const's exact placement determines whether it's the pointed-to value or the pointer itself that's protected, and the two combine independently. volatile is a different, less commonly needed qualifier: it tells the compiler a value might change due to something outside the program's own control flow (memory-mapped hardware, a signal handler, another thread), disabling optimizations that would otherwise assume the value can't change between two reads with no visible write in between.

A basic const variable

Attempting to assign to a const variable after initialization is a compile error, caught immediately rather than surfacing as a confusing bug later.

Cbasic_const.c
const int MAX_USERS = 100;
// MAX_USERS = 200;   // COMPILE ERROR: assignment of read-only variable

const with pointers: placement changes the meaning

Read right-to-left from the variable name: const int *p means "p points to a const int" (the VALUE is protected, p can be reassigned); int * const p means "p is a const pointer to int" (p itself can't be reassigned, but the value it points to can change).

Cconst_pointers.c
int x = 5, y = 10;

const int *p1 = &x;   // pointer to a const int — the VALUE is protected
// *p1 = 20;            // COMPILE ERROR
p1 = &y;                 // fine — p1 itself CAN be reassigned

int * const p2 = &x;   // const pointer to an int — the POINTER is protected
*p2 = 20;                 // fine — the value CAN change
// p2 = &y;              // COMPILE ERROR: p2 itself can't be reassigned

const int * const p3 = &x;   // BOTH protected — neither the value nor p3 can change

volatile: telling the compiler not to assume

Without volatile, the compiler might optimize away the second read entirely, assuming the value can't have changed since nothing in the visible code wrote to it — a real, historically significant bug class in embedded and hardware-interfacing code.

Cvolatile_example.c
volatile int *statusRegister = (int *)0x1000;   // a hardware register address

while (*statusRegister == 0) {
    // without volatile, the compiler might read this ONCE and cache it,
    // assuming it can't change — creating an infinite loop even if the
    // actual hardware register's value changes
}

Sources

1
ISO/IEC 9899 (C Standard), "Type qualifiers," cppreference.com/w/c/language/const and cppreference.com/w/c/language/volatile.