Ground Embedded KO Subscribe

Notes / C Language

volatile, const, static

volatile blocks optimisation, const fixes a value, static changes scope and lifetime. The three keywords asked about most in embedded work.

GROUND EMBEDDED · NOTEC Language 10volatile, const, static 1volatile uint32_t *ODR 2 = (uint32_t*)0x40020014; 3*ODR |= (1u << 5); 4while (1) { 5 if (flag) toggle(); 6}

volatile

while (!rx_done)Compiler"never changes in the loop"Reads it oncewhile (1)never gets out
Without volatile, the compiler reads the value once and turns the loop into an infinite one.

The compiler assumes "nobody but my code changes this variable" and skips reads. For a hardware register, or a flag changed in an interrupt, that assumption is wrong.

volatile uint8_t rx_done = 0;   // set to 1 in the ISR
while (!rx_done) { }            // without volatile this can become an infinite loop

volatile only forces a read from memory every time. It does not guarantee atomicity, that is, reading a multi-byte value in one go.

const

A promise that the value will not change. A large lookup table marked const is placed in Flash instead of RAM, which saves RAM. On a function argument it also documents "this buffer is only read".

static

  • On a variable or function outside any function: visible only within this file. A tool for hiding a module's internals.
  • On a variable inside a function: the value survives after the function returns. Use it to count calls or remember the previous value.

What happens without volatile

This is the common pattern of raising a flag in an interrupt and waiting for it in the main loop.

uint8_t rx_done = 0;              // no volatile

void USART1_IRQHandler(void) { rx_done = 1; }

int main(void) {
    while (!rx_done) { }          // with optimisation, never exits
    process();
}

As the compiler sees it, nothing inside the loop changes rx_done. So it reads the value once and turns the loop into while (1). This is the classic cause of a bug that runs fine in a debug build (-O0) and hangs only in a release build (-O2). Declared as volatile uint8_t rx_done, it is read from memory every time.

volatile does not guarantee atomicity

volatile only means "read and write every time". If an interrupt changes the value while a 32-bit MCU is reading a 64-bit variable, or an 8-bit MCU is reading a 16-bit counter, you read a half-changed value. For such variables, briefly block interrupts while reading, or read twice and check that the two match.

RoadmapSee where this note sits in C Language→

Where const data is placed

Flashconst table · initial valuesRAMcopy of a non-const table
A const table lives only in Flash. Without const it is in Flash and also copied to RAM.
const uint16_t sine_table[256] = { /* ... */ };   // in Flash, 0 bytes of RAM
uint16_t       sine_copy[256]  = { /* ... */ };   // Flash + 512 bytes of RAM

With const, the linker keeps the table only in Flash (.rodata). On an MCU with a few KB of RAM, leaving const off a lookup table costs that much RAM.

With pointers, the meaning depends on position.

Declaration What cannot change
const uint8_t *p The value p points to
uint8_t * const p p itself (the address)
const uint8_t * const p Both

Three uses of static

  • Inside a function: the value is kept between calls. Suited to state only that function uses, such as a debounce counter.
  • File-scope variable: not visible from other files. Use it to hide a module's internal state.
  • File-scope function: becomes private to that file. Fewer name clashes, and easier for the compiler to inline.

Check yourself

  • Why would a pointer to a hardware status register be declared const volatile together?
  • Is a static variable inside a function placed on the stack, the heap or in the data area?
FREE PDFGet the 3 free ebooksIf you want to keep reading this topic as one book: BLDC motor control, serial communication and sensors at book length. PDFs are in Korean.Get them free →