Раздел: Статьи / С/С++ /

Препроцессор языка Си

Быстрый старт в С++ Быстрый старт в С++

Бесплатная небольшая книга, которую не надо читать полгода, но которая покажет вам, что такое С++ и как начать его изучение. А также поможет решить, надо ли вам это вообще. Кроме того, подписавшись на рассылку, вы будете получать от меня полезные материалы по С++, которые я периодически выпускаю... Подробнее...

Лично для меня слово “препроцессор” в начале моего пути программиста звучало как-то уж очень загадочно. Я не понимал, что это и для чего. А какой-то доступной литературы по этому вопросу мне тогда не попалось. Если для вас это тоже загадка, то читайте далее.

Разработка программ - это не только написание исходных текстов на языке программирования, например, на С/С++ или Паскале. Знания языка программирования необходимы, но их недостаточно для создания современных программ.

Кроме языка программирования каждый программист должен знать множество других вещей, таких, например, как директивы компилятора в Паскале или препроцессор в С/С++. Этой статьёй и видео (см. в конце страницы) я начинаю рассказ о препроцессоре Си.

Что такое препроцессор

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

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

Назначение препроцессора

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

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

Что делает препроцессор

Когда препроцессор получает исходный файл, он первым делом удаляет из него все комментарии. Вместо каждого комментария вставляется пробел.

Затем выполняется поиск директив (команд). Если таковые имеются, то препроцессор их выполняет.

После этого преобразованный файл передаётся компилятору или сохраняется в файл (поведение зависит от параметров командной строки).

Препроцессор GCC

Поскольку для примеров я использую среду разработки Dev-C++, а она, в свою очередь, использует компилятор MinGW, то в качестве препроцессора там применяется GCC. Поэтому и все дальнейшие рассказы будут о нём, хотя многое будет справедливо и для других препроцессоров С/С++.

Пример вызова GCC:

gcc -E myprog.cpp -o myprog.i

Здесь мы отправляем в препроцессор исходный файл myprog.cpp на обработку. Чтобы препроцессор записал результат работы в файл myprog.i, не передавая его в компилятор, мы используем параметр -E.

Расширение файла i в данном случае означает, что этот файл не требует обработки препроцессором, то есть при выполнении следующей команды для файла myprog.i препроцессор уже не будет вызываться, а сразу начнёт работать компилятор. Например, вызов:

gcc myprog.i -o myprog

не приведёт к вызову препроцессора, а сразу будет выполнена компиляция и создана программа myprog.

Директивы препроцессора

Для управления ходом компиляции и выполнения некоторых других действий применяются особые команды - директивы препроцессора. Директивы записываются в исходном тексте программы. Когда при обработке файла препроцессор находит директиву, он её выполняет.

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

Директива начинается со знака номера (#). Когда препроцессор находит этот знак, он понимает, что это директива, и выполняет её.

Директивы можно записывать в любой строке исходного файла. Как вне функций, так и внутри их.

Одна из таких директив, с которой встречался любой, даже самый начинающий программист на С/С++, это директива подключения файла #include.

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

Макросы препроцессора

Препроцессор умеет обрабатывать и выполнять не только директивы, но и, например, макросы.

Макрос, также как и константа, обозначается директивой #define. Но, в отличие от константы, в макросе могут быть параметры, которые препроцессор использует при его расшифровке. Пример:

#define kub(x) x*x*x

Здесь мы определили макрос kub(x), который возводит число в третью степень (в куб).

Теперь этот макрос можно использовать в программе как обычную функцию:

cout << "x*x*x = " << kub(3);

Этот код выведет на экран:

x*x*x = 27

Зачем нужны такие макросы, если можно использовать обычные функции?

  1. Иногда это проще. Для простых функций запись макроса будет короче, чем запись функции. Следовательно, на разработку программы в итоге потребуется меньше времени.
  2. Макросы можно собрать в заголовочный файл и использовать его в разных программах.

Однако есть одна особенность. Макрос не доходит до компилятора. Он преобразуется препроцессором в исходный код. Например, нашу запись (см. выше) компилятор увидит уже такой:

cout << "x*x*x = " << x*x*x;

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

cout << "x*x*x = " << kub(1+2);

Что выведет этот код? Число 27? А вот и нет. Он выведет число 7. Потому что вместо макроса будет подставлен его код:

cout << "x*x*x = " << 1+2 * 1+2 * 1+2;

Как вы понимаете, сначала будет выполнено умножение, а потом - сложение. Поэтому результат будет 7, а не 27. Неожиданно для тех, кто привык использовать только функции, правда?

Переменные препроцессора

В препроцессоре можно использовать предопределённые переменные. Список этих переменных зависит от конкретного препроцессора, но есть переменные, которые поддерживают практически все препроцессоры С/С++. Вот они:

  • __LINE__ - номер текущей строки обрабатываемого файла
  • __FILE__ - имя обрабатываемого файла
  • __DATE__ - дата компиляции
  • __TIME__ - время компиляции
  • __STDC__ - если файл соответствует стандарту языка С, то значение этой переменной равно 1, иначе - какое-то другое значение

Эти переменные, как видите, начинаются и заканчиваются двумя знаками подчёркивания (__).

Как их использовать - зависит от фантазии программиста и его потребностей. Пример:

#define file __FILE__

out << "Source file name: " << file;

Этот код выведет имя исходного файла, в котором определена константа file, которой присваивается значение переменной препроцессора __FILE__.

Использование препроцессора

Как я уже говорил, применение препроцессора зависит от вашего воображения. Обычно директивы используют для управления ходом компиляции.

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

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

Почему противная? Потому что я “паскалист”. А в Паскале такое в принципе невозможно (точнее, возможно, но там для этого предусмотрены защитные механизмы и не требуется применение директив).

С помощью директив условной компиляции эту неприятность можно устранить. Но об этом я расскажу в отдельной статье. Если не терпится, то можете прочитать об этом в книге Основы С++.

Видео о препроцессоре

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


Все способы изучить С++ Все способы изучить С++

Начинающие программисты даже не догадываются о том, какой огромный пласт в этой области скрыт от их глаз, и чего многие из новичков не увидят никогда, потому что это тёмная сторона программирования - чистый исходный код системного уровня… Подробнее...
Помощь в технических вопросах Помощь в технических вопросах

Помощь студентам. Курсовые, дипломы, чертежи (КОМПАС), задачи по программированию: Pascal/Delphi/Lazarus; С/С++; Ассемблер; языки программирования ПЛК; JavaScript; VBScript; Fortran; Python и др. Разработка (доработка) ПО ПЛК (предпочтение - ОВЕН, CoDeSys 2 и 3), а также программирование панелей оператора, программируемых реле и других приборов систем автоматизации. Подробнее...

Инфо-МАСТЕР ®
Все права защищены ©
e-mail: mail@info-master.su

Яндекс.Метрика