Пытаясь понять препроцессор C - PullRequest
6 голосов
/ 19 июля 2010

Почему эти блоки кода дают разные результаты?

Какой-то общий код:

#define PART1PART2 works
#define STRINGAFY0(s) #s
#define STRINGAFY1(s) STRINGAFY0(s)

case 1:

#define GLUE(a,b,c) a##b##c  
STRINGAFY1(GLUE(PART1,PART2,*))
//yields
"PART1PART2*"

case 2:

#define GLUE(a,b) a##b##*
STRINGAFY1(GLUE(PART1,PART2))
//yields
"works*"

case 3:

#define GLUE(a,b) a##b
STRINGAFY1(GLUE(PART1,PART2*))
//yields
"PART1PART2*"

Я использую MSVC ++ из VS.net 2005 sp1

Редактировать: в настоящее время я считаю, что препроцессор работает так же при расширении макросов: Шаг 1: - взять тело - удалить все пробелы вокруг операторов ## - проанализировать строку, если найден идентификатор, соответствующий имени параметра: - если он находится рядом с оператором ##, заменитьИдентификатор с буквальным значением параметра (т. е. передаваемой строкой) - если он НЕ находится рядом с оператором ##, сначала запустите весь процесс объяснения значения параметра, а затем замените идентификатор на этот результат.(игнорируя строковый одиночный регистр '#') - удалите все операторы ##

Шаг 2: - возьмите эту результирующую строку и проанализируйте ее для любых макросов

сейчас, поскольку я считаю, чтово всех трех случаях должна получиться одна и та же результирующая строка:

PART1PART2 *

и, следовательно, после шага 2, должно привести к

работам *

, нопо крайней мере, должно привести к тому же.

Ответы [ 2 ]

3 голосов
/ 19 июля 2010

случаи 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,ваш компилятор дает неверный результат.Он должен

  1. вычислить аргументы в STRINGAFY1
  2. , чтобы сделать это для расширения GLUE
  3. GLUE приводит к PART1PART2*
  4. , который должен быть расширен снова
  5. результат равен works*
  6. , который затем передается в STRINGAFY1
1 голос
/ 19 июля 2010

Он делает именно то, что вы говорите. Первый и второй берут имена символов, переданные внутрь, и вставляют их вместе в новый символ. Третий берет 2 символа и вставляет их, затем вы помещаете * в строку самостоятельно (что в конечном итоге приведет к чему-то другому).

В чем конкретно вопрос с результатами? Что вы ожидали получить? Кажется, все работает так, как я ожидал.

Тогда, конечно, вопрос в том, почему вы играете с темным искусством символизирующих, как это? :)

...