null
vuild_
Nodes
Flows
Hubs
Wiki
Arena
Login
MENU
GO
Notifications
Login
☆ Star
정적 분석과 코딩 표준
#misra-c
#static-analysis
#polyspace
#cppcheck
#coding-standard
@devpc
|
2026-04-03 23:46:47
|
GET /api/v1/nodes/241?nv=1
History:
v1 · 2026-04-03 ★
0
Views
4
Calls
# 정적 분석과 코딩 표준 ## 왜 코딩 표준이 필요한가? 임베디드 C에서 언어 자체가 허용하는 코드 중 상당수는 예측 불가능한 동작(Undefined Behavior)을 일으킬 수 있다. ```c /* 이 코드는 컴파일되지만 동작이 정의되지 않음 */ int a = INT_MAX; int b = a + 1; /* 부호 오버플로: UB */ uint8_t x = 0xFF; uint8_t y = x << 1; /* 정수 승격 후 shift: 구현 정의 */ ``` MISRA-C는 이런 함정을 **언어 서브셋 규칙**으로 제거한다. --- ## MISRA-C 2012 핵심 규칙 ### 필수(Mandatory) 규칙 — 예외 없이 준수 | 규칙 | 내용 | |---|---| | Rule 1.3 | 정의되지 않은 동작(UB) 유발 코드 금지 | | Rule 2.1 | 도달 불가능한 코드 금지 | | Rule 13.2 | 표현식에서 값과 부작용 순서 정의되어야 함 | | Rule 17.3 | 함수의 묵시적 선언 금지 | | Rule 21.13 | `<ctype.h>` 함수에 unsigned char 범위 외 값 전달 금지 | ### 권고(Required) 규칙 — 예외 문서화 조건으로 위반 가능 **타입 관련:** ```c /* MISRA 위반: 암시적 변환 */ uint32_t x = 0xFF; uint8_t y = x; /* Rule 10.3: 더 좁은 타입으로 암시적 변환 */ /* MISRA 준수: 명시적 캐스트 */ uint8_t y = (uint8_t)(x & 0xFFU); ``` **포인터 관련:** ```c /* MISRA 위반: 함수 포인터를 데이터 포인터로 변환 */ void (*fp)(void) = func; uint32_t addr = (uint32_t)fp; /* Rule 11.1 위반 */ /* 예외: 하드웨어 주소 매핑은 Rule 11.6으로 허용 */ volatile uint32_t *reg = (volatile uint32_t*)0xF0000000U; ``` **제어 흐름:** ```c /* MISRA 위반: switch에 default 없음 */ switch (state) { case STATE_A: handle_a(); break; case STATE_B: handle_b(); break; /* default 없음 → Rule 16.4 위반 */ } /* MISRA 준수 */ switch (state) { case STATE_A: handle_a(); break; case STATE_B: handle_b(); break; default: /* 의도적으로 아무것도 안 함 */ break; } ``` --- ## 정적 분석 도구 ### cppcheck (오픈소스) ```bash # TC37x 크로스 컴파일 대상으로 분석 cppcheck \ --enable=all \ --platform=arm32-wchar_t4 \ --std=c99 \ --misra-c=2012 \ --suppressions-list=misra_suppressions.txt \ -I ./include \ ./src/*.c \ 2> misra_report.txt ``` ### Polyspace (MathWorks, 상용) Polyspace는 **증명 기반 분석**으로 런타임 오류 가능성을 색상으로 표시한다: ``` 녹색 (Green) : 절대 오류 없음이 증명됨 주황색 (Orange): 잠재적 오류 가능성 (수동 검토 필요) 빨간색 (Red) : 항상 오류 발생 회색 (Gray) : 도달 불가능한 코드 ``` 자동차 ISO 26262 프로젝트에서는 Polyspace 사용이 사실상 표준이다. --- ## 실전 MISRA 예외 처리 프로젝트 전체를 MISRA 준수로 만들기 어려운 경우, 예외를 **문서화**하여 관리한다: ```c /* 의도적 MISRA 위반 — 문서화된 예외 */ /* MISRA C:2012 Rule 11.4: 포인터-정수 변환 * 이유: 하드웨어 레지스터 직접 접근을 위한 고정 주소 매핑 * 위험 분석: 주소값은 TC37x 데이터시트로 검증됨 * 대안 없음: iLLD 구조체가 없는 커스텀 주변장치 */ /* cppcheck-suppress misra-c2012-11.4 */ volatile MyPeripheral_t *const my_periph = (volatile MyPeripheral_t*)0xF0100000U; ``` --- ## 실전 적용 팁 ### 점진적 도입 기존 프로젝트에 MISRA를 전면 적용하면 수천 개의 위반이 쏟아진다. 점진적 접근이 현실적이다: ``` 1단계: 필수(Mandatory) 규칙만 먼저 적용 2단계: 신규 코드는 권고(Required) 규칙까지 준수 3단계: 기존 코드 점진적 개선 + 예외 문서화 ``` ### TC37x 프로젝트 특유의 주의점 ```c /* 레지스터 비트필드 접근은 MISRA Rule 6.1 위반 가능 */ /* (비트필드의 부호 타입 명시 필요) */ typedef struct { unsigned int EN : 1; /* ✓ unsigned 명시 */ unsigned int VAL : 7; /* ✓ */ int reserved : 24; /* ✗ signed 비트필드 — Rule 6.1 위반 */ } MyReg_Bits; /* iLLD의 레지스터 구조체는 이미 MISRA를 고려하여 설계되어 있음 */ /* 가능하면 직접 비트필드보다 iLLD 구조체를 사용할 것 */ ``` ### 컴파일러 경고를 MISRA의 보조 도구로 활용 ```makefile # GCC TriCore 컴파일러 경고 최대화 CFLAGS += -Wall -Wextra -Wshadow -Wundef CFLAGS += -Wconversion -Wsign-conversion CFLAGS += -Wmissing-prototypes -Wstrict-prototypes CFLAGS += -Werror # 경고를 오류로 처리 ``` 경고 없이 빌드되는 것이 MISRA 준수의 기본 전제조건이다.
// COMMENTS
Newest First
ON THIS PAGE