Почему препроцессор не приводит к тому, что два соседних знака минус являются декрементом?
Рассмотрим следующий код:
#include <stdio.h>
#define A -B
#define B -C
#define C 5
int main()
{
printf("The value of A is %d\n", A);
return 0;
}
Здесь предварительная обработка должна выполняться следующим образом:
- сначала A следует заменить на -B
- тогда B следует заменить на -C, таким образом выражение, выраженное в - C
- тогда C следует заменить на 5, таким образом, выражение, выраженное в - 5
Таким образом, полученное выражение должно давать ошибку компиляции (ошибка lvalue).
Но правильный ответ - 5, как выход может быть 5?
Пожалуйста, помогите мне в этом.
Ответы
Ответ 1
Это препроцессы до (обратите внимание на пробел):
int main()
{
printf("The value of A is %d\n", - -5);
return 0;
}
Препроцессор вставляет жетоны, а не строки. Он не будет создавать --
из двух смежных маркеров -
, если вы не принудительно объединяете токены с ##
:
#define CAT_(A,B) A##B
#define CAT(A,B) CAT_(A,B)
#define A CAT(-,B)
#define B -C
#define C 5
int main()
{
printf("The value of A is %d\n", A); /* A is --5 here—no space */
return 0;
}
Ответ 2
Хотя препроцессор C часто чувствует, что буквально выполняет поиск и заменяет код, препроцессор фактически работает немного по-другому.
Перед запуском препроцессора исходный файл разбивается на токены предварительной обработки, которые являются отдельными единицами текста. Например, один знак минус обрабатывается не как символ, а как знак, состоящий из знака минус, а знак двойного минуса рассматривается как токен, состоящий из двух знаков минус.
Препроцессор C запускает и заменяет каждый макрос не литеральным текстом замены макроса, а скорее серией токенов препроцессора в этой замене. В этом случае препроцессор заменяет A минусом, за которым следует B, затем заменяет B минусом, за которым следует C, а затем заменяет C на 5. Эффект здесь состоит в том, что есть два унарных минуса, примененных к 5, а не декремент оператора, хотя литеральный поиск и замена сгенерировали бы оператор декремента, создающий синтаксическую ошибку.
Это интересно тем, что вы не можете писать два последовательных минусовых знака в исходном коде и интерпретировать их как два унарных минусов. Это работает только потому, что к тому времени, когда препроцессор объединяет все вместе, он уже знает, что он смотрит на два унарных минусов. Получившийся C-код не будет повторно отсканирован, чтобы его токенизировать во второй раз.
Теперь в разделе legalise: section & sect; 5.1.1.2/7 говорится, что после макроподстановки каждый токен предварительной обработки - и здесь есть два из них (два минусовых знака) - преобразуются в фактические токены, а затем theyre синтаксически и семантически анализируется. Это означает, что у theres нет возможности для компилятора пересканировать эти жетоны, чтобы переосмыслить их как один токен. Так что это странный случай, когда результирующий поток токена не может быть введен в исходный код без изменения значения.
Ответ 3
Вспомните результирующее выражение как это:
-(-(5))