개념
왜 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 단계에서 잡는 것이 정적 분석 후 되돌리는 것보다 싸다.