Quick Reference
함수형 macro는 호출이 아니라 텍스트 치환입니다. 계산·비교는 static inline으로 두고, #·##·조건부 컴파일처럼 전처리에서만 가능한 작업에만 macro를 남깁니다.
#define MAX(a, b) ((a) > (b) ? (a) : (b))
/* MAX(i++, j++) 는 위험 */
static inline int max_int(int a, int b) {
return a > b ? a : b;
}
#define LOG_ERROR(msg) do { \
fprintf(stderr, "error: %s\n", (msg)); \
} while (0)MAX(i++, j++)는 한 인자를 두 번 평가할 수 있으므로 함수로 바꿉니다. 여러 문장을 담아야 하는 macro는 do { ... } while (0)로 한 문장처럼 만들고, 문자열화·토큰 결합처럼 전처리에서만 가능한 경우에만 macro를 남깁니다.
문법
함수형 매크로는 함수 호출이 아니라 텍스트 치환입니다.
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int i = 1;
int j = 2;
int m = MAX(i++, j++);이 코드는 결국 아래처럼 여러 번 평가될 수 있습니다.
((i++) > (j++) ? (i++) : (j++))즉 값 자체보다 "몇 번 평가되는가"가 문제입니다.
선택 기준
계산 로직이면 대부분 static inline 쪽이 더 낫습니다.
static inline int max_int(int a, int b) {
return a > b ? a : b;
}이쪽이 더 나은 이유는:
- 타입 검사 가능
- 인자 한 번 평가
- 디버거에서 읽기 쉬움
여러 문장을 감싼 macro가 꼭 필요하면 do { ... } while (0) 형태로 한 문장처럼 만듭니다. 그렇지 않으면 if (cond) MACRO(); else ...에서 else가 엉뚱하게 결합될 수 있습니다.
#define LOG_ERROR(msg) do { \
fprintf(stderr, "%s\n", (msg)); \
} while (0)그렇더라도 macro는 타입 검사·지역 scope·return/break 같은 control flow를 함수처럼 제공하지 않습니다. #, ##, 조건부 컴파일처럼 전처리 단계에만 가능한 일이 아니라면 함수를 우선합니다.
빠른 점검
- 계산 로직이면
static inline이 먼저입니다. - 매크로 인자에
i++, 함수 호출, 대입식 같은 부작용 표현식은 피합니다. - 괄호는 기본이지만 중복 평가 문제까지 해결해 주지는 않습니다.
- 조건부 컴파일,
#,##같은 전처리 전용 기능일 때 매크로가 남을 이유가 큽니다.
주의할 점
매크로는 타입이 다른 인자 조합도 조용히 받아들일 수 있어서, "잘 컴파일되는데 이상하게 동작하는" 버그를 만들기 쉽습니다. 계산이나 비교 로직을 매크로로 두는 건 편해 보여도 유지보수 비용이 큽니다.
참고 링크
3 sources