Почему эти блоки кода приводят к различным результатам?
Некоторый общий код:
#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 2 005 sp1
Править: это в настоящее время - моя вера, что препроцессор работает как это при расширении макросов: Шаг 1: - берут тело - удаляют любой пробел вокруг ## операторов - анализируют строку, в случае, которым идентификатор найден, который соответствует названию параметра: - если это рядом с ## оператором, замените идентификатор литеральным значением параметра (т.е. строка передала в) - если это не рядом с ## оператором, выполняет этот целый процесс объяснения на значении параметра сначала, затем заменяет идентификатор тем результатом. (игнорирование stringafy единственного '#' банкомат случая) - удаляют все ## операторы
Шаг 2: - берут ту результирующую строку и анализируют его для любых макросов
теперь, от этого я полагаю, что все 3 случая должны произвести ту же самую результирующую строку:
PART1PART2*
и следовательно после шага 2, должен привести к
работы*
но самое малое должен привести к тому же самому.
, случаи 1 и 2 не имеют определенного поведения, так как вы пытаетесь вставить *
в один токен препроцессора. Согласно правилам ассоциации вашего препроцессора, он либо пытается склеить токены PART1PART2
(или просто PART2
) и *
. В вашем случае это, вероятно, не работает тихо, что является одним из возможных результатов, когда что-то не определено. Маркер PART1PART2
, за которым следует *
, не будет снова рассматриваться для расширения макроса. Затем Stringfication дает результат, который вы видите.
Мой 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 *
работает *
STRINGAFY1
Он делает именно то, что вы ему говорите. Первый и второй берут переданные имена символов и вставляют их вместе в новый символ. Третий берет 2 символа и вставляет их, затем вы сами помещаете * в строку (что в конечном итоге приведет к чему-то другому).
В чем именно заключается вопрос с результатами? Что ты ожидал получить? Кажется, все работает так, как я ожидал.
Тогда, конечно, возникает вопрос, почему вы вообще так играете с темным искусством манипулирования символами? :)