Что такое директива процессора
Обновлено: 20.11.2024
Что такое директива препроцессора?
Директива препроцессора передается компилятору перед компиляцией кода C. Компилятор сначала работает с директивами препроцессора, делая замены, включения и даже решения, прежде чем исходный код будет преобразован в объектный код.
Вот различные директивы препроцессора:
Вы увидите, что большинство этих директив используются в различных заголовочных файлах. На самом деле, я рекомендую вам просмотреть файлы заголовков, чтобы изучить возможности того, как эти директивы работают. Это потому, что большая часть вашего кода, вероятно, не будет использовать этих ребят.
компилятор ищет этот файл в каталоге /usr/include (или аналогичном). В противном случае, когда имя файла заключено в двойные кавычки:
файл ищется в текущем каталоге.
Выше компилятор будет искать любой экземпляр ROWS и заменять его значением 5.
Вы можете включать переменные в расширения макросов, что делается для определения putchar() в заголовочном файле stdio.h
Однако будьте осторожны при создании таких расширений, так как иногда компилятор может расширить макрос дважды.
Конечно, мне пришлось вручную установить значение ЦВЕТА. Это было нормально; это позволило мне использовать один файл исходного кода для создания двух разных версий программы, одной для цветных систем и одной для монохромной.
Эти две директивы соответствуют элементам else if и else в дереве решений языка C if. Они позволяют создавать сложные решения с использованием директив препроцессора.
Эти директивы формируют определенные условия if в зависимости от того, определено что-либо или еще не определено. Значение того, что определено, не имеет значения, важно, был ли элемент определен или нет. Например:
В приведенном выше примере создается константа BLORFUS, если она еще не определена. Вы увидите множество таких примеров внутри заголовочных файлов, если решите их просмотреть. Прочтите мой урок от 21 февраля 2015 г., чтобы узнать, как использовать эти директивы.
Препроцессор — это часть компилятора, которая выполняет предварительные операции (условную компиляцию кода, включая файлы и т. д.) с вашим кодом до того, как компилятор увидит его. Эти преобразования являются лексическими, что означает, что на выходе препроцессора остается текст.
ПРИМЕЧАНИЕ. Технически выходные данные этапа предварительной обработки для C состоят из последовательности токенов, а не исходного текста, но вывести исходный текст, который эквивалентен заданной последовательности токенов, и который обычно поддерживается компиляторами, несложно. с помощью параметра -E или /E — хотя параметры командной строки для компиляторов C не являются полностью стандартными, многие следуют аналогичным правилам.
Содержание
Директивы [ редактировать | изменить источник ]
Директивы — это специальные инструкции, направленные препроцессору (директива препроцессора) или компилятору (директива компилятора) о том, как он должен обрабатывать часть или весь ваш исходный код или устанавливать некоторые флаги для конечного объекта, и используются для создания исходного кода. код проще (например, более переносимый) и сделать исходный код более понятным. Директивы обрабатываются препроцессором, который представляет собой либо отдельную программу, вызываемую компилятором, либо часть самого компилятора.
Когда вы используете функции из библиотеки, C требует, чтобы вы декларировали, что вы будете использовать. Первая строка в программе — это директива предварительной обработки, которая должна выглядеть так:
Строка выше приводит к включению объявлений C, которые находятся в заголовке stdio.h, для использования в вашей программе. Обычно это реализуется путем простой вставки в вашу программу содержимого заголовочного файла с именем stdio.h, расположенного в системно-зависимом месте. Расположение таких файлов может быть описано в документации вашего компилятора. Список стандартных файлов заголовков C приведен ниже в таблице заголовков.
Заголовок stdio.h содержит различные объявления для ввода-вывода (I/O) с использованием абстракции механизмов ввода-вывода, называемых потоками. Например, существует объект потока вывода с именем stdout, который используется для вывода текста на стандартный вывод, который обычно отображает текст на экране компьютера.
При использовании угловых скобок, как в приведенном выше примере, препроцессор получает указание искать включаемый файл по пути среды разработки для стандартных включений.
Заголовки [ редактировать | изменить источник ]
Список стандартных заголовков C90:
Заголовки, добавленные после C90:
Прагмы используются в исходной программе.
ПРИМЕЧАНИЕ. Существуют и другие методы выполнения этого действия, которые обычно называют включением защиты.
Тем не менее, есть случаи, когда макросы очень полезны (см. пример макроса отладки ниже).
При использовании сложных макросов обычно рекомендуется использовать дополнительные круглые скобки.Обратите внимание, что в приведенном выше примере переменная «x» всегда заключена в скобки. Таким образом, он будет оцениваться целиком, прежде чем сравниваться с 0 или умножаться на -1. Кроме того, весь макрос заключен в круглые скобки, чтобы предотвратить его загрязнение другим кодом. Если вы не будете осторожны, вы рискуете, что компилятор неправильно интерпретирует ваш код.
Из-за побочных эффектов использование макрофункций, как описано выше, считается очень плохой идеей.
Если бы ABSOLUTE_VALUE() была реальной функцией, 'x' теперь имел бы значение '-9', но, поскольку это был аргумент в макросе, он был развернут дважды и, таким образом, имеет значение -8.
Чтобы проиллюстрировать опасность макросов, рассмотрим этот наивный макрос
Взгляните на это и подумайте, каким может быть значение после выполнения. Операторы превращаются в
Таким образом, после выполнения i=8 и j=3 вместо ожидаемого результата i=j=8! Вот почему вы были предупреждены об использовании дополнительного набора скобок выше, но даже с ними дорога чревата опасностями. Внимательный читатель может быстро понять, что если a или b содержат выражения, определение должно заключать в скобки каждое использование a,b в определении макроса, например:
Это работает, если a,b не имеет побочных эффектов. Действительно,
приведет к k=4, i=3 и j=5. Это было бы очень неожиданно для тех, кто ожидает, что MAX() будет вести себя как функция.
Какое правильное решение? Решение состоит в том, чтобы вообще не использовать макрос. Глобальная встроенная функция, подобная этой
не имеет ни одной из описанных выше ошибок, но работает не со всеми типами.
ПРИМЕЧАНИЕ. В явном встроенном объявлении нет необходимости, если только определение не находится в заголовочном файле, поскольку ваш компилятор может встроить функции за вас (в gcc это можно сделать с помощью -finline-functions или -O3 ). Компилятор часто лучше программиста предсказывает, какие функции стоит встраивать. Кроме того, вызовы функций не очень дороги (раньше были).
На самом деле компилятор может игнорировать ключевое слово inline. Это всего лишь подсказка (за исключением того, что inline необходим, чтобы разрешить определение функции в заголовочном файле без создания сообщения об ошибке из-за того, что функция определена более чем в одной единице перевода).
заставит компилятор включить эту команду
заставит компилятор включить
что, конечно же, будет отображать 10 в стандартном выводе.
Можно объединить аргумент макроса с постоянным префиксом или суффиксом, чтобы получить допустимый идентификатор, как в
который определит функцию с именем my_bar(). Но невозможно интегрировать аргумент макроса в константную строку с помощью оператора конкатенации. Чтобы получить такой эффект, можно использовать свойство ANSI C, состоящее в том, что две или более последовательных строковых константы при встрече считаются эквивалентными одной строковой константе. Используя это свойство, можно написать
в который превратится макропроцессор
который, в свою очередь, будет интерпретирован синтаксическим анализатором C как одна строковая константа.
Для преобразования числовых констант в строковые литералы можно использовать следующий прием
Это немного сложно, так как раскрывается в 2 шага. Первый num2str(CONST) заменяется на str(23) , который в свою очередь заменяется на "23" . Это может быть полезно в следующем примере:
Это даст вам красивое отладочное сообщение, включая файл и строку, в которой было выдано сообщение. Однако, если DEBUG не определен, сообщение об отладке полностью исчезнет из вашего кода. Будьте осторожны, чтобы не использовать такую конструкцию с чем-либо, что имеет побочные эффекты, так как это может привести к ошибкам, которые появляются и исчезают в зависимости от параметров компиляции.
макросы [ редактировать | изменить источник ]
Макросы не проверяются на тип, поэтому они не оценивают аргументы. Кроме того, они не подчиняются области действия должным образом, а просто берут переданную им строку и заменяют каждое вхождение аргумента макроса в тексте макроса фактической строкой для этого параметра (код буквально копируется в место, где он был вызван). из).
Пример использования макроса:
-- результатом "a" должно быть "2" (b + c = 16 -> передано в ADD -> 16/SLICES -> результат "2")
ПРИМЕЧАНИЕ.
Определение макросов в заголовках обычно является плохой практикой.
Макрос следует определять только тогда, когда невозможно достичь того же результата с помощью функции или какого-либо другого механизма. Некоторые компиляторы могут оптимизировать код таким образом, что вызовы небольших функций заменяются встроенным кодом, сводя на нет любое возможное преимущество в скорости. Использование typedefs, enums и inline (в C99) часто является лучшим вариантом.
Одна из немногих ситуаций, когда встроенные функции не будут работать (поэтому вам придется вместо них использовать макросы, подобные функциям), — это инициализация констант времени компиляции (статическая инициализация структур). Это происходит, когда аргументы макроса являются литералами, которые компилятор может оптимизировать до другого литерала. [2]
Условное выражение может содержать любой оператор C, кроме операторов присваивания, операторов увеличения и уменьшения, оператора адреса и оператора sizeof.
Один уникальный оператор, используемый в предварительной обработке и нигде больше, является определенным оператором. Он возвращает 1, если имя макроса, необязательно заключенное в круглые скобки, в настоящее время определено; 0, если нет.
эквивалентно
эквивалентно
Эта директива препроцессора используется для установки новых значений имени файла и номера строки, следующей за директивой. Используется для установки макросов __FILE__ и __LINE__.
Полезные макросы препроцессора для отладки [ edit | изменить источник ]
ANSI C определяет некоторые полезные макросы и переменные препроцессора, [3] [4], также называемые «магическими константами», включая:
__FILE__ => Имя текущего файла в виде строкового литерала
__LINE__ => Текущая строка исходного файла в виде числового литерала
__DATE__ => Текущая системная дата в виде строки < br />__TIME__ => Текущее системное время в виде строки
__TIMESTAMP__ => Дата и время (нестандартные)
__cplusplus => не определено, когда код C компилируется компилятором C; 199711L, когда код C компилируется компилятором C++, соответствующим стандарту C++ 1998 года.
__func__ => Имя текущей функции исходного файла в виде строки (часть C99)
__PRETTY_FUNCTION__ => "decorated" Имя текущей функции исходного файла в виде строки (в GCC; не- стандарт)
Утверждения времени компиляции [ edit | изменить источник ]
Утверждения времени компиляции могут помочь отлаживать быстрее, чем использование только операторов assert() времени выполнения, потому что все утверждения времени компиляции проверяются во время компиляции, а тестовый запуск программы может привести к сбою. использовать некоторые операторы assert() во время выполнения.
До стандарта C11 некоторые люди [5] [6] [7] определили макрос препроцессора, чтобы разрешить утверждения во время компиляции, например:
Подобный макрос определен в библиотеке static_assert.hpp Boost. [8]
Начиная с C11, такие макросы устарели, так как _Static_assert и его макроэквивалент static_assert стандартизированы и встроены в язык.
X-Macros [ редактировать | изменить источник ]
Один малоизвестный шаблон использования препроцессора C известен как "X-Macros". [9] [10] [11] [12] X-Macro — это заголовочный файл или макрос. Обычно они используют расширение «.def» вместо традиционного «.h». Этот файл содержит список похожих вызовов макросов, которые можно назвать «макросами компонентов». Затем на включаемый файл неоднократно ссылаются в следующем шаблоне. Здесь включаемым файлом является "xmacro.def", и он содержит список макросов компонентов в стиле "foo(x, y, z)".
Общие наборы объектов – это набор глобальных параметров конфигурации, набор элементов структуры, список возможных тегов XML для преобразования файла XML в быстро просматриваемое дерево или тело объявления перечисления; возможны и другие списки.
После обработки X-Macro для создания списка объектов можно переопределить макросы компонента для создания, например, функций доступа и/или изменения. Также обычно выполняется сериализация и десериализация структур.
Вот пример X-Macro, который устанавливает структуру и автоматически создает функции сериализации/десериализации. Для простоты в этом примере не учитываются порядок следования байтов или переполнение буфера.
Файл star.def:
Файл star_table.c:
Обратите внимание, что в этом примере вы также можете избежать создания отдельных функций обработчика для каждого типа данных в этом примере, определив формат печати для каждого поддерживаемого типа, с дополнительным преимуществом сокращения кода расширения, создаваемого этим файлом заголовка:< /p>
Создания отдельного файла заголовка можно избежать, создав один макрос, содержащий то, что будет содержимым файла. Например, указанный выше файл «star.def» можно заменить этим макросом в начале:
Файл star_table.c:
и можно добавить обработчик печати, а также:
Вариант, который позволяет избежать необходимости знать элементы каких-либо расширенных вложенных макросов, заключается в том, чтобы принимать операторы в качестве аргумента макроса списка:
Файл star_table.c:
Этот подход может быть опасным, поскольку весь набор макросов всегда интерпретируется так, как будто он находится в одной строке исходного кода, что может привести к ограничениям компилятора со сложными макросами компонентов и/или длинными списками элементов.
Об этой технике сообщил Ларс Вирзениус [13] на веб-странице от 17 января 2000 г., на которой он отдает должное Кеннету Оксанену за «усовершенствование и развитие» техники до 1997 г.В других источниках этот метод описывается как метод, существовавший как минимум за десять лет до начала века.
Мы подробнее обсудим X-Macros в следующем разделе, Сериализация и X-Macros.
Веб-разработка, языки программирования, тестирование программного обеспечения и другое
Существуют определенные приспособления или возможности, которые может предоставить препроцессор C:
- Файлы заголовков. Включение файлов заголовков позволяет заменить объявления синтаксисом и телом программы.
- Расширение макроса. Определение макроса похоже на сокращение фрагмента кода, в котором препроцессор C заменяет макросы соответствующим определением.
- Компиляция по условиям: В зависимости от различных сценариев или различных условий возможно включение определенных частей программы путем условной компиляции.
- Управление строками. Если вы используете программу для объединения или преобразования одного или нескольких исходных файлов в промежуточный файл для компиляции, вы можете использовать управление строками, чтобы информировать компилятор о том, откуда взялась каждая строка исходного кода.
Типы директив препроцессора
Все типы директив препроцессора следующие:
Синтаксис:
Существует два типа макросов:
- Функции — как макросы
- Объект – как макрос
Функция — как макрос
Функция like-macro работает почти как вызов функции.
MAX здесь имя макроса.
Пример:
Вывод:
Объект – как макрос
Макросы, подобные объектам, представляют собой тип идентификатора, замененный значением. В основном он используется для представления числовых констант.
Здесь значение PI будет заменено макросом.
Обучение программированию на C (3 курса, 5 проектов) 3 онлайн-курса | 5 практических проектов | 34+ часа | Поддающийся проверке сертификат об окончании | Пожизненный доступ
4,5 (8 529 оценок)
Вывод:
У директивы препроцессора include есть и другие функции. Он имеет три собственных варианта, которые заменяют код текущим кодом исходных файлов.
Три варианта:
Ищет файл в заданном списке системы или каталогов, как указано, а затем ищет стандартный список системных библиотек.
Этот тип используется для ваших собственных настраиваемых заголовочных файлов программы. Сначала выполняется поиск файла с именем file в текущем каталоге, за которым следуют системные заголовочные файлы и текущие каталоги каталога с текущим файлом.
Этот тип директивы препроцессора include используется, когда ни один из оставшихся двух типов директивы и ее аргументов не подходит и не удовлетворяет вычислительной структуре.
Синтаксис:
Синтаксис:
Вывод:
4. Еслиndef
Синтаксис:
Этот процессор работает только как цикл if, он оценивает выражение или условие. Если идентификатор условия истинен, он выполнит код, иначе нет.
Синтаксис:
Синтаксис:
Пример:
Вывод:
Как следует из названия, директива препроцессора Error используется для обозначения ошибки, а затем компилятор выдает фатальную ошибку, если директива ошибки найдена, и пропускает следующие шаги компиляции.
Вывод:
Это зависит от компилятора, поскольку разные ОС и разные машины предоставляют все типы функций операционной системы, которые используются компилятором для предоставления компилятору дополнительной информации.
Синтаксис:
Пример:
Вывод:
Каждый препроцессор имеет свое собственное значение, например, условная директива используется для проверки того, следует ли учитывать часть программы на основе сценариев или нет.
Предположим, что программа хочет быть скомпилирована в определенной среде с определенной конфигурацией операционной системы, но как только она переходит к этой фазе компиляции, она выдает ошибку или может дать недопустимый код, просто давая своей программе возможность большое нет, чтобы связать программу и запустить ее во время выполнения. Также может быть другая возможность, когда один и тот же исходный файл с двумя разными программами может выполнять трудоемкую проверку согласованности или его непосредственных данных, или распечатывать значения данных с отладкой.
Кроме того, эти сценарии, создаваемые с помощью вычислений, также можно использовать для запуска на одном компьютере с помощью директив предварительной обработки.
Заключение
Вывод препроцессора C очень похож на ввод, за исключением того, что все директивы препроцессора заменены пустыми строками или пробелами. Разные файлы и форматы имеют разный синтаксис, говорящий о начале нового файла или указывающий на возврат к файлу или обработку, которую необходимо выполнить перед компиляцией.
Все сценарии используются для того, чтобы другие узнали о возможностях препроцессора C и о том, как он развивается с различными версиями компилятора, основанными на стандартах GCC и ANSI.
Рекомендуемые статьи
Это руководство по директивам препроцессора в C. Здесь мы обсудим типы директив препроцессора с синтаксисом и примерами. Вы также можете ознакомиться со следующими статьями, чтобы узнать больше:
Препроцессор — это уникальная функция C++. В C++ у нас есть такие шаги, как компиляция, компоновка и выполнение для типичной программы. На самом деле у нас есть много других функций в программе на C++, которые необходимо обработать перед передачей программы на компиляцию.
Для этого выполняется специальный шаг, называемый предварительной обработкой. Предварительная обработка выполняется до процесса компиляции, и специальные функции предварительно обрабатываются. В результате получается расширенная программа на C++, которая затем передается компилятору.
Что вы узнаете:
Обзор
Специальные функции для предварительной обработки идентифицируются с помощью объекта, который называется «Директива препроцессора». Эти директивы препроцессора сообщают компилятору, что определенная информация в программе C++, помеченная директивами препроцессора, должна быть предварительно обработана перед компиляцией.
В отличие от других инструкций C++, директивы препроцессора не заканчиваются точкой с запятой.
В этом руководстве мы рассмотрим различные директивы препроцессора, поддерживаемые C++.
Директивы включения файлов
Мы уже видели это в наших программах на C++. Заголовок iostream содержит функции, необходимые для потоковой передачи данных ввода/вывода, такие как cout, cin и т. д.
Это определяемый пользователем заголовочный файл, который мы намерены включить в нашу программу, чтобы использовать его функциональные возможности.
Вывод:
Директивы определения макросов
Вывод:
Площадь круга: 78,55
Пример показан ниже.
Вывод:
Площадь прямоугольника: 100
Условные директивы компиляции
Помимо описанных выше директив, C++ также предоставляет следующие директивы, которые можно использовать для условной компиляции кода. Эти директивы можно использовать в аналогичных строках оператора if-else C++.
Например, мы можем включить или отключить DEBUG для программы, используя эти условные директивы.
Некоторые директивы условной компиляции, представленные в C++, включают:
Приведенная ниже программа демонстрирует использование директив условной компиляции в программе C++.
Вывод:
Трассировка: начало основной функции
Максимум – 100
Трассировка: конец основной функции
Ниже приведен пример, демонстрирующий оба этих оператора.
Это указывает компилятору изменить внутренний номер строки и имя файла, хранящиеся в компиляторе, на заданный номер строки и имя файла.
Последовательность цифр может быть целочисленной константой.
В приведенном выше примере внутренний номер строки установлен на 200, а имя файла изменено на test.c.
Предоставляет компилятору инструкции, определенные реализацией. Эти инструкции специфичны для компилятора и платформы. Если инструкция не совпадает, директива игнорируется без возникновения синтаксической ошибки.
Предустановленные макросы
C++ также определяет множество предопределенных макросов, которые могут использоваться программистами.
Некоторые из этих макросов представлены в таблице ниже.
Предопределенный макрос | Описание |
---|---|
__FILE__ | Текущий имя файла компилируемой программы |
__DATE__ | Дата трансляции исходного кода в объектный код в формате месяц/день/год |
__TIME__ | Время в формате час:минута:секунда, когда компилируется программа |
__LINE__ | Текущий номер строки компилируемой программы |
__cplusplus | Целая константа, определяемая для каждой версии компилятора |
Следующая программа демонстрирует эти макросы в программе.
Вывод:
__LINE__ :5
__FILE__ :prog.cpp
__DATE__ :15 апреля 2019 г.
__TIME__ :12:09:15
__cplusplus:201402
Приведенный выше вывод программы соответствует объяснению предопределенных макросов выше и не требует пояснений.
Заключение
В этом руководстве мы рассмотрели различные директивы препроцессора, предоставляемые C++, а также их примеры. Директивы препроцессора помогают нам писать более эффективные программы и в некоторой степени более читаемые программы.
Директивы условной компиляции также позволяют нам различными способами разветвлять вывод нашей программы.
Читайте также: