"이 값은 C 코드 밖에서 바뀔 수 있다" 를 컴파일러에게 알리는 것이 전부다.
없으면 벌어지는 일
uint32_t *status = (uint32_t *)0x40020000;
while (*status == 0) { } /* 루프 안에서 아무도 안 바꾼다고 판단 →
한 번만 읽고 무한 루프 */
컴파일러의 추론은 논리적으로 옳다. 값을 바꾸는 주체가 하드웨어라는 사실을 모를 뿐이다.
붙일 자리
volatile uint32_t *reg; /* 주변장치 레지스터 — 하드웨어가 바꾼다 */
volatile uint8_t flag; /* ISR 이 쓰고 메인이 읽는 변수 */
붙이지 않을 자리
일반 지역 변수·계산용 버퍼에 습관적으로 붙이면 최적화만 막아 느려진다.
가장 흔한 오해
volatile 은 원자성을 주지 않는다. 읽기-수정-쓰기는 여전히 세 단계이고, 인터럽트가 끼어들면 값이 사라진다. 원자성이 필요하면 임계 구역이나 전용 레지스터를 쓴다. 멀티코어의 메모리 순서 보장도 아니다 — 그건 배리어의 일이다.