[MISRA-C] Rule 적용

CS·2025년 6월 19일

SW설계

목록 보기
11/13

MISRA-C
안전 중심의 시스템에서 C 코드를 안전하고 신뢰성 있게 만들기 위한 코딩 표준

  • Mandatory : 반드시 준수. deviation 불가능
  • Required : 가능한 준수. 공식적인 deviation 가능
  • Advisory : 무시하면 안되지만 추천하는 항목

최대한 MISRA-Compliant 하게 만드는게 목표

MISRA-C 확인

MISRA-C 2012

Rule 8.8

static int CalcBonus(int score);
static char getGrade(int score); //선언시 Static(Internal linkage)


static int base = 1000; // 다른 파일에서 extern 접근시 8.8 위반 위험가능

int CalcBonus(int score) { // Define시 external linkage 가능, 충돌
//동일 함수가 다른 linkage로 두 번 선언되어 Rule 8.8 위반

int bonus;

switch (getGrade(score)) {
 case 'S':
 bonus = base * 1.3;
 break;
 case 'A':
 bonus = base * 1.1;
 break;
 case 'B':
 bonus = base * 0.9;
 break;
 }
 
 return bonus;
}
 char getGrade(int score) {
	 if (score > 90)return 'S';
	 else if (score > 80) return 'A';
	 else return 'B';
 }

MISRA-C 8.8 Rule만을 적용하면,


Rule 8.8: An external object or function shall be declared in one and only one file.

이 규칙의 목적은 외부 linkage
(즉 다른 파일에서 접근 가능한 전역 객체/함수)의 선언이
여러 번 중복되지 않도록 하여,
링커 오류나 이름 충돌을 방지.

static만 있어 Internal linkage만 존재해 외부에서 extern으로 접근 할 수가 없고, 중복 정의도 없고, 이 file 내에서만 정의되어야 함.


static char getGrade(int score) {
	 if (score > 90)return 'S';
	 else if (score > 80) return 'A';
	 else return 'B';
 }

static int base = 1000;

static int CalcBonus(int score) {

int bonus=0;

switch (getGrade(score)) {
 case 'S':
 bonus = base * 1.3;
 break;
 case 'A':
 bonus = base * 1.1;
 break;
 case 'B':
 bonus = base * 0.9;
 break;
 }
 
 return bonus;
}

추가로 Mandatory 항목들도 수정했다.


Rule 7.4

 int main() {
 
 char buf[20] = "Hello World!";
 char* str = "Hello World!";
 //info 1776: converting string literal to 
 //'char *' is not const safe [MISRA 2012 Rule 7.4, required]
 
 SYSTEM_Init();
 buf[0] = 'h';
 my_printf("buf[] = %s\n", buf);
 
 //str[0]은 실행불가
 str[0] = 'h';
 my_printf("*str = %s\n", str);
 while(1);
 return 0;
 
 }

문자열 리터럴을 수정 가능한 포인터(char*)에 대입했기 때문에 위반

"Hello World!"는 문자열 리터럴이며, read-only 메모리 영역(ROM, .rodata 등)에 저장됩니다.

char str = "Hello World!";처럼 선언하면, str은 읽기 전용 데이터를 가리키지만 char 타입은 수정 가능하다는 의미이므로, 형식적으로 쓰기 가능한 메모리를 가리키는 것처럼 보입니다.

이후 str[0] = 'h';는 실제로는 읽기 전용 메모리 변경 시도이기 때문에 → 정의되지 않은 동작(UB)이며, 런타임 크래시(segfault)로 이어질 수도 있습니다.

const char* str = "Hello World!"; // ✅ OK (읽기 전용으로 선언)
이렇게 하면:

컴파일러가 문자열 리터럴을 수정하지 않도록 보장해 주고

MISRA Rule 7.4 위반도 방지됩니다.

반면, 수정 가능한 문자열을 쓰고 싶다면?
char buf[20] = "Hello World!"; // ✅ OK
buf[0] = 'h'; // ✅ OK (수정 가능)
이건 수정 가능한 스택 버퍼이므로 문제 없습니다.

MISRA-C:2012 Rule 7.4의 의도는 “문자열 리터럴은 읽기 전용이므로, 이를 수정 가능하다고 가정되는 char에 넣지 말라”는 것이 맞습니다.
→ const char
또는 수정 가능한 char buf[]를 사용하세요.

 int main() {
 char buf[20] = "Hello World!";
 
 //Modified
 const char* str = "Hello World!";


SYSTEM_Init();
 
 
 buf[0] = 'h';
 my_printf("buf[] = %s\n", buf);
 
 //값 변경은 불가, 가르키는 문자열은 변경가능
 //char * const라면 현재 가르키는 것 값 변경은 가능
 str[0] = 'h';
 
 my_printf("*str = %s\n", str);
 while(1);
 return 0;
 }

Volatile의 중요성(제거 하지 마라)

#include <iostream>
#include <vector>
#include <algorithm>
#include <stdio.h>
using namespace std;

int main() {

	//아래 두문장 volatile안하면 최적화되어 i=10으로 초기값 최적화때리고 반복문 안함 
	int i = 0;
	while (i < 10) {
		i++;
	}

	printf("%d\n", i);
	
	return 0;

}

while문의 어셈블리어가 생성되지 않음
i를 0으로 정의하는 어셈블리어 또한 생성되지 않고
컴파일러가 최적화하여 10을 넣고 증가시키지 않음.
while문 돌지 않음.

int i = 10;
printf("%d\n", i);

이렇게 대체하여 최적화를 해버림

#include <iostream>
#include <vector>
#include <algorithm>
#include <stdio.h>
using namespace std;

int main() {

	//while에서 정상 증가하여 10도출
	volatile int i = 0;
	while (i < 10) {
		i++;
	}

	printf("%d\n", i);
	
	return 0;

}

의도대로 while문 돌아 올라감


RULE 11.8

포인터 타입 변환에 관한 규칙
포인터 캐스팅을 통해 const나 volatile 속성을 제거하면 안 된다

const int *ptr = ...;
int *p2 = (int *)ptr;  // ❌ const 속성 제거 → Rule 11.8 위반

const int 는 "읽기 전용" 메모리를 가리킵니다.
이를 (int
*)로 캐스팅하면 "쓰기 가능"한 포인터가 됩니다.
컴파일러나 코드 리뷰어 입장에서 안전하지 않은 동작을 하려는 시도로 보임.
MISRA는 이런 의도적 속성 제거를 금지.

마찬가지로, Volatile도 제거하면 안됨.

volatile int *pv = ...;
int *p = (int *)pv;  // ❌ Rule 11.8 위반 (volatile 제거)

수정 예시 :

const int *ptr = ...;
const int *p2 = ptr;  // ✅ const 유지 → OK

volatile int *pv = ...;
volatile int *p2 = pv;  // ✅ volatile 유지 → OK

const나 volatile은 하드웨어 제어, 메모리 보호, 병렬 처리에서 중요한 의미를 갖습니다.

이들을 임의로 제거하면, 정의되지 않은 동작이나 하드웨어 오류를 유발할 수 있기 때문에 MISRA는 이를 엄격히 제한

예시 코드Rule 11.8 위반 여부설명
(int *) (const int *)❌ 위반const 제거
(int *) (volatile int *)❌ 위반volatile 제거
(const int *) (int *)✅ OKconst 추가 (허용)
(volatile int *) (int *)✅ OKvolatile 추가 (허용)
(const volatile int *) (int *)✅ OKconst, volatile 추가

MISRA C Dir 4.10

모듈화/재사용 등을 위해
헤더파일과 C파일로 분기하여 개발하게 됨.
C파일에 기능을 구현하고 헤더파일에 선언후

헤더파일을 Include하면 사용 가능

#ifndef MY_EX_H
#define MY_EX_H
int defNum=3;
#end if

----------------------------------------------

#ifndef MY_EX_H2
#define MY_EX_H2
#include "ex.h"
#endif

---------------------------------------

#include <ex.h>
#include <ex2.h>
하여도 Header중복 X

로 중복 Header file을 Include해 재정의 Error가 나지 않도록 해라.

즉, #include를 통해 같은 헤더 파일이 여러 번 포함되더라도
헤더 안의 선언/정의가 중복 포함되지 않도록 보호하라.
(Guard를 써라)

하지만, 이것도 가능은 함
#pragma once // ✅ 하지만 MISRA에서는 권장하지 않음 (비표준)


RULE 20.8

#if
#elif

문의 조건문은 전처리기가 0 or 1로 판별할 수 있어야 함

전처리기가 처리상수에 대한 논리/산술 연산만 가능

#define FALSE 0
#define TRUE 1

#if FALSE // OK
#end if

#if 10 // 위반(0 or 1로 판별 불가능)
#endif

#if ! defined (X) // 0 or 1로 판별 가능. OK
#endif

#if A>B // 0 or 1로 판별 가능. OK
#end if
#include "main.h"
 #define TRUE 11 //여기
 int main(void)
 {
   SYSTEM_Init();
   
#if TRUE
   my_printf("preprocessor is true!!\n");
 #endif
   while(1);
   return 0;
 }

정상 실행되나, 20.8을 준수하지 못하고 있음.

 #define TRUE 1
 int main(void)
 {
   SYSTEM_Init();
   
#if TRUE
   my_printf("preprocessor is true!!\n");
 #endif
   while(1);
   return 0;
 }

1또는 0으로 판별 가능하도록 변경하여야 함


Rule 8.5

Rule 8.5는 전역 객체(변수, 함수 등)의 정의에 대한 명확한 규칙

전역 변수나 함수의 ‘정의(Definition)’는 헤더 파일에 쓰지 말고, 소스 파일(.c)에만 작성해야 한다. 헤더에는 선언만 해라

그리고 한 개의 헤더 파일에서만 해라. Type은 당연히 일치해야하고

Data type의 선언/정의를 관리해 일관성 유지가능
type 변경 경우 Header에서 type을 선언하면 전체 프로젝트에 적용 가능

위반 예시

int global_value = 100;           // ❌ 전역 변수 정의 → Rule 8.5 위반
int add(int x, int y) {           // ❌ 함수 정의 → Rule 8.5 위반
    return x + y;
}

--------------------------------------

#include "my_header.h"

int main() {
    return add(global_value, 5);
}

표준에 따라 수정

#ifndef MY_HEADER_H
#define MY_HEADER_H

extern int global_value;         // ✅ 전역 변수 선언
int add(int x, int y);           // ✅ 함수 선언

#endif

-------------------------------------

#include "my_header.h"

int global_value = 100;          // ✅ 전역 변수 정의
int add(int x, int y) {          // ✅ 함수 정의
    return x + y;
}

extern, static inline 선언 허용
다만 extern시 무결성이 깨질 수 있다. 들어와서 재정의하게 되면서


Rule 20.2

Header file 이름에 , / /* / / 등을 쓰지마라


Rule 20.3

Header file명은 <>(표준) 혹은 ""(사용자 정의)에 담아라.
매크로로 쓰는거는 괜찮다

단,

#include "./include/cpu.h"로 path를 포함하면 괜찮다.

Rule 20.5

#undef

쓰지마라지마라
헷갈리고 명확해지지 않기 때문에


함수 Macro

함수 매크로는 기계적인 대치

#define SQUARE(X) (X*X)

SQUARE(y++);로 하면 y++ * y++로 확장되어 +2에 주의
의도한바와 다르게 동작할 수 있어 조심해서 사용

함수 매크로 사용시 모든 인자에 괄호를 추가하는게 좋음(우선순위)

#define SQUARE(X) ((X) *(X))

Type검사가 불가능하다
함수 호출 Overhead가 없음(실제론, 함수가 아닌 대치라서)

전 처리후에, 해당 내용으로 컴파일 시기 전에 대치됨.

인라인 함수

함수 호출 없이 코드 복사해 넣는 것
인라인은 컴파일러에게 인라인으로 생성하도록 알려주는 역할이며,
항상 인라인으로 생성되지 않을 수 있음.

컴파일러 종류(GCC/MSVC) 및 최적화 설정에 따라 다를 수 있음.

int main(){
	int res = square(10);
    return 0;
}

**inline** int square(int i){
	return i*i;
}

---------------------------------------------

int main(){
	int res = 10 * 10;
    return 0;
}

inline인 경우에 Body를 call한 곳에 그대로 insert

Rule 8.10

Inline 함수는 static으로 선언 필요.

External linkage를 사용하는 경우
Inline으로 컴파일될지
외부 함수를 호출할지 정의 되어 있지 않음.

기능 수행에는 문제가 없으나, 수행 시간에 차이 발생(RT에 악영향)

다양한 파일에서 인라인 함수 사용하고자 할때에는
Header에 정의해야 함.

#include "main.h"

 inline int square(int i) {
  volatile int res = i*i; 
  return i * i;
 }
 
 int main() {
 SYSTEM_Init();
 my_printf("square(2) = %d\n", square(2));
 my_printf("square(3) = %d\n", square(3));
 while(1);
 return 0;
 }
 
 ------------------------------------------
 main()이 포함된 1 file에서만 쓰일때
 
 #include "main.h"

 static inline int square(int i) {
  volatile int res = i*i; 
  return i * i;
 }
 
 int main() {
 SYSTEM_Init();
 my_printf("square(2) = %d\n", square(2));
 my_printf("square(3) = %d\n", square(3));
 while(1);
 return 0;
 }
 
 -----------------------------------------
 square() 함수가 여러 파일에서 사용된다고 가정시
 Guard를 통해 재정의 방지
 
 #ifndef MAIN_H_ //start
 #define MAIN_H_
 
 #include "~"
 
 #ifndef NULL
 #define NULL 0
 #endif
 
 static inline int square(int i) {
 	volatile(별 의미없음 이 경우에는) int res = i * i;
    return res;
}

#endif //end
 
 

MISRA C Directive 4.9

필수적이지 않으면 매크로 함수 대신 일반 함수를 사용해야 함.

"Function-like macros shall not be used."
즉, 매개변수를 받아 연산하는 매크로는 지양해야 하며, 반드시 필요할 때가 아니면 일반 함수로 대체하라는 규칙

연산자를 함수의 매개변수로 처리할 수 없음
Static 변수의 초기값은 매크로로 결정할 수 없음

바로 다음과 같은 경우들

#define EVAL_BINOP ( OP, L, R ) ( (L) OP (R) )
uint32_t x = EVAL_BINOP (+,1,2);

#define DIV2(X) ( (X) / 2 ) //일반 함수 사용 권장

void f(void){
	static uint16_t x = DIV2 (10); // 불가능
    	   uint16_t y = DIV2 (10); // 가능
 }

매크로 함수는 가능하면 사용하지 말고 일반 함수로 대체

특히 static 초기화처럼 컴파일 타임 평가(초기화)가 필요한 곳에서는 매크로 사용 금지. 매크로 사용이 부작용이나 복잡한 식 평가를 유도할 수 있끼 때문에 상수식으로 보일지라도 사용해선 안됨.

일반 변수는 런타임 평가라 매크로는 가능하다 지양

static 지역 변수 초기화
한 번만 초기화되며, 컴파일 타임 상수 또는 constant expression으로 초기화되어야 함

“function-like macro” 는 때때로 평가 가능성이 불확실함
특히, 매개변수 연산이 포함된 매크로는 constant expression 보장 불가

매크로 사용이 허용되더라도, MISRA에서는 "컴파일러 입장에서 완전히 예측 가능해야 한다"는 점이 중요

전처리 연산자 #

일반 함수로 구현하기 어려운 매크로 함수 예시

#define PRINT_INT(a) my_printf("%s=%d\n", #a, a)
//#a -> 토큰 a를 문자열로 변환
// 이 함수는 일반 함수로 절대 구현불가. 변수명 전달이 불가능해서

int main(){
	int a=1, b=2;
    
    PRINT_INT(a); // a= 1
    PRINT_INT(b); // b= 2
    
    while(1);
    
 }

전처리 연산자 ##

#define PRINT_INT(a,b) my_printf("Result: %d\n", a##b)

//a와b를 붙여 하나의 토큰으로 만드는 역할

int main(){
	int a1 = 1, a2 = 2;
    
    PRINT_INT(a,1); // Result: 1 , a##1 , a1
    PRINT_INT(a,2); // Result: 2 , a##2 , a2
    
    while(1);
 }

Rule 20.10

하지만, #와 ## 전처리기 연산을 쓰지마라.
정적 분석기 입장에서 해석이 곤란함.


Rule 5.4

매크로 이름은 뚜렷하여 명확히 구분할 수 있어야 함
앞에서부터 32개의 문자를 대소문자 구분해 인식


Rule 20.4

매크로 명으로 Keyword명 써서는 안됨


Rule 20.6

매크로 인자 내에 전처리기로 보이는 토큰이 존재해서는 안된다.

#define M(A) printf (#A)
#include <stdio.h>

void main(void){
	M( #ifdef SW  // Non-compliant
    "Message 1" 
    #else "Message 2" //Non-compliant
    #end if ); // Non-compliant

#ifdef등의 전처리 지시어를 인자로 사용 하지마라.
문자열 처리된다. 애초에 실행도 안됨.


Rule 20.7

#define M1 (x, y) (x * y)
r = M1 (1+2, 3+4);

매크로는
r = (1+2 * 3+4)로 확장시켜준다. // non-compliant


r = M1 ( (1+2), (3+4) ); // compliant

#define M2 (x,y) ( (x) * (y) )

r = M2 ( 1+2, 3+4); // compliant

매크로 인자들의 연산식 결과는 괄호로 닫혀야 한다.


Rule 20.13 (Required)

#로 시작하는 토큰은 타당한 전처리 시작하는 토큰은 타당한 전처리 명령이여야함.

#ifndef AAA
	x =1;
#else1 //non-compliant , #으로 시작해도 else1은 전처리 명령이 아님
	x = AAA;
#endif

Rule 20.14 (Required)

동일한 파일에서 전처리 명령의 짝을 맞춰 끝내야함.

ex) file2.c에서 #if열고 file2.h에서 #endif로 닫는 기행을 벌여선 안됨.


함수 및 변수 이름 정의 : Rule 5

함수 및 변수명은 가독성을 고려해 쉽게 이해할 수 있게 작성되어야 함
ex) int a,b; 는 안됨

변수명으로 어떤 역할을 하는지 알 수 있도록 작성해야 함

조직/프로젝트 별 변수/함수명 네이밍 규칙 보유
type을 알 수 있는 키워드변수 역할을 알 수 있는 이름을 연결해 작성

ex) u16MaxSpeed / i32DemandToque

31글자 이상은 인식하지 못하는 경우 존재. 30글자 이하로 작성 요망

MISRA C Dir 4.5

같은 Scope(예: 파일, 함수 등 = Namespace)에서
동시에 보이는 이름들은 헷갈리지 않게 비슷한 철자나 형태로 작명하지 말라.

int motorSpeed;
int motorspeed;   // ⛔ 오해하기 쉬움 → MISRA 비권장

void control_motor(void);
void ControlMotor(void); // ⛔ 대소문자만 다름 → 혼동 우려

철자만 조금 다른 변수/함수 → 사람 눈으로 구분하기 어려움

유지보수 중 실수 유발 가능성 큼

특히 C는 대소문자 구분(Case-sensitive)이기 때문에, abc와 ABC는 완전히 다른 식별자지만 사람 눈엔 비슷하게 보임

int motor_speed;
int motor_direction;   // ✔️ 형태, 의미가 확실히 다름

void controlMotor();
void stopMotor();      // ✔️ 구분 쉬움
용어설명
Name space같은 종류의 식별자들이 속하는 공간 (예: 변수 이름 공간, 함수 이름 공간)
Overlapping visibility한 코드 블록 안에서 동시에 접근 가능한 상태 (예: 같은 파일 안의 글로벌 변수들)
Typographically unambiguous철자나 형태(대소문자 등)가 명확히 구분됨

추가로 abc랑 ABC도 non-compliant임

0과 O도, B와 8도, Z와 2도, 같지만 언더바 개수가 다르더라도
전부 Non-compliant(눈으로 헷갈려 보이니까)


Rule 5.1 (Required)

외부 식별자들이 명확히해 구분되어야함.

같은 프로젝트 내에서 링커에 노출되는 이름은 서로 구분 가능하게 만들어야 한다는 의미.

컴파일러/링커/분석 도구가 오해하지 않도록 식별자 이름 충돌을 방지

많은 C 컴파일러나 링커는 식별자의 길이 일부만 사용하거나, 대소문자 구분 없이 처리하기도 함.

오래된 시스템에서는 식별자 31자까지만 구분하거나, 앞 6자만 유효한 경우도 있었음.

이런 제약 때문에 식별자들이 겉으론 달라도 **링커 관점에서는 동일하게 처리될 수 있음 → 링크 충돌** 발생

extern int motor_speed_controller;
extern int motor_speed_controlled; // ⛔ 일부 컴파일러에서 동일하게 인식될 수 있음
extern void ReadSensor(void);
extern void readsensor(void);  // ⛔ 일부 링커는 대소문자 구분 안 함
  • 권장 방식

  • 식별자는 충분히 길고 의미 있는 이름으로 작성

  • 대소문자, 언더스코어만으로 구분하는 방식은 피함

  • 프로젝트 따라 prefix 등을 붙이는 것도 좋음

extern void APP_ReadSensor(void);
extern void HW_ReadSensor(void);  // ✔️ 명확히 구분됨

컴파일러에서 잘 잡히지 않고,
정적 분석 도구(e.g., Polyspace, LDRA, PC-lint 등)에서 주로 확인함.

Rule 5.2(Required)

같은 scope, name space에 선언된 식별자들은 뚜렷해야 한다.

31자가 넘어가면, 두 변수는 구분이 안된다. // Non-compliant

scope가 다르면, 초과해도 인식 가능
31글자 이하는 문제 자체가 없음.


Rule 5.3

내부 scope에 선언된 식별자는
바깥 scope에 선언된 식별자를 숨겨선 안된다.

void fn1(void){
 int16_t i; // 함수 scope 전체에서 유효
 
 {
 	int16_t i; // Non-compliant(바깥 i를 숨김)
    i=3; //중첩 블록 내 유효, 다른 메모리 공간에 있는 i
    
 }
 }
 
 
 -------------------------------------
 
 struct astruct{
 	int16_t m;
 };
 extern void g (struct astruct *p);
 
 int16_t xyz = 0;
 
 void fn2 (struct astruct xyz){
 	g(&xyz); // 바깥 xyz가 내부 xyz parameter에 의해 숨겨짐
 }
 

주로 코딩테스트에서 Parameter랑 전역변수를 같은 이름으로 쓰는 경우가 종종 있는데 주의해야겠음.


Rule 5.8 (Required)

External linkage를 갖는 객체나 함수를 정의하는 식별자는 유니크 해야한다.

extern 선언된 변수/함수 명이 지역 변수명과 중복되지 말란 뜻
어느 변수가 사용되는지 "모호"함.

// file1.c //

int32_t count; // count는 external linkage를 갖고있음.

void foo(void){ // foo또한 갖고 있음.
	int16_t index; // index는 없음.
}

-------------------------------------------------

// file2.c //

static void foo(void) // non-compliant(foo 중복, 유니크 하지않음)
{

	int16_t count; // non-compliant, count 충돌
    
    int32_t index; // compliant - no linkage
 
 }

겹칠 경우, 더 가까운 Scope의 변수를 사용
단, 원천적으로 금지하는 것이 좋음


Rule 5.9

Internal Linkage를 갖는 객체나 함수를 정의하는 식별자는 유니크 해야함.

Linkage 종류설명예시
external linkage다른 파일에서도 참조 가능int count; (전역, static 없음)
internal linkage선언된 파일 내에서만 참조 가능static int count;
no linkage다른 선언과 연결되지 않음지역 변수, 함수 파라미터 등
// file1.c //
static int32_t count; //count는 Internal linkage 보유(static)
static void foo(void) // foo도 Internal linkage 보유
{
	int16_t count; // Non-compliant(hiding)
    
    int16_t index; // index has no linkage
 }
 
 
 void bar1 (void)
 {
 	static int16_t count; // Non-compliant(hiding)
    
    int16_t index; // compliant 다른 함수의 scope
    
    foo();
  }
  
  
  
  // file2.c //
  
  static int8_t count;
  
  static void foo(void)
  {
  
  	int32_t index;
    int16_t nbytes;
  
  }
  
  void bar(void)
  {
  	static uint8_t nbytes; 
  }
  
 

적절한 Data type 정의

  • typedef의 필요성 :
    보다 명확한 Data type을 재정의해 가독성 높이고
    Human error를 방지함.
  1. int type은 CPU마다 크기가 다름
  2. uint32, uint16등을 활용해 unsigned int type 정의
  3. 코드 재사용성/가독성 증대

가독성을 높이기 위한 타입 재정의 :
질량을 나타내는 정수형 변수를 위해 typedef unsigned int mass 정의
mass a, b, c; 등으로 활용가능

typedef vector<vector<int>> Graph;
//기존 type들도 재정의 가능

Rule 5.6

typedef 할 이름은 모든 공간에서 유일한 식별자여야함.

void func(void)
{
	{
    	typedef unsigned char u8_t;
    }
    {
    	typedef unsigned char u8_t; // Non-compliant - reuse
        //u8_t이름이 중복되어 사용됨
    }
}

typedef float mass;

void func1(void)
{
	//Non-compliant - reuse
    // mass가 type명, 변수명으로 중복 사용
	float32_t mass = 0.0f;
    
}

typedef struct list
{
	struct list *next;
    uint16_t element;
}list;  // compliant - 이 경우에는 예외로 가능

typedef struct
{
	struct chain
    {
    	struct chain *list; // x
        uint16_t element;
     }s1;
     uint16_t length;
 }chain;
 
 //Non-compliant , chain중복 

typedef list와 변수 list는 다른 공간에 존재해 충돌로 간주하지않음.
따라서 chain만 중복이라고는 하는데. 아무래도 이 쪽도 위반으로 봐야하지않나 생각이 든다. Human error줄이려고 하는건데

typedef와 struct tag의 tag가 명이 같으면 안됨.


Rule 5.7

구조체, 공용체, enum의 이름(tag)는 모든 공간에서 유일한 식별자여야 함.

즉, 동일 이름을 구조체/공용체/enum에서 사용하지 말 것.

컴파일은 가능하지만, 혼동 야기

struct stag
{
	uint16_t a;
    uint16_t b;
};

struct stag a1 = {0,0}; //compliant
union stag a2 = {0,0}; // Non-compliant


struct elk
{
	uint16_t x;
 };
 
 struct elk // Non-compliant
 {
 	uint32_t x;
 };

Rule 6.1

bit 필드는 적절한 type으로 선언되어야 함.

이때, signed int(2의보수 음수표현)와 unsigned int만 허용함

ex) 0111 -> 7 / -1
0100 -> 4 / -4로 정확히 음수표현이 가능해짐(signed int)

typedef unsigned int UINT_16

struct s
{
	unsigned int b1:2; // compliant, 2bit만 차지
    int b2:2; // Non-compliant
    UINT_16 b3:2 // compliant
    signed long b4:2 // Non-compliant
};

Rule 7.1

8진수 쓰지마라. 단 0은 예외
별도 표기 없으면 10진법이랑 헷갈림
C에서 0으로 시작하면 8진수 리터럴임

extern uint16_t code[10];

code[1] = 109; // compliant , 109
code[2] = 100; // compliant , 100
code[3] = 052; // Non-compliant , 42
code[4] = 071; // Non-compliant , 57

Rule 7.2

'u'나 'U' 접미어는 unsigned type 표현에 적용되어야한다.

0x8000 << 이건 Non-compliant다
0x8000u로 Compliant하게 해줘야함.

  • 리터럴 suffix

없음 : int

l,L : long
u,U : unsigned int
ul, UL : unsigned long
ll, LL : long long
ull, ULL : unsigned long long


Rule 7.3

소문자 l을 리터럴 suffix로 쓰지마라
1과 헷갈리니까 대문자 L로 써라.

0LL;은 되는데
0ll;은 쓰지마라.

1.2L은 되는데
1.2l은 쓰지마라.

참고로 순서와 대소문자 혼용을 허용한다
0Lu; 같은것도 됨

근데 0lU;로 소문자 l쓰는건 안됨


Rule 8.1

type들은 명시적으로 구체화되어야함

extern x; // Non-compliant(Implict type)
extern int16_t x; // Compliant - explict type

typedef (*pfi) (void); // implicit int(Non-compliant)
typedef int16_t (*pfi) (void); // compliant

//참고로 이렇게 쓴다.
pfi fuc_ptr = foo;
fun_ptr();

type을 빼먹지 말라는 규칙인듯
컴파일러한테 컴파일타임에 추정시키지말라는 얘기같음


Rule 8.2

함수 타입은 원형에 이름붙은 파라미터와 함께 있어야함.

extern int16_t func1 (int16_t n); //compliant

extern void func2( int16_t); // Non-compliant

static int16_t func3 (); // Non-compliant

static int16_t func4 (void); // compliant

int16_t func1 ( int16_t n)
{
	return n;
} // compliant

static int16_t func3 (vec, n)
int 16_t *vec;
int 16_t n;
{
	return vec[n-1];
} // old style 선언 식별자 , Non-compliant

Rule 8.3

객체나 함수의 모든 선언은 같은 이름과
같은 const, volatile등의 키워드를 사용해야 함. (정의와)

그러니까 헤더와 같은 원형을 써서 정의하란 얘기


Rule 8.4

extern되는 경우 반드시 정의 전, 선언해야함.
일반적으로 1개의 헤더 파일에서만 선언(일관성 유지를 위해)

static이나 지역 변수에는 당연히 선언 불필요.

file1.h
extern int16_t count; //메모리 할당x

file1.c
int16_t count =0; //compliant, 여기서 메모리 할당됨(정의)

file2.c
count =5; // include header후 가능

extern uint16_t speed = 6000u; // Non-compliant, 선언없음

extern void func1 (void);
extern void func2 (int16_t x, int16_t y);
extern void func3 (int16_t x, int16_t y);

void func1 (void)
{
	// compliant
}

void func2 (int16_t x, 1nt16_t y)
{
	//compliant
}

void func3 (1nt16_t x, uint16_t y) // 8.3 x
{
	//Non-compliant by Rule 8.3
}

void func4 (void)
{
	//Non-compliant(no declaration of func4 before this definition
}

static void func5 (void)
{
	//compliant - static exception/ internal 적용x
}

그리고 일반 함수도 정의 전에 Prototype을 선언하라는 의미도 내포되어 있다.

func4는 extern이 아닌데, 8.4 위반으로 나온다.

앞으로는 선언하고 main 뒤에서 정의하자


Rule 8.11

extern 되는 배열의 크기는 반드시 명시적으로 명시해야함

extern int32_t array1[10] // compliant
extern int32_t array2[]; // Non-compliant

Rule 8.12

enum에서 값이 중복되어 지정되는 경우 반드시 명시적으로 지정해야함.

enum colour { red=3, blue, green, yellow = 5};
//Non-compliant

enum colour { red=3, blue, green=5, yellow =5};
profile
학습

0개의 댓글