Макроподстановка (#define, #undef)

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

Каждая директива описывается отдельной записью и действует до переноса строки. Нельзя в одной записи использовать несколько директив. Если запись директивы получается слишком большой, то её можно разбить на несколько строк с помощью обратного слеша '\', в таком случае следующая строка будет считаться продолжением записи директивы.

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

#define identifier expression                   // беспараметрическая форма
#define identifier(par1,... par8) expression    // параметрическая форма

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

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

#define ABC               100
#define PI                3.14
#define COMPANY_NAME      "MetaQuotes Software Corp."
...
void ShowCopyright()
  {
   Print("Copyright  2001-2009, ",COMPANY_NAME);
   Print("https://www.metaquotes.net");
  }

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

Пример:

#define TWO        2
#define THREE      3
#define INCOMPLETE TWO+THREE
#define COMPLETE  (TWO+THREE)
void OnStart()
  {
   Print("2 + 3*2 = ",INCOMPLETE*2);
   Print("(2 + 3)*2 = ",COMPLETE*2);
  }
/* Результат
   2 + 3*2 = 8
   (2 + 3)*2 = 10
*/

 

Параметрическая форма #define

При параметрической форме все последующие найденные вхождения identifier будут заменены на expression с учетом фактических параметров. Например,

 // пример с двумя параметрами a и b
#define A 2+3
#define B 5-1
#define MUL(a, b) ((a)*(b))
 
double c=MUL(A,B);
Print("c=",c);
/*
выражение double c=MUL(A,B);
равносильно double c=((2+3)*(5-1));
*/
// Результат
// c=20

Обязательно заключайте параметры в круглые скобки при использовании параметров в expression, так как это позволит избежать неочевидных ошибок, которые трудно найти. Если переписать пример без использования скобок, то результат окажется совсем другой:

 // пример с двумя параметрами a и b
#define A 2+3
#define B 5-1
#define MUL(a, b) a*b
 
double c=MUL(A,B);
Print("c=",c);
/*
выражение double c=MUL(A,B);
равносильно double c=2+3*5-1;
*/
// Результат
// c=16

При использовании параметрической формы допускается не более 8 параметров.

 // правильная параметрическая форма
#define LOG(text)  Print(__FILE__,"(",__LINE__,") :",text)   // один параметр - 'text'
 
 // неправильная параметрическая форма         
#define WRONG_DEF(p1, p2, p3, p4, p5, p6, p7, p8, p9)   p1+p2+p3+p4 // более 8 параметров от p1 до p9

Директива #undef

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

Пример:

#define MACRO
 
void func1()
  {
#ifdef MACRO
   Print("MACRO is defined in ",__FUNCTION__);   
#else
   Print("MACRO is not defined in ",__FUNCTION__);
#endif
  }
 
#undef MACRO
 
void func2()
  {
#ifdef MACRO
   Print("MACRO is defined in ",__FUNCTION__);
#else
   Print("MACRO is not defined in ",__FUNCTION__);
#endif
  }
 
void OnStart()
  {
   func1();
   func2();
  }
 
/* Результат:
 MACRO is defined in func1
 MACRO is not defined in func2
*/

Смотри также

Идентификаторы, Символьные константы