Back
                            Перегруженные операции

      С++  позволяет  переопределить действие большинства операций,
 так чтобы при использовании с  объектами  конкретного  класса  они
 выполняли  заданные  функции. Как и в случае перегруженных функций
 С++  в  целом,  компилятор  определяет  различия  в  функциях   по
 контексту вызова: по числу и типам аргументов операндов:

      Переопределение  может  быть  выполнено  для  всех  операций,
 приведенных на стр.20 оригинала, за исключением

    .   .*   ::   ?:

      Также невозможна перегрузка символов препроцессора # и ##.

      Ключевое слово operator, за которым следует символ  операции,
 называется   именем   функции  операции;  при  определении  нового
 (перегруженного) действия операции оно  используется  как  обычное
 имя функции.

      Операция-функция,  вызываемая  с  аргументами, ведет себя как
 операция,  выполняющая  определенные  действия  с   операндами   в
 выражении.  Операция-функция изменяет число аргументов или правила
 приоритета и ассоциативности  операции  (таблица  1.20  на  стр.75
 оригинала),   сравнительно   с   ее   нормальным   использованием.
 Рассмотрим класс complex:

    class complex {
       double real, imag;                // по умолчанию private
    public:
       ...
       complex() { real = imag = 0; }    // встроенный конструктор
       complex(double r, double i = 0) { // еще один
            real = r; imag = i;
       }
       ...
    }

      Данный класс был создан только для  примера.  Он  отличен  от
 класса complex из библиотеки исполняющей системы.

      Мы  можем  легко разработать функцию для сложения комплексных
 чисел, например,

    complex AddComplex(complex c1, complex c2);

 однако будет более естественным иметь возможность записать:

    complex c1(0,1), c2(1,0), c3
    c3 = c1 + c2;

 вместо

    c3 = AddComplex(c1, c2);

      Операция + легко может  быть  перегружена,  если  включить  в
 класс complex следующее объявление:

    friend complex operator +(complex c1, complex c2};

 и сделав его определение (возможно, встроенное) следующим образом:

    complex operator +(complex c1, complex c2)
    {
       return complex(c1.real + c2.real, c1.imag + c2.imag);
    }
                               Операции-функции

      Операции-функции  можно вызывать непосредственно, хотя обычно
 они вызываются косвенно, при использовании перегруженных операций:

    c3 = c1.operator + (c2);   // то же, что c3 = c1 + c2

      В отличие от new и delete,  которые  имеют  свои  собственные
 правила  (см. следующий раздел), операция-функция должна быть либо
 не-статической функцией-компонентом, либо иметь как  минимум  один
 аргумент  типа  класса.  Операций-функции   =,  (), [] и -> должны
 являться нестатическими функциями-компонентами.

 Перегруженные операции и наследование

      За исключением операции-функции присвоения  =()  (см.  раздел
 "Перегрузка  операции  присвоения  ="  на  стр.127  оригинала) все
 функции операций для класса X наследуются классом, производным  от
 X,  согласно  стандартным  правилам  разрешения  для перегруженных
 функций. Если Х является  базовым  классом  для  Y,  перегруженная
 функция операции для Х может далее быть перегружена для Y.

 Перегрузка new и delete

      Операции  new  и delete могут быть перегружены таким образом,
 чтобы  давать  альтернативные  варианты  подпрограммы   управления
 свободной памятью (кучей). Определяемая пользователем операция new
 должна  возвращать  void*  и  иметь  в  качестве первого аргумента
 size_t. Определяемая пользователем операция  delete  должна  иметь
 тип  void  и  первый аргумент void*; второй аргумент, типа size_t,
 является опциональным.

      Тип size_t определяется в stdlib.h.

      Например,

    #include 

    class X {
       ...
    public:
       void* operator new(size_t size) { return newalloc(size);}
       void operator delete(void* p} {newfree(p); }
       X() { /* здесь инициализация */
       X(char ch) { /* здесь тоже */ }
       -X() { /* очистка */ }
       ...
    };

      Аргумент size задает размер создаваемого объекта, а  newalloc
 и  newfree  это определяемые пользователем функции распределения и
 отмены распределения памяти. Вызовы конструктора и деструктора для
 объектов класса Х (или объектов, производных от Х, для которых  не
 существует   собственных  перегруженных  операций  new  и  delete)
 приведет  к  запуску  соответствующих  определяемых  пользователем
 X::operator new() и X::operator delete(), соответственно.

      Операции-функции   X::operator   new   и  X::operator  delete
 являются статическими компонентами Х, как при явном объявлении  их
 static,  так  и  без  него, поэтому они не могут быть виртуальными
 функциями.

      Стандартные, предопределенные  (глобальные)  операции  new  и
 delete могут при этом также использоваться в контексте Х, как явно
 с  операциями  глобального  контекста (::operator new и ::operator
 delete), так и неявно, при создании и разрушении объектов классов,
 отличных от класса Х и не являющихся  производными  от  класса  Х.
 Например,   можно   использовать  стандартные  new  и  delete  при
 определении перегруженных версий:

    void* X::operator new(size_t)
    {
       void* ptr = new char[s];  // вызов стандартной new
       ...
       return ptr;
    }
    void X::operator delete(void* ptr)
    {
       ...
       delete (void*) ptr;       // вызов стандартной delete
    }

      Причиной  того,  что  размер  аргумента  определяется   size,
 является  то,  что классы, производные от Х, наследуют X::operator
 new.  Размер  объекта  производного   класса   может   существенно
 отличаться от размера, определяемого базовым классом.

 Перегрузка унарных операций

      Перегрузка   префиксной   или  постфиксной  унарной  операции
 выполняется   при   помощи   объявления   не-статической   функции
 компонента,  не  принимающей  никаких  аргументов, либо при помощи
 объявления функции, не являющейся функцией-компонентом принимающей
 один аргумент. Если @ представляет собой унарную операцию, то @x и
 x@  можно  интерпретировать  как  x.operator@()  и   operator@(x),
 соответственно,  в зависимости от объявления. Если объявление было
 сделано в обеих формах, то разрешение неоднозначности  зависит  от
 переданных при вызове операции стандартных аргументов.

      Следует  быть  внимательным при перегрузке ++ и --, поскольку
 постфиксное и префиксное использование не может быть определено  в
 перегруженной функции. Например,

    class X {
       ...
       X operator ++() { /* здесь подпрограмма инкремента X */ }
    }
    ...
    X x, y;
    y = ++x;             // то же, что и  y = x++ !

 Перегрузка бинарных операций

      Перегрузка   бинарной   операции   выполняется   при   помощи
 объявления не-статической  функции  компонента,  принимающей  один
 аргумент,  либо  при  помощи  объявления не являющейся компонентом
 функции  (обычно  friend),  принимающей  два  аргумента.  Если   @
 представляет    собой    бинарную    операцию,    то   x@y   можно
 интерпретировать либо как x operator@(y), либо как operator@(x,y),
 в зависимости от выполненных объявлений. Если объявлены обе формы,
 то разрешение неоднозначности зависит  от  переданных  при  вызове
 операции стандартных аргументов.

 Перегрузка операции присвоения =

      Операция  присвоения  =  может  быть  перегружена  только при
 помощи объявления не-статической функции-компонента. Например,

 Внимание :    пример программы смотрите в файле  struct.app


      Данный код,  совместно  с  объявлениями  String::operator=(),
 позволяет  выполнять  строковые  присвоения  str1  = str2, как это
 делается в прочих языках программирования.  В  отличие  от  прочих
 функций   операций,   функция   операции   присвоения   не   может
 наследоваться производными классами. Если для какого-либо класса Х
 не существует определяемой операции =, то операция =  определяется
 по умолчанию как покомпонентное присвоение компонентов класса Х:

    X& X::operator = (const X& source)
    {
       // покомпонентное присвоение
    }

      Перегрузка операции вызова функции ()

      Вызов функции

    первичное-выражение(<список-выражений>)

 рассматривается   в   качестве   двоичной  операции  с  операндами
 первичное-выражение   и   список-выражений   (возможно,   пустой).
 Соответствующая  функция  операции  это operator(). Данная функция
 может являться определяемой пользователем для класса  Х  (и  любых
 производных    классов)    только    в   качестве   не-статической
 функции-компонента. Вызов x(arg1, arg2), где x есть объект  класса
 Х, интерпретируется в таком случае как x.operator()(arg1,arg2).

 Перегрузка операции индексирования []

      Аналогичным образом, операция индексирования

    первичное-выражение [выражение]

 рассматривается     как    двоичная    операция    с    операндами
 первичное-выражение и выражение. Соответствующая функция  операции
 это operator[]; она может быть определена пользователем для класса
 Х  (и любых производных классов) только посредством не-статической
 функции-компонента. Выражение x[y], где x  это  объект  класса  Х,
 интерпретируется в данном случае как x.operator[](y).

 Перегрузка операции доступа к компоненту класса ->

      Доступ к компоненту класса при помощи

    первичное-выражение->выражение

 рассматривается  как  унарная  операция. Функция operator-> должна
 рассматриваться как не-статическая  функция-компонента.  Выражение
 x->m,   где   x   это   объект   класса  {,  интерпретируется  как
 (x.operator->())->m, таким образом, что operator->()  должен  либо
 возвращать  указатель  на  объект  данного класса, либо возвращать
 объект класса, для которого определяется operator->.
       Виртуальные функции   -------------------------------------------

      Виртуальные    функции    позволяют    производным    классам
 обеспечивать  разные  версии  функции  базового  класса. Вы можете
 объявить  виртуальную   функцию   в   базовом   классе   и   затем
 переопределить  ее  в  любом производном классе, даже если число и
 тип ее аргументов остается без изменений. Вы также можете объявить
 функции int Base::Fun (int) и int Derived::Fun  (int),  даже  если
 они  не  являются  виртуальными.  Версия  базового класса доступна
 объектам производного класса через переопределение контекста. Если
 они являются виртуальными, то доступна только функция, связанная с
 фактическим типом объекта.

      В случае  виртуальных  функций  вы  не  можете  изменить  тип
 функции.   Следовательно,  недопустимым  является  переопределение
 виртуальной функции таким образом,  чтобы  она  отличалась  только
 типом  возврата.  Если  две  функции с одним и тем же именем имеют
 разные аргументы, С++  рассматривает  их  как  разные  функции,  и
 механизм виртуальных функций игнорируется.

      Говорят,  что переопределенная функция переопределяет функцию
 базового  класса.  Для  объявления  виртуальной   функции   служит
 спецификатор    virtual.    Спецификатор   virtual   подразумевает
 компонентство в классе, поэтому виртуальная функция не может  быть
 глобальной (не являющейся компонентом) функцией.

      Если базовый класс В содержит виртуальную функцию vf, и класс
 D, являющийся производным от класса B, содержит функцию vf того же
 типа,  то  если  функция  vf  вызывается  для  объекта  d  или  D,
 выполняется  вызов  D::vf,  даже  если  доступ   определен   через
 указатель или ссылку на B. Например,

    struct B {
       virtual void vf1();
       virtual void vf2();
       virtual void vf3();
       void f();
    }
    class D : public B {
       virtual void vf(); // спецификатор virtual допустим, но
                          // избыточен
       void vf2(int);     // не virtual, поскольку здесь
                          // используется другой список аргументов
       char f();          // так нельзя: изменяется только тип
                          // возврата
       void f();
    };

    void extf()
    {
       D d;               // объявление объекта D
       B* bp = &d;        // стандартное преобразование из D* в B*
       bp->vf1();         // вызов D::vf1
       bp->vf2();         // вызов B::vf2, так как vf2 из D имеет
                          // отличные аргументы
       bp->f();           // вызов B::f (не виртуальной)
    }

      Переопределяющая  функция  vf1  в  D автоматически становится
 виртуальной.  Спецификатор  virtual  может  быть   использован   в
 определении  переопределяющей  функции в производном классе, но на
 самом деле он является в данном случае избыточным.

      Интерпретация вызова  виртуальной  функции  зависит  от  типа
 объекта,   для   которого   она   вызывается;   в   случае  вызова
 не-виртуальных  функций  интерпретация  зависит  только  от   типа
 указателя   или   ссылки,  указывающих  объект,  для  которой  она
 вызывается.

 Примечание:

      Виртуальные функции должны являться  компонентами  некоторого
 класса,   но   они   не   могут  быть  статическими  компонентами.
 Виртуальная функция может являться другом (friend) другого класса.

      Виртуальная   функция   в   базовом   классе,   как   и   все
 функции-компоненты базового класса, должна быть определена, а если
 не определена, то объявлена  как  функция  без  побочного  эффекта
 ("чистая").

    class B {
    virtual void vf(int) = 0;   // = 0 означает "чистую" функцию

      В  классе,  производном  от  такого  базового  класса, каждая
 "чистая"  функция  должна  быть  определена  или  переобъявлена  в
 качестве таковой (см. следующий раздел, "Абстрактные классы").

      Если  виртуальная функция определена в базовом классе, то нет
 необходимости ее переопределения в производном классе. При вызовах
 будет просто вызвана соответствующая базовая функция.

      Виртуальные   функции   заставляют    определенным    образом
 расплачиваться за свою универсальность: каждый объект производного
 класса  должен содержать указатель на таблицу функций с тем, чтобы
 во время выполнения программы вызвать нужную (поздняя компоновка).
 См. главу 5, в документе "Начало работы".
                              Абстрактные классы

      Глава   5   документа   "Начало   работы"   приводит   пример
 абстрактного класса в действии.

      Абстрактным  называется  класс  с  как  минимум  одной чистой
 виртуальной функцией. Виртуальная функция  задается  как  "чистая"
 при помощи соответствующего спецификатора.

      Абстрактный  класс  может  использоваться  только  в качестве
 базового класса для других классов.  Объекты  абстрактного  класса
 созданы быть не могут. Абстрактный класс не может быть использован
 как   тип   аргумента   или  как  тип  возврата  функции.  Однако,
 допускается объявлять указатели на  абстрактный  класс.  Допустимы
 ссылки  на абстрактный класс при условии, что при инициализации не
 требуется создание временного объекта. Например,

    class shape  {    // абстрактный класс
       point center;
       ...
    public:
       where() { return center; }
       move(point p) { center = p; draw(); }
       virtual void rotate(int) = 0;   // чистая виртуальная функция
       virtual void draw() = 0;        // чистая виртуальная функция
       virtual void hilite() = 0;      // чистая виртуальная функция
       ...
    }

    shape x;         // ошибка: попытка создания объекта абстрактного
                     // класса
       shape* sptr;  // указатель на абстрактный класс допустим
       shape f();    // ошибка: абстрактный класс не может являться
                     // типом возврата
    int q(shape s);  // ошибка: абстрактный класс не может являться
                     // типом аргумента функции
    shape& h(shape&);// ссылка на абстрактный класс в качестве типа
                     // возврата или аргумента функции допустим

      Предположим,   что    D    является    производным    классом
 непосредственно  от  абстрактного  базового  класса  B.  Тогда для
 каждой  чистой  виртуальной  функции  pvf  в  B  либо   D   должен
 обеспечивать  определение  pvf,  либо  D  должен объявлять pvf как
 чистую функцию.

      Например, с использованием показанного выше класса shape:

    class circle : public shape {   // circle является производным
                                    // от абстрактного класса
       int radius;                  // private

    public:
       void rotate(int) { }         // определяется виртуальная
                                    // функция:
                                    // действия по вращению окруж-
                                    // ности отсутствуют
       void draw();                 // circle::draw должна быть
                                    // где-либо определена
       void hilite() = 0;           // переопределение функции
                                    // как "чистой"

      Функций-компоненты  могут  быть  объявлены  из   конструктора
 абстрактного   класса,   но   вызов   чистой  виртуальной  функции
 непосредственно или косвенно из  такого  конструктора  приводит  к
 ошибке времени выполнения.
                                 Контекст С++

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

 Контекст класса

      Имя М компонента класса Х имеет контекст класса "локальный по
 отношению Х"; оно может использоваться в следующих ситуациях:

 - в функциях-компонентах Х

 - в выражениях типа x.M, где x есть объект Х

 - в выражениях типа xptr->M, где xptr есть указатель объекта Х

 - в выражениях типа X::M или D::M, где D есть производный класс от X

 - в ссылках вперед в пределах класса, которому принадлежит компонент

      Классы,  перечислимые данные или имена typedef, объявленные в
 пределах класса Х, либо имена функций, объявленные как "друзья" Х,
 не являются компонентами  Х;  их  имена  просто  имеют  объемлющий
 контекст.

 Скрытые имена

      Имя  может  быть  скрыто  явным  объявлением  того же имени в
 объемлющем блоке или в классе. Скрытый  компонент  класса  тем  не
 менее   остается  доступным  при  помощи  модификатора  контекста,
 заданного с именем класса X:M.  Скрытое  имя  с  контекстом  файла
 (глобальное)  позволяет ссылку на него при помощи унарной операции
 ::, например ::g. Имя класса Х может быть скрыто  именем  объекта,
 функции,  либо  нумератора, заданного в контексте Х, независимо от
 последовательности объявления имен. Однако, имя скрытого класса  Х
 доступно  при  использовании префикса Х с соответствующим ключевым
 словом class, struct или union.

      Точка объявления имени х находится непосредственно  после  его
 полного объявления, но до инициализатора, если таковой существует.

 Краткое изложение правил определения контекста С++

      Следующие  правила  применимы  ко  всем именам, включая имена
 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-группа   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   amp   apt   aus   big   cin   cpt
    def   dup   elf   mod   par   pia   pro
    rch   ret   rng   rpt   rvf   sig   str
    stu   stv   sus   ucp   use   vol   zst

 левая-круглая-скобка:
    символ левой круглой скобки без предшествующих пробельных
    символов

 список-замены:
    <лексемы-препроцессора>

 лексемы-препроцессора:
    имя-заголовка (только для директивы #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 
    ...
    GETSTD   /* ошибка компиляции */

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

    #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  сначала  в  текущей  директории,  а  затем  во
 включаемых директориях.
                              Условная компиляция

      Turbo  C++  поддерживает  условную  компиляцию  путем  замены
 соответствующих  строк исходного кода пустой строкой. Игнорируемые
 таким образом строки это те строки, что начинаются с символа # (за
 исключением директив #if, #ifdef, #ifndef, #else, #elif и #endif),
 а также любые строки, которые в результате выполнения директив  не
 должны  компилироваться.  Все директивы условной компиляции должны
 завершаться в том же исходном или включаемом файле, где  находится
 их начало.

 Директивы условной компиляции #if, #elif, #else и #endif

      Директивы  условной  компиляции  #if,  #elif,  #else и #endif
 работают  аналогично  обыкновенным  условным  операторам  С.   Они
 используются следующим образом:

    #if выражение-1-типа-константы
    <раздел-1>
    <#elif выражение-2-типа-константы новая-строка раздел-2>
       ...
    <#elif выражение-n-типа-константы новая-строка раздел-n>

    <#else последний-раздел>
    #endif
    ...

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

      В  случае  "истина"  после обработки раздела-1 препроцессором
 управление передается соответствующей  директиве  #endif  (которая
 заканчивает   данную   условную   конструкцию)  и  продолжается  в
 следующем-разделе. В случае "ложь" управление передается следующей
 строке #elif (если  она  определена  в  данной  конструкции),  где
 вычисляется    выражение-2-типа-константы.   В   случае   "истины"
 обрабатывается  раздел-2,   после   чего   управление   передается
 соответствующей  директиве  #endif,  и  т.д.,  до тех пор, пока не
 будет   достигнута   последняя   директива   #else   или   #endif.
 Опциональная    директива    #else    используется    в   качестве
 альтернативного условия в том случае, если все предыдущие проверки
 дали  значение  "ложь".   Последовательность   условных   директив
 заканчивается директивой #endif.

      Обработанный  препроцессором  раздел  может  в  свою  очередь
 содержать вложенные на любую глубину условные предложения,  причем
 каждой  директиве #if должна соответствовать закрывающая директива
 #endif.

      Итоговым результатом вышеописанного сценария является то, что
 для дальнейшей обработки передается только один раздел  (возможно,
 пустой).   Опущенные   разделы   служат  только  для  отслеживания
 возможных вложенных условных конструкций, так что каждая директива
 #if  должна  обязательно  иметь  соответствующую  ей   завершающую
 директиву #endif.

      Проверяемые  выражения-типа-константы  при  вычислении должны
 давать целочисленную константу.

 Операция defined

      Операция defined дает  альтернативный,  более  гибкий  способ
 проверки  того, определены ли комбинации идентификаторов, или нет.
 Данная операция допустима только в выражениях #if и #elif.

      Выражение defined(идентификатор)  или  defined  идентификатор
 (скобки  необязательны) дает 1 (истина), если данное символическое
 имя было ранее определено (при помощи директивы  #defined)  и  это
 определение  не  было впоследствии отменено (при помощи #undef); в
 противном случае оно дает 0 (истина). Поэтому директива

    #if defined(mysym)

 это то же, что

    #ifdef mysym

      Преимущество  заключается  в  том,  что  можно   использовать
 defined  циклически  в  сложном выражении, следующем за директивой
 #if, например

    #if defined(mysym) && !defined(yoursym)

 Условные директивы #ifdef и #ifndef

      Условные директивы  #ifdef  и  #ifndef  позволяют  проверить,
 определен  ли  в текущий момент данный идентификатор, то есть была
 ли   обработана   предыдущая   директива   #define   для   данного
 идентификатора  и  продолжает ли она действовать в текущий момент.
 Строка

    #ifdef идентификатор

 имеет точно такой же эффект, что и

    #if 1

 если идентификатор в текущий момент определен, и такой же эффект,
 что и

    #if 0

 если идентификатор в текущий момент не определен.

      #ifndef, напротив, дает значение "истина", если идентификатор
 "не определен", поэтому строка

    #ifndef идентификатор

 имеет точно такой же эффект, что и

    #if 0

 если идентификатор в текущий момент определен, и такой же эффект,
 что и

    #if 1

 если идентификатор в текущий момент не определен.

      Синтаксис следует синтаксису директив  #if,  #elif,  #else  и
 #endif, описанному в предыдущем разделе.

      Идентификатор,   определенный   как   имеющий  пустое  (NULL)
 значение, считается определенным.
                  Директива управления нумерацией строк #line

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

    #line целочисленная-константа <"имя-файла">

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

    /* TEMP.C: пример директивы #line  */

    #include 

    #line 4 "junk.c"

    void main()
    {
       printf(" in line %d of %s",__LINE__,__FILE__);
    #line 12 "temp.c"
       printf("\n");
       printf(" in line %d of %s",__LINE__,__FILE__);
    #line 8
       printf("\n");
       printf(" in line %d of %s",__LINE__,__FILE__);
    }

      Включение stdio.h означает, что на выходе препроцессора будет
 нечто большее.

      Если запустить TEMP.C через CPP  (cpp  temp),  то  на  выходе
 получится файл TEMP.I, который выглядит так:

    temp.c 1:
    c:\borland\tc\cpp\include\stdio.h 1:
    c:\borland\tc\cpp\include\stdio.h 2:
    c:\borland\tc\cpp\include\stdio.h 3:
    ...
    c:\borland\tc\cpp\include\stdio.h 212:
    c:\borland\tc\cpp\include\stdio.h 213:
    temp.c 2:
    temp.c 3:
    junk.c 4: void main()
    junk.c 5: {
    junk.c 6: printf(" in line %d of %s",6,"junk.c");
    junk.c 7:
    temp.c 12: printf("\n");
    temp.c 13: printf(" in line %d of %s",13,"temp.c");
    temp.c 14:
    temp.c 8: printf("\n");
    temp.c 9: printf(" in line %d of %s",9,"temp.c");
    temp.c 10: }
    temp.c 11:

      Если  вы  затем  компилируете  TEMP.C, то получится следующий
 выход:

    in line 6 of junk.c
    in line 13 of temp.c
    in line 9 of temp.c

      Макросы расширяются в  аргументах  #line,  как  в  директивах
 #include.

      Прежде   всего   назначение  директивы  #line  заключается  в
 использовании ее в утилитах, имеющих на выходе  коды  С,  а  не  в
 кодах, создаваемых человеком "вручную".
                               Директива #error

      Директива #error имеет следующий синтаксис:

    #error сообщение-об-ошибке

      Директива генерирует сообщение:

    Error: имя-файла номер-строки : Error directive: сообщение

      Данная  директива  обычно встраивается в условные конструкции
 препроцессора,  которые   отслеживают   какие-либо   нежелательные
 условия  времени  компиляции.  Обычно  это  условие  "ложно". Если
 условие "истинно", то компилятор может выдать сообщение об  ошибке
 и  прекратить  работу.  Для  этого  директива  #error помещается в
 условную ветвь, которая дает для искомого  нежелательного  условия
 результат "истина".

      Например, вы определили #define MYVAL, принимающую значения 0
 или  1.  Затем  можно  включить в исходный код условную директиву,
 которая будет проверять MYVAL на предмет неверного значения:

    #if (MYVAL != 0 && MYVAL != 1)
    #error MYVAL must be defined to either 0 or 1
    #endif
                               Директива #pragma

      Директива  #pragma  позволяет  использовать  специфичные  для
 конкретных реализаций директивы в форме

    #pragma имя-директивы

      При  помощи  #pragma  Turbo  C++  позволяет  определить любые
 желаемые   директивы,   не   обращаясь   для   этого   к   другим,
 поддерживающим  их  компиляторам.  Если компилятор не поддерживает
 данное имя-директивы, то он просто игнорирует  директиву  #pragma,
 не   выдавая   при   этом   никаких   сообщений   об  ошибках  или
 предупреждений.

      Turbo C++ поддерживает следующие директивы #pragma:

 - #pragma argsused

 - #pragma exit

 - #pragma inline

 - #pragma option

 - #pragma saveregs

 - #pragma startup

 - #pragma warn


 Директива #pragma argsused

      Директива   #pragma   argsused   допустима    только    между
 определениями функций и действует только на следующую функцию. Она
 отменяет сообщение уровня предупреждения:

    "Parameter name is never used in function  имя-функции"
   ("имя параметра нигде не используется в функции  имя-функции")

 Директивы #pragma exit и #pagma startup

      Данные  две  директивы  позволяют  программе  задать  функцию
 (функции), которые должны вызываться либо при  загрузке  программы
 (перед    вызовам    main),   либо   при   выходе   из   программы
 (непосредственно перед выходом из программы через _exit).

      Синтаксис этих директив следующий:

    #pragma exit имя-функции <приоритет>
    #pragma startup имя-функции <приоритет>

      Заданное имя-функции должно относиться  к  ранее  объявленной
 функции,  не  принимающей аргументов и возвращающей значение void;
 другими словами, эта функция должна быть объявлена как:

    void func(void);

      Опциональный параметр приоритет должен являться целым  числом
 в  диапазоне  от  64  до  255.  Старшим  приоритетом  является  0.
 (Приоритеты от 0 до 63 используются библиотеками  С  и  не  должны
 использоваться  пользователем).  Функции  со старшими приоритетами
 вызываются первыми при загрузке программы и последними при  выходе
 из  нее.  Если  приоритет  не задан, то по умолчанию он равен 100.
 Например,

    #include 

    void startFunc(void)
    {
       printf("Startup function.\n");
    }

    #pragma startup startFunc 64
    /* приоритет 64 --> вызывается при загрузке первой */

    void exitFunc(void)
    {
       printf("Wrapping up execution.\n");
    }

    #pragma exit exitFunc
    /* приоритет по умолчанию равен 100 */

    void main(void)
    {
       printf(This is main.\n");
    }

      Отметим, что имя функции, используемой в #pragma startup  или
 exit,   должно  быть  определено  (или  объявлено)  до  того,  как
 встретится соответствующая строка с директивой #pragma.

 Директива #pragma inline

      Данная директива  эквивалентна  опции  компилятора  командной
 строки  -B  или соответствующей опции интегрированной среды Turbo.
 Она  сообщает  компилятору,  что  программа  содержит   встроенные
 ассемблерные  коды (см. главу 6, "Интерфейс с языком ассемблера").
 Синтаксис ее следующий:

    #pragma inline

      Эту директиву лучше всего помещать вверху  файла,  поскольку,
 встретив директиву #pragma inline, компилятор перезапускает себя с
 опцией  -B.  Фактически  можно  опустить  и  опцию -В, и директиву
 #pragma inline, и компилятор тем  не  менее  выполнит  перезапуск,
 когда встретит операторы asm. Назначение опции и директивы состоит
 в том, чтобы сэкономить время компиляции.

 Директива #pragma option

      Директива  #pragma  option  используется  для включения опций
 компилятора командной строки в код вашей программы. Синтаксис этой
 директивы следующий:

    #pragma option [опции...]

      Опции могут являться  любыми  опциями  командной  строки  (за
 исключением   перечисленных   в   следующем  параграфе).  В  одной
 директиве может находиться любое число опций.

      Ниже  приводятся  опции,  которые  не  могут   находиться   в
 директиве pragma option:

    -B (компиляция с использованием ассемблерных кодов)

    -c (компиляция без компоновки)

    -dxxx (определение макроса)

    -Dxxx = ccc (определение макроса с текстом)

    -efff (имя .EXE-файла fff)

    -lfff (имя включаемой директории)

    -Lfff (имя директории с библиотекой)

    -lxset (опция компоновщика x)

    -M (создание при компоновке .MAP-файла)

    -o оверлеи

    -Q EMS

    -S (создание на выходе .ASM-файла и остановка)

    -Uxxx (отмена определения макроса)

    -V (virtual)

    -Y (оверлеи)

      Существует  два  состояния  компилятора. В первом состоянии в
 директиву pragma option можно включить большее  количество  опций,
 нежели   во   втором.   Первое  состояние  компилятора  называется
 состоянием   лексического   анализа,   а   второе   -   состоянием
 кодирования.

      Использование  имени  макроса,  начинающегося двумя символами
 подчеркивания (которое может являться именем встроенного макроса),
 в директивах #if, #ifdef, #ifndef  или  #elif  изменяет  состояние
 компилятора на состояние кодирования.

      Появление  первой  реальной  лексемы  (первого  объявления С)
 также изменяет состояние компилятора на кодирование.

      Другими  словами,  можно  использовать   директивы   #pragma,
 #include, #define и некоторые разновидности #if во время состояния
 лексического  анализа  компилятора.  Во  время этой фазы вы имеете
 возможность при помощи #pragma  option  изменять  опции  командной
 строки.

      В  число  опций,  которые  могут быть заданы в #pragma option
 только в состоянии лексического анализа компилятора, входят:

    -Efff (строка с ассемблерным именем)

    -f* (любая опция плавающей точки, кроме -ff)

    -l# (значащие символы идентификатора)

    -m* (любая опция модели памяти)

    -nddd (выходная директория)

    -offf (имя выходного файла fff)

    -u (использование символов подчеркивания в именах cdecl)

    -z* (опция задания любого имени сегмента)

      Прочие опции могут изменяться  где  угодно.  Следующие  опции
 оказывают  воздействие  на  компилятор  только если они изменяются
 между объявлениями функций или объектов:

    -1        Управление набором команд

    -2        Управление набором команд

    -a        Управление выравниванием. (Отметим, что выравнивание
              компонентов   структуры   устанавливается   в   точке
              определения  структуры, а не далее, при использовании
              этой структуры объектами.)

    -ff       Управление быстрыми операциями с плавающей точкой

    -G        Генерация кода, оптимизированного по быстродействию

    -k        Стандартное управление стековым фреймом

    -N        Управление контролем стека

    -O        Управление оптимизацией

    -P        Установка по умолчанию соглашений о связях Pascal

    -r и -rd  Управление регистровыми переменными

    -v        Управление отладкой по действиям

    -y        Управление строчной информацией


      Следующие  опции  могут изменяться в любой момент и оказывают
 немедленное воздействие на компилятор:

    -A        Управление ключевыми словами

    -C        Управление вложенностью комментариев

    -d        Слияние повторяющихся строк

    -gn       Остановка компиляции после n предупреждений

    -jn       Остановка компиляции после n ошибок

    -K        Тип char устанавливается как unsigned

    -wxxx     Предупреждение (то же самое, что и #pragma warn)

      Любые  из  имеющих два переключающихся состояния опций (такие
 как -a или -K) могут быть переведены во включенное или выключенное
 состояние, как это  делается  в  командной  строке.  Дополнительно
 имеется  возможность  задавать  их с точкой (.), что устанавливает
 такие опции в состояние, задаваемое командной строкой.

 Директива #pragma saveregs

      Директива #pragma  saveregs  гарантирует,  что  при  входе  в
 функцию  huge  значения  всех  регистров  останутся без изменений.
 Данная директива иногда бывает нужна для интерфейса  с  кодами  на
 языке  ассемблера.  Директива  должна  находиться  непосредственно
 перед определением функции. Ее действие распространяется только на
 данную функцию.

 Директива #pragma warn

      Данная директива позволяет  переопределять  конкретные  опции
 командной  строки  -wxxx  (или управлять опцией Display Warnings в
 диалоговом поле Options | Compiler | Messages).

      Например, если в вашем исходном коде имеются директивы

    #pragma warn +xxx
    #pragma warn -yyy
    #pragma warn .zzz

 то выдача предупреждения xxx будет разрешена (даже если в меню
 Options | Compiler | Messages она была переведена в состояние off),
 предупреждения yyy запрещена, а статус выдачи сообщения zzz будет
 восстановлен в то состояние, которое было к моменту начала компи-
 ляции файла.

      Полный список трехбуквенных сокращений и сообщений, к которым
 они относятся, см. в Главе  4,  "Компилятор  командной  строки"  в
 Руководстве пользователя.
                           Предопределенные макросы

      Turbo   C++   имеет   следующие  предопределенные  глобальные
 идентификаторы.  За  исключением  __cplusplus,  каждое  из  них  и
 начинается,  и  заканчивается  двумя символами подчеркивания (__).
 Эти макросы также называют буквальными константами.

 __CDECL__

      Данный макрос специфичен для Turbo C++. Он  сообщает  о  том,
 что  флаг  -p не использовался (меню Cflling Conventions...C) : он
 устанавливается равным целочисленной  константе  1,  если  -p  не
 использовался; в противном случае он неопределен.

      Следующие символические имена определяются согласно выбранной
 во время компиляции модели памяти:

    __COMPACT__       __MEDIUM__
    __HUGE__          __SMALL__
    __LARGE__         __TINY__

      Для  конкретной  компиляции определенным является только один
 из этих макросов; прочие, по определению, не определены. Например,
 если  при  компиляции  выбрана  модель  памяти  small,  то  макрос
 __SMALL__ определен, а остальные неопределены, поэтому директива

    #if defined(__SMALL__)

 даст значение "истина", а

    #if defined(__HUGE__)

      либо  любая  другая  из  оставшихся)  даст  значение  "ложь".
 Фактическое значение любого из этих макросов, когда он  определен,
 равно 1.

 __cplusplus

      Данный макрос специфичен для Turbo C++. Он позволяет написать
 модуль, который в некоторых случаях будет компилироваться в С, а в
 некоторых  -  в  С++.  Использование  условных директив компиляции
 позволяет управлять, какие части  программы  для  С  и  С++  будут
 включены в компиляцию.

 __DATE__

      Данный  макрос  дает  дату  начала  обработки  препроцессором
 данного исходного файла (в виде строкового литерала).

      Каждое включение __DATE__ в данный файл дает  одно  и  то  же
 значение,   независимо   от  того,  сколько  времени  продолжается
 обработка файла препроцессором. Дата имеет  форму  mmmddyyyy,  где
 mmm  это  месяц  (Jan, Feb и т.д.), dd равно числу месяца (от 1 до
 31, причем если это число меньше 10,  то  первый  символ  d  равен
 пробелу), а yyyy это год (1990, 1991 и т.д.)

 __FILE__

      Данный    макрос    дает    имя    текущего   обрабатываемого
 препроцессором исходного файла (в виде строкового литерала). Когда
 препроцессор обрабатывает директиву #include или #line,  либо  при
 завершении    обработки    включаемого    файла,   данный   макрос
 соответствующим образом меняет свое значение.

 __LINE__

      Данный макрос дает количество обработанных  препроцессором  к
 данному  моменту  строк  текущего  исходного  файла. Обычно первая
 строка исходного файла определяется с номером 1, хотя на это может
 повлиять директива #line. Информацию о директиве #line см. на стр.
 144 оригинала.

 __MSDOS__

      Данный макрос специфичен для Turbo C++. Он дает целочисленную
 константу 1 для всех случаев компиляции.

 __OVERLAY__

      Данный макрос специфичен для С++. Он предопределен равным  1,
 если   модуль   компилируется  с  опцией  -Y  (включена  поддержка
 оверлейных структур). Если оверлейные структуры не поддерживаются,
 то данный макрос неопределен.

 __PASCAL__

      Данный макрос специфичен для С++. Он сообщает о том, что  был
 использован   флаг  -p.  Макрос  установлен  равным  целочисленной
 константе 1, если флаг -p использовался;  в  противном  случае  он
 неопределен.

 __STDC__

      Данный  макрос  определен равным константе 1, если компиляция
 выполняется при установленном флаге совместимости с ANSI  (-A  или
 меню   ANSI   Keywords   Only...On);  в  противном  случае  макрос
 неопределен.

 __TIME__

      Данный макрос  дает  время  начала  обработки  препроцессором
 текущего исходного файла (в виде строкового литерала).

      Как  и  в  случае макроса __DATE__, каждое включение __TIME__
 будет содержать одно и то же значение, независимо от того, сколько
 времени продолжалась обработка файла. Он  имеет  формат  hh:vv:ss,
 где  hh  это часы (от 00 до 23), mm это минуты (от 00 до 59), а ss
 это секунды (от 00 до 59).

 __TURBOC__

      Данный макрос специфичен  для  С++.  Он  дает  номер  текущей
 версии  Turbo  C++  в  виде шестнадцатиричной константы. Например,
 версия 1.0 будет представлена в виде 0x0100.

        Глава 2.  Перекрестные ссылки по библиотеке исполняющей системы

      Данная глава содержит обзор библиотечных подпрограмм Turbo
 C++ и включаемых файлов.

      В данной главе

 - объясняется, зачем вам могут понадобиться исходные коды библио-
   теки исполняющей системы Turbo C++

 - перечисляются и описываются файлы заголовка

 - библиотечные подпрограммы группируются в соответствии с различ-
   ными категориями выполняемых ими задач.

      Turbo C++ поставляется с более чем 450 функциями и макросами,
 которые  вы  можете  вызывать  из  своей  программы для выполнения
 широкого круга задач, включая задачи  ввода/вывода  как  высокого,
 так   и   низкого   уровня,  манипуляции  с  потоками  и  файлами,
 распределения  памяти,   преобразования   данных,   математических
 вычислений  и  многие  другие.  Эти  функции и макросы, называемые
 библиотечными подпрограммами, подробно описаны  в  Справочнике  по
 библиотеке.

      Подпрограммы  Turbo  C++  содержатся  в  библиотечных  файлах
 (Cx.LIB, CPx.LIB, MATHx.LIB и GRAPHICS.LIB). Поскольку  Turbo  C++
 поддерживает  шесть  различных  моделей  памяти, каждая модель, за
 исключением модели tiny, имеет свой собственный библиотечный  файл
 и  файл  математических  функций,  в которых находятся версии этих
 подпрограмм специально для соответствующей модели памяти.  (Модель
 tiny использует одни файлы с моделью small).

      В  С++  всегда  нужно  использовать прототипы. Дополнительную
 информацию о прототипах функций см. на стр.60 оригинала).

      Turbo C++ является реализацией последнего стандарта  ANSI  C,
 который,  помимо всего прочего, позволяет (и усиленно рекомендует)
 задавать прототипы функций для используемых в вашей программе на С
 подпрограмм.

      Библиотечные подпрограммы Turbo  C++  объявляются  со  своими
 прототипами в одном или более файлов заголовка.
      Зачем нужен доступ к исходным кодам библиотеки исполняющей системы

      Библиотека  исполняющей  системы Turbo C++ содержит свыше 450
 функций, относящихся к широкому диапазону задач: управление IBM PC
 на  нижнем  уровне,  интерфейс  с  DOS,   ввод/вывод,   управление
 обработкой,  манипуляции  со  строками  и  памятью, математические
 вычисления, сортировка и поиск, и т.д. Существует несколько веских
 причин, по которым вам может понадобиться доступ к исходным  кодам
 этих функций:

 - Может   обнаружиться,  что  некоторая  функция,  которую  вы
   собираетесь   написать,   аналогична,   однако   не    полностью
   эквивалентна,  функции из библиотеки. Имея доступ к ее исходному
   коду в библиотеке исполняющей  системы,  вы  имеете  возможность
   скорректировать  ее  по своим требованиям, что позволит избежать
   создания собственной отдельной функции.

 - Иногда при отладке требуется побольше узнать о том, как устроена
   та или  иная  библиотечная  функция.  Доступ  к  исходным  кодам
   библиотеки исполняющей системы в данной ситуации весьма полезен.

 - Когда вы не можете в точности выяснить, что конкретно делает та
   или иная функция, полезно взглянуть на ее исходный код.

 - Вам может понадобиться убрать ведущие символы подчеркивания в
   символических  именах  С. Это можно сделать только имея доступ к
   исходным кодам функций библиотеки исполняющей системы.

 - Вы многому можете научиться, изучая сжатые, профессионально на-
   писанные исходные коды библиотечных функций.

      По всем этим причинам, а также по многим  другим,  вам  может
 понадобиться   доступ  к  исходным  кодам  библиотеки  исполняющей
 системы Turbo C++. Поскольку фирма Borland придерживается принципа
 "открытой    архитектуры",    мы     предоставляем     возможность
 лицензированного  доступа  к исходным кодам библиотеки исполняющей
 системы Turbo C++. Все, что  вам  нужно  для  этого  сделать,  это
 заполнить  бланк,  поставляемый  с  пакетом  Turbo  C++, приложить
 платежный чек,  и  вам  будут  высланы  исходные  коды  библиотеки
 исполняющей системы Turbo C++.
                           Файлы заголовка Turbo C++

      Файлы    заголовка,    определяемые    стандартом   ANCI   C,
 соответственно обозначены на  полях.  Файлы  заголовка  С++  также
 обозначены на полях слева.

      Файлы   заголовка,   называемые  также  включаемыми  файлами,
 содержат объявления прототипов библиотечных функций. В  них  также
 находятся  определения  типов данных и символических имен констант
 библиотечных функций, а также глобальные переменные,  определяемые
 Turbo  C++  и библиотечными функциями. Имена файлов заголовка и их
 содержимое в библиотеках Turbo C++ следуют стандарту ANSI C.

         alloc.h     Объявляет функции управления памятью (распре-
                     деление и отмена распределения памяти и т.д.)

 ANSI C  assert.h    Определяет отладочный макрос assert

 C++     bcd.h       Определяет класс С++ bcd и перегруженные опе-
                     рации для класса bcd и математические функции
                     для bcd

         bios.h      Объявляет различные функции, используемые при
                     вызове подпрограмм ROM BIOS IBM PC

 C++     complex.h   Объявляет комплексные математические функции
                     С++

         conio.h     Объявляет различные функции, используемые при
                     вызове подпрограмм DOS ввода/вывода с консоли

 ANSI C  ctype.h     Содержит информацию, используемую макросами
                     символьной классификации и символьных преобра-
                     зований (такими, как isalpha и toascii)

         dir.h       Содержит структуры, макросы и функции для
                     работы с директориями и путями доступа

         dos.h       Определяет различные константы и содержит объ-
                     явления, необходимые при вызовах DOS и специ-
                     альных вызовах 8086

 ANSI C  errno.h     Определяет мнемонические константы кодов
                     ошибок

         fcntl.h     Определяет символические константы, исполь-
                     зуемые совместно с библиотечной подпрограммой
                     open

 ANSI C  float.h     Содержит параметры подпрограмм обработки чисел
                     с плавающей точкой.

 C++     fstream.h   Объявляет классы потоков С++, поддерживающие
                     ввод-вывод в файлы

 C++     generic.h   Содержит макрос для объявлений родовых файлов

         graphics.h  Объявляет прототипы графических функций

         io.h        Содержит структуры и объявления подпрограмм
                     ввода/вывода низкого уровня

 C++     iomanip.h   Объявляет манипуляторы ввода/вывода потоков
                     С++ и содержит макрос для создания параметри-
                     зованных манипуляторов

 C++     iostream.h  Объявляет подпрограммы (ввода/вывода) потоков
                     базового (версии 2.0) С++

 ANSI C  limits.h    Содержит параметры среды программирования,
                     информацию об ограничениях времени компиляции,
                     а также численные диапазоны интегральных
                     типов

 ANSI C  locale.h    Объявляет функции, содержащие информацию, спе-
                     цифичную для конкретной страны и языка

 ANSI C  math.h      Объявляет прототипы математических функций;
                     определяет макрос HUGE_VAL и объявляет струк-
                     туру исключения, используемую подпрограммой
                     matherr

         mem.h       Объявляет функции манипулирования памятью.
                     (Многие из них также определены в string.h)

         process.h   Содержит структуры и объявления для функций
                     spawn... и exec...

 ANSI C  setjmp.h    Определяет тип jmp_buf, используемый функциями
                     longjmp и setjmp, и объявляет подпрограммы
                     longjmp и setjmp

         share.h     Определяет параметры, используемые в функциях,
                     работающих с разделением файла

 ANSI C  signal.h    Определяет макросы, используемые для чтения
                     списков аргументов функций, объявленных как
                     принимающие переменное число аргументов
                     (например, vprintf, vscanf и т.д.)

 ANSI C  stddef.h    Определяет несколько общих типов данных и
                     макросов

 ANSI C  stdio.h     Определяет типы данных и макросы, необходимые
                     для пакета стандартного ввода/вывода (Standard
                     I/O Package), определенного Керниганом и Ритчи
                     и расширенного в Системе UNIX V.
                     Определяет предопределенные потоки стандарт-
                     ного ввода/вывода stdin, stdout, stdprn и
                     stderr, а также объявляет подпрограммы ввода/
                     вывода уровня потоков

 C++     stdiostr.h  Объявляет классы потоков С++ для использования
                     в файловых структурах stdio FILE.

 ANSI C  stdlib.h    Объявляет некоторые широко используемые под-
                     программы: подпрограммы преобразования, под-
                     программы поиска/сортировки и прочие

 C++     stream.h    Объявляет подпрограммы (ввода/вывода) потоков
                     С++ (версии 1.2)

 ANSI C  string.h    Объявляет несколько подпрограмм строковых
                     манипуляций и манипуляций с памятью

 C++     strstrea.h  Объявляет классы потоков С++ для работы
                     с байтовыми массивами в памяти

         sys\stat.h  Объявляет символические константы, используе-
                     мые при открытии и создания файлов

         sys\timeb.h Объявляет функцию time и структуру timeb,
                     возвращаемую time

         sys\types.h Объявляет тип type_t, используемый функциями
                     времени

 ANSI C  time.h      Определяет структуру, заполняемую подпрограм-
                     мами преобразования времени asctime, localtime
                     и gmtime, а также тип, используемый подпрог-
                     раммами ctime, difftime, gmtime, localtime и
                     stime; также содержит прототипы этих
                     подпрограмм.

         values.h    Определяет важные константы, включая машино-
                     зависимые константы; обеспечивает совмести-
                     мостью с системой UNIX V
                      Категории библиотечных подпрограмм
 -----------------------------------------------------------------

      Библиотечные  подпрограммы  Turbo   C++   выполняют   большое
 количество  различных  задач.  В  данном  разделе перечислены все
 подпрограммы, а также включаемые файлы, в которых  они  объявлены,
 разбитые  на  несколько  общих  категорий  по выполняемым задачам.
 Полную информацию по перечисленным ниже функциям  см.  в  главе  1
 "Библиотека исполняющей системы" Справочника по библиотеке.

 Подпрограммы классификации   ------------------------------------

      Данные  подпрограммы  классифицируют символы ASCII как буквы,
 управляющие символы, знаки пунктуации, символы верхнего регистра и
 т.д.

      isalnum (ctype.h)   isascil (ctype.h)   isdigt  (ctype.h)
      isalpha (ctype.h)   iscntrl (ctype.h)   isgraph (ctype.h)
      islower (ctype.h)   ispunct (ctype.h)   isupper (ctype.h)
      isprint (ctype.h)   isspace (ctype.h)   isxdigit(ctype.h)

 Подпрограммы преобразования   -----------------------------------

      Данные подпрограммы выполняют преобразования символов и строк
 из  алфавитного  в  различные  числовые  представления  (числа   с
 плавающей   точкой,   целые  числа,  long)  и  обратно,  а  также
 преобразования символов верхнего регистра к нижнему, и обратно.

      atof    (stdlib.h)  itoa    (stdlib.h)  _tolower (ctype.h)
      atof    (stdlib.h)  itoa    (stdlib.h)  tolower  (ctype.h)
      atof    (stdlib.h)  strtod  (stdlib.h)  _tolower (ctype.h)
      ocvt    (stdlib.h)  strtol  (stdlib.h)  tolower  (ctype.h)
      fcvt    (stdlib.h)  strtcul (stdlib.h)  ultoa    (stdlib.h)
      gcft    (stdlib.h)  toascil (ctype.h)

 Подпрограммы управления директориями   --------------------------

      Эти подпрограммы манипулируют  директориями  и  именами  пути
 доступа.

      chdir     (dir.h)     fnsplit   (dir.h)     mkdir    (dir.h)
      findfirst (dir.h)     getcurdir (dir.h)     mktemp   (dir.h)
      findnext  (dir.h)     getcwd    (dir.h)     rmdir    (dir.h)
      fnmerge   (dir.h)     detdisk   (dir.h)    searchpath(dir.h)
                                                  setdisk  (dir.h)

 Диагностические подпрограммы   ----------------------------------

      Эти подпрограммы реализуют встроенные средства поиска ошибки.

      assert    (assert.h)
      matherr   (math.h)
      perror    (errno.h)

 Графические подпрограммы   --------------------------------------

      Эти  подпрограммы  позволяют  создавать  экранные графические
 представления с текстовой частью.

      arc         (graphics.h)     fillellipse       (graphics.h)
      bar         (graphics.h)     fillpoly          (graphics.h)
      bar3d       (graphics.h)     floofill          (graphics.h)
      circle      (graphics.h)     getarccoords      (graphics.h)
      cleardevice (graphics.h)     getaspectratio    (graphics.h)
      clearviewport(graphics.h)    getbkcolor        (graphics.h)
      closgraph    (graphics.h)    getcolor          (graphics.h)
      detectgraph  (graphics.h)    getdefaultpallette(graphics.h)
      drawpoly     (graphics.h)    getdrivername     (graphics.h)
      ellipse      (graphics.h)    getfillpattern    (graphics.h)
      getfillsettings (graphics.h) outtext (graphics.h)
      getgraphmode    (graphics.h) outtextxy(graphics.h)
      getimage        (graphics.h) pieslice (graphics.h)
      getfinesettings (graphics.h) pufimage (graphics.h)
      getmaxcolor     (graphics.h) pulpixel (graphics.h)
      getmaxmode      (graphics.h) rectangle(graphics.h)
      getmaxx         (graphics.h) registerbgidriver(graphics.h)
      getmaxy         (graphics.h) registerbgifont  (graphics.h)
      getmodename     (graphics.h) restorecrtmode   (graphics.h)
      getmoderange    (graphics.h) sector           (graphics.h)
      getpalette      (graphics.h) settaffpalette   (graphics.h)
      getpixel        (graphics.h) setaspectratio   (graphics.h)
      gettextsettings (graphics.h) setbkcolor       (graphics.h)
      getviewsettings (graphics.h) setcolor         (graphics.h)
      getx            (graphics.h) setcursortype    (conio.h)
      gety            (graphics.h) setfillpattern   (graphics.h)
      graphdefaults   (graphics.h) setfillstyle     (graphics.h)
      grapherrormsg   (graphics.h) setgraphbufsize  (graphics.h)
      _graphfreemem   (graphics.h) setgraphmode     (graphics.h)
      _graphgetmem    (graphics.h) setlinestyle     (graphics.h)
      graphresult     (graphics.h) setpalette       (graphics.h)
      imagesize       (graphics.h) setrgbpalette    (graphics.h)
      initgraph       (graphics.h) settextjunistify (graphics.h)
      installuserdriver(graphics.h)settexttyle      (graphics.h)
      installuserfont  (graphics.h)setusercharsize  (graphics.h)
      line             (graphics.h)setviewport      (graphics.h)
      linerel          (graphics.h)setvisualpage    (graphics.h)
      lineto           (graphics.h)setwritemode     (graphics.h)
      moverei          (graphics.h)textheight       (graphics.h)
      moveto           (graphics.h)textwidth        (graphics.h)

 Подпрограммы ввода/вывода   -------------------------------------

      Эти подпрограммы реализуют средства  ввода/вывода  на  уровне
 потоков и DOS.

      access      (io.h)                creatnew         (io.h)
      cgets       (conio.h)             creattemp        (io.h)
      _chmod      (io.h)                cscanf        (conio.h)
      chmod       (io.h)                dup           (io.h)
      chsize      (io.h)                dup2          (io.h)
      clearerr    (stdio.h)             eof           (io.h)
      _close      (io.h)                fclosse       (stdio.h)
      close       (io.h)                fcloseali     (ctdio.h)
      cprintf     (conio.h)             fdopen        (stdio.h)
      cputs       (conio.h)             foof          (stdio.h)
      _creat      (io.h)                ferror        (stdio.h)
      creat       (io.h)                fflush        (stdio.h)
      fgetc       (stdio.h)             printf        (stdio.h)
      fgetchar    (stdio.h)             putc          (stdio.h)
      fgetpos     (stdio.h)             putch         (conio.h)
      fgets       (stdio.h)             putchar       (stdio.h)
      fllelength  (io.h)                puts          (stdio.h)
      flleno      (stdio.h)             putw          (stdio.h)
      flushall    (stdio.h)             _read         (io.h)
      fopen       (stdio.h)             read          (io.h)
      fprintf     (stdio.h)             remove        (stdio.h)
      fputc       (stdio.h)             rename        (stdio.h)
      fputchar    (stdio.h)             rewind        (stdio.h)
      fputs       (stdio.h)             scanf         (stdio.h)
      fread       (stdio.h)             setbuf        (stdiio.h)
      freopen     (stdio.h)             setcursortype (conio.h)
      fscanf      (stdio.h)             setftime      (io.h)
      fseek       (stdio.h)             setmode       (io.h)
      fsetpos     (stdio.h)             setvbuf       (stdio.h)
      fstat       (sys\stat.h)          sopen         (io.h)
      ftell       (stdio.h)             sprintf       (stdio.h)
      fwrite      (stdio.h)             sscanf        (stdio.h)
      getc        (stdio.h)             stat          (sys\stat.h)
      getch       (conio.h)           _strerror (string.h,stdio.h)
      getchar     (stdio.h)           strerorr        (stdio.h)
      getche      (conio.h)           tell            (io.h)
      getftime    (io.h)              tmpfile         (stdio.h)
      getpaus     (conio.h)           tmpnam          (stdio.h)
      gets        (stdio.h)           ungetc          (stdio.h)
      getw        (stdio.h)           ungetch         (conio.h)
      iocti       (io.h)              unlock          (io.h)
      isatty      (io.h)              vfprintf        (stdio.h)
      kbhit       (conio.h)           vfscanf         (stdio.h)
      lock        (io.h)              vprintf         (stdio.h)
      iseek       (io.h)              vscanf          (stdio.h)
      _open       (io.h)              vsprintf        (stdio.h)
      open        (io.h)              vsscanf         (io.h)
      perror      (stdio.h)           _write          (io.h)

 Подпрограммы интерфейса   ---------------------------------------
 (DOS, 8086, BIOS)

      Эти  подпрограммы реализуют обращения к средствам DOS, BIOS и
 специфичным средствам данного компьютера.

      absread  (dos.h) bioskey   (bios.h)  dosexterr (dos.h)
      abswrite (dos.h) bioskey   (bios.h)  enable    (dos.h)
      bdos     (dos.h) biosprint (bios.h)  FP_OFF    (dos.h)
      bdosptr  (dos.h) biostime  (bios.h)  FP_SEG    (dos.h)
      bioscom  (bios.h)country   (dos.h)   freemem   (dos.h)
      blosdisk (bios.h)ctrlbrk   (dos.h)   geninterrupt(dos.h)
      biosequip(bios.h)disable   (dos.h)   getcbrk    (dos.h)
      getdfree (dos.h) int86     (dos.h)   poke       (dos.h)
      getdta   (dos.h) int86x    (dos.h)   pokeb      (dos.h)
      getfat   (dos.h) intdos    (dos.h)   randbrd    (dos.h)
      getfatd  (dos.h) intdosx   (dos.h)   randbwr    (dos.h)
      getpsp   (dos.h) intr      (dos.h)   segread    (dos.h)
      getvect  (dos.h) keer      (dos.h)   setcbrk    (dos.h)
      getverity(dos.h) MK_FP     (dos.h)   setdta     (dos.h)
      harderr  (dos.h) outport   (dos.h)   setvect    (dos.h)
      hardresume(dos.h)outportb  (dos.h)   setverity  (dos.h)
      hardretn  (dos.h)parsfnm   (dos.h)   sleep      (dos.h)
      inport    (dos.h)peek      (dos.h)   unlink     (dos.h)
      inportb   (dos.h)peekb     (dos.h)

 Подпрограммы манипуляции   --------------------------------------

      Эти  подпрограммы  обрабатывают  строки   и   блоки   памяти:
 копирование, сравнение, преобразования и поиск.

      memccpy   (mem.h,string.h)      stricmp   (string.h)
      memchr    (mem.h,string.h)      stricmpi  (string.h)
      memcmp    (mem.h,string.h)      sprien    (string.h)
      memcpy    (mem.h,string.h)      striwr    (string.h)
      memicmp   (mem.h,string.h)      stncat    (string.h)
      memmoye   (mem.h,string.h)      stncmp    (string.h)
      memset    (mem.h,string.h)      strncmpi  (string.h)
      movedata  (mem.h,string.h)      strncpy   (string.h)
      movmem    (mem.h,string.h)      strnicmp  (string.h)
      setmem    (mem.h)               strnset   (string.h)
      stpcpy    (string.h)            strpbrk   (string.h)
      strcat    (string.h)            strrchr   (string.h)
      strchr    (string.h)            strrev    (string.h)
      strcmp    (string.h)            strset    (string.h)
      strcoll   (string.h)            strspn    (string.h)
      strcpy    (string.h)            strstr    (string.h)
      strcspn   (string.h)            strtok    (string.h)
      strdup    (string.h)            strupr    (string.h)
      strerror  (string.h)            strxfrm   (string.h)

 Математические подпрограммы   -----------------------------------

      Эти   подпрограммы   выполняют  математические  вычисления  и
 преобразования.

      abs     (complex.h,stdlib.h)     atof     (stdlib.h,math.h)
      acos    (complex.h,math.h)       atoi     (stdlib.h)
      arg     (complex.h)              atol     (stdlib.h)
      asin    (complex.h,math.h)       bcd      (std.h)
      atan    (complex.h,math.h)       cabs     (math.h)
      atan2   (complex.h,math.h)       ceil     (math.h)
      clear87 (float.h)                ltoa     (stdlib.h)
      complex (complex.h)              _matherr (math.h)
      conj    (complex.h)              matherr  (math.h)
      _control(float.h)                modf     (math.h)
      cos     (complex.h,math.h)       norm     (complex.h)
      cosh    (complex.h,math.h)       polar    (complex.h)
      div     (math.h)                 poly     (math.h)
      ecvt    (stdlib.h)               pow      (complex.h,math.h)
      exp     (math.h)                 pow10    (math.h)
      fabs    (math.h)                 rand     (stdlib.h)
      fcvt    (stdlib.h)               random   (stdlib.h)
      floor   (math.h)                 randomize(stdlib.h)
      fmod    (math.h)                 real     (complex.h)
      _fpreset(float.h)                _rotl    (stdlib.h)
      frexp   (math.h)                 _rotr    (stdlib.h)
      gcvt    (stdlib.h)               sin      (complex.h,math.h)
      hypot   (math.h)                 sinh     (complex.h,math.h)
      imag    (complex.h)              sqrt     (complex.h,math.h)
      itoa    (stdlib.h)               srand    (stdlib.h)
      labs    (stdlib.h)               _status87(float.h)
      ldexp   (math.h)                 strtod   (stdlib.h)
      ldiv    (math.h)                 strtol   (stdlib.h)
      log     (complex.h,math.h)       strtoul  (stdlib.h)
      log10   (complex.h,math.h)       tan      (complex.h,math.h)
      _lrotl  (stdlib.h)               tanh     (complex.h,math.h)
      _lrotr  (stdlib.h)               ultoa    (stdlib.h)

 Подпрограммы управления памятью   -------------------------------

      Эти  подпрограммы  обеспечивают  динамическое   распределение
 памяти для моделей данных small и large

      allocmem        (dos.h)          farrealloc      (alloc.h)
      brk             (alloc.h)        free            (alloc.h,
      calloc          (alloc.h)                         stdlib.h)
      coreleft        (alloc.h,        heapcheck       (alloc.h)
                       stdlib.h)       heapcheckfree   (alloc.h)
      farcalloc       (alloc.h)        heapcheckknode  (alloc.h)
      farcoreleft     (alloc.h)        heapwalk        (alloc.h)
      farfree         (alloc.h)        malloc          (alloc.h,
      farheapcheck    (alloc.h)                         stdlib.h)
      farheapcheckfree(alloc.h)        realloc         (alloc.h,
      farheapchecknode(alloc.h)                         stdlib.h)
      farheapfllfree  (alloc.h)        sbrk            (alloc.h)
      farheapwalk     (alloc.h)        setblock        (dos.h)
      farmalloc       (alloc.h)

 Разные подпрограммы   -------------------------------------------

      Эти  подпрограммы  предоставляют средства перехода за пределы
 локального  контекста,  различные  звуковые  эффекты  и  локальные
 эффекты.

      delay           (dos.h)          setjmp        (setjmp.h)
      localeconv      (locale.h)       setlocale     (locale.h)
      longjmp         (setjmp.h)       sound         (dos.h)
      nosound         (dos.h)

 Подпрограммы управления процессами   ----------------------------

      Эти  подпрограммы  запускают  и  завершают  выполнение  одних
 процессов из других.

  abort  (process.h) execvp (process.h) spawnl (process.h)
  execl  (process.h) execvpe(process.h) spawnle (process.h)
  execle (process.h) _exit  (process.h) spawnlp (process.h)
  execlp (process.h) exit   (process.h) spawnlpe(process.h)
  execlpe(process.h) getpid (process.h) spawnv  (process.h)
  execv  (process.h) reise  (signal.h)  spawnve (process.h)
  execve (process.h) signal (signal.h)  spawnvp (process.h)
                                        spawnvpe(process.h)

 Стандартные подпрограммы   --------------------------------------

      Эти подпрограммы являются стандартными.

  abort  (stdlib.h)  exit   (stdlib.h)  malloc  (stdlib.h)
  abs    (stdlib.h)  fcvt   (stdlib.h)  putenv  (stdlib.h)
  atexit (stdlib.h)  free   (stdlib.h)  qsort   (stdlib.h)
  atof   (stdlib.h)  gcvt   (stdlib.h)  rand    (stdlib.h)
  atol   (stdlib.h)  getenv (stdlib.h)  realloc (stdlib.h)
  atol   (stdlib.h)  itoa   (stdlib.h)  srand   (stdlib.h)
  bsearch(stdlib.h)  labs   (stdlib.h)  stdtod  (stdlib.h)
  calloc (stdlib.h)  lfind  (stdlib.h)  strtol  (stdlib.h)
  ecvt   (stdlib.h)  lsearch(stdlib.h)  swab    (stdlib.h)
  _exit  (stdlib.h)  itoa   (stdlib.h)  system  (stdlib.h)

 Подпрограммы вывода на дисплей текстовых окон   -----------------

      Эти подпрограммы выводят текст на экран.

      clreol     (conio.h)    gotoxy     (conio.h)
      clrscr     (conio.h)    highvideo  (conio.h)
      delline    (conio.h)    insline    (conio.h)
      gettext    (conio.h)    lowvideo   (conio.h)
      gettextinvo(conio.h)    movetext   (conio.h)
      normvideo     (conio.h)    textcolor  (conio.h)
      puttext       (conio.h)    textmode   (conio.h)
      necursortype  (conio.h)    wherex     (conio.h)
      textattr      (conio.h)    wherey     (conio.h)
      textbackground(conio.h)    window     (conio.h)

 Подпрограммы времени и даты   -----------------------------------

      Эти   подпрограммы   предназначены   для   преобразований   и
 манипуляций временем и датой.

      asctime       (time.h)     mktime     (time.h)
      ctime         (time.h)     setdate    (dos.h)
      difftime      (time.h)     settime    (dos.h)
      dostounix     (dos.h)      stime      (time.h)
      ftime         (sys\timeb.h)strftime   (time.h)
      getdate       (dos.h)      time       (time.h)
      gettime       (dos.h)      tzset      (time.h)
      gmtime        (time.h)     unixtodos  (dos.h)
      locoltime     (time.h)

 Подпрограммы для обработки переменного списка аргументов   ------

      Эти   подпрограммы  используются  для  доступа  к  переменным
 спискам аргументов (например, vprintf и т.д.).

      va_arg        (stdarg.h)
      va_end        (stdarg.h)
      va_stsrt      (stdarg.h)




Глава 3            Потоки С++

      Данная глава содержит краткий обзор ввода/вывода потоков С++.
 Ввод/вывод  потоков  в   С++   используется   для   преобразования
 типизированных  объектов  в  читаемый текст, и обратно. Он позволяет
 определять  функции  ввода/вывода,  которые  затем   автоматически
 используются    применительно   к   соответствующим   определенных
 пользователем типам. Последующие  примеры  находятся  в  Главе  5,
 "Основы   С++"   документа  "Начало  работы";  приводимая  там  же
 библиография предлагает несколько названий книг  для  углубленного
 изучения данного примера.

Новые потоки вместо старых

      Turbo  C++ поддерживает как старую библиотеку С++ stream, так
 и новую усовершенствованную библиотеку iostream  С++  версии  2.0.
 Возможность  работы  с  обеими  версиями  поможет  вам, если у вас
 имеются программы, написанные по старым соглашениям, и вы  желаете
 использовать  Turbo  C++  для перехода к более эффективным потокам
 ввода/вывода  версии2.0.  Мы  настоятельно  рекомендуем  вам   при
 создании  новых  программ пользоваться библиотекой iostream версии
 2.0. Приводя  некоторые  материалы,  необходимые  для  перехода  к
 потокам  версии  2.0  (начиная со стр.184 оригинала), данная глава
 главным образом посвящается классам и  правилам  потоков  iostream
 версии 2.0.
                           Использование потоков 2.0

      Усовершенствованные   потоки  iostream  версии  2.0,  хотя  и
 обеспечивают по большей части совместимость для старой версии С++,
 предлагают  тем  не   менее   новые   возможности,   связанные   с
 использованием  множественного  наследования и прочими средствами,
 появившимися в С++ версии 2.0.

      Обсуждение различий между старыми потоками и новыми потоками,
 а также  основы  преобразования  старых  потоков  в  новые  см.  в
 разделах  "Использование  старых  потоков"  и  "Основы  перехода к
 потокам версии 2.0" в конце настоящей главы.

      Концепция потоков С++ нацелена на решение нескольких проблем,
 решаемых  стандартными  библиотечными  функциями  ввода/вывода  С,
 такими   как   printf  и  scanf.  Последние,  разумеется  остаются
 доступными для программиста,  работающего  в  С++,  но  улучшенная
 гибкость  и  элегантность  потоков С++ уменьшают привлекательность
 функций библиотеки stdio.h.  Классы,  связанные  с  потоками  С++,
 предлагают  вам  расширяемые библиотеки, позволяющие вам выполнять
 форматированный   ввод/вывод   с   контролем   типов    как    для
 предопределенных,  так  и  для  определяемых  пользователем  типов
 данных    с    помощью    перегруженных    операций    и    прочих
 объектно-ориентированных методов.

      Для  обращения  к  вводу/выводу потоком ваша программа должна
 включать файл iostream.h. Для некоторых функций потоков  требуются
 и  другие файлы заголовка. Например, для выполнения форматирования
 в  оперативной  памяти  с  использованием  классов  istrstream   и
 ostrstream  необходим файл strstream.h. Файл заголовка strstream.h
 включает также  iostream.h.  Если  вам  требуется  класс  fstream,
 включите   файл   fstream.h,   также   включающий   iostream.h.  И
 разумеется,   можно   включить   одновременно   и   fstream.h,   и
 strstream.h.

Что такое поток?

      Потоком  называется абстрактное понятие, относящееся к любому
 переносу данных от источника (или поставщика данных)  к  приемнику
 (или  потребителю) данных. Также используются синонимы извлечение,
 прием и получение, когда речь идет о вводе символов от  источника,
 и  вставка,  помещение  или  запоминание, когда речь идет о выводе
 символов на приемник.

      Несмотря на свое имя, класс потока может быть использован для
 форматирования данных в ситуациях,  не  подразумевающих  реального
 выполнения  ввода/вывода.  Вы увидите, что форматирование в памяти
 можно применять к символьным массивам и прочим структурам.
                              Библиотека iostream

      Библиотека iostream имеет два параллельных класса:  streambuf
 и ios. Оба они являются классами низкого уровня и каждый выполняет
 свой круг задач.


 streambuf
 _________________________________________________________________

      Класс  streambuf  обеспечивает  общие  правила  буферизации и
 обработки потоков в тех случаях, когда не требуется  значительного
 форматирования  этих потоков. streambuf представляет собой удобный
 базовый класс, используемый другими частями  библиотеки  iostream,
 хотя  он доступен также и для производных классов в ваших функциях
 и библиотеках. Большинство функций-компонентов (правил)  streambuf
 являются  встраиваемыми для обеспечения максимальной эффективности.
 Классы strstreambuf и filebuf являются производными от streambuf.

 ios
 _________________________________________________________________

      Класс ios  (и  следовательно,  производные  от  него  классы)
 содержит указатель на streambuf.

      ios  имеет  два  производных  класса:  istream  (для ввода) и
 ostream (для вывода). Другой класс, iostream, является производным
 классом сразу  от  istream  и  ostream  вследствие  множественного
 наследования:

    class ios;
    class istream : virtual public ios;
    class ostream : virtual public ios;
    classiostream : public istream, public ostream;

      Кроме  того,  существует  три  класса  withassign, являющихся
 производными классами от istream, ostream и iostream:

    class istream_withassign : public istream;
    class ostream_withassign : public ostream;
    class iostream_withassign : public iostream;

 Классы потоков
 _________________________________________________________________

 - Класс ios содержит переменные состояния для интерфейса с
   streambuf и обработки ошибок.

 - Класс istream поддерживает как форматированные, так и неформати-
   рованные преобразования потоков символов, извлекаемых из
   streambuf.

 - Класс ostream поддерживает как форматированные, так и неформати-
   рованные преобразования потоков символов, помещаемых в
   streambuf.

 - Класс iostream объединяет классы istream и ostream для двунап-
   равленных операций, в которых один поток действует и как источ-
   ник, и как приемник.

 - Производные классы withassign обеспечивают четыре предопределен-
   ных "стандартных" потока: cin, cout, cerr и clog, описываемые в
   следующем разделе. Классы withassign добавляют к соответствующим
   базовым классам операции присвоения, следующим образом:

    class istream_withassign : public istream {
       istream_withassign();
       istream& operator=(istream&);
       istream& operator=(streambuf*);
     }

    и аналогично для ostream_withassign и iostream_withassign.

      Классом потока называется любой класс, производный от классов
 istream и ostream.
                           Четыре стандартных потока

      Программы  С++  начинаются   с   четырьмя   предопределенными
 открытыми  потоками, объявленными как объекты классов withassign в
 iostream.h следующим образом:

    extern istream_withassign cin;
    extern ostream_withassign cout;
    extern ostream_withassign cerr;
    extern ostream_withassign clog;

      Их  конструкторы  вызываются   всякий   раз   при   включении
 iostream.h,  но  фактическая инициализация выполняется только один
 раз.

      Четыре стандартных потока выполняют следующее:

    cin    Стандартный ввод (дескриптор файла 0)
           (Соответствует stdin)

    cout   Стандартный вывод (дескриптор файла 1)
           (Соответствует stdout)

    cerr   Стандартный вывод ошибок (дескриптор файла 2). cerr буфе-
           ризуется поблочно; очистка буфера происходит при каждой
           новой вставке
           (Соответствует stderr)

    clog   Данный поток представляет собой полностью буферизуемую
           версию cerr.

      Как и в С, вы можете переназначить эти стандартные  имена  на
 другие файлы или символьные буферы после загрузки программы.
                                     Вывод

      Вывод   потока   осуществляется   оператором   вставки,   или
 помещения,  <<.  Для  операций  вывода перегружается   стандартная
 операция  сдвига  влево  <<.  Ее  левый операнд представляет собой
 объект типа класса ostream. Правый операнд может иметь любой  тип,
 для  которого определен вывод потоком (подробнее об этом говорится
 ниже). Вывод потоком определен для встроенных типов. Операция  <<,
 перегруженная  для  типа  type,  называется  вставкой  этого типа.
 Например,

    cout << "Hello!\n";

 записывает строку "Hello!" в cout (стандартный поток вывода, кото-
 рый обычно направлен на экран), после чего следует новая строка.
 Здесь << - это строка вставки типа char*.

      Операция <<  обладает  ассоциативностью  слева  и  возвращает
 ссылку  на  объект  ostream,  для  которого  она  вызывалась.  Это
 позволяет организовать каскадные вставки:

    void function_display(int i, double d)
    {
       cout << "i=" << i << ", d=" << d << "\n";
    }

      Это вызовет вывод чего-либо вроде:

    i = 8, d = 2.34

 на стандартное устройство вывода.

      Отметим, что перегрузка не  изменяет  нормального  приоритета
 выполнения операции <<, поэтому можно записать:

    cout << "sum = " << x+y << "\n";

 без круглых скобок. Однако, в случае

    cout << (x&y) << "\n";

 круглые скобки нужны.

 Встроенные типы
 _________________________________________________________________

      Типы  вставок,  поддерживаемые  непосредственно,  это  : char
 (signed и unsigned), short (signed  и  unsigned),  int  (signed  и
 unsigned),  long  (signed  и unsigned), char* (рассматриваемый как
 строка), float, double, long double  и  void*.  Интегральные  типы
 преобразовываются по правилам, по умолчанию действующим для printf
 (если  эти  правила  не  изменены путем установки различных флагов
 ios).  Например,  если  заданы  объявления  int  i;  long  l;,  то
 следующие два оператора

    cout << i << " " << l;
    printf("%d %ld, i, l);

 приведут к одному и тому же результату.

      Аналогичным     образом,     типы    с    плавающей    точкой
 преобразовываются правилам умолчания для printf с  преобразованием
 %g. Итак, в случае объявления double d;, операторы

    cout << d;
    printf("%g", d);

 дают один и тот же результат.

      Вставка указателя (void*) также предопределена:

    int i = 1;
    cout << &i; // указатель выводится на дисплей в шестнадцати-
                // ричном формате

      Вставка char работает следующим образом:

    char ch = 'A';
    cout << ch;       // на дисплей выводится А

 Функции put и write
 _________________________________________________________________

      Для  вывода  двоичных  данных  или  отдельного  символа можно
 использовать  функцию-компонент   put,   объявленную   в   ostream
 следующим образом:

    ostream ostream::put (char ch);

      При объявлении int ch='x'; следующие две строки эквивалентны:

    cout.put(ch);
    cout << (char)ch;

      Функция-компонент  write  позволяет  вывод больших по размеру
 объектов:

    ostream& ostream::write(const signed char* ptr, int n);
    ostream& ostream::write(const unsigned char* ptr, int n);

      Функции write выводят  n  символов  (включая  любые  входящие
 пустые  символы)  в  двоичном  формате.  В  отличие  от  строковой
 вставки, write  не  прекращает  работу,  встретив  пустой  символ.
 Например,

    cout.write((char *)&x, sizeof(x))

      пошлет   непреобразованное  представление  х  на  стандартное
 устройство вывода.

      Существует тонкое различие между форматированной операцией <<
 и  неформатированными  функциями  put  и  write.   Форматированная
 операция  может вызвать очистку связанных потоков и иметь атрибут
 ширины  поля.  Неформатированные  операции   не   обладают   этими
 свойствами.  Поэтому  cout  <<  'a'  и cout put ('a') могут давать
 разные результаты. Все флаги форматирования применимы к <<, но  ни
 один из них не применим к put или write.

 Форматирование вывода
 _________________________________________________________________

      Форматирование ввода и вывода определяется различными флагами
 состояний  формата,  перечисленными  в  классе  ios. Эти состояния
 определяются битами числа типа long int следующим образом:

    public:
       enum {
          skipws   = 0x0001, // пропуск пробельного символа на вводе
          left     = 0x0002, // вывод с левым выравниванием
          right    = 0x0004, // вывод с правым выравниванием

          internal = 0x0008, // заполнитель после знака или
                             // указателя системы счисления
          dec      = 0x0010, // десятичное преобразование
          oct      = 0x0020, // восьмеричное преобразование
          hex      = 0x0040, // шестнадцатиричное преобразование
          showbase = 0x0080, // показать на выходе указатель
                             // системы счисления
         showpoint = 0x0100, // показать позицию десятичной точки
                             // (на выходе)
         uppercase = 0x0200, // вывод шестнадцатиричных значений
                             // буквами верхнего регистра
          showpos  = 0x0400, // показать знак "+" для положительных
                             // чисел
        scientific = 0x0800, // использовать запись чисел с плава-
                             // ющей точкой с выводом экспоненты Е
                             // например, 12345E2
          fixed    = 0x1000, // использовать запись чисел с плава-
                             // ющей точкой типа 123.45
          unitbuf  = 0x2000, // стирание всех потоков после вставки
          stdio = 0x4000,    // стирание stdout и stderr после
                             // вставки
    };

      Разумеется,  эти  флаги  наследуются  производными   классами
 ostream   и   istream.   При   отсутствии   специальных   действий
 пользователя   флаги   устанавливаются   таким   образом,    чтобы
 выполнялось   показанное   в   примерах   выше  форматирование  по
 умолчанию. Существуют функции, позволяющие установке,  проверку  и
 сброс  флагов  формата,  как  по  отдельности,  так  и  связанными
 группами. Некоторые флаги автоматически  очищаются  после  каждого
 ввода/вывода.

 Основание системы счисления при преобразованиях
 _________________________________________________________________

      По   умолчанию   вставка  интегральных  типов  выполняется  в
 десятичной записи. Это можно изменить соответствующими установками
 флагов ios::dec, ios::oct и ios::hex (см. раздел  "манипуляторы").
 Если   все  эти  флаги  равны  нулю  (по  умолчанию),  то  вставка
 выполняется в десятичном формате.

 Ширина
 _________________________________________________________________

      По умолчанию  вставки  выводят  минимальное  число  символов,
 которыми  может  быть  представлен операнд правой части. Для того,
 чтобы изменить это умолчание, можно использовать  удобные  функции
 задания ширины вывода:

    int ios::width(int w);  // устанавливает поле ширины в w
                            // и возвращает предыдущую ширину
    int ios::width();       // возвращает текущую ширину --
                            // не внося никаких изменений

      Значение  width  по  умолчанию  равно  нулю, что эквивалентно
 выводу без дополнительных заполнителей. Ненулевая ширина означает,
 что вставки будут выполнять вывод не уже заданной  ширины,  и  при
 необходимости  будут  использованы  заполнители  до  этой  ширины.
 Отметим,  что  усечение  ширины  при  этом  не  выполняется:  если
 указанная   ширина   меньше   фактического   числа   символов  для
 представления вывода, то она будет проигнорирована (как при width,
 установленной равной нулю). Например,

    int i = 123;
    int old_w = cout.width(6);
    cout << i;                // на выводе будет bbb123, где bbb =
                              // пробелы. Затем ширина устанавлива-
                              // ется равной 0
    cout.width(old_w);        // восстановление предыдущей ширины
                              // поля

      Отметим, что  после  каждой  форматированной  вставки  ширина
 очищается в ноль, так что в

    int i, j;
    ...
    cout.width(4);
    cout << i << " " << j;

 i будет представлена минимум четырьмя символами, однако пробел в
 середине выражения и j будут представлены минимально необходимым
 числом символов.

 Манипуляторы
 _________________________________________________________________

      Более  простой  способ  изменения  состояния  ширины и прочих
 переменных  форматирования  состоит  в  использовании  специальной
 функциообразной  операции, называемой манипулятором. Манипуляторы
 принимают в качестве аргументов ссылку на поток и возвращают ссылку
 на тот же  поток  -  поэтому  манипуляторы  могут  объединяться  в
 цепочку  вставок  (или  извлечений  из  потока)  для  того,  чтобы
 изменять  состояния  потока  в   виде   побочного   эффекта,   без
 фактического   выполнения  каких-либо  вставок  (или  извлечений).
 Например,

    cout << setw(4) << i << setw(6) << j;

 эквивалентно более широкой записи

    cout.width(4);
    cout << i;
    cout.width(6);
    cout << j;

      setw  представляет   собой   параметризованный   манипулятор,
 объявление     которого     находится    в    iomanip.h.    Прочие
 параметризованные манипуляторы,  setbase,  setfill,  setprecision,
 setiosflags  и  resetiosflags,  работают  аналогичным  образом (см
 таблицу 3.1). Для того, чтобы использовать эти манипуляторы,  ваша
 программа   должна  включать  iomanip.h.  Вы  можете  писать  свои
 собственные манипуляторы, без параметров:

    ostream& dingy( ostream os)
    {
       return os << "\a\a";
    }
    ...
    cout << i << dingy << j;

      Манипуляторы с параметрами более сложны и  требуют  включения
 iomanip.h.

 Манипуляторы                                          Таблица 3.1
 -----------------------------------------------------------------
 Манипулятор         Синтаксис              Действие
 -----------------------------------------------------------------
 dec                 outs<>dec               тирования с десятичными
                                            преобразованиями

 hex                 outs<>hex               тирования с шестнадцати-
                                            ричными преобразованиями

 oct                 outs<>oct               тирования с восьмеричными
                                            преобразованиями

 ws                  ins>>ws                Извлечение пробельных
                                            символов

 endl                outs<>resetiosflags(l)  Очистка форматных битов
                     outs<>setiosflags(l)    Установка форматных битов
                     outs<>setfill(n)        Установка символа-запол-
                     outs<>setprecision(n)   Установка точности пред-
                     outs<>setw(n)           Установка ширины поля
                     outs<>, и называется операцией извлечения, или
 извлечением. Операция >> обеспечивает более компактную и  читаемую
 альтернативу  семейству  функций  scanf  в  stdio (она также лучше
 защищена от ошибок). Левый операнд >>  представляет  собой  объект
 типа  класса  istream. Как и для вывода, правый операнд может быть
 любого типа, для которого определен вывод потоком.

      Все встроенные типы, перечисленные  выше  для  вывода,  также
 имеют предопределенные операции извлечения. Операция >> может быть
 также  перегружена  для  ввода  потоком  ваших  собственных  типов
 данных. Операция  >>,  перегруженная  для  типа  type,  называется
 извлечением type. Например,

    cin >> x;

 вводит значение из cin (стандартный поток ввода, обычно направляе-
 мый с клавиатуры) в x. Функции преобразования и форматирования за-
 висят от типа x, от того, каким образом определено извлечение, а
 также от установок флагов состояния формата.

      По  умолчанию  >> опускает пробельные символы (как определено
 функцией  isspace  в  ctype.h),   а   затем   считывает   символы,
 соответствующие  типу  объекта  ввода. Пропуск пробельных символов
 управляется флагом ios::skipws в перечислимой переменной состояний
 формата (см. "Форматирование вывода" на стр.170  оригинала).  Флаг
 skipws  обычно  устанавливает пропуск пробельных символов. Очистка
 этого  флага  (например,  при  помощи  setf)   выключает   пропуск
 пробельных   символов.   Отметим   также  специальный  манипулятор
 "приемника", ws, который позволяет игнорировать пробельные символы
 (см. таблицу 3.1).

 Изменение извлечений
 _________________________________________________________________

      Как  и  в  случае  <<,   операция   >>   обладает   свойством
 ассоциативности  слева  и  возвращает левый операнд. Левый операнд
 является ссылкой на объект  istream,  для  которого  была  вызвана
 данная  операция.  Это  позволяет  объединять  в  одном  операторе
 несколько операций ввода. Рассмотрим следующий пример:

    int i;
    double d;
    cin >> i >> d;

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

 Извлечения для встроенных типов
 _________________________________________________________________

      Извлечения для встроенных типов  делятся  на  три  категории:
 интегральные,  с  плавающей  точкой  и  строковые.  Каждое  из них
 описано ниже, в следующих разделах.  Для  всех  числовых  типов  в
 случае,  если  первый  не-пробельный символ не является цифрой или
 знаком (или  десятичной  точкой  для  преобразований  с  плавающей
 точкой),  поток  вводит  состояние  ошибки (это описано на стр.177
 оригинала) и вплоть до сброса состояния  ошибки  любой  дальнейший
 ввод запрещен.

                    Интегральные извлечения

      Для   типов   short,   int   и   long   (signed  и  unsigned)
 действие операции  >>   по   умолчанию   заключается   в   пропуске
 не-пробельных  символов  и  преобразовании  интегрального значения
 путем чтения символов ввода до тех пор, пока не встретится символ,
 который не может являться допустимой частью представления  данного
 типа. Формат распознаваемых интегральных значений тот же, что и для
 целочисленных   констант   С++,   за   исключением   целочисленных
 суффиксов. (См. стр.11 оригинала).

 Предупреждение

      Если вы задали преобразования  типа  hex,  dec  или  oct,  то
 именно  такие  результаты  вы  и  получите.  0x10  становится  0 в
 десятичном или восьмеричном представлении;  010  становится  10  в
 десятичном представлении и 16 в шестнадцатиричном.


                   Извлечения с плавающей точкой

      Для  типов  float  и  double  действие  операции >> состоит в
 пропуске    пробельных  символов  и  преобразовании   значения   с
 плавающей  точкой  путем чтения вводимых символов до тех пор, пока
 не  встретится  символ,   который   не   может   являться   частью
 представления  числа  с  плавающей  точкой.  Формат распознаваемых
 значений с плавающей точкой тот же, что и для констант с плавающей
 точкой С++, за исключением суффиксов. (См. стр.16 оригинала).


                       Символьные извлечения

      Для типа char (signed  или  unsigned)  действие  операции  >>
 состоит   в  пропуске  пробельных  символов  и  записи  следующего
 (не-пробельного) символа. Если вам  требуется  прочесть  следующий
 символ,  неважно,  является  ли  он  пробельным  или нет, то можно
 использовать одну из функций-компонентов get:

    char ch;
    cin.get(ch);    // ch устанавливается на следующий символ потока
                    // даже если это пробельный символ

      Функции get для ввода играют ту же роль, что функции put  для
 вывода.   Следующий   вариант   get   позволяет  управлять  числом
 извлекаемых символов, их размещением и оконечным символом:

    istream& istream::get(char *buf, int max, int term='\n');

      Эта функция считывает символы из входного потока в символьный
 массив buf до тех пор, пока не будет считано max-1 символов,  либо
 пока  не  встретится символ, заданный term, в зависимости от того,
 что  произойдет  раньше.  Завершающий  пустой  символ  добавляется
 автоматически.  По  умолчанию  терминатором  (который не требуется
 задавать) является символ новой строки ('\n').  Сам  терминатор  в
 массив  buf  не  считывается и из istream не удаляется. Массив buf
 должен иметь размер как минимум max символов.

      По аналогии с функцией-компонентом ostream write (см. стр.170
 оригинала)  можно  прочитать  "сырые"  двоичные  данные  следующим
 образом:

    cin.read ( (char*)&x, sizeof(x) );

      Для   типа   char*  (рассматриваемого  как  строка)  действие
 операции >>  состоит  в  пропуске  пробельных  символов  и  записи
 следующих  (не-пробельных) символов до тех пор, пока не встретится
 следующий пробельный символ. Затем добавляется завершающий нулевой
 (0)  символ.   Следует   предъявлять   осторожность   и   избегать
 "переполнения" строки. Ширина по умолчанию, равная нулю (означает,
 что предельное значение не задано), может быть изменена при помощи
 setw следующим образом:

    char array[SIZE];
    ...
    // инициализация массива
    ...
    cin.width(sizrof(array));
    cin >> array              // позволяет избежать переполнения

      В  случае  любого  ввода  встроенных  типов, если конец ввода
 встретится ранее первого  не-пробельного  символа,  в  мишень  buf
 ничего  записано  не  будет, а состояние istream будет установлено
 равным   "отказу".   Таким   образом,   если   мишень   была    не
 инициализирована, то она и останется не инициализированной.

 Функция возвращения
 _________________________________________________________________

      Функция-компонент

    istream istream::putback(char c);

 возвратит обратно в istream один символ c; если этот символ не мо-
 жет быть помещен обратно, то устанавливается состояние потока "от-
 каз". Следующая простая подпрограмма выполняет считывание иденти-
 фикатора С++ со стандартного устройства ввода:

    void getident (char *s  /* сюда помещается идентификатор */ )
    {
       char c = 0;         // защита от конца файла
       cin >> c;           // пропуск пробельных символов
       if (isalpha(c) || c == '_')
          do {
             *s++ = c;
             c = 0;        // защита от конца файла
             cin.get(c);
           } while (isalnum(c) || c =='_');
       *s = 0;             // терминатор строки
       if (c)
          cin.putback(c);  // один символ всегда лишний
       }

 Ввод типов, определяемых пользователем
 _________________________________________________________________

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

    istream& operator >> (istream& s, info& m);
    {
       s >> m.name >> m.val >> m.units;
       return s;
    }

      (В реальных  прикладных  программах,  разумеется,  вы  можете
 добавить  коды  для  проверки ошибок ввода). Для считывания строки
 ввода,  такой  как  "capacity  1.25  liters",  можно  использовать
 следующую запись:

    cin >> m;
                             Инициализация потоков

      Потоки  cin, cout, cerr и clog инициализируются и открываются
 при загрузке программы  и  затем  подключаются  к  соответствующим
 стандартным   файлам.   Инициализация   (конструирование)   потока
 означает ассоциирование его с буфером потока. Класс ostream  имеет
 следующий конструктор:

    ostream::ostream(streambuf*);

 который инициализирует переменные состояния ios и ассоциирует бу-
 фер потока с объектом ostream. Конструктор istream работает анало-
 гичным образом. В большинстве случаев вам не требуется специально
 рассматривать вопросами буферизации.

      Библиотека iostream предлагает множество классов, производных
 от  streambuf,  ostream  и istream, что дает широкий выбор методов
 создания потоков с различными источниками и приемниками,  а  также
 различными методами буферизации.

      Следующие классы являются производными от класса streambuf:

 filebuf        filebuf поддерживает ввод/вывод через дескрипторы
                файлов. Функции-компонент класса поддерживают
                функции открытия, закрытия файлов и поиска.

 stdiobuf       stdiobuf поддерживает ввод/вывод через структуры
                stdio FILE и предназначается исключительно для
                совместимости кодов С++ при их комбинировании с
                существующими программами С.

 strstreambuf   strstreambuf позволяет ввод и вывод символов из
                байтовых массивов в памяти. Два дополнительных
                класса, istrstream и ostrstream, обеспечивают ввод/
                вывод с форматированием в памяти.

      Специализированные классы для ввода/вывода в файл являются
 производными:

    ifstream  является производным от istream
    ofstream  является производным от ostream
     fstream  является производным от iostream

      Эти  три  класса  поддерживают  форматированный  ввод/вывод в
 файлы при помощи буферов файлов (filebuf).
                           Простой ввод/вывод в файл

      Класс ofstream  наследует  операции  вставки  от  ostream,  а
 ifstream  наследует  операции  извлечения  от  istream.  Они также
 обеспечивают конструкторы и функции-компоненты для создании файлов
 и обработки ввода/вывода в этот файл. Следует  включать  fstream.h
 во все программы, где используются эти файлы. Рассмотрим следующий
 пример, в котором файл FILE_FROM копируется в FILE_TO:

    #include fstream.h
    ...
    char ch;
    ifstream f1("file_from");
    if (!f1) errmsg("Cannot open 'filr_from' for input");
    ofstream f2("file_to");
    if (!f2) errmsg("Cannot open 'filr_to' for output");
    while ( f2 && f1.get(ch) ) f2.put(ch);

      Ошибки, связанные с потоками, подробно обсуждаются на стр.181
 оригинала.

      Отметим, что если конструкторы ifstream или ofstream не могут
 открыть   указанные   файлы,  то  устанавливается  соответствующее
 состояние ошибки потока.

      Конструкторы  позволяют  объявить  поток  файла  без  задания
 именованного  файла.  Затем  вы  можете ассоциировать данный поток
 файла с конкретным файлом:

    ofstream ofile;          // создание выходного потока файла
    ...
    ofile.open("payroll");   // поток ofile ассоциируется с
                             // файлом payroll
    // работа с некоторым паролем
    ofile.close();           // payroll закрывается
    ofile.open("employee");  // поток ofile можно использовать
                             // повторно

      По  умолчанию  файлы  открываются  в  текстовом  режиме.  Это
 означает,     что    на    вводе    последовательность    возврата
 каретки/перевода строки преобразуется в  символ  '\n'.  На  выводе
 символ    '\n'    преобразуется   в   последовательность   возврат
 каретки/перевод строки. В двоичном режиме такие преобразования  не
 выполняются.

      Функция   компонента   ofstream::open  объявляется  следующим
 образом:

    void open(char * name, int=ios::out, int prot=filуbuf::openprot);

      Аналогично, объявление ifstream::open имеет вид:

    void open(char * name, int=ios::in, int prot=filуbuf::openprot);

      Второй   аргумент,   называемый   режимом   открытия,   имеет
 показанные   умолчания.   Аргумент   режима   открытия  (возможно,
 связанный операцией ИЛИ с несколькими битами  режима)  можно  явно
 задать в следующей форме:

 -----------------------------------------------------------------
 Бит режима     Действие
 -----------------------------------------------------------------
 ios::app       Добавление данных - запись всегда в конец файла
 ios::ate       Поиск конца файла после первоначального открытия
 ios::in        Открытие на ввод (подразумевается для ifstream)
 ios::out       Открытие на вывод (подразумевается для ofstream)
 ios::trunc     Уничтожение содержимого в случае, если файл суще-
                ствует (подразумевается, если ios::out задано, и
                ни ios::ate, ни ios::app не заданы)
 ios::nocreate  Если файл не существует, то open дает ошибку
 ios::noreplace Если файл существует, open для файлов вывода дает
                ошибку, если не установлены ate или app
 -----------------------------------------------------------------

      Мнемоника  режима берется из перечислимого значения open_mode
 в ios:

    class ios {
    public:
       enum open_mode { in, out, app, ate, nocreate, noreplace };
    };

      Оператор

    ofstream ofile("data",ios::app|ios::nocreate);

 попытается открыть файл DATA на вывод в режиме append; если файл
 не существует, это приведет к неудаче. Информация об этой неудаче
 будет обозначена состоянием ошибки ofile. В случае удачного завер-
 шения поток ofile будет добавлен к файлу DATA. Класс fstream (про-
 изводный от двух классов ifstream и ofsrtream) может использовать-
 ся для создания файлов, одновременно позволяющих и ввод, и вывод:

    fstream inout("data:,ios::in|ios::out);
    inout << i;
    ...
    inout >> j;

      Для определения текущей позиции  "get"  или  текущей  позиции
 "put"  файла  можно  воспользоваться  функциями tellg и tellp; они
 определяют положение  в  потоке,  где  будет  выполнена  следующая
 операция вывода или ввода:

    streampos cgp = inout.tellg(); // cgp - это текущая позиция get

 где  streampos  это  typedef  в fstream.h. Функции-компоненты
 seekg и seekp могут сбрасывать значения текущей позиции get и put:

    inout.seekg(cp);   // установка в cp текущей позиции "put"

      Варианты seekp и seekg позволяют получить искомые  позиции  в
 относительных смещениях:

    inout.seekg(5,ios::beg);  // перемещение cp на 5 байт от начала
    inout.seekg(5,ios::cur);  // перемещение cp на 5 байт вперед
    inout.seekp(5,ios::end);  // перемещение cp на 5 байт до конца

      Вам может понадобиться распечатать и изучить комментированные
 файлы  заголовка, чтобы узнать, как взаимосвязаны различные классы
 потоков и как объявляются их функции компоненты.
                     Состояния ошибки потока ввода/вывода

      Каждый поток имеет связанное с  ним  состояние  ошибки,  т.е.
 набор битов ошибок, объявленный как перечислимое значение io_state
 в классе ios:

    class ios {
    public:
    ...
       // биты состояния потока
       enum io_state {
          goodbit   = 0x00,
          eofbit    = 0x01,
          failbit   = 0x02,
          badbit    = 0x04,
          hardfail  = 0x10
       };
    ...
    };

      Отметим,  что goodbit в действительности не является витом, а
 представляет  собой  нулевое  значение,  указывающее  на  то,  что
 никакие биты ошибки не устанавливались.

      Ошибки  ввода/вывода  потоком  устанавливает  соответствующий
 бит(ы), как указано в табл.3.2.

 Биты ошибок ios                                       Таблица 3.2
 -----------------------------------------------------------------
 Бит состояния   Его смысл
 -----------------------------------------------------------------
 goodbit         Если этот бит не установлен, то все в порядке.

 eofbit          "Конец файла": устанавливается, если istream не
                 имеет больше битов для извлечения. Последующие
                 попытки выполнить извлечение игнорируются.

 failbit         Устанавливается, если последняя операции ввода/
                 вывода (извлечение или преобразование) окончилась
                 неудачей. После сброса данного бита ошибки поток
                 готов к последующему использованию.

 badbit          Устанавливается, если последняя попытка ввода/
                 вывода являлась недопустимой. Поток может быть ис-
                 пользован (не всегда) после сброса условия ошибки.

 hardfail        Устанавливается, если для данного потока встрети-
                 лось невосстановимое состояние ошибки.
 -----------------------------------------------------------------

      После того, как поток получил состояние ошибки,  все  попытки
 вставки  или  извлечения из данного потока будут игнорироваться до
 тех пор, пока не будет  исправлено  условие,  вызвавшее  состояние
 ошибки,  а  бит(ы) ошибки очищен(ы) (при помощи, например, функции
 компонента    ios::clear(i).    Функция-компонент    ios::clear(i)
 фактически    устанавливает   биты   ошибки   в   соответствии   с
 целочисленным аргументом i, так что ios::clear(0) очищает все биты
 ошибки, за исключением hardfail, который таким образом очищен быть
 не может.

      Отметим, что операции вставки и извлечения не  могут  изменить
 состояния  потока  после  того,  как  произошла  ошибка.  Из этого
 следует, что хорошей практикой является проверка состояния  ошибки
 потока в соответствующих точках программы. В таблице 3.3 приведены
 функции-компоненты,  позволяющие  выполнять  проверку  и установку
 битов ошибки.

 Функции-компоненты для обработки
 текущего состояния потока                              Таблица 3.3
 ------------------------------------------------------------------
 Функция компонент          Действие
 ------------------------------------------------------------------
 int rdstate();        Возвращает текущее состояние ошибки

 void clear(int i=0);  Устанавливает биты ошибки в i. Например, код

                          str.clear(ios::failbit|str.rdstate());

                       устанавливает failbit потока str без разру-
                       шения прочих битов

 int good();           Возвращает не-нулевое значение, если биты
                       ошибки не устанавливались; в противном случае
                       возвращает ноль

 int eof();            Возвращает не-нулевое значение, если
                       установлен бит eofbit istream; в противном
                       случае возвращает ноль.

 int fail();           Возвращает не-нулевое значение, если был
                       установлен один из битов failbit, badbit или
                       hardfail; в противном случае возвращает ноль.

 int bad();            Возвращает не-нулевое значение, если был
                       установлен один из битов badbit или
                       hardfail; в противном случае возвращает ноль.
 ------------------------------------------------------------------

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

    if (cin >> x) return;   // ввод в порядке
    ...                     // здесь восстановление в случае ошибки
    if (!cout) errmsg("Ошибка вывода!");

      Эти  примеры  подчеркивают  элегантность С++. Класс ios имеет
 следующие объявления функции operator:

    int operator! ();
    operator void* ();

      Операция  void*()  определена  как  "преобразующая"  поток  в
 указатель, который будет равен 0 (ложь), если установлены failbit,
 badbit  или  hardfail,  и не-нулевому значению в противном случае.
 (Отметим, что возвращаемый указатель должен использоваться  только
 в  логических  проверках;  другого  практического применения он не
 имеет).   Перегруженная   операция   "не"   (!)   определена   как
 возвращающая  не-нулевое  значение (истина), если установлены биты
 ошибки потока failbit, badbit или hardfail; в противном случае она
 возвращает ноль (ложь).

Использование потоков прошлых версий

      Хотя библиотеки stream  версий  1.x  и  iostream  версии  2.0
 разделяют  многие  имена  классов  и  функций  и предлагают многие
 аналогичные средства, их структуры  в  некоторых  важных  областях
 несколько   отличны  друг  от  друга.  Turbo  C++,  следовательно,
 реализует два потока с разными библиотеками и  файлами  заголовка.
 Для  работы  целиком  со  старыми кодами, использующими потоки, вы
 должны включить файл stream.h,  избегать  включения  iostream.h  и
 выполнять  компоновку со старой библиотекой stream. Дополнительная
 информация о потоках версии 1.х находится в файле  OLDSTR.DOC.  Мы
 также  рекомендуем вам ознакомиться с объявлениями и комментариями
 в stream.h.

      В зависимости  от  классов  и  средств,  используемых  вашими
 старыми  работающими  с  потоками  программами,  не  исключена  их
 успешная компиляция и выполнение с использованием новой библиотеки
 iostream.
                 Рекомендации по переходу к потокам версии 2.0

      Ключевое различие между старыми  и  новыми  классами  потоков
 состоит  в  том,  что  компоненты  public старого класса streambuf
 теперь, в новом классе streambuf, объявлены  как  protected.  Если
 ваш  старый  код  с  потоками выполняет к таким компонентам прямые
 ссылки, либо если у вас имеются производные от  streambuf  классы,
 определенные   на   основании  таких  компонентов,  то  вы  должны
 пересмотреть такие программы, прежде чем они пойдут с  библиотекой
 iostream.  Другой  аспект,  способный  повлиять  на совместимость,
 состоит  в   том,   что   старый   streambuf   прямо   поддерживал
 использование символьных массивов для форматирования в оперативной
 памяти.   В   случае   iostream  эта  поддержка  предполагается  в
 производном классе strstreambuf, объявляемом в strstream.h.

      Старые  конструкторы  потока,  запускающие   буферы   файлов,
 например

    istream instream(дескриптор_файла)4

 должны быть заменены на

    ifstream instream(дескриптор_файла);

 в программах с использованием iostream.

      Старые  и  новые  классы потоков по-разному взаимодействуют с
 stdio. Например, stream.h включает stdio.h,  а  старые  istream  и
 ostream  поддерживают  указатели на структуру stdio FILE. В случае
 iostream  stdio  поддерживается  через  специализированный   класс
 stdiostream, объявленный в stdiostream.h.

      В  старой библиотеке stream предопределенные потоки cin, cout
 и cerr связаны непосредственно с файлами структуры FILE  в  stdio:
 stdin,  stdout  и  stderr.  В  случае  iostream они подключаются к
 дескрипторам файлов и используют различные стратегии  буферизации.
 Для  того,  чтобы  избежать  проблем  с буферизацией при смешанном
 использовании кодов с stdout и cout, можно записать:

    ios::sync_with_stdio();

 где выполняется подключение предопределенных потоков к файлам
 stdio в режиме без буферизации. Отметим, однако, что такой способ
 значительно замедляет работу cin, cout и cerr, соответственно.

      Старая   библиотека    stream    позволяла    непосредственно
 присваивать один поток другому; например,

    ostream outs; outs = cout;    // только для старых потоков

      В  случае iostream допустимо присвоение только потоку в левой
 части    выражения    присвоения;    другими     словами,     типа
 istream_withassign  или  ostream_withassign.  Если  ваша программа
 содержит  такие  присвоения,  то  их  можно  либо   переписать   с
 использованием ссылок или указателей, либо изменить объявления:

    ostream_withassign outs = cout;    // только для новых потоков
    outs << i;                         // iostream
Next
Используются технологии uCoz