Автор: Пользователь скрыл имя, 02 Декабря 2011 в 09:03, реферат
Хотя Turbo C++ использует использует интегрированный однопроходный компилятор как при работе в интегрированной среде разработки (IDE), так и при вызове компилятора из командной строки, полезно тем не менее сохранить терминологию, сохранившуюся от более ранних, многопроходных компиляторов. В случае последних на первом проходе обработки исходного текста программы выполняется подключение всех имеющихсявключаемых файлов, проверка всех условных директив компиляции, расширение всех имеющихся макросов и получение промежуточного файла для обработки последующими проходами компилятора. Посколькукак интегрированная среда разработки, так и версия командной строки Turbo C++ выполняют первый проход, не создаваяпри этом каких-либо промежуточных файлов, Turbo C++ включаетв себя независимый препроцессор, CPP.EXE, который имеет на выходе такой промежуточный файл. CPP полезен на стадии отладки, посколькупоказывает в чистом виде результаты работы директив включения, условных директив компиляции и сложных макрорасширений.
Программисты, привыкшие работать на языке ассемблера, должны преодолеть желание написать:
#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 сначала в текущей директории, а затем во включаемых директориях.