Препроцессорная обработка (макрообработка) — это преобразование текста путем замены препроцессорных переменных их значениями и выполнения препроцессорных операторов (директив препроцессора).
В общем случае препроцессорные средства включают:
- определение препроцессорных переменных и присвоенных им значений;
- средства управления просмотром преобразуемого текста;
- правила подстановки значений макропеременных.
Определение препроцессорной переменной часто называют макроопределением или макросом, а подстановку ее значения в обрабатываемый текст — макрорасширением.
Макрообработка состоит в последовательном просмотре исходного текста и выделения в нем лексем — сканировании текста. Если выделенная лексема является препроцессорной переменной, она заменяется на свое значение, т.е. строится макрорасширение. Если встречается препроцессорная директива, то она выполняется. Лексемы, не являющиеся препроцессорными переменными или директивами, переносятся в выходной текст без изменения. Результатом такой обработки является текст, не содержащий препроцессорных директив и препроцессорных переменных. Если исходный текст был программой на C или C++, то после макрообработки должен быть получен синтаксически правильный текст на C или C++.
Как правило, строковые литералы (строки в кавычках) рассматриваются препроцессором как отдельные лексемы и переносятся в выходной текст без изменения.
Препроцессор обычно обеспечивает возможность включения в программу исходных текстов из других файлов, в Си/Си++ это выполняется по директиве
# include имя файла
Если включаемый файл находится в одном из оглавлений, указываемых в установках интегрированной среды в пункте options-directory-include, где можно указать несколько путей, разделяя их точкой с запятой, имя файла заключается в уголковые кавычки, например
# include <iostream.h> в остальных случаях указывается полный путь к включаемому файлу в кавычках:
# include “c:\myinclud\includ1.h”
При включении файла на место директивы #include вставляется текст из этого файла, а последующие строки исходного файла сдвигаются вниз, после чего вставленный текст сканируется препроцессором.
Отметим, что директивы препроцессора всегда записывается с новой строки и первым символом директивы должен быть знак #, которому могут предшествовать только пробелы и знаки табуляции. Концом текста директивы служит конец строки. Если директива не помещается в одной строке, в конце строки ставится знак \ и директива продолжается на следующей строке. Количество строк продолжения не ограничивается.
3.2. Препроцессорные переменныеПрепроцессорная переменная (макроимя) объявляется директивой
# define идентификатор значение
Например:
# define L_NAME 6
# define END_FORMULA ‘;’
# define DEBUG
Если объявленное таким способом макроимя встретится в последующем тексте программы, оно будет заменено на соответствующее значение:
char namevar [L_NAME]; // эквивалентно char namevar[6];
if ( c != END_FORMULA ) ...... // if ( c != ‘;’).....
Переменная DEBUG объявлена. но не имеет значения. В последующем тексте можно проверять. объявлено или нет это имя, и в зависимости от результата проверки включать или не включать в программу некоторые операторы.
Объявленное в define макроимя известно препроцессору от точки его объявления до конца файла или пока не встретится директива
# undef имя
Например, #undef DEBUG
Если в последующем тексте встретится имя DEBUG, оно будет рассматриваться как обычное, а не препроцессорное имя.
Имеется ряд предопределенных макроимен, предусмотренных стандартами на языки C и C++, в том числе:
_ _LINE_ _ - номер строки в исходном файле,
_ _FILE_ _ - имя обрабатываемого файла,
_ _DATE_ _ - дата начала обработки препроцессором,
_ _TIME_ _ - время начала обработки,
_ _STDC_ _ - программа должна соответствовать стандарту ANSI.
_ _cplusplus - компилировать программу в соответствии с синтаксисом Си++,
_ _PASCAL_ _ - последующие имена по умолчанию имеют тип “имя языка Pascal”
Предопределенные имена нельзя объявлять в #define или отменять в #undef.
Макросы FILE, DATE и TIME могут использоваться в сообщениях, выдаваемых в начале программы для указания, какая версия программы используется, например,
cout << “\n Файл “ << __FILE__ << “ От “ << __DATE__ << “\n”;
Макрос PASCAL применяется при описании функций, предназначенных для использования в программах, написанных на языке Pascal, а также функций, вызываемых операционной системой Windows.
При программировании на Си директивы типа
#define MAX_LEN 80 обычно применяются для задания именованных констант. Введение описателя const в последние версии Си и в Си++ позволяет определять именованные константы так же, как и обычные переменные. 3.3. Макроопределения (макросы)
Рассмотренный выше вариант директивы #define — частный случай. Полный синтаксис этой директивы имеет вид:
# define идентификатор(параметры) список_замены
Параметры задаются их именами, список замены - это текст на C, C++, содержащий имена параметров, например:
# define MAX(a,b) ( (a) > (b) )? (a) : (b)
# define PRINT(a) cout<< #a<<“= “<<(a)<<endl;
Если в области действия этих макроопределений встретится текст
x = MAX( y + p, s);
то он будет заменен на
x = ( (y + p) > (s)) ? (y + p) : (s);
оператор PRINT(x) будет заменен на
cout<<“x”<<“= “<<(x)<<endl;
Знак # перед именем параметра означает, что значение аргумента рассматривается как строковый литерал. Если между двумя параметрами в макрорасширении стоят знаки ##, то значения аргументов сцепляются по правилу сцепления строк, например,
# define VAR(a,b) ( a##b )
x = d [ VAR(i,j)]; // x = d [ ( ij )];
Использование макросов в ряде случаев позволяет сократить исходный текст программы и сделать его более наглядным. Например, если поместить в файл-заголовок макросы
#if defined(__cplusplus)
# define _PTRDEF(name) typedef name * P##name;
# define _REFDEF(name) typedef name & R##name;
# define _STRUCTDEF(name) struct name; \
_PTRDEF(name) \
_REFDEF(name) \
#endif то мы получим возможность одной строкой программы
_STRUCTDEF(MyStruct) объявить имя структурного типа MyStruct, указатель на этот тип PMyStruct и тип ссылки на него RMyStruct, т.е. получить в выходном тексте строчки
struct MyStruct;
typedef MyStruct *PMyStruct;
typedef MyStruct &RMystruct; 3.4. Условная компиляция
Директивы препроцессора # if, # else , # endif и # elif позволяют, в зависимости от результатов проверки некоторых условий, включать в программу один из нескольких вариантов текста:
# if препроцессорное_условие
текст 1
# else
текст 2
# endif
дальнейший текст.
Условие — это константное выражение, которое строится из макроимен, констант и знаков операций, включая логические связки && и | | . Допускается также выражение sizeof (имя_типа) и препроцессорная функция defined( макроимя ), возвращающая 1, если это макроимя определено, и 0, если оно не определено. Вместо директивы
# if defined( DEBUG ) можно написать
# ifdef DEBUG а вместо
# if !defined( DEBUG ) написать
# ifndef DEBUG
Комбинации #if - #else могут быть вложенными, причем последовательность #else - #if заменяется одной директивой #elif с условием:
# if препроцессорное_условие_1
текст 1
# elif препроцессорное_условие_2
текст_2
# else
текст_3
# endif
В файлах заголовков для предотвращения многократного включения одного и того же заголовка в программу обычно присутствует текст вида:
#if !defined(_ _DEFS_H)
#define _ _DEFS_H
/* Текст объявляемых заголовков */
.......................................
#endif /* Конец _ _DEFS_H */
... найти ошибки и повысит мобильность прикладной программы. Мобильность на уровне исходных текстов Материал, рассмотренный нами в предыдущем разделе, относится к вопросам мобильного программирования в связи с использованием функций операционной среды. Однако, если говорить о переносимости программ между компьютерами с разной архитектурой, имея в виду использование языка Си (не слишком высокого ...
... новые и новые пользователи. И с эволюционным развитием всех трех систем наблюдается устойчивый рост количества пользователей Linux. Компьютерное моделирование Прежде чем приступить к компьютерному моделированию технологического процесса, необходимо знать простейшие математические уравнения для его проведения начнем с проверки воспроизводимости опыта. Проверим воспроизводимость опытов ...
... доступа с записью равной байту. Такие файлы называются двоичными. Файлы прямого доступа незаменимы при написании программ, которые должны работать с большими объемами информации, хранящимися на внешних устройствах. В основе обработке СУБД лежат файлы прямого доступа. Кратко изложим основные положения работы с файлами прямого доступа. 1). Каждая запись в файле прямого доступа имеет свой номер ...
... буквы из имеющихся двадцати шести букв/. 4.5. Правила, определяющие область действия. Функции и внешние переменные, входящие в состав “C”-программы, не обязаны компилироваться одновременно; программа на исходном языке может располагаться в нескольких файлах, и ранее скомпилированные процедуры могут загружаться из библиотек. Два вопроса представляют интерес: Как следует составлять описания, чтобы ...
0 комментариев