본문으로 건너뛰기
고급약 30분

MISRA C — 지침의 범주·결정 가능성·분석 범위를 읽고 판(2012·2023·2025)을 확정하기

기능안전 규격은 코딩 표준을 요구하고, C 언어 프로젝트에서 그 표준은 대부분 MISRA C다. 규칙과 지시의 차이, 필수·요구·권고 범주, 결정 가능성과 분석 범위가 정적 분석 결과의 신뢰도를 어떻게 바꾸는지 읽고, 대표 지침을 코드로 확인한 뒤 2012판에서 개정안·2023판·2025판으로 이어지는 판 관계를 계약 문구로 확정하는 법을 다룬다.

  • #MISRA-C
  • #코딩표준
  • #정적분석
  • #기능안전
  • #C언어
  • #결정가능성

개념

왜 C에는 코딩 규칙이 필요한가

레슨 1에서 61508-3의 A.3·A.4 표가 설계·코딩 표준(B.1)을 강하게 권고하는 것을 봤고, 레슨 2에서 ISO 26262-6의 8절이 코딩 가이드라인을 요구한다는 것도 봤다. 자동차·산업·철도의 임베디드 소프트웨어는 대부분 C로 쓰이고, 그 코딩 표준으로 가장 널리 쓰이는 것이 MISRA C 다.

C가 특히 규칙을 필요로 하는 이유는 언어 표준 자체에 있다. C 표준은 일부 동작을 일부러 정하지 않고 남겨 둔다.

분류뜻예
정의되지 않은 동작표준이 어떤 결과도 보장하지 않는다초기화하지 않은 지역 변수 읽기, 부호 있는 정수 오버플로
명시되지 않은 동작여러 결과 중 무엇이 될지 정하지 않는다함수 인자의 평가 순서
구현 정의 동작컴파일러마다 다르되 문서화해야 한다int 의 크기, 부호 있는 정수의 오른쪽 시프트

레슨 2의 호스트·타깃 차이 분석에서 정수 크기 가 함정이었는데, 그것이 바로 구현 정의 동작이다. 같은 소스 코드가 컴파일러와 CPU에 따라 다르게 동작할 수 있는 지점을 코드에서 없애거나 드러내는 것 이 MISRA C의 기본 목적이다. 모듈 9 레슨 1에서 코드를 읽는 네 블록을 배웠다면, MISRA는 그 블록들 중 어떤 조합이 위험한가 를 목록으로 만든 것이라고 보면 된다.

지침의 두 종류 — 규칙과 지시

MISRA C는 지침(guideline) 을 두 종류로 나눈다.

종류성격확인 방법
규칙(Rule)소스 코드만으로 판단할 수 있게 완결되게 쓰였다정적 분석 도구로 대부분 확인
지시(Directive)소스 코드만으로는 판단할 수 없고 설계·문서·프로세스 정보가 필요하다리뷰·문서 확인이 함께 필요

MISRA C:2012는 처음 발행될 때 규칙 143개와 지시 16개 였다. 지시의 예로는 런타임 실패를 최소화하라거나, 구현 정의 동작 중 코드가 의존하는 것을 문서화하라는 종류의 요구가 있다. 이런 요구는 도구가 "위반 없음"이라고 말해도 충족됐다고 할 수 없다. 도구 리포트에 지시가 빠져 있다면 그것은 준수가 아니라 미확인이다. 레슨 5의 준수 매트릭스에서 지시가 따로 "수동 확인" 열을 갖는 이유다.

세 범주 — 필수·요구·권고

모든 지침에는 범주가 붙는다.

범주뜻위반을 남길 수 있는가
필수(Mandatory)반드시 따라야 한다어떤 경우에도 편차를 허용하지 않는다
요구(Required)따라야 한다정해진 편차 절차를 거치면 가능
권고(Advisory)모범 사례편차 절차 없이도 벗어날 수 있으나, 준수 체계 안에서 관리한다

범주가 레슨 1의 HR·R과 겹쳐 보이지만 성격이 다르다. 61508의 HR은 기법 선택의 권고였고, MISRA의 범주는 코드 한 줄 한 줄의 위반을 어떻게 처리할 수 있는가 를 정한다. 필수 지침 위반은 고치는 것 말고는 방법이 없다.

레슨 5에서 볼 MISRA Compliance 체계는 여기에 "적용 제외(Disapplied)" 라는 네 번째 상태를 더하고, 프로젝트가 범주를 조정할 수 있는 규칙을 둔다. 핵심만 미리 적어 두면, 더 엄격한 쪽으로의 재분류는 허용되고, 느슨한 쪽으로는 권고 지침만 적용 제외로 내릴 수 있다.

결정 가능성 — 도구가 "없다"고 말할 때 믿어도 되는가

MISRA C:2012는 각 규칙에 결정 가능(Decidable) 인지 결정 불가능(Undecidable) 인지를 표시한다.

  • 결정 가능 — 원칙적으로 정적 분석이 위반 여부를 항상 정확히 판단할 수 있는 규칙. 예: 함수를 암묵적으로 선언하지 말라
  • 결정 불가능 — 프로그램의 실행 경로와 값에 따라 달라져, 정적 분석이 모든 경우에 확실히 판단할 수 없는 규칙. 예: 자동 저장 기간의 객체를 값이 설정되기 전에 읽지 말라(초기화 전 사용)

이 표시가 시험 해석을 바꾼다.

결정 가능 규칙에서 "위반 0"   → 도구 설정이 맞다면 강한 증거
결정 불가능 규칙에서 "위반 0"  → "도구가 찾지 못했다"는 뜻. 미탐 가능성이 남는다
결정 불가능 규칙에서 "위반 N"  → 일부는 오탐일 수 있다. 사람의 판정이 필요하다

초기화 전 사용 규칙은 필수이면서 결정 불가능 이다. 편차는 허용되지 않는데 도구는 확실히 판단할 수 없다. 이런 규칙은 정적 분석만으로 닫지 않고 코드 리뷰, 동적 분석, 컴파일러 경고, 방어적 초기화 정책 을 함께 증거로 묶는다. 모듈 3 레슨 7에서 "커버리지 100%가 결함 0%가 아니다"를 봤다면, 여기서는 "위반 0이 준수가 아닐 수 있다" 를 기억한다.

분석 범위 — 파일 하나만 보면 되는가

규칙에는 결정 가능성과 함께 분석 범위 도 표시된다.

범위뜻시험에 주는 의미
단일 번역 단위소스 파일 하나(와 포함 헤더)만 보면 판단 가능파일 단위 분석으로 확인 가능
시스템여러 번역 단위를 함께 봐야 판단 가능전체 프로젝트를 한 번에 분석 해야 한다

"같은 이름의 외부 식별자가 여러 파일에서 다르게 정의되지 않았는가" 같은 규칙은 파일 하나를 봐서는 알 수 없다. 개발자가 자기가 고친 파일만 IDE에서 분석하고 "위반 없음"을 보고하는 것은 시스템 범위 규칙에 대해서는 아무것도 확인하지 않은 것 이다. 레슨 5에서 시험소가 독립 재수행을 할 때 분석 대상 파일 목록과 빌드 설정 을 원자료로 받는 이유가 이것이다.

대표 지침을 코드로 읽기

지침 문구를 외우는 것보다, 위반 코드와 수정 코드를 나란히 보는 편이 빠르다. 아래는 자주 만나는 유형을 요지로 옮긴 예다(정확한 문구·번호·범주는 사용 중인 판의 원문으로 확인한다).

반환값을 버리지 않는다 (요구 범주로 분류되는 대표 규칙)

/* 위반 */
write_eeprom(addr, data);            /* 실패 코드가 반환되는데 무시 */
 
/* 수정 */
if (write_eeprom(addr, data) != E_OK) {
    enter_safe_state(ERR_EEPROM);
}
/* 정말 무시할 의도라면 (void) 캐스트로 의도를 드러낸다 */
(void)log_debug(msg);

기능안전에서 이 규칙이 중요한 이유는 명확하다. 실패를 알리는 반환값을 무시하는 코드는 안전 상태로 갈 기회를 버린다. 모듈 34 레슨 3에서 본 EN 18031의 입력 검증처럼, 보안 코딩과도 겹치는 지점이다.

본질적 자료형(essential type)을 섞지 않는다

uint8_t  count = 200;
int8_t   delta = -100;
 
/* 위반 — 부호 없는 값과 부호 있는 값의 산술 */
if (count + delta > 50) { ... }
 
/* 수정 — 의도를 명시하는 변환과 같은 범주의 연산 */
int16_t sum = (int16_t)count + (int16_t)delta;
if (sum > 50) { ... }

MISRA C:2012는 C의 자료형 위에 본질적 자료형 모델 을 두고, 부호 있는·없는·불리언·문자 같은 범주를 섞는 연산을 제한한다. 레슨 2의 과제 3(16비트 타깃의 raw * 4)이 이 모델로 잡히는 종류의 결함이다.

단일 출구 (2012판에서 권고 범주)

/* 권고 위반 — 중간 return 여러 개 */
int check(int v) {
    if (v < 0)   return ERR_NEG;
    if (v > MAX) return ERR_MAX;
    return OK;
}

오랫동안 논쟁이 많던 규칙이다. 중간 return 이 오히려 읽기 쉬운 경우가 많기 때문이다. 이 규칙은 MISRA C:2025에서 적용 제외(disapplied) 로 바뀌었다. 판에 따라 같은 코드의 판정이 달라지는 대표 사례라 뒤 절에서 다시 본다.

표준 라이브러리의 동적 메모리 할당을 쓰지 않는다

/* 위반 */
buf = malloc(len);
 
/* 대안 — 컴파일 시점에 크기가 정해진 정적 풀 */
static uint8_t pool[POOL_SIZE];

동적 할당은 단편화와 할당 실패 시점을 예측하기 어렵다. 안전 관련 임베디드 코드에서 실행 시간과 메모리 사용량을 결정적으로 만들기 위한 규칙이다. 레슨 2의 방법 표에 있던 자원 사용량 평가 가 이런 규칙과 짝을 이룬다.

판의 관계 — 2012에서 2025까지

MISRA C는 한 번 발행하고 끝나지 않았다. 판 관계를 시간순으로 놓으면 이렇다.

MISRA C:2012            규칙 143 · 지시 16. C90·C99 대상
  + Amendment 1         보안 관련 지침 추가
  + Amendment 2         C11/C18 핵심 기능과 정의되지 않은·명시되지 않은 동작 대응
  + Amendment 3 (2022)  C11/C18 신규 기능 지침
  + Amendment 4 (2023)  멀티스레딩·원자적 연산 지침
        │  (개정안과 기술 정오표를 하나로 통합)
        ▼
MISRA C:2023            지침 221개
        │
        ▼
MISRA C:2025 (2025-03)  활성 지침 225개
                        신규 4 · 삭제 2 · 적용 제외 1(단일 출구) · 일부 범주 변경
                        (예: 포인터↔정수 변환 관련 한 규칙이 권고 → 요구)
                        삭제된 번호는 재사용하지 않는다

2025판의 신규 지침 넷은 헤더 파일의 잠정적 정의, 소스 파일 안의 외부 선언, 포인터와 NULL의 명시적 비교, 공용체의 비활성 멤버 읽기를 다룬다. 해설 자료들은 이 판을 전면 개편이 아니라 정밀 조정 으로 평가한다.

이 표에서 시험 담당자가 가져가야 할 것은 판마다 판정이 달라지는 코드가 실제로 있다 는 사실이다.

같은 코드MISRA C:2012 판정MISRA C:2025 판정
중간 return 여러 개권고 위반적용 제외 — 위반 아님
포인터를 정수로 변환(권고였던 규칙)권고 위반(편차 없이 관리 가능)요구 위반 — 편차 절차 필요

두 번째 줄이 무섭다. 판을 올리는 순간 편차 없이 넘어가던 코드가 편차 기록을 요구 한다. 판 전환은 도구 설정 파일 교체가 아니라 위반 재분류와 편차 재작성 작업 이다.

계약의 "MISRA C 준수"를 확정하는 네 가지

계약서나 발주 사양서에 "MISRA C 준수"라고만 적혀 있으면, 시험 착수 전에 네 가지를 확정한다.

1. 판        MISRA C:2012 (어느 개정안까지) / 2023 / 2025
2. 언어 표준  C90 / C99 / C11 / C18 — 판에 따라 대상 언어가 다르다
3. 준수 체계  MISRA Compliance 기준으로 주장하는가 (GEP·GRP·편차·요약서 — 레슨 5)
4. 대상 코드  자체 작성 코드만인가, 제3자·생성·레거시 코드를 포함하는가

모듈 32 레슨 7의 인정범위 목록에는 MISRA C:2012 가, 다른 기관 목록에는 MISRA C:2012, MISRA C:2023 이 함께 올라가 있었다. 시험기관이 판마다 따로 인정 받는 이유가 위 표의 판정 차이다. 1번과 2번이 어긋나면(예: C11 기능을 쓰는 코드를 개정안 없는 2012판으로 검사) 규칙이 그 기능을 다루지 않아 판정 공백 이 생긴다.

보안 코딩 규칙과의 관계

인정범위 목록에는 MISRA와 함께 SEI CERT C 가 오르는 경우가 많다. 두 표준은 겹치는 부분이 크지만 출발점이 다르다. MISRA C는 예측 가능성과 안전, CERT C는 악용 가능한 취약점 방지 에서 출발했다. MISRA C:2012의 Amendment 1이 보안 관련 지침을 더한 것이 두 세계의 접근을 보여준다. 모듈 34의 EN 18031·IEC 62443 제품이 C로 쓰였다면, 발주처는 두 표준을 모두 요구할 수 있고 준수 매트릭스는 지침별로 어느 표준의 어느 항목에 대응하는지 를 함께 적는 편이 효율적이다.

실무에서 왜 필요한가

MISRA C 도입의 실패는 대부분 첫 분석 결과 에서 시작한다. 기존 코드에 도구를 처음 돌리면 위반이 수천 건 나오고, 팀은 "이걸 다 고칠 수 없다"며 도구를 끄거나 규칙을 무더기로 비활성화한다. 이 레슨의 범주·결정 가능성·분석 범위를 알면 대응이 달라진다. 필수 지침 위반부터, 결정 가능한 요구 지침, 그리고 결정 불가능 지침의 사람 판정 순서 로 나누면 수천 건이 관리 가능한 덩어리가 된다.

판 확정은 비용을 막는다. 개발 중간에 도구 벤더가 기본 설정을 2025판으로 바꾸거나, 발주처가 계약 갱신에서 판을 올리면 편차 기록이 대량으로 다시 필요해진다. 판을 계약 문구와 도구 설정 파일에 함께 고정 해 두는 것이 가장 싼 대비다.

코드 리뷰에서도 쓰인다. 모듈 9에서 QA가 PR diff를 읽는 법을 배웠는데, 안전 관련 코드에서는 반환값 무시·자료형 혼합·동적 할당 같은 패턴을 diff 단계에서 잡는 것이 정적 분석 후 되돌리는 것보다 싸다.

실습 과제

과제 1 — 위반 찾기와 범주 붙이기 (20분)

아래 코드에서 본문에서 다룬 유형의 위반을 모두 찾고, 각 위반에 대해 (1) 유형, (2) 본문 기준 범주, (3) 수정안을 적는다.

#include <stdlib.h>
int16_t filter(uint8_t n, int8_t *samples) {
    int16_t acc;
    int8_t *copy = malloc(n);
    for (uint8_t i = 0; i < n; i++) {
        if (samples[i] < 0) return -1;
        acc += samples[i] + n;
    }
    memcpy(copy, samples, n);
    return acc / n;
}

(힌트: 초기화 전 사용이 있다. 그 규칙이 필수이면서 결정 불가능이라는 점이 수정 방식에 어떤 영향을 주는지도 적는다.)

과제 2 — 결정 가능성에 따른 증거 설계 (15분)

"초기화 전 사용 금지"와 "함수 암묵 선언 금지" 두 규칙에 대해, 정적 분석 리포트가 각각 "위반 0"을 보고했다. 두 규칙에 대해 추가로 필요한 증거 를 각각 적고, 차이가 나는 이유를 결정 가능성으로 설명한다.

과제 3 — 판 전환 영향 표 (15분)

한 프로젝트가 MISRA C:2012(Amendment 없음)에서 MISRA C:2025로 전환한다. 본문 표의 두 사례를 포함해, 전환 시 새로 편차가 필요해지는 경우 / 편차가 필요 없어지는 경우 / 판정 공백이 해소되는 경우 를 각각 하나 이상 적는다.

과제 4 (선택) — 계약 문구 확정 (10분)

"공급자는 MISRA C를 준수한 소스 코드를 납품한다."를 본문의 네 가지 확정 항목이 모두 들어간 문구로 고쳐 쓴다.

자가 체크리스트

  • 정의되지 않은·명시되지 않은·구현 정의 동작의 차이와 MISRA C의 목적을 설명할 수 있다
  • 규칙과 지시의 차이, 지시가 도구만으로 닫히지 않는 이유를 안다
  • 필수·요구·권고 범주별로 위반을 남길 수 있는지 말할 수 있다
  • 재분류가 허용되는 방향(엄격하게 / 권고만 적용 제외로)을 안다
  • 결정 가능·결정 불가능 규칙에서 "위반 0"의 증거 강도 차이를 설명할 수 있다
  • 단일 번역 단위와 시스템 분석 범위의 차이와, 파일 단위 분석의 한계를 안다
  • 반환값 무시·본질적 자료형 혼합·단일 출구·동적 할당 유형을 코드에서 찾을 수 있다
  • MISRA C:2012 → 개정안 → 2023(221) → 2025(225)의 판 관계를 설명할 수 있다
  • 판에 따라 같은 코드의 판정이 달라지는 사례를 두 가지 들 수 있다
  • "MISRA C 준수" 계약 문구에서 확정해야 할 네 가지를 안다
  • MISRA C와 CERT C의 출발점 차이를 설명할 수 있다

흔한 실수

도구 리포트의 "위반 0"을 준수로 보고한다. 지시와 결정 불가능 규칙은 도구만으로 닫히지 않는다.

필수 지침 위반에 편차를 쓴다. 필수 범주는 편차가 허용되지 않는다. 고치는 방법뿐이다.

변경한 파일만 분석한다. 시스템 범위 규칙은 전체 프로젝트 분석이 필요하다.

판을 확인하지 않고 도구 기본 설정을 쓴다. 도구 업데이트로 판이 바뀌면 판정이 바뀌고 편차가 대량으로 다시 필요해질 수 있다.

언어 표준과 MISRA 판을 따로 본다. C11 기능을 쓰는 코드를 그 기능을 다루지 않는 판으로 검사하면 판정 공백이 생긴다.

첫 분석의 수천 건 앞에서 규칙을 무더기로 끈다. 범주와 결정 가능성으로 나눠 우선순위를 세우고, 끄는 대신 준수 체계(레슨 5) 안에서 관리한다.

지침 문구를 해설 자료에서 옮겨 적는다. 번호·범주·문구는 판마다 달라질 수 있다. 계약·성적서에는 사용 판의 원문을 근거로 적는다.

참고 자료

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

ISO — 국제표준화기구
여러 나라가 모여 "이건 이렇게 하자"를 문서로 정하는 단체. 그 문서 자체를 번호로 부른다.
MISRA — 미스라
안전이 중요한 분야에서 "이런 식으로는 코드를 쓰지 말라"를 모아 둔 규칙집.
CPU — 중앙처리장치
계산을 담당하는 부품. 사람으로 치면 머리다.
HR — 인사
채용과 인사를 담당하는 조직.
IDE — 통합 개발 환경
코드를 쓰고 돌려 보고 오류를 찾는 일을 한 창에서 하게 해 주는 프로그램.
EN — 유럽 표준
유럽 공통 표준 문서에 붙는 번호 앞머리.
NULL — 값 없음
"값이 아직 없다" 를 나타내는 특별한 표시. 0 이나 빈 글자와는 다르다.
IEC — 국제전기기술위원회
전기·전자 분야의 국제 규격을 만드는 단체.
QA — 품질보증
만든 것이 쓸 만한지 확인하고, 애초에 덜 망가지게 만드는 방법을 정하는 일. "테스트"는 그 일의 일부다.
PR — 풀 리퀘스트
"내가 고친 코드를 본체에 합쳐 달라"고 올리는 요청. 합치기 전에 사람들이 보고 의견을 단다.
LDRA — 엘디알에이
안전이 중요한 분야에서 쓰는 코드 검사·커버리지 측정 도구 제품군.

용어집 전체 보기