volatile and const
volatile tells the compiler that a value can change outside the code it sees, so every read and write must really happen: registers and flags set by interrupts need it. It gives no atomicity. const makes data read-only through that name, and on an MCU constant tables usually stay in Flash.
Why volatile exists
The optimiser assumes that only the code it can see changes a variable. In while (!tick_flag) { } nothing in the loop changes tick_flag, so it may read the flag once and loop forever, even after an interrupt sets it. Declaring the flag volatile forces a real read on every pass. The same applies to hardware registers, whose values change on their own and where each read or write can have an effect, such as clearing a status bit.
#include <stdint.h>#include <stdio.h>static const uint8_t table[4] = {0x00, 0x07, 0x0E, 0x09}; /* read-only data */static volatile int tick_flag; /* on a real MCU, set by an interrupt */static void fake_isr(void) { tick_flag = 1;}int main(void) { const char *msg = "ok"; /* pointer to const: the text can't change */ char buf[] = "hi"; char *const fixed = buf; /* const pointer: always points at buf */ fixed[0] = 'H'; fake_isr(); while (!tick_flag) { /* volatile: tick_flag is read again on every pass */ } tick_flag = 0; printf("%s %s table[2]=0x%02X\n", msg, fixed, (unsigned)table[2]); return 0;}
ok Hi table[2]=0x0E
const and where data lives
const means the program won't change the data through that name. Read declarations from the name outward: const char *msg is a pointer to constant characters, so the text can't change but msg can point elsewhere. char *const fixed is a constant pointer: it always points at buf, but the characters can change. A read-only status register is volatile const: the program never writes it, but it changes anyway.
On an MCU the linker usually places const objects with static storage, such as table above, in Flash, where they cost no RAM. Without const, the same table is copied from Flash into RAM at startup.