임베디드 면접 학습 노트 목차

임베디드 C++·코드 품질 — 임베디드 면접 (실제 기출 기반·심화)

"임베디드에서 C++ 쓰면 뭐가 문제냐", "가상 테이블(vtable) 동작 원리", "예외·RTTI를 왜 끄나", "동적 할당(malloc/new)을 왜 피하나", "MISRA C가 뭐냐", "volatile을 왜 쓰나" — C++ 비용을 이해하고 제약 환경에서 절제하는지를 본다. "공짜"라고 답하면 함정에 빠진다.

함정 다발(★): ① C++는 무조건 C보다 느리다? → 아님·zero-overhead(쓰는 기능만 비용) ② 가상함수는 공짜? → vptr(객체당)+vtable(클래스당)+간접호출·인라인 불가 ③ try 안 쓰면 예외는 공짜? → 실행은 0이나 코드 크기는 비용 ④ new는 malloc과 달라 안전? → 둘 다 힙 ⑤ volatile이 스레드 동기화? → 아님·atomic 아님(SEI CERT CON02-C).


1. C vs C++ — zero-overhead 원칙 (★ 단골)

실제 질문: "임베디드에서 C 대신 C++ 쓰면 좋고 나쁜 점?" · "C++가 C보다 무겁다고 보나? 근거는?" · "malloc과 new 차이?" (velog @jacod2·@mardi2020)

  • zero-overhead 원칙(Stroustrup): "쓰지 않는 것엔 비용을 내지 않고, 쓰는 것은 손으로 짠 것보다 못하지 않다." → C++가 무조건 느린 게 아니라 특정 기능(예외·RTTI·동적할당·가상함수)만 비용.
  • 임베디드 C++ 셋업: -fno-exceptions -fno-rtti -fno-unwind-tables로 비싼 기능을 꺼 C 수준 footprint 유지(실측 13KB → 1.3KB). 전역 생성자(.init_array) 수동 호출·operator new 커스텀으로 힙 오사용 탐지.
  • malloc vs new: new는 생성자 호출 + 타입 안전이 추가될 뿐 둘 다 힙 → 단편화·비결정성은 동일.

꼬리질문: "C++의 어떤 기능이 실제 비용?" → 예외·RTTI·동적할당·가상함수. / "어떤 플래그를 켜겠나?" → -fno-exceptions -fno-rtti.

함정: ❌ "C++는 무조건 무겁다" → 안 쓰는 기능은 비용 0. ❌ "new는 안전하니 써도 됨" → 힙은 malloc과 동일.


2. 가상함수·vtable (★★★ C++ 최빈출)

실제 질문: "가상 테이블(vtable)을 설명하라" · "가상함수 동작 원리?" · "정적 바인딩 vs 동적 바인딩?" · "상속에서 소멸자에 virtual을 왜 붙이나?" · "가상함수 오버헤드는?" (velog @jacod2·@mardi2020·@ci___der)

메커니즘(정확히):

  • 가상함수를 하나라도 가진 클래스마다 컴파일러가 vtable 1개(클래스당·인스턴스 공유) 생성 = 가상함수 주소 배열(함수 포인터 배열).
  • 그 클래스의 각 객체엔 숨은 vptr(vtable 포인터) 추가 = 64비트에서 8바이트/객체.
  • 호출 시: 객체 → vptr 역참조 → vtable 진입 → 슬롯의 함수 포인터 → 호출 = 간접 호출 1단계(추가 메모리 적재).
다이어그램 로딩 중…
  • 비용 정리: RAM = 객체당 vptr(8B)·ROM = 클래스당 vtable·간접호출이라 인라인 불가(대상이 런타임 결정).
  • 가상 소멸자: base 포인터로 delete하는 다형적 삭제가 있으면 가상 소멸자 없을 시 미정의 동작(파생 소멸자 미호출). 단 가상 소멸자도 vtable을 만들므로 다형 삭제 안 할 클래스엔 안 붙임.

꼬리질문: "가상함수 1개 추가 시 메모리?" → 객체마다 vptr 8B + 클래스당 vtable 1개. / "vtable은 객체마다? 클래스마다?" → 클래스마다 1개·vptr만 객체마다. / "왜 인라인 안 되나?" → 대상이 런타임 결정. / "vtable 없이 다형성?" → CRTP(템플릿 컴파일타임 다형성).

함정: ❌ "가상함수는 공짜" → vptr+vtable+간접호출+인라인 불가. ❌ "가상함수 안 부르면 비용 0" → 존재만 해도 모든 객체에 vptr 8B.


3. 예외(Exception)·RTTI (★★ 단골)

실제 질문: "임베디드에서 예외를 끄는 이유?" · "RTTI가 뭐고 왜 비활성화?" · "dynamic_cast 동작·실패하면?" · "예외 대신 에러를 어떻게 처리?"

  • 예외 비용: 현대 구현은 zero-cost(table-based)던지지 않는 한 런타임 명령 0개. throw 시점에만 unwind table로 스택을 되감고 소멸자 실행. 비용은 ① 코드 크기(ARM은 .ARM.exidx+.ARM.extab 테이블·typeinfo가 바이너리 상주·실측 +~10KB, 일부 +100KB) ② 비결정성(throw→handler 도달 시간 가변 → WCET 분석 곤란·언와인딩에 동적 할당 쓰는 구현도). → 임베디드는 예외를 끈다.
  • RTTI(typeid·dynamic_cast): 다형성 클래스에만 적용 — vtable에 typeinfo 메타데이터 연결(ROM). dynamic_cast는 런타임에 상속 계층 순회(비용). -fno-rtti로 제거(아두이노 기본).

꼬리질문: "예외 없이 에러 전달?" → 리턴 코드·std::expected<T,E>(C++23·errors-as-values)·optional/variant·치명적은 assert/fault handler. / "RTTI 끄면 dynamic_cast 대신?" → 타입 enum+switch·visitor(double dispatch).

함정: ❌ "try 안 쓰면 예외 공짜" → 실행 비용만 0·코드 크기는 비용. ❌ "예외가 리턴코드보다 항상 느리다" → 정상 경로는 분기 없어 빠를 수도·문제는 크기·비결정성.


4. 동적할당 회피·STL 제약 (★★ 단골)

실제 질문: "동적 할당을 왜 피하나?" · "힙 단편화가 뭐고 왜 문제?" · "꼭 필요하면?" · "STL(vector)을 써도 되나?" · "스마트 포인터?"

동적할당 회피 3대 이유:

  1. 힙 단편화: 반복 할당/해제로 힙이 조각나 총 여유는 충분해도 큰 연속 블록 실패. 장시간(24/7) 누적.
  2. 비결정적 시간: malloc/new 소요가 힙 상태 따라 가변 → WCET 보장 불가.
  3. OOM 처리 곤란: new는 기본 bad_alloc(예외 끄면 abort).

→ 대안: 정적 할당(.data/.bss) + 메모리 풀/고정크기 할당자(결정적)·메모리를 사전 파악.

  • STL 제약: 표준 컨테이너(vector·map·string)는 힙 할당 → 단편화·비결정성 상속·예외 의존·코드 크기. 대안 = ETL(Embedded Template Library): 힙 절대 미사용(컴파일타임/스택)·고정 최대 용량·RTTI 미사용·오류처리 선택·연속 블록(캐시 친화)·C++03 호환. etl::vector<T, N> 등.

꼬리질문: "꼭 필요하면?" → 시동 시 1회만 할당 후 정적 유지·메모리 풀·etl 고정용량·placement new. / "vector를 써야 하면?" → reserve() 1회 선할당 또는 etl::vector<T,N>. / "힙 오사용 탐지?" → operator new/delete 오버라이드 후 부팅 후 호출 시 fault.

함정: ❌ "vector는 stack에 있다" → 객체 헤더만·원소 저장소는 힙. ❌ "단편화는 메모리 부족할 때만" → 총량 충분해도 연속 블록 없어 실패.


5. MISRA C·정적분석 (★ 자동차/안전 직군 단골)

실제 질문: "MISRA C가 뭐고 왜 쓰나?" · "규칙 예시 하나?" · "정적 분석 도구 써봤나?" (자동차·방산 빈출)

  • MISRA C = 자동차/안전필수 C 코딩 가이드라인(MISRA C:2012·최신 개정은 2023/2025나 한국 면접에선 2012가 가장 널리 참조·아래 규칙 번호는 동일)·ISO 26262·IEC 61508 맥락. : 이식성·안전·미정의 동작(UB) 회피 — C의 위험 영역(포인터·암묵 형변환·동적 메모리)을 제한해 실수 자체를 못 하게 + 정적 분석 가능 상태로.
  • 분류: Directive(문서/프로세스) + Rule(정적분석 검증). 준수 등급 — Mandatory(절대)·Required(원칙 준수·정당한 근거 문서화 시 deviation 허용Advisory(권고).
  • 대표 규칙: Dir 4.12/Rule 21.3 동적 메모리 금지·Rule 17.2 재귀 금지(스택 오버플로)·Rule 15.1 goto 금지(advisory)·10.x 암묵 형변환 제한·21.x 0 나누기 방지.
  • 정적분석: 컴파일러 경고가 1차 방어선 — -Wall -Wextra -Werror·cppcheck·clang-tidy·상용(PC-Lint·Coverity·Polyspace·QAC). 실행 없이 소스만 분석해 결함 탐지.

꼬리질문: "동적할당 금지 이유?" → 단편화·비결정성(§4). / "Required vs Advisory?" → Required는 deviation 문서화 필요·Advisory는 권고. / "정적분석 vs 단위테스트?" → 실행 없이 소스 분석 vs 실행하며 동작 검증.

함정: ❌ "MISRA는 강제 표준(법)" → 가이드라인·등급화·Required는 deviation 문서화 시 예외. ❌ "MISRA 따르면 버그 0" → UB·위험 패턴을 줄일 뿐·논리 버그는 별개.


6. 단위 테스트 (★ 단골)

실제 질문: "임베디드에서 단위 테스트를 어떻게?"(하드웨어 없이) · "HW 의존 코드를 어떻게 테스트?" · "HIL과 차이?"

  • 프레임워크: Unity(가벼운 C xUnit)·Ceedling(Unity + CMock + CException + 빌드 자동화·C TDD 표준)·CppUTest(C/C++·메모리 누수 탐지·mocking).
  • 목/테스트 더블: HW 의존(레지스터·드라이버)을 HAL로 격리하고 단위 테스트는 그 인터페이스를 mock으로 대체(반환값 설정·인자 검증·호출 확인) → 호스트 PC에서 실행(보드 없이 CI).
  • HIL과 구분: 단위 테스트=호스트에서 로직 격리 검증 / HIL(Hardware-in-the-Loop)=실제·시뮬레이션 하드웨어 연결 통합 검증. 서로 대체 불가.

꼬리질문: "레지스터 직접 접근 코드 테스트?" → 접근을 함수/HAL로 감싸 mock 주입. / "Ceedling은 뭘 묶은 거?" → Unity + CMock + CException.

함정: ❌ "임베디드는 하드웨어 있어야만 테스트" → HAL/mock으로 호스트 단위 테스트 가능. ❌ "단위 테스트 = HIL" → 격리 수준이 다름.


7. const·volatile·inline (★★ volatile 최빈출)

실제 질문: "volatile을 왜 쓰나? 예를 들어라"(임베디드 최빈출) · "volatile vs static?" · "const correctness?" · "const volatile을 함께?" · "inline 장단점·재귀 inline?"

  • volatile: "프로그램이 모르게 바뀔 수 있다" → 컴파일러에게 매 접근마다 실제 메모리 읽기/쓰기·레지스터 캐싱·중복 제거·죽은 저장 제거 등 최적화 금지. 용도: ① 메모리 매핑 I/O·하드웨어 레지스터(읽을 때마다 다름) ② ISR↔메인 공유 전역(ISR이 바꾼 값을 메인이 봐야) ③ 시그널 핸들러 공유(volatile sig_atomic_t).
  • const volatile = 읽기 전용 하드웨어 상태 레지스터: 프로그램은 못 쓰지만(const) 하드웨어가 바꾸니 매번 다시 읽어야(volatile).
// 읽기 전용 상태 레지스터: const(쓰기 금지) + volatile(매번 실제 읽기)
volatile uint32_t* const DATA_REG   = (uint32_t*)0x40000000; // R/W HW 레지스터
const volatile uint32_t* STATUS_REG = (uint32_t*)0x40000004; // 읽기 전용 상태
while (!(*STATUS_REG & READY_BIT)) { /* 매 루프 실제 메모리 폴링 */ }
  • const/inline: const=불변 의도를 타입에 명시(컴파일러 검증 + 문서화·const T* vs T* const 구분). inline=본문 펼침 권고(강제 아님)·호출 오버헤드↓ vs 코드 크기↑.

volatile ≠ atomic(★ 최우선 함정): volatile은 원자성·가시성·메모리 순서 어느 것도 보장 안 함(SEI CERT CON02-C). volatile 변수 간 순서는 지켜도 다른 메모리는 재정렬 가능 → 동기화 프리미티브로 부적합. 멀티스레드는 std::atomic/mutex. C++11 이후 volatile은 메모리 모델·멀티스레딩과 무관.

꼬리질문: "volatile vs atomic?" → volatile=최적화 억제(단일 스레드·하드웨어용)·atomic=원자성+순서+가시성(멀티스레드). 직교. / "ISR 공유 변수에 volatile만으로 충분?" → 단일 워드는 보통 OK·멀티바이트/RMW는 원자성 미보장 → 인터럽트 비활성화·atomic. / "const volatile 언제?" → 읽기 전용 상태 레지스터.

함정: ❌ "volatile이 atomic/동기화 보장" → 거짓·데이터 레이스는 UB. ❌ "inline 붙이면 무조건 인라인" → 권고일 뿐.


한국 임베디드 면접 단골 Q&A (답변 골격)

질문핵심 답
임베디드 C++ 트레이드오프zero-overhead·예외/RTTI/동적할당/가상함수만 비용
malloc vs newnew=생성자+타입안전·둘 다 힙
vtable 동작클래스당 vtable·객체당 vptr(8B)·간접호출·인라인 불가
가상 소멸자 왜다형 삭제 시 없으면 미정의 동작
예외를 왜 끄나코드 크기(테이블)·비결정성(WCET)
RTTItypeinfo·dynamic_cast 런타임·-fno-rtti
동적할당 회피단편화·비결정성·OOM → 풀/정적
STL 제약힙·예외 의존 → ETL(고정용량)
MISRA C안전 C 가이드라인·Mandatory/Required/Advisory·동적할당 금지
정적분석-Wall -Wextra -Werror·cppcheck·clang-tidy
단위 테스트Unity/Ceedling·HAL mock·호스트 실행
volatile 왜HW 레지스터·ISR 공유·최적화 억제
volatile vs atomic최적화 억제 vs 원자성/순서(동기화 아님)

꼬리질문 대비 (상 난이도)

  • "가상함수를 vtable 없이?" → CRTP(컴파일타임 정적 다형성).
  • "예외 없이 에러 처리 모던 C++?" → std::expected<T,E>(C++23) errors-as-values.
  • "동적 할당이 꼭 필요하면?" → 시동 시 1회 할당·메모리 풀·etl 고정용량.
  • "volatile만으로 ISR 공유가 안전한가?" → 멀티바이트/RMW는 원자성 미보장 → 인터럽트 차단/atomic.
  • "MISRA Required를 어길 수 있나?" → deviation을 문서화하면.

한 줄 요약 — C++는 zero-overhead(쓰는 기능만 비용). vtable=클래스당 1개·vptr 객체당 8B·간접호출·인라인 불가(가상 소멸자는 다형 삭제 시 필수). 예외는 zero-cost지만 코드 크기·비결정성이 비용이라 끔(-fno-exceptionsRTTI-fno-rtti. 동적할당 회피(단편화·비결정성·OOM → 풀/정적·STL 대신 ETL). MISRA C(안전 가이드라인·Mandatory/Required/Advisory·동적할당·재귀 금지)·정적분석(-Wall -Wextra -Werror). 단위 테스트는 HAL mock으로 호스트 실행. volatile=HW 레지스터·ISR 공유의 최적화 억제일 뿐 atomic·동기화가 아니다(CON02-C).

(출처 — 한국 면접 기출·1차자료 교차검증 2026-06: velog @jacod2·@mardi2020·@ci___der(vtable)·medium @snghojeong(RTTI)·@bazzi95(MISRA) · 1차자료: SEI CERT CON02-C — volatile ≠ 동기화·bare_metal_cpp — RTTI/예외/vtable 실측·bitbashing.io — C++ on Embedded·ETL 공식 문서·MathWorks — MISRA C:2012 레퍼런스·ThrowTheSwitch — Unity/Ceedling 교차검증.)

펌웨어 빌드·부팅 — 빌드 과정·메모리 섹션(.data/.bss)·링커·startup·OTA디버깅·신뢰성 — JTAG/SWD·HardFault(fault 레지스터)·트레이스·watchdog·EMC