Реферат: Turbo C++ Programer`s guide
Краткое изложение правил
определения контекста С++
Следующие правила применимы ко всем именам, включая
имена typedef и имена классов, при условии, что то или иноеимя допустимо в С++
в конкретном обсуждаемом контексте:
1. Сначала само имя проверяется на наличие неоднозначностей.
Если в пределах контекста неоднозначности отсутствуют, то инициируется
последовательность доступа.
2. Если ошибок управления доступа не обнаружено, то
проверяется тип объекта, функции, класса, typedef, и т.д.
3. Если имя используется вне какой-либо функции или
класса, либо имеет префикс унарной операции контекста доступа ::, и если имя не
квалифицировано бинарной операцией ::или операциямивыборакомпонента . или
->, то это имя должно быть именем глобального объекта, функции или
нумератора.
4. Если имя n появляется в одном из видов: X::n, x.n (где
x это объект класса Х или ссылка на Х), либо ptr->n (где ptrэто указатель на
Х),то n является именем компонента Хили компонентом класса, производным от
которого является Х.
5. Любое до сих пор не рассмотренное имя,
используемое в качестве статической функции-компонента, должно быть объявлено в
блоке, в которомоно встречается, либо в объемлющемблоке, либо являться
глобальным именем. Объявление локального имени n скрывает объявления n в
объемлющих блоках и глобальные объявления n. Имена в различныхконтекстах
немогут быть перегружены.
6. Любое не рассмотренное до сих пор имя, используемое в
качестве имени нестатической функции компонента класса Х, должно быть объявлено
в блоке, в котором оно встречается, либо в объемлющем блоке, быть компонентом
класса Х или базового относительно Х класса, либо быть глобальным именем.
Объявление локального имени n скрывает объявления n в объемлющих
блоках,компонентах класса- функциях и глобальных объявлениях n. Объявление имени
компонента скрывает объявления этого имени в базовых классах.
7. Имя аргумента функции в определении функции находится в
контексте самого внешнего блока данной функции. Имя аргумента функции в
не-определяющем объявлении функции вообще не имеет контекста. Контекст
аргумента по умолчанию объявляется точкой объявления данного аргумента, ноне
позволяет доступ к локальным переменным или не-статическим компонентам класса.
Аргументы по умолчанию вычисляются в каждой точке вызова.
8. Инициализатор конструктора (см. "инициализатор-конструктора"
в описании синтаксиса декларатора класса в таблице
1.12 на стр.37 оригинала) вычисляется в контексте самого внешнего блока
конструктора, и поэтому разрешает ссылки на имена аргументов конструктора.
Директивы препроцессора Turbo
C++
Хотя Turbo C++ использует использует
интегрированный однопроходный компилятор как при работе в интегрированной среде
разработки (IDE), так и при вызове компилятора из командной строки, полезно тем
не менее сохранить терминологию, сохранившуюся от более ранних, многопроходных
компиляторов. В случае последних на первом проходе обработки исходного текста
программы выполняется подключение всех имеющихсявключаемых файлов, проверка
всех условных директив компиляции, расширение всех имеющихся макросов и получение
промежуточного файла для обработки последующими проходами компилятора.
Посколькукак интегрированная среда разработки, так и версия командной строки
Turbo C++ выполняют первый проход, не создаваяпри этом каких-либо промежуточных
файлов, Turbo C++ включаетв себя независимый препроцессор, CPP.EXE, который
имеет на выходе такой промежуточный файл. CPP полезен на стадии отладки,
посколькупоказывает в чистом виде результаты работы директив включения,
условных директив компиляции и сложных макрорасширений.
CPP позволяет обращение к документации по нему в диалоговом режиме.
Следующее обсуждение директив препроцессора, их синтаксис
и семантика, применимы, следовательно, как к самому препроцессору CPP, так и к
его функциям, встроенным в компиляторы Turbo C++.
Препроцессор находит директивы препроцессора (которые
называют также управляющимистроками препроцессора) и выполняет лексический
анализ находящихся в них фраз.
Препроцессор Turbo C++ включаетв себя сложный процессор
макросов, сканирующий исходный код перед обработкойего
компилятором.Препроцессор обеспечивает мощные средства и гибкость,
заключающиеся в следующем:
- Определение макросов, которые служат для снижения
трудоемкости программирования и улучшении читаемости кода. Некоторые макросы
позволяют избежать затрат на вызов функций.
- Включение текстов из других файлов, таких как файлы
заголовка, в которых содержатся прототипы стандартных библиотечных и
определяемых пользователем функций, а также буквальные константы.
- Установка условной компиляции для улучшения мобильности
получаемых кодов и для целей отладки.
Директивы препроцессора обычно помещаются в начало
исходного кода, но допустимы в любой точке программы.
Любая строка с ведущим символом # рассматриваетсякак
директива препроцессора, еслитолько# не входит в строковый литерал, символьную
константу или комментарий. Ведущему символу # может предшествовать, либо
следовать за ним,пробельные символы (за исключением символа новой строки).
Полный синтаксис препроцессора Turbo C++ показан в следующей таблице:
Синтаксис директив
препроцессора Turbo C++ Таблица 1.23
файл-для-препроцессора:
группа
группа:
часть группы
группа часть-группы
часть-группы:
<лексемы-препроцессора>
новая-строка
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)
идентификатор
(без различения ключевого слова)
константа
строковый-литерал
операция
пунктуатор
любой не-пробельный символ, не относящийся к предыдущим пунктам
имя-заголовка:
<последовательность-символов-заголовка>
последовательность-символов-заголовка:
символ-заголовка
последовательность-символов-заголовка
символ-заголовка
символ-заголовка:
любой символ из исходного множества символов, за
исключением символа новой-строки (\n) или символа "больше чем"
(>).
новая-строка:
символ новой строки
Пустая директива #
Пустая директива состоитиз строки, вкоторой
содержится единственныйсимвол #. Эта директива всегда игнорируется
препроцессором.
Директивы #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);
что в свою очередь будет
рассматриваться как
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
|