Объединение строк макросов 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.