Работа препроцессора C - PullRequest
4 голосов
/ 10 июля 2011

Как работает следующий фрагмент кода, другими словами, каков алгоритм препроцессора C?Это работает на всех компиляторах?

#include <stdio.h>

#define b a
#define a 170


int main() {
  printf("%i", b);
  return 0;
}

Ответы [ 7 ]

9 голосов
/ 10 июля 2011

Препроцессор просто заменяет b на a, где бы он ни находился в программе, а затем заменяет a на 170 Это просто текстовая замена.

Работает на gcc.

4 голосов
/ 18 февраля 2013

Это в §6.10.3 (Замена макроса):

6.10.3.4 Повторное сканирование и дальнейшая замена

1) После того, как все параметры в списке замены были заменены и #и ## обработка выполнена, все маркеры предварительной обработки меток удалены.Затем результирующая последовательность токенов предварительной обработки пересканируется вместе со всеми последующими токенами предварительной обработки исходного файла для замены других имен макросов.

В следующих параграфах изложены некоторые дополнительные правила и исключения, но в основном этоit.

Хотя это может нарушать некоторые определения «одного прохода», это очень полезно.Как рекурсивная предварительная обработка включенных файлов (§5.1.1.2p4).

1 голос
/ 19 февраля 2013

Препроцессор просто заменяет символы последовательно всякий раз, когда они появляются.Порядок определений в данном случае не имеет значения, b сначала заменяется на a, а оператор printf становится

printf("%i", a);

и после замены aна 170 оно становится

printf("%i", 170);

Если порядок определения был изменен, то есть

#define a 170
#define b a

Тогда препроцессор сначала заменяет a, а второе определение становится

#define b 170

Итак, наконец, оператор printf становится

printf("%i",170);

Это работает для любого компилятора .

1 голос
/ 10 июля 2011

Эта простая замена (сначала b на a, а затем a на 170) должна работать с любым компилятором.Вы должны быть осторожны с более сложными случаями (обычно включающими строковую '#' и конкатенацию токенов '##'), поскольку угловые случаи обрабатываются по-разному, по крайней мере, MSVC и gcc.

В случае сомнений, вы всегда можете проверитьСтандарт ISO ( черновик доступен онлайн ), чтобы увидеть, как вещи должны работать :).Раздел 6.10.3 является наиболее актуальным в вашем случае.

0 голосов
/ 19 февраля 2013

Я думаю, вы пытаетесь получить информацию о том, как исходный код обрабатывается компилятором. Чтобы точно знать, вам нужно пройти Фазы перевода . Основные шаги, которые выполняются каждым компилятором (пытались дать каждую деталь - собранные с разных блогов и сайтов) приведены ниже:

  1. Первый шаг компилятора - При необходимости символы физического файла источника сопоставляются с исходным набором символов (вводятся символы новой строки для индикаторов конца строки). Последовательности триграфа заменяются соответствующими односимвольными внутренними представлениями.

  2. Второй шаг компилятора - Каждый экземпляр символа новой строки и непосредственно предшествующего символа обратной косой черты удаляется, объединяя физические исходные строки для формирования логических исходных строк. Исходный файл, который не является пустым, должен заканчиваться символом новой строки, которому непосредственно не должен предшествовать символ обратной косой черты.

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

  4. Четвертый шаг компилятора - Директивы предварительной обработки выполняются и макропрограммы расширяются. Директива предварительной обработки #include вызывает рекурсивную обработку именованного заголовка или исходного файла от фазы 1 до фазы 4.

  5. Пятый шаг компилятора - Каждая escape-последовательность в символьных константах и ​​строковых литералах преобразуется в член набора символов выполнения.

  6. Шестой шаг компилятора - Литеральные токены смежных символьных строк объединяются, а смежные литеральные токены широких строк объединяются.

  7. Седьмой шаг за компилятором - Пробельные символы, разделяющие токены, больше не имеют значения. Токены предварительной обработки преобразуются в токены. Полученные токены синтаксически и семантически анализируются и переводятся.

  8. Последний шаг - Все внешние ссылки на объекты и функции разрешены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на функции и объекты, не определенные в текущем переводе. Весь такой вывод транслятора собирается в образ программы, который содержит информацию, необходимую для выполнения в среде выполнения.

0 голосов
/ 18 февраля 2013

#define просто присваивает значение ключевому слову.

Здесь «b» - это первое присвоенное значение «a», затем «a» - присвоенное значение «170». Для простоты это можно выразить следующим образом:

b=a=170

Это просто другой способ определить одно и то же.

0 голосов
/ 18 февраля 2013

Чтобы получить подробную информацию, вы можете попробовать gcc -E, чтобы проанализировать выходной сигнал препроцессора, который может легко устранить ваши сомнения

...