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

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

Синтаксис

#define identifier token-stringнеоб

#define identifier( identifierнеоб, ... , identifieropt ) token-stringнеоб

Заметки

Директива #define указывает компилятору, что нужно подставить строку, определенную аргументом token-string, вместо каждого заданного аргументом identifier идентификатора в исходном файле. Идентификатор, определенный аргументом identifier, заменяется, только если он формирует токен. Это означает, что identifier не заменяется, если находится в комментарии, строке или является частью более длинного идентификатора. Дополнительные сведения см. в разделе Токены C++.

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

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

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

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

Формальные параметры в списке разделяются запятыми. Все имена в списке должны быть уникальными, и список должен быть заключен в скобки. Между открывающей скобкой и identifier не должно быть пробелов. Для длинных директив, расположенных в нескольких строках исходного кода, используйте объединение строк: непосредственно перед символом новой строки разместите обратную косую черту (\). Область имени формального параметра расширяется до новой строки, завершающей token-string.

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

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

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

// Macro to get a random integer with a specified range 
#define getrandom(min, max) \
    ((rand()%(int)(((max) + 1)-(min)))+ (min))

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

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

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

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

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

Если новое определение синтаксически совпадает с исходным, 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 )

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

В следующем примере иллюстрируется директива #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;

что равно 280.

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

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

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

См. также

Ссылки

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