Попытка понять препроцессор C
-
17-09-2020 - |
Вопрос
Почему эти блоки кода дают разные результаты?
Некоторый общий код:
#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)
Дело 1:
#define GLUE(a,b,c) a##b##c
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"
случай 2:
#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"
случай 3:
#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"
Я использую MSVC++ из VS.net 2005 SP1.
Редактировать:в настоящее время я считаю, что препроцессор работает следующим образом при расширении макросов:Шаг 1:- Возьмите тело - удалите любое пробелы вокруг операторов ## - проанализируйте строку, в случае, когда найден идентификатор, который соответствует имени параметра:-если он находится рядом с оператором ##, замените идентификатор буквальным значением параметра (т. е.Строка прошел в) -Если она не находится рядом с оператором ##, запустите весь этот процесс объяснения на значении параметра сначала, а затем замените идентификатор этим результатом.(игнорируя Stringafy Single '#' Case ATM) -Ремоурация All ## Operators
Шаг 2:- возьмите полученную строку и проанализируйте ее на наличие макросов
теперь, исходя из этого, я считаю, что все три случая должны давать одну и ту же результирующую строку:
ЧАСТЬ1ЧАСТЬ2*
и, следовательно, после шага 2 это должно привести к
работает*
но, по крайней мере, должно привести к тому же самому.
Решение
случаи 1 и 2 не имеют определенного поведения, поскольку у вас возникает соблазн вставить *
в один токен препроцессора.Согласно правилам ассоциации вашего препроцессора, он либо пытается склеить токены, PART1PART2
(или просто PART2
) и *
.В вашем случае это, вероятно, не удастся, что является одним из возможных результатов, когда что-то не определено.Токен PART1PART2
с последующим *
в этом случае больше не будет рассматриваться для макрорасширения.Затем строкификация дает результат, который вы видите.
Мой gcc ведет себя по-другому в ваших примерах:
/usr/bin/gcc -O0 -g -std=c89 -pedantic -E test-prepro.c
test-prepro.c:16:1: error: pasting "PART1PART2" and "*" does not give a valid preprocessing token
"works*"
Итак, подводя итог вашему случаю 1, есть две проблемы.
- Вставьте два токена, которые не приводят к действительному жетону препроцессора.
- порядок оценки
##
оператор
В случае 3 ваш компилятор выдает неверный результат.Должно
- оценить аргументы,
STRINGAFY1
- для этого он должен расшириться
GLUE
GLUE
приводит кPART1PART2*
- который необходимо снова расширить
- результат
works*
- который затем передается
STRINGAFY1
Другие советы
Это делает именно то, что вы говорите.Первый и второй возьмите имена символов, переданные и вставьте их вместе в новый символ.Третий занимает 2 символа и вставляет их, вы помещаете * в строке самостоятельно (что в конечном итоге будет оценивать во что-то другое.)
Что именно вопрос с результатами?Что вы ожидали получить?Это все, кажется, работает, как я ожидаю.
Тогда, конечно, вопрос о том, почему вы играете с темным искусством символов, подобных таковым в любом случае?:)