Действия, основанные на включении в основную программу текстов из файлов данных, формировании макроопределений и на условных переходах называются входными преобразованиями, а программа, реализующая эти преобразования и обычно являющаяся составной частью компилятора, называется препроцессором.
Директивы препроцессора начинаются знаком # перед которым в строке могут быть пробельные символы.
Включающие директивы
Имеют вид
#include <name>
или
#include “name”
Интерпретация директивы #includeприводит к включению в месте ее появления содержимого файла name. Если имя файла заключено в угловые скобки, то поиск файла ограничивается каталогом файлов, определенным с помощью компилятора. Если имя заключено в кавычки, то предварительно проводится его поиск в текущем файле. Если указано полное имя то поиск не производится, а сразу включается содержимое указанного файла.
Чаще всего эта директива используется для включения стандартных файлов. Например, функции ввода-вывода находятся в файле stdio.h.
Пример
Пусть в файле eva.txt содержится текст
{ printf(“hi”);
то программа
#include <stdio.h>
voidmain ()
#include “eva.txt”
}
будет выводить на экран hi
Определяющие директивы
Макросредства позволяют поставить в соответствие идентификатору текстовую строку; все последующие появления в тексте этого идентификатора заменяются соответствующей строкой. Макроопределения препроцессора имеют 2 вида
простую
#define идентификатор строка-замены
или параметризованную
#define идентификатор (x1,x2, ..., xn) строка-замены
Макроопределение в первой форме обеспечивает замену каждого идентификатора строкой-замены для всех идентификатором расположенных после макроопределения.
#definesize 200
При изменении размера в дальнейшем можно будет поменять только эту директиву.
Если значение определяемой константы задается выражением, то имеет смысл заключать его в скобки.
Например, рассмотрим ситуацию, когда константа определена следующим образом
#definee 5+10
а где-то в программе написано выражение е*10. то значение этого выражения будет не 150, как, скорее всего, ожидается, а 5+10*10=105
Макросы могут применяться и для ограниченных изменений в синтаксисе языка.
Например, для схожести программы на си с программой на паскале, можно сделать
#define begin {
#define end }
void main()
begin
...
end
Во втором варианте, перед заменой идентификатора строкой замены при каждом появлении формального параметра в строке замены вместо него подставляется соответствующий фактический параметр.
Пример
#define max(a,b) ((a)>(b)?(a):(b))
void main()
{ int x=5, y=6;
printf(“%d”,max(x,y));
}
интерпретация приведенной записи приведет к генерации выражения
void main()
{ int x=5, y=6;
printf(“%d”, x>y?x:y));
}
Макроопределения, образованные с помощью директивы define могут быть аннулированы с помощью директивы
#undef
Пример 1
Многократное развитие макровызовов
#include <stdio.h>
#define alfa char beta
#define beta c=’d’
void main()
{
alfa; /* интерпретация этой записи приведет к записи charc=’d’;*/
printf(“%c”,c)
}
в результате работы программы выведется символ ‘d’
Пример 2
Игнорирование макровызовов в директивах #define
#include <stdio.h>
#define beta c=’d’
#define alfa char beta
#undefbeta
voidmain()
{
alfa; /* интерпретация этой записи приведет к записи charbeta;*/
printf(“%c”,c)
}
В результате компилятор выдаст сообщение об ошибке, так как переменная с не определена.
Пример 3
Игнорирование макровызовов в символьных и строчных литералах
#include <stdio.h>
#define c 4
#define alfa 35
void main()
{
char a=’c’;
printf(“alfa\n%c”,a)
}
в результате напечатается
alfa
c