Директивы процессора в С++
Министерство образования и науки РФ
Государственное образовательное учреждение высшего профессионального образования
ДАГЕСТАНСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
Кафедра
ММвЭ
Реферат
На
тему: Директивы процессора в С++
Выполнил:
ст. курса, И912 гр. Келеметов С.Ш.
Руководитель практики:
Зав. каф.
Махачкала 2011 г.
Директивы препроцессора Turbo C++
Хотя Turbo C++ использует использует интегрированный однопроходный компилятор как при работе в интегрированной среде разработки (IDE), так и при вызове компилятора из командной строки, полезно тем не менее сохранить терминологию, сохранившуюся от более ранних, многопроходных компиляторов. В случае последних на первом проходе обработки исходного текста программы выполняется подключение всех имеющихсявключаемых файлов, проверка всех условных директив компиляции, расширение всех имеющихся макросов и получение промежуточного файла для обработки последующими проходами компилятора. Посколькукак интегрированная среда разработки, так и версия командной строки Turbo C++ выполняют первый проход, не создаваяпри этом каких-либо промежуточных файлов, Turbo C++ включаетв себя независимый препроцессор, CPP.EXE, который имеет на выходе такой промежуточный файл. CPP полезен на стадии отладки, посколькупоказывает в чистом виде результаты работы директив включения, условных директив компиляции и сложных макрорасширений.
CPP позволяет обращение к документации по нему в диалоговом режиме.
Следующее обсуждение директив препроцессора, их синтаксис и семантика, применимы, следовательно, как к самому препроцессору CPP, так и к его функциям, встроенным в компиляторы Turbo C++.
Препроцессор
находит директивы
Препроцессор Turbo C++ включаетв себя сложный процессор макросов, сканирующий исходный код перед обработкойего компилятором.Препроцессор обеспечивает мощные средства и гибкость, заключающиеся в следующем:
-
Определение макросов, которые служат
для снижения трудоемкости
-
Включение текстов из других
файлов, таких как файлы заголовка,
в которых содержатся
-
Установка условной компиляции
для улучшения мобильности
Директивы препроцессора обычно помещаются в начало исходного кода, но допустимы в любой точке программы.
Любая
строка с ведущим символом # рассматриваетсякак
директива препроцессора, еслитолько#
не входит в строковый литерал, символьную
константу или комментарий. Ведущему символу
# может предшествовать, либо следовать
за ним,пробельные символы (за исключением
символа новой строки).
файл-для-препроцессора:
группа
группа:
часть группы
группа часть-группы
часть-группы:
<лексемы-препроцессора> новая-строка
if-раздел
управляющая строка
if-раздел:
if-группа <elif-группы> <else-группа> endif-строка
if-группа:
#if выражение-типа-константы новая-строка <группа>
#ifdef идентификатор новая-строка <группа>
#ifndef идентификатор новая-строка <группа>
elif-группы:
elif-группа
elif-группы elif-группа
elif-группа:
#elif выражение-типа-константы <группа>
else-группа:
#else новая-строка <группа>
endif-строка:
#endif новая-строка
управляющая-строка:
#include лексемы-препроцессора новая-строка
#define идентификатор список-замены новая-строка
#define идентификатор левая-круглая-
<список-идентификаторов>) список-замены новая-строка
#undef идентификатор новая-строка
#line <лексемы-
#pragma <лексемы-
#pragma warn действие сокращение новая-строка
#pragma inline новая-строка
? новая-строка
действие: одно из
+ - .
сокращение:
amb ampapt aus big cincpt
def dupelf mod par piapro
rch retrng rpt rvf sigstr
stu stvsus ucp use volzst
левая-круглая-скобка:
символ левой круглой скобки без предшествующих пробельных символов
список-замены:
<лексемы-препроцессора>
лексемы-препроцессора:
имя-заголовка (только для директивы #include)
идентификатор (без различения ключевого слова)
константа
строковый-литерал
операция
пунктуатор
любой не-пробельный символ, не относящийся к предыдущим пунктам
имя-заголовка:
<последовательность-
последовательность-
символ-заголовка
последовательность-
символ-заголовка:
любой
символ из исходного множества символов,
за исключением символа новой-
новая-строка:
символ новой строки
Пустая директива #
Пустая директива состоитиз строки, вкоторой содержится единственныйсимвол #. Эта директива всегда игнорируется препроцессором.
Директивы #define и #undef
Директива #define определяет макрос. Макросы обеспечивают механизм замены лексемы набором формальных, подобных используемых в функциях параметров, либо пустой замены.
Простые макросы #define
В простых случаях, без параметров, синтаксис данной директивы следующий:
#define идентификатор_макроса <последовательность_лексем>
Каждое
вхождение идентификатора_
Любые
вхожденияидентификаторамакроса
Пустая последовательность лексем позволяетэффективное удаление всех найденных идентификаторов макросов из исходного кода:
#define HI "Добрый день!"
#define empty
#define NIL ""
...
puts(HI); /* расширяется в: puts("Добрый день!"); */
puts(NIL); /* расширяется в: puts(""); */
puts("empty"); /* расширения empty не происходит ! */
/* расширение empty не будет выполнено и в комментариях! */
После расширения каждого конкретного макроса дальнейшее сканирование продолжится уже для нового, расширенного текста. Это дает возможность организации вложенных макросов: расширенный текст может в свою очередь содержать подлежащие расширению идентификаторы макросов. Однако, если макрос при расширении образует директиву препроцессора, то такая директива препроцессором уже не распознается:
#define GETSTD #include <stdio.h>
...
GETSTD /* ошибка компиляции */
GETSTD будет расширен в #include<stdio.h>. Однако, препроцессор не станет сам обрабатывать эту вполне допустимую в других условиях директиву, а передаст ее в таком виде компилятору. Компилятор воспримет#include <stdio.h> как недопустимый ввод. Макрос не может быть расширен во время собственногорасширения. Поэтому выражения типа #define A A недопустимы вследствие неопределенности результата.
Директива #undef
Можно отменить определение макроса при помощи директивы #undef:
#undef идентификатор_макроса
Данная строка удаляетлюбую ранее введенную последовательность лексем из идентификатора макроса;определение макроса теряется, и идентификатор его становится неопределенным.
Макрорасширения внутри строк #undef не выполняются.
Состояние определенности и неопределенности является важным свойством идентификатора, независимо от его фактического определения. Условные директивы препроцессора #ifdef и #ifndef, которые служат для проверки того, является ли идентификатор в текущий момент определенным, или нет, представляют собой гибкий механизм управления многими аспектами компиляции.
После
того, как идентификатор
неопределенным,
он может бытьдалеепереопределендиректив
#define, с использованием той же самой или другой последовательности лексем.
#define BLOCK_SIZE 512
...
buff = BLOCK_SIZE*blks; /* расширяется в: 512*blks */ ...
#undef BLOCK_SIZE
/* использование BLOCK_SIZE теперь невозможно - это "неизвестный" препроцессору идентификатор */
...
#define BLOCK_SIZE 128 /*переопределение размера блока*/
...
buf = BLOCK_SIZE*blks; /* расширяется в: 128*blks */
...
Попыткапереопределения уже определенного идентификатора макроса приведет к сообщению уровня предупреждения, если только новоеопределения не повторяет текущее с точностью до последней лексемы. Предпочтительный способ работы с теми же определениями в других файлах заголовка следующий:
#ifndef BLOCK_SIZE
#define BLOCK_SIZE 512
#endif
Если
идентификатор BLOCK_SIZE в текущий момент
определен, то средняя строка не обрабатывается
препроцессором; в противном же случае
выполняется определение
Отметим,что директива препроцессора не должна заканчиваться точкой с запятой (;). Любые символы, найденные препроцессором в последовательности лексем, включая точки с запятой, появятся в макрорасширениях. Последовательность лексем заканчивается первой встреченной новой строкой без предшествующего символа обратной наклонной черты. Любая последовательность пробельных символов, включая комментарии в последовательности лексем, заменяется на один символ пробела.
Программисты, привыкшие работать на языке ассемблера, должны преодолеть желание написать:
#define BLOCK_SIZE = 512 /* почему последовательность лексем включает символ = */
Опции -D и -U
Определение
и отмена определения идентификаторов
выполняется также при помощи
опций компилятора командной
строки - D и -U (см. Главу 4,"Компилятор
командной строки" в Руководстве
пользователя). Идентификаторы могут
быть определены, но не могут бытьявно
отменены, при помощи меню интегрированной
среды разработки Options \! Compiler \! Defines (см.
Главу 1,"Справочник по интегрированнойсредеразработки
Командная строка
tcc -Ddebug=1; paradox=0; X -Umysym myprog.c
эквивалентна помещению в программу строк:
#define debug 1
#define paradox 0
#define X
#undef mysym
Ключевые слова и защищенные слова
Допустимо, но не рекомендуется, использовать ключевые слова Turbo C++ в качестве идентификаторов макросов:
#define int long /* допустимо, но может привести к катастрофическим последствиям */
#define INT long /* допустимо и, вероятно, полезно */
Следующие предопределенные глобальные идентификаторы не могут появляться непосредственно следом за директивами #defineили #undef:
__STDC__ __DATE__
__FILE__ __TIME__
__LINE__
Отметим наличие в этих именах ведущих и хвостовых двойных символов подчеркивания.
Макросы с параметрами
Для определения макросов с параметрами используется следующий синтаксис:
#define идентификатор_макроса(<список-
Любая запятая в круглых скобках внутри аргумента рассматривается как часть аргумента, а не какразделитель аргументов.
Отметим,что между идентификатором-макроса и левой круглой скобкой списка-аргументов не может находитьсяни одного пробельного символа. Опциональный список-аргументов -это последовательность идентификаторов, разделенных запятыми, как в списке аргументов функции С. Каждый разделенный запятой идентификаториграет рольформального аргумента, или же метки-заполнителя.
Вызов таких макросов выполняется записью
идентификатор-макроса<
в последующем исходном коде. Синтаксис вызова аналогичен синтаксису вызова функций; действительно, многиестандартные библиотечные "функции" С реализованы в виде макросов. Однако, имеется ряд возможных различий, которые могут привести к случайным ошибкам (см. стр.140 оригинала).
Опциональный список-фактических-аргументов должен содержать то же число разделяемых запятой лексем, известных как фактические аргументы, что содержится в списке-формальных-аргументов в строке с #define: каждому формальному аргументу должен соответствовать один фактический аргумент. Если число аргументах в двух указанных списков различно, то выдается сообщение об ошибке.
Вызов макроса приводитк двум типамзамены. Во-первых, идентификатор макроса и заключенные в круглые скобки аргументы заменяются последовательностью лексем. Затем формальные аргументы, найденные в данной последовательности лексем, заменяются соответствующими фактическими аргументами из списка-фактических-аргументов. Например,
#define CUBE(x) ((x)*(x)*(x))
...
int n,y
n = CUBE(y):
дает в результате следующую замену:
n = ((y)*(y)*(y));
Аналогичным образом, последняя строка в
#define SUM ((a) + (b))
...
int i,j,sum;
sum = SUM(i,j);
при расширении даст sum = ((i)+(j)). Причина кажущегося избытка круглых скобок станет очевидной, если рассмотреть пример:
n = CUBE(y+1);
Без внутренней пары круглых скобок в определении расширение даст запись: n=y+1*y+1*y+1, что при лексическом анализе равно:
n = y + (1*y) + (1*y) + 1; // если y не равен 0 или -3, то в // куб возводится (y+1) !
Как
и в случае простых макроопределений,
производится повторное сканирование
текста для определения необходимости
повторных макрорасширений
При использовании макросов со спискамиаргументов следует обратить внимание на следующие моменты:
1. Вложенные круглые скобки и запятые:
Список-фактических-
#define ERRMSG(x, str) showerr("Error",x,str)
#define SUM(x,y) ((x) + (y))
...
ERRMSG(2, "Press Enter, then Esc");
// расширится в: showerr("Error",2,"Press Enter, then Esc"); */ return SUM(f(i,j), g(k.l));
// расширится в: return ((f(i,j)) + (g(k,l))); */
2.
Склеивание лексем при помощи
##: можно выполнить склеивание (слияние)
двух лексем, разделив их символами
## (и плюс опциональными
#define VAR(i,j) (i##j)
и затем вызвав VAR(x,6), можно получить расширение (x6). Этот метод заменяет старый (не обеспечивающий мобильность кода) метод использования (i/**/j).
3. Преобразование к строкам при помощи #: символ #можно поместить перед формальным аргументом макроса с тем, чтобы после подстановкифактический аргумент был преобразован в строку. Поэтому, с учетом следующего определения макроса:
#define TRACE(flag) printf(#flag "=%d\n",flag)
фрагмент кода
int highval = 1024;
TRACE(highval);
станет равным
int highval = 1024;
printf("highval" "= %d\n", highval);
что в свою очередь будет рассматриваться как
int highval = 1024;
printf("highval=%d\n", highval);
4
Символ обратной наклонной
#define WARN "фактически это одно\
строчное сообщение"
...
puts(WARN);
/* на экране будет: фактически это однострочное сообщение */
5.
Побочные эффекты и прочие
опасности: схожесть между
int cube(int x) (*
return x*x*x;
*)
#define CUBE(x) ((x)*(x)*(x))
...
int b =0, a = 3;
b = cube(a++);
/* cube() передается фактический аргумент = 3; поэтому b = 27, и теперь a = 4 */
a = 3;
b = CUBE(a++);
/* расширяется в: ((a++)*(a++)*(a++)); и теперь a = 6 */
Итоговое значение b зависит от того, что компилятор делает с расширенным выражением.
Включение файлов директивой #include
Директива #include подключает к исходному коду заданные в ней файлы, известные как включаемые файлы, файлы заголовковили заголовки. Синтаксис этой директивы имеет три формы:
#include <имя_заголовка>
#include "имя_заголовка"
#include идентификатор_макроса
В данном случае угловые скобки являются фактически записываемымив тексте директивы лексемами, а не метасимволами, обозначающими, что имя_заголовка является опциональным.
Третий вариант записи предполагает, что ни символ <, ни символ " не являются первым не-пробельным символом, следующим за #include; кроме того, предполагается, что существует такое макроопределение, которое расширит идентификатор макроса в допустимое, следующее за разделителем имя заголовка в формате либо <имя_заголовка>, либо "имя_заголовка".
Первый
и второй варианты предполагают, что
попыток макрорасширений
Препроцессор удаляет строку #include и заменяет ее, начиная с текущей строки исходного кода, полным текстом файла заголовка. Сам исходный код остается без изменений,однакокомпилятор "видит" весь расширенный текст целиком. Таким образом, помещение в текст директивы #include может повлиять на контекст и продолжительность жизни любых идентификаторов во включаемых файлах.
Если поместить в имя_заголовка полное имя пути доступа к файлу, то поиск файла будет выполнентольков указанной таким образом директории.
Различиемежду форматами <имя_заголовка> и "имя_заголовка" заключается в алгоритме поиска включаемого файла, применяемом в каждом случае; эти алгоритмы описаны в следующих двух разделах.
Поиск файла заголовка при формате <имя_заголовка>
Вариант <имя_заголовка>задает стандартный включаемый файл; поиск последовательно выполняется во всех включаемых директориях в той последовательности, в которой они определены. Если ни в одной из этих директорий по умолчанию искомый файл не найден, то выдается сообщение об ошибке.
Поиск файла заголовка при формате "имя_заголовка"
Вариант "имя_заголовка" задает включаемый файл, создаваемый пользователем; сначала выполняется его поиск в текущей директории (обычно в той директории, в которой находится исходный компилируемый файл). Если там файл не найден, то поиск продолжается во всех включаемых директориях, как в случае формата <имя_заголовка>.
Приводимые ниже примеры поясняют описанные различия:
#include <stdio.h>
/* заголовок
из стандартной включаемой
#define
myinclude"c:\tc\include\
/* Примечание:
здесь допустимы одинарные
наклонной черты; в операторе С пишется:
"c:\\tc\\include\\mystuff.
#include myinclude
/* макрорасширение */
#include "myinclude.h"
/* макрорасширение отсутствует */
После расширения второй оператор#include заставит препроцессор искать нужный файл в C:\TC\INCLUDE\mystuff.h, и нигде более. Третий пример #includeзаставляет препроцессор выполнить поискmyinclude.h сначала в текущей директории, а затем во включаемых директориях.

- Директ-кост» в системе управленческого учета
- Директ-костинг
- «Директ-костинг» жүйесінің теориялық негізі
- «Директ-костинг»: методика учета производственных затрат и финансовых результатов в сельскохозяйственной организации
- Директ-маркетинг
- Директ-маркетинг
- Директория Совет пяти
- Директива 1996 року про інтегровану систему контролю за забрудненням с точки зору здійснення контролю за використанням земель. Порівняльна
- Директива, как один из актов Президента Республики Беларусь
- Директива Президента Республики Беларусь
- Директивное и индикативное планирование
- Директивное и индикативное планирование
- Директивный и функциональный стиль в семейном консультировании. Гештальт терапия в семейном консультировании
- Директивы ЕС – система обязательного подтверждения качества на европейском рынке