Препроцессор

Препроцессор - это программа, которая производит некоторые (иногда весьма значительные) манипуляции с первоначальным текстом программы перед тем, как он подвергается компиляции. Будучи дословно переведенным, с английского, слово препроцессор означает предварительный обрабатыватель

Препроцессоры создают входной текст для компиляторов и могут выполнять следующие функции:

  • обработку макроопределений;
  • включение файлов;
  • "рациональную" предобработку;
  • расширение языка.

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

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

#line <константа> которая указывает компилятору, что следующая ниже строка текста имеет номер, определяемый целой десятичной константой. Команда может определять не только номер строки, но и имя файла: #line <константа> "<имя_файла>"

  • #error директива предпроцессора приводит к выдаче диагностического сообщения в виде последовательности лексем.
  • #pragma определяет действия, зависящие от конкретной реализации компилятора, и позволяет выдавать компилятору различные инструкции. Например подавление ошибок в Visual Studio будет выглядеть так:
    #include  <iostream>
    using namespace std;
    
    #pragma warning(disable :  4101 ) //подавить вывод ошибки 4101
    #pragma warning(disable :  4081 )
    void main()
    
    {
      int i;
    }
#pragma comment(lib, "Ws2_32.lib")//Директива #pragma comment вызывает добавление указанного ключа к командной строке компоновщика на этапе связывания.
  • В языке С существует возможность работы с операторами # и ##. Данные операторы используются в альянсе с директивой #define.

Оператор # превращает аргумент, которому он предшествует, в строку, заключенную в кавычки. #include <iostream>

using namespace std;
# define mkstr(s) #s
void main()
{
	cout<<mkstr(I love C);
	// Для компилятора cout<<"I love C";
}

Оператор ## используется для конкатенации (объединения) двух лексем #include <iostream>

using namespace std;
# define concat(a,b) a##b
void main()
{
	int xy=10;
	cout<<concat(x,y);
	// Для компилятора cout<<xy;
}

В языке С существуют встроенные (заранее определенные) макроимена, доступные препроцессору во время обработки. Они позволяют получить следующую информацию:

Условная компиляция

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

#if <константное_выражение>
#ifdef <идентификатор>
#ifndef <идентификатор>
#else#endif#elif

Первые три команды выполняют проверку условий, две следующие - позволяют определить диапазон действия проверяемого условия. Последняя команда используется для организации проверки серии условий. Общая структура применения директив условной компиляции такова:

#if/#ifdef/#ifndef <константное_выражение или идентификатор>
          <текст_1>
#else //необязательная директива
          <текст_2>
#endif

Конструкция#else <текст_2> не обязательна. Текст_1 включается в компилируемый текст только при истинности проверяемого условия. Если условие ложно, то при наличии директивы #else на компиляцию передается текст_2. Если директива #else отсутствует, то весь текст от #if до #endif при ложном условии опускается. Различие между формами команд #if состоит в следующем.

  • В первой из перечисленных директив #if проверяется значение константного целочисленного выражения. Если оно отлично от нуля, то считается, что проверяемое условие истинно. Например, в результате выполнения директив:
#if 5+12
       <текст_1>
#endif

текст_1 всегда будет включен в компилируемую программу.

  • В директиве #ifdefпроверяется, определен ли с помощью команды #define к текущему моменту идентификатор, помещенный после #ifdef. Если идентификатор определен, то текст_1 используется компилятором.
  • В директиве #ifndef проверяется обратное условие - истинным считается неопределенность идентификатора, т.е. тот случай, когда идентификатор не был использован в команде #define или его определение было отменено командой #undef.

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

#elif <константное_выражение>

является сокращением конструкции #else#if.

Структура исходного текста с применением этой директивы такова:

#if <константное_выражение_1>
          <текст_1>
     #elif <константное_выражение_2>
          <текст_2>
     #elif <константное_выражение_3>
          <текст_3>
     .   .   .   .
     #else
          <текст_N>
     #endif

Препроцессор проверяет вначале условие в директиве #if, если оно ложно (равно 0) - вычисляет константное_выражение_2, если оно равно О - вычисляется константное_выражение_3 и т.д. Если все выражения ложны, то в компилируемый текст включается текст для случая #else. В противном случае, т.е. при появлении хотя бы одного истинного выражения (в #if или в #elif), начинает обрабатываться текст, расположенный непосредственно за этой директивой, а все остальные директивы не рассматриваются. Таким образом, препроцессор обрабатывает всегда только один из участков текста, выделенных командами условной компиляции. A, теперь, рассмотрим несколько примеров.


Пример 1. Простая директива условного включения.

#ifdef ArrFlg
      int Arr[30];
   #endif

Если во время интерпретации директивы определено макроопределение ArrFlg, то приведенная запись дает генерацию выражения

 int Arr[30];

В противном случае не будет генерировано ни одно выражение.


Пример 2.

#include <iostream>
using namespace std;
#define ArrFlg 1
void main ()
{
   #ifdef ArrFlg
       int Arr[30];
   #else
       cout << "Array is not defined!";
   #endif
}

Пример 3. Директива условного включения с альтернативой

#if a+b==5
      cout << 5;
   #else
      cout << 13;
   #endif

Если выражение a+b==5 представляет величину, отличную от 0, то будет сгенерирована команда cout « 5;, в противном случае будет сгенерирована команда cout « 13;.


Пример 4. Составная директива условного включения

#include <iostream>
using namespace std;
//++++++++++++++++++++++
#define Alfa 5
//++++++++++++++++++++++
#if Alfa*5>20
      void main ()
	//++++++++++++++++++++++
	#if Alfa==4
		int Arr[2];
	#elif Alfa==3
		char Arr[2];
	#else
		{
	#endif
	//++++++++++++++++++++++
	#if 0
		cout<<"One";
	#else
		cout<<"Two";
	#endif
//++++++++++++++++++++++
#else
   cout<<"Test";
#endif
//++++++++++++++++++++++
}

Интерпретация приведенной записи приведет к генерации

void main ()
   {
      cout<<"Kaja";
   }

Создание Demo - версии программы

Ниже показано как можно включать или исключать куски кода. Раз куска кода нет - сооответсвенно и взламывать нечего. Вместо этого куска кода соответственно можно подставить всплывающее меню с предложением о покупке.

#include  <iostream>
using namespace std;
#define ABC1
void main()
{
#ifdef ABC1
	cout << "first part" << endl;
#else 
        cout << "second part" << endl;
#endif
}
PQ VPS сервера в 28+ странах.