Разделы

2.11. Препроцесс

Директивы препроцессора - строки кода, начинающиеся с символа решетки #. Эти строки не являются программными выражениями, и обрабатываются препроцессором. Препроцессор осуществляет обработку кода до компиляции и выполняет все подстановки.

Директива препроцессора располагается в одной строке и заканчивается символом конца строки. В конце директивы не требуется точка с запятой (;). Чтобы расширить директиву на более чем одну строку - нужно поместить в конце строки обратный слеш (\).

Макроопределения (#define, #undef)

Чтобы задать определение для препроцессора, используется директива #define, ее синтаксис:

#define identifier replacement

Когда препроцессор встречает эту директиву, он заменяет все встреченные identifier на replacement далее по тексту программы. replacement может быть выражением, конструкцией, блоком, или вообще ничем. Препроцессор просто заменяет identifier на replacement.

#define SIZE 100
$some_size = SIZE;
$some_more_size = SIZE + 10;

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

$some_size = 100;
$some_more_size = 100 + 10;

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

#define getsum(a,b) a+b

Это заменит все встреченные getsum с двумя аргументами на выражение замены, и каждый аргумент на его значение.

#define getsum(a,b) a+b

$a = getsum(1,2);
//после подстановки станет
$a = 1+2;

Заданный макрос не зависит от структуры блоков. Он существует до тех пор, пока не будет отменен с помощью директивы #undef

#define SIZE 100
$some_size = SIZE;
#undef SIZE
#define SIZE 200
$some_more_size = SIZE;

сгенерирует такой же код, как

$some_size = 100;
$some_more_size = 200;

Функциональные макросы так же поддерживают два специальных оператора (#, ##) в выражении замены.

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

#define str(x) #x
print(str(test));

будет преобразовано в

print("test");

Оператор ## соединяет два аргумента, не оставляя пробелов между ними:

#define concat(a,b) a ## b
concat(pri, nt)("text");

Будет преобразовано в

print("text");

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

Условные включения (#ifdef, #ifndef, #if, #endif, #else)

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

#ifdef позволяет участку кода скомпилироваться только в том случае, если макрос, указанный в параметре, определен, в не зависимости от его значения.ъ

#ifdef SIZE
a = SIZE;
#endif

В этом примере, строка кода a = SIZE; будет скомпилирована только в том случае, если макрос SIZE был предварительно определен с помощью #define, независимо от его значения.

#ifndef является противоположностью #ifdef - код будет скомпилирован только если макрос не определен.

#ifndef SIZE
#define SIZE 100
#endif
a = SIZE;

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

Директивы #if, #else служат для задания специальных условий. Условные выражения должны включать только константные значения, включая макросы.

#if SIZE>200
#undef SIZE
#define SIZE 200

#else

#if SIZE<50
#undef SIZE
#define SIZE 50
#endif

#else
#undef SIZE
#define SIZE 100
#endif

$a = SIZE;

Поведение #ifdef и #ifndef так же может быть достигнуто с помощью операторов defined() и !defined() в директиве #if

#if defined(ARRAY_SIZE)
#define SIZE ARRAY_SIZE
#else
#if !defined(BUFFER_SIZE)
#define SIZE 128
#else
#define SIZE BUFFER_SIZE
#endif 
#endif 

Включения файлов кода (#include)

Когда препроцессор встречает директиву #include, он заменяет ее на содержимое файла, указанного в аргументе.

#include "file"

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