Что такое директива в c
Перейти к содержимому

Что такое директива в c

  • автор:

Что такое директива в c

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

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

Предпроцессор С распознает следующие директивы:

#define #if #line #elif #ifdef #undef #else #ifndef #endif #include

Знак номера (#) должен быть первым неразделительным символом на строке, содержащей директиву, между символом номера и первой буквой директивы могут появляться разделительные символы. Некоторые директивы содержат аргументы или значения. Любой текст, который следует за директивой (кроме аргумента или значения, который является частью директивы) должен быть заключен в скобки комментария (/* */).

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

«Оператор предпроцессора» это оператор, который признается оператором только в контексте директив предпроцессора. Есть только три специфические оператора предпроцессора: «строковый» (#), «вставки лексем» (##) и defined. Первые два оператора будут рассмотрены в данной Главе позднее в контексте директивы #define. Оператор defined также будет позже рассмотрен в данной Главе в Разделе «Директивы #if, #elif, #else и #endif».

«Прагма» это «прагматическая», или практическая, инструкция компилятору С. Прагмы в исходном файле С обычно используются для управления действиями компилятора над конкретной частью программы без воздействия на программу в целом. (Синтаксис прагм рассмотрен в Разделе 8.6.) Однако, доступность и предназначение отдельных прагм определяется конкретной реализацией компилятора. Информацию об использовании и действии конкретных прагм можно найти в Вашем Руководстве по компилятору.

Директива #define обычно используется для организации связи содержательных идентификаторов с константами, ключевыми словами и часто используемыми операторами и выражениями. Представляющие константы идентификаторы называются «объявленными константами». Представляющие операторы или выражения константы называются «макросами».

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

Директива #undef удаляет определение идентификатора. После удаления определения идентификатор можно переопределить другим значением. Директивы #define и #undef рассматриваются соответственно в Разделах 8.2.2 и 8.2.3.

Практически можно выделить два типа макросов. «Объектные» макросы не принимают аргументов, а «функциональные» определены таким образом, что принимают аргументы, и выглядят и действуют подобно вызовам функций. Макросы не генерируют действительные вызовы функций, поэтому программа будет работать быстрее, если заменить вызовы функций макросами. Однако, макросы могут создать свои проблемы, если не подойти к их определению и использованию со всей тщательностью. В определении макросов с аргументами возможно придется воспользоваться скобками для установления надлежащего порядка проведения вычислений в выражениях. Кроме того, макросы могут некорректно обработать выражения с побочными эффектами. Дополнительную информацию можно увидеть в примерах Раздела 8.2.2, «Директива #define».

Операторы предпроцессора

Есть три специфических оператора предпроцессора: один представлен знаком номера (#), другой удвоенным знаком номера (##), а третий словом defined. «Строковый» оператор (#), который предшествует имени формального параметра макро, заставляет предпроцессор заключить соответствующий действительный аргумент в строковые цитатные скобки. Оператор «вставки лексем» (##) позволяет осуществить слияние лексем, заданных в качестве действительных аргументов, в форму другой лексемы. Эти два оператора используются в контексте директивы #define и описаны в Разделах 8.2.2.1 и 8.2.2.2.

И, наконец, оператор defined упрощает написание составных выражений в некоторых директивах макро. Он используется при условной компиляции и поэтому рассмотрен в Разделе 8.4.1 «Директивы #if, #elif, #else и #endif».

Директива #define

Синтаксис: #define идентификатор текст-замены #define идентификатор(список-параметров) текст-замены

Директива #define заменяет все появления «идентификатор» в исходном файле на «текст-замены». Идентификатор заменяется только тогда, когда он формирует лексему. (Лексемы описаны в Главе «Элементы языка С» и «Кратком обзоре синтаксиса».) Например, идентификатор не будет заменен, если он появляется в строке или как часть более длинного идентификатора.

Если после идентификатора следует список параметров, то директива #define заменит каждое появление идентификатор(список-параметров) на версию аргумента текст-замены в которой формальные параметры заменены на действительные аргументы.

Аргумент «текст-замены» состоит из ряда лексем, таких как ключевые слова, константы или полные операторы. Один или несколько разделительных символов должны отделять текст-замены от идентификатора (или от закрывающей скобки списка параметров). Эти разделительные символы не считаются частью текста-замены, также как и любые разделительные символы, которые следуют за последней лексемой текста. Текст, занимающий более одной строки, может быть продолжен на следующей строке, если до символа перехода на новую строку поместить знак обратного деления (\).

Аргумент текст-замены может быть пустым. Выбор этой опции удаляет все появления идентификатора из исходного файла. Однако, идентификатор все еще считается определенным и дает значение 1 при его проверке директивой #if (рассматривается в Разделе 8.4.1).

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

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

Действительные аргументы, которые следуют за появлениями идентификатора в исходном файле совпадают с соответствующими формальными параметрами списка параметров. Каждый формальный параметр в тексте замены, которому не предшествует оператор # или ##, или за которым следует оператор ##, будет заменен соответствующим действительным аргументом. Все макросы действительного аргумента будут раскрыты до замены ими формального параметра. (Операторы # и ## описаны в Разделах 8.2.2.1 и 8.2.2.2.) Список действительных аргументов должен иметь столько аргументов, сколько их в списке параметров.

Если имя определенного макро появляется в тексте замены (даже в результате раскрытия другого макро), то оно не раскрывается.

Аргументы с побочными эффектами иногда вынуждают макро выдавать неожиданные результаты. Заданный формальный параметр может появиться в тексте замены несколько раз. Если формальный параметр заменяется выражением с побочным эффектом, то выражение, с его побочным эффектом, будет вычисляться несколько раз. (См. Пример 4 в Разделе 8.2.2.2, «Оператор вставки лексем».

Знак номера или строковый оператор (#) используется только с макросами, которые принимают аргументы. Если в определении макро он предшествует формальному параметру, то передаваемый при вызове макро действительный аргумент заключается в цитатные скобки и обрабатывается как строковый литерал. Затем строковый литерал заменяет каждое появление комбинации строкового оператора и формального параметра в определении макро. Предшествующие первой лексеме действительного аргумента разделительные символы и следующие за последней лексемой действительного аргумента разделительные символы игнорируются. В результирующем строковом литерале разделительные символы между лексемами действительного аргумента сокращаются до одного. Следовательно, если между двумя лексемами действительного аргумента есть комментарий, он будет сокращен до одного разделительного символа. Результирующий строковый литерал автоматически сливается с любым соседним строковым литералом от которого он разделен лишь разделительным символом. Более того, если аргумент содержит символ, который обычно требует управляющей последовательности для использования в качестве строкового литерала — например, цитатная скобка («) или знак обратного деления (\) — то перед этим символом будет автоматически вставлен необходимый для управляющей последовательности знак обратного деления. Следующий пример показывает определение макро, которое содержит строковый оператор, и функцию main, которая вызывает это макро:

#define stringer(x) printf(#x «\n») main()

В результате предпроцессорной обработки получим следующий код:

printf("I will be in quotes in the printf function call" "\n"); printf("\"I will be in quotes when printed to the screen\"" "\n"); printf(This: \\\" prints an escaped double quote mark");

При работе программы на экран выведется следующий текст:

I will be in quotes in the printf function call "I will be in quotes when printed to the screen" This: \" prints an escaped double quote mark

Оператор вставки лексем (##)

Удвоенный знак номера или оператор «вставки лексем» (##) используется и в объектный и в функциональных макро. Он позволяет объединять отдельные лексемы в единую лексему и следовательно не может быть первой или последней лексемой в определении макро.

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

В данном примере определяется идентификатор WIDTH как целая константа 80, в терминах WIDTH определяется LENGTH и целая константа 10. Каждое появление LENGTH заменяется на (WIDTH + 10). В свою очередь, каждое появление WIDTH + 10 заменяется выражением (80+10).

#define WIDTH 80 #define LENGTH (WIDTH + 10)

Замыкающие WIDTH + 10 скобки очень важны, т.к. они управляют интерпретацией операторов, подобных следующему:

var = LENGTH * 20;

После стадии предпроцессорной обработки этот оператор будет выглядеть так:

var = (80 + 10) * 20;

что даст в результате 1800. Без скобок результат будет 280:

var = 80 + 10 * 20;

В данном примере определяется идентификатор FILEMESSAGE. Его определение расширяется на вторую строку использованием символа обратного деления, за которым следует символ новой строки.

#define FILEMASSAGE "Attempt to create file \ failed because of insufficient space"

В данном примере определяются три идентификатора: REG1, REG2 и REG3. REG1 и REG2 определяются как ключевое слово register. Определение REG3 пусто, поэтому из исходного файла будет удалены все появления REG3. Эти директивы можно использовать для того, чтобы убедиться в том, что наиболее важные переменные программы (объявленные с REG1 и REG2) заданы с классом хранения register. (Расширенную версию данного примера можно найти при рассмотрении директивы #if в Разделе 8.4.1.)

#define REG1 register #define REG2 register #define REG3

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

((x) > (y)) ? (x) : (y)

Директива #define (C/C++)

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

Синтаксис

#define маркер-строкаидентификатора
#define идентификатор (идентификатор opt, . ,identifier opt)token-string opt

Замечания

Директива #define приводит компилятору заменить строку маркера для каждого вхождения идентификатора в исходном файле. Идентификатор заменяется только в том случае, если он формирует маркер. То есть идентификатор не заменяется, если он отображается в комментарии, в строке или в составе более длинного идентификатора. Дополнительные сведения см. в разделе «Токены».

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

Без #define строки маркера удаляется вхождения идентификатора из исходного файла. Идентификатор остается определенным и может быть проверен с помощью #if defined директив и #ifdef инструкций.

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

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

Формальные параметры в списке разделяются запятыми. Все имена в списке должны быть уникальными, и список должен быть заключен в скобки. Пробелы не могут разделять идентификатор и открываемую скобку. Используйте объединение строк — поместите обратную косую косую черту ( \ ) непосредственно перед новым символом — для длинных директив в нескольких исходных строках. Область формального имени параметра распространяется на новую строку, которая заканчивается строкой токена.

Если макрос определен во второй форме синтаксиса, последующие текстовые экземпляры, за которыми находится список аргументов, указывают на вызов макроса. Фактические аргументы, следующие за экземпляром идентификатора в исходном файле, соответствуют соответствующим формальным параметрам в определении макроса. Каждый формальный параметр в строке токена, который не предшествует строковой (), charizing ( #@ # ), или оператор вставки маркеров ( ## ) или не следует ## оператору, заменяется соответствующим фактическим аргументом. Перед заменой директивой формального параметра все макросы в фактическом аргументе разворачиваются. (Операторы описаны в разделе Операторы препроцессора.)

В следующих примерах макросов с аргументами показана вторая форма синтаксиса #define :

// Macro to define cursor lines #define CURSOR(top, bottom) (((top)  

Аргументы с побочными эффектами иногда приводят к тому, что макросы дают непредвиденные результаты. Заданный формальный параметр может отображаться несколько раз в строке токена. Если этот формальный параметр заменяется выражением с побочными эффектами, выражение с такими эффектами может вычисляться несколько раз. (См. примеры в разделе Оператор вставки токенов (#).)

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

Если имя определяемого макроса происходит в строке маркера (даже в результате другого расширения макроса), он не расширяется.

Второй #define для макроса с тем же именем создает предупреждение, если только вторая последовательность маркеров не идентична первой.

Блок, относящийся только к системам Майкрософт

Если новое определение синтаксически совпадает с исходным, Microsoft C и C++ позволяют переопределить макрос. Другими словами, два определения могут иметь разные имена параметров. Это поведение отличается от ANSI C, которое требует, чтобы два определения были лексически идентичны.

Например, следующие два макроса идентичны, за исключением имен параметров. ANSI C не разрешает такое переопределение, но Microsoft C/C++ компилирует его без ошибок.

#define multiply( f1, f2 ) ( f1 * f2 ) #define multiply( a1, a2 ) ( a1 * a2 ) 

С другой стороны, следующие два макроса неидентичны и приводят к выдаче предупреждения в Microsoft C и C++.

#define multiply( f1, f2 ) ( f1 * f2 ) #define multiply( a1, a2 ) ( b1 * b2 ) 

Завершение блока, относящегося только к системам Майкрософт

В этом примере показана директива #define :

#define WIDTH 80 #define LENGTH ( WIDTH + 10 ) 

Первый оператор определяет идентификатор WIDTH как целочисленную константу 80, а затем LENGTH задается в виде WIDTH и целочисленной константы 10. Каждое вхождение LENGTH заменяется на ( WIDTH + 10 ). В свою очередь, каждое вхождение WIDTH + 10 заменяется выражением ( 80 + 10 ). Скобки вокруг WIDTH + 10 имеют важное значение, поскольку управляют интерпретацией в операторах, например в следующем:

var = LENGTH * 20; 

После этапа предварительной обработки этот оператор принимает следующий вид:

var = ( 80 + 10 ) * 20; 

что равно 1800. Без скобок результат будет следующим:

var = 80 + 10 * 20; 

Блок, относящийся только к системам Майкрософт

Определение макросов и констант с параметром компилятора /D имеет тот же эффект, что и при использовании директивы предварительной обработки #define в начале файла. С помощью параметра /D можно определить до 30 макросов.

Завершение блока, относящегося только к системам Майкрософт

Урок №22. Директивы препроцессора

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

Оглавление:

  1. Директива #include
  2. Директива #define
  3. Макросы-объекты с текст_замена
  4. Макросы-объекты без текст_замена
  5. Условная компиляция
  6. Область видимости директивы #define

Директива #include

Вы уже видели директиву #include в действии. Когда вы подключаете файл с помощью директивы #include, препроцессор копирует содержимое подключаемого файла в текущий файл сразу после строки с #include. Это очень полезно при использовании определенных данных (например, предварительных объявлений функций) сразу в нескольких местах.

Директива #include имеет две формы:

#include , которая сообщает препроцессору искать файл в системных путях (в местах хранения системных библиотек языка С++). Чаще всего вы будете использовать эту форму при подключении заголовочных файлов из Стандартной библиотеки C++.

#include "filename" , которая сообщает препроцессору искать файл в текущей директории проекта. Если его там не окажется, то препроцессор начнет проверять системные пути и любые другие, которые вы указали в настройках вашей IDE. Эта форма используется для подключения пользовательских заголовочных файлов.

Директива #define

Директиву #define можно использовать для создания макросов. Макрос — это правило, которое определяет конвертацию идентификатора в указанные данные.

Есть два основных типа макросов: макросы-функции и макросы-объекты.

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

Макросы-объекты можно определить одним из следующих двух способов:

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

Верхнее определение не имеет никакого текст_замена , в то время как нижнее — имеет. Поскольку это директивы препроцессора (а не простые стейтменты), то ни одна из форм не заканчивается точкой с запятой.

Макросы-объекты с текст_замена

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

Рассмотрим следующий фрагмент кода:

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

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

1.8.1. Директива #include

Директива #include включает в текст программы содержимое указанного файла. Эта директива имеет две формы:

#include "имя файла" #include

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

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

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

1.8.2. Директива #define

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

Директива #define имеет две синтаксические формы:

#define идентификатор текст #define идентификатор (список параметров) текст

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

#define WIDTH 80 #define LENGTH (WIDTH+10)

Эти директивы изменят в тексте программы каждое слово WIDTH на число 80, а каждое слово LENGTH на выражение (80+10) вместе с окружающими его скобками.

Скобки, содержащиеся в макроопределении, позволяют избежать недоразумений, связанных с порядком вычисления операций. Например, при отсутствии скобок выражение t=LENGTH*7 будет преобразовано в выражение t=80+10*7, а не в выражение t=(80+10)*7, как это получается при наличии скобок, и в результате получится 780, а не 630.

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

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

Пример: #define MAX(x,y) ((x)>(y))?(x):(y) Эта директива заменит фрагмент t=MAX(i,s[i]); на фрагмент t=((i)>(s[i])?(i):(s[i]);

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

Например, при наличии скобок фрагмент t=MAX(i&j,s[i]||j); будет заменен на фрагмент t=((i&j)>(s[i]||j)?(i&j):(s[i]||j); а при отсутствии скобок - на фрагмент t=(i&j>s[i]||j)?i&j:s[i]||j; в котором условное выражение вычисляется в совершенно другом порядке.

1.8.3. Директива #undef

Директива #undef используется для отмены действия директивы #define. Синтаксис этой директивы следующий #undef идентификатор

Директива отменяет действие текущего определения #define для указанного идентификатора. Не является ошибкой использование директивы #undef для идентификатора, который не был определен директивой #define.

#undef WIDTH #undef MAX

Эти директивы отменяют определение именованной константы WIDTH и макроопределения MAX.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *