Как именно работает трюк с двойной строкой?

По крайней мере, некоторые препроцессоры C позволяют вам преобразовать значение макроса в строку, а не его имя, передав его через один функционально-подобный макрос другому, который преобразовывает его в строку:

#define STR1(x) #x
#define STR2(x) STR1(x)
#define THE_ANSWER 42
#define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */

Примеры использования здесь.

Это работает, по крайней мере, в GCC и Clang (оба с -std=c99), но я не уверен, как это работает в терминах стандарта C.

Гарантируется ли такое поведение C99?
Если да, то как это гарантирует C99?
Если нет, то в какой момент поведение переходит от C-определенного к GCC?


person Peter Hosey    schedule 01.05.2010    source источник
comment
Если вы скажете хоть что-то, значит ли это, что вы видели один, где он не работает? Я готов написать поставщику отчет об ошибке.   -  person Jens    schedule 21.05.2011
comment
@Jens: Нет; Я не знал. Каждый компилятор, который я использовал (а именно, GCC и Clang), реализует это поведение.   -  person Peter Hosey    schedule 21.05.2011


Ответы (2)


Да, это гарантировано.

Это работает, потому что аргументы макроса сами раскрываются макросом, кроме, где имя аргумента макроса появляется в теле макроса с указателем строки # или лексем-пастером ##.

6.10.3.1/1:

... После того, как аргументы для вызова функционально-подобного макроса были идентифицированы, происходит подстановка аргументов. Параметр в списке замены, если ему не предшествует токен предварительной обработки # или ## или не следует токен предварительной обработки ## (см. Ниже), заменяется соответствующим аргументом после того, как все содержащиеся в нем макросы были раскрыты ...

Итак, если вы сделаете STR1(THE_ANSWER), вы получите «THE_ANSWER», потому что аргумент STR1 не раскрывается макросом. Однако аргумент STR2 раскрывается макросом, когда он подставляется в определение STR2, что, следовательно, дает STR1 аргумент 42 с результатом "42".

person Steve Jessop    schedule 01.05.2010

Как отмечает Стив, это гарантировано, и это гарантируется со времен стандарта C89 - это был стандарт, кодифицировавший операторы # и ## в макросах и рекурсивно предписывающий раскрытие макросов в args перед их заменой в тело, если и только если тело не применяет # или ## к аргументу. В этом отношении C99 не отличается от C89.

person Chris Dodd    schedule 01.05.2010
comment
+1 за то, что нашел время, чтобы подтвердить, что он также является частью C89. Некоторые из нас заботятся о переносимости, в том числе о создании кода, который люди, компилирующие в режиме C89, могут использовать - всего несколько лет назад для выпусков gcc по умолчанию использовался C89 (плюс расширения gcc, если честно), MSVC, о котором я слышал, по-прежнему поддерживает только C89 и встроенные или устаревшие системы также иногда имеют только компиляторы C89. C89 обеспечивает отличную переносимость первого этажа, стандарт наименьшего общего знаменателя, который люди могут нацелить на компиляцию и запуск для всего, что сегодня используется на практике. Так что очень приятно видеть, что это вспоминают. - person mtraceur; 26.01.2020