Namespaces
Variants

Replacing text macros

From cppreference.net

전처리기는 텍스트 매크로 치환과 함수형 텍스트 매크로 치환을 지원합니다.

목차

구문

#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 는 토큰 시퀀스 __VA_OPT__ ( content ) 를 포함할 수 있으며, 이는 __VA_ARGS__ 가 비어 있지 않으면 content 로 대체되고, 그렇지 않으면 아무것도 확장되지 않습니다.

#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)"라고 합니다. 문자열화의 결과가 유효한 문자열 리터럴이 아닌 경우, 그 동작은 정의되지 않습니다.

# __VA_ARGS__ 앞에 나타날 때, 전체 확장된 __VA_ARGS__ 가 따옴표로 둘러싸입니다:

#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 표준의 각 버전마다 증가합니다:
  • 199409L (C95)
  • 199901L (C99)
  • 201112L (C11)
  • 201710L (C17)
  • 202311L (C23)
    (매크로 상수)
__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__ (자세한 내용은 함수 정의 참조)는 때때로 __FILE__ __LINE__ 와 함께 사용되지만(예: assert ) 전처리기 매크로가 아닙니다.

(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++ 문서 에 대한 텍스트 매크로 교체