Объединение строк макросов C / C ++


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

Можно ли объединить STR3 == "s1"? Вы можете сделать это, передав аргументы другой функции макроса. Но есть ли прямой путь?



Этого не должно быть, потому что это определяет STR3 как токен предварительной обработки STR1STR2. И передача аргументов в другую макро-функцию не помогает, потому что строковые литералы не могут быть вставлены вместе - «s» «1» не является допустимым токеном.
Джим Балтер

Ответы:


157

Если это обе строки, вы можете просто сделать:

#define STR3 STR1 STR2

Препроцессор автоматически объединяет соседние строки.

РЕДАКТИРОВАТЬ:

Как отмечено ниже, конкатенацию выполняет не препроцессор, а компилятор.


17
Технически конкатенация строк выполняется на уровне языка.
Мартин Йорк

47
Препроцессор такого не делает. Это собственно язык C, который обрабатывает смежные строковые литералы, как если бы они были одним строковым литералом.
Джим Балтер,

7
Это больше, чем формальность - вы не можете объединить L"a"и "b"получить L"ab", но можете объединить L"a"и L"b"получить L"ab".
MSalters

115

Вам не нужно такое решение для строковых литералов, поскольку они объединяются на уровне языка, и это все равно не сработает, потому что «s» «1» не является допустимым токеном препроцессора.

[Изменить: в ответ на некорректный комментарий «Только для записи» ниже, который, к сожалению, получил несколько голосов, я повторю приведенное выше утверждение и замечу, что фрагмент программы

#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, если sне определен как макрос, и в этом случае PPCAT(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- это правильно; это, как вы говорите, два жетона. Но объединение их вместе с помощью ## сделало бы их одним токеном предварительной обработки, а не двумя токенами, и поэтому компилятор не будет выполнять конкатенацию, а лексер отклонит их (язык требует диагностики).
Джим Балтер,

8
@ mr5 Внимательно прочтите комментарии. Имена макросов, переданные в качестве аргументов макроса, не раскрываются перед передачей. Однако они раскрываются в теле макроса. Итак, если A определен как FRED, STRINGIZE_NX (A) расширяется до «A», но STRINGIZE (A) расширяется до STRINGIZE_NX (FRED), который расширяется до «FRED».
Джим Балтер

1
@bharath результирующая строка будет "PPCAT (T1, T2)" - как и ожидалось и желательно. а не ожидаемый "s1" - совсем не ожидался. Зачем нам нужно дополнительное косвенное обращение / вложение? - Прочтите комментарии к коду и мой комментарий выше с 6 голосами "за". Раскрываются только тела макросов; вне тела макроса аргументы макроса в круглых скобках не раскрываются перед передачей макросу . Таким образом, STRINGIZE_NX(whatever occurs here)расширяется до «все, что здесь происходит», независимо от любых макроопределений для того, что происходит или здесь.
Джим Балтер

1
@bharath Конечно, он не печатает «Имя A» - A - это имя параметра, а не аргумент макроса, которым является ALEX. Вы заявили if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- это ложь и не имеет ничего общего с вашим тестом. Вы изо всех сил пытаетесь не понять или не понять это правильно, и я не собираюсь вам больше отвечать.
Джим Балтер

24

Подсказка: STRINGIZEприведенный выше макрос хорош, но если вы допустили ошибку и его аргумент не является макросом - у вас есть опечатка в имени или вы забыли #includeфайл заголовка - тогда компилятор с радостью поместит предполагаемое имя макроса в строка без ошибок.

Если вы хотите, чтобы аргумент STRINGIZEвсегда был макросом с нормальным значением C, тогда

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

расширит его один раз и проверит его на предмет действительности, отбросит его, а затем снова расширит в строку.

Мне потребовалось некоторое время, чтобы понять, почему в STRINGIZE(ENOENT)итоге "ENOENT"вместо "2"... я не включил errno.h.


2
Важное замечание и +1 за правильное использование ,оператора. :)
Джесси Чизхолм

2
Нет особой причины, по которой содержимое строки должно быть допустимым выражением C. Если вы хотите это сделать, я советую дать ему другое имя, например STRINGIZE_EXPR.
Джим Балтер

Этот трюк мог сработать изолированно. Но это не позволяет компилятору увидеть последовательность строк, которые он будет объединять. (в результате получаются такие последовательности, как ((1),"1") "." ((2),"2")«1». «« 2 »)
автоморфный

Просто чтобы прояснить, что говорит автоморф: с исходным STRINGIZEопределением "The value of ENOENT is " STRINGIZE(ENOENT)работает, тогда как "The value of ENOENT is" STRINGIZE_EXPR(X)выдает ошибку.
Джим Балтер,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.