Найти в Дзене

Основы С++: Директивы препроцессора и знакомство с макросами

Оглавление

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

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

В каждой программе с вводом и выводом данных вы используете директиву #include <iostream>

Все директивы препроцессора начинаются с новой строки и символа #, а заканчиваются переходом на следующую строку (а не точкой с запятой, как инструкции).

Препроцессор не понимает синтаксис С++, он реагирует только на директивы и не различает описанные программистом объекты.

Директива включения #include

Директива #include (включи) — дает препроцессору команду вставить содержимое файла, указанного после директивы, на место, где была определена директива.

Например, когда вы используете директиву #include <iostream>, препроцессор вставляет на место этой директивы содержание заголовочного файла iostream, в котором описаны правила ввода и вывода данных.

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

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

Директива определения #define

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

Макрос в С++ — это правило, применяемое к тексту, которое определяет его преобразование с помощью замены (подстановки). Все макросы делятся на два типа: макросы подобные объектам и макросы подобные функциям.

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

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

Да, аргументировать самому: "Почему бесконтрольное использование макросов подобных функциям опасно?", — мне лень. Поэтому я оставлю ссылку на эту статью. Мое мнение совпадает с мнением ее автора.

Что ж, а теперь, ближе к теме!

Макросы подобные объектам

Разберемся с определениями. Когда вы пишите макрос подобный объекту, вы можете использовать две формы его определения:

____________________________________________________________________________________

#define идентификатор

#define идентификатор "текст_для_замены"

____________________________________________________________________________________

Для чего и как использовать?

Для начала, разберемся со второй формой определения макроса подобного объекту.

#define идентификатор "текст_для_замены"

Эта форма говорит препроцессору:

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

Вот так это выглядит на практике:

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

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

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

По идее, это бесполезная фишка? Для манипуляций с текстом, да! Но эта форма макросов используется для других целей и ниже, в блоке условной компиляции мы разберем их использование.

Условная компиляция

Еще одна форма директив препроцессора, которую мы с вами разберем, относится к условной компиляции. Директив из области условной компиляции существует много, но мы разберем четыре: #ifdef #ifndef #if 0 #endif

Директива #ifdef

Рассмотрим следующую программу:

Вот так и работают объектоподобные макросы без подстановки текста
Вот так и работают объектоподобные макросы без подстановки текста

Перепишите ее код в свой редактор, скомпилируйте и запустите. Вы получите странный, на первый взгляд, результат. После запуска в консоли напечатается только первый текст, поданный в печать (строка 7): "Hello!"

Текст "Bye!" (строка 11) печататься не будет.

Именно так работает директива #ifdef. Дословно, она говорит: если макрос HELLO был определен ранее, оставь код без изменений, начиная от этой строчки и до #endif включительно. Если же условие не выполняется, удали код. Так как макрос HELLO был определен на строке 2, код остается без изменений. И наоборот, макрос BYE не был определен нигде до появления директиве "#ifdef BYE", а значит условие не выполнено. И весь код с 10 по 12 строку компилироваться не будет.

Директива #ifndef — это противоположность директиве "ifdef". Попробуйте заменить ifdef на ifndef в коде программы, которую вы только что переписали себе со скрина. Теперь код с 6-8 строку компилироваться не будет и наоборот, код с 10-12 строку будет компилироваться.

Если еще не поняли, #endif обозначает конец действия условия.

Директива #if 0

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

-3

То есть, никаким! Программа запустит пустую функцию main, она вернет операционной системе 0 и завершит свою работу.

В финале статьи хочу добавить пару важных вещей:

Директивы препроцессора выполняются сверху вниз для каждого файла в решении

Объекто-подобные макросы не влияют на другие директивы препроцессора.

И на этом сегодня все. Спасибо за внимание.

Оставляйте лайки, если материал вам понравился.

Остались вопросы, задавайте их в комментариях.

Подпишитесь на канал, чтобы не пропустить выход новых статей!