Table of Contents
C의 매크로는 프로그래머가 일정한, 인라인 코드 스니펫을 정의하고 조건 컴파일을 수행 할 수있는 사전 처리 단계의 강력한 기능입니다. 효과적으로 사용될 때, 그들은 크게 코드 재사용성을 향상시키고 유지 보수성을 향상시키고 개발자가 더 유연하고 프로그램을 작성 할 수 있습니다. 그러나, 그들의 힘은 위험과 함께 온다; 부적절한 매크로 사용은 미묘한 버그를 도입하고 코드를 디버그에 강하게 만들 수 있습니다. 이 문서는 일반적인 관행을 방지하면서 최대 이득을 위해 매크로를 어떻게 활용하는지 탐구합니다. C의 가장 일반적인 관행과 C의 경험에서 가장 좋은 사례를 피하십시오.
매크로는 무엇입니까?
매크로는 지시어를 사용하여 정의되며 컴파일이 시작되기 전에 C preprocessor에 의해 처리됩니다. 사전 처리기는 간단한 텍스트 대변을 수행합니다. 소스 코드의 매크로 이름의 각 발생은 매크로의 정의로 대체됩니다. 이 대용은 순으로 텍스트이며 범위 또는 유형 검사를 존중하지 않습니다. 매크로는 object-like (정상적인]) (]) (]) (])를 사용하거나 ]를 를 갖는 것을 의미합니다.
#define PI 3.14159 // object-like macro
#define SQUARE(x) ((x)*(x)) // function-like macro
이 사전 처리 단계는 매크로가 기능 또는 변수와 같은 규칙을 따르지 않기 때문에 중요합니다. 그들은 확장 인라인으로, 이는 성능의 장점을 제공하지만 또한 독특한 도전을 소개합니다.
Code Reusability 및 유지 보수를위한 Macros의 이점
적용된 생각으로, 매크로는 큰 성과 과민한 코디에 있는 몇몇 구체적인 이점을 제공합니다:
- Code Reusability: 한 번에 일반적인 계산 또는 스니펫을 정의하고 프로그램을 통해 사용하십시오. 매크로 업데이트를 변경하면 자동으로 업데이트됩니다.
- ] Magic Numbers의 제거: ] 또는 ]와 같은 리터를 흩어지게 하는 대신, 의미 있는 이름을 부여합니다. 이 개선은 읽을 수 있고 튜닝 파라미터 트리 바이알을 만듭니다.
- Performance Gains: Function-like Macros는 함수 호출의 머리 위를 피합니다. 작은, 자주 사용되는 작업(예: min/max, square)의 경우, 매크로는 인라인 함수보다 더 빠르게 할 수 있습니다.
- Conditional 컴파일: ], , ], 컴파일시에 따라 코드를 제외한 코드를 제외한다. 이것은 플랫폼 별 코드, 디버그 빌드 또는 기능 투글에 필수적이다.
- Simplified Configuration: 단일 헤더의 다른 값으로 매크로를 정의함으로써 구현 파일 없이 전체 코베이스의 동작을 조정할 수 있습니다.
로버스트로피드의 글: Best Practices
매크로의 유연성은 위험도입니다. 이로 인해 쓴 매크로는 추적하기 어려운 예상치 못한 행동을 일으킬 수 있습니다. 이 모범 사례를 따라 매크로를 안전하고 유지할 수 있습니다.
항상 Parenthesize 매개 변수와 전체 표현
매크로는 텍스트 대용암호이므로, 연산자 선행은 끊을 수 있습니다. ]와 같은 매크로는 ] 에 를 확장합니다 (그것은 11, not 25). 수정은 모체의 모든 매개 변수를 둘러싸고 전체 표현을 포장하는 것입니다.
#define SQUARE(x) ((x) * (x)) // now SQUARE(3+2) works correctly
Arguments의 여러 평가를 피하십시오.
매크로 인수는 as-is를 대용합니다. 인수가 부작용이 있는 경우 (예: ]), 한번 이상 평가될 수 있습니다. 예를 들어:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
int x = 5;
int max = MAX(x++, 10); // expands to ((x++) > (10) ? (x++) : (10)) // x incremented twice!
이 기능을 피하려면 인라인 함수를 선호하거나 주의적인 naming Conventions를 사용하여 warn user를 사용합니다. 만약 당신이 매크로를 사용해야 하는 경우, 인수가 부작용을 포함하지 않아야 하는 문서.
Macro Names의 Uppercase 사용
컨벤션에 의해, 매크로는 위문자(예:, ]], ])에 쓰여져 있습니다. 이로 인해 변수와 함수를 갖춰 사고의 가능성을 줄입니다.
멀티스테이트 블록을 위한 Do-While를 가진 엔드 매크로
여러 문장을 포함하는 매크로를 정의할 때, 루프에서 감싸십시오. 이 매크로는 모든 컨텍스트에서 올바르게 동작합니다 (예를 들어, ] 괄호 없이):
#define LOG_ERROR(msg) do { \
fprintf(stderr, "Error: %s\n", msg); \
exit(1); \
} while (0)
문서 Macros Thoroughly
매크로는 기능의 자기 문서화 성격이 부족합니다. 항상 인수, 예상 행동 및 제한에 대한 의견을 추가합니다. 적절한 경우 Doxygen-style 의견을 사용하십시오.
고급 Macro 기술
C preprocessor는 간단한 대변을 넘어가는 강력한 연산자를 제공합니다. 이 마스터는 더 유연한 매크로를 만들 수 있습니다.
와 문자열화
연산자는 문자열 리터럴로 매크로 인수를 켭니다. 이것은 디버깅 또는 로깅에 유용합니다.
#define PRINT_VAR(x) printf(#x " = %d\n", x)
int value = 42;
PRINT_VAR(value); // prints: value = 42
]로 붙여진 토큰
연산자는 단일 토큰으로 두 개의 토큰을 구성합니다. 이것은 컴파일 시간에 다이렉트로 구별되는 식별자를 만들 수 있습니다.
#define CREATE_VAR(prefix, index) prefix ## index
int CREATE_VAR(arr, 0) = 10; // becomes int arr0 = 10;
토큰 파킹은 X 매크로에서 널리 사용되고 반복적인 코드 패턴을 생성하는 데 사용됩니다.
대뇌광 (C99)
C99에 소개 된 variadic 매크로는 를 사용하여 다양한 인수를 허용합니다. 이 매크로를 유연한 로깅 및 디버깅 할 수 있습니다.
#define DEBUG_PRINT(fmt, ...) \
fprintf(stderr, "DEBUG: " fmt, __VA_ARGS__)
DEBUG_PRINT("x = %d, y = %d", x, y);
C11 이후, ]를 사용하여 빈 인수로 출현을 처리하고, 패시성을 개선할 수 있습니다.
일반적인 Pitfalls 및 Them을 방지하는 방법
C 개발자가 매크로 관련 버그를 발견하면서도 경험 많은 C 개발자가 있습니다. 이 pitfalls를 인식하고 초기는 두통을 방지합니다.
- 미싱 반콜론 또는 브레이징: 반콜론 없이 블록으로 확장되는 매크로는 구문 오류를 일으킬 수 있습니다. 패턴이 해결됩니다.
- 운영자 선행 깜짝: 항상 부모의 크기-위에 주목.
- 함수와 함께 쉐도잉 매크로: 표준 라이브러리 함수와 같은 이름을 가진 매크로를 정의하면, 침묵적으로 무시할 수 있습니다. 고유의 naming 또는 #undef를 사용하십시오.
- Non-constant Initializers: Object-like macros는 상수로 사용되지만, 타입이 되지 않습니다. 확장자가 일정한 표현이면 일정한 표현에만 사용할 수 있습니다.
- 실행 난이도: 매크로는 컴파일러가 보았기 때문에 오류 메시지가 확장된 코드가 아니라, 매크로 이름을 참조합니다. 플래그를 사용하여 GCC 또는 Clang (예: ])는 사전 처리 된 출력을 추적하는 문제를 보여줍니다.
Macros에 대안: const, enum 및 인라인 함수
현대 C는 종종 매크로에 대한 필요를 삭제하는 더 안전한 대안을 제공합니다:
- ] variables]: 수치 상수의 경우 를 선호합니다. 그들은 적절한 유형 검사 및 존경 범위를 가지고 있습니다. 그러나 모든 상황에 컴파일 시간 상수가 없습니다 (예 : C90의 배열 크기. C99 이상에서, 는 변수가 진정으로 일정한 경우 배열 크기에 사용할 수 있습니다 (예 : ).
- : 관련 정수의 세트를 위해, enums는 유형 안전 이고 자동적으로 값을 할당합니다. 그들은 과실 부호와 깃발을 위해 널리 이용됩니다.
- 인라인 함수: C99에 소개, 기능은 전체 유형의 안전, 디버깅 지원, 부작용 문제의 피임과 매크로의 성능을 제공합니다. 예를 들어:
static inline int square(int x) {
return x * x;
}
함수와 같은 매크로가 필요한 경우 인라인 함수를 사용해서, 즉, 인자를 정확히 한번 평가할 수 있습니다. 함수와 같은 매크로를 위한 유일한 사용 사례는 타입(예: GNU C)에서 동작해야 할 때이며, 새로운 범위(do-while idiom과 같은)를 열 수 있는 능력이 필요합니다.
실제 사례: Debugging 및 구성
상태 시나리오에서 발췌. 여기에 두 가지 실용적인 응용 분야 :
Compile-Time Control을 사용한 디버그 로깅
#ifdef DEBUG
#define LOG(fmt, ...) fprintf(stderr, "[DEBUG] " fmt, ##__VA_ARGS__)
#else
#define LOG(fmt, ...) /* no-op */
#endif
]과 컴파일 할 때만 이 매크로는 0 오버 헤드가 릴리스 빌드에서 발생합니다. ] 앞에 ]는 인수가 제공되지 않을 때 트레일 코타를 제거하는 GNU 확장입니다 (]).
플랫폼 Specific 부호
#if defined(_WIN32) || defined(_WIN64)
#define PLATFORM "Windows"
#elif defined(__linux__)
#define PLATFORM "Linux"
#else
#define PLATFORM "Unknown"
#endif
이 패턴은 전체 기능을 duplicating 없이 플랫폼 의존성을 가집니다. 가드와 결합하여, 그것은 더 깨끗하고 유지를 유지.
관련 기사
매크로는 C에서 코드 재사용성 및 유지 보수에 대한 비유적 도구로 유지되며, 특히 모든 사이클 카운트가 있는 성능 크리티컬 또는 임베디드 컨텍스트에서 특히 유지됩니다. 올바르게 사용되었을 때 적절한 모체, 제한적 부작용 및 적절한 naming-they는 복제 및 중앙화 구성을 감소시킵니다. 그러나 현대 C의 추세는 인라인 함수 또는 일반 (사용 [)과 같은 매크로를 대체하는 것입니다. CLT는 유형의 안전과 안전성을 위해 [FLT]]를 사용합니다. [FLT]].[FLT]]].[FLT]].[F]].
이 더 안전한 대안으로 전통적인 매크로 전력을 균형을 맞추면 C 코드를 작성할 수 있습니다. 더 읽기에 대해서는 GNU C Preprocessor Manual]과 ]]]C 참조를 매크로] 참조 참조를 참조합니다. 상세에 매크로 pitfalls는이 embeddedded.com ]에 있습니다.