Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Рацеев С.М. Программирование на языке Си.pdf
Скачиваний:
369
Добавлен:
23.03.2016
Размер:
1.65 Mб
Скачать

10. ДИРЕКТИВЫ ПРЕПРОЦЕССОРА

Препроцессор – программа, которая производит некоторые манипуляции с исходным текстом программы перед тем, как он подвергнется трансляции. Препроцессор запускается автоматически при компиляции программы. Все директивы препроцессора начинаются со знака #. После директив препроцессора точка с запятой не ставится.

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

Директива #include включает в текст программы содержимое указанного файла. Эта директива имеет две формы записи:

#include "имя файла"

#include <имя файла>

Если имя файла указано в кавычках, то препроцессор осуществляет поиск файла в соответствии с заданным маршрутом, а при его отсутствии – в текущем каталоге. Если имя файла задано в угловых скобках, которые используются для файлов стандартной библиотеки, то поиск файла производится в стандартных директориях операционной системы, задаваемых командой PATH.

Директива #include очень часто используется для включения следующих заголовочных файлов стандартной библиотеки: stdio.h (стандартная библиотека ввода-вывода), stdlib.h (функции общего назначения), string.h (библиотека функций, оперирующих со строками) и т.д.

10.2. Директива #define

Данная директива служит для замены констант, операторов или выражений некоторыми идентификаторами. Идентификаторы, которые заменяют строковые или числовые константы, называют именованными константами. Идентификаторы, заменяющие фраг-

162

менты программ, называют макроопределениями, причем макроопределения могут иметь аргументы.

Директива #define имеет две формы записи: #define идентификатор текст

#define идентификатор (список параметров) текст

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

Если же текст отсутствует, тогда все экземпляры идентификатора удаляются из программы.

Макроопределение без параметров обрабатывается так же, как и именованная константа. Например, можно записать

#define FOREVER for(; ; )

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

чеством параметров.

В качестве первого примера приведем макроопределение с одним параметром для нахождения квадрата числа:

#include <stdio.h>

#define SQR(x) ((x) * (x)) int main( )

{

printf("%d\n", SQR(5)); printf("%f\n", SQR(SQR(0.12))); return 0;

}

163

Поясним значения круглых скобок при макроопределении ((x) * (x)). Конечно, можно было бы записать

#define SQR(x) x * x

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

r = SQR(a + b);

тогда после работы препроцессора этот код развернется в следующий код:

r = a + b * a + b;

Поэтому будет корректнее записать

#define SQR(x) ((x) * (x))

В качестве второго примера приведем макроопределение с двумя параметрами для нахождения максимально значения из данных параметров:

#include <stdio.h>

#define MAX(x, y) ((x) > (y) ? (x) : (y)) int main( )

{

int x = 1, y = 2;

int max = MAX(x, y); return 0;

}

10.3. Директива #undef

Директива #undef используется для отмены действия директивы #define. Синтаксис данной директивы следующий:

#undef идентификатор

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

164

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

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

Директивы #ifdef, #else и #endif. В общем виде оператор #ifdef выглядит следующим образом:

#ifdef идентификатор последовательность операторов

#else

другая последовательность операторов

#endif

При этом директива #else может отсутствовать. В директиве #ifdef проверяется, определен ли с помощью директивы #define к текущему моменту идентификатор, помещенный после #ifdef. Если идентификатор определен, то выполняется последовательность соответствующих операторов, в противном же случае выполняется последовательность операторов, помещенных после директивы

#else.

Часто, например при отладке программ, требуется иметь возможность включать или исключать некоторые фрагменты программы на этапе компиляции:

#include<stdio.h> #define DEBUG int main()

{

#ifdef DEBUG printf("Отладочная часть");

#endif return 0;

}

165

Директива #ifndef. Директива #ifndef, как и # ifdef, используется совместно с директивами #else и #endif. Данная директива обрабатывает условие, если идентификатор не определен, то есть она представляет отрицание директивы #ifdef. Эта директива часто применяется для определения констант при условии, что они не были определены ранее. Например:

#ifndef SIZE #define SIZE 100

#endif

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

Приведем пример. Разобьем программу на два файла: заголовочный файл file.h и файл реализаций array.c. В заголовочном файле file.h с помощью директивы #include подключим все необходимые нашей программе файлы стандартной библиотеки языка Си. В нашем случае это файлы <stdio.h> и <stdlib.h>. Также в файле file.h определим все необходимые константы, прототипы функций и т.д. Файл реализаций array.c подключает с помощью директивы #include "file.h" заголовочный файл file.h и содержит реализацию программы.

// заголовочный файл file.h. #ifndef FILE_H

#define FILE_H

//заголовочные файлы

#include <stdio.h> #include <stdlib.h>

//константы

#define SIZE 100

166

//определение типов typedef unsigned long UINT;

//прототипы функций

void Init(UINT *, int); void Print(UINT *, int);

#endif

//файл реализаций array.c #include "file.h"

//инициализация элементов массива void Init(UINT *a, int n)

{

int i;

for(i = 0; i < n; i++) a[i] = rand() % 100;

}

//вывод элементов массива на экран void Print(UINT *a, int n)

{

int i;

for(i = 0; i < n; i++) printf("%u ", a[i]);

}

int main()

{

UINT a[SIZE]; Init(a, SIZE); Print(a, SIZE); return 0;

}

167