volatile & restrict
volatile and restrict are two type qualifiers that tell the compiler something about how a variable or pointer will be used — but they point in opposite directions. volatile restrains optimization by warning the compiler a value can change unexpectedly, while restrict enables more aggressive optimization by promising the compiler something about pointer aliasing.volatile
Normally, the compiler assumes a variable's value can only change through visible code — if you read the same variable twice in a row with no assignment in between, it may optimize the second read away and reuse the first value (perhaps keeping it in a CPU register).
volatile tells the compiler that assumption does not hold: the value can change outside the normal flow of the program — from a memory-mapped hardware register, another thread, or a signal handler — so every single read and write must actually touch memory.C
// A busy-wait flag set by a signal handler or another thread.
#include <stdio.h>
volatile int stop_requested = 0;
void handle_signal(int sig) {
(void)sig;
stop_requested = 1; // may run "unexpectedly" between any two instructions
}
int main(void) {
// Without 'volatile', an optimizing compiler could assume
// stop_requested never changes inside this loop (since nothing in
// main() itself modifies it) and turn this into an infinite loop
// that never re-reads memory.
while (!stop_requested) {
// do work...
}
printf("Stopped.\n");
return 0;
}Without
volatile, the compiler is free to load stop_requested into a register once and never check memory again — because, as far as it can see, nothing in main() changes it. volatile forces a fresh memory read on every loop iteration, so the flag being set elsewhere (a signal handler, an interrupt, another thread) is actually observed.volatile is not a substitute for thread synchronization
volatile only prevents certain compiler optimizations — it says nothing about atomicity, memory ordering across CPU cores, or mutual exclusion. For safely sharing data between threads, use proper synchronization primitives (mutexes, atomics from <stdatomic.h>) rather than relying on volatile alone.restrict
Introduced in C99,
restrict is a promise you make to the compiler, not a safety check it performs for you. Applying restrict to a pointer parameter tells the compiler: "for the lifetime of this pointer, no other pointer will be used to access the same memory." Armed with that guarantee, the compiler can skip re-reading memory that might otherwise have been changed through an aliasing pointer, enabling much more aggressive optimization — particularly in tight numeric loops.C
// Without restrict, the compiler must assume 'a' and 'b' might overlap,
// and re-read *a on every iteration in case writing to b[i] changed it.
void add_arrays(int *a, int *b, int *result, int n) {
for (int i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
}
// With restrict, you promise a, b, and result never alias each other,
// letting the compiler keep values in registers and vectorize freely.
void add_arrays_fast(int * restrict a, int * restrict b,
int * restrict result, int n) {
for (int i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
}A broken restrict promise is undefined behavior
restrict is not checked or enforced by the compiler at all — it is purely an optimization hint that you assert is true. If you mark a pointer restrict and then actually pass overlapping or aliasing pointers into that function, the resulting program has undefined behavior. The compiler may produce incorrect results because it legitimately optimized based on a promise you broke.volatiletells the compiler a variable can change outside normal program flow, forcing every access to hit real memoryTypical uses for
volatile: memory-mapped hardware registers, flags set by signal handlers, variables shared with interrupt routinesvolatiledoes not provide atomicity or thread synchronization by itselfrestrict(C99) is a promise that a pointer does not alias with any other pointer used to access the same memoryViolating a
restrictpromise is undefined behavior -- the compiler trusts you and optimizes accordingly