Replacing text macros
전처리기는 텍스트 매크로 치환과 함수형 텍스트 매크로 치환을 지원합니다.
목차 |
구문
#define
identifier
replacement-list
(optional)
|
(1) | ||||||||
#define
identifier
(
parameters
)
replacement-list
|
(2) | ||||||||
#define
identifier
(
parameters
, ... )
replacement-list
|
(3) | (C99부터) | |||||||
#define
identifier
( ... )
replacement-list
|
(4) | (C99부터) | |||||||
#undef
identifier
|
(5) | ||||||||
설명
#define 지시자
#define
지시문은
식별자(identifier)
를 매크로로 정의합니다. 즉, 컴파일러에게
식별자(identifier)
의 모든 연속적인 발생을
대체 리스트(replacement-list)
로 대체하도록 지시하며, 이는 선택적으로 추가 처리가 가능합니다. 만약 식별자가 이미 어떤 유형의 매크로로 정의되어 있다면, 정의가 동일하지 않는 한 프로그램은 잘못된 형식입니다.
객체형 매크로
객체형 매크로는 정의된
identifier
의 모든 발생을
replacement-list
로 대체합니다.
#define
지시자의 버전 (1)은 정확히 이러한 방식으로 동작합니다.
함수형 매크로
함수형 매크로는 정의된 identifier 의 각 발생을 replacement-list 로 대체하며, 추가적으로 여러 인수를 취하여 replacement-list 내의 parameters 중 해당하는 발생을 대체합니다.
함수형 매크로 호출의 구문은 함수 호출의 구문과 유사합니다: 매크로 이름 다음에 ( 토큰이 바로 뒤따르는 각 인스턴스는 교체 목록(replacement-list)으로 대체되는 토큰 시퀀스를 시작합니다. 이 시퀀스는 일치하는 ) 토큰으로 종료되며, 중간에 있는 일치하는 왼쪽 및 오른쪽 괄호 쌍은 건너뜁니다.
인수의 개수는 매크로 정의( parameters )에 있는 인수 개수와 동일해야 하며, 그렇지 않으면 프로그램이 잘못 형성됩니다. 식별자가 함수 표기법이 아닌 경우, 즉 식별자 뒤에 괄호가 없으면 전혀 치환되지 않습니다.
(2)번 버전의
#define
지시자는 단순한 함수형 매크로를 정의합니다.
#define 지시문의 버전 (3)은 가변 인자를 갖는 함수형 매크로를 정의합니다. 추가 인자들은 __VA_ARGS__ 식별자를 사용하여 접근할 수 있으며, 이 식별자는 교체 대상 식별자와 함께 제공된 인자들로 대체됩니다.
#define 지시문의 버전 (4)는 일반 인수 없이 가변 인수를 갖는 함수형 매크로를 정의합니다. 인수는 __VA_ARGS__ 식별자로만 접근할 수 있으며, 이는 이후 대체될 식별자와 함께 제공된 인수들로 대체됩니다.
|
버전 (3,4)의 경우,
replacement-list
는 토큰 시퀀스
#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) F(a, b, c) // replaced by f(0, a, b, c) F() // replaced by f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c) // replaced by f(0, a, b, c) G(a, ) // replaced by f(0, a) G(a) // replaced by f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo); // replaced by S foo; SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; |
(C23부터) |
참고: 함수형 매크로의 인수에 일치하는 왼쪽 및 오른쪽 괄호 쌍으로 보호되지 않은 쉼표(예:
macro
(
array
[
x
=
y, x
+
1
]
)
또는
atomic_store
(
p,
(
struct
S
)
{
a, b
}
)
;
)가 포함된 경우, 해당 쉼표는 매크로 인수 구분자로 해석되어 인수 개수 불일치로 인한 컴파일 오류가 발생합니다.
# 및 ## 연산자
함수형 매크로에서,
#
연산자가
replacement-list
내의 식별자 앞에 위치하면, 해당 식별자를 매개변수 치환 과정에 통과시킨 후 결과를 따옴표로 감싸 문자열 리터럴을 효과적으로 생성합니다. 또한 전처리기는 포함된 문자열 리터럴 주변의 따옴표를 이스케이프하기 위해 백슬래시를 추가하고, 필요한 경우 문자열 내부의 백슬래시를 두 배로 늘립니다. 모든 선행 및 후행 공백은 제거되며, 텍스트 중간의 공백 시퀀스(그러나 포함된 문자열 리터럴 내부는 제외)는 단일 공백으로 축소됩니다. 이 연산을 "문자열화(stringification)"라고 합니다. 문자열화의 결과가 유효한 문자열 리터럴이 아닌 경우, 그 동작은 정의되지 않습니다.
|
#define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(C99부터) |
replacement-list
내에서 연속된 두 식별자 사이에 있는
##
연산자는 두 식별자에 대해 매개변수 치환을 수행한 후 결과를 연결합니다. 이 연산을 "토큰 접합(token pasting)" 또는 "연결(concatenation)"이라고 합니다. 함께 유효한 토큰을 형성하는 토큰만 접합할 수 있습니다: 더 긴 식별자를 형성하는 식별자, 숫자를 형성하는 숫자, 또는
+
와
=
를 접합하여
+=
를 형성하는 경우 등입니다.
/
와
*
를 접합하여 주석을 생성할 수는 없습니다. 왜냐하면 주석은 매크로 치환이 고려되기 전에 텍스트에서 제거되기 때문입니다. 접합 결과가 유효한 토큰이 아닌 경우, 그 동작은 정의되지 않습니다.
참고: 일부 컴파일러는 쉼표 다음과
__VA_ARGS__
앞에
##
가 나타나는 확장 기능을 제공하는데, 이 경우
__VA_ARGS__
가 비어 있지 않을 때는
##
가 아무 작업도 수행하지 않지만,
__VA_ARGS__
가 비어 있을 때는 쉼표를 제거합니다: 이를 통해
fprintf
(
stderr
, format,
##__VA_ARGS__)
와 같은 매크로를 정의할 수 있습니다.
#
및
##
연산자의 평가 순서는 명시되지 않았습니다.
#undef 지시자
#undef
지시자는
identifier
를 정의 해제합니다. 즉,
#define
지시자에 의한
identifier
의 이전 정의를 취소합니다. identifier에 연결된 매크로가 없는 경우, 해당 지시자는 무시됩니다.
사전 정의된 매크로
다음 매크로 이름들은 모든 번역 단위에서 미리 정의되어 있습니다:
|
__STDC__
|
정수 상수
1
로 확장됩니다. 이 매크로는 준수 구현을 나타내기 위한 것입니다.
(매크로 상수) |
|
__STDC_VERSION__
(C95)
|
long
타입의 정수 상수로 확장되며, 그 값은 C 표준의 각 버전마다 증가합니다:
|
|
__STDC_HOSTED__
(C99)
|
구현이 호스팅된 경우(OS에서 실행) 정수 상수
1
으로, 독립 실행형인 경우(OS 없이 실행)
0
으로 확장됩니다.
(매크로 상수) |
|
__FILE__
|
현재 파일 이름을 문자 문자열 리터럴로 확장되며,
#line
지시문으로 변경할 수 있습니다.
(매크로 상수) |
|
__LINE__
|
소스 파일의 행 번호를 정수 상수로 확장되며,
#line
지시문으로 변경할 수 있습니다.
(매크로 상수) |
|
__DATE__
|
변환 날짜를 "Mmm dd yyyy" 형태의 문자 문자열 리터럴로 확장됩니다. 월 이름은
asctime
에 의해 생성된 것과 같으며, "dd"의 첫 번째 문자는 일(day)이 10보다 작을 경우 공백입니다.
(매크로 상수) |
|
__TIME__
|
변환 시간을 "hh:mm:ss" 형태의 문자 문자열 리터럴로 확장되며,
asctime
(
)
에 의해 생성된 시간과 같습니다.
(매크로 상수) |
|
__STDC_UTF_16__
(C23)
|
1
으로 확장되어
char16_t
가 UTF-16 인코딩을 사용함을 나타냅니다.
(매크로 상수) |
|
__STDC_UTF_32__
(C23)
|
1
으로 확장되어
char32_t
가 UTF-32 인코딩을 사용함을 나타냅니다.
(매크로 상수) |
|
__STDC_EMBED_NOT_FOUND__
__STDC_EMBED_FOUND__
__STDC_EMBED_EMPTY__
(C23)
|
각각
0
,
1
, 그리고
2
로 확장됩니다.
(매크로 상수) |
다음 추가 매크로 이름들은 구현에 의해 미리 정의될 수 있습니다:
|
__STDC_ISO_10646__
(C99)
|
yyyymmL
형식의 정수 상수로 확장되며,
wchar_t
가 유니코드를 사용하는 경우에 해당함; 해당 날짜는 지원되는 유니코드의 최신 개정판을 나타냄
(매크로 상수) |
|
__STDC_IEC_559__
(C99)
(C23에서 사용 중단됨)
|
IEC 60559를 지원할 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_IEC_559_COMPLEX__
(C99)
(C23에서 사용 중단됨)
|
IEC 60559 복소수 연산이 지원되는 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_UTF_16__
(C11)
|
1
로 확장됨
만약 char16_t 가 UTF-16 인코딩을 사용하는 경우 (매크로 상수) |
|
__STDC_UTF_32__
(C11)
|
UTF-32 인코딩을 사용하는
char32_t
인 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_MB_MIGHT_NEQ_WC__
(C99)
|
1
로 확장되며, 기본 문자 집합의 구성원에 대해
'x'
==
L
'x'
가 거짓일 수 있는 경우(예: Unicode를
wchar_t
로 사용하는 EBCDIC 기반 시스템에서)를 나타냅니다
(매크로 상수) |
|
__STDC_ANALYZABLE__
(C11)
|
분석 가능성이 지원되는 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_LIB_EXT1__
(C11)
|
정수 상수
201112L
로 확장됨
( bounds-checking interfaces 가 지원되는 경우) (매크로 상수) |
|
__STDC_NO_ATOMICS__
(C11)
|
1
로 확장되며,
atomic
타입과
atomic operations library
가 지원되지 않는 경우를 나타냅니다
(매크로 상수) |
|
__STDC_NO_COMPLEX__
(C11)
|
복소수 타입
및
복소수 수학 라이브러리
가 지원되지 않는 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_NO_THREADS__
(C11)
|
멀티스레딩
을 지원하지 않는 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_NO_VLA__
(C11)
|
가변 길이 배열
및 가변 수정 타입
(C23까지)
(자동 저장 기간)
(C23부터)
이 지원되지 않을 경우
1
로 확장됨
(매크로 상수) |
|
__STDC_IEC_60559_BFP__
(C23)
|
IEC 60559 이진 부동 소수점 연산이 지원되는 경우
202311L
로 확장됨
(매크로 상수) |
|
__STDC_IEC_60559_DFP__
(C23)
|
IEC 60559 십진 부동 소수점 연산이 지원되는 경우
202311L
로 확장됨
(매크로 상수) |
|
__STDC_IEC_60559_COMPLEX__
(C23)
|
IEC 60559 복소수 연산이 지원되는 경우
202311L
로 확장됨
(매크로 상수) |
|
__STDC_IEC_60559_TYPES__
(C23)
|
IEC 60559 교환 및 확장 타입이 지원되는 경우
202311L
로 확장됨
(매크로 상수) |
이러한 매크로의 값들(
__FILE__
과
__LINE__
제외)은 번역 단위 전체에서 일정하게 유지됩니다. 이러한 매크로를 재정의하거나 정의 해제하려는 시도는 정의되지 않은 동작을 초래합니다.
|
미리 정의된 변수
__func__
(자세한 내용은
함수 정의
참조)는 때때로
|
(C99부터) |
예제
#include <stdio.h> // 함수 팩토리를 만들고 사용 #define FUNCTION(name, a) int fun_##name(int x) { return (a) * x; } FUNCTION(quadruple, 4) FUNCTION(double, 2) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) puts( #a ) int main(void) { printf("quadruple(13): %d\n", fun_quadruple(13)); printf("double(21): %d\n", fun_double(21)); printf("%d\n", FUNCTION); OUTPUT(billion); // 따옴표가 없음에 주목 }
출력:
quadruple(13): 52 double(21): 42 34 billion
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| DR 321 | C99 | 기본 문자 집합에서 L 'x' == 'x' 가 항상 성립하는지 불명확했음 | 해당 목적으로 __STDC_MB_MIGHT_NEQ_WC__ 추가됨 |
참고문헌
- C23 표준 (ISO/IEC 9899:2024):
-
- 6.10.4 매크로 치환 (p: 187-184)
-
- 6.10.9 미리 정의된 매크로 이름 (p: 186-188)
- C17 표준 (ISO/IEC 9899:2018):
-
- 6.10.3 매크로 치환 (p: 121-126)
-
- 6.10.8 미리 정의된 매크로 이름 (p: 127-129)
- C11 표준 (ISO/IEC 9899:2011):
-
- 6.10.3 매크로 치환 (p: 166-173)
-
- 6.10.8 미리 정의된 매크로 이름 (p: 175-176)
- C99 표준 (ISO/IEC 9899:1999):
-
- 6.10.3 매크로 치환 (p: 151-158)
-
- 6.10.8 미리 정의된 매크로 이름 (p: 160-161)
- C89/C90 표준 (ISO/IEC 9899:1990):
-
- 3.8.3 매크로 치환
-
- 3.8.8 사전 정의된 매크로 이름
참고 항목
|
C++ 문서
에 대한
텍스트 매크로 교체
|