Зв'язування макросмужкових рядків C / C ++


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

Чи можливо об'єднати STR3 == "s1"? Це можна зробити, передавши аргументи в іншу функцію Макрос. Але чи є прямий шлях?


Чи не повинно бути #define STR3 STR1 ## STR2
Shrinidhi

Це не повинно бути, тому що це визначає, що STR3 є маркером попередньої обробки STR1STR2. І передача аргументів до іншої функції макросу не допомагає, тому що рядкові літерали не можна вставити разом - "s" "1" не є дійсним маркером.
Джим Балтер

Відповіді:


157

Якщо вони обидва рядки, ви можете просто зробити:

#define STR3 STR1 STR2

Препроцесор автоматично об'єднує суміжні рядки.

Редагувати:

Як зазначено нижче, конкатенацію робить не препроцесор, а компілятор.


17
Технічно рядкове конкатенація проводиться на мовному рівні.
Мартін Йорк

47
Препроцесор такого не робить. Саме мова С розглядає суміжні літеральні рядки так, ніби вони є однорядними літералами.
Джим Балтер

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: error: вставлення "" 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 ++). Вони є двома лексемами (рядковими буквами), які компілятор сформулював би себе та загрозу як один маркер.
Шахбаз

4
Ви неправильно розумієте і мій коментар, і мову С. Я сказав "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 - це ім'я параметра, а не аргумент макросу, який 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.