.: [предыдущая | оглавление | следующая] :.

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

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

  1. включение текста из других файлов, обычно это заголовочные файлы, хотя может быть любой текстовый файл;
  2. замена одних последовательностей символов на другие, причем возможна параметризация получаемых последовательностей;
  3. условная трансляция;
  4. установка состояний компилятора для различных вариантов компиляции.

Важно отметить, что препроцессор не знает языка Си. Просто на входе текст, на выходе преобразованный текст. Препроцессором управляет специальный набор директив (операторов), которые должны начинаться с символа #. Рассмотрим основные директивы.

2.13.1. Директива #include

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

Имеются следующие формы записи директивы:
1. #include <имя файла>
2. #include "имя файла"

Первый вариант предназначен для включения файлов из системных каталогов компилятора. Например:

#include <stdio.h> //включить заголовочный файл для использования функций стандартного ввода/вывода

Второй вариант предназначен для включения файлов из текущего каталога пользователя, обычно это заголовочный файл прикладного программиста Например:

#include “my_header.h”

2.13.2 Директива #define

Директива #define определяет макрос, который обеспечивает механизм подстановки лексем без или со списком формальных параметров, подобно функциям. Рассмотрим макросы без параметров. Запишем ряд примеров:

#define HI “Какой прекрасный день!”
#define SIZE 100
#define empty
printf(HI); //подстановка в результате будет printf(“Какой прекрасный день!”);
for (i=0; i<SIZE; i++) //вместо SIZE будет подставлено 100
    printf(“empty”); //в данном случае подстановки не будет, т.к. это строка символов

Рассмотрим макроопределения со списком параметров. Рассмотрим на примере:

#define CUBE ((x) (x)*(x)*(x))
...
int n, y;
n=CUBE(y);
после подстановки препроцессор сгенерирует следующий текст:
n=((y)*(y)*(y));

Скобки при написании макроса очень важны, поскольку если бы не было скобок, то можно получить такой эффект:

n=CUBE(y+1);
тогда после подстановки будет получен следующий текст:
n=y+1*y+1*y+1; //результат совершенно другой 3y+1

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

  1. Вложение скобок и команд. Например:
    #define ERRMSG(x,str) Show(“Ошибка”,x,str)
    #define SUM(x,y) ((x)+(y))
    ...
    ERRMSG(2,”Неверный ключ”); //замещение будет нормальное Show(“Ошибка”,2, ,”Неверный ключ”);
    return SUM(f(i,j),g(k,l)); //замещение нормальное return ((f(i,j))+(g(k,l)));
  2. Соединение лексем с помощью ##. Например:
    #define VAR(i,j) (i##j)
    ...
    VAR(u,10) //в результате замены даст u10
  3. Сторонние эффекты. При использовании макросов необходимо соблюдать осторожность. Например, если взять макрос CUBE, описанный выше, то может быть следующий эффект:
    a=3;
    n=CUBE(a++);
    printf(“%d\n”,n);
В результате подстановки будет:
n=((a++)*(a++)*(a++));

После выполнения n будет 27 а=6.

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

Условная компиляция предназначена для генерации конкретного варианта текста программы и создается с помощью специальных директив: #if, #ifdef, ifndef,#else,#endif.

Рассмотрим некоторые типичные примеры:

#ifndef MyHeaderH
#define MyHeaderH
...
...
#endif

Это типичный пример создания заголовочного файла пользователя. Здесь условная компиляция позволяет не вставлять несколько раз один и тот же заголовочный файл. Первая строка (#ifndef) проверяет, не определен ли макрос MyHeaderH, если нет, то определяет его и вставляет текст файла. Если макрос MyHeaderH определен, то весь текст пропускается до #endif, тем самым он не вставляется. Другой пример:

#ifdef DOS
... //программный код для OS DOS
...
#elif define(WINDOWS)
... //программный код для OS WINDOWS
...
#elif define(UNIX)
... //программный код для OS UNIX
...
#else
... //программный код для других OS
...
#endif

В этом примере показано, как создавать программный код для разного класса операционных систем. Если при трансляции установлен макрос DOS, то будет вставлен программный код, следующий за #ifdef. Если установлен макрос WINDOWS, то будет вставлен код только для WINDOWS и т.д.

.: [предыдущая | оглавление | следующая] :.