임베디드 면접 용어 사전
C 언어

volatile

컴파일러 최적화로 접근을 생략하지 못하게 막는 한정자. 레지스터·ISR 공유 변수·멀티스레드 플래그에 붙인다. 원자성은 보장하지 않는다.

"이 변수는 컴파일러가 모르는 이유로 바뀔 수 있다"고 알려 주는 한정자. 최적화를 금지하는 것이 전부다.

왜 필요한가 — 컴파일러의 합리적 착각

uint32_t *status = (uint32_t *)0x40020000;   // 하드웨어 레지스터
while (*status == 0) { }                      // 준비될 때까지 대기

컴파일러는 이렇게 판단한다.

  • "이 루프 안에서 *status 를 바꾸는 코드가 없다.

    • 그러면 매번 메모리를 읽을 필요가 없지 않은가?"
  • 한 번만 읽어 레지스터에 담고, 그 값으로 비교한다

  • 값이 0이면 영원히 무한 루프 ✗

컴파일러의 판단은 논리적으로 옳다. 문제는 그 값을 바꾸는 주체가 C 코드 밖(하드웨어)에 있다는 사실을 모른다는 것이다.

volatile uint32_t *status = (volatile uint32_t *)0x40020000;
while (*status == 0) { }     // 매 반복마다 실제 메모리를 읽는다

어떤 최적화가 금지되나

  • 레지스터 캐싱 금지 — — 매번 메모리에서 읽는다
  • 접근 제거 금지 — — 결과를 안 쓰는 읽기도 실제로 수행한다
  • 순서 재배치 금지 — — volatile 접근끼리는 순서가 유지된다
  • 접근 병합 금지 — — 두 번 쓰면 두 번 쓴다

②가 중요한 경우가 있다. 어떤 레지스터는 읽는 행위 자체가 플래그를 지우는 부수 효과를 갖는다(read-to-clear). 컴파일러가 "결과를 안 쓰니 지우자"고 없애 버리면 인터럽트가 계속 발생한다.

반드시 붙여야 하는 세 가지 경우

// ① 메모리 맵 하드웨어 레지스터
#define GPIOA_IDR  (*(volatile uint32_t *)0x40020010)

// ② ISR과 메인 루프가 공유하는 변수
volatile uint8_t flag = 0;
void EXTI0_IRQHandler(void) { flag = 1; }
int main(void) { while (!flag) { } }    // volatile 없으면 무한 루프

// ③ setjmp/longjmp 를 넘나드는 지역 변수

②가 실무에서 가장 흔하다. "인터럽트가 분명히 걸리는데 메인 루프가 빠져나오지 않는다"는 증상의 단골 원인이다. 특히 최적화를 -O0에서 -O2로 올린 순간 동작이 깨진다 — 디버그 빌드에서는 멀쩡하다가 릴리스에서만 터진다.

가장 중요한 오해 — volatile은 원자성을 보장하지 않는다

volatile uint32_t counter;
counter++;         // 여전히 읽기-수정-쓰기 3단계. 인터럽트가 끼면 깨진다

volatile이 보장하는 것은 "실제로 접근한다"뿐이다.

volatile   → 최적화 금지         (컴파일러에 대한 지시)
원자성      → 인터럽트 차단 / CAS  (하드웨어 수준 보호)
메모리 배리어 → CPU 재배치 방지     (멀티코어에서 필요)

셋은 다른 문제를 푸는 다른 도구다.

// 8비트 MCU에서 16비트 카운터를 안전하게 읽기
uint32_t safe_read(void) {
    uint32_t v;
    __disable_irq();
    v = counter;
    __enable_irq();
    return v;
}

멀티코어에서는 부족하다

volatile컴파일러 최적화만 막는다. CPU가 명령 순서를 바꾸거나 캐시가 코어 간에 동기화되지 않는 것은 막지 못한다. 멀티코어 SoC나 DMA와 공유하는 버퍼에는 메모리 배리어(__DMB())나 캐시 유지 연산이 함께 필요하다.

const volatile 은 모순이 아니다

const volatile uint32_t *sensor = (const volatile uint32_t *)0x4000A000;

"내가 쓸 수는 없지만(const), 하드웨어가 바꿀 수 있다(volatile)" — 읽기 전용 상태 레지스터를 정확히 표현한다. 면접에서 종종 묻는 조합이다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — C 언어 핵심 — 포인터·메모리클래스·const/volatile·비트·struct 패딩