Почему эти последовательные замены макросов не приводят к ошибке? - PullRequest
0 голосов
/ 24 июня 2018

Эта программа выводит как 5. Но после замены всех макросов это приведет к --5.Это должно вызвать ошибку компиляции, пытаясь уменьшить значение 5.Но он компилируется и работает нормально.

#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;
} 

Почему нет ошибки?

Ответы [ 3 ]

0 голосов
/ 24 июня 2018

Две последовательные черты не объединяются в один оператор предварительного декремента --, потому что препроцессор C работает с отдельными токенами, эффективно вставляя пробелы вокруг подстановок макросов. Запуск этой программы через gcc -E

#define A -B
#define B -C
#define C 5

int main() {
    return A;
}

выдает следующий вывод:

int main() {
    return - -5;
}

Обратите внимание на пробел после первого -.

Согласно стандарту, замены макросов выполняются на уровне токенов препроцессора, а не на уровне отдельных символов (6.10.3.9):

Директива предварительной обработки вида

# define identifier replacement-list new-line

определяет объектоподобный макрос, который вызывает замену каждого последующего экземпляра имени макроса списком замены токенов предварительной обработки, которые составляют остаток директивы.

Следовательно, две черты - представляют собой два разных токена, поэтому они выводятся отдельно друг от друга на выходе препроцессора.

0 голосов
/ 24 июня 2018

Всякий раз, когда мы используем #include в программе на C, компилятор заменяет переменную на значение, где бы она ни использовалась.

#define A -B
#define B -C
#define C 5

Итак, когда мы печатаем А, он будет выполнен в следующих шагах.

A => - B

В => - С

А => - (- С) => С

Поэтому, когда мы печатаем значение A, оно получается равным 5.

Обычно эти операторы #define используются для объявления значений констант, которые должны использоваться в коде.

Подробнее см. эта ссылка на # define директива

0 голосов
/ 24 июня 2018

Вот шаги для составления оператора printf("The value of A is %d\n", A);:

  • лексический анализатор создает токены предварительной обработки printf, (, "The value of A is %dn", ,, A, ) и ;.
  • A - это макрос, который распространяется на 2 токена - и B.
  • B также является макросом и расширяется до - и C.
  • C снова макрос и расширяется до 5.
  • токены затем преобразуются в токены C, что приводит к ошибкам при предварительной обработке токенов, которые не преобразуются в надлежащие токены C (например: 0a). В этом примере токены идентичны.
  • компилятор анализирует полученную последовательность в соответствии с грамматикой C: printf, (, "The value of A is %d\n", ,, -, -, 5, ), ; сопоставляет вызов функции с printf с 2 аргументами: строкой формата и константным выражением - - 5, которое во время компиляции оценивается в 5.
  • код, следовательно, эквивалентен printf("The value of A is %d\n", 5);. Будет выдан вывод:

    The value of A is 5
    

Эта последовательность макросов раскрывается как токены, а не как последовательность символов, следовательно, A расширяется не как --5, а как - -5. Хорошие компиляторы Си вставили бы дополнительный пробел при предварительной обработке исходного текста для текстового вывода, чтобы гарантировать, что результирующий текст при повторном анализе выдает ту же последовательность токенов. Однако обратите внимание, что в стандарте C ничего не говорится о предварительной обработке для текстового вывода, он только определяет предварительную обработку как один из этапов синтаксического анализа, и это является проблемой качества реализации для компиляторов, чтобы не создавать потенциальных побочных эффектов при предварительной обработке для текстового вывода.

В препроцессоре есть отдельная функция для объединения токенов в новые токены, называемая вставкой токенов. Он требует определенного оператора ## и довольно сложен в использовании.

Обратите внимание, что макросы должны быть определены с круглыми скобками вокруг каждого аргумента и круглыми скобками вокруг всего раскрытия, чтобы избежать проблем с приоритетами операторов:

#define A  (-B)
#define B  (-C)
#define C  5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...