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.
const int MAX_USERS = 100;
// MAX_USERS = 200; // COMPILE ERROR: assignment of read-only variableconst 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).
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 changevolatile: 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.
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
}