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

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

  • автор:

Директива #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 идентификатор текст_замена

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

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

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

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

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

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

#define идентификатор последовательность_символов

Определяемый идентификатор еще называют препроцессорным символом. Используем директиву #define:

#include #define N 23 int main(void) < int x = N; printf("Number: %d", x); // Number: 23 return 0; >

Здесь определен один идентификатор N. В программе, где встречается этот идентификатор, он будет заменяться на число 23. Например, строка

int x = N;

после обработки препроцессором будет иметь следующий код

int x =23;

Более сложный пример

#include #define BEGIN < #define END >#define N 23 int main(void) BEGIN int x = N; printf("Number: %d", x); // Number: 23 return 0; END

Здесь определены три идентификатора BEGIN, END, N. В итоге все вхождения последовательности символов "BEGIN" будут заменяться на открывающую фигурную скобку, а "END" - на закрывающую, а символ "N" на число 23.

Таким образом, после обработки препроцессора функция main приобретет следующий вид:

int main(void)

#define также может определять более сложные выражения. Например:

#include #define ADD(a,b) (a+b) int main(void) < int n1 = 10; int n2 = 5; printf("%d + %d = %d", n1, n2, ADD(n1, n2)); // 10 + 5 = 15 >

В данном случае выражение ADD(a,b) будет заменяться операцией сложения двух чисел (a + b)

Особенно удобно использовать директиву #define для определения размеров массивов:

#include #define N 4 int main(void) < int numbers[N] = ; for(int i=0; i return 0; >

В данном случае если мы захотим глобально поменять размер массива, то достаточно изменить значение N в директиве define.

Следует учитывать, что директива препроцессор не заменяет последовательности символов в двойных и одинарных кавычках и в комментариях:

#include #define N 4 int main(void) < char symbol = 'N'; printf("%c \n", symbol); // N printf("N"); //N return 0; >

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

#define REAL long double

Директива #undef

В процессе работы мы можем многократно определять новое значение для одного идентификатора:

#define N 23 #define N 32 #define N 55

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

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

#include #define STRING "Good morning \n" int main(void)

Определение крнстанты в командной строке

При использовании компилятора GCC мы можем с помощью флага -D определить константу аналогично, как это делается с помощью директивы %define . Для этого применяется выражение

gcc -D ИЛЕНТИФКАТОР=ЗНАЧЕНИЕ

Например, определим следующую программу:

#include int main(void)

В данном случае переменной x присваивается значение некоторого идентификатора NUMBER. Обратите внимание, что он нигде не определен, и среда разработки или текстовый редактор могут подчеркивать этот идентификатор как некорректный.

Теперь скомпилируем данную программу с помощью следующей команды (допустим, исходный файл называется "app.c"):

gcc -D NUMBER=25 app.c -o app.exe & app.exe

В данном случае мы определяем препроцессорный символ NUMBER и присваиваем ему значение 25. В итоге программа успешно скомпилируется, а переменная x получит значение 25.

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

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

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

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

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

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

См. также

Обратная связь

Были ли сведения на этой странице полезными?

Обратная связь

Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделе https://aka.ms/ContentUserFeedback.

Отправить и просмотреть отзыв по

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

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