Подтвердить что ты не робот

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

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

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

Я получаю ошибку: Exponent не имеет цифр.

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

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

Любые комментарии/предложения?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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

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

Ответ 4

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

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

Проблема заключается в том, почему препроцессор интерпретирует '1e-X' как единственный токен предварительной обработки. Ясно, что он будет рассматривать "1е-10" как единый токен. Нет действительной интерпретации для '1e-', если за ней не будет цифра после прохождения препроцессора. Итак, я должен угадать, что препроцессор видит '1e-X' как единственный токен (фактически ошибочный). Но я не расчленял правильные предложения в стандарте, чтобы увидеть, где это требуется. Но определение "числа предварительной обработки" или "pp-number" в стандарте (см. Ниже) несколько отличается от определения действительной целочисленной или постоянной с плавающей запятой и позволяет многим "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.

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; это не отдельный токен, подлежащий расширению.