C / C ++ 매크로 문자열 연결


121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

STR3 == "s1"을 연결하는 것이 가능합니까? args를 다른 매크로 함수에 전달하여이를 수행 할 수 있습니다. 그러나 직접적인 방법이 있습니까?


그것은 #DEFINE STR3 STR1 ## STR2 안
Shrinidhi

그것은 STR3을 전처리 토큰 STR1STR2로 정의하기 때문입니다. 인수를 다른 매크로 함수에 전달하는 것은 도움이되지 않습니다. 문자열 리터럴을 함께 붙여 넣을 수 없기 때문입니다. "s" "1"은 유효한 토큰이 아닙니다.
Jim Balter 2011 년

답변:


157

둘 다 문자열이면 다음과 같이 할 수 있습니다.

#define STR3 STR1 STR2

전처리 기는 인접한 문자열을 자동으로 연결합니다.

편집하다:

아래에서 언급했듯이 연결을 수행하는 것은 전처리 기가 아니라 컴파일러입니다.


17
기술적으로 문자열 연결은 언어 수준에서 수행됩니다.
Martin York

47
전처리 기는 그런 일을하지 않습니다. 인접한 문자열 리터럴을 단일 문자열 리터럴 인 것처럼 처리하는 것은 C 언어 고유입니다.
Jim Balter 2011 년

7
그것은 더 전문적 이상 - 당신은 합칠 수 없습니다 L"a""b"얻을에 L"ab",하지만 당신은 할 수 있습니다 연결할 L"a"L"b"수에 L"ab".
MSalters 2011 년

115

문자열 리터럴은 언어 수준에서 연결되기 때문에 이러한 종류의 솔루션이 필요하지 않으며 "s" "1"이 유효한 전 처리기 토큰이 아니기 때문에 어쨌든 작동하지 않습니다.

[편집 : 안타깝게도 여러 개의 찬성표를받은 아래의 잘못된 "Just for the record"주석에 대한 응답으로 위의 설명을 반복하고 프로그램 조각이

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

gcc의 전처리 단계에서 다음 오류 메시지를 생성합니다. 오류 : ""s ""및 ""1 ""붙여 넣기는 유효한 전처리 토큰을 제공하지 않습니다.

]

그러나 일반적인 토큰 붙여 넣기의 경우 다음을 시도하십시오.

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

그런 다음 예를 들어 모두 PPCAT_NX(s, 1)PPCAT(s, 1)식별자를 생성합니다 . 이 경우 매크로로 정의 s1되지 않는 한 .sPPCAT(s, 1)<macro value of s>1

주제에서 계속되는 것은 다음 매크로입니다.

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

그때,

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

대조적으로

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"

8
레코드의 경우 "s""1"C (및 C ++)에서 유효합니다. 컴파일러가 자신을 연결하고 하나의 토큰으로 위협하는 두 개의 토큰 (문자열 리터럴)입니다.
Shahbaz

4
내 의견과 C 언어를 모두 오해합니다. 나는 말했다 "s""1" isn't a valid token– 그것은 맞다; 말했듯이 두 개의 토큰입니다. 그러나 ##과 함께 붙이면 두 개의 토큰이 아닌 단일 전처리 토큰이되므로 컴파일러는 연결을 수행하지 않고 어휘 분석기가이를 거부합니다 (언어에는 진단이 필요함).
Jim Balter

8
@ mr5 주석을주의 깊게 읽으십시오. 매크로 인수로 전달 된 매크로 이름은 전달되기 전에 확장되지 않습니다. 그러나 매크로 본문에서는 확장됩니다. 따라서 A가 FRED로 정의되면 STRINGIZE_NX (A)는 "A"로 확장되지만 STRINGIZE (A)는 "FRED"로 확장되는 STRINGIZE_NX (FRED)로 확장됩니다.
Jim Balter 2014

1
@bharath 결과 문자열은 예상하고 원하는대로 "PPCAT (T1, T2)" 입니다. 예상되는 "s1"이 아닙니다. 전혀 예상되지 않습니다. 추가 간접 / 중첩이 필요한 이유는 무엇입니까? -코드 댓글과 위의 내 댓글을 6 개의 찬성표로 읽어보세요. 매크로의 본문 만 확장됩니다. 매크로 본문 외부에서 괄호 사이의 매크로 인수는 매크로 로 전달되기 전에 확장 되지 않습니다 . 따라서 STRINGIZE_NX(whatever occurs here)발생, 발생 또는 여기에 대한 매크로 정의에 관계없이 "여기서 발생하는 모든 항목"으로 확장됩니다.
Jim Balter

1
@bharath 물론 "이름 A"를 인쇄하지 않습니다. A는 ALEX 인 매크로에 대한 인수가 아니라 매개 변수 이름입니다. 당신이 주장한 if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"것은 거짓이며 당신의 시험과는 전혀 다릅니다. 당신은 이것을 이해하지 못하거나 옳게하기 위해 열심히 노력하고 있습니다.
Jim Balter

24

힌트 : STRINGIZE위 의 매크로는 멋지지만 실수하고 인수가 매크로가 아닌 경우 (이름에 오타가 있거나 #include헤더 파일을 잊어 버린 경우) 컴파일러가 의도 한 매크로 이름을 오류가없는 문자열.

인수 STRINGIZE가 항상 정상적인 C 값을 가진 매크로가 되도록하려면

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

한 번 확장하고 유효성을 확인하고 폐기 한 다음 다시 문자열로 확장합니다.

대신 ... 내가 포함하지 않은 STRINGIZE(ENOENT)것처럼 끝나는 이유 를 알아내는 데 시간이 좀 걸렸 습니다 ."ENOENT""2"errno.h


2
중요한 관찰 및 ,운영자의 적절한 사용을 위해 +1 . :)
Jesse Chisholm

2
문자열의 내용이 유효한 C 표현식이어야하는 특별한 이유는 없습니다. 이렇게하려면 STRINGIZE_EXPR과 같은 다른 이름을 지정하는 것이 좋습니다.
Jim Balter 2018 년

그 속임수는 분리되어 작동했을 수도 있습니다. 그러나 컴파일러가 연결할 문자열 시퀀스를 보지 못하게합니다. (결과적으로 ((1),"1") "." ((2),"2")"1" "." "2"대신에 같은 시퀀스로
나타남

automorphic이 말하는 것을 명확히하기 위해 : 원래 STRINGIZE정의에서는 "The value of ENOENT is " STRINGIZE(ENOENT)작동하지만 "The value of ENOENT is" STRINGIZE_EXPR(X)오류가 발생합니다.
Jim Balter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.