Директивы процессора в С++

Автор: Пользователь скрыл имя, 02 Декабря 2011 в 09:03, реферат

Описание работы

Хотя Turbo C++ использует использует интегрированный однопроходный компилятор как при работе в интегрированной среде разработки (IDE), так и при вызове компилятора из командной строки, полезно тем не менее сохранить терминологию, сохранившуюся от более ранних, многопроходных компиляторов. В случае последних на первом проходе обработки исходного текста программы выполняется подключение всех имеющихсявключаемых файлов, проверка всех условных директив компиляции, расширение всех имеющихся макросов и получение промежуточного файла для обработки последующими проходами компилятора. Посколькукак интегрированная среда разработки, так и версия командной строки Turbo C++ выполняют первый проход, не создаваяпри этом каких-либо промежуточных файлов, Turbo C++ включаетв себя независимый препроцессор, CPP.EXE, который имеет на выходе такой промежуточный файл. CPP полезен на стадии отладки, посколькупоказывает в чистом виде результаты работы директив включения, условных директив компиляции и сложных макрорасширений.

Работа содержит 1 файл

Директивы препроцессора Turbo C.docx

— 48.44 Кб (Скачать)

   Программисты, привыкшие работать на языке ассемблера, должны преодолеть желание написать:

   #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. Побочные эффекты и прочие  опасности: схожесть между вызовами  макросов и функциями иногда  скрывает различия между ними. При вызове макроса отсутствует  встроенный контроль типов данных, поэтому различиев типах данных формального и фактического аргументов может вызвать непредсказуемые результаты, причину которых нелегко установить, причем относительно такой ошибки не будет выдано никаких предупреждений. При вызовах макросов могут также возникнуть нежелательные побочные эффекты, особенно когдафактический аргумент вычисляется более одного раза. Сравните CUBE и cube в следующем примере:

   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; кроме того, предполагается, что существует такое макроопределение, которое расширит идентификатор макроса в допустимое, следующее за разделителем имя заголовка в формате либо <имя_заголовка>, либо "имя_заголовка".

   Первый  и второй варианты предполагают, что  попыток макрорасширений сделано не будет; другими словами, имя_заголовка никогда не сканируется в поискахидентификаторов макросов. Имя_заголовка должно быть допустимым именем файла DOS с расширением (традиционно файлы заголовка имеют расширение .h) и опциональнымименемпути доступа к немус соответствующими разделителями.

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

   Если  поместить в имя_заголовка полное имя пути доступа к файлу, то поиск файла будет выполнентольков указанной таким образом директории.

   Различиемежду форматами <имя_заголовка> и "имя_заголовка" заключается в алгоритме поиска включаемого файла, применяемом в каждом случае; эти алгоритмы описаны в следующих двух разделах.

   Поиск файла заголовка при формате <имя_заголовка>

   Вариант <имя_заголовка>задает стандартный включаемый файл; поиск последовательно выполняется во всех включаемых директориях в той последовательности, в которой они определены. Если ни в одной из этих директорий по умолчанию искомый файл не найден, то выдается сообщение об ошибке.

   Поиск файла заголовка при формате "имя_заголовка"

   Вариант "имя_заголовка" задает включаемый файл, создаваемый пользователем; сначала выполняется его поиск в текущей директории (обычно в той директории, в которой находится исходный компилируемый файл). Если там файл не найден, то поиск продолжается во всех включаемых директориях, как в случае формата <имя_заголовка>.

   Приводимые  ниже примеры поясняют описанные  различия:

   #include <stdio.h>

   /* заголовок  из стандартной включаемой директории */

   #define  myinclude"c:\tc\include\mystuff.h"

   /* Примечание: здесь допустимы одинарные символы  обратной

   наклонной черты; в операторе С пишется:

   "c:\\tc\\include\\mystuff.h"  */

   #include myinclude

   /* макрорасширение */

   #include "myinclude.h"

   /* макрорасширение  отсутствует */

   После расширения второй оператор#include заставит препроцессор искать нужный файл в C:\TC\INCLUDE\mystuff.h, и нигде более. Третий пример #includeзаставляет препроцессор выполнить поискmyinclude.h сначала в текущей директории, а затем во включаемых директориях.

Информация о работе Директивы процессора в С++