Почему этот C или C++ являются макросом, не расширенным препроцессором?

Может кто-то указывать на меня проблема в коде при компиляции с gcc 4.1.0.

#define X 10
int main()
{
  double a = 1e-X;
  return 0;
}

Я добираюсь, error:Exponent не имеет никаких цифр.

Когда я заменяю X 10, это хорошо работает. Также я сверился с g ++-E команда для наблюдения файла с примененными препроцессорами, это не заменило X 10. У меня создалось впечатление, что препроцессор заменяет каждый макрос, определенный в файле с текстом замены с применением любой аналитики.Я неправ?

Я знаю, что это - действительно глупый вопрос, но я смущен, и я был бы глуп, чем смущен :).

Какие-либо комментарии/предложения?

31
задан Suraj Jain 5 February 2017 в 22:46
поделиться

4 ответа

Когда вы пишете 1e-X вот так, X не является отдельным символом для замены препроцессором - с обеих сторон должны быть пробелы (или некоторые другие символы). Подумайте об этом немного, и вы поймете, почему... :)

Edit: "12-X" валиден, потому что он разбирается как "12", "-", "X", которые являются тремя отдельными лексемами. "1e-X" не может быть разделен таким образом, потому что "1e-" сам по себе не образует правильную лексему, как упомянул Джонатан в своем ответе.

Что касается решения вашей проблемы, вы можете использовать конкатенацию токенов:

#define E(X) 1e-##X
int main()
{
  double a = E(10); // expands to 1e-10
  return 0;
}
14
ответ дан 27 November 2019 в 22:28
поделиться

GCC 4.5.0 также не меняет X.

Ответ будет заключаться в том, как препроцессор интерпретирует токены предварительной обработки - и в правиле «максимального пережевывания». Правило «максимального пережевывания» - это то, что диктует, что «x +++++ y» трактуется как «x ++ ++ + y» и, следовательно, является ошибочным, а не как «x ++ + ++ y», которое законный.

Проблема в том, почему препроцессор интерпретирует «1e-X» как единственный токен предварительной обработки. Очевидно, что он будет рассматривать «1e-10» как один токен. Для «1e-» не существует допустимой интерпретации, если за ним не следует цифра после прохождения препроцессора. Итак, я должен предположить, что препроцессор видит «1e-X» как единственный токен (на самом деле ошибочный). Но я не проанализировал правильные разделы в стандарте, чтобы увидеть, где это требуется . Но определение «числа предварительной обработки» или «pp-числа» в стандарте (см. Ниже) несколько отличается от определения действительной целочисленной константы или константы с плавающей запятой и допускает использование многих «pp-чисел», которые недопустимы в качестве целочисленная константа или константа с плавающей запятой.

Если это поможет, то вывод компилятора Sun C для 'cc -E -v soq.c' будет следующим:

# 1 "soq.c"
# 2
int main()
{
"soq.c", line 4: invalid input token: 1e-X
  double a =  1e-X ;
  return 0;
}
#ident "acomp: Sun C 5.9 SunOS_sparc Patch 124867-09 2008/11/25"
cc: acomp failed for soq.c

Итак, по крайней мере один компилятор C отклоняет код в препроцессоре - возможно, GCC препроцессор немного провисает (я пытался спровоцировать его на жалобу с помощью gcc -Wall -pedantic -std = c89 -Wextra -E soq.c , но он не издал ни звука). И использование 3 X как в макросе, так и в нотации «1e-XXX» показало, что все три X использовались как GCC, так и Sun C. Compiler.

Стандартное определение номера предварительной обработки C

Из стандарта C - ISO / IEC 9899: 1999 §6.4.8 Номера предварительной обработки:

pp-number:
    digit
    . digit
    pp-number digit
    pp-number identifier-nondigit
    pp-number e sign
    pp-number E sign
    pp-number p sign
    pp-number P sign
    pp-number .

Учитывая это, «1e-X» является допустимым «номером pp», и поэтому X не является отдельным токеном (и «XXX» в «1e-XXX» не является отдельным токеном). Следовательно, препроцессор не может раскрыть X; это не отдельный токен, подлежащий расширению.

6
ответ дан 27 November 2019 в 22:28
поделиться

Препроцессор не является текстовым процессором, он работает на уровне токенов. В вашем коде после определения каждое появление токена X будет заменено токеном 10 . Однако в остальной части кода нет токена X .

1e-X синтаксически недействителен и не может быть преобразован в токен, что, по сути, и сообщает вам ошибка (в нем говорится, что для того, чтобы сделать его действительным токеном - в данном случае литералом с плавающей запятой - вы должны предоставить действительный показатель степени).

17
ответ дан 27 November 2019 в 22:28
поделиться

Некоторые люди сказали, что 1e-X лексируется как один токен, что частично верно. Для объяснения:

Во время трансляции есть два класса токенов: токены предварительной обработки и токены . Исходный файл изначально разбивается на токены предварительной обработки; эти токены затем используются во всех задачах предварительной обработки, включая замену макросов. После предварительной обработки каждый токен предварительной обработки преобразуется в токен; эти результирующие токены используются во время фактической компиляции.

Типов токенов предварительной обработки меньше, чем типов токенов. Например, ключевые слова (например, для , , а , , если ) не имеют значения на этапах предварительной обработки, поэтому токен предварительной обработки ключевого слова отсутствует. Ключевые слова просто лексируются как идентификаторы.Когда происходит преобразование токенов предварительной обработки в токены, проверяется каждый токен предварительной обработки идентификатора; если он соответствует ключевому слову, он преобразуется в токен ключевого слова; в противном случае он преобразуется в токен идентификатора.

Во время предварительной обработки используется только один тип числового токена: номер предварительной обработки . Этот тип токена предварительной обработки соответствует двум различным типам токенов: целочисленный литерал и плавающий литерал .

Маркер предварительной обработки номера предварительной обработки определяется очень широко. Фактически он соответствует любой последовательности символов, которая начинается с цифры или десятичной точки, за которой следует любое количество цифр, нецифров (например, букв) и e + и e- . Итак, все следующие допустимые токены предварительной обработки числа предварительной обработки:

1.0e-10
.78
42
1e-X
1helloworld

Первые два могут быть преобразованы в плавающие литералы; третий можно преобразовать в целочисленный литерал. Последние два не являются допустимыми целочисленными литералами или плавающими литералами; эти токены предварительной обработки не могут быть преобразованы в токены. Вот почему вы можете предварительно обработать источник без ошибок, но не можете его скомпилировать: ошибка возникает при преобразовании токенов предварительной обработки в токены.

9
ответ дан 27 November 2019 в 22:28
поделиться