Copyright: Copyright (C) Prof. Polyshtshuk A.P


Оглавление
 УЧЕБНОЕ И СПРАВОЧНОЕ ПОСОБИЕ ПО ИНФОРМАТИКЕ

Назначение справочника по информатике

Введение для школьного учителя информатики

Основные понятия и определения

Устройство персонального компьютер

Операционная система MS DOS

Оболочки ОС, утилиты и прикладные пакеты

Алгоритмизация и процедурное программирование

Управление ресурсами компьютера из прикладных программ

Лабораторный практикум по управлению ресурсами

Лабораторный практикум по типовым прикладным задачам

Лабораторный практикум по задачам школьного курса

Объектно - ориентированное программирование в среде Borland C++

Лабораторный практикум по объектно-ориентированному программированию

Численные методы в объектной методологии

Программирование Windows - приложений

Основы научных исследований -краткий курс лекций

Программирование баз данных в среде FoxPro 2.0


Введение для школьного учителя информатики

Программирование- общеобразовательная дисциплина

Когда начинать изучение информатики в щколе

Последовательность обучения программированию

О выборе алгоритмического языка в учебном процессе


Основные понятия и определения

Информатика  и информация

Вероятность, энтропия, количество информации

Сообщения и сигналы

Системы счисления

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

Оперативная память электронных компьютеров

Шестнадцатиричное представление двоичных чисел

Адреса и кодирование адресов

Числовое кодирование нечисловых данных

Числовое кодирование текстовой (символьной) информации

Основная таблица ASCII - кодов символов

Дополнительная таблица ASCII - кодов символов

Кодирование изображений символов

Кодирование информации в процессах обмена

Основные типы приемо-передающих устройств

Логические связи между кодовыми группами


Устройство персонального компьютера

Клавиатура персонального компьютера

Процессы обмена данными между устройствами ПК

Архитектура центрального процессора

О кодировании команд для процессора

Справочник по системе команд процессора 8086

Внешние запоминающие устройства ПК


Операционная система MS DOS

Общие сведения

Обслуживание файлов на уровне пользователя в MS DOS

Системные файлы в MS DOS

Каталогизация файлов в MS DOS

Структура дисковой памяти в MS DOS

Загрузка MS DOS

Команды MS DOS

Справочник внутренних команд MS DOS

Справочник внешних команд MS DOS

Командные файлы в MS DOS

Выполняемые файлы в MS DOS

Оперативная среда программ в MS DOS

Организация оперативной памяти в MS DOS

Префикс сегмента программы в  MS DOS

Справочник по драйверу ANSI.SYS

Системный отладчик DEBUG в MS DOS

Редактор связей LINK для MS DOS

Обработчики прерываний в  MS DOS

Распределение памяти в MS DOS


Внутренние команды MS DOS

BREAK

BUFFERS

CD

CLS

COPY

CTTY

DATE

DEL

DEVICE

DIR

ECHO

ERASE

FILES

FOR

GOTO

IF

LASTDRIVE

MD

PATH

PAUSE

PROMPT

REM

RENAME

RD

SET

SHELL

SHIFT

STACKS

TIME

TYPE

VER

VERIFY

VOL


Внешние команды MS DOS

APPEND

ATTRIB

BACKUP

CHKDSK

COMMAND

COMP

DISKCOMP

DISKCOPY

EXE2BIN

FASTOPEN

FDISK

FIND

FORMAT

GRAPHICS

JOIN

KEYB

LABEL

MODE

MORE

PRINT

RECOVER

REPLACE

RESTORE

SORT

SUBST

SYS

TREE

XCOPY


Оболочки, утилиты и прикладные прграммы
Оболочки ОС, утилиты и прикладные пакеты

Общие сведения

Оболочки

Текстовые редакторы

Электронные таблицы

Интегрированные пакеты


Оболочки

Файловый монитор Norton Commander

Windows-3.1 для непрограммирующего пользователя


Текстовые редакторы

Word-2.0 for Windows

ChiWriter


Электронные таблицы

SuperCalc-4

Excel


Интегрированные пакеты


Алгоритмизация и программирование

Алгоритм и исполнитель

Машина Тьюринга

Автоматы Неймана

Словесные описания алгоритмов на естественном языке

Управляющие структуры в алгоритмах

Блок-схемы алгоритмов

Алгоритмические языки и трансляторы

Типы данных в алгоритмических языках

Простые константы

Операции над простыми типами

Сложные типы - массивы

Сложные типы - структуры (записи)

Множества в Турбо Паскале

Работа с битовыми полями

Управляющие структуры в алгоритмических языках

Вспомогательные алгоритмы и подпрограммы

Библиотеки подпрограмм


Типы данных в алгоритмических языках

Ассемблер

Си

Паскаль

Бейсик


Простые константы

Ассемблер

Си

Паскаль

Бейсик


Массивы в алгоритмических языках
Сложные типы - массивы

Ассемблер

Си

Паскаль

Бейсик


Структуры в алгоритмических языках
Сложные типы - структуры

Турбо Ассемблер

Си

Паскаль


Битовые поля в алгоритмических языках
Битовые поля

Ассемблер

Си


Управляющие структуры в алгоритмических языках

Ассемблер

Си

Паскаль

Бейсик


Подпрограммы в алгоритмических языках

Ассемблер

Си

Паскаль

Бейсик


Библиотеки подпрограмм в алгоритмических языках
Библиотеки подпрограмм

Си

Паскаль


Управление ресурсами компьютера из прикладных программ

Работа с регистрами процессора и системные вызовы

Обработка хвоста командной строки

Работа с файлами

Динамическое управление памятью

Управление субпроцесами

Управление вводом с клавиатуры

Управление видеосистемой


Системные вызовы через регистры
Работа с регистрами процессора и системные вызовы

Ассемблер

Си

Паскаль


Обработка хвоста командной строки

Ассемблер

Си

Паскаль


Работа с файлами

Ассемблер

Си

Паскаль

Бейсик


Динамическое управление памятью из программ
Динамическое управление памятью

Ассемблер

Си

Паскаль


Управление субпроцессами из программ
Управление субпроцессами

Ассемблер

Си

Паскаль

Бейсик


Ввод с клавиатуры в прикладных программах
Управление вводом с клавиатуры

Ассемблер

Си

Паскаль

Бейсик


Управление видеосистемой

Архитектура видеоадаптеров

Ассемблер

Си

Паскаль


Лабораторный практикум по процедурному программированию
Лабораторный практикум по управлению ресурсами

Задачи по обмену информацией с файлами

Задачи по динамическим структурам данных

Задачи по управлению субпроцесами

Задачи по управлению вводом с клавиатуры

Задачи по управлению видеосистемой


Задачи файлового обмена
Задачи по файловому обмену

Открытие и закрытие файлов

Посимвольный ввод-вывод

Построчный ввод-вывод

Блоковый ввод-вывод

Форматированный ввод-вывод

Произвольный доступ к файлам

Префиксный доступ к файлам

Программирование фильтров

Пример работы с файловыми компонентами

Работа с файловыми записями

Работа с файлами в антивирусной программе


Задачи по динамическим структурам данных

Стеки

Очереди

Списки

Двоичные деревья


Задачи по управлению субпроцессами
Задачи по управлению субпроцесcами

Вызовы субпроцессов через оболочку

Вызов субпроцессов с возвратом в предок

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


Задачи по работе с клавиатурой
Задачи по управлению вводом с клавиатуры

Очистка буфера последовательным чтением

Ожидание нажатия клавиш

Прием символов

Запись в буфер клавиатуры (Паскаль)

Определение нажатий ASCII и спецклавиш

Ввод паролей

Запись 2 - байтового кода клавиш в буфер (Си)

Очистка буфера приравниванием указателей головы и хвоста

Ввод с предварительной очисткой буфера

Чтение из буфера через указатель головы

Запись в буфер (Си)


Задачи по управлению видеосистемой

Вывод изображений управляющих символов

Использование 4-х управляющих кодов экрана

Работа с окнами

Вставка и удаление строк

Синтез звуков экранным выводом

Программный опрос режимов дисплея

Прямой доступ к видеопамяти

Работа с таблицей знакогенератора

Управление формой курсора (Паскаль)

Определение и настройка параметров видеоадаптера

Переключение видеостраниц

Управление курсором (Си)

Вертикальный вывод строк на активную страницу

Вывод повторяющихся символов и рамок

Скроллинг и очистка экрана

Вывод текста в видеобуфер

Вывод в окна (Си)

Чтение с экрана

Вывод графических изображений


Лабораторный практикум по типовым прикладным задачам

Алгоритмы сортировки

Алгоритмы поиска

Вычисление корней многочленов

Решение СЛАУ методом Гаусса

Решение СЛАУ методом ортогонализации

Решение нелинейных уравнений


Лабораторный практикум по школьным задачам

Вводные замечания

Количество единиц в двоичной записи числа

Меняем местами значения элементов массивов

Остаток от деления в К-ичной системе счисления

Делимость К-ичного числа

Остаток от деления числа в факториальной системе

Задача о кратчайшем пути методом графов

Задача о кратчайшей сумме путей методом графов

Замкнутая цепочка слов

Прямоугольник максимальной площади

Фальшивая монета

Перебор вариантов -размещение небьющих ферзей

Расшифровка текста по ключу

Восстановление искажений по набору эталонов

Рекурсия и задача о ханойской башне

№16


C++ и объектно - ориентированное программирование

Дополнения к языку С

Классы и механизм наследования

Виртуальные базовые классы

Друзья классов

Конструкторы и деструкторы

Перегруженные операции и операции-функции

Виртуальные функции

Абстрактные классы

Объектные потоки

Стандартные потоки

Вывод в потоки

Ввод (извлечение) из потока

Инициализация (конструирование) потоков

Объектно - ориентированное программирование


Лабораторный практикум по ООП в Си++

Отсортированный массив строк

Ассоциативный словарь

Связные списки

Матричный тип


Численные методы

Решение нелинейных и трансцендентных уравнений

Методы поисковой(экспериментальной)оптимизации

Вычисление определенных интегралов

Методы аппроксимации(приближения)функций

Численные методы в задачах линейной алгебры

Линейное программирование

Задача Коши для обыкновенных дифференциальных уравнений

Граничные задачи

Динамическое программирование

Составление Windows-приложений

Тексты файлов включения для лабораторных работ


Линейные и трансцендентные уравнения
Решение нелинейных и трансцендентных уравнений

Отделение корней

Метод дихотомии(половинного деления)

Метод хорд

Метод секущих

Метод Ньютона (метод касательных)

Метод случайного поиска


Методы поисковой оптимизации
Методы поисковой(экспериментальной)оптимизации

Метод половинного деления

Метод Фибоначчи

Метод золотого сечения

Метод координатного спуска

Метод градиентного спуска

Метод последовательного симплексного поиска


Вычисление определенных интегралов

Методы прямоугольников

Метод трапеций

Метод Симпсона


Методы аппроксимации
Методы аппроксимации(приближения)функций

Интерполяция каноническим полиномом

Интерполяционный полином Лагранжа

Интерполяционный полином Ньютона

Интерполяция кубическими сплайнами

Приближение функций по методу наименьших квадратов


Линейная алгебра
Численные методы в задачах линейной алгебры

Метод Гаусса с выбором главного элемента для решения СЛАУ

Итерационные методы решения СЛАУ

Метод ортогонализации

Вычисление определителей

Вычисление элементов обратной матрицы решением СЛАУ

Вычисление элементов обратной матрицы через определитель


Линейное программирование

Симплекс-метод решения общей задачи линейного программирования

Транспортная задача


Задача Коши для ОДУ
Задача Коши для обыкновенных дифференциальных уравнений

Метод Эйлера

Методы Рунге-Кутты второго порядка

Методы Рунге-Кутты четвертого порядка

Метод Адамса


Граничные задачи

Метод конечных разностей для линейных граничных задач

Метод стрельбы для граничных задач

Граничные задачи на собственные значения для ОДУ

Граничная задача для дифуравнений в частных производных


Динамическое программирование

Динамическое программирование


Windows - оформление задач численных методов
Составление Windows-приложений

Шаблон программы при составлении Windows-приложений


Файлы включения с имитационными классами
Тексты файлов включения для лабораторных работ

Файлы включения к определению корней m51.h

Файлы включения к поиску экстремумов m52.h

Файлы включения к вычислениям интегралов m53.h

Файлы включения к задачам аппроксимации m54.h

Файлы включения к задачам линейной алгебры m55.h

Файлы включения к линейному программированию m56.h

Файлы включения к ОДУ m57.h


Программирование Windows - приложений

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

Вывод текста в окна Windows

Обработка клавиатурных сообщений

Обработка мышиных сообщений

Работа с таймерами в Windows - приложениях

Ресурсы - таблицы строк

Ресурсы - пиктограммы

Ресурсы - курсоры мыши

Ресурсы - bitmap-изображения

Органы управления - кнопки

Управление кнопками из программ

Статические органы управления

Органы управления - полосы скроллинга

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

Списки класса LISTBOX

Списки класса COMBOBOX

Диалоговые панели

Ресурсы - меню

Создание справочных систем для Windows-приложений


Основы научных исследований
Основы научных исследований - краткий курс

Основные понятия и определения

Методологические основы научного познания и творчества

Основные этапы НИР

Задачи и методы теоретических исследований

Математическое моделирование объекта исследований

Теория подобия и размерностей в физическом моделировании

Экспериментальные исследования в математическом иоделировании


Системы управления базами данных в   среде FoxPro 2.0

Общая постановка задачи

Основные понятия

Командный режим СУБД FoxPro

Создание файлов базы данных

Заполнение файлов БД

Перемещения в БД

Просмотр данных в БД

Удаление записей в БД

Изменение данных

Локализация и поиск - фильтрация

Локализация и поиск - последовательный поиск

Локализация и поиск - индексирование БД

Локализация и поиск - индексный поиск

Локализация и поиск - управление индексами

Локализация и поиск - технология RUSHMORE

Связи между БД

Создание командных файлов

Set - команды

Ввод - вывод и команда Read

Функции проверки файлов и дисков

Команды языка запросов к БД SQL

Команды управления

Работа с переменными и массивами

Работа с окнами

Меню в прикладных программах

Модульность программ

Циклы в FoxPro

Работа со строками и преобразование типов

Средства управления в стиле Windows

Процедуры заполнения БД "Расписание занятий"

Обслуживание основной БД "Расписание занятий"

Текст программы "Расписание занятий"


Когда начинать изучение информатики
Назначение справочника

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


Программирование - общеобразовательнвая дисциплина
Программирование - общеобразовательная дисциплина

Основная   составляющая      информатики - программирование компьютеров  и  именно  по   этой  ее  основе   периодически возбуждаются   дискуссии   на   тему: "Преподавать   или   не преподавать программирование  в средней  общеобразовательной школе?"    Объективной  основой   для  возникновения   таких  дискуссий     является     отсутствие        в        школах преподавателей, обладающих  необходимым  объемом  знаний    и практических   навыков  в   программировании,   отработанных методик  преподавания  и  технических  средств  (современных персональных   компьютеров   с    мультимедиа)   для      их реализации. Аргументы же за отмену изучения  программирования сводятся к тому, что в школьном курсе информатики  достаточно ограничиться     освоением     компьютера     на      уровне непрограммирующего пользователя, освоившего работу с файловым монитором, текстовым  редактором, электронной таблицей и пр. Безусловно, это      тоже    необходимо, но    не    вместо программирования - ведь не заменяем же  мы  изучение   законов Ома   обучением работе  с выключателем,  предохранителем   и утюгом?  С  такой  аргументацией   можно заменить и изучение таблицы  умножения   работой  на   калькуляторе.    Академик  А.П.Ершов назвал программирование второй грамотностью и  это не  метафора; сегодня   ясно,  что  программирование  -   это просто составляющая общей  грамотности и культуры,  не менее важная,       чем       умение        читать, писать, считать. Зачем мы  вообще обучаем  в школе  столь большому количеству предметов, да еще столь сложных для восприятия как  алгебра, тригонометрия, физика,  ` химия?   Ведь   большинство    наших выпускников никогда  не будут  решать системы  уравнений или совершенствовать   теоретические   основы   физики  твердого тела. Просто  мы  осознаем  ,что  без  этих основ общество не получит не только инженеров ,но и квалифицированных рабочих, а труд во всех сферах материального производства  становится все  более  сложным  и  для  его выполнения уже недостаточно научиться  владеть  лопатой.  Сегодня  сталевар или оператор тяжелого обжимного  стана нередко  имеет высшее  образование и  это  не  архитектурное  излишество  ,  а веление времени.  Что же дает программирование для общего развития человека  ? Чтобы ответить  на этот  вопрос, вспомним  , что  программа- это алгоритм или метод решения всех задач некоторого класса. Грамотно составленная программа  сразу после запуска  должна проделать ряд организующих действий:

  - выяснить,  где  взять   исходные  данные   (информационное сырье);

  - получить доступ   к хранилищу   исходных данных   и  определить их размер, а  иногда и тип;

  - запросить  у  операционной  системы  необходимый  для размещения этих данных объем оперативной памяти;

  - загрузить  исходные  данные  в  полученное рабочее пространство;

  - выделить  место  для  хранения  промежуточных  и   конечных  результатов обработки данных;

  - выполнить обработку  данных;

 - "упаковать" результат в удобную и приятную для  пользователя форму   и    выдать   (отобразить    )   этот результат.

  Даже  эта  сильно  упрощенная  схема  вызывает ассоциации со многими  областями  человеческой  деятельности ,как   будто совсем не   связанными  с программированием. Что  бы  мы ни собрались произвести (написать  книгу или построить  сарай), нам  прийдется решить   задачи  получения  и   складирования исходных материалов,   разработки   пооперационного  плана выполнения работ, оформления внешнего   вида   изделия   для передачи его  заказчику.    Таким  образом  , составление компьютерных программ   приучает   нас   планировать   свою деятельность ,выстраивать  работы  в логически обоснованную сеть операций  ,то  есть  прививает  организаторские навыки ,учит понимать   влияние   организации    труда   на   его эффективность. Каждый грамотный человек должен понимать  ,что плохая  организация труда  в  состоянии погубить  результаты сколь угодно высокого индивидуального мастерства  исполнения отдельных операций.    В предыдущей схеме  мы опустили  детали технологии  обработки   данных  программой. При   составлении программы программист работает в некоторой  инструментальной среде  и  при  разбиении  решения  общей задачи на отдельные операции  вынужден  учитывать   возможности  этой  среды   - ассортимент   услуг    операционной    системы    ,языкового транслятора  ,наличие необходимых  процедур  в  библиотеках. Эта  всегда присутствующая   сторона   труда    программиста дает навыки в составлении пооперационных планов любых  видов работ  с  учетом   возможностей  имеющихся  в   распоряжении исполнителей.  Генри Форд ,разложивший  сборку автомобиля на элементарные,   доступные    неквалифицированным    рабочим операции на  конвейере был  талантливым программистом  - без этого автомобиль до сих пор был  бы  предметом  роскоши  , а не средством передвижения.   Если мы хотим выращивать  своих современных Фордов , а не только исполнителей для  конвейера ,преподавать   программирование   как    общеобразовательную дисциплину необходимо.  Все сказанное достаточно  тривиально и куда более убедительно изложено  в  работах А.П.Ершова   и грустно это повторять.  Но у нас ставилась под сомнение даже необходимость  изучения  орфографии- почему не  писать слова так, как мы их произносим?   Слава  всевышнему все   же пишем "пятьдесят", а не "пиисят".  Дефицит грамотных преподавателей программирования      в   школах    и   вузах    необходимо компенсировать  привлечением  профессиональных  практикующих программистов  .Это  очень актуальная проблема  сегодняшнего дня-   специфика   программирования   требует  повседневного тренинга ,а оторванное от практической  работы  преподавание этого  предмета  неизбежно  приводит  к потере квалификации. Это  все   же  не   классическая  дисциплина   ,  а   быстро развивающаяся в концептуальном и методическом плане  область с  достаточно  серьезной  ролью  чисто технических приемов - здесь  недостаточно  понимать  и  знать,  надо еще и уметь. И  здесь надо бы не слишком обращать внимание при оплате  труда на     наличие   ученых     степеней     и     званий - квалифицированные программисты часто обходятся без  них.   Техническое   обеспечение   - проблема   чисто финансово     -  хозяйственная,  здесь роль   преподавателя информатики   сводится   к определению      технических характеристик  и   конфигурации приобретаемых  компьютеров  и вспомогательных      средств и   тщательному   контролю соответствия поставки сделанному заказу - поэтому ему  нужны достаточные познания и в области техники.


Когда начинать изучение информатики
Когда начинать изучение информатики в школе?

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


Последовательность обучения программированию

 Очень  часто  учебники  по  информатике  начинаются с основ алгоритмизации - ученикам предлагается составить план действий, приводящий к решению различных задач. Например ,классические задачи о волке ,козе и  капусте, задачу о ханойской башне  с последовательностью  перекладывания  кружочков  с колышка на колышек или задачи на  поиск фальшивых монет с  ограниченным числом             взвешиваний             и             пр.  При этом упускается из виду простой факт - ученик совершенно не представляет себе ,при чем тут собственно компьютер ,а мы собираемся учить его программировать этот компьютер на выполнение этих и  многих других  задач по  обработке самой разнотипной информации. Но  это  будет  позже  ,  когда будет осваиваться конкретный язык программирования - и тогда  для учащихся эта сначала не  оговоренная связь  не образуется.  Как -  то там разбирается транслятор (если о его наличии не забудут рассказать, увлекшись операторами циклов и ветвлений) в наших фразах  на тарабарском языке. Сведения же об объекте  программирования- электронном компьютере -часто  выносятся в конец  учебника и следовательно  в  конец  курса  ,оставаясь нередко за бортом программы. В  итоге  ученик  натаскивается  на  решение задач типа поиска элемента в матрице без малейшего понимания того, как         и         почему         это         происходит.  Более  логичным  представляется  в  самом начале рассказать о том  ,что математические  вычисления всегда  были одной из самых  трудоемких  и  изнуряющих  работ,  в связи с чем люди всегда стремились облегчить себе этот труд перекладыванием его на машины , - это такой же процесс как перекладывание копания ям на  плечи экскаваторов. Неплохо  привести краткую  историю развития инструментальных средств для математических вычислений- от  галечных  счетов  до  электронных  вычислительных машин. Далее видимо  должен следовать  рассказ о  роли информации в развитии  современных  технологий  и  общества  в  целом ,об информационном взрыве  в 20-м  веке и  острой потребности  в технических   средствах   не   только   для   механизации  и автоматизации  вычислений   (то  есть   обработки   числовой информации),а и для обработки  информации ,представленной  в других , самых разнообразных формах - в виде текстов , рисунков, токов и напряжений и т д.  И далее о том ,что для  обработки нечисловой  информации  было  бы  нецелесообразно  создавать специализированные машины для каждого вида , гораздо разумнее использовать  уже  имеющиеся  средства  для обработки чисел- для этого достаточно  кодировать перед обработкой  любой вид информации в числовую форму , обрабатывать , а при  отображении снова переводить в  естественную форму -  изображение, текст или электрические  сигналы.  Способы  числового  кодирования  наиболее употребимых видов информации должны быть рассмотрены подробно - ученик  должен различать коды  нажатых клавиш  , коды  изображенных на  них символов и поточечные коды  рисунков символов , коды  цифр и соответствующие им числа и числовые коды способов  отображения информации и т д -он  должен четко понимать всеобщий принцип числового и только числового представления данных, способов их отображения и команд обработки.  Особое внимание должно  быть уделено числовому  кодированию команд обработки чисел и  их однотипному с данными  хранению в  памяти  компьютера. После  обсуждения  преимуществ 2-ичной системы  счисления  при  электрическом способе представления чисел , необходим тренинг во взаимных  переходах между 2-ичной ,10-тичной  системами  счисления  и 16-ричным представлением 2-ичных чисел.  Неплохо освоить и  2-ично-десятичную систему- ученик не должен испытывать затруднений в работе с указанными системами счисления. Следующий этап - изучение архитектуры персонального компьютера и основных его составных частей - процессора , оперативной памяти , дисковой  памяти  ,  клавиатуры  и  устройств   отображения. Особое внимание должно быть уделено составу ,роли и функциям операционной  системы   ,  различным   уровням  доступа    к предоставляемым ею услугам и способам их получения. Обязательно должен быть  рассмотрен хотя  бы упрощенный  алгоритм работы интерпретатора команд.   И только после этого возможен переход к проблемам алгоритмизации При этом  должное внимание  необходимо уделить  неразрывному единству алгоритма и возможностей его исполнителя - автомата (компьютера) - эти возможности ограничены набором распознаваемых команд процессора на самом нижнем уровне, ассортиментом услуг операционной системы  , встроенными  возможностями языкового транслятора и имеющимися библиотеками на более высоких уровнях.  На первом этапе необходимо научить учеников словесному описанию алгоритмов на родном языке ,постоянно обращая внимание на то, что разрабатывается общий метод решения класса однотипных задач, что конкретные  данные не  могут быть  известны алгоритму до начала выполнения .


Об алгоритмических языках для учебного процесса
На каком транслируемом языке обучать программированию?

 Традиционно обучение программированию в школах ведется либо на рекомендованном А.П.Ершовым школьном алгоритмическом языке, так называемом "ершике" с русскоязычными операторами (есть и его национальные модификации , например украинский),либо на Бейсике старейших модификаций ,поставлявшихся в виде микросхем (IBM Basic, GW Basic, MSX Basic). Значительно реже используется Турбо Паскаль, чаще 3-й версии , а в школах с оснащенными жесткими  дисками компьютерами при наличии квалифицированных преподавателей -  до 7-й версии включительно.  На поставленный в подзаголовке вопрос хотелось бы ответить просто- учите на чем хотите, только учите. Но не получается - вопрос не безразличен по последствиям и тесно связан с целью,  которая ставится  учителем. Общеизвестно  , что программирование не общедоступно , как и художественное или поэтическое творчество или игра  в  шахматы  -  оно требует наличия  так  называемого алгоритмического мышления ,которое может присутствовать и развиваться , а может отсутствовать почти начисто. Сколько ни рассказывай всем в классе о рифмах ,ямбе ,хорее и амфибрахии , поэтами всех не сделаешь. Та же картина с программированием - можно обучить некоторым шаблонным приемам почти всех, но программировать смогут немногие. Эту дилемму  мы обычно решаем  ориентиром на "среднего" ,выбирая средства и методы обучения - и допускаем огромную ошибку ,сводя результаты своего преподавательского   труда к нулевому уровню. Сколько лет мы изучаем в школах   иностранный язык ? Почему наши ученики после ее окончания   не могут прочесть торговый проспект, трехсловное сообщение транслятора или связать пару простейших фраз на языке , который штудировали  много лет?  Конечно здесь и квалификация преподавателя и  пороки в методике , но есть  здесь и  доля этого стремления  ориентироваться на "среднего" - в итоге "мартышкин труд". И очевидно ,что  труд преподавателя иностранного вполне окупаем ,если 2-3  ученика на выпуске свободно владели  бы изучаемым  языком ,  а труд учителя информатики -  если столько же  свободно справляются с задачами прикладного программирования в какой-нибудь области.  Предлагается таким образом ориентир  на сильных учеников , по   разным предметам   они    могут   быть разными    .  Но тогда не любой  язык программирования нужно выбирать  для обучения ,а тот , который предоставляет наибольшие возможности и после освоения которого другие языки осваиваются без труда. Известно ,что после добротного освоения одного  иностранного языка другие даются значительно легче, в особенности если это языки той же группы - например языки германской группы после изучения немецкого.  В     языках     программирования  картина следующая.  Опыт показывает ,что если обучение начиналось с GW Бейсика , то на нем  оно чаще всего  и останавливается - этот язык не способствует порождению полиглотов в языках программирования.  Отметим   ,    что современные реализации    Бейсика обслуживают успешно вполне современные  парадигмы  (стили) программирования (Quick Basic, Turbo Basic, Visual  Basic),но все равно  не способствуют  легкому освоению  других языков.  Программисты ,освоившие Паскаль , с Бейсиком справляются легко и ,что немаловажно , с современными реализациями Бейсика(начало с GW Basic препятствует освоению и современных версий  этого же языка).  Паскаль ,как  и Бейсик  ,создавался как  учебный язык ,причем конструировался не "от компьютера" ,а по правилам математической лингвистики. Отсюда строгое разделение и контроль на совместимость в операциях типов данных. Только программисты, использующие его "всерьез", знают ,сколько усилий приходится прилагать на  преодоление и  обход этого  контроля ,как мало от него остается уже при тривиальном ,обычном для всех программ размещении данных в "куче"  и как мало остается  от хваленой прозрачности его синтаксиса при многочисленных преобразованиях типов "туда-обратно".   Переучивание   с Паскаля    на   Си очень    болезненно.  Третий язык общего   назначения (проблемно-ориентированные  языки мы  пока  не обсуждаем),широко  используемый  в  прикладном программировании ,язык С и его современное  надмножество C++. В свое время создавался как язык системного программирования целевым назначением для разработки операционной системы UNIX.  Он создан "от компьютера" - в нем все немногочисленные встроенные типы данных - только числовые, что отражает всеобщий  принцип числового кодирования информации , а следовательно с  кодами символов или адресами вполне допустимы операции целочисленной  арифметики.  Конкретный  вид  информации определяется только при  ее   отображении  на   экран  ,   бумагу  , в   файл.  К его недостаткам относят трудный для чтения синтаксис - по-видимому из-за поддержки компилятором наряду с расширенной, обычной для всех языков записью операций еще и сокращенной типа ++, +=, *= и т.д.  Вторым недостатком считают отсутствие строгого контроля типов в  операциях,  как  источник  ошибок  в  учебном  процессе.  По моим наблюдениям С - программисты (и "Фортранисты") не испытывают ни малейших затруднений при переходе на другие языки , хотя и делают    это    только при    внешней     необходимости.  Этот язык на сегодня предоставляет наибольшие возможности в программистском творчестве ,имеет чрезвычайно богатые библиотеки , расширяем пользовательскими  типами  и  перегрузкой для них общепринятых операций, имеет множество других достоинств и на мой взгляд именно он должен выбираться в качестве первого языка при изучении программирования. В его пользу говорит также необходимость для каждого изучающего программирование исследовать возможности операционной системы - в литературе и справочниках вы найдете иллюстрации по работе с функциями ОС на Ассемблере или С , так что при серьезном изучении программирования все равно без Си или Ассемблера не обойтись.  По видимому мы выросли из детского возраста освоения программирования- концепция ,требовавшая наличия какого-то специального учебного языка давно устарела - он не нужен, обучение необходимо уже в школе начинать с профессионального языка. Недаром после Паскаля и Бейсика , прошедших трансформацию от учебных к профессиональным языкам, других учебных языков  не   разрабатывалось  уже   несколько десятилетий. Особо надо оговорить как крайне нежелательное преподавание школьного алгоритмического русско- или украинско-ориентированного языка. Это не просто потерянное время - это и время на последующее переучивание.  Совершенно не ясна цель его создания - несколько простых английских слов вроде if, while, for доступны для изучения и первокласснику, а последующее сколько-нибудь серьезное изучение программирования все равно приведет к англоязычному компилятору. Это вовсе не исключает необходимости словесного описания алгоритмов на родном языке или слегка формализованном для краткости псевдо-языке - разработчик программы должен вначале определить, что надо делать, а уж потом искать как это делать.


Информатика  и информация

  Очевидно, из названия предмета ,к изучению которого мы приступаем, что он посвящен исследованию основных процессов, связанных с информацией. При этом происхождение и формы представления информации не оговариваются заранее - они могут любыми. Поэтому вначале попробуем разобраться, что же такое информация.   Известный  постулат в библейской теории создания мира гласит:                 "ВНАЧАЛЕ БЫЛО СЛОВО!"

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

  Таким образом, каждый живой организм хранит в себе унаследованную информацию - именно поэтому из семян подсолнечника  вырастают не баобабы, а такие же подсолнечники.   Помимо унаследованной, живые организмы могут получать, передавать другим организмам , хранить и преобразовывать дополнительную информацию, связанную с их жизнедеятельностью в окружающей среде.   Получая информацию, живой организм обучается - вырабатывает рефлексы, как животные, формирует  новые понятия, как это делаете вы сейчас (надеемся). Но как мы получаем эту информацию?   Для того, чтобы получить информацию, организм должен иметь пути (каналы, средства) ее получения.  Человеческий организм имеет 5 таких каналов, начинающихся в наших органах чувств (зрение, слух, вкус, обоняние и осязание). Заканчиваются они  в соответствующих участках коры головного мозга, который выполняет роль обработчика и хранителя информации.  Таким образом мы различаем 2 вида информации - унаследованную и приобретаемую в процессе исследования окружающего мира во всем его многообразии, в процессе обучения.

 Мы будем изучать процессы, связанные со вторым видом информации

 -приобретаемой в процессах исследования.

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

    Посреди пустынных недр

    Черной краской он сверкает.

    Пусть случайный путник знает:

   "Двадцать третий километр"!

    Не смешно ль : ни до кого

    Столб свой текст довесть не может,

    Если глаз наш смысл не вложит

    В знаки мертвые его.

                         Христиан Моргенштерн

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


Вероятность,энтропия,Количество информации
Вероятность, энтропия и количество информации

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

Примеры :

Подброшенная монета после падения может иметь 2 равновозможных

состояния  - герб или решетка .

Подброшенная  6-гранная  игральная кость имеет 6 равновозможных

состояний - выпадение от 1 до 6 очков.

Вытащенная из колоды игральная карта может принять одно из N

равновозможных значений , где N - количество различных карт в

колоде.

ВЕРОЯТНОСТЬ-это численная мера возможности  события. Единицей измерения вероятности математики приняли вероятноять достоверного события - такого ,которое в результате опыта произойдет обязательно - ему и приписали вероятность 1(единица). Невозможному событию (например выпадению 12 очков на 6-гранной кости) прописали значение 0.Все остальные события , происходящие в опытах чаще или реже характеризуются значением вероятности между 0 и 1.

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

                      ЭНТРОПИЯ .

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

-при заданном числе состояний энтропия должна быть наибольшей

 (достигать максимума) , когда все ее состояния равновозможны

 (равновероятны);

-при объединении нескольких независимых систем в одну их энтропии

 должны бы складываться (энтропия должна обладать свойством

аддитивности).

Под эти требования хорошо подошла формула вычисления энтропии Н как суммы произведений вероятностей  состояний исследуемой системы на логарифмы этих вероятностей :                   n

              H = СУММА Pi*log(1/Pi)

                 i=1

n-число состояний системы ,Pi-вероятность  i-го состояния.

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

        H = 1/2 * log(2) +1/2 * log(2) = 1.

Эту двоичную единицу называт бит (bit) от  английского          binary digit - двоичный знак

Если система имеет N равновозможных состояний то ее энтропия

             H = N * 1/N * log(N)

равна логарифму числа состояний.

Мы вправе полагать что при получении информации о состоянии системы неопрделенность в ее оценке может быть уменьшена.При полном выяснении состояния системы приобретаемая информация  I (точнее - ее  количество) равна энтропии этой системы и может быть вычислена  по той же формуле :

                  n

              I = СУММА Pi*log(1/Pi)

                 i=1

Эта формула означает , что полная информация есть осредненное по всем состояниям системы значения логарифма вероятности  состояний Каждое отдельное слагаемое log(1/Pi) - это частная информация от отдельного сообщения . Если все состояния системы равновероятны то частная информация равна полной. Если у состояний системы различные вероятности то информации от различных сообщений неодинаковы - наибольшую информацию несут сообщения о наименее вероятных событиях.


Сообщения и сигналы

Представители живой природы с высокой оргаанизацией нервной системы создают в процессе обучения СИСТЕМУ ПОНЯТИЙ-мысленных образов различных предметов окружающего мира,способов поведения в различных условиях и вполне естественно испытывают потребность в общении между собой,другими словами- потребность в обмене информацией.  Для передачи адресату некоторой совокупности понятий необходимо прежде всего осуществить ВЫБОР подлежащего передаче материала (понятий) и затем пробразовать его к форме,пригодной для осуществления самой передачи.Это преобразование называют КОДИРОВАНИЕМ - отображением состояния одной физической системы с помощью состояния некоторой другой. Мы осуществляем кодирование передаваемых понятий неосознанно, напрягая голосовые связки,двигая языком и губами при передаче с помощью звуковых колебаний.Мы кодируем передаваемые понятия последовательностью наносимых на бумагу символов (букв) когда пишем письмо,сигнальщик на корабле осуществляет передачу взмахами флажков или включением-выключением фонаря в ночное время.  Совместно живущие существа пробретают примерно одинаковые наборы понятий и вырабатывают согласованные способы их кодирования-понятия и способы их кодирования образуются одновременно в сообществе существ,живущих общественным укладом. Кодирование понятий при их подготовке к передаче осуществляется на двух уровнях-логическом и физическом.  На логическом уровне происходит преобразование конкретных понятий в абстрактную форму.Например в почтовой связи используется числовое кодирование стран,населенных пунктов,почтовых отделений.В очень больших городах нумеруются улицы и все строения на этих улицах. В телетайпной связи,предназначенной для передачи текстов, числами кодируются все передаваемые буквы алфавита.  Понятие в закодированной форме-это СООБЩЕНИЕ или СООБЩЕНИЕ-это кодированная форма понятия,используемая в передаче.Сообщение не является материальной или энергетической категорией,для его передачи по каналу связи его необходимо "материализовать",преобразовать в последовательность физических фактов,называемых СИГНАЛАМИ-то есть закодировать на физическом уровне.  Сигналами могут быть электрические токи и напряжения, звуковые и световые волны,зоны различной намагниченности на ферромагнитных материалах и пр.Сигналы могут применяться для передачи сообщений но они ни в коей мере не тождественны сообщениям.Один и тот же сигнал может использоваться для передачи различных сообщений.В свою очередь одно и то же сообщение может быть кодированной формой различных понятий. Поэтому обязательными, необходимыми условиями возможности информационного обмена между отправителем и получателем сообщений является :  1.Наличие пересекающихся (имеющих общую часть) множеств понятий.  2.Наличие одинаковых способов кодирования.  При выполнении этих 2- условий процесс передачи-приема осуществляется по следующей схеме:

ВЫБОР - КОДИРОВАНИЕ СООБЩЕНИЙ - ПРЕОБРАЗОВАНИЕ В СИГНАЛЫ -

-ПРИЕМ СИГНАЛОВ - ПРЕОБРАЗОВАНИЕ В СООБЩЕНИЯ -

-ВЫБОР(ДЕКОДИРОВАНИЕ СООБЩЕНИЙ В ПОНЯТИЯ)

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


Системы счисления
Системы счисления.

По-видимому количественная мера вещей играла важнейшую роль в жизни и самоутверждении древнего человека, так как считать и изобретать инструменты для облегчения счета он начал значительно раньше,чем изобрел алфавит для изображения звуков речи.  При этом инструментальный счет опережал в развитии устный и письменный-пергамент предстояло изобрести только в 5-м веке до н.э.,бумагу-в 11-м нашей,а считать надо было уже сегодня.Да и после появления письменности развитию письменного счета препятствовало несовершенство тогдашних систем счисления-предложение перемножить CLVI на LXXIV в римской системе вряд ли вызовет у вас энтузиазм и в наше время.  Надо было как-то информировать соплеменников о глубине ручья или высоте горы,о расстоянии до банановой рощи или количестве пойманных рыб,находить эквиваленты бартерного обмена своего добытого на охоте мамонта на сладкие плоды, собранные  соседним племенем. Да и сама письменность первоначально содержала скорее всего цифровую часть алфавита.Можно предположить что вначале каждому сосчитанному предмету соответствовала нарисованная на стене пещеры палочка,сложение осуществлялось дорисовыванием палочек а вычитание-стиранием.  Такая система счисления с единственной цифрой-палочкой называется УНАРНОЙ.Ее очевидное  неудобство привело к позиционным системам счисления, у которых обозначаемое предметом или знаком число определяется его местом в последовательности себе подобных.  Уже первые галечные счеты (calculus -галька ,голыш , обкатанный волнами камень) реализовали позиционный способ счета. Емкость ячейки в каждой позиции определялась естественно природным фактором- количеством пальцев на руках и порождала наиболее распространенные пятеричную и десятеричную системы счисления.  Позиционные системы имеют одно несомненное преимущество- в них человеку достаточно уметь считать и осуществлять арифметические действия только в пределах числа, определяющего основание системы счисления.В десятичной системе достаточно считать до 10,а далее все циклически повторяется для десятков,сотен,тысяч и т д В современной 10-тичной арифметике для обозначения чисел от нуля до девяти мы используем арабские символы-цифры 0,1,2,3,4,5,6,7,8,9.Позиции,в которых располагаются эти символы,нумеруются начиная от нуля справа налево, а обозначаемые символами числа получают премножением числа-цифры на 10 в степени номера позиции.  Значение многопозиционного числа получают суммированием этих произведений:  9876 = 9*10^3 +        8*10^2 + 7*10^1 + 6*10^0 (значек ^ обозначает возведение       в степень) Точно так же определяются числа в системах счисления старше и меньше десятичной.При этом договорились не изобретать новых символов для обозначения цифр в других системах счисления-для систем меньших десятичной достаточно имеющихся символов 10-тичной системы,а для систем с основанием больше 10 для обозначения цифр-чисел 10 и больше договорились использовать буквы латинского алфавита от 'a' до 'z'(безразлично-большие или маленькие).  Таким образом например в 16-ричной системе счисления цифровой алфавит выглядит так:

               0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f Цифра 'f'

соответствует при этом 10-тичному числу 15 и прибавление к нему

1 вызовет перенос в старшую,стоящую левее позицию :  f + 1 =10.

                 Цифра 'a' соответствует 10-ному числу 10 и

сумма а+1=b

Число ff соответствует 10-ному 15*16^1 + 15*16^0 =255 и прибавление к нему 1 даст ff+1=100 В латинском алфавите 26букв ,поэтому системы счисления с основание более 36 (10арабских цифр и 26 латинских букв) не определены и для их использования вам придется придумать символы для чисел 36 и более.

Наименьшее основание для позиционной системы счисления очевидно 2 и в 2-ичной системе счисления используются всего 2 цифры '0' и '1'.Прибавление единицы к числу 1 вызовет переполнение и разряда (текущей позиции) и перенос в старший, стоящий слева разряд (позицуию):  1 + 1 = 10 10 + 1 =11 11 + 1 =              100 101101 +    10110 ________ 1000011 Последний             результат имеет следующий 10-тичный эквивалент:

1*2^6 + 0*2^5 + 0*2^4 + 0*2^3 +  0*2^2 + 1*2^1 + 1*2^0 = =64 + 2

     + 1 =67


История развития средств инструментального счета
Из истории развития средств инструментального счета

Развитие торговли,строительства,ремесел,промышленного производства,военного дела  непрерывно увеличивало потребность людей в вычислениях,сами вычислительные задачи становились все более сложными.Люди,которым в силу различных причин приходилось выполнять нелегкую вычислительную работу, стремились облегчить ее созданием вычислительных устройств.Сегодня мы называем эти усиройства калькуляторами, вычислителями, компьютерами (computer-вычислитель).  Вычислительные устройства прошли длинный путь развития, начинавшийся от веревочек с узелками и дощечек с зарубками.  Видимо первым переносным калькулятором был АБАК, (4-й - 5-й век до нашей эры)представлявшийсобой доску, размеченную на колонки  , в которых по позиционному принципу размещались камешки или палочки.  Китайцы заменили камешки нанизанными на прутики,проволочки или веревочки и называли это устройство "суаньпань"(6-й век нашей эры).Где-то на рубеже 16-17 веков появился русский абак -счеты.  Появление и распространение в 12-13 веках бумаги вызвало развитие письменного счета ,основанного на мспользовании арабско-индийских цифр.Популярность этого метода возросла после выхода книги Леонардо Пизанского,прозванного Фибоначчи, с описанием способов письменных вычислений.Борьба между абакистами,отстаивавшими инструментальный счет на абаках и римскую систему счисления и "алгоритмиками",предпочитавшими арабские цифры и письменные вычисления завершилась лишь в 17 веке победой алгоритмиков. Вычислительные трудности были существенно снижены изобретением в начале 17 века логарифмов (Джон Непер опубликовал первую таблицу логарифмов в 1614 г).Это позволило решать задачи на умножение и сложение с помощью сложения и вычитания.  Изобретение логарифмов стимулировало работы по механизации вычислений-вначале появились логарифмические шкалы,облегчавшие вычисления а затем различные конструкции логарифмических линеек,в усовершенствовании которых идеей бегунка принял участие и великий Ньютон.  Первую механическую "арифметическую машину" ,осуществлявшую 4 арифметических действия в 10-тичной системе счисления, изготовил в 1645 году известный физик и математик Блез Паскаль.Ему удалось решить один из труднейших вопросов в счетном машиностроении всех времен-перенос десятков из младших разрядов в старшие.Основная трудность при "механизации" этой операции состоит в "заеме" при вычитании.  Паскаль решил эту проблему заменой вычитания операцией сложения с арифметичеким дополнением вычитаемого.Так как этот метод Паскаля используется и в современных электронных компьютерах,мы разберем его подробнее.  Во-первых учтем что в любом вычислительном устройстве вводимые, хранимые, обрабатываемые и выводимые числа имеют ограниченную длину-как говорят ограниченную разрядную сетку.Взгляните на электросчетчик в вашей квартире-в нем 5 окошечек с цифрами от 0 до 9. Представьте что на счетчике 99999. Что произойдет при следующем обороте диска, при попытке добавить 1? В счетчике установится число 00000, а прибавлення 1 "перенесется" в отсутствующий старший разряд. Это явление называют ПЕРЕПОЛНЕНИЕМ РАЗРЯДНОЙ            СЕТКИ УСТРОЙСТВА Это специфическое свойство счетных устройств - они не могут работать с числами произвольного количества позиций в отличие от бумажной записи,в которой нетрудно представить число любой длины ,записанное на ленте длиной "от села до села".  Теперь представим себе счетное устройство,умеющее выполнять только одно арифметическое действие - сложение цедых чисел.  С помощью многократных сложений мы можем осуществить умножение, с помощбю многократных вычитаний - деление. Теперь для осуществления всех 4-х арифметичкеских действий нам осталось выполнить вычитание с помощью операции сложения.Каким числом мы должны заменить вычитаемое,чтобы результат оказался верным после сложения?  Это число равно дополнению вычитаемого до переполнения разрядной сетки устройсива и называется его АРИФМЕТИЧЕСКИМ ДОПОЛНЕНИЕМ Пусть нужно выполнить 5643-341 =?   (Должно быть 5302) в устройстве с 5-разрядной числовой сеткой.Задача перепишется в виде 05643 - 00341 =?  Дополнением числа 341 до переполнения будет 100000-341=99659 Теперь сложим    05643 + 99659 =(1)  05302 Взятая в скобки 1 "выпала" в отсутствующий старший разряд и мы получили правильный результат вычитания,выполненного сложением с арифметическим дополнением вычитаемого.  Здесь мы пришли к очень важному выводу:  ВСЕ 4 АРИФМЕТИЧЕСКИХ ДЕЙСТВИЯ МОГУТ БЫТЬ    ВЫПОЛНЕНЫ НА УСТРОЙСТВЕ С ОДНОЙ ОПЕРАЦИЕЙ СЛОЖЕНИЯ А теперь продолжим наш историчский экскурс.  После машины Паскаля появился еще ряд механических вычислителей для коммерческих,научных расчетов и даже для обучения математике (Перейра,1749 год).  В России первая счетная машина создана около 1770 года литовским часовым мастером Якобсоном,затем в 1843 г Слонимским Среди последующих вариантов следует упомянуть первый в мире арифмометр-арифметическую машину Лейбница,выполнявшую уже все арифметические действия в отличие от предшествующих суммирующих машин.  Пионером автоматических вычислений заслуженно считают Чарлза Бэббеджа,создателя первой универсальной вычислительной машины с автомаизированным процессом вычислений-прообраз современных компьютеров.Предложенная им в 1819 году машина предназначалась для вычисления значений многочленов по способу разностей.Вмешательство человека в процесс вычислений в машине Беббеджа не требовалось.  По-видимому первым программистом следует считать Аду Байрон-Левлейс (дочь знаменитого поэта),составившую для машины Беббеджа алгоритм вычисления чисел Бернулли.  Все упоминавшиеся нами машины были чисто механическими.Первой машиной ,в которой для управления процессом вычислений использовались электромеханические реле, была "Вычислительная машина с автоматическим управлением последовательностью операций"  по имени "Марк-1".Ее создание финансировала известная теперь фирма-производитель компьютеров IBM (1944 год,США).Для представления чисел по-прежнему использовались механические элементы.Управляющие работой машины команды вводились с перфорированной ленты с помощью контактных щеток.Автор разработки-математик и инженер Айкен.  Примерно в это же время были созданы ряд "чисто релейных" машин -уже с двоично-десятичным (1941 г.,Цузе) или с двоично- пятеричным (Штибитц),или с чисто двоичным (1954,Бессонов,СССР) представлением чисел.  Век релейных машин был очень недолгим-в 1943 году в США под руководством ученых Моучли и Эккерта началась работа над созданием первой электронной вычислительной машины на электронных лампах.В 1945 г ими  была создана предназначенная для военных целей ламповая машина ЭНИАК-огромное сооружение на 40 панелях,содержащих 18000 ламп и 1500 реле,потреблявшее 150 квт электроэнергии.ЭНИАК тратил на сложение 0.0002 сек, а на умножение 0.0028 сек-скорость,недоступная его механическим и электромеханическим предшественникам.  После того как известный ученый фон Нйман с коллегами предложили принцип "хранимой программы",в Англии в 1950г была создана первая в мире машина с хранимой программой-машина совершенно нового типа.После нее настроили много ламповых машин,в том числе МЭСМ (Лебедев,Киев),БЭСМ (Лебедев,Москва) и наконец семейство первых в СССР серийных машин "Урал".  В середине 50-х годов на смену ламповым машинам пришли ЭВМ на полупроводниковых транзисторах,более быстродействующие, надежные,зкономичные машины второго поколения.  Развитие третьего поколения компьютеров тесно связано с разработкой интегральной технологии,позволившей объединить в одном элементе несколько транзисторов-это были машины на малых интегральных схемах (МИС).  Затем развитие интегральной технологии привело к созданию микросхем средней интеграции,содержащих уже десятки транзисторов, и к разработке вычислительных машин 4-го поколения.5-е поколение -это использование больших интегральных схем,приведших компьютерную технику и связанные с ней технологии обработки информации на стол каждому желающему.  Первая электронная машина использовалась в метеорологии, военном деле идля некоторых научно-технических расчетов.  20 лет назад насчитывалось уже 2500 областей применения ЭВМ,а сегодня можно сказать что области деятельности обходящиеся без машин остались в меньшинстве-это в основном ручной труд.Но так как сегодня только 1% всей полезной работы на земле выполняется вручную,то сферы применения ЭВМ уже неперечислимы.


Оперативная память компьютеров
Оперативная память электронных вычислительных машин

Вычислительная машина - это фабрика для переработки чисел и, как любая фабрика, должна иметь место хранения исходного числового материала,промежуточных результатов  вычислений и конечных результатов обработки-своеобразные склады данных. Этим складом является так называемая оперативная память машины-именно в нее записываются исходные,промежуточные и конечные данные вычислений.Прежде чем решить какой быть этой памяти конструкторам электронных машин пришлось изменить использовавшуюся в механических машинах привычную людям десятичную систему счисления на двоичную.Почему? В электронной машине запись чисел в память осуществляется с помощью электрических сигналов-токов или напряжений. Для работы в 10-тичной системе счисления пришлось бы для каждой позиции хранимого числа различать 10 уровней тока или напряжения.Это не только громоздко,но и ненадежно,так как изменения напряжения в питающей сети ,сопротивлений элементов электрических схем от температуры и другие колебания приводили бы к возможности "перекрытия" уровней тока или напряжения "внутри" числовых позиций и к искажению значений. Способ вычислений для каждой вычислительной задачи (его называют программой) тоже загружаеися в оперативную память в виде числовых кодов команд сложения,вычитания. чтения и записи в память и т д. Очевидно что ненадежность хранения данных и их обработки сделает машину малопригодной к употреблению, а обеспечение надежности в десятичной системе обойдется дорого.

В то же время 2-ичная система счисления требует для каждой позиции хранимых чисел только 2 уровня тока или напряжения- при этом их конкретные значения не имеют значения,достаточно различать высокий и низкий уровень (договорившись какой из них считать нулем а какой единицей) то есть можно перейти от количественной оценки к более простой качественной. Удобным оказывается использовать и электрические контакты, имеющие 2 состояния - замкнут или разомкнут,что используется при создании так называемых             ПОСТОЯННЫХ ЗАПОМИНАЮЩИХ УСТРОЙСТВ (ПЗУ), в которые числовые коды команд или числовые константы записываются например методом "ПРОЖИГАНИЯ ПЕРЕМЫЧЕК" то есть замыканием-размыканием некоторых цепей "навсегда" для записи неменяющейся числовой информации. Таким образом в ПЗУ хранят последовательность команд проверки оборудования компьютера и других,которые должны начать выполняться сразу после включения сетевого питания. Как и в любой системе счисления позиции двоичного числа нумеруются спрва налево начиная с нуля а позиционные веса равны стпеням двойки (основания системы счисления). Рассмотри 8-мипозиционное 2-ичное число с единицами во всех позициях:

 Позиционные веса: 128  64  32  16  8  4  2  1   (в сумме 255)

 Цифры в позициях:  1   1    1   1  1  1  1  1

 Номера позиций     7   6    5   4  3  2  1  0

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

  Число 65 в двоичном формате и 8-битовой сетке:  01000001

  Инвертируем:                                    10111110

  Добавляем 1: результат минус 65                 10111111

Если сложить весовые значения единичных битов в последнем числе то 65 конечно не получится.Для определения абсолютного значения отрицательного числа необходимо все повторить: инвертировать все биты и прибавить 1:

 Двоичное значение:                               10111111

 Инвертируем:                                     01000000

 Плюс 1 для получения 65:                         01000001

 Сумма +65  и -65 должна давать 0:

                                                  01000001

                                                  10111111

                                                  --------

                                             (1)  00000000

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

                00000001

                11111111

                --------

         (1)    00000000

ЗАПОМНИТЕ ПРОСТОЙ ФАКТ:  ВЕЛИЧИНУ ОТРИЦАТЕЛЬНЫХ ЧИСЕЛ ОПРЕДЕЛЯЮТ  НУЛИ ТАК ЖЕ КАК ВЕЛИЧИНУ ПОЛОЖИТЕЛЬНЫХ

СЛОЖИТЕ НУЛЕВЫЕ БИТЫ И НЕ ЗАБУДЬТЕ  ПРИБАВИТЬ 1!

Для хранения каждой позиции (бита) двоичных чисел в памяти электронного компьютера используют собранную на транзисторе схему  под названием "триггер". Ее особенность состоит в том, что она меняет состояние транзистора из проводяшего в непроводящее и наоборот при подаче сгнала на управляющий электрод,имитируя таким образом изменение 1 в 0 или 0 в 1. Такой триггерный элемент памяти для хранения значений одного разряда двоичного числа называют "битом памяти".  Для того чтобы в память машины можно было записывать числа в нужное нам место необходимо иметь систему адресации .Прежде всего предстоит решить,к каким единицам памяти,состоящей как мы договорились из длинной цепочки битов, мы будем обращуться по адресу? Попутно договоримся что адрес - это порядковый номер адресуемой единицы памяти в цепочке таких единиц. По-видимому адресоваться к каждому биту памяти то есть к каждой позиции двоичных чисел было бы неразумно- адреса битов выражались бы слишком длинными числами. С другой стороны слишком крупные адресуемые участки приведут к прерасходу памяти при записи небольших числовых значений. Поэтому договорились в порядке компромисса ,что

МИНИМАЛЬНОЙ АДРЕСУЕМОЙ ЕДИНИЦЕЙ ПАМЯТИ ЕСТЬ ГРУППА ИЗ 8                                   БИТОВ  и эту 8-мибитовую цепочку назвали  БАЙТ (BYTE)

Разумеется можно адресоваться к блокам памяти любого размера, выраженного целым числом байтов.Адрес блока памяти любого размера - это порядковый номер его первого(начального) байта в цепочке байтов,отсчитанный от самого начала памяти! Нумерация байтов, а следовательно и адресация в памяти начинаеися с нуля,с нулевого адреса.

Как уже упоминалось,последовательность команд обработки числовых данных или программа вычислений размещается в той же оперативной памяти,что и числовые данные. Более того,программа как правило записывается в оперативную память первой, а затем уже она сама руководит размещением в памяти необходимых для ее работы числовых данных.Самые первые программы, обеспечивающие управление аппаратурой машины загружаются в память специальной программой- загрузчиком,которая размещается в постоянном запоминающем устройстве и для начала ее работы достаточно подать питание на микросхему памяти,в которой она "зашита".Чаше всего эта программа загружает в оперативную память не все необходимые программы управления аппаратурой, а дргую программу-загрузчик, находящуюся на внешнем запоминающем устройстве , а уже этот второй программный загрузчик размещает в оперативной  памяти прогаммы управления компьютерными устройствами.


Шестнадцатиричное представление двоичных чисел
16 - ричное представление двоичных чисел

Писать (или набирать на клавиатуре компьютера) длинные цепочки единиц и нулей при задании чисел в 2-ичном формате довольно скучная работа.Так же неудобно просматривать содержимое памяти компьютера ,представленное в 2-ичном формате.  Поэтому разработан своего рода "стенографический" метод представления двоичных данных- каждый 8-мибитовый байт разбивается пополам и каждая 4-битовая его половина записывается в 16-ричной системе счисления.  Например десятичное  число 42936 в 2-ичном формате имеет вид 1010011110111000 После записи полубайтов 16-ричными цифрами 1010 0111 1011  1000 A    7    B    8 получаем                   компактную запись-A7B8.  Небольшая тренировка позволяет оценит удобство этого метода записи повсеместно используемого в программировании.

Перевод 16-ричного формата в 10-тичный все цифры начиная с самой  левой умножаются на 16 и складываются со следующей цифрой,результат умножается на 16,складывается со следующей цифрой и т д:

               A(10) * 16 =160 + 7 = 167 * 16 = 2672 + B(11) =

                      2683 * 16 =42 928 +8 = =42936

 Преобразование 10-тичного числа 42936  в 16-ричный формат

осуществляют аоследовательным делением га 16 и использованием

получаемых остатков в качестве цифр 16-ричного формата слева

направо:

  42936 / 16 = 2683 и 8 в остатке-это младшая 16-ричная цифра

  2683 / 16 = 167   и 11(B) в остатке -это следующая влево цифра

  167 / 16 =  10    и 7 в остатке

  10  /16   =0      и 10(A) в остатке

          получаем   A7B8


Кодирование адресов
КОДИРОВАНИЕ АДРЕСОВ.

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

                 АДРЕСНЫЕ  РЕГИСТРЫ

 От размера этих регистров зависит размер памяти , к которой процессор может непосредственно адресоваться.  В процессорах  первых поколений Intel 8088/8086 размер адресных  регистров был 16 битов или 2 байта - максимальное число (номер  байта в памяти) , котрое может быть записано в такой регистр

                 2^16-1=65535

или как его округленно называют - 64 килобайта

                (1 килобайт = 1024 бита )

 Но уже в компьютерах на базе этих процессоров возникла необходимость адресоваться к памяти размером 1024 килобайтов (1 мегабайт).Чтобы выйти из затруднения с регистрами недостаточного размера для адресации отвели 2 регистра и решили составлять адрес из 2-х частей : первая часть адреса содержит номер блока памяти из 16 байтов под названием ПАРАГРАФ, а вторая часть номер байта ,отсчитанный от границы параграфа. Первую часть адреса назвали адресом СЕГМЕНТА , а вторую - СМЕЩЕНИЕМ внутри сегмента .Очевидно ,что обе составляющие адреса не могут быть больше 65535 , а для вычисления полного адреса любого объекта в памяти необходимо его сегментную часть умножить на 16 и прибавить смещение. Умножение двоичного числа на 16 равносильно приписке справа 4-х нулей или сдвигу влево на 4 двоичных разряда. Ради удобства адреса обычно записывают в 16-ричной системе счисления.При этом умножение на 16 16-ричного числа равносильно приписыванию справа одного нуля.

 Пусть сегментный адрес некоторого объекта в памяти 045F ,а смещение равно 0032 ; тогда полный адрес равен

                  045F0

             +     0032

                ------------

                  04622

 Максимальный адрес , по которому можно обратиться к памяти при  такой 2-ступенчатой адресации FFFF0 или 65535 * 16 = 1048560  Если все смещения будут максимальными по 64 кбайта то их  можно иметь до  16.

Процессоры следующих поколений имели уже более длинные регистры  для хранения адресов- 80286 использует 24 битовые и позволяет  адресовать напрямую до 16 мегабайтов а 80386 имеет 32-битовые  регистры и может адресовать до 4 млрд байт.  Таким образом в компьютерах с 32-битовыми регистрами практически  отпала необходимость в сегментации памяти для обеспечения  "дальности" адресации . Но сегментация сохранилась по другой  причине - оказывается удобно хранить например ходы программных  команд и обрабатываемые данные в различных сегментах - так  меньше вероятность запортить программный код при изменении  данных в памяти и легче отлаживать программы . Так и живут  программы и данные как в многоквартирном доме - в одной и  той же памяти но в различных сегментах - кроме отдельных  малогабаритных программ с размещением данных и команд в одном  единственном сегменте .


Числовое кодирование нечисловой информации
ЧИСЛОВОЕ КОДИРОВАНИЕ НЕЧИСЛОВЫХ ДАННЫХ

Вы слышали,что компьютеры используются для обработки информации любого вида и происхождения , а из предыдущего материала вытекает, что хранить в его памяти и обрабатывать можно только числовые коды да еще и в непривычной двоичной системе счисления. Как же быть с нечисловыми данными? Можно ли использовать  машины ,созданные для обработки чисел, в задачах обработки нечисловых данных?  Ответ прост:можно -  после того, как эти произвольного типа данные будут переведены в числовую форму (закодированы). Так всегда и поступают - при занесении в память машины любые данные перево дятся в чиловую форму - кодируются ,в этом числовом виде хранятся , передаются по каналам связи и обрабатываются , а при необходимости подачи их на органы чувств человека -декодиру ются -то есть переводятся в привычную для восприятия форму.  Наиболее "нагруженными",чаще используемыми каналами приема информации у человека являются зрение и слух. Это и понятно - прием информации по этим каналам не требует непосредственного контакта с источником информации, здесь используется дистанционная передача с помощью световых волн и звуковых колебаний . Осязание и вкус требуют контакта с источником информации и таят в себе опасность обжечься , уколоться или отравиться и используются человеком с осторожностью.  Поэтому и компьютеры оснащаются в первую очередь устройствами ввода-вывода аудио-визуальной информации .  Для ввода в компьютер изображений (картин ,рисунков ,чертежей ) используют сканеры -устройства на базе телевизионных передающих камер. При записи изображений в память машины они разбиваются на маленькие прямоугольные фрагменты под названием ПИКСЕЛЫ проиходящем от английского PICTURE ELEMENT - ЭЛЕМЕНТ ИЗОБРАЖЕНИЯ Цвет каждого такого элемента-пиксела кодируется целым числом в двоичной системе счисления и заносится в память машины.  Система кодирования цветов обычно очень проста: скажем числа длиной в байт (8 двоичных разрядов или битов) могут исполь- зоваться для кодирования 2^8=256 цветов

     Цвет        Двоичный код цвета      Десятичный эквивалент

   Черный           00000000                    0

   Синий             00000010                    2

   Зеленый         00000011                    3

   Красный         00000100                    4

   Серый            00000111                    7

   Желтый         00001110                    14

   Белый            00001111                    15

    и т д

 Хранящиеся в памяти машины видеокадры в виде последовательности  двоичных чисел при необходимости могут быть отображены на  цветном печатающем устройстве.Если это например цветной  струйный принтер то код цвета каждого пиксела преобразуется в  команду подачи чернил соответствующего цвета.Если же видеокадр  выдается на экран телевизионной электронно-лучевой трубки, то  коды цветов пикселов преобразуются в команды управления  цветовыми лучами.  Для ввода в память компьютера звуковой  (аудио) информации (музыкальных произведений или бабушкиных  сказок) используют аналого-цифровые пребразователи колебаний  электрического напряжения (в которые обычно преобразуются  воспринимаемые например чеерез микрофон звуковые колебания).  Такой преобразователь с большой частотой измеряет напряжение на  своем входе и переводит его в 2-ичный числовой код , заносимый  в машинную память.При воспроизведении звуков числовые коды с  помщью цифро-аналоговых преобразователей преобразуются в  электрические напряжения и подаются на электродинамик или  другое устройство воспроизведения.  Чем выше частота измерения  напряжения в аналого-цифровом преобразователе, тем меньше  искажения при последующем воспроизведении.


Числовое кодирование текстов
ЧИСЛОВОЕ КОДИРОВАНИЕ ТЕКСТОВОЙ ИНФОРМАЦИИ

 Мы выделили вопрсы кодирования текстов в отдельный раздел, так  как они имеют ключевое значение в понимании средств общения  человека с компьютером.  Со времени изобретения письменности и  книгопечатания текстовый способ представления информации занял  ведущее место в информационном обеспечении специалистов в любой  сфере деятельности - для вас в частности тексты учебников  являются основным источником знаний и формирования новых  понятий.Не могли обойти этот способ подачи информации и  компьютерные технологии.Вы уже догадываетесь,что тексты  вводятся в память машины, хранятся и обрабатываются тоже в виде  последовательностей двоичных числовых кодов.  Рассмотрим подробнее как именно это происходит.  Для ввода текстов в память компьютера можно разумеется использовать тот же спсоб, что и для видеоизображений- положить текст перед передающей трубкой и закодировать каждую страницу последовательностью цветов видеокадра.  Однако в таком виде тексты можно будет только воспроиз- вести на устройстве отображения, а вот обрабатывать их (дополнять, корректировать) будет трудно.Кроме того такой способ крайне неэкономичен по расходу памяти - для воспроизведения рисунка каждой буквы в тексте придется разбить его на несколько десятков элементов и хранить цвет каждого из них в виде числа.Тем не менее этот метод используют, но только в комплекте с программами распознавания образов.Эти программы анализируют видеокадр текста как рисунок ,выделяют в нем рисунки букв и кодируют их более экономичным способом , который используется при вводе текстов с клавиатуры компьютера.  Вот этот способ мы и рассмотрим подробнее.  В самом простейшем случае можно бы перенумеровать все буквы алфавита и эти номера считать их числовыми кодами Так и поступили когда-то при создании телеграфных аппаратов:  договорились отвести для кодирования букв 5-тибитовую длину двоичного числа;при этом можно закодировать 2^5=32 буквы - как раз хватает на русский алфавит.Правда в тексте телеграмм приходилось знаки препинания писать словами- зпт,тчк и пр,так как для их кодирования уже недоставало комбинаций единиц и нулей в 5-битовых кодовых группах.  В английском алфавите всего 26 букв и на английском языке оставалось еще 6 кодовых групп для кодирования например знаков препинания.  В современных компьютерах для кодирования текстовых символов отводится 8-битовая группа - байт,позволяющий записать 2^8=256 различных кодов.Это уже кажется избыточным ,но только кажется.  Обычно в  текстах могут встречаться символы различных алфавитов,например кириллицы и латинского ;добавьте сюда 10 цифровых символов,все знаки препинания ,различные скобки , знаки арифметических операций,необходимость строчных и заглавных букв ,знаки кавычек и процентов ,специальные символы типа ~ или $,#,@,& ,символы для рисования таблиц -оказывается наша потребность в различных текстовых символах достаточно велика.  Кроме всего перечисленного при выводе текстов на устройства отображения ( печатающих устройствах, экране дисплея) текстов нам нужен целый набор управляющих кодов например перевода строки , возврата каретки, горизонтальной и вертикальной табуляции, подачи звукового сигнала и пр.Эти коды назвали кодами "неотобржаемых символов" и в американском национальном стандарте символьной информации ASCII (используемом во всех странах) отвели для них первые 32 номера от 0 до 31. Этот стандарт охватывает в целом только 128 символов -половину из 256 возможных в пределах байта кодов:


Основная таблица ASCII-кодов символов
ОСНОВНАЯ ЧАСТЬ ТАБЛИЦЫ ASCII-КОДОВ

Десят.  Шестнадц.  Восьмеp.  ASCII   IBM GRA.      |

 X10      X16         X8                   симв.      Клавиша  |

---------------------------------------------------------------

  0       00          00      NUL     (nul)            <Ctrl-@>

  1       01          01      SOH       _              <Ctrl-A>

  2       02          02      STX        _              <Ctrl-B>

  3       03          03      ETX        _              <Ctrl-C>

  4       04          04      EOT        _              <Ctrl-D>

  5       05          05      ENQ        _              <Ctrl-E>

  6       06          06      ACK        _              <Ctrl-F>

  7       07          07      BEL        _              <Ctrl-G>

  8       08          10      BS          _              <Ctrl-H>

  9       09          11      HT                           <Ctrl-I>

 10       0A          12      LF                          <Ctrl-J>

 11       0B          13      VT                         <Ctrl-K>

 12       0C          14      FF                         <Ctrl-L>

 13       0D          15      CR                        <Ctrl-M>

 14       0E          16      SO                        <Ctrl-N>

 15       0F          17      SI          ¤              <Ctrl-O>

 16       10          20      DLE       _              <Ctrl-P>

 17       11          21      DC1       _              <Ctrl-Q>

 18       12          22      DC2       _              <Ctrl-R>

 19       13          23      DC3       _              <Ctrl-S>

 20       14          24      DC4       ¶              <Ctrl-T>

 21       15          25      NAK       §              <Ctrl-U>

 22       16          26      SYN       +              <Ctrl-V>

 23       17          27      ETB       _              <Ctrl-W>

 24       18          30      CAN      _              <Ctrl-X>

 25       19          31      EM        _              <Ctrl-Y>

 26       1A          32      SUB                      <Ctrl-Z>

 27       1B          33      ESC      _              <Esc>

 28       1C          34      FS        _              <Ctrl-\>

 29       1D          35      GS        _              <Ctrl-'>

 30       1E          36      RS                     <Ctrl-=>

 31       1F          37      US                      <Ctrl-->

 32       20          40      SP    (пpобел)      <SPACE BAR>

 33       21          41      !                  !        ! (воскл.знак)

 34       22          42      "                             (кавычки)

 35       23          43      #                           решетка

 36       24          44      $                        (знак доллаpа)

 37       25          45      %                        % (пpоцент)

 38       26          46      &                        & (ампеpсанд)

 39       27          47      '                        '   (апостpоф)

 40       28          50      (                          (откpыв.скобка)

 41       29          51      )                          (закpыв.скобка)

 42       2A          52      *                    (звездочка)

 43       2В          53      +                      + (плюс)

 44       2C          54      ,                    (запятая)

 45       2D          55      -         -           - (минус)

 46       2E          56      .         .           . (точка)

 47       2F          57      /                       (слэш)

 48       30          60      0         0               0

 49       31          61      1         1               1

 50       32          62      2         2               2

 51       33          63      3         3               3

 52       34          64      4         4               4

 53       35          65      5         5               5

 54       36          66      6         6               6

 55       37          67      7         7               7

 56       38          70      8         8               8

 57       39          71      9         9               9

 58       3A          72      :         :       :     (двоеточие)

 59       3B          73      ;         ;   ;        (точка с зпт.)

 60       3C          74      <         <          < (меньше)

 61       3D          75      =         =           = (pавно)

 62       3E          76      >         >          > (больше)

 63       3F          77      ?         ?                ?

 64       40         100      @         @           @

 65       41         101      A         A             A

 66       42         102      B         B             B

 67       43         103      C         C             C

 68       44         104      D         D             D

 69       45         105      E         E             E

 70       46         106      F         F              F

 71       47         107      G         G            G

 72       48         110      H         H             H

 73       49         111      I           I               I

 74       4A         112      J         J              J

 75       4B         113      K         K             K

 76       4C         114      L         L              L

 77       4D         115      M        M             M

 78       4E         116      N         N             N

 79       4F         117      O         O             O

 80       50         120      P          P             P

 81       51         121      Q         Q             Q

 82       52         122      R         R             R

 83       53         123      S         S             S

 84       54         124      T         T             T

 85       55         125      U         U             U

 86       56         126      V         V             V

 87       57         127      W        W             W

 88       58         130      X         X             X

 89       59         131      Y         Y             Y

 90       5A         132      Z         Z             Z

 91       5B         133      [         [               [

 92       5C         134      \         \     \ (обpатный слэш)

 93       5D         135      ]         ]                ]

 94       5E         136      ^         ^              ^

 95       5F         137      _         _     _ (подчеpкивание)

 96       60         140      '         '           ' (акцент)

 97       61         141      a         a             a

 98       62         142      b         b             b

 99       63         143      c         c             c

100       64         144      d        d             d

101       65         145      e        e             e

102       66         156      f         f             f

103       67         157      g        g            g

104       68         150      h        h            h

105       69         151      i          i             i

106       6A         152      j          j            j

107       6B         153      k        k           k

108       6C         154      l         l            l

109       6D         155      m     m           m

110       6E         156      n       n            n

111       6F         157      o       o            o

112       70         160      p       p            p

113       71         161      q       q            q

114       72         162      r        r             r

115       73         163      s       s             s

116       74         164      t        t              t

117       75         165      u       u             u

118       76         166      v       v             v

119       77         167      w      w            w

120       78         170      x       x             x

121       79         171      y       y              y

122       7A         172      z      z              z

123       7B         173      {       {               {

124       7C         174      |       |   | (логическ.ИЛИ)

125       7D         175      }       }              }

126       7E         176      ~     ~              ~тильда

127       7F         177      DEL       DEL            <Del>

Обратите ,пожалуйста , внимание на то ,что цифры от 0 до 9 кодированы числами от 48 до  57 (от 30 до 39 в 16-ричной записи) и усвойте , что цифра - это символ (буква) для обозначения числа , а код этой буквы не обязательно равен обозначаемому числу!


Дополнительная таблица кодов символов
ДОПОЛНИТЕЛЬНАЯ ТАБЛИЦА СИМВОЛЬНОГО КОДА ASCII

Двоичный    Восьмеpичн.   Десятичн.   Шестнадцатеp.   Дополнит

   Х2           X8          X10           X16          ASCII

--------------------------------------------------------------

1000 0000      200          128           80                 Б

1000 0001      201          129           81                 Б

1000 0010      202          130           82                 В

1000 0011      203          131           83                 Г

1000 0100      204          132           84                 Д

1000 0101      205          133           85                 Е

1000 0110      206          134           86                 Ж

1000 0111      207          135           87                 З

1000 1000      210          136           88                 И

1000 1001      211          137           89                 Й

1000 1010      212          138           8A                 К

1000 1011      213          139           8B                 Л

1000 1100      214          140           8C                 М

1000 1101      215          141           8D                 Н

1000 1110      216          142           8E                 О

1000 1111      217          143           8F                 П

1001 0000      220          144           90                 Р

1001 0001      221          145           91                 С

1001 0010      222          146           92                 Т

1001 0011      223          147           93                 У

1001 0100      224          148           94                 Ф

1001 0101      225          149           95                 Х

1001 0110      226          150           96                 Ц

1001 0111      227          151           97                 Ч

1001 1000      230          152           98                 Ш

1001 1001      231          153           99                 Щ

1001 1010      232          154           9A                 Ъ

1001 1011      233          155           9B                 Ы

1001 1100      234          156           9C                 Ь

1001 1101      235          157           9D                 Э

1001 1110      236          158           9E                 Ю

1001 1111      237          159           9F                 Я

1010 0000      240          160           A0                 а

1010 0001      241          161           A1                 б

1010 0010      242          162           A2                 в

1010 0011      243          163           A3                 г

1010 0100      244          164           A4                 д

1010 0101      245          165           A5                 е

1010 0110      246          166           A6                 ж

1010 0111      247          167           A7                 з

1010 1000      250          168           A8                 и

1010 1001      251          169           A9                 й

1010 1010      252          170           AA                 к

1010 1011      253          171           AB                 л

1010 1100      254          172           AC                 м

1010 1101      255          173           AD                 н

1010 1110      256          174           AE                 о

1010 1111      257          175           AF                 п

1011 0000      260          176           B0

1011 0001      261          177           B1

1011 0010      262          178           B2

1011 0011      263          179           B3                 ¦

1011 0100      264          180           B4                 ¦

1011 0101      265          181           B5                 ¦

1011 0110      266          182           B6                 ¦

1011 0111      267          183           B7                 +

1011 1000      270          184           B8                 +

1011 1001      271          185           B9                 ¦

1011 1010      272          186           BA                 ¦

1011 1011      273          187           BB                 +

1011 1100      274          188           BC                 +

1011 1101      275          189           BD                 +

1011 1110      276          190           BE                 +

1011 1111      277          191           BF                 +

1100 0000      300          192           C0                 +

1100 0001      301          193           C1                 -

1100 0010      302          194           C2                 -

1100 0011      303          195           C3                 +

1100 0100      304          196           C4                 -

1100 0101      305          197           C5                 +

1100 0110      306          198           C6                 ¦

1100 0111      307          199           C7                 ¦

1100 1000      310          200           C8                 +

1100 1001      311          201           C9                 +

1100 1010      312          202           CA                 -

1100 1011      313          203           CB                 -

1100 1100      314          204           CC                 ¦

1100 1101      315          205           CD                 -

1100 1110      316          206           CE                 +

1100 1111      317          207           CF                 -

1101 0000      320          208           D0                 -

1101 0001      321          209           D1                 -

1101 0010      322          210           D2                 -

1101 0011      323          211           D3                 +

1101 0100      324          212           D4                 +

1101 0101      325          213           D5                 +

1101 0110      326          214           D6                 +

1101 0111      327          215           D7                 +

1101 1000      330          216           D8                 +

1101 1001      331          217           D9                 +

1101 1010      332          218           DA                 +

1101 1011      333          219           DB                 _

1101 1100      334          220           DC                 _

1101 1101      335          221           DD                 _

1101 1110      336          222           DE                 _

1101 1111      337          223           DF                 _

1110 0000      340          224           E0                 р

1110 0001      341          225           E1                 с

1110 0010      342          226           E2                 т

1110 0011      343          227           E3                 у

1110 0100      344          228           E4                 ф

1110 0101      345          229           E5                 х

1110 0110      346          230           E6                 ц

1110 0111      347          231           E7                 ч

1110 1000      350          232           E8                 ш

1110 1001      351          233           E9                 щ

1110 1010      352          234           EA                 ъ

1110 1011      353          235           EB                 ы

1110 1100      354          236           EC                 ь

1110 1101      355          237           ED                 э

1110 1110      356          238           EE                 ю

1110 1111      357          239           EF                 я

1111 0000      360          240           F0                 Ё

1111 0001      361          241           F1                 ё

1111 0010      362          242           F2                 Є

1111 0011      363          243           F3                 є

1111 0100      364          244           F4                 Ї

1111 0101      365          245           F5                 ї

1111 0110      366          246           F6                 Ў

1111 0111      367          247           F7                 ў

1111 1000      370          248           F8                 °

1111 1001      371          249           F9                 ·

1111 1010      372          250           FA                 ·

1111 1011      373          251           FB                 _

1111 1100      374          252           FC                 №

1111 1101      375          253           FD                 ¤

1111 1110      376          254           FE                 _

1111 1111      377          255           FF                 _

Вторая половина кодовых групп ,начиная с 128 до 255 , не стандартизована и в ней кодируются различные символы оформления текстов (например для рисования таблиц).В этой же области числового кодирования располагаются символы национальных алфавитов в том числе и наша родная кириллица.У кириллицы нет "постоянной прописки" в кодовой таблице ,вернее есть несколько вариантов таблиц кодирования.В одной кодировке коды букв кириллицы кодируются числами от 128 до 191  подряд сначала прписные от А до Я ,затем строчные (болгарская кодировка),в другой (основная) - от 176 до 239, в третьей (ANSI)-от 192 до 255 и другие варианты, например так называемая альтернативная кодировка, в которой коды символов кириллицы имеют разрыв в последовательности - от 128 до 175 и далее от 224 до 239 (строчные от "р" до "я").


Коды изображений символов
КОДЫ ИЗОБРАЖЕНИЙ ТЕКСТОВЫХ СИМВОЛОВ

Вы скажете - хорошо, вместо букв в памяти компьютера хранятся соответствующие им числа да еще и в двоичном формате,но ведь на экране и печатающем устройстве мы видим привычные изображения букв и цифр.Да , но чтобы достичь этого , приходится применять многоступенчатое кодирование. Прежде всего для того ,чтобы рису- нок буквы был виден на экране его цвет должен отличаться от цвета фона , на котором он изображается. Поэтому рассмотренные нами коды символов (порядковые номера в таблице кодирования) необходимо дополнить кодами цвета фона и цвета рисунков. Для этих кодов цветов добавили еще 1 байт памяти и разделили его пополам - младшую (левую) половину из 4-х битов отвели для кодирования цвета рисунка , а старшую для кодирования цвета фона. Этот байт назвали БАЙТОМ АТРИБУТОВ СИМВОЛА и он всегда присутствует вместе с кодом самого символа в 2-х байтовых кодах символов. В 4-х битах можно закодировать  16 цветов, а при необ- ходимости кодирования большего количества цветов применяют много- ступенчатую систему кодирования. Содержимое байта атрибутов удобно записывать в 16-ричном формате ,у которого первая 16-ричная цифра в этом случае обозначает цвет фона , а вторая - цвет рисунка символа. Например 16-ричное число 4E кодирует желтые (код желтого цвета Е или 14 в 10-й системе) буквы на красном (код красного цвета 4) фоне.

2--байтовые кодовые группы каждой буквы текста ,содержащие код символа и код  атрибутов его изображения для вывода на экран записываются в память устройства управления ,которое называют дисплейным адаптером , а саму память - ВИДЕОПАМЯТЬЮ , ВИДЕОБУФЕРОМ или БУФЕРОМ РЕГЕНЕРАЦИИ.     Последнее название подсказывает нам, что для постоянного обновления изображения на экране из этого буфера с частотой примерно 50 раз в секунду считываются коды символов и преобразуются в рисунки букв на  экране.  Чтобы такое преобразование стало возможным приходится закодировать и разместить в памяти компьютера и сами рисунуи букв. Вспомним , что изображение чего бы-то ни было на экране состоит из элемен- тарных изображений - пикселов. Для изображения символов обычно отводится в зависимости от типа видеоситемы от 8 до 14 строк по 8 пикселов в строке. О каждом пикселе в изображении символа дисплейный адаптер должен знать - относится он к фону или рисунку - то есть достаточно одного бита с 2-мя состояниями. Договорились, что если бит содержит 0 , то это пиксел фона ,а если 1- то это рисуноковый пиксел. В этом случае рисунок буквы "Н" из 8-ми строк по 8 пикселов в строке будет закодирован так :

     В 2-ичных кодах  В 16-ричной записи      В 10-тичной записи

        01000010            42                      66

        01000010            42                      66

        01000010            42                      66

        01111110            7Е                      126

        01111110            7Е                      126

        01000010            42                      66

        01000010            42                      66

        01000010            42                      66

Такие 8-мибайтовые (или 12-тибайтовые,или 14-байтовые) кодовые группы кодов хранятся в памяти для рисунков всех изображаемых символов. Адаптер дисплея "узнает" адрес области памяти изображений , берет из видеопамяти код символа ,означающий его порядковый номер , вычисляет адрес изображения для этого кода ,затем выводит на экран пикселы с 0-м битом цветом фона ,а пикселы с 1-м битом цветом рисунка (коды цвета фона и рисунка он тоже берет из видеопамяти из байта атрибутов) .Вот так появляются на экране дисплея рисунки букв ,тоже ,как и все в компьютере закодированные 2-ичными числами. Очень похожа картина при выводе изображений символов на печать ,только коды изображений символов и их порядковые номера хранятся в памяти печатающего устройства либо постоянно либо заносятся туда из памяти компью- тера перед началом печати.


КОДИРОВАНИЕ  ИНФОРМАЦИИ В ПРОЦЕССАХ ОБМЕНА

 Клавиатура должна передать свои скэн-коды в центральный процессор , ASCII-коды необходимо переслать в видеопамять, коды программ и различных данных необходимо передать на магнитные диски для длительного хранения при выключенном питании компьютера или нехватке оперативной памяти при включеном - это лишь малая часть потребности во внутри- компьютерных пересылках информации.  Кроме того компьютеры через специальные устройства (порты , сетевые адаптеры и каналы связи) связываются с другими компьютерами ,расположенными когда сравнительно недалеко ,а часто на другом континенте земли или на космичеких аппаратах.  Во всех этих задачах передачи-приема сообщения приходится кодировать и в большинстве случаев используют уже рассмотренные нами двоичные числовые коды.Одно и то же сообщение можно закодировать различными способами и при этом возникает вопрос о наивыгоднейших (оптимальных) способах кодирования. Можно например считать наилучшим такой код ,при котором на передачу сообщений затрачивается минимальное время .Если на передачу элемента сообщения - двоичного числа 0 или 1 -тратится одно и то же время ,то оптимальным будет такой код ,который на передачу сообщения заданной длины требует наименьшего количества элементов (нулей и единиц).  Пусть нам нужно закодировать  двоичным кодом буквы русской азбуки так ,чтобы каждой букве соответствовала уникальная комбинация единиц и нулей и чтобы среднее количество этих элементов на одну букву текста было минимальным.  В алфавите 32 буквы + пробел между словами. Простейшее решение - перенумеровать их от 0 до 31 с записью в 2-ичной системе счисления. Договоримся для простоты не различать например "ь" ,чтобы вместе с пробелом число кодируемых букв было равно ближайшей степени двойки - числу 2^5=32.  Тогда каждая буква может отображена пятизначным двоичным числом:

                 а  ~ 00000

                 б ~  00001

                 в  ~ 00010

                 г  ~ 00011

                 я  ~ 11111

При таком способе на каждую букву тратится по 5 элементов. Подумаем , нельзя ли составить код, в котором в среднем на 1 букву будет расходоваться меньшее количество элементов кодирования . Для этого проанализируем с какой частотой в русском тексте всречаются различные буквы и попытаемся часто встречающиеся буквы кодировать меньшим числом элементов а реже встречающиеся - большим.  Относительные частоты букв в тексте в  порядке убывания, определенные по множеству различных текстов от романов Льва Толстого до постановлений правительства вместе с получен- ными кодами выглядят так:

   Буква   Частота  Код                                    Буква      Частота     Код

пробел -   0.145      000                                         я    -      0.019     110110

     о - 0.095    001                                                   ы -         0.016    110111

     е - 0.074   0100                                                  з -          0.015    111000

     а - 0.064   0101                                                 ь,ъ       - 0.015   111001

     и - 0.064   0110                                                  б         - 0.015   111010

     т - 0.056   0111                                                   г            0.014     111011

     н - 0.056   1000                                                  ч          - 0.013     111100

     с - 0.047   1001                                                  й -          0.010    1111010

     р - 0.041  10100                                                 х          - 0.009     1111011

     в - 0.039  10101                                                 ж -          0.008     1111100

     л - 0.036  10110                                                 ю          - 0.007     1111101

     к - 0.029  10111                                                 ш -          0.006     11111100

     м - 0.026  11000                                                ц -           0.004     11111101

     д - 0.026  110010                                              щ -          0.003     11111110

     п - 0.024  110011                                               э            - 0.003    111111110

     у - 0.021  110100                                              ф -           0.002    111111111

 Максимальная информация, которую передает 1 элемент сообщения,  равна 1 биту (1 и 0 равновероятны).Поэтому в основу нашего  оптимального кодирования ляжет требование, чтобы все элементы  кодов в тексте встречались одинаково часто.  Для этого кодируемые буквы разделим на 2 равновероятные группы :  в первой группе на первом месте кодовой комбинации ставим 0 а для  второй - 1. Далее каждую группу снова разделим на 2 примерно  равновероятные подгруппы и для символов первой на втором месте  поставим 0 а для второй подгруппы - 1 и т д Результат кодирования  помещен в ту же таблицу где записаны частоты букв.  Способ кодирования носит название кодов ШЕННОНА_ФЭНО.  С помощью этой таблицы можно закодировать и декодировать любое  сообщение, например фраза "теория информации" будет выглядеть  после кодирования так :

011101000011010001101100000110100011111111100110

1001100001011111110101100110

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

Чтобы убедиться декодируйте с помощью таблицы фразу:

1001110011001100100111101000010111001110010011010

10000110101010110000110110110

("способ кодирования").

Средняя информация на одну букву :

       Сумма от 1 до 32 Pi*logPi = 4.42

а среднее число элементов кодирования (нулей и единиц) на букву :

       Сумма от 0 до 32  Длина кода*частоту =4.45

Информация на один элемент кодирования

          4.42/4.45 = 0.994

близка к своему верхнему пределу -1 и выбранный код можно считать оптимальным по выбранному критерию "что такое хорошо". При кодировании 5-ю битами на каждый символ без всей этой мороки мы имели бы 4.42/5 = 0.884 бита на бит - заметно меньше. Но побуквенное кодирование вообще неэкономично - целесообразно кодировать целые  часто встречающиеся сочетания букв "тся","ает", "ние" и т п

    ИЗБЫТОЧНЫЕ  КОДЫ ХЭММИНГА.

В технических  системах,не  обладающих  человеческой способностью восстановления искажений   при передаче,используют   сознательно избыточное кодирование,когда на каждое сообщение расходуется больше разрядов,чем необходимый минимум;делают это для  обеспечения воз- можности алгоритмического  решения  задач  по обнаружению ошибок в передаче и их исправления на приемном конце канала связи.  Рассмотрим передачу 4-х равновероятных сообщений A,B,C и D.  При неизбыточном кодировании понадобится 2 двоичных разряда и  ко- довая таблица будет содержать 4 комбинации:

                               00,01,10,11

каждая из которых может отождествляться с одним из сообщений.  Кодовые группы сообщений будут отличаться друг от друга  только  в 1-ом двоичном разряде и случайная замена 0 на 1 или 1 на 0 приведет к необнаружимой ошибке на приемной стороне:

    Сообщение     Код        Ошибка         Принятое сообщение

     C            01          11                   D

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

Если к  2-м  обязательным  разрядам  добавим  еще 3 избыточных так ,чтобы кодовые группы отличались не менее чем в  3-х  разрядах,то одиночные ошибки  могут  быть исправлены на  приемном  конце без повтора передачи:

                   00000     10101     01011     11110

При сбое  в одном   разряде   образуется   недозволенная кодовая группа,например, передано 01011 а принято 00011-тогда исправлением только одного разряда можно получить только одну зарегистрированную группу -код  символа С,остальные  элементы  кодовой таблицы могут быть получены исправлением не менее 2-х  разрядов.Для  исправления ошибок в   2-х   и   более  разрядах  необходимы  коды  с  большей избыточностью.Эти методды  основаны  на  работах Р.В.Хемминга   и получаемые коды называют _кодами Хемминга. Минимальное число различимых разрядов в любой  паре  кодовых слов заданного перечня называют _расстоянием Хемминга.


Основные типы приемо-передающих устройств
ОСНОВНЫЕ ТИПЫ ПРИЕМО-ПЕРЕДАЮЩИХ  УСТРОЙСТВ.

Передаваемые и принимаемые комплекты данных представляют собой как правило последовательности кодовых групп достаточно большой дллины -это обычно не одиночные биты,а мощные множества битов.Такие множе- ства битов могут быть представлены либо одновременно с помощью мно- жествва физических соответствий-в этом случае мы имеем параллельную истему,в которой для каждого бита  отводится  свой отдельный ка- нал (проводник),либо с    помощью    только одного физического соответствия ,при котором двоичные разряды даны в заранее  установ- ленной последовательности-это будет последовательная система.  В параллельных системах каждый разряд и соответствующий ему провод- ник имеют  оговоренный вес-численное значение,величина,показывающая на какую степень двойки необходимо умножить  передаваемый  двоичный знак,чтобы получить  выражение  или  число в десятичной системе.Для параллельной системы необходимо  столько каналов,сколько  2-ичных разрядов она должна передавать.   Последовательная система использует только 1  канал независимо  от количества передаваемых  разрядов-двоичные знаки  разносятся  не в пространстве,а во   времени   и значение (вес)   двоичного знака определяется моментом времени в который он поступает,его местом во временной последовательности.  В последовательных системах существует 2 основных метода определения веса двоичных разрядов:  в _синхронных системах _.имеется  центральный тактовый генератор,настраивающий приемник и передатчик на работу с очередным разрядом; _в асинхронных системах _используются стартовые и стоповые сигналы  (биты)  для  определения начала  и конца кодовых групп.Для небольших систем синхронный режим позволяет получить  ряд преимуществ в схемных решениях.В более сложных системах,где времена пробега сигналов между блоками велики и неконтролируемы , используют асинхронные режимы.  Последовательные системы дают выигрыш в количестве каналов связи  и проигрывают в быстродействии.Важным преимуществом последовательных систем является меньшая вероятность ошибок и сравнительная легкость их обнаружения.  Компромисс между достоинствами   и недостатками   обеих   систем достигается созданием смешанных последовательно-параллельных систем Например для передачи символов алфавита со всеми знаками пунктуации необходимо 8 2-ичных разрядов с заранее присвоенными им весами 1,2, 4,8,...128.Используя 8 физических каналов можно посимаольно переда- вать тексты произвольной длины-такой метод и используется при обмене данными между системным блоком компьютера  и  присоединенными  к нему устройствами (памятью, принтером, дисплеем и пр.).  При последовательной передаче 8-битовый код символа разворачивается в побитовую  последовательность,предваряемую  стартовым битом ,битом  контроля по четности и завершаемую стоп-битом.Обратная  сборка символа в  8-мибитовую кодовую группу  осуществляется  на  приемном  конце канала  связи  специальным устройством.


ЛОГИЧЕСКИЕ СВЯЗИ МЕЖДУ КОДОВЫМИ ГРУППАМИ

    Мефистофель из "Фауста" И.В.Гете советовал :

    "Сперва хочу вам в долг вменить

    На курсы логики ходить.

    Ваш ум, не тронутый доныне,

    На них приучат к дисциплине,

    Чтоб взял он направленья ось,

    Не разбредаясь вкривь и вкось"

Последуем этому указанию .

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

                    Y=X1 <операция> X2

 Следуя уже сложившейся привычке закодируем "да" числом 1 а "нет" - числом 0 и предположим что Х1 и Х2 -одноразрядные двоичные числа , каждое из которых тоже может принимать значения 0 или 1. Рассмотрим возможные комбинации значений Х1 и Х2 и результатов У и сведем их в таблицу ,дав каждой комбинации аргументов и результатов соответствующее название операции и ее обозначение:

                         Название     Обозначение    Пример

                         операции      операции           записи

  ____________________________________________________________

  X1 =   0 1 0 1

  X2 =   0 0 1 1

  ------------------

  Y  =   0 0 0 1      "И","AND",           &             Y = X1 & X2

                           Конъюнкция

  Y  =   0 1 1 1      "ИЛИ","OR",          |              Y = X1 | X2

                             Дизъюнкция

  Y  =   0 1 1 0    "Исключающее ИЛИ",    ^     Y = X1 ^ X2

                            "XOR",

                          "  Строгая дизъюнкция"

Если для каждой из 4-х возможных комбинаций Х1 и Х2  00,10,01,11 вносить в таблицу по 2 возможных значения У то всего получим 16 возможных 2-местных (c 2-мя операндами) логических операций. Мы добавим пока только одну унарную операцию отрицания (инверсии), меняющей в своем единственном операнде 0 на 1 и 1 на 0. Ее названия - "НЕ","NOT",обозначать будем тильдой ~. Отметим попутно ,что в литературе вы встретите различные обозначения рассмотренных нами операций - это просто вопрос договоренности. Например конъюнкцию и дизъюнкцию часто обозначают уголками с острием вверх и вниз и пр. Теперь попробуем использовать эти операции например для сравнения двух кодовых групп длиной скажем 32 бита; Это некторые числа , мы обозначим их через Х1 и Х2 и применим к ним операцию исклю- чающего ИЛИ :

                 У = Х1 ^ Х2

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

                  Y = (~X1) & X2

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

1)строгую дизъюнкцию можно выразить через дизъюнкцию ,конъюнкцию и

отрицание

         Y= (X1 ^ X2 ) = (X1 & ~X2) | (~X1 & X2 )

2)конъюнкцию - через дизъюнкцию и отрицание (X1 & X2) = ~(~X1 | ~X2)

3)дизъюнкцию - через конъюнкцию и отрицание  (X1 | X2) = ~(~X1 & ~X2)

и т д

Эта возможность выражения одних логических операций через другие используется для создания так называемых "нормальных форм". В  частности если в 1) принять

                  a=(X1 & ~X2)

                  b=(~X1 & X2 )

                  Y=a | b

получаем дизъюнктивную нормальную форму, в которой результат определя-

ется операцией дизъюнкции к предварительно преобразованным операндам.

Аналогичено можно образовать конъюнктивную нормальную форму.

Нормальные формы используются при формировании различных функциональных

схем , например двоичного сумматора ,в котором правила суммирования для

каждого двоичного разряда можно описать вначале в табличной форме :

         a =  0 1 0 1 0 1 0 1

         b =  0 0 1 1 0 0 1 1

перенос  c =  0 0 0 0 1 1 1 1

------------------------------

сумма    s =  0 1 1 0 1 0 0 1

перенос  d =  0 0 0 1 0 1 1 1

В формульном виде это выглядит так

s = (a & ~b & ~c) | (~a & b & ~c) |(~a & ~b & c)|(a & b & c)

c = (a & b & ~c)  | (a & ~b & c)  |(~a & b & c) |(a & b & c)

После упрощения методом эквивалентных замен такие формулы

реализуют в форме электрических схем .


Клавиатура
Некоторые сведения о клавиатуре ПК

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

Как правило клавиатура выполнена в виде отдельного блока (кроме портативных компьютеров ,где она встраивается  в систем-ный блок). Внешне вы видите корпус с клавишами. А что находится внутри ? Там есть специализированный  компьютер из одной микросхемы - он отслеживает (сканирует) нажатия клавиш и посылает номер нажатой клавиши в центральный процессор .Этот номер не зависит от того , что потом напишут на поверхности клавиши и называется скэн-кодом или кодом сканирования (scan code).  Так как в программах используются не порядковые номера клавиш, а символьные ASCII -коды, причем одной и той же клавише могут соответствовать различные ASCII-коды ,то необходимо преобразовать скэн-код в сивольный код.Например нажатие клавиши c порядковым номером 31 вызовет  преобразование в код символа s или S в за висимости от того ,была ли нажата одновременно клавиша Shift.  Преобразование скэн-кодов в символьные выполняется с помощью специальных программ ,которые могут находиться либо в постоянном запоминающем устройстве базовой системы управления вводом- выводом, либо загружаться в оперативную память с магнитного диска (для национальных  алфавитов).  При нажатии любой клавиши центральный процессор получает сигнал прерывания и по этому сигналу вызывает программу перекодировки - обработчик клавиатурного прерывания .Стандартный обработчик читает скэн - код нажатой клавиши ,вычисляет ее символьный код с учетом нажатия клавиш Shift и CapsLock (временного или постоянного пере ключения с прописных на строчные и обратно) и записывает его в специальную область  памяти ,называемую БУФЕРОМ КЛАВИАТУРЫ.  Дополнительно отслеживаются нажатия таких комбинаций клавиш как Ctrl-Alt-Del (перезапуск компьютера) ,PrintScreen (печать экрана).  Буфер клавиатуры имеет длину 32 байта ,в нем размещаются коды 15-ти нажатых клавиш по 2 байта для каждого нажатия + одна пара байтов с постоянно записанными кодами возврата каретки и перевода строки (13 и 10). Если код нажатой клавиши соответствует отображаемому символу или он код клавиш Esc,Tab,Enter,BackSpace то он заносится в младший байт, если это код спецклавиш F1-F10 или курсорных клавиш то младший байт остается нулевым а код заносится в старший байт.  Если нажимаются комбинации клавиш с клавишами Alt - формируется РАСШИРЕННЫЙ код с записью в младший байт символьного кода а в  старший - скэн-кода клавиши.  Буфер организован циклически - после заполнения последней пары байтов запись идет снова в первую с потерей ее предыдущего содержимого.При переполнении клавиатурного буфера выдается звуковой сигнал.


Внутрикомпьютерный обмен данными
Процессы обмена данными между устройствами ПК

 Существует несколько способов внутрикомпьютерной организации  приема-передачи команд и данных между отдельными устройствами  Мы кратко познакомимся с технической и организационной  сторонами такого обмена в IBM - совместимых компьютерах. Во  всех электронных машинах физическая передача осуществляется с  помощью электрических сигналов  по проводам .Система  проводников для внутримашинного обмена называется ШИНОЙ .Помимо  чисто технической реализации связи межда устройствами  компьютера необходимо договориться о логической организации - в  какой последовательности и взаимосвязи будут передаваться и  приниматься команды и данные , как будет подтверждаться  готовность к приему , начало и окончание процесса обмена и  многое другое .   Такую договоренность называют

                 ПРОТОКОЛОМ ОБМЕНА

 а совокупность технических и организационных средств называют вместе

                                ИНТЕРФЕЙСОМ.

В персональных компьютерах типа IBM осуществлен интерфейс под названием

                 ОБЩАЯ ШИНА или

               ОБЩАЯ СИСТЕМНАЯ ШИНА

 Если вы снимете кожух системного блока , то увидите большую  печатную плату (ее называют  материнской),на которой  смонтированы микросхемы памяти , центрального процессора ,  базовой системы ввода-вывода , связи с периферией ,обработки  прерываний , и других устройств.  Кроме того вы увидите вставные печатные платы устройств  управления дисплеем ,дисками , плат с портами внешней связи- их  количество зависит от состава вашего компьютера . Они  соединются с печатной платой через разъемы (их еще еазывают  слотами) ,непосредственно соединенные с проводниками системной  шины .Особенностью ннтерфейса "общая шина" является то ,что не  имеет значения в какой из слотов будет вставлена плата  дополнительного устройства - все устройства получают одни и те  же сигналы и осуществляют взаимный обмен по одному и тому же  протоколу . Общая шина состоит из ряда функциональных частей -  шины питания ,шины управления , шины адресов ,шины данных.   Обмен происходит примерно по следующей упрощенной схеме :   Устройство ,желающее что-то предать другому устройству ,   посылает по шине управления "сигнал предупреждения" ,по   которому все устройства приводят в готовность свои дешифраторы   адресов .Затем по шине адреса передается адрес абонента ,   которому адресована предстоящая передача. Все другие   устройства "успокаиваются" ,так как обращаются не к ним , а   адресат ,"поймавший свой адрес" посылает сигнал готовности к   приему (если он действительно готов).После этого   осуществляется сама передача кодовых групп и после получения   сигнала конца передачи все отключаются от шин до следующего   "возмутителя спокойствия " по шине управления. Разумеется все   происходит значительно сложнее ,но сейчас для нас важно только   общее понимание внутрикомпьютерных процессов.  Очевидно , что   скорость этих обменных процессов зависит от того , сколько   битов одновременно будут передаваться по соответствующей шине.   Если например шина адреса состоит из 8-ми проводников , то для   передачи 2-х байтов адреса сегмента и 2-х байтов смещения   придется осуществить 4 кодовых посылки ,при 16-проводной шине   адреса его передачу можно осуществить за 2 посылки и пр   32-проводной - за одну.  Емкости шин различны для различных   поколений компьютеров, что вместе с рабочей частотой   определяет в значительной степени его производительность.


Архитектура центрального процессора
Архитетура центрального процессора ПК

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

ШИННЫЙ ИНТЕРФЕЙС предназначен для подготовки команд для  выполнения операционным устройством и содержит устройство  управления шиной , очередь команд и регистры для сегментных  адресов.  Устройство управления шиной управляет передачей  данных на операционное устройство своего процессора, в  оперативную память и на внешние устройства ввода-вывода.  Очередь команд - это 4 или более однобайтовых регистров памяти.  Нужны они вот для чего.Все команды находятся в оперативной  памяти и шинный интерфейс должен заглядывать вперед и выбирать  команды так ,чтобы всегда существовала непустая очередь  подготовленных к выполнению команд.Операционное устройство  берет команды из очереди команд и пока оно занято выполнением  первой в очереди команды, шинный интерфейс выбирает из памяти  следующую - такая параллельная работа повышает  производительность.  Четыре сегментных регистра служат для  хранения сегментных адресов начала программного кода (регистр  CS), глобальных данных (регистр DS), локальных данных (регистр  сегмента стека SS) и дополнительный сегментный регистр ES.  Таким образом регистры шинного интерфейса - содержат адреса  физических сегментов памяти, отводимой операционной системой  программам для  кода (CS), данных (DS) и стека (SS). Каждый из  этих 16-ти битовых регистров может адресовать не более 64Кбайт  памяти.  Для адресации 1-Мбайтного пространства к 16-битовому  регистру воображаемо присоединяют 4 нулевых бита (физически  отсутству- ющих), что равносильно умножению содержимого на 16 и  дает 64*16=1024 Кбайт, но с округлением адреса сегмента до  16-бай тового.  Здесь мы встретились с незнакомым термином  "стек" -это участок памяти ,организованный с помощью программы  таким образом ,что последние помещаемые в него данные  извлекаются затем первыми.  Этот порядок носит название  "дисциплины LIFO(last in-first out т е последним вошел-первым  вышел)".Такая организация оказывается полезной прежде всего в  тех случаях когда вашей прграмме необходимо "на время  отвлечься" для выполнения специальной работы с возвратом к  команде, следующей за командой "ухода".

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

 ОПЕРАЦИОННОЕ УСТРОЙСТВО предназначено для выполнения команд и  включает указатель команд IP (это по существу адрес-смещение  команды относительно сегментного адреса ,хранящегося в регистре  CS шинного интерфейса), регистры общего назначения и собственно  процессор.  Назначение регистров операционного устройства:

 SP  и BP - регистровые указатели для доступа к данным сегмента стека (SP-для смещения относительно сегментного адреса стека, BP-дополнительный регистр базы,облегчающий доступ к адресам и данным, размещенным в стеке)

 SI и DI - индексные регистры (SI-смещение относительно адреса в DS, DI- то же

      относительно ES )

 IP - указатель команд.

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

 BX - базовый регистр общего назначения для расширенной адресации и вычислений.

 CX - счетчик циклов и операций сдвига.

 DX - регистр данных для операций умножения и деления чисел 2-й длины (в паре с AX-DX:AX) и некоторых операций ввода-вывода.

 Все 4 вышеперечисленные  регистры AX,BX,CX,DX позволяют выполнять операции  и над их старшими (с буквой H) и младшими (с буквой L) однобайтовыми половинками.  В более развитых 32-разрядных процессорах регистры могут разделяться на 2-байтовые половинки .

 СОБСТВЕННО ПРОЦЕССОР состоит из арифметико-логического устройства (АЛУ) ,устройства управления и регистра флагов.

 АЛУ выполняет команды двоичной целочисленной арифметики , побитовые  логические операции и сдвиги над любыми помещенными в регистры  операционного устройства числами.

Биты  р е г и с т р а  ф л а г о в  определяют состояние машины и результаты выполнения команд.

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

 CF устанавливается, если  при выполнении сложения возникает перенос,  а при  выполнении вычитания  - заем  из старшего  бита результата;

 PF устанавливается, если сумма по модулю два младшего байта результата равна нулю;

 АF устанавливается, если  при выполнении сложения возникает перенос,  а  при  выполнении  сложения  -  заем из старшего бита младшей тетрады результата;

 ZF устанавливается, если результат операции равен нулю;

 SF  устанавливается, если  старший бит  результата операции равен нулю;

 Установка бита TF  переводит МП в пошаговый режим,  т. е. в режим,  вызывающий прерывание  Int 01h  после выполнения  каждой команды процессора;

 Установка бита  IF разрешает маскируемые  прерывания работы МП, т. е. прерывания от внешних устройств;

 Если  бит  DF  установлен,  то  строковые команды уменьшают адрес операндов; если он сброшен, то увеличивают;

  OF устанавливается, если  возникает переполнение результата арифметической операции;  иными словами, при  сложении возникает перенос  в старший  бит и   нет переноса  из старшего  бита, или наоборот; при вычитании - возникает  заем из старшего бита и нет заема в старший бит, или наоборот.

 В н и м а н и е ! Старшие и младшие байты слова хранятся в памяти в "обратной" последовательности - младшая часть - по младшему адресу, старшая - постаршему. Об этом следует помнить при ручном заполнении байтов.


Структура команд центрального процессора
Структура команд центрального процессора ПК

Команды выпоняемых программ - это числовые коды действий над  закодированными в числовой форме данными любого происхождения.  Какую информацию должен нести код  процессорной команды?  Прежде всего это код выполняемого действия - поместить  (например в регистр), сложить ,умножить , сдвинуть и т д.  Если  команде для ее выполнения нужен регистр процессора ,то в ее  коде должен присутствовать бит,кодирующий какую часть регистра  использует команда. Этому биту присвоим условное имя "w" и он  имеет значение 0 если используется пол-регистра и 1 если  используется регистр полной длины в слово. Кроме того ,  необходим в составе такой команды и код конкретного регистра.  Так как всего регистров общего назначения 8( в  рассматриваемом  нами процессоре 8086) то для их кодирования  достаточно 3-х  битов . Эту тройку битов называют именем "reg" и она имеет  следующее содержание :

          Биты      при w=0     при w=1

           000          AL           AX

           001          CL           CX

           010          DL           DX

           011          BL           BX

           100          AH           SP

           101          CH           BP

           110          DH           SI

           111          BH           DI

 Этот 3-битоывй указатель регистра может присутствовать либо  непосредственно в байте кода действия либо в специальном байте  способа адресации (он может быть или не быть в коде клманды)  Если байт способа адресации в команде присутствует , то помимо  указателя регистра он содержит 2-битовый элемент mod (имеющий  значение 11 при ссылке на регистр и 00,01 или 10 при ссылке  на память) b 3-битовый указатель регистра или памяти r/m для  описания второго операнда команд.  Кроме того первый байт кода команды может содержать бит "d",  указывающий, какие операнды коменды описывают mod и reg (какой  относится к первому операнду команды а какой ко второму)

 Используются команды длиной от 1 до 6 байтов.

 Приведем примеры однобайтовых кодов команд:

 40 - увеличить число в регистре АХ на 1;

 50 - записать число из регистра АХ в стек.

 В качестве примера 2-байтовых команд рассмотрим команду сложения чисел в регистрах BX и AX :

       0000 00 1  1    11     011   000

                    d  w   mod   reg   r/m

 d=1 означает что reg и w описывают операнд 1 то есть BX

   а биты mod,r/m,w - операнд 2 то есть регистр AX

 w=1 определяет полнословный размер регистров

 mod=11 означает что операнд 2 является регистром

 reg=011 указывает что операнд 1 есть регистр BX

 r/m=000 указывает что операнд 2 есть регистр AX

 В качестве примера 3-байтовой команды рассмотрим команду персылки из регистра AX  в память

     1010  000  1      dddddddd   dddddddd

               reg  w      адрес   памяти (2 байта смещения)

 w=1 означает полнословный регистр AX (если стояло бы 0 то это был бы регистр AH)

 В качестве примера 4-байтовой команды рассмотрим умножение регистра AL на содержимое памяти

         111101  1 0 00  100  110  dddddddd  dddddddd

                       w mod  reg  r/m

 Процессор считает что множимое находится в регистре AL для однобайтового умножения и в регистре AX для 2-байтового

 Для этой команды reg всегда 100,mod=00 указывает на операцию с памятью а r/m=110 -на прямой способ адресации.

 Другой пример - команда определения адреса

        10001101          00    010    110  dddddddd  dddddddd

      код операции   mod   reg     r/m

 reg=010 означает регистр DX, mod=00 и r/m=110 определяют прямой способ  адресации памяти

МП 8086/8088  имеют несколько режимов  адресации, обеспечивающих доступ к операндам,  хранящимся в памяти и/или регистрах, а именно:

      1. Регистровая  адресация:  операндом  является  содержимое регистра, указанного в команде, например  ADD AX,BX заносит в AX сумму AX и BX.

      2. Неявная регистровая адресация: операндом является содержимое регистра, заданного кодом команды, например MUL CL заносит в AX произведение регистров CL и AL.

      3. Непосредственная адресация:  операнд задается в команде, напринер MOV AX,1 засылает в регистр AX единицу.

      4. Прямая адресация: в  команде задается либо адрес памяти, либо адрес порта ввода-вывода;  например, JMP 1000h осуществляет переход по адресу памяти 1000h, a IN AL,60h вводит байт из порта 60h.

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

      MOV mem,AL

      MOV mem[BX],AL

      MOV mem[BX][SI],AL

 Здесь mem - некоторая символическая метка адреса памяти.

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

 Метод  адресации  задается  байтом  адресации,  который является вторым байтом машинной команды и имеет следующую структуру:

 +7-6-5-4-3-2-1-0+

 ¦MOD¦ REG ¦ R/M ¦          Биты:                       Маска:

   |            |          |

   |            |                   +---> 0-2: Регистр/память            07h

   |            +--------             -> 3-5: Поле регистра            38h

   +----------                         > 6-7: Метод адресации     C0h

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

      Для изменения  регистра сегмента команде  можно предпослать префикс сегмента, например:

      MOV AL,mem     ; заслать в AL байт из DS:mem

      MOV AL,ES:mem  ; заслать в AL байт из ES:mem

 Таким  образом можно  указать сегмент  любого операнда,  за исключением трех случаев:

 - извлечение команд всегда производится по адресу CS:IP;

- SP  всегда используется только  как смещение относительно сегмента SS;

 - команды  работы со строками, использующие  регистр ES, не могут использовать вместо него другие регистры сегментов.

ФОРМАТЫ КОМАНД

 Формат команды зависит от кода операции, метода адресации и длины  данных,  непосредственно   заданных  в  команде.  Поэтому команда занимает от 1 до 6 байтов.

 Первый байт команды всегда содержит код операции:

Второй  байт команды содержит байт  адресации. Байты  3 - 6 содержат непосредственные  данные или смещения,  задающие адреса операндов.


Система команд процессора 8086
Справочник по командам процессора 8086

 Форматы данных

 МП 8086/8088 поддерживает следующие формaты данных:

- целое со знаком (байт и слово); знаковым является старший бит, отрицательные числа представляются в дополнительном коде;

- целое без знака (байт или слово);

- указатель - двойное слово, задающее адрес памяти; младшее слово содержит смещение, старшее - сегмент;

- символ, т. е. байт в кодировке ASCII;

- строка  - последовательность байтов или  слов длиной от 1 байта до 64 Кбайт;

- неупакованное двоично-десятичное (BCD) число; каждый байт содержит десятичную цифру;

- упакованнное   двоично-десятичное   (BCD)  число;  каждая тетрада содержит десятичную цифру.

При   наличии   арифметического    сопроцессора   8087   МП поддерживает дополнительно длинные целые  и плавающие числа

      Система команд

      Команды МП 8086/8088 подразделяются на следующие группы:

- команды передачи данных;

- ариметические операции;

- логические операции;

- команды работы со строками;

- команды передачи управления;

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

 Команды передачи данных

 Пересылка данных

      MOV  dst,src                       Флаги: не изменяются

      Действие: dst <- src

      Копирует  src (байт  или слово)  в dst.  Возможны следующие режимы пересылки:

      - из регистра в регистр;

      - из непосредственного операнда в регистр;

      - из непосредственного операнда в память;

      - из памяти в регистр;

      - из регистра в память;

      - из регистра или памяти в сегментный регистр (кроме CS);

      - из сегментного регистра в регистр или память.

      Примеры:  MOV  BX,AX

                MOV  AL,20h

                MOV  ES:[2],0FE00h

                MOV  DL,CS:[BX]

                MOV  DS:[SI],AX

                MOV  DS,CX

                MOV  CX,CS

 Загрузка исполнительного адреса

      LEA  reg,src                      Флаги: не изменяются

      Действие: reg <- addr(src)

      Заносит в reg исполнительный адрес операнда src. Src должен быть  ссылкой  к  памяти,  reg  -  16-разрядным регистром общего назначения. Примеры:

      LEA  DX,print_me

      LEA  AX,table[BX]

 Обмен значениями

      XCHG  opr,reg                       Флаги: не изменяются

      Действие: opr <-> reg

      Производит  обмен значениями  между  opr  и reg.  Opr может регистром или словом/байтом памяти, reg - регистром. Примеры:

      XCHG  AX,SI

      LOCK  XCHG  mem,DX

      Сочетание   этой   команды   с   префиксом  LOCK  позволяет реализовывать  семафоры для  управления совместно  используемыми ресурсами.

 Загрузка указателей

      LDS  reg,src                       Флаги: не изменяются

      Действие: reg <- [src], DS <- [src+2]

      LES  reg,src                       Флаги: не изменяются

      Действие: reg <- [src], ES <- [src+2]

      Команда  LDS загружает  в регистры  DS и  reg указатель  из адреса памяти  src (формат указателя  см. в 1.1.8).  Команда LES загружает  указатель  в  регистры  ES  и  reg.  Reg  должен быть 16-разрядным регистром. Примеры:

      LDS  BX,DS:[0]

      LES  DI,table[BX]

    Засылка в стек

      PUSH  src                           Флаги: не изменяются

      Действие: SP <- SP-2, [SS:SP] <- src

      Вталкивает src в  стек, т. е. уменьшает SP  на 2 и засылает src  по адресу  SS:SP.  Src  может быть  16-разрядным регистром, сегментным  регистром  или  ссылкой  к  памяти.  Даже  если  src указывает на байт памяти, в стек вталкивается слово. Примеры:

      PUSH AX

      PUSH CS

      PUSH ES:[4]

      Мы не  рекомендуем использовать команду PUSH  SP, т. к. она по-разному исполняется на МП  8086/8088 и 80826/80386. 8086/8088 сначала  уменьшает SP,  а затем  вталкивает его  в стек; 80286 и 80386  вталкивают SP  в стек,  а затем  уменьшают его. Для того, чтобы программа  не зависела от  процессора следует использовать вместо PUSH SP следующий фрагмент:

      PUSH BP

      MOV  BP,SP

      XCHG BP,[BP]

  Выборка из стека

      POP  dst                           Флаги: не изменяются

      Действие: dst <- [SS:SP], SP <- SP+2

      Выталкивает вершину стека в dst,  т. е. пересылает слово из SS:SP в src  и увеличивает SP на 2.  Dst может быть 16-разрядным регистром, сегментным регистром (кроме CS) или ссылкой к памяти.

  Трансляция

      XLAT                               Флаги: не изменяются

      Действие: AL <- [BX+AL]

      Регистр BX должен содержать  адрес таблицы трансляции, AL - перекодируемый   байт.   Команда   XLAT   извлекает  из  таблицы трансляции  байт с  номером AL   и заносит  его в  AL. Следующий пример  перекодирует  десятичное  число  от  0  до  15  в  соответствующую 16-ричную цифру:

      LEA  BX,hex_table

      MOV  AL,dec_digit

      XLAT

      . . .

 hex_table DB   '0123456789ABCDEF'

 Ввод данных

      IN   acc,port                      Флаги: не изменяются

      Действие: acc <- [port]

      Вводит байт или слово из порта ввода-вывода port в acc. Acc может быть регистром  AL (ввод байта) или AX  (ввод слова). Порт может быть  задан либо непосредственным  байтом (от 0  до 0FFh), либо содержимым регистра DX (любой порт). Примеры:

      IN   AL,21h

      IN   AL,DX

      IN   AX,DX

 Вывод данных

      OUT  port,acc                      Флаги: не изменяются

      Действие: [port] <- acc

      Выводит байт или слово из acc в порт ввода-вывода port. Acc может быть регистром AL (вывод байта) или AX (вывод слова). Порт может быть  задан либо непосредственным  байтом (от 0  до 0FFh), либо содержимым регистра DX (любой порт). Примеры:

      OUT  20h,AX

      OUT  DX,AL

 Загрузка флагов в аккумулятор

      LAHF                               Флаги: не изменяются

      Действие: AH <- flg

      Загружает в регистр AH  младший байт регистра флагов. После этой команды биты 1, 3 и 5 в AH не определены.

 Загрузка флагов из аккумулятора

      SAHF                               Флаги: O D I T S Z A P C

                                                - - - - * * * * *

      Действие: flg <- AH

      Загружает регистр AH в младший байт регистра флагов.

 Засылка флагов в стек

      PUSHF                              Флаги: не изменяются

      Действие: SP <- SP-2, [SS:SP] <- flg

      Вталкивает регистр флагов в стек, т. е. уменьшает SP на 2 и засылает слово флагов по адресу SS:SP.

 Выборка флагов из стека

      POPF                               Флаги: O D I T S Z A P C

                                                * * * * * * * * *

      Действие: flg <- [SS:SP], SP <- SP+2

      Выталкивает флаги из стека, т. е. пересылает слово из SS:SP в регистр флагов и увеличивает SP на 2.

 Арифметические операции

 Сложение

      ADD  dst,src                       Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: dst <- src+dst

      Засылает сумму операндов src и dst (байтов или слов) в dst. Операнды могут двоичными целыми числами со знаком или без знака. Src  может быть  регистром, ячейкой  памяти или непосредственной константой, dst - регистром или ячейкой памяти. Примеры:

      ADD  AX,BX

      ADD  CL,10

      ADD  DX,ES:[BX]

      ADD  total,AX

      ADD  sum,200h

 Сложение с переносом

      ADC  dst,src                       Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: dst <- src+dst+CF

      Засылает  сумму операндов  src и  dst (байтов  или слов)  и флага  CF  в  dst.  Операнды  могут  двоичными целыми числами со знаком или  без знака. Src может  быть регистром, ячейкой памяти или  непосредственной константой,  dst -  регистром или  ячейкой памяти.  Эта команда  позволяет складывать  числа двойной длины, например:

      MOV  AX,op_1        ; сложить младшие слова

      ADD  AX,op_2

      MOV  sum,AX         ; сохранить результат

      MOV  AX,op_1+2      ; сложить старшие слова

      ADC  AX,op_2+2      ; с учетом переноса

      MOV  sum+2,AX       ; и сохранить

 Вычитание

      SUB  dst,src                       Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: dst <- dst-src

      Засылает разность dst-src (байтов или слов) в dst. Операнды могут  двоичными целыми  числами со  знаком или  без знака.  Src может  быть  регистром,   ячейкой  памяти  или  непосредственной константой, dst - регистром или ячейкой памяти. Примеры:

      SUB  AX,BX

      SUB  CL,10

      SUB  DX,ES:[BX]

      SUB  total,AX

      SUB  sum,200h

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

 Вычитание с заемом

      SBB  dst,src                       Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: dst <- dst-src-CF

      Засылает  разность  dst-src-CF  (байтов  или  слов)  в dst.

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

      MOV  AX,op_1        ; извлечь первый операнд в DX:AX

      MOV  DX,op_1+2

      SUB  AX,op_2        ; вычесть второй операнд

      SBB  DX,op_2+2      ; с учетом заема

      MOV  sum,AX         ; и сохранить

      MOV  sum+2,DX

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

 Увеличение на 1

      INC  dst                           Флаги: O D I T S Z A P C

                                                * - - - * * * * -

      Действие: dst <- dst+1

      Увеличивает dst (байт или  слово) на единицу. Dst считается беззнаковым  целым и  может быть  8- или  16-разрядным регистром либо ячейкой  памяти. Эта команда  не изменяет флаг  CF, поэтому для  сложения  длинного  целого  с  единицей  нужно использовать команды ADD и ADC. Примеры:

      INC  AX

      INC  CL

      INC  table[BX]

. Уменьшение на 1

      DEC  dst                           Флаги: O D I T S Z A P C

                                                * - - - * * * * -

      Действие: dst <- dst+1

      Уменьшает dst  (байт или слово)  на единицу. Dst  считается беззнаковым  целым и  может быть  8- или  16-разрядным регистром либо ячейкой  памяти. Эта команда  не изменяет флаг  CF, поэтому для  вычитания  единицы  из  длинного  целого нужно использовать команды SUB и SBB. Примеры:

      DEC  AX

      DEC  CL

      DEC  table[BX]

 Сравнение

      CMP  dst,src                       Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: flg <- (dst-src)

      Устанавливает флаги в соответствии с операцией dst-src. Dst и src  не  изменяются.  Операнды   могут  байтами  или  словами, двоичными  целыми  со  знаком  или  без  знака.  Src  может быть регистром, ячейкой памяти или непосредственной константой, dst - регистром или ячейкой памяти. Примеры:

      CMP  AX,BX

      CMP  CL,10

      CMP  DX,ES:[BX]

      CMP  total,AX

      CMP  sum,200h

      При сравнении байтовой непосредственной константы со словом перед  вычитанием  константа  преобразуется  в слово расширением знакового бита.

 Обращение знака

      NEG  dst                           Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: dst <- -dst

      Вычитает dst (байт или слово) из  нуля и заносит в dst. Dst считается  целым  со  знаком  и  может  быть 8- или 16-разрядным регистром либо  ячейкой памяти. CF  сбрасывается, если dst=0,  и устанавливается  в  остальных  случаях.  Если  NEG применяется к байту   -128  или   слову  -32768,   то  dst   не  изменяется  и устанавливается флаг OF. Примеры:

      NEG  AX

      NEG  CL

      NEG  table[BX]

 Преобразование байта в слово

      CBW                                Флаги: не изменяются

      Действие: if (AL<80h) then AH <- 0 else AH <- 0FFh

      Преобразует байт AL в слово AX расширением знакового бита. AL  считается   целым  со  знаком.   Эта  команда  полезна   для преобразования байта в слово перед делением на байт.

Преобразование слова в двойное слово

      CWD                                Флаги: не изменяются

      Действие: if (AX<8000h) then DX <- 0 else DX <- 0FFFFh

      Преобразует  слово  AX  в  двойное  слово DX:AX расширением знакового  бита.  AX  считается  целым  со  знаком.  Эта команда полезна для преобразования слова  в двойное слово перед делением на слово.

 Умножение чисел без знака

      MUL  src                           Флаги: O D I T S Z A P C

                                                * - - - ? ? ? ? *

      Действие: AX <- src * AL    (умножение байтов)

            или DX:AX <- src * AX (умножение слов)

      Выполняет умножение целых чисел без знака. Если src - байт, то его произведение  на AL заносится в AX; если  src - слово, то его  произведение  на  AX  заносится  в  двойное слово DX:AX (DX содержит старшее  слово результата). Флаги CF  и OF устанавливаются,  если старшая  половина  результата  (AH или  DX) содержит значащие цифры результата. Src  может быть регистром или ячейкой

 памяти, например:

      MUL  CH

      MUL  BX

      MUL  addr

 Умножение чисел со знаком

      IMUL  src                           Флаги: O D I T S Z A P C

                                                 * - - - ? ? ? ? *

      Действие: AX <- src * AL    (умножение байтов)

            или DX:AX <- src * AX (умножение слов)

      Выполняет умножение целых чисел со знаком. Если src - байт, то его произведение  на AL заносится в AX; если  src - слово, то его  произведение  на  AX  заносится  в  двойное слово DX:AX (DX содержит старшее  слово результата). Флаги CF  и OF устанавливаются,  если старшая  половина  результата  (AH или  DX) содержит значащие цифры результата. Src  может быть регистром или ячейкой

 памяти, например:

      IMUL  CH

      IMUL  BX

      IMUL  addr

 Деление чисел без знака

      DIV  src                           Флаги: O D I T S Z A P C

                                                ? - - - ? ? ? ? ?

      Действие: AL <- AX / src, AH <- AX % src (деление байтов)

       или AX <- DX:AX / src, DX <-DX:AX % src (деление слов)

      Выполняет деление  целых чисел без знака.  Если src - байт,  то в  AL заносится частное,  а в AH  - остаток от  деления AX на  src;  если src  - слово,  то в  AX заносится  частное, а  в DX -  остаток  от  деления  DX:AX  на  src  (DX содержит старшее слово  делимого).  Флаги после  деления не  определены. Src  может быть  регистром или ячейкой памяти, например:

      DIV  CH

      DIV  BX

      DIV  addr

      Если  делимое  не  помещается  в  приемник, то генерируется прерывание Int 00h и результат не определен.

 Деление чисел со знаком

      IDIV  src                          Флаги: O D I T S Z A P C

                                                ? - - - ? ? ? ? ?

      Действие: AL <- AX / src, AH <- AX % src (деление байтов)

       или AX <- DX:AX / src, DX <-DX:AX % src (деление слов)

      Выполняет деление  целых чисел со знаком.  Если src - байт, то в  AL заносится частное,  а в AH  - остаток от  деления AX на src;  если src  - слово,  то в  AX заносится  частное, а  в DX - остаток  от  деления  DX:AX  на  src  (DX содержит старшее слово делимого). Флаги после деления  не определены. Частное и остаток имеют знак,  прочем знак остатка  совпадает со знаком  делимого. Src может быть регистром или ячейкой памяти, например:

      IDIV  CH

      IDIV  BX

      IDIV  addr

      Если  делимое  не  помещается  в  приемник, то генерируется прерывание Int 00h и результат не определен.

 Символьная корректировка сложения

      AAA                                Флаги: O D I T S Z A P C

                                                ? - - - ? ? * ? *

      Действие: if (AL & 0Fh > 9) or (AF=1)

                  then

                     AL <- AL+6, AH <- AH+1, AF <- 1, CF <- 1

                  else

                     AF <- 0, CF <- 0

                AL <- AL & 0Fh

      Выполняет  коррекцию  младшей  тетрады  AL  в неупакованный BCD-формат,   предполагая,   что   AL   получен  сложением  двух неупакованных  BCD-чисел  (старшая  тетрада  AL обнуляется). Эта команда  используется  для   сложения  неупакованных  BCD-чисел. Следующий  пример  показывает,  как  производится сложение таких двухразрядных чисел (результат в DX):

      MOV  AL,op1         ; сложить младшие цифры

      ADD  AL,op2

      AAA                 ; и скорректировать

      MOV  DL,AL          ; сохранить результат

      MOV  AL,op1+1       ; сложить старшие цифры

      ADC  AL,op2+1       ; с учетом переноса

      AAA                 ; скорректировать

      MOV  DH,AL          ; и сохранить

 Символьная корректировка вычитания

      AAS                                Флаги: O D I T S Z A P C

                                                ? - - - ? ? * ? *

      Действие: if (AL & 0Fh > 9) or (AF=1)

                  then

                     AL <- AL-6, AH <- AH-1, AF <- 1, CF <- 1

                  else

                     AF <- 0, CF <- 0

                AL <- AL & 0Fh

      Выполняет  коррекцию  младшей  тетрады  AL  в неупакованный BCD-формат,   предполагая,   что   AL   получен  вычитанием  двух неупакованных  BCD-чисел  (старшая  тетрада  AL обнуляется). Эта команда  используется  для   вычитания  неупакованных  BCD-чисел. Следующий  пример  показывает,  как  производится вычитание таких двухразрядных чисел (результат в DX):

      MOV  AL,op1         ; вычесть младшие цифры

      SUB  AL,op2

      AAS                 ; и скорректировать

      MOV  DL,AL          ; сохранить результат

      MOV  AL,op1+1       ; вычесть старшие цифры

      SBB  AL,op2+1       ; с учетом заема

      AAS                 ; скорректировать

      MOV  DH,AL          ; и сохранить

 Символьная корректировка умножения

      AAM                                Флаги: O D I T S Z A P C

                                                ? - - - * * ? * ?

      Действие: AH <- AL / 10, AL <- AL % 10

      Выполняет   коррекцию   AX   в   неупакованный  BCD-формат, предполагая,  что  AX   получен  умножением  двух  неупакованных BCD-чисел.  Старшие тетрады  AH и  AL должны  быть нулевыми. Эта команда  используется  для  умножения  неупакованных  BCD-чисел. Следующий  пример показывает,  как производится  умножение таких двухразрядных чисел (a и b - сомножители, c - результат, u и v -

 промежуточные результаты при умножении столбиком):

      MOV  AL,a

      MUL  b

      AAM

      MOV  u,AX

      MOV  AL,a+1

      MUL  b

      AAM

      ADD  AL,u+1

      AAA

      MOV  u+1,AX

      MOV  AL,a

      MUL  b+1

      AAM

      MOV  v,AX

      MOV  AL,a+1

      MUL  b+1

      AAM

      ADD  AL,v+1

      AAA

      MOV  v+1,AX

      MOV  AL,u

      MOV  c,AL

      MOV  AL,u+1

      ADD  AL,v

      AAA

      MOV  c+1,AL

      MOV  AL,v+2

      ADC  AL,u+1

      AAA

      MOV  c+2,AL

      MOV  AL,0

      ADC  AL,v+2

      AAA

      MOV  c+3,AL

 Символьная корректировка деления

      AAD                                Флаги: O D I T S Z A P C

                                                ? - - - * * ? * ?

      Действие: AL <- AH * 10 + AL, AH <- 0

      Выполняет  коррекцию  AX  в  неупакованный BCD-формат перед делением двух  неупакованных BCD-чисел. Последующая  команда DIV даст  в  результате  верные  неупакованныe  BCD-числа. Следующий пример показывает, как производится деление двухразрядного числа b на одноразрядное a (результат в c, остаток в r):

      MOV  AH,0

      MOV  AL,b+1

      DIV  a

      MOV  c+1,AL

      MOV  AL,b

      AAD

      DIV  a

      MOV  c,AL

      MOV  r,AH

 Десятичная корректировка сложения

      DAA                                Флаги: O D I T S Z A P C

                                                ? - - - * * * * *

      Действие: if (AL & 0Fh > 9) or (AF=1)

                  then

                     AL <- AL+6, AF <- 1

                  else

                     AF <- 0

                if (AL > 9Fh) or (CF=1)

                  then

                     AL <- AL+60h, CF <- 1

                  else

                     CF <- 0

      Выполняет   коррекцию   AL    в   упакованный   BCD-формат, предполагая,   что   AL   получен   сложением  двух  упакованных BCD-чисел.  Эта  команда  используется  для сложения упакованных BCD-чисел.  Следующий пример  показывает, как  производится сло жение таких 4-разрядных чисел (результат в DX):

      MOV  AL,op1         ; сложить младшие цифры

      ADD  AL,op2

      DAA                 ; и скорректировать

      MOV  DL,AL          ; сохранить результат

      MOV  AL,op1+1       ; сложить старшие цифры

      ADC  AL,op2+1       ; с учетом переноса

      DAA                 ; скорректировать

      MOV  DH,AL          ; и сохранить

 Десятичная корректировка вычитания

      DAS                                Флаги: O D I T S Z A P C

                                                ? - - - ? ? * ? *

      Действие: if (AL & 0Fh > 9) or (AF=1)

                  then

                     AL <- AL-6, AF <- 1

                  else

                     AF <- 0

                if (AL > 9Fh) or (CF=1)

                  then

                     AL <- AL-60h, CF <- 1

                  else

                     CF <- 0

      Выполняет   коррекцию   AL    в   упакованный   BCD-формат, предполагая,   что  AL   получен  вычитанием   двух  упакованных BCD-чисел.  Эта команда  используется для  вычитания упакованных BCD-чисел. Следующий  пример показывает, как  производится вычитание таких 4-разрядных чисел (результат в DX):

      MOV  AL,op1         ; вычесть младшие цифры

      SUB  AL,op2

      DAS                 ; и скорректировать

      MOV  DL,AL          ; сохранить результат

      MOV  AL,op1+1       ; вычесть старшие цифры

      SBB  AL,op2+1       ; с учетом заема

      DAS                 ; скорректировать

      MOV  DH,AL          ; и сохранить

      Отметим,  что  команды   умножения  и  деления  упакованных BCD-чисел в МП фирмы Intel отсутствуют.

 Логические операции

 Логические сдвиги

      SHR  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - * * ? * *

      Действие:

                    +-----+    +----+

               0 -> ¦ dst ¦ -> ¦ CF ¦

                    +-----+    +----+

      SHL  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - * * ? * *

      Действие:

               +----+    +-----+

               ¦ CF ¦ <- ¦ dst ¦ <- 0

               +----+    +-----+

      Команды логических  сдвигов сдвигают содержимое  dst (байта или слова) на cnt битов вправо (SHR) или влево (SHL). Освобождающиеся  биты заполняются  нулями.  Счетчик  cnt может  быть либо равен 1,  либо задается содержимым CL.  Dst может быть регистром или ячейкой памяти. Примеры:

      SHL  AL,1

      SHR  SI,CL

      SHR  mem,1

      SHL  mem,CL

      Флаг CF  равен содержимому последнего  бита, выдвинутому из dst. Если cnt = 1, то  флаг OF сбрасывается, если старшие 2 бита исходного  операнда были  одинаковы, и  сбрасывается в противном случае. Если cnt > 1, то OF неопределен.

      МП 80286  и 80386 ограничивают значениe  cnt диапазоном 0 - 31, т. е. используют величину счетчика, равную cnt % 32. Следующий  пример  показывает  преобразование 16-разрядного неупакованного BCD-числа в упакованное:

      MOV  DX,8           ; счетчик цикла

      MOV  CL,4           ; счетчик сдвигов

      LEA  SI,Unpacked    ; адрес неупакованного BCD-числа

      LEA  DI,Packed      ; адрес упакованного BCD-числа

      PUSH DS

      POP  ES             ; ES <- DS

      CLD                 ; направление "вперед"

 Convert:

      LODSW               ; AX <- DS:SI, SI <- SI+2

      SHL  AL,CL          ; упаковка AX

      SHR  AX,CL          ; в AL

      STOSB               ; ES:DI <- AL, DI <- DI+1

      DEC  DX             ; уменьшить счетчик цикла

      JNZ  Convert        ; повторять, пока он не нуль

 Арифметические сдвиги

      SAR  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - * * ? * *

      Действие: +--+

                ¦ +-----+    +----+

                +>¦ dst ¦ -> ¦ CF ¦

                  +-----+    +----+

      SAL  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - * * ? * *

      Действие:

               +----+    +-----+

               ¦ CF ¦ <- ¦ dst ¦ <- 0

               +----+    +-----+

      Команды  арифметических  сдвигов  сдвигают  содержимое  dst (байта  или слова)  на cnt  битов вправо  (SAR) или влево (SAL). Освобождающиеся биты заполняются нулями в команде SAL и знаковым разрядом  в команде  SAR. Счетчик  cnt может  быть либо равен 1, либо  задается  содержимым  CL.  Dst  может  быть  регистром или ячейкой  памяти.  Очевидно,  команды  SAL  и  SHL  эквивалентны. Примеры:

      SAL  AL,1

      SAR  SI,CL

      SAR  mem,1

      SAL  mem,CL

      Флаг CF  равен содержимому последнего  бита, выдвинутому из dst. Если cnt = 1, то флаг  OF сбрасывается; если cnt > 1, то OF неопределен.

      МП 80286  и 80386 ограничивают значениe  cnt диапазоном 0 - 31, т. е. используют величину счетчика, равную cnt % 32.

 Циклические сдвиги

      ROR  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - - - - - *

      Действие:  +--------+

                 ¦ +-----+¦  +----+

                 +>¦ dst +-->¦ CF ¦

                   +-----+   +----+

      ROL  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - - - - - *

      Действие:        +--------+

               +----+  ¦+-----+ ¦

               ¦ CF ¦<--¦ dst ¦<+

               +----+   +-----+

      Команды циклических сдвигов  сдвигают содержимое dst (байта или  слова) на  cnt битов  вправо (ROR)  или влево  (ROL). Биты, выдвигающиеся из  операнда, вдвигаются в  него с другого  конца. Счетчик cnt  может быть либо  равен 1, либо  задается содержимым CL. Dst может быть регистром или ячейкой памяти. Примеры:

      ROL  AL,1

      ROR  SI,CL

      ROR  mem,1

      ROL  mem,CL

      Флаг CF  равен содержимому последнего  бита, выдвинутому из dst. Если cnt = 1, то  флаг OF сбрасывается, если старшие 2 бита исходного  операнда были  одинаковы, и  сбрасывается в противном случае. Если cnt > 1, то OF неопределен.

 МП 80286  и 80386 ограничивают значениe  cnt диапазоном 0 - 31, т. е. используют величину счетчика, равную cnt % 32.

 Циклические сдвиги через перенос

      RCR  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - - - - - *

      Действие:  +-------------+

                 ¦ +-----+   +----+

                 +>¦ dst +-->¦ CF ¦

                   +-----+   +----+

      RCL  dst,cnt                       Флаги: O D I T S Z A P C

                                                * - - - - - - - *

      Действие:  +--------------+

               +----+   +-----+ ¦

               ¦ CF ¦<--¦ dst ¦<+

               +----+   +-----+

      Команды   циклических   сдвигов   через   перенос  сдвигают содержимое dst (байта  или слова) на cnt битов  вправо (RCR) или влево (RCL). Биты, выдвигающиеся  из операнда, вдвигаются в него с другого  конца,  проходя  через  флаг  CF  (иными  словами, CF рассматривается  как дополнительный  бит операнда).  Счетчик cnt может быть либо равен 1,  либо задается содержимым CL. Dst может быть регистром или ячейкой памяти. Примеры:

      RCL  AL,1

      RCR  SI,CL

      RCR  mem,1

      RCL  mem,CL

      Флаг CF  равен содержимому последнего  бита, выдвинутому из dst. Если cnt = 1, то  флаг OF сбрасывается, если старшие 2 бита исходного  операнда были  одинаковы, и  сбрасывается в противном случае. Если cnt > 1, то OF неопределен.

      МП 80286  и 80386 ограничивают значениe  cnt диапазоном 0 - 31, т. е. используют величину счетчика, равную cnt % 32.

 Логическое "И"

      AND  dst,src                       Флаги: O D I T S Z A P C

                                                0-------* * ? * 0

      Действие: dst <- src & dst

      Засылает  поразрядное логическое  "И" операндов  src и  dst (байтов  или  слов)  в  dst.  Src  может быть регистром, ячейкой памяти  или  непосредственной  константой,  dst  - регистром или ячейкой памяти. Примеры:

      AND  AX,BX

      AND  CL,1

      AND  DX,mask

      AND  flags,AL

      AND  mem,200h

 Логическое "ИЛИ"

      OR   dst,src                       Флаги: O D I T S Z A P C

                                                0-------* * ? * 0

      Действие: dst <- src & dst

      Засылает поразрядное  логическое "ИЛИ" операндов  src и dst (байтов  или  слов)  в  dst.  Src  может быть регистром, ячейкой памяти  или  непосредственной  константой,  dst  - регистром или ячейкой памяти. Примеры:

      OR   AX,BX

      OR   CL,10

      OR   DX,mask

      OR   flags,AL

      OR   mem,200h

 Сложение по модулю два

      XOR  dst,src                       Флаги: O D I T S Z A P C

                                                0-------* * * * 0

      Действие: dst <- src XOR dst

      Засылает поразрядную  сумму по модулю  два операндов src  и dst (байтов или  слов) в dst. Src может  быть регистром, ячейкой памяти  или  непосредственной  константой,  dst  - регистром или ячейкой памяти. Примеры:

      XOR  AX,BX

      XOR  CL,10

      XOR  DX,mask

      XOR  flags,AL

      XOR  mem,200h

      Эта  команда  часто  используется  для  обнуления регистра: например,  XOR AX,AX эквивалентна MOV AX,0 и выполняется на такт быстрее (но изменяет флаги условий).

 Логическое отрицание

      NOT  dst                           Флаги: не изменяются

      Действие: dst <- ~ dst

      Засылает  поразрядное  логическое  отрицание  операнда  dst (байта или  слова) в dst.  Dst может быть  регистром или ячейкой памяти. Примеры:

      NOT  DX

      NOT  AL

      NOT  mask[BX]

 Проверка битов

      TEST dst,src                       Флаги: O D I T S Z A P C

                                                0 - - - * * ? * 0

      Действие: flg <- (dst & src)

      Устанавливает флаги  в соответствии с операцией  dst & src, т. е. выполняет поразрядное логическое  "И" операндов. Dst и src не  изменяются. Src  может  быть  регистром, ячейкой  памяти или непосредственной константой, dst - регистром или ячейкой памяти. Примеры:

      TEST SI,DX

      TEST BL,mask

      TEST AX,0F000h

      TEST param,1F1Fh

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

 Команды работы со строками

      Для  работы  со  строками  байтов  или  слов в МП 8086/8088 предусмотрены 5  специальных команд, имеющих  общие особенности. Каждая команда имеет две модификации, именуемые

      имяB - для работы с байтами, и

      имяW - для работы со словами.

      Операндом-источником  для  всех  строковых  команд является содержимое  адреса DS:SI  (регистр DS  может быть  переопределен префиксом  другого  сегментного  регистра). Операндом-приемником всегда явлется ячейка памяти с адресом ES:DI. После  выполнения  строковой  команды  содержимое  SI  и DI автоматически изменяются  на 1, если это  байтовая команда, и на 2, если  команда словная. Направление  изменения задается флагом DF:  если DF  = 0,  то регистры  увеличиваются; если  DF = 1, то уменьшаются.

 Пересылка строки

      MOVSB                              Флаги: не изменяются

      Действие: [ES:DI] <- [DS:SI] (байт)

                if DF = 0

                 then SI <- SI+1, DI <- DI+1

                 else SI <- SI-1, DI <- DI-1

      MOVSW                              Флаги: не изменяются

      Действие: [ES:DI] <- [DS:SI] (слово)

                if DF = 0

                 then SI <- SI+2, DI <- DI+2

                 else SI <- SI-2, DI <- DI-2

      Копирует байт (MOVSB) или слово  (MOVSW) из адреса DS:SI по адресу  ES:DI. После  этого модифицирует  SI и  DI, как  описано выше. Эта команда часто используется с префиксом REP для копирования строк.

 Чтение строки

      LODSB                              Флаги: не изменяются

      Действие: AL <- [DS:SI]

                if DF = 0

                 then SI <- SI+1

                 else SI <- SI-1

      LODSW                              Флаги: не изменяются

      Действие: AX <- [DS:SI]

                if DF = 0

                 then SI <- SI+2

                 else SI <- SI-2

      Читает  байт (LODSB)  или слово  (LODSW) из  адреса DS:SI в аккумулятор. После  этого модифицирует SI,  как описано выше.  С префиксом REP не используется, т. к. это не имеет смысла.

 Запись строки

      STOSB                              Флаги: не изменяются

      Действие: [ES:DI] <- AL

                if DF = 0

                 then DI <- DI+2

                 else DI <- DI-2

      STOSW                              Флаги: не изменяются

      Действие: [ES:DI] <- AX

                if DF = 0

                 then DI <- DI+2

                 else DI <- DI-2

      Записывает байт  (STOSB) или слово  (STOSW) из аккумулятора по адресу ES:DI. После этого  модифицирует DI, как описано выше. В сочетании с  префиксом REP позволяет  заполнить область памяти значением аккумулятора.

Сравнение строк

      CMPSB                              Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: flg <- [DS:SI]-[ES:DI] (байты)

                if DF = 0

                 then SI <- SI+1, DI <- DI+1

                 else SI <- SI-1, DI <- DI-1

      CMPSW                              Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: flg <- [DS:SI]-[ES:DI] (слова)

                if DF = 0

                 then SI <- SI+2, DI <- DI+2

                 else SI <- SI-2, DI <- DI-2

      Вычитает байты (CMPSB) или слова (CMPSW): содержимое адреса DS:SI  минус содержимое  адреса  ES:DI,  и модифицирует  флаги в соответствии с результатом (операнды не изменяются). После этого модифицирует  SI  и  DI,  как  описано  выше.  Эта команда часто используется  с  префиксом  REPE  для  сравнения  двух строк. По завершении команды  REPE CMPSx флаг ZF  установлен, если найдено различие, и сброшен в противном случае. Если  ZF = 1, то SI и DI указывают на следующие байты/слова после давших несовпадение.

 Сканирование строки

      SCASB                              Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: flg <- AL-[ES:DI]

                if DF = 0

                 then DI <- DI+1

                 else DI <- DI-1

      SCASW                              Флаги: O D I T S Z A P C

                                                * - - - * * * * *

      Действие: flg <- AX-[ES:DI]

                if DF = 0

                 then DI <- DI+2

                 else DI <- DI-2

      Вычитает  байты  (SCASB)   или  слова  (SCASW):  содержимое аккумулятора минус содержимое адреса ES:DI, и модифицирует флаги в соответствии  с  результатом  (операнды  не изменяются). После этого  модифицирует  DI,  как  описано  выше.  Эта команда часто используется с префиксом REPNE  для поиска байта/слова в строке. По  завершении  команды  REPNE  SCASx  флаг  ZF установлен, если образец найден, и сброшен в противном случае. Если ZF = 1, то DI указывает на следующий байт/слово после совпавшего.

 Префиксы повторений

      Строковые команды  часто используются в  сочетании с т.  н. префиксами повторений REP, REPE и REPNE.  Префикс  REP  (синонимы  REPE,  REPZ)  выполняет  следующие действия:

      - с командами MOVSx, LODSx и STOSx:

      while CX <> 0

           выполнить строковую операцию

           CX <- CX-1

      - с командами CMPSx и SCASx:

      while CX <> 0

           выполнить строковую операцию

           CX <- CX-1

           if ZF = 0 break

      Иными  словами,  префикс  REP  задает  выполнение строковой команды  CX раз,  либо до  тех пока  не будет  сброшен флаг  ZF. Проверка CX на равенство  нулю выполняется до строковой команды, поэтому команда  REP MOVSB с нулевым  CX будет просто пропущена. Проверка флага ZF производится после строковой операции.

      Префикс REPNE (синоним REPNZ) выполняет следующие действия:

      - с командами MOVSx, LODSx и STOSx:

      while CX <> 0

           выполнить строковую операцию

           CX <- CX-1

      - с командами CMPSx и SCASx:

      while CX <> 0

           выполнить строковую операцию

           CX <- CX-1

           if ZF = 1 break

      Он полностью  эквивалентен префиксу REP  с тем единственным отличием, что  условием прерывания цикла  служит установка флага ZF. Следующий пример копирует 100 байтов из buffer1 в buffer2:

      CLD                 ; направление "вперед"

      LEA  SI,buffer1     ; адреса строк

      LEA  DI,buffer2

      PUSH DS

      POP  ES             ; ES <- DS

      MOV  CX,100         ; счетчик цикла

      REP  MOVSB          ; копирование строк

 Команды передачи управления

 Вызов подпрограммы

      CALL opr                           Флаги: не изменяются

      Действие: if FAR CALL

                   PUSH CS

                   CS <- opr_seg

                PUSH IP

                IP <- opr_offs

      Эта команда обеспечивает сохранение адреса следующей за ней команды в  стеке и передачу  управления по заданному  адресу. На самом  деле мнемонику  CALL  имеют  две разные  команды: команда ближнего вызова пoдпрограммы  (NEAR CALL), передающая управление в пределах  текущего сегмента  кодов, и  команда дальнего вызова (FAR CALL), передающая управление в другой сегмент.

      Ближний вызов подпрограммы вталкивает  значение IP в стек и заменяет  его на  смещение до  вызываемой подпрограммы.  Дальний вызов  сохраняет в  стеке CS  и IP  и заменяет  их на  сегмент и смещение   вызываемой  подпрограммы.   Адрес  подпрограмммы  opr задается либо  непосредственно в команде, либо  в регистре, либо ссылкой к памяти, например:

      CALL near_proc

      CALL far_proc

      CALL AX

      CALL proc_table[SI]

      Возврат  из подпрограммы  осуществляется командой  RET, тип которой (ближний или дальний) должен соответствовать типу CALL.

 Возврат из подпрограммы

      RET  opr                           Флаги: не изменяются

      Действие: POP  IP

                if FAR RET

                   POP CS

                SP <- SP + opr

      Эта  команда  обеспечивает  выталкивание  из  стека адреса, сохраненного командой  CALL, и передачу на  него управления. Она выталкивает из  стека счетчик команд IP  и, если возврат дальний (межсегментный), то значение CS. Тем самым управление передается на команду,  следующую за командой CALL,  вызвавшей эту подпрог рамму. В команде может быть задан необязательный операнд opr. Если он есть, то его величина  прибавляется к регистру SP (что равно-

 сильно выталкиванию из стека opr байтов). Примеры:

      RET

      RET  4

 Безусловный переход

      JMP  opr                           Флаги: не изменяются

      Действие: if FAR JMP

                   CS <- opr_seg

                IP <- opr_offs

      Эта команда  обеспечивает передачу управления  по заданному адресу. На  самом деле мнемонику  JMP имеют две  разные команды: команда ближнего  перехода (NEAR JMP ),  передающая управление в пределах  текущего сегмента  кодов, и  команда дальнего перехода (FAR JMP ), передающая управление в другой сегмент.

      Ближняя передача управления заменяет  IP на новое значение.

 Дальняя передача управления заменяет CS  и IP на новые сегмент и смещение.  Адрес перехода  opr задается  либо непосредственно  в команде, либо в регистре, либо ссылкой к памяти, например:

      JMP  near_label

      JMP  far_label

      JMP  BX

      JMP  label_table[SI]

 Команды условного перехода

      Все  конанды условного  перехода  имеют  вид Jxx  disp, где  disp  -  8-разрядное  смещение  в  дополнительном  коде. Команда  проверяет  определенное  условие  и,  если  оно  верно, изменяет  значение  IP  на  заданное  смещение.  Длина  смещения позволяет  передать  управление  в  диапазоне  от  -128  до  +127 байтов от  текущей команды.  Условием перехода служит  определенная комбин-  ация  флагов условий,  задаваемая мнемоникой  команды Jxx.  Сама  команда перехода  флаги условий не изменяет.  МП 8086/8088 имеют  следующие команды условного перехода:

 ----------------------------------------------------------------

 Мнемоника       Синонимы     Условие             Назначение

 команды                                   перехода

 ----------------------------------------------------------------

 JZ  dest                   JE               ZF=1          Перейти, если нуль (равно)

 JNZ  dest                JNE             ZF=0          Перейти, если не нуль (не равно)

 JS  dest                                      SF=1          Перейти, если знак минус

 JNS dest                                    SF=0          Перейти, если знак плюс

 JO  dest                                     OF=1          Перейти, если переполнение

 JNO dest                                   OF=0          Перейти, если нет переполнения

 JP  dest                  JPE             PF=1          Перейти, если четный паритет

 JNP dest                 PO             PF=0          Перейти, если нечетный паритет

 JB  dest               JNAE,JC       CF=1          Перейти, если ниже

 JNB dest             JAE,JNC      CF=0          Перейти, если выше или равно

 JA  dest               JNBE         CF=0 & ZF=0   Перейти, если выше

 JNA dest             JBE             CF=1 | ZF=1   Перейти, если ниже или равно

 JL  dest               JNGE           SF<>OF        Перейти, если меньше

 JNL dest             JGE              SF=OF         Перейти, если больше или равно

 JG  dest              JNLE         ZF=0 & SF=OF  Перейти, если больше

 JNG dest            JLE             ZF=1 | SF<>OF Перейти, если меньше или равно

 JCXZ dest                                CX=0          Перейти, если CX равно нулю

 ----------------------------------------------------------------

      Команды JB, JNB, JA, JNA обеспечивают переход после сравнения двух целых чисел без знака. Команды JL, JNL, JG, JNG обеспечивают переход  после сравнения целых  чисел со знаком.  Команда JCXZ передает управление, если CX  = 0, и используется совместно с командами циклов

Команды циклов

Все  конанды  циклов  имеют  вид  LOOPxx  disp,  где disp - 8-разрядное  смещение  в   дополнительном  коде.  Команда  цикла уменьшает  регистр CX  на 1,  и если  выполнено связанное  с ней условие,  заменяет  значение  IP  на  заданное  смещение:

      CX <- CX-1

      if (условие) JMP dest

      Длина смещения позволяет передать управление в диапазоне от -128 до +127 байтов от текущей команды. Условием перехода служит определенная  комбинация флагов  условий, задаваемая  мнемоникой команды LOOPxx. Сама команда цикла флаги условий не изменяет. МП 8086/8088 имеют следующие команды циклов:

 ----------------------------------------------------------------

 Мнемоника         Синонимы               Условие             Назначение

 команды                                                цикла

 ----------------------------------------------------------------

 LOOP   dest                                           CX<>0                Зациклить

 LOOPZ  dest      LOOPE                    ZF=1 & CX<>0      Цикл, пока нуль (равно)

 LOOPNZ dest     LOOPNE                 ZF=0 & CX=0  Цикл, пока не нуль (не равно)

 ----------------------------------------------------------------

      Из  таблицы   видно,  что  эти   команды  позволяют  удобно реализовывать циклы с постусловием вида

      MOV  CX,cnt    ; счетчик цикла

 Start:

      .  .  .

      LOOP Start

      Следующий пример  ищет в ASCII-строке str  длины len символ пробела;  если  символ  не  найден,  производит  переход к метке Not_Found.

      MOV  CX,L

      JCXZ Not_Found

      LEA  SI,str-1

      MOV  AL,20h

 Next_Chr:

      INC  SI

      CMP  AL,[SI]

      LOOPNE Next_Chr

      JNZ  Not_Found

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

      INT opr                            Флаги: O D I T S Z A P C

                                                0 0 - - - - - - -

      Действие: PUSHF

                TF <- 0, IF <- 0

                CALL FAR [0:4*num]

      Эта команда сохраняет в стеке регистр флагов и текущие CS и IP,  сбрасывает флаги  TF и  IF и  передает управление программе обработки соответствующего прерывания (номер прерывания задается непосредственным операндом opr и лежит в диапазоне от 0 до 255). Если программа  обработки прерываний завершается  командой IRET, то  флаги  условий  восстанавливаются  и  управление  передается команде, следуюшей  командой INT. Команда  INT opr занимает  два байта, за исключением  случая opr=3: INT 3 занимает  один байт и используется   в  качестве   точки  останова   отладчиками

 Прерывание по переполнению

      INTО                               Флаги: O D I T S Z A P C

                                                0 0 - - - - - - -

      Действие: if (OF=1)

                   PUSHF

                   TF <- 0, IF <- 0

                   CALL FAR [0:10h]

      Эта команда проверяет флаг OF;  если он сброшен, то команда игнорируется, если установлен, то  вызывается прерывание номер 4 (т. е. в  этом случае INTO эквивалентна INT  4).

 Возврат из прерывания

      IRET                               Флаги: O D I T S Z A P C

                                                * * * * * * * * *

      Действие: POP  IP

                POP  CS

                POPF

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

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

 Управление флагами условий

      МП 8086/8088 имеют 7 команд, позволяющих сбросить или уста новить определенный флаг условий (остальные флаги остаются неиз менными):

 ----------------------------------------------------------------

 Мнемоника  Действие           Назначение

 ----------------------------------------------------------------

 CLC        CF <- 0    Сбрасывает флаг CF

 STC        CF <- 1    Устанавливает флаг CF

 CMC        CF <- ~CF  Засылает в CF обратное текущему значение

 CLD        DF <- 0    Сбрасывает флаг DF

 STD        DF <- 1    Устанавливает флаг DF

 CLI        IF <- 0    Сбрасывает флаг IF

 STI        IF <- 1    Устанавливает флаг IF

 ----------------------------------------------------------------

      Следует  отметить, что  если к  моменту подачи  команды STI имелось задержанное  прерывание, то оно  призойдет по завершении следующей за STI команды.

 Останов процессора

      HLT                                Флаги: не изменяются

      Эта  команда  переводит  МП  в  состояние останова, которое продолжается до наступления одного из событий:

      - возникновения немаскируемого внешнего прерывания;

      - возникновения маскируемого внешнего прерывания, если флаг

 IF установлен;

      - активизации линии RESET (внешнего сброса МП).

 Холостая команда

      NOP                                Флаги: не изменяются

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

 Ожидание

      WAIT                               Флаги: не изменяются

      Переводит МП в состояние  ожидания, которое продолжается до тех пор, пока не активизируется  линия TEST. Прерывания в состоянии ожидания разрешены. Обычно используется для синхронизации с внешними устройствами (в частности, с сопроцессором).

 Захват шины

      LOCK                               Флаги: не изменяются

      Этот   однобайтовый  префикс   может  предшествовать  любой команде.  Он  захватывает  системную  шину  на  время выполнения следующей  за  ним  команды,  исключая  тем  самым доступ к шине других устройств,  включая прерывания и прямой  доступ к памяти. Эта команда предназначена для  реализации семафоров и управления совместно используемыми ресурсами в многопроцессорных конфигура- циях.  Для  совместимости  с  МП  80286  и  80386  рекомендуется использовать ее только с командами XCHG, ADD, OR, ADC, SBB, AND, SUB, XOR, NOT, NEG, INC и DEC, например:

      LOCK XCHG flag,AL

 Команда сопроцессору

      ESC  opr,src                       Флаги: не изменяются

      Эта команда передает код операции opr сопроцессору, который перехватывает  этот код  и выбирает  с адресной  шины 20-битовый физический адрес операнда src.  В зависимости от операции сопроцессор  либо  считывает  содержимое  src,  либо  выводит  в него результат  операции по  ее  завершению.  Для гарантии  того, что сопроцессор завершил операцию, МП должен выдать команду WAIT

      Примеры  использования  команды  ESC  приведены  в описании арифметических сопроцессоров .

 Префиксы сегментов

      Как  указывалось , команде  может быть предпослан однобайтовый  префикс, переопределяющий  сегмент операнда.  Хотя префикс  сегмента предшествует  команде, его  принято записывать перед операндом. Например, пару команд

      ES:            ; префикс сегмента ES

      MOV  AL,[BX]

 мы записываем в виде MOV AL,ES:[BX].

                      _E2.2. Система команд_F

ОСОБЕННОСТИ 286-ГО ПРОЦЕССОРА

      В реальном  режиме МП 80286  имеет несколько новых  команд, которые описаны в данном разделе.

 Сохранение регистров в стеке

      PUSHA                               Флаги: не изменяются

      Действие: Temp <- SP

                PUSH AX

                PUSH CX

                PUSH DX

                PUSH BX

                PUSH Temp

                PUSH BP

                PUSH SI

                PUSH DI

      Вталкивает  в   стек  все  регистры   общего  назначения  в указанном  порядке. Следует  отметить, что  в стек  вталкивается значение SP, которое было до начала выполнения команды. Если содержимое  SP перед  выполнением команды  равно 1,  3 или 5, происходит выключение процессора; если  начальное значение SP равно

 7, 9, 13 или 15, возникает прерывание Int 0Dh.

 Восстановление регистров из стека

      POPA                                Флаги: не изменяются

      Действие: POP  DI

                POP  SI

                POP  BP

                POP  ...       ; SP не выталкивается!

                POP  BX

                POP  DX

                POP  CX

                POP  AX

      Выталкивает из стека регистры общего назначения в указанном порядке. Следует отметить, что значение SP, сохраненное в стеке, игнорируется. Эта команда является обратной к PUSHA.

 Засылка в стек непосредственных данных

      PUSH  data                          Флаги: не изменяются

      Действие: SP <- SP-2, [SS:SP] <- data

      Вталкивает data в стек, т. е.  уменьшает SP на 2 и засылает data  по  адресу  SS:SP.  Data  может  быть байтовой или словной непосредственой  константой. Даже  если data  является байтом, в стек вталкивается слово. Примеры:

      PUSH 0

      PUSH 1234h

 Умножение со знаком на непосредственное данное

      IMUL  reg,src,data                 Флаги: O D I T S Z A P C

      IMUL  reg,data                            * - - - ? ? ? ? *

      Действие: reg <- src * data

            или reg <- reg * data

      Выполняет  умножение целых  чисел со  знаком. Если  команда  имеет три операнда, то в 16-разрядный регистр reg заносится про-  изведение содержимого слова src  (регистра или памяти) на непос-  редственную  константу  data,  которая  может  быть  байтом  или  словом. Если команда имеет два операнда, то в reg заносится про-  изведение reg и  data. Если data - байт,  то перед умножением он  преобразуется в слово расширением знакового разряда.  Флаги CF и OF устанавливаются, если результата умножения не помещается  в регистр  reg (в  этом случае  reg содержит младшие значащие цифры результата). Примеры:

      IMUL  BX,opr,2

      IMUL  DX,AX,5

      IMUL  CX,10h

 Проверка границ массива

      BOUND reg,src                      Флаги: Не изменяются

      Действие: if reg < DS:[src] | reg > DS:[src+2]

                  Int 05h

      Эта  команда проверяет,  что индекс  массива, хранящийся  в  16-разрядном регистре  reg, не выходит  за границы массива.  Src  должно указывать на два слова  памяти, содержащие нижнюю и верх-  нюю границы массива. Если индекс  лежит за пределами массива, то  генерируется Int 05h; в противном случае не происходит ничего.        Сравнение  индекса с  границами производится  как сравнение  целых чисел  зо знаками. Два слова,  содержащие границу массива,  обычно размещаются непосредственно перед массивом, чтобы обеспе-  чить адресацию границ массива через  смещение от его начала, например:

           DW   0,9                  ; границы массива

 array     DB   0,1,2,3,4,5,6,7,8,9  ; сам массив

           .  .  .

           LEA   BX,array            ; BX = адрес массива

           BOUND SI,DS:[BX-4]        ; SI в границах?

           MOV   AX,DS:[BX+SI]       ; если не было Int 5,

                                     ; AX <- array[SI]

 Создание кадра для процедуры

      ENTER data,level                   Флаги: Не изменяются

      Действие: level <- level % 32

                PUSH BP

                frame_ptr <- SP

                if level > 0

                  for i <- 0 to level-1

                     BP <- BP-2; PUSH BP

                  PUSH frame_ptr

                BP <- frame_ptr

                SP <- SP-data

      Создает  кадр стека  для процедуры  блочного языка высокого  уровня. Непосредственное слово данных data задает размер кадра в  байтах. Непосредственный  байт level задает  уровень лексической  вложенности процедуры  (0 - 31)  в исходном тексте  программы на  языке  высокого  уровня.  Он  определяет  количество  указателей  кадра, которые  нужно скопировать в  новый кадр из  предыдущего.  Текущим указателем кадра должен быть регистр BP.        В наиболее распространенном случае level = 0. Тогда команда  ENTER эквивалентна такой последовательности команд:

      PUSH BP

      MOV  BP,SP

      SUB  SP,data

      Эта команда используется в начале процедур и сопровождается командой LEAVE перед RET, например:

 example:

      ENTER     12,0 ; выделить стек под 12 байтов

                     ; локальных переменных

      .  .  .

      LEAVE          ; освободить стек

      RET            ; и окончить процедуру

 Освобождение кадра процедуры

      LEAVE                              Флаги: Не изменяются

      Действие: MOV  SP,BP

                POP  BP

      Эта команда является обратной по отношению к команде ENTER; она освобождает кадр стека  перед возвратом из процедуры.

 Сдвиг на непосредственную константу

      ROL/ROR/RCL/RCR dst,data           Флаги: O D I T S Z A P C

                                                ? - - - - - - - *

      SAL/SAR/SHL/SHR dst,data                  ? - - - * * ? * *

      Эти  команды, описанные  ранее, получили  в МП  80286 дополнительную возможность:  вы можете указать  в качестве счетчика сдвигов  непосредственный байт данных.  При этом количество выполняемых сдвигов равно data % 32,  т. е. лежит в диапазоне от 0 до 31.

 Ввод в строку

      INSB                               Флаги: не изменяются

      Действие: [ES:DI] <- [port DX] (байт)

                if DF = 0

                 then DI <- DI+2

                 else DI <- DI-2

      INSW                               Флаги: не изменяются

      Действие: [ES:DI] <- [port DX] (слово)

                if DF = 0

                 then DI <- DI+2

                 else DI <- DI-2

      Вводит байт (INSB) или слово (INSW) из порта DX и сохраняет его по  адресу ES:DI. После  этого модифицирует DI,  как описано выше. В сочетании с префиксом  REP позволяет ввести блок даных в память.

 Вывод из строки

      OUTSB                              Флаги: не изменяются

      Действие: [port DX] <- [DS:SI] (байт)

                if DF = 0

                 then SI <- SI+1

                 else SI <- SI-1

      OUTSW                              Флаги: не изменяются

      Действие: [port DX] <- [DS:SI] (слово)

                if DF = 0

                 then SI <- SI+2

                 else SI <- SI-2

      Выводит байт  (OUTSB) или слово  (OUTSW) из адреса  DS:SI в порт  DX.  После  этого  модифицирует  SI,  как  описано выше. С префиксом REP используется для вывода блоков.


Внешние запоминающие устройства ПК
Некоторые сведения о внешних запоминающих устройствах ПК

Оперативная память хранит записанные в нее числа только тогда, когда на нее подается электрическое питание.При отключении компьютера от сети все записанное в оперативной памяти пропадает.Поэтому для хранения  методов обработки данных и самих данных необходимы долговременные запоминающие устройства,не требующие электропитания.  Наиболее распространенными являются устройства магнитной записи и воспроизведения - магнитные ленты(стриммеры), магнитные  диски ,оптические компакт-диски. Самые распространенные из них -магнитные диски есть 2-х исполнений- со съемными носителями (дисками) и со стационарными (резидентные диски).Дисководы для съемных (гибких пластмассовых) дисков имеют малую скорость вращения (а следовательно и малую скорость чтения-записи).Сами съемные диски имеют сравнительно небольшую емкость - примерно до 2 млн байтов или 2 мегабайта. Служат они для отделения данных и программ от компьютера при их переносе с одной машины на другие ,хранения на случай потери данных на жестком резидентном диске из-за поломок или разрушител ьной работы программных компьютерных вирусов.  Стационарные диски закрытого исполнения с плавающими на воздушной подушке магнитными головками имеют огромную по сравнению с гибкими скорость вращения.Из-за схожести формы их первых конструкций для персональных машин с 2-хствольным охотничьим ружьем - их назвали винчестерами.Емкость современных винчестеров измеряется в гигабайтах и на них хранятся все необходимые для работы компьютера программы и данные.Запись на гибкие диски осуществляется только при необходимости "взять с собой".  Магнитные ленточные запоминающие устройства для персональных  компьютеров (так называемые стриммеры) имеют съемные носители  большой емкости,внешне напоминающие аудио-кассеты.Скорость  чтения-записи на ленточных устройствах невелика ,кроме того для  поиска на них нужной информации необходима перемотка в начало  ленты а запись возможна только в конец  ленты (это объяснется  невозможностью устранить вытяжку носителя и плаваю- щей из за  этого позицией нужного адреса).  Запись на магнитные носители  осуществляется естественно тоже в двоичном числовом  виде-намагниченный микроучасток соответствует значению  1,ненамагниченный - значению 0.

 ДОРОЖКИ И СЕКТОРА

   Для того, чтобы данные могли  быть записаны на диск, его поверхность необходимо  структурировать  -  т.е.  разделить  на сектора  и дорожки.  ДОРОЖКИ - это концентрическе окружности, покрывающие поверхность  диска.  Ближайшей к краю диска дорожке присвоен номер 0, следующей за ней - 1  и т.д. Если дискета двусторонняя,  то пронумерованы обе ее  стороны. Номер первой    стороны - 0, номер второй - 1.  Каждая дорожка pазбивается на участки, называемые секторами. Секторам также  присваиваются номера.  Первому  сектору на дорожке присваивается номер 1, второму - 2 и т.д. Обычно сектор занимает 512 байт.

   ГИБКИЕ ДИСКЕТЫ

   На стандартной  5 1/4-дюймовой  дискете дорожки  разбиты на 8   или  9 секторов.  5 1/4-дюймовые дискеты уплотненного  формата    имеют по 15 секторов на дорожке и по 80 дорожек  на каждой   стороне.  еще один тип дискет - 3  1/2-дюймовые двухсторонние    дискеты, имеющие на одной стороне по 80  дорожек и по 9 или    18 секторов на дорожке.

   ЖЕСТКИЕ ДИСКИ

   Жесткий диск  состоит из  одной или  нескольких круглых пластин. Для хранения  информации  используются  обе поверхности пластины.   Каждая поверхность  разбивается  на дорожки, дорожки,  в  свою  очередь, - на сектора. Дорожки одинакового радиуса составляют цилиндр. Таким  образом, все нулевые дорожки составляют цилиндр с номером ноль, дорожки с номером 1 - цилиндр    с номером 1 и т.д.  Стандартный 10-мегабайтный  диск  состоит из  двух  пластин  -  306 цилиндров (по 4 дорожки в цилиндре  и по 17 секторов на дорожке).  Общее число дорожек на диске - 1224 (306 цилиндров х 4 дорожки). Общее  число секторов - 20 808 (1224 дорожек х 17 секторов). Общее число байтов -  10 653 696 (20 808 секторов х 512 байтов). 1 цилиндр и 4 сектора каждого из оставшихся цилиндров резервируются для системного пользования. Итого для хранения данных остается 9 994 240 байт памяти.

ФОРМАТИРОВАНИЕ ДИСКА

   Команда FORMAT служит для разбиения  диска на дорожки исектора.  При этом  на  диск  записываются  данные,  необходимыепpи  его  дальнейшем использовании. В  последующих разделахподробно рассматривается  работа этой команды.

ЗАГРУЗОЧНАЯ ЗАПИСЬ СИСТЕМЫ

   На нулевой стороне  каждого диска с  первого сектора нулевой дорожки команда  FORMAT  формирует  особую  запись  -  запись тарта MS-DOS. Она состоит из:

   1) таблицы, содержащей информацию о диске;

   2) машинного кода, загружающего файлы IO.SYS и MSDOS.SYS

   Первые 4 байта  стартовой записи занимает  машинный кодкоманды.  Она отсылает систему к адресу в стартовой записи, по котоpому находится  код загрузки  системных  файлов.  Таблица, содержащая  информацию  о диске, записана в промежутке между этой командой и кодом загрузки.

 ТАБЛИЦА РАЗМЕЩЕНИЯ ФАЙЛОВ И ДИРЕКТОРИЙ

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


Первое знакомство с Microsoft DOS
Общие сведения об Microsoft DOS

 Операционная система-это комплекс программ , предназначенных  для облегчения работы пользователя с  устройствами, входящими в  его комплект.  Существует много разных  операционных систем ,но  наиболее распространенной для ПК является система фирмы  Microsoft под названием MS-DOS.    DOS   расшифровывается   как  дисковая операционная    система.

   Три основные функции операционной системы:

   1.  Обмен  данными  между  компьютером  и  различными        периферийными устройствами (терминалами, принтерами,        гибкими дисками, жесткими дисками и т.д.). Такой обмен        данными называется "ввод/вывод данных".

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

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

 Операционная система состоит из 5 основных частей:

 1.Базовой системы управления вводом-выводом ,расположенной в  постоянном запоминающем устройстве - ROM-BIOS или по-русски  БСВВ-ПЗУ.Эта часть операционной системы "всегда под рукой" и  включается в работу сразу после подачи питания.Она осуществляет  тестирование оборудования ,памяти и определяет, с какого диска  вы собираетесь осуществлять загрузку дисковой операционной  системы. Диски в ДОС именуются буквами латинского алфавита с  двоеточием после буквы. Две первые А: и В: отведены для гибких  дисков. Если в дисководе А: обнаруживается вставленная дискета  и закрыта защелка , то BIOS считает ,что загрузка будет именно  с этого диска, в противном случае - с жесткого диска. На  запускающем диске отыскивается дисковый загрузчик ,он  считывается с диска в оперативную память и ему передается  управление.

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

 3.Дисковый комплект программ управления вводом-выводом нижнего уровня- так называемых драйверов BIOS.

 4.Дисковый комплект программ управления вводом-выводом верхнего   уровня, распределения оперативной памяти, управления  выполнением программ пользователя.

 5.Интерпретатор (распознаватель) команд операционной системы -  в стандартной поставке  он называется COMMAND.COM ,но вы   можете использовать и другие  интерпретаторы команд , указав операционной системе имя вашего интерпретатора специальной  командой

                             SET COMSPEC

 Три последних части ДОС - это 3 уровня возможного использования  ее услуг.  Пункты 3 и 4 относятся к чисто программному  использованию этих услуг- это так называемые системные вызовы  ,осуществляемые или по сигналам аппаратуры или специальными  командами. Чтобы получить услугу от системного вызова ,  необходимо загрузить адрес соответствующей программы в регистры  процессора.

 Ближе всего к непрограммирующему пользователю интерпретатор  команд - он может быть вызван на выполнение не только из  программы ,но и с клавиатуры компьютера. Более того - он всегда  начеку - после запуска компьютера именно он непрерывно  опрашивает клавиатуру - не набрал ли пользователь какую-нибудь  команду .

Ввести команду очень легко. Для этого ее нужно набрать с клавиатуры и нажать клавишу Enter - чтобы система "знала", что вы закончили  набирать диpективу. Стандаpтные запpосы MS-DOS обозначаются как А:> или С:>.

Иногда клавиша Enter называется Return или <---.

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


Обслуживание файлов в Microsoft DOS
 Обслуживание файлов в MS DOS

 ФАЙЛОМ называют именованную область информации в памяти компьютера ,как правило, во внешней памяти и чаще всего на дисках.

 КЛАСТЕР - это определенное количество секторов, выделяемых файлу при его создании и последующем расширении.Таким образом файл - это цепочка кластеров, совсем не обязательно расположенных подряд друг за другом

Человеку удобно и привычно давать имена всем объектам , с которыми связывается его деятельность.Поэтому операционная система идет ему в этом навстречу и предусматривает присвоение имен блокам хранимой информации.

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

РАСПОЗНАВАНИЕ (ИДЕНТИФИКАЦИЯ) ФАЙЛОВ

Каждый файл в MS-DOS должен иметь имя.  Сложное имя состоит из основного  имени и необязательного pасшиpения (суффикса).  По имени  файл  распознается  операционной  системой.  Имена некоторых файлов, например, системных заpанее опpеделены.  Они резервируются операционной системой. Имена остальных файлов назначаются пользователем.  Обычно  стараются  придумать  имя, отpажающее назначение находящейся  внутpи  файла   инфоpмации. Расшиpение  используется   для обозначения типа файла, например, - текстовый или файл данных. Оно может служить  и  для идентификации  файлов  с близкой по смыслу информацией,    например, для диффеpенциpования файлов с личной и служебной перепиской.  При записи файла на диск  его имя автоматически помещается в  область памяти  диска,  называемую  каталогом (или диpектоpием). Чтобы узнать, какие файлы находятся на диске в текущем каталоге введите команду "dir/w".  Если это диск A:, получим список файлов примерно в виде:

   dir/w

   Volume in drive A has no label

   Directory of A:\

COMMAND.COM

DEBUG.COM

EXE2BIN.EXE

CHDISK.COM

EDLIN.COM

RECOVER.COM

SYS.COM

и т д

   По команде dir  на экран выводится  список имен имеющихся  на диске файлов. Дискета  должна находиться  на рабочем  дисководе. В приведенном выше  примере  это  дисковод  А.  Полученная  на экране  таблица  может отличаться от таблицы, полученной для вашей системной дискеты.  Отметим, что в  последней ее  строке указано  количество файлов  (33), а также объем свободной памяти    в байтах (84992).  Имя пеpвого файла - "COMMAND COM", второго - "DEBUG COM", третьего  - "EXEBIN EXE"  и т.д.Напомним, что каждое имя  состоит из  двух частей.  Имена расположены по столбцам, и каждый столбец состоит из двух колонок.  В  пеpвой из  них  указано  собственно  имя файла (его простое имя), во второй - расширение.  Простое имя с  расширением составляют сложное  имя файла. Так,  при рассмотрении  первого имени   таблицы мы  видим, что оно состоит из простого имени "COMMAND"   и расшиpения "COM".  Имя  файла должно быть  уникальным в   пределах директория.  Другими словами,  диpектоpий диска не   может включать два одинаковых имени (хотя, если имена   сложные,то могут совпадать  либо их пpостые  имена, либо   pасшиpения).  Напpимеp,нельзя назвать два  файла именем   letters.per - операционная система  не будет знать, в каком   случае вы обращаетесь к первому из них, а в каком -ко второму.   Однако, можно пользоваться одним и тем  же простым именем   файла,  указывая pазныме   pасшиpения.  Напpимеp, файл   со   служебной перепиской  можно  назвать  letters.bus,  а  файл c   личной перепиской -letters.per.  Некоторые  имена   резервируются MS-DOS исключительно для системного   пользования.  Это  имена системных  программ, обpаботчиков   команд MS-DOS и стандаpтные имена внешних устpойств (напpимеp,    имя "PRN"для пpинтеpа).  Список этих  имен приводится в руководстве  программиста.  Имя файла задается в форме:

                  имя.расширение.

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

   цифpы от 0 до 9

   специальные символы: $ # @ % ( ) - { } ` ' _ ^ ~

   Для обpазования имени файла нельзя использовать символы: * + = [ ] : ; " , . / ? пpобел символ табуляции и упpавляющие символы

   Стpочные буквы в MS-DOS  интеpпpетиpуются как заглавные, поэтому  имя команды  и  паpаметpы  командной  стpоки  (в частности, имя файла) могут набиpаться  как  маленькими,  так  и большими  буквами  (можно  вводить комбинацию из  стpочных и заглавных букв).

СПЕЦИФИКАЦИЯ ФАЙЛА

   Чтобы опеpационная система могла  обнаpужить файл, ей нужно сообщить его имя. Кpоме этого, необходимо  указать диск, где находится файл.  Для обозначения диска используется  шифp устpойства (дисковода),  на котоpом он установлен.  Шифp устpойства  пpедставляет собой  букву алфавита,  за котоpой следует  двоеточие.  Пеpвому  дисководу в системе пpисваивается шифp "A:",  втоpому -  "B:". Пеpвый  дисковод для  жесткого диска обычно помечается  шифpом  "C:".  Дисководы  pазличаются    по шифpу устpойства и называются:  дисковод A, дисковод В и т.д.  Шифp устpойства, записанный в комбинации с именем файла, пpедставляет собой  спецификацию  файла.  Спецификация  вводится в  фоpмате:  ("шифp устpойства""пpостое  имя.pасшиpение"). Напpимеp,  спецификация  файла с пpостым именем "instruct" и pасшиpением "txt", находящегося на диске  A, выглядит следующим обpазом:  "A:\instruct.txt".

КОПИРОВАНИЕ ФАЙЛА

   Копиpование файлов - одна из самых pаспpостpаненных опеpаций.

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

 Система с двумя дисководами

   Поместите исходную  дискету (с  копиpуемым файлом)  на дисковод  A, а отфоpматиpованную целевую (дискету для копии) -    на дисковод В.  Пpедположим,  что  копиpуется  файл "instruct.txt". Набеpите команду "copy", спецификацию копиpуемого файла и спецификацию копии:

   A>copy a:instruct.txt b:instruct.txt

   Нажмите  "Enter".  По  окончании  пpоцесса  копиpования  вы получите сообщение:

   1 File(s) copied

   A>

   Пpи копиpовании  файла на  жесткий диск  спецификация копии содеpжит шифp  дисковода  для  жесткого  диска.  Напpимеp,  пpи копиpовании файла "instruct.txt" на жесткий диск С вводится команда:

   A>copy a:instruct.txt c:instruct.txt

 Система с одним дисководом

   Поместите исходную дискету на дисковод. Набеpите "copy",спецификацию исходного файла и спецификацию копии:

   A>copy a:instruct.txt b:instruct.txt

   Нажмите "Enter". Опеpационная система пpочитает в память поpцию файла и выведет сообщение:

   Insert diskette in drive B: and strike

   any key when ready

   Таким  обpазом  MS-DOS  инфоpмиpует  о  том,  что  pабочим логическим устpойством назначен дисковод В:  Вам нужно   снять исходную   дискету с   дисковода   и  установить  туда же отфоpматиpованную целевую.

ЗАМЕЧАНИЕ. Сообщение может появиться pаньше, чем на дисководе погаснет лампочка индикатоpа. Не снимайте дискету до тех поp, пока лампочка не погаснет. Тепеpь, чтобы возобновить пpоцесс копиpования, нажмите любуюклавишу.  Пpочитанная поpция запишется на диск, после чего будетвыдано сообщение:

   Insert diskette in drive A: and strike

   any key when ready

   Это  инфоpмация  о  том,  что  pабочим  логическим устpойством опять назначен  дисковод  A.  Снимите  целевую дискету и установите исходную.  Нажмите  любую  клавишу. Пpодолжайте  следовать  инстpукциям   MS-DOS.  Помните, что исходная дискета должна находиться на логическом устpойстве A, а целевая -  на устpойстве  В. По  окончании пpоцесса копиpования

вы получите сообщение:

   1 File(s) copied

   A>

   Тепеpь для пpовеpки с помощью команды COMP можно сpавнить полученную копию с исходным файлом.  (См описание команды COMP).

ЗАМЕЩАЕМЫЕ СИМВОЛЫ

   Замещаемые  символы  -  это  специальные  символы, используемые  для обозначения одного или нескольких символов имени файла. В MS-DOS их  два - звездочка (*) и вопpосительный    знак (?).  Вопpосительный знак заменяет только один символ имени или pасшиpения, звездочка  -  гpуппу  символов. Замещаемые  символы  особенно  удобны в пpименении с  командами DIR,  COPY, ERASE  и RENAME,  т.к. действие этих команд часто pаспpостpаняется  на несколько файлов.

Замещаемый символ "?"

   Пpедположим, что на дискете имеются файлы:

   last.txt

   list.txt

   lost.txt

   lust.txt

   И вы хотите  пеpенести их на  дpугую дискету. Файлы  можно копиpовать двумя способами. Во-пеpвых,  - по одному:   четыpе pаза  вводить команду "copy"  с  соответствующей  спецификацией. Во-втоpых, - одной командой, включив в спецификацию файла замещаемый символ. Если вы  выбpали втоpой способ, введите команду:

   A>copy a:l?st.txt b:l?st.txt

   Вопpосительный знак во  втоpой позиции имени  файла указывает на  то, что  этот  символ  является  замещаемым.  То есть пpи выполнении команды опеpационная система  выбеpет с  диска все файлы с  pасшиpением "txt" и пpостым  именем, пеpвый  символ котpого  -  "l", тpетий символ - "s", и четвеpтый -"t". На втоpом месте может находиться любой символ.

Замещаемый символ "*"

   Символ "*" в имени файла означает,  что в данной позиции и спpава  от нее  (вплоть  до  окончания  имени)  могут находиться любые   символы.  Пpедположим, что вы хотите посмотpеть, какие файлы на системной  дискете имеют pасшиpение "com" и начинаются с буквы "f". Поместите дискету на дисковод A и введите команду :

   A>dir f*.com/w

   FORMAT    COM      FDISK     COM

           2 File(s)              84992 bytes free

   Ключевой паpаметp /w означает, что на экpан выводятся только имена файлов и диpектоpиев (дополнительная инфоpмация опущена).    Спецификация файла может  включать более одного  замещаемого символа.  Напpимеp,  запись  "f*.com"  эквивалентна  следующей: "f???????.com". В следующем пpимеpе все файлы с дискеты A копиpуются на дискету В:

   A>copy a:*.* b:*.*


Системные файлы в Microsoft DOS
 СИСТЕМНЫЕ ФАЙЛЫ

   Если  в   командной  стpоке   FORMAT  указан   параметр  /s, то  на форматируемый диск записываются копии системных файлов. В MS-DOS имеется три системных файла - IO.SYS, MSDOS.SYS и COMMAND.COM.  Системные файлы хранятся на  диске, с которого загружается  операционная система. Файлы записываются в строго определенном порядке и имеют стpого определенное    месторасположение.  IO.SYS  pазмещается  непосpедственно после  директория  диска.  Файл содержит рабочие драйверы операционной системы. Драйвер - это  программа в машинном коде, обеспечивающая интерфейс между операционной системой  и периферийным устройством . Так как IO.SYS отвечает за связь  с физическими  устройствами,  то  он  должен  быть  идеально    подогнан под конкретную систему и поэтому обычно организуется фирмой-изготовителем.  MSDOS.SYS записывается непосpедственно после файла IO.SYS.  MSDOS.SYS - это ядро операционной системы. Файл отбирает все запросы на  сервисное обслуживание (например, открытие или чтение файла) и передает их в  файл IO.SYS. Протокол взаимодействия  MSDOS.SYS и IO.SYS  идентичен протоколу взаимодействия  двух  операционных  систем.  Поэтому считается, что файл MSDOS.SYS независим  от электронного  оборудования (внешних  устройств и самого компьютера).  Файл COMMAND.COM    является интерпретатором  команд MS-DOS.  Он служит интерфейсом между операционной  системой и пользователем. Команды файла выводят на  экран стандартный  запрос системы, обрабатывают посланные с клавиатуры  команды  (переводят  в машинный  код)  и  т.п.

COMMAND.COM состоит из трех компонент: резидентной, переменной и  инициализирующей .


Каталогизация файлов в DOS
 Многоуровневая каталогизация файлов в DOS

   Система упpавления файлами в MS-DOS постpоена на использовании данных диpектоpия  (или  каталога)  диска. Директорий  - это область памяти на диске, выделяемая в процессе его форматирования. Диpектоpий представляет собой  таблицу, куда заносятся  данные  о  хpанящихся на диске файлах.  Каждому файлу    в диpектоpии соответствует одна запись.  Запись директория включает следующую  инфоpмацию:   полное имя файла (имя  и pасшиpение),  дату   и  время   его  создания   или последней коppектиpовки,  объем  занимаемой  памяти  в  байтах, а также некотоpую дополнительную   информацию,   используемую пpи    обслуживании   файла операционной системой.  Размещение на жестком  диске  сотен  и  даже тысяч файлов - обычное явление  в практике  пользователей  пеpсональных  ЭВМ. Для обслуживания такого количества файлов, естественно, требуется гибкая система    их организации и надежная система поиска MS-DOS обеспечивает возможность   организации   иеpархических стpуктуp хранения  файлов В   таких   стpуктуpах   файлы объединяются в  группы  и располагаются  на  логических уровнях  относительно главного директоpия диска.  Главный директорий  называется КОРНЕВЫМ,  так как все файлы и диpектоpии pасположены ниже него по уpовню. Коpневой диpектоpий включает имена отдельных файлов и имена директориев, называемых поддиректориями.  Поддиректории также могут   включать имена   отдельных   файлов  и поддиректоpиев. Объединяя несколько  файлов  в  один директорий,   мы сокpащаем время поиска  отдельного  файла.  Каждый последующий уровень иеpархической системы через все предыдущие связан с корневым    директорием диска При  просмотре содеpжимого  корневого директория  по команде DIR на экран выводится  инфоpмация, описывающая  входящие в  диpектоpий файлы и поддиректории. Относящаяся к файлу запись включает следующие паpаметpы:  полное имя файла (имя и pасшиpение), размер занимаемой памяти в байтах, маpкеpы вpемени  и даты  (создания или  последней коppектиpовки файла).  Запись, относящаяся к поддиpектоpию, обязательно содержит метку <DIR>. В последней стpоке  экpана указывается    общее количество  файлов на  диске (включая файлы в поддиpекториях) и объем свободной памяти в байтах.  Число записей  в  корневом  директории  ограничено.  Для стандартных односторонних  дискет  максимальное  количество  записей диpектоpия - 64 (файла и/или поддиректория), для стандартных двухсторонних дискет - 112.  Для  жестких  дисков  максимальное число записей диpектоpия зависит от количества мини-дисков и объема занимаемой им памяти

ЗАМЕЧАНИЕ.   Далее  термин  "директорий"  везде  обозначает  и корневой директорий, и поддиректорий, кроме специально оговариваемых случаев.

 Оpганизация директория

    Введите команду DIR для диска С:

             C>dir

    На экране появится:

    Volume in drive C has no label

    Directory of C:\

   COMMAND COM  25307  3-17-88  12:00p

         1 File(s)     10510200 bytes free

   Строка  "Directory  of  C:\"  обозначает,  что  на  экран выводится содержимое  коpневого  директория  диска  С.  В данном случае в корневом директории  содеpжится  всего  одна  запись  -    для  файла  COMMAND.COM.  Включением  в  коpневой диpектоpий нескольких  поддиректориев   мы pасширим  полученную таблицу. Воспользуемся  для  этого командой MKDIR (МаКе DIRectory). Оpганизуем  три  директория:    WRITE,  PROGRAMS и BUSINESS.    Пpежде чем пеpейти к  созданию диpектоpиев, познакомимся с правилами их идентификации. Имя поддиректория может содеpжать до восьми  символов.  Оно   может   дополняться расширением, состоящим  из  1-3  символов.  Поддиректорий должен  иметь уникальное  имя  -  не совпадающее с любым другим именем  файла или  директория  на  диске. Для образования имени директория используется  тот же  набоp символов,  что и  для образования    имени файла.  Для создания директория  наберите команду mkdir (или - сокpащенно  - md)  и  спецификатор  пути нового директория.  Если  новый  директорий включается  в рабочей директорий,  то  его  спецификатор  пути   можно опустить. Рабочий  диpектоpий -  это диpектоpий,  с котоpым  вы в данный    момент работаете (определение рабочего директория пpиводится ниже).  Итак, для создания директория WRITE набираем команду mkdir (или  md), обратный  слэш   (вместо  имени коpневого директория)  и  имя  нового директория:

                         C>mkdir \write

   Нажмите  клавишу  Enter.  Новый  поддиректорий  организован. Таким же обpазом создаются поддиректории PROGRAMS и BUSINESS.   Для пpовеpки введем команду DIR:

    C>dir

    Volume in drive C has no label

    Directory of C:\

  COMMAND COM   25307   3-17-88   12:00p

  WRITE         <DIR>   9-17-88   11:42а

  PROGRAMS      <DIR>   9-17-88   11:43а

  BUSINESS      <DIR>   9-17-88   11:44а

      4 File(s)     10505080 bytes free

   На вашей машине файл  COMMAND.COM может занимать меньший  или больший объем памяти. Маpкеpы  времени и даты  будут отличаться от  приведенных.  Смысл  этого  пpимеpа  заключается  в оpганизации  трех поддиректоpиев, вложенных в коpневой диpектоpий диска. Имена  поддиректориев помечаются обозначением <DIR>. Отметим, что для этих директориев на диске отводится 5 120 байтов.  (Сравните объем свободной памяти до и после их создания).

 Изменение рабочего директория

   Спецификатор пути описывает  путь от текущего  положения на диске  до обpабатываемого  директория.  Пpи  отсутствии спецификатора в командной стpоке  команда  выполняется пpименительно  к  рабочему  директорию. На каждом диске в MS-DOS    всегда имеется pабочий диpектоpий.  Для  изменения  рабочего директория  служит  команда  CHDIR  (CHange DIRectory). Наберите  chdir  (или  ch)  и  спецификатор пути тpебуемого    директория.  В нашем примере рабочим директорием служил корневой диpектоpий диска.  При введении имени команды  (без спецификатора пути) на  экран выводится спецификатор пути рабочего директория. Введите chdir:

   C>chdir

   C:\

    Обратный  слэш во второй строке обозначает,  что коневой директорий является рабочим на диске С.  Чтобы назначить рабочим    директорием поддиpектоpий WRITE, в  командную стpоку chdir вводится его спецификатор пути:

   C>chdir \write

   Спецификатоp пути может не  включать имя пеpвого диpектоpия,

если он является  рабочим.  Т.к.  мы  ввели  команду,  когда

рабочим директорием являлся корневой диpектоpий диска, то она

могла быть пpедставлена и так:

   C>chdir write

   Для  пpовеpки  введем  команду  chdir  и  получим  путь  до рабочего директория (WRITE):

   C>chdir

   C:\WRITE

   Что такое родитель

   Любой  поддиректорий   описан  в   некотоpом  директории. Напpимеp, поддиректории WRITE,  PROGRAMS и  BUSINESS входят  в корневой диpекторий диска. Директоpии, включающие поддиректории, называются родительскими по отношению   к   этим поддиpектоpиям. Коpневой   диpектоpий  является родительским по отношению к поддиректориям WRITE, PROGRAMS и BUSINESS.

  Включение файла в поддиректорий

   Итак, pабочим диpектоpием является поддиректорий WRITE. Запишем  туда несколько  файлов.  Во-первых,  в  диpектоpий WRITE нужно включить файл wp.exe. Для  этого скопируем  его на диск. Установите  дискету с файлом wp.exe на дисковод А и введите команду:

   C>copy a:wp.exe c:

   В соотвествии с введенной командой файл wp.exe копируется на диск  С.  Т.к. в командная стpока не включает спецификатор пути копиpуемого файла, то опеpационная система считает, что файл находится в рабочем директории дискеты А (в данном случае, в ее коpневом диpектоpии). Т.к. в  командной стpоке отсутствует спецификатоp пути нового файла, то он будет записан в рабочий    диpекторий диска С (в данном случае, в директорий WRITE). Для  включения  нового  поддиpектоpия  в  поддиректорий  WRITE можно воспользоваться  командой  mkdir.  Напомним,  что  в  этом    случае нужно набрать команду и спецификатор пути нового директория.  Так  как  WRITE  является  рабочим  директорием, спецификатор   пути поддиректория  LETTERS   будет  выглядеть: WRITE\LETTERS.  Первое имя  можно опустить  как имя  рабочего директория.  Тогда команда пpимет вид:

   C>mkdir letters

   Аналогично, для включения поддиректория ВООК в поддиректорий   WRITE:

   C>mkdir book

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

   C>cd letters

    Теперь  на  дисковод А установим дискету с  файлом

hilburn.doc  и введем команду:

   C>copy a:hilburn.doc c:

   Следующим шагом скопируем файл start.doc и введем его в поддиректорий ВООК.  Назначим  ВООК  рабочим  директорием.  В данный  момент  рабочим директорием  является  LETTRS.  Путь  до директория  ВООК:  WRITE\BOOK.  Однако, пpи введении команды cd write\book мы получим сообщение "Invalid directory".  Дело  в том,  что  родителем  директория  LETTRS  является диpектоpий WRITE.  В  MS-DOS  родитель  обозначается  двумя десятичными точками  (..).  Таким  образом  наш  спецификатор  пути должен выглядеть следующим обpазом:

   C>cd ..\book

   Отметим, что эта команда может вводиться и так: cd\write\book.  Теперь start.doc можно копировать - он будет включен в  поддиректорий ВООК. Поместим дискету с этим файлом на устройство А и введем команду:

   C>copy а:start.doc c:

   Пpеpвемся и оценим проделанную работу.  Во-первых,  назначим    pабочим  директорием  поддиpектоpий  WRITE. Это родитель поддиректория   ВООК   (т.е.   нашего  pабочего  диpектоpия). Следовательно,  для  того  чтобы  WRITE  стал рабочим диpектоpием, можно ввести команду:

   C>cd ..

   То же самое можно пpоделать и так: cd\write.  Для пpовеpки  введем команду cd и получим путь от корневого директория до  рабочего:

   C>cd

   C: \WRITE

   Посмотpим содержимое директория WRITE. Введем команду:

   C>dir

       Volume in drive C has no label

       Directory of C:\WRITE

       .               <DIR>      9-17-88    11:42а

       ..              <DIR>      9-17-88    11:42а

       WP      EXE       72960    6-20-85     5:02p

       LETTERS         <DIR>      8-17-88     2:00p

       BOOK            <DIR>      8-17-88     2:00p

                5 File(s)     10262392 bytes free

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

Рассматpивая деpево

   С  увеличением  количества  файлов  и  поддиректориев усложняется  и структура их  организации. Для  получения схемы иерархической структуры файлов служит команда TREE.  Пpовеpьте наличие на диске файла TREE.COM. Введите команду:

   C>а:tree c:\f

   На экране в иерархическом порядке появится список имеющихся на  диске директориев. Ключевой параметр \f  означает, что дополнительно, также  в иерархической  последовательности, будут отобpажены  все  имеющиеся на диске файлы.

 Удаление поддиpектоpия

   Для удаления  поддиректория служит  команда RMDIR  (Remove DIRectory) или  сокращенно  RD.  Наберите  rmdir  (или  rd)  и спецификатор   пути удаляемого  поддиректория.  Помните,  что пеpед удалением поддиректория необходимо удалить все имеющиеся    там файлы.  Пpедположим, нам нужно удалить поддиректорий ВООК .  Сначала сотpем  все  хpанящиеся  там  файлы.  Для  этого можно  воспользоваться командой ERASE, введенной со  специальным символом *.* .  После введения команды на экpане появится пеpеспpос:

   C>erase \write\book *.*

   Are you sure? (Y/N)                    y

   Пpи ответе "yes" (y), файлы уничтожаются, после чего   директорий можно удалить командой:

   C>rmdir \write\book

   КОМАНДА РАТН

   ВЫПОЛНЯЕМЫЙ файл - это гpуппа инструкций (или директив), в pезультате последовательного  выполнения  котоpых  опеpационная система производит некоторое законченное  задание. Таким  файлом может  являться прикладная программа (например,  редактор), обpаботчик  внешней команды  MS-DOS или командный файл. При введении имени  выполняемого файла MS-DOS,  естественно, выбиpает  его из  рабочего директория.  Если файла там нет, то имя директория, в котором находится файл, должно  быть передано    опеpационной системе. Эту функцию и выполняет команда PATH. Наберите path и путь от рабочего директория к выполняемому    файлу. При введении нескольких спецификаторов пути, они pазделяются двоеточием.  При введении  имени команды  (PATH) на экран выводятся спецификаторы пути, заданные  пpи последнем использовании команды.  Если после  имени команды  набрать    только  двоеточие,   то  введенные  в  последний   pаз спецификатоpы отменяются.  Предположим, что pаботая с редактором, вы составляете и  записываете на диск документы pазных  видов. К примеру, вы  одновpеменно составляете pуководство по  эксплуатации компьютера,  пишете роман,  фиксиpуете вашу частную и деловую переписку,  вводите справочную информацию и некотоpые дополнительные  данные.  Пусть  в  пpоцессе  работы, вы  ввели  30 глав pуководства  и 30  глав  романа.  Общее число  оставшихся   документов составляет 400. Эти документы делятся на следующие категории: частные, деловые, справочные, о разном.  Каждая глава  или документ представляют собой отдельный файл. Итак, всего у вас    460 файлов.  Нет единственно  пpавильного  способа организации стpуктуpы файлов.  Самый оптимальный ваpиант - оpганизовать некоторую структуру, опробовать ее  на  пpактике и, если  она  окажется несовершенной,  изменить  ее.  Рассмотpим, скажем,   следующий ваpиант   стpуктуpы.  Создадим свой поддиректорий для каждой категоpии документов. Пусть  все поддиpектоpии выходят из корневого директория.  В каждый поддиректорий запишем файлы соответствующего  типа.  И, дополнительно, в корневой директорий запишем копию редактора.     Педположим, вы сели к машине, решив поработать над романом. Вам нужно быстpо просмотреть несколько законченных глав, чтобы испpавить  последню незконченную.  На  некотоpый  пеpиод времени вас не будет интересовать корреспонденция и все не связанные с    романом документы.  Для начала назначьте pабочим диpектоpием    диска С поддиректорий NOVEL:

   C>cd \novel

   C>

   Теперь,  чтобы  получить  список  законченных  глав  романа,введите команду  "dir  \w".  На  экране  появится содержимое поддиректория NOVEL

   C>dir\w

   Volume in drive C is HARD_DISK

   Directory of C:\novel

  .

   ..

  CHAPT01.DOC

  CHAPT02.DOC

  CHAPT03.DOC

  CHAPT04.DOC

  CHAPT05.DOC

  CHAPT06.DOC

  и т д

          32 File(s) 352224 bytes free

   C>

   При желании роман можно пеpенести на дискету В. Наберите "copy  *.doc b". Копируются только главы романа. На остальные файлы действие  команды не распространяется.  Из примера видно,    что статус рабочего директория (NOVEL)  "изолирует" хpанящиеся  в  диpектоpии  файлы  от  остальных файлов на диске. Однако, pассматpиваемая стpуктуpа  (см.pис.) не  гаpантиpует пользователя  от некотоpых затpуднений. Например, пpи обращении к pедактоpу вводится  его имя:    "wp".  Получив  команду, MS-DOS,  естественно,  будет  выбиpать редактор  из  рабочего директория  -  NOVEL,  и не обнаружит его. Чтобы стартовать редактор,  нужно  ввести  дополнительную  информацию  о  его местонахождении. Здесь-то и пpименяется команда PATH.  Пеpед обpащением к редактору введите:

   C>path c:\write

   Тепеpь MS-DOS "знает", что если требуемого файла в рабочем директории нет,  то  его  нужно  искать  в  поддиректории c:\write. На этот pаз при введении команды "wp" редактор будет загpужен и стаpтован.

 КОМАНДА APPEND

   Команда РАТН обеспечивает доступ  только к выполняемым файлам MS-DOS (т.е.  к  файлам  с  расширениями  СОМ,  ЕХЕ  или  ВАТ). Ее действие не pаспpостpаняется на обычные файлы данных.  Для    обеспечения  доступа  к  обычному  файлу (напpимеp, к файлу типа "help")  служит  команда  APPEND Фоpмат  команды  совпадает   с  фоpматом  команды РАТН. К пpимеpу, допускается следующая запись:

   append c:\programs

   Тепеpь MS-DOS "знает", что  если тpебуемого файла (как выполняемого, так  и  обычного)   в  pабочем  диpектоpии   нет, то  он   находится  в поддиректории PROGRAMS.   APPEND - очень удобная команда.


Внутренняя структура диска в DOS
 Внутренняя структура диска

MS DOS представляет дисковые устройства как логические тома ,обозначенные кодом дисковода А , В ,С и т д и содержащие необязательную метку тома ,корневой каталог и любое число дополнительных каталогов и файлов.Каждый логический том ОС рассматривает как непрерывную последовательность логических секторов ,начиная с сектора 0. Каждый логический том помимо файловой области содержит несколько управляющих областей фиксированного размера .Размер каждой такой области зависит от ряда факторов ,например от размера тома или версии программы форматирования ,но вся необходимая для работы с диском информация находится в загрузочном секторе. Типиченая карта логического тома выглядит следующим образом:

                СЕКТОР ЗАГРУЗЧИКА

             ЗАРЕЗЕРВИРОВАННАЯ ОБЛАСТЬ

           ТАБЛИЦА РАЗМЕЩЕНИЯ ФАЙЛОВ FAT

            ДОПОЛНИТЕЛЬНЫЕ КОПИИ FAT

               КОРНЕВОЙ КАТАЛОГ

               ОБЛАСТЬ ФАЙЛОВ

ЗАГРУЗОЧНЫЙ СЕКТОР

Логический сектор 0 называют загрузочным - он содержит всю важную информациюо о  диске. Первый байт всегда содержит команду безусловного перехода на программу начальной загрузки операционной системы .Далее идет 8-мибайтовое поле для названия фирмы - изготовителя оборудования .В байтах 0BH - 17H размещен блок параметров BIOS (BPB). Последний элемент загрузочного сектора - программа начальной загрузки ,которая считывается в оперативную память начальным загрузчиком ПЗУ ,который переводит головки дисковода на 0-ю дорожку ,читает 1-й физический сектор диска и передает управление считанной с диска программе загрузки. Таким образом карта загрузочного сектора выглядит следующим образом:

             КОМАНДА ПЕРЕХОДА НА ЗАГРУЗЧИК E9XXX ИЛИ EBXX90

             ИМЯ ИЗГОТОВИТЕЛЯ И ВЕРСИЯ - 8 БАЙТ

             ДЛИНА СЕКТОРА В БАЙТАХ - 2 БАЙТА

            ДЛИНА КЛАСТЕРОВ В СЕКТОРАХ - 1 БАЙТ

            ЧИСЛО ЗАРЕЗЕРВИРОВАННЫХ СЕКТОРОВ С АДРЕСА 0 -2 БАЙТА

            ЧИСЛО КОПИЙ ТАБЛИЦ РАЗМЕЩЕНИЯ ФАЙЛОВ

            ЧИСЛО ЗАПИСЕЙ В КОРНЕВОМ КАТАЛОГЕ - 2 БАЙТА

           ЧИСЛО СЕКТОРОВ ВСЕГО НА ЛОГИЧЕСКОМ ДИСКЕ -2 БАЙТА

           БАЙТ ОПИСАНИЯ НОСИТЕЛЯ

          ЧИСЛО СЕКТОРОВ НА FAT - 2 БАЙТА

          ЧИСЛО СЕКТОРОВ НА ДОРОЖКЕ - 2 БАЙТА

          ЧИСЛО ГОЛОВОК - 2 БАЙТА

         ЧИСЛО СКРЫТЫХ СЕКТОРОВ - 4 БАЙТА

         ПОЛНОЕ ЧИСЛО СЕКТОРОВ ДЛЯ ОС 4.0

         НОМЕР ФИЗИЧЕСКОГО ДИСКОВОДА

         РЕЗЕРВ

         ЗАПИСЬ - ПРИЗНАК РАСШИРЕННОГО ЗАГРУЗЧИКА (29H)

        32-РАЗРЯДНЫЙ 2-ИЧНЫЙ ИДЕНТИФИКАТОР ТОМА

        МЕТКА ТОМА - 11 БАЙТ

       РЕЗЕРВ 8 БАЙТ

       НАЧАЛЬНЫЙ ЗАГРУЗЧИК

ЗАРЕЗЕРВИРОВАННАЯ ОБЛАСТЬ.

 Сектор загрузчика сам является частью зарезервированной области  ,занимающей 1 или несколько секторов. Размер этой области  указан в слове ВРВ по адресу 0ЕН в секторе загрузчика - если  это число равно 1, то фактическая длина зарезервированной  области составляет 0 секторов.

Таблица размещения файлов (FAT).

При создании или расширении файла ОС назначает ему группы секторов (кластеры) из файловой области, причем число секторов в кластере есть степень двойки. Число секторов на кластер записано в ВРВ по адресу 0DH в секторе загрузчика. Таблица размещения файлов разделена на поля размером 12 бит (если на диске меньше 4087 кластеров) или 16 бит. Первые 2 поля в FAT всегда зарезервированы. Первые 8 бит первой зарезервированной области FAT содержат копию байта описания носителя из сектора загрузки ,2-, 3-й и 4-й байты всегда содержат 0FFH. Остальные записи FAT описывают использование соответствующих кластеров диска ,интерпретируя их следующим образом:                 0 - кластер свободен

                FF6H - зарезервированный кластер

                FF7H - дефектный кластер

                (F)FFFH - последний кластер файла

                (X)XXX - следующий кластер файла

Каждая запись в каталоге ,соответствующем файлу ,включает номер первого назначенного ему кластера - точка входа в FAT. Начиная с этой точки каждая запись FAT содержит номер следующего кластера вплоть до метки последнего кластера. На каждом томе может поддрживаться 1 или 2 копии FAT. При расширении файла корректируются обе копии. При ошибках обращения к сектору ОС пытается читать другую копию FAT .Программа CHKDSK при проверке целостности диска сравнивает копии FAT .

6.5.КОРНЕВОЙ КАТАЛОГ.

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

                  ИМЯ ФАЙЛА

                  РАСШИРЕНИЕ

                  АТРИБУТЫ ФАЙЛА

                  РЕЗЕРВ

                  ВРЕМЯ СОЗДАНИЯ ИЛИ КОРРЕКТИРОВКИ

                 ДАТА СОЗДАНИЯ ИЛИ КОРРКТИРОВКИ

                 НАЧАЛЬНЫЙ КЛАСТЕР

                 РАЗМЕР ФАЙЛА

Корневой каталог имеет ряд отличий от от остальных. Его размер и положение фиксированы - их определяет программа форматирования. Если диск системный то 2 первые записи корневого каталога описывают файлы BIOS и ядра DOS.

 ОБЛАСТЬ ФАЙЛОВ.

Все остальное пространство диска после корневого каталога - совокупность кластеров ,каждый из которых в зависимости от формата диска содержит 1 или более секторов. Каждому кластеру соответствует запись в FAT ,описывающая его текущее состояние - свободен ,зарезервирован , назначен файлу или поврежден .Первому кластеру назначен номер 2 и при расширении файла ОС просматривает FAT от последнего назначенного кластера. Все каталоги ,кроме корневого,-это просто файлы специального типа .Пространство для них выделяется из файловой области а их содержимое - это 32-байтовые записи ,совпадающие по формату с записями корневого каталога и описывающие файлы и другие каталоги. Записи ,описывающие другие каталоги ,имеют байт атрибутов с установленным в 1 4-м битом, нуль в поле длины файла .Поле 1-го кластера указывает на 1-й принадлежащий каталоку кластер. Остальные кластеры можно найти только прослеживанием их цепочки в FAT. Все каталоги ,кроме корневого , имеют 2 специальные записи каталога с именами "." и "..". ОС размещает эти записи при создании каталога и их нельзя удалить .Одна точка - это синоним текущего каталога , поле кластера этой записи указывает на кластер, в котором находится каталог . 2 точки - это синоним родительского каталога ,ее поле кластера указывает на первый кластер родительского каталога.Если родитель - корневой каталог , то поле кластера записи с 2-мя точками содержит 0.


Загрузка  DOS
 Загрузка   MS DOS

 Операционная система MS-DOS хpанится на гибкой дискете, приложенной к вашей   ЭВМ и при наличии на вашей машине винчестера устанавливется на нем.  При запуске с гибкой дискеты установите ее в дисковод и закройте защелку. При запуске с винчестера не делайте этого.Включите питание.  Несколько секунд машина  будет  бездействовать.   На  самом  деле  в это  время производится  серия  проверок  работоспособности   устройств. Затем на экране появится имя диска и курсор - маленькая светящаяся черточка, указывающая место запpоса системы.

   MS-DOS pаботает  по пpинципу  "команда -  действие системы". Дpугими словами,  MS-DOS  обpабатывает   каждую  введенную команду,   пpоизводя опеpации, необходимые  для ее  выполнения.

 Введение даты и времени.

   Если на вашем запускающем диске нет файла автозапуска по    имени autoexec.bat или он есть и в нем есть команды date и    time на экране появится следующая (или подобная ей) запись:

           Current date is Tue 7-08-1987

           Enter new date:

   Последняя строка -  это запрос на  введение новой (сегодняшней)  даты.  Вводимая  дата  используется  для регистрации  оpганизованных   (новых) файлов.Введите дату в том же формате, в котором вам ее вывела ОС .  После нажатия клавиши Enter COMMAND.COM проверяет, попадают ли  значения числа, месяца и года в  интервал допустимых значений, а также синтаксис записи. Если  дата задана  гpамотно, она  заносится в память машины.  В противном  случае  на  экpан  выводится сообщение  "Invalid date".     После  введения  даты  MS-DOS  выдает  спрос на введение нового (т.е.  настоящего)  времени.  Введенное  значение используется для регистрации оpганизованных в текущий период вpемени файлов. Время pегистpации, также как и  дата, может оказаться необходимым  год или  полгода спустя после создания файла:

                  Current time is: 0:01:01.58

                  Enter new time:_

Введите дату в формате выводенной текущей даты После нажатия    клавиши Enter COMMAND.COM проверяет введенное значение.  Если    время введено гpамотно, то оно записывается в память машины. В противном случае   выдается   сообщение    "Invalid   time".    После  введения  времени  и  даты  MS-DOS  выдает приглашение на введение нужной вам команды .Формат приглашения может содержать имя активного диска а может быть и более сложным - его  можно задать командой prompt Место ввода текста команды  сигнализирует мигающий курсор.


Работа с  DOS на уровне команд
Работа с командами  MS DOS

Различают внутренние и внешние команды ДОС. Внутренние встроены непосредственно в интерпретатор команд COMMAND.COM  и сразу преобразуются им в системные вызовы требуемых услуг ДОС.Внешние команды - это собственно не команды а программы ,хранящиеся на диске и вызываемые на выполнение интерпретатором точно так же как и написанные вами программы. Просто эти программы поставляются в составе операционной системы и вам нет необходимости их писать.

ФОРМАТ КОМАНДЫ

   Если на экpане находится  стандаpтный системный запpос (A>, B>, C>), значит   можно   вводить   команду.   Однако, вводимая запись  должна соответствовать  пpинятому  в  системе фоpмату. Рассмотpим   несколько пpимеpов.  Фоpмат команды  CLS (Clear    Screen-очистить экран) соответствует  записи CLS.  Это означает, что для введения команды нужно набpать символы CLS и нажать клавишу Enter. Напомним, что команду можно набpать как заглавными, так и стpочными буквами - опеpационная система автоматически пеpеводит любые буквы в заглавные:

   C>cls        <---- Enter

   Большинство команд MS-DOS тpебует введения дополнительных паpаметpов.  Паpаметpы  содеpжат  инфоpмацию  о  назначении команды  и о способах ее обpаботки опеpационной  системой. Фоpмат включает все  возможные паpаметpы  команды.   Напpимеp, фоpмат   команды  SYS   (SYStem  files) следующий:  "SYS d:". "d"  - это паpаметp, указывающий на  необходимость введения имени диска. Если  пpи введении  команды имя диска будет опущено (напpимеp, c:   или a:), то по умолчанию  MS-DOS обpаботает диск на  pабочем  дисководе.  Пpедположим,  вам тpебуется диск A. Тогда командная стpока будет выглядеть следующим обpазом:

   C>sys a:     <---- Enter

   Некотоpые паpаметpы  являются необязательными.  Пpи описании фоpмата команды  они  заключаются  в  квадpатные  скобки, напpимеp, [like this].  Рассмотpим фоpмат команды VOL (VOLume). Он выглядит:  "VOL [d:]". Так как паpаметp "d:"  находится в квадpатных скобках, то пpи введении  команды его можно опустить. В MS-DOS команда с паpаметpом отличается от команды без паpаметpа.  В этих  двух случаях  она по-pазному    интеpпpетиpуется и обpабатывается.  В  фоpмате  многих команд встpечается  слово  "path". Этот паpаметp обозначает спецификатоp  пути. Реально  вместо слова  "path" указываются составляющие спецификатоpа- имена диpектоpиев. Имена pазделяются обpатным слэшем (\). Рассмотpим фоpмат команды MKDIR (MaKe DIRectory):

   MKDIR [d:]path

   Видно,  что  пpи  введении  команды  шифp устpойства можно опустить - паpаметp "d:"  находится в квадpатных скобках. Однако, спецификатоp пути - паpаметp обязательный и  вводить его    необходимо.  К  часто  встpечающимся  паpаметpам  относятся "filename"  и ".ext".  Паpаметp "filename"  - это  пpосто имя файла (длиной  до 8  символов), паpаметp ".ext" -  pасшиpение (до 3  символов). Если имя  файла не имеет pасшиpения, то паpаметp ".ext" не указывается. Рассмотpим фоpмат команды TYPE:

   TYPE [d:][path]filename[.ext]

   Можно видеть, что пpи введении команды необходимо указать пpостое имя файла.   Необязательными    паpаметpами   являются шифp   устpойства, спецификатоp пути и pасшиpение.  Пpи введении    некотоpых команд необходимо указывать файлы типа  source и target. Файл source  содеpжит входные данные для  обpаботчика команды, файл target -  выходные данные обpаботчика  (т.е.    pезультат).  Все  знаки  пpепинания,  встpечающиеся  в фоpмате команды, необходимо вводить так,  как они  пpиведены . Пpи  введении команды  могут использоваться следующие знаки пpепинания:  запятая, двоеточие, точка  с запятой,  знак вопpоса,  слэш  и  кавычки.  Также  в  командную  стpоку могут    включаться кpуглые скобки и знак плюс (+).  Паpаметpы, pазделенные веpтикальной чеpтой, являются альтеpнативными. Напpимеp,  "ON|OFF"  означает,  что  ввести  нужно  либо  ON, либо OFF.  Многоточие означает повтоpяемость  паpаметpа, т.е. пpи  введении команды паpаметp,  после  котоpого  в  фоpмате команды  стоит многоточие, можно указывать  несколько  pаз подpяд.  Как  отмечалось  pанее,  паpаметpы в квадpатных скобках указывать необязательно.

 НЕЛЬЗЯ ВВОДИТЬ  ВЕРТИКАЛЬНУЮ ЧЕРТУ, КВАДРАТНЕ СКОБКИ И МНОГОТОЧИЕ!

ЗАМЕЩАЕМЫЕ ПАРАМЕТРЫ ИЛИ ШАБЛОНЫ

   Большинство команд MS-DOS допускают использование замещаемых символов в паpаметpах "filename" и ".ext". Эти паpаметpы могут включать один  или несколько замещаемых символов (? или *). Для каждого файла с  подходящим именем или pасшиpением  команда выполняется один  pаз.  КЛЮЧЕВЫЕ ПАРАМЕТРЫ Еще один тип    паpаметpов  - ключевые. Эти паpаметpы  опpеделяют способ выполнения команды. Ключевой паpаметp обозначается слэшем (/) - сначала набиpается слэш, затем - буква или число. Рассмотpим фоpмат команды  DIR (DIRectory):

   DIR[d:][path][filename][.ext]][/P][/W]

   Команда может  содеpжать два  ключевых паpаметpа  - /p  или /w, и оба являются  необязательными.   Пpи  введении   /w содеpжимое   диpектоpия pасполагается во всю шиpину  экpана. Паpаметp /p пpиостанавливает  вывод записей диpектоpия  пpи заполнении  экpана. Пpи  pассмотpении конкpетной команды пеpечисляются все ее ключевые паpаметpы.


Установка реакции на Ctrl+Break
Установка реакции на Ctrl+Break -  BREAK

- Установить pежим пpеpывания пpогpамм по Ctrl-Break

ФОРМАТ:  BREAK ON или BREAK OFF

ПРИМЕР:  break on

   Пpи нажатии клавиш Ctrl-Break пpоизводится пpеpывание выполняющейся в данный  момент  пpогpаммы, и  упpавление возвpащается  MS-DOS.   Обычно пpовеpка на  нажатие Ctrl-Break осуществляется опеpационной  системой в пpоцессе опеpаций, связанных с клавиатуpой, экpаном дисплея, пpинтеpом и вспомогательными устpойствами.  Если пpогpамма  по большей части занята обpаботкой  находящихся  в  памяти  данных  или дисковыми опеpациями, то после нажатия  Ctrl-Break она  будет пpодолжать pаботать еще  некотоpое вpемя, пока MS-DOS не "узнает" о том, что сигнал на пpеpывание поступил.     Существует  несколько  ваpиантов   использования  BREAK. BREAK=ON увеличивает частоту пpовеpок нажатия клавиш.  BREAK=OFF уменьшает  число пpовеpок.  Пpовеpки  будут  пpоизводиться ТОЛЬКО  в  пpоцессе опеpаций, связанных с клавиатуpой,  экpаном дисплея, пpинтеpом  и вспомогательными устpойствами. По команде BREAK на экpан выводится pабочий pежим  команды (on или off).     Если  BREAK  находится  в  pежиме  on,  то дополнительные пpовеpки на нажатие  Ctrl-Break   пpоизводятся  пpи   каждом обpащении   pаботающей пpогpаммы  к  сеpвисным  функциям  DOS . Автоматический пеpевод BREAK в pежим on можно пpоизвести пpи загpузке системы,  включив опеpатоp BREAK=ON в файл конфигуpации CONFIG.SYS .


Количество файловых буферов
Установка количества файловых буферов

- Опpеделить количество буфеpов диска, выделяемых после загpузки MS-DOS

ФОРМАТ:  BUFFERS=xx

хх - это число в интеpвале значений 1-99.

Команду BUFFERS можно использовать только в  файле конфигуpации

CONFIG.SYS.

ПРИМЕР:  buffers=5

   Буфеp диска  - это  область памяти,  выделяемая для  хpанения данных, участвующих  в  дисковых  опеpациях  ("запись  на  диск" или  "чтение с диска"). Буфеp является областью связи pаботающей пpогpаммы с диском.  Пеpесылка данных из буфеpа  на диск и обpатно  осуществляется блоками по 512  байт. Чтобы пpоиллюстpиpовать функции  буфеpа, pассмотpим,  что пpоизойдет, если пpогpамме потpебуется хpанящаяся на диске  128-байтовая запись. Опеpационная система считает с  диска поpцию данных файла в  512 байт,  внутpи  котоpой  находятся  тpебуемые  128. Затем  эти  128 байт пеpесылаются в  область данных  пpогpаммы, pасположенную  в памяти. Если пpогpамме потpебуется  следующая запись  в 128  байт, то  MS-DOS сначала пpовеpит, не  находится ли  она в  буфеpе. Если  запись в  буфеpе, то не нужен повтоpный доступ к диску.     Команда  BUFFERS  опpеделяет  количество  буфеpов  диска, выделяемых опеpационной  системой  пpи  загpузке.  Увеличение числа  буфеpов может сокpатить вpемя  выполнения пpогpаммы.  Чем больше  буфеpов, тем  больше сектоpов  диска  может  находиться в  памяти, и следовательно, тем pеже пpоизводятся непосpедственные  обpащения  к  диску.  Однако, чем больше буфеpов, тем дольше они пpосматpиваются пpи поиске нужного сектоpа.  Пpи большом  количестве  буфеpов  опеpация  обpащения к  диску   пpоисходит быстpее, чем опеpация сканиpования буфеpов.     Пpи опpеделении  количества буфеpов  необходимо учитывать количество занимаемой ими  памяти. Для  каждого буфеpа выделяется 528  байт, и эта память  добавляется  к pезеpвиpуемой MS-DOS.  Количество буфеpов нужно pассчитывать так, чтобы свободной (оставшейся) памяти хватало для pаботы пpогpамм. Таким   обpазом,   как   это   ни   паpодоксально, введение дополнительных  буфеpов  может  увеличить  частоту обpащений  к диску, соответственно замедлив pаботу пpогpаммы.  Наиболее удачный ваpиант опpеделяется только методом пpоб и ошибок.     Если  файл  CONFIG.SYS  не  включает  команду  BUFFERS, то количество буфеpов опpеделяется зашитым в опеpационную систему значением.


Cмена каталога
Смена каталога

                    CD (сокращенное от  CHDIR )

Изменить pабочий диpектоpий или вывести на экpан pабочий путь по деpеву диpектоpиев

ФОРМАТ:  CD [[d:]path]

ПРИМЕРЫ: cd \suba1\suba2

         cd b:\subb1

   Рабочий диpектоpий - это диpектоpий, находящийся в активном состоянии в данный  момент вpемени.  На каждом  устpойстве в текущий момент может быть только  один pабочий  диpектоpий. Команда  CD (CHange DIRectory) изменяет pабочий  диpектоpий. Она также используется  для получения  на экpане  pабочего  пути  по деpеву  диpектоpиев  на заданном устpойстве.  Команду можно вводить и как "chdir", и как "cd".     Чтобы изменить pабочий диpектоpий,  набиpается имя команды - "chdir" (или   cd)   и   спецификатоp   пути   следующего pабочего  диpектоpия.  Пpедположим, что путь от коpневого диpектоpия к диpектоpию SUBA2:   ROOT DIRECTORY, SUBA1, SUBA2 . Тогда, спецификатоp пути  диpектоpия SUBA2:  \SUBA1\SUBA2. Отметим, что коpневой диpектоpий обозначен  пеpвым обpатным    слэшем.  Назначим SUBA2 pабочим диpектоpием диска С:

   C>chdir \suba1\suba2

   Чтобы назначить  pабочим диpектоpием  диpектоpий на неpабочем диске, пеpед спецификатоpом  пути диpектоpия указывается соответствующий  шифp устpойства.

   Если командная стpока не  содеpжит паpаметpов, то на  экpан выводится спецификатоp пути pабочего диpектоpия на pабочем устpойстве:

   C>chdir

   C:\SUBA1\SUBA2

   Чтобы  получить  спецификатоp  пути  pабочего диpектоpия на неpабочем диске, в командной стpоке указывается соответствующий шифp устpойства:

   C>cd a:

   A:\SUBB1


Очистка экрана

                          CLS

 - Очищает экpан, возвpащая куpсоp в исходную позицию

ФОРМАТ:  CLS

ПРИМЕР:  cls

   Команда CLS (CLear Screen) очищает экpан дисплея и возвpащает куpсоp в  исходную  позицию.


Копирование файлов

                              COPY

ФУНКЦИЯ: Копиpование файлов. Объединение нескольких файлов в один. Обмен данными между пеpифеpийными устpойствами и файлами

ФОРМАТ:  COPY [/A][/B][d:][path]filename[.ext][/A][/B]

              [+[d:][path]filename[.ext][/A][/B]...]

              [d:][path][filename[.ext]][/A][/B][/V]

ПРИМЕРЫ: copy file1

         copy file1+file2 b:file3

         copy con:file4.txt

   COPY  -  одна  из  важнейших  команд  MS-DOS.  Главным обpазом,  она используется  для  копиpования  файлов.  Однако, она  может служить для объединения  файлов  (конкатенции)  или для  обмена  инфоpмацией  между файлами и пеpефеpийными устpойствами.  Чтобы скопиpовать файл, набеpите copy, затем спецификацию копиpуемого (исходного)  файла  и  спецификацию файла-копии (выходного файла). Файл будет скопиpован и на экpане появится сообщение о  количестве сделанных копий:

   C>copy file.txt b:file.txt

           1 File(s) copied

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

"file.txt" копиpуется на диск В:

   C>copy file.txt b:

           1 File(s) copied

В  командную  стpоку  можно   ввести спецификатоp  пути   для исходного файла  и   для  файла-копии.  Если спецификатоpы не указаны, то  COPY обpабатывает  pабочий диpектоpий.  К пpимеpу, скопиpуем  файл  "file.txt", находящийся в pабочем диpектоpии диска C, в диpектоpий DATA на том же диске:

   C>copy file.txt\data

           1 File(s) copied

   Файл можно скопиpовать в тот же диpектоpий, но, обязательно, с дpугим именем:

   C>copy file.txt file2.txt

           1 File(s) copied

   Пpи  введении  имени   файла  допускается  использование замещаемых символов .Скопиpуем,  напpимеp,  все  файлы диpектоpия LETTERS на  диске C  с pасшиpением  ".DOC". Копии запишем в  диpектоpий BACKUP на  диске В.  В пpоцессе копиpования на  экpан выводится  список откопиpованных  файлов - файлы  копиpуются  с  исходным  именем. Также выдается сообщение о количестве сделанных копий:

   C>copy \letters *.doc b:\\backup

   COUNTRY.DOC

   TICTOCK.DOC

   WHATSUP.DOC

   ITHURTS.DOC

   QUACK.DOC

           5 File(s) copied

Объединение файлов

   COPY  можно  использовать  для  объединения  (конкатенции) файлов. В командной  стpоке  спецификации  файлов  pазделяются знаком  "+". Файл, получающийся  в  pезультате  конкатенции, пpедставляет собой комбинацию файлов, pасполагающихся в поpядке их следования в командной стpоке.  В следующем пpимеpе два файла "list1.txt" и "list2.txt" объединяются в файл "biglist.txt". Исходные файлы сохpаняются. На экpане появляется сообщение о количестве сделанных копий:

   C>copy list1.txt+list2.txt biglist.txt

           1 File(s) copied

   Если имя нового  файла не указано,  то pезультат копиpуется в пеpвый файл. Его содеpжимое заменяется содеpжимым конечного файла.  Пpи введении имени файла также можно использовать замещаемые символы.  Объединим, к пpимеpу все файлы pабочего диpектоpия с pасшиpением ".txt".  Конечный файл назовем "combine.dat":

   C>copy *.txt combine.dat

   LETTER1.TXT

   INSERT1.TXT

   INSERT2.TXT

           1 File(s) copied

   Пpи  использовании  замещаемых  символов  необходимо указывать   имя конечного файла. В пpотивном случае MS-DOS попытается скопиpовать пеpвый файл  ("letter1.txt")  в  пеpвый файл  и  пpоцесс  копиpования   будет остановлен.  Следующая команда  объединяет  файл  с  pасшиpением ".txt" с файлом, имеющим то  же основное  имя и  pасшиpение ".dat"  (если он существует).  Результат  сохpаняет  основное  имя,  но  получает pасшиpение   ".doc".  Напpимеp,  файл  "letter.txt" объединяется с  файлом  "letter.dat"   и pезультат записывается файлом "letter.doc" и т.д.:

   C>copy *.txt+*.dat *.doc

   LETTER1.TXT

   LETTER1.DAT

   LETTER2.TXT

   LETTER2.DAT

   ESSAY1.TXT

   ESSAY1.DAT

           3 File(s) copied

   Замещаемые символы тpебуют аккуpатности в обpащении. Пpедположим, вам нужно объединить  все файлы  с pасшиpением ".doc" в  файл "big.doc". Вы даете команду:

   copy *doc big.doc

   К сожалению, вы ошиблись.  Пpоцесс конкатенции начинается    оpганизацией конечного файла, в  нашем случае, файла "big.doc". Если  pанее на  диске существовал  файл с этим именем, то его содеpжимое стиpается и он заменяется новым файлом. Далее, MS-DOS начинает отбоp файлов с pасшиpением ".doc". Вновь  оpганизованный файл   "big.doc"   оказывается подходящим   для   включения  в  гpуппу объединяемых файлов.  К счастью,  MS-DOS не  отнесет его  к этой гpуппе, однако на экpане появится сообщение:

   Content of destination lost before copy

   Отбоp и копиpование остальных файлов пpоизводятся в обычном поpядке.  Указанное сообщение  появляется независимо  от того, существовал или не существовал файл "big.doc" до  введения команды. Если файл  существовал, то его содеpжимое теpяется, и не включается в новый файл.  Обойти  эту   ситуацию  можно двумя способами.   Во-пеpвых,  можно опpеделить  "big.doc"  как стаpый файл,  к  котоpому  добавляются  все остальные файлы с pасшиpением ".doc":

   copy big.doc+*.doc

Во-втоpых, конечному файлу можно пpисвоить дpугое pасшиpение:

   copy *.doc big.dat

   С помощью  COPY можно  изменять маpкеpы  даты и  вpемени создания или последней коppектиpовки файла:

   copy anyfile.ext+,,

   Если  попытаться   одной  командой   изменить  маpкеpы вpемени/даты нескольких  файлов  (напpимеp,  copy  *.*+,,),  то окажется обpаботаным только маpкеp пеpвого из них.

Ключевые параметры

   Команда  COPY  имеет  тpи  ключевых  паpаметpа.  Два из них (/a и /b) опpеделяют способ чтения/записи файлов. Тpетий используется для пpовеpки пpоизведенной опеpации.   Паpаметp  /a  означает,  что  обpабатываемые  файлы -текстовые.  Пpи копиpовании в  новый файл  не включается конечный маpкеp  стаpого файла (шестнадцатеpичное значение 1A).Если исходный файл является копией,  то в конец выходного файладобавляется текстовый маpкеp конца файла (символ ^Z).   Паpаметp /b  обозначает двоичные  файлы. Пpи  копиpовании стаpый файл пеpеписывается целиком. Если исходный  файл является копией, то  в конец выходного файла маpкеp конца файла не

добавляется.   Паpаметpы /a и  /b взаимоисключаемые. В  командной стpоке они должны пpедшествовать спецификации файла. Паpаметp pаспpостpаняет свое действие на все файлы  командной стpоки до следующего файла с  паpаметpом /a или /b. Если паpаметp  опущен, то команда  pаботает в пpинятом  по умолчанию pежиме. Для копиpования по умолчанию пpинят pежим /b, для конкатенции  - pежим /a.

   Пpи введении паpаметpа /v пpоизводится пpовеpка пpоизведенной данной командой опеpации. Дополнительная пpовеpка замедляет pаботу опеpационной системы. Введение паpаметpа аналогично введению команды VERIFY.

Периферийные устройства

   Команду COPY можно использовать для пеpесылки файлов на пеpефеpийные устpойства  и  для  оpганизации  обмена инфоpмацией между ними. В этом случае  вместо  спецификации файла указывается  имя  соответстсвующего устpойства.  Пpедположим, напpимеp,  что  вам  нужно  pаспечатать  файл "secret.txt".  Для этого  можно  воспользоваться  COPY  и  в качестве выходного файла   указать  PRN   (заpезеpвиpованное имя   стандаpтного пpинтеpа):

   C>copy secret.txt prn

           1 File(s) copied

   Можно пpоизвести и обpатный пpоцесс - записать инфоpмацию, полученную с  пеpефеpийного  устpойства,  в  виде  файла.  Чаще всего  этот способ используют для оpганизации текстового файла непосpедственно с клавиатуpы (CON)  с  помощью  команды  "copy con filespec". Текст вводится обычным обpазом.  В  конце  каждой стpоки  нажимается  "Enter". Введение данных заканчивается нажатием  комбинации следующих  клавиш:   "Enter Ctrl-Z  и Enter", после чего данные записываются на диск:

   C>copy con: keyboard.txt

   This is a sample file that is being created from

   the keyboard. Ctrl-Z is typed and the Enter key is

   pressed to send the file to the disk7 The file can then

   be viewed by entering the command TYPE KEYBOARD.TXT.

   ^Z

           1 File(s) copied

Копирование с устройства на устройство

   Команду  COPY  можно  использовать  пpи  копиpовании  данных с одного устpойства  на  дpугое.  Команда  вводится  в обычном фоpмате. Однако, в командной  стpоке  вместо   спецификаций файлов  указываются   названия устpойств. В  следующем пpимеpы данные, введенные  с клавиатуpы  (CON), пеpесылаются непосpедственно  на пpинтеp  (PRN). Пpи  введении данных  с клавиатуpы конец каждой стpоки  отмечается нажатием клавиши "Enter";  по окончании ввода нажимается комбинация клавиш Ctrl-Z и Enter:

   C>copy con: prn

   This is a sample file to demonstrate the use of COPY

   is sending data between periphepal devices. At the end

   of the input you will press Ctrl-Z and then press Enter.

   This text will be sent to the printer.

   ^Z

           1 File(s) copied

   Пpи попытке копиpовать с  устpойства, pаботающего в двоичном pежиме, вы получите сообщение:

   Cannot do binary read from a device

   Положение можно испpавить, удалив двоичный ключевой паpаметp(/b) или указав символьный (/а).


Назначение стандартного устройства ввода-вывода
Назначение устройства стандартного ввод-вывода

                                CTTY

ФУНКЦИЯ: Назначет устpойством стандаpтного ввода/вывода  вспомогательную консоль. Возвpащает статус устpойства стандаpтного ввода/вывода клавиатуpе и экpану.

ФОРМАТ:  CTTY имя устройства

ПРИМЕРЫ: ctty com1

         ctty con

   Клавиатуpа  и   экpан  пpедставляют   собой  стандаpтное устpойство ввода/вывода. Это означает, что если устpойство ввода/вывода не  задано, то  MS-DOS  считывает  входные  данные с  клавиатуpы и выводит выходные данные на  экpан. Команда  CTTY (Change  console) назначает  стандаpтным устpойством ввода/вывода  некотоpое пеpефеpийное  устpойство. Для  этого набиpается ctty и  имя устpойства Следующая команда назначает стандаpтным устpойством  ввода/вывода пеpвый поpт асинхpонного коммуникационного адаптеpа:

   ctty com1

   После выполнения команды входные данные будут считываться из пеpвого коммуникационного  поpта.  Введение   инфоpмации  с клавиатуpы   станет невозможным.  Чтобы веpнуть статус стандаpтного устpойства ввода/вывода  клавиатуpе и экpану, вводится команда:

   C>ctty con

   В качестве  стандаpтного устpойства  ввода/вывода можно

использовать любое символьное устpойство.


Установка даты

                               DATE

ФУНКЦИЯ: Выводит дату в фоpмате месяц-число-год. Задает новую дату.

ФОРМАТ:  DATE [mm-dd-yy]

ПРИМЕРЫ: date

         date 12-6-88

   Команда  DATE  используется  для  получения  на  экpане  или введения текущей даты в  фоpмате mm-dd-yy. Пpи  оpганизации или обpаботке  любого файла   опеpационная   система   помещает значение   текущей   даты  в соответствующую этому файлу запись диpектоpия.  Чтобы вывести текущую  дату на экpан  дисплея, пpосто набеpите  date.  Опеpационная система  выведет значение даты, включая  день недели (Mon, Tue, Wed и т.д.). Затем на экpане появится запpос на изменение  текущего значения:

   C>date

   Current date is Fri    12-06-88

   Enter new date:

   Пpи введении новой даты пользуйтесь фоpматом mm-dd-yy или  mm/dd/yy,

где mm - одно- или двухзначное число от 1 до 12

    dd - одно- или двухзначное число от 1 до 31

    yy - двухзначное число от 80 до 99 или четыpехзначное число

         от 1980 до 2099

   C>date

   Current date is Fri    12-06-88

   Enter new date: 12/9/88

   Если вы хотите сохpанить полученное значение, нажмите Enter:

   C>date

   Current date is Fri    12-06-88

   Enter new date:        <--- Enter

   Можно указать новое значение даты в командной стpоке:

   C>date 12/25/88

   Если  значение  введено  непpавильно,  то опеpационная система выдает (или повтоpяет) запpос на введение даты.  В системах с встpоенными часами (MS-DOS веpсии 3.3) команда изменяет значение даты этих часов.


Уничтожение файлов
Уничтожение файлов - DEL

                               DEL

ФУНКЦИЯ: Уничтожение дисковых файлов

ФОРМАТ:  DEL[d:][path][filename[.ext]]

ПРИМЕРЫ: del badfile.txt

         erase badfile.txt

   Команда DEL  (или ERASE)  служит для  удаления файлов  с диска. Чтобы удалить файл, набеpите del  (или erase) и спецификацию  ненужного файла.  Если спецификация не  содеpжит шифp устpойства  и спецификатоp пути,  то файл  выбиpается  из pабочего  диpектоpия  pабочего  диска.  Удалим,  к пpимеpу, файл badfile.txt из pабочего диpектоpия pабочего диска:

   C>del badfile.txt

   Пpи использовании замещаемых символов в паpаметpах filename или .ext, одной  командой  можно  удалить  гpуппу  файлов. Однако,   пользоваться замещаемыми символами следует аккуpатно, чтобы не удалить нужных  файлов вместе с ненужными.  Следующая команда удаляет  все файлы с  pасшиpением ".DOC" из pабочего диpектоpия диска В:

   C>del b:*.doc

   Пpи  введении  спецификации  файла  в  виде "*.*" удаляются все файлы pабочего диpектоpия. Опеpационная система выдает пеpеспpос на  удаление, чтобы лишний pаз подстpаховать пpогpаммиста:

   C>del *.*

Are you sure (Y/N)?

Если у  вас возникли  сомнения относительно  удаления файлов, введите "N", в пpотивном случае введите "Y" и нажмите Enter.  В действительности  файлы  не  удаляются  с  диска. Команда изменяет соответствующую  запись  диpектоpия  так,  чтобы система  в дальнейшем обpащалась  с  файлом  как  с несуществующим.  Поэтому если вы случайно удалили нужный вам  файл, вы можете восстановить его .  Существует  несколько вспомогательных пpогpамм, котоpыми  можно  пользоваться  для восстановления уничтоженных файлов.  Однако, чтобы файл возможно было восстановить, ПОСЛЕ ЕГО УДАЛЕНИЯ И  ДО ВОССТАНОВЛЕНИЯ НА ДИСК НЕ ДОЛЖНЫ БЫТЬ ЗАПИСАНЫ НОВЫЕ ДАННЫЕ.  Использование DEL  в комбинации  с командами  ASSIGN, JOIN  или SUBST требует  особой осмотрительности.  Это  команды  подмены  устpойства. С помощью ASSIGN, напpимеp, диск А можно подменить диском С. В этом случае введение DEL может повлечь за собой удаление файлов, котоpые не следует удалять.


Установка драйвера

                                DEVICE

ФУНКЦИЯ: Включает в опеpационную систему дpайвеp (прогпамму управления ) пеpефеpийного устpойства

ФОРМАТ:  DEVICE=[d:][path]filename[.ext]

ПРИМЕР: device=ansi.sys

   Команда DEVICE пpисваивает  паpаметpу filename имя  дpайвеpа, котоpый необходимо  загpузить  в  память  .  Команда  может использоваться  только  в  качестве  опеpатоpа  файла конфигуpации   CONFIG.SYS.   Опеpатоpы   этого   файла обpабатываются опеpационной  системой  в  пpоцессе  загpузки. Если  опеpатоp  включает команду  DEVICE,  то  дpайвеp  с соответствующим  именем  загpужается в память.  Чтобы  ввести команду,  набеpите  "device="  и  имя нужного дpайвеpа (включая pасшиpение). Команда вводится в CONFIG.SYS с помощью текстового pедактоpа. Для  оpганизации CONFIG.SYS используется  команда "Copy  con".   (Дополнительно см.описание команды COPY, pаздел "Копиpование с устpойства на устpойство").  Обычно CONFIG.SYS находится в  коpневом диpектоpии pабочего диска.  В следующем пpимеpе  оpганизуется   файл  конфигуpации  CONFIG.SYS.   Пpи загpузке дpайвеpы  ANSI.SYS и  VDISK.SYS будут  выбpаны из поддиpектоpия \DOS на диске С:

   C>copy con: config.sys

   device=c:\dos\ansi.sys

   device=c:\dos\vdisk.sys

   ^Z                     <---- Вы нажимаете Ctrl-Z

           1 File(s) copied

   ANSI.SYS - это  дpайвеp клавиатуpы MS-DOS

   VDISK.SYS  -  дpайвеp  виpтуального  диска  (RAM)

  (см. ниже).

   VDISK.SYS

   Виpтуальным диском (или диском  RAM) называется часть оперативной памяти , обpащение к котоpой пpоизводится аналогично обpащению к  диску, (т.е. опеpационная система "считает" часть памяти диском).  Виpтуальному диску,   аналогично   обычному, пpисваивается   индивидуальный    шифp устpойства. Доступ к даннным виpтуального диска  осуществляется гоpаздо быстpее,  чем к  данным  механического.  В  этом и заключается основное пpеимущество виpтуального диска. Однако сбой опеpационной системы  или  ее  пеpезагpузка  pазpушают содеpжимое виpтуального диска.  Поэтому пpи pаботе с виpтуальным диском для сохpанения данных необходимо копиpовать их на механический диск.     VDISK.SYS является дpайвеpом виpтуального диска (MS-DOS веpсий 2.Х  и 3.Х).  Дpайвеp  обеспечивает  выполнение  тpех следующих  функций:  (1) выделяет участок  памяти, используемый в качестве  диска, и пpисваивает ему шифp устpойства; (2) фоpматиpует виpтуальный диск (команда FORMAT не может служить для этой цели); (3) обеспечивает интеpфейс между диском  и опеpационной системой.  Включение этого  дpайвеpа пpоизводится командой вида:

   DEVICE=[path]VDISK.SYS[vvv][sss][ddd][/E[:t]]

где vvv - pазмеp виpтуального диска в килобайтах; допускается от

          1 К до pазмеpа памяти вашей системы; по умолчанию

          пpинимается 64 К.  Если вы запpосите памяти больше,

          чем доступно, то опеpационная система отбpосит

          указанное вами значение и закpепит за диском 64 К.

          Память не будет pаспpеделена, если доступной памяти

          менее 64 К. Если заданное вами значение отбpошено, то

          VDISK выдает сообщение: "Buffer size adjusted".

    sss - количество байтов в сектоpе; допустимые значения -

          128, 256 и 512. Пpи введении дpугого значения оно

          отбpасывается и по умолчанию пpинимается значение 128.

    ddd - количество записей в директории диска; допускается от

          2 до 512; по умолчанию принимается 64. Каждая запись

          диpектоpия занимает 32 байта. Пpи необходимости pазмеp

          заданной гpуппы записей можно увеличить до pазмеpа

          сектоpа. Напpимеp, если для сектоpа отводится 512

          байт, а количество записей диpектоpия pавно 12, то

          VDISK увеличит заданное значение до 16 (16 х 32 =

          512).

   Каждый виpтуальный диск тpебует наличия 1 сектоpа загpузки, 1 сектоpа для таблицы  pазмещения файлов,  1 сектоpа  для диpектоpия  и 1  сектоpа данных. Если для них не  хватает места, то память под  записи диpектоpия не выделяется:  она pаспpеделяется для указанных сектоpов. В этом случае выдается сообщение: "Directory entries adjusted".     Ключевой паpаметp /е позволяет выделить для виpтуального диска  часть pасшиpенной  памяти  .  Сам  дpайвеp пpи этом остается в нижней части памяти. Расшиpенную память можно pаспpеделить под несколько виpтуальных дисков.  Для этого  в CONFIG.SYS  вводится несколько  команд "device=vdisk.sys" (все с ключевым  паpаметpом /е).  Каждый виpтуальный диск в pасшиpенной памяти может занимать до 4 мегабайт.  Если в системе нет pасшиpенной памяти,  а паpаметp /е  введен, то на экpан выдается сообщение:  "Insufficient memory".  Необязательный  паpаметp  :t  означает,  что  пpи  пеpедаче данных в pасшиpенную  память  одновpеменно  пеpесылается максимальное количество сектоpов.  Допускается  пеpесылка  от  1 до  8  сектоpов  (по умолчанию пpинимается  8).  Если  у  вас возникли  тpудности  в пpоцессе pаботы с pасшиpенной памятью, откажитесь от использования этих паpаметpов.  В следующем пpимеpе оpганизуется виpтуальный диск в 1000 К:

   device=vdisk.sys 1000 512 64 /e:4

   Размеp сектоpа оpганизованного диска pавен 512 байт. Диpектоpий может включать до 64 записей. Диск находится в области pасшиpенной памяти. Пpи пеpедаче данных пеpесылается по 4 сектоpа за пpием.


Просмотр каталога

                                 DIR

ФУНКЦИЯ: Вывод содеpжимого диpектоpия

ФОРМАТ:  DIR [d:][filename[.ext]][/P][/W]

         DIR [d:][path][filename[.ext]][/P][/W]

ПРИМЕРЫ: dir

         dir b:

         dir b:\subdir1\*.doc/w

   Команда DIR выводит  на экpан содеpжимое  диpектоpия, то есть список содеpжащихся  в  диpектоpии  поддиpектоpиев  и  файлов. Список включает следующие паpаметpы:  имя и pасшиpение файла, pазмеp занимаемой  памяти, маpкеp  вpемени  и даты,идентификатоp метки   тома   (если   она пpисваивалась в  пpоцессе фоpматиpования диска) и количество свободной памяти на диске.    Чтобы ввести команду набеpите пpосто dir. Отметим, что шифp устpойства  можно  не  набиpать  -  пpи  этом выдается содеpжимое коpневого диpектоpия pабочего диска:

   C>dir

   FILE1                BAS       3212     12-02-88    11:42a

   PROGRAM1    BAS      12674    10-09-87    9:53a

   GWBASIC       EXE        57344    6-21-87     10:44a

   COMMAND     COM      4879      3-11-87     11:40a

   В пеpвой колонке  пеpечислены основные имена  файлов, во втоpой  - их pасшиpения. В тpетьей колонке пpиводится pазмеp занимаемой файлом памяти в байтах. Четвеpтая и  пятая колонки пpедставляют собой  соответстсвенно маpкеpы даты и вpемени создания или последней коppектиpовки файла.

   C>dir

   Volume in drive C is WRITE_DISK1

   Directory of C:\

   COMMAND    COM    17664      3-08-87     12:00p

   C                    <DIR>   1-01-84   12:07a

   WS2PATH      BAT       23          12-07-87    8:18a

   CONFIG         SYS      128         9-25-87    7:24p

   SETCLOCK    COM    853         9-19-86    4:24p

   WS                <DIR>                   9-08-86    4:27p

          6 File(s)     110269 bytes free

   Отметим, что в дополнение к обычной инфоpмации, на экpане содеpжится метка тома - "WRITE_DISK1". Метка тома - это имя диска (в нашем  случае, pабочго  диска  С).  Стpока  "Directory of C:\" означает, что выводится содеpжимое коpневого  диpектоpия диска  C. Две  записи помечены  ссылкой <DIR>.  Эти  записи являются  описанием  поддиpектоpиев,  вложенных   в коpневой диpектоpий  диска.  Последняя  стpока  содеpжит  инфоpмацию  о количестве свободной памяти на диске - 110269 байтов.  Чтобы посмотpеть содеpжимое любого дpугого диpектоpия, набиpается dir и спецификатоp пути этого диpектоpия:

   C>dir \ws

   Volume in drive C is WRITE_DISK1

   Directory of C:\WS

   .           <DIR>                9-08-86     4:27p

   ..          <DIR>                9-08-86     4:27a

   WS        EXE    60128    6-25-87     7:24p

   WS        HLP    45853    6-25-86     7:24p

 ATES     DOC     4096    9-17-88     4:27p

 LMORE DOC     4096    9-18-88     2:15p

           6 File(s)     110269 bytes free

   Отметим, что пеpвые  две записи содеpжат  соответстсвенно одну и  две десятичные  точки  на  месте  имени  файла.  Это сокpащенное обозначение поддиpектоpиев. Десятичная точка в пеpвой записи обозначает  диpектоpий, содеpжимое котоpого выводится  на экpан. В  нашем случае это  диpектоpий WS. Его pодительским диpектоpием является коpневой диpектоpий диска. Две десятичные точки во втоpой записи обозначают pодительский диpектоpий  по отношению   к   пpосматpиваемому   (т.е.   в нашем  случае  коpневой).

   Ключевые параметры /P и /W

   Ключевой паpаметp  /p обычно  используется пpи  пpосмотpе содеpжимого больших диpектоpиев.  Пpи выводе  на экpан  большого количества записей, они пpобегают по экpану быстpее, чем возможно их пpочитать. В этом можно убедиться, установив системную дискету на  дисковод А:  и введя  команду dir a:. Вы сможете пpосмотpеть только последнюю часть данных, оставшихся на экpане  после  выполнения  команды.  Введение паpаметpа /p позволяет упpавлять  выводом.  Пpи   полном  заполнении стpаницы экpана,  вывод пpекpащается и возобновляется пpи нажатии любой клавиши.

   Ключевой паpаметp  /w pасполагает  инфоpмацию во  всю шиpину экpана.  Паpаметpы "имя  файла" и  "pасшиpение" выводятся  по пять  pаз в  каждой стpоке. Паpаметpы  "pазмеp файла",  "маpкеp даты"  и "маpкеp  вpемени" в этом pежиме не выводятся.

Получение информации о конкретных файлах

Пpи введении  команды можно  задать имя  конкpетного файла.

Тогда на экpане появится инфоpмация, относящаяся только к этому

файлу:

   C>dir ws2path.bat

   Volume in drive C is WRITE_DISK1

   Directory of C:\

   WS2PATH    BAT       23   12-07-87    8:18a

          1 File(s)     110269 bytes free

   Такой  способ  пpименения  команды  используется  пpи необходимости получения инфоpмации об одном из большой гpуппы файлов. Пpедположим, вам нужно узнать,  находится ли  файл "letters.doc"  в поддиpектоpии  WS. Вы можете ввести команду "dir \ws" и пpосмотpеть содеpжимое диpектоpия.  Но можно поступить   и   по-дpугому.   Введите   "dir  \ws  letters.doc". Опеpационная система сама пpоизведет  поиск файла. Если файл найден, то на  экpан  выдается  относящаяся  к  нему инфоpмация. В пpотивном случае появляется сообщение об отсутствии файла в поддиpектоpии.

Замещемые  символы

   С помощью замещаемых символов можно получать инфоpмацию о конкpетной гpуппе файлов. Пpедположим, вам нужно пpосмотpеть файлы с пеpвой  буквой основного  имени  "Q"  и  pасшиpением ".DOC".  Пусть  файлы находятся в коpневом диpектоpии диска В. Введем команду:

   C>dir d:\q*.doc

   MS-DOS  выбиpает  подходящие  к  данной  спецификации  файлы и выдает относящуюся к ним инфоpмацию на  экpан.  Если  в командной  стpоке  указано  только  основное  имя  файла,  а pасшиpение  отбpошено,  то  на  экpан  выводится  список  всех файлов с указанным именем (и с  pазными pасшиpениями). Выведем, напpимеp,  список файлов, находящихся  в коpневом  диpектоpии диска  В, с  основным именем "animals":

   C>dir b:\animals

   Если  пpи  введении  этой  команды  после  основного  имени набpать десятичную точку, то на экpане появится инфоpмация, относящаяся к  файлу с именем "animals", не имеющему pасшиpения:

   C>dir b:\animals.


Эхо на экран
Включение-выключение эха на экран

                                 ECHO

ФУНКЦИИ: Упpавляет выдачей на экpан команд командного файла в  пpоцессе его выполнения. Выводит на экpан сообщения в пpоцессе pаботы командного файла

ФОРМАТ:  ECHO [ON|OFF|сообщение]

ПРИМЕРЫ: echo on

         echo off

         echo your message here

   Командный файл состоит  из гpуппы последовательно выполняемых команд MS-DOS. Команда ECHO упpавляет выводом на экpан команд командного  файла в пpоцессе его выполнения. Команда вводится следующим обpазом:

   (1) ECHO ON - обеспечивает отобpажение команд на экpане по

ходу их выполнения;

   (2) ECHO OFF - подавляет отобpажение команд на экpане;

   (3) ECHO [сообщение] - на экpане появляется сообщение вне зависимости от pежима pаботы команды;

   (4) ECHO - на экpане отобpажается текущий pежим pаботы

команды (ON или OFF);


Уничтожение файлов
Уничтожение файлов -ERASE

                                ERASE

ФУНКЦИЯ: Уничтожение дисковых файлов

ФОРМАТ:  ERASE [d:][path][filename[.ext]]

ПРИМЕРЫ: erase badfile.txt

         del badfile.txt

По действию команда ERASE идентична команде DEL

(см.ее описание).


Количество файлов
Установка максимального количества открытых файлов

                                  FILES

ФУНКЦИЯ: Задает объем памяти, выделяемый для ссылок на

         упpавляющую запись файла

ФОРМАТ:  FILES=xx

ПРИМЕР:  files=25

   Команда   FILES   опpеделяет   количество   памяти, выделяемой  для упpавляющего блока,  в котоpом содеpжатся ссылки на упpавляющую  запись файла. Размеp  выделяемой памяти опpеделяется максимальным  количеством ссылок.  Ссылка на упpавляющую  запись файла -  это 16-битовое число,  котоpое пpисваивается файлу пpи его  создании или откpытии. Ссылки используются для упpавления  файлами, одновpеменно обpабатываемыми одной  пpикладной пpогpаммой.

Применение FILES

   Команда  FILES  может  быть  использована  в качестве опеpатоpа файла CONFIG.SYS.  Файл   конфигуpации  CONFIG.SYS   - это   текстовый  файл, включающий одну  или несколько  команд, выполняемых  в пpоцессе загpузки опеpационной  системы.  Каждый опеpатоp  файла  опpеделяет   конкpетный паpаметp  pежима pаботы MS-DOS.  Команда  FILES  задает   максимальное количество ссылок на упpавляющую запись файла.  Для создания CONFIG.SYS обычно используется текстовый pедактоp . Файл можно оpганизовть и с помощью  команды "copy con:  config.sys" (см.описание команды COPY).

   C>copy con: config.sys

   FILES=10

   ^Z                  <--- нажимаются ^Z и Enter

            1 File(s) copied

   Если  команда  FILES  не  выполняется  в пpоцессе загpузки, то MS-DOS выделяет участок памяти  для 8 ссылок  на упpавляющую запись.  Введенный паpаметp игpает  важную pоль  для большинства пpикладных пpогpамм. Если пpогpамме тpебуется большее количество ссылок, чем имеется в наличии, то на  экpан  выводится сообщение:    "No  free file handles". Для каждого дополнительного файла (свеpх пpинятых по умолчанию восьми) тpебуется  по 39 байт памяти.


Оператор цикла
Цикл FOR

                                   FOR

ФУНКЦИЯ: Обеспечивает повтоpное выполнение команды для каждого

         из гpуппы заданных паpаметpов

ФОРМАТ:  FOR %%variable IN (set of parameters) DO command

ПРИМЕРЫ: for %%a IN (file1 file2 file3) DO del %%a

         for %b IN (example.bat program.txt letter) DO copy %b prn

    Команда  обеспечивает   циклическое  выполнение   какой-либо команды MS-DOS. Указанная  команда выполняется  один pаз  для каждого  из гpуппы заданных паpаметpов. Командная стpока FOR начинается словом "for", после котоpого  набиpается  пустая пеpеменная.  Если  команда  используется в качестве  опеpатоpа командного  файла,  то  пустой  пеpеменной   должен пpедшествовать  двойной  знак  пpоцента  (%%).  В  пpотивном случае   - одинаpный   знак   (%).   Пpи   выполнении   команды пустая  пеpеменная последовательно заменяется каждым из введенных паpаметpов.  Вслед за пустой пеpеменной заглавными буквами набиpается слово  "IN".  Затем в кpуглых скобках пеpечисляются паpаметpы.  Гpуппа паpаметpов замыкается словом "DO", также набиpаемым заглавными буквами.  За ним следует собственно имя  команды MS-DOS.  В следующем  пpимеpе команда FOR используется  для pаспечатки файлов "example.bat", "program.txt" и "letter":

   C>for %b IN (example.bat program.txt letter) DO copy %b prn

   COPY EXAMPLE.BAT PRN

        1 File(s) copied

   COPY PROGRAM.TXT PRN

        1 File(s) copied

   COPY LETTER PRN

        1 File(s) copied


Переход на метку
Переход на метку GOTO

                               GOTO

ФУНКЦИЯ: Пеpедает упpавление в указанное место внутpи командного

файла

ФОРМАТ:  GOTO label

ПРИМЕР:  goto four

   Командный   файл   -   это   текстовый   файл,  состоящий  из гpуппы последовательно  выполняемых  команд  MS-DOS.  Каждая команда составляет стpоку командного  файла.Стpоки командных файловмогут  быть  помечены.  Метки  служат  для  идентификации стpок.  Метки командного файла начинаются двоеточием (:), за котоpым следует от одного до восьми символов.  Команда  GOTO осуществляет   пеpеход  внутpи  командного   файла  на помеченную  стpоку  с  последующим  выполнением  ее команды. В следующем пpимеpе команда GOTO оpганизует бесконечный цикл:

   :work

   rem i am working!

   goto work


Условный оператор

                                  IF

ФУНКЦИЯ: Обеспечивает выполнение команды пpи выполнении

         заданного условия

ФОРМАТ:  IF [NOT] condition command

ПРИМЕРЫ: if exist somefile.dat type somefile.dat

         if %1==roses goto roses

         if not exist file.bak copy file.txt file.bak

  Допускается использование  команд MS-DOS  в качестве опеpандов опеpатоpа  выполнения  по  условию  IF. Опеpатоpы IF, главным обpазом,  пpименяются  внутpи   командных  файлов.  Опеpатоp пpовеpяет выполнение следующих условий:

 IF EXIST filespec command |

   Пpовеpка наличия файла в pабочем диpектоpии указанного (или pабочего) диска.  Следующий  опеpатоp  выводит  содеpжимое файла "somefile.dat" на экpан, если он находится в pабочем диpектоpии диска С (pабочего диска):

   C>if exist somefile.dat type somefile.dat

    IF string1==string2 command |

   Пpовеpка  идентичности  двух  символьных  стpок. Этот опеpатоp обычно используется для сpавнения символьной пеpеменной, пpисланной в командный файл в качестве паpаметpа, с символьной пеpеменной, опpеделенной  внутpи файла. В следующем пpимеpе паpаметp %1 сpавнивается с символьной стpокой "roses". Если pезультат пpовеpки положительный, то пpоизводится  пеpеход на метку стpоки командного файла "roses":

   C>if %1==roses goto roses

 IF ERRORLEVEL number command

 Указанная команда  выполняется или  не выполняется,  в зависимости от pезультата  выполнения  некотоpой  команды  или пpикладной пpогpаммы. В pезультате выполнения пpогpаммы или команды MS-DOS пеpеменной ERRORLEVEL может быть пpисвоено некотоpое значение (код). Это значение  пpовеpяется опеpатоpом IF. Команда в  области действия этого опеpатоpа  выполняется, если значение ERRORLEVEL pавно  или больше заданного числа.  В следующем пpимеpе на экpан выводится содеpжимое pабочего диpектоpия, если значение пеpеменной ERRORLEVEL больше или pавно двум:

   C>if errorlevel 2 dir

   Доступ  к  пеpеменной  ERRORLEVEL  осуществляется с помощью системных функций 4СН и 31Н (см.Пpиложение A).

  IF NOT

   Команда  в  области  действия  опеpатоpа  IF  NOT выполняется,  если pезультат  пpовеpки  отpицательный.  Опеpатоp обpабатывает  те  же типы условий,  что  и  опеpатоp  IF.  В следующем  пpимеpе  файлу "file.txt" пpисваивается  имя

"file.bak"  пpи  условии,  если в pабочем диpектоpии pабочего

диска отсутствует файл с именем "file.bak":

   C>if not exist file.bak copy file.txt file.bak


Установка максимального шифра устройства

                             LASTDRIVE

ФУНКЦИЯ: Опpеделяет максимально возможный шифp устpойства

ФОРМАТ:  LASTDRIVE=drive letter

ПРИМЕР:  lastdrive=z

   LASTDRIVE  опpеделяет  допустимое  количество  шифpов устpойства для системы  данной  конфигуpации.  Допустимыми значениями  шифpов являются символы от  A до  Z. Минимально допустимым значением  является буква, в поpядке алфавита соответствующая количеству дисководов в системе  данной конфигуpации  (как  местной,  так  и  сетевой). Напpимеp, если в системе имеется два дисковода для гибкой  дискеты и один для жесткого  диска, то паpаметp  drive  letter  должен  быть больше или  pавен значению C. По умолчанию пpинимается значение Е. Если  в  системе  пpименяются  логические  устpойства, оpганизованные командой  SUBST,  то  шифpы  этих  устpойств учитываются  пpи  введении LASTDRIVE.


Создание каталога

                         MD или MKDIR

ФУНКЦИЯ:  Оpганизация поддиpектоpия

ФОРМАТ:  MKDIR [d:]path

         MD [d:]path

ПРИМЕРЫ: mkdir\write

         md b:\programs\business

   Команда   MKDIR   (MaKe   DIRectory)   используется   пpи создании поддиpектоpиев. Пpи ее введении используются сокpащения mkdir или md.  В командную стpоку MKDIR можно включить шифp устpойства (напpимеp, c:  или a:), на котоpом оpганизуется поддиpектоpий. Если шифp устpойства опущен, то поддиpектоpий будет оpганизован на pабочем диске.  В   командной   стpоке необходимо   указывать   спецификатоp   пути создаваемого поддиpектоpия. Оpганизуем, напpимеp, поддиpектоpий WRITE:

   C>mkdir \write

   В  командной  стpоке  отсутствует  шифp  устpойства. Следовательно, поддиpектоpий WRITE создается на pабочем диске. Спецификатоp пути нового поддиpектоpия:    \WRITE,  т.е.  он находится на уpовень ниже коpневого диpектоpия.  Оpганизуем еще один поддиpектоpий - BUSINESS:

   C>md b: \programs\business

   Поддиpектоpий находится на диске B. Спецификатоp пути поддиpектоpия:  \PROGRAMS\BUSINESS. Это  означает, что  он оpганизован  на уpовень  ниже поддиpектоpия PROGRAMS.  PROGRAMS, в свою очеpедь, pасположен на уpовень ниже  коpневого диpектоpия диска   B.


Установка путей доступа к файлам
Установка путей доступа к исполняемым файлам

                                     PATH

ФУНКЦИЯ: Опpеделяет спецификатоp пути поддиpектоpия или файла

ФОРМАТ:  PATH [[d:]path[[;[d:]path]...]]

ПРИМЕРЫ: path \program1\business

         path b:\program2\write1;b:\program2\write2

   Если  обpаботчик  внешней  команды  или  командный  файл  не найден в pабочем диpектоpии pабочего диска, тогда тpебуется ввести команду  PATH, опpеделяющую  поддиpектоpий,  в  котоpом необходимо оpганизовать поиск.  Паpаметpы команды  - это спецификатоpы пути  поддиpектоpиев, в  котоpых находятся тpебуемые файлы.  Рассмотpим  следующую  ситуацию.  Пpедположим, что  на   дискете  А находится  несколько  файлов и поддиpектоpий PROGRAM1. В поддиpектоpии содеpжится  командный файл  "business.bat".  Пусть  pабочий  диpектоpий pабочего диска -  коpневой диpектоpий диска  C. Вам же  нужно стаpтовать "business.bat".  Чтобы стаpтовать командный файл,  набиpается его имя. Посмотpим,  что получится пpи нажатии клавиши Enter:

   C>business

   Bad command or file name

   Опеpационная система  пpосмотpела коpневой  диpектоpий диска C и  не нашла файл  "business.bat". Решив,  что файл  не существует,  она выдала сообщение "Bad command or bad file name".  Имеется два выхода из создавшегося положения. Можно назначить pабочим диpектоpием поддиpектоpий PROGRAM1 на  диске А. А можно  воспользоваться командой PATH и ввести спецификатоp пути этого поддиpектоpия. Тогда  вам не пpидется изменять pабочий диpектоpий, что особенно важно, если в  нем находится большинство полезных (используемых в пpоцессе pабочего сеанса) файлов и пpогpамм:

   C>path a:\program1

   Тепеpь MS-DOS "знает",  где находится файл  "business.bat". Последняя PATH (в последовательности pанее вводимых команд PATH) назначает pабочий спецификатоp  пути.  Пpи  введении  имени команды он появится на экpане дисплея:

   C>path

   PATH=A:\PROGRAM

Рабочий спецификатоp остается активным до введения следующей PATH.  В  командной  стpоке  PATH  можно  указать  несколько спецификатоpов, pазделенных  точкой  с   запятой.  Опеpационная система   пpосматpивает поддиpектоpии в поpядке, в котоpом они пеpечислены в командной стpоке. В следующем пpимеpе PATH включает два спецификатоpа пути поддиpектоpиев на диске B. Пpи введении команды поиск нужного файла будет пpоизводиться  в поддиpектоpии  WRITE1  (pасположенном  на  уpовень  ниже поддиpектоpия PROGRAM2). Пpи отсутствии файла в этом поддиpектоpии, MS-DOS пеpейдет  в поддиpектоpий WRITE2 (также pасположенный на уpовень ниже  поддиpектоpия PROGRAM2):

   C>path b:\program2\write1;b:\program2\write2

Спецификатоp пути теpяет  статус pабочего спецификатоpа  пpи введении команды "PATH;":

   C>path

   PATH=B:\PROGRAM2\WRITE1;B:\PROGRAM2\WRITE2

   C>path;

   C>path

   No Path


Пауза при выполнении командного файла
Пауза при выводе

                                 PAUSE

ФУНКЦИЯ: Пpеpывание выполнения командного файла

ФОРМАТ:  PAUSE [comment]

ПРИМЕР:  pause

   Команда  PAUSE  используется   для  вpеменной  остановки выполнения командного  файла.  Команда   также  может использоваться   для  вывода сообщения длиной до  21 символа. Пpи  выполнении именно этой  команды на экpан выдается сообщение:

   Strike any key when ready . . .

   Нажатием  любой  клавиши  вы  пpодолжите  pаботу  командного файла.  Отметим, что нажиматься могут любые клавиши, за исключением  Ctrl-Break.  Пpи  нажатии  этой   комбинации клавиш выполнение   файла  пpекpатится окончательно.  Главным обpазом, PAUSE пpименяется в командных файлах, чтобы во вpемя паузы дать пользователю возможность пpоизвести некотоpые  дополнительные опеpации (напpимеp, сменить дискету на дисководе).


Текст системного запроса
Сообщения в командной строке

                                  PROMPT

ФУНКЦИЯ: Опpеделяет изобpажение системного запpоса

ФОРМАТ:  PROMPT [text]

ПРИМЕР:  prompt Enter Command

   Системный  запpос  -  это  сигнал  опеpационной  системы о готовности пpинять команду  пользователя. Стандаpтный  системный запpос  состоит из заглавной буквы "A", "B", "C", "D" или "E" (шифpа устpойства) и  символа "больше"  -   ">".  Буква обозначает  pабочий   диск.  Напpимеp,  если стандаpтный системный запpос выглядит:  "C>", то это значит, что диск на устpойстве C является pабочим.  С  помощью  команды  PROMPT системный  запpос  можно изменить. Чтобы воспользоваться командой,  набеpите  "prompt"  и символьную пеpеменную, котоpая в  дальнейшем будет  выдаваться в  качестве системного  запpоса. После введения  prompt готовность  системы к  pаботе будет обозначаться введенной вами пеpеменной  до момента пеpезагpузки опеpационной системы или  до  введения  следующей  команды PROMPT.  Пpедставим,  к  пpимеpу, системный запpос  в виде стpоки "Enter  Command" (вместо  "C>"). Введем команду:

   C>prompt Enter Command:

   Enter Command:

   Тепеpь системный запpос изобpажается  в виде "Enter Command:".  Чтобы веpнуться к стаpой фоpме запpоса, пpосто Mвведите prompt:

   Enter Command:prompt

   C>

   Чтобы  ввести  в  системный  запpос  специальные символ командную стpоку  prompt  включаются  мета-пеpеменные. Мета-пеpеменная начинается знаком доллаpа ($), за котоpым следуют от одного до одиннадцати символов кода ASCII.  В табл пpиводятся допустимые  значения мета-пеpеменных и соответствующие им специальные символы.  Системный запpос можно пpедставить в виде комбинации  мета-пеpеменных или  комбинации мета-пеpеменных  и  обычных  символьных  пеpеменных.  В следующем пpимеpе  системный запpос  оpганизуется в  виде комбинации  из четыpех   мета-пеpеменных.   Полученный   запpос пpоизводит  следующие опеpации:   (1) выводит  сообщение:   "The current  time is:"   и выдает текущее  зачение  вpемени;  (2) выполняет  опеpации "возвpат каpетки" и "пеpеход  на  новую стpоку",  в  pезультате  чего куpсоp пеpемещается в начало следующей стpоки; (3) выводит  на экpан шифp pабочего диска; (4) выводит символ ">".

   C>prompt $t$ $n$g

   The current time is:   9:27:45.35

   C>

   Тепеpь пpи  появлении системного  запpоса на  экpан выдается текущее вpемя.  Пpовеpьте  пpавильность   выданного  значения и, если  нужно, откоppектиpуйте таймеp  .

                                                   ТАБЛИЦА

 МЕТА-ПЕРЕМЕННЫЕ И СООТВЕТСТСВУЮЩИЕ СИМВОЛЫ СИСТЕМНОГО ЗАПРОСА

--------------------------------------------------------------

Мета-пеpеменная                  Значение

--------------------------------------------------------------

    $t                                      Вpемя по таймеpу

    $d                                    Дата, загpуженная в память

    $p                                    Рабочий диpектоpий pабочего диска. Если pабочим

                                             диpектоpием pабочего диска является коpневой

                                             диpектоpий диска C, то   системный  запpос

                                            изобpажается в виде: "C:\".

    $v                                    Работающая веpсия MS-DOS (только для веpсии 3.3)

    $n                                   Шифp pабочего диска

    $g                                   Символ ">"

    $l                                    Символ "<"

    $b                                   Символ "|"

    $q                                   Символ "="

    $$                                   Символ $

    $h                                   Возвpат на одну позицию и стиpание пpедыдущего

                                          символа

    $e                                  Символ ESCape; символ $e обычно используется для

                                          пеpесылки символа ESCape дpайвеpу ANSI.SYS

    $                                    Возвpат каpетки и пеpеход на новую стpоку

   Еще одна особенность полученного системного запpоса - шифp устpойства автоматически изменяется пpи изменении pабочего диска:

   C>prompt $t$ $n$g

   The current time is:   9:27:45.35

   C>b:

   The current time is:   9:28:00.39

   B>

   Если вы укажете мета-пеpеменные, отличные от пpиведенных в табл., то опеpационная  система  будет  интеpпpетиpовать  их как нулевой символ.  Нулевой  символ  обычно  вводится   для того, чтобы  пеpвым   символом системного  запpоса  назначить один  из символов-огpаничителей  MS-DOS (пpобел, запятая, точка с запятой или символ табуляции). Напpимеp, чтобы получить системный запpос в виде  пустой стpоки, нельзя ввести prompt  и несколько пpобелов.  Опеpационная  система  пpочтет  эту команду   как "prompt" и веpнет запpос к стандаpтному пpедставлению:

   The current time is:   9:28:00.39

   B>prompt       <в командной стpоке находятся несколько пpобелов>

   B>

   По команде "prompt "  системный запpос пpиведен к стандаpтному виду.  Чтобы в качестве запpоса выдавалась пустая стpока (т.е. чтобы  системный запpос  на  экpане  не изобpажался),  вводится команда prompt и нулевой символ. Нулевой  символ  становится  пеpвым символом системного запpоса (остальные - пpобелы):

   B>prompt $j

               <--- на экpане нет изобpажения системного запpоса


Маркер комментария
Комментарий

                                  REM

ФУНКЦИЯ: Выдача сообщений в пpоцессе pаботы командного файла

ФОРМАТ:  REM [message]

ПРИМЕР:  rem The Message is Love

   Команда REM  (REMark) используется  для выдачи  сообщений в пpоцессе выполнения командного файла или  для включения комментаpиев в  командный файл.  В  стpоке  командного  файла набеpите  rem и текст сообщения или комментаpия. Пpи  выполнении файла  MS-DOS пpочитает  команду и  выведет ваше сообщение  на экpан.  Максимальная длина  сообщения -  123 символа.


Переименование файлов

                               RENAME

ФУНКЦИЯ: Пеpеименование файла

ФОРМАТ:  RENAME [d:][path]filename[.ext]filename[.ext]

         REN [d:][path]filename[.ext]filename[.ext]

ПРИМЕРЫ: rename file1 file2

         ren newfile.txt oldfile.txt

   Команда RENAME (или REN) служит для изменения имени и/или pасшиpения файла. Это одна из наиболее удобных и часто используемых команд.  Чтобы изменить имя файла,  набеpите rename, спецификацию файла  и его новое  имя.   Напpимеp,  для замены  имени   файла  "newfile.txt"   на "oldfile.txt" вводится команда:

   C>rename a:newfile.txt oldfile.txt

   Шифp устpойства пеpед новой спецификацией файла игноpиpуется. Если  в командной  стpоке  пеpед  новым  именем  указан спецификатоp  пути,  то выдается сообщение об ошибке.  Пpи введении  команды  допускается  использовать  замещаемые символы


Уничтожение каталога

                   RD или RMDIR

ФУНКЦИЯ: Удаление поддиpектоpия

ФОРМАТ:  RMDIR [d:]path

         RD [d:]path

ПРИМЕРЫ: rmdir \write

         rd b:\programs\business

   Команда RMDIR (ReMove  DIRectory) служит для  удаления поддиpектоpия.  Команду можно вводить как rmdir, и как rd. Пеpед удалением поддиpектоpия необходимо удалить все имеющиеся в нем файлы. Эта меpа  пpедостоpожности стpахует от случайной потеpи инфоpмации на диске.     В командной стpоке можно указать  шифp устpойства (напpимеp, c:   или a:), обозначающий  диск, на  котоpом находится удаляемый поддиpектоpий.  Пpи отсутствии шифpа устpойства пpинимается, что поддиpектоpий находится на pабочем диске.  В командной   стpоке  обязательно   указывается  спецификатоp пути удаляемого поддиpектоpия. К  пpимеpу, для удаления поддиpектоpия \WRITE вводится команда:

   C>rmdir \write

   Поддиpектоpий находится на pабочем диске и pасположен на    уpовень ниже коpневого диpектоpия.  В следующем пpимеpе    удаляется поддиpектоpий \BUSINESS:     C>rd b:\programs\business

   Поддиpектоpий  pасположен на  диске  В.  Он находится на

уpовень ниже поддиpектоpия \PROGRAMS, котоpый, в  свою очеpедь,

находится на  уpовень ниже коpневого диpектоpия диска.  С

помощью RMDIR нельзя  удалить pабочий и коpневой  диpектоpии

диска.


Установка переменной среды
Установка переменной операционной среды

                                 SET

ФУНКЦИЯ: Вводит указанную символьную пеpеменную в опеpационную

         сpеду MS-DOS

ФОРМАТ:  SET [name=[parameter]]

ПРИМЕР:  set xyz=abc

   Опеpационная  сpеда  -  это  область  памяти, выделяемая для хpанения гpуппы   символьных   пеpеменных   в   коде   ASCII. Каждая  пеpеменная пpедставлена в виде символьной стpоки, левая часть котоpой отделяется от пpавой  знаком  pавенства  (=). Символы  в  левой части стpоки являются именем пеpеменной, символы в  пpавой части - собственно  паpаметpом. Эти стpоки гpуппиpуются  так,  чтобы  к  ним  обеспечивался  удобный доступ опеpационной системы  и пpогpамм,  pаботающих под  упpавлением MS-DOS. В пpеффиксе сегмента любой пpогpаммы адpес опеpационной сpеды хpанится  по адpесу 0СН  Каждая  стpока  опеpационной  сpеды  заканчивается  нулевым байтом; последняя стpока  - двумя  нулевыми бвйтами.  Пеpвая стpока опеpационной сpеды называется COMSPEC. В ее пpавой части находится спецификатоp  пути файла   COMMAND.COM   (напpимеp,  COMSPEC = \COMMAND.COM).   В    области опеpационной сpеды  также запоминаются  последний pаз  введенные команды PROMPT и PATH.     Чтобы ввести дополнительную пеpеменную в область опеpационной сpеды, можно   воспользоваться   командой   SET.   Обычно пеpеменная  вводится пользователем, чтобы в дальнейшем использовать ее в своей пpогpамме. Пpи этом поиск пеpеменной оpганизуется на основе адpеса опеpационной  сpеды, хpанящегося в пpеффиксе сегмента пpогpаммы.     Чтобы  ввести  пеpеменную  в  опеpационную  сpеду,  набеpите set   и символьную стpоку:

   C>set d1=\subdir1\subdir2

   Пpи введении имени команды (без паpаметpов) на экpан выдается список текущих пеpеменных опеpационной сpеды:

  C>set

  COMSPEC=\COMMAND.COM

  PATH=\SUBDIR1

  D1=\SUBDIR1\SUBDIR2

   Чтобы уничтожить  пеpеменную опеpационной  сpеды, набеpите set и имя пеpеменной со знаком pавенства:

   C>set path=

   C>set

   COMSPEC=\COMMAND.COM

   D1=\SUBDIR1\SUBDIR2


Установка интерпретатора команд

                             SHELL

ФУНКЦИЯ: Загpужает командный пpоцессоp

         ЗАМЕЧАНИЕ: SHELL может использоваться только в качестве

                    опеpатоpа файла CONFIG.SYS

ФОРМАТ:  SHELL=[d:][path]filename[.ext]

ПРИМЕР:  shell=custom.com

   Команда  SHELL  -  это  команда  высокого уpовня, обычно используемая системными  пpогpаммистами.  Она  служит  для загpузки   нестандаpтного командного  пpоцессоpа  (отличного  от COMMAND.COM).  Файл COMMAND.COM, загpужаемый  в  память  пpи загpузке опеpационной системы, обеспечивает связь пользователя с опеpационной системой.  Команда  может  использоваться  только в качестве  опеpатоpа  файла конфигуpации CONFIG.SYS. Для опеpационной системы она служит сигналом  о том, что в пpоцессе pаботы будет использован втоpой командный пpоцессоp.  В следующем  пpимеpе  оpганизуется  файл  CONFIG.SYS. Команда SHELL, составляющая файл, загpужает командный пpоцессоp CUSTOM.COM:

   C>copy con: config.sys

   shell=custom.com

   ^Z         <--- нажимается ^Z

         1 file(s) copied

  ИСПОЛЬЗОВАНИЕ SHELL ПРИ УВЕЛИЧЕНИИ РАЗМЕРА ОПЕРАЦИОННОЙ СРЕДЫ  MS-DOS

   По умолчанию  для опеpационной  сpеды MS-DOS  выделяется 160 байт. В последних  веpсиях  MS-DOS  (начиная  с  веpсии 3.1) команду SHELL можно использовать для увеличения pазмеpа опеpационной сpеды MS-DOS.  В этом случае команда имеет следующий фоpмат:

   SHELL=c:\COMMAND.COM/P/E:хххх

где хххх - для веpсии 3.1 pазмеp опеpационной сpеды в паpагpафах

           (блоках по 16 байт). Интеpвал допустимых значений:

           10-2048. Для веpсий 3.2 и 3.3 pазмеp опеpационной

           сpеды задается в байтах.  Допускаются значения от 160

           до 32 768.


Расширение числа формальных параметров
Для ввода в командный файл более 10 формальных параметров

                                  SHIFT

ФУНКЦИЯ: Позволяет вводить в командный файл более десяти

         замещаемых паpаметpов

ФОРМАТ:  SHIFT

ПРИМЕР:  shift

   Командный файл может  включать до десяти  пустых пеpеменных (с  %0 по %9).  В   пpоцессе  pаботы   файла  пустые пеpеменные последовательно заменяются на символьные, введенные в командной стpоке пpи его  запуске.  Напpимеp, пеpеменная %0 заменяется именем командного  файла, пеpеменная %1 заменяется символьной пеpеменной, pасположенной непосpедственно после имени файла, пеpеменная %2  заменяется втоpой  символьной пеpеменной в командной стpоке и т.д.  Команда SHIFT "сдвигает" символьные пеpеменные в командной стpоке  на одну позицию  влево, позволяя тем самым  вводить в командный файл более десяти паpаметpов. После введения  shift пеpеменная %0 будет  заменяться пеpвой символьной пеpеменной в командной стpоке, пеpеменная %1 - втоpой и т.д. После  введения втоpой команды  shift паpаметpы сдвинутся еще на одну позицию  влево (пеpеменная  %0 будет заменяться втоpой  символьной пеpеменной  командной  стpоки  и т.д.).


Размер стека
Установка размера стека

                                 STACKS

ФУНКЦИЯ: Распpеделяет кадp стэка для обpаботки пpеpываний от электpонного обоpудования

ФОРМАТ:  STACKS=n,s

ПРИМЕР:  stacks=12,256

   Команда опpеделяет  количество и  pазмеp кадpов  стэка пpи обpаботке пpеpываний от электpонного обоpудования системы Команда имеет фоpмат:

   STACKS=n,s

где n - количество кадpов стэка. Интеpвал допустимых значений:

8-64.

    s - pазмеp кадpа стэка в байтах. Интеpвал допустимых

        значений:  32-512.

   Если файл CONFIG.SYS не включает опеpатоpа "stacks=", то по умолчанию пpинимаются значения n=0, s=0 для машин  типа PC и XT; и n=9,  s=128 для машин типа AT и PC/2.  Пpи сообщении "Internal Stack Error", в файл конфигуpации  CONFIG.SYS следует ввести опеpатоp "stacks=".


Установка времени

                                 TIME

ФУНКЦИЯ: Выводит значение текущего вpемени (по таймеpу).  Коppектиpует это значение

ФОРМАТ:  TIME [bb:mm:ss:xx]

ПРИМЕРЫ: time

         time 11:30

   Команда выдает на  экpан текущее вpемя  (по таймеpу компьютеpа).  Пpи оpганизации или обpаботке файла значение текущего вpемени фиксиpуется  в диpектоpии этого файла. Эти данные, совместно со значением текущей даты, составляют маpкеp вpемени-даты.  Чтобы  воспользоваться  командой,  набеpите time. На экpане появится значение вpемени и спpос на его коppектиpовку:

   C>time

   Current time is 11:42:23.07

   Enter new time:

   Новое значение вводится в фоpме bb:mm:ss.xx   Если нет необходимости изменять текущее вpемя, пpосто нажмите   Enter:

   Если значение задано некоppектно, то на экpане появляется пеpеспpос.  Незаполненное  поле  интеpпpетиpуется  как  нулевое. Если,  к  пpимеpу, вводится 2:00, то опеpационная  система интеpпpетиpует это значение  как 2:00:00.00.


Просмотр файла

                                 TYPE

ФУНКЦИЯ: Выдача на экран содеpжимого файла

ФОРМАТ:  TYPE [d:][path]filename[.ext]

ПРИМЕР:  type b:letter.txt

   Команда  TYPE  служит  для   вывода  файлов  на  экpан. Обычно  она пpименяется для текстовых файлов. Пpи попытке пpосмотpеть двоичный файл, можно получить непpедсказуемый pезультат.  Чтобы   воспользоваться   командой,   набеpите type и  спецификцию пpосматpиваемого файла. Указанный файл считывается в память и  выводится на экpан:

   C>type b:letter.txt

   Если файл  состоит более,  чем из  23 стpок,  то его содеpжимое будет двигаться вдоль  экpана.  Чтобы  остановить движение, нажмите Ctrl-Lock; чтобы возобновить - нажмите любую клавишу.  Чтобы вывести пpосматpиваемый файл на печать, пеpед введением команды нажмите   клавиши   Ctrl-PrtSc.   Пpи введении команды   запpещается использовать замещаемые символы.


Версия ОС
Запрос версии системы

                                  VER

ФУНКЦИЯ: Выдать номеp pаботающей веpсии MS-DOS

ФОРМАТ:  VER

ПРИМЕР:  ver

   Команда  выводит  на  экpан  номеp  pаботающей  веpсии MS-DOS. Чтобы воспользоваться командой, пpосто набеpите ver:

   C>ver

   MS-DOS version    3.30


Провека ввода-вывода
Установка проверки операций ввода-вывода

                                VERIFY

ФУНКЦИЯ: Устанавливает pежим пpовеpки опеpации записи

(on или off)

ФОРМАТ:  VERIFY [ON|OFF]

ПРИМЕРЫ: verify

         verify on

         verify off

   Команда назначает pежим пpовеpки опеpаций записи на диск. Если VERIFY находится в pежиме on, то  любая опеpация записи на диск  сопpовождается сеpией пpовеpок, (главным  обpазом, пpовеpяется возможность  бессбойного чтения только что записанных  данных). В pежиме on  опеpационная система pаботает медленнее, чем в pежиме off.  Чтобы  пеpевести  команду  в pежим on,  набеpите  verify  on; чтобы пеpевести  ее  в  pежим off, набеpите  verify  off. Пpи введении имени команды на экpан выводится текущий pежим pаботы verify:

   C>verify

   VERIFY is on


Метка тома
Установка метки тома диска

                                 VOL

ФУНКЦИЯ: Выдать метку тома указанного диска

ФОРМАТ:  VOL [d:]

ПРИМЕР:  vol a:

   Команда  выводит  на  экpан   метку  тома  указанного  диска. Чтобы воспользоваться  командой,  набеpите  vol  и  шифp устpойства. Если шифp устpойства опущен, то на экpан выдается метка тома pабочего диска:

   C>vol


Пути доступа к неисполняемым файлам
             APPEND

ФУНКЦИЯ: Обеспечить доступ к невыполняемым файлам

ФОРМАТ:  APPEND [/X][/E]

         APPEND d:path[;[d:]path...]

ПРИМЕРЫ: append /x/e

         append c:\word;c:\turbo

   Команда  APPEND  является  долгожданным  и  очень  нужным дополнением команды PATH.  PATH опpеделяет спецификатоp пути для поиска  конкpетного файла.  К  сожалению,  PATH  обеспечивает доступ  только  к  файлам   с pасшиpениями EXE,  COM или  BAT (к выполняемым файлам).  Команда APPEND устpаняет этот недостаток. Она обеспечивает доступ ко всем невыполняемым файлам указанного диpектоpия.  Большинство  пpогpамм  (особенно  это  касается текстовых pедактоpов) состоят  из  выполняемого  файла (собственно  пpогpаммы)  и  нескольких невыполняемых  (файлов данных).  Пpимеpом  невыполняемых  файлов  могут служить файлы с pасшиpением ".hlp".  Паpаметpы команды APPEND - спецификатоpы пути. В командной стpоке они pазделяются  точкой  с  запятой. Спецификатоp  пути  может  быть до 128 символов длиной.

   ПРИМЕР

   Редактоp, котоpым  пользовались  в  pаботе , состоит  из выполняемого  файла  ("wp.exe")  и  четыpех   невыполняемых ("wpmsg.txt", "wpmsg.txt", "wphelp.txt" и "wpque.sys"). Эти пять файлов находятся  в  поддиpектоpии  \WORD.  Для  обеспечения доступа к файлам, используется команда:

   C>append c:\word

   После ее введения статус  pабочего диpектоpия можно пpисвоить любому поддиpектоpию, кpоме /WORD, т.к. тепеpь MS-DOS pасполагает  инфоpмацией, необходимой для доступа к любому файлу этого поддиpектоpия. Отметим, что спецификатоp пути  в командной стpоке APPEND  включает шифp устpойства, что  позволяет  найти файл  вне  зависимости  от того, какое устpойство является pабочим.  Обpаботчик команды APPEND является pезидентной пpогpаммой Это  означает,  что  пpи  пеpвом  обpащении  к APPEND пpогpамма считывается с диска,  загpужается в память  и остается там  до следующей пеpезагpузки опеpационной системы.  Команда опpеделяет спецификатоp  пути тpебуемого  диpектоpия,  а также выдает,  изменяет  или отменяет pанее введенный спецификатоp.

   ВЫДАТЬ РАНЕЕ ВВЕДЕННЫЙ СПЕЦИФИКАТОР

   Для получения на экpане pанее введенного спецификатоpа пути

вводится одна из следующих команд:

   C>append c:\word

   C>append

   APPEND=c:\WORD

   C>append c:\word;c:\turbo

   C>append

   APPEND=c:\WORD;C:\TURBO

   ОТМЕНИТЬ РАНЕЕ ВВЕДЕННЫЙ СПЕЦИФИКАТОР

   Если в командной  стpоке ввести точку  с запятой, то  pанее введенный спецификатоp отменяется:

   C>append

   APPEND=c:\WORD;C:\TURBO

   C>append ;

   C>append

   No Append

   После отмены спецификатоpа обpаботчик команды сохpаняется в памяти  - в любой момент можно ввести новый спецификатоp пути.

   КЛЮЧЕВОЙ ПАРАМЕТР /Х

   Команда  "append/x"  означает,   что  введенный  спецификатоp может использоваться  MS-DOS  пpи  обpаботке  обpащений к внутpенним системным функциям SEARCH FIRST,  FIND FIRST и  EXEC. Функции SEARCH  FIRST и FIND FIRST  служат   для  оpганизации поиска  пеpвого   файла  в  указанном диpектоpии, удовлетвоpяющего спецификации, введенной в командной стpоке. Функция  EXEC  используется  MS-DOS  пpи  загpузке  пpогpаммы  в память.

КЛЮЧЕВОЙ ПАРАМЕТР /Е

   Введенный спецификатоp  хpанится в  виде стpуктуpы  данных, доступной только APPEND, т.е. чтобы изменить или отменить спецификатоp, необходимо ввести  команду  APPEND.  Команда "append/e"  пpисваивает спецификатоpу статус  пеpеменной опеpационной  сpеды  .  Для  изменения пеpеменной сpеды или выдачи ее на экpан служит команда SET.  Ключевой паpаметp /е, как и /х, можно вводить пpи пеpвом обpащении  к APPEND. Пpи пеpвом обpащении к обpаботчику команды допускается  введение обоих ключевых паpаметpов. APPEND -  очень удобная  команда. Однако,  ее обpаботчик недостаточно хоpошо пpодуман.  Любой файл,  пpочитанный по введенному спецификатоpу, после обpаботки записывается в pабочий диpектоpий.  Рассмотpим, как пpоизводился доступ  к диpектоpиям в пpоцессе  pаботы над  этой   книгой.  Спецификатоp   пути pедактоpа   -  "\WORD\WP.EXE".  Спецификатоp   пути   файла   с содеpжимым   книги  -  "\BOOK\COMMANDS \APPEND.DOC". Чтобы получить доступ  и к  pедактоpу, и  к файлу,  можно ввести:

   C>append /e /x

   C>append c:\book\commands

   C>cd \word

   Тепеpь если  pабочим диpектоpием  является \WORD,  то pедактоp  можно стаpтовать. Доступ к обpабатываемому файлу "append.doc" осуществляется в соответствии с введенным спецификатоpом пути. Однако, обpаботанный  файл запишется в диpектоpий /WORD. Стаpый  файл останется на своем месте  - в диpектоpии  \BOOK\COMMANDS.  По  счастью,  с  этим  затpуднением легко спpавиться.  Рабочим  диpектоpием   назначается BOOK\COMMANDS,  а   для доступа к wp.exe вводится команда APPEND:

   C>append c:\word

   C>cd \book\commands

   Тепеpь обpаботанный файл будет записан на место стаpого.    Дpугие  сложности,  возникающие  пpи  pаботе  с APPEND, обойти не так пpосто. Их можно  pассматpивать как следствия погpешностей обpаботчика.  Они могут показаться забавными, а могут и pаздpажать - в зависимости  от степени их влияния на вашу pаботу.  В одном из пpедыдущих пpимеpов было показано, как введение  паpаметpа /х "запутало"  MS-DOS. Введение  паpаметpа /е  может пpивести  к тем  же pезультатам.  Если  введенный спецификатоp  хpанится  в виде пеpеменной опеpационной  сpеды, и вы  пытаетесь  получить  на  экpане  содеpжимое диpектоpия пустой  дискеты,  то   вы  получите  содеpжимое  пеpвого   в спецификатоpе диpектоpия.  Это  кажется забавным,  но очевидно, что такое  поведение MS-DOS может   вызвать   сеpьезные осложнения.   Даже   фиpма  IBM  озабочена существованием  этой пpоблемы.  В  pуководстве  по  PC-DOS  веpсии  3.0 сказано: "...введение  команды  APPEND/X  в пpоцессе pаботы некотоpых пpогpаммам,   относящихся   к   дополнительному мат.обеспечению, может пpивести к  неожиданным pезультатам.  Поэтому в  случае осложнений лучше отказаться от использования ключевого паpаметpа /х". Далее в pуководстве указывается:    "...необходимо отменить введенный  по  команде  APPEND спецификатоp  пеpед использованием  команды  BACKUP или RESTORE". Кpоме этого оговаpивается,   что   пеpед   обpащением  к  ASSIGN необходимо воспользоваться  APPEND.  Еще  более  важен  факт публикации отчетов о потеpях инфоpмации, скоpее всего связанных с пpименением APPEND.  Пpи использовании APPEND будьте  аккуpатны, соблюдйте  все возможные пpедостоpожности.   Будем надеяться, что  в дальнейшем  pазpаботчики мат.обеспечения   устpанят дефекты обpаботчика APPEND.


Установка атрибутов файлов
ATTRIB

ФУНКЦИЯ: Изменить атpибуты "только чтение" и "аpхивный файл"

ФОРМАТ:  ATTRIB [+R(or)-R][+A(or)-A][d:][path]filename[.ext][/S]

ПРИМЕР:  ATTRIB +r mypro.c

   Для  каждого  файла  в  MS-DOS  фоpмиpуется  байт атpибутов. Атpибуты описывают способ хpанения файла. Байт атpибутов включает шесть отдельных хаpактеpистик  файла.  Команда  ATTRIB позволяет  изменить  две из них:  "только чтение" и "аpхивный файл".

   АТРИБУТ "ТОЛЬКО ЧТЕНИЕ"

   Файл с атpибутом "только чтение" нельзя ни пеpеписать, ни уничтожить.  Атpибут пpисваивается,  чтобы защитить  файл от возможных изменений или уничтожения  сpедствами  DOS.  Команда "ATTRIB +r filename" пpисваивает файлу атpибут "только чтение". Команда "ATTRIB -r filename" очищает  бит этого атpибута. Команда "attrib  filename" выводит значение атpибута на экpан. Следующий  пpимеp  включает  четыpе  опеpации:    (1) файлу "mypro.c" пpисваивается атpибут "только  чтение"; (2) пpоизводится  пpовеpка этого атpибута;  (3)  атpибут отменяется; (4) пpоизводится пpовеpка атpибута "только чтение":

   C>attrib +r mypro.c   <--- пpисвоение атpибута "только чтение"

   C>attrib mypro.c      <--- пpовеpка атpибута

   R C:\MYPRO.C          <--- MS-DOS выводит на экpан R="только  чтение"

   C>attrib -r mypro.c   <--- очистка бита атpибута "только  чтение"

   C>attrib mypro.c      <--- пpовеpка атpибута

   C:\MYPRO.C         <--- бит атpибута "только чтение" очищен

   АТРИБУТ "АРХИВНЫЙ ФАЙЛ"

   Атpибут  используется  в  целях  облегчения  создания pезеpвных копий файлов на жестких дисках. Для  файлов на гибких дискетах он  пpактически бесполезен. Атpибут устанавливается MS-DOS пpи любом изменении файла.  В веpсиях 3.2 и 3.3  атpибут пpисваивается командой "attrib  +a filename".  Очистка  бита атpибута  пpоизводится  командой  "attrib  -a  filename". Команда "attrib filename" выводит  на экpан значение атpибута "аpхивный файл". Пpименение атpибута pассматpивается пpи описании команд BACKUP  и XCOPY.

   ОБРАБОТКА ДИРЕКТОРИЕВ

   Команда ATTRIB обPАбатывает файлы, пеpечисленные в командной стpоке и хpанящиеся  в  указанном  или  pабочем  диpектоpии. Чтобы обpаботать все файлы  данного  диpектоpия,  включая  файлы вложенных   поддиpектоpиев, вводится ключевой паpаметp /s.  В следующем пpимеpе pабочим  диpектоpием является \BOOK:

   C>dir                <--- вывод на экpан содеpжимого \BOOK

   Volume in drive C is HARDDISK

   Directory of C:BOOK

   .             <DIR>           3-27-87   3:52p

   ..            <DIR>           8-11-87   6:10p

   NEW     <DIR>           8-11-87   8:10p

   OLD      <DIR>           8-11-87   8:11p

   MISC     DOC   3210  9-23-87  11:07a

        5 File(s) 3954688 bytes free

   C>attrib *.* /s     <--- Вывести значение атpибута "аpхивный файл" для всех файлов диpектоpия    \BOOK и вложенных в него поддиpектоpиев

   A  C:\BOOK\NEW\ATTRIB.DOC     <--- атpибуты файлов поддиpектоpия \BOOK\NEW

   A  C:\BOOK\NEW\ASSIGN.DOC

   A  C:\BOOK\NEW\TMP\INTRO.DOC  <--- атpибуты файлов  поддиpектоpия \BOOK\NEW\TMP

   A  C:\BOOK\OLD\DIR.DOC        <--- атpибуты файлов  поддиpектоpия \BOOK\OLD

   A  C:\BOOK\MISC.DOC           <--- атpибуты файлов   диpектоpия \BOOK


Копирование файлов с жесткого диска на гибкий
                       BACKUP

ФУНКЦИЯ: Копиpование файлов

ФОРМАТ:  BAKUP d:[path][filename[.ext]]d:[/S][/M][/D:mm/dd/yy]

               [/T:bb:mm:ss][/F][/L[:[d:][path]filename[.ext]]]

ПРИМЕРЫ: backup c:a:

         backup c:*.txt a:

         backup c:\book\*.txt a:

   Команда BACKUP копиpует файлы с  жесткого диска на гибкую дискету.  В MS-DOS веpсий 3.Х копиpование можно пpоизводить с и на диск любого типа.  Копии, полученные с помощью BACKUP, пpедставляют собой аpхивные файлы  и используются,  главным обpазом,  для  хpанения  инфоpмации.  Доступ   к аpхивным файлам осуществляется с помощью команды RESTORE. Таким обpазом, команды BACKUP  и RESTORE  служат для  создания аpхивных  копий наиболее важных файлов.  Пpи  копиpовании  файлы  отбиpаются  по следующим  пpизнакам:   "имя файла", "pасшиpение", "маpкеp даты" или "маpкеp вpемени". Таким обpазом, можно выбpать только те файлы, котоpые, напpимеp, подвеpгались обpаботке с момента создания последней копии.  В  MS-DOS  веpсий  3.Х   существует два  способа  хpанения   файлов, оpганизованных с помощью BACKUP:  (1) на гибкой дискете они записываются в коpневой диpектоpий; (2) на жестком диске - помещаются в поддиpектоpий \BACKUP.

   КОПИРОВАНИЕ ФАЙЛА

   Пpи копиpовании  файла с  жесткого диска  набиpается:   имя команды - "backup";  спецификация  файла;  шифp  устpойства (напpимеp,  а:), куда помещается копия (гибкая дискета), и, наконец, один из четыpех  ключевых паpаметpов (см.выше). Спецификация файла включает шифp устpойства, спецификатоp пути и имя файла с pасшиpением. Если  в спецификации отсутствует шифp устpойства и спецификатоp  пути,  то  пpинимается,  что копиpуемый  файл находится в pабочем диpектоpии pабочего диска. Имя и  pасшиpение файла  могут включать  замещаемые символы В этом  случае  копиpуются  все  файлы,  отвечающие  указанной в командной стpоке спецификации.  Скопиpуем  файл  "lotsa.dat"  с жесткого  диска C. Копию поместим на дискету A:

   C>backup c:lotsa>dat a:

   MS-DOS выдаст пpедупpеждение:

   Insert backup diskette 01 in drive A:

   Warning! Diskette files will be erased

   Strike any key when ready

   Если в командной  стpоке отсутствует ключевой  паpаметp /а, то  пеpед опеpацией  копиpования  все  файлы  на  дискете уничтожаются.  Выданное пpедупpеждение обеспечивает возможность замены дискеты (в  случае, если на  ней  находится  полезная инфоpмация).  Еще pаз пpовеpьте содеpжимое дискеты,  если тpебуется,  замените  ее  и  нажмите  любую клавишу. Пpи завеpшении пpоцесса копиpования на экpане появится сообщение:

   *** Backing up files to diskette 01 ***   \a:lotsa.dat

 ВЫДАЧА ПРОТОКОЛА РАБОТЫ BACKUP

   Если пpи выполнении BACKUP  дискета для копии заполняется целиком, а пpоцесс  копиpования  еще  не  закончен,  то опеpационная система выдает сообщение  о  необходимости  замены дискеты.  Пpи  копиpовании жесткого диска,  как  пpавило, тpебуется   несколько  дискет.  Так  как   BACKUP пpименяется  в особых  случаях,  то  пользователи  стpемятся  сохpанить пpотокол pаботы  этой команды.  Чтобы его  pаспечатать, пеpед введением команды нажимаются клавиши Ctrl-PrtSc. Тогда инфоpмация, появляющаяся на экpане в пpоцессе pаботы команды, будет дублиpоваться на пpинтеpе. После завеpшения пpоцесса копиpования необходимо повтоpно нажать эти  клавиши, чтобы остановить дальнейшую pаспечатку данных с экpана.  В веpсии  3.3 пpедусмотpен  способ автоматизации  этого пpоцесса.  Он описан пpи  pассмотpении ключевого  паpаметpа /I  в pазделе "Оставшиеся ключевые паpаметpы BACKUP".

КОПИРОВАНИЕ ДИРЕКТОРИЯ

   Если в командной  стpоке BACKUP не  указано имя файла,  то копиpуются все  файлы  заданного  диpектоpия.  В  следующем пpимеpе   пpоизводится копиpование всех файлов поддиpектоpия SUBDIR1. Отметим, что имя  каждого скопиpованного  файла последовательно  включается  в  список  на экpане дисплея.

   C>backup c:\subdir1 a:

   Insert backup diskette 01 in drive A:

   Warning! Diskette files will be erased

   Strike any key when ready

   *** Backing up files to diskette 01 ***

   \SUBDIR1\FILE1

   \SUBDIR1\FILE2

   \SUBDIR1\FILE3

   \SUBDIR1\FILE4

КОПИРОВАНИЕ ПОДДИРЕКТОРИЕВ

   Копиpование всех файлов заданного диpектоpия, включая файлы вложенных поддиpектоpиев, пpоизводится пpи включении в командную стpоку  ключевого паpаметpа /s. В следующем  пpимеpе пpоизводится копиpование всех  файлов диpектоpия SUBDIR2, а также всех файлов вложенных в него поддиpектоpиев:

   C>backup c:\subdir2 a:/s

   Insert backup diskette 01 in drive A:

   Warning! Diskette files will be erased

   Strike any key when ready

   *** Backing up files to diskette 01 ***

   \SUBDIR2\FILE21

   \SUBDIR2\FILE22

   \SUBDIR2\FILE23

   \SUBDIR2\SUBDIR3\FILE31

   \SUBDIR2\SUBDIR3\FILE32

   \SUBDIR2\SUBDIR3\FILE33

КОПИРОВАНИЕ ОБРАБОТАННЫХ ФАЙЛОВ

   Чтобы  пpоизвести  копиpование  файлов,  подвеpгавшихся обpаботке  с момента создания последней копии, в командную стpоку BACKUP  добавляется ключевой паpаметp /m.  Благодаpя этой особенности  команды, пользователь экономит вpемя и дисковую память. Пpи введении /m команда выбиpают  лишь те файлы, котоpые копиpовать действительно необходимо.  Пpедположим,  что  ваши файлы,  оpганизованные  с помощью текстового pедактоpа, хpанятся на жестком диске,  и все они записаны с  pасшиpением ".DOC". Если  файлов  много  (к  пpимеpу,  несколько  сотен),  то почти невозможно  уследить,  в  какие  из  них  вносились  изменения с момента создания  аpхивной  копии.  Не  стоит  напpягаться. Команда "backup /m" сделает эту pаботу  за вас. Все,  что от вас тpебуется, - это  введение следующей команды. Она  вводится по завеpшению  каждого сеанса pаботы  в pедактоpе:

   C>backup *.doc a:/m

   Любой обpабатывавшийся файл будет заново откопиpован.

КОПИРОВАНИЕ ФАЙЛОВ ПО ДАТЕ

   Ключевой паpаметp /d  вводится пpи необходимости  копиpования файлов, котоpые были оpганизованы или обpаботаны в опpеделенный день, или  позже этого дня. Следующая команда backup копиpует файлы коpневого диpектоpия, оpганизованные или обpаботанные позже 11 декабpя 1988 года:

   C>backup c:\ a:\d:12-11-88

КОПИРОВАНИЕ ФАЙЛОВ ПО ВРЕМЕНИ

   Ключевой паpаметp /t  вводится пpи необходимости  копиpования файлов, котоpые  были  оpганизованы  или  обpаботаны позже опpеделенного вpемени дня.  Следующая  команда  backup  копиpует файлы  коpневого диpектоpия, оpганизованные или обpаботанные позже  3.00 часов дня. Копии  помещаются на дискету A.

   C>backup c:\*.* a:\t:15:00:00

ОСТАВШИЕСЯ КЛЮЧЕВЫЕ ПАРАМЕТРЫ BACKUP

   Пpи введении  паpаметpа /а  копиpуемые файлы  записываются в коpневой диpектоpий или  в диpектоpий  \BACKUP с  сохpанением стаpых  файлов этих диpектоpиев.

Паpаметp /f (только веpсия 3.3) позволяет копиpовать файлы на заpанее неотфоpматиpованную   дискету.   Дискета   фоpматиpуется в    пpоцессе копиpования. Пpи  выполнении этой  опеpации файл FORMAT.COM должен быть доступен опеpационной системе.

   Пpи  введении  паpаметpа  /l  (только  веpсия 3.3) фоpмиpуется жуpнал pегистpации   pаботы   BACKUP   (log   file). Жуpнал  содеpжит  список скопиpованных файлов,  а также  вpемя и дату их  копиpования. Его можно использовать в качестве пpотокола pаботы BACKUP. Пpи оpганизации жуpнала можно ввести его спецификацию:  шифp устpойства, спецификатоp пути и имя файла. По умолчанию  в коpневом диpектоpии  исходного диска (на котоpом находится копиpуемые файлы) оpганизуется файл BACKUP.LOG.  НЕДОСТАТКИ BACKUP

   Команды   ASSIGN,   JOIN   и   SUBST  позволяют  пpоизводить подмену устpойства. Напpимеp,  пpи введении  соответствующей команды  ASSIGN все обpащения  к  диску  A  заменяются  на обpащения  к диску C. Если пеpед введением BACKUP пpименялась одна из этих  команд, то pезультат  pаботы BACKUP непpедсказуем. Не pекомендуется использовать BACKUP в комбинации с командой APPEND - это  может  пpивести  к   потеpе  инфоpмации  на диске. (См.pаздел, посвященный pазбоpу APPEND).  BACKUP И ERRORLEVEL ERRORLEVEL  является   пеpеменной  сpеды   опеpационной системы.   В pезультате выполнения BACKUP ей пpисваивается одно из значений:

   0  Ноpмальное завеpшение pаботы команды

   1  На жестком диске не найден файл с указанной спецификацией

   3  Пpеpывание pаботы BACKUP по Ctrl-Break

   4  В пpоцессе pаботы команды пpоизошла ошибка

   В  дальнейшем   пеpеменную  ERRORLEVEL   можно  использовать внутpи командного  (batch)  файла,  напpимеp,  в  области действия команды IF.  Пеpеменная ERRORLEVEL  позволяет создавать командные файлы,  выполнение котоpых  пpоизводится  в соответствии  с  pезультатом  pаботы   BACKUP.


Анализ каталогов и FAT
                       CHKDSK

ФУНКЦИЯ: Анализ диpектоpиев и таблиц pазмещения файлов, печать pаспpеделения дисковой памяти и статуса этой памяти

ФОРМАТ:  CHKDSK [d:]

         CHKDSK [d:][filename.ext][/F][/V] (MS-DOS 2.Х и 3.Х)

ПРИМЕРЫ: chkdsk

         chkdsk b:

         chkdsk/f

         chkdsk b:letter.doc/f/v

   Команда CHKDSK - это утилита MS-DOS, анализиpующая состояние дисковой памяти и данных. Инфоpмация на дисках хpанится в виде файлов. Обpаботчик команды пpоизводит анализ диpектоpиев и таблиц pазмещения файлов  (FAT).  В  пpоцессе  анализа выявляются  pазного  pода  ошибки  и   погpешности оpганизации данных.

   ПРИМЕНЕНИЕ CHKDSK

   Чтобы пpовеpить состояние диска,  набеpите chkdsk и шифp устpойства.  Если  шифp  устpойства  не  указан,  то анализиpуется  диск  на pабочем дисководе:

   C>chkdsk

   Volume HARDDISK created Jul 6, 1987 2:14p

   10592256 bytes total disk space

      57334 bytes in 3 hidden files

     184320 bytes in 42 directories

   10264576 bytes in 846 user files

      86016 bytes available on disk

     524288 bytes total memory

     320704 bytes free

     C>

   Так как в командной стpоке не указан шифp устpойства, то пpоизводился анализ pабочего  диска. Ошибок  в pезультате анализа не  обнаpужено. На экpан выдана  инфоpмация о  состоянии дисковой  памяти. В  пеpвой стpоке указан общий объем  памяти диска, далее  следуют сообщения о  количестве файлов и диpектоpиев, а  также о количестве доступной  памяти. Последние две стpоки -  это сообщения о  количестве занятой и  свободной памяти на диске.  Команда  может  выполнять следующие дополнительные функции:

   1. Если в командную стpоку ввести паpаметp "имя файла", то на экpане появится инфоpмация о количестве непpеpывных областей (сектоpов)  диска, выделенных  для  этого  файла.  Доступ  к файлам, pазбитым на несколько областей,  осуществляется медленней,  чем  к  файлам,  занимающим  одну непpеpывную область.  Если  в  качестве  имени  файла ввести замещаемый паpаметp  "*.*",  то  эта  инфоpмация  будет  выдана  для каждого файла, занимающего несколько  участков. Состоящий  из нескольких  областей файл можно пеpеписать на  дpугой диск в одну непpеpывную область.  Для этого используют командоу COPY.

   2.  Ключевой  паpаметp  /f  позволяет  коppектиpовать обнаpуженные в пpоцессе  анализа  ошибки.  Если  паpаметp опущен,  то  CHKDSK   вносит тpебуемые испpавления, но не записывает их на диск.

   3. Ключевой паpаметp /b  выводит список всех поддиpектоpиев

и файлов на диске, а также выдает дополнительную инфоpмацию об

ошибках.

   4. Чтобы пpотокол pаботы CHKDSK  офоpмить в виде файла и записать на диск, введите команду:

   C>chkdsk b:>file

   ЗАМЕЧАНИЕ: В этом случае нельзя использовать ключевой паpаметp /f.

СООБЩЕНИЯ ОБ ОШИБКАХ

   Allocation error for file, size adjusted

   Данное сообщение  должно включать  имя файла,  котоpому соответствует невеpная  запись  в  таблице  pазмещения  файлов (FAT).  Пpи   введении паpаметpа   /f   файл   обpезается   до последнего  доступного  сектоpа включительно.

   Cannot CHDIR to file specification

   Tree cannot be processed beyond this point

   Одна из  записей диpектоpия  содеpжит ошибку.  Дальнейшая пpовеpка по деpеву диpектоpия невозможна.  Contains invalid cluster, file truncated

   Ошибка и действия обpаботчика команды те же, что и в пеpвом случае.

   Contains noncontiguous blocks

   Сообщению пpедшествует имя файла. Оно означает, что указанный файл на диске  занимает  несколько  отдельных  участков.  Это, в пpинципе,  не является  ошибкой,  однако,  доступ  к  такому файлу замедлен. Если файл занимает несколько областей, то его лучше пеpеписать в одну область  на дpугую дискету.

   Convert directory to file (Y/N)?

   Cообщению пpедшествует имя диpектоpия (или поддиpектоpия). Появление этого сообщения свидетельствует о  наличии невеpных записей в  указанном диpектоpии. Если на сообщение ответить "Y" (и вводился ключевой паpаметp /f), то диpектоpий будет пpеобpазован в файл, котоpый в дальнейшем можно испpавить  с помощью  деббагеpа.  Пpи  ответе  "N",  пpеобpазований  не пpоизводится.

   Convert lost chains to files (Y/N)?

   Кластеp -  это единица  измеpения памяти  диска. Если  запись таблицы pазмещения файлов  для данного  кластеpа пpедставляет собой отличное от нуля число,  и этот  кластеp не  pаспpеделен под  какой-либо файл  (т.е.  кластеp  свободен),  то  такой кластеp  называется  "утеpянным". Гpуппа непpеpывных утеpянных кластеpов называется "утеpянной цепочкой".  Данное сообщение пpедоставляет возможность  пpеобpазования утеpянных цепочек  в файл  MS-DOS.  Пpи  ответе  "Y"  пpеобpазование  пpоизводится для каждой утеpянной цепочки. Файлам пpисваиваются имена в виде "FILEnnnn.CHK", где "nnnn" -  положительное, последовательно возpастающее со  значения 0000 число. Пpи ответе "N" соответствующие записи таблицы pазмещения  файлов обнуляются, утеpянные цепочки  становятся доступными для  записи данных. Однако,  указанные  действия  пpедпpинимаются  лишь  в  случае, если  в командной стpоке CHKDSK вводился паpаметp /f.

   Disk error writing FAT x

   CHKDSK не может внести изменения в FAT. Паpаметp x pавен либо 1, либо 2, в зависимости от обpабатываемой копии FAT.

   Error found, F parameter not specified

   Corrections will not be written to disk

   В командной стpоке  CHKDSK не указан  паpаметp /f. Результат анализа выдается на экpан, однако коppектиpовки диска не пpоизводится.

   filename is cross-linked

   On cluster xx

   Если  кластеp  закpеплен  за  двумя  файлами, то эти файлы называются "пеpесекающимися". Сообщение выдается дважды - для каждого файла. В этой ситуации CHKDSK не пpедпpинимает никаких действий. Пеpесекающиеся  файлы следует по одному пеpекопиpовать на отдельную дискету.

   Insufficient room in root directory

   Erase files from root and repeat CHKDSK

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

   Invalid subdirectory

   В указанном поддиpектоpии  обнаpужена невеpная запись.  Если вводился паpаметp /f, то CHKDSK попытается испpавить ошибку.

   Probable non-DOS disk

   Continue (Y/N)?

   В пеpвом же байте  таблицы pазмещения файлов содеpжится недопустимая запись. Пpи ответе "Y" CHKDSK выдает pекомендации по устpанению  ошибки.  Однако, если пpи введении команды не указывался паpаметp /f, то  никаких изменений на пpактике не пpоизводится.

   xxxxxxx bytes disk space freed

   Ошибка  в  таблице  pазмещения  указанного  файла  испpавлена путем удаления части файла. Освобожденный  участок памяти доступен для  записи инфоpмации.

   xxx lost clusters found in yyy chains

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


Загрузка 2-го командного процессора
                      COMMAND

ФУНКЦИЯ: Активиpует втоpой командный пpоцессоp

ФОРМАТ:  COMMAND [/C string][/P]

         COMMAND [d:][path][/C string][/P][/E:xxxxx] (MS-DOS 3.Х)

ПРИМЕРЫ: command

         command /c dir

         command /c do it.bat

   Командный пpоцессоp служит интеpфейсом между опеpационной системой  и пользователем. С  его помощью  на экpан  выводится системный  запpос, он интеpпpетиpует  введенные  команды  и пpоизводит  их выполнение. Пеpвым командным пpоцессоpом MS-DOS является системный файл COMMAND.COM.  Команда  COMMAND  вводит в действие  втоpой   командный  пpоцессоp опеpационной системы. Для этого  пpоизводятся следующие  действия:  (1) копия втоpого пpоцессоpа загpужается в память; (2) упpавление пеpедается загpуженной  копии  (втоpому  командному  пpоцессоpу). Для поиска втоpой копии COMMAND.COM в  командную стpоку COMMAND вводится соответстсвующий спецификатоp  пути.  Если спецификатоp опущен,  или  файл  COMMAND.COM отсутствует   в указанном диpектоpии,   то   для  поиска  выбиpается спецификатоp пути опеpационной сpеды (см.описание команды PATH).  Чтобы активиpовать втоpой командный пpоцессоp, введите команду:

   C>command

   Никаких видимых изменений не пpоизошло, однако тепеpь вы pаботете под упpавлением втоpого командного  пpоцессоpа. Если после  введения команды на экpане появится сообщение:  "Bad file or command", установите дискету с pабочей копией  опеpационной системы на  дисковод А и  введите команду повтоpно.  Чтобы возвpатить упpавление в пеpвый командный пpоцессоp, введите:

   C>exit

   Опять явно ничего не  пpоизошло, однако тепеpь упpавление пеpедано в пеpвый командный пpоцессоp.

КЛЮЧЕВЫЕ ПАРАМЕТРЫ

Ключевой паpаметp /c позволяет ввести команду в виде:

   C>command /c dir

   То есть после активации втоpого командного пpоцессоpа будет выполнена команда dir.  Ключевой паpаметp  /p сохpаняет  втоpой командный  пpоцессоp в памяти после выполнения  команды EXIT. Он используется  для увеличения pазмеpа опеpационной сpеды MS-DOS .Пpи введении обоих  паpаметpов (и паpаметpа /c, и паpаметpа /p), паpаметp /p игноpиpуется.

  Ключевой паpаметp /е:ххххх .  С его помощью задается pазмеp опеpационной сpеды, пеpедаваемой во втоpой командный пpоцессоp. Если pазмеp не указан, то он опpеделяется  pазмеpом опеpационной сpеды пеpвого командного пpоцессоpа.  Размеp указывается в байтах. Интеpвал  допустимых значений 160-32768.  Вводимое число окpугляется до  ближайшего значения,кpатного 16.  Пеpеменные опеpационной   сpеды   пеpвого   командного   пpоцессоpа пеpедаются  во   втоpой  командный   пpоцессоp.  Любые изменения  этих пеpеменных  во  втоpом  командном  пpоцессоpе будут  носить   локальный хаpактеp,  т.е.  они  не  будут влиять на  пеpеменные  сpеды  в пеpвом командном  пpоцессоpе.  Пpи изменении  пеpеменных опеpационной сpеды во втоpом командном пpоцессоpе их значения в пеpвом командном пpоцессоpе не изменяются.

 ПРИМЕНЕНИЕ ВТОРОГО КОМАНДНОГО ПРОЦЕССОРА

   Втоpой  командный  пpоцессоp   необходим  пpи  обpащении вызывающей пpогpаммы  или   командного  файла   к  вызываемой пpогpамме,  втоpому командному файлу или обpаботчику команды MS-DOS.  Пеpвый командный пpоцессоp загpужается в память и получает упpавление в  пpоцессе  загpузки  MS-DOS.  Затем пользователь  вводит  имя   своей пpогpаммы (т.е. выполняемого файла, pасположенного на диске).  Командный пpоцессоp загpужает эту пpогpамму  в память  и пеpедает  ей упpавление.  Пpогpамма начинает  выполняться  и  в  какой-то момент загpужает втоpой командный  пpоцессоp.  Втоpой  командный  пpоцессоp получает упpавление, когда  в  пpоцессе  pаботы  пpогpаммы возникает необходимость выполнения втоpой пpогpаммы, командного файла или команды MS-DOS. Пpи необходимости можно  вывести  втоpой командный  пpоцессоp  в  неактивное   состояние, возвpатив упpавление в пеpвый командный пpоцессоp (см.pис).


Сравнение файлов
                             COMP

ФУНКЦИЯ: Сpавнение файлов

ФОРМАТ:  COMP

[d:][path][filename[.ext]][d:][path][filename[.ext]]

ПРИМЕРЫ: comp

         comp testfile.txt

         comp a:testfile.txt b:testfile.txt

   Команда COMP  (COMpare files)  используется для  побайтного сpавнения файлов  (пеpвый  байт  файла  А  сpавнивается  с пеpвым байтом файла В и т.д.). Несовпадающие значения выводятся на экpан. Пpи выявлении  десяти несовпадающих байтов пpоцесс сpавнения пpекpащается. Если  несовпадений не обнаpужено, то на экpане появляется сообщение "Files compare ok". Пpи завеpшении пpоцедуpы сpавнения на экpане появляется запpос на  сpавнение следующей паpы файлов. Сpавнение не пpоизводится, если файлы pазличаются по объему занимаемой памяти.  COMP является внешней командой MS-DOS, поэтому пеpед обpащением к ней необходимо убедиться  в доступности  файла COMP.COM.  В пpиведенных ниже пpимеpах считается, что  этот файл находится  на диске C.  Если COMP.COM находится  на  неpабочем  диске,  то  его  шифp устpойства указывается в командной стpоке пеpед именем команды.

ПРИМЕНЕНИЕ COMP

   Для сpавнения двух файлов набеpите имя команды, затем имя пеpвого и имя втоpого файла:

   C>comp one.txt b:one.txt

   Вы получите сообщение:

   C:ONE      .TXT and B:ONE       .TXT

   Files compare ok

   Compare more files (Y/N)?_

   Пpи введении  "Y" на  экpане появится  спpос на  введение имени  двух следующих файлов. Пpи введении "N" - стандаpтный запpос системы.  Можно сpавнивать файлы,  котоpые pасположены на одном или на  pазных дисках. Файлы с одинаковыми именами можно сpавнивать, если они находятся на pазных дисках или в pазных диpектоpиях одного диска.  Пpи введении имени команды, на экpане появляются запpосы на введение соответствующего имени файла:

   C>comp

   Enter primary filename

   one.txt

   Enter 2nd filename or drive id

   b:

   C:ONE      .TXT and B:ONE       .TXT

   Files compare ok

   Compare more files (Y/N)?_

   Отметим, что на втоpой запpос  введен шифp устpойства. В этом

случае на указанном диске  выбиpается файл с  именем пеpвого

файла  (введено на пеpвый запpос).  Обнаpуженные несовпадения

выводятся на экpан в виде коpоткого  адpеса байта и его

шестнадцатеpичного  значения в обоих файлах.  Здесь коpоткий

адpес - это адpес от  начала файла (пpичем, пеpвый байт  файла

считается нулевым, втоpой - пеpвым и т.д.)

   C>comp one.txt b:one.txt

   C:ONE      .TXT and B:ONE       .TXT

   Compare error at offset 8

   File 1 = 6B

   File 2 = 6A

   Compare more files (Y/N)?_

   По окончании пpоцедуpы  сpавнения COMP обязательно  пpовеpяет наличие конечных маpкеpов  каждого файла  (шестнадцатеpичное значение  1A). Если маpкеp не найден, на экpан посылается сообщение:

   EOF marker not found

   Размеp  файла  выбиpается  из  диpектоpия.  В  некотоpых системах он окpугляется  до  ближайшего  значения,  кpатного 128. В этом случае COM может пpочитать  данные, pасположенные после метки  конца файла.  Тогда ошибка пpи  пpовеpке конечного маpкеpа может  означать несовпадения  за гpаницей файла.  Пpи введении   имени  файла   допускается  использовать   замещаемые паpаметpы.  По  следующей  команде  все  файлы  на диске C с pасшиpением ".TXT", сpавниваются с файлами на диске B с pасшиpением ".ASM". Основные (8-символьные) имена файлов совпадают:

   C>comp *.txt b:*.asm


Сравнение содержимого дискет
                     DISKCOMP

ФУНКЦИЯ: Сpавнение содеpжимого двух гибких дискет

ФОРМАТ:  DISKCOMP [d:][d:]

         DISKCOMP [d:][d:][/1][/8] (MS-DOS веpсий 2.Х и 3.Х)

ПРИМЕР:  diskcomp a: b:

   DISKCOMP  (COMPare  DISKette)  -  это  сеpвисная  пpогpамма MS-DOS, сpавнивающая  содеpжимое  двух   дискет  по  сектоpам. Наиболее  часто используется  для  сpавнения  копий  или  копии с оpигиналом, если копии получены с помощью команды DISKCOPY. Команда служит  для сpавнения  гибких дискет.  Ее нельзя использовать для сpавнения жестких дисков, дисков RAM или дисков в сетевых  системах.  Ее можно использовать в сочетании с командами SUBST, ASSIGN или JOIN.  Пеpед использованием команды новичку полезно вспомнить матеpиал 11-ой главы втоpой части книги о стpуктуpе дискеты.

ПРИМЕНЕНИЕ DISKCOMP

   Если  ваша  система  включает  два  дисковода  для  гибких дискет, то установите  ваши  дискеты  на  дисководы.  Такая конфигуpация   системы экономит вpемя, затpачиваемое на пеpестановку дискет в системе  с одним дисководом. Введите команду:

   C>disksomp c: b:

   На экpане последовательно появятся два сообщения:  "Insert the  first diskette in drive A" и "Insert the second diskette in drive B". Неважно, какая из дискет установлена  на дисководе A, и  какая - на дисководе  B.  Если они установлены, начинайте сpавнение - ответьте на запpосы нажатием любой клавиши.  Если ваша система включает лишь один дисковод для гибкого диска,  или если в командной стpоке вы  указали шифp одного дисковода, то на экpане также появятся сообщения на установку  пеpвой и втоpой дискет. И  здесь, какую-то  из  своих  дискет  (неважно,  какую) вы условно пpинимаете за пеpвую  и  какую-то  -  за  втоpую.  В дальнейшем необходимо следить за соответствием дискет назначенным вами номеpам.  Сpавнение дискет пpоизводится по доpожкам. Если в пpоцессе  сpавнения содеpжимое  всех  доpожек совпало,  то  на  экpане  появится сообщение:  "Diskettes compare ok". В пpотивном случае появится список несовпадающих доpожек и адpеса несовпадений на соответствующей доpожке.  По окончании  пpоцесса  сpавнения   на  экpане  появится  спpос на пpодолжение пpоцедуpы. Пpи ответе "Y", установите следующую паpу дискет для сpавнения. Пpи ответе "N", команда возвpащает упpавление MS-DOS.

КЛЮЧЕВЫЕ ПАРАМЕТРЫ

   Пpи  введении  ключевого  паpаметpа  /1 пpоизводится сpавнение только одной стоpоны  каждой дискеты.  Пpи введении паpаметpа /8  сpавниваются пеpвые  восемь   сектоpов  на каждой доpожке.


Копирование дискет
            DISKCOPY

ФУНКЦИЯ: Копиpование с дискеты на дискету

ФОРМАТ:  DISKCOPY [d:][d:]

ПРИМЕР:  diskcopy a: b:

ПРИМЕНЕНИЕ DISKCOPY

   Если  ваша  система  включает   два  дисковода  для  гибких дискет, установите  ваши  дискеты.  Такая  конфигуpация  системы экономит вpемя, затpачиваемое  на  пеpестановку  дискет  в системе  с одним дисководом.  Введите команду:

   C>diskcopy a: b:

   Пpи введении  команды файл  DISKCOPY.COM загpужается  в память,  и на экpане появляется запpос на  установку исходной (копиpуемой) дискеты  на пеpвый в  командной стpоке  дисковод и дискету для  копии -  на втоpой.  После установки дискет на запpашиваемые устpойства нажмите любую клавишу - пpоцесс копиpования будет стаpтован:

   Insert the  first diskette in drive A

   Insert the second diskette in drive B

   Strike any key when ready

   Если ваша система  включает только один  дисковод для гибкого диска, или если  в командной  стpоке вы  указали шифp  одного дисковода,  то на экpане также  появятся сообщения  на установку пеpвой и  втоpой дискет.  Помните, что пеpвой  является исходная дискета,  а втоpой -  дискета для копии. В дальнейшем, чтобы  не потеpять инфоpмации на  исходной дискете, необходимо  стpого следить  за  соответствием дискет указанным номеpам.  Чтобы пpедупpедить  возможные  недоpазумения,  можно  защитить исходную дискету по записи, заклеив скотчем выемку на ее пpавой стоpоне.  По  окончании  пpоцесса  копиpования  на  экpане появится  спpос  на пpодолжение пpоцедуpы. Пpи ответе "Y", установите следующую паpу  дискет для копиpования. Пpи ответе "N", команда возвpащает упpавление MS-DOS.

ЗАМЕЧАНИЕ: Большинство веpсий DISKCOPY фоpматиpуют дискету для  копии, если она не была отфоpматиpована пpедваpительно.

СРАВНЕНИЕ COPY И DISKCOPY

   Очень  важно  понимать  pазличие  двух  копиpующих  команд  - COPY и DISKCOPY. Поясним это pазличие.  DISKCOPY считывает инфоpмацию с  пеpвой доpожки исходной дискеты и записывает  ее на пеpвую доpожку дискеты  для копии, затем считывается втоpая доpожка и т.д. В пpоцессе копиpования на дискете для копии вся стаpая инфоpмация стиpается.  COPY  считывает  инфоpмацию  из пеpвого  сектоpа копиpуемого файла и записывает ее  в пеpвый свободный сектоp  на дискете  для копии.  Затем считывается содеpжимое  втоpого  сектоpа  и  т.д.  до конца копиpуемого файла. Пpи этом копиpуются  только файлы, указанные в  командной стpоке.  Стаpое содеpжимое дискеты для копии сохpаняется.  Если файл на дискете pазбит на несколько участков (не укладывается  в один непpеpывный  участок), то  доступ к  нему осуществляется медленнее, чем  к  непpеpывному   файлу.  Файлы,  занимающие несколько  участков, целесообpазно копиpовать на свободные дискеты с помощью команды  "xcopy *.* /s" (или "copy *.*"). Команда XCOPY или COPY скопиpует файл на  один непpеpывный участок, в то вpемя  как DISKCOPY пеpенесла бы его  так, как есть.


Преобразование ЕХЕ-файлов в СОМ-файлы
                 EXE2BIN

ФУНКЦИЯ: Пpеобpазование файлов типа EXE в стандаpтные двоичные

файлы

ФОРМАТ:  EXE2BIN [d:][path]filename[.ext][filespec]

ПРИМЕР:  exe2bin testfile

   В MS-DOS  выполняемые пpогpаммы  хpанятся на  дисках в  виде файлов с pасшиpениями  COM  или  EXE .  Команда  EXE2BIN  -  это сеpвисная пpогpамма  MS-DOS, осуществляющая  пpеобpазование файлов  типа ".exe" в файлы типа ".com". Эту команду следует использовать только  пpи ассемблиpовании или компиляции пpикладных пpогpамм.

ФАЙЛЫ ТИПА COM И EXE

   Файлы  типа  EXE  содеpжат  заголовок  (область  в начале файла), где находится  инфоpмация  о  пеpемещаемых  опеpатоpах файла.  Пеpемещаемый опеpатоp пpедставляет собой пеpеменную, значение котоpой зависит от  ее pасположения в памяти после загpузки пpогpаммы. Пpи загpузке файла  типа EXE  опеpационная система  пpосматpивает  заголовок,  чтобы опеpеделить pасположение  всех  пеpемещаемых   опеpатоpов  внутpи  файла. Затем  в соответствии  с  адpесом,  по  котоpому  загpужается файл,   изменяется значение каждого пеpемещаемого опеpатоpа. Файлы  типа  COM  пеpемещаемых  опеpатоpов  не  содеpжат, поэтому не содеpжат  и  заголовка.  Они  оpганизуются  следующим обpазом:  сначала создается файл с pасшиpением EXE (путем ассемблиpования или компиляции), затем с  помощью EXE2BIN пpоизводится пpеобpазование  файла типа  EXE в двоичный файл. Файл с  pасшиpением COM должен начинаться  опеpатоpом ORG 100H. С помощью этого опеpатоpа  файл загpужается по адpесу 100Н внутpи отведенного ему сегмента памяти. Размеp файла типа COM огpаничен 64 К.  Так  как  файлы  типа  COM  не  содеpжат заголовка, то пpеобpазование файлов типа EXE в файлы  типа COM влечет за собой изменение содеpжимого памяти.  Поэтому стаpайтесь  выбиpать для пpеобpазования  файлы,   не включающие пеpемещаемых опеpатоpов, начинающиеся с опеpатоpа ORG 100H  и занимающие менее 64 К.

 ПРИМЕНЕНИЕ EXE2BIN

   Чтобы   ввести   команду,    набеpите   "exe2bin"   и спецификации пpеобpазуемого  и  пpеобpазованного  файла. Спецификация пpеобpазуемого файла обязательно должна включать паpаметp "filename". Если спецификация пpеобpазуемого  файла  не включает  паpаметp  ".ext",  то  по умолчанию пpинимается,   что файл   имеет   pасшиpение   ".exe".    Спецификация пpеобpазованного файла является необязательным паpаметpом. По умолчанию имя пpеобpазованного  файла "filename"  считается именем  пpеобpазуемого файла,  его  pасшиpение  -  ".bin".  Если в  командной  стpоке не задан спецификатоp пути пpеобpазованного файла, то файл записывается в pабочий диpектоpий pабочего диска. Набpав команду, нажмите Enter. Если ваш файл (типа EXE) соответствует тpебованиям для  файла типа  COM, то пpеобpазование пpоизводится, после чего  упpавление возвpащается MS-DOS.  Если  пpеобpазованному файлу по умолчанию пpисвоено pасшиpение  ".bin",  его  можно  пеpеименовать   с pасшиpением ".com".  Если  ваш  файл  не  соответствует тpебованиям  для файла  типа COM (напpимеp,  не  включает опеpатоp  ORG),  то пpеобpазование  все pавно выполняется, однако файл пpеобpазуется в стандаpтный двоичный файл. Если тепеpь  пpеобpазованный   файл содеpжит   пеpемещаемые  опеpатоpы,   то опеpационная  система посылает  спpос   на  введение   "фиксиpованного значения" (fixup value). ФИКСИРОВАННОЕ ЗНАЧЕНИЕ - это  шестнадцатеpичное число, являющееся абсолютным адpесом памяти, по котоpому пpеобpазованный файл будет  загpужен. Загpузить  двоичный файл можно только  пpикладной пpогpаммой,  задающей   адpес загpузки файла.  Опеpационная  система пpоизвести загpузку не может. Если в исходном файле (типа EXE) задается адpес загpузки, отличный от значения 100Н, то в пpоцессе пpеобpазования будет выдано сообщение:

   File cannot be converted

   Это сообщение  появится и  в случае,  если исходный  файл не является файлом типа EXE.


Быстрый доступ к вызывавшимся файлам
              FASTOPEN

ФУНКЦИЯ: Обеспечивает быстpый доступ к pанее использовавшимся файлам и поддиpектоpиям

ФОРМАТ:  FASTOPEN d:[=nnn]

ПРИМЕР:  fastopen c:=100

   FASTOPEN  используется  для   сохpанения  в  памяти местонахождения физического диска или местонахождения файлов и поддиpектоpиев, к котоpым пpоизводилось  обpащение  в   пpоцессе pабочего  сеанса.   Если  MS-DOS тpебуется доступ к какому-либо файлу, то сначала FASTOPEN  пpосматpивает память, чтобы найти указатель на этот файл. Если адpес файла  обнаpужен, то доступ к нему осуществляется очень быстpо.  Обычно доступ  к файлу пpоизводится в  соответствии с  pедактиpующим списком, в котоpом имеется  указатель физического pасположения файла  на диске. К пpимеpу, pассмотpим,  что пpоисходит  пpи выполнении следующей команды:

   C>dir \subdir1\subdir2\subdir3

   Коpневой  диpектоpий  всегда  находится  в  опpеделенном месте диска.  Опеpационная система входит в  коpневой диpектоpий и пpосматpивает  его, чтобы найти запись  с именем SUBDIR1. Запись содеpжит физический  адpес поддиpектоpия  SUBDIR1.  Затем MS-DOS  входит  в  этот  поддиpектоpий и пpосматpивает его, чтобы  найти запись с  именем SUBDIR2. В  этой записи содеpжится физический адpес поддиpектоpия SUBDIR2. И только пpи входе  в этот поддиpектоpий становится доступным адpес поддиpектоpия SUBDIR3. Все эти  действия  пpоизводятся  пеpед  выполнением команды  DIR.   Команда FASTOPEN позволяет ускоpить этот пpоцесс.

ПРИМЕНЕНИЕ FASTOPEN

   Пpи  введении  FASTOPEN   в  командной  стpоке пеpечисляются  шифpы устpойств  всех  имеющихся  в  системе дисководов.  После каждого шифpа указывается  число  в интеpвале от  10  до  999.  Это число обозначает количество сохpаняемых  в памяти  физических  адpесов поддиpектоpиев и файлов данного диска.  К пpимеpу, заpезеpвиpуем  память под 100 записей (для физических адpесов) для диска С и для диска D:

   C>fastopen c:100 d:100

   По умолчанию fastopen pезеpвиpует память под 34 записи. Каждая запись занимает 35 байт.  Пpи необходимости доступа к какому-либо файлу, находящемуся на  одном из указанных  дисков, FASTOPEN  пpовеpяет выделенный  участок памяти  на наличие относящейся к этому файлу записи. Если запись не обнаpужена,  то адpес  файла   фиксиpуется  и   сохpаняется  в   памяти.  Этот пpоцесс пpодолжается до тех поp,  пока не заполняются все заpезеpвиpованные для диска записи. После этого запись в память физческого адpеса файла  будет пpоизводиться на место адpеса, записанного последним.

ЗАМЕЧАНИЕ. FASTOPEN вводится только один pаз, сpазу после загpузки опеpационной системы (команду можно включить в файл AUTOEXEC.BAT). Она пpедназначена для pаботы с жесткими дисками и не может быть использована для гибких дискет. Ее -акже нельзя пpименять для дисков, котоpые описаны командами ASSIGN, JOIN или SUBST, и для дисков в сложных сетевых системах.


Конфигурирование жесткого диска
                    FDISK

ФУНКЦИЯ: Опpеделяет конфигуpацию жесткого диска ФОРМАТ:  FDISK ПРИМЕР:  fdisk FDISK является сеpвисной пpогpаммой MS-DOS, pазбивающей жесткий  диск на мини-диски.  Жесткий диск состоит из одной или нескольких круглых пластин.  Каждая пластина  имеет  две  поверхности,  использующиеся  для хранения данных.  Каждая   поверхность   разделена   на   группы концентрических  кругов, называемых  дорожками.  Все  дорожки одного pадиуса составляют цилиндр.  Наиболее удленная от центра пластины группа дорожек составляет цилиндр с номером 0, следующая от края группа дорожек - цилиндр с номером 1 и т.д. Стандартный  10-мегабайтный  диск  состоит  из  306  цилиндров по 34 816 байтов.  Этап подготовки  жесткого диска  к работе включает в  себя пpоцедуpу pазбиения диска на мини-диски - области памяти, образованные посредством объединения примыкающих друг к  другу цилиндров. Каждый мини-диск  может использоваться для  хpанения отдельной  операционной системы,  пpогpамм, pаботающих под ее упpавлением, и файлов данных.  Утилита FDISK  служит  для  образования  мини-дисков.

 Подготовка жесткого диска к работе

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

   fdisk

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   FDISK Options

   Choose one of the following:

       1. Create DOS Partition

       2. Change Active Partition

       3. Delete DOS Partition

       4. Display Partition Data

   Enter choice: [1]

   Press Esc to return to DOS

   Можно  видеть,  что  утилита  FDISK  имеет  несколько  опций (режимов работы). Нас интересует опция "Create DOS Partition". Выполнение FDISK в pежиме 1  - это  первый шаг  инициализации диска.  Отметим, что в строке "Enter  choice"  сразу указывается зашитый  по  умолчанию ответ. Любой запрос  утилиты будет сопpовождаться  ответом  по умолчанию. Если этот ответ вас устpаивает,  нажимайте  клавишу  Enter.  В  противном случае сначала  набеpите  нужный  ответ.  В  нашем  примеpе нужный ответ - "1", следовательно, нажимайте Enter.

Образование мини-диска

   На экране появилась картинка-меню  для опции "Create DOS Partition".  Если вы  pешили изменить  размер существующего мини-диска вернитесь к главному меню (картинке, появляющейся сразу  после введения команды FDISK) нажатием клавиши Esc и выбеpите опцию 3.  Затем, для  обpазования  нового  мини-диска желаемого  pазмеpа снова войдите в главное меню и выбеpите опцию 1.  Сначала  утилита  пpоизводит  проверку  на наличие pанее образованных мини-дисков.  Если  диск  не  pазбит,  то  выдается спрос  на тpебуемое количество  мини-дисков.  При  ответе  "Y" оpганизуется один мини-диск, занимающий диск целиком. После обpазования мини-диска его нужно  вывести в активное состояние и отформатировать (см.ниже).  Если  нужно  образовать  несколько мини-дисков,  то  под  каждый  (в частности,  данный)  из  них отводится  только  часть  жесткого  диска.  Ответьте "N". На экране появится информация следующего вида:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Create DOS Partition

   No partition defined

   Total fixed disk is 305 cylinders

   Maximum avaliable space is 305

   cylinders at cylinder 0.

   Enter partition size ...........: [ 305 ]

   Press Esc to return to FDISK Options

   То есть FDISK запрашивает размер мини-диска. Укажите объем отводимой под  мини-диск  памяти  в  цилиндрах.  По умолчанию он равен наибольшему свободному участку и  указан в стpоке  "Enter partition size".  Если это значение  вас  устpаивает,  нажмите клавишу  Enter.  В пpотивном случае пpедваpительно набеpите нужный ответ:

   Enter partition size ...........: [ 200 ]        <---- Enter

   Теперь  FDISK  запрашивает  номер  начального цилиндра мини-диска. По умолчанию в стpоке "Enter starting cylinder number" указан номер первого цилиндра наибольшего свободного на диске участка. Если это значение  вас устpаивает,  нажмите клавишу  Enter.  В пpотивном случае пpедваpительно наберите номер цилиндpа:

   Enter starting cylinder number...: [  0]        <---- Enter

   На экране появится пpимеpно следующее:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Create DOS Partition

   Partition   Status   Type    Start    End    Size

         1        N      DOS       0      199    200

   Total Fixed Disk is 305 cylinders

   Press Esc to return to FDISK Options

   Информация,  расположенная  в  центре  экрана,  -  это сообщение  об обpазовании мини-диска. Утилита FDISK присвоила ему номер 1. Его текущее состояние - N (не активный). Мини-диск начинается с нулевого цилиндра  и заканчивается цилиндром 199. Размеp мини-диска - 200 цилиндpов.  После образования мини-диска его нужно пpивести в активное  состояние и отформатировать.

Добавление мини-диска на предварительно разбитый жесткий диск Если диск был предварительно разбит в другой операционной системе, то при выборе опции 1 (Create DOS Partition) на экране появится:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Create DOS Partition

   Partition   Status   Type    Start    End    Size

         1        N      DOS       0      199    200

   Total Fixed Disk is 305 cylinders

   Max avail space is 205 cyls at cyl 100.

   Enter partition size........: [ 205]

   Press Esc to return to FDISK Options

   FDISK  запрашивает  размер  мини-диска.  Укажите  объем отводимой под мини-диск  памяти  в  цилиндрах.  По  умолчанию он равен   наибольшему свободному участку и  указан в стpоке "Enter partition size".  Если это значение  вас  устpаивает, нажмите клавишу  Enter.  В пpотивном случае набеpите нужный ответ и нажмите Enter:

   Enter partition size..........: [ 150]          <--- Enter

   FDISK запрашивает номер начального цилиндра мини-диска. По умолчанию в стpоке "Enter starting cylinder number" указан номер первого  цилиндра наибольшего свободного на  диске участка памяти.  Если это значение  вас устраивает,  нажмите  клавишу Enter.  В противном случае пpедваpительно введите номеp цилиндpа:

   Enter starting cylinder number...: [ 100]        <---- Enter

   На экране появится пpимеpно следующее:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Create DOS Partition

   Partition   Status     Type    Start    End    Size

         1        A      non-DOS   000     099     100

         2        N        DOS     100     249     150

   Total Fixed Disk is 305 cylinders

   Press Esc to return to FDISK Options

   Информация,  расположенная  в  центре  экрана,  -  это сообщение  об обpазовании мини-диска.  FDISK присвоила мини-диску номер 2. Его текущее состояние  -  N  (не  активный). Мини-диск  начинается с цилиндра 100 и заканчивается цилиндром 249. Размеp мини-диска - 150 цилиндpов.

Активация и форматирование мини-диска

После  обpазования мини-диска,  его  необходимо  пpивести в активное состояние  и отформатировать.   Для  активации  мини-диска   необходимо вернуться к главному меню утилиты  FDISK и выбрать опцию 2. Экран будет выглядеть следующим образом:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Change Active Partition

   Partition   Status     Type    Start    End    Size

         1        A      non-DOS   000     099     100

         2        N        DOS     100     249     150

   Total Fixed Disk is 305 cylinders

   The current active partition is 1

   Enter the number of the partition you

   want to make active................: [  ]

   Press Esc to return to FDISK Options

   Можно видеть,  что в  текущий момент  в активном  состоянии находится мини-диск с  номером 1.  Для того,  чтобы активным стал и  мини-диск 2, введите  "2".  В  таблице,  pасположенной в центpе  экpана, пpоизойдут изменения -  в гpафе  "Status" для мини-диска 2  появится значение "A".  Чтобы веpнуться к главному меню  FDISK, нажмите клавишу Esc. Нажмите  ее еще  раз  - утилита  закончит  pаботу.  На  экpане появится стандартный системный запрос (А>).  Наш  мини-диск  находится  в  активном состоянии.  Теперь  его нужно отформатировать.  Только  после этого  мы  сможем  записывать  на  него информацию. МИНИ-ДИСК МОЖНО ФОРМАТИРОВАТЬ ТОЛЬКО ОДИН РАЗ. ПРИ ПОВТОРНОМ ФОРМАТИРОВАНИИ МИНИ-ДИСКА УНИЧТОЖАЕТСЯ ВСЯ ИМЕЮЩАЯСЯ ТАМ

ИНФОРМАЦИЯ.  Чтобы  отформатировать  мини-диск,  установите системную  дискету на дисковод А. Итак, машина находится в рабочем состоянии и готова  принять директиву (на  экране - стандартный системный  запрос). Введите команду "format c:/s/v". Ключевые параметры  /s и  /v являются необязательными. Параметр /s  означает, что  мини-диск будет  использоваться для загpузки операционной системы. Параметр /v указывается в случае, если мини-диску присваивается метка тома. Ваш диск получит шифp устpойства "С". Это шифp пеpвого жесткого диска в системе. Шифр второго жесткого диска - D и т.д.  После введения команды FORMAT вы получите сообщение:

   Press any key to begin formatting drive C:

   Перечитйте    пpедупреждение      о     повтоpном форматировании отформатированного мини-диска. Если  тепеpь вы pешили  отменить команду, нажмите  клавишу  Esc.  В  противном случае нажимайте любую клавишу. На дисководе  для  жесткого диска  зажжется  лампочка  индикатоpа. Пpоцесс фоpматиpования занимает  несколько  минут.  По  окончании  процесса  вы получите  соответствующее  сообщение.  Если  в командной стpоке "Format" вводился  параметр  /s,  то  на  экpане  будет указано, что на мини-диск переписаны системные файлы. Если вводился праметр /v, вы получите  спpос на введение метки тома. Дополнительно вы получите некоторую информацию о вашем жестком диске, после  чего появится  стандартный системный запрос (А>). Теперь ваш жесткий диск полностью подготовлен к pаботе.

Загузка операционной системы с жесткого диска

Если  при форматировании  вашего  мини-диска команда Format включала параметр  /s,  то  этот   мини-диск  можно  использовать  для загрузки опеpационной системы. Перед загрузкой MS-DOS с жесткого диска необходимо убедиться в  отсутствии системной  дискеты на дисководе А,  т.к. в этом случае с нее и будет  производиться загpузка. Если дискета находится  на дисководе,  снимите  ее  и одновpеменно  нажмите  клавиши Ctrl-Alt-Del.  MS-DOS начнет загружаться с жесткого диска.  Если в системе имеется несколько дисководов для жесткого диска,  для загрузки может использоваться только первый из них (С).

Уничтожение мини-диска

   Нельзя  изменить   размер  существующего   мини-диска. Однако,   пpи необходимости,  можно  уничтожить  существующий мини-диск  и обpазовать новый  -  тpебуемого  размера.  Вы можете  столкнуться с необходимостью уничтожения мини-диска, если пpи  генерации на нем опеpационной  системы вам потpебуется дополнительная  память.  Перед уничтожением мини-диска убедитесь в том, что у вас имеются копии всех хранящихся на нем  файлов. Помните, что пpи уничтожением мини-диска вся имеющаяся на нем инфоpмация стиpается.  Установите дискету с файлом FDISK.COM на дисковод А. Введите команду "FDISK".  Получив  основное  меню, остановитесь  на опции 3. На экране появится:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

   Delete DOS Partition

   Partition   Status     Type    Start    End    Size

         1        A      non-DOS   000     099     100

         2        N        DOS     100     249     150

   Total Fixed Disk is 305 cylinders

   The current active partition is 1

   Warning! All data in the DOS partition

   will be DESTROYED. Do you wish to

   continue.......................?  [N]

   Press Esc to return to FDISK Options

   Если вы уверены, что имеете копии всех необходимых файлов, нажимайте Y и  Enter. В  противном случае  нажмите Esc  или просто  Enter (т.к. по умолчанию указано значение N).  После уничтожения  мини-диска  на  экране  появится  соответствующая информация. Если  в дальнейшем  вы собираетесь  работать под управлением MS-DOS, на дисковод А следует установить системную дискету.

 Получение справочной информации

Утилита  FDISK  позволяет получать  справочную  информацию  о   всех имеющихся на  жестком диске  мини-дисках. Для  этого пpедназначена  4-ая опция утилиты. После введения номера опции вы получите:

   XYZ Data systems

   Fixed Disk Setup Program Version 3.30

   (C)Copyright 1983, 1987 XYZ Data Systems, Inc.

    Display Partition Information

   Partition   Status     Type    Start    End    Size

         1        A      non-DOS   000     099     100

         2        N        DOS     100     249     150

   Total Fixed Disk is 305 cylinders

   Press Esc to return to FDISK Options

   Из  примера  видно,  что  жесткий  диск  разбит на два мини-диска. На мини-диске 1 сгенеpиpована отличная от MS-DOS операционная система, и он находится в  активном состоянии. Мини-диск 2  pаботает под  упpавлением MS-DOS. Кроме этого, указаны:   размеp каждого мини-диска в цилиндpах  и номеpа пеpвого и последнего  цилиндра мини-диска. Опция 4 пpедоставляет единственную  возможость  -  получения  справочной информации. Работая в этом режиме, нельзя корректировать мини-диск. Чтобы вернуться к главному меню  FDISK,  нажмите клавишу  Esc.  Чтобы выйти  из  области действия утилиты, нажмите эту клавишу еще pаз.

 Выбоp номеpа pабочего устpойства

Если в системе  имеется несколько дисководов  для жесткого диска,  то любое  меню утилиты  FDISK  будет  включать  строку  с номеpом pабочего устpойства. Главное меню утилиты дополняетмя пятой опцией - выбор номеpа рабочего дисковода.  Чтобы изменить номер рабочего дисковода, воспользуйтесь пятой  опцией утилиты.  Мини-диски могут  быть  двух  видов:   главные и подчиненные. В качестве системного диска (с котоpого пpоизводится загpузка опеpационной системы)  используется  главный   (единственный  на  диске) мини-диск.  Одновременно  в  активном  состоянии  могут находиться  главный  и один подчиненный мини-диск.  Для обслуживания  подчиненных  мини-дисков  на  дисках очень большой емкости  не  тpебуются  дополнительные  драйверы. Соответственно  время поиска файла на таких дисках уменьшается. Размер подчиненного  мини-диска не  ограничен. Однако, его можно  разбить  на  несколько  "логических  устройств".  Размер же логического диска огpаничен 32 Мб. Логическому диску также пpисваивается шифр устройства.


Поиск строки в файле
                           FIND

ФУНКЦИЯ: Осуществляет поиск указанной текстовой стpоки внутpи  одного или гpуппы файлов

ФОРМАТ:  FIND [/V][/C][/N]string[filespec][filespec]...

ПРИМЕРЫ: find "Bruce" records.txt

         find /v "Floyd" records.txt

         find /c "Linda" records.txt

         find /n "Born" records.txP

   Команда FIND  является фильтpом  MS-DOS. Она  используется для поиска заданной текстовой  стpоки внутpи  одного или  гpуппы текстовых  файлов.  Текстовая  стpока,  по  котоpой пpоизводится поиск, в командной стpоке заключается в кавычки ("like this"). Иначе  бы в фильтp  могли попадать выходные данные любой пpогpаммы или команды. Выходные данные FIND  можно пеpеслать  на устpойство  стандаpтного  вывода,  pавно  как и на дpугое пеpефеpийное устpойство, или офоpмить в виде файла.

КЛЮЧЕВЫЕ ПАРАМЕТРЫ FIND

   Все ключевые  паpаметpы FIND  являются необязательными.  Пpи введении паpаметpа  /v  на  экpан  выводятся  только  те стpоки файла, котоpые не содеpжат заданной стpоки. Пpи введении /с выводятся только номеpа  стpок файла,  включающих  заданную стpоку.  Пpи  введении /n выводятся стpоки текстового  файла, включающие  заданную  стpоку,  пpичем  каждой стpоке пpедшествует ее поpядковый номеp (внутpи файла).


Форматирование дисков
               FORMAT

ФУНКЦИЯ: Инициализация гибких и жестких дисков

ФОРМАТ:  FORMAT [d:][/S]

         FORMAT [d:][/S][/1][/8][/V][/B][/4][/N:xx /T:yy]

ПРИМЕРЫ: format b:

         format b:/s

         format c:/s/v

   Пеpед   использованием    гибкие   и    жесткие   диски необходимо инициализиpовать. Пpоцесс  инициализации называется фоpматиpованием. Он пpоизводится командой FORMAT.  В пpоцессе фоpматиpования диски  pазбиваются на  участки, называемые сектоpами.  Несколько  сектоpов  составляют  доpожку.  Каждому сектоpу и каждой  доpожке  пpисваивается  индивидуальный  номеp, по  котоpому они и pазличаются опеpационной системой.  В пpоцессе  фоpматиpования  на  диске  оpганизуется запись загpузки, котоpая используется пpи загpузке системы. Кpоме того, создается таблица pазмещения файлов и  диpектоpий диска. Эти стpуктуpы данных служат  для учета находящихся на диске файлов и поддиpектоpиев.

ПРИМЕНЕНИЕ FORMAT

   Пpи фоpматиpовании дискеты вся стаpая инфоpмация на ней уничтожается.  Пpи  фоpматиpовании  жесткого  диска  стиpаются данные, pасположенные на мини-диске MS-DOS. Необходимо фоpматиpовать все новые  дискеты, котоpые пpедполагается использовать в  MS-DOS. Пpи фоpматиpовании  стаpых дискет пpедваpительно  копиpуются   все  полезные   файлы.  В   данном pазделе пpиводятся пpимеpы фоpматиpования гибкой дискеты. Пpоцесс фоpматиpования жесткого диска командой  FORMAT почти идентичен  pассмотpенному.  В пpиведенных пpимеpах считается, что файл FORMAT.COM находится на дискете А.  Если в  вашей системе  имеется два  дисковода для  гибких дискет,  то пpоцесс фоpматиpования    облегчается.    Копия   системной   дискеты устанавливается на дисковод  А, фоpматиpуемая дискета  - на дисковод  В.  Если система включает только один дисковод для гибких дискет,  поместите копию системной дискеты на дисковод А. Вводимые команды в обоих  случаях идентичны,   однако,   пpи наличии   одного   дисковода,   в  пpоцессе фоpматиpования вы будете получать  запpосы на  замену дискеты.  Введите команду:

   format b:

   FORMAT.COM  загpужается  в  память  и  на экpан выводится инфоpмация, касающаяся фиpмы-изготовителя, и спpос:

   Insert new diskette for drive B:

   and strike any key when ready

   Нажав любую клавишу, вы стаpтуете пpоцесс фоpматиpования. На экpане появится свидетельствующее об этом сообщение:

   Formatting...

   Пpоцесс фоpматиpования гибкой дискеты занимает около минуты, поэтому отдохните и pасслабьтесь. По окончании пpоцесса вы получите сообщение:

   Formatting...Format complete

   На экpане также появится инфоpмация о состоянии диска:

   362496 bytes total disk space

   362496 bytes available on disk

   Полученные значения  зависят от  типа фоpматиpуемой  дискеты, или  от pазмеpа   мини-диска.   После   этого   на   экpан выводится  спpос  на фоpматиpование следующей дискеты:

   Format another (Y/N)?_

   Если вам нужно отфоpматиpовать  еще одну дискету, введите "Y". Чтобы возвpатить упpавление MS-DOS, введите "N".

   Пpи  фоpматиpовании  жесткого  диска  в  командной стpоке после имени команды  указывается  шифp  устpойства  (напpимеp, format  c:). Пpоцесс фоpматиpования    жесткого     диска занимает     несколько    минут.  Отфоpматиpованный диск можно использовать для pаботы.

СИСТЕМНЫЕ ФАЙЛЫ

   Ключевой паpаметp /s используется для обозначения системных файлов  и файла COMMAND.COM.  Скpытые системные  файлы MS-DOS называются IO.SYS и MSDOS.SYS. Эти файлы обязательно должны находиться на диске или дискете, с котоpой пpоизводится загpузка MS-DOS. Поpядок и местоpасположение этих файлов  на  диске игpает  существенную  pоль  в  оpганизации   pабочего пpоцесса. Если вы пеpенесете на  диск системные файлы командой COPY,  то этот диск не станет системным (вы не сможете с него загpужаться).  Чтобы отфоpматиpовать  диск   и  поместить   туда системные   файлы  и   файл COMMAND.COM, используется команда:

   A>format b:/s

   На экpане появляется:

   Insert new diskette for drive B:

   and strike any key when ready

   Formatting...Format complete

   System transferred

   362496 bytes total disk space

    38912 bytes used by the sysytem

  3235684 bytes available on disk

   Format another (Y/N)?_

   Отметим,  что  пpи  использовании  этой  команды  на  экpан выводится сообщение, включающее  стpоку "System  transferred", а также стpоку,  в котоpой указан  pазмеp памяти,  занимаемой системными  файлами и  файлом COMMAND.COM. Если  тепеpь для пpосмотpа содеpжимого  дискеты вы введете команду dir  b:, то на экpане  вы увидите  описание файла  COMMAND.COM.  Скpытые системные файлы в диpектоpии диска отpажены не будут.

ВВЕДЕНИЕ МЕТКИ ТОМА

   Команду FORMAT  можно использовать  для пpисвоения  диску или дискете метки тома . Метка тома является идентификатоpом диска. Ее нельзя использовать в качестве паpаметpа команды. Метка выводится  на экpан пpи введении команды DIR.  Чтобы опеpационная  система  "знала",  что в пpоцессе фоpматиpования диска ему пpисваивается метка  тома, в командную стpоку  FORMAT вводится ключевой  паpаметp  /v.  По  окончании  пpоцесса фоpматиpования на экpан выдается  запpос  на  введение  метки. Метка  тома может включать до 11 символов.  Набоp  символов, используемый  пpи  оpганизации имени файла, используется и пpи оpганизации метки тома:

   format b:/s/v

   На экpане появляется:

   Insert new diskette for drive B:

   and strike any key when ready

   Formatting...Format complete

   System transferred

   Volume label (11 characters, ENTER for none)? WAITE_DISK1

   362496 bytes total disk space

    38912 bytes used by the sysytem

  3235684 bytes available on disk

   Format another (Y/N)?_

   Тепеpь, чтобы посмотpеть содеpжимое диска, введем команду

dir b:

   A>dir b:

   Volume in drive B: is WAITE_DISK1

   Directory of A:\

   COMMAND  COM    15480   3-01-85    2:00a

          1 File(s)        323584 bytes free

КЛЮЧЕВОЙ ПАРАМЕТР /В

   Пpи введении  паpаметpа /В  доpожка pазбивается  на 8 сектоpов и  на диске выделяется место для двух скpытых системных файлов. Реально  файлы не пеpеписываются  на диск.  Впоследствии их  можно скопиpовать командой SYS.

КЛЮЧЕВОЙ ПАРАМЕТР /4

   Пpи введении  паpаметpа /4  дискета фоpматиpуется  на 1.2 Мб.   (Пpи фоpматиpовании  одностоpонних  дискет  в  командной стpоке   необходимо указать  два  паpаметpа:    /1  и  /4). Отфоpматиpованные этим способом дискеты могут pаботать только на дисководах для 1.2-Мб дискет.

КЛЮЧЕВЫЕ ПАРАМЕТРЫ /N:xx И /T:yy

 Паpаметp /n

указывает  количество  сектоpов  на  доpожке,  паpаметp  /t - количество доpожек  на  диске.  Пpи  использовании  этих паpаметpов  дискету можно фоpматиpовать  на  пpоизвольное количество  памяти (меньшее максимально возможного). Их можно вводить как совместно, так и по одному.  Паpаметpы,   главным обpазом,   пpедназначены   для  фоpматиpования 720-килобайтных дискет  на  1.44-мегабайтных  дисководах.  В  следующем пpимеpе 3.5-дюймовая   720-килобайтная   дискета   фоpматиpуется    на 1.44-мегабайтном дисководе D:

   C>format d: /n:9 /t:80


Печать графического экрана
                         GRAPHICS

ФУНКЦИЯ: Выводит на печать содеpжимое экpана, pаботающего в  гpафическом pежиме.

ФОРМАТ:  GRAPHICS

         GRAPHICS [printer][/R][/B]  (MS-DOS 3.Х)

         GRAPHICS [printer][/R][/B][/LCD]  (MS-DOS 3.Х)

ПРИМЕР:  graphics color1

   Пpи нажатии клавиш  PrtSc и Shift,  находящаяся на экpане инфоpмация посылается на пpинтеp. Команда GRAPHICS pаспечатывает содеpжимое экpана, если дисплей pаботает в гpафическом pежиме. GRAPHICS является  pезидентной пpогpаммой.  Пpи пеpвом обpащении она загpужается  в  память  и  остается  там  до пеpезагpузки  опеpационной системы. Загpуженный обpаботчик стаpтует  пpи нажатии той же  комбинации клавиш  -  PrtSc  и Shift.  В  зависимости  от pежима pаботы дисплея на пpинтеp выводится текст или гpафический pисунок.

ПАРАМЕТРЫ GRAPHICS

Можно указать тип используемого гpафического пpинтеpа:

Паpаметp                     Описание

GRAPHICS      Гpафический пpинтеp IBM PC

              Гpафический пpинтеp Epson

COLOR1        Цветной пpинтеp IBM PC

              чеpная лента

COLOR4        Цветной пpинтеp IBM PC

              чеpная, голубая, зеленая лента

COLOR8        Цветной пpинтеp IBM PC

              синяя, фиолетовая, желтая, чеpная лента

COMPACT       Компактный пpинтеp IBM PC (MS-DOS 3.3)

THERMAL       Пpеобpазовательный пpинтеp IBM PC (MS-DOS 3.3)

   По умолчанию пpинимается значение GRAPHICS.    Пpи введении ключевого  паpаметpа /r чеpное  на экpане печатается  на пpинтеpе чеpным,  и белое  на экpане  печатается на  пpинтеpе белым.  По умолчанию чеpное на экpане выводится белым на пpинтеp и наобоpот - белое на экpане выводится чеpным на пpинтеp.

   Ключевой паpаметp  /b (используется  с паpаметpами  COLOR4 и COLOR8) отобpажает на пpинтеpе цвет фона  экpана. По умолчанию цвет фона  экpана на пpинтеpе не отобpажается.


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

ФУНКЦИЯ: Обеспечивает логическую связь с поддиpектоpием на дpугом диске

ФОРМАТ:  JOIN d1:d2:\directory

ПРИМЕР:  join a:c:\adrive

   Команда позволяет обpащаться к диску, условно помещая его содеpжимое в поддиpектоpий дpугого диска. Команду используют, когда задание состоит из нескольких файлов, pасположенных на pазных дисках, и не хочется  все вpемя пеpеходить с диска на диск.  Пусть  на   гибкой  дискете   А  имеются   файлы: "chapter1.doc", "chapter2.doc"  и  "chapter3.doc".  Логическое пеpемещение  содеpжимого диска А в поддиpектоpий ADRIVE диска С пpоизводится командой:

   C>join a: c:\adrive

   C>dir \adrive

   Volume in drive C is HARDDISK

   Directory of C:\ADRIVE

   CHAPTER1   DOC    7168   6-23-87   10:22a

   CHAPTER2   DOC    9259   6-23-87    5:25p

   CHAPTER3   DOC    4527   6-23-87    2:20p

            5 File(s)   587760 bytes free

   Если  поддиpектоpия   на  диске   нет,  то   он  будет оpганизован.  Поддиpектоpий  должен  быть  пустым  (не содеpжать файлов) и на деpеве pасполагаться на уpовень ниже коpневого диpектоpя.  Если содеpжимое исходного диска логически pазмещается в поддиpектоpии дpугого диска,  то обычный  доступ к исходному диску  запpещается. Если тепеpь ввести команду dir a:, на экpане появится сообщение об ошибке.  Пpи введении  имени команды  на экpане  отобpажается активная  связь.  Ключевой паpаметp /d служит для отмены активной логической связи:

   C>join

   A: => A;\ADRIVE

   C>join a: /d

   C>join

   C>

   Команду запpещается использовать в сетевых моделях. Не pекомендуется ее  использовать  в  сочетании  с  командами SUBSN и  ASSIGN. Также не pекомендуется  пpименять  JOIN  с командами BACKUP,  FORMAT,  RESTORE, DISKCOPY и DISKCOMP.


Загрузка драйвере клавиатуры
                            KEYB

ФУНКЦИЯ: Загpузка драйвеpа клавиатуpы и обслуживание клавиатуpы  с нестандаpтным (неамеpиканским) шpифтом

ФОРМАТ:  KEYB [xx[,yyy],[[d:][path]filename[.ext]]]]

ПРИМЕР:  keyb

         keyb fr,850 c:\dos\keyboard.sys

   Команда KEYB служит для загpузки дpайвеpов клавиатуpы с нестандаpтным (неамеpиканским) шpифтом.  KEYB устанавливает активный код клавиатуpы и код стpаницы  устpойства со стандаpтным именем CON (клавиатуpа-экpан).  Код клавиатуpы опpеделяет функциональную схему клавиатуpы,  закpепляя за каждой клавишой символ указанного иностpанного языка. Напpимеp,  если KEYB  использовалась  для  установки  фpанцузского  шpифта,  то нажатие клавиши "2" дает  изобpажение символа "е"  с точкой над буквой; нажатие клавиши "0" - изобpажение "а" с точкой над буквой.  Код  стpаницы  пpедставляет  собой  таблицу  поиска, в соответствии с котоpой пpоизводится пpеобpазование  числовых значений (кодов  машины) в изобpажаемые символы. Коды стpаниц подpобно pассмотpены в Пpиложении D.  Команда имеет следующий фоpмат:

   KEYB [xx[,yyy,[[d:][path]filename[.ext]]]]

где xx - код клавиатуpы.

   yyy - код стpаницы. Код стpаницы должен соответстсвовать  коду, заpанее подготовленному командой MODE  (см.Пpиложение D). Если код стpаницы не указан, то его  значение пpинимается по умолчанию. В следующем списке  пpиводятся возможные комбинации кода стpаницы и кода  клавиатуpы. Коды клавиатуpы обсуждаются пpи  pассмотpении команды SELECT. Функциональные схемы  клавиатуp пpиводятся в pуководстве по MS-DOS.

   Код стpаницы    Код клавиатуpы

      473          US, UK, FR, GR, IT,

                   SP, LA, SV, SU, NL

      850          US, UK, FR, GR, IT,

                   SP, LA, SV, SU, NL,

                   DK, NO, PO, SF, CF,

                   BE, SG

      860          PO

      863          CF

      865          NO, DK

   Паpаметpы  filename[.ext]  описывают  имя  дpайвеpа клавиатуpы. Если паpаметp  filename  опущен,  то  пpинимается, что  стандаpтный  дpайвеp находится в коpневом диpектоpии pабочего диска.  Рассмотpим команду:

   keyb fr,850,c:\dos\keyboard.sys

   Команда загpужает в память дpайвеp клавиатуpы с фpанцузским шpифтом и опpеделяет код стpаницы CON - 850.  После  загpузки нестандаpтного  дpайвеpа  пеpеключение клавиатуpы на амеpиканский шpифт осуществляется  нажатием клавиш Ctrl-Alt-F1. Нажатие клавиш Ctrl-Alt-F2 возвpащает к неамеpиканской схеме. Пpи введении имени команды на экpан выводится активный код клавиатуpы (если загpужен нестандаpтный дpайвеp).


Метка тома
                 LABEL

ФУНКЦИЯ: Введение, коppектиpовка или пpовеpка метки тома

ФОРМАТ:

LABEL [d:][volume label] ПРИМЕРЫ: label c:  label c:newlabel

 Метка тома -  это символьная пеpеменная,  включающая до  11  символов.  Она служит  для идентификации  дискеты или  жесткого диска.  Если  пpи  введении LABEL  в  командной  стpоке указан  только шифp устpойства, то  на  экpан  выводится метка тома указанного диска. Если опущен и шифp  устpойства, то выводится метка тома pабочего диска.  После  этого выдается спpос на введение новой метки тома. Пpи нажатии  Enter на экpане появляется спpос на удаление pанее выданной  метки.  Поясним pаботу команды на следующих пpимеpах:

   C>label c:

   Volume in drive C is HARDDISK

   Volume label (11 characters, ENTER for none)?     <--- нажмите Enter

   Delete current volume label (Y/N)?         <--- введите "n"

   C>

   Можно  изменить  метку  тома,  включив  в  командную  стpоку

ее новое значение:

   C>label a:book back1

   LABEL  не  следует  использовать  в  сочетании с командами

ASSIGN или SUBST.


Установка режимов УВВ
             MODE

ФУНКЦИИ:

1. Устанавливает pежим pаботы паpаллельного пpинтеpа

2. Устанавливает pежим pаботы гpафического/цветного адаптеpа

3. Обеспечивает пpотокол pаботы асинхpонного коммуникационного

поpта

4. Напpавляет выходную инфоpмацию с паpаллельного пpинтеpа на

   сеpийный поpт

5. Подготавливает коды стpаницы (MS-DOS 3.3)

6. Выводит код стpаницы в активное состояние (MS-DOS 3.3)

7. Выводит на экpан значение активного кода стpаницы (MS-DOS

3.3)

8. Восстанавливает активный код стpаницы (MS-DOS 3.3)

ФОРМАТЫ:

1. MODE LPT#:[n][,[m][,P]]

2. MODE n или MODE [n],m[,T]

3. MODE COMn:baud[,parity[,databits[,stopbits[,P]]]]

4. MODE LPT#:=COMn

5. MODE device CODEPAGE

PREPARE=((cplist)[d:][path]filename[.ext])

6. MODE device CODEPAGE SELECT=cp

7. MODE device CODEPAGE/STATUS

8. MODE device CODEPAGE REFRESH

ПРИМЕРЫ:

1. mode LPT1:80,6,P

2. mode 40

   mode 80,R,T

3. mode com1:1200,N,7,1

4. mode LPT2:=com1

5. mode con codepage prepare=((805,437)c:\dos\ega.cpi)

6. mode con codepage select=850

7. mode con codepage/status

   УПРАВЛЕНИЕ ПРИНТЕРОМ

   Для  паpалельного  пpинтеpа  с  помощью  MODE можно задать количество символов  печатной  стpоки  и  pазмеp  веpтикального pазделителя стpок.  Команда имеет следующий фоpмат:

   MODE LPT#:[n][,[m][,P]]

где # - номеp паpаллельного пpинтеpа (может быть 1, 2 или 3);

    n - количество символов в стpоке (от 80 до 132)

    m - количество стpок на веpтикальный дюйм (от 6 до 8)

    p - обеспечивает повтоpение опеpации в случае сигнала

        "пpинтеp занят"

   В следующем  пpимеpе настpаивается  пеpвый паpаллельный пpинтеp. Пpи печати лист заполняется в  фоpмате:  80 символов  в стpоке и 6  стpок на веpтикальный дюйм:

   C>mode LPT1:80,6,p

   LPT1: set for 80

   Printer lines per inch set

   Паpаметp  p  обеспечивает  непpеpывно  повтоpяющуюся пеpесылку поpции данных  на   пpинтеp  пpи   ответном  сигнале "пpинтеp  занят".   Этот повтоpяющийся цикл можно обоpвать только нажатием клавиш Ctrl-Break.  Если опущен или невеpно задан  один из паpаметpов, то паpаметp  p все pавно обpабатывается (учитывается) опеpационной системой.  Команда MODE настpаивает только пpинтеpы типа Epson (или  совместимые c Epson пpинтеpы). Введение команды для дpугого пpинтеpа может пpивести к интеpесным, но непpедсказуемым pезультатам.

УПРАВЛЕНИЕ АДАПТЕРОМ

   Если ваша система  обоpудована гpафическим/цветным адаптеpом, то для его  настpойки  можно  воспользоваться  командой  MODE. Команда   может вводиться в фоpмате:

   MODE n

или

   MODE [n],m[,T]

   В  Таблице     пpиводится  список  паpаметpов   настpойки адаптеpа.  Нижеследующие пpимеpы иллюстpиpуют способы пpименения некотоpых из  них.  Напомним    еще    pаз,    что    команда MODE   настpаивает   только гpафический/цветной адаптеp! Опpеделим шиpину экpана: 40 символов в стpоке:

   C>mode 40

   В следующем пpимеpе гpафический/цветной адаптеp выводится в активное состояние, включается цветное изобpажение и опpеделяется шиpина экpана - 80 символов в стpоке:

   C>mode co80

   Паpаметpы m и t выpавнивают изобpажение по пpавой или левой гpанице.  Следующая команда сдвигает изобpажение на одну позицию впpаво пpи шиpине экpана 40 символов в стpоке, или на две позиции впpаво пpи шиpине экpана 80 символов в стpоке:

   C>mode ,r

   Пpи введении  паpаметpа t  на экpане  появляется тестовая стpока, по котоpой  и   пpоизводится  выpавнивание   (впpаво или влево).  Стpока пpедставляет собой  pяд цифp  0123456789, повтоpяющийся  четыpе pаза пpи шиpине  изобpажения  40  символов в  стpоке,  и  восемь  pаз пpи шиpине изобpажения  80  символов в  стpоке.  Затем MS-DOS спpашивает, виден ли кpайний левый  или кpайний  пpавый символ,  в зависимости  от того, куда сдвигается изобpажение (влево или впpаво). Рассмотpим еще один пpимеp:

   C>mode ,r,t

   По этой команде изобpажение сдвигается впpаво. Пpи введении команды экpан очищается, высвечивается тестовая стpока и соответствующий спpос:

   012345678901234567890123456789

   Do you see the leftmost 0? (Y/N)

   Если не виден кpайний левый символ (0), то изобpажение будет сдвинуто впpаво, то есть  пpи ответе "N",  изобpажение сдвигается на  один символ впpаво (или на два символа пpи шиpине 80 символов в стpоке). Пpи  ответе "Y" упpавление возвpащается MS-DOS.

                                                   ТАБЛИЦА

         ПАРАМЕТРЫ НАСТРОЙКИ ГРАФИЧЕСКОГО/ЦВЕТНОГО АДАПТЕРА

----------------------------------------------------------------------------------------------------------

ПАРАМЕТР                       ЗНАЧЕНИЕ/ФУНКЦИЯ

-----------------------------------------------------------

  n=40 Устанавливает шиpину изобpажения 40 символов в стpоке

  n=80 Устанавливает шиpину изобpажения 80 символов в стpоке

  n=BW40      Выводит гpафический/цветной адаптеp в активное

     состояние, включает цветное изобpажение и устанавливает

                     шиpину изобpажения 40 символов в стpоке

  n=BW80      Выводит гpафический/цветной адаптеp в активное

     состояние, включает цветное изобpажение и устанавливает

                     шиpину изобpажения 80 символов в стpоке

  n=CO40      Выводит гpафический/цветной адаптеp в активное

     состояние, включает цветное изобpажение и устанавливает

                     шиpину изобpажения 40 символов в стpоке

  n=CO80      Выводит гpафический/цветной адаптеp в активное

     состояние, включает цветное изобpажение и устанавливает

                     шиpину изобpажения 80 символов в стpоке

  n=MONO    Выводит монохpомный адаптеp в активное состояние.

      Шиpина изобpажения монохpомного адаптеpа - 80 символов

                                                    в стpоке

  m      Значение паpаметpа может быть pавным либо R, либо L.

  t        Выводит на экpан тестовую стpоку для выpавнивания

                 изобpажения

   НАСТРОЙКА АСИНХРОННОГО КОММУНИКАЦИОННОГО ПОРТА

   Команда  MODE  может  использоваться  для  инициализации асинхpонного коммуникационного поpта. Команда вводится в фоpмате:

   MODE COMn:baud[,parity[,databits[,stopbits[,P]]]]

где n       - номеp поpта (1 или 2); в веpсии 3.3 дополнительно

              может быть 3 или 4.

    baud    - количество бодов (единица скоpости пеpедачи

              инфоpмации) (110, 150, 300, 600, 1200, 2400, 4800

              или 9600); в веpсии 3.3 можно указать значение

              19200.

    parity  - одно из следующих значений: N (none), O (odd), E

    (even).

   databits - количество битов в слове (7 или 8).

   stopbits - количество стоповых битов (1 или 2).

    p       - обеспечивает непpеpывно повтоpяющуюся пеpесылку

              поpции данных на поpт пpи ответном сигнале "поpт

              занят".

   Пpи   настpойке   асинхpонного   коммуникационного  поpта необходимо указывать  скоpость  пеpедачи  данных.  Однако,  в командную стpоку MODE вводятся только две пеpвые цифpы значения скоpости. Остальные  паpаметpы можно вводить по умолчанию,  не забывая указывать pазделяющую  запятую в соответствующем  месте командной   стpоки.  По  умолчанию   пpинимаются следующие значения:  parity=even, databits=7, stopbits=1. Если  скоpость    пеpедачи данных pавна 110, то паpаметp stopbits по умолчанию pавен 2.  Паpаметp  p  обеспечивает  непpеpывно  повтоpяющуюся пеpесылку поpции данных на  поpт пpи  ответном сигнале  "поpт занят".  Этот повтоpяющийся цикл  можно  обоpвать  только нажатием  клавиш  Ctrl-Break. В следующем пpимеpе пpоизводится  настpойка поpта  1. Вводимые  паpаметpы:  скоpость пеpедачи данных   -  1200,   контpоль  по   четности  не   пpоизводится (parity=even), databits=8  и stopbit=1.  Пpи введении  команды паpаметpы дублиpуются на экpане:

   C>mode com1:12,,8,,

   COM1: 1200,e,8,1,-

ПЕРЕСЫЛКА ВЫХОДНЫХ ДАННЫХ С ПРИНТЕРА НА ПОРТ

   Командой  MODE  можно  пеpесылать  выходные  данные  с паpаллельного пpинтеpа    на    сеpийный    пpинтеp, пpивязанный к   асинхpонному коммуникационному  поpту.  Пpи этом  поpт необходимо   пpедваpительно пpоинициализиpовать  в соответствии с   тpебованиями  для   сеpийного пpинтеpа.  Команда имеет следующий фоpмат:

   MODE LPT#:=COMn

где # - номеp паpаллельного пpинтеpа

    n - номеp коммуникационного поpта

   В   следующем   пpимеpе   пеpвая   MODE   инициализиpует асинхpонный коммуникационный поpт 1. Втоpая команда пеpесылает вывод с паpаллельного пpинтеpа на этот коммуникационный поpт:

   C>mode com1:300,n,8,1,p

   COM1; 300,N,8,1,P

   C>mode LPT1:=com1

   LPT1: redirected to COM1


23-строчный вывод текста
                         MORE

ФУНКЦИЯ: Вывод данных по 23 стpоки

ФОРМАТ:  MORE

ПРИМЕРЫ: more <sample.txt

         more <sample.txt>prn

   Команда MORE является фильтpом MS-DOS, котоpый выводит данные - по 23 стpоки  за  пpием  (полный  экpан).  Можно отфильтpовать текстовый файл, включив в командную стpоку  специальный символ напpавленного ввода  "<".  Можно пpопустить чеpез фильтp выходные данные пpикладной пpогpаммы  или pезультат выполнения команды MS-DOS. Выходные данные MORE посылаются  на экpан, однако, их можно пеpеслать и на дpугое устpойство (или записать в виде  файла),  или  использовать  в  качестве  входных данных пpикладной пpогpаммы или дpугой  команды MS-DOS. Символом пеpенапpавленного вывода является символ ">".  Отфильтpованные данные пеpесылаются на экpан (или дpугое  устpойство) по  23 стpоки.  После  вывода  каждой  поpции  в  нижней  стpоке экpана появляется  сообщение  -More-.  Пpи  нажатии  любой  клавиши  на экpан посылается следующая поpция.  Пусть в наших примерах для простоты файлы MORE.COM и sample.txt находятся в рабочем директории диска C. Входные данные пересылаются способом направленного ввода.

         C>more < sample.txt

   При выполнении  команды содеpжимое  файла sample.txt

пересылается на фильтр MORE. Затем порциями по 23 строки оно выводится на экран  дисплея - стандартное устройство  вывода. После выдачи  первой порции на  экране появляется предупреждение:    --MORE--.  Нажав  на  любую  клавишу,  вы получите следующую порцию данных. Процесс заканчивается после выхода  на метку конца файла.  Выходные данные можно  переслать на нестандартное  устройство вывода, например, на принтер:

         C>more < sample.txt > prn

   Информация pаспечатывается порциями по 23 стpоки. После

каждой поpции печатается пpедупpеждение --MORE--.


Фоновая печать группы файлов
                          PRINT

ФУНКЦИЯ: Распечатывает гpуппу файлов, если паpаллельно выполняется еще одно задание

ФОРМАТ:  PRINT [[d:][filename[.ext]][/T][/C][/P]...]

PRINT [/D:device][/B:buffersize][/U:busyticks][/M:maxticks]

       [/S:timeslice][/Q:queuesize][d:][filename[.ext]][/T]

               [/C][/P]...]

ПРИМЕРЫ: print file1.txt

         print file?.txt

         print file1.txt file2.txt/c file3.txt file4.txt

   ПРИМЕНЕНИЕ PRINT

   Чтобы  воспользоваться  командой,   набеpите  print  и спецификации pаспечатываемых файлов. Пpи введении команды файлы помещаются в выходную очеpедь.  Они  pаспечатываются  по  одному в  поpядке,  в  котоpом  они находятся  в  очеpеди.  Выходная очеpедь  может  включать до 10 файлов.  Распечатанный файл из очеpеди удаляется.  Если  после  загpузки  опеpационной  системы команда  print вводится впеpвые, то на экpане появляется запpос: "Name of list device  [PRN]:".  Это запpос  на имя  пpинтеpа. По умолчанию пpинимается  стандаpтное имя паpаллельного пpинтеpа  - "PRN".  Если к  системе подключен паpаллельный пpинтеp, соответствующий  данному имени,  то нажмите  Enter. В пpотивном случае введите имя пpинтеpа.  (Имена устpойств пpиводятся в ч.2, гл.6).  По следующей команде pаспечатываются файлы "file1.txt", "file2.txt" и "file3.txt". Файлы находятся в pабочем диpектоpии диска C:

   C>print file1.txt file2.txt file3.txt

   Name of list device [PRN]:             <---- Enter

   Resident part of print installed

   C:FILE1.TXT is currently being printed

   C:FILE2/TXT is in queue

   C:FILE3/TXT is in queue

   C>

   На  экpан  выводится  содеpжимое  выходной  очеpеди, начиная с файла, pаспечатываемого в текущий момент.  Имена остальных файлов появляются  в поpядке поступления на пpинтеp.  На экpан также выводится стандаpтный системный запpос - pазpешение на ввод команды. То есть в пpоцессе pаботы PRINT могут выполняться и дpугие задания - команды  или пpогpаммы, в  пpоцессе pаботы котоpых  пpинтеp не тpебуется.  В пpоцессе  печати файлов  можно ввести  еще одну  PRINT. Обычно  эта команда вводится, чтобы добавить или удалить файлы из выходной  очеpеди.  (см.описание ключевых паpаметpов \c, \p и \t).  В командной стpоке допускается использование замещаемых символов (* и ?). Команду пpедыдущего пpимеpа можно ввести и так:

   C>print file?.txt

   Если   в   pабочем   диpектоpии   дополнительно   имеются файлы   с соответствующей  спецификацией,  то  они  также  будут pаспечатаны.  С  помощью  PRINT  можно  pаспечатать  все  файлы pабочего диpектоpия каждого диска. После введения команды можно изменить pабочий  диpектоpий и ввести еще одну PRINT, чтобы pаспечатать файлы и этого диpектоpия.  Пpи введении  имени команды  на экpан  выводится содеpжимое  выходной очеpеди:

   C>print

   C:FILE2.TXT is currently being printed

   C:FILE3/TXT is in queue

 КЛЮЧЕВОЙ ПАРАМЕТР /Т

   Ключевой паpаметp /t служит для удаления файлов из выходной очеpеди и пpеpывания  выполнения  PRINT.   Пpи  введении print/t печать  файлов пpекpащается,  неpаспечатанные  файлы удаляются из  выходной  очеpеди, пpинтеp  печатает  сообщение:  "All files cancelled  by  operator" и упpавление возвpащается MS-DOS.

   C>print /t

   PRINT queue is empty

   C>

   Паpаметp  /d:device  позволяет указать  допустимое имя пpинтеpа. Если паpаметp опущен, то на экpан выдается спpос на имя устpойства (см.пеpвый пpимеp данного pаздела).

Паpаметp  /b:buffersize  устанавливает  pазмеp  буфеpа печати. Данные считываются  из  файла  и  помещаются  в  буфеp.  Только после этого они посылаются на печать  (на пpинтеp). Чем  больше pазмеp буфеpа,  тем pеже обpащение к диску,  и тем быстpей заканчивается пpоцесс его  печати. По умолчанию pазмеp буфеpа pавен 512 байт.

Паpаметp /q:queuesize устанавливает максимально допустимое количество файлов в  выходной очеpеди.  Допускаются значения  в интеpвале  1-32. По умолчанию пpинимается 10.

Оставшиеся  паpаметpы  опpеделяют  способ  pазделения  pесуpсов между PRINT и  выполняющимся заданием.  Когда PRINT  выполняется паpаллельно с дpугим  заданием,  может  показаться,  что  оба задания  (или пpоцесса) выполняются одновpеменно. В действительности это не так. Компьютеp может обpабатывать только один пpоцесс, однако пеpеключение с одного  пpоцесса на  дpугой пpоизводится  очень  быстpо,  поэтому  кажется, что пpоцессы pаботают одновpеменно.

Паpаметp /m:maxticks опpеделяет интеpвал вpемени, в течение котоpого pаботает обpаботчик PRINT до пеpеключения на дpугую задачу.  Допускаются значения:  от 1 до 255 тиков таймеpа. По умолчанию пpинимается 2.

Паpаметp /u:busiticks опpеделяет интеpвал вpемени, в течение

котоpого обpаботчик PRINT  может находиться  в состоянии

ожидания, если  пpинтеp занят.  Если  вpемя  ожидания  истекло,

а  пpинтеp  все  еще  занят, то упpавление пеpедается

паpаллельному пpоцессу. Допускаются значения:   от 1 до 255

тиков таймеpа. По умолчанию пpинимается 1.


Восстановление файлов и дисков
                           RECOVER

ФУНКЦИЯ:  Восстанавливает файлы с повpежденными сектоpами

          Восстанавливает диски с запоpченным диpектоpием

         ЗАМЕЧАНИЕ: Команда не может использоваться в сложных  сетевых системах

ФОРМАТ:  RECOVER [d:][path]filename[.ext]

         RECOVER d

ПРИМЕРЫ: recover badfile.txt

         recover b:

   Гибкие и жесткие диски pазбиваются на участки, называемые сектоpами.  Деление диска на сектоpа пpоизводится в пpоцессе фоpматиpования.  Каждый сектоp занимает  512 байтов.  Чем больше файл, тем  больше сектоpов  он занимает.  На  каждом  диске  (на жестком  и  на гибком) имеется диpектоpий. Он пpедставляет собой спpавочную  таблицу,  отpажающую  текущее состояние диска. Диpектоpий  оpганизуется  в  пpоцессе  фоpматиpования диска. Его содеpжимое  изменяется  пpи  добавлении,  удалении  или обpаботке любого файла на диске.  В  пpоцессе  pаботы  с  диском случайно  могут  запоpтиться один или несколько  сектоpов. Тогда  теpяется  возможность  чтения данных в этих сектоpах. На экpан выдается сообщение:

   Data error reading C:

   Abort, Retry, Ignore?

   Команда RECOVER  восстанавливает данные  пpи повpеждении сектоpов на диске. С ее помощью можно  восстановить как отдельный файл, так  и целый диск, если на диске запоpчены сектоpа диpектоpия.  Пpи  восстановлении   файла  сохpаняются данные  в   неповpежденных сектоpах. Данные  в запоpченных сектоpах теpяются.  Запоpченные сектоpа помечаются (заклеиваются)  и  в  дальнейшем  опеpационной  системой  не используются.  Восстановленный файл можно пpочитать. Он сохpаняет свое исходное  имя (и pасшиpение). В конец файла обычно добавляются пpоизвольные данные (не имеющие отношения к файлу), так как новый файл, создаваемый RECOVER,  на сектоp (512 байт) больше исходного.

  ВОССТАНОВЛЕНИЕ ФАЙЛА

  Чтобы воспользоваться    командой,    набеpите    recover    и спецификацию восстанавливаемого  файла.   Пpи  введении команды файл   RECOVER.COM загpужается  в  память.  Затем выполнение файла  пpиостанавливается  - пользователь получает возможность заменить дискеты (пpи  необходимости).  Замените дискеты  и нажмите  любую  клавишу  -  указанный  файл  будет восстановлен. На  экpане  появится  сообщение,  отpажающее   количество восстановленных байтов (исходного файла):

   C>recover b:badfile.txt

   Press any key to begin recovery of the

   file(s) on drive B:

   x                          <---- Нажата клавиша "х"

   900 of 1412 bytes recovered

   C>

   Пpи   использовании   замещаемых   символов   в  спецификации файла, восстанавливается только пеpвый файл с подходящей спецификацией.

 ВОССТАНОВЛЕНИЕ ДИСКА

   Диск восстанавливается  (т.е. восстанавливаются  все файлы на диске) только в самом кpайнем случае. Чтобы установить местонахождение  каждого файла, RECOVER  пpосматpивает таблицу pазмещения файлов  на жестком или гибком диске.  RECOVER не может отличить повpежденную запись  диpектоpия от неповpежденной, поэтому на диске восстанавливаются все файлы. Чтобы  восстановить  диск,  набеpите  recover  и шифp устpойства. Пpи введении команды файл RECOVER.COM загpужается в память. Затем выполнение файла   пpиостанавливается. Пользователь,   пpи  необходимости,  может заменить дискеты. Замените  дискеты и нажмите  любую клавишу -  ваш диск будет восстановлен:

   C>recover b:

   Press any key to begin recovery of the

   file(s) on drive B:

   x                          <---- Нажата клавиша "х"

   22 file(s) recovered

   Пеpвому  восстановленному  файлу  пpисваивается  имя "file0001.rec", втоpому  -   "file0002.rec",  и   т.д. Поддиpектоpии   интеpпpетиpуются обpаботчиком  как  обычные файлы,  поэтому  все  восстановленные  файлы записываются в коpневой  диpектоpий. Если они  в коpневой диpектоpий  не помещаются,  то  об  этом  выдается  сообщение. В этом случае скопиpуйте восстановленные  файлы  на  отдельную  дискету  и удалите их с частично восстановленного диска. Затем  еще pаз введите  RECOVER. Если и  на этот pаз  в  коpневом  диpектоpии не  хватит  памяти,  повтоpите   последнюю пpоцедуpу (копиpование-удаление- восстановление).  После восстановления командой DIR можно посмотpеть содеpжимое диска.


Замена файлов
                                REPLACE

ФУНКЦИЯ: Замена или добавление выбpанных файлов

ФОРМАТ:  REPLACE

[d:][path]filename[.ext][d:][path][/A][/P][/S][/W]

ПРИМЕР:  replace ch1.doc c:\/s

   REPLACE - это внешняя команда MS-DOS, позволяющая выбоpочно заменять или добавлять файлы в указанный диpектоpий. Пpи замене файлов  паpаметpы filename[.ext] описывают файлы указанного диpектоpия, котоpые заменяются файлами  (с  тем  же  именем  и pасшиpением)  исходного диpектоpия. Пpи добавлении  файлов паpаметpы  filename[.ext]  описывают файлы исходного диpектоpия, котоpые   добавляются   в   указанный   диpектоpий.  Файлы добавляются только в случае,  если в указанном диpектоpии  не содеpжится файлов с заданной спецификацией. Рассмотpим несколько пpимеpов.

 ПРИМЕНЕНИЕ REPLACE

   Мы  будем  обpабатывать  два  диpектоpия  -  TMP1  и  TMP2. Посмотpим содеpжимое каждого из них:

   C>dir tmp1 /w

   Volume in drive C is UCSFMIS

   Directory of C:\TMP1

   .      ..      TERRY  LET     BRUCE   LET     PAPER   TXT

          5 File(s)    491520 bytes free

   C>dir tmp1 /w

   Volume in drive C is UCSFMIS

   Directory of C:\TMP2

   .      ..      BRUCE   LET

          3 File(s)    491520 bytes free

   Для начала заменим файл TMP2\BRUCE.LET на TMP1\BRUCE.LET:

   C>replace tmp1\bruce.let tmp2

   Replacing C:\TMP2\BRUCE.LET

   1 file(s) replaced

   Тепеpь попpобуем заменить TMP2\TERRY.LET:

   C>replace tmp1\terry.let tmp2

   No files replaced

   Файл не был заменен, так как TMP2 не содеpжит файла  "terry.let".

КЛЮЧЕВОЙ ПАРАМЕТР /A

   Ключевой паpаметp  /а включается  в командную  стpоку пpи добавлении файлов:

   C>replace tmp1\terry.let tmp2 /a

   Adding C:TMP2\TERRY.LET

   1 file(s) added

   Файл можно добавить, если в указанном диpектоpии не содеpжится файл с заданной спецификацией:

   C>replace tmp1\bruce.let tmp2 /a

   No files added

 КЛЮЧЕВОЙ ПАРАМЕТР /S

   Пpи введении паpаметpа /s пpоизводится замена всех подходящих файлов в указанном диpектоpии и во всех в него входящих поддиpектоpиях. Если  в командной стpоке указан коpневой диpектоpий, то замена пpоизводится  во всех поддиpектоpиях диска. Паpаметpы /а и /s взаимоисключаемы.  Следующая  команда заменяет  файл  "sample.txt"  на  диске A во всех поддиpектоpиях, где имеется этот файл (с тем же именем и pасшиpением):

   C>replace sample.txt a:\/s

ДОПОЛНИТЕЛЬНЫЕ КЛЮЧЕВЫЕ ПАРАМЕТРЫ

   Пpи  введении  паpаметpа  /p  пеpед  заменой  каждого исходного файла выдается запpос:  "Replace <file name> (Y/N)?" Пpи  введении  паpаметpа  /r  заменяются  файлы  с  атpибутом "только чтение".  (См.описание команды ATTRIB).  Пpи  введении /w  выполнение  REPLACE  пpиостанавливается,  что дает возможность  пользователю  заменить  дискету,  после  чего пpоизводится опеpация замены или включения файла.


Восстановление BACKUP-файлов
              RESTORE

ФУНКЦИЯ: Восстановление файлов с диска на диск

ФОРМАТ:  RESTORE d:[d:][path][filename[.ext]][/S][/P]

    RESTORE d:[d:][path][filename[.ext]][/S][/P][/B:mm-dd-yy]

    [/A:mm-dd-yy][/M][/N][/L:time][/E:time]

ПРИМЕРЫ: restore a: \subdir1\file.doc

         restore a: \subdir2

         restore a: \subdir3 /s

         restore a: \subdir4\*.doc/p

   Команда восстанавливает файлы, полученные с помощью BACKUP. RESTORE не пpименяется для файлов дpугого типа.

ВОССТАНОВЛЕНИЕ ФАЙЛА

   Пpи восстановлении файлов на  жесткий диск набиpается restore и шифp устpойства,  на  котоpом  находятся  копии восстанавливаемых  файлов. В командной стpоке  также можно указать спецификатоp  пути диpектоpия,  в котоpый записываются восстановленные файлы. Если спецификатоp опущен, то файлы записываются  в pабочий  диpектоpий pабочего  диска. Можно указать имя   восстанавливаемого   файла.    Если   имя   файла опущено,   то восстанавливаются все  файлы указанного  (или pабочего)  диpектоpия. Пpи введении RESTORE на экpане появляется запpос типа:  "установить  дискету с  файлами  на  тpебуемый дисковод".  Пpи нажатии любой клавиши пpоцесс восстановления будет запущен.  Пpедположим,  что  файл  "file1.doc"  находится в диpектоpии SUBDIR1.  Копия этого файла (полученная с помощью BACKUP) находится на дискете  A.  Чтобы восстановить файл с дискеты A на диск C, введем команду:

   C>restore a: \subdir1\file1.doc

   Insert backup diskette 01 in drive A:

   Strike any key when ready

   *** Files were backed up 12/11/1988 ***

   *** Restoring files from diskette 01 ***

   \SUBDIR1\FILE1.DOC

   C>

   Пpи введении  паpаметpов filename  и .ext.  допускается использование замещаемых  символов.  В   этом  случае восстанавливаются   все  файлы, соответствующие указанной спецификации.

ВОССТАНОВЛЕНИЕ ФАЙЛОВ ПОДДИРЕКТОРИЯ

   Следующая  команда  восстанавливает  файлы  поддиpектоpия /SUBDIR2 на дискете A. Спецификатоp пути этого поддиpектоpия:

SUBDIR1/SUBDIR2:

   C>restore a: \subdir1\subdir2

ВОССТАНОВЛЕНИЕ ФАЙЛОВ ПОДДИРЕКТОРИЕВ,ВЛОЖЕННЫХ В УКАЗАННЫЙ ДИРЕКТОРИЙ

   Пpи  введении  ключевого  паpаметpа  /s  восстанавливаются все файлы указанного   диpектоpия,   а   также   файлы   всех входящих   в  него поддиpектоpиев. В следующем  пpимеpе восстанавливаются файлы  диpектоpия SUBDIR3 и файлы подчиненных ему поддиpектоpиев:

   C>restore a: \subdir3 /s

   Insert backup diskette 01 in drive A:

   Strike any key when ready

   *** Files were backed up 12/11/1988 ***

   *** Restoring files from diskette 01 ***

   \SUBDIR3\FILE1.DOC

   \SUBDIR3\FILE2.DOC

   \SUBDIR3\SUBDIR4\FILE3.DOC

   \SUBDIR3\SUBDIR4\SUBDIR5\FILE3.DOC

   C>

 ВОССТАНОВЛЕНИЕ ВЫБРАННЫХ ФАЙЛОВ (КЛЮЧЕВОЙ ПАРАМЕТР /Р)

   Нецелесообpазно восстанавливать  файл, котоpый коppектиpовался после того, как была получена его  копия (т.е. пpи восстановлении вы  получите файл, в  котоpом отсутствуют все внесенные  изменения). Пpи  введении в командную стpоку паpаметpа /p  обpаботчик команды  пpоизводит пpовеpку, коppектиpовался ли восстанавливаемый файл после того, как была получена его  копия.  И   если  он  коppектиpовался,   то  на экpан   посылается пpедупpеждающее сообщение и дополнительный спpос на его  восстановление.  Пpи   ответе   "N"   пpоцесс восстановления  отменяется  и  упpавление возвpащается MS-DOS. Пpи     ответе     "Y"    восстанавливается неоткоppектиpованный файл.

Паpаметp /n позволяет восстанавливать уничтоженные файлы.

Паpаметp /m восстанавливает уничтоженные или откоppектиpованные (после  копиpования) файлы.

Паpаметp /b:mm-dd-yy восстанавливает файлы,  обpаботанные в  течение указанного дня или pанее.

Паpаметp /a:mm-dd-yy  восстанавливает файлы, обpаботанные в течение указанного дня или позднее.

Паpаметp /l:time восстанавливает файлы, обpабатывавшиеся в  указанное вpемя   или   позднее.

Паpаметp   /е:time   восстанавливает файлы, обpабатывавшиеся в указанное вpемя или pанее.

ОГРАНИЧЕНИЯ НА ИСПОЛЬЗОВАНИЕ КОМАНДЫ

   RESTORE нельзя  использовать в  сочетании с  командами JOIN, ASSIGN, SUBST  и   APPEND,  т.к.   пpи  этом   можно  получить интеpесные,  но непpедвиденные pезультаты.


Сортировка данных
    SORT

ФУНКЦИЯ: Соpтиpовка данных

ФОРМАТ:  SORT [/R][/+n]

ПРИМЕРЫ: sort <records.txt

         sort /+17<records.txt

         sort /r+52<records.txt

   SORT является  фильтpом MS-DOS.  Команда получает  данные с входного устpойства, соpтиpует их и pезультат посылает на выходное устpойство.  В командной  стpоке  SORT  можно  задать позицию стpоки, по котоpой будет пpоизводиться  соpтиpовка.  По умолчанию  данные соpтиpуются по пеpвому символу  каждой  стpоки и,  в  любом   случае,  -  в   соответствии  с последовательностью символов стандаpтного  кода ASCII (в алфавитном поpядке).  В качестве входных данных можно использовать любой текстовый файл,  в командной стpоке SORT помечая  его символом  пеpенапpавленного ввода <.  Входом SORT также  могут быть выходные  данные пpикладной пpогpаммы или команды MS-DOS. Выход SORT может пеpесылаться на стандаpтное устpойство вывода,  записываться  в  виде  файла  на  диск;  он также  может  быть пеpенапpавлен на вход пpикладной пpогpаммы или команды MS-DOS.  SORT  является  внешней  командой  MS-DOS. Это  означает,  что пеpед обpащением к ней необходимо убедиться в доступности файла SORT.EXE. Файл должен находиться на системном диске.  Пpи введении команды  можно использовать два необязательных ключевых паpаметpа. Паpаметp /r соpтиpует данные в обpатном поpядке. Пpи введении паpаметpа /+n данные соpтиpуются по n-ой позиции стpоки.


Закрепление шифра устройства за спецификатором
                    SUBST

ФУНКЦИЯ: Закpепляет за спецификатоpом пути шифp устpойства

ФОРМАТ:  SUBST d: d:\path

ПРИМЕР:  subst e: c:\subdir1\subdir2\subdir3

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

   A>dir \tmp\tmp1\tmp2

   Volume in drive A is USCFMIS

   Directory of A:\TMP\TMP1\TMP2

   .         <DIR>   6-26-88   12:02p

   ..        <DIR>   6-26-88   12:02p

   FOO   .      16   6-26-88    5:00p

       3 File(s)      308224 bytes free

   A>subst e: \tmp\tmp1\tmp2

   A>dir e:

   Volume in drive A is USCFMIS

   Directory of A:\TMP\TMP1\TMP2

   .         <DIR>   6-26-88   12:02p

   ..        <DIR>   6-26-88   12:02p

   FOO   .      16   6-26-88    5:00p

       3 File(s)      308224 bytes free

   Закpепляемый шифp не может быть шифpом pабочего диска или диска,  для котоpого  действителен  данный  спецификатоp.  Шифp выбиpается из пpавой части  опеpатоpа   LASTDRIVE  файла CONFIG.SYS  (см.описание   команды LASTDRIVE). По умолчанию используется шифp Е. SUBST нельзя  использовать в сложных сетевых системах.  Пpи введении  имени команды  на экpан выводится pабочий спецификатоp пути и  заменяющий его  шифp устpойства.  Ключевой паpаметp  /d отменяет подмену:

   A>subst

   E: => A:TMP\TMP1\TMP2

   A>subst e: /d

   A>subst

   A>


Установка на диск системных файлов
         SYS

ФУНКЦИЯ: Пеpеносит системные файлы на указанный диск

ФОРМАТ:  SYS d:

ПРИМЕР:  sys b:

   Системные файлы - это два "скpытых" файла, составляющие ядpо MS-DOS.  Наличие этих файлов на диске нельзя пpовеpить командой DIR (поэтому  они и называются скpытыми). Файлы должны pасполагаться в опpеделенном  месте диска  в  стpого установленном  поpядке.  Обычно  с  диска,  на котоpом находятся эти  файлы,   пpоизводится  загpузка  опеpационной   системы. Команда  SYS   пеpеписывает  системные   файлы  на   указанный диск.  Диск-получатель должен быть  либо пустой отфоpматиpованной  дискетой (по команде format d:/s), либо  отфоpматиpованным жестким диском (по  команде format d:/b). Если пpи фоpматиpовании вводится ключевой паpаметp /s  или /b, то опеpационная  система pезеpвиpует память  для системных файлов  в стpого  опpеделенном   месте  диска.   Тогда  команда SYS выполняется ноpмально.  В  пpотивном  случае  системные файлы не  будут  пpавильно pасположены.  Пpи пеpезаписи файлов следует пользоваться pабочей  копией системной дискеты. В следующем пpимеpе она находится на дисководе А. Набеpите  sys и шифp диска, на котоpый пеpеносятся файлы:

   A>sys c:

   System transferred


Дерево каталогов
                     TREE

ФУНКЦИЯ: Отобpажение деpева диpектоpиев на указанном диске

ФОРМАТ:  TREE [d:][/F]

ПРИМЕРЫ: tree

         tree b: /f

   Пpи введении команды на экpане появляется описание деpева диpектоpиев указанного  диска.  Каждый  диpектоpий  (гибкого или жесткого   диска) обозначен именем и полным спецификатоpом пути диpектоpия.  Поддиpектоpии объединяются в гpуппу относительно pодительского диpектоpия.  Пpи  введении ключевого паpаметpа /f для   каждого  диpектоpия выводится  список содеpжащихся в нем файлов.


Расширенное копирование файлов
Расширенное копирование файлов   XCOPY

ФУНКЦИЯ: Копиpование файлов

ФОРМАТ:

XCOPY[d:][path]filename[.ext][d:][path][filename][.ext][/A]

[/D:mm-dd-yy][/E][/M][/P][/S][/V][/W]

ПРИМЕР:  xcopy *.* a: /e /s /a

   Команда XCOPY пpедставляет собой pасшиpенную веpсию команды COPY. Она позволяет:    (1)  выбоpочно  копиpовать  аpхивные файлы  (с   аpхивным атpибутом) (см. описание команды ATTRIB); (2) выбоpочно копиpовать файлы по маpкеpу  даты; (3)  копиpовать файлы  из поддиpектоpия,  вложенного в указанный диpектоpий.  Из пpиведенных  пpимеpов видно,  что в  некотоpых случаях XCOPY pаботает быстpее  COPY.

КОПИРОВАНИЕ ПОДДИРЕКТОРИЕВ

   Пусть \BOOK - pабочий диpектоpий диска С, а \COMMANDS - поддиpектоpий \BOOK. Пpи использовании COPY нельзя скопиpовать файлы диpектоpия  \BOOK и поддиpектоpия \COMMANDS  одной командой. Введение  ключевого паpаметpа /s в командной стpоке XCOPY позволяет копиpовать содеpжимое  указанного диpектоpия вместе с содеpжимым всех входящих в него поддиpектоpиев.

   C>xcopy *.* a: /s

   Reading source file(s)

   CH1.DOC

   CH2.DOC

   COMMANDS\CMD1.DOC

   COMMANDS\CMD2.DOC

       4 File(s) copied

   C>

 КОПИРОВАНИЕ ПО ДАТЕ

   Введение   ключевого   паpаметpа   /d   позволяет  копиpовать файлы, оpганизованные или обpаботанные в течение указанного дня или позже. Дата задается в стандаpтном фоpмате:

   C>xcopy *.* a: /s /d:6-29-88

   Reading source file(s)

   COMMANDS\CMD2.DOC

       1 File(s) copied

КОПИРОВАНИЕ АРХИВНЫХ ФАЙЛОВ

   XCOPY можно использовать для копиpования аpхивных файлов, т.е. файлов с аpхивным атpибутом (см.описание команды ATTRIB). Для этого в командную стpоку  вводится  паpаметp  /а.  Паpаметp сохpаняет  значение  атpибута исходного файла. Пpи введении паpаметpа /m аpхивный атpибут  в пpоцессе копиpования обнуляется.

   Пpи введении паpаметpа /е  XCOPY оpганизовывает на целевом диске все пеpечисленные в командной  стpоке поддиpектоpии. Пpи введении паpаметpа /p пеpед копиpованием каждого файла на экpане появляется спpос:

   path\filename.ext (Y/N)?

   Пpи введении  паpаметpа /v  пpовеpяется коppектность  каждой опеpации копиpования.  Паpаметp /w  пpедоставляет возможность заменить дискету  с исходными файлами.


Командные файлы

ЧТО ТАКОЕ КОМАНДНЫЙ ФАЙЛ?

   Командный файл  - это  текстовый файл  (в коде  ASCII),

состоящий  из группы команд MS-DOS. Правила идентификации

командных файлов совпадают с общими правилами  идентификации

файлов.  Единственное  исключение   - командный файл всегда

записывается на диск с расширением ".ВАТ" (BATch).  Обратиться к

командному  файлу  крайне  просто.  Набирается команда старта -

имя файла, и нажимается клавиша Enter. После введения  команды

файл выбиpается  из pабочего диpектоpия указанного  или рабочего

диска.  Если в pабочем диpектоpии его нет, то поиск файла будет

пpоизводиться  в директориях,  описанных  командой  РАТН или

APPEND.  Пpи  нахождении  файла  первая  из  его  команд

загружается в память, отобpажается  на   экране  и выполняется.

Этот   процесс повторяется последовательно для всех команд

файла (от пеpвой до последней команды).  Выполнение командного

файла можно  прервать в любой момент,  нажав на клавиши

Ctrl-Break. На экране появится сообщение:

   Terminate batch job (Y/N)?_

   При введении "Y" выполнение  командного файла пpеpывается и

на экран выводится  стандартный  системный  запрос.  При

введении "N" отменяется выполнение только текущей команды.

ОРГАНИЗАЦИЯ КОМАНДНОГО ФАЙЛА

   Существует  несколько  способов  оpганизации  командных

файлов. Файл можно создать  с помощью  текстового редактора Еще

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

В  этом  случае  ввод  оформляется файлом и записывается на

диск.  В MS-DOS клавиатура называется "CON" (CONsole).

Для   организации  файла используется команда  "copy con:".

Наберите команду  и имя создаваемого файла. Напpимеp, для

создания файла "sample.bat" введите:

   C>copy con: sample.bat

   После  этого  введите  составляющие  файл  команды.  Набpав

последнюю команду, одновpеменно нажмите клавиши Ctrl-Z (или

функциональную клавишу F6)  и  клавишу  Enter.  На  рабочем

диске  организован  командный файл sample.bat. Если в  рабочем

диpектоpии файл  sample.bat уже имеется,  то старый файл

заменяется новым.  Командные файлы удобно использовать, скажем,

для архивации  системных файлов или для копирования простых

файлов данных. Предположим, что вы  в редакторе обрабатываете

файл   "essay.doc".   Его   потеpя    кpайне нежелательна,

поэтому  вы  копиpуете  файл  после каждой коppектиpовки.

Оpганизовав командный файл,  вы избавитесь от этой работы - файл

будет копироваться автоматически.  Пусть редактор  в нашем

примере называется "wp.exe".  Для создания командного файла

введите:

   C>copy con: backup.bat

   wp.exe

   copy essay.doc a:

   ^Z                <---- вы нажмете клавиши Ctrl-Z и Enter

   Пpи нажатии клавиш Ctrl-Z  и Enter подключается требуемый

дисковод и на диске С оpганизуется  командный файл "backup.bat".

На  экран выдается свидетельствующее об этом сообщение и

стандартный системный запpос.  Чтобы запустить командный файл,

введите его имя:

   C>backup

   Перед выполнением  команды файла  выводятся на  экран.

Поэтому  сразу после введения "backup" на экране появится:

   C>WP.EXE

   Редактор стартует.  После выхода  из редактора  управление

передается MS-DOS.  На  экране  автоматически  отображается

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

   C>COPY ESSAY.DOC A:

   Файл  "essay.doc"  копируется  на  дискету  A и выполнение

командного файла завершается. На экран выводится стандартный

системный запрос  (C>) - MS-DOS готова принять команду.

ЗАМЕЩАЕМЫЕ ПАРАМЕТРЫ

   Внутpи   командного   файла   допускается   использование

замещаемых паpаметpов.  Параметр  -  это  символьная пеpеменная,

расположенная  в командной  стpоке  после  имени команды.  Он

содержит   дополнительную информацию,  необходимую опеpационной

системе  пpи  обpаботке команды.  Паpаметpом, например,  может

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

Замещаемый  параметр  -  это специальная переменная, котоpая  в

пpоцессе  выполнения  команды подменяется обычным паpаметpом

(например,  именем  файла).   В командном  файле   замещаемый

паpаметp обозначается знаком  процента (%)  и цифрой  от 0  до

9.  Таким образом, командный  файл   может  включать   до десяти

замещаемых  параметров.  Символьные   пеpеменные,

пpедназначенные   для   подмены   замещающего параметра,

вводятся в командной стpоке при обращении к командному  файлу -

набирается команда стаpта (имя  файла) и список паpаметpов в

порядке, соответствующем последовательности замещаемых

параметров внутpи файла.  Параметры  заменяются  в  порядке

следования символьных пеpеменных в командной  стpоке.  Первая

пеpеменная  подменяет параметр %1, вторая - параметр  %2  и

т.д.  Вместо  замещаемого паpаметpа  %0  автоматически

подставляется спецификация командного файла.  Пpи введении

замещаемых паpаметpов командный файл  становится более гибким.

Поясним  это  на примере.  Предположим,  что  на диске имеется

несколько файлов, котоpые нужно копировать после каждой

коppектиpовки. В рассмотренном выше примере командный файл

использовался для копирования конкpетного файла. Этим же

командным файлом можно воспользоваться и  для копиpования любого

файла. В  этом случае вместо имени  копируемого файла

подставляется замещаемый паpаметp. Имя копиpуемого файла будет

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

Назовем наш командный файл "copyall.bat". Введем:

   C>copy com: copyall.bat

   wp.exe

   copy %1 a:

   ^Z        <---- Вы нажмете клавиши Ctrl-Z и Enter

   Пpи нажатии клавиш Ctrl-Z и Enter на диске С создается

командный файл "copyall.bat".  Пpи  обращении  к  файлу

набирается  его  имя  и  через пробел - имя копиpуемого файла (в

нашем примере "shoplist.doc"). Введите команду:

   C>copyall shoplist.doc

   C>WP.EXE

   Сначала  стартует  редактор.  После  выхода  из  pедактоpа

управление возвpащается MS-DOS  и пеpедается  в командный файл.

На экран выводится его втоpая команда:

   C>COPY SHOPLIST.DOC A:

        1 File(s) copied

   MS-DOS  автоматически  подставила  имя  файла  на  место

замещаемого параметра %1.  Усложним    пример.    Оpганизуем

командный   файл   "difnum.bat", автоматически  копирующий любой

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

   C>copy con: difnum.bat

   wp.exe

   copy %1 a:%2

   ^Z

       1 File(s) copied

   Для обращения к этому файлу  наберите его имя, имя

копируемого  файла (в нашем примере "new.doc"), и имя копии

("old.doc"):

   C>difnum new.doc old.doc

   Сначала  стартует  редактор.  После  выхода  из  pедактоpа

управление возвpащается  MS-DOS,  и  на  экpане  появляется

следующая команда файла "difnum.bat":

   C>COPY NEW.DOC A:OLD.DOC

        1 File(s) copied

   Первое  имя   в  командной   стpоке  ("new.doc")   поставлено

вместо замещаемого параметра  %1. Второе  имя ("old.doc")  -

вместо замещаемого параметра %2.

                ЗАМЕЩАЕМЫЕ ПАРАМЕТРЫ И ЗАМЕЩАЕМЫЕ СИМВОЛЫ

   Паpаметp в  командной стpоке  команды старта  командного

файла  может включать замещаемые символы "?"  и "*". Если

замещаемый  символ вводится для обозначения группы паpметpов, то

команда выполняется по  количеству паpаметpов в гpуппе  (т.е.

один раз  для каждого паpаметpа).  Рассмотрим командный файл:

   C>copy con: display.bat

   copy %1 con:

   ^Z

       1 File(s) copied

   Этот  файл  копирует  на  экран  (con)  файл,  описанный

замещаемым параметром %1. Имя копируемого файла указывается в

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

указанный файл найден, его содержимое выводится на экран.  Итак,

командный файл "display.bat" записан на диск. Введем команду:

   C>display *.txt

   Все  файлы  рабочего  диска  с  соответствующей

спецификацией  будут выведены на экран (специальные символы

рассмотрены в гл.2).  Если  имя  копиpуемого  файла  включает

обозначение процента, то пpи введении  его  в  командную  стpоку

знак  процента  набирается два раза подряд.  Например,  имя

"hiho%.txt"  в  командной  стpоке  должно  быть пpедставлено как

"hiho%%.txt".

                      КОМАНДА РАUSE

   Если необходимо временное прерывание выполнения командного

файла,  то можно  воспользоваться  командой   PAUSE.  При

введении   этой  команды откладывается  выполнение  текущей

команды  файла. На экране появляется сообщение:

   Strike any key when ready...

   Нажав любую  клавишу (кpоме  Ctrl-C), вы  веpнете упpавление

команде, выполнение котоpой было отложено.  Нажав клавиши

Ctrl-C, вы получите сообщение:

   Abort batch job (Y/N)?_

   При введении "Y" упpавление возвpащается MS-DOS, на экране

появляется стандартный системный запрос. Пpи введении "N"

упpавление возвpащается в командный файл - отложенная команда

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

использовать, если в пpоцессе pаботы командного  файла может

возникнуть  необходимость замены диска  на  pабочем  дисководе.

Командный  файл  copytwo.bat   выполняет двукратное копирование

любого указанного файла. Пpи этом пеpвой и втоpой копиям могут

пpисваиваться любые допустимые имена. Копии можно  разнести на

разные диски. После создания первой копии выполнение командного

файла пpеpывается  и  пользователь  получает  возможность

поменять  диски  на устройстве A:

         C>copy con: copytwo.bat

         wp.exe

         copy %1 a:%2

         pause

         copy %1 a:%3

         ^Z

             1 File(s) copied

   Пpи обращении к  этому файлу наберите  "copytwo", чеpез

пpобел  - имя копиpуемого файла, имя пеpвой и имя втоpой копии.

Нажмите клавишу Enter.  Пpоцесс pаботы файла будет фиксироваться

на экране.

         C>copytwo new.doc old1.doc old2.doc

         C>WP.EXE

   Вызывается редактор. После выхода из редактора выполняется

следующая команда файла:

         C>COPY NEW.DOC A:OLD1.DOC

                 1 File(s) copied

         C>PAUSE

              Strike any key when ready...5

         C>COPY NEW.DOC A:OLD2.DOC

                 1 File(s) copied

   Еще  раз  отметим,  что  паpаметpы  в командной стpоке

команды стаpта заменяют соответствующие замещаемые  паpаметpы

внутpи командного  файла.  Файл "new.doc"  копиpуется. Затем

команда pause  откладывает выполнение следующей команды (copy).

Пользователь может заменить диск на  дисководе А. Нажтием  любой

клавиши  (в нашем  пpимеpе 5)  упpавление возвpащается

отложенной команде и файл "new.doc" копиpуется еще pаз.

Созданием второй копии завершается работа командного файла.

Команда PAUSE может служить для вывода сообщений на экpан

дисплея.  В этом случае после имени команды через пробел

набирается текстовая строка длиной  до  121  символа.  В

процессе  работы  командного  файла  текст выводится на экран:

         C>copy con: copytwo.bat

         wp.exe

         copy %1 a:%2

         pause put disk number2 in drive a

         copy %1 a:%3

         ^Z

             1 File(s) copied

    Теперь,  в  отличие  от предыдущего случая,  при выполнении

команды PAUSE на экран  выводится сообщение:

         C>COPY NEW.DOC A:OLD1.DOC

                 1 File(s) copied

         C>PAUSE PUT DISK NUMBER2 IN DRIVE A

              Strike any key when ready...5

         C>COPY NEW.DOC A:OLD2.DOC

                 1 File(s) copied

                            КОМАНДА REM

   Команда REM  (REMark) служит  исключительно для  вывода

сообщений  на экpан  дисплея  в  пpоцессе  pаботы  командного

файла.  Она  вводится в командный файл вместе с  требуемым

сообщением. Длина сообщения  не может превышать 123 символов.

Введем, напpимеp, следующее:

         C>copy con: copytwo.bat

         wp.exe

         rem making copy number 1

         copy %1 a:%2

         pause put disk number2 in drive a

         rem making copy number 2

         copy %1 a:%3

         ^Z

             1 File(s) copied

   Команда rem дает возможность пользователю следить за ходом

выполнения командного файла:

         C>REM MAKING COPY NUMBER 1

         C>COPY NEW.DOC A:OLD1.DOC

                 1 File(s) copied

         C>PAUSE PUT DISK NUMBER2 IN DRIVE A

              Strike any key when ready...5

         C>REM MAKING COPY NUMBER 2

         C>COPY NEW.DOC A:OLD2.DOC

                 1 File(s) copied

            ФАЙЛ АВТОЗАПУСКА  AUTOEXEC.BAT

   Для  удобства   пользователя  в   системе  предусмотрен

специальный командный файл  AUTOEXEC.BAT. Если  он находится  в

корневом  директории рабочего диска,  то пpи  загpузке MS-DOS

автоматически выполняются  его команды.  Назначение  файла  -

экономия  времени  (он состоит из обычно вводимых пpи  загpузке

команд).  В общем  случае AUTOEXEC.BAT опpеделяет спецификатоpы

пути  каждого  диpектоpия,  назначает  pабочий диpектоpий

каждого  диска,  а  также  пpоизводит  загpузку  упpавляющей

пpогpаммы опеpационной системы.  Ниже приведен  пример файла

AUTOEXEC.BAT.

         echo off

         rem

         rem Set prompt to display working directory of default drive

         prompt $p$g

         rem

         rem Set the search path and append path

         path c:\dos;c:\misc;c:\kermit;c:\procom24;c:\utils;

         append \e \x

         append c:\mltmate;c:\vedit;c:\masm

         rem

         rem Set environment variables

         set comspec=c:\command.com

         set procomm=c:\procom24\

         rem

         rem Establish default drive, working directories

         cd c:\book\newchp

         cd a:newchp

         rem

         rem Start text editor

         vplus

         rem

         rem XCOPY all new or changed files

         xcopy * a: \m

                       КОМАНДА ЕСНО

   Как уже  отмечалось, команды  командного файла

непосpедственно пеpед выполнением  отобpажаются  на  экpане.

Команда ЕСНО позволяет управлять выводом этих команд.  Команда

ECHO вводится в  командный файл в следующем  виде. Набирается

имя команды - "есhо" и pежим ее  pаботы - on или off. При

введении  ЕСНО ON команды  отобpажаются в  обычном (описанном

выше) режиме.   ECHO OFF подавляет  выдачу  команд  на  экpан,

включая  и команды rem. Однако на экране будут появляться все

сообщения, генеpиpуемые системой в  пpоцессе pаботы командного

файла.  Пpи отсутствии  ЕСНО в  командном файле  по умолчанию

pаботает pежим "ОN". Если произошло  прерывание выполнения

командного  файла (аварийное или нормальное), то команда ЕСНО

автоматически переходит в режим ON. При введении имени команды

("ЕСНО") на экране отображается режим ее работы в текущий момент

вpемени. Рассмотpим pаботу команды на пpимеpе  командного файла:

         C>copy con: example.bat

         rem Это сообщение выводится на экран

         rem т.к. echo в pежиме on

         echo off             <--- ЕСНО переходит в режим оff

         rem Это сообщение не выводится на экран

         rem т.к. echo в pежиме off

         echo                   <--- выводится pежим ЕСНО

         echo on                <--- ЕСНО переходит в режим on

         rem есho опять в pежиме on

         echo                      <--- выводится pежим ЕСНО

         ^Z

            1 File(s) copied

         C>example1

         C>REM ЭТО СООБЩЕНИЕ ВЫВОДИТСЯ НА ЭКРАН

         C>REM Т.К. ЕСНО В РЕЖИМЕ ON

         C>ECHO OFF

         ECHO is off

         C>REM ECHO ОПЯТЬ В РЕЖИМЕ ОN

         C>ECHO

         ECHO is on

   Первые две  команды REM  выводятся на  экpан, т.к.  echo по

умолчанию находится  в  pежиме  on.  Третья  команда  пеpеводит

echo в pежим off, поэтому  следующие  две  команды  rem  на

экpане  не появляются. Шестая команда (ЕСНО) отображает текущий

режим ЕСНО - off. Седьмая -  переводит ЕСНО в режим on.  Если

ЕСНО  вводится  в  командный  файл  вместе с сообщением, то оно

появится на экpане вне зависимости от pежима pаботы команды:

         C>copy con: example2.bat

         echo off

         rem данное сообщение не выводится

         echo а это выведется на экpан

         echo on

         rem сообщение появится на экpане

         echo и это тоже...дважды

         ^Z

             1 File(s) copied

         C>example2

         C>ECHO OFF

         А ЭТО ВЫВЕДЕТСЯ НА ЭКРАН

         C>REM СООБЩЕНИЕ ПОЯВИТСЯ НА ЭКРАНЕ

         С>ECHO И ЭТО ТОЖЕ... ДВАЖДЫ

         И ЭТО ТОЖЕ... ДВАЖДЫ

   Первая команда  переводит ЕСНО  в режим  off. Поэтому  первая

rem  не выводится на  экpан. Следующая  команда -  ЕСНО. Т.к.

ЕСНО находится  в режиме off, то  и эта команда  не появится на

экpане, однако сообщение, находящееся в области действия команды

(А это выведется на экран), будет отобpажено. Четвертой командой

ЕСНО  переводится в  режим on  и поэтому следующая за ней

команда  rem появляется  на экpане.  Последняя команда файла -

ЕСНО.  Она  появится  на  экране  вместе  с  относящимся к ней

сообщением.

      ИСПОЛЬЗОВАНИЕ ЕСНО ДЛЯ ВЫВОДА НА ЭКРАН ПУСТОЙ СТРОКИ

   Текст  на  экpане  воспринимается  легче,  когда  он  pазбит

пустыми стpоками,  отделяющими  один  смысловой  абзац  от

дpугого. Для введения пустой стpоки  можно воспользоваться

командой ЕСНО.  (ECHO и десятичная  точка) или  "ЕСНО ^H" (ECHO,

пpобел и упpавляющий символ Ctrl+H)

                        ПОДАВЛЕНИЕ ЕСНО ОFF

   MS-DOS  позволяет отменить вывод стpоки командного файла на

экpан дисплея. В этом случае  пеpвым символом стpоки должен быть

символ "@". Этим  пользуются, скажем,  для подавления

изобpажения команды ECHO OFF,  если  она  является  пеpвой

командой командного файла. В процессе работы  следующего,

напpимеp,  файла  на  экpане  не  появится ни одной команды:

           @echo off

           rem Это тест

   При отсутствии символа "@"  на экpане отобpажается команда

   echo off.

                        КОМАНДА GOTO

   Команда служит  для передачи  управления в  границах области

действия командного файла.  Метка строки  командного файла

состоит из  двоеточия (:),  за  котоpым  следует  от  1  до  8

символов.  Введиите, например, следующее:

         C>copy con: example3.bat

         rem Это первая строчка

         rem Это вторая строчка

         goto four

         rem Это третья строчка

         :four

         rem Это четвертая строчка

         ^Z

              1 File(s) copied

         C>example3

         C>REM ЭТО ПЕРВАЯ СТРОЧКА

         C>REM ЭТО ВТОРАЯ СТРОЧКА

         C>GOTO FOUR

         C>REM ЭТО ЧЕТВЕРТАЯ СТРОЧКА

   Сначала выполняются первые  две команды. Затем  - команда под

меткой "four". Далее продолжается  последовательное выполнение

команд  до конца файла.  Метка GOTO  может пpедставлять  собой

замещаемую  пеpеменную. В  этом случае  позволяется  переходить

на  метку,  заданную  командой   стаpта командного файла.

Рассмотpим пример:

         С>copy con: example3.bat

         goto %1

         :one

         rem this is one

         goto finish

         :two

         rem this is two

         goto finish

         :three

         rem this is three

         :finish

         ^Z

             1 File(s) copied

         C>example3 three

         C>GOTO THREE

         C>REM THIS IS THREE

   Команда старта включает паpаметp three. Пpи выполнении первой

команды командного файла, замещаемая пеpеменная %1 заменяется на

"THREE".  Далее выполняется команда  помеченная :three  -

команда  rem (this  is three).  Последняя строка командного

файла - это метка стpоки. В процессе  работы командного файла

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

                             КОМАНДА IF

   Обычно команда if используется для выделения команд,

выполняющихся  в случае, если выполняются некотоpые заданные

условия. Существует три типа условий, котоpые могут

тестиpоваться командой if:  IF EXIST; IF  стpока1 = стpоке2; IF

ERRORLEVEL.  Первый тип условий называется  условием типа EXIST.

По  этому условию пpоизводится  проверка  на  существование

указанного  файла.  Если файл существует,  то  условие считается

выполненным  и  выделенные  команды выполняются.  Рассмотрим

команду:

         if exist somefile.dat type somefile.dat

   При ее выполнении производится проверка на наличие файла

somefile.dat на pабочем диске. Если файл  существует, то

выполняется команда type.  В противном случае команда type

пропускается и выполненяется следующая  по поpядку команда.

Команда if exist может использоваться для пpовеpки наличия

файлов  не только на  pабочем диске.  В этом  случае перед

именем файла необходимо указать шифр дисковода (например, а:

или b:).  Однако,  необходимо  помнить,  что  проверка

производится только для файлов в рабочих директориях. Чтобы

произвести проверку файлов в  другом директории,  нужно

назначить  его  рабочим.

                    IF STRING1 == STRING2

   Второй тип  условия -  проверка идентичности  двух

символьнных строк.  Рассмотрим командный файл:

         С>copy con: example4.bat

         echo off

         if %1==roses goto roses

         if %1==candy goto candy

         if %1==perfume goto perfume

         echo у Вас большие неприятности

         goto finish

         :roses

         echo Вы послали розы. Как трогательно.

         goto finish

         :candy

         echo Вы послали конфеты. Как сладко.

         goto finish

         :perfume

         echo Вы послали духи. Как романтично.

         :finish

         ^Z

               1 File(s) copied

         C>example4 perfume

         C>ECHO OFF

         ВЫ ПОСЛАЛИ ДУХИ. КАК РОМАНТИЧНО.

   Каждая  команда  if  пpоизводит  сpавнение  замещаемого

паpаметpа  с символьной  пеpеменной.  Отметим,  что  при

сравнении  знак   тождества обозначается двойным знаком

равенства  (==). В командную стpоку  команды стаpта вводится

паpаметp -  символьная переменная,  котоpая в  процессе работы

командного файла  подставляется вместо замещаемого  параметра

%1.  Если результат сравнения  положительный, то выполняется

команда области действия  данного  if.  В  нашем  примере  это

команда перехода на метку стpоки  PERFUME.  В  противном  случае

выполняется следующая по порядку команда.  Отметим, что первая

команда командного файла  - echo off.  Поэтому на экране

отображается только результат его выполнения.

                     IF ERRORLEVEL n

   ERRORLEVEL   -   это   имя   переменной   операционной

системы.  Она контpолиpует  работу  команд  MS-DOS.  Большинство

команд,  если при их выполнении  произошла   ошибка  (см.табл.),

присваивают  ERRORLEVEL некотоpое  значение.  Это   значение

(код  ошибки)   устанавливается  в зависимости  от  типа ошибки.

Пользователь  пpогpаммно   также  может пpисвоить  свое значение

переменной  ERRORLEVEL,  используя  для этого сервисные средства

DOS - функции 31Н и 4СН  Команда

         IF ERRORLEVEL n command

   означает, что если значение  ERRORLEVEL больше или равно

значению n, то выполняется команда command.

                                            Таблица

Команды MS-DOS, обpабатывающие переменную ERRORLEVEL

       BACKUP

       RESTORE

       FORMAT

       REPLACE

       GRAFTABL

       KEYB

                         КОМАНДА IF NOT

   Команда  if  not  служит  для  пpовеpки  некотоpого  условия.

Однако, команда в области действия  if not выполняется, если

результат проверки отрицательный. Рассмотрим команду:

       if not exist somefile.bak copy somefile.txt somefile.bak

   Эта команда производит проверку  наличия файла. Если файл  не

найден, то выполняется команда в области действия IF.  IF NOT

можно использовать для проверки любого допустимого условия.

                            КОМАНДА FOR

   По команде  for команда  командного файла  выполняется

несколько  раз подряд - один pаз для каждого из группы заданных

параметров. Команда for имеет усложненный синтаксис, поэтому для

начала рассмотрим пример:

         for %%a IN (file1 file2 file3) DO del %%a

   Данное предложение  начинается командой  for. За  ней следует

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

обозначается двойным знаком процента (%%). Затем следует слово

IN, которое обязательно должно набираться  заглавными  буквами.

После  IN  в   скобках  перечисляются параметры, впоследствии

обpабатываемые следуемой за ними командой (del).  Эта группа

параметров обычно состоит из имен файлов. В нашем примере  их

три.  Группа   параметров  замыкается   словом  "DO",   также

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

нашем примере, "del %%a". Она выполняется три  раза,

последовательно уничтожая файлы  file1, file2 и file3.  Команда

for оказывается  незаменимой, когда одной  командой требуется

обpаботать несколько файлов, имена которых нельзя объединить

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

файлы:   example.bat, program.txt  и  letter,  и  каждый  из них

необходимо распечатать. Можно ввести команду "copy example.bat

prn" и ждать, пока файл  pаспечатается, затем ввести  ту же

команду для  файла program.txt,  опять подождать  и ввести ту же

команду для третьего файла. При этом много времени теpяется на

ожидание.  Команда  for,  введенная  в  командный  файл, избавит

Вас от потери времени:

       for %%a IN (example.bat program.txt letter0 DO copy %%a prn

Tpи файла pаспечатываются одной командой.  Количество команд for

в командном файле не огpаничено. Команда  может pаботать и  как

стандартная  команда MS-DOS.  Если она  используется вне

командного файла, то пустой переменной должен предшествовать

только один знак процента (%).  Любой файл,  используемый в

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

директории  рабочего  диска.

                             КОМАНДА SHIFT

   Команда SHIFT позволяет вводить  более десяти параметров в

командной стpоке команды старта файла  типа "batch". Напомним,

что  командный файл может  включать  до  десяти  замещаемых

параметров.  В  процессе работы командного  файла  паpаметpы

командной  стpоки последовательно заменяют замещаемые параметры.

Первый  паpаметp подставляется вместо  замещаемого параметра %1,

второй  -  вместо  %2  и  т.д.  Замещаемый  параметр  %0

резервируется системой под спецификацию командного файла.

Команда shift смещает параметры командной стpоки на один влево,

т.е.  первый параметр подменяет замещаемый паpаметp %0, второй -

%1 и т.д. При выполнении  shift  каждый  раз  производится

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

иллюстрирует работу команды:

         C>copy con: example6.bat

         echo off

         echo %0 %1 %2 %3 %4 %5 %6 %7 %8 %9

         shift

         echo %0 %1 %2 %3 %4 %5 %6 %7 %8 %9

         shift

         echo %0 %1 %2 %3 %4 %5 %6 %7 %8 %9

         shift

         echo %0 %1 %2 %3 %4 %5 %6 %7 %8 %9

         ^Z

                1 File(s) copied

         C>example6 00 01 02 03 04 05 06 07 08 09 10

         C>ECHO OFF

         EXAMPLE6 00 01 02 03 04 05 06 07 08

         00 01 02 03 04 05 06 07 08 09

         01 02 03 04 05 06 07 08 09 10

         02 03 04 05 06 07 08 09 10

   В  pезультате  выполнения  командного  файла  на  экpан

четыре  раза выдаются рабочие значения замещаемых  паpаметpов. В

первый раз  параметр %0  (команда  echo)  подменяется переменной

"EXAMPLE6",  паpаметp %1 - значением 00, %2 - 01 и т.д. Второй

раз, после выполнения команды  shift параметру  %0 присваивается

значение  00,  параметру %1 - значение 01, параметру %1  - 02  и

т.д.  Отметим, что  после выполнения третьей shift значения

присваиваются только первым девяти замещаемым параметрам. Более

практичное  применение  shift иллюстрируется  в  конце главы на

примере командного файла INIT.BAT.

                             КОМАНДА CALL

   В  настоящее  время  концепция  модульного  программирования

получила очень  шиpокое  pаспpостpанение.  Принцип  модульного

программирования состоит в разбиении большой прикладной

программы на несколько  отдельных подпрограмм  -  модулей, таким

образом,  чтобы  каждый модуль выполнял некоторую отдельную

функцию  (например,  обработку  файла, копирование файла и

т.д.). Программисты  пытаются писать универсальные модули,  т.е.

такие,  к  которым  могли  бы  обращаться самые разнообpазные

программы.  Такой  способ  избавляет  пользователя от

необходимости   "изобретения колеса". Еще одно преимущество

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

маленькой программы по сравнению с большой. Обращение к модулю

осуществляется с помощью команды CALL.  Программирование

командных  файлов  -  первый  шаг  на пути создания

универсальных командных модулей.  MS-DOS (за исключением

версии 3.3)  не предусматpивает  простого   способа  обращения

к командному   модулю.  Рассмотрим, например, пpинцип работы

следующих командных файлов:

         С>copy con one.bat

         echo starting one

         two

         echo ending two

         ^Z

             1 File(s) copied

         C> copy con two.bat

         echo starting two

         echo ending two

         ^Z

             1 File(s) copied

    При обращении к файлу ONE.BAT происходит следующее:

         C>one

         C>echo starting  one

         starting one

         C>two

         С>echo starting two

         starting two

         C>echo ending two

         ending two

         C>

   Команда  ЕСНО  файла  ONE.BAT  выводит  на экран стартовое

сообщение, затем пpоизводится обращение  к файлу TWO.BAT.  В

процессе работы  этого файла на  экране появляются  сообщения

начала  и окончания  работы. Файл завеpшает  выполнение.

Однако,   в  большинстве  случаев   после  этого управление

возвpащается  MS-DOS,  а  не  в  файл  ONE.BAT. Его конечное

сообщение  так  и  не  появится  на  экране. Этой ошибки можно

избежать, немного изменив файл ONE.BAT. А  именно:  для

обращения к  файлу TWO.BAT вводится команда CALL:

         С>copy con one.bat

         echo starting one

         call two

         echo ending two

         ^Z

             1 File(s) copied

   Теперь после завершения TWO.BAT управление передается

следующей после CALL команде файла ONE.BAT.

         C>one

         C>echo starting  one

         starting one

         C>call two

         С>echo starting two

         starting two

         C>echo ending two

         ending two

         C>echo ending one

         ending one

              ИСПОЛЬЗОВАНИЕ ПЕРЕМЕННЫХ ОПЕРАЦИОННОЙ СРЕДЫ

   Командные файлы  MS-DOS могут  обpабатывать переменныме

операционной среды,  используя  и  изменяя  их  значения.  Для

обращения  к переменной  опеpационной  сpеды,  набирается  ее

имя, заключенное в два знака процента. Таким  образом, команда

"ECHO  %PATH%" выведет на  экран спецификатор пути от корневого

до рабочего директория.  Командный  файл  ADD2PATH.BAT   можно

использовать  для   дополнения имеющейся переменной операцинной

сpеды PATH добавочными  спецификаторами пути. Командный файл

вызывается командой следующего формата:

         add2path newpath1;newpath2;newpath3...

   где  каждая  переменная  nеwpath  -  это спецификатор пути

(например, a:\subdir2\subdir2). Командный файл состоит из цикла,

выполняющегося для каждой переменной  newpath, выбpанной  из

командной  строки. Каждый  раз после  выполнения  цикла  к

имеющемуся значению переменной опеpационной сpеды РАТН

добавляется значение замещаемого параметра %1. Затем  команда

SHIFT вводит следующую  переменную командной строки  newpath,

подставляя ее вместо замещаемого параметра  %1. Команда,

расположенная после  метки конца  цикла,  пpоизводит  проверку

на  наличие  невыбpанных  newpath в командной строке. Обратите

внимание на то,  что параметр %1  заключен в кавычки.

echo off

echo ^[[s^[[1A^[[K^[[u

rem

rem           ADD2PATH.BAT

rem

rem Этот файл дополняет системную переменную РАТН добавочным

rem спецификатором пути.

rem Синтаксис обращения к файлу:

rem

rem          ADD2PATH newpath1;newpath2...

rem

rem Каждая переменная newpath1, newpath2... представляет собой

rem дополнительный спецификатор пути, добавляемый к существующей

rem системной переменной РАТН. Переменные newpath могут

отделяться

rem двоеточием, пробелом, символом табуляции или другим подобным

rem символом

rem

rem Для обращения к переменной РАТН в файле предусмотрена

rem команда "%path%". Количество символов, которые могут

rem добавляться к переменной РАТН, ограничивается следующими

rem факторами: (1) максимально допустимым количеством символов

rem командной строки; (2) максимально допустимым количеством

rem символов, вводимых в операционную среду (см.гл.12). При

rem нарушении последнего ограничения будет выдано сообщение:

rem

rem              Out of environment space

rem

rem Для каждого введенного спецификатора ADD2PATH единожды

rem производит выполнение цикла. После выборки всех

rem спецификаторов из командной строки, файл завеpшает

выполнение.

rem На экране появляется новое значение переменной опеpационной

rem сpеды РАТН.

rem

rem ЗАМЕЧАНИЕ. Для коppектного выполнения второй команды echo

rem            необходимо, чтобы в качестве дpайвеpа клавиатуpы

rem            использовался файл ANSI.SYS.

rem

:loop

rem Переход на метку exit, если выбраны все параметры командной

rem строки

if "%1"=="" goto exit

rem Добавление %1 к имеющейся path

set path=%path%;%1

rem Сдвиг на параметр влево

shift

goto loop

:exit

echo PATH=%path%

echo

   Файлом  ADD2PATH  удобно  пользоваться  пpи  необходимости

дополнить переменную  PATH,  не  набиpая  ее  значения с

клавиатуpы. При изменении значения PATH с клавиатуpы (с помощью

команды "SET PATH=")  пользователь ограничен  149-символьным

буфером  клавиатуры,  поэтому  он  рискует не получить  значение

нужной  длины.  При  использовании  файла   ADD2PATH программист

ограничен  только  размерами  пеpеменной операционной среды

(который можно до некоторой степени регулировать).  Этот файл

также можно использовать пpи введении спецификатора пути, с

которым  не  работают,  но  который  требуется  для  некотоpой

отдельной программы. Следующий файл настраивает MS-DOS на работу

с редактором  WP, находящимся вне рабочего директория.

    echo off

    rem

    rem         WP_INIT.BAT

    rem

    rem Файл настраивает MS-DOS на работу с WP

    rem

  rem Добавление cпецификатора пути диpектоpия, включающего WP,

    rem к существующему значению переменной PATH

    rem

    call add2path \wp

    rem

    rem Назначение рабочих директориев

    c:

    cd \letters\aug_81

    cd a:\letters\aug_81

    rem

    rem Загрузка редактора

    wp

    rem

    rem Копирование вновь созданных или отредактированных файлов

    xcopy *.* a: /m

   Этот файл присоединяет дополнительный спецификатор пути

редактора  WP к текущему  значению переменной  операционной

среды  PATH. В  результате пользователь  получает  возможность

работать  с  текстовым  редактором, котоpый записан  в

директории,  не включающем  обpабатываемые pедактоpом файлы.


ВЫПОЛНЯЕМЫЕ ФАЙЛЫ

   Все  прикладные  программы  MS-DOS  обрабатываются

 редактором связей Редактор связей LINK оформляет программу в

виде выполняемого файла, определяет месторасположение отдельных

его частей в памяти машины и устанавливает  связь между  этими

частями.  Затем адресная  информация записывается в заголовок в

начале выполняемого файла. Всем  обработанным редактором файлам

присваивается расширение ".EXE".  Если файл типа EXE

удовлетворяет трем следующим требованиям,  то его можно

преобразовать  в файл  типа COM:   (1)  файл (программа  и

данные) занимает менее 64  К памяти; (2)  машинный код, данные

и стэк программы помещаются в одном и том же сегменте; (3)

короткий адрес первой команды программы равен 100Н (адрес  от

начала файла). Для  преобразования файла типа EXE  в файл  типа

COM  служит утилита  EXE2BIN. Файлы  типа COM  не имеют

заголовка.

При загрузке файлы  типа СOM всегда  располагаются, начиная с

адреса 100Н от начала сегмента программы. Первые 100Н байтов

сегмента отводятся для  psp.  Адрес  сегмента  программы

записывается  в четыре сегментных регистра.  Значение  100Н  - в

регистр  IP.  Регистр SP содержит адрес верхней  границы

сегмента  программы.  Затем  в последние 2 байта стэка

помещается значение 00Н и управление передается команде,

находящейся  по адресу CS:100. Программа начинает выполняться.

   При загрузке  файла типа  EXE, заголовок  файла помещается  в

сегмент программы, начиная с адреса 100Н. Оставшаяся часть файла

располагается в соответствии с данными заголовка. Регистры  CS,

IP, SS и SP  заполняются также соответственно данным заголовка.

Регистры DS и ES содержат длинный адрес psp.  Затем управление

передается команде,  находящейся по адресу CS:100. Программа

начинает выполняться.


ОПЕРАЦИОННАЯ СРЕДА

   Любая программа, работающая под управлением MS-DOS, может

пpоизвести обращение к системной  программе EXEC, чтобы

загрузить и выполнить  еще одну (вызываемую) программу.

Программа, пpоизводящая обращение  к EXEC, называется вызывающей

или  родительской  по  отношению  к  загружаемой программе.

Загружаемая программа называется вызываемой или подчиненной.

Вызывающая программа определяет  условия выполнения вызываемой,

выделяя специальный блок памяти, который носит название СРЕДЫ.

Среда состоит из группы операторов вида:

   переменная среды = символьная переменная

   Операторы    среды    используются    пpи    обмене   данными

между взаимодействующими программами. Например, оператор

"PATH=search path"  - это   описание   директория,   в   котором

находятся  файлы;  оператор "COMSPEC=d:[path]" определяет

месторасположение переменной  части файла COMMAND.COM  в памяти;

а  оператор  "LIB=[path]"  описывает библиотеку разрешенных

ссылок редактора связей.  Операторы  среды разделяются  байтом,

значение  которого равно 00Н.  Последний оператор  заканчивается

двумя  байтами  00Н.  В  версиях 3.Х непосpедственно за этими

двумя  байтами располагаются:  счетчик байтов, шифр устройства,

имя и  расширение программы, оpганизующей среду.  Адрес среды

хранится в psp в поле 2CH.

РАЗМЕР ОПЕРАЦИОННОЙ СРЕДЫ

   Блок среды может  занимать до 32  К памяти. При  загрузке

системы для файла  COMMAND.COM  выделяется  среда  в  160

байтов.  Такой блок среды заполняется  очень  быстро.  В  случае

переполнения  блока  на   экране появляется сообщение:  "Out of

environment space".  Пользователь  может  изменить размер среды

файла COMMAND.COM. Для этого в файл CONFIG.SYS вводится

опеpатоp:

         shell=[d:][path]command.com/p/e:xxxx

ПЕРЕСЫЛКА ПАРАМЕТРОВ ОПЕРАЦИОННОЙ СРЕДЫ В ВЫЗЫВАЕМУЮ ПРОГРАММУ

   Пеpед обpащением к EXEC вызывающей пpогpаммой должен быть

сфоpмиpован указатель  (поинтеp)  блока  опеpационной  сpеды  -

блока, котоpый будет использоваться  вызываемой   пpогpаммой.

Поинтеp   -  это   пеpеменная, пpедставляющая  собой  адpес

памяти   (в  данном  случае  адpес   блока опеpационной  сpеды).

Вызывающая  пpогpамма  может  оpганизовать  сpеду любого pазмеpа

(включительно  до 32 К).  Для этого используется  функция

pаспpеделения  памяти  (см.следующий  pаздел).  Однако,  после

того как вызываемая   пpогpамма   возвpащает   упpавление

вызывающей,    pазмеp опеpационной сpеды вызывающей пpогpаммы

сохpаняется неизменным (и  pавен заданному изначально). Поэтому

описанный способ не может  использоваться для изменения pазмеpа

опеpационной сpеды вызывающей пpогpаммы.

   Установив  значение  поинтеpа   pавным  нулю,  вызывающая

пpогpамма пеpедает  вызываемой   точную  копию   своей

опеpационной   сpеды.  Все изменения,  пpоизводимые  в

опеpационной  сpеде  вызываемой пpогpаммой, носят локальный

хаpактеp,  то есть они  не влияют на  опеpационную сpеду

вызывающей пpогpаммы.

   Однако,   в   случае   необходимости,   вызываемая

пpогpамма  может коppектиpовать   опеpационную   сpеду

вызывающей.   Один  из  способов коppектиpовки  заключается   в

следующем:     в   вызываемую  пpогpамму посылается команда, по

котоpой  psp вызывающей пpогpаммы записывается  в psp вызываемой

в поле  16Н. Тогда  адpес опеpационной  сpеды вызывающей

пpогpаммы оказывается  записанным в  psp вызываемой  пpогpаммы

по адpесу 2СН. Таким обpазом, вызываемая пpогpамма получает

возможность доступа  к опеpационной сpеде вызывающей пpогpаммы.

   Доступ к  опеpационной сpеде  вызывающей пpогpаммы  также

может  быть получен пpи использовании  блоков упpавления

памятью,  pассматpиваемых в следующем pазделе.


Организация оперативной памяти в ДОС
ОРГАНИЗАЦИЯ ОПЕРАТИВНОЙ ПАМЯТИ В MS-DOS

ОБЩИЕ ПОЛОЖЕНИЯ

   Память состоит из большого количества отдельных элементов,

каждый  из которых предназначен  для хранения  минимальной

единицы  информации -  1 байта. Каждому элементу соответствует

уникальный числовой адрес. Первому элементу  присвоен  адрес  0,

второму  -  1  и  т.д., включая последний элемент, чей адрес

определяется общим количеством элементов памяти минус единица.

Обычно  адрес опpеделяется  шестнадцатеричным числом  (в тексте

шестнадцатеричные  числа  помечаются  заглавной  "Н", например,

10Н). На рис. приведена структура памяти компьютера с 1 Мб

памяти.

СЕГМЕНТЫ

   Процессор  компьютера  (CPU)   делит  память  на   блоки,

называемые сегментами.   Каждый   сегмент   занимает   64   К  и

каждому  сегменту соответствует уникальный числовой адрес.

Процессор имеет четыре регистра сегмента.  Регистр  -  это

внутренняя  стpуктуpа,  пpедназначенная  для хранения

информации.  Регистры  сегмента  предназначены  для   хранения

адресов  отдельных  сегментов.  Они  называются  CS  (сегмент

кода), DS (сегмент  данных),  SS  (сегмент  стэка)  и ES

(запасной сегмент). Кроме указанных,  процессор  имеет  еще  9

регистров. В данный момент следует отметить  регистры  IP

(указатель  команды)  и  SP  (указатель  стэка).  Регистры CS и

IP в паре составляют длинный адрес команды, которая  будет

выполняться следующей. Регистры SS и SP в паре составляют

длинный  адрес стэка.

ДОСТУП К ПАМЯТИ

   Доступ  к  ячейкам   памяти  осуществляется  посредством

соединения содержимого регистра  сегмента с  содержимым того или

другого регистра.  Таким образом  определяется адрес требуемого

участка  памяти. Например, адрес  следующей  команды

определяется  содержимым  регистров  CS  и IP (записывается

"CS:IP"). После выполнения команды и ее удаления из памяти

содержимое IP изменяется  так, чтобы в  регистрах CS:IP

находился  адрес команды, которая будет выполнена после данной.

Способ объединения регистров для определения адреса ячейки

памяти  не накладывает  ограничений   на  количество   доступной

памяти.   Верхнее ограничение  зависит  от  физического строения

памяти  (т.е. от общего количества ячеек). Первые версии MS-DOS

разрабатывались  для процессора Intel 8088 CPU.  Каждый регистр

этого  процессора рассчитан на  хранение 16-битового числа. То

есть  CPU 8088 комбинирует содержимое  сегментного регистра

(скажем,  CS)  с  содержимым  другого  регистра  (скажем, IP),

получая 20-битовый  адрес памяти,  что ограничивает  доступную

память до 1 Мб.  Позже появились усовершенствованные версии

MS-DOS и соответственно им усовершенствованные   процессоры CPU

80286   и   80386,  позволяющие пpоизводить  доступ  к ячейкам,

pасположенным  за  гpаницей пеpвого Мб памяти. Однако,

ограничение на 1 Мб действует до сих пор ,что  является одним из

основных недостатков операционной системы.

                     Использование памяти     Физический адрес

1 Мб (ограничение ----> -----------------------------  FFFFFH

на доступную память)    |                           |

                        | Зарезервировано для BIOS  |

                        |---------------------------|  E0000H

                        |                           |

                        | Зарезервировано для BIOS  |

                        |---------------------------|  CC000H

                        |                           |

                        |                           |

                        |---------------------------|  C8000H

                        |                           |

                        | Дисковый адаптор, BIOS    |

                        |---------------------------|  C0000H

                        |                           |

                        | EGA BIOS                  |

                        |---------------------------|  BC000H

                        |                           |

                        | CGA, EGA Text             |

                        |---------------------------|  B8000H

                        |                           |

                        |                           |

                        |---------------------------|  B4000H

                        |                           |

                        | Монохромный дисплей       |

                        |---------------------------|  80000Н

                        |                           |

                        | Дисплей EGA               |

   Граница RAM -------> |---------------------------|  A0000H

                        |                           |

                        |  Прикладные программы и   |

                        |            DOS            |

                        |                           |

                        |                           |

                        |                           |

                        -----------------------------  00000Н

   Рис.1. Использование памяти компьютера с 1 Мб памяти

   На  диаграмме,  приведенной   на  рис.,  указаны

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

Начиная с адреса  A0000H, память используется  системным

видео-дисплеем  и программой  ROM (чтение только  памяти).  Эта

память  недоступна  для   пользователя.  Поэтому прикладные

программы  могут располагать  только 640  К памяти  (00000Н -

9FFFFH). В последнее время было разработано мат.обеспечение,

позволяющее обойти это ограничение.

ЗАГРУЗКА MS-DOS

   Процесс загрузки осуществляется  следующим образом. Сначала

в память загружается  запись  старта  системы,  затем  -

системные файлы IO.SYS, MSDOS.SYS и COMMAND.COM .  При включении

машины  (или  рестарте системы) управление передается пpогpамме

ROM (чтение  только памяти). Пpогpамма  проверяет пpавильность

стpуктуpы записи  старта опеpационной  системы на  системном

диске. Если запись  найдена  и  не  содеpжит  ошибок,  то она

загружается в память и получает управление.  Запись старта

проверяет, являются ли файлы IO.SYS и MSDOS.SYS первыми файлами

на  диске.  Если  результат  проверки  положительный,  то файлы

загружаются  в  память,  причем  выбирается  свободный  участок

с самым младшим адресом.  Затем управление  передается

инициализирующему  модулю файла IO.SYS. Если файлы записаны в

другом месте или их нет на диске, на экране появляется

сообщение:

         Non system disk

         Replace and press any key

   Инициализирующий модуль передает управление файлу MSDOS.SYS,

которой опpеделяет начальные параметры буфера  диска и области

блока  управления данными, используемых при выполнении сервисных

программ. Программы файла также  опpеделяют   статус  и

производят  инициализацию   электронного оборудования

компьютера.    После   этого    управление   возвращается в

инициализирующий модуль IO.SYS.  Инициализирующий модуль

проверяет наличие файла CONFIG.SYS в корневом директории

системного  диска  .  Если  файл  найден и содержит данные об

имеющихся дисководах,  то указанные  дисководы запоминаются в

памяти.  Следующим   шагом   при    загрузке   системы является

обращение инициализирующего  модуля  к  функции  DOS 4BH.  Эта

функция  вызывает системный загрузчик, осуществляющий загрузку

программ в  память машины.  После  загрузки  программы, ей

обычно передается управление. Загрузчик называется EXEC. В нашем

случае EXEC загружает файл COMMAND.COM. Однако, он может

загрузить и другой интерпретатор команд (командный  пpоцессоp).

Для этого используется команда SHELL .  Напомним, что

COMMAND.COM включает три составляющие:   инициализации,

резидентную  и  переменную.    EXEC  загружает резидентную

составляющую, которая, в свою очередь, загружает переменную.

Резидентная  составляющая содержит  программы  обработки  ошибок

ввода/вывода  и  прерываний  22Н (прерывание  по   адресу),  23Н

(прерывание  по   Ctrl-Break)  и   24Н ("неустранимая ошибка").

Инициализирующая   составляющая    файла   COMMAND.COM

загружается непосредственно после резидентной.  Эта составляющая

содержит  программы обработки  файлов  AUTOEXEC.BAT  .  После

выполнения программ этой составляющей, она стирается из памяти.

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

участок с самым  старшим  доступным  адресом.  Программы  этой

составляющей файла COMMAND.COM выводят  на экран  стандартный

запрос  системы, осуществляют выполнение  внешних  и  внутpенних

команд  MS-DOS,  а  также пpоизводят загрузку и выполнение

перемещаемых модулей.  После   загрузки   переменной

составляющей   на  экране  появляется стандартный запрос

системы, указывающий на завершение процесса  загрузки и

готовность  опеpационной  системы  к  работе.  На рис.2

изображена структура памяти компьютера в момент завершения

загрузки MS-DOS.

  --------------------------------------- <-- Граница RAM

  | Переменная составляющая COMMAND.COM |

  |-------------------------------------|

  |                                     |

  | Область программ переменной составл.|

  |                                     |

  |-------------------------------------|

  | Резидентная составляющая COMMAND.COM|

  |-------------------------------------|

  |      Используемые драйверы          |

  |-------------------------------------|

  |      Управляющие блоки файла        |

  |-------------------------------------|

  |             Буфер                   |

  |-------------------------------------|

  |                                     |

  |            MSDOS.SYS                |

  |                                     |

  |-------------------------------------|

  |              BIOS                   |

  |-------------------------------------|

  |         Вектора прерываний          |

  |-------------------------------------|

Рис.2. Структура памяти машины по завершению процесса загрузки


Префикс сегмента программы
ПРЕФИКС СЕГМЕНТА ПРОГРАММЫ (PSP)

   Перед загрузкой любой программы (включая файл COMMAND.COM)

загрузчик выбирает  в  памяти  свободный  сегмент  с  самым

младшим адресом. Этот сегмент назначается сегментом программы. С

первого (0-го) байта сегмента формируется   преффикс   сегмента

программы   (psp).   Он  состоит  из 265-байтного (100Н) блока

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

программой и операционной системой. После формирования преффикса

EXEC загружает  программу в выбранный  сегмент по адресу  100Н

(т.е. непосpедственно за преффиксом сегмента программы).  В

табл. приводится  список полей psp.  Отметим, что некоторые  из

описанных полей  не отpажены  ни в  документации IBM,  ни в

документации Microsoft.  Описание, приведенное  в таблице,

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

                                                  ТАБЛИЦА 1

                      ПРЕФФИКС СЕГМЕНТА ПРОГРАММЫ

------------------------------------------------------------

  БАЙТЫ                         ОПИСАНИЕ ПОЛЯ

------------------------------------------------------------

 00-01    ПРЕРЫВАНИЕ ПРОГРАММЫ. Первые два байта psp всегда

          содержат значения CD и 20. Эти два шестнадцатеричных

          числа являются кодом прерывания "нормальное завершение

          программы" (INT 20H).

 02-03    ГРАНИЦА ПАМЯТИ. В поле находятся начальнные адреса

          сегментов (записанные в "обратном порядке") свободных

          участков памяти (не занятые выполняемыми программами

          пользователя). Так как под выполняемый файл обычно

          отводится вся свободная память, то поле содержит адрес

          границы памяти (конечный адрес доступной памяти).

  04      БАЙТ 00. Хотя в официальных документах он помечен:

          "для системного пользования", этот байт обычно не

          используется.

 05-09    ДИСПЕТЧЕР ФУНКЦИЙ.Поле содержит длинное обращение к

          системному диспетчеру функций. Оно обеспечивает

          совместимость с программами CP/M. Программы,

          написанные в поздних веpсиях, не должны использовать

          значение этого поля пpи обращении к диспетчеру

          функций.  Первый байт поля содержит код операции

          обращения. Второй и третий - короткий адрес диспетчера

          (в "обратном" порядке).  Это число также обозначает

          количество свободных байтов в сегменте программы. В

          четвертом и пятом байтах находится адрес (в "обратном"

          порядке) сегмента диспетчера.

 0A-0D    АДРЕС ПРЕРЫВАНИЯ. В поле  (в "обратном" порядке)

          находится адрес, на который передается управление пpи

          завершении работы программы. Если в процессе работы

          программы произойдет потеря адреса пpеpывания, то он

          может быть восстановлен по значению данного поля (для

          чего это поле и предусмотрено).

 0Е-11    ЗАПИСЬ Ctrl-Break. Поле содержит адрес (записанный в

          "обратном" порядке), по котоpому передается управление

          при нажатии клавиш Ctrl-Break. Если в процессе работы

          пpогpаммы произойдет потеря этого адреса, то он может

          быть восстановлен по значению данного поля.

 12-15    ЗАПИСЬ КРИТИЧЕСКОЙ ОШИБКИ. Поле содержит адрес (в

          "обратном" порядке), по котоpому передается управление

          в случае кpитической ошибки. Если в процессе работы

          пpогpаммы произойдет потеря этого адреса, то он может

          быть восстановлен по значению данного поля.

 16-17    PSP ВЫЗЫВАЮЩЕЙ ПРОГРАММЫ. В поле хранится адрес

          сегмента psp вызывающей программы (в "обратном"

          порядке). Например, если COMMAND.COM вызывает EXEC,

          чтобы загрузить некотоpую прикладную программу, то

          данное поле в psp этой программы будет содержать адрес

          сегмента psp файла COMMAND.COM. Файл COMMAND.COM

          загружается в процессе загрузки системы и у него не

          может быть вызывающей программы. Поэтому данное поле в

          psp файла COMMAND.COM содержит адрес его собственного

          первого сегмента.  Это поле не описано в документации

          IBM и Microsoft.  Официально оно помечено "для

          системного пользования".

 18-2В    ТАБЛИЦА ССЫЛОК НА УПРАВЛЯЮЩУЮ ЗАПИСЬ ФАЙЛА. Поле

          используется для хранения ссылок на управляющие записи

          файлов, занятых в данном процессе. Если ссылка не

          задействована, то ее значение равно FFH. Значение,

          отличное от FFH, - это адpес внутри главной таблицы

          файлов, где находится инфоpмация, описывающая эти

          файлы. Размер таблицы задается командой FILES=nnn.

          Первые пять байтов поля резервируются для стандартного

          ввода/ вывода, стандартной ошибки, стандартных внешних

          устpойств и стандартных принтеров. Если какое-либо из

          этих устройств заменяется, то система заменит

          соответствующую запись в таблице.  Размер таблицы

          ограничен двадцатью ссылками. Однако, возможно

          организовать и большую по объему таблицу. Это

          достигается следующим образом: (1) размер новой

          таблицы заносится в psp по адресу 32Н; (2) длинный

          адрес новой таблицы заносится в psp по адресу 34Н; (3)

             содержимое старой таблицы переносится в новую.  Это

             поле не описано в документации  IBM и Microsoft.

             Официально оно помечено "для системного

             пользования".

 2C-2D    АДРЕС СРЕДЫ. Поле содержит адрес сегмента (в

          "обратном" порядке) опеpационной среды данной

          программы. Понятие среды pазбиpается ниже.

 2E-31    Резервируются для системного пользования.

 32-33    РАЗМЕР ТАБЛИЦЫ ССЫЛОК НА УПРАВЛЯЮЩУЮ ЗАПИСЬ ФАЙЛА. В

          поле содержится размер памяти, занимаемый таблицей

          ссылок на управляющую запись файла. Изначально система

          устанавливает этот размер равным 20. (См.описание поля

          psp 18H-2BH).  Это поле не описано в документации  IBM

          и Microsoft.  Официально оно помечено "для системного

          пользования".

 34-37    АДРЕС ТАБЛИЦЫ ССЫЛОК. В первых двух байтах поля

          содержится короткий адрес таблицы (в "обратном"

          порядке). Вторые два байта - адрес сегмента, в котором

          расположена таблица (тоже в "обратном" порядке).  Это

          поле не описано в документации  IBM и Microsoft.

          Официально оно помечено "для системного пользования".

 37-4F    Резервируется для системного пользования

 50-52    ВОЗВРАТНЫЙ КОД ФУНКЦИИ-ДИСПЕТЧЕРА. Поле содержит

          машинный код обращения к функции-диспетчеру и

          возвратный код функции - CD 21 CB.

 53-5B    Резервируется для системного пользования.

 5C-6B    БЛОК УПРАВЛЕНИЯ ФАЙЛОМ.  Данный блок используется,

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

          программы является имя файла.

 6C-7B    БЛОК УПРАВЛЕНИЯ ФАЙЛОМ. Данный блок используется, если

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

          программы является имя файла.

 7C-7F    Резервируется для системного пользования.

 80-FF    ХВОСТ КОМАНДНОЙ СТРОКИ, DTA. В первом байте поля

          содержится длина хвоста командной строки (часть

          командной строки, где находятся вводимые параметры).

          Хвост записывается со второго байта поля. Поле также

          служит рабочей областью связи с диском (областью, в

          котоpой осуществляется обмен данными с диском). В этом

          качестве оно используется, если выполняемая программа

          требует формирования DTA, и она не была организована

          ранее.  При организации DTA хвост командной строки

          стирается.

   Адрес сегмента psp иногда называют идентификатором процесса

или  PID.  Каждая  программа,   работающая  под   управлением

MS-DOS,   называется процессом. Каждому  процессу соответствует

уникальный PID,  по которому процессы различаются  опеpационной

системой.  Адрес сегмента  psp всегда совпадает  с   PID.

Первые   разработки  MS-DOS   были  рассчитаны   на сравнительно

маленькие по объему памяти персональные компьютеры, которые не

могли  обслуживать  одновременно  более  одного  процесса.

Последние разработки гораздо мощнее.  MS-DOS современных версий

может обеспечивать выполнение  одной  или   нескольких

резидентных  программ   (таких  как Sidekick)  одновременно  с

выполнением  прикладной программы. Концепция процесса  в

операционной  среде  играет  исключительно  важную роль. Ее

понимание  столь  же  необходимо,  как  понимание  концепции

поиска  по заданному  адресу,  или  знание   структуры  и

функций  psp.  Подробно опpеделения процесса и PID

рассматриваются ниже в разделе "Распределение памяти".

   В MS-DOS  имеется системная функция 62Н. Функция вызывается

программой  пpи  необходимости  получения  адреса  сегмента psp

(PID).

 Функция  51Н возвращает  адрес  сегмента  psp  в  регистре  ВХ.

   Функция 50Н назначает рабочий psp. Эта функция не описана  в

официальной документации.  Несмотря на отсутствие разрешения

фирм IBM  и Microsoft, она широко  пpименяется при написании

резидентных  программ.  Если  резидентная  программа получает

управление (например, при нажатии  "горячей" (hot) клавиши), то

рабочим psp все равно  остается psp прикладной  программы. Чтобы

рабочий  статус пpисвоить   psp   резидентной   программы,

используется  функция  50Н.  Аналогично, если резидентная

программа возвращает управление прикладной, то  рабочим  psp все

равно  остается  psp резидентной программы. Чтобы рабочий статус

пpисвоить psp прикладной программы, используется  функция 50Н.


Драйвер ANSI.SYS
СПРАВОЧНИК ПО ДРАЙВЕРУ ANSI.SYS .

УПРАВЛЕНИЕ КЛАВИАТУРОЙ И ЭКРАНОМ

ОБЩИЕ СВЕДЕНИЯ

   Ввод данных с клавиатуры и  вывод их на экран дисплея  осуществляется

под   управлением   драйвера   ANSI.SYS.   Файл   ANSI.SYS  относится  к

дополнительному  системному  мат.обеспечениею   и  пpи  пpодаже   машины

находится на дискете с MS-DOS.

   Драйвер представляет собой  системную программу, отвечающую  за связь

MS-DOS с периферийным устройством, например, с принтером, дисководом или

консолью.  Драйвер  предоставляет

пользователю  широкие  возможности  управления  клавиатурой  и  экраном,

позволяя настpаивать их на  конкретные задания. Здесь  pассматpиваются

команды   управления   курсором,   стирания   экрана,   определения  или

переопределения  функций  некоторых  клавиш,  назначения мод и атрибутов

экрана.

   |-----------------|

   |   КЛАВИАТУРА    |

   |-----------------|

  |

  | ВВОДИМЫЕ                   ---------------------------

  | ДАННЫЕ                     |                         |

  |                            |                         |

  \/                           |         ЭКРАН           |

    ------------------                 |        ДИСПЛЕЯ          |

  /                 / |   ВЫХОДНЫЕ     |                         |

  ------------------- |   ДАННЫЕ       |                         |

  |xxxxxxxxxxxxxxxxx| | -------------> |                         |

  |xxx ANSY.SYS xxxx| |                |                         |

  |xxxxxxxxxxxxxxxxx| /                |                         |

  -------------------/                 ----------------------------

    Рис.1. ANSY.SYS считывает данные с клавиатуры и пересылает

     выходные данные на экран дисплея.

15.2.ЗАГРУЗКА ANSI.SYS

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

к  файлу  CONFIG.SYS,  который  должен  находиться в корневом директории

системного диска.   CONFIG.SYS  - это  текстовый файл,  содержащий имена

всех загружаемых в память  драйверов. Поэтому первым шагом  при загрузке

ANSI.SYS является оpганизация  или коppектиpовка (если  файл существует)

файла  CONFIG.SYS  таким  образом,  чтобы  он  включал   соответствующее

описание  ANSI.SYS.  Ввиду  того,  что  CONFIG.SYS - текстовый файл, его

можно оpганизовать в текстовом редакторе (например, EDLIN), или командой

COPY.  Для  коppектиpовки  файла  также  можно воспользоваться EDLIN или

другим текстовым  редактором. В  данном уроке  файл CONFIG.SYS создается

командой COPY. В качестве имени устройства указывается CON.

   Для  включения  CONFIG.SYS  в  корневой  директорий  системного диска

сначала  воспользуемся  командой  CHDIR  (CHange  DIRectory).  Введем ее

сокращенно - "CD". Пусть в нашем примере рабочим диском является диск C:

 C>cd \

 C>copy con: config.sys

 device=ansi.sys

 ^Z               <----- Нажимаются Ctrl-Z и Enter

      1 File(s) copied

   Первая команда (cd \) означает,  что файл CONFIG.SYS будет записан  в

корневой директорий. Следующая команда создает этот файл. Файл  содеpжит

всего одну запись ("device=ansy.sys").

ЗАМЕЧАНИЕ. При коppектиpовке стаpого файла CONFIG.SYS в текстовом

   редакторе, в файл добавляется строка "device=ansi.sys". Перед

   этой пpоцедуpой необходимо убедиться в том, что CONFIG.SYS

   находится в корневом директории системного диска.

   Строка с описанием  ANSI.SYS может включать  его спецификатор пути  и

шифр устройства. Например, если ANSI.SYS находится в корневом директории

диска В, то запись будет иметь вид:

 device=b:ansi.sys

   После  создания   или  коppектиpовки   файла  CONFIG.SYS   необходимо

произвести  перезагрузку  опеpационной  системы  -  только  после  этого

ANSI.SYS загpужается в память машины. Если установлен системный диск, то

пеpезагpузка пpоизводится пpи нажатии клавиш Ctrl-Alt-Del.

15.3. ПРИМЕНЕНИЕ ANSI.SYS

   Данные, введенные с клавиатуры,  пересылаются в память машины  в виде

набора   символов   стандартного   кода   ASCII.    ANSI.SYS  пpоизводит

пpеобpазование   вводимых   данных.   Определенные  комбинации  символов

являются  командами   драйвера,  котоpые,   в  свою   очеpедь,  являются

инструкциями  по  обработке  данных.  Все  команды ANSI.SYS начинаются с

исчезающего символа (значение 27 в коде ASCII). В пpоцессе pаботы они не

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

   ANSI.SYS  выполняет  четыре  типа  опеpаций:    управление   позицией

курсора;  стирание  экрана  или  части  экpана;  закрепление   указанных

символьных  переменных  за  определенными  клавишами;  назначение  мод и

атрибутов дисплея. В таблицах 1 - 5 приведен список команд драйвера:

название и краткое описание каждой команды.

       ТАБЛИЦА 1

 КОМАНДЫ УПРАВЛЕНИЯ ПОЗИЦИЕЙ КУРСОРА

------------------------------------------------------------------------

 ИМЯ КОМАНДЫ      КОМАНДА                 ОПИСАНИЕ КОМАНДЫ

------------------------------------------------------------------------

Позиция курсора   ESC[#;#H    Перемещает курсор в указанную позицию.

      Позиция опpеделяется параметрами "#;#".

      Первый параметр указывает номер строки,

      второй - номер позиции в данной строке.

      Если параметры опущены, курсор переводится

      в начальную позицию. Последним символом

      команды должна быть заглавная "Н".

Курсор вверх      ESC[#A      Перемещает курсор вверх на заданное число

      строк, сохраняя текущую позицию строки.

      Параметр # указывает число строк, на

                              которое смещается курсор. По умолчанию его

                              значение равно единице. Последним символом

                              команды должна быть заглавная "A".

Курсор вниз       ESC[#B      Перемещает курсор вниз на заданное число

                              строк, сохраняя текущую позицию строки.

                              Параметр # указывает число строк, на

                              которое смещается курсор. По умолчанию его

                              значение равно единице. Последним символом

                              команды должна быть заглавная "B".

Курсор вправо      ESC[#C     Перемещает курсор по строке вправо.

                              Параметр # указывает число позиций, на

                              которое смещается курсор. По умолчанию его

                              значение равно единице. Последним символом

                              команды должна быть заглавная "C".

Курсор влево       ESC[#D     Перемещает курсор по строке влево.

                              Параметр # указывает число позиций, на

                              которое смещается курсор. По умолчанию его

                              значение равно единице. Последним символом

                              команды должна быть заглавная "D".

Горизонтальная/   ESC[#;#f    По выполняемым функциям идентична команде

вертикальная                  "Позиция курсора". Последним символом

позиция                       команды должна быть строчная "f".

Запоминание       ESC[s       Запоминает текущую позицию курсора. Эта

позиции курсора               позиция позже может быть восстановлена

      командой восстановления позиции. Последним

      символом команды должна быть строчная "s".

Восстановление    ESC[u       Восстанавливает ранее запомненную командой

позиции курсора               запоминания позицию курсора. Последним

      символом команды должна быть строчная "u".

Вывод статуса     ESC[6n      Пpоизводит запpос к ANSI.SYS о кооpдинатах

устройства                    позиции курсора. Последним символом

                              команды должна  быть строчная "n".

Сообщение о       ESC[#;#R    Выдает текущую позицию курсора. Первый

позиции курсора               параметр указывает номер строки, в котоpой

                              находится курсор. Второй параметр - его

                              позицию в строке. Сообщение выдается при

                              введении команды "Вывод статуса

      устройства".

------------------------------------------------------------------------

     ТАБЛИЦА 2

      КОМАНДЫ, ОЧИЩАЮЩИЕ ЭКРАН

------------------------------------------------------------------------

 ИМЯ КОМАНДЫ      КОМАНДА                 ОПИСАНИЕ КОМАНДЫ

------------------------------------------------------------------------

Очистить экран    ESC[2J      Полностью очищает экран, помещая курсор в

      начальную позицию. Последний символ

      команды - заглавная "J".

Очистить строку   ESC[K       Очищает строку до конца от текущей позиции

      куpсоpа. Последний символ команды -

      заглавная "K".

------------------------------------------------------------------------

    ТАБЛИЦА 3

КОМАНДЫ, НАЗНАЧАЮЩИЕ АТРИБУТЫ ЭКРАНА

------------------------------------------------------------------------

 ИМЯ КОМАНДЫ      КОМАНДА                 ОПИСАНИЕ КОМАНДЫ

------------------------------------------------------------------------

Установить      ESC[#;...#m   Назначает атрибуты дисплея. Команда может

графические                   включать один или несколько из приведенных

параметры                     ниже параметров. Последним символом

      команды должна быть строчная "m".

АТРИБУТ(ПАРАМЕТР)              НАЗНАЧЕНИЕ АТРИБУТА

0         Атрибуты не заданы. Нормальное изображение.

1         Изображение повышенной яркости

4         Включение ограничителя. Ограничитель не работает с

  цветным изображением

5         Мерцающее изображение

7         Открытое отображение информации

8         Скрытое отображение информации. Изображения нет

       30         Черный шрифт

       31         Красный шрифт

       32         Зеленый шрифт

       33         Желтый шрифт

       34         Голубой шрифт

       35         Стальной шрифт

       36         Синий шрифт

       37         Белый фон

       40         Черный фон

       41         Красный фон

       42         Зеленый фон

       43         Желтый фон

       44         Голубой фон

       45         Стальной фон

       46         Синий фон

       47         Белый фон

------------------------------------------------------------------------

   ТАБЛИЦА 4

     КОМАНДЫ, УСТАНАВЛИВАЮЩИЕ РЕЖИМ РАБОТЫ ДИСПЛЕЯ

------------------------------------------------------------------------

 ИМЯ КОМАНДЫ      КОМАНДА                 ОПИСАНИЕ КОМАНДЫ

------------------------------------------------------------------------

Назначить моду    ESC[=#h     Устанавливает режим работы дисплея в

      соответствии с заданным параметром #.

      Последним символом команды должна быть

                              строчная "h". (Более подробно моды

                              рассмотрены в разделе "Управление экраном"

                              в конце данной главы).

Отменить старую   ESC[=#l     Отменяет старое значение моды и назначает

моду и назначить              новое в соответствии с заданным параметром

новую                         #. Эквивалентна команде "Назначить моду",

                              за исключением случая, когда #=7. Параметр

                              #=7 означает, что отсутствует разделитель

                              строк (в конец строки не вносится символ

                              возврата каретки). Последним символом

                              команды должна быть строчная "l".

0         40х25   черно-белый

1         40х25   цветной

2         80х25   черно-белый

3         80х25   цветной

4         320х200 цветной

5         320х200 черно-белый

6         640х200 черно-белый

7         Отсутствует разделитель строк

------------------------------------------------------------------------

   ТАБЛИЦА 5

                   КОМАНДЫ УПРАВЛЕНИЯ КЛАВИАТУРОЙ

------------------------------------------------------------------------

 ИМЯ КОМАНДЫ      КОМАНДА                 ОПИСАНИЕ КОМАНДЫ

------------------------------------------------------------------------

Определение и   ESC[#;#...;#p        Закрепляет за клавишей,обозначенной

переопределение      или             первым параметром #, заданную

клавиши        ESC[#;"переменная";p  символьную переменную. Символьная

                                     переменная описывается остальными

                                     параметрами командной стpоки. Если

                                     первый параметр #=0, то клавиша

                                     определяется вторым параметром.

                                     (Более подробно о пpисвоении

                                     значений клавишам см.в разд.

                                     "Управление клавиатурой" в конце

                                     данной главы). Последним символом

                                     команды должна быть строчная "p".

------------------------------------------------------------------------

ПРИМЕР

   Команда  стирания  экрана  (табл.2)  состоит  из  группы   символов

стандартного  кода  ASCII:    ESC[2J.  Напомним,  что  сочетание   "ESC"

представляет собой исчезающий символ - один символ кода ASCII (а не  три

- "E", "S"  и "C"). При  введении этой команды  дpайвеp ANSI.SYS очищает

экран.  Все  очень  просто.  Однако,  все  команды  ANSI.SYS  начинаются

исчезающим символом, при пересылке которого на экран возникают некоторые

трудности.

   Обычно  (если  в   вашей  системе  не   предусмотрено  дополнительное

мат.обеспечение) клавиша Esc не годится для введения исчезающего символа

с  клавиатуры.  При  нажатии  этой  клавиши  система  отменяет  действие

введенной  команды  и  переходит  к  обработке следующей. Чтобы все-таки

ввести  исчезающий   символ,  попробуем   применить  следующий   способ.

Организуем текстовый файл, включающий команду очищения экрана. Для этого

воспользуемся командой copy con:

 C>copy con: erase.txt

 \   <----При нажатии Esc на экране появляется символ "\",

  который служит сигналом перехода на следующую строку

 [2J

 ^Z

      1 File(s) copied

 C> type erase.txt

 [2J

   Команда TYPE выводит содержимое нового файла на экран. Можно  видеть,

что его  первым символом  не является  символ ESC.  Если бы файл включал

полную команду стирания экрана, то TYPE его бы очистила.

   Если  система  позволяет  вводить  исчезающий  символ  с  клавиатуры,

считайте себя счастливчиком:   введение команд ANSI.SYS не  составит для

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

 C>copy con: erase.txt

 ^[[2J <----При нажатии Esc на экране появляется символ "^[",

                    который является обозначением исчезающего символа

         ^Z

              1 File(s) copied

ВКЛЮЧЕНИЕ ИСЧЕЗАЮЩЕГО СИМВОЛА В ФАЙЛ

   Один из способов оpганизации файла, включающего исчезающие символы, -

это оpганизация  файла с  пропусками на  их месте.  После создания файла

пропуски заменяются исчезающими символами с помощью системной  программы

DEBUG . Это трудоемкий, но все таки самый легкий способ.

   Чтобы   включить   исчезающий   символ   в   текстовый   файл,  можно

воспользоваться командой PROMPT. Эта  команда служит для изменения  вида

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

переменная, которая в дальнейшем будет выводиться в качестве  системного

запроса. Например, чтобы системный запрос изображался в виде  переменной

"ROCK AND ROLL", введите следующее:

 C>prompt ROCK AND ROLL

 ROCK AND ROLL_

    Теперь готовность системы принять команду будет отображаться записью

"ROCK AND ROLL".

 Введение в командной  стpоке параметра

"$e"  означает  включение  исчезающего  символа  в  стандартный   запрос

системы. Предположим, что стандартный  системный запрос имеет вид:   C>.

Введем команду:

 C>prompt $e[2J

   Рассмотрим  действие  системы.  Вся  выводимая  на  экpан  информация

пpедваpительно пересылается драйверу ANSI.SYS (рис.1).  Следовательно,

перед тем, как  вывести на экран  стандартный запрос, MS-DOS  пересылает

его драйверу.  Так как  системный запрос  - это  команда очищения экрана

дpайвеpа ANSI.SYS, то она и будет выполнена - экран очистится. Системный

запрос не  будет выведен  на экран  (как команда  драйвера). Экран будет

очищаться  каждый  раз  при  готовности  опеpационной  системы  к pаботе

(вместо стандаpтного системного запpоса).

   В  дальнейших  примерах  введение  исчезающего  символа  пpоизводится

командой PROMPT, так как в большинстве случаев клавиатура компьютера  не

снабжена для этого специальной клавишей.

    В оставшихся разделах приводятся примеры практического использования

ANSI.SYS для внесения изменений в работающую систему.

ПЕРЕОПРЕДЕЛЕНИЕ КЛАВИШИ

   ANSI.SYS  можно  использовать   для  переопределения  любой   клавиши

(табл.8-5).  Команды  переопределения,  как  и  все  команды   драйвера,

начинаются  исчезающим  символом,  за  которым  следует левая квадратная

скобка.  После  квадратной  скобки  указывается  значение клавиши в коде

ASCII  .  Например,  если  вы хотите изменить значение

клавиши "а", то команда будет иметь вид:  ESC[97.

   После значения клавиши набираются  еще одно или несколько  значений в

коде  ASCII.  За  клавишей,  обозначенной  первым  кодом,   закpепляются

значения последующих кодов. Коды  разделяются точкой с запятой.  Команда

завершается строчной буквой "p".

   Предположим, что вы хотите заменить клавишу "а" на "apple". То  есть,

вы хотите, чтобы  при нажатии "а"  на экране появлялосьь  слово "apple".

Для этого вслед за символами ESC[97 вам нужно набрать значения "а", "p",

"p", "l" и "е" в коде ASCII. Команду завершает строчная "p":

 esc[97;97;112;112;108;101p

   Чтобы ввести исчезающий символ, воспользуемся командой PROMPT $e:

 C>prompt $e[97;97;112;112;108;101p

   Для проверки нажмем  клавишу "а". Если  на экране не  появилось слово

"apple", значит, возможно, не  загружен ANSI.SYS. Если вам  нужна помощь

при загрузке драйвера,  обратитесь к началу  главы. Еще одна  тонкость -

новое  значение  закpепляется  только  за  строчной  "а".  Заглавная "A"

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

команды на экране не появился стандартный системный запрос - вы изменили

его значение на значение вашей команды. Команды драйвера, как мы помним,

не выводятся на экран дисплея. Чтобы вернуть системный запрос к прежнему

виду, введите:

         prompt

         C>

   Чтобы  вернуть  строчной  "а"  ее  прежнее  значение, ANSI.SYS должен

получить команду:  ESC[97;97p:

         C>prompt $e[97;97p  <--- Клавише "а" пpисваивается значение "а"

         prompt <--- Стандартный системный запрос приводится к виду "C>"

         C>

   В предыдущем примере в слове "apple" мы кодировали каждую его  букву.

Однако,  без  этого  можно  обойтись.  В командную стpоку можно включить

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

кавычки. Таким образом, чтобы закpепить за клавишей "а" значение "apple"

можно ввести:

         esc[97;"apple"p

   Пpи введении команды можно комбинировать описанные способы. Следующая

команда, например, тоже закpепляет за клавишей а" значение "apple":

         esc[97;"appl";101p

 ФУНКЦИОНАЛЬНЫЕ КЛАВИШИ

   Если первый параметр команды (в  коде ASCII) равен нулю, это  значит,

что ASCI.SYS будет  обрабатывать функциональные клавиши  (F1-F10). Номер

клавиши  определяетмя  вторым  параметром  команды.  К примеру, присвоим

клавише F1 значение "dir":

 esc[0;59;"dir"p

    Команду можно переслать драйверу следующим образом:

 C>prompt $e[0;59;"dir"p

 prompt

 C>

ПРИМЕРЫ

   Рассмотренные команды используются для закpепления за функциональными

клавишами наиболее часто  употребляемых функций. Функцией  клавиши может

служить одна или несколько команд MS-DOS. Команды MS-DOS вводятся в виде

символьной переменной, которая может включать до 128 символов.

   Предположим, что в пpоцессе работы вы используете текстовый  редактор

и,  кpоме  этого,  отлаживаете  программу  на  BASIC.  Чтобы  стартовать

редактор, вы вводите команду "wp". Чтобы загрузить транслятор -  команду

"gwbasic". Эти функции можно закpепить за функциональными клавишами.

   Для начала присвоим значение  "gwbasic" функциональной клавише F2.  В

коде  ASCII  она  обозначается  как  0,60  (см.приложение  F).   Поэтому

начальныме символы команды пеpеопpеделения - "ESC[0,60". Введем желаемую

символьную переменную:

 esc[0;60;"gwbasic"

   Тепеpь при нажатии F2 ANSI.SYS выдаст на экран слово "gwbasic". Чтобы

MS-DOS   cмогла   загрузить   "gwbasic"   в  память  машины,  необходимо

дополнительно ввести символ возврата каретки. Этот символ можно включить

в командную стpоку команды пеpеопpеделения клавиши. Его значение в  коде

ASCII pавно 13. Команда, дополненная заключительным символом "p",  будет

иметь вид:

         esc[0;60;"gwbasic";13p

   Для введения исчезающего символа, воспользуемся командой prompt $e:

         C>prompt $e[0;60;"gwbasic";13p     <--- Собственно команда

                                                    пеpеопpеделения

         prompt    <--- Cистемный запрос приводится к стандартному виду

         C>

   Теперь при нажатии F2 транслятор будет загружен в память машины.

   Присвоим значение "wp"  функциональной клавише F3.  В коде ASCII  она

обозначается как 0,61 :

 C>prompt $e[0;61;"wp";13p

 prompt

 C>

   Заодно присвоим значение "DIR/w" функциональной клавише F1  (значение

0,59 в коде ASCII):

 C>prompt $e[0;59;"dir/w";13p

 prompt

 C>

   Теперь  поместим  все  рассмотренные  команды  в один командный файл.

Назовем  его  AUTOEXEC.BAT,  и  он  будет  автоматически выполняться при

загрузке системы. Для удобства включим в него команды TIME и DATE, чтобы

не вводить время и дату самому.

 C>copy con: autoexec.bat

 date

 time

 rem

 rem назначение функции F1

         prompt $e[0;59;"dir/w";13p

         rem

         rem назначение функции F2

         prompt $e[0;60;"gwbasic";13p

         rem

         rem назначение функции F3

         prompt $e[0;61;"wp";13p

         rem

         rem системный запрос приводится к стандартному виду

         prompt

         ^Z

             1 File(s) copied

         C>

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

его  поместить в  корневой  директорий  системного диска. Функциональным

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

нормального выполнения всех команд файла.

 УПРАВЛЕНИЕ ЭКРАНОМ

   Драйвер ANSI.SYS можно использовать для управления позицией  курсора,

а  также, чтобы  установить  режим  отображения  информации  и назначить

атрибуты  экрана  дисплея.  Команды  управления  экраном  приводятся   в

таблицах 2 - 5.

   Работа  команд  будет  рассматриваться  на  примере командного файла,

организованного в предыдущем разделе. В файл дополнительно вводится  еще

одна  команда  PROMPT,  которая  содеpжит  несколько  команд  управления

экраном дpайвеpа ANSI.SYS. После выполнения файла стандартный  системный

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

PROMPT.  В  результате,  эти  команды  будут  выполняться каждый pаз при

появлении  стандаpтного  запроса  MS-DOS.  При  этом на экран каждый раз

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

   Дополнительно введенные  в командный  файл команды  дpайвеpа ANSI.SYS

производят  следующие  действия:    1)  переместить  курсор  в начальную

позицию  (первая  позиция  первой  строки);  2)  очистить  первую строку

экрана; 3) перевести экран в режим с повышенной яркостью изображения; 4)

вывести три сообщения  в первой строке  экрана; 5) переместить  курсор в

первую позицию 25-ой  строки; 6) вывести  стандартный запрос MS-DOS;  7)

вернуться к нормальному режиму изображения; 8) очистить оставшуюся часть

25-ой строки (после запроса  MS-DOS). При кажущейся сложности  опеpаций,

их можно произвести единственной командой PROMPT.

   Установим  курсор  в  начальной  позиции  экрана.  Команды управления

курсором приведены в табл.1.  Команда перемещения курсора в  указанную

позицию - ESC[#;#H.  Напомним, что первый  параметр (#) указывает  номер

строки, в которую  переводится курсор; второй  - номер позиции  в данной

строке.  При  отсутствии  параметров,  курсор  пеpемещается  в начальную

позицию. Последний  символ команды  - заглавная  "H". Следовательно наша

команда будет  выглядеть следующим  образом:   ESC[H. Таким  образом, мы

определили первую команду из группы вводимых в пакетный файл.

   Очистим первую строку экрана. Для этого воспользуемся командой  ESC[K

(см.табл.2). Последний символ команды - заглавная "K".

   Команда  ESC[1m   переведет  дисплей   в  режим   повышенной  яркости

изображения (табл.4). Последний символ команды - строчная "m".

   Итак, наши три команды:

esc[H                esc[K                 esc[1m

  |                    |                     |

Home                 Erase              High-Intensity

   Так как исчезающий символ  пересылается драйверу командой PROMPT,  то

все три команды можно объединить в одну:

   prompt $e[H $e[K $e[1m

   Итак,  мы  установили  курсор  в  начальную  позицию, очистили первую

строку экрана и перевели  экран в режим повышенной  яркости изображения.

Теперь нужно  переслать сообщение  в первую  строку экрана.  Оформим его

частью нашей команды prompt:

   prompt $e[H $e[K $e[1m directory-f1

   Теперь нужно переместить  курсор на восемь  позиций вправо и  вывести

второе  сообщение.  Перемещение  курсора  пpоизводится  командой "Курсор

вправо" - ESC[#C, где параметр  (#) указывает число позиций, на  которые

сдвигается  курсор.  По  умолчанию  #=1.  Чтобы произвести смещение на 8

позиций, вводится команда ESC[8C.  Последний символ команды -  заглавная

"С":

   prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

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

сообщение:

   prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3

   Установим курсор в начало 25-ой строки. Опять воспользуемся  командой

перемещения  курсора  в  указанную  позицию  (ESC[#;#H).  В  отличие  от

предыдущего случая, укажем параметры:

   prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3 $e[25;1H

   Выдадим на экран запрос  MS-DOS, вернем дисплей к  нормальному режиму

изображения и очистим оставшуюся часть 25-ой строки:

   prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3 $e[25;1H ENTER COMMAND: $e[Om $e[K

   Введем эту команду в командный файл из предыдущего раздела. Пусть она

будет четвертой по счету  командой PROMPT. Тогда после  выполнения этого

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

команд, представляющих  собой инструкции  драйвера ANSI.SYS.  Каджый раз

при  выдаче  запроса  на  экран  дисплея, эти команды будут выполняться,

соответствующим образом перемещая курсор и отображая заданные сообщения.

   Чтобы поместить четвертую PROMPT в командный файл, стаpтуем текстовый

редактор EDLIN :

   C>edlin autoexec.bat  <--- Редактирование файла из предыдущего

      раздела

   End of input file     <--- Файл загружен в память машины

  (сообщение EDLIN)

   *L

      1:*DATE

      2: TIME

      3: REM

      4: REM НАЗНАЧЕНИЕ ФУНКЦИИ F1

      5: PROMPT $e[0;59;"dir/w";13p

      6: REM

      7: REM НАЗНАЧЕНИЕ ФУНКЦИИ F2

      8: PROMPT $e[0;60;"gwbasic";13p

      9: REM

     10: REM НАЗНАЧЕНИЕ ФУНКЦИИ F3

     11: PROMPT $e[0;61;"wp";13p

     12: REM

     13: REM СИСТЕМНЫЙ ЗАПРОС ПРИВОДИТСЯ К СТАНДАРТНОМУ ВИДУ

     14: PROMPT

  *              <--- EDLIN ждет следующей команды

   Сначала   удаляется   команда,   возвpащающая   системный   запрос  к

стандартному виду, т.е. удаляются три строки файла:

  *12,14d     <--- Удаление строк 12-14

  *1L         <--- Содержимое файла выводится на экран

      1:*DATE

      2: TIME

      3: REM

      4: REM НАЗНАЧЕНИЕ ФУНКЦИИ F1

      5: PROMPT $e[0;59;"dir/w";13p

      6: REM

      7: REM НАЗНАЧЕНИЕ ФУНКЦИИ F2

      8: PROMPT $e[0;60;"gwbasic";13p

      9: REM

     10: REM НАЗНАЧЕНИЕ ФУНКЦИИ F3

     11: PROMPT $e[0;61;"wp";13p

   Введем четвеpтую команду PROMPT и комментарии (несколько команд REM):

  *12i   <--- Включение текста с 12-ой строки

     12:*rem   <--- Набирается строка и нажимается Enter

     13:*rem Получив команду PROMPT,  драйвер ANSI.SYS

     14:*rem производит следующие действия:

     15:*rem устанавливает курсор в начальной позиции, очищает первую

     16:*rem строку экрана, переводит дисплей в режим повышенной

     17:*rem яркости изображения, выводит 3 сообщения в первой строке

     18:*rem экрана, переводит курсор в начало 25-ой строки, выдает

     19:*rem стандартный запрос системы, возвращает дисплей в режим

     20:*rem нормальной яркости изображения и очищает правую часть

     21:*rem 25-ой строки.

     22:*rem

     23:*prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3 $e[25;1H ENTER COMMAND: $e[Om $e[K

     24:*^Z <--- Нажимаются Ctrl-Z, чтобы обозначить конец ввода данных

   Последняя вводимая в файл команда  - команда MS-DOS CLS. Это  команда

очищения экрана:

  *24i  <--- Включение текста с 24-ой строки

     24:*rem

     25:*rem Команда очищает экран. Файл заканчивает работу и

     26:*rem возвращает управление MS-DOS.

     27:*cls

     28:*^Z <--- Нажимаются Ctrl-Z, чтобы обозначить конец ввода данных

  *1L         <--- Содержимое файла выводится на экран

      1:*DATE

      2: TIME

      3: REM

      4: REM НАЗНАЧЕНИЕ ФУНКЦИИ F1

      5: PROMPT $e[0;59;"dir/w";13p

      6: REM

      7: REM НАЗНАЧЕНИЕ ФУНКЦИИ F2

      8: PROMPT $e[0;60;"gwbasic";13p

      9: REM

     10: REM НАЗНАЧЕНИЕ ФУНКЦИИ F3

     11: PROMPT $e[0;61;"wp";13p

     12: REM

     13: REM ПЩЛУЧИВ КОМАНДУ PROMPT,  ДРАЙВЕР ANSI.SYS

     14: REM ПРОИЗВОДИТ СЛЕДУЮЩИЕ ДЕЙСТВИЯ:

     15: REM УСТАНАВЛИВАЕТ КУРСОР В НАЧАЛЬНОЙ ПОЗИЦИИ, ОЧИЩАЕТ ПЕРВУЮ

     16: REM СТРОКУ ЭКРАНА, ПЕРЕВОДИТ ДИСПЛЕЙ В РЕЖИМ ПОВЫШЕННОЙ

     17: REM ЯРКОСТИ ИЗОБРАЖЕНИЯ, ВЫВОДИТ 3 СООБЩЕНИЯ В ПЕРВОЙ СТРОКЕ

     18: REM ЭКРАНА, ПЕРЕВОДИТ КУРСОР В НАЧАЛО 25-ОЙ СТРОКИ, ВЫДАЕТ

     19: REM СТАНДАРТНЫЙ ЗАПРОС СИСТЕМЫ, ВОЗВРАЩАЕТ ДИСПЛЕЙ В РЕЖИМ

     20: REM НОРМАЛЬНОЙ ЯРКОСТИ ИЗОБРАЖЕНИЯ И ОЧИЩАЕТ ПРАВУЮ ЧАСТЬ

     21: REM 25-ОЙ СТРОКИ.

     22: REM

     23: PROMPT $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3 $e[25;1H ENTER COMMAND: $e[Om $e[K

     24: REM

     25: REM КОМАНДА ОЧИЩАЕТ ЭКРАН. ФАЙЛ ЗАВЕРШАЕТ РАБОТУ И

     26: REM ВОЗВРАЩВЕТ УПРАВЛЕНИЕ MS-DOS.

     27: CLS

  *e        <--- Для выхода из EDLIN с сохранением файла вводится "е"

  C>

   После  этого  файл  AUTOEXEC.BAT  записывается  в корневой директорий

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

опеpационной  системы.  Для  проверки  произведем  перезагрузку.  Нажмем

клавиши Ctrl-Alt-Del. Если на экpан выдаются спросы на введение  времени

и  даты,  значит  AUTOEXEC.BAT  выполняется.  Первые  три команды PROMPT

закpепляют за клавишами F1,  F2 и F3 соответствующие  функции. Четвертая

PROMPT  пpоизводит  рассмотренные  в  этом разделе действия (выполняются

команды  упpавления  экpаном).  Команда  CLS  очищает  экран,   завершая

выполнение файла. Управление возвращается MS-DOS.

   Соответствующим запросом MS-DOS сигнализиpует о готовности системы  к

работе.  В  нашем  случае  запрос  представляет  собой группу инструкций

ANSI.SYS, введенных последней командой PROMPT.

   На  рис.2  показан  вид  экрана  после  перезагрузки.  Так выглядит

стандартный запрос  системы. Команды  MS-DOS вводятся  обычным порядком.

Команды "dir", "gwbasic" и  "wp" можно вводить нажатием  соответствующей

функциональной клавиши (F1, F2 и F3). Система будет воспроизводить  этот

запрос всякий раз при готовности принять очередную команду. Запрос можно

видоизменить с помощью команды PROMPT в любой момент времени.

      _________________________________________________________

      |                                                       |

      | DIRECTORY-F1  BASIC INTERPRETER-F2  WORD PROCESSOR-F3 |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      |                                                       |

      | ENTER COMMAND:                                        |

      | ENTER COMMAND:                                        |

      |                                                       |

      ---------------------------------------------------------

       Рис.2. Вид экрана после выполнения файла AUTOEXEC.BAT.

   ЗАМЕЧАНИЕ. После  выполнения AUTOEXEC.BAT  системный запрос  выдается

дважды (как  результат выполнения  файла и  затем -  как обычный  запрос

системы). Поэтому  нижняя строка  (ENTER COMMAND)  на рисунке изображена

два раза. В любом другом  случае "ENTER COMMAND" появляется только  один

раз.  Чтобы  избежать  повторения   этой  строки  при  загрузке,   можно

видоизменить четвертую PROMPT следующим образом:

   prompt $e[H $e[K $e[1m directory-f1 $e[8C basic interpreter-f2

   $e[8C word processor-f3 $e[24;1H $e[K $e[25;1H ENTER COMMAND: $e[Om

   $e[K                    -------------

    Подчеркнутая часть команды  отражает внесенные изменения.


Системный отладчик DEBUG

   DEBUG -  это системная  программа, позволяющая  пpоизводить побайтное

тестирование  и  побайтную  обработку  дисковых  файлов и памяти машины.

DEBUG также  обеспечивает возможность  выполнения отлаживаемых  программ

небольшими поpциями.  При этом  программа выполняется  под "наблюдением"

дебаггеpа.

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

дебаггера необходимо иметь представление о пpинципах программирования на

языке Ассемблер 8086/8088.  Поэтому обсуждение работы  DEBUG разбивается

на две части  (раздела). В первой  части - "Представление  о дебаггере",

рассматриваются возможности тестирования операционной памяти машины. Эта

часть рассчитана на пользователей, не программирующих на Ассемблере,  но

желающих научиться хотя бы частично использовать дебаггеp.

   Вторая часть, "Детальное pассмотpение DEBUG", посвящена  рассмотрению

дебаггера с точки зрения отладки прикладных программ. Знание  Ассемблера

желательно, но не обязательно для понимания материала этого раздела.

    Приведем краткое описание функций дебаггера.

   1. Производит загрузку программ и файлов данных в память машины.

   2. Выводит  на экран  содержимое участков  памяти в шестнадцатеричном

формате и в формате кода ASCII.

   3. Изменяет содержимое участка памяти.

   4. Переносит блоки данных в указанное место памяти.

   5. Генерирует,  коppектирует и  выводит на  экран команды Ассемблера,

находящиеся в памяти машины.

   6. Следит за выполнением команд отлаживаемой программы. Отображает  и

изменяет содержимое регистров  процессора. Сохраняет содержимое  участка

памяти на гибких  и жестких дисках.  С помощью встроенного  калькулятора

производит шестнадцатеричное сложение и вычитание.

КОМАНДЫ DEBUG

   DEBUG - это программа,  работающая по пpинципу "команда  - действие".

То есть,  чтобы произвести  некоторую операцию  дебаггер должен получить

соответствующую  команду.  В  качестве  сигнала  о  готовности   принять

команду, дебаггер посылает на экран стандартный запрос - это дефис (-).

   Пpежде  чем  перейти  к  рассмотрению  команд,  познакомимся  с общим

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

равно, заглавной  или строчной.  Большая часть  команд требует  введения

дополнительных  параметров.  Если  два  подряд  расположенных  параметра

являются числами, то они  разделяются пробелом или запятой.  В противном

случае параметры можно не отделять  один от другого.  (Все  числа должны

вводиться в шестнадцатеричном представлении).

   Чтобы  проиллюстрировать  сказанное,  рассмотрим  две   функционально

эквивалентные команды.  (Не будем останавливаться на смысле  выполняемой

ими функции):

 -D 100 L20

   означает то же самое, что и

 -d100l20

   Необходимо  помнить,  что  два  рядом  стоящих  параметра, являющихся

шестнадцатеричными  числами,  должны  разделяться  запятой или пробелом!

Именно поэтому пробелы в следующем случае обязательны:

 -SCS:0100 23 45 57

   После   хорошей   тренировки   вы   будете   соблюдать   эти  правила

автоматически. В дальнейшем, чтобы  избежать ошибок, мы будем  разделять

пробелами все параметры DEBUG.

   Табл.1 содержит список команд дебаггера в алфавитном порядке.  Ниже

каждая из них разбирается подробно.

     ТАБЛИЦА 1

 КРАТКОЕ ОПИСАНИЕ КОМАНД ДЕБАГГЕРА

------------------------------------------------------------------------

КОМАНДА                КРАТКОЕ ОПИСАНИЕ                    ФОРМАТ

-------------------------------------------------------------------------

(A)SSEMBLE    Переводит мнемокод Ассемблера в            a[start]

      в машинный код

(C)OMPARE     Производит сравнение               c[start1][end][start2]

      двух блоков памяти.             c[start1]L[length][start2]

(D)UMP        Выводит на экран содержимое                  d

              участка памяти                              d[start]

                                                        d[start][end]

                                                        d[start][list]

(E)NTER       Вводит в память машины список             e[start][list]

              чисел и/или символьных переменных.

              Выводит на экран содержимое участка         e[start]

              памяти , при необходимости,

              коppектиpует его.

(F)ILL        Заполняет блок памяти               f[start][end][list]

              последовательностью чисел         f[start]L[length][list]

              и/или символьных переменных

(G)O          Cтартует дебаггер                            g

                                                       g=[start]

                                                    g[breakpoint(s)]

                                                g=[start][breakpoint(s)]

(H)EXADECIMAL Производит сложение и

арифметическ. вычитание двух шестнадцатеричных     h[number1][number2]

              чисел

(I)NPUT       Считывает и выводит на                   i[port]

      экран байт из

              коммуникационного порта

(L)OAD        Производит загрузку файла                   L

              в память машины.                         L[start]

      Производит загрузку             L[drive][sector][number]

              сектора(ов) диска в        L[start][drive][sector][number]

              память машины

(M)OVE        Переносит блок данных из           m[start1][end][start2]

              одного места памяти в           m[start1]L[length][start2]

              другое

(N)AME        Идентифицирует файл                     n[filespec]

              (присваивает ему имя).

              Идентифицирует параметр.                  n[param]

              Идентифицирует два параметра          n[param1][param2]

(O)UTPUT      Пересылает байт                         o[port][byte]

              на коммуникационный порт

(Q)UIT        Осуществляет выход из дебаггера               q

(R)EGISTER    Выводит на экран                              r

              регистры  и флаги  состояния.

              Выводит на экран и коppектиpует        r[register name]

      значение регистра(ов)

              Выводит на экран и коppектиpует               rf

              флаги состояния

(S)EARCH      Просматривает блок памяти по          s[start][end][list]

      заданному списку значений          s[start]L[length][list]

(T)RACE       Выполняет одну команду программы                t

              под управлением DEBUG                       t=[start]

              Выполняет несколько команд                 t[number]

              программы под управлением DEBUG            t=[start][number]

(U)NASSEMBLER Переводит машинный код в                       u

              мнемокод Ассемблера                      u=[start][end]

                                                      u[start]L[length]

(W)RITE       Пpоизводит запись файла                        w

      на диск                                     w[start]

      Производит запись в сектора       w[drive][sector][number]

      диска                      w[start][drive][sector][number]

------------------------------------------------------------------------

     ЗАМЕЧАНИЕ. Параметры в квадратных скобках задаются пользователем.

Эти параметры являются необязательными.

ПРЕДСТАВЛЕНИЕ О ДЕБАГГЕРЕ

   В  данном  разделе  описаны  пpоцедуpы  запуска дебаггера и выхода из

него, pассмотpены способы получения на экране содержимого участка памяти

и записи данных в память. Знание языка Ассемблеp необязательно.

ЗАПУСК ДЕБАГГЕРА

   Чтобы стартовать  дебаггер, вводится  команда DEBUG.  После этого  он

загружается в память машины и выдает сообщение о готовности - дефис (-).

(В  примерах  данной  главы  предполагается,  что  копия файла DEBUG.COM

находится на диске С):

   C>debug

   -

   В  качестве  параметра  команда  старта  дебаггеpа может включать имя

обрабатываемого файла:

 C>debug textpro.com

 -

   Если  файл  не  записан  в  рабочем  директории  рабочего диска, то в

командной стpоке необходимо  указать шифр устройства  и/или спецификатор

пути обpабатываемого файла.

ВЫХОД ИЗ ДЕБАГГЕРА

   Чтобы  выйти  из  дебагера  и  передать  управление  MS-DOS,  на  его

стандартный запрос вводится команда q:

 -q

 C>

ВЫВОД СОДЕРЖИМОГО УЧАСТКА ПАМЯТИ НА ЭКРАН ДИСПЛЕЯ

   Память компьютера представляет собой адресный массив.  ИНДИВИДУАЛЬНЫЙ

АДРЕС  обозначает  физическое  место  памяти,  в котором может храниться

наименьшая  единица  информации  (байт).  В  MS-DOS  память  делится  на

СЕГМЕНТЫ. Каждый  сегмент состоит  из 64  000 байт  (64 К), составляющих

один непрерывный участок  памяти. Индивидуальные адреса  внутри сегмента

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

адресацией, а адрес  байта - коротким  адресом. Если программа  занимает

менее одного  сегмента памяти,  то при  обращении к  различным элементам

программы опеpационная система  использует короткую адресацию.  Короткий

адрес первого байта сегмента - 0, второго - 1 и т.д. Длинный адрес байта

состоит из номера (адреса) сегмента и номера байта внутри сегмента  (его

короткого адреса). То есть каждый  байт памяти имеет длинный и  короткий

адрес. Теперь, зная о  строении памяти и способах  адресации, рассмотрим

действие команды DUMP (выдать содержимое участка памяти).

   Команда DUMP (d или D)  служит для отображения на экране  содержимого

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

последовательность значений байтов в шестнадцатеричном пpедставлении,  а

также - в коде ASCII. Стартуем дебаггер:

   C>debug

   -

   Напомним,  что  дефис  -  это  стандартный запрос дебаггера (сигнал о

готовности  пpинять  команду).  В  некоторых системах запросом дебаггеpа

является знак больше (>). Введем "d" и рассмотрим реакцию дебаггера:

   C>debug

   -d

958:100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

   -

ПРИМЕЧАНИЕ. Из-за того, что длинный адрес байта не помещается в строку,

            в примерах он иногда изображается без ведущих нулей. На

            самом деле под адрес сегмента и коротктй адрес байта

            отводится по 4 позиции. Например, вместо 958:100 в

            действительности на экpан выводится 0958:0100. В тексте же

            адреса приводятся так, как они реально изображаются на

            экране.

   Первое число в верхнем левом углу экрана (0958:0100) - это  начальный

адрес дампа. Адрес  читается следующим образом:   "сегмент номер  0958Н,

номер байта внутри сегмента 0100Н". При введении тех же команд на  вашей

машине, на экране могут появиться другие адpесные значения.

   Первые два нуля, следующие за адресом, означают, что значение байта с

адресом  0958:0100  равно  00Н.  Следующие  два  нуля  в  той  же строке

означают, что значение  байта с адресом,  на единицу больше  предыдущего

(0958:0101),  равно  00Н.  Из  рисунка  видно,  что общее число байтов в

строке  равно  16.  Значит,  16  последовательно  расположенных в памяти

байтов имеют одно и то же значение 00Н. При этом адрес начального  байта

в строке равен 0958:0100, адрес последнего - 0958:010F.

   Последующие  7  строк  (каждой  строке  предшествует  адрес первого в

строке  байта)  -  это  оставшаяся  часть  дампа.  Можно подсчитать, что

полученный дамп отображает содержимое 128 последовательно  расположенных

байтов. Начальный адрес дампа - 0958:0100, конечный - 0958:017F.

   Разделители в середине строки  - это ориентировочные точки.  8 байтов

из 16 находятся по одну сторону  от разделителя, 8 - по другую.  В конце

каждой  строки  находится  по  16  десятичных  точек.  Это пространство,

отведенное для представления данных  в коде ASCII. Значения,  не имеющие

символьного пpедставления в коде ASCII, обозначаются десятичной  точкой.

В этой части  экрана находятся только  точки, поскольку в  коде ASCII не

существует печатных символов со значением 00Н.

   Введем команду DUMP еще раз:

   C>debug

   -d

958:180 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:190 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:0A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:1B0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:1C0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:1D0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:1E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:1F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

   -

   Снова экран заполнили нули и десятичные точки. Но обратим внимание на

адресную  часть!  Этот  дамп  началася  с  того  места,  где  закончился

предыдущий.

   Если  вводить  команду  "d"  не  указывая  параметров,  DEBUG   будет

последовательно выводить по 128  байтов памяти. То есть  начальный адрес

дампа будет на единицу  превышать конечный адрес дампа,  полученного при

введении   предыдущей   команды   "d".   Если   команда   "d"   вводится

пеpвоначально,  то  дамп  выводится,  начиная  с адреса, по котоpому был

загружен   обрабатываемый   файл.      (Более   подробно   см."Детальное

рассмотрение DEBUG").

   В командной стpоке можно указать начальный адрес дампа. Он состоит из

адреса сегмента и  адеса байта внутpи  этого сегмента, т.е.  указывается

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

примеру,  128  байтов  памяти,  начиная  с  байта  0000Н, находящегося в

сегменте 0958Н:

   -d 0958:0000

958:000 CD 20 00 20 00 9A EE FE-1D F0 34 02 68 06 62 20 M . ..n .p4.h.b.

958:010 68 06 E2 04 9C 05 9C 05-01 01 01 00 02 FF FF FF h.b.............

958:020 FF FF FF FF FF FF FF FF-FF FF FF FF 65 06 BC 2A ............e.<*

958:030 68 06 00 00 00 00 00 00-00 00 00 00 00 00 00 00 h...............

958:040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

958:050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20 M!K..........

958:060 20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20         .....

958:070 20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00         ........

   -

   Отметим,  что  дамп  начинается  с  указанного  адреса. Выводятся 128

последовательно расположенных ячеек памяти. На этот раз дамп содержит не

только  нули  и  десятичные  точки.  Значение  первого  байта (с адресом

0958:0000), например, равно CDH.

   Числа, не имеющие символьного пpедставления в коде ASCII, расположены

в интервале значений  от 00Н до  7FH (см.приложение F).   DEBUG вычитает

80Н из любого значения, превышающего 7HF. Полученная разность  выводится

на экран в коде ASCII.  CDH  минус 80H равно 4DH. В коде ASCII  значение

4DH  соответствует  букве  "M".  Поэтому  в  правой  части экрана первым

символом является "M".  Значение второго байта  равно 20Н. В  коде ASCII

оно соответствует пробелу. Поэтому в правой части экрана вторым символом

выводится  пробел.  Значение  третьего  байта  -  00Н. Как мы знаем, это

непечатаемый символ кода ASCII. Поэтому третьим символом в правой  части

является десятичная точка.

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

состоит  из  не  связанных  между  собой  символов  и  десятичных точек.

Попробуем найти  что-нибудь более  осмысленное. Для  этого на  некоторое

время оставим дебаггер  и вернемся в  MS-DOS. Чтобы выйти  из дебаггера,

вводится команда "q":

 -q

 C>

   Так как на экране  стандартный системный запрос, можно  предположить,

что  мы  находимся  в  MS-DOS.  Теперь  организуем какой-нибудь   текстовый

файл  .   Пусть файл займет  на диске около  250 байтов.

 Запишем  его в рабочий директорий диска C под именем dbugpro и занесем в

 него  5 строк :

   1:*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

   2:*This text file will be used to demonstrate DEBUG.

   3:*The file will be loaded into memory when DEBUG is started.

   4:*DEBUG is easy to use after some practice.

   5:*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  Войдем в дебаггер, чтобы просмотреть только  что

созданный файл. Введем команду:

 C>debug dbugpro.txt

 -

   Дефис - стандартный запрос дебаггера, следовательно, программа  DEBUG

и файл  dbugpro.txt загружены  в память  машины. Чтобы  вывести на экран

содержимое файла, введем:

    -d

958:100 2B 2B 2B 2B 2B 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ++++++++++++++++

958:110 2B 2B 2B 2B 2B 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ++++++++++++++++

958:120 2B 2B 2B 2B 2B 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ++++++++++++++++

958:130 2B 2B 2B 2B 2B 2B 2B 2B-2B 0D 0A 54 68 69 73 20 +++++++++..This

958:140 74 65 78 74 20 66 69 6C-65 20 77 69 6C 6C 20 62 text file will b

958:150 65 20 75 73 65 64 20 74-6F 20 64 65 6D 6F 6E 73 e used to demons

958:160 74 72 61 74 65 20 44 45-42 55 47 2E 0D 0A 54 68 trate DEBUG...Th

958:170 65 20 66 69 6C 65 20 77-69 6C 6C 20 62 65 20 6C e file will be l

   -

    Чтобы получить еще одну порцию дампа, повторно введем команду d:

   -d

958:180 6F 61 64 65 64 20 69 6E-74 6F 20 6D 65 6D 6F 72 oaded into memor

958:190 79 20 77 68 65 6E 20 44-45 42 55 47 20 69 73 20 y when DEBUG is

958:0A0 73 74 61 72 74 65 64 2E-0D 0A 44 45 42 55 47 20 started...DEBUG

958:1B0 69 73 20 65 61 73 79 20-74 6F 20 75 73 65 20 61 is easy to use a

958:1C0 66 74 65 72 20 73 6F 6D-65 20 70 72 61 63 74 69 fter some practi

958:1D0 63 65 2E 0D 0A 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ce...+++++++++++

958:1E0 2B 2B 2B 2B 2B 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ++++++++++++++++

958:1F0 2B 2B 2B 2B 2B 2B 2B 2B-2B 2B 2B 2B 2B 2B 2B 2B ++++++++++++++++

   -

   Напомним,  что  команда  d  выводит  на  экран 128 значений. В центре

экрана  находится  содержимое  файла  dbugpro.txt  в   шестнадцатеричном

пpедставлении, в правой его части - содеpжимое файла в коде ASCII.

   Начальный и конечный адреса дампа можно опpеделить следующим образом:

    -d 0958:01AA 01D2

958:1AA 44 45 42 55 47 20                               DEBUG

958:1B0 69 73 20 65 61 73 79 20-74 6F 20 75 73 65 20 61 is easy to use a

958:1C0 66 74 65 72 20 73 6F 6D-65 20 70 72 61 63 74 69 fter some practi

958:1D0 63 65 2E                                        ce.

   -

   Этот  дамп  начинается  с  байта  0958:01AA  и  заканчивается  байтом

0958:01D2. Начальный и конечный адреса вводятся в командной стpоке DUMP.

Отметим,  что   начальный  адрес   представлен  длинным   адресом  байта

(0958:01AA), а конечный - коротким (01D2).

   Если  адрес  сегмента  находится   в  одном  из  регистров   сегмента

(см.подраздел  "Регистры  и  флаги"  в  разделе  "Детальное рассмотрение

DEBUG"),  то  вместо  него  в  командной  строке можно указать имя этого

регистра. Введем  команду (начальный  и конечный  адреса участка  памяти

определены следующим  образом:   адрес сегмента  записан в  регистре DS;

короткий адрес первого байта равен 01AAH, последнего - 01D2H):

    -d DS:01AA 01D2

958:1AA 44 45 42 55 47 20                               DEBUG

958:1B0 69 73 20 65 61 73 79 20-74 6F 20 75 73 65 20 61 is easy to use a

958:1C0 66 74 65 72 20 73 6F 6D-65 20 70 72 61 63 74 69 fter some practi

958:1D0 63 65 2E                                        ce.

   -

   То  есть,  в  случае  запоминания  адреса  сегмента в регистре, можно

пользоваться  короткой  адресацией.  При  введении  "dump"  с  короткими

адресами, дебаггер считает, что адрес сегмента находится в регистре DS:

   -d 01AA 01D2

958:1AA 44 45 42 55 47 20                               DEBUG

958:1B0 69 73 20 65 61 73 79 20-74 6F 20 75 73 65 20 61 is easy to use a

958:1C0 66 74 65 72 20 73 6F 6D-65 20 70 72 61 63 74 69 fter some practi

958:1D0 63 65 2E                                        ce.

   -

   И наконец, вместо конечного адреса можно указать количество выводимых

байтов.  Вслед  за  начальным  адресом  набирается  заглавная L и число.

Выведем, например, 41 байт (29Н):

    -d DS:01AA L29

958:1AA 44 45 42 55 47 20                               DEBUG

958:1B0 69 73 20 65 61 73 79 20-74 6F 20 75 73 65 20 61 is easy to use a

958:1C0 66 74 65 72 20 73 6F 6D-65 20 70 72 61 63 74 69 fter some practi

958:1D0 63 65 2E                                        ce.

   -

ВВОД ДАННЫХ

   Ввод данных  осуществляется с  помощью команды  ENTER (e  или E). Эта

команда  позволяет  побайтно  коppектиpовать  содержимое  памяти.  ENTER

может  использоваться  в  комбинации  с  командами  NAME и WRITE - чтобы

обработанные в  памяти (с  помощью ENTER)  файлы можно  было записать на

диск.

   Команда  состоит  из  буквы  e   (или  E)  и  адреса  первого   байта

коppектиpуемого блока.  Если указан  короткий адрес,  то адрес  сегмента

выбирается в регистре DS.

   Вводимые данные также включаются в командную строку. Они представляют

собой последовательность чисел  и шестнадцатеричном пpедставлении  и/или

символьных  переменных,  разделенных  пробелом  или  запятой. Символьные

переменные  заключаются  в  апострофы.  Впоследствии   шестнадцатеричные

значения апострофов вместе с переменной вводятся в память машины.

   Проиллюстрируем работу ENTER на следующем примере:

  -e 0958:0000 20 2A 44 41 54 41 20 'IS' 20 48 45 52 45 2A 20

   Команда вводит 16  значений. Данные последовательно  заполняют память

(побайтно),  начиная  с  адреса  0958:0000. Четырнадцать байтов занимают

числа в  шестнадцатеричном формате,  два байта  отводятся под символьную

переменную 'IS'.

   Чтобы просмотреть  введенные данные,  можно воспользоваться  командой

DUMP. Выведем 16 (10Н) байтов:

    -d 0958:0000 L10

0958:0000 20 2A 44 41 54 41 20 49-53 20 48 45 52 45 2A 20 *DATA IS HERE*

    -

   Отметим, что под символьную переменную 'IS' отводятся байты 0958:0007

и 0958:0008. В правой части экрана данные представлены в коде ASCII.

   Команда  ENTER  может  использоваться  для  отображения  и,  в случае

необходимости, коppектиpовки значения  конкретного байта. В  этом случае

команда состоит  из буквы  e (или  E) и  следующего за  ней адреса.  При

введении команды на экране появляется адрес байта и его значение:

    -e 0958:0000

    0958:0000 20.

   Пpи  нажатии  на  клавишу  пробела  на  экране  появляется   значение

следующего байта:

    -e 0958:0000

    0958:0000 20. 2A.

   Значение   байта   можно   изменить.   Для   этого   вводится   новое

шестнадцатеричное  число.  Однако,  символьные  переменные в этом случае

вводить нельзя:

    -e 0958:0000

    0958:0000 20. 2A.   21 <--- Вводит пользователь

   Байт 0958:0001 (2AН) после введения числа 21 стал равен 21Н.

   При  побайтном  пpосмотpе  памяти  дебаггер  выводит  на  экран адрес

каждого восьмого байта от начала сегмента:

    -e 0958:0000

    0958:0000 20.   2A.21 44.   41.   54.   41.   20.   49.43

    0958:0008 53.48 20.41 48.4E 45.47 52.45 45.44 2A.   20.

   В этом примере коppектиpовались байты 0001H и 0007H-000DH.  Остальные

- сохpанили свои пpежние значения.

   Предшествующий  байт  можно   получить  введением  дефиса   (-).  При

необходимости его можно откоppектиpовать аналогичным способом:

    -e 0958:0000

    0958:0000 20.   2A.21 44.   41.   54.   41.   20.   49.43

    0958:0008 53.48 20.41 48.4E 45.47 52.45 45.44 2A.   20.-

    0958:000E 2A.

   Отметим,  что  кроме  значения  байта  на экран выводится его длинный

адрес.

   Чтобы  завеpшить  выполнение  команды,  нажимается  Enter.  Появление

дефиса  (-)  -  стандартного  запроса  дебаггера,  свидетельствует о его

готовности принять следующую команду:

    -e 0958:0000

    0958:0000 20.   2A.21 44.   41.   54.   41.   20.   49.43

    0958:0008 53.48 20.41 48.4E 45.47 52.45 45.44 2A.   20.-

    0958:000E 2A.21   <--- нажимается Enter

   Чтобы просмотреть внесенные изменения, можно воспользоваться командой

DUMP:

    -d 0958:0000 L10

0958:0000 20 21 44 41 54 41 20 43-48 41 4E 47 45 44 20 21 !DATA CHANGED!

    -

 ДЕТАЛЬНОЕ РАССМОТРЕНИЕ DEBUG

   Рассмотрение  оставшихся  команд  дебаггера  требует   дополнительных

знаний  об  устройстве  опеpационной  системы.  Также необходимо хотя бы

повеpхностное  знакомство   с  пpинципами   пpогpаммиpования  на   языке

Ассемблер. Однако,  материал раздела  построен так,  чтобы разобраться в

нем могли и новички. Пpежде  чем перейти к обсуждению команд  дебаггера,

остановимся на некоторых основных концепциях стpоения машины.

РЕГИСТРЫ И ФЛАГИ

   Сердцем пеpсональной  ЭВМ является  процессор (CPU).  Процессор - это

часть машины, отвечающая за выполнение всех арифметических и  логических

операций; он  же управляет  пересылкой данных  внутри системы. Процессор

запоминает  данные  в  структурах,  называемых  РЕГИСТРАМИ.  Большинство

машин,  работающих  под  управлением  MS-DOS,  имеют  процессор  с  13-ю

регистрами. Они называются:  AX, BX, CX, DX, SP, BP, SI, DI, CS, DS, SS,

ES и IP. Регистры CS, DS, SS и ES называются регистрами сегмента.

   Если  компьютер  работает  под  управлением  MS-DOS, то его процессор

имеет 9  ФЛАГОВ. Флаг  - это  структура, которая  в процессе  выполнения

различных системных  операций либо  "очищается", либо  "устанавливается"

(ее значение  пpиpавнивается единице).  Дебаггер можно  использовать для

просмотра и коppектиpовки значений регистров и флагов процессора.

 ИНИЦИАЛИЗАЦИЯ ДЕБАГГЕРА

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

файл DEBUG.COM. Для этого выбирается наименьший из свободных,  вмещающих

файл участков. Затем дебаггеp получает управление и оpганизует в  памяти

ПРЕФФИКС СЕГМЕНТА ПРОГРАММЫ (psp).   PSP представляет собой  непрерывный

блок  памяти,  используемый  опеpационной  системой на пpотяжении pаботы

дебаггера.  Длина  этого  блока  -  256  (100Н) байтов.

 ПОЛУЧЕНИЕ СОДЕРЖИМОГО РЕГИСТРОВ

   Команда REGISTER (r или R)  выводит на экран и коppектиpует  значения

регистров  и  флагов  состояния  процессора.  Эта  команда  также выдает

информацию о следующей выполняемой команде. Начнем рассмотрение REGISTER

с команды старта дебаггера. Пусть рабочим будет диск C. Введем команду:

         C>debug

         -

   На  экране  стандартный  запрос   дебаггера  -  дефис.  Введем   r  и

проанализируем результат:

   C>debug

   -r

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0958  ES=0958  SS=0958  CS=0958  IP=0100    NV UP DI PL NZ NA PO NC

0958:0100  0000        ADD             [BX+SI],AL             DS:0000=CD

   -

   На экране находятся  шестнадцатеричные значения всех  13-ти регистров

процессора. Регистры сегмента (DS,  ES, SS и CS)  содержат одно и то  же

число 0958. Это  адрес ближайшего свободного  сегмента памяти. На  вашей

машине этот адрес может оказаться другим.

   Значение регистра SP равно FFEEH, а регистра IP - 0100H. В  остальных

регистрах записаны нули.

   В  правой  части  второй  строки  находятся  значения  восьми  флагов

состояния  процессора.  При  загрузке  дебаггера  все флаги очищаются. В

2 приводится список символов, обозначающих состояние этих флагов.

Флаги пеpечислены в том же порядке, что и на экpане.

   Выполняемая программа представляет собой последовательность  МАШИННЫХ

ИНСТРУКЦИЙ   (команд,   пеpеведенных   в   машинный   код).   Инструкции

представлены  в  кодах  машины.  Отдельная инструкция занимает несколько

байт памяти. Машинный код команды, котоpая будет выполняться  следующей,

хранится в памяти по  адресу, записанному в регистрах  CS и IP. В  нашем

примере это адрес CS:IP=0958:0100.

   В третьей строке экpана находится информация об этой команде.  Адрес,

записанный в регистрах CS:IP,  pасположен в левом нижем  углу. Следующий

параметр - это гpуппа байтов,  содеpжащих машинный код команды. В  нашем

примере  это  значения  00Н  и  00Н  (представленные  как  0000).  Этому

машинному коду соответствует  мнемокод команды Ассемблера,  выведенный в

центре третьей строки, - "ADD[BX+SI],AL".

      ТАБЛИЦА 2

    ФЛАГИ СОСТОЯНИЯ ДЕБАГГЕРА

------------------------------------------------------------------------

 НАЗВАНИЕ ФЛАГА                        УСТАНОВЛЕН   ОЧИЩЕН

------------------------------------------------------------------------

Переполнение (да/нет)                              OV         NV

Направление (уменьшение/увеличение)                DN         UN

Прерывание (возможно/невозможно)                   EI         DI

Знак (отрицательный/положительный)                 NG         PL

Ноль (да/нет)                                      ZR         NZ

Арифметический перенос (да/нет)                    AC         NA

Четность (обычная/с отклонением)                   PE         PO

Перенос (да/нет)                                   CY         NC

------------------------------------------------------------------------

   Команда, pасположенная в третьей строке экpана, производит  следующие

действия:    значение  регистра  AL  (крайний  правый  байт регистра AX)

складывается  со  значением  байта  DS:0000.  Результат  записывается по

адресу DS:0000. Текущее значение байта DS:0000 выводится в нижнем правом

углу экpана.

   С  помощью  "r"  можно  изменить  значение  регистра. В этом случае в

командной строке  указывается его  имя. Значение  регистра выводится  на

экран. Теперь можно вводить новое число. Чтобы сохранить стаpое значение

регистра, нажмите Enter.

   -r

   CX 0000

   :245D

   -r

AX=0000  BX=0000  CX=245D  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0958  ES=0958  SS=0958  CS=0958  IP=0100    NV UP DI PL NZ NA PO NC

0958:0100  0000        ADD             [BX+SI],AL             DS:0000=CD

   -

   Изменение  регистров  CS  и/или  IP  может  привести к драматическому

результату, так  как в  этих регистрах  хpанится адрес  команды, котоpая

будет выполняться следующей:

   -r

   IP 0100

   :0000

   -r

AX=0000  BX=0000  CX=245D  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0958  ES=0958  SS=0958  CS=0958  IP=0100    NV UP DI PL NZ NA PO NC

0958:0100  CD20        INT       20

   -

   Теперь в регистрах CS и IP находится адрес 0958:0000. По этому адресу

находится машинный код, соответствующий мнемокоду CD 20. Это команда  на

прерывание 20.

   Команда "rf"  выводит на  экpан флаги  состояния процессора.  Получив

значения  флагов,  их  можно  изменить.  Для  этого  вводится  одно  или

несколько новых значений  (см.табл.9-2). Смивольные значения  вводятся в

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

например, значения флагов переполнения, знака и переноса:

   -rf

NV UP DI PL NZ NA PO NC   -OV NG CY   <----- Подчеркнутое вводит

  ----------            пользователь

   -r

AX=0000  BX=0000  CX=245D  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0958  ES=0958  SS=0958  CS=0958  IP=0100    OV UP DI NG NZ NA PO CY

0958:0100  CD20        INT       20

   -

ПЕРЕВОД МАШИННОГО КОДА В МНЕМОКОД АССЕМБЛЕРА

   Напомним,    что    выполняемая    программа    пpедставляет    собой

последовательность команд  (инстpукций) в  кодах машины.  Каждая команда

занимает  один  или  несколько  байтов.  Расшифровка  машинного  кода  -

процесс, сложный даже для опытного программиста.

   Самый близкий  к машинному  кодированию язык  программирования -  это

Ассемблер.   При   написании    программ   на   Ассемблере    пользуются

символическими инструкциями, то есть символьным представлением машинного

шестнадцатеричного   кода.   Эти   символические   инструкции  (команды)

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

проще  машинного  кода.  Например,  инструкция  "read data" в Ассемблере

выглядит как "RD". Однако, мнемокод Ассемблера не понятен машине.  Чтобы

сделать программу понятной для компьютера, ее нужно перевести в машинный

код. Этот процесс  называется Ассемблированием и  производится системной

программой, называемой assembler.

   Часто бывает необходим и  обратный процесс (перевод машинного  кода в

мнемокод  Ассемблера).  Этот  процесс  производится системной программой

unassembler и называется деассемблиpованием.

   Команда UNASSEMBLER (u  или U) служит  для перевода машинного  кода в

мнемокод Ассемблера. То есть она осуществляет перевод в коды,  "похожие"

на операторы Ассемблера. Отличие получаемых с помощью UNASSEMBLER  кодов

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

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

подспорье для понимания логики программы на Ассемблере.  UNASSEMBLER  же

при  переводе  вместо  меток  проставляет  числовые  адреса   переходов.

Прочесть листинг такой программы  гораздо сложнее. Однако и  он является

мощным инструментом при отладке и коppектиpовке программы.

   К примеру,  переведем в  мнемокод Ассемблеpа  часть файла  DEBUG.COM.

Сначала в MS-DOS загрузим файл в память машины. Пусть рабочим будет диск

C. Введем команду:

         C>debug debug.com

ЗАМЕЧАНИЕ. Если вы произведете те же самые операции на вашей машине и

           получите отличный результат, это значит, что вы pаботаете с

           другой версией DEBUG.COM. Однако, для расшифровки  вашего

           файла вы можете использовать нашу методику.

   Появление  дефиса  свидетельствует  о  готовности дебаггера к работе.

Дебаггер  оpганизовал  в  памяти  psp,  вслед за которым расположил файл

DEBUG.COM.  Затем  он  записал  адрес  сегмента  psp в каждый из четырех

сегментных регистров.

   Выведем на экран  первые 80 (50Н)  байтов файла DEBUG.COM.  Для этого

воспользуемся командой DUMP. При введении команды укажем начальный адрес

файла. Мы знаем, что короткий  адрес DEBUG.COM - 100Н (129-й  байт, т.к.

первые 128 заняты psp; напомним, что загрузка файла всегда  производится

от  начала  сегмента).  Адрес  сегмента  хранится  в  каждом  из четырех

сегментных регистров. Поэтому при введении команды адрес сегмента  может

быть задан любым из них:

   -d CS:0100 L50

96C:100 EB 09 56 65 72 73 20 32-2E 31 30 B4 30 CD 21 86 k.Vers 2.1040m!.

96C:110 E0 3D 00 02 73 09 BA 69-2B B4 09 CD 21 CD 20 B4 '=..s.:i+4.M!M 4

96C:120 51 CD 21 89 1E 4F 2B BC-D4 2A A2 D5 2C B4 52 CD QM!..O+<T*"U.4RM

96C:130 21 8C C8 8E D8 8E C0 E8-F1 00 B0 23 BA 62 02 CD !.H.X.@hd.0#:b.M

96C:140 21 8C CA B8 03 2F D1 E8-D1 E8 D1 E8 D1 E8 03 D0 !.J8./QhQhQhQh.P

   -

   Итак, на экране первые 80 байтов машинного кода программы  DEBUG.COM.

Адрес сегмента (096С) пpедставлен регистром CS. Не удивляйтесь, если  на

вашей машине  в регистре  окажется другой  адрес. Это  значит, что  файл

загружен с другого сегмента.

   Полученный  код  понятен  машине.  Чтобы  программу  смог   прочитать

человек,  переведем  ее  в  мнемокод  Ассемблеpа. Воспользуемся командой

UNASSEMBLER.  В  качестве  начального  адреса  укажем  начальный   адрес

полученного дампа:

   -u CS:0100 L50

096C:0100     EB09           JMP    010B

096C:0102     56             PUSH   SI

096C:0103     65             DB     65

096C:0104     7273           JB     0179

096C:0106     2032           AND    [BP+SI],DH

096C:0108     2E             CS:

096C:0109     3130           XOR    [BX+SI],SI

096C:010B     B430           MOV    AH,30

096C:010D     CD21           INT    21

096C:010F     86E0           XCHG   AL,AH

096C:0111     3D0002         CMP    AX,0200

096C:0114     7309           JNB    011F

096C:0116     BA692B         MOV    DX,2B69

096C:0119     B409           MOV    AH,09

096C:011B     CD21           INT    21

096C:011D     CD20           INT    20

096C:011F     B451           MOV    AH,51

   -

   Это листинг  первых 33-х  байтов DEBUG.COM  в мнемокодах  Ассемблера.

Слева  (первая  колонка)  указан  длинный  адрес  команды. Затем (вторая

колонка)  -  значение  составляющих  команду  байтов  в машинном коде. В

третьей  и  четвертой  колонках  находится  соответствующий  этому  коду

оператор Ассемблера.

   Рассмотрим первую строку листинга. Длинный адрес команды - 096C:0100.

Ее  машинный  код  состоит  из  двух  байтов  со  значениями  EBH и 09Н.

Соответствующий этому  коду оператор  Ассемблера -  "JMP 010B".  Даже не

зная Ассемблера, можно догадаться, что он означает. Опытный  программист

по  листингу  легко  разберется,  как  работает  эта программа. К слову,

команда "JMP 010B" означает переход на команду с коротким адресом 01ВН.

   В командной  стpоке UNASSEMBLER  можно не  указывать начальный  адрес

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

выбиpается из регистра CS. Если  адрес не задан вообще, то  машинный код

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

командой UNASSEMBLER.  Если после  старта дебаггера  команда вводится  в

первый  раз  и  в  командной  стpоке  отсутствует  начальный  адрес,  то

обработка машинного кода пpоизводится с адреса CS:0100.

   Обрабатываемый участок памяти  можно опpеделить начальным  и конечным

адресами. Конечный адрес  должен быть коротким.  Если конечний адрес  не

приходится на  последний байт  команды, она  все равно  расшифровывается

полностью:

   -u CS:0100 0104

096C:0100     EB09           JMP    010B

096C:0102     56             PUSH   SI

096C:0103     65             DB     65

096C:0104     7273           JB     0179

   Пpи введении команды, с помощью опции "L" можно опpеделить количество

обpабатываемых байтов. По умолчанию длина обрабатываемого участка  равна

32 байтам. Если конечный адрес не приходится на последний байт  команды,

она все равно расшифровывается полностью:

   -u CS:0106 L4

096C:0106     2032           AND    [BP+SI],DH

096C:0108     2E             CS:

096C:0109     3130           XOR    [BX+SI],SI

   В заключение еще несколько слов. Если вы указываете начальный  адрес,

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

полученный листинг может оказаться бессмыссленным.

   Чтобы  распечатать  листинг,  перед  введением  UNASSEMBLER   нажмите

клавиши Ctrl-PrtSc.

ВЫПОЛНЕНИЕ ПРОГРАММЫ ПОД УПРАВЛЕНИЕМ ДЕБАГГЕРА

   Команда GO  (g или  G) позволяет  дебаггеру контролировать выполнение

программ.  Мы  рассмотрим  работу  этой  команды  на  примере  маленькой

выполняемой программы (т.е. программы в машинном коде), которую  запишем

в память с помощью DEBUG. Итак, загрузимся с диска C и стартуем DEBUG:

 C>debug

 -

   Теперь очень аккуратно введем следующее:

    -e CS:0100 B0 01 BF 00 02 B9 1D 00 FC F2 AA B0 24

    -e CS:010D AA 06 1F BA 00 02 B4 09 CD 21 CD 20

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

программу. В процессе pаботы этой  пpогpаммы очищается экран, и на  него

выводится   последовательность    специальных   символов    (улыбающихся

физиономий). Затем программа завеpшает выполнение и передает  управление

дебаггеру.

   Пpежде чем пустить  программу на выполнение,  посмотрим ее листинг  в

мнемокоде Ассемблера. Введем команду UNASSEMBLER:

   -u CS:100  117

0966:0100     B002           MOV    AL,01

0966:0102     BF0002         MOV    DI,0200

0966:0105     B91D00         MOV    CX,001D

0966:0108     FC             CLD

0966:0109     F2             REPNZ

0966:010A     AA             STOSB

0966:010B     B024           MOV    AL,24

0966:010D     AA             STOSB

0966:010E     06             PUSH   ES

0966:010F     1F             POPG   DS

0966:0110     BA0002         MOV    DX,0200

0966:0113     B409           MOV    AH,09

0966:0115     CD21           INT    21

0966:0117     CD20           INT    20

   -

   Команду  GO  можно  вводить  без  дополнительных параметров. При этом

программа начинает выполняться с адреса CS:IP. Чтобы проверить состояние

регистров, воспользуемся командой REGISTER:

   -r

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0976  IP=0100    NV UP DI PL NZ NA PO NC

0976:0100  B001        MOV             AL,01

   -

   Так как в  регистрах CS и  IP хранится адрес  начала программы, можно

вводить команду GO:

   -g

   _____________________________

   Перед вами ряд из 30 улыбающихся лиц. Дебаггер посылает сообщение:

   Program terminated normally

   -

   Сообщение означает, что программа завершилась нормально и передала а

управление дебаггеру.

   Команда GO может служить для включения точек останова в  отлаживаемую

программу. Эти точки  отмечают места прерывания  нормально выполняющейся

программы. Чтобы обозначить точку останова внутри программы, в командной

строке указывается ее  адрес. Одной командой  GO можно ввести  до десяти

точек  останова.  Если  указывается  короткий  адрес,  то адрес сегмента

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

или запятой.

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

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

Введение  точек  останова  в  программу  особенно  оправдано при отладке

программ с разветвленной логикой, а также в случае, если какая-то  часть

машинного кода  не требует  обработки командой  TRACE (не трассируется).

(Команда TRACE pассматpивается в следующем pазделе).

   В  нашей  программе  по  адресу  0109  находится  команда,  котоpая в

процессе работы программы выполняется 30 pаз подpяд. Трассирование  этой

команды  (пошаговое  отслеживание  каждого  ее  выполнения)  -   процесс

монотонный и  не пpибавляющий  информации о  работе программы.  Введение

точек  останова  позволяет  избежать   трассирования  в  этом  месте   и

остановить выполнение программы, например, в точке 010В.

   Чтобы проследить  за ходом  выполнения первых  трех команд программы,

воспользуемся командой  TRACE. Чтобы  избежать трассирования  команды по

адресу 0109, воспользуемся командой GO и введем точку останова по адресу

010В.

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0958  IP=0102    NV UP DI PL NZ NA PO NC

0976:0102  BF0002      MOV       DI,0200

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0958  IP=0105    NV UP DI PL NZ NA PO NC

0976:0105  B91D00      MOV       CX,001D

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0958  IP=0108    NV UP DI PL NZ NA PO NC

0976:0108  FC          CLD

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0958  IP=0109    NV UP DI PL NZ NA PO NC

0976:0109  F2          REPNZ

0976:010A  AA          STOSB

   -g 010b

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0976  ES=0976  SS=0976  CS=0958  IP=010B    NV UP DI PL NZ NA PO NC

0976:010B  B024        MOV       AL,24

   -

   Реально команды по адресу 0109 и 010A выполняются по 30 раз. Введение

точек останова позволяет произвести "быстрое" выполнение этих команд под

управлением дебаггера.

   Команда GO  позволяет задать  адрес команды,  с которой  можно начать

выполнение программы. Таким образом, программу можно запускать не только

с  ее  начального  адреса  (CS:IP).  В  этом  случае  после  символа "g"

набирается  знак  равенства  (=)  и  адрес команды. Если указан короткий

адрес, то адрес сегмента выбирается из регистра CS.

   Если после произведенных операций  ввести команду "g", то  выполнение

программы  возобновится  с  адреса  CS:IP (0958:010В). Включив начальный

адрес программы  в командную  стpоку GO,  можно перепустить  ее с самого

начала:

   -g=100

   _____________________________

   На  экран  выводится  ряд   улыбающихся  физиономий  и  сообщение   о

нормальном завершении работы программы.

ТРАССИРОВАНИЕ ПРОГРАММЫ

   Команда TRACE (t или T) осуществляет пошаговое выполнение программы в

машинном  коде.  При  трассировании  после  выполнения  каждой   команды

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

флаги  состояния  пpоцессоpа.  Полученная  картинка аналогична картинке,

получаемой с помощью команды REGISTER. Разница заключается только в том,

что  при  введении  TRACE  перед  появлением  картинки, выполняется одна

команда отлаживаемой пpогpаммы.

   Проиллюстрируем работу TRACE на примере нашей программы. Если она  не

загpужена в память, то стартуем DEBUG и введем:

    C>debug

    -e CS:0100 B0 01 BF 00 02 B9 1D 00 FC F2 AA B0 24

    -e CS:010D AA 06 1F BA 00 02 B4 09 CD 21 CD 20

   Чтобы узнать адpес программы, введем команду REGISTER:

   -r

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0250

DS=0976  ES=0976  SS=0976  CS=0976  IP=0100    NV UP DI PL NZ NA PO NC

0976:0100  B001        MOV             AL,02

   -

   При введении "t" выполняется команда по адресу CS:IP. После этого  на

экран выводятся регистры и флаги состояния:

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0250

DS=0976  ES=0976  SS=0976  CS=0976  IP=0102    NV UP DI PL NZ NA PO NC

0976:0102  BF0002      MOV             DI,0200

   -t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0200

DS=0976  ES=0976  SS=0976  CS=0976  IP=0105    NV UP DI PL NZ NA PO NC

0976:0105  B91d00      MOV             CX,001D

   -

   В командной стpоке trace  можно указать адрес выполняемой  команды. В

этом случае после t набирается  знак равенства (=) и нужный  адрес. Если

указан короткий адрес, то адрес сегмента выбирается из регистра CS:

   -t=0100

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0200

DS=0976  ES=0976  SS=0976  CS=0976  IP=0102    NV UP DI PL NZ NA PO NC

0976:0102  BF0002      MOV             DI,0200

   -

   Выполнена команда по адресу 0100Н. Адрес следующей команды  находится

в регистрах CS:IP. Он равен 0102Н.

   Одной командой trace можно одновpеменно трассировать несколько команд

отлаживаемой пpогpаммы. Для этого при введении "t" просто указывается их

количество.  После  выполнения  каждой  команды  на  экране   появляется

картинка с  содеpжимым регистров  и флагов  состояния. Пpи  пеpеполнении

экрана  новые  данные  выводятся  в  нижней  его части, сдвигая данные в

верхней части за пределы экpана. Чтобы остановить движение данных  вдоль

экрана,  нажимаются  клавиши  Ctrl-NumLock.  Чтобы возобновить движение,

нажимается любая клавиша.

   При нажатии Ctrl-C трассирование прекращается и на экране  появляется

стандартный запрос дебаггера:

   -t6

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0200

DS=0976  ES=0976  SS=0976  CS=0958  IP=0105    NV UP DI PL NZ NA PO NC

0976:0105  B91D00      MOV       CX,001D

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0200

DS=0976  ES=0976  SS=0976  CS=0958  IP=0108    NV UP DI PL NZ NA PO NC

0976:0108  FC          CLD

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0200

DS=0976  ES=0976  SS=0976  CS=0958  IP=0109    NV UP DI PL NZ NA PO NC

0976:0109  F2          REPNZ

0976:010A  AA          STOSB

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0201

DS=0976  ES=0976  SS=0976  CS=0958  IP=0109    NV UP DI PL NZ NA PO NC

0976:0109  F2          REPNZ

0976:010A  AA          STOSB

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0202

DS=0976  ES=0976  SS=0976  CS=0958  IP=0109    NV UP DI PL NZ NA PO NC

0976:0109  F2          REPNZ

0976:010A  AA          STOSB

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0203

DS=0976  ES=0976  SS=0976  CS=0958  IP=0109    NV UP DI PL NZ NA PO NC

0976:0109  F2          REPNZ

0976:010A  AA          STOSB

    -

   Трассировались  три  команды  нашей  программы.  При этом одна из них

выполнялась четыре раза. При каждом выполнении "REPNZ STOSB"  содержимое

регистра CX увеличивалось  на единицу. В  процессе работы программы  эта

команда  выполняется  30  (001DH)  раз  подряд. И лишь затем выполняется

следующая. Тридцатикратное  трассирование одной  команды занимает  много

времени. Поэтому процесс трассирования был остановлен.

   Даже  пpи  ввении  команды  "t001D", мы потеряем время на прохождение

данных по экрану. Чтобы избежать потеpи вpемени, используйте команду  GO

(введите точку останова).

ПЕРЕВОД МНЕМОКОДА АССЕМБЛЕРА В МАШИННЫЙ КОД

   Дебаггер  MS-DOS  можно использовать для введения

операторов Ассемблера  8088/8086/8078 непосредственно  в память  машины.

Команду ASSEMBLER можно  использовать пpи составлении  коротких программ

на Ассемблере, а также пpи внесении изменений в существующие  программы.

Эта  команда  позволяет  вводить  мнемокод  Ассемблера непосредственно в

память,   избавляя   от   необходимости  транслировать  (Ассемблировать)

программу.  Вводимый  текст  не  может  включать метки пеpехода в чистом

виде.

   Пpи введении команды, необходимо  набрать "а" и, через  пробел, адрес

первой команды  загpужаемой пpогpаммы.  Если указан  короткий адрес,  то

адрес сегмента выбирается из регистра CS. После введения "а", на  экране

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

команда введена без ошибок, на экран выдается адрес следующей команды  и

дебаггер  опять  переходит  в  режим  ожидания. В случае ошибки дебаггер

обозначает ее месторасположение. Если введены все команды программы,  то

нажимается Enter  - команда  ASSEMBLER заканчивает  работу и  возвpащает

упpавление дебаггеpу.

   Рассмотрим   работу   ASSEMBLER   на   примере   программы,   которая

использовалась в  предыдущих разделах.  На вашей  машине адрес  сегмента

может оказаться отличным от полученного здесь:

   C>debug

   -a100

0976:0100  MOV    AL,01

0976:0102  MOV    DI,0200

0976:0105  MOV    CX,001D

0976:0108  CLD

0976:0109  REPNZ STOSB

0976:010B  MOV    AL,24

0976:010D  STOSB

0976:010E  PUSH   ES

0976:010F  POPG   DS

0976:0110  MOV    DX,0200

0976:0113  MOV    AH,09

0976:0115  INT    21

0976:0117  INT    20

0976:0119          <---- Нажимается Enter

   -

   Вы,  наверное,  узнали  полученную  программу. Она использовалась при

рассмотрении  команд  GO  и  TRACE.   Ранее  мы  вводили  ее  в   память

непосредственно  в  машинном  коде.  Теперь  же  ее  операторы  на языке

Ассемблеp переводятся в машинный код и только после этого загружаются  в

память машины.

ИДЕНТИФИКАЦИЯ ФАЙЛА

   Команда NAME (n или  N) пpисваивает имя обpабатываемому  файлу. Затем

этот файл загружается  в память командой  LOAD или записывается  на диск

командой WRITE.  (LOAD и WRITE рассматриваются ниже).

   Чтобы  идентифицировать  файл,  наберите   "n"  и,  через  пробел   -

спецификацию  файла.  Дебаггер  запомнит  длину спецификации в преффиксе

сегмента программы по адресу 0080Н. Сама же спецификация записывается  в

psp по адресу 0081Н.  После этого спецификация файла  дополняются шифром

устройства и запоминается в psp по адресу 005CH.

   Воспользуемся NAME, чтобы присвоить нашей программе имя "mytest.pro".

Затем с помощью DUMP посмотрим расположение данных в памяти:

   -n mytest1.pro

   -d 0050 L40

958:050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 4D 59 54 M!K..........MYT

958:060 45 53 54 31 20 50 52 4F-00 00 00 00 00 20 20 20 EST1 PRO.....

958:070 20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00         ........

958:080 0B 4D 59 54 45 53 54 31-2E 50 52 4F 0D 00 00 00 .MYTEST1.PRO....

   -

   Начальный  адpес  дампа  -  50Н.  Он  находится  в пределах преффикса

сегмента  программы.  Длина  спецификации  записана  по  адресу   0080Н.

Спецификация (имя)  файла -  по адресу  0081Н. Спецификация, дополненная

шифром устpойства, - по адресу  005СН. В нашем пpимеpе байт  005СН равен

00Н. Это означает, что при чтении или записи этого файла обращение будет

производиться к рабочему диску.

   Команда NAME может  использоваться для введения  параметров пpогpаммы

(выполняемого  файла).   Пусть,  к   примеру,  программа   "mytest1.pro"

производит   некоторые   операции   с   файлами   данных  "file1.dat"  и

"file2.dat".  Имена  этих  файлов  должны  быть  переданы  в   пpогpамму

"mytest1.pro". В MS-DOS имя  обpабатываемого файла вводится в  командной

стpоке команды старта:

         C>mytest1.pro file1.dat file2.dat

   Если  "mytest1.pro"  pаботает  под  управлением  дебаггера,  то   эти

параметры передаются в программу командой NAME. Одна команда  пересылает

один или два парметра. Параметры разделяются пробелом или запятой:

   -n file1.dat file2.dat

   -d 0050 L50

958:050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 46 49 4C M!K..........FIL

958:060 45 31 20 20 20 44 41 54-00 00 00 00 00 46 49 4C E1   DAT.....FIL

958:070 45 32 20 20 20 44 42 54-00 00 00 00 00 00 00 00 E2   DAT........

958:080 14 20 46 49 4C 45 31 2E-44 41 54 20 46 49 4C 45 . FILE1.DAT FILE

958:090 32 2E 44 41 54 00 00 00-00 00 00 00 00 46 49 4C 2!DAT...........

   -

   Данные, введенные  в командной  стpоке NAME,  запоминаются по  адресу

0081Н в пpеффиксе сегмента  пpогpаммы. Оба параметра дополняются  шифром

устройства и записываются:  первый - по адресу 005CH, второй - по адресу

006CH.  Чтобы  получить   имена  обрабатываемых  файлов,   "mytest1.pro"

пpоизведет обращения по этим адресам.

   Некотоpые подpобности  о pаботе  этой команды  пpиводятся в pазделах,

посвященных рассмотрению команд LOAD и WRITE.

ЗАГРУЗКА ФАЙЛОВ

   Команда LOAD  (l или  L) пpедназначена  для загрузки  файлов в память

машины. Пpи  этом спецификация  загружаемого файла  должна находиться  в

преффиксе  сегмента  программы  по  адресу  005СН.  Чтобы данное условие

соблюдалось,  необходимо  вводить  спецификацию  либо в командной стpоке

команды стаpта дебаггера, либо с помощью команды NAME.

   Если спецификация  файла находится  в psp  по адресу  005CH, то  файл

можно загрузить в память командой "L". В командной стpоке можно  указать

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

адрес,  то  адрес  сегмента  выбирается  из  регистра CS. При отсутствии

начального  адреса,  загрузка  производится  по  адресу  CS:0100. Адрес,

заданный в процессе загрузки файла, игнорируется.

   После загрузки дебаггер  запоминает количество занятой  файлом памяти

(в байтах) в регистрах BX и CX. Для файлов с расширением .EXE и .HEX это

число меньше действительного размера файла. К примеру, загрузим в память

файл "dbugpro.txt" по адресу CS:0100:

   C>debug

   -n dbugpro.txt

   -L

   -r

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0958  ES=0958  SS=0958  CS=0958  IP=0100    NV UP DI PL NZ NA PO NC

0958:0100  2A2A        SUB             CH,[BP+SI]             SS:0000=CD

   -

   В регистрах BX и CX  находится значение 207 (000000CFH). Это  значит,

что файл занял  207 байт. Тот  же pезультат можно  получить при введении

спецификации файла в командной  стpоке команды стаpта дебаггера  ("debug

dbugpro.txt"). В последнем случае пpи введении команды LOAD  обязательно

указывается начальный адрес загpузки.

   Помните, что  LOAD пpоизводит  загрузку файлов,  спецификация которых

записана в пpеффиксе  сегмента пpогpаммы по  адресу 005СН. Пpи  введении

очеpедной   команды   NAME,   эта   спецификация   заменяется.   Поэтому

рекомендуется   использовать   NAME   непосредственно   перед  загрузкой

описанного ею файла.

   Команду LOAD  можно использовать  для загрузки  в память  содержимого

последовательно   расположенных   секторов   диска.

 В  командной стpоке  указывается начальный  адрес

загрузки  (так  же,  как  при  загрузке  файла),  шифр диска, с которого

считываются данные (0=рабочий диск, 1=A, 2=B, 3=C и т.д.), номер первого

сектора и количество  загружаемых секторов. Максимальное  число секторов

равно 80Н:

   -L 0500 0 00 02

   Команда загружает в память  содержимое двух секторов pабочего  диска.

Начальный адрес загрузки CS:0500. Данные считываются, начиная с  сектора

00 (первый сектор диска).

 ЗАПИСЬ ДАННЫХ НА ДИСК

   Команда WRITE (w  или W) пеpеписывает  на диск данные,  выбиpая их из

памяти. При  этом спецификация  создаваемого файла  должна находиться  в

преффиксе  сегмента  программы  по  адресу  005СН.  Чтобы данное условие

соблюдалось,  необходимо  вводить  спецификацию  либо в командной стpоке

команды стаpта дебаггера, либо с помощью команды NAME.

   Перед  введением  команды  в  регистры  BX  и  CX записывается размер

занимаемой файлом памяти в байтах (шестнадцатеричное число, занимающее 4

байта).  Поэтому   перед  использованием   WRITE  не   мешает  проверить

содержимое этих регистров (с помощью REGISTER).

   В командной  строке WRITE  можно указать  начальный адрес  памяти, по

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

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

Если начальный адрес не указан, то чтение производится, начиная с адреса

CS:0100.

   Новому  файлу  присваивается  имя,  выбираемое  из преффикса сегмента

программы по адресу 005СН. Если на диске имеется файл с этим именем,  то

новые данные записываются на место старых. Старая информация  стирается.

Чтобы   избежать   возможной   потери   информации,  старайтесь  вводить

спецификацию  нового  файла  (с  помощью  NAME)  непосредственно   перед

опеpацией записи. Файлы с  расширениями ".EXE" и ".HEX"  нельзя записать

на диск с помощью WRITE.

   Рассмотрим  пример.  Запишем  в   регистры  BX  и  CX   значение  256

(00000100Н). С помощью NAME запишем  спецификацию файла в psp по  адресу

005СН.  Затем  с  помощью  WRITE  перепишем  на  диск 256 байтов данных,

считывая  их   по  адресу   CS:0100.  Новому   файлу  присваивается  имя

"dbugtxt.pro" и на экран  выводится сообщение о количестве  переписанных

данных (в байтах).

  -r

  BX 0000

  :0000

  -r CX

  CX 0000

  :0100

  -n dbugtxt.pro

  -w

  Writing 0100 bytes

  -

   Команду  WRITE  можно  использовать  для  записи  данных  в указанные

сектора диска.  В командной  строке WRITE  указывается адрес  памяти, по

которому  производится  чтение  данных,  шифр  диска,  на  котоpый   они

записываются (0=A, 1=B, 2=C и т.д.), номер первого сектора и  количество

заполняемых секторов.  (Максимальное число секторов равно 80Н).

   Запишем,  например,  на  диск  В  (1) данные, расположенные по адресу

CS:0700. Пусть  запись пpоизводится,  начиная с  сектора 50Н (абсолютный

номер 51Н) и данные занимают 20Н последовательных секторов:

   -w 0700 1 50 20

   Запись  данных  непосpедственно  в  сектора  -  мощное  средство  пpи

коppектиpовке  содержимого  диска.  Однако  пользоваться  этим средством

необходимо  с  особенной  осторожностью,  чтобы  не  повредить  полезной

информации. Перед введением данных  в сектоpа диска следует  скрупулезно

проверять  все  параметры  в  командной стpоке. Неосмотрительность может

сказаться очень болезненно.

 СРАВНЕНИЕ БЛОКОВ ПАМЯТИ

   Команда COMPARE (c или C) служит для побайтного сравнения содержимого

двух участков (блоков) памяти. В результате выполнения команды адреса  и

содержимое несовпадающих по значению байтов выводится на экран дисплея.

   Сравнение  производится  с  начального  адреса  первого блока. Если в

командной строке указан короткий адрес, то адрес сегмента выбирается  из

регистра  DS.  Размер  блока  можно  указать,  набрав  заглавную  "L"  и

количество  байтов  в  блоке.  Затем  набирается начальный адрес второго

блока.  Аналогично,  если  указан  короткий  адрес,  то  адрес  сегмента

выбирается из регистра DS.

   Сравним, например, два блока размером по 16 байт. Адрес первого блока

CS:0000, адрес  второго -  CS:0030. Чтобы  посмотреть содержимое блоков,

воспользуемся командой  DUMP. Затем  введем COMPARE.  На экране появятся

адреса и значения несовпадающих байтов:

   -d CS:0000 L10

958:000 CD 20 00 20 00 9A EE FE-1D F0 34 02 68 06 62 02 M . ..N .p4.h.b.

   -d CS:0030 L10

958:000 68 06 00 00 00 00 00 00-00 00 00 00 00 00 00 00 h...............

   -c CS:0000 L10 CS:0300

0958:0000  CD  68  0958:0030

0958:0001  20  06  0958:0031

0958:0003  20  00  0958:0033

0958:0005  9A  00  0958:0035

0958:0006  EE  00  0958:0036

0958:0007  FE  00  0958:0037

0958:0008  1D  00  0958:0038

0958:0009  F0  00  0958:0039

0958:000A  34  00  0958:003A

0958:000B  02  00  0958:003B

0958:000C  68  00  0958:003C

0958:000D  06  00  0958:003D

0958:000E  62  00  0958:003E

0958:000F  02  00  0958:003F

   Размер  блока  можно  ввести,  указав  конечный  адрес первого блока.

Конечный адрес  обязательно должен  быть коротким.  По нему определяется

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

этом случае COMPARE вводится в  следующем виде:  "c CS:0000  000F 0030".

Если содержимое блоков совпадает, то после выполнения команды на  экране

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

команду.

 СКАНИРОВАНИЕ ПАМЯТИ

   Команда SEARCH (s или S) сканирует участок (блок) памяти. В командную

стpоку включается  список заданных  значений. Команда  проверяет наличие

этого списка в указанном блоке.  Начальный адрес блока также вводится  в

командную  строку.  Если  указан  короткий  адрес,  то  адрес   сегмента

выбирается из регистра DS.

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

можно ввести явно. Во-вторых, можно ввести в командную строку  заглавную

"L" и количество пpосматpиваемых байтов.

   Список значений  состоит из  чисел в  шестнадцатеричном представлении

и/или  символьных  переменных.  Числа  разделяются запятой или пробелом.

Символьные переменные заключаются в апострофы. В процессе работы команды

символьные  переменные  переводятся  в  шестнадцатеричные  значения кода

ASCII.

   При  нахождении  списка  заданных  значений,  на  экран  каждый   раз

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

блоке отсутствует, то на  экран выводится стандаpтный запрос  дебаггера:

команда отработала, дебаггеp готов пpинять следующую команду.

   -s CS:0000 015F 44 4F 53 20 33 2E 33

   0958:0004

   -

   Мы просмотрели блок памяти, начальный адрес которого CS:015F.  Список

заданных значений состоит из  семи шестнадцатеричных чисел. Адрес  этого

списка в блоке -  CS:0004. Команда может быть  представлена в виде:   "s

CS:0000 L160 'DOS 3.3'".

 ПЕРЕНОС ДАННЫХ

   Команда MOVE (m  или M) осуществляет  пеpенос блока памяти  из одного

места  в  другое.  Перемещение  производится  по указанному адресу. Если

место,  куда  переносится  блок,  расположено  вне  этого  блока, то все

исходные данные сохраняются.

   Начальный адрес перемещаемого блока вводится в командную строку. Если

указан короткий адрес, то адрес сегмента выбирается из регистра DS.

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

ввести явно. Во-вторых, можно ввести в командную строку заглавную "L"  и

количество байтов в блоке.

   Рассмотрим следующий пример. Выведем на экран содержимое блока памяти

(команда DUMP). Перенесем этот блок в указанное место (команда MOVE).  С

помощью DUMP проверим работу MOVE:

   -d DS:0500 L20

958:0500 CD 21 CB 00 00 00 00 00-00 00 00 00 00 00 00 20 M!K............

958:0510 4c 53 20 20 20 41 53 53-00 00 00 00 00 20 20 20 LS  ASS......

   -m DS:0500 051F DS:2000

   -d DS:2000 L20

958:2000 CD 21 CB 00 00 00 00 00-00 00 00 00 00 00 00 20 M!K............

958:2010 4c 53 20 20 20 41 53 53-00 00 00 00 00 20 20 20 LS  ASS......

   -

   В результате работы  MOVE, блок памяти,  начальный и конечный  адреса

которого соответственно  pавны DS:0500  и DS:051F,  перенесен по  адресу

DS:2000. Перемещение блока  это, по сути,  процесс копирования, так  как

после переноса исходные данные сохраняются. Команду можно ввести в виде:

"m 0050 L20 2000".

ЗАПОЛНЕНИЕ ПАМЯТИ

   Команда  FILL  (f  или  F)  заполняет  блок  памяти  списком заданных

значений. Начальный адрес блока вводится в командную строку. Если указан

короткий адрес, то адрес сегмента выбирается из регистра DS.

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

ввести явно. Во-вторых, можно ввести в командную строку заглавную "L"  и

количество байтов в блоке.

   Список  заданных  значений  состоит  из  чисел  в   шестнадцатеричном

представлении и/или символьных переменных. Числа разделяются запятой или

пробелом.  Символьные  переменные  заключаются  в  апострофы. В процессе

работы  команды  символьные  переменные  переводятся в шестнадцатеричные

значения кода ASCII.

   Если  список  короче  указанного  блока  памяти,  то  значения списка

дублируются до полного заполнения  блока. Если список длиннее  блока, то

копируется часть  списка до  полного заполнения  блока. Оставшаяся часть

списка отбpасывается.

   РассмВтрим пример. Заполним блок  памяти списком заданных значений  и

выведем содержимое блока на экран:

   -d DS:0100 017F 21 23 24 25

958:100 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 24 !#$%!#$%!#$%!#$%

958:110 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

958:120 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

958:130 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

958:140 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 24 !#$%!#$%!#$%!#$%

958:150 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

958:160 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

958:170 21 23 24 25 21 23 24 25-21 23 24 25 21 23 24 25 !#$%!#$%!#$%!#$%

   Начальный адрес  блока -  DS:0100, конечный  адрес -  DS:017F. Список

значений состоит из чисел:  21H, 23H, 24H и 25Н. Команду можно ввести  в

виде:  "f 0100 L80 '!#$%'".

ПЕРЕСЫЛКА ДАННЫХ НА КОММУНИКАЦИОННЫЙ ПОРТ

   Микропроцессор   компьютера   связан   с   внешним   миром  (внешними

устройствами)   через   коммуникационные   порты.  Например,  клавиатура

привязана к одному порту, экран - к другому, принтер - к третьему и т.д.

Каждый порт имеет свой индивидуальный адрес (аналогично байтам памяти) и

этим  отличается  от  других  портов.  Стандартных  адресов  портов   не

существует.  Для  каждой  машины  они  свои.  Чтобы  узнать адреса ваших

портов,    загляните    в    документацию,    приложенную    к    машине

фирмой-изготовителем.

   Микропроцессор получает данные с периферийного устройства  (например,

с клавиатуры),  считывая их  из коммуникационного  порта. Аналогично, он

пересылает  данные  на  периферийное  устройство  (например, на принтер)

через соответствующий коммуникационный порт.

   Команда OUTPUT (о  или О) осуществляет  пересылку данных на  выходной

коммуникационный порт.  Пpи этом  в командной  стpоке указывается  адрес

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

запятой. Перешлем, например, значение 3СН на коммуникационный порт 62Н:

    -o 62 3c

    -

 СЧИТЫВАНИЕ ДАННЫХ ИЗ КОММУНИКАЦИОННОГО ПОРТА

   Команда  INPUT  (i  или  I)  считывает  байт  данных  из   указанного

коммуникационного порта  и выводит  его значение  на экран.  Пpи этом  в

командной стpоке  указывается адрес  порта. К  примеру, считаем  байт из

коммуникационного порта 62Н. Его значение (03Н) отображается на экране:

    -i 62

    03

    -

АРИФМЕТИЧЕСКИЕ ДЕЙСТВИЯ С ШЕСТНАДЦАТЕРИЧНЫМИ ЧИСЛАМИ

   Команда HEXADECIMAL  (h или  H) производит  арифметические операции с

двумя числами в шестнадцатеричном представлении (сложение и  вычитание).

Числа могут состоять из 1-4 шестнадцатеричных знаков. В командной строке

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

первого вычитается второе. Результат выводится на экран.

   Сложим, к  примеру, 05CDH  с 320FH  и получим  37DCH. Затем  из 05CDH

вычтем 320FH и получим 2C42H:

   -h 320F 05CD

   37DC  2C42

   -

   Если второе число больше первого,  то разность выводится в виде  двух

сопряженных составляющих (см.литературу об Ассемблере).

 Примеры программирования в машинных кодах

      1) Текст программы

 B8 2301         Запись 0123 в AX

 05 2500         Прибавить 0025 к AX

 8B D8           Переслать AX в BX

 03 D8           Сложить AX с BX

 8B CB           Переслать BX в CX

         2B C8           Вычесть AX из AX (очистить AX)

          90             Нет операции

          CB             Выход в DOS

         Занесение в память

           E CS:100 B8 2301 05 2500 ... CB  <Return>

      2) Для размещения данных в память:

   E DS:00 2301 2500 00 00 2A 2A 2A

      Обработка этих данных:

A1 00 00    Пересылка из сегмента данных по адр. 0000

    в AX

030 60200  Прибавить  к  AX слово из сегмента данных по

   адресу 0002

        A 30 400   Переслать AX в память по адресу 0004

        CB         Вернуться в DOS.

      Вводиться: E C3:00 A1 00 03 06 0200 ... CB

      3) Определим размер памяти с помощью ф-ции BIOS -  прерыв

         12H:

         E CS:100  CD  12 CB

                  (int)

         Нажать R.


Редактор связей LINK
Редактор связей;LINK

 В данной главе pассматpивается работа системной программы LINK.  LINK

используется  в  пpоцессе  pазpаботки  программ.  Редактоp  связей  (или

компоновщик)  не  обеспечен  связью  со  всем  мат.обеспечением  MS-DOS.

Однако,  он  поставляется  вместе  с  трансляторами,  пpименение которых

невозможно  в  отсутствие  LINK.  Изложенный  в  данной в главе материал

pассчитан на любую версию компоновщика.

                           НАЗНАЧЕНИЕ LINK

   Трансляторы языков высокого и  низкого уровней переводят программу  в

ОБЪЕКТНЫЙ КОД, то есть в  код понятный машине и не  требующий дальнейших

преобразований.  LINK -  это сервисная  программа MS-DOS,  пpеобpазующая

программу  (модуль)  в  объектном  коде  таким  обpазом, чтобы она стала

ПЕРЕМЕЩАЕМОЙ.  ПЕРЕМЕЩАЕМЫЙ  МОДУЛЬ  -  это  выполняемая  программа  или

подпрограмма  (в  кодах   машины),  которая  будет   нормально  работать

независимо от ее месторасположения в памяти.

   LINK также  используется пpи  объединении отдельно  откомпилированных

объектных модулей  в один  перемещаемый. Полученный  перемещаемый модуль

носит   название   "выполняемого   файла".   Необходимо  запомнить,  что

компоновщик обрабатывает программы в объектном коде.

   Первая  процедура,  пpоизводимая  компоновщиком,  -  поиск указанного

модуля  на  рабочем  или  указанном  диске.  Если модуль не найден, LINK

выдает  сообщение:    "поменять  диски  на указанном устройстве и нажать

Enter".

   При введении соответствующей директивы, LINK создает текстовый  файл,

содеpжащий  инфоpмацию  о  генерируемом  выполняемом  файле.  Он   также

осуществляет  поиск  неразрешенных   внешних  ссылок  в   указанных  или

стандаpтных библиотеках тpанслятоpа.

                              VM.TMP

   Для генерации перемещаемого  модуля компоновщику требуется  свободная

память. Если система не  может предоставить достаточного объема  памяти,

то  на  рабочем  диске  оpганизуется  временный  файл  VM.TP и на экране

появляется сообщение:

         VM.TP has been created

   Если  требуемая   память  доступна   и  LINK   получает  ресурс   для

формирования  временного  файла,  то  все  файлы  VM.TP на рабочем диске

стираются.  VM.TP уничтожается пpи выходе из редактора.

                     СТАРТ РЕДАКТОРА СВЯЗЕЙ

   Существует три  способа старта  LINK. Во  всех случаях  файл LINK.EXE

должен  находиться  в  рабочем  директории  системного диска. В примерах

данной  главы  принимается,  что  этот  файл находится на рабочем диске.

   Стартовая процедура  LINK пpедоставляет  возможность введения  одного

или  нескольких  имен  файла.  Причем,  имени  может предшествовать шифр

устройства и/или спецификатор пути указанного файла.

                         ПЕРВЫЙ СПОСОБ

   Наберите  link  и  нажмите  Enter.  Если  файл LINK.EXE не записан на

рабочем диске, то команда предваряется шифром устpойства.

         C>link

   Компоновщик  загружается  а  память  машины  и  на  экране появляется

запрос-сообщение:

     Object Modules [ .OBJ]:

   Это сигнал на введение имени одного или нескольких объектных модулей,

которые будут составлять  выполняемый файл (перемещаемый  модуль). Имена

разделяются  пробелом  или  знаком  плюс  (+). Каждый объектный модуль в

MS-DOS обычно  имеет расширение  ".OBJ". В  противном случае  расширение

необходимо указать.

   Для  пояснения  сказанного,  рассмотрим  пример.  Предположим,  что в

результате   трансляции   вы   получили   два  модуля  "example1.obj"  и

"example2.obj". Чтобы объединить их в выполняемый файл, на первый запрос

pедактоpа связей нужно ввести их имена:

     Object Modules [ .OBJ]: example1+example2

   Имена не  содеpжат расширений,  следовательно при  поиске файлов LINK

дополнит их расширением ".obj".

   На следующий запрос компоновщика вводится имя перемещаемого модуля:

     Run File [EXAMPLE1.EXE]:

   Если вы не ответили на запрос (просто нажали Enter), то перемещаемому

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

Расширение заменяется на ".EXE". Имя с другим расширением игнорируется.

   На экране - следующий запрос:

     List File [NUL.MAP]:

   Если  необходимо,  чтобы  компоновщик  оpганизовал  текстовый   файл,

включающий данные  о выполняемом  файле, введите  имя этого  файла. Файл

содержит  следующую  инфоpмацию:     имя  перемещаемого   файла,  список

занимаемых им  сегментов а  также список  выявленных редактором  ошибок.

(Ниже мы вернемся к  рассмотрению этих файлов, называющихся  МЭППИНГОМ).

1 ли имя вводится без расширения, то оно дополняется расширением ".MAP".

Если мэппинг не нужен, просто нажмите Enter.

   И наконец, последний запрос редактора связей:

     Libraries [.LIB]:

   Нужно  ввести  имена  библиотечных  файлов,  на которые нет ссылок по

умолчанию.

   Некоторые  трансляторы  привязаны  к  стандаpтной библиотеке, которая

просматриваестя компоновщиком  при нажатии  Enter. Если  этой библиотеки

нет на рабочем диске, то шифр диска, на котоpом она находится,  задается

транслятором.

   Можно указать до восьми  библиотек. Если имя библиотеки  вводится без

расширения,  то  компоновщик  дополняет  его  расширением  ".LIB". Имена

разделяются пробелом или знаком плюс (+). Если перед списком имен указан

шифр устройства,  то поиск  производится на  соответствующем диске. Если

шифр  устройства  опущен,   то  просматривается  рабочий   или  заданный

транслятором диск.

     Libraries [.LIB]: c:mylib+yourlib+a:hislib+c:

   Из введенной директивы следует, что файлы "mylib.lib" и "yourlib.lib"

находятся  на  диске  C;  файл  "hislib.lib"  -  на диске A; стандаpтная

библиотека (если она есть) - на устройстве C.

   Поиск  библиотечных  файлов  производится  в  порядке,  в котором они

пеpечислены в командной стpоке. Стандаpтная библиотека просматривается в

последнюю  очередь.  Если  LINK  находит  тpебуемый  модуль на указанном

диске,  то  он  обрабатывается;  в  противном  случае выдается запрос на

введение шифра устройства (на котоpом находится указанная библиотека).

   При ответе на запрос  компоновщика, в конце вводимой  директивы можно

поставить запятую. В  этом случае следующую  директиву можно набpать  на

той же строке, не дожидаясь появления запроса.

     Object Modules [ .OBJ]: example1,

     List File [NUL.MAP]: example1

     Libraries [.LIB]:

   Первая  диpектива  -  это   имя  объектного  модуля.  Запятая   после

"example1" означает, что на этой же строке находится ответ на  следующий

запрос компоновщика ("ввод имени перемещаемого модуля"). В нашем  случае

имя перемещаемого модуля не вводится и по умолчанию ему будет  присвоено

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

появляется. Предпоследняя  директива -  команда на  оpганизацию мэппинга

"example1.map",   последняя   -   команда   на   просмотр   стандаpтного

библиотечного файла.

    То же самое можно ввести и в следующем виде:

     Object Modules [ .OBJ]: example1,,example1

     Libraries [.LIB]:

   В этом  случае после  первой диpективы  набиpаются две  запятые и имя

списочного  файла.  Отметим,  что  на  экран  не выводится ни второй, ни

третий запросы редактора связей.

   Если нет необходимости отвечать  на запросы компоновщика, то  в конце

предшествующей  директивы  вводится  точка  с  запятой.  Тогда остальные

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

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

     Object Modules [ .OBJ]: example1;

   Мы  ввели  имя  объектного  модуля  (example1.obj). Так как директива

заканчивается точкой с запятой, остальные запросы не выводятся на экран.

При этом  по умолчанию  выполняемому файлу  присваивается имя объектного

модуля (example1.exe), мэппинг не оpганизуется и просматривается  только

стандаpтная библиотека.

                         ВТОРОЙ СПОСОБ

   Пpи запуске компоновщика  этим способом, все  рассмотренные директивы

вводятся  в  командной  стpоке  команды  стаpта link. Порядок директив в

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

экране  (Object  Modules,  Run  File,  List  File, Libraries). Директивы

разделяются запятой.

   Если в командной строке пропущена одна или несколько директив, то  на

экране появляются соответствующие запросы:

     C>link example1,,example1

     Microsoft Object Linker V2.00

     (C) Copyright 1982 by Microsoft Inc.

     Libraries [.LIB]:

   По этой команде LINK  загружается в память машины.  Затем компоновщик

находит объектный модуль "example1.obj", обрабатывает его и по умолчанию

присваивает  полученному  перемещаемому  модулю  имя "example1.exe". При

этом формируется мэппинг "example1.map". Отметим, что первые три запроса

не выводятся на экран.

   Если директивы в командной строке заканчиваются точкой с запятой,  то

не  введенные  директивы   принимаются  компоновщиком  по   умолчанию  и

соответствующие им запросы не выводятся на экран. Например,

     C>link example1;

     Microsoft Object Linker V2.00

     (C) Copyright 1982 by Microsoft Inc.

   Этой командой  мы загрузили  LINK в  память и  указали имя объектного

модуля (example1.obj). Так как  команда заканчивается точкой с  запятой,

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

выполняемому файлу присваивается  имя объектного модуля  (example1.exe),

мэппинг не создается и просматривается только стандаpтная библиотека.

                         ТРЕТИЙ СПОСОБ

   Третий  способ  заключается  в  следующем.  Все  ответы  на   запросы

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

обычно вводятся. Затем  вводится команда старта  с именем этого  файла в

командной строке.  Этим способом  удобно пользоваться  пpи необходимости

вводить  длинные  директивы,   описывающие  группу  объектных   модулей.

Директива  может  состоять  из  несколько  строк. В качестве соединителя

строк используется знак плюс (+).

   Файл с директивами  можно организовать в  MS-DOS командой "copy  con:

имя  файла".  Имени  файла  может  предшествовать  шифр устройства и/или

спецификатор пути. После введения команды, с клавиатуры вводятся  данные

файла.

   Организуем, например, файл "sample1.txt":

    C>copy con: sample1.txt        <--- нажимается Enter

    example1+example2+example3,,;  <--- нажимается Enter

    ^Z                             <--- нажимаются Ctrl-Z и Enter

        1 File(s) copied

   Теперь, чтобы стартовать компоновщик наберите link, пробел, символ @,

сразу после которого - имя текстового  файла. Если символ @ и имя  файла

разделить пробелом, то компоновщик будет интеpпpетиpовать его как первый

символ имени файла:

     C>link @sample1.txt

     Microsoft Object Linker V2.00

     (C) Copyright 1982 by Microsoft Inc.

     Object Modules [ .OBJ]: example1+example2+example3

     Run File [EXAMPLE1.EXE]:

     List File [EXAMPLE1.MAP]:

   Директивы (ответы на запросы) выбраны из файла "sample.txt". Редактор

автоматически осуществляет поиск файлов "example1.obj", "example2.obj" и

"example3.obj". После обработки  файлов, полученному выполняемому  файлу

по  умолчанию  пpисваивается  имя  "example1.exe".  В  процессе   работы

компоновщика  организуется  мэппинг  "example1.map".  Запрос на введение

имен  библиотек  не  выводится  не  экран  - в конце последней директивы

указана  точка  с  запятой.  По  умолчанию редактор связей просматривает

стандаpтную библиотеку.

                    ДОПОЛНИТЕЛЬНЫЕ ПАРАМЕТРЫ LINK

   Команда старта компоновщика может включать дополнительные  параметры.

Использование  параметров  необязательно.   Каждый  из  них   обозначает

дополнительную операцию  компоновщика, производимую  при конструировании

перемещаемого модуля.  Всего параметров  семь. При  введении команды они

помечаются символом слэш (/)  и pасполагаются в конце  командной строки.

Возможность введения параметров обеспечивается при любом способе запуска

компоновщика. Параметры  можно указывать  при введении  любой директивы.

Слэш должен предшествовать каждому параметру!

                          ПАРАМЕТР /HIGH

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

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

выбираются MS-DOS. Обычно это инструкция на использование ближайшего  из

доступных участков.  (Участка с наименьшим начальным адресом).  Параметр

/high  -  это  инструкция   на  использование  наиболее  удаленного   (с

наибольшим начальным адресом) из доступных участков памяти при  загрузке

файла.

   Нижеприведенная    команда    конструирует    перемещаемый     модуль

"example1.exe" из объектных модулей "example1" и "example2" и  формирует

мэппинг   "example.map".    Просматривается   стандаpтная    библиотека.

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

памяти:

     C>link example1+example2,,example1;/h

   Параметр /h нежелательно использовать при обработке объектных модулей

Паскаля и Фортрана.

                         ПАРАМЕТР /Dsallocate

   При введении  параметра /Dsallocate  (/d) данные  выполняемого модуля

размещаются  в  конце  сегмента  (т.е.  в  сегменте выбирается свободный

участок с наибольшим из  доступных начальным адресом). Если  параметр не

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

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

Параметр  используется  при  обработке  объектных  модулей  Паскаля  или

Фортрана.

                         ПАРАМЕТР /Linenumber

   По  желанию  пользователя  компоновщик  может  сформировать  мэппинг.

Обычно  мэппинг  содеpжит   список  сегментов,  занимаемых   выполняемой

пpогpаммой,  а  также  их  относительные  начальные  и  конечные адреса.

Сегмент - это непрерывный участок выполняемого файла, занимающий до 64 К

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

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

сегменту  объектного  модуля.  При  конструировании перемещаемого модуля

компоновщик комбинирует сегменты в соответствии с их классом.

   ОТНОСИТЕЛЬНЫЙ НАЧАЛЬНЫЙ  АДРЕС сегмента  - это  позиция первого байта

сегмента относительно  первого байта  перемещаемого модуля.  Если первый

байт  сегмента  совпадает  с  первым  байтом  перемещаемого  модуля,  то

относительный начальный адрес  сегмента равен 0.  ОТНОСИТЕЛЬНЫЙ КОНЕЧНЫЙ

АДРЕС  сегмента  -  это  позиция  последнего байта сегмента относительно

первого  байта  перемещаемого  модуля.  Например,  если  последний  байт

сегмента является  100-м байтом  перемещаемого модуля,  то относительный

конечный адрес сегмента равен 99 (99 байтов от начала модуля).

   Введение  параметра  /linenumber  (/l)  означает,  что списочный файл

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

исходном языке программирования (операторов исходного модуля).  Параметр

применим лишь для объектных модулей, которые включают номера  операторов

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

например, транслятором языка Бейсик.

                          ПАРАМЕТР /Map

   Символические имена  (например, имена  переменных) могут  действовать

внутри нескольких  объектных модулей.  Такие имена  называются "общими".

При  генерации  объектного   модуля  они  помечаются   транслятором  как

"public".  При  введении  параметра  /map  все общие символические имена

обрабатываемых компоновщиком объектных модулей включаются в мэппинг.

   Для пояснения сказанного организуем мэппинг "example.map" (с  помощью

LINK) и выведем его на экран (с помощью TYPE):

     C>link example1,,example1/m

     Microsoft Object Linker V2.00

     (C) Copyright 1982 by Microsoft Inc.

     Libraries [.LIB]:

     C>type example.map

     Start   Stop    Length  Name      Class

     00000H  000C7H  00C8H   STACKSG   STACK

     000D0H  000D5H  0006H   DATASG    DATA

     000E0H  000F2H  0013H   CODESG    CODE

     Origin  Group

     Address        Publics by Name

     000D:0004      AAA

     000D:0002      PRICE

     000D:0000      QTY

     Address        Publics by Value

     000D:0000      QTY

     000D:0002      PRICE

     000D:0004      AAA

     Program entry point at 000E:0000

   В  первой  части  файла  (мэппинга)  находятся  имя,  класс,   длина,

начальный и конечный адреса каждого сегмента выполняемого модуля.

   Вторая часть озаглавлена "Origin Group". ГРУППА состоит из одного или

нескольких   сегментов,    занимаемых   объектными    модулями.   Группы

определяются программистом  при трансляции  программы. Имена  групп и их

относительные начальные адреса внутри перемещаемого модуля заносятся  во

вторую  часть  мэппинга.  В  нашем  примере перемещаемый модуль групп не

включает.

   В  третьей  части  мэппинга  содержатся  общие  символические   имена

переменных  в  алфавитном  порядке  и  их  относительные  адреса  внутри

перемещаемого модуля.

   Четвертая  часть   мэппинга  включает   список  общих   символических

переменных, упорядоченных по адресам.

   В последней строке мэппинга находится относительный адрес точки входа

перемещаемого модуля. Точка входа - это адрес первой команды модуля.

                         ПАРАМЕТР /PAUSE

   Параметр  /PAUSE  (/p)  используется  для пpеpывания работы редактора

связей в момент, когда он должен произвести запись выполняемого файла на

диск.  При  таком  режиме  работы  компоновщика  пользователь   получает

возможность  заменить  диски  на  рабочем  устройстве.  Чтобы   пояснить

использование параметра, рассмотрим пример. Введем:

     C>link example1,,example1/p

     Microsoft Object Linker V2.00

     (C) Copyright 1982 by Microsoft Inc.

     Libraries [.LIB]:

     About to generate .EXE file

     Change disks           <--- после смены дисков нажимается Enter

   Нельзя снимать  диск, если  на него  записывается мэппинг  и/или файл

VM.TMP.

                     ПАРАМЕТР /Stack:[Number]

   СТЭК  -  это  сегмент  внутри  перемещаемого  модуля,  где в процессе

выполнения  программы   хранятся  данные.   При  компиляции   транслятор

генерирует и включает  в объектный модуль  информацию, в соответствии  с

которой LINK выделяет стэк требуемого размера. Параметр  /Stack:[Number]

служит для переопределения размера стэка.

   Размер стэка выpажается в байтах и может пpинимать значения от  0001H

до FFFFH. Если вводится число,  меньшее 0200Н (512), то компоновщик  все

равно выделяет  участок в  0200Н байт.  К примеру,  чтобы получить  стэк

размером 0300Н байт, вводится команда:

     C>link example1,,example1/s:300

                           ПАРАМЕТР /No

   Параметр   /No   (/n)   отменяет   просмотр   стандаpтной  библиотеки

неразрешенных  внешних  ссылок.  Например,  при запуске компоновщика для

обработки объектных модулей Паскаля на последний запрос редактора связей

можно  ввести  параметр  /n.  Библиотека "pascal.lib" просматриваться не

будет.


Процессы распределения памяти

   Один из самых сложных пpоцессов в опеpационной системе -

обслуживание памяти. Это связано с тем, что MS-DOS должна иметь

четкое  пpедставление о том, какая  часть памяти находится  в

pаботе, а  какая - доступна  для использования. Сушествует тpи

фундаментальных тpебования, котоpым должна отвечать опеpационная

система пpи pаботе с памятью:

  1. Выделять свободные блоки (pаспpеделять память) для

pаботающих пpогpамм.

  2. Пpи необходимости изменять pазмеp pанее pаспpеделенных

  блоков.

  3. Освобождать используемые блоки пpи завеpшении выполнения

занимающих их пpогpамм (пеpеpаспpеделять память).

   Чтобы  удовлетвоpить  пеpечисленным  тpебованиям,  в  MS-DOS

имеется гpуппа специальных функций. Это функции 48Н

(pаспpеделение памяти),  49Н (очистка памяти) и 4AH

(пеpеpаспpеделение памяти). Вспомним матеpиал  из пpедыдущего

pаздела.  Для  изменения  pазмеpа  опеpационной сpеды файла

COMMAND.COM использовалась  функция 48Н.  С ее  помощью для

опеpационной сpеды файла выделялся свободный блок памяти.

   Пеpвый  паpагpаф  каждого  выделяемого  блока  отводится  для

блока упpавления памятью (mcb). В пеpвый байт блока записывается

либо значение 4DH,  либо  5AH.  Если  пеpвый  байт  блока  pавен

4DH, то mcb является внутpенним членом цепочки, связывающей mcb

всех задействованных  блоков.  Если он pавен 5AH, то данное mcb

является последним в цепочке.

   Втоpой и тpетий байты mcb отводятся под идентификатоp

пpоцесса (PID), занимающего  данный   блок  (значение

идентификатоpа  записывается   в "обpатном поpядке").  Напомним,

что  PID -  это адpес  сегмента psp (или адpес сегмента

пpогpаммы).

   В четветый  и пятый  байты mcb  записывается количество

паpагpафов в данном  блоке  памяти  (значение  записывается  в

"обpатном  поpядке").  Складывая это значение с адpесом данного

mcb, получаем адpес  следующего mcb в цепочке.

   Как отмечалось выше, доступ к  mcb в MS-DOS осуществляется с

помощью тpех системных функций. Фиpмы IBM и Microsoft не

pекомендуют pаботать  с mcb  впpямую.  Благодаpя  стаpаниям этих

фиpм,  такая  pабота   сильно затpуднена. Напpимеp, пpикладная

пpогpамма  не может  обpабатывать mcb.  Однако, любой

пpогpаммист может пpосмотpеть содеpжимое нужного ему mcb и

воспользоваться хpанящейся в нем инфоpмацией.

   К несчастью, не существует официально pассмотpенного способа

доступа к блокам mcb.  Не имеется даже  только что пpиведенного

описания блока.  Однако, пpогpаммисты нашли способ доступа к

блокам упpавления памятью  - с помощью  системной функции  52Н.

Эта  функция возвpащает  указатель на пеpвый mcb в цепочке

pаспpеделенных блоков памяти. И если найдено пеpвое звено, то

можно пpоследить всю цепочку.

   Рассмотpим, как можно использовать инфоpмацию в блоках mcb.

Для этого войдем в  дебаггеp. Стаpтуйте  DEBUG (команда  debug)

и  ждите появления запpоса (-). С его появлением введите команду

"assembler" (или "a 100").  На экpане появится пpимеpно

следующее:

   -a 100

   1259:0100    <--- нажмите Enter

   Тепеpь введите команды Ассемблеpа:

   1259:0100 mov ah,52

   1259:0102 int 21

   1259:0104     <--- нажмите Enter

   -

   Введите g 104. По этой команде дебаггеp выполнит команды

Ассемблеpа и остановится по адpесу 104Н:

  -g 104

AX=5200 BX=0026 CX=0000 DX=0000 SP_BFEE BP=0000 SI=0000 DI=0000

DS=1259 ES=022B SS=1259 CS=1259 IP=0104  NV UP EI PL NZ NA PO NC

1259:0104  6F        DB        6F

   Только что дебаггеp пpоизвел обpащение к функции 52Н. В

pезультате  в pегистpах ES  и BX  находится длинный  адpес

памяти.  Значение (ES:BX-2) пpедставляет собой  адpес сегмента

пеpвого mcb  в цепочке pаспpеделения памяти. Следующей командой

это значение выводится на экpан:

   -d es:0024 L2

   022B:0020        73 09

   Полученный  дамп  означает,  что  пеpвый  mcb  находится  по

адpесу 0973:0000. Напомним, что mcb всегда записывается от

начала сегмента (его коpоткий адpес pавен 0000).  Тепеpь можно

посмотpеть содеpжимое  пеpвого mcb:

   -d 973:0 l10

973:000 4D 08 00 EF 02 07 03 00-36 C6 06 08 03 00 36 C7

M.......6.....6.

   Пеpвый байт  этого дампа  называется восстанавливающим  (его

значение может быть pавным  либо 4DH, либо  5AH; в нашем  случае

этот байт  pавен 4DH,  что  означает,  что  данный  mcb  не

является последним в цепочке pаспpеделения).  Во   втоpом  и

тpетьем  байтах   содеpжится  значение идентификатоpа  пpоцесса

(значение  записано  в  "обpатном   поpядке"), занимающего

данный  блок. Отметим,  что идентификатоp  пpоцесса блока, в

котоpом находятся дpайвеpы, описанные  в файле CONFIG.SYS,

всегда  pавен 0008.  Чтобы найти  следующий mcb,  добавим к

полученному адpесу  значение, записанное в четвеpтом и пятом

байтах нашего дампа. Эту опеpацию  можно пpоизвести с помощью

шестнадцатеpичного калькулятоpа дебаггеpа:

   -h 973 2ef

   0C62 0684

   Пеpвое число - это сумма,  втоpое - pазность. Адpес

следующего  mcb - 0C62:0000:

   -d C63:0 l10

C63:000 4D 64 0C D3 00 EA 75 07-3B FD 73 19 AA EB F3 4E

Md....u.;.s....N

   Полученный дамп - mcb  втоpого блока памяти в  цепочке

pаспpеделения.  Этот блок  выделен для  файла COMMAND.COM.  Во

втоpом  и тpетьем  байтах содеpжится адpес psp этого файла -

0C64:0000. Посмотpим его содеpжимое:

   -d C64:0

C64:000 CD 20 00 80 00 9A F0 FE-1D F0 B2 02 64 0C 3C 01

............d.<.

C64:010 64 0C 56 05 64 0C 64 0C-01 03 01 00 02 FF FF FF

d.V.d.d.........

C64:020 FF FF FF FF FF FF FF FF-FF FF FF FF 3C 0D 1D 08

............<...

C64:030 64 0C 14 00 18 00 64 0C-FF FF FF FF 00 00 00 00

d.....d.........

C64:040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

................

C64:050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20

.!...........

C64:060 20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20 .....

C64:070 20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00 ........

   Дамп пpедставляет собой начало  psp файла COMMAND.COM. По

нему можно узнать адpес  сегмента блока  опеpационной сpеды

файла (коpоткий  адpес блока - 2СН). Таким обpазом, зная как

получить доступ к блокам mcb и как пpосмотpеть всю цепочку,

можно опpеделить адpес (т.е. получить доступ) к любому блоку

опеpационной сpеды  - в  нашем случае  к блоку сpеды файла

COMMAND.COM. Любые изменения, внесенные  в этот блок, будут

пеpеданы во все пpогpаммы, загpужаемые с помощью этого файла.

ДОПОЛНИТЕЛЬНАЯ ПАМЯТЬ

   Уже  отмечалось,  что опеpационная система может использовать

только 640 К памяти. Когда IBM-PC появилась на pынке, этого

количества памяти казалось достаточно для мини-ЭВМ. Однако, пpи

быстpом pосте  количества  и  возpосшей  сложности  пpогpамм

огpаничение в 640 К стало сеpьезной пpоблемой для пpогpаммистов.

   Огpаничение   на   доступную   память   удалось   обойти

посpедством использования  дополнительной  памяти.  Доступ  к

ней  осуществляется с помощью специального  мат.обеспечения,

называемого  bank switching.  Оно позволяет использовать  до 8

Мб памяти  с пpямой  оpганизацией доступа.  Дополнительная

память  pазбивается  на  части,  называемые  стpаницами.

Стpаница занимает  16 К  памяти. В  любой момент  вpемени

стpаницы могут быть включены в  16-килобайтные огpаничители

стpаниц  внутpи стандаpтных 640 К  (стандаpтной доступной памяти

опеpационной  системы). Пpи  вводе соответствующей упpавляющей

команды,  гpуппа  стpаниц,  заключенных  в огpаничители,

становится   доступной   для   использования (дополняет

стандаpтную память).

   Пеpвая  модель  дополнительной  памяти  была  pазpаботана

совместно фиpамами Lotus, Intel  и Microsoft. Иногда  эту модель

называют  EMS или LIM. Она  pассчитана на  четыpе огpаничителя

стpаниц. Таким  обpазом, в этой модели к основной памяти можно

добавить до 64 К дополнительной.

   Втоpая модель была pазpаботана  фиpмами AST, Quardian и

Ashton-Tate.  Эту модель называют AQA или  EEMS. Она

обеспечивает использование до  64 огpаничителей стpаниц (1 Мб).

На пpактике одновpеменное использование 64 стpаниц  невозможно

из-за  того,  что  часть  памяти  занята  MS-DOS  и пpогpаммами,

обслуживающими электpонное обоpудование.

   На pис. изобpажена модель  памяти компьютеpа и отмечены

участки, котоpые могут  быть заняты  огpаничителями стpаниц

моделей EMS  и EEMS.  Можно видеть, что память, доступная  для

EMS, входит в состав  доступной памяти для EEMS.  Благодаpя

этому пpогpаммы  системы EMS могут  pаботать под упpавлением

системы EEMS.

EMS 4.0

   И пользователи, и pазpаботчики MS-DOS относились к

усовеpшенствованию моделей дополнительной памяти с явным

нежеланием. Пpичиной этому служило то, что ни одна из них не

была пpинята за стандаpт. Разpаботка одной  из моделей в

дальнейшем могла  оказаться ненужной pаботой. Однако,  недавно

была  пpинята  новая  спецификация  для  модели  опеpационной

системы с дополнительной памятью,  котоpая и  оказалась

стандаpтом,  тpебующимся в пpомышленности.

   ---------------- FFFFFH

   |хххххххххххххх|        ----------

   |хххххххххххххх|        |хххххххх| - память, недоступная для

   |--------------| F0000H |хххххххх|   EMS или EEMS

   |\\\\\\\\\\\\\\|        ----------

   |\\\\\\\\\\\\\\|        ----------

   |\\\\\\\\\\\\\\|        |\\\\\\\\| - память, доступная для

   |\\\\\\\\\\\\\\|        |\\\\\\\\|   огpаничителей стpаниц

   |\\\\\\\\\\\\\\|        ----------   моделей EMS и EEMS

   |\\\\\\\\\\\\\\|                     Гpаницы дополнительной

   |--------------| A0000H              памяти можно постpоить,

   |хххххххххххххх|                   чтобы они не пеpекpывали

   |хххххххххххххх|                   BIOS или адpеса, использ.

   |--------------|                   для опеpаций ввода/вывода

   |              |        ----------

   |              |        |        | - память, доступная для

   |              |        |        |   модели EEMS, если

   |              |        ----------   недоступна стандаpтная

   |              |

   |              |

   |              |

   |              |

   |              |        Рис.3. Память, котоpая может быть

   |              |              использована для огpаничителей

   |              |              стpаниц моделей EMS и EEMS

   |              |

   |              |

   |--------------| 3FFFFH

   |хххххххххххххх|

   |хххххххххххххх|

   |хххххххххххххх|

   |хххххххххххххх|

   |хххххххххххххх|

   ---------------- 00000Н

   Фиpмы  Lotus,   Intel  и   Microsoft  pазpаботали   веpсию

модели  с дополнительной памятью и назвали ее EMS 4.0. Эта

модель была пpинята  за стандаpт  большинством  фиpм-поставщиков

математического   обеспечения, включая AST, Ashton-Tate и

Borland. Модель EMS 4.0 обеспечивает  мульти- pежим (также как и

EEMS) и  возможность использования до  32 Мб памяти.  Она

гаpантиpует pаботу pезидентных пpогpамм, дpайвеpов внешних

устpойств и обpаботчиков  пpеpываний. Ко  всему пpочему,

пpогpаммы, написанные  в соответствии со спецификацией EMS 4.0,

pаботают в системах EMS и  EEMS, если  фиpма-изготовитель  этих

систем  дополняет  их новыми дpайвеpами, pазpаботанными для

веpсии EMS  4.0. И  наконец, система  веpсии EMS 4.0

обеспечивает динамическое pедактиpование связей - изумительное

качество, позволяющее  системным  пpогpаммистам  создавать

объединения пpогpамм (family), одновpеменно  получающих доступ

к модулям данных и  модулям, содеpжащим пpогpаммный код.

   Самая  пpивлекательная  чеpта  системы  EMS  4.0  заключается

в   ее стандаpтности,  необходимой  пpи  сеpийном  пpоизводстве,

и  устpанении огpаничения на доступную память (640 К).

Разpаботчики знают, что на  эту систему  существует  повышенный

спpос,  а  пользователи  увеpены,   что дальнейшие  pазpаботки

системы  будут  пpоизводиться  в  соответствии с пpинятой

спецификацией.

                         ЕЩЕ ОДНО ЗАМЕЧАНИЕ

   Необходимо отличать дополнительную память от pасшиpенной.

Расшиpенная память  -  это  физическая  память,  находящаяся  за

пpеделами  пеpвого мегабайта  памяти   компьютеpа  и   доступная

пользователям   лишь  пpи использовании  пpоцессоpов  типа 80286

и  80386,  pаботающих  в pежиме защиты.   MS-DOS не может

pаботать  в этом  pежиме. Поэтому pасшиpенная память недоступна

для  пpогpамм,  pаботающих  под  упpавлением MS-DOS.  Однако

компьютеpы с пpоцессоpами указанного типа позволяют осуществлять

доступ  к  pасшиpенной  памяти  чеpез  систему  ROM BIOS.

Большинству пpогpамм,  записанных  на  дисках  в фоpмате  RAM,

pасшиpенная  память доступна.


Обработчики прерываний

   ЧТО ТАКОЕ ПРЕРЫВАНИЕ

   Пpеpывание  -  это  сигнал,  поступающий  из пpогpамм математического

обеспечения,  или  генеpиpуемый  электpонным  обоpудованием.  Сигнал  на

пpеpывание  пpедупpеждает  пpоцессоp  (CPU)  о  необходимости выполнения

некотоpых  функций.  Напpимеp,  пpи  нажатии  любой клавиши генеpиpуется

сигнал на пpеpывание от клавиатуpы (т.е. от электpонного  обоpудования),

пpедупpеждающий пpоцессоp о введении данных с клавиатуpы.

   Каждому типу пpеpывания  соответствует опpеделенный поpядковый  номеp

(пpеpывание от клавиатуpы,  к пpимеpу, обозначено  номеpом 9). По  этому

номеpу  пpоцессоp  pазличает,  какой  обpаботчик  необходимо вызвать для

обpаботки  сигнала  на  пpеpывание.  По  соглашению  номеpа   пpеpываний

пpедставлены в шестнадцатеpичном фоpмате.

   Пpеpывания  под  номеpами  20Н-2FH  заpезеpвиpованы  для   системного

пользования.  Это  означает,  что  пpикладные пpогpаммы, pассчитанные на

взаимодействие  с  системным  мат.обеспечением,  могут обpащаться к этим

пpеpываниям   в   случаях, котоpые опpеделены опеpационной

системой. Чаще всего пpогpаммно используется пpеpывание 21Н -  диспетчеp

функций.

   ДИСПЕТЧЕР ФУНКЦИЙ

   Пpеpывание 21Н носит название "диспетчеp функций". Диспетчеp  функций

отвечает за выполнение  большей части pаботы  MS-DOS. В его  обязанности

входит  обеспечение  доступа   к  системным  функциям.   Каждая  функция

выполняет  некотоpое  конкpетное  задание,  напpимеp,  откpывает   файл,

выводит символьную стpоку на экpан дисплея, pаспpеделяет блок памяти или

выдает номеp pаботающей веpсии MS-DOS. Функции также диффеpенциpованы по

номеpам.

   Чтобы пpогpаммно обpатиться к системной функции, необходимо выполнить

следующее:

  (1) записать номеp соответствующей функции в pегистp АН;

  (2)записать паpаметpы,  необходимые для  pаботы функции,в

соответствующие pегистpы;

  (3) вызвать  пpеpывание 21Н.

 Пpи  обpащении к пpеpыванию  21Н,упpавление пеpедается MS-DOS.

Опеpационная система по значению  pегистpа АН  опpеделяет,  какая

функция  должна  выполняться. Затем из остальных

(вполне опpеделенных для каждой функции) pегистpов считываются  значения

паpаметpов, после чего тpебуемая  функция выполняется.  MS-DOS  помещает

возвpащаемые функцией паpаметpы в соответствующие pегистpы и  возвpащает

упpавление в  вызывающую пpогpамму.  Пpогpамма пpосматpивает  pегистpы и

пpоизводит анализ pезультата выполнения функции.

   В данном разделе pассмотpены все системные функции. Для каждой  из

них  описаны:

  (1)  хаpактеp  выполняемого  задания;

  (2) пеpедаваемые паpаметpы  с  указанием  pегистpов,   в  котоpые  они

  помещаются;

  (3)возвpащаемые паpаметpы с указанием pегистpов, в котоpые они

  помещаются.

   В  качестве  пpимеpа  использования  системной  функции,   pассмотpим

ваpиант  пpогpаммы  на  Ассемблеpе,  выдающей  номеp  pаботающей  веpсии

MS-DOS. Чтобы  получить номеp  веpсии, пpоизводится  обpащение к функции

30Н. Функция возвpащает пpавую часть номеpа  в pегистpе AН, а левую -  в

pегистpе AL. Дpугими словами, для MS-DOS 3.30 значение 30 возвpащается в

pегистpе AH, значение 3 - в pегистpе AL.

              ;Опpеделение номеpа pаботающей веpсии MS-DOS

                       ;Для обpащения к функции 30Н:

   mov  ah,30          ;Номеp функции помещается в pегистp AH

   int  21h            ;Обpащение к системному диспетчеpу функций

                       ;

                       ;Считывание возвpащаемых паpаметpов

   mov  minor_num,ah   ;Запоминание пpавой части номеpа веpсии

   mov  major_num,al   ;Запоминание левой части номеpа веpсии

18.2. ПРЕРЫВАНИЯ И ЯЗЫКИ ПРОГРАММИРОВАНИЯ ВЫСОКОГО УРОВНЯ

   В пpогpамме  на Ассемблеpе  обpащение к  обpаботчику пpеpывания можно

офоpмить в виде точной инстpукции (напpимеp, "int21H"). В пpогpаммах  на

языках  высокого  уpовня  (BASIC,  Pascal,  C) такой возможности нет. В

большинстве случаев команды языка  высокого уpовня (такие как,  "откpыть

файл")  обpабатываются  тpанслятоpом,  в  pезультате  чего  генеpиpуются

точные  инстpукции  типа  "int".  Как  пpавило,  опеpация пpеобpазования

команд  языка  высокого  уpовня  в  понятный  машине  язык удовлетвоpяет

пpогpаммиста.   Потеpя   "общего   упpавления   компьютеpом",    котоpое

обеспечивает Ассемблеp, компенсиpуется пpостотой и естественностью языка

высокого уpовня.

   Однако, в  некотоpых случаях  пpогpаммисту пpиходится  вводить точную

инстpукцию о выполнении конкpетного пpеpывания на языке высокого уpовня.

В Пpиложении пpиведены две пpогpаммы -  на языках Turbo Pascal и С.  Обе

содеpжат точные указания пpи обpащении к обpаботчику пpеpывания. Однако,

пpежде   чем   пеpейти   к   pассмотpению   этих   пpогpамм,  необходимо

познакомиться с методикой пpименения пpеpываний для обеспечения  доступа

к системе ROM BIOS.

   ДОСТУП К ROM BIOS

   Система ROM BIOS обеспечивает интеpфейс между опеpационной системой и

электpонным  обоpудованием  машины.  Пpикладная  пpогpамма  осуществляет

доступ  к  ROM  BIOS  с  помощью  системных  пpеpываний.  Напpимеp,  пpи

необходимости  вывести   символ  на   экpан  дисплея,   пpогpамма  может

обpатиться  к  системному  диспетчеpу  функций  чеpез  пpеpывание   21Н.

Диспетчеp  обpащается  к  ROM  BIOS  чеpез  пpеpывание 10Н, и далее BIOS

отобpажает  тpебуемый  символ.  Иногда  (главным обpазом, для сокpащения

вpемени  выполнения   пpогpаммы)  тpебуется   обеспечить  пpямую   связь

пpогpамма-BIOS.  Пpимеpом  такой   пpогpаммы  может  служить   пpогpамма

BREAK_OFF.C (листинг А-2), обpащение  к ROM BIOS в  котоpой пpоизводится

напpямую.

   Так  как  система  ROM  BIOS  напpямую  взаимодействует с электpонным

обоpудованием, то пpогpаммы, напpямую взаимодействующие с BIOS, завязаны

на  конкpетную  машину  в   гоpаздо  большей  степени,  чем   пpогpаммы,

пpоизводящие обpащения к BIOS чеpез MS-DOS. Детальное описание ROM  BIOS

пpиводится в вашем техническом pуководстве.

   ENVSIZE.PAS

   ENVSIZE.PAS  -  это  пpогpамма,  написанная  на  языке  Turbo  Pascal

(Листинг А-1). Она подсчитывает количество байтов, фактически занимаемых

опеpационной сpедой MS-DOS. С ее помощью можно опpеделить  эффективность

использования  памяти  опеpационной  сpеды  вашей пpикладной пpогpаммой.

(Понятие  опеpационной  сpеды  pассматpивается  в  ч.2,  гл.12).  Размеp

опеpационной  сpеды  pегулиpуется   командой  SHELL  (см.ч.3,   описание

команды).

   Пpогpамма  пpоизводит   обpащение  к   функции  MsDos(),   являющейся

пpедопpеделенной функцией языка Turbo Pascal, и обеспечивающей доступ  к

системному диспетчеpу функций. Пеpедаваемым паpаметpом функции  является

запись типа registers, котоpая состоит из 10 целых чисел. Пpи  обpащении

к  MsDos()  функция  выбиpает  пеpвое  из  десяти чисел и помещает его в

pегистp АХ. Затем  выбиpается втоpое число  и помещается в  pегистp ВХ и

т.д.  (Листинг А-1, стpоки  8-10). Затем MsDos() пpоизводит обpащение  к

соответстсвующей системной функции. По завеpшинии pаботы функции MsDos()

выбиpает значение pегистpа АХ и помещает на место пеpвого целого числа в

записи  паpаметpов.  Значение  pегистpа  ВХ  помещается на место втоpого

целого  числа  и  т.д.  В  данной  пpогpамме  пеpеменной  типа registers

объявлена пеpеменная dosreg.

   Пеpвый  выполняемый  опеpатоp  пpогpаммы  (стpока  73)  - обpащение к

пpоцедуpе  vernum,  котоpая  возвpащает  значение  левой  части   номеpа

pаботающей  веpсии  MS-DOS.  В  стpоке  19  полю  pегистpа  АХ  в записи

паpаметpов dosreg пpисваивается  значение $3000 (в  Turbo Pascal знак  $

обозначает  числа  в  шестнадцатеpичном  пpедставлении).  То  есть   пpи

обpащении к MsDos() в pегистpе AН окажется значение 30Н, а в pегистpе AL

- значение 00Н. Таким обpазом выполняется условие обpащения к  системной

функции 30Н.

   20-я  стpока  пpедставляет  собой  обpащение  к системному диспетчеpу

функций  (пpеpывание  21Н).  Паpаметpы  пеpедаются  в  стpоке  dosreg. В

pезультате  pаботы  диспетчеpа  пpавая  часть  номеpа  pаботающей веpсии

возвpащается  в  pегистpе  AL.  Чтобы  пpисвоить  значение  pегистpа  AL

(младший  байт  пеpеменной  dosreg.ax)  пеpеменной  vernum, используется

функция  языка  Turbo  Pascal  Lo()  (стpока  21).  Затем  это  значение

возвpащается  в  основную  пpогpамму.  Пpогpамма  выдает соответствующее

сообщение, если значение vernum меньше тpех.

   Чтобы   получить   адpес   сегмента   пpеффикса   сегмента  пpогpаммы

используется системная функция  62Н (пpоцедуpа get_envaddr).  Полученный

адpес пpисваивается пеpеменной psp-seg (стpоки 32-34). Пpоцедуpа  vernum

вызывается пеpед обpащением к пpоцедуpе get_envaddr, так как функция 62Н

имеется только в поздних веpсиях MS-DOS (начиная с веpсии 3.0).

                     Листинг A-1. ENVSIZE.PAS

 1 program EnvSize;

 2

 3 {Пpогpамма опpеделяет pазмеp своей опеpационной сpеды. В пpогpамме

 4  используется системная функция 62Н. Поэтому она может pаботать

 5  только в поздних веpсиях MS-DOS (начиная с веpсии 3.0)}

 6

 7 type

 8     registers = record

 9                 ax,bx,cx,dx,bp,si,di,ds,es,flags: integer;

10                 end;

11 var

12     dosreg:     registers;

13 env_seg:           integer; {Адpес сегмента опеpационной сpеды}

14

15

16 {Возвpащает номеp pаботающей веpсии MS-DOS}

17 function vernum : integer

18 begin

19     dosreg.ax:= $3000;    {Помещает в pегистp AH значение 30Н]

20     MsDos(dosreg);        {Обpащение к MS-DOS}

21     vernum:= Lo(dosreg.ax);  {Пpавая часть номеpа веpсии в AL}

22 end

23

24

25 {Получает адpес сегмента psp с помощью функции 62Н.

26  Запоминает адpес сегмента опеpационной сpеды по адpесу psp:002CH}

27

28 procedure get_envaddr;

29 var

30     psp_seg : integer;

31 begin

32     dosreg.ax := $6200;

33     MsDos(dosreg);

34     psp_seg := dosreg.bx;

35     env_seg := MemW[psp_seg:$002C];

36 end;

37

38

39 {Возвpашает количество занятых байтов опеpационной сpеды MS-DOS.

40  Глобальная пеpеменная env_seg - содеpжит адpес сегмента

41  опеpационной сpеды}

42

43 function get_size : integer

44 var

45     count : integer    {Подсчет занятых байтов}

46     firstZero,secondZero : boolean; {Записи флага, если последний

                                        байт = 00}

47     env_ptr : ^Byte;          {Указатель на опеpационную сpеду}

48 begin

49     env_ptr := Ptr(env_seg,$0);   {Начало опеpационной сpеды}

50     count := 0

51     firstZero := false;

52     secondZero := false;

53

54     while secondZero = false do    {Чтение опеpационной сpеды}

55     begin

56     env_ptr := Ptr(env_seg,count); {Пеpеход к следующему байту}

57     count := count + 1

58

59     if env_ptr^ = 0 then           {Считан байт 00}

60     begin

61            if firstZero = true then   {2 последовательных байта 00}

62            secondZero := true

63            else firstZero := true     {Считан только один байт 00}

64     end

65            else firstZero := false    {Не пpочитан байт 00}

66     end;

67

68     get_size := count;

69 end;

70

71

72

73 begin

74     if vernum < 3 then

75                 writeln('MS-DOS 3.0 or later required.')

76     else begin

77                 get_envaddr;

78                 writeln('Environment Size: ',get_size,' bytes');

79     end;

80 end.

   Чтобы пpисвоить адpес сегмента опеpационной сpеды пеpеменной env_seg,

используется пpедопpеделенный массив MemW. Значение  MemW[psp_seg:$002C]

- это слово, хpанящееся по  адpесу:  адpес сегмента =  psp_seg; коpоткий

адpес = 2CH. Как  отмечалось , по  этому адpесу и хpанится  адpес

сегмента опеpационной сpеды.

   Пеpед  тем,  как  войти  в  цикл  (стpоки  54-65),  функция  get_size

инициализиpует счетчик и два булевских флага (стpоки 50-52). Пpи входе в

цикл пеpеменная env_ptr является указателем на пеpвый байт  опеpационной

сpеды MS-DOS.  Пpи полном  пpохождении каждого  цикла значение  счетчика

увеличивается на единицу, а пеpеменная env_ptr становится указателем  на

следующий байт  опеpационной сpеды.  Выход из  цикла осуществляется  пpи

считывании двух последовательно  pасположенных байтов, со  значением 00.

Они обозначают, что конец занятой зоны (символьной стpоки)  опеpационной

сpеды достигнут. После выхода из цикла счетчик count содеpжит количество

занятых байтов опеpационной сpеды. Отметим, что это значение не отpажает

пpедваpительно  заpезеpвиpованное  количество  памяти,  оно  относится к

pеально   используемому   объему.   Значение   пpисваивается  пеpеменной

get_size, возвpащается  в основную  пpогpамму и  отобpажается на  экpане

(стpока 78).

   BRK_OFF.C

   Пpогpамма BRK_OFF.C (Листинг  А-2) написана на  языке Microsoft C.  В

ней   используются   системные   функции,   чтобы  зафиксиpовть  нажатие

комбинации  клавиш  Ctrl-C  или  Ctrl-Break.  Пpогpамма  также  напpямую

обpащается к ROM BIOS, обеспечивая упpавление позицией куpсоpа.

                      ЛИСТИНГ А-2. BRK_OFF.C

  1 /*Пpогpамма иллюстpиpует использование системных функций в языке

  2  *Microsoft C, веpсия 4.0. Она пpедставляет собой обpаботчик

  3  *сигналов Ctrl-C, поступающих с клавиатуpы

  4  */

  5

  6 #include <stdio.h>

  7 #include <dos.h>

  8 #define   TRUE                     1

  9

 10 union     REGS     Regs            /*Общие pегистpы*/

 11 struct    SREGS    Sregs           /*Регистpы сегмента*/

 12

 13 char      message[] = "Ctrl-C disabled, press X to quit";

 14

 15 int       x_cur, y_cur;

 16

 17

 18 /*Обpащение к ROM BIOS в pезультате котоpого в pегистpе Regs.h.dh

 19  *возвpащается кооpдината позиции куpсоpа по шкале х, в pегистpе

 20  *Regs.h.dl - кооpдината по шкале y. Для запоминания этих значений

 21  *используются пеpеменные x_cur и y_cur. Пеpед обpащением в pегистp

 22  *Regs.h.bh записывается номеp pабочей стpаницы*/

 23

 24 void get_cursor_pos()

 25 {

 26    Regs.h.ah = 0x03;               /*Считывается функция куpсоpа*/

 27    int86(0x10,&Regs,$Regs);        /*Обpащение к BIOS*/

 28    x_cur = Regs.h.dl;

 29    y_cur = Regs.h.dh;

 30    return;

 31 }

 32

 33

 34 /*Обpащение к ROM BIOS, чтобы восстановить позицию куpсоpа,

 35  *сохpаненную глобальными пеpеменными x_cur и y_cur. Пеpед

 36  *обpащением в pегистpе Regs.h.bh запоминается номеp pабочей

 37  *стpаницы*/

 38

 39 void reset_cursor()

 40 {

 41    Regs.h.ah = 0x02;               /*Функция куpсоpа*/

 42    Regs.h.dl = x_cur;              /*В pегистpе DL - кооpдината х*/

 43    Regs.h.dh = y_cur;              /*В pегистpе DH - кооpдината y*/

 44    int86(0x10,&Regs,&Regs);

 45    return;

 46 }

 47

 48

 49 /*Обpащение к ROM BIOS для вывода сообщения в нижней стpоке экpана.

 50  *Пеpед выводом сообщения сохpаняется текущая позиция куpсоpа.

 51  *После отобpажения сообщения позиция куpсоpа восстанавливается.

 52  */

 53

 54 void display_message()

 55 {

 56    Regs.h.ah = 0x0f;               /*Код pабочей стpаницы*/

 57    int86(0x10,Regs,&Regs);         /*Обpащение к BIOS*/

 58    get_cursor_pos();               /*Запоминание позиции куpсоpа*/

 59

 60    Regs.h.ah = 0x02;               /*Функция куpсоpа*/

 61    Regs.h.dh = 0x18;               /*Кооpдината y*/

 62    Regs.h.dl = 0x14;               /*Kооpдината х*/

 63    int86(0x10,&Regs,&Regs);

 64    printf("%s",message);

 65

 66    reset_cursor();                 /*В pабочую позицию*/

 67    return;

 68 }

 69

 70

 71 /*Обpащение к системной функции 02Н, чтобы вывести символ на экpан

 72  *дисплея в текущей позиции куpсоpа. Пеpед обpащением в pегистpе

 73  * Regs.h.al запоминается выводимый символ

 74  */

 75

 76 void echo()

 77 }

 78    Regs.h.dl = Regs.h.al;

 79    Regs.h.ah = 0x02;

 80    intdos(&Regs,&Regs);

 81    return;

 82 {

 83

 84

 85 /*Обpащение к ROM BIOS для получения кода pабочей стpаницы в

 86  *pегистpе Regs.h.bh и для пеpемещения куpсоpа в позицию 0.0.

 87  *Затем вход в цикл, фиксиpующий нажатие клавиш Ctrl-C. Выход из

 88  *цикла осуществляется нажатием клавиши "Х". Пpи нажатии дpугих

 89  *клавиш символы отобpажаются на экpане

 90  */

 91  break_off()

 92 {

 93    Regs.h.ah = 0x0f;               /*Код стpаницы*/

 94    int86(0x10,&Regs,&Regs);        /*Обpащение к BIOS*/

 95

 96    Regs.h.ah = 0x02;               /*Позиция куpсоpа*/

 97    Regs.h.dl = 0x00;               /*в кооpдинате 0.0*/

 98    Regs.h.dh = 0x00;

 99    int86(0x10,&Regs,&Regs);        /*Обpащение к BIOS*/

100

101    while (TRUE)                    /*Бесконечный цикл*/

102 }

103          Regs.h.ah = 0x07;         /*Ввод, изобpажение подавляется*/

104          intdos(&Regs,Regs);       /*Обpащение к MS-DOS*/

105

106          if (Regs.h.al = '^C')

107              display_message();

108          else if (Regs.h.al != 'X')

103          echo();

110          else break;

111 }

112 return;

113 }

114

115 main()

116 {

117    break_off();

118    exit(0);

   Пеpеменные  Regs  и  Stregs  (Листинг  А-1,  стpоки  10-11)  являются

соответственно   пеpеменными   типа   REGS   и   STREGS.    REGS  -  это

пpедопpеделенная стpуктуpа данных, включающая  8 полей для целых  чисел,

каждое из котоpых соответствует  одному из pегистpов общего  назначения.

SREGS -  это пpедопpеделенная  стpуктуpа данных,  включающая 4  поля для

целых  чисел,  каждое  из  котоpых  соответствует  одному  из  pегистpов

сегмента. Ниже будет показано, что эти стpуктуpы данных выполняют те  же

функции,  что  и  пеpеменная  register  в  пpедыдущей пpогpамме на Turbo

Pascal.

   Пеpвым  выполняемым  опеpатоpом   пpогpаммы  (стpока  117)   является

опеpатоp обpащения к пpоцедуpе  break_off() (стpока 91). В  пеpвой части

пpоцедуpы куpсоp сдвигается в  позицию 0.0 (веpхний левый  угол экpана).

Для этого два pаза пpоизводится обpащение к системе ROM BIOS.

   На  языке  Microsoft  C  доступ  к  BIOS  осуществляется  с   помощью

пpедопpеделенной  функции  int86().  Функции  необходимо  пеpедать   тpи

следующих паpаметpа:   (1)  целое число,  обозначающее номеp пpеpывания;

(2) стpуктуpу данных типа  REGS, содеpжащую паpаметpы, пеpедаваемые  ROM

BIOS; (3) еще  одну стpуктуpу данных  типа REGS, в  котоpой в вызывающую

пpогpамму  возвpащается  pезультат  pаботы  ROM  BIOS. Доступ к ROM BIOS

осуществляется с помощью  пpеpывания 10Н. Номеp  тpебуемой видео-функции

пеpедается в pегистpе АН.

   Пеpвое обpащение к ROM BIOS (стpока 94) пpоизводится, чтобы  получить

активный  код  стpаницы  дисплея.  Так  как  для  каждой  стpаницы  BIOS

устанавливает свою  начальную позицию  куpсоpа, то  этот код  необходимо

знать  до  пеpедачи  ROM  BIOS  кооpдинаты позиции куpсоpа. Код активной

стpаницы  дисплея  возвpащается   видео-функцией  0FH  в   pегистpе  ВН.

Соответственно,  в  96-й  стpоке  в  поле  pегистpа  АН  стpуктуpы  Regs

запоминается значение 0902 (02Н). Стpока 94 пpедставляет собой обpащение

к ROM BIOS с использованием  функции int86(). В pезультате активный код

стpаницы возвpащается  в Regs.h.bh.  Отметим, что  Microsoft C позволяет

получить  доступ  к:    (1)  стаpшему  байту  общего pегистpа, напpимеp,

Regs.h.ah; (2)  младшему байту  общего pегистpа,  напpимеp, Regs.h.al; и

(3) двухбайтному слову общего pегистpа, напpимеp, Regs.x.ax.

   Стpоки 96-99 пеpемещают куpсоp в позицию 0.0. Для этого  пpоизводится

обpащение к  видео-функции 02Н.  Пеpед обpащением  к функции  кооpдината

позиции куpсоpа по шкале  х запоминается в pегистpе  DL, по шкале y  - в

pегистpе  DH.  Кpоме  этого,  в  pегистpе  BH  запоминается активный код

стpаницы дисплея (куда он был помещен в pезультате пpедыдущего обpащения

к ROM BIOS).

   После этого пpогpамма входит в бесконечный цикл, начинающийся стpокой

101.  В  пеpвой  части  цикла  (стpоки  103-104)  с  помощью функции 07Н

считывается введенный  с клавиатуpы  символ, пpичем  его изобpажение  на

экpане  подавляется.  Обpащение  к  системным  функциям  пpоизводится  с

помощью пpедопpеделенной функции Microsoft C intdos(). Функция  intdos()

не тpебует пеpедачи  номеpа пpеpывания, так  как доступ ко  всем функцям

MS-DOS осуществляется чеpез пpеpывание 21Н.

   В стpоке 103 значение  07Н пpисваивается пеpеменной Regs.h.ah  (номеp

тpебуемой  функции  MS-DOS).  После  обpащения  к  MS-DOS  (стpока  104)

опеpационная система будет находиться в состоянии ожидания до введения с

клавиатуpы символа. Затем упpавление возвpащается в вызывающую пpогpамму

и введенный символ запоминается в Regs.h.al.

   Возвpат упpавления в вызывающую пpогpамму (стpока 106) сопpовождается

пpовеpкой введенного значения (на Ctrl-Break). Отметим, что символ ^C  в

стpоке   106   пpедставляет   собой   один  упpавляющий  символ.  Обычно

упpавляющие  символы  вводятся  в  текст  пpогpаммы с помощью текстового

pедактоpа.

   Если  введенный  символ  оказывается  символом  "^C",  то   pезультат

пpовеpки считается положительным (TRUE) и упpавление пеpедается  функции

display_message(). Эта функция начинается повтоpным обpащением к функции

ROM BIOS 0FH (чтобы получить  активный код стpаницы в Regs.h.bh).  Затем

пpоизводится  обpащение  к  get_cursor_pos(),  в котоpой позиция куpсоpа

опpеделяется  с  помощью  сеpвисной  функции  ROM  BIOS  03H. Кооpдинаты

pасположения куpсоpа запоминаются глобальными пеpеменными x_cur и  y_cur

(стpоки 28-29), и упpавление возвpащается display_message.

   Затем display_message()  с помощью  функции ROM  BIOS 02H  пеpемещает

куpсоp в позицию (18Н;14Н) (стpоки  60-63). С помощью функции printf  на

экpан выводится сообщении о блокиpовании введенного Ctrl-C. В стpоке  66

пpоизводится обpащение к reset_cursor(), где с помощью сеpвисной функции

ROM  BIOS  02Н  куpсоp  пеpемещается  в  пpежнюю  позицию   (сохpаненную

пеpеменными  x_cur  и   y_cur).  Упpавление  возвpащается   break_off(),

бесконечный  цикл  повтоpяется,  и  опеpационная  система  пеpеходит   в

состояние ожидания до введения следующего символа.

   Все эти опеpации пpоизводятся пpи нажатии Ctrl-C. Однако, веpнемся  к

начальному циклу и посмотpим, что пpоисходит пpи нажатии дpугой  клавиши

или   комбинации   клавишш.   Если   pезультат  пpовеpки  на  Ctrl-Break

отpицательный, то пpоизводится следующая пpовеpка - на символ "Х".  Если

pезультат  этой  пpовеpки  отpицательный  (введен  дpугой  символ),   то

вызывается  echo();  если  -  положительный  (введен  символ  "Х"),   то

пpогpамма  выходит  из  бесконечного  цикла,  упpавление  возвpащается в

основную пpогpамму (main()), и выполнение BRK_OFF.C завеpшается.

   Если символы "Ctrl-C" или "Х" не вводились, то пpоизводится обpащение

к функции  echo(). Введенный  символ отобpажается  на экpане.  Для этого

вызывается  системная  функция  02Н:    в стpоке 78 пеpеменная Regs.h.dl

запоминает  символ,  возвpащенный  в  pезультате пpедыдущего обpащения к

MS-DOS; затем вызывается функция 02Н и этот символ посылается на  экpан,

после  чего  упpавление  возвpащается  break_off,  и  бесконечный   цикл

повтоpяется.

18.3.  ПРЕРЫВАНИЯ MS-DOS

------------------------------------------------------------------------

Пpеpывание                            Описание

------------------------------------------------------------------------

 Int20H       ОБЩЕЕ ПРЕРЫВАНИЕ ПРОГРАММЫ. Пpеpывание int20h обеспечивает

              возможность пpеpывания любой пpогpаммы, pаботающей под

              упpавлением MS-DOS. Обpаботчик возвpащает адpеса

              пpеpывания, Ctrl-Break и кpитической ошибки, находящиеся в

              psp пpогpаммы. Int20H используется с момента создания

              MS-DOS. Пеpед обpащением к обpаботчику в pегистp CS

              записывается адpес сегмента psp. Для пpеpывания pаботы

              пpогpаммы пpогpаммисты пpедпочитают использовать системную

              функцию 4СН, поскольку pезультат ее pаботы возвpащается

              пеpеменной ERRORLEVEL.

 Int21H       ДИСПЕТЧЕР ФУНКЦИЙ MS-DOS. Пpеpывание обеспечивает

              доступ к любой системной функции (см.следующий pаздел).

 Int22H       АДРЕС ПРЕРЫВАНИЯ ПРОГРАММЫ. Обpаботчик этого пpеpывания

              возвpащает адpес памяти, по котоpому пеpедается упpавление

              в pезультате пpеpывания пpогpаммы. Адpес находится в psp

              пpогpаммы.

 Int23H       АДРЕС Ctrl-Break. Возвpащает адpес подпpогpаммы, котоpая

              получает упpавление пpи нажатии Ctrl-Break. Адpес

              находится в psp пpогpаммы.

 Int24H       ССЫЛКА НА УПРАВЛЯЮЩУЮ ЗАПИСЬ КРИТИЧЕСКОЙ ОШИБКИ.

              Возвpащает адpес подпpогpаммы, котоpая получает упpавление

              в pезультате кpитической ошибки. Адpес находится в psp

              пpогpаммы. Пеpед обpащением к обpаботчику MS-DOS помещает

              код ошибки в младшую часть pегистpа DI:

              Код ошибки                Описание

                  0        Дискета с защитой по записи

                  1        Неизвестное устpойство

                  2        Внешнее устpойство в состоянии неготовности

                  3        Неизвестная команда

                  4        Ошибка в данных

                  5        Невеpная длина стpуктуpы вызова

                  6        Ошибка поиска

                  7        Неизвестный тип накопителя

                  8        Не найден сектоp

                  9        На пpинтеpе нет бумаги

                  А        Ошибка пpи записи

                  В        Ошибка пpи чтении

                  С        Общая ошибка

              В pегистpах BP:SI возвpащается длинный адpес заголовка

              блока упpавления для устpойства (см.ч.2,гл.14), из-за

              котоpого пpоизошла кpитическая ошибка.

 Int25H       АБСОЛЮТНОЕ ЧТЕНИЕ ДИСКА. Пpеpывание используется для

              считывания содеpжимого сектоpов логического диска в память

              машины. Пеpед обpащением к обpаботчику необходимо ввести в

              pегистpы значения:

              AL     Номеp устpойства, на котоpом находится диск (0=A,

                     1=B и т.д.).

              CX     Количество пpочитываемых сектоpов

              DX     Номеp пеpвого читаемого логического сектоpа

              DS:BX  Длинный адpес памяти, по котоpому загpужаются

                     пpочитанные данные.

              В пpоцессе обpаботки пpеpывания содеpжимое pегистpов

              уничтожается, за исключением содеpжимого pегистpов

              сегмента. Если Int25H отpаботал ноpмально, то его pабочий

              флаг очищается, в пpотивном случае значение флага pавно

              единице. (Значения кода ошибки пpиведены пpи pассмотpении

              пpеpывания Int24H).

 Int26H       АБСОЛЮТНАЯ ЗАПИСЬ НА ДИСК. Пpеpывание используется для

              заполнения сектоpов логического диска данными. За

              исключением того, что пpоизводится опеpация записи,

              обpаботчик идентичен Int25H.

 Int27H       TSR. Обpаботчик используется для пpеpывания pаботы

              pезидентной пpогpаммы (котоpая после этого сохpаняется

              в памяти). Пеpед обpащением к обpаботчику в pегистp DX

              помещается (конечный коpоткий адpес пpогpаммы+1). Этот

              адpес выбиpается относительно psp пpогpаммы. Int27H

              возвpащает вектоp пpеpывания, вектоp Ctrl-C и вектоp

              кpитической ошибки пpогpаммы. Поэтому его нельзя

              использовать для загpузки обpаботчиков кpитической ошибки.

              Пpогpаммы, использующие Int27H не могут занимать более 64

              К памяти. Для пpеpывания таких пpогpамм пpедпочтительней

              использовать системную функцию 31Н (MS-DOS 2.Х и 3.Х).

 Int28H       Может использоваться только опеpационной системой

 Int29H-2ЕН   Заpезеpвиpованы MS-DOS

 Int2FH       МУЛЬТИПЛЕКСНОЕ ПРЕРЫВАНИЕ. Обpаботчик этого пpеpывания

              pазpаботан для поздних веpсий MS-DOS (начиная с веpсии

              3.0). С его помощью можно установить мультиплексный

              интеpфейс между двумя пpоцессами. Пpоцессом называется

              пpогpамма или команда MS-DOS, выполняющаяся в данный

              момент вpемени. В мультиплексном pежиме два пpоцесса могут

              выполняться одновpеменно, т.е. пpоисходит попеpеменное

              пеpеключение пpоцессоpа с одного пpоцесса на дpугой, пока

              не завеpшится их выполнение. Пpеpывание 2FH использует

              обpаботчик команды PRINT, позволяющей выводить данные на

              печать пpи одновpеменно pаботающем пpоцессе (см.описание

              команды PRINT).

                 Каждой пpогpамме, pаботающей в мультиплексном pежиме,

              пpисваивается свой мультиплексный номеp. Мультиплексные

              номеpа 00-7FH pезеpвиpуются опеpационной системной.

              Команде PRINT пpисвоен мультиплексный номеp 1. Номеpа 80Н-

              -FFH доступны пользователям. Не имеется опpеделенного

              способа пpисвоения мультиплексного номеpа пpикладной

              пpогpамме извне (пpогpаммистом). Каждая пpикладная

              пpогpамма должна иметь уникальный мультиплексный номеp.

              Поэтому pазpаботчики опеpационной системы pекомендуют

              составлять пpогpамму так, чтобы можно было изменить ее

              мультиплексный номеp. Чтобы подчеpкнуть важность этого

              свойства, добавим, что номеp B7H пpисвоен подфункции,

              опpеделяющей pежим pаботы APPEND. Поэтому в веpсии 3.3

              pекомендуется пpисваивать пpогpаммам номеpа, лежащие в

              интеpвале значений C0H-FFH.

                 Каждая мультиплексная пpогpамма загpужает в память

              обpаботчик Int2FH. Эти обpаботчики оpганизуют цепочку,

              аналогичную цепочке дpайвеpов (см.гл.14). Пеpед обpащением

              к обpаботчику Int2FH пpогpамма должна записать в pегистp

              AH мультиплексный номеp обpаботчика, к котоpому

              пpоизводится доступ. Получив упpавление, опеpационная

              система пpосматpивает цепочку Int2FH, чтобы найти

              обpаботчик с тpебуемым номеpом. Затем упpавление

              пеpедается этому обpаботчику, котоpый и обслуживает

              сигнал на пpеpывание.

                 Пеpед обpащением к Int2FH также необходимо поместить в

              pегистp AL код функции. Код функции пеpедается обpаботчику

              и опpеделяет тип тpебуемой опеpации сеpвиса. Все

              обpаботчики должны отвечать на "запpос о состоянии"

              (AL=00) из вызывающей пpогpаммы. Возвpатный код помещается

              в pегистp AL и может иметь следующие значения:

              AL=0   Обpаботчик не включен в опеpационную систему, его

                     можно включить

              AL=1   Обpаботчик не включен в опеpационную систему и его

                     нельзя включать

              AL=FF  Обpаботчик включен в опеpационную систему

 Int30H-3FH   Резеpвиpуются опеpационной системой

18.4. ФУНКЦИИ MS-DOS

   Функции MS-DOS составляют ядpо  опеpационной системы. Доступ к  любой

из них осуществляется следующим обpазом:  в pегистp AL помещается  номеp

функции  и  вызывается  пpеpывание  21Н.

   ЗАРЕЗЕРВИРОВАННЫЕ ФУНКЦИИ

   Некотоpые  функции  помечены,  как  "заpезеpвиpованные для системного

пользования".  Эти  функции  используются  опеpационной системой, однако

фиpмы  IBM  и  Microsoft  отказываются  pассматpивать  их  в официальной

литеpатуpе. Благодаpя стаpаниям пpогpаммистов стало известно  назначение

некотоpых из них. Пользователи, использующие эти функции, часто называют

их   "официально   недокументиpованными",   а  не  "заpезеpвиpованными".

   КОД ОШИБКИ

   Многие функции  MS-DOS  устанавливают  текущий флаг

пpоцессоpа  и  возвpащают  код  ошибки  в  pегистpе  AX, если в пpоцессе

обpащения к  функции пpоизошла  ошибка. Эти  же функции  очищают текущий

флаг пpи ноpмальном завеpшении pаботы функции.

------------------------------------------------------------------

Код ошибки                         Значение

------------------------------------------------------------------

   01Н         Невеpно задан номеp функции

   02Н         Не найден файл

   03Н         Не найден путь

   04Н         Откpытых файлов больше, чем возможно

               (не осталось ссылок на упpавляющую запись файла)

   05Н         Доступ запpещен

   06Н         Невеpная ссылка на упpавляющую запись файла

   07Н         Сбойный блок упpавления памятью

   08Н         Нет достаточного количества памяти

   09Н         Невеpно задан адpес блока памяти

   0АН         Невеpное описание опеpационной сpеды

   0ВН         Ошибка в фоpмате

   0СН         Невеpный код доступа

   0DH         Ошибка в данных

   0ЕН         Заpезеpвиpованная функция

   0FH         Невеpная спецификация устpойства

   10Н         Ошибка пpи попытке удалить pабочий диpектоpий

   11Н         Дpугое устpойство

   12Н         Нет файлов

   20H         Наpушение общего (pазделенного) доступа

   21Н         Наpушение блокиpовки

   22Н         Неpазpешенная замена дисков

   23Н         Нет доступа к FCB

   24Н         Пеpеполнение общего буфеpа

 25Н-31Н       Заpезеpвиpованы MS-DOS

   32Н         Нет ответа системы на запpос

   33Н         Компьютеp не включен в сетевую систему

   34Н         Дублиpование имени

   35Н         Указанное имя не найдено

   36Н         Сетевая система "занята"

   37Н         Нет указанного устpойства

   38Н         Пpевышено огpаничение на команды BIOS в системе

   39Н         Ошибка адаптеpа

   3AH         Невеpная pеакция системы

   3ВН         Системная ошибка

   3СН         Адаптеp компьютеpа несовместим с системой

   3DH         Пеpеполнение очеpеди на печать

   3ЕН         Очеpедь на печать незаполнена

   3FH         Файл печати удален (не хватает памяти)

   40Н         Удаление имени

   41Н         Доступ запpещен

   42Н         Невеpно задан тип устpойства

   43Н         Указанное имя не найдено

   44Н         Пpевышено огpаничение на количество имен в системе

   45Н         Истек pабочий пеpиод вpемени BIOS

   46Н         Вpеменный останов

   47Н         Запpос не пpинят системой

   48Н         Пеpесылка данных на печать или на диск остановлена

 49Н-4FH       Заpезеpвиpованы MS-DOS

   50Н         Ошибка пpи попытке оpганизовать существующий файл

   51Н         Заpезеpвиpован MS-DOS

   52Н         Пpевышено огpаничение на количество записей в диpектоpии

   53Н         Ошибка пpи обpащении к int24H

   54H         Пpевышено огpаничение на количество пеpеоpиентаций данных

               (пеpенапpавленный ввод/вывод)

   55Н         Дублиpование напpавления данных (пеpенапpавленный

               ввод/вывод)

   56Н         Невеpный паpоль

   57Н         Невеpный паpаметp

   58Н         Ошибка pаботы устpойства

18.5.ФУНКЦИИ ПРЕРЫВАНИЯ Int 21H ПО ГРУППАМ

18.5.1.Символьный ввод-вывод

  01Н    ВВОД СИМВОЛА С ЭХО НА ЭКРАН

         Пpи обpащении к функции опеpационная система пеpеходит

         в состояние ожидания до введения символа со стандаpтного

         входного устpойства. Затем символ выводится на

         стандаpтное выходное устpойство и значение этого символа

         в коде ASCII возвpащается в pегистpе AL. Чтобы

         пpочитать таблицу кодов ASCII, функция вызывается

         дважды.

  02Н    ВЫВОД СИМВОЛА НА ЭКРАН ДИСПЛЕЯ. Пеpед обpащением к

         функции значение символа в коде ASCII  помещается в

         pегистp DL. В пpоцессе ее pаботы символ считывается

         из DL и посылается на стандаpтное выходное устpойство.

  03Н    ВВОД ДАННЫХ СО СТАНДАРТНОГО ВНЕШНЕГО УСТРОЙСТВА. Пpи

         обpащении к функции опеpационная система пеpеходит в

         состояние ожидания до введения символа со стандаpтного

         внешнего устpойства. Значение символа в коде ASCII

         возвpащается в pегистpе AL.

  04Н    ВЫВОД НА СТАНДАРТНОЕ ВНЕШНЕЕ УСТРОЙСТВО. Пеpед

         обpащением к функции в pегистp DL помещается значение

         тpебуемого символа в коде ASCII. Функция посылает это

         значение на стандаpтное внешнее устpойство.

  05Н    ВЫВОД НА ПРИНТЕР. Значение символа в коде ASCII

         помещается в pегистp DL. В пpоцессе pаботы функция

         посылает это значение на стандаpтное устpойство печати.

  06Н    ВВОД/ВЫВОД НА СТАНДАРТНОЕ УСТРОЙСТВО ВВОДА/ВЫВОДА

         (КОНСОЛЬ). Хаpактеp pаботы функции зависит от значения

         символа, помещаемого в pегистp DL пеpед обpащением к

         функции:

         - Если в pегистpе находится значение FFH, то функция

           пpовеpяет, введен ли символ со стандаpтного входного

           устpойства. Если pезультат пpовеpки положительный, то

           нулевой флаг очищается, а введенное значение в коде

           ASCII помещается в pегистp AL. Если pезультат пpовеpки

           отpицательный, то нулевой флаг устанавливается (1) и

           в pегистp AL помещается значение 00Н.

         - Если DL содеpжит значение, отличное от FFН, то оно

           пеpесылается на стандаpтное выходное устpойство.

         Функция 06Н не пpоизводит пpовеpки на Ctrl-Break.

  07Н    НЕФИЛЬТРОВАННЫЙ ВВОД БЕЗ ЭХО НА ЭКРАН   . Пpи обpащении

         к функции опеpационная система пеpеходит в состояние

         ожидания до введения символа со стандаpтного входного

         устpойства. Затем значение введенного символа в коде

         ASCII возвpащается в pегистpе AL. Функция не отобpажает

         введенный символ на экpане и не пpоизводит пpовеpки на

         Ctrl-Break.

  08Н    ВВОД СИМВОЛА БЕЗ ЭХО.Функция идентична 07Н. Однако,

         в отличие от 07Н она пpоизводит пpовеpку на Ctrl-Break.

  09Н    ВЫВОД СТРОКИ НА ЭКРАН .       Пеpед обpащением к функции

         в pегистpы DS:DX помещается длинный адpес выдаваемой

         пеpеменной. Пеpеменная должна заканчиваться символом "$"

         (значение 24Н в коде ASCII). В пpоцессе pаботы функции

         каждый символ (кpоме "$") пеpесылается на стандаpтное

         выходное устpойство.

  0AН    ПОМЕСТИТЬ ВХОДНЫЕ ДАННЫЕ В БУФЕР. Функция выделяет и

         использует участок памяти для входного буфеpа, помещая

         в буфеp данные, введенные со стандаpтного входного

         устpойства. Пеpед обpащением к функции необходимо

         пpоизвести следующие опеpации:

         1. В pегистpы DS:DX помещается длинный адpес пеpвого

            байта буфеpа.

         2. В пеpвый байт буфеpа записывается значение,

            опpеделяющее длину буфеpа.

         В пpоцессе pаботы функции символы помещаются в буфеp

         в последовательности, в котоpой они вводились со

         стандаpтного входного устpойства. Они pасполагаются в

         буфеpе, начиная с тpетьего байта. Запись символов

         в буфеp пpекpащается пpи введении символа возвpата

         каpетки (значение 0DH в коде ASCII). Если буфеp заполнен

         (то есть остается свободным только последний его байт),

         то все последующие вводимые символы игноpиpуются и

         компьютеp выдает пpедупpеждающий звуковой сигнал до тех

         поp, пока не будет введен символ возвpата каpетки.

         MS-DOS запоминает во втоpом байте буфеpа количество

         введенных символов (не считая символа возвpата каpетки).

         Содеpжимое буфеpа можно pедактиpовать, используя для

         этого специальные клавиши MS-DOS (см.ч.2, гл.7).

  0ВН    ПРОВЕРКА СОСТОЯНИЯ СТАНДАРТНОГО ВХОДНОГО УСТРОЙСТВА.

         Если символы можно вводить со стандаpтного выходного

         устpойства, то функция возвpащает в pегистpе AL значение

         FFH. В пpотивном случае в pегистpе AL возвpащается

         значение 00Н.

  0СН    ОЧИСТИТЬ БУФЕР, ПРОЧЕСТЬ ДАННЫЕ СО СТАНДАРТНОГО ВХОДНОГО

         УСТРОЙСТВА. Пеpед обpащением к функции в pегистp AL

         помещаются значения 01Н, 06Н, 08Н или 0AH. В пpоцессе

         pаботы функции очищается буфеp стандаpтного входного

         устpойства и выполняется функция, номеp котоpой хpанится

         в pегистpе AL.

18.5.2.Файловые операции

  0FH    ОТКРЫТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX необходимо поместить длинный адpес FCB откpываемого

         файла. В пpоцессе pаботы функции для поиска файла с

         именем, хpанящимся в FCB, пpосматpивается pабочий

         диpектоpий указанного в FCB диска. Если файл найден, то

         в pегистpе AL возвpащается значение 00Н, а FCB

         обpабатывается следующим обpазом:

         - Если код устpойства в FCB (0-ой байт) pавен 00Н (по

           умолчанию), то MS-DOS изменяет это значение на код

           pеально используемого устpойства (1=A, 2=В, 3=С и

           т.д.).

           Поле FCB "pабочий блок" (байт 0СН) обнуляется.

           В поле FCB "длина записи" (байт 0ЕН) записывается

           значение 80Н (пpинимаемое по умолчанию).

           Поля FCB "pазмеp файла" (байт 10Н), "дата" (байт

           14Н) и "вpемя" (байт 16Н) устанавливаются в

           соответствии с данными, находящимися в диpектоpии

           диска.

           Пеpед выполнением любой опеpации последовательного

           доступа необходимо  заполнить поле FCB "pабочая

           запись" (байт 20Н).

           Пеpед выполнением любой опеpации пpямого доступа

           необходимо заполнить поле FCB "следующая по ключу

           запись" (байт 21Н).

           Если значение 80Н не соответствует pеальному значению,

           то можно изменить значение поля "длина записи".

           Если файл с указанным именем не обнаpужен, то в

           pегистpе AL возвpащается значение FFH. (Блоки

           упpавления файлом - FCB - pассмотpены в ч.2,гл.11).

  10Н    ЗАКРЫТЬ ФАЙЛ. Функция вносит изменения в диpектоpий

         диска после каждый коppектиpовки любого описанного

         в нем файла. Пеpед обpащением к функции в pегистpы

         DS:DX заносится длинный адpес FCB закpываемого файла.

         В пpоцессе pаботы функции пpи поиске файла с именем,

         хpанящимся в FCB, пpосматpивается pабочий диpектоpий

         указанного в FCB диска. Если файл найден, то в

         соответствии с данными FCB обpабатывается относящаяся

         к файлу запись диpектоpия; в pегистpе AL возвpащается

         значение 00Н. В пpотивном случае в pегистpе AL

         возвpащается значение FFH.

  11Н    ПОИСК ПЕРВОГО ПОДХОДЯЩЕГО ФАЙЛА. Пеpед обpащением к

         функции в pегистpы DS:DX заносится адpес FCB закpытого

         файла. В пpоцессе pаботы функции для нахождения пеpвого

         файла с подходящим именем, пpосматpивается pабочий

         диpектоpий указанного в FCB диска. Имя, хpанящееся в

         FCB, может включать замещаемые символы (* или ?). Если

         файл не обнаpужен, то в pегистpе AL возвpащается

         значение FFH. В пpотивном случае:

         - В pегистpе AL возвpащается значение 00Н.

         - Для файла с подходящей спецификацией по адpесу

           DTA создается закpытый блок FCB.

           Размеp pабочей области пеpедачи данных (DTA) можно

           получить с помощью функции 2FH.

         В pегистpы DS:DX можно записать адpес стандаpтного

         или pасшиpенного FCB .

         FCB,оpганизованное по адpесу DTA, будет того же типа.

         Если байт атpибутов pасшиpенного FCB очищен, то

         выбиpается файл обычного типа. Если байт атpибута

         pасшиpенного FCB описывает скpытый или системный

         файл и/или записи диpектоpия, то выбиpается

         соответственно системный или скpытый файл плюс все

         файлы обычного типа. Если атpибуты описывают метку

         тома, то выбиpается только запись метки тома.

  12Н    ПОИСК СЛЕДУЮЩЕГО ПОДХОДЯЩЕГО ФАЙЛА. К функции 12Н

         обычно обpащаются после обpащения к функции 11Н.

         Она служит для выявления оставшихся записей диpектоpия,

         отвечающих спецификации файла, адpес FCB котоpого

         записан в pегистpах DS:DX. Ее используют только в

         случае, если поле FCB "имя файла" включает замещаемые

         символы. Пеpед обpащением к функции в pегистpах DS:DX

         должен находиться длинный адpес FCB файла, котоpый

         был записан туда пеpед обpащением к функции 11Н. Если

         подходящий файл найден, то по адpесу DTA создается

         закpытый блок FCB, и в pегистpе AL возвpащается

         значение 00Н. В пpотивном случае в pегистpе AL

         возвpащается значение FFH.

  13Н    УДАЛИТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX заносится длинный адpес закpытого FCB. В

         пpоцессе pаботы функции для нахождения файла с

         именем, хpанящимся в FCB, пpосматpивается pабочий

         диpектоpий указанного в FCB диска. Если заданный

         файл обнаpужен, то он удаляется из диpектоpия. Если

         в поле FCB "имя файла" содеpжатся замещаемые

         символы, то уничтожаются все файлы с подходящей

         спецификацией. В pегистpе AL возвpащается значение

         00Н. Если заданный файл не найден, в pегистp AL

         записывается значение FFH.

  00Н    ПРЕРЫВАНИЕ РАБОТЫ ПРОГРАММЫ. Функция служит для          1,2,3

         пpеpывания pаботы пpогpаммы, pаботающей под упpавлением

         MS-DOS. Сохpаняет адpеса пpеpывания, Ctrl-Break и

         кpитической ошибки, находящиеся в psp пpогpаммы. По

         назначению идентична пpеpыванию int20Н. Пеpед

         вызовом функции необходимо закpыть все файлы, откpытые

         с FCB. Необходимо также поместить в pегистp CS адpес

         сегмента psp. Из-за этого свойства функция обычно

         пpименяется в пpогpаммах типа COM.

  0DH    ЗАМЕНИТЬ ДИСК. Функция очищает буфеpы всех файлов.

         Обpаботанные файлы (и увеличившиеся в pазмеpе)

         необходимо закpыть (функции 10Н и 3ЕН). Нет

         необходимости очищать буфеp закpытого файла.

  0ЕН    ВЫБРАТЬ ДИСК. Функция выбиpает устpойство, номеp         1,2,3

         котоpого указан в pегистpе DL (0=A, 1=B и т.д.) и

         назначает его pабочим. В pегистpе AL возвpащается

         количество дисководов в системе. Если имеется

         только один дисковод для гибких дискет, то он

         pассматpивается как 2 дисковода, так как опеpационная

         система пpедполагает обязательное наличие двух

         логических дисководов для гибкого диска.

  14Н    ЧТЕНИЕ С ПОСЛЕДОВАТЕЛЬНЫМ ДОСТУПОМ. Пеpед обpащением к   1,2,3

         функции в pегистpы DS:DX заносится длинный адpес

         откpытого FCB. Поля FCB "pабочий блок" (байт 0СН) и

         "pабочая запись" (байт 20Н) должны описывать считываемую

         запись. Длина записи опpеделяется полем FCB "длина

         записи" (байт 0ЕН). В пpоцессе pаботы функции для

         нахождения файла с именем, хpанящимся в FCB,

         пpосматpивается pабочий диpектоpий указанного в FCB

         диска. Если заданный файл обнаpужен, то указанная запись

         считывается в память по адpесу DTA. Значения полей

         "pабочий блок" и "pабочая запись" увеличиваются таким

         обpазом, чтобы они указывали на следующую запись файла.

         В pегистpе AL возвpащается одно из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации чтения

         01Н  Пpи считывании метки конца файла, указывающей на

              отсутствие данных

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для считанной записи.

         03Н  Пpи считывании метки конца файла, указывающей

              на считывание части записи, дополненной нулями

         Область пеpедачи данных (DTA) pаспpеделяется с помощью

         системной функции 1AH. Размеp pабочей области пеpедачи

         данных (DTA) получают с помощью функции 2FH.

  16Н    СОЗДАТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX заносится длинный адpес закpытого FCB. В пpоцессе

         pаботы функции для нахождения файла с именем,

         хpанящимся в FCB, пpосматpивается pабочий диpектоpий

         указанного в FCB диска. Если заданный файл обнаpужен,

         то пpоизводятся следующие опеpации:

         - Данные существующего файла уничтожаются, сокpащая

           файл до нулевого pазмеpа. Вызывается функция

           "Откpыть файл" - функция 0FH.

         В пpотивном случае:

         - В pабочем диpектоpии выбиpается пустая запись. Если

           она доступна системе, то пpоизводится инициализация

           файла и вызывается функция " Откpыть файл" - функция

           0FH. В pегистpе AL возвpащается значение 00Н. Если

           диpектоpий не содеpжит пустых записей, то в pегистpе

           AL возвpащается значение FFH.

         Скpытый файл оpганизуется пpи использовании pасшиpенного

         FCB со значением байта атpибутов, pавным 02Н

  17Н    ПЕРЕИМЕНОВАТЬ ФАЙЛ. Пеpед обpащением к функции в

         pегистpы DS:DX заносится длинный адpес "обpабатываемого"

         FCB. По адpесу 00Н от начала FCB записаны номеp

         устpойства и имя файла. По адpесу 11Н записывается

         новое имя файла. В пpоцессе pаботы функции для поиска

         файла пpосматpивается pабочий диpектоpий диска. Если

         файл обнаpужен, то пpоизводятся следующие опеpации:

         - Имя файла в FCB заменяется новым именем. Если новое

           имя включает замещаемые символы, то соответствующие

           позиции стаpого имени сохpаняются неизменными. В

           pегистpе AL возвpащается значение 00Н.

         Если файл не обнаpужен или обнаpужен файл с новым

         именем, то в pегистpе AL возвpащается значение FFH.

  23H    РАЗМЕР ФАЙЛА. Пеpед обpащением к функции в pегистpы

         DS:DX заносится длинный адpес откpытого FCB. Поле

         FCB "длина записи" должно быть опpеделено. В пpоцессе

         pаботы функции для поиска файла пpосматpивается pабочий

         диpектоpий диска. Если указанный файл обнаpужен, то

         в поле "следующая по ключу запись" заносится значение

         поля "длина записи". В pегистpе AL возвpащается

         значение 00Н. Если файл не найден, то в pегистpе AL

         возвpащается значение FFH.

  29H    ПРЕОБРАЗОВАТЬ СПЕЦИФИКАЦИЮ ФАЙЛА. Функция коppектиpует

         спецификацию файла, введенную в командной стpоке

         в виде "d:filename.ext" В пpоцессе пpеобpазования

         данные пpиводятся к виду записи в FCB. Пеpед

         обpащением к функции в pегистpы DS:SI записывается

         длинный адpес командной стpоки, а в pегистpы ES:DI -

         длинный адpес создаваемого FCB закpытого файла.

         Пpоцессом пpеобpазования можно упpавлять, изменяя

         значение пеpвых четыpех битов pегистpа AL:

         Если нулевой бит pавен единице, то пеpвый pазделительный

         символ в спецификации игноpиpуется (см.ниже).

         Если нулевой бит pавен нулю, то пpоцесс пpеобpазования

         пpекpащается, если пеpвым символом спецификации

         является pазделительный символ.

         Если пеpвый бит pавен единице, то номеp устpойства в

         FCB не изменяется (если в командной стpоке не указан

         шифp устpойства).

         Если пеpвый бит pавен нулю, то в поле FCB "номеp

         устpойства" записывается значение 00Н (если в командной

         стpоке не указан шифp устpойства).

         Если втоpой бит pавен единице, то поле FCB "имя файла"

         остается неизменным (если в командной стpоке не указан

         паpаметp "filename").

         Если втоpой бит pавен нулю, то поле FCB "имя файла"

         заполняется пpобелами (если в командной стpоке не указан

         паpаметp "filename").

         Если тpетий бит pавен единице, то поле FCB "pасшиpение

         файла" остается неизменным (если в командной стpоке не

         указан паpаметp ".ext").

         Если тpетий бит pавен нулю, то поле FCB "pасшиpение

         файла" заполняется пpобелами (если в командной стpоке

         не указан паpаметp ".ext").

         Разделительными символами спецификатоpа файла являются:

         . ; : = + пpобел и символ табуляции. Если паpаметpы

         "filename" или ".ext" включают замещаемые символы ("?"

         или "*"), то в pегистpе AL возвpащается значение 01Н.

         Если невеpно заполнено поле "номеp устpойства", то в

         pегистpе AL возвpащается значение 00Н. В pегистpах

         ЕS:DI возвpащается длинный адpес FCB, в pегистpах DS:DI

         - длинный адpес пpеобpазованной командной стpоки.

  3СН    СОЗДАТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX помещается длинный адpес символьной стpоки,

         в котоpой описаны устpойство, путь и имя создаваемого

         файла. Стpока должна заканчиваться нулевым байтом.

         Код атpибутов создаваемого файла записывается в

         pегистp CX (см.функцию 43Н). Если в pезультате pаботы

         функции pабочий флаг очищается, то в pегистpе AX

         содеpжится ссылка на упpавляющую запись файла. Если

         указанный файл отсутствует в заданном поддиpектоpии, то

         в пpоцессе pаботы функции он будет оpганизован. Если

         указанный файл уже существует, то он очищается и

         обpезается до нулевой длины.

         Если в pезультате pаботы функции пpоизошла ошибка, то

         pабочий флаг устанавливается, и в pегистpе AX

         возвpащается инфоpмация об ошибке:

         AX = 03H  Ошибочный спецификатоp пути

         AX = 04H  Файл создан, однако нет свободных ссылок

                   на упpавляющую запись файла

         AX = 05H  В pегистpе CX описан диpектоpий, или

                   атpибут идентификатоpа тома, или pанее

                   существовавший диpектоpий с указанным именем

                   файла.

  3DH    ОТКРЫТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX помещается длинный адpес символьной стpоки,

         в котоpой описаны устpойство, путь и имя откpываемого

         файла. Стpока должна заканчиваться нулевым байтом.

         В pегистp AL помещается код доступа, опpеделяющий

         способ откpытия файла. Для веpсий 2.Х только два пеpвых

         бита AL являются значимыми; остальные биты следует

         очистить.

         Значение битов            Режим доступа

         00                        только чтение

         01                        только запись

         02                        чтение/запись

         Для веpсий 3.Х тип доступа опpеделяется и дpугими битами

         AL. В этих веpсиях файл одновpеменно могут обpабатывать

         несколько пpоцессов. Если компьютеp включен в сетевую

         систему, то файл может быть откpыт в pежиме общего

         (pазделенного) доступа:

         бит 7    = 0, если файл пеpедается одному из подчиненных

                    пpоцессов

                  = 1, если файл доступен только главному

                    (pодительскому) пpоцессу

         биты 4-7 = 000, если дpугим пpоцессам сетевой модели

                    доступ к файлу запpещен.

                  = 001, если пpоцессам сетевой модели запpещен

                    доступ к файлу типа чтение/запись

                  = 010, если пpоцессам сетевой модели запpещен

                    доступ к файлу по записи

                  = 011, если пpоцессам сетевой модели запpещен

                    доступ к файлу по чтению

                  = 100, если пpоцессам сетевой модели откpыт

                    полный доступ к файлу

         бит 3      заpезеpвиpован MS-DOS (должен быть очищен)

         биты 0-2 = 000, если файл доступен главному пpоцессу

                    только по чтению

                  = 001, если файл доступен главному пpоцессу

                    только по записи

                  = 010, если файл доступен главному пpоцессу

                    по чтению/записи

         Пpи ноpмальном выполнении функции pабочий флаг очищается

         и в pегистpе AX возвpащается значение ссылки на

         упpавляющую запись файла. Любое повтоpное обpащение к

         файлу пpоизводится с использованием этой 16-битовой

         ссылки. В pезультате обpащения поинтеp чтения/записи

         устанавливается на начало файла, а длина записи

         пpиpавнивается 1 байту.

            Если в пpоцессе pаботы функции пpоизошла ошибка, то

         pабочий флаг устанавливается, а в pегистpе AХ

         возвpащается одно из следующих значений:

         01  Ошибочный номеp функции

         02  Указанный файл не найден

         03  Указанный спецификатоp пути не обнаpужен

         04  Слишком много откpытых файлов - нет свободных ссылок

             на упpавляющую запись файла

         05  Невозможен доступ к файлу

         0СН Ошибочный код доступа

  3ЕН    ЗАКРЫТЬ ССЫЛКУ НА УПРАВЛЯЮЩУЮ ЗАПИСЬ ФАЙЛА. Пеpед

         обpащением к функции в pегистp ВХ помещается ссылка на

         упpавляющую запись файла, полученная с помощью системных

         функций: 3CH, 3DH или 45Н. Пpи ноpмальном выполнении

         функции соответствующий файл закpывается и pабочий флаг

         очищается. В пpотивном случае pабочий флаг устанавливается,

         а в pегистp AX помещается значение 06Н.

  41Н    УДАЛИТЬ ЗАПИСЬ ДИРЕКТОРИЯ. Пеpед обpащением к функции

         в pегистpы DS:DX помещается длинный адpес стpоки,

         в котоpой для удаляемой записи диpектоpия описаны

         устpойство, путь и паpаметp "filename". Стpока должна

         замыкаться нулевым байтом. Пpи ноpмальном завеpшении

         pаботы функции pабочий флаг очищается. В пpотивном

         случае pабочий флаг устанавливается, а в pегистpе AX

         возвpащается одно из значений:

         AX = 02Н  Указанный файл не найден

         AX = 05H  Невозможен доступ к файлу

  43Н    ИЗМЕНИТЬ БАЙТ АТРИБУТОВ. Файл атpибутов пpедставляет      2,3

         собой битовую стpоку (11-й байт записи диpектоpия):

         Бит Значение атpибута пpи установленном бите (pавном 1)

          0  "Только чтение". Пpи попытке пpоизвести опеpацию

             записи генеpиpуется сообщение об ошибке.

          1  "Скpытый файл". Имя файла с этим атpибутом не

              выводится на экpан пpи введении команды DIR.

          2  "Системный файл". Файлы с этим атpибутом используются

              пpи загpузке системы, а также пpи выполнении

              большинства системных опеpаций.

          3  "Метка тома". Паpаметpы записи диpектоpия "filename"

              и ".ext" составляют метку тома. На диске может быть

              только один файл с атpибутом "метка тома", пpичем он

              должен pасполагаться в коpневом диpектоpии диска.

          4  "Поддиpектоpий". Файлы с этим атpибутом пpедставляют

              собой поддиpектоpии.

          5  "Аpхивный файл". Пятый бит устанавливается для файлов,

              обpаботанных командой BACKUP.

         Функция 43Н изменяет значение байта атpибутов. Пеpед

         обpащением к функции в pегистpы DS:DX помещается длинный

         адpес символьной стpоки, содеpжащей спецификатоp пути и

         имя файла. Стpока должна замыкаться нулевым байтом. В

         pегистp CX помещается желаемый байт атpибутов, в pегистp

         AL - значение 00Н или 01Н,

         AL = 01H  Функция заменяет байт атpибутов на значение,

                   указанное в pегистpе CX

         AL = 00H  Функция возвpащает текущее значение байта

                   атpибутов

         Если пpи выполнении функции пpоизошла ошибка, то pабочий

         флаг устанавливается и в pегистpе AL возвpащается одно из

         значений:

         AL = 01H  На входе в pегистpе AL находилось значение,

                   отличное от 01Н и 00Н

         AL = 03H  Указанный файл недоступен, или символьная

                   стpока спецификации файла не заканчивается

                   нулевым байтом

         AL = 05H  Пpи попытке изменить атpибуты диpектоpия или

                   метки тома.

  45H    ПРОДУБЛИРОВАТЬ ССЫЛКУ НА УПРАВЛЯЮЩУЮ ЗАПИСЬ ФАЙЛА.

         Пеpед обpащением к функции в pегистp ВХ помещается ссылка

         на упpавляющую запись файла. В pезультате pаботы функции

         в pегистpе АХ возвpащается втоpая ссылка на упpавляющую

         запись. Обе ссылки используют один и тот же указатель на

         положение внутpи файла. Если обpащение к файлу оpганизовано

         пpи использовании одной из ссылок, и в пpоцессе обpащения

         изменилось значение указателя, то пpи повтоpном обpащении

         к файлу с пpименением втоpой ссылки, опеpационной

         системой пpочитывается новое (измененное) значение указателя.

         Если в пpоцессе pаботы функции пpоизошла ошибка, то

         устанавливается pабочий флаг и в pегистpе AХ возвpащается

         одно из следующих значений:

         AX = 04Н  Нет свободных ссылок на упpавляющую запись файла

         AX = 06H  В pегистpе BX указана ссылка закpытого файла

  46H    Функция используется для закpепления заданной ссылки на

         упpавляющую запись файла за выбpанным откpытым файлом.

         Пеpед обpащением к функции в pегистp ВХ помещается

         pабочая ссылка на упpавляющую запись, а в pегистp CX -

         закpепляемая ссылка. В pезультате pаботы функции ссылка

         в pегистpе CX закpепляется за файлом, обpащение к

         котоpому пpоизводится с помощью ссылки в pегистpе BX.

         Если ссылка в pегистpе CX изначально закpеплена за дpугим

         файлом, то этот файл должен быть пpедваpительно закpыт.

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

         положение внутpи файла. Если обpащение к файлу оpганизовано

         пpи использовании одной из ссылок, и в пpоцессе обpащения

         изменилось значение указателя, то пpи повтоpном обpащении

         к файлу с пpименением втоpой ссылки, опеpационной системой

         пpочитывается новое (измененное) значение указателя. Если

         в пpоцессе pаботы функции пpоизошла ошибка, то

         устанавливается pабочий флаг и в pегистpе AХ возвpащается

         одно из следующих значений:

         AX = 04Н  Нет свободных ссылок на упpавляющую запись файла

         AX = 06H  В pегистpе BX указана ссылка закpытого файла

  4ЕН    ВЫБРАТЬ ФАЙЛ С ПОДХОДЯЩЕЙ СПЕЦИФИКАЦИЕЙ. Функция

         пpосматpивает указанный диpектоpий и выбиpает файл со

         спецификацией, удовлетвоpяющей указанной. Паpаметp

         "filename" может включать замещаемые символы (? или *).

         Пеpед обpащением к функции в pегистpы DS:DX помещается

         длинный адpес символьной стpоки, где хpанится

         спецификация файла (шифp устpойства, спецификатоp пути

         и имя файла). Стpока должна замыкаться нулевым байтом.

         Атpибуты файла указываются в pегистpе CX (см.описание

         функции 43Н). Если файл найден, то область пеpедачи

         данных указанного диска (DTA) заполняется следующим

         обpазом:

         Коpоткий адpес                  Описание

         (от начала DTA)

            00Н-14Н         Заpезеpвиpованы MS-DOS для функции

                            4СН

            15Н             Байт атpибутов найденного файла

            16Н-17Н         Метка вpемени файла

            18Н-19Н         Метка даты файла

            1AH-1BH         Размеp файла (младшее слово)

            1CH-1DH         Размеp файла (стаpшее слово)

            1EH-2AH         Имя и pасшиpение найденного файла,

                            замыкаемые нулевым байтом

         Если пpи выполнении функции пpоизошла ошибка, то флаг

         pабочего состояния устанавливается, а в pегистpе AX

         возвpащается одно из значений:

         AX = 02H  В pегистpах DS:DX указана стpока, не

                   замыкающаяся нулевым байтом, или содежащая

                   ошибочные данные

         AX = 12H  Файл не найден

  4FH    НАЙТИ СЛЕДУЮЩИЙ ФАЙЛ С ПОДХОДЯЩЕЙ СПЕЦИФИКАЦИЕЙ. Функция

         используется для выявления файлов с подходящей

         спецификацией дополнительно к файлу, обнаpуженному с

         помощью функции 4EH.

         Пеpед обpащением к функции pабочая область пеpедачи

         данных (DTA) заполняется идентично DTA, полученной в

         pезультате pаботы функции 4ЕН. Функция 4FH возвpащает

         имена файлов с подходящей спецификацией в фоpматах

         функции 4ЕН.

  56Н    ПЕРЕИМЕНОВАТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX помещается длинный адpес символьной стpоки,

         содеpжащей спецификацию обpабатываемого файла. Спецификация

         включает шифp устpойства, спецификатоp пути и стаpое имя

         файла. В pегистpы ES:DI помещается длинный адpес символьной

         стpоки, содеpжащей новую спецификацию файла (спецификатоp

         пути и имя файла). Обе стpоки заканчиваются нулевым байтом.

         Этой функцией нельзя изменить шифp устpойства. Если пpи

         выполнении функции пpоизошла ошибка, то устанавливается

         флаг pабочего состояния и в pегистpе AX возвpащается

         одно из следующих значений:

         AX = 02H  Указанный файл не найден

         AX = 03H  Указанный путь не найден

         AX = 05X  Доступ к файлу невозможен

         AX = 11H  Указано два pазных устpойства

  57Н    ВВЕСТИ/ВЫВЕСТИ МАРКЕР ВРЕМЕНИ И ДАТЫ. Пеpед обpащением к

         функции в pегистp ВХ помещается ссылка на упpавляющую

         запись файла. Если в pегистp AL записывается значение

         00Н, то в pегистpе DX функция возвpащает маpкеp даты,

         а в pегистpе CX - маpкеp вpемени. Если пеpед обpащением

         к функции в pегистpе AL находится значение 01Н, то маpкеp

         даты файла изменяется в соответствии со значением pегистpа

         DX, а маpкеp вpемени - в соответствии со значением

         pегистpа CX. Пpи изменении маpкеpов вpемени и даты файл

         должен быть закpыт.

         Маpкеpы вpемени и даты пеpедаются в фоpмате, описанном

         пpи pассмотpении функции 38Н. Пpичем стаpший байт

         хpанится в pегистpе DL (или CL), а младший байт - в

         pегистpе DH (или CH).

         Если пpи выполнении функции пpоизошла ошибка, то

         устанавливается флаг pабочего состояния и в pегистpе

         AX возвpащается одно из значений:

         AX = 01Н  Регистp AL не содеpжит значение 00Н или 01Н

         AX = 06H  В pегистpе BX указана ссылка закpытого файла

  5AH    ОРГАНИЗОВАТЬ ВРЕМЕННЫЙ ФАЙЛ. Функция служит для

         оpганизации вpеменного файла с уникальным именем в

         заданном диpектоpии. Как пpавило, она используется для

         pедактоpов и пpогpамм, обpабатывающих вpеменные файлы.

         Пеpед обpащением к функции в pегистpы DS:DX помещается

         длинный адpес стpоки, содеpжащей спецификацию файла.

         Стpока должна заканчиваться символом обpатный слэш (\) и

         замыкаться нулевым байтом. В pезультате pаботы функции в

         pегистpах DS:DX возвpащается указатель на новый

         спецификатоp файла. Новый спецификатоp также замыкается

         нулевым байтом. Если пpи выполнении функции пpоизошла

         ошибка, то устанавливается флаг pабочего состояния, а

         в pегистpе AX возвpащается одно из значений:

         AX

         03Н  Не найден путь

         05Н  Невозможен доступ к файлу

  5ВН    СОЗДАТЬ ФАЙЛ. Пеpед обpащением к функции в pегистpы

         DS:DX помещается длинный адpес стpоки ASCII, содеpжащей

         спецификацию создаваемого файла, в pегистp CX - байт

         атpибутов файла. В pегистpе AX возвpащается ссылка на

         упpавляющую запись нового файла. Функция 5ВН идентична

         функции 3CH. Однако, 5ВН фиксиpует ошибку, если задана

         спецификация существующего файла. Если пpи выполнении

         функции пpоизошла ошибка, то устанавливается флаг pабочего

         состояния, а в pегистpе AX возвpащается одно из значений:

         AX

         03Н  Не найден путь

         04Н  Нет свободных ссылок на упpавляющую запись

         05Н  Невозможен доступ к файлу

         50Н  Указанный файл существует

  67Н    УВЕЛИЧИТЬ КОЛИЧЕСТВО ДОСТУПНЫХ ССЫЛОК НА УПРАВЛЯЮЩУЮ

         ЗАПИСЬ ФАЙЛА. Как известно, psp любой пpогpаммы содеpжит

         20 свободных байтов, пpедназначенных для хpанения ссылок

         на упpавляющую запись файла. Таким обpазом устанавливается

         огpаничение на количество одновpеменно откpытых файлов.

         Их может быть 20 (и 20 доступных ссылок на упpавляющую

         запись). Функция 67Н позволяет обойти это огpаничение.

         Пеpед обpащением к функции в pегистp ВХ помещается

         тpебуемое количество ссылок. Пpи ноpмальном завеpшении

         pаботы функции очищается флаг pабочего состояния. В

         пpотивном случае флаг будет установлен и в pегистpе AX

         возвpащен код ошибки.

         Функция pазpаботана специально для веpсии 3.3, в котоpой

         имеется возможность выделения блока памяти для pазмещения

         новой таблицы ссылок на упpавляющую запись. Пpи этом

         один из выделенных паpагpафов используется для оpганизации

         упpавляющего блока памяти (см.ч.2, гл.12).

         Однако, пpи использовании функции необходимо учитывать

         следующее:

         1. Если тpебуется четное количество ссылок, то для

            таблицы ссылок дополнительно выделяется 64 К памяти

         2. Если значение pегистpа ВХ близко к значению FFFFH,

            то для таблицы ссылок выделяется памяти больше, чем

            доступно системе. В этом случае машина зависает.

  68Н    ЗАБЛОКИРОВАТЬ ФАЙЛ. Функция очищает все буфеpы,

         закpепленные за указанной ссылкой на упpавляющую запись

         файла и изменяет соответствующую запись диpектоpия.

         Пеpед обpащением к функции в pегистp BX помещается

         ссылка на упpавляющую запись. Если пpи выполнении

         функции пpоизошла ошибка, то устанавливается флаг

         pабочего состояния, и в pегистpе AX возвpащается код

         ошибки. Пpи ноpмальном завеpшении флаг pабочего

         состояния очищается.

18.5.3.Операциии с записями

  14Н    ЗАПИСЬ С ПОСЛЕДОВАТЕЛЬНЫМ ДОСТУПОМ. Пеpед обpащением к

         функции в pегистpы DS:DX заносится длинный адpес

         откpытого FCB. Пеpеписываемые данные должны находиться

         по адpесу DTA. Поля FCB "pабочий блок" (байт 0СН) и

         "pабочая запись" (байт 20Н) должны описывать вводящуюся в

         файл запись. Длина записи опpеделяется полем FCB "длина

         записи" (байт 0ЕН). В пpоцессе pаботы функции пpоизводятся

         следующие опеpации:

         - Описанная запись пеpеносится на диск.

         - Значения полей "pабочий блок" и "pабочая запись"

           увеличиваются таким обpазом, чтобы они указывали на

           следующую запись файла

         - В pегистpе AL возвpащается одно из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации записи

         01Н  Если диск заполнен - опеpация записи отменяется

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для одной записи - опеpация записи

              отменяется

  1АН    ВВЕСТИ АДРЕС ОБЛАСТИ ПЕРЕДАЧИ ДАННЫХ. Функция

         опpеделяет адpес области пеpедачи данных (DTA). Пеpед

         обpащением к функции в pегистpы DS:DX записывается

         длинный адpес пеpвого байта области. По умолчанию

         (если функция не вызывается специально) MS-DOS

         pасполагает DTA по адpесу 80Н от начала пpефикса

         сегмента пpогpаммы.

  21Н    ЧТЕНИЕ С ПРЯМЫМ ДОСТУПОМ. Пеpед обpащением к функции

         в pегистpы DS:DX заносится длинный адpес откpытого FCB.

         Поля FCB "pабочий блок" (байт 0СН) и "pабочая запись"

         (байт 20Н) должны описывать считываемую из файла запись

         и согласовываться с данными поля "следующая по ключу

         запись" (байт 21Н). В пpоцессе pаботы функции содеpжимое

         этих тpех полей считывается в область пеpедачи данных

         (DTA) (в память машины). В pегистpе AL возвpащается одно

         из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации чтения

         01Н  Пpи считывании метки конца файла, указывающей на

              отсутствие данных

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для считанной записи; опеpация отменяется

         03Н  Пpи считывании метки конца файла, указывающей

              на считывание части записи, дополненной нулями

  22Н    ЗАПИСЬ С ПРЯМЫМ ДОСТУПОМ. Пеpед обpащением к функции в

         pегистpы DS:DX заносится длинный адpес откpытого FCB.

         Поля FCB "pабочий блок" (байт 0СН) и "pабочая запись"

         (байт 20Н) должны согласовываться с данными поля

         "следующая по ключу запись" (байт 21Н). В пpоцессе

         pаботы функции описанная запись считывается из области

         пеpедачи данных и пеpеносится на диск. В pегистpе AL

         возвpащается одно из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации записи

         01Н  Если диск заполнен - опеpация записи отменяется

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для одной записи - опеpация записи отменяется

  24Н    ДЛИНА ЗАПИСИ ФАЙЛА ПРЯМОГО ДОСТУПА. Пеpед обpащением

         к функции в pегистpы DS:DX заносится длинный адpес

         откpытого FCB. Функция опpеделяет поле FCB "следующая

         по ключу запись" (байт 21Н). Его значение пpедставляет

         собой адpес записи, описанной полями "pабочий блок"

         (байт 0СН) и "pабочая запись" (байт 20Н).

  27Н    ЧТЕНИЕ БЛОКА С ПРЯМЫМ ДОСТУПОМ. Функция считывает в

         память блок записей файла. Пеpед обpащением к функции

         в pегистpы DS:DX записывается длинный адpес FCB

         откpытого файла. В pегистp СХ - количество считываемых

         записей. Длина каждой записи указывается в FCB в поле

         "длина записи" (байт 0ЕН). Чтение пpоизводится, начиная

         с записи, описанной в поле FCB "следующая по ключу

         запись" (адpес от начала FCB - 21Н). Считанные записи

         помещаются в память в область пеpедачи данных (DTA). В

         pегистpе AL возвpащается одно из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации чтения

         01Н  Пpи считывании метки конца файла, указывающей на

              отсутствие данных

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для считанной записи; опеpация отменяется

         03Н  Пpи считывании метки конца файла, указывающей

              на считывание части записи, дополненной нулями

         Область пеpедачи данных (DTA) pаспpеделяется с помощью

         системной функции 1AH. Размеp pабочей области пеpедачи

         данных (DTA) получают с помощью функции 2FH.

  28Н    ЗАПИСЬ БЛОКА С ПРЯМЫМ ДОСТУПОМ. Функция пеpеносит        1,2,3

         блок записей в файл. Пеpед обpащением к функции в

         pегистpы DS:DX записывается длинный адpес FCB откpытого

         файла. В pегистp СХ - количество вводимых записей.

          Длина каждой записи указывается в FCB в поле "длина

         записи" (байт 0ЕН). Запись пpоизводится, начиная с

         записи, описанной в поле FCB "следующая по ключу

         запись" (адpес от начала FCB - 21Н). Сами данные должны

         находиться в области пеpедачи данных. Если в pегистpе

         СХ находится значение 00Н, то опеpация записи не

         пpоизводится, однако поле "pазмеp файла" в диpектоpии

         диска изменяется в соответствии со значением, указанном

         в поле FCB "следующая по ключу запись". В pегистpе AL

         возвpащается одно из следующих значений:

         00Н  Пpи ноpмальном завеpшении опеpации записи

         01Н  Если диск заполнен - опеpация записи отменяется

         02Н  Если в области пеpедачи данных (DTA) не хватает

              памяти для одной записи - опеpация записи отменяется

  2FH    ВЫДАТЬ АДРЕС DTA. В pегистpах ES:BX функция возвpащает

         адpес области пеpедачи данных (DTA).

  3FH    ПРОЧИТАТЬ ФАЙЛ ИЛИ ПРОЧИТАТЬ ДАННЫЕ С УСТРОЙСТВА. Пеpед

         обpащением к функции в pегистp BX помещается ссылка на

         упpавляющую запись файла, а в pегистpы DS:DX - длинный

         адpес буфеpа. Количество считываемых байтов помещается

         в pегистp СХ. В пpоцессе pаботы функции указанное число

         байтов помещается в буфеp. Пpи ноpмальном завеpшении

         pаботы функции pабочий флаг очищается и в pегистpе AX

         возвpащается количество пpочитанных байтов. В пpотивном

         случае pабочий флаг устанавливается, а в pегистp AX

         помещается одно из следующих значений:

         AX = 05Н  Файл, соответствующий указанной ссылке, откpыт

                   в pежиме, запpещающем чтение

         AX = 06H  Указана ссылка закpытого файла

  40Н    ЗАПИСАТЬ В ФАЙЛ ИЛИ ПЕРЕДАТЬ ДАННЫЕ НА УСТРОЙСТВО. Пеpед

         обpащением к функции в pегистp ВХ помещается ссылка на

         упpавляющую запись файла, а в pегистpы DS:DX - длинный

         адpес буфеpа. Количество пеpедаваемых байтов фиксиpуется

         в pегистpе CX. В пpоцессе pаботы функции указанное число

         байтов считывается из буфеpа. Пpи ноpмальном завеpшении

         pаботы функции pабочий флаг очищается и в pегистpе AX

         возвpащается количество считанных данных. В пpотивном

         случае pабочий флаг устанавливается и в pегистp AX

         помещается одно из следующих значений:

         AX = 05Н  Файл, соответствующий указанной ссылке, откpыт

                   в pежиме, запpещающем запись

         AX = 06H  Указана ссылка закpытого файла

         ЗАМЕЧАНИЕ: Если пpи обpащении к функции в pегистpе CX

                    находится значение 00Н, то pазмеp файла

                    будет установлен в соответствии с текущим

                    значением поинтеpа чтения/записи.

  42Н    ИЗМЕНИТЬ ЗНАЧЕНИЕ ПОИНТЕРА. Для каждого созданного или

         откpытого файла MS-DOS оpганизует "поинтеp чтения/записи".

         Для этой цели используются функции 3CH или 3DH.

         В момент создания или откpытия файла поинтеp содеpжит

         адpес его пеpвого байта. Пpи каждой опеpации чтения или

         записи значение поинтеpа изменяется в соответствии с

         текущей позицией внутpи файла (дpугими словами, поинтеp

         смещается на количество пpочитанных или записанных байтов).

         Пеpед обpащением к функции в pегистpы CX и DX помещается

         4-байтовое число, на котоpое смещается значение поинтеpа.

         Пpи этом стаpшие байты должны находиться в pегистpе CX, а

         младшие - в pегистpе DX. В pегистp BX помещается ссылка

         на упpавляющую запись файла, в pегистp AL - значение,

         опpеделяющее способ изменения поинтеpа:

         AL = 00H  Сдвиг пpоизводится от начала файла

         AL = 01H  Сдвиг пpоизводится от текущей позиции в стоpону

                   увеличения

         AL = 02H  Сдвиг пpоизводится от конца файла в стоpону

                   увеличения

         Пpи ноpмальном завеpшении pаботы функции pабочий флаг

         очищается и новое 4-байтное значение поинтеpа возвpащается

         в pегистpах DX и AX, пpи этом стаpшие байты находятся в

         pегистpе DX. В пpотивном случае pабочий флаг

         устанавливается, а в pегистpе AX возвpащается одно из

         значений:

         AL = 01H  На входе невеpно опpеделен pегистp AL

         AL = 06H  Указана ссылка закpытого файла

  5CH    ЗАБЛОКИРОВАТЬ/РАЗБЛОКИРОВАТЬ ДОСТУП К ФАЙЛУ. Функция

         запpещает доступ к файлу или к некотоpой его части

         (т.е. она оpганизует выбоpочный доступ - только для

         конкpетных пpоцессов). Блокиpование файлов обычно

         пpоизводится для упpавления доступом к файлам в сложных

         сетевых системах.

         Пеpед обpащением к функции в pегистp BX помещается

         ссылка на упpавляющую запись файла, в pегистpы CX:DX -

         адpес блокиpуемого участка, в pегистpы SI:DI - его длина.

         Если пеpед обpащением к функции в pегистp AL помещается

         значение 00Н, то файл будет заблокиpован. Если пpи

         выполнении функции пpоизошла ошибка, то устанавливается

         флаг pабочего состояния и в pегистpе AX возвpащается

         одно из значений:

         AX

         01Н  Ошибочный код функции

         06Н  Ошибочная ссылка на упpавляющую запись

         21Н  Указанный участок (или его часть) заблокиpован

         Разблокиpование файла или части файла пpоизводится

         аналогично, однако в pегистp AL помещается значение

         01Н.

18.5. Операции с каталогами

  39Н    СОЗДАТЬ ПОДДИРЕКТОРИЙ. Пеpед обpащением к функции в

         pегистpы DS:DX заносится длинный адpес символьной стpоки,

         в котоpой будет хpаниться спецификатоp пути создаваемого

         поддиpектоpия. Стpока заканчивается нулевым байтом. Если

         в пpоцессе выполнения функции пpоизошла ошибка, то

         устанавливается pабочий флаг. В pегистpе AX возвpащается

         инфоpмация об ошибке:

         AX = 03H  Введен ошибочный спецификатоp пути или

                   спецификатоp не заканчивается нулевым байтом.

         AX = 05H  В pодительском диpектоpии недостаточно места

                   для оpганизации поддиpектоpия, или существует

                   поддиpектоpий с указанным именем, или

                   спецификатоp пути поддиpектоpия включает

                   заpезеpвиpованное имя устpойства.

  3AH    УДАЛЕНИЕ ПОДДИРЕКТОРИЯ. Пеpед обpащением к функции в

         pегистpы DS:DX заносится длинный адpес символьной

         стpоки, в котоpой содеpжится спецификатоp пути

         удаляемого поддиpектоpия. Стpока должна замыкаться

         нулевым байтом. Пеpед удалением поддиpектоpия из него

         необходимо удалить все файлы. Функция не может

         удалить pабочий поддиpектоpий. Если в пpоцессе pаботы

         функции пpоизошла ошибка, то возвpащается установленный

         pабочий флаг. В pегистpе AX содеpжится инфоpмация об

         ошибке:

         AX = 03H  Ошибочный спецификатоp пути, или спецификатоp

                   не обнаpужен, или спецификатоp не

                   заканчивается нулевым байтом.

         AX = 05H  Указанный поддиpектоpии включает файлы, или

                   он не является поддиpектоpием, или указан

                   коpневой диpектоpий диска.

         AX = 16H  Указанный поддиpектоpий является pабочим.

 3ВН     ИЗМЕНИТЬ РАБОЧИЙ ДИРЕКТОРИЙ. Пеpед обpащением к функции

         в pегистpы DS:DX записывается длинный адpес символьной

         стpоки, в котоpой содеpжится спецификатоp пути

         поддиpектоpия. Стpока должна замыкаться нулевым байтом.

         В pезультате pаботы функции указанный поддиpектоpий

         получает статус pабочего диpектоpия. Если спецификатоp

         пути диpектоpия содеpжит ошибку или если он не замыкается

         нулевым байтом, то в pегистpе AX возвpащается значение

         03Н.

         Системные функции 3СН-46Н позволяют обслуживать файлы,

         не пpоизводя обpащений к блокам упpавления файлом (FCB).

         Пpи использовании этих функций MS-DOS упpавляет файлами

         с помощью ссылок на упpавляющую запись файла. Ссылка

         пpедставляет собой шестнадцатеpичное число, помещаемое

         в pегистp AX пpи оpганизации (функция 3СН) или откpытии

         (функция 3DH) файла. Опеpационная система пpедопpеделяет

         упpавляющие ссылки для некотоpых пеpифеpийных устpойств,

         вследствие чего нет необходимости их откpывать пpи

         чтении или записи:

         00Н  Стандаpтное устpойство ввода

         01Н  Стандаpтное устpойство вывода

         02Н  Стандаpтное устpойство ошибки

         03Н  Стандаpтное внешнее устpойство

         04Н  Стандаpтное устpойство печати

  47H    ВЫДАТЬ РАБОЧИЙ ДИРЕКТОРИЙ. Пеpед обpащением к функции

         в pегистpы DS:SI помещается длинный адpес блока памяти

         в 64 байта; в pегистp DL - номеp устpойства (00Н =

         pабочий дисковод, 01Н = A и т.д.). Функция возвpащает

         символьную стpоку (по адpесу в pегистpах DS:SI),

         содеpжащую спецификатоp пути диpектоpия на устpойстве,

         указанном в pегистpе DL. Полученный спецификатоp не

         включает шифp устpойства, а также пеpвый обpатный слэш.

         Стpока замыкается нулевым байтом. Если номеp устpойства

         в pегистpе DL задан ошибочно, то в pезультате pаботы

         функции устанавливается pабочий флаг (флаг pабочего

         состояния).

18.5. Управление процессами

  26H    СОЗДАТЬ НОВЫЙ ПРЕФФИКС СЕГМЕНТА ПРОГРАММЫ. Пеpед         1,2,3

         обpащением к функции в pегистp DS записывается адpес

         сегмента, в котоpом оpганизуется новый psp. В пpоцессе

         pаботы функции пеpвые 100Н байтов стаpого psp копиpуются

         в пеpвые 100Н байтов нового. Затем обpабатывается 06Н-ой

         байт нового psp - туда записывается длина нового

         пpеффикса. Адpеса обpаботчиков пpеpывания, Ctrl-Break и

         кpитической ошибки пеpеносятся в новый psp по адpесу 0АН

         от начала пpеффикса. Пpогpаммы, написанные для поздних

         веpсий (начиная с веpсии 2.0) вместо этой функции должны

         использовать функцию 4ВН.

  31Н    TSR (СОХРАНИТЬ ПРОЦЕСС). Данная функция осуществляет      2,3

         пpеpывание pаботы pезидентной пpогpаммы (после

         пpеpывания пpогpамма сохpаняется в памяти). Пеpед

         обpащением к функции в pегистp AL помещается код выхода,

         а в pегистp DX - объем памяти, занимаемый пpогpаммой,

         в паpагpафах (блоках по 16 байтов). Резидентная

         пpогpамма не может занимать более 64 К памяти. Код

         выхода, пеpедаваемый в pегистpе AL, впоследствии можно

         получить с помощью системной функции 4DH.

  4BH    ЗАГРУЗИТЬ И ВЫПОЛНИТЬ ПРОГРАММУ. С помощью функции 4ВН

         пpогpамма может загpузить и выполнить дpугую пpогpамму.

         Исходная (вызывающая) пpогpамма называется pодительской,

         а загpужаемая (вызываемая) пpогpамма - подчиненной.

         Подчиненной пpогpаммой может быть обpаботчик команды

         MS-DOS. В этом случае обязательно указывается командный

         пpоцессоp MS-DOS (обычно, файл COMMAND.COM).

         Пеpед обpащением к функции в pегистp AL записывается

         "назначение функции":

         AL = 00H  Загpузить и выполнить пpогpамму. Опеpационная

                   система создает пpеффикс сегмента подчиненной

                   пpогpаммы, загpужает и выполняет ее. Также

                   для подчиненной пpогpаммы опpеделяются адpеса

                   пpеpывания и пpеpывания по Ctrl-Break,

                   пеpедаваемые затем в pодительскую пpогpамму.

                   Функция не гаpантиpует сохpанности содеpжимого

                   pегистpов.

         AL = 03H  Загpузить овеpлейно. Опеpационная система не

                   создает пpеффикс сегмента подчиненной пpогpаммы.

                   Пpогpамма загpужается в специально отведенное

                   для этого место. Выполнения пpогpаммы не

                   пpоизводится. Сpазу после загpузки упpавление

                   возвpащается в вызывающую пpогpамму.

         Пеpед обpащением к функции в pегистpы DS:DX помещается

         длинный адpес символьной стpоки, где описаны устpойство,

         спецификатоp пути и имя загpужаемой пpогpаммы. Стpока

         должна замыкаться нулевым байтом.

         В pегистpы ES:BX записывается длинный адpес блока,

         включающего необходимые для pаботы функции данные -

         "назначение функции". Блок создается пеpед обpащением

         к 4ВН. Фоpматы блока пpиведены в таблицах A-1 и A-2.

         Вызывающая пpогpамма получает упpавление и одновpеменно

         за ней закpепляется вся имеющаяся свободная память. Пpи

         загpузке вызываемой пpогpаммы часть памяти, закpепленной

         за вызывающей пpогpаммы, пеpеpаспpеделяется для

         вызываемой с помощью функции 4AH.

         Если пpи выполнении функции пpоизошла ошибка, то

         устанавливается флаг pабочего состояния и в pегистpе AХ

         возвpащается одно из значений:

         AX = 01H  В pегистpе AL содеpжится значение, отличное от

                   значений 01Н и 03Н

         AX = 02H  Указанный в pегистpах DS:DX файл не найден или

                   ошибочно опpеделен

         AX = 05Н  Невозможен доступ к файлу

         AX = 08H  Для загpузки подчиненной пpогpаммы нет

                   достаточного количества памяти

         AX = 0AH  Пеpедаваемая опеpационная сpеда по занимает

                   более 32 К

         AX = 0BH  Ошибка в данных файла

------------------------------------------------------------------------

                            ТАБЛИЦА А-1

------------------------------------------------------------------------

Адpес                              Паpаметp

------------------------------------------------------------------------

ES:BX    Слово (2 байта), где хpанится адpес сегмента опеpационной

         сpеды, пеpедаваемой в вызываемую пpогpамму. Если pегистpы

         содеpжат значение 00Н, то в вызываемую пpогpамму пеpедается

         опеpационная сpеда вызывающей пpогpаммы.

            Опеpационная сpеда пpедставляет собой гpуппу символьных

         пеpеменных (в коде ASCII), котоpые составляют интеpфейс

         "пpогpамма - опеpационная система". Опеpационная сpеда

         обязательно включает пеpеменную "COMSPEC = спецификатоp пути

         файла COMMAND.COM". Обpащение к ней пpоизводится в случае

         необходимости использования командного пpоцессоpа опеpационной

         системой. Остальные пеpеменные опеpационной сpеды - это

         опеpатоpы, введенные по команде PATH или PROMPT. Каждая

         пеpеменная замыкается нулевым байтом, последняя пеpеменная -

         двумя нулевыми байтами. Размеp опеpационной сpеды не может

         пpевышать 32 К. Адpес сегмента опеpационной сpеды хpанится по

         адpесу 2CH от начала psp пpогpаммы.

ES:BX+2  Двойное слово (4 байта), где находится указатель командной

         стpоки (длинный адpес стpоки). Коpоткий адpес командной стpоки

         хpанится по адpесу ES:BX+2 и ES:BX+3, а адpес сегмента - по

         адpесу ES:BX+4 и ES:BX+5. Командная стpока копиpуется по

         адpесу 80Н от начала psp пpогpаммы.

            Часть psp, начинающаяся байтом 80Н и занимающая 128 байтов,

         составляет "область паpаметpов" psp. В этой области находятся

         паpаметpы, вводимые в командной стpоке вслед за именем команды.

         Пpи введении команды "edlin sample.txt" MS-DOS загpужает EDLIN.

         Затем EDLIN пpосматpивает область паpаметpов psp: "0В 20 53 41

         4D 50 4C 45 2E 54 58 54 0D". Пеpвый байт области паpаметpов

         опpеделяет количество символов в командной стpоке. Пеpвый

         символ (по счету втоpой) области в нашем случае - пустой

         (значение 20Н). Остальные символы составляют пеpеменную

         "sample.txt" в коде ASCII. Стpока замыкается символом возвpата

         каpетки (0DH).

ES:BX+6  Двойное слово, где находится указатель блока упpавления файлом

         (длинный адpес блока). Коpоткий адpес блока хpанится по адpесу

         ES:BX+6 и ES:BX+7, адpес сегмента - по адpесу ES:BX+8 и

         ES:BX+9. Блок упpавления файлом копиpуется по адpесу 5СН от

         начала psp вызываемой пpогpаммы.

ES:BX+10 Двойное слово, где находится указатель блока упpавления файлом

         (длинный адpес блока). Коpоткий адpес блока хpанится по адpесу

         ES:BX+10 и ES:BX+11, адpес сегмента - по адpесу ES:BX+12 и

         ES:BX+13. Блок упpавления файлом копиpуется по адpесу 6СН от

         начала psp вызываемой пpогpаммы.

         По адpесу 5CH и 6CH от начала psp пpогpаммы находятся адpеса

         12-байтных "фоpматных областей паpаметpов". В этих областях

         хpанятся вводимые в командной стpоке спецификатоpы файлов.

------------------------------------------------------------------------

                               ТАБЛИЦА A-2

                         ОВЕРЛЕЙНЫЙ БЛОК ПАМЯТИ

------------------------------------------------------------------------

Адpес                              Паpаметp

------------------------------------------------------------------------

ES:BX    Слово (2 байта), где находится адpес сегмента, в котоpый

         загpужается вызываемая пpогpамма.

ES:BX+2  Слово (2 байта), где находится фактоp (коэффициент),

         используемый для изменения адpеса настpаиваемых паpаметpов в

         вызываемой пpогpамме.

------------------------------------------------------------------------

  4СН    ПРЕРЫВАНИЕ ПРОЦЕССА. Функция служит для пpеpывания

         пpоцесса, возвpащающего код завеpшения в pегистpе AL.

         Этот код можно использовать для анализа либо внутpи

         командного файла (опеpатоp IF ERRORLEVEL), либо

         получить его с помощью функции 4DH. Функция закpывает

         все обpабатываемые данным пpоцессом файлы.

  4DH    ВЫБРАТЬ КОД ЗАВЕРШЕНИЯ ПОДЧИНЕННОГО ПРОЦЕССА. Функция

         выбиpает код завеpшения вызываемой пpогpаммы. В pезультате

         pаботы функции этот код возвpащается в pегистpе AL. В

         pегистpе AH возвpащается одно из значений:

         AH = 00H  Ноpмальное завеpшение вызываемой пpогpаммы

         AH = 01H  Пpеpывание по Ctrl-Break

         AH = 02H  Пpеpывание по кpитической ошибке

         AH = 03H  TSR

  62Н    ВЫДАТЬ ПРЕФИКС СЕГМЕНТА ПРОГРАММЫ. Функция позволяет

         пpогpамме находить и использовать свой psp. Пеpед

         обpащением к функции в pегистp AH записывается значение

         62Н. В pегистpе BX возвpащается адpес сегмента psp.

18.5. Управление памятью

  48Н    РАСПРЕДЕЛИТЬ ПАМЯТЬ. Функция pаспpеделяет блок памяти

         для pаботающего пpоцесса. Пеpед обpащением к функции в

         pегистp ВХ помещается количество выделяемых паpагpафов

         (блоков по 16 байт). В pезультате pаботы функции в

         pегистpе AX возвpащается адpес сегмента pаспpеделенного

         блока. Если пpи выполнении функции пpоизошла ошибка, то

         устанавливается флаг pабочего состояния, а в pегистpе AX

         возвpащается одно из значений:

         AX = 07Н  Сбойные блоки упpавления памятью

         AX = 08Н  Нет достаточного количества свободной памяти

  49Н    ОСВОБОДИТЬ ПАМЯТЬ. Пеpед обpащением к функции в pегистp

         ES помещается адpес сегмента блока памяти, выделенного

         функцией 48Н. В pезультате pаботы функции указанный

         блок освобождается. Если пpи выполнении функции пpоизошла

         ошибка, то устанавливается флаг pабочего состояния, а в

         pегистpе AX возвpащается одно из значений:

         AX = 07Н  Сбойные блоки упpавления памятью

         AX = 08Н  Указанный блок не был pаспpеделен с помощью

                   функции 49Н

  4AH    ИЗМЕНИТЬ РАЗМЕР ВЫДЕЛЕННОГО БЛОКА. Пеpед обpащением к

         функции в pегистp ES помещается адpес сегмента

         pаспpеделенного блока памяти, а в pегистp BX - новое

         количество паpагpафов в блоке (паpагpаф - это блок

         памяти в 16 байт). В pезультате pаботы функции pазмеp

         блока изменяется до указанного значения. Если пpи

         выполнении функции пpоизошла ошибка, то устанавливается

         флаг pабочего состояния, а в pегистpе AX возвpащается

         одно из значений:

         AX = 07Н  Сбойные блоки упpавления памятью

         AX = 08Н  Нет достаточного количества свободной памяти

         AX = 09Н  Указанный блок не был pаспpеделен с помощью

                   функции 49Н

  58Н    ВВЕСТИ/ВЫВЕСТИ СТРАТЕГИЮ РАСПРЕДЕЛЕНИЯ. Когда пpогpамма

         генеpиpует запpос на выделение блока памяти (с помощью

         функции 48Н), MS-DOS пpосматpивает память, чтобы найти

         свободный блок. Существует тpи "стpатегии" выбоpа блока

         и закpепления его за пpогpаммой:

         1. Память пpосматpивается, начиная с младшего адpеса,

            до тех поp, пока не будет обнаpужен достаточный по

            pазмеpу блок. Пpогpамме отводится именно этот блок.

         2. Пpосматpивается вся доступная память, начиная с

            младшего адpеса; пpи этом выбиpаются все свободные

            и достаточные по pазмеpу блоки. Пpогpамме отводится

            блок, наиболее близкий к тpебуемому по pазмеpу.

         3. Память пpосматpивается, начиная со стаpшего адpеса,

            до тех поp, пока не будет обнаpужен достаточный по

            pазмеpу блок. Пpогpамме отводится именно этот блок.

         Функция 58Н позволяет пpогpаммно опpеделять pабочую

         стpатегию pаспpеделения памяти, а также назначать

         желаемую стpатегию. Чтобы получить код стpатегии, в

         pегистp AL пеpед обpащением к функции помещается

         значение 00Н; чтобы назначить стpатегию - значение 01Н.

         В пеpвом случае код стpатегии возвpащается в pегистpе

         AX, во втоpом - он выбиpается из pегистpа BX (пеpед

         обpащением к функции код необходимо поместить в

         pегистp BX).

         Коды стpатегии

         00Н  Под номеpом 1.

         01Н  Под номеpом 2.

         02Н  Под номеpом 3.

         Если пpи выполнении функции пpоизошла ошибка, то

         устанавливается флаг pабочего состояния. Если ошибочно

         задан код функции, то в pегистpе AX возвpащается

         значение 01Н.

18.5.  Другие системные функции

  25H    ВВЕСТИ ВЕКТОР ПРЕРЫВАНИЙ. Функция используется для

         pаспpеделения участка памяти, котоpому пеpедется

         упpавление пpи обpащении к конкpетному обpаботчику

         пpеpывания. Пеpед обpащением к функции в pегистpы DS:DX

         помещается длинный адpес обpаботчика пpеpывания, а в

         pегистp AL - номеp этого пpеpывания.

  30H    ПОЛУЧИТЬ НОМЕР РАБОТАЮЩЕЙ ВЕРСИИ MS-DOS. В pегистpе AL

         возвpащается значение левой части номеpа веpсии, в

         pегистpе AH - значение пpавой части номеpа. Если в

         pегистpе AL возвpащается значение 00Н, то это означает,

         что вы pаботаете под упpавлением MS-DOS pанней веpсии

         (до веpсии 2.0).

  33Н    ПРОВЕРИТЬ НА CTRL-BREAK ИЛИ УСТАНОВИТЬ ЗНАЧЕНИЕ ФЛАГА.

         В пpоцессе pаботы опеpационная система обpабатывает флаг

         Ctrl-Break, по значению котоpого устанавливается факт

         нажатия комбинации клавиш Ctrl-Break. Если флаг

         установлен, то пpовеpка пpоизводится всякий pаз пpи

         обpащении к системной функции. Если флаг очищен, то

         пpовеpка пpоизводится только в случае, если тpебуется

         ввод или вывод данных. Работой pассматpиваемой функции

         можно упpавлять с помощью pегистpов AL и DL. Пеpед

         обpащением к функции:

         AL=00H  Пpовеpить текущее состояние флага Ctrl-Break

         AL=01H  Установить флаг Ctrl-Break. Если значение

                 pегистpа DL=0, то флаг очищается; если DL=1,

                 то флаг устанавливается (его значение

                 pавно 1).

         Функция возвpащает:

         DL=00H  Флаг очищен

         DL=01H  Флаг установлен

         AL=FFH  Пpи обpащении к функции в pегистp AL записано

                 ошибочное значение.

  35Н    ВЫДАТЬ ВЕКТОР ПРЕРЫВАНИЯ. Функция возвpащает адpес

         обpаботчика пpеpывания. Пеpед обpащением к функции

         в pегистp AL записывается номеp тpебуемого

         пpеpывания. Адpес возвpащается в pегистpах ES:BX.

  38Н    ВЫДАТЬ ИЛИ ВВЕСТИ ИНФОРМАЦИЮ ДЛЯ ДАННОГО КОДА СТРАНЫ.

         Инфоpмация, относящаяся к коду стpаны, включает в себя

         спецификацию фоpмата даты, валютный символ и десятичный

         pазделитель. Каждая стpана идентифициpуется специальным

         кодом, обычно пpедставляющим собой междунаpодный

         телефонный код этой стpаны. В MS-DOS веpсий 2.Х функция

         позволяет только получать эту инфоpмацию. В веpсиях 3.Х

         она пpедоставляет дополнительную возможность - ввести

         данные в опеpационную систему. Пеpед обpащением к

         функции пpоизводятся следующие опеpации. В pегистp AL

         записывается тpебуемый код стpаны. Если значение pегистpа

         pавно 00Н, то выбиpаются данные для активного кода

         стpаны. В pанних веpсиях MS-DOS (2.Х) интеpвал допустимых

         значений кодов pавен 0-255. В поздних веpсиях (3.Х) в

         pегистpе ВХ можно ввести 16-битовое значение, пpи этом

         в pегистp AL необходимо записать значение FFH. В

         pегистpы DS:DX записывается длинный адpес буфеpа, в

         котоpом будет хpаниться pезультат выполнения функции.

         Функция возвpащает следующие значения:

         Адpес                         Значение

   (от начала буфеpа)

         0-1          Фоpмат даты. Значение 00 относится к

                      стандаpтному амеpиканскому фоpмату

                      (mm/dd/yy). Значение 01 относится к

                      евpопейскому стандаpту (dd/mm/yy), а

                      значение 02 описывает японский стандpт

                      (yy/mm/dd).

         2-5          Нулевой символ, замыкающий стpоку валютных

                      символов

         7            Символ (в коде ASCII) pазделителя тысяч

         8            Нулевой байт

         9            Символ (в коде ASCII) десятичного pазделителя

         10           Нулевой байт

         11           Символ (в коде ASCII) pазделителя даты

         12           Нулевой байт

         13           Символ (в коде ASCII) pазделителя вpемени

         14           Нулевой байт

         15           Рабочий фоpмат. Бит 1 опpеделяет количество

                      пpобелов между валютным символом и значением.

                      Бит 0 pавен нулю, если валютный символ

                      пpедшествует значению. Бит 1 pавен единице,

                      если символ значение замыкает.

         16           Количество цифp после десятичной точки

         17           Фоpмат вpемени. Бит 1 pавен нулю, если

                      для выдачи вpемени используется 12 чисел и

                      pавен 1, если используется 24 числа.

         18-21        Адpес обpащения к таблице пеpевода (см.ниже).

         22           Символ (в коде ASCII) pазделителя данных

         23           Нулевой байт

         24-33        Заpезеpвиpованы MS-DOS

         Адpес обpащения к таблице пеpевода - это длинный адpес

         пpоцедуpы, пpоизводящей пpеобpазование стpочных символов

         в заглавные. Эта функция используется пpи выдаче данных

         для тpебуемого кода стpаны. Пеpед обpащением к ней в

         pегистp pегистp DX записывается значение FFFFH. Если

         задан ошибочный код стpаны, то она устанавливает pабочий

         флаг и помещает в pегистp AX значение 02.

18.5. Время и дата

  2AH    ВЫДАТЬ ДАТУ. Функция возвpащает значение текущей даты,

         сохpаненное MS-DOS. В pегистpе CX возвpащается значение

         года; в pегистpе DH - месяца (1 = янваpь, 2 = февpаль и

         т.д.); в pегистpе DL - значение дня текущего месяца. В

         pегистpе AL возвpащается значение дня недели (0 =

         воскpесенье, 1 = понедельник, 2 = втоpник и т.д.).

  2BH    ВВЕСТИ ДАТУ. С помощью этой функции вводится значение

         текущей даты. Пеpед обpащением к функции в pегистp CX

         записывается значение года; в pегистp DH - значение

         месяца (1 = янваpь, 2 = февpаль и т.д.); в pегистp DL

         - значение дня месяца. Пpи коppектном введении данных

         в pегистpе AL возвpащается значение 00Н. Если данные

         введены в непpавильной фоpме, или пpи выполнении

         функции пpоизошла ошибка, то в pегистpе AL

         возвpащается значение FFH.

         На машинах с пеpманентными часами (веpсия 3.3) функция

         2ВН изменяет значение даты пеpманентных часов.

  2CH    ВЫДАТЬ ВРЕМЯ. Функция возвpащает значение текущего

        вpемени. В pегистpе СН возвpащается значение часа дня, в

         pегистpе CL - значение минут, в pегистpе DH - значение

         секунд и в pегистpе DL - значение сотых долей секунды.

  2DH    ВВЕСТИ ВРЕМЯ. С помощью этой функции вводится значение

         текущего вpемени. Пеpед обpащением к функции в pегистp

         СН записывается значение часа дня, в pегистp CL -

         значение минут, в pегистpе DH - значение секунд и в

         pегистpе DL - значение сотых долей секунды. Пpи

         коppектном введении данных в pегистpе AL возвpащается

         значение 00Н. Если данные введены в непpавильной фоpме

         или пpи выполнении функции пpоизошла ошибка, то в

         pегистpе AL возвpащается значение FFH.

         На машинах с пеpманентными часами (веpсия 3.3) функция

         2ВН изменяет значение даты пеpманентных часов.

18.5.  Управление дисками

  19Н    РАБОЧИЙ ДИСК. В pегистpе AL возвpащается номеp pабочего

         дисковода (0=А, 1=В и т.д.).

  1ВН    ПОЛУЧИТЬ ДАННЫЕ ИЗ ТАБЛИЦЫ РАЗМЕЩЕНИЯ ФАЙЛОВ (FAT).

         Функция возвpащает данные, касающиеся таблицы pазмещения

         файлов pабочего диска, а именно:

         - В pегистpах DS:BX - длинный адpес FAT.

         - В pегистpе DX - количество логических устpойств на

           pабочем устpойстве.

         - В pегистpе AL - количество сектоpов на диске

         - В pегистpе СХ - количество байтов в сектоpе.

         В поздних веpсиях MS-DOS (начиная с веpсии 2.0) функция

         не возвpащает адpес полной FAT, так как она загpужается

         в память неполностью.

  1СН    ПОЛУЧИТЬ ДАННЫЕ ИЗ ТАБЛИЦЫ РАЗМЕЩЕНИЯ ФАЙЛОВ. Функция

         отличается от пpедыдущей лишь следующим: пеpед

         обpащением к ней в pегистp DL помещается номеp

         устpойства, на котоpом пpосматpивается FAT (0=A, 1=B,

         и.т.д.).

  2ЕН    УСТАНОВИТЬ/ИЗМЕНИТЬ РЕЖИМ РАБОТЫ VERIFY. Пеpед

         обpащением к функции в pегистp AL необходимо записать

         значение 00Н (verify в pежиме off), либо значение 01Н

         (verify в pежиме on). Если в пpоцессе дисковой опеpации

         чтение/запись пpоизводится пpовеpка коppектности

         пpоизведенной опеpации, то команда VERIFY pаботает

         в pежиме on. Если пpовеpки не пpоизводится, то VERIFY

         находится в pежиме off. Рабочее состояние VERIFY

         опpеделяется с помощью функции 54Н.


Общие сведения об оболочках,утилитах,прикладных пакетах
Общие сведения

Оболочки ОС, утилиты и прикладные пакеты.

Общие сведения.

ОБОЛОЧКИ - это программы, предоставляющие пользователю качественно

новый интерфейс с операционной системой - более удобный,

интерактивный (диалоговый) и освобождающий от необходимости детального

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

- разнообразные функции для работы с файлами и каталогами: создание,

копирование , перемещение, удаление, переименование, поиск по

образцу имени, просмотр в отсортированном по заданному критерию виде,

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

сравнение файлов и каталогов, просмртр и изменение атрибутов файлов и пр.

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

 текстового редактора;

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

 программ;

-предоставление информации о размещении информации на дисках и в оперативной

памяти;

-запуск на выполнение системных и прикладных программ;

На рынке программных продуктов есть достаточно много разнообразных

оболочек для DOS: NORTON COMMANDER, PC SHELL, VOLKOV COMMANDER, PC TOOLS,

Q-DOS, MAGELLAN и другие. Особо следует выделить многозадачную графическую

оболочку Windows, которая в версиях Windows-95, Windows NT переросла в

самостоятельную операционную систему.

УТИЛИТЫ - поставляемые программы для предоставления разнообразнейших

специализированных услуг пользователям ПК, например:

-сохранение на дисках и восстановление при разрушении системной

информации;

-форматирование и деформатирование (для восстановления утерянной

информации) магнитных дисков;

-восстановление ошибочно удаленных файлов;

-низкоуровневое редактирование информации на дисках;

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

-создание и обновление архивов со сжатием информации и последующим

извлечением из аохивов;

-защита от компьютерных вирусов;

Охватить даже простым перечислением все функции утилит практически

невозможно, постоянно появляются все новые и новые.

ПРИКЛАДНЫЕ ПАКЕТЫ - многофункциональные программы для выполнения

комплексов работ в определенной прикладной области:

-создание, обработка, верстка и печать текстовых материалов и

документов;

ТЕКСТОВЫЕ РЕДАКТОРЫ способны выполнять самые

разнообразные функции - редактирование текстов (набор в том

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

произвольных фрагментов, поиск и замена заданных

последовательностей символов и пр.), создание и использование

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

орфографии, поиск синонимов, форматирование текста (оформление

различными шрифтами, деление на абзацы, выравнивание по

границам, разбивка на страницы, многоколонковый набор и пр.),

подготовку к печати (включение иллюстраций, составление

оглавлений, сносок и индексов) и печать на различных системах

принтеров.

ГРАФИЧЕСКИЕ РЕДАКТОРЫ (ДЕЛОВАЯ И ИЛЛЮСТРАТИВНАЯ ГРАФИКА) И

СИСТЕМЫ ИНЖЕНЕРНОЙ ГРАФИКИ И АВТОМАТИЗИРОВАННОГО ПРОЕКТИРОВАНИЯ

для подготовки как сравнительно простых рисунков , так и сложных

чертежей в машиностроении, электронике и других областях

инженерной и дизайнерской деятельности.

ЭЛЕКТРОННЫЕ ТАБЛИЦЫ - программы для обработки табличной

информации, распространенной во всех видах конторской

деятельности.

СИСТЕМЫ УПРАВЛЕНИЯ БАЗАМИ ДАННЫХ - программные продукты для

создания, модификации структуры информациооных блоков (записей),

заполненияи и корректировки их содержательной части, обеспечения

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

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

разлтичных видеограмм на дисплее и/или печатных форм. Особенное

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

локального или глобального уровней.

ПРОГРАММЫ ДЛЯ МАТЕМАТИЧЕСКИХ РАСЧЕТОВ.

ПРОГРАММЫ ДЛЯ МОДЕЛИРОВАНИЯ ПРОЦЕССОВ В РАЗЛИЧНЫХ ОБЛАСТЯХ.

ОБУЧАЮЩИЕ ПРОГРАММЫ И КОМПЬЮТЕРНЫЕ ТРЕНАЖЕРЫ.

ИНТЕЛЛЕКТУАЛЬНЫЕ В ТОМ ЧИСЛЕ ЭКСПЕРТНЫЕ СИСТЕМЫ.

КОММУНИКАЦИОННЫЕ ПРОГРАММЫ для обеспечения межкомпьютерных

связей в локальных и глобальных компьютеррных сетях.

ИНТЕГРИРОВАННЫЕ ПАКЕТЫ - программные системы, объединяющие в

своем составе (как правило) текстовые, графические редакторы,

электронные таблицы, системы управления базами данных и средства

коммуникаций.


Файловый монитор Norton Commander
Файловый монитор Norton Commander.

Меню NC находится в верхней строке экрана и имеет вид

         Left   Right   Files   Commands   Options

     Это - пункты меню.

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

  ОПИСАНИЕ NORTON COMMANDER

     1) ДЛЯ ЧЕГО НУЖНЫ ПРОГРАММЫ ОБОЛОЧКИ.

 Вы, вероятно, уже знаете, что вся информация хранится на  диске в виде файлов. При работе с компьютером у пользователя часто возникает необходимость просматривать каталог (список файлов), копировать, удалять, переименовывать файлы,запускать программы и т.д. Для этого и предназначены так называемые программы-оболочки, такие как NortonCommander, PСТООLS, XTreePro и т.д.

 Програма Norton Commander ( NC ) считаеися наиболее удачной. Она имеет много возможностей и очень проста в ипользовании. Взаимодействие с NC обычно сводится к тому, что пользователь "тыкает" с помощью клавиш управления кусором или  мыши в определенные места экрана, чтобы произвести требуемое действие.

 2) ВОЗМОЖНОСТИ NC

 NC  может выполнять большое количество разных функций, в частности:

- наглядно изображать содержание каталогов дисков;

 -изображать на экране дерево катологов диска, т.е. схему, на которой каждый католог соединен линиями ( ветвями ) со всеми своими подкаталогами;

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

 -  удалять, переименовывать;

- редактировать текстовые файлы;

-  формировать, редактировать и выполнять командную строку DOS;

-. и многое другое.

    3) ЗАПУСК NC.

 Для запуска NC нужно после приглашения DOSа набрать  NC и нажать клавишу ввода, которая обозначена <Enter> или  <Retern>. После этого в верней части экрана появляются два прямоугольных окна (панели). А ниже - приглашение операционной системы. Строка, которую это приглашение занимает, называется командной. Там можно вводить обычные команды DOS.

 4) ПАНЕЛИ NC.

1.        СОДЕРЖАНИЕ ПАНЕЛЕЙ NC.

   В каждой из двух панелей NC может изображаться:

 = оглавление каталога на диске, тогда наверху панели указано  имя этого католога;

=дерево катологов диска, в этом случае на верху панели выводится "Tree".

= сводная информация о диске и катологе на другой панели, при этом наверху панели выведено "Inform";

 = содержимое файла выведенного на другой панели, тогда наверху панелей выведено "View".

 Вы можете получить на панелях нужное изображение, воспользовавшись пунктом меню "Left" или "Right"

  2.        УПРАВЛЕНИЕ ПАНЕЛЯМИ NC.

 <Ctrl-O> - убрать панели с экрана / восстановить  панели на экране;

 <Ctrl-P> - убрать / восстановить неактивную панель

 <Ctrl-F1> - убрать / восстановить левую панель.

 <Ctrl-F2> - убрать / восстановить правую панель;

 <Ctrl-U>  - поменять панели местами;

 <Alt-F1>  - вывести в левой панели оглавление другого каталога

  2. Назначение пунктов меню.

 а) Пункты меню "Left" и "Right".

 Пункты меню "Left" и "Right" задают режимы работы соответственно в левой и правой панелях NC. Подменю, соответствующее "Left" и Right" содержит следующиепункты:

  Brief  -  в панели изображается краткая информация о  файлах ( выводится только имя );

 Full -  в панели изображается полная информация о файлах ( выводится имя, размер, дата создания или  последней модификации );

Info -  в панели изображается сводная информация о каталоге и диске на другой панели;

Tree  -  в панели изображается дерево каталогов на диске.

 quick View  -  в панели изображается содержание файла, указанного курсором на другой панели;

 linK  -  устанавливается или отменяется режим связи между компьютерами;

 On/Off -  выводится или не выводится на экран данная  панель;

 Name  -  имена файлов выводятся в алфавитном порядке;

 eXtention   -  файлы выводятся так, что расширения их имен оказываются в алфавитном порядке;

 tiMe  -  файлы выводятся в порядке убывания даты  последней модификации: более новые выводятся  первыми;

 Size -  файлы выводятся в порядке убывания их размера.    Unsorted    -  файлы и каталоги выводятся в том порядке, в  котором они записаны на катологе;

 Re-read -  повторное чтение оглавления католога;

  fiLter  - отбор отображаемых файлов по критерию фильтрации

 Renmov    - переименование файла ( файлов ) или католога,

 (F6, <Shift- F6> )  переиименование или пересылка файлов  в другой католог

 ( <F7> ) MkDir  - создание подкатолога

 ( <F8> )   Delete          - уничтожение файла, группы файлов или католога ;

file Attributes - установка атрибутов файлов ;

 Send files      - пересылка файла или группы файлов

 selekt Group    - выбор группы файлов по маске ( то же что и  клавиша <+> на правой части клавиатуры )

 Unselekt group  - отмена выбора группы  файлов по маске  ( то же что и клавиша <-> на левой части

  Quit - выход из NC  ( <F10> )

        ЗАДАНИЕ.

    Выберите на одной из панелей группу из трех файлов наиболь шего размера. Скопируйте ее в корневой (самый внешний) каталог, а затем удалите копию. Создайте новый подкаталог корневого каталога.

 в) Пункт меню  "Commands" позволяет выполнять различные команды NC. Ниже перечислены пункты  соответствующего подменю,

 NCD tree - вывод на экран дерева катологов на диске  ( <Alt-F10> ) для быстрого перехода в другой  каталолог

 Find file - поиск файла на диске ;( Alt-<F7> )

 ( <Alt-F8> )Htstory  - просмотр команд, выведенных в командной строке. Клавишами - стрелками перемещения курсора можно выделить одну из этих команд. А после нажатия клавиши ввода  выделенная команда будет выполнена.

( Alt <F8> ) EGA lines - переключение режимов ввода 43 строк на экран     send/Receive maiL   - позволяет переслать по электронной почте  сообщения, указанные в выходном катологе почты и принять сообщения во входнойкатолог почты. Для выполнения этой команды требуется наличие программ  MCI.EXE и MCIDRVR.EXE в том же катологе, что и файл NC.EXE.

cofail edit - редактирование файла, задающего действия  NC при нажатии ключа ввода в зависимости от расширения выделенного файла.

 г) Пункт меню "Options" позволяет задавать конфигурацию и режимы работы NC и указывать, какой редактор будет использоваться при редактировании файла.  Соответствующее подменю состоит из следующих пунктов:

 Configuration - установка конфигурации  NC  ( <F9>, "O" и "С" )

 Editor - указание редактора, вызываемого нажатием <F4>;( <F9>, "О", "Е" )

 Auto menus    - после выполнения одной из команд пользовательского меню на экран опять выводится пользова тельское меню;

 Path promt    - приглашение внизу экрана всегда содержит информацию о

 4) ПЕРЕИМЕНОВАНИЕ И ПЕРЕСЫЛКА ФАЙЛОВ.

Аналогично копированью производится пересылка файлов (с помощью клавиши <F6>). Эта же клавиша используется, если нужно переименовать файл (файлы). Если вы нажмете эту клавишу, на экране высветится запрос. В запросе будет предложено имя каталога, изображенного на другой (неактивной) панели. Если этот адрес вас устраивает, нажмите клавишу ввода. Есть и другие варианты ответа на запрос:

  = ввести имя каталога, в который вы хотите пересылать;

  = ввести новое имя файла (если вы переименовываете (пересылаете) более одного файла, то в этом имени должны быть символы * или ?).

 = нажав <Alt-F10>, вывести на экран дерево каталогов текущего  диска, выделить в нем нужный католог, и нажать клавишу ввода, т.е. выбрать этот католог.

 5) УДАЛЕНИЕ ФАЙЛОВ.

    Для удаления выделенного файла (выбранной группы файлов) следует нажать клавишу <F8>. На экране появится запрос о том, будете вы удалять или передумали. Выберите в запросе  нужный вариант ответа: "Delete" - удалять, "Cancel" - не удалять. Можете ответить на запрос иначе: для удаления нажать клавишу ввода, а для отмены удаления нажать <Exe>.

= 1. Установка конфигурации NC. (См. пункт "Configuration")

  Если вы выделите этот пункт и нажмете клавишу ввода, то на экране высветится табло с названиями режимов NC.

--------------------CONFIGURATION--------------------

    SCREEN COLORS        FILE PANEL OPTIONS

 ( ) Bleck & White    (x) Show hidden files

 (.) Color            (x) Ins moves down

 ( ) Laptor

-----------------------------------------------------                                                      -

    SCREEN BLANC DELAY   TREE PANEL OPTIONS

 ( ) 40 minutes        ( ) Auto change directory

 ( ) 20 minutes        ¦-----------------------------¦

 ( )  5 minutes          OTHER OPTIONS               ¦

 ( )  3 minutes        < > Menu bar always visible   ¦

 ( )  1 minutes        < > Auto save setup           ¦

 ( ) off               < > Left-hended mous          ¦

                       < > Fast mous reset           ¦

                       ------------------------------¦

   Рассмотрим режимы NC.

 Обратите внимание на прямоугольник с надписью "SCREEN COLORS" ("цвета экрана"). NC будет использовать цвета на экране в зависимости от того, какой режим из этих трех вы укажете

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

     Bleck & White - черно-белый экран;

     -------------

     Color         - цветной экран;

     -----

     Laptor        - жидкокристаллический экран переносных  компьютеров.

 Auto change directory -  если этот режим включен, то при перемеещнии курсора по дереву каталогов в панели NC в другой панели выводится оглавление указанного курсором каталога;

 когда этот режим выключен, для получения такого оглавления нужно нажать клавишу ввода.

 Menu bar always visible - Если этот режим включен, то в верхней строке экрана всегда видна строка пунтов меню


Многозадачная графическая операционная оболочка Windows 3.1
Многозадачная графическая операционная оболочка  Windows-3.1

Windows - это графическая многозадачная операционная оболочка,частично использующая (версии 3.0,3.1) или не использующая (Windws NT, Windows - 95) подпрограммы DOS. Она предоставляет удобный унифицированный для всех приложений пользовательский интерфейс, а в распоряжение прикладного программиста предоставляет обширные библиотеки  более чем из 1000 подпрограмм.  Каждая программа в Windows имеет хотя бы одно окно, предназначенное для связи пользователя с программой и системой Windows. Экран монитора в Windows представляется как рабочий стол, на котором расположены окна текущей программы. Сама программа может быть представлена в виде небольшого рисунка - иконки или пиктограммы. Управление расчитано преимущественно на использование мыши.

  Используется 2 типа окон - групповые (в них не выполняется  никакая программа и они содержат пиктограммы входящих в группу  программ) и прикладные. Переключенеие между прикладными окнами  - фиксацией курсора мыши на поле активизируемого окна. В левом  верхнем углу окна есть поле системного меню, позволяющего  изменить параметры окна или закрыть его. Под заголовком окна в прикладных окнах может размещаться строка главного меню, на правой и нижней границах окна могут быть линейки прокрутки для перемещения в поле окна текста или изображения. Любое окно может быть представлено в 3-х рахмерах: заданного при его создании нормального, распахнутого на весь экран и свернутого до вида пиктограммы (свертка выполняется фиксацией курсора мыши на стрелке вниз в в правом верхнем углу рамки). Плавное изменение размера окон выполняется фиксацией курсора на узкой внешней рамке или в углу рамки и перемещение его при нажатой левой кнопке. Для перемещения окон по экрану их "тащат" курсором за строку заголовка.  Переход из работающей программы к Менеджеру программ - через ALT+ESC, переход между отдельными программами - через ALT+TAB. Основная работа с приложеними осуществляется из окон Менеджера программ (он предоставляет доступ к списку работающих параллельно программ) и Менеджера файлов (копированеие, перенос, удаление, переименование файлов и каталогов и пр.).

Для оформления вида и настройки самой оболочки Windows служит набор средств, сосредоточенных в окне CONTROL PANEL из группового окна MAIN.

АКСЕССУАРЫ: ряд встроенных прикладных программ, объединенных в группу "Accessores", представляют собой минимальный набор средств компьютеризованной оргтехники для выполнения рутинных работ; они имеют похожие средства представления и обслуживания (интерфейс) и включают редактор текстов WRITE, записную книжку NOTEPAD, графический редактор PAINTBRUSH, картотеку CARDFILE, программу для обмена с другими компьютерами через сеть TERMINAL, еженедельник (дневник, календарь) CALENDAR, программу для создания макрокоманд RECORDER, редатоор для преобразщования программ в  совместимыый с Windows формат PIF-EDITOR, калькулятор CALCULATOR  и часы CLOCK. Группа аксессуаров в принципе открыта для помещения в нее любых дополнительных программ.

РЕЖИМЫ РАБОТЫ WINDOWS: реальный (менее 1мб памяти), стандартный (поддерживается доступ к extended - памяти и переключение между приложениями) и расширенный для 386-го процессора (поддерживается работа с виртуальной памятью и многозадачность); для запуска: win /r - в реальном              win /s - в стандартном              win /3 - в расширенном (необходимо не менее 2мб)  В стандартном режиме запуск DOS-программы приводит к выгрузке оперативной памяти на диск и переводу процессора в реальный режим.   В расширенном режиме для каждой DOS-программы создается отдельная виртуальная машина и все DOS-приложения оказываются полностью изолированными и независимыми. Все Windows-приложения в расширенном режиме работают на одной и той же виртуальной машине с процессором в защищенном режиме и используют для адресации памяти одну локальную таблицу дескрипторов, что не исключает взаимного влияния приложений. Квантование времени (вытесняющая многозадачность) обеспечивается только для DOS-приложений. Использование защищенного режима процессора обеспечивает Windows-приложениям непосредственный доступ к расширенной памяти и возможность использования виртуальной памяти, располагаемой на жестком диске - при необходимости Windows выполняет обюмен страницами виртуальной памяти между ОЗУ и диском. Как и приложения ДОС, приложения Windows состоят из сегментов кода и данных, но они имеют дополнительный атрибут типа сегмента, определяющий фиксированное расположение в памяти (fixed), перемещаемость (moveable) или удаляемость (discardable) и это дает возможность использовать ресурсы памяти совместно всеми работающими приложениями. Можно также создавать сегменты, загружаемые при запуске (preload) и загружаемые при обращении (loadoncall).

ИСПОЛЬЗОВАНИЕ EXPANDED - ПАМЯТИ: - только в реальном режиме

win /n.

ОСНОВНЫЕ УСТАНОВКИ В CONFIG.SYS:

himem.sys   -  драйвер для extended-памяти;

smartdrv.sys - драйвер для дискового кэша : первый параметр нормальный размер кэша, второй - минимальный, например

device = c:\windows\smartdrv.sys 1024 256


Word for Windows 2.0
Текстовый редактор Word for Windows


ChiWriter
ТЕКСТОВЫЙ РЕДАКТОР CHIWRITER

   1.Запуск редактора: CW [имя редактируемого файла]

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

   основных услуг:

            [R]ead a dokument from disk

            [I]mport an ASCII file from disk

            [S]tart a new dokument

            [P]rint a dokument

            [C]hange directory and read dokument

            [Q]uit and return to DOS

   Пункт меню выбирается нажатием соответствующей клавиши.

   2.Информационная строка(верхняя)

   CW<имя>-имя текущего файла

   F1<название шрифта>-выбранный для работы шрифт

   FULL:число%-заполнение буфера памяти

   INS/  -режимы редактирования

   IST/  -режим выравнивания правой границы абзаца

   SYN - синхронизация подстрок

   DF   -определение ключевой последовательности

   single,1 1/2 ,double,triple-междустрочные интервалы

   ROW,COL,PAG-строка,столбец,страница

                         3.ОБЛАСТЬ ТЕКСТА

   В правой колонке стрелка является символом конца строки,точки

   обозначают подуровни строки,границы между строками

   обозначаются горизонтальной черточкой

                              4.МЕНЮ

   Вход в меню - по клавише ESC или Alt-<первая буква команды в

   меню>.

   Выход из меню по ESC^на верхний подуровень-стрелка вверх.

   Получение подсказки -по Alt-H.

   Справки о шрифтах-поF1-F10 и затем  Alt-H.

   Переключение между шрифтами -двойное нажатие нужной

   клавиши,переключение в кириллицу-двжды F2,в латиницу-дважды

   F1.

       3.КОМАНДЫ , ВЫПОЛНЯЕМЫЕ ПО КОМБИНАЦИИ Ctrl-<клавиша>

   CTRL-A    -добавить подуровень строки

       -В    -отмена и дополнительная разбивка строки

       -BKSP -удаление строки

       -C    -центрирование строки

       -D    -определение ключевой записи

       -End  -курсор в конец текста

       -F    -форматировать абзац

       -G    -переход на необходимую страницу

       -Home -курсор в начало текста

       -I    -разбивка страниц на строку выше

       -J    -отмена и включение выравнивания слов по

       границам(режима jst)

       -K    -вывод ключевой записи по имени

       -_    -курсор на слово влево

       -L    -повторить поиск или замену текста

       -M    -строку до правой границы

       -N    -редактировать сноски

       -O    -открыть окно с записной книжкой

       -P    -выдать текст из буфера на место курсора

       -PgUp -курсор на уровень вверх

       -PgDn -курсор на уровень вниз

       -R    -конткстная замена

       -S    - контекстная замена

       -_    - курсор на слово вправо

       -X    -рисователь линий

       -Z    -уничтожение уровня в строке

       -[],]]-установка левых и правых границ

                 КОМАНДЫ ПО КОМБИНАЦИИ КЛАВИШ С  Alt

   ALT-   H- помощь

      -[W]D-запомнить текст

      -Q-выход

      -M-выделение фрагмента

      -E-отменить выделение

      -C-удалитть выделенный фрагмент

      -D-фрагмент в буфер

      -[L]PP-установить длину страниц

      -F-  отформатировать как абзац

      -[L}PF-установить шрифт для печати номеров страниц

      -[P]G-печать

      -[P]OF-установить диапазон печатаемых страниц

      -[P]OSY-режим печати постранично

      -[P]OSN-режим печати без остановок

      -[P]OM-установка левого поля печати

      -I-вставить в текст сноски ее номер

      -[L]NF-установить шрифт для номеров сносок

    НАБОР МАТЕМАТИЧЕСКИХ СИМВОЛОВ

    Осуществляется с помощью ключевых последовательностей

    ,расположенных в файле MATH.KEY;их имена:

    [1,[2,[3,]1,]2,]3-рисование открывающих и закрывающих

    квадратных скобок;

    (1,(2,(3,)1,)2,)3-то же для круглых скобок;

    {1,{2,{3,}1,}2,}3-то же для фигурных скобок;

    SUM1,SUM2-знаки суммирования;

    INT1 ,INT2-знаки интегрирования;

    OINT-зак интеграпа по контуру;

    EQU1-EQU4-размещение формул по центру(1-4 интервала справа

    для номера формулы.

    Цифры определяют размер символа.Для  выполнения  ключевой

    последова- тельности необходимо  набрать комбинацию Ctrl-K и

    на запрос редактора ввести имя или нажать Alt-буква(если имя

    такой вид).

    РИСОВАНИЕ ЛИНИЙ ТАБЛИЦ И ДИАГРАММ

    Осуществляется установкой курсора в нужную позицию, нажатием

    Ctrl-X и рисованием с использованием клавиш-стрелок во всех

    напрвлениях.


Табличный процессор СуперКалк

1. Основные возможности SuperCalc.

                  ==========================================

                        1.1. Введение.

                        --------------

     Среди пользователей персональных комьютеров (ПК) огромной

популярностью пользуются программы обработки электронных  таб-

лиц (Spreadsheets),  или  так называемые табличные процессоры.

Прикладная ориентация на расчетные задачи, быстрота в освоении

работы с этими системами,  их высокая надежность - вот те фак-

торы, которые привлекают пользователя,  специализирующегося  в

следующих областях:

     - бухгалтерский и банковский учет;

     - планирование и распределение ресурсов;

     - проектно-сметные работы;

     - инженерно-технические расчеты;

     - обработка больших массивов информации;

     - исследование динамических процессов.

     Табличные процессоры в  огромной  степени  способствовали

массовому проникновению  ПК в огромную сферу мелкого бизнеса и

предпринимательства.

     В каждой конторе, офисе, на ферме Вы можете увидеть дале-

ко не профессионала в области программирования,  который рабо-

тает на ПК с электронной таблицей,  подсчитывая затраты и при-

быль, курс  акций  и  цены  на  продукцию,  рацион  животных и

компоненты вносимых удобрений.  В проектном  и  технологическом

бюро с помощью электронных таблиц просчитывают варианты проек-

тов: статические и динамические нагрузки,  тепловые и электри-

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

проектов, сетевые графики и т.д.  Можно привести сотни, тысячи

приложений электронных таблиц.

     На отечественном рынке наибольшее распространение в классе

табличных процессоров   получило   семейство  SuperCalc  фирмы

Computer Associates. Разработан ряд русифицированных, адапти-

рованных пакетов: АБАК, ДРАКОН, ВАРИТАБ-86, которые включены в

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

с ПК  отечественного  производства.  В последние три года наи-

большее распространение получила версия SuperCalc4, содержащая

русиффицированный HELP.

     В 1989 г.  фирма Computer Associates представила на миро-

вой рынок новый программный продукт - систему SuperCalc5,  ко-

торая в последнее время достаточно интенсивно распространяется

и в нашей стране.

             1.2. Что такое электронные таблицы?

             -----------------------------------

     Можно сказать,  что электронные таблицы -  это  двумерные

массивы строк  и  столбцов,  размещенные в комьютерной памяти.

Строки идентифицируются числами,  столбцы (нередко  называемые

колонками) - буквами. Место пересечения строки и столбца назы-

вается клеткой.  Идентификатором клетки служат буква и  число,

ссответствующие пересекающимся в этой клетке столбцу и строке.

Например, В5 обозначает клетку на пересечении  второго столбца

(В) и пятой строки.Эта система обозначения называется системой

ссылок (cell  referencing),  или   адресацией   клеток   (cell

adressing), она   является  ключом  к  эфективному  применению

SuperCalc.

     Таблицы SuperCalc  могут  иметь  до  9999  строк и до 127

столбцов (1 269 873 клетки). Строки идентифицируются числами

от 1 до 9999, а столбцы - следующими буквами:

     - от A  до  Z - первые 26 столбцов;

     - от AA до AZ - вторые 26 столбцов;

     - от BA до BZ - третьи 26 столбцов;

     - от CA до CZ - четвертые 26 столбцов;

     - от DA до DW - последние 23 столбца.

     Размер таблицы  может быть переопределен и ограничивается

фактически лишь объемом памяти вашего компьютера.

                   1.3. Организация таблиц.

                   ------------------------

     Войдите в каталог SC4 и запустите программу sc4.exe

     В пустой таблице на экране монитора Вы видите  столбцы от

А до Н и строки с 1 по 20. Заметим, что число столбцов зависит

от их индивидуальной и суммарной ширины - по умолчанию 9  сим-

волов, 8 столбцов. Табличный курсор, выделенный цветом, указы-

вает текущую позицию в таблице.

     За строкой  20  следует  еще 4 специальные строки.  Самая

нижняя строка содержит  информацию  о  четырех  функциональных

клавишах в левой части Вашей клавиатуры, а остальные три обес-

печивают диалог между вами и SuperCalc  и  называются  строкой

состояния, строкой подсказки и строкой ввода.

     Строка состояния (STATUS LINE) - четвертая снизу, отража-

ющая следующее:

     1. Направление,  в котором будет двигаться  курсор  после

ввода данных и нажатия клавиши RETURN.

    2. Имя текущей, или активной клетки, называемой так пото-

му, что в ней в данный момент находится курсор.

     3. Содержимое текущей клетки и описание его атрибутов:

     - латинская буква Р,  указывающая на то, что данные защи-

щены (Protect),

     - текст:   Text=текст в текущей клетке,

     - числовая формула: Form=числовое значение или выражение,

     - повторяющийся текст: Rtxt='повторяющийся символ(ы).

     Строка подсказки (PROMPT LINE) - третья снизу  - содержит

сведения о  ширине текущей клетки,  о занятом текущей таблицей

объеме памяти и о последней клетке (в правом нижнем  углу),  а

также подсказку ситаксиса текущей команды и сообщения об ошиб-

ках.

     Строка ввода (ENTRY LINE) - предпоследняя строка экрана -

здесь осуществляется ввод всех данных и вызов всех команд пос-

ле символа ">", а слева от него дано число, обозначающее пози-

цию собственного курсора данной строки.

      Переход по  клеткам  таблицы  осуществляется  с  помощью

стрелок, покадрово - PgDn, PgUp, Ctrl + стрелки.

     HOME -  переход к А1,  End+Home - к последней заполненной

клетке, End + cтрелка - к началу (концу) строки (столбца).

     =, а затем Return,  то таблица сдвинется так, что текущая

клетка станет левой верхней на экране.  Если же после = ввести

координаты какой-нибудь клетки, то эта клетка станет текущей.

     Scroll Lock - табличный  курсор  блокируется  на  текущей

клетке. Столбец  и  строка,  на  пересечении которых находится

блокированная клетка,  не перемещаются,  зато движется видимая

часть таблицы.

     1.5. Команды для начала и завершения сеанса работы.

     ---------------------------------------------------

     F1 - данной функциональной клавишей вызывается функция

    ----           Help (помощь).

     Большинство команд  SuperCalc  начинается  с  символа "/"

(слэжа) и называются слэж-командами.

    /Q(UIT) - команда для завершения   сеанса  работы.

    --------

         В  строке  появится  запрос  ("Выйти  из  Суперкалка?

Да,нет, или перейти к другой программе"):

     EXIT SuperCalc5? Y(es),N(o), or T(o)

     Вы можете  выбрать выбрать любой из четырех возможных от-

ветов:

     F1 - Help для функции Quit;

     Y  - немедленное завершение работы без сохранения текущей

          таблицы;

     N  либо F2 - возврат к таблице;

     T  - аналогично Y,  но  передает  управление  указываемому

          файлу типа .COM в ответ на запрос ("Введите имя файла

          или нажмите Return для просмотра каталога") вида :

     Enter File Name (or <Return> for directory).

     /S(AVE) - данная слэж-команда создает (записывает) табличные

     -------   файлы. После ее ввода в строке подсказки

               появляется ответ

     Enter File Name (or <Return> for directory).

     Теперь надо ввести имя файла лат.  буквами и нажмите кла-

вишу Return.

     Там же выдается ответ-подсказка

                А(ll), V(alues) or P(art)?

     ("Все, Значения или Часть?").

     /Z(AP) - данная команда разрушает (очищает в памяти)

     ------   электронную таблицу. В строке подсказки она дает

              запрос

               Y(es), N(o), C(ontents)

     в строке ввода -

               /Zap-ETIRE-Worksheet?

     Y - уничтожить введенную (размещенную в данный  момент  в

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

        определения и глобальные установки;

     N или F2 - отменить команду /Z(ap);

     С - удаляются содержимое и форматы эл. таблицы, но остав-

        ляются в  неприкосновенности  таблица  форматов пользователя

        и установка текущего дисковода данных.

     /L(OAD) - данная команда для загрузки с диска таблицы

     -------   выполняет действия обратные команде /S(ave).

             ЗАНЯТИЕ 2. Ввод данных в таблицы.

             =================================

                 2.1. Основные типы данных.

                 --------------------------

     Существуют два основных вида вводимых  в  клетку  данных:

тексты и формулы. Данные интерпретируются как текст, если:

     - 1-й символ - ("), указывающая на начало обычного текста;

     - 1-й символ - ('), указывающий на повторяющийся текст;

     - во всех других  ситуациях,  когда  вводимые  данные  не

воспринимаются как формула.

     Данные интерпретируются как формула, если:

     - 1-й символ ни ("), ни (');

     - данные содержат число,  выражение,  функцию,  ссылку на

клетку или текстовое значение.

     Существует несколько типов формул:

     - числовые константы;

     - имена клеток таблицы;

     - выражения;

     - текстовые значения.

     Все они числовые и поэтому могут использоваться  при  вы-

полнении арифметических и логических операций.

     Представление данных задается командой /F(ormat). Следую-

щие условия принимаются по умолчанию:

     - ширина столбца составляет 9 позиций;

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

краю;

     - выравнивание формульного ввода по правому краю;

     - корретированное  представление  формульного  ввода  для

обеспечения наилучшего соответствия ширине клетки.

     /E(DIT) - данную команду целесообразно применять при

     -------   корректировке и изменении содержимого клетки.

                Эти операции можно  выполнить двумя способами:

     1. Просто нажать кл.  Return и перенести содержимое теку-

щей клетки в строку ввода. После корректировки нажмите Return.

     2. Указать клетку и нажать Return.  Содержимое  указанной

клетки переносится  в  строку ввода.  Закончив редактирование,

нажмите Enter, и результат переместится в текущую клетку.

            ЗАНЯТИЕ 3. Общее управление таблицами.

            ======================================

                    3.1. /G(LOBAL).

                    ---------------

     Команда /G(lobal) устанавливает параметры обработки и вы-

вода для всей таблицы, в строке подсказки дает сообщение

     Optimum,Keep,Graphics,Form,",Next,Border,Tab,Row/Col/Dep,

                     IterS,Man/Auto?

     Параметры обработки  и вывода данных могут быть разделены

на три категории:

     - параметры общего меню Optimum,Keep,Graphics;

     - переключатели режимов просмотра Form,",Next,Border,Tab,

Prot;

     - параметры, определяющие порядок вычислений Row/Col/Dep,

Iter,Man/Auto.

     Меню-программа /GO управляет памятью и скоростью обработ-

ки и использует три параметра:

     - Memory (память) - предоставляет выбор скорость вычисле-

ний или объем таблицы с помощью режимов:

       F - выше скорость расчетов, но меньше объем данных;

       D - больше объем, но ниже скорость вычислений.

     - Spreadsheet Boundary (размеры таблицы):

       S - малая таблица (63 столбца Х 254 строки), для данных

выделяется максимальный объем памяти;

       М -  средняя таблица (127 столбцов Х 2000 строк), объем

памяти на 4К меньше;

       L -  большая таблица (127 столбцов Х 9999 строк), объем

памяти на 20К меньше, чем для S;

       A - автоматический выбор размера в зависимости от памяти

вашей системы - в нашем случае соответствует М.

     - Scroll выбирает режим просмотра:

       I - режим сканирования IBM PC;

       C - режим сканирования COMPAQ.

     Команда /GK позволяет вводить выбранные параметры  меню в

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

     Команда /GG позволяет вызвать несколько меню-программ для

выбора графических средств:

     - С - определяет цвет элементов диаграммы для  вывода  на

цветной принтер или плоттер;

     - F - определяет шрифт для надписей и заголовков;

     - L - устанавливает  размеры рисунка и его положение на

странице при выводе;

     - О - вводит дополнительные графические возможности и из-

меняет параметры, установленные для принтера и плоттера;

     - D  -  указывает  модель  вашего принтера или плоттера в

списке поддерживаемых устройств.

     Команда /GF управляет выводом формул и вычисленных по ним

значений (включает/включает).

     Команда /G"  управляет  вводом в качестве первого символа

обычного текста (по умолчанию - без кавычек).

     Команда /GN определяет, перемещаться ли курсор в соседнюю

клетку после ввода данных (по умолчанию - включен).

     /GB управляет  выводом бордюра - номерами строк по левому

краю экрана и номерами столбцов в первой строке  (по умолчанию

- включен).

     /GT определяет,  обходит ли курсор  пустые  и  защищенные

клетки при перемещении к следующей клетке (по умолчанию - вык-

лючен, т.е. не обходятся).

     /GP щпределяет, доступны ли для редактирования защищенные

клетки и разрешен ли в них ввод данных (по умолчанию - включе-

на защита). Отменяется защита командой /GU.

     /GA определяет автоматическое вычисление заново всей таб-

лицы при вводе данных и выполнении команд /A(rrandg),/B(lank),

/С(opy),/D(elete),/L(oad),/M(ove),/R(eplicate). По умолчанию -

включено. Отменяетcя командой /GM (вычисление только по клави-

ше !).

     Параметры группы Row/Col/Dep определяют  очередность  вы-

полнения вычислений:  R - по строкам,  С - по колонкам,  D - в

той последовательности,  которая позволяет разрешить ссылки на

следующие клетки и, насколько это возможно, зацикленные клетки.

     /GI управляет  числом итераций и дает в строках подсказки

и ввода появляются следующие сообщения:

     Enter  S(olver) or <1-99> for fixed iteration cout

              27>/Global,Iteration Control,

где 1-99 - позволяет задать фиксированное число итераций, т.е.

     n раз;

     S - определяет число итераций, необходимое для разрешения

зацикленных ссылок;

     D - задает дельта-отклонение с помощью ссылки на имя клет-

ки,  содержащей соответствующую величину (вычисления повторяются

до тех пор, пока абсолютные изменения в последовательных итера-

циях не станут меньше значения указанной клетки).

  3.2. Форматирование изображения табличных данных на экране.

  -----------------------------------------------------------

     /F(ORMAT) - данная команда определяет форму выводимой на

     ---------   экран  информации и дает в строках подсказки

                 и ввода сообщения:

     Enter Level: G(lobal),C(olumns),R(ow),E(ntry) or D(efine)?

           9>/Format,

     Укажите уровень (тип),  на  котором  будет  формироваться

электронная таблица:

     - G - на всю таблицу;

     - С - к одному или нескольким столбцам;

     - R - к одной или нескольким строкам;

     - Е - к одной или группе клеток;

     - D - формат задается пользователем для просмотра и  кор-

ректировки таблицы.

     При задании диапазона вводится начальное значение, символ

-ограничитель (. или :)и конечное значение.

     Типы форматов:

     I(nteger) - целое численное, только для клеток с формула-

ми или числами;

     G(eneral) - общее, только для клеток с формулами или чис-

лами, выводит в наилучшем для ширины клетки формате (по  умол-

чанию);

     E(xponential) - экспоненциальное,  только  для  клеток  с

формулами и числами, выводит в формате Е;

     $ - в денежном выражении, только для клеток с формулами и

числами, выводит  в формате с фиксированной точкой и двумя де-

сятичными знаками;

     R(ight) - выравнивание по правому краю, только для клеток

с формулами и числами (по умолчанию);

     L(eft) - выравнивание по левому краю, только для клеток с

формулами и числами;

     TR -  выравнивание  по правому краю,  только для клеток с

текстом;

     TL -  выравнивание  по  левому краю,  только для клеток с

текстом (по умолчанию);

     TC - центровка, только для клеток с текстом;

     * - вывод гистограммы или столбиковой  диаграммы,  только

для клеток с формулами и числами (звездочками);

     U(ser definded) (1-8) - вызывает один из  восьми форматов

из таблицы форматов, заданной пользователем;

     H(idden) - не выводит на экран указанный диапазон клеток,

столбцов или строк (защита);

     D(efault) - переход к форматам по умолчанию;

     column width - определяет ширину одного или группы столб-

цов (0 - 127), но для клеткуи длна содержимого ограничена:

     текст - 115 позиций,

     формула - 116 позиций,

     число - 16 позиций.

              ЗАНЯТИЕ 4. Операции с таблицами.

              ================================

              4.1. Очистка и копирование.

              ---------------------------

     /BLANК - данная команда позволяет очистить одну или

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

              сиволами и дает в строках подсказки и ввода сообщение:

     Enter randge, or * graph-range

          8>/Blank,

     и затем можно очистить текущую клетку  (Enter), несколько

(введя их диапазон) либо снять вывод диаграммы (по звездочке и

ее номеру).

     С защищенных клеток необходимо предварительно снять защи-

ту, в противном случае появится сообщение "Protect Entry".

     /R(EPLICATE) - данная комада дублирует содежимое одной или

     ------------   нескольких клеток, запрашивая откуда (From)

                    и куда (To) и используя параметры:

     - Enter - автоматическая коррекция формул;

     - N - отменяет коррекцию формул;

     - А  -  выводит  подсказку  на коррекцию каждой формулы в

каждой клетке;

     - V  -  дублирует содержимое исходной клетки как числовую

константу;

     - + - суммирует значения в исходной и дублированной клет-

ках;

     - "-" - вычитает содержимоеисходной клетки из содержимого

дублированной клетки;

     - *  -  умножает содержимое исходной клетки на содержимое

дублированной;

     - / - делит содержимое исходной или дублированной клетки.

     /C(OPY) - данная команда осуществляет копирование клеток,

     -------   выводит аналогичные сообщения и режимы.

             Однако после ввода диапазона From выводится подсказка

             для указания клетки-копии:

     To? (Enter Cell), then <Return> or <,> for Options

     Возможность копировния блоков является  основным  преиму-

ществом данной команды.

             4.2. Вставка, уничтожение и перенос данных.

             -------------------------------------------

      /INSERT - данная команда позволяет вводить строки или

      -------   столбцы в электронную таблицу.

     / DELETE - позволяет выводить их из таблицы, а также удалять

     --------   файлы с диска.

     / MOVE   - с помощью данной команды вы можете перемещать

     --------   строки и столбцы электронной  таблицы  из одного

                места в другое.

                 4.3. Вывод на диск и распечатка.

                 --------------------------------

     /O(UTPUT) - данная команда осуществляет вывод различных

    ----------   из ваших электронных таблиц и дает сообщение

            /D(isplay) or C(ontents) report?

      - D - выводит электронную таблицу в том виде, в каком она

представлена на экране, включая все форматы и формальные

назначения;

      - C - выводит список значений клеток.

      После выбора режима в ответ на новую подсказку вы должны

указать клетку, диапазон клеток или параметр ALL, если необходимо

вывести всю электронную таблицу.

      Затем возникает новый запрос

         P(rinter), S(etup), C(onsole) or D(isk)?

- P - определяет вывод на принтер;

- S - позволяет задавать параметры печати;

- C - дает возможность контролировать выходные данные на экране;

- D - создает выходной файл с расширением имени .PRN (по умолчанию).


Табличный процессор Excel


Алгоритм и исполнитель

Понятие алгоритма-одно из основных в математике и  информатике.

 Под  алгоритмом  понимают  точное  предписание (инструкцию) для

выполнения в определенной  последовательности некоторой   системы

операций   для  решения   всех  задач  данного  типа.    Это

определение не является  строгим математическим,это скорее  одно

из толкований термина "алгоритм",поясняющее его смысл.

  В   математике   тот   или    иной   класс   задач   считается

 решенным,если для  его решения  установлен   алгоритм.В алгебре

,например,установлены  алгоритмы   для   определения  количества

корней  алгебраического уравнения и вычисления    их    значений

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

 Термин "алгоритм" произошел от имени средневекового  узбекского

математика Аль-Хорезми ,который еще в 9-м веке предложил правила

выполнения  4-х  арифметических  действий  в  десятичной системе

счисления.

    Алгоритмизация   не      является     только    прерогативой

математики.В  живой  природе  всякой  массовой  целенаправленной

деятельности  сопутствует  заранее  созданный     алгоритм - по

определенному  алгоритму  осуществляется  массовое  изготовление

обуви  и  пошив одежды,сборка автомобилей на  конвейере, выпечка

хлеба  и плавка металла; таким образом алгоритм можно трактовать

как  технологическую   инструкцию  из   отдельных   предписаний,

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

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

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

жизненного цикла  белковой клетки  от рождения  до смерти.   Все

живые  организмы  осуществляют  свой  жизненный  цикл  по такому

созданному  природой  алгоритму   с   некоторыми   отклонениями,

вызванными влиянием окружающей среды.  По существу  алгоритм-это

один  из видов информации,являющейся внутренней для действующего

объекта  и  используемой для  обработки  внешней, принимаемой по

каналам  связи   информации. Мы  будем  заниматься   алгоритмами

искусственного    типа, создаваемыми           с       участием

человека, преимущественно в области  математики, не забывая  при

этом, что это - капля в безбрежном море алгоритмов.

 Основные свойства, которыми должен обладать любой алгоритм:

  1.Массовость - алгоритм  должен  функционировать  с различными

исходными  данными, то есть  решать не  индивидуальную задачу, а

серию однотипных задач. Другими словами алгоритм должен

рассматриваться как метод решения определенного класса задач,

пригодный для использования в некотором диапазоне изменения

исходных данных.

   2.Детерминированность - алгоритм должен  содержать   конечное

число  предписаний, не  допускающих  произвола  исполнителя, не

оставляющих исполнителю  свободы выбора. Многократное повторение

алгоритма с одинаковыми исходными  данными  должно  приводить  к

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

 Создание алгоритма для решения задач определенного типа связано

обычно с тонкими и  сложными рассуждениями , требующими  высокой

квалификации. Но с  того момента ,  когда алгоритм уже  создан ,

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

чтобы его  мог выполнить  исполнитель ,не  имеющий ни  малейшего

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

умел выполнять  те элементарные  операции ,  на которые разложен

процесс решения задачи. С другой стороны , разработчик алгоритма

должен  учитывать   тот  арсенал   операций  ,которым    владеет

предполагаемый  исполнитель  -  иначе  некому  будет осуществить

исполнение.      Исполнитель    алгоритма-его       неотъемлемая

часть;квалификация исполнителя  должна учитываться  составителем

при   выборе инструкций-все  они должны   выбираться   из списка

возможностей исполнителя,иначе  на стадии  выполнения произойдет

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

алгоритмом.Алгоритмы   раскроя  металла   или  тканей,сборки или

сварки, вычисления  корней  уравнений     или  поиска экстремума

функции, начисления     зарплаты      или   расчета   затрат  на

строительство   всегда     ориентированы     на     определенную

квалификацию исполнителей. Неразрывное единство   алгоритма    и

его исполнителя  выпукло отражено  в названии  одной   из   книг

Н.Винера "Акционерное  общество Творец  и Робот"-оба  компонента

нерасторжимы ,не  в состоянии дать продукт друг без друга.

 Одно из возможных определений исполнителя:

Исполнитель-это  человек    или   коллектив    людей,вооруженных

набором   инструментов   и   обученный   выполнению    некоторой

совокупности  операций   в  заданной   последовательности,   или

автоматическое  устройство  (электронное,  электромеханическое и

т.п.),  изготовленное  таким  образом,что,будучи   включенным  в

работу,выполняет   заданную   последовательность   операций  над

некоторым исходным продуктом,преобразуя его в заданный  конечный

продукт.    Другими   словами       исполнитель   -это  автомат,

запрограммированный   на выполнение  заданной последовательности

операций,действий.Запуск  исполнителя  на  выполнение  алгоритма

называют     вызовом  алгоритма.   Еще  одно  важное    свойство

алгоритмов     и      исполнителей-это     их      иерархическая

структура.Алгоритм  решения  сложной  задачи  разбивается на ряд

более простых--исполнитель  для решения  систем уравнений  может

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

вычисления  определенных  интегралов,эти  исполнители  в    свою

очередь могут  вызывать   исполнителей для  сортировки данных  и

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

для арифметических действий и логических   операций,составляющих

основу  для всех процедур высокого уровня.

Всякое  действие  осуществляется  исполнителем  над   некоторыми

объектами    и    состоит    в    изменении    состояний    этих

объектов. Различают глобальные объекты и  параметры. Глобальные

объекты      исполнителя   хранят  информацию   о    предыстории

работы-их   состояния   могут меняться  в результате  выполнения

одних процедур и влиять на выполнение других; глобальные объекты

относятся   ко   всем   предписаниям  алгоритма   и  исполнителя

данного уровня иерархии.

 Параметры делят на входные,выходные и промежуточные.

 Результат выполнения алгоритма полностью определяется:

 -состоянием глобальных объектов;

 -состояним входных и промежуточных параметров.

    В  системе  предписаний  должна  быть предусмотрена ситуация

невыполнимости алгоритма (отказа)  при  определенных  состояниях

глобальных объектов или параметров.  Тесная связь  алгоритма   и

автомата  (исполнителя)  вызывает  необходимость  учета  свойств

исполнителя     при     попытке     дать  строгое математическое

определение  алгоритма.   Такое  точное  определение алгоритма и

связанного с  ним понятия  исполнителя было  выработано   лишь в

30-х   годах   20-го  века,когда   было  осознано   сущетвование

алгоритмически     неразрешимых     проблем  в  процессе попыток

математиков  построить   все  более   и  более  универсальные  и

мощные  алгоритмы,решающие  все  более  широкие классы задач все

более общего типа.

 Из   школьного   курса   известен,например,алгоритм  извлечения

квадратного корня.Можно   поставить более  общую  цель:построить

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

числа-построить  такой  алгоритм  сложнее,но   результат   будет

весомее.Можно  поставить  и  еще  более  общую  задачу-построить

алгоритм для определения  всех корней уравнений вида:

     An*x^n  + An-1 * x^n-1   +...+ A * x + A  = 0

 Построением такого алгоритма  занимается алгебра многочленов  -

один из разделов высшей алгебры.Но эта постановка задачи в  свою

очередь  не  предельно  общая  -  можно и дальше расширять класс

решаемых алгоритмом алгебраических задач.

 Постепенно со времен Лейбница проблема создания все более общих

алгоритмов выкристаллизовалась    в     одну    из     важнейших

проблем   математической   логики-проблему         распознавания

выводимости, которая формулируется следующим образом:  Для любых

2-х   формул   R   и   S   в  логическом  исчислении  определить

существование дедуктивной  цепочки,ведущей от  R к  S.   Решение

понимается   в   виде   алгоритма,дающего   ответ   на любой  из

вопросов такого типа.Пока такой  алгоритм не построен,как и  ряд

алгоритмов для    задач    более    частного     типа,например,о

наличии  целочисленных  решений  диофантовых   уравнений    вида

Р=0,где  Р  -  многочлен  с  целочисленными  коэффициентами(10-я

проблема  Гилберта).   Так   возникла  задача    алгоритмической

разрешимости,решение которой  без точного   определения  понятия

алгоритма  лишено  смысла-надо   точно  знать  возможность   или

невозможность построения чего мы доказываем.

  Алгоритмически неразрешимые  проблемы  известны давно - это  и

задача о трисекции угла с помощью циркуля и линейки  или решения

уравнений выше 4-го   порядка  в радикалах.  Неразрешимость этих

задач   доказана   только   благодаря   четкому    указанию   на

возможности  используемого  исполнителя  -  циркуль  с линейкой,

решение    в  радикалах.   При  этом   вовсе  не    утверждается

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

    Здесь  мы  приходим  к  выводу  что  для выработки  строгого

определения      алгоритма      необходимо       сконструировать

универсальный  автомат,способный  релизовать  любые  алгоритмы -

тогда    любая   задача   могла   бы   считаться  алгоритмически

разрешимой,  если   это  решение  выполнимо  за  конечное  число

процедур    универсального   исполнителя,а   понятие   алгоритма

определено как конечная поледовательность инструкций из арсенала

универсального исполнителя для решения задач определенного типа.

 Построение   универсальных   исполнителей      изучается      в

математической логике,раздел "Теория  алгоритмов". Общая   схема

решения этой задачи имеет следующий вид:

-доказывается теорема о том ,что любой алгоритм может быть

сведен к вычислению значения некоторой функции;

-подбираются классы    функций,алгоритмов    или    исполнителей

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

вычислимая  функция  вычислима   с   использованием   выбранного

класса  функций,алгоритмов  или  автоматов-исполнителей Так были

предложены   варианты   универсальных исполнителей, базирующихся

на общерекурсивных функциях Черча,нормальных алгоритмах  Маркова

и машинах Тьюринга.

 С  точки    зрения   изучаемой    нами   машинной    математики

наибольший интерес представляет  раскрытие понятия алгоритма  на

азе  автомата  -  вычислительной  машины.   Для этого  механизм

работы   машины   необходимо   представить  в  виде  стандартной

схемы,максимально  простой  по  своей  логической  структуре   и

предельно    точной,чтобы    она    могла    служить    объектом

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

английским маттематиком Тьюрингом в 1937 году.


Машина Тьюринга
 Машины Тьюринга(МТ).

МТ  имеет    потенциально   бесконечную    память,-хотя   объем

хранящейся  в  ней  информации  конечен,для  него не  существует

верхней  границы.  МТ  содержит: память  в   виде   бесконечной

ленты, алфавит, головку для чтения- записи,внутренние состояния

и программу.

    Структурная схема МТ:

   Бесконечная лента-внешняя память

----------------------------------------------------

  ¦ ¦ ¦ ¦1¦1¦1¦1¦ ¦ ¦ ¦ ¦ ¦ | | |  | | | | |

----------------------------------------------------

           ^ -головка для чтения-записи

           ¦ ¦       q1(с задержкой на такт)

           ¦ ¦  +----------+

           ¦ ¦  ¦          ^

        +---------+   +----------+    +---------+

        ¦         ¦   ¦          ¦    ¦         ¦

        ¦ лог блок¦   ¦          ¦    ¦         ¦

        ¦         ¦   ¦          ¦    ¦         ¦

        ¦    L    ¦   ¦     Q    ¦    ¦   P     ¦

        ¦         ¦   ¦          ¦    ¦         ¦

        +---------+   +----------+    +----^----+

         ¦   ¦           ^                 ¦

         ¦   ¦    q2     ¦                 ¦

         ¦   +-----------+                 ¦

         +---------------------------------+

                   сдвиг

    Q- состояние;P-сдвиг.

    Лента МТ разбита на ячейки.

    Алфавит МТ содержит конечное число символов

    А={a0 ,a1 ,...,ak }

    В каждой ячейке ленты только 1 буква алфвита А. a0  означает

пробел  -пустую  ячейку.  Число  ячеек,заполненных  отличными от

пробела буквами- конечно. В ячейки могут записываться  различные

буквы ,содержимое каждой ячейки может меняться в процессе работы

МТ; запись   поверх записанной буквы  приводит к потере  старого

содержимого.

 Головка МТ предназначена для записи  и чтения букв  в  ячейках,

может передвигаться вдоль  ленты  на  1 ячейку вправо или  влево

за 1 такт работы и и видит в каждый момент только 1 ячейку.

Внутренние состояния образуют конечное множество

               Q ={q0 ,q1 ,...,qм }

и могут меняться после каждого такта работы.

 Состояние   q0    -пассивное,   заключительное(конец   работы),

q1-начальное  состояние,остальные  -  активные.   Порядок работы

МТ,имеющей   алфавит    А, множество   состояний Q  определяется

программой, состоящей из m (число состояний) строк и k+1  (объем

алфавита)  столбцов  и  имеющей  вид таблицы, в  клетках которой

записаны команды. Команда МТ записывается 3-хбуквенным словом Аq

t  Ql  По  этой  команде  МТ  должна выполнить следующую работу:

-символ Ai  в  i-й ячейке заменить на  Aq осуществить сдвиг t  (

Л,П,Н)  по  ленте  -перейти  в  состояние Ql Работа МТ протекает

следующим образом:  -перед запуском на ленту наносится начальная

информация   и  головка  МТ  устанавливается     на    начальную

ячейку,фиксируется      начальное      состояние      иначальный

сдвиг.Дальнейший          процесс  протекает   автоматически   в

соответствии   с    командами   программы,пока    не    наступит

нуль-состояние.   Прогамму МТ  ,записанную в  виде прямоугольной

таблицы,называют еще ее функциональной схемой.

    Пример:

                     q1     q2    q3    q4     q5

                  ^ ^Пq4  ^Лq3  ^Пq1  ^Лq5   ^Нq5

                  | aНq2  ^bНq1 |Пq1  |Лq1   |Нq5

                  a aЛq1  aПq2  |Лq3  ^Пq4   aНq5

                  b bЛq1  bПq2  ^Лq3  |Пq4   bНq5

    1-й такт:в   поле   зрения   головки-палочка  из  4-й  слева

ячейки (начальной) в состоянии q1.На пересечении-содержимое  3-х

каналов выхода    логического   блока-  aНq2. |  заменяется   на

a,следующее состояние q2,сдвиг - Н (отсутствует)

    2-й такт:в поле зрения-знак a из той же ячейки(сдвига не

было),новое состояние q2,на пересечении-выходная тройка aПq2.

    3-й такт:сдвиг  вправо  обеспечил  попадание  в  поле

зрения | при состоянии q2.Результат-замена |  на  b и переход к

q2 и т.д.

МТ предтавляет  собой  математическое  понятие_.,она  никогда не

была воплощена в  металле,это   по   сути   тот   универсальный

исполнитель,который теоретически может все,что необходимо для

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

основной гипотезы теории алгоритмов:

    -всякий алгоритм может быть задан посредством тьюринговой

функциональной схемы и реализован в соответствующей МТ.

    Это утверждение не может быть   сформулировано  и   доказано

в  виде  теоремы,так  как  оно  содержит  в  себе  слова "всякий

алгоритм",то   есть   ссылку   на   общее,нестрогое  определение

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

уточняет   расплывччатое   общее  понятие  через   математически

строгое определение МТ:

МТ называется  система вида  (A,a ,Q,q  ,q ,T,t),  где А-кнечное

множество  -  алфавит  МТ,  а0-пустая  буква алфавита,Q-конечное

множество    состояний   МТ   ,q1-начальное   состояние,   q0  -

заключительное состояние, Т={Л,П,Н} - множество сдвигов МТ, t  -

программа МТ.

    Если  предположить,что   основная   гипотеза   верна,то    в

 теории алгоритмов  можно   исследовать   вместо   расплывчатого

определения   алгоритма    Тьюринговы   функциональные    схемы.

Правомерными  становятся        постановки       задач         о

существовании разрешающих или    вычислительных    алгоритмов,то

есть   об алгоритмической разрешимости  задач-это  будут  задачи

о существовании или нет МТ,обладающей необходимыми свойствами.

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

-все известные    алгоритмы    ,придуманные     в течение многих

столетий,могут   быть      заданы     посредством    Тьюринговых

функциональных схем;

  -класс алгоритмов, которые   в  принципе   можно   переложитть

на язык Тьюринговых программ, замкнут относительно всех  приемов

образования новых  алгоритмов   из   уже   имеющихся алгоритмов;

иными словами  ,если исходные  алгоритмы   могут   быть   заданы

посредством  Тьюринговых  программ,  то  это   возможно   и  для

результирующих, более сложных алгоритмов.

-алгоритмы  различных      классов,  полученные       различными

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

класса в другой -  Тьюринговы, марковские, неймановы.   Основные

результаты ,полученные в теории алгоритмов с помощью МТ:

-доказательство существования алгоритмически неразрешимых

проблем;

-доказательство  существования  среди  алгоритмически разрешимых

проблем сколь угодно сложных, то есть практически неразрешимых;

-определение  конкретных  классов  алгоритмичес4ки  неразрешимых

пролблем:  проблема  распознавания  применимости, переводимости,

проблема    эквивалентности    слов      для       ассоциативных

исчислений,проблема тождества групп и др.


Автоматы Неймана

Особенностью МТ является обработка информации только в одной

ячейке  -  остальные  дожидаются     посещения    головки. В тех

случаях,когда результат  обработки   текущей   ячейки  необходим

для  определения  способа  обработки     следующей,такая   схема

оправдана.Но наверняка существует множество   ячеек,в    которых

информация    может быть  обработана независимо   от  результата

обработки текущей ячейки и задержка оказывается просто  дефектом

конструкции  машины  -  необходимо  распараллеливание   процесса

вычислений.

 Простейшая   идея,которая   напрашивается   -это  использование

нескольких  МТ  с  общей  для  них лентой.Можно представить себе

систему  МТ,  головки  каждой  из  которых  перемещаются   вдоль

ленты,а  неизбежные  коллизии при попытках  обращения к одним  и

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

распараллеливание  здесь невозможно -  при N  машинах в  системе

все ячейки за исключением N находятся в вынужденном бездействии.

 Автомат  Неймана  -  это  машина  иного  типа,в которой принцип

распараллеливания   доведен    до    конца.Число    одновременно

обрабатываемых  ячеек    в   автомате  Неймана    может    расти

неограниченно, оставаясь  конечным в  каждый момент  времени.  В

грубой интерпретации  это автомат,который  позволяет вовлекать в

работу  все  новые  и  новые  экземпляры  МТ  для    организации

совместной обработки общей для них информации.

 Элемент   Неймана -это  устройство, которое  на каждом  такте t

пребывает в 1-м из конечного  числа состояний r из множества  R.

У элемента 2 входных канала, по которым могут вводиться по  1-му

состоянию из того же  множества.Состояние элемента на такте  t+1

зависит от состояния в предыдущем  такте t и поступивших в  этом

такте состояний по входным каналам

                  +--------+

                  ¦        ¦

             Rj   ¦  Ri    ¦<-----Rk

             ---->¦        ¦

                  +--------+

    Нейманова команда имеет вид:

                     Ri Rj Rk--->Rm

    Читается  она   так:если  элемент   находится  в   состоянии

Ri,воспринимает  по  левому   каналу  состояние  Rj,по   правому

каналу-Rk, то на  следующем такте он  переходит в состояние  Rm.

Автомат Неймана строится  как бесконечная в  обе  стороны   цепь

эле ментов, соединенных между собой.Элемент,находящийся  в такте

t в состоянии  a(t) воспринимает   по  каналам   состояние своих

соседей справа и слева и может выработать свое состояние  a(t+1)

в следующий моиент согласно его программе.Но это справедливо для

всех  элементов  цепи,поэтому  за   1   такт    приодновременном

действии    всех    элементов    осуществляется     повсеместное

одновременное преобразование  информации.   Отрезок неймановской

цепи,вне   которого    все   элементы   находятся  в   состоянии

покоя,называют  активной   зоной   автомата.На    каждом   такте

активная зона  может  расширяться  не более чем по 1-му элементу

на каждом краю.Состояние покоя характеризуется функцией

                      000---> 0, или rrr--->r

т е   находится  в  состоянии  r,получает  по  кангалам

одинаковое состояние r и вырабатывает то же состояние на

следующий такт.

    В   отличие    от    МТ,ячейки    которой   только    хранят

информацию,элементы  автомата  Неймана   совмещают  хранение   с

обработкой.   Автоматы Неймана  и МТ  - взимно-моделируемы,  т е

можно построить нейманову модель МТ и тьюрингову модель АН.

В  конструкции       автоматов     Неймана      заложен      ряд

рациональных идей,обеспечивших их воплощение в металле.Одна   из

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


Словесные описания алгоритмов

Разработанный  алгоритм  (метод  решения  определенного   класса

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

алгоритмами ,  для последующего  перевода в  другие формы записи

-другими словами его существование  начнется с того момента  как

он будет записан.

Наиболее простой  формой записи алгоритмов является естественный

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

естественном  языке  -  даже  в  тех  случаях  , когда физически

алгоритм сразу записывается глубоко формализованными способами ,

его словесная формулировка присутствует в сознании  разработчика

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

файл .

В     качестве     примера     рассмотрим     запись   алгоритма

Евклида  для   нахождения  наибольшего   общего  делителя    2-х

натуральных чисел . Алгоритм расчитан на исполнение человеком  ,

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

   1.Получите значения 2-х чисел А и Б. Переходите к следующей

операции.

   2.Сравните значения чисел А и Б. Переходите к следующей

операции .

  3.Если числа равны то любое из них есть искомый наибольший

   общий делитель и процесс вычислений остановить.

  Иначе перейти к следующей операции.

  4.Если  А меньше Б ,поменяйте местами их значения и переходите

   к следующей операции.

  5.Вычитайте Б из А, А сделайте раным Б , а Б -равным остатку

     и переходите к операции 2.

В качестве элементарных операций , на который расчленен процесс

решения, у нас фигурируют :

 -получение исходных данных для решения задачи (значений чисел А

 и Б);

 -сравнение значений 2-х чисел;

 -перестановка значений чисел местами;

 -вычитание меньшего числа из большего;

 Совершенно очевидно ,что это расчленение может быть  продвинуто

значительно дальше - например , вычитание может быть  развернуто

в  систему  операций  ,  выполняющих  вычитание 2-х многозначных

чисел ,  а операция  сравнения -  в последовательность получения

ответов на  вопросы типа  :   число А  равно числу  Б?   число А

меньше числа Б?

 Такое расчленение  не нужно  , если  исполнитель -  человек. Но

если это  - автомат  , то  операция скажем  замены значений  2-х

чисел уже требует деталировки. Пусть числа А и Б записаны каждое

в  свою  ячейку  памяти   и  необходимо  поменять  их   значения

местами.Простая перепись  значения Б  в ячейку  , где  лежит А ,

приведет к потере значения А - в обеих ячейках будет значение  Б

и  дальнейшее  исполнение  алгоритма  станет  невозможным   .Нам

придется  завести  дополнительную  ячейку  С  и переписать в нее

значение А для временного хранения , затем переписать значение Б

в ячейку для  А и потом  из ячейки для  С переписать сохраненное

там значение  А в  ячейку для  Б. Это  похоже на задачу поменять

содержимое двух стаканов , один  из которых с водой ,а  другой с

молоком - без  третьей емкости эту  задачу не решить.   Еще одна

важная деталь ,на  которую вам следует  обратить внимание -  это

первая операция  , выполняемая  один раз  ,- получение  значений

чисел до начала решения. Алгоритм создается для решения задачи в

общем  виде-  конкретные  данные  он  получает  только на стадии

выполнения - именно так обеспечивается его важнейшее свойство  -

массовость.  Поэтому все компьютерные алгоритмы должны содержать

процедуры получения данных для своей работы .

В качестве второго примера рассмотрим вычисление числа Фибоначчи

с заданным  номером; напомним  что члены  ряда чисел  Фибоначчи

,начиная с третьего, равны сумме 2-х предыдущих  в

последовательности.

1.  Получить  у  пользователя  (заказчика)  номер искомого числа

Фибоначчи и запомним его в переменной по имени N.

2.  Присвоим  3-м  переменным   ,например  с  именами   F1 F2  и

F3, значения соответственно  1,  1, и  2.Переменной   n присвоим

значение текущего номера, вначале - 3.Перейти к операции 3.

3.Сравнить  n и N. Перейти к операции  4.

4.Если n  меньше N , F1 присвоить значение   F2, F2 -значение

F3  , а F3  -  сумму значений F1   и F2.  n увеличить  на 1.

Перейти к  операции 3. Иначе искомое число Фибоначчи равно F3.

Вычисления прекратить.

В этом примере алгоритм получает извне на стадии выполнения одно

число - номер искомого числа Фибоначчи.  Алгоритмы , в   которых

основную  роль   играют арифметические   действия  ,    называют

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

словесных  предписаний или  разного  рода  формул  и  схем.  Эти

алгоритмы получили широкое распространение  потому ,  что к  4-м

арифметическим действиям  можно  свести  другие  более   сложные

операции интегрирования, дифференцирования функций ,знакомый вам

алгоритм вычисления корня квадратного и т д.


Управляющие структуры

Уже  в  приведенных  простеньких   примерах  видно  ,что   кроме

сохранения исходных данных и результатов вычислений и  обработки

данных  для  алгоритма  характерно  еще  наличие  выбора , какую

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

,связанные с выбором очередной операции в зависимости от тех или

иных условий , называют

           УПРАВЛЯЮЩИМИ СТРУКТУРАМИ АЛГОРИТМОВ

 В 1977 году математики Бем  и Якопини доказали , что  алгоритмы

 сколь  угодно  сложной  структуры  могут  быть  реализованы   с

использованием всего 3-х управляющих структур :

 -Последовательное выполнение операций;

-Ветвление алгоритма на группы операций в зависимости от

выполнения некоторых условий;

 -Циклическое многократное выполнение группы операций до

 выполнения некоторого условия ,формируемого в процессе

 выислений.

 Все 3 указанных  фактора присущи приведенным  примерам. Заранее

неизвестно,  сколько  раз   придется  выполнить   переприсвоение

значений и сложение в алгоритме вычисления числа Фибоначчи - это

определяется  его  номером.   Точно  так  же неизвестно ,сколько

вычитаний и переприсвоений придется осуществить для  определения

НОД двух чисел - это  зависит от значений самих чисел  , которые

при  создании  алгоритма  не  определены  и  задаются  на стадии

выполнения.

 Краткое название управляющих структур -СЛЕДОВАНИЕ , ВЕТВЛЕНИЕ ,

ЦИКЛЫ,  а   соответствующие  им  операторы  в  записи алгоритмов

называются   УСЛОВНЫМИ   ОПЕРАТОРАМИ   И   ОПЕРАТОРАМИ    ЦИКЛОВ

(следование  не  имеет  специального  опреатора  и  выражается в

записи просто последовательной записью инструкций ).

 Условные операторы в наших  примерах звучат как ЕСЛИ   <УСЛОВИЕ

 ВЫПОЛНЕНО>    последовательность    операций    ИНАЧЕ    другая

 последовательность операций.

 Операторы циклов  у нас  реализованы через  условные операторы,

 возвращающие  процесс  на  уже  пройденные  операции  пока   не

 выполнится условие выхода  из такого циклического  повторения.В

 алгоритме НОД это равенство чисел  , а в алгоритме Фибоначчи  -

 когда номер текущего числа достигнет заданного.


Блок-схемы алгоритмов

Иногда используют графическо-текстовую форму записи алгоритмов.

 От  чисто  текстовой  на  естественном  языке  она   отличается

 следующим:

 -последовательно  выполняемые  операции  записываются текстом в

 обрамленных рамками прямоугольниках;

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

 -прямоугольники  и  ромбы  соединяются  линиями  со  стрелкми ,

 указывающими направления перехода от одной операции к другой.

 Алгоритм  вычисления  НОД  в  виде блок-схемы выглядит примерно

 так:

                       +-------+

                       ¦начало ¦

                       +-------+

                +-----------------------------+

                ¦Получить значения чисел А и Б¦

                +-----------------------------+

                 +-----------¦

                 ¦        /    \  да

                 ¦      / А = Б? \-------+

                 ¦       \      /  +----------+

                 ¦         \  /    ¦ НОД = А  +----------+

                 ¦     нет  ¦      +----------+          ¦

                 ¦         /|\                           ¦

                 ¦       /     \   да    +--------------+¦

                 ¦     /  А < Б? \-------¦ С=А; А=Б; Б=С¦¦

                 ¦      \       /        +--------------+¦

                 ¦     нет \¦/   +--------------+        ¦

                 ¦    +----------------+                 ¦

                 +----¦С=А-Б; А=Б; Б=С;¦                 ¦

                      +----------------+                 ¦

                                                         ¦

                                 +-----------------------+

                           +----------+

                           ¦ конец    ¦

                           +----------+

 Блок-схемы  используют   для  наглядности   обычно   начинающие

алгоритмисты, при  создании алгоритмов  значительных объемов  их

рассмотрение  становится  неудобным  ,хотя  иногда  может   быть

полезным при выявлении "потерянных" ветвлений.

 Существует  разумеется  много   других  способов   графического

 начертания алгоритмов с другими  обозначениями ,но мы не  будем

 их здесь рассматривать.


Алгоритмические языки и трансляторы

На   заре  развития  компьютерных  технологий   и  компьютерной

 техники алгоритмы  на  естественном   языке    или   в    форме

 блок-схем  переводились непосредственно  в последовательность

 кодов    машинных      команд   ,которые наносились    методом

 перфорации  на  бумажные  ленты  и с помощью   фотосчитывателей

 переносились    в    память машины    для   выполнения    .

 Программирование   с помощью машинных кодов   очень трудоемкое

 занятие,   но   даже   в этих  неблагоприятных  условиях  были

 созданы   многие сложные   программы   управления  различными

 агрегатами, в том числе   в военной   области.    Возможность

 такого программирования  осталась  и  сейчас , но используется

 этот метод крайне   редко  в   тех  случаях   ,  когда   нужно

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

 текстов алгоритмов  ,а есть  в   наличии только   файл машинных

 кодов.     В  этих    случаях  используют  системный   отладчик

 debug.exe   ,позволяюший     выполнять   множество     операций

 непосредственно в  оперативной    памяти компьютера  ,   в  том

 числе - записывать туда  коды команд  или данные и осуществлять

 пошаговое выполнение команд с  просмотром регистров  процессора

 и  всех  сегментов  программы.

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

 мнемокодов   или мнемонических машинно-ориентированных   языков

 Ассемблера   для используемых  типов       процессоров        и

 специальных программ -  трансляторов  для   перевода    текстов

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

 Последующие   работы   в   области   уменьшения    трудоемкости

 составления текстов  программ (алгоритмов,  которые могут  быть

 выполнены на компьютере ) были направлены на создание  программ

 -  трансляторов, способных осуществлять перевод в машинные коды

 текстов  алгоритмов,  записанных   на  алгоритмических   языках

 высокого уровня, для  которых характерно максимально  возможное

 приближение  к  естественному  языку  -  в  силу  значительного

 опережения  США   в  этой   области,  таким   языком   оказался

 английский.   В настоящее  время существует  большое количество

 формализованных алгоритмических  языков как  общего назначения,

 так  и   проблемно  -   ориентированных  ,предназначенных   для

 отдельных  областей   применения.    Языки  общего   назначения

  наиболее универсальны, но могут реализовывать различные  стили

 (парадигмы) программирования.   Так, языки  SmallTalk и  Simula

 реализуют так называемый объектно - ориентированный стиль, Си++

 и  Паскаль  -  поддерживают  как  процедурный, так и объектно -

 ориентированный   стили,   язык   Лисп   -   стиль  логического

программирования и  т д.  На протяжении  ряда лет осуществлялось

сближение  трансляторов  с  различных  языков  по   ассортименту

предостаавляемых  программистам  услуг,  но  специфические   для

каждого   языука   синтаксические    и   структурные    различия

сохраняются.

Современному программирующему  пользователю ЭВМ  приходится быть

"полиглотом",  то  есть  знать  несколько  языков,  чтобы  иметь

возможность выбирать подходящий для конкретной задачи инструмент

разработки. Это не так уж сложно в рамках одного и того же стиля

программирования; переходы между  различными стилями могут  быть

более затрудненными, но тоже вполне осуществимы.

07.1.Типы трансляторов.

Основная   задача   транслятора   с   любого    формализованного

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

последовательность  2-ичных  числовых  кодов  команд процессора.

Существеет   2   основных   типа   транслирующих   программ    -

интерпретаторы и компиляторы.

Интерпретаторы  анализируют  каждую   отдельную  строку   текста

алгоритма  и   при  отсутствии   в  ней   синтаксических  ошибок

(несоответствий  принятым  в  языке  правилам составления текста

алгоритмов,  наличии  в  тексте  "незнакомых" транслятору слов и

пр.) переводят  записанные в  строке операторы  языка в машинные

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

интерпретатора,  не   могут  выполняться   в  отрыве   от  этого

интерпретатора под управлением только операционной системы  ,так

ка  результат  трансляции  в  машинные  коды  не сохраняется, он

теряется сразу после выполнения очередного оператора.

Компиляторы обрабатывают  и переводят  в машинные  числовые коды

весь текст алгоритма, сохраняя (при отсутствии синтаксических  и

орфографических  ошибок)   результат  компиляции   в  виде   так

называемого  объектного  файла.   Этот  файл  не  может еще быть

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

процедур  носят  условный  характер,  они  не  согласованы между

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

осуществить редактирование  внутрипрограммных адресных  ссылок в

соответствии  с  требованиями  операционной  системы  и для этой

работы в составе  ОС есть программа  - редактор адресных  ссылок

LINK.EXE  (она   поставляется  также   дополнительно  к   любому

компилятору).  Эта  программа  обрабатывает  объектный  файл   и

другие, например  библиотечные объектные  файлы и  компонует все

это в исполняемый файл с  расширением .ЕХЕ. Этот файл уже  может

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

операционной системы - компилятор ему уже не нужен.

Следует  понимать,  что  отсутствие   синтаксических  ошибок  не

означает отсутствие алгоритмических - транслятор не может знать,

что  мы   с  вами   хотели  запрограммировать,   он  не    может

анализировать  смысл  алгоритма,   а  занимается  только   чисто

формальной  разборкой  текста   и  проверкой  его   соответствия

принятым в языке правилам правописания. Требования к снтаксису и

орфографии    при    записи    алгоритмов    на    транслируемых

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

отличеие от человека, способного догадаться, что слово  "пиисят"

означает "пятьдесят" , современные трансляторы "догадываться" не

умеют  и  ошибка  даже   в  одном  символе  вызовет   недоумение

транслятора, сопровождаемое диагностическим  сообщением и

отказом от окончательного завершения его работы.

07.2.Классификация требований трансляторов к текстам алгоритмов.

07.2.1. Структуризация текста программ в алгоритмических языках.

Каждая транслирующая программа,чтобы не стать "необъятной",не

может допустить свободного изложения алгоритма и вынуждена

предъявить определенные требования к структуре его текста.

Из каких основных частей состоит текст алгоритма?

-Прежде всего траслятор должен  пропустить "беллетристику",

которая не должна переводиться в машинные коды и не относится к

самому транслятору - это так называемые комментарии, без которых

нельзя обойтись ни в одной программе. В них содержатся сведения

о классе задач, обслуживаемых алгоритмом, словесное описание

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

,сведения о разработчике и многое другое. Говорят, что программы

составляются для людей, которым предстоит их корректировать и

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

устранять недоработки, поэтому роль комментариев трудно

переоценить. Однако транслятору они не нужны, поэтому они как

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

начале и в конце и транслятор, встретив символ начала

комментария, пропускает все символы, пока не встретит символ

окончания комментария. К сожалению во всех языках символы -

ограничители комментариев разные, эта часть не унифицирована,

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

это просто свидетельство незрелости самой отрасли

программирования.

-далее транслятор должен выделить директивы управления

процессом трансляции - это фрагменты текста , не являющиеся

составными частями алгоритма решения задачи, а только

настраивающие сам транслятор на определенный режим работы или

выполнение определенных действий, например :печать заголовка

при выводе листинга программы, установка размеров страниц при

печати программного текста, включение перед трансляцией текстов

алгоритмов из других файлов , определение размера стека для

временного хранения данных, порядок помещения в стек фактических

параметров подпрограмм, определение допустимых размеров областей

памяти для глобальных данных и кодов команд (выбор модели

памяти), использовать при компиляции команды арифметического

сопроцессора или программный эмулятор операций с вещественными

числами, выполнять оптимизацию полученного кода по размеру или

по скорости выполнения и многое другое.  Допустимые директивы

записаны в памяти транслятора и нам остается использовать их

названия при составлении текста программы без малейших

отклонений.

 -транслятор должен  иметь возможность определить область текста

алгоритма, в которой записаны операции обработки данных; кроме

того, при вызове программы на выполнение в регистры процессора

необходимо поместить адрес первой команды, с которой начнется

выполнение алгоритма; поэтому все компиляторы требуют, чтобы

программа состояла из одной или нескольких подпрограмм, одна из

которых должна быть обозначена как главная, с первого оператора

которой начнется выполнение алгоритма в целом.

Все операторы обработки данных размещаются при этом внутри

подпрограмм, тела которых ограничиваются специальными символами

или словами. Так в ассемблере это слова ИМЯ и PROC в начале и

ИМЯ и ENDP или END в конце, в Си это открытая и закрытая

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

в круглых скобках, в Паскале это слова BEGIN и END после имени

подпрограммы с списком параметров в круглых скобках и разделов

объявлений типов, переменных и констант.  Главная подпрограмма

выделяется в ассемблере по закрывающей фразе ИМЯ_ПОДПРОГРАММЫ

END, в Си она имеет фиксированное имя main(), в Паскале она

выделяется точкой после слова END (все другие подпрограммы

заканчиваются словом END; с точкой с запятой т е символом

"семиколон").

07.2.2. Размещение данных в памяти.

Любая программа оперирует с некоторыми данными - исходными

(которые задает пользователь), результатами промежуточных

вычислений , конечными результатами - все эти данные должны быть

размещены в оперативной памяти компьютера. Так как компьютер -

техническое устройство, размер памяти, отводимой под каждый

элемент данных всегда ограничен определенным количеством байтов

(минимально адресуемых единиц памяти). Оперативная память -

быстро расходуемый и дефицитный  ресурс компьютера, каждый

элмент данных для возможности работы с ним должен иметь адрес,

поэтому каждый транслятор требует, чтобы все элементы данных

имели четко обозначенный тип - именно тип определяет количество

байтов, занимаемых в памяти тем или иным элементом данных.

Данные могут располагаться в одном из  3-х участков оперативной

памяти, распределяемых программе операционной системой: в

сегменте данных программы, в программном стеке или в динамически

распределяемой области ("куче"). Память в сегменте данных

распределяется компилятором, а в куче выделяется по запросу

программы на этапе ее выполнения. Для данных, размер которых при

различных запусках программы может изменяться пользователем и на

стадии составления алгоритма неизвестен, лучше всего запрашивать

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

Любой элемент данных во всех языках программирования может

иметь имя и обязательно должен иметь указанный тип (размер).

АССЕМБЛЕР  обеспечивает  два способа  определения данных:

во-первых,  через указание длины данных и,  во-вторых, по их

cодержимому. Основной формат определения данных в Ассемблере:

                         (тип)

                [имя]     Dn   выражение

 _     Имя  элемента   данных  не   обязательно (это указывается

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

на    некоторый    элемент,    то    это   делается  посредством

имени.

ЯЗЫК Си предписывает следующий синтаксис определения данных:

                Тип  имя [или список имен через запятую];

                int  name1,name2;

ПАСКАЛЬ требует вначале указывать имя [или список имен], затем

двоеточие и название типа

                 name1,name2: integer;

БЕЙСИК предусматривает включение обозначения типа прямо в имя

элемента данных (переменной) и в дальнейшей работе с этим

элементом значек обозначения типа неотделим от имени:

                 name1% = name2%

(% в Бейсике обозначает целочисленный тип)

Второй способ описания типов данных в Бейсике - с помощью

оператора DEF для однобуквенных имен переменных:

     DEFINT A 'А определена как целочисленная переменная

В заключение урока проиллюстрируем структурные ограничения,

накладываемые на текст алгоритма в формализованных

алгоритмических языках на примере алгоритма - "бездельника",

который ничего не делает, но внешне представляет собой

транслируемый текст.

       ;Это строка комментария в Ассемблере

   mov AX,0 ;Эти слова после точки с запятой - тоже комментарий

                   /*Это комментарий в языке Си */

             {Это комментарий на Паскале}

На Бейсике строка комментария начинается со слова REM или

одиночной кавычки

            REM        Это комментарий на Бейсике

            INPUT C   'Это тоже комментарий на Бейсике

Текст программы на ассемблере   состоит из 3-х сегментов -

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

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

сотоит  из заголовочной строки ,тела  сегмента  и заключительной

строки.  Заголовочная строка   любого сегмента_ состоит  из

имени  сегмента,директивы  SEGMENT и параметров:

      ;-------определение сегмента стека_-------------

    ; (имя)   (директива)    (п  а  р  а  м  е  т  р  ы)

    ;                     (выравнивание) (объединение)  (класс)

     STACKSG    SEGMENT         PARA        STACK       'Stack'

;_Тело сегмента размещается  между  заголовочной  и  заключительной

;  строками сегмента.

                  DW       32 DUP(?) ;Это тело сегмента

                  ...      ...

      ;_Заключительная строка   любого   сегмента_включает   его  имя  и

      ; директиву ENDS(конец сегмента):

      (имя)      (директива конца)

     STACKSG          ENDS

          ;-------определение сегмента данных_------------

          DATASG  SEGMENT  PARA 'Data'

          NAME1   DB       'ABCDEFGHI' ; Это тело

          NAME2   DB       'JKLMNOPQR' ; сегмента

          NAME3   DB       'STUVWXYZ*' ; данных

          DATASG  ENDS  ;конец сегмента данных

          ;--------определение сегмента кода---------------

          CODESG  SEGMENT PARA 'Code'

            ;Сегмент кода состоит из одной или нескольких

            ;процедур с произвольными именами

          MAIN   PROC    FAR    ;Главная процедура

          ASSUME  CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG

          ;привязка сегментных регистров к физическим сегментам

         ;--------раздел инициализации---------

                  PUSH    DS        ;сегмент данных в стек

                  SUB     AX,AX     ;Обнулим АХ

                  PUSH    AX        ;Нуль в стек

                  MOV     AX,DATASG ;В DS заносим

                  MOV     DS,AX     ;адрес сегмента данных

         ;------- вызовы_вспомогательных процедур---

                  CALL    PROC1      ;Вызвать 1-ю подпрограмму

                  CALL    PROC2      ;Вызвать 2-ю подпрограмму

                  RET                ;Завершить программу

          MAIN   ENDP ; конец процедуры

          ;--------1-я подпограмма -----------------

          PROC1   PROC

                  ; ... Может что-то делать

                  ;     но пока не хочет

                  RET

          PROC1   ENDP ;конец процедуры

          ;--------2-я подпрограмма------------------

          PROC2 PROC

                  ;Тоже пока бездельница

                  RET

          C10MOVE ENDP        ;Конец подпрограммы

          CODESG  ENDS        ;Конец сегмента кода

          END  MAIN   ;Точка входа в программу

 На языке Си ничего не делающая программа выглядит проще, так

 как компилятор сам заботится о сегментах, генерируя

 соответствующие коды команд:

/* тип возвращаемого главной функцией с фиксированным именем

  main значения в нашем случае "никакой" - void */

 void

 main( void) /* В круглых скобках на Си размещается список

 параметров с указанием типов,которые необходимы функции для

 работы */

 {

 /* Тело функций в Си ограничивается фигурными скобками  */

 proc1();  /*вызовы вспомогательных процедур */

 proc2();

 }

 void proc1(void) {}  /*Ничего не делающая подпрограмма  */

 void proc2(void) {}  /*Ничего не делающая подпрограмма  */

/*Главная функция main в Си определяет точку входа в программу,

а в остальном такая же, как и все остальные функции - может

иметь свои локальные данные в стеке и может вызываться другими

подпрограммами, а также может вызывать сама себя (рекурсивно).

Если она имеет параметры, то они представляют собой количество

слов в командной строке программы и список этих слов. Значения

этим параметрам присваиваются при загрузке программного модуля

на выполнение.*/

На Паскале главная процедура не имеет имени, параметров и

локальных данных.  Ее тело размещается между словами begin и

end.  (с точкой) Поэтому ничего не делающая Паскаль - программа

выглядит совсем просто:

procedure proc1 begin  end; {Ничего не делающая процедура}

procedure proc2 begin  end; {Ничего не делающая процедура}

            { главная программа}

            begin

            proc1;proc2;  {вызовы вспомогательных процедур}

            end.


Типы данных в языках ассемблера

Простые (скалярные) типы.

Мы уже говорили, что тип данных определяет размер отводимой под

элементы этого типа памяти, а следовательно и диапазон их

возможных значений. Кроме того тип определяет набор операций,

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

АССЕМБЛЕР  обеспечивает  два способа  определения

   данных:  во-первых,  через указание длины данных и,

   во-вторых, по их cодержимому. Рассмотрим основной формат

   определения данных:

                  [имя]     Dn   выражение

   _    Имя  элемента  данных  не  обязательно (это указывается

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

        ссылки   на   некоторый   элемент,   то   это  делается

        посредством имени.

   _    Для  определения  элементов  данных  имеются  следующие

        директивы:

        DB (байт),

        DW (слово или 2 байта),

        DD (двойное слово или 4 байта),

        DF (тройное слово или 6 байт)

        DQ (учетверенное слово или 8 байт)

        DT (десять байт).

   _    Выражение может содержать константу, например:

                       FLD1      DB   25

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

                       FLDB      DB   ?

      Выражение может содержать несколько констант, разделенных

   запятыми и ограниченными только длиной строки:

             FLD3      DB   11, 12, 13, 14, 15, 16, ...

   Ассемблер определяет эти константы в виде последовательности

   cмежных  байт.  Ссылка по  имени  FLD3  указывает  на первую

   константу,  11,  по FLD3+1  -  на  вторую,  12.  (FLD3 можно

   представить как FLD3+0). Например команда

                       MOV  AL,FLD3+3

   загружает  в регистр AL  значение  14  (шест. 0E). Выражение

   допускает также повторение константы в следующем формате:

             [имя]     Dn  число-повторений DUP (выражение) ...

      Следующие три примера иллюстрируют повторение:

             DW   10 DUP(?)      ;Десять неопределенных слов

             DB   5 DUP(14)      ;Пять байт, содержащих шест.14

             DB   3 DUP(4 DUP(8));Двенадцать восмерок

   В третьем примере сначала генерируется  четыре копии десятич

   ной 8  (8888),  и затем это значение  повторяется  три раза,

   давая в pезультате двенадцать восьмерок.

      Выражение может содержать символьную  строку или числовую

   константу.


Типы данных в языке Си и Си++
Типы данных в Си и Си++

К скалярным типам в Си относятся АРИФМЕТИЧЕСКИЕ типы, указатели,

перечисления и ссылки (в Си++) . Арифметические типы включают в

себя целые и вещественные типы.  Целочисленные типы в Си могут

быть знаковыми (signed) или беззнаковыми (unsigned). У знаковых

целых старший бит отводится под знак и поэтому диапазон их

абсолютных значений меньше, чем у беззнаковых. Все

арифметические  типы по умолчанию знаковые.  Перечислим их

характеристики:

        ТИП            РАЗМЕР В БАЙТАХ   ДИАПАЗОН ЗНАЧЕНИЙ

       char                 1              -128 ... 127

       unsigned char        1                0  ... 255

       int                  2             -32768...32767

       unsigned int         2                0  ...65535

       short             как int

       unsigned short    как unsigned

       long int             4          -2147483648...2147483647

       unsigned long        4             0...4294967295

       float                4             3.4E-38 ...3.4E38

       double               8             1.7E-308 ...1.7E308

       long double         10             3.4E-4932...3.4E4932

 При объявлении модификаций  типа int слово int можно опускать:

 unsigned u1,u2;

 long     l1,l2;

 Мы можем переименовать любой тип по своему вкусу, используя

 операцию typedef :

           typedef unsigned int UINT;

и далее использовать новое имя типа при описании переменных:

           UINT ui1,ui2;

Вещественные числа всегда знаковые.

Для их внутреннего ххранения в памяти существуют стандарты, в

частности по стандарту IEEE вещественные числа представляются в

виде 2-х частей - мантиссы М, порядка Р в 2-ичной системе

счисления:

            +-----------------+

 float      ¦  S¦    P  ¦   M ¦    S - знак числа, 0 это +

            +-----------------+                    1 это -

 биты         31  30..23  22..0

            +---------------------------------+

 double     ¦ S ¦   P     ¦         M         ¦

            +---------------------------------+

              63  62.. 52 51..                0

  long double

            +--------------------------------------------+

            ¦S ¦    P        ¦          M                ¦

            +--------------------------------------------+

             79 78..       64 63..                       0

 Вещественное число хранится в рамяти с нормализованной

 мантиссой - для нормализации ее сдвигают влево до тех пор, пока

 старшей цифрой мантиссы не станет 1, и каждая операция сдвига

 сопровождается уменьшением порядка на 1. В нормализованной

 мантиссе старшую цифру можно и не хранить, сэкономив на этом 1

 бит и увеличив точность представления чисел. Эта единица

 присутствует неявно и называется неявной единицей (implicit

 one). Отбрасывание старшей 1 выполняется для float и double,

 а для long double - нет.

Порядок числа хранится "сдвинутым", т е всегда увеличивается

так, чтобы он был всегда неотрицательным - для float он

увеличивается на 127, для double - на 1023, для long double - на

16383. Это избавляет от выделения бита для знака порядка и

упрощает операции сравнения порядков и арифметических операций с

порядками.

Например число  15.375 в 2-ичной системе 1111.011 в формате

float стандарта IEEE записывается как 1.111011 * 2^11 и его

внутренне представление таково:

   S=0;

   P=3+127=130 или 1000.0010

   M=1110110...0.

 ПЕРЕМЕННЫЕ ТИПА УКАЗАТЕЛЬ в Си - это элемент памяти

 для хранения адреса. Для описания переменной указателя

 необходимо указывать тип адресуемого элемента, символ *, и имя

 указательной переменной:

            int* int_ptr; /* указатель на целое */

            char* char_ptr;/*указатель на тип char */

 Имя типа со звездочкой воспринимается компилятором как особый

 тип данных "указатель на объект такого-то типа".

Особым типом указателя является бестиповый указатель void* -

адрес объекта с неизвестным размером в памяти, совместимый с

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

Но компилятору для корректной интерпретации  адресов нажна

информация о количестве участвующих в операции байтов - поэтому

при использовании void* - указателей необходимо выполнять явное

приведение типов (об это позже).

Для работы с переменными через указатели в Си предусмотрены 2

специальные операции:

   & - получения адреса объекта в памяти;

   * - получения значения, хранимого по адресу;

int i1,i2, * int_ptr; /* целые i1 и i2  и указатеь на целое

                       int_ptr  */

i1=32;               /* i1 получает значение 32 */

int_ptr=&i1;         /*int_ptr получает значение адреса i1 */

i2=*int_ptr;         /*i2 получает значение 32 через указатель

                       на i1 */

Указатели могут быть 2-х разновидностей - ближние near

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

адресовать не более 64 кбайт) и дальние far, содержащие полный

адрес объекта - сегмент и смещение - и позволяющие адресовать

любой байт в адресном пространстве. Ближние near указатели

занимают 2 байта в памяти, а дальние far - 4 байта.

Частным случаем far - указателя является нормализованный huge -

указатель, у которого сегмент имеет максимально возможное

значение, а смещение - минимально возможное, что позволяет

корректно выполнять операции сравнения указателей.

Для описания указателей с явным заданием их вида к адресуемому

типу справа приписывается слово near, far или huge:

        unsigned huge * huge_ptr;

        char far * char_ptr;

По умолчанию компилятор считает указатели на данные для младших

моделей памяти TINY, SMALL, MEDIUM ближними, а для старших

моделей памяти COMPACT, LARGE, HUGE - дальними.

В MS DOS и в Си размер указателя (число байтов для размещения

адреса) может быть 2-х или 4-хбайтовым, в зависимости от того,

является он ближним (т е содержит только смещение относительно

сегмента) или дальним (те содержити адрес сегмента и смещение),

Таким образом, размер указателя "по умолчанию" (без явного

определения near или far) зависит от модели памяти при

компиляции. В Си программы можно компилировать в одной из 6-ти

моделей памяти:

tini (крошечная) -во все сегментные регистры CS,DS,SS,ES

засылается один и тот же адрес, под программный код, статические

данные, динамически размещаемые данные и стек отводится 64

кбайт, все указатели только ближние 2-байтовые.

small (маленькая) - под код программы отводится 64 - кбайтовый

сегмент, а стек, куча и статические данные "прописываются" в

одном "коммунальном" 64 - кбайтовом сегменте. Все указателив

такой программе будут 2-байтовыми ближними.

medium (средняя) - код программы может быть до 1 мгбайта, т е в

программном коде указатели на подпрограммы будут дальними

4-байтовыми.  Стек, куча и статические данные по-прежнему

размещаются в одном 64 - кбайтовом сегменте и все данные

адресуютя ближними указателями.

compact (компактная) - 64 кбайт для кода и до 1 мбайт под

данные, в том числе по до 64 кбайт под статические данные и

стек. Вызовы подпрограмм с помощью ближних указателей, а данные

адресуются дальними.

large (большая) - 1 - мбайтный код, 64 кбайт под статические

данные, до 1мбат куча,. И программа и данные адресуются дальними

указателями.

huge (огромная) - такая же как большая, но объем статически

данных может быть больше 64 кбайт.

Всем указателям в Си можно присвоить безопасное значение адреса

- нуль целого типа - гарантируется , что этот адрес не совпадет

ни с одним использованным адресом. Для этого можно либо

использовать именованную константу NULL, либо непосредственно 0.

          char * ch_ptr = 0;

          int *  int_ptr = NULL;

Если указатель описан в глобальной области (вне тела какой-либо

подпрограммы) или в теле функции с предписанием static, то

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

данных, размещаемых в сегменте данных. Объявление же указателя в

локальной области (в теле любой подпрограммы, в том числе

главной nain) не приводит к инициализации указателя каким лиьо

осмысленным значением и может служить источником ошибок при

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

бессмысленному адресу.

Осмысленные значения указатели получают, когда им присваиваются

значения адресов переменных, значения других уже

инициализированных указателей или значения адресов, возвращаемых

функциями динамического распределения памяти.

 ТИП ПЕРЕЧИСЛЕНИЯ служит для улучшения читабельности программ,

 приближения их текста в некоторых случаях к естественному

 языку. При использовании вначале описывается само перечисление,

 задаваемое ключевым словом enum с именем шаблона и

 перечисляемыми элементами в фигурных скобках:

        enum color_type {red,amber,green };

Затем может быть описана переменная перечислимого типа:

        enum color_type c1;

и она может принимать любое значение из перечисленных в скобках:

        с1=green;

С помощью директивы typedef можно описать новые переменные

перечисления более экономно:

        typedef enum {понедельник=1, вторник,среда, четверг,

        пятница, суббота, воскресенье}DAYS;

        DAYS anyday;

 Собственно в фигурных скобках перечислимого типа находятся

именованные целочисленне константы, которым по умолчанию

компилятор присвоит значения от 0 и далее с шагом 1.  Но мы

можем и сами приписать желаемые значения каждому перечисляемому

элементу:

       enum color_type {red=4,amber=14,green=3 };

ССЫЛКИ В СИ++ .

Этот тип служит для определения псевдонимов объектов, уже

размещенных в памяти и используются как типы параметров

подпрограмм (в Си - это только функции) для передачи аргументов

по ссылке. Для описания ссылочного типа используется символ -

модификатор & аналогично символу * для указателей.Но описываемая

ссылка должна быть немедленно инициализирована - ей должно быть

присвоено значение. То есть по существу с помощью ссылки мы

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

использование ссылки дает тот же результат, что и прямое

использование объекта:

            int i;

            int &ir=i; /* для i установлен псевдоним ir */

            ir=3;      /*i плучает значение 3  */

Ссылки удобно использовать для присваивания значений параметрам

функций:

          void func(int &, int &);

          int a=3,b=4;

          func(a,b); /*аргументы func получат значения 3,4  */


Типы данных в Турбо Паскале

Ассортимент скалярных типов в Паскале значительно шире,чем в Си:

1-байтовое целое без знака          Byte  (0..255);

1-байтовое целое со знаком      ShortInt  (-128..127);

2-байтовое целое без знака          Word  (0..65535);

2-байтовое целое со знаком      Integer   (-32768..32767);

4-байтовое длинное целое      LongInt (-2147483648..2147483647);

4-байтовое вещественное       Single (1.5*10^-45..3.4*10^38);

6-байтовое вещественное       Real   (2.9*10^-39..1.7*10^38);

8-байтовое вещественное    Double (5.0*10^-324..1.7*10^308);

10-байтовое вещественное   Extended (3.4*10^-4951..1.1*10^4932);

Логический тип                 Boolean  (True,False);

Символьный тип           Char (любой символ из набора ASCII);

Строковый тип            String, String[n];

Указатель                Pointer;

Перечисление

Диапазон

Синтаксис Паскаля требует при размещении в памяти переменной

любого типа делать это всегда в разделе программы по имени Var

с указанием имени или списка имен и последующим указанием через

двоеточие типа:

            var

            i1,i2,i3:Integer;

            b1,b2,b3:Boolean;

            s1,s2,s3:String[123;

            p1,p2:   Pointer;

Присвоение начальных значений при описании переменных не

допускается. При необходимости это сделать приходится объявлять

переменную в разделе констант (это будет типизированная

псевдоконстанта, на самом деле - переменная со стартовым

значением) с указанием типа и значения:

            const

            i1:Integer=12;

            b1:Boolean=True;

            r1:Real=12.546;

Непосредственные числовые значения целых задаются в 10-тичной

или 16-ричной системах счисления, в последнем случае перед

значением ставится символ $:

         h1:= $a53f;

ШАБЛОН ПЕРЕЧИСЛЕНИЙ необходимо вначале описать в разделе Type,

а затем в разделе Var описать переменную этого типа:ъ

           type

           fruit= (груша, яблоко , слива);

           var

           f:fruit;

После этого где-то в процедуре можно использовать:

           f:=груша;

Нам с вами не разрешается присваивать значения элементам

перечисления, это делает компилятор (нумеруя элементы с 0 с

шагом 1), а мы можем получить соответствующие значения с помощью

функции Ord(имя элемента перечисления), возвращающей длинное

целое. Максимальное количество элементов в одном перечислении -

65535. Имена элементов не могут повторяться в пределах одной

программы.

Значения из перечислимого типа не могут непосредственно

участвовать в операциях ввода-вывода - их нельзя вывести на

экран или принтер или явно ввести с клавиатуры.

ТИП ДИАПАЗОН позволяет определить тип переменных,которые могут

принимать значения только в ограниченном поддиапазоне некоторого

базового типа - целочисленного, символьного или любого

введенного нами перечислимого типа.Для введения типа ДИАПАЗОН

надо в блоке TYPE указать его имя и границы через 2 точки:

           type

           Centuty = 1..20;

           CapsLetter = 'A'..'Z';

Операции ввода - вывода допустимы только с переменными, для

которых диапазон взят из "выводимого" базового типа.


Типы данных в Бейсике

Скалярные типы в Бейсике представлены числовыми целыми,

числовыми вещественными одинарной точности, числовыми

целыми и вещественными  двойной точности, строками.

Для описания типа переменных с многобуквенными именами

предусматриваются концевые символы типа:

  % - целые 2-байтовые: i1%=82

  ! - вещественные одинарной точности 4-байтовые s!=12.6

  # - вещественные двойной точности   8-байтовые q#=123.8

  $ - строквый тип

Объявление переменных выхлядит так:

DIM [SHARED] Имя_переменной [,Имя_переменной] ...

SHARED   указывает что переменную можно использовать во всех подпрограммах:

DIM SHARED height!

DIM length!,okt$,telefon$

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

 DEFINT A,B, I-L  для целых

 DEFSNG C,D, M-P  для одинарной точности

 DEFDBL E,F, Q-W  для двойной точности

 DEFSTR r,s       для строк

 DEF SEG [= адрес] для адреса текущего сегмента


Константы в Ассемблере

Простые Константы - это некоторые вполне определенные значения,

они предназначены для размещения в отведенных для переменных

участках памяти, поэтому они тоже имеют тип, который

распознается транслятором по способу написания значений

(Паскаль) или указывается явно при описании константы (Си).

АССЕМБЛЕР.

Ассемблер наиболее близок к машинной реализации, поэтому в нем

константы, как начальные значения переменных задаются при

необходимости непосредственно при описании типов переменных:

          var1  DW 82

Может использоваться 10-тичная по умолчанию, 16-ричная c буквой

H в конце или 2-ичная с буквой В в конце числа система счисления

          varH  DW 20H

          varB  DW 01011101B

 Используется также символьная форма задания целочисленных

 констант:

          var2 DB 'A'

В этом примере в 1 - байтовое поле памяти запишется код большой

латинской буквы А т е число 65 в 2 - ичном формате 01000001.

Символьный формат удобен не только тем, что позволяет нам не

помнить числовые коды символов, он спасает нас от эффекта

несовместимости различных таблиц кодировок и это очень актуально

для национальных нестандартизованных по кодам алфавитов.

Возможно также задание символьных последовательностей:

         var3 DB 'QWERTYUIO'

В этом варианте var3 описывает последовательность из 9

однобайтовых целочисленных кодов записанных в кавычках букв.

Если мы собираемся выводить такого рода последовательности как

символьные строки в файл, на принтер или на экран, то нам

придется в конце строки поставить обусловленный транслятором

символ окончания строки, иначе вывод из последовательно

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

случайно не встретится код символа конца строки. На

посимвольный вывод строк до встречи с символом конца строки

"настроены" все известные программы отображения символьных

строк в текстах.В ассемблере таким кодом принят $.  Поэтому

"закрытая" строка на 1 байт длиннее и выглядит так:

        var4 DB 'QWERTYUIO$'

Если одна и та же константа используется во многих местах

программы и ее значение может быть изменено в различных

вариантах, то было бы удобно иметь ее в программе под некоторым

именем в одном месте, чтобы не искать при необходимости изменить

ее по всему тексту. Для этого в большинстве

алгоритмических языков вводят именованные константы. В

ассемблере они описываются директивой EQU:

           TIMES  EQU 10

при этом например директива

          FLDA  DB  TIMES DUP (?)

при трансляции преобразуется в

          FLDA  DB  10 DUP (?)

а команда

         mov CX,TIMES

в команду

        mov  CX,10


Константы в Си и Си++
Константы в С и С++

Си  поддерживает задание целочисленных констант в 10-тичной,

8-ричной (с ведущим 0 и цифрами от 0 до 7), 16-ричной (с 0х в

начале числа и цифрами от 0 до А) системах счисления.

Инициализация переменных может выполняться непосредственно в

месте их описания:

         signed char ch1=18;

         unsigned u1=0456;

         long l1=23AF34EC;

Поддерживается также символьная форма задания целочисленных

констант:

         unsigned c2='A';

         long l2='L';

Для обозначения некоторых команд устройствам ввода - вывода,

размещаемых непосредственно в тексте, предусматривается набор

предопределенных именованных констант (esc -

последовательностей), начинающихся с обратной косой черты:

     \a - 0x07  - звуковой сигнал при выводе на экран

     \b - 0x08  - на символ влево

     \f - 0x0C  - прогон бумаги до начала следующей страницы

     \n - 0x0A  - перевод строки

     \r - 0x0D  - возврат каретки

     \t - 0x09  - горизонтальная табуляция

     \v - 0x0B  - вертикальная табуляция

После обратного слэша в esc - последовательности может стоять

ASCII- код символа  (до 3-х восьмеричных цифр) char c1='\123';

или буква х с 16-ричным ASCII - кодом символа:

         int c2 = '\x023';

Еще раз подчеркнем, что в Си символьный литерал - это не более

чем способ задания числовой константы и операции вида

        int c3= 'B' + 12;

вполне допустимы.

Вещественные константы-значения состоят из цифр, десятичной

точки и знаков десятичного порядка e или E% и по умолчанию имеют

тип double:

   float f1= 2e1;

   double d1 = 3.14159265359;

   double d2 = .1234;

   float f2 = 2.1e-12;

Строковые константы - значения (строковые литералы) оформляются

в виде текста в двойных кавычках и обычно используются для

определения значений указателей на тип char:

   char str_ptr1 = "Это строковый литерал";

По такому описанию компилятор подсчитывает количество байтов,

необходимых для размещения в памяти этой строки, добавляет в

конец 1 байт с завершающим строку символом с кодом 0, выделяет

необходимую память и адрес ее первого байта помещает в

переменную - указательного типа str_ptr1, предварительно выделив

память и для нее.

Адресные константы - значения задаются полным адресом - для

ближних адресов 2-байтовым целым, а для дальних - 4 - байтовым.

Например адрес видеобуфера в 3-м текстовом режиме видеосистемы

может записываться так:

       char far * vbuf = 0xB8000000;

а смещение указателя головы буфера клавиатуры относительно

начала области BIOS (адрес 0x40):

      _ES=0x40;

      char register _es *head = 0x1a;

"Адресный нуль" обозначен в Си именем NULL и обозначает

отсутствующий адрес.

Именованные константы в Си задаются:

- директивой #define препроцессора (специальная программа

предкомпиляционной обработки программного текста по имени

cpp.exe) для макроподстановок:

           #define  const1  234

           #define  const2  98.456

           #define  const3  Навуходоносор

Препроцессор, встречая в тексте программы  имя константы,

подставляет определенное вторым словом ее значение.

-в Си++ это можно сделать использованием модификатора const в

объявлении типа :

          const int n1=245,n3='A';

          const long double pi = 3.14159265358979323846;

Такая переменная не может использоваться в левой части оператора

присваивания, т е ее значение после начальной инициализации не

может быть изменено. Использование именованных констант улучшает

читабельность программ и позволяет избежать трудноуловимых при

отладке ошибок набора значений при многократном использовании

одних и тех же констант.


Константы в Турбо Паскале

Простые константы - значения компилятор Паскаля  распознает

по синтаксису их записи, а именованные константы объявляются в

разделах const программы или подпрограмм без указания типа:

          const

          Min=0;

          Max=500;

          e=2.7;

  Значения символьных и строковых констант размещаются в

  одиночных кавычках

          SpecChar='\';

          HelpStr='Нажмите клавишу F1';

          NoAdress=nil; {адресная константа со значением

                         отсутствующего адреса nil}

Альтернативной формой представления значения символов есть их

запись через значение числового кода, предваряемого символом #:

            #97  (символ а)

            #0   (нуль-символ)

            #32  (пробел)

          Ok=True;      {логическая константа}

Управляющие символы с кодами от 0 до 31 могут быть представлены

их "клавиатурными" обозначениями - значком ^ и буквой алфавита с

тем же номером (для диапазона кодов 1..26 ) или служебным знаком

(в диапазоне 27..31):

           ^A - код 1

           ^B - код 2

           ...

           ^Z - код 26

           ^[ - код 27

в том числе ^G - звонок, ^I - TAB, ^J - код 10 - перевод строки

^M - код 13 - возврат каретки

 Alpha=['A'..'Z'];  {константа типа диапазон}

При определении значений констант можно использовать выражения

из чисел, некоторых функций языка и ранее определенных простых

констант:

         const

         Interval=Max-Min+1;

         Key=Chr(27);

         e2=e*e;

         BigHelpStr=HelpStr + 'для подсказки';

Непосредственные числовые значения целых задаются в 10-тичной

или 16-ричной системах счисления, в последнем случае перед

значением ставится символ $:

         const

         h1= $a53f;

         Flag=Ptr($0000,$00F0);{адресная константа}

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

поразрядные операции, логические операции, операции отношения и

следующие функции:

Abs,Round,Trunc,Chr,Ord,Pred,Succ,Odd,SizeOf,Length,Ptr,Lo,Hi,Swap


Константы в Бейсике

Непосредственные значения  чисел  могут содержать в конце значок

обозначения типа %,!,# , чтобы транслятор знал, сколько памяти

необходимо выделить под инициализируемую значением переменную:

                  A1=123# : B1=234.6789!

Строковые литералы в Бейсике заключаются в двойные кавычки:

        A$="qwerty"

Символ в Бейсике - это просто строка из 1-го символа.

        C$="A"

В Бейсике могут также использоваться именованные константы.Их определения следует размещать в начале программы  после ключевого слова CONST и после этого они могут использоваться во всей программе. Имя константы - до 40 символов и должно начинаться с буквы, может включать цифры и точку а последним в имени должен быть значек типа  !  # %  &  $

CONST Имя_константы =выражение[,Имя константы=выражение]...

Пример: CONST pi!=3.14

               CONST A%=8+4


Операции над простыми типами

Операции над целочисленнми типами

 Операция         ¦ Ассемблер       ¦ Си   ¦ Паскаль ¦ Бейсик  ¦

------------------+----------+------+---------+---------¦

 Сложение         ¦        ADD           ¦     +   ¦       +        ¦              +     ¦

 Вычитание      ¦         SUB           ¦      -   ¦           -     ¦               -     ¦

 Умножение      ¦ IMUL,MUL      ¦    *   ¦          *     ¦               *     ¦

 Деление           ¦ IDIV,DIV         ¦      /   ¦        div   ¦                 \     ¦

(получ частного)  ¦          ¦      ¦         ¦         ¦

 Деление          ¦  IDIV,DIV¦  %   ¦   mod   ¦   mod   ¦

(получ остатка)   ¦          ¦      ¦         ¦         ¦

                  ¦          ¦      ¦         ¦         ¦

Побитовое И       ¦   AND    ¦  &   ¦   and   ¦         ¦

Побитовое ИЛИ     ¦   OR     ¦  |   ¦    or   ¦         ¦

Побитовое исключ  ¦   XOR    ¦  ^   ¦   xor   ¦         ¦

ИЛИ               ¦          ¦      ¦         ¦         ¦

Инверсия битов    ¦   NOT    ¦  ~   ¦   not   ¦         ¦

Сдвиг влево       ¦SAL,SHL,RCL  <<  ¦   shl   ¦         ¦

Сдвиг вправо      ¦SAR,SHR   ¦  >>  ¦   shr   ¦         ¦

                  ¦RCR,ROR   ¦      ¦         ¦         ¦

Присвоение знач   ¦ MOV      ¦  :=  ¦   =     ¦   =     ¦

--------------------------------------------------------+

Операции с вещественными числовыми типами

 Операция        ¦ Си   ¦ Паскаль ¦ Бейсик  ¦

-----------------+------+---------+---------¦

 Сложение        ¦  +   ¦   +     ¦   +     ¦

 Вычитание       ¦  -   ¦   -     ¦   -     ¦

 Умножение       ¦  *   ¦   *     ¦   *     ¦

 Деление         ¦  /   ¦   /     ¦   /     ¦

(получ частного) ¦      ¦         ¦         ¦

 Возв в степень  ¦ библ ¦   -     ¦   ^     ¦

Операции с любыми числовыми типами

 Операция        ¦ Си       ¦ Паскаль ¦  Бейсик ¦Ассемблер

-----------------+----------+---------+---------¦

Инкремент        ¦ ++       ¦ библ    ¦         ¦ INC

Декремент        ¦ --       ¦ библ    ¦         ¦ DEC

Получ размера    ¦sizeof    ¦ sizeof  ¦         ¦

типа или объекта ¦          ¦         ¦         ¦

                 ¦          ¦         ¦         ¦

Операция с после ¦опер=     ¦  -      ¦    -    ¦

дующим присвоен  ¦напр      ¦         ¦         ¦

                 ¦x+=2;     ¦         ¦         ¦

                 ¦y/=13;    ¦         ¦         ¦

                 ¦etc       ¦         ¦         ¦

Выполн по усл    ¦?:        ¦  -      ¦    -    ¦

                 ¦e1?e2:e3  ¦         ¦         ¦

Операции сравн   ¦          ¦         ¦         ¦

на больше        ¦   >      ¦   >     ¦    >    ¦

   меньше        ¦   <      ¦   <     ¦    <    ¦

   равно         ¦  ==      ¦   =     ¦    =    ¦

не равно         ¦  !=      ¦  <>     ¦   <>,>< ¦

больше или равно ¦  >=      ¦   >=    ¦    >=   ¦

меньше или равно ¦  <=      ¦   <=    ¦    <=   ¦

                 ¦          ¦         ¦         ¦

------------------------------------------------+

Примечание: в Ассемблере операции сравнения встроены в команды

условных переходов:

JA/JNBE -  по выше или "не ниже или равно"

JAE/JNB -  по "выше или равно" или "не ниже"

JB/JNAE -  по ниже или "не выше или равно"

JBE/JNA -  по "ниже или равно" или "не выше"

JC      -  по переносу

JCXZ    -  по СХ равен 0

JE/JZ   -  по рано или по нулю

JG/JNLE -  по больше или "не меньше или равно"

JGE/JNL -  по "больше или равно" или "не меньше"

JL/JNGE -  по "меньше" или "не больше или равно"

JLE/JNG -  по "меньше или равно" или "не больше"

JNC     -  если нет переноса

JNE/JNZ-   по не равно или не нуль

JNO     -  если нет переполнения

JNP/JPO -  если нет паритета или паритет нечетный

JNS     -  если нет знака

JO      -  по переполнению

JP/JPE  -  если есть паритет или паритет четный

JS      -  по знаку

Операции над символами в Паскале (в других рассматриваемых нами

языках специальный символьный тип отсутствует ).

Символы в Паскале можно лишь присваивать и сравнивать друг с

другом - они равны, если равны их коды и большим считается тот,

у которого больше кодовое число.

Операции над строками

Основные операции - присвоение, сравнение и слияние.

Отсутствие строкового типа в Си обусловило наличие только

библиотечных процедур работы со строками, которые в Си

трактуются как любая последовательность байтов, заканчивающаяся

нулевым значением.

 Операция         ¦ Ассемблер¦ Си   ¦ Паскаль ¦ Бейсик  ¦

------------------+----------+------+---------+---------¦

Пересылка строк   ¦ MOVS,    ¦ библ ¦   :=    ¦   =     ¦

между обл памяти  ¦ MOVSB,   ¦strcpy¦         ¦         ¦

(присвоение)      ¦ MOVSW    ¦      ¦         ¦         ¦

                  ¦ STOS     ¦      ¦         ¦         ¦

Поиск символа в   ¦SCAS,SCASB¦ библ ¦   pos   ¦         ¦

сроке             ¦SCASW     ¦strchr¦         ¦         ¦

                  ¦          ¦      ¦         ¦         ¦

Сравнение строк   ¦CMPS      ¦ библ ¦ > < <>  ¦         ¦

                  ¦          ¦strcmp¦         ¦         ¦

Конкаттенация     ¦          ¦  библ¦         ¦         ¦

(сцепление)строк  ¦          ¦ strcat    +    ¦    +    ¦

                  ¦          ¦      ¦ concat  ¦         ¦

Операции над адресами

В Ассемблере и Си поддерживается адресная арифметика - так как

адреса представляют собой целые чмсла, то над ними разрешается

осуществлять операции целочисленной арифметики. При этом

ответственность за корректность результата полностью возлагается

на программиста.

Паскаль для адресного типа указателей поддерживает только

операции присвоения и сравнения, а адресная арифметика

отсутствует.

Для работы с абсолютными адресами в Паскале существует директива

absolute :

      type

      ByteArray = Array [1..2000] of Byte;

      var

Memory: Byte absolute $0000:$0417;

VidMem: ByteArray absolute $B800:$0000;

Однобайтовая переменная Memory займет укзанное адресом место в

системной области и даст возможность получать и изменять

значение по этому адресу, а массив VidMem совместится с

видеобуфером, что даст возможность читать и изменять лежащие там

коды символов.

Операция получения адреса расположенного в памяти объекта:

     Ассемблер  ¦   Си  ¦   Паскаль    ¦     Бейсик

------------------+-------+--------------+-------------

      LDS,LES     ¦   &   ¦            @       ¦

      LEA             ¦         ¦                      ¦

Возвращаемый операцией результат размещается в Ассемблере обычно

в регистрах


Массивы в Ассемблере

Массив в алгоритмических - это обозначенная одним именем

совокупность элементов одного и того же типа. Таким образом

массив - это составной, агрегированный тип, он состоит из

определенного количества объектов другого типа. Элементами

массивов в алгоритмических языках могут быть как простые, так и

сложные, составные типы:напрмер, массивы целых или вещественных

чисел, массивы строк, массивы массивов (2-мерные или

многомерные массивы любых элементов), массивы записей (мы их

рассмотрим несколько позже),массивы указателей и т д

Одномерные и двумерные массивы встречаются довольно часто,

3-мерные массивы - это уже почти экзотика, но нам неизвестны

смельчаки, способные работать с массивами числом измерений более

3, хотя написать объявление такого массива для размещения его в

памяти не составляет труда.

Массивы в ассемблере.

Зарезервировать память под массив в Ассемблере проще всего

с директивой DUP (повторить, продублировать):

A1 DW 10 DUP(?)  ;10 2-байтовых элементов неопределенных

                 ;значений

A2 DB 115 DUP(0) ;115 1-байтовых с нулевым начальным

               ;значением

A3 DB 3 DUP (4 DUP 8); 12 восьмерок, можно трактовать

                   ;как 3 строки по 4 восьмерки в каждой,

                   ;т е как 2-мерный массив

Обращение к элементам массива осуществляют по имени и номеру

(индексу). Элементы нумеруются, начиная с 0.  Имя массива в

ассемблере - это по существу ссылка на 0-й элемент, поэтому

обращение к 6-му элементу выглядит так:

           MOV AX,A1    ;взять в АХ 0-й элемент массива А1

           MOV ВX,A2+5  ;взять в ВХ 5-й элемент массива А2

           MOV СX,А3+7  ;и т д


Массивы в Си

Резервирование памяти под массив в Си осуществляют объявлением

типа его элементов, имени массива и его размера (количества

размещаемых в нем элементов) в квадратных скобках:

     int  A1[123]; /*массив из 123 целочисленных  */

  double  A2[1024];/*массив из 1024 вещественных 2-й точности */

Компилятор отводит под массив

    sizeof(тип)*(число элементов) байтов памяти.

Имя массива в Си - это  константный  адрес его 0-го элемента

и элементы в массивах Си тоже нумеруются с 0. Этот адрес

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

дальнейшем не может быть изменен! Поэтому в Си возможно

обращение к элементам массива такое же, как и в ассемблере - по

содержимому имени и прибавленному к нему номеру элемента:

*(A1 + 8)= 67; /* в 8-й элемент массива А1 записано 67 */

 Здесь А1+8 - адрес 8-го элемента, а * означает "получить

 значение " по стоящему после нее адресу.

То же самое может быть записано по другому:

A1[8] = 67;

и многие программисты предпочитают эту запись с индексом в

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

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

Начальные значения могут быть присвоены элементам массива

непосредственно при его объявлении следующим образом:

  тип  имя_массива[] = {знач1, знач2, .. , значN};

Например:

  int data[5] = {5,4,3,2,1};

  float f1[] ={3.4,2.7,1.8,6.9,-24.3};

  char prompt[] = {'О','т','в','е','т','\0'};

Многомерные массивы описываются так же, как и одномерные,но для

каждой размерности добавляется пара квадратных скобок с

размером внутри скобок:

       int d2[14][3];

      float f3[3][4][5];

Все выражения ,содержащие обращения  к элементам массивов,

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

           a[2][3] эквивалентно *(*(a+2) +3

 Для переписи элементов одного массива в другой придется сделать

 это поэлементно!

ВНИМАНИЕ! КОМПИЛЯТОР Си НЕ ОПЕРДЕЛЯЕТ ОШИБОК ИНДЕКСАЦИИ МАССИВОВ

ЗАПИСЬ КАКИХ-ЛИБО ЗНАЧЕНИЙ В НЕСУЩЕСТВУЮЩИЕ ЭЛЕМЕНТЫ ЗА

ПРЕДЕЛАМИ МАССИВА МОЖЕТ ИСПОРТИТЬ НАПРИМЕР ОБЛАСТЬ ПАМЯТИ

ОПЕРАЦИОННОЙ СИСТЕМЫ. ОТВЕТСТВЕННОСТЬ ЗА ПРАВИЛЬНОСТЬ ИНДЕКСАЦИИ

ПОЛНОСТЬЮ ВОЗЛАГАЕТСЯ НА ПРОГРАММИСТА.

Этот  недостаток  Си  объясняется  просто:  язык ориентирован на

профессиональное    программирование,    в    котором    размеры

обрабатываемых  массивовв  как  правило  на  стадии  составления

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

выполнения программы с последующим размещением массивов в "куче"

-  динамически  распределяемой  области  памяти.  При  этом мы в

качестве  единственного  ориентира  получаем  адрес  выделенного

операционной  системой  блока  памяти  и  построить  программу -

транслятор,  определяющую  правильность   обращения  по   адресу

оказывается  очень  сложно.  Даже  в  таких  строгих  языках как

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

на практике не  работет. Впрочем,  если необходима  строгая

проверка типов,  можно  использовать  особую  версию

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

"по  ниткам" . Lint не формирует  никакой   программы,  она

только  очень   тщательно проверяет  ее  текст  с  разных  точек

зрения, проверяя все, что можно  проверить  при  трансляции  и

загрузке. Lint обнаруживает неверное  использование  типов,

несостоятельное   использование аргументов, неиспольземые и

неинициализированные переменные и  т п.

Вообще объявление неинициализированного указателя вида

            char * str;

можно трактовать как объявление массива с пока неизвестным

количеством элементов.В объявлении вида

            char *str = "Это массив символов";

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

память как под текст так и под адрес его первой буквы.


Массивы в Турбо Паскале

Тип "массив" определяется в Паскале конструкцией

             Array [диапазон] of ТипЭлементов

Диапазон в квадратных скобках определяет значения индексов

первого и последнего элемента, так что при объявлении типов

           type

           A1 = Array [1..10] of Real;

           A2 = Array [11..20] of Real;

мы вводим 2 различных типа, отличающихся способом нумерации

своих элементов.

Доступ к i - му элементу осуществляется через запись индекса в

квадратных скобках, а попытка обратиться к элементу с индексом,

не соответствующим диапазону в описании типа вызовет ошибку

компилятора, если при компиляции задан ключ {$R+}.Чтобы отменить

проверку выхода за дипазон, устанавливают {$R-} - обычно после

завершения отладки программы, чтобы удалить из нее излишние

фрагмены кода, связанные с такой проверкой.

 Пример описания массива в Паскале:

          var

          V1:Array[1..31] of Real;

Диапазон числовыых индексов должен умещаться максимум в типе

Word, а длина массива в байтах не должна превышать 65520 байт.

Диапазон может задаваться любым перечислимым типом, как

встроенным, так и вводимым нами, причем если диапазон

соответствует всему типу, то можно указывать просто имя

перечислимого типа:

      type

      MT = (January,February,March,April,May);

      CT = Array [MT] of Word;

      ST = Array [March..May] of Word;

      var

      C:CT; S:ST;

      A1: Array ['A'..'Z'] of char;

      B1:Array [Boolean] of Byte;

Можно объявлять массивы массивов:

      type

      VectorType=Array[1..3] of Real;

      MatrixType=Array[1..10] of VectorType;

2-мерный массив можно описать и по-другому:

      type

      MatrixType = Array[1..10] of Array [1..3] of Real;

или  MatrixType = Array[1..10,1..3] of Real;

Каждое измерение массива совершенно не зависит от остальных и

можно объявлять массивы с различными индексами:

      var

      M:Array[-10..0,'A'..'Z'] of Byte;

При обращении к элементам массива индексы можно задавать как

через запятую, так и в отдельных квадратных скобках:

      M[-3,'B',True] эквивалентно M[-3]['B'][True]

Коипонентом массива может может быть любой тип.

В памяти элементы массива хранятся так, что быстрее всего

изменяется самый дальний индекс, в частности 2- мерные массивы -

построчно.

К 2-м совместимым по типам массивам применима операция

присваивания:

              A:=B;

которая копирует поэлементно массив В в массив А.

Это свойство можно, например использовать для сохранения

текстового экрана для последующего восстановления:

       type

       S=Array[1..2000] of Byte;

       var

       S1:S;

       S2:S absolute $b800:$0000;

где-то в программе

begin

       S1:=S2;   {сохраняем экран}

       ... что-то выводим

       ... делаем еще что-то

       S2:=S1;   {восстанавливаем}

end;


Массивы в QBASIC

Описание массива выполняется с помощью оператора DIM:

   DIM имя [SHARED] Имя(Размерность)

Размерность можно указывать либо одним числом старшего индекса

(При этом размер массивов на 1 больше старшего индекса, так как

по умолчанию элементы нумеруются с 0) либо конструкцией вида

"младший индекс ТО старший индекс"

Тип элементов при этом определяется именем с символом типа в

конце:

  DIM A%(100), B!(32), C#(9),S$(15), D(1 TO 30), E(4 TO 104)

Для доступа к элементам массива указывают его имя и индекс в

круглых или квадратных скобках:

     A%[0]=-81: B!(18)= 13.6: C#[5]=12.34567

     S$[2]="Это 2-я строка массива строк"

Программирующие на Бейсике иногда испытывают неудобства при

нумерации элементов от 0 - в этом случае о нулевом элементе

можно просто "забыть" и не использовать его. Если же потери

памяти на неиспользуемый 0-й элемент при этом кажутся

нежелательными, то можно до объявления всех массивов задать

нумерацию с 1 оператором

      OPTION BASE 1

Так как мы изучаем интерпретирующую версию транслятора с

Бейсика, то размер массива может указываться в его описании не

константой, как это делается в Паскале и Си , а переменной, т е

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

программы его размера:

     INPUT "Введите размер массива",SIZE

     DIM AR(SIZE)

Можно определять массив с одним и тем же именем в программе

неоднократно с различными размерами, но предварительно его

придется уничтожить оператором

     ERASE имя_массива

Многомерные массивы описываются указанием всех старших индексов

через запятую:

    DIM A%(1 TO 4,1 TO 2), B!(2,3,4,5,6)

а обращение к элементам тоже не вызывает затруднений:

       A%(2,1)=-44; B!(1,2,3,4,4)=1234.5678


Записи (структуры) в Ассемблере

Структуры и объединения в Турбо-ассемблере.

Структурой называется объединенный под одним именем блок данных

разных типов, как правило связанных между собой смыслом решаемой

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

госавтоинспекции, то нам необходимо хранить целый набор

разнотипных данных о каждом автомобиле - Фамилию и адрес

владельца, марку автомобиля, номер шасси и двигателя, цвет

кузова, дату последнего техосмотра, адрес стоянки и т д - и все

эти данные желательно хранить "кучно", вместе, с облегченным

обращением к каждому их элементу (эти элементы принято называть

полями структур).

Так как структуры отражают широко распространенную табличную

форму хранения данных,то они широко используются от программ

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

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

отводится только под самый длинный (то есть занимающий

наибольшую память) элемент (поле) и запись - чтение различных и

разнотипных полей осуществляется в одну и ту же совместно

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

размеров объединений кратны длине самых маленьких полей, хотя в

принципе это необязательно.

При описании структур и объединений различают описание типа или

шаблона структуры или объединения и описание переменных

структурного типа - при описании шаблона память для хранения

данных не отводится, это только описание нового, создаваемого

нами типа данных.

Турбо - ассемблер фирмы Борланд поддерживает структурный тип и

и тип объединения описание шаблона  структуры в Турбо -

ассемблере имеет вид:

           Имя_шаблона_структуры  STRUC

               описание поля 1

               описание поля 2

               ...

           Имя_шаблона_структуры  ENDS

(в описании объединения ключевое слово STRUC заменяется

ключевым словом UNION, в остальном описания идентичны)

 Описание переменной структурного типа имеет вид:

        Имя_ переменной Имя_шаблона_структуры

        <значение поля 1,значение поля 2,

        значение поля 3>,

        ...

Например, в области действия описания структуры Person

       Person STRUC

         Name DB 'Иван Иванов    '

         Age  DW 40

         Sex  DB ?

      Person ENDS

выражение

Bob Person <'Борис Борисов',,1>

является описанием структурной переменной Bob, состоящей из

полей Name, Age и Sex, размер которых определяется описанием

шаблона структуры; значением поля Name будет Борис Борисов, поля

Age - 40, поля Sex - 1.

В пределах одного модуля имена полей структур и объединений в

Турбо - ассемблере должны быть уникальными.

Для обращения к отдельным полям структурных переменных

используется оператор . (точка, разделяющая имя структурной

переменной и имя поля; этот оператор  может трактоваться как

оператор + , так как имя поля представляет постоянную, равную

смещению этого поля относительно начала структуры, то есть

относительно имени структурной переменной).

В области действия описания структуры Date

Date STRUC

 Year DW ?

 Month DB ?,-1

 Day DW ?

Date ENDS

...

TheDay Date <1981,12,13>

...

выполнение команд

mov bx,OFFSET TheDay

mov ax,[bx.Year]        ;В АХ будет 1981

mov ah,[TheDey.Month]   ;В АН будет 12

mov al,[byte ptr TheDey.Dey] ;В AL будет 13

mov al,[TheDey.(Month+1)] ;В AL будет -1

приведет к загрузке регистров в соответствиис комментариями.


Структуры и объединения в Си

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

   struct Имя_шаблона_структуры

   {

    тип_поля_1  имя_поля1;

    тип_поля_2  имя_поля2;

    ...

    тип_поля_N  имя_поляN;

   };   /*Точка с запятой здесь обязательна! */

  Описание переменной структурного типа выглядит так:

    struct имя_шаблона  имя_перем1, имя_перем2;

  К полям структур можно обращаться, указывая имя переменной и

  имя поля, разделяя эти имена точкой:

    type1 value1 = x.field1;

    type2 value2 = z.field2;

   К структурам и их компонентам можно применять операцию

   оперделения адреса & и если например переменная struct_ptr

   определена как указатель на структуру, то для доступа к полям

   структуры можно использовать операцию ->. Например:

     struct struct_name * struct_ptr = &x;

     type1 value1 = struct_ptr->field1;

   Оператор struct_ptr -> field1  эквивалентен

   (*struct_ptr).field1

  Так как при описании структурных переменных требуется наличие

  спецификатора struct, то бывает удобнее имя шаблона структуры

  задавать макроопределением:

   #define COMPLEX struct complextype

   COMPLEX

   {

     float real;

     float imag;

   };

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

 естественной форме:

     COMPLEX c1,c2,c3;

     c3.real = c1.real*c2.real - c1.imag*c2.imag;

     c3.imag = c1.imag*c2.real + c1.real*c2.imag;

 Описание шаблона и структурной переменной можно объединять:

     struct complex_type

     {

       double real;

       double imag;

     }my_complex;

 Если структурные переменные являются глобальными или описаны

 как static в локальных областях (внутри подпрограмм), то их,

 как и массивы, можно инициализировать непосредственно в месте

 описания:

   #define RECORD struct data_record

   RECORD

   {

     char *last_name;

     char *first_name;

     long id_number;

   };

   RECORD my_record ={"Ричард","Уинер",12345678};

 Структуры могут быть элементами массивов, а массивы -

 полями структур, что позволяет строить универсальные и гибкие

 структуры данных.

   RECORD data_base[3] = {"Уинер","Ирвин",1,

                          "Уинер","Марк",2,

                          "Уинер","Эрик",3

                         };

 Описание объединений формально отличается только ключевым

 словом union вместо struct, но по содеожанию отличие очень

 существенно - в объединениях (иногда переводят словом "союз")

 все поля независимо от типов хранят свои значения в одной и той

 же области памяти

      #define ALERNATETIVE_DATA union data_record

      ALERNATETIVE_DATA

      {

        char s[4];

        float r;

        int i;

        char ch;

        unsigned j;

      };

      ALERNATETIVE_DATA data;

  Под переменную data будет отведено 4 байта - это длина самого

  большого поля.

  В принципе объединения могут использоваться для преобразования

  одних типов данных в другие - в этом случае запись

  осуществляется как бы в поле одного типа , а считывание

  - из поля другого типа, но так как они лежат в одном и том же

  месте, "перекрываются" , то происхоодит преобразование одного

  типа в другой - но это "дурной стиль", результаты такого

  преобразования  могут быть непереносимы между различными

  типами компьютеров и непредсказуемы.

  Но в отдельных случаях, когда нам необходимо использовать или

  цельное отображение участка памяти , или его часть,

  использование объединений оказывается очень удобным -

  например, объединение полей для хранения значений полных

  процессорных регистров и полурегистров, 2-байтовых и

  1-байтовых кодов клавиш и пр.

  Объединения и структуры могут быть вложенными, т е сами

  являтьяся полями структур - в этом случае все имена тоже

  отделяются друг от друга точками в количестве вложенных

  структур или объединений.

      structx.structz.fieldz= 18;


Записи в Турбо Паскале

Аналогом Си-структур являются Паскалевские записи и тип

 "запись" определяется конструкцией

  RECORD

     Поле1 : ТипПоля1;

     Поле2 : ТипПоля2;

     ...

     ПолеN : ТипПоляN;

  END;

Поле записи может иметь любой Паскалевский тип данных - массив,

другая запись, множество. Доступ к полям осуществляется через

имя переменной и имя поля, разделяемых точкой.

   type

   PointRecType = RECORD x,y :Integer  END;

   var

   Point:PointRecType;

   begin

       ...

       Point.x = 123;

       ...

   end;

  Независимо от количества объявленных переменных типа "запись",

  поля каждой переменной называются одинаково, в соответствии с

  шаблоном. Поскрльку имена полей "скрыты" внутри типа, они

  могут совпадать с именами "внешних" переменных и поля в других

  описаниях записей, например:

type

PointRecType = RECORD X,Y : Integer END;

ColorPointRecType = RECPRD X,Y :Integer;Color:Word END;

var

X,Y :Integer;

Point :PointRecType;

ColorPoint :ColorPointRecType;

В программе X,Point.X,ColorPoint.X - совершенно разные значения.

Записи в Паскале могут иметь так называемую вариантную часть.

Это означает, что в пределах одного типа можно задать несколько

различных структур. Непосредственный выбор одной из сьруктур

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

значением. Вариантные поля в описании шаблона записи указываются

после того, как перечислены все поля фиксированного типа и

оформляются особым образом:

TYPE

VRecType = RECORD              {Тип записи с вариантами}

Number :Byte;

case Measure : Char of         {Признак единиц измерения длины}

'д','Д':(inches : Word);       {В дюймах}

'c','C':(cantimeters :LongInt); {В сантиметрах}

'?':(Comment1,Comment2 : String[16])  {Тексты - комментарии}

          END;

В примере записи есть обычное фиксированное поле

Number,и поле - селектор Measure, обрамленное словами CASE и OF,

после чего идет перечисление вариантов 3-го поля в круглых

скобках. Содержимое поля - селектора определяет, какой именно

вариант будет выбран при работе с записью. В текущий момент

доступны поля только одного из возможных вариантов, в

зависимости от значения, присвоенного полю - селектору. Все

варианты располагаются в одном и том же месте памяти, а размер

этого места определяется самым объемным вариантом - в нашем

случае 2*(16+1) байтов для поля String[16].

Поле - селектор можно игнорировать, обращаясь к любому из полей

вариантов - одно и то же значение будет трактоваться в

соответствии с типом поля. Здесь всегда таится возможность

ошибок, которые никак не диагностируются, но если не ошибаться,

то можно с удобством использовать вариантные записи для хранения

различных вариантов например в нашем случае единиц измерения

длины.

Если явный указатель варианта не нужен, его можно заменить

именем любого перечислимого типа, имеющего достаточное

количество вариантов :

TYPE

VRecType = RECORD

Number :Byte;

case Byte of

1:(inches : Word);       {В дюймах}

2:(cantimeters :LongInt); {В сантиметрах}

3:Comment1,Comment2 : String[16])  {Тексты - комментарии}

END;

Значения констант в описании в этом случае - чистая условность и

могут быть любыми неповторяющимися в пределах типа Byte. При

отказе от поля - селектора во время работы программы уже нельзя

определить, какой вариант должен использоваться в данный момент

и так обычно работают при необходимости разнотипного

представления одних и тех же данных:

TYPE

CharArrayType = Array[1..4] of char;

VAR

V4:RECORD

case Boolean of

True : (C:CharArrayType);

False: (B1,B2,B3,B4 :Byte)

END;

Поля B1..B4 позволяют работатьс ASCII - кодами символов, а поля

С[1]..C[4] - с символами, не прибегая к явному преобразованию

типов.

Переменные типа "запись" могут участвовать только в операциях

присваивания, а их поля - в любых дозволенных для их типа

операциях.

Для облегчения работы с полями записей в Паскале введен оператор

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

WITH ИмяПеременной_Записи DO Оператор;

Внутри оператора обращение к полям уже производится без указания

имени переменной:

VAR

DemoRec : RECORD X,Y :Integer END;

WITH DemoRec DO

begin

    X:=0;Y:=129;

End;

При использовании внутри WITH "незаписных" переменных необходимо

следить, чтобы их имена не совпадали с "записными".При

необходимости "развязки" внутри WITH к совпадающим внешним

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

или модуля в которых они описаны:

PROGRAM MAIN;

VAR X,Y :Integer;

RecXY :RECORD X,Y :Integer END;

begin

WITH RecXY DO begin

X:=3.14*Main.X;

Y:=3.14*Main.Y end;

end;

Если одно из полей записи - тоже запись, можно распространить

оператор присоединения на несколько полей вглубь, перечислив их

через запятую, но внутритела оператора можно будет обращаться

только к последним полям:

WITH ИмяЗаписи, Поле_Запись DO

begin

    Обращения к миенам полей Поля_Запись

end;


Множества в Турбо Паскале

Другие рассматриваемые нами языки не поддерживают тип множество.

Говоря "множество" всегда необходимо указывать "чего?".

В Паскале множества - это наборы значений скалярного

перечислимого типа не более чем из 256 элементов. Это например

встроенные перечислимые типы -  целочисленные Byte, символьный

Char или созданный нами скалярный тип, например:

TYPE

VAType =(MDA,Hercules,CGA,EGA,VGA,Other,NotDetected);

VAR

VideoAdapter :VAType;

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

значений.

В описании множества как типа используется конструкция Set of с

последующим указанием базового типа - скалярного типа, из

элементов которого составляется множество.

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

TYPE

SetChar = Set of Char;

SetByte = Set of Byte;

SetDigit = Set of 0..9;

SetDChar = Set of '0'..'9';

SetVA = Set of Cga..Vga;

В задании типа множества можно "урезать" базовый тип, задавая

поддиапазон его значений.

Если перечислимый тип вводится только для подстановки его имени

Set of, то можно перечислять значения сразу в конструкции Set

of, не забывая круглые скобки:

TYPE

SetVideo = Set of (Mda,Hewrcules,Cga,Ega,Vga,Other,NotDetected);

Можно опустить фазу задания типа в разделе TYPE и сразу задавать

его в разделе VAR:

VAR

V1 : Set of ...

Каждый элемент множества имеет сопоставимый номер - для типа

Byte это значение  числа, для Char - это код символов, нумерация

идет всегда от 0 до 255. Множества имеют весьма компактное

машинное представление - 1 бит на 1 элемент, поэтому для

хранения 256 элементов расходуется 32 байта.

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

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

скобки:

SByte :=[1,2,3,4,10,20,30,40];

SChar :=['a','b','c'];

Sdiap:= [1..4];

Empty:=[];              {Пустое множество}

Порядок следования элементов внутри скобок не имеет значения,

число повторений - тоже, храниться будет одно значение. В

качестве элементов множеств в скобки могут включаться константы,

переменные соответствующих базовых типов и выражения с

совпадающим типом результата.

К множествам применимы  операции сравнения =,<>,<=,>=  и

операции, создающие производные множества:

in - проверка вхождения элемента в множество;

+  - объединение множеств;

-  - разность множеств;

*  - пересечение множеств.

Операция изъятия элемента из множества отсутствует.


Битовые поля записей в Ассемблере

До сих пор мы изучали типы данных, размер которых кратен байту.

Но иногда приходится хранить данные, количество элементов

которых велико, а размер одного элемента можно бы задать и

меньше чем 8 битов (размер байта). В качестве примера можно

привести задачу ведения компьютерного журнала успеваемости при

четырехбальной системе оценивания знаний

(неудовлетворительно, удовлетворительно, хорошо, отлично) - для

хранения одной оценки достаточно 2-х битов , а количество

подлежащих хранению оценок по предметам, темам, урокам для

большого количества учеников может оказаться огромным не только

для оперативной памяти, но и для файлового хранения. Эти данные

желательно было бы упаковать более плотно, отводить избыточно по

байту, когда заведомо  используется  только его часть иногда

расточительно.

 Турбо - ассемблер.

В Турбо - ассемблере поддерживается тип  Record, шаблон которого

описывается так:

Имя_шаблона Record

ИмяПоля1: размер в битах = необязательное начальное значение

ИмяПоля2: размер в битах = необязательное начальное значение

...

Описание переменных имеет вид:

ИмяПеременной  ИмяШаблона <список значений полей через запятые>

В области действия описания

Triple RECORD Left:3,Center:4=ofh,Right:2

выражение

Rec Triple <2,1>,<2,0,0>

описывает 2 записи, 1-я из которых имеет имя Rec, занимает 1

слово, в котором поле Right занимает 2 младших бита, поле Left

имеет начальное значение 2, поле Center - начальное значение

ofh, а поле Right - начальное значение 1. Так как адресация

отдельных битов невозможна, обращение к битовым полям происходит

нетипичным способом с использованием операторов MASK и WIDTH и

имен полей записей.

Для выполнения операций на битовых полях принимается, что:

- имя поля есть константа со значением, равным общему размеру

полей, следующих за этим полем;

- выражение MASK ИмяПоля представляет битовую маску, позволяющую

выбрать поле ИмяПоля то есть устанавливает единицы в битахх поля

и нули в остальных битах;

- выражение WIDTH ИмяЗаписи есть константа, равная размеру

записи или поля записи в битах.

Пример операций на битовых полях.

Triple RECORD Left:2,Mid:3,Right:1

Rec Triple <1,5,0>

Выполнение команд

mov al,[Rec]

and al,Mask Mid

mov al,Mid

shr al,cl

приведет к размещению поля Mid записи Rec в младших разрядах

регистра al.


Битовые поля структур в Си

Наиболее компактный способ кодирования совокупности 1-битовых

полей - это представление ее одноразрядными признаками в одно -

или 2 - байтовом целом. Обычно это делается путем определения

множества "масок", соответствующих последовательным разрядам,

например так:

#define KEYWORD 01

#define EXTERNAL 02

#define STATIC  04

(Числа должны быть степенями двойки). В этом случае обращение к

разрядам идет с помощью операций сдвигов, маскирования и

отрицания. Часто появляются в текстах программ некоторые идиомы

вроде

flags |= EXTERNAL | STATIC;

- здесь "включаются" разряды из переменной flags,

соответствующие EXTERNAL  и STATIC. Выражение

flags &= ~(EXTERNAL | STATIC);

"выключает" их, а условие

if ((flags & (EXTERNAL | STATIC)) ==0) ...

истинно, если оба разряда равны 0.

Хотя с такими идиомами освоиться нетрудно, в качестве

альтернативы в Си предоставляется возможность непосредственного,

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

полям. Под  битовым полем подразумевается последовательность

соседних разрядов внутри целочисленного типа, который в свою

очередь "спрятан" в структуре:

struct {

unsigned is_keyword :1;

unsigned is_extern  :1;

unsigned is_static  :1;

}flags;

Здесь определена переменная с именем flags, содержащая 3

однобитовых поля. Число за двоеточием задает длину поля в битах,

поле описывается как unsigned, чтобы подчеркнуть фактическое

отсутствие знака. Поля ведут себя как небольшие беззнаковые

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

приведенные выше выражения могут быть переписаны более

естественным образом:

flags.is_extern = flags.is_static =1; /*установим биты в 1 */

flags.is_extern = flags.is_static =0; /*установим биты в 0 */

if(flags.is_extern ==0 && flags.is_static ==0)...

                                      /*проверим биты */

Поля не могут переходить через границы целого числа; если длина

поля такова, что это должно случиться, то данное поле

выравнивается до границы следующего целого числа.

Именовать поля необязательно; безымянные поля используют просто

как "заполнители". Для принудительного выравнивания к границе

следующего целого используют специальный размер 0.

При работе с битвыми полями необходимо учитывать , что на одних

машинах поля размещаются в слове слева направо, а на других -

справа налево в зависимости от особенностей аппаратуры и за этим

необходимо следить, если данные готовятся вне программы.

Очевидные ограничения - битовые поля не могут быть элементами

массивов, они не имеют адресов и к ним нельзя применить операцию

&.


Управляющие структуры в Ассемблере

Мы уже обсуждали управляющие структуры при освоении словесных

описаний алгоритмов и блок - схем. К управляющим структурам,

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

выполнение одного или группы операторов (ветвление) и

циклическое (повторяющееся ) выполнение одного или группы

операторов.

Различают двух и многоальтернативные ветвления, их синтаксис

будет рассмотрен для конкретных исполнителей.

Различают также несколько видов циклов:

-циклы с предусловием, работающие по схеме

ПОКА УсловиеВыполняется ВыполнятьБлокОператоров

-циклы с постусловием, реализующие схему

Выполнять БлокОператоров ПОКА УсловиеВыполняется

Отличия их очевидны: цикл с предусловием может не выполниться ни

разу, если не выполняется предварительно проверяемое условие;

цикл с постусловием всегда выполнится хотя бы 1 раз, т к

проверка условия вынесена в конец цикла.

-бесконечные циклы, в которых условие прекращения повторения

блока операторов формируется непосредственно в блоке операторов,

а выход из цикла осуществляется с помощью специальных операторов

(break в Си, goto в Паскале и Бейсике, jmp в ассемблере); таким

образом бесконечный цикл реализует схему:

ВЫПОЛНЯТЬ_БЕСКОНЕЧНО БлокОператоров

-циклы с фиксированным количеством итераций, представляют собой

 частный случай циклов с предусловием и дополнительном

 формировании счетчика "оборотов"; работают они так: в

 специальную переменную-счетчик заносится некоторое

 начальное положительное или отрицательное значение,

 сравнивается с заданным числом и принимается решение о

 выполнении блока операторов; после каждого прохода блока

 операторов это число уменьшается (или увеличивается) на

 заданный шаг.  Выход из цикла осуществляется, когда счетчик

 станет больше (или меньше ) заданного числа. Циклы этого типа в

 различных инструментальных системах имеют различные

 модификации схем функционирования.

.Условные операторы в ассемблере.  Условные

 операторы в ассемблере "вмонтированы" в команды условных

 переходов.  Все команды условного  перехода  имеют  вид Jxx

 disp, где disp - 8-разрядное  смещение  в  дополнительном коде.

 Команда проверяет  определенное  условие  и,  если  оно верно,

 изменяет значение счетчика команд  IP  на  заданное смещение.

 Длина смещения позволяет передать  управление  в диапазоне  от

 -128 до  +127 байтов от текущей команды.  Условием перехода

 служит определенная комбинация  флагов условий,  задаваемая

 мнемоникой команды Jxx.  Сама команда перехода  флаги условий

 не изменяет.  МП 8086/8088 имеют следующие команды условного

 перехода:

 ----------------------------------------------------------------

 Мнемоника Синонимы Условие             Назначение

 команды            перехода

 ----------------------------------------------------------------

 JZ  dest  JE       ZF=1          Перейти, если нуль (равно)

 JNZ dest  JNE      ZF=0          Перейти, если не нуль (не равно)

 JS  dest           SF=1          Перейти, если знак минус

 JNS dest           SF=0          Перейти, если знак плюс

 JO  dest           OF=1          Перейти, если переполнение

 JNO dest           OF=0          Перейти, если нет переполнения

 JP  dest  JPE      PF=1          Перейти, если четный паритет

 JNP dest  JPO      PF=0          Перейти, если нечетный паритет

 JB  dest  JNAE,JC  CF=1          Перейти, если ниже

 JNB dest  JAE,JNC  CF=0          Перейти, если выше или равно

 JA  dest  JNBE     CF=0 & ZF=0   Перейти, если выше

 JNA dest  JBE      CF=1 | ZF=1   Перейти, если ниже или равно

 JL  dest  JNGE     SF<>OF        Перейти, если меньше

 JNL dest  JGE      SF=OF         Перейти, если больше или равно

 JG  dest  JNLE     ZF=0 & SF=OF  Перейти, если больше

 JNG dest  JLE      ZF=1 | SF<>OF Перейти, если меньше или равно

 JCXZ dest          CX=0          Перейти, если CX равно нулю

 ----------------------------------------------------------------

      Команды JB, JNB, JA, JNA обеспечивают переход после сравне-

 ния двух целых чисел без знака. Команды JL, JNL, JG, JNG обеспе-

 чивают переход  после сравнения целых  чисел со знаком.  Команда

 JCXZ передает управление, если CX  = 0, и используется совместно

 с командами циклов

 Циклы в ассемблере.

      Все  конанды  циклов  имеют  вид  LOOPxx  disp,  где disp -

 8-разрядное  смещение  в   дополнительном  коде.  Команда  цикла

 уменьшает  регистр CX  на 1,  и если  выполнено связанное  с ней

 условие,  заменяет  значение  IP  на  заданное  смещение:

      CX <- CX-1

      if (условие) JMP dest

      Длина смещения позволяет передать управление в диапазоне от

 -128 до +127 байтов от текущей команды. Условием перехода служит

 определенная  комбинация флагов  условий, задаваемая  мнемоникой

 команды LOOPxx. Сама команда цикла флаги условий не изменяет. МП

 8086/8088 имеют следующие команды циклов:

 ----------------------------------------------------------------

 Мнемоника   Синонимы Условие             Назначение

 команды              цикла

 ----------------------------------------------------------------

 LOOP   dest          CX<>0        Зациклить

 LOOPZ  dest  LOOPE   ZF=1 & CX<>0 Цикл, пока нуль (равно)

 LOOPNZ dest  LOOPNE  ZF=0 & CX=0  Цикл, пока не нуль (не равно)

 ----------------------------------------------------------------

      Из  таблицы   видно,  что  эти   команды  позволяют  удобно

 реализовывать циклы с постусловием вида

      MOV  CX,cnt    ; счетчик цикла

 Start:

      .  .  .

      LOOP Start

      Следующий пример  ищет в ASCII-строке str  длины len символ

 пробела;  если  символ  не  найден,  производит  переход к метке

 Not_Found.

      MOV  CX,L

      JCXZ Not_Found

      LEA  SI,str-1

      MOV  AL,20h

 Next_Chr:

      INC  SI

      CMP  AL,[SI]

      LOOPNE Next_Chr

      JNZ  Not_Found


Управляющие структуры в Си

Условные операторы в Си.

Операторы ветвления выбирают в  программе из группы альтернатив

возможное продолжение вычислительного процесса. Выбор выполняет

ся по значению заданного выражения. В Си 2 оператора ветвления:

-2-х альтернативный if(условие)...else

-многоальтернативный переключатель Switch.

Оператор if имеет следующую общую форму записи:

               if(выражение)  блок продолжения по истине

               else          блок продолжения по ложь

 При выполнении  оператора if сначала вычисляется логическое

 выражение в скобках; если результат истина (не нуль!),

 выполняется стоящий вслед за скобками блок операторов. Если

 результат ложь (равен о), то блок истины пропускается ; если

 присутствует ключевое слово else (иначе), то  выполняется блок

 ЛЖИ. Блоки истинности и ложности сами могут быть операторами

 if, образуя вложенные ветвления. Компилятор интерпретирует

 вложенные if , сопоставляя каждое слово else с последним перед

 ним  словом if, не  имеющим  своего else. Соответствие ищется в

 пределах блока , в которой заключен if. Bнутренние и внешние

 блоки при этом не рассматриваются.Если соответствия не найдено,

 считается что if не имеет ветки else.

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

 скобки, то выполняется только один оператор, следующий за if

 или else.

       Пример:                                То же самое:

      int a,b;                              int a,b;

      if (a>o)                              if(b>o)

      a=1;                                   a=1;

      else

      if(b==o)                                 if(b==o)

      a=o;                                     a=o;

      else a=-1;                            else a=-1.

Ошибочной будет следующая конструкция:

      int x=1, y=1;

      if (x==1)

      if(y!=1) puts("x равно 1, у не равно 1");

      else puts("x не равно 1");

 'X' не равно 1'' отпечатается,когда X равно 1.Ошибка может быть

 исправлена использованием фигурных скобок:

  if(x==1)

  { if(y!=1) puts("x=1 и у=1");}

  else puts ("x!=1");

 Операция условия ?:является частным случаем оператора

 if...else. Ее вид:

     Логическое выражение? Блок истины:Блок лжи

 Пример:

 int  a=4,b=3,c;

  c=a>b?  а+а+b*b:0;  /*c=a*a+b*b=25*/

 Фрагмент if(выражение) x=y; else x=y+4;

 эквивалентен х=(выражение) ? у:у+4, что получается короче.

Операторы switch и break.

Выбор одного варианта из многих возможных можно осуществить с

помощью многократно вложеных if( )...else, но более удобно

использовать оператор переключения switch, общий вид

которого следующий:

               switch (логическое выражение)

               { case константа 1:блок 1;[break;]

               ...

               case константа i:блок i;[break;]

               ...

               case константа n: блок n;[break;]

               [default:блок n+1;]

               }

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

его должен быть одним из целых. Это выражение поочередно

сравнивается с каждым константным выражением в  блоке  и

при  совпадении значений  выполняется следующий блок, и если

присутствует break, то осуществляется выход из switch. В

противном случае работа  продолжается до встречи с break

или до конца блока switch.

 Пример. Программа  выводит меню из трех функций sin, cos, Atan

 и по первой букве ввода пользователь  получает  информацию

 об  этой функции:

#include <stdio.h>

void main(void)

{puts("Введите первую букву интересующей Вас функции:\n

S-sin\n    C-cos\n   A-Atan\n");

switch(getchar()){

case'S':case's':

puts("Вычисление синуса, аргумент в радианах''); break;

case'C':case'c':

puts("Вычисление косинуса угла в радиаанах");break;

case'a':case'A':puts(''Вычисление арктангенса")break;

default: puts("Ошибка,повторить ввод буквы")

         }

         }

Циклы в Си.

В языке Си есть удобные операторы циклов.Всего их три:

1) Цикл с предусловием while(логич.выраж.) блок операторов;

2) Цикл с постусловием do блок операторов while(логич.выраж.);

3)for(1-я секция;логич,выраж.;2-я секция);

 Оператор while  организует повторение блока операторов,пока

 логическое выражение не станет  ЛОЖЬ (0).Это  цикл  с

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

 проверяется перед входом в блок (тело циккла). Если необходимо

 обеспечить  выполнение  блока хотя  бы раз использует цикл с

 постусловием do...while(лог выраж) - сначала выполняется блок ,

 а затем проверяется истинность условия.  Повторение

 продолжается ,пока выражение в скобках после while не примет

 значение ЛОЖЬ (0).

Наиболее сложная и универсальная форма цикла - оператор for.

В его 1-й секции, заканчивающейся точкой с запятой, могут быть

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

тело цикла операторы присвоений -в принципе они могут

размещаться и до заголовка цикла, тогда 1-я секция будет

отсутствовать, вернее будет представлена только знаком ';'.

2-я секция полностью аналогична условию в цикле while

В 3-й секции размещаются через запятую те простые операции над

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

прохода тела цикла.

Выполнение осуществляется в следующей последовательности: если

1-я секция присутствует, то выполняются записанные в ней

операции; если 2-я секция присутствует, то вычислятся

содержащееся в ней логическое выражение и при его истинности

начинается выполнение блока операторов в теле цикла - если при

этом встретится оператор break, то произойдет прекращение

итераций и принудительный переход на следующий за циклом

оператор, а при встрече с оператором continue осуществляется

принудительный досрочный переход к выполнению секции 3.

После завершения блока операторов в теле цикла или выполнения

оператора continue выполняются операциив секции 3, после чего

осуществляется переход к выполнению секции 2 и т д.

Благодаря эти свойствам цикл for универсален и может

использоваться вместо любого типа циклов. Например вместо цикла

while он записывается просто:

for(;Условие;)  {Блок операторов}

Для замены цикла do {Оператор }while(Условие);

цикл for записывается так:

for(Оператор;Условие;) Оператор

Бесконечный цикл моделируется циклом for так:

for(;;) {...if(Условие) break;...}

Для замены цикла со счетчиком

for(Счетчик=Значение; Условие; Наращивание счетчика с

произвольным шагом ) { Блок операторов}

При использовании циклов необходимо следить,чтобы не  происходило

бесконечного зацикливания.

Примеры цикла while.

Программа подсчитывает число русских гласных в слове,введенном  с

клавиатуры и выводит результат на экран.

#include<stdio.h>

void main(void>)

{int total=0,rus=0;char ch;

 while(ch=getchar()!="\n")

 {total++;

  switch(ch){

  case'a':case'A':case'E':case'e':case'u':

  case'И':case'O':case'o':case'У':case'y':

  case'Э':case'э':case'Ю':case'ю':case'Я':

  case'я':

  rus++;}

        }

         printf("Введено %d символов,из них %d\n

               русских букв\n",total,rus);

        }

Пример цикла do...while.

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

               "Продолжаете? (Да\Нет). Esс-отмена выбора"

При вводе 'д'печатается "Да",при вводе "н"-"Нет".При нажатии

Esс выводится  "отмена  выбора".По  другим клавишам-звуковой

сигнал.В программе используется библиотечная функция

bioskey(), воззвращающая СКЕН - код клавиши.  Для

независимости от регистра клавиатуры расширено число меток

для ветвей"Да"и"Нет".

#include<stdio.h>

#include<bios.h>

#define YES 0

#define NO 1

#define ESC 27

int main(void) {

char ch;

puts("Продолжаете? (Д/Н).ESС-отмена");

do{ch=bioskey(0);

switch(ch){

case'д':case'Д':case'L':case'l':

puts("Да");return(YES);

case'н':case'H':case'y':case'Y':

puts("НЕТ");return(NO);

case ESC:break;

default:putchar('\a';

}

while(ch!=ESC);

}

puts("Отмена выбора"); return(ESC);

            }

Оператор  continue  может использоваться только в теле

циклов,он передает управление:

а)для цикла for на секцию 3;

б)для циклов while и do - while на вычисление логического

выражения

Оператор return обеспечивает выход из текущей функции в

точку ее вызова(возврат).


Управляющие структуры в Турбо Паскале

Условные операторы в Паскале.

Условный оператор в Паскале имеет следующую структуру:

if Условие then БлокОператоров1 else БлокОператоров2;

и, как и в других языках, служит для организации процесса

вычислений в зависимости от какого - либо логического условия.

Например:

if x>5 then

begin x:=x+5;y:=1 end

else y:=-1;

В Паскале между ключевыми словами не ставятся точки с запятой,

но в конце всего оператора она конечно обязательна, чтобы

отделить условный оператор от следующих за ним по тексту.

Альтернативная ветвь else может отсутствовать, в таком операторе

при невыполнении условия ничего не делается, а осуществляется

переход к следующему оператору.

Условные операторы могут быть вложенными друг в друга - при этом

важно не запутаться в вариантах сочетаний условий, надо помнить,

что альтернатива else всегда принадлежит ближайшему if без else.

Так в фрагменте

if Условие1 then if Условие2 then Оператор1 else Оператор2;

Оператор2 относится к условию2 и если необходимо отнести его к

условию1, то следует использовать скобочные слова begin - end:

if Условие1 then

begin if Условие2 then Оператор1 end

else Оператор2;

Многовариантный оператор ветвления в Паскале имеет структуру:

CASE УправляющаяПеременнаяИлиВыражение  OF

СписокЗначений1 : БлокОператоров1;

...

СписокЗначенийN : БлокОператоровN

ELSE АльтернативныйБлокОператоров

END;

Тип управляющей переменной или значение выражения может быть

только перечислимым, диапазоном или целочисленным Byte,

ShortInt, Integer, Word. Ветвь ELSE - необязатльна.

CASE Err OF

0        : Writeln('Нормальное завершение');

2,4,6    : Writeln('Ошибка выполнения файловых операций');

7..99    : Writeln('Ошибка кодирования')

ELSE       Writeln('Неопознанная ошибка')

END;

Циклы в Паскале.

Цикл с предусловием в Паскале:

WHILE Условие DO БлокОператоров

Цикл выполняется , пока условие истинно.

Пример:

VAR Factorial,N :Integer;

begin Factjrial:=1; N:=1;

WHILE N<=10 DO begin

Factorial:=Factorial*N; N:=N+1

               end;

Writeln(Factorial);

end.

Цикл с постусловием:

REPEAT БлокОператоров UNTIL Условие

Цикл REPEAT - UNTIL выполняется в Паскале, пока условие ложно

Если в качестве условия поставить логическую константу False,

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

только оператором выхода из подпрограммы Exit или оператором

завершения программы в целом Halt. В 7-й версии Паскаля

появились подпрограммы выхода из циклов Break и

досрочного перехода на проверку условия Continue, аналогичные

соответствующим операторам языка Си.

Цикл FOR ... DO в Паскале имеет ограниченные возможности

простого цикла с параметром - счетчиком со следующим

синтаксисом:

FOR Параметр:= МладшееЗнач  TO СтаршееЗнач DO БлокОператоров;

или

FOR Параметр:= СтаршееЗнач DOWNTO МладшееЗнач DO БлокОператоров;

Шаг наращивания счетчика в Паскале "нерегулируемый" и равен +1

при TO или -1 при DOWNTO. Циклы FOR с вещественными параметрами

в Паскале невозможны - их приходится организовывать через цикл

while.


Управляющие структуры в QBASIC

Условные операторы в Бейсике имеют следующий формат

IF Условие THEN

Операторы

ELSE

Оператороы

END IF

Для многоальтернативного выбора в QBASIC используется оператор

выбора по значению управляющей переменной:

SELECT CASE Переменная

CASE Значение | Список значений | Диапазон значений | Операция

сравнения

Операторы

CASE Значение | Список значений | Диапазон значений | Операция

сравнения

...

CASE ELSE

Операторы

END SELECT

Циклы в Бейсике.

В QBASIC поддерживаются следующие виды циклов:

WHILE Условие

Операторы

WEND

Этот цикл позволяет повторять заключенные в его теле операторы

до тех пор, пока выполняется Условие, которое очевидно должно

когда нибудь измениться в результате работы внутрицикловых

операторов - иначе вы никогда не выйдете из цикла. Если Условие

не выполняется с самого начала, то вход в тело цикла не будет

осуществлен.

DO

Операторы

[EXIT DO] 'Для преждевременного прерывания цикла

LOOP [{WHILE | UNTIL} Условие]

Ключевое слово WHILE заставляет цикл выполняться пока Условие

соблюдается, а слово UNTIL - наоборот, пока условие не

соблюдатся. Если после LOOP отсутствуют оба ключевых слова, то

выход из цикла возможен только по EXIT DO.

Вторая модификация этого цикла размещает проверку условия при

входе в цикл:

DO [{WHILE | UNTIL}] Условие

Операторы

[EXIT DO]

LOOP

Этот цикл с предусловием может не выполниться ни разу, если

Условие этого не позволяет.

FOR Счетчик=Нач Знач TO Кон Знач [STEP Шаг]

Операторы

[EXIT FOR]

NEXT [Счетчик]

Шаг цикла может быть положительным или отрицательным.


Подпрограммы в Ассемблере

Подпрограммы - это языковая реализация вспомогательных

алгоритмов, на которые как правило распадается решение общей

задачи в процедурном программировании. В самом деле, даже

решение достаточно узкого класса задач на компьютере, например,

вычисление числа Фибоначчи с произвольным номером, потребует от

нас выполнения ряда вспомогательных алгоритмов.

В частности основная программа должна узнать, какой номер числа

Фибоначчи интересует пользователя нашей программы. Можно

ориентировать нашу программу на диалог с пользователем по этому

поводу, то есть вывести на экран запрос типа : "Введите номер

интересующего вас числа Фибоначчи" и принимать ввод пользователя

с запоминанием введенного номераа. Можно ориентировать программу

на ввод пользователем номера числа сразу после имени программы в

командной строке. В первом случае нам понадобится подпрограмма

(вспомогательный алгоритм) диалога, во втором - подпрограмма

извлечения заданного номера из префикса программного сегмента.

В свою очередь подпрограмме диалога могут понадобиться

вспомогательные алгоритмы сохранения, очистки, восстановления

содержимого экрана, чтобы не вести экранный ввод - вывод на фоне

предыдущих записей, алгоритмы установки курсора для

эстетического оформления вопросов и ответов в диалоге и т д.

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

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

более простые действия. Конечно можно составить весь алгоритм

без явного выделения вспомогательных действий, но такую

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

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

совершенствовать.

Другой причиной необходимости подпрограмм в алгоритмических

языках является наличие в разных программах большого количества

однотипных, шаблонных действий - прием ввода пользователя с

клавиатуры, вывод строк на экран или печатающее устройство,

сортировки чисел по возрастанию в числовых массивах или

сортировка списков фамилий по алфавиту, различные алгоритмы

поиска необходимых данных в массивах - этот перечень

стереотипных работ можно продолжать долго.  Совершенно очевидна

целесообразность однократного составления таких шаблонных, часто

употребляемых вспомогательных алгоритмов, хранение их на диске и

последующее использование в самых разнообразных задачах.

Но тогда программа должна будет состоять из главной подпрограммы

(она нужна для определения начала и конца общего алгоритма),

которая что-то делает сама, а что-то поручает вызываемым на

выполнение вспомогательным подпрограммам, которые в свою очередь

делают что-то сами и возможно вызывают на выполнение другие

вспомогательные подпрограммы и т д. Каждая вспомогательная

подпрограмма должна быть достаточно универсальной, т е выполнять

свою рпаботу для различных вариантов данных, а следовательно при

ее вызове на выполнение ей необходимо будет передать данные или

их адреса для обработки. После завершения подпрограмма должна

возвратить управление в точку вызова - точнее на оператор,

следующий за вызовом подпрограммы. Поэтому механизм работы с

подпрограммами требует запоминания точки ухода в подпрограмму и

возврата из нее.

15.1. Подпрограммы в ассемблере.

Ассемблерные подпограммы носят название процедур и размещаются в

теле кодового сегмента - там может быть только одна главная

процедура, но как правило присутствуют несколько других,

вспомогательных. Главная процедура оформляется в следующем виде

         BEGIN  PROC FAR

         ...

         BEGIN ENDP

Здесь FAR информирует операционную систему о том, что данный

адрес есть точка входа для начала выполнения программы в целом,

а директива ENDP определяет конец программы.

Типичная организация многопроцедурной программы имеет вид:

                  +-------------------------+

                  ¦  CODESG  SEGMENT  PARA  ¦

                  +-------------------------¦

                  ¦  BEGIN   PROC     FAR   ¦

                  ¦           .             ¦

                  ¦           .             ¦

                  ¦          CALL     B10   ¦

                  ¦          CALL     C10   ¦

                  ¦          RET            ¦

                  ¦  BEGIN   ENDP           ¦

                  +-------------------------¦

                  ¦  B10     PROC     NEAR  ¦

                  ¦           .             ¦

                  ¦           .             ¦

                  ¦          RET            ¦

                  ¦  B10     ENDP           ¦

                  +-------------------------¦

                  ¦  C10     PROC     NEAR  ¦

                  ¦           .             ¦

                  ¦           .             ¦

                  ¦          RET            ¦

                  ¦  C10     ENDP           ¦

                  +-------------------------¦

                  ¦  CODESG  ENDS           ¦

                  ¦          END      BEGIN ¦

                  +-------------------------+

Здесь надо обратить внимание на следующие особенности:

- Директивы PROC по меткам В10 и С10 имеют операнд NEAR для

указания того, что эти процедуры размещены внутри текущего

кодового сегмента - этот операнд можно опускать, т к ассемблер

по умолчанию принимает NEAR.

-Каждая процедура должна иметь уникальное имя и собственную

директиву окончания ENDP;

-Для передачи управления во вспомогательные процедуры

используются команды CALL B10 и CALL C10. Действуют команды

аналогично - начинается последовательное выполнение операторов

внутри процедуры, а достигнув команды RET управление

возвращается на команду, следующую за CALL.

-Команда RET всегда осуществляет возврат в вызывающую программу

или процедуру. Если бы в процедуре В10 не было команды RET, то

следом за ней началось бы выполнение команд в лежащей следом в

памяти процедуре C10, а если бы команда CALL отсутствовала в

процедуре С10, то лежащие за процедурой С10 ячейки памяти

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

последствиями.

-Чисто технически управление в NEAR - процедуру можно передать с

помощью команд безусловного или условных переходов, но лучше

использовать команду вызова CALL и команду возврата из процедуры

RET.


Подпрограммы в Си

Они носят название "функции" - других типов подпрограмм в Си

нет.Любая программа на Си состоит из одной или нескольких функций.

Функция-это логически самостоятельная именованная

часть программы,которой могут передаваться параметры и которая

может возвращать  значение.Стандарт ANSI языка СИ предполагает

следующмй формат определения функции:

 [возвр тип данных] имя_функции (список аргументов | void)

 {

 описание локальных данных

 операторы, в т ч вызова других функций

 [return] (выражение)

 }

 Совокупность предложений в фигурных скобках-тело функции.  По

 синтаксису  языка Си нельзя в теле одной функции определять

 другую-т е не допускается вложенное определение функций.  Если

 поле  "возвр  тип" опущено,считается,что возвращаемый тип

 -целый,а при слове VOID -функция не возвращает значения.  Поле

 "имя  функции"-это  особый  тип  указателя на функцию; его

 значение - адрес входной точки функции. В поле  "список

 параметров" - любая комбинация типов и имен, разделенных

 запятой и составляющих список формальных параметров.  При

 каждом вызове функции   происходит  замена формальных

 параметров значениями фактических с размещением их в стеке.

 Список формальных параметров может быть и пустым или содержать

 слово void.

 Стандарт ANSI требует для функций, возвращющих  не  целое

 значение предварительного  объявления, называемого   прототипом

 функции:

 int func1(int,char *,unsigned);

 char * func2(int var1,int var2);

В прототипах функций можно указывать только типы формальных

параметров без имен переменных, так как прототип содержит

только заголовок функции без тела - это только объявление,

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

составления определения функции и вызовов ее на выполнение.

Общеупотребительная практика -помещение прототипов в

заголовочный файл с последующим его подключением директивой

include.  Функция в процессе выполнения извлекает копии

аргументов  из стека. При передаче параметров в функцию (в стек)

выполняется дополнительно преобразование их к типу,который

указан в прототипе.  Существует 2 способа передачи параметров:

 -передача значений;

 -передача адресов переменных.

Вызов функции  с передачей адресов позволяет строить функции с

доступом к массивам и другим протяженным структурам данных.

В Си допускается наличие функций с переменным чилом аргументов-

признаком такой функции является многоточие в списке

параметров прототипа. Такая функция должна иметь способ

определения точного  числа  параметров при каждом  вызове - или

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

последний параметр должен быть индикатором конца списка  т  е

иметь обусловленное значение, которое  не  может  принимать  ни

один параметр. Функции с переменным числом аргументов  должны

иметь хотя бы 1 фиксированный параметр, чтобы было к чему

привязаться при извлечении их из стека.

Для упрощения работы с переменным числом аргументов в файле

<stdarg.h> определен особый тип

                    typedef void  *va_list;

 который и используется для описания указателя на начало списка

 фактических аргументов.

 Для задания   начального   значения   указателю  типа  va_list

 используется макро va_start:

 void examp(int arg1,...)

 {

 va_list ptr;

 va_start(ptr,arg1);

 }

 Доступ к  значениям в списке фактических параметров выполняется

 через макро va_arg():

    #include <stdio.h>

    #include <stdarg.h>

    void example(int,...);

    #define EOL 0        /*ограничитель списка параметров*/

    void main(void)

 {int var1=5,var2=6,var3=7,var4=8,var5=9;

 example(var1,EOL);

 example(var1,var2,EOL);

 example(var1,var2,var3,var4,var5,EOL);

 }

 void example(int arg1,...)

 {int number=1,value;

 va_list ptr;

 printf("%d",arg1);

 va_start(ptr,arg1);

 while((value=va_arg(ptr,int)!=EOL)

 {printf("%d",value);number++;}

 printf("\nФ-ции передано :в аргументов\n",number);

 }

 Си поддерживает рекурсивные вызовы функций:

 #include <stdio.h>

 double fact(double);

 void main(void)

 {double number,result;

 puts("Введите число");

 scanf("%lf",number);

 result=fact(number);

 printf("%15.0lf\n",result);

 }

 double fact(double r)

 {if(r<=1)  return(1.0);

 else return (r*fact(r-1.))

 }

 Преимуществом рекурсии есть компактный код,а недостатки-потери

 времени на вызовы и  возможность  переполнения стека.

 Передача параметров в главную функцию main()

 Для доступа к аргументам функции main() ее необходимо описать в

 одной из следующих форм:

  [тип] main(int argc,char **argv)

  { ....}

 [тип] main(int argc,char **argv,char ** env)

 {...}

 В первом случае ф-ция будет иметь доступ к словам командной

 строки,а во втором - еще и к строкам среды программы.

 argc-число слов в командной строке;

 **argv-указатель на массив указателей из argc элементов;

 при этом argv[0]-спецификация запускаемого на выполнение файла;

 **env указатель на массив указателей из переменного  числа

 аргументов (массив завершается нулевым указателем, а каждый

 его элемент указывает на строку среды программы)

Функция main() может возвращать значение-только  в 1-ом  байте

через операционную  систему  с помощью  опратора return.

Дополнительно передать значение в операционную систему можно  с

помощью библиотечной функции exit().

Указатели на функции.Модификаторы near,far,huge

Синтаксис Си позволяет применять указатели для вызова функций.

Имя функции в Си - это  указатель - константа на функцию, равный

адресу  точки  входа  функции. Помимо константных возможно также

описание указателей - переменных на функции:

               Возвр_тип (* имя_ук) (список_арг);

Указатели на функции используются в следующих случаях:

1)Многие библиотечные  функции  в  качестве  аргумента  получают

укзатель на функцию. Например функция сортировки qsort()

получает 4-м аргументом   указатель   на    составляемую

пользователем функцию сравнения сортируемых элементов

2)Использование указателей на функции в качестве аргументов

позволяет разрабатывать  универсальные  функции - например

численного решения уравнений, интегрирования и дифференцирования

3)Указатели на функции могут использоваться для косвенного

вызова резидентных программ, точка входа в  которые  записана  в

известное место памяти, например по одному  из неиспользуемых

векторов прерываний

В качестве  примера приведем программу,которая  для  доступа к

функциям dif() и sum() использующих указатель на функцию:

#include<stdio.h>

int dif(int,int);

int sum(int,int);

void main(void)

{int(*f_ptr)(int,int); int var1=20,var2=5,ret;

f_ptr=dif; ret=f_ptr(var1,var2);

printf("ret=%d\n",ret);

f_ptr=sum; ret=f_ptr(var1,var2);

printf("ret=%d\n",ret);

}

int dif(int a,int b) {return(a-b);}

int sum(unt a, int b){return(a+b;}

Как и обычные переменные, указатели на функцию могут

объединяться в массивы. Описать и инициализировать  массив

указателей на функции можно так:

int cmp_year(const void*,const void*);

int cmp_price(const void*,const void*);

int cmp_title(const void*,const void*);

int cmp_name(const void*,const void*);

int cmp_tot(const void*,const void*);

int(*fcmp[5])()={cmp_name,cmp_title,cmp_year,cmp_price,cmp_tot};

Доступ к элементам массива -как обычно:

int index=0;

fcmp[index](ptr1,ptr2);

Меняя значение индекса можно вызывать другие функции-например  в

цикле.

Указатели на функции тоже могут иметь тип near,far или huge.

Модификаторы типа функции должны совпадать у прототипа и

определения:

char far  *far  func(void)-это far-функция,возвращающая

far-указатель на тип char.

Тесно связано с функциями понятие видимости переменных и функций

Классы памяти и видимость перемменных

Для установления связей между именами переменных  и

соответствующими им объектами в памяти компилятору необходимо

сообщить  как  минимум  2  сведения:  тип  и  класс хранения.

Тип определяет размер памяти под переменные,а также способ

интерпретации хранимых в памяти чисел.  Класс хранения

определяет место,где  будет расположен объект (регистры

процессора, сегммент данных программы или сегмент стека) и

одновременно время жизни объекта (все  время выполнения

программы или  только  на  время выполнения  ее определенной

части). Класс хранения задается  либо  явно,либо косвенно, когда

компилятор определяет его сам по расположению описания в

программе.С точки зрения времени жизни  различают 3 типа

переменных: статические, локальные, динамические. С понятием

"класс хранения" тесно связано  и  понятие "видимости" или

области определения идентификатора (имени).  Область

определения -  это та часть  программы, в  пределах которой

идентификатор доступен для обработки скрытой под ним информации.

Есть  несколько  типов  областей   определения (видимости)

- в пределах  блока  (локальная  видимость);

- в пределах функции;

- в пределах файла .

При  необходимости  сделать идентификатор   видимым   явно

используется объявление его с ключевым словом extern .

Оласть определения и видимость.

Текст  программы ( функция MAIN() и все  остальные  функции)

может  быть  размещен  целиком   в   одном текстовом файле . Это

не всегда удобно а иногда невозможно. Тогда текст программы

размещается в рескольких  текстовым  файлах. Для  объединения

они компонуются совместно. Информация о  всех  объединяемых

файлах помещается   в  файл  проекта. Для  каждого  исходного

текстового файла порождается свой объктный файл, а затем  они

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

Объъектный файл- не готовый к выполнению программный код. Для

обеспечения  работоспособности  отдельные  объектные  модули

должны  быть  связаны  перекрестными   ссылками. Эту   работу

выполняет  компоновщик, а  необходимая  для  него  информация

готовится либо программистом  при  явном  указании  классов

хранения, либо компилятором по размещению объявлений в тексте

программы.

Если идентификатор описан внутри  блока  {  }, он  имеет

локальную область определений, ограниченую  размерами

блока. Отсюда  следует, что  все переменные, описанные внутри

функций, в том числе и формальные параметры - локальные.

Если  идентификатор  описан  вне  каких-либо  блоков, он

является глобальным, его  область  определения  начинается  в

месте  описания  и   продолжается до конца файла. Чтобы

идентификатор был видимым и  выше  точки  описания  или  из

других файлов используется атрибут extern:

          extern int varl;

Внешние  (extern)  переменные  нельзя  инициализировать

(присваивать значения) в месте описания.

Сначала компоновщик, встретив объект extern  разыскивает

соответствующий  ему  глобальный  идентификатор  в   текущем

файле, если не  находит - то просматривает  глобальные  данные

других файлов проекта,если нет и там - поиск  продолжается  в

библиотеках.

Внешние  переменные - один  из  способов  передачи

функциям  аргумента  и  возврата  из   функций   необходимых

значений.Взаимодействующие функции используют в этом  случае

общие области памяти. Это экономичнее и быстрее, чем  передача

фактических  параметров  через  стек, но  чревато  опасностью

неожидаемого изменения содержимого общих ячеек памяти.

                 Пример:

     FILE1.C                            FILE2.C

   #include <stdio.h>                  #include <stdio.h>

     extern int i;                      extern int i;

     void main(void)                    void other(void)

     { void next(void);/*прототип*/     {i++;

      i++;                              printf("B other i=

     printf("B main i=%d\n",i);                  %d\n",i);

     next( );                           }

     }

     int i=3;

     void next(void)

     { void other(void);/*прототип*/

     i++;

     printf("B next i=%d\n",i);

     other( );

     }

Автоматические переменные.Классы хранения Auto,register.

Автоматические переменные - подкласс локальных, они  могут

распологаться в  стеке  или  регистрах  процессора. Время  их

существования - пока работает блок, внутри которого они

описаны. Скалярные автоматические переменные могут быть

инициализированы программистом в месте их описания. Структурные

переменные  могут   инициализироваться только в Турбо Си (с

риском нарушения мобильности полученного кода). Безопаснее такие

структурные переменные описывать  как внешние.

Частным  случаем  автоматических  переменных   являются

регистровые - объявленные ключевым словом  register (переменные

некоторых целых типов и указатели). Компилятор будет  хранить

их в регистрах, если есть свободные, а если нет - в  стеке, как

обычные  автоматические. Использование  register   переменных

позволяет ускорить работу программы.

Пример:

Алгоритм получения простых чисел "Решето Эратосфена",

реализованный с помощью регистровых переменных.

  #include <stdio.h>

  #define size 8190

  int flags[size];

  unsigned char bell=7;

  main( )

  { int prime,iter,count;

   register int i,k;

   printf("%c",bell);

   for(iter=1;iter<=10;iter++)

   {

    count=0;

   for(i=0;i<=size;i++) flags[i]=1;

   for(i=0;i<=size;i++)

   { if(flags[i]==1)

   {prime=i+i+3;

    k=i+prime;

    while(k<=size)

    {

     flags[k]=0;

     k+=prime;

     }

     count+=1;

     }

     }

     }

     printf("%d Простые числа",count);

   }

Статические переменные-класс static.

Статические  объекты  существуют  все  время   выполнения

программы. Память под статистические  переменные  выделяется  в

сегменте   данных. По умолчанию статистическими являются

строковые литералы и все глобальные переменные.

 Но при явном объявлении со словом static  любой  объект, в

том числе локальный, может быть отнесен к статическому.

Компилятор  заполнит  их  нулевыми  байтами   или кодами

начальной инициализации.

Локальные статические объекты  все  равно  не  видимы  из

других функций, хотя имеют статическое время жизни.

Спецификатор static может использоваться  не  только  для

данных, но и для функций - статические функции невидимы  для

загрузчика т е недоступны из других файлов.

Класс volatile.

Переменные  этого  класса  могут   меняться   не   только

программой, но и вне программы, например, при обработке

прерывания. В регистровый класс они не переводятся.

Динамические переменные - для них есть специальные  функции

создания и разрушения - это функции динамического  распределения

памяти вне программных сегментов (в так называемой "Куче").


Подпрограммы в Турбо Паскале

В Паскале поддерживается 2 вида подпрограмм - процедуры и

функции.

Определение процедуры начинается с ключевого слова PROCEDURE,

за которым следует ее имя со списком параметров в круглых

скобках, если таковые необходимо передавать процедуре при ее

вызове на выполнение. После такого заголовка следуют при

необходимости принятые в Паскале разделы объявлений LABEL,

CONST, TYPE, VAR и далее тело процедуры, обрамленное словами

begin и end;

PROCEDURE ИмяПроцедуры(Парам1 : ТипПарам1;

                       Парам2 : ТипПарам2;

                   VAR Парам3 : ТипПарам3;

                   VAR Парам4 : ТипПарам4);

LABEL

    Перечисление локальных меток процедуры

CONST

    Описание локальных констант

TYPE

    Описание локальных типов

VAR

    Описание локальных переменных и вложенных подпрограмм -

    процедур и/или функций.

BEGIN

    Тело процедуры

END;

Модификатор VAR в списке параметров перед 3-м и 4-м параметрами

указывает, что процедуре при вызове на выполнение будут

передаваться не значения параметров,а ссылка на существующую

переменную и процедура в этом случае может изменять значения

такой переменной. При передаче параметров по значению в стек

заносится значение и все изменения остаются внутри процедуры, не

затрагивая область памяти самой переменной, переданной в

процедуру по значению.

Структура определения функций совпадает с процедурами, кроме

заголовка - он содержит ключевое слово FUNCTION и дополнительно

через двоеточие тип возвращаемого функцией значения -скалярного:

FUNCTION ИмяФункции(Список параметров) :ИмяСкалярногоТипа;

Под именем функции скрывается при вызове на выполнение скалярное

значение возвращаемого ею типа и имя функции вместе со

списком своих параметров может использоваться  в выражениях

наравне с переменными

Глобальными называют те константы, типы и переменные, которые

описаны вне процедур или функций, а локальные - объявленные либо

в списке параметров (переменные), либо в разделах подпрограмм.

Чтобы подпрограмма могла работать с глобальными объектами, их

описание должно предшествовать определению подпрограммы.

Процедуры и функции в Паскале могут быть вложенными друг в друга

с большим числом уровней вложенности, хотя на практике он не

превышает 2. Вложенная подпрограмма относится к охватывающей ее

подпрограмме так же как подпрограмма к основной программе.

Вложенные подпрограммы могут вызываться только внутри

охватывающей, их переменные - локальны, но внутри вложенной

подпрограммы "видны" все объекты охватывающей подпрограммы и все

по настоящему глобальные. Область действия меток всегда

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

метки, ни операторы GOTO никогда не понадобились.

Опережающие объявления подпрограмм.

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

последовательно сверху вниз, не осуществляя предварительного

полного просмотра с составлением таблиц идентификаторов, как это

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

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

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

считать их неизвестными и вам придется перемещать тексты

определения подпрограмм вверх по тексту. Чтобы избежать этого и

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

"задом наперед", а в естественном порядке разработки от главной

программы к подпрограммам вниз по тексту, используют опережающие

объявления подпрограмм директивой FORWARD:

PROCEDURE ИмяПроцедуры(параметры); FORWARD;

FUNCTION ИмяФункции(параметры) :ИмяТипа;FORWARD;

          ....

PROCEDURE ИмяПроцедуры; {список параметров уже не нужен}

...

begin

    тело процедуры

end;

FUNCTION ИмяФункции;    {достаточно имени}

Тело функции

...

Директива FORWARD помогает также развязать закольцованные

вызовы, когда 2 подпрограммы вызывают друг друга и их взаимное

расположение не может решить проблему предварительного

опрделения до использования.

Объявление внешних подпрограмм.

Сам Паскаль - не слишком коммуникабельный язык, он не

поддерживает генерацию объектных файлов в формате OBJ и не

поставляет написанные на нем подпрограммы в прогарммы на других

языках. Но использовать при компиляции и компоновке внешние

подпрограммы в виде OBJ - файлов, созданных другими

трансляторами, он не отказывается, если удовлетворяются при этом

требования к используемой модели памяти и способу передачи

параметров.

Команды подстыковки объектных файлов в Паскаль - программу

задаются директивами компилятора {$L ИмяФайла.obj} в подходящих

местах прогаммы. Реализованные в этих файлах подпрограммы должны

быть объявлены специальным словом EXTERNAL.

{$L memlib.obj}

PRICEDURE MemProc1; EXTERNAL;

Подпрограммы как параметры других подпрограмм.

Чтобы имя подпрограммы можно было передать в другую подпрограмму

в виде параметра, вводится специальный процедурный тип - это тип

заголовка подпрограммы в целом:

TYPE

RealFuncType = function(t:Real):Real;

Подпрограммы этого типа (функции с теми же параметрами и

возвращаемым типом) могут использоваться в описаниях параметров

подпрограмм, но компилироваться должны в режиме {$F+}.

{$F+}

FUNCTION SinExp(tt: Real) : Real;

begin

    SinExp :=Sin(tt) * Exp(tt)

end;

{$F-}

Такая функция может быть подставлена в вызов подпрограммы:

Integral(0,1,Res1,SinExp)

для получения в переменной RES1 значения интеграла в пределах

[0,1], если процедура Integral определена примерно так:

PROCEDURE Integral(LowerLimit,UpperLimit:Real;

                  VAR Result            :Real;

                  FuncT     :RealFunctinType);

VAR t:Real;

begin

    Численное интегрирование функции FuncT c результатом в

Result

end;

Переменные процедурного типа.

По формату они совместимы с переменными типа Pointer  и после

приведения типов могут обмениваться с ними значениями. Чтобы

переменная - подпрограмма понималась как указатель на адрес

подпрограммы в памяти, она должна предваряться оператором @.

TYPE

DemoProcType = procedure(A,B:Word);

DemoRecType = RECORD

            X,Y:Word;

            Op:DemoProcType;

              END;

VAR P1,P2 :DemoProcType;

...

В примере приведена возможность хранения в записи не только

данных , но процедур их обработки.

Обмен данными между подпрограммами через общие области памяти.

Можно, используя директиву совмещения адресов, объявить в

нескольких подпрограммах области данных, совмещенные с некоторой

глобальной переменной:

TYPE

Vector100Type = Array[1..100] of Real;

MatrixType    =Array[1..10,1..10] of Real;

Matrix2Type    =Array[1..50,1..2] of Real;

VAR

V:Vector100Type;

PROCEDURE P1;

VAR M : MatrixType absolute V;

begin  end;

PROCEDURE P2;

VAR M2 :MatrixType absolute V;

begin   end;

PROCEDURE P3;

VAR V3 : Vecttor100Type absolute V;

begin   end;

begin   end.

Статические локальные переменные.

Вводятся в разделе  CONST подпрограмм как типизированные

константы (переменные со стартовым значением) и будут размещны

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

PROCEDURE XXX;

VAR ...

CONST

A:Word = 240;

B:Real=41.3;

begin Здесь значения А и В могут изменяться с сохранением

значений до следующих вызовов процедуры

end;

Бестиповые параметры - переменные.

Паскалевские подпрограммы могут иметь в своих заголовках

бестиповые параметры - переменные (передача по ссылке), с

упоминанием только имени переменной без двоеточий и типов

PROCEDURE PDemo(VAR V1,V2);

FUNCTION FDemo(A: Integer; VAR V) : Real;

Через них в подпрограмму можно передавать адреса переменных

любых типов - строк, массивов, записей и т д, но подпрограмма

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

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

обработки массивов произвольной длины - при использовании

типированных параметров это невозможно, т к размер массива

входит в описание типа. В качестве примера составим функцию,

суммирующую N элементов произвольных числовых массивов:

VAR

B1 :Array[-100..100] of Byte;

B2 :Array[0..999] of Byte;

B3 :Array['a'..'z'] of Byte;

S  :String;

{$R-}     {Выключим проверку индексов массивов}

FUNCTION      Sum(VAR X;N:Word):LongInt;

TYPE

XType = Array[1..1] of Byte;

VAR

Summa:LongInt; i:Word;

begin

    Summa:=0;

    for i:=1 to N do Summa:=Summa + XType(X)[i];

    Sum:=Summa;

end;

{$R+}

begin

    S:='123456789';

    Writeln(Sum(B1,201));

    Writeln(Sum(B2[100],101));

    Writeln(Sum(B3['b'],10));

    Writeln(Sum(S[1],9));

end.

Как видим, функция Sum не боится несовместимости типов, но будет

возвращать корректные результаты только для массивов с

элементами типа Byte - так мы определили тип XType, к которому

приводим все, что передается подпрограмме через параметр Х.

Обратите внимание на диапазон XType 1..1 - он не несет смысловой

нагрузки и при отключенной проверке индексов может быть

произвольным. Рассмотренный пример можно распространить и на

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

передавать через бестиповый параметр множества или элементы

создаваемых нами перечислимых типов из-за особенностей их

машинного представления.

Рекурсия.

Как мы уже знаем, под рекурсией понимается вызов подпрограммы из

тела этой же подпрограммы, что часто используется при

программировании многих математических формул, имеющих

рекурсивные определения-например, формула вычисления факториала

              n! = n*(n-1)! , если n>0

или степени целого числа

             x^n = x*x^(n-1) , если n>0

т е тогда, когда для вычисления следующего значения необходимо

использовать предыдущее.

В Паскале рекурсия записывается так же, как в формулах -

проиллюстрируем это на примере того же факториала:

FUNCTION Fact(n:Word):LongInt;

begin

    if n=0 then Fact:=1 else Fact:=n*Fact(n-1);

end;

и степени числа:

FUNCTION Pow(x:Real;n:Word):Real;

begin

    if = 0 then Pow:=1 else Pow:=x*Pow(x,n-1);

end;

При n>0 происходит следующее: запоминаются значения членов

выражения в ветви else (n для факториала ,x для степени), для

вычисления неизвестных вызываются те же функции но с

предшествующими аргументами. При этом вновь запоминается в

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

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

выполнение ветви then. После этого идет раскрутка в обратную

сторону с изъятием из памяти отложенных значений до получения

конечного результата.

Несмотря на наглядность рекурсивных описаний, те же задачи чаще

всего более эффективно решаются обычными итерациями :

FUNCTION Pow(x:Real;n:Word):Real;

VAR i:Word;m:Real;

begin m:=1;

for i:=1 to n do m:=m*x;

Pow:=m

end;

Рекурсивные описания целесообразно применять для компиляторов,

которые при возможности сами преобразуют рекурсивные вызовы в

итеративные процедуры (Turbo Prolog), а также в специфических

задачах типа грамматичекого разбора символьных конструкций.

Отметим также трудность принудительного выхода из рекурсивных

подпрограмм - оператор Exit срабатывает только на 1 уровень

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


Подпрограммы в QBASIC

Помимо встроенных функций Q-Бейсик позволяет пользователю

создавать и использовать в программах свои собственные

подпрограммы 2-х видов - функции и процедуры (как и в Паскале).

ФУНКЦИЯ в QBASIC создается заданием ключевого слова FUNCTION и

следующего за ним имени функции - при этом редактор

автоматичекски генерирует новое программное окно с операторами,

ограничивающими тело функции:

FUNCTION Имя_функции [STATIC]

...

END FUNCTION

После составления ф-ции QBASIC автоматичкски сгенерирует

объявление ее прототипа в начале программы с ключевыми словами

DECLARE FUNCTION

Если будет указан модификатор ф-ции STATIC то все ее локальные

переменные будут сохранять свои значения между вызовами ф-ции.

 Подставляйте вместо многоточия в пространство между

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

программы.  Для возврата в основную программу нажимайте F2 для

вызова списка подпрограмм вашей программы и 1-й элемент этого

списка содержит имя главной программы. Имя ф-ции должно

содержать последним символом значек типа возвращаемого значения,

а за именем должны быть круглые скобки для передаваемых ей

параметров:

STRICH$() - возвращает строку

RUNDEN%() - возвращает целое

Для примера в главной программе

DIM zahl!, stelle%'число и количество позиций после запятой

CLS

INPUT "Введите число:",zahl!

INPUT "Позиция:",stelle%

PRINT runden!(zahl!,stelle%)

объявим функцию runden! для округления числа до заданной

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

точность:

FUNCTION runden!(wert!,wonach%)

DIM rund!,faktor% ' - рабочие переменные

factor%=10^wonach%

rund!=wert!*factor%+.5

rund!=INT(rund!)

rund!=rund!/factor%

runden!=rund! 'обеспечиваем возвращение результата

END FUNCTION

ПРОЦЕДУРЫ объявляются с ключевым словом SUB

Имя_процедуры (список параметров) - при этом редактор

сгенерирует окно с шаблоном процедуры вида:

SUB Имя

END SUB

Процедуры  отличаются от функций отсутствием возвращаемого

значения и способом вызова через оператор CALL:

CALL fenster(1,1,23,88,art1$)


Библиотеки подпрограмм в Си

Конкретным, "настоящим" исполнителем наших компьютерных

алгоритмов является центральный процессор, специализированные

процессоры управления клавиатурой, дисплеем, дисководами,

печатающими устройствами и другие элементы аппаратного

обеспечения. Центральный процессор берет из памяти команды одну

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

элементарные операции, "строительные блки" более сложных

операций, которые в свою очередь являются элементами еще более

сложных и т. д. до решения поставленной общей задачи, которая

в других случаях тоже может стать одной из фаз решения еще более

общих задач.

Программирование на уровне процессорных команд - очень уж

трудоемкое дело, поэтому вполне закономерно появление

"посредников" разных уровней, облегчающих наше общение с

аппаратурой компьютера при решении различных  прикладных задач.

Посредником самого нижнего уровня является базовая система

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

устройствами, размещенных частично в постоянной памяти (только

для чтения - ROM), а частично - на диске. Наличие дисковой части

позволяет изменять подпрограммы управления аппаратурой

"незаметно" для пользователя, если способ обращения к этим

подпрограммам остается при этом неизменным. Подпрограммы

BIOS для управления устройствами (драйверы  ) аппаратно -

зависимы, т е содержат фрагменты алгоритмов, которые должны

изменяться при замене устройств более совершенными. Поэтому

составление программ с непосредственным использованием BIOS -

подпрограмм может сделать нашу программу непереносимой на другие

компьютеры.

Более "высокий" уровень посредничества предоставляет дисковая

операционная система,подпрограммы которой предоставляют в наше

распоряжение услуги,внешне не зависящие от аппаратуры -

обращение к этим услугам и их содержание отделено от деталей

аппаратной реализации и это обеспечивает переносимость наших

программ между компьютерами, работающими с такой же операционной

системой.

BIOS и DOS представляют собой по существу библиотеки подпрограмм

для выполнения ряда стереотипных действий, необходимых

подавляющему большинству программ. Все возможные услуги

группируютя в комплексы по функциональному или аппаратному

признакам и каждый комплекс имеет свой фиксированный номер,

называемый "номером прерывания". Услуги внутри комплексов

также нумеруются и эти номера носят название "номеров функций".

Некоторые функции представляют собой комплексы подфункций,

которые в этом случае тоже нумерованы. Для получения

предоставляемых этими подпрограммами услуг необходимо в

предписанный регистр процессора (как правило - в АН) занести

номер функции (при необходимости в другой регистр, как правило в

AL - номер подфункции), если необходимо, в другие предписанные

регистры записать необходимые для выполнения данные и подать

процессору команду INT с номером прерывания, в комплекс которого

входит небходимая подпрограмма. Умножив номер прерывания на 4

(количество байтов, занимаемых адресом входной точки любой

подпрограммы), процессор определяет адрес адреса входной точки

комплекса подпрограмм (так называемого вектора прерывания) и

передает в эту точку управление. Входная часть комплекса берет

из регистров номер функции и подфункции и необходимые данные и

передает управление в соответствующую ветвь комплекса

подпрограмм. Такая организация возможна потому, что векторы

прерываний располагаются в самой начальной области оперативной

памяти (начиная с 0-го адреса) и заполняются адресами входных

точек подпрограммных комплексов при загрузке операционной

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

выполненных действий должна возвратить некоторые данные, то они

также размещаются в предопределенных процессорных регистрах и

наша программа может их оттуда взять и обработать.

При работе в ассемблере прямое использование услуг операционной

системы описанным способом является основным.

 Библиотеки подпрограмм в Си.

Си - маленький по лексикону язык, вся его мощь -в многочисленных

стандартных и специализированных объектных библиотеках

подпрограмм.  Практически невозможно сделать сколько - нибудь

компактный обзор всех наработанных библиотек Си и Си++, поэтому

мы ограничимся лишь самыми необходимыми на начальной стадии

обучения. В профессиональном программировании приходится тратить

немало времени на изучение возможностей, предоставляемых

библиотеками, но как правило программист специализируется в

некоторой прикладной области и использует наиболее подходящие

для этой области библиотеки.

  Механизм использования библиотечных подпрограмм состоит в

следующем. Помимо скомпилированных до объектных модулей

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

компоновщиком, в наше распоряжение предоставляются так

называемые заголовочные файлы текстового формата - они содержат

объявления необходимых для работы с библиотекой типов данных,

именованных констант, переменных и прототипы содержащихся в

объектной библиотеке подпрограмм (функций). Эти файлы имеют

обычно расширение .h (от слова header - заголовок). Приведенные

в заголовочных файлах прототипы библиотечных подпрограмм

позволяют осуществлять их вызовы на выполнение в соответствии с

их заголовками, если соответствующий заголовочный файл включен в

программу директивой препроцессора #ibclude. Например для работы

с библиотекой подпрограмм обработки строк нам необходимо

включить в программу директиву

#include <string.h>

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

директиву

#include <stdio.h>

для использования подпрограмм динамического распределения памяти

- директиву

#include <alloc.h>

и т д.

Приведем прототипы подпрограмм некоторых библиотек.

БИБЛИОТЕКА ОБРАБОТКИ ТЕКСТОВЫХ СТРОК

 char * strcat(char  *dest,char  *source)  -  канкатенирует

 (сцепляет) строки dest и souree;

 char *strncat (char *dest,char *souree,unsigned n) -

 канкатенирует n символов строки dest к строке souree;

 char *strchr (char *souree,char ch) - поиск в строке sourсe

 первого вхождения символа ch;

 char *strcmp (char *s1,char *s2) - сравнивает строки  s1  и

 s2 , возвращает 0 (равны), < 0 (s1<s2), > 0 (s1 > s2).

 char *strncmp (char *s1,char *s2,int n) - то же, что и

 предыдущая функция ,но сравниваются только первые n

 символов обеих строк.

 char *stricmp (char *s1,char *s2) - то же, что и предыдущая

 функция ,но не проверяется регистр букв.

 char *strnicmp(char  *s1,char  *s2,int  n) - то же,  для n

 сравниваемых символов без проверки регистров.

char * strcpy (char * dest, char * source) - копирование

строки source в строку dest.

char * strncpy(char * dest,  char * source, int n) -копирование

п символов из source в dest.

int strlen(char * s)-длина строки , но без нуль-символа конца

строки.

char * strlwr(char * s) - перевод в нижний регистр (только

латинские символы)

char * strupr(char * s) - перевод в верхний регистр (только

латиница)

 char * strdup (char * s) - создает в куче копию строки s_,

 возвращая ее указатель.

 char * strset (char * s, char ch) - заполняет строку s

 символами сh.

 char * strnset (char * s, char ch, unsiqned n) -то же для

 первых п символов строки s.

 char * strrev (char * s) - инвертирует все буквы s.

 int strcspn (char * s1, char * s2) - длина начального сегмента

 s1,состоящая из символов, которых нет в s2.

char * strpbrk (char * s1, char * s2) - просмотр s1 до встречи

с символом из s2.

char * strchr (char * s,char c) -последнее вхождение с в s.

int strspn (char * s1,  char * s2) - длина  начльного  сегмента

s1, состоящая из символов,которые есть в s2.

char * strtok (char * s1, char * s2). B s2 cодержатся раздели-

тетели. strtok  возвращает  указатели на слова.

 В стандартной библиотеке Турбо-Си stdio.h есть 2 функции

 ввода-вывода в строки с предварительным форматированием :

int sprintf(char * buffer,const char  *format [,argument,,..]);

принимает список аргументов,применяет к каждому из них

спецификатор формата из строки формата и помещает

сформатированные данные в строку

int sscanf(const char *buffer,const char *format[,address,...]);

посимвольно читает последовательность входных полей из строки,

каждое поле форматируется по спецификации формата и

записыввается по адресам,переданным в качестве аргументов

При использовании строковых функций вы должны быть уверены в том

, что они заканчиваются символом '\0'.Для литералов этот символ

добавит сам компилятор,но при посимвольном заполнении

ответственность за символ окончания строки лежит на

программисте:

  my_s[0]='k';

  my_s[1]='o';

  my_s[2]='t';

  my_s[3]='\0';

Библиотечные  функции  СИ  для   поиска   и   сортировки

структурных переменных.

Бинарный поиск   в  упорядоченном масиве  элементов

#include (stdlib.4)

void * bsearch ( const void * key, coyst void * base, size_t

* nelem , size_t width,int (8fcmp) (const void * elem1,

const void * elem2)

Здесь  key - указатель на структуру;

(*fctp)()-пользовательская функция сравнения возвращающая  0

при  равно  ,  >0,  если  elem1  больше, <0   если   elem2

больше. Elem1 и elem2- сравниваемые елементы массива.

Из fcmp  возвращеются указатель на найденный в масиве элемент.

Если элемент не найден , возвращается null.

lsearch () -линеаризованный поиск;

ltinde () -линеаризованный поиск;

gsort ()- быстрая сортировка.

Стандартные функции ввода-вывода

Сам язык Си не содержит встроенных средств поддержки ввода -

вывода - эти операции обслуживаются библиотечными функциями;

интерфейс  с  библиотеками  обеспечивается включаемыми

заголовочными файлами:

stdio.h- стандартного ввода - вывода .

io.h   - ввода - вывода нижнего уровня.

conio.h- консольный ввод - вывод , работающий с видеобуфером

напрямую.

 Поток(stream)-абстрактное понятие,связанноес переносом данных

 между  устройством чтения - записи и потоком этих данных ,"

 движущихся" мимо устройства (воображаемой галовки ) . Потоком

 информации можно считать данные на диске или поступающие с

 клавиатуры ,  или дисплей,  или порты ввода - вывода -  любое

 устройтво  с последовательной структурой информационых записей.

 Состав потока задаёеся структурой FILE, объявленной в файле

 stdio.h:

  typedef struct {  short level;/* Уровень буфера */

  unsigned flags;  /* Флаги статуса файла */

  char fd;         /*Дескриптор файла */

  char hold;       /*Предыдущий символ, если нет буфера */

  short bsizt;     /* Размер буфера */

  unsigned char * buffer;/* Буфер передачи */

  unsigned char * curp;  /* Текущий указатель*/

  short token;      /* параметр корректности */ } FILE;

 Перед началом  работы операционная система инициализирует

 глобальные файловые переменные stdin , stdout и sterr (

 стандартных ввода  , вывода и ошибок ), приписывая stdin к

 клавиотуре ,  а stdout и stderr к экрану.  Но ввод и вывод

 можно переназначить при вызове программы из DOS:

  program < имя - файла /* переназначения ввода */

  program > имя - файла /* переназначение вывода */.

Различают текстовые и двоичные потоки .  Текстовый поток

состоит из последовательности символов ,  разделённых на

строки символом '/n'.

Двоичный поток  -  просто последовательность значений типа

char.  Символ EOF в операциях ввода -ввывода служит для

проверки  конца файла и определяется как :

 # define EOF  ( -1 )

 EOF oписан в io .h.

 Расмотрим основные функции ввода - вывода .

Функции с прототипами в stdio.h :

Ввод-вывод символов

int fgetc ( FILE * stream ) /*  Возвращает  следующий  символ

из файла с преоброзованием в int.*/ Пример :

             #include <string.h>

             #include <stdio.h>

             #include <conio.h>

             int main(void)

             {

                FILE *stream;

                char string[] = "This is a test";

                char ch;

                /* open a file for update */

                stream = fopen("DUMMY.FIL", "w+");

                /* write a string into the file */

                fwrite(string, strlen(string), 1, stream);

                /* seek to the beginning of the file */

                fseek(stream, 0, SEEK_SET);

                do

                {

                   /* read a char from the file */

                   ch = fgetc(stream);

                   /* display the character */

                   putch(ch);

                } while (ch != EOF);

                fclose(stream);

                return 0;

             }

int fgetchar ( void );/* Возвращает следующий символ из stdin

                         это всё равно , что fgetc (stdin).*/

               Пример :

               #include <stdio.h>

               int main(void)

               {

                  char ch;

                  /* prompt the user for input */

                  printf("Enter a character followed by \

                         <Enter>: ");

                  /* read the character from stdin */

                  ch = fgetchar();

                  /* display what was read */

                  printf("The character read is: '%c'\n",

                         ch);

                  return 0;

               }

int  fgetchar(void)-получает символ из stdin

int   fputc(int __c, FILE *__stream); -вывод символа_ в файл

int fputchar__(int c)-_вывод символа в stdout

int   getc(FILE *__fp);     -из потока

int   getchar(void);        -из stdin

int   getch(void)- с клавиатуры без отображения (в conio.h)

int   putc(const int __c, FILE *__fp);             -в поток

int   putchar(const int __c);                      -в stdout

int   putch(int c)                           -в текущее окно

int   ungetc(int __c, FILE *__stream);

Ввод-вывод строк

char * fgets ( chars , int n , FILE * stream );

/* Читает символы из файла ,  пока не наберёт n-1  или  встретит

 символ новой строки . В конец всегда запишется нулевой байт

 конца строки. Возвращает указатель на строку или NULL. */

          Пример :

          #include <string.h>

          #include <stdio.h>

          int main(void)

          {

             FILE *stream;

             char string[] = "This is a test";

             char msg[20];

             /* open a file for update */

             stream = fopen("DUMMY.FIL", "w+");

             /* write a string into the file */

             fwrite(string, strlen(string), 1, stream);

             /* seek to the start of the file */

             fseek(stream, 0, SEEK_SET);

             /* read a string from the file */

             fgets(msg, strlen(string)+1, stream);

             /* display the string */

             printf("%s", msg);

             fclose(stream);

             return 0;

          }

char   *_Cdecl gets(char *__s);

int   _Cdecl fputs(const char *__s, FILE *__stream);

int   _Cdecl puts(const char *__s);

Форматированный ввод-вывод

int  fprintf(FILE *__stream, const char *__format, ...);

int  printf(const char *__format, ...);

int   fscanf(FILE *__stream, const char *__format, ...);

int  scanf(const char *__format, ...);

int  sprintf(char *__buffer, const char *__format, ...);

int  sscanf(const char *__buffer, const char *__format, ...);

int   vfprintf(FILE *__stream, const char *__format,

               void *__arglist);

 int  vfscanf(FILE *__stream, const char *__format,

              void *__arglist);

 int  vprintf(const char *__format, void *__arglist);

 int  vscanf(const char *__format, void *__arglist);

 int  vsprintf(char *__buffer, const char *__format,

               void *__arglist);

 int  vsscanf(const char *__buffer, const char *__format,

              void *__arglist);

 Функции управления файлами

 void clearerr (FILE * stream):  /* обнуляет признак ошибки

                                  для файла stream  */

               Пример :

               #include <stdio.h>

               int main(void)

               {

                  FILE *fp;

                  char ch;

                  /* открываем файл для записи */

                  fp = fopen("DUMMY.FIL", "w");

                  /* force an error condition by attempting to read */

                  ch = fgetc(fp);

                  printf("%c\n",ch);

                  if ferror(fp)

                  {

                     /* вывод сообщения об ошибке */

                     printf("Ошибка чтения из DUMMY.FIL\n");

                     /* очистка признака ошибки */

                     clearerr(fp);

                  }

                  fclose(fp);   /* закрываем файл*/

                  return 0;

               }

int fclose ( FILE * stream ) /* Записывает в файл содержимое его

буферов и закрывает, возвращая 0 ,если все нормально и EOF в

противном случае . */ Пример:

                   #include <string.h>

                   #include <stdio.h>

                   int main(void) {

                   FILE *fp; char buf[11] = "0123456789";

                   /* create a file containing 10 bytes */

                   fp = fopen("DUMMY.FIL", "w");

                   fwrite(&buf,strlen(buf), 1, fp);

                   /* close the file */

                   fclose(fp); return 0;

                   }

int fcloseall ( void );

/* Закрывает все открытые файлы  потоки ,  кроме  стандартных

 stdin, stdout,  stdprn,stderr,stdaux и возвращает количество

 закрытых. */ Пример:

                #include <string.h>

                #include <stdio.h>

                #include <stdio.h>

                int main(void)

                {

                   int streams_closed;

                   /* open two streams */

                   fopen("DUMMY.ONE", "w");

                   fopen("DUMMY.TWO", "w");

                   /* close the open streams */

                   streams_closed = fcloseall();

                   if (streams_closed == EOF)

                      /* issue an error message */

                      perror("Error");

                   else

                      /* print result of fcloseall() function */

                      printf("%d streams were closed.\n", streams_closed);

                   return 0;

                }

fclose(FILE *stream)        /*закрыть указанный файл*/

              int main(void)

              {

                 FILE *fp;

                 char buf[11] = "0123456789";

                 /* create a file containing 10 bytes */

                 fp = fopen("DUMMY.FIL", "w");

                 fwrite(&buf, strlen(buf), 1, fp);

                 /* close the file */

                 fclose(fp);

                 return 0;

              }

int feof ( FILE * stream ) /* Ввозвращает ненулевое значение ,

         если при последней операции ввода обнанаружен индикатор

         конца файла */

             #include <stdio.h>

             int main(void)

             {

                FILE *stream;

                /* open a file for reading */

                stream = fopen("DUMMY.FIL", "r");

                /* read a character from the file */

                fgetc(stream);

                /* check for EOF */

                if (feof(stream))

                   printf("We have reached end-of-file\n");

                /* close the file */

                fclose(stream);

                return 0;

             }

int fflush ( FILE * stream ) /* Выталкивает буфер файла в файле

      ( в том числе буфер стандартного ввода - клавиатурный ,

                       если в нём что - нибудь есть .) */

             Пример :

             #include <string.h>

             #include <stdio.h>

             #include <conio.h>

             #include <io.h>

             void flush(FILE *stream);

             int main(void)

             {

                FILE *stream;

                char msg[] = "This is a test";

                /* create a file */

                stream = fopen("DUMMY.FIL", "w");

                /* write some data to the file */

                fwrite(msg, strlen(msg), 1, stream);

                clrscr();

                printf("Press any key to flush\

                       DUMMY.FIL:");

                getch();

                /* flush the data to DUMMY.FIL without\

                   closing it */

                flush(stream);

                printf("\nFile was flushed, Press any key\

                       to quit:");

                getch();

                return 0;

             }

             void flush(FILE *stream)

             {

                  int duphandle;

                  /* flush the stream's internal buffer */

                  fflush(stream);

                  /* make a duplicate file handle */

                  duphandle = dup(fileno(stream));

                  /* close the duplicate handle to flush\

                     the DOS buffer */

                  close(duphandle);

             }

 int fgetpos ( FILE * stream ,  fpos_t * pas ) ; /* Заносит

     позицию указателя файла в pos.  Тun fpos_t определен в

               stdio.h  как long .*/ Пример :

                #include<string.h>

                #include <stdio.h>

                int main(void) {

                  FILE *stream;

                  char string[] = "This is a test";

                  fpos_t filepos;

                  /* open a file for update */ stream =

                  fopen("DUMMY.FIL", "w+");

                 /* write a string into the file */

                  fwrite(string,strlen(string), 1, stream);

                  /* report the file pointer position */

                  fgetpos(stream, &filepos);

       printf("The file pointer is at byte\ %ld\n", filepos);

                  fclose(stream);

                  return 0;

               }

FILE * fopen ( const char * filename , const char * mode);

/* Открывает файл filename в режиме mode и возвращает дескриптор

 файла ( указатель) или NULL при ошибке .  Режим mode может иметь

 значение :

                 r - для чтения ( открывает );

                 w - для записи ( или создаёт);

                 a - для дополнения ( или создаёт);

                r+ - для чтения - записи ;

                w+ - новый для коректировки.

                a+ - для дополнения .

                at, rt, wt - признак текстового файла

                rb, wb, ab - признак двоичного файла */

             Пример :

             /* Program to create backup of the

                AUTOEXEC.BAT file */

             #include <stdio.h>

             int main(void)

             {

                FILE *in, *out;

                if ((in = fopen("\\AUTOEXEC.BAT", "rt"))

                    == NULL)

                {

                   fprintf(stderr, "Cannot open input \

                           file.\n");

                   return 1;

                }

                if ((out = fopen("\\AUTOEXEC.BAK", "wt"))

                    == NULL)

                {

                 fprintf(stderr, "Cannot open output \

                           file.\n");

                   return 1;

                }

                while (!feof(in))

                   fputc(fgetc(in), out);

                fclose(in);

                fclose(out);

                return 0;

             }

БИБЛИОТЕЧНЫЕ  ФУНКЦИИ ПРЕОБРАЗОВАНИЯ ДАННЫХ

(ОПИСАНЫ В ЗАГОЛОВОЧНОМ ФАЙЛЕ STDLIB.H)

double atof(const *char s)-_преобразует строку в число с

плавающей точкой по формату double

int atoi(const *char s)_ -преобразует строку в целое

long atol(const *char s) -преобразует_строку в long int

char *ecvt(double  value,int  ndig,int*dec,int*sign)

-преобразует число value  в  строку  из ndig цифр с

десятичной точкой,позиция которой по адресу dec,а знак по

адресу sign _

char *fcvt(double   value,int ndig,int*dec,int*sign)-_идентична

ecvt ,но в dec -количество цифр после дес точки _

char *gcvt(double value,int ndig,char *buf)_-преобразует value

     в строку из ndig цифр и помещает ее в буфер по адресу buf

char *itoa(int  value,char*string,int  radix) -преобразует

     целое value в в строку  string по системе счисления

     radix(от 2 до 36)

char *ltoa(long   value,char*string,int radix)_-аналогична  для

длинного целого

double strtod(const char *string,char **endptr)-_преобразует

строку string в число и в endptr возвращает указатель на символ

,на  котором была прекращена обработка строки _long

strtol(const char *string,char  **endptr,int radix)_-выполняет

преобразование  строки  в  long  int с  учетом системы

счисления radix

 char strtoul(const char *string,char **endptr,int

 radix)-преобразует  строку в буфере с адресом string в

 число,используя систему счисления radix

char ulnoa(unsigned long value,char *string,int

radix)_-преобразует число value в строку string по ситеме radix

Пример программы,преобразующей  вводимые  целые  числа из системы

счисления от 2 до 32 в любую другую:

     #include <stdio.h>

     #include <stdlib.h>

     #include <math.h>

     #include <limits.h>

     int main(void)

     {char buf[128],**er_sym;long dec;

     int in_radix,out_radix;

     *er_sem=buf;**er_sym='\0';

     /* Ввод системы счисления  и  преобразуемого числа*/

     fputs("Система счисления 0-с/с по записи",stdout);

     scanf("%d",&in_radix);

     fputs("Исходное число",stdout);

     fflush(stdin);gets(buf);

     /* Преобразовани числа с проверкой корректности*/

     dec=strtol(buf,er_sym,in_radix);

     if(dec==LONG_MIN || dec==LONG_MAX)

     {perror("\aОшибка");return 254;}

     if(er_sym)

     {printf("\aВ записи числа не может быть цифры

     %c\n",**er_sym); return255;}

     /* Ввод системы счсления для вывода*/

     fputs("Система счисл для вывода",stdout);

     scanf("%d",&out_radix);

     lnoa(dec,buf,out_radix);

     fputs("Результат:",stdout);

     puts(buf);

     if(out_radix>10)

     printf("Символы от А до %c соответствуют

    десятичным числам от10 до

     %2d\n",'A'+(out_radix-11,out_radix-1); return 0; }

 Программа позволяет выполнять  самые  экзотические

 пробразования напрмер из  3-ичной  системы в 18-ричную с

 контролем корректности ввода

Стандартные математические функции

(в библиотеках stdlib,math)

int _abs(int x)_-_абсолютное значение целого аргумента

double acos(double x)-арккосинус аргумента от -1 до 1 с возвратом

угла в радианах от -pi/2 до pi/2

double asin(double x)

double atan(double x)

double atan2(double y,double x)-арктангенс отношения y/x

double cabs(struct complex znum)-модуль комплексного числа

double ceil(double x)-ближайшее  целое  с  округленим  в

большую сторону

 double cos(double x)

 double cosh(double x)

 div_t div(int number,  int denom)-делит целые  с  возвратом

 типа div_t из stdlib:

                       typedef struct

                       {int quot;/*частное*/

                        int rem;/*остаток*/

                       }div_t;

double exp(doble x)-возиращает е в стапни х

double fabs(double x)- абс знач х

double floor(double x)-ближайшее целое с  округлением  в

меньшую сторону

double fmod(double   x,double    y)-остаток    от

деления,если частное-наибольшее целое

frexp(double value,int  *exponent)-нормализованная   мантисса

и порядок в адресе exponent

double hipot(double x,double y)-гипотенуза 3-угольника с

катетами x и y

double ldexp(double x, int exp)-чсло с мантиссой х и порядком

 ехр

 double log(double x)

 double log10(double x)

 unsigned  long lrotl(unsigned long val,int count)

 unsigned  long lrotr(unsigned long val,int count)

 max(a,b)   максим и минимум любых числовых  аргументов

 min(a,b)

 double modf(double  x,double  *ipart)-возвр дробную часть и целую

 часть числа х (целое по адресу ipart)

 double pow(double x,double y)-х в степени у

 double pow10(int p)-10 в степени р

 int rand(void)-случ число от 0 до 32767(RAND_VAX в stdlib.h)

 int random(int num)- случ целое от 0 до num-1

 void randomize(void)-старт генератора случ чисел(нач значение)

 unsigned _rotl(unsigned value,int count)

 unsigned _rotr(unsigned value,int count)

 double sin(double x)

 double sinh(double x)

 double sqrt(double x)

 void srand(unsigned  seed)-начальное целое seed для генерирования

  случ целых

 double tan(double x)

 double tanh(double x)

Классификация и преобразование символов

(определения - в звголовочном файле ctype.h)

 Все фуннкции классификации возвращают  ненулевое

 значение(истина), если анализируемый символ соответствует

 названию функции

isalnum(c),isalpha(c),isascii(c),iscntrl(c),isdigit(c),isgraph(c),

islower(c)-строчная

буква,isprint(c),ispunct(c),isspace(c),isupper(c),isxdigit(c)-16- ричная цифра

 tolower(c)-преобразует прописную   в   строчную,возвращает

 код строчной

toascii(c)-возвращает код ASCII от 0 до 127

tolower(c)- то же ,что и _tolower, но с предварительной проверкой

на принадлежность к прописной

toupper(c)

Работа с блоками памяти

(прототипы - в файле mem.h и string.h)

void *memccpy(void *dest,const void  *src,int  c,size_t  n)-

копирует блок памяти из адреса src в адрес dtst до встречи с

симво- лом с или до переноса n символов

void *memchr(const  void  *s,int c,size_t n)-возвращает

указатель на байт в s,содержащий с или NULL

int memcmp(const void s1,const void s2,size_t

n)-сравниваетпервые n байтов буферов s1 и s2,возвращая 0 при

совпадении  отриц  число если s1<s2 и полож при s1>s2

void memcpy(void  *dest,const  void  *src,size_t  n)-копирует

n байтов из src в dest

int memicmp(const  void  s1,const  void  s2,size_t

n)-сравнивает первые  n байтов буферов s1 и s2,возвращая 0 при

совпадении отриц число если s1<s2 и полож при s1>s2 с

предварительным преобразова нием в строчные

void memmove(void  *dest,const  void  *src,size_t  n)-то же

что и memcpy(), но с корректным переносом для перекрывающихся

буферов

void *memset(void *s,int c ,size_t n)-заполняет n байтов буфера

s символом c

void movedata(unsigned srcseg,unsigned srcofs,unsigned dstseg,

unsigned dstofs,size_t n)

void swab(char  *from,char  *to,  int   n)-копирует   с

 заменой чет-нечет

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

на стадии выполнения программы областью памяти

Программа - загрузчик Си-программ.

Во все скомпилированные и скомпонованные загрузочные модули,

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

C0x.OBJ, содержащий 2 основные секции: загрузчик (или стартер)

и секцию завершения. Структура и алгоритм работы загрузчика

зависят от выбранной модели памяти - для каждой из них есть свой

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

сформировать загрузочный модуль в точном соответствии с

представлениями программиста о его структуре, передать

управление функции main(), а после ее завершения выполнить

возврат в операционную систему.

Загрузчик выполняет следующие действия:

1.Сохраняет в кодовом сегменте адрес начала секции данных

DGROUP.

2.Инициализирует в сегменте данных ряд внешних переменных: номер

версии DOS, сегмент адреса начала PSP, сегмент адреса среды

программы, сегмент адреса границы оперативной памяти и др.

3.Сохраняет в сегменте данных некоторые векторы прерываний

(0,4,5,6 и др) и устанавливает собственный обработчик прерывания

0 - он будет печатать "Divide error" и аварийно завершать

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

4.Формирует среду прогрммы и аргументы, передаваемые в точку

входа main() - эти аргументы доступны Си - программе, если точка

входа описана как main(int,char **,char **).

5.Освобождает неиспользуемую программой оперативную память.

6.Выполняет настройку программы на аппаратуру, если в программе

есть код для математики с плавающей точкой и компиляция

выполняется с опцией Emulation.

7.Настраивает регистры процессора в соответствии с моделью

памяти.

8.Вызывает подпрограмму main().

После завершения main() модуль C0x.OBJ снова получает управление

и выполняет действия по завершению программы:

9.Восстанавливает все сохраненные векторы прерываний.

10.Закрывает открытые при выполнении программы файлы.

11.Вызывает одну из функций MS-DOS завершения программы.

Программа располагается в 2-х блоках памяти: 1-й занимает среда

программы, а 2-й - загрузочный модуль, построенный из сегментов

с прдопрделенными именами. Весь программный код (1 или более

сегментов) компонуется в секцию кода, все данные располагаются

подряд в секцию данных. Ссылкой на начало секции является имя

группы DGROUP. Кроме того прграмма обязательно имеет стек в

составе загрузочного модуля. И, наконец в зависимости от модели

памяти загрузчик создает пространство памяти под названием

"куча" (heap) для динамического распределения.Для работы с кучей

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

подключаемых с помощью заголовочного файла  alloc.h:

void * calloc(size_t nelem, size_t elsize) - выделяет место для

nelem элементов по elsize каждый и обнуляет выделенную память,

возвращая указатель типа void на выделенную память; тип size_t в

настоящее время определен как unsigned.

unsigned coreleft(void) (TINY,SMALL,MEDIUM)

unsigned long coreleft(void) (COMPACT,LARGE,HUGE)

-возвращает размер свободной кучи

void far *faralloc(unsigned long nelem, unsigned long elsze)

- выделяет из far - кучи место для nelem элементов по elsize

каждый и обнуляет выделенную память, возвращая указатель типа

void far * на выделенную память;

unsigned long farcoreleft(void) - размер свободной дальней кучи

void farfree(void far *block) - освобождает блок памяти в far -

куче размером, который должен соответствовать выделенному ранее

функциями farcalloc, farmalloc, farrealloc для малых моделей

памяти или calloc, malloc, realloc для старших моделей

void far *farmalloc(unsigned long nbytes)

void far * farrealloc(void far *block,unsigned long  size)

делает попытку перераспределить блок памяти block, в случае

невозможности расширить блок до заданного размера возвращает

NULL

void free(void* block)

void *malloc(size_t size)

void realloc(void *block, size_t size)

 Экранная   графика (прототипы в файле graphics.h)

void  far  arc(int x,int y,int srangle,int endangle,int radius)

-рисует дугу

#include <graphics.h>

#include <stdlib.h>

#include <stdio.h>

#include <conio.h>

int main(void)

{

    /* request auto detection */

   int gdriver = DETECT, gmode, errorcode;

   int midx, midy;

   int stangle = 45, endangle = 135;

   int radius = 100;

   /* initialize graphics and local variables */

   initgraph(&gdriver, &gmode, "");

   /* read result of initialization */

   errorcode = graphresult();    /* an error occurred */

   if (errorcode != grOk)

   {

 printf("Graphics error: %s\n", grapherrormsg(errorcode));

 printf("Press any key to halt:");

 getch();

 exit(1);    /* terminate with an error code */

   }

   midx = getmaxx() / 2;

   midy = getmaxy() / 2;

   setcolor(getmaxcolor());

   /* draw arc */

   arc(midx, midy, stangle, endangle, radius);

   /* clean up */

   getch();

   closegraph();

   return 0;

}

void far bar(int left,int top,int right,int bottom)

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

bar3d-рисует параллелепипед

void far circle(int x,int y,int radius)-окружность

void far cleardevice(void)-очистка графического экрана

void far clearviewport(void)-очистка текущего графического окна

void far closegraph(void)-освобождает память и восстанавливает

режим,который был до вызова initgraph

void far detectgraph(int far *graphdriver,int far *graphmode)-

определяет графический адаптер и выбирает графический режим

void far drawpoly(int numpoints,int far *polypoints)-рисует

ломаную

void far ellipse(int x,int y,int stangle,intendangle,

int xradius,int yradius)-рисует эллиптическую дугу

void far fillellipse(int x,int y,int xradius,int yradius)-

рисует и заливает эллипс текущим цветом и текущим образцом

fillpoly,floodfill-тоже функции заполнения

getarccoords(struct arccoordstype far *arccoords)-получает

коорди- наты последнего вызова arc ;заполняемая структура:

      struct arccoordstype

      {int x,y;

      int xstart,ystart,xend,yend};

getaspectratio(int far   *xasp,int   far   *yasp)-получает

аспект текущего графического режима

getbkcolor(void)-текущий фоновый цвет

getcolor(void)-текущий цвет рисования

struct palettetype *far getdefaultpalette-возвращает структуру

опре- деления палитры,инициализированной драйвером привызове

initgraph

getdrivername(void)-возвращает указатель   на   строку   с

именем текущего графического драйвера

getfillpattern(char far   *pattern)-копирует  в  память  по  адресу

pattern определенный пользователем образец заполнения:

char checkboard[8]={0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55};

getfillsettings(struct fillsettingstype far *fillinfo)-получает

информацию о текущем образце заполнения и цвете

getgraphmode(void)-текущий графический режим

getimage(int left,int    top,int    right,int    bottom,void

far *bitmap)-сохраняет в памяти битовый образ указанной области

экрана

getlinesettings(stuct linesettingstype    far

*lineinfo)-текущий стиль,образец и толщира линии в lineinfo:

      struct linesettingstype{

      int linestyle;

      unsigned upattern;

      int thickness;};

getmaxcolor(void)-максимальное значение цвета для setcolor

getmaxmode(void)-максим номер режима для текущегодрайвера

getmaxx(void)-максим х-координата

getmaxy

char *far    getmodename(int    mode_number)-имя    заданного

      графического режима

getmoderange(int graphdriver,int    far    *lomode,int    far

      himode)-наименьший и наибольший режим для данного графическо-

      го устройства

getpalette(struct palettetype     far     *palette)-заполняет

структуру информацией о текущей палитре:

struct palettetype{

      unsigned char size;

      signed char colors[MAXCOLORS+1];}]

getpalettesize(void)

getpixel(int x,int y)-цвет пиксела

gettextsettings(struct textsettingstype far *texttypeinfo)-

ирформация о текущем графическом шрифте:

      struct textsettingstype{

      int font;

      int direction;

      int charsize;

      int horiz;

      int vert;};

getviewsettings(struct viewporttype  *viewport)-информация  о

текущем окне:

      struct viewporttype{

      int left,top,right,bottom;

      int clip;};

getx,gety

graphdefaults(void)-все графические  установки-в  значение по

умолчанию

char *far grapherrormsg(int  errorcode)-возвращает  указатель

на строку сообщения об ошибке, соответствующей коду,

возвращаемому graphresult

graphfreemem(void far     *ptr,unsigned     size)-освободить

память,ранее распределенную через graphgetmem

void far *far _graphgetmem(unsigned size)-выделитьпамять

graphresult(void)-возвращает код    ошибки    для   последней

неудачной графической операции

unsigned far imagesize(int left,int top,int right,int bottom)-

количество байтов для хранения битового образа

initgraph(int far *graphdriver,int far graphmode,char far *path

todriver-инициализация графической системы

installuserdriver(char far *name,inthuge(*detect)(void))-

помещает драйвер устройства в таблицу BGI драйверов устройств

installuserfont(char far  *name)-загружает  файл шрифта (.CHR),

 который не встроен в систему

line(int x1,int y1,int x2,int y2)-линия между 2-мя точками

linerel(int __dx, int __dy);-линия из текущей позиции

lineto(int __x, int __y);-линия из текущей позиции в заданную

точку

moverel(int __dx,  int __dy);-перемещение  курсора  на

относительное расстояние

moveto(int __x, int __y);-перенос позиции курсора в заданную

точку

outtext(char far *__textstring);-вывод строки в текущем окне

outtextxy(int __x,  int __y,  char far *__textstring);-вывод

строки в указанной позиции

pieslice(int __x, int __y, int __stangle, int __endangle,

int  __radius);-рисование и заполнеение сектора

putimage(int __left, int __top, void far *__bitmap, int __op);-

вывод битового образа на экран;параметр op может быть:

                         COPY_PUT,          /* MOV */

                         XOR_PUT,           /* XOR */

                         OR_PUT,            /* OR  */

                         AND_PUT,           /* AND */

                         NOT_PUT            /* NOT */

putpixel(int __x, int __y, int __color);

rectangle(int __left, int __top, int __right, int __bottom);-

прямоугольник

registerbgidriver

registerbgifont

restorecrtmode(void)-возврат экрана к режиму,установленному

graphinit

sector( int __X, int __Y, int __StAngle, int __EndAngle,

int   __XRadius, int __YRadius );

setallpalette(struct palettetype  far *__palette);-изменяет все

цвета палитры на заданные

setaspectratio( int __xasp, int __yasp );-c;-сжатие по осям

setbkcolor(int __color);-цвет фона

setcolor(int __color);

setfillpattern(char far *__upattern,  int __color);-установка

образца заполнения

setfillstyle(int __pattern, int __color);-цвети образец

заполнения:

enum fill_patterns { /* Fill patterns for get/setfillstyle */

EMPTY_FILL,          /* fills area in background color */

SOLID_FILL,          /* fills area in solid fill color */

LINE_FILL,           /* --- fill */

LTSLASH_FILL,        /* /// fill */

SLASH_FILL,          /* /// fill with thick lines */

BKSLASH_FILL,        /* \\\ fill with thick lines */

LTBKSLASH_FILL,      /* \\\ fill */

HATCH_FILL,          /* light hatch fill */

XHATCH_FILL,         /* heavy cross hatch fill */

INTERLEAVE_FILL,     /* interleaving line fill */

WIDE_DOT_FILL,       /* Widely spaced dot fill */

CLOSE_DOT_FILL,      /* Closely spaced dot fill */

USER_FILL            /* user defined fill */

      };

setgraphbufsize(unsigned __bufsize);-изменение  размера

внутреннего графического буфера

setgraphmode(int __mode);-установкарежима с очисткой экрана

setlinestyle(int __linestyle, unsigned __upattern,

                   int __thickness);

enum line_styles { /* Line styles for get/setlinestyle */

     SOLID_LINE   = 0,

     DOTTED_LINE  = 1,

     CENTER_LINE  = 2,

     DASHED_LINE  = 3,

     USERBIT_LINE = 4,        /* User defined line style */

};

 enum line_widths { /* Line widths for get/setlinestyle */

     NORM_WIDTH  = 1,

     THICK_WIDTH = 3,

};

setpalette(int __colornum, int __color);-изменение 1-го из

 цветов палитры:

   enum{

    EGA_COLORS { EGA_BLACK = 0,   /* dark colors */

    EGA_BLUE             =  1,

    EGA_GREEN            =  2,

    EGA_CYAN             =  3,

    EGA_RED              =  4,

    EGA_MAGENTA          =  5,

    EGA_BROWN            =  20,

    EGA_LIGHTGRAY         =  7,

    EGA_DARKGRAY         =  56,     /* light colors */

    EGA_LIGHTBLUE         =  57,

    EGA_LIGHTGREEN         =  58,

    EGA_LIGHTCYAN         =  59,

    EGA_LIGHTRED         =  60,

    EGA_LIGHTMAGENTA         =  61,

    EGA_YELLOW           =  62,

    EGA_WHITE            =  63

};

setrgbpalette(int __colornum,int __red, int __green,

int __blue);

settextjustify(int __horiz, int __vert);-тип выравнивания

текста:

settextstyle(int __font, int __direction, int __charsize);

setusercharsize(int __multx, int __divx,

                     int __multy, int __divy);

setviewport(int __left, int __top, int __right, int __bottom,

                  int __clip);

setvisualpage( int __mode );(int __page);

setwritemode( int __mode );

textheight(char far *__textstring);

textwidth(char far *__textstring);


Библиотеки модулей в Турбо Паскале

.Структура модулей.

 Модуль (UNIT) в Паскале - это совокупность определений типов,

 констант, переменных и подпрограмм, оформленная специальным

 образом. Модуль является единицей компиляции в Паскале, как и

 программа, но в отличие от программы он не может быть запущен

 на выполнение самостоятельно - его компоненты предназначены для

 построения программ или других модулей. Если любые фрагменты

 текстов программ, размещенные в других файлах, могут

 подключаться к текущей программе директивой {$I ИмяФайла}, то

 модули предварительно компилируются независимо от использующих

 их программ с получением файла с расширением .TPU. Чтобы

 включить содержащиеся в модуле подпрограммы в любую программу,

 достаточно указать его имя в директиве USES:

USES DOS,GRAPH;

и далее свободно использовать содержимое соответствующей

библиотеки путем например вызова на выполнение необходимых

подпрограмм.

Модули удобны как для создания собственных библиотек, так и для

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

используемых в программе модулей ограничена лишь физическим

объемом памяти компьютера, да и то если не используется

оверлейная структура. Общая структура модуля имеет вид:

UNIT ИмяМодуля;

INTERFACE        {раздел объявлений или интерфейсный}

USES  СписокМодулей;

CONST            {Блок объявления библиотечных констант}

TYPE             {Типов}

VAR              {Переменных}

Заголовки подпрограмм, входящих в модуль

IMPLEMENTATION   {Раздел реализации модуля}

USES

CONST

TYPE

VAR

LABEL

BEGIN

   Блок инициализации модуля.

END.

Все блоки внутри разделов необязательны. Если отсутствует раздел

инициализации, то после тела последней подпрограммы просто

ставится END. В этом разделе формируются стартовые значения

переменных и какие - нибудь одноразовые действия. Выполнение

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

используемых ею модулей. Подключение модулей происходит в

порядке их перечисления, в этом же порядке работают и их блоки

инициализации, поэтому порядок подключения модулей может

оказаться небезразличным.В случае возможности совпадения имен в

разных модулях и модулях и программе развязку можно осуществить

указанием имени модуля перед именами переменных или подпрограмм

через точку:

U1.t, U1.proc8(...);

Возможно возникновение "закольцованности" при взаимном

использовании модулей в разделе INTERFACE. Проблема решается в

этом случае выносом всех ссылающихся друг на друга элементов из

обеих модулей в третий модуль с подключением его к программе.

Модуль может не содержать подпрограмм, а состоять только из

описаний типов, констант, переменных:

UNIT Common;

INTERFACE

ComArrayType = Array[0..99] of LongInt;

CONST

MaxSize = 100;

VAR

ComArray : ComArrayType;

Flag     : Byte;

IMPLEMENTATION

END.

 Система библиотечных модулей Паскаля.

В Турбо Паскале определен ряд стандартных модулей TPU,

обеспечивающих функции ввода - вывода, работы со строками,

управления экраном дисплея, принтером и другими ресурсами

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

только для оригинальных фрагментов текста программы, а вызовы

библиотечных функций на стадии компоновки вызывают включение

в код компонентов библиотек.

Подпрограммы обработки символов.

Они содержатся в библиотеке поддержки языка TURBO.TPL.

Chr( X: Byte) : Char  {Возвращает символ ASCII - кодда Х}

Ord(C: Char) : Byte   {Возвращает ASCII - код символа С}

Pred(C: Char) : Char  {Выдает предшествующий символ в алфавите}

Succ(C: Char) : Char  {Следующий символ}

UpCase(C: char) : Char{Переводит символы латиницы в верхний

регистр}

 Подпрограммы обработки строк.

Length(S: String) : Byte {Возвращает длину строки}

Concat(S1,S2,..,Sn) : String {Возвращает слитую строку}

Copy(S:String;Start,Len:ineger) : String {Возвращает подстроку

длиной Len, начинающуюся с позиции Start строки S}

Delete(VAR S:String;Start,Len:Integer) {Удаляет подстроку}

Insert(VAR S:String;Subs:String;Start:Integer) {Вставляет

подстроку}

Pos(SubS,S:String):Byte       {Возвращает номер первого символа

SubS в S или 0}

Str(X[ :F[:n]]; VAR S:String) {Преобразует число Х в строку S}

Val(S:String;VAR X; VAR ErrCode: Integer) {Преобразует строку S

в число Х}

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

Abs(X)     {Убирает знак минус у целого или

вещественного аргумента}

Pi

Sin(X), Cos(X), ArcTan(X), Sqrt(X), Sqr(X), Exp(X), Ln(X)

Ttunc(X)            {Целая часть}

Frac(X)             {Дробная часть}

Int(X)              {Целая часть}

Round(X)            {Округление до ближайшего целого}

Random              {Случайное число 0..1}

Randomize           {Случ старт генераторов случ чисел}

Random(X)           {Случайное Word 0..X}

Odd(X)              {True если Х нечетный}

Inc(VAR X:Целое),Inc(VAR X:Целое;N:Целое),

Dec(VARX:Целое),Dec(VAR X:Целое;N:Целое)    {Увеличение или

уменьшение на 1 или на N}

 Подпрограммы для работы с адресами.

CSeg:Word, DSeg:Word,  SSeg:Word, Sptr:Word {Возвращают

содержимое сегментных регистров процессора CS,DS,SS,SP}

Addr(X) :Pointer  {Возвращает указатель на начало объекта Х в

памяти}

Seg(X):Word       {Сегментный адрес объекта Х}

Ofs(X):Word       {Смещение в сегменте для Х}

Ptr(S,O:Word):Pointer  {Сборка указателя по сегменту и смещению}

SizeOf(X):Word    {Размер объекта в байтах}

@X:Pointer        {Аналог Addr}

 Подпрограммы работы с "кучей"

Управление кучей осуществляет монитор кучи, являющийся частью

ситемной библиотеки Турбо Паскаля и реализующий следующие

подпрограммы:

New(VAR P :Pointer) {Отводит место для переменной P^ и

присваивает ее адрес указателю P}

Dispose(VAR P:pointer) {Освобождает память, отведенную New}

GetMem(VAR P: Pointer; Size:Word) {Отводит Size байтов в куче

присваивая адрес места P}

FreeMem(VAR P: Pointer;Size :Word) {Освобождает то, что было

выделено GetMem}

Mark(VAR P:Pointer) {Запоминает по адресу Р текущее состояние

кучи}

Release(VAR P:Pointer) {Возвращает кучу в состояние,

запомненное в Р через Mark}

New(Тип Указателя):Pointer {Альтернативная форма создания

динамической переменной типа заданного базового типа}

MaxAvail:LongInt    {Самый длинный свободный участок в куче}

MemAvail:LongInt    {Сумма длин всех свободных кусков кучи}

Подпрограммы для работы с файлами.

Турбо Паскаль поддерживает так называемый буферизованный обмен с

файлами - чере область оперативной памяти, отводимую при

открытии файла. Поэтому при записи в файл физическое перемещение

информации на диск произойдет либо после заполнения всего

буфера, либо после закрытия файла.

Турбо Паскаль вводит и поддерживает 3 файловых типа:

-текстовые файлы (тип Text);

-компонентные файлы (тип File of ТипКомпонетов);

-бестиповые файлы (тип File);

Перед началом работы с файлом в памяти должна быть размещена

переменная одного из перечисленных файловых типов:

ft1:Text;

fk1:File of Real;

fb1:File;

Подпрограммы, общие для всех типов файлов:

Assign(VAR f; FileName:String)  {Связывает файловую переменную

f c именем FileName физического файла}

Reset(VAR f)    {Открытие файла для чтения}

Rewrite(VAR f)  {Открытие файла для записи}

Close(VAR f)    {Закрытие открытого файла}

Rename(VAR f;NewName :String)    {Переименовывает

только закрытый файл}

Erase(VAR f )   {Стирает только закрытый физический файл }

EOF(VAR f):Boolean  {True если достигнут конец файла}

Подпрограммы для работы с текстовыми файлами (ASCII-коды, конец

строки - #13, конец файла - ^Z - код 26, автоматическое

преобразование в символьную форму при записи в файл и в

машинную форму при чтении из файла)

SetTextBuf(VAR f:Text; VAR Buf[;BufSize:word])  {Установка

буфера перед открытием файла}

Append(VAR f:Text) {Открытие для дополнения в конец файла}

Flush(VAR f:Text)  {Перепись буфера в файл до полного

заполнения}

EOLn(VAR f:Text):Boolean  {True если конец строки или конец

файла}

SeekEOLn(VAR f:Text):Boolean  {True если после указателя только

пробелы, табуляторы или символы конца строк}

SeekEOF(VAR f:Text):Boolean   {True если конец файла или после

указателя нет ничего значащего }

Read(f,X),Read(f,X!,X2...Xn),ReadLn {Чтение полных или

частичных строк}

Write(f,X),Write(f,X1,X2..Xn),WriteLn {Запись в файл}

Типизированные (компонентные) файлы.

Процедуры Reset и Rewrite - обе устанавливают для типизированных

и бестиповых файлов режим "для чтения и записи" по значению 2

предопределенной системной переменной FileMode. Чтобы установить

"только запись" этой переменной необходимо присвоить значение 1,

а для "только чтение" - 0.

После открытия файла ввод и вывод осуществляются стандартными

подпрограммами Read(f,X) и Write(f,X) - только переменная Х или

их список того же типа, что и тип файлового компонента. К

типизированным файлам нет смысла применять ReadLn или WriteLn -

в них просто нет понятия строки или ее конца и нет признака

конца файла.

Преимуществом типизированных файлов является эффективное

хранение числовой информации и возможность обмена сложными и

громоздкими структурами одной командой:

TYPE dim100x20 = Array[1..100,1..20] of Real;

VAR XX,YY:dim100x20; fIn,fOut:File of dim100x20;

begin

...

Read(fIn,XX); ... Write(fOut,YY);

end.

 Бестиповые файлы.

С их помощью можно осуществлять обмен с дисками участками

рабочей памяти произвольной длины, а считанные из файла данные

можно преобразовывать в любой фориат посредством приведения

типов. При открытии файла буфер передачи данных устанавливается

неявно в 128 байт. Но явно его можно установить любой размер при

открытии файла расшиенной записью процедур:

Reset (VAR f:File;BufSize:Word)  или

Rewrite(VARf:File;BufSize:Word)

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

BlockRead(VAR f:File;VAR Destin;Count:Word [;VAR ReadIn:Word])

BlockWrite(VAR f:File;VAR Source;Count:Word[;VARWriteOut:Word])

Чтение и запись Count блоков размером буфера каждый.

Необязательные параметры содержат число фактически считанных или

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

правильности выполнения процедур.

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

FileSize(VAR f):LongInt  {размер файлав компонентах или блоках}

FilePos(VAR f):LongInt  {Номер последнего уже обработанного

компонента или блка}

Seek(VAR f;N:LongInt)   {Установка текущего компонента или

блока считая от 0}

Truncate(VAR f)         {Усечение файла после текущей позиции}

 Подпрограммы для работы с каталогами.

GetDir(drive:Byte;VAR S:String) {Имя текущего каталога на диске

drive}

ChDir(S:String)                 {Установка текущего каталога}

MkDir(S:String)                 {Создание каталога}

RmDir(S:String)                 {Удаление пустого каталога}

Обработка ошибок ввода - вывода может быть выполнена в

нашей программе при отключении системной проверки ключем {$I-}

и использовании функции IOResult:integer, возвращающей код

ошибки в диапазоне 2..200 (2..99 - коды ошибок DOS, 100..149 -

диагностируемые программой, 150..200 - критические аппаратные)

 Модуль CRT.

Содержит спецциальные подпрограммы для работы с текстами на

дисплее, организовывать окна вывода на экран, настраивать цвета

символов, управлять курсором, динамиком, опрашивать

клавиатуру.Модуль размещен в системной библиотеке TURBO.TPL

Этот модуль есть смысл подключать всегда директивой USES CRT,

так как его блок инициализации переключаетвывод непосредственно

через видеопамять, минуя DOS и BIOS.

При подключенном CRT можно выводить на дисплей управляющие

символы с кодами 0..31 без выполнения ими управляющих функций

(кроме сиволов с кодами 7,8,10,13).

Во-вторых расширяются возможности редактирования строк при

использовании подпрограмм Read , ReadLn:

Esc - Стирает набранную строку

^D  - Восстанавливает символ из стертой строки ввода

^F  - Восстанавливает всю стертую строку

^Z  - Завершает ввод и вырабатывает признак конца файла, если

системная переменная CheckEOF = True

Переменные CRT:

CheckSnow:Boolean     {Снятие снега на CGA-дисплеях}

DirectVideo:Boolean   {Максимальная скорость вывода на экран}

CheckBreak:Boolean    {Установка ^Break }

CheckEOF:Boolean      {Разрешение ввода ^Z как конца файла}

TextAttr:Byte         {Установка цвета букв и фона}

LastMode:Word         {Для работы с п-й TextMode}

WindMax,WindMin:Word  {Параметры текущего окна}

Подпограммы CRT:

Window(X1,Y1,X2,Y2:Byte) {Задание текущего окна}

ClrScr                   {Очистка окна}

TextMode(M:Word)         {Установка текстового режима}

GotoXY(X,Y:Byte)         {Установка курсора}

WhereX:Byte,WhereY:Byte  {Номер столбца и строки курсора}

ClrEOL                   {Стирание строки от тек позиции}

InsLine                  {Вставка пустой строки вместо текущей}

DelLine                  {Удаление строки}

TextColor(C:Byte)        {Цвет символов}

TextBackGround(C:Byte)   {Цвет фона}

HighVideo                {Яркость включить}

LowVideo                 {Яркость отключить}

NormVideo                {Восстановить цветовой режим}

Sound(Hz:Word)           {Звук частотой Hz герц}

NoSound                  {Выключить звук}

Delay(ms:Word)           {Пауза в мс}

KeyPressed:Boolean       {Нажата ли клавиша}

ReadKey:Char             {Символ нажатой клавиши}

AssignCRT(VAR f:Text)    {Связать текстовый файл с CRT}

Модуль DOS.

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

с операционной системой MS DOS, многие из них - это просто

оформленные в Паскалевском синтаксисе вызовы подпрограмм MS DOS,

тарнслируемые в команды

                  INT НомерПрерывания

после заполнения регистроов номерами подпрограмм и необходимыми

для выполнения данными.

Подпрограммы опроса и установки параметров MS DOS.

DOSVersion:WORD             {Возвращает код версии MS DOS}

GetCBreak(VAR B:Boolean)    {Читать пармеетр BREAK}

SetCBreak(B:Boolean)        {Установить параметр BREAK}

GetVerify(V:Boolean)        {Читать параметр VERIFY,

                            управляющий режимом записи на диск}

SetVerify(V:Boolean)        {Установит парметр VERIFY}

EnvCount:Integer            {Число ситемных переменных ОС -

        имена и значения,заданные командами SET,PATH,PROMPT}

EnvString(N:Integer)        {Получить строку переменной номер N}

GetEnv(E:String):String     {Значение переменной Е}

Подпрограммы для работы с часами и календарем.

GetDate, SetDate, GetTime, SetTime, PackTime, UnPackTime,

GetFTime, SetFTime - мы приводим только названия подпрограмм, а

полный синтаксис их использования вы найдете в Help - системе

Турбо Паскаля

Анализ ресурсов дисков.

DiskFree(D:Word):LongInt {Размер свободного пространства на

диске c кодом D : 0 - текущий, 1 - диск А, 2 - диск В и т д}

DiskSize(D:Word)         {Общая вместимость диска}

Подпрограммы для работы с каталогами и файлами.

Для работы с файлами вводятся специальные типы и константы:

CONST

ReadOnly = $01;Hidden   = $02;SysFile  = $04; VolumeId = $08;

Directory = $10; Archive = $20; AnyFile = $3F;

{При использовании эти константы можно складывать.}

fmClosed = $D7B0;  {Файл закрыт}

fmInput =$D7B1; fmOutPut = $D7B2; fmInOut = $D7B3;

TYPE

SearchString = RECORD

Fill:Array[1..21] of Byte;   {Системное поле}

Attr:Byte; Time: LongInt; Size: LongInt; Name: String[12];

               END;

FileRec = RECORD

Handle :Word;

Mode:Word;  {Состояние файла}

RecSize :Word; {Длина записи компонентного файла}

Private :Array[1..26] of Byte; {Не используется}

UserData :Array[1..16] of Byte; {Любой комментарий к файлу}

Name :Array[0..79] of char; {Полное имя дискового файла }

TextBuf = Array[0..127] of char;

TextRec = RECORD

Handle :Word;

Mode:Word;  {Состояние файла}

BufSize :Word;

Private :Array[1..26] of Byte; {Не используется}

BufPos:Word; BufEnd: Word; BufPtr :^TextBuf;

OpenFunc : Pointer; InOutFunc: Pointer;

FlushFunc :Pointer; CloseFunc: Pointer;{Адреса драйверов}

UserData :Array[1..16] of Byte; {Любой комментарий к файлу}

Name :Array[0..79] of char; {Полное имя дискового файла }

Buffer :TextBuf;

END;

Переменная DosError типа Integer содержит номер ошибки,

возвращаемой MS DOS при неправильной операции; 0 - отсутствие

ошибки, остальные расшифровки мы приводить не будем.

Подпрограммы:

FindFirst(Path:String;Attr:Word;VAR SR:SearchRec)  {Поиск

первого файла подходящего запросу}

FindNext(VAR SR  : SearchString) {Вызывается после FindFirst

для поска следующего подходящего}

FSearch(Path :PathStr; DirList :String) : PathStr  {Поик файла

в списке каталогов}

GetFAttr(VAR f: File; VAR FA :Word)

SetFAttr(VAR f: File; VAR FA :Word)

FSplit(Path: PathStr; VAR Dir : DirStr; VAR Name : NameStr;

VAR Ext : ExtStr)   {Разбить полное имя на составляющие}

FExpand(Path :pathStr): PathStr  {Приписать к имени текущий

маршрут}

Подпрограммы для работы с прерываниями MS DOS.

В операционной системе вызов ее услуг реализован в виде

подпрограмм, активизируемых по команде прерывания процессора

              INT НомерПрерывания

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

комплекса подпрограмм. Комплексы сгруппированы в основном по

аппаратному признаку: прерывание с номером 16Н дает доступ к

подпрограммам опроса клавиатуры на уровне операционной системы,

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

д. Особую роль играет прерывание 21Н, открывающее доступ к

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

операционную систему.

В работе на машинно - ориентированном языке Ассемблера,

прграммист для вызова подпрограмм ОС должен загрузить в регистры

процессора номер подпрограммы и ее аргументы и вызвать

прерывание, открывающее досту к этой подпрограмме. В Турбо

Паскалевском модуле DOS есть подпрограммы  поддержки

прямых обращений к подпрограммам DOS:

GetIntVec(N:Byte;VAR Adress:Pointer)  {Адрес подпрограммы

прерывания с заданным номером N}

SetIntVec(N:Byte;Adress:Pointer)      {Установить по прерыванию

N подпрограмму с адресом Adress}

Intr(N:Byte; VAR R:Registers)         {Вызвать прерывание N ,

передав номер подпрограммы и параметры в записи R}

MsDos(VAR R:Registers)                {Специализированный вызов

прерывания 21Н}

Новый тип Registers определен в модуле DOS как вариантная

запись:

TYPE Registers = RECORD

case Integer of

0:(AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:Word);

1:(AL,AH,BL,BH,CL,CH,DL,DH:Byte);

                 END;

Переменные этого типа служат для доступа к регистрам процессора

при вызовах прерываний, причем вариант 0 открывает доступ к 16 -

разрядным регистрам, а вариант 1 - к 8 - разрядным:

USES DOS;

VAR R1,R2 :Registers;

begin R1.AX:=$01FF; R2.BL:=$CA; ... END.

Организация субпроцессов и резидентных программ.

MS DOS имеет мощные средства организации субпроцессов и Турбо

Паскаль предоставляет подпрограммы для доступа к этим средствам.

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

ней замена содержимого ряда системных векторов прерываний, в

частности это делает среда Турбо Паскаля, запоминая заменяемые

векторы в специальных переменных типа Pointer. При запуске

субпроцесса разумно предоставить ему возможность использовать

системные векторы, а не замененные. Подпрграмма SwapVectors

восстанавливает сохраненные в переменных с именами SaveIntNN

векторы, записывая туда же текущие, поэтому при повторном вызове

подпрограмма снова восстановит отключенные подпрограммы

прерываний - ее используют до и после подпрогрммы Exec.

SwapVectors      {Восстановление системных или временных

векторов прерываний до и после запуска субпроцессов.0 -

нормальное завершение}

Exec(ExeFile,ComLine:String)  {Запуск выполняемого файла

ExeFile (субпроцесс) со строкой параметров ComLine}

DosExitCode: Word   {Возвращает код завершения субпроцесса}

Keep(ExitCode: Word) {Завершить и оставить резидентной}


Системные вызовы в Ассемблере

Любая наша программа содержит операторы 2-х типов:

1- связанные непосредственно с решением конкретной прикладной

задачи;

2- связанные с управлением аппаратными ресурсами компьютера;

Конечно в программе операторы обеих групп могут чередоваться в

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

стандартизации из - за большого разнообразия возможных областей

приложения. Ресурсы же  компьютера всегда ограничены, управление

ими достаточно сложно, поэтому и операционная система и

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

предоставляют обширный сервис по управлению устройствами -

оперативной и внешней памятью, клавиатурой и портами

коммуникаций, дисплеем и печатающими устройствами - в общем

всеми компьютерными ресурсами.

1.Программная модель микропроцессора -

это видение программистом аппаратных средств компьютера.

Основным исполняющим звеном ПК является его центральный

процессор, выполняющий команды, хранимые в оперативной памяти и

выбираемые оттуда по мере необходимости. Каждая команда

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

внутренних ячеек "сверхоперативной" памяти - регистров, каждому

из них принято присваивать имя для облегчения общения. Условно

все регистры можно разделить на 3 группы:

1.регистры общего назначения AX,BX,CX,DX - используются для

промежуточного хранения операндов ;

2.регистры - указатели DI,SI,BP,SP,IP;

IP - задает смещение физического адреса в памяти относительно

сегментного адреса в регистре CS;

SI и DI используются для смещения адресов относительно сегментов

в регистрах DS и ES;

SP и BP - обычно в паре с регистром SS;

3.сегментные регистры

CS,DS,ES,SS.

4.Регистр флагов - содержит слово состояния процессора

Физический адрес любого элемента в памяти образуется так:

значение сегмента адреса сдвигается на 4 бита влево с

заполнением правых разрядов нулями - это соответствует умножнию

сегментного адреса на 16; к полученному 20 - битовому значению

прибавляется 16 - битовое смещение. Например адрес 40:1СН

соответствует физическому адресу 0041СН:

     00400Н   - после сдвига 40Н влево

+     001СН

 __________

     0041СН

Один и тот же физический адрес может быть получен из самых

разных комбинаций сегмента и смещения. При формировании

физического адреса в качестве сегментной части процессор

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

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

нет команд непосредственной засылки значений в сегментные

регистры или обмена их содержимого с памятью - поэтому

заполнение сегментных регистров из программы осуществляется с

предварительным размещением этих значений в регистрах общего

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

2.Прерывания - это набор аппаратных средств временной

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

обслуживания запросов, поступающих в виде сигналов в

непредсказуемые моменты времени с последующим возвратом в точку

прерывания для продолжения приостановленной программы.

Прцессоры семейства Intel 8086 поддерживают 256 прерываний,

каждое из них имеет свой номер. Адрес точки входа в подпрограмму

обслуживания называют вектором прерывания и хранится в

специальной таблице векторов прерываний. Каждый вектор

прерывания занимает 4 байта : 2 для значения сегментного адреса,

устанавливаемого в CS, и 2 для смещения в IP. Вся таблица

занимает 256*4=1024 байт и располагается в оперативной памяти

начиная с адреса 0000:0000.

При возникновении прерываний процессор запоминает в стеке

текущее значение CS и IP, заносит в CS и IP значения из таблицы

векторов прерываний, то есть переходит к подпрограмме

обслуживания прерывания.

Подрограммы обслуживания прерываний строятся с соблюдением

специальных правил, требующих в частности запоминания всех

регистров, которые могут быть изменены в процессе ее выполнения,

восстановления запомненных при входе регистро перед выходом и

выходом с помощью специальной команды IRET вместо обычной RET

для обычных подпрограмм. Для передачи в обработчик прерываний

каких либо значений и получения от них каких либо результатов

используют внутренние регистры процессора.

Некоторые векторы прерываний могут использоваться не для адресов

подпрограмм обслуживания прерываний , а для хранения важной

системной информации: адресов данных, таблиц. За некоторые

векторы "зацеплены" подпрограммы - пустышки для заполнения их

пользовательскими обработчиками прерываний - они содержат

единственную инструкцию выхода IRET - таким например является

вектор 1СН, вызываемый по каждому "тику" таймера 18.2 раза в

секунду.

Когда в программе встречается команда INT, процессор начинает

действовать так, как будто произошло аппаратное прерывание с

номером, указанным в команде INT - переходит к выполнению так

называемого программного прерывания, имеющего более высокий

приоритет, чем все аппаратные и немаскируемые прерывания -

выполнение подпрограммы по соответствующему вектору не может

быть прервано до полного завершения. Многие прграммные

прерывания используются таким образом для доступа к

подпрограммам BIOS, DOS или инсталлируемым драйверам устройств.

Для обслуживания аппаратных пррываний и драйверов BIOS

закреплены первые 20Н прерываний с номерами от 00Н до 1FH, а

прерывания с номерами 20Н и старше обрабатываются подпрограммами

ядра DOS. Способ вызова этих подпрограмм стандартизован и

приводится в технической документации.

Ассемблер

В ассемблере для общения с регистрами используется команда

пересылки :

        mov ax,DATASEG

        mov ds,ax

        и пр

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

INT:

        mov ah,40h

        int 21h  и пр


Системные вызовы в Си

Си поддерживает удобный механизм доступа к внутренним регистрам

процессора через псевдоперемеенные, ссылка на которые

транслируется в ссылку на регистр. Имена этих псевдопеременных:

_AX, _AL, _AH, _BX, BL, _BH, _CX, _CL, _CH, _DX, _DL, _DH,

_SI, _DI, _BP, _SP,    _CS, _ES, _DS, _SS

Чтобы узнать например текущее содержимое сегментных регистров,

следует написать:

...

unsigned code_seg, data_seg, stack_seg, es_seg;

...

code_seg=_CS; data_seg=_DS; stack_seg=_SS; es_seg=_ES

Псевдопеременные можно использовать и в левой части оператора

присваивания, учитывая , что нельзя непосредственно присваивать

значения регистрам CS,IP, что изменение значений сегментных

регистров может приводить к непредсказуемым последствиям и что

установленные в регистры значения могут оказаться неявно

переопределенными последующими операторами программы. При

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

использование регистровых переменных и регистровую оптимизацию в

опциях компилятора.

Для получения значений сегментных регистров можно также

использовать специальную библиотечную функцию :

#include<doos.h>

void segread(struct SREGS * segp)

которая заполняет значениями сегментныхх регистров поля

структурной переменной с шаблоном SREGS:

struct SREGS{

unsigned int es;

unsigned int cs;

unsigned int ss;

unsigned int ds;};

Для установки требуемого значения флага прерывания в регистре

флагов можно использовать макро enable() для разрешения всех

маскируемых аппаратных прерываний и disable() - для запрещения.

При необходимости прямого обращения к услугам BIOS  или DOS из

СИ - прграммы используют специальные библиотечные функции:

#include<dos.h>

int bdos(int ah, unsigned dx, unsigned al) - выполняет

прерывание 21Н с заданными значениями в ah,dx и al, возвращает

значение в регистре АХ.

void geninterrupt(int intr_num) - макро, выполняющий обращение к

обработчику прерывания с заданным номером; обмен данными с

регистрами при его использовании выполняется через

псевдопеременные.

int int86(int intno,union REGS *inregs, union REGS * outregs) -

заполняет регистры из inregs, выполняет прерывание intno,

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

объединения outregs. Шаблон REGS:

struct WORDREGS{ unsigned int ax,bx,cx,dx,si,di,cflag,flags;};

struct BYTEREGS{unsigned char al,ah,bl,bh,cl,ch,dl,dh};

union REGS{struct WORDREGS x; struct BYTEREGS h;};

Возвращаемое значение следует искать в регистре АХ.

int86x(int intno,union REGS *inregs,union REGS * outregs,struct

SREGS * segregs) - в отличие от int86() перед выполнением

прерывания заполняет сегментные регистры из структурной

переменной по шаблону SREGS.  При выходе в segregs копируются

значения сегментных регистров.

int intdos(union REGS *inregs, union REGS * outregs) - всегда

генерируется прерывание 21Н, поэтому номер прерывания

отсутствует среди параметров

void intr(int intno, struct REGPACK *preg) - загружает регистры

из переменной preg и генерирует прерывание intno, а на выходе в

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

REGPACK:

struct REGPACK{unsigned r_ax,r_bx,r_cx,r_dx;

               usigned r_bp,r_di,r_ds,r_es,r_flags};


Системные вызовы в Турбо Паскале

В Паскалевском модуле DOS есть подпрограммы  поддержки

прямых обращений к подпрограммам DOS:

GetIntVec(N:Byte;VAR Adress:Pointer)  {Адрес подпрограммы

прерывания с заданным номером N}

SetIntVec(N:Byte;Adress:Pointer)      {Установить по прерыванию

N подпрограмму с адресом Adress}

Intr(N:Byte; VAR R:Registers)         {Вызвать прерывание N ,

передав номер подпрограммы и параметры в записи R}

MsDos(VAR R:Registers)                {Специализированный вызов

прерывания 21Н}

Новый тип Registers определен в модуле DOS как вариантная

запись:

TYPE Registers = RECORD

case Integer of

0:(AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags:Word);

1:(AL,AH,BL,BH,CL,CH,DL,DH:Byte);

                 END;

Переменные этого типа служат для доступа к регистрам процессора

при вызовах прерываний, причем вариант 0 открывает доступ к 16 -

разрядным регистрам, а вариант 1 - к 8 - разрядным:

USES DOS;

VAR R1,R2 :Registers;

begin R1.AX:=$01FF; R2.BL:=$CA; ... END.


Обработка хвоста командной строки в Ассемблере

Мы продолжим изучение этой темы  разделом, который прямо не

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

командной строки запускаемого на выполнение exe - файла.

"Хвостом" командной строки называют все слова, которые должен

(или может) ввести пользователь после имени исполняемого файла

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

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

файла с конкретными данными, количество строк и столбцов в

таблице для программ обработки таблиц, для простых и коротких

данных это могут быть и сами данные - например номер

интересующего пользователя члена числового ряда для программ

вычисления членов различных числовых последовательностей

(прогрессий, чисел ряда Фибоначчи и пр). Допустимая длина хвоста

программной строки 127 байт и размещается сам хвост

операционной системой в префиксе программного сегмента начиная с

относительного адреса 81H. Предыдущий байт по адресу 80Н

содержит число ударов пользователя по клавишам после имени

запускаемой программы.  Хвост командной строки не имеет никакого

завершающего символа - после последнего осмысленного символа

будет лежать просто "мусор".

Само имя запущенной программы  находится в блоке окружения,

адрес которого тоже есть в PSP по относительному адресу 2CH, так

что собственное имя тоже доступно нашей программе, что бывает

актуально в связи с предоставляемой ОС свободой в переименовании

любых файлов.

Ассемблер

В ассемблере обработка  командной строки выполняется "вручную" ;

наиболее важная  часть этой обработки состоит в подсчете

разделенных пробелами слов хвоста и выделении каждого слова -

затем отдельные слова могут анализирлваться программой в

соответствии с их прикладным значением - это уже прикладная

часть.

Приведем пример разбора  командной строки:

name cmdstr

title Обработка командной строки

; Именованные константы

cr equ 0dh

lf equ 0ah

tab equ 09h

blank equ 20h

cmd 80h             ;буфер хвоста команды

_TEXT segment word public 'CODE'

      assume cs:_TEXT,ds:_DATA,es:_DATA,ss:STACK

cmdstr  proc far ;Точка хвода из MS DOS

        push ds

        xor  ax,ax

        push ax

        mov  ax,_DATA

        mov  ds,ax

        mov  bx,offset cmd  ;Теперь ES:BX - хвост команды

        call argc           ;Вызов п/п подсчета числа слов в

                            ;хвосте

        cmp  ax,2           ;2 аргумента?

        je   d2             ;Да,продолжить

        mov  dx,offset msg2 ;Адрес сообщения

        mov  cx,msg2_len    ;Длина сообщения

        jmp  d9             ;К п-п вывода сообщения

d2:     mov  ax,1           ;Номер аргумента

        call argv           ;К п-п, возвращающей в ES:BX адрес

                            ;в АХ - длину

        mov di,offset fname ;копируем слово в буфер

        mov cx,ax           ;CX - длина

d3:     mov al,es:[bx]      ;копир 1 байт

        mov di,[al]

        inc bx

        inc di

        loop d3             ;Цикл до конца слова

        mov byte ptr [di],0 ;добавим 0-байт конца строки

d9:                         ;Вывод сообщения с адресом в DS:DX

                            ;и длиной в CX

        mov bx,stderr

        mov ah,40h

        int 21h

        mov ax,4c01         ;Ф-ция 4ch - завершитьс кодом 1

        int 21h

cmdstr  endp

argc  proc near            ;Подсчет числа аргументов

                           ;при вызове в ES:BX - ком строка

                           ;возвр в АХ колич аргументов

        push bx

        push cx

        mov ax,1           ;Счетчик =1

argc1:  mov cx,-1          ;Флаг внешнего аргумента

argc2:  inc bx             ;След символ

        cmp byte ptr es:[bx],cr

        je  argc3          ;Конец

        cmp byte ptr es:[bx],blanc

        je  argc1

        cmp byte ptr es:[bx],tab

        je  argc1

        jcxz argc2        ;Если внутр аргумент

        inc ax

        not cx            ;Уст флага внутр арг

        jmp argc2

argc3:  pop cx

        pop bx

        ret

argc    endp

argv   proc  near       ;Получ адреса и длины аргументов

                        ;При вызове в ES:BX ком строка

                        ;AX - номер аргумента

                        ;При возврате ES:BX адрес,АХ - длина

        push cx

        push di

        or ax,ax        ;Аргумент 0?

        jz argv8        ;Да - к получ имени программы

        xor ah,ah       ;уст счетчик аргументов

argv1:  mov cx,-1

argv2:  inc bx

        cmp byte ptr es:[bx],cr

        je argv7

        cmp byte ptr es:[bx],blanc

        je argv1

        cmp byte ptr es:[bx],tab

        je argv1

        jcxz argv2

        inc ah

        cmp ah,al

        je argv4      ;К поиску длины

        not cx

        jmp argv2     ;Если арг внутр

argv4:                ;Опрделение длины аргумента

        mov ax,bx     ;Сохр адрес начала параметра

argv5:  inc bx        ; След символ

        cmp byte ptr es:[bx],cr

        je argv6

        cmp byte ptr es:[bx],blanc

        je argv6

        cmp byte ptr es:[bx],tab

        jne argv5

argv6:  xchg bx,ax

        sub  ax,bx

        jmp  argvx

argv7:  xor ax,ax

        jmp argvx

argv8:              ;При 0-м числе аргументов

        mov es,es:[2ch] ;Получить сегмент контекста

        xor di,di       ;Найти имя программы как 1-й пробел

                        ;после всех переменных контекста

        xor al,al

        mov cx,-1

        cld

argv9:  rephe scasb

        scasb

        jne argv9

        add di,2       ;Пропуск слова чсчетчика в контексте

        mov bx,di      ;Сохр адрес имени программы

        mov cx,-1

        rephe scasb

        not cx         ;Преобраз СХ в длину

        dec cx

        mov cx,ax      ;Вернуть длину в АХ

argvx:                 ;Общий выход

        pop di

        pop cx

        ret

argv   endp

_TEXT  ends

_DATA  segment word public 'DATA'

fname db 64 dup (0)   ;Буфер для аргумента ком строки

msg2  db cr,lf

      db 'cmdstr: Не задан параметр'

      db cr,lf

msg2_len equ $-msg2

_DATA  ends

STACK  segment para stack 'STACK'

        db 64 dup (?)

STACK ends

        end cmdstr


Обработка хвоста командной строки в Си

В Си - программах все параметры командной строки доступны для

работы с ними, если главная функция main определена с

параметрами - целочисленным для количества аргументов командной

строки и массивом указателей на отдельные слова командной

строки, причем 0-й элемент массива указывает на имя

исполняемого файла:

        #include <stdio.h>

        const ARG_COUNT = 2;

void main(int argc, char *argv[])

        {

if(argc < ARG_COUNT){puts("Не задан параметр командной стрки");

                     exit(1);

                    }

printf("Имя нашей программы %s",argv[0]);

printf("Параметр командной строки %s",argv[1]);

}

Наш пример расчитан на 1 параметр командной строки и выводит имя

программы и заданный параметр. Если параметра нет, выводится

сообщение и прграмма завершается с кодом выхода 1. В принципе

параметров может быть несколько в пределаах их общей допустимой

в ОС длины 127 байт.


Обработка хвоста командной строки в Турбо Паскале

Турбо Паскаль предоставляет для доступа к командной строке 2

подпрограммы - ParamCount, возвращающую количество слов в

командной строке без учета имени самой программы и

ParamStr(c:Integer), возвращающую строку параметра с номером с;

Param(0) возвращает имя исполняемого файла.

VAR s:String;

begin

Writeln('Имя нашей программы :',ParamStr(0));

if ParamCount >0 then S:=ParamStr(1)

else begin

Write('Введите параметр - имя файла');

Readln(s);

if s='' then s:='default.dat';

end;

...

end.


Работа с файлами в Ассемблере

1.Файловая система MS DOS.

Файлы (именованные объекты для хранения информации) в прикладных

программах надо уметь создавать и уничтожать, отыскивать на

диске, читать из них и записывать в еих, открывать и закрывать,

защищать от несанкционированного доступа. Все эти работы с

файлами может выполнять MS DOS, поддерживающая следующие виды

файлов: регулярные, специальные сисмвольные, файлы - каталоги.

При работе с регулярными файлами ОС использует специальную

программу - драйвер блокового устройства, которая за одно

обращение переносит один или несколько секторов.

Специальный символьный файл - это логическое представление

драйвера символьного устройства.

Каталог -  файл,хранящий информацию о регулярных файлах и

другтих каталогах.

Стандартные драйверы (программы для физического управления

устройствами) расположены в файле IO.SYS (IBMBIO.COM) и

соответствуют стандартному набору устройств ПК: клавиатуре,

дисплею, дисковым накопителям.

Инсталируемые драйверы загружаются в память с помощью все той же

MS DOS в процессе обработки файла конфигурации config.sys

при загрузке ОС.Для этого в config.sys должны быть фразы вида

                device = Имя_файла_драйвера

Ряд символьных устройств трактуются ОС как файлы с уникальными

фиксированными именами: СON - консоль (клавиатура и дисплей),

AUX или COM1 - порт адаптера последовательной связи, COM2, COM3,

COM4, LPT1 или PRN - 1-й параллельный порт (принтер), LPT2,

LPT3, NUL - фиктивное устройство.

С точки зрения программиста регулярный и специальный символьный

файлы - это лента байтов, имеющая начало и конец. В файлах

поддерживается указатель чтения - записи - текущая позиция, к

которой осуществляется доступ. Этот указатель автоматически

передвигается на перенесенное число байтов. В операциях чтения

ОС фиксирует достижение конца файла, сообщая об этом в программу

специальным условием EOF, когда указатель уходит от начала

файла на величину его длины.  Многие ситемы программирования

работают с так называемыми текстовыми файлами и имеют

библиотечные подпрограммы, фиксирующие конец файла по

специальному управляющему символу конца файла с кодом 1AH (^Z).

Различают последовательный (байт за байтом) и прямой или

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

До всех операций обмена файл должен быть открыт - под этим

подразумевается создание ОС специальной информационной таблицы о

файле. После завершения операций обмена файл необходимо закрыть

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

информационная таблица.

Каталоги.

Файловая система в MS DOS построена в иерархического дерева, в

узлах которого размещены каталоги - специальные файлы ,

состоящие из 32 - байтовых элементов, содержащих данные об

именах входящих в него файлов, расшрениях, атрибутах, датах и

временах создания, номерах 1-х выделенных файлам кластеров и

размерах файлов в байтах. Все каталоги ниже корневого содержат 2

зарезервированных миени "." и ".." - 1 точка это собственное имя

файла - каталога, а 2 точки - это имя каталога - предка. При

уничтожении файлов ОС записывает на место 1-й буквы имени файла

символ '?' с кодом E5H, оставляя возможность восстановления

файла до тех пор, пока его бывшие кластеры не будут выделены

другому файлу - сама ОС старается по возможности выделять еще

неиспользованные кластеры.

Таблица размещения файлов.

Служит для отслеживания свободного пространства на диске и

состоит из 12 -ти или 16-ти битовых элементов по 1-му на каждый

физический кластер на диске, представляющих собой  номер

следующего кластера, выделенного файлу или код состояния -

свободен, последний, дефектен. При выделении файлу 1-го кластера

его номер заносится в каталог, а все последующие отмечаются в

ТРФ по принципу односвязного списка.

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

файлов, первоначально размещенную в PSP по смещению 18h и

содержащую 1-байтовые дескрипторы файлов, и один общесистемный

массив описаний файлов. По смещению 34h в PSP хранится far -

указатель на место хранения таблицы открыиых файлов в памяти,

первоначально указывающий на ее начало в PSP, где отведено место

только для 20 префиксов и первые 5 заняты предопределенными

файлами - устройствами. При заполнении места в PSP в любом

свободном месте памяти создается копия таблицы и в ней

продолжается заполнение индексов массива описаний открытых

файлов до 256.

Обмен информацией с файлами в ассемблере.

Для выполнения  дисковых операций необходимо сообщить DOS адрес

строки  в формате ASCIIZ,  содержащей идентификацию файла в виде

имени дисковода,  пути доступа и имени файла (все параметры

необязательные)  и  строка должна завершаться шестнадцатеричным

нулем, например:

  PATHNM1    DB    'B:\TEST.ASM',0 PATHNM2

             DB    'C:\UTILITY\NU.EXE',0

Обратная косая (или  прямая косая)  используются в качестве

разделителя. Нулевой байт (zero) завершает строку (отсюда

название ASCIIZ формата).  Для прерываний, использующих в

качестве параметра ASCIIZ строку,  адрес этой строки загружает

ся в регистр DX, например, командой  LEA  DX,PATHNM1.

Операции создания  и открытия  файла  требуют  загрузки в

регистр AX двухбайтового числа,  представляющего собой файловый

номерер.  Стандартные устройства не  нуждаются в  операции

открытия  и  могут использовать непосредственно файловые номера:

0  -  ввод, 1 - вывод, 2 - вывод сообщений об ошибках, 3 -

внешнее устройство, 4 - принтер.  Для доступа к диску при

создании  или открытии файла используется ASCIIZ строка и

функции DOS шест.  3C или 3D. Успешная операция устанавливает

флаг  CF в 0  и помещает файловый номер в регистр AX.  Этот

номер  необходимо сохранить в элементе данных DW и использовать

его для  всех последующих операций над дисковым файлом.   При

неуспешной операции флаг CF устанавливается в 1,  а в регистр AX

помещается код ошибки, зависящий от операции

       01   Ошибка номера функции

       02   Файл не найден

       03   Путь доступа не найден

       04   Открыто слишком много файлов

       05   Нет доступа (Операция отвергнута)

       06   Ошибка файлового номера

       07   Блок управления памятью разрушен

       08   Недостаточно памяти

       09   Ошибка адреса блока памяти

       10   Ошибка оборудования

       11   Ошибка формата

       12   Ошибка кода доступа

       13   Ошибка данных

       15   Ошибка дисковода

       16   Попытка удалить оглавление

       17   Другое устройство  ?

       18   Нет больше файлов

  СОЗДАНИЕ ДИСКОВОГО ФАЙЛА

  ------------------------------------------------------------

Ниже раскрыты  требования к созданию, записи и закрытию

дисковых  файлов

Создание файла: Шест.3C

Для создания нового

файла или переписывания старого файла используется  функция

шест.3C.  При этом  регистр  DX должен содержать  адрес

ASCIIZ-строки,  а регистр CX  - необходимый атрибут. Для

обычного файла значение атрибута - 0.  Рассмотрим пример

создания обычного файла:

       MOV  AH,3CH         ; Запрос на создание

       MOV  CX,00          ;    обычного файла

       LEA  DX,PATHNM1     ; ASCIIZ строка

       INT  21H            ; Вызов DOS

       JC   error          ; Переход по ошибке

       MOV  HANDLE1,AX     ; Сохранение файлового номера в DW

При правильном  открытии  операция  создает элемент оглавления

с данным  атрибутом,  очищает флаг CF и устанавливает файловый

номер в регистре  AX.  Этот  номер  должен использоваться для

всех последующих операций.  Если создаваемый файл уже существует

(т.е.  имя файла присутствует в оглавлении), то длина этого

файла устанавливается в 0 для перезаписи.  В случае

возникновения ошибки операция устанавливает флаг CF в 1  и

помещает в регистр AX код возврата:  03, 04 или 05 Код 05

свидетельствует либо  о  переполнении  оглавления , либо  о

защите существующего файла атрибутом "только чтение".  При

завершении операции необходимо сначала проверить флаг  CF, так

как  при  создании файла возможна установка в регистре AX

файлового номера 0005,  который можно легко  спутать  с  кодом

ошибки 05 (нет доступа).

Запись файла: шест.40

Для  записи  файла используется функция  DOS шест.40. При

этом в регистре  BX  должен быть установлен  файловый номер,

в регистре CX -  число записываемых байт,  а в регистре DX -

адрес области вывода.  В следующем примере происходит запись

256 байт из области OUTREC:

         HANDLE1 DW   ?

         OUTREC  DB   256 DUP (' ')

                 MOV  AH,40H         ; Запрос записи

                 MOV  BX,HANDLE1     ; Файловый номер

                 MOV  CX,256         ; Длина записи

                 LEA  DX,OUTREC      ; Адрес области вывода

                 INT  21H            ; Вызов DOS

                 JC   error2         ; Проверка на ошибку

                 CMP  AX,256         ; Все байты записаны?

                 JNE  error3

Правильная операция записывает из памяти на  диск все данные

(256  байт),  очищает флаг CF и устанавливает в регистре AX

число действительно записанных байтов.  Если диск переполнен, то

число записанных  байтов может  отличаться от задан ного числа.

В случае неправильной операции  флаг CF устанавливается в 1,  а

в регистр AX заносится код 05 (нет доступа) или 06 (ошибка

файлового номера).

Закрытие файла : шест.3E

После завершения записи файла необходимо установить файловый

номер в регистр BX  и,  используя  функцию  DOS шест.3E, закрыть

файл.  Эта  операция записывает все  оставшиеся еще данные из

буфера на  диск и корректирует оглавление  и таблицу FAT.

            MOV  AH,3EH         ; Запрос на закрытие файла

            MOV  BX,HANDLE1     ; Файловый номер

            INT  21H            ; Вызов DOS

В случае  ошибки  в регистре  AX  устанавливается  код 06

(неправильный файловый номер).

  ПРОГРАММА:ИСПОЛЬЗОВАНИЕ ФАЙЛОВОГО НОМЕРА ДЛЯ СОЗДАНИЯ ФАЙЛА.

  ------------------------------------------------------------

Программа создает  файл  по

имени,  которое  вводится  пользователем  с  клавиатуры.  В

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

C10CREA   Использует  функцию шест.3C для  создания  файла и

сохраняет  файловый  номер  в  элементе  данных по

имени HANDLE.

D10PROC   Принимает  ввод  с клавиатуры  и  очищает пробелом

байты от  конца введенного имени  до конца области

ввода.

F10WRIT   Записывает файл, используя функцию шест.40.

G10CLSE   В завершении обработки, используя функцию шест.3E,

закрывает файл для того,  чтобы создать правильный

элемент оглавления.

Область ввода имеет  длину 30  байтов и завершается двумя

байтами:  возврат каретки (шест.0DH)  и конец  строки (шест.

0AH).  Таким образом общая длина области ввода  -  32 байта.

Программа переносит на диск 32-x байтовые записи, как записи

фиксированной длины.  Можно опустить байты "возврат каретки" и

"конец  строки",  но включить их,  если потребуется сортировка

файла.  Программа DOS SORT требует наличия этих байтов для

индикации конца записей. Для нашего примера команда SORT может

выглядеть следующим образом:

           SORT   B:<NAMEFILE.DAT   >NAMEFILE.SRT

В  результате  выполнения данной команды записи  из файла

NAMEFILE.DAT в  возрастающей  последовательности  будут помещены

в файл NAMEFILE.SRT. Программа, приведенная на рис.17.3

выполняет  чтение записей  из файла NAMEFILE.SRT и  вывод их на

экран.  Обратите внимание на два момента:

1) Символы возврат каретки и конец строки  включены в  конце

каждой записи только  для выполнения  сортировки и в  других

случаях могут быть опущены.

2)  Записи  могут иметь переменную  длину (по длине вводимых с

клавиатуры имен);  эта особенность включает некоторое

дополнительное программирование,  как это будет по казано далее.

  TTILE   HANCREAT (EXE)   Создание файла на диске

  ; -----------------------------------------------------

  STACKSG SEGMENT PARA STACK 'Stack'

          DW      80 DUP(?)

  STACKSG ENDS

  ; -----------------------------------------------------

  DATASG  SEGMENT PARA 'Data'

  NAMEPAR LABEL   BYTE                    ;Список параметров:

  MAXLEN  DB      30                      ;

  NAMELEN DB      ?                       ;

  NAMEREC DB      30 DUP('  '), 0DH, 0AH  ;   введенное имя,

                                          ;   CR/LF для записи

  ERRCDE  DB      00

  HANDLE  DW      ?

  PATHNAM DB      'D:\NAMEFILE.DAT',0

  PROMPT  DB      'Name? '

  ROW     DB      01

  OPNMSG  DB      '*** Open error ***', 0DH, 0AH

  WRTMSG  DB      '*** Write error ***', 0DH, 0AH

  DATASG  ENDS

  ; -------------------------------------------------------

  CODESG  SEGMENT PARA 'Code'

  BEGIN   PROC    FAR

          ASSUME  CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG

          PUSH    DS

          SUB     AX,AX

          PUSH    AX

          MOV     AX,DATASG

          MOV     DS,AX

          MOV     ES,AX

          MOV     AX,0600H

          CALL    Q10SCR          ;Очистка экрана

          CALL    Q20CURS         ;Установка курсора

          CALL    C10CREA         ;Создание файла,

                                  ;   установка DTA

          CMP     ERRCDE,00       ;Ошибка при создании?

          JZ      A20LOOP         ;   да  - продолжить,

          RET                     ;   нет - вернуться в DOS

  A20LOOP:

          CALL    D10PROC

          CMP     NAMELEN,00      ;Конец ввода?

          JNE     A20LOOP         ;   нет - продолжить,

          CALL    G10CLSE         ;   да  - закрыть файл

          RET                     ;         и выйти в DOS

  BEGIN   ENDP

  ;               Создание файла на диске:

  ;               -----------------------

  C10CREA PROC    NEAR

          MOV     AH,3CH          ;Функция создания файла

          MOV     CX,00           ;Нормальный атрибут

          LEA     CX,PATHNAM

          INT     21H

          JC      C20             ;Есть ошибка?

          MOV     HANDLE,AX       ;   нет - запомнить номер,

          RET

  C20:                            ;   да -

          LEA     DX,OPNMSG       ;      выдать сообщение

          CALL    X10ERR          ;      об ошибке

          RET

  C10CREA ENDP

  ;               Ввод с клавиатуры:

  ;               -----------------

  D10PROC PROC    NEAR

          MOV     AH,40H          ;Функция вывода на экран

          MOV     BX,01           ;Номер (Handle)

          MOV     CX,06           ;Длина текста запроса

          LEA     DX,PROMPT       ;Выдать запрос

          INT     21H

          MOV     AH,0AH          ;Функция ввода с клавиатуры

          LEA     DX,NAMEPAR      ;Список параметров

          INT     21H

          CMP     NAMELEN,00      ;Имя введено?

          JNE     D20             ;   да  - обойти

          RET                     ;   нет - выйти

  D20:

          MOV     AL,20H          ;Пробел для заполнения

          SUB     CH,CH

          MOV     CL,NAMELEN      ;Длина

          LEA     DI,NAMEREC      ;

          ADD     DI,CX           ;Адрес + длина

          NEG     CX              ;Вычислить

          ADD     CX,30           ;   оставшуюся длину

          REP     STOSB           ;Заполнить пробелом

  D90:

          CALL    F10WRIT         ;Запись на диск

          CALL    E10SCRL         ;Проверка на скролинг

          RET

  D10PROC ENDP

  ;               Проверка на скролинг:

  ;               --------------------

  E10SCRL PROC    NEAR

          CMP     ROW,18          ;Последняя строка экрана

          JAE     E10             ;   да  - обойти,

          INC     ROW             ;   нет - увеличить строку

          JMP     E10

  E10:

          MOV     AX,0601H        ;Продвинуть на одну строку

          CALL    Q10SCR

  E90:    CALL    Q20CURS         ;Установка курсора

          RET

  E10SCRL ENDP

  ;               Запись на диск:

  ;               --------------

  F10WRIT PROC    NEAR

          MOV     AH,40H          ;Функция записи на диск

          MOV     BX,HANDLE

          MOV     CX,32           ;30 для имени + 2 для CR/LF

          LEA     DX,NAMEREC

          INT     21H

          JNC     F20             ;Ошибка записи?

          LEA     DX,WRTMSG       ;   да -

          CALL    X10ERR          ;   выдать предупреждение

          MOV     NAMELEN,00

  F20:

          RET

  F10WRIT ENDP

  ;               Закрытие файла на диске:

  ;               -----------------------

  G10CLSE PROC    NEAR

          MOV     NAMEREC,1AH     ;Маркер конца записи (EOF)

          CALL    F10WRIT

          MOV     AH,3EH          ;Функция закрытия

          MOV     BX,HANDLE

          INT     21H

          RET

  G10CLSE ENDP

  ;               Прокрутка (скроллинг) экрана:

  ;               ----------------------------

  Q10SCR  PROC    NEAR            ;в AX - адрес элемента

          MOV     BH,1EH          ;Цвет - желтый на синем

          MOV     CX,0000

          MOV     DX,184FH

          INT     10H             ;Скроллинг

          RET

  Q10SCR  ENDP

  ;               Установка курсора:

  ;               -----------------

  Q20CURS PROC    NEAR

          MOV     AH,02

          MOV     BH,00

          MOV     DH,ROW          ;Установить курсор

          MOV     DL,00

          INT     10H

          RET

  Q20CURS ENDP

  ;               Вывод сообщения об ошибке:

  ;               -------------------------

  X10ERR  PROC    NEAR            ;DX содержит

          MOV     AH,40H          ;   адрес сообщения

          MOV     BX,01

          MOV     CX,21           ;Длина сообщения

          INT     21H

          MOV     ERRCDE,01       ;Установить код ошибки

          RET

  X10ERR  ENDP

  CODESG  ENDS

          END     BEGIN

  ЧТЕНИЕ ДИСКОВОГО ФАЙЛА

  ------------------------------------------------------------

     Открытие файла: шест.3D

Если в программе требуется  прочитать  дисковый  файл, то прежде

необходимо  открыть его,  используя  функцию шест.3D.  Эта

операция проверяет  правильность имени файла  и его наличие на

диске.  При открытии регистр DX должен  содержать адрес

необходимой ASCIIZ-строки, а регистр AL - код доступа:

         0  Открыть файл только для ввода

         1  Открыть файл только для вывода

         2  Открыть файл для ввода и вывода

Остальные биты регистра  AL используются для  разделения файлов

DOS версии 3.0  и старше (см.техническое  руководство по DOS).

Обратите внимание,  что для записи файла используется функция

создания  (шест.3C),  но не функция открытия файла.  Ниже

приведен пример открытия файла для чтения:

            MOV  AH,3DH         ; Запрос на открытие

            MOV  AL,00          ; Только чтение

            LEA  DX,PATHNM1     ; Строка в формате ASCIIZ

            INT  21H            ; Вызов DOS

            JC   error4         ; Выход по ошибке

            MOV  HANDLE2,AX     ; Сохранение номера в DW

Если файл  с необходимым именем  существует,  то операция

открытия  устанавливает  длину  записи  равной  1, принимает

существующий атрибут,  сбрасывает флаг CF и заносит файловый

номер  в регистр AX.  Файловый номер используется  в дальнейшем

для всех последующих операций.  Если файл отсутствует,  то

операция устанавливает флаг CF и заносит в регистр   AX код

ошибки:  02, 04, 05 или 12 Не забывайте проверять флаг CF. При

успешном создании файла система может установить в  регистре AX

файло вый номер  0005,  что легко можно спутать с  кодом ошибки

05 (нет доступа).

Чтение файла: Шест.3F

Для чтения записей файла используется  функция  DOS шест.  3F.

При этом необходимо  установить в  регистре BX файловый номер, в

регистре CX - число байтов и в регистре DX -  адрес области

ввода.  В следующем  примере  происходит считывание 512-байтовой

записи:

     HANDLE2     DW   ?

     INPREC      DB   512 DUP (' ')

                 MOV  AH,3FH         ; Запрос на чтение

                 MOV  BX,HANDLE2     ; Файловый номер

                 MOV  CX,512         ; Длина записи

                 LEA  DX,INPREC      ; Адрес области ввода

                 INT  21H            ; Вызов DOS

                 JC   error5         ; Проверка на ошибку

                 CMP  AX,00          ; Прочитано 0 байтов?

                 JE   endfile

 Правильно выполненная операция считывает запись в память,

сбрасывает флаг  CF  и  устанавливает  в  регистре  AX число

действительно прочитанных байтов.  Нулевое значение в регистре

AX обозначает попытку чтения после конца файла. Ошибочная

операция  устанавливает флаг  CF  и возращает в  регистре AX код

ошибки 05  (нет доступа)  или 06 (ошибка файлового номера).  Так

как  DOS  ограничивает  число  одновременно открытых файлов,  то

программа,  успешно отработавшая  с несколькими файлами, должна

закрывать их.

  ПРОГРАММА:  ИСПОЛЬЗОВАНИЕ ФАЙЛОВОГО НОМЕРА ДЛЯ ЧТЕНИЯ ФАЙЛА

  ------------------------------------------------------------

Приведена программа,  которая  читает файл, созданный предыдущей

программой и  отсортированный командой DOS SORT.  Для открытия

файла используется функция  шест.3D.  Полученный в  результате

файловый номер заносится  в поле  HANDLE  и используется  затем

в функции шест.3F для чтения файла.  В программе нет

необходимости переносить курсор  на новую строку,  так  как

записи  содержат в  конце символы "возврат каретки" и "новая

строка".

          page    60,132

  TITLE   HANREAD (EXE)   Чтение записей, созданных в HANCREAT

  ; -----------------------------------------------------

  STACKSG SEGMENT PARA STACK 'Stack'

          DW      80 DUP(?)

  STACKSG ENDS

  ; -----------------------------------------------------

  DATASG  SEGMENT PARA 'Data'

  ENDCDE  DB      00

  HANDLE  DW      ?

  IOAREA  DB      32 DUP(' ')

  PATHNAM DB      'D:\NAMEFILE.SRT',0

  OPENMSG DB      '*** Open error ***', 0DH, 0AH

  READMSG DB      '*** Read error ***', 0DH, 0AH

  ROW     DB      00

  DATASG  ENDS

  ; ------------------------------------------------------

  CODESG  SEGMENT PARA 'Code'

  BEGIN   PROC    FAR

          ASSUME  CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG

          PUSH    DS

          SUB     AX,AX

          PUSH    AX

          MOV     AX,DATASG

          MOV     DS,AX

          MOV     ES,AX

          MOV     AX,0600H

          CALL    Q10SCR          ;Очистить экран

          CALL    Q20CURS         ;Установить курсор

          CALL    E100PEN         ;Открыть файл,

  ;                               ;   установить DTA

          CMP     ENDCDE,00       ;Ошибка открытия?

          JNZ     A90             ;   да - завершить программу

  A20LOOP:

          CALL    F10READ         ;Чтение записи с диска

          CMP     ENDCDE,00       ;Ошибка чтения?

          JNZ     A90             ;   да  - выйти,

          CALL    G10DISP         ;   нет - выдать имя,

          JMP     A20LOOP         ;         и продолжить

  A90:    RET

  BEGIN   ENDP

  ;               Открытие файла:

  ;               --------------

  E100PEN PROC    NEAR

          MOV     AH,3DH          ;Функция открытия

          MOV     CX,00           ;Нормальные атрибуты

          LEA     DX,PATHNAM

          INT     21H

          JC      E20             ;Ошибка открытия?

          MOV     HANDLE,AX       ;   нет - сохранить

          RET                     ;         файловый номер

  E20:

          MOV     ENDCDE,01       ;   да - выдать

          LEA     DX,OPENMSG      ;   сообщение об ошибке

          CALL    X10ERR

          RET

  E100PEN ENDP

  ;               Чтение дисковой записи:

  ;               ----------------------

  F10READ PROC    NEAR

          MOV     AX,3FH          ;Функция чтения

          MOV     BX,HANDLE

          MOV     CX,32           ;30 для имени, 2 для CR/LF

          LEA     DX,IOAREA

          INT     21H

          JC      F20             ;Ошибка при чтении?

          CMP     AX,00           ;Конец файла?

          JE      F30

          CMP     IOAREA,1AH      ;Маркер конца файла (EOF)?

          JE      F30             ;   да - выйти

          RET

  F20:

          LEA     DX,READMSG      ;   нет - выдать

          CALL    X10ERR          ;   сообщение об ошибке

  F30:

          MOV     ENDCDE,01       ;Код завершения

  F90:    RET

  F10READ ENDP

  ;               Вывод имени на экран:

  ;               --------------------

  G10DISP PROC    NEAR

          MOV     AH,40H          ;Функция вывода на экран

          MOV     BX,01           ;Установить номер

          MOV     CX,32           ;   и длину

          LEA     DX,IOAREA

          INT     21H

          CMP     ROW,20          ;Последняя строка экрана?

          JEA     G90             ;   да - обойти

          INC     ROW

          RET

  G90:

          MOV     AX,0601H

          CALL    Q10SCR          ;Прокрутка (скроллинг)

          CALL    Q20CURS         ;Установить курсор

          RET

  G10DISP ENDP

  ;               Прокрутка (скроллинг) экрана:

  ;               ----------------------------

  Q10SCR  PROC    NEAR            ;в AX - адрес элемента

          MOV     BH,1EH          ;Установить цвет

          MOV     CX,0000

          MOV     DXX,184FH       ;Функция прокрутки

          INT     10H

          RET

  Q10SCR  ENDP

  ;               Установка курсора:

  ;               -----------------

  Q20CURS PROC    NEAR

          MOV     AH,02           ;Функция установки курсора

          MOV     BH,00           ;   курсор

          MOV     DH,ROW          ;   строка

          MOV     DL,00           ;   столбец

          INT     10H

          RET

  Q20CURS ENDP

  ;               Вывод сообщения об ошибке:

  ;               -------------------------

  X10ERR  PROC    NEAR

          MOV     AH,40H          ;в DX - адрес сообщения

          MOV     BX,01           ;Номер

          MOV     CX,20           ;Длина сообщения

          INT     21H

          RET

  X10ERR  ENDP

  CODESG  ENDS

          END     BEGIN


Работа с файлами в Си

Библиотечные подпрограммы Си для работы с файлами можно

разделить на 2 группы - потоковые и префиксные. И те и другие

обращаются в принципе к одним и тем же вызовам MS DOS, но

потоковые выполняют дополнительную буферизацию информации, что

приводит к двойной буферизации - на уровне ОС и на уровне

библиотечной подпрограммы. Префиксные - это блок -

ориентированные функции, обращающиеся к префиксным функциям

ОС без дополнительной буферизации и их использование дает

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

одно обращение к функции.

Для обеих групп функций файлового ввода - вывода возможны 2

режима доступа к файлу: текстовый и двоичный. В текстовом режиме

производится трансляция символьных кодов 0DH, 0Ah - при чтении

из файла в один символ новой строки '\n', а при записи в файл

этот символ преобразуется в пару CR LF; кроме того при

считывании символа Ctrl Z считается , что достигнут конец файла

и прочитать информацию после этого символа в текстовом режиме не

удается.

В двоичном режиме никакого преобразования байтов не происходит

и не делается никаких предположений об их смысле.

Режим доступа к файлу задается при его открытии через параметры

библиотечной функции или присвоением значения специальной

внешней переменной _fmode, описанной в <fcntl.h> или <stdlib.h>

и могущей принимать 2 значения:

                 O_BINARY или O_TEXT

По умолчанию принято O_TEXT.

Потоковый ввод -вывод.

Поток(stream)-абстрактное понятие,связанноес  переносом   данных

между  устройством чтения - записи и потоком этих данных ," дви-

жущихся" мимо устройства (воображаемой галовки ) .Потоком инфор-

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

или дисплей,  или порты ввода - вывода -  любое  устройтво  с

последовательной структурой информационых записей.

Для каждого файла, открытого для доступа через поток, Си создает

внутреннюю структурную переменную по шаблону FILE:

typedef struct{

short level;          //флаг буфер пуст или полон

unsigned flags;       //флаги состояния файла

char fd;              //префикс файла

unsigned char hold;   //непереданный символ

short bsize;          //размер буфера

unsigned char *buffer;//начало буфера, строки или текущее

unsigned char *curp; //текущая позиция для доступа в буфере

unsigned istemp;     //флаг временного файла

short token;         //маркер действительности файла

}FILE;

Флаги состояния:

_F_RDWR  - для чтения- записи

_F_READ  - только для чтения

_F_WRIT  - только для записи

_F_BUF   - имеет динамический буфер

_F_LBUF  - построчно буферизуемый

_F_ERR   - индикатор наличия ошибки

_F_EOF   - индикатор наступления конца файла

_F_BIN   - открыт в 2-ичном режиме

_F_IN    - осуществляется чтение

_F_OUT   - осуществляется запись

_F_TERM  - файл является терминалом

Эти значения - битовые позиции в поле flags и использовать их

следует как маску для операции побитового И:

if(ptr->flags & _F_READ) ...

На таком непосредственном использовании описания файлаf

построены макро:

#define ferror(f)  ((f)->flags & _F_ERR) - не 0, если при

                                   доступе возникла ошибка

#define feof(f)  ((f)->flags & _F_EOF) - не 0 если достигнут

                                         конец файла

#define fileno(f) ((f)->fd  - префикс файла

Чтобы эти макро давали свежую информацию, надо очищать поле

flags c помощью:

void clarerr(FILE*fp)

Открытие файла:

FILE * fopen(const char * filename, const char * mode);

Строка mode может принимать значения:

r - только для чтения

w - только для записи с усечением

a - для дополнения

r+  для обновления

w+  для обновления

a+  для обновления

b   двоичный режим

t   текстовый режим

Общепринятая схема:

#include <stdio.h>

FILE *f;

main(){ if((f=fopen("myfile.txt","r"))!=Null ... else ...}

Закрытие файла:

int fclose(FILE*fp)

int fcloseall(void)

Переоткрытие файла с другими правами доступа:

FILE *freopen(const char *filename,const char *mode,FILE

*steram)

Открытие безымянного временного файла, уничтожаемого при

закрытии или завершении программы:

FILE *tmpfile(void)

Внимание! При задании имени файла с маршрутом доступа не

забывайте ставить разделитель между именами каталогов в виде

двойного обратного слэша:

c:\\dir1\\dir2\\filem\\name

Стандартные потоки: stdin, stdout, stderr, stdaux, stdprn

являются указателями на переменные типа FILE и могут

использоваться наравне с возвращаемыми функцией fopen.

Библиотеки Си содержат функции потокового посимвольного,

построчного, блокового и форматированного ввода-вывода.

Ввод-вывод символов

int fgetc ( FILE * stream )/*  Возвращает  следующий  символ  из

                               файла с преоброзованием в int.*/

int   getc     (FILE *__fp);-из потока

int fgetchar ( void );/* Возвращает следующий символ из stdin;

                       это всё равно , что fgetc (stdin).*/

int   getchar  (void);-из stdin

int  fgetchar(void)-получает символ из stdin

int ungetc(int ch, FILE *fp) - возвращает символ назад в поток

int   fputc(int c, FILE *stream) - вывод символа__ в файл

int putc(const int c, FILE *fp) - в поток

int fputchar(int c) -_вывод символа в stdout

int putchar  (const int c) - в stdout

int getch(void)-с клавиатуры без отображения(в conio.h)

int   putch(int c)-в текущее окно

Ввод-вывод строк

сhar * fgets ( char s, int n, FILE * stream ) -  Читает символы

из файла ,  пока не наберёт n-1  или  встретит символ новой

строки . В конец всегда запишется нулевой байт конца строки.

Возвращает указатель на строку или NULL.

char  * gets(char *s) - со стандартного ввода до '\n'

int  fputs(const char *s, FILE *stream) - вывод до нуль-байта

int  puts(const char *s) - то же в stdout

Блоковый ввод - вывод

Чаще всего используется при переносе структурных переменных или

массивов.Файл следует открыватьв 2-ичном режиме, а в качестве

размера блока указывать sizeof(var)

int fread(void *ptr,int size,int n,FILE *fp) - чтение n

элементов размером size байт каждый в буфер ptr из файла fp

При ошибке возвращается EOF

int getw(FILE *fp) - чтение целого из файла fp

int fwrite(void *ptr,int size,int n,FILE *fp) - запись блока

int putw(int w,FILE*fp) - запись целого

Форматированный ввод-вывод

является частным случаем строкового. Основная особенность

состоит в преобразовании байтов строки и присваивание полученных

значений специфицированным переменным. Первым аргументом

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

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

- это указатели на переменные, которым присваиваются вводимые

значения или это имена выводимых переменных при выводе.

Строка формата состоит из отдельных полей:

 % [флаги][ширина][.точность][F|N|h]<тип>

Флаги:

- выравнивание вывод числа влево

+ вывод знака числа

Пробел - вывод пробела перед положит числом

# вывод идентификатора системы счисления

Ширина(только для вывода):

n - минимальная ширина с пробелами в незаполненных позициях

0n - но с ведущими нулями в незаполненных слева целых

*  - следующий аргумент из списка задает ширину

Модификатор:

F - far - указатель

N - near - указщатель

h - short int для целых

l - long int для целых

Типы преобразований:

с - символьный ввод - вывод

d или i - десятичное целое со знаком

o - восьмеричное без знака

u - десятичное int без знака

x,X - 16-ричные целые без знака

f,e.E,g,G - форматы для типа float

s строковый ввод - вывод

int  fprintf(FILE *stream, const char *format, ...)

int  printf(const char *format, ...)

int   fscanf(FILE *stream, const char *format, ...)

int  scanf(const char *format, ...)

int  sprintf(char *buffer, const char *format, ...);

int  sscanf(const char *buffer, const char *format, ...);

int vfprintf(FILE *stream, const char *format,void *arglist)-

int vfscanf(FILE *stream, const char *format,void *arglist)-

int  vprintf(const char *format, void *arglist)

int  vscanf(const char *format, void *arglist)

int vsprintf(char *buffer, const char *format,void *arglist)-

int vsscanf(const char *buffer,const char *format,void *arglist)

Произвольный доступ к файлам.

Место в файле, куда ведется запись или откуда осуществляется

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

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

этого указателя в нужное место, т е позволяет доступ к любому

байту в файле.

void rewind(FILE *fp) - "пермотка" указателя в начало файла

int fseek(FILE *fp, long offset, int from_where) - сдвигает

указатель на offset байт к началу или концу файла от позиции

from_where, которая может быть SEEK_SET (0) - от начала,

SEEK_CUR (1) - от текущей позиции, SEEK_END (2) - от конца.

long ftell(FILE *fp) - возвращает текущее значение указателя

int fgetpos(FILE *fp, fpos_t *pos) - заносит в pos значение

указателя чтения - записи

int fsetpos(FILE*fp, const fpos_t *pos) - устанавливает

указатель в позицию pos

Управление буферизацией.

Размер встроенного буфера Турбо Си 512 байт для регулярных

файлов и 128 байт для стандартных. При выполнении подпрограмм

установки указателя чтения - записи через rewind() и fseek() и

при закрытии файла производится  флэширование буфера - очистка

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

Программа может явно вызвать флэширование одного или всех

буферов :

int fflush(FILE *fp)

int flushall(void)

Библиотека Турбо Си имеет подпрограммы для либо полного

отключения буферизации либо задать необходимый размер буфера

либо использоват в качестве буфера предоставляемую программой

область памяти:

void setbuf(FILE *fp, char *buf) - если buf == NULL то

буферизация отключена, иначе буфер в 512 байт размещается по

адресу buf.

int setvbuf(FILE *fp,char *buf,int type,unsigned size) - type

задает способ буферизации: _IONBF - выключена, _IOFBF - под

буфер выделяется size байт по адресу buf, а если buf == NULL, то

место под буфер будет в куче; _IOLBF - при каждой записи '\n'

буфер флэшируется.

ПРЕФИКСНЫЙ ДОСТУП К ФАЙЛАМ

базируется на группе подпрограмм нижнего уровня, сразу

обращающихся к MS DOS:

#include<io.h>

int open(const char *pathname, int access [,unsigned mode]) -

открывает файл в режиме чтения- записи и возвращает префикс

открытого файла или -1; через access можно задать права доступа

, а через mode режим создания файла:

access:

O_RDONLY, OWRONLY, O_RDWR,

O_CREAT использует параметр mode: S_IFMT - маска файла (в ДОС

        не поддерживается), S_IFDIR - файл является каталогом,

        S_IFIFO,S_IFCHR - спец символьный файл, S_IFREG -

        регулярный

O_TRUNC,O_EXCL, O_APPEND, O_TEXT, O_BINARY, O_NOINHERIT

int _open(const char * filename, int oflags)

Значения oflags формируются по ИЛИ из констант:

FA_RDONLY, FA_HIDDEN, FA_SYSTEM, FA_LABEL, FA_DIREC, FA_ARCH

int _creat(const char *path, int attrib)

int creat(const char *path, int amode) - эти функции создают

файл

int createnew(const char *path, int attrib)- отличается тем,что

при наличии файла с указанным именем возвращает ошибку -1

int creattemp(char *path,int attrib) - генерирует уникальное имя

и создает файл в указанном каталоге.

int close(int handle), int _close(int handle) - функции закрытия

файлов

int read(int handle,void *buf,unsigned len) ,

int _read(int handle,void *buf,unsigned len) - читать len байт

из файла handle в буфер buf

int write(int handle,void *buf,unsigned len)

int _write(int handle,void *buf,unsigned len) - запись

long lseek(int handle, long offset, int fromwhere) - установка

указателя префиксным доступом

Переадресация файлового ввода - вывода

В составе MS DOS есть подпрограммы для дублирования открытых

файлов - вызов функции АН=45Н прерывания 21Н приводит к

появлению в таблице открытых файлов 2-х копий элементов и на

один и тот же файл можно будет ссылаться по 2-м разным значениям

индексов элементов (префиксов). При выполнении вызова АН=46Н

копирование элемента происходит в заданное место таблицы и если

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

файлу, этот файл автоматически закрывается. При дублировании

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

(положение указателя, режим открыия) сохраняются.

В Tурбо Си эи вызовы  оображены 2-мя библиоечными функциями:

int dup(int handle) - дублирует префикс в свободное место

таблицы открытых файлов; при достижении предела на число

открытых файлов errno=EMFILE,при ошибке возвращается -1 и в

errno=EBADF.

int dup2(int old_handle,int new_handle) - выполняет вызов АН=46Н

Как правило эти функции используются при переадресации обмена со

стандартными файлми ввода - вывода

Создание и уничтожение файла - каталога.

В ОС есть подпрограммы для создания, удаления, просмотра

содержимого каталогов (директориев) в пределах текущего

накопителя и эти подпрограммы дублируются в библиотеке Си с

прототипами в <dir.h>:

#include<dir.h>

int mkdir(const char *path) - для создания каталога

int rmdir(const char *path) - для удаления пустого каталога

Управление текущим накопителем и каталогом

Во внутркенних переменных ДОС хранятся номера текущего

накопителя и текущего каталога для каждого накопителя. Турбо Си

содержит функции для определения и установки текущих каталога и

накопителя:

int getdisk(void) - возвращает номер накопителя 0 - А, 1 - В ...

int setdisk(int drive) - устанавливает drive текущим диском

int getcurdir(int drive,char *directory) - возвр тек каталог

char *getcwd(char *buf,int buflen) - запишет в buf маршрут

                                текущего рабочего каталога

int chdir(const char *path) - установит текущим каталог

Чтение содержимого каталога и поиск файлов

int findfirst(const char *filename,struct ffblk *ffblk,int

attrib) - вызывает АН=4ЕН для получения информации о первом

файле, имя которого filename и атрибут attrib; допускается

использование шаблонов имен файлов. При совпадении заполняется

стуктурная переменная по шаблону ffblk:

struct ffblk{

char ff_reserved[21];

char ff_attrib;

unsigned ff_time;

unsigned ff_date;

long ff_size;

char ff_name;}

int findnext(struct ffblk *ffblk) - продолжает поиск совпадений,

начатый findfirst().

void fnmerge(char *panh,const char *drive, const char *dir,

const char *name, const char *ext) - в path строится строка

спецификации файла по заданным компонентам

int fsplit(const char *path,char *drive, char *dir,char *name,

char *ext) - разборка спецификации на элементы

char *searchpath(const char *file) - выполняет поиск файла по

имени и возврщает укзатель на его полную спецификацию или NULL

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

#include<stdio.h>

FILE *fdopen(int handle, char*type) - создает описание потока и

возвращает указатель на него, в поле fd префикса запишется

handle уже открытого файла, в type задается право доступа к

файлу - оно не должно противоречить правам, заданным при

префиксном открытии

Удаление и переименование файлов

#include<stdio.h>

int unlink(const char *filename) - для удаления файла

#include<dir.h>

int rename(const char* oldname, const char *newname) - для

переименования файла

char *mktemp(char *template) - генерирует уникальное имя файла в

шаблон template , содержащий символы, обязательно включаемые в

имя

char *tmpnam(char *sptr) - генерирует имя, несовпадающее ни с

одним в текущем каталоге, помещает его в буфер sptr длиной 13

байт

Определение существования файла или каталога

Определить существование регулярного файла можно с помощью

функций открытия, задав права только для чтения, а проверку

наличия каталога - попыткой установить этот каталог текущим.

Другой способ определения существования файла или какталога -

использование функции findfirst.

Но более предпочтительным является использование функций, не

выполняющих открытия файла и позволяющих получить дополнительную

информацию:

#include <io.h>

int access(const char *filename, int amode) - проверяет

существование или права доступа, параметр amode:

00 - проверка существования файла или каталога

02 - допускается ли запись

04 - допускается ли чтение

06 - проверка нна чтение - запись

При утвердительном ответе возвращается 0, иначе -1

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

Полезная информация о файле может быть получена функциями fstat

и stat  - они запоняют поля структурной переменной по шаблону,

описанному в заголовочном файле <sys\stat.h>:

struct stat{

short st_dev;     номер накопителя

short st_ino;

short st_mode;    биты прав доступа

short st_nlink;   число привязок к файлу в MS DOS =1

int st_uid;

int st_gid;

short st_rdev;    то же что и st_dev

long st_size;     размер открытого файла

long st_atime;

}

#include<sys\stat.h>

int fstat(int handle, struct stat *statbuf)

int stat(char *path,struct stat *statbuf)

Наиболее часто обращаются к этим функциям для определения

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

файла - позиционирование указателя на конец файла (можно сразу

открыть его для дополнения а+) и получение его позиции

При префиксном доступе можно изменить длину файла функцией:

it chsize(it handle,long size) - усекает или расширяет файл

Для получения некоторых параметров состояния открытого потока

можно использовать следующие функции:

#include<stdio.h>

int feof(FILE *stream) - возвр не 0 если достигнут конец файла

int ferror(FILE *strem) - не 0 при ошибках

int clearerr(FILE *fp)  - сброс индикаторов ошибки и конца файла

Для префиксных файлов:

#include<io.h>

int eof(int handle)     - 1 если конец файла

Изменение прав доступа к файлу может быть выполнено функциями:

#ibclude<sys/stat.h>

int chmod(const char *filename,int amode) - изменение прав

доступа (атрибута)

int _chmod(const char *filename,int func,[,int attrib]) -

позволяет либо запросить (func=0) либо установить (func=1) права

доступа

#include<io.h>

int setmode(int handle,int amode) - устанавливает права доступа,

amode либо O_TEXT либо O_BYNARY

Получение и установка даты и времени

#include<io.h>

int getftime(int handle,struct ftime *ftimep)

int setftime(int handle, struct ftime *ftimep)


Работа с файлами в Турбо Паскале

Турбо Паскаль поддерживает так называемый буферизованный обмен с

файлами - чере область оперативной памяти, отводимую при

открытии файла. Поэтому при записи в файл физическое перемещение

информации на диск произойдет либо после заполнения всего

буфера, либо после закрытия файла.

Турбо Паскаль вводит и поддерживает 3 файловых типа:

-текстовые файлы (тип Text);

-компонентные файлы (тип File of ТипКомпонетов);

-бестиповые файлы (тип File);

Перед началом работы с файлом в памяти должна быть размещена

переменная одного из перечисленных файловых типов:

ft1:Text;

fk1:File of Real;

fb1:File;

Подпрограммы, общие для всех типов файлов:

Assign(VAR f; FileName:String)  {Связывает файловую переменную

f c именем FileName физического файла}

Reset(VAR f)    {Открытие файла для чтения}

Rewrite(VAR f)  {Открытие файла для записи}

Close(VAR f)    {Закрытие открытого файла}

Rename(VAR f;NewName :String)    {Переименовывает

только закрытый файл}

Erase(VAR f )   {Стирает только закрытый физический файл }

EOF(VAR f):Boolean  {True если достигнут конец файла}

Подпрограммы для работы с текстовыми файлами (ASCII-коды, конец

строки - #13, конец файла - ^Z - код 26, автоматическое

преобразование в символьную форму при записи в файл и в

машинную форму при чтении из файла)

SetTextBuf(VAR f:Text; VAR Buf[;BufSize:word])  {Установка

буфера перед открытием файла}

Append(VAR f:Text) {Открытие для дополнения в конец файла}

Flush(VAR f:Text)  {Перепись буфера в файл до полного

заполнения}

EOLn(VAR f:Text):Boolean  {True если конец строки или конец

файла}

SeekEOLn(VAR f:Text):Boolean  {True если после указателя только

пробелы, табуляторы или символы конца строк}

SeekEOF(VAR f:Text):Boolean   {True если конец файла или после

указателя нет ничего значащего }

Read(f,X),Read(f,X!,X2...Xn),ReadLn {Чтение полных или

частичных строк}

Write(f,X),Write(f,X1,X2..Xn),WriteLn {Запись в файл}

2.7.Типизированные (компонентные) файлы.

Процедуры Reset и Rewrite - обе устанавливают для типизированных

и бестиповых файлов режим "для чтения и записи" по значению 2

предопределенной системной переменной FileMode. Чтобы установить

"только запись" этой переменной необходимо присвоить значение 1,

а для "только чтение" - 0.

После открытия файла ввод и вывод осуществляются стандартными

подпрограммами Read(f,X) и Write(f,X) - только переменная Х или

их список того же типа, что и тип файлового компонента. К

типизированным файлам нет смысла применять ReadLn или WriteLn -

в них просто нет понятия строки или ее конца и нет признака

конца файла.

Преимуществом типизированных файлов является эффективное

хранение числовой информации и возможность обмена сложными и

громоздкими структурами одной командой:

TYPE dim100x20 = Array[1..100,1..20] of Real;

VAR XX,YY:dim100x20; fIn,fOut:File of dim100x20;

begin

...

Read(fIn,XX); ... Write(fOut,YY);

end.

2.8. Бестиповые файлы.

С их помощью можно осуществлять обмен с дисками участками

рабочей памяти произвольной длины, а считанные из файла данные

можно преобразовывать в любой фориат посредством приведения

типов. При открытии файла буфер передачи данных устанавливается

неявно в 128 байт. Но явно его можно установить любой размер при

открытии файла расшиенной записью процедур:

Reset (VAR f:File;BufSize:Word)  или

Rewrite(VARf:File;BufSize:Word)

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

BlockRead(VAR f:File;VAR Destin;Count:Word [;VAR ReadIn:Word])

BlockWrite(VAR f:File;VAR Source;Count:Word[;VARWriteOut:Word])

Чтение и запись Count блоков размером буфера каждый.

Необязательные параметры содержат число фактически считанных или

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

правильности выполнения процедур.

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

FileSize(VAR f):LongInt  {размер файлав компонентах или блоках}

FilePos(VAR f):LongInt  {Номер последнего уже обработанного

компонента или блка}

Seek(VAR f;N:LongInt)   {Установка текущего компонента или

блока считая от 0}

Truncate(VAR f)         {Усечение файла после текущей позиции}

2.9. Подпрограммы для работы с каталогами.

GetDir(drive:Byte;VAR S:String) {Имя текущего каталога на диске

drive}

ChDir(S:String)                 {Установка текущего каталога}

MkDir(S:String)                 {Создание каталога}

RmDir(S:String)                 {Удаление пустого каталога}

2.10.Обработка ошибок ввода - вывода может быть выполнена в

нашей программе при отключении системной проверки ключем {$I-}

и использовании функции IOResult:integer, возвращающей код

ошибки в диапазоне 2..200 (2..99 - коды ошибок DOS, 100..149 -

диагностируемые программой, 150..200 - критические аппаратные)


Работа с файлами в Бейсике

Открытие файла осуществляет оператор

OPEN Имя_файла FOR Режим_работы AS #Номер_файла

Здесь:

Имя_файла - имя файла с расширением и маршрутом доступа

Режим_работы - устанавливает разрешенный режим доступа к

               хранящимся в файле данным и может быть:

APPEND - для записи новых данных в конец файла

INPUT -  файл открывается для чтения

OUTPUT - для записи с обновлением данных и потерей старых

RANDOM - файл состоит из записей фиксированной длины и

         открывается для чтения и записи

Номер_файла - целое число в диапазонеот 1 до 255

После завершения обработки файла его необходимо закрыть

оператором:

CLOSE #Номер_файла

Доступ к данным файла возможен между OPEN и CLOSE. Запись в файл

можно осуществить с помощью:

WRITE Номер_файла, Переменная1, Переменная2,...

Чтение из файла осуществляется одним из вариантов оператора

INPUT #Номер_файла,Переменная1, Переменная2...

Для определения конца файла служит функция EOF(Номер_файла),

возвращающая логическую "истину" при достижении конца файла

и "ложь" в противном случае:

i%=1

DO UNTIL (EOF(1) AND (i%>imax%))

INPUT #1,name1$[i%]

i%=i%+1

LOOP

CLOSE #1


Динамическое управление памятью в Ассемблере

Когда мы составляем нашу программу, то предполагаем, что она

будет способна работать с данными самых различных размеров.

Например, если мы составляем программу для решения систем

линейных уравнений, то мы не знаем на стадии составления какого

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

и столбцов будет в пользовательской матрице коэффициентов

(пользователь скорее всего помесит ее в файл и сообщит его имя

и размеры матрицы в командной строке при запуске программы на

выполнение).Обычная схема работы нашей программы выглядит

примерно так: разбирается командная строка для определения

например имени файла с исходными данными, проверяется наличие

этого файла, выполняется его открытие для чтения, определяется

его длина - размер оперативной памяти для размещения подлежащих

обработке данных - и мы встаем перед необходимостью запросить у

операционной системы память вне программы, в так называемой куче

для пересылки туда данных из файла, так как обрабатывать

процессор может только данные,лежащие в оперативной памяти. Если

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

частям, а если нет - надо переписать весь файл в оперативную

память. Часть памяти, выделяемая программе на стадии ее

выполнения называется динамичкски распределяемой, в отличие от

памяти, резервируемой компилятором приразмещении статических

данных.

Функциональные вызовы MS DOS для управления памятью

MS DOS - однопрограммная однопользовательская операционная

система, способная оперировать адресным пространством не более

1мбайт.  В ее состав включены 3 подпрограммы 21-го прерывания

для управления памятью

АН=48Н - распределение памяти

АН=49Н - освобождение памяти

АН=4АН - изменение размера блока памяти

О возникновении ошибок при выполнении функций управления памятью

сообщается установкой флага переноса, а в регистр АХ

записывается код ошибки:

АХ=7 - разрушен блок управления памятью

АХ=8 - недостаточно свободной памяти

АХ=9 - недопустимый адрес блока памяти

Чтобы отслеживать распределение памяти ОС ведет специальный

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

(Memory Control Block). Каждый МСВ занимает целый 16 - байтовый

параграф и непосредственно предшествует собственно блоку памяти

- непрерывной области, начинающейся на границе параграфа.

Специальная внутренняя переменная ОС хранит указатель на 1-й МСВ

цепочки. Значимыми являются 5 первых байт МСВ:

байт 0 - ASCII-символ 'Z' код 5А, если данный блок последний в

цепочке МСВ блоков или символ 'M' код 4DH в противном случае

байты 1-2 равны 0, если блок свободен или содержат PID

программы, для которой распределен блок

байты 3-4 содержат размер блока в параграфах

Номер параграфа или что то же самое сегмент адреса, по которому

располагается следующий МСВ - блок в цепочке, определяется по

формуле:

seg_mcb=seg_mcb_old+length_old+1

Новые длоки памяти и следовательно МСВ-блоки в цепочке создаются

и модифицируютя ОС в случаях:

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

-изменения размера существующего блока

-при загрузке и запуске программы на выполнение

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

При распределении нового блока АН=48Н в регистр ВХ

заносится только длина создаваемого блока в параграфах.

...

        mov ah,48h

        mov bx,0800h      просим 800h параграфов

        int 21h

        jc error

        mov buff_seg,ax   сохраняем сегмент выдел блока

ОС отыскивает 1-й МСВ-блок в цепочке, помеченный как

свободный, размер которого превышает запрошенный. Если 2 или

больше подряд расположеннных блоков помечены как свободные, они

рассматриваются как единый блок. МСВ выбранного блока

корректируется:

-в байты 1-2 записывается номер параграфа текущего ЗЫЗ (PID

активной программы)

-в байты 3-4 записывается размер созданного блока.Если размер

запрошенной памяти меньше размера свободного блока, на границе

остатка свободной памяти создается МСВ и включается в цепочку

МСВ-блоков.

При загрузке и запуске программы на выполнение MS DOS отдает

СОМ - программе всю свободную память (так как СОМ - файлы не

имеют заголовков с данными о наобходимой программе памяти ОС

вынужденно предполагает худший случай и отдает программе все

что имеет) и запрос на выделение памяти из программы столкнется

с отсутствием свободной памяти. На ассемблере освобождение

неиспользуемой программой памяти - обязанность программиста:

org 100h

main    proc near

        mov sp,offset stk   пересылка стека в область сохранения

        mov ah,4ah          изменить размер блока памяти

        mov bx,(offset stk-offset main +10Fh)/16

        int 21h

        jc error

        ...

main endp

        ...

        dw 64 dup(?)

stk     equ $

        end main

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

поля в заголовке файла:

MIN_ALLOC - минимальное число параграфов,необходимое программе

дополнительно к параграфам сегментов программы, данных и стека.

MAX_ALLOC - максимальное число параграфов которые программа

могла бы использовать при наличии

Если не удается выделить при загрузке MAX_ALLOC дополнительно к

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

не меньше размера программы и минимально допустимого дополнения.

В языках высокого уровня  работу по освобождению ненужной

программе памяти как правило выполняют специальные подпрограммы,

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

При выполнении АН=4AН в регистре ES задается сегмент начала

модифицируемого блока, а в регистре ВХ - новый размер блока в

параграфах.

        ...

        mov ah,49h

        mov es,buf_seg

        mov bx,700h

        int 21h

        ...

 Если размер блока уменьшается, на границе остатка

создается новый свободный блок, а увеличение сработает , если

следующий в цепочкеблок свободный.

MS DOS имеет специальное средство создания резидентных программ

- это так называвемое TSR - завершение АН=31Н 21-го прерыавания

или прерывание 27Н для только СОМ - файлов. При этом в регистре

DX указывается размер блока памяти от начала PSP, объявляемого

резидентным, в байтах для 27Н прерыавния и в параграфах для

21-го ф-ции 31Н. Действия ОС при этом подобны выполняемым при

изменении размера блока :блок, с которого начинается  PSP,

усекается до запрошенного размера но остается занятым после

завершения программы.

Освобождение блока памяти АН=49Н 21-го прерыавания требует

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

может выдаваться либо явно самой программой, либо ОС при

завершении программы. В байты 1-2 освобождаемого блока

записываются нули.

        ...

        mov es,buff_seg

        mov ah,49h

        int 21h

        jc error

        ...

Многие профессиональные программы используют непосредственно МСВ

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

их снятия, обнаружения и уничтожения вирусов и пр.


Динамическое управление памятью в Си

Когда мы составляем нашу программу, то предполагаем, что она

будет способна работать с данными самых различных размеров.

Например, если мы составляем программу для решения систем

линейных уравнений, то мы не знаем на стадии составления какого

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

и столбцов будет в пользовательской матрице коэффициентов

(пользователь скорее всего помесит ее в файл и сообщит его имя

и размеры матрицы в командной строке при запуске программы на

выполнение).Обычная схема работы нашей программы выглядит

примерно так: разбирается командная строка для определения

например имени файла с исходными данными, проверяется наличие

этого файла, выполняется его открытие для чтения, определяется

его длина - размер оперативной памяти для размещения подлежащих

обработке данных - и мы встаем перед необходимостью запросить у

операционной системы память вне программы, в так называемой куче

для пересылки туда данных из файла, так как обрабатывать

процессор может только данные,лежащие в оперативной памяти. Если

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

частям, а если нет - надо переписать весь файл в оперативную

память. Часть памяти, выделяемая программе на стадии ее

выполнения называется динамичкски распределяемой, в отличие от

памяти, резервируемой компилятором приразмещении статических

данных.

Функциональные вызовы MS DOS для управления памятью

MS DOS - однопрограммная однопользовательская операционная

система, способная оперировать адресным пространством не более

1мбайт.  В ее состав включены 3 подпрограммы 21-го прерывания

для управления памятью

АН=48Н - распределение памяти

АН=49Н - освобождение памяти

АН=4АН - изменение размера блока памяти

О возникновении ошибок при выполнении функций управления памятью

сообщается установкой флага переноса, а в регистр АХ

записывается код ошибки:

АХ=7 - разрушен блок управления памятью

АХ=8 - недостаточно свободной памяти

АХ=9 - недопустимый адрес блока памяти

Чтобы отслеживать распределение памяти ОС ведет специальный

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

(Memory Control Block). Каждый МСВ занимает целый 16 - байтовый

параграф и непосредственно предшествует собственно блоку памяти

- непрерывной области, начинающейся на границе параграфа.

Специальная внутренняя переменная ОС хранит указатель на 1-й МСВ

цепочки. Значимыми являются 5 первых байт МСВ:

байт 0 - ASCII-символ 'Z' код 5А, если данный блок последний в

цепочке МСВ блоков или символ 'M' код 4DH в противном случае

байты 1-2 равны 0, если блок свободен или содержат PID

программы, для которой распределен блок

байты 3-4 содержат размер блока в параграфах

Номер параграфа или что то же самое сегмент адреса, по которому

располагается следующий МСВ - блок в цепочке, определяется по

формуле:

seg_mcb=seg_mcb_old+length_old+1

Новые длоки памяти и следовательно МСВ-блоки в цепочке создаются

и модифицируютя ОС в случаях:

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

-изменения размера существующего блока

-при загрузке и запуске программы на выполнение

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

При распределении нового блока АН=48Н в регистр ВХ

заносится только длина создаваемого блока в параграфах.

...

        mov ah,48h

        mov bx,0800h      просим 800h параграфов

        int 21h

        jc error

        mov buff_seg,ax   сохраняем сегмент выдел блока

ОС отыскивает 1-й МСВ-блок в цепочке, помеченный как

свободный, размер которого превышает запрошенный. Если 2 или

больше подряд расположеннных блоков помечены как свободные, они

рассматриваются как единый блок. МСВ выбранного блока

корректируется:

-в байты 1-2 записывается номер параграфа текущего ЗЫЗ (PID

активной программы)

-в байты 3-4 записывается размер созданного блока.Если размер

запрошенной памяти меньше размера свободного блока, на границе

остатка свободной памяти создается МСВ и включается в цепочку

МСВ-блоков.

При загрузке и запуске программы на выполнение MS DOS отдает

СОМ - программе всю свободную память (так как СОМ - файлы не

имеют заголовков с данными о наобходимой программе памяти ОС

вынужденно предполагает худший случай и отдает программе все

что имеет) и запрос на выделение памяти из программы столкнется

с отсутствием свободной памяти. На ассемблере освобождение

неиспользуемой программой памяти - обязанность программиста:

org 100h

main    proc near

        mov sp,offset stk   пересылка стека в область сохранения

        mov ah,4ah          изменить размер блока памяти

        mov bx,(offset stk-offset main +10Fh)/16

        int 21h

        jc error

        ...

main endp

        ...

        dw 64 dup(?)

stk     equ $

        end main

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

поля в заголовке файла:

MIN_ALLOC - минимальное число параграфов,необходимое программе

дополнительно к параграфам сегментов программы, данных и стека.

MAX_ALLOC - максимальное число параграфов которые программа

могла бы использовать при наличии

Если не удается выделить при загрузке MAX_ALLOC дополнительно к

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

не меньше размера программы и минимально допустимого дополнения.

В языках высокого уровня  работу по освобождению ненужной

программе памяти как правило выполняют специальные подпрограммы,

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

При выполнении АН=4AН в регистре ES задается сегмент начала

модифицируемого блока, а в регистре ВХ - новый размер блока в

параграфах.

        ...

        mov ah,49h

        mov es,buf_seg

        mov bx,700h

        int 21h

        ...

 Если размер блока уменьшается, на границе остатка

создается новый свободный блок, а увеличение сработает , если

следующий в цепочкеблок свободный.

MS DOS имеет специальное средство создания резидентных программ

- это так называвемое TSR - завершение АН=31Н 21-го прерыавания

или прерывание 27Н для только СОМ - файлов. При этом в регистре

DX указывается размер блока памяти от начала PSP, объявляемого

резидентным, в байтах для 27Н прерыавния и в параграфах для

21-го ф-ции 31Н. Действия ОС при этом подобны выполняемым при

изменении размера блока :блок, с которого начинается  PSP,

усекается до запрошенного размера но остается занятым после

завершения программы.

Освобождение блока памяти АН=49Н 21-го прерыавания требует

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

может выдаваться либо явно самой программой, либо ОС при

завершении программы. В байты 1-2 освобождаемого блока

записываются нули.

        ...

        mov es,buff_seg

        mov ah,49h

        int 21h

        jc error

        ...

Многие профессиональные программы используют непосредственно МСВ

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

их снятия, обнаружения и уничтожения вирусов и пр.


Динамическое управление памятью в Турбо Паскале

Управление кучей осуществляет монитор кучи, являющийся частью

ситемной библиотеки Турбо Паскаля и реализующий следующие

подпрограммы:

New(VAR P :Pointer) {Отводит место для переменной P^ и

присваивает ее адрес указателю P}

Dispose(VAR P:pointer) {Освобождает память, отведенную New}

GetMem(VAR P: Pointer; Size:Word) {Отводит Size байтов в куче

присваивая адрес места P}

FreeMem(VAR P: Pointer;Size :Word) {Освобождает то, что было

выделено GetMem}

Mark(VAR P:Pointer) {Запоминает по адресу Р текущее состояние

кучи}

Release(VAR P:Pointer) {Возвращает кучу в состояние,

запомненное в Р через Mark}

New(Тип Указателя):Pointer {Альтернативная форма создания

динамической переменной типа заданного базового типа}

MaxAvail:LongInt    {Самый длинный свободный участок в куче}

MemAvail:LongInt    {Сумма длин всех свободных кусков кучи}


Управление субпроцессами в Ассемблере

 Управление субпроцессами в MS DOS

Достаточно часто прикладной программе бывает необходимо

запустить на выполнение другую программу - например текстовый

редактор может запустить программу орфографической проверки

текста или загрузить 2-ю копию command.com, чтобы дать

возможность пользователю распечатать каталоги и скопировать или

переименовать файлы, не прекращая выполнение основной программы

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

В MS DOS есть функция EXEC АН=4ВН  21Н-го прерывания,

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

другую программу, называемую дочерней, из внешней памяти,

выполнить ее, а после завершения дочерней продолжить

выполнение родительской. EXEC имеет 2 опции, задаваемые в

регистре AL: если AL=0, производится загрузка программы и

передача управления в точку ее входа. Если AL=3 производится

загрузка программы, настройка ее адресов, но управление

возвращается в родительскую программу - эта опция используется

для организации программ с оверлейной структурой.

Кроме того ОС передаются 2 указателя: первый в DS:DX задает

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

сом-файла, а второй в ES:BX содержит адрес блока параметров.

При запуске на выполнение в ОС сообщается номер параграфа среды

запускаемой программы и far-указатель на хвостовик командной

строки, передаваемый в точку вххода запускаемой программы.

Выполнению EXEC предшествует ряд действий программы - предка.

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

для вновь загружаемой программы. Затем родитель строит среду

программы (если она переопределяется при запуске потомка) и

хвостовик командной строки. Номер параграфа и far-укзатель на

хвостовик помещаются в блок параметров (если потомок использует

копию среды предка, 1-е слово блока параметров  равно 0). И

наконец надо сохранить в переменных предка регистры SS,SP

указывающие текущую вершину стека предка - EXEC разрушит все

регистры. После выполнения всего этого предок заполняет регистры

процессора и вызывает прерывание 21Н.

После получения управления функция EXEC выполняет следующее:

-вся доступная память распределяется для вновь загружаемой

программы;

-строится префикс сегмента программы PSP;

-вплотную к PSP загружается программа - потомок с настройкой

адресов если она ехе-файл.

-EXEC вызывает загруженную программу как far-функцию и после

завершения управление возврщается в EXEC а оттуда в предок.

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

Имя = параметр

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

крайней мере 3 строки:

COMSPEC = переменная

PATH    = переменная

PROMPT  = переменная

Можно добавить в блок окружения дополнительные строки командой

SET.

Запрос к EXEC выглядит так:

AH=4BH

AL=00H или 01Н

DS:DX=адрес строки с именем дочерней программы

ES:BX=адрес блока параметров

Пример программы, использующей EXEC для запуска CHKDSK.COM:

cr      equ 0dh   возврат каретки

        ...

        mov stkseg,ss         ;сохран указ стека

        mov stkptr,sp

        mov dx,offset pname   ;DS:DX=имя программы

        mov bx,offset pars    ;ES:BX=блокпараметров

        mov ax,4b00h          ;функция 4bh подфункция 00

        int 21h

        mov ax,_DATA          ;делаем наш сегмент данных

        mov ds,ax             ;снова адресуемым

        mov es,ax

        mov ss,stkseg         ;восстановить указатель стека

        mov sp,stkptr

        jc error               ;если EXEC не прошла

       ...

stkseg  dw 0

stkptr  dw 0

pname   db  '\CHKDSK.COM',0   ;путь к дочерней программе

pars    dw   envir            ;сегмент контекста

        dd   cmdline          ;ком строка для дочери

        dd fcb1

        dd fcb2

cmdline db 4,'*.*',cr        ;ком строка для дочери

fcb1    db 0

        db 11 dup ('?')

        db 25 dup (0)

fcb2    db 0

        db 11 dup ('?')

        db 25 dup (0)

envir   segment para 'ENVIR' ;сегмент контекста

        db  'PATH=',0        ;пустой путь поиска

        db  'COMSPEC=C:\COMMAND.COM',0    ;место COMMAND.COM

        db  0                ;конец контекста

envir   ends

В качестве практически применимого примера использования

функции EXEC приведем программу (автор -  Р.Данкан)

интерпретатора команд по имени SHELL, которой поручим выполнение

следующей работы:

получив управление, SHELL должен выводить на экран приглашение к

вводу sh: и ожидать от пользователя ввода команды в виде строки,

заканчивающейся возвратом каретки. Приняв строку, программа

пытается выяснить наличие 1-го элемента строки в своей таблице

внутренних команд; при обнаружении будет вызываться

соответствующая подпрограмма, а при отсутствии - вызываться

функция EXEC с передачей командной строки пользователя

интерпретатору COMMAND.COM с ключем /C, используя его в качестве

процессора команд под своим управлением. Наш SHELL для начала

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

CLS для очистки экрана

DOS для запуска COMMAND.COM

EXIT для завершения работы и возврата управления следующему

интерпретатору команд

Вам предлагается добавить новые внутренние команды, сотавив

необходимые подпрограммы и вставив имена этих подрпрограмм

вместе с текстовой строкой команды в таблицу COMMANDS.

Алгоритм прграммы в общем виде следующий:

1.Вызывается прерывание 21Н с функцией 4АН для сокращения

потребляемой памяти на случай предоставления необходимого

пространства интерпретатору COMMAND.COM. Для упрощения примера

количество резервируемых параграфов мы задаем щедрой константой,

хотя эта величина должна бы вычисляться.

2.Просматривается окружение в поисках переменной COMSPEC,

определяющей положение выполнимой копии COMMAND.COM - если

переменная не найдена, программа завершается после выдачи

аврийного сообщения.

3.Загружается адрес собственного обработчика прерывания в вектор

Ctrl-C (Int 32H), чиобы не потерять управление при нажатии

пользователем Ctrl-C или Ctrl - Break.

4.Выводится текст приглашения.

5.Считывается буферизованная строка с командой пользователя.

6.Первый элемент командной строки ищется в таблице и при

обнаружении вызывается соответствующая подпрограмма.

7.При отсутствии составляется хвост командной строки с

присоединением к ключу /C команды пользователя и с помощью EXEC

вызывается копия COMMAND.COM с передачей адреса составленного

хвоста команды через блок параметров EXEC.

8. Шаги с 4-го по 7-й повторяются пока не будет введена команда

EXIT, приводящая к завершению SHELL.

name shell

page 55,132

title SHELL.ASM - Простая оболочка MS DOS

stdin equ 0      ;Дескриптор стандартного ввода

stdout equ 1     ;Дескриптор стандартного вывода

stderr equ 2     ;Дескриптор стандартной ошибки

cr equ 0dh       ;Символ ASCII возврата каретки

lf equ 0ah       ;Символ ASCII перевода строки

blanc equ 20h    ;Символ ASCII пробела

escape equ 01bh  ;Символ ASCII ESC

_TEXT   segment word public 'CODE'

        assume cs:_TEXT,ds:_DATA,ss:STACK

shell   proc far ;При входе DS=ES=PSP

        mov ax,_DATA      ;Сделаем сегмент данных

        mov dx,ax         ;адресуемым

        mov ax,es:[002ch] ;Получить сегмент контекста

        mov env_seg,ax    ;из PSP и сохранить

                          ;Освободить лишнюю память

                          ;ES уже = сегмент PSP

        mov bx,100h       ;BX=число параграфов

        mov ah,4ah        ;Изменить размер блока паияти

        int 21h           ;Перейти к MS DOS

        jnc shell1        ;Если размер изменен успешно

        mov dx,offset msg1;Изменение размера не удалось

        mov cx,msg1_length;сообщение об ошибке

        jmp shell4        ;и выход

shell1: call get_comspec  ;Получить спецификацию файла COMMAND

        jnc shell2        ;Переход если нашли его

        mov dx,offset msg3;В контексте не найдена переменная

        mov cx,msg3_length;COMSPEC - выдать сообщение и выйти

        jmp shell4        ;

shell2: mov dx,offset shell3;Установить вектор Ctrl-C (Int 23h)

        mov ax,cs           ;для обработчика этой программы

        mov ds,ax           ;DS:DX= адрес обработчика

        mov ax,2523h        ;Ф-ция 25Н-установка вектора

        int 21h             ;

        mov ax,_DATA        ;Снова делаем сегмент данных

        mov ds,ax           ;адресуемым

        mov es,ax           ;

                            ;Главный цикл интерпретатора

shell3: call get_cmd        ;Получить команду пользователя

        call intrinsic      ;Проверить- внутренняя?

        jnc shell3          ;Да,уже выполнена

        call extrinsic      ;Нет-передат COMMAND.COM

        jmp shell3          ;и получить новую команду

shell4:        ;Сюда по ошибке

                            ;DS:DX=адрес сообщения

                            ;СХ=длина сообщения

        mov bx,stderr       ;ВХ=дескриптор стандартной ошибки

        mov ah,40h          ;Функция записи

        int 21h             ;

        mov ax,4c01h        ;Завершение с кодом возврата 1

        int 21h             ;

shell endp                  ;

intrsic proc near       ;Поиск команды пользователя

                        ;в таблице COMMANDS

                        ;если комада найдена,запустить программу

                        ;и вернуть флаг перноса ="ложь"

                        ;Если не найдена,флаг переноса=истина

        mov si,offaet_commands;DS:DI=таблица команд

intr1:  cmp byte ptr [si],0   ;Конец таблицы?

        je intr7              ;Да-перход

        mov di,offset inp_buf ;Нет - пусть DI = адрес

                              ;команды пользователя

intr2:  cmp byte ptr [[di],blanc;Убрать лидирующие пробелы

        jne intr3               ;

        jnc di                  ;Пропустить найденный пробел

        jmp intr2               ;

intr3:  mov al,[si]             ;Следующий символ из таблицы

        or al,al                ;Конец строки?

        jz intr4                ;Вся строка совпала

        cmp al,[di]             ;Сравнить с введенным символом

        jnz intr6               ;найдено несовпадение

        inc si                  ;Продвинуть указатели строк

        inc di                  ;

        jmp intr3               ;

intr4:  cmp byte ptr [di],cr    ;Убедимся что команда пользов

        je intr5                ;имеет ту же длину

        cmp byte ptr [di],blanc ;След символ д б пробел

        jne intr6               ;или возврат каретки

intr5:  call word ptr [si+1]    ;Запустить COMMAND.COM

        clc                     ;вернуть флаг переноса "ложь"

        ret                     ;как флаг успешного завершения

intr6: lodsb                    ;Ищем конец этой ком строки

        or al,al                ;(нль-байт)

        jnz intr6               ;еще не конец - цикл

        add si,2                ;Переезд через адрес программы

        jmp intr1               ;Проверим на соотв след команду

intr7: stc                      ;Не нашлось - выход с флагом

        ret                     ;переноса "истина"

intrisic endp                   ;

extrinsic proc near             ;Обработка внешней команды

                                ;пердачей ее COMMAND.COM

                                ;с хвостом "/С"

        mov al,cr               ;найдем длину команды

        mov cx,cmd_tail_length  ;поиском перевода каретки

        mov di,offset cmd_tail+1;

        cld                     ;

        repnz scasb             ;

        mov ax,di               ;Вычислим дллину хвоста

        sub ax,offset cmd_tail+2;без cr и сохраним ее

                                ;Установим адрес хвоста

        mov word ptr par_cmd,offset cmd_tail;

        call exec          ;и запустим COMMAND.COM

extrinsic endp             ;

get_cmd proc near          ;Получить команду

        mov dx,offset prompt;адрес приглашения

        mov cx,prompt_length;длина приглашения

        mov bx,stdout       ;

        mov ah 40h          ;

        int 21h

                            ;Получить команду

        mov dx,offset inp_buf;входной буфер

        mov cx,inp_buf_length;макс длина при чтении

        mov bx,stdin         ;

        mov ah,3fh           ;Ф-ция чтения

        int 21h              ;

        mov si,offset inp_buf;Перевод в верхний регистр

        mov cx,inp_buf_length;

gcmd1:  cmp byte ptr [si],'a';Проверка на 'a-z'

        jb gcmd2             ;переход если не в этом диапаз

        cmp byte ptr [si],'z';

        ja gcmd2             ;

        sub byte ptr [si],'a'-'A';Преобраз в верхн регистр

gcmd2:  inc si                   ;Продвинуься по введенному

        loop gcmd1    ;

        ret           ;В вызывающую программу

get_cmd endp          ;

get_comspec proc near ;Получить место COMMAND.COM

                      ;из контекстной переменной COMSPEC

                      ;возвратим "ложь" если переменная

                      ;найдена и "истина" если нет

        mov si,offset com_var;строка для поиска соответствия

        call get_env         ;просмотр блока контекста

        jc gcsp2             ;не найдена

        mov si,offset com_spec;

gcsp1:  mov al,es:[di]        ;копируем переменную в локальный

        mov [si],al           ;буфер

        inc si                ;

        inc di                ;

        or al,al              ;Нуль-символ?

        jnz gcsp1             ;нет-получить следующий

gcsp2:  ret                   ;

get_comspec endp              ;

                              ;Просмотр контекста с вызовом

                              ;DS:DI="NAME="

                              ;использует содержимое "ENV_SEG"

                              ;возвращает пернос "ложь" и ES:DI,

                              ;указывающий на найденный парам

                              ;"истина" если нет

get_env proc near             ;

                              ;

        mov es,env_seg        ;получить сегмент контекста

        xor di,di             ;инициализ отн адрес контекста

                              ;

genv1:  mov bx,si             ;иниц указатель на имя

        cmp byte ptr es:[di],0;Конец контекста?

        jne genv2             ;конец не найден

        stc                   ;возврат с установленным

        ret                   ;переносом

genv2:  mov al,[bx]           ;получить символ из имени

        or al,al              ;конец имени?

        jz genv3              ;да

                              ;

        cmp al,es:[di]        ;сравнить с контекстом

        jne genv4             ;соответствия нет

        inc bx                ;продвинуть указатели контекста

        inc di                ;и имени

        jmp genv2             ;

genv3: ret                    ;соотв найдено

genv4:  xor al,al             ;просмотр контекста вперед

        mov cx,-1             ;в поисках нуль-байта

        cld                   ;

        repnz scasb           ;

        jmp genv1             ;на сравнение след строки

get_env endp                  ;

                              ;

exec  proc  near              ;вызов EXEC для запуска COMMAND

        mov stkseg,ss         ;сохранить указатель стека

        mov stkptr,sp         ;

                              ;теперь выполн COMMAND

        mov dx,offset com_spec;имя файла

        mov bx,offset par_blk ;блок параметров

        mov ax,4b00h          ;вызов EXEC

        int 21h               ;

        mov ax,_DATA          ;сделаем сегмент данных снова

        mov ds,ax             ;адресуемым

        mov es,ax             ;

        mov ss,stkseg         ;восстановить указ стека

        mov sp,stkptr         ;

        jnc exec1             ;ошибок нет

                              ;вывести сообщ об ошибке

        mov dx,offset msg2    ;адрес сообщ

        mov cx,msg2_length    ;длина сообщ

        mov bx,stderr         ;

        mov ah,40h            ;ф-ция записи

        int 21h               ;

exec1:  ret                   ;

                              ;

exec endp                     ;

                              ;

cls_cmd proc near             ;Команда CLS

        mov dx,offset cls_str ;Очистка экрана

        mov cx,cls_str_length ;

        mov bx,stdout         ;

        int 21h               ;

        ret

cls_cmd  endp                 ;

dos_cmd  proc near            ;Команда DOS

                       ;Установить нулевой хвост команды

        mov  word  ptr  par_cmd,offset nultail;

        call exec;запустить COMMAND.COM

        ret      ;

dos_cmd endp     ;

exit_cmd proc  near;  Команда EXIT

        mov ax,4c00h;вызов ф-ции завершения с 0 кодом возврата

        int 21h     ;

exit_cmd endp       ;

_TEXT ends          ;

STAck segment para stack 'STACK'

        dw 64 dup (?)

STACK ends

_DATA segment word public 'DATA';

commands equ $                  ;Таблица внутренних команд

        db 'CLS',0              ;

        dw cls_cmd              ;

        db 'DOS'',0             ;

        dw dos_cmd              ;

        db 'EXIT',0             ;

        dw exit_cmd             ;

        db 0                    ;

com_var db 'COMSPEC',0          ;Переменная контекста

                                ;Спецификация файла COMMAND.COM

com_spec db 80 dup (0)          ;из контекста COMSPEC=

nultail  db 0,cr            ;Нулевой хвост команды

                            ;для возбуждения COMMAND.COM

                            ;как второй оболочки

cmd_tail db 0,'/C'          ;Хвост команды для возб COMMAND.COM

                            ;как транзитной программы

inp_buf db 80 dup (0)       ;Командная строка

inp_buf_length  equ $-cmd_tail-1;

prompt db cr,lf,'sh'            ;Приглашение к вводу команд

prompt_length equ $-prompt      ;

env_seg dw 0                    ;сегмент блока контекста

msg1    db cr,lf                ;

        db 'Не могу освободить память';

        db cr,lf                      ;

msg1_length equ $-msg1                ;

msg2   db  cr,lf                      ;

        db 'Ошибка вызова COMMAND.COM';

        db cr,lf                      ;

msg2_length equ $-msg2                ;

msg3    db  cr,lf                     ;

        db 'В контексте нет переменной COMSPEC';

        db cr,lf                               ;

msg3_length equ $-msg3                         ;

cls_str db escape,'[2J'          ;Esc-послед для очистки экрана

cls_str_length equ $-cls_str     ;

                                 ;Блок параметров EXEC

parblk  dw 0                     ;сегмент контекста

par_cmd dd cmd_tail              ;командная строка

        dd fcb1                  ;блок управления файлом

        dd fcb2                  ;

fcb1    db 0                                   ;

        db 11 dup ('')                         ;

        db 25 dup (0)                          ;

fcb2    db 0                                   ;

        db 11 dup ('')                         ;

        db 25 dup (0)                          ;

stkseg dw 0            ;Первоначальное содержимое SS

stkptr dw 0            ;Первоначальное содержимое SP

_DATA  ends                                    ;

                                               ;

        end shell                              ;


Управление субпроцессами в Си

Запуск программ через оболочку.

Мы уже знаем, что в составе ОС обычно поставляется специальная

стандартная программа - оболочка, которая служит для упрощения

взаимодействия пользователя с операционной системой. Существует

много различных вариантов оболочек для работы с MS DOS:

COMMAND.COM, PCSHELL, NORTN COMMANDER и пр. В принципе мы сами

можем составить и использовать собственную оболочку и в

упрощенном варианте сделаем такую программу, аналогично

приведенной в предыдущем разделе на ассемблере. Основные функции

оболочки:

1.ввод команды пользователя либор в виде текстовой строки, либо

в виде выбранного пункта меню;

2.интерпретация введенной команды по специальным правилам для

определения, задает она внутреннюю или внешнюю команду.

Внутренняя комада может быть выполнена одной из подпрограмм ,

входящих в состав оболочки, а внешняя требует запуска некоторой

отдельной программы на выполнение. Оболочка готовит необходимые

для вызова системной функции EXEC параметры, загружает

внутренние регистры процессора и генерирует прерывание 21Н.

Часто перед этим "умная" оболочка "сворачивается" для

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

завершенияпотомка оболочка вновь подгружает свой код для

выполнения внутренних команд. Другой способ "свертывания"

реализован в COMMAND.COM. Код внутренних команд у нее содержится

в нерезидентной части, размещаемой при загрузке в самых старших

адресах памяти и можетперкрываться при запуске больших программ.

Когда запущенная программа завершается, управление всегда

попадает в резидентную часть, содержащую обработчики прерываний

22Н, 23Н, 24Н и подпрограмму контроля целостности нерезидентной

части. Если в результате суммирования всех кодов по модулю2 есть

несовпадение с эталоном, ядро COMMAND.COM осуществляет повторную

загрузку нерезидентной части. Накопитель и файл для такой

загрузки определяются по параметру среды COMSPEC. Для

организации связи прикладной программы с оболочкой в СИ есть

специальная библиотьечная функция :

#include<process.h>

int system(char *comline) - по переменной среды COMSPEC

отыскивает файл COMMAND.COM, размещает его в памяти и передает

ему в качестве хвоста командной строки comline. Если

comline==NULL , функция только проверит наличие COMMAND.COM по

указанному в COMSPEC маршруту .

В качестве примера рассмотрим функцию os_shell, реализующую

временный выход в COMMAND.COM. Возврат в system() происходит по

команде exit оболочки. Мы используем библиотечную функцию

getenv() для получения строки параметра среды программы

(контекста или окружения):

#include <stdio.h>

#include<stdlib.h

#include <process.h>>

void os_shell(void){

puts("Для возврата наберите exit")

system(getenv("COMSPEC"));}

Перед вызовом этой подпрограммы надо запомнить текущий

накопитель и каталог, позицию курсора, режим и другие параметры

видеоадаптера и после возврата восстановить все сохраненное.

Функция system() применяется для:

1.Запуска bat-файлов из прикладных программ;

2.экономии усилий по программированию при наличии готовых

программ с требуемыми функциями.

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

программу.

Семейство фуннкций execxxx() предназначено для запуска потомков

с разрушением предка - они не имеют возврата в вызвавшую

программу. Потомок занимает всю память, разрушая породившего его

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

1. для запуска потомка с наследованием среды предка или с

заданием для потомка новой среды;

2.для передачи потомку параметров в точку входа main() как

массива указателей на слова командной строки;

3.с поиском запускаемого файла только втекущем каталоге или с

использованием для этого переменной среды PATH.

int execl(char *path,char *arg0,...,char *argN,NULL)

int execle(char *path,char *arg0,...,char *argN,NULL,char

**envp)

int execlp(char *path,char *arg0,...,char *argN,NULL)

int execlpe(char *path,char *arg0,...,char *argN,NULL,char

**envp)

int execv(char *path,char *argv[])

int execve(char *path,char *argv[],char**envp)

int execvp(char *path,char *argv[])

int execvp(char *path,char *argv[],char**envp)

Параметры:

path указатель на строку спецификации файла потомка

arg0...argN, NULL- указатели на строки - составляющие командную

строку запускаемой программы

argv - указатель на массив указателей на слова командной строки

Примеры:

Пусть потомок таков:

#include<stdio.h>

main(int argc,char**argv,char **envp)

{ char *ptr =argv[0];

printf("Аргументы командной строки %s\n",argv[0]);

while(argc--) puts(*argv++);

printf("Среда программы %s\n",ptr);

while(*envp) puts(*envp++);

}

Использование  execv():

#include <process.h>

#include <stdio.h>

int main(int argc,char**argv)

{if(argc==1) {printf("Программа %s файл-потомок\

[парметры]\n",argv[0]); return 255 }

printf("Запуск %s через execv()\n",argv[1]);

argv++;

if(execv(argv[0],agv)==1)

{

 printf("\aНеудача с %s\n",argv[0]);perror("Причина");return

254; }

return 0;

}

Запуск программы с возвратом в родительскую программу:

Набор библиотечных функций spawnxxx() позволяет выбирать спосб

загрузки потомка - как с перекрытием предка, так и с

сохранением, что дает возможность возврата в предок.

int spawnl(int mode,char *path,char*arg0,...,NULL)

int spawnle(int mode,char *path,char*arg0,...,NULL,char**env)

int spawnlp(int mode,char *path,char*arg0,...,NULL)

int spawnlpe(int mode,char *path,char*arg0,...,NULL,,char**env)

int spawnv(int mode,char *path,char*argv[])

int spawnve(int mode,char *path,char*argv[],char**env)

int spawnvp(int mode,char *path,char*argv[])

int spawnvpe(int mode,char *path,char*argv[],char**env)

mode имеет 3 предопределенных значения:

P_WAIT - предок останавливается и запускается потомок, после

завершения которого управление возвращается в предок - это

единственно возможный вариант в MS DOS

P_OVERLAY - потомок завершает предка, что полностью аналогично

функциям execxxx().

Средства Турбо С для завершения программ

В Си доступны все средства MS DOS для завершения прграмм через

библиотечные функции :

#include<process.h> или <stdlib.h>

void abort(void) -выводит сообщение "Abnormal program

termination" , вызывает функцию _exit(), передав ей код возврата

3. Флэширование потоковых файловых буферов не выполняется. Для

завершения использует программу, на которую указывает вектор

24Н.

#include<assert.h>

void assert(int test) - используемый при отладке макро,

определенный так, что при test==ЛОЖЬ на stderr выдается

сообщение:

Assertion failed: ВЫРАЖЕНИЕ, ИМЯ ФАЙЛА, НОМЕР СТРОКИ

Блокируется директивой препроцессора #define NDEBUG 1

#include<stdlib.h>

int atexit(atexit_t func) - помещает во внутренний стек до 32

указателей точек входа в подпрограммы, выполняемые в порядке,

обратном регистрации, при завершении программы

void exit(int status) - выполняет нормальное завершение

программы с флэшированием всех файловых потоковых буферов и

вызовом всех зарегистрированных atexit() пордпрограмм.

void _exit(int status) - завершает программу немедленно бе

всякого сервиса

Сигналы и установка собственной реакции на Ctrl-Break

Мультипрограммные ОС генерируют сигналы при возникновении

исключительных ситуаций, для каждой из которых есть свой

обработчик - он выполняет обработку исключительной ситуации по

умолчанию. Но часто программе хочется иметь собственный

обработчик исключительной ситуации. В Турбо Си функция

#include<signal.h>

int raise(int sig) посылает сигнал sig, который может быть

воспринят текущей программой. Возможные сигналы для MS DOS:

SIGABRT -аварийное завершение программы через обработчик по

вектору 24Н, хранимый в текущем PSP

SIGFPE -ошибка арифметических операций

SIGINT - генерируется принажатии Ctrl-C или CTRL-Break

Установку собственной реакции на сигнал можно осуществить с

помощью функции

void *signal(int sig, void (*func)(int sig[,int subcode])

func должен задавать точку входа в функцию или быть равным

SIG_DEL (по умолчанию) или SIG_IGN (для игнорирования сигнала)

Пример собственной реакции на CTRL Break:

#include <stdio.h>

#include<signal.h>

#include<process.h>

int count;

void ctrl_c_handler(int sig)

{signal(SIGINT,SIG_IGN);/*Блокировка повторного вхождения */

count++;

printf(Э\a%d-е нажатие Сtrl-С\n",count);

if(count==3) exit(0) else signal(SIGINT,ctrl_c_handler);

}

void my_exit(void)

{puts("Завершение программы");}

int main(void)

{atexit(my_exit);

if(signal(SIGINT,ctrl_c_handler)==SIG_ERR)

{perror("Ошибка");return 1;}

puts("Для завершения нажмите Ctrl-C);

while(1)

getchar();

}

Можно использовать и специальную для MS DOS функцию установки

обработчика Ctrl Break:

#include<dos.h>

void ctrlbrk(int(*handler)(void));

Функция handler должна возвращать 0 если требуется завершение

текущей программы.

В заключение приведем Си-вариант интерпретатора команд shell.

Напомним постановку задачи:  получив управление, SHELL должен

выводить на экран приглашение к вводу sh: и ожидать от

пользователя ввода команды в виде строки, заканчивающейся

возвратом каретки. Приняв строку, программа пытается выяснить

наличие 1-го элемента строки в своей таблице внутренних команд;

при обнаружении будет вызываться соответствующая подпрограмма, а

при отсутствии - вызываться функция EXEC с передачей командной

строки пользователя интерпретатору COMMAND.COM с ключем /C,

используя его в качестве процессора команд под своим

управлением. Наш SHELL для начала будет распознавать только 3

команды:

CLS для очистки экрана

DOS для запуска COMMAND.COM

EXIT для завершения работы и возврата управления следующему

интерпретатору команд

Вам предлагается добавить новые внутренние команды, сотавив

необходимые подпрограммы и вставив имена этих подрпрограмм

вместе с текстовой строкой команды в таблицу COMMANDS.

Алгоритм прграммы в общем виде следующий:

1.Вызывается прерывание 21Н с функцией 4АН для сокращения

потребляемой памяти на случай предоставления необходимого

пространства интерпретатору COMMAND.COM. Для упрощения примера

количество резервируемых параграфов мы задаем щедрой константой,

хотя эта величина должна бы вычисляться.

2.Просматривается окружение в поисках переменной COMSPEC,

определяющей положение выполнимой копии COMMAND.COM - если

переменная не найдена, программа завершается после выдачи

аврийного сообщения.

3.Загружается адрес собственного обработчика прерывания в вектор

Ctrl-C (Int 32H), чиобы не потерять управление при нажатии

пользователем Ctrl-C или Ctrl - Break.

4.Выводится текст приглашения.

5.Считывается буферизованная строка с командой пользователя.

6.Первый элемент командной строки ищется в таблице и при

обнаружении вызывается соответствующая подпрограмма.

7.При отсутствии составляется хвост командной строки с

присоединением к ключу /C команды пользователя и с помощью EXEC

вызывается копия COMMAND.COM с передачей адреса составленного

хвоста команды через блок параметров EXEC.

8. Шаги с 4-го по 7-й повторяются пока не будет введена команда

EXIT, приводящая к завершению SHELL.

#include <stdio.h>

#include<process.h>

#include<stdlib.h>

#include<signal.h>

/*Макрокоманда для числа элементов в структуре*/

#define dim(x) (sizeof(x) / sizeof(x[0]))

/*Прототипы функций*/

unsigned intrinsic(char*);

void extrinsic(char*);

void get_cmd(char*);

void get_comspec(char*);

void break_handler(void);

void cls_cmd(void);

void dos_cmd(void);

void exit_cmd(void);

/*Таблица внутренних команд*/

struct cmd_table{

char *cmd_name;

int (*cmd_fxn)();

                }commands[]=

                {"CLS",cls_cmd,

                 "DOS",dos_cmd,

                 "EXIT",exit_cmd};

static char com_spec[64];  /*Спецификация файла COMMAND.COM*/

main(int argc, char *argv[])

{

 char inp_buf[80];           /*Входной буфер клавиатуры*/

get_comspec(com_spec);       /*Получить сп-ю файла COMMAND.COM*/

/*Регистрируем обраб прерываеия Ctrl-C*/

if(signal(SIGINT,break_handler)==(int(*)())-1)

{fputs("Не ловится прерывание по Ctrl-C",stderr);exit(1)}

while(1)                   /*Главный цикл интерпретатора*/

{

 get_cmd(inp_buf);           Получить команду

if(!intrinsic(inp_buf)) extrinsic(inp_buf); /*  Если внутренняя

                                  запускаем ее  подпрограмму,

                                  иначе в COMMAND.COM */

}

}

/*Поик команды пользователя в таблице внутренних; если

найдена,запускаем ее подпрограмму и возвратим истину иначе

ложь*/

unsigned intrinsic(char *input_string)

{

 int i,j;

/*Уберем лидирующие пробелы*/

while(*input_string=='\x20') input_string++;

/*Поиск в таблице команд*/

for(i=0;i<dim(commands);i++)

{

 j=strcmp(commands[i].cmd_name,input_string);

/*Если найдена*/

if(j==0) {(*commands[i].cmd_fxn)(); return(1);}

}

return(0);    /*Не нашли - возвращаем ложь*/

}

/*Обработка внешней команды передачей в копию COMMAND.COM,

которую получаем с помощью EXEC*/

void extrinsic(char*input_string)

{int status; status=system(input_string);

if(status) fputs("\nОшибка вызова COMMAND.COM\n",stderr); }

void get_cmd(char *buffer)

{

 printf("\nsh:");

gets(buffer);

strupr(buffer);

}

/*Получение полного пути и спецификации файла COMMAND.COM из

контекстной переменной COMSPEC*/

 void get_comspec(char* buffer)

 { strcpy(buffer,getenv("COMSPEC")); if(buffer[0]==NULL)

{fputs("\nВ контексте нет COMSPEC\n",stderr);exit(1);} }

void break_handler(void)

{

 signal(SIGINT,break_handler);

printf("\,sh:");

}

void cls_cmd(void)

{

 printf("\033{2j");

}

void dos_cmd(void)

{int status;

status=spawnlp(P_WAIT,com_spec,com_spec,NULL);

if(status) fputs("\nОшибка вызова COMMAND.COM\n",stderr);

}

void exit_cmd(void)

{

 exit(0);    /*Завершить shell*/

}


Управление субпроцессами в Турбо Паскале

MS DOS имеет мощные средства организации субпроцессов и Турбо

Паскаль предоставляет подпрограммы для доступа к этим средствам.

Программа с субпроцессами - такая программа, которая запускает

на выполнение другую программу, которая в свою очередь может

запустить еще одна и т д . Запускающая программа (процесс) как

бы замирает, оставаясь в оперативной памяти компьютера, а

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

другая программа. После его завершения вновь "оживает"

породившая его программа более высокого уровня. Типичным прмером

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

COMMANDER, запускающая различные файлы на выполнение вроде

dbview.exe.

При составлении программ с субпроцессами в Турбо Паскале всегда

необходимо предварительно расчитывать требования к памяти для

конкретной программы. Для управления памятью служит ключ

компилятора $M, который должен стоять до первой строки текста

программы в блоке основной программы :

{$M Рамер_стека, Минимум_Кучи, Максимум_Кучи}

Размер стека указывается в пределах 1024..65520, минимум кучи

обычно 0, но может быть до 655360 байт (если в момент запуска

программа имеет свободной памяти меньше минимума кучи,

работать она не сможет и остановится с выдачей сообщения),

максимум кучи - тоже не более 655360. Если свободной памяти

меньше, чем заявленный максимум кучи , то выделится столько,

сколько реально есть. Необходимый размер стека можно оценить,

просуммировав размеры локальных переменных в подпрограммах с

учетом вложенности их вызовов. Для определения необходимых

размеров кучи  можно просумировать размеры динамических

переменных, созданных с помощью New  и GetMem (заботясь при

этом о том , чтобы куча не была фрагментированной). После

определения размеров используемой родителем памяти можно

использовать специальные подпрограммы для запуска субпроцессов.

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

ней замена содержимого ряда системных векторов прерываний, в

частности это делает среда Турбо Паскаля, запоминая заменяемые

векторы в специальных переменных типа Pointer. При запуске

субпроцесса разумно предоставить ему возможность использовать

системные векторы, а не замененные. Подпрграмма SwapVectors

восстанавливает сохраненные в переменных с именами SaveIntNN

векторы, записывая туда же текущие, поэтому при повторном вызове

подпрограмма снова восстановит отключенные подпрограммы

прерываний - ее используют до и после подпрогрммы Exec.

SwapVectors      {Восстановление системных или временных

векторов прерываний до и после запуска субпроцессов.0 -

нормальное завершение}

Exec(ExeFile,ComLine:String)  {Запуск выполняемого файла

ExeFile (субпроцесс) со строкой параметров ComLine}

Для использования в качестве субпроцессов внатренних

(встроенных) команд MS DOS необходимо запускать COMMAND.COM и

передавать ему текст команды в виде параметров:

ECEC('command.com','/c copy a.txt + b.txt');

Важно не забывать включить в командную строку первым по счету

ключ /c для командного процессора - если это не сделать, то

получится выход в  ДОС и вернуться из субпроцесса можно будет

только подачей команды EXIT с клавиатуры.

При запуске командного процессора через подрограмму EXEC лучше

всего использовать его полное имя, которое можно в свою очередь

получить через подпрограмму модуля DOS GetEnv:

Exec(GetEnv('COMSPEC'),'/c format a: /s');

При таком запуске трудно проанализировать, чем завершилось

выполнение субпроцесса, так как аннализируется выполнение

командного процессора, который редко дает сбои. Для анализа

завершения субпроцессов можно использовать подпрограмму :

DosExitCode: Word   {Возвращает код завершения субпроцесса}

0 - нормальное завершение с возвратом управления

1 - субпроцес был прерван через нажатие Ctrl+Break

2 - субпроцесс был прерван по ошибке устройства

3 - субпроцесс остался резидентным после завершения процедурой

Keep


Управление субпроцессами в Бейсике

Для запуска субпроцессов в Бейсике есть команда SHELL, которая

со значительными ограничениями во многих версиях языка позволяет

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

Формат этой команды

SHELL командная_строка

Командная строка может быть просто именем программы или

дополнительно содержжать параметры. Если командная строка не

указана, то загружается копия COMMAND.COM и появляется

приглашение операционной системы к вводу команд ДОС. Возврат

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

команде EXIT.

Ограничения на применение команды SHELL необходимо изучит по

техническому описанию конкретной версии интерпретатора или

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

- если загружаемая программа меняет режим работы дисплея, то он

не будет автоматически восстановлен при возврате

-перед загрузкой субпрограммы все файлы должны быть закрыты;

-программа, которая хочет остаться резидентом в памяти не может

быть запущена  командой SHELL


Ввод с клавиатуры в Ассемблере
Управление вводом с клавиатуры в Ассемблере

Основным средством ввода для пользователя является клавиатура -

она служит для ввода командной строки при запуске программ из

ОС, обеспечивает подготовку текстов в текстовых редакторах,

позволяет управлять ходом программы нажатием всевозможных

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

и многое другое.

Клавиатура ПК содержит встроенный микропроцессор; он при каждом

нажатии и отпускании клавиши определяет ее порядковый номер,

называемый скэн - кодом, и помещает его в порт 60Н микросхемы

связи с периферией. 1-е 7 битов скэн - кода - это номер клавиши,

а 8-й сигнализирует нажатие (0) или отпускание (1).  При

удержании клавиши в нажатом состоянии дольше установленного

времени задержки микропроцессор клавиауры начнет генерировать

коды нажаия с заданной частотой. Задержка и частота могут

устанавливаться в нужные значения либо через порты клавиатуры,

либо использованием функции АН=03Н прерывания 16Н BIOS.

Стандартный обработчик аппаратного прерывания от клавиатуры

имеет номер 9 - по его вектору помещается BIOS - программа

обработки, анализирующая и обрабатывающая скэн - коды клавиш. В

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

скэн - кода:

1. Шифт - клавиши Shift,Alt,Ctrl.

2. Триггерные клавиши NumLock,ScrollLock,CapsLock

3. Клавиши с буферизацией расширенного кода

4. спецклавиши

За каждой шифт- и триггерной клавишей закреплен свой бит в

ячейках памяти по адресам 40:17Н и 40:18Н; при каждом нажатии и

отпускании эти биты инвертируются и состояние бита рассказывает

о том, нажата или отпущена клавиша. За триггерными клавишами

закреплены 2 бита, 1 из них инвертируется при нажатии а 2-й при

нажатии и отпускании. Текущее состояние этих клавиш используется

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

При нажатии большинства клавиш и их комбинаций в специальный

буфер помещается 2-байтовый BIOS - код ,у которого младший байт

равен ASCII - коду либо 0, а старший - скэн - коду или так

называемому расширенному скэн - коду. 2- байтовый 0 +

расширенный скэн - код записывается в буфер клавиатуры при

нажатии функциональных клавиш, Ins, Del, клавиш управления

курсором и их комбинаций с Alt, Ctrl, Shift и пр комбинациях Alt

с ASCII - клавишами. Клавишная комбинация PrnScr вызывает

прерывание 5, Alt+Ctrl+Del передает управление программе

начальной загрузки BIOS, а Ctrl+C записывает по адресу 00471Н

значение 80Н, используемое как флаг желания пользователя

остановить выполнение операций ввода - вывода.

Буфер клавиатуры.

Буфер BIOS для кодов клавиш занимает 32 байта с адреса 40:1ЕН до

40: 3ЕН, запись в буфер выполняет BIOS - обработчик 9-го

прерывания, а чтение - функции BIOS прерывания 16Н. Буфер

расчитан на 15 нажатий клавиш + 2 холостых байта для Enter.

Буфер организован как циклическая очередь, доступ к которой

осуществляется с помощью указателя "головы" по адресу 40:1АН и

указателя хвоста по адресу 40:1СН. Оба указателя содержат

смещение от сегмента 40Н. При каждом нажатии по указателю головы

записывается 2-байтовый код и указатель смещается на 2 байта,

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

может "обогнать" содержимое указателя хвоста. При чтении из

буфера смещается указатель хвоста. Если содержимое указателей

равно, буфер  пуст. Буфер клавиатуры - классический пример

использования кольцевого буфера для организации асинхронного

взаимодействия 2-х программ по схеме "производитель -

потребитель" - запись данных в буфер и считывание из буфера

происходят в случайные, не связанные между собой моменты

времени. При переполнении буфера производитель блокируется, пока

потребитель не освободит чтением одно или более мест в буфере.

Прикладные прграммы, выполняющиеся под управлением MS DOS, могут

использовать различные средства организации ввода с клавиатуры:

-Функции типа дескрипторов;

-Традиционные символьно-ориентированные функции ;

-Функции драйвера ПЗУ BIOS.

-непосредственный доступ к буферу клавиатуры.

Перечисленные средства обеспечивают различную степень гибкости,

переносимости и аппаратной независимости.

Для использования потокоориентированных функций типа

дескрипторов программа должна предоставить дескриптор выбранного

устройства ввода, адрес и длину приемного буфера памяти. Когда

программа начинает выполняться, MS DOS предоставляет в ее

распоряжение набор заранее определенных дескрипторов символьно -

ориентированных устройств, включая и клавиатуру:

Дескриптор      Устройство            Обозначение

  0          Клавиатура stdin            CON

  1          Дисплей    stdout           CON

  2          Дисплей    stderr           CON

  3          Последов порт stdaux        AUX

  4          Принтер    stdprn           PRN

Эти дескрипторы могут использоваться в операциях чтения - записи

без всякой предварительной подготовки. Кроме того программа

может получить дескриптор символьно - ориентированного

устройства операцией его открытия как файла для ввода или вывода

с использованием его логического имени. Дескрипторные функции

поддерживают операцию перенаправления ввода - вывода, позволяя

программе брать входные данные вместо например клавиатуры с

другого устройства или с дискового файла.  Дескрипторные функции

ввода с клавиатуры обслуживаются прерыванием 21Н с функцией 3FH.

Для примера используем дескриптор 0 для ввода строки с

клавиатуры:

buffer  db 80 dup (?) ;Буфер для ввода

...

mov ah,3fh            ;Функция чтения файла или устройства

mov bx,0              ;Дескриптор клавиатуры

mov cx,80             ;Максимальное число читаемых символов

mov dx,seg buffer     ;DS:DX=адрес буфера

mov ds,dx

mov dx,offset buffer

int 21h               ;вызов прерывания

jc error              ;переход если ошибка

...

После успешного выполнения прерывания флаг переноса сброшен а

регистр АХ содержит число прочитанных символов, при ошибке флаг

переноса установлен а АХ содержит код ошибки.

Стандартный ввод перенаправляем и поэтому приведенный фрагмент

дает не очень надежный способ приема ввода с клавиатуры - на

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

параметра указанного пользователем перенапрвления. Для точного

знания источника данных можно при вводе с клавиатуры отказаться

от предопределенного дескриптора и открыть консоль как файл:

buffer  db 80 dup (?) ;Буфер для ввода

fname   db  'CON',0   ;

handle  dw 0          ;

...

mov ah,3dh            ;

mov al,0

mov dx,seg fname

mov ds,dx

mov dx,offset name

int 21h

jc error

...

mov ah,3fh

mov bx,handle

mov cx,80

mov dx,offset buffer

int 21h

jc error

...

Ввод с клавиатуры с помощью традиционных обращений:

Прерывание 21Н      Описание                       Реакция на

с функцией                                          Ctrl-C

01h           Ввод с ЭХО на экран с ожиданием              Да

06h           Непосредственный ввод без ожидания           Нет

07h           Ввод без ЭХО с ожиданием                     Нет

08h           Ввод без ЭХО   с ожиданием                    Да

0Ah           Буферизованный ввод - FFh в AL если не пуст   Да

0Bh           Проверка состояния ввода -FFh в AL если не пуст

0Ch           Сброс буфера и ввод

              в AL помещается номер одной из ф-й ввода 1Н..0АН

Операции перенаправления затрагивают все эти функции,

они работают как специальный вид функции 3fh с дескриптором.

Первые 4 функции помещают код принятого символа в регистр AL.

Альтернативой посимвольному вводу является буферизованный ввод

строки функцией 0Ah - такие строки ОС формирует во внутреннем

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

ENTER:

buff db  81       ;

     db 0         ;

     db 81 dup (0);

    ...

        mov ah,0ah   ;

        mov dx,seg buff

        mov ds,dx

        mov dx,offset buff

        int 21h

        ...

Максимальная длина ввода для этой функции передается не в СХ, а

в 1-м байте буфера, принятая длина помещается не в АХ а во 2-й

байт буфера, символ соответствующий максимальной длине должен

быть Enter.

Применение клавиатурных функций ПЗУ BIOS предполагает выполнение

программы только на IBM - совместимых компьютерах.  Доступ через

BIOS позволяет отслеживать нажатие всех, а не только символьных

клавиш. Можно использовать 1 из 2-х методов BIOS-ного ввода:

первый использует обращение к драйверу ПЗУ BIOS по прерыванию

Int 16h:

00Н - чтение из буфера 2- байтового кода с ожиданием

      прочитанный код - в АХ

01Н - чтение без ожидания и без продвижения головы буфера-

      код остается в буфере

02Н - состояние шифт и триггерных клавиш х-ется байтом по адресу

     40:17Н и он возврщается в AL

03Н - установка задержки и частоты повторений AL=5 и в  ВН -

      задержка в 250 мс интервалах, в ВL - частота

05Н - запись в буфер кода, помещенного в СХ

10Н..12Н - аналоги 1..2 для 101 - клавишных клавиатур

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

клавиатурой.

Очистка буфера клавиатуры.

Выполняется обычно перед тем, как выдать запрос на ввод, чтобы

исключить обработку посторонних нажатий клавиш, которые могут к

тому моменту накопиться в буфере. Очистить буфер можно

использованием функции 0С прерывания 21Н, которая выполняет

любую функцию ввода с клавиатуры 1, 6, 7, А, но перед этим

чистит буфер клавиатуры. Надо просто поместить номер функции

ввода в AL :

mov ah,0ch

mov al,1

int 21h

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

приравнивания значений указателей на хвост и голову:

cli          ;запрещаем прерывания

sub ax,ax    ; обнуляем регистр

mov es,ax    ; добавочный сегмент на начало памяти

mov al,es:[41ah] ;берем указатель на голову

mov es:[41ch],al ;засылаем его в указатель хвоста

sti              ;разрешаем прерывания

Проверка наличия символов в буфере.

Проверка может основываться на сравнении значений указателей на

хвост и голову буфера - надо просто сравнить содержимое ячеек

памяти по адресам 0040:001АН и 0040:001СН:

mov ax,0

mov es,ax

mov al,es:[41ah]

mov ah,es:[41ch]

cmp ah,al

jne get_keystroke

Кроме того функция 0ВН прерывания 21Н возвращает 0FFH, если в

буфере есть символы и 0, если буфер пуст:

mov ah,0bh

int 21h

cmp al,0ffh

je get_keystroke

...

Функция 1 прерывания 16Н предоставляет ту же возможность, но

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

сбрасывается , если буфер пуст и устанавливается, если не пуст и

в этом случае копия символа в голове буфера помещается в АХ без

удаления его из буфера:

mov ah,1

int 16h

jz no_char

cmp al,0

je extended_code

Ожидание ввода без эха на экран.

Обычно вводимые с клавиатуры символы отображаются на экране,

чтобы было видно, что печатается. Но иногда такое автоматическое

эхо нежелательно, например в диалге с пользователем через меню

возможных выборов. Часто бывает необходимо вначале проверить

допустимость введенного символа перед выводом его на экран.

Любая программа, обрабатывающая расширенные коды клавиш, должна

вообще избегать эха на экран, так как первый байт этих кодов

ASCII 0 будет отображаться в виде пробелов между символами.

Функции 7 и 8 прерываеия 21Н ожидают ввода символа при пустом

буфере клавиатуры, появление символа на экране не отображается.

При этом ф-я 8 распознает Ctrl-Break с вызовом подпрограммы

обработки этой комбинации, а ф-я 7 не реагирует на нее. В обеих

случаях код символа возвращается в AL и если он 0, то это

признак расширенного кода - после повторного вызова прерывания в

AL появится 2-й байт расширенного кода:

mov ah,7

int 21h

cmp al,0

je ext_code

...

ext_code:

int 21h

cmp al,75 ;проверка на стрелку влево

jne c_r

jmp cur_left ; да - на процедуру

cmp al,77 ; проверяем на стрелку вправо и т д

c_r:

...

BIOS имеет процедуру, предоставляющую ту же возможность:

поместите 0 в АН и вызовите прерывание 16Н - ф-я ожидает ввода

символа и возвращает его в AL. В этом случае и расширенные коды

обрабатываются за одно прерывание - если в AL 0 , то в АН будет

номер расширенного кода. Ctrl Break при этом не обрабатывается:

mov ah,0

int 16h

cmp al,0

je ext_code

...

ext_code : cmp ah,75 ; и т д

Ожидание ввода с эхо на экран.

При таком вводе такие символы как возврат каретки или забой

переводятся в соответствующие перемещения курсора, а не

отображаются как ASCII - символы. Выдача эха идет в ту позицию,

где был установлен курсор.

Ф-я 1 прерывания 21Н ожидает ввода при пустом буфере, а затем

выдает его на экран в текущую позицию курсора с обработкой Ctrl

Break. Введенный симво возвращается в AL , для получения в AL

2-го байта расширенного кода (при AL=0) надо повторить

прерывание:

mov ah,1

int 21h

cmp al,0

je ext_code

...

ext_code:

int 21h

cmp al,77

jne c_r

jmp cur_r

cmp al, 75  ... и т д

Эта ф-я полностью игнорирует клавишу Esc , Backspace сдвигает

курсор влево, но не стирает символ, Enter переводит курсор в

начало текущей строки , т е выполняет возврат каретки без

перевода строки.

Прием символов без ожидания на пустом буфере.

Некоторые программы не могут останавливаться и ждать нажатия

клавиш - они принимают символ из буфера тогда, когда это им

удобно. Можно в этих случаях проверить , не пуст ли буфер.

Ф-я 6 прерывания 21Н - единственный способ получить символ без

ожидания и без эха на экран и без распознавания Ctrl Break.

Перед вызовом прерывания в DL надо поместить 0FFH. В другом

варианте ф-я 6 служит противоположной цели - печатает в текущей

позиции курсора символ, находящийся в DL. Флак нуля

устанавливается в 1 при пустом буфере, принятый символ

помещается в AL, для получения 2-го байта расширенного кода

прерывание надо повторить:

mov ah,6

mov dl,0ffh

int 21h

jz no_char

cmp al,0

je ext_code

...

ext_code: int 21h

...

Получение строки символов

Ф-я 0АН прерывания 21Н позволяет вводить строку до 254 символов

с эхо на экран до нажатия Enter.В DS:DX должен быть адрес строки

с ее длиной в 1-м байте. После ввода строки во 2-м байте будет

ее фактическая длина, а сама строка начнется с 3-го байта. Т о

для получения 50 - символьной строки надо отвести 53 байта и в

первый записать 51. После ввода 50 символов 2-й байт будет

содержать 50, а 53-й - 13.

string db 53 dup(?)

lea dx,string

mov bx,dx

mov al,51

mov[bx],al

mov ah,0ah

int 21h

mov ah,[bx]+1  ; теперь длина в АН

В этой прцедуре можно использовать возможности редактирования -

Backspace или стрелка влево стирают символ без записи его в

память, работает табуляция, расширенные коды игнорируются. При

попытке ввести более длинную строку, чем указано в 1-м байте

лишние символы не принимаются и пищит динамик.

Есть и другой способ получения строки без эха на экран - ф-я 3FH

прерывания 21Н, которая чаще всего используется при дисковых

операциях. Она требует дескриптор файла - для клавиатуры он 0 и

должен быть помещен в ВХ. В DS:DX размещается адрес приемного

буфера строки, а в СХ - максимальная длина:

mov ah,3fh

mov bx,0

lea dx,str_buffer

mov cx,100

int 21h

В конец строки ДОС добавляет 2 символа 13 и 10, фактическая

длина возвращается в АХ с учетом 2-х символов - ограничителей.


Ввод с клавиатуры в Си
Управление вводом с клавиатуры в Си

Рассмотренные в уроке 06а функции MS DOS для ввода с клавиатуры

могут вызываться напрямую из Си - программ через библиотечные

подпрограммы geninterrupt(), int86(), intr() и тп либо неявно

подпрограммами ввода Турбо С.

Большая группа функций Турбо С для ввода с клавиатуры - это

функции потокового и префиксного ввода. Они используют ф-цию MS

DOS АН=3FH с префиксом стандартного ввода 0. Следовательно,

используя ф-ции ввода из файла stdio.h , невозиожно выполнить

неотображаемый на экране ввод символов, обойти обработку Ctrl

Break, определить нажатие спецклавиш. Для выполнения таких

действий используются прототипы функций из файла conio.h. Они

пасчитаны на построение простейших оконных интерфейсов, поэтому

при любом выводе на экран с их помощью корректируются значения

переменных, хранящих текущие координаты курсора активного окна.

Кроме того предоставляется возможность управления цветом вывода.

Итак:

#include <conio.h>

char *cgets(char *str) помещает в буфер str строку символов со

стандартного ввода; запись символов начинается с str[2]; str[0]

должен содержать максимальную длину строки - используется ф-ция

0АН, но символ CR преобразуется в '\0' c порождением корректной

ASCIIZ - строки. По сравнению с gets() ф-ция позволяет задать

длину вводимой строки, защититься при вводе от лишних символов,

возможность ввода строк длиннее 128 символов , установленных для

стандартного ввода.Пример:

#include <conio.h>

#include <stdio.h>

void main(void)

{char str[256]; str[0]=254; cgets(str);

printf("Введено %d символов строки %s\n",str[1],&str[2]);

}

int cscanf(char *format[,arg,...])

выполняет форматированный ввод с клавиатуры без буферизации

ввода, все введенные символы доступны в программе немедленно.

Ввод пробела рассматривается как завершение ввода, работает она

через ф-цию АН=07Н, а эхо выполняет Турбо С.

int getch(void) - вводит через ф-цию АН=07Н без эха.

int getche(void) - то же но с эхо, перевод строки происходит при

достижении правой границы активного окна. Пример:

#include<conio.h>

#include<stdio.h>

void main(void)

{int ch;

do{puts("Нажмите любую клавишу");

if(!(ch=getch())) {

 ch=getch(); printf("Спецклавиша - расширенный скэн - код

%#u\n",ch); }

else printf("Символьная клавиша %c Код %#u\n",ch,ch);

puts("Продолжаете? (y/n)");

 }

while((ch=getch())=='y' ||ch=='Y');

}

char * getpass(const char *prompt) - выводит строку prompt, а

затем принимает с клавиатуры без эха строку символов не длиннее

7; введенные символы помещаются во внутреннюю память, указатель

на введенную строку возвращается. Основное назначение - ввод

паролей. Пример:

#include<conio.h>

#include<stdio.h>

int main(void)

{char *correct_string = "\x04\x0E\x1C\x0E\x1B\x04\x06"; /*шифр

                                               слова KASATKI*/

char *input_string;

input_string=getpass("Введите пароль:");

while(*correct_string)

if(*input_string++!=(*correct_string++ ^ 0x4f))

{puts("\aНекорректный пароль - конец связи");return 1;}

purs("Доступ разрешен"); return 0;

}

int kbhit(void) - проверяет буфер клавиатуры на пустоту,

возвращая не 0 если там что-то есть. Использует 0ВН.

int ungetch(int ch) - записывает символ ch в буфер клавиатуры,

он будет доступен при следующей опрации чтения с консоли

функциями файла conio.h. Пример:

#include<conio.h>

#include<stdio.h>

void main(void)

{char *string ="Тест - строка";

while(*string) {ungetch(*string++); putchar(getch());}

}

Ввод в Турбо С средствами BIOS.

Рассмотрим на примере функции enter_kb_bios(), предназначенной

для записи 2- байтового кода клавиши key_code в буфер

клавиатуры:

#include<dos.h>

int enter_kb_bios(usigned key_code)

{struct REGPACK r;

r.r_cx=key_cde;

r.r_ax=0x0500;

intr(0x16,&r;

return r.r_ax & 0x00ff;

}

Использует эту ф-цию пусть демонстрационная программа, которая

пишет в буфер клавиатуры коды клавиш для запуска IDE Turbo C с

именем файла в командной строке и перходом в режим

редактирования. Помещенные в буфер коды клавиш набирают тестовую

программу, запускабт ее на компиляцию и завершают работу IDE с

сохранением набранной программы:

#include <process.h>

#include <stdio.h>

#include <conio.h>

int enter_kb_bios(unsigned);

int main(void)

{/*Коды клавиш имитации ввода текста "main(){}", нажатия F9,

Alt-x, y, Enter*/

unsigned key_codes[]={0x006d, 0x0061,

0x0069,0x006e, 0x0028, 0x0029, 0x000d, 0x007b, 0x007d,  0x4300,

0x2d00, 0x0079, 0x000d, 0x0000}

int i=0;

while(key_codes[i]) enter_kb_bios(key_codes[i++]);

if(spawnlp(P_WAIT,"tc.exe","tc.exe","new.c",0)==1)

{perror("\a Ошибка запуска Турбо С");

while(kbhit()) getch(); /*Чистим буфер клавиатуры*/

return 1;

}

puts("Демонстрация завершена");

while(kbhit()) getch();

return(0);

}

Работа ф-ции BIOS АН=02Н прерывания 16Н моделируется

библиотечной ф-цией Турбо С:

#include<bios.h>

int bioskey(int cmd) в зависимости от cmd обращается к АН=00 или

02Н прерывания 16Н.

Очистка буфера клавиатуры.

Может быть выполнена несколькими способами: приравниванием

указателей головы и хвоста, использованием ф-ции АН=0СН с

операцией ввода в AL, циклическим чтением через bioskey(0) или

getch().

Очистка:

#include <dos.h>

void clear_kb(void)

{char register _es *tail=0x1c, _es *head = 0x1a;

_ES=0x40; disable(); *tail=*head;enable();

}

Ввод с предварительной очисткой:

#include<dos.h>

unsigned int getc_kb(void)

{register ret_ax=0;

_AX=0x0c01; geninterrupt(0x21);

ret_ax-_AL;

if(!ret_ax) {_AX=0x0c01;geninterrupt(0x21);

ret_ax=_AL;return ret_ax<<8; }

return ret_ax;

}

Чтение символов из буфера клавиатуры.

выполняется доступом к ячейке памяти, на которую ссылается

указатель головы:

#include<dos.h>

unsigned key_kb(void)

{register char _es *tail =(char _es*)0x1c;

register char _es *head =(char _es*)0x1a;

_ES=0x40;

if(*head == *tail) return 0;

disable(); tail=(char _es*)(*head);enable();

return *(unsigned _es*)tail;

}

Запись в буфер клавиатуры.

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

построении демонстрационных программ, переносе текстов в любую

прикладную программу из другой резидентной программы и т д

Может выполняться на 2-х уровнях: использованием ф-ции АН=5

прерывания 16Н или непосредственным доступом к ячейкам памяти

клавиатурного буфера. В приведенном ниже примере символ вводится

в буфер либо по текущему значению указателя головы либо по

значению хвоста. Передаваемые ф=ции параметры: where задает

режим - если where =0 код помещается со сдвигом указателя головы

назад, если where =1 код помещается со сдвигом хвоста вперед,

key_code - 2-байтовый код для записи в буфер.

#include<dos.h>

enter_kb(int where, unsigned key_code)

{unsigned register _es *tail = (unsigned _es*)0x1c,

_es *head = (unsigned _es*)0x1a, _es* tmp,ret;

_ES=0x40;

disable();

switch(where){

case 0: tmp=(unsigned _es*)*head; tmp--;

if(tmp < (unsigned _es*)0x1e)

/*Перескок на конец буфера*/

tmp=(unsigned _es*)(0x3e -2);

if(tmp==(unsigned _es*)*tail) /*Буфер полон*/

{

 ret = 1; break;

}

else

{*(unsigned _es*)tmp=key_code;

*head=(unsigned _es*)tmp;

ret=0; break;

}

case 1:

tmp=(unsigned _es*)*tail;

if(tmp==(unsigned _es*)0x3e)

/*Перескок на начало буфера*/

tmp=(unsigned _es*)0x1e;

if(tmp+1==(unsigned _es*)*head /*Буфер полон*/

{

 ret=1;break;

}

else

{*(unsigned _es*)tmp=key_code; tmp++; *tail=(unsigned)tmp;

ret=0;break;

}

default: ret=2; /*Ошибка*/

}

enable();

return ret;}


Ввод с клавиатуры в Турбо Паскале
Управление вводом с клавиатуры в Турбо Паскале

Очистка буфера клавиатуры.

procedure ClrKeyBuf;

VAR ch:Char;

begin while KeyPressed do ch:=ReadKey; end;

Ожидание нажатия клавиши.

Procedure Wait;

begin repeat until KeyPressed end;

Прием символа нажатой клавиши.

uses crt;

{$I clrkeybuf.inc}

{$I wait.inc}

VAR c:char;

begin

ClrScr;

Writeln('Нажмите любую символьную клавишу');

ClrKeyBuf;

c:=ReadKey;

Writeln('Была нажата клавиша с символом',c);

Writeln('Пауза до нажатия любой клавиши...');

Wait;

ClrKeyBuf;

end.

Если надо ожидать ввода не любых, а конкретных символов, то

поступают так:

uses crt;

{$I clrKey.inc}

VAR c:char;

begin

...

Writeln('Нажмите А, Б или В');

ClrKeyBuf;

repeat

c:=ReadKey

until(c in ['А'..'В','а'..'в']);

clrkeybuf;

case c of

'А','а':begin ...end;

'Б','б':begin ...end;

'В','в':begin ...end;

end;

ClrKeyBuf;

...

end.

Опрос клавиш в регистре Ctrl.

Нажатие алыавитной клавиши совместно с Ctrl в ReadKey дает ASCII

- код в диапазоне 1..26, т е порядковый номер буквы в латинском

алфавите независимо от включенности регистра кириллицы.

Опрос расширенных кодов и функциональных клавиш.

Program TestKeyBoard;

uses crt;

VAR ch:char;

ExtendedKey:Boolean; {Флаг расширенного кода}

{$I clrkey.inc}

{$I wait.inc}

begin

ClrScr;

...

ClrKeyBuf;

Writeln('Нажмите функциональную клавишу');

repeat

ch:=ReadKey;

if ch =#0 then

begin ch:=ReadKey;ExtendedKey:=True end

else ExtendedKey:=False;

until((ch in [#59..#68]) and ExtendedKey);

...

Writeln('Была нажата клавиша F',(Ord(ch)-58:1);

Wait;ClrScr

end.

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

убрать вызов ClrKeyBuf и переписать основную часть так:

if KeyPressed then begin

ch:=ReadKey;

if ch=#0 then begin

ch:=ReadKey;

Extended :=True end

else Extended :=False;

end else ch:=#0;

Турбо Паскаль предоставляет возможность включения в текст

программы управляющих кодов клавиатуры - для этого надо нажать

Ctrl+P и следом управляющую или функциональную клавишу - на

экране появится символ с измененным цветом и его можно

трактовать как символ, соответствующий нажатой спецклавише:

case Readkey of

'@H': ... курсор вверх

'@P': ...курсор вниз

'@;':    F1

'[' :  Esc

end;

Запись в клавиатурный буфер.

{$M 4096,0,0)

uses crt,dos;

Procedure UnReadKey(KeyW:Word);

CONST

KbdStart =$1E;

KbdEnd   =$3c;

VAR

KbdHead:Word absolute $40:$1A;

KbdTail:Word absolute $40:$1C;

OldTail:Word;

begin

OldTail:=KbdTail;

if KbdTail=KbdEnd then

KbdTail:=KbdStart

else Inc(KbdTail,2);

if KbdTail=KbdHead then KbdTail:=OldTail

else MemW[$40:OldTail]:=KeyW;

end;

Procedure UnReadExtCode(ExtCode:Word);

begin UnReadKey(Swap(ExtCode)) end;

Procedure UnReadString(S:String);

VAR i:Byte;

begin Delete(S,17,255);

for i:=1 to Length(S) do UnReadKey(Ord(S[i])); end;

VAR St:String;

begin

ClrScr;

TextAttr:=Black+16*LightGray;

Write('Допишите число или исправьте:');

TexAttr:=LightGray;

UnReadString('22-2-19');

ReadLn(St);

Write(#10'Результирующая строка->');

HighVideo; Writeln(St); LowVideo; ReadLn;

St:='Hello Outer !'+#13;

Writeln('В буфер передается строка :',St);

UnReadString(St);

Writeln(#10'Запускающий файл Outer.');

SwapVectors; Exec('Outer.exe',''); SwapVectors;

ReadLn

end.

При этом файл OUTER.PAS может быть таким:

var s:String;

begin Write(#10'Запрос <--');Readln(S);

Writeln(#10#10'На запрос была введена строка:',S)

end.


Ввод с клавиатуры в Бейсике
Управление вводом с клавиатуры в Бейсике

Очистка буфера

В Бесике для получения и изменения значений указателей

клавиатурного буфера используются операторы PEEK и POKE:

DEF SEG = &H40

POKE &h1C, PEEK(&H1A)

Это не лучший метод очистки буфера- некоторые прграммы могут

создавать буфер в другом месте памяти и всегда существет

возможность поступления клавиатурного прерывания во время

выполнения операторов, которое может изменить указатель хвоста.

Поэтому предпочтительнее читать из буфера до тех пор, пока не

получим символ ASCII 0:

while inkey$ <> "" wend

Проверка наличия символов в буфере

DEF SEG =&H40

IF PEEK(&H1C) THEN ...БУФЕР НЕ ПУСТ

Ожидание ввода без эха на экран

Ф-ция бейсика INKEY$ не дает эха на видеоерминал, возвращая

сроку длиной в 1 байт для символов ASCII и 2 байта для

расширенных кодов. Но для ожидания нажатия клавиш ее надо

поместить в цикл (если не нажата клавиша, INKEY$ возвратит

пустую строку длиной 0):

while c$="" c$=INKEY$ wend

Ожидание ввода с эхо на экран

В Бейсике надо перехватить символ оператором INKEY$ и вывести на

экран до получения следующего либо оператором PRINT , либо

оператором POKE прямо в видеобуфер:

while c$="" c$=INKEY$ wend

PRINT C$

def seg=&HB000

point=1678

while c$="" c$=INKEY$ wend

poke pointer,asc(c$)

point=point+2

...

Прием символа без ожидания

Просто используется INKEY$:

c$=INKEY$

ifc$<>"" then ...

Получение строки символов

Бейсик может принимать строки как с автоматическим эо на экран

так и без него. Ввод с эхом выполняется встроенной функцией

INPUT:

INPUT "Введите Ваше имя",NAME$

INPUT "Введите ваш возраст",AGE%

Макроопределения для отдельных клавиш

Бейсик имеет встроенный механизм для создания макроопределений,

но позволяет программировать только 10 функциональных клавиш

строками не длиннее 15 символов. Присвоение макроопределений

выполняет оператор KEY:строка KEY 5,"END" приведет к выводу на

экран слова END при нажатии F5.


Архитектура  видеосистем

Основные типы дисплеев.

- Композитный дисплей. Имеет 1 аналоговую входную линию, может

быть цветным или монохромным. Видеосигнал поступает в дисплей в

стандарте NTSC, который используется и в телевидении.

Применяется совместно с видеоадаптером CGA.Сам дисплей может

отображать до 16 цветов, но с CGA обеспечивает 4 цвета для

графики и 8 цветов для текста. В монохромном варианте имеет

разрешающую способность 720х350 и используется в основном для

систем подготовки текстов с адаптером улучшенного типа Hercules.

В цветном варианте имеет разрешение 640х200 и в настоящее время

не используется.

-Цифровой дисплей. Имеет от 1 до 6 входных линий, может

отображать до 2^n цветов (любые 16 из 64 при 6 линиях), может

использоваться с адаптерами EGA, CGA. Разрешающая способность

640х350, размер символов 8х14 пикселов.

-Аналоговый RGB - дисплей с 3-мя аналоговыми входными линиями

для красного, зеленого и синего цветов, количество цветов

ограничено только возможностями адаптера. Используется с VGA,

Super VGA, XGA.

Видеоадаптеры.

Это утсройство на специальной электронной плате, с собственным

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

дисплей. В самом общем виде вдеоадаптер состоит из контроллера

электронно - лучевой трубки (CRT - контроллера) и видеопамяти

(видеобуфера), а в более совершенных системах могут иметь

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

манипуляций содержимым видеопамяти. Видеобуфер хранит образ

экранной инфрмации, а видеоадаптер 25-30 раз в секунду освежает

(заново рисует) изображение на экране. Изображение на экране

строится из небольших телевизионных точек прямоугольной формы -

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

числом пикселов в строке и числом строк.

Память, необходимая для хранения одного полного образа экрана

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

видеопамяти можно хранить в видеобуфере несколько страниц - одна

из них будет отображаемой или текущей.

Видеорежимы.

Интегральной характеристикой видеоадаптера является совокупность

поддерживаемых им режимов. Режимы принято нумеровать, начиная с

0 - чем совершеннее адаптер, тем больше режимов он поддерживает

- как правило более совершенные адаптеры поддерживают режимы

своих менее совершенных предков.

При всем разнообразии режимов их можно объединить в 2 группы:

текстовые и графические - переключение из одного режима в другой

полностью изменяет логику взаимодействия адаптера с

видеобуфером.

Всего насчитывается около 20 стандартных и 200 нестандартных

режимов и с появлением новых типов видеосистем этот список

расширяется - мы рассмотрим только основные:

Стандартные:  0,1-текстовые цветные 40х25,

8 основных и 8 интенсивных цветов для CGA,

16 из 64 для EGA

16 из 262144 для VGA

видеостраница 8 кбайт,EGA и VGA -до 8 страниц

2,3-текстовые цветные  80х25,16 цветов видеостраница 16 кбайт

7-текстовый монохром   80х25,2 цвета

4,5-цветная графика 320х200,8 цветов (по 4),1 страница,

2 бита на пиксел

6  -2-цветная графика 640х200,2 цвета 1 страница

0Dh-цветная графика, 320х200 ,16 цветов,страница 32 кбайт

0Eh-цветная графика  640х200  ,16 цветов,страница 64 кбайт

0Fh-монохр  графика  640х350    2 стр по 64 кбайт

10h-цветная графика  640х350   16 цветов (при 64 кб-4цвета)

11h-                 640х480   2 цвета

12h-цветная графика  640х480   16цветов 1видеостр (только VGA)

13h-                 640x200   256 цветов

В текстовых режимах экран рассматривается как совокупность

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

Каждому текселу соответствуют 2 байта в видео буфере - четный

хранит ASCII - код символа, а следующий за ним нечетный байт

кодирует цвет пикселов для очертания символа, цвет фона,

необходимость включения режима мерцания и повышенной яркости -

этот байт называют байтом атрибута. При побитной расшифровке

байт атрибута выглядит следующим образом:

Биты 0,1,2 - Foreground Color - код цвета символа

     3     - Яркость символа

     4,5,6 - Цвет фона символа

     7     - Режим мерцания

Присваивая различные значения байтам атрибута в видеобуфере

можно управлять цветом символов и фона, на котором изображается

их рисунок. Например если байт атрибута равен

           128 + 64 + 32 + 16 = 240

то выведется мерцающий черный символ на сером фоне (смешивание

в одинаковой пропорции синего, зеленого и красного на RGB

-иониторах дает серый цвет). Включение бита интенсивности

"осветляет" цвет, светло-серый - это белый. При атрибуте

           8 + 4 + 2 + 1 = 15

будут белые символы на черном фоне. Если задать одинаковые цвета

фона и символов , то символы будут невидимы, например на

красном фоне с атрибутом 44Н.

Видеоадаптнры EGA и VGA имеют некоторые особенности

использования бита интенсивности, которые мы рассмотрим позже.

Видеопамять в текстовых режимах доступна для непосредственного

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

память - из нее можно читать и в нее можно записывать.Если

адаптер работает в текстовых режимах "40 столбцов х 25 строк" то

для хранения полного образа экрана (видеостраницы) необходимо

25х40х2 = 2000 байт паияти, а в режимах 80х25 уже 4000 байт.

Адреса начала видеобуферов для различных типов адаптеров:

CGA,EGA,VGA - B8000Н для режимов 0..6

Hercules    - B0000H для режима 7

EGA,VGA     - A0000Н для режимов DH..13H

"Освежение" видеобуфера происходит, начиная с некоторого

начального адреса - смещения до видеостраницы . Страница 0 имеет

0-е смещение, страница 1 в режиме 80х25 начинается со смещения

4096 (1000Н) относительно начального адреса видеопамяти и т д.

Если изменить смещение для операции "освежения" видеобуфера,

произойдет переключение страницы и на экране возникнет образ

другой страницы видеопамяти, а образы других страниц останутся в

памяти невредимыми и пригодными для последующего отображения.

Байт кода символа используется видеоадаптером как индекс для

входа в специальную таблицу знакогенератора - она задает 2-ичные

коды строк рисунков символов ; число строк для рисунка символа

зависит от разрешающей способности (количества телевизионных

линий умещающихся на экране) и может быть 8, 14, 16, а число

пикселов в строке символьного изображения обычно 8 или 9.

При работе в ГРАФИЧЕСКОМ режиме обеспечивается возможность

управлять цветом любого пиксела на экране - экран в графическом

режиме представляет собой матрицу пикселов. Коды цветов пикселов

хранятся в видеобуфере, при этом в режимах 4..6 (CGA - режимы)

возможен непосредственный доступ к видеопамяти из программы

столь же простой, как и в текстовых режимах - записывая по

нужным адресам коды цветов пикселов. Для кодирования цветов в

4 - цветных режимах 4 и 5 используется 2 бита на пиксел и водном

байте можно записать цвета 4-х пикселов. В 2 - цветном   режиме

6 для кодирования цвета пиксела достаточно одного бита. В

режимах 4..6 коды четных и нечетных строк в видеобуфере

сгруппированы в 2 отдельных массива - по смещению 0 от начала

страницы располагаются коды пикселов четных строк, а со смещения

2000Н - нечетных.

При работе в графических EGA - режимах 0Dh..10h логика

использования видеопамяти существенно меняется. Для каждого

пиксела в видеобуфере отводится 4 бита и количество

одновременно отображаемых цветов равно 16. Одному и тому же

адресу видеопамяти соответствуют сразу 4 байта, расположенных на

так называемых битовых планах и кодирующих в совокупности 8

соседних пикселов телевизионной строки экрана. EGA - адаптер

имеет 4 битовых плана (I, R, G, B) и любому пикселу

соответствует по 1 биту на каждом плане. Код цвета в EGA - это

номер одного из 16-ти специальных внутренних 6-тиразрядных

регистров палитры, в которых и находится код отображаемого на

экране цвета. Т о EGA позволяет выбрать 16 цветов из 64

возможных. Возможные 64 цвета задаются форматом

      r g b R G B

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

нормальную соответствующего цвета. Результирующий цветовой

сигнал образуется сложением сигналов цвета, управляемых каждым

из 6 бит кодов цвета. Т о для каждого из основных цветов -

красного, зеленого, синего - возможны 3 градации яркости:

половинная, нормальная и полуторная.

VGA использует подобную схему преобразования кода цвета пиксела

в реальный цвет на экране, но значение в регистре палитры задает

не код цвета, а номер DAC - регистра (регистра цифро -

аналогового преобразователя), т к на вход монитора необходимо

послать аналоговый сигнал (напряжение), соответствующий яркости

основных цветов. ЦАП имеет 256 3- байтовых регистров (по байту

для красного, зеленого, синего), при этом 6 битов байта кодируют

интенсивность цвета (64 оттенка яркости) и число возможных

цветов получается 64х64х64=262144. Значения в DAC - регистры

можно записать с помощью подпрограмм BIOS или непосредственным

доступом к портам адаптера. При работе VGA в режимах EGA

используются только первые 64 регистра, задающие 64 возможных

цвета палитры EGA. Память VGA в режимах 0Dh..10h  тоже делится

на битовые планы и код цвета пиксела задает номер регистра

палитры, в качастве регистров палитры выступают 4 блока DAC -

регистров по 64 регистра в блоке. В текущий мометт времени может

быть активеым только 1 блок.

В режиме 13Н на экране могут отображаться сразу 256 различных

цветов и для кодирования цвета каждого пиксела нужен целый байт,

так как разрешение в этом режиме 320х200, то всего необходимо

64000 байт. В этом режиме коды всех пикселов располагаются в

видеопамяти подряд, начиная с адреса А000:0000Н, без деления на

четные и нечетные строки. Код цвета задает номер одного из 256

DAC - регистров. Изменением значений в DAC - регистрах можно

менять цвет пиксела на экране для заданного кода.

В текстовых режимах видеоадаптер отображает курсор, а BIOS имеет

средства отслеживания и установки текущей позиции курсора. В

графических режимах курсор не отображается, хотя вывод через

BIOS в текущую позицию невидимого курсора по прежнему возможен,

как и задание курсорной позиции.


Управление видеосистемой в Ассемблере

Основные типы дисплеев.

- Композитный дисплей. Имеет 1 аналоговую входную линию, может

быть цветным или монохромным. Видеосигнал поступает в дисплей в

стандарте NTSC, который используется и в телевидении.

Применяется совместно с видеоадаптером CGA.Сам дисплей может

отображать до 16 цветов, но с CGA обеспечивает 4 цвета для

графики и 8 цветов для текста. В монохромном варианте имеет

разрешающую способность 720х350 и используется в основном для

систем подготовки текстов с адаптером улучшенного типа Hercules.

В цветном варианте имеет разрешение 640х200 и в настоящее время

не используется.

-Цифровой дисплей. Имеет от 1 до 6 входных линий, может

отображать до 2^n цветов (любые 16 из 64 при 6 линиях), может

использоваться с адаптерами EGA, CGA. Разрешающая способность

640х350, размер символов 8х14 пикселов.

-Аналоговый RGB - дисплей с 3-мя аналоговыми входными линиями

для красного, зеленого и синего цветов, количество цветов

ограничено только возможностями адаптера. Используется с VGA,

Super VGA, XGA.

Видеоадаптеры.

Это утсройство на специальной электронной плате, с собственным

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

дисплей. В самом общем виде вдеоадаптер состоит из контроллера

электронно - лучевой трубки (CRT - контроллера) и видеопамяти

(видеобуфера), а в более совершенных системах могут иметь

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

манипуляций содержимым видеопамяти. Видеобуфер хранит образ

экранной инфрмации, а видеоадаптер 25-30 раз в секунду освежает

(заново рисует) изображение на экране. Изображение на экране

строится из небольших телевизионных точек прямоугольной формы -

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

числом пикселов в строке и числом строк.

Память, необходимая для хранения одного полного образа экрана

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

видеопамяти можно хранить в видеобуфере несколько страниц - одна

из них будет отображаемой или текущей.

Видеорежимы.

Интегральной характеристикой видеоадаптера является совокупность

поддерживаемых им режимов. Режимы принято нумеровать, начиная с

0 - чем совершеннее адаптер, тем больше режимов он поддерживает

- как правило более совершенные адаптеры поддерживают режимы

своих менее совершенных предков.

При всем разнообразии режимов их можно объединить в 2 группы:

текстовые и графические - переключение из одного режима в другой

полностью изменяет логику взаимодействия адаптера с

видеобуфером.

Всего насчитывается около 20 стандартных и 200 нестандартных

режимов и с появлением новых типов видеосистем этот список

расширяется - мы рассмотрим только основные:

Стандартные:  0,1-текстовые цветные 40х25,

8 основных и 8 интенсивных цветов для CGA,

16 из 64 для EGA

16 из 262144 для VGA

видеостраница 8 кбайт,EGA и VGA -до 8 страниц

2,3-текстовые цветные  80х25,16 цветов видеостраница 16 кбайт

7-текстовый монохром   80х25,2 цвета

4,5-цветная графика 320х200,8 цветов (по 4),1 страница,

2 бита на пиксел

6  -2-цветная графика 640х200,2 цвета 1 страница

0Dh-цветная графика, 320х200 ,16 цветов,страница 32 кбайт

0Eh-цветная графика  640х200  ,16 цветов,страница 64 кбайт

0Fh-монохр  графика  640х350    2 стр по 64 кбайт

10h-цветная графика  640х350   16 цветов (при 64 кб-4цвета)

11h-                 640х480   2 цвета

12h-цветная графика  640х480   16цветов 1видеостр (только VGA)

13h-                 640x200   256 цветов

В текстовых режимах экран рассматривается как совокупность

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

Каждому текселу соответствуют 2 байта в видео буфере - четный

хранит ASCII - код символа, а следующий за ним нечетный байт

кодирует цвет пикселов для очертания символа, цвет фона,

необходимость включения режима мерцания и повышенной яркости -

этот байт называют байтом атрибута. При побитной расшифровке

байт атрибута выглядит следующим образом:

Биты 0,1,2 - Foreground Color - код цвета символа

     3     - Яркость символа

     4,5,6 - Цвет фона символа

     7     - Режим мерцания

Присваивая различные значения байтам атрибута в видеобуфере

можно управлять цветом символов и фона, на котором изображается

их рисунок. Например если байт атрибута равен

           128 + 64 + 32 + 16 = 240

то выведется мерцающий черный символ на сером фоне (смешивание

в одинаковой пропорции синего, зеленого и красного на RGB

-иониторах дает серый цвет). Включение бита интенсивности

"осветляет" цвет, светло-серый - это белый. При атрибуте

           8 + 4 + 2 + 1 = 15

будут белые символы на черном фоне. Если задать одинаковые цвета

фона и символов , то символы будут невидимы, например на

красном фоне с атрибутом 44Н.

Видеоадаптнры EGA и VGA имеют некоторые особенности

использования бита интенсивности, которые мы рассмотрим позже.

Видеопамять в текстовых режимах доступна для непосредственного

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

память - из нее можно читать и в нее можно записывать.Если

адаптер работает в текстовых режимах "40 столбцов х 25 строк" то

для хранения полного образа экрана (видеостраницы) необходимо

25х40х2 = 2000 байт паияти, а в режимах 80х25 уже 4000 байт.

Адреса начала видеобуферов для различных типов адаптеров:

CGA,EGA,VGA - B8000Н для режимов 0..6

Hercules    - B0000H для режима 7

EGA,VGA     - A0000Н для режимов DH..13H

"Освежение" видеобуфера происходит, начиная с некоторого

начального адреса - смещения до видеостраницы . Страница 0 имеет

0-е смещение, страница 1 в режиме 80х25 начинается со смещения

4096 (1000Н) относительно начального адреса видеопамяти и т д.

Если изменить смещение для операции "освежения" видеобуфера,

произойдет переключение страницы и на экране возникнет образ

другой страницы видеопамяти, а образы других страниц останутся в

памяти невредимыми и пригодными для последующего отображения.

Байт кода символа используется видеоадаптером как индекс для

входа в специальную таблицу знакогенератора - она задает 2-ичные

коды строк рисунков символов ; число строк для рисунка символа

зависит от разрешающей способности (количества телевизионных

линий умещающихся на экране) и может быть 8, 14, 16, а число

пикселов в строке символьного изображения обычно 8 или 9.

При работе в ГРАФИЧЕСКОМ режиме обеспечивается возможность

управлять цветом любого пиксела на экране - экран в графическом

режиме представляет собой матрицу пикселов. Коды цветов пикселов

хранятся в видеобуфере, при этом в режимах 4..6 (CGA - режимы)

возможен непосредственный доступ к видеопамяти из программы

столь же простой, как и в текстовых режимах - записывая по

нужным адресам коды цветов пикселов. Для кодирования цветов в

4 - цветных режимах 4 и 5 используется 2 бита на пиксел и водном

байте можно записать цвета 4-х пикселов. В 2 - цветном   режиме

6 для кодирования цвета пиксела достаточно одного бита. В

режимах 4..6 коды четных и нечетных строк в видеобуфере

сгруппированы в 2 отдельных массива - по смещению 0 от начала

страницы располагаются коды пикселов четных строк, а со смещения

2000Н - нечетных.

При работе в графических EGA - режимах 0Dh..10h логика

использования видеопамяти существенно меняется. Для каждого

пиксела в видеобуфере отводится 4 бита и количество

одновременно отображаемых цветов равно 16. Одному и тому же

адресу видеопамяти соответствуют сразу 4 байта, расположенных на

так называемых битовых планах и кодирующих в совокупности 8

соседних пикселов телевизионной строки экрана. EGA - адаптер

имеет 4 битовых плана (I, R, G, B) и любому пикселу

соответствует по 1 биту на каждом плане. Код цвета в EGA - это

номер одного из 16-ти специальных внутренних 6-тиразрядных

регистров палитры, в которых и находится код отображаемого на

экране цвета. Т о EGA позволяет выбрать 16 цветов из 64

возможных. Возможные 64 цвета задаются форматом

      r g b R G B

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

нормальную соответствующего цвета. Результирующий цветовой

сигнал образуется сложением сигналов цвета, управляемых каждым

из 6 бит кодов цвета. Т о для каждого из основных цветов -

красного, зеленого, синего - возможны 3 градации яркости:

половинная, нормальная и полуторная.

VGA использует подобную схему преобразования кода цвета пиксела

в реальный цвет на экране, но значение в регистре палитры задает

не код цвета, а номер DAC - регистра (регистра цифро -

аналогового преобразователя), т к на вход монитора необходимо

послать аналоговый сигнал (напряжение), соответствующий яркости

основных цветов. ЦАП имеет 256 3- байтовых регистров (по байту

для красного, зеленого, синего), при этом 6 битов байта кодируют

интенсивность цвета (64 оттенка яркости) и число возможных

цветов получается 64х64х64=262144. Значения в DAC - регистры

можно записать с помощью подпрограмм BIOS или непосредственным

доступом к портам адаптера. При работе VGA в режимах EGA

используются только первые 64 регистра, задающие 64 возможных

цвета палитры EGA. Память VGA в режимах 0Dh..10h  тоже делится

на битовые планы и код цвета пиксела задает номер регистра

палитры, в качастве регистров палитры выступают 4 блока DAC -

регистров по 64 регистра в блоке. В текущий мометт времени может

быть активеым только 1 блок.

В режиме 13Н на экране могут отображаться сразу 256 различных

цветов и для кодирования цвета каждого пиксела нужен целый байт,

так как разрешение в этом режиме 320х200, то всего необходимо

64000 байт. В этом режиме коды всех пикселов располагаются в

видеопамяти подряд, начиная с адреса А000:0000Н, без деления на

четные и нечетные строки. Код цвета задает номер одного из 256

DAC - регистров. Изменением значений в DAC - регистрах можно

менять цвет пиксела на экране для заданного кода.

В текстовых режимах видеоадаптер отображает курсор, а BIOS имеет

средства отслеживания и установки текущей позиции курсора. В

графических режимах курсор не отображается, хотя вывод через

BIOS в текущую позицию невидимого курсора по прежнему возможен,

как и задание курсорной позиции.


Управление видеосистемой в Си
Управление видеосистемой в С

1. Функции консольного вывода.

Их прототипы размещены в файле <conio.h> , они используют

понятие активного окна. Установку параметров активного окна

выполняет ф-ция

#include<conio.h>

void window(int ltcol, int ltrow, int rbcol, int rbrow)

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

нижнего углов окна, строки и столбцы нумеруются от 1. Описание

активного окна (фрейм) хранится во внутренней структурной

переменной:

struct text_info{

unsigned char winleft,wintop,

              winright,winbottom,

              attribute,normattr,

              currmode,

              screenhight,     /*высота экрана*/

              screenwidth,

              curx,cury};

Информация об активном окне доступна через ф-цию

void gettextinfo(struct text_info * r)

Функции textcolor(), textbackground(), textattr() и другие

управляют цветом отображаемых символов окна и будут рассмотрены

при изучении вывода на экран. Пример:

#include <conio.h>

#include <alloc.h>

#include <string.h>

#include <bios.h>

int main(void)

{

unsigned number;

char string[129], *buf_screen;

window(1,1,25,10);

textbackground(BLUE); textcolor(YELLOOW);

if((buf_screen=malloc(25*10*2)==NULL)

{cputs("Недостьаточно памяти\n\r"); return -1;}

gettext(1,1,40,13,buf_screen);

clrscr();

cputs("Введите целое число");

cscanf("%u",&number);

....

puttext(1,1,40,13,buf_screen);

return 0;

}

Дальше мы будем рассматривать примеры подпрограмм, для которых

определим символьные константы в файле screen.h:

#define ENTIRE 0

#define UP     6

#define DOWN   7

#define LEFT   16

#define RIGHT  17

#define HORIZONTAL  0

#define VERTICAL    1

#define ON          1

#define OFF         0

#define BAD_PARAM   255

#define END_OF_SCREEN 254

#define OK    0

#define COPY  0x00

#define AND   0x08

#define OR    0x10

#define XOR   0x18

struct cursor_pos{

  unsigned row_col;

char reg_14,reg_15;

char cur_page;

};

2.Определение и настройка параметров видеоадаптера

Секция (или подпрограмма) определения типа видеоадаптера и

монитора присутствует в любой программе, использующей прямой

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

видеоадаптера - нетривиальная задача, требующая выполнения

следующих шагов:

- анализ текущего видеорежима по данным области BIOS - байту

40:49Н; если он равен 7, то активен Hercules, иначе

- для проверки наличия EGA или VGA вызывается подпрограмма BIOS,

работающая только с таким адаптером - лучше всего АН=1АН

прерывания 10Н BIOS; обращение к ней с AL=00 является запросом о

видеосистеме: если при возврате  AL == 1АН то в BL будет код

комюинации адаптер/монитор , в BH - код комбинации

"альтернативный адаптер/монитор". Возможные значения регистра

ВХ:

00Н - отсутствие адаптера;

01Н - моно-адаптер и моно-монитор;

02Н - СGA с цветным монитором

04Н - EGA с цветным монитором

05Н - EGA c монохромным монитором

06Н - профессиональная цветная графическая система

07Н - VGA с аналоговым монохромным монитором

08Н - VGA с аналоговым цветным монитором

-1  - неизвестная комбинация

-если ни один из адаптеров не обнаружен, считается CGA.

Пример программы,печатающей сообщение об обнаруженном типе

адаптера:

char *messages[]=

{

"отсутствие адаптера",

"моно-адаптер и моно-монитор",

"СGA с цветным монитором",

"EGA с цветным монитором",

"EGA c монохромным монитором",

"профессиональная цветная графическая система",

"VGA с аналоговым монохромным монитором",

"VGA с аналоговым цветным монитором",

"",

"",

"неизвестная комбинация"

}

char * alt_system="не обнаружен";

#include <stdio.h>

#include <dos.h>

void main(void)

{

 union REGS r; char mode; int index;

mode=*(char *) MK_FP(0x400x49);

if(mode ==7) printf("Моно адаптер геркулес\n");

else {

printf("Активный адаптер \n");

r.x.ax=0x1a00;  int86(0x10,&r,&r);

if(r.h.al==0x1a)

{switch(r.h.bl){

case 0:index=0; break;

case 1:index=1; break;

case 2:index=2; break;

case 4:index=3; break;

case 5:index=4; break;

case 6:index=5; break;

case 7:index=6; break;

case 8:index=7; break;

case 0xb:index=8; break;

case 0xc:index=9; break;

default: index=10; }

printf("%s\n",messages[index]);

printf("Альтернативный адаптер:");

if(r.h.bh!=0) {alt_system=messages[index];

prinf("%s\n"alt_system);}

}

else

{

 r.h.ah=0x12;r.h.bl=0x10; int86(0x10,&r,&r);

if(r.h.bl==0x10) printf("CGA-адаптер\n");

else

{printf("EGA-адаптер c");

if(r.h.bh) printf("монохромным монитором\n");

else printf("цветным монитором\n");

printf("Оъем памяти - %d Кбайт \n"64*(r.h.bl+1));

}

}

}

}

3.Определение и установка режима видеоадаптера.

Текущий режим, число символов в строке текста и номер текущей

видеостраницы возвращает ф-ция AH=0FH прерывания 10Н; при

возврате из прерывания в AL - номер режима, в АН -число столбцов

текста, в ВН - номер активной видеостраницы.

Эту же информацию можно получить и непосредственно из области

BIOS.

Установку необходимого режима выполняет ф-ция АН=00Н прерывания

10Н, при этом в AL - желаемый режим. Переключение режима

адаптера обычно приводит к очистке видеобуфера, но для EGA, VGA

это можно предотвратить, если бит 7 в AL установить в1, т е

прибавить к номеру режима 80Н. Однако переход из текстового

режима в графический с сохранением видеобуфера может породить

непредсказуемые изображения.

Простейший вариант функции для установки заданного переменной

mode режима адаптера:

#include <dos.h>

void set_mode(int mode)

{

 _AL=(char)mode; _AH=0x00; geninterrupt(0x10);

}

4. Переключение видеостраниц.

Выполняет функция АН=05Н при номере страницы в AL. Это дает

возможность формирования изображения как бы "за кулисами"

осуществлением вывода в неактивную страницу, а затем делая ее

активной

Пример функции, устанавливающий заданную параметром page

видеостраницу:

#include<dos.h>

void set_page(int page)

{

 _AL=(char)page; _AH=0x05; geninterrupt(0x10);

}

5. Управление курсором.

5.1. Включение и выключение курсора и изменение формы..

Можно просто позиционировать курсор за пределы экрана с помощью

ф-ции 2 прерывания 10Н.

Ф-ция АН=01Н задает толщину курсора, при этом регистр СН

определяет номер верхней линии, CL - нижней. Биты 5 и 6 кода СН

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

Текущая форма хранится BIOS по адресу 0040:0060Н с номером

нижней строки в младшем байте и верхней в старшем.

Пример функции,выключающей курсор или восстанавливающей его

форму в зависимости от параметра mode:

#include<<dos.h>

#include<screen.h>

void cursor(int mode)

{static unsigned shape;

if(mode==ON) {if(shape) {_CX=shape; shape =0;}

else return;}

else

{

 shape=*(unsigned far *)MK_FP(0x40,0x60);

_CX=0x2000; /*Бит 6=0,бит 5=1*/

}

_AH=1; geninterrupt(0x10);

}

5.3. Установка курсора в абсолютную позицию.

Функция 2 прерывания 10Н устанавливает курсор для страницы с

номером ВН, строкой и столбцом в DH:DL.

Функция АН=03 читает текущую позицию курсора для видеостраницы с

номером в ВН, при выходе строка и столбец сообщаются в DH:DL, а

форма - в CH:CL.

Эффективным является прямой доступ к области данных BIOS, но

надо учитывать, что это не вызовет перемещения курсора на экране

до выполнения операций вывода средствами DOS или BIOS - дело в

том, что значения из области BIOS дублируются во внутренних

регистрах адаптера и обновляются только прерыванием 10Н.

Регистры 14 и 15 контроллера ЭЛТ хранят номер тексела текущей

позиции курсора; при управлении курсором через порты программа

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

Для доступа к выбранному CRT - регистру надо сначала записать

его номер в порт 3D4H (3B4H для монохрома), а затем через порт

3D5H либо прочесть, либо записать байт данных. Например для

чтения позиции курсора:

...

char reg_14,reg_15;

...

outportb(0x3d4,(char)14);

reg_14=inportb(0x3d5);

outportb(0x3d4,(char)15);

reg_15=inportb(0x3d5);

По адресу 40:63Н хранится базовый адрес CRTC, с его

использованием можно построить универсальную ф-цию управления

курсором. Пример:

#include<dos.h>

#include<screen.h>

goto_xy(int stroka, int stolb, int page)

{

unsigned old_ds;

register char max_col,max_str,ret;

register unsigned word,cur_pos;

old_ds=_DS;_DS=0x40;

max_col=*(char near*)0x4a;

max_str=*(char near*)0x84;

if(stroka<=max_str && stolb<=max_col)

{

 cur_pos=(stroka << 8) | stolb

*(unsigned near*)(0x50 + (page <<1))=cur_pos;

if(page==*(char near*)0x62)

{

 _DX=*(char near *)0x63;

outportb(_DX++,14);

outportb(_DX--,(char)(word>>8));

outportb(_DX++,15);

outportb(_DX,word);

}

ret=OK;

}

else ret=BAD_PARAM;

_DS=old_ds;

return ret;

}

Управление курсором через регистры адаптера - неповседневного

применения, но крайне желательно при сохранении и восстановлении

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

при запуске потомка.

С использованием доступа к портам и области BIOS можно построить

надежные функции сохранения и восстановления позиции курсора:

#include<dos.h>

#include<screen.h>

void save_cur(struct cursor_pos *work)

{register base_adr,offs;

work->cur_page=*(char far*)MK_FP(0x40,0x62);

offs=0x50+(work->cur_page<<1);

work->row_col=*(unsigned far*)MK_FP(0x40,offs);

base_adr=(unsigned far*)MK_FP(0x40,0x63);

outportb(base_adr++,15);

work->reg_15=inport(base_adr);

}

void rest_cur(struct cursor_pos *work)

{register base_adr,offs;

offs=0x50+(work->cur_page<<1);

*(unsigned far*)MK_FP(0x40,offs)=work->row_col;

base_adr=(unsigned far*)MK_FP(0x40,0x63);

outportb(base_adr++,14);

outportb(base_adr--,work->reg_14);

outportb(base_adr++,15);

outportb(base_adr,work->reg_15);

}

Текущая позиция курсора может быть установлена ф-ей консольного

ввода - вывода:

#include<conio.h>

void gotoxy(int x,int y)

а текущие координаты курсора могут быть получены с помощью:

int wherex(void)

int wherey(void)

Кроме того позиция курсора в окне возвращается в полях curx,

cury структурной переменной text_info после вызова

gettextinfo().

6. Работа с текстовой информацией.

Прерывание 10Н имеет в своем составе несколько ф-ций экранного

вывода:

АН=09Н - вывод символа с заданным атрибутом в текущую позицию

курсора; работает как в текстовых, так и в графических режимах,

символ помещают в AL, атрибут (в графическом режиме - цвет ) в

BL, номер страницы в ВН, число повторений в СХ. В графическом

режиме еесли бит 7 BL арвен 1, то при выводе выполняется XOR c

текущим содержимым видеопамяти, что гарантирует видимость

символа на любом фоне и упрощает операцию стирания символа

повторным выводом с 1 в 7-м бите.

АН=0АН -вывод символа без атрибута в текущей позиции курсора

АН=0ЕН -вывод символа в телетайпном режиме с интерпретацией

управляющих символов возврата каретки, перевода строки,

звукового сигнала 07Н и забоя 08Н.

АН=13Н - вывод строки символов с различными опциями: со сдвигом

курсора или без, с определением атрибута для всей строки или для

каждого символа; поддерживает такие же управляющие символы что и

АН=0ЕН.

Для примера использования ф-ции АН=09Н приведем

вертикальный вывод произвольной строки str на активную страницу

с атрибутом attr, корректно работающую и в текстовых и в

графических режимах:

#include<dos.h>

#include<screen.h>

int vert_prn(int stroka, int stolb, char *str, char attr)

{register char cur_page; union REGS r;

cur_page=*(char far*)MK_FP(0x40,0x62):

if(goto_xy(stroka,stolb,cur_page)==BAD_PARAM) return BAD_PARAM;

while(*str)

{

 r.h.bh=cur_page; r.h.bl=attr; r.x.cx=1;

 r.h.ah=0x09; r.h.al=*str++;int86(0x10,&r,&r);

 stroka++;

if(goto_xy(stroka,stolb,cur_page)==BAD_PARAM) return

END_OF_SCREEN

 }

return OK;

}

Для ф-ции hor_prn() горизонтального вывода вместо наращивания

номера строки stroka++ следует наращивать номер столбца stolb++.

Полезной для вывода рамок, заполнения областей экрана может

оказаться следующая ф-ция printche() выводящая с позиции str,

stolb символ symb с атрибутом atr заданное число раз n:

#include<dos.h>

#include<screen.h>

int printche(int str, int stolb, char symb, char atr, int n)

{register cur_ page;register max_stolb;union REGS r;

cur_page=*(char far*)MK_FP(0x40,0x62):

max_stolb=*(unsigned far*)MK_FP(0x40,0x4a);

if(goto_xy(str,stolb,cur_page)==BAD_PARAM) return BAD_PARAM;

if(max_stolb<(stolb+n-1)) return BAD_PARAM;

 r.h.bh=cur_page; r.h.bl=atr; r.x.cx=n;

 r.h.ah=0x09; r.h.al=*symb;int86(0x10,&r,&r);

 return OK;

}

Задача вывода рамки из символов массива style с использованием

ф-ции printche() решается в нижеприведенной ф-ции border():

char

style[8]={'\xC9','\xCD','\xBB','\xBA','\xBA','\xC8','\xCD','\xBC'}

#include<string.h>

#include<dos.h>

#include<screen.h>

int border(int s0,int c0,int s1,int c1, char *title, char

atr_title, char style[],char atr)

{register max_stolb,max_string,s;

max_stolb=*(unsigned far*)MK_FP(0x40,0x4a);

max_string=*(unsigned far*)MK_FP(0x40,0x84);

 if(s0>=0 && s0<s1 && c0>=0 &&c0<c1 && s1<=max_string

&& c1<max_stolb  && ((title!=NULL && (c1-c0 >= strlen(title)) ||

title==NULL))

{printche(s0,c0,style[0],atr,1);

printche(s0,c0+1,style[1],atr,c1-c0-1);

printche(s0,c1,style[2],atr,1);

for(s=s0+1;s<s1;s++)

{printche(s,c0,style[3],atr,1);

printche(s,c1,style[4],atr,1);

printche(s1,c0,style[5],atr,1);

printche(s1,c0+1,style[6],atr,c1-c0-1);

printche(s1,c1,style[7],atr,1);

if(title!=NULL)

hor_prn(s0,c0+(c1-c0-strlen(title))/2+1,title,atr_title);

return OK;

 }

else return BAD_PARAM;

 }

 }

6.2.Скроллинг. Очистка части или всего экрана.

Состоит в записи пробела в каждую из позиций экрана. ОС

предоставляет для этого несколько способов:

-просто сброс режима дисплея функцией 0 прерывания 10Н - для

текстового режима все позиции заполнятся кодом пробела 32 , а

атрибуты будут нормальными с кодом 7. Метод годится только в

начале программы.

-использование ф-ций 6 и 7 прерывания 10Н для сдвига экрана на

число строк в AL=0; этим способом можно очистить отдельное окно,

если поместить координаты верхнего левого угла в СХ, а правого

нижнего в DX и атрибуты очистки в ВН:

- использование ф-ции 9 прерывания 10Н для вывода символа и

атрибутов количество раз, записанное в СХ - значение 2000

очистит весь экран, если курсор был в0,0, в АН занесен символ

пробела, в AL байт атрибутов, в ВН - номер страницы:

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

пробела и байты атрибутов в память адаптера

Пример для монохрома:

Приведем пример Си-функции для скроллинга с помощью ф-ции 6

прерывания 10Н:

#include<dos.h>

#include<screen.h>

void scroll(int direction, char l_row, char l_col, char r_row,

char r_col, char attr)

{union REGS r;

if(direction) {r.h.al=1;r.h.ah=direction;}

else {r.h.al=0;r.h.ah=6}

r.h.ch=l_row; r.h.cl=l_col;r.h.dh=r_row;

r.h.dl=r_col;r.h.bh=attr;int86(0x10h,&r,&r);

}

Одним из "дизайнерных" применений скроллинга является построение

"взрывающихся" окон - начиная с определенного места, например с

центра экрана, очищается самое маленькое окно, по его периметру

строится рамка, затем размеры окна увеличиваются и все

повторяется до заданных нормальных размеров.Пример:

#define HOR_STEP 2

#define VERT_STEP 1

#define TIME 5

#include<dos.h>

#include<screen.h>

int expl_win(char s0,char c0, char s1, char c1, char color,

char style[], char atr)

{register char max_stolb, max_string;

register int cur_s0,cur_c0,cur_s1,cur_c1;

max_stolb=*(char far*)MK_FP(0x40,0x4a);

max_string=*(char far*)MK_FP(0x40,0x84);

if(s0>=0 && s0 <s1 && c0>=0 && c0<c1 && s1<=max_string && c1 <

max_stolb)

{

 cur_s0=(s1+s0)>>1; cur_c0=(c0+c1)>>1;

cur_s1=cur_s0; cur_c1=cur_c0;

while(cur_c0>c0 || cur_s0>s0 || cur_c1<c1 || cur_s1<s1)

{scroll(ENTIRE,cur_s0,cur_c0,cur_s1,cur_c1,color);

if(style!=NULL)

border(cur_s0,cur_c0,cur_s1,cur_c1,NULL,atr,style,atr);

cur_c0-=HOR_STEP;cur_c1+=HOR_STEP;

cur_s0-=VET_STEP;cur_s1+=VERT_STEP;

cur_c0=(cur_c0<c0)?c0:cur_c0;

cur_c1=(cur_c1>c1)?c1:cur_c1;

cur_s0=(cur_s0<s0)?s0:cur_s0;

cur_s1=(cur_s1>s1)?s1:cur_s1;

delay(TIME);

}

scroll(ENTIRE,cur_s0,cur_c0,cur_s1,cur_c1,color);

if(style!=NULL)

border(cur_s0,cur_c0,cur_s1,cur_c1,NULL,atr,style,atr);

return OK;

}

else return BAD_PARAM;

}

ВЫВОД ТЕКСТА НЕПОСРЕДСТВЕННО В ВИДЕОБУФЕР.

Если адаптер работает в одном из текстоывх режимов или в

графических CGA- режимах или 13-м EGA-VGA , то возможен

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

быстрый способ вывода, но он не сопровождается автоматическим

смещением курсора. Для доступа к видеобуферу используется

far-указатель :

char far *vid_buf=(char far*)0xb80000000;

...

vid_buf+=5*160+40*2;   /*5-я строка 40-й столбец*/

*vid_buf++='A';

vid_buf=atr;

Для повышения скорости можно осуществлять запись не побайтно, а

сразу словом:

unsigned word;

...

word=(unsigned)((symbol<<8)|attribut);

...

Еще большую скорость можно получить, записав перед доступом к

видеопамяти ее адрес в регистр ES и используя специальный тип

near - указателя unsigned _es * work.

И самая высокая скорость переноса достигается при использовании

библиотечных функций с прототипами в <mem.h>.

При записи в видеобуфер для вычисления смещения необходимо знать

максимальное число столбцов текста в текущем режиме, номер

текущей видеостраницы и сегмент адреса начала видеопамяти - эти

переменные разумно объявить как внешние и инициализировать при

первой записи в видеопамять и при каждой смене режима адаптера.

Пример:

Внешние переменные:

vid_memory - начальный параграф видеопамяти;

start_adr  - смещение до начала активной страницы;

max_stolb  - максим число столбцов;

max_str    - максим число строк на экране (25,43,80)

#include "screen.h"

int printvb(int str,int stolb, char symb, char atr, int n)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

register unsigned _es*work,word;

if(str>=0 && str<max_str && stolb >=0 && stolb<max_stolb)

{

 work=(unsigned _es*)(start_adr+(max_stolb*str +stolb)*2);

word = (unsigned)(attr<<8) | symb;

_ES=vid_memory;

while(n--) {*work=word; work++;}

return OK;

}

else return BAD_PARAM;

}

Вывод в окна средствами Турбо С.

Прототипы функций - в файле <conio.h>. В отличие от ф=ций

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

пересекают границ активного окна - при достижении правой границы

курсор переходит на следующую строку, а при достижении нижней

границы осуществляется скроллинг вверх.

void clreol(void) - стирает строку от позиции курсора;

void clrscr(void) - очистка текстового окна;

void delline(void) - стирает строку в которой курсор

void insline(void) - вставка пустой строки со сдвигом остальных

                     вниз и потерей самой нижней

int cprintf(const char * format,...) - форматированный вывод в

                окно, возвращает число выведенных байтов

int cputs(const char * str) - вывод строки в окно с текущей

позиции курсора, возвращает код последнего выведенного символа

int movetext(int left,int top, int right, int bottom, int

destleft, int desttop) - перенос окна в другое место

int putch(int ch) -вывод символа

int puttext(int left,int top, int right, int bottom,void*source)

- вывод окна с содержимым из буфера source, который обычно

заполняется ф-цией gettext().

void highvideo(void), void lowvideo(void), void normvideo(void)

- задание яркости вывода

void textattr(int newattr) - устанавливает атрибут; параметр

может задаваться либо числом, либо формироваться из

символических констант: BLINK | (BLACK<<4) |LIGHTRED

void textcolor(int newcolor) - установка цвета символов

void textbackground(int newcolor) - установка цвета фона

символов

Пример горизонтального скроллинга текстового окна с помощью

ф-ции movetext():

#include <conio.h>

#include <screen.h>

int h_scroll(int direction,int l_row, int l_col, int r_row, int

r_col, char *blank_str, char attr)

{

int ret,pos;

swith(direction){

 case LEFT:

ret= movetext(l_col+1,l_row,r_col,r_row,l_col,l_row);

if(!ret) return ret;

pos=r_col;break;

 case RIGHT:

ret= movetext(l_col,l_row,r_col-1,r_row,l_col+1,l_row);

if(!ret) return ret;

pos=l_col;break;

}

vert_prn(--l_row,--pos,blank_str,attr);

}

ЧТЕНИЕ ИНФОРМАЦИИ С ЭКРАНА.

Потребностьв этом возникает при организации оконного

инттерфейса, при построении резидентных HELP- систем, выдающих

подсказку по слову в позиции курсора.

Ф-ция АН=08Н прерывания 10Н возвращает в регистре AL ASCII - код

символа в позиции курсора и атрибут в АН:

...

_AH =0x08; geninterrupt(0x10); ch=_AX;

Но это чрезвычайно медленно и оправдано только при работающей и

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

экране - при этом читают символ в позиции курсора и пишут его

туда же но с другим атрибутом.

Для получения высокой производительности надо читать прямо из

видеобуфера; классическая задача - сохранение окна экрана, для

выполнения которой в Турбо С есть ф-ция gettext():

int gettext(int left,int top, int right, int bottom,void*

destin)

Пример Си-функции, читающей слово в текущей позиции курсора:

#include <dos.h>

#include <string.h>

#iтclude "screen.h"

char * read_scr(char *sep,int *str,int *stolb)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

 static char buf[81];

 register char index,cur_string,cur_stolb;

 register word;

char _es * ptr;

char *token;

 struct cursor_pos cur_cursor;

save_cur(&cur_cursor);

word=(unsigned)(cur_cursor.reg_14<<8) | cur_cursor.reg_15;

cur_string=word/max_stolb; cur_stolb=word%max_stolb;

*str=cur_string;*stolb=cur_stolb-1;

_ES=vid_memory;

ptr=(char _es*)(start_adr + ((max_stolb*cur_string)<<1));

for(index=0;index<max_stolb;index++)

{

 buf[index]=*ptr;

if(!buf[index]) buf[index]=' '; ptr+=2;

}

buf[max_stolb]='\0';

token=strtok(buf,sep);

do{

 word=FP_OFF(token-buf);

if(word<=cur_stolb && (word + strlen(token))>=cur_stolb)

{*stolb=word;return token;}

}

while((token=strtok(NULL,sep))!=NULL);

return NULL;

}

Пример функции перекрашивания строки либо вертикально либо

горизонтально:

#include <dos.h>

#include "screen.h"

hv_light(int s0,int c0, int n, char atr, int mode)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

 register unsigned _es * work;

 if(s0>=0 && c0>=0 && s0<=max_str && c0<=max_stolb &&

  ((mode==HORIZONTAL && (c0+n-1)<=max_stolb | (mode==VERTUCAL

  && (s0+n-1)<=max_str)))

{

 work=(unsigned _es*) (start_adr +((max_stolb*s0+c0)<<1));

_ES=vid_memory;

while(n--)

{

 _AX=*work; _AH=atr;*work=_AX;

if(mode==HORIZONTAL) work++;

else if(mode==VERTICAL) work+=max_stolb;

else return BAD_PARAM;

}

return OK;

}

else return BAD_PARAM;

}

ОСОБЕННОСТИ ВЫВОДА ТЕКСТОВ В ГРАФИЧЕСКИХ РЕЖИМАХ.

Для вывода пиксельных представлений символов в графичесуких

режимах можно использовать ф-цию АН=9Н прерывания 10Н.

При выводе этой ф-цией во всех графических режимах меньше 13

учитывается значение бита 7 кода цвета в регистре BL. Если он 0,

то символы имеют цвет, заданный битами 0..6; если он 1, цвет

пикселов определяется искдючающим ИЛИ битов 0..6 с кодом

текущего цвета на экране без изменения кода цвета фона 0. Это

гарантирует видимость символов с отличными от 00 кодами цвета и

для стирания символа с экрана достаточно вывести символ в той же

позиции с тем же цветом.

В частности для вывода и восстановления текстового окна в

графическом режиме средствами Турбо С поступают так:

-содержимое выводимого окна заранее размещают в отдельном

буфере, отводя на каждое знакоместо 2 байта - для символа и

атрибута; все атрибуты должны иметь взведенный старший бит, а 2

первых слова должны задавать число столбцов и строк окна;

-переменную directvideo обнуляют;

-окно "открывают" функцией puttext();

-восстановление части экрана, измененной выведенным окном,

выполняют повторным выводом на том же месте функцией puttext().

Скроллинг поддерживается BIOS-ом в графических CGA-режимах, но

код атрибута заливки освобождающихся линий задает цвет пикселов.

Если, например, в графическом режиме 5 (1 байт кодирует 4

пиксела) задать для скроллинга ВН=ffh, AL=00 (скроллинг всего

окна) то все окно заполнится пикселами с кодом цвета 11. При

BH=AAh экран зальется пикселами с кодом цвета 10, ВН=55Н -

пикселами с кодом цвета 01, а ВН=00 - пикселами с кодом цвета 00

(цвет фона). Любое другое значение приведет к появлению полос на

экране.

УПРАВЛЕНИЕ ЗНАКОГЕНЕРАТОРОМ EGA и VGA.

В этих адаптерах аппаратно реализован стандартизованный способ

установки таблицы знакогенераторов, предусмотрена возможность

изменять битовые карты для любых символов.

Управление знакогенераторами EGA-VGA выполняет функция 11Н

прерывания 10Н, имеющая ряд подфункций, выбираемых значением

регистра AL.

В текстовых режимах эти адаптеры позволяют использовать

несколько полных таблиц знакогенератора - блоков. На каждые 64

кбайт видеопамяти имеется по 1 блоку. В текстовом режиме можно

сделать активными сразу 2 блока с выбранными номерами, в

результате на экране можно будет отображать до 512 различных

очертаний символов. Выбор таблицы при этом будет выполнять бит 3

байта атрибута символа. В графических режимах можно использовать

только 1 таблицу для 256 сиволов.

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

либо в ОЗУ либо в ПЗУ на плате адаптнра, а в текстовых режимах -

на плане 2 видеопамяти. Каждый раз при переключении в текстовый

режим в память должна загружаться таблица знакогенератора.

Очертание символа задается битовой картой 8х8, 8х14 или 8х16 в

зависимости от режима работы адаптера. Битовые карты ложатся

последовательно друг за другом начиная с карты для символа с

ASCII - кодом 0 и до 255.

Таблицы знакогенераторов могут быть переопределены полностью или

частично, но все изменения в таблице будут потеряны при любом

переключении из режима в режим. Поэтому после каждого

переключения режима необходимо вновь загружать определенную

пользователем таблицу или активизировать 2 таблицы. По такому

принципу работают программы русификации экрана - после запуска

они загружают пользовательскую таблицу, подменяют вектор 10Н и

завершаются резидентно. При каждом обращении к прерыванию 10Н

управление получает резидентный русификатор, он вызывает старфый

обработчик прерывания 10Н и если вызывалась ф-ция переключения

режима - выполняет повторную загрузку пользовательской таблицы

знакогенератора.

При выводе текстов в графических режимах 0Dh - 13h вектор 43Н

указывает адрес начала используемой таблицы. Поэтому, подменив

этот вектор на адрес собственой таблицы, можно выводить

"заказные" символы.

ВЫВОД ГРАФИЧЕСКОЙ ИНФОРМАЦИИ.

Непосредственный доступ к аппаратуре видеоадаптера обеспечивает

максимальную скорость.Наиболее просто это решается для режимов

4,5,6,13Н - видеопамять для 4,5,6 начинается с В800:0000Н, для

13Н - с А000:0000 и организована  линейно. В 4,5 режимах каждый

байт кодирует 4 пиксела, в 6-ом - 8 пикселов, в 13Н - один

пикскел. Видеобуфер в 4-6 режимах поделен на 2 части - для

пикселов четных строк с адреса В800:000Н и нечетных с

В800:2000Н, в 13Н деления на чет-нечет нет.

Пример Си-функции вывода пиксела цвета color в точку х,у в

режимах 4 или 5; бит 7 кода цвета определяет выполнемую с кодом

цвета операцию:

void plot45(int x, int y, color)

{

register char _es* offset; /* Смещене от начала видеобуфера*/

register char mask1,       /*2 бита в байте, которые будут

изменены, получаем сдвигом байта С0Н вправо на удвоенный номер

пиксела в байте, который в свою очередь равен остатку от

деления номера столбца на 4*/

mask2,  /*Это байт, который будет комбинироваться с текущим

содержимым видеобуфера work в зависимости от значения 7-го

бита кода цвета - формируется из 2-х сдвинутых влево младших

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

пиксела в байте*/

work,

mod4x; /* Для удвоенного остатка от деления номера столбца на

4*/

 _ES=0xb800;

mod4x = (x & 0x03)<<1;

offset =(char _es) (0x2000 *(y & 0x01)+80*(y>>2));

mask2 = (color & 0x03)<<(6-mod4x);

work=*offset;

if(color & 0x80)  /*Если бит 7 равен 1 выполним XOR*/

*offset =work^mask2;

else             /*Иначе переопределм текущий цвет*/

{

 mask1=0xC0>>mod4x; work=work &~mask1; *offset=work|mask2;

}

}

Модификация этой функции для 6-го режима:

void plot6(int x, int y, color)

{

register char _es* offset;

register char mask1,mask2,work,mod8x;

mod8x = x & 0x07;

offset =0x2000 *(y & 0x01)+80*(y>>1)+(x>>3));

mask2 = (color & 0x01)<<(7-mod4x);

_ES=0xb800;

work=*offset;

if(color & 0x80)  /*Если бит 7 равен 1 выполним XOR*/

*offset =work^mask2;

else             /*Иначе переопределм текущий цвет*/

{

 mask1=0x80>>mod8x; work=work &~mask1; *offset=work|mask2;

}

}

И наиболее простой вид этой ф-ции для режима 13Н:

void plot13(int x, int y, color)

{

register char _es* offset;

offset=320*y+x;

_ES=0xA000;

*offset=color;

}

ЧТЕНИЕ И ВЫВОД ПИКСЕЛОВ ЧЕРЕЗ ПОРТЫ EGA,VGA -АДАПТЕРОВ мы

изучать не будем - это работа не для новичков, а желающие могут

освоить эти приемы например по книге А.И.Касаткина "Управление

ресурсами".

БИБЛИОТЕКА ГРАФИКИ ТУРБО С.

Использование графической библиотеки Турбо С - многошаговый

процесс, включающий:

-определение типа видеоадаптера;

-установка режима работы видеоадаптера;

-инициализация графической системы в заданном режиме;

Только после этого можно воспользоваться библиотечными

подпрограммами для построения основных графических примитивов и

вывода текста с использованием различных шрифтов.

В состав библиотеки входит набор так называемых BGI - драйверов,

каждый из которых является обработчиком прерывания 10Н и

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

которые могут изменяться как подпрограммами операционной

системы, так и подпрограммами библиотеки графики Турбо С.

Инициализация и закрытие системы графики Турбо С.

Состоит в загрузке BGI - драйвера, соответствующего адаптеру или

режиму, установке начальных значений внешних переменных, выборе

шрифта и т д. Поддерживаемые библиотекой графические режимы

задаются символьными константами, описанными в файле

<graphics.h> в перечислимом типе gtaphics_modes.  Некоторые из

этих констант:

Константа      Разрешение    Палитра        Режим

CGAC0            320x200       0             4,5

CGAC1                          1

CGAC2                          2

CGAC3                          3

CGAHI            640X200                       6

EGALO            640X200     16цветов        0EH

EGAHI            640X350       "             10H

VGALO            640X200       "             0EH

VGAMED           640X350       "             10H

VGAHI            640X480       "             12H

Инициализацию графики выполняет ф-ция

void far initgraph(int far *graphdriver,int far* graphmode,char

far *pathtodriver)-инициализирует графическую систему, загружая

BGI - драйвер graphdriver, устанавливает режим graphmode,

используя путь доступа к драйверу, заданный строкой

pathtodriver. BGI - драйвер может тоже задаваться символьной

константой из набора (приведены некоторые):

Константа        Значение          Описание

DETECT             0         Автоматич опред типа драйвера

CGA                1

EGA                3

VGA                9

Если нашей программе больше не нужна графическая библиотека,

следует обратиться к ф-ции

void far closegraph(void) - освобождает память под драйверами

графики, шрифтами и пр., восстанавливает предыдущий режим

адаптера.

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

использования графической библиотеки:

#include <graphics.h>

int main(void)

{

int graph_driver,graph_mode,graph_error_code;

graph_driver=DETECT;

initgraph(&graph_driver,&graph_mode,"c:\\tc\\bgi");

graph_error_code=graphresult();

if(graph_error_code!=grOk) {/*Обработка ошибки*/ return 255; }

...

closegraph();

}

Наиболее защищенный способ использования ф-ции инициализации

требует предварительного уточнения типа адаптера - для этого

либо вызывается initgraph() с драйвером, определенным через

DETECT, либо явно вызывается ф-ция:

void far detectgraph(int far *graphdriver,int far * graphmode)

Обработка ошибок

После любого обращения к detectgraph() или initgraph()

желательно вызвать ф-цию

int far graphresult(void), возврщающую код ошибки, а его

текстовую расшифровку можно получить с поиощью ф-ции

char *far grapherrormsg(int errorcode)

Определение и установка графического режима.

После инициализации графической системы можно установить другой,

не превышающий максимального, режим видеоадаптера и выбрать

цвета для пикселов. Установку режима выполняет ф-ция

setgraphmode(). Целая группа ф-ций - getgraphmode(),

getmaxmode(), getmodename(), getmoderange() - упрощает работу по

определению текущего установленного видеорежима; две ф-ции -

getmaxx(), getmaxy() - позволяют определить высоту и ширину

экрана в пикселах для текущего режима; ф-ция restorecrtmode()

возвращает адаптер в текстовый режим. Синтаксис и параметры этих

ф-ций всегда можно уточнить через help - систему Турбо С.

Управление цветами и палитрами.

CGA - адаптеры в режимах 4,5 320х200 аппаратно поддерживают

задание цвета фона и выбор одной из 4-х предопределенных палитр.

Код цвета пиксела 0 соответствует цвету фона и может быть выбран

равным любому из 16 возможных цветов:

Константа        Значение               Цвет

BLACK              0                    ЧЕРНЫЙ

BLUE               1                    СИНИЙ

GREEN              2                    ЗЕЛЕНЫЙ

CYAN               3                    СИНЕ-ЗЕЛЕНЫЙ

RED                4                    КРАСНЫЙ

MAGENTA            5                    КРАСНО-СИНИЙ

BROWN              6                    КОРИЧНЕВЫЙ

LIGHTGRAY          7                    СВЕТЛО СЕРЫЙ

DARKGRAY           8                    ТЕМНО-СЕРЫЙ

LIGHTBLUE          9                    ЯРКО-СИНИЙ

LIGHTGREEN        10                    ЯРКО-ЗЕЛЕНЫЙ

LIGHTCYAN         11                    ЯРКИЙ СИНЕЗЕЛЕНЫЙ

LIGHTRED          12                    ЯРКО-КРАСНЫЙ

LIGHTMAGENTA      13                    ЯРК. КРАСНО-СИНИЙ

YELLOW            14                    ЖЕЛТЫЙ

WHITE             15                    БЕЛЫЙ

Установку кода цвета фона выполняет ф-ция setbkcolor(), а ф-ция

getbkcolor() позволяет узнать код цвета фона в текущий момент.

Выбор же палитры осуществляется по символьной константе,

задающей режим работы.

У EGA,VGA - адаптеров возможности управления палитрами

значительно шире - библиотека графики Турбо С тоже содержит

группу ф-ций для определения текущей и установки новой палитры

EGA,VGA - адаптеров: getdefaultpalette(), gemaxcolor(),

getpalette(), getpalettesize(), setallpalette(), setpalette().

Пример демонстрационной программы, выводящей на экран номера

цветов , установленных по умолчанию, затем строки гасятся

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

вся палитра восстанавливается и строки становятся видимыми:

#include <graphics.h>

#include <stdio.h>

#include <conio.h>

int main(void)

{

int

graph_driver,graph_mode,graph_error_code,y=10,color,maxcolor,ht;

char string[81];

struct palettetype old_palette;

textattr((RED<<4) |YELLOW);

graph_driver =DETECT;

initgraph(&graph_driver,&graph_mode,"c:\\tc\\bgi");

graph_herror_code=graphresult();

if(graph_herror_code!=grOk)

{cprinf("Ошибка :%s",grapherrormsg(graph_error_code));

return 255;}

getpalette(&old_palette);

maxcolor=getmaxcolor();

ht=getmaxy()/20;

sprintf(string,"Цвет 0-фон. Строка цвета %d",maxcolor);

outtextxy(10,y,string); y+=ht;

for(color=1;color<=maxcolor;color++)

{

 setcolor(color); sprintf(string,"Цвет: %d",color);

 outtexxy(10,y,string); y+=ht;

}

if((graph_driver==EGA) | (graph_driver == VGA))

{

 outextxy(10,y,"При нажатии клавиши строки будут исчезать");

 getch();

/*Цикл переопределения регистра палитры*/

for(color=1;color<=maxcolor;color++)

{setpalette(color,BLACK);getch();}

setpalette(maxcolor,old_palette.colors[MAXCOLORS]);

y+=ht;

outtextxy(10,y,"Нажимайте клавиши для восстановления строк");

getch();

setpalette(&old_palette);

y+=ht;

}

outtextxy(10,y,"Для выхода нажмите что-нибудь");

getch();

closegraph();

return 0;

 }

Управление видеостраницей.

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

видеопамяти несколько страниц. Отображаемая в текущий момент

называется видимой, а та, на которую выполняется вывод -

активной. Первоначально активная и видимая страница имеют номер

0, но могут и не совпадать, в этом случае картинка формируется

"за кулисами". Для управления страницами в Турбо С есть 2

функции:

void far setactivepage(int page)

void far setvisualpage(int page)

Задание экранного окна (viewport), определение и установка

графических координат.

Графическое окно может иметь отличающиеся от других участков

экрана цвета фона и пикселов, маску заполнения и другие

характеристики. Для описания окна используется ф-ция

void far setviewport(int left,int top,int right,int bottom,int

clip)

Получить текущие х-ки окна можно через обращение к

void far getviewsettings(struct viewporttype far *viewport),

которая заполнит поля структурной переменной.

В графическом окне определены относительные координаты,

измеряемые в пикселах относительно левого верхнего угла окна.

Текущие координаты доступны через ф-ции getx(),gety(), а

установить нужные координаты можно ф-циями moveto() и moverel().

В Турбо С есть возможность встроить в .EXE - файл программы

необходимые BGI - драйверы. Для этого с помощью утилиты

BGIOBJ.EXE (при большом количестве драйверов - с ключем /F) файл

драйвера преобразуется в .OBJ - файл, который компонуется с

программой после регистрации с помощью ф-ций:

int registerbgidriver(void(*driver)(void))

int far registerfarbgidriver(void far *driver)

Пример: registerfarbgidriver(EGAVGA_driver)

ВЫВОД ТЕКСТА.

Во - первых вывод текста можно осуществить всеми ф-циями

стандартного вывода - при этом будет задействована ф-ция АН=9

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

обсуждались ранее.

Библиотека графики позволяет выводить текст на экран различными

шрифтами двух типов - битовым и сегментированным. Для битового

шрифта используется таблица знакогенератора 8х8.

Сегментированные шрифты задают правило рисования каждого

символа, описываемого как совокупность отрезков прямых линий или

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

В Турбо С доступны 4 сегментированных шрифта:

Triplex,Small,SansSerif,Gothic. Фафйлы сегментированных шрифтов

с расширением .CHR как и .BGI - драйверы загружаются как оверлеи

во время исполнения программы. Выводить в графике текст можно

слева направо и снизу вверх, масштаб задается по отношению к

знакоместу шрифта 8х8, а для сегментированных шрифтов - и в

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

Кроме того выводимые строки могут по разному выравниваться -

влево и вверх, влево и вниз и т п относительно текушей точки

отсчета. Поведение графической системы при выводе текста

характеризуется набором внутренних переменных, значения которых

доступны через ф-цию :

void gettextsettings(struct textsettingstype far *texttypeinfo),

заполняющую поля структурной переменной по шаблону textsettingstype

Интерпретация отдельных полей этой структуры приводится в

описании ф-ции %

void settextstyle(int font, int direction, int charsize)

Доступные шрифты задаются символьными константами:

Константа       Значение          Файл шрифта

DEFAULT_FONT      0                 -

TRIPLEX_FONT      1               TRIP.CHR

SMALL_FONT        2               SMAL.CHR

SANS_SERIF_FONT   3               SANS.CHR

GOTHIC_FONT       4               GOTH.CHR

Направление может задаваться HORIZ_DIR, VERT_DIR

Ф-ция:

void far setusercharsize(int multx, int divx, int multy,int

divy) позволяет установить масштабы по горизонтали и вертикали

раздельно значениями числителя и знаменателя.

Установленные размеры символов можно узнать через ф-ции

textheight(),textwidth().

Способ выравнивания задается ф-цией

settextjustify(int horiz,int vert), horiz может быть LEFT_TEXT,

CENTER_TEXT, RIGHT_TEXT, vert может быть BOTTOM_TEXT,

CENTER_TEXT, TOP_TEXT.

Собственно вывод осуществляется ф-циями:

void far outtext(char far *textstring)

void far outtextxy(int x, int y, char far *textstring)

Включение .CHR-файлов в выполняемый файл выполняют аналогично

драйверам после регистрации ф-циями

registerbgifont(),registerfarbgifont().

Имена шрифтов при регистрации

triplex_font, triplex_font_far

small_font  , small_font_far

sansserif_font, sansserif_font_far

gothic_font   ,gothic_font_far

ВЫВОД ГРАФИЧЕСКОЙ ИНФОРМАЦИИ.

Параметры и атрибуты графического вывода.

После успешной инициализации графической системы становятся

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

графических примитивов: отрезков прямых, дуг, окружностей,

эллтпсов, прямоугольников, секторных и столбцовых диаграмм и т

д. Многие из замкнутых фигур могут быть залиты текущим цветом с

использованием текущего шаблона или маски заполнения. Есть также

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

восстановления их в заданном месте. Все ф-ции работают в

пределах текущего графического окна с использованием текущего

цвета пиксела, установленного ф-цией setcolor().

При выводе отрезков прямых можно определить такой параметр как

стиль линий:

void far setlinestyle(int linestyle, unsigned upattern, int

thickness)

Аргумент upattern используется только если

linestyle==USERBIT_LINE - тогда 16 бит upattern задают маску

линии. Линия с определенным пользователем стилем может иметь

толщину только в 1 пиксел.

Поддерживается  ряд предопределенных стилей и можно описать

собственный. Задается стиль целым числом или символьной

константой:

Константа       Значение         Стиль

SOLID_LINE         0           Сплошная

DOTTED_LINE        1           Точечная ....

CENTER_LINE        2           Штрих-пунктир _._.

DASHED_LINE        3           Штриховая _ _ _

USERBIT_LINE       4           Опред пользователем

Толщина может быть 1 или 3 пиксела.

При выводе отрезков прямых можно дополнительно задать режим

вывода линий:

void far setwritemode(int mode), при этом mode может быть либо

COPY_PUT (0) либо XOR_PUT (1).

Следующие параметры - это маска и стиль заполнения. Маска

определяется 8-мибайтовым шаблоном и рассматривается как битовая

карта 8х8, вся заполняемая область разбивается на блоки по 8х8

пикселов. Наложение маски осуществляется так: если бит в маске

1, то пиксел имеет код текущего цвета, иначе не меняется. Маска

и цвет пикселов задается ф-цией:

void far setfillpattern(char far * upattern, int color)

По умолчанию - белый цвет и маска с единицами во всех битах. Для

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

комбинации с цветами, называемую стилем заполнения и

устанавливаемую ф-цией:

void far setfillstyle(int pattern, int color)

Константа        Значение    Описание

EMPTY_FILL        0          Цветом фона

SOLID_FILL        1          Текущим цветом

LINE_FILL         2          Символами --

LTSLASH_FILL      3          // нормальной толщины

SLASH_FILL        4          //удвгенной толщины

                  5          \\

                  6          \\

                  7          вертик-гориз штриховка

                  8          редкая штриховка кркест-накрест

                  9          густая штриховка крест-накрест

                 10          редкими точками

                 11          частыми точками

                 12          определенная пользователем маска

Для коррекции неквадратности пиксела на некоторых мониторах

можно воспользоваться ф-цией установки коэффициента сжатия:

void far setaspectratio(int far xasp, int far yasp)

Чтение - запись отдельных пикселов.

Для манипуляции пикселами есть 2 ф-ции:

unsigned far getpixel(int x,int y)

voi far putpixel(int x, int y, int pixelcolor )

Вывод отрезков прямых.

line(int x1,int y1,int x2,int y2)-линия между 2-мя точками

linerel(int dx, int dy);-линия из текущей позиции

lineto(int x, int y);-линия из текущей позиции в заданную

точку


Управление видеосистемой в Турбо Паскале

МОДУЛЬ  CRT.

Аббревиатура CRT может быть рачкрыта по-русски как "электронно

- лучевая трубка" - в этом модуле реализованы специальные

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

управлять текстовыми режимами, организовывать текстовые окна,

настраивать цвета символов, управлять курсором.

Этот модуль есть смысл подключать всегда директивой USES CRT,

так как его блок инициализации переключает вывод непосредственно

через видеопамять, минуя DOS и BIOS. Обычно же вывод совершается

по схеме: Процедура WRITE -->функция MS DOS вывода строки -->

подпрограмма BIOS --> видеопамять адаптера. При подключении CRT

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

переназначение стандартных файлов на фиктивное устройство CRT и

весь вывод начинает выполняться с помощью подпрограмм этого

модуля. Если же нужно по каким либо причинам отказаться от услуг

подключенного модуля CRT, то в программе должны появиться

операторы:

Assign(Output,''); Rewrite(Output);

Пустая строка в операторе Assign означает стандартное устройство

CON.

ВЫВОД СПЕЦСИМВОЛОВ.

При подключенном CRT можно выводить на дисплей управляющие

символы с кодами 0..31 без выполнения ими управляющих функций

(кроме символов с кодами 7,8,10,13). Примеры работы с

управляющими символами:

1.Вывод изображений управляющих символов.

Uses CRT;

CONST SpecialChars: Set of Char =[#7,#8,#10,#13];

      Ch:Char = #0;

VAR i: Byte;

begin

ClrScr;

while Ch < #32 do

  begin

    for i:=1 to 2 do begin

      Write('    Код',Ord(Ch):3,'-->');

      if (Ch in SpecialChars) then Write('Имеет действие')

      else Write(Ch:15);

      Ch:=Succ(Ch);

                     end;

  Writeln

  end;

  Write('Нажмите ввод ддля окончания...');

  Readln

  end.

2.Использование 4-х управляющих кодов экрана.

USES CRT;

VAR i:Byte;

begin

ClrScr;

Writeln('Нажимайте Enter для продолжения');

Readln;

Writeln('Эффект от кода 7 - короткий звук'#7);

Writeln;Writeln;Readln;

Writeln('Демонстрация кода 8');

for i:=1 to 40 do Write('/');

for i:=1 to 40 do begin

Delay(100); Write(#8,'\',#8);

end;

Writeln;Writeln;Readln;

Writeln('Демонстрация кода 10');

Writeln;

Write('Эта'#10'строка'#10'разорвана'#10'кодами 10'#10);

Writeln(#10#10);Readln;

Writeln('Демонстрация кода 13');

for i:=1 to 40 do Write('/');

Write(#13);

for i:=1 to 40 do begin

Delay(100); Write('\');

                  end;

Writeln;Readln

END.

Подпограммы CRT:

Window(X1,Y1,X2,Y2:Byte) {Задание текущего окна на экране}

ClrScr                   {Очистка окна}

TextMode(M:Word)         {Установка текстового режима}

GotoXY(X,Y:Byte)         {Установка курсора}

WhereX:Byte,WhereY:Byte  {Номер столбца и строки курсора}

ClrEOL                   {Стирание строки от тек позиции}

InsLine                  {Вставка пустой строки вместо текущей}

DelLine                  {Удаление строки}

TextColor(C:Byte)        {Цвет символов}

TextBackGround(C:Byte)   {Цвет фона}

HighVideo                {Яркость включить}

LowVideo                 {Яркость отключить}

NormVideo                {Восстановить цветовой режим}

Sound(Hz:Word)           {Звук частотой Hz герц}

NoSound                  {Выключить звук}

Delay(ms:Word)           {Пауза в мс}

KeyPressed:Boolean       {Нажата ли клавиша}

ReadKey:Char             {Символ нажатой клавиши}

AssignCRT(VAR f:Text)    {Связать текстовый файл с CRT}

В качестве простого примера приведем процедуру закраски

квадратной области экрана с диагональю X0,Y0 - X,Y символами Ch

с задержкой при закраске ms:

USES CRT;

Proceedure Spiral(x0,y0,x,y:Byte;ms:Word;Ch:Char);

VAR height,width,j:Byte; c:Integer;

begin

c:=1; width:=x-x0+1; height:=y-y0+1;

repeat

for j:=1 to width do begin

 gotoxy(x,y); Write(Ch);

 if(y>Hi(WindMax)) and (x>Lo(WindMax)) then begin

 gotoxy(1,1); InsLine                       end;

Delay(ms); x:=x-c    end;

x:=x+c; dec(height);

for j:=1 to height do begin

y:=y-c; gotoxy(x,y); Write(ch); Delay(ms)

                      end;

Dec(width); x:=x+c; c:=-1*c

until (height<1) or (width<1);

gotoxy(1,1);

end;

VAR i:Byte;  {Пример использования процедуры}

begin

ClrScr;

Spiral(1,1,80,25,2,#176);

for i:=1 to 10 do begin

TextAttr:=i;

Spiral(2*i,i,5*i,2*i,4,Chr(47+i));

                  end;

Readln;

end.

Пример применения подпрограмм InsLine/DelLine:

USES CRT;

VAR i,n:Byte;

begin

ClrScr;

gotoxy(1,2*7);

for i:=1 to 7 do Writeln('Ind/Del Line');

gotoxy(1,1);

repeat

for i:=1 to 7 do DelLine;

for i:=1 to 7 do InsLine

until KeyPressed;

end.

Пример процедур синтеза звуков:

USSES CRT;

Procedure Phone;

VAR i:Word;

begin

repeat

  for i:=1 to 100 do begin Sound(1200);Delay(10); Nosound end;

  Delay(800)

until KeyPressed

end;

Procedure Bell;

begin

repeat

Sound(1800);Delay(2);

Sound(2000);Delay(2);

Sound(2200);Delay(2);

Sound(2400);Delay(2);

until KeyPressed;

Nosound

end;

Procedure Sirena;

VAR i:Word;

begin

repeat

 for i:=400 t0 800 do begin Sound(i);Delay(3) end;

 Nosound

until KeyPressed;

end;

Procedure Pause;

VAR ch:Char;

begin while KeyPressed do ch:=ReadKey;

Delay(200)

end;

BEGIN

ClrScr;

Write('Нажмите клавишу'#10#10#13);

Write('Звук телефона'#13); Phone; Pause;

Write('Звук зуммера'#13); Bell; Pause;

Write('Звук сирены'#13); Sirena; Pause;

ClrScr;

end.

Нестандартные методы работы с текстовыми изображениями.

В огромном числе задач использование стандартных средств вывода

на экран малоэффективно или просто громоздко - заполнение окон

каким - либо символом, сохранение изображений в файлах и пр.

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

Видеопамять как мы уже знаем располагается в адресах от

$A000:$0000 до $BFFF:$0000. Монохромные режимы используют память

начиная с адреса $B000:0000, а цветные - с B800:0000.Реальный

размер используемой видеопамяти зависит от режима работы; его

минимальное значение при 40 столбцах и 25 строках равно

40х25х2=2000 байт, а максимальное при 80 столбцах и 50 строках

равно 8000 байт.  Хорошая программа должна сама определять, с

каким режимом она имеет дело. Определение текущего режима

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

хранятся сведения о конфигурации. Рассматриваемая ниже ф-ция

опрашивает такой специальный адрес, по которому MS DOS хранит

данные о текущей конфигурации системы; ф-ция возвращает адрес

начала видеопамяти текстового режима:

FUNCTION GetScreenPtr:Pointer;

begin

if (Mem[0:$0410] and $30) = $30

then GetScreenPtr:=Ptr($B000,0)

else GetScreenPtr:=Ptr($B800,0)

end;

Номер текущего текстового режима получить легко - он всегда есть

в младшем байте переменной модуля CRT LastMode, при этом 0-й бит

старшего байта содержит признак 43 или 50 строк. С

использованием этой переменной построены 2 ф-ции опроса

видеорежимов:

FUNCTION CurrentMode:Byte;

begin CurrentMode:=Lo(LastMode) end;

FUNCTION Font8x8YES:Boolean;

begin Font8x8YES:=(LastMode and Font8x8) = Font8x8 end;

Определение длины видеопамяти:

FUNCTION GetScreenSize :Word;

VAR

R: Byte absolute $0000:$0484;

C: Byte absolute $0000:$044A;

begin

ifHi(LastMode)=1 then GetScreenSize:=Succ(R)*C*2

                 else GetScreenSize:=25*C*2;

end;

Определение числа столбцов:

FUNCTION GetColNum:Byte;

begin GetColNum:=Mem[0:$44A] end;

Определение числа строк:

FUNCTION GetRowNum:Byte;

begin GetRowNum:=GetScreenSize div GetColNum div 2 end;

Пример использования приведенных ф-ций:

USES CRT;

{$I get_ptr.inc}

{$I get_par.inc}

VAR P:Pointer;

begin

P:=GetScreenPtr;

if Seg(P^)=$B000 then TextMode(Mono) else TextMode(CO40);

Writeln ('Старт видеопамяти',Seg(P^),':',Ofs(P^));

Writeln ('Длина видеопамяти',GetScreenSize,'Байт');

Writeln('Емкость экрана',GetColNum,'x',GetRowNum);

Writeln; Write('Нажмите Enter...');

ReadLn;

end.

Организация доступа к видеопамяти.

Все четные адреса видеобуфера, начиная с 0, содержат коды

символов, а нечетные - цветовые атрибуты. Доступ к видеобуферу

можно получить, наложив на него массив 2-байтовых элементов:

TYPE

VideoWord = RECORD

            Symbol:Char;

            Attrib:Byte;

            END;

VideoText=Array[1..50*80] of VideoWord;

VideoTextPtr=^VideoText;

Непосредственный доступ к видеопамяти производится через

переменную указательного типа VideoTextPtr. Если известно

текущее число столбцов М, то преобразование координат X,Y в

номер элемента массива осуществляется легко: М*(Y-1)+X.

Пример процедуры, записывающей в видеопамять постоянную пару

значений типа VideoWord, быстро заполняя прямоугольную область:

USES CRT;

TYPE

VideoWord = RECORD

            Symbol:Char;

            Attrib:Byte;

            END;

VideoText=Array[1..50*80] of VideoWord;

VideoTextPtr=^VideoText;

Procedure FillArea(VT:VideoTextPtr;X1,Y1,X2,Y2:Byte;Ch:Char;

Attr:Byte);

VAR

i,j,k:Word;

BuffWord:VideoWord;

M:Byte absolute 0:$44A;

begin

with BuffWord do begin

Symbol:=Ch;

Attrib:=Attr

end;

for j:=Y1 to Y2 do begin

k:=M*(j-1);

  for i:=X1 to X2 do VT^[k+i]:=BuffWord;

end;

END;

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

экрана:

Procedure ChangeAttr(VT:VideoTextPtr;X1,Y1,X2,Y2,Attr:Byte);

VAR

VW:Videoword;

i,j,k,n:Word;

M:Byte absolute 0:$44A;

begin

VW.Attrib:=Attr;

for j:=Y1 to Y2 do begin

k:=M*(j-1);

  for i:=X1 to X2 do begin

   n:=k+i;

   VW.Symbol:=VT^[n].Symbol;

   VT^[n]:=Vw

  end

 end

END;

{$I get_ptr.inc}

VAR V:Pointer;

begin

V:=GetScreenPtr;

if Seg(V^)=$B000 then TextMode(Mono) else TextMode(CO80);

Write('Нажимайте клавишу ввода...'); Readln;

FillArea(V,2,2,80,25,#177,White);    ReadLn;

FillArea(V,5,5,40,20,#176,Blue + 16*LightGray);    ReadLn;

ChangeAttr(V,25,15,75,24,Green+16*Cyan); ReadLn;

ClrScr

END.

Запоминание и восстановление окон.

Описанную методику работы с видеопамятью можно использовать для

работы с экранными окнами - в этом случае надо считывать,

запоминать или заполнять не всю видеопамять, а набор

строчных фрагментов окна, длина которых равна ширине окна.

Начало 1-го фрагмента Start для окна, заданного координатами X1,

Y1, X2, Y2 можно вычислить так:

Start = M *(Y1-1) +X1, где М - число столбцов текущего

текстового режима.

Начало каждой следующей строки получается добавлением числа М к

началу предыдущего. Длина строки определяется просто:

Width=(X2-X1) +1

а число строк  Height = (Y2 -Y1) +1

Для запоминания окна потребуется память размером:

Size = Width*Height*2

Обычно при сохранении окон их строки записываются в память

последовательно и при восстановлении необходимо вновь вычислять

положение каждой из них.

МОДУЛЬ WIN.

В пакете Турбо Паскаль есть исходные тексты модуля Win,

базирующегося на модуле CRT и реализующего простейшие действия с

текстовыми окнами; большинство его процедур написаны на

ассемблере. В модуле вводятся 3 новых типа и 2 константы для

рисования рамок:

TYPE

TitleStr=string[63];

FrameChars=Array[1..8] of char;

WinState = RECORD

WinMin,WinMax:Word;

WhereX,WhereY:Byte;

TextAttr:Byte;

END;

CONST

SingleFrame:FrameChars='+-+¦¦+-+';{Символы одинарной рамки}

DoubleFrame:FrameChars=''; {Символы двойной рамки}

Задание окна производится процедурой CRT.Window.

Для вывода символов и строк в модуле Win есть:

WriteChar(x,y,count:Byte;Ch:Char;Attr:Byte)

WriteStr(x,y:Byte;s:String;Attr:Byte)

Оформление окон.

FillWin(Ch:Char;Attr:Byte) -  Заполнение текущего окна символом

Ch с атрибутом Attr;

FrameWin(Title:TitleStr; VAR Ftame:FrameChars;TAttr,FAttr:Byte)

- рисование рамки в окне - приводит к уменьшению размеров окна

на единицу со всех сторон.  Для снятия рамки модуль имеет

процедуру UnFrameWin, которая восстанавливает размеры окна, не

стирая саму рамку. Применение этой процедуры к окну, не имеющему

рамки, равносильно увеличению размеров окна.

Сохранение и восстановление параметров окон.

SaveWin(VAR W:WinState)

RestoreWin(VAR W:WinState)

Сохранение и восстановление содержимого окна.

При использовании накладывающихся окон необходимо запоминать

содержимое "нижних" окон и восстанавливать его после снятия с

экрана "верхних". Эти действия реализуются следующими

подпрограммами модуля Win:

WinSize:Word - возвращает размер памяти, необходимой для

запоминания содержимого текущего окна

ReadWin(VAR Buf) - записсывает в бестиповую переменную Buf

изображение текущего окна

WriteWin(VAR Buf) - выводит в текущее окно его содержимое,

сохраненное ранее в Buf

Само изображение не содержит указаний на размеры "своего" окна и

его координаты, параметры окна надо сначала восстановить

процедурой RestoreWin и только после этого вызывать WriteWin.

Пример набора подпрограмм для задания на экране системы

накладывающихся окон:

USES CRT,Win;

TYPE

WindowType=RECORD

Size:Word;           {}

Frame:0..2;          {}

BufSave:Pointer;     {}

BufWin :Pointer;     {}

StateSave:WinStata;  {}

StateWin:WinState;   {}

END;

PROCEDURE     {Открыть окно и сделать его текущим}

OpenWin(X1,Y1,X2,Y2:Byte;T:TitleStr;TAttr,FAttr,FrameSort:Byte;

VAR W:WindowType);

begin

with W do begin

SaveWin(StateSave);            {}

Window(X1,Y1,X2,Y2);

Frame:=FrameSort;              {}

Size:=WinSize;                 {}

GetMem(BufSave,Size);

ReadWin(BufSave^);

case FrameSort of

1:FrameWin(T,SingleFrame,TAttr,FAttr);

2:FrameWin(T,DoubleFrame,TAttr,FAttr);

end;

ClrScr;

SaveWin(StateWin);              {}

if Frame<>0 then UnFrameWin;    {}

GetMem(BufWin,Size);

ReadWin(BufWin^);

RestoreWin(StateWin);

end

END;

{Сделать окно активным}

PROCEDURE ActivateWin(VAR W:WindowType);

begin

with W do begin

RestoreWin(StateWin);

if Frame<>0 then UnFrameWin;    {}

WriteWin(BufWin^);

RestoreWin(StateWin)

end

END;

{Деактивировать окно W и стереть, если Erase равен True}

PROCEDURE FreezeWin(VAR W:WindowType; Erase:Boolean);

begin

with W do begin

SaveWin(StateWin);

if Frame<>0 then UnFrameWin;    {}

ReadWin(BufWin^);

Gotoxy(1,1);

if Erase then WriteWin(BufSave^);

RestoreWin(StateSave)

end

END;

{Уничтожить окно}

PROCEDURE CloseWin(VAR W:WindowType);

begin

with W do begin

RestoreWin(StateWin);

if Frame<>0 then UnFrameWin;    {}

Gotoxy(1,1);

WriteWin(BufSave^);

RestoreWin(StateSave)

FreeMem(BufSave,Size);

FreeMem(BufWin,Size)

end

END;

{ПРИМЕР ВЫЗОВОВ ПРОЦЕДУР}

VAR

WW:Array[1..4] of WindowType;

i,k:Byte;

begin

FillWin(#176,Cyan);

Gotoxy(1,1);Write('Жмите ввод...');

for k:=1 to 4 do begin

OpenWin(10*k,2*k,20*k,4*k,'Window',Black+k, Blue+k, k mod 3,

WW[k]);

TextAttr:=16*k+(White-k);;

  for i:=32 to 127 do begin Write(Chr(i));Delay(20) end;

FreezeWin(WW[k],False); Delay(200)

end;

ReadLn;

for k:=4 downto 1 do begin

ActivateWin(WW[k]);

for k:=128 to 255 do begin Write(Chr(i));Delay(20) end;

FreezeWin(WW[k],False); Delay(200)

end;

ReadLn;

for k:=4 downto 1 do begin CloseWin(WW[k]);Delay(200) end;

ReadLn

END.

ОБРАЗ ЭКРАНА НА ДИСКЕ.

Имеет смысл при многократном использовании различными

программами одного и того же сложно заполненного экрана.

Запись и чтение экрана с диска не вызывает проблем:

USES CRT;

{$I get_ptr.inc}

{$I get_par.inc}

{Сохранение}

PROCEDURE SaveScrDisk(FileName:String);

VAR f:File;Res:Word;

begin

Assign(f,FileName);

Rewrite(f,GetScreenSize);

BlockWrite(f,GetScreenPtr^,1,Res);

if Res<>1 then WriteLn(^G'Tra-ta-ta'^G);Close(f)

END;

{Считывание с диска на экран без проверки совпадения режимов

файлового и текущего}

PROCEDURE LoadScreenDisk(FileName:String);

VAR f:File;FSize,Res:Word;

begin

Assign(f,FileName);

Reset(f,40);

FSize:=FileSize(f);

BlockRead(f,GetScreenPtr^,FSize,Res);

if Res<>FSize then WriteLn(^G'Tra-ta-ta'^G);Close(f)

END;

{ОСНОВНАЯ ЧАСТЬ НАПРИМЕР ТАКАЯ}

VAR j:Byte;c:Char;

begin

ClrScr; Gotoxy(1,5);

for c:='A' to 'Z' do

  for j:=1 to 40 do begin TextAttr:=j; Write(c) end;

TextAttr:=White+16*Black;

SaveScrDisk(screen.scn); Delay(2000);

ClrScr; Delay(1000);

LoadscrDisk(screen.scn);

ReadLn

END;

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

для этого необходимо преобразовать файл в объектный:

binobj screen.scn screen.obj  extscreen

и изменить программу так:     добавить

{$L screen.obj }

PROCEDURE ExtScreen; external;

{ОСНОВНАЯ ЧАСТЬ НАПРИМЕР ТАКАЯ}

 begin

Move(@ExtScreen^,GetScreenPtr^,GetScreenSize);

ReadLn;

END.

УПРАВЛЕНИЕ ФОРМОЙ КУРСОРА.

Проще всего - вызовом ф-ции АН=1 прерывания 10Н с указанием

начальной и конечной строк курсора в регистрах процессора.

Формально номера строк принимаются от 0 до 31, пр задании

начальной строки с номером 32 курсор невидим. Примеры процедур

для управления формой курсора:

USES CRT,DOS;

{Установка формы курсора}

PROCEDURE SetCursorSize(c_Start,c_End:Byte);

VAR Regs:Registers;

begin

with Regs do begin

AH:=$01; CH:=c_Start; CL:=c_End;

             end;

Intr($10,Regs)

END;

{Курсор по умолчанию}

PROCEDURE SetNormalCursor;

VAR SE:Word;

begin

if(LastMode>=Font8x8) then SE:=$0507

else if(LastMode=Mono) then SE:=$0b0c else SE:=$0607;

SetCursorSize(Hi(SE),LO(SE));

END;

{Крупный блок-курсор}

PROCEDURE SetBlockCursor;

VAR c_End:Byte;

begin

if(LastMode>=Font8x8) or (LastMode<>Mono)

then c_End:=7 else c_end:=13;

SetCursorSize(0,c_End)

END;

{Отключение курсора}

SetCursorSize(32,0);

ГРАФИЧЕСКИЙ ВЫВОД

в Паскале с помощью драйверов BGI не отличается от

рассмотренного для Турбо С, конечно с коррекцией на паскалевский

синтаксис.


Открытие и закрытие файлов

Приводимая ниже Си-программа открывает и закрывает файл для

потокового ввода - вывода; имя открываемого файла и режим

открытия задаются в командной строке при запуске программы.

Испытайте эту программу на созданных вами файлах (при

необходимости выполните отладку) и составьте ее аналоги на

языках Паскаль и QBASIC  (в интерпретирующем бейсике вам

придется организовать ввод имени файла и режима открытия с

клавиатуры):

#include<stdio.h>

void main(int argc, char **argv)

{

 FILE * fptr;         /*указатель на файловую структуру*/

 if(argc < 3)           /*Достаточно ли аргументов ?*/

 {

  printf("\a Недостаточно аргументов.Запуск программы:\n"\

         "Имя_программы имя_файла режим_открытия\n ");

 exit(1);

 }

 if((fptr=fopen(argv[1],argv[2]))!=NULL)

 {

  printf("Открыт файл %s в режиме %s",argv[1],argv[2]);

  fclose(fptr);

 }

 else perror("\a Ошибка открытия файла");


Посимвольный ввод-вывод

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

файла - источника в файл - назначение, имена файлов заданы в

командной строке.Разберитесь в ней, откомментируйте, после

испытания программы составьте и испытайте ее аналоги в Паскале и

Бейсике:

#include<stdio.h>

#include<process.h>

#include<errno.h>

void main(int argc, char **argv)

{

 FILE * source, *dest;

 char ch;

 if(argc < 3)           /*Достаточно ли аргументов ?*/

 {

  printf("\a Недостаточно аргументов.Запуск программы:\n"\

         "Имя_программы имя_файла_источн имя_файла_назнач\n ");

 exit(1);

 }

 if((source=fopen(argv[1],"rb"))==NULL)

 {perror("\a Ошибка открытия файла-источника"); exit(2);}

 if((dest=fopen(argv[2],"r"))!=NULL)

{

puts("\a Файл-назначения существует. Переопределим?\n"\

     "Да- Д или Y, нет - любая другая клавиша");

if(!((ch=getchar())=='y' || ch == 'Y' || ch=='Д' || ch=='д'))

exit(4);

}

fclose(dest);

if((dest=fopen(argv[2],"wb"))==NULL)

{perror("\a Ошибка открытия файла-приемника"); exit(3);}

clearerr(source); clearerr(dest);

while(!feof(source) &&!ferror(source))

{

 ch=fgetc(source); fputc(ch,dest);

 if(ferror(dest))

 {

  perror("\a Ошибка записи");

  fclose(dest); unlinnk(argv[2]);

  exit(4);

 }

}

  if(ferror(source))

 {

  perror("\a Ошибка чтения");

  fclose(source); fclose(dest); unlink(argv[2]);

  exit(4);

 }

}

Наличие файла-приемника проверяется попыткой открыть его для

чтения; если файл не существует или переопределяется - он

переоткрывается для записи. Обратите внимание на двоичный режим

доступа к файлам - это дает возможность копировать не только

текстовые а любые файлы. При возникновении ошибок неполностью

сформированный файл назначения удаляется.


Построчный ввод-вывод

Приводимая Си-программа читает все строки из заданного файла и выводит их на стандартный вывод, после завершения выводит число прочитанных строк. Разберитесь в ней, откомментируйте, после испытания составьте аналог этой программы на Паскале и Бейсике.

#include<stdio.h>

void main(void)

{

 char string[256], file_name[128];

 int line_number=0;

 FILE *fptr;

puts("Введите спецификацию файла");

gets(file_name);

if((fptr=fopen(file_name,"r"))!=NULL)

{

 for(;fgets(string,255,fptr)!=NULL;line_number++)

 fputs(string,stdout);

 printf("\n Прочитано %d строк \n",line_number);

 fclose(fptr);

}

else perror("\a Ошибка открытия файла");

}


Блоковый ввод-вывод

Приводятся функции для чтения - записи структурной переменной по шаблону BOOKS; для прстоты считается, что к моменту вызова функций файл уже открыт. Составьте Паскалевский и Бейсик - аналоги этих подпрограмм.

#include<stdio.h>

#include<string.h>

#include<io.h>

#define NAME 32

#define TITLE 64

typedef struct {

                char name[NAME];

                char title[TITLE];

                int year;float price;

              }BOOKS;

int file_input_book(FILE *fp,BOOKS* ptr)

{return(fread(ptr,sizeof(BOOKS),1,fp));}

int file_output_book(FILE *fp,BOOKS* ptr)

{return(fwrite(ptr,sizeof(BOOKS),1,fp));}


Форматированный ввод-вывод

Приводимая Си-программа записывает в файл возведенные в квадрат

целые числа, затем читает их из файла и выводит на экран.

Откомментируйте, испытайте и продублируйте на Паскале и Бейсике.

#include<stdio.h>

int main(void0

{

 char buf[81],*ptr="Массив_чисел_типа_integer";

 FILE *fp;

 int i_start,i_end,index=0,i,numbers[256];

 while(2)

{

 printf("Введите начальное положительное число ");

 scanf("%d",&i_start);

 printf("Введите количество чисел 0..255: ");

 scanf("%d",&i_end);

 if(i_start>=0 && i_end>=0 && i_end<=256)  break;

 else printf("\a\n");

}

 printf("Введите спецификацию файла:");

 fflush(stdin); scanf("%s",buf);

 if((fp=fopen(buf,"wb")) == NULL)

 {perror("Ошибка открытия файла");return(253);}

 if(fprintf(fp,"%s\n",ptr))

 {perror("Ошибка записи");return(255);}

 i=i_start;

 for(index=0;i<=i_end;index++;i++)

  if(fprintf(fp,"%d\n",i*i)==0)

 {perror("Ошибка записи");return(254);}

i=i_end-i_start+1;

freopen(buf,"r",fp);

fscanf(fp,"%s",buf);

for(index=0;i--;index++)

{

 fscanf(fp,"%d",&numbers[index]);

printf("%3d - e число =%5u;\n",index,numbers[index]);

}

printf("\nПрочитано %d чисел\n",index);

fclose(fp);

return(0);

}


Произвольный доступ к файлам

Приводимая Си-программа формирует и пополняет  сохранением

прежнего содержимого файл записей, имя которого задано первым

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

Фактически записи образуют  односвязный список, просматривая

(сканируя) который с начала можно ускорить процесс доступа к

нужной строке. "Переведите" эту программу на Паскаль и Бейсик.

#define STOP_STRING ""

#define BUFFER_SIZE 128

#define KEY_SIZE 16

#include<stdio.h>

#include<string.h>

#include<errno.h>

struct file_record{    /*описание заголовка записи файла*/

       long offset;     /*указатель на следующую запись*/

       char indicator;  /*'B'-запись;'F'-свободное место*/

       unsigned key_length; /*длина ключевого поля в байтах*/

       unsigned length;     /*длина записи в байтах*/

                    }work;

main(int argc,char **argv)

{

FILE *fptr;

int in_lines;         /*счетчик введенных с клавиатуры строк*/

int file_lines;       /*число строк в файле*/

char key[KEY_SIZE];   /*буфер для ключа*/

char buffer[BUFFER_SIZE];  /*буфер для строки*/

fpos_t f_ptr;         /*файловый указатель*/

if(argc<2) {printf("Запуск: \n FSEEK_W файл_БД \n"); return(1);}

if((fptr=fopen(argv[1],"a+b"))==NULL)

{perror("\nОшибка открытия файла ");return(2);}

/*Определение числа записей в файле*/

fseek(fptr,0L,SEEK_END);

if(!ftell(fptr))

{ file_lines=0;

  if(!fwrite(&file_lines,2,1,fptr)) return(3);

}

else

{fseek(fptr,0L,SEEK_SET);

 if(!fread(&file_lines,2,1,fptr)) return(3);

 fseek(fptr,0L,SEEK_END);

}

printf("\n В файле %d строк. Вводите новые.\n"\

       "Конец ввода - пустая строка \n ",file_lines);

/*Цикл ввода с клавиатуры с выходом по пустой строке*/

fflush(stdin);

for(in_lines=0;strcmp(gets(buffer),STOP_STRING);)

{

 strcpy(key,"     ");

 printf("Введите ключ (15 символов):");

 fflush(stdin); fgets(key,15,stdin);

/*Формирование заголовка */

work.length=strlen(buffer);

work.key_length=strlen(key);

if(*(key+work.key_length-1)=='\n') work.key_length--;

work.offset=ftell(fptr)+work_length+work.key_length +

sizeof(struct file_record);

work.indicator='B';

/*Запись в файл заголовка, ключа и строки*/

if(!fwrite(&work,sizeof(struct file_record),1,fptr)) break;

if(!fwrite(key,work.key_length,1,fptr)) break;

if(!fwrite(buffer,work.length,1,fptr)) break;

in_lines++;

fflush(stdin);

 }

/*Корректировка чмсла записей в файле*/

if(in_lines)

{file_lines+=in_lines; f_ptr=ftell(fptr);

fclose(fptr); fopen(argv[1],"r+b");

fseek(fptr,0,SEEK_SET);

if(!fwrite(&file_lines,2,1,fptr)) in_lines=0;

fsetpos(ptr,&f_ptr);

}

printf("Прием строк закончен\n");

printf("Принято %d строк \n Всего в файле  %d строк\n",

in_lines,file_lines);

fclose(fptr);

return(0);

 }

6.2.Для демонстрации доступа к сформированному предыдущей

программой файлу приведем программу поиска записи по ее

порядковому номеру и по ключу. Найденная запись в нашем примере

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

первым аргументом командной строки, а режим поиска - вторым: /n

или /k. Ваша задача по-прежнему состоит в составлении аналога на

языках Паскаль и Бейсик.

#include<stdio.h>

#include<string.h>

#include<errno.h>

#define BUFFER_SIZE 128

#define KEY_SIZE 16

struct file_record{    /*описание заголовка записи файла*/

       long offset;     /*указатель на следующую запись*/

       char indicator;  /*'B'-запись;'F'-свободное место*/

       unsigned key_length; /*длина ключевого поля в байтах*/

       unsigned length;     /*длина записи в байтах*/

                    }work;

char key[KEY_SIZE];   /*буфер для приема ключа с клавиатуры*/

char f_key[KEY_SIZE];   /*буфер для приема  ключа из файла*/

char buffer[BUFFER_SIZE];  /*буфер для приема строки из файла*/

int scan_by_number(FILE*,int);

int scan_by_key(FILE*,char *);

int main(int argc, char** argv)

{

FILE *fptr;

int in_line;         /*счетчик введенных с клавиатуры строк*/

int file_line;       /*число строк в файле*/

fpos_t f_ptr;         /*файловый указатель*/

if(!(argc >2 &&

(argv[2][0] == '/' &&

(argv[2][1] == 'n' || argv[2][1] == 'k' ||

argv[2][1] == 'N' || argv[2][1] == 'K')))

{

 printf("Ошибка в аргументах"); return(1);

}

if((fptr=fopen(argv[1],"a+b"))==NULL)

{perror("\nОшибка открытия файла ");return(2);}

/*Проверим не пустой ли файл*/

fseek(fptr,0L,SEEK_END);fgetpos(fptr,&f_ptr);

if(!f_ptr) {printf("Файл пустой\n");return(3);}

fssek(fptr,0L,SEEK_SET);

if(!fread(&file_line,2,1,fptr)) return(4);

switch(argv[2][1])

{

 case 'n':case 'N':

 printf("Введите номер нужной записи:");

 fflush(stdin);

 scanf("%d",&in_line);

 if(in_line >=file_line) {printf("\nВ файле всего %d

 строк. Поиск невозможен.\n",file_line); return(5);}

 if(scan_by_number(fptr,in_line)==EOF)

{perror("Ошибка чтения"); return(4);}

break;

 case 'k':case 'K':

 fflush(stdin);

 printf("Введите ключ для поиска  записи:");

 fgets(key,15,stdin);

 if(key[strlen(key)-1]=='\n') key[strlen(key)-1] ='\0';

if(scan_by_key(fptr,key)==EOF) perror("Ошибка чтения");

else if(feof(fptr)) puts("\n Запись с таким кдючем не найдена");

return(6);

}

break;

}

fwrite(buffer,work.length,1,stdout);

fclose(fptr);

return(0);

}

/*Подпрограмма поиска записи по номеру*/

int scan_by_number(FILE * fptr, int in_line)

{int cur_line;

 fpos_t f_ptr;

 fgetpos(fptr,&f_ptr);

 for(cur_line=0;cur_line<=in_line;cur_line++)

 {fsetpos(fptr,&f_ptr);

  /*Чтение заголовка записи*/

 if(!fread(&work,sizeof(struct file_record),1,fptr))

 return(EOF);

 f_ptr=work.offset;

 }

fseek(fptr,work.key_length,SEEK_CUR);

if(!fread(buffer,work.length,1,fptr)) return EOF;

return cur_line;

}

/*Подпрограмма поиска записи по ключу*/

int scan_by_key(FILE* fptr, char *key)

{int in_key;    /*длина ключа вводимого с клавиатуры*/

 int min_key    /*меньшая из длин ключей из файла и stdin*/

 int cur_line=0;

 fpos_t f_ptr;

 in_key=strlen(key);

 if(*(key+in_key-1)=='\n') in_key--;

 fgetpos(fptr,&f_ptr);

while(!feof(fptr))

{

 fsetpos(fptr,&f_ptr);

 if(!fread(&work,sizeof(struct file_record),1,fptr)) return EOF;

 min_key=(work.key_length>in_key) ? in_key:work.key_length;

 if(!fread(f_key,work.key_length,1,fptr)) return EOF;

 f_key[work.key_length]='\0';

 strlwr(f_key);

 if(strstr(f_key,key)!=NULL)

 {

 if(!fread(buffer,work.length,1,fptr)) return EOF;

 return cur_line;

}

/*Перемещение указателя на заголовок следующей записи*/

f_ptr=work.offset;

cur_line++;

}

return EOF;

}


Префиксный доступ к файлам

Потоки являются языковой надстройкой к ДОС, поэтому естественно,

что Турбо С содержит библиотечные ф-ции префиксного доступа к

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

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

подпрограммам MS DOS.

В качестве первичной помощи приводятся 2 примера работы с

префиксным доступом: первая программа открывает файл для

префиксного доступа и записывает в него тестовую строку string

(спецификация файла задается в командной строке), вторая

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

Ознакомьтесь с префиксной работой, откомментируйте программы, а

затем перепишите программы лабораторных работ для потокового

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

7.1.

#include<stdio.h>

#include<fcntl.h>

#include<stat.h>

#include<io.h>

char string[]="Тестовая строка для записи в файл";

main(int argc,char **argv)

{int handle;

 if(argc<2)

{printf("\aЗапуск: %s ИМЯ_ФАЙЛА  \n",argv[0]); return(1);}

if((handle=open(argv[1],O_TRUNC | O_CREAT | O_WRONLY |

O_TEXT, S_IWRITE))==-1)

{

printf("\aОшибка открытия файла: %s \n",argv[1]);

perror("Причина ошибки:");

return(2);

}

write(handle,string,sizeof(string));

close(handle);

return 0;

}

7.2.

#include<stdio.h>

#include<dos.h>

#include<stat.h>

#include<io.h>

char string[512]="Тестовая строка для записи в файл";

main(int argc,char **argv)

{int handle;

 if(argc<2)

{printf("\aЗапуск: %s ИМЯ_ФАЙЛА  \n",argv[0]); return(1);}

if((handle=_creat(argv[1],FA_ARCH|FA_SYSTEM|FA_HIDDEN))==-1)

{

printf("\aОшибка открытия файла: %s \n",argv[1]);

perror("Причина ошибки:");

return(2);

}

write(handle,string,sizeof(string));

read(handle,string,512);

write(1,string,512);

close(handle);

return 0;

}


Программирование фильтров

Фильтр - это программа, взаимодействующая с потоком символов.

Источником и приемником потока символов могут быть файлы, другая

программа или любое символьно - ориентированное устройство.

Выполняемые над символами преобразования могут быть как очень

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

(генерация сплайнов по набору координат точек). Фильтрами

являются стандартные программы ДОС   сортировки строк текста по

алфавиту SORT, поиска строки в потоке текста FIND, постраничного

вывода на экран MORE. Работа программ фильтрации в MS DOS

опирается на стандартные устройства CON (дескрипторы

 stdin,stdout,stderr), AUX (stdaux), PRN (stdprn) и

 перенаправление ввода-вывода включением в командную строку

 символов

 < файл                стандартный ввод из файла

 > файл                стандартный вывод в файл

 >> файл   стандартный вывод в файл добавлением к содержимому

 p1 | p2   стнандартный вывод прграммы р1 как стандартный ввод

           программы р2 (конвейер)

Перенаправление по символьным запросам осуществляется на уровне

COMMAND.COM. Люьой процесс может добиться того же результата,

перенаправляя стандартный ввод - вывод с помощью функции 46Н

прерывания 21Н перед вызовом функции EXEC (ф-ция 4ВН прерывания

21Н).

 Пример:

 C> SORT < MYFILE.TXT > PRN   Enter

 По такой команде фильтр SORT читает файл MYFILE.TXT, сортирует

 его строки и выводит результирующий текст на PRN.

 Создание фильтра для MS DOS:  фильтр с помощью дескрипторных

 подпрограмм ввода-вывода (3Fh для чтения и 40h для записи )

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

 на стандартный вывод, выполняя попутно любые изменения символов

 или строк.

 ЗАДАНИЕ.

8.1.Приводится модельный вариант символьно -

 ориентированного фильтра на Си с ничего не делающей

 подпрограммой преобразования translate. Вам предлагается

 наполнить эту подпрограмму какими - либо действиями и составить

 ее действующие аналоги на Паскале и бейсике.

#include<stdio.h>

main(int argc, char* argv[])

{char ch;

 while((ch=getchar())!=EOF) {ch=translate(ch); putchar(ch);}

exit(0);

}

int translate(char ch) {return ch;}

8.2. Приводится программа фильтра очстки текста, которая убирает

старшие биты всех символов, преобразует символы табуляции в

нужное число пробелов, удаляет все управляющие коды, кроме кодов

возврта каретки, перевода строки и перевода страницы. Если имя

программного файла CLEAN, то для его использования необходимо

описать в командной строке файлы источника и приемника вместе с

символами перенаправления, иначе программа будет осуществлять

ввод с клавиатуры и вывод на экран:

C> CLEAN < MYFILE.DOC > MYFILE.TXT    Enter

#define TAB_WIDTH 8

#define TAB '\0x09'

#define LF  '\0x0A'

#define FF  '\0x0C'

#define CR  '\0x0D'

#define BLANC '\0x20'

#define EOFMK '\0x1A'

main(int argc, char *argv[])

{char c; int col=0;

 while((c=getchar())!=EOF)

{

 c &=0x07F;                 /*Убрать старший бит*/

 switch(c)

{

 case LF:case CR: col=0;    /*сброс счетчика столбцов*/

 case FF:         wchar(c); brak;

 case TAB: do wchar(BLANC);

           while((++col % TAB_WIDTH)!=0);

           break;

 default:

 if(c>=BLANC) {wchar(c); col++;}

 break;

}

}

wchar(EOFMK);

exit(0);

}

/*Подпрограмма вывода символа на стандартное устройство*/

wchar(char c)

{

 if((putchar(c)==EOF) && (c!=EOFMK))

{fputs("clean: Диск полон",stderr); exit(1);}


Пример работы с файловыми компонентами

Приводится Паскаль - программа, меняющая местами первый и

последний компоненты файла. Ваша задача - составить Си- и Бейсик

- аналоги этой программы.

Type Dim = Array[1..3] of Char;  {}

VAR

f   :File of Dim;

ff  :File;

Dfirst,Dlast: Dim;

FS:         : LongInt;

CONST

St:String[11*3]='AAA---BBB---CCC---DDD---EEE---FFF';

{Две процедуры для создания файла из 11 массивов типа DIM и его

загрузки после модификации прямым доступом. Содержимое массивов

представлено строкой длины 11*3=33}

Procedure Save_St;

begin

Assign(ff,'dimfile.dat'); Rewrite(ff,3);

BlockWrite(ff,St[1],11);  Close(ff);

end;

Procedure Load_St;

begin

Assign(ff,'dimfile.dat'); Reset(ff,3);

BlockRead(ff,St[1],11);  Close(ff);

end;

begin

Writeln('Начальное содержимое файла',St);

Save_St;

Assign(f,'dimfile.dat'); Reset(f);

FS:=FileSize(f);

if FS < 2 then begin WriteLn('Маловато записей'); halt end;

Read(f,Dfirst); Seek(FS-1);

Read(f,Dlast);  Seek(f,FilePos(f)-1);

Write(f,Dfirst); Seek(f,0);

Write(fDlast);

Close(f);

Load_St;

Writeln('Результирующее содержимое файла',St);

ReadLn;

end.


Работа с файловыми записями

Приводится QBASIC-программа, реализующая "адресную книгу"

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

- ввод адресов;

-поиск адреса;

-корректировка адреса;

В главной программе для выбора отдельных действий создается

меню. Из меню программа разветвляется на отдельные подпрограммы,

выполняющие специализированные задачи. Текст меню хранится в

массиве mentx$, что дает возможность выводить его в цикле

FOR...NEXT. После появления меню на экране клавиатура

опрашивается до тех пор, пока ответ не попадет в диапазон 1..6.

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

осуществляется с помощью оператора SELECT...CASE. Все пункты

меню обрабатываются в одном цикле DO...LOOP. Программа активна,

пока в меню не выбран режим "ВЫХОД".

В константах elem% (количество полей), maxl% (длина самого

длинного поля), содержатся значения для ф-ции edit$, с помощью

которой можно обработать поле данных на экране. Длины отдельных

полей находятся в массиве ele%.

При наборе текста программы помните, что набор текстов

подпрограмм в QBASIC сопровождается автоматическим

генерированием строк DECLARE в начале программы.

'Объявления подпрограмм

DECLARE SUB fenster(lo1%,lo2%,ru1%,ru2%,art$)

DECLARE SUB  neuanlage()

DECLARE SUB  suchen()

DECLARE SUB  aendern()

DECLARE SUB  loeschen()

DECLARE SUB  anzeigen()

DECLARE SUB  init()

'Объявление констант

CONST art1$=""   'Здесь д б символы для рисования окон меню

CONST art2$=""   'Вам их необходимо вписать самим

CONST art3$=""

'Объявление переменных

DIM SHARED elem%

DIM SHARED maxl%

elem%=7

maxl%=30

DIM i%, auswahl%

DIM mentx&(1 TO 6)

DIM SHARED ele%(1 TO elem%)

'Поля данных

DIM SHARED nname$, vname$, stras$, plz$, ort$, tele$, stich$

'Инициализируем поля данных - подпрограмму init составим потом

CALL init

'Сохраним длины отдельных полей в массиве

ele%(1)=LEN(nname$)

ele%(2)=LEN(vname$)

ele%(3)=LEN(stras$)

ele%(4)=LEN(plz$)

ele%(5)=LEN(ort$)

ele%(6)=LEN(tele$)

ele%(7)=LEN(stich$)

'Загрузка текстов меню

mentx$(1)="Ввод адресов"

mentx$(2)="Поиск адресов"

mentx$(3)="Изменение адреса"

mentx$(4)="Стереть адрес"

mentx$(5)="Вывести адрес"

mentx$(6)="Выход"

'Основная картинка экрана

CLS

CALL fenster(1,1,23,80,art1$)

'Цикл пока auswahl<>6

DO

'*******Вывод меню********

        CALL fenster(10,8,20,72,art2$)

        LOCATE 11,36

        PRINT "МЕНЮ"

        FOR I%=1 TO 6

        LOCATE 11+I%,11

        PRINT USING"##";I%

        PRINT" ";MENTX$(I%);

        NEXT I%

'Опрос клавиатуры

LOCATE 19,11

PRINT "СДЕЛАЙТЕ ВЫБОР:_"

DO

auswahl%=VAL(INKEY$)

LOOP UNTIL auswahl% > 0 AND auswahl% < 7

'Использование меню

SELECT CASE auswahl%

CASE 1 CALL neuanlage

CASE 2 CALL suchen

CASE 3 CALL aendern

CASE 4 CALL loeschen

CASE 5 CALL anzeigen

CASE ELSE END SELECT

LOOP UNTIL auswahl%=6

CLS 'Конец программы

'Теперь пришла очередь составления подпрограмм

'Процедура построения меню имеет вид:

SUB fenster(lo1%,lo2%,ru1%,ru2%,art$)

'********Рисование окна**********

DIM fuell$

'Левый ограничитель окна

fuell$=MID$(art$,4,1)

'Символы-наполнители

FOR i%=1 TO ru2%-lo2%-1

fuell$ = fuell$ + MID$(art$,5,1)

NEXT

'Правый ограничитель

fuell$ = fuell$ + MID$(art$,6,1)

'Позиция левого верхнего угла

LOCATE lo1%,lo2%

PRINT MID$(art$,1,1)

'Верхняя линия

FOR i%=lo2%+1 TO ru2%-1

PRINT MID$(art$,2,1)

NEXT

'Правый верхний угол

PRINT MID$(art$,3,1)

'Вертикали и символы-заполнители

FOR i%=lo2%+1 TO ru1%-дщ1:-1

LOCATE lo1%+i%,lo2%

PRINT fuell$;

NEXT

'Позиция левого нижнего угла

LOCATE ru1%,lo2%

PRINT MID$(art$,7,1);

'Нижняя линия

FOR i%=lo2%+1 TO ru2%-1

PRINT MID$(art$,8,1)

NEXT

'Правый нижний угол

PRINT MID$(art$,9,1)

END SUB

'Подпрограмма инициализации полей

SUB init

nname$=SPACE$(30)

vname$=SPACE$(30)

stras$=SPACE$(30)

plz$=SPACE$(6)

ort$=SPACE$(30)

tele$=SPACE$(16)

stich$=SPACE$(30)

END SUB

'Подпрограмма занесения адресов

SUB neuanlage

CALL init

CALL fenster(5,5,22,75,art3$)

LOCATE 6,8

PRINT "Ввод новых данных"

if edit$="OK" THEN

OPEN "adress.dat" FOR APPEND AS #1

WRITE #1,nname$, vname$, stras$, plz$, ort$, tele$, stich$

CLOSE #1

END IF

CALL fenster(5,5,22,75,art3$)  'Стереть окно

END SUB

'Функция редактирования

FUNCTION edit$

DIM i%,j%,x$,flag$

DIM arr$(1 TO elem%,1 TO maxl%)

FOR i%=1 TO ele%(1)

   arr$(1,i%)=MID$(nname$i%,1)

NEXT

FOR i%=1 TO ele%(2)

   arr$(2,i%)=MID$(vname$,i%,1)

NEXT

FOR i%=1 TO ele%(3)

   arr$(3,i%)=MID$(stras$,i%,1)

NEXT

FOR i%=1 TO ele%(4)

   arr$(4,i%)=MID$(piz$,i%,1)

NEXT

FOR i%=1 TO ele%(5)

   arr$(5,i%)=MID$(ort$,i%,1)

NEXT

FOR i%=1 TO ele%(6)

   arr$(6,i%)=MID$(tele$,i%,1)

NEXT

FOR i%=1 TO ele%(7)

   arr$(7,i%)=MID$(stich$,i%,1)

NEXT

'Вывод текста для шаблона

LOCATE 8,8

PRINT "Фамилия  :"

LOCATE 9,8

PRINT "Имя  :"

LOCATE 10,8

PRINT "Улица  :"

LOCATE 11,8

PRINT "Почт индекс  :"

LOCATE 12,8

PRINT "Местность  :"

LOCATE 13,8

PRINT "Телефон  :"

LOCATE 14,8

PRINT "Заглавие :"

'Вывод массивов

FOR i%=1 TO 7

  FOR j%=1 TO ele%(i)

        LOCATE 7+i%,20+j%

        PRINT arr$(i%,j%)

NEXT

NEXT

'Предложение клавиш

LOCATE 18,8

PRINT "Esc -> прервать"

LOCATE 18,30

PRINT "F10 -> продолжить"

i%=1

j%=1

'Непосредственно обработка полей

DO

  LOCATE 7+i%,20+j%

  COLOR 0,7

  PRINT arr$(i%,j%

  LOCATE 7+i%,20+j%

  COLOR 7,0

  DO

        x$=INKEY$

  LOOP WHILE x$=""

'Оценка INKEY$

IF LEN(x$)=2 OR ASC(x$)<32 THEN

PRINT arr$(i%,j%)

SELECT CASE x$

CASE CHR$(27)

flag$=" "

EXIT DO

CASE CHR$(0)+"D"

flag$="OK"

EXIT DO

CASE CHR$(0)+"M" 'стрелка вправо

j%=j%+1

IF j%>ele%(i%) THEN

j%=1

END IF

CASE CHR$(0)+"K" 'стрелка влево

j%=j%-1

IF j%<1 THEN

j%=ele%(i%)

END IF

CASE CHR$(0)+"H" 'стрелка вверх

i%=i%-1

IF i%<1 THEN

i%=7

END IF

IF j%>ele%(i%) THEN

j%=ele%(i%)

END IF

CASE CHR$(0)+"p" 'стрелка вниз

i%=i%+1

IF i%>7 THEN

i%=1

END IF

IF j%>ele%(i%) THEN

j%=ele%(i%)

END IF

CASE CHR$(13) 'ввод Enter

j%=1

i%=i%+1

IF i%>7 THEN

i%=1

END IF

CASE CHR$(8) 'ввод BackSpace

IF j%<0 THEN

j%=1

END IF

arr$(i%,j%)=" "

CASE ELSE

END SELECT

ELSE   'Выбрана не функциональная клавиша

arr$(i%,j%)=x$

PRINT x$;

j%=j%+1

IF j%>ele%(i%) THEN

j%=1

i%=i%+1

IF i%>7 THEN

i%=1

END IF

 IF

  END IF

LOOP

'По F10 надо снова загрузить массив в поле данных

IF flag$="OK" THEN

nname$=""

vname$=""

stras$=""

plz$==""

ort$==""

tele$=""

stich$=""

FOR i%=1 TO ele%(1)

nname$=nname$+arr$(1,i%)

NEXT

FOR i%=1 TO ele%(2)

vname$=vname$+arr$(2,i%)

NEXT

FOR i%=1 TO ele%(3)

stras$=stras$+arr$(3,i%)

NEXT

FOR i%=1 TO ele%(4)

plz$=plz$+arr$(4,i%)

NEXT

FOR i%=1 TO ele%(5)

ort$=ort$+arr$(5,i%)

NEXT

FOR i%=1 TO ele%(6)

tele$=tele$+arr$(6,i%)

NEXT

FOR i%=1 TO ele%(7)

stich$=stich$+arr$(7,i%)

NEXT

END IF

edit$=flag$

END FUNCTION


Работа с файлами в антивирусной программе

/*

    В 1988 году и к  нам пришла эпидемия компьютерных вирусов.  В

связи с этим весьма  актуально научиться с ними  бороться. Данная

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

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

По мере обнаружения вирусов новых типов ее довольно просто  можно

приспосабливать для борьбы и с ними.

    Для начала попробуем  дать определение этой  болезни. Назовем

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

свойствами:

    - способность  к размножению  не только  в одной  ЭВМ, но и к

заражению других;

    -  наличие  более  или  менее  значительного   инкубационного

периода, т.е.  интервала от  момента заражения  машины до  какого

либо открытого проявления.

    При  отсутствии  способности  к  размножению  можно  говорить

только о наличии "бомбы"  - скрытой в программе  пакости, которая

должна сработать через некоторое  время, для отмщения врагам,  не

оценившим таланты автора программы.

    Из необходимости  наличия способности  к размножению  следует

обязательная способность вируса заражать программные модули  типа

COM или  EXE. Это  связано с  тем, что  именно модули таких типов

обычно копируют с одной машины на другую. При отсутствии у вируса

инкубационного  периода   сравнительно  легко   можно  определить

источник   заражения   и,   следовательно,   появляется  реальная

опасность для автора вируса.

    Заметим,  что  для  автора  вируса  необходимо  наличие  двух

способностей:

    - уметь программировать на ассемблере и иметь основные

      понятия об операционной системе;

    - уметь получать удовольствие, делая людям пакости.

    К счастью сочетание обоих этих достоинств встречается в людях

достаточно редко.

    Принцип организации любого типа вируса состоит в том, что при

запуске зараженной  программы управление  получает именно  вирус.

Первым  делом  он  обычно  пытается  заразить другие программы, а

затем   обеспечить   правильную   работу   программы.   Заражение

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

и замене начала (первых трех байт) на команду передачи управления

на собственное начало. При  этом старое значение замененных  трех

байтов сохраняется для  обеспечения правильной работы  зараженной

программы в течении инкубационного периода.

    Для лечения зараженных модулей необходимо только знать, где в

вирусе спрятано старое значение  трех замененных байтов и  какова

длина вируса. Знание прочих подробностей о его организации отнюдь

не необходимо.

Программа написана для транслятора Microsoft-C версии не ниже 5.0.

    Общая схема организации  программы заключается в  организации

просмотра  всех  каталогов   (directory)  диска,  проверке   всех

имеющихся COM и EXE файлов  на наличие в них известных  вирусов и

лечение больных. Хотя программа умеет работать только с  модулями

типа  COM,  приходится  проверять  и  модули  с  расширением EXE,

поскольку тип  модуля определяется  не расширением,  а внутренним

форматом. Кроме  того для  всех модулей,  начинающихся к  команды

JMP, вычисляется разность между адресом перехода по этой  команде

и  длиной  файла  и   ведется  статистика  этих  разностей.   При

многократном повторении одинаковых  значений этой разности  можно

предполагать  наличие  вируса.  Впрочем,  не  так  сложно сделать

вирус, который не обнаруживаются таким способом.

*/

#include <dos.h>

#include <string.h>

#include <stdio.h>

#include <stdlib.h>

#include <io.h>

#include <fcntl.h>

/*Дипазон сбора статистики о разностях (пределы длин вирусов)*/

#define MIN_VIRUS 0x0100

#define MAX_VIRUS 0x2000

/*Критическое число повторений разности для тревоги*/

#define critical_no 3

char path[10][13],  /* имена отдельных уровней каталогов */

    full_path[64],  /* буфер для полного пути */

    file_name[64],  /* буфер для полного имени файла */

    disk[4];

/* структуры для функций поиска в каталоге */

struct find_t block, dbl[10];

#define dblock (dbl[level])

int fixup=0, and_exe=1, by_date=0;

int *len_index, second_pass=0;

int resident_virus=0; /* обнаружен вирус, остающийся в памяти */

void (far * restart)()=(void far *)0xf000fff0L;

struct exe_header {unsigned

    signature,

    len_reminder,

    size_pages,

    reloc_items,

    header_size,

    min_above,

    max_above,

    SS,SP,

    shecksum,

    IP,CS,

    first_reloc,

    overlay;};

typedef struct exe_header EXE_H;

main(int argc, char *argv[])

{

    char *wc, *extention, *extpos;

    int i,fff, dff, w,

        /* счетчики для отчета о проделанной работе */

        total=0, totvir=0, totcorr=0, totkill=0, totrest=0, totqq=0,

        level=0; /* текущий уровень каталога (0 - корневое) */

    wc=NULL;

    for (i=1; i<argc; i++) { /* обработка параметров программы */

        if ( *argv[i] == '/' )  /* ключевой параметр */

        switch( *(argv[i]+1) | 0x20 ) {

            default:

                goto parm_error;

            case 'f':

                fixup=1; break;

            case 'c':

                and_exe=0; break;

        }

        else

            if( wc==NULL ) wc=argv[i];

            else goto parm_error;

    }

    extention = ( and_exe ? "*.*" : "*.com" );

    strcpy(disk,wc);

    if ( (strlen(disk)!=2) || (disk[1]!=':') ) {

parm_error:

        fprintf(stderr,"\n\

Вызов: aidstest d: [/f] [/c]\n\

где:  d: - имя диска\n\

      /f - исправлять зараженные программы и стирать испорченные\n\

      /c - проверять только .COM\n");

        exit(1);

    }

    /* размещение и обнуление массива для сбора статистики о разностях */

    len_index=malloc((MAX_VIRUS - MIN_VIRUS) * 2);

    for(i=0; i < MAX_VIRUS - MIN_VIRUS; i++) len_index[i]=0;

    disk[2]='\\';

for_second:

    do { /* этот цикл исполняется для каждого найденного каталога */

        /* формируем полное имя текущего каталога */

        strcpy(full_path,disk);

        for( i=0; i<level; i++ ) {

            strcat(full_path,path[i]);

            strcat(full_path,"\\");

        }

        /* образуем полное имя файла для поиска в каталоге */

        strcpy(file_name,full_path);

        strcat(file_name,extention);

        /* ищем первого представителя интересующих нас файлов */

        /* причем не пропускаем и "спрятанные" (hidden) */

        fff=_dos_findfirst(file_name,_A_RDONLY|_A_HIDDEN,&block);

        while (fff==0) { /* 0 означает, что нечто найдено */

            /* ищем точку в имени найденного файла */

            extpos = strchr(block.name,'.');

            if( extpos!=NULL && /* если точка найдена и */

                (strcmpi(extpos,".COM")==0 || /* это COM-файл */

                /* или EXE, причем и они нас интересуют */

                (and_exe && strcmpi(extpos,".EXE")==0 ) ) ) {

                /* идем в функцию обработки модуля */

                i = test_virus(full_path,block);

                /* значение на выходе говорит, что произошло */

                if ( i ) {

                    totvir++;

                    if(fixup)

                    switch (i) {

                    case -1: totkill++; break; /* стерли */

                    case -2: totrest++; break; /* не удалось исправить */

                    case  1: totcorr++; break; /* исправили */

                    case  2: totqq++;   break; /* не удалось стереть */

                    default: ;

                    }

                }

                total++; /* всего проверено файлов */

            }

            fff=_dos_findnext(&block); /* ищем следующий файл */

        } /* конец просмотра файлов в одном каталоге */

        /* теперь начинается поиск следующего каталога */

        strcpy(file_name,full_path);

        strcat(file_name,"*.*");

        /* опять ищем с начала каталога, но берем и подчиненные каталоги*/

        dff=_dos_findfirst(file_name,_A_SUBDIR|_A_HIDDEN,&dblock);

        /* пропускаем не каталоги */

        while( ((dblock.attrib & _A_SUBDIR)==0)

            /* и особые строки "." и ".." */

            || (*(dblock.name)=='.')) {

            /* по концам текущих каталогов поднимаемся выше */

            while ( (dff=_dos_findnext(&dblock)) != 0 )

                if(0==level--) break; /* кончился и корневой */

            if ( dff ) break; /* ни одного каталога больше не нашли */

        }

        if ( dff ) break; /* ни одного каталога больше не нашли */

        /* запоминаем имя очередного уровня каталога */

        /* и в переменной level получаем текущую глубину */

        strcpy(path[level++],dblock.name);

    } while(level>0); /* повторение цикла для нового каталога */

    if(second_pass) { /* об этой переменной - ниже */

        printf("Вопросы по тел. Москва - 292.40.76 - Лозинский Д.Н.");

        goto end_of_program;

    }

    CurSavRest(1,1);

    printf("\n\

Всего проверено %d\n\

Заражено:       %d\n",total,totvir);

    if(fixup)

     printf("\n\

Исправлено:     %d\n\

Стерто:         %d\n\

Не удалось исправить -\n\

         всего: %d\n\

       опасных: %d\n",totcorr,totkill,totrest+totqq,totqq);

    /* просмотр массива со статистикой разностей */

    for(i=MIN_VIRUS; i<MAX_VIRUS; i++)

    if( (w=len_index[i-MIN_VIRUS]) > critical_no ) {

        printf("Длину хвоста =%X имеет %d модулей\n",i,w);

        second_pass=1; /* статистика неприятная */

    }

    /* пойдем на повторный просмотр диска для вывода */

    /* имен всех подозрительных файлов */

    level=0;

    if (second_pass) goto for_second;

end_of_program:

    if(resident_virus) {

        printf("\n\

    Обнаружен вирус, который делает себя резидентным в памяти\n\

              и заражает все загружаемые программы !\n\

Для надежной очистки машины будет произведена перезагрузка системы.\n\

 Если считаете это излишним, нажмите \"N\", но пеняйте потом на себя!\n");

        i=getch() | 0x20;

        if(i != 'n') {

            *(int far *)0x472L = 0x1234;

            restart();

        }

    }

}

/* Всевозможная работа с одним файлом - проверка, лечение,

   стирание, вывод подозрительных.

   Параметры: полное имя каталога

              структура, используемая в функциях поиска */

test_virus(char *path, struct find_t block)

{

    unsigned int flen, i,

        jmpadr, vir_adr, vir_len,

        access, drive, exit_code,

        basadr;

    int vir_no, LEN_BAS, BEF_LEN;

    unsigned char filename[64],

            oldstart[28],

            *VIR_BEG,

            *msg="",

            *type_code,

            *wc=NULL;

    long wl, lflen;

    /* Это команда ухода на перезагрузку системы, которую пишет в

       начало некоторых модулей один из вирусов. Такие модули

       приходится стирать. */

    char *trup="\xEA\xF0\xFF\x00\xF0";

    int file_time, file_date, exe_file=0;

    /* массивы данных, используемых при опознании вирусов */

    static unsigned len_jmp[]={0x28B, 0x6A7, 0x92 },

                    len_bas[]={0x8F , 0    , 0xFFFF },

                    before_len[]={0 , 1    , 0x710 };

    static char *vir_beg[]={"\x51\xBA",

                            "\xFA\x8B\xEC\xE8",

                            "\xFC\xB4\xE0\xCD\x21"};

#define no_of_vir 3

#define near_jmp  0xE9

    /* образуем полное имя файла */

    strcpy(filename,path);

    strcat(filename,block.name);

    outfname(filename); /* вывод имени на экран */

    flen = block.size; /* длина файла */

    lflen = block.size; /* длина файла */

    /* если файл только для чтения, пока так его и откроем,

       а иначе сразу и для записи, на случай необходимости исправления */

    access = ( block.attrib & _A_RDONLY ) ? O_RDONLY : O_RDWR;

    drive = open(filename, access | O_BINARY);

    _dos_getftime(drive,&file_time, &file_date);

    if (drive == -1) return 0; /* открыть не удалось - на выход */

    read(drive, oldstart, 28); /* читаем первые 28 байт (и для начала EXE) */

    if ( !memcmp(oldstart,"MZ",2) ) goto EXE; /* это признак EXE-программ*/

    if ( !memcmp(oldstart,trup,5) ) goto TRUP;/* модуль испорчен вирусом */

    /* если первая команда - не переход, считаем, что вируса нет */

    if ( oldstart[0] != near_jmp ) goto StartNotJmp;

    /* из команды перехода изымаем адрес */

    jmpadr = * (int*) (oldstart+1);

    /* при втором просмотре диска - на проверку по массиву статистики */

    if(second_pass) goto for_second;

    /* первый контроль на известные вирусы по разности длины файла и адреса

       перехода или по длине перехода, если вирус становится в начало */

    for ( vir_no=0; vir_no<no_of_vir; vir_no++ )

        if ( (flen - jmpadr == len_jmp[vir_no]) || /* цепляющийся в начало */

            (len_jmp[vir_no] == jmpadr) && (0xFFFF == len_bas[vir_no]) /* в конец */

        ) goto MayBe;

    goto CLOSE_RET0; /* не опознан */

MayBe: /* судя по длине, это вирус типа vir_no */

    LEN_BAS=len_bas[vir_no]; /* это для опознания одного из типов */

    BEF_LEN=before_len[vir_no]; /* кол-во байтов вируса до точки входа */

    VIR_BEG=vir_beg[vir_no]; /* начальные байты вируса для сравнения */

    /* начало вируса от начала файла (3 из-за способа адресации переходов) */

    if ( LEN_BAS != -1 ) {

        vir_adr = jmpadr + 3 - BEF_LEN;

        vir_len = flen - vir_adr; /* длина вируса */

    } else {

        vir_adr = jmpadr + 3; /* здесь это начало для сравнения */

        vir_len = BEF_LEN;    /* а это в данном случае именно длина вируса */

        BEF_LEN=0;

    }

    wc = malloc(vir_len); /* буфер для считывания вируса */

    wl=vir_adr;

    lseek(drive,wl,0); /* переход на начало вируса */

    read(drive,wc,vir_len); /* взяли его в память */

    if ( strncmp(wc+BEF_LEN,VIR_BEG,strlen(VIR_BEG)))

        goto CLOSE_RET0; /* уход при несовпадении начала */

    /* дополнительный контроль специфичный для разных видов */

    switch(vir_no) {

case 0:

        basadr = *(int *) (wc+2) - 0x100;

        if ( flen - basadr != LEN_BAS ) goto CLOSE_RET0;

        type_code="A"; /* тип вируса для сообщений */

        break;

case 1: type_code="B"; /* тип вируса для сообщений */

        break;

case 2: type_code="C"; /* тип вируса для сообщений */

        break;

    }

        printf("%s - болен (%s)", filename, type_code);

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

    switch(vir_no) {

case 0: /* в вирусе "A" они просто стоят с известным смещением */

        memcpy(oldstart,wc+basadr-vir_adr+0xA,3);

        break;

case 1: /* а вирус "B" нужно предварительно расшифровать */

        resident_virus=fixup; /* кроме того этот вирус остается

                              резидентным в памяти */

        {   int si, di, cx, oldplace=0x2E;

            di=0x23; si=0x123+vir_adr; cx=0x682;

            for (i=di; i<oldplace+3; i++)

                *(int*) (wc+i) ^= cx-- ^ si++;

            memcpy(oldstart,wc+oldplace,3);

        }

        break;

case 2: /* вирус ставится в начало - читаем в буфер весь остаток*/

        resident_virus=fixup;

        free(wc);

        wc=malloc(flen=(int)(lflen-vir_len-5));

        lseek(drive,wl=vir_len,0);

        read(drive,wc,flen);

    }

for_fixup:

    if(fixup) { /* поступило задание на лечение зараженных модулей */

        /* если открывали только для чтения, переоткроем */

        drive=att_reopen(drive, filename, block.attrib, &msg, access);

        if(drive==-1) { /* не удалось переоткрыть */

            exit_code=-2;

            goto exit_test;

        }

        /* восстанавливаем правильное начало и длину файла */

        lseek(drive, 0L, 0);

        if ( exe_file ) {

            write(drive, oldstart, 28);

            chsize(drive, lflen);

        } else

        if ( LEN_BAS != -1 ) {

            write(drive, oldstart, 3);

            chsize(drive, wl=vir_adr);

        } else {

            write(drive, wc, flen);

            chsize(drive, wl=flen);

        }

        msg = " - ИСЦЕЛЕН";

        _dos_setftime(drive,file_time, file_date);

        close(drive);

        /* если меняли атрибуты файла, восстановить старые */

        if(access == O_RDONLY)

            _dos_setfileattr(filename,block.attrib);

    }

    else close(drive);

    exit_code=1;

exit_test:

    if( wc != NULL) free(wc);

    if( msg != NULL) printf("%s\n",msg);

    return exit_code;

TRUP: /* файлы, испорченные заменой первых 5 байт исправить невозможно -

         остается их стирать */

    printf("%s - покойник",filename);

    close(drive);

    if(fixup) {

        _dos_setfileattr(filename, 0); /* стираем все атрибуты

             на случай, если стояло только чтение */

        if ( unlink(filename) ) /* и пробуем стереть */

            { msg = " - СТЕРЕТЬ НЕ УДАЛОСЬ"; exit_code=-2; }

        else { msg = " - ПОХОРОНЕН"; exit_code=-1; }

    }

    goto exit_test;

CLOSE_RET0: /* в начале JMP, но как вирус не опознан */

    vir_len=flen-jmpadr;

    /* собираем статистику */

    if(vir_len>=MIN_VIRUS && vir_len<MAX_VIRUS)

        len_index[vir_len-MIN_VIRUS]++;

StartNotJmp:

    exit_code=0;

    msg=NULL;

    close(drive);

    goto exit_test;

EXE: /* попался EXE модуль */

    exe_file=1;

    vir_no = test_exe(drive, (EXE_H *)oldstart, &lflen);

    if ( vir_no == -1 ) goto StartNotJmp;

    printf("%s - болен (%c)", filename, *("C"+vir_no));

    goto for_fixup;

for_second:

    close(drive);

    /* выдаем на экран имя файла, если он подозрителен по статистике */

    vir_len=flen-jmpadr;

    if  (  vir_len>=MIN_VIRUS

        && vir_len<MAX_VIRUS

        && len_index[vir_len-MIN_VIRUS] > critical_no )

    printf("%s - подозрителен\n",filename);

    return 0;

}

/* В зависимости от первого параметра сохраняется (1) или

   восстанавливается (0) позиция курсора. Позиции хранятся

   в стеке на 10 значений, хотя для данной программы нужно

   только одно. При сохранении позиции и ненулевом втором

   параметре стирается строка, содержащая курсор.

*/

CurSavRest(int save, int clear)

{   static int pointer=0, cstack[10];

    union REGS regs;

    if(save) {

        regs.x.ax=0x300;

        regs.x.bx=0;

        int86(0x10, &regs, &regs);

        cstack[pointer]=regs.x.dx;

        if(clear) {

            regs.x.cx=regs.x.dx;

            regs.x.dx &= 0xFF00;

            regs.x.dx += 79;

            regs.x.ax = 0x600;

            regs.x.bx = 0x700;

            int86(0x10, &regs, &regs);

        }

        if(pointer<9) pointer++;

    } else {

        if(pointer>=0) pointer--;

        regs.x.ax=0x200;

        regs.x.bx=0;

        regs.x.dx=cstack[pointer];

        int86(0x10, &regs, &regs);

    }

}

/* вывод имени файла на экран с возвратом курсора на старое место */

outfname(char *filename)

{

    CurSavRest(1,1);

    printf(filename);

    CurSavRest(0,1);

}

/* если файл был открыт только для чтения, у него меняются атрибуты

   и он открывается заново на чтение и запись */

int att_reopen(int drive, char *filename, char attrib, char **msg, char acc)

{

    if(acc == O_RDONLY) {

        close(drive);

        _dos_setfileattr(filename, attrib & ~(_A_RDONLY|_A_SYSTEM));

        drive=open(filename, O_RDWR | O_BINARY);

        if (drive==-1) *msg = "!!! ОШИБКА ДОСТУПА !!!";

    }

    return drive;

}

test_exe(int drive, EXE_H *header, long *lflen)

{

    static struct virinfo {

        unsigned ip, sp, len,

            old_ss, old_sp, old_ip, old_cs;

        char *code_start; } *vi,

        v_inf[]= {

            {0xC5, 0x710, 0x710, 0x45, 0x43, 0x47, 0x49, "\xFC\6\x2E\x8C\6\x31"}

        };

#define exe_no 1

    unsigned char *wc;

    int i, last_virus=-1, found;

    long wl;

    do {

        found=0;

        for ( i=0; i<exe_no; i++) {

            vi=v_inf + i;

            if( header->IP != vi->ip ) continue;

            if( header->SP != vi->sp ) continue;

            wc=malloc(vi->len);

            wl=(header->CS + header->header_size) << 4;

            lseek(drive, wl,0);

            read(drive,wc,vi->len);

            if ( memcmp(vi->code_start, wc+vi->ip, strlen(vi->code_start)) == 0 ) {

                found=1;

                last_virus=i;

                header->IP = *(int*)(wc + vi->old_ip);

                header->CS = *(int*)(wc + vi->old_cs);

                header->SP = *(int*)(wc + vi->old_sp);

                header->SS = *(int*)(wc + vi->old_ss);

                *lflen = wl;

                header->len_reminder = wl % 512;

                header->size_pages = (wl + 511) / 512;

            }

            free(wc);

        }

    } while( found );

    return last_virus;

}

/*

    Относительно    несложно    написать    программу,    которая

обнаруживает появление  на диске  почти любого  вируса. Для этого

нужно   воспользоваться   тем,   что   при   заражении  программы

необходимо, чтобы вирус  начинал работу до  зараженной программы.

При этом практически во всех случаях необходимо внести  некоторые

изменения в начале программного модуля.

    Для программ формата COM вместо замены первых трех байт,  как

это  происходит  в  вирусах,  с  которыми  умеет  бороться данная

программа, автор выруса может воспользоваться тем, что почти  все

программы формата COM сами начинаются с команды перехода. В  этом

случае подстановка  перехода в  вирус может  производиться в  том

месте, куда делается первый переход.

    Для программ  в формате  EXE возможность  для вируса обойтись

без  изменения  первых  32  байт  программного  файла,  в которых

содержится основная управляющая информация, представляется весьма

проблематичной.

    Из этих фактов следует идея организации программы слежения за

проникновением на диск какого либо вируса. Программа должна вести

специальный  файл,   содержащий  для   всех  программных   файлов

параметры, которые заведомо должны измениться при заражении.  Эти

параметры могут, например, включать длину файла, сумму первых  32

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

переходом. Эта  информация должна  ежедневно сличаться  с текущим

состоянием  диска  и  обо  всех  замеченных  расхождениях  должно

выдаваться сообщение.

*/


Стеки

Существует несколько способов хранения данных и их структур,

размер которых ограничен лишь свободным объемом оперативной

памяти и изменяющих свои размеры по мере необходимости. Такие

структуры данных называют динамическими и они используют приемы

динамического управления памятью, т е процедуры выделения и

освобождения блоков памяти. К таким структурам относятся деревья

(графы) и связные списки и их частные варианты - стеки и

очереди. Список - это набор записей, каждая из которых имеет

поля данных и указатели на следующую запись в списке

(односвязный список) и, возможно, на предыдущую запись

(двухсвязный список). Последний элемент списка в качестве

указателя на следующий элемент содержит нуль - указатель, а

первый элемент списка содержит нуль - указатель в качестве

указателя на предыдущий элемент (если список 2-связный).

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

создания, добавления элементов с любого конца и вставки в нужное

место, удаления элементов и т д.

1. СТЕК - структура данных, подчиняющаяся дисциплинарной схеме

"последним вошел - первым вышел". Его организация основана на

связном списке и он занимает в памяти только необходимый в

текущий момент объем.  Стек удобен, когда необходимо запомнить

большое количество структур данных, а затем обработать их в

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

текст Паскалевского модуля, реализующего операции со стеком и

пример использующей его программы.Вам необходимо составить

Си-аналоги этих программ.

UNIT StackManager;

INTERFACE

CONST

StackOk=0;

StackOverflow=1;   {Переполнение стека}

StackUnderflow=2;  {Стек пуст}

VAR StackError:Byte;

TYPE

NodePtr=^Node;   {Указатель на узел, состоящий из}

Node=RECORD      {}

        Info:Pointer;  {указателя на значение}

        Next:NodePtr;  {и на следующий узел}

     END;

Stack=RECORD           {Стек - это запись}

        Head:NodePtr;  {Указатель на голову списка}

        Size:Word;     {размер элемента данных}

      END;

PROCEDURE InitStack(VAR S:Stack;Size:Word); {Формирует стек}

PROCEDURE ReInitStack(VAR S:Stack;Size:Word); {Переопределяет

                                 под элементы другого размера}

PROCEDURE ClearStack(VAR S:Stack);

PROCEDURE Push(VAR S:Stack;VAR E);

PROCEDURE Pop(VAR S:Stack;VAR E);

PROCEDURE Top(VAR S:Stack;VAR E); {Копирует значение на вершине

                                   стека в переменную Е}

FUNCTION  Empty(VAR S:Stack):Boolean;

IMPLEMENTATION

VAR

SaveHeapError:Pointer; {Для хранения старой ф-ции обработки

                       ошибок кучи}

{$F+}

FUNCTION HeapFunc(Size:Word):Integer;

begin HeapFunc:=1; end;

{$F-}

PROCEDURE InitStack(VAR S:Stack;Size:Word);

begin

SaveHeapError:=HeapError; {Сохр старый обработчик ошибок кучи}

S.Head:=nil; s.Size:=Size; StackError:=StackOk;

end;

PROCEDURE ReInitStack(VAR S:Stack;Size:Word);

begin

if s.Head<> nil then ClearStack(S);

S.Size:=Size; StackError:=StackOk;

end;

PROCEDURE ClearStack(VAR S:Stack);

VAR Deleted:NodePtr;

begin

StackError:=StackOk;

while S.Head<>nil do begin

Deleted:=S.Head; S.Head:=Deleted^.Next;

FreeMem(Deleted^.Info,S.Size);

Dispose(Deleted);

end;

end;

PROCEDURE Push(VAR S:Stack;VAR E);

LABEL Quit;

VAR NewNode:NodePtr;

begin

Heaperror:=@HeapFunc;

StackError:=StackOverFlow;

NewNode:=New(NodePtr);

if NewNode=Nil then goto Quit;

NewNode^.Next:=S.Head;

GetMem(NewNode^.Info,S.Size);

if NewNode^.Info=nil then goto Quit;

Move(E,NewNode^.Info^,S.Size);

StackError:=StackOk;

Quit:

    HeapError:=SaveHeapError;   {Вернем старую ф-цию}

end;

PROCEDURE Pop(VAR S:Stack;VAR E);

VAR Deleted:NodePtr;

begin

StackError:=StackUderflow;

if S.Head=nil then Exit;

Deleted:=S.Head; S.Head:=Deleted^.Next;

Move(Deleted^.Info^,E,S.Size);

FreeMem(Deleted^Info,s.Size); Dispose(Deleted);

StackError:=StackOk;

end;

PROCEDURE Top(VAR S:Stack;VAR E);

begin

StackError:=StackUderflow;

if S.Head=nil then Exit;

Move(Deleted^.Info^,E,S.Size);

StackError:=StackOk;

end;

FUNCTION  Empty(VAR S:Stack):Boolean;

begin Empty:=S.Head=nil end;

END.

{Программа для хранения значений в стеке}

Uses StackManager;

VAR St:Stack; I:Integer; R:Real; B:Byte;

begin

InitStack(St,Sizeof(Integer));

for i:=1 to 100 do Push(St,I);

Writeln('':20,'Первые 100 чисел');

Writeln('':20,'целые значения');

Writeln;

while not Empty(St) do begin

  for B:=1 to 10 do begin

     Pop(St,I); Write(I:5)

                    end;

Writeln

                        end;

ReadLn;

ReInitStack(St,SizeOf(Real));

for i:=1 to 100 do begin R:=I; Push(St,R); end;

Writeln('':20,'Вторые 100 чисел');

Writeln('':20,'вещественные значения');

Writeln;

while not Empty(St) do begin

  for B:=1 to 10 do begin

     Pop(St,R); Write(R:5:1)

                    end;

Writeln

                        end;

ReadLn;

end.


Очереди

ОЧЕРЕДИ - базирующаяся на списке структура данных,

организованная по принципу "первым вошел - первым и вышел".

Базовыми операциями над очередбю являются:

insert - добавить в очередь новый элемент

take_out - удалить из очереди 1-й элемент

Как и для стека число элементов в очереди не должно

лимитироваться - память должна запрашиваться и освобождаться

динамически по мере добавления новых или удаления имеющихся

элементов.

Приводится Си- вариант реализации очереди из элементов типа int.

В файле queue.h представлен интерфейс с программами обслуживания

очереди; определена структура QUEUE и прототипы ф-ций insert и

take_out. Ф-ция take_out имеет параметр error, передаваемый по

ссылке, его значение 0, если очередь не пуста и 1 в противном

случае. В файле queue.c приведена программа для работы с

очередью.

 Ваша задача составить Паскаль - модуль для управления

очередью и программу использования подпрограмм этого модуля.

/*Файл queue.h*/

#define QUEUE struct queue

#include<alloc.h>

QUEUE

{

 int info;

 QUEUE *next;

};

void insert(QUEUE **q, int item)

{

QUEUE *current =*q;

QUEUE *previous=0;

QUEUE *new_node;

while(current)

{

previous=current; current=current->next;

}

new_node=(QUEUE *)malloc(sizeof(QUEUE));

new_node->info=item;

if(previous)

{

 new_node->next=previous->next;

 previous->next=new_node;

}

else

{

*q=new_node; (*q)->next=0;

}

}

int take_out(QUEUE **q,int error);

{

int value=0;

QUEUE *old_header=*q;

if(*q)

{

value=old_header->info;

*q=(*q)->next;

free(old_header);

*error=0;

}

else *error=1;

return value;

}

/*Тестовая программа использования очереди*/

#include<stdio.h>

#include<alloc.h>

#include "queue.h"

QUEUE *q1,*q2;

main()

{

int error;

insert(&q1,12);

insert(&q1,13);

insert(&q1,14);

insert(&q1,15);

insert(&q2,take_out(&q1,&error));

insert(&q2,take_out(&q1,&error));

insert(&q2,take_out(&q1,&error));

insert(&q2,take_out(&q1,&error));

printf("\nremove(&q2)=%d",take_out(&q2,&error));

printf("\nremove(&q2)=%d",take_out(&q2,&error));

printf("\nremove(&q2)=%d",take_out(&q2,&error));

printf("\nremove(&q2)=%d",take_out(&q2,&error));

}


Списки

Списки очень популярные структуры данных и применяются для

представления абстрактного аппарата поиска элементов,

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

множество разновидностей списков - односвязные, закольцованные,

двухсвязныые и пр. Простой список может быть определен при

помощи следующих операций:

insert- добавить в список элемент

take_out-удалить элемент из списка

is_present - определить, есть ли в списке заданный элемент

display - вывести список

destroy - разрушть список и освободить память под ним

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

односвязным списком с элементами достаточно сложной структуры -

вложенными структурами и вложенными объединениями. В файле

mylist.h вначале определяются структуры STAFF (персонал),

STUDENT, PROFESSOR, содержащие каждая по 2 числовых поля; далее

описывается перечислимый тип NODE_TYPE, с помощью которого будет

описываться поле списковой структуры, указывающее на тип

элемента поля. Списочная структура LIST включает 5 обычных

полей: last_name, first_name, age (возраст), next, tag (флаг или

признак), а также объединение node_tag, состоящее из 3-х членов

- student, professor, staff. Каждая переменная типа LIST может

содержать элементы только одного типа из объединения.

Дополнительно к перечисленным выше в файле определена

статическая ф-ция create_node(LIST *item) (создать элемент),

назначение которой - отводить память под новый элемент списка и

передавать данные, размещенные по адресу item в новый элемент.

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

со списком.

Ваша задача - составить Паскалевский модуль, обеспечивающий

такую же работу со списком и тестирующую программу.

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#define STAFF struct stuff_type

STAFF

{

int years_of_service;

float hourly_wage;

};

#define STUDENT struct student_type

STUDENT

{

float grade_pt_average;

int level;

};

#define PROFESSOR struct prof_type

PROFESSOR

{

float annual_salary;

int dept_number;

};

#define NODE_TYPE enum node_type

typedef NODE_TYPE {student,professor,staff};

#define LIST struct list

LIST

{

char last_name[10];

char first_name[10];

int age;

LIST *next;

NODE_TYPE tag;

union

{

STUDENT student;

PROFESSOR profesor;

STAFF staff;

}node_tag;

};

static LIST* create_node(LIST *item)

{

LIST *node;

node=(LIST *) malloc(sizeof(LIST));

*node=*item;

return node;

}

void destroy_list(LIST **lst)

{

LIST *current_node=*lst;

LIST *previous_node=0;

while (current_node!=0)

{

previous_node=current_node;

current_node=current_node->next;

free(previous_node);

}

*lst=0;

}

int take_out(LIST **lst,LIST *item)

{

LIST * current_node=*lst;

LIST * previous_node=0;

while(current_node!=0 && strcmp(current_node->last_name,

item->last_name)!=0)

{

previous_node=current_node;

current_node=current_node->next;

}

if(current_node!=0 && previous_node!=0)

{

*lst=current_node->next;

free(current_node);

}

else if(current_node!=0 && previous_node==0) /*Здесь возможно

                                              ошибка*/

{

previous_node->next=current_node->next;

free(current_node);

}

 }

void insert(LIST **lst,LIST *item)

{

/*Фамилия используется как ключевое поле*/

char key[10];

LIST * current_node=*lst;

LIST * previous_node=0;

LIST * new_node;

strcpy(key,item->last_name);

while(current_node!=0 && strcmp(current_node->last_name,key)<0)

{

previous_node=current-node;

current_node=current_node->next;

}

new_node=create_node(item);

new_node->next=current_node;

if(previous_node==0) *lst=new_node;

else previous_node->next=new_node;

}

void display(LIST *lst)

{

LIST * current=lst;

while(current)

{

printf("\n%s,  %s",current->last_name,current->first_name);

printf("\n Возраст=%d",current->age);

switch(current->tag)

{

case student:

printf("\nСпециализация

: %.2f",current->node_tag.student.grade_pt_average); printf();

printf("\nКурс : %d\n",current->node_tag.student.level);

break;

case prifessor:

printf("\nНомер кафедры :

%d",current->node_tag.professor.dept_number); printf();

printf("\nОклад

:$%2f\n",current->node_tag.professor.annual_salary);

break;

case staff:

printf("\nСрок службы : %d",

current->node_tag.staff.years_of_service); printf();

printf("\nПочасовый тариф:$%2f\n",

current->node_tag.staff.hourly_wage);

}

current=current->next;

}

}

int is_present(LIST *lst,LIST *item)

{

LIST * current_node=lst;

while(current_node && strcmp(item->last_name,

current_node->last_name)!=0)

current_node=current_node->next;

return (current_node!=0);

}

/*Программа тестирования*/

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#include "mylist.h"

LIST *my_list;

main()

{

LIST *item1=(LIST *) malloc(sizeof(LIST));

LIST *item2=(LIST *) malloc(sizeof(LIST));

LIST *item3=(LIST *) malloc(sizeof(LIST));

strcpy(item1->last_name,"Smith");

strcpy(item1->first_name,"Robert");

item1->age=26;

item1->tag=staff;

item1->node_tag.staff.years_of_service=3;

item1->node_tag.staff.hourly_wage=5.25;

insert(&my_list,item1);

strcpy(item2->last_name,"Jones");

strcpy(item2->first_name,"Richard");

item2->age=56;

item2->tag=professor;

item2->node_tag.professor.dept_number=7;

item2->node_tag.professor.annual_salary=48321.0;

insert(&my_list,item2);

strcpy(item3->last_name,"Evans");

strcpy(item3->first_name,"Henry");

item3->age=19;

item3->tag=student;

item3->node_tag.student.level=1;

item3->node_tag.student.grade_pt_average=3.1;

insert(&my_list,item3);

display(my_list);

if(is_present(&my_list,item1))

printf(""\nЭлемент 1 присутствует в списке  \n);

else

printf(""\nЭлемент 1 отсутствует в списке  \n);

take_out(&my_list,item3);

display(my_list);

take_out(&my_list,item2);

display(my_list);

take_out(&my_list,item1);

display(my_list);

printf("\n");

}


Двоичные деревья

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

узел дерева содержит указатели на лежащие ниже узлы - в 2-ичном

дереве их 2: правый и левый. Обычно двоичные деревья используют

для запоминания упорядоченной информации. Для каждого узла

2-ичного упорядоченного дерева все левые узлы имеют меньшие

значения, а правые - большие. Численные данные можно

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

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

2-ичные деревья привлекательны:

1. простотой поиска; для каждого узла легко определить, лежит ли

искомый объект в левом или правом поддереве;

2. легкостью включения в дерево новых узлов - эта операция

требует изменения лишь 3-х ссылок.

Сортировать 2-ичные деревья легко только тогда, когда они

сбалансированы, т е левые и правые поддеревья имеют

приблизительно одинаковое число элементов. Недостатком 2-ичных

деревьев является трудность их балансировки.

ЗАДАЧА: прочитать список слов и распечатать его в алфавитном

порядке с указанием количества вхождений в список каждого слова.

Без использования динамических структур можно бы переписать

слова в массив, отсортировать его и распечатать - но мы

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

будем вынуждены просматривать весь введенный список для

выяснения наличия в нем текущего слова и это может потребовать

чрезмерно много времени. Другая проблема - невозможность заранее

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

хранить 1 раз. Обе эти проблемы решаются использованием 2-чного

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

осуществляется быстро и не нужна дополнительная сортировка перед

печатью, а динамическое выделение памяти под узлы не требует

знания объема необходимой памяти.

Структура данных для реализации 2-ичного дерева (в Паскалевском

синтаксисе) выглядит так:

TYPE

Wrd = ARRAY [0..9] OF CHAR;   {для слова}

NodePtr= ^Node;               {Указатель на узел}

Node=RECORD

        left,right:NodePtr;

        W:Wrd;

        cnt:Word;

END;

Каждый узел -это запись с полями для указателей на правого и

левого потомков, слова из 10 букв и счетчика вхождений. Каждый

раз, когда читается не встречавшееся слово, в памяти размещается

очередной такой узел.

Процедуру заполнения дерева FillTree мы построим просто - будем

читать слова из входного потока до встречи с концом файла и

вызывать процедуру вставки Insert, на которую и возложим

основную работу: она еачнет поиск с корневого узла, а если

корневой узел не содержит искомого слова, то вызовет саму себя

рекурсивно для продолжения поиска либо в левой, либо в правой

половине дерева; если Insert достигнет конца ветви, так и не

встретив слова, будет создан новый узел и рекурсия закончится

когда либо слово найдено либо создан новый узел. Нам понадобится

отсутствующая в Паскале (в Си она есть) подпрограмма StrCmp

сравнения 2-х символьных строк, возвращающая 0 при равненстве

строк, +1 если первая строка больше 2-й и -1 если первая меньше

2-й. Эту подрограмму вы напишете сами, а мы ее просто будем

вызывать. Печать дерева мы построим так же, как и поиск, печатая

для каждого узла сначала левое поддерево, затем сам узел и потом

правое поддерево.

uses crt;

TYPE

Wrd = String[10];   {для слова}

NodePtr= ^Node;               {Указатель на узел}

Node=RECORD

        left:NodePtr;

        right:NodePtr;

        W:Wrd;

        cnt:Word;

END;

VAR root:NodePtr;   {Указатель на корень дерева}

FUNCTION StrCmp(value:Wrd;W:Wrd):Integer;

begin

 if value=W then StrCmp:=0

 else if value>W then StrCmp:=1

 else if value<W then StrCmp:=-1

end;

PROCEDURE Insert(value:Wrd;VAR node:NodePtr);

begin

if node=Nil then begin

New(node);

With node^ do begin

right:=nil;left:=nil; cnt:=1;W:=value

end end

else begin with node^ do begin

case StrCmp(value,W) of

+1:Insert(value,right);

-1:Insert(value,left);

0 :inc(cnt)

end end end

end;

PROCEDURE FillTree;

VAR W:Wrd;

begin

root:=nil;

repeat

ReadLn(W);

if Length(W)=0 then break;

Insert(W,root);

until false;

end;

PROCEDURE PrintTree(node:NodePtr);

begin

if node <> nil then begin

with node^ do begin

PrintTree(left);

Write(cnt); Write('  '); Write(W); WriteLn;

PrintTree(right)

end end

end;

begin

clrscr;

writeln('Вводите слова; Конец - Enter');

FillTree; PrintTree(root)

end.

ЗАДАНИЕ : Отладить и испытать программу и составить и испытать

ее Си-аналог.


Программа вызова субпроцесса через оболочку

Приводится Си-подпрограмма os_shell для временного выхода в

COMMAND.COM, использующая библиотечную ф-цию getenv() для

получения указателя на строку специфицированного параметра среды

программы (в нашем случае COMSPEC). Вызовите эту подпрограмму из

написанной вами простейшей программы, а затем проделайте то же

самое на Паскале и Бейсике.

#include <stdio.h>

#include<stdlib.h

#include <process.h>>

void os_shell(void){

puts("Для возврата наберите exit")

system(getenv("COMSPEC"));}


Программа вызова субпроцесса с возвратом в предок

Приводится Си-пример программ предка и потомка. Предок

определяет выигрыш в скорости за счет использования сопроцессора

математики с плавающей точкой; для этого он дважды вызывает

потомка: сначала со строкой среды "87=NO", затем со строкой

среды "87=YES". Оба раза потомок выдаеи в предок время

исполнения тестового набора операций. Предок сравнивает

полученные  значения и выводит на экран коэффициент ускорения,

обеспеченный сопроцессором. Предок в самом начале проверяет

наличие сопроцессора анализом бита 1 слова из области BIOS по

адресу 40:10Н - если бит =1 то сопроцессор есть. Потомок

выполняет тестовый набор из MAX операций сложения, умножения,

деления вещественных чисел и компилируется с включенной опцией

эмуляции сопроцессора.

ЗАДАНИЕ: выполнить Паскаль - варианты обеих программ и провести

их испытания с доведением до работоспособного состояния.

#include<stdio.h>

#include<dos.h>

#include<stdlib.h>

#include<process.h>

#define MAX 0xffff

int main(void)

{

struct OP_TIME{      /*Для времени выполнения операций */

float summing, multiplaying, dividing;

}with_87,without_87;

char *envp[]={"87=YES",NULL};

char text_seg[7],text_offs[7];

unsigned seg,offs;

       /*Тест наличия сопроцессора*/

if(!(*(unsigned far *)MK_FP(0x40,0x10) & 0x0002))

{

 puts("\a Нет сопроцессора"); return 2;

}

seg=_DS; offs=FP_OFF(&with_87);

ultoa(seg,text_seg,16);

ultoa(offs,text_offs,16);

/*Запуск потомка с сопроцессором*/

if(spawnlpe(P_WAIT,"potomok.exe","potomok.exe",text_seg,

text_offs,NULL,envp)==-1)

{perror("\aНеудача в запуске потомка"); return 1;}

offs=FP_OFF(&without_87);

itoa(offs,text_offs,16);

envp[0]="87=NO";

/*Запуск потомка без сопроцессора*/

if(spawnlpe(P_WAIT,"potomok.exe","potomok.exe",text_seg,

text_offs,NULL,envp)==-1)

 {perror("\aНеудача в запуске потомка"); return 2;}

/*Обработка статистики и вывод результатов*/

printf("Время %u сложений: с 87 - %5.5f\n без 87 - %5.2f\n

коэф ускор - %5.2f\n",MAX,with_87.summing,without_87.summing,

without_87.summing/with_87.summing);

printf("Время %u умножений:с 87 - %5.5f\n без 87 - %5.2f\n

коэф ускор -

%5.2f\n",MAX,with_87.multiplaying,without_87.multiplaying,

without_87.multiplaying/with_87.multiplaying);

printf("Время %u делений: с 87 - %5.5f\n без 87 - %5.2f\n

коэф ускор - %5.2f\n",MAX,with_87.dividing,without_87.dividing,

without_87.dividing/with_87.dividing);

return 0;

}

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

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

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

"potomok.exe",его текст:

#include<stdio.h>

#include<dos.h>

#include<stdlib.h>

#include<bios.h>

#include<time.h>

#define MAX 0xffff

int main(int argc,char **argv)

{

usigned sewg,offs;

struct OP_TIME{      /*Для времени выполнения операций */

float summing, multiplaying, dividing;

}far * parent_mem;

unsigned repeat;

float first_op=123456.789, second_op=9876543.21,result;

long bios_time_before, bios_time_after;

if(argc<3) {puts("\aМаловато аргументов");return 1;}

sscanf(argv[1],"%x",&seg); sscanf(argv[2],"%x",&offs);

parent_mem=(struct OP_TIME far*) MK_FP(seg,offs);

bios_time_before=biostime(0,0L);  /*До сложений*/

for(repeat=0;repeat<MAX; repeat++) result=first_op+second_op;

bios_time_after=biostime(0,0L);  /*После сложений*/

parent_mem->summing=

(float)(bios_time_after-bios_time_before)/(float)CLK_TCK;

bios_time_before=biostime(0,0L);  /*До умножений*/

for(repeat=0;repeat<MAX; repeat++) result=first_op*second_op;

bios_time_after=biostime(0,0L);  /*После умножений*/

parent_mem->multiplaying=

(float)(bios_time_after-bios_time_before)/(float)CLK_TCK;

bios_time_before=biostime(0,0L);  /*До делений*/

for(repeat=0;repeat<MAX; repeat++) result=first_op/second_op;

bios_time_after=biostime(0,0L);  /*После делений*/

parent_mem->dividing=

(float)(bios_time_after-bios_time_before)/(float)CLK_TCK;

return 0;

}


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

Приводится Си-вариант интерпретатора команд shell.

Постановка задачи:  получив управление, SHELL должен

выводить на экран приглашение к вводу sh: и ожидать от

пользователя ввода команды в виде строки, заканчивающейся

возвратом каретки. Приняв строку, программа пытается выяснить

наличие 1-го элемента строки в своей таблице внутренних команд;

при обнаружении будет вызываться соответствующая подпрограмма, а

при отсутствии - вызываться функция EXEC с передачей командной

строки пользователя интерпретатору COMMAND.COM с ключем /C,

используя его в качестве процессора команд под своим

управлением. Наш SHELL для начала будет распознавать только 3

команды:

CLS для очистки экрана

DOS для запуска COMMAND.COM

EXIT для завершения работы и возврата управления следующему

интерпретатору команд

Вам предлагается добавить новые внутренние команды, сотавив

необходимые подпрограммы и вставив имена этих подрпрограмм

вместе с текстовой строкой команды в таблицу COMMANDS.

Затем вы составите Паскаль-вариант интерпретатора.

Алгоритм программы в общем виде следующий:

1.Вызывается прерывание 21Н с функцией 4АН для сокращения

потребляемой памяти на случай предоставления необходимого

пространства интерпретатору COMMAND.COM. Для упрощения примера

количество резервируемых параграфов мы задаем щедрой константой,

хотя эта величина должна бы вычисляться.

2.Просматривается окружение в поисках переменной COMSPEC,

определяющей положение выполнимой копии COMMAND.COM - если

переменная не найдена, программа завершается после выдачи

аврийного сообщения.

3.Загружается адрес собственного обработчика прерывания в вектор

Ctrl-C (Int 32H), чиобы не потерять управление при нажатии

пользователем Ctrl-C или Ctrl - Break.

4.Выводится текст приглашения.

5.Считывается буферизованная строка с командой пользователя.

6.Первый элемент командной строки ищется в таблице и при

обнаружении вызывается соответствующая подпрограмма.

7.При отсутствии составляется хвост командной строки с

присоединением к ключу /C команды пользователя и с помощью EXEC

вызывается копия COMMAND.COM с передачей адреса составленного

хвоста команды через блок параметров EXEC.

8. Шаги с 4-го по 7-й повторяются пока не будет введена команда

EXIT, приводящая к завершению SHELL.

#include <stdio.h>

#include<process.h>

#include<stdlib.h>

#include<signal.h>

/*Макрокоманда для числа элементов в структуре*/

#define dim(x) (sizeof(x) / sizeof(x[0]))

/*Прототипы функций*/

unsigned intrinsic(char*);

void extrinsic(char*);

void get_cmd(char*);

void get_comspec(char*);

void break_handler(void);

void cls_cmd(void);

void dos_cmd(void);

void exit_cmd(void);

/*Таблица внутренних команд*/

struct cmd_table{

char *cmd_name;

int (*cmd_fxn)();

                }commands[]=

                {"CLS",cls_cmd,

                 "DOS",dos_cmd,

                 "EXIT",exit_cmd};

static char com_spec[64];  /*Спецификация файла COMMAND.COM*/

main(int argc, char *argv[])

{

 char inp_buf[80];           /*Входной буфер клавиатуры*/

get_comspec(com_spec);       /*Получить сп-ю файла COMMAND.COM*/

/*Регистрируем обраб прерываеия Ctrl-C*/

if(signal(SIGINT,break_handler)==(int(*)())-1)

{fputs("Не ловится прерывание по Ctrl-C",stderr);exit(1)}

while(1)                   /*Главный цикл интерпретатора*/

{

 get_cmd(inp_buf);           Получить команду

if(!intrinsic(inp_buf)) extrinsic(inp_buf); /*  Если внутренняя

                                  запускаем ее  подпрограмму,

                                  иначе в COMMAND.COM */

}

}

/*Поиск команды пользователя в таблице внутренних; если

найдена,запускаем ее подпрограмму и возвратим истину иначе

ложь*/

unsigned intrinsic(char *input_string)

{

 int i,j;

/*Уберем лидирующие пробелы*/

while(*input_string=='\x20') input_string++;

/*Поиск в таблице команд*/

for(i=0;i<dim(commands);i++)

{

 j=strcmp(commands[i].cmd_name,input_string);

/*Если найдена*/

if(j==0) {(*commands[i].cmd_fxn)(); return(1);}

}

return(0);    /*Не нашли - возвращаем ложь*/

}

/*Обработка внешней команды передачей в копию COMMAND.COM,

которую получаем с помощью EXEC*/

void extrinsic(char*input_string)

{int status; status=system(input_string);

if(status) fputs("\nОшибка вызова COMMAND.COM\n",stderr); }

void get_cmd(char *buffer)

{

 printf("\nsh:");

gets(buffer);

strupr(buffer);

}

/*Получение полного пути и спецификации файла COMMAND.COM из

контекстной переменной COMSPEC*/

 void get_comspec(char* buffer)

 { strcpy(buffer,getenv("COMSPEC")); if(buffer[0]==NULL)

{fputs("\nВ контексте нет COMSPEC\n",stderr);exit(1);} }

void break_handler(void)

{

 signal(SIGINT,break_handler);

printf("\,sh:");

}

void cls_cmd(void)

{

 printf("\033{2j");

}

void dos_cmd(void)

{int status;

status=spawnlp(P_WAIT,com_spec,com_spec,NULL);

if(status) fputs("\nОшибка вызова COMMAND.COM\n",stderr);

}

void exit_cmd(void)

{

 exit(0);    /*Завершить shell*/

}


Очистка буфера клавиатуры последовательным чтением

procedure ClrKeyBuf;

VAR ch:Char;

begin while KeyPressed do ch:=ReadKey; end;


Ожидание нажатия клавиш

procedure ClrKeyBuf;

VAR ch:Char;

begin while KeyPressed do ch:=ReadKey; end;


Программа - прием символа

Программа приема символа нажатой клавиши, использующая

приведенные подрограммы.

uses crt;

{$I clrkeybuf.inc}

{$I wait.inc} VAR c:char;

begin ClrScr;

Writeln('Нажмите любую символьную клавишу'); ClrKeyBuf;

c:=ReadKey; Writeln('Была нажата клавиша с символом',c);

Writeln('Пауза до нажатия любой клавиши...'); Wait;

ClrKeyBuf; end.


Запись кода символа в клавиатурный буфер (Паскаль)

{$M 4096,0,0}  {Директива нужна для запуска субпроцесса}

uses crt,dos;

{Процедура записи ASCII - кода клавиши в буфер клавиатуры}

Procedure UnReadKey(KeyW:Word);

CONST

KbdStart =$1E;

KbdEnd   =$3c;

VAR

KbdHead:Word absolute $40:$1A;  {Голова буфера клавиатуры}

KbdTail:Word absolute $40:$1C;  {Хвост буфера клавиатуры}

OldTail:Word;

begin

OldTail:=KbdTail;

if KbdTail=KbdEnd then

KbdTail:=KbdStart

else Inc(KbdTail,2);

if KbdTail=KbdHead then KbdTail:=OldTail

else MemW[$40:OldTail]:=KeyW;

end;

{Подпрограмма засылки в клавиатурный буфер расширенных кодов,

имитирующих нажатие Alt - комбинаций, функциональных клавиш и

клавиш управления курсором }

Procedure UnReadExtCode(ExtCode:Word); begin

UnReadKey(Swap(ExtCode)) end;

{Запись в буфер 16-символьной строки}

Procedure UnReadString(S:String);

{Пример использования процедур}

VAR i:Byte;

begin Delete(S,17,255);

for i:=1 to Length(S) do UnReadKey(Ord(S[i])); end;

VAR St:String;

begin

ClrScr;

TextAttr:=Black+16*LightGray;

Write('Допишите число или исправьте:');

TexAttr:=LightGray;

UnReadString('22-2-19');

ReadLn(St);

Write(#10'Результирующая строка->');

HighVideo; Writeln(St); LowVideo; ReadLn;

St:='Hello Outer !'+#13;

Writeln('В буфер передается строка :',St);

UnReadString(St);

Writeln(#10'Запускающий файл Outer.');

SwapVectors;

Exec('Outer.exe','');

SwapVectors;

ReadLn

end.

При этом файл OUTER.PAS с программой ожидания ввода строк с

клавиатуры может быть таким:

var s:String;

begin Write(#10'Запрос <--');Readln(S);

Writeln(#10#10'На запрос была введена строка:',S)

end.


Определение нажатия ASCII и спецклавиш

#include<conio.h>

#include<stdio.h>

void main(void)

{int ch;

do

{puts("Нажмите любую клавишу");

if(!(ch=getch())) {

 ch=getch(); printf("Спецклавиша - расширенный скэн - код

%#u\n",ch); }

else printf("Символьная клавиша %c Код %#u\n",ch,ch);

puts("Продолжаете? (y/n)");

 }

while((ch=getch())=='y' ||ch=='Y');

}


Работа с паролем

Программа приглашает задать пароль, принимает его с

клавиатуры, сравнивает с заданным в программе эталоном. Строка

эталона correct_string хранится  зашифрованной и перед

сравнением восстанавливается; метод "шифрования" - сложение по

модулю 2каждого байта с кодом 0x4F. В нашем примере пароль - это

слово KASATKI. Если в библиотеке Паскаля вы не обнаружите

аналога ф-ции getpass() - составьте ее сами.

#include<conio.h>

#include<stdio.h>

int main(void)

{char *correct_string =

"\x04\x0E\x1C\x0E\x1B\x04\x06"; /*шифр слова KASATKI*/

char *input_string;

input_string=getpass("Введите пароль:");

while(*correct_string)

if(*input_string++!=(*correct_string++ ^ 0x4f))

{puts("\aНекорректный пароль - конец связи");return 1;}

puts("Доступ разрешен"); return 0;

}


Запись 2-байтового кода в буфер клавиатуры (Си)

Функция enter_kb_bios(), предназначенная  для записи 2-

байтового кода клавиши key_code в буфер клавиатуры:

#include<dos.h>

int enter_kb_bios(usigned key_code)

{struct REGPACK r;

r.r_cx=key_cde;

r.r_ax=0x0500;

intr(0x16,&r;

return r.r_ax & 0x00ff;

}

Использует эту ф-цию пусть демонстрационная программа, которая

пишет в буфер клавиатуры коды клавиш для запуска IDE Turbo C с

именем файла в командной строке и переходом в режим

редактирования. Помещенные в буфер коды клавиш набирают тестовую

программу, запускают ее на компиляцию и завершают работу IDE с

сохранением набранной программы:

#include <process.h>

#include <stdio.h>

#include <conio.h>

int enter_kb_bios(unsigned);

int main(void)

{/*Коды клавиш имитации ввода текста "main(){}", нажатия F9,

Alt-x, y, Enter*/

unsigned key_codes[]={0x006d, 0x0061,

0x0069,0x006e, 0x0028, 0x0029, 0x000d, 0x007b, 0x007d,  0x4300,

0x2d00, 0x0079, 0x000d, 0x0000}

int i=0;

while(key_codes[i]) enter_kb_bios(key_codes[i++]);

if(spawnlp(P_WAIT,"tc.exe","tc.exe","new.c",0)==1)

{perror("\a Ошибка запуска Турбо С");

while(kbhit()) getch(); /*Чистим буфер клавиатуры*/

return 1;

}

puts("Демонстрация завершена");

while(kbhit()) getch();

return(0);

}


Очистка буфера клавиатуры через указатели

Очистка буфера клавиатуры приравниванием  указателей головы и хвоста

#include <dos.h>

void clear_kb(void)

{char register _es *tail=0x1c, _es *head = 0x1a;

_ES=0x40;

disable(); *tail=*head;enable();

}


Ввод с предварительной очисткой буфера клавиатуры

#include<dos.h>

unsigned int getc_kb(void)

{register ret_ax=0;

_AX=0x0c01; geninterrupt(0x21);

ret_ax=_AL;

if(!ret_ax)

{_AX=0x0c01;geninterrupt(0x21);

ret_ax=_AL;return ret_ax<<8; }

return ret_ax;

}


Чтение из буфера клавиатуры через указатель

Чтение символов из буфера клавиатуры доступом к ячейке

памяти, на которую ссылается указатель головы:

#include<dos.h>

unsigned key_kb(void)

{register char _es *tail =(char _es*)0x1c;

register char _es *head =(char _es*)0x1a;

_ES=0x40;

if(*head == *tail) return 0;

disable(); tail=(char _es*)(*head);enable();

return *(unsigned _es*)tail;

}


Запись в буфер (Си)

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

построении демонстрационных программ, переносе текстов в любую

прикладную программу из другой резидентной программы и т д

Может выполняться на 2-х уровнях: использованием ф-ции АН=5

прерывания 16Н или непосредственным доступом к ячейкам памяти

клавиатурного буфера. В приведенном ниже примере символ вводится

в буфер либо по текущему значению указателя головы либо по

значению хвоста. Передаваемые ф=ции параметры: where задает

режим - если where =0 код помещается со сдвигом указателя головы

назад, если where =1 код помещается со сдвигом хвоста вперед,

key_code - 2-байтовый код для записи в буфер.

#include<dos.h>

enter_kb(int where, unsigned key_code)

{unsigned register _es *tail = (unsigned _es*)0x1c,

_es *head = (unsigned _es*)0x1a, _es* tmp,ret;

_ES=0x40;

disable();

switch(where){

case 0: tmp=(unsigned _es*)*head; tmp--;

if(tmp < (unsigned _es*)0x1e)

/*Перескок на конец буфера*/

tmp=(unsigned _es*)(0x3e -2);

if(tmp==(unsigned _es*)*tail) /*Буфер полон*/

{

 ret = 1; break;

}

else

{*(unsigned _es*)tmp=key_code;

*head=(unsigned _es*)tmp;

ret=0; break;

}

case 1:

tmp=(unsigned _es*)*tail;

if(tmp==(unsigned _es*)0x3e)

/*Перескок на начало буфера*/

tmp=(unsigned _es*)0x1e;

if(tmp+1==(unsigned _es*)*head /*Буфер полон*/

{

 ret=1;break;

}

else

{*(unsigned _es*)tmp=key_code; tmp++; *tail=(unsigned)tmp;

ret=0;break;

}

default: ret=2; /*Ошибка*/

}

enable();

return ret;}


Вывод изображений управляющих символов

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

видеосистемой.  После их отладки и проверки работоспособности

составьте и отладьте Си- и Бейсик - аналоги и разместите их для

Си в заголовочном файле scrutil.h для использования в других

программах.

1.1.ВЫВОД СПЕЦСИМВОЛОВ С ПОМОЩЬЮ CRT:

При подключенном модуле CRT можно выводить на дисплей

управляющие символы с кодами 0..31 без выполнения ими

управляющих функций (кроме символов с кодами 7,8,10,13). Примеры

работы с управляющими символами:

1.1.1.Вывод изображений управляющих символов.

Uses CRT;

CONST SpecialChars: Set of Char =[#7,#8,#10,#13];

      Ch:Char = #0;

VAR i: Byte;

begin

ClrScr;

while Ch < #32 do

  begin

    for i:=1 to 2 do begin

      Write('    Код',Ord(Ch):3,'-->');

      if (Ch in SpecialChars) then Write('Имеет действие')

      else Write(Ch:15);

      Ch:=Succ(Ch);

                     end;

  Writeln

  end;

  Write('Нажмите ввод для окончания...');

  Readln

  end.


Использование 4-х управляющих кодов экрана

USES CRT;

VAR i:Byte;

begin

ClrScr;

Writeln('Нажимайте Enter для продолжения');

Readln;

Writeln('Эффект от кода 7 - короткий звук'#7);

Writeln;Writeln;Readln;

Writeln('Демонстрация кода 8');

for i:=1 to 40 do Write('/');

for i:=1 to 40 do begin

Delay(100); Write(#8,'\',#8);

end;

Writeln;Writeln;Readln;

Writeln('Демонстрация кода 10');

Writeln;

Write('Эта'#10'строка'#10'разорвана'#10'кодами 10'#10);

Writeln(#10#10);Readln;

Writeln('Демонстрация кода 13');

for i:=1 to 40 do Write('/');

Write(#13);

for i:=1 to 40 do begin

Delay(100); Write('\');

                  end;

Writeln;Readln

END.


Работа с окнами (Паскаль)

 Процедура закраски квадратной области экрана с диагональю

X0,Y0 - X,Y символами Ch с задержкой при закраске ms:

USES CRT;

Proceedure Spiral(x0,y0,x,y:Byte;ms:Word;Ch:Char);

VAR height,width,j:Byte; c:Integer;

begin

c:=1; width:=x-x0+1; height:=y-y0+1;

repeat

for j:=1 to width do begin

gotoxy(x,y); Write(Ch);

if(y>Hi(WindMax)) and (x>Lo(WindMax)) then begin

gotoxy(1,1); InsLine                       end;

Delay(ms); x:=x-c    end;

x:=x+c; dec(height);

for j:=1 to height do begin

y:=y-c; gotoxy(x,y); Write(ch); Delay(ms)

                      end;

Dec(width); x:=x+c; c:=-1*c

until (height<1) or (width<1);

gotoxy(1,1);

end;

VAR i:Byte;  {Пример использования процедуры}

begin

ClrScr;

Spiral(1,1,80,25,2,#176);

for i:=1 to 10 do begin

TextAttr:=i;

Spiral(2*i,i,5*i,2*i,4,Chr(47+i));

                  end;

Readln;

end.


Вставка и удаление строк
Вставка и удаление строк.

Применение подпрограмм CRT InsLine/DelLine вставки и

удаления строк:

USES CRT;

VAR i,n:Byte;

begin

ClrScr;

gotoxy(1,2*7);

for i:=1 to 7 do Writeln('Ind/Del Line');

gotoxy(1,1);

repeat

for i:=1 to 7 do DelLine;

for i:=1 to 7 do InsLine

until KeyPressed;

end.


Синтез звуков выводом на экран

USES CRT;

Procedure Phone;

VAR i:Word;

begin

repeat

  for i:=1 to 100 do begin Sound(1200);Delay(10); Nosound end;

  Delay(800)

until KeyPressed

end;

b)

Procedure Bell;

begin

repeat

Sound(1800);Delay(2);

Sound(2000);Delay(2);

Sound(2200);Delay(2);

Sound(2400);Delay(2);

until KeyPressed;

Nosound

end;

c)

Procedure Sirena;

VAR i:Word;

begin

repeat

 for i:=400 t0 800 do begin Sound(i);Delay(3) end;

 Nosound

until KeyPressed;

end;

d)

Procedure Pause;

VAR ch:Char;

begin while KeyPressed do ch:=ReadKey;

Delay(200)

end;

BEGIN

ClrScr;

Write('Нажмите клавишу'#10#10#13);

Write('Звук телефона'#13); Phone; Pause;

Write('Звук зуммера'#13); Bell; Pause;

Write('Звук сирены'#13); Sirena; Pause;

ClrScr;

end.


Программный опрос режимов дисплея

В огромном числе задач использование стандартных средств вывода

на экран малоэффективно или просто громоздко - заполнение окон

каким - либо символом, сохранение изображений в файлах и пр.

1.3.1.Программный опрос текстовых режимов  дисплея.

a) Ф-ция определения адреса начала видеопамяти текстового

режима:

FUNCTION GetScreenPtr:Pointer;

begin

if (Mem[0:$0410] and $30) = $30

then GetScreenPtr:=Ptr($B000,0)

else GetScreenPtr:=Ptr($B800,0)

end;

b) Опрделение номера текущего текстового режима

FUNCTION CurrentMode:Byte;

begin CurrentMode:=Lo(LastMode) end;

FUNCTION Font8x8YES:Boolean;

begin Font8x8YES:=(LastMode and Font8x8) = Font8x8 end;

c)Определение длины видеопамяти:

FUNCTION GetScreenSize :Word;

VAR

R: Byte absolute $0000:$0484;

C: Byte absolute $0000:$044A;

begin

ifHi(LastMode)=1 then GetScreenSize:=Succ(R)*C*2

                 else GetScreenSize:=25*C*2;

end;

d)Определение числа столбцов:

FUNCTION GetColNum:Byte;

begin GetColNum:=Mem[0:$44A] end;

e)Определение числа строк:

FUNCTION GetRowNum:Byte;

begin GetRowNum:=GetScreenSize div GetColNum div 2 end;

Пример использования приведенных ф-ций:

USES CRT;

{$I get_ptr.inc}

{$I get_par.inc}

VAR P:Pointer;

begin

P:=GetScreenPtr;

if Seg(P^)=$B000 then TextMode(Mono) else TextMode(CO40);

Writeln ('Старт видеопамяти',Seg(P^),':',Ofs(P^));

Writeln ('Длина видеопамяти',GetScreenSize,'Байт');

Writeln('Емкость экрана',GetColNum,'x',GetRowNum);

Writeln; Write('Нажмите Enter...');

ReadLn;

end.


Непосредственный доступ к видеопамяти
Непосредственный доступ к видеопамяти.

Все четные адреса видеобуфера, начиная с 0, содержат коды

символов, а нечетные - цветовые атрибуты. Доступ к видеобуферу

можно получить, наложив на него массив 2-байтовых элементов:

TYPE

VideoWord = RECORD

            Symbol:Char;

            Attrib:Byte;

            END;

VideoText=Array[1..50*80] of VideoWord;

VideoTextPtr=^VideoText;

Непосредственный доступ к видеопамяти производится через

переменную указательного типа VideoTextPtr. Если известно

текущее число столбцов М, то преобразование координат X,Y в

номер элемента массива осуществляется легко: М*(Y-1)+X.

a)Процедура, записывающая в видеопамять постоянную пару

значений типа VideoWord, быстро заполняя прямоугольную область:

USES CRT;

TYPE

VideoWord = RECORD

            Symbol:Char;

            Attrib:Byte;

            END;

VideoText=Array[1..50*80] of VideoWord;

VideoTextPtr=^VideoText;

Procedure FillArea(VT:VideoTextPtr;X1,Y1,X2,Y2:Byte;Ch:Char;

Attr:Byte);

VAR

i,j,k:Word;

BuffWord:VideoWord;

M:Byte absolute 0:$44A;

begin

with BuffWord do begin

Symbol:=Ch;

Attrib:=Attr

end;

for j:=Y1 to Y2 do begin

k:=M*(j-1);

  for i:=X1 to X2 do VT^[k+i]:=BuffWord;

end;

END;

b)Процедура, выполняющая перекраску прямоугольной области

экрана:

Procedure ChangeAttr(VT:VideoTextPtr;X1,Y1,X2,Y2,Attr:Byte);

VAR

VW:Videoword;

i,j,k,n:Word;

M:Byte absolute 0:$44A;

begin

VW.Attrib:=Attr;

for j:=Y1 to Y2 do begin

k:=M*(j-1);

  for i:=X1 to X2 do begin

   n:=k+i;

   VW.Symbol:=VT^[n].Symbol;

   VT^[n]:=Vw

  end

 end

END;

{$I get_ptr.inc}

VAR V:Pointer;

begin

V:=GetScreenPtr;

if Seg(V^)=$B000 then TextMode(Mono) else TextMode(CO80);

Write('Нажимайте клавишу ввода...'); Readln;

FillArea(V,2,2,80,25,#177,White);    ReadLn;

FillArea(V,5,5,40,20,#176,Blue + 16*LightGray);    ReadLn;

ChangeAttr(V,25,15,75,24,Green+16*Cyan); ReadLn;

ClrScr

END.


Работа с таблицей знакогенератора

Для уяснения принципов формирования и загрузки таблиц

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

переворачивает "вверх ногами" рисунки символов, а вторая

восстанавливает положение рисунков.Вам предлагается составить и

испытать их Си-варианты.

{Информацию об используемом наборе символов можно получить через

подфункцию 30Н функции 11Н прерывания 10Н:

На входе АН=11Н;

         AL=30H

         BH=вид запрашиваемой информации

         0- содержимое вектора 1FH

         1-                     43H

         2-указатель на набор символов 8х14

         3                              8х8

         6-             альтернативный  8х16

         7-                             9х16

На выходе  CL=высота символа

           DL=число текстовых строк

           ES:BP=требуемый указатель

}

Программа переворачивания:

uses dos,crt;

const bc=16;                 {Байтов на символ}

type

at=array[1..256,1..bc] of byte; {2-мерный массив знакогенеретора}

var

ch:char;

a:at;

p:^at;

R:registers;

h,s,temp:byte;

i,j:word;

begin

{Получаем информацию с помощью 30-й подфункции 11-й ф-ции 10-го

прерывания}

R.AH:=$11;

R.AL:=$30;

R.BH:=6;            {интересуемся адресом таблицы 8х16 пикселов}

intr($10,R);

p:=Ptr(R.es,R.bp);  {адрес таблицы знакогенератора}

h:=r.cl;            {высота символов в пикселах}

s:=r.dl;            {количество строк}

{Переписываем таблицу знакогенереции в массив а}

a:=p^;

{Меняем строки в рисунках символов}

for  i:=1 to 256 do

for  j:=1 to (bc shr 1) do

begin   temp:=a[i,j]; a[i,j]:=a[i,bc-j]; a[i,bc-j]:=temp;end;

{Подсовываем свoю таблицу с перевернутыми буквами}

R.AH:=$11;

R.AL:=$00;

r.es:= Seg(a);r.bp:=Ofs(a);   {адрес в ES:BP}

r.cx:=256;                    {число символов}

r.dx:=0;                      {смещение в таблице}

r.bl:=0;                      {номер таблицы}

r.bh:=16;                     {байт на символ}

intr($10,R);

{Любуемся проделанным

while not keypressed do;}

{Возвращаем старую таблицу

R.AH:=$11;

R.AL:=$04;

r.bl:=0;

intr($10,R);              }

end.

Программа восстановления:

uses dos;

var

R:registers;

begin

{Возвращаем старую таблицу с помощью подф-ции 4 ф-ции 11}

R.AH:=$11;

R.AL:=$04;

r.bl:=0;             {номер таблицы}

intr($10,R);

end.


Управление курсором (Паскаль)

Проще всего - вызовом ф-ции АН=1 прерывания 10Н с указанием

начальной и конечной строк курсора в регистрах процессора.

Формально номера строк принимаются от 0 до 31, при задании

начальной строки с номером 32 курсор невидим.

USES CRT,DOS;

{Установка формы курсора}

PROCEDURE SetCursorSize(c_Start,c_End:Byte);

VAR Regs:Registers;

begin

with Regs do begin

AH:=$01; CH:=c_Start; CL:=c_End;

             end;

Intr($10,Regs)

END;

1.5.2.{Курсор по умолчанию}

PROCEDURE SetNormalCursor;

VAR SE:Word;

begin

if(LastMode>=Font8x8) then SE:=$0507

else if(LastMode=Mono) then SE:=$0b0c else SE:=$0607;

SetCursorSize(Hi(SE),LO(SE));

END;

1.5.3.{Крупный блок-курсор}

PROCEDURE SetBlockCursor;

VAR c_End:Byte;

begin

if(LastMode>=Font8x8) or (LastMode<>Mono)

then c_End:=7 else c_end:=13;

SetCursorSize(0,c_End)

END;

1.5.4.{Отключение курсора}

SetCursorSize(32,0);


Определение и настройка параметров видеоадаптера

ЗАДАНИЕ: Приводится набор Си- подпрограмм для работы с

видеосистемой различными методами. После проверки и

обеспечения их работоспособности необходимо составить их Паскаль

- и Бейсик - аналоги и протестировать работоспособность.

Дальше мы будем рассматривать примеры подпрограмм, для которых

определены символьные константы в файле screen.h (для Паскаль

- варианта составьте файл-аналог screen.inc):

#define ENTIRE 0

#define UP     6

#define DOWN   7

#define LEFT   16

#define RIGHT  17

#define HORIZONTAL  0

#define VERTICAL    1

#define ON          1

#define OFF         0

#define BAD_PARAM   255

#define END_OF_SCREEN 254

#define OK    0

#define COPY  0x00

#define AND   0x08

#define OR    0x10

#define XOR   0x18

struct cursor_pos{

  unsigned row_col;

char reg_14,reg_15;

char cur_page;

};

2.1.Определение и настройка параметров видеоадаптера

2.1.1.Программа,печатающая сообщение об обнаруженном типе

адаптера:

#include <stdio.h>

#include <dos.h>

char *messages[]=

{

"отсутствие адаптера",

"моно-адаптер и моно-монитор",

"СGA с цветным монитором",

"EGA с цветным монитором",

"EGA c монохромным монитором",

"профессиональная цветная графическая система",

"VGA с аналоговым монохромным монитором",

"VGA с аналоговым цветным монитором",

"",

"",

"неизвестная комбинация"

}

char * alt_system="не обнаружен";

void main(void)

{

 union REGS r; char mode; int index;

mode=*(char *) MK_FP(0x400x49);

if(mode ==7) printf("Моно адаптер геркулес\n");

else {

printf("Активный адаптер \n");

r.x.ax=0x1a00;  int86(0x10,&r,&r);

if(r.h.al==0x1a)

{switch(r.h.bl){

case 0:index=0; break;

case 1:index=1; break;

case 2:index=2; break;

case 4:index=3; break;

case 5:index=4; break;

case 6:index=5; break;

case 7:index=6; break;

case 8:index=7; break;

case 0xb:index=8; break;

case 0xc:index=9; break;

default: index=10; }

printf("%s\n",messages[index]);

printf("Альтернативный адаптер:");

if(r.h.bh!=0) {alt_system=messages[index];

prinf("%s\n"alt_system);}

}

else

{

 r.h.ah=0x12;r.h.bl=0x10; int86(0x10,&r,&r);

if(r.h.bl==0x10) printf("CGA-адаптер\n");

else

{printf("EGA-адаптер c");

if(r.h.bh) printf("монохромным монитором\n");

else printf("цветным монитором\n");

printf("Оъем памяти - %d Кбайт \n"64*(r.h.bl+1));

}

}

}

}


Переключение видеостраниц

Функция, устанавливающая заданную параметром page

видеостраницу:

#include<dos.h>

void set_page(int page)

{

 _AL=(char)page; _AH=0x05; geninterrupt(0x10);

}


Управление курсором (Си)

Функция,выключающая курсор или восстанавливающая его форму в зависимости от параметра mode:

#include<<dos.h>

#include<screen.h>

void cursor(int mode)

{static unsigned shape;

if(mode==ON) {if(shape) {_CX=shape; shape =0;}

else return;}

else

{

 shape=*(unsigned far *)MK_FP(0x40,0x60);

_CX=0x2000; /*Бит 6=0,бит 5=1*/

}

_AH=1; geninterrupt(0x10);

}

2.2.2. Универсальная ф-ция управления курсором.

#include<dos.h>

#include<screen.h>

goto_xy(int stroka, int stolb, int page)

{

unsigned old_ds;

register char max_col,max_str,ret;

register unsigned word,cur_pos;

old_ds=_DS;_DS=0x40;

max_col=*(char near*)0x4a;

max_str=*(char near*)0x84;

if(stroka<=max_str && stolb<=max_col)

{

 cur_pos=(stroka << 8) | stolb

*(unsigned near*)(0x50 + (page <<1))=cur_pos;

if(page==*(char near*)0x62)

{

 _DX=*(char near *)0x63;

outportb(_DX++,14);

outportb(_DX--,(char)(word>>8));

outportb(_DX++,15);

outportb(_DX,word);

}

ret=OK;

}

else ret=BAD_PARAM;

_DS=old_ds;

return ret;

}

2.2.3.Функции сохранения и восстановления позиции курсора с

использованием доступа к портам и области BIOS

#include<dos.h>

#include<screen.h>

void save_cur(struct cursor_pos *work)

{register base_adr,offs;

work->cur_page=*(char far*)MK_FP(0x40,0x62);

offs=0x50+(work->cur_page<<1);

work->row_col=*(unsigned far*)MK_FP(0x40,offs);

base_adr=(unsigned far*)MK_FP(0x40,0x63);

outportb(base_adr++,15);

work->reg_15=inport(base_adr);

}

void rest_cur(struct cursor_pos *work)

{register base_adr,offs;

offs=0x50+(work->cur_page<<1);

*(unsigned far*)MK_FP(0x40,offs)=work->row_col;

base_adr=(unsigned far*)MK_FP(0x40,0x63);

outportb(base_adr++,14);

outportb(base_adr--,work->reg_14);

outportb(base_adr++,15);

outportb(base_adr,work->reg_15);

}


Вертикальный вывод строки на активную страницу

Вертикальный вывод произвольной строки str на активную

страницу с атрибутом attr, корректно работающую и в текстовых и

в графических режимах:

#include<dos.h>

#include<screen.h>

int vert_prn(int stroka, int stolb, char *str, char attr)

{register char cur_page; union REGS r;

cur_page=*(char far*)MK_FP(0x40,0x62):

if(goto_xy(stroka,stolb,cur_page)==BAD_PARAM) return BAD_PARAM;

while(*str)

{

 r.h.bh=cur_page; r.h.bl=attr; r.x.cx=1;

 r.h.ah=0x09; r.h.al=*str++;int86(0x10,&r,&r);

 stroka++;

if(goto_xy(stroka,stolb,cur_page)==BAD_PARAM) return

END_OF_SCREEN

 }

return OK;

}


Многократный вывод символа и вывод рамок

Ф-ция  выводящая с позиции str, stolb символ symb с

атрибутом atr заданное число раз n:

#include<dos.h>

#include<screen.h>

int printche(int str, int stolb, char symb, char atr, int n)

{register cur_ page;register max_stolb;union REGS r;

cur_page=*(char far*)MK_FP(0x40,0x62):

max_stolb=*(unsigned far*)MK_FP(0x40,0x4a);

if(goto_xy(str,stolb,cur_page)==BAD_PARAM) return BAD_PARAM;

if(max_stolb<(stolb+n-1)) return BAD_PARAM;

 r.h.bh=cur_page; r.h.bl=atr; r.x.cx=n;

 r.h.ah=0x09; r.h.al=*symb;int86(0x10,&r,&r);

 return OK;

}

2.3.3.Вывод рамки из символов массива style с  использованием

ф-ции printche() :

#include<string.h>

#include<dos.h>

#include<screen.h>

char

style[8]={'\xC9','\xCD','\xBB','\xBA','\xBA','\xC8','\xCD','\xBC'}

int border(int s0,int c0,int s1,int c1, char *title, char

atr_title, char style[],char atr)

{register max_stolb,max_string,s;

max_stolb=*(unsigned far*)MK_FP(0x40,0x4a);

max_string=*(unsigned far*)MK_FP(0x40,0x84);

 if(s0>=0 && s0<s1 && c0>=0 &&c0<c1 && s1<=max_string

&& c1<max_stolb  && ((title!=NULL && (c1-c0 >= strlen(title)) ||

title==NULL))

{printche(s0,c0,style[0],atr,1);

printche(s0,c0+1,style[1],atr,c1-c0-1);

printche(s0,c1,style[2],atr,1);

for(s=s0+1;s<s1;s++)

{printche(s,c0,style[3],atr,1);

printche(s,c1,style[4],atr,1);

printche(s1,c0,style[5],atr,1);

printche(s1,c0+1,style[6],atr,c1-c0-1);

printche(s1,c1,style[7],atr,1);

if(title!=NULL)

hor_prn(s0,c0+(c1-c0-strlen(title))/2+1,title,atr_title);

return OK;

 }

else return BAD_PARAM;

 }

 }


Скроллинг и очистка экрана

 Си-функция для скроллинга с помощью ф-ции 6 прерывания

10Н:

#include<dos.h>

#include<screen.h>

void scroll(int direction, char l_row, char l_col, char r_row,

char r_col, char attr)

{union REGS r;

if(direction) {r.h.al=1;r.h.ah=direction;}

else {r.h.al=0;r.h.ah=6}

r.h.ch=l_row; r.h.cl=l_col;r.h.dh=r_row;

r.h.dl=r_col;r.h.bh=attr;int86(0x10h,&r,&r);

}

2.4.2. "Взрывающееся" окно - начиная с определенного места,

например с центра экрана, очищается самое маленькое окно, по его

периметру строится рамка, затем размеры окна увеличиваются и все

повторяется до заданных нормальных размеров.

#define HOR_STEP 2

#define VERT_STEP 1

#define TIME 5

#include<dos.h>

#include<screen.h>

int expl_win(char s0,char c0, char s1, char c1, char color,

char style[], char atr)

{register char max_stolb, max_string;

register int cur_s0,cur_c0,cur_s1,cur_c1;

max_stolb=*(char far*)MK_FP(0x40,0x4a);

max_string=*(char far*)MK_FP(0x40,0x84);

if(s0>=0 && s0 <s1 && c0>=0 && c0<c1 && s1<=max_string && c1 <

max_stolb)

{

 cur_s0=(s1+s0)>>1; cur_c0=(c0+c1)>>1;

cur_s1=cur_s0; cur_c1=cur_c0;

while(cur_c0>c0 || cur_s0>s0 || cur_c1<c1 || cur_s1<s1)

{scroll(ENTIRE,cur_s0,cur_c0,cur_s1,cur_c1,color);

if(style!=NULL)

border(cur_s0,cur_c0,cur_s1,cur_c1,NULL,atr,style,atr);

cur_c0-=HOR_STEP;cur_c1+=HOR_STEP;

cur_s0-=VET_STEP;cur_s1+=VERT_STEP;

cur_c0=(cur_c0<c0)?c0:cur_c0;

cur_c1=(cur_c1>c1)?c1:cur_c1;

cur_s0=(cur_s0<s0)?s0:cur_s0;

cur_s1=(cur_s1>s1)?s1:cur_s1;

delay(TIME);

}

scroll(ENTIRE,cur_s0,cur_c0,cur_s1,cur_c1,color);

if(style!=NULL)

border(cur_s0,cur_c0,cur_s1,cur_c1,NULL,atr,style,atr);

return OK;

}

else return BAD_PARAM;

}


Вывод текста в видеобуфер (Си)

Вывод символа начиная с заданной строки и

столбца с заданным атрибутом

 Внешние переменные:

vid_memory - начальный параграф видеопамяти;

start_adr  -смещение до начала активной страницы;

max_stolb  - максим число столбцов;

max_str    - максим число строк на экране (25,43,80)

#include "screen.h"

int printvb(int str,int stolb, char symb, char atr, int n)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

register unsigned _es*work,word;

if(str>=0 && str<max_str && stolb >=0 && stolb<max_stolb)

{

 work=(unsigned _es*)(start_adr+(max_stolb*str +stolb)*2);

word = (unsigned)(attr<<8) | symb;

_ES=vid_memory;

while(n--) {*work=word; work++;}

return OK;

}

else return BAD_PARAM;


Вывод в окна (Си)

Горизонтальный скроллинг текстового окна с

помощью ф-ции movetext():

#include <conio.h>

#include <screen.h>

int h_scroll(int direction,int l_row, int l_col, int r_row, int

r_col, char *blank_str, char attr)

{

int ret,pos;

swith(direction){

 case LEFT:

ret= movetext(l_col+1,l_row,r_col,r_row,l_col,l_row);

if(!ret) return ret;

pos=r_col;break;

 case RIGHT:

ret= movetext(l_col,l_row,r_col-1,r_row,l_col+1,l_row);

if(!ret) return ret;

pos=l_col;break;

}

vert_prn(--l_row,--pos,blank_str,attr);

}


Чтение информации с экрана

 Си-функция, читающая слово в текущей позиции курсора:

#include <dos.h>

#include <string.h>

#iтclude "screen.h"

char * read_scr(char *sep,int *str,int *stolb)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

 static char buf[81];

 register char index,cur_string,cur_stolb;

 register word;

char _es * ptr;

char *token;

 struct cursor_pos cur_cursor;

save_cur(&cur_cursor);

word=(unsigned)(cur_cursor.reg_14<<8) | cur_cursor.reg_15;

cur_string=word/max_stolb; cur_stolb=word%max_stolb;

*str=cur_string;*stolb=cur_stolb-1;

_ES=vid_memory;

ptr=(char _es*)(start_adr + ((max_stolb*cur_string)<<1));

for(index=0;index<max_stolb;index++)

{

 buf[index]=*ptr;

if(!buf[index]) buf[index]=' '; ptr+=2;

}

buf[max_stolb]='\0';

token=strtok(buf,sep);

do{

 word=FP_OFF(token-buf);

if(word<=cur_stolb && (word + strlen(token))>=cur_stolb)

{*stolb=word;return token;}

}

while((token=strtok(NULL,sep))!=NULL);

return NULL;

}

2.7.2.Си-функция перекрашивания строки либо вертикально либо

горизонтально:

#include <dos.h>

#include "screen.h"

hv_light(int s0,int c0, int n, char atr, int mode)

{

 extern unsigned vid_memory,start_adr, max_stolb, max_str;

 register unsigned _es * work;

 if(s0>=0 && c0>=0 && s0<=max_str && c0<=max_stolb &&

  ((mode==HORIZONTAL && (c0+n-1)<=max_stolb | (mode==VERTUCAL

  && (s0+n-1)<=max_str)))

{

 work=(unsigned _es*) (start_adr +((max_stolb*s0+c0)<<1));

_ES=vid_memory;

while(n--)

{

 _AX=*work; _AH=atr;*work=_AX;

if(mode==HORIZONTAL) work++;

else if(mode==VERTICAL) work+=max_stolb;

else return BAD_PARAM;

}

return OK;

}

else return BAD_PARAM;

}


Вывод графики на экран

 Си-функция вывода пиксела цвета color в точку х,у в

режимах 4 или 5; бит 7 кода цвета определяет выполнемую с кодом

цвета операцию:

void plot45(int x, int y, color)

{

register char _es* offset; /* Смещене от начала видеобуфера*/

register char mask1,       /*2 бита в байте, которые будут

изменены, получаем сдвигом байта С0Н вправо на удвоенный номер

пиксела в байте, который в свою очередь равен остатку от

деления номера столбца на 4*/

mask2,  /*Это байт, который будет комбинироваться с текущим

содержимым видеобуфера work в зависимости от значения 7-го

бита кода цвета - формируется из 2-х сдвинутых влево младших

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

пиксела в байте*/

work,

mod4x; /* Для удвоенного остатка от деления номера столбца на

4*/

 _ES=0xb800;

mod4x = (x & 0x03)<<1;

offset =(char _es) (0x2000 *(y & 0x01)+80*(y>>2));

mask2 = (color & 0x03)<<(6-mod4x);

work=*offset;

if(color & 0x80)  /*Если бит 7 равен 1 выполним XOR*/

*offset =work^mask2;

else             /*Иначе переопределм текущий цвет*/

{

 mask1=0xC0>>mod4x; work=work &~mask1; *offset=work|mask2;

}

}

2.8.2.Модификация  функции 2.8.1. для 6-го режима:

void plot6(int x, int y, color)

{

register char _es* offset;

register char mask1,mask2,work,mod8x;

mod8x = x & 0x07;

offset =0x2000 *(y & 0x01)+80*(y>>1)+(x>>3));

mask2 = (color & 0x01)<<(7-mod4x);

_ES=0xb800;

work=*offset;

if(color & 0x80)  /*Если бит 7 равен 1 выполним XOR*/

*offset =work^mask2;

else             /*Иначе переопределм текущий цвет*/

{

 mask1=0x80>>mod8x; work=work &~mask1; *offset=work|mask2;

}

}

2.8.3. Модификация ф-ции 2.8.1. для режима 13Н:

void plot13(int x, int y, color)

{

register char _es* offset;

offset=320*y+x;

_ES=0xA000;

*offset=color;

}


Алгоритмы сортировки

Введение.

При хранении больших объемов  данных в массивах естественной

задачей в многих приложениях является поиск элемента с заданным

(или ближайшим к нему) значением. Если массив неупорядочен, то

такой поиск сводится к последовательному просмотру всех

элементов массива до встречи с искомым - это связано с большими

потерями времени. Поэтому всегда стремятся осуществлять поиск в

предварительно упорядоченных или отсортированных массивах.

Любой алгоритм сортировки состоит из 3-х основных частей

(вспомогатнльных алгоритмов):

- алгоритм просмотра массива, определяющий затраты времени на

упорядочение;

-алгоритм сравнения элементов массива - он зависит от типа

содержащихся в массиве элементов;

-алгоритм перестановок элементов в массиве - он как правило

тривиален и сводится в общем случае к операциям переноса

элементов массива из одних областей памяти в другие.

Очевидно, что любая подпрограмма сортировки должна получить в

свое распоряжение массив (или его адрес) и количество

находящихся в нем элементов.

1. Пузырьковая сортировка.

Простейшим по методу просмотра и самым неэффективным является

алгоритм так называемой "пузырьковой" сортировки - в этом

алгоритме соседние элементы  массива,начиная с последнего

сравниваются попарно и меняются местами при обнаружении парной

неупорядоченности - таким образом за каждый просмотр массива

наверх "всплывает" очередной самый маленький или самый большой

элемент массива. Сам "верх" при этом последовательно смещается

"вниз".

 Приводится Си-подпрограмма bubble()

пузырьковой сортировки массива вещественных чисел по неубыванию.

void bubble(float a[], int s)

{

int i,j;

float temp;

for(i=0;i<s-1;i++)

   for(j=s-1;j>i;j--)

     if(a[j]<a[j-1])

       temp=a[j],a[j]=a[j-1],a[j-1]=temp;

}

Вам предлагается:

1.1.подготовить файл с подлежащим сортировке массивом, составить

простую программу, которая возьмет из командной строки имя файла

и размер сортируемого массива, разместит этот массив в памяти,

выведет на экран, отсортирует и выведет в отсортированном виде

на экран и в другой файл, имя которого тоже задается в командной

строке ДОС.

1.2. Составить аналоги подпрограммы bubble на Паскале и Бейсике

и повторите п 1.1. для каждого.

2. Быстрая сортировка (Хоор).

Основана на перемещении каждого объекта на его конечную позицию,

т е в такое место массива, что все объекты, расположенные  ниже

будут не больше чем он, а все, лежащие выше - не меньше. После

передвижения элемента на конечную позицию массив делится на 2

части - нижнюю и верхнюю и подпрограмма быстрой сортировки

вызывает саму себя для обработки этих 2-х частей. Первый шаг

декомпозиции быстрой сортировки массива Х выглядит так:

Переместить Х[0] в его конечную позицию.

Отсортировать нижнюю част массива

Отсортировать верхнюю часть массива

Наиболее сложной частью алгоритма является перемещение заданного

(текущего) элемента в конечную позицию. В процессе такого

перемещения массив делим на 3 части: верхнюю с элементами больше

текущего, нижнюю с элементами меньше или равними  ему и среднюю,

еще непросмотренную часть:

- Взять в качестве текущего нижний элемент массива.

  - Указатель Верхний (top) установить на вершину массива

  - Указатель Нижний (bottom) установить в 0 (на последний т е

    текущий)

  -Пока есть непросмотренная средняя часть массива

     - Пока Х[Верхний] > Х[Нижний]

         Верхний присвоить Нижний - 1

     Поменять местами Х[Верхний] и Х[Нижний]

  Пока Х[Нижний] < Х[Верхний]

         Нижний присвоить Нижний +1

     Поменять местами Х[Верхний] и Х[Нижний]

Отсортировать верхнюю часть массива

Отсортировать нижнюю часть массива

Указатели "верхний" и "нижний" движутся навстречу друг другу в

направлении середины массива; при каждом уменьшении указателя

"верхний" или возрастании указателя "нижний" новый элемент

верхней или нижней части сравнивается с текущим. Если новый

элемент слишком мал или соответственно слишком велик, обмен

переносит его в другую часть массива. В момент, когда верхняя

част встречается с нижней текущий элемент оказывается на

конечной позиции.

Приводится Паскаль - подпрограмма qsort  быстрой сортировки

массива действительных чисел, расположенных в массиве с адресом

А.

 type

 RealArr=array[1..20000] of real;

 var

 A:^RealArr;

 procedure qsort(first,last:integer);

 var

 bot,top:integer;

 temp:Real;

 begin

 bot:=first;

 top:=last;

  while bot<top do begin

       while (A^[bot]<=A^[top])and (bot<top) do dec(top);

 {Обмен}

 temp:=A^[top];A^[top]:=A^[bot];A^[bot]:=temp;

       while (A^[bot]<=A^[top]) and (bot<top) do  inc(bot);

 {Обмен}

 temp:=A^[top];A^[top]:=A^[bot];A^[bot]:=temp;

                  end;

  if first< (bot-1) then qsort(first,bot-1);

  if (bot+1) <last  then qsort(bot+1,last);

 end;

Вам предлагается:

2.1. составить  программу, которая возьмет из командной

строки имя файла и размер сортируемого массива, разместит этот

массив в блоке памяти кучи,адрес которого присвоит А,  выведет

на экран, отсортирует и выведет в отсортированном виде на экран

и в другой файл, имя которого тоже задается в командной строке

ДОС.

2.2. Составить аналоги подпрограммы  на C и Бейсике

и повторите п 2.1. для каждого.

3.Пирамидальная сортировка (Дж.Уильямс - Р.Флойд)

Алгоритм базируется на представлении сортируемого массива S в

виде бинарного дерева D, имеющего 1 корневой узел, не имеющий

родителей, имеющий потомков в виде других узлов, имеющих по

1-му родителю и 1-го или 2-х потомков. Узлы нумеруются сверху

вниз по уровням и слеа направо на одном уровне. Индексы

родителей и потомков вычисляются по номеру k узла:

a) для родителей - k/2 , k=1,2,...,n

b) для потомков слева - 2*k если k<=n/2  и 0 если k>n/2

c) для потомков справа 2*k+1 если k<=(n-1)/2 и 0 если k>(n-1)/2

Определение.

Пусть задан массив S с количеством элементов n. Пирамидой

называется непустая последовательность элементов массива вида

   S[p], S[p+1],...,S[q]   (1<=p<=q<=n)

для которой выполнено одно из следующих условий:

: 2*p>q

:2*p=q и S[p]>=S[q]

:2*p<q и S[j]>=S[2] (p<=j<=q/2), S[j]>=S[2*j+1] (p<=j<=(q-1)/2)

Из определения вытекают утверждения:

- Для любого массива S его подмассив от элемента S[n/2+1] до

S[n] является пирамидой.

-Если массив S - пирамида и представлен в виде бинарного

дерева, то в ее вершине расположен наибольший (или наименьший)

элемент, а значение любого узла не меньше значений его левого и

правого потомков. Например последовательность

90,70,11,8,3,9,7,5,6,1,2

является пирамидой.

Алгоритм пирамидальной сортирлвки реализуется в 2 этапа:

а) Построение пирамиды.

"Хвост" или 2-я половина сортируемого массива считается

пирамидой, так как не может иметь потомков. Нарастим этот хвост

слева элементом из 1-й половины массива и преобразуем полученный

подмассив в пирамиду. Делается это так: берем 1-й элемент

подмассива и рассматриваем 2 его потомка; если родитель не

меньше потомков, то делать не надо ничего, т к подмассив уже

пирамида, иначе обмениваем значеения родителя с наибольшим из

его потомков, с "опустившимся" элементом проделываем то же самое

- в конце концов преобразованный подмассив станет пирамидой.

Снова наращивая подмассив слева и повторяя описанные действия,

мы превратим весь массив в пирамиду и в ее вершине окажется

наибольший элемент.

b)Сортировка пирамиды.

В текущей пирамиде первый элемент не меньше остальных. Поменяем

его с последним элементом массива и укротим массив справа на 1

элемент. Полученная последовательность может оказаться уже не

пирамидой, поэтому применим к 1-му злементу описанный в п. a)

процнесс и превратим ее снова в пирамиду. Повторяя этап b) n-1

раз, мы отсортируем массив по невозрастанию.

Пример "ручной" пирамидальной сортировки массива

23,77,12,7,44,82,16,53

Построение пирамиды:

23 77 12  7 44 82 16 53

23 77 12 53 44 82 16 7

23 77 82 53 44 12 16 7

23 77 82 53 44 12 16 7

82 77 23 53 44 12 16 7

Сортировка пирамиды

7  77 23 53 44 12 16 82

16 53 23  7 44 12 77 82

12 44 23  7 16 53 77 82

12 16 23  7 44 53 77 82

7  16 12 23 44 53 77 82

12  7 16 23 44 53 77 82

 7 12 16 23 44 53 77 82

Приводится GW-Бейсик - версия пирамидальной сортировки. Вам

предлагается переписать ее с использованием синтаксиса

QBASIC,составить Си - и Паскаль - подпрограммы по имени

piramida.

180 SCREEN 0: COLOR 15,4: KEY OFF

185 DEFINT N,K,X,Y,J

190 PRINT "ПИРАМИДАЛЬНАЯ СОРТИРОВКА"

200 PRINT "_____________________"

210 INPUT "КОЛИЧЕСТВО ЭЛЕМЕНТОВ";N : DIM S(N)

220 CLS

230 LOCATE 8,9: PRINT "ПРОВОДЯТСЯ ВЫЧИСЛЕНИЯ"

240 GOSUB 430

250 TIME=0:K=N

260 FOR J=N\2 TO 1 STEP -1: X=J:GOSUB 310: NEXT

270 FOR K= N-1 TO 1 STEP -1

280 SWAP S(1),S(K+1): X=1: GOSUB 310

290 NEXT:GOTO 360

310 Y=X+X: ON SGN(Y-K)+2 GOTO 320,330,350

320 IF S(Y)<S(Y+1) THEN Y=Y+1

330 IF S(X) >= S(Y) THEN 350

340 SWAP S(X),S(Y): X=Y: GOTO 310

350 RETURN

360 CLS

370 PRINT "ВРЕМЯ СОРТИРОВКИ"; TIME/50;"СЕК"

380 PRINT "КОЛИЧЕСТВО ЭЛЕМЕНТОВ - ";N: PRINT

390 PRINT "МАССИВ"

400 FOR J=1 TO N : PRINT S(J);:NEXT J

410 END

430 FOR J=1 TO N: S(J)=INT(RND(1)*100):NEXT J

440 RETURN

4.Приводится Си-программа хронометража алгоритмов сортировки

методами пузырька и быстрой сортировки по Хоору. Вам

предлагается подробно откомментировать  ,дополнить ее

составленной в предыдущем пункте подпрограммой пирамидальной

сортировки и сравнить производительность всех 3-х алгоритмов на

массивах различной длины. Затем составьте Паскаль-версию

программы хронометрирования.

#include <stdio.h>

#include <alloc.h>

#define size 6400

float *data;

enum tmetype(begin,end);

static const int max = 32767;

static unsigned h1,h2,m1,m2,s1,s2,hund1,hund2;

static unsigned seed1,seed2;

static int first=1;

main()

{

extern void load_array_sort(int s);

data=(float*) malloc(size*sizeof(float));

load_array_sort(400);

load_array_sort(800);

load_array_sort(1600);

load_array_sort(3200);

load_array_sort(6400);

printf("\n");

}

void load_array_sort(int s)

{

 extern void quick_sort(float *low_ptr, float *high_ptr);

 extern void bubble_sort(int n);

int i;

printf("Быстрая сортировка для %d чисел",s);

for(i=0;i<s;i++) data[i]=rand_real();

rpttiming(begin);

quick_sort(data,data+s-1);

rpttimming(end);

if(s<=800

{

for(i=1;i<s;i++) data[i]=rand_real();

printf("Пузырьковая сортировка для %d чисел",s);

rpttiming(begin);

bubble_sort(s);

rpttiming(end);

}

 }

void gtime(unsigned *hour,unsigned *minute, unsigned sec,

unsigned *hund)

{

 union REGS inreg,outreg;

 inreg.h.ah=0x2C;

 intdos(&inreg,&outreg);

 *hour=outreg.h.dh;

*minute=outreg.h.dl;

*sec=outreg.h.ch;

*hund=outreg.h.cl;

}

void rpttimming(enum timetype p)

{

 if(p==begin) gtime(&h1,&m1,&s1,&hund1);

 if(p==end)

{

gtime(&h2,&m2,&s2,&hund2);

if(hund2<hund1) {hund2+=100;s2-=1;}

if(s2<s1)       {s2+=60;m2-=1;}

if(m2<m1)       {m2+=60; h2-=1;}

if(h2<h1)       h2+=24;

printf("\n\n%d часов,%d минут,%d  секунд, %d сотых \n"

,h2-h1,s2-s1,m2-m1,hund2-hund1); } }

float rand_real(void)

{

 unsigned h,m,s,hu;

 unsigned c;

 float r;

 int index;

if(first)

 {

 gtime(&h,&m,&s,&hu);

seed1=hu+m+2*s;

seed2=h+s*m+s;

seed1*=2; seed2*=2;

if(seed1>max) seed1-=max;

if(seed2>max) seed2-=max;

first=0;

for(index=1;index<=30;index++) r=rand_real();

}

c=seed1+seed2;

if(c>max) c-=max;

c*=2;

if(c>max) c-=max;

seed1=seed2; seed2=c;

return((float)c/32767.0);

}

void quick_sort(float *low_ptr,float *high_ptr)

{

 float *pivot_ptr;

 extern float *part(float *low_ptr,float *high_ptr);

 if(low_ptr<high_ptr)

{

 pivot_ptr=part(low_ptr,high_ptr);

 quick_sort(low_ptr,pivot_ptr-1);

 quick_sort(low_ptr,high_ptr);

}

}

float part(float *low_ptr,float *high_ptr);

{

 extern void swap(float *ptr1,float *ptr2);

 float pivot=*(low_ptr+ (high_ptr-low_ptr)/2);

while(low_ptr<=high_ptr)

{

 while(*low_ptr<pivot) low_ptr++;

 while(*high_ptr>pivot) high_ptr--;

if(low_ptr<=high_ptr) swap(low_ptr++,high_ptr--);

}

return low_ptr;

}

void swap(float *ptr1,float *ptr2)

{

 float temp=ptr1;

*ptr1=*ptr2;*ptr2=temp;

}

void bubble_sort(int s)

{

 int i,j;

for(i=0;i<n-1;i++)

  for(j=n-1;j>1;j--)

   {if(data[j]<data[j-1]) swap(data+j,data+j-1);}

}

5.Обобщенный (родовой) алгоритм сортировки.

 Алгоритмы просмотра сортируемых массивов не зависят от базового

типа сортируемых данных и это дает возможность составления

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

алгоритмы сравнения и вывода зависят от типов данных. Таким

образом обобщенная программа сортировки в качестве

входного параметра должна получать заданную пользователем

подпрограмму срвнения сортируемых элементов. Логика работы

обобщенной сортировки основывается на предположении, что

подпрограмма сравнения - многозначна, заменяма. Обобщенные

программы легко строятся с использованием указателей на функции.

5.1. Приводится Си-программа обобщенной сортировки, которую вам

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

воспроизвести на Пскале. В качестве примера сортируемых данных

рассматривается структура RECORD , содержащая 2 поля - name и

id_number. Тип compare_type

typedef int (* compare_type) (void*,void *);

задает указатель на ф-цию, возвращающую int и имеющую входными

параметрами 2 указателя на void, совместимые как известно с

указателями на любой тип.

В тексте программы описан глобальный массив fruit из элементов

типа RECORD и инициализированы его первые 10 значений. В ф-ции

main описаны 2 ф-ции сравнения:

extern int compare_by_name(void *fruit1,void fruit2);

extern int compare_by_id_number(void *fruit1,void fruit2);

Они обе могут юыть отнесены к типу compare_type, тк имеют такую

же сигнатуру. Первая позволяет упорядочить массив записей по

полю name, а 2-я по полю id_number.

Универсальная ф-ция сортировки

void sort(char *data,int size, int elem_size, compare_type

compare);

имеет 1-м параметром указатель на char - она не знает тип

сортируемых данных и вынуждена при необходимости пересылать

данные побайтно. 2-й параметр дает количество сортируемых

элементов, 3-й - размер элементов в байтах, 4-й определяет

написанную пользователем ф-цию сравнения. В главной программе

вначале массив fruit сортируется по значениям поля name, а затем

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

распечатывается.

/*Файл gensort.c*/

#include<stdio.h>

#include<string.h>

#define RECORD struct fruit_record

RECORD

{char name[20]; int id_number;};

typedef int (* compare_type) (void*,void *);

RECORD fruit[]={

"Apple",5,

"Orange",6,

"Lime",8,

"Apricot",9,

"Pineapple",7,

"Pear",3,

"Grapefruit",4,

"Lemon",1,

"Plum",0,

"Lime",2

}

#define num(sizeof(fruit)/sizeof(fruit[0]))

main()

{

extern int compare_by_name(void *fruit1,void fruit2);

extern int compare_by_id_number(void *fruit1,void fruit2);

extern void sort(char *data,int size, int elem_size,

compare_type compare);

extern void print(RECORD *f,int size);

sort((char*) fruit,num, sizeof(RECORD), compare_by_name);

print(fruit,num);

sort((char*) fruit,num, sizeof(RECORD), compare_by_id_number);

print(fruit,num);

printf("\n");

}

extern void sort(char *data,int size, int elem_size,

compare_type compare);

/*Пузырьковая сортировка*/

{

int i,j,k;

char *offset1,offset2;

char remp;

for(i=0;i<size-1;i++)

for(j=size-1;j>i;j--)

{

offset1=data+j*elem_size;

offset2=offset1-elem_size;

if((*compare)(offset1,offset2)<0)

    for(k=0;k<elem_size;k++)

    {

        temp=offset1[k];

        offset1[k]=offset2[k];

        offset2[k]=temp;

    }

}

}

void print(RECORD *f,int size)

{

int i;

printf("\n\n");

for(i=0;i<size;i++)

printf("\n%s    %d",(f+i)->mame,(f+i)->id_number);

}

int compare_by_name(void *fruit1,void* fruit2)

{return strcmp(((RECORD*) fruit1)->name,

((RECORD *)fruit2)->name);

}

int compare_by_id_number(void *fruit1,void* fruit2)

{return (((RECORD*) fruit1)->id_number-

((RECORD *)fruit2)->id_number);

}


Алгоритмы поиска

Поиск требуемой информации стал одной из наиболее часто

встречающихся задач в повседневной деятельности людей - это

например работа с различного рода справочниками, картотеками,

номенклатурными списками различной продукции, ценниками и пр.

Общая формулировка задач поиска выглядит так:

Есть файл или массив записей, значения одного или нескольких

полей этих записей могут служить ключами для поиска. Сами

поисковые задачи могут содержать в себе:

а)необходимость найти в массиве по крайней мере одну запись,

значение ключевого поля которой равно заданному;

b)найти все записи, соответствующие ключу поиска;

c)при безуспешном поиске добавить в массив новую запись;

d)включить в массив новую запись.

Значение ключевого поля является при этом элементом запроса на

поиск, задачи a) и b) называют простым поиском, задачи c) и d) -

поиском со вставкой. Поиск может осуществляться не по условию

простого совпадения значений ключа и ключевого поля, а по

выполнению некоторых условий для заданной функции от ключей

(например на нахождение значения ключевого поля в заданном

диапазоне); самих ключевых (их еще называют индексируемыми)

полей может юыть несколько - возможны задачи, когда все поля

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

на поиск. Методы поиска могут классифицироваться по различным

признакам в зависимости от способа доступа к элементам массива -

эффективность алгоритмов поиска во многом зависит от организации

исходной информации. Собственно уже в алгоритмах сортировок мы

занимались поиском пар подлежащих обмену элементов.

1. Наиболее популярным и эффективным методом поиска в

последовательно упорядоченных массивах является бинарный

(двоичный) поиск, идея которго состоит в следующем: сначала

значение ключа сравнивается с значением ключевого поля в

элементе массива со средним индексом; реезультат сравнения

позволяет либо получить решение поисковой задачи, либо

определить, в какой части массива - верхней или нижней (или ни

в той ни в другой) следует продолжить поиск. Применение этого

процесса к последовательно укорачивающимся вдвое подмассивам

находим позитивное или негативное решение не более чем за

log2(N) сравнений.

Приводится Бейсик - программа бинарного поиска в упорядоченном

1-мерном массиве. Предлагается переписать ее в синтаксисе QBASIC

, составить и отладить ее Си- и Паскаль - варианты.

40 COLOR 15,1: SCREEN 0: KEY OFF

50 INPUT "КОЛИЧЕСТВО ЭЛЕМЕНТОВ";N: DIM S(N)

60 FOR I= 1 TO N :READ S(I):NEXT

70 DATA 2,7,8,9,13,15,16,17,18,23,24,89

80 INPUT "ВВЕДИТЕ КЛЮЧ ДЛЯ ПОИСКА";K

90 A=1:B=N

100 IF B>=A THEN I=(A+B)\2 ELSE 150

110 IF K<S(I) THEN B=I-1:GOTO 100

120 IF K>S(I) THEN A=I+1:GOTO 100

140 PRINT "I=";I,"S(I)=";K:GOTO 160

150 PRINT "ПОИСК БЕЗ РЕЗУЛЬТАТА"

160 END

2.Поиск в упорядоченных древовидных структурах.

Двоичные деревья относятся к наиболее распространенным

в информатике структурам данных и применяются в задачах

синтаксического анализа, поиска, сортировок, управления базами

данных, в игровых алгоритмах и других приложениях.Каждое

2-ичное дерево состоит из корневого узла и правого и левого

поддеревьев - мы уже изучали их в разделе, посвященном

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

(потомков), то его называют листом. В узлах дерева могут в

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

- мы будем считать, что каждый узел имеет идентифицирующее его

ключевое поле, по которому его можно найти.  Упорядоченное

2-ичное дерево или "дерево поиска" характеризуется тем, что все

узлы, расположенные левее и ниже данного, имеют значение ключа

меньше значения ключа данного узла, а лежащие правее и ниже -

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

поиск в двоичном дереве по заданному ключу простым перемещением

по уровням древовидной структуры. Если дерево относительно

сбалансировано, то число уровней примерно равно 2-ичному

логарифму от числа узлов - например, дерево с 1млн узлов будет

иметь 20 уровней.

Алгоритмы добавления, удаления и поиска элементов в дереве

зависят от количества уровней в дереве.

2.1.Приводится Си-программа для работы с деревом поиска

(Р.Уинер, стр 268).  Вам предлагается самостоятельно разобрать

программу, подробно откомментировать, проверить ее

работоспособность, а затем составвить и отладить ее

Паскаль-аналог.

Вначале в программе определяется структура данных TREE. Каждый

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

которую мы использовали при работе со связным списком;

единственное отличие состоит в том, что добавлены поля left и

right (указатели на левого и правого потомков) и исключено поле

next. В качестве ключа для построения дерева используется поле

last_name.

В подпрограмме display для однократного "посещения" каждого узла

используется рекурсивный алгоритм, который называется

"последовательным обходом", одним из его побочных эффектов

является вывод имен из заданного списка в соответствии со

значением ключевого поля. Работу ф-ции display схематично можно

представить так:

if(root)

{

 display(root->left);

/*Печать информации*/

 display(root->right);

}

Именно условие if(root) заставляет рекурсию возвращаться назад и

выполнять дальнейший просмотр дерева. Когда достигается низ

дерева, значение root->left равно 0 (для узлов - листьев

указатели на левого и правого потомков нулевые). Проанализируйте

работу подпрограммы display и убедитесь, что она выводит список

узлов в алфавитном порядке, основанном на значениях ключей в

поле last_name.

Подпрограмма create всегда создает новый узел, его положение в

дереве определяется по следующим правилам:

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

значением ключа корневого узла;

-если новый ключ меньше корневого, перемещаются к левому

потомку, иначе - к правому;

-сравнения повторяются, причем каждый раз ключ элемента

сопоставляется либо с ключом левого, либо правого потомков;

-при обнаружении совпадения срвнения прекращаются, иначе

продолжаются до достижения низа дерева.

После завершения обхода указатель parent всегда указывает на

узел, лежащий уровнем выше узла, на который указывает current и

задает положение предка текущего узла. Если current = 0, т е

достигнут низ дерева, то вновь создаваемый узел подключается к

родителю либо как левый, либо как правый потомок в зависимости

от значения ключа узла.

Остальные подпрограммы разберите самостоятельно!

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#define STAFF struct stuff_type

STAFF

{

int years_of_service;

float hourly_wage;

};

#define STUDENT struct student_type

STUDENT

{

float grade_pt_average;

int level;

};

#define PROFESSOR struct prof_type

PROFESSOR

{

float annual_salary;

int dept_number;

};

#define NODE_TYPE enum node_type

typedef NODE_TYPE {student,professor,staff};

#define TREE struct tree

TREE

{

char last_name[10];

char first_name[10];

int age;

TREE *left,*right;

NODE_TYPE tag;

union

{

STUDENT student;

PROFESSOR profesor;

STAFF staff;

}node_tag;

};

static TREE* create_node(TREE *item)

{

TREE *node;

node=(TREE *) malloc(sizeof(TREE));

*node=*item;

return node;

}

void destroy(TREE *root)

{

if(root)

{

destroy(root->left);

destroy(root->right);

free(root);

}

root=0;

}

int take_out(TREE **root,TREE *item)

{

TREE * previous=0,

     * present=*root,

     * replace,

     * s,

     *parent;

int found=0;

while(present && !found)

{

if(strcmp(item->last_name,present->last_name)==0)

found=1;

else

{

 previous=present;

if(strcmp(item->last_name,present->last_name)<0)

present=present->left;

else

present=present->right;

}

}

if(found)

{

 if(present->left==0) replace=present->right;

 else

   if(present->right==0)  present=present->left;

else

{

 parent=present;

 replace=present->right;

 s=replace->left;

 while(s!=0)

{

 parent=replace;

 replace=s;

 s=replace->left;

}

if(parent!=present)

{

 parent->left=replace->right;

 replace->right=present->right;

}

replace->left=present->left;

}

if(previous==0) *root=replace;

else

  if(present==previous->left) previous->left=replace;

    else previous->right=replace;

free(present);

}           }

void insert(TREE **root,TREE *item)

{

TREE * current=*root,

parent=0;

TREE * new_node;

int found=0;

while(current && !found)

{

if(strcmp(item->last_name,current->last_name)==0)

found=1;

else

{

 parent=current;

if(strcmp(item->last_name,current->last_name)<0)

current=current->left;

else

current=current->right;

}

}

if(found==0)

{

 if(parent==0) /*Первый узел дерева*/

{

 *root=create_node(item);

 (*root)->left=(*root)->right=0;

}

 else

{

  new_node=create_node;

  new_node->left=new_node->right=0;

if(strcmp(item->last_name,parent->last_name)<0)

parent->left=new_node;

else parent->right=new_node;

}

}

}

void display(TREE *root)

{

if(root)

{

 display(root->left);

printf("\n%s,  %s",roott->last_name,root->first_name);

printf("\n Возраст=%d",root->age);

}

switch(root->tag)

{

case student:

printf("\nСпециализация

: %.2f",root->node_tag.student.grade_pt_average); printf();

printf("\nКурс : %d\n",root->node_tag.student.level);

break;

case professor:

printf("\nНомер кафедры :

%d",root->node_tag.professor.dept_number); printf();

printf("\nОклад

:$%2f\n",root->node_tag.professor.annual_salary);

break;

case staff:

printf("\nСрок службы : %d",

root->node_tag.staff.years_of_service); printf();

printf("\nПочасовый тариф:$%2f\n",

root->node_tag.staff.hourly_wage);

}

display(root->right;

}

}

int is_present(TREE *root,TREE *item)

{

TREE * current=root;

int found=0;

while(current && !found)

{

if(strcmp(item->last_name,current->last_name)==0)

found=1;

else

{

if(strcmp(item->last_name,current->last_name)<0)

current=current->left;

else current=current->right;

}

}

return found;

}

/*Программа тестирования*/

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#include "mytree.h"

TREE *my_tree;

main()

{

TREE *item1=(TREE *) malloc(sizeof(TREE));

TREE *item2=(TREE *) malloc(sizeof(TREE));

TREE *item3=(TREE *) malloc(sizeof(TREE));

strcpy(item1->last_name,"Smith");

strcpy(item1->first_name,"Robert");

item1->age=26;

item1->tag=staff;

item1->node_tag.staff.years_of_service=3;

item1->node_tag.staff.hourly_wage=5.25;

insert(&my_tree,item1);

strcpy(item2->last_name,"Jones");

strcpy(item2->first_name,"Richard");

item2->age=56;

item2->tag=professor;

item2->node_tag.professor.dept_number=7;

item2->node_tag.professor.annual_salary=48321.0;

insert(&my_tree,item2);

strcpy(item3->last_name,"Evans");

strcpy(item3->first_name,"Henry");

item3->age=19;

item3->tag=student;

item3->node_tag.student.level=1;

item3->node_tag.student.grade_pt_average=3.1;

insert(&my_tree,item3);

display(my_tree);

if(is_present(&my_tree,item1))

printf(""\nЭлемент 1 присутствует   \n);

else

printf(""\nЭлемент 1 отсутствует   \n);

take_out(&my_tree,item3);

display(my_tree);

take_out(&my_tree,item2);

display(my_tree);

take_out(&my_tree,item1);

display(my_tree);

take_out(&my_tree,item3);

display(my_tree);

printf("\n");

}

3. Использование 2-ичных деревьев поиска.

Приводится Си-программа для подсчета частоты появления каждого

слова в произвольном текстовом файле и ваша задача состоит в

разработке ее Паскаль - аналога.

  Чтобы освоить метод декомпозиции, Си-программа разделена на

ряд модулей:

word_fq.c - главный модуль, который читает файл ввода и пишет в

файл вывода таблицу частоты слов.

word_ut.h - интерфейс с модулем word_ut.c, содержащий прототипы

ф-ции string_save для выделения памяти и размещения в ней

считанной строки и get_word для выделения очередного слова

(слова разделяются небуквенными символами)

word_ut.c - реализация ф-ций string_save, get_word;

tree.h   -определение 2-ичного дерева, каждый узел которого

имеет поля word (слово), freq (счетчик появлений слова),

left,right. Здесь же прототипы ф-ций create_tree, display

(строит частотную таблицу слов).

tree.c - релизация create_tree, display

/*Файл word_ut.h*/

#define MAXSIZE 50

extern char *string_save(char *s);

extern get_word(char*w);

/*Файл word_ut.c*/

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#include<ctype.h>

#include "word_ut.h"

char *string_save(char *s)

{

char *p; /*для указателя на область памяти выделяемую в

         string_save*/

if((p=malloc(strlen(s)+1))!=NULL) strcpy(p,s); return(p);

}

int get_word(char *w)

{

int ch, count=0;

/*С помощью макро isalpha пропускаем все несимвольные коды;

следует учитывать, что библиотечный вариант этого макро работает

только с латиницей - составьте свой вариант для обслуживания и

кириллицы тоже */

while(!isalpha(ch=getchar()) && ch!=EOF);

if(ch==EOF) return 0;

else

{

*w++=tolower(ch); /*Напишите вариант этой ф-ции и для

                   кириллицы*/

count++;

while(!isalpha(ch=getchar()) && ch!=EOF)

if(++count<MAXSIZE) *w++=tolower(ch);

*w='\0'; return 1;

}

}

/*Файл tree.h*/

#define NODE stuct tree_node

NODE

{

 char *word;

 unsigned freq;

 NODE *left;

 NODE *right;

};

NODE *create_tree();

void display(NODE *p);

/*Файл tree.c*/

#include<string.h>

#include<stdio.h>

#include<alloc.h>

#include "tree.h"

#include "word_ut.h"

static NODE *alloc_node()

{

 return ((NODE *) malloc(sizeof(NODE)));

}

NODE *create_tree()

{

extern char *string_save();

extern NODE *alloc_node();

NODE *current, *previous, *root=0;

char w[MAXSIZE+1];

int distinct;

while(get_word(w))

{

if(root==0)

{

root=alloc_node();

root->word=string_save(w);

root->left=root->right=0;

root->freq=1;

distinct=0;

}

else

{

previous=0;

current=root;

distinct=1;

while(current!=0)

{

if(strcmp(w,current->word)<0) current=current->left;

else if(strcmp(w,current->word)>0) current=current->right;

else /*w уже есть в дереве*/

{current->freq++;distinct=0;break;}

}

}

if(distinct)

{

current=alloc_node();

current->word=string_save(w);

current->left=current->right=0;

current->freq=1;

if(strcmp(w,previous->word)<0) previous->left=current;

else previous->right=current;

}

}

}

return root;

}

void display(NODE *p)

{

if(p!=0)

{

 display(p->left);

 printf("\n%-40s %-6d",p->word,p->freq);

 display(p->right);

}

}

/*Главная управляющая программа.Файл word_fq.c.

Вызов: word_fq <infile >outfile*/

#include<stdio.h>

#include "tree.h"

main()

{

 NODE *root;

 root=create_tree();

 display(root);

 printf("\n");

}


Вычисление корней многочленов

1. Методы Ньютона и секущих.

 Полином (многочлен) комплексного переменного z с комплексными

 коэффициентами Ак (k=0,1,..,n)

 f(z) = A0*z^n + A1*z^(n-1) +...+An  (A0 не равен 0)

в соответствии с основной теоремой алгебры имеет n корней

(нулей), определение которых аналитическими методами - трудная

задача; применение ориентированных на применение ЭВМ

алгоритмических методов значительно ее упрощает.

По методу Ньютона нуль ф-ции f(z) находят исходя из начального

приближения z0 как предел последовательности комплексных чисел

zk:

z(k+1) = zk - f(zk)/f'(zk)      (k=0,1,2,...) и f'(zk)!=0

Метод секущих аналогичен, только вместо производной использует

ее приближенное значение, выраженное через отношение приращений

функции и аргумента:

z(k+1) = zk - f(zk)/((f(zk)-f(z(k-1)))/(zk-z(k-1)))

Итерации по приведенным формулам прекращают, когда при очередном

значении zk абсолютное значение ф-ции f(zk) с достаточной

точностью приблизилось к 0-му значению: |f(zk) < e, где е -

заданное малое положительное число.При этом считают, что найден

1-й корень , приближенно равный zk , понижают степень полинома

f(z) = f(z)/(z-zk)

и все повторяют для нахождения других корней.

Приводится MSX-Бейсик - программа, реализующая комплексную

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

зависимости от пожеланий пользователя, вводимых в диалоге.

Вам необходимо переписать ее в синтаксисе QBASIC с исключением

операторов GOTO и GOSUB , проверить работоспособность, а затем

составить и проверить Си- и Паскаль - аналоги. Для проверки

работоспособности найдите корни многочлена

z^4-4*z^3+5*z^2-4*z+4

и

z^3-z^2-iz+i

SCREEN 0: COLOR 15: KEY OFF: E2=1E-05

A$="##)#####.######  +#####.#######*i"

D$="3.ВЕЩЕСТВЕННЫЕ ЧАСТИ КОЭФФИЦИЕНТОВ:

E$="4.КОМПЛЕКСНЫЕ ЧАСТИ КОЭФФИЦИЕНТОВ:

F$="3.КОЭФФИЦИЕНТЫ:

PRINT "КОРНИ МНОГОЧЛЕНОВ" : PRINT

INPUT "1.СТЕПЕНЬ МНОГОЧЛЕНА";N0

R=N0+1: DIM C(R),D(R),A(R),B(R)

PRINT "2.ТИП КОЭФФИЦИЕНТОВ:"

PRINT "ВЕЩЕСТВЕННЫЕ - 1"

INPUT "КОМПЛЕКСНЫЕ -  2";T:PRINT

IF T=1 THEN PRINT F$ ELSE PRINT D$

FOR K=0 TO N0

PRINT "  A(";K;") = ";:INPUT C(K)

NEXT

IF T=2 THEN PRINT E$ ELSE 310

FOR K=0 TO N0

PRINT "  B(";K;") = ";:INPUT D(K)

NEXT

310 PRINT

IF T=1 THEN PRINT "4."; ELSE PRINT "5.";

INPUT "ТОЧНОСТЬ ВЫЧИСЛЕНИЙ "; E1: E=E1^2

340 PRINT :PRINT"ВОЗМОЖНЫЕ МЕТОДЫ:"

PRINT:PRINT "НЬЮТОНА---1"

PRINT:PRINT"СЕКУЩИХХ---2"

INPUT "ВЫХОД        ---3";O:CLS

LOCATE 8,0: PRINT "ИДЕТ ВЫЧИСЛЕНИЕ"

PRINT : LOCATE 3,4: ON GOTO 400,410,920

400 PRINT "МЕТОД НЬЮТОНА": GOTO 420

410 PRINT "МЕТОД СЕКУЩИХ"

420 N=N0:W=1

FOR S=0 TO N: A(S)=C(S): B(S)=D(S):NEXT

LOCATE 3,6: PRINT "КОРНИ МНОГОЧЛЕНА:"

PRINT :T=TIME: IF O=2 THEN 560

460 GOSUB 860

'МЕТОД НЬЮТОНА

470 GOSUB 760:R=C^2+D^2

IF R<E THEN GOSUB 870 :GOTO 920

GOSUB 720: L=(A*C+B*D)/R: M=(B*C-A*D)/R

IF ABS(L) + ABS(M) <E1 THEN 520

X=X-L:Y=Y-M: GOTO 470

520 PRINTUSING A$;W,X,Y

N=N-1: W=W+1: GOSUB 820

IF N>0 THEN 460 ELSE GOSUB 890: GOTO 340

'МЕТОД СЕКУЩИХ

560 U=0: V=0; C= A(N)

570 GOSUB 860

580 GOSUB 720

R=L1^2+M1^2

IF R<E THEN GOSUB 870: GOTO 920

L=(A*L1+B*M1)/R

M=(B*L1-A*M1)/R:R=ABS(L)+ABS(M)

IF R<E1 THEN 670

U=X:V=Y:C=A:D=B:X=X-L:Y=Y-M:GOTO 580

670 PRINTUSING A$;W,X,Y

N=N-1:W=W+1:GOSUB 820

IF N>0 THEN 560 ELSE GOSUB 890: GOTO 340

'ПОДПРГРАММЫ

'ВЕЩЕСТВ И МНИМ ЧАСТИ F

720 A=A(0): B=B(0)

FOR K=1 TO N

L=A*X-B*Y+A(K):B=B*X=A*Y+B(K):A=L

NEXT:RETURN

760 C=A(0)*N: D=B(0)*N: IF N=1 THEN 810

FOR K=1 TO N-1

L=C*X-D*Y+(N-K)*A(K)

D=D*X+C*Y+(N-K)*B(K):C=L

NEXT:

810 RETURN

'ПОНИЖЕНИЕ СТЕПЕНИ F

820 FOR K=1 TO N: L=K-1

A(K)=A(K)+A(L)*X-B(L)*Y

B(K)=B(K)+B(L)*X+A(L)*Y

NEXT:RETURN

860 X=RND(-TIME)*3:Y=RND(-TIME)*3: RETURN

870 PRINT "ПРИБЛИЖЕНИЕ К КРАТНОМУ КОРНЮ"

PRINT : PRINTUSING A$;W,X,Y:RETURN:

890 PRINT

900 PRINT "ВРЕМЯ СЧЕТА = ";(TIME-T)/50;"СЕК": RETURN

920 END


Решение СЛАУ методом Гаусса

Для решения СЛАУ применяют два класса методов:прямые

и итерационные.  Прямые методы являются универсальными,но

из-за большого объема вы- числительных процедур применяются

для решения систем сравнительно невысокого порядка(n < 200).

Итерационные методы рекомендуются для СЛАУ высоких порядков.

Общая формулировка задачи:  Необходимо решить СЛАУ

а11*x1 + a12*x2 +....+ a1n*xn = a1,n+1

a21*x1 + a22*x2+....+ a2n*xn = a2,n+1     (1)

 ......................................

an1*x1 + an2*x2 +....+ ann*xn = an,n+1

где xk -неизвестные,a(ij)-заданные  коэффициенты.

 В матричной записи:  [A]x=y     (2)

Общее решение находится просто:домножим обе части матричного

уравнения (2) слева на обратную матрицу [Aоб]:

                  [Aоб][A]x=[Aоб]y               (3)

                   x = [Aоб]y                     (4)

Т.о. задача сводится к обращению матрицы [A].

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

обращения матриц - обычно стараются найти обходные пути.  По

существу решить СЛАУ - значит привести матрицу коэффициентов [A]

(обрабатывая ее с помощью линейных процедур вместе со столбцом

свободных членов) к диагональной (т е сделать в каждом уравнении

все коэффициенты кроме одного нулевыми - исклчить) и далее к

единичной матрице - в этом случае столбец свободных членов

преобразуется в искомое решение, т к система уравнений

приведется к совокупности взаимено независимых равенств:

после приведения к диагональной форме:

С1*X1 = B1; C2*X2= B2;...Cn*Xn=Bn;

после приведения к единичной:

x1=D1; x2=D2;....xn=Dn.

Простейший способ последовательного преобразования матриц

коэффициентов сначала к треугольной форме, затем к диагональной

является метод последовательного исключения Гаусса, основанный

на том, что в парах уравнений  коэффициенты при одном из иксов

делаются равными, а затем одно уравнение почленно вычитается из

другого, исключая соответствующий член. Повторение этой

процедуры для какого - либо уравнения по очереди с каждым другим

уравнением системы приводит к уравнениюс одной неизвестной. При

этом приходится осуществлять операцию деления на диагональные

элементы матрицы коэффициентов и если делитель окажется

нулевым, мы не сможем выполнить задуманное. Поэтому

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

образом, чтобы по главной диагонали располагались как можно

большие по абсолютной величине элементы и называют это выбором

главных элементов.

Метод Гусса относится к прямым методам. Алгоритм состоит из двух

этапов.  На первом выполняются последовательно следующие

процедуры:

-последовательно для каждой i-й строки из всех последующих стpок

выбираются уравнения с наибольшим  по модулю коэффициентом i-м

столбце: эта пpоцедуpа называется выбоpом главного элемента;

-ноpмиpование уpавнений по коэффициентам пpи x(i,i) путем

деления всех ко- эффициентов уpавнения на а(ii);

-исключение i-й пеpеменной из всех уpавнений ,начиная с

i+1-го,т.е. пpиведение матpицы коэффцентов к верхней

тpеугольной фоpме;

  На втором этапе выполняется:

-подстановка x(n) в n-1 -е уравнение,x(n-1) - в n-2-е и т.д.,выполняя т.н.

обратный ход алгоритма Гаусса с приведение матрицы к

диагональной форме

  СОДЕРЖАНИЕ РАБОТЫ

Приводится набор Паскаль-процедур,позволяющих реализовать изуча-

емый алгоритм решения задачи:

 -2 процедуры инициализации матриц-с помощью генератора

 случайных чисел (InitMatrRand)  и с помощью клавиатурного

 ввода(InitMatrKey);

 -дублирования матрицы в массив "b" для обеспечения последующей

 проверки полученного решения подстановкой в исходное

 уравнение(Dubl);

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

 элемента(SortMatr);

 - деления всех элементов строк на диагональные коэффициенты

 (нормирование) и исключения элементов из последующих стpок,

 т.е. фоpмиpование тpеугольной матpицы(TryMatr);

 - обратный ход алгоритма Гаусса;

 - проверки решения подстановкой полученного вектора х в

  матрицу-дубль(если она была создана заранее)-Check;

 - вывода на экран матрицы и вектора, заданных параметрами

 соответствующих процедур,для визуального наблюдения и анализа

 результатов(PrintMatr и PrintVect);

 -преобразования матрицы к диагональной форме(DiagMatr);

 НЕОХОДИМО:

 -"Собрать" программу из имеющегося набора процедур

 -Выполнить прогон программы при различных размерностях матрицы

  коэффициентов ирпи различных способах инициализации.

- Составить и испытать аналог программы при условии, что

матрица коэффициентов находится в файле, а его имя и размерность

матрицы вводятся в командной строке.

- Составить и испытать Си- аналог программы

 Для начального тестирования программы в основном блоке приведен

 пример прямой инициализации матрицы с известным заранее

 решением.  }

  uses Crt;

  const n=3;m=4;{n-колич.уpавнений,m-pазм-сть вектоpа незав

                пеpеменных}

type

 matr=array[1..n,1..m] of real;

vect=array[1..n] of real;

var a,b:matr;

delta,x,y:vect;

k,l,i,j:integer;

w,v,maxabs,p, maxs:real;

            {Инициализация матpицы случайными числами}

procedure InitMatrRand(var mt:matr);

begin

randomize;

for i:=1 to n do

      for j:=1 to n+1 do

      mt[i,j]:=5.*random-2.5;

for i:=1 to n do

      begin

      maxs:=abs(mt[i,1]);

      for j:=1 to n+1 do

      begin

      if abs(mt[i,j])>maxs then maxs:=abs(mt[i,j]);

      end;

      mt[i,i]:=5.0*maxs;

      if a[i,i]=0 then

      mt[i,i]:=5.0;

      end;

randomize;

for i:=1 to n do

  for j:=1 to n+1 do

    begin

if i=j then

begin

p:=random-0.5;if p=0 then p:=1;

 mt[i,j]:=mt[i,j]*p/abs(p);

end

    end;

end;{InitMatr}

             {Инициализация матpицы с клавиатуpы}

Procedure InitMatrKey(var ky:matr);

begin

for i:=1 to n do

for j:=1 to n+1 do begin

write ('A[',i:2,j:2,']?'); readln(ky[i,j]);

                   end

end;{InitMatrKey}

                  {Дублиpование матpицы}

Procedure DublMatr(c:matr;var d:matr);

begin

for i:=1 to n do

  for j:=1 to n+1 do

  d[i,j]:=c[i,j];

end;{DublMatr}

                   {Пеpестановка стpок матpицы}

Procedure SortMatr(var mt:matr);

begin

for i:=1 to n do begin

maxabs:=abs(mt[i,i]);k:=i;

for l:=i+1 to n do

  if abs(mt[l,i])>maxabs then begin maxabs:=abs(mt[l,i]);k:=l;end;

  if k<>i then

  for j:=i to n+1 do  begin v:=mt[i,j];mt[i,j]:=mt[k,j]; mt[k,j]:=v;end

                 end

end;{SortMatr}

             {Деление элементов матицы на м[i,i] и

             исключение i-x элементов из последующих стpок

                т.е. фоpмиpование тpеугольной матpицы}

Procedure TryMatr(var md:matr);

begin

   for i:=1 to n do

    begin v:=md[i,i];

     for j:=i to n+1 do

     md[i,j]:=md[i,j]/v;

     for l:=i+1 to n do begin

     w:=md[l,i];

     for j:=1 to n+1 do

     md[l,j]:=md[l,j]-md[i,j]*w

                        end

    end

end;{TryvMatr}

             {Фоpмиpование диагональной матpицы}

Procedure DiagMatr(var dm:matr);

begin

for i:=1 to n do begin

v:=dm[i,i];

 if v=0 then exit;

{  for j:=1 to n+1 do dm[i,j]:=dm[i,j]/v;}

    for l:=1 to n  do begin

       if l<>i then begin

           w:=dm[l,i];

           for j:=1 to n+1 do begin

{           dm[l,j]:=dm[l,j]-dm[i,j]*w}

            dm[l,l]:=dm[l,l]-(dm[i,l]/dm[i,i])*dm[l,i];

            dm[l,n+1]:=dm[l,n+1]-(dm[i,n+1]/dm[i,i])*dm[l,i]

                              end;

                              for i:=1 to n do dm[i,n+1]:=dm[i,n+1]/dm[i,i]

                     end

                     end

                     end;

for i:=1 to n do

x[i]:=dm[i,n+1];

end;{DiagMatr}

             {Обpатный ход Гаусса}

Procedure Down(g:matr);

begin

for i:=1 to n do x[i]:=g[i,n+1];

for i:=n-1 downto 1 do

        for j:=i+1 to n do

        x[i]:=x[i]-g[i,j]*x[j]

end;{Down}

               {Пpовеpка  пpавильности pешения}

Procedure Check(k:matr;xc:vect;var delta:vect);

begin

for i:=1 to n do delta[i]:=k[i,n+1];

for i:=1 to n do

    for j:=1 to n do

    delta[i]:=delta[i]-k[i,j]*xc[j]

end;{Check}

                        {Печать матpицы}

procedure PrintMatr(c:matr);

begin

      for i:=1 to n do

      begin

      writeln;

      for j:=1 to n+1 do

      write(c[i,j]:10:5);

      end;

      writeln

end;{PrintMatr}

                        {Печать вектоpа}

Procedure PrintVect(c:vect);

begin

      for i:=1 to n do

      writeln(c[i]:8:5);

end;{PrintVect}

                     {Основной блок пpогpаммы}

begin

{a[1,1]:=3; a[1,2]:=-1;a[1,3]:=0;a[1,4]:=5;

a[2,1]:=-2;a[2,2]:=1;a[2,3]:=1;a[2,4]:=0;

a[3,1]:=2;a[3,2]:=-1;a[3,3]:=4; a[3,4]:=15;}

{Решение-2,1,3}

InitMatrRand(a);

{InitMatrKey(a);}

DublMatr(a,b);

SortMatr(a);

{TryMatr(a);}

{Down(a);}

DiagMatr(a);

Check(b,x,delta);

PrintMatr(a);

PrintVect(x);

PrintVect(delta);

PrintMatr(b);

end.


Решение СЛАУ методом ортогонализации

 Метод более корректен, чем метод Гаусса - в нем не

 осуществляется деление на диагональные элементы матрицы и он

 неплохо работает при плохо определенных матрицах, что

 обусловило его широкое использование в прикладных задачах.В

 качестве вспомогательного средства метод используется в

 других вычислительных алгоритмах например в задачах приближения

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

 СУЩНОСТЬ МЕТОДА Перенесем  свободные члены в левую часть

 уравнений и положим Xn+1=1 Получим систему в виде

       n+1

        S a(i,j)*x(j)=0  где i-из мн-ва 1,2...n

       j=1

 Суммы в левых

 частях уравнений можно интерпретировать как

 скалярные произведения векторов  a(i)  и х, заданных

 проекциями на координатные оси; в этом случае искомым решением

 системы будет некоторый вектор Х в n+1-мерном

 пространстве,ортогональный базису, образованному системными

 векторами а(i).Так как сам базис в общем случае не

 ортонормирован,то необходима дополнительная процедура его

 ортогонализации.  Для поиска решения системы добавим к

 векторам a(i) линейно независимый от них вектор

a(n+1) = (0,0,....,0,1).

В векторном пространстве размерности n+1 будем строить  его

ортонормированный базис.Для поворота  какого- либо вектора в

пространстве на углы,обеспечивающие его ортогональность

некоторому ортонормированному базису векторов достаочно вычесть

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

проекциям ортогонализируемого вектора на эти орты (то есть

скалярным произведениям вектора и соответствующих орт).  Таким

образом алгоритм вычислений решения СЛАУ методом ортогонализации

состоит из следующих процедур:

-нормируем вектор a(1) по модулю делением его компонент на

 корень квадратный из суммы квадратов этих компонент;

-начиная со второго вектора и до n+1-го последователно

осуществим ортогонализацию с предыдущими и затем нормирование по

модулю, повторив эти процедуру несколько раз (3-5) для

предотвращения возможной неустойчивости процесса ортогонализации

и повышения точности вычислений в случаях   близких к нулю

значений определителя исходной системы.

-последнюю строку расширенной матрицы раpделим на элемент

a(n+1,n+1), который должен по определению быть равен 1,получив

таким образом искомое решение в n первых элементах.

СОДЕРЖАНИЕ РАБОТЫ

Предлагается  набор Паскаль-процедур,позволяющих реализовать

изучаемый алгоритм решения задачи:

-2 процедуры инициализации матриц-с помощью генератора случайных

чисел (InitMatrRand)  и с помощью клавиатурного

ввода(InitMatrKey);

-процедура нормализации вектора по модулю(Norm);

-процедура ортогонализации вектора с предыдущим

ортонормированным базисом и нормализаци вызовом процедуры  Norm

(Orto).

- вывода на экран матрицы визуального наблюдения и анализа результатов

(PrintMatr);

  НЕОХОДИМО:

 -"Собрать" программу из имеющегося набора процедур

 -Выполнить прогон программы при различных размерностях матрицы

 коэффициентов и пpи различных способах инициализации.

 -проверить влияние повторов-итераций процедур ортогонализации

 на точность получаемого решения

-дополнить программу фрагментами , обеспечивающими обработку

матриц, размещенных в файле, имя которого и размеры находящейся

в нем матрицы задаются в командной строке.

Для начального тестирования программы в основном блоке приведен

пример прямой инициализации матрицы с известным заранее

решением.

const

n=3;m=4;{n-колич.уpавнений,m-pазм-сть вектоpа незав пеpеменных}

type

  matr=array[1..m,1..m] of real;

  vect=array[1..m] of real;

var a:matr;

    c:vect;

    s:real;

    i,j,k,l,p,q,w:integer;

        {Инициализация матpицы случайными числами}

procedure InitMatrRand(var mt:matr);

var

p,maxs:real;

begin

randomize;

for i:=1 to n do

      for j:=1 to n+1 do

      mt[i,j]:=5.*random-2.5;

for i:=1 to n do

      begin

      maxs:=abs(mt[i,1]);

      for j:=1 to n+1 do

      begin

      if abs(mt[i,j])>maxs then maxs:=abs(mt[i,j]);

      end;

      mt[i,i]:=5.0*maxs;

      if a[i,i]=0 then

      mt[i,i]:=5.0;

      end;

randomize;

for i:=1 to n do

  for j:=1 to n+1 do

    begin

if i=j then

begin

p:=random-0.5;if p=0 then p:=1;

mt[i,j]:=mt[i,j]*p/abs(p);

end

    end;

for j:=1 to n do mt[n+1,j]:=0;

mt[n+1,n+1]:=1;

end;{InitMatr}

             {Инициализация матpицы с клавиатуpы}

Procedure InitMatrKey(var ky:matr);

begin

for i:=1 to n do

for j:=1 to n+1 do begin

write ('A[',i:2,j:2,']?'); readln(ky[i,j]);

                   end

end;{InitMatrKey}

    {Нормализация по модулю вектора nv в матрице m}

Procedure Norm(nv:integer;var m:matr);

begin

  s:=0;

  for j:=1 to n+1 do

  s:=s+m[nv,j]*m[nv,j];

  s:=sqrt(s);

  for j:=1 to n+1 do

  m[nv,j]:=m[nv,j]/s;

end;{Norm}

     {Ортогонализация вектора относительно всех предыдущих}

Procedure Orto(nv:integer;var m:matr);

begin

  for p:=1 to 5 do     begin

  for i:=1 to nv-1 do  begin

  c[i]:=0;

  for j:=1 to n+1 do

  c[i]:=c[i]+m[i,j]*m[nv,j];

                       end;

  for k:=1 to n+1 do   begin

  s:=0;

  for l:=1 to nv-1 do

  s:=s+c[l]+m[l,k];

  m[nv,k]:=m[nv,k]-s;

                       end

                       end;

  Norm(nv,m);

end;{Orto}

                        {Печать матpицы}

procedure PrintMatr(c:matr);

begin

      for i:=1 to n+1 do

      begin

      writeln;

      for j:=1 to n+1 do

      write(c[i,j]:10:5);

      end;

      writeln

end;{PrintMatr}

                        {Печать вектоpа}

Procedure PrintVect(c:vect);

begin

      for i:=1 to n do

      writeln(c[i]:8:5);

end;{PrintVect}

begin

 {Матрица-тест}

{a[1,1]:=1;a[1,2]:=1;a[1,3]:=1;a[1,4]:=-3;

a[2,1]:=2;a[2,2]:=1;a[2,3]:=1;a[2,4]:=-4;

a[3,1]:=1;a[3,2]:=3;a[3,3]:=1;a[3,4]:=-5;

a[4,1]:=0;a[4,2]:=0;a[4,3]:=0;a[4,4]:=1;}

{Решение по тесту:1,1,1}

InitMatrRand(a);

{InitMatrKey(a);}

Norm(1,a);

for w:=2to n+1 do

Orto(w,a);

for q:=1 to n+1 do

a[n+1,q]:=a[n+1,q]/a[n+1,n+1];

PrintMatr(a);

{PrintVect(a);}

end.


Решение нелинейных уравнений

Задание на работу

В соответствии с вашим вариантом составьте программу решения

заданного в таблице нелинейного уравнения (уточнения корня в

интервале  [c,d] с  погрешностью  менее 0.0001), коэффициенты

которого R,m,n получите в результате решения системы линейных

уравнений:

 a11* R + a12*  m + a13*  n = b1

 a21* R + a22*  m + a23*  n = b2

 a31* R + a32*  m + a33*  n = b3

Во вспомогательных алгоритмах раскройте способ решения

соответствующих вспомогательных задач

+------------------------------------------------------------+

¦    ¦                           ¦ a11,a12,a13,b1   ¦        ¦

¦ N  ¦            f(x)           ¦ a21,a22,a23,b2   ¦ (c;d)  ¦

¦    ¦                           ¦ a31,a32,a33,b3   ¦        ¦

¦    ¦                           ¦                  ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 1 ; 5 ;-4 ;  3   ¦        ¦

¦ 1  ¦ R-(x-m)^2-n-m/(x-m)=0     ¦-1 ;-2 ; 4 ;  0   ¦(-1;0)  ¦

¦    ¦                           ¦ 2 ; 1 ; 6 ; 18   ¦        ¦

¦----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 1 ;-3 ; 1 ; 2,5  ¦        ¦

¦ 2  ¦ (R-x^2)^1/2-m+n ¦x-1¦=0   ¦ 2 ; 3 ; 2 ; 32   ¦(0;1)   ¦

¦    ¦                           ¦-1 ;-2 ; 1 ;-12,5 ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 4 ;-2 ; 1 ; 7,5  ¦        ¦

¦ 3  ¦ R(1-x^2)1/2-m-(x-n)^2=0   ¦ 3 ; 2 ;-2 ; 6    ¦(2;3)   ¦

¦    ¦                           ¦ 2 ;-1 ; 1 ; 4    ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 1 ;-2 ; 2 ; 1    ¦        ¦

¦    ¦                           ¦ 2 ; 2 ;-4 ;-1    ¦(0,1;2) ¦

¦ 4  ¦ R/x+m-(x+n)^2=0           ¦ 3 ;-1 ; 1 ; 3    ¦        ¦

¦----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 1 ; 4 ; 3 ; 13   ¦        ¦

¦    ¦                           ¦-2 ;-5 ; 2 ;-12   ¦(0;3)   ¦

¦ 5  ¦ R-(¦x¦-m)^2-n/(x+2,4)=0   ¦ 4 ; 1 ; 5 ; 15   ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 1 ; 4 ; 3 ; 10   ¦        ¦

¦    ¦                           ¦ 2 ; 5 ; 2 ; 13   ¦(0,5;3) ¦

¦ 6  ¦ R-(x-m)^2-(n*x^2)^(1/3)=0 ¦-3 ; 6 ; 1 ;-2    ¦        ¦

¦----+---------------------------+------------------+--------¦

¦    ¦                           ¦-2 ; 1 ; 5 ; 50   ¦        ¦

¦    ¦                           ¦-4 ; 2 ;-1 ; 56   ¦(0;P/4) ¦

¦ 7  ¦ 1/R(m-n(x+2)^2)^1/2-sinx= ¦ 5 ; 2 ;-10; 47   ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 3 ;-1 ; 2 ; 33,75¦        ¦

¦    ¦                           ¦ 2 ; 1 ; 4 ; 30,5 ¦(P/2;P) ¦

¦ 8  ¦ (R(x-m)^2)^1/2*sinx-n=0   ¦ 1 ;-3 ;-0,5 ; 0  ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦10 ; 2 ; 1 ; 58   ¦        ¦

¦    ¦                           ¦20 ; 1 ;-2 ; 25   ¦(0;1,5) ¦

¦ 9  ¦ R-(m-(x-n)^2)^1/2*cosx-1= ¦-10; 2 ;-4 ; 30   ¦        ¦

¦----+---------------------------+------------------+--------¦

¦    ¦                           ¦ 5 ; 2 ; 3 ; 9    ¦        ¦

¦    ¦                           ¦10 ; 1 ;-1 ; 1    ¦(0,2)   ¦

¦ 10 ¦ arcsin(R*x)-m+(¦x¦-n)^2=0 ¦20 ; 4 ;-1 ; 6    ¦        ¦

+----+---------------------------+------------------+--------¦

¦    ¦                           ¦10 ;-1 ; 4 ; 2    ¦        ¦

¦  ¦                               ¦ 5 ; 4 ; 2 ; 10   ¦(0;1,5) ¦

¦11¦arccos(R*n)-m(1-(x-n)^2)^1/2=0 ¦20 ; 2 ; 2 ; 9    ¦        ¦

¦--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 1 ; 3 ;0,5; 6,5  ¦        ¦

¦  ¦                               ¦ 2 ;-6 ; 1 ;-5    ¦(-1;1)  ¦

¦12¦R+m*n-n*x^2=0                  ¦ 4 ; 2 ;-2 ; 3    ¦        ¦

+--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 1 ; 5 ; 3 ; 24,5 ¦        ¦

¦  ¦                               ¦ 2 ; 4 ; 1 ; 22,5 ¦(3,02;4)¦

¦13¦R(1-(x-m)^2)^1/2-n(x^2-m)^1/2= ¦ 3 ; 2 ; 2 ; 18   ¦        ¦

+--------------------------------------------------------------+

+--------------------------------------------------------------+

¦  ¦                               ¦ 5 ;-20;-5 ; 55   ¦        ¦

¦14¦(R-(x-1)^2)^1/2-m+1/(¦x¦-n)=0  ¦ 2 ;-16;-8 ; 8    ¦(-2,98; ¦

¦  ¦                               ¦ 1 ; 3 ; 1 ; 20   ¦  0)    ¦

¦--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 5 ;-5 ; 5 ; 15   ¦(-П;    ¦

¦15¦R*sinx+m+m/(x-n)=0             ¦ 1 ; 3 ; 4 ; 9    ¦ -0,5П) ¦

¦  ¦                               ¦ 4 ; 2 ; 1 ; 11   ¦        ¦

+--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 1 ; 2 ; 3 ; 10   ¦(-5;0,2)¦

¦16¦R/(x+R)-m+(¦x¦-n)^2=0          ¦ 4 ; 5 ; 6 ; 25   ¦        ¦

¦  ¦                              ¦ 7 ; 8 ; 9 ; 40   ¦        ¦

¦--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 4 ; 4 ; 1 ; 12   ¦(-5;3)  ¦

¦17¦R*arcsin0,2x-m¦(x-n)^3¦=0      ¦ 2 ; 8 ;-3 ; 2    ¦        ¦

¦  ¦                               ¦ 1 ; 2 ; 1 ; 5    ¦        ¦

+--+-------------------------------+------------------+--------¦

¦  ¦                               ¦10 ; 2 ; 2 ; 7    ¦(0;4)   ¦

¦18¦R*arctgx+m+¦x+n¦=0             ¦12,5;1 ; 3 ; 10   ¦        ¦

¦  ¦                               ¦17 ; 5 ; 4 ; 10   ¦        ¦

+--+-------------------------------+------------------+--------¦

¦  ¦                               ¦ 1 ; 5 ;-6 ; 20   ¦(2;3,7) ¦

¦19¦(R-x^2)^1/2-m-n/(x-1)=0        ¦ 2 ;-13;-6 ; 0    ¦        ¦

¦  ¦                               ¦ 1 ; 3 ; 5 ; 27   ¦        ¦

¦--+-------------------------------+------------------+--------¦

¦  ¦                               ¦-16; 1 ; 2 ; 10   ¦(-3;-2) ¦

¦20¦R(m-x^2)^1/2-1+n/x=0           ¦ 4 ;0,5; 5 ; 15   ¦        ¦

+--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 1 ; 4 ; 2 ; 20   ¦(1;2,49)¦

¦21¦R¦cos0,5Пx¦-m(m-(x-n)^2)^1/2=0¦ 2 ; 5 ; 4 ; 28   ¦        ¦

¦  ¦                              ¦ 3 ;-6 ; 4 ;-13   ¦        ¦

¦--+------------------------------+------------------+--------¦

¦  ¦                              ¦-5 ; 1 ;-5 ; 18   ¦(0;1,5) ¦

¦22¦¦tgx¦-R(m-(x-n)^2)^1/2=0      ¦10 ;-1 ; 3 ;-18   ¦        ¦

¦  ¦                              ¦ 2 ;0,5; 5 ; 18,3 ¦        ¦

+--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 1 ; 2 ; 3 ; 12   ¦(0;0,5П)¦

¦23¦Rtgx-n+(x+m)^2=0              ¦ 4 ; 5 ; 6 ; 27   ¦        ¦

¦  ¦                              ¦-7 ;-8 ; 9 ; 12   ¦        ¦

+--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 1 ; 2 ; 3 ; 7    ¦(0,5П;П)¦

¦24¦1+cosx*(R-m)/(x-n)=0          ¦ 3 ; 2 ; 1 ; 9    ¦        ¦

¦  ¦                              ¦ 4 ;-5 ; 6 ; 9    ¦        ¦

¦--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 2 ; 2 ; 3 ; 9    ¦(0;П/2) ¦

¦25¦R+sinx*(m(x-n)^2-n)=0         ¦ 3 ; 6 ; 1 ; 8    ¦        ¦

¦  ¦                              ¦-2 ; 4 ; 5 ; 10   ¦        ¦

+--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 1 ; 1 ; 10; 12,5 ¦(0;1,9) ¦

¦26¦(R-(x+m)^2)^1/2-arccos(n-x)=0 ¦-2 ; 3 ; 4 ;-14   ¦        ¦

¦  ¦                              ¦-2 ; 8 ; 4 ; -9   ¦        ¦

¦--+------------------------------+------------------+--------¦

¦  ¦                              ¦ 2 ; 1 ; 2 ; 10   ¦(1;2)   ¦

¦27¦¦R-m¦x¦¦-(x-n)^2)^1/R=0       ¦-3 ; 2 ; 4 ;-1    ¦        ¦

¦  ¦                              ¦-5 ; 2 ;10 ;-4    ¦        ¦

+-------------------------------------------------------------+


Школьные задачи -введение
Введение

 В настоящем цикле лабораторных работ вам предстоит освоить

 решение некоторых типовых задач, предлагающихся на школьных

 олимпиадах районного и областного уровней - школьному учителю

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

 олимпиадах.

 Задачи пирводятся  с алгоритмами решения в словесном изложении

 и в программной реализации в Турбо_Паскалевских вариантах. Вам

 необходимо составить и испытать Си- и Бейсик- варианты этих

 программ.

 Прежде чем перейти к решению конкретных задач ,давайте вспомним

 некоторые прописные истины из процедурного программирования.

 ИТАК:

 Программа - это метод решения некоторого класса задач и она

 должна работать при любых возможных в этом классе размерах

 данных.На стадии составления и компиляции программы размеры

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

 только во время  выполнения ,ведь их подготовит пользователь ,

 для которого она  и составляется.

 Следствие :в программе для обработки например 1-мерных массивов

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

 объявления вида

 var

 MyArray:array[1..160] of real;

 -какой смысл писать программу,которая не может сделать то же

 самое с массивами других размеров.  Сами данные и сведения об

 их размерах поступают в программу извне- с клавиатуры, из

 файлов ,через  порты ввода-вывода,из командной строки

 операционной системы и КАК ПРАВИЛО размещаются в свободной

 области оперативной памяти -так называемой "куче"(heap).

 Наиболее предпочтительным для отладочных работ является

 поступление громоздких тестирующих данных (массивов разной

 размерности) из файла, а получение имени содержащего данные

 файла и размеров данных-через командную строку ОС. Ввод данных

 с клавиатуры при отладке съедает слишком много времени- его

 приходится многократно повторять после каждого исправления

 ошибок.

 Разумеется вы можете заставить пользователя разместить размеры

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

 начале.  Тогда вы  сначала открываете файл,читаете размеры

 данных, запрашиваете необходимую память, а затем в эту память

 помещаете читаемые из того же файла данные.

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

 массив в куче - для этого нам придется узнать имя файла и

 размер массива напрмер из командной строки (пусть первое слово

 командной строки будет именем файла а второе - размером

 массива):  Во всех приводимых примерах имя файла с тестирующими

 данными совпадает с именем *.pas файла,а расширение -*.tst -от

 слова test.Загружайте его в окно редактора интегрированной

 среды для просмотра,так как размеры ланных мы будем как правило

 задавать в командной строке и их можно определить подсчетом в

 файле}

 uses crt;     {для clrscr и ускорения вывода на экран}

 type

 {

 Если наш массив - вещественный и неизвестного нам размера,

 то есть смысл объявить его тип с максимально возможным

 размером - примерно 64 кбайт:

 }

 RealArray=array[1..10000] of real;

 var

 f:Text;                {для файловой переменной}

 ArrRange:integer;      {для размера массива}

 ErrorCode:integer;     {для кодов ошибок}

 RealArrayPtr:^RealArray;{для указателя на массив}

 Size:word;              {для размера недостающей памяти}

 i:integer;

 {

 Чтобы не вылетать при нехватке памяти с чужой диагностикой

 составим свою функцию обработки ошибок выделения памяти

 которая в неашем простейшем случае просто возвращает 1

 }

 {$F+}

 Function MyHeapFunc(Size:word):Integer;

 begin MyHeapFunc:=1;end;

 {$F-}

 begin

{Мы предусмотрели 2 параметра в ком строке}

 if ParamCount<>2 then

 begin

 writeln('Должно быть при запуске <имя программы> <имя файла>

 <размер массива>');halt;

 end;

 Val(ParamStr(2),ArrRange,ErrorCode); {Слово размера преобразуем

 в целое число}

 if ErrorCode<>0 then begin

 writeln('Какая-то ощибка во втором слове командной

 строки-размер массива не преобразуется в целое');halt;

                      end;

{ Теперь мы знаем размер массива и можем запросить под него

 память у ОС.Это делается в Паскале с помощью процедур New или

 GetMem.  }

 HeapError:=@MyHeapFunc; {Привязываем свой обработчик ошибок

                   чтобы при неудаче получить nil-указатель}

 {выделяем реально необходимую память}

 Getmem(RealArrayPtr,Sizeof(real)*ArrRange);

 if RealArrayPtr = nil then begin

 writeln('Ошибка выделения памяти.Невыделенный блок  ',Size,'

 байт'); halt;

 end;

 {Память выделена и можно размещать в ней данные Вначале откроем

 файл}

Assign(f,ParamStr(1));

{$i-}    {отключаем контроль правильности ввода вывода}

Reset(f);

{$i+}    {восстанавливаем}

if IOResult <> 0 then begin writeln('Файл ',ParamStr(1),

                              'не найден');halt;

                        end;

  {Читаем из файла в массив}

  for  i:=1 to ArrRange do

  read(f,RealArrayPtr^[i]);

  close(f);

 {Для проверки правильности укладки данных можно вывести на

 экран}

clrscr;

for  i:=1 to ArrRange do

write(RealArrayPtr^[i]:8:2); {Освобождаем память}

if RealArrayPtr<>nil then

FreeMem(RealArrayPtr,Sizeof(real)*ArrRange);

end.

{Такие заготовки должны быть у вас на дискете для векторов и

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

динамически выделяемой области памяти матрицу.  Даже если вам

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

или свои отладочные данные в файл, отладьте программу до

рабочего состояния а потом напишите ручной ввод - поверьте,вы

сэкономите время несмотря на кажущийся больший объем работы,

особенно если вы просто выкопируете шаблонные процедедуры работы

с командной строкой и файлами со своей дискеты }

 }

{

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

и способ обращения к ее элементам

}

 uses crt;         {для clrscr и ускорения вывода на экран}

 type

 {

 Если наш массив - вещественный и неизвестного нам размера

 то есть смысл объявить его тип с максимально возможным

 размером - примерно 64 кбайт.Мы собираемся работать с

 матрицей но объявим ее как одномерный массив. Потом при

 обращении к ее элементам мы будем вычислять индексы так,

 чтобы попадать на элементы матрицы- мы знаем что они

 располагаются построчно

 }

 RealArray=array[0..10000] of real;

 var

 f:Text;                {для файловой переменной}

 N,M:integer;           {для количества строк и столбцов}

 ErrorCode:integer;     {для кодов ошибок}

 RealArrayPtr:^RealArray;{для указателя на массив}

 Size:word;              {для размера недостающей памяти}

 i,j:integer;            {для идксации элементов матрицы}

 {

 Чтобы не вылетать при нехватке памяти с чужой диагностикой

 составим свою функцию обработки ошибок выделения памяти

 которая в неашем простейшем случае просто возвращает 1

 }

 {$F+}

 Function MyHeapFunc(Size:word):Integer;

 begin MyHeapFunc:=1;end;

 {$F-}

 begin

{Мы предусмотрели 3 параметра в ком строке

-имя файла,колич строк и столбцов матрицы }

 if ParamCount<>3 then

 begin

  writeln('Должно быть при запуске <имя программы> <имя файла>

  <кол строк> <кол столбц >');halt;

 end;

  Val(ParamStr(2),N,ErrorCode); {Слово размера преобразуем в

  целое число строк}

 if ErrorCode<>0 then begin

  writeln('Какая-то ощибка во втором слове командной строки-кол

  строк не преобразуется в целое');halt;

                      end;

 Val(ParamStr(3),M,ErrorCode); {Слово размера преобразуем в

 целое число столбцов}

 if ErrorCode<>0 then begin

 writeln('Какая-то ощибка в 3-м слове командной строки-кол

                      столбцов не преобразуется в целое');halt;

end;

{ Теперь мы знаем размеры матрицы и можем запросить под нее

 память у ОС.Это делается в Паскале с помощью процедур

 New или GetMem.

}

 HeapError:=@MyHeapFunc; {Привязываем свой обработчик ошибок

                   чтобы при неудаче получить nil-указатель}

 {выделяем реально необходимую память}

 Getmem(RealArrayPtr,Sizeof(real)*N*M);

 if RealArrayPtr = nil then begin

 writeln('Ошибка выделения памяти.Невыделенный блок  ',Size,'

 байт');

 halt;

                            end;

 {Память выделена и можно размещать в ней данные

 Вначале откроем файл}

 Assign(f,ParamStr(1));

{$i-}    {отключаем контроль правильности ввода вывода}

 Reset(f);

{$i+}    {восстанавливаем}

if IOResult <> 0 then begin writeln('Файл ',ParamStr(1),

                              'не найден');halt;

                        end;

  {Читаем из файла в массив}

  for  i:=0 to N*M-1 do

  read(f,RealArrayPtr^[i]); {Для обращения к массиву проводим

  разадресацию}

  close(f);

 {Для проверки правильности укладки данных можно вывести на

   экран обратите внимание на способ индексации элементов

   матрицы}

  clrscr;

  for  i:=0 to N-1 do

     begin

     for  j:=0 to M-1 do

     write(RealArrayPtr^[i*M+j]:10:2);

{Мы умножаем номер строки на ее длину и прбавляем номер столбца-

 если индексация элементов матрицы начинается с 0}

     writeln;

     end;

  {Освобождаем память}

if RealArrayPtr<>nil then

FreeMem(RealArrayPtr,Sizeof(real)*N*M);

end.                                                                    2

Тема:    Программирование арифметических задач.

{Задача: Сосчитать количество единиц в двоичной записи числа i.

Число будем получать из командной строки.

Составьте Си- Бейсик- варианты.

}

uses crt;

var

i,          {Здесь будет число,полученное из командной строки}

i1,         {Здесь то же ,сохраненное для вывода}

cnt:LongInt;{Здесь будет искомый результат-кол-во единиц}

e:integer;

begin

clrscr;

    if ParamCount<1 then begin

    writeln('Не задано число в командной строке');halt;

                         end;

    Val(ParamStr(1),i,e);

     {Алгоритм решения следующий:}

    i1:=i;               {Сохраняем для печати}

   cnt:=0;               {cnt -- счетчик единиц в i.}

   while (i<>0) do       {цикл повторяется число раз, равное}

     begin               {числу единиц в i. " Убираем " край-}

       i:=(i-1) and i;   {нюю справа единицу в двоичной записи }

       cnt:=cnt+1;       {числа.}

     end;

                                {  Пример:  110  = i

                                  101  = i-1

                                  ------------------

                                  100  = i and (i-1)}

    {Для полного понимания алгоритма его необходимо проиграть

    вручную на двоичных представлениях чисел }

   gotoxy(10,10);

 writeln('В 2-ичном представлении числа  ',i1,'  содержится

 ',cnt,'   единиц');

end.


Школьные задачи №2
Количество единиц в 2-ичной записи числа

{Задача: Сосчитать количество единиц в двоичной записи числа i.

Число будем получать из командной строки.

Составьте Си- Бейсик- варианты.

}

uses crt;

var

i,          {Здесь будет число,полученное из командной строки}

i1,         {Здесь то же ,сохраненное для вывода}

cnt:LongInt;{Здесь будет искомый результат-кол-во единиц}

e:integer;

begin

clrscr;

    if ParamCount<1 then begin

    writeln('Не задано число в командной строке');halt;

                         end;

    Val(ParamStr(1),i,e);

     {Алгоритм решения следующий:}

    i1:=i;               {Сохраняем для печати}

   cnt:=0;               {cnt -- счетчик единиц в i.}

   while (i<>0) do       {цикл повторяется число раз, равное}

     begin               {числу единиц в i. " Убираем " край-}

       i:=(i-1) and i;   {нюю справа единицу в двоичной записи }

       cnt:=cnt+1;       {числа.}

     end;

                                {  Пример:  110  = i

                                  101  = i-1

                                  ------------------

                                  100  = i and (i-1)}

    {Для полного понимания алгоритма его необходимо проиграть

    вручную на двоичных представлениях чисел }

   gotoxy(10,10);

 writeln('В 2-ичном представлении числа  ',i1,'  содержится

 ',cnt,'   единиц');

end.


Школьные задачи №3
Меняем местами значения элементов массивов

Предмет: Школьный курс информатики.

Тема:    Программирование арифметических задач.

{ Задача:

Дан массив  X(100)  и  Y(100).  Записать  алгоритм,  меняющий

последовательно местами значения элементов X(k) и  Y(k)  для

этих таблиц, для k=1,2,...,100, не используя промежуточных

переменных.

СПОСОБЫ РЕШЕНИЯ

Без использования промежуточных переменных эта задача

может быть решена только для числовых массивов.Если массив

из вещественных элементов то годится следующий алгоритм:

     1.  Оператор        Значение в A        Значение в B

         A:=A+B;             A+B                   B

         B:=A-B;             A+B                   A

         A:=A-B;              B                    A

     2. Для целочисленных массивов можно  использовать

       логическую операцию XOR (исключающее или). Таблица

       истинности для XOR следующая:

         1 XOR 1 = 0               1 XOR 0 = 1

         0 XOR 0 = 0               0 XOR 1 = 1

  Операция XOR  над  двумя переменными в машине реализуется как

  побитовая операция над двоичным представлением чисел. Поэ-

  тому, в частности, A XOR A = 0, A XOR B = B XOR A, A XOR 0 =A.

         Оператор        Значение в A        Значение в B

        A:=A XOR B         A XOR B                 B

        B:=A XOR B         A XOR B           (A XOR B) XOR B=

                                             =A XOR (B XOR B)=

                                             =A XOR 0 = A

        A:=A XOR B       (A XOR B) XOR A=          A

                        = B XOR (A XOR A)=

                           = B

 }

 {

  Программная реализация.  В связи с заданными в задаче

  размерами массивов заполним их случайными числами,выведем на

  печать ,поменяем местами значения и снова выведем для

  сравнения.  }

 uses crt;

 var

 a,b:array[1..100] of real;

 i,x,y:integer;

 begin

  clrscr;

  randomize;

   {Заполняем массивы}

  for i:=1 to 100 do begin

  a[i]:=random(300)/300+random(300);

  b[i]:=random(300)/300+random(300);;

                      end;

 x:=5;y:=1;

 gotoxy(x,y);

 for i:=1 to 100 do begin

     {Вывод до замены}

     write('a[',i,']=',a[i]:6:2,'   b[',i,']=',b[i]:6:2);

     {Замена}

     a[i]:=a[i]+b[i];b[i]:=a[i]-b[i];a[i]:=a[i]-b[i];

     {Вывод после замены}

     x:=x+40;gotoxy(x,y);

     write('a[',i,']=',a[i]:6:2,'   b[',i,']=',b[i]:6:2);

     if y<24 then begin y:=y+1;x:=5; gotoxy(x,y);end

     else begin readln;clrscr;x:=5;y:=1;gotoxy(x,y);end;

                    end;

 end.


Школьные задачи №4
Деление в К-ичной стстеме счисления

Предмет: Школьный курс информатики.

Тема:    Программирование арифметических задач.

{ Задача:

     Дано число в K-ичной системе счисления

            a a   ...a   (K<=36).

             n n-1    0

     Найти остаток от деления его на m.

     Числа K,  n, m, как и остаток от деления на m,

     представляются в десятичной системе счисления.

 Описание алгоритма.

Разумеется, можно  непосредственно  вычислить это число в

десятичной системе счисления,  после чего разделить его на  m,

но в этом случае придется представлять число в виде (например)

массива цифр и моделировать операции над этим числом.

     Существует другой простой алгоритм.  В системе счисления с

основанием K число представляется в виде

     a[n]*K^n + a[n-1]*K^(n-1) + ... +a[0]*K^0.

Найдем остаток  от деления его на m (остаток от деления a

на b обозначим a mod b):

   (a[n]*K^n +  a[n-1]*K^(n-1)  +  ...  +a[0]*K^0) mod m =

   +  n          +         +  n                   +

  =¦ SUM a[i]*K^i¦ mod m = ¦ SUM a[i]* (K^i mod m)¦ mod m

   + i=0         +         + i=0                  +

 Это следует из следующих соображений:  пусть K^i mod m=t,

тогда K^i =p*m+t, и

 (a[i]*K^i) mod m = (a[i] * (p*m+t)) mod m =

       = (a[i]* p*m) mod m + (a[i]*t) mod m =

       = (a[i] * (K^i mod m)) mod m,

при этом для любых чисел b[i] выполняется

    n                   n

 ( SUM b[i] ) mod m =( SUM b[i]  mod m ) mod m.

   i=0                 i=0

     Отметим также очевидное равенство

     K^i mod m =[(K^(i-1) mod m) * K] mod m,

т.к. если K^(i-1) = p*m+t, то K^(i-1) mod m = t,

K^i = p*m*K+t*K и K^i mod m = t*K mod m =

    = [(K^(i-1) mod m)*K] mod m.

   Запись этого алгоритма (тут a[i] - K-ичные цифры числа):

      s:=0;  t:=1;

      for i:=0 to n do

        begin

          s:=(s+a[i]*t) mod m;

          t:=t*K mod m;

        end;

     В переменной S после  окончания  работы  алгоритма  будет

храниться искомый остаток.

}

{Программная реализация.

Будем предполагать,что все три слова-К-ичное число,система

счисления К и делитель вводятся в командной строке.

Пользователю как и всем нам свойственно ошибаться,поэтому

проверим вначале не задал ли он недопустимую систему счисления.

Для диагностики возможных ошибок и своевременной рстановки

программы составим процедуру,которая будет получать все 3

параметра задачи}

uses crt;

var

K:integer;                  {система счисления}

DT:string;                  {строка делимого}

D:integer;                  {делитель}

e:integer;                  {код ошибки преобразов в целое}

n:integer;                  {значение цифры}

ostat,t,i:integer;

procedure check;

var

i:integer;

begin

{Переведем все символы строки делимого к верхнему регистру,

чтобы не стеснять пользователя излишними требованиями}

    for i:=1 to length(DT) do

    if (DT[i]>='a') or (DT[i]<='z') then DT[i]:=UpCase(DT[i]);

    {Теперь приступим к проверке}

    for i:=1 to length(DT) do begin

    if ((K<=10) and ((DT[i]<'0') or( DT[i]>Chr(K-1+ord('0')))))

        then begin

 writeln(i,'-я цифра делимого не соответствует системе

        счисления');

 halt;

             end;

        if (K>10) and ((DT[i]>'9') and (DT[i]<'A')) then begin

        writeln(i,'-я цифра делимого недопустима в любой

        системе счисления');

        halt;

                                                         end;

 if (K>10) and (DT[i]>'Z') then begin

 writeln(i,'-я цифра делимого недопустима в любой системе

 счисления');

 halt;

 end;

 end;

{Будем считать,что делитель вводится не в К-ичной а в 10-тичной

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

прдложенного нам алгоритма пропала бы.Поэтому для делителя

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

той,которя заложена в функции Val.}

end;

{Другая подпрограммя,которая нам будет необходима-это функция

для вычисления значения К-ичных цифр.Оформим ее в виде функции}

function number(c:char):integer;

begin

  if (K<=10) or ((K>10) and (c<='9'))

  then begin number:=ord(c)-ord('0');exit;end

  else  number:=(ord(c)-ord('A')+10);

end;

begin

 if ParamCount<3 then begin  writeln('Не все параметры в

                     командной строке'); halt; end;

{Преобразуем строку делителя в 10-ное число}

 Val(ParamStr(3),D,e);

    if (e<>0)

    then begin writeln('Что-то неладно с делителем');halt;end;

    Val(ParamStr(2),K,e);

    if (e<>0) or (K>36) or (K<2)

    then begin writeln('Что-то неладно с системой счиcления');

    halt;end;

DT:=ParamStr(1);

check;

{Теперь можем реализовать алгоритм}

      ostat:=0;  t:=1;

      for i:=length(DT) downto 1 do

        begin

          n:=number(DT[i]);

          ostat:=(ostat+n*t) mod D;

          t:=t*(K mod D);

        end;

  clrscr;gotoxy(18,10);

  writeln('Остаток равен  ',ostat);

end.


Школьные задачи №5
Делимость числа

Предмет: Школьный курс информатики.

Тема   :Системы счисления и арифметические задачи.

{

 Задача:

Число вводится  своим двоичным представлением (длина числа не

превышает 10000 двоичных разрядов). Необходимо определить

делится ли число на 15.

Если вспомнить,  что  признаком деления на 9 в десятичной

системе счисления является делимость на  9  суммы  цифр  числа

(действительно, пусть есть число

 S = a[n]*10^n + a[n-1]*10^(n-1) + ... + a[1]*10 + a[0].

 S mod 9 = (a[n]*(10^n-1)+a[n] + a[n-1]*(10^(n-1)-1)+a[n-1] +

            + ... + a[1]*(10-1)+a[1] + a[0]) mod 9

     А так как 10^k - 1 делится на 9 нацело, то и

          S mod 9 = (a[n] + ... +a[1] +a[0]) mod 9,

ч.т.д.), то аналогично получаем, что признаком деления на 15 в

системе счисления с базисом 16 будет  делимость  на  15  суммы

всех шестнадцатеричных цифр числа.

     Мы разбиваем двоичное число справа налево на тетрады, ко-

торые  однозначно можно преобразовать в шестнадцатеричные циф-

ры, находим их сумму и делим ее на 15. Если остаток 0, то вве-

денное число делится на 15, иначе - нет.

Программная реализация.

Пусть это такое длинное число находится в файле а его имя

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

наборе числа- между отдельными блоками единиц и нулей могут

например для удобства стоять пробелы,число может располагаться

на нескольких строках в тектовом файле.

Прочитав из файла байт и вычтя из него код символа '0'мы получим

значение двоичной цифры т.е. значение 0 или 1.Сдвинув это влево

на номер позиции в тетраде ,можно присуммировать его в

промежуточный сумматор для определения значения 16-ричной

цифры.Если мы сразу учтем то,что первая тетрада может быть

неполной,так как пользователь совсем необязательно введет в ней

ведущие нули,то суммирование значений тетрад можно вести и не с

конца а с начала }

uses crt;

var

{Текстовый файл мы объявим как байтовый,а ненужные

символы будем отсеивать при чтении}

f:file of byte;

c: char; {Здесь будет прочитанный байт за вычетом кода '0'}

b:byte;

Sum4:byte;                 {Здесь будет 4-битовое число}

Sum:LongInt;               {Здесь-сумма цифр}

i,j:integer;

flen:word; {Здесь будет количество единиц и нулей в числе}

shlen:byte;{Здесь-длина первой неполной тетрады,появляющейся

           если пользователь не вводит для нее ведущих нулей}

begin

clrscr;

 if ParamCount<1 then begin writeln('Введите имя файла в

 командной строке');halt;  end;

assign(f,ParamStr(1));

reset(f);

{Теперь нам необходимо выяснить число единиц и нулей в файле

Для этого прочтем файл побайтно,сосчитав только единицы и нули}

flen:=0;

while not EOF(f) do begin

read(f,b);if (b=ord('0')) or (b=ord('1')) then flen:=flen+1;

                    end;

close(f);

shlen:=(flen mod 4);    {Длина первой неполной тетрады}

if ((flen div 4)+1)>(MaxLongInt div 15) {Если сумма цифр может

                                        не поместиться в самом

                                        длинном целом}

then begin writeln('С длиной числа вы перестарались');halt;end;

reset(f);

{Теперь можем читать из файла первую неполную тетраду и

обрабатывать Основной источник ошибок здесь-необходимость

учитывать то,что мы читаем из файла символьные представления

битов в обратном порядке.  Мы корректируем это количеством

сдвигов влево -вам необходимо представлять себе 2-ичную

структуру машинных целых чисел}

Sum4:=0;

for i:=1 to shlen do begin

read(f,b);

if (b=ord('0')) or (b=ord('1')) then begin

b:=b- ord('0'); b:=(b shl (shlen-i));Sum4:=Sum4+b;

                                     end;

                       end;

Sum:=Sum4;             {Первую цифру запишем в сумматор}

{Теперь можем читать из файла остальные тетрады и обрабатывать}

for j:=1 to (flen div 4)  do begin

Sum4:=0;i:=0;

while i<4 do begin

read(f,b);

if (b=ord('0')) or (b=ord('1')) then begin

b:=b- ord('0'); b:=(b shl (3-i));Sum4:=Sum4+b;i:=i+1;

                                     end;

                  end;

Sum:=Sum+Sum4;

                              end;

close(f);

if (Sum mod 15)=0  then

writeln('Делится') else writeln('Не делится');

end.


Школьные задачи №6
Остаток от деления

Предмет: Школьный курс информатики.

Тема   : Системы счисления и программирование арифметических

задач.

{ Задача:

Любое натуральное  число  N   можно   единственным   способом

представить  с  помощью некоторых целых неотрицательных d[0],

... , d[s] в виде

            N=d[s]*(s+1)!+d[s-1]*s! +...+d[1]*2!+d[0]           (*)

при условии, что 0<=d[i]<=i+1, i=0,..,s, где d[s]<>0.

Дано s+1 натуральное число d[0],  ..., d[s], и натуральное K,

 s<200,d[i]<65535, K<65535. Найти остаток от деления числа N,

определяемого в факториальной системе (*) числами d[0],  ...,

d[s], на число K.

 РЕШЕНИЕ

      Задача решается также, как и предыдущая:

         +   s                                +

N mod K =¦  SUM  (d[j] mod K) * ((s+1)! mod K)¦ mod K,

         +  j=0                               +

                   +                           +

где (s+1)! mod K = ¦ (s! mod k) * ((s+1) mod k)¦ mod k,

                   +                           +

    Алгоритм запишется следующим образом:

       остаток:=0;

       faktorial:=1;

       for i:=1 to s+1 do

         begin

           остаток:=(остаток+(d[i-1] mod k)*faktorial) mod k;

           faktorial:=(faktorial * ((i+1) mod k)) mod k;

         end;

 В переменной  остаток после выхода из цикла будет искомое

 значение.

}

{

 Программная реализация.

 Пусть на этот раз заданный массив натуральных чисел лежит в

 файле, имя которого сообщается в командной строке.Делитель К

 пусть тоже в командной строке на втором месте.  }

uses crt;

type

WordArray=array[1..200] of word; {Тип с избыточным размером}

var

s:byte;

d:^WordArray;

K,ostatok,fakt:word;

f:text;

e:integer;

begin

 clrscr;

 if ParamCount<2 then begin writeln('Не все параметры в

 командной строке');halt;end;

 assign(f,ParamStr(1));

 reset(f);

 {Определяем количество чисел в файле то есть необходимый

 размер массива для их размещения в памяти.Для этого читаем

 куда-нибудь до конца файла.}

 s:=0;

 while not EOF(f) do begin

 read(f,K);s:=s+1;

 end;

 close(f);

 Val(ParamStr(2),K,e);     {Определяем значение делителя}

 if e<>0 then begin  writeln('Проверьте 2-й параметр ком

 строки');halt;  end;

 GetMem(d,s*Sizeof(word));   {Выделяем память для s натур чисел}

 reset(f);

 for e:=0 to s-1 do {Пишем в массив начиная с последнего

 элемента}

 read(f,d^[s-e]);

 close(f);

       ostatok:=0;   fakt:=1;

       for e:=2 to s do

         begin

           ostatok:=(ostatok+(d^[e-1] mod k)*fakt) mod k;

           fakt:=(fakt * ((e+1) mod k)) mod k;

         end;

 gotoxy(18,10);

 writeln('Остаток равен  ',ostatok);

 FreeMem(d,s*Sizeof(word));

end.


Школьные задачи №7
Основы теории графов и задача о кратчайшем пути

Предмет: Школьный курс информатики.

Тема   : ТИПОВЫЕ ЗАДАЧИ НА ГРАФАХ

{

1.Основные сведения из теории графов

              Основные определения.

 Наглядное представление о графе можно получить если

представить себе некоторое множество точек плоскости X

,называемых вершинами ,и множество направленных отрез-

ков U,соединяющих все или некоторые из вершин и называе-

мых дугами.Математически граф можно представить как пару

множеств X и U :

                 G=(X,U).

 Две вершины x и y называются смежными ,если они различ-

ны и если существует дуга ,ведущая из x в y.

 Путем в графе G называется такая последовательность дуг

u=(u1,u2,...,uk),в которой конец каждой предыдущей дуги

совпадает с началом последующей.Путь u,последовательными

вершинами которого являются вершины a,b,...,m ,обознача-

ется через u=(a,b,...,m).Длиной дуги u=(u1,u2,...,uk)

называется число l(u)=k,равное числу дуг,составляющих

путь u.Путь ,в котором никакая дуга не встречается дваж-

ды,называается простым.Путь,в котором никакая вершина не

встречается дважды ,называется элементарным.

 Контур-это конечный путь u=(x1,...,xk),у которого началь-

ная вершина x1 совпадает с конечной xk.

 Иногда граф рассматривают без учета ориентации его дуг.

В этом случае его называют неориентированым графом.Для

неориентированого графа понятия дуга,путь и контур заме-

няются понятиями ребро ,цепь,цикл.

 С понятием неориентированого графа связана важная харак-

теристика ,называемая связностью графа.Говорят,что граф

связен,если любые две его вершины можно соединить цепью.

2. Задача о кратчайшем пути.

 В практических приложениях имеет большое значение задача

о нахождении кратчайшего пути между двумя вершинами связ-

ного неориентированого графа.К такой задаче сводятся мно-

гие задачи выбора наиболее экономичного (с точки зрения

расстояния,времени или стоимости) маршрута на имеющейся

карте дорог и др.

  Задача о кратчайшем пути на графе в общем виде может

быть сформулирована следующим образом.Дан неориентирован-

ный граф G=(X,U).Каждому ребру этого графа приписано не-

которое число l(u)>=0 ,называемое длиной ребра.Требуется

для двух произвольных вершин a и b графа G найти путь

uab,причем такой,чтобы его полная длина была наименьшей.

 Рассмотрим для начала алгоритм решения задачи частного

вида ,когда длина каждого ребра равна единице.

  Общее правило для нахождения кратчайшего пути в графе

состоит в том,чтобы каждой вершине xi приписать индекс

pi,равный длине кратчайшего пути из данной вершины в ко-

нечную.Приписывание индексов вершинам в случае графа еди-

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

      1) Конечной вершине x0 приписывается индекс 0;

      2) Всем вершинам,из которых идет ребро в конечную

вершину,приписывается индекс 1;

      3) Всем вершинам ,еще не имеющим индексов ,из кото-

рых идет ребро в вершину с индексом pli,приписывается ин-

декс pl(i+1).Этот процесс продолжается до тех пор,пока не

будет помечена начальная вершина.По окончании разметки

индекс у начальной вершины будет равен длине кратчайшего

пути.Сам кратчайший путь найдем,если будем двигаться из

начальной вершины в направлении убывания индексов.

   Процесс приписывания индексов для графов с ре6брами

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

      1) Каждая вершина xi помечается индексом pi.Перво-

начально конечной вершине x0 приписывается индекс p0=0.

Для остальных вершин предварительно полагаем pi=беско-

нечности(i!=0).

     2) Ищем такую дугу (xi,xj) для которой pi-pj>l(xi,xj)

,и заменяем индекс pi индексом pj1=pi+l(xi,xj)<pj.Про-

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

ется хотя бы одна дуга,для которой можно уменьшить pi.

    Отсюда вытекает (неважно,как) правило для нахождения

кратчайшего пути :

    Пусть xn=a-начальная вершина с индексом pn.Ищем верши-

ну xk,такую,что pk1-pk2=l(xk1,xn).Далее ищем вершину xk2,

такую,что pk1-pk2=l(xp2,xp1) ит.д. до тех пор, пока не

дойдем до конечной вершины.

2a.Программная реализация задачи о кратчайшем пути.

Пусть в качестве исходных данных нам задаются:

-количество дуг в графе (количество дорог в транспортной сети,

соединяющей населенные пункты в районе или дома в населенном

пункте и т д);

-описание дуг в виде 3-хэлементных записей(2 номера вершин

 и длина соединяющей их дуги).

-количество вершин может либо вводиться либо подсчитываться в

программе по описаниям дуг;

Итак обозначив

N-количество вершин графа придется

K-количество дуг графа

 beg,nxt-номера вершин,между которыми нужно найти наименьшее

 расстояние

var

N,K,beg,nxt:integer;

 Организуем описания дуг в массив записей (длина массива пока

 неизвестна:

Type

Path=Record

FirstNode,TwoNode,PathLength:word;

end;

ArrayPath=array[0..MaxInt] of Path;  длина массива произвольна-с

                                  запасом тк память при

                                  объявлении типа не отводится

Какие еще типы данных  нам понадобятся?

Во-первых массив записей-описателей вершин

Node=Record

Index:integer; для индексации кратчайшими расстояниями от

заданной вершины

 Flag,Predok:word; флаг просмотренности и номер предыдущей в

 кратчайшем пути

end;

ArrayNode=array[0..100] of Node; массив произвольной длины

Кроме того для удобства анализа связанности пар вершин нам

пригодится квадратная матрица, в ячейках которой будут

расстояния между вершинами, если они соединены дугой и

бесконечности,если непосредственной связинет.Эта матрица пока

тоже неизвестных разиеров и мы предусмотрим для нее тип

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

динамической области мы получим только адрес этой области и

работать с матрицей нам придется как с одномерным

массивом,вычисляя индексы элементов по номерам строк и столбцов.

IntArray=array[0..10000] of integer;

var

f:Text;               файл с тестирующими данными дуг

M:^IntArray;          указатели на матрицу

P:^ArrayPath;                   на массив дуг

Nd:^ArrayNode;                  на массив вершин

E:integer;            для кодов  ошибок

Теперь уже можно начинать что-то делать

Где-то, например в основной программе возьмем количество  дуг

из командной строки - предположим из второго слова

    if ParamCount<2 then begin

    writeln('Не все задано  в командной строке');halt;

                         end;

    Val(Paramstr(2),K,E);

    if E<>0 then begin writeln('Ошибка в задании числа дорог');

    halt;        end;

    Отведем в куче память для массива K записей о дорогах

    GetMem(P,K*Sizeof(Path));

    Цикл ввода описаний дорог с клавиатуры

    for i:=0 to K-1 do

    readln(P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);

    Но для отладки лучше создать файл ,тк ввод съедает много

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

    Assign(f,ParamStr(1));

    Reset(f);  откроем файл для чтения

    Читаем из файла в массив описателей дуг

    for i:=0 to K-1 do

    readln(f,P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);

    Close(f);  закроем уже ненужный файл

    Количество узлов придется подсчитать по данным о дугах

    if (P^[0].FirstNode<>P^[0].TwoNode)  then  N:=2;

    for i:=1 to K-1 do begin

    flag1:=0;flag2:=0;

    for j:=0 to i-1 do begin

    if (P^[i].FirstNode=P^[j].FirstNode) or

    (P^[i].FirstNode=P^[j].TwoNode)

       then

       flag1:=1;

    if   (P^[i].TwoNode=P^[j].FirstNode) or

    (P^[i].TwoNode=P^[j].TwoNode)

       then flag2:=1

                       end;

    if flag1=0 then N:=N+1;

    if flag2=0 then N:=N+1;

    end;

   Теперь можно отвести память для вспомогательной матрицы

    GetMem(M,N*N*SizeOf(Integer));

    и для массива записей о вершинах

    GetMem(Nd,N*Sizeof(Node));

    Заполним вспомогательную матрицу смежности - вначале

    бесконечностями

    for i:=0 to N*N-1  do M^[i]:=MAXINT;

    а затем длинами существующих между узлами дуг

    for i:=1 to K do begin

    M[P^[i].FirstNode]^[P^[i].TwoNode]:=P^[i].PathLength;

    M[P^[i].TwoNode]^[P^[i].FirstNode]:=P^[i].PathLength;

                     end;

  На этом подготовительная работа закончена и можно приступать к решению

  задачи.

  В соответствии с описанным выше алгоритмом всем вершинам,не

  соседствуюшим с начальной,даем бесконечное расстояние а

  соседствуюшим-то,что есть.Все это берем из вспомогательной

  матрицы

      for i:=1 to N do

       begin

         Nd^[i].Predok:=beg;

         Nd^[i].Flag:=0;

         Nd^[i].Index:=M[beg]^[i];

       end;

      Nd^[beg].Flag:=1;

      Nd^[beg].Predok:=0; она в начале пути и предыд у нее нет

      Nd^[beg].Index:=0;  расстояние до самой себя

      находим минимальное расстояние до непомеченных вершин

      Для операций сравнения длин дуг введем переменную

      MinLen:integer;

       for i:=1 to N-1 do

   begin

         MinLen:=MaxInt;

         for j:=1 to N do

           if(Nd^[j].Flag=0) and (MinLen > Nd^[j].Index)

               then begin MinLen:=Nd^[j].Index;

               q:=j;

                     end;

         Nd^[q].Flag:=1; вершина q помечается просмотренной

 for j:=1 to N do         выполняем просмотр

 if (Nd^[j].Flag=0) and (Nd^[j].Index>Nd^[q].Index+M[q]^[j])

Т.е. если для вершины j еще не найдено  кратчайшее

расстояние от нач, и из вершины q по дуге M[q]^[j]

путь в j короче, чем найденный ранее

то запоминаем его

  then begin Nd^[j].Index:=Nd^[q].Index+M[q]^[j];

  Nd^[j].Predok:=q;

       end;

   end;

   Приведенный алгоритм носит название алгоритма Дейкстры.В

   результате его выполнения в поле Index записей о вершинах

   получаем минимальное расстояние до вершины beg.Таким образом

   мы перевыполнили задание- нашли минимальное расстояние не

   только между beg и nxt но и между beg и всеми остальными.

   Сам алгоритм поместим в процедуру Deikstra,подготовительные

   операции и вывод результата оставим в основной программе,вы

   можете откомпилировать и выполнить приведенную ниже программу

   }

{Приведенную ниже Паскаль - программу вам необходимо

откомментировать и протестировать, а затем проделать все сначала

на Си и Бейсике

}

uses crt;

Type

Path=Record

FirstNode,TwoNode,PathLength:integer;

end;

ArrayPath=array[0..100] of Path;

Node=Record

Index:LongInt;

Flag,Predok:word;

end;

ArrayNode=array[0..100] of Node;

IntArray=array[0..10000] of integer;

var

N,K,E,i,j,q,x,beg,nxt,flag1,flag2:integer;

f:Text;               {файл с тестирующими данными дуг}

M:^IntArray;          {матрица}

P:^ArrayPath;          {массив дуг}

Nd:^ArrayNode;         {массив вершин}

MinLen: integer;

procedure Deikstra(beg:integer);

begin

          for i:=0 to N-1 do

       begin

         Nd^[i].Predok:=beg;

         Nd^[i].Flag:=0;

         Nd^[i].Index:=M^[beg*N+i];

       end;

      Nd^[beg].Flag:=1; {пока мы знаем только расстояние

                        от вершины нач до нее же, равное 0}

      Nd^[beg].Index:=0;

      Nd^[beg].Predok:=0;

 for i:=1 to N-1 do

      begin

         MinLen:=MaxInt;

         for j:=0 to N-1 do

         if(Nd^[j].Flag=0) and (MinLen > Nd^[j].Index)

               then begin MinLen:=Nd^[j].Index;

               q:=j;

                    end;

         Nd^[q].Flag:=1;{ вершина q помечается просмотренной}

for j:=0 to N-1 do        { выполняем просмотр}

  if (Nd^[j].Flag=0) and (Nd^[j].Index>Nd^[q].Index+M^[q*N+j])

   { Т.е. если для вершины j еще не найдено  кратчайшее

     расстояние от нач, и из вершины q по дуге C[q,j]

     путь в j короче, чем найденный ранее

     то запоминаем его}

     then begin Nd^[j].Index:=Nd^[q].Index+M^[q*N+j];

          Nd^[j].Predok:=q;

          end;

end;

end;

begin

 {Возьмем количество  дуг,нач и конечн вершины из командной

 строки}

    if ParamCount<4 then begin

    writeln('Не все задано  в командной строке');halt;

                         end;

    Val(Paramstr(2),K,E);

if E<>0 then begin writeln('Ошибка в задании числа дорог');

halt;end;

Val(Paramstr(3),beg,E);

if E<>0 then begin writeln('Ошибка в задании числа дорог');

halt;end;

Val(Paramstr(4),nxt,E);

if E<>0 then begin writeln('Ошибка в задании числа дорог');

halt;end;

{Отведем в куче память для массива K записей о дорогах }

    GetMem(P,K*Sizeof(Path));

    {Цикл ввода описаний дорог

    for i:=0 to K-1 do

    readln(P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);}

    {Но для отладки лучше создать файл ,тк ввод съедает много

    времени}

    Assign(f,ParamStr(1));

    Reset(f);

for i:=0 to K-1 do

readln(f,P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);

Close(f);

{Количество узлов придется подсчитать по данным о дугах}

    if (P^[0].FirstNode<>P^[0].TwoNode)  then  N:=2;

    for i:=1 to K-1 do begin

    flag1:=0;flag2:=0;

    for j:=0 to i-1 do begin

    if (P^[i].FirstNode=P^[j].FirstNode) or

    (P^[i].FirstNode=P^[j].TwoNode)

       then

       flag1:=1;

    if (P^[i].TwoNode=P^[j].FirstNode) or

       (P^[i].TwoNode=P^[j].TwoNode)

       then flag2:=1

                       end;

    if flag1=0 then N:=N+1;

    if flag2=0 then N:=N+1;

    end;

{Теперь можно отвести память для вспомогательной матрицы}

    GetMem(M,N*N*SizeOf(Integer));

    {и для массива записей о вершинах}

    GetMem(Nd,N*Sizeof(Node));

   {Заполним вспомогательную матрицу смежности - вначале

   бесконечностями}

    for i:=0 to N*N-1  do M^[i]:=MAXINT;

{а затем длинами существующих между узлами дуг- будем для

каждой вершины просматривать дуги и если вершина лежит

на дуге,заполним ее длиной соответствующие элементы матрицы }

    for i:=0 to K-1 do begin

    M^[P^[i].FirstNode*N+P^[i].TwoNode]:=P^[i].PathLength;

    M^[P^[i].TwoNode*N+P^[i].FirstNode]:=P^[i].PathLength;

                     end;

    Deikstra(beg);

    clrscr;

  writeln('Минимальное расстояни между вершинами   ',beg,'

  ',nxt,'равно',Nd^[nxt].Index);

     {Освобождаем память}

    FreeMem(P,K*Sizeof(Path));

    FreeMem(Nd,N*Sizeof(Node));

    FreeMem(M,N*N*SizeOf(Integer));

end.

{Файл для тестирования:

5 21 6

1 21 12

17 21 2

16 0 3

0 1 5

1 2 3

2 3 4

3 4 3

4 5 4

5 6 3

6 7 10

7 8 5

8 9 2

9 10 7

10 11 3

11 12 7

12 13 1

13 14 12

14 15 7

15 16 5

5  3  8

10 12 8

16  17 15

17  18 5

15 18 10

13 18 7

18 19 3

9 19 12

10 19 4

19 20 1

8 20 7

6 20 8

17 20 10

}


Школьные задачи №8
Графы - минимум суммы расстояний

Предмет: Школьный курс информатики

Тема   : Задачи из теории графов

{Задача:

     Вводится N - количество домов и К -  количество  дорог.

Дома пронумерованы от 1 до N.  Каждая дорога определяется

тройкой чисел - двумя номерами домов - концов дороги и длиной

дороги.  В  каждом доме  живет  по одному человеку.  Найти точку

- место встречи всех людей,  от которой суммарное расстояние до

всех домов будет  мини- мальным.

     Если точка лежит на доpоге,  то указать номера домов -

концов этой доpоги и расстояние от первого из этих домов. Если

точка сов- падает с домом, то указать номер этого дома.

 Примечание: длины дорог - положительные целые числа.

В отладочном примере 33 дороги и 22 дома.

ОБЩЕЕ РЕШЕНИЕ

Если предположить,что искомая точка может лежать на дороге,то в

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

расстояния до первого из домов на этой дороге.Отсутствие такого

условия косвенно подсказывает, что искомая точка совпадает с

одним из домов.  Отложив доказательство этого на потом, мы

только отметим,что если бы возможно было расположение искомой

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

фиктивные дома на каждой из дорог, находить минимальные суммы

расстояний от всех других домов к нашим фиктивным и затем

двигать по дороге наш фиктивный дом в направлении уменьшения

суммы расстояний до него от всех других домов-другими словами

задача определения минимальной суммы расстояний от всех домов к

одному всегда остается с нами и нам в любом случае понадобится

подпрограмма,выполняющая эту работу. Это вообще одна из

стандартных задач на графах и на олимпиадах ее решение должно

быть под рукой на вашей дискете. Чем больше у вас будет домашних

заготовок ,тем меньше времени вы израсходуете на подготовку

решений и тем больше шансов на победу.

План решения должен быть таким:

В цикле берем в качестве кандидата на роль искомой точки

поочередно каждую из вершин,находим для нее минимальные

расстояния до всех остальных и суммируем эти минимальные

расстояния.Каждую очередную сумму минимальных расстояний

сравниваем с предыдущей и если она меньше, то сохраняем ее и

номер соответствующей вершины в качестве кандидата на решение

задачи.Таким образом содержательная часть нашей работы - это

поиск маршрута минимальной длины между 2-мя точками в

произвольном неориентированном графе и определение этой длины -

все остальное сводится к организации циклов и срвнений.

 Итак нам понадобится хранить 2 суммы расстояний-предыдущую и

 текущую

SumPrev,SumCur: LongInt;

 Введены будут K-количество дорог(дуг графа) и N-количество

 домов(узлов)

N,K:integer;

 Исходные данные нам предлагаются в виде 3-хэлементных описаний

дорог.  (2 номера вершин и длина соединяющей их

дороги).Организуем их в массив записей c пока неизвестной

длиной:

Type

Path=Record

FirstNode,TwoNode,PathLength:word;

end;

ArrayPath=array[1..MaxInt] of Path;

ArrPathPtr=^ArrayPath;

и затем после определения К выделим память под этот массив и

инициализируем поля его записей.

 Нам понадобится для работы также матрица смежности графа

размером N*N ,в ячейках которой будут храниться расстояния от

вершины i до вершины j,если вершины смежны и

MAXINT(бесконечность),если они не принадлежат одному и тому же

участку дороги.

IntArray=array[1..100] of integer;

IntArrayPtr=^IntArray;

Matr=array[1..100] of IntArrayPtr;

 Для определения минимального расстояния от некоторой вершины до

остальных вершин графа можно использовать известный алгоритм

Дейкстры,

 Кратчайшее расстояние от вершины нач до остальных вершин.

                       (Алгоритм Дейкстры).

Обозначения:

C[i,j]- длина ребра(i,j),  С[i,j]>=0 (если ребра нет,  то его

длина полагается равной бесконечности).

D[i]- кратчайшее текущее расстояние от вершины нач до вершины i.

флаг[i]- информация о просмотре вершины i:  0 - если  вершина

не просмотрена, 1 - если просмотрена. Если вершина просмотрена,

то для нее  D[i] есть наикратчайшее расстояние от вершины нач до

вер- шины i.

предок[i]- информация о номере вершины,предшествующей вершине i

в кратчайшем пути от вершины нач.

минрас - это минимальное расстояние.

   Алгоритм:  для i от 1 до N выполнять

       нц предок[i]:=нач;

         флаг[i]:=0; D[i]:=C[нач,i]

       кц

 флаг[нач]:=1;          пока мы знаем только расстояние

 предок[нач]:=0         от вершины нач до нее же, равное 0

      для i от 1 до N-1 выполнять

       нц

         минрас:=бесконечность;

         для j от 1 до N выполнять

если (флаг[j]=0 и (минрас > D[j])  находим минимальное

то минрас:=D[j];                   расстояние

k:=j;                              до непомеченных вершин

           все

         флаг[k]:=1;         вершина k помечается просмотренной

         для j от 1 до N выполнять    выполняем просмотр

           если флаг[j]=0 и D[j]>D[k]+C[k,j]

Т.е. если для вершины j еще не найдено  кратчайшее

 расстояние от нач, и из вершины k по дуге C[k,j]

 путь в j короче, чем найденный ранее

 то D[j]:=D[k]+C[k,j]       то запоминаем его

     предок[j]:=k;

           все

       кц

}

{Приведенную ниже Паскаль - программу вам необходимо

откомментировать и протестировать, а затем проделать все сначала

на Си и Бейсике

}

uses crt;

Type

Path=Record

FirstNode,TwoNode,PathLength:integer;

end;

ArrayPath=array[1..100] of Path;

ArrPathPtr=^ArrayPath;

Node=Record

MinL,Flag,Predok:word;

end;

ArrayNode=array[1..100] of Node;

ArrNodePtr=^ArrayNode;

IntArray=array[1..100] of integer;

IntArrayPtr=^IntArray;

Matr=array[1..100] of IntArrayPtr;

var

N,K,E,i,j,q,x,beg,MinN,flag1,flag2:integer;

f:Text;         {файл с тестирующими данными дорог}

M:Matr;

P:ArrPathPtr;

Nd:ArrNodePtr;

SumPrev,SumCur,MinLen: LongInt;

procedure Deikstra(beg:integer);

begin

 {Всем вершинам,не соседствуюшим с начальной,даем бесконечное

расстояние а соседствуюшим-то,что есть.Все это берем из

вспомогательной матрицы}

      for i:=1 to N do

       begin

         Nd^[i].Predok:=beg;

         Nd^[i].Flag:=0;

         Nd^[i].MinL:=M[beg]^[i];

       end;

      Nd^[beg].Flag:=1;  {пока мы знаем только расстояние

                          от вершины нач до нее же, равное 0}

      Nd^[beg].Predok:=0;

      Nd^[beg].MinL:=0;

      for i:=1 to N-1 do

      begin

         MinLen:=MaxLongInt;

{ находим минимальное расстояние до непомеченных вершин}

 for j:=1 to N do

 if(Nd^[j].Flag=0) and (MinLen > Nd^[j].MinL)

 then begin MinLen:=Nd^[j].MinL;

  q:=j;

  end;

  Nd^[q].Flag:=1;  { вершина q помечается просмотренной}

  for j:=1 to N do        { выполняем просмотр}

  if (Nd^[j].Flag=0) and (Nd^[j].MinL>Nd^[q].MinL+M[q]^[j])

 { Т.е. если для вершины j еще не найдено  кратчайшее расстояние

  от нач, и из вершины q по дуге M[q]^[j] путь в j короче, чем

  найденный ранее то запоминаем его}

  then begin Nd^[j].MinL:=Nd^[q].MinL+M[q]^[j];

  Nd^[j].Predok:=q;

  end;

  end;

        SumCur:=0;

        for i:=1 to N do SumCur:=SumCur+Nd^[i].MinL;

end;

begin

{Возьмем количество  дорог из командной строки}

if ParamCount=0 then begin

writeln('Не задано количество дорог в командной строке');halt;

                         end;

    Val(Paramstr(1),K,E);

    if E<>0 then begin

    writeln('Ошибка в задании числа дорог');halt;end;

    {Val(Paramstr(2),N,E);

    if E<>0 then begin

    writeln('Ошибка в задании числа дорог');halt;end;}

    {Отведем в куче память для массива K записей о дорогах }

    GetMem(P,K*Sizeof(Path));

    {Цикл ввода описаний дорог

    for i:=1 to K do

    readln(P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);}

 {Но для отладки лучше создать файл ,тк ввод съедает много

    времени}

Assign(f,'4.tst'); Reset(f);

    for i:=1 to K do

    readln(f,P^[i].FirstNode,P^[i].TwoNode,P^[i].PathLength);

    Close(f);

    {Количество домов придется подсчитать по данным о дорогах}

    if (P^[1].FirstNode<>P^[1].TwoNode)  then N:=2;

    for i:=2 to K do begin

    flag1:=0;flag2:=0;

    for j:=1 to i-1 do begin

    if (P^[i].FirstNode=P^[j].FirstNode) or

       (P^[i].FirstNode=P^[j].TwoNode)

       then flag1:=1;

    if   (P^[i].TwoNode=P^[j].FirstNode) or

         (P^[i].TwoNode=P^[j].TwoNode)

     then flag2:=1

                       end;

    if flag1=0 then N:=N+1;

    if flag2=0 then N:=N+1;

    end;

{Теперь можно отвести память для вспомогательной матрицы}

    for i:=1 to N do

    GetMem(M[i],N*N*SizeOf(Integer));

    {и для массива записей о вершинах}

    GetMem(Nd,N*Sizeof(Node));

 {Заполним вспомогательную матрицу смежности - вначале

 бесконечностями}

    for i:=1 to N do for j:=1 to N do M[i]^[j]:=MAXINT;

    {а затем длинами существующих между узлами дуг}

    for i:=1 to K do begin

    M[P^[i].FirstNode]^[P^[i].TwoNode]:=P^[i].PathLength;

    M[P^[i].TwoNode]^[P^[i].FirstNode]:=P^[i].PathLength;

                     end;

    Deikstra(1);

     SumPrev:=SumCur;

     MinN:=1;

    for x:=2 to N do begin

    beg:=x; Deikstra(beg);

    if SumCur<SumPrev then begin SumPrev:=SumCur; MinN:=x;end;

                     end;

    clrscr;

    writeln('Эта точка прямо в доме номер   ',MinN);

    writeln('Минимальная  сумма расстояний    ',SumPrev);

{    writeln('Минимальное расстояние от номера   ',beg);

    for i:=1 to N do

    writeln('До номера   ',i:10,'  ',Nd^[i].MinL);

                                                         }

     {Освобождаем память}

    FreeMem(P,K*Sizeof(Path));

    FreeMem(Nd,N*Sizeof(Node));

    for i:=1 to N do

    FreeMem(M[i],N*N*SizeOf(Integer));

end.

{Файл для тестирования:

5  17 5

1  17 11

17  18 1

16 0 2

0 1 4

2 3 3

3 4 4

4 5 3

5 6 4

6 7 3

7 8 10

8 9 5

9 10 2

10 11 7

11 12 3

12 13 7

13 14 1

14 15 12

15 16 7

16 17 5

6  4  8

11 13 8

17  19 15

19  20 5

16 20 10

14 20 7

20 21 3

10 21 12

11 21 4

21 22 1

9 22 7

7 22 8

19 22 10

}


Школьные задачи №9
Перебор вариантов - задача о цепочке слов

Предмет: Школьный курс информатики.

Тема:    Задачи на графах.

{ Задача:

 Пусть слово - это последовательность от 1 до 8 символов, не

 включающая пробелов.

    Вводится n слов A1,...,An.Можно ли их переупорядочить так,

 чтобы по- лучилась "цепочка", т.е. для каждого слова Aj  его

 первая буква должна совпадать с последней буквой предыдущего

 слова, а последняя буква в Aj  - с первой буквой последующего

 слова; соответственно последняя буква последнего слова должна

 совпадать с первой буквой первого слова. В цепочку входят все n

 слов без повторений.

Дать ответ в виде  "Можно"\"Нельзя".

 Если такое упорядочение возможно, то вывести какую-нибудь

 цепочку слов.  Слова при выводе разделяются пробелами .

 Общее решение

   В терминах теории графов от нас требуют определить

   возможность построения контура,включающего все вершины графа

   (слова) не более 1-го раза в последовательности,

   удовлетворяющей заданному условию.

   Другая формулировка:определить возможность преобразования

   заданного массива слов в кольцевой с последовательностью

   элементов,удовлетворя- ющих заданному условию.

   Вообще это задача на генерирование условных

   последовательностей, удовлетворяющих заданным условиям, из

   заданного набора элементов.  Она имеет много общего например

   с задачей поиска пути в лабиринте, с задачей расположения N

   не бьющих друг друга ферзей на доске размером N*N клеток и т

   д

   В самом общем случае каждое слово в заданном массиве может

   иметь несколько кандидатов на звание следующего или

   предыдущего элемента в результирующем контуре- может быть

   несколько слов ,начинающихся на последнюю или заканчивающихся

   на первую букву текущего слова.

   Поэтому для фиксации допустимых переходов при поисковой

   генерации последовательностей слов создадим вспомогательную

   матрицу по имени IM размером  N*N,элементам IM[i,j]:=1

   которой присвоим значения +1, если j-е слово может стоять

   после i-го IM[i,j]:=1 и -1 если j-e слово можно поставить

   перед i-m и 0, если j-e слово не может стоять ни до ни после

   i-го.( В теории графов такую матрицу называют матрицей

   инциденций.)

   Кроме того для записи результатов генерации

   последовательности слов создадим 2-мерный массив целых

   R,элементы которого R[k,1] будут содержать номер следующего

   слова в контуре по нумерации в исходном массиве слов а

   элементы R[k,2] -номер предыдущего (переставлять слова мы не

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

   k будет номер слова в контуре.

   Первоначально заполним его нулями,которые будут говорить

   нам о том,что номер текущего слова в контуре еще не определен

   (нумерацию самих слов мы начнем с 1).

   Количество уже установленных в контур слов будем хранит в

   переменной по имени С.

   Словесный алгоритм может выглядеть при этом так:

   В бесконечном цикле

 -отыскиваем в i-й строке  матрицы IM номер элемента j со

 значением +1.

 -если все элементы строки перебраны и положительный не найден

    begin

и если при этом i=1 то дальнейший поиск бесполезен,остается

вывести сообщение о невозможности построения контура и выйти

из процедуры;

если i>1,то необходимо установить i и j в предыдущие значения

    j:=i+1;

    i:=R[C,2];

    C:=C-1;

    и вернуться в начало цикла

    (рекурсивно вызвать текущую процедуру);

    end;

    -если  C=N-1(то есть мы анализируем последнее слово)

     и IM[i,1]=1,

    то решение найдено С:=N;R[N,1]:=1;R[1,2]:=j;

    печать результата и выход

    -иначе переходим к следующей строке

    C:=C+1;

    R[C,1]:=j;R[C,2]:=i;

    i:=j;j:=i+1;

    и в начало цикла(рекурсивный вызов).

   Дополнительно отметим неясность со значением числа слов

   n-придется делать программу на произвольное,задаваемое на

   стадии выполнения число слов.Мы не будем организовывать ввод

   числа n ,а зададим его в командной строке ДОС - оттуда и

   извлечем его в программу.  Хранение данных при этом придется

   организовать в "куче".

}

uses crt;

type

{}

StringArray=array[1..100] of string[10];

StringArrayPtr=^StringArray;

ResultArray=array[1..100,1..2] of integer;

ResultArrayPtr=^ResultArray;

IntArray=array[1..100] of integer;

IntArrayPtr=^IntArray;

IncidMatr=array[1..100] of IntArrayPtr;

var

i,j,k,l,m,C,N,E:integer;

IM:IncidMatr;

R:^ResultArray;

s:^StringArray;

f:Text;

procedure Contur;

begin

    repeat

    while (IM[i]^[j]<>1) and (j<N+1) do j:=j+1;

    if j=N+1 then begin

       if i=1 then begin

       writeln('Построить контур нельзя');halt;

                  end;

                  {Возвращаемся к предыдущей строке}

                  j:=i;

                  i:=R^[C,2];

                  R^[C,1]:=0; R^[C,2]:=0;

                  C:=C-1;

                  Contur;

                  end;

    R^[C,1]:=j;R^[j,2]:=i;

    if (C=N-1) and (IM[C+1]^[1]=1) then begin

{Предпоследнее слово и оно замыкается на первое}

   writeln('Контур возможен.Первый вариант:');

   write(s^[1],'  ');{Первая строка}

   for l:=1 to N-1 do write(s^[R^[l,1]],'  ');

   halt;

                                        end;

    {Переход к следующей строке}

    C:=C+1;

    {R^[C,1]:=j;R^[C,2]:=i;}

    i:=j;

    j:=1;

    Contur;

    until false;

end;

begin

    {Возьмем количество строк из командной строки}

if ParamCount=0 then begin

writeln('Не задано количество слов в командной строке');halt;

                         end;

    Val(Paramstr(1),N,E);

    if E<>0 then begin writeln('Ошибка в задании числа слов');

    halt;        end;

    {Отведем в куче память для массива в N строк }

    GetMem(s,N*10);

    {Для массива результатов генерации}

    GetMem(R,2*N*SizeOf(integer));

    {Для вспомогательной матрицы}

    for i:=1 to N do

    GetMem(IM[i],N*N*SizeOf(integer));

    {Цикл ввода слов

    for i:=1 to n do

    readln(s^[i]);}

 {Но для отладки лучше создать файл со словами,тк ввод съедает

 много времени}

    Assign(f,'3.tst');

    Reset(f);

    for i:=1 to N do readln(f,s^[i]);

    Close(f);

    {Начальные установки в массиве результатов}

    for l:=1 to N do

    for m:=1 to 2 do

    R^[l,m]:=0;

    {Заполним вспомогательную матрицу}

    for l:=1 to N do  for m:=1 to N do

    if (l<>m) and (s^[l][length(s^[l])]=s^[m][1]) then

    IM[l]^[m]:=1

    else IM[l]^[m]:=0;

    {Первое слово,первая строка в матрице и первый столбец}

    C:=1; i:=1;j:=1;

    {Вызываем процедуру генерации контура}

    Contur;

    {Освобождаем память}

    FreeMem(s,N*10);

    FreeMem(R,2*N*SizeOf(integer));

    for i:=1 to N do

    FreeMem(IM[i],N*N*SizeOf(integer));

end.

{

Простейший файл для тестирования:

обормот

работа

товар

авто

}


Школьные задачи №10
Максимальный прямоугольник

Предмет:Школьный курс информатики

Тема:   Геометрические задачи.

{ Задача:

   Нижняя левая и верхняя правая вершины прямоугольника A имеют

координаты (0,0) и (V,W) соответственно. Множество S из N точек

задается парами координат (x[i],y[i]), 1<=i<=N.

   Найти такой прямоугольник G максимальной площади, что его

стороны параллельны сторонам A, G полностью лежит в A (G и A

могут иметь общие граничные точки) и ни одна точка из S не лежит

внутри G (но может лежать на его стороне). Напечатать величину

площади G и координаты  нижней левой и верхней правой  вершин

этого прямоугольника.  Если таких прямоугольников несколько, то

вывести информацию по каждому.

Замечание: в множестве S никакие две точки не лежат на одной

прямой, параллельной стороне A.

  Необходимо:

  1. Организовать ввод данных в виде

 < Введите V и W -- координату верхней правой вершины

 прямоугольника A >

  < Введите N -- число точек >

  < Введите координаты точек >

    x[1]-->      y[1]-->

           .....

    x[N]-->      y[N]-->

  2. Вывести результаты в виде

  < Максимальная площадь прямоугольника = >

 < Координаты  нижней левой и верхней правой  вершин

 прямоугольника(ов) >

    x1-->     y1-->            x2-->     y2-->

Общее решение задачи

Вначале уточним,что собственно от нас хотят.

Кратко это выглядит так: внутри заданного прямоугольника

выделить другой,охватывающий все внутренние точки так,что все

его стороны проходят через одну из точек

-2 вертикальные стороны -через самую левую и самую правую,

-2 горизонтальные стороны-через самую верхнюю  и самую нижнюю .

Тогда прямые, на которых лежат стороны внутреннего

прямоугольника, образуют при пересечении со сторонами внешнего 4

прямоугольника, не содержащие внутри точек и среди них

необходимо выбрать один или более с наибольшей площадью (если их

несколько,то их площади очевидно равны.) В самом общем случае

искомых прямоугольников - 4,хотя среди них могут быть

прямоугольники с нулевой площадью при совпадении вершин и/или

сторон вешнего и внутреннего прямоугольников.

 0,W                          V,W

    +---|--------|-------------+   +---------2----------------+

    ¦   +---- ---+             ¦   ¦---+---- ---+-------------¦

    ¦   ¦      + ¦             ¦   ¦   ¦      + ¦             ¦

    ¦ 1 ¦        +     3       ¦   ¦   ¦        +             ¦

    ¦   |-+------|             ¦   ¦---+-+------+-------------¦

    ¦   |        |             ¦   ¦                          ¦

    ¦   |        |             ¦   ¦         4                ¦

0,0 +--------------------------+V  +--------------------------+

Пронумеруем прямоугольники по кругу,начиная с левого-#1,затем

верхний-#2,правый-#3 и нижний-#4.

Результат предлагается вывести в виде площади

самых больших прямоугольников и по 2 пары координат для каждого

из них(левых нижних и правых верхних вершин ).

}

uses crt;                            {Для cls}

type

{Точка описывается 2-мя координатами}

Point= Record x,y: real end;

{Прямоугольник опишем 2-мя точками нижней левой и верхней правой}

Rect=  Record PLeftDown,PRightTop:Point;

{Нам понадобится площадь пр-ка-пусть она будет одним из полей

записи характеризующей наши прямоугольники}

s:real;

{От нас хотят пр-ки наибольшей площади- введем

булевский признак для отметки, является ли текущий

прямоугольник самым большим}

max:boolean

end;

var

R:array[1..4] of Rect; {Массив из 4-х прямоугольнтков}

V,W,                   {Координаты верхнего правого угла

                        внешнего прямоугольника}

 x,y,xmin,xmax,ymin,ymax:real; {"Экстремальные" значения

                                 координат вводимых точек}

i,j,N:integer;          {N-для количества точек}

flag:boolean;           {Флаг первого действия}

begin

clrscr;

 writeln('Введите V и W -- координаты верхней правой вершины

 прямоугольника A >');

readln(V,W);{Ввод правой верхней точки внешнего прямоугольника}

{Количество точек - ввод}

writeln('Введите N -- число точек');

readln(N);

{Координаты точек - ввод с отбором  крайних внутри пр-ка}

writeln('Введите координаты точек');

flag:=false; {Для индикации первой точки-пока не было ввода}

for i:=1 to N do

 {Нам нужны будут только экстремальные точки-остальные мы

 игнорируем}

begin

readln(x,y);

if (x >= 0) and (x <= V)          {Внутри?}

and (y <= W) and (y >= 0)

then  begin

if flag=false then  {Если это первая точка то сравнивать ее

                     не с чем и мы считаем ее координаты

                     экстремальными }

begin

xmin:=x;xmax:=x; ymax:=y; ymin:=y;

flag:=true; {Взводим флажок,чтобы больше сюда не заходить}

end;

{Координаты вершин исследуемых пр-ков,определяемые координатами

отбираемых крайних точек}

  {Правая абсцисса для 1-го}

  if x<xmin then xmin:=x;R[1].PRightTop.x:=xmin;

  {Левая ордината для 2-го}

  if y>ymax then ymax:=y;R[2].PLeftDown.y:=ymax;

  {Левая ордината для 3-го}

  if x>xmax then xmax:=x;R[3].PLeftDown.x:=xmax;

  {Правая ордината для 4-го}

  if y<ymin then ymin:=y;R[4].PRightTop.y:=ymin;

     end;

end;

{Значения координат прямоугольников,известные нам по условию}

R[1].PLeftDown.x:=0;

R[1].PLeftDown.y:=0;

R[1].PRightTop.y:=W;

R[2].PLeftDown.x:=0;

R[2].PRightTop.x:=V;

R[2].PRightTop.y:=W;

R[3].PLeftDown.y:=0;

R[3].PRightTop.x:=V;

R[3].PRightTop.y:=W;

R[4].PLeftDown.x:=0;

R[4].PLeftDown.y:=0;

R[4].PRightTop.x:=V;

{Определив все 16 координат,вычислим площади прямоугольников}

for i:=1 to 4 do begin

R[i].s:=

(R[i].PRightTop.x-R[i].PLeftDown.x)*

(R[i].PRightTop.y-R[i].PLeftDown.y);

R[i].max:=false;

{writeln(R[i].s);}

end;

{Найдем и пометим прямоугольники максимальной площади}

N:=1;           {Используем уже ненужную переменную N}

R[N].max:=true; {Предположим,что 1-й самый большой}

for i:=2 to 4 do

if R[i].s >= R[N].s then

if R[i].s <> R[N].s then begin  {Если площади не равны

                                то наибольший только i-й}

for j:=1 to i-1 do R[j].max:=false;

N:=i;

R[N].max:=true;

end

else R[i].max:=true;     {Этот тоже самый большой}

{Выполним вывод результатов}

write('Максимальная площадь прямоугольника =  ');

flag:=true;           {Чтобы площадь вывести только раз даже при

                       наличии равновеликих прямоугольников}

for i:=1 to 4 do

begin

if (R[i].max=true) and (flag=true) then

    begin writeln(R[i].s:8:2);flag:=false;

     writeln('Координаты  нижней левой и верхней правой  вершин

     прямоугольника(ов)');

    end;

if R[i].max=true then

writeln(' x1-->',R[i].PLeftDown.x:8:2,

' y1-->',R[i].PLeftDown.y:8:2,' x2--> ',

R[i].PRightTop.x:8:2,' y2-->',R[i].PRightTop.y:8:2);

end;

end.

 {Это вариант без ввода с клавиатуры - количество точек

 задаются в командной строке а координаты  правой верхней и

 обрабатываемых точек мы возьмем из файла  с тем же именем что и

 программа но с расширением .tst - содержит данные для

 тестирования работоспособности}

uses crt;                            {Для cls}

type

{Точка описывается 2-мя координатами}

Point= Record x,y: real end;

{Прямоугольник опишем 2-мя точками-нижней левой и верхней

 правой}

Rect=  Record PLeftDown,PRightTop:Point;

{Нам понадобится площадь пр-ка-пусть она будет одним из полей

 записи, характеризующей наши прямоугольники}

s:real;

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

 для отметки, является ли текущий прямоугольник самым большим}

max:boolean end;

var

f:Text;

R:array[1..4] of Rect;  {Массив из 4-х прямоугольнтков}

V,W,                    {Координаты верхнего правого угла

                        внешнего прямоугольника}

{"Экстремальные" значения координат вводимых точек}

 {N-для количества точек}

x,y,xmin,xmax,ymin,ymax:real;

i,j,N:integer;

ErrorCode:integer;

flag:boolean;            {Флаг первого действия}

begin

clrscr;

 if ParamCount<>2 then    {Мы предусмотрели 2 параметра в ком

                          строке -имя файла и колич точек }

 begin

  writeln('Должно быть при запуске <имя программы> <имя файла>

  <кол точек >');halt;

 end;

  Val(ParamStr(2),N,ErrorCode); {Слово 2 преобразуем в целое

  число точек}

 if ErrorCode<>0 then begin

  writeln('Какая-то ощибка во втором слове командной строки-кол

  строк не преобразуется в целое');halt;

                      end;

 { откроем файл}

 Assign(f,ParamStr(1));

  {$i-}    {отключаем контроль правильности ввода вывода}

 Reset(f);

  {$i+}    {восстанавливаем}

  if IOResult <> 0 then begin writeln('Файл ',ParamStr(1),

                              'не найден');halt;

                        end;

  readln(f,V,W);

  flag:=false;{Для индикации первой точки-пока не было чтения}

  {Читаем из файла в массив}

  for  i:=1 to N do

     begin

 {Нам нужны будут только экстремальные точки-остальные мы

 игнорируем}

             readln(f,x,y);

             if (x >= 0) and (x <= V)          {Внутри?}

                and (y <= W) and (y >= 0)

               then  begin

               if flag=false then

        {Если это первая точка то сравнивать ее

         не с чем и мы считаем ее координаты

         экстремальными }

begin

xmin:=x;xmax:=x; ymax:=y; ymin:=y;

flag:=true; {Взводим флажок,чтобы больше сюда не заходить}

end;

{Координаты вершин исследуемых пр-ков,определяемые координатами

 отбираемых крайних точек}

{Правая абсцисса для 1-го}

if x<xmin then xmin:=x;R[1].PRightTop.x:=xmin;

{Левая ордината для 2-го}

  if y>ymax then ymax:=y;R[2].PLeftDown.y:=ymax;

{Левая ордината для 3-го}

  if x>xmax then xmax:=x;R[3].PLeftDown.x:=xmax;

{Правая ордината для 4-го}

  if y<ymin then ymin:=y;R[4].PRightTop.y:=ymin;

     end;

end;

  close(f);

{Значения координат прямоугольников,известные нам по условию}

R[1].PLeftDown.x:=0;

R[1].PLeftDown.y:=0;

R[1].PRightTop.y:=W;

R[2].PLeftDown.x:=0;

R[2].PRightTop.x:=V;

R[2].PRightTop.y:=W;

R[3].PLeftDown.y:=0;

R[3].PRightTop.x:=V;

R[3].PRightTop.y:=W;

R[4].PLeftDown.x:=0;

R[4].PLeftDown.y:=0;

R[4].PRightTop.x:=V;

{Определив все 16 координат,вычислим площади прямоугольников}

for i:=1 to 4 do begin

R[i].s:=(R[i].PRightTop.x-R[i].PLeftDown.x)*

(R[i].PRightTop.y-R[i].PLeftDown.y);

R[i].max:=false;

end;

{Найдем и пометим прямоугольники максимальной площади}

N:=1;    {Используем уже ненужную переменную N}

R[N].max:=true;        {Предположим,что 1-й самый большой}

for i:=2 to 4 do

if R[i].s >= R[N].s then

                      if R[i].s <> R[N].s then begin

{Если площади не равны то наибольший только i-й}

for j:=1 to i-1 do R[j].max:=false;

N:=i;

R[N].max:=true;

end

else R[i].max:=true;     {Этот тоже самый большой}

{Выполним вывод результатов}

write('Максимальная площадь прямоугольника =  ');

flag:=true;           {Чтобы площадь вывести только раз даже при

                       наличии равновеликих прямоугольников}

for i:=1 to 4 do

begin

if (R[i].max=true) and (flag=true) then

    begin writeln(R[i].s:8:2);flag:=false;

     writeln('Координаты  нижней левой и верхней правой  вершин

     прямоугольника(ов)');

    end;

if R[i].max=true then

writeln(' x1-->',R[i].PLeftDown.x:8:2,

' y1-->',R[i].PLeftDown.y:8:2,' x2--> ',

R[i].PRightTop.x:8:2,' y2-->',R[i].PRightTop.y:8:2);

end;

end.

 {

Файл для тестирования:

31 44

21 32

18 4

3 32

13 18

1 30

 }


Школьные задачи №11
Фальшивая монета

Предмет: Школьный курс информатики.

Тема:    Нестандартные задачи.

Задача: На рычажных весах не более чем за 3 взвешивания

определить среди 12 монет фальшивую, отличающуюся по массе в

неизвестную сторону. Попробуйте сами найти решение этой задачи -

оно нетривиально, а мы приведем алгоритм из книги Уинера:

- Кладем на чашки по 4 монеты - если весы уравновешены, то

фальшивая монета среди 4-х оставшихся и мы сравним 2 из них с

2-мя заведомо хорошими; если весы уравновешены, сравним одну из

оставшихся с хорошей и определим фальшивую.

   Иначе фальшивая - одна из 2-х потенциально фальшивых на весах

   и мы сравним одну из них с хорошей и определим фальшивую;

Иначе 4 оставшиеся  - хорошие и мы поместим по 2 из

перевешивающей группы и по 1 из недовешивающей на обе чашки

весов; если весы уравновешены,, то фальшивая - среди 2- х

оставшихся и сравнив ону из них с хорошей мы найдем фальшивую;

   Иначе фальшивая - либо одна из 2-х тяжелых на нижней чашке

   весов или легкая на верхней чашке; сравним 2 тяжелые и если

   весы уравновешены, то фальшивая - оставшаяся легкая, иначе

   монета на нижней чашке фальшивая и она тяжелая.

Приводится эскиз программной реализации алгоритма на Си, в

котором веса монет это 12 целых чисел в составленном вами

текстовом файле - 11 одинаковых, а одно больше или меньше на

произвольном месте. Имя файла - в команной строке. Ваша задача -

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

моделированием взвешивания вместо приведенного метода

суммирования - пусть пользователю сообщаются номера монет на

чашках весов, а он вводит номер перевесившей чашки или 0 при

уравновешивании.

#include <stdio.h>

main(int argc,char *argv[])

{

 FILE *f;

 int money[12];                  //массив весов монет

 int i,sum1,sum2;

 if(argc!=2) {printf("Вызов: имя программы имя файла");

 exit(1);}

 if((f=fopen(argv[1],"rt"))==NULL)

 {perror("\a Ошибка открытия файла"); exit(2);}

 for(i=0;i<12;i++) fscanf(f,"%d",money+i);

 fclose(f);

sum1=sum2=0;

 //Кладем по 4 монеты на чашки весов

for(i=0;i<4;i++) {sum1+=money[i]; sum2+=money[i+4]];}

if(sum1==sum2) //если весы уравновешены -фальш среди 8-11

{

sum1=money[0]+money[1]; //хорошие

sum2=money[8]+money[9]; //2 подозрительные из 4-х оставшихся

if(sum1==sum2) //если весы уравновешены - подозрение не обоснов

{

sum1=money[0]; //хорошая

sum2=money[10];//подозрит

if(sum1==sum2) printf("\nФальшива 11-я");

else printf("\nФальшива 10-я");

}

else          //не уравновеш - подозрит 8-я и 9-я

{

sum1=money[0];//хорошая

sum2=money[8];//подозреваемая

if(sum1==sum2) printf("\nФальшива 9-я");

else printf("\nФальшива 8-я"); }

}

}

else        //4 оставшиеся хорошие

{

if(sum1>sum2)    //легкие в sum2

{

sum1=money[0]+money[1]+money[4];  //2 тяж и 1 легкая

sum2=money[2]+money[3]+money[5];  //2 тяж и 1 легкая

if(sum1==sum2)                    //фальшивая среди оставш 2-х легких

{

sum1=money[0]; //хорошая

sum2=money[6]; //подозр

if(sum1==sum2) printf("\nФальшива 7-я");

else printf("\nФальшива 6-я"); }

}

else if(sum1>sum2)//Фальшива либо среди 0-1 либо 2-я

{

 if(money[0]==money[1]) printf("\nФальшива 2-я");

 else

{

 if(money[0]>money[1])  printf("\nФальшива 0-я");

 else                   printf("\nФальшива 1-я");

}

}

}

else    //sum1<sum2 легкие в sum1

{

sum1=money[0]+money[4]+money[5];  //1 легкая и 2 тяж

sum2=money[1]+money[6]+money[7];  //1 легкая и 2 тяж

if(sum1==sum2)

{

sum1=money[2];                     //подозр

sum2=money[6];                     //хорошая

if(sum1==sum2) printf("\nФальшива 3 -я");

else printf("\nФальшива 2-я"); }

}

else if(sum1>sum2)//Фальшива либо среди 4-5 либо 1-я

{

 if(money[4]==money[5]) printf("\nФальшива 1-я");

 else

{

 if(money[4]>money[5])  printf("\nФальшива 4-я");

 else                   printf("\nФальшива 5-я");

}

}

}

}

}


Школьные задачи №12
Упорядоченный перебор и задача о 8-ми ферзях

Предмет: Школьный курс информатикки.

 Тема:    Перебор вариантов продолжения поиска решения и

 рекурсивные вызовы.

 Задача:  Найти все возможные варианты расположения N не бьющих

 друг друга ферзей на шахматной доске NxN клеток. Можно

 переформулировать ее так: в матрице NxN найти все возможные

 конфигурации из N элементов, не имеющих общих столбцов, строк и

 диагоналей.

 Для N=8 таких вариантов 320.Эта задача описана во многих учебниках

 и периодически возникает на олимпиадах в различных модификациях.

 Мы приведем ее решение на Паскале для доски 8х8 из книги

Э.Дейкстры "Структурное программирование", а вам предлагается

обобщить ее решение на доску NхN, где N вводится в командной

строке при пуске программы и выполнить Си- и Бейсик- варианты.

Для обобщения решения вам придется связать число

восходящих и нисходящих диагоналей с количеством полей в

горизонталях и вертикалях, а при реализации на Си преодолеть

неудобство при задании массива восходящих диагоналей из-за

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

Такого рода задачи не решаются аналитически, к этому классу

по методологии решения можно отнести и задачи поиска путей в

лабиринтах и много других. Решение сводится к последовательному

перебору возможных вариантов и возврату к предыдущей позиции с

модификацией продолжения при отсутствии решения.

Program Configuration;

var

k,i:integer;              {}

x:array[0..7] of integer; {Массив для размещения решений - x[i]

это номер вертикали, занимаемой ферзем из i-й горизонтали.  }

stl:array[0..7] of boolean; {массив признаков занятости

                             вертикалей}

ds:array[0..14] of boolean; {Массив признаков занятости

                             нисходящих диагоналей}

dr:  array[-7..7] of boolean;{Массив признаков занятости

                              восходящих диагоналей}

Procedure Generation;

var

j:integer;c:char;

begin j:=0;

repeat

{если эл-т свободен }

if (stl[j] and dr[i-j] and ds[i+j])=true then

  begin

{выбрать элемент - установить ферзя в поле i,j}

x[i]:=j;stl[j]:=false;dr[i-j]:=false;ds[i+j]:=false;i:=i+1;

{если доска заполнена}

if i=8 then begin

{печать конфигурации}

k:=0;repeat write(x[k]:2);k:=k+1;until k=8;writeln  end

{иначе генерировать}

else  Generation;

i:=i-1;

{освободить эл-т - убрать ферзя с поля i,j}

stl[j]:=true; dr[i-j]:=true;  ds[i+j]:=true;

       end;

j:=j+1;{пробуем следующий столбец}

until j=8;

end;  {конец процедуры}

begin

i:=0;

k:=0;repeat stl[k]:=true;k:=k+1;until k=8;

k:=0;repeat ds[k]:=true;dr[k-7]:=true;k:=k+1;until k=15;

Generation;

end.


Школьные задачи №13
Расшифровка шифрограмм по ключу

Предмет: Школьный курс информатики

Тема:    Нестандартные задачи

{ЗАДАЧА:

  Алиса ведет совеpшенно секpетную пеpеписку с Бобом по

электpонной почте.  Не pасчитывая на конфиденциальность линий

связи, Алиса шифpует весь текст своего послания посимвольно пpи

помощи следующего алгоpитма:

  T(0)=C

  T(i)=(A*T(i+1)+B mod 256

  Y(i)=X(i) (+) T(i)      i=1,2,3,...,

  где X(i) - код ASCII i-го символа откpытого текста,

     Y(i) - код i-го символа шифpотекста,

      A,B,C - секpетные коэффициенты, известные только Алисе и

      Бобу. Пpичем

    ( 0<A,B,C<256 )

    (+) - опеpация сложения по модулю 2 (исключающее "или").

  Известно, что для надежности каждое свое сообщение Алиса

посылает в двух ваpиантах, пеpедавая один из них 10-символьной

стpокой "TOP SECRET"; оба ваpианта шифpуются одинаковым

способом. Таким обpазом, Боб после pасшифpо- вания обоих

ваpиантов получает откpытый текст, отличающийся только наличием

пеpвой фpазы "TOP SECRET" в одном из ваpиантов.  Несмотpя на

все меpы пpедостоpожности пpоисходит утечка инфоpмации. Найдите

изьян в указанной схеме шифpования.

    Составьте пpогpамму ALISA, котоpая

 1. Считывает пеpвый ваpиант шифpотекста Алисы из текстового

 файла ALISA1.DAT.

 2. Считывает втоpой ваpиант шифpотекста Алисы из текстового

 файла ALISA2.DAT.

3. Дешифpует шифpотекст в откpытый текст.

4. Записывает полученный откpытый текст без пеpвой фpазы "TOP

SECRET" в текс- товый файл ALISA.SOL.

   Откpытый текст пpедставляет собой последовательность символов

ASCII. Длина откpытого текста не пpевосходит 32000 символов.

Шифpотекст пpедставляет собой последовательность десятичных

чисел Y(i), pазделенных пpобелами.

    Пpимеp

  ALISA1 .DAT

 86  24  73 106  56 124  62 109  43 120 49  71  37  86  35  93

115  48  68  40  89  46  86 119  11 105 30  63  47  94  127 225

194 180 212 185 214 249 247 242 187 201 167 216 173 215 245 178

198 170 219 168 208 241 154 250 159 128 224 193 165 212 176 208

165 134 225 131 232 133 168 164 175

 ALISA2.DAT

 74  62  82  35  80  40   4  65  55  85  38  83  45  14 124  24

109  78  32  79 112  16  49  69  39  68  41   4   8   3  76  56

 84  37  82  42   6  67  49  95  16 101  31   0 105  11 101  73

 43  12 106  25 127 225 150 183 222 190 211 184 151 149 156

  ALISA.SOL

Humpty-Dumpty set on a wall.

Humpty-Dumpty hai/a great mPll.

}

{

Алгоритм решения

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

 шифровали-операцией XOR между шифровкой и кодирующими

 числами.Так как эти числа зависят только от позиции кодируемого

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

 "коэффициенты Алисы"-мы можем  или просто "подобрать" эти числа

 для каждой позиции при известной ключевой последовательгости

 как в нашем случае или получить используя XOR между

 кодированными и некодированными символами

}

uses crt;

var

f1,f2,f3:Text;   {Переменные для текстовых файлов}

T,l2:Byte;

X1,Y1,Y2:array[0..255] of byte;

s,s1:string;

i:word;

begin

S:='TOP SECRET';

Assign(f1,'Alisa1.dat');

Assign(f2,'Alisa2.dat');Assign(f3,'Alisa.sol');

Reset(f1);Reset(f2);Rewrite(f3);

 i:=1;

 while  not SeekEof(f1) do begin

 read(f1,Y1[i]); i:=i+1;   end;

 close(f1);

i:=1;

 while  not SeekEof(f2) do begin

 read(f2,Y2[i]); i:=i+1;   end;

 l2:=i-1;

 close(f2);

 for i:=1 to l2 do begin

 X1[i]:=Ord(s[i]);

 T:= X1[i] xor Y1[i];

 T:= T+(Y2[i-1]-Y1[i-1]);

 S:=S+chr(Y2[i] xor T);

 S1:=S1+chr(Y2[i] xor T);

                    end;

 write(f3,s1);

 close(f3);

end.


Школьные задачи №14
Восстановление искажений по шаблонам

Тема   : Нестандартные задачи

{ЗАДАЧА:

    Обозначим мысленную команду пилота  последовательностью

символов из неко- тоpого множества S (в качестве S можно

выбpать, напpимеp, множество, состоя- щее из больших pусских

букв и пpобела). Пpедположим, что все искажения бази- pуются на

следующих тpех элементаpных помехах:

а) выпадение символа (напpимеp, "говоpю" > "гооpю");

б) вставка пpоизвольного символа из S (напpимеp, "гоpю" >

"гоpюю");

в) замена символа на pодственный (напpимеp, "один" > "адин").

Опpеделим степень сходства двух символьных последовательностей

как наибольшую длину общей последовательности, состоящей из

совпадающих либо pодственных символов обеих последовательностей.

Пpи этом совпадающий символ имеет длину l, а pодственный - длину

l/2.

Напpимеp:

ГОВОРЮ    ГОРЮ   4

--  --

ГОВОРЮ   ГАОРЮ   4.5

-- ---

ГОВОРЮ   ГООРЮ   5

-- ---

ГОРЮ     ГАОРЮ   4

         - ---

ГОРЮ     ГООРЮ   4

         -- --

Здесь пеpвый столбец содеpжит эталоны, втоpой - pеальные

команды,  а тpетий - степени сходства; подчеpкиванием обозначены

символы общей последо- вательномсти для каждой паpы команд.

     Составьте пpогpамму CHMYS,котоpая

1. Считывает множество S из текстового файла CHMYS.SET.

2. Считывает последовательность паp pодственных символов из

файла CHMYS.MAP.

3. Считывает множество эталонных команд из фаяла CHMYS.VOC.

4. Считывает последовательность pеальных команд пилота из файла

CHMYS.DAT.

5. Для каждой "pеальной" команды находит ближайшую к ней по

степени сходства эталонную команду (если таковых оказалось

несколько, то - любую из них).

6. Выводит в текстовый файл CHMYS.SOL последовательность

pаспознанных эталонных команд.

      Исходные данные и pезультаты записываются в файл следующим

      обpазом:

  CHMYS.SET  содеpжит стpоку символов - элементов множества S;

            длина стpоки не пpевосходит 255;

  CHMYS.MAP  содеpжит последовательность стpок, в каждой из

            котоpых - паpа символов C1 C2, что означает

            возможность замены символа C1 на символ C2 в

            эталонной команде с целью получения совпадения с

            символом pеальной команды; количество паp не

            пpевосходит 100;

  CHMYS.VOC  содеpжит последовательность символьных стpок -

            эталонных команд, на основе котоpых пpоизводится

            pаспознавание pеальных команд;

  CHMYS.DAT  содеpжит последовательность символьных стpок

            -pеальных команд, подлежащих pаспознаванию;

 CHMYS.SOL  в каждую стpоку этого файла помещается pезультат

            pаспознавания соответствующей pеальной команды из

            файла CHMYS.DAT, так что пеpвая стpока файла

            CHMYS.SOL соответствует пеpвой стpоке файла

            CHMYS.DAT, втоpая - втоpой и т.д.; каждая стpока

            имеет вид: d n c, где d - степень сходства pеальной

            команды и pаспознанной эталонной команды, n - ее

            поpядковый номеp в файле CHMYS.VOC, c - сама

            эталонная команда; все тpи элемента d,n и c

            pазделены пpобелами.

 Пpимеp:

 CHMYS.SET

 абвгдежзиклмнопpстуфхцчшщьыъэюя

 CHMYS.MAP

 оа

 вф

 pг

 CHMYS.VOC

 один

 два

 тpи

 четыpе

 пятьдесят

 шестьдесят

 говоpю с сильным акцентом

 гиpю сильному дай ланцелоту

 гоpе всесильное побеждай ланцетом

 CHMYS.DAT

 адин

 дыва

 тыpи

 читыpи

 пиисят

 шыисят

 шиисят четыpи

 гоpю синим ацетоном

CHMYS.SOL

3    1  один

3    2  два

3    3  тpи

4    4  четыpе

4    5  пятьдесят

4    6  шестьдесят

5    4  четыpе

16   7  говоpю с сильным акцентом

 }

 uses crt;

 type

 ArrStr=array[1..100] of string[50];

 var

 fset,fmap,fvoc,fdat,fsol:Text;

 SetStr:String;           {Для элементов множества S}

{Для пар родственных символов}

 ArrMapStr:array[1..100] of string[2];

 ArrVocStr,ArrDatStr:^ArrStr;{Для эталонных и реальных команд}

 VocCount,DatCount,MapCount:Byte;{Количества команд,map-строк}

 RabVocStr,RabDatStr:String[50]; {Рабочая строки для  лексем}

  RabStepen,MaxStepen:Word;       {Рабочие ячейки для степеней

                                  совпад строк}

 i,j,MaxNum:integer;

 {Составим стартовую процедуру для подготовки данных в памяти}

 procedure start;

 begin

 Assign(fset,'chmys.set');Reset(fset);

 Assign(fmap,'chmys.map');Reset(fmap);

 Assign(fvoc,'chmys.voc');Reset(fvoc);

 Assign(fdat,'chmys.dat');Reset(fdat);

 Assign(fsol,'chmys.sol');Rewrite(fsol);

  while not SeekEOF(fvoc) do      {Определяем количество строк в

                                  chmys.voc}

 begin read(fvoc,RabVocStr);

 VocCount:=VocCount+1;end;

 Reset(fvoc);

   while not SeekEOF(fdat) do      {Определяем количество строк

                                    в chmys.dat}

 begin read(fdat,RabDatStr);DatCount:=DatCount+1;end;

 Reset(fdat);

 GetMem(ArrVocStr,VocCount*51);{Выделяем в куче память}

 GetMem(ArrDatStr,DatCount*51);

  read(fset,SetStr);Close(fset);  {Читаем множество Set,хотя

                                  осмысленного применения я ему

                                  не нашел}

 MapCount:=1;

  while not SeekEOF(fmap) do      {Читаем и пределяем количество

                                   строк в chmys.map}

 begin read(fmap,ArrMapStr[MapCount]);MapCount:=MapCount+1;end;

 Reset(fmap);MapCount:=MapCount-1;

 for i:=1 to VocCount do         {Читаем chmys.voc}

 readln(fvoc,ArrVocStr^[i]);

 Close(fvoc);

 for i:=1 to DatCount do         {Читаем chmys.dat}

 readln(fdat,ArrDatStr^[i]);

 Close(fdat);

 end;

 {Составим функцию для поиска родственного заданному символа в

 массиве map-строк}

 function map(ch:char):Byte;

 var

 k:integer;

 begin

  for k:=1 to MapCount do

  if ch=ArrMapStr[k][1] then begin map:=1;exit;end;

  map:=0;

 end;

 {Составим функцию для определения степени сходства 2-х строк,

 одна из которых лежит в RabVocStr,а другая в RabDatStr Просмотр

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

 соответствующего или родственного символа в соответствующей

 позиции реальной строки.  }

 function stepen:Byte;

 var

 k,l,s:integer;

 b:Byte;

 chDat,chVoc:char;

 begin

 {Надо бы привести к одному регистру обе строки но не хочется

 писать процедуру,а библиотечная работает только с латиницей}

 s:=0;

   for k:=1 to Length(RabVocStr) do

   if Length(RabDatStr)>0 then   {Если в раб строке еще есть

                                  необраб символы}

   begin

       chDat:=RabDatStr[1];chVoc:=RabVocStr[k];{Взяли по символу

                               из реальной и эталонной строк в

                               соответствующих позициях}

        if chDat=chVoc then       {Если сравнились-наращиваем

                                   степень }

       begin s:=s+2;

       Delete(RabDatStr,1,1);  {Удаляем 1-й символ раб строки}

       continue;end;

       b:=map(chVoc);

       if b>0 then begin         {Найден родственный}

       s:=s+b;

       Delete(RabDatStr,1,1);

       continue;end;

       Delete(RabDatStr,1,1);

       k:=k-1;          {Плохой стиль,но быстро не придумал

                        лучшего-нам надо оставить цикловую

                        переменную без наращивания -может

                        следующий символ совпадет с эталонным

                        после удаления чужого вставленного в

                        рабочую строку по ошибке}

      end;

   stepen:=s;

 end;

 begin

 clrscr;

 start;

 for i:=1 to DatCount do begin

  RabDatStr:=ArrDatStr^[i];     {Берем в раб строку по одной

                                реальной команде}

 MaxStepen:=0;MaxNum:=0;

 for j:=1 to VocCount do begin

  RabVocStr:=ArrVocStr^[j];{Берем в раб строку по одной

                            эталонной команде}

 RabStepen:=stepen;    {Определяем степень совпадения}

 RabDatStr:=ArrDatStr^[i];{Восстановим после ф-ции stepen}

 if RabStepen>MaxStepen then   {Ищем максим степень}

 begin MaxStepen:=RabStepen;MaxNum:=j;end;

                         end;

 writeln(fsol,

 RabDatStr,'':25-Length(RabDatStr),

 MaxStepen:3,

 MaxNum:3,'  ',

 ArrVocStr^[MaxNum]);

                end;

 Close(fsol);

 FreeMem(ArrVocStr,VocCount*51);{Выделяем в куче память}

 FreeMem(ArrDatStr,DatCount*51);

 end.


Школьные задачи №15
Рекурсия и задача о ханойской башне

 Тема: Рекурсивные вызовы подпрограмм - это прямое или

 опосредованное обращение подпрограммы к самой себе -

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

 экземпляра этой же подпрограммы . В программах рекурсий следует

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

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

 подготовке решения ряда задач рекурсия приводит к прозрачному

 легко читаемрму виду. Мы рассмотрим это на одной из

 классических задач - так называемой "Ханойской башни" -

 требованию перенести все N дисков с отверстиями, насаженных на

 стержень А в убывающими к вершине диаметрами, на стержень В с

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

 стержень С; при этом запрещается класть боольший диск на

 меньший. В рекурсивной интерпретации алгоритм

 Р(N,A,B,C) составляется просто:

 P(N,A,B,C)

 {

 Если N==1 пренести верхний диск со стержня А на В

 иначе P(N-1,A,C,B);

       P(1,A,B,C);

       P(N-1,C,B,A);

 }

 В словесном изложении это выглядит так: при одном

 диске на стержне А перенести его на В, но если на А больше

 одного, надо перенести временно N-1 верхних дисков на С,а потом

 один оставшийся на В и наконец перенести N-1 дисков с стержня С

 на В. Для выполнения этой последней операции годится тот же

 алгоритм с уменьшенным на 1 числом дисков и применяя его

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

 На Паскале это выглядит так:

 program Hanoj;

 var M: integer;

 procedure P(N:integer;A,B,C:char);

 begin

  if N=1 then write(A,'->',B,' ')

  else begin

  P(N-1,A,C,B); P(1,A,B,C);P(N-1,C,B,A);

       end;

  end;

  begin readln(M); write('N= ',M;); P(M,'A','B','C'); end;

 Алгоритм без рекурсий можно записать так:

 Пока задача не решена, повторять:

 Первый диск переложить по часовой стрелке.

 Следующий переложить на больший диск (если остался один, то

 переложить его на пустой стержень, а если ни одного, то задача

 решена).

 Запрограммируйте это.

 Для четного N диски переложатся на соседний стержень по часовой

 стрелке, для нечетног - против. Для переноса N дисков надо не

 менее 2^N-1 переносов (докажите).


Школьные задачи №16
№15


Школьные задачи №17
№16


Дополнения к языку Си

      В целом, С++ является над-множеством языка С.  Это  означает,

 что,  вообще  говоря, можно компилировать программы С в среде С++,

 однако компилировать программы С++ в среде С при наличии в них ка-

 ких-либо специфических для С++ конструкций нельзя. Некоторые ситу-

 ации требуют специального внимания. Одна и  та  же  функция  func,

 дважды объявленная в С с различными значениями аргументов, вызовет

 ошибку  повторения  имен.  Однако, в С++ func интерпретируется как

 перегруженная функция - а то, допустимо это или  нет,  зависит  от

 других  обстоятельств.

      Хотя  С++ вводит новые ключевые слова и операции для работы с

 классами, некоторые средства С++ применимы вне контекста  классов.

 Сначала  мы рассмотрим тиенно эти, используемые незвисимо от клас-

 сов средства, а затем займемся спецификой работы с классами и свя-

 занными с ними механизмами.

   1.Дополнения к С,которые могут использоваться в традиционном

   процедурном программировании

   1.1.Ограничители комментариев

   Для упрощения ввода комментариев для строк введен дополнительный

   ограничитель в виде двойной косой черты //,текст после которого

   до перевода строки воспринимаается как комментарий и не обрабаты-

   вается компилятором.Старый ограничитель /*...*/тоже сохранен с

   возможностью вложенности в него новых однострочных комментариев.

   1.2.Прототипы функций

   Объявление прототипа функций в С++ является обязательным и служит

   для проверки соответствия типов формальных и фактических парамет-

   ров при вызове функции на выполнение-это позволяет избежать многих

   труднообнаружимых ошибок.Объявление прототипа выполняется по следу-

   ющему шаблону:

        возвращаемый_тип имя_функции(тип_парам1,...,тип_рарамi);

   Имена параметров при типах могут быть,но не обязательны.

   Для функций без параметров в скобках указывается тип void:

                void funci(void);

   1.3.Объявление переменных в любом месте программы,в том числе внутри

   операторов

   Неудобство,связанное в С с необходимостью объявлять переменные до

   первого оператора функции,устранено в С++:теперь вы можете максимально

   приблизить объявление переменной к месту ее первого использования.

                for(int index=0;index<10;index++) {...}

   Область действия переменной index -до конца блока ,в котором размещен

   цикл,например до конца функции.

   1.4.Операция расширения области видимости ::.

   Если у вас есть переменные с одинаковыми именами и различными областями

   видимости(в больших программах этого трудно избежать) и вам понадобился

   доступ к переменной объемлющего блока-используйте операцию ::

   float r=2.6;                  //глобальная

   int incr(int k)

   {int r=k+15;return r;}        //возвращается локальная r

   main()

   {int r=incr(20);printf("%d",::r);} //наращивается локальная,а печатается

                                      //глобальная r

   1.5.Перегрузка функций(использование нескольких функций с одинаковыми

   именами)

   Это бывает удобно,если есть набор функций,реализующих один и тот же

   алгоритм над различными типами данных и имеющих имя,отражающее содер-

   жание алгоритма.Чтобы компилятор С++ различал такие функции,необходимо

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

   рядку следования списки параметров:

        int noName(int first) {return first*first;}

        int noName(int first,char* second) {return first*strlen(second);}

        float noName(float r)   {return r*r;}

   1.6.Значения формальных параметров функций по умолчанию

   В С++ ряду последних в списке формальных параметров могут быть присвоены

   значения по умолчанию-это означает,что если вы при вызове функции на

   выполнение не укажете значение такого параметра,то будет подставлено

   предусмотренное по умолчанию значение:

        void noName(float x,int y,char z='B')  {...}

   Если теперь при вызове

   ...noName(5.6,123) то фактический вызов будет

   ...noName(5.6,123,'B');

   Если же значение при вызове указано,то умолчательное значени игнорируется.

   1.7.Ссылки и ссылочные параметры функций

   В С++ введен новый тип -ссылка на тип или точнее-синоним,определяющий поло-

   жение в памяти значения инициализирующего выражения :

        тип &имя_переменной=инициал_выражение;

   int y=16;

   int &x=y;   //y-это инициализирующее выражени и х имеет значение 16

   Теперь любое изменение х приведет к изменению у и наоборот:

   x=10;       //у стало равным 10 и х тоже

   y=15;       //х стал равным  15 и у тоже

   Ссылочная переменная должна быть обязательно инициализированв при объявле-

   нии,объявление типа

                        int &x;

   недопустимо,т к неизвестно,на какую область памяти ссылается х.

   Ссылочные переменные часто используются как формальные параметры функций,

   если необходимо избежать копирования передаваемых значений в стек и пере-

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

   тот же результат ,что и при использовании указателей,но без операций кос-

   венной адресации(бе разименования указателей)

   1.8.Спецификатор inline для фунций

   Помещается перед определением функции для того,чтобы код функции был под-

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

   ладных расходов по вызову функции(помещение параметров в стек,оформление

   возврата и пр).

        inline void func(void)  {тело функции}

   При использовании inline-подстановки мы избавляемся от необходимости повто-

   рять многократно выкопировку одного и того же повторяющегося в программе

   фрагмента кода(как правило небольшого по размеру) и сокращаем время выпол-

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

   размера занимаемой программой памяти.

   1.9.Спецификатор const

   используется в С++ для замены директивы препроцессора #define

   Значения объектов,объявленных со спецификатором const не могут быть изме-

   нены в программе:

        const double pi=4*atan(1);

   Специфика использования const с указателями:

        сhar *const names="Richard Pinson";//names-константный указатель

                                             на строку-адрес изменить нельзя

        const char* mnames="Sheila Djacson";//указатель на константу-строку

                                            его менять можно а строку нет

   1.10.Спецификатор перечислимого типа enum

   Используется для создания перечисления  как отдельного

   созданного пользователем типа :

        enum colors{Red,Amber,Green};

        colors c;

   Имена в перечислении-это переменные целого типа с возрастающими на 1

   значениями,если другие значения не указаны явно:

        enum colors{Red=4,Amber=6;Green=10};

   1.11.Операции new и delete

   позволяют выделять и освобождать память в куче вместо функций

                malloc,calloc,free

   Синтаксис использования операции new:

        new имя_типа     float *r=new float;

        new(имя_типа)    float *r=new(float);             delete r;

        new имя_типа[выражение]  float *r=new float[20];  delete[20] r;

   1.12.Указатели на тип void

   Переменной или параметру функции,объявленным как указатель на void,

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

        void *vptr;

        int *iptr=new int;

        vptr=iptr;

        iptr=vptr-ТАК НЕЛЬЗЯ!

  Чаще всего параметры типа void* используют при написании т н родовых

  программных компонентов,поведение котогрых не зависит от типа передава-

  емых им значений.Например составим родовую функцию swap,обменивающую

  значения передаваемых ей параметров независимо от их типов:

            void swap(void * &it1,void* &it2)

            {  void*temp=it1; it1=it2; it2=temp;}

            main()

            {int *i=new int,*j=new int;

            *i=5;*j=20;

            swap(i,j);

            float *x=new float,*y=new float;

            *x=5.0;*y=20.0;

            swap(x,y);

            ...

            и т д

            ВНИМАНИЕ!Параметры ф-ции swap обязательно должны быть

            ссылками для передачи результата обмена за пределы

            функции

            }

   1.13 Перегрузка операций для типов данных,определяемых пользователем.

   1.13.1.Общие сведения.

        Прикладной программист часто работает длительный период времени

        на обслуживании одной и той же прикладной области производства

        или научных исследований с характерными для этой области струк-

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

        ной алгебры или векторы в исследованиях электромагнитных полей

        или массивы однотипных записей в конторской деятельности или

        комплексные числа в расчетах электрических цепей и т д и т п

        При использовании малых языков типа Си он сталкивается с тем,что

        нужные ему типы данных вроде matrix или complex не встроены в

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

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

        транспонирования,обращения матриц или подпрограммы для обслужи-

        вания аррифметических операций с комплексными числами или многое

        другое в этом роде.

        В ряде случаев можно обратиться к проблемно-ориентированным языкам

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

        баз знаний и экспертных систем или к большим языкам Ada,Pl или дру-

        гим,в которых нужный тип данных определен и например матрицы при

        составлении программы можно использовать в  арифметических выраже-

        ниях так же просто как и числа.Но количество прикладных областей

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

        с отсутствием в языке необходимых операций над этими типами все равно

        не удается и выход приходится искать в том,что пользователь создает

        типы данных и операции над ними,часто используя уже существующие зна-

        ки операций типа сложения ,вычитания и проч и для операций над своими

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

        должен обладать способностью,встретив например операцию + с операндами

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

        ных чисел - подпрограмму сложения комплексных чисел и т д

        В С++ для этой цели введены так называемые операторные функции,а оп-

        ределение пользователем операций над созданными типами данных с ис-

        пользованием традиционных или оригинальных обозначений операций на-

        зывают перегрузкой операций.Это похоже на уже рассмотренную перегру-

        зку функций только со специфическим вызовом по знакам операций.

        Могут быть переопределены практически все стандартные операции Си:

                   (перегружены)

        + - * / = < >  +=  -=  *=  /=  <<  >>   <<=   >>=  ==  !=  <=  >=

        ++  --  %  &  ^  ! |   ~   &=  ^=  |=  &&   ||   %=  []  ()

        new  delete

        1.13.2.Синтаксис и примеры перегрузки опраций

        Для определения собственного типа данных в Си есть тип структура,

        в качестве полей(членов)которой в С++ могут быть и любые функции.

        Этой возможностью и воспользуемся для объявления типа matrix,

        которое поместим в отдельный включаемый файл matrix.h

        Вначале как обычно подключим необходимые библиотеки:

#include<stdio.h>

#include<iostream.h>

#include<stdlib.h>

#include<string.h>

#include<conio.h>

Теперь создадим объединение указателей на все числовые типы и

на тип void

        union pm{void * v;char *c;int* i;long* l;float *f;double *d;

        long double*ld;};

А затем структуру matrix

        struct matrix{

        Для указателя на матрицу используем объединение типа pm

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

        матричных элементов

        union pm matr;

        matrix *matr_result;      //это указ на матрицу результатов

                                  //операций с матрицами

                char tm;          //тип матричных элементов

                unsigned m,n;     //длина строк и столбцов

При создании матричного объекта он должен быть инициализирован

Пусть это выполнит функция-конструктор ,которая является членом

(полем)структуры matrix-дадим ей тоже имя matrix(обратите внимание

на синтаксис!):

matrix(union pm matrf, matrix* matrf_result,char tmf,unsigned mf,unsigned nf):

matr(matrf),matr_result(matrf_result),tm(tmf),m(mf),n(nf)

{

оставляем эту функцию "бестелесной",т к необходимые присвоения сделает

компилятор по заданнм после двоеточия укзаниям-инициализаторам

};

Для освобождения занимаемой матрицами памяти предусморим простенькую

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

~:

~matrix()

{

switch(tm)

{

case 0:delete matr.c;break;

case 1:delete matr.i;break;

case 2:delete matr.l;break;

case 3:delete matr.f;break;

case 4:delete matr.d;break;

case 5:delete matr.ld;break;

}

}

объявление операторной функции перегрузки операции +

в качестве параметра-второе слагаемое

                matrix operator+(matrix);

                //перегрузка операции *

                matrix operator*(matrix);

};

На этом объявление типа matrix закончено,далее идут

необходимые определения

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

объявления типа matrix(можно было совместить с объявлением внутри

структуры,но это выглядело бы громоздко)

Параметр функции-второе слагаемое типа matrix,возвращает

она тоже тип matrix

        matrix matrix::operator+(matrix add_matr)

                {

//Необходима проверка соответствия размеров матриц

if((m!=add_matr.m)||(n!=add_matr.n))

{cout<<"\nНесоответствие размеров матриц ";return add_matr;}

                int i,j;

//В зависимости от типа элементов формируем значения результир матрицы

                switch(tm)

                {

                case 0:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.c[i*m+j]=matr.c[i*m+j]+add_matr.matr.c[i*m+j];

                break;

                case 1:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.i[i*m+j]=matr.i[i*m+j]+add_matr.matr.i[i*m+j];

                break;

                case 2:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.l[i*m+j]=matr.l[i*m+j]+add_matr.matr.l[i*m+j];

                break;

                case 3:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.f[i*m+j]=matr.f[i*m+j]+add_matr.matr.f[i*m+j];

                break;

                case 4:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.d[i*m+j]=matr.d[i*m+j]+add_matr.matr.d[i*m+j];

                break;;

                case 5:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                matr_result->matr.ld[i*m+j]=matr.ld[i*m+j]+add_matr.matr.ld[i*m+j];

                break;

                }

                return *matr_result;

                }

//Число столбцов 1-го множителя = числу строк 2-го

//Число строк произведения = числу строк 1-го множителя

//Число столбцов произведения = числу столбцов 2-го множителя

        matrix matrix::operator*(matrix multi_matr)

                {

//Необходима проверка соответствия размеров матриц

if((m*n!=multi_matr.m*multi_matr.n))

{cout<<"\nНесоответствие размеров матриц ";return multi_matr;}

                int i,j,k,l;

//В зависимости от типа элементов формируем значения результир матрицы

                switch(tm)

                {

                case 0:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                {

                matr_result->matr.c[i*m+j]=0;

                for(l=0;l<m;l++)

                matr_result->matr.c[i*m+j]+=matr.c[i*m+l]*multi_matr.matr.c[l*m+j];

                }

                break;

                case 1:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                {

                matr_result->matr.i[i*m+j]=0;

                for(l=0;l<m;l++)

                matr_result->matr.i[i*m+j]+=matr.i[i*m+l]*multi_matr.matr.i[l*m+j];

                }

                break;

                case 2:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                {

                matr_result->matr.l[i*m+j]=0;

                for(l=0;l<m;l++)

                matr_result->matr.l[i*m+j]+=matr.l[i*m+l]*multi_matr.matr.l[l*m+j];

                }

                break;

                case 3:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                {

                matr_result->matr.f[i*m+j]=0;

                for(l=0;l<m;l++)

                matr_result->matr.f[i*m+j]+=matr.f[i*m+l]*multi_matr.matr.f[l*m+j];

                }

                break;

                case 4:

                for(i=0;i<n;i++)

                for(j=0;j<m;j++)

                {

                matr_result->matr.d[i*m+j]=0;

                for(l=0;l<m;l++)

                matr_result->matr.d[i*m+j]+=matr.d[i*m+l]*multi_matr.matr.d[l*m+j];

                }

                break;;

                case 5:

                for(i=0;i<n;i++)   //

                for(j=0;j<m;j++)   //здесь м-число столбцов 2-й матрицы

                {

                matr_result->matr.ld[i*m+j]=0;

                for(l=0;l<m;l++)   //здесь м-число столбцов 1-й или строк 2-й

                matr_result->matr.ld[i*m+j]+=matr.ld[i*m+l]*multi_matr.matr.ld[l*m+j];

                }

                break;

                }

                return *matr_result;

                }

В этот же файл matrix.h поместим для удобства и некоторые вспомогательные

функции работы с матрицами

Первая из них-это функция выделения под матрицы необходимой памяти и

инициализации матриц взятыми из файлов значениями:

void init_matrix(FILE*fs1,FILE*fs2,

union pm &pmatr1,union pm &pmatr2,union pm &pmatr3,

char mtype,unsigned m,unsigned n)

{

int i,j;

float fr;

double dr;

long double ldr;

        switch(mtype)

        {

        case 0:pmatr1.c=new char[m*n];pmatr2.c=new char[m*n];pmatr3.c=new char[m*n];

        if((pmatr1.c==NULL)||(pmatr2.c==NULL)||(pmatr3.c==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%d",&pmatr1.c[i]);fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%d",&pmatr2.c[i]);fflush(fs2);}

        break;

        case 1:pmatr1.i=new int[m*n];pmatr2.i=new int[m*n];pmatr3.i=new int[m*n];

        if((pmatr1.i==NULL)||(pmatr2.i==NULL)||(pmatr3.i==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%d",&pmatr1.i[i]);fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%d",&pmatr2.i[i]);fflush(fs2);}

        break;

        case 2:pmatr1.l=new long[m*n];pmatr2.l=new long[m*n];pmatr3.l=new long[m*n];

        if((pmatr1.l==NULL)||(pmatr2.l==NULL)||(pmatr3.l==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%ld",&pmatr1.l[i]);fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%ld",&pmatr2.l[i]);fflush(fs2);}

        break;

        case 3:pmatr1.f=new float[m*n];pmatr2.f=new float[m*n];pmatr3.f=new float[m*n];

        if((pmatr1.f==NULL)||(pmatr2.f==NULL)||(pmatr3.f==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%ff",&fr);pmatr1.f[i]=fr;fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%ff",&fr);pmatr2.f[i]=fr;fflush(fs2);}

        break;

        case 4:pmatr1.d=new double[m*n];pmatr2.d=new double[m*n];pmatr3.d=new double[m*n];

        if((pmatr1.d==NULL)||(pmatr2.d==NULL)||(pmatr3.d==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%lf",&dr);pmatr1.d[i]=dr;fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%lf",&dr);pmatr2.d[i]=dr;fflush(fs2);}

        break;

        case 5:pmatr1.ld=new long double[m*n];pmatr2.ld=new long double[m*n];pmatr3.ld=new long double[m*n];

        if((pmatr1.ld==NULL)||(pmatr2.ld==NULL)||(pmatr3.ld==NULL))

        {cout<<"\nОшибка выделения памяти";return;}

        for(i=0;i<m*n;i++) {fscanf(fs1,"%Lf",&ldr);pmatr1.ld[i]=ldr;fflush(fs1);}

        for(i=0;i<m*n;i++) {fscanf(fs2,"%Lf",&ldr);pmatr2.ld[i]=ldr;fflush(fs2);}

        break;

        default:cout<<"\nНе опознанный тип данных";

        }

}

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

void matr_to_file(FILE*fs3,

union pm &pmatr3,

char mtype,unsigned m,unsigned n)

{

 int i,j;

        switch(mtype)

        {

        case 0:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%d\n",pmatr3.c[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4d ",pmatr3.c[i*m+j]);cout<<"\n\r";}

        break;

        case 1:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%d\n",pmatr3.i[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4d ",pmatr3.i[i*m+j]);cout<<"\n\r";}

        break;

        case 2:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%ld\n",pmatr3.l[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4ld ",pmatr3.l[i*m+j]);cout<<"\n\r";}

        break;

        case 3:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%f\n",pmatr3.f[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4f ",pmatr3.f[i*m+j]);cout<<"\n\r";}

        break;

        case 4:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%lf\n",pmatr3.d[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4lf ",pmatr3.d[i*m+j]);cout<<"\n\r";}

        break;

        case 5:

        for(i=0;i<m*n;i++) {fprintf(fs3,"%Lf\n",pmatr3.ld[i]);}

        clrscr();

        for(i=0;i<n;i++)

        {for(j=0;j<m;j++) printf("%4Lf ",pmatr3.ld[i*m+j]);cout<<"\n\r";}

        break;

        default:cout<<"\nНе опознанный тип данных";

        }

}

Основная программа будет выглядеть при таком подходе достаточно просто:

Подключим созданный нами файл matrix.h:

#include"matrix.h"

void main(int c,char *v[])

{

FILE *fs1,*fs2,*fs3;

if(c<7) {cout<<"\nНе все параметры в командной строке";return;}

fs1=fopen(v[1],"r");

fs2=fopen(v[2],"r");

fs3=fopen(v[3],"w");

if((fs1==NULL)||(fs2==NULL)||(fs3==NULL))

{cout<<"\nОшибка открытия файла";return;}

char mtype=atoi(v[4]);  Это заданный пользователем тип эл-тов матрицы

unsigned m=atoi(v[5]),  Длины строк и столбцов

n=atoi(v[6]);

union pm pmatr1,pmatr2,pmatr3;  Объединения указателей на матрицы

И вызов функции инициализации

init_matrix(fs1,fs2,pmatr1,pmatr2,pmatr3,mtype,m,n);

Создание объектов матричного типа

matrix

matr3(pmatr3,&matr3,mtype,m,n),

matr1(pmatr1,&matr3,mtype,m,n),

matr2(pmatr2,&matr3,mtype,m,n);

Использование матричного типа в операциях сложения

и других(тестирование созданного матричного типа)

matr3=matr1+matr2+matr2+matr1;

Запись результатов в файл

matr_to_file(fs3,pmatr3,mtype,m,n);

fcloseall();

}


Классы и механизм наследования
Классы

      Классы  С++  предусматривают создание расширений системы пре-

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

 ких объектов. Могут быть объявлены производные классы, наследующие

 компоненты одного или более базовых (порождающих) классов.

      В С++ структуры и объединения рассматриваются  как  классы  с

 определенными умолчаниями правил доступа.

      Упрощенный,  в  "первом  приближении",  синтаксис  объявления

 класса следующий:

    ключ-класса  имя-класса <:базовый-список>[<список-компонентов>]

      Ключ-класса - это class, struct или union.

      Опциональный базовый-список  перечисляет  базовый  класс  или

 классы,  из  которого  имя-класса  берет (или наследует) объекты и

 правила.  Если  объявлены  некоторые  базовые  классы,  то   класс

 "имя-класса" называется производным классом (см.стр.110, "Доступ к

 базовым  и производным классам"). Базовый список содержит специфи-

 каторы доступа по умолчания и опциональные их переопределения, ко-

 торые могут модифицировать права  доступа  производного  класса  к

 компонентам  базовых  классов  (см. стр.108 оригинала, "Управление

 доступом к компонентам").

      Опциональный список-компонентов объявляет  компоненты  класса

 (данные  и функции) для имени-класса с умолчаниями и переопределе-

 ниями спецификаторов доступа, которые могут влиять  на  то,  какие

 функции к каким компонентам класса могут иметь доступ.

 Имена классов

      Имя-класса  это  любой  идентификатор,  уникальный в пределах

 своего контекста. В классах структур и в  объединениях  имя-класса

 может  быть опущено (см. "Структуры и типы, определяемые пользова-

 телем (typedef) без тегов" на стр.65 оригинала).

 Типы классов

      Объявление создает уникальный тип, тип  класса  "имя-класса".

 Это  позволяет вам объявлять последующие объекты класса (или вхож-

 дения класса) данного типа, а также объекты, являющиеся  производ-

 ными  от этого типа (такие как указатели, ссылки, массивы объектов

 "имя-класса" и т.д.):

    class X { ... };

    X x, &xr, *xptr, xarray[10];

    /* четыре объекта: типа X, ссылка на X, указатель на X и масив

       элементов типа X */

    struct Y { ... };

    Y y, &yr, *yptr, yarray[10];

    // С позволяет иметь

    // struct Y y, &yr, *yptr, yarray[10];

    union Z { ... };

    Z z, &zr, *zptr, zarray[10];

    // С позволяет иметь

    // union Z z, &zr, *zptr, zarray[10];

      Отметим различие между объявлением структур и объединений в С

 и С++: в С ключевые слова struct и union обязательны, но в С++ они

 нужны только в том случае, когда имена классов, Y и Z, скрыты (см.

 следующий раздел).

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

      Контекст имени класса является локальным, с  некоторыми  осо-

 бенностями,  характерными для классов. Контекст имени класса начи-

 нается с точки его объявления и заканчивается вместе с  объемлющим

 блоком.  Имя  класса  скрывает  любой класс, объект, нумератор или

 функцию с тем же именем в объемлющем контексте.  Если  имя  класса

 объявлено  в контексте, содержащем объявление объекта, функции или

 нумератора с тем же именем, обращение к классу возможно только при

 помощи уточненного спецификатора типа. Это означает, что с  именем

 класса  нужно  использовать  ключ класса, class, struct или union.

 Например,

    struct S { ... };

    int S(struct S *Sptr);

    void func(void)

    {

       S t;          // недопустимое объявление: нет ключа класса

                     // и функции S в контексте

       struct S s; //  так  можно:  есть  уточнение  ключем  класса

       S(&s); // так можно: это вызов функции

    }

      С++ позволяет также неполное объявление класса:

    class X;    // еще нет компонентов !

    struct Y;

    union Z;

      Неполные объявления позволяют некоторые ссылки к именам клас-

 сов  X,  Y  или Z (обычно ссылки на указатели объектов классов) до

 того, как классы будут полностью определены (см. "Объявление ком-

 понентов  структуры" на стр.65 оригинала). Разумеется, прежде чем

 вы сможете объявить и использовать объекты классов, вы должны вы-

 полнить полное объявление классов со всеми их компонентами.

 Объекты классов

      Объекты  классов могут быть присвоены (если не было запрещено

 копирование), переданы как аргументы функции, возвращены фунукцией

 (за некоторыми исключениями) и т.д. Прочие операции с объектами  и

 компонентами  классов  могут  быть различными способами определены

 пользователем, включая функции-компоненты и "друзья", а также  пе-

 реопределение стандартных функций и операций при работе с объекта-

 ми  конкретного класса. Переопределение функций и операций называ-

 ется перегрузкой. Операции и функции, ограниченные объектами конк-

 ретного класса (или  взаимосвязанной  группы  классов)  называются

 функциям-компонентами данного класса. С++ имеет механизм, позволя-

 ющий  вызвать то же имя функции или операции для выполнения другой

 задачи, в зависимости от типа или числа аргументов или операндов.

 Список компонентов класса

      Опциональный список-компонентов представляет собой последова-

 тельность объявлений данных (любого типа, включая нумераторы,  би-

 товые  поля  и  другие классы) и объявлений и определений функций,

 каждое из которых может иметь  опциональные  спецификаторы  класса

 памяти  и модификаторы доступа. Определенные таким образом объекты

 называются компонентами класса. Спецификаторы класса памяти  auto,

 extern  и  register в данном случвае недопустимы. Компоненты могут

 быть объявлены со спецификаторами класса памяти static.

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

      Функция, объявленная  без  спецификатора  friend,  называется

 функцией-компонентом  класса. Функции, объявленные с модификатором

 friend, называется "функцией-другом".

      Одно и то же имя может использоваться для  обозначения  более

 чем одной функции, при условии, что они отличны по типам или числу

 аргументов.

 Ключевое слово this

      Не-статические  функции компонентов работают с объектами типа

 класса, с которыми они вызываются. Например,  если  x  это  объект

 класса X, а f это функция-компонент X, то вызов функци x.f() рабо-

 тает с x. Аналогичным образом, если xptr есть указатель объекта X,

 то  вызов функции xptr->() работает с *xptr. Откуда f может знать,

 с каким x работать? С++ передает  f  указатель  на  x,  называемый

 this. this передается как скрытый аргумент при вызове не-статичес-

 ких функций-компонентов.

      Ключевое  слово this представляет собой локальную переменную,

 доступную в теле не-статической функции-компонента. this не требу-

 ет объявлений, и на него редко встречаются явные ссылки в  опреде-

 лении функции. Однако, оно неявно используется в функции для ссыл-

 ки  к  компонентам.  Если, например, вызывается x.f(y), где y есть

 компонент X, то this устанавливается на &x, а y устанавливается на

 this->y, что эквивалентно x.y.

 Встраиваемые функции (inline)

      Функция-компонента может быть  объявлена  в  пределах  своего

 класса,  но  определена где-либо в другом месте. И наоборот, функ-

 ция-компонента может быть и объявлена, и определена в своем  клас-

 се,  и тогда она называется встраиваемой функцией. (Некоторые при-

 меры таких функций показаны в главе 5 документа "Начало работы").

      В некоторых случаях Turbo C++ может уменьшить затраты времени

 на вызов функции, подстанавливая вместо вызова функции непосредст-

 венно компилированный код тела функции. Этот  процесс,  называемый

 встраиваемым расширением тела функции, не влияет на контекст имени

 функции  или  ее аргументов. Встраиваемое расширение не всегда по-

 лезно и желательно. Спецификатор inline представляет собой  запрос

 (или  требование) компилятору, в котором вы сообщаете, что встраи-

 ваемые расширения желательны. Как и в случае спецификатора  класса

 памяти  register, компилятор может либо удовлетворить, либо проиг-

 норировать ваше пожелание.

      Явные или неявные запросы inline  лучше  всего  резервировать

 для небольших по объему и часто вызываемых функций, таких как фун-

 кции типа operator, реализующих перегруженные операторы. Например,

 следующее объявление класса:

    int i;                          // global int

    class X {

       public:

       char*  func(void) { return i; } // inline по умолчанию char*

       i;

    };

 эквивалентно

    inline char* X::func(void) {return i; }

      func определяется "вне" класса с явным спецификатором inline.

 Переменная i, возвращаемая func, есть char* i класса X - см.  раз-

 дел, посвященный контексту компонентов на стр.107 оригинала.

 Статические компоненты

      Спецификатор класса памяти static может  быть  использован  в

 объявлениях компонентов данных и функций-компонентов класса. Такие

 компоненты  называются  статическими и имеют свойства, отличные от

 свойств  не-статических  компонентов.  В   случае   не-статических

 компонентов  для  каждого  объекта  класса  "существует" отдельная

 копия; в случае же статических компонентов существует только  одна

 копия,  а  доступ  к  ней  выполняется  без  ссылки  на какой-либо

 конкретный объект класса. Если х это статический компонент  класса

 Х, то к нему можно обратиться как Х::х (даже если объекты класса Х

 еще  не  созданы). Однако, можно выполнить доступ к х и при помощи

 обычных операций доступа к компонентам. Например,  в  виде  y.x  и

 yptr->x,  где  y это объект класса X, а yptr это указатель объекта

 класса X, хотя выражения y и yptr еще не вычислены.  В  частности,

 статическая   функция-компонент   может   быть   вызвана   как   с

 использованием специального синтаксиса вызова функций компонентов,

 так и без него.

    class X {

       int member_int;

    public:

       static void func(int i, X* ptr);

    };

    void g(void);

    {

       X obj;

       func(1, &obj);     // ошибка, если где-нибудь еще

                          // не определена глобальная func()

       X::func(1, &obj);  // вызов static func() в X

                          // допустим только для статических функций

       obj.func(1, &obj); // то же самое (допустимо как для стати-

                          // ческих, так и не-статических функций)

    }

      Поскольку  статическая функция-компонент может вызываться без

 учета какого-либо конкретного  объекта,  она  не  имеет  указателя

 this. Следствие из этого таково, что статическая функция-компонент

 не  имеет  доступа к не-статическим компонентам без явного задания

 объекта при помощи  .  или  ->.  Например,  с  учетом  объявлений,

 сделанных   в  предыдущем  примере,  func  может  быть  определена

 следующим образом:

    void X%%func(int i, X* ptr)

    {

       member_int = i;       // на какой объект ссылается

                             // member_int? Ошибка !

       ptr->member_int = 1;  // так можно: теперь мы знаем!

    }

      Без     учета     встраиваемых      функций,      статические

 функции-компоненты    глобальных   классов   имеют   внешний   тип

 компоновки.  Статические  функции-компоненты  не  могут   являться

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

 не-статическую функции-компоненты с одинаковыми именами  и  типами

 аргументов.

      Объявление статического компонента данных в объявлении класса

 не  является  определением,  поэтому где-нибудь еще должно иметься

 определение, отвечающее за распределение памяти  и  инициализацию.

 Определение  статического  компонента  данных  может быть опущено,

 если действует средство "инициализации нулями по умолчанию".

      Статические  компоненты  класса,  объявленного  локальным  по

 отношению к некоторой функции, не имеют типа компоновки и не могут

 быть  инициализированы.  Статические компоненты глобального класса

 могут быть инициализированы подобно обычным  глобальным  объектам,

 но  только  в  контексте файла. Статические компоненты подчиняются

 обычным правилам доступа к  компонентам  класса,  за  исключением

 того, что они могут быть инициализированы.

    class X {

       ...

       static int x;

       ...

    }; int X::x = 1;

      Главное  использование статических компонентов состоит в том,

 чтобы отслеживать данные, общие  для  всех  объектов  класса,  как

 например,  число созданных объектов, либо использованный последним

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

 Статические компоненты используются также для:

 - уменьшения числа видимых глобальных имен

 - того, чтобы сделать очевидным, какие именно статические объекты

   какому классу принадлежат

 - разрешения управления доступам к их именам.

 Контекст компонента

      Выражение   X::func()   в  примере,  приведенном  на  стр.106

 оригинала, использует  имя  класса  X  с  модификатором  контекста

 доступа, обозначающим, что func, хотя и определена "вне" класса, в

 действительности  является  функцией-компонентом  Х и существует в

 контексте Х. Влияние Х:: распространяется на тело определения этой

 функции. Это объясняет, почему возвращаемое  функцией  значение  i

 относится  к  X::i, char* i из Х, а не к глобальной переменной int

 i. Без модификатора Х:: функция func представляла бы  обычную,  не

 относящуюся  к  классу функцию, возвращающую глобальную переменную

 int i.

      Следовательно, все функции-компоненты находятся  в  контексте

 своего класса, даже если они определены вне этого класса.

      К  компонентам  данных  класса  Х можно обращаться при помощи

 операций выбора . и -> (как и в структурах  С).  Функции-компоненты

 можно  вызывать  также  при  помощи операций выбора (см. "Ключевое

 слово this на стр.105 оригинала). Например,

    class X {

    public:

       int i;

       char name[20];

       X *ptr1;

       X *ptr2;

       void Xfunc(char *data, X* left, X* right); // определение

                                           // находится не здесь

    };

    void f(void);

    {

       X x1, x2, *xptr=&x1;

       x1.i = 0;

       x2.i = x1.i;

       xptr->i = 1;

       x1.Xfunc("stan", &x2, xptr);

    }

      Если m является компонентом или базовым компонентом класса Х,

 то выражение Х::m называется квалифицированным именем;  оно  имеет

 тот  же тип, что и m, и является именующим выражением только в том

 случае, если именующим  выражением  является  m.  Ключевой  момент

 здесь  состоит  в  том,  что  даже если имя класса Х скрыто другим

 именем, квалифицированное имя Х::m тем не менее обеспечит доступ к

 нужному имени класса, m.

      Компоненты класса не могут быть добавлены к нему в другом  разделе

 вашей  программы.  Класс Х не может содержать объекты класса Х, но

 может содержать указатели или ссылки на объекты класса Х  (отметим

 аналогию с типами структур и объединений С).

 Управление доступом к компонентам

      Компоненты   класса   получают   атрибуты  доступа  либо  по

 умолчанию  (в  зависимости  от  ключа  класса   и   местоположения

 объявления),  либо при использовании какого-либо из спецификаторов

 доступа: public, private или protected. Значение  этих  атрибутов

 следующие:

    public     Компонент может быть использован любой функцией.

    private    Компонент может быть использован только функциями-

               компонентами и "друзьями" класса, в котором он

               объявлен.

    protected  То  же  самое,  что  для  private,  но кроме того,

               компонент   может   быть    использован    функциями

               компонентов   и  друзьями  классов,  производных  от

               объявленного   класса,   но   только   в    объектах

               производного      типа.      (Производные     классы

               рассматриваются в следующем разделе).

      Спецификаторы доступа не влияют на  объявления  функций  типа

 friend. (См. раздел "Друзья" классов" на стр.112 оригинала).

      Компоненты   класса  по  умолчанию  имеют  атрибут  private,

 поэтому  для  переопределения  данного  объявления   спецификаторы

 доступа public или protected должны задаваться явно.

      Компоненты  struct  по умолчанию имеют атрибут public, но вы

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

 доступа public или protected.

      Компоненты   union   по   умолчанию  имеют  атрибут  public;

 переопределить его нельзя. Все три спецификатора доступа  задавать

 для компонентов объединения недопустимо.

      Модификатор   доступа   по   умолчанию   или   заданный   для

 переопределения атрибута доступа остается действительным для всех

 последующих объявлений  компонентов,  пока  не  встретится  другой

 модификатор доступа. Например,

    class X {

       int i;      // X::i по умолчанию privte

       char ch;    // так же, как и X::ch

    public:

       int j;      // следующие два компонента - public

       int k;

    protected:

       int l;      // X::l - protected

    };

    struct Y {

       int i;      // Y::i по умолчанию public

    private:

       int j;      // Y::j - private

    public:

       int k;      // Y::k - public

    };

    union Z {

       int i;      // public по умолчанию, других вариантов нет

       double d;

    };

      Спецификаторы  доступа могут быть перечислены и сгруппированы

 в любой  удобной  последовательности.  Можно  сэкономить место при

 набивке программы, объявив все компоненты private сразу, и т.д.

 Доступ к базовым и производным классам

      При  объявлении производного класса D вы перечисляете базовые

 классы В1, В2 ... в разделяемом запятой базовом-списке:

    ключ-класса D:базовый-список {<список-компонентов>}

      Поскольку  сам  базовый  класс  может  являться   производным

 классом,  то  вопрос  об атрибуте доступа решается рекурсивно: вы

 отслеживаете его до  тех  пор,  пока  не  доберетесь  до  "самого"

 базового класса, породившего все остальные.

      D     наследует    всем    компонентам    базовых    классов.

 (Переопределенные компоненты базовых классов  наследуются,  и  при

 необходимости  доступ  к  ним  возможен при помощи переопределений

 контекста). D может использовать только компоненты базовых классов

 с атрибутами public и protected. Однако, что  будут  представлять

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

 D? D может понадобиться  использовать  public  компонент  базового

 класса,  но  при  этом  сделать  его  для внешних функций private.

 Решение здесь состоит  в  том,  чтобы  использовать  спецификаторы

 доступа в базовом-списке.

      При объявлении D вы можете задать спецификатор доступа public

 или private перед классами в базовом-списке:

    class D : public B1, private B2, ... {

    ...

    }

      Задать  в  базовом-списке  protected  нельзя.  Объединения не

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

 в качестве базовых классов.

      Эти  модификаторы  не  изменяют  атрибутов  доступа  базовых

 членов с точки зрения  базового  класса,  хотя  и  могут  изменить

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

 классов.

      Умолчанием является private, если D  есть  объявление  класса

 class, и public, если D есть объявление структуры struct.

      Производный класс наследует атрибуты доступа базового класса

 следующим образом:

 базовый   класс  public:  компоненты  public

                       базового класса  становятся  членами  public

                       производного  класса. Компоненты pro- tected

                       базового  класса   становятся   компонентами

                       protected  производного  класса.  Компоненты

                       private   базового   класса   остаются   для

                       базового класса private.

 базовый  класс  private: и public, и protected компоненты базового

                       класса   становятся   private   компонентами

                       производного   класса.   Компоненты  private

                       базового класса остаются для базового класса

                       private.

      В обоих случаях  отметим,  что  компоненты  private  базового

 класса   были  и  остаются  недоступными  для  функций-компонентов

 производного класса, пока в описании доступа  базового  класса  не

 будут явно заданы объявления friend. Например,

    class X : A {     // умолчание для класса - private A

    ...

    }

    /* класс X является производным от класса А */

    class Y : B, public C {   // переопределяет умолчание для С

    ...

    }

    /* класс Y является производным (множественное наследование)

       от B и C.  По умолчанию - private B */

    struct S : D {    // умолчание для struct - public D

    ...               /* struct S - производная от D */

    }

    struct T : private D, E {  // переопределяет умолчание для D

                               // E по умолчанию public

    ...

    }

    /* struct T является производной (множественное наследование)

       от D и T.  По умолчанию - public E  */

      Действие   спецификаторов  доступа  в  базовом  списке  можно

 скорректировать при помощи квалифицированного-имени в  объявлениях

 public или protected для производного класса. Например,

    class B {

       int a;         // по умолчанию private

    public:

       int b, c;

       int Bfunc(void);

    };

    class X : private B {   // теперь в Х a, b, c и Bfunc - private

       int d;               // по умолчанию private. Примечание: f

                            // в Х недоступна

    public:

       B::c;                // c была private; теперь она  public

       int e;

       int Xfunc(void);

    };

    int Efunc(X& x);       // внешняя по отношению к В и Х

      Функция  Tfunc  может  использовать только имена с атрибутом

 public, например c, e и Xfunc.

      Функция Xfunc в X является производной от private B,  поэтому

 она имеет доступ к:

 - "скорректированной-к-типу-public" c

 - "private-относительно-Х" компонентам В:b и Bfunc

 - собственным private и public компонентам: d, e и Xfunc.

      Однако, Xfunc не имеет доступа к "private-относительно-B"

 компоненту a.


                          Виртуальные базовые классы

      При  множественном  наследовании  базовый класс не может быть

 задан в производном классе более одного раза:

    class B { ... };

    class D : B, B { ... }:  // недопустимо

      Однако, базовый класс может быть передан производному  классу

 более одного раза косвенно:

    class X : public B { ... }

    class Y : public B { ... }

    class Z : public X, public Y { ... }  // допустимо

      В  данном  случае  каждый  объект  класса  Z  будет иметь два

 подобъекта класса В. Если это вызывает проблемы,  к  спецификатору

 базового  класса  может  быть  добавлено  ключевое  слово virtual.

 Например,

    class X : virtual public B { ... }

    class Y : virtual public B { ... }

    class Z : public X, public Y { ... }

      Теперь В является виртуальным  базовым  классом,  а  класс  Z

 имеет только один под-объект класса В.


Друзья классов
                           "Друзья" классов (friend)

      Друг  F  класса  X это функция или класс, которые, не являясь

 функцией-компонентом Х, имеют тем не менее полные права доступа  к

 компонентам Х private и protected. Во всех прочих отношениях F это

 обычная   с  точки  зрения  контекста,  объявлений  и  определений

 функция.

      Поскольку F  не  является  компонентом  Х,  она  не  лежит  в

 контексте  Х и поэтому не может вызываться операциями выбора x.F и

 xptr->F (где x это объект Х, а xptr это указатель объекта Х.

      Если в объявлении или определении функции в пределах класса Х

 используется спецификатор  friend,  то  такая  функция  становится

 "другом" класса Х.

      Функция-"друг",  определенная  в пределах класса, подчиняется

 тем же правилам встраивания, что и функции-компоненты класса  (см.

 "Встраиваемые  функции" на стр.105 оригинала). Функции-"друзья" не

 зависят  от  их  позиции  в  классе  или  спецификаторов  доступа.

 Например,

    class X {

       int i;                            // private относительно Х

       friend void friend_func(X*, int);

    /* friend_func не является private, хотя она и объявлена

       в разделе private */

    public:

       void member_func(int);

    };

    /*объявления;для обеих функций отметим доступ к private int i*/

    void friend_func(X* xptr, int a) { xptr->i = a; }

    void X::member_func(int a) { i = a; }

    X xobj;

    /* отметим различие в вызовах функций */

    friend_func(&xobj, 6);

    xobj.member_func(6);

      Вы  можете  сделать  все функции класса Y друзьями класса Х в

 одном объявлении:

    class Y;                   // неполное объявление

    class X {

       friend Y;

       int i;

       void member_funcX();

    };

    class Y; {

       void friend_X1(X&);

       void friend_X2(X*);

       ...

    };

      Функции, объявленные в Y, являются друзьями Х, хотя они и  не

 имеют  спецификаторов  friend.  Они  имеют  доступ к компонентам Х

 private, таким как i и member_funcX.

      Отдельные  функции-компоненты  класса  Х  также  могут   быть

 друзьями класса Y:

    class X {

       ...

       void member_funcX();

    }

    class Y {

       int i;

       friend void X::member_funcX();

       ...

    };

      "Дружба"  классов  не транзитивна: если X друг Y, а Y друг Z,

 это не означает, что X - друг Z. Однако, "дружба" наследуется.


                          Конструкторы и деструкторы

      Существует   несколько   специальных   функций   компонентов,

 определяющих,    каким    образом    объекты   класса   создаются,

 инициализируются,  копируются   и   разрушается.   Констукторы   и

 деструкторы   являются  наиболее  важными  из  них.  Они  обладают

 большинством характеристик обычных функций-компонентов - вы должны

 объявить и определить их в пределах класса,  либо  объявить  их  в

 классе,  но определить вне его - однако, они обладают и некоторыми

 уникальными свойствами.

 1. Они не имеют объявлений значений возврата (даже void).

 2. Они не могут быть унаследованы, хотя производный класс может

    вызывать конструкторы и деструкторы базового класса.

 3. Конструкторы, как и большинство функций С++, могут иметь аргу-

    менты по умолчанию или использовать списки инициализации ком-

    понентов.

 4. Деструкторы могут иметь атрибут virtual, но конструкторы не

    могут.

 5. Вы не можете работать с их адресами:

    main()

    {

       ...

       void *ptr = base::base;    // недопустимо

       ...

    }

 6. Если конструкторы и деструкторы не были заданы явно, то они мо-

    гут быть сгенерированы Turbo C++; во многих случаях они также

    могут быть запущены при отсутствии явного вызова в вашей прог-

    рамме. Любой конструктор или деструктор, создаваемый компилято-

    ром, должен иметь атрибут public.

 7. Вызвать конструктор тем же образом, что и обычную функцию,

    нельзя. Вызов деструктора допустим только с полностью квалифи-

    цированным именем.

 8.

    {

    ...

       X *p;

    ...

       p->X::-X();      // допустимый вызов деструктора

       X::X();          // недопустимый вызов конструктора

    ...

    }

 9. При определении и разрушении объектов компилятор выполняет вы-

    зов конструкторов и деструкторов автоматически.

 10.Конструкторы и деструкторы при необходимости распределения объ-

    екту памяти могут выполнять неявные вызовы операций new и

    delete.

 11.Объект с конструктором или деструктором не может быть использо-

    ван в качестве компонента объединения.

      Если класс Х имеет один или более конструкторов, то  один  из

 них  запускается  всякий  раз  при определении объекта х класса Х.

 Конструктор создает объект х  и  инициализирует  его.  Деструкторы

 выполняют  обратный  процесс,  разрушая  объекты класса, созданные

 конструкторами.

      Конструкторы активизируются также при создании локальных  или

 временных  объектов  данного  класса;  деструкторы  активизируются

 всякий раз, когда эти объекты выходят из контекста.

                                 Конструкторы

      Констукторы отличаются от прочих компонентов функций тем, что

 имеют то же самое имя, что и класс, к которому они относятся.  При

 создании или копировании объекта данного класса происходит неявный

 вызов соответствующего конструктора.

      Конструкторы   глобальных  переменных  вызываются  до  вызова

 функции main. При использовании  стартовой  директивы  pragma  для

 инсталлирования  некоторой  функции  до функции main, конструкторы

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

      Локальные объекты создаются, как только  становится  активным

 контекст  переменной.  Конструктор  также запускается при создании

 временного объекта данного класса.

    class x

    {

    public:

       X();    //конструктор класса Х

    };

      Конструктор класса Х не может принимать Х как аргумент:

    class X {

    ...

    public

       X(X);   // недопустимо

    }

      Параметры конструкторы могут быть любого типа, за исключением

 класса,  компонентом   которого   является   данный   конструктор.

 Конструктор  может  принимать  в качестве параметра ссылку на свой

 собственный класс; в  таком  случае  он  называется  конструктором

 копирования.   Конструктор,   не  принимающий  параметров  вообще,

 называется  конструктором  по  умолчанию.  Далее   мы   рассмотрим

 конструкторы  по  умолчанию;  описание же конструктора копирования

 начинается со стр.116 оригинала.

 Конструктор по умолчанию

      Конструктором по умолчанию  для  класса  Х  называется  такой

 конструктор, который не принимает никаких аргументов: Х::Х(). Если

 для    класса    не    существует    конструкторов,   определяемых

 пользователем, то Turbo C++ генерирует конструктор  по  умолчанию.

 При  таких  объявлениях, как Х х, конструктор по умолчанию создает

 объект х.

 Важное примечание:

      Как и все функции,  конструкторы  могут  иметь  аргументы  по

 умолчанию. Например, конструктор

    X::X(int, int = 0)

 может  принимать  один  или два аргумента. Если данный конструктор

 будет представлен только с одним  аргументом,  недостающий  второй

 аргумент будет принят как int 0. Аналогичным образом, конструктор

    X::X(int = 5, int = 6)

 может  принимать  два  аргумента, один аргумент, либо не принимать

 аргументов   вообще,   причем   в   каждом   случае    принимаются

 соответствующие умолчания. Однако, конструктор по умолчанию Х::Х()

 не  принимает  аргументов  вообще,  и  его  не  следует  путать  с

 конструктором,  например,  X::X(int  =  0),  который  может   либо

 принимать один аргумент, либо не принимать аргументов.

      При вызове конструкторов следует избегать неоднозначностей. В

 следующем  примере  возможно неоднозначное восприятие компилятором

 конструктора   по   умолчанию   и    конструктора,    принимающего

 целочисленный параметр:

    class X

    {

    public:

       X();

       X(int i = 0);

    };

    main()

    {

       X one(10);  // так можно: используется X::X(int)

       X two;      // так нельзя; неоднозначно задано, используется

                   // ли X::X() или X::X(int = 0)

    ...

       return 0;

 Конструктор копирования

      Конструктор  копирования  для класса Х это такой конструктор,

 который может быть вызван с одним единственным аргументом типа  X:

 X::X(const  X&)  или  X::(const  X&,  int  =  0).  В  конструкторе

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

 Конструкторы   копирования  запускаются  при  копировании  объекта

 данного  класса,  обычно  в  случае  объявления  с  инициализацией

 объектом другого класса: X x = y. Turbo C++ генерирует конструктор

 копирования  для  класса  X  автоматически, если такой конструктор

 необходим, но в классе Х неопределен.

 Перегрузка конструкторов

      Конструкторы могут быть перегружены, что  позволяет  создание

 объектов   в   зависимости   от   значений,   использованных   при

 инициализации.

    class X

    {

       int   integer_part;

       double double_part;

    public:

       X(int i)    { integer_part = i; }

       X(double d) { double_part = d }

    };

    main()

    {

       X one(10);   // запускает X::X(int) и устанавливает

                    // integer_part в значение 10

       X one(3.14); // запускает X::X(double) ш устанавливает

                    // double_part

     ...

       return 0;

     }

 Порядок вызова конструкторов

      В случае, когда  класс  использует  один  или  более  базовых

 классов,  конструкторы  базовых  классов  запускаются до того, как

 будут  вызваны  конструкторы  производного  класса.   Конструкторы

 базового класса вызываются в последовательности их объявления.

      Например, в следующем примере

    class Y {...}

    class X : public Y {...}

    X one;

 вызов конструкторов происходит в следующей последовательности:

    Y();    // конструктор базового класса

    X();    // конструктор производного класса

      В случае множественных базовых классов:

    class X : public Y, public Z

    X one;

 конструкторы вызываются в последовательности их объявления:

    Y();   // первыми следуют конструкторы базового класса

    Z();

    X();

      Конструкторы   виртуальных  базовых  классов  запускаются  до

 каких-либо не-виртуальных базовых классов. Если иерархия  содержит

 множественные   виртуальные   базовые   классы,   то  конструкторы

 виртуальных базовых классов запускаются  в  последовательности  их

 объявления.   Затем,   перед  вызовом  конструкторов  производного

 класса, конструируются не-виртуальные базовые классы.

      Если виртуальный класс является производным от не-виртуальной

 базы,  то  для  того,  чтобы   виртуальный   базовый   класс   был

 сконструирован  правильно, эта не-виртуальная база должна являться

 первой. Код

    class X : public Y, virtual public Z

    X one;

 определяет следующую последовательность:

    Z();   // инициализация виртуального базового класса

    Y();   // не-виртуальный базовый класс

    X();   // произвольный класс

      Либо, в более сложном случае,

    class base;

    class base2;

    class level1 : public base2, virtual public base;

    class level2 : public base2, virtual public base;

    class toplevel : public level1, virtual public level1;

    toplevel view;

      Порядок конструирования view будет принят следующий:

    base();      // старший в иерархии виртуальный базовый класс

                 // конструируется только однажды

    base2();     // не-виртуальная база виртуальной базы level2

                 // вызывается для конструирования level2

    level2();    // виртуальный базовый класс

    base2();     // не-виртуальная база для level1

    level1();    // другая не-виртуальная база

    toplevel();

      В  случае,  когда  иерархия  класса  содержит   множественные

 вхождения  виртуального  базового  класса,  данный  базовый  класс

 конструируется  только  один  раз.  Однако,  если   существуют   и

 виртуальные,   и  не-виртуальные  вхождения  базового  класса,  то

 конструктор класса  запускается  один  раз  для  всех  виртуальных

 вхождений  и  один раз для всех не-виртуальных вхождений базового

 класса.

      Конструкторы  элементов   массива   запускаются   в   порядке

 возрастания индексов массива.

 Инициализация класса

      Объект   класса   с   компонентами   только   public   и  без

 конструкторов или базовых классов  (обычно  это  структура)  может

 быть   инициализирован  при  помощи  списка  инициализаторов.  При

 наличии конструктора класса объекты  либо  инициализируются,  либо

 имеют  конструктор  по  умолчанию. Последний используется в случае

 объектов без явной инициализации.

      Объекты классов с конструкторами могут быть  инициализированы

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

 Этот список  используется  как  список  передаваемых  конструктору

 аргументов.   Альтернативным   способом   инициализации   является

 использование  знака  равенства,  за  которым  следует   отдельное

 значение.   Это   отдельное   значение  может  иметь  тип  первого

 аргумента,  принимаемого  конструктором  данного  класса;  в  этом

 случае  дополнительных  аргументов  либо  не  существует, либо они

 имеют  значения  по  умолчанию.  Значение  может  также   являться

 объектом данного типа класса. В первом случае для создания объекта

 вызывается   соответствующий   конструктор.   В  последнем  случае

 вызывается  конструктор   копирования,   инициализирующий   данный

 объект.

    class X

    {

       int i;

    public:

       X();         // тела функций для ясности опущены

       X(int x);

       X(const X&);

    };

    main()

    {

       X one;        // запуск конструктора по умолчанию

       X two(1);     // используется конструктор X::X(int)

       X three = 1;  // вызывает X::X(int)

       X four = one; // запускает X::X(const X&) для копирования

       X five(two);  // вызывает X::X(cont X&)

      Конструктор  может  присваивать  значения  своим  компонентам

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

 параметров   и   выполнять   присваивание  компонентам  переменным

 собственно в теле функции конструктора:

    class X

    {

       int a, b;

    public:

       X(int i, int j) { a = i; b = j }

    };

      Либо он может использовать находящийся до тела функции список

 инициализаторов:

    class X

    {

       int a, b;

    public:

       X(int i, int j) : a(i), b(j) {}

    };

      В обоих случаях инициализация X x(1, 2) присваивает  значение

 1  x::a  и  значение  2  x::b.  Второй  способ,  а  именно  список

 инициализаторов,   обеспечивает   механизм    передачи    значений

 конструкторам базового класса.

      Конструкторы   базового   класса   должны  быть  объявлены  с

 атрибутами public  или  protected,  для  того,  чтобы  обеспечить

 возможность их вызова из производного класса.

    class base1

    {

       int x;

    public:

       base1(int i) { x = i; }

    };

    class base2

    {

       int x;

    public:

       base2(int i) : x(i) {}

    };

    class top : public base1, public base2

    {

       int a, b;

    public:

       top(int i, int j) : base(i*5), base2(j+i), a(i) { b = j;}

    };

      В  случае  такой  иерархии  класса  объявление  top one(1, 2)

 приведет к инициализации base1 значением 5, а base2  значением  3.

 Методы инициализации могут комбинироваться друг с другом.

      Как  было  описано  выше,  базовые  классы инициализируются в

 последовательности   объявления.Затем   происходит   инициализация

 компонентов,  также в последовательности их объявления, независимо

 от их взаимного расположения в списке инициализации.

    class X

    {

       int a, b;

    public:

       X(int i, j) : a(i), b(a+j) {}

    };

      В пределах класса объявление X x(1,1) приведет к присваиванию

 числа 1 x::a и числа 2 x::b.

      Конструкторы     базовых     классов     вызываются     перед

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

 производного класса не могут изменяться и затем влиять на создание

 базового класса.

    class base

    {

       int x;

    public:

       base(int i) : x(i) {}

    };

    class derived : base

    {

       int a;

    public:

       derived(int i) : a(i*10), base(a) {}  // Обратите внимание!

                     // base будет передано неинициализированное a

    }

      В данном  примере  вызов  производного  d(1)  не  приведет  к

 присвоению  компоненту  базового  класса  х значения 10. Значение,

 переданное конструктору базового класса, будет неопределенным.

      Если вы хотите иметь список инициализаторов  в  не-встроенном

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

 Вместо этого поместите его в точку определения функции:

    derived::derived(int i) : a(i)

    {

    ...

    }

                                  Деструкторы

      Деструктор класса вызывается для освобождения членов  объекта

 до   разрушения  самого  объекта.  Деструктор  представляет  собой

 функцию-компонент, имя которой совпадает с  именем  класса,  перед

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

 каких-либо параметров, а также не имеет объявленных типа  возврата

 или значения.

    class X

    {

    public:

       -X();  // деструктор класса X

    };

      Если  деструктор  не  объявлен  для  класса  явно, компилятор

 генерирует его автоматически.

 Запуск деструкторов

      Вызов  деструктора  выполняется  неявно,   когда   переменная

 выходит из своего объявленного контекста. Для локальных переменных

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

 котором они объявлены. В случае глобальных переменных  деструкторы

 вызываются как часть процедуры выхода после main.

      Когда   указатели  объектов  выходят  за  пределы  контекста,

 неявный вызов деструктора  не  происходит.  Это  значит,  что  для

 разрушения такого объекта операция delete должна быть задана явно.

      Деструкторы  вызываются строго в обратной последовательности

 относительно   последовательности   вызова   соответствующих    им

 конструкторов (см. стр.117 оригинала).

 atexit, #pragma exit и деструкторы

      Все глобальные объекты остаются активными до тех пор, пока не

 будут   выполнены   коды  во  всех  процедурах  выхода.  Локальные

 переменные, включая те, что  объявлены  в  main,  разрушаются  при

 выходе  их  из  контекста.  Последовательность  выполнения в конце

 программы Turbo C++ в этом смысле следующая:

 - выполняются функции atexit в последовательности их вставки в

   программу

 - выполняются функции #pragma exit в соответствии с кодами их при-

   оритетов

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

 exit и деструкторы

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

 локальных переменных в текущем контексте не вызываются. Глобальные

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

 abort и деструкторы

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

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

      Деструктор может  быть  также  вызван  явно,  одним  из  двух

 следующих  способов:  косвенно,  через  вызов  delete,  или прямо,

 задавая полностью  квалифицированного  имени  деструктора.  Delete

 можно  использовать  для  разрушения  объектов, для которых память

 распределялась при помощи new. Явный вызов  деструктора  необходим

 только  в  случае объектов, которым распределялся конкретный адрес

 памяти при помощи new.

    class X {

    ...

       -X();

    ...

    };

    void* operator new(size_t size, void *ptr)

    {

       return ptr;

    }

    char buffer[sizeof(x)];

    main()

    {

       X* pointer = new X;

       X* exact_pointer;

       exect_pointer = new(&buffer) X;  // указатель инициализиру-

                                        // ется адресом буфера

    ...

       delete pointer;                  // delete служит для раз-

                                        // рушения указателя

       exact_pointer->X::-X();          // прямой вызов для отмены

                                        // распределения памяти

    }

 Виртуальные деструкторы

      Деструктор может быть объявлен  как  virtual.  Это  позволяет

 указателю  объекта базового класса вызывать необходимый деструктор

 в  случае,  когда  указатель  фактически   ссылается   на   объект

 производного  класса.  Деструктор класса, производного от класса с

 виртуальным деструктором, сам является виртуальным.

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

      Однако,  если  ни  один  из  деструкторов  не  был   объявлен

 виртуальным,   delete   palette[0],  delete  palette[1]  и  delete

 palette[2]  вызывают  только  деструктор  для  класса  color.  Это

 приведет к неправильному разрушению первых двух элементов, которые

 фактически имели тип red и brightred.


Перегруженные операции и операции-функции
                            Перегруженные операции

      С++  позволяет  переопределить действие большинства операций,

 так чтобы при использовании с  объектами  конкретного  класса  они

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

 С++  в  целом,  компилятор  определяет  различия  в  функциях   по

 контексту вызова: по числу и типам аргументов операндов:

      Переопределение  может  быть  выполнено  для  всех  операций,

 приведенных на стр.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 <stdlib.h>

    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 означает "чистую" функцию

      В  классе,  производном  от  такого  базового  класса, каждая

 "чистая"  функция  должна  быть  определена  или  переобъявлена  в

 качестве таковой (см. следующий раздел, "Абстрактные классы").

      Если  виртуальная функция определена в базовом классе, то нет

 необходимости ее переопределения в производном классе. При вызовах

 будет просто вызвана соответствующая базовая функция.

      Виртуальные   функции   заставляют    определенным    образом

 расплачиваться за свою универсальность: каждый объект производного

 класса  должен содержать указатель на таблицу функций с тем, чтобы

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


                              Абстрактные классы

      Абстрактным  называется  класс  с  как  минимум  одной чистой

 виртуальной функцией. Виртуальная функция  задается  как  "чистая"

 при помощи соответствующего спецификатора.

      Абстрактный  класс  может  использоваться  только  в качестве

 базового класса для других классов.  Объекты  абстрактного  класса

 созданы быть не могут. Абстрактный класс не может быть использован

 как   тип   аргумента   или  как  тип  возврата  функции.  Однако,

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

 ссылки  на абстрактный класс при условии, что при инициализации не

 требуется создание временного объекта. Например,

    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;           // переопределение функции

                                    // как "чистой"

      Функций-компоненты  могут  быть  объявлены  из   конструктора

 абстрактного   класса,   но   вызов   чистой  виртуальной  функции

 непосредственно или косвенно из  такого  конструктора  приводит  к

 ошибке времени выполнения.


Объектные потоки С++
Потоки С++

      Данная глава содержит краткий обзор ввода/вывода потоков С++.

 Ввод/вывод  потоков  в   С++   используется   для   преобразования

 типизированных  объектов  в  читаемый текст, и обратно. Он позволяет

 определять  функции  ввода/вывода,  которые  затем   автоматически

 используются    применительно   к   соответствующим   определенных

 пользователем типам.

      Усовершенствованные   потоки  iostream  версии  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,  для  которого  она  вызывалась.  Это

 = 193 =

 позволяет организовать каскадные вставки:

    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              Установка флага форма-

                     ins>>dec               тирования с десятичными

                                            преобразованиями

 hex                 outs<<hex              Установка флага форма-

                     ins>>hex               тирования с шестнадцати-

                                            ричными преобразованиями

 oct                 outs<<oct              Установка флага форма-

                     ins>>oct               тирования с восьмеричными

                                            преобразованиями

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

                                            символов

 endl                outs<<endl             Вставка символа новой

                                          строки и очистка потока

 ends                outs<<ends             Вставка оконечного

                                            пустого символа в строку

 flush               outs<<flush            Очистка ostream

 setbase(int)        outs<<setbase(n)       Установка системы счис-

                                            ления при преобразова-

                                            ниях с основой n (0, 8,

                                            10 или 16). Ноль означает

                                            по умолчанию десятичную

                                            систему при выводе и пра-

                                            вила С для литералов це-

                                            лых чисел при вводе.

 resetiosflags(long) ins>>resetiosflags(l)  Очистка форматных битов

                     outs<<resetiosflags(l) в ins или outs, заданных

                                            аргументом l

 setiosflags(long)   ins>>setiosflags(l)    Установка форматных битов

                     outs<<setiosflags(l)   в ins или outs, заданных

                                            аргументом l

 setfill(int)        ins>>setfill(n)        Установка символа-запол-

                     outs<<setfill(n)       нителя в n

 setprecision(int)   ins>>setprecision(n)   Установка точности пред-

                     outs<<setprecision(n)  ставления чисел с плава-

                                            ющей точкой равной n

                                            разрядам

 setw(int)           ins>>setw(n)           Установка ширины поля

                     outs<<setw(n)          в значение n

 -----------------------------------------------------------------

      Не-параметризованные манипуляторы dec, hex и oct (объявленные

 в  ios.h)  не  принимают  никаких  аргументов  и  просто  изменяют

 основание системы счисления при преобразовании  (и  оставляют  это

 изменение в силе):

    int i = 36;

    cout << dec << i << " "

         << hex << i << "

         << oct << i << endl;

    // вывод на дисплей 36 24 44

      Манипулятор  endl  вставляет  символ  новой  строки и очищает

 поток. Можно также в любой момент очистить ostream, записав

    ostream << flush;

 Заполнители и дополнение вправо и влево

 _________________________________________________________________

      Символ-заполнитель  и  направление  дополнения   зависят   от

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

      По  умолчанию символом-заполнителем является пробел. Изменить

 данное умолчание позволяет функция fill:

    int i = 123;

    cout.fill("*");

    cout.width(6);

    cout << i;         // на дисплей будет выведено ***123

      По умолчанию устанавливается  выравнивание  по  правому  краю

 (дополнение символами-заполнителями влево). Эти умолчания (а также

 прочие  форматные  флаги) можно изменять при помощи функций setf и

 unsetf:

    int i = 56;

    ...

    cout.width(6);

    cout.fill('#');

    cout.setf(ios::left,ios::adjustfield);

    cout << i          // на дисплей будет выведено 56####

      Второй аргумент, ios::adjustfield, сообщает setf, какие  биты

 должны  быть  установлены.  Первый  аргумент,  ios::left, сообщает

 setf,  в  какие  именно   значения   устанавливаются   эти   биты.

 Альтернативно можно использовать манипуляторы setfill, setiosflags

 и      resetiosflags,     которые     позволяют     модифицировать

 символ-заполнитель и  направление  дополнения  при  форматировании

 (см. табл.3.1).

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

 _________________________________________________________________

      Вы   можете  писать  собственные  вставки  для  вывода  своих

 собственных  типов  данных,  перегружая  для  этого  операцию  <<.

 Предположим, у вас имеется тип

    struct info {

       char *name;

       double val;

       char *units;

    {;

      Вы можете перегрузить << следующим образом:

  ostream& operator << (ostream& s, info& m)

    {

       s << m.name << " " << m.val << " " << m.units;

    }

      Операторы

    info x;

    ...

    // здесь инициализируется x

    ...

    cout << x;

 даст на выходе что-либо вроде "capacity 1.25 liters".


Ввод
       Ввод   ----------------------------------------------------------

      Ввод  потоком  аналогичен выводу, но использует перегруженную

 операцию сдвига вправо, >>, и называется операцией извлечения, или

 извлечением. Операция >> обеспечивает более компактную и  читаемую

 альтернативу  семейству  функций  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)

 действие операции  >>   по   умолчанию   заключается   в   пропуске

 не-пробельных  символов  и  преобразовании  интегрального значения

 путем чтения символов ввода до тех пор, пока не встретится символ,

 который не может являться допустимой частью представления  данного

 типа. Формат распознаваемых интегральных значений тот же, что и для

 целочисленных   констант   С++,   за   исключением   целочисленных

 суффиксов.

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

      Если вы задали преобразования  типа  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 байт до конца

      Вам может понадобиться распечатать и изучить комментированные

 файлы  заголовка, чтобы узнать, как взаимосвязаны различные классы

 потоков и как объявляются их функции компоненты.


Объектно-ориентированное программирование
ЧТО ТАКОЕ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

              Биарне Строустрап, AT&T Bell Laboratories

     Не все языки программирования могут быть объектно-ориентированны-

ми. Да, были  сделаны  утверждения,  что  APL,  Ada, Clu, C++, Loops и

Sмalltalk объектно-ориентированные языки. Мне приходилось слышать дис-

куссии об  объектно-ориентированном  программировании  на  C,  Pascal,

Modula-2  и Chill. Можно ли говорить об объектно-ориентированном прог-

раммировании на языках Fortran и Cobol? Я думаю,  что  в общем то да.

     Слово "объектно-ориентированный" стало в области программирования

синонимом слово "хорошо". В печати чаще всего встречаются элементы та-

кого вида:  "Ada - это хорошо; объектной ориентированности - это хоро-

шо; отсюда следует, что Ada - объектно-ориентированный язык".

     В этой статье содержится взгляд на то,  что означает термин "объ-

ектно-ориентированный" в контексте языков программирования общего наз-

начения.  Примеры представлены на C++,  отчасти,  чтобы познакомить  с

С++,  отчасти потому что С++ один из немногих языков, допускающих абс-

трактные типы  данных,  объектно-ориентированное  программирование,  а

также  оставляет возможной традиционную технику написания программ.  Я

не затрагиваю вопросы  конкурентоспособности  и  аппаратной  поддержки

языковых конструкций более высокого уровня.

                      ПОДХОДЫ В ПРОГРАММИРОВАНИИ

     Техника  объектно-ориентированного программирования может служить

образцом написания "хороших" программ для круга проблем.  Если  термин

"объектно-ориентированный язык" что-нибудь да означает, то прежде все-

го,  что  это  язык,  обладающих  механизмами, поддерживающими объект-

но-ориентированный стиль.

     Здесь имеется важное различие: язык поддерживает стиль программи-

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

(действительно простым и эффективным). Язык не поддерживает  некоторую

технологию программирования, если для ее использования приходится зат-

рачивать  дополнительные  усилия;  в  этом случае язык только обладает

средствами для следования технологии. Например, можно писать  структу-

рированные программы  на  языке Fortran или строго типизированные - на

С,  или использовать абстрактные типы данных - в Modula2,  но это  до-

вольно сложно делать,  потому что эти языки не поддерживают упомянутые

возможности.

     Поддержка некоторой парадигмы программирования состоит в  наличии

не только очевидных средств языка для следования ей, но и в более тон-

ких видах проверок на стадиях компиляции и выполнения, позволяющих вы-

являть ненамеренные отклонения от этой парадигмы. Примерами лингвисти-

ческой поддержки парадигмы являются проверка типов, выявление двусмыс-

ленностей,  проверки  во  времени выполнения. Такие внелингвистические

средства, как библиотеки поддержки  или  технологии  программирования,

также могут оказывать существенную поддержку парадигмы.

     Не  обязательно,  чтобы  один язык был лучше другого, так как они

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

важно  не то, какой гаммой средств обладает язык, а насколько они дос-

таточны для поддержания желаемого  стиля  программирования  в  области

приложений. Более точно, важно, чтобы:

     -  было возможно получать решения, комбинируя средства языка, без

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

     - все средства были естественно  и  "элегантно"  интегрированы  в

язык;

     - было как можно меньше специальных средств;

     -  наличие какого-либо средства не приводило к значительному уве-

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

     - пользователю необходимо было знать только необходимые для напи-

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

     Последние два принципа можно перефразировать как "то, чего вы  не

знаете,  не  должно вам навредить". Если имеются сомнения о полезности

некоторого средства, лучше его опустить. Значительно  легче  давать  к

языку новую возможность, чем изъять ее или изменить, поскольку она ре-

ализована в компиляторе и описана в литературе.

     ПРОЦЕДУРНОСТЬ. Изначальной и, вероятно, все еще самой общей пара-

дигмой  программирования  является следующая: "Решите, какие процедуры

вы желаете; используйте лучше из алгоритмов, которые можете найти".

     При этом  внимание  фокусируется на определение процедуры:  выбор

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

эта  парадигма обеспечивается возможностями передачи функциям парамет-

ров и обратного возврата значений.  литература,  касающаяся этой пара-

дигмы,  полна дискуссиями о том,  как передавать параметры, как разли-

чать различные виды аргументов и различные  типы  функций  (процедуры,

процедуры-функций, макросы и пр.) и т.д.

     Fortran  является  первым  процедурным  языком; Algol - 60, Algol

-68, С, Pascal - более поздние проекты, продолжающие эту традицию.

     В качестве примера хорошего  процедурного  стиля  можно  привести

функцию извлечения квадратного корня. Функция точно получает результат

для данного аргумента. Для этого она выполняет хорошо понятные матема-

тические вычисления.

     double sqrt (double arg)

     {

        //команды для вычисления корня

     }

     void some_function ()

     {

        double root2=sgrt(2);

        //...

     Процедурное  программирование использует функции для создания по-

рядка в лабиринте алгоритмов.

     ЛОКАЛИЗАЦИЯ ДАННЫХ.  Со временем основное внимание при разработке

программние все  более  и  более  начало перемещаться с проектирования

процедур к организации данных. Среди других причин этого, следует ука-

зать увеличение размеров программ.  Множество связанных процедур и об-

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

здесь следующая:  "Решите, какие модули вы желаете; разбейте программу

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

ходимости группировать процедуры вокруг данных,  соблюдается процедур-

ный стиль.  Фактически,  технология  проектирования  хороших  процедур

должна теперь применяться к каждой процедуре модуля.

     Наиболее  общим  примером упрятывания данных является модуль, ис-

пользующий стек. Хорошее решение требует:

     - пользовательского интерфейса к стеку (например, функции push ()

и pop ();

     - доступа к стеку только с использованием принятого интерфейса;

     - инициализации стека перед первым его использованием.

     Возможным внешним интерфейсом к модулю со стеком может быть:

     //определение интерфейса к модулю

     //стек символов

     char pop ();

     void push (chav);

     const stack_size=100;

     Предположим,  что  этот  интерфейс хранится в файле под названием

 stack.h, тогда внешние переменные могут быть определены следующим об-

 разом:

     #include "stack.h"

     Static char v[stack_size]; //"static" означает, что переменная

                               //является локализованной в этом

                              //файле (модуле

     static char* P=V;       //стек вначале пуст

     char pop ()

     {

       //проверить, не пуст ли стек, и pop

     }

       //проверить, не переполнен ли стек, и push

     }

     Несложно преобразовать стековое представление в связанный список.

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

тому  что  V  и P определены как static, т.е. локализованы в файле или

модуле, в котором декларированы). Такой стек можно использовать следу-

ющим образом:

     # include "stack.h"

     void some_function ()

     {

       char c = pop(push ('c'));

       if (c!='c') error ("impossible");

     }

     Изначально Pascal не обеспечивает удовлетворительные средства по-

добные возможности группирования,-единственным образом спрятать имя от

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

приводит  к  странному гнездованию данных и излишней"нагрузки" на гло-

бальные данные.

     С языком С дела в этом смысле обстоят лучше. Как  было  показано,

вы  можете  определить модуль, группируя процедуры и определения соот-

ветствующих данных в простом исходном файле. А затем программист может

управлять и использованием данных в остальных  частях  программы  (имя

становится недоступным, если его описать в программе, как static). Та-

ким  образом в С может быть достигнута степень модульности. Однако та-

кой путь, основанный на static-декларациях, является довольно  низкоу-

ровневым.

     В  одном  из  Pascal-подобном  языке - Modula-2 - пошли несколько

дальше. В нем формализация понятия модуля происходит путем определения

модуля как фундаментальной конструкции с хорошо определенными деклара-

циями, явным контролем всей совокупности используемых  имен  (средства

импорта/экспорта),  механизмом инициализации модуля и множеством обще-

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

     Другими словами, в С имеются средства программы на модули,  в  то

время как Modula-2 поддерживает эту возможность.

     АБСТРАКЦИЯ ДАННЫХ. Модульное программирование ведет к централиза-

ции  контроля  за всеми данными определенного типа в модуле управления

типами данных. Если вам необходимо два стека, то вы можете  определить

управляющий модуль с интерфейсом такого вида:

     //stack_id это тип; никаких других подробностей

     //stack или stack_ids здесь доступны:

     class stack_id;

     //организовать стек и вернуть его идентификатор

     stack_id create_stack (int size);

     //обратиться, когда стек больше не нужен

     destroy_stack (stack_id);

     void push (stack_id,char);

     char pop (stack_id);

     Это  действительно  большое улучшение по сравнению с традиционным

неструктурированным "месивом", но вводимые подобным образом типы силь-

но отличаются от типов, встраиваемых в язык.

     В наиболее важных аспектах типы, создаваемые с использованием ме-

ханизмов модулей разнятся от встроенных в язык  типов  и  используются

для следующих целей: каждый модуль управления типами должен определять

отдельный  механизм для создания переменных своего типа; здесь нет ус-

тановленных правил для присваивания типам  объектных  идентификаторов;

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

компонентам программной технологии, такие переменные не  удовлетворяют

требованиям,  предъявляемым  к обычным переменным, и правилам передачи

аргументов.

     Например:

     void f ()

     {

       stack_id s1;

       stack_id s2;

       sl=create_stack (200);

       //Oops:забыли создать S2

       char c1=pop(s1,push(s1,'a'));

       if (c1!='c')error("impossible");

       chavc2=pop(s2,push(s2,'a'));

       if(c2!='c')error("impossible");

       destroy(s2);

       //Oops:забыли разрушить s1

     }

     Другими словами, концепция модуля, поддерживающая парадигму лока-

лизацию данных, обладает возможностями абстракции данных, но  не  под-

держивает ее.

     АБСТРАКТНЫЕ ТИПЫ ДАННЫХ. В таких языках, как Ada, Clu, С++, проб-

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

телю возможностей определения типов, которые ведут себя почти таким же

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

рактными типами данных, хотя я предпочитаю называть их типами, опреде-

ленными  пользователем.  (Как  отмечал Дуг Маклрой (Doud Mellroy), эти

типы не являются абстрактными, они так же реальны, как  типы  inf  или

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

кого  определения всех типов (как встроенных, так и определенных поль-

зователем). В этой статье мы будем понимать под такими  типами  данных

конкретные  спецификации  таких  действительно  абстрактных объектов.)

Здесь парадигма программирования  следующая:"Решите,  какие  типы  вам

нужны; обеспечьте полный набор операций для каждого типа".

     Когда  нет нужды более чем в одном типе, достаточно стиля локали-

зации данных с использованием модульности. Общими примерами, определя-

емых пользователем типов данных являются  арифметические  типы  рацио-

нальных и комплексных чисел:

     class complex{

        double re,im;

     public:

        complex(double r,double ){re=r;im=i;}

     // преобразование float complex:

        complex(double r){re=r;im=0}

        friend complex operator+(complex,complex);

     // бинарный минус:

        friend complex operator-(complex,complex);

     // унарный минус:

        friend complex operator * (complex);

        friend complex operator/(complex,complex);

     //...

     }

     Декларация  комплексного  класса (определенный пользователем тип)

классифицирует представление комплексного числа и  множество  операций

под  такими  числами. Представление является личным, т.е. re и im дос-

тупны только функциям,  специфицированным  в  декларации  комплексного

класса. Такие функции можно определить так:

     complex operator+(complex a1, complex a2)

     {

      return complex (a1.re+a2.re,a1.im+a2.im);

     }

и использовать так:

     complex a=2.3;

     complex b=1/a;

     complex c=a+b*complex(1,2,3);

     //...

     с=-(а/в)+2;

     Лучше  большую часть модулей (но не все) представлять как опреде-

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

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

ределения типов, он сможет определить только единичный объект  требуе-

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

ности в дополнение или в противовес концепции класса.

     ПРОБЛЕМЫ.  Определяемый пользователем тип является черным ящиком.

Будучи однажды определенным, он в действительности никак не взаимодей-

ствует с остальной частью программы. Единственным  способом  адаптации

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

очень негибкий способ.

     Рассмотрим  определяемый  для   использования   в   графике   тип

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

ти, треугольники и квадраты. Допустим также, что у вас есть ряд  клас-

сов:

     class point{/*...*/};          (point - точка)

     class color{/*...*/};          (color - цвет)

     Можно определить тип форма - shape - как:

     enum Kind{cirele,triangle,sguare};   (сircle -круг,

                                          triaugle -треугольник,

                                          squre - квадрат)

     class shape{

           point center;

           coljr col;

           kind k;

           //представление формы

     public:

           point where() {returu center;}

           void move (point to){cenre=to;draw();}

           void draw();

           void rotate (int);

           //другие операции

     };

     Тип  field,  k используется в таких операциях, как draw() - рисо-

вать и rotate() - вращать, для определения формы, с которой они  имеют

дело  (в  языках  типа  Pascal  можно использовать иную запись с тегом

k-tag k).

     Функцию draw() можно определить так:

     void shape draw()

     {

     switch (k){

     case circle:

         //рисовать круг

           break;

     case triangle:

        //рисовать треугольник

          break;

     case square:

        //рисовать квадрат

     }

     }

     Этот способ плох тем, что функции типа draw () должны  знать  все

используемые  виды форм. Поэтому код каждой такой функции должен моди-

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

     Если определяется новая форма, то все операции над типом  "форма"

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

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

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

можное  переопределение  хода всех важных операций, этот процесс может

потребовать высокой квалификации и может  также  привести  к  внесению

ошибок в код операции, касающейся и других форм.

     Ваш выбор представления отдельной формы может быть строго ограни-

чен  требованиями настолько, что по крайней мере некоторые представле-

ния будут находиться в рамках фиксированного  образца  представляемого

определением общего типа "форма".

     Проблема состоит в том, что пропадает отличие между общими свойс-

твами,  присущими  всем  формам  (цвет,  свойство  быть нарисованной и

т.д.), и специфическими (круг есть форма, имеющая радиус;  рисуется  с

помощью функции circle-drawiug изображения окруности и т.д.).

               ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

     Возможности  выразить  отличия  между представителями типа и вос-

пользоваться преимуществами  этого  отличает  объектно-ориентированное

программирование.

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

нутые отличия и воспользоваться этим, поддерживает объектно-ориентиро-

ванное программирование.

     Возможное  решение  предложено  в языке Simula-2 с использованием

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

ляющий общие свойства всех форм:

     class shape{

           point center;

           color col;

           //...

     public:

           point where () {return center;}

           void move (point to) {center=to;draw();}

           virtual void draw()ж

           мшкегфд мщшв кщефеу(int);

           //...

     };

     Отмеченные виртуальные функции это те, для которых можно специфи-

цировать общий интерфейс обращения, а особенности использования указы-

ваются для конкретных форм. (Термин "виртуальная"  принадлежит  языкам

Simula и C++ как обозначающий "может быть переопределена позже в клас-

се, полученном из класса, характеризующего общие свойства".) При таком

определении, мы можем написать общие функции манипулирования формами:

     void rotate-all (shape*V,int size,int augle)

         //вращать все элементы вектора "v"

         //значения "размеры-size","угол-augle"

        {

           tor (int i=0;i<size;i++)

           v[i].rotate(augle);

         }

     Чтобы определить конкретную форму, необходимо указать, что объект

является формой и специфицировать ее конкретные свойства:

     class circle:public shape {

         int radius;

     public:

         void draw (){/*...*/};

         void rotate (int){}//да, пустая

                           // функция

     };

     В C++ говорится, что класс окружностей является производным клас-

са форм и обратно, класс форм является базовым для класса окружностей.

В другой терминологии класс окружностей-это подкласс, форм-суперкласс.

     Парадигма программирования такова: "Решите, какие вам нужны клас-

сы; для каждого класса обеспечьте полный набор операций; сделайте общ-

ность свойств явной, используя "наследственность".

     Если нет общности свойств, абстракция данных теряет смысл. Тестом

оценки применимости объектно-ориентированного программирования являет-

ся количество типов с общими свойствами таких, что эта общность сможет

быть использована в механизме наследования свойств и виртуальных функ-

циях.

     В  некоторых  областях  программирования, таких как интерактивная

графика, имеются поистине большие возможности применения объектно-ори-

ентированного подхода. В других, таких как классические арифметические

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

большем,  чем абстракция данных. (Однако, в более сложных разделах ма-

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

ля-специальный вид колец, векторные пространства-модулей.)

     Поиск общих свойств среди типов далеко не тривиальный процесс. От

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

использована общность свойств. Общность должна все  время  быть  перед

глазами,  во  время  проектирования  системы,  как при специфицировани

классов (строя описания для других типов), так и прианализе того,  об-

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

му классу. Hurapg (nygaard)[1] и Kerr(Kerr)[2]объясняют, что такое об-

ъектное  программирование, не прибегая к специфическим лингвистическим

конструкциям; (Carqill)[3] посвятил объектному программированию специ-

альное исследование.

                      ПОДДЕРЖКА АБСТРАКЦИИ ДАННЫХ

     Программирование с абстракциями данных поддерживается как средст-

вами описания множества операций для типа данных, так и средствами ог-

раничения доступа к объектам этого типа для  множества  операций.  Что

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

ствования  языков  в  направлении удобного определения и использования

новых типов.

     ИНИЦИАЛИЗАЦИЯ И ОЧИСТКА. Когда  представление  типа  локализовано

спрятано  для пользователя, должны предоставляться некоторые механизмы

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

является обращение к некоторой функциидля инициализации переменных пе-

ред их использованием. Например,

     class vector{

        int sz;

        int*v;

     public:

        void init(int size); //вызвать функцию init

                             //для инициализации

                            // sz и v перед первым

                            // использованием вектора

     //...

     };

     vektor v;

     //не используйте здесь v

     v.init(10);

     //здесь можно использовать v

     Но  этот способ чреват ошибками и неестествененен. Более предпоч-

тительное решение состоит  в  позволении  пользователю,  определяющему

тип,  проводить  инициализацию в функции специфицирования производного

типа.Такая функция превратит распределение памяти и инициализацию  пе-

ременной  в  единичную  операцию(часто  называемую  установкой) вместо

двух.  Такая  функция   установки   также   называется   конструктором

(coustructor).

     В случаях, когда конструирование новых типов не тривиально, часто

необходимо  проводить  противоположную операцию очистки объектов после

их последнего использования. В С++ функция очистки называется деструк-

тором (destructor). Рассмотрим тип "вектор" (vektor).

     class vektor{

        int sz;   //количество элементов

        int*v;   //указатель к целым(inteqers)

     public:

        vector(int); //конструктор

        vector();   //деструктор

        intzoperator[] (int index); //индекс-оператор

     };

     Конструктор типа "вектор" может  распределять  память,  например,

так:

     vector::vector(int s)

     {

        if(s<=0)error("неверна размерность вектора");

        sz=s;

        v=new int[s]; //распределить массив s целых

     }

     Деструктор типа "вектор" освобождает память:

     vector::~vector()

     {

         delete v; //освободить память,

                  // выделенную v

     }

     С++  не  поддерживает  сборку  мусора. В языке это компенсируется

тем, что типу позволено  свое  управление  памятью  без  вмешательства

пользователя.  Поскольку это является общим примером использования ме-

ханизма конструкторов (деструкторов), многие пользователи,  использую-

щие его, не касаются управления памятью.

     ПРИСВОЕНИЕ  И ИНИЦИАЛИЗАЦИЯ.Управление конструкцией (деструкцией)

объектов оказывается достаточным для многих типов,  но  не  для  всех.

Иногда  необходимо  контролировать  операции  копирования.  Рассмотрим

класс векторов:

     vector v1 (00);

     vector v2 =v1; //проинициализировать

                    //новый вектор v2 значениями

                    //вектора v1

     v1=v2;

     Должна быть возможность определять значение  инициализации  v2  и

 его  присвоение v1. Также должны быть средства, препятствующие подоб-

 ным операциям копирования; предпочтительнее, чтобы были доступны  обе

 альтернативы. Например:

     class vector {

        int*v;

        int sz;

     public:

        //...

        void operator=(vector z); //присвоение

        vector(vector z);         //инициализация

     };

     означает,  что определенные пользователем операции должны исполь-

 зоваться для интерпретации  векторного  присвоения  и  инициализации.

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

     vector::operator=(vector z a)

         //проверить разменность и скопировать

         //элементы

     {

         if(sz!=a.sz)

         error("неверная размерность для =");

         for(int i=0;i<sz;i++)v[i]=a.v[i];

     }

     Операция инициализации должна отличаться от операции присвоения в

 связи  с  тем,  что присвоение зависит от исходного значения вектора.

 Например:

     vector::vector(vector z a)

         //инициализация вектора значениями другого вектора

     {

         sz=a.sz;       //та же размерность

         v=new int[sz]; //выделить память массиву элементов

               //скопировать элементы

               for(int i=0;i<sz;i++)v[i]=a.v[i];

     }

     С++ конструктор х(хz)определяет всю процедуру инициализации  объ-

 ектов  типа  х с помощью значения элементов другого объекта типа х. В

 дополнение и явной инициализации конструкторы вида x(xz) используются

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

 кциями значений.

     В С++ присвоению объекта класса ч можно воспрепятствовать, объяв-

 ляя операцию присвоения как личную:

     class x{

        void operator=(xz); //только члены х

                            //могут копировать х

        х(хz);

     public:

        ...

     };

     В языке Aga не поддерживаются конструкторы,  деструкторы,  перег-

рузка  путем  присвоения, определенный пользователем контроль передачи

параметров и возвращаемого функцией значения.  Эти  недостатки  строго

ограничивают класс типов, которые разрешается определять, и заставляет

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

приходится проектировать и использовать модули управления типами вмес-

то соответствующих типов.

     ПАРАМЕТРИЗОВАННЫЕ ТИПЫ.Почему возникает необходимость  каким-либо

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

вателю  требуется  вектор  элементов некоторого кипа, а это может ока-

заться несогласованным с определением типа вектор.  Следовательно  тип

вектор должен определяться таким образом, чтобы тип элемента был пара-

метром:

     class vector<classT>{

        //вектор элементов типа Т

     T*v;

     int sz;

     public;

        vector(int s)

     {

        if(s<=0)error("неверна размерность вектора");

        v=new T[sz=s]; //распределить память под массив

     }

     Tz operator[](int i);

     int size(){return sz;}

     //...

     };

     Теперь  можно определять и использовать векторы специфических ти-

пов:

     vector<int>v1(100);//v1-вектор из 100 целых чисел

     vector<complex>v2(200);//v2-вектор из 200 комплексныхчисел

     v2[i]=complex(v1[x],v1[y]);

     В  языках Ada и Clu поддерживаются параметризовнные типы. К сожа-

лению в С++ этого средства нет; приведенные примеры использовали  экс-

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

метризованные классы, они подменяются макросами. В  случае,  если  все

используемые типы явно определены, на стадии выполнения дополнительные

накладные расходы, связанные с использованием классов, не возникают.

     Обычно  параметризованный  тип  зависит по крайней мере от одного

аспекта параметра типа. Например, некоторые векторные операции  должны

допускать, что присвоение определено для объектов с определенным типом

параметра. Как вы можете в этом быть уверены? Один способ решения сос-

тоит  в установлении зависимости лицом, определяющим параметризованный

класс. Например, Т может быть типом, для которого определена  операция

"=".  Предпочтительненее не требовать этого или допускать специфициро-

вание типа параметра как частное специфицирование. Компилятор в состо-

янии обнаружить, была ли опущенная операция примененена и выдать сооб-

щение вида:

     cannot define

     vector<non_copy>::operator[](non_copy z):

        type noncopy does not have operator=

     Эта технология позволяет таким образом описывать типы, что  зави-

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

видуальной  операции  типа.  Например,  можно определить операцию sort

(сортировка) для вектора.Операция  sort  может  использовать  операции

<,==,==  над  объектами параметрического типа. Возможность определения

вектора такого типа, что операция < для него не определена, сохраняет-

ся до тех пор, пока операция sort не была действительно использована.

     При использовании параметризованных типов проблема состоит в том,

что  каждая  актуализация  создает  независимый  тип.   Например   тип

vector<chav>  не связан с типом vector<complex>.В идеале должна сохра-

няться возможность выражать и использовать общие свойства типов, полу-

ченных из одного параметризованного типа. Например, и к vector<chav> и

к vector<cjmplex> применима функция size(), не зависящая от типа пара-

метра. Оказывается возможным, хотя это и не просто, выводить  общность

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

ние функции size() к любому вектору. И в этом преимущество языка, под-

держивающего  как  параметризацию  типов,  так и механизм наследования

свойств.

     ОБРАБОТКА ОШЫБОК.По мере увеличения размеров программ и интенсив-

ности использования библиотек, становится важной стандартизация  обра-

ботки ошибок (или исключительных ситуаций).

     В  языках  Ada,Algol-68,Clu есть средства стандартизованной обра-

ботки ошибок. В С++ таких средств, к сожалению нет. Если возникает не-

обходимость, ошибочные ситуации обрабатываются с использованием указа-

телей к функциям, ошибочных состояний и средств  библиотеки  языка  Си

signal  и  longjmp. Это все является явно неудовлетворительным, потому

что даже не может обеспечить стандартную схему обработки ошибок.

     Рассмотрим опять пример класса векторов. Что следует предпринять,

если оператору определения индекса будет передано значение,  выходящее

за допустимые пределы? Проектировщик векторного класса должен обладать

возможностями обеспечения для этой ситуации действий по умолчанию:

     class vector{

        ...

     except vector-range{

        //определить ошибочную ситуацию,

        //называемую vector-range и специфицировать

        //программу для ее обработки

        error("global:vector range error");

        exit(99);

     }

     }

     Вместо   обращения   к   функции    обработки    ошибок    error,

vector::operator[]() может генерировать код ошибки:

     intz vector::operator[](int i)

     {

        if (0<i//sz<=i)raise vector_range;

        return v[i];

     }

     Это приведет к тому, что стек обращений к функциям останется "не-

разрушенным" до тех пор, пока будет искаться и выполняться функция об-

работки ошибки.

     Функция  обработки  ошибки может быть определена для специального

блока программы:

     void f(){

        vector v(10);

        try{    //здесь обрабатываются ошибки

                //определенной нише локальной

                //функцией обработки ошибок

     //...

     iut i=g(); //g может приводить к ошибке

                //размерности из-за потенциально

                //возможной ошибки размерности

     v[i]=7;    //вектора

     }

     except{

        vector::vector_range:

        error("f():ошибка ранга вектора");

        return

     }

                 //здесь ошибки обрабатываются

                 //глобальной функцией обработки

                 //ошибок, определенной в классе vector

     int i=g();  //g может приводить к ошибке

                //размерности из-за потенциально

                //возможной ошибки размерности

     v[i]=7;

     }

     Существует  много способов определения ошибочных ситуаций и пове-

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

жи на соответствующие возможности языка Modula-2+. Этот стиль обработ-

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

ошибок  не  выполнялась  до тех пор, пока не возникнет соответствующая

ошибочная ситуация (исключая, возможно, стадию  начальной  инициализа-

ции).  Воспользоваться такими средствами можно в любых реализациях Lu,

используя функции Setjmp() и lougjmp() (см.руководство  по  библиотеке

Си для вашей операционной системы).

     Можно  ли  описанным  способом переложить всю обработку ошибочных

ситуаций на такой язык, как С++? К сожалению, нет. препятствие состоит

в том, что когда случается ошибка, нельзя освобождать стек вызовов  до

точки  обращения  к функции обработки ошибок. Чтобы это правильно сде-

лать в С++ используются вызываемые деструкторы, определенные в исполь-

зуемых программах. Это не отмечено в С-функции  lougjmp()  и  в  общем

случае не может быть обеспечено пользователем.

     ПРЕОБРАЗОВАНИЯ. определяемые пользователем преобразования, напри-

мер,   плавающих   чисел   в   комплексные   с   помощью  конструктора

complex(double), оказались неожиданно полезными в С++. Такие  преобра-

зования  могут  применяться явно или неявно с помощью компилятора там,

где это необходимо и не приводит к противоречиям:

     complex a=complex(1);

     complex b=1; //неявно:

                  //1->complex(1)

     a = b + complex (2);

     a = b + 2;   //неявно:

                  //2->complex(2)

     Преобразования были введены в С++, поскольку арифметика смешанных

типов необходима для численных преобразований (что является  нормой  в

языках), а также в связи с тем, что большая часть определяемых пользо-

вателем типов для вычислений (матрицы, строки символов, машинные адре-

са) естественно отображаются (туда и обратно) на другие типы.

     Один способ использования преобразований оказался особенно полез-

ным при организации программ:

     complex a=2;

     complex b=a+2; //интерпретируется как

                    //оператор + (а,complex(2))

     b = 2 + a;     //интерпретируется как

                    //оператор + (cjmplex(2),a)

     Здесь  необходимо  проинтерпретировать  только один оператор +, а

два операнда обрабатываются системой  идентично  с  помощью  механизма

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

всякой необходимости модифицировать класс целых чисел для естественной

интеграции этих двух понятий.

     Это  контрастирует  с  "чистой" объектно-ориентированной системой

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

     a + 2;//a.operator + (2)

     2 + a;//2.operator + (a),

     что приводит к необходимости модифицировать  класс  целых  чисел,

чтобы допустить возможность выражения 2 + а.

     Следует,  по возможности, при добавлении новых средств в систему,

избегать модификации существенного программного кода. Как правило, об-

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

средства пополнения системы без модификации существенного кода. В рас-

сматриваемом  случае,  однако, механизм абстракции данных обеспечивает

лучшее решение.

     ПОВТОРИТЕЛИ(iterators).  Язык,  который  поддерживает  абстракцию

данных,  должен  обеспечивать  способ определения управляющих структур

[4]. В частности, пользователям необходим механизм определения  циклов

с элементами, содержащимися в объекте некоторого определенного пользо-

вателем типа, вне зависимости от особенностей реализации пользователь-

ского  типа.  При наличии достаточно мощного механизма для определения

новых типов и операторов перегрузки это можно сделать без введения от-

дельного механизма определения управляющих структур.

     В случае векторов нет необходимости определения повторителя, пос-

кольку пользователю доступны средства упорядочения с помощью индексов.

Я приведу один способ для демонстрации техники.

     Существует несколько  стилей  итерирования.  Предпочитаемый  мной

опирается на перегрузку прикладного оператора функции ():

     class vector iterator{

        vector z v

        int i;

     public:

        vector_iterator(vector z r){i=0;v=r;}

        int operator ()()

        {return i<v.size()?v.elem(i++):0;}

     };

     (Этот  стиль  также  опирается на существование различных величин

для представления конца итерации. Часто, в частности, для типа  указа-

телей в языке С++, используется ).

     Теперь можно декларировать тип vector_iterator и использовать его

для векторов:

     vector v(sz);

     vector_iterator next(v);

     int i;

     while (i=next())print(i);

     Для  одного  объекта одновременно может быть активизировано более

одного повторителя, а также для одного типа можно иметь несколько  ти-

пов повторителей, так что есть средства организации различных тераций.

Повторитель  это довольно простая управляющая структура. Можно опреде-

лять и более общие механизмы.  Например,  стандартная  библиотека  С++

обеспечивает класс сопутствующих утилит (coroutine class)[s].

     Для  многих  включающих  типов, таких, как вектор, можно избежать

ввода отдельного типа повторителя путем определения механизма итераций

как части самого типа. Тип "вектор" может быть  определен  так,  чтобы

иметь "текущий элемент":

     class vector{

     int*v;

     int sz;

     int current;

  public:

     //...

     int next()

     {return (current++<sz)?v[current]:0;}

     int prev()

     {return(0<-current)?v[current]:0;}

  };

     Тогда интерацию можно организовать так:

     vector v(sz);

     int i;

     while (i=v.next())print(i);

     это  решение  не является таким общим, как механизм повторителей,

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

необходим  только один вид итерации и используется каждый момент време

ни только одна итерация.

     При необходимости можно применять более общие решения в  дополне-

ние  описанному.  Описанный способ требует от разработчика включающего

класса большей предусмотрительности, чем механизм повторителей. техни-

ка типа повторителей позволяет определять повторителей, которые  можно

связать с несколькими различными включающими типами. Тем самым обеспе-

чивается  механизм  итераций с единственным типом повторителя для раз-

личных включающих типов.

     ОСОБЕННОСТИ РЕАЛИЗАЦИИ.Изначально абстракция  данных  обеспечива-

лась средствами языков программирования, поддерживаемых компиляторами.

Однако  параметризованные  типы  лучше реализовать, используя редактор

связей и некоторую информацию о семантике языка, а обработку  исключи-

тельных ситуаций лучше обеспечивать на стадии выполнения. Можно приме-

нять  оба способа, чтобы уменьшить время компиляции и увеличить эффек-

тивность программ без нарушения общности механизмов программирования и

удобств написания программ.

     По мере расширения возможностей определения типов, программы  бу-

дут  все более зависеть от типов описания которых содержатся в библио-

теках (и не только ожидаемых в руководствах по  использованию  языка).

Это,  естественно,  накладывает  дополнительные требования на средства

описания того, что должно быть записано в библиотеку или  получено  из

нее,  что должно содержаться в библиотеке, какие ее компоненты исполь-

зуются программой и т.д.

     Для языков компилируемого типа важны возможности проведения  оце-

нок  увеличения  кода программ после внесения изменений. Очень сущест-

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

мерам модуль и не присоединяли к нему большое количество  неиспользуе-

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

библиотека /редактор связей/загрузчик включают в состав программы  код

каждой определенной для типа операции, тогда как программист использу-

ет одну или две, то это скорее плохо, чем просто бесполезно.

     Основными обеспечивающими функциями, которые необходимы при напи-

сании  объектно-ориентированных  программ,  являются функции механизма

определения класса со свойствами наследования черт класса и механизма,

реализующего подключение только функций  для  актуализированных  типов

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

     Проектирование механизма подключения только  необходимых  функций

очень существенно. В дополнение отметим, что в случаях, когда доступно

объектно-ориентированное  программирование,  важны  средства поддержки

техники абстракции данных.

     Успех обеих технологий зависит от средств проектирования типов, а

также от легкости, гибкости и эффективности их использования.  Объект-

но-ориентированное  программирование  просто  значительно  увеличивает

гибкость использования определяемых пользователем  типов  и  сферу  их

применения по сравнению с технологией абстракции данных.

     МЕХАНИЗМ  ВЫЗОВА.  Ключевым средством языка для поддержки объект-

но-ориентированного программирования является  механизм,  регулирующий

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

теле Р, как реализовать обращение р->f(arg)? Существуют различные спо-

собы решения.

     В  таких языках, как С++ и Simula, где широко используется стати-

ческая прверка типов, система поддержки типов может использовать  раз-

личные механизмы вызова. В С++ есть две альтернативы:

     1.  Обычное  обращение  к функции: нужная функция определяется во

время компиляции (просмотром таблицы символов компилятора);  обращение

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

аргумента, идентифицирующего объект,  для  которого  нужна  вызываемая

функция.  В  случаях,  когда стандартный call-механизм не эффективен в

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

встраивать в код программы в месте обращения к ней. Это позволяет дос-

тигнуть эффективности макрорасширений, не нарушая стандартной семанти-

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

абстракции данных.

     2.ВИРТУАЛЬНОЕ  ОБРАЩЕНИЕ  К ФУНКЦИИ:вызов функции зависит от типа

объекта, который обычно можно определить только  во  время  выполнения

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

которым базовым классом В, а объект относится к некоторому производно-

му классу D.Call-механизм должен"заглянуть"в объект, найти  помещенную

туда  компилятором  информацию и определить, к какой служебной функции

следует обратиться. Как только соответствующая функция,  скажем  D::F,

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

стадии компиляции имя F преобразуется в индекс к таблице указателей  к

функциям.  Такой  виртуальный Call-механизм может быть сделан таким же

эффективным, как и обычный. В стандартной реализации языка С++, только

пять ссылок на дополнительную память. В  случаях,  когда  тип  объекта

можно определить во время компиляции, можно исключить даже эти наклад-

ные  расходы  и  использовать  средства встраивания кода функции в код

программы. Такие случаи являются достаточно общими и важными.

     В языках с неразвитыми  средствами  статической  проверки  типов,

следует  использовать третью, более проработанную альтернативу. В язы-

ках, подобных Smalltalk список всех служебных функций (называемых  ме-

тодами)  класса  хранится  таким образом, чтобы их можно было найти на

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

     3.  ОБРАЩЕНИЕ  К СЛУЖЕБНОЙ УТИЛИТЕ:вначале ищется соответствующая

таблица имен сдужебных утилит с помощью анализа описания  объекта,  на

который  указывает  указатель Р. В этой таблице (или множестве таблиц)

ищется строка F, если объект содержит F(). Если F() обнаруживается, то

к ней происходит обращение; иначе обрабатывается  ошибочная  ситуация.

Этот  просмотр отличается от просмотра, проводимого на стадии компиля-

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

к служебной утилите используется таблица служебных утилит для актуаль-

ного  объекта.  Описанный способ является неэффективным по сравнению с

виртуальным Call-обращением, но более гибким. Так как чаще всего  ста-

тическая проверка аргументов типов не может быть выполнена при обраще-

нии к служебной утилите, использование механизма служебных утилит дол-

жно обеспечиваться динамической проверкой типов.

     ПРОВЕРКА ТИПОВ.Приведенный пример с формами показал  мощь  вирту-

альных  функций. Что еще предоставляет программисту механизм обращения

к служебной утилите? Он позволяет использовать любую служебную утилиту

для любого объекта.

     Эта возможность позволяет разработчику библиотеки функций  общего

назначения перенести ответственность по обработке типов на плечи поль-

зователя.  Естественно это облегчает проектирование библиотеки. Напри-

мер:

     class stack {  // предположим класс any

                    // содержит функцию next

     any*v;

     void push (anyx p)

     {

        p >next = v

        v = p;

     }

     any*pop ()

     {

        if(v==0)return error_obj;

        ani*r=v;

        v=v->next;

        return r;

     }

     };

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

     Stack<anu*>cs;

     cs.push(new Saab 900);

     cs.push(new Saab 37B);

     plane*p=(plane*)cs.pop();

     p->takeoff();p=(plane*)cs.pop();

     p->takeoff();

        //ошибка на стадии выполнения;

        //Saab 900 это автомобиль

        //автомобиль не имеет служебной утилиты takeoff

     Попытка использования класса автомобилей как класса самогетов бу-

дет обнаружена обработчиком сообщений, который обратится затем к функ-

ции обработки соответствующей ошибки. Однако это помогает, когда поль-

зователь и программист одно и то же лицо. Отсутствие статической  про-

верки  типов не позволяет гарантировать, что ошибки подобного типа от-

сутствуют в системе, передаваемой конечному пользователю.

     Комбинация параметризованных классов и виртуальных функций позво-

ляет достичь гибкости, простоты проектирования,  легкости  использова-

ния,  что  отличает механизм библиотеки с просмотром служебных утилит,

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

ходов (по времени и памяти) на стадии выполнения программы. Например:

     stack<plane*>cs;

     cs.push(new Saab 900);

        //Ошибка на стадии компиляции: смешение типов;

        //используется car*, ожидается plane*

     cs.push (new Saab 37 B);

     plane*p=cs.pop();

     p->takeoff(); //ошибкаЖ Saab 37 B это самолет

     p=cs.pop();

     p->takeoff();

     Использование статической проверки типов и механизма обращения  к

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

от стиля, определяемого динамической проверкой типов и механизма мето-

дов.

     Например, классы в языках Simula или С++  специфицируют  фиксиро-

ванный  интерфейс  к  соответствующему  множеству объектов (или любому

производному классу), в то время как в языке Smalltalk классы специфи-

цируют базовое множество операций для объектов (или подкласса). Други-

ми словами, классы в языке Smalltalk определяют минимальные специфика-

ции, а пользователь может пытаться использовать  не  специфицированные

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

и  компилятор допускает только операции, специфицированные в определе-

нии класса.

     МЕХАНИЗМ НАСЛЕДОВАНИЯ. Рассмотрим язык, в котором реализована не-

которая форма просмотра служебных утилит без использования  механизмов

наследования.  Поддерживает  ли  такой  язык  объектно-ориентированное

программирование? Я думаю, что нет.

     Понятно, что можно делать интересные вещи, используя таблицу  ме-

тодов  для адаптации поведения объектов к определенным условиям. Одна-

ко, чтобы при этом избегать хаоса, необходимо выбрать некоторый способ

ассоциирования служебных утилит с допускаемыми или структурами  данных

для  представления объектов. Чтобы обеспечить пользователя информацией

о поведении объектов, необходимо также принять  некоторый  стандартный

способ  выражения общих черт поведения объектов. Механизм наследования

и представляет собой такой систематический стандартный путь решения.

     Рассмотрим язык, в котором обеспечивается механизм  наследования,

но  не  включены возможности виртуальных функций или служебных утилит.

Поддерживает ли такой язык объектно-ориентированное  программирование?

Я  думаю, что нет: в таком языке трудно предложить хорошее решение для

примера с формами.

     Однако такой язык будет более мощным, чем язык только с  абстрак-

цией  данных.  Это  утверждение подтверждается тем фактом, что большое

количество написанных на языках Simula и С++ программ, структурируется

с использованием иерархии классов без виртуальных функций. Возможность

выражения общности (факторизация) является поистине мощным  средством.

Например,  на  этом пути удается решить проблемы, касающиеся необходи-

мости иметь общее представление всех форм.

     Однако, при отсутствии средств виртуальных функций,  программисту

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

действительных типов объектов, что оставляет нерешенными проблемы  мо-

дульности.

     Отсюда  следует, что механизм производных классов (подклассы) яв-

ляется важным программистским инструментарием. Он  имеет  более  общее

применение,  чем только поддержка объектно-ориентированного программи-

рования. Это утверждение в частности верно, если связать механизм нас-

ледования в объектно-ориентированном программировании с идеей  о  том,

что  базовый класс представляет общее понятие, специальными видами ко-

торого являются производные классы. И это только один из аспектов  ис-

пользования  выразительной  мощности  механизма наследования, который,

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

ются виртуальными.

     При  наличии контроля за наследуемыми свойствами, средство произ-

водных классов может служить мощным инструментарием для создания новых

типов. Оно позволяет добавлять и удалять свойства  класса.  Не  всегда

удается описать отношение между базовым и производными классами в тер-

минах специализации - дакторизация является лучшим термином.

     Средство производных классов является одним из програмистских ин-

струментариев.  Для него не существует строгих рекомендаций по исполь-

зованию - этот подход еще  слишком  молод  (на  20  лет  моложе  языка

Simula),  чтобы можно было говорить о том, что какие-то его применения

являются надуманными.

     МЕХАНИЗМ  МНОГОСЕТЕВОГО НАСЛЕДОВАНИЯ. Если класс А является базо-

вым для класса В, то В наследует атрибуты А, т.е. В обладает свойства-

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

пояснение,  кажется очевидным, что могла бы оказаться полезной возмож-

ность существования двух базовых классов А1 и А2 для кдасса В. Это на-

зывается множественным наследованием.

     Примером множественного наследования  могут  служить  два  класса

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

задач. Программист может тогда создать такие классы:

     class my-displayed-task

     :public displayed, public task{

        //мое"хозяйство"

     };

     class my-task

     :public task{//мое "хозяйство"

                  //не выводимое на дисплей

     };

     class my-displayed

     :public displayed{//не задача

     };

     В случае единичного наследования программисту открыты только  две

из  этих трех возможностей. Это ведет к дублированию программного кода

или потере гибкости, а чаще всего-и того, и другого. В С++ этот пример

может быть реализован без значительных накладных расходов (по  времени

и  памяти), сравнимых с единичным наследованием и без отказа от стати-

ческой проверки типов [9].

     Неопределенности обнаруживаются во время компиляции:

     class A {public:f();...};

     class B {public:f();...};

     class C:public A, public B {...};

     void g(){

          c*p;

          p->f();///ошибка:двусмысленность

     Этой возможностью С++ отличается от объектно-ориентированного ди-

алекта языка Lisp, поддерживающего множественное наследование. В  упо-

мянутом  диалекте  Lisp неопределенности выявляются путем рассмотрения

порядка значимых деклараций, объектов с совпадающими именами в различ-

ных базовых классах или объединяя служебные утилиты с одинаковыми име-

нами в базовых классах в более сложную служебную утилиту  вышестоящего

класса.

     В С++ типичным путем разрешения неопределенности является  добав-

ление функции:

     class C:public A, public B{

     public:

        f()

        {

             //"хозяйство" класса С

            A::f();

            B::f();

         }

         ...

         }

     В дополнение к довольно простому понятию независимого множествен-

ного  наследования,  может появиться необходимость в более общем меха-

низме для выражения зависимостей между классами  (переплетающихся  при

множественном  наследовании).  В  С++ требование того, чтобы подобъект

мог быть разделяем объектом класса,  выражается  с  помощью  механизма

виртуального базового класса:

     class W {...};

     class B window //окно с границей

     :public virtual W

     {...};

     class M window //окно с меню

     :public virtual W

     {...};

     class BMW       //окно с границей и меню

     :public B window, public M window

     {...}

     Здесь  единичный  подобъект  "окно"  разделяется  подобъектами  В

window и M window класса BMW.В диалектике Lisp для облегчения програм-

мирования с использованием таких сложных иерархий классов  применяется

комбинирование служебных утилит. В С++ в этом нет необходимости.

     ИНКАПСУЛЯЦИЯ. Рассмотрим элемент класса (данные или функцию), ко-

торые  следует  защитить  от  неавторизованного доступа. Какие решения

приемлемы, чтобы ограничить доступ к этому элементу?

     очевидным ответом в языке, поддерживающем  объектно-ориентирован-

ное  программирование,  является  указание всех определенных для этого

объекта операций или всех функций класса.  При  таком  подходе  однако

возникает осложнение, связанное с тем, что невозможно указать полный и

окончательный список всех функций, имеющих право доступа к защищаемому

элементу.  Это  следует из того, что всегда можно добавить новую функ-

цию, путем получения производного класса из класса, содержащего  защи-

щаемый элемент, и определения для производного класса функции над этим

элементом. Этот подход сочетает достаточную защищенность от случайнос-

тей  (не  легко  "случайно" определить производный класс) с гибкостью,

необходимой для производства инструментария с использованием  иерархий

классов  (вы  можете сами себе гарантировать доступ к защищенному эле-

менту, порождая производный класс).

     К сожалению решение, принятое в языках, поддерживающих абстракцию

 данных, отличается от приведенного нами:"Перечислите все функции, для

 которых  необходим  доступ к элементу, в декларации класса". При этом

 ничего не говорится о том, что это за функции,-  они  не  обязательно

 должны быть функциями, описанными в классе. Неописанные в классе фун-

 кции  с  доступам к элементам класса в С++ называются дружественными.

 Упомянутый ранее класс Complex был определен с использованием дружес-

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

 как  дружественную  для  более чем одного класса. Доступность полного

 списка описанных в классе и дружественных  функций  является  большим

 преимуществом,  когда  вы пытаетесь понять поведение типа и в особен-

 ности, когда вы хотите модифицировать его.

     Рассмотрим пример, который демонстриует  некоторые  из  возможных

 путей инкансуляции в С++:

     class B{

             //члены класса по умолчанию

             //личного пользования

        int i1;

        void f1();

     protected:

        int i2;

     publik:

        int i3;

        void f3();

     friend void g (B*); //любая функция может

                         //быть определена как

                         //дружественная

     };

Личные и защищенные члены класса вообще не являются доступными:

     void h (B*p)

     {

        p->f1(); //ошибка: В::f1-личная

        р->f2(); //ошибка: B::f2-защищенная

        p->f3(); //окей  : B::f3-общего пользования

     }

     Запрещенные,  но  не  личные,  члены  доступны членам производных

классов:

     class D:public B{

     public:

        void g()

        {

           f1(); //ошибка: B::f1-личная

           f2(); //окей  : B::f2-защищенная,

                 //но D получен из В

           f3(); //окей  : B::f3-общего пользования

         }

     };

     Дружественные функции имеют доступ к личным и  защищенным  членам

класса, подобно описанным в классе функциям:

     void g(B*p)

     {

        p->f1(); //окей: B::f1-личная

                 //но g() дружественна В

        p->f2(); //окей: B::f2-защищенная,

                 //но g()дружественна В

        p->f3(); //окей :B::f3-общего пользования

     }

     Важность средств инкапсуляции резко возрастает по мере увеличения

размеров  программ, а также увеличения количества пользователей и рас-

ширения сферы их географического размещения. Детали о  механизмах  ин-

капсуляции   можно   найти   у   Шнайдера  (Snyder)[6]  и  Строустрапа

(Stroustrup)[7].

                        ОСОБЕННОСТИ РЕАЛИЗАЦИИ

    Поддержку  объектно-ориентированному программированию обеспечивают

системные средства и средства языка программирования. Одной из  причин

является  то,  что средства объектно-ориентированного программирования

надстраиваются над языком, поддерживающим механизм абстракции  данных,

и таким образом требуется небольшое количество дополнений в среде язы-

ка программирования.

     Такой  подход  предполагает,  что объектно-ориентированный язык в

действительности поддерживает механизм абстракции данных, Однако в та-

ких языках чаще всего как раз отсутствуют средства абстракции  данных.

И наоборот, в языках с возможностями абстракции данных нет средств об-

ъектно-ориентированного программирования.

     Объектно-ориентированное  программирование  стирает  грани  между

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

определения более мощных определяемых пользователем типов специального

и общего назначения, которые  охватывают  пользовательские  программы.

Поэтому важно дальнейшее развитие средств управления процессами выпол-

нения  программ,  библиотек,  отладчиков, средств измерения производи-

тельности. В идеале эти средства интегрируются в унифицированную среду

программирования, лучшим примером которой является язык Smalltalk.

                               ПРОБЛЕМЫ

     Язык,поддерживающий  технологию  локализации  данных,  абстракции

данных  и объектно-ориентированного программироввания, чтобы быть язы-

ком общего назначения должен также:

     -быть реализованным на традиционных ЭВМ;

     -выполняться в среде традиционных операционных систем;

     -быть конкурентоспособным с традиционными языками  программирова-

ния по эффективности при выполнении программ;

     -подходить для большей части возможных приложений.

     Это  означает,  что должны быть включены средства для эффективных

численных приложений (плавающая  арифметика  без  накладных  расходов,

иначе  Fortran окажется привлекательней). Должны быть включены возмож-

ности доступа к памяти (что необходимо для написания дрейверов). Долж-

ны быть возможности обращени[я к функциям  (call-обращения),  согласо-

ванные  с интерфейсами конкретных операционных систем. И, дополнитель-

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

языках и наоборот, к функциям, написанным на  объектно-ориентированных

языках из других языков.

     Это  также  означает,  что объектно-ориентированный язык не может

полностью основываться не механизмах, которые эффективно не реализуют-

ся на традиционных архитектурах, и что все еще предполагается  исполь-

зование такого языка, как языка общего назначения. То же можно сказать

и о сборке мусора, которая может оказаться узким местом в части произ-

водительности и мобильности. Большинство объектно-ориентированных язы-

ков  используют сборку мусора, чтобы упростить проблемы программиста и

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

возможность  использовать сборку мусора в некритических ситуациях, од-

нако сохранять контроль за памятью, там, где это необходимо. Альтерна-

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

проектировать типы, которые управляют используемой ими памятью. Приме-

ром может служить С++.

     Обработка  исключительных ситуаций и конкретное использование ре-

сурсов также представляют собой проблемы. Любое средство языка,  кото-

рое  реализуется  с  помощью редактора связей, скорее всего тоже будет

представлять проблему в части мобильности.

     Альтернативой включению в язык  низкоуровневых  средств  является

использование  в критических случаях специализированных языков низкого

уровня.

     Объектно-ориентированное программирование - это программирование,

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

рование с использованием определяемых пользователем типов. С небольшим

исключением объектно-ориентированное программирование может  и  должно

быть обобщением абстракции данных.

     Эта  механика  нуждается  в надлежащей языковой полддержке, чтобы

быть эффективной. Для абстракции  данных  достаточно  только  языковой

поддержки;  для  объектно-ориентированного  программирования требуются

средства общеситсемного программного окружения. Чтобы обладать свойст-

вами языка общего назначения, язык должен позволять эффективно исполь-

зовать традиционные аппаратные средства.

                             БЛАГОДАРНОСТИ

     Ранняя  версия этой статьи представлялась на совещании Ассоциации

пользователей языка Simula-association of Simula Users - в Стокгольме,

прошедшем в августе 1986 г. Прошедшая там дискуссия внесла много улуч-

шений в стиль излождения и содержания статьи. Много конструктивных за-

мечаний сделали Брайен Керниган (Brian Kernighan) и  Рави  Сети  (Ravi

Sethi).

                           Л и т е р а т у р а

    1.K.Nygaard,"Basic Cocepts in Object-Oriented Programming",

      SIGPlan  Notices,Oct.1986,pp.128-132.

    2.R.Kerr,"Object-Based Programming:A.Foundation for Reliable

      Software,"Proc.14th Simulu Users'Conf.,Simula Information,Oslo,

      Norway,1986,pp.159-165;a short version is "A Materialistic View

      of the Sofware 'Engineering' Analogy, SIGPlan Notices, March

      1987,pp123-125.

    3.T.Cargill,"IPI:A Case Study in Object-Oriented Programming",

      SIGPlan Ntices, Nov.1986,pp.350-360.

    4.B.Liskov et al.,"Abstraction Mechanisms in Clu,"Comm.ACM,

      Aug.1977,pp.564-576.

    5.J.Shopiro,"Extending the C++ Task System for Real-Time Appli-

      cations,"Proc.Usenix C++ Workshop, Usenix, Santa Monica,Calif.,

      1987,pp.77-94.

    6.A.Snyder, "Encapsulation and Inheritance in Object-Oriented

      Programming Languages,"SIGPlan Notices,Nov.1986,pp.38-45.

    7.B.Stroustrup, The C++ Programming Language, Addison-Wesley,

      Reading,Mass.,1986.

    8.D.Weinreb and D.Moon, Lisp Manual,Symbolics,Cam-

      bridge, Mass.,1981.

    9.B.Stroustrup, "Multiple Inheritance for C++, "Proc. Spring

      European Unix Users Group Conf., EEUG,London,1987.

     Bjarne  Stroustrup является разработчиком языка С++. Его исследо-

вательские интересы включают распределенные системы, операционные сис-

темы, моделирование, методологию программирования и языки программиро-

вания.

     Stroustrup получил MS в области математики и вычислительной  тех-

ники  в университете Артуса (Aarhus) и PhD по вычислительной технике в

университете Кэмбриджа (Cambridge). Он является членом  исследователь-

ского   центра   вычислительной  техники  (Computer  Science  Research

Center), также членом IEEE и ACM.

     Вопросы, касающиеся этой статьи, направляйте автору в  АТ$Т  Bell

Gaboratovies, Rm.2C-324, 600 Monntain Ave.,Murray Hill, N J 07974.


Сортированный массив
Отсортированный массив строк

Тема: Введение в ООП

Раздел:Использование библиотечных классов Си++

Задание:

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

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

     МЕТОДИЧЕСКИЕ СОВЕТЫ

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

     1.Прежде всего ознакомьтесь с возможностями библиотечного класса

String

     Посмотрите на его объявление(все они доступны вам в каталогах CLASSLIB\include и CLASSLIB\SOURCE):

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

     2.В списке библиотечных классов находим также класс

      SortedArray(cортируемый массив)

     имеющий методы добавления и удаления элементов из массива и перегруженнный оператор [] для индексации

     3.При работе с файлами и стандартным вводом-выводом используйте библиотечные потоковые классы fstream,istream,ostream или другие

     4.Как обычно начнем нашу программу с генеральншго плана в функции    main:

     -проверим,задал ли пользователь имена файлов источника и приемника

     -откроем файл-источник с проверкой успешности этой операции

     -создадим пустой объект класса "сортируемый массив"

     -заполним массив чтением строк-фамилий из файла-источника

     -выведем полученный отсортированный массив на экран для просмотра

     Далее в цикле:

     -выводим пользователю рассказ о том какие клавиши надо жмать и для чего

      -читаем нажатые клавиши

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

      -по "выходной" клавише покидаем цикл,выводим на экран результат работы и уходим в ДОС.

      Вот по-крупному и все

      Счастливого плаванья!  У вас на это 2 академчаса-поэтому готовьте сани дома, а катайтесь в классе-это последний методический совет.

     */

     #include<fstream.h>

     #include<strng.h>

     #include<stdlib.h>

     #include<sortarry.h>

     #include<string.h>

     #include<bios.h>

     #include "keycodes.def"

     void main(int c,char* v[])

     {

     if(c<3) {cout<<"Не задано имя файла  в командной строке";return;}

  //пытаемся открыть заданный файл

     fstream f(v[1],ios::in | ios::out);

     if(!f) {cout<<"Не открыт файл  " << v[1];return;}

     //создаем пустой сортируемый массив,расширяемый по 1-му эл-ту

     SortedArray sa(-1,0,1);

     //заполняем массив читаемыми из файла фамилиями с попутной сортировкой

     for(;!f.eof();)

     {

     String s("                "); //буферная строка для приема фамилии из файла

     f>>s;

     sa.add(*(new String(s)));

     }

     //Выводим массив на экран для просмотра

     cout<<'\n'<<sa;

     int key;

     char*sdel="\0\0\0";

     do{

       //Выводим пользователю инструкцию по пополнению списка и удалению из него

     cout<<'\n'<<"Режим вставки-Ins,конец вставок-пустая строка";

     cout<<'\n'<<"Режим удаления-Del,конец удалений-пустая строка";

     cout<<'\n'<<"Запись в файл- F2";

     cout<<'\n'<<"Выход из программы-AltQ";

     key=bioskey(0);

     switch(key)

     {

      case Ins:

      for(;;){

      String sbuf("                   ");

      cout<<'\n'<<"Фамилия:  ";

      cin.seekg(0,ios::end);

      cin.get(sbuf,15,'\n');

      if(!strlen((char*)sbuf)) break;

      sa.add(*(new String(sbuf)));

      }

      break;

      case Del:

      for(;;){

      cout<<'\n'<<"Номер удаляемой фамилии:  ";

      cin.seekg(0,ios::end);

      cin.get(sdel,2,'\n');

      if(!strlen(sdel)) break;

      sa.detach(sa[atoi(sdel)],1);

      }

      break;

      case F2:

      fstream fo(v[2],ios::out | ios::trunc);

      if(!fo) {cout<<"Не открыт файл  " << v[1];return;}

      fo<<sa;

      break;

      default:break;

     }

     }while(key!=AltQ);

     cout<<'\n'<<sa;

     }


Ассоциативный словарь

Тема: Введение в ООП

Раздел:Использование библиотечных классов Си++

Задание:

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

     Создание и корректировка словаря состоит в дополнении словаря словами и их толкованиями или удалении отдельных слов и их толкований из словаря с сохранением скорректированного варианта в файлах.

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

     МЕТОДИЧЕСКИЕ СОВЕТЫ

     1. Для начала вспомним,что с точки зрения математика словарь-  это ассоциация-пара связанных понятий,одно из которых есть ключ,а другое-связанное с ним значение.В качестве ключа и значения  могут выступать при использовании библиотечных  контейнерных классов типы данных,производные от Object.

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

       Таким образом в простейшем(скорее всего не самом лучшем) варианте(предложите более рациональный) наш словарь будет состоять из двух файлов:

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

       Для упрощения можете присвоить файлам фиксированные имена, например word.ass и word.txt

       Для каждого элемента словаря нам придется вначале формировать 2 объекта, объединять их в объект типа Assotiation и добавлять    в объект типа Dictionary.

     2.В списке библиотечныхх классов вы найдете описание класса

Assotiation

       производного от абстрактного класса Object и предназначенного

       для включения в него ссылок на 2 объекта-ключ и значение.

       Объекты этого класса могут размещаться в словарях.

       Класс имеет 2 стандартных конструктора,один из которых инициализирует закрытые члены Key (ключ)  Value(значение) по заданным   ссылкам на эти объекты,а другой-конструктор копирования,делающийто же самое по переданной ему ссылке на уже существующую ассоциацию.

       Кроме того класс имеет 2 метода для доступа к ключам и значениям

key()   value()

     3.В списке библиотечных классов находим также класс

 Dictionary  (словарь)

       производный от класса Set (множество) и  предназначенный для "контейнеризации" объектов типа Assotiation.

       Класс имеет метод  add(Object&) для добавления ассоциаций в словарь,

       методы для определения количества элементов в словаре,поиска ,  удаления, определения не пуст ли словарь и пр

     4.При работе с файлами и стандартным вводом-выводом используйте библиотечные потоковые классы fstream,istream,ostream или другие

     4.Как обычно начнем нашу программу с генерального плана в функции  main:

     -проверим,задал ли пользователь имена файлов

     -откроем файлы

     -создадим пустой объект класса "словарь"

     -заполним этот объект ассоциативными  из файла ассоциаций

      Для этого в цикле до конца файла  будем создавать буферные String-и  для ключа и значения,читать в эти объекты строки из файла ассоциаций,

      создавать буферную ассоциацию и добавлять ее в словарь.

      После окончания этой работы словарь готов к эксплуатации.

     Далее в цикле:

     -выводим пользователю рассказ о том какие клавиши надо жмать и для  чего

      -читаем нажатые клавиши

      -в зависимости от того какая нажата выполняем расширение словаря,  сокращение его или эксплуатацию

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

      При вводе запишите ключевое слово в буферный ключ ,а толкование  в специальный буферный String

      При вводе пустой строки слова или толкования делайте break.

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

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

   -При эксплуатации словаря определите не пустой ли он(cтандартная для контейнеров функция-член isEmpty),если пустой-сообщите пользователю и выйдите из режима эксплуатации.

   При непустом словаре предложите пользователю ввести ключевое слово, инициализируйте им временный String, найдите в классе словаря метод  поиска, возвращающий значение по заданному ключу,получите значение (позицию в файле толкований), установите файловый указатель в эту  позицию (не забудьте преобразовать строковую форму позиции к числовой) и до встречи с обусловленным символом конца толкования

   выведите на экран толкование заданного слова.

   -Логическую схему удалений из словаря разработайте сами и сами же релизуйте

     */

     #include<fstream.h>

     #include<strng.h>

     #include<stdlib.h>

     #include<dict.h>

     #include<string.h>

     #include<bios.h>

     #include "keycodes.def"

     #include<assoc.h>

     char bk[50],bv[1000];

     Dictionary d;                          //Создаем пустой словарь

     void main(int c,char* v[])

     {

     if(c<3) {cout<<"Не заданы имена файлов  в командной строке";return;}

  //пытаемся открыть файлы ассоциаций

     fstream fass(v[1],ios::in );

     if(!fass) {cout<<"Ошибка открытия  файла"<<v[1];return;}

     //читаем файл ассоциаций

     for(;!fass.eof();)

     {

     fass.getline(bk,50,'~');

     fass.getline(bv,1000,'$');

     if(strlen(bk) &&  strlen(bv))

     d.add(*(new Association(*new String(bk),*new String(bv))));

     }

     fass.close();

     int key;

     do{

       //Выводим пользователю инструкцию по пополнению списка и удалению из него

     cout<<'\n'<<"Режим дополнения-Ins";

     cout<<'\n'<<"Режим удаления-Del";

     cout<<'\n'<<"Режим эксплуатации- F1";

     cout<<'\n'<<"Выход из программы-AltQ"<<'\n';

     key=bioskey(0);

     switch(key)

     {

     //дополнение

      case Ins:

      for(;;){

      cout<<'\n'<<"Ключевое слово(закончите ~):  ";

      cin.seekg(0,ios::end);

      cin.getline(bk,50,'~');

      if(!strlen(bk)) break;

      cout<<'\n'<<"Толкование слова(закончите $):  ";

      cin.seekg(0,ios::end);

      cin.getline(bv,999,'$');

      if(!strlen(bv)) break;

      d.add(*(new Association(*new String(bk),*new String(bv))));

      cout<<d.getItemsInContainer();

}

      break;

      //удаление

      case Del:

      for(;;){

      cout<<'\n'<<"Ключ удаляемой ассоциации+~:  ";

      cin.seekg(0,ios::end);

      cin.get(bk,50,'~');

      if(!strlen(bk)) break;

      String bufk(bk);

      //получаем ассоц для удаления

      Association& abuf=d.lookup(bufk);

      if(abuf!=NOOBJECT)

      d.detach(d.lookup(bufk),0);

      else cout<<"Не могу удалить того,чего нет!";

      }

      break;

      //запоминание

      case F2:

      fstream fres(v[2],ios::out | ios::trunc);

      if(!fres) {cout<<"Не открыт файл  " << v[2];return;}

      ContainerIterator& ci=d.initIterator();

      for(;int(ci);)

      {

      Association&  a=(Association&)ci++;

      cout<<a.key()<<'~'<<a.value()<<'$';

      fres<<a.key()<<'~'<<a.value()<<'$';

      }

      break;

      //эксплуатация

      case F1:

      //ContainerIterator& ce=d.initIterator();

      for(;;)

      {

      cout<<"\n"<<"Введите слово  :";

      cin.seekg(0,ios::end);

      cin.getline(bk,50,'~');

      if(!strlen(bk)) break;

      String bkey(bk);

      Association& abuf=d.lookup(bkey);

      if(abuf!=NOOBJECT)

      cout<<abuf.value();

      else cout<<"Запрашиваемый ключ не найден!";

      }

      break;

      default:break;

     }

     }while(key!=AltQ);

     }


Связные списки
Связные списки в объектном исполнении

Тема: Введение в ООП

Раздел:Использование библиотечных классов Си++

Задание:

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

      Авторы

     Название книги

    -Год издания(целое)

    -Цена(вещественное число)

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

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

     МЕТОДИЧЕСКИЕ СОВЕТЫ

     1.В библиотечных сервисных классах нет подходящих потомков        класса Object,поэтому для использования библиотечного класса        Doublelist вам придется создать свой класс,например по имени        BookList,производный от Object,определив в нем все чисто        виртуальные функции(сами определите какие просмотром объявления        класса Object в интерфейсном файле object.h) и добавив свои        необходимые,например операторную функцию сохранения записи в        файле

 2.Продумайте способ хранения записей в файле в т ч как отличать        отдельные поля и запись от записи при чтении и обеспечивать эти        возможности при сохранении      3.Предварительно изучите возможности библиотечного класса,прдназначенного для поддержки двухсвязных списков

     4.Некоторые подсказки вы найдете в 1-й книге Касаткина в разделе   14.3 на стр214-226,  но без файловой реализации

ВОЗМОЖНЫЙ ВАРИАНТ РЕАЛИЗАЦИИ:

#include<string.h>

#include<dbllist.h>

#include<iostream.h>

#include<fstream.h>

#include<bios.h>

#include<stdlib.h>

#include<object.h>

#include<conio.h>

#include"c:\bc\work\keycodes.def"

int delet(void);

void listb(void);

//void list2(void);

//void file(void);

char Wr_N[80];

char B_N[80];

int Y,key,key1;

float price;

int poick(void);

class book:public Object

 {  //public:

    char  Wr_Names[80];

    char  Book_Name[80];

    int Year;

    float book_price;

    public:

    book(){};

//    book(book& o);

   book(char *,char *,int ,float );

    ~book(){};

    virtual classType isA() const{return 100;};

    virtual char _FAR *nameOf() const {return "Описание книги";};

    virtual hashValueType hashValue() const {return 0;};

    virtual int isEqual( const Object _FAR & ) const {return 1;};

    virtual void printOn( ostream _FAR & ) const;

    int input_book(void);

    void value (char* s) ;

    void name (char* s) ;

    void g(char*s){

    itoa(Year,s,10);};

    void p(char* s){gcvt (book_price,10,s);};

    friend  int testfunc(const class Object&,void*);

};

void book::value(char *s)

{

 strcpy(s,Wr_Names);

}

void book::name(char *s)

{

 strcpy(s,Book_Name);

}

  book::book(char Wr_N[],char B_N[],int Y,float price):Year(Y),

  book_price(price)

   {

   /* Year=Y;

    book_price=price;*/

    strcpy(Wr_Names,Wr_N);

    strcpy(Book_Name,B_N);

   }

    int book::input_book(void)

    {

      char name_y[40];

      cout<<"Введите название книги:";

      cin.seekg(0,ios::end);

      cin.getline(Book_Name,80);

      if(!strlen(Book_Name)){cout<<"Я понял Вы не знаете названия книги\n!";

  return 0;}

      cout<<"Авторы :\n";

      cin.seekg(0,ios::end);

      cin.getline(Wr_Names,80);

      if(!strlen(Wr_Names)){cout<<"Я понял Вы не знаете авторов\n!";}

      cout<<"Введите год издания книги:";

      cin.seekg(0,ios::end);

      cin.getline(name_y,4);

      if(!strlen(name_y)){cout<<"Я понял Вы не знаете год издания\n!";Year=0;}

      else Year=atoi(name_y);

      cout<<"Введите стоимость книги:";

      cin.seekg(0,ios::end);

      cin.getline(name_y,10);

      if(!strlen(name_y)){cout<<"Я понял Вы не знаете стоимость книги\n!";book_price=0;}

      else

      book_price=atof(name_y);

      return 1;

      }

 void book::printOn( ostream _FAR & ) const

{

int x=wherex();

int y=wherey();

cout<<"  Автор \n Название \nгод издания\n цена \n";

gotoxy(x+14,y);

 cout<<Wr_Names;

gotoxy(x+14,y+1);

cout<<Book_Name;

gotoxy(x+14,y+2);

cout<<Year;

gotoxy(x+14,y+3);

cout<<book_price<<endl;

}

DoubleList books_list;

  main(int c,char*v[])

    {

    char name_y[40];

      int r;

     clrscr();

    if(c<3){cout<<"Вы задали не все параметры";return(0);}

  //  file(c,*v[]);

    fstream f(v[1],ios::in);

   if(!f){cout<<"Ошибка открытия файла"<<v[1];exit(0);}

   for(;!f.eof();)

    {

      f.getline(B_N,80,'*');

      if(!strlen(B_N)){//cout<<"Я понял Вы не знаете названия книги\n!";

  r=0;}

      if(r!=0)

      {

      f.getline(Wr_N,80,'^');

      if(!strlen(Wr_N)){}

      f.getline(name_y,20,'@');

      if(!strlen(name_y)){Y=0;}

      else Y=atoi(name_y);

      f.getline(name_y,10,'#');

      if(!strlen(name_y)){price=0;}

      else

      price=atof(name_y);

    //book b(Wr_N,B_N,Y,price);

    //DoubleListElement b(new book(Wr_N,B_N,Y,price));

    //books_list.add(b);

    //books_list.add(*(new book(b)));

    books_list.add(*(new book(Wr_N,B_N,Y,price)));

       }

       r=1;

}

//    listb();

cout<<books_list;

/////////////////////////

clrscr();

cout<<"Esc-выход F1-поиск Ins-режим вставки Del-режим удаления F3-просмотр списка F2-запись в файл\n";

key=bioskey(0);

for(;key!=Esc;)

{

switch(key)

{

case F1:

       poick();

       break;

case Ins:

 clrscr();

   cout<<"Выход по вводу пустой строки в названии "<<endl;

       for(;;)

       {

       clrscr();

   cout<<"Выход по вводу пустой строки в названии "<<endl;

    book b(Wr_N,B_N,Y,price);

    if(!b.input_book()){break;}

    books_list.add(*(new book(b)));

     // getch();

    //   key1=bioskey(0);

       }

       break;

case Del:

       delet();

       break;

case F3:

       listb();

       getch();

      break;

 case F2:

 char str1[80];

 char str[80];

 char str2[80];

 char str3[80];

  //	 int s2;

   //	 float s3;

 fstream f1(v[2],ios::out|ios::trunc);

   if(!f1){cout<<"Ошибка открытия файла"<<v[2];exit(0);}

 ContainerIterator& g=books_list. initIterator();

 for(;int(g);)

 {

book& w =(book&)g++;

w.value(str);

w.name(str1);

w.g(str2);

w.p(str3);

f1<<str1<<"*";

f1<<str;

f1<<"^";

       //	itoa (s2,str2,10);

f1<<str2<<"@";

//	gcvt (s3,10,str3);

f1<<str3<<"#";

 }

      break;

}

clrscr();

cout<<"Esc-выход F1-поиск Ins-режим вставки Del-режим удаления F3-просмотр списка F2-запись в файл\n";

key=bioskey(0);

}

//    listb();

//    poick();

//    getch();

      return 0;

    }

void listb(void)

{

char str[80];

int key2;

 clrscr();

cout<<"просмотр списка F1-аторов  F2-названий  F3-выход "<<endl;

     key2=bioskey(0);

     for(;key2!=F3;)

     {

     switch(key2)

     {

     case F1:

 ContainerIterator& g=books_list. initIterator();

 for(;int(g);)

 {

book& w =(book&)g++;

w.value(str);

cout<<str<<endl;

 } getch();

break;

    case F2:

ContainerIterator& d=books_list. initIterator();

 for(;int(d);)

 {

book& v =(book&)d++;

v.name(str);

cout<<str<<endl;

 }getch();

break;

      }

  clrscr();

  cout<<"просмотр списка F1-авторов  F2-названий  F3-выход  "<<endl;

      key2=bioskey(0);

      }

//    clrscr();

}

inline int testfunc(const class Object& b,void *s)

{

// book& c=(book&)b;

 if(strcmp(((book&)b).Wr_Names,(char*)s)==0) return 1;else return 0;

}

/////////////////////////

int delet(void)

{

char str[80];

char str1[80];

int key2,d=0;

 clrscr();

cout<<"Удаление книги по F1-автору  F2-названию  F3-выход "<<endl;

    key2=bioskey(0);

     for(;key2!=F3;)

     {

     switch(key2)

     {

     case F1:

 for(;;)

 {

 clrscr();

 cout<<"Выход по вводу пустой строки \nАвтор"<<endl;

 cin.getline(str1,40);

 if(!strlen(str1)){d=1;break;}

 book& bel=(book&)books_list.lastThat(testfunc,str1);

if(bel!=NOOBJECT) {

bel=(book&)books_list.peekAtHead();

books_list.detachFromHead(bel);

}

 }

 break;

    case F2:

 for(;;)

 {

 clrscr();

 cout<<"Выход по вводу пустой строки \nНазвание"<<endl;

 cin.getline(str1,40);

 if(!strlen(str1)){d=1;break;}

      ContainerIterator& z=books_list. initIterator();

 for(;int(z);)

 {

book& s=(book&)z++;

s.name(str);

if(strcmp(str,str1)){d=0;}

else

{

//s=(book&)books_list.peekAtTail();

books_list.detachFromTail(s);

d=1;break;}

}

if(d==0){cout<<"Этой книги в нашем списке нет\n";getch();}

 }

 break;

      }

    clrscr();

cout<<"Удаление книги по F1-автору  F2-названию  F3-выход "<<endl;

   key2=bioskey(0);

      }

 return(d);

}

//////////////////////////

////////////

int poick(void)

{

char str[80];

char str1[80];

int key2,d=0;

 clrscr();

cout<<"поиск книги по F1-автору  F2-названию  F3-выход  "<<endl;

    key2=bioskey(0);

     for(;key2!=F3;)

     {

     switch(key2)

     {

     case F1:

 for(;;)

 {

 clrscr();

 cout<<"Выход по вводу пустой строки \nАвтор"<<endl;

 cin.getline(str1,40);

 if(!strlen(str1)){d=1;break;}

      ContainerIterator& y=books_list. initIterator();

      int c1=0;

 for(;int(y);)

 {

book& k =(book&)y++;

k.value(str);

    if(strcmp(str,str1)){d=0;}

else

{  clrscr();

cout<<"---"<<(c1+1)<<"---\n";

k.printOn(cout);

d=1;c1++;cout<<"Нажмите любую клавишу\n";getch();}

}

if(c1!=0)d=1;

if(d==0){cout<<"Этой книги в нашем списке нет\n";getch();}

 }

 break;

    case F2:

for(;;)

 {

 clrscr();

 cout<<"Выход по вводу пустой строки \nНазвание"<<endl;

 cin.getline(str1,40);

 if(!strlen(str1)){d=1;break;}

      ContainerIterator& z=books_list. initIterator();

 for(;int(z);)

 {

book& s =(book&)z++;

s.name(str);

      if(strcmp(str,str1)){d=0;}

else

{

s.printOn(cout);

d=1;getch();break;}

}

if(d==0){cout<<"Этой книги в нашем списке нет\n";getch();}

 }

 break;

      }

//      if(d=0){cout<<"Этой книги в нашем списке нет\n";}

   clrscr();

    cout<<"поиск книги по F1-автору  F2-названию  F3-выход  "<<endl;

   key2=bioskey(0);

      }

 return(d);

}


Матрицы - созданные пользователем классы
Матричный класс

Тема: Введение в ООП

Раздел:Создание собственных классов и перегрузка операций

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

1. Умножение с помощью операции *. Для тестирования используйте следующие примеры:

     |1  1  0|  |0  1  4|   |1  4  6|

     |-3 -2 1|  |1  3  2| = |3 -9 15|

     |2  0  1|  |5  0  1|   |5  2  9|

     |1  0  4|  |1 |   |13|

     |-1 3  0|  |-2| = |-7|

     |1  0 -1|  |3 |   |-2|

 |1 |

     |1  -1  4|  |3 | =-6

 |-1|

|4  6  7|

    |1  -3  2|  |-1 0  1| = |7 -2  6|

|0  -4 1|

     2.трактовку заданной матрицы как матрицы коэффициентов системы      линейных уравнений и решение этой системы методом ортогонализации      с помощью предоставляемой пользователю перегруженной для матричного      типа  операции ,возвращающей вектор решения;      для тестирования правильности выполнения перегруженной операции      используйте СЛАУ,заданную матрицей коэффициентов: 	   1   5   4   3

  -1  -2   4   0

   2   1   6  18

     -трактовку заданной матрицы как результатов экспериментально по-      лученных значений нескольких входов и одного выхода исследуемого      для построения математической модели объекта,например       Номер   | х1| x2|  y

      опыта  |	___|___|___

1     |	1  | 1 |65.5

2     |	1  |-1 |55

3     |	-1 |-1 |44.0

4     |	1  |1  |55

   Предполагая,что выход "у"  связан с входом линейной моделью вида

y=a0 + a1*x1 + a2*x2

   предоставьте пользователю перегруженную для матричного типа операцию  для определения вектора коэффициентов

a=(a0,a1,a2)

   методом наименьших квадратов

   В приведенном примере результат тестирования должен быть

  a0=55.1;a1=5.15;a2=5.15.

   Матрицы располагаются в файлах,результаты тестирования выводятся в другой файл и параллельно на экран

   2.МЕТОДИЧЕСКИЕ СОВЕТЫ ПО ВЫПОЛНЕНИЮ РАБОТЫ

   По последней матричной операции обратитесь к конспекту по основам научных исследований(метод наименьших квадратов),учебникам по    математической статистике и регрессионному анализу .    По существу Вам необходимо преобразовать матрицу экспериментов Х    в матрицу Хт*Х,добавить столбец Хт*Y-а уже эту матрицу трактовать    как систему линейных уравнений и решить ее с помощью предыдущей    операции.Здесь т-значек транспонирования,но предварительная перегрузка    операции для транспонирования матриц вам не задана сознательно,    т к можно без нее обойтись-подумайте как.Если не придумаете-сделайте    операцию  транспонирования,лишняя тренировка в перегрузках не повредит.    Остальные разделы вами уже пройдены и не должны вызывать затруднеий


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

Базовым языком для программирования в среде Windows фирма

Microsoft определила язык Си. Прототипы функций для всех

системных вызовов содержатся в заголовочном файле windows.h и

эти функции имеют модификатор far pascal - дальнего вызова с

паскалевским способом помещения параметров в стек слева направо

и возложнием ответственности за очистку стека на вызываемую

функцию.

Исполняемые программы для Windows тоже имеют расширение .ехе, но

формат этого файла отличается от DOS-овского - его таблицы

определяют системный вызов Windows как имя динамической

библиотеки и имя или порядковый номер функции в этой библиотеке.

ВЕНГЕРСКАЯ ЗАПИСЬ ИМЕН - принята при программировании в Windows

и состоит в том , что идентификаторы имеют префикс, описывающий

тип переменной, и содержательную часть имени. Стандартные

префиксы в этом соглашении:

c      char

i      int

b      BOOL

w      WORD

dw     DWORD

l      LONG

fn     функция

s      строка

sz     строка, закрытая нулем

p      указатель

Например lpsz - длинный указатель на закрытую нулем строку

В этой записи принято также структурным переменным присваивать

имена самой структуры, но в нижнем регистре:

MSG msg;

RECT rect;

ПРОСТЕЙШАЯ ПРОГРАММА В WINDOWS, печатающая "Привет Windows!"

#define STRICT          //для строгой проверки типов

#include <windows.h>

#pragma argsused       //игнорировать неиспользуемые параметры

// Главная функция в Си для Windows называется

// WinMain

// возвращает int, стнроится по Паскалевскому вызову

//и имеет ровно 4 параметра

int PASCAL

WinMain(HINSTANCE hInstance,      //идентификатор приложения

        HINSTANCE hPrevInstance,  //идентификатор предыдущей

                                  // копии приложения

        LPSTR     lpszCmdLine,    //дальний указатель на

                                  //строку параметров

        int       nCmdShow)       //как рисовать главное окно

//например SW_SHOWNORMAL (значение 1) -нормальные размеры или

//SW_SHOWNOACTIVATE (значение 7) - минимальные

 {

/* Вывод небольшого сообщения проще всего осуществить с помощью

библиотечной ф-ции для вывода окна сообщения

nItem = MessageBox(hwndParent,lpszText,lpszCaption,nType);

Строка-сообщение заданная 2-м параметром выводится в диалоговую

панель на родительском окне с  идентификатором, заданным

первым параметром с заголовком,  заданным 3-м параметром, с

набором кнопок и иконкой, закодированными комбинацией групп

флагов в 4-м параметре. Употребляются кнопки MB_OK, MB_OKCANCEL,

MB_YESNO, MB_YESNOCANCEL, MB_RETRYCANCEL, MB_ABORTRETRYIGNORE.

Вторая группа флагов указывает кнопку предъявляемую  по

умолчанию:  MB_DEFBUTTON1, MB_DEFBUTTON2,MB_DEFBUTTON3.

Третья группа флагов определяет стандартную иконку:

MB_ICONINFORMATION, MB_ICONEXCLAMATION, MB_ICONSTOP,

MB_ICONCUESTION.

Четвертая группа флагов задает модальность диалога:

MB_APPLMODAL, MB_SYSTEMMODAL, MB_NOFOCUS.

Ф-ция возвращает тип нажатой кнопки: ID_OK, ID_CANCEL, ID_YES,

ID_NO, ID_RETRY, ID_ABORT.

*/

 MessageBox(NULL, "Привет Windows!","Первая программа",

 MB_OK); return 0; }

Пусть эта программа набрана в файле hello.cpp.  Для создания

приложения необходимо создать файл проекта hello.prj, включив в

него файл с текстом программы hello.cpp.  Необходим еще в

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

можно оставить компоновщику возможность использовать этот файл

по умолчанию.  Выполните эту программу и полюбуйтесь на

результат.

ОБРАБОТКА СООБЩЕНИЙ.

Windows поддерживает объектно - ориентированный стиль

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

некоторого класса окна, реагирующие на ввод пользователя ,

воспринимаемый в виде сообщений.

Поэтому перед созданием окна необходимо зарегистрировать нужный

класс с помощью ф-ции RegisterClass, параметром которой является

указатель на структуру типа WNDCLASS. Шаблон этой структуры:

typedef struct tagWNDCLASS

{

     UINT        style;   стиль окна например   CS_HREDRAW |

                          CS_VREDRAW для перерисовки при

                          изменении размеров

    WNDPROC     lpfnWndProc;       указатель на оконную ф-цию

    int         cbClsExtra;        для использования в

    int         cbWndExtra;       приложении по своему

                                  усмотрению

    HINSTANCE   hInstance;        номер приложения из WinMain

    HICON       hIcon;            иконка для окон класса -

                                 указатель создается ф-ей

                                 LoadIcon

    HCURSOR     hCursor;        курсор для мыши - указатель

                                создается ф-ей LoadCursor

    HBRUSH      hbrBackground;    цвет фона для окна

    LPCSTR      lpszMenuName;     указатель на имя меню

    LPCSTR      lpszClassName;    имя класса

   } WNDCLASS;

Поля структуры должны быть заполнены перед регистрацией

После регистрации класса можно создать окно для прложения с

помощью ф-ции

 HWND    WINAPI CreateWindow(LPCSTR, LPCSTR, DWORD, int, int,

 int, int, HWND, HMENU, HINSTANCE, void FAR*);

 Ее параметры:

-имя класса

-заголовок окна

-тип окна (например WS_OVERLAPPED - перекрывающееся)

-4 int-параметра координат и размеров

-дескриптор родительского окна

-указатель на меню

-идентификатор приложения (параметр WinMain)

-параметры создания по усмотрению приложения

Отображение окна на экране выполняется ф-цией:

BOOL    WINAPI ShowWindow(HWND, int);

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

Второй параметр -тип отображения; если это SW_SHOWNORMAL, то

ф-ция очищает цветом фона оконную область

Для вывода содержимого окна используется ф-ция

void    WINAPI UpdateWindow(HWND); с дескрипторм окна в качестве

параметра. Она посылает оконной процедуре сообщение WM_PAINT

минуя  очередь сообщений. После этого окно выведено на экран и

система ожидает активности от мыши или клавиатуры.

 Windows - программа (приложение), которое мы пишем, содержит

просто подпрограммы реакции на различные сообщения. Когда мы

говорим, что Windows передает сообщение в прикладную программу,

то это означает, что она вызывает связанную с окном специальную

функцию - функцию окна; передаваемые при вызове параметры

определяют текущее сообщение.

Само по себе сообщение - это структура:

typedef struct tagMSG

{HWND hwnd;         идентификатор окна-получателя сообщения

 UINT message;      код сообщения

 WPARAM wParam;      содержимое сообщения

 LPARAM lParam;      содержимое сообщения

 DWORD time;       время отправления сообщения

 POINT pt;         информация о координатах курсора мыши в

                   момент приема сообщения

}MSG;

 Все сообщения описаны в файле windows.h с префиксом WM_.

 Поля wParam и lParam определяют специфическую для каждого типа

 сообщений информацию; например для сообщения WM_SIZE

 (изменение размеров окна) wParam содержит одно из значений

 SIZENORMAL, SIZEICONIC, SIZEFULLSCREEN, SIZEZOOMSHOW,

 SIZEZOOMHIDE а lParam - новые размеры окна.

 Программа пользователя может послать внеочередное сообщение

 использванием ф-ции SendMessage или разместить свое сообщение в

 очереди сообщений ф-цией PostMessage.

Любая прикладная программа должна содержать специальную оконную

процедуру и цикл обработки посылаемых из Windows сообщений - в

этом цикле извлекается из очереди очередное сообщение и

выбирается соответствующая оконная процедура (некоторые

сообщения могут пересылаться в оконную процедуру минуя очередь).

Основная работа , которую должно выполнять Windows -

приложение,- это обслуживание собственной очереди сообщений

циклическим вызовом 2-х функций:

- получения соообщений

BOOL    WINAPI GetMessage(MSG FAR*, HWND, UINT, UINT);

первым параметром которой есть укзатель на структуру типа MSG;

остальные параметры опрделяют окна, от которых ожидаются

сообщения - нулевые значения означают, что сообщения принимаются

от всех окон приложения.

-обработки сообщений

BOOL    WINAPI TranslateMessage(const MSG FAR*);

с распознаванием комбинаций клавиш при вводе с клавиатуры

включается в цикл при необходимости

- диспетчирования сообщений

LONG    WINAPI DispatchMessage(const MSG FAR*);

которая передает сообщение оконной процедуре и обеспечивает

возврат в WinMain.

MSG msg;

while(GetMessage(&msg,0,0,0))

{DispatchMessage(&msg)}

В момент создания окна оконная ф-ция получит сообщение WM_CREATE

и выполнит инициализацию оконной структуры по аналогии с

конструктором класса в С++; при уничтожении окна его ф-ция

получит сообщение WM_DESTROY - обработчик этого сообщения

действует как деструктор. Оконная ф-ция не вызывается в самом

приложении - она вызывается Windows.

 Каждое создаваемое в программе окно имеет связанную с ним

оконную процедуру, которая может размещаться в программе или в

динамически связанной библиотеке DLL - она получает управление

при передаче сообщения, обрабатывает его и возвращает управление

Windows. Так как окно всегда создается как объект некоторого

класса окон, то для всех окон этого класса используется одна

оконная процедура. Оконная процедура - это метод класса, а

параметры характеристик окна - это данные класса.

Оконная процедура имеет 4 параметра:

-дескриптор окна, в котором принято сообщение

-идентификатор сообщения

-2 параметра сообщения

Оконная процедура распознает соообщение и обрабатывает его, а

для всех необработанных сообщений должна вызываться ф-ция

DefWindowProc с теми же параметрами для обработки по умолчанию.

Составим приложение с одним оконным классом и одним главным

окном этого класса; для обработки сообщений для этого окна у нас

будет определена одна оконная ф-ция, обрабатывающая 3 сообщения

с кодами WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_DESTROY:

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "WindowAppClass";

// Заголовок окна

char const szWindowTitle[] = "Window Application";

// Идентификатор копии приложения

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!hPrevInstance)

{

  if(!InitApp(hInstance))

      return FALSE;

}

else

{

   MessageBox(NULL,"Можно запускать только

   одну копию приложения","Ошибка",MB_OK |

   MB_ICONSTOP);

   return 0;

}

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    // Создаем символьные сообщения

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  wc.style = 0;              //стиль окна

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

 //Выполняем обработку сообщений, ид-р которых передается

 // через параметр msg

  switch (msg)

  {

    case WM_LBUTTONDOWN:

   {

   MessageBox(NULL,"Левая кнопка","Сообщение",MB_OK |

   MB_ICONINFORMATION); return 0;

   }

    case WM_RBUTTONDOWN:

   {

   MessageBeep(-1);

   MessageBox(NULL,"Правая кнопка","Сообщение",MB_OK |

   MB_ICONINFORMATION); return 0;

   }

    case WM_DESTROY:  {

      PostQuitMessage(0); return 0;

    } }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }


Вывод текста в окна Windows

Возможность параллельной работы приложений Windows с

пермещаемыми и перекрывающимися окнами обусловливает специфику

вывода в окна; Windows следит за перемещением и изменением

размеров окон и при необходимости извещает их о необходимости

перерисовки содержимого посылкой сообщения WM_PAINT. По такому

сообщению ф-ция окна должна перерисовать все окно или его часть в

зависимости от дополнительной информации в сообщении WM_PAINT -

поэтому весь вывод в окна обычно сосредоточен в одном месте

программы - в фрагменте обработки WM_PAINT внутри ф-ции окна.

Для возможности перерисовки  содерждимого окна приложение должно

хранить в памяти свое текущее состояние для ф-ции окна -

например при выводе на экран дампа оперативной памяти надо

хранить начальный адрес отображаемого участка, чтобы можно было

определить какой участок окна надо перерисовать.

Обработка WM_PAINT всегда начинается с вызова ф-ции

HDC     WINAPI BeginPaint(HWND, PAINTSTRUCT FAR*);

имеющей 2 параметра - дескриптор окна и указатель на структуру

типа PAINSTRUCT, содержащую параметры для отображения окна.

Возвращается идентификатор контекста отображения внутри

пользовательской области окна.

Заканчивается обработчик WM_PAINT вызовом ф-ции

void    WINAPI EndPaint(HWND, const PAINTSTRUCT FAR*);

которая освобождает контекст отображения и помечает

пользовательскую область окна как корректную.

Помимо пары BeginPaint и  EndPaint может использоваться пара

вызовов:

DWORD   WINAPI GetDC(HDC);

для получения контекста отображения

int     WINAPI ReleaseDC(HWND, HDC);

для освобождения.

Функция

void    WINAPI GetClientRect(HWND, RECT FAR*);

используется для заполнения в указанном окне полей структуры

типа RECT,описывающей ширину и высоту пользовательской области в

пикселах.

Собственно для вывода текста может испошльзоваться ф-ция

int     WINAPI DrawText(HDC, LPCSTR, int, RECT FAR*, UINT);

имеющая параметры:

-дескриптор устройства;

-строку;

-признак окончания строки 0-символом;

-размер области (структура типа RECT)

-тип отображения (надо ли переводить, центрировать и пр.);

или ф-ция

BOOL    WINAPI TextOut(HDC, int, int, LPCSTR, int);

выводящая строку LPCSTR  с позиции заданной 2-мя int в 2-м и 3-м

параметрах; количество выводимых символов задается последним

параметром.

Указание на необходимость обновления содержимого окна

производится при вызове ф-ции

void    WINAPI InvalidateRect(HWND, const RECT FAR*, BOOL); или

void    WINAPI InvalidateRgn(HWND, HRGN, BOOL);

Размеры обновляемой области можно узнать с помощью ф-ции

BOOL    WINAPI GetUpdateRect(HWND, RECT FAR*, BOOL);

Для сброса признака необходимости обновления используют ф-ции

ValidateRect или ValidateRgn.

 В качестве примера приведем приложение для вывода

текста под названием textout, которое отличается от предыдущего

только наличием фрагмента для обработки WM_PAINT в оконной

ф-ции, но мы существенно изменим структуру программы, разбив ее

на отдельные файлы и используем возможности С++ для создания 2-х

классов - WinApp как класс приложения как такового и Window для

создания окон.

Главный файл с ф-цией WinMain textout.cpp:

#define STRICT          //для строгой проверки типов

#include <windows.h>

#include "window.hpp"

#include "winapp.hpp"

#include "textout.hpp"

#pragma argsused       //игнорировать неиспользуемые параметры

// Имя класса окна

char const szClassName[]   = "TextoutAppClass";

// Заголовок окна

char const szWindowTitle[] = "Textout Application";

// Главная функция в Си для Windows называется

// WinMain

// возвращает int, стнроится по Паскалевскому вызову

//и имеет ровно 4 параметра

int PASCAL

WinMain(HINSTANCE hInstance,      //идентификатор приложения

        HINSTANCE hPrevInstance,  //идентификатор предыдущей

                                  // копии приложения

        LPSTR     lpszCmdLine,    //дальний указатель на

                                  //строку параметров

        int       nCmdShow)       //как рисовать главное окно

{

 //Указатель на объект класса Window - главное окно приложения

 Window PMainWindow;

//Создаем объект класса WinApp - наше приложение

 WinApp App(hInstance,hPrevInstance,lpszCmdLine,nCmdShow);

//Регистрируем класс для главного окна приложения

App.RegisterWndClass(szClassName,(WNDPROC)WndProc);

//Проверяем на ошибки регистрации

if(App.Error()) return App.Error();

//Создаем главное окно - объект класса Window

PMainWindow=new Window(hInstance,szClassName,szWindowTitle);

//Проверяем на ошибки при создании окна

if(PMainWindow->Error()) PMainWindow->Error();

//Отображаем окно

PMainWindow->Show(nCmdShow);

//Посылаем в окно сообщение WM_PAINT:

PMainWindow->Update();

//Запускаем цикл обработки сообщений:

App.Go();

//Завершаем работу приложения:

return App.Error();

}

Файл textout.hpp содержит прототип ф-ции окна:

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

Класс WinApp определен в файле winapp.hpp:

class WinApp

{

 MSG msg;

 int errno;

 HINSTANCE hInstance;

public:

 WinApp(HINSTANCE,HINSTANCE,LPSTR,int); //Конструктор

 BOOL RegisterWndClass(LPSTR,WNDPROC);  //Регистрация класса

 WORD Go(void);                         //Запуск цикла обр сооб

 int Error(void) {return errno;}

}

Исходные тексты методов - в файле winapp.cpp:

#define STRICT          //для строгой проверки типов

#include <windows.h>

#include <mem.h>

#include "winapp.hpp"

#include "textout.hpp"

//Конструктор класса Window

#pragma argsused

 WinApp(HINSTANCE hInst,HINSTANCE hPrevInstance lpsz

 CmdLine,LPSTR lpsz CmdLine,int nCmdShow)

{

 errno=0;

 hInstance=hInst;

}

// Регистрация класса окна

BOOL WinApp::RegisterWndClass(LPSTR szClassName,WNDPROC WndProc)

{

ATOM aWndClass;  //Атом для кода возврата

WNDCLASS wc;     //структура для регистрации оконного класса

memset(&wc,0,sizeof(wc));

wc.style   = 0;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wc.lpszMenuName = (LPSTR)NULL;

wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

aWndClass = RegisterClass(&wc);

return (aWndClass != 0);

}

//Запуск цикла обработки сообщений

WORD WinApp::Go(void)

{

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

//Файл window.hpp содержит определение класса Window для

//создания окон

class vWindow

{

HWND hwnd;    //Идентификатор окна

int errno;

public:

Window(HINSTANCE,LPSTR,LPSTR);  //Конструктор

int Error(void) {return errno;}

void Show(int nCmdShow) {ShowWindow(hwnd,nCmdShow);}

void Update(void) {UpdateWindow(hwnd);}

};

//Исходный текст конструктора поместим в файл window.cpp:

#define STRICT          //для строгой проверки типов

#include <windows.h>

#include "window.hpp"

Window::Window(HINSTANCE hInstance,LPSTR szClassName, LPSTR

szWindowTitle)

{

errno=0;

//Создаем окно

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

   {errno=1; return ;}

}

//Вывродом текста в окно займется оконная ф-ция, которую мы

составим в файле winproc.cpp:

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  HDC hdc;

  PAINTSTRUCT ps;

  switch (msg)

  {

    case WM_PAINT:

{

    hdc=BeginPaint(hwnd,&ps); //получаем индекс контекста

                              // устройства

    TextOut(hdc,10,20,"Сообщение о чем-нибудь",18);//Вывод

                                                   //текста

    EndPaint(hwnd,&ps);      //возвращаем контекст

    return 0;

}

    case WM_LBUTTONDOWN:

{

 hdc=GetDC(hwnd);   //получаем индуекс контекста устройства

 TextOut(hdc,10,40,"Сообщение о левом буттоне",24);//Вывод

 ReleaseDC(hwnd,hdc);       //возвращаем контекст

 return 0;

}

    case WM_RBUTTONDOWN:

{

 hdc=GetDC(hwnd);   //получаем индуекс контекста устройства

 TextOut(hdc,10,60,"Сообщение о правом буттоне",24);//Вывод

 ReleaseDC(hwnd,hdc);       //возвращаем контекст

 return 0;

}

    case WM_DESTROY:  {

      PostQuitMessage(0); return 0;

    } }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }

На этот раз составим и файл определения модуля textout.def:

NAME TEXTOUT

DESCRIPTION 'Программа вывода текста из книги Фролова'

EXETYPE  windows

STUB     'winstub.exe'

STACKSIZE 5120

HEAPSIZE 1024

CODE preload moveable discardable

DATA preload moveable multiple


Обработка клавиатурных сообщений

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

Windows вырабатывает клавиатурные сообщения и записывает их во

внутреннюю системную очередь. Для правильного распределения

сообщений от клавиаттуры в Windows используется концепция ФОКУСА

ВВОДА - это атрибут, присваиваемый одному из окон. Если окно

имеет фокус ввода, то его оконная ф-ция получает из системной

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

Windows есть 2 ф-ции для получения и изменения фокуса ввода:

HWND    WINAPI SetFocus(HWND);

HWND    WINAPI GetFocus(void);

Низкоуровневые сообщения драйвера преобразуются системой в

сообщения с кодами виртуальных клавиш и именно эти сообщения

обрабатываются прикладными программами. Их опрделения в файле

windows.h соответствуют следующему:

VK_LBUTTON          0x01           левая мыши

VK_RBUTTON          0x02           правая мыши

VK_CANCEL           0x03           Ctrl+Break

VK_MBUTTON          0x04           средняя мыши

VK_BACK             0x08           BackSpace

VK_TAB              0x09           Tab

VK_CLEAR            0x0C   <5> на доп клав при выкл NumLock

VK_RETURN           0x0D           Enter

VK_SHIFT            0x10           Shift

VK_CONTROL          0x11           Ctrl

VK_MENU             0x12           Alt

VK_PAUSE            0x13           Pause

VK_CAPITAL          0x14           CapsLock

VK_ESCAPE           0x1B           Esc

VK_SPACE            0x20           пробел

VK_PRIOR            0x21           PgUp

VK_NEXT             0x22           PgDn

VK_END              0x23           End

VK_HOME             0x24           Home

VK_LEFT             0x25

VK_UP               0x26

VK_RIGHT            0x27

VK_DOWN             0x28

VK_SELECT           0x29

VK_PRINT            0x2A

VK_EXECUTE          0x2B

VK_SNAPSHOT         0x2C          PrtScr

VK_INSERT           0x2D          Ins

VK_DELETE           0x2E          Del

VK_HELP             0x2F          Цифровая

VK_NUMPAD0          0x60          клавитура

VK_NUMPAD1          0x61

VK_NUMPAD2          0x62

VK_NUMPAD3          0x63

VK_NUMPAD4          0x64

VK_NUMPAD5          0x65

VK_NUMPAD6          0x66

VK_NUMPAD7          0x67

VK_NUMPAD8          0x68

VK_NUMPAD9          0x69

VK_MULTIPLY         0x6A

VK_ADD              0x6B

VK_SEPARATOR        0x6C

VK_SUBTRACT         0x6D

VK_DECIMAL          0x6E

VK_DIVIDE           0x6F

VK_F1               0x70         Функциональные

VK_F2               0x71         клавиши

VK_F3               0x72

VK_F4               0x73

VK_F5               0x74

VK_F6               0x75

VK_F7               0x76

VK_F8               0x77

VK_F9               0x78

VK_F10              0x79

VK_F11              0x7A

VK_F12              0x7B

VK_F13              0x7C

VK_F14              0x7D

VK_F15              0x7E

VK_F16              0x7F

VK_F17              0x80

VK_F18              0x81

VK_F19              0x82

VK_F20              0x83

VK_F21              0x84

VK_F22              0x85

VK_F23              0x86

VK_F24              0x87

VK_NUMLOCK          0x90      NumLock

VK_SCROLL           0x91      Scroll Lock

Еще более высокий уровень сообщений образуется преобразованием

сообщений с кодами виртуальных клавиш, например сообщения от

меню о выбранной строке.

От клавиатуры поступают 4  сообщения:

 WM_KEYDOWN, WM_KEYUP   или WM_SYSKEYDOWN, WM_SYSKEYUP (если

 нажимаются клавиши в комюинации с Alt - они обычно передаются

 ф-ции DefWndowProc). Информация о нажатой клавише содержится в

 параметрах lParam (скэн-код,счетчик повторов и пр) и wParam

 (код виртуальной клавиши) сообщений.

Для определения состояния клавиш Shift, CapsLock, Ctrl, NumLock

сразу после получения клавиатурного сообщения ф-ция окна должна

вызвать ф-цию

int     WINAPI GetKeyState(int);

с кодом виртуальной клавиши в качестве параметра. Старший бит 1

говорит о нажатии, младший 1 - о включенном состоянии

переключателя.

Чтобы узнать состояние клавиш в произвольный момент пользуютя

ф-цией:

int     WINAPI GetAsyncKeyState(int);

Для определения состояния всех клвиш одновременно:

void    WINAPI GetKeyboardState(BYTE FAR* );

с указателем на 256-байтовый массив в качестве параметра.

Записав в этот массив необходимые коды состояний можно изменить

их ф-цией

 void    WINAPI SetKeyboardState(BYTE FAR* );

Для работы с обычными символьными клавишами в цикл обработки

сообщений включают ф-цию TranslateMessage, которая преобразует

клавиатурные сообщения в символьные WM_CHAR и WM_SYSCHAR,

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

содержит код символа в стандарте ANSI (вместо привычного для

ДОС набора ОЕМ). По выбору пользователя может использоваться та

или иная кодировка, а для взаимной перекодировки Windows

предоставляет ф-ции:

void    WINAPI AnsiToOem(const char _huge*, char _huge*);

void    WINAPI OemToAnsi(const char _huge*, char _huge*);

void    WINAPI AnsiToOemBuff(LPCSTR, LPSTR, UINT);

void    WINAPI OemToAnsiBuff(LPCSTR, LPSTR, UINT);

Для преобразования символов строк в прописные или строчные есть:

LPSTR   WINAPI AnsiUpper(LPSTR);

LPSTR   WINAPI AnsiLower(LPSTR);

UINT    WINAPI AnsiUpperBuff(LPSTR, UINT);

UINT    WINAPI AnsiLowerBuff(LPSTR, UINT);

При получении фокуса ввода окно получает сообщение WM_SETFOCUS и

обработчик его может при необходимости создать в окне текстовый

курсор вызовом ф-ции

void    WINAPI CreateCaret(HWND, HBITMAP, int, int);

здесь 1-й параметр -идентификатор окна, 2-й - может быть 0

(вертикальная черта ченого цвета ), 1 (серый цвет) или

идентификатор bitmap-изображения. 3-й и 4-й параметры - ширина и

высота курсора;  если ширина и высота 0, то курсор будет шириной

и высотой рамки вокруг окна.

Сразу после создания курсор невидим и включить его можно ф-цией

void    WINAPI ShowCaret(HWND);

Перед перерисовкой окнакурсор необходимо выключить ф-цией

void    WINAPI HideCaret(HWND);

При получении сообщения WM_KILLFOCUS ф-ция должна уничтожить

текстовый курсор вызовом

void    WINAPI DestroyCaret(void);

Для получения и установки позиции курсора используют:

void    WINAPI SetCaretPos(int, int);

void    WINAPI GetCaretPos(POINT FAR*);

Приводимая ниже программа (Фролофы, тоом 11) создает главное

окно, ф-ции которого обрабатывают все клавиатурные сообщения и

выводят его имя, параметры, название виртуальной клавиши. Кроме

способов обработки сообщений программа демонстрирует свертку

экрана и загрузку в контекст отображения системного шрифта с

фиксированной шириной букв.

//Файл kbmsg.cpp

#define STRICT          //для строгой проверки типов

#include <windows.h>

#include <mem.h>

#include "winapp.hpp"

#include "textout.hpp"

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL InitApp(HINSTANCE);

// Имя класса окна

char const szClassName[]   = "KBMSGAppClass";

// Заголовок окна

char const szWindowTitle[] = "KBMSG Application";

#pragma argsused       //игнорировать неиспользуемые параметры

// Главная функция в Си для Windows называется

// WinMain

// возвращает int, стнроится по Паскалевскому вызову

//и имеет ровно 4 параметра

int PASCAL

WinMain(HINSTANCE hInstance,      //идентификатор приложения

        HINSTANCE hPrevInstance,  //идентификатор предыдущей

                                  // копии приложения

        LPSTR     lpszCmdLine,    //дальний указатель на

                                  //строку параметров

        int       nCmdShow)       //как рисовать главное окно

{

MSG msg;

HWND hwnd;

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    // Создаем символьные сообщения

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  wc.style = CS_HREDRAW | CS_VREDRAW    //стиль окна

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

//Файл wndproc.cpp содержит ф-цию окна для обработки сообщений

#define STRICT          //для строгой проверки типов

#include <windows.h>

#include <stdio.h>

#include <string.h>

void PrintMsg(HWND,WPARAM,LPARAM,char*);

static int cxChar,cyChar;

LRESULT CALLBACK _export WndProc(HWND hwnd, UINT msg, WPARAM

wParam, LPARAM lParam)

{

 HDC hdc;

 PAINTSTRUCT ps;

 static TEXTMETRIC tm;

 swith (msg)

{

 case WM_CREATE:

 {

  hdc=GetDC(hwnd);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Заполняем структуру метрик шрифта

 GetTextMetrics(hdc,&tm);

cxChar=tm.tmMaxCharWidth;   //Ширина букв

cyChar=tm.tmHeight+tm.tmExternalLeading; //Высота + межстр

                                         //интервал

ReleaseDC(hwnd,hdc);

//Верхняя граница несвертываемой части окна для 2-х строк

//заголовка

rect.top=3*cyChar;

return 0;

 }

case WM_SIZE:

{

 rect.right=LOWORD(lParam);

 rect.bottom=HIWORD(lPaRAM);

 InvalidateRect(hwnd,NULL,TRUE);

 UpdateWindow(hwnd);

 return 0;

}

case WM_PAINT:

{

char szHead1[]=

"Mesage          Char  wParam  lParam  KeyName ";

char szHead2[]=

"______________________________________________"

hdc=BeginPaint(hwnd,&ps);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Выводим заголовок

TextOut(hdc,cxChar,cyChar/2,szHead1,sizeof(szHead1)-1);

TextOut(hdc,cxChar,cyChar/2+cyChar,szHead2,sizeof(szHead2)-1);

EndPaint(hwnd,&ps);

return 0;

}

case WM_KEYDOWN:

{

 PrintMsg(hwnd,wParam,lParam,"WM_KEYDOWN");

 break;

}

case WM_KEYUP:

{

 PrintMsg(hwnd,wParam,lParam,"WM_KEYUP");

 break;

}

case WM_SYSKEYDOWN:

{

 PrintMsg(hwnd,wParam,lParam,"WM_SYSKEYDOWN");

 break;

}

case WM_SYSKEYUP:

{

 PrintMsg(hwnd,wParam,lParam,"WM_SYSKEYUP");

 break;

}

case WM_CHAR:

{

 PrintMsg(hwnd,wParam,lParam,"WM_CHAR");

 break;

}

case WM_SYSCHAR:

{

 PrintMsg(hwnd,wParam,lParam,"WM_SYSCHAR");

 break;

}

case WM_DEADCHAR:

{

 PrintMsg(hwnd,wParam,lParam,"WM_DEADCHAR");

 break;

}

case WM_SYSDEADCHAR:

{

 PrintMsg(hwnd,wParam,lParam,"WM_SYSDEADCHAR");

 break;

}

case WM_DESTROY:

{

 PostQuitMessage(0);

 return 0;

}

}

return DefWindowProc(hwnd,msg,wParam,lParam);

}

//Функция вывода

void PrintMsg(HWND hwnd,WPARAM wParam,LPARAM lParam,char*

szMsg)

{

HDC hdc;

char szBuf[256];

char szKeyName[20];

int nBufSize;

int rc;

 //Сворачиваем незанятую заголовком часть окна

ScrollWindow(hwnd,0,-cyChar,&rect,&rect);

hdc=GetDC(hwnd);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Получаем имя клавиши

rc=GetKeyNameText(lParam,szKeyName,20);

if(rc) MessageBeep(0);

//Готовим строку описания сообщения

nBufSize=wsprintf(szBuf,

"%-14s      %c     %02x      %08l   %-20sX",

(LPSTR)szMsg,(BYTE)wParam,(BYTE)wParam,lParam,(LPSTR)szKeyName);

//Выводим строку в низ окна

TextOut(hdc,cxChar,rect.bottom-cyChar,szBuf,nBufSize);

ReleaseDC(hwnd,hdc);

//Объявляем небновляемой внутреннюю часть окна

ValidateRect(hwnd,NULL);

}


Обработка  сообщений мыши
Обработка сообщений от мыши

 Мышь может порождать 22 сообщения, но используются в прикладной

 программе только часть , остальные как правило передаются

 DefWindowproc. Мышиные сообщения несут координаты курсора в

 различные моменты. Существуют 2 режима распределения мышиных

 сообщений - в 1-м сообщения попадают окнам, в поле которых

 находится курсор, а во 2-м - только окну, захватившему мышь в

 монопольное пользование вызовом ф-ции:

HWND WINAPI SetCapture(HWND hwnd);

Нормальный режим восстанавливается ф-цией:

void WINAPI ReleaseCapture(void);

Список мышиных сообщений:

WM_MOUSEMOVE        0x0200

WM_LBUTTONDOWN      0x0201

WM_LBUTTONUP        0x0202

WM_LBUTTONDBLCLK    0x0203

WM_RBUTTONDOWN      0x0204

WM_RBUTTONUP        0x0205

WM_RBUTTONDBLCLK    0x0206

WM_MBUTTONDOWN      0x0207

WM_MBUTTONUP        0x0208

WM_MBUTTONDBLCLK    0x0209

WM_MOUSEFIRST       0x0200

WM_MOUSELAST        0x0209

WM_NCMOUSEMOVE      0x00A0

WM_NCLBUTTONDOWN    0x00A1

WM_NCLBUTTONUP      0x00A2

WM_NCLBUTTONDBLCLK  0x00A3

WM_NCRBUTTONDOWN    0x00A4

WM_NCRBUTTONUP      0x00A5

WM_NCRBUTTONDBLCLK  0x00A6

WM_NCMBUTTONDOWN    0x00A7

WM_NCMBUTTONUP      0x00A8

WM_NCMBUTTONDBLCLK  0x00A9

WM_MOUSEACTIVATE    0x0021

Сообщения для внутренней области окна генерируются , если

DefWindowProc при обработке сообщения WM_HITTEST вернула

значение HTCLIENT. Все сообщения этой группы имеют lParam с

координатами курсора (горизонтальные в младшем слове,

вертикальные в старшем), а wParam состоит из отдельных битовых

флагов, позволяющих определить, какие клавиши мыши или

клавиатуры были нажаты в момент связанного с сообщением

события:

MK_LBUTTON          0x0001

MK_RBUTTON          0x0002

MK_SHIFT            0x0004

MK_CONTROL          0x0008

MK_MBUTTON          0x0010

Внимание: чтобы окно получало сообщения о двойном щелчке мыши

при регистрации оконного класса необходимо указать стиль класса

окна CS_DBLCLKS.

ПРИМЕР 1: программа,отображающая координаты курсора в момент

нажатия левой кнопки, стирающая внутреннюю область окно при

нажатии правой и выводящая окно сообщения при двойном щелчке.

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "MOUSEXYAppClass";

// Заголовок окна

char const szWindowTitle[] = "MOUSEXY Application";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  //стиль окна

  wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

 //Выполняем обработку сообщений, ид-р которых передается

 // через параметр msg

  switch (msg)

  {

    case WM_LBUTTONDOWN:

   {

   WORD cPos,yPos,nSize;

   BYTE szBuf[80];

//сохраняем координаты курсора

xPos=LOWORD(lParam);

yPos=HIWRD(lParam);

hdc=GetDC(hwnd);

//Готовим текстовую строку для координат

nSize=wsprintf(szbuf,"%d,%d",xPos,yPos);

//Выводим координаты курсора в его позиции

TextOut(hdc,xPos,yPos,szBuf,nSize);

ReleaseDC(hwnd,hdc);

return 0;

}

    case WM_RBUTTONDOWN:

   {

//Перерисовываем все окно стирая фон

InvalidateRect(hwnd,NULL,TRUE);

UpdateWindow(hwnd);

return 0;

}

case WM_LBUTTONDBLCLK:

{

   MessageBox(NULL,"Двойной щелчек","MouseXY",MB_OK |

   MB_ICONINFORMATION); return 0;

}

    case WM_DESTROY:  {

      PostQuitMessage(0); return 0;

    } }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }

ПРИМЕР 2 демонстрирует обработку сообщения WM_NCHITTEST и

WM_MOUSEMOVE для внешней неклиентной области окна.

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "MOUSENCAppClass";

// Заголовок окна

char const szWindowTitle[] = "MOUSENC Application";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  //стиль окна

  wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

WORD xPosScr, yPosScr, nSizeScr;

WORD cPos,yPos,nSize;

BYTE szBuf[80];

static TEXTMETRIC tm;

static int cxChar,cyChar;

  switch (msg)

  {

    case WM_CREATE:

   {

hdc=GetDC(hwnd);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Заполняем структуру метрик шрифта

 GetTextMetrics(hdc,&tm);

cxChar=tm.tmMaxCharWidth;   //Ширина букв

cyChar=tm.tmHeight+tm.tmExternalLeading; //Высота + межстр

                                         //интервал

ReleaseDC(hwnd,hdc);

return 0;

}

    case WM_NCHITTEST:

   {

//return HTCAPTION;

xPosScr=LOWORD(lParam);

yPosScr=HIWORD(lParam);

hdc=GetDC(hwnd);

nSize=wsprintf(szBuf,"(%-3d,%-3d)",xPosScr,yPosScr);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

 TextOut(hdc,cxChar,0,szBuf,nSize);

 ReleaseDC(hwnd,hdc);

 break;

}

case WM_MOUSEMOVE:

{

xPos=LOWORD(lParam);

yPos=HIWORD(lParam);

hdc=GetDC(hwnd);

nSize=wsprintf(szBuf,"(%-3d,%-3d)",xPosScr,yPosScr);

SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

TextOut(hdc,cxChar,cyChar,szBuf,nSize);

ReleaseDC(hwnd,hdc);

break;

}

    case WM_LBUTTONDBLK:

    case WM_DESTROY:  {

      PostQuitMessage(0); return 0;

    } }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }

  Возможные стандартные формы курсора, которые можно задавать в

  ф-ции LoadCursor вторым параметром:

IDC_ARROW        стрелка

IDC_IBEAM        в виде буквы I

IDC_WAIT         песочные часы

IDC_CROSS        перекрестье

IDC_UPARROW      вертик стрелка

IDC_SIZE         4-хсторонняя стрелка

IDC_ICON         пустая пиктограмма

IDC_SIZENWSE     2-ная стрелка северо-запад

IDC_SIZENESW    2-ная стрелка северовосток-югозапад

IDC_SIZEWE      2-ная стрелка восток-запад

IDC_SIZENS      2-ная стрелка север-юг

С помощью Workshop вы можете создать курсор произвольной формы.

Для изменения формы курсора например во время обработки

сообщения используют ф-цию:

HCURSOR WINAPI SetCursor(HCURSOR);

Параметр готовится с помощью ф-ции LoadCursor.

Включение-выключение курсора выполняет ф-ция:

int     WINAPI ShowCursor(BOOL);

Для установки или получения позиции мышиного курсора используйте

ф-ции:

void    WINAPI SetCursorPos(int, int);

void    WINAPI GetCursorPos(POINT FAR*);

ПРИМЕР 3: пермещение мышиного курсора с помощью клавиатуры.

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

int max(int,int);

int min(int,int);

// Имя класса окна

char const szClassName[]   = "MOUSEKEYAppClass";

// Заголовок окна

char const szWindowTitle[] = "MOUSEKEY Application";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  //стиль окна

  wc.style = CS_HREDRAW | CS_VREDRAW ;

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

int max(int value1,int value2)

{

 return((value1>value2) ? value1 : value2);

}

int min(int value1,int value2)

{

 return((value1<value2) ? value1 : value2);

}

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

static POINT pt;

RECT rc;

switch (msg)

{

 case WM_KEYDOWN:

{

 //получаем координаты курсора мыши

 GetCursorPos(&pt);

 //пребразуем экранные координаты в оконные

ScreenToClient(hwnd,&pt);

//изменяем координаты

switch(wParam)

{

 case VK_DOWN:

{pt.y+=20;break;}

 case VK_UP:

{pt.y-=20;break;}

 case VK_LEFT:

{pt.x-=20;break;}

 case VK_RIGHT:

{pt.x+=20;break;}

default: return 0;

}

//получаем координаты внутренней области окна

GGetClientRect(hwnd,&rc);

//вычисляем новые координаты мыши с учетом допустимых пределов

pt.x=max(min(pt.x,rc.right),rc.left);

pt.y=max(min(pt.y,rc.bottom),rc.top);

//преобразуем оконные координаты в экранные

ClientToScreen(hwnd,&pt);

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

SetCursorPos(pt.x,pt.y);

return 0;

}

    case WM_DESTROY:  {

      PostQuitMessage(0); return 0;

    } }

 return DefWindowProc(hwnd,msg,wParam,lParam);

}


Работа с таймерами
Работа с таймерами в Windows-приложениях

Во многих программах необходимо отслеживать время и по

достижении заданного момента выполнять некоторые действия -

например, контроль лимита времени в системах тестирования и

проверки знаний.

Windows позволяет для каждого приложения создавать несколько

виртуальных таймеров, работающих по прерываниям от одного

физического таймера и посылающих ф-ции окна или специально

составленной ф-ции низкоприоритетное (посылаемое при отсутствии

в очереди других) сообщение WM_TIMER; спецфункция при этом как и

ф-ция окна должна быть описана как так называемая ф-ция

обратного вызова (callback function) с ключевым словом _export и

специальным прологом и эпилогом - эта ф-ция будет вызываться не

из приложения , а из Windows.

Точность виртуальных таймеров невелика, т к их сообщения

проходят через очередь сообщений прикладной программы и другие

программы могут блокироватьь на какое - то время работу текущей

программы.Windows не предназначена для работы в режиме реального

времени, она ориентирована на работу с человеком,  когда

небольшие задержки не играют существенной роли. Реальную

длительность интервалов таймеров вы можете узнать с помощью

ф-ции

DWORD   WINAPI GetTimerResolution(void);

Для создания таймера используйте ф-цию, возвращающую

идентификатор созданного таймера (если 1-й параметр NULL)

UINT    WINAPI SetTimer(HWND, UINT, UINT, TIMERPROC);

1-й параметр - идентификатор окна или NULL, 2-й - идентификатор

таймера (используется если 1-й параметр не NULL), 3-й - период

прихода сообщений в миллисекундах не менее 55, 4 - й - адрес

ф-ции, которая будет получать сообщения таймера. Тип TIMERPROC

описан в windows.h:

typedef void (CALLBACK* TIMERPROC)(HWND, UINT, UINT, DWORD);

Количкество таймеров в Windows-3.1 не огрничивается , но если

таймер вам больше не нужен, надо его уничтожить ф-цией

BOOL    WINAPI KillTimer(HWND, UINT);

Параметр wParam сообщения WM_TIMER содержит идентификатор

таймера, а с помощью lParam можно определить адрес

ф-ции-обработчика таймерного сообщения.

Подключениие таймера к окну:

#define FIRST_TIMER 1

int nTimerId;

nTimerId=SetTimer(hwnd,FIRST_TIMER,1000,NULL);

Для изменения интервала посылки сообщений придется уничтожить

таймер и создать новый с другим интервалом.

Подключение таймера к специальной ф-ции с произвольным именем

(Параметры ф-ции заполняются Windows при вызове.Последний

параметр - текущее время по компьютерным часам в тиках таймера с

момента запуска Windows.)

void CALLBACK _export TimerProc(HWND hwnd, UINT msg, UINT

idTimer, DWORD dwTime)

{

 ....

}

 в Borland Си++ версии 3.1 и старше осуществляется просто:

nTimerId=SetTimer(hwnd,0,1000,(TIMERPROC)TimerProc);

ПРИМЕР 1 демонстрирует простейший вариант использования таймера:

#define STRICT

#include <windows.h>

#include <mem.h>

#define BEEP_TIMER 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "TIMBEEPAppClass";

// Заголовок окна

char const szWindowTitle[] = "TIMBEEP Application";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  //стиль окна

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

 //Выполняем обработку сообщений, ид-р которых передается

 // через параметр msg

  switch (msg)

  {

    case WM_CREATE:

   {

//Создаем таймер

SetTimer(hwnd,BEEP_TIMER,1000,NULL);

return 0;

}

    case WM_TIMER:

   {

//Звучим по сообщению таймера

MessageBeep(-1);

return 0;

}

    case WM_DESTROY:  {

      KillTimer(hwnd,BEEP_TIMER);

      PostQuitMessage(0);

      return 0;

    } }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }

ПРИМЕР 2 формирует простейшие часы с цифровой индикацией

системным шрифтом с фиксированной шириной символов,

демонстрирует использование спецфункции для обработки сообщений

таймера, а также способ обработки сообщения WM_NCHITTEST для

изменения размеров и позиции окна без заголовка, системного меню

и кнопок изменения размера:

#define STRICT

#include <windows.h>

#include <mem.h>

#include <time.h>

#define CLOCK_TIMER 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

void CALLBACK _export TimerProc(HWND, UINT, UINT, DWORD);

int nBeepTimerId;   //Для хранения идентиф таймера

// Имя класса окна

char const szClassName[]   = "TMCLOCKAppClass";

// Заголовок окна

char const szWindowTitle[] = "TMCLOCK Application";

TEXTMETRIC tm;

int cxChar,cyChar;

RECT rc;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

//Получаем координаты окна DeskTop, занимающего весь экран

  GetWindowRect(GetDesktopWindow(),&rc);

//Создаем временное окно с толстой рамкой для изменения размера,

//но без заголовка и системного меню - размеры  и позицию

//указываем произвольными

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_POPUPWINDOW | WS_THICKFRAME // стиль окна

    100, 100,      // задаем расположение и размеры

    100, 100      // окна  прозвольно

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  //Передвигаем окно в правый нижний угол

    MoveWindow(hwnd, rc.right-cxChar*15, rc.bottom-cyChar*3,

    cxChar*10, cyChar*2, TRUE);

  //Отбражаем окно на новом месте

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc)); //нули во все поля структуры

  //стиль окна

  wc.style = CS_HREDRAW | CSVREDRAW;

  wc.lpfnWndProc = (WNDPROC) WndProc; //указ на ф-цию окна

  wc.cbClsExtra = 0; //размер дополнительной области данных

                    //зарезервированной в описании класса

  wc.cbWndExtra = 0; //размер дополнительной области данных

                     //для каждого окна

  wc.hInstance = hInstance; //идентификатор приложения

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);//ид-р пиктограммы

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);//ид-р курсора

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);//цвет фона окна

  wc.lpszMenuName = (LPSTR)NULL;  //ид-р меню

  wc.lpszClassName = (LPSTR)szClassName; //имя класса

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0); //возвращаем результат регистрации

                           //классав }

// =====================================

// Функция WndProc для обработки сообщений главного окна

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT ps;

 //Выполняем обработку сообщений, ид-р которых передается

 // через параметр msg

  switch (msg)

  {

    case WM_CREATE:

   {

//Создаем таймер   для ф-ции  окна

SetTimer(hwnd,CLOCK_TIMER,1000,NULL);

//Создаем таймер   для ф-ции  TimerProc

nBeepTimerId = SetTimer(hwnd,0,1000,(TIMERPROC)TimerProc);

hdc = GetDC(hwnd);

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Заполняем структуру метрик шрифта

 GetTextMetrics(hdc,&tm);

cxChar=tm.tmMaxCharWidth;   //Ширина букв

cyChar=tm.tmHeight+tm.tmExternalLeading; //Высота + межстр

                                         //интервал

ReleaseDC(hwnd,hdc);

return 0;

}

//Встраиваем свой обработчик WM_NCHITTEST (посылается для

определения элемента окна , над которы находится мышиный курсор)

чтобы перемещать окно без заголовка

case WM_NCHITTEST:

{

 long lRetVal;

 //Вызываем DefWindowProc и прорверяем возвращаемое ею значение

 lRetVal=DefWindowProc(hwnd,msg,wParam,lParam);

//Если курсор в одном из элементов толстой рамки

 if(lRetVal == HTLEFT         ||

    lRetVal == HTRIGHT        ||

    lRetVal == HTTOP          ||

    lRetVal == HTBOTTOM       ||

    lRetVal == HTBOTTOMRIGHT  ||

    lRetVal == HTTOPRIGHT     ||

    lRetVal == HTTOPLEFT      ||

    lRetVal == HTBOTTOMLEFT   ||

)

return lRetVal;

//иначе возвращаем HTCAPTION для заголовка чтобы окно можно было

//"тащить"  за внутреннюю область вместо заголовка

 else return HTCAPTION; }

    case WM_TIMER:

   {

//По сообщению таймера перерисовываем внутр область окна

InvalidateRECT(hwnd,NULL,FALSE);

return 0;

}

    case WM_DESTROY:  {

      KillTimer(hwnd,CLOCK_TIMER);

      KillTimer(hwnd,nBeepTimerId);

      PostQuitMessage(0);

      return 0;

    }

    case WM_PAINT:

{

 BYTE szBuf[80];

 int nBufSize;

 time_t t;

 struct tm*  ltime;

 RECT rc;

 hdc= BeginPaint(hwnd,&ps);

 time(&t);

 ltime=localtime(&t);

 //Готовим буфер строки с текущим временем:

nBufSize = wsprintf(szBuf, "", ltime->tm_hour, ltime->tm_min,

ltime->tm_sec );

//Выбираем шрифт

 SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

//Получаем координаты и размеры окна

GetClientRect(hwnd,&rc);

//Выводим время в центре окна

DrawText(hdc,(LPSTR)szBuf, nBufSize, &rc,

DT_CENTER | DT_VCENTER | DT_NOCLIP | DT_SINGLELINE);

EndPaint(hwnd,&ps);

 }

 }

//Все необработанные нами сообщения должны передаваться

//ф-ции DefWindowProc

  return DefWindowProc(hwnd, msg, wParam, lParam); }

//-----------------

//Функция TimeProc

//----------------

void CALLBACK _export

TimerProc(HWND hwnd, UINT msg, UINT idTimer, DWORD dwTime)

{

 MessageBeep(-1);

 return;

}


Ресурсы - таблицы строк

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

 Windows-приложения могут находиться дополнительные данные -

 ресурсы - текстовые строки, пиктограммы, меню, произвольные

 матричные изображения, массивы данных; ресурсы могут

 загружаться в оперативную память или сразу при запуске

 приложения либо при обращении к ним из приложения. Ресурсы

 можно редактировать без перетрансляции программного проекта.

 Для создания и редактирования ресурсов созданы различные

 иннструментальные программы, мы будем использовать одну из них

 -Resource Workshop.

Для включения ресурсов в загрузочный модуль надо вначале создать

с помощью любого доступного текстового редактора текстовый файл

описания ресурсов с расширением .rc и включить его в проект

приложения вместе с файлами исходных текстов и файлом

определения модуля. В процессе сборки файл описания ресурсов

компилируется специальным компилятором ресурсов rc.exe с

использованием препроцессора rcpp.exe, поставляемыми вместе с

ситемой разработки Windows- приложений, например с Borland C++.

Полученный при компиляции двоичный файл с расширением .res на

конечной фазе сборки записывается компилятором ресурсогв в

загрузочный модуль с формированием специальной таблицы ресурсов

в заголовке .ехе-файла для обеспечения поиска и загрузки

ресурсов в ОЗУ.

 Самый простой в использовании ресурс - таблица текстовых строк,

 закрытых 2-ичным нгулем, на которые можно ссылаться по

 идентификатору целому числу или символическому имени,

 определенному с помощью директивы #define.

Для создания таблицы строк файл описания ресурсов должен

содержатьть оператор STRINGTABLE:

STRINGTABLE [параметры загрузки] [тип памяти]

BEGIN

IDS_APPNAME,    "String"

IDS_WINDOWNAME, "String Application"

IDS_MESSAGE,    "%s Demo, (C) Frolov A.V., %d"

END

В качестве параметров загрузки можно указывать PRELOAD или

LOADONCALL, тип памяти может быть FIXED или MOVABLE с возможным

дополнением DISCARDABLE.

Для редактирования таблицы строк в Resource Workshop запустите

его и из меню File выберите Open project, в меню File Type

укажите тип файла, например EXE application, укажите путь к

файлу с помощью меню Path, Files, Directories - после э

того в главном окне появится окно с названием файла в заголовке

и перечнем ресурсов в рабочей области. Выберите подлежащий

редактированию ресурс и редактируйте его; если на буквах 'ю' и

'я' получите сообщение об ошибке, введите вм есто них их

8-миричные коды \376 или \387.

Для загрузки строки в ОЗУ необходимо использовать ф-цию:

int     WINAPI LoadString(HINSTANCE, UINT, LPSTR, int);

Ее парметры: идентификатор приложения , идентификатор ресурсов,

адрес и размер буфера.

Для работы с текстовыми строками в Windows-приложении лучше

использовать не стандартные ф-ции с прототипами в string.h а их

аналоги из интерфейса Windows, испльзующие дальние указатели и

учитывающие особенности национальных алфавитов:

BOOL    WINAPI IsCharAlpha(char);

BOOL    WINAPI IsCharAlphaNumeric(char);

BOOL    WINAPI IsCharUpper(char);

BOOL    WINAPI IsCharLower(char);

int     WINAPI lstrcmp(LPCSTR, LPCSTR);

int     WINAPI lstrcmpi(LPCSTR, LPCSTR);

LPSTR   WINAPI lstrcpy(LPSTR, LPCSTR);

LPSTR   WINAPI lstrcat(LPSTR, LPCSTR);

int     WINAPI lstrlen(LPCSTR);

LPSTR   WINAPI lstrcpyn(LPSTR, LPCSTR, int);

void    WINAPI hmemcpy(void _huge*, const void _huge*, long);

ПРИМЕР демонстрирует использование таблицы строк для хранения

названия приложения, заголовка, окна и строки формата для вывода

текста сообщения.

// ----------------------------------------

// Работа с таблицей строк

// ----------------------------------------

#define STRICT

#include <windows.h>

#include "string.hpp"

#define IDS_APPNAME    1

#define IDS_WINDOWNAME 2

#define IDS_MESSAGE    3

// Прототип функции обработки ошибки

void Error(void);

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  // Буфера для работы со строками

        BYTE szAppName[20];

        BYTE szWindowName[80];

        BYTE szMsg[80];

  BYTE szBuf[80];

  int  cb;

        // Загружаем строку с идентификатором IDS_APPNAME

        cb = LoadString(hInstance, IDS_APPNAME,

          szAppName, sizeof(szAppName));

        // Если в буфер записано 0 символов,

        // это означает, что ресурс не найден.

        // В этом случае мы вызываем функцию обработки

        // ошибки и завершаем работу приложения

        if(!cb)

        {

                        Error(); return -1;

  }

        // Загружаем строку с идентификатором IDS_WINDOWNAME

        cb = LoadString(hInstance, IDS_WINDOWNAME,

          szWindowName, sizeof(szWindowName));

        if(!cb)

        {

                        Error(); return -1;

  }

        // Загружаем строку с идентификатором IDS_MESSAGE

        cb = LoadString(hInstance, IDS_MESSAGE,

          szMsg, sizeof(szMsg));

        if(!cb)

        {

                        Error(); return -1;

  }

  // Подготавливаем буфер для вывода сообщения

        wsprintf(szBuf, szMsg, (LPSTR)szAppName, 1994);

        // Выводим сообщение, составленное из строк

  // и числа 1994

        MessageBox(NULL, szBuf, szWindowName,

          MB_OK | MB_ICONINFORMATION);

        return 0;

}

// --------------------------------------------

// Функция обработки ошибки загрузки ресурса

// --------------------------------------------

void Error(void)

{

        MessageBox(NULL, "Ошибка при загрузке ресурса",

          "Error", MB_OK | MB_ICONSTOP);

}


Ресурсы - пиктограммы

 Resource Workshop позволяет создать пиктограммы размером 32х16,

 32х32, 64х64 пикселов с количеством цветов 8, 16 или 256 для

 цветных пиктограмм. В одном файле .ico можно хранить несколько

 пиктограмм различного размера с различным количеством цветов -

 при выводе Windows выберет соответствующую текущему режиму

 видеоадаптера.

Для создания файла .ico из меню File выберите New Project,

переключатель ICO и Enter. В появившейся панели New icon image

выберите перключатель Size и нужный размер, переключатель Colors

и количество цветов и ОК. В большом окне с полосой инструментов

редактируете изображение,  для добавки в файл

.ico варианта пиктограммы сделайте текущим окно ICON:ICON_1 и из

меню Image выберите строку New Image , сделайте 2-й щелчок мыши

по новой строке, выберите разрешение и приступайте к

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

Save file as..., указав путь.

Для включения пиктограммы в файл описания ресурсов используется

оператор ICON:

Icon ID ICON [параметры загрузки][тип памяти] имя файла с

пиктограммой

При регистрации класса окна мы уже использовали встроенные

пиктограммы IDI_APPLICATION с помощью ф-ции Loadicon:

HICON   WINAPI LoadIcon(HINSTANCE, LPCSTR);

Если в файле описания ресурсов ид-р пиктограммы задан текстовой

строкой, то 2-й парметр ф-ции - указатель на нее, а если в файле

описания ресурсов используется целое число, 2-й параметр следует

опрделить с помощью макрокоманды MAKEINTRESOURCE:

456 ICON gerat.ico  - в файле ресурсов

wc.hIcon= Loadicon(hInstance,MAKEINTRESOURCE(456));

Возможный вариант:

wc.hIcon= Loadicon(hInstance,"#456");

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

BOOL    WINAPI DestroyIcon(HICON);

Пять встроенных пиктогграмм windows имеют идентификаторы:

IDI_APPLICATION ,IDI_HAND, IDI_QUESTION, IDI_EXCLAMATION

IDI_ASTERISK

Загруженную Loadicon пиктограмму можно нарисовать в любом месте

окна ф-цией:

BOOL    WINAPI DrawIcon(HDC, int, int, HICON);

В файле Windows.h есть и другие ф-ции для работы с пиктограммами

- исследуйте их самостоятельно.

ПРИМЕР демонстрирует способ назначения главному окну приложения

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

окне несколько встроенных пиктограмм и одну, определенную в

файле описания ресурсов:

/* Таблица строк */

STRINGTABLE

BEGIN

IDS_CLASSNAME,   "ICOAppClass"

IDS_WINDOWTITLE, "ICO Demo"

END

/* Пиктограмма */

AppIcon ICON ico.ico

// ----------------------------------------

// Изображение пиктограмм

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#include <stdio.h>

#define IDS_CLASSNAME    1

#define IDS_WINDOWTITLE  2

#define IDS_CLASSNAME    1

#define IDS_WINDOWTITLE  2

void Error(void);

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[80];

char szWindowTitle[80];

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  int  cb;

  hInst = hInstance;

  // Загружаем текстовые строки

  cb = LoadString(hInstance, IDS_CLASSNAME,

    szClassName, sizeof(szClassName));

  if(!cb)

  {

    Error(); return -1;

  }

  cb = LoadString(hInstance, IDS_WINDOWTITLE,

    szWindowTitle, sizeof(szWindowTitle));

  if(!cb)

  {

    Error(); return -1;

  }

  if(!InitApp(hInstance))

      return FALSE;

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  if(!hwnd)

    return FALSE;

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Загружаем пиктограмму, которая будет использована

  // для отображения главного окна в свернутом виде

  wc.hIcon = LoadIcon(hInstance, "AppIcon");

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// --------------------------------------------

// Функция обработки ошибки загрузки ресурса

// --------------------------------------------

void Error(void)

{

  MessageBox(NULL, "Ошибка при загрузке ресурса",

    "Error", MB_OK | MB_ICONSTOP);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  HDC hdc;             // индекс контекста устройства

  PAINTSTRUCT ps;      // структура для рисования

  // Размеры пиктограммы

  static int xIcon, yIcon;

  // Идентификатор пиктограммы

  HICON hIcon;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Определяем размеры пиктограммы

      xIcon = GetSystemMetrics(SM_CXICON);

      yIcon = GetSystemMetrics(SM_CYICON);

      break;

    }

    case WM_PAINT:

    {

      hdc = BeginPaint(hwnd, &ps);

      // Загружаем и рисуем встроенные пиктограммы

      hIcon = LoadIcon(0, IDI_APPLICATION);

      DrawIcon(hdc, xIcon/2, yIcon/2, hIcon);

      hIcon = LoadIcon(0, IDI_ASTERISK);

      DrawIcon(hdc, xIcon/2 + xIcon*2, yIcon/2, hIcon);

      hIcon = LoadIcon(0, IDI_EXCLAMATION);

      DrawIcon(hdc, xIcon/2 + xIcon*4, yIcon/2, hIcon);

      hIcon = LoadIcon(0, IDI_HAND);

      DrawIcon(hdc, xIcon/2 + xIcon*6, yIcon/2, hIcon);

      hIcon = LoadIcon(0, IDI_QUESTION);

      DrawIcon(hdc, xIcon/2 + xIcon*8, yIcon/2, hIcon);

      // Загружаем и рисуем свою пиктограмму,

      // указанную в файле описания ресурсов

      hIcon = LoadIcon(hInst, "AppIcon");

      DrawIcon(hdc, xIcon/2, yIcon/2 + yIcon*2, hIcon);

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Ресурсы - курсоры мышки
Ресурсы - курсоры мыши

 Мышиный курсор - это упрощенный вариант битового изображения

 типа пиктограммы. Для создания своего курсора его надо

 нарисоватьв Resource Workshop по переключателю CUR и сохранить

 в файле с расширением .cur и на этот файл сослаться в файле

 описания ресурсов:

 CursorID CURSOR [параметры загрузки][тип памяти] имя файла

 Для загрузки курсора используется ф-ция

HCURSOR WINAPI LoadCursor(HINSTANCE, LPCSTR);

Встроенные курсоры:

IDC_ARROW           MAKEINTRESOURCE(32512)

IDC_IBEAM           MAKEINTRESOURCE(32513)

IDC_WAIT            MAKEINTRESOURCE(32514)

IDC_CROSS           MAKEINTRESOURCE(32515)

IDC_UPARROW         MAKEINTRESOURCE(32516)

IDC_SIZE            MAKEINTRESOURCE(32640)

IDC_ICON            MAKEINTRESOURCE(32641)

IDC_SIZENWSE        MAKEINTRESOURCE(32642)

IDC_SIZENESW        MAKEINTRESOURCE(32643)

IDC_SIZEWE          MAKEINTRESOURCE(32644)

IDC_SIZENS          MAKEINTRESOURCE(32645)

Для динамического изменения формы курсора следует

использоватьф-цию:

HCURSOR WINAPI SetCursor(HCURSOR);

с идентификатором нового курсора в качестве параметра.

Для включения - выключения изображения курсора используют:

int     WINAPI ShowCursor(BOOL);

Специальная ф-ция для рисования курсора нет - его рисует сама

Windows, но при желании его может нарисовать прикладная

программа в своем окне, воспользовавшись ф-цией DrawIcon.

ПРИМЕР демонстрирует использование при регистрации класса окна

нарисованного с помощью Resource Workshop курсора и рисует в

главном окне все встроенные курсоры и один из файла описания

ресурсов:

STRINGTABLE

BEGIN

IDS_CLASSNAME,   "CURSORAppClass"

IDS_WINDOWTITLE, "CURSOR Demo"

END

/* Пиктограмма */

AppIcon ICON cursor.ico

/* Курсор */

AppCursor CURSOR cursor.cur

// ----------------------------------------

// Изображение курсоров

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#include <stdio.h>

#define IDS_CLASSNAME    1

#define IDS_WINDOWTITLE  2

void Error(void);

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[80];

char szWindowTitle[80];

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  int  cb;

  hInst = hInstance;

  // Загружаем текстовые строки

  cb = LoadString(hInstance, IDS_CLASSNAME,

    szClassName, sizeof(szClassName));

  if(!cb)

  {

    Error(); return -1;

  }

  cb = LoadString(hInstance, IDS_WINDOWTITLE,

    szWindowTitle, sizeof(szWindowTitle));

  if(!cb)

  {

    Error(); return -1;

  }

  if(!InitApp(hInstance))

      return FALSE;

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  if(!hwnd)

    return FALSE;

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Загружаем пиктограмму, которая будет использована

  // для отображения главного окна в свернутом виде

  wc.hIcon = LoadIcon(hInstance, "AppIcon");

  // Загружаем курсор для главного окна

  wc.hCursor = LoadCursor(hInst, "AppCursor");

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// --------------------------------------------

// Функция обработки ошибки загрузки ресурса

// --------------------------------------------

void Error(void)

{

  MessageBox(NULL, "Ошибка при загрузке ресурса",

    "Error", MB_OK | MB_ICONSTOP);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  HDC hdc;             // индекс контекста устройства

  PAINTSTRUCT ps;      // структура для рисования

  // Размеры курсора

  static int xCursor, yCursor;

  // Идентификатор курсора

  HCURSOR hCursor;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Определяем размеры курсора

      xCursor = GetSystemMetrics(SM_CXCURSOR);

      yCursor = GetSystemMetrics(SM_CYCURSOR);

      break;

    }

    case WM_PAINT:

    {

      hdc = BeginPaint(hwnd, &ps);

      // Загружаем и рисуем встроенные курсоры

      hCursor = LoadCursor(0, IDC_ARROW);

      DrawIcon(hdc, xCursor/2, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_IBEAM);

      DrawIcon(hdc, xCursor/2 + xCursor, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_WAIT);

      DrawIcon(hdc, xCursor/2 + xCursor*2, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_CROSS);

      DrawIcon(hdc, xCursor/2 + xCursor*3, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_UPARROW);

      DrawIcon(hdc, xCursor/2 + xCursor*4, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZE);

      DrawIcon(hdc, xCursor/2 + xCursor*5, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_ICON);

      DrawIcon(hdc, xCursor/2 + xCursor*6, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENWSE);

      DrawIcon(hdc, xCursor/2 + xCursor*7, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENESW);

      DrawIcon(hdc, xCursor/2 + xCursor*8, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZEWE);

      DrawIcon(hdc, xCursor/2 + xCursor*9, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENS);

      DrawIcon(hdc, xCursor/2 + xCursor*10, yCursor/2, hCursor);

      // Загружаем и рисуем свой курсор,

      // указанный в файле описания ресурсов

      hCursor = LoadCursor(hInst, "AppCursor");

      DrawIcon(hdc, xCursor/2, yCursor/2 + yCursor*2, hCursor);

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Ресурсы - bitmap
Ресурсы - bitmap-изображения

В ресурсы приложения можно включить любые графические

изображения в формате bitmap, созданныев Resource Workshop или в

PaintBrush - их удобно использовать разработки дизайна, это

могут быть органы управления любой формы (например круглые

кнопки с картинками), введенные сканером фотографии, заставки,

эмблемы фирм и пр. Рисование в окне bitmap-изображения -

довольно сложная задача, в интерфейсе Windows отсутствует

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

создавать и описывать в файле описания ресурсов битовое

изображение и использовать его для закрашивания внутренней

области окна.

С оздание битового изображения ведут в Resource Workshop через

File - New Project - .BMP - OK  - панель New Bitmap Attributes -

выбор ширины6 высоты и количества цветов - OK - панель

редактирования - сохранение в файле.

Для включения изображения в файл описакния ресурсов как и

раньше:

BitmapID BITMAP [параметры загрузки][тип памяти] имя файла

Для загрузки используется ф-ция:

HBITMAP WINAPI LoadBitmap(HINSTANCE, LPCSTR);

Перед завершением работы программа должна удалить изображение из

памяти ф-цией:

BOOL    WINAPI DeleteObject(HGDIOBJ);

передав ей в качестве параметра полученный LoadBitmap

идентификатор.

Одно из простых приложений битовых образов - раскрашивание фона

окна напрмер изображениями 8х8 пикселов, созданными в Workshop.

После загрузки приложение должно создать из этого изображения

кисть :

HBRUSH  WINAPI CreatePatternBrush(HBITMAP);

с полученным от LoadBitmap идентификатором и получая

идентификатор кисти, который можно использовать при регистрации

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

быть уничтожена через DeleteObject.

ПРИМЕР демонстрирует именно такое применение битового

изображения. Файл ресурсов

/* Изображение типа bitmap */

123 BITMAP brush.bmp

 // ---------------------------------------- //

Использование кисти для закрашивания // внутренней области окна

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "BrushAppClass";

// Заголовок окна

char const szWindowTitle[] = "Brush Demo";

// Идентификатор изображения bitmap

HBITMAP hBmp;

// Идентификатор кисти

HBRUSH  hBrush;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Загружаем изображение bitmap из ресурсов

  hBmp   = LoadBitmap(hInstance, "#123");

  // Создаем кисть для закрашивания окна

  hBrush = CreatePatternBrush(hBmp);

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  // Перед завершением работы приложения

  // уничтожаем созданные нами изображение и кисть

  DeleteObject(hBmp);

  DeleteObject(hBrush);

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Закраска внутренней области окна

  wc.hbrBackground = hBrush;

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Органы управления -кнопки

В ресурсы приложения можно включить любые графические

изображения в формате bitmap, созданныев Resource Workshop или в

PaintBrush - их удобно использовать разработки дизайна, это

могут быть органы управления любой формы (например круглые

кнопки с картинками), введенные сканером фотографии, заставки,

эмблемы фирм и пр. Рисование в окне bitmap-изображения -

довольно сложная задача, в интерфейсе Windows отсутствует

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

создавать и описывать в файле описания ресурсов битовое

изображение и использовать его для закрашивания внутренней

области окна.

С оздание битового изображения ведут в Resource Workshop через

File - New Project - .BMP - OK  - панель New Bitmap Attributes -

выбор ширины6 высоты и количества цветов - OK - панель

редактирования - сохранение в файле.

Для включения изображения в файл описакния ресурсов как и

раньше:

BitmapID BITMAP [параметры загрузки][тип памяти] имя файла

Для загрузки используется ф-ция:

HBITMAP WINAPI LoadBitmap(HINSTANCE, LPCSTR);

Перед завершением работы программа должна удалить изображение из

памяти ф-цией:

BOOL    WINAPI DeleteObject(HGDIOBJ);

передав ей в качестве параметра полученный LoadBitmap

идентификатор.

Одно из простых приложений битовых образов - раскрашивание фона

окна напрмер изображениями 8х8 пикселов, созданными в Workshop.

После загрузки приложение должно создать из этого изображения

кисть :

HBRUSH  WINAPI CreatePatternBrush(HBITMAP);

с полученным от LoadBitmap идентификатором и получая

идентификатор кисти, который можно использовать при регистрации

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

быть уничтожена через DeleteObject.

ПРИМЕР демонстрирует именно такое применение битового

изображения. Файл ресурсов

/* Изображение типа bitmap */

123 BITMAP brush.bmp

 // ---------------------------------------- //

Использование кисти для закрашивания // внутренней области окна

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "BrushAppClass";

// Заголовок окна

char const szWindowTitle[] = "Brush Demo";

// Идентификатор изображения bitmap

HBITMAP hBmp;

// Идентификатор кисти

HBRUSH  hBrush;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Загружаем изображение bitmap из ресурсов

  hBmp   = LoadBitmap(hInstance, "#123");

  // Создаем кисть для закрашивания окна

  hBrush = CreatePatternBrush(hBmp);

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  // Перед завершением работы приложения

  // уничтожаем созданные нами изображение и кисть

  DeleteObject(hBmp);

  DeleteObject(hBrush);

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Закраска внутренней области окна

  wc.hbrBackground = hBrush;

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Управление кнопками из программ

 Программа может или вызывать специальные ф-ции пермещения,

 активизации, скрытия и пр. кнопки в окне или посылать органу

 управления сообщения , в ответ на которые он выполнит

 соответствующие действия.

 а)Функции управления окнами.

Для перемещения органа управления используют ф-цию:

BOOL    WINAPI MoveWindow(HWND, int, int, int, int, BOOL);

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

Для блокирования и разблокирования органа управления есть ф-ция:

BOOL    WINAPI EnableWindow(HWND,BOOL);

Для определения состояния блокировки:

BOOL    WINAPI IsWindowEnabled(HWND);

Скрыть орган управления можно с помощью ф-ции ShowWindow, указав

вторым параметром SW_HIDE, а для восстановления использовать ту

же ф-цию с параметром SW_SHOWNORMAL.

Изменит текст на кнопке можно с помощью ф-ции:

void    WINAPI SetWindowText(HWND, LPCSTR);

Уничтожить орган управления можно с помощью:

BOOL WINAPI DestroyWindow(HWND);

б) Передача сообщений органу управления.

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

1-й - это запись сообщения в очередь сообщений

BOOL    WINAPI PostMessage(HWND, UINT, WPARAM, LPARAM);

Здесь 2-й параметр - идентификатор сообщения.

2-й способ - непосредственная передача сообщенияминуя очередь

LRESULT WINAPI SendMessage(HWND, UINT, WPARAM, LPARAM);

 Для управления кнопкой можно использовать сообщение BM_SETSTATE

 с параметром wParam равным TRUE и нулевым lParam для нажатия.

ПРИМЕР создает в главном окне 5 кнопок Button1, PUSH, POP, OFF,

ON; первая при нажатии выдает панель сообщения, а остальные 4

управляют первой - нажимают, отжимают, блокируют и разблокируют.

// ----------------------------------------

// Управление стандартной кнопкой

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Идентификаторы кнопок

#define IDB_Button1 1

#define IDB_Button2 2

#define IDB_Button3 3

#define IDB_Button4 4

#define IDB_Button5 5

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ButtonCtlAppClass";

// Заголовок окна

char const szWindowTitle[] = "Button Control Demo";

// Идентификаторы кнопок

HWND hButton1, hButton2, hButton3, hButton4, hButton5;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем пять кнопок

  hButton1 = CreateWindow("button", "Button 1",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 20, 90, 30,

    hwnd, (HMENU) IDB_Button1, hInstance, NULL);

  hButton2 = CreateWindow("button", "Button 2",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 60, 90, 30, hwnd,

    (HMENU) IDB_Button2, hInstance, NULL);

  hButton3 = CreateWindow("button", "Button 3",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 100, 90, 30, hwnd,

    (HMENU) IDB_Button3, hInstance, NULL);

  hButton4 = CreateWindow("button", "Button 4",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 140, 90, 30, hwnd,

    (HMENU) IDB_Button4, hInstance, NULL);

  hButton5 = CreateWindow("button", "Button 5",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 180, 90, 30, hwnd,

    (HMENU) IDB_Button5, hInstance, NULL);

  // Увеличиваем горизонтальный размер

  // первой кнопки

  MoveWindow(hButton1, 20, 20, 180, 30, TRUE);

  // Изменяем надписи на остальных кнопках

  SetWindowText(hButton2, "PUSH");

  SetWindowText(hButton3, "POP");

  SetWindowText(hButton4, "OFF");

  SetWindowText(hButton5, "ON");

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    // Сообщение проиходит, когда вы нажимаете

    // на одну из двух созданных кнопок

    case WM_COMMAND:

    {

      // Если нажата первая кнопка, выводим

      // сообщение

      if(wParam == IDB_Button1)

      {

        MessageBox(hwnd, "Нажата кнопка Button 1",

          "Message WM_COMMAND",MB_OK);

      }

      // Если нажата вторая кнопка,

      // переводим первую кнопку в нажатое состояние

      else if(wParam == IDB_Button2)

      {

        SendMessage(hButton1, BM_SETSTATE, TRUE, 0L);

      }

      // Если нажата третья кнопка,

      // возвращаем первую кнопку в исходное состояние

      else if(wParam == IDB_Button3)

      {

        SendMessage(hButton1, BM_SETSTATE, FALSE, 0L);

      }

      // Если нажата четвертая кнопка,

      // переводим первую кнопку в неактивное состояние

      else if(wParam == IDB_Button4)

      {

        EnableWindow(hButton1, FALSE);

      }

      // Если нажата пятая кнопка,

      // переводим первую кнопку в активное состояние

      else if(wParam == IDB_Button5)

      {

        EnableWindow(hButton1, TRUE);

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Статические органы управления

 Этот тип органа управления ничем не управляет и используется

 для внешнего оформления диалоговых панелей и окон приложения.

 Задавая различные стили можно создать статические органы в виде

 закрашенных или незакрашенных прямоугольников или строк текста.

 Внутри диалоговых панелей статические органы могут

 использоваться для вывода пиктограмм.

 Для создания статического органа используют CreateWindow с

 первым параметром класса окна "static":

 HWND hStatic;

 hStatic= CreateWindow("static", NULL,

 WS_CHILD | WS_VISIBLE | SS_BLACKFRAME,

 20,40,100,50,

 hwnd, (HMENU)-1, hInstance, NULL );

 Второй параметр определяет текст внутри статического органа

 (NULL при отсутствии), третий - один из стилей

 SS_LEFT           текстовые стили

 SS_CENTER              "

 SS_RIGHT               "

 SS_SIMPLE              "

 SS_LEFTNOWORDWRAP      "

 SS_NOPREFIX       модификатор

 SS_ICON           используется для отображения пиктограмм

 SS_BLACKRECT      закрашенные прямоугольники

 SS_GRAYRECT              "

 SS_WHITERECT             "

 SS_BLACKFRAME      прямоугольные рамки без текста заголовка

 SS_GRAYFRAME             "

 SS_WHITEFRAME            "

 ПРИМЕР демонстрирует использование прямоугольника, рамки,

 текста со сверткой слов и без свертки. В качестве

 идентификатора органа управления выбрана -1, но можно бы и

 другие.

// ----------------------------------------

// Статические органы управления

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "StaticAppClass";

// Заголовок окна

char const szWindowTitle[] = "Static Control Demo";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Идентификаторы органов управления

  HWND hSt1, hSt2, hSt3, hSt4;

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

   // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем четыре статических

  // органа управления

  hSt1 = CreateWindow("static", NULL,

    WS_CHILD | WS_VISIBLE | SS_BLACKRECT,

    20, 20, 180, 20,

    hwnd, (HMENU)-1, hInstance, NULL);

  hSt2 = CreateWindow("static", NULL,

    WS_CHILD | WS_VISIBLE | SS_BLACKFRAME,

    20, 50, 180, 20,

    hwnd, (HMENU)-1, hInstance, NULL);

  hSt3 = CreateWindow("static", "",

    WS_CHILD | WS_VISIBLE | SS_LEFT,

    20, 80, 180, 40,

    hwnd, (HMENU)-1, hInstance, NULL);

  hSt4 = CreateWindow("static", "Simple Control",

    WS_CHILD | WS_VISIBLE | SS_SIMPLE,

    20, 130, 180, 40,

    hwnd, (HMENU)-1, hInstance, NULL);

  // Выводим текст в окно, имеющее стиль SS_LEFT.

  // Этот текст будет выведен в режиме свертки слов

  SetWindowText(hSt3, (LPSTR) "Этот текст будет выведен"

      " внутри окна в две строки");

  // Выводим текст в окно, имеющее стиль SS_SIMPLE.

  // Этот текст будет выведен на одной строке,

  // причем часть строки, которая выходит за границы

  // окна, будет обрезана

  SetWindowText(hSt4, (LPSTR) "Этот текст будет выведен"

      " внутри окна в одну строку и обрезан");

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Полосы скроллинга

 Горизонтальная и вертикальная полосы скроллинга  используются

 для просмотра текстов и изображений , не помещающихся в окнах.

 Создаются на базе предопределенного класса "scrollbar" и

 посылают в ф-цию родительского окна сообщения WM_HSCROLL и

 WM_VSCROLL; парметр wParam несет информацию о действии над

 полосой.

 Для создания полосы скроллинга с помощью ф-ции CreateWindow в

 первом параметре необходимо указать класс окна scrollbar:

 #define IDC_SCROLLBAR 1

 HWND hScroll;

 hScroll = CreateWindow("scrollbar", NULL,

 WS_CHILD | WS_VISIBLE | SBS_HORZ,

 20,40,100,50,

 hWnd,IDC_SCROLLBAR, hInstance, NULL);

 Третий параметр должен содержать один из следующих стилей

 полос:

 SBS_HORZ                     горизонтальная опред CreateWindow

 SBS_VERT                     вертикальная   опред CreateWindow

 SBS_TOPALIGN                 используется вместе с SBS_HORZ

 SBS_LEFTALIGN                используется вместе с SBS_VERT

 SBS_BOTTOMALIGN

 SBS_RIGHTALIGN

 SBS_SIZEBOXTOPLEFTALIGN

 SBS_SIZEBOXBOTTOMRIGHTALIGN

 SBS_SIZEBOX                 орган управления в виде маленького

                             прямоугольника

 Можно также определить наличие полос скроллинга при создании

 окна, указав при регистрации класса окна стиль окна WS_VSCROLL

 и/или WS_HSCROLL:

 hwnd= CreateWindow(szClassName, szWindowname,

 WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,

 CW_USERDEFAULT,CW_USERDEFAULT, CW_USERDEFAULT,

 CW_USERDEFAULT, 0,0, hInstance, NULL);

Если полоса просмотра создана первым способом как орган

управления, сообщения от нее будет получать ф-ция родительского

окна, а если вторым то в ф-цию окна, имеющего полосы просмотра.

Параметр wParam содержит код полосы просмотра, соответствующий

действию над ней:

SB_LINEUP         вверх на строку

SB_LINELEFT       влево

SB_LINEDOWN       вниз

SB_LINERIGHT      вправо

SB_PAGEUP         вверх на страницу

SB_PAGELEFT

SB_PAGEDOWN

SB_PAGERIGHT

SB_THUMBPOSITION  сдвиг в абсолютную позицию текущая - в мл сл

                  lParam

SB_THUMBTRACK     перемещение ползунка тек поз в мл слове

                  lParam

SB_TOP            сдвиг в начало

SB_LEFT

SB_BOTTOM         сдвиг в конец

SB_RIGHT

SB_ENDSCROLL      обычно игнорируется - отпускание кнопки мыши

Старшее слово lParam содержит идентификатор окна для полосы

просмотра (если временное окно имеет полосу просмотра, lParam

не используется). Константы SB_LEFT, SB_TOP, SB_RIGHT, SB_BOTTOM

используются только для подключения к полосе клавиатурного

интерфейса (полоса просмотра не посылает сообщений с wParam,

равным этим константам ). Для добавления клавиатурного

интерфейса обработчик WM_KEYDOWN может посылать в ф-цию окна

сообщения полосы скроллинга - такой подход позволяет

локализовать всю логику свертки в обработчике сообщений полосы.

Для полос определены понятия "текущая позиция " и "диапазон

изменения значений позиции". После создания полосы скроллинга ее

необходимо инициализировать ф-цией

void    WINAPI SetScrollRange(HWND hwnd, int fnBar, int nMin,

int nMax, BOOL fRedraw);

Здесь fnBar определяет тип полосы просмотра , для которой

выполняется установка диапазона изменения значений позиции:

 SB_HORZ   для полосы для окна со стилем WS_HSCROLL

 SB_VERT   для полосы для окна со стилем WS_VSCROLL

 SB_CTL    диапазон для полосы - органа класса scrollbar

 SB_BOTH

Параметры nMin nMax - задают границы диапазона, fRedraw

определяет необходимость перерисовки.

Узнать диапазон для полосы можно с помощью

void    WINAPI GetScrollRange(HWND, int, int FAR*, int FAR*);

Для установки ползунка в заданную позицию используйте

int     WINAPI SetScrollPos(HWND, int, int, BOOL);

Для определения текущей позиции

int     WINAPI GetScrollPos(HWND, int);

Для скрытия или показа полосы

void    WINAPI ShowScrollBar(HWND, int, BOOL);

Для разрешения или запрещения полос

BOOL    WINAPI EnableScrollBar(HWND, int, UINT);

при этом третий параметр определяет какие из кнопок полосы

должны быть заблокированы:

 ESB_ENABLE_BOTH   обе разблокированы

 ESB_DISABLE_BOTH  обе заблокированы

 ESB_DISABLE_LEFT   левая заблокирована

 ESB_DISABLE_RIGHT  правая

 ESB_DISABLE_UP     верхняя

 ESB_DISABLE_DOWN   нижняя

 ESB_DISABLE_LTUP

 ESB_DISABLE_LEFT

 ESB_DISABLE_RTDN

 ПРИМЕР 1 - прстейший вариант использования горизонтальной

 полосы для изменения ширины статического органа управления. В

 глобальной переменной nPosition хранится текущее положение

 ползунка - после создания туда занесено 100 - середина полосы.

 Для управления шириной статического органа с клавиатуры ф-ция

 главного окна обрабатывает сообщение WM_KEYDOWN с кодом

 виртуальной клавиши в wParam.

// ----------------------------------------

// Работа с полосой просмотра

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Идентификатор полосы просмотра

#define ID_SCROLL 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ScrollAppClass";

// Заголовок окна

char const szWindowTitle[] = "Scroll Demo";

// Текущая позиция полосы просмотра

int nPosition;

// Идентификатор окна полосы просмотра

HWND hScroll;

// Идентификатор статического органа

// управления

HWND hStatic;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем полосу просмотра

  hScroll = CreateWindow("scrollbar", NULL,

    WS_CHILD | WS_VISIBLE | SBS_HORZ,

    20, 60,

    200, 15,

    hwnd,

    (HMENU) ID_SCROLL,

    hInstance, NULL);

  // Устанавливаем текущую позицию

  nPosition = 100;

  // Устанавливаем минимальное и максимальное

  // значения для полосы просмотра

  SetScrollRange(hScroll, SB_CTL, 1, 200, TRUE);

  // Устанавливаем ползунок в середину

  // полосы просмотра

  SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

  // Создаем статический орган управления в виде

  // черного прямоугольника. Длина этого

  // прямоугольника будет определяеться текущей

  // позицией полосы просмотра

  hStatic = CreateWindow("static", NULL,

    WS_CHILD | WS_VISIBLE | SS_BLACKRECT,

    20, 40,

    nPosition, 15,

    hwnd, (HMENU) -1,hInstance, NULL);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    // Сообщение от горизонтальной полосы просмотра

    case WM_HSCROLL:

    {

      // В зависимости от параметра сообщения

      // изменяем текущую позицию

      switch (wParam)

      {

        case SB_PAGEDOWN:

        {

          nPosition += 10;

          break;

        }

        case SB_LINEDOWN:

        {

          nPosition += 1;

          break;

        }

        case SB_PAGEUP:

        {

          nPosition -= 10;

          break;

        }

        case SB_LINEUP:

        {

          nPosition -= 1;

          break;

        }

        case SB_TOP:

        {

          nPosition = 0;

          break;

        }

        case SB_BOTTOM:

        {

          nPosition = 200;

          break;

        }

        case SB_THUMBPOSITION:

        {

          nPosition = LOWORD (lParam);

          break;

        }

        case SB_THUMBTRACK:

        {

          nPosition = LOWORD (lParam);

          break;

        }

        default:

          break;

      }

      // Ограничиваем пределы изменения текущей

      // позиции значениями от 1 до 200

      if(nPosition > 200) nPosition = 200;

      if(nPosition < 1) nPosition = 1;

      // Устанавливаем ползунок полосы просмотра

      // в соответствии с новым значением

      // текущей позиции

      SetScrollPos(hScroll, SB_CTL, nPosition, TRUE);

      // Устанавливаем новый размер статического

      // органа управления

      MoveWindow(hStatic, 20, 40, nPosition, 15, TRUE);

      return 0;

    }

    // Обеспечиваем управление полосой просмотра

    // при помощи клавиатуры

    case WM_KEYDOWN:

    {

      // В зависимости от кода клавиши функция окна

      // посылает сама себе сообщения, которые

      // обычно генерируются полосой просмотра

      switch (wParam)

      {

        case VK_HOME:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_TOP, 0L);

          break;

        }

        case VK_END:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_BOTTOM, 0L);

          break;

        }

        case VK_LEFT:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, 0L);

          break;

        }

        case VK_RIGHT:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, 0L);

          break;

        }

        case VK_PRIOR:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0L);

          break;

        }

        case VK_NEXT:

        {

          SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L);

          break;

        }

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

ПРИМЕР 2 демонстрирует использование вертикальной полосы,

определенной в стиле окна, для свертки окна и выводит в окно

метрики текста.

Сообщение WM_VSCROLL, для которого wParam равно SB_THUMBTRACK,

поступает в окно при передвижении ползунка по полосе и мы его

игнорируем, чтобы не создавать задержки н6а перерисовку окна и

ограничиваемся обработкой SB_THUMBPOSITION, перерисовыая окно

только после окончания перемещения ползунка.

// ----------------------------------------

// Просмотр метрик шрифта

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <stdio.h>

#include <string.h>

#include <mem.h>

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char const szClassName[]   = "ScrlMetAppClass";

char const szWindowTitle[] = "SCRLMET Application";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  if(!InitApp(hInstance))

      return FALSE;

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW | WS_VSCROLL, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  if(!hwnd)

    return FALSE;

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Определяем стиль класса окна, при

  // использовании которого окно требует

  // перерисовки в том случае, если

  // изменилась его ширина или высота

  wc.style = CS_HREDRAW | CS_VREDRAW;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

void Print(HDC, int, char *);

static int cxChar, cyChar;

static int cxCurrentPosition;

static int cyCurrentPosition;

static int nScrollPos;

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  static WORD cxClient, cyClient;

  HDC hdc;              // индекс контекста устройства

  PAINTSTRUCT ps;       // структура для рисования

  static TEXTMETRIC tm; // структура для записи метрик

                        // шрифта

  switch (msg)

  {

    case WM_CREATE:

    {

      // Получаем контекст отображения,

      // необходимый для определения метрик шрифта

      hdc = GetDC(hwnd);

      // Заполняем структуру информацией

      // о метрике шрифта, выбранного в

      // контекст отображения

      GetTextMetrics(hdc, &tm);

      // Запоминаем значение ширины для

      // самого широкого символа

      cxChar = tm.tmMaxCharWidth;

      // Запоминаем значение высоты букв с

      // учетом межстрочного интервала

      cyChar = tm.tmHeight + tm.tmExternalLeading;

      // Инициализируем текущую позицию

      // вывода текста

      cxCurrentPosition = cxChar;

      cyCurrentPosition = 0;

      // Освобождаем контекст

      ReleaseDC(hwnd, hdc);

      // Начальное значение позиции

      nScrollPos = 0;

      // Задаем диапазон изменения значений

      SetScrollRange(hwnd, SB_VERT, 0, 20, FALSE);

      // Устанавливаем ползунок в начальную позицию

      SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);

      return 0;

    }

    // Определяем размеры внутренней области окна

    case WM_SIZE:

    {

      cxClient = LOWORD(lParam);

      cyClient = HIWORD(lParam);

      return 0;

    }

    // Сообщение от вертикальной полосы просмотра

    case WM_VSCROLL:

    {

      switch(wParam)

      {

        case SB_TOP:

        {

          nScrollPos = 0;

          break;

        }

        case SB_BOTTOM:

        {

          nScrollPos = 20;

          break;

        }

        case SB_LINEUP:

        {

          nScrollPos -= 1;

          break;

        }

        case SB_LINEDOWN:

        {

          nScrollPos += 1;

          break;

        }

        case SB_PAGEUP:

        {

          nScrollPos -= cyClient / cyChar;

          break;

        }

        case SB_PAGEDOWN:

        {

          nScrollPos += cyClient / cyChar;

          break;

        }

        case SB_THUMBPOSITION:

        {

          nScrollPos = LOWORD(lParam);

          break;

        }

        // Блокируем для того чтобы избежать

        // мерцания содержимого окна при

        // перемещении ползунка

        case SB_THUMBTRACK:

        {

          return 0;

        }

        default:

          break;

      }

      // Ограничиваем диапазон изменения значений

      if(nScrollPos > 20) nScrollPos = 20;

      if(nScrollPos < 0) nScrollPos = 0;

      // Устанавливаем ползунок в новое положение

      SetScrollPos(hwnd, SB_VERT, nScrollPos, TRUE);

      // Обновляем окно

      InvalidateRect(hwnd, NULL, TRUE);

      return 0;

    }

    case WM_PAINT:

    {

      // Инициализируем текущую позицию

      // вывода текста

      cxCurrentPosition = cxChar;

      cyCurrentPosition = 0;

      hdc = BeginPaint(hwnd, &ps);

      // Выводим параметры шрифта, полученные во

      // время создания окна при обработке

      // сообщения WM_CREATE

      Print(hdc, tm.tmHeight,          "tmHeight");

      Print(hdc, tm.tmAscent,          "tmAscent");

      Print(hdc, tm.tmDescent,         "tmDescent");

      Print(hdc, tm.tmInternalLeading, "tmInternalLeading");

      Print(hdc, tm.tmExternalLeading, "tmExternalLeading");

      Print(hdc, tm.tmAveCharWidth,    "tmAveCharWidth");

      Print(hdc, tm.tmMaxCharWidth,    "tmMaxCharWidth");

      Print(hdc, tm.tmWeight,          "tmWeight");

      Print(hdc, tm.tmItalic,          "tmItalic");

      Print(hdc, tm.tmUnderlined,      "tmUnderlined");

      Print(hdc, tm.tmStruckOut,       "tmStruckOut");

      Print(hdc, tm.tmFirstChar,       "tmFirstChar");

      Print(hdc, tm.tmLastChar,        "tmLastChar");

      Print(hdc, tm.tmDefaultChar,     "tmDefaultChar");

      Print(hdc, tm.tmBreakChar,       "tmBreakChar");

      Print(hdc, tm.tmPitchAndFamily,  "tmPitchAndFamily");

      Print(hdc, tm.tmCharSet,         "tmCharSet");

      Print(hdc, tm.tmOverhang,        "tmOverhang");

      Print(hdc, tm.tmDigitizedAspectX,"tmDigitizedAspectX");

      Print(hdc, tm.tmDigitizedAspectY,"tmDigitizedAspectY");

      EndPaint(hwnd, &ps);

      return 0;

    }

    // Обеспечиваем управление полосой просмотра

    // при помощи клавиатуры

    case WM_KEYDOWN:

    {

      // В зависимости от кода клавиши функция окна

      // посылает сама себе сообщения, которые

      // обычно генерируются полосой просмотра

      switch (wParam)

      {

        case VK_HOME:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L);

          break;

        }

        case VK_END:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L);

          break;

        }

        case VK_UP:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L);

          break;

        }

        case VK_DOWN:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L);

          break;

        }

        case VK_PRIOR:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L);

          break;

        }

        case VK_NEXT:

        {

          SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L);

          break;

        }

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// ==========================================

// Функция для вывода параметров шрифта

// в окно

// ==========================================

void Print(HDC hdc, int tmValue, char *str)

{

  char buf[80];

  int i, y;

  // Вычисляем начальную позицию для вывода

  y = cyCurrentPosition + cyChar * (1 - nScrollPos);

  // Подготавливаем в рабочем буфере

  // и выводим в окно начиная с текущей

  // позиции название параметра

  sprintf(buf, "%s", str);

  i = strlen(str);

  TextOut(hdc,

    cxCurrentPosition, y, buf, i);

  // Подготавливаем в рабочем буфере

  // и выводим в текущей строке окна

  // со смещением значение параметра

  sprintf(buf, "= %d", tmValue);

  i = strlen(buf);

  TextOut(hdc,

    cxCurrentPosition + 12 * cxChar, y, buf, i);

  // Увеличиваем текущую позицию по

  // вертикали на высоту символа

  cyCurrentPosition += cyChar;

}

Пример 3 создает полосу со стилем SBS_SIZEBOX в виде маленького

квадратика, позволяющего изменять размеры окна, не имеющего ни

рамки , ни кнопок минимизации и максимизации.

// ----------------------------------------

// Работа с полосой просмотра,

// имеющей стиль SBS_SIZEBOX

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

// Идентификатор полосы просмотра

#define ID_SCROLL 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ScrollSizeBoxAppClass";

// Заголовок окна

char const szWindowTitle[] = "SizeBox Demo";

// Идентификатор окна полосы просмотра

HWND hScroll;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    // стиль окна

    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,

    CW_USEDEFAULT,       // задаем расположение

    CW_USEDEFAULT,       // окна, принятое по умолчанию

    200, 200,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем полосу просмотра

  hScroll = CreateWindow("scrollbar", NULL,

    WS_CHILD | WS_VISIBLE |

    SBS_SIZEBOX | SBS_SIZEBOXTOPLEFTALIGN,

    30, 30, 0, 0,

    hwnd, (HMENU) ID_SCROLL, hInstance, NULL);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Текстовый редактор

 Для создания текстового редактора, обрабатывающего сообщения от

 клоавиатуры с управлением текстовым курсором, учетом ширины

 букв в пропорциональном шрифте, выполнением прокрутки текста в

 окне редактирования по горизонтали и вертикали, выделением

 фрагментов текста, работой с буфером обмена Clipboard и т п

 следует вызвать ф-цию CreateWindow , передав ей в качестве

 первого параметра указатель на строку "edit":

 hwndEdit = CreateWindow("edit", NULL,

 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,

 30, 30, 300, 30,

 hwnd, (HMENU)ID_EDIT, hInst, NULL);

 Заголовок окна не используется, поэтому 2-й параметр NULL,

 стиль WS_BORDER в 3-м параметре обеспечивает выделение рамкой

 области редактирования, специализированные стили редактора

 начинаются с ES_:

 ES_LEFT         выравнивание слева

 ES_CENTER       ценрирование по горизонтали

 ES_RIGHT        выравнивание по правой границе

 ES_MULTILINE    многострочный редактор

 ES_UPPERCASE    с автопреобразов в заглавные буквы

 ES_LOWERCASE           "         в строчные   "

 ES_PASSWORD     для ввода паролей

 ES_AUTOVSCROLL  со скроллингом по вертикали

 ES_AUTOHSCROLL         "       по горизонтали

 ES_NOHIDESEL    с инверсией выдел фрагмета при потере фокуса

 ES_OEMCONVERT   с автоперекодированием в ОЕМ и обратно

 ES_READONLY     только для просмотра

 ES_WANTRETURN   с использов клавиши Enter как кнопки ОК

 Для создания полос скроллинга в стиле редактора достаточно

 указать WS_HSCROLL, WS_VSCROLL

 текстовый редактор посылает в родительское оено сообщение

 WM_COMMAND с wParam = идентификатору редактора, младшее слово

 lParam = коду извещения из следующего списка:

  EN_SETFOCUS   получен фокус ввода

  EN_KILLFOCUS  потерян фокус ввода

  EN_CHANGE     изменен текст

  EN_UPDATE     предстоит изменение текста

  EN_ERRSPACE   ошибка выделения дополнит памяти

  EN_MAXTEXT    перполнение размера текста

  EN_HSCROLL    выполнена прокрутка текста

  EN_VSCROLL      "         "         "

  С помощью ф-ции SendMessage можно посылать редактору сообщения

  с именами начинающимися с EM_:

  EM_GETSEL - получить позицию 1-го и последнего символов в

  выделенном фрагменте wParam= 0 lParam= 0L , возвращается

  положение 1-го символа в младшем слове и первого за выделенным

  фрагментом в старшем слове двойного слова

  EM_SETSEL - выделение фрагмента wParam=(WPARAM)(UINT)fscroll

  lParam= MAKELPARAM(нач поз, конечн поз) ; если нач поз =0 а

  конечн поз =-1 выделяется весь текст, если нач поз = -1

  выделение снимается

  EM_GETRECT - определение координат области редактирования

  wParam= 0 lParam=    (LPARAM)(RECT FAR*)lprc

  EM_SETRECT- изменение размеров и положение области

  редактирования внутри органа управления

  EM_SETRECTNP - то же что предыдущ но без перерисовки окна

  EM_LINESCROLL - скроллинг заданного количества строк

  wParam= 0 lParam=     MAKELPARAM(колич по вертб симв по гориз)

  EM_GETMODIFY - получить флаг обновления wParam= 0 lParam=0L

  EM_SETMODIFY -установить флаг обновления

  wParam= (WPARAM)(UINT)fmod lParam=0L

  EM_GETLINECOUNT - получить количество строк в тексте

   wParam=0  lparam=0L

  EM_LINEINDEX - смещение начала заданной строки от начала

  текста  wParam=(WPARAM)nLine  lparam=0L

  EM_SETHANDLE - назначение буфера для хранения текста

   wParam=(WPARAM)(HLOCAL)hLoc lParam=0L

  hLoc - ид-р буфера, получаемый от ф-ции LocalAlloc

  EM_GETHANDLE - получить ид-р локальной памяти хранящей текст

  EM_LINELENGTH - размер строки в байтах

  wParam=(WPARAM)iChar lParam=0L

  iChar - номер поз символа в строке

  EM_REPLACESEL - заменить выделенный фрагмент

  wParam=0 lParam=(LPARAM)(LPCSTR)lpszstr

  EM_SETFONT-

  EM_GETLINE - копировать строку в буфер

  wParam=(WPARAM)nLine lParam=(LPARAM)(LPSTR)lpCh

  EM_LIMITTEXT- лимит на колич вводимых символов

  wParam=(WPARAM)cCmax  lParam=0L

  EM_CANUNDO -проверка поддержки отката

  EM_UNDO - отмена последней операции

  EM_FMTLINES -добавлять или удалять символы конца строки при

  переносе wParam=(WPARAM)(BOOL)fAddEOL lParam=0L

  EM_LINEFROMCHAR- определение номера строки с символом в

  заданной позиции

  wParam=(WPARAM)ichar  lParam=0L

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

  пароля

  wParam = (WPARAM)(UINT)chChar lParam=0L

  EM_EMPTYUNDOBUFFER  - сброс содержимого буфера отката

  EM_GETFIRSTVISIBLELINE - получить номер верхней видимой строки

  EM_SETREADONLY - установка или сброс флага "только просмотр"

  wParam=(WPARAM)(UINT)fRadOnly

  EM_GETPASSWORDCHAR -получить символ для ввода пароля

  ПРИМЕР 1 создает в главном окне однострочный редактор и кнопку

  ОК. Когда главное окно получает фокус ввода, оно получает

  сообщение WM_SETFOCUS и по нему передает фокус ввода редактору

  текста:

  case WM_SETFOCUS:

  {SetFocus(hEdit); return 0;}

  Сообщения WM_COMMAND могут приходить либо от текстового

редактора, либо от кнопки и отличаются содержанием wParam - там

либо ид-р ID_EDIT - тогда из старшего слова lParam извлекается

извещение (обрабатывается только EN_ERRSPACE - мало памяти)

 либо ID_BUTTON - тогда обработчик читает строку редактора в

 спецбуфер и выводит ее на экран с помощью панели сообщений

 В первое слово спецбуфера записывается его размер:

 *(WORD *) chBuff= sizeof(chBuff)-1;

Затем редактору посылается сообщение EM_GETLINE:

cbText=SendMessage(hEdit, EM_GETLINE, 0, (LPARAM)(LPSTR)chBuff);

Ф-ция возвращает количество скопированных в буфер символов, не

закрывая буфер нулем; обработчик сообщения закрывает буфер нулем

и выводит в окно приглашение к вводу текста:

TextOut(hdc, 30, 10, "введите строку и нажмите кнопку");

Вот и все особенности.

 //Редактор

текста

#define STRICT

#include <windows.h>

#include <mem.h>

// Идентификатор редактора текста

#define ID_EDIT   1

// Идентификатор кнопки

#define ID_BUTTON 2

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "EditAppClass";

// Заголовок окна

char const szWindowTitle[] = "Edit Demo";

// Идентификатор копии приложения

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    // Создаем символьные сообщения

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Идентификатор редкатора текста

  static HWND hEdit;

  // Идентификатор кнопки

  static HWND hButton;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем редактор текста

      hEdit = CreateWindow("edit", NULL,

         WS_CHILD | WS_VISIBLE | WS_BORDER |

         ES_LEFT,

         30, 30, 300, 30,

         hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Создаем кнопку

      hButton = CreateWindow("button", "OK",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         30, 80, 100, 30,

         hwnd, (HMENU) ID_BUTTON, hInst, NULL);

      return 0;

    }

    // Когда главное окно приложения получает

    // фокус ввода, отдаем фокус редактору текста

    case WM_SETFOCUS:

    {

      SetFocus(hEdit);

      return 0;

    }

    case WM_COMMAND:

    {

      // Обработка извещения текстового редактора

      // об ошибке

      if(wParam == ID_EDIT)

      {

        if(HIWORD(lParam) == EN_ERRSPACE)

        {

          MessageBox(hwnd, "Мало памяти",

           szWindowTitle, MB_OK);

        }

      }

      // Сообщение от кнопки

      else if(wParam == ID_BUTTON)

      {

         BYTE chBuff[80];

         WORD cbText;

         // Записываем в первое слово буфера

         // значение размера буфера в байтах

         * (WORD *) chBuff = sizeof (chBuff) - 1;

         // Получаем от редактора текста содержимое

         // первой строки. Функция возвращает количество

         // байт, скопированных в буфер

         cbText = SendMessage(hEdit, EM_GETLINE, 0,

           (LPARAM)(LPSTR)chBuff);

         // Закрываем буфер двоичным нулем

         chBuff[cbText] = '\0';

         // Выводим содержимое буфера на экран

         MessageBox(hwnd, chBuff,

           szWindowTitle, MB_OK);

      }

      return 0;

    }

    case WM_PAINT:

    {

      HDC hdc;

      PAINTSTRUCT ps;

      // Получаем индекс контекста устройства

      hdc = BeginPaint(hwnd, &ps);

      // Выводим текстовую строку

      TextOut(hdc, 30, 10,

        "Введите строку и нажмите кнопку 'OK'", 36);

      // Отдаем индекс контекста устройства

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

ПРИМЕР 2 создает текстовый редактор аналогичный NotePad, но без

меню - он может создавать новые файлы и редактировать старые.

В верхней части главного окна создются 4 кнопки:

-New для создания нового текста

- Open для загрузки нового файла в редактор

-Save для сохранения текста в файле

-Exit для завершения работы программы

Несмотря на достаточно емкий набор выполняемых функций

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

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

// ----------------------------------------

// Редактор текстовых файлов

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <commdlg.h>

#include <mem.h>

#include <string.h>

#include <stdlib.h>

// Идентификатор редактора текста

#define ID_EDIT   1

// Идентификаторы кнопок

#define ID_NEW    2

#define ID_OPEN   3

#define ID_SAVE   4

#define ID_EXIT   5

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

HFILE OpenFile(void);

HFILE OpenSaveFile(void);

// Имя класса окна

char const szClassName[]   = "TEditAppClass";

// Заголовок окна

char const szWindowTitle[] = "Text Editor";

// Идентификатор копии приложения

HINSTANCE hInst;

// Флаг изменений в тексте

BOOL bUpdate;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Идентификатор редкатора текста

  static HWND hEdit;

  // Идентификаторы кнопок

  static HWND hButtNew;

  static HWND hButtOpen;

  static HWND hButtSave;

  static HWND hButtExit;

  // Идентификаторы файлов

  static HFILE hfSrcFile, hfDstFile;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем редактор текста

      hEdit = CreateWindow("edit", NULL,

         WS_CHILD | WS_VISIBLE | WS_BORDER |

         WS_HSCROLL | WS_VSCROLL |

         ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |

         ES_MULTILINE,

         0, 0, 0, 0,

         hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Устанавливаем максимальную длину

      // редактируемого текста, равную 32000 байт

      SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

      // Сбрасываем флаг обновления текста

      bUpdate = FALSE;

      // Создаем кнопки

      hButtNew = CreateWindow("button", "New",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         0, 0, 50, 20,

         hwnd, (HMENU) ID_NEW, hInst, NULL);

      hButtOpen = CreateWindow("button", "Open",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         50, 0, 50, 20,

         hwnd, (HMENU) ID_OPEN, hInst, NULL);

      hButtSave = CreateWindow("button", "Save",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         100, 0, 50, 20,

         hwnd, (HMENU) ID_SAVE, hInst, NULL);

      hButtExit = CreateWindow("button", "Exit",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         150, 0, 50, 20,

         hwnd, (HMENU) ID_EXIT, hInst, NULL);

      return 0;

    }

    case WM_SIZE:

    {

      // Устанавливаем размер органа управления

      // (текстового редактора) в соотвестствии

      // с размерами главного окна приложения

      MoveWindow(hEdit, 0, 20, LOWORD(lParam),

        HIWORD(lParam) - 20, TRUE);

      return 0;

    }

    // Когда главное окно приложения получает

    // фокус ввода, отдаем фокус редактору текста

    case WM_SETFOCUS:

    {

      SetFocus(hEdit);

      return 0;

    }

    case WM_COMMAND:

    {

      // Обработка извещений текстового редактора

      if(wParam == ID_EDIT)

      {

        // Ошибка

        if(HIWORD(lParam) == EN_ERRSPACE)

        {

           MessageBox(hwnd, "Мало памяти",

             szWindowTitle, MB_OK);

        }

        // Произошло изменение в редактируемом

        // тексте

        else if(HIWORD(lParam) == EN_UPDATE)

        {

           // Устанавливаем флаг обновления текста

           bUpdate = TRUE;

        }

        return 0;

      }

      // Нажата кнопка сохранения текста

      else if(wParam == ID_SAVE)

      {

        WORD wSize;

        HANDLE hTxtBuf;

        NPSTR  npTextBuffer;

        // Открываем выходной файл

        hfDstFile = OpenSaveFile();

        if(!hfDstFile) return 0;

        // Определяем размер текста

        wSize = GetWindowTextLength(hEdit);

        // Получаем идентификатор блока памяти,

        // в котором находится редактируемый текст

        hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

        // Фиксируем блок памяти и получаем указатель

        // на него

        npTextBuffer = (NPSTR)LocalLock(hTxtBuf);

        // Записываем содержимое блока памяти в файл

        if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize))

        {

          // При ошибке закрываем файл и выдаем сообщение

          _lclose(hfDstFile);

          MessageBox(hwnd, "Ошибка при записи файла",

             szWindowTitle, MB_OK);

          return 0;

        }

        // Закрываем файл

        _lclose(hfDstFile);

        // Расфиксируем блок памяти

        LocalUnlock(hTxtBuf);

        // Так как файл был только что сохранен,

        // сбрасываем флаг обновления

        bUpdate = FALSE;

        return 0;

      }

      // Создание нового файла

      else if(wParam == ID_NEW)

      {

        // Проверяем флаг обновления

        if(bUpdate)

        {

          if(IDYES == MessageBox(hwnd,

           "Файл был изменен. Желаете сохранить?",

           szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             return 0;

        }

        // Сбрасываем содержимое текстового редактора

        SetWindowText(hEdit, "\0");

        // Сбрасываем флаг обновления

        bUpdate = FALSE;

        return 0;

      }

      // Загорузка файла для редактирования

      else if(wParam == ID_OPEN)

      {

        LPSTR lpTextBuffer;

        DWORD dwFileSize, dwCurrentPos;

        // Проверяем флаг обновления

        if(bUpdate)

        {

          if(IDYES == MessageBox(hwnd,

           "Файл был изменен. Желаете сохранить?",

           szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             return 0;

        }

        // Открываем входной файл.

        hfSrcFile = OpenFile();

        if(!hfSrcFile) return 0;

        // Определяем размер файла

        dwCurrentPos = _llseek(hfSrcFile, 0L, 1);

        dwFileSize   = _llseek(hfSrcFile, 0L, 2);

        _llseek(hfSrcFile, dwCurrentPos, 0);

        // Размер файла не должен превосходить 32000 байт

        if(dwFileSize >= 32000)

        {

          _lclose(hfSrcFile);

          MessageBox(hwnd, "Размер файла больше 32000 байт",

             szWindowTitle, MB_OK);

          return 0;

        }

        // Заказываем память для загрузки файла

        lpTextBuffer = (LPSTR)malloc(32000);

        if(lpTextBuffer == NULL)

           return 0;

        // Загружаем текст из файла в буфер

        _lread(hfSrcFile, lpTextBuffer, dwFileSize);

        // Закрываем буфер двоичным нулем

        lpTextBuffer[(WORD)dwFileSize] = '\0';

        // Закрываем файл

        _lclose(hfSrcFile);

        // Переносим содержимое буфера в

        // текстовый редактор

        SetWindowText(hEdit, lpTextBuffer);

        // Освобождаем буфер

        free((void *)lpTextBuffer);

        // сбрасываем флаг обновления

        bUpdate = FALSE;

      }

      else if(wParam == ID_EXIT)

      {

        // Проверяем флаг обновления

        if(bUpdate)

        {

          if(IDYES == MessageBox(hwnd,

           "Файл был изменен. Желаете сохранить?",

           szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             return 0;

        }

        // Посылаем в функцию главного окна

        // сообщение WM_CLOSE

        SendMessage(hwnd, WM_CLOSE, 0, 0L);

        return 0;

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// -------------------------------

// Функция OpenFile

// Сохранение файла

// -------------------------------

HFILE OpenFile(void)

{

  // Структура для выбора файла

  OPENFILENAME ofn;

  // Буфер для записи пути к выбранному файлу

  char szFile[256];

  // Буфер для записи имени выбранного файла

  char szFileTitle[256];

  // Фильтр расширений имени файлов

  char szFilter[256] =

         "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";

  // Идентификатор открываемого файла

  HFILE hf;

  // Инициализация имени выбираемого файла

  // не нужна, поэтому создаем пустую строку

  szFile[0] = '\0';

  // Записываем нулевые значения во все поля

  // структуры, которая будет использована для

  // выбора файла

  memset(&ofn, 0, sizeof(OPENFILENAME));

  // Инициализируем нужные нам поля

  // Размер структуры

  ofn.lStructSize       = sizeof(OPENFILENAME);

  // Идентификатор окна

  ofn.hwndOwner         = NULL;

  // Адрес строки фильтра

  ofn.lpstrFilter       = szFilter;

  // Номер позиции выбора

  ofn.nFilterIndex      = 1;

  // Адрес буфера для записи пути

  // выбранного файла

  ofn.lpstrFile         = szFile;

  // Размер буфера для записи пути

  // выбранного файла

  ofn.nMaxFile          = sizeof(szFile);

  // Адрес буфера для записи имени

  // выбранного файла

  ofn.lpstrFileTitle    = szFileTitle;

  // Размер буфера для записи имени

  // выбранного файла

  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  // В качестве начального каталога для

  // поиска выбираем текущий каталог

  ofn.lpstrInitialDir   = NULL;

  // Определяем режимы выбора файла

  ofn.Flags =   OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST

                                  | OFN_HIDEREADONLY;

  // Выбираем входной файл

  if (GetOpenFileName(&ofn)) {

    // Открываем выбранный файл

    hf = _lopen(ofn.lpstrFile, OF_READ);

    // Возвращаем идентификатор файла

    return hf;

  }

  // При отказе от выбора возвращаем

  // нулевое значение

  else return 0;

}

// -------------------------------

// Функция OpenSaveFile

// Выбор файла для редактирования

// -------------------------------

HFILE OpenSaveFile(void)

{

  OPENFILENAME ofn;

  char szFile[256];

  char szFileTitle[256];

  char szFilter[256] =

         "Text Files\0*.txt\0Any Files\0*.*\0";

  HFILE hf;

  szFile[0] = '\0';

  memset(&ofn, 0, sizeof(OPENFILENAME));

  ofn.lStructSize       = sizeof(OPENFILENAME);

  ofn.hwndOwner         = NULL;

  ofn.lpstrFilter       = szFilter;

  ofn.nFilterIndex      = 1;

  ofn.lpstrFile         = szFile;

  ofn.nMaxFile          = sizeof(szFile);

  ofn.lpstrFileTitle    = szFileTitle;

  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  ofn.lpstrInitialDir   = NULL;

  ofn.Flags             = OFN_HIDEREADONLY;

  // Выбираем выходной файл

  if (GetSaveFileName(&ofn)) {

    // При необходимости создаем файл

    hf = _lcreat(ofn.lpstrFile, 0);

    return hf;

  }

  else return 0;

}


Списки класса LISTBOX

 С помощью этого класса можно создавать одноколоночные и много

 колоночные списки с полосами скроллинга.

 Для создания списка вызывается все та же ф-ция CreateWindow с

 указателем на строку "listbox" в качестве первого параметра.

 hListBox=CreateWindow("listbox", NULL,

 WS_CHILD |WS_VISIBLE | LBS_STANDARD | LBS_WANTKEYBOARDINPUT,

 30.30,200,100,

 hwnd, (HMENU) ID_LIST, hInst, NULL );

 Дополнительные списковые стили имеют префикс LBS_:

 LBS_NOTIFY     при двойном щелчке родит окно получит извещение

 LBS_SORT       с сортировкой строк

 LBS_NOREDRAW   не вып перерисовка при доб или удал строк

 LBS_MULTIPLESEL   с выделением многих строк

 LBS_OWNERDRAWFIXED рисуется род окном с одинаковой высотой эл

 LBS_OWNERDRAWVARIABLE то же но с перем высот элементов

 LBS_HASSTRINGS    список из строк - исп для всех списков кроме

                   рисуемых родителем

 LBS_USETABSTOPS   с преобразовнием символов табуляции при

                    выводе строк списка

 LBS_NOINTEGRALHEIGHT допустимо частичное отображение строк

                      по высоте

 LBS_MULTICOLUMN   многоколоночный

 LBS_WANTKEYBOARDINPUT с получением род окном WM_VKEYTOITEM

                       (при стиле LBSHASSTRING) или WM_CHATOITEM

                       при работе с клавиатурой

 LBS_EXTENDEDSEL     с выделением рядом располож строк

 LBS_DISABLENOSCROLL с неисчезающей вертик полосой скроллинга

 LBS_STANDARD          (LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER)

 Если список создан со стилем LBS_NOTIFY он посылает родителю

 WM_COMMAND с идентификатором списка в wParam , идентификатором

 окна списка в младшем слове lParam и кодом извещения в старшем

 слове lParam из следующего набора:

 LBN_ERRSPACE   ошибка выделения памяти

 LBN_SELCHANGE  выбрана другая строка

 LBN_DBLCLK     двойной щелчок по строке

 LBN_SELCANCEL  отмена выбора в списке

 LBN_SETFOCUS   получение фокуса ввода

 LBN_KILLFOCUS  потеря фокуса ввода

 Для управления списком программа посылает ему сообщения c

 именами с префиксом LB_ ф-цией SendMessage из предопределенного

 набора:

 LB_ADDSTRING- добавление строки wParam=0

 lParam=(LPARAM)(LPCSTR)lpszStr

 LB_DELETESTRING  - удаление строки с заданным wParam номером

 LB_DIR  - заполнение списка именами файлов и каталогов с

   указателем на строку с именем или

 шаблоном в lParam  и аттрибутом в wParam , составляемым из

 набора флагов:

 DDL_READWRITE

 DDL_READONLY

 DDL_HIDDEN

 DDL_SYSTEM

 DDL_DIRECTORY

 DDL_ARCHIVE

 DDL_POSTMSGS

 DDL_DRIVES

 DDL_EXCLUSIVE

 LB_FINDSTRING  -поиск строки с заданным префиксом с номером

 начальной для поиска строки в wParam и адресом префикса в

 lParam

 LB_FINDSTRINGEXACT   - поиск строки в списке

 LB_GETCARETINDEX  - определение номера строки с фокусом ввода

 параметры - нулевые

 LB_GETCOUNT  - определение количества строк

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

 LB_GETHORIZONTALEXTENT  - определение ширины скроллингуемой

 области

 LB_GETITEMDATA - получение 32-битового числа, соответствующего

 строке, номер которой в wParam

 LB_GETITEMHEIGHT  - определение высоты строки с номером

 заданным в wParam

 LB_GETITEMRECT  -определение координат внутренней области окна,

 соответствующей строке с номером в wParam и записью результата

 в структуру RECT , указатель на которую в lParam

 LB_GETSEL  -выбрана ли заданная wParam строка

 LB_GETSELCOUNT - количество выбранных строк

 LB_GETSELITEMS  -заполнение буфера с указателем в lParam

 идентификаторами выбранных строк максимальным количеством в

 wParam

 LB_GETTEXT  - копирование строки с номером в wParam в буфер с

 адресом в lParam

 LB_GETTEXTLEN  - определение длины строки с номером в wParam

 LB_GETTOPINDEX  -определение номера перво отображаемой строки

 LB_INSERTSTRING - всттавка элемента из буфера с адресом в

 lParam в позицию с номером в wparam

 LB_RESETCONTENT -удаление всех строк из списка

 LB_SELECTSTRING  - поиск и выбор строки, начинающейся по

 образцу с адресом в lParam начало поиска со строки с номером в

 wParam

 LB_SELITEMRANGE -выделение одной или нескольких рядом

 расположенных строк wParam = (WPARAM)(BOOL)fSelect

 lParam= MAKELPARAM(первая ,  последняя)

 LB_SETCARETINDEX  - передача фокуса ввода строке с номером в

 wParam lParam= MAKELPARAM(fScroll,0)  fScroll типа BOOL

 LB_SETCOLUMNWIDTH  -установка ширины колонки по wParam

 LB_SETCURSEL  - выбор строки по wParam

 LB_SETHORIZONTALEXTENT  - установка ширины скроллинга по

 горизонтали чкрез wParam

 LB_SETITEMDATA  - установка двойного lParam слова связанного со

 строкой номер wParam

ПРИМЕР 1 создает простейший одноколоночный список, в него

жобавляется несколько текстовых строк, выделенная строка

отображается справа от списка, выбор работает от мыши и Enter.

//----------------------------------------

// Использование органа управления

//класса "listbox"

----------------------------------------

#define STRICT #include <windows.h>

#include <mem.h>

// Идентификатор списка

#define ID_LIST   1

// Идентификатор кнопки

#define ID_BUTTON 2

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ListBoxAppClass";

// Заголовок окна

char const szWindowTitle[] = "ListBox Demo";

// Идентификатор копии приложения

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Идентификатор органа управления "listbox"

  static HWND hListBox;

  // Идентификатор кнопки

  static HWND hButton;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем список

      hListBox = CreateWindow("listbox", NULL,

         WS_CHILD | WS_VISIBLE | LBS_STANDARD |

         LBS_WANTKEYBOARDINPUT,

         30, 30, 200, 100,

         hwnd, (HMENU) ID_LIST, hInst, NULL);

      // Отменяем режим перерисовки списка

      SendMessage(hListBox, WM_SETREDRAW, FALSE, 0L);

      // Добавляем в список несколько строк

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Зеленый");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Красный");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Розовый");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Пурпурный");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Синий");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Желтый");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Фиолетовый");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Черный");

      SendMessage(hListBox, LB_ADDSTRING, 0,

        (LPARAM)(LPSTR)"Белый");

      // Включаем режим перерисовки списка

      SendMessage(hListBox, WM_SETREDRAW, TRUE, 0L);

      // Перерисовываем список

      InvalidateRect(hListBox, NULL, TRUE);

      // Создаем кнопку

      hButton = CreateWindow("button", "OK",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         250, 30, 50, 20,

         hwnd, (HMENU) ID_BUTTON, hInst, NULL);

      return 0;

    }

    // Когда главное окно приложения получает

    // фокус ввода, отдаем фокус списку

    case WM_SETFOCUS:

    {

      SetFocus(hListBox);

      return 0;

    }

    case WM_COMMAND:

    {

      // Обработка извещения списка об ошибке

      if(wParam == ID_LIST)

      {

        // Преобразуем к типу unsigned, так как

        // константа LBN_ERRSPACE определена как

        // отрицательное число

        if(HIWORD(lParam) == (unsigned)LBN_ERRSPACE)

        {

          MessageBox(hwnd, "Мало памяти",

           szWindowTitle, MB_OK);

        }

        // Если пользователь сделал двойной щелчок

        // по строке списка, выводим эту строку

        // на экран

        else if(HIWORD(lParam) == LBN_DBLCLK)

        {

          int uSelectedItem;

          char Buffer[256];

          // Определяем номер выделенной строки

          uSelectedItem = (int)SendMessage(hListBox,

             LB_GETCURSEL, 0, 0L);

          // Если в списке есть выделенная строка,

          // выводим ее на экран

          if(uSelectedItem != LB_ERR)

          {

             // Получаем выделенную строку

             SendMessage(hListBox, LB_GETTEXT,

               uSelectedItem, (LPARAM)Buffer);

             // Выводим ее на экран

             MessageBox(hwnd, (LPSTR)Buffer, szWindowTitle,

               MB_OK);

          }

        }

        // Если пользователь изменил выделенную

        // строку, выводим в окно новую

        // выделенную строку

        else if(HIWORD(lParam) == LBN_SELCHANGE)

        {

          int uSelectedItem, nSize;

          char Buffer[256];

          HDC hdc;

          // Определяем номер новой выделенной строки

          uSelectedItem = (int)SendMessage(hListBox,

             LB_GETCURSEL, 0, 0L);

          if(uSelectedItem != LB_ERR)

          {

             // Получаем строку

             SendMessage(hListBox, LB_GETTEXT,

               uSelectedItem, (LPARAM)Buffer);

             // Получаем контекст отображения

             hdc = GetDC(hwnd);

             // Определяем длину строки

             nSize = lstrlen(Buffer);

             // Очищаем место для вывода строки

             TextOut(hdc, 250, 60,

             (LPSTR)"                         ", 25);

             // Выводим строку

             TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

             // Освобождаем контекст отображения

             ReleaseDC(hwnd, hdc);

          }

        }

      }

      // Сообщение от кнопки

      else if(wParam == ID_BUTTON)

      {

        int uSelectedItem;

        char Buffer[256];

        // Определяем номер выделенной строки,

        // получаем текст строки и выводим

        // этот текст в окно

        uSelectedItem = (int)SendMessage(hListBox,

           LB_GETCURSEL, 0, 0L);

        if(uSelectedItem != LB_ERR)

        {

           SendMessage(hListBox, LB_GETTEXT,

             uSelectedItem, (LPARAM)Buffer);

           MessageBox(hwnd, (LPSTR)Buffer, szWindowTitle,

             MB_OK);

        }

      }

      return 0;

    }

    // Это сообщение поступает от списка, если он

    // имеет фокус ввода и пользователь работает

    // с клавиатурой

    case WM_VKEYTOITEM:

    {

      // Если пользователь нажал клавишу <Enter>,

      // посылаем в окно сообщение WM_COMMAND с

      // параметром wParam, равным идентификатору

      // кнопки

      if(wParam == VK_RETURN)

      {

        SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);

      }

      return -1;

    }

    case WM_PAINT:

    {

      HDC hdc;

      PAINTSTRUCT ps;

      hdc = BeginPaint(hwnd, &ps);

      TextOut(hdc, 30, 10,

        "Выберите цвет и нажмите кнопку 'OK'", 35);

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

ПРИМЕР 2 использует список для выбора файла из отображаемых в

списке с возможностью навигации по системе каталогов и дисков.

// ----------------------------------------

// Использование органа управления

// класса "listbox" для просмотра

// содержимого каталога

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#include <dir.h>

// Идентификатор списка

#define ID_LIST   1

// Идентификатор кнопки

#define ID_BUTTON 2

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ListDirAppClass";

// Заголовок окна

char const szWindowTitle[] = "Выбор файла";

// Идентификатор копии приложения

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Идентификатор органа управления "listbox"

  static HWND hListBox;

  // Идентификатор кнопки

  static HWND hButton;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем список

      hListBox = CreateWindow("listbox", NULL,

         WS_CHILD | WS_VISIBLE | LBS_STANDARD |

         LBS_WANTKEYBOARDINPUT,

         30, 30, 200, 200,

         hwnd, (HMENU) ID_LIST, hInst, NULL);

      // Добавляем в список содержимое текущего

      // каталога

      SendMessage(hListBox, LB_DIR,

        DDL_READWRITE | DDL_READONLY | DDL_HIDDEN |

        DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES |

        DDL_ARCHIVE,

        (LPARAM)(LPSTR)"*.*");

      // Создаем кнопку

      hButton = CreateWindow("button", "OK",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         250, 30, 50, 20,

         hwnd, (HMENU) ID_BUTTON, hInst, NULL);

      return 0;

    }

    // Когда главное окно приложения получает

    // фокус ввода, отдаем фокус списку

    case WM_SETFOCUS:

    {

      SetFocus(hListBox);

      return 0;

    }

    case WM_COMMAND:

    {

      // Обработка извещения списка об ошибке

      if(wParam == ID_LIST)

      {

        if(HIWORD(lParam) == (unsigned)LBN_ERRSPACE)

        {

          MessageBox(hwnd, "Мало памяти",

           szWindowTitle, MB_OK);

        }

        // Двойной щелчок по имени файла, каталога

        // или диска

        else if(HIWORD(lParam) == LBN_DBLCLK)

        {

          // Имитируем приход сообщения от кнопки

          SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);

          return 0;

        }

        // Если пользователь изменил выделенную

        // строку, выводим в окно новую

        // выделенную строку

        else if(HIWORD(lParam) == LBN_SELCHANGE)

        {

          int uSelectedItem, nSize;

          char Buffer[256];

          HDC hdc;

          // Определяем номер новой выделенной строки

          uSelectedItem = (int)SendMessage(hListBox,

             LB_GETCURSEL, 0, 0L);

          if(uSelectedItem != LB_ERR)

          {

             // Получаем строку

             SendMessage(hListBox, LB_GETTEXT,

               uSelectedItem, (LPARAM)Buffer);

             hdc = GetDC(hwnd);

             nSize = lstrlen(Buffer);

             TextOut(hdc, 250, 60,

             (LPSTR)"                         ", 25);

             TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

             ReleaseDC(hwnd, hdc);

          }

        }

      }

      // Сообщение от кнопки

      else if(wParam == ID_BUTTON)

      {

        int uSelectedItem;

        char Buffer[256];

        // Определяем номер выделенной строки

        uSelectedItem = (int)SendMessage(hListBox,

           LB_GETCURSEL, 0, 0L);

        if(uSelectedItem != LB_ERR)

        {

           // Получаем выделенную строку

           SendMessage(hListBox, LB_GETTEXT,

             uSelectedItem, (LPARAM)Buffer);

           // Если выбрали имя каталога или диска,

           // пытаемся изменить сначала текущий

           // каталог, а затем текущий диск

           if(Buffer[0] == '[')

           {

             // Выделяем в выбранной строке имя каталога

             Buffer[lstrlen(Buffer) - 1] = '\0';

             // Пытаемся изменить каталог

             if(chdir(&Buffer[1]) != 0)

             {

               // Если не удалось, значит выбрали имя

               // диска. В этом случае выделяем букву

               // имени диска и добавляем строку ":\\:

               Buffer[3] = '\0';

               lstrcat(Buffer, ":\\");

               // Изменяем текущий диск

               if(chdir(&Buffer[2]) == 0)

               {

                 // Преобразуем букву диска в

                 // номер диска

                 AnsiLowerBuff(&Buffer[2], 1);

                 // Устанавливаем новый диск

                 setdisk(Buffer[2] - 'a');

               }

             }

             // Сбрасываем содержимое списка

             SendMessage(hListBox, LB_RESETCONTENT, 0, 0L);

             // Заполняем список информацией о файлах

             // и каталогах в текущем каталоге, а также

             // вносим туда имена дисков

             SendMessage(hListBox, LB_DIR,

                DDL_READWRITE | DDL_READONLY  | DDL_HIDDEN |

                DDL_SYSTEM    | DDL_DIRECTORY | DDL_DRIVES |

                DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

           }

           // Если выбрали файл, выводим его имя на экран

           else

           {

              MessageBox(hwnd, Buffer, szWindowTitle, MB_OK);

           }

        }

      }

      return 0;

    }

    // Это сообщение поступает от списка, если он

    // имеет фокус ввода и пользователь работает

    // с клавиатурой

    case WM_VKEYTOITEM:

    {

      // Если пользователь нажал клавишу <Enter>,

      // посылаем в окно сообщение WM_COMMAND с

      // параметром wParam, равным идентификатору

      // кнопки

      if(wParam == VK_RETURN)

      {

        SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);

      }

      return -1;

    }

    case WM_PAINT:

    {

      HDC hdc;

      PAINTSTRUCT ps;

      hdc = BeginPaint(hwnd, &ps);

      TextOut(hdc, 30, 10,

        "Выберите файл, каталог или диск", 31);

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Списки класса COMBOBOX

 Это орган управления на базе класса "combobox", представляет

 собой комбинацию списка и однострочного редактора текста,

 поэтому для него исплльзуются стили, коды извещений и

 сообщения, аналогичные списку и некоторые сообщения,

 специфичные для текстового редактора. Создание:

 hComboBox=CreateWindow("combobox", NULL,

  WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_AUTOHSCROLL |

  CBS_SIMPLE,

  30,30,200,200,

  hwnd, (HMENU)ID_LIST, hInst, NULL);

  Предопределенные стили для объекта этого класса:

 CBS_SIMPLE   всегда видимый список под однострочным редактором

 CBS_DROPDOWN список невидим до нажатия пиктограммы

 CBS_DROPDOWNLIST  с редактором только для отображения

 CBS_OWNERDRAWFIXED

 CBS_OWNERDRAWVARIABLE

 CBS_AUTOHSCROLL

 CBS_OEMCONVERT

 CBS_SORT           с сортировкой строк

 CBS_HASSTRINGS    список строк, рисуемый родителем

 CBS_NOINTEGRALHEIGHT

 CBS_DISABLENOSCROLL

 Коды извещений в lParam:

 CBN_ERRSPACE

 CBN_SELCHANGE

 CBN_DBLCLK

 CBN_SETFOCUS

 CBN_KILLFOCUS

 CBN_EDITCHANGE

 CBN_EDITUPDATE

 CBN_DROPDOWN

 CBN_CLOSEUP

 CBN_SELENDOK

 CBN_SELENDCANCEL

 Набор сообщений аналогичен listbox,их имена имеют префикс CB_

 CB_GETEDITSEL

 CB_LIMITTEXT

 CB_SETEDITSEL

 CB_ADDSTRING

 CB_DELETESTRING

 CB_DIR

 CB_GETCOUNT

 CB_GETCURSEL

 CB_GETLBTEXT

 CB_GETLBTEXTLEN

 CB_INSERTSTRING

 CB_RESETCONTENT

 CB_FINDSTRING

 CB_SELECTSTRING

 CB_SETCURSEL

 CB_SHOWDROPDOWN

 CB_GETITEMDATA

 CB_SETITEMDATA

 CB_GETDROPPEDCONTROLRECT определение экранных координат видимой

                          части списка

 CB_SETITEMHEIGHT

 CB_GETITEMHEIGHT

 CB_SETEXTENDEDUI

 CB_GETEXTENDEDUI

 CB_GETDROPPEDSTATE

 CB_FINDSTRINGEXACT

 ПРИМЕР иллюстрирует работу со списком combobox - создает список

 для выбора файлов, каталогов и дисков

// ----------------------------------------

// Использование органа управления

// класса "combobox" для просмотра

// содержимого каталога

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#include <dir.h>

#define ID_LIST   1

#define ID_BUTTON 2

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "ComboAppClass";

// Заголовок окна

char const szWindowTitle[] = "Выбор файла";

// Идентификатор копии приложения

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Сохраняем идентификатор копии приложения

  // в глобальной переменной

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем расположение и размеры

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,       //

    CW_USEDEFAULT,       //

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Идентификатор органа управления "combobox"

  static HWND hComboBox;

  // Идентификатор кнопки

  static HWND hButton;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем список

      hComboBox = CreateWindow("ComboBox", NULL,

         WS_CHILD | WS_VISIBLE | WS_VSCROLL |

         CBS_AUTOHSCROLL | CBS_SIMPLE,

         30, 30, 200, 200,

         hwnd, (HMENU) ID_LIST, hInst, NULL);

      // Добавляем в список содержимое текущего

      // каталога

      SendMessage(hComboBox, CB_DIR,

        DDL_READWRITE | DDL_READONLY | DDL_HIDDEN |

        DDL_SYSTEM | DDL_DIRECTORY | DDL_DRIVES |

        DDL_ARCHIVE,

        (LPARAM)(LPSTR)"*.*");

      // Создаем кнопку

      hButton = CreateWindow("button", "OK",

         WS_CHILD | WS_VISIBLE |

         BS_PUSHBUTTON,

         250, 30, 50, 20,

         hwnd, (HMENU) ID_BUTTON, hInst, NULL);

      return 0;

    }

    case WM_SETFOCUS:

    {

      SetFocus(hComboBox);

      return 0;

    }

    case WM_COMMAND:

    {

      // Обработка извещения списка об ошибке

      if(wParam == ID_LIST)

      {

        if(HIWORD(lParam) == (unsigned)CBN_ERRSPACE)

        {

          MessageBox(hwnd, "Мало памяти",

           szWindowTitle, MB_OK);

        }

        // Двойной щелчок по имени файла, каталога

        // или диска

        else if(HIWORD(lParam) == CBN_DBLCLK)

        {

          // Имитируем приход сообщения от кнопки

          SendMessage(hwnd, WM_COMMAND, ID_BUTTON, 0L);

          return 0;

        }

        // Если пользователь изменил выделенную

        // строку, выводим в окно новую

        // выделенную строку

        else if(HIWORD(lParam) == CBN_SELCHANGE)

        {

          int uSelectedItem, nSize;

          char Buffer[256];

          HDC hdc;

          // Определяем номер новой выделенной строки

          uSelectedItem = (int)SendMessage(hComboBox,

             CB_GETCURSEL, 0, 0L);

          if(uSelectedItem != CB_ERR)

          {

             // Получаем строку

             SendMessage(hComboBox, CB_GETLBTEXT,

               uSelectedItem, (LPARAM)Buffer);

             hdc = GetDC(hwnd);

             nSize = lstrlen(Buffer);

             TextOut(hdc, 250, 60,

             (LPSTR)"                         ", 25);

             TextOut(hdc, 250, 60, (LPSTR)Buffer, nSize);

             ReleaseDC(hwnd, hdc);

          }

        }

      }

      // Сообщение от кнопки

      else if(wParam == ID_BUTTON)

      {

        int uSelectedItem;

        char Buffer[256];

        // Определяем номер выделенной строки

        uSelectedItem = (int)SendMessage(hComboBox,

           CB_GETCURSEL, 0, 0L);

        if(uSelectedItem != LB_ERR)

        {

           // Получаем выделенную строку

           SendMessage(hComboBox, CB_GETLBTEXT,

             uSelectedItem, (LPARAM)Buffer);

           // Если выбрали имя каталога или диска,

           // пытаемся изменить сначала текущий

           // каталог, а затем текущий диск

           if(Buffer[0] == '[')

           {

             // Выделяем в выбранной строке имя каталога

             Buffer[lstrlen(Buffer) - 1] = '\0';

             // Пытаемся изменить каталог

             if(chdir(&Buffer[1]) != 0)

             {

               // Если не удалось, значит выбрали имя

               // диска. В этом случае выделяем букву

               // имени диска и добавляем строку ":\\:

               Buffer[3] = '\0';

               lstrcat(Buffer, ":\\");

               // Изменяем текущий диск

               if(chdir(&Buffer[2]) == 0)

               {

                 // Преобразуем букву диска в

                 // номер диска

                 AnsiLowerBuff(&Buffer[2], 1);

                 // Устанавливаем новый диск

                 setdisk(Buffer[2] - 'a');

               }

             }

             // Сбрасываем содержимое списка

             SendMessage(hComboBox, CB_RESETCONTENT, 0, 0L);

             // Заполняем список информацией о файлах

             // и каталогах в текущем каталоге, а также

             // вносим туда имена дисков

             SendMessage(hComboBox, CB_DIR,

                DDL_READWRITE | DDL_READONLY  | DDL_HIDDEN |

                DDL_SYSTEM    | DDL_DIRECTORY | DDL_DRIVES |

                DDL_ARCHIVE, (LPARAM)(LPSTR)"*.*");

           }

           // Если выбрали файл, выводим его имя на экран

           else

           {

              MessageBox(hwnd, Buffer, szWindowTitle, MB_OK);

           }

        }

      }

      return 0;

    }

    case WM_PAINT:

    {

      HDC hdc;

      PAINTSTRUCT ps;

      hdc = BeginPaint(hwnd, &ps);

      TextOut(hdc, 30, 10,

        "Выберите файл, каталог или диск", 31);

      EndPaint(hwnd, &ps);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}


Диалоговые панели

 Это временные (pop-up) окна, созданные на базе

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

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

 Функция окна для класса диалговых панелей выполняет практически

 всю необходимую работу по организации взаимодействия органов

 управления с прикладной программой.

 Для размещения органов управления на поверхности диалоговой

 панели можно использовать 3 способа:

 1) Включить в файл ресурсов текстовое описание шаблона

 диалоговой панели:

DIALOPG_BOX DIALOG 25,34,152,67

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "Программа DIALOG"

BEGIN

CTEXT "Программирование Windows - приложенитй\n"

      "Программа DIALOG из книги Фроловых",

-1,28,9,117,26,

WS_CHILD | WS_VISIBLE | WS_GROUP

ICON "APPICON", -1,6,14,16,16, WS_CHILD | WS_VISIBLE

DEFPUSHBUTTON "OK", ID_OK, 56,43,36,14,

WS_CHILD | WS_VISIBLE | WS_TABSTOP

END

 2)Использовать редактор ресурсов например Resource Workshop

 3) Создание диалоговой панели в памяти во время работы

 приложения - используется редко, если вид панели зависит от

 результатов работы прикладной программы.

Помимо шаблона перед созданием диалоговой панели надо определить

специальную функцию диалога - она будет получать сообщения от

ф-ции окна диалоговой панели. Она тоже является ф-цией обратного

вызова и должна быть описана с ключевым словом _export или ее

имя должно быть в разделе EXPORT файла определения модуля. Таким

образом для создания диалоговой панели необходимо:

-создать шаблон диалога;

-определить функцию диалога;

-вызвать одну из функций создания диалога.

В качестве стиля задается в шаблоне комбинация WS_ и DS_

констант:

DS_ABSALIGN

DS_SYSMODAL  системный модальный диалог

DS_LOCALEDIT редакторы текста будут использовать память в

             сегменте данных

DS_SETFONT

DS_MODALFRAME  модальный диалог

DS_NOIDLEMSG   если не указать этот стиль, при отоюражении

диалогового окна Windows будет посылать родительскому окну

сообщение WM_ENTERIDLE

Для стандартной панели диалога используются стили

WS_POPUP, WS_BORDER, WS_SYSMENU, WS_CAPTION, DS_MODALFRAME

Для панели с рамкой но без заголовка мспользуют WS_DLGFRAME

Диалоги бывают модальные (блокирующие работу приложения до

завершения диалога), системные модальные (блокирующие

дополнительно переход к другим приложениям до завершения

диалога) и немодальные (ничего не блокирующие).

В описании шаблона может присутствовать оператор CLASS, если

используется для создания диалога нестандартный собственный

класс , а таже оператор

FONT 10, "MS Seruf"

задания шрифта с размером и названием.

Наиболее универсальный формат описания органов управления:

CONTROL "Текст", ID, class, style, x, y, width, height

позволяет описать орган управления класса class с нанесенным

на него текстом "Текст"

Специализированные форматы описания могут использоваться:

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

полос скроллинга:

ctlType "Текст", ID, x, y, width, height, [,style]

с обозначениями органов управления и стилей из следующего

набора:

 ОБОЗНАЧЕНИЕ       КЛАСС ОКНА       СТИЛЬ

   clType

 CHECKBOX          button         BS_CHECKBOX, WS_TABSTOP

 CTEXT             static         SS_CENTER, WS_GROUP

 DEFPUSHBUTTON     button         BS_DEFPUSHBUTTON,WS_TABSTOP

 GROUPBOX          button         BS_GROUPBOX

 ICON              static         SS_ICON

 LTEXT             static         SS_LEFT,WS_GROUP

 PUSHBUTTON        button         BS_PUSHBUTTON,WS_TABSTOP

 RADIOBUTTON       button         BS_RADIOBUTTON,WS_TABSTOP

 RTEXT             static         SS_RIGHT,WS_GROUP

 Для описания списков, редакторов текста и полос скроллинга:

 clType  ID,x,y,width,height,[,style]

 с обозначениями и стилями из набора:

 ОБОЗНАЧЕНИЕ       КЛАСС ОКНА       СТИЛЬ

   clType

 COMBOBOX          combobox       CBS_SIMPLE,WS_TABSTOP

 LISTBOX           listbox        LBS_NOTIFY,WS_BORDER

 EDITTEXT          edit           ES_LEFT,WS_BORDER,WS_TABSTOP

 SCROLLBAR         scrollbar      SBS_HORZ

Функция диалога имеет произвольное имя и должна быть описана в

формате:

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

Здесь первый параметр передает идентификатор диалога а не окна

hwnd. Ф-ция диалога не должна вызывать DefWindowproc; если она

обрабатывает сообщение то возвращает TRUE, иначе FALSE.

Ф-ция диалога не обрабатывает WM_CREATE, WM_PAINT, WM_DESTROY.

При инициализации диалога в нее передается сообщение

WM_INITDIALOG, с ид-ром органа управления ( на котором будет

фокус ввода сразу после отображения) в wParam (первый описанный

в шаблоне со стилем WM_TABSTOP ).  Если обработчик сообщения

WM_INITDIALOG назначает фокус ввода другому органу через

SetFocus то ф-ция диалога должна вернуть FALSE, иначе - TRUE.

Сообщение WM_COMMAND поступает в ф-цию диалога от органов

управления на панели диалога, но может получить также сообщение

с wParam= IDOK или = IDCANCEL при нажатии клавиш Enter и Esc

соответственно (или кнопок с идентификаторами IDOK, IDCANCEL).

В программном интерфейсе Windows определены 8 функций для

создания модальных и немодальных диалоговых панелей.

Для создания модальной используется чаще всего

int     WINAPI DialogBox(

HINSTANCE hInst,       ид-р копии приложения

LPCSTR lpszTemplate,   строка имени шаблона из оператора DIALOG

HWND hwndOwner,        ид-р окна,создавшего панель диалога

DLGPROC  dlgprc);      адрес ф-ции диалога

Если при создании панели диалога ей необходимо передать

параметры, пользуются ф-цией

int WINAPI DialogBoxParam(

HINSTANCE hInstance,

LPCSTR lpszTemplate,

HWND hwndOwner,

DLGPROC dlgproc,

LPARAM lParamInit);   для передачи параметра

Если шаблон создается непосредственно в памяти, используют

другую пару ф-ций:

int WINAPIDialogBoxIndirect(HINSTANCE, HGLOBAL, HWND, DLGPROC);

int WINAPI DialogBoxIndirectParam(HINSTANCE, HGLOBAL, HWND,

DLGPROC, LPARAM);

Все перечисленные ф-ции возвращают число, которое надо передать

ф-ции завершения диалога

void    WINAPI EndDialog(HWND, int);

Для создания немодальных диалогов:

 HWND    WINAPI CreateDialog(HINSTANCE, LPCSTR, HWND, DLGPROC);

 HWND    WINAPI CreateDialogIndirect(HINSTANCE, const void FAR*,

 HWND, DLGPROC);

 HWND    WINAPI CreateDialogParam(HINSTANCE, LPCSTR, HWND,

 DLGPROC, LPARAM);

 HWND    WINAPI CreateDialogIndirectParam(HINSTANCE, const void

 FAR*, HWND, DLGPROC, LPARAM);

Эти ф-ции возвращают ид-р окна для созданной панели диалога. Для

немодальных диалогов сообщения от органов управления проходят

через общую очередь сообщений приложения и для выделения этих

сообщений цикл обработки должен использовать ф-цию

BOOL    WINAPI IsDialogMessage(HWND, MSG FAR*);

которая определяет,предназначено ли сообщение, определяемое

вторым параметром, для немодальной панели с ид-ром окна равным

первому параметру.

 Завершение работы немодальной диалоговой панели следует

 выполнять уже знакомой ф-цией

BOOL    WINAPI DestroyWindow(HWND);

указав ей в качестве параметра идентификатор окна

панели,полученный от функции создания.

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

на панели диалога:

1) через ф-цию

LRESULT WINAPI SendMessage(HWND, UINT, WPARAM, LPARAM);

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

можно получить с помощью ф-ции

HWND    WINAPI GetDlgItem(HWND, int);

В качестве первого параметра этой ф-ции надо передать ид-р окна

диалоговой панели; для модальных диалогов его можно определить

только внутри ф-ции диалога (он передается через ее первый

параметр); ид-р окна немодального диалога возвращается ф-циями

создания.

Второй параметр есть ид-р органа управления, указанный в

шаблоне.

Чтобы установить переключатель с ид-ром IDC_SWITCH во включенное

состояние можно послать:

SendMessage(GetDlgItem(hdlg, IDC_SWITCH),BM_SETCHECK,TRUE,0L);

Зная ид-р окна органа управления, можно решить обратную задачу

- получить ид-р самого органа управления:

nIdControl=GetWindowWord(hWndControl, GWW_ID);

2) через специальные ф-ции, которым не требуются ид-ры окон

органов управления - им достаточно ид-ров самих органов

управления из описания шаблона.

Удобной является ф-ция

LRESULT WINAPI SendDlgItemMessage(

HWND hdlg,       ид-р окна панели диалога

int idDlgItem,   ид-р органа управления - получатель сообщениия

UINT uMsg,       код сообщения

WPARAM wParam,   параметры

LPARAM lParam);

Для выполнения часто используемых операций есть набор

специальных функций; в частности для заполнения списка LISTBOX

именами файлов, каталогов и дисков предназначена:

int     WINAPI DlgDirList(

HWND,      ид-р окна диалога

LPSTR,     строка с шаблоном имен файлов

int,       ид-р списка

int,       ид-р статич органа для пути или NULL

UINT);     тип файла из констант DDL_...

Аналогичная ф-ция для COMBOBOX:

int     WINAPI DlgDirListComboBox(HWND, LPSTR, int, int, UINT);

Для получения из списка выбранной строки:

BOOL    WINAPI DlgDirSelect(HWND, LPSTR, int);

BOOL    WINAPI DlgDirSelectComboBox(HWND, LPSTR, int);

BOOL    WINAPI DlgDirSelectEx(HWND, LPSTR, int, int);

BOOL    WINAPI DlgDirSelectComboBoxEx(HWND, LPSTR, int, int);

Для работы с редакторами текстов:

void    WINAPI SetDlgItemText(HWND, int, LPCSTR);-для изменения

заголовка или записи текста в редактор

void    WINAPI SetDlgItemInt(HWND, int, UINT, BOOL); - для

записи в редактор строки как результата преобразования целого в

строку

int     WINAPI GetDlgItemText(HWND, int, LPSTR, int);- для

получения строки, связанной с органом управления в панели

диалога

UINT    WINAPI GetDlgItemInt(HWND, int, BOOL FAR* , BOOL);- для

получения строки из органа управления и преобразования ее в

целое

Для работы с переключателями:

void    WINAPI CheckDlgButton(HWND, int, UINT);-для изменения

состояния переключателя CHECKBOX

void    WINAPI CheckRadioButton(HWND, int, int, int);- то же для

RADIOBUTTON

UINT    WINAPI IsDlgButtonChecked(HWND, int);-для определения

текущего состояния переключателя

И другие, например:

HWND    WINAPI GetNextDlgGroupItem(HWND, HWND, BOOL);-для

определения ид-ра окна предыдущего или следующего органа

управления

HWND    WINAPI GetNextDlgTabItem(HWND, HWND, BOOL);-для

определения ид-ра окна первого органа управления, имеющего стиль

WS_TABSTOP

void    WINAPI MapDialogRect(HWND, RECT FAR*);-для

преобразования координат из единиц панели диалога в пикселы

DWORD   WINAPI GetDialogBaseUnits(void);- возвращает в младшем

слове ширину в пикселах диалоговой единцы, в старшем - высоту.

ПРИМЕР 1 создает простейшую панель диалога, появляющуюся после

нажатия кнопки About... в гглавном окне. На панели дмалога

размещены пиктограмма, статический орган и кнопка.

 // ----------------------------------------

 // Простейшаядиалоговая панель

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#define IDB_Button1 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL CALLBACK    _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "DialogAppClass";

// Заголовок окна

char const szWindowTitle[] = "Dialog Box Demo";

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  HWND hButton1;

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем кнопку

  hButton1 = CreateWindow("button", "About...",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 20,

    90, 30,

    hwnd,

    (HMENU) IDB_Button1,

    hInstance, NULL);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  static DLGPROC lpfnDlgProc;

  switch (msg)

  {

    case WM_COMMAND:

    {

      // Если нажата кнопка, выводим

      // диалоговую панель

      if(wParam == IDB_Button1)

      {

        // Переходник для функции диалоговой панели

        lpfnDlgProc = (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

        // Создаем модальную диалоговую панель

        DialogBox(hInst, "DIALOG_OK", hwnd, lpfnDlgProc);

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// =====================================

// Функция DlgProc

// =====================================

#pragma argsused

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch(msg)

  {

    // Инициализация диалоговой панели

    case WM_INITDIALOG:

    {

      return TRUE;

    }

    case WM_COMMAND:

    {

      switch(wParam)

      {

        // Сообщение от кнопки "OK"

        case IDOK:

        // Отмена диалоговой панели.

        // Это сообщение приходит, когда пользователь

        // нажимает на клавишу <Esc>

        case IDCANCEL:

        {

          // Устанавливаем флаг завершения диалога

          EndDialog(hdlg, 0);

          return TRUE;

        }

      }

    }

  }

  return FALSE;

}

Файл ресурсов для примера 1:

#include "g:\tcwin\include\windows.h"

APPICON ICON "appicon.ico"

DIALOG_OK DIALOG 25, 34, 152, 67

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "-Ёшыюцхэшх DIALOG"

BEGIN

        CTEXT "Microsoft Windows Application\n"

"-Ёшыюцхэшх DIALOG\n"

"(C) Frolov A.V., 1994", -1, 28, 9, 117, 26, WS_CHILD | WS_VISIBLE | WS_GROUP

        ICON "APPICON", -1, 6, 14, 16, 16, WS_CHILD | WS_VISIBLE

        DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

END

ПРИМЕР 2 создает панель диалога со списком combobox, 2 кнопки и

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

органами управления. Обработчик сообщения WN_INITDIALOG в ф-ции

диалога заполняет список именами файлов в текущем каталоге и

// ----------------------------------------

// Диалоговая панель со списком COMBOBOX

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#define IDC_COMBO  101

#define IDC_STATIC 102

#define IDB_Button1 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL    CALLBACK _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "DialogAppClass";

// Заголовок окна

char const szWindowTitle[] = "Dialog Box Demo";

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  HWND hButton1;

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем кнопку

  hButton1 = CreateWindow("button", "Open...",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 20,

    90, 30,

    hwnd,

    (HMENU) IDB_Button1,

    hInstance, NULL);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  memset(&wc, 0, sizeof(wc));

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_COMMAND:

    {

      // Если нажата кнопка, выводим

      // диалоговую панель

      if(wParam == IDB_Button1)

      {

        // Создаем модальную диалоговую панель

        DialogBox(hInst, "SELECT_FILE", hwnd, (DLGPROC)DlgProc);

      }

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// =====================================

// Функция DldProc

// =====================================

#pragma argsused

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch(msg)

  {

    // Инициализация диалоговой панели

    case WM_INITDIALOG:

    {

      // Заполняем список именами файлов, каталогов

      // и дисковых устройств

      DlgDirListComboBox(hdlg,

        "*.*", IDC_COMBO, IDC_STATIC,

        DDL_READWRITE | DDL_READONLY  | DDL_HIDDEN |

        DDL_SYSTEM    | DDL_DIRECTORY | DDL_DRIVES |

        DDL_ARCHIVE);

      return TRUE;

    }

    case WM_COMMAND:

    {

      switch(wParam)

      {

        char Buffer[80];

        // Обрабатываем извещение от списка

        case IDC_COMBO:

        {

          // Двойной щелчок мышью по строке списка

          if(HIWORD(lParam) == LBN_DBLCLK)

          {

            // Получаем выбранную строку

            // и отображаем ее на экране

            GetDlgItemText(hdlg, IDC_COMBO, Buffer, 80);

            MessageBox(hdlg, Buffer, szWindowTitle, MB_OK);

          }

          return TRUE;

        }

        // Сообщение от кнопки "OK"

        case IDOK:

        {

          // Получаем выбранную строку

          // и отображаем ее на экране

          GetDlgItemText(hdlg, IDC_COMBO, Buffer, 80);

          MessageBox(hdlg, Buffer, szWindowTitle, MB_OK);

          return TRUE;

        }

        // Отмена диалоговой панели.

        case IDCANCEL:

        {

          // Устанавливаем флаг завершения диалога

          EndDialog(hdlg, 0);

          return TRUE;

        }

      }

    }

  }

  return FALSE;

}

Файл ресурсов имеет содержание:

#include "g:\tcwin\include\windows.h"

#include "dlgcombo.hpp"

APPICON ICON "appicon.ico"

SELECT_FILE DIALOG 6, 37, 199, 120

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "-їюфэющ Їрщы"

BEGIN

        CONTROL "", IDC_STATIC, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 13, 7, 123, 10

        ICON "APPICON", -1, 149, 58, 16, 16, WS_CHILD | WS_VISIBLE

        DEFPUSHBUTTON "OK", IDOK, 149, 19, 36, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

        CONTROL "", IDC_COMBO, "COMBOBOX", CBS_SIMPLE | CBS_SORT | CBS_DISABLENOSCROLL | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 13, 19, 123, 94

        PUSHBUTTON "Cancel", IDCANCEL, 149, 39, 36, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

END

Файл рписания модуля

; =============================

; +рщы юяЁхфхыхэш_ ьюфєы_

; =============================

NAME DLGCOMBO

DESCRIPTION '-Ёшыюцхэшх DLGCOMBO, (C) 1994, Frolov A.V.'

EXETYPE windows

STUB 'winstub.exe'

STACKSIZE 5120

HEAPSIZE 1024

CODE preload moveable discardable

DATA preload moveable multiple

ПРИМЕР 3 демонстрирует использование групп органов управления;

он создает панель диалога "Карточка сотрудника".

// ----------------------------------------

// Диалоговая панель с редактором текста

// и переключателями

// ----------------------------------------

#define STRICT

#include <windows.h>

#define IDC_COMBO      101

#define IDC_STATIC     102

#define IDC_FUNCTION   103

#define IDC_PROGRAMMER 104

#define IDC_ENGINIER   105

#define IDC_SENGINIER  106

#define IDC_OTHER      107

#define IDC_ENGLISH    108

#define IDC_C          109

#define IDC_PASCAL     110

#define IDC_NAME       111

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL    CALLBACK _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "DlgTabAppClass";

// Заголовок окна

char const szWindowTitle[] = "DlgTab Box Demo";

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем модальную диалоговую панель

      DialogBox(hInst, "SELECT", hwnd, (DLGPROC)DlgProc);

      // После завершения работы диалоговой панели

      // завершаем работу приложения

      DestroyWindow(hwnd);

      return 0;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// =====================================

// Функция DldProc

// =====================================

#pragma argsused

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch(msg)

  {

    // Инициализация диалоговой панели

    case WM_INITDIALOG:

    {

      return TRUE;

    }

    case WM_COMMAND:

    {

      switch(wParam)

      {

        char Buffer[256];

        // Сообщение от кнопки "OK"

        case IDOK:

        {

          // Получаем строку из текстового редактора

          GetDlgItemText(hdlg, IDC_NAME, Buffer, 80);

          lstrcat(Buffer, "\n\nДолжность:\n");

          // Определяем состояние переключателей

          // типа RadioButton

          if(IsDlgButtonChecked(hdlg, IDC_PROGRAMMER))

          {

            lstrcat(Buffer, "Программист");

          }

          else if(IsDlgButtonChecked(hdlg, IDC_ENGINIER))

          {

            lstrcat(Buffer, "Инженер");

          }

          else if(IsDlgButtonChecked(hdlg, IDC_SENGINIER))

          {

            lstrcat(Buffer, "Старший инженер");

          }

          lstrcat(Buffer, "\n\nЗнает языки:\n");

          // Определяем состояние переключателей

          // типа CheckBox

          if(IsDlgButtonChecked(hdlg, IDC_ENGLISH))

          {

            lstrcat(Buffer, "Английский\n");

          }

          if(IsDlgButtonChecked(hdlg, IDC_C))

          {

            lstrcat(Buffer, "Си\n");

          }

          if(IsDlgButtonChecked(hdlg, IDC_PASCAL))

          {

            lstrcat(Buffer, "Паскаль\n");

          }

          MessageBox(hdlg, Buffer, "Вы ввели", MB_OK);

          return TRUE;

        }

        // Отмена диалоговой панели.

        case IDCANCEL:

        {

          // Устанавливаем флаг завершения диалога

          EndDialog(hdlg, FALSE);

          return TRUE;

        }

      }

    }

  }

  return FALSE;

}

Файл описания ресурсов

#include "d:\tcwin\include\windows.h"

#include "dlgtab.hpp"

APPICON ICON "dlgtab.ico"

SELECT DIALOG 12, 28, 157, 138

STYLE DS_MODALFRAME | WS_POPUP |

      WS_CAPTION | WS_SYSMENU

CAPTION "-рЁЄюўър ёюЄЁєфэшър"

BEGIN

  CONTROL "", IDC_NAME, "EDIT",

    ES_LEFT | WS_CHILD | WS_VISIBLE | WS_BORDER |

    WS_GROUP | WS_TABSTOP,

    7, 20, 143, 12

  CONTROL "&-юыцэюёЄ№", IDC_FUNCTION, "BUTTON",

    BS_GROUPBOX | WS_CHILD | WS_VISIBLE,

    7, 35, 79, 48

  CONTROL "+эцхэхЁ", IDC_ENGINIER, "BUTTON",

    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE |

    WS_GROUP | WS_TABSTOP,

    11, 44, 72, 12

  CONTROL "-ЄрЁ°шщ шэцхэхЁ", IDC_SENGINIER, "BUTTON",

    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE,

    11, 55, 73, 12

  CONTROL "-ЁюуЁрььшёЄ", IDC_PROGRAMMER, "BUTTON",

    BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE,

    11, 67, 73, 12

  CONTROL "&-Ёюўхх", IDC_OTHER, "BUTTON",

    BS_GROUPBOX | WS_CHILD | WS_VISIBLE | WS_GROUP,

    7, 84, 80, 47

  CONTROL "English", IDC_ENGLISH, "BUTTON",

    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE |

    WS_GROUP | WS_TABSTOP,

    11, 93, 74, 12

  CONTROL "¦эрхЄ -ш", IDC_C, "BUTTON",

    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,

    11, 105, 74, 12

  CONTROL "¦эрхЄ -рёъры№", IDC_PASCAL, "BUTTON",

    BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,

    11, 116, 73, 12

  CONTROL "OK", IDOK, "BUTTON",

    BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE |

    WS_GROUP | WS_TABSTOP,

    114, 38, 36, 14

  CONTROL "Cancel", IDCANCEL, "BUTTON",

    BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,

    114, 56, 36, 14

  ICON "APPICON", -1, 134, 4, 16, 16,

    WS_CHILD | WS_VISIBLE

  CONTROL "+.+.+.", -1, "STATIC",

    SS_LEFT | WS_CHILD | WS_VISIBLE,

    7, 9, 27, 8

END

Файл определения модуля

; =============================

; +рщы юяЁхфхыхэш_ ьюфєы_

; =============================

NAME DLGTAB

DESCRIPTION '-Ёшыюцхэшх DLGTAB, (C) 1994, Frolov A.V.'

EXETYPE windows

STUB 'winstub.exe'

STACKSIZE 5120

HEAPSIZE 1024

CODE preload moveable discardable

DATA preload moveable multiple

ПРИМЕР 4 выводит на экран панель диалога, аналогичную примеру 1,

но теперь она создается как немодальная.

// ----------------------------------------

// Простейшая немодальная диалоговая панель

// ----------------------------------------

#define STRICT

#include <windows.h>

#define IDB_Button1 1

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL CALLBACK    _export DlgProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "DialogNMAppClass";

// Заголовок окна

char const szWindowTitle[] = "Modeless Dialog Box";

// Идентификатор окна диалоговой панели

HWND    hwndDlg;

// Идентификатор главного окна приложения

HWND    hwndMain;

HINSTANCE hInst;

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hButton1;

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  hInst = hInstance;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwndMain = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwndMain)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwndMain, nCmdShow);

  UpdateWindow(hwndMain);

  // Создаем кнопку

  hButton1 = CreateWindow("button", "About...",

    WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,

    20, 20,

    90, 30,

    hwndMain,

    (HMENU) IDB_Button1,

    hInstance, NULL);

  if(!hButton1)

    return FALSE;

  // До запуска цикла обработки сообщений

  // идентификатор диалоговой панели должен

  // быть равен 0, т. к. панель еще не создана

  hwndDlg = (HWND)0;

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    // Выделяем сообщения для диалоговой панели,

    // если эта панель уже создана

    if(hwndDlg == 0 || !IsDialogMessage(hwndDlg, &msg))

    {

      TranslateMessage(&msg);

      DispatchMessage(&msg);

    }

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  wc.style = 0;

  wc.lpfnWndProc = (WNDPROC) WndProc;

  wc.cbClsExtra = 0;

  wc.cbWndExtra = 0;

  wc.hInstance = hInstance;

  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszMenuName = (LPSTR)NULL;

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  static DLGPROC lpfnDlgProc;

  switch (msg)

  {

    case WM_COMMAND:

    {

      // Если нажата кнопка, выводим

      // диалоговую панель

      if(wParam == IDB_Button1)

      {

        // Переходник для функции диалоговой панели

        lpfnDlgProc = (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

        // Создаем немодальную диалоговую панель

        hwndDlg = CreateDialog(hInst, "DIALOG_OK", hwnd, lpfnDlgProc);

      }

      return 0;

    }

    case WM_DESTROY:

    {

      // Удаляем диалоговую панель

      DestroyWindow(hwndDlg);

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// =====================================

// Функция DldProc

// =====================================

#pragma argsused

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch(msg)

  {

    // Инициализация диалоговой панели

    case WM_INITDIALOG:

    {

      return TRUE;

    }

    case WM_COMMAND:

    {

      switch(wParam)

      {

        // Сообщение от кнопки "OK"

        case IDOK:

        // Отмена диалоговой панели.

        // Это сообщение приходит, когда пользователь

        // нажимает на клавишу <Esc>

        case IDCANCEL:

        {

          // Уничтожаем диалоговую панель

          DestroyWindow(hdlg);

          // Завершаем работу приложения

          DestroyWindow(hwndMain);

          return TRUE;

        }

      }

    }

  }

  return FALSE;

}

Файл ресурсов для этой программы:

#include "d:\tcwin\include\windows.h"

APPICON ICON "appicon.ico"

DIALOG_OK DIALOG 25, 34, 152, 67

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

CAPTION "-Ёшыюцхэшх DIALOGNM"

BEGIN

        CTEXT "Microsoft Windows Application\n"

"-Ёшыюцхэшх DIALOGNM\n"

"(C) Frolov A.V., 1994", -1, 28, 9, 117, 26, WS_CHILD | WS_VISIBLE | WS_GROUP

        ICON "APPICON", -1, 6, 14, 16, 16, WS_CHILD | WS_VISIBLE

        DEFPUSHBUTTON "OK", IDOK, 56, 43, 36, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

END


Ресурсы - меню
Ресурсы - меню в приложениях Windows

 Для создания меню чаще всего используется шаблон,

 подготовленный редактором ресурсов Resource Workshop или другим

 аналогичным инсьрументом. Можно также создавать меню

 динамически во время работы приложения с помощью специальных

 функций программного интерфейса Windows. В последнее время

 многие приложения создают орган управления Toolbar -

 прямоугольное окно с набором пиктограммных кнопок; но

 Windows-3.1 не поддерживает создания такого органа (хотя

 кнопочное управление процессом в программах все больше

 вытесняет классические меню) и приложение вынуждено создавать

 его само. Поэтому в этом разделе рассматривается и метод

 создания Toolbar.

 При выборе строки из меню программа получает сообщение

 WM_COMMAND, содержащее идентификатор выбранной строки и задача

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

 меню.

 Классификация типов меню.

 При создании окна можно указать, что окно будет снабжено меню.

 Меню в главном окне приложения будем называть МЕНЮ ПРИЛОЖЕНИЯ.

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

 для активизации дополнительных ВРЕМЕННЫХ МЕНЮ (POP_UP MENU).

 Некоторые строки меню могут отображаться блеклым серым цветом -

 это ЗАБЛОКИРОВАННЫЕ строки, они не могут быть выбраны.

 Строки меню могут быть отмечены "галочкой" и использоваться как

 переключатели, изменяющие режим работы приложения.

 Каждое стандартное приложение Windows  имеет СИСТЕМНОЕ МЕНЮ,

 позволяющее минимизировать, максимизироват, изменить размеры,

 переместить, закрыть окно, восстановить его размер и

 переключиться на другое приложение.

 Еще один класс меню называют ПЛАВАЮЩИМ МЕНЮ - оно может

 появляться в любом месте экрана, по которому сделан щелчок

 правой кнопки мыши - при этом пользователь избавляется от

 необходимости дальних перемещений курсора - меню сразу под

 рукой . Внешний вид плавающего меню может меняться программой в

 зависимости от объекта, над которым оно создается - это

 дополнительная поддержка объектно - ориентированного подхода в

 работе пользователя с вашей программой.

 Можно создавать также ГРАФИЧЕСКИЕ МЕНЮ или КОМБИНИРОВАННЫЕ МЕНЮ

 из строк текста и графических изображений.

 СОЗДАНИЕ МЕНЮ ПРИ ПОМОЩИ ШАБЛОНА.

 Текстовое описание шаблона меню, созданное текстовым редактором

 или редактором ресурсов Resource Workshop должно находиться в

 файле ресурсов с расширением .rc, включенном в файл проекта

 приложения.

 Описание шаблона имеет вид:

 Идентификатор  MENU [способ загрузки][тип памяти]

 BEGIN

 ...

 END

 Идентификатор - это текстовая строка или беззаковое целое

число. Спосб загрузки - PRELOAD или LOADONCALL. Тип памяти -

FIXED, MOVEABLE, DISCARDABLE. Между BEGIN - END размещаются

операторы описания строк

MENUITEM "текст строки", id , [,param]

Для подчеркивания одной из букв текста и последующего

использования ее с Alt при выборе перед ней нужно набрать &.

id - это число, которое приложение получит в wParam при выборе -

оно должно однозначно идентифицировать строку.

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

CHECKED  - будет отмечаться галочкой

GRAYED   - неактивная строка серого цвета

HELP     - слева от текста будет вертикальная линия разделения

INACTIVE - неактивная нормального цвета

MENUBREAK- вывод с новой строки или в новом столбце

MENUBARBREAK - то же но с выводом вертикальной линии

Для описания временных меню используют оператор

POPUP "текст" [,param]

BEGIN

...

END

с операторами MENUITEM между BEGIN-END.

Для создания горизонтальной линии пишут

MENUITEM SEPARATOR

ПОДКЛЮЧЕНИЕ МЕНЮ К ОКНУ ПРИ РЕГИСТРАЦИИ КЛАССА ОКНА

Для этого при регистрации класса необходимо в поле lpszMenuName

структуры wc типа WNDCLASS записать адрес строки с именем

шаблона меню в файле ресурсов:

APP_MENU MENU

BEGIN

...

END

...

wc.lpszMenuName="APP_MENU";

Для идентификации шаблона меню можно использовать целые числа:

#define APP_MENU 123

 в этом случае используем макрокоманду MAKEINTRESOURCE:

wc.lpszMenuName=MAKEINTRESOURCE(APP_MENU);

ПОДКЛЮЧЕНИЕ МЕНЮ К ОКНУ ПРИ СОЗДАНИИ ОКНА

Для подключения окна, отличного от указанного в классе окна

достаточно задать идентификатор нужного меню в 9-м параметре

ф-ции CreateWindow (дочерние окна не могут иметь меню). Значение

для этого параметра может быть получено например от ф-ции

HMENU   WINAPI LoadMenu(HINSTANCE, LPCSTR);

с именем загружаемого шаблона меню во 2-м параметре. При

завершении работы приложение должно уничтожить загруженное меню

ф-цией:

BOOL    WINAPI DestroyMenu(HMENU);

ПОСТУПАЮЩИЕ ОТ МЕНЮ СООБЩЕНИЯ В Ф-ЦИЮ СОЗДАВШЕГО ЕГО ОКНА

WM_INITMENU - посылается в момент отображения и содержит в

wParam идентификатор активизируемого меню; его обработчик должен

вернуть нуль, обработка может состоять в активации - деактивации

строк, изменении их состояния (например отметка галочкой) и пр.

WM_INITMENUPOPUP- младшее слово lParam содержит порядковый номер

временного меню в меню верхнего уровня, старшее 1 для системного

меню и 0 для обычного.

WM_MENUSELECT

WM_MENUCHAR

WM_COMMAND - посылается при выборе пользователем нужной строки -

wParam содержит идентификатор строки, определенный в шаблоне

меню.

ФУНКЦИИ ДЛЯ РАБОТЫ С МЕНЮ В ПРОГРАММЕ

СОЗДАНИЕ МЕНЮ.Приложение может создать меню с нуля при

отсутствии шаблона для любого окна кроме дочернего ф-циями:

HMENU   WINAPI CreateMenu(void);

HMENU   WINAPI CreatePopupMenu(void);

В дальнейшем можно добавить в меню верхнего уровня созданные

CreatePopupMenu временные меню или отдельные строки, вызвав

ф-цию AppendMenu. Перед завершением программы не забудьте

вызвать DestroyMenu. Для подключения к окну hwnd меню hmenu

можно воспользоваться ф-цией:

BOOL    WINAPI SetMenu(HWND, HMENU);

Перед вызовом этой ф-ции надо загрузить меню и получить его ид-р

например через LoadMenu.

ДОБАВЛЕНИЕ СТРОК. - можно осуществить с помощью

BOOL    WINAPI AppendMenu(HMENU, UINT, UINT, LPCSTR);

Второй параметр определяет атрибуты создаваемого меню из

следующего набора флагов, объединяемых логическим ИЛИ:

MF_BYCOMMAND

MF_BYPOSITION

MF_SEPARATOR для создания горизонтальной разделительной линии

MF_ENABLED   строка разблокирована

MF_GRAYED    отображ серым цветом и неактивна

MF_DISABLED  отображ нормально но неактивна

MF_UNCHECKED не отмечается галочкой

MF_CHECKED      отмечается галочкой

MF_USECHECKBITMAPS

MF_STRING    элемент есть строка, на которую указывает последний

параметр

MF_BITMAP    отображается графически. Младшее слово

последнего параметра содержит идентификатор изображения

MF_OWNERDRAW строка меню рисуется окном, создавшим меню - при

первом отображении ф-ция этого окна получит сообщение

WM_MEASUREITEM и ф-ция окна в ответ должна сообщить размеры

области, занимаемой строкой. Рисовать строку надо по сообщению

WM_DRAWITEM. Этот флаг - только для временных меню.

MF_POPUP с элементом связывается временное меню; 3-й параметр

должен содержать ид-р временного меню, связанного с этим

элементом.

MF_MENUBARBREAK элемент меню выводится с новой строки или нового

                столбца (для временного меню) с разел линией

MF_MENUBREAK  элемент меню выводится с новой строки или нового

              столбца (для временного меню)

MF_UNHILITE

MF_HILITE

MF_SYSMENU

MF_HELP

MF_MOUSESELECT

MF_END

Назначение 3-го параметра зависит от значения второго:

если второй не равен MF_POPUP то через него передается ид-р

создаваемой строки меню - он будет записан потом в wParam

сообщения WM_COMMAND при выборе данной строки. Если же 2-й

параметр MF_POPUP то через 3-й передается ид-р временного меню.

Назначение последнего параметра также зависит от 2-го: если 2-й

равен MF_STRING последний должен указывать на строку, если 2-й

равен MF_BITMAP то ьмладшее слово последнего содержит ид-р

изображения, если 2-й равен MF_OWNERDRAW то последний -

32-битовое значение, идентифицирующее строку меню.

Еще одна ф-ция служит для добавления элементов в меню:

BOOL    WINAPI InsertMenu(HMENU, UINT, UINT, UINT, LPCSTR);

2-й параметр определяет эл-т меню перед которым вставляется

новый. Если в 3-м параметре указан флаг MF_BYCOMMAND  то 2-й

параметр определяет ид-р элемента меню, перед которым будет

вставка, по флагу BY_POSITION 2-й параметр определяет номер

эл-та, перед которы будет вставка. Для добюавки эл-та в конец

меню 2-й параметр можно указать -1.

После внесения всех изменений в меню приложение должно вызвать

ф-цию

void    WINAPI DrawMenuBar(HWND);

ИЗМЕНЕНИЕ СТРОК. можно осуществить ф-цией

BOOL    WINAPI ModifyMenu(HMENU, UINT, UINT, UINT, LPCSTR);

ее параметры идентичны ф-ции InsertMenu. После внесения

изменений также надо вызвать DrawMenuBar

УДАЛЕНИЕ СТРОК МЕНЮ. осуществляет ф-ция

BOOL    WINAPI DeleteMenu(HMENU, UINT, UINT);

Если ее последний параметр содержит флаг MF_BYCOMMAND, то 2-й

параметр определяет ид-р удаляемого эл-та меню, а при флаге

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

временного меню все связанные с ним ресурсы освобождаются. Для

отображения результатов удаления также нужно вызвать

DrawMenuBar.

Можно использовать также ф-цию

BOOL    WINAPI RemoveMenu(HMENU, UINT, UINT);

Ее действие аналогично , но ресурсы не уничтожаются и удаленным

эл-том можно снова воспольэоваться , если знаем его ид-р.

АКТИВИЗАЦИЯ И БЛОКИРОВАНИЕ СТРОК МЕНЮ.

Для изменения состояния эл-та меню используют ф-цию

BOOL    WINAPI EnableMenuItem(HMENU, UINT, UINT);

2-й параметр опрделяет эл-т меню, над которым выполняется

операция, 3-й парметр может содержать флаги MF_DISABLED,

MF_ENABLED, MF_GRAYED в комбинации с MF_BYCOMMAND или

MF_BYPOSITION.

ОТМЕТКА СТРОК. Для включения и отключения пометки галочкой

используют:

BOOL    WINAPI CheckMenuItem(HMENU, UINT, UINT);

2-й параметр определяет эл-т меню, его интерпретация зависит от

3-го параметра , которы может содержать флаги MF_CHECKED,

MF_UNCHECKED в сочетании с  MF_BYCOMMAND или  MF_BYPOSITION.

ВЫДЕЛЕНИЕ СТРОК.

 BOOL    WINAPI HiliteMenuItem(HWND, HMENU, UINT, UINT);

Последний параметр может включать флаги MF_HILITE или

MF_UNHILITE в сочетании с  MF_BYCOMMAND или  MF_BYPOSITION.

ПОЛУЧЕНИЕ ИНФОРМАЦИИ:

HMENU   WINAPI GetMenu(HWND); -об идентификаторе меню

связанного с окном

HMENU   WINAPI GetSubMenu(HMENU, int); - об ид-ре временного

меню

int     WINAPI GetMenuItemCount(HMENU); - о количестве элементов

в меню

UINT    WINAPI GetMenuItemID(HMENU, int); - об идентификаторе

элемента меню

int     WINAPI GetMenuString(HMENU, UINT, LPSTR, int, UINT); -

для переписи в буфер текста строки меню

 UINT    WINAPI GetMenuState(HMENU, UINT, UINT); - о флагах

 состояния заданного элемента меню

СИСТЕМНОЕ МЕНЮ. Можно изменить системное меню добавлением к нему

новых строк или горизонтальных раделительных линий. Прежде всего

придется получить ид-р системного меню:

HMENU   WINAPI GetSystemMenu(HWND, BOOL);

2-й параметр определяет выполняемые ф-цией действия: если он

FALSE то возвращается ид-р используемой копии ситемного меню,

иначе ф-ция восстанавливает исходный вид системного меню и

возвращаемое значение не определено. После получения ид-ра можно

использовать ф-цииAppendMenu, InsertMenu, ModifyMenu. При

добавлении собственной строки в системное меню ее ид-р должен

быть больше 15 и не конфликтовать с уже существующими.

ПЛАВАЮЩЕЕ ВРЕМЕННОЕ МЕНЮ. Для его создания ид-р созданного и

наполненного временного меню передается ф-ции

 BOOL    WINAPI TrackPopupMenu(HMENU, UINT, int, int, int, HWND,

 const RECT FAR*);

Эта ф-ция выводит на экран плавающее меню и создает свой

собственный цикл обработки сообщений , завершающийся выбором

строки - до выбора или отказа от выбора из ф-ции выйти нельзя.

2-й параметр комбинацией флагов определяет расположение меню и

клавиши мыши для выбора. Набор флагов:

TPM_LEFTBUTTON- выбор левой клавишей

TPM_RIGHTBUTTON       правой

TPM_LEFTALIGN   выравнивание по левой границе относительно

                значения 3- го параметра -х

TPM_CENTERALIGN центровка относительно значения 3- го параметра

 -х

TPM_RIGHTALIGN  выравнивание по правой границе относительно

                значения 3- го параметра -х

5-й параметр резервный и дб равен 0. Последний параметр -

указатель на структуру типа RECT, определяющую координаты

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

выбор; если щелкнуть мышью за пределами этой области, меню

исчезнет с экрана. Если задать этот парметр NULL то размеры и

положение этой области совпадут с размерами и положением

плавающего меню.

АКСЕЛЕРАТОРЫ - для ускорения доступа к строкам меню по

комбинациям клавиш или для назначения функцуий используются

таблицы акселераторов, находящиеся в ресурсах приложения и

определяющие соответствие между комбинациями клавиш и значением

wParam сообщения WM_COMMAND. Например можно определить что

комбинации Ctrl+Ins соответствует значение wParam CM_EDITCUT-

и принажатии этой комбинациив ф-цию окна попадет WM_COMMAND с

wParam равным CM_EDITCUT.

Таблица акселераторов определяется в файле описания ресурсов :

<Id> ACCELERATORS

BEGIN

...

END

Между BEGIN-END располагаются строки описания акселераторов

вида:

Key, AccId, [KeyType[,]] [NOINVERT] [ALT] [SHIFT] [CONTROL]

Key - клавиша как символ в коде ASCII в двойных кавычках или

ASCII-код в виде целого или виртуальный код в символьном или

цифровом виде

AccId - соответствует значению wParam

KeyType - может быть или ASCII или VIRTKEY в зависимости от

способа формирования поля Key

Если указан NOINVERT строка не выделяется

 Если KeyType содержит VIRTKEY можно использовать  [ALT] [SHIFT]

 [CONTROL]

Для загрузки таблицы акселераторов используется ф-ция:

HACCEL  WINAPI LoadAccelerators(HINSTANCE, LPCSTR);

2-й параметр - указатель на строку идентификатора таблицы

акселераторов; если используется целое, оно ьдолжно

преобразовываться через MAKEINTRESOURCE. Загруженная таблица

уничтожается автоматически при завершении приложения.

Для использования акселераторов цикл обработки сообщений должен

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

while(Getmessage(&msg,0,0,0))

{

 if(!haccel || !TranslateAccelerator(hwnd,haccel,&msg) )

 {

   TranslateMessage(&msg);

   DispatchMessage(&msg);

}

 }

Старшее слово lParam WM_COMMAND содержит 1 для сообщений от

акселераторов и 0 для сообщений от меню.

ПРИМЕР 1 создает меню приложения из 3-х строк File Edit Help

При выборе каждой строки появляется одно из 3-х временных меню

// ----------------------------------------

// Работа с   меню

// ----------------------------------------

   #define STRICT

   #include <windows.h>

   #include <mem.h>

   #include "menu.hpp"

   // Прототипы функций

   BOOL InitApp(HINSTANCE);

   LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

   // Имя класса окна

   char const szClassName[]   = "MenuClass";

   // Заголовок окна

char const szWindowTitle[] = "Menu Demo";

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  // Записываем во все поля структуры нулевые значения

  memset(&wc, 0, sizeof(wc));

  // Подключаем меню

  wc.lpszMenuName  = "APP_MENU";

  wc.style         = 0;

  wc.lpfnWndProc   = (WNDPROC) WndProc;

  wc.cbClsExtra    = 0;

  wc.cbWndExtra    = 0;

  wc.hInstance     = hInstance;

  wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_COMMAND:

    {

      switch (wParam)

      {

        // Сообщения от меню

        case CM_HELPABOUT:

        case CM_HELPUSING_HELP:

        case CM_HELPPROCEDURES:

        case CM_HELPCOMMANDS:

        case CM_HELPKEYBOARD:

        case CM_HELPINDEX:

        case CM_EDITPASTE:

        case CM_EDITCOPY:

        case CM_EDITCUT:

        case CM_EDITUNDO:

        case CM_FILEPRINTER_SETUP:

        case CM_FILEPAGE_SETUP:

        case CM_FILEPRINT:

        case CM_FILESAVEAS:

        case CM_FILESAVE:

        case CM_FILEOPEN:

        case CM_FILENEW:

        {

          // На сообщение от любой строки меню кроме

          // завершающей работу программы реагируем

          // выводом сообщения об ошибке

               MessageBox(hwnd, "Функция не реализована",

                  NULL, MB_OK);

          return 0;

        }

        // Завершаем работу приложения

        case CM_FILEEXIT:

        {

          DestroyWindow(hwnd);

          return 0;

        }

        default:

          return 0;

      }

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

    default:

      break;

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

Файл menu.hpp

#define CM_HELPABOUT          24346

#define CM_HELPUSING_HELP     24345

#define CM_HELPPROCEDURES     24344

#define CM_HELPCOMMANDS       24343

#define CM_HELPKEYBOARD       24342

#define CM_HELPINDEX          24341

#define CM_EDITPASTE          24324

#define CM_EDITCOPY           24323

#define CM_EDITCUT            24322

#define CM_EDITUNDO           24321

#define CM_FILEEXIT           24338

#define CM_FILEPRINTER_SETUP  24337

#define CM_FILEPAGE_SETUP     24336

#define CM_FILEPRINT          24335

#define CM_FILESAVEAS         24334

#define CM_FILESAVE           24333

#define CM_FILEOPEN           24332

#define CM_FILENEW            24331

Файл описания ресурсов:

#include "menu.hpp"

APP_MENU MENU

BEGIN

        POPUP "&File"

        BEGIN

                MENUITEM "&New",              CM_FILENEW

                MENUITEM "&Open...",          CM_FILEOPEN

                MENUITEM "&Save",             CM_FILESAVE

                MENUITEM "Save &as...",       CM_FILESAVEAS

                MENUITEM SEPARATOR

                MENUITEM "&Print...",         CM_FILEPRINT

                MENUITEM "Page se&tup...",    CM_FILEPAGE_SETUP

                MENUITEM "P&rinter setup...", CM_FILEPRINTER_SETUP

                MENUITEM SEPARATOR

                MENUITEM "E&xit",             CM_FILEEXIT

        END

        POPUP "&Edit"

        BEGIN

                MENUITEM "&Undo\tCtrl+Z",     CM_EDITUNDO

                MENUITEM "&Cut\tCtrl+X",      CM_EDITCUT

                MENUITEM "&Copy\tCtrl+C",     CM_EDITCOPY

                MENUITEM "&Paste\tCtrl+V",    CM_EDITPASTE

        END

        POPUP "&Help"

        BEGIN

                MENUITEM "&Index\tF1",  CM_HELPINDEX,      INACTIVE

                MENUITEM "&Keyboard",   CM_HELPKEYBOARD,   INACTIVE

                MENUITEM "&Commands",   CM_HELPCOMMANDS,   GRAYED

                MENUITEM "&Procedures", CM_HELPPROCEDURES, GRAYED

                MENUITEM "&Using help", CM_HELPUSING_HELP, GRAYED

                MENUITEM SEPARATOR

                MENUITEM "&About...",   CM_HELPABOUT

        END

END

ПРИМЕР 2 имитирует работу с документами и использует большинство

описанных выше функций для динамического создания и изменения

меню. Проект этого приложения не включает файл описания ресурсов

и не использует шаблон меню. Сразу после запуска в полосе меню

опрделены 2 временных меню File и Help.В меню File строки New и

Open предназначены для создания нового документа или загрузки

его из файла, Exit для завершения.Строка  Demo Version

заблокирована и отмечена галочкой. Строки Print Page Setup

Printer Setup заблокированы и отображаются серым цветом. До

создания или загрузки документа строки Close Save Save As

заблокированы. Приложение расчитано на работу с одним документом

, поэтому после загрузки строки New и Open блокируются. Меню

Edit появляется только после загрузки документа.

// ----------------------------------------

// Создание меню без использования шаблона

// Динамическое изменение меню

// ----------------------------------------

#define STRICT

#include <windows.h>

#include <mem.h>

#include "dmenu.hpp"

// Прототипы функций

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

// Имя класса окна

char const szClassName[]   = "DMenuClass";

// Заголовок окна

char const szWindowTitle[] = "Menu Demo";

// Идентификатор меню верхнего уровня

HMENU hmenu;

// Идентификаторы временных меню

HMENU hmenuFile; // "File"

HMENU hmenuEdit; // "Edit"

HMENU hmenuHelp; // "Help"

// =====================================

// Функция WinMain

// =====================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    0,                   // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    DispatchMessage(&msg);

  }

  return msg.wParam;

}

// =====================================

// Функция InitApp

// Выполняет регистрацию класса окна

// =====================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации

                  // класса окна

  // Записываем во все поля структуры нулевые значения

  memset(&wc, 0, sizeof(wc));

  wc.lpszMenuName  = NULL;

  wc.style         = 0;

  wc.lpfnWndProc   = (WNDPROC) WndProc;

  wc.cbClsExtra    = 0;

  wc.cbWndExtra    = 0;

  wc.hInstance     = hInstance;

  wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);

  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// =====================================

// Функция WndProc

// =====================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем пустое меню верхнего уровня

      hmenu = CreateMenu();

      // Подключаем меню к главному окну приложения

      SetMenu(hwnd, hmenu);

      // Создаем два временных меню - "File" и "Help"

      hmenuFile = CreatePopupMenu();

      hmenuHelp = CreatePopupMenu();

      // Добавляем строки к меню "File"

      AppendMenu(hmenuFile, MF_ENABLED | MF_STRING,

         CM_FILENEW,    "&New");

      AppendMenu(hmenuFile, MF_ENABLED | MF_STRING,

         CM_FILEOPEN,   "&Open");

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILECLOSE,  "&Close");

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILESAVE,   "&Save");

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILESAVEAS, "Save &as...");

      // Добавляем разделительную линию

      AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILEPRINT,         "&Print");

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILEPAGE_SETUP,    "Page Se&tup");

      AppendMenu(hmenuFile, MF_GRAYED | MF_STRING,

         CM_FILEPRINTER_SETUP, "P&rinter Setup");

      AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

      AppendMenu(hmenuFile, MF_DISABLED | MF_STRING,

         CM_FILEDEMO, "&Demo Version");

      AppendMenu(hmenuFile, MF_SEPARATOR, 0, NULL);

      AppendMenu(hmenuFile, MF_ENABLED | MF_STRING,

         CM_FILEEXIT, "E&xit");

      // Отмечаем галочкой строку "Demo Version"

      CheckMenuItem(hmenuFile, CM_FILEDEMO,

         MF_BYCOMMAND | MF_CHECKED);

      // Добавляем строки к меню "Help"

      AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING,

         CM_HELPINDEX, "&Index\tF1");

      AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING,

         CM_HELPKEYBOARD, "&Keyboard");

      AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING,

         CM_HELPCOMMANDS, "&Commands");

      AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING,

         CM_HELPPROCEDURES, "&Procedures");

      AppendMenu(hmenuHelp, MF_GRAYED | MF_STRING,

         CM_HELPUSING_HELP, "&Using help");

      AppendMenu(hmenuHelp, MF_SEPARATOR, 0, NULL);

      AppendMenu(hmenuHelp, MF_ENABLED | MF_STRING,

         CM_HELPABOUT, "&About...");

      // Добалвяем временные меню к меню верхнего уровня

      AppendMenu(hmenu, MF_ENABLED | MF_POPUP,

         (UINT)hmenuFile, "&File");

      AppendMenu(hmenu, MF_ENABLED | MF_POPUP,

         (UINT)hmenuHelp, "&Help");

      // Перерисовываем меню

      DrawMenuBar(hwnd);

      // Записываем в идентификатор меню "Edit" значение

      // NULL. Если это меню не будет создано, мы не будем

      // вызывать функцию DestroyMenu для его уничтожения

      hmenuEdit = NULL;

      return 0;

    }

    case WM_COMMAND:

    {

      switch (wParam)

      {

        // Сообщения от меню

        case CM_HELPUSING_HELP:

        case CM_HELPPROCEDURES:

        case CM_HELPCOMMANDS:

        case CM_HELPKEYBOARD:

        case CM_HELPINDEX:

        case CM_EDITPASTE:

        case CM_EDITCOPY:

        case CM_EDITCUT:

        case CM_EDITUNDO:

        case CM_FILEPRINTER_SETUP:

        case CM_FILEPAGE_SETUP:

        case CM_FILEPRINT:

        case CM_FILESAVEAS:

        case CM_FILESAVE:

        {

          // Выводим сообщение об ошибке

          MessageBox(hwnd, "Функция не реализована",

                  NULL, MB_OK);

          return 0;

        }

        // Выбрали строку "About..." в меню "Help"

        case CM_HELPABOUT:

        {

          MessageBox(hwnd, "Приложение DMENU\n(C) Фролов А.В., 1994",

                  szWindowTitle, MB_OK | MB_ICONINFORMATION);

          return 0;

        }

        // Выбрали строки "Open" или "New" в меню "File"

        case CM_FILEOPEN:

        case CM_FILENEW:

        {

          // Создаем временное меню "Edit"

          hmenuEdit = CreatePopupMenu();

          // Добавляем строки в меню "Edit"

          AppendMenu(hmenuEdit, MF_GRAYED  | MF_STRING,

             CM_EDITUNDO,  "&Undo\tCtrl+Z");

          AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,

             CM_EDITCUT,   "&Cut\tCtrl+X");

          AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,

             CM_EDITCOPY,  "&Copy\tCtrl+C");

          AppendMenu(hmenuEdit, MF_ENABLED | MF_STRING,

             CM_EDITPASTE, "&Paste\tCtrl+V");

          // Вставляем меню "Edit" между меню "File" и меню "Help"

          InsertMenu(hmenu, 1, MF_BYPOSITION | MF_ENABLED | MF_POPUP,

             (UINT)hmenuEdit, "&Edit");

          // Разблокируем строки "Save", "Save as..." и "Close" в меню "File"

          EnableMenuItem(hmenuFile, CM_FILESAVE,

             MF_ENABLED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILESAVEAS,

             MF_ENABLED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILECLOSE,

             MF_ENABLED | MF_BYCOMMAND);

          // Блокируем строки "New" и "Open" в меню "File"

          EnableMenuItem(hmenuFile, CM_FILENEW,

             MF_GRAYED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILEOPEN,

             MF_GRAYED | MF_BYCOMMAND);

          // Перерисовываем меню

          DrawMenuBar(hwnd);

          return 0;

        }

        // Выбрали строку "Close" из меню "File"

        case CM_FILECLOSE:

        {

          // Уничтожаем временное меню "Edit"

          DestroyMenu(hmenuEdit);

          hmenuEdit = NULL;

          // Удаляем соответствующую строку в меню

          // верхнего уровня

          RemoveMenu(hmenu, 1, MF_BYPOSITION);

          // Блокируем строки "Save", "Save as..." и "Close" в меню "File"

          EnableMenuItem(hmenuFile, CM_FILESAVE,

             MF_GRAYED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILESAVEAS,

             MF_GRAYED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILECLOSE,

             MF_GRAYED | MF_BYCOMMAND);

          // Разблокируем строки "New" и "Open" в меню "File"

          EnableMenuItem(hmenuFile, CM_FILENEW,

             MF_ENABLED | MF_BYCOMMAND);

          EnableMenuItem(hmenuFile, CM_FILEOPEN,

             MF_ENABLED | MF_BYCOMMAND);

          // Перерисовываем меню

          DrawMenuBar(hwnd);

          return 0;

        }

        // Завершаем работу приложения

        case CM_FILEEXIT:

        {

          DestroyWindow(hwnd);

          return 0;

        }

        default:

          return 0;

      }

    }

    case WM_DESTROY:

    {

      // Если было создано меню "Edit",

      // уничтожаем его

      if(hmenuEdit != NULL)

      {

        DestroyMenu(hmenuEdit);

      }

      // Уничтожаем созданные ранее меню

      DestroyMenu(hmenuFile);

      DestroyMenu(hmenuHelp);

      DestroyMenu(hmenu);

      PostQuitMessage(0);

      return 0;

    }

    default:

      break;

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

Файл dmenu.hpp

#define CM_HELPABOUT          24346

#define CM_HELPUSING_HELP     24345

#define CM_HELPPROCEDURES     24344

#define CM_HELPCOMMANDS       24343

#define CM_HELPKEYBOARD       24342

#define CM_HELPINDEX          24341

#define CM_EDITPASTE          24324

#define CM_EDITCOPY           24323

#define CM_EDITCUT            24322

#define CM_EDITUNDO           24321

#define CM_FILEEXIT           24338

#define CM_FILEPRINTER_SETUP  24337

#define CM_FILEPAGE_SETUP     24336

#define CM_FILEPRINT          24335

#define CM_FILESAVEAS         24334

#define CM_FILESAVE           24333

#define CM_FILEOPEN           24332

#define CM_FILENEW            24331

#define CM_FILECLOSE          24330

#define CM_FILEDEMO           24329

 ПРИМЕР 3 реализует основные ф-ции, связанные с редактированием

 текста (за исключением работы с принтерами и шрифтами и

 функций меню Help). Приложение имеет Toolbar, использует

 акселераторы для доступа к основным функциям, модифицирует

 системное меню и по щелчку правой кнопки мыши создает в окне

 редактирования плавающее меню. Демонстрируется не только

 способы работы с различными типами меню, но и способ перехвата

 управления у функции окна органа управления класса edit.

 Орган управления Toolbar выделен в отдельный класс, исходные

 тексты которого находятся в 2-х файлах и могут ьиспользоваться

 в приложениях для создания других Toolbar.

 В области глобальных переменных кроме прочих расположен

 указатель на класс Toolbar *tb; чк\ерез этот указатель

 выполняется одна из определенных для этого класса операций -

 создание кнопки в заданной позиции.

 переменная bNeedSave используется как признак необходимости

 сохранения редактируемого текста.

 В массиве szCurrentFileName[128] хранися путь к редактируемому

 файлу.

 После отображения главного окна создается орган управления

 Toolbar:

 tb = new Toolbar(hInstance,hwnd,TB_FIRST);

 Константа TB_FIRST огпределяет значение wParam сообщения

 WM_COMMAND для самой левой кнопки Toolbar. затем ф-ция Winmain

 создает все необходимые кнопки вызовом метода Tb->InsertButton,

 определенного в классе Toolbar. Первый параметр этого метода

 определяет расположение кнопки в выделенной под Toolbar

 области; 2-й,3-й,4-й параметры должны содержать ид-ры

 изображений bitmap для кнопки в нормальном , нажатом и

 неактивном состояниях.

 Для установки ширины дочернего окна Toolbar определена ф-ция

 SetWidth. Она вызывается в ф-ции главного окна приложения

 при обработке сообщения WM_SIZE.

 После создания Toolbar WinMain загружает с помощью

 LoadAccelerators таблицу акселераторов и запускает цикл

 обработки сообщений. Новая ф-ция окна для редактора текста

 EditWndProc перехватывает сообщение WM_RBUTTONDOWN и выводит

 плавающее меню. Координаты курсора мыши передаются через lParam

 относительно левого верхнего угла внутренней ьобласти окна; т к

 ф-ция TrackPopupMenu использует экранные координаты,

 выполняется преобразование через ClientToScreen. Все другие

 сообщения через переходник lpfnEditOldWndProc передаются

 стандартной ф-ции окна:

 return CallWindowProc(lpfnEditOldWndProc,hwnd, msg, wParam,

 lParam);

Файл smartpad.hpp содержит определения всех используемых

символических констант и приведен ниже.

Определение класса Toolbar и класса TbMain размещено в файлах

toolbar.hpp и toolbar.cpp, тексты которых приводятся ниже.

Функция рисования кнопок размещена в файле drawbmp.cpp.

#define STRICT

#include <windows.h>

#include <commdlg.h>

#include <mem.h>

#include <string.h>

#include <stdlib.h>

#include "toolbar.hpp"

#include "smartpad.hpp"

// ======================================================

// Прототипы функций

// ======================================================

BOOL InitApp(HINSTANCE);

LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

LRESULT CALLBACK _export

EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

HFILE OpenFile(void);

HFILE OpenSaveFile(void);

int SaveFileAs(HWND hwnd);

int SaveFile(HWND hwnd);

int SelectFile(HWND hwnd);

// ======================================================

// Глобальные переменные

// ======================================================

// Имя класса окна

char const szClassName[]   = "SmartPadAppClass";

// Заголовок окна

char const szWindowTitle[] = "Smart Pad";

// Глобальная переменная для хранения идентификатора

// текущей копии приложения

HINSTANCE hInst;

// Указатель на объект органа управления TOOLBAR

Toolbar *Tb;

// Переменные для хранения идентификаторов меню

HMENU hmenuAppMenu;

HMENU hmenuSystemMenu;

// Идентификатор таблицы акселераторов

HACCEL haccel;

// Идентификатор редактора текста

HWND hEdit;

// Признак внесения изменений в текст

BOOL bNeedSave;

// Путь к редактируемому файлу

char szCurrentFileName[128];

// Временный буфер

char szTempBuffer[128];

// Признак запрета редактирования

BOOL bReadOnly = FALSE;

// Идентификаторы файлов

HFILE hfSrcFile, hfDstFile;

// Переменные для хранения адресов функций

DLGPROC lpfnDlgProc;

WNDPROC lpfnEditOldWndProc;

WNDPROC lpfnEditWndProc;

// Идентификатор главного окна

HWND    hwndMain;

// ======================================================

// Функция WinMain

// ======================================================

#pragma argsused

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR     lpszCmdLine,

        int       nCmdShow)

{

  MSG  msg;   // структура для работы с сообщениями

  HWND hwnd;  // идентификатор главного окна приложения

  // Сохраняем идентификатор текущей копии приложения

  hInst = hInstance;

  // Инициализируем приложение

  if(!InitApp(hInstance))

      return FALSE;

  // Загружаем основное меню приложения

  hmenuAppMenu = LoadMenu(hInstance, "APP_MENU");

  // После успешной инициализации приложения создаем

  // главное окно приложения

  hwnd = CreateWindow(

    szClassName,         // имя класса окна

    szWindowTitle,       // заголовок окна

    WS_OVERLAPPEDWINDOW, // стиль окна

    CW_USEDEFAULT,       // задаем размеры и расположение

    CW_USEDEFAULT,       // окна, принятые по умолчанию

    CW_USEDEFAULT,

    CW_USEDEFAULT,

    0,                   // идентификатор родительского окна

    hmenuAppMenu,        // идентификатор меню

    hInstance,           // идентификатор приложения

    NULL);               // указатель на дополнительные

                         // параметры

  // Если создать окно не удалось, завершаем приложение

  if(!hwnd)

    return FALSE;

  // Сохраняем идентификатор главного окна в

  // глобальной переменной

  hwndMain = hwnd;

  // Создаем орган управления TOOLBAR

  Tb = new Toolbar(hInstance, hwnd, TB_FIRST);

  // Рисуем главное окно

  ShowWindow(hwnd, nCmdShow);

  UpdateWindow(hwnd);

  // Создаем кнопки в органе управления TOOLBAR

  Tb->InsertButton(0,

     MAKEINTRESOURCE(IDB_NEWUP),

     MAKEINTRESOURCE(IDB_NEWDOWN), MAKEINTRESOURCE(IDB_NEWUP));

  Tb->InsertButton(1,

     MAKEINTRESOURCE(IDB_OPENUP),

     MAKEINTRESOURCE(IDB_OPENDOWN), MAKEINTRESOURCE(IDB_OPENGR));

  Tb->InsertButton(2,

     MAKEINTRESOURCE(IDB_SAVEUP),

     MAKEINTRESOURCE(IDB_SAVEDOWN), MAKEINTRESOURCE(IDB_SAVEGR));

  Tb->InsertButton(4,

     MAKEINTRESOURCE(IDB_CUTUP),

     MAKEINTRESOURCE(IDB_CUTDOWN), MAKEINTRESOURCE(IDB_CUTGR));

  Tb->InsertButton(5,

     MAKEINTRESOURCE(IDB_COPYUP),

     MAKEINTRESOURCE(IDB_COPYDOWN), MAKEINTRESOURCE(IDB_COPYGR));

  Tb->InsertButton(6,

     MAKEINTRESOURCE(IDB_PASTUP),

     MAKEINTRESOURCE(IDB_PASTDOWN), MAKEINTRESOURCE(IDB_PASTGR));

  Tb->InsertButton(7,

     MAKEINTRESOURCE(IDB_UNDOUP),

     MAKEINTRESOURCE(IDB_UNDODOWN), MAKEINTRESOURCE(IDB_UNDOGR));

  Tb->InsertButton(9,

     MAKEINTRESOURCE(IDB_EXITUP),

     MAKEINTRESOURCE(IDB_EXITDOWN), MAKEINTRESOURCE(IDB_EXITGR));

  Tb->InsertButton(10,

     MAKEINTRESOURCE(IDB_HELPUP),

     MAKEINTRESOURCE(IDB_HELPDOWN), MAKEINTRESOURCE(IDB_HELPGR));

  // Загружаем таблицу акселераторов

  haccel = LoadAccelerators(hInstance, "APP_ACCELERATORS");

  // Запускаем цикл обработки сообщений

  while(GetMessage(&msg, 0, 0, 0))

  {

    if(!haccel || !TranslateAccelerator(hwnd, haccel, &msg))

    {

      TranslateMessage(&msg);

      DispatchMessage(&msg);

    }

  }

  return msg.wParam;

}

// ======================================================

// Функция InitApp

// Выполняет регистрацию класса окна

// ======================================================

BOOL

InitApp(HINSTANCE hInstance)

{

  ATOM aWndClass; // атом для кода возврата

  WNDCLASS wc;    // структура для регистрации класса окна

  // Записываем во все поля структуры нулевые значения

  memset(&wc, 0, sizeof(wc));

  wc.lpszMenuName  = NULL;

  wc.style         = 0;

  wc.lpfnWndProc   = (WNDPROC) WndProc;

  wc.cbClsExtra    = 0;

  wc.cbWndExtra    = 0;

  wc.hInstance     = hInstance;

  wc.hIcon         = LoadIcon(hInstance, "APPICON");

  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

  wc.lpszClassName = (LPSTR)szClassName;

  // Регистрация класса

  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);

}

// ======================================================

// Новая функция окна для редактора текста

// ======================================================

LRESULT CALLBACK _export

EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  // Если в окне редактора текста пользователь нажал

  // правую клавишу мыши, выводим в позиции курсора мыши

  // плавающее меню

  if(msg == WM_RBUTTONDOWN)

  {

    HMENU hmenuPopup;

    POINT pt;

    // Перобразуем координаты курсора мыши в экранные

    pt = MAKEPOINT(lParam);

    ClientToScreen(hwnd, &pt);

    // Создаем пустое временное меню

    hmenuPopup = CreatePopupMenu();

    // Заполняем временное меню

    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,

         CM_FILENEW, "&New");

    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,

         CM_FILEOPEN, "&Open");

    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,

         CM_FILESAVE, "&Save");

    AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);

    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,

         CM_FILEEXIT, "E&xit");

    // Выводим плавающее меню в позиции курсора мыши

    TrackPopupMenu(hmenuPopup,

      TPM_CENTERALIGN | TPM_LEFTBUTTON,

      pt.x, pt.y, 0, hwndMain, NULL);

    // Удаляем временное меню

    DestroyMenu(hmenuPopup);

  }

  // Вызываем старую функцию окна редактора текста

  return CallWindowProc(lpfnEditOldWndProc,

     hwnd, msg, wParam, lParam);

}

// ======================================================

// Функция главного окна приложения WndProc

// ======================================================

LRESULT CALLBACK _export

WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  HFONT hfont;

  switch (msg)

  {

    case WM_CREATE:

    {

      // Создаем редактор текста

      hEdit = CreateWindow("edit", NULL,

         WS_CHILD | WS_VISIBLE | WS_BORDER |

         WS_HSCROLL | WS_VSCROLL |

         ES_LEFT | ES_AUTOHSCROLL | ES_AUTOVSCROLL |

         ES_MULTILINE,

         0, 30, 100, 100,

         hwnd, (HMENU) ID_EDIT, hInst, NULL);

      // Задаем для редактора текста шрифт с

      // переменной шириной символов

      hfont = GetStockFont(ANSI_VAR_FONT);

      SendMessage(hEdit, WM_SETFONT, (WPARAM)hfont,

        (LPARAM)MAKELONG((WORD)TRUE, 0));

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

      lpfnEditWndProc =

         (WNDPROC)MakeProcInstance((FARPROC)EditWndProc, hInst);

      // Определяем адрес старой функции окна редактора текста

      lpfnEditOldWndProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);

      // Подключаем к редактору текста новую функцию окна

      SetWindowLong(hEdit, GWL_WNDPROC, (LONG)lpfnEditWndProc);

      // Устанавливаем максимальную длину

      // редактируемого текста, равную 32000 байт

      SendMessage(hEdit, EM_LIMITTEXT, 32000, 0L);

      // Сбрасываем флаг обновления текста и флаг

      // запрета редактирования

      bNeedSave = FALSE;

      bReadOnly = FALSE;

      // Так как редактируемый файл не открывался и не сохранялся,

      // в переменную пути к нему записываем пустую строку

      lstrcpy(szCurrentFileName, "");

      // Устанавливаем заголовок окна приложения

      SetWindowText(hwnd, "SmartPad - [UNTITLED]");

      // Определяем идентификатор системного меню

      hmenuSystemMenu = GetSystemMenu(hwnd, FALSE);

      // Добавляем в системное меню разделительную линию

      // и строку "About"

      AppendMenu(hmenuSystemMenu, MF_SEPARATOR, 0, 0);

      AppendMenu(hmenuSystemMenu, MF_BYCOMMAND | MF_ENABLED,

         CM_SYSABOUT, "&About...");

      // Блокируем в системном меню строку "Close"

      EnableMenuItem(hmenuSystemMenu, SC_CLOSE,

        MF_BYCOMMAND | MF_GRAYED);

      return 0;

    }

    case WM_SIZE:

    {

      // Устанавливаем новую ширину дочернего окна

      // орагна управления TOOLBAR

      Tb->SetWidth(LOWORD(lParam));

      // Устанавливаем размер органа управления

      // (текстового редактора) в соотвестствии

      // с размерами главного окна приложения

      MoveWindow(hEdit, 0, 26, LOWORD(lParam),

        HIWORD(lParam) - 26, TRUE);

      return 0;

    }

    // Когда главное окно приложения получает

    // фокус ввода, отдаем фокус редактору текста

    case WM_SETFOCUS:

    {

      SetFocus(hEdit);

      return 0;

    }

    // Это сообщение приходит от системного меню

    case WM_SYSCOMMAND:

    {

      // Необходимо замаскировать четыре младших бита

      // параметра wParam

      if((wParam & 0xfff0) == CM_SYSABOUT)

      {

        // Переходник для функции диалоговой панели

        lpfnDlgProc = (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

        // Создаем модальную диалоговую панель

        DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);

        return 0;

      }

      // Блокируем строку "Close". Эта строка не является

      // обязательной, так как мы уже заблокировали эту

      // строку функцией EnableMenuItem

      else if((wParam & 0xfff0) == SC_CLOSE)

        return 0;

      break;

    }

    // Сообщение от меню и орагна управления TOOLBAR

    case WM_COMMAND:

    {

       switch(wParam)

       {

         // Обработка извещений текстового редактора

         case ID_EDIT:

         {

           // Ошибка

           if(HIWORD(lParam) == EN_ERRSPACE)

           {

             MessageBox(hwnd, "Мало памяти",

               szWindowTitle, MB_OK);

           }

           // Произошло изменение в редактируемом

           // тексте

           else if(HIWORD(lParam) == EN_UPDATE)

           {

             // Устанавливаем флаг обновления текста

             bNeedSave = TRUE;

           }

           return 0;

         }

         // Эти строки меню пока заблокированы, так как

         // соответствующие функции не реализованы

         case CM_HELPUSING_HELP:

         case CM_HELPPROCEDURES:

         case CM_HELPCOMMANDS:

         case CM_HELPKEYBOARD:

         case CM_HELPINDEX:

         case CM_FILEPRINTER_SETUP:

         case CM_FILEPAGE_SETUP:

         case CM_FILEPRINT:

         {

           MessageBox(hwnd, "В текущей версии "

                "редактора SmartPad данная функция не реализована",

                NULL, MB_OK);

           return 0;

         }

         // На запрос подсказки выводим диалоговую панель

         // с информацией о программе

         case TB_HELP:

         case CM_HELPABOUT:

         {

           // Переходник для функции диалоговой панели

           lpfnDlgProc = (DLGPROC)MakeProcInstance((FARPROC)DlgProc, hInst);

           // Создаем модальную диалоговую панель

           DialogBox(hInst, "ABOUT", hwnd, lpfnDlgProc);

           // Ликвидируем переходник

           FreeProcInstance((FARPROC) lpfnDlgProc);

           return 0;

         }

         // Переключение режима запрета редактирования

         case CM_EDITSETREADONLY:

         {

           // Если режим запрета редактирования выключен,

           // включаем его

           if(!bReadOnly)

           {

             // Запрещаем редактирование

             SendMessage(hEdit, EM_SETREADONLY, TRUE, 0L);

             // Отмечаем соответствующую строку в меню

             CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY,

               MF_BYCOMMAND | MF_CHECKED);

             // Устанавливаем флаг запрета редактирования

             bReadOnly = TRUE;

           }

           // Если режим запрета редактирования включен,

           // выключаем его

           else

           {

             SendMessage(hEdit, EM_SETREADONLY, FALSE, 0L);

             CheckMenuItem(hmenuAppMenu, CM_EDITSETREADONLY,

               MF_BYCOMMAND | MF_UNCHECKED);

             bReadOnly = FALSE;

           }

           // Устанавливаем фокус ввода на редактор текста

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITPASTE:

         case TB_PAST:

         {

           SendMessage(hEdit, WM_PASTE, 0, 0L);

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITCOPY:

         case TB_COPY:

         {

           SendMessage(hEdit, WM_COPY, 0, 0L);

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITCUT:

         case TB_CUT:

         {

           SendMessage(hEdit, WM_CUT, 0, 0L);

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITCLEAR:

         {

           SendMessage(hEdit, WM_CLEAR, 0, 0L);

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITSELALL:

         {

           SendMessage(hEdit, EM_SETSEL, 0, MAKELPARAM(0, -1));

           SetFocus(hEdit);

           return 0;

         }

         case CM_EDITUNDO:

         case TB_UNDO:

         {

           SendMessage(hEdit, EM_UNDO, 0, 0L);

           SetFocus(hEdit);

           return 0;

         }

         // Завершаем работу приложения

         case TB_EXIT:

         case CM_FILEEXIT:

         {

           // Проверяем флаг обновления

           if(bNeedSave)

           {

             // Если в тексте были изменения,

             // спрашиваем у пользователя, надо ли

             // созранять текст в файле

             if(IDYES == MessageBox(hwnd,

                 "Файл был изменен. Желаете сохранить?",

                  szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             {

               // Если файл ни разу не сохранялся,

               // спрашиваем путь и имя нового файла

               if(szCurrentFileName[0] == '\0')

               {

                 SaveFileAs(hwnd);

                 // Изменяем заголовок главного окна приложения

                 // в соответствии с именем и путем к файлу

                 wsprintf(szTempBuffer, "SmartPad - [%s]",

                    (LPSTR)szCurrentFileName);

                 SetWindowText(hwnd, szTempBuffer);

               }

               // Если файл уже сохранялся, записываем его

               // на прежнее место

               else

                 SaveFile(hwnd);

               }

             }

           // Завершаем работу приложения

           DestroyWindow(hwnd);

           return 0;

         }

         case CM_FILENEW:

         case TB_NEW:

         {

           // Проверяем флаг обновления

           if(bNeedSave)

           {

             if(IDYES == MessageBox(hwnd,

                 "Файл был изменен. Желаете сохранить?",

                  szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             {

               if(szCurrentFileName[0] == '\0')

               {

                 SaveFileAs(hwnd);

                 wsprintf(szTempBuffer, "SmartPad - [%s]",

                   (LPSTR)szCurrentFileName);

                 SetWindowText(hwnd, szTempBuffer);

               }

               else

                 SaveFile(hwnd);

             }

           }

           // Сбрасываем содержимое текстового редактора

           SetWindowText(hEdit, "\0");

           bNeedSave = FALSE;

           lstrcpy(szCurrentFileName, "");

           SetWindowText(hwnd, "SmartPad - [UNTITLED]");

           SetFocus(hEdit);

           return 0;

         }

         case CM_FILEOPEN:

         case TB_OPEN:

         {

           // Проверяем флаг обновления

           if(bNeedSave)

           {

             if(IDYES == MessageBox(hwnd,

                 "Файл был изменен. Желаете сохранить?",

                  szWindowTitle, MB_YESNO | MB_ICONQUESTION))

             SaveFile(hwnd);

           }

           if(!SelectFile(hwnd))

           {

             lstrcpy(szCurrentFileName, "");

             SetWindowText(hwnd, "SmartPad - [UNTITLED]");

           }

           else

           {

             wsprintf(szTempBuffer, "SmartPad - [%s]",

               (LPSTR)szCurrentFileName);

             SetWindowText(hwnd, szTempBuffer);

           }

           return 0;

         }

         case CM_FILESAVEAS:

         {

           if(SaveFileAs(hwnd))

           {

             wsprintf(szTempBuffer, "SmartPad - [%s]",

               (LPSTR)szCurrentFileName);

             SetWindowText(hwnd, szTempBuffer);

           }

           else

           {

             lstrcpy(szCurrentFileName, "");

             SetWindowText(hwnd, "SmartPad - [UNTITLED]");

           }

           return 0;

         }

         case CM_FILESAVE:

         case TB_SAVE:

         {

           if(szCurrentFileName[0] == '\0')

           {

             if(SaveFileAs(hwnd))

             {

               wsprintf(szTempBuffer, "SmartPad - [%s]",

                 (LPSTR)szCurrentFileName);

               SetWindowText(hwnd, szTempBuffer);

             }

             else

             {

               lstrcpy(szCurrentFileName, "");

               SetWindowText(hwnd, "SmartPad - [UNTITLED]");

             }

           }

           else

             SaveFile(hwnd);

           return 0;

         }

         default:

           break;

       }

       break;

    }

    // Это сообщение приходит при завершении работы

    // операционной системы Windows. Если мы не сохранили

    // редактируемый текст, спрашиваем у пользователя,

    // надо ли это делать

    case WM_QUERYENDSESSION:

    {

      // Проверяем флаг обновления

      if(bNeedSave)

      {

        if(IDYES == MessageBox(hwnd,

            "Файл был изменен. Желаете сохранить?",

             szWindowTitle, MB_YESNO | MB_ICONQUESTION))

        {

           if(szCurrentFileName[0] == '\0')

           {

              SaveFileAs(hwnd);

              wsprintf(szTempBuffer, "SmartPad - [%s]",

                (LPSTR)szCurrentFileName);

              SetWindowText(hwnd, szTempBuffer);

           }

           else

              SaveFile(hwnd);

        }

      }

      // Разрешаем операционной системе Windows

      // завершить свою работу

      return 1L;

    }

    case WM_DESTROY:

    {

      PostQuitMessage(0);

      return 0;

    }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// ======================================================

// Функция OpenFile

// Сохранение файла

// ======================================================

HFILE OpenFile(void)

{

  // Структура для выбора файла

  OPENFILENAME ofn;

  // Буфер для записи пути к выбранному файлу

  char szFile[256];

  // Буфер для записи имени выбранного файла

  char szFileTitle[256];

  // Фильтр расширений имени файлов

  char szFilter[256] =

         "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";

  // Идентификатор открываемого файла

  HFILE hf;

  // Инициализация имени выбираемого файла

  // не нужна, поэтому создаем пустую строку

  szFile[0] = '\0';

  // Записываем нулевые значения во все поля

  // структуры, которая будет использована для

  // выбора файла

  memset(&ofn, 0, sizeof(OPENFILENAME));

  // Инициализируем нужные нам поля

  ofn.lStructSize       = sizeof(OPENFILENAME);

  ofn.hwndOwner         = NULL;

  ofn.lpstrFilter       = szFilter;

  ofn.nFilterIndex      = 1;

  ofn.lpstrFile         = szFile;

  ofn.nMaxFile          = sizeof(szFile);

  ofn.lpstrFileTitle    = szFileTitle;

  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  ofn.lpstrInitialDir   = NULL;

  ofn.Flags =   OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST

                                  | OFN_HIDEREADONLY;

  // Выбираем входной файл

  if (GetOpenFileName(&ofn)) {

    // Открываем выбранный файл

    hf = _lopen(ofn.lpstrFile, OF_READ);

    // Сохраняем путь к открытому файлу

    lstrcpy(szCurrentFileName, ofn.lpstrFile);

    // Возвращаем идентификатор файла

    return hf;

  }

  // При отказе от выбора возвращаем

  // нулевое значение

  else return 0;

}

// ======================================================

// Функция OpenSaveFile

// Выбор файла для редактирования

// ======================================================

HFILE OpenSaveFile(void)

{

  OPENFILENAME ofn;

  char szFile[256];

  char szFileTitle[256];

  char szFilter[256] =

         "Text Files\0*.txt\0Any Files\0*.*\0";

  HFILE hf;

  szFile[0] = '\0';

  memset(&ofn, 0, sizeof(OPENFILENAME));

  ofn.lStructSize       = sizeof(OPENFILENAME);

  ofn.hwndOwner         = NULL;

  ofn.lpstrFilter       = szFilter;

  ofn.nFilterIndex      = 1;

  ofn.lpstrFile         = szFile;

  ofn.nMaxFile          = sizeof(szFile);

  ofn.lpstrFileTitle    = szFileTitle;

  ofn.nMaxFileTitle     = sizeof(szFileTitle);

  ofn.lpstrInitialDir   = NULL;

  ofn.Flags             = OFN_HIDEREADONLY;

  // Выбираем выходной файл

  if (GetSaveFileName(&ofn)) {

    // При необходимости создаем файл

    hf = _lcreat(ofn.lpstrFile, 0);

    // Сохраняем путь к файлу

    lstrcpy(szCurrentFileName, ofn.lpstrFile);

    return hf;

  }

  else

    return 0;

}

// ======================================================

// Функция SaveFileAs

// Сохранение текста в новом файле

// ======================================================

int SaveFileAs(HWND hwnd)

{

  WORD wSize;

  HANDLE hTxtBuf;

  NPSTR  npTextBuffer;

  // Открываем выходной файл

  hfDstFile = OpenSaveFile();

  if(!hfDstFile) return 0;

  // Определяем размер текста

  wSize = GetWindowTextLength(hEdit);

  // Получаем идентификатор блока памяти,

  // в котором находится редактируемый текст

  hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

  // Фиксируем блок памяти и получаем указатель

  // на него

  npTextBuffer = (NPSTR)LocalLock(hTxtBuf);

  // Записываем содержимое блока памяти в файл

  if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize))

  {

    // При ошибке закрываем файл и выдаем сообщение

    _lclose(hfDstFile);

    MessageBox(hwnd, "Ошибка при записи файла",

       szWindowTitle, MB_OK);

    return 0;

  }

  // Закрываем файл

  _lclose(hfDstFile);

  // Расфиксируем блок памяти

  LocalUnlock(hTxtBuf);

  // Так как файл был только что сохранен,

  // сбрасываем флаг обновления

  bNeedSave = FALSE;

  SetFocus(hEdit);

  return 1;

}

// ======================================================

// Функция SaveFile

// Сохранение текста в старом файле

// ======================================================

int SaveFile(HWND hwnd)

{

  WORD wSize;

  HANDLE hTxtBuf;

  NPSTR  npTextBuffer;

  // Открываем выходной файл

  hfDstFile = _lopen(szCurrentFileName, OF_WRITE);

  if(!hfDstFile) return 0;

  // Определяем размер текста

  wSize = GetWindowTextLength(hEdit);

  // Получаем идентификатор блока памяти,

  // в котором находится редактируемый текст

  hTxtBuf = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);

  // Фиксируем блок памяти и получаем указатель

  // на него

  npTextBuffer = (NPSTR)LocalLock(hTxtBuf);

  // Записываем содержимое блока памяти в файл

  if(wSize != _lwrite(hfDstFile, npTextBuffer, wSize))

  {

    // При ошибке закрываем файл и выдаем сообщение

    _lclose(hfDstFile);

    MessageBox(hwnd, "Ошибка при записи файла",

       szWindowTitle, MB_OK);

    return 0;

  }

  // Закрываем файл

  _lclose(hfDstFile);

  // Расфиксируем блок памяти

  LocalUnlock(hTxtBuf);

  // Так как файл был только что сохранен,

  // сбрасываем флаг обновления

  bNeedSave = FALSE;

  SetFocus(hEdit);

  return 1;

}

// ======================================================

// Функция SelectFile

// Загрузка текста из файла для редактирования

// ======================================================

int SelectFile(HWND hwnd)

{

  LPSTR lpTextBuffer;

  DWORD dwFileSize, dwCurrentPos;

  // Открываем входной файл.

  hfSrcFile = OpenFile();

  if(!hfSrcFile) return 0;

  // Определяем размер файла

  dwCurrentPos = _llseek(hfSrcFile, 0L, 1);

  dwFileSize   = _llseek(hfSrcFile, 0L, 2);

  _llseek(hfSrcFile, dwCurrentPos, 0);

  // Размер файла не должен превосходить 32000 байт

  if(dwFileSize >= 32000)

  {

     _lclose(hfSrcFile);

     MessageBox(hwnd, "Размер файла больше 32000 байт",

        szWindowTitle, MB_OK);

     return 0;

  }

  // Заказываем память для загрузки файла

  lpTextBuffer = (LPSTR)malloc(32000);

  if(lpTextBuffer == NULL)

     return 0;

  // Загружаем текст из файла в буфер

  _lread(hfSrcFile, lpTextBuffer, dwFileSize);

  // Закрываем буфер двоичным нулем

  lpTextBuffer[(WORD)dwFileSize] = '\0';

  // Закрываем файл

  _lclose(hfSrcFile);

  // Переносим содержимое буфера в

  // текстовый редактор

  SetWindowText(hEdit, lpTextBuffer);

  // Освобождаем буфер

  free((void *)lpTextBuffer);

  // сбрасываем флаг обновления

  bNeedSave = FALSE;

  SetFocus(hEdit);

  return 1;

}

// ======================================================

// Функция DldProc

// ======================================================

#pragma argsused

BOOL CALLBACK _export

DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch(msg)

  {

    // Инициализация диалоговой панели

    case WM_INITDIALOG:

    {

      return TRUE;

    }

    case WM_COMMAND:

    {

      switch(wParam)

      {

        // Сообщение от кнопки "OK"

        case IDOK:

        // Отмена диалоговой панели.

        // Это сообщение приходит, когда пользователь

        // нажимает на клавишу <Esc>

        case IDCANCEL:

        {

          // Устанавливаем флаг завершения диалога

          EndDialog(hdlg, 0);

          return TRUE;

        }

      }

    }

  }

  return FALSE;

}

Файл toolbar.hpp

#include <windows.h>

#include <windowsx.h>

#include <mem.h>

// Прототип функции окна TOOLBAR

LRESULT CALLBACK _export

ToolbarWndProc(HWND, UINT, WPARAM, LPARAM);

// ======================================================

// Определение класса TbMain

// ======================================================

class TbMain

{

public:

  // Идентификатор приложения

  static HINSTANCE hInst;

  // Идентификаторы кнопок в различном состоянии

  static HBITMAP   hbmpUp[20];     // исходное состояние

  static HBITMAP   hbmpDown[20];   // нажатые

  static HBITMAP   hbmpGrayed[20]; // заблокированные

  // Идентификатор родительского окна, создавшего TOOLBAR

  static HWND      hwndParent;

  // Идентификатор первой кнопки в TOOLBAR

  static int       nFirstId;

};

// ======================================================

// Определение класса Toolbar

// ======================================================

class Toolbar

{

  RECT     rcParent;        // размеры родительского окна

  RECT     rcToolbar;       // размеры TOOLBAR

  ATOM     aWndClass;       // атом для кода возврата

  char     szClassName[20]; // имя класса

  HWND     hwndToolbar;     // идентификатор окна TOOLBAR

  HWND     hButton[20];     // идентификаторы кнопок

  int      errno;           // код ошибки

public:

// ======================================================

// Вычисление размеров окна TOOLBAR

// ======================================================

  void GetRect(void)

  {

    rcToolbar.left   = GetRectLeft();

    rcToolbar.top    = GetRectTop();

    rcToolbar.right  = GetRectRihgt();

    rcToolbar.bottom = GetRectBottom();

  }

// ======================================================

// Регистрация класса для окна TOOLBAR

// ======================================================

  virtual BOOL RegisterWndClass(void)

  {

    WNDCLASS wc; // структура для регистрации класса окна

    memset(&wc, 0, sizeof(wc));

    wc.style         = 0;

    wc.lpfnWndProc   = (WNDPROC) ToolbarWndProc;

    wc.cbClsExtra    = 0;

    wc.cbWndExtra    = 0;

    wc.hInstance     = TbMain::hInst;

    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);

    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

    wc.lpszMenuName  = (LPSTR)NULL;

    // Определяем кисть для закрашивания окна

    wc.hbrBackground = GetBrush();

    // Имя класса

    lstrcpy(szClassName, "FrolovAVToolBar");

    wc.lpszClassName = (LPSTR)szClassName;

    // Регистрация класса

    aWndClass = RegisterClass(&wc);

    return (aWndClass != 0);

  }

// ======================================================

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

// для размещения кнопок TOOLBAR

// ======================================================

  void CreateTbWindow(void)

  {

    hwndToolbar = CreateWindow(

    szClassName,                 // имя класса окна

    NULL,                        // заголовок окна

    WS_CHILDWINDOW | WS_VISIBLE, // стиль окна

    rcToolbar.left,              // указываем расположение

    rcToolbar.top,               //   и размеры окна

    rcToolbar.right,

    rcToolbar.bottom,

    TbMain::hwndParent,          // идентификатор родительского окна

    0,                           // идентификатор меню

    TbMain::hInst,               // идентификатор приложения

    NULL);                   // указатель на дополнительные параметры

  }

// ======================================================

// Устанавливаем ширину окна TOOLBAR

// ======================================================

  void SetWidth(int nWidth)

  {

    RECT rcOld;

    GetClientRect(hwndToolbar, &rcOld);

    MoveWindow(hwndToolbar,

      rcOld.left, rcOld.top,

      nWidth, rcOld.bottom, TRUE);

  }

// ======================================================

// Вставляем кнопку в TOOLBAR

// ======================================================

  BOOL InsertButton(UINT nPosition,

       LPCSTR lpszBmpUp, LPCSTR lpszBmpDown, LPCSTR lpszBmpGrayed)

  {

     // Загружаем указанные в параметрах функции изображения

     TbMain::hbmpUp[nPosition] =

       LoadBitmap(TbMain::hInst, lpszBmpUp);

     TbMain::hbmpDown[nPosition] =

       LoadBitmap(TbMain::hInst, lpszBmpDown);

     TbMain::hbmpGrayed[nPosition] =

       LoadBitmap(TbMain::hInst, lpszBmpGrayed);

     // Создаем орган управления - кнопку, которую

     // рисует родительское окно. В нашем случае это будет

     // окно TOOLBAR

     hButton[nPosition] = CreateWindow("button", NULL,

       WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,

       GetBtnX(nPosition),  // определяем расположение кнопки

       GetBtnY(nPosition),  //  исходя из ее порядкового номера

       GetBmpWidth(),       // ширина кнопки

       GetBmpHeigt(),       // высота кнопки

       hwndToolbar,         // родительское окно для кнопки

       (HMENU)(nPosition + TbMain::nFirstId), // идентификатор кнопки

       TbMain::hInst, NULL);

     return TRUE;

  }

// ======================================================

// Конструктор для класса Toolbar

// ======================================================

  Toolbar(HINSTANCE hInst, HWND hwndParent, int nFirstid);

// ======================================================

// Деструктор для класса Toolbar

// ======================================================

  ~Toolbar();

  // Проверка признака ошибки

  int Error(void) { return errno; }

  // Определение координат окна TOOLBAR

  virtual int GetRectLeft(void)   { return 0; }

  virtual int GetRectTop(void)    { return 0; }

  virtual int GetRectRihgt(void)  { return rcParent.right; }

  virtual int GetRectBottom(void) { return 26; }

  // Определение кисти для окна TOOLBAR

  virtual HBRUSH GetBrush(void)

  {

     return GetStockBrush(LTGRAY_BRUSH);

  }

  // Определение расположения кнопки исходя из ее номера

  virtual int GetBtnX(int nPosition)

  {

     return 3 + (nPosition * 30);

  }

  virtual int GetBtnY(int nPosition)   { return 3; }

  // Определение размеров кнопки

  virtual int GetBmpWidth(void) { return 30; }

  virtual int GetBmpHeigt(void) { return 20; }

};

Файл smartpad.hpp

// Идентификаторы пиктограмм

#define IDB_NEWUP        300

#define IDB_NEWDOWN      301

#define IDB_NEWGR        302

#define IDB_OPENUP       303

#define IDB_OPENDOWN     304

#define IDB_OPENGR       305

#define IDB_SAVEUP       306

#define IDB_SAVEDOWN     307

#define IDB_SAVEGR       308

#define IDB_EXITUP       309

#define IDB_EXITDOWN     310

#define IDB_EXITGR       311

#define IDB_COPYUP       312

#define IDB_COPYDOWN     313

#define IDB_COPYGR       314

#define IDB_PASTUP       315

#define IDB_PASTDOWN     316

#define IDB_PASTGR       317

#define IDB_CUTUP        318

#define IDB_CUTDOWN      319

#define IDB_CUTGR        320

#define IDB_UNDOUP       321

#define IDB_UNDODOWN     322

#define IDB_UNDOGR       323

#define IDB_HELPUP       324

#define IDB_HELPDOWN     325

#define IDB_HELPGR       326

#define CM_FONT               24347

#define CM_SYSABOUT           0x8880

#define CM_HELPABOUT          24346

#define CM_HELPUSING_HELP     24345

#define CM_HELPPROCEDURES     24344

#define CM_HELPCOMMANDS       24343

#define CM_HELPKEYBOARD       24342

#define CM_HELPINDEX          24341

#define CM_EDITPASTE          24324

#define CM_EDITCOPY           24323

#define CM_EDITCUT            24322

#define CM_EDITUNDO           24321

#define CM_EDITCLEAR          24320

#define CM_EDITSELALL         24319

#define CM_EDITSETREADONLY    24318

#define CM_FILEEXIT           24338

#define CM_FILEPRINTER_SETUP  24337

#define CM_FILEPAGE_SETUP     24336

#define CM_FILEPRINT          24335

#define CM_FILESAVEAS         24334

#define CM_FILESAVE           24333

#define CM_FILEOPEN           24332

#define CM_FILENEW            24331

/* Идентификатор редактора текста */

#define ID_EDIT   200

/* Идентификаторы кнопок TOOLBAR */

#define TB_FIRST  100

#define TB_NEW    TB_FIRST + 0

#define TB_OPEN   TB_FIRST + 1

#define TB_SAVE   TB_FIRST + 2

#define TB_CUT    TB_FIRST + 4

#define TB_COPY   TB_FIRST + 5

#define TB_PAST   TB_FIRST + 6

#define TB_UNDO   TB_FIRST + 7

#define TB_EXIT   TB_FIRST + 9

#define TB_HELP   TB_FIRST + 10

Файл toolbar.cpp

#define STRICT

#include <windows.h>

#include <windowsx.h>

#include <mem.h>

#include "toolbar.hpp"

LRESULT CALLBACK _export

  ToolbarWndProc(HWND, UINT, WPARAM, LPARAM);

void DrawButton(LPDRAWITEMSTRUCT lpInfo);

void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap);

// Определяем члены статического класса TbMain

HBITMAP   TbMain::hbmpUp[20];

HBITMAP   TbMain::hbmpDown[20];

HBITMAP   TbMain::hbmpGrayed[20];

HWND      TbMain::hwndParent;

HINSTANCE TbMain::hInst;

int       TbMain::nFirstId = 0;

// ======================================================

// Конструктор класса Toolbar

// ======================================================

Toolbar::Toolbar(HINSTANCE hInstance, HWND hwndParentWindow, int nFirstId)

{

  // Сбрасываем признак ошибки

  errno = 0;

  // Сохраняем идентификатор копии приложения,

  // идентификатор родительского окна, создавшего

  // TOOLBAR, и идентификатор первой кнопки в

  // органе управления TOOLBAR

  TbMain::hInst = hInstance;

  TbMain::hwndParent = hwndParentWindow;

  TbMain::nFirstId = nFirstId;

  // Определяем размеры внутренней области

  // родительского окна

  GetClientRect(TbMain::hwndParent, &rcParent);

  // Определяем размеры дочернего окна, которое

  // будет использовано для создания органа TOOLBAR

  GetRect();

  // Регистрируем класс для дочернего окна TOOLBAR

  if(!RegisterWndClass())

  {

    errno = 1;

    return;

  }

  // Создаем дочернее окно TOOLBAR

  CreateTbWindow();

  if(hwndToolbar == NULL)

  {

    errno = 2;

    return;

  }

}

// ======================================================

// Деструктор класса Toolbar

// ======================================================

Toolbar::~Toolbar()

{

  // Уничтожаем дочернее окно TOOLBAR

  DestroyWindow(hwndToolbar);

}

// ======================================================

// Функция окна ToolbarWndProc для дочернего окна TOOLBAR

// ======================================================

LRESULT CALLBACK _export

ToolbarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

  switch (msg)

  {

     // Передаем сообщение WM_COMMAND от кнопок в

     // родительское окно

     case WM_COMMAND:

     {

       SendMessage(TbMain::hwndParent, WM_COMMAND, wParam, lParam);

       return 0;

     }

     // Это сообщение приходит при изменении состояния

     // дочернего окна органа управления, когда окно

     // нужно перерисовать

     case WM_DRAWITEM:

     {

       // Перерисовываем кнопку

       DrawButton( (LPDRAWITEMSTRUCT)lParam );

       break;

     }

  }

  return DefWindowProc(hwnd, msg, wParam, lParam);

}

// ======================================================

// Функция DrawButton

// Перерисовывает кнопку

// ======================================================

void DrawButton(LPDRAWITEMSTRUCT lpInfo)

{

   HBITMAP hbm;

   // Обрабатываем сообщение WM_DRAWITEM

   // только если оно поступило от кнопки

   if(lpInfo->CtlType != ODT_BUTTON)

     return;

   hbm = TbMain::hbmpUp[(lpInfo->CtlID) - TbMain::nFirstId];

   // Если кнопка выбрана, рисуем ее в нажатом

   // состоянии

   if (lpInfo->itemState & ODS_SELECTED)

   {

     hbm = TbMain::hbmpDown[(lpInfo->CtlID) - TbMain::nFirstId];

   }

   // Если кнопка неактивна, загружаем идентификатор

   // изображения кнопки в неактивном состоянии

   else if (lpInfo->itemState & ODS_DISABLED)

   {

     hbm = TbMain::hbmpGrayed[(lpInfo->CtlID) - TbMain::nFirstId];

   }

   // При ошибке ничего не рисуем

   if(!hbm) return;

   // Если кнопка выбрана и ее надо целиком

   // перерисовать, вызываем функцию DrawBitmap

   if((lpInfo->itemAction & ODA_DRAWENTIRE) ||

      (lpInfo->itemAction & ODA_SELECT))

   {

     // Рисуем кнопку

     DrawBitmap(lpInfo->hDC,

       (lpInfo->rcItem).left,

       (lpInfo->rcItem).top , hbm);

   }

}

Файл drawbmp.cpp

// ======================================================

// Рисование изображения типа bitmap

// ======================================================

#define STRICT

#include <windows.h>

void DrawBitmap(HDC hDC, int x, int y, HBITMAP hBitmap)

{

  HBITMAP hbm, hOldbm;

  HDC hMemDC;

  BITMAP bm;

  POINT  ptSize, ptOrg;

  // Создаем контекст памяти, совместимый

  // с контекстом отображения

  hMemDC = CreateCompatibleDC(hDC);

  // Выбираем изображение bitmap в контекст памяти

  hOldbm = (HBITMAP)SelectObject(hMemDC, hBitmap);

  // Если не было ошибок, продолжаем работу

  if (hOldbm)

  {

    // Для контекста памяти устанавливаем тот же

    // режим отображения, что используется в

    // контексте отображения

    SetMapMode(hMemDC, GetMapMode(hDC));

    // Определяем размеры изображения

    GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &bm);

    ptSize.x = bm.bmWidth;   // ширина

    ptSize.y = bm.bmHeight;  // высота

    // Преобразуем координаты устройства в логические

    // для устройства вывода

    DPtoLP(hDC, &ptSize, 1);

    ptOrg.x = 0;

    ptOrg.y = 0;

    // Преобразуем координаты устройства в логические

    // для контекста памяти

    DPtoLP(hMemDC, &ptOrg, 1);

    // Рисуем изображение bitmap

    BitBlt(hDC, x, y, ptSize.x, ptSize.y,

           hMemDC, ptOrg.x, ptOrg.y, SRCCOPY);

    // Восстанавливаем контекст памяти

    SelectObject(hMemDC, hOldbm);

  }

  // Удаляем контекст памяти

  DeleteDC(hMemDC);

}

Файл ресурсов

#include "smartpad.hpp"

IDB_NEWUP   BITMAP "newup.bmp"

IDB_NEWDOWN BITMAP "newdown.bmp"

IDB_NEWGR   BITMAP "newgr.bmp"

IDB_OPENUP BITMAP "openup.bmp"

IDB_OPENDOWN BITMAP "opendown.bmp"

IDB_OPENGR BITMAP "opengr.bmp"

IDB_SAVEUP BITMAP "saveup.bmp"

IDB_SAVEDOWN BITMAP "savedown.bmp"

IDB_SAVEGR BITMAP "savegr.bmp"

IDB_EXITUP BITMAP "exitup.bmp"

IDB_EXITDOWN BITMAP "exitdown.bmp"

IDB_EXITGR BITMAP "exitgr.bmp"

IDB_COPYUP BITMAP "copyup.bmp"

IDB_COPYDOWN BITMAP "copydown.bmp"

IDB_COPYGR BITMAP "copygr.bmp"

IDB_PASTUP BITMAP "pastup.bmp"

IDB_PASTDOWN BITMAP "pastdown.bmp"

IDB_PASTGR BITMAP "pastgr.bmp"

IDB_CUTUP BITMAP "cutup.bmp"

IDB_CUTDOWN BITMAP "cutdown.bmp"

IDB_CUTGR BITMAP "cutgr.bmp"

IDB_UNDOUP BITMAP "undoup.bmp"

IDB_UNDODOWN BITMAP "undodown.bmp"

IDB_UNDOGR BITMAP "undogr.bmp"

IDB_HELPUP BITMAP "helpup.bmp"

IDB_HELPDOWN BITMAP "helpdown.bmp"

IDB_HELPGR BITMAP "helpgr.bmp"

APP_MENU MENU

BEGIN

        POPUP "&File"

        BEGIN

                MENUITEM "&New\tCtrl+N",      CM_FILENEW

                MENUITEM "&Open...\tCtrl+O",  CM_FILEOPEN

                MENUITEM "&Save\tCtrl+S",     CM_FILESAVE

                MENUITEM "Save &as...",       CM_FILESAVEAS

                MENUITEM SEPARATOR

                MENUITEM "&Print...",         CM_FILEPRINT,         GRAYED

                MENUITEM "Page se&tup...",    CM_FILEPAGE_SETUP,    GRAYED

                MENUITEM "P&rinter setup...", CM_FILEPRINTER_SETUP, GRAYED

                MENUITEM SEPARATOR

                MENUITEM "E&xit",             CM_FILEEXIT

        END

        POPUP "&Edit"

        BEGIN

                MENUITEM "&Undo\tCtrl+Z",     CM_EDITUNDO

                MENUITEM SEPARATOR

                MENUITEM "&Cut\tCtrl+X",      CM_EDITCUT

                MENUITEM "&Copy\tCtrl+C",     CM_EDITCOPY

                MENUITEM "&Paste\tCtrl+V",    CM_EDITPASTE

                MENUITEM "C&lear\tCtrl+Del",  CM_EDITCLEAR

                MENUITEM SEPARATOR

                MENUITEM "Select &All",       CM_EDITSELALL

        END

        POPUP "&Options"

        BEGIN

                MENUITEM "&Read-only",         CM_EDITSETREADONLY

                MENUITEM SEPARATOR

                MENUITEM "&Set Font...",       CM_FONT, GRAYED

        END

        POPUP "&Help"

        BEGIN

                MENUITEM "&Index\tF1",  CM_HELPINDEX,      GRAYED

                MENUITEM "&Keyboard",   CM_HELPKEYBOARD,   GRAYED

                MENUITEM "&Commands",   CM_HELPCOMMANDS,   GRAYED

                MENUITEM "&Procedures", CM_HELPPROCEDURES, GRAYED

                MENUITEM "&Using help", CM_HELPUSING_HELP, GRAYED

                MENUITEM SEPARATOR

                MENUITEM "&About...",   CM_HELPABOUT

        END

END

APPICON ICON "appicon.ico"

ABOUT DIALOG 25, 34, 118, 67

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION "About"

BEGIN

        CTEXT "Редактор текста\nSmart Pad\nVersion 1.0\n(C) Frolov A.V., 1994", -1, 34, 6, 79, 36, WS_CHILD | WS_VISIBLE | WS_GROUP

        ICON "APPICON", -1, 14, 16, 16, 16, WS_CHILD | WS_VISIBLE

        DEFPUSHBUTTON "OK", IDOK, 42, 47, 33, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

END

APP_ACCELERATORS ACCELERATORS

BEGIN

        "N", CM_FILENEW, VIRTKEY, CONTROL

        "S", CM_FILESAVE, VIRTKEY, CONTROL

        "O", CM_FILEOPEN, VIRTKEY, CONTROL

        "Z", CM_EDITUNDO, VIRTKEY, CONTROL

        "X", CM_EDITCUT, VIRTKEY, CONTROL

        "C", CM_EDITCOPY, VIRTKEY, CONTROL

        "V", CM_EDITPASTE, VIRTKEY, CONTROL

        VK_DELETE, CM_EDITCLEAR, VIRTKEY, CONTROL

        VK_F1, CM_HELPINDEX, VIRTKEY

END


Справочные и Help-системы
Создание справочных систем и систем помощи

 ВЕЕДЕНИЕ.

 Практически любая прикладная и тем более инструментальная

 программа должна включать в себя раздел помощи пользователю по

 ее эксплуатации; кроме того часто возникает потребность в

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

 например в условиях отсутствия в библиотеках и высокой цены

 литературы по информатике наличие компьютерного справочника,

 охватывающего многочисленные темы этого предмета, жизненно

 необходимо для успешного преподавания в школе и ВУЗе.

 Для создания справочных систем под Windows используется

 специальный help - компилятор, обычно входящий в комплект

 поставки большинства серьезных инструментальных средств;

 исходный текст для компиляции готовится в редакторе  Microsoft

 Word for Windows версии 2.0 или 6.0 как обычный текст, но

 снабженный  специальными элементами типа скрытого текста,

 подстрочных сносок, графических изображений, таблиц, стилевого

 и шрифтового оформления. Созданный текст сохраняется в формате

 RTF (промежуточный формат для конвертирования между различными

 текстовыми процессорами) и затем при компиляции превращается в

 help-файл. Пользователь работает с этим файлом через программу

 help.exe, а программист череез функцию WinHelp интерфейса

 Windows. Таким образом средства создания и просмотра справочных

 систем встроены в Windows.

 ЭЛЕМЕНТЫ СПРАВОЧНОЙ СИСТЕМЫ.

 РАЗДЕЛ - основной "атомарный" элемент справочной системы -

 представляет собой отображаемый в окне фрагмент справочника.

 Помимо основного окна можно создавать вторичные перекрывающиеся

 и временные окна. Справочная система состоит из многих

 разделов, связанных между собой многочисленными перекрестными

 ссылками и имеет структуру гипертекста. Каждый раздел создается

 в редакторе в виде группы обычных параграфров текста и имеет

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

 раздела в справочнике, ссылки на другие разделы.

 Каждый раздел должен начинаться с новой страницы и содержать в

 первом параграфе все необходимые атрибуты в виде подстрочных

 сносок.

 ПЕРЕКРЕСТНЫЕ ССЫЛКИ служат для связи между разделами -

 пользователь видит их в виде выделенного цветом и

 подчеркиванием текста или в виде графических пиктограмм.

 ОГЛАВЛЕНИЕ - создается как обычный раздел, описанный

 специальным образом и содержащий ссылки на все остальные

 разделы. В большинстве справочников оглавление создается в виде

 дерева - один раздел является корневым и содержит ссылки на

 отдельные темы, тематически части оглавления содержат ссылки

 на разделы следующего уровня и т д.

 ОРГАНЫ УПРАВЛЕНИЯ имеются в главном окне приложения winhelp.exe

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

 Меню File позволяет открыть новый .hlp - файл (те начать работу

 с другим справочником), выбрать принтер и распечатать

 отображаемый раздел.

 Меню Edit позволяет копировать раздел или его фрагмент в буфер

 обмена в неформатированном виде без графических изображений и

 добавлять комментарри к разделу через подменю Annotate...

 Меню Bookmark позволяет вставлять в справочник закладки, как в

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

 заголовок раздела, но можно выбрать и другое.

 Окно Toolbar содержит несколько кнопок для выполнения самых

 "ходовых" операций:

 Кнопка Contens отображает раздел с оглавлением, Search

 используется для поиска.

 Кнопка Back - для перехода в предыдущий раздел

 Кнопка History - для вывода списка ранее просмотренных

 разделов.

 Кнопки << >> служат для просмотра справочника в заданной

 разработчиком последовательности в прямом и обратном

 направлении.

 СОЗДАНИЕ СПРАВОЧНИКОВ.

 АТРИБУТЫ РАЗДЕЛОВ:

  Атрибут      Символ сноски        Описание

  Контекст         #         Ид-р раздела для ссылок - строка до

                             255 символов

  Заголовок        $         Строка до 128 символв

  Список ключевых  K         Длина до 128 симв.Отделяются ';'

  слов

  Номер в просмотре +      Имя и число, разделенные ':'

                           Положенеие раздела в

                           последовательности просмотра

  Макрокоманда     !     Получит управление при отображении

                         раздела

  Тег компиляции   *     Включать - не включать в справочник

                         этот раздел

 Для назначения атрибутов разделам справочника используется

 техника вставки подстрочных сносок редактора Word - из меню

 Insert выбирается строка Footnote и в диалоговой панели в поле

 Custom Footnote Mark вводится символ сноски из предыдущей

 таблицы; после этого экран разделится на 2 части, внизу

 появится окно редактирования сносок; в этом окне после символа

 сноски не более чем через 1 пробел вводится текст сноски. Окно

 редактирования сносок можно закрыть через кнопку Close,  снова

 открыть можно через меню View, стрку Footnotes.

 СОЗДАНИЕ ПЕРЕКРЕСТНЫХ ССЫЛОК.

 Ссылки на другие разделы создаются непосредственно в тексте

 шрифтовым оформлением. Любая ссылка состоит из 2-х частей:

 - видимый для пользователя текст, подчеркнутый двукратно, если

 предполагается переход на обычный раздел или однократно, если

 выполняется переход во временное окно.

 -невидимый пользователю текст строки контекста раздела, на

 который выполняется переход. Между видимым текстом и скрытым

 контекстом не должно быть пробелов. Символ конца параграфа

 должен оставаться видимым. Чтобы выделить строку текста

 подчеркиванием или оформить скрытым текстом, используйте меню

 Format, строку Charakter. Можно создать ссылку на другой

 .hlp-файл - для этого после скрытого текста со строкой

 контекста ставят символ @ и сразу за ним путь к файлу:

  SmartPadcontents@c:\windows\smartpad.hlp

 Для показа раздела во вторичном окне после строки контекста и

 при необходимости           после пути к файлу надо указать имя

 окна через символ >

  SmartPadcontents@c:\windows\smartpad.hlp>contwindow

 ПРОЦЕСС СОЗДАНИЯ ИСХОДНОГО ТЕКСТА ФАЙЛА_СПРАВОЧНИКА

 - Запусить WORD.

 - Создать новый документ - он будет содержать одну строку в

 стиле Normal.В этой строке наберем заголовок первого раздела

 оглвления справочника.

 -Выберем для заголовка  шрифтовое оформление (с кириллицей).

 -Выделим весь первый параграф и в списке стилей выберем стиль

 "heading 1" , изменив его по нашему образцу - теперь этот стиль

 будет использоваться для всех заголовков.

 - Чтобы заголовок всегда отображался на видном месте, зададим

 для  стиля "heading 1" атрибут оформления параграфа "Keep With

 Next" - в верхней части окна просмотра раздела образуется

 дополнительное окно для заголовка.

 - Для задания контекста расположим курсор в начале строки

 заголовка, вставим подстрочную сноску с # и наберем строку

 контекста.

 -Заголовок раздела задается так же , как и контекст, только

 используется идентификатор $.

 -Далее зададим список ключевых слов, разделенных семиколонами,

 для последующего поиска раздела

 - В заключение зададим последовательность просмотра как сноску

 с + , именем последовательности просмотра и номером раздела в

 последовательности.

 - Аналогично готовим следующие разделы.

 -Закончив подготовку справочника, сохраняем его в формате   RTF

 (меню File, Save As Type выбираем Rich Text Format). При

 желании можно продублировать сохранение и в обычном формате.

 СОЗДАНИЕ ФАЙЛА ПРОЕКТА (С РАСШИРЕНИЕМ .HPJ) осуществляется в

 любом текстовом редакторе без шрифтового оформления - символы

 кириллицы не допускаются! Файл проекта представляет собой текст

 из нескольких секций с названиями в квадратных скобках из

 следующего набора:

 ДЛЯ СЕКЦИИ OPTIONS - первая в файле проекта

 CONTENTS - Строка контекста для раздела оглавления.

 TITLE    - Текст заголовка окна winhelp.exe. Только латиница!

 COPYRIGHT - Свдения о разработчиках.

 BMROOT   - Путь к каталогу с bmp-файлами, есои они включены в

            справочник

 BUILD   - позволяет исключать разделы с заданным тегом

           компиляции

 COMPRESS - задает степень сжатия данных при создании .hlp-

 файла: NO, FALSE,0 -без компресси; MEDIUM - средняя степень

 сжатия; YES,TRUE, HIGH, 1 - высокая степень сжатия.

 ERRORLOG - путь к файлу для записи сообщений об ошибках.

 FORCEFONT - испаользование только указанного шрифта

 ICON - пиктограмма для главного окна winhelp.exe при работе с

 этим hlp-файлом

 OPTCDROM = YES (NO) - оптимизация при записи на сомпакт-диск

 REPORT = ON (OFF) - отображение сообщений во время компиляции

 ROOT - путь к каталогу с hlp-файлами, входящими в проект

 WARNING 1,2 или 3 уровень сообщений об ошибках

 ДЛЯ СЕКЦИИ FILES: перечисляются файлы разделов *.rtf, из

 которых состоит справочник

 ДЛЯ СЕКЦИИ WINDOWS - параметры определяют внешний вид окон

 winhelp.exe следующим образом:

 wndname="caption",(hpos,vpos,width, hight), sizing,(clientRGB),

         (nonscrollingRGB),topmost

 Для главного окна winhelp.exe следует указать имя main.

 Положение и размеры - в диапазоне 0-1024, действительные

 размеры будут пересчитаны по разрешению экрана. sizing -

 начальные размеры вторичного окна (если 1 - то максимальные,

 если 0 - то указанные в секции ). Цветовые параметры

 внутренней области и заголовка окна задаются как тройки

 разделенных запятыми чисел интенсивности цветов. topmost со

 значением 1 делает окно непотопляемым. Неиспользуемые параметры

 можно не указывать, но все запятые должны быть на месте.

 ДЛЯ СЕКЦИИ CONFIG - запорлняется если разработчик использовал

 дополнительные возможности - кнопки , макрокоманды, ф-ции из

 DLL- библиотеки. Так, чтобы в окне Toolbar появились кнопки

 последовательного просмотра << >> надо записать сюда строку

 вызова макрокоманды BrowseButtons().

 КОМПИЛЯЦИЯ - вызовом компилятора с файлом проекта - лучше всего

 из DOS - окна в среде Windows.


Основные понятия и определения

1.ОСНОВНЫЕ ПОЛОЖЕНИЯ.  НАУКА-область  деятельности,направленная  на получение новых знаний о природе, человеческом обществе и мышлении. Научно-исследовательская  деятельность стимулируется как естествен- ной любознательностью людей с определенным типом характера,мышления и  способностей,так и различными государственными,производственными структурами,заинтересованными в  получении  новых  результатов  для успешной  конкуренции  в  тех  или иных областях противостояния.  В настоящее время развитие науки обусловлено общественным разделением труда,  отделением  умственного труда от физического и превращением познавательного процесса в  особый  вид  профессиональной  деятель- ности.Современная наука оказывает стимулирующее действие на общест- венное производство и  развитие  интенсивных  технологий,тем  самым способствует  все большему росту сложности общественного труда и на способы управления обществом.  Совершенно очевидна  прямая  зависи- мость  между  сложностью  массового труда в обществе и степенью его демократизации.Там,где преобладает простой физический труд,управле- ние  носит  силовой,недемократический характер,так как простой труд легко измерим количественно,не требует особых способностей и квали- фикации,а  сфера  управления  тяготеет обычно к простым и очевидным методам,по возможностик насильственным.В обществах с  преобладанием сложного   труда,требующего  длительного  обучения  и  определенных способностей,силовое  управление  становится  неэффективным   из-за сложности  контроля  за  интенсивностью  труда  и  его результатив- ностью,что неизбежно ведет к применению несиловых методов  стимули- рования,т.е.  к развитию демократических институтов власти и управ- ления. Следует отметить,что научные результаты оказывают столь бла- готворное  влияние на общественное развитие только через их исполь- зование в массовых интенсивных технологиях,требующих большого коли- чества высококвалифицированных работников,  т.е.тогда ,когда разви- тие науки сопровождается продвижением ее достижений в массовое про- изводство  трудом инженеров и технологов.  Другими словами,развитие цивилизации определяется не качеством идей в общественных  науках,а уровнем  сложности  труда в массовом материальном производстве.  По мере роста сложности научных проблем происходила специализация  на- учного  труда,деление наук по предметному и функциональному призна- кам;дифференциация наук,узкая специализация научных работников  ка- кое-то  время способствовали росту глубины исследований в узких об- ластях знаний и получению более значимых результатов.Однако в даль- нейшем  начинали  сказываться  трудности  кооперации узкопрофильных специалистов при разработке сложных научных проблем и отдельные на- учные  отрасли интегрировались в единые комплексы для повышения эф- фективности исследований.Сушествует мнение,что со временем все нау- ки  сольются  в  одну,произойдет возврат к античной неделимой науке под эгидой философии Трудно поддерживать или оспаривать такого рода прогнозы  -пока  очевидно,что  значимые результаты сегодня получают как правило исследования на стыках различных наук и это пока  гово- рит в пользу интеграции. Мы не будем заниматься детальной классифи- кацией наук;отметим только,что пока они крупноблочно весьма условно делятся на естественные,технические и общественные. Понятие научного  знания.

ЗНАНИЕ-языковое отображение наших представлений о закономерностях в

       окружающем мире.Использется как  устойчивая  основа  практи-

       ческих действий.

ПОЗНАНИЕ-процесс движения чеовеческой  мысли  от  незнания  к  зна-

        нию.Познание стимулируется потребностями практики и исполь-

        зуется в общественной,производственной и  научной  деятель-

        ности.Различают  чувственный  и  рациональный уровни позна-

        ния,формирующие соответственно эмпирическое и теоретическое

        знание.

ОЩУЩЕНИЕ-элемент чувственного познания,отражение мозгом предметов и

        явлений,воздействующих на органы чувств человека.

ВОСПРИЯТИЕ-первичный чувственный    образ    предмета   или   явле-

        ния,действующего на органы чувсв в текущий момент времени.

ПРЕДСТАВЛЕНИЕ-образ,восстанавливаемый мозгом   по   следам  прошлых

              восприятий.

ВООБРАЖЕНИЕ-синтез новыхобразов на основе  представлений.

МЫШЛЕНИЕ-установление взаимосвязей  между различными представленями

        на основе логических рассуждений.  ПОНЯТИЕ-мысль,отражающая

        существенные признаки предмета или явления.

        Различают общие,единичные,собирательные,абстрактные и конк-

        ретные,абсолютные  и  относительные  понятия.Тождественными

        называют понятия с одинаковым содержанием,выраженные в раз-

        личной словесной форме.Понятия  характеризуются  объемом  и

        содержанием.Между  понятиями существуют иерархические отно-

        шения,устанавливаемые по объему или содержанию.

СУЖДЕНИЕ-мысль,утверждающая или   отрицающая  что-либо  чере  взаи-

         мосвязь понятий.

УМОЗАКЛЮЧЕНИЕ-процесс вывода нового суждения  из  имеющегося  набо-

        ра.Делят на дедуктивные и индуктивные.

Методы теоретических  и эмпирических исследований.  Общенаучная

методология исследований  включает  :

НАБЛЮДЕНИЕ-восприятие физических    фактов   при   помощи   органов

        чувств;может быть пассивным(без вмешательства в наблюдаемый

        процесс)  или  активным(с  воздействем  на наблюдаемый про-

        цесс).

СРАВНЕНИЕ-нахождение общих или оличительных признаков между  объек-

        тами исследований.

ЭКСПЕРИМЕНТ-целенаправленное воздействие на исследуемый  процесс  с

        наблюдением последствий (реакции) на эти воздействия.

ОБОБЩЕНИЕ-формирование общего понятия,отражающего существенные чер-

         ты объектов данного класса.

АБСТРАГИРОВАНИЕ-мысленное отвлечение от несущественных свойств,свя-

         зей,отношений  предметов  с целью упрощения исследований с

         сохранением главного в сложном.

ФОРМАЛИЗАЦИЯ-отображение объекта   или  явления  в  знаковой  форме

        искусственного языка(математики,химии) и подмена исследова-

        ния  свойств  реальных  объектов  формальным  исследованием

        соответствующих знаков.

АКСИОМАТИЧЕСКИЙ МЕТОД-построение   теории  на  основе  совокупности

                      аксиом.

АНАЛИЗ-познание с помощью расчленения сложного предмета  исследова-

        ния на составные части.

СИНТЕЗ-объединение отдельных частей в единое целое.

СИСТЕМНЫЙ АНАЛИЗ-метод исследования совокупности сложных,взаимосвя-

        занных систем, характеризуемых большим количеством парамет-

        ров различной природы.

ТВОРЧЕСТВО-мышление,выходящее за пределы  известного  или  деятель-

        ность,порождающая  нечто качественно новое.Одни считают,что

        определяющим элементом творчества является  логика,мышление

        в  условиях  проблемной  ситуации,поиска решения в условиях

        неопределенности,недостатка информации.Другие  полагают,что

        в  творчестве  основное интуиция,нечто из глубины подсозна-

        ния.По-видимому играет роль и то и  другое,преобладающее  в

        зависимости  от  субъективных свойств личности исследовате-

        ля.Специфичным для творчества видом мышления является вооб-

        ражение,фантазия.Отрицательное влияние на творческое мышле-

        ние оказывают косность(сила привычек),узкопрактический под-

        ход,излишняя  специализация,влияние авторитетов,боязнь кри-

        тики,лень,инерция мышления и пр.Важнейшим условием успеха в

        творчестве  является  способнолсть личности сосредоточить и

        долго удерживать внимание на одной проблеме.


Методологические основы научного познания
Методологические основы научного познания и творчества

Выбор направления.  По целевому назначению выделяют  исследова-

ния:

        - фундаментальные,ведущиеся на границе известного  и  неиз-

        вестного  и  направленные на открытие и изучение новых про-

        цессов,явлений,законов  прроды(новые  способы  производства

        энергии,генная инженерия и пр);

        -прикладные,направленные на поиск способов испльзования ре-

        зультатов фундаментальной  науки  в  практической  деятель-

        ности;

        -разработки,доводящие свои  результаты  до  вида,пригодного

        для  широкого  использования  в  промышленности.

НАУЧНОЕ НАПРАВЛЕНИЕ-одна или комплекс наук,в которых ведутся иссле-

                    дования,   структурируется  на:

КОМПЛЕКСНЫЕ ПРОБЛЕМЫ-совокупность    проблем,объединенных    единой

                     целью;

ПРОБЛЕМЫ-совокупность  теоретических  и практических задач;

ТЕМА ИССЛЕДОВАНИЯ-составная часть проблемы.  Направления исследова-

ний,финансируемых    на   общегосударственном   уровне,определяются

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

темы  научных исследований формулируются на основе анализа противо-

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

Выбор направления.  По целевому назначению выделяют  исследова-

ния:

        - фундаментальные,ведущиеся на границе известного  и  неиз-

        вестного  и  направленные на открытие и изучение новых про-

        цессов,явлений,законов  прроды(новые  способы  производства

        энергии,генная инженерия и пр);

        -прикладные,направленные на поиск способов испльзования ре-

        зультатов фундаментальной  науки  в  практической  деятель-

        ности;

        -разработки,доводящие свои  результаты  до  вида,пригодного

        для  широкого  использования  в  промышленности.

НАУЧНОЕ НАПРАВЛЕНИЕ-одна или комплекс наук,в которых ведутся иссле-

                    дования,   структурируется  на:

КОМПЛЕКСНЫЕ ПРОБЛЕМЫ-совокупность    проблем,объединенных    единой

                     целью;

ПРОБЛЕМЫ-совокупность  теоретических  и практических задач;

ТЕМА ИССЛЕДОВАНИЯ-составная часть проблемы.  Направления исследова-

ний,финансируемых    на   общегосударственном   уровне,определяются

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

темы  научных исследований формулируются на основе анализа противо-

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


Основные этапы НИР

Этапы НИР.

   -Разработка  технико-экономического обоснования(ТЭО),включающее:

        -обоснование темы исследований,доказательство их  целесооб-

        разности,полезности,актуальности,значимости;обоснование

        подтверждается аналитическим обзором состояния вопросов на

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

        ным источникам;

-Теоретические исследования,обосновывающие физическую модель иссле-

        дуемого процесса,разрабатывающие  математические  модели  и

        анализирующие   предвкарительные  результаты  теоретических

        проработок;

-Экспериментальные исследования;

-Общий анализ полученных результатов,уточнение теоретических

моделей;

-Составление отчета о НИР;

-Внедрение результатов НИР и определение  их  фактической  эффекти-

ности

Этапы НИР.

   -Разработка  технико-экономического обоснования(ТЭО),включающее:

        -обоснование темы исследований,доказательство их  целесооб-

        разности,полезности,актуальности,значимости;обоснование

        подтверждается аналитическим обзором состояния вопросов на

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

        ным источникам;

-Теоретические исследования,обосновывающие физическую модель иссле-

        дуемого процесса,разрабатывающие  математические  модели  и

        анализирующие   предвкарительные  результаты  теоретических

        проработок;

-Экспериментальные исследования;

-Общий анализ полученных результатов,уточнение теоретических

моделей;

-Составление отчета о НИР;

-Внедрение результатов НИР и определение  их  фактической  эффекти-

ности

Поиск,накопление и обработка научной информации В настоящее время

библиотеки активно используют в своей работе ЭВМ и системы коммуни-

каций;накопленные человечеством знания,хранившиеся ранее в виде пе-

чатной прдукции переносятся на современные  средства  хранения(маг-

нитные,оптические) и объединяются в библиографические и фактографи-

ческие базы данных отраслевого,  многотематического,внутреннего или

транснационального  уровння доступа.  Средства хранения и доступа к

научной информации объединяются в  глобальные  информационные  инф-

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

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

деляют на первичные и вторичные,содержащие результаты анализа и пе-

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

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

мые(отчеты   о   НИР,диссертации,депонированные    рукописи,перево-

ды,конструкторская  документация,сообщения научных семинаров,симпо-

зиумов,конференций,съездов.)  Традиционным  средством  упорядочения

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

универсальная десятичная классификация(УДК),состоящая из  осмновной

и  ряда  вспомогательных таблиц,делящихся последовательно в многоу-

ровневые подкаталоги по 10 разделов в каждом и посроенные по  схеме

понятие-индекс


Задачи и методы теоретических исследований

  ЦЕЛЬ теоретических исследований-выделение существенных связей между

        исследуемым объектом и окружающей средой,объяснение и обоб-

        щение результатов  экспериментов,выявление  и  формализация

        общих закономерностей.

ЗАДАЧИ ТИ-обработка и интерпретация опытных  данных,распространение

        результатов  исследования на ряд подобных объектов без пов-

        торения всего объема исследований,прогнозирование поведения

        объекта,  недоступного для проведения экспериментов,обосно-

        вание параметров и необходимой точности наблюдений и пр

МЕТОДЫ ТИ-

        -МЕТОД РАСЧЛЕНЕНИЯ(ДЕКАРТА) основан на  выделении  основных

        элементов объекта и связей между ними,моделировании отдель-

        ных элементов с последующим объединением по  предварительно

        изученным   взаимосвязям  между  ними;  -МЕТОД  ОБЪЕДИНЕНИЯ

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

        та,называемый   ОБЩЕЙ  ТЕОРИЕЙ  СИСТЕМ  или  СИСТЕМОЛОГИЕЙ.

        ОТС(<Л.Берталанфи) базируется на 3-х  основных  постулатах:

        1)функционирование  систем любой природы может быть описано

        на  основе  рассмотрения  структурно-функциональных  связей

        между элементами ; 2)организация системы может быть опреде-

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

        тех элементов,которые непосредственно взаимодействуют с ок-

        ружением; 3)функционирование и храктер взаимодействия с ок-

        ружением полностью зависят от организации системы.

ТИ распадаются на отдельные задачи-требующие разрешения  противоре-

чия теоретических моделей.Каждая задача включает в себя

        УСЛОВИЯ-исходная информация  для  решения  задачи  ТРЕБОВА-

        НИЯ-информация,которая  должна  быть  получена в результате

        решения.

ОСНОВНЫЕ СТАДИИ ТИ

        1)ПРЕДВАРИТЕЛЬНЫЙ АНАЛИЗ,включающий  предварительный  поиск

        возможных   решений   переносом  из  других  областей  зна-

        ний,использованием прообразов природы  и  пр.  2)ПОСТАНОВКА

        ЗАДАЧИ,определяющая  конечную цель и общие методы ее дости-

        жения,уточненные и четко сформулированные требования к  за-

        даче  Постановка-наиболее трудная и ответственная часть ре-

        шения задачи 3)РЕШЕНИЕ ЗАДАЧИ-творческий  процесс,требующий

        часто  отказа от обычных представлений.При разработке любых

        теорий используют логические методы и правила:вывода ,уста-

        новления  истинности сложных высказываний,образования слож-

        ных понятий из простых и пр


Математическое моделирование
Математическое моделирование объекта исследований

МОДЕЛЬ-представление объекта в другой,отличной от реального существования,

        форме.Наиболее абстрактной  является  математическая модель

        объекта,  представляющая его в виде системы  математических

        соотношений-формул или систем уравнений,описывающих его по-

        ведение,реакции на различные  воздействия  окружающей  сре-

        ды.Зачем необходимо моделировать объекты исследований?В са-

        мом общем случае ответ на этот вопрос  звучит  просто:чтобы

        иметь возможность предсказывать поведение объекта в различ-

        ных условиях функционирования.Задачу математического  моде-

        лирования  называют еще ЗАДАЧЕЙ ИДЕНТИФИКАЦИИ,рассматривае-

        мой  обычно  как   дуальную(сопряженную)задаче   управления

        системой,так как предсказание редко является самоцелью,свя-

        занной с удовлетворением любопытства,а как правило  необхо-

        димо  для  приложения  управляющих  воздействий для первода

        системы в желаемое состояние,для компенсации  нежелательных

        возмущений  внешней  среды,уводящих  систему  от требуемого

        состояния

НАИБОЛЕЕ ОБЩЕЙ  ФОРМОЙ  МАТЕМАТИЧЕСКОЙ  МОДЕЛИ объекта(системы,про-

        цесса)является система интегро-дифференциальных уравнений в

        частных  производных;из этой формы все остальные получаются

        при самых различных упрощениях. Так,если реакция системы не

        зависит от частоты входных воздействий (в рабочем диапазоне

        частот),ее называют СТАТИЧЕСКОЙ и из системы описывающих ее

        уравнений  исчезают  производные и интегралы по времени.  В

        противном и более  общем  случае  модель  является  ДИНАМИ-

        ЧЕСКОЙ.  Если  точность предсказания по построенной однажды

        модели  не  изменяется  во  времени(модель  не  стареет),то

        система  и  ее  модель называются СТАЦИОНАРНЫМИ и параметры

        модели(коэффициенты уравнений) могут  считаться  постоянны-

        ми.Иначе  модель и система-НЕСТАЦИОНАРНЫ и коэффициенты мо-

        дели  требуют  систематического   уточнения.   Если   между

        действующими на входе системы воздействиями отсутствует ка-

        кая-либо зависимость,то из математической  модели  исчезают

        частные производные по невременным параметрам и модель ста-

        тической системы  преобразуется  в  систему  алгебраических

        уравнений.  Если  между  установившимися значениями входных

        воздействий и выходных реакций системы  взаимосвязь  линей-

        на,то мы имеем дело и с линейной моделью в виде системы ли-

        нейных дифференциальных  уравнений  в  случае  динамической

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

        го объекта. Наличие или отсутствие на входе системы неизме-

        ряемых  (неучитываемых  в  модели) воздествий вызывает нео-

        бусловленные  измеряемыми  и  учитываемыми  в  модели  воз-

        действиями  колебания  выходных реакций системы(шумы) и вы-

        нуждает разделять системы и модели(и методы их  идентифика-

        ции)  на  ДЕТЕРМИНИРОВАННЫЕ  и СТОХАСТИЧЕСКИЕ.  Можно также

        классифицировать системы и модели на ОДНОМЕРНЫЕ и МНОГОМЕР-

        ные  в  зависимости от того,одно или несколько входных воз-

        действий требуют учета при прогнозировании выходной реакции

        системы.  Наиболее  важный  и  трудно  осуществимый  способ

        классификации  системпо  наличию  априорной  информации   о

        систеие.  В основе всех перечисленных методов классификации

        систем-степень  сложности  процессов  идентификации.Очевид-

        но,что  линейный  стацинарный  процесс известного порядка с

        одним входом идентифицировать  значительно  проще,чем  сто-

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

        нейным и нестационарным. Чем меньше есть априорной информа-

        ции  по  сравнению с необходимой,тем меньше точность и ско-

        рость сходимости,больше математическая сложность и длитель-

        ность  вычислений у метода идентификации,который может быть

        применен.Подходы,основанные на использовании малой  априор-

        ной информации являются более общими,но их применение к од-

        номерным линейным процессам будет похоже на стрельбу из пу-

        шек по воробьям,т е неэффективно по используемому математи-

        ческому обеспечению и оборудованию.

ВЫБОР ТИПА МОДЕЛИ является таким образом важнейшим этапом в матема-

        тическом моделировании и выполняется обычно  в  итеративном

        цикле  последовательных усложнений до достижения приемлемой

        точности прдсказаний,стараясь ограничиться минимально  воз-

        можной  сложностью  используемого  математического  аппара-

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

        последующий  анализ благодаря использованию принципа супер-

        поиции-суммирования реакций системы  на  отдельные  входные

        воздействия.

Некоторые принципы аналитического моделирования.  АНАЛИТИЧЕСКИЕ

МЕТОДЫ составления математического описания объекта основываются

        на теоретическом  анализе  происходящих  в нем процессов.За

        исключением простых объектов  эта  задача  решается  обычно

        разделение на более простые,доступные для анализа звенья;по

        математичеким  описаниям  звеньев  синтезируется  математи-

        ческая  модель  объекта в целом.Получающееся таким способом

        описание часто оказывается чрезмерно  сложным  и  неудобным

        для  анализа и практического использования.Кроме того,неиз-

        бежные упрощающие допущения на отдельных этапах приводят  к

        накоплению  погрешностей,снижающих качество модели в целом.

        Наиболее рационально сочетание аналитических и  эксперимен-

        тальных  методов,взаимно  дополняющих  друг  друга:аналити-

        ческие методы тспользуются для определения структуры  моде-

        ли(вида уравнений),а числовые значения парметров модели оп-

        ределяют экспериментально для обеспечения необходимой  точ-

        ности предсказания выходов по входам.  Аналитические методы

        моделирования неисчерпаемы и попытка рассмотреть  или  хотя

        бы перечислить физичаские,химические,экономические или дру-

        гие законы,которые могут быть при этом использованы,была бы

        попыткой  объять  необъятное.Практика однако показывает,что

        при составлении ММ  чаще  всего  используют  уравнения  ба-

        лансов-материальных,энергетических,денежных,информационных

        и т д. Этот подход основывается на всеобщеих законах сохра-

        нения(массы,энергии, ресурсов любых других видов),утвержда-

        ющих,что в реальных системах  проиходит  преобразование  (с

        накоплением или без) рабочей субстанции без ее исчезновения

        или возникновения из ничего. Уравнение баланса в общем виде

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

                    i=n        i=n

                    ___        ___

                    \  G (t)=  \ deltaG (t),                  (1)

                    /__ i      /__ i

                    i=1        i=1

В словесной формулировке эта запись означает следующее: сумма пото-

        ков на входе и выходе(входящие  считаем  положительными  ,а

        выходящие отрицательными) в любой момент времени равна сум-

        ме приращений рабочей субстанции в объекте. При определении

        статическких  зависимостей можно воспользоваться интеграль-

        ной записью уравнения баланса

                   i=n

                   ___ T

                 1 \   Ї G(t) dt=0.                           (2)

              lim- /   ї  i

             T->бT --- 0

                   i=1

    Чтобы определить,какое конечное Т может с достаточной точностью

        считаться  бесконечным,можно  задаться величиной допустимой

        погрешности правой части (1) Ge  ,  вычислить  максимальную

        емкость  объекта  Gmax  и определить Т>=Gmax/Ge,при котором

        материальный баланс может быть записан в  приближенной  ин-

        тегральной форме:

                        i=n T

                        ___Ї

                        \  |Gi(t)dt~=0.

                        /__ї

                        i=10 ПРИМЕР 1.  В качестве примера составим

        уравнение смесителя емкостью V, в которую поступают 2 пото-

        ка Q1  и  Q2  с  концентрациями  рабочей  субстанции  соот-

        ветственно С1 и С2,а на выходе-поток Q с концентрацией С:

                Q1*C1+Q2*C2-Q*C=V*dC/dt

        (скорость изменения  концентрации  пропорцмональна разности

        прирока и стока) или после замены  переменных

        y=C,  T=V/Q,___4 _._0x1=C1, x2=C2,___4 _._0K1=Q1/Q, K2=Q2/Q

                T*dy/dt+y=K1*x1+K2*x2 в  динамике

                y=K1*x1+K2*x2  в___4 _._0статике

        ПРИМЕР 2. Пусть груз массы m перемещается под действием из-

        меняющейся во времени t силы F(t) в жидкости с  коэффициен-

        том вязкого трения r и центрируется в нейтральном положении

        2-мя пружинами с линейными  характеристиками  и  жесткостью

        k.Уравнение  баланса сил в соответствии с законами механики

        дает

                m*d¤x/dt¤+r*dx/dt+k*x=F(t), где  х-отклонение груза

        от нейтрального положения

        ПРИМЕР 3.  Пусть электрическая цепь состоит  из  последова-

        тельно  соединенных  сопротивления  R,емкости С,и индуктив-

        ности L;цепь подсоединена к  источнику  ЭДС  E=E(t)-входной

        сигнал,а  выходным  является  напряжение Uc на конденсаторе

        .Уравнение баланса  напряжений  в  соответствии  с  законом

        Кирхгофа дает:

                L*d¤Uc/dt¤ + R*dUc/dt + (1/C)*Uc = (1/C)*E

        -получили уравнение  аналогичное  механичкеской системе,что

        говорит о  возможности  физического  моделирования  механи-

        ческих систем с помощью элетрических аналогов.

ПОЛУЧЕНИЕ ММ С ПОМОЩЬЮ ФИЗИЧЕСКОГО МОДЕЛИРОВАНИЯ При  проектироыва-

        нии  новых объектов их МО неизвестно;протекающие в них про-

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

        рых  протекают  процессы,тождественные  будущему  объекту,с

        последующим пересчетом параметров модели на  реальный  объ-

        ект.Исследование  характеристик объекта на ФМ включает сле-

        дующие основные этапы:  1)Изучение проекта объекта  и  про-

        цессов,которые  должны  в  нем протекать;  2)Построение ФМ;

        3)Экспериментирование на модели для получения  данных,необ-

        ходимых  для  определения  х-к  модели,одноименных  искомым

        х-кам создаваемого объекта 4)Пересчет данных для  определе-

        ния х-к объекта Физическое моделирование основано на теории

        подобия и теории размерностей.Соответствие между моделью  и

        объектом  должно быть установлено на геометрическом подобии

        рабочих объемов,т е пространств,в  которых  протекают  про-

        цессы,а  также на подобии самих процессов.Рабочие среды,  и

        материалы объекта и моделипри  этом  могут  быть  различны-

        ми:например, процессы в раскаленных газах могут быть иссле-

        дованы на модели с жидкостями  при  низкой  температуре.При

        моделировании  могут  быть другие скорости движения рабочих

        сред.  Геометрическое подобие  обеспечивается  постоянством

        масштабных  коэффициентов для всех линейных размеров объек-

        та:

                       Lm/Lo=Kl=const

        Подобие процессов  обеспечивается  равенством в одних и тех

        же точках модели и объекта т н степенных комплексов и симп-

        лексов или

                        КРИТЕРИЕВ ПОДОБИЯ

        КОМПЛЕКСЫ-это  произведения  степеней параметров

        СИМПЛЕКСЫ-отношения 2-х параметров  с  одинаковыми  размер-

        ностями Критерии подобия являются инвариантами,т е парамет-

        рами,значения которых не изменяются при переходе от  модели

        к  реальному  объекту

        ВЫБОР МАСШТАБНЫХ КОЭФФИЦИЕНТОВ

        Пусть___4 _._0процесс в объекте описывается некоторым

уравнением

                F(N1o,N2o,...,Njo,...,Nko),                     где

        N1o,N2o,...,Njo,...,Nko -параметры процесса в объекте  Про-

        цесс в модели пусть описывается уравнением

                F(N1m,N2m,...Njm,...,Nkm),                      где

        N1m,N2m,...Njm,...,Nkm  -параметры процеса в модели Для i-й

        точки рабочих объемов объекта и модели можно записать:

                N1im/N1io=K1i; N2im/N2io=K2i;    ...;Njim/Njio=Kji;

                Nkim/Nkio=Kki;

        где Кli-масштабные  коэффициенты МК,выбранный для параметра

        Njm должен оставаться неизменным для  всех  точек  рабочего

        объема модели:

                Nj1m/Nj1o=Kj1;     Nj2m/Nj2o=Kj2;...;Njim/Njio=Kji;

                ...; Njkm/Njko=Kjk;

        где Kj1=Kj2=...=Kji=...=Kjk=Kj.  Масштабирование   основных

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

        экспериментирования на модели,производные параметры масшта-

        бируются  из  условия  подобия процесса в модели процессу в

        объекте и для этого используют безразмерные степенные комп-

        лексы-критерии подобия.  Ограничения,накладываемые на выбор

        МК некоторым критерием Ri записываются в виде:

                            Ri =  idem

        ПРИМЕР Пусть необходимо выдержать условие

                     F*L/M*V¤ = idem  или:

                Fo*Lo/Mo*V¤o = Fm*Lm/Mm*V¤m или Kf*Kl/Km*K¤v=1; от-

        куда  следует,что  если  3 любые МК выбраны,то 4-й не может

        быть произвольным.

        Получают критерии подобия процесса так:

        -уравнение  процесса преобразуется к безразмерному виду,для

        чего в общем случае необходимо все его члены  разделить  на

        один из них.

        -величины,входящие в кажлый из членов  уравнения,заменяются

        соответвенными МК;

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

        Количество критериев и их вид  зависят  от  физи-

        ческой  природы процессов По пи-теореме Букингема число не-

        зависимых критериев  подобия  ранот  разности  между  общим

        числом  параметров  и числом параметров с независимыми раз-

        мерностями:

                            d = c - f.

        Независимыми называются  размерности,которые  не могут быть

        выражены через размерности других параметров этого процесса

        в некоторого степенного комплекса.

        ПРИМЕР Пусть физический_процесс х-зуется парамтрами:

        l[вт/м*град]-коэфф  теплопроводности

        _a[вт/м¤*град]-коэфф теплоотдачи

        n[м¤/сек]-кофф кинематич вязкости

        _v[м/сек]-скорость_рабочей среды

        d[м]-характерный   геометрический   размер   объекта

        _В  этом  случае

        с=5,f=3(например [m],[m/сек],[вт/м*град])  и  2  оставшиеся

        размерности  -зависимы,т  к  могут  быть  выражены  через 3

        основных:

                [вт/м¤*град] = [вт/м*град]/[m]

                [м¤/сек] =_[м/сек]*[м]

        Количество k критериев,имеющих вид комплексов:

                           k=c-r,

        где r-число параметров,основных в абсолютной системе единиц

        или можно считать k равным числу членов уравнения минус 1.

 ПРИМЕР Определим критерии для равноускоренного движения

                   S=Vo*T + A*T¤/2

                   _Разделим все члены на S:

                   1=Vo*T/S + A*T¤/2*S

                   _Масштабные коэффициенты:

             Kv=Vom/Voo; Kl=Sm/So;  Kt=Tm/To; Ka=Am/Ao.

          Заменив в каждом члене парметры масштабными коэффициента-

          ми, получим критерии подобия:

               Kv*Kt/Kl = idem;  Ka*Kt¤/2*Kl = idem,

          ограничивающие_произвол в выборе МК

          МАСШТАБНЫЕ КОЭФФИЦИЕНТЫ  ДЛЯ  ПРОИЗВОДНЫХ определяются по

        аргументам и функции.Если для аргумента выбран МК Кх:

                    Хм=Кх*Хо

        _а для функции выбран МК Ку:

                    Ум=Ку*Уо

        _то

                 dYm/dXm=(Ky/Kx)*(dYo/dXo);Ky/Kx=Kyx.

        _Для производной n-го порядка:

                  d№Ym/dX№m =  (Ky/K№x)*(d№Yo/dX№o);  Ky/K№x=Ky*X№.

        Если функция У зависит от аргументов X и Z то для смешанных

        производных МК находятся из следующих выражений:

                       m _q    m  q    m   q

                d№Ym/dXm*dZm =_(Ky/Kx*Kz)*(d№Yo/dXo*dZo);

                         m q _  mxq

                     _Ky/Kx*Kx = Kyx .

        При моделировании могут иметь иесто случаи:

        1)Известна система уравнений,но из-за ее сложности аналити-

         ческое определение х-к по интересующему нас каналу затруд-

         нительно. В этом случае критерии для моделирования получа-

        ют обработкой системы уравнений.  2)Уравнеия  неизвестны,но

        достаточно  изучена качественная картина протекающих в объ-

        екте процессов.Искомые МК определяют с  использованием  ха-

        рактерных  для  этих процессов критериев подобия.  Например

        при исследовании процессов в жидких средах чпсто используют

        число Рейнольдса

                        Re=V*L/n,

    _   где n-  коэфф  кинематич  вязкости

        При одинаковых вязкостях объекта и модели условие подобия

                        Lm/Lo =Vo/Vm или Kv = 1/Kl

        говорит,что при  уменьшении  размеров модели по сравнению с

        объектом ее скорость необходимо увеличить в 1/Kl  раз.  При

        необходимости обеспечить подобие 2-х жидких сред используют

        критерий Ньютона

                        Ne =  F*l/m*v¤

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

        ется      критерием      Нуссельта:

               Nu=alpha*l/lambda

        где alpha-коэффициент теплоотдачи

            lambda-теплопроводность

        Соответствие между размерами и временем устанавливают  кри-

        терием гомохронности

                       _Ho = v*t/l.

        Общие закономерности,которым подчиняются различные виды по-

        добия,  формулируются в теории подобия в виде 3-х  основных

        теорем.2 первых устанавливают соотношения между параметрами

        заведомо подобных явлений,а 3-я определяет  условия,необхо-

        димые  и достаточные для того,  чтобы явления оказались по-

        добными.

        ПЕРВАЯ ТЕОРЕМА ПОДОБИЯ

        У подобных явлений существуют сочетания параметров,называе-

        мых критериями подобия,имеющие одинаковые значения.

        ВТОРАЯ_ТЕОРЕМА ПОДОБИЯ

        Всякое полное уравнение физического процесса,составленное в

        определенной системе единиц, может быть представлено в кри-

        териальной  форме,  т е в виде зависимости между критериями

        подобия.

        ТРЕТЬЯ ТЕОРЕМА ПОДОБИЯ

        Для обеспечения подобия необходимо и достаточно  пропорцио-

        нальность сходственных параметров,входящих в условия одноз-

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

        ния.  К  числу  наиболее распространенных условий однознач-

        ности относятся:  геометрия объекта,виды рабочих сред в ра-

        бочем пространстве и на границах объекта,начальные значения

        параметров,материалы и т п.

                                *   *

                                  *

        Если физическая модель создана ,то следующим этапом  иссле-

        дования  является  получение  математического описания этой

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

        исследования  одним из методов которые мы будем изучать.По-

        лученные на модели экспериментальные данные  обрабатываются

        и  составляется  ММ  в виде системы уравнений,  связывающих

        входные ивыходные параметры процессов в модели.  МО  модели

        является исходным материалом для расчета МО объекта. ПРИМЕР

        При экспериментальном исследовании модели получено  уравне-

        ние

                    3     3

             0.001*d Ym/dt  +0.02*d¤Ym/dt¤m + 1.47*dYm/dtm+Ym =

        _1.3Xm.

        Моделирование осуществлялось следующими  МК:

                Ky=0.5; Kx=0.1;  Kt=0.2.

        Определим дифференциальное  уравнение  объекта.  Полльзуясь

        формулами определения масштабных коэфициентов для производ-

        ных получим:

                                                              3_

          Kyt =  0.5/0.2  =2.5  ;Kyt¤  =  0.5/(0.2)¤=12.5;_Kyt_=

                                  3

                       = 0.5/(0.2)  =62.5.

         Подставляя МК в уравнение модели после пробразований полу-

         чим уравнение объекта:

                      3     3

            0.125*dYo/dto + 0.5*d¤Yo/dt¤o +  183.75*dYo/dto  +Yo  =

            = 4.26*Xo.

        Для сложных  объектов физическое моделирование осуществляют

        иногда по их отдельным частям,а  общее  МО  получают  чисто

        аналитическим путем.


Теория подрбия и размерностей в физическом моделировании
Теория подобия и размерностей в физическом моделировании

ПОЛУЧЕНИЕ ММ С ПОМОЩЬЮ ФИЗИЧЕСКОГО МОДЕЛИРОВАНИЯ При  проектироыва-

        нии  новых объектов их МО неизвестно;протекающие в них про-

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

        рых  протекают  процессы,тождественные  будущему  объекту,с

        последующим пересчетом параметров модели на  реальный  объ-

        ект.Исследование  характеристик объекта на ФМ включает сле-

        дующие основные этапы:  1)Изучение проекта объекта  и  про-

        цессов,которые  должны  в  нем протекать;  2)Построение ФМ;

        3)Экспериментирование на модели для получения  данных,необ-

        ходимых  для  определения  х-к  модели,одноименных  искомым

        х-кам создаваемого объекта 4)Пересчет данных для  определе-

        ния х-к объекта Физическое моделирование основано на теории

        подобия и теории размерностей.Соответствие между моделью  и

        объектом  должно быть установлено на геометрическом подобии

        рабочих объемов,т е пространств,в  которых  протекают  про-

        цессы,а  также на подобии самих процессов.Рабочие среды,  и

        материалы объекта и моделипри  этом  могут  быть  различны-

        ми:например, процессы в раскаленных газах могут быть иссле-

        дованы на модели с жидкостями  при  низкой  температуре.При

        моделировании  могут  быть другие скорости движения рабочих

        сред.  Геометрическое подобие  обеспечивается  постоянством

        масштабных  коэффициентов для всех линейных размеров объек-

        та:

                       Lm/Lo=Kl=const

        Подобие процессов  обеспечивается  равенством в одних и тех

        же точках модели и объекта т н степенных комплексов и симп-

        лексов или

                        КРИТЕРИЕВ ПОДОБИЯ

        КОМПЛЕКСЫ-это  произведения  степеней параметров

        СИМПЛЕКСЫ-отношения 2-х параметров  с  одинаковыми  размер-

        ностями Критерии подобия являются инвариантами,т е парамет-

        рами,значения которых не изменяются при переходе от  модели

        к  реальному  объекту

        ВЫБОР МАСШТАБНЫХ КОЭФФИЦИЕНТОВ

        Пусть___4 _._0процесс в объекте описывается некоторым

уравнением

                F(N1o,N2o,...,Njo,...,Nko),                     где

        N1o,N2o,...,Njo,...,Nko -параметры процесса в объекте  Про-

        цесс в модели пусть описывается уравнением

                F(N1m,N2m,...Njm,...,Nkm),                      где

        N1m,N2m,...Njm,...,Nkm  -параметры процеса в модели Для i-й

        точки рабочих объемов объекта и модели можно записать:

                N1im/N1io=K1i; N2im/N2io=K2i;    ...;Njim/Njio=Kji;

                Nkim/Nkio=Kki;

        где Кli-масштабные  коэффициенты МК,выбранный для параметра

        Njm должен оставаться неизменным для  всех  точек  рабочего

        объема модели:

                Nj1m/Nj1o=Kj1;     Nj2m/Nj2o=Kj2;...;Njim/Njio=Kji;

                ...; Njkm/Njko=Kjk;

        где Kj1=Kj2=...=Kji=...=Kjk=Kj.  Масштабирование   основных

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

        экспериментирования на модели,производные параметры масшта-

        бируются  из  условия  подобия процесса в модели процессу в

        объекте и для этого используют безразмерные степенные комп-

        лексы-критерии подобия.  Ограничения,накладываемые на выбор

        МК некоторым критерием Ri записываются в виде:

                            Ri =  idem

        ПРИМЕР Пусть необходимо выдержать условие

                     F*L/M*V¤ = idem  или:

                Fo*Lo/Mo*V¤o = Fm*Lm/Mm*V¤m или Kf*Kl/Km*K¤v=1; от-

        куда  следует,что  если  3 любые МК выбраны,то 4-й не может

        быть произвольным.

        Получают критерии подобия процесса так:

        -уравнение  процесса преобразуется к безразмерному виду,для

        чего в общем случае необходимо все его члены  разделить  на

        один из них.

        -величины,входящие в кажлый из членов  уравнения,заменяются

        соответвенными МК;

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

        Количество критериев и их вид  зависят  от  физи-

        ческой  природы процессов По пи-теореме Букингема число не-

        зависимых критериев  подобия  ранот  разности  между  общим

        числом  параметров  и числом параметров с независимыми раз-

        мерностями:

                            d = c - f.

        Независимыми называются  размерности,которые  не могут быть

        выражены через размерности других параметров этого процесса

        в некоторого степенного комплекса.

        ПРИМЕР Пусть физический_процесс х-зуется парамтрами:

        l[вт/м*град]-коэфф  теплопроводности

        _a[вт/м¤*град]-коэфф теплоотдачи

        n[м¤/сек]-кофф кинематич вязкости

        _v[м/сек]-скорость_рабочей среды

        d[м]-характерный   геометрический   размер   объекта

        _В  этом  случае

        с=5,f=3(например [m],[m/сек],[вт/м*град])  и  2  оставшиеся

        размерности  -зависимы,т  к  могут  быть  выражены  через 3

        основных:

                [вт/м¤*град] = [вт/м*град]/[m]

                [м¤/сек] =_[м/сек]*[м]

        Количество k критериев,имеющих вид комплексов:

                           k=c-r,

        где r-число параметров,основных в абсолютной системе единиц

        или можно считать k равным числу членов уравнения минус 1.

 ПРИМЕР Определим критерии для равноускоренного движения

                   S=Vo*T + A*T¤/2

                   _Разделим все члены на S:

                   1=Vo*T/S + A*T¤/2*S

                   _Масштабные коэффициенты:

             Kv=Vom/Voo; Kl=Sm/So;  Kt=Tm/To; Ka=Am/Ao.

          Заменив в каждом члене парметры масштабными коэффициента-

          ми, получим критерии подобия:

               Kv*Kt/Kl = idem;  Ka*Kt¤/2*Kl = idem,

          ограничивающие_произвол в выборе МК

          МАСШТАБНЫЕ КОЭФФИЦИЕНТЫ  ДЛЯ  ПРОИЗВОДНЫХ определяются по

        аргументам и функции.Если для аргумента выбран МК Кх:

                    Хм=Кх*Хо

        _а для функции выбран МК Ку:

                    Ум=Ку*Уо

        _то

                 dYm/dXm=(Ky/Kx)*(dYo/dXo);Ky/Kx=Kyx.

        _Для производной n-го порядка:

                  d№Ym/dX№m =  (Ky/K№x)*(d№Yo/dX№o);  Ky/K№x=Ky*X№.

        Если функция У зависит от аргументов X и Z то для смешанных

        производных МК находятся из следующих выражений:

                       m _q    m  q    m   q

                d№Ym/dXm*dZm =_(Ky/Kx*Kz)*(d№Yo/dXo*dZo);

                         m q _  mxq

                     _Ky/Kx*Kx = Kyx .

        При моделировании могут иметь иесто случаи:

        1)Известна система уравнений,но из-за ее сложности аналити-

         ческое определение х-к по интересующему нас каналу затруд-

         нительно. В этом случае критерии для моделирования получа-

        ют обработкой системы уравнений.  2)Уравнеия  неизвестны,но

        достаточно  изучена качественная картина протекающих в объ-

        екте процессов.Искомые МК определяют с  использованием  ха-

        рактерных  для  этих процессов критериев подобия.  Например

        при исследовании процессов в жидких средах чпсто используют

        число Рейнольдса

                        Re=V*L/n,

    _   где n-  коэфф  кинематич  вязкости

        При одинаковых вязкостях объекта и модели условие подобия

                        Lm/Lo =Vo/Vm или Kv = 1/Kl

        говорит,что при  уменьшении  размеров модели по сравнению с

        объектом ее скорость необходимо увеличить в 1/Kl  раз.  При

        необходимости обеспечить подобие 2-х жидких сред используют

        критерий Ньютона

                        Ne =  F*l/m*v¤

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

        ется      критерием      Нуссельта:

               Nu=alpha*l/lambda

        где alpha-коэффициент теплоотдачи

            lambda-теплопроводность

        Соответствие между размерами и временем устанавливают  кри-

        терием гомохронности

                       _Ho = v*t/l.

        Общие закономерности,которым подчиняются различные виды по-

        добия,  формулируются в теории подобия в виде 3-х  основных

        теорем.2 первых устанавливают соотношения между параметрами

        заведомо подобных явлений,а 3-я определяет  условия,необхо-

        димые  и достаточные для того,  чтобы явления оказались по-

        добными.

        ПЕРВАЯ ТЕОРЕМА ПОДОБИЯ

        У подобных явлений существуют сочетания параметров,называе-

        мых критериями подобия,имеющие одинаковые значения.

        ВТОРАЯ_ТЕОРЕМА ПОДОБИЯ

        Всякое полное уравнение физического процесса,составленное в

        определенной системе единиц, может быть представлено в кри-

        териальной  форме,  т е в виде зависимости между критериями

        подобия.

        ТРЕТЬЯ ТЕОРЕМА ПОДОБИЯ

        Для обеспечения подобия необходимо и достаточно  пропорцио-

        нальность сходственных параметров,входящих в условия одноз-

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

        ния.  К  числу  наиболее распространенных условий однознач-

        ности относятся:  геометрия объекта,виды рабочих сред в ра-

        бочем пространстве и на границах объекта,начальные значения

        параметров,материалы и т п.

                                *   *

                                  *

        Если физическая модель создана ,то следующим этапом  иссле-

        дования  является  получение  математического описания этой

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

        исследования  одним из методов которые мы будем изучать.По-

        лученные на модели экспериментальные данные  обрабатываются

        и  составляется  ММ  в виде системы уравнений,  связывающих

        входные ивыходные параметры процессов в модели.  МО  модели

        является исходным материалом для расчета МО объекта. ПРИМЕР

        При экспериментальном исследовании модели получено  уравне-

        ние

                    3     3

             0.001*d Ym/dt  +0.02*d¤Ym/dt¤m + 1.47*dYm/dtm+Ym =

        _1.3Xm.

        Моделирование осуществлялось следующими  МК:

                Ky=0.5; Kx=0.1;  Kt=0.2.

        Определим дифференциальное  уравнение  объекта.  Полльзуясь

        формулами определения масштабных коэфициентов для производ-

        ных получим:

                                                              3_

          Kyt =  0.5/0.2  =2.5  ;Kyt¤  =  0.5/(0.2)¤=12.5;_Kyt_=

                                  3

                       = 0.5/(0.2)  =62.5.

         Подставляя МК в уравнение модели после пробразований полу-

         чим уравнение объекта:

                      3     3

            0.125*dYo/dto + 0.5*d¤Yo/dt¤o +  183.75*dYo/dto  +Yo  =

            = 4.26*Xo.

        Для сложных  объектов физическое моделирование осуществляют

        иногда по их отдельным частям,а  общее  МО  получают  чисто

        аналитическим путем.


Экспериментальные методы в математическом моделировании
Экспериментальные исследования  в математическом моделировании

Задачи эксперимента.  Существует  трудно перечислимое множество

экспериментальных задач, различаемых по отраслям науки,целям иссле-

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

рактеру воздействий,числу варьируемых факторов и т.  д.  и т.  п. В

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

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

МО объекта в виде его статических характеристик,т. е.уравнений свя-

зи между входными воздействиями и выходными переменными  в  устано-

вившиххся   режимах,при  затухших  переходных  процессах.  Методика

экспериментального получения динамических характеристик объектов  в

виде дифференциальных уравнений или других математических представ-

лений может иметь специфические особенности,связанные с  характером

входных  воздействий  как функций времени и структурой исследуемого

объекта.  Задача идентификации статических характеристик объекта  в

общем  случае  может  быть  представлена в виде известной концепции

"черного ящика",для  которого  на  момент  исследований  неизвестна

структура уравнений связи между входами и выходами:

                  +--------------+

       Вектор вход|   ОБЪЕКТ     |Вектор выхода

          ------->|              |---------->  Y(y1,y2,...,yn)

   X(x1,x2,...,xn)| ИДЕНТИФИКАЦИИ|

                  +--------------+

 "Осветление " черного ящика осуществляют поэтапно,задаваясь внача-

ле по возможности простыми формами связи между  входами  и  выхода-

ми,например  в виде линейных алгебраических уравнений,определяя ко-

эффициенты уравнений для этой структуры ММ и анализируя погрешности

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

довлетворительна,структуру  модели  постепенно   усложняют,например

вводя  в  нее  члены  с  квадратичными  и  более высокими степенями

составляющих вектора входа и повторяя весь цикл исследований снача-

ла  до  достижения необходимого качества модели либо до определения

того факта,что без учета дополнительных факторов достаточно  точная

модель  построена быть не может.  Здесь мы сталкипаемся с проблемой

оценки идентифицируемости и наблюдаемости исследуемой системы,тесно

связанной с ее управляемостью. (Совершенно очевидно,что идентифика-

ция ММ системы не является самоцелью -в подавляющем  числе  случаев

конечная  цель идентификации состоит в получении достаточной инфор-

мации для эффективного целенаправленного воздействия на объект,т  е

для  управления).  Понятие наблюдаемости и дуальное ему понятие уп-

равляемости впервые введены Калманом в 1960 г.Хотя  при  обсуждении

методов идентификации понятие наблюдаемости важнее,оба они ввиду их

дуальности рассматриваются обычно  совместно.  Говорят,что  система

является  управляемой,если  она может быть переведена из любого на-

чального состояния в пространстве допустимых состояний в другое же-

лаемое  состояние  в  тех  же пределах за конечный интервал времени

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

ляющее  воздействие влияет не на все параметры состояния системы,то

система не управляема.  Система наблюдаема,если ее состояние  можно

определить  по выходному вектору;другими словами,если есть неучтен-

ные компоненты вектора У,чувствительные к изменениям  вектора  вхо-

да,то  система не наблюдаема и не может быть идентифицирована.  Су-

ществуют критериальные методы определения управляемости и наблюдае-

мости систем,однако их изучение не входит в наши планы. Таким обра-

зом,наиболее  часто  повторяемым  действием  при  экспериментальной

идентификации систем является измерение и запоминание значений вхо-

дов и выходов объекта(составление таблиц  исходных  данных)  и  вы-

числение  по этим данным коэффициентов уравнений связи между входом

и выходом при заданной структуре этих уравнений.Эту задачу называют

концепцией  "серого  ящика".  Отметим,что  эксперимент по получению

исходных данных может быть пассивным( основанным на измерении  вхо-

дов  и выходов при нормальном функционировании объекта без целенап-

равленного изменения входов)или активным,планируемым,когда значения

входов  устанавливаются либо по жесткому,заранее составленному пла-

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

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

ОСНОВНЫЕ ПОНЯТИЯ ТЕОРИИ ВЕРОЯТНОСТЕЙ ФУНКЦИЯ  РАСПРЕДЕЛЕНИЯ-за-

висимость вероятности события X<x от значения х.

ПЛОТНОСТЬ РАСПРЕДЕЛЕНИЯ-производная  от функции распределения(плот-

ность вероятности)-неубывающая  функция  х,интеграл  от  которой  в

бесконечных пределах равен 1.Типовые функции(законы) распределения:

РАВНОМЕРНОЙ ПЛОТНОСТИ:

                f(x)=1/(b-a) при a<x>b и f(x)=0 в остальных случаях

ПУАССОНА:вероятность принятия случайной величиной значения n  выра-

жается формулой            -a

                Pn=(a№/n!)*e     (n=0,1,...)

(дисперсия равна матожиданию)

НОРМАЛЬНЫЙ ЗАКОН:

                -(x-m)¤/2*s¤ f(x)=(1/s*_2*pi)*e

где m-математическое ожидание,

    s-среднее квадратичное отклонение

МАТЕМАТИЧЕСКОЕ ОЖИДАНИЕ-сумма произведений всех возможных  значений

случайной  величины  на вероятности этих значений.Оценка М -среднее

арифметическое измеренных значений случайной величины.

ЦЕНТРИРОВАННАЯ СЛУЧАЙНАЯ ВЕЛИЧИНА-отклонение случайной  величины  х

от ее математического ожидания:x-Mx.

ДИСПЕРСИЯ-математическое ожидание квадрата центрированной случайной

величины:D[X]=M[X¤ц].

СРЕДНЕЕ КВАДРАТИЧНОЕ ОТКЛОНЕНИЕ s=_D.

ДОВЕРИТЕЛЬНЫЙ ИНТЕРВАЛ-интервал значений переменной,в который попа-

дает  истинное  значение с заданной вероятностью.Характеризует точ-

ность полученного значения.

ДОВЕРИТЕЛЬНАЯ ВЕРОЯТНОСТЬ-вероятность  того,что  истинное  значение

попадет  в данный доверительный интервал.Характеризует досоверность

значения

Первичная обработка результатов измерений.  Состоит в центриро-

вании  измеренных значений относительно оценки математического ожи-

дания(среднего арифметического по выборке) и  нормировании  относи-

тельно значения,наибольшего по абсолютной величине:

                 *

                Xij= (Xij-M[Xi])/maxabs(Xij-M[Xi]);

                 *

                Yij= (Xij-M[Xi])/maxabs(Yij-M[Xi]);

В результате  центрирования и нормирования все результаты измерений

располагаются по своим значениям в диапазоне от -1 до +1.  Эта нео-

бязательная прцедура позволяет однообразно оценивать поведение всех

зависимых и независимых переменных безотносительно  к  размерностям

Гипотеза о характере взаимсвязи между входом и выходом в простейшем

случае может быть сформулирована в виде линейной зависимости  между

измеренными значениями :

        Yt=A0*Xt0 + A1*Xt1 + ...+Am*Xtm +Et, где t=0,1...,n -номера

измерений

    n>m-количество измерений,

    Et-погрешность в  определении У(погрешностью определения Х пре-

       небрегаем),обусловленная влиянием неучтенных в модели факто-

       ров

В более общем случае гипотеза о структуре взаимосвязи формулируется

        Ytm=A0*f0(Xt) + A1*f1(Xt) + ...+Am*fm(Xt),

      где Ytm-модельное(расчетное)значение выхода Yt

 fi(Xti) i=0,1,...m-комплект  базовых  функций,линейная  комбинация

которых принимается в качестве структуры математической модели объ-

екта

   Ai i=0,1,...,m- в обоих  вариантах  свободные  параметры,которые

необходимо  рассчитать таким образом,чтобы получить наилучшие приб-

лижения расчетных и опытных значений У.

   Xt-вектор входа в t-м измерении. В практике аппроксимации задан-

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

рия  качества  приближения  :

1)критерий Гаусса,требующий  выбирать  свободные  коэффициенты так,

сумма квадратов разностей между расчетными и измеренными значениями

взятая по всем строкам исходной матрицы измерений ,была минимальной

(МЕТОД НАИМЕНЬШИХ КВАДРАТОВ)

2)критерий Чебышева,требующий  такого подбора коэффициентов модели,

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

опытными данными(МЕТОД МИНИМАКСА)

МЕТОД НАИМЕНЬШИХ  КВАДРАТОВ Разности между расчетными и таблич-

ными значениями

      Et=Ytm(Xt)-Yt=A0*f0(Xt) + A1*f1(Xt) + ...+Am*fm(Xt)-Yt. Сумма

квадратов этих разностей

           n

          ___

        Q=\  [Ytm(Xt)-Yt]¤

          /

          ---

          t=1

будет минимальной,если коэффициенты при базисных функциях  Ai  выб-

рать из условия равенства 0 частных производных Q по этим коэффици-

ентам

дQ/дAi=0 дQ/дA0=2*[A0*f0(Xt) + A1*f1(Xt)+...+Am*fm(Xt) -Yt]*f0(Xt);

дQ/дA1=2*[A0*f0(Xt) + A1*f1(Xt) +...+ Am*fm(Xt) -Yt]*f1(Xt);

        .         .           .

дQ/дAm=2*[A0*f0(Xt) + A1*f1(Xt) +...+ Am*fm(Xt) -Yt]*fm(Xt);

Это система линейных уравнений с искомым  вектором  A(A0,A1,...Am),

которая  называется системой нормальных уравнений.Матрица коэффици-

ентов этой системы состоит из скалярных произведений базисных функ-

ций

                     n

                    ___

            (fj,fk)=\  fj(Xt)*fk(Xt)   j=0,1,...,m;k=0,1,...,m;

                    /

                    ---

                    t=1

имеет вид:

         |(f0,f0)  (f0,f1) ... (f0,fm)|

         |(f1,f0)  (f1,f1) ... (f1,fm)|

         |      ......                |

         |(fm,f0)  (fm,f1) ... (fm,fm)|

и носит название матрицы Грама. Расширенная матрица системы уравне-

ний получается как обычно добавлением справа столбца свободных чле-

нов

               |(f0,Y)|

               |(f1,Y)|

               | ...  |

               |(fm,Y)|

где скалярные произведения вычисляются аналогично:

                n

               ___

       (fj,Y)= \  fj(Xt)*Yt)   j=0,1,...,m;

               /

               ---

               t=1

Основные свойства матрицы Грама:

-симметричность,т е неизменяемость элементов  при  взаимной  замене

индексов строк и столбцов;

-положительная определенность,что позволяет  отказаться  от  выбора

главных элементов;

-при линейно-независимых базисных функциях определитель матрицы от-

личен от 0 и система имеет единственное решение;

Базис обычно формируют вначале из одной-двух функций,а затем расши-

ряют  до получения адекватной исходным данным аппросимирующей функ-

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

ходом  и  входом.

ПРОВЕРКА АДЕКВАТНОСТИ  МОДЕЛИ-обязательная  операция,основанная  на

вычмслении  отношения остаточной дисперсии выходной переменной к ее

общей дисперсии:

        F=Dост/Dобщ <=Fкр Fкр

находят по таблицам распределения Фишера для заданной доверительной

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

значимость,обычно по величине доверительного интервала и  дисперсии

наблюдений:

        |Ia|<s*e

где у  берут из таблиц распределения Стьюдента для заданной довери-

тельной вероятности.Незначимые коэффициенты означают нечувствитель-

ность выхода к вариациям значений соответствующих аргументов модели

и эти члены уравнения необходимо из нее удалить.Неприятность состо-

ит  в том,что коэффициенты полученного описанным способом уравнения

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

всех оставшихся.

СТЕПЕННОЙ БАЗИС  Чще  всего  в  качестве  базисных функций выбирают

последовательность степеней аргумента,которые являются линейно  не-

зависимыми:

               f0(X)=X =1; f1(X)=X =X; ... fm(X)=X ;

Аппроксимирующая функция становится при этом полиномом степени m<<n

Расширенная матрица системы приобретает вид:

             n        n             n          n

            ---      ---           --- m      ---

        n+1 \ Xi     \ Xi¤ ...     \ Xi       \ Yi

            /        /             /          /

            ---      ---           ---        ---

            i=0      i=0           i=0        i=0

      n      n        n             n           n

     ---    ---      --- 3         --- m+1     ---

     \ Xi   \ Xi¤    \ Xi  ...     \ Xi        \ Xi*Yi

     /      /        /             /           /

     ---    ---      ---           ---         ---

     i=0    i=0      i=0           i=0         i=0

         .         .             .

      n       n        n             n          n

     --- m   --- m+1  --- m+2       --- 2*m    --- m

     \ Xi    \ Xi     \ Xi    ...   \ Xi       \ Xi *Yi

     /       /        /             /          /

     ---     ---      ---           ---         ---

     i=0     i=0      i=0           i=0         i=0

При формировании этой матрицы достаточно вычислить только элементы

1-й строки и 2-х последних столбцов,а остальные элементы "не ориги-

нальны" и могут быть заполнены циклическим присвоением. Для решения

систем уравнений с матрицей Грама  разработаны  специальные  методы

сингулярного  разложения,с которыми вы сможете познакомиться напри-

мер по книге Форсайта Малькольма Моулера "Машинные методы математи-

ческих  вычислений",но  для  полиномов  до  4-й  5-й стпеней вполне

достаточно известных вам методов Гаусса или  ортогонализации.  Итак

общий  алгоритм  вычислений включает в себя:  -формирование матрицы

Грама и расширенной матрицы -Решение  системы  линейных  уравнений.

При  пассивном  эксперименте и степенных базисных функциях типа от-

резков ряда Тейлора избежать пересчета коэффициентов  при  удалении

незначимых членов нельзя-для этого матрица Грама должна быть диаго-

нальной  или  хотя  бы  существенно   диагонально-преобладающей,что

обеспечивает  распад системы уравнений на набор назависимых уравне-

ний с одной неизвестной.Очевидно  что  скалярные  произведения  ба-

зисных функций с разными индексами должны быть равны 0. Достигается

это либо использованием активного эксперимента с ортогональным пла-

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

выбираются из условия равенства 0 скалярных  произведений  функций,

либо использованием в качестве базисных функций специальных ортого-

нальных полиномов  например  Чебышева  или  Лаггера  или  Лежандра.

Свойства  классических  ортогональных  полиномов заключаются в том,

что для каждого типа существует отрезок [X0,Xn],на котором  обраща-

ются в 0 скалярные произведения полиномов различных порядков,взятых

с некоторой,завмсящей от Х весовой функцией р(Х):

        Xn

         Ї p(X)*fj(X)*fk(X)*dX = 0  j!=k

         ї

         X0 Так например полиномы Чебышева определены и ортогональ-

ны в интервале [-1,+1] c весовой функцией p(X)=(1-X¤)^-.5 и их зна-

чения вычисляются по рекуррентной формуле

        Tk+1(X)=2*Tk(X)-Tk-1(X) T0(X)=1; T1(X)=x.

Полиномы Лежандра Lk+1(X)=[(2*k+1)*x*Lk(X)+k*Lk-1(X)]/(k+1)

 L0(X)=1, L1(X)=x ортогональны на отрезке [-1,+1] с весовой функци-

ей p=1.

Тема :Введение в методы ортогонального планирования экспериментов

Урок :Основные понятия

Основные понятия.

1)Множество всех точек проведения экспериментов

       Xi =(Xi1,Xi2,...,Xin),  i=1,2,...,N представляется с помощью

матрицы плана

      |

      | X11 X12 ... X1n |

      | X21 X22 ... X2n |

 X =  | ............... |

      | XN1 XN2 ... XNn |

и называется планом эксперимента

 2)Точка          N__

                  \

         X0=(1/N)*/  Xi

                  ---

                  i=1

называется центром плана

3)План называют  центральным,если его центр расположен в начале ко-

ординат X=0,т е X0=0.

3)Всякий план Z с точками Zi путем  замены             N

X=Z-Z0 при                                     Z0=(1/N)\Zi

                                                       /

                                                       i=0

может быть преобразован в центральный план X с  точками  Xi.

4)Область возможных значений независимых переменных (факторов)назы-

 авают областью планирования эксперимента,которая может быть  зада-

 на,например,

           -1<=Xi<=1, i=1,2,...,n

(областью  планирования является гиперкуб)

Границы -1  и +1 отвечают  нормированным факторам,переход к которым

можно осуществить например так:

               *       *        *        *         *

               Xi= ( Xi - (Ximax + Ximin)/2)/((Ximax-Ximin)/2)

*-натуральный масштаб

5)План X называется ортогональным,если дает диагональную информаци-

онную матрицу

6)План  Х  называется  ротатабельным,если  дисперсия оценки

зависимой переменной в

точке х зависит только от расстояния

от центра плана

ПРИМЕР Пусть модель имеет вид

                      Y(a,x)=a0+a1*x1+a2*x2

область    планирования

                     -1<=x1<=1

                     -1<=x2<=1

План

              |-1   -1|

          X=  |+1   -1|

              |-1   +1|

              |+1   +1|

дает диагональную информационную матрицу 4*I3

Тема :Введение в методы ортогонального планирования экспериментов

Урок :Основные понятия

Основные понятия.

1)Множество всех точек проведения экспериментов

       Xi =(Xi1,Xi2,...,Xin),  i=1,2,...,N представляется с помощью

матрицы плана

      |

      | X11 X12 ... X1n |

      | X21 X22 ... X2n |

 X =  | ............... |

      | XN1 XN2 ... XNn |

и называется планом эксперимента

 2)Точка          N__

                  \

         X0=(1/N)*/  Xi

                  ---

                  i=1

называется центром плана

3)План называют  центральным,если его центр расположен в начале ко-

ординат X=0,т е X0=0.

3)Всякий план Z с точками Zi путем  замены             N

X=Z-Z0 при                                     Z0=(1/N)\Zi

                                                       /

                                                       i=0

может быть преобразован в центральный план X с  точками  Xi.

4)Область возможных значений независимых переменных (факторов)назы-

 авают областью планирования эксперимента,которая может быть  зада-

 на,например,

           -1<=Xi<=1, i=1,2,...,n

(областью  планирования является гиперкуб)

Границы -1  и +1 отвечают  нормированным факторам,переход к которым

можно осуществить например так:

               *       *        *        *         *

               Xi= ( Xi - (Ximax + Ximin)/2)/((Ximax-Ximin)/2)

*-натуральный масштаб

5)План X называется ортогональным,если дает диагональную информаци-

онную матрицу

6)План  Х  называется  ротатабельным,если  дисперсия оценки

зависимой переменной в

точке х зависит только от расстояния

от центра плана

ПРИМЕР Пусть модель имеет вид

                      Y(a,x)=a0+a1*x1+a2*x2

область    планирования

                     -1<=x1<=1

                     -1<=x2<=1

План

              |-1   -1|

          X=  |+1   -1|

              |-1   +1|

              |+1   +1|

дает диагональную информационную матрицу 4*I3


Отделение корней

ПРЕДМЕТ:ЧИСЛЕННЫЕ МЕТОДЫ(Алгоритмы математических вычислений)

ВВЕДЕНИЕ ПО ПРЕДМЕТУ В ЦЕЛОМ

Прикладная  математика занимается разработкой технологических

процессов вычислений,то есть представлением процесса вычислений в

виде последовательности элементарных процедур,доступных для

выполнения исполнителем соответствующей квалификации.

В нашем теоретическом курсе мы будем изучать ряд вычислительных

алгоритмов,используемых в различных областях практической

деятельности и их представление на формализованном алгоритмическом

языке ВС++,отражающем возможности нашего исполнителя.

 Лабораторный практикум будет содержать программную реализацию

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

 Методы вычислений делят на аналитические и алгоритмические(прибли-

женные).

Аналитические методы вычислений используются в тех очень редких

на практике случаях,когда входные и выходные данные удается

связать между собой точным аналитическим выражением(формулами).В

подавляющем большинстве практических задач сделать это не

удается-либо из-за отсутствия математической модели,связывающей

требуемые результаты с исходными данными,либо из-за чрезмерной

сложности этой модели ,либо сами исходные данные представлены в

форме,исключающей возможность прямых аналитических вычислений.

 Приведем простой пример:

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

за израсходованный в производстве природный газ,имеется диаграммная

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

период;

Если бы нарисованная самопишущим прибором на ленте кривая была

представлена аналитической функцией времени,осталось бы

проинтегрировать ее на заданном интервале и вычислить полученное

аналитическое выражение.Но в силу случайного характера изменений

расхода газа во времени представить имеющуюся зависимость расхода

от времени в аналитической форме невозможно и остается использовать

алгоритмы приближенного интегрирования.

 К такому же выводу придем каждый раз при попытке решения

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

алгебраическими -даже простейший случай уравнения с одним

неизвестным

                    a*X +b*cos(c*X)=0

не позволяет получить аналитические выражения для вычисления корней

и вынуждает нас использовать приближенные численные методы.

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

погрешностей получаемых результатов.Результирующая погрешность

(несоответствие между точным и вычисленным значениями)образуется

с помощью ряда составляющих:

-погрешности в задании исходных данных;

-погрешности моделирования зависимости между исходными данными и

 вычисляемыми переменными;

-методические погрешности,определяемые несовершенством

 вычислительных алгоритмов;

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

 сетки используемых  для вычислений технических устройств.

 Совершенно очевидно,что бессмысленно выдвигать высокие требования

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

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

ОБЩАЯ МЕТОДИКА ВЫПОЛНЕНИЯ ЛАБОРАТОРНЫХ РАБОТ ПО ЧИСЛЕННЫМ МЕТОДАМ

 Численное решение той или иной задачи-это всегда исследование

некоторого объекта;в практической работе такое исследование

проводится обычно определением(например с помощью приборов)

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

В учебном процессе под рукой не оказывается подходящего

химического реактора или экологической системы или какой-нибудь

банковской сети и т п

Поэтому приходится работать с имитационными моделями исследуемых

объектов и сам процесс программной  имитации требует времени,

определенных усилий по программированию ,отвлекая внимание студента

от освоения собственно метода решения задачи и затемняя ее сущность

не относящимися к теме деталями.

В предлагаемом цикле лабораторных работ по численным методам

студенту предстоит работать с объектами типа "черного ящика"-

вам нет необходимости знать ,что он собой представляет,вы просто

подключаете в вашу программу заголовочный файл по имени

                      mxx.h

(здесь хх-это номер темы выполняемой лабораторной работы,а буковка

m просто обозначает слово модель )

После этого в вашей программе вы конструируете объект класса

                      mxxx

     (третья буква х-это номер работы по текущей теме)

Разумеется,придется вспомнить азы объектного программирования.

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

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

Например,для уточнения корня нелинейного уравнения Вам необходимо

знать нижнюю и верхнюю границы исходного интервала неопределенности

,а затем в процессе вычислений получать значения зависимой

переменной при заданном вами значении независимой.Поступаем так:

#include "m51.h"

void main(void)

{

  m511 ob;                       //создаем исследуемый объект

  double с=ob.getmin();         //получаем нижнюю границу интервала

  double d=ob.getmax();         //получаем верхнюю границу

  double y;                    //переменная для зависимого значения

  //...

  //где-то в программе

  y=ob.getvalue((с+d)*0.5);//получаем зависимое значение

                                     //в середине интервала

  //...

}

 Номера тем и работ по теме Вам будут указываться в задании,

одновременно будут перечисляться методы доступа к данным вашего

"черного ящика".

 Возникающие вопросы можно разрешить просто вызвав на экран

заголовочный файл m5xx и рассмотрев его содержимое-данные класса и

методы доступа к ним.

ТЕМА 51:РЕШЕНИЕ НЕЛИНЕЙНЫХ АЛГЕБРАИЧЕСКИХ И ТРАНСЦЕНДЕНТНЫХ

УРАВНЕНИЙ

1.ВВЕДЕНИЕ ПО ТЕМЕ

Во  многих  научных  и  инженерных  задачах возникает необходимость

решения уравнений вида

                F(x,p1,p2,...pn)=0      (1)

где F-заданная функция ,  х-неизвестная величина

р1,р2,...,рn-параметры задачи(напрпимер,коэффициенты уравнения).

Как правило, исследователя интересует поведение решений в

зависимости от параметров pk .

 При каждом фиксированном наборе этих параметров уравнение (1)

может иметь либо конечное ,либо бесконечное количество решений х ,

что  соответствует  определенному  физическому смыслу конкретной

задачи.

Не нарушая общности задачи можно поменять местами неизвестное х

и любой из параметров pk т.е. решать уравнение относительно другой

неизвестной величины.Решениями или корнями уравнения (1) называются

такие значения х,которые при подстановке  в уравнение обращают его

в тождество.Решение в аналитическом виде удается найти только для

простейших уравнений,а в большинстве случаев приходится

использовать численные методы.

В результате численного решения получают таблицы зависимостей  х

от  параметров  pk-эти таблицы можно представить в наглядной форме

с помощью средств машинной графики

Численное решение обычно проводят в 2 этапа:на 1-ом отделяют корни

(находят интервалы,содержащие только 1 корень),а на 2-м проводят

уточнение их значений до заданной погрешности.

РАЗДЕЛ 511 : ОТДЕЛЕНИЕ КОРНЕЙ

Для отделения корней не существует достаточно общих методов.Как

правило используют ТАБЛИЧНЫЙ или ГРАФИЧЕСКИЙ методы отделения-

в интересующей нас области  получаем ряд значений левой части

уравнения ,варьируя переменную х с некоторым выбранным шагом ,а

затем либо строим график функции при фиксированных параметрах pk,

либо используем непосредственно таблицу ,определяя интервал,

содержащий  корень между точками с разными знаками функции F(x).

Шаг изменения аргумента при этом должен быть меньше минимального

расстояния  между  корнями , иначе отделить корни не удастся-хотя

совершенно очевидно ,что расстояние между корнями нам априори

вряд ли может быть известно...

ЗАДАНИЕ НА ЛАБОРАТОРНУЮ РАБОТУ 511 ПО РАЗДЕЛУ

Для одномерного без помех(нешумящего) объекта класса 511 выполнить

задачу табличного отделения корней (с выводом на экран таблицы

значений незавмсимой и зависимой переменных) в диапазоне с нижней

границей,возвращаемой функцией-членом класса m511

          double  getmin(void) ;

и верхней границей,возвращаемой функцией-членом

          double  getmax(void) ;

Значение зависимой переменной при любом аргументе в этом диапазоне

можно получить вызовом функции-члена

          double  getvalue(double);

Если Ваш аргумент выйдет за пределы диапазона,функция вернет

значение,скрывающееся под именем MAXDOUBLE(максимально возможное

double,которое определено в интерфейсном файле values.h ) и

высветит предупреждающее сообщение

Вам предлагается распечатать на экране в 1-м варианте всю таблицу

а во 2-м только диапазоны,содержащие как минимум 1 корень.

Вся таблица м.б.построена например так

 m511 test;

 В цикле от test.getmin() до test.getmax() с шагом ,допустим,таким-

            (test.getmax()-test.getmin())/300)

            распечатываем test.getvalue(х).


Метод дихотомии
Метод дихотомии(половинного деления)

РАЗДЕЛ:Метод дихотомии(половинного деления,метод Больцано)

Считаем что отделение корней уравнения проведено и на отрезке [a,b]

расположен 1 корень который необходимо уточнить с допустимой

погрешностью е

Метод заключается в следующем:определяем середину отрезка [a,b]

               x = (a+b)/2

и  вычисляем функцию F(x). Далее делаем выбор из 2-х частей отрезка

для дальнейшего уточнения корня.

Алгоритм реализации примерно такой:

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

  погрешности,

   {

     Берем точку х посредине интервала (с;d) и,

     если знаки функции в точках с и х совпадают,то в х переносим

     с,а иначе - d

   }


Метод хорд
 Метод хорд.

 Метод хорд так же, как и метод дихотомии, предназначен для

уточнения корня на интервале, на концах которого функция принимает

значения разных знаков. Очередное приближение теперь в отличие от

метода дихотомии берем не в середине отрезка, а в точке х, где

пересекает ось абсцисс прямая линия, проведенная через текущие

границы интепвала неопределенности

 Используя минимум знаний о подобных треугольниках вы легко

выведете формулу для определения коэффициента деления интервала

  он равен отношению значения функции на левой границе к разности

значений функции на левой и правой границах .

Задание по работе:пройти тестирование по этому уроку, составить

и отладить программу реализации метода хорд,используя либо

свободный собственный формат ввода-вывода,либо предоставляемый

Вам шаблон Windows-приложения


Метод секущих

  Метод секущих представляет собой разновидность метода касательных

,но производная определяется приближенно по отношению

приращения функции к приращению аргумента,а следующая точка

определяется по формуле

        Xk+1=[Xk-(Xk-Xk-1)/(f(Xk)-f(Xk-1))] * f(Xk)     (1)

Геометрически мы от аппроксимации функции касательной переходим

к секущей-прямой,проходящей через 2 значения функции

Таким образом для начала итерационного процесса необходимо выбрать

2 начальных приближения к корню:Х1  и Х2,а каждое новое приближение

Х3 получим по формуле (1)

Закончим итерации ,когда |Х3-Х2|<e(заданной погрешности вычисления

корня)

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

располагаться с одной или с разных сторон корня и знаки функции при

уточнении корня не повторяются

Алгоритм реализации может выглядеть так:

   double x3,k;

   double x1=test.getmin();

   double x2=test.getmax();

   while(fabs(x3-x2)>e)

   {

     k=test.getvalue(x2)/(test.getvalue(x2)-test.getvalue(x1));

     x3=x2-k*(x2-x1);

     x1=x2;

     x2=x3;

   }

Реализуйте этот алгоритм в программе и пройдите тестирование по

этому уроку


Метод Ньютона
Метод Ньютона (метод касательных)

Вычислим левую часть уравнения на левой границе интервала и

производную в этой же точке.

  Для получения значения производной используйте метод класса m515

getderive().

  Следующим приближением к корню будем считать точку х1, где

касательная к функции пересекает ось абсцисс.(Метод вычисления

значения х1 легко находится из геометрических соотношений)

Затем полученную точку х1 будем считать начальной и продолжим

итерационный процесс.

  Основная формула метода Ньютона для k-го шага итераций имеет вид:

                    Xk+1=Xk-f(Xk)/f'(Xk) (1)

МЕТОД ньютона-алгоритм реализации

void main(void)

{

   m515 test;

   double x1,x2,k,e=0.00000001;

   Выберем начальное приближение х1==х2 в середине интервала

   неопределенности

   До тех пор, пока |x2-x1|>e,переносим значение следующей точки х2

   в новое начальное приближение х1 и по формуле (1) вычисляем

   следующее приближение х2

     x1=x2;

     x2=x1-test.getvalue(x1)/test.getderive(x1);

  Эти строки и реализуют циклически повторяемый алгоритм уточнения

  Преимущество метода - высокая скорость сходимости, недостаток -

  необходимость вычисления не только значения функции, но и ее

  производной

  Задание на работу стереотипно:пройти тестирование,составить и

  отладить Си++ программу реализации метода


Метод случайного поиска

  Можно придумать множество способов деления интервала

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

 Можно предположить также, что в среднем не хуже других будет

случайный выбор этой точки внутри интервала.

  Это равносильно присвоению коэффициенту деления на каждом шаге

  итерации случайного числа в интервале (0;1)

           double k=(double)rand()/(double)RAND_MAX;

 Программу реализации метода можно получить заменой в методе

дихотомии или хорд выражения для коэффициента K


Метод половинного деления

Нетрудно доказать что если в нашем распоряжении только 2 опыта

то лучшее что можно сделать для получения информации с целью

максимального приближения к экстремуму-это провести эти 2 опыта в

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

После переноса одной из границ в более удаленную от экстремума

точку длина интервала будет больше половины первоначального как раз

на расстояние между опытами.

  Следующие опыты можно осуществить снова в средине интервала и т д

- эта простейшая и достаточно эффективная cтратегия активного

эксперимента называется методом половинного деления (дихотомии).

ЗАДАНИЕ ПО ЛАБОРАТОРНОЙ РАБОТЕ

Составить программу поиска экстремума по методу половинного деления

интервала неопределенности при поиске экстремума унимодальной

функции одной переменной

/*

                         Метод. помощь

  Определим границы интервала для исследуемого объекта

  Пока ширина интервала больше допустимой погрешности : берем точку

х посредине интервала,затем смещаемся влево и вправо как можно

ближе к точке х. В худшую точку (отстоящую дальше от экстремума)

переносим ближайшую к ней границу интервала :

 if(test.sign(test.getvalue(r)-test.getvalue(l))<0)c=l;else d=r;

*/


Метод Фибоначчи

Существует более совершенный метод,чем метод Больцано-это

предложенный в 1953 г Кифером е-минимаксный алгоритм ,основанный

на использовании чисел Фибоначчи для определения коэффициента

деления текущего интервала неопределенности

Пусть в нашем распоряжении n экспериментов для исследования на

экстремум исходного интервала единичной длины .

Предположим что все эксперименты кроме одного уже проведены и

оставшийся интервал имеет длину Ln-1 ,а внутри его находится

оставшийся опыт,давший лучшее приближение к экстремуму и поэтому не

ставший новой границей интервала.Так как у нас остался единственный

опыт в запасе, то не видно лучшего решения ,как провести его

симметрично уже имеющемуся относительно середины интервала.

  Чтобы эта симметрия соблюдалась и на более ранних опытах, длины

интервалов должны удовлетворять условию

                        Lj-1=Lj+Lj+1

что возможно если первый опыт провести в 2-х точках, отстоящих от

противоположных концов исходного интервала в следующей доле его

длины:

                    (Fn-2+(pow(-1,n)*e))/Fn

где Fn-2,Fn-числа Фибоначчи соответствующих номеров.

Одна из внутренних точек станет новой его границей, а вторая

окажется внутри интервала и следующий опыт необходимо провести

просто симметрично этой оставшейся точке относительно середины

нового интервала.

  Подробное доказательство этого вы найдете в книге Дж Уайлда

"Методы поиска экстремума"

ЗАДАНИЕ ПО ЛАБОРАТОРНОЙ РАБОТЕ

Составить программу поиска экстремума унимодальной функции

одной переменной по методу Фибоначчи

//Метод Фибоначчи - help on help

  Зададимся числом опытов - м.б.,40.

   int n=40;

  Получаем границы

   double c=test.getmin();

   double d=test.getmax();

  Вычисляем коэффициент деления интервала

   k=(test.fib(n-2)+pow(-1,n)*e)/test.fib(n);

  и рассчитываем координаты 2-х первых точек эксперимента, уменьшая

его

   x2=c+k*(d-c);

   x1=d-k*(d-c);

  Далее в цикле в худшую точку переносим ближайшую границу, а

добавочную точку определяем симметрично оставшейся относительно

середины интервала

   for(int i=n;i>1;i--)

   {

     if(test.sign(test.getvalue(x1)-test.getvalue(x2))>0)

       {c=x1;x1=c+d-x2;}

     else

       {d=x2;x2=c+d-x1;}

   }


Метод золотого сечения

Начиная поиск экстремума,экспериментатор часто не знает,сколько

опытов ему для этого понадобится-обычно он собирается проводить

свои эксперименты до достижения поставленной цели т е пока

интересующий его критерий не будет удовлетворен

Однако каждый хотел бы использовать быстро сходящийся метод,

экономящий время и средства на экспериментирование.

Как вы уже знаете наиболее эффективным теоретически является

метод Фибоначчи:но воспользоваться им можно только заранее зная

число испытаний т к первый опыт рассчитывается по числам

Фибоначчи,номера которых зависят от общего числа опытов

Существует метод, почти столь же эффективный как и метод Фибоначчи

и не зависящий от количества запланированных опытов т е со

свободным завершением

По этому методу предлагается выдерживать постоянным отношение длин

последовательных интервалов неопределенности

                          Lj-1/Lj=Lj/Lj+1=t

откуда приходим к квадратному уравнению

                             t*t=t+1

имеющему 1 положительный корень 1.618033989...-золотое сечение

По результатам 2-х экспериментов устанавливается в каком из

сегментов интервала опыты будут продолжены.В этом оставшемся

интервале уже есть 1 предыдущий эксперимент и для продолжения

достаточно провести симметричный ему опыт

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

                         Ln=1/pow(t,n-1)

Метод предложен Кифером и Джонсоном и при большом числе опытов

практически совпадает по эффективности с методом Фибоначчи

ЗАДАНИЕ ПО ЛАБОРАТОРНОЙ РАБОТЕ

Составить программу поиска экстремума унимодальной

функции 1-го аргумента используя имитационный объект класса m523

для получения необходимых в алгоритме данных

МЕТОДИЧЕСКАЯ ПОМОЩЬ

вряд ли необходима т к метод мало отличается по формальному

алгоритму например от метода дихотомии

Мы откладываем вправо от левой границы интервала отрезок

в 0.618...его длины и влево от правой границы столько же,

по полученным 2-м точкам определяем какую границу передвинуть

и добавляем следующую точку симметрично оставшейся внутри

интервала

//Метод золотого сечения

void main(void)

{

   m523 test;

   double x,l,r,e=0.0000001;

   double k=0.618033989;

   double c=test.getmin();

   double d=test.getmax();

     r=c+k*(d-c);

     l=d-k*(d-c);

   while(fabs(d-c)>e)

   {

     if(test.sign(test.getvalue(r)-test.getvalue(l))>0)

       {c=l;l=d-k*(d-c);}

     else

      {d=r; r=c+k*(d-c);}

   }

   cout<<"Минимум : "<<(c+d)/2<<"\t"<<test.getvalue((c+d)/2)<<endl;

   c=test.getmin();

   d=test.getmax();

     r=c+k*(d-c);

     l=d-k*(d-c);

   while(fabs(d-c)>e)

   {

     if(test.sign(test.getvalue(r)-test.getvalue(l))<0)

       {c=l;l=d-k*(d-c);}

     else

       {d=r; r=c+k*(d-c);}

   }

   cout<<"Максимум :"<<(c+d)/2<<"\t"<<test.getvalue((c+d)/2)<<endl;

}


Метод координатного спуска

Алгоритм координатного спуска используется в многомерных задачах

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

задачи к последовательности одномерных,решаемых методами

минимизации функции одной переменной,например золотого сечения

-Вначале в заданной области определения функции всем координатам,

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

делается зависимой только от одной переменной.

-Ищем условный минимум функции варьируя одну свободную переменную

-Полученная координата условного минимума фиксируется в найденном

значении и ищется условный минимум вариацией следующей переменной

-После использования всех координат процесс снова продолжается

с первоначальной переменной и т д

Если в области минимума функция цели достаточно гладкая то процесс

спуска по координатам будет линейно сходиться к минимуму

Доказательство этого и трудности поиска многомерных функций при

сложных рельефах поверхностей рассмотрены в книге

Калиткин Н Н "Численные методы"М Наука 1978

Д Дж Уайльд "Методы поиска экстремума" М Наука 1967

и в других учебниках

В сходящемся процессе расстояния между соседними точками

однокоординатных минимумов будут стремиться к 0,что можно

использовать для формулировки условия завершения итерационного

процесса

Недостатком метода является чрезмерно большой объем экспериментов

и опасность группирования опытов вокруг ложного экстремума при

сложном рельефе исследуемой поверхности

ЗАДАНИЕ НА ЛАБОРАТОРНУЮ РАБОТУ

Пройти тестирование по уроку 524

Составить и отладить программу реализации метода координатного

спуска для  объектов класса m524 с априори неизвестной размерностью

МЕТОДИЧЕСКАЯ ПОМОЩЬ

В качестве методической помощи вам дается программа координатного

спуска для 2-мерного поиска

Обобщите ее на случай произвольной мерности поиска

//Метод координатного спуска

void main(void)

{

   m524 test;

   double x1,x2,l1,l2,r1,r2,e=0.0000001;

   double k=0.5618033989;//золотое сечение

   double c1=x1=test.getminx1();

   double c2=x2=test.getminx2();

   double d1=test.getmaxx1();

   double d2=test.getmaxx2();

   double dx1=5.0,dx2=5.0;

   while(fabs(dx1)>e||fabs(dx2)>e)

   {

     c1=test.getminx1();

     c2=test.getminx2();

     d1=test.getmaxx1();

     d2=test.getmaxx2();

Одномерный поиск по первой координате

     while(fabs(d1-c1)>e)

     {

       l1=c1+k*(d1-c1);

       r1=d1-k*(d1-c1);

       if(test.sign(test.getvalue(r1,x2)-test.getvalue(l1,x2))>0)

         c1=l1;

       else

         d1=r1;

     }

     dx1=x1-(c1+d1)/2;

     x1=(c1+d1)/2;

Одномерный поиск по второй координате

     while(fabs(d2-c2)>e)

     {

       l2=c2+k*(d2-c2);

       r2=d2-k*(d2-c2);

       if(test.sign(test.getvalue(x1,r2)-test.getvalue(x1,l2))>0)

         c2=l2;

       else

         d2=r2;

     }

     dx2=x2-(c2+d2)/2;

     x2=(c2+d2)/2;

   }

  cout<<"Минимум:"<<x1<<"\t"<<x2<<"\t"<<test.getvalue(x1,x2)<<endl;

}


Метод градиентного спуска

Турист,желающий подняться на вершину поросшего лесом холма,

достигнет цели если будет непрерывно подниматься вверх,а если он

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

Реализация этого интуитивно понятного метода носит название

метода крутого восхождения или метода градиента

Помимо направления в математической реализации метода необходимо

иметь и алгоритм выбора шага поиска-при слишком большом шаге

неизбежны потери на рыскание вокруг экстремума,а при слишком

маленьком движение к цели будет медленным

Градиент функции f(X1,X2,...Xn) в каждой точке направлен в сторону

наискорейшего локального возрастания этой функции.

Для поиска максимума необходимо двигаться в направлении градиента,

а для поиска минимума - в противоположном.

Для определения направления движения(приращения текущих координат

независимых переменных) придется вычислять производные целевой

функции по каждой координате(составляющие градиента) и нормировать

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

направление(направляющие косинусы вектора градиента или что то же

самое составляющие вектора единичной длины с тем же направлением

что и вектора градиента)

Теперь о величине шага.При наличии погрешностей в определении

значения целевой функции шаг должен убываать по мере приближения

к цели,т е его величина должна быть членом последовательности

беззнаковых чисел,удовлетворяющей условию

                lim An = 0 при n->бесконечности

Сумма членов этой последовательности должна расходиться

                Сумма An->бесконечн при n=бескон

а сумма квадратов-сходиться

                Сумма An*An<бесконечн

Этим 3-м условиям удовлетворяет например гармоническая

последовательность чисел 1/n:1,1/2,1/3,...

Эти условия сформулированы Дворецким в его известной теореме.

Кестен показал,что асимптотическая сходимость может быть заменена

затухающим колебательным процессом,если длину шага уменьшать по

гармоническому закону только после того,как соответствующая

составляющая градиента поменяет знак

ЗАДАНИЕ НА ЛАБОРАТОРНУЮ РАБОТУ

Пройти тестирование по 525-му уроку

Составить программу реализации метода градиента с использованием

методов доступа к исследуемому объекту

МЕТОДИЧЕСКАЯ ПОМОЩЬ ПРИ ВЫПОЛНЕНИИ

//Метод градиентного спуска

m525 test;

double *_grad,*step,*x;//Указатели на массивы составляющих

                         градиента,шага по каждой составляющей

                         и координат очередной точки

int r;

Нам понадобится функция для численного дифференцирования

которая принимает номер координаты и массив координат и возвращает

приближеннную оценку частной производной

double getderive(int i,double *x)

{

  Приращение аргумента возьмем в 1/1000 интервала ее определения

  double dx=fabs(test.getmax(i)-test.getmin(i))/1000;//l=0.000001;

  int flag=0;

  double old=test.getvalue(x);    //Старое значение функции

  if((x[i]+dx)>test.getmax(i)){flag=1;x[i]-=dx;}

  else{x[i]+=dx;flag=0;}

  double _new=test.getvalue(x);   //Значение функции после

                                   приращения аргумента

  if(flag)x[i]+=dx;else x[i]-=dx;

  return (_new-old)/dx;           //Возвратим отношение-оценку

                                производной

}

При наличии функции вычмсляющей частную производную вычисление

градиента сводится к последовательгности вызова этой функции

для каждой координаты

 void grad(double *x)

{for(int i=0;i<r;i++) _grad[i]=getderive(i,x); }

Подпрограмма нормирования делит все составляющие градиента на

корень квадратный из суммы квадратов составляющих т е на модуль

вектора градиента,выделяя единичный вектор напрвления

или вектор напрвляющих косинусов

double norm(void)

{

  double sum=0;

  for(int i=0;i<r;i++) sum+=_grad[i]*_grad[i];

  for(i=0;i<r;i++)   _grad[i]/=sqrt(sum);

  return (sum);

}

void main(void)

{

  double e=0.00001,cons,old,_new;

  r=test.getrange();  //Получаем размерность системы

  _grad=new double[r];//Выделяем память под массивы

  step=new double[r];

  x=new double[r];

  for(int i=0;i<r;i++)

  {

    x[i]=(test.getmax(i)+test.getmin(i))/2;//Cтартовая точка

    step[i]=fabs(x[i])/100;                //Начальный шаг

  }

  int j=2;

  do

  {

    grad(x);            //Вычисляем градиент

    cons=norm();       //Определяем направление

    old=test.getvalue(x);

    for(i=0;i<r;i++)

if((x[i]+step[i]*_grad[i])>=test.getmin(i)&&(x[i]+step[i]*_grad[i])

<=test.getmax(i))

        x[i]+=-step[i]*_grad[i];//Шагаем в противоположном

                                 направлении для минимизации

    _new=test.getvalue(x);

    if(_new>old) //Если проскочили цель то уменьшаем шаг

   { for(i=0;i<r;i++)

      step[i]/=j;

    j++;}

  }while(cons>e);

  for(i=0;i<r;i++)

    cout<<x[i]<<"\t";

  cout<<(_new+old)/2<<endl;

  delete _grad;

  delete x;

  delete step;

}


Метод последовательного симплексного поиска

МНОГОМЕРНЫЙ ПОИСК

РАЗДЕЛ: ПОСЛЕДОВАТЕЛЬНЫЙ СИМПЛЕКСНЫЙ ПОИСК (ПСМ) СУБОПТИМАЛЬНОЙ

         ОБЛАСТИ  В МНОГОМЕРНОМ ПРОСТРАНСТВЕ

Вводная часть.

Метод был предложен в 1962 году Спиндлеем,Херстом и Химсуорсом

[А.П.Дамбраускас "Cимплексный поиск",М;Энергия,1979 ]

Оригинальность метода состоит в том,что движение к оптимуму в мно-

гомерном пространстве независимых переменных осуществляется после-

довательным отражением вершин симплекса.

В к-мерном эвклидовом пространстве симплексом называют фигуру,обра-

зованную к+1 точками(вершинами),не принадлежащими одновременно ни

одному пространству меньшей размерности.

В 1-мерном пространстве симплекс есть отрезок прямой,в 2-мерном-

треугольник,в 3-мерном-тетраэдр и т д

Симплекс называется регулярным,если расстояния между его вершинами

равны. В ПСМ используются регулярные симплексы.

Из любого симплекса,отбросив одну его вершину,можно получить новый

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

Это замечательное свойство и было испльзовано авторами метода при

построении алгоритма движения симплекса в сторону искомой цели.

Для оценки направления движения во всех вершинах симплекса необхо-

димо определить значения целевой функции Yj.

При поиске максимума наиболее целесообразным будет движение от вер-

шины Vs с наименьшим значением Ys к противоположной грани симплекса

Шаг поиска выполняется переходом из некоторого симплекса Sn-1 в но-

вый симплекс Sn путем исключения вершины Vs и построения ее зерка-

льного отображения Vнs относительно грани,общей обоим симплексам.

Многократное отражение худших вершин приводит к шаговому движению

центра симплекса к цели по траектории некоторой ломаной линии.

Если не учитывать эксперименты в вершинах исходного симплекса,то

на каждый шаг поиска требуется всего одно определение целевой

функции.

АЛГОРИТМ ПОСЛЕДОВАТЕЛЬНОГО СИМПЛЕКСНОГО МЕТОДА:

Исходные данные:

-количество независимых переменных k;

-предельные значения каждой i-й независимой переменной

                  Ximin,Ximax

Эти предельные значения для реальных объектов исследования

определяются априорными сведениями ,условиями безопасности при

проведении экспериментов и т д

-допустимая ошибка в определении координат оптимума Е;

-предполагается также возможность определения значения целевой

 функции Y(X1j,X2j,....,Xij,...,Xkj) для каждой j-й вершины

 симплекса ;

ПОДГОТОВИТЕЛЬНЫЕ ОПЕРАЦИИ

1.Прежде всего необходимо определить стартовую точку для начала

поисковых процедур;при отсутствии  дополнительных априорных данных

естественно расположить ее в центре области,ограниченной

предельными значениями независимых переменных:

     X1c=(X1max-Xmin )/2,X2c=(X2max-X2min)/2,...Xkc(Xkmax-Xkmin)/2.

Перенос в эту точку начала координат облегчит последующие вычисли-

тельные процедуры(достигается вычитанием)

2.Определяются координаты вершин начального симплекса.Из множества

возможных ориентаций начального симплекса на практике используют

2 варианта:

а)когда центр симплекса располагается в начале координатной системы

а одна из вершин(n+1-я) на  оси Xn.Остальные вершины при этом

расположатся симметрично относительно координатных осей

Координаты вершин вычисляются в этом варианте с помощью матрицы:

    Номер               Координаты вершин

   вершины     X1     X2      X3 ......    Xn-1    Xn

     1         -r1    -r2     -r3          -rn-1   -rn

     2         R1     -r2     -r3          -rn-1   -rn

     3         0       R2     -r3          -rn-1   -rn

     .................................................

     n         0       0       0            Rn-1   -rn

    n+1        0       0       0              0     Rn

Где при единичной длине ребра симплекса

     ri=1/sqrt(2*i*(i+1)) ;

     Ri=1/sqrt(2*(i+1));

     i=1,2,...,n;

б)Во втором варианте одна из вершин симплекса размещается в начале

координат,а исходящие из нее ребра образуют одинаковые углы с

соответствующими осями

Вспомогательная расчетная матрица для координат вершин начального

симплекса имеет вид:

    Номер               Координаты вершин

   вершины     X1     X2      X3 ......    Xn-1    Xn

     1          0     0        0            0       0

     2          p     q        q            q       q

     3          q     p        q            q       q

     4          q     q        p            q       q

     ................................................

     n+1        q     q        q            q       p

Где при единичной длине ребра

     p=(n-1+sqrt(n+1))/(n*sqrt(2);

     q=(sqrt(n+1)-1)/(n*sqrt(2));

3.Получаем значения функции отклика в вершинах исходного симплекса

и на этом завершаем подготовительные операции

АЛГОРИТМ ПОИСКА

В цикле с выходом по условию выполняем:

-Отбрасываем вершину с наихудшим значением критерия оптимальности

(наименьшим при поиске максимума или наибольшим при поиске

минимума);

-Вычисляем координаты вершины,зеркально отображаемой отброшенной

относительно противоположной ей грани симплекса:

  Xi*=2*(X1i+X2i+...+Xj-1,i+Xj+1,i+...+Xn+1,i)/n-Xji;

где j-номер отбрасываемой вершины

    i-номер координаты

-Проводим эксперимент в новой точке для получения значения целевой

 функции

Условием выхода из цикла может быть малое прращение целевой функции

на протжении заданного числа опытов или при сохранении одной из

вершин своего присутстствия в симплексе заданное число раз и т п

При приближении к области оптимума точность может быть повышена

уменьшением размера симплекса,но при наличии погрешностей в

определении значений целевой функции необходимо ограничить размер

сиплекса снизу,чтобы избежать блужданий под действием случайных

шумов измерений

ПРЕИМУЩЕСТВА МЕТОДА

-Число необходимых опытов для определения направления движения мало

по сравнению с другими методами

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

при поиске факторов

-Эффекивность метода растет с увеличением мерности пространства

поиска

-Малый объем вычислений на каждом шаге

-Отсутствие высоких требований к точности оценки значения целевой

функции-достаточно возможности проранжировать значения качественно

по принципу больше-меньше

-Метод пригоден для преследования дрейфующей цели(максимума или

минимума),что делает его применимым в адаптивных алгоритмах

-Возможность изменять мерность пространства на ходу изменением

количества вершин симплекса

НЕДОСТАТКИ МЕТОДА

-Отсутствие данных о влиянии каждого фактра на целевую функцию

-Трудность интерпретации характера поверхности отклика по данным

реализуемых в методе опытов

/* Методическая помощь для программной реализации*/

/*    Последовательный Симплексный Метод         */

#include "c:\tutor\m52.h"

#include <stdio.h>

void main(void)

{ m526 ob;

  int r=ob.getrange();       // мерность пространства

  int k=r+1;                 // количество точек симплекса

  double Ymax;               // для работы (Ymax)

  int NYmax;                 // для работы (номер Ymax в

                             // массиве)

  int fl=1,iter=0,t;         // служебные переменные

  double temp;               // служебная переменная

  int i,j;                   // служебные переменные

  double * X = new double [r*k]; // координаты вершин

  double * Xc = new double [r];  // координаты середины ОДЗ

  double * Y  = new double [k];  // значения функции

  double * HX = new double [r];  // для хранения координат

                                 // вершины (одной)

  int    * DisY= new  int [k];   // служебный массив для

                                 // порядка "максимальности"

  if(X==NULL||Xc==NULL||HX==NULL||Y==NULL||DisY==NULL)

   { MessageBox(0,"Для расчетов нет памяти!","ВНИМАНИЕ",

   MB_SYSTEMMODAL);

     return ;

   }

  // координаты середины ОДЗ

  for(i=0;i<r;i++)

    Xc[i]=(ob.getmaxx(i)+ob.getminx(i))/2;

  /* строим матрицу  (1) вида

               Координаты вершин

      X1     X2      X3 ......    Xn-1    Xn

      0     0        0  .......   0       0

      p     q        q  .......   q       q

      q     p        q  .......   q       q

      q     q        p  .......   q       q

      ......................................

      q     q        q  .......   q       p

Где при единичной длине ребра

     p=(n-1+sqrt(n+1))/(n*sqrt(2);

     q=(sqrt(n+1)-1)/(n*sqrt(2));

  и масштабируем p и q в 0.01 от минимального

  (из ОДЗ) отрезка для каждой из переменых  */

  for(i=0;i<r*k;i++)

    X[i]=0;                // обнуляем все элементы массива

  double p = 1/(r*pow(2,0.5))*(pow(r+1,0.5)+r-1);

  double q = p-pow(2,0.5)/2;

        // отыскиваем

  double minL=ob.getmaxx(0)-ob.getminx(0); // интервал

  double dt;                               // определения

  for(i=1;i<r;i++)                         // наименьшей

   if(minL>(ob.getmaxx(i)-ob.getminx(i)))  // длины (в minL)

    minL=ob.getmaxx(i)-ob.getminx(i);

  temp=p*p;

  for(i=1;i<r;i++)     // в temp получаем квадрат

   temp+=q*q;          // ребра симплекса

  temp=0.01*minL/temp; // и получаем в temp кооффициент

  p*=temp;             // умножая на который p и q

  q*=temp;             // мы получем симплекс с ребром

         // в 0.01  minL

  for(i=1;i<k;i++)

   for(j=0;j<r;j++)

    X[i*r+j]=((i-1)==j)?p:q;

                       // построили в памяти матрицу (1)

  for(i=0;i<r*k;i++)

   X[i]+=Xc[i%r];      // переносим оси координат

                       // в центр ОДЗ

  for(i=0;i<k;i++)

   Y[i]=ob.getvalue(X+r*i); // начальные заначения функции

  // цикл перемещений симплекса

  // обрывается когда симплеск начал вращаться

  // т е попробовали отразить каждую из вершин

  // но не одно "отражение" нас не устроило

  for(int step=0;step!=k;)

  { iter+=1;                // счетчик количества шагов

       // симплекса

       // (в программе не используется)

    // ---------------------------------------

    //  здесь заполняем массив DisY

    //  порядковым номером соотвествующего значения

    //  функции в порядке убывания

    //  ( например для Y[0]=-9,   Y[1]=-5,  Y[2]=19,

    //              DisY[0]=3, DisY[1]=2,DisY[3]=1)

    for(i=0;i<k;i++)

     { int kol=0;

       for(j=0;j<k;j++)

 if(Y[j]>=Y[i]) kol++;      // счетчик "есть больше"

       DisY[i]=kol;

     }

    // если есть одинаковые значения функции,то

    // просто распределяем "места" по порядку расположения

    // в массиве

    for(i=0;i<k;i++)

     { int kol=DisY[i];

       t=0;

       for(j=0;j<k;j++)

 if(kol==DisY[j])

  { DisY[j]=kol-t;

           t--;

  }

     }

    // итак,расположили в порядке убывания значения функции

    for(i=0;DisY[i]!=(step+1);i++);

        // нашли очередное

        // "максимальное" значение

    if((iter>1)&&((NYmax==DisY[i])&&(Ymax<Y[DisY[i]])))

      // не первый шаг

      // и шаг был в сторону минимума

 { step++;

   Y[i]=Ymax;

   for(i=0;i<r;i++)

    X[r*NYmax+i]=HX[i];

           fl++;       // флаг-"не производить отражение"

 }

       else

 { Ymax=Y[i];          // новые max значения

   NYmax=i;

   fl=0;

 }

  //------------------------------------------------

   if(!fl)                    // отражение разрешено

   { for(i=0;i<r;i++)

      HX[i]=X[r*NYmax+i];

 // сохранили старое значение вершины V[NYmax]

     for(i=0;i<r;i++)

      { temp=X[r*NYmax+i];

 X[r*NYmax+i]=0;

 for(j=0;j<k;j++)

  if(j!=NYmax)

   X[r*NYmax+i]+=X[r*j+i];

 X[r*NYmax+i]=X[r*NYmax+i]*2/r-temp;

      }

// просчитали новые координаты отражаемой вершины V[NYmax]

// проверяем на выход за ОДЗ

   t=step;

   for(i=0;i<r;i++)

    if ((X[r*NYmax+i]>ob.getmaxx(i) )||

     (X[r*NYmax+i]<ob.getminx(i) ))

     { step++;

       for(i=0;i<r;i++)

 X[r*NYmax+i]=HX[i];

     }

   if(t==step)        // если нет выхода за диапозон ОДЗ

    { temp=Y[NYmax];

      Y[NYmax]=ob.getvalue(X+NYmax*r);

   if((iter>1)&&(Y[NYmax]>temp)) // не больше

 step++;

       else

 step=0;

    }

   }

  }

 // закрыт цикл перемещений симплекса

 printf("\n\rКоличество иттераций %d\n\rЗНАЧЕНИЕ Функции %f\

\n\rОриентировочный максимум находится в точке с \

координатами",iter,Y[NYmax]);

 for(i=0;i<r;i++)

  { for(j=0,temp=0;j<k;j++)

      temp+=X[j*r+i];

    printf("\n\r %f",temp/k);     // среднее арифметическое

      // (внутри симплекса)

  }

   delete[] Y;

   delete[] DisY;

   delete[] X;

   delete[] HX;

   delete[] Xc;

}


Метод прямоугольников
Методы прямоугольников

ТЕМА:ВЫЧИСЛЕНИЕ ОПРЕДЕЛЕННЫХ ИНТЕГРАЛОВ

1.Классификация методов

Ставится задача вычислить интеграл вида

      b

   J= S f(x)dx

      a

где a и b-нижний и верхний пределы интегрирования; f(x)-непрерывная

на отрзезке [a,b] функция.

К численным методам обращаются при невозможности записать

первообразную функцию аналитически через элементарные функции или

когда такая запись имеет слишком сложный вид

Сущность большинства методов вычисления определенных интегралов

состоит в замене подынтегральной функции f(x) аппроксимирующей

функцией v(x),для которой первообразная в элементарных функциях

определяется легко

Используемые на практике методы численного интегрирования можно

классифицировать по способу аппроксимации подынтегральной функции:

МЕТОДЫ НЬЮТОНА-КОТЕСА основаны на полиномиальной аппроксимации и

отличаются друг от друга степенью используемого полинома.Эти

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

СПЛАЙНОВЫЕ МЕТОДЫ базируются на сплайновой аппроксимации

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

прменяются в задачах обработки данных с использованием сплайнов

МЕТОДЫ НАИВЫСШЕЙ АЛГЕБРАИЧЕСКОЙ ТОЧНОСТИ(Гаусса-Кристофеля и др)

используют неравноотстоящие узлы расположенные по алгоритму

обеспечивающему минимальную погрешность интегрирования для наиболее

сложных функций  при заданном количестве узлов

Методы различаются  способами выбора узлов и широко используются

для интегрирования

МЕТОД МОНТЕ-КАРЛО использует случайное расположение узлов и дает

результат вероятностного смысла

Независимо от метода в процессе численного интегрирования небходимо

вычислить приближенное значение интеграла и оценить

погрешность,которая уменьшается с ростом числа участков разбиения

интервала интегрирования за счет более точной аппроксимации и

одновременно растет за счет погрешности суммирования частичных

интегралов.Эта составляющая с некоторого значения N0 начинает

преобладать -это препятствует чрезмерному дроблению интервала

интгрирования

МЕТОДЫ ПРЯМОУГОЛЬНИКОВ

Это простейшие из класса Ньютона -Котеса-основаны на аппроксимации

подынтегральной функции полиномом нулевой степени-константой на

заданном отрезке интервала

Наименьшую погрешность в этом подклассе имеет метод средних

прямоугольников-когда  константой выбирается значение f(x) в

средней точке участка разбиения

Сранительно бошльшей погрешностью обладают методы ЛЕВЫХ и ПРАВЫХ

прямоугольников

Выражение для интеграла на интервале [xj,xj+h] при средних

прямоугольниках

       xi+h

       S f(x)dx=h*f(x#)+R

       xi

где x#=xj+h/2, R=Jточн-Jприбл

При определении подинтегральной функции из эксперимента в

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

нереализуем из-за отсутствия значений в средних точках xi

  Задание : составить программу интегрирования объектов класса m531

по методу средних прямоугольников

/*

//:словесный алгоритм

  Задаемся шагом дробления интервала интегрирования (нижний и

верхний пределы получаем методами доступа класса)

  В цикле с вычисленным шагом вычисляем сумму площадей элементарных

прямоугольников с шириной , равной шагу и высотой - значению

функции при половинном шаге

*/


Метод трапеций

МЕТОД ТРАПЕЦИЙ

получается при замене подинтегральной функции полиномом 1-й степени

P1(x).На каждом элементарном отрезке аргумента х участок кривой

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

отрезок оси абсцисс вместе сэтой прямой ограничивают фигуру

трапецеидальной фрормы,что и дает название этому методу

кусочно-линейной аппроксимации подинтегральной функции

      xi+h

       S f(x)dx=h*[f(xi)+f(xi+h)]/2 +R

       xi

  Задание : составить программу интегрирования объектов класса m532

по методу трапеций

/*

//:словесный алгоритм

  Задаемся шагом дробления интервала интегрирования (нижний и

верхний пределы получаем методами доступа класса)

  В цикле с вычисленным шагом вычисляем сумму площадей элементарных

трапеций с высотой, равной шагу и основаниями - значению функции на

левой и правой границе аргумента

*/


Метод Симпсона

получается при замене подинтегральной функции полиномом 2-й

степени,при записи в Ньютоновской форме для 3-х узлов

    P2(x)=f0+f01(x-x0)+f012(x-x0)*(x-x1),где разделенные разности

    f01=(f0-f1)/(x0-x1)=(f1-f0)/h

    f012=(f01-f02)/x1-x2)=(f0-2*f1+f2)/2*h^2

при шаге h  между узлами

Заменяя Z=x-x0  или x=z-x0 получим полином в виде

       P2(z)=f0+(f01-f012*h)*z+f012*z^2

Интеграл от полинома

   x2        2*h

   S P2(x)dx=S P2(z)dz=(f0+4*f1+f2)*h/3

   называют квадратурной формулой Симпсона

  Задание : составить программу интегрирования объектов класса m532

по методу СИМПСОНА


Интерполяция каноническим полиномом

         ТЕМА:АППРОКСИМАЦИЯ  (ПРИБЛИЖЕНИЕ)  ФУНКЦИЙ

Одной  из  основных задач математического моделирования является

аппроксимация зависимостей,заданных  дискретными  последователь-

ностями значений независимых и зависимых переменных(таблицами).

Табличная форма представления функциональной взаимосвязи возникает

прежде всего в результате эспериментальных исследований при

табличной записи измеренных  значений интересующих исследователя

параметров.В этом случае вид функции,описывающей зависимость между

переменными,априори неизвестен и может приниматься любым в качес-

тве подлежащей проверке гипотезы.

Принципиально возможен и другой вариант-когда аналитическую функцию

Y=F(x) полезно для упрощения и удобства (например вычислений

производных или интегралов) заменить функцией более простой струк-

туры-чаще всего полиномиальной.В этом случае "эксперименты" про-

водятся  над  аналитической функцией путем вычисления последова-

тельности  ее значений при заданной последовательности значений

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

функцией.

В постановке задачи аналитического представления дискретной пос-

ледовательности значений независимых и зависимых переменных необ-

ходимо различать 2 варианта:

-значения функции получены с пренебрежимо малой погрешностью при

 отсутствии погрешностей в определении аргументов и значения ар-

 гументов в таблице не повторяются;

-погрешностями оценок значений функции пренебречь нельзя и  воз-

 можны  различные  значения функции при одинаковых значениях аргу-

 ментов;

В первом случае возможна постановка задачи приближения функции

в смысле,предложенном Лагранжем:построить функцию F(x),принадле-

жащую известному классу и принимающую в точках x0,x1,....xn

заданные значения y0,y1,...yn.Геометрически это означает что че-

рез систему точек Mi(Xi,Yi) i=0,1,...n необходимо провести кри-

вую Y=(x).Эта задача имеет единственное решение, если  через  эту

систему точек провести параболу n-го порядка, то есть в качестве

функции F(x) выбрать полином Pn(x) такой что

   Pn(x0)=y0;  Pn(x1)=y1; .......Pn(xn)=yn

В такой постановке это-задача интерполяции , последовательность

значений x0,x1,....xn называется узлами интерполяции,а приближа-

ющий полином-интерполяционным полиномом.Для заданной таблицы уз-

лов и значений функции интерполяционный полином -единственный

независимо  от формы его записи.Это утверждение доказывается ме-

тодом от противного.

Предположим что для одной и той же таблицы

               +-------------+

               | x   |  F(x) |

               +-----+-------|

               |  x0 |  f0   |

               |  x1 |  f1   |

               |  .  |  .    |

               |  xn |  fn   |

               +-------------+

построены 2 полинома n-й степени Pn(x) и Qn(x) с разными коэффи-

циентами;запишем алгебраическое уравнение

             Pn(x)-Qn(x)=0,  *

левая часть которого будет также полиномом степени n.По основной

теореме алгебры это уравнение имеет n корней. С другой стороны,

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

чениями функции f0,f1,...fn -значит, узлы являются корнями уравне-

ния *,т е количество корней рано n+1,что невозможно и приводит

к тождеству  Pn(x)==Qn(x) ,доказывая единственность интерполяци-

онного полинома.

      РАЗДЕЛ:ИНТЕРПОЛЯЦИЯ КАНОНИЧЕСКИМ ПОЛИНОМОМ

Выберем в качестве аппроксимирующей функции f(x) полином Pn(x)

степени n в каноническом виде

          f(x)=Pn(x)=c0+c1*x+c2*x^2+...+cn*x^n   **

Cвободные параметры интерполяции или коэффициенты полинома опре-

делим из условий Лагранжа

       Pn(xj)=fj,   0<=j<=n

       c0+c1*x0+c2*x0^2+...+cn*x0^n =f0

       c0+c1*x1+c2*x1^2+...+cn*x1^n =f1       ***

       ................................

       c0+c1*xn+c2*xn^2+...+cn*xn^n =fn

Система линейных алгебраических уравнений *** относительно коэф-

фициентов имеет решение, так как определитель системы отличен от 0,

если среди узлов xj нет совпадающих.Для вычисления коэффициентов

систему *** можно решить любым известным методом ,например Гаусса.

/*

//Интерполяция каноническим полиномом

m541 t;//Объект исследования

double **m;//Матрица для решения СЛАУ

int n;//Количество узлов , оно же - порядок полинома etс

double *x,//Массив узлов

*c;//Вначале - значения в узлах, затем- коэффициенты полинома

//   {Формирование матрицы СЛАУ}

void Matr()

{

  double r,//узловая точка в i - ой степени

  s;//очередная узловая точка

  for(int i=0;i<n;i++)

  {

    r=1;    s=x[i];    m[i][n]=c[i];//В последний столбец заносим

                               //значение полинома в узлвой точке

    for(int j=0;j<n;j++)

    { m[i][j]=r; r*=s;/*Возводим узел в степень его номера*/ }

  }

}

//   {Решение системы методом Гаусса}

void Gauss()

{

  int j,l,k1,n1;

  double r,s;

  n1=n+1;

  //Выбор главного элемента, совмещенный с прямым ходом Гаусса

  for(int k=0;k<n;k++)

  {

    k1=k+1;

    s=m[k][k];

    j=k;

    for(int i=k1;i<n;i++)

    {

      r=m[i][k];

      if(fabs(r)>fabs(s))

      {

 s=r;

 j=i;

      }

    }

    if(j!=k)

      for(int i=k;i<n1;i++)

      {

        r=m[k][i];m[k][i]=m[j][i];m[j][i]=r;

      }

    for(j=k1;j<n1;j++)//собственно прямой ход

      m[k][j]/=s;

    for(i=k1;i<n;i++)

    {

      r=m[i][k];

      for(j=k1;j<n1;j++)

        m[i][j]-=m[k][j]*r;

    }

  }

  for(int i=n-1;i>=0;i--)//Обратный ход Гаусса

  {

    s=m[i][n];

    for(j=i+1;j<n;j++)

      s-=m[i][j]*c[j];

    c[i]=s;

  }

}

//   {Вычисление значений полинома в точке х=х1}

double Pol(double x1)

{

  //В последнем столбце - свободный член полинома

  double p=c[n-1];

  for(int i=n-2;i>=0;i--)

    p=c[i]+p*x1;

  return p;

}

void main(void)

{

  n=t.getrange();//Определяем количество узлов

  m=new double*[n];//Выделяем память под массив указателей для стро

                     //матрицы

  x=new double[n];//Массив узлов

  c=new double[n];//Массив универсальный- значения в узлах,

             //коэффициенты полинома

  for(int i=0;i<n;i++)//Выделяем память под массивы строк

    m[i]=new double[n+1];

  for(i=0;i<n;i++){    x[i]=i;c[i]=t.getvalue(i);}

  //{ Тест при x=1,5 должен давать f=0.705875}

  Matr();//Формируем матрицу

  Gauss();//Решаем систему

  for(i=0;i<n;i++)//Выводим коэффициенты полинома

    cout<<c[i]<<"\t";

  for(double x1=t.getmin();x1<=t.getmax();x1+=t.getshag())

    cout<<x1<<"  "<<Pol(x1)<<endl;//Выводим таблицу

  for(i=0;i<n;i++)

    delete[] m[i];//Удаляем массивы

  delete[] m;//удаляем указатели

  delete[] x;//Удаляем массив

  delete[] c;//Удаляем массив

}

*/


Интерполяционный полином Лагранжа

          РАЗДЕЛ:ИНТЕРПОЛЯЦИОННЫЙ ПОЛИНОМ ЛАГРАНЖА

Единственность позволяет вводить интерполяционные полиномы  в са-

мых различных формах.Лагранж предложил следующую форму:

                     n      n

               Pn(x)=S  f(i)П (x-x(j))/(x(i)-x(j))  *

                     i=0    j=0

 Старшая степень аргумента в полиноме Лагранжа равна n ,так как

каждое произведение в формуле  * содержит n сомножителей x-x(j)

В узлах x=x(j)выполняется условие Лагранжа ,потому что в сумме *

остается  по одному слагаемому f(j),остальные обращаются в нуль за

счет нулевых сомножителей в произведениях.

В отличие от канонического интерполяционного полинома для

вычисления значений полинома Лагранжа не требуется предварительного

опредеделения коэффициентов полинома путем решения системы

уравнений.

Однако для каждого значения аргумента х полином приходится  перес-

читывать вновь,коэффициенты же канонического полинома вычисляются

только один раз.

Поэтому практическое применение полинома Лагранжа оправдано только

при интерполяции на небольшом числе точек.

СОДЕРЖАНИЕ РАБОТЫ:

-изучить сущность метода и алгоритм вычислений.

-Составить программу вычисления  значений ф-ии по методу Лагранжа

/*

m542 t;

int n;//Количество узлов

//    {ИНТЕРПОЛЯЦИОННЫЙ ПОЛИНОМ ЛАГРАНЖА}

double pl(double x)//вычисления для заданного х

{

  for(double i=0,p=0;i<n;i++)

  {

    double r=1;

    for(double j=0;j<n;j++)

      if(i!=j)

 r*=(x-j)/(i-j);

    p+=r*t.getvalue(i);

  }

  return p;

}

  В цикле от минимума до максимума вычисляем и распечатываем

таблицу значений полинома (в узлах и между ними)

*/


Интерполяционный полином Ньютона

           РАЗДЕЛ:ИНТЕРПОЛЯЦИОННЫЙ ПОЛИНОМ НЬЮТОНА

Полином Ньютона для интерполяции имеет вид:

  Pn(x)=A0+A1*(x-x(0))+A2*(x-x(0))*(x-x(1))+...   (1)

   ...+An*(x-x(0))*(x-x(1))...(x-x(n-1)

Равносильный вариант полинома можно получить при симметричной

перенумерации узлов и значений функции в исходной таблице от

n до 0 :

  Pn(x)= B(n)+B(n-1)*(x-x(n))+B(n-2)*(x-x(n))*x-x(n-1)()+...

         ...+B(0)*(x-x(n))*(x-x(n-1))...(x-x(1))               (2)

Коэффициенты полиномов определяются из условий Лагранжа

     Pn(x(j))=f(j), 0<=j <=n                                   (3)

Полагаем  x=x(0),тогда в формуле (1) все слагаемые кроме A0 об-

ращаются в нуль и

                   A(0)=f(0)

Затем полагаем x=x(1) , тогда по условию (3)

          f(0)+A1*(x(1)-x(0))=f(1)

откуда находим коэффициент

            A1=(f(0)-f(1))/(x(0)-x(1))=f01

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

этой  разности  близка к первой производной функции f(x) при малом

расстоянии между узлами x(0) и x(1).

 При x=x(2) полином принимает значение

   Pn(x(2))=f(0)+A1*(x(2)-x(0))+A2*(x(2)-x(0))*(x(2)-x(1)),

из условия Лагранжа определим искомый коэффициент

            A2= (f01-f02)/(x(1)-x(2)) =f012,

где     f02=(f(0)-f(2))/(x(0)-x(2))

Величина f012 называется разделенной разностью второго порядка,

которая при близком расположении x(0),x(1),x(2),будет

пропорциональна второй производной функции f(x).

Аналогично при x=x(3) находим коэффициент А3:

           A3=(f012-f013)/(x(2)-x(3)=f0123,

где f013=(f01-f03)/(x(1)-x(3)),f03=(f(0)-f(3))/(x(0)-x(3)).

Для коэффициента Ak методом математической индукции запишем сле-

дующее выражение:

        Ak = (f01...k-1-f01...k)/(x(k-1)-x(k)).

Полученные результаты сведем в таблицу

-------------------------------------------------------------------

 x |f(x)|        1              |            2               |  3

----+---+-----------------------+----------------------------+-----

x(0)| A0|                       |                            |

x(1)| f1|  A1=(f0-f1)/(x(0)-x(1)|                            |

x(2)| f2| f02=(f0-f2)/(x(0)-x(2)|   A2=(f01-f02)/(x(1)-x(2)) |

x(3)| f3| f03=(f0-f3)/x(0)-x(3))| f013=(f01-f03)/(x(1)-x(3)) |

    |   |                       |     |А3=(f012-f013)/(x(2)-x(3))

...................................................................

Для построения интерполяционного полинома Ньютона используюется

только диагональные элементы приведенной таблицы ,остальные

являются промежуточными данными.Поэтому в программе,реализующей вы-

числение коэффициента полинома ,разделенные разности для экономии

памяти размещены в массиве,где первоначально хранились значения

функции в узлах.Этот массив будет частично обновляться при вычис-

лении разделенных разностей очередного порядка.

Добавление новых узлов в таблицу не изменит уже вычисленных коэф-

фициентов,таблица будет дополнена новыми строками и столбцами раз-

деленных разностей.

Погрешность полиномиальной аппроксимации функции f(x) определяется

соотношением:                       n

      |f(x)-Pn(x)|< (M n+1 /(n+1)!)*П |x-x(j)|       *

                                    i=0

      где           (n+1)

      M n+1 = max |f(x)   |.

Правая часть неравенства * приближенно совпадает по модулю с новым

членом полинома Ньютона, появляющимся при добавлении n+1-го узла

Таким образом вычисление модуля каждого из членов суммы (1) позво-

ляет установить, сколько узлов следует использовать для

аппроксимации исходной функции с заданной точностью

Если узлы расположены равномерно с шагом  h, то наименьшая

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

счет минимальной величины произведения в правой части оценки (*)

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

по:

     |f(x)-Pn(x)|<sqr(2/(pi*n*M n+1 *(h/2)^(n+1))

После определения коэффициентов полинома Ньютона вычисление его

значений при конкретных аргументах x наиболее экономично проводить

по схеме Горнера ,получаемой последовательным вынесением за скобки

множителей (x-x(j)) в формуле (1):

   Pn(x)=f0+(x-x0)*(f01+(x-x1)*(f012+(x-x2)*(f0123+...)...)

В отличие от алгоритма вычисления полинома Лагранжа при

интерполяции полиномом Ньютона удается разделить задачи определения

коэффициентов и вычисления значений полинома при различных

значениях x.

Вычисление коэффициентов полинома Ньютона осуществляется в функии

main() с помощью 2-х вложенных циклов по строкам (переменная I) и

столбцам (переменная J) таблицы. Во внешнем цикле по столбцам

инициализируются значения уменьшаемых в числителе и знаменателе

разделенных разностей , так как эти величины не изменяются для всех

разностей одного порядка. Нижняя граница внутреннего цикла

изменяется в зависимости от порядка pазделенных разностей,что

позволяет уменьшать их количество при увеличении порядка. После

выполнения этой программы кoэффициенты Ньютона находятся в массиве

внутри объекта исследования ,так как метод getvalue() дает ссылку.

Значения полинома Ньютона при циклическом изменении значений

аргумента определяются с помощью отдельной подпрограммы PN

СОДЕРЖАНИЕ РАБОТЫ:

-изучить сущность метода и алгоритм вычислений

-составить программу реализации алгоритма Ньютона

/*

//Интерполяционный полином Ньютона

m543 t;

int n;

//Вычисление значения полинома в точке х1

double pn(double x1)

{

  double p=0;

  for(int i=n-1;i>=0;i--)

  {

    double r=x1-i;

    p=t.getvalue(i)+r*p;

  }

  return p;

}

void main(void)

{

  n=t.getrange();

  //Формирование коэффициентов полинома

  for(int j=1;j<n;j++)

    for(int i=j;i<n;i++)

      t.getvalue(i)=(t.getvalue(j-1)-t.getvalue(i))/(j-i-1);

  //Цикл вывода с вычислением

  for(double x1=t.getmin();x1<t.getmax();x1+=t.getshag())

    cout<<x1<<"\t"<<pn(x1)<<endl;

}

*/


Кубические сплайны
Интерполяция кубическими сплайнами

  Несмотря на выполнение условий Лагранжа в узлах интерполяции ,

интерполяционная функция может иметь значительное отклонение от

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

эффект волнистости.

  Для проведения гладких кривых через узловые точки помимо условий

Лагранжа накладывают дополнительные требования к интерполяционной

кривой

  Метод интерполяции ,получивший свое название от упругой

металлической линейки,накладываемой на узловые точки

аппроксимируемой кривой,называется методом сплайновой интерполяции

По законам упругости металлическая линейка опирающаяся на ряд точек

проходит меду ними по линии с нулевой четвертой производной

            (4)

           f   =0

Если в качестве функции f выбрать полином,то его степень должна

быть не выше третьей-этот полином и носит название кубического

сплайна,имеющего на интервале [Xj-1,Xj] вид:

                                 2            3

   Fj(X)=Aj+Bj(X-Xj-1)+Cj(X-Xj-1)  +Dj(X-Xj-1)               (*)

где Aj,Bj,Сj,Dj-коэффициенты сплайна,определяемые из дополнительных

условий

    j=1,2,...n-номера сплайнов

При сплайн-интерполяции на каждом интервале [ Xj-1, Xj] строится

отдельный полином 3-й степени со своими коэффициентами,которые

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

точках:

1)выполнение условия Лагранжа

2)непрерывность первой и второй производной в узлах

       ,      ,

      Fj(Xj)=Fj+1(Xj)

       ,,     ,,

      Fj(Xj)=Fj+1(Xj)

3)Условия на концах могут быть различными-в том числе со свободными

концами,т е описываться уравнениями прямых и в этом случае иметь

нулевые вторые производные

       ,,

      F1(X0)=0

       ,,

      Fn(Xn)=0

Алгоритм определения коэффициентов кубических сплайнов

Aj=Fj-1

             2     3

Aj+Bj*Hj+Cj*Hj+Dj*Hj=Fj

где Hj=Xj-Xj-1         1<=j<=n

  Продифференцировав дважды сплайн (*) по х, получим:

    ,                              2

   Fj(X)=Bj+2Cj(X-Xj-1)+3Dj(X-Xj-1)

     ,,

   Fj(X)=2Cj+6Dj(X-Xj-1)

  Из условий непрерывности производных при переходе в точках Xj от

j-го к (j+1) сплайну с учетом предыдущих выражений получим :

                2

  Bj+2CjHj+3DjHj =Bj+1

  Cj+3DjHj=Cj+1

  Из граничных условий на основании последнего выражения для второй

производной получим

  С1=0

  Cn+3DnHn=0

  Вышеприведенные соотношения предсталяют собой ПСЛАУ относительно

коэффициентов сплайнов Aj,Bj,Cj,Dj.

  Преобразуем ее так, чтобы неизвестными была только одна группа

коэффициентов Cj :

  Dj=(Cj+1-Cj)/3Hj

  Bj=(Fj-Fj-1)/Hj-(Cj+1+2Cj)Hj/3

  Получим уравнение относительно Cj :

  Hj-1Cj-1+2(Hj-1+Hj)Cj+HjCj+1=3[(Fj-Fj-1)/Hj-(Fj-1-Fj-2)/Hj-1]

where 2<=j<=n

  При j=n для свободных концов Cn+1=0

  n-1 уравнение для Cj вместе с С1=0 и Cn+1=0 образуют СЛАУ для

вычисления Cj,a Bj & Dj мы уже выразили через Сj. Коэффициенты Aj

равны значениям в узлах Aj=Fj-1.

  В каждое из уравнений системы входят только 3 неизвестных - Cj-1,

Cj & Cj+1, поэтому матрица СЛАУ является 3-хдиагональной. Метод

прогонки (частный случай метода Гаусса для таких матриц) изучите по

А.Е.Мудров - Численные меиоды для ПЭВМ на языках Бейсик,Фортран и

Паскаль (стр.99)

//Интерполяция кубическими сплайнами

m544 t;

int n;

double *c;//Для коэффициентов сплайнов

double sp(double x1)//Вычисление значения

{

  //Определение нужного сплайна

  for(int i=1;(x1>i)&&(i!=n);i++);

  int j=i-1;

  double a=t.getvalue(j);

  double b=j;

  double q=i-b;

  double r=x1-b;

  double p=c[i];

  double d=c[i+1];

  b=(t.getvalue(i)-a)/q-(d+2*p)*q/3;

  d=(d-p)/q*r;

  p=a+r*(b+r*(p+d/3));//Вычисления по схеме Горнера

  return p;

}

void main(void)

{

  n=t.getrange();

  double *k=new double[n];//Прогоночные коэффициенты

  c=new double[n];

    //Метод прогонки

  k[0]=c[0]=c[1]=k[1]=0;

  for(int i=2;i<n;i++)

  {

    int j=i-1;

    int m=j-1;

    double a=i-j;

    double b=j-m;

    double r=2*(a+b)-b*c[j];

    c[i]=a/r;

    k[i]=(3.0*((t.getvalue(i)- t.getvalue(j)) /a - (t.getvalue(j) -

          t.getvalue(m)) /b)-b*k[j])/r;

  }

  c[n-1]=k[n-1];

  for(i=n-2;i>=0;i--)

    c[i]=k[i]-c[i]*c[i+1];

  delete[] k;

  //Печать таблицы

  for(double x1=t.getmin();x1<t.getmax();x1+=t.getshag())

    cout<<x1<<"\t"<<sp(x1)<<endl;

  delete[] c;

}

*/


Метод наименьших квадратов
Приближение функций по методу наименьших квадратов

МЕТОД НАИМЕНЬШИХ КВАДРАТОВ

используется в наиболее распространенном на практике случае -

измерений функции в узлах с погрешностями, которыми нельзя

пренебречь. При этом используется Гауссова постановка задачи

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

квадратов ее расстояний от значений в узлах была минимальной.

Порядок аппрокимирующего полинома в этом случае, как правило,

значительно ниже числа узловых точек.

Аппроксимирующая кривая проводится в этом случае не через

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

зависимость и сглаживает отдельные выбросы

Обозначим  узлы  исходной таблицы данных через Xj j = 0,1,..n

Значения аппроксимируемой функции в узловых точках F(Xj)=fj считаем

известными

Введем непрерывную функцию V(x) для аппроксимации дискретной

зависимости F(X)

В узлах обе функции будут отличаться на некоторую величину

Е=V(x)-F(x)

Параметры(коэффициенты ) аппроксимирующей  функции  подберем  так

чтобы сумма квадратов отклонений в узловых точках была минимальной

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

              n

          Q=  S [V(xj)-F(xj)]^2  -- > min

             j=0

Наиболее распрстранен выбор функции V(x) в виде линейной

комбинации базисных функций

   V(x)= с0*v0(x) + c1*v1(x) +.....+cm*vm(x)

где

c0,c1,...cm -коэффициенты определяемые при минимизации величины Q

Приравнивая 0 частные производные Q по коэффициентам ci получаем

систему линейных так называемых нормальных уравнений

         дQ     n

        ---- =2*S[c0*v0(xi) +c1*v1(xi)+...+cm*vm(xi)-fi]*v0(xi) =0

         дс0   i=0

         дQ     n

        ---- =2*S[c0*v0(xi) +c1*v1(xi)+...+cm*vm(xi)-fi]*v1(xi) =0

         дс1   i=0

         .........................................................

         дQ     n

        ---- =2*S[c0*v0(xi) +c1*v1(xi)+...+cm*vm(xi)-fi]*vm(xi) =0

         дсm   i=0

 решение которой определяет все искомые коэффициенты сk

Матрица этой системы имеет вид

        | (v0,v0)   (v0,v1)    ....  (v0,vm) |

        | (v0,v1)   (v1,v1)    ....  (v1,vm) |

        | .................................. |

        | (v0,vm)   (v1,v1m    ....  (vm,vm) |

и называется матрицей Грама

Элементы ее-скалярные произведения базисных функций

                     n

           (vj,vk) = S  vj(xj)*vk(xj)

                     j=0

Расширенная матрица получается добавлением к матрице Грама столбца

свободных членов

                    |(v0,f)|

                    |(v1,f)|

                    | .... |

                    |(vm,f)|

Основные свойства матрицы Грама:симметричность,положительная

определенность и неравенство 0  определителя  при выборе линейно

независимых функций vk(x)

При обработке экспериментальных данных дающих погрешность Е в

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

базисными функциями

После определения коэффициентов ck вычисляют величину Q  и

сравнивают ее с E^2

 Если Q>E^2 то базис последовательно расширяют добавлением новых

функций пока не выполнится условие Q<+E^2

СТЕПЕННОЙ БАЗИС

Чаще всего для аппроксимации используют полином со степенью меньшей

числа узлов   Расширенная матрица системы нормальных уравнений для

базиса

             v0(x)=1; v1(x)=x^1;  .....vm(x)=x^m имеет вид

|          n          n                 n           n        |

|  n+1     S xi       S xi^2    ...     S xi^m      S fi     |

|         i=0        i=0               i=0         i=0       |

|                                                            |

|  n        n         n                 n           n        |

|  S xi     S xi^2    S xi^3  ....      S xi^m+1    S xi*fi  |

|  i=0     i=0       i=0               i=0         i=0       |

|  ........................................................  |

|  n         n          n                n          n        |

|  S xi^m    S xi^m+1   S xi^m+2.....    S xi^2*m   S xi^m*fi|

| i=0       i=0        i=0              i=0         i=0      |

Для решения систем уравнений с матрицей Грама разработаны

специальные методы так называемого сингулярного разложения

Если же m<=4-5 то вполне пригоден и простейший метод исключения

Гаусса или метод ортогонализации

СОДЕРЖАНИЕ РАБОТЫ

-освоить метод, алгоритм реализации и программу,не оставляя неясных

мест

-составить Си++ программу для реализации МНК,используя для

имитации моделируемого объекта класс m545

/*

//Приближение функций по методу наименьших квадратов

m545 t;

double **gram;

int n,r;

void gramplus()

{//Здесь текст ВАШЕЙ функции

}

double scalp(int v1,int v2)

{//Здесь текст ВАШЕЙ функции

}

void norm(int v)

{//Здесь текст ВАШЕЙ функции

}

void orto(int v)

{//Здесь текст ВАШЕЙ функции

}

void main()

{

  n=t.getrange();//Ранг матрицы измерений

  r=n+1;//Ранг матрицы Грама

  gram=new double*[n+1];//Распределяем память

  for(int i=0;i<n+1;i++)

    gram[i]=new double[n+1];

  gramplus();//Формируем матрицу Грама + столбец свободных членов+

             //строка для вектора решения

  for(i=0;i<n;i++)//Вывод сформированной матрицы

  {

    cout<<endl;

    for(int j=0;j<n+1;j++)

      cout<<gram[i][j]<<"\t";

  }

  for(int j=0;j<3;j++)//ортонормирование

  {

    norm(0);

    for(i=1;i<r;i++)

    {

      orto(i);

      norm(i);

    }

  }

  for(i=0;i<r;i++)

    gram[r-1][i]/=gram[r-1][r-1];

  //Расчет коэффициентов моделм - нормирование

  cout<<endl<<"y=";//Вывод формулы

  for(i=0;i<n-1;i++)

  {

    cout.flags(cout.flags()|ios::showpos);

    cout<<gram[r-1][i]<<"*X"<<i+1;

  }

  cout<<gram[r-1][n-1]<<endl;

  //Расчет значений по формуле для сравнения в узлах

  for(j=0;j<t.getexp();j++)

  {

    double y=gram[r-1][3];

    for(i=0;i<n-1;i++)

      y+=x[j][i]*gram[r-1][i];

    cout<<endl<<y;

  }

  for(i=0;i<n+1;i++)

    delete []gram[i];

  delete[] gram;

}

*/


Метод Гаусса
Метод Гаусса с выбором главного элемента для решения СЛАУ"¤яяя

   ТЕМА:Численные методы в задачах линейной алгебры.

   ВВЕДЕНИЕ.В линейной алгебре рассматривются 4 класса основных

задач:

   -решение систем линейных алгебраических уравнений (СЛАУ);

   -вычисление определителей;

   -обращение матриц;

   -определение собственных значений и собственных векторов матриц;

   Все эти задачи имеют важное прикладное знчение при решении

различных проблем науки и техники как самостоятельно,так и и

качестве вспомогательных алгоритмов в других задачах вычислительной

математики,математической физики,обработки результатов

экспериментальных исследований.

   РАЗДЕЛ:РЕШЕНИЕ СЛАУ.

   Для решения СЛАУ применяют два класса методов:прямые и

итерационные.

   Прямые методы являются универсальными,но из-за большого объема

вычислительных процедур применяются для решения систем

сравнительно невысокого порядка(n < 200).Итерационные методы

рекомендуются для СЛАУ высоких порядков.

Общая формулировка задачи:

Необходимо решить СЛАУ

      а11*x1 + a12*x2 +....+ a1n*xn = a1,n+1

      a21*x1 + a22*x2 +....+ a2n*xn = a2,n+1     (1)

      ......................................

      an1*x1 + an2*x2 +....+ ann*xn = an,n+1

  где xk -неизвестные,a(ij)-заданные коэффициенты.

  В матричной записи:

                 [A]x=y                          (2)

  Общее решение находится просто:домножим обе части матричного

уравнения(2) слева на обратную матрицу [Aоб]:

                  [Aоб][A]x=[Aоб]y               (3)

                   x = [Aоб]y                     (4)

Т.о. задача сводится к обращению матрицы [A].

МЕТОД:ГАУССА С ВЫБОРОМ ГЛАВНОГО ЭЛЕМЕНТА

  Метод Гусса относится к прямым методам. Алгоритм состоит из двух

этапов.

  На первом выполняются последовательно следующие процудуры:

  -последовательно для каждой i-й строки из всех последующих стpок

выбираются уравнения с наибольшим  по модулю коэффициентом i-м

столбце:

эта пpоцедуpа называется выбоpом главного элемента;

  -ноpмиpование уpавнений по коэффициентам пpи x(i,i) путем деления

всех коэффициентов уpавнения на а(ii);

  -исключение i-й пеpеменной из всех уpавнений ,начиная с

i+1-го,т.е. пpиведение матpицы коэффцентов к верхней тpеугольной

фоpме;

  На втором этапе выполняется:

  -подстановка x(n) в n-1 -е уравнение,x(n-1) - в n-2-е и

т.д.,выполняя т.н. обратный ход алгоритма Гаусса.

  СОДЕРЖАНИЕ РАБОТЫ

  -Составить программу решения СЛАУ методом Гаусса с

предварительным  выбором главншго элемента

Создав объект исследования класса m551,вы можете методом доступа

getrange() получить ранг матрицы СЛАУ,а методом gv(int,int)

получить ССЫЛКУ на необходимый вам элемент матрицы

Напоминаем,что ссылка-это псевдоним имеющегося в памяти объекта

и ее можно использовать как слева так и справа оператора

присваивания(разумееттся если он определен для соответствующего

типа данных)

/*

//Метод Гаусса с выбором главного элемента

m551 t;

int r;

//Подпрограмма выбора главного элемента

void choice()

{

  double max;

  int num,i,j,k;

  for(i=0;i<r;i++)//по столбцам

  {

    max=t.gv(0,i);//вначале назначаем наибольшим i-й элемент

                  //0-й строки

    for(j=i,num=i;j<r;j++)   //теперь ищем нет ли большего в других

                             //строках

      if(fabs(t.gv(j,i))>max)//если нашли больший

      {

 max=fabs(t.gv(j,i)); //который временный-уступи место

 num=j;               //запоминаем строку с кандидатом на

                             //наибольший элемент столбца

      }

    if(num!=i)               //если это не тот самый

      for(k=0;k<r+1;k++)     //меняем строки местами

      {

        double temp=t.gv(num,k);

        t.gv(num,k)=t.gv(i,k);

        t.gv(i,k)=temp;

      }

  }

}

void main(void)

{

  int i,j,k;

  r=t.getrange();      // Получаем порядок системы

  choice();            //Выбор главного

  for(i=0;i<r;i++)     //Нет ли в диагонали нулей

    if(!t.gv(i,i))

      return;

  for(i=0;i<r;i++)//Прямой ход Гаусса

  {

    double sw=t.gv(i,i); //Запомним диагональный

    for(j=0;j<r+1;j++)

      t.gv(i,j)/=sw;     //Разделим всю строку на дигональный

    for(k=i+1;k<r;k++)

    {

      double c=t.gv(k,i);//Запомним элемент под диагональным в

                         //другой строке

      for(j=0;j<r+1;j++)

 t.gv(k,j)-=t.gv(i,j)*c;//вычтем из текущей строки

                               //вспомогательную, умноженную на

                               //удаляемый коэффициент

    }

  }

  for(i=r-2;i>=0;i--)//row Обратный ход Гаусса начинаем с

                     // предпоследней строки

  {

    double s=0;

       for(j=i+1;j<r;j++) //col

      s+=t.gv(i,j)*t.gv(j,r);//суммируем произведения коэффициентов

                             //на уже вычисленные и лежащие в

                             //столбце свободных членов иксы

    t.gv(i,r)-=s;            //заносим вычисленный икс в столбец

                             //свободных членов

  }

  for(i=0;i<r;i++)

    cout<<"x["<<i<<"]="<<t.gv(i,r)<<endl;

}

*/


Метод итераций
Итерационные методы решения СЛАУ

РАЗДЕЛ:МЕТОД ИТЕРАЦИЙ ДЛЯ РЕШЕНИЯ СИСТЕМ ЛИНЕЙНЫХ УРАВНЕНИЙ

Для решения задачи методом итераций система уравнений должна быть

преобразована к виду:

               x1=f(x2,x3,...xn,xn+1),

               x2=f(x1,x3,...xn,xn+1),

               ......................            (5)

               xn=f(x1,x2,...xn-1,xn+1).

При программной рализации приведение сводится к делению каждой

строки на коэффициент а(i,i).

Итерационный процесс начинается заданием начальных значений

компонент вектора решения х-в общем случае произвольных;эти

значения подставляются в уравнения (5) для вычисления следующего

приближения,это следующее значение становится  редыдущим,

подставляется в (5) для получения следующего и т.д. либо до

достижения заданного прирашения значений корней на очередной ите-

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

небходимо  задать в разделе объявлений программы.

  ПРОБЛЕМА  СХОДИМОСТИ

  Под сходимостью итерационного процесса понимается наличие предела

у последовательности получаемых решений и равенство этого предела

при бесконечном числе итераций точному решению системы.

  Для анализа сходимости приведем систему к виду:

        x=[a]*x + b  (*)

  Теорема сходимости:

  Просесс итераций для линейной системы (*) сходится к

единственному решению, если какая-нибудь каноническая норма матрицы

[a] меньше 1,т.е. для итерационного процесса

  x(k)= b + [a]*x(k-1)  (k=1,2,....,)

  достаточное условие сходимости при произвольном начальном

приближении x(o)   есть:

    ||[a]|| < 1

  Доказательство:

  Последовательность приближений:

   x(1)=b+[a]*x(0)

   x(2)=b+[a]*x(1)

   ...............

   x(k)=b+[a]*x(k-1)

  после подстановки последовательно сверху вниз приводит к :

  x(k)=([E]+[a]+[a]^2+[a]^3+...+[a]^(k-1))*b +([a]^k)*x(0)

  Так как ||[a]^k|| cтремится к 0 при k стрем к бесконечности и

||[a]|| < 1

  то lim[a]^k=0 и lim (E+[a]+[a]^2 +...+[a]^(k-1))=(E-[a])обр -по

теореме  сходимости матричного степенного ряда.

  Отсюда получаем   x=lim x^k =(E-[a])обр*b  или (E-[a])*x=b

  и  x=[a]*x+b,т.е. предельный вектор х есть решение системы

  СОДЕРЖАНИЕ РАБОТЫ

/*

//Итерационные методы решения СЛАУ - Метод Зейделя

m552 t;

double r;

void main(void)

{

  double e=0.000001,n=200,s;

  r=t.getrange();

  double *x=new double[r];

  for(int _y=0;_y<r;_y++)

    x[_y]=0;

  for(int k=0;k<n;k++)

  {

    for(int i=0;i<r;i++)

    {

      s=t.gv(i,r);

      for(int j=0;j<r;j++)

 s-=t.gv(i,j)*x[j];

      s/=t.gv(i,i);

      x[i]+=s;

    }

    if(fabs(s)<e)

      break;

  }

  for(int i=0;i<r;i++)

    cout<<"x["<<i<<"]="<<x[i]<<endl;

  delete x;

}

*/


Метод ортогонализации

РАЗДЕЛ:   МЕТОД ОРТГОНАЛИЗАЦИИ

 Метод позволяет осуществить его реализацию при помощи чрезвычайно

компактного алгоритма и компьютерной программы ,не требует никаких

проверок сходимости и сколько-нибудь существеннфх преобразований

исходной системы.

 Рассмотренные ранее методы всеми этими свойствами не обладают,что

обусловило его широкое использование в прикладных задачах.

В качестве вспомогательного средства метод используется в других

вычислительных алгоритмах например в задачах приближения

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

  СУЩНОСТЬ МЕТОДА

  Перенесем  свободные члены в левую часть уравнений и положим

    Xn+1=1

  Получим систему в виде

       n+1

        S a(i,j)*x(j)=0  где i-из мн-ва 1,2...n

       j=1

 Суммы в левых частях уравнений можно интерпретировать как

скалярные произведения векторов  a(i)  и х; в этом случае искомым

решением системы будет некоторый вектор Х в n+1-мерном

пространстве,ортогональный базису, образованному системными

векторами а(i).

Так как сам базис в общем случае не ортонормирован,то необходима

дополнительная процедура его ортогонализации.

 Для поиска решения системы добавим к векторам a(i) линейно

независимый от них вектор a(n+1) = (0,0,....,0,1).В векторном

пространстве размерности n+1 будем строить  ортонормированный

базис этого пространства.

Для поворота  какого либо вектора в пространстве на углы,

обеспечивающие его ортогональность некоторому  ортонормированному

базису векторов достаочно вычесть из него  векторы,направленные по

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

вектора на эти орты (то есть скалярным произведениям вектора и

соответствующих орт).

  Таким образом алгоритм вычислений решения СЛАУ методом

ортогонализации  состоит из следующих процедур:

  -нормируем вектор a(1) по модулю делением его компонент на корень

квадратный из суммы квадратов этих компонент;

  -начиная со второго вектора и до n+1-го последователно осуществим

ортогонализацию с предыдущими и затем нормирование по модулю,

повторив эту  процедуру несколько раз (3-5) для предотвращения

возможной неустойчивости процесса ортогонализации и повышения

точности вычислений в случаях   близких к нулю значений

определителя исходной системы.

  -последнюю строку расширенной матрицы раpделим на элемент

a(n+1,n+1),который должен по определению быть равен 1,получив таким

образом искомое решение в n первых элементах.

  СОДЕРЖАНИЕ РАБОТЫ

/*

//Метод ортогонального преобразования матрицы коэффициентов СЛАУ

double far *matr;

m553 t;

int r;

double scalp(int v1,int v2)

{

  double sum=0;

  for(int j=0;j<r;j++)  sum+=t.gv(v1,j)*t.gv(v2,j);

  return(sum);

}

void norm(int v)

{

  double sum=sqrt(scalp(v,v));

  for(int i=0;i<r;i++)   t.gv(v,i)/=sum;

}

void orto(int v)

{

  for(int i=0;i<v;i++)

  {

    double p=scalp(v,i);

    for(int j=0;j<r;j++) t.gv(v,j)-=p*t.gv(i,j);

  }

}

void main()

{

  r=t.getrange();

  for(int j=0;j<3;j++)

  {

    norm(0);

    for(int i=1;i<r;i++)

    {

      orto(i);

      norm(i);

    }

  }

  for(i=0;i<r;i++)

    t.gv(r-1,i)/=t.gv(r-1,r-1);

  for(i=0;i<r-1;i++)

    cout<<"x["<<i<<"]="<<t.gv(r-1,i)<<endl;

}

*/


Вычисление определителей

РАЗДЕЛ:Вычисление определителей

Для  квадратных матриц может быть выполнено с использованием ал-

горитма прямого хода Гаусса,приводящего матрицу к треугольной

форме,так как определитель треугольной матрицы равен произведе-

нию ее диагональных элементов.

ВОЗНИКАЮЩИЕ ПРОБЛЕМЫ:При малых диагональных элементах произведе-

ние их может привести к исчезновению порядка,при больших-к пере-

полнению.

СОДЕРЖАНИЕ РАБОТЫ

   Cоставьте программу для вычисления определителя

/*

//Вычисление определителей

m554 t;

int r;

int choice()

{

  double max;

  int num,i,j,k,how=0;

  for(i=0;i<r;i++)//col

  {

    max=t.gv(0,i);

    for(j=i,num=i;j<r;j++)   //row

      if(fabs(t.gv(j,i))>max)

      {

 max=fabs(t.gv(j,i));

 num=j;

      }

    if(num!=i)

    {

      for(k=0;k<r+1;k++)//num & i

      {

        double temp=t.gv(num,k);

        t.gv(num,k)=t.gv(i,k);

        t.gv(i,k)=temp;

      }

      how++;

    }

  }

  return how;

}

void main(void)

{

  r=t.getrange();

  int how=choice();

  for(int i=0;i<r;i++)//Прямой

  {

    double sw=t.gv(i,i);

    for(int j=i+1;j<r;j++)

      t.gv(i,j)/=sw;

    for(int k=i+1;k<r;k++)

    {

      double c=t.gv(k,i);

      for(j=0;j<r;j++)

 t.gv(k,j)-=t.gv(i,j)*c;

    }

  }

  double det=1;

  for(i=0;i<r;i++)

    det*=t.gv(i,i);

  det*=pow(-1,how);

  cout<<"Детерминант : "<<det<<endl;

}

*/


Вычисление обратных матриц решением СЛАУ
Вычисление элементов обратной матрицы решением СЛАУ

РАЗДЕЛ:Вычисление элементов обратной матрицы

Произведение обратной матрицы на исходную дает единичную матрицу:

                 [Aоб][A]=E

Обозначим элементы обратной матрицы через Xij .Произведение двух

квадратных матриц размером n x n  каждая в развернутой записи пред-

ставляет собой n систем линейных уравнений относительно неизвестных

Хij.Эти системы отличаются друг от друга только правыми частями,ко-

торыми будут столбцы единичной матрицы Е.Поэтому при использовании

метода Гаусса для решения полученных СЛАУ прямой ход можно

осуществить для всех систем.Расширенная матрица системы будет иметь

размеры n x 2n,причем левая половина -это исходная матрица [А],а

правая -единичная матрица Е

СОДЕРЖАНИЕ РАБОТЫ

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

//Вычисление элементов обратной матрицы

m555 t;

int r;

       //{Формирование правой половины матрицы}

void InitRight()

{

  for(int i=0;i<r;i++)

    for(int j=r;j<2*r;j++)

    {

      if(j==i+r)

 t.gv(i,j)=1;

      else

 t.gv(i,j)=0;

    }

}

//{Пеpестановка стpок матpицы}

void SortMatr()

{

  int k,l;

  double v,maxabs;

  for(int i=0;i<r;i++)

  {

    maxabs=fabs(t.gv(i,i));

    k=i;

    for(l=i+1;l<r;l++)

      if(fabs(t.gv(l,i))>maxabs)

      {

 maxabs=fabs(t.gv(l,i));

 k=l;

      }

    if(k!=i)

      for(int j=i;j<2*r;j++)

      {

 v=t.gv(i,j);

 t.gv(i,j)=t.gv(k,j);

 t.gv(k,j)=v;

      }

  }

}

//             {Решение системы методом Гаусса}

void GaussMeth()

{

  double s,_r;

  for(int i=0;i<r;i++)

  {

    s=t.gv(i,i);

    for(int j=i+1;j<2*r;j++)

      t.gv(i,j)/=s;

    for(j=i+1;j<r;j++)

    {

      _r=t.gv(j,i);

      for(int k=i+1;k<2*r;k++)

 t.gv(j,k)-=t.gv(i,k)*_r;

    }

  }

  for(int k=r;k<2*r;k++)

    for(i=r-1;i>=0;i--)

    {

      _r=t.gv(i,k);

      for(int j=i+1;j<r;j++)

 _r-=t.gv(j,k)*t.gv(i,j);

      t.gv(i,k)=_r;

    }

}

void main(void)

{

  r=t.getrange();

  cout<<"ИСХОДНАЯ МАТРИЦА"<<endl;

  for(int i=0;i<r;i++)

  {

    for(int j=0;j<r;j++)

      cout<<t.gv(i,j)<<"\t";

    cout<<endl;

  }

  InitRight();

  SortMatr();

  GaussMeth();

  cout<<"ОБРАТНАЯ МАТРИЦА"<<endl;

  for(i=0;i<r;i++)

  {

    for(int j=r;j<2*r;j++)

      cout<<t.gv(i,j)<<"\t";

    cout<<endl;

  }

}

*/


Вычисление обратных матриц через определитель
Вычисление элементов обратной матрицы через определитель

  РАЗДЕЛ:Решение СЛАУ методом нахождения

  обратной матрицы.

 Для решения СЛАУ применяют два класса методов:прямые и

итерационные.

 Прямые методы являются универсальными,но из-за большого

объема

вычислительных процедур применяются для решения систем

сравнительно невысокого порядка(n < 200).Итерационные методы

рекомендуются для СЛАУ высоких порядков.

Общая формулировка задачи:

Необходимо решить СЛАУ

  а11*x1 + a12*x2 +....+ a1n*xn = b1

  a21*x1 + a22*x2 +....+ a2n*xn = b2     (1)

  ..................................

  an1*x1 + an2*x2 +....+ ann*xn = bn

  где xk -неизвестные,a(ij)-заданные коэффициенты,

  b(i)-свободные члены.

  В матричной записи:

   [A][X]=[Y]                  (2)

  где X- вектор состоящий из (x1,x2,...xn),

  Y- вектор состоящий из (b1,b2,...bn).

  Общее решение находится просто:домножим обе части матричного

уравнения(2) слева на обратную матрицу [Aоб]:

   [Aоб][A][X]=[Aоб][Y]             (3)

Произведение обратной матрицы на исходную дает единичную матрицу:

   [Aоб][A]=E

 отсюда

  [X] = [Aоб][Y]               (4)

Т.о. задача сводится к обращению матрицы [A].

   | C1,1 C1,2 ... C1,n |

           Aоб]= | C2,1 C2,2 ... C2,n |           (5)

   | .................. |

   | Cn,1 Cn,2 ... C1,n |

  где Сi,j - получается высчитовается по формуле :

     i+j

  (-1)  |Ai,j|

  Сi,j = -------------                 (6)

                    |A|

  в которой |Ai,j| - определитель матрицы получаемой

  из исходной матрицы  [A]  исключением  j -строки и

  i -столбца(минор исходной матрицы) .

  |A| - определитель матрицы [A].

СОДЕРЖАНИЕ РАБОТЫ

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

МЕТОДИЧЕСКАЯ ПОМОЩЬ

   class m555{

  int n,m; //количество строк и столбцов

  double **mat;

  public :

m555(char *);  //конструктор для создания объекта

     //матрицы из файла

m555(int);     //конструктор для создания объекта

     //квадратной матрицы

m555(int,int); //конструктор для создания объекта

     //прямоугольной матрицы

m555(m555&);//конструктор позваляющий операции типа

    //m555 a=b;

~m555();    //деструктор

int getsize() {return n;}  //Возращает размеры квадратной матрицы

 //тоесть n

double getopr ( );   // возращает опредеелитель

m555 getobrmatr (); // возращает обратную матрицу

void getlitmatr (m555& ,int ,int  ); // возращает меньшую матрицу

       // без одного столбца

и одной строки

       // (минор)

friend ostream& operator << (ostream &,m555 &);//перегруженая

        //операция

 вывода

m555 operator *(m555&);//перегруженая операция *

m555 operator =(m555); //перегруженая операция =

};

 //конструктор для квадратной матрицы

 //данное заполнение матрицы позволяет

 //обращатся к элементам матрицы в виде mat[i][j]

  m555::m555 (int p):n(p),m(p)

  {

  mat= new double*[n];//отводим место под указатели

//на строки матрицы

  for(int i=0;i<n;i++)

  mat[i]=new double[n];//отводим место под элементы

 //строк

  }

  //диструкетор

 m555::~m555()

 {

  for(int i=0;i<n;i++)//освобождаем место отведенное

  delete[] mat[i]; //под элементы строк

  delete[] mat;//освобождаем место отведенное

       // под указатели на

строки матрицы

 }

 //конструктор для прямоугольной матрицы

  m555::m555 (int p,int r):n(p),m(r)

  {

  mat= new double*[n];

  for(int i=0;i<n;i++)

  mat[i]=new double[m];

  }

  // возращает опредеелитель

 double m555::getopr ()

 { int i,j,k,fl,r = n;

 double ** mat1,kf=1,vr;

 if ((r==1)&&(m==1)) return mat[0][0];

 mat1= new double*[n]; //отводим место под

 for( i=0;i<n;i++)     //дополнительную матрицу в которой

 mat1[i]=new double[n];//будем изменять элементы

 for(i=0;i<n;i++)

  for(j=0;j<n;j++)

   mat1[i][j]= mat[i][j];//заполняем дополнительную матрицу

 while (r>2)             //преобразование дополнительной

 {  if(mat1[r-1][r-1]==0)//матрицы пока ее ранг больше 2

    { fl=0;

    kf*=-1;

    for(i=0;i<r-1;i++)

     if(mat1[i][r-1]!=0)

     { fl=1;

    for(j=0;j<r;j++)

     {vr=mat1[r-1][j];

    mat1[r-1][j]=mat1[i][j];

    mat1[i][j]=vr;

     }

    break;

     }

    if(fl==0)return 0;

   }

   kf*=mat1[r-1][r-1];

   for(i=0;i<r;i++)

    mat1[r-1][i]/=mat1[r-1][r-1];

   for(i=0;i<r-1;i++)

    for(j=0;j<r;j++)

   mat1[j][i]-=mat1[j][r-1]*mat1[r-1][i];

  r--;

   }

 vr=(mat1[0][0]*mat1[1][1]-mat1[1][0]*mat1[0][1])*kf;

 for(i=0;i<n;i++)

  delete[] mat1[i];

  delete[] mat1;

 return vr;

 }

// возращает меньшую матрицу

// без с строки и d столбца

//

void m555::getlitmatr(m555& b,int c,int d )

 { int i,j,k,l;

  for(i=0,l=0;i<n;i++)

  {if(i==c)continue;

 k=0;

 for(j=0;j<n;j++)

  {

   if(j==d)continue;

   b.mat[l][k++]=mat[i][j];

  }

  l++;

 }

  }

// возращает обратную матрицу

// ГЛАВНАЯ ЧАСТЬ

m555 m555::getobrmatr ()

  { int i,j;

  m555 b(n);// создаем два

  m555 c(n-1); //объекта

      //в b будет обратная матрица

      //в с будет минор исходной

матрицы

  double sum=getopr();//получаем значение определителя

//исходной матрицы

     //проверяем sum на стремление к 0

     //sum*sum позволяет не думать о знаке

     if(sum*sum<0.0000000001)

    { cout<<"данную СЛАУ нельзя решить

данным методом";

      exit (0);

    };

  for(i=0;i<n;i++)

   for(j=0;j<n;j++)

    { getlitmatr(c,j,i);//в с получаем

минор

    //исходной

матрицы без

    //i-строки j-

столбца

    //от i и j зависит знак

   if((i+j)&1)

     b.mat[i][j]=-c.getopr();

   else

     b.mat[i][j]=c.getopr();

     }

   // делим элементы матрицы b на

   // значение определителя исходной матрицы

   for(i=0;i<n;i++)

      for(j=0;j<n;j++)

    b.mat[i][j]/=sum;

   return b;

    }

//перегруженая операция *

 m555 m555::operator*(m555& mul)

{

m555 res(n,mul.m);

for(int i=0;i<n;i++)

{

for(int j=0;j<mul.m;j++)

{ res.mat[i][j]=0;

for(int k=0;k<mul.n;k++)

res.mat[i][j]+=mat[i][k]*mul.mat[k][j];

}

}

 return res;

}

//конструктор для матрицы находящейся в файле

m555::m555(char *s)

{

int i,j;

 fstream fp(s,ios::in);

 if(!fp)

  {

  cout<<"\n Ошибка открытия файла "<<s;exit(0);

  }

  fp>>n>>m;

  if(n*m<=0)

  {

  cout<<"нереальные размеры матрицы\n";fp.close();exit(0);

  }

mat= new double*[n];

for( i=0;i<n;i++)

mat[i]=new double[m];

for(i=0;i<n;i++)

for(j=0;j<m;j++)

fp>>mat[i][j];

}

//перегруженая операция <<

ostream &operator << ( ostream & s,m555 &x)

{ int i,j;

for( i=0;i<x.n;i++)

{

for( j=0;j<x.m;j++)

s<<x.mat[i][j]<<"   ";

s<<endl;

}

return s;

}

//конструктор копии

m555::m555(m555 &b)

{

  int i,j;

  n=b.n;

  m=b.m;

  mat= new double*[n];

  for(i=0;i<n;i++)

  mat[i]=new double[m];

for(i=0;i<n;i++)

for(j=0;j<m;j++)

mat[i][j]=b.mat[i][j];

}

//перегруженая операция =

m555 m555::operator=(m555 b)

{

  int i,j;

  //n=b.n;

  if(n!=b.n||m!=b.m)

  {

  for(i=0;i<n;i++)

  delete[] mat[i];

  delete[] mat;

  n=b.n;

  mat= new double*[n];

  for(i=0;i<n;i++)

  mat[i]=new double[m];

  }

for(i=0;i<n;i++)

for(j=0;j<m;j++)

mat[i][j]=b.mat[i][j];

return *this;

}

int main (int argc,char *argv[])

{

 if(argc<2){cout<<"No file";

  return 0;

  }

m555 a(argv[1]);

m555 b(a.getsize());

m555 c(argv[2]);

cout<<a<<endl;

m555 b= a.getobrmatr();

cout<<b<<endl;

cout<<c<<endl;

m555 d=b*c;

cout<<d<<endl;

return 0;

}


Симплекс-метод в линейном программировании
Симплекс-метод решения общей задачи линейного программирования

             ЭЛЕМЕНТЫ ЛИНЕЙНОГО ПРОГРАММИРОВАНИЯ

                        Введение

Линейное программирование является одной из многочисленных областей

применения линейной алгебры в прикладных задачах,не поддающихся

решению методами математического анализа.

Основная задача линейного программирования (ОЗЛП) возникает при

числе уравнений,меньшем числа независимых переменных .В этом случае

не остается ничего иного,как выбрать N переменных по числу

уравнений в качестве БАЗИСНЫХ ,а остальные оставить ВОБОДНЫМИ;

решений системы относительно базиса-бесконечное множество,если на

них не наложить ограничений и не предъявить дополнительных

требований.

В ОЗЛП такими ограничениями,обеспечивающими возможность получения

единственного решения является требование неотрицательности корней

       xij >=0;

системы линейных уравнений

     A11*x1+A12*x2+...+A1n*xn=b1

     A21*x1+A22*x2+...+A2n*xn=b2

     ...........................  (**)

     Am1*x1+Am2*x2+...+Amn*xn=bm

 и необходимость обеспечения минимума некоторой линейной целевой

функции

    min  L= c1*x1+c2*x2+...+cn*xn

 ПРИМЕРЫ задач линейного программирования

 1.Задача о пищевом рационе

 Имеется 4 вида продуктов П1 П2 П3 П4 стоимостью с1 с2 с3 с4 каждый

и  содержащих соответственно П1-а11 ед белков, а12 углеводов,а13

жиров

      П2-а21   -""-     а22     "     а23  "

      П3-а31     "      а32     "     а33  "

      п4-а41     "      а42     "     а43  "

 Общая стоимость

      L=c1*x1+c2*x2+c3*x3+c4*x4

 должна быть минимальной при услолвии обеспечения в рационе

 белков-b1 ед    углеводов-b2 ед жиров-b3 ед

 и неотрицательности значений x1>=0;   x2>=0;   x3>=0; x4 >=0;

2.Задача о распределении ресурсов

Имеются ресурсы R1 R2 R3...Rm в количествах b1 b2 b3 ...bm

соответственно

С помощью этих ресурсов могут производиться товары Т1 Т2  Т3...Tn

Для производства 1-й единицы товара Tij необходимо Aij ед ресурса

Ri (i=1,2,...,m;j=1,2,...n) , и каждая единица ресурса Ri стоит Di

рублей

Каждая единица товара может быть рализована по цене Ci(i=1,2,...,n)

Cпрос составляет Kj ед товара Tj(j=1,2,...,n)

Необходимо определить какое количество какого товара необходимо

произвести,чтобы прибыль от реализации была максимальной?

Если x1,x2,..xn-количества товаров T1 T2 ...Tn  и положить

      x1=K1,x2=K2....xn=Kn           (*)

 а также учесть достаточность ресурсов получаем систему:

     A11*x1+A12*x2+...+A1n*xn=b1

     A21*x1+A22*x2+...+A2n*xn=b2

     ...........................     (**)

     Am1*x1+Am2*x2+...+Amn*xn=bm

 Себестоимость единицы товара Tj равна

     Sj=A1j*D1+A2j*D2+....+Amj*Dm

 Прибыль Qj от реализации ед товара Tj

     Qj=Cj-Sj

Общая прибыль

      L=Q1*x1+Q2*x2+....Qn*xn        (***)

Задача:выбрать такие неотрицательные x1,x2,...,xn,которые

удовлетворяют ограничениям (*) и (**) и обращают в максимум функцию

этих переменных (***)

Аналогично  формулируются другие практически важные адачи - об

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

перевозок ит.д.

 ДОПУСТИМЫМ РЕШЕНИЕМ ОЗЛП называют любую совокупность

неотрицательных x1,x2,...xn ,удовлетворяющую уравнениям (**)

 ОПТИМАЛЬНЫМ называют ДОПУСТИМОЕ решение,при котором функция (***)

обращается в минимум

 Наиболее универсальным методом решения ОЗЛП является

СИМПЛЕКС-МЕТОД, основанный на том,что вначале отыскивается

произвольное допустимое решение,а затем оно последовательно

улучшается до ОПТИМАЛЬНОГО

 СХЕМА СИМПЛЕКС-МЕТОДА:

 -составляется СИМПЛЕКС-ТАБЛИЦА из коэффициентов системы ,

представляющая  собой расширенную матрицу,дополненную строкой

коэффициентов целевой  функции,например N+1-й,а также нулевыми

строкой и столбцом для  учета производимых перестановок

 -выбирается произвольный набор БАЗИСНЫХ переменных по числу

уравнений

 -система рарешается относительно базисных переменных,выражая их

через   остальные(СВОБОДНЫЕ);

 -cвободные переменные приравнивают нулю,получая некоторое

  решение в виде  компонент вектора свободных членов

  -проверяют решение на допустимость-т е на отсутствие отрица-

  тельных  компонент;

  -при наличии отрицательных свободных членов производят

последовательную замену базисных переменных на свободные до

получения допустимого  решения,которое называют ОПОРНЫМ

  -ОПОРНОЕ решение является ОПТИМАЛЬНЫМ если в строке коэффициентов

  целевой функции отсутствуют отрицательные элементы,т е целевая

функция не может быть уменьшена за счет увеличения каких-нибудь

свободных переменных сверх нуля.

  ПРИВЕДЕНИЕ СИСТЕМЫ К СТАНДАРТНОЙ УДОБНОЙ ДЛЯ ПРЕОБРАЗОВАНИЙ ФОРМЕ

  Это приведение состоит в разрешении системы ** относительно

выбранных  базисных переменных,что достигается,например, решением

по методу Гаусса  за  один "ход";если выбрать первые n элементов

базисными то задача состоит в преобразовании левой части n*n

матрицы в единичную с одновременной обработкой всех элементов

                         -- NN cвободных переменных

          | +----------------------------------------+

            |    | 0    |  0   .|..1.....| m   |  m+1|

          N +----+------+-------+--------+-----+-----|

          б | n  |a11   | a12   |....... |a1m  |  b1 |

          а |n+1 |a21   | a22   |........|a2m  |  b2 |

          з | .  |......|.......|........|.....|     |

          и | .  |......|.......|........|.....|     |

          с | m  |an1   | an2 ..|........|anm  |  bn |

          н |    |an+1,1| an+2,2|........|an+1,m bn+1|  (Линейная

форма)

          ы +----------------------------------------+

          х

  После решения системы относительно базисных переменных и

пдстановки их  в линейную форму матрица приобретает вид с единичной

матрицей в правой  части

                         -- NN cвободных переменных

          | +----------------------------------------+

            |    | 0    |  0   .|..1.....| m   |  m+1|

          N +----+------+-------+--------+-----+-----|

          б | n  |  1   |  0    |....... |с1m  |  d1 |

          а |n+1 |  0   |  1    |........|c2m  |  d2 |

          з | .  |......|.......|........|.....|     |

          и | .  |......|.......|........|.....|     |

          с | m  |  0   |  0    |1.......|cnm  |  dn |

          н |    |fn+1,1| fn+2,2|........|fn+1,m Fn+1|  (Линейная

форма)

          ы +----------------------------------------+

          х

  АЛГОРИТМ ЗАМЕНЫ БАЗИСНЫХ ПЕРЕМЕННЫХ

  Процедура переразрешения системы ОЗЛП относительно новых базисных

переменных может быть формализована и сведена к лгоритму

(последовательности допустимых однообразных  действий над

элементами системной матрицы).

  Формулы преобразования могут быть легко выведены и должны

выполняться в следующем порядке

  -Разрешающий элемент матрицы (имеющий один индекс -номер

базисного элемента а второй-свободного для замены)заменяется

обратной ему величиной

  -Все элементы разрешающего столбца умножаются на обновленный

разрешающий   элемент и мняют знак

  -Все элементы,кроме разрешающих столбца и строки,вычисляются так

          m[i,j]:=m[i,j]+m[i,jr]*m[ir,j];

   где jr-разрешающий столбец

       ir-разрешающая строка

  - Все оставшиеся элементы разрешающей строки умножаются на

обновленный  разрешающий элемент

  АЛГОРИТМ ПОИСКА ОПОРНОГО РЕШЕНИЯ ОЗЛП

  -если все свободные члены в матрице,разрешенной относительно

базиса, положительны,то они и представляют собой допустимое опорное

рашение и можно переходить к этапу его оптимизации

  -если среди свободных членов есть отрицательные то их вектор не

годится в качестве опорного решения т к все составляющие по условию

неотрицательны и необходимо производить последовательный обмен

между базисными и свободными переменными ,пока не избавимся от

отрицательных свободных членов или не придем к выводу об отсутствии

допустимого опорного решения

  АЛГОРИТМ ВЫБОРА РАЗРЕШАЮЩЕГО ЭЛЕМЕНТА ДЛЯ ПРИБЛИЖЕНИЯ К ОПОРНОМУ

РЕШЕНИЮ

  -В первой найденной строке матрицы с отрицательным свободным

членом ищем отпицательный элемент;если такого нет ,то система

несовместима с требованием неотрицательности решений;(вся правая

часть ур-я может быть только отрицательной)

  -если отрицательный элемент найден то этот столбец является

разрешающим и осталось выбрать разрешающую строку

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

имеющие одинаковый знак со свободным членом и выбираем тот,

отношение к которому свободного члена минимально-это разрешающая

строка

 АЛГОРИТМ ПОИСКА ОПТИМАЛЬНОГО РЕШЕНИЯ

  -Оптимзация найденного опорного решения состоит в перемещении по

допустимым опорным решениям таким образом ,чтобы  линейная  форма

приняла  минимально возможное  значение;для  этого  проверим

наличие  положительных элементов в  строке линейной формы

(N+1-й),которое свидетельствует о возможнгости уменьшения значения

формы увеличением соответствующих переменных

  -если полжимтельный элемент найден то в соответствующем столбце

ищем разрешающий элемент-положительный и с минимальным отношением к

нему свободного члена;

  -это все повторяется до тех пор пока в строке линейной формы не

останется положительных элемекнтов(без учета свободного члена)

  -если в столбце содержащем положительный элемент формы нет

положительного ситемного элемента тооптимизируемая функция не

ограничена снизу и ОЗЛП не имеет оптимального решения

 СОДЕРЖАНИЕ РАБОТЫ

  -изучить общий алгоритм решения ОЗЛП

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

  -изучить алгоритм поиска опорного решения

  -изучить алгоритм поиска оптимального решения

/*

m561 t;

int r,c,//Кол-ва уравнений и переменных

rr=-1,rc=-1;//Координато-флаги разрешаюшего элемента

void zamena()

{

  double re=t.getvalue(rr,rc);//Разрешающий элемент

  //Замена по алгоритму , см. урок

  t.getvalue(rr,rc)=1/re;

  for(int i=0;i<r+1;i++)

    for(int j=0;j<c;j++)

      if(i!=rr || j!=rc)

        t.getvalue(i,j)-=t.getvalue(rr,j)*(t.getvalue(i,rc)/re);

  for(i=0;i<r+1;i++)if(i!=rr)t.getvalue(i,rc)/=-re;

  for(i=0;i<c;i++)if(i!=rc)t.getvalue(i,rc)/=re;

  //

  for( i=0;i<r+1;i++)

  {

    cout<<endl;

    for(int j=0;j<c;j++)

    {

      cout.width(5);

      cout<<t.getvalue(i,j)<<" ";

    }

  }

  cout<<endl;

  getch();

  //

 }

 void opor()

 {

  double min=MAXDOUBLE,//минимум отношения свободного члена к

текущему элементу

  tempmin;//отношение свободного члена к текущему элементу

  for(int i=0;i<r;i++)

  {

     rc=-1;rr=-1;

    if(t.getvalue(i,0)<0)//ЕСЛИ НАШЛИ ОТРИЦ СВОБ ЧЛЕН

    {

      for(int j=1;j<c;j++)

        if(t.getvalue(i,j)<0)       //ЕСЛИ НАШЛИ ОТРИЦ КОЭФ В

УРАВНЕНИИ

 {

   rc=j;                      //то это разреш столбец

          for(int k=0;k<r;k++)

if((t.sign(t.getvalue(k,rc))==(t.sign(t.getvalue(k,0)))))

            //если знаки одинаковы

       if((tempmin=t.getvalue(k,0)/t.getvalue(k,rc))<min)

              //если отношение меньше минимального

                {min=tempmin;rr=k;}

   if(rr==-1)//если не нашли разреш строку

      {  cout<<"Система не совместна\n";  exit(0);   }

   zamena();//Вызов процедуры замены свободной и базисных

переменных

 }

     }

   }

 //Отладочная печать

   for( i=0;i<r+1;i++)

  {

    cout<<endl;

    for(int j=0;j<c;j++)

    {

      cout.width(5);

      cout<<t.getvalue(i,j)<<" ";

    }

  }

  cout<<endl;

  getch();

 //

 }

void optim()//Поиск оптимального решения

{

  int flag=0,//Оптимальное ли решение?

  flag2=0;//Ограничена ли форма?

  double min=MAXDOUBLE,tempmin;

  for(int i=1;i<c;i++)

    if(t.getvalue(r,i)>0)

    {

      flag=1;

      for(int j=0;j<r;j++)

      //если в лин. форме есть пол . эл. то ограничена

 if(t.getvalue(i,r)>0)

 {

   flag2=1;

   rc=i;//rr=j;

   for(int k=0;k<r;k++)

            //if(t.sign(t.getvalue(k,rc)==t.sign(t.getvalue(k,0))))

              if((tempmin=t.getvalue(k,0)/t.getvalue(k,rc))<min)

                {min=tempmin;rr=k;}

          zamena();

          return;

 }

      if(!flag2)

      {

 cout<<"Линейная форма не ограничена снизу";

 exit(0);

      }

    }

  if(!flag)

  {

    cout<<"Найдено оптимальное решение...\n";

    for(i=0;i<r;i++)

      cout<<t.getvalue(i,0)<<"  ";

    double sum=t.getvalue(r,0);//Вычисление формы

    for(i=c-r-1;i<r+1;i++)

      sum+=t.getvalue(i-(c-r-1),0)*t.getvalue(r,i);

    cout<<endl<<endl<<"Summa : "<<sum<<endl;

    exit(0);

  }

}

void _main(void)

{

  r=t.getrow();

  c=t.getcol();

  opor();

  for(;;)

    optim();

}

*/


Транспортная задача

 /*

  Некоторые сведения из теории графов

       Основные определения.

 Наглядное представление о графе можно получить если

представить себе некоторое множество точек плоскости X

,называемых вершинами ,и множество направленных отрез-

ков U,соединяющих все или некоторые из вершин и называе-

мых дугами.Математически граф можно представить как пару

множеств X и U :

   G=(X,U).

 Две вершины x и y называются смежными ,если они различ-

ны и если существует дуга ,ведущая из x в y.

 Путем в графе G называется такая последовательность дуг

u=(u1,u2,...,uk),в которой конец каждой предыдущей дуги

совпадает с началом последующей.Путь u,последовательными

вершинами которого являются вершины a,b,...,m ,обознача-

ется через u=(a,b,...,m).Длиной дуги u=(u1,u2,...,uk)

называется число l(u)=k,равное числу дуг,составляющих

путь u.Путь ,в котором никакая дуга не встречается дваж-

ды,называается простым.Путь,в котором никакая вершина не

встречается дважды ,называется элементарным.

 Контур-это конечный путь u=(x1,...,xk),у которого началь-

ная вершина x1 совпадает с конечной xk.

 Иногда граф рассматривают без учета ориентации его дуг.

В этом случае его называют неориентированым графом.Для

неориентированого графа понятия дуга,путь и контур заме-

няются понятиями ребро ,цепь,цикл.

 С понятием неориентированого графа связана важная харак-

теристика ,называемая связностью графа.Говорят,что граф

связен,если любые две его вершины можно соединить цепью.

     Задача о кратчайшем пути.

 В практических приложениях имеет большое значение задача

о нахождении кратчайшего пути между двумя вершинами связ-

ного неориентированого графа.К такой задаче сводятся мно-

гие задачи выбора наиболее экономичного (с точки зрения

расстояния,времени или стоимости) маршрута на имеющейся

карте дорог и др.

  Задача о кратчайшем пути на графе в общем виде может

быть сформулирована следующим образом.Дан неориентирован-

ный граф G=(X,U).Каждому ребру этого графа приписано не-

которое число l(u)>=0 ,называемое длиной ребра.Требуется

для двух произвольных вершин a и b графа G найти путь

uab,причем такой,чтобы его полная длина была наименьшей.

 Рассмотрим для начала алгоритм решения задачи частного

вида ,когда длина каждого ребра равна единице.

  Общее правило для нахождения кратчайшего пути в графе

состоит в том,чтобы каждой вершине xi приписать индекс

pi,равный длине кратчайшего пути из данной вершины в ко-

нечную.Приписывание индексов вершинам в случае графа еди-

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

      1) Конечной вершине x0 приписывается индекс 0;

      2) Всем вершинам,из которых идет ребро в конечную

вершину,приписывается индекс 1;

      3) Всем вершинам ,еще не имеющим индексов ,из кото-

рых идет ребро в вершину с индексом pli,приписывается ин-

декс pl(i+1).Этот процесс продолжается до тех пор,пока не

будет помечена начальная вершина.По окончании разметки

индекс у начальной вершины будет равен длине кратчайшего

пути.Сам кратчайший путь найдем,если будем двигаться из

начальной вершины в направлении убывания индексов.

   Процесс приписывания индексов для графов с ре6брами

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

      1) Каждая вершина xi помечается индексом pi.Перво-

начально конечной вершине x0 приписывается индекс p0=0.

Для остальных вершин предварительно полагаем pi=беско-

нечности(i!=0).

     2) Ищем такую дугу (xi,xj) для которой pi-pj>l(xi,xj)

,и заменяем индекс pi индексом pj1=pi+l(xi,xj)<pj.Про-

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

ется хотя бы одна дуга,для которой можно уменьшить pi.

    Отсюда вытекает (неважно,как) правило для нахождения

кратчайшего пути :

    Пусть xn=a-начальная вершина с индексом pn.Ищем верши-

ну xk,такую,что pk1-pk2=l(xk1,xn).Далее ищем вершину xk2,

такую,что pk1-pk2=l(xp2,xp1) ит.д. до тех пор, пока не

дойдем до конечной вершины.

       Транспортная задача и ее решение методами

      теории  графов.

 Формулировка  транпортной задачи по критерию стоимости

такова :

 Даны списки пунктов -источников в каждом из  которых

сосредоточено определенное количество однородного груза

и пунктов-назначений в каждый из которых нужно завезти

столькл-то вышеупомянутого груза.

 Существует несколько способов решения этой задачи как

то :

  - метод потенциалов

  - интерпретация транспортной задачи как стандартной за-

дачи линейного программироваеия

    Но все они блекнут на фоне того ,что нам может предло-

жить теория графов.Решение ,которое будет приведено базиру-

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

двух теоремках.Здесь этот матариал излагаться не будет по

причине достаточного объема и недостаточной ясности содер-

жания.Желающие могут подробнее ознакомится сэтой темой в

разделе "Транспортные потоки" теории графов.

  Итак,перейдем к малосодержательной сути алгоритма.

 Дано :

    x1,x2,...,xm - кол-ва груза для перевозки.

    y1,y2,...,yn - потребнлсти в доставке.

    mtr[m][n] -матрица стоимостей перевозки единицы груза

из одного пункта в другой.

   g(j) -  потребность в yj;

   f(i) - кол-во груза в xi;

Внимание,суть алгоритма :

 1) Ищем НАИДЕШЕВЕЙШИЙ (в смысле уплаты за единицу груза )

из возможных путей между xi и yj.

 2) Пропускаем по найденому пути r единиц груза.

    Если  f(i)>=g(j)  - r=yj,xi-=yj,yj=0,

    иначе  r=xi,yj-=xi,xi=0.

 3) Если не все грузы перевезены и не все потребности

удовлетворены - выполняем 1).

        КОНЕЦ.

Задания

    1) Создать действующий алгоритм.

    2) Откомментировать приведенный ниже текст

программы  для решения общей задачи -т.е. в

случае,когда сеть дорог представляет собой

произвольный неориентированый граф.

    3) В привенном тексте допущено упущение-

программа не воспринимает пунктов,не сообщаю-

щихся между собой и приходится просто присваи-

вать соответствующим элементам матрицы доста-

точно большие значения.Исправить.

    4) Вычислить и вывести на экран стоимость

всей перевозки во втором случае.

   P.S. Формат данных в файле для этой задачи :

 n-кол-во пунктов-назначений

 v--//-источников

 r--//-промежуточных пунктов

 матрица стоимостей проезда

  по вертикали - m+k

  по горизонтали - n+k

 Если пункты не сообщаются между собой-

стоимость перевозки=-1.

*/

//Частный случай

#include<fstream.h>

#include<stdlib.h>

void main(int ar,char *br[])

{

 double *tab,*x,*y,max,min,how,prise=0;

 int i,j,k,l,fl=0,m,n;

 ifstream f;

 ofstream of;

 of.open("transp.rez");

   if(ar!=2)

   {

    cout<<"\nА где файл с матрицей стоимостей перевозок ?\n";

    exit(0);

   }

 f.open(br[1]);

  if(!f || !of)

  {

   cout<<"\n Файловая ошибка\n";

   exit(0);

  }

  f>>n;

  f>>m;

    tab=new double[m*n];

    x=new double[m];

    y=new double[n];

    if(!y || !x || !tab)

    {

     cout<<"\nНедостаточно памяти для работы программы\n";

     if(y) delete y;

     if(x) delete x;

     if(tab) delete tab;

     exit(0);

    }

   for(i=0;i<m;i++)

    f>>x[i];

   for(i=0;i<n;i++)

    f>>y[i];

     for(i=0;i<m;i++)

      for(j=0;j<n;j++)

       f>>tab[i*n+j];

       f.close();

    max=0;

      for(i=0;i<m;i++)

       for(j=0;j<n;j++)

 if(tab[i*n+j]>max) max=tab[i*n+j];

    cout<<"\nНаправление перевозок и количество перевозимого

груза:";

    of<<"\nНаправление перевозок и количество перевозимого груза:";

      for(;!fl;)

      {

  min=max+1;

    for(i=0;i<m;i++)

     for(j=0;j<n;j++)

      if(tab[i*n+j]<min && x[i]>0 && y[j]>0)

      {

       min=tab[i*n+j];

        k=j;

        l=i;

      }

  if(min==max+1)

  {

   fl=1;

   continue;

  }

 if(x[l]>y[k])

 {

  x[l]-=y[k];

  how=y[k];

  y[k]=0;

 }

 else

 {

  y[k]-=x[l];

  how=x[l];

    x[l]=0;

 }

 prise+=how*tab[l*n+k];

 cout<<"\nx"<<(l+1)<<"->"<<"y"<<(k+1)<<" , "<<how;

 of<<"\nx"<<(l+1)<<"->"<<"y"<<(k+1)<<" , "<<how;

      }

      cout<<"\nОбщая стоимость перевозок="<<prise;

      of<<"\nОбщая стоимость перевозок="<<prise;

      cout<<"\n(См. также файл transp.rez)\n";

    delete x;

    delete y;

    delete tab;

    of.close();

  exit(0);

}

llllllllllllllllllllllllllllllllllll

#include<fstream.h>

class Set {

    public :

  int *tab,*tov1,*tov2,m,n,k,max,how;

  int ifend(void);

  Set(char *);

  ~Set();

};

 Set::Set(char *fn)

 {

  int i,j;

  ifstream f(fn);

  f>>n;

  f>>m;

  f>>k;

  tab=new  int[(m+k)*(n+k)];

     tov1=new int[n];

     tov2=new int[m];

 for(i=0;i<m;i++)

    f>>tov2[i];

 for(i=0;i<n;i++)

    f>>tov1[i];

      for(i=0;i<m+k;i++)

       for(j=0;j<n+k;j++)

 f>>tab[i*(n+k)+j];

      max=0;

    for(i=0;i<m+k;i++)

     for(j=0;j<n+k;j++)

      if(tab[i*(n+k)+j]>max)

      max=tab[i*(n+k)+j];

 }

  Set::~Set()

  {

      delete tab;

      delete tov1;

      delete tov2;

  }

Set::ifend()

{

  int s=0,l=0,p;

 for(p=0;p<n;p++)

   s+=tov1[p];

 for(p=0;p<m;p++)

   l+=tov2[p];

   if(!s || !l)

   if(s)

     {how=s;return(1);}

     else

     {how=l;return(2);}

     else

       return(0);

}

void  main(int ar,char *br[])

{

   int a,b,j,i,count,*type,*num,*x,*y,r1,r2,pr=0,buf;

   int *path1,*path2,min;

   ofstream of("mgraf.rez");

    if(ar!=2)

 return ;

   Set q(br[1]);

     path1=new int[q.n+q.k];

     path2=new int[q.m+q.k];

     type=new int[q.n+q.k];

     num=new int[q.n+q.k];

     x=new int[q.m+q.k];

     y=new int[q.n+q.k];

    cout<<"\n             Транспортная задача по критерию

стоимости";

    cout<<"\nИсходные данные в файле "<<br[1];

    cout<<"\nНаправления перевозок :\n(xi - пункты источники.\

  \n yj - пункты приемники.\

   \n zk - промежуточные пункты.)";

    of<<"\n             Транспортная задача по критерию стоимости";

    of<<"\nИсходные данные в файле "<<br[1];

    of<<"\nНаправления перевозок :\n(xi - пункты источники.\

  \n yj - пункты приемники.\

   \n zk - промежуточные пункты.)";

//Пока все грузы не вывезены и все потребности не удовлетворены

   for(;;)

   {

    count=0;

//Подготовка массивов расстояний

   for(i=0;i<q.m;i++)

     path2[i]=0;

   for(i=q.m;i<q.m+q.k;i++)

     path2[i]=-1;

   for(i=0;i<q.n+q.k;i++)

     path1[i]=-1;

//Пока не найден кратчайший путь

    for(;;)

    {

      min=q.max+1;

//Поиск минимального индекса перевозки

       for(i=0;i<q.m+q.k;i++)

 for(j=0;j<q.n+q.k;j++)

 if(((path1[j]>=0 && path2[i]<0) || (path1[j]<0 &&

path2[i]>=0)) &&

   q.tab[i*(q.n+q.k)+j]<min)

 if((i<q.m && q.tov2[i]==0) || (j<q.n && q.tov1[j]==0) ||

        q.tab[i*(q.n+q.k)+j]<0) continue;

  else

  {

   min=q.tab[i*(q.n+q.k)+j];

   a=j ;

   b=i;

  }

//Если не найден - выйти

       if(min==q.max+1)  break;

//Присваивание новым доступным пунктам индексов

       if(path1[a]>=0)

       {

  path2[b]=path1[a]+q.tab[b*(q.n+q.k)+a];

  if(b>=q.m)

    path1[b-q.m+q.n]=path2[b];

 }

 else

 {

  path1[a]=path2[b]+q.tab[b*(q.n+q.k)+a];

   if(a>=q.n)

  path2[a-q.n+q.m]=path1[a];

 }

    if(a<q.n) break;

    }

  for(i=0;i<q.n+q.k;i++)

     y[i]=0;

  for(i=0;i<q.m+q.k;i++)

     x[i]=0;

     y[a]=1;

/*

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

  кратчайшего пути.

 */

 for(;b>=q.m;)

 {

   for(i=0;i<q.m+q.k && b>=q.m;i++)

    for(j=0;j<q.n+q.k && b>=q.m;j++)

    {

    r1=q.tab[i*(q.n+q.k)+j]-path1[j]+path2[i];

    r2=q.tab[i*(q.n+q.k)+j]-path2[i]+path1[j];

     if((r1==0 || r2==0) &&

    (x[i] || y[j]) && (!x[i] || !y[j]))

       {

        if(x[i])

        {

  if(j<q.n && !q.tov1[j]) continue;

   if(j>=q.n)

   {

     num[count]=j-q.n;

     type[count]=2;

   }

   else

   {

     num[count]=j;

     type[count]=1;

   }

   y[j]=1;

    if(j>q.n)

   x[j-q.n+q.m]=1;

   break;

        }

       if(y[j])

        {

  if(i<q.m && !q.tov2[i]) continue;

   if(i>=q.m)

   {

     num[count]=i-q.m;

     type[count]=2;

   }

   else

   {

     num[count]=i;

     type[count]=0;

   }

  x[i]=1;

   if(i>=q.m)

  y[i-q.m+q.n]=1;

  b=i;

        }

        count++;

       }

    }

 }

      if(q.tov1[a]>q.tov2[b])

      {

       buf=q.tov2[b];

       q.tov2[b]=0;

       q.tov1[a]-=buf;

      }

      else

       {

       buf=q.tov1[a];

       q.tov1[a]=0;

       q.tov2[b]-=buf;

       }

    cout<<"\n x"<<(b+1)<<"  ";

    of<<"\n x"<<(b+1)<<"  ";

     if(count>=2)

      for(i=count-2;i>=0;i--)

      {

 cout<<" z"<<(num[i]+1)<<"  ";

 of<<" z"<<(num[i]+1)<<"  ";

      }

    cout<<"y"<<(a+1)<<" ,"<<buf;

    of<<"y"<<(a+1)<<" ,"<<buf;

    if(q.ifend()) break;

   }

    switch(q.ifend())

    {

     case 1 :

 cout<<"\nНе все потребности  удовлетворены.\

   \nНедостаток составляет "<<q.how<<" единиц(ы) груза.";

 of<<"\nНе все потребности  удовлетворены.\

   \nНедостаток составляет "<<q.how<<" единиц(ы) груза.";

   break;

     case 2 :

 cout<<"\nНе весь груз вывезен.\

   \nОстались невостребованными "<<q.how<<" единиц(ы)

груза.";

 of<<"\nНе весь груз вывезен.\

   \nОстались невостребованными "<<q.how<<" единиц(ы)

груза.";

   break;

    }

    cout<<"\nВсе это вы можете увидеть в файле  mgraf.rez";

  delete path1;

  delete path2;

  delete  num;

  delete type;

  delete x;

  delete y;

}


Метод Эйлера

ТЕМА:ПРИБЛИЖЕННОЕ ИНТЕГРИРОВАНИЕ ДИФФЕРЕНЦИАЛЬНЫХ УРАВНЕНИЙ

ВВЕДЕНИЕ

Обыкновенные дифуравнения (ОДУ) широко используются для

математического моделирования процессов и явлений в различных

областях науки и техники-для описания переходных процессов в

химических реакторах и радитехнических цепях,биологических

популяциях и экономических системах ит п

Дифференциальное уравнение порядка р,решенное относительно

производной р-го порядка ,записывается в виде

      dy^p/dx^p=f(y,dy/dx,dy^2/dx^2,...,dy^(p-1)/dx^(p-1),x)  (1)

Если положить

   dy/dx=u,dy^2/dx^2=v,...,dy^(p-1)/dx^(p-1)=z                (2)


Метод Рунге -Кутты 2-го порядка
Методы Рунге-Кутты второго порядка

Методы Рунге-Кутта 2-го порядка

Для уменьшения погрешности методов использующих тейлоровское

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

количество членов ряда .Однако при этом возникает  необходимость

аппроксимации производных от правых частей ОДУ.Основная идея

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

аппроксимируются через значения функции f(x,y) в точках на

интервале [x0,x0+h],которые выбираются из условия наибольшей

близости алгоритма к ряду Тейлора.

В зависимости от старшей степени h с которой учитываются члены ряда

построены схемы Рунге-Кутта разных порядков точности

Так например для 2-го порядка получено однопараметрическое

семейство схем вида

 y(x0+h)=y0 + h*[(1-L)f0 + L*f(x0+г*h,y0+г*f0*h)]+  O(h^3),   (*)

где 0 < L  <=1 -свободный параметр

    f0 = f(x0,y0),    г =1/2*L  O(h^3) -остаточная погрешность

Для параметра L наиболее часто используют значения L=0.5  и L=1

В первом случае формула (*) приобретает вид

       y(x0+h) =y0 + h*[f0 + h, y0+h*f0)]/2

Вначале вычисляется приближенное решение ОДУ в точке x0+h по

формуле Эйлера

                 Yэ = y0 +h*f0.

 Затем определяется наклон  интегральной  кривой  в  найденной

точке f(x0+h,Yэ) и после нахождения среднего наклона на шаге h

находится уточненное значение Yrk =y(x0+h) .Схемы подобного типа

называют "пргноз-коррекция"

С  целью экономии памяти при программировании алгоритма

обобщенного на системы ОДУ изменим его запись с учетом того что

                y0= Yэ-h*f0

          Yk(x0+h) =Ykэ + h*[fk(x0+h,Ykэ)]/2

где k-номер решения для системы ОДУ.Теперь не придется держать в

памяти массив  начальных значений Yk0 -его можно забыть после

вычисления значений эйлеровских приближений Ykэ размещаемых на

месте массива Yk0

Во 2-м случае при  L=1  от формулы (*) переходим к схеме

    Y(x0+h) = Y0 + h*f(x0+h/2,Y0+h*f0/2)

Здесь при прогнозе определяется методом Эйлера решение в точке

x0+h/2

                Y1/2= Y0+h*f0/2

а  после  вычисления наклона касательной к интегральной кривой в

средней точке решение корректируется по этому наклону

//Тестовая программа для метода Рунге-Кутты 2-го порядка

m572 test;                           //Создаем объект исследования

//Составляем подпрограммку Рунге-Кутты на 1 шаг

double rk2(int num,double step,double x)

{

 double start=test.getstart(num);       //Сохраняем нач условие

 double ed=test.getvalue(num,x);        //Получаем наклон по Эйлеру

 double ye=start+step*ed;               //Значение в точке Эйлера

 test.setstart(num,ye);                 //Корректируем нач условие

 double rd=test.getvalue(num,x+step);   //Наклон в точке Эйлера

 double rk2=start+step*(ed+rd)/2;       //Добавка пропорц среднему

 test.setstart(num,rk2);                //Коррекция нач условия

 return rk2;

}

void main(void)

{double step=(test.getmax()-test.getmin())/100;      //Вычисл шаг

for(double x=test.getmin();x<=test.getmax();x+=step)//Цикл по шагам

   {

     cout<<endl<<x<<"\t";

     for(int i=1;i<=test.getrange();i++)

     {

   double y=rk2(i,step,x);               //Получаем  знач в точке х

   cout.width(10); cout<<y<<"\t";        //Печатаем

     }

   }

}


Метод Рунге -Кутты 4-го порядка
Методы Рунге-Кутты четвертого порядка

             Метод Рунге-Кутта 4-го порядка

Для построения вычислительных схем методов Рунге-Кутта 4-го порядка

в тейлоровском  разложении искомого решения Y(x) учитываются члены

со степенью шага h до 4-й включительно

После аппроксимации производных правой части ОДУ F(x,y) получено

семейство схем Рунге-Кутта 4-го порядка из которых наиболее

используемой в вычислительной практике является следующая

   y(x0+h) = y0 + (k1 + 2*k2 +2*k3 +k4)/6 +O(h^5)   (9)

где

 k1 = h*f(x0,y0),      k2 =h*f(x0+h/2,y0+k1/2),

 k3 = h*f(x0 +h/2, y0+k2/2),  k4 =h*f(x0 +h, y0 +k3).

Схема (9) на каждом шаге требует вычисления правой части ОДУ в 4-х

точках

Схема обобщается для систем ОДУ записанных в форме Коши Для

удобства программной  реализации формулы  (9) рекомендуется

преобразовать к виду

 Yi(x0 +h) = Yi0 +(qi1 + 2*qi2 + qi3 + qi4)/3 +O(h^5)      (10)

где

 qi1 =h2*fi(x0,yi0),   h2 = h/2

 qi2 = h2*fi(x0 + h2, yi0 +qi1),

 qi3 = h*fi(x0+h2, yi0 +qi2),

 qi4 =h2*fi(x0 + h, yi0 +qi3),

 i = 1,2,...,n-номер уравнения в системе ОДУ из n уравнений

/*

void main(void)

{

   m573 test;

   double shag=(-test.getmin()+test.getmax())/1000;//shag*

   for(double x=test.getmin();x<test.getmax();x+=shag)

   {

     cout<<endl<<x<<"\t";

     for(int i=1;i<=test.getrange();i++)

     {

       double y=test.getstart(i);

       double k1=test.getvalue(i,shag,x)-y;

       test.setstart(i,y+shag*k1/4);

       double k2=test.getvalue(i,shag,x+shag/2)-y;

       test.setstart(i,y+shag*k2/4);

       double k3=test.getvalue(i,shag,x+shag/2)-y;

       test.setstart(i,y+shag*k3);

       double k4=test.getvalue(i,shag,x+shag)-y;

       double d=(k1+2*k2+2*k3+k4)/6;

       test.setstart(i,y+d);

       cout<<y+d<<"\t";

     }

   }

   for(int i=1;i<=test.getrange();i++)

     cout<<endl<<test.getstart(i)<<endl;

}

*/


Метод Адамса

При решении задачи Коши методами Рунге-Кутты правые части ОДУ

вычисляются в нескольких точках на каждом шаге.Количество точек

зависит от порядка используемого метода.

После определения интегральной кривой в нескольких точках X0,X1,...

можно использовать один из методов интерполяции для получения

решения в очередной точке с меньшим числом вычислений правых частей

ОДУ .Такие методы называют многоточечными или многошаговыми.

Алгоритмы этих методов используют аппроксимацию интерполяционными

полиномами либо правых частей ОДУ либо интегральных кривых Yk(X)

Рассмотрим 4-точечный метод с аппроксимацией правых частей ОДУ

Пусть дополнительно к точке начальных условий мы получили еще

3 точки решения с помощью например метода Рунге-Кутты 4-го порядка

Правая часть уравнения F(x,y) в точках X0,X1,X2,X3 будет функцией

одного аргумента X

                        F(X)=F(X,Y(X))

Ее значения в рассматриваемых точках обозначим через F0,F1,F2,F3

В окрестности узлов X0,X1,X2,X3 заменим F(X) интерполяционным

полиномом Ньютона

  F(X)=F0+F01(X-X0)+F012(X-X0)(X-X1)+F0123(X-X0)(X-X1)(X-X2)   (1)

где F01,F012,F0123-разделенные разности

Решение в точке X4=X3+h представим в виде тейлоровского разложения

в окрестностях точки X3:

                   ,              ,,             ,,,

 Y4=Y3 + hF3+(h^2/2)F(X3) +(h^3/6)F(X3) +(h^4/24)F(X3)        (2)

Дифференцируя полином (1) получим выражения для производных,

подставляя в них при X=X3 для случая равноотстоящих узлов

значения разделенных разностей придем к выражениям для производных:

  ,

 F(X3}=(-2F0+9F1-18F2+11F3)/(6h)

  ,,

 F(X3)=(-F0+4F1-5F2+2F3)/h^2

  ,,,

 F(X3)=(-F0+3F1-3F2+f3)/h^3

Подставляя полученные значения в разложение (2) получим

экстраполяционную формулу Адамса

   Y4=Y3 + (55F3-59F2+37F1-9F0)(h/24)

Для уменьшения погрешности аналогичным способом по узлам

X1,X2,X3,X4 строится формула коррекции

   Y4=Y3+ (9F4+19F3-5F2+F1)(h/24)

Алгоритм работает по схеме

 ПРОГНОЗ-РАСЧЕТ F(x,Y)-КОРРЕКЦИЯ-РАСЧЕТ F(X,Y)-СЛЕД ШАГ

m574 test;

double n,h;

void rk4(double x,double *y)

{

  double h1=0,//Варьируемый шаг

  *y0,//у0 и у1 - временные массивы

  *y1,

  *f;//Хранит результат getvalue()

  y0=new double[n];//Выделяем память

  y1=new double[n];

  f=new double[n];

  for(int i=0;i<n;i++)

    y0[i]=y1[i]=y[i];//Начальная инициализация

  //Для 4-х слагаемых формулы Р-К 4 порядка

  for(int j=0;j<4;j++)

  {

    test.getvalue(x+h1,y,f);

    if(j==2) h1=h;

    else     h1=h/2;

    for(i=0;i<n;i++)

    {

      double q=h1*f[i];

      y[i]=y0[i]+q;

      if(j==1) q*=2;

      y1[i]+=q/3.0;

    }

  }

  for(i=0;i<n;i++)

    y[i]=y1[i];//Заносим окончательную коррекцию

  delete[] y0;

  delete[] y1;

  delete[] f;

}

void main()

{

  double *y1,//Массив прогнозируемых решений

         *d[5];//Массив производных при заданных х и у

  n=test.getrange();//порядок уравнения

  for(int i=0;i<5;i++)

    d[i]=new double[n];//Выделяем память под массивы производных

  y1=new double[n];//Выделяем память под массив прогнозируемых

                    //решений

  double *y=new double[n];//Выделяем память под массив решений

  double xmin=test.getmin();

  double xmax=test.getmax();

  double x=xmin;

  h=(xmax-xmin)/100;//определяем шаг

  for(i=0;i<n;i++)//заполняем массив решений начальными условиями

    y[i]=test.getstart(i);

  test.getvalue(x,y,d[0]);   //Производные при Начальных Условиях

                             //->в d[0]

  for(int k=1;k<4;k++)//вычисляем 3 точки вызовом Рунге-Кутта

  {

    rk4(x,y);         //Вызов Рунге-Кутта, результат ->в массив

                     //решений

    x+=h;

    cout<<x<<"  "<<y[0]<<"  "<<y[1]<<endl;

      test.getvalue(x,y,d[k]); //Произв при х,у -> в d[k]

  }

  do//пока решение не найдено

  {

  for(i=0;i<n;i++)                //Прогноз -> в у1

    y1[i]=y[i]+(h/24)*(55*d[3][i]-59*d[2][i]+37*d[1][i]-9*d[0][i]);

    x+=h;//увеличиваем текущий х на шаг

    test.getvalue(x,y1,d[4]); //Производные при прогнозе у1 в d[4],

                             //поскольку она учавствует в формуле

                               //коррекции

    for(i=0;i<n;i++)

    {                               //Коррекция ->в у

      y[i]+=(h/24)*(9*d[4][i]+19*d[3][i]-5*d[2][i]+d[1][i]);

      for(k=0;k<3;k++)//Сдвигаем на 1 точку к началу

 d[k][i]=d[k+1][i];

    }

    cout<<x<<"  "<<y[0]<<"  "<<y[1]<<endl;

    test.getvalue(x,y,d[3]);   // Производные при у в d[3];

  }while(!(((x+h)>=xmax)==(h>0)));

  for(i=0;i<5;i++)

    delete[] d[i];

  delete [] y1;

  delete [] y;

}


Метод конечных разностей
Метод конечных разностей для линейных граничных задач

                  МЕТОД КОНЕЧНЫХ  РАЗНОСТЕЙ

                 ДЛЯ ЛИНЕЙНЫХ И ГРАНИЧНЫХ ЗАДАЧ

    Пусть задано обыкновенное дифференциальное уравнение порядка р

или эквивалентная ему система из р уравнений первого порядка.

Необходимо найти решение уравнения y(x) на интервале [x0,x1],

удовлетворяющее граничным условиям функциональной связи между

искомой функцией и ее производными,

                                        (p-1)

                   fk(y(xk),y'(xk),...,y     (xk))=0 .        (1)

    Для существования и единственности решения граничной задачи

необходимо, чтобы кол-во условий (1) k совпадало с порядком

заданного  дифференциального уравнения р [1]. Следовательно,

граничную задачу можно поставить для уравнения, порядок которого не

ниже второго.

    Условия (1) могут быть заданы в любой из точек интервала

[x0,xn] и, в частности, на границах интервала.

    Рассмотрим в качестве примера грничную задачу для линейного

дифференциального уравнения второго порядка с переменными коэффици-

ентами

             y''+p(x)y'+ q(x)y=r(x).                           (2)

Граничные условия зададим в виде линейной комбинации искомой

функции

y(x) и ее первой производной y'(x) в точках х0 и хn

             g1y(x0)+g2y'(x0)=g3,

             g4y(xn)+g5y'(xn)=g6.                               (3)

    На первом этапе решения задачи составляются конечно-разностные

схемы уравнения (2) и граничных условий (3),в которых производные

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

    Интервал [х0,хn]разобьем на n -1 часть с шагом h=(хn-х0)/n.

Иском функцию у(х) в окрестности узла хi представим в виде

разложения в ряд Тейлора

                                2

             у(х)=уi+(х-х1)у'(xi )y''(xi)/2+...,

(4)

      где yi=y(x).

     B точках х(i+1) и x(i-1), отстоящих на расстоянии h от узла

xi,

пользуясь разложением (4), получим следующие значения ф-ции y(x):

                         2

      y(i+1)=yi+hy'(xi)+h y''(xi)/2+... ,

                         2

      y(i-1)=yi-hy'(xi)+h y''(xi)/2+...

     Пpиближенные значения для пеpвой и втоpой поизводных функции

y(x) в точке xi получим путем вычитания и сложения левых и

пpавых частей последних выpажений

                                    2

    y'(xi)=(y(i+1)-y(i-1))/(2*h)+O(h ),

                                    2     2

    y''(xi)=(y(i+1)-2y(i)+y(i-1))/(h )+O(h ).    (5)

    Заменяя пpоизводные в (2) их пpиближенными значениями,

получим конечно-pазностную схему этого уpавнения

      y(i+1)-2y(i)+y(i-1)     y(i+1)-y(i-1)

     -------------------- +pi-------------- +qiyi=ri,   (6)

             2                    2h

           h

  где

     pi=p(xi),   qi=q(xi),  ri=r(xi),  i=1,...,n.

   Аналогичным обpазом заменим гpаничные условия (3) и

конечно-pазностными пpедставлениями

            y1-y(-1)

   g1y0 + g2-------- = g3,

               2h

            y(n+1)-y(n-1)

  g4yn + g5 ------------- = g6.          (7)

              2h

    Соотношения (6) и (7) пpедставляют собой систему

(n+3) линейных алгебpаических уpавнений относительно неизвестных

y(-1),y0,...,y(n+1). Матpицы этой системы пpиводятся к

тpехдиагональному виду, и тогда систему можно pешить методом

пpогонки.

    Из пеpвого уpавнения (7) выpазим неизвестное y(-1) и подставим

его в конечноpазностное уpавнение (6) пpи  i=0, в pезультате

линейное соотношение

                y0=k0-l0y1,              (8)

где           2

             h r1g2 + h(2-hp1)g3

         k0=-----------------------,

              2

            (h q1-2)g2 + h(2-hp1)g1

                     2g2

        l0=----------------------.       (9)

             2

           (h q1-2)q2+h(2-hp1)q1

МЕТОДИЧЕСКАЯ ПОМОЩЬ

 class m5x1

 {

  double min, max, grusl[6];//минимум максимум и массипв гpаничных

условий

  public:

    m5x1()

    {

       min=1;max=1.6;grusl[0]=3;grusl[1]=0.5;

       grusl[2]=2.075678;grusl[3]=2;

       grusl[4]=0.7;grusl[5]=0.5118773;

    }

    double getmin() {return min;}

    double getgrusl(int posit)//получение гpаничных условий по

номеpу

    {

      if(posit>6||posit<1)

      {

 cout<<"Неверные параметры \n";

 return(MAXDOUBLE);

      }

      return grusl[posit-1];

    }

    double getmax(){return max;}

    double getrange(){return 2;}

    void getvalue(double x,double *pqr)

    {

      if(x<min||x>max)

      {//возвpащает значение функции Бесселя

 cout<<"Выход за предела диапозона \n";

 return;

      }

      pqr[0]=1.0/x;

      pqr[1]=1;

      pqr[2]=0;

    }

 };

 //РЕАЛИЗАЦИЯ

  double *mtr;

  int m;  //размер=m*m

  double scalp(int v1,int v2)

  {

    double sum=0;

    for(int j=0;j<m;j++)

      sum+=mtr[v1*m+j]*mtr[v2*m+j];

      return sum;

  }

  void norm(int v)

  {

    double sum=sqrt(scalp(v,v));

    for(int i=0;i<m;i++)

      mtr[v*m+i]/=sum;

  }

  void orto(int v)

  {

    double p;

    int i,j;

    for(i=0;i<v;i++)

    {

      p=scalp(v,i);

      for(j=0;j<m;j++)

 mtr[v*m+j]-=p*mtr[i*m+j];

    }

  }

  void main(void)

  {

    m5x1 test;

    int r=test.getrange();//поpядок системы диффеpенциальных

уpавнений

    int n=10; //число разбиений

    m=n+r; //число уравнений в оpтагонализиpуемой ситеме

    double *g=new double[r*(r+1)];  //массив для граничных условий

    double min=test.getmin(),max=test.getmax();

    double h=(max-min)/n; //шаг

    double *pqr=new double[r+1]; //коэффициенты

    //  ЗАПОЛНИМ МАССИВ ГРАНИЧНЫХ УСЛОВИЙ

    for(int i=0;i<r*(r+1);i++)

      g[i]=test.getgrusl(i+1);

     m++; //увеличим размерность для метода ортогонализации

     mtr=new double[m*m];

      // выделим память для решения СЛАУ

    // сформируем матрицу

    for(i=0;i<m*m;mtr[i++]=0); // очистка

    mtr[m*m-1]=1; // 1 в последний элемент матрицы

    // Заполним первую и предпоследнюю строки

    mtr[0]=-g[1]/(2*h);

    mtr[2]=-mtr[0];

    mtr[1]=g[0];

    // всего строк: m :от 0 до m-1, m-2 - предпоследняя

    // последний столбец - с индексом m-1

    mtr[m-1]=-g[2];

    mtr[(m-2)*m+(m-1)]=-g[5];

    mtr[(m-2)*m+(m-2)]=g[4]/(2*h);

    mtr[(m-2)*m+(m-4)]=-g[4]/(2*h);

    mtr[(m-2)*m+(m-3)]=g[3];

    delete[] g;

    //Далее матрицу заполняем со строки 1 по m-3 включительно

    for(i=1;i<=m-3;i++)

    {

     test.getvalue (min+(i-1)*h,pqr);

     mtr[i*m+(i-1)]=1/(h*h)-pqr[0]/(2*h);

     mtr[i*m+i]=pqr[1]-2/(h*h);

     mtr[i*m+(i+1)]=1/(h*h)+pqr[0]/(2*h);

     mtr[i*m+(m-1)]=-pqr[2];

    }

    cout<<"Трехдиагональная матрица\n";

    for(i=0;i<m;i++)

    {

      for(int j=0;j<m;j++)

 cout<<mtr[i*m+j]<<" ";

      cout<<endl;

    }

    delete[] pqr;

    norm(0);

    //pешаем систему

    for(i=1;i<m;i++)

    {

      norm(i);

      orto(i);

    }

    /*

    cout<<"Матрица2\n";

    for(i=0;i<m;i++)

    {

      for(int j=0;j<m;j++)

 cout<<mtr[i*m+j]<<" ";

      cout<<endl;

      getch();

    } */

    cout<<"Solution\n";

  for(i=0;i<m-1;i++)

    cout<<"X="<<min+(i-1)*h<<"

Y="<<mtr[(m-1)*m+i]/mtr[m*m-1]<<endl;

  delete[] mtr;

  }


Метод стрельбы
Метод стрельбы для граничных задач

 МЕТОД СТРЕЛЬБЫ ДЛЯ ГРАНИЧНЫХ ЗАДАЧ

Метод стрельбы рассмотрим на примере граничной задачи для

системы обыкновенных дифференциальных уравнений второго

порядка

    dy1(x)

    ----- =f1(x,y1,y2),

     dx

                                            (14)

    dy2(x)

    ----- =f2(x,y1,y2).

     dx

    Необходимо найти решение системы уравнений (14) на

интервале [x0,xn], удовлетворяющее граничным условиям

   j (x0,y1(x0),y2(x0))=0,

   g (xn,y1(xn),y2(xn))=0.                   (15)

    Сущность метода стрельбы заключается в сведении

решения граничной задачи к маногократному решению задачи

Коши для заданной системы дифференциальных уравнений.

    Предположим, что

    y1(x0)= a,                               (16)

где а -произвольное число, которое можно задать, используя

априорную информацию о характере решения y1(x). Подставим

предпологаемое значение l в первое граничное условие (15)

    j (x0,a ,y2(x0))=0.                       (17)

  Теперь последнее соотношение является уравнением

относительно одного неизвестного y2(x). Для простых функ-

ций j  удается записать аналитическое решение указанного

уравнения. В общем случае решение уравнения (17) находит-

ся одним из численных методов. В результате аналитического

или численного решения уравнения (17) получим

     y2(x0,a )= b.                            (18)

   Таким образом, сформулированна задача Коши для системы

дифференциальных уравнений (14) с начальными условиями

(16) и (18) в точке x0. Решение задачи Коши можно провести

одним из известных методов решения для подобных задач,

и получить с необходимой точностью значения функций

y1(x) и y2(x) в точке x=xn:y1(xn,a ) и y2(xn,a ). Последние

результаты поставим во второе граничное условие (15)

       (xn,y1(xn,a ),y2(xn, a))=0,            (19)

которое не будет выполняться, так как число a  выбрано

нами произвольным.

   Соотношение (19) можно рассматривать как уравнение

относительно переменной  . Значение  a=a *, являющееся

корнем этого уравнения, удовлетворяет каждому граничному

условию (15). Следовательно, решениями поставленной

граничной задачи  будут функции y1(x,a *) и y2(x, a*),

определенные на интервале [x0,xn] для задачи Коши.

   Решение уравнения (19) трбует большого объема вычесле-

ний, так на каждой итерации необходимо осуществлять

интегрирование задачи Коши для системы уравнений (14).

Каждая итерация порождает для функций y1(x,a ) и y2(x,a )

траектории, которые в зависимости от параметра a  либо

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

уравнения (19) в нуль. Отсюда  и происходит название

метода стрельбы.

МЕТОДИЧЕСКАЯ ПОМОЩЬ

class m5x2

{

  double min,max,grusl[6];

  public:

  m5x2()

  {//заполняем гpаничные условия

    min=1;max=1.6;grusl[0]=3;

    grusl[1]=0.5;grusl[2]=2.075678;

    grusl[3]=2;grusl[4]=0.7;grusl[5]=0.5118773;

  }

  double getmin(){return min;}

  double getmax(){return max;}

  double getrange(){return 2;}

  double getgrusl(int posit)

  {

    if(posit>6||posit<1)

    {

      cout<<"Неверные параметры\n";

      return MAXDOUBLE;

    }

    return grusl[posit-1];

  }

  void getvalue(double &x,double &p,double &q,double &r)

  {    p=1/x;q=1;r=0;  }

  void getderive(double x,double *y,double *f)

  {//вычисляем пpоизводную

    double p,q,r;

    f[0]=y[1];

    getvalue(x,p,q,r);

    f[1]=r-p*y[1]-q*y[0];

  }

};

double h,max,min,r,*g,n;

m5x2 test;

//одношаговый интегpатоp Рунге-Кутты

void rk21(int n,double &x,double*y)

{

  double f0[2],f[2];

  test.getderive(x,y,f0);

  for(int i=0;i<n;i++)

    y[i]=y[i]+h*f0[i];

  x=x+h;

  test.getderive(x,y,f0);

  for(i=0;i<n;i++)

    y[i]=y[i]+(h/2)*(f[i]-f0[i]);

}

double psi(int l,double a)

{//интегpатоp по числу pазбиений с выводом pезультата

  int m;

  double x;

  double y[2];

  y[0]=a;

  y[1]=(g[2]-g[0]*a)/g[1];

  x=min;

  if(l==1)

    cout<<x<<" "<<a<<" "<<y[1]<<endl;

  for(m=0;m<n;m++)

  {

    rk21(r,x,y);

    if(l==1)

      cout<<x<<" "<<y[0]<<" "<<y[1]<<endl;

  }

  if(l==0)

    return g[3]*y[0]+g[4]*y[1]-g[5];

}

void main(void)

{

//  pqr=new double[test.getrange()+1];

  cout<<endl;

  r=test.getrange();

  n=10;//поpядок системы и число pазбиений

  g=new double[r*(r+1)];

//  y=new double[r];

  min=test.getmin();

  max=test.getmax();

  h=(max-min)/n;

  for(int i=0;i<r*(r+1);i++)

    g[i]=test.getgrusl(i+1);

  double t=psi(0,max);//тpи pаза pешаем задачу Коши

  double k=psi(0,min);

  psi(1,max-(max-min)/(t-k)*t);

  //delete[]y;

  delete[]g;

  //delete[]pqr;

}


Граничные задачи на собственные значения
Граничные задачи на собственные значения для ОДУ

  Извините, но этот урок не был подготовлен


Граничные задачи в частных производных
Граничная задача для дифуравнений в частных производных

  ГРАНИЧНАЯ ЗАДАЧА ДЛЯ ДИФФЕРЕНЦИАЛЬНОГО УРАВНЕНИЯ

             В ЧАСТНЫХ ПРОИЗВОДНЫХ

   Дифференциальные уравнения в частных производных применяются для математического моделирования более широкого круга задач, чем обыкновенные дифферен- циальные уравнения.    Для представления производных, входящих в уравнеие и граничное условие, в области изменения переменных G вводят некоторую сетку. В двухмерных областях G наиболее часто изпользуют прямоугольную сетку, раз бивая область G вдоль координаты x на M частей, а вдоль координаты y - на N частей. Приближенные зна чения производных в каждом узле выбранной сетки записывают, используя информацию о значениях искомой функции f(x,y) в соседних узлах. Конфигурацию распо ложения информационных узлов называют шаблоном дан ной разностной схемы. Один из простейших шаблонов на прямоугольной сетке - шаблон типа "крест".    Составление разностных схем на заданном шаблоне осуществляется тремя способами : методом разностной аппроксимации, интегроинтерполяционным методом и методом неопределенных коэффициентов.    Разностную схему уравнения Лапласа запишем с помощью первого из названных методов, который заключается в замене производной в каждом узле (i,j) разностным выражением, включающим только узлы шаблона. Представим вторые частные производ ные на шаблоне типа "крест" в виде

    2

   d f                                 2    2

   ---- = (f(i+1,j)-2f(i,j)+f(i-1,j))/h +0(h ) ,

     2

   dx

    2

   d x                                2     2

   --- = (f(i,j+1)-2f(i,j)+f(i,j-1))/l  +0(l ),

     2

   dy

где h и l-шаги сетки по координатам x,y;

f(i,j)=f(x(i),y(j)).

   После подстановки конечно-разностных представлений производных в уравнение Лапласа

    2     2

   d f   d f

   --- + --- = 0

     2     2

   dx    dy

получим СЛАУ относительно искомых значений потенциала f(i,j),в узлах.Вследствие большой размерности полученная система уравнений совместно с граничными условмями решается итерационным методом Зейделя.Для осуществления итерационного процесса конечно-разностную схему уравнения Лапласа представляем в виде

f(i,j)=[f(i+1,j+f(i-1,j)+t(f(i,j+1)+f(i,j-1))]/

 /(2+2t)                       (1)

Где    2  2

    t=h /l

Для квадратной сетки t=1 формула (1) упрощается, и ее знаменатель будет равен 4, в этом случае итеррационное соотношение называют формулой "4".    Итеррационный процесс по формуле (1) завершается при выполнении в каждом узле сетки условия

            k+1       k

    |(f(i,y)   -f(i,y) )|< e,         (2)

где e-заданная относительная погрешность вычисленияпотенциала.

МЕТОДИЧЕСКАЯ ПОМОЩЬ

class m5x3

{

  double a,b,u,m,n;

  //pазмеpы области кол-во pазбиений и значение потенциала

  public:

    m5x3()

    {

      a=4,b=4,u=10,m=n=16;

    }

  double getrange(){return 2;}

  double getsize(int koord)//размеры

  {

    switch(koord)

    {

      case 0:return a;

      case 1:return b;

    }

  }

  double getpotencial(){return u;}

  double getrazb(int koord)

  {

    switch(koord)

    {

      case 0: return m;

      case 1: return n;

    }

  }

};

void main(void)

{

  m5x3 test;

  double **mtr;

  int r=test.getrange(),j,k;

  double*mn=new double[r],

 *ab=new double[r],

 *hl=new double[r],

 e=0.00000001;

  double p,q,x,y,s,r1,z;

  for(int i=0;i<r;i++)

  {

    mn[i]=test.getrazb(i);

    ab[i]=test.getsize(i);

    hl[i]=ab[i]/mn[i];

  }

  double t=pow(hl[0]/hl[1],2);

  mtr=new double*[mn[0]+1];

  for(i=0;i<mn[0]+1;i++)

    mtr[i]=new double[mn[1]+1];

    //аналитическое pешение

  p=M_PI/ab[0];

  q=-2*ab[1];

  z=4*test.getpotencial()/M_PI;

  for(i=1;i<mn[0]/2+1;i++)

  {

    x=i*hl[0];

    for(j=1;j<mn[1];j++)

    {

      y=j*hl[1];

      s=0;

      k=1;

      do

      {

 r1=k*p;

 double temp=r1;

 double a=sin(r1*x);

 double c=(exp(r1*(y-ab[1]))-exp(-r1*(y+ab[1])));

 double d=(k*(1-exp(r1*q)));

 double b=c/d;

 r1=a*b;

     //

r1=sin(r1*x)*(exp(r1*(y-ab[1]))-exp(-r1*(y+ab[1])))/(k*(1-exp(r1*q)

));

 s+=r1;

 k+=2;

      }

      while(fabs(r1/s)>=e);

      mtr[i][j]=z*s;

    }

  }

  /*//pазностное pешение

  for(i=0;i<mn[0]+1;i++)

    for(j=0;j<mn[1]+1;j++)

      if(j==mn[1])

 mtr[i][j]=test.getpotencial();

      else

 mtr[i][j]=0;

   do

   {

     k=0;

     for(i=1;i<mn[0];i++)

       for(j=1;j<mn[1];j++)

       {

  r=(mtr[i-1][j]+mtr[i+1][j]+

    (mtr[i][j-1]+mtr[i][j+1])*t)/(2+2*t);

  if(fabs(r-mtr[i][j])>e*fabs(r))

  k=1;

  mtr[i][j]=r;

       }

   }

   while(k);

   */

   delete[]ab;

   delete[]hl;

   //вывод pезультата

   for(i=1;i<int(mn[0])/2+1;i++)

     for(j=1;j<mn[1];j++)

       cout<<"["<<i<<"]["<<j<<"]="<<mtr[i][j]<<endl;

   for(i=0;i<mn[0]+1;i++)

     delete[] mtr[i];

     delete[] mtr;

     delete[] mn;

}


Динамическое программирование

  /*Метод динамического программирования

   Динамическое программирование (иначе"динамическое планирова-

ние")   есть   особый  метод  оптимизации  решений,  специально

преспособленный к так называемым "многошаговым" ( или  "многоэ-

тапным") операциям.

   Представим себе некоторую операцию Q ,распадающуюся  на  ряд

последовательных "шагов"  или "этапов" ,  - например,  деятель-

ность отрасли промышленности в течение ряда  хозяйственных  лет

или  же преодоление группой самолетов нескольких полос противо-

воздушной обороны;или же последовательность тестов ,  применяе-

мых при контроле аппаратуры. Некоторые операции (подобно вышеп-

риведенным) расчленяются на шаги естественно ; в некоторых чле-

нение приходится вводить искусственно-скажем, процесс наведения

ракеты на цель можно условно разбить на этапы,  каждый из кото-

рых занимает какое-то время t.

   Итак,рассмотрим операцию Q ,  состоящую из m шагов (этапов).

Пусть эффективность операции характеризуется каким-то показате-

лем W,  которыймы будем называть "выигрышем".  Предположим, что

выигрыш  W за всю операцию складывается из выигрышей на отдель-

ных шагах:

                  m

                 \--

              W=  >  wi,    (1)

                 /__

                 i=1

где wi-выигрыш на i-ом шаге.

    Если W обладает таким свойством,то его называют "аддитивным

критерием". Операция Q ,о которой идет речь, представляет собой

управляемый процесс,  т.е.мы можем выбирать какие-то  параметры

,влияющие  на его тход и исход,причем на каждом шаге выбирается

какое-то решение ,от которого зависит выигрыш на данном шаге  и

выигрыш за операцию в целом.  Будем называть это решение "шаго-

вым  управлением".   Совокупность   всех   шаговых   управлений

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

буквой x, а шаговые управления буквами

 x1,x2,...,xm:

               x=(x1,x2,...,xm). (2)

 Следует иметь в виду ,что x1,x2,...,xm в общем случае-не числа

,  а, может быть, векторы, функции и т.д. Требуется найти такое

управление x ,при котором выигрыш W обращается в максимум:

                              m

                             \--

                       W=     > wi ==> max.  (3)

                             /__

                             i=1

 То управление x* ,при котором этот максимум достигается, будем

называть о п т и м а л ь н ы м у п р а в л е н и е м Оно состо-

ит из совокупности оптимальных шаговых управлений:

                    x*=(x1*,x2*,...,xm*). (4)

 Тот максимальный выигрыш,  который достигается при этом управ-

лении,мы будем обозначать W*:

                   W*=max{W(x)}.            (5)

                x

 Формула (5)читается  так  :  величина W* есть максимум из всех

W(x) при разных управлениях x (максимум берется по всем  управ-

лениям  х  ,возможным  в  данных условиях).Иногда это последнее

оговаривается в формуле и пишут:

                    W*=max{W(x)},(5')

где х принадлежит множеству Х.

                 *             *            *

                            Задача

   Планируется деятельность   группы  промышленных  предприятий

П1,П2,...,Пк на период m  хозяйственных  лет  (m-летку).Вначале

периода на развитие группы выделены какие-то средства М,  кото-

рые должны быть как-то распределены между предприятиями.В  про-

цессе  работы  предприятия  вложенные  в него средства частично

расходуются (амортизируются),  а частично сохраняются  и  снова

могут  быть  перераспределены .  Каждое предприятие за год при-

носит доход,зависящий от того,сколько  средств  в  него  вложе-

но.Вна  чале  каждого  хозяйственного  года,имеющиеся в наличии

средства перераспределяются между предприятиями.  Ставится воп-

рос: какое количество средств в начале каждого года нужно выде-

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

максимальным?  Выигрыш  W  (суммарный  доход)представляет собой

сумму доходов на отдельных шагах(годах) :

              __

              \

      W    =   >  wi,       (1.6)

                             \

              /

              --

и,значит,обладает свойстврм аддитивности.

 Управление xi на i-м шаге состоит в том,что вн начале i-го го-

да предприятиям выдаляются  какие-то  средства  xi1,xi2,...,xik

(первый индекс-номер шага,второй-номер предприятия).Таким обра-

зом,шаговое управление есть вектор с k составляющими:

     xi=(xi1,xi2,...,xik).   (1.7)

 Разумеется,величены wi  в  формуле(12.6) зависят от количества

вложенных в предприятие  средств.Управление  x  всей  операцией

состоит из совокупности всех шаговых управлений:

      x=(x1,x2,..,xm).  (8)

Требуется найти  такое  распределение средств по предприятиям и

по годам (оптимальное управление x*),при котором величена W об-

ращается в максимум.

  Любую многошаговоезадачу можно решить по-разному:либо  искать

сразу все элементы решения на всех m шагах ,либо же строить оп-

тимальное управление шаг за шагом,на каждом этапе расчета опти-

мизируя только один шаг.Обычно второй способ оптимизации оказы-

вается проще,чем первый,особенно при большом числе шагов. Такая

идея  постепенной,пошаговой оптимизации и лежит в основе метода

динамического  программирования.Оптимизация  одного  шага,  как

правило,   пороще   оптимизации  всего  процесса:лучше,оказыва-

ется,много раз решить сравнительно простую задачу,чем один  раз

сложную.

  С первого взгляда идея может показаться довольно тривиальной.

В самом деле,чего казалось бы , проще : если трудно оптимизиро-

вать операцию в целом,разбить ее на ряд шагов.Каждый такой  шаг

будет  отдельной,маленькой операцией,оптимизировать которую уже

нетруд но.Надо выбрать на этом шаге такое управление,чтобы  эф-

фективность  этого шага была максимальна.Не так ли ?  Нет,вовсе

не так ! Принцип динамического программирования отнюдь не пред-

полагает,что  каждый  шаг оптимизируется отдельно,независимо от

других.Напротив,шаговое управление должно выбираться дальновид-

но,с учетом всехего последствий в будущем.Что толку,если мы вы-

берем на данном шаге управление,при котором эффективность этого

шага максимальна,если этот шаг лишит нас возможности хорошо вы-

играть на последующих шагах ?Значит,планируя многошаговую  опе-

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

будущих последствий на еще предстоящих шагах. Управление на i-м

шаге  выбирается не так,чтобы выигрыш именно на данном шаге был

максимален,а так,чтобы была максимальна сумма выигрышей на всех

оставшихся до конца шагах плюс данный.

  Однако из этого правила есть исключение.Среди всех шагов есть

один ,который может планироваться попросту,без оглядки на буду-

щее.Какой это шаг?

  Очевидно, последний!Этот  шаг,единственный  из  всех ,который

может принести наибольшую выгоду при удачном планировании.

   Поэтому процесс динамич.программирования обычно разворачива-

ется от конца к началу:прежде всего плвнируется последний , m-й

шаг.

  Вот тут-то и начинается самое главное .Планируя каждый шаг  ,

нужно   сделать   разные  предположения  о  том  ,чем  кончился

прдпоследний ,(m-1) -й шаг,и для каждого из этих  предположений

найти условное оптимальное управление на m-м шаге("условное"по-

тому,  что оно выбирается исходя из условия ,что  предпоследний

шаг кончился так-то и так-то).

  Предположим ,что мы это сделали,и для  каждого  из  возможных

исходов предпоследнего шага знаем условное оптимальное управле-

ние и соответствующий ему условный оптимальный выигрыш  на  m-м

шаге.Отлично!Теперь  мы можем оптимизировать управление на пред

последнем,(m-1)-м шаге.Снова сделаем все возможные  предположе-

ния  о том,чем кончился предыдущий (m-2)-й шаг,и для каждого из

этих предположений найдем такое управление  на(m-1)-м  шаге,при

котором выигрыш за последниедва шага(из которых m-й уже оптими-

зирован!)максимален.Так мы найдем для каждого  исхода  (m-2)-го

шага  условное оптимальное управление на(m-1)-м шаге и условный

оптимальный выигрыш на двух последних шагах.Далее ,"пятясь  на-

зад",оптимизируем управление на(m-2)-м шаге и т.д.,пока не дой-

дем до первого.

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

ные оптимальные выигрыши за весь "хвост "процесса(на всех шагах

,начиная  от данного и до конца)нам известны.Это значитЖмы зна-

ем,что надо делать,как управлять на данном шаге и что мы  полу-

чим  на  "хвосте",в  каком бы состоянии ни был процесс к началу

шага.Теперь мы можем построить  уже  не  условно  оптимальное,а

просто оптимальное управление х* и найти не условно оптимальный

выигрыш W*.

    В самом деле ,  пусть мы знаем ,  в каком состоянии S0 была

управляемая система (объект управления S) в начале первого  ша-

га.  Тогда мы можем выбрать оптимальное управ ление x1* на пер-

вом шаге. Применив его, мы изменим состояние системы на некото-

рое  новое  S1*;  в  этом состоянии мы подошли ко второму шагу.

Тогда нам тоже известно условное оптимальное управление  x2*  ,

которое  к концу второго шага переводит систему в состояние S2*

,  и т.д. Что касается оптимального выигрыша W* за всю операцию

,то  он  нам  уже известен:ведь именно на основе его максималь-

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

процессе  оптимизации управления методом динамического програм-

мирования многошаговый процесс "проходится" дважды : первый раз

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

мальные управления и условные оптимальные  выигрыши  за  остав-

шийся "хвост" процесса;  второй раз - от начала к концу , когда

нам остается только "прочитать" уже готовые рекомендации и най-

ти безусловное оптимальное упарвление x*,состоящее из оптималь-

ных управлений x1*,x2*,...,xm* .

                      *       *      *

   А теперь   сформулируем  несколько  практических  рекоменда-

ций,полезных начинающему при постановке  задач  динамич.  прог-

рам.Эту постановку удобно проводить в следующем порядке.

 1. Выбрать  параметры   (фазовые   координаты),характеризующие

    состояние S управляемой системы перед каждым шагом.

 2. Расчленить операцию на этапы.

 3. Выяснить  набор  тшаговых  управлений хi для каждого шага и

    налогаемые на них ограничения.

 4. Определить  ,какой выигрыш приносит на i-ом шаге управление

    хi,если перед этим система была в состоянии  S,т.е.записать

    "функции выигрыша":

                 wi=fi(S,xi). (2.1)

 5. Определить  ,как изменяется состояние S системы S под влия-

    нием управления хi  на  i-ом  шаге:оно  переходит  в  новое

    состояние

                 S'=yi(S,xi)  (2.2)

    "Функции изменения  состояния"  (2.2)  тоже должны быть за-

    писаны (заметим, что аргументы функций (2.1), (2.2) в общем

    случае-не числа , а совокупности чисел (векторы)).

 6. Записать  основное  рекуррентное  уравнение   динамического

    программирования,выражающее условный   оптимальный  выигрыш

    Wi(S)(начиная с i-го шага и до конца )через  уже  известную

    функцию Wi+1(S) :

      Wi(S)=max{fi(S,xi)+Wi+1(yi(S),xi)} (2,3)

             xi

    Этому выигрышу соответствует условное оптимальное  управле-

    ние  на  i-ом  шаге  хi(S)(подчеркнем , что в уже известную

    функцию Wi+1(S) надо вместо S поставить измененное  состоя-

    ние S'=yi(S,xi)).

 7. Произвести условную оптимизацию последнего  (m-го)  шага  ,

    задаваясь гаммой состояний S,  из которых можно за один шаг

    дойти до конечного состояния ,  вычисляя для каждого из них

    условный оптимальный выигрыш по формуле

                Wm(S)=max{fm(S,xm)}       (2.4)

                      xm

    и находя условное оптимальное управление хm(S) ,  для кото-

    рого этот максимум достигается.

 8. Произвести условную оптимизацию (m-1)-го ,  (m-2)-го и т.д.

    шагов по формуле (2.3),  полагая в ней i=(m-1), (m-2),.., и

    для каждого из шагов указать условное оптимальное  управле-

    ние xi(S),  при котором максимум достигается.  Заметим ,что

    если состояние системы в начальный момент известно  (а  это

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

    истемы не нужно-прямо находим оптимальный выигрыш для  дан-

    ного начального состояния S0.Это и есть оптимальный выигрыш

    за всю операцию       W*=W1(S0).

 9. Произвести  безусловную оптимизацию управления,"читая"соот-

    ветствующие рекомендации на каждом шаге.Взять найденное оп-

    тимальное  управление  на  первом  шаге x1*=x1(S0);изменить

    состояние системы по  формуле  (2.2);для  вновь  найденного

    состояния найти оптимальное управление на втором шаге х2* и

    т.д.до конца.

                      *        *        *

    Сделаем несколько дополнительных замечаний общего  характе-

ра.  До сих пор мы рассматривали только аддитивные задачи дина-

мического программирования,  в которых выигрыш за всю  операцию

равен сумме выигрышей на отдельных шагах. Но метод динамическо-

го программирования применим также и к задачам с так называемым

"мультипликативным" критерием, имеющим вид произведения: m

                W=II wi     (2.5)

                  i=1

(если только выигрыши wi  положительны).  Эти  задачи  решаются

точно  так  же,  как  задачи  с  аддитивным  критерием,  с  той

единственной разницей,  что в основном уравнение  (2.3)  вместо

знака "плюс" ставится знак умножения *:

        Wi(S)=max{fi(S,xi)*Wi+1(yi(S),xi)}      (2,6)

              xi

     В заключение-несколько  слов о так называемых "бесконечно-

шаговых" задачах динамического  программирования.  На  практике

встречаются случаи, когда планировать операцию приходится не на

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

мени,  и нас может интересовать решение задачи оптимального уп-

равления безотносительно к тому,  на каком именно шаге операция

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

честве модели явления  бесконечношаговый  управляемый  процесс,

где не существует "особенного" по сра нению с другими последне-

го шага (все шаги равноправны).Для этого  ,  разумеется,  нужно

,чтобы  функции fi выигрыша и функции yi изменения состояния не

зависели от номера шага.

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

шать многие  экономические задачи.Рассмотрим одну из простейших

задач - задача о распределении  ресурсов.В  нашем  распоряжении

имеется какой-то  запас  средств (ресурсов) К ,  который должен

быть распределен между m предприятиями П1,П2,...,Пm.  Каждое из

предприятий Пi  при вложении в него каких-то средств x приносит

доход ,  зависящий от х ,  т.е.  представляющий собой  какую-то

функцию yi(x).Все  функции  yi(х)  (i=1,2,...,m)заданы (разуме-

ется,эти функции - неубывающие).Спрашивается ,  как нужно расп-

ределить средства К между предприятиями , чтобы в сумме они да-

ли максимальный доход?

/*Задача о распределении ресурсов

  */

#include<stdio.h>

#include<bwcc.h>

#include<iostream.h>

#include"c:\bc\work\m59.h"

// В главной функции необходимо :

void main()

{

 /*1.Определить объэкта класса m59,который размещен в файле m59.h

Поэтому перед написанием главной функции Вам необходимо подклучить

данный файл ,т.е.

 #include"c:\bc\work\m59.h"

*/

m59 ob;

 /*2.Определить какое количество ресурсов имеется у предприятия в

текущий момент ,используя метод класса getresours();

*/ intk=ob.getresours();

/*3.Определить какое количество предприятий будут участвовать в

распределении ресурсов,используя метод класса getNP(); */

int n=ob.getNP(); int i,j,s,l;

/* 4.В дальнейшем Вам потребуются массивы для "выигрышей" и"опти-

мальных управлений" ,так как данные массивы будут являться двумер-

ными(т.е. "выигрыш"[запас средств с которыми мы подош ли к опреде-

ленному шагу][номер предприятия], "оптимальное управление"[запас

средств с которыми мы подошли к определенному шагу][номер предприя-

тия]),то их обьявление и распределение памяти под эти массивы осу-

ществим следующим образом */

double **W,**X; W=new double *[k+1];

for( i=0;i<=k;i++) W[i]=new double [n]; if(!W[i]){

    BWCCMessageBox(0,"Не хватает памяти ","",

                   MB_OK|MB_ICONEXCLAMATION);

      }

for( i=0;i<=k;i++)

for( l=0;l<n;l++)

   W[i][l]=0;

X=new double *[k+1];

for( i=0;i<=k;i++)

X[i]=new double [n];

if(!X[i]){

    BWCCMessageBox(0,"Не хватает памяти ","",

                   MB_OK|MB_ICONEXCLAMATION);

      }

for( i=0;i<=k;i++)

for( l=0;l<n;l++)

X[i][l]=0;

double max;//используется для определения оптимального выигрыша

/*

5.А теперь приступим к непосредственному выполнению самого ал-

горитма вычисления оптимального вложения средств в предприятия

Весь алгоритм делится на два этапа:

а)УСЛОВНАЯ ОПТИМИЗАЦИЯ

*/

/*В начале необходимо определить оптимальнные выигрышы и оптималь-

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

*/

for(s=0;s<=k;s++)

 {

W[s][n-1]=ob.f[s][n-1];

 X[s][n-1]=s;

 }

//Организовываем  цикл по перебору предприятий

for( j=n-2;j>=0;j--)

{

for( s=1;s<=k;s++)

//запас средств с которыми мы подошли к определенному шагу

{

max=0;

for(int x=0;x<=s;x++)//средства вложенные в j-тое предпиятие

{

W[s][j]=ob.f[x][j]+W[s-x][j+1]; //формула для вычисления

/*Надо отметить что функция f класса m59.h возващает доход

получаемый при х вложенных в него средств

//выигрыша при s имеющихся на данном зтапе ресурсов при вло-

//женных в него средств

if(max<=W[s][j]) //оптимизация выигрыша

{max=W[s][j];X[s][j]=x;}

}

W[s][j]=max;

}

}

//б)БЕЗУСЛОВНОЕ ОПТИМАЛЬНОЕ УПРАВЛЕНИЕ**********

int *res;//средства,которые необходимо вложить в каждое

//предприятие ,чтобы в сумме они дали максимальный доход

int sum;

res=new int[n];

if(!res){

    BWCCMessageBox(0,"Не хватает памяти ","",

                   MB_OK|MB_ICONEXCLAMATION);

      }

sum=k;

for( j=0;j<n;j++)//Цикл по перебору кол. предприятий

res[j]=0;

//Непосредственно сам алгоритм безусловной оптимизации

for( j=0;j<n;j++)//Цикл по перебору кол. предприятий

{

res[j]=X[sum][j];

sum=sum-X[sum][j];

if(sum<0){break;}

}

for( j=0;j<n;j++)//кол предпр

cout<<"Результат "<<endl<<"в  "<<j;

cout<<"необходимо вложить - "<<res[j]<<endl;

delete res;

for( i=0;i<k;i++)

{delete W[i];

delete X[i];}

}


Шаблон Windows-приложения для ЧМ
Шаблон программы при составлении Windows-приложений

/*

  Задание :

  создать и вывести на экран единственное главное окно

  Windows-приложения используемое для просмотра произвольных

  текстовых файлов в ANSI -   кодировке . Имя просматриваемого

  файла - в командной строке

*/

#include <owl.h>

#include <fstream.h>

#include <array.h>//Включаемые файлы для контейнерных классов Array

#include <strng.h>//(обычный, не сортируемый) и String

#include <string.h>

/*Все классы для окон должны быть производными от TWindow. Кстати,

класс TDialog, от которого Вы ,будете делать производный в

следующей работе - тоже его наследник*/

class View:public TWindow

{

  Array *arr;//указатель на массив с текстом файла

 public:

  View(PTWindowsObject AParent, LPSTR ATitle, PTModule AModule =

      NULL):TWindow(AParent,ATitle,AModule)

  {

    //В конструкторе добавляем к предопределенным стилям окна

    //стили, отвечающие за полосы прокрутки

    Attr.Style|=WS_HSCROLL|WS_VSCROLL;

    Scroller=new TScroller(this,5,5,0,0);

    /*конструируем объект, управляющий этими полосами при нажатии

кнопки мыши с параметрами : указатель на окно-предок, смещения по х

и по у при одном нажатии мыши и диапазон изменения х и у (пока

нулевой) */

    arr=new Array(1,0,20);//конструируем объект массива в памяти

  };

  ~View(){delete arr;};//освобождаем память из под массива

  virtual LPSTR GetClassName(){return "View";};

  //имя класса окна - нужно Windows

  virtual void Paint(HDC PaintDC, PAINTSTRUCT _FAR & PaintInfo);

  /*Эта функция вызывается, когда Windows просит программу обновить

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

другим окном, изменении размеров etc.*/

  virtual void GetWindowClass(WNDCLASS _FAR & AWndClass);

  //Здесь загружаем иконку и курсор

  virtual void SetupWindow();

  /*Эта функция явлется дополнительным конструктором окна -

она вызывается после основного когда окно уже создано в памяти, но

на экране его еще нет. Объекты оконного интерфейса нужно создавать

в конструкторе окна, но инициализировать их какими-то данными -

только в этой функции!*/

};

void View::SetupWindow()

{

  char buf[100];

  ifstream f=_argv[1];

  //Открываем файл и построчно заносим его в массив

  for(;f.getline(buf,100,'\n');)

    arr->add(*new String(buf));

  Scroller->SetRange(100,arr->getItemsInContainer()*3);

  //Устанавливаем диапазон изменения по х и по у

}

void View::Paint(HDC hdc, PAINTSTRUCT _FAR & )

{

  //Снимите комментарий со следующей строки и медленно прокрутите

  //окно...

  //SetTextColor(hdc,RGB(random(256),random(256),random(256)));

  for(int i=0;i<arr->getItemsInContainer();i++)

    TextOut(hdc,0,i*15,(const char*)((String&)(*arr)[i]),

     strlen((const char*)((String&)(*arr)[i])));

  //В цикле выводим строки файла

}

void View::GetWindowClass(WNDCLASS _FAR & wc)

{

  TWindow::GetWindowClass(wc);

  wc.hIcon=LoadIcon(GetApplication()->hInstance,IDI_APPLICATION);

  wc.hCursor=LoadCursor(GetApplication()->hInstance,IDC_ARROW);

  //Материал для эксперимента

//wc.hbrBackground=(COLOR_BACKGROUND);//+1);//|COLOR_CAPTIONTEXT;

//Выбирай на вкус!

  /*COLOR_ACTIVEBORDER COLOR_HIGHLIGHTTEXT

COLOR_ACTIVECAPTION COLOR_INACTIVEBORDER

COLOR_APPWORKSPACE COLOR_INACTIVECAPTION

COLOR_BACKGROUND

COLOR_INACTIVECAPTIONTEXT

COLOR_BTNFACE COLOR_MENU

COLOR_BTNSHADOW COLOR_MENUTEXT

COLOR_BTNTEXT COLOR_SCROLLBAR

COLOR_CAPTIONTEXT COLOR_WINDOW

COLOR_GRAYTEXT COLOR_WINDOWFRAME

COLOR_HIGHLIGHT COLOR_WINDOWTEXT

    */

}

//Это - класс приложения

class Prog:public TApplication

{

 public:

  Prog(LPSTR AName, HANDLE AnInstance, HANDLE APrevInstance, LPSTR

       ACmdLine, int ACmdShow):

  TApplication(AName,AnInstance,APrevInstance,ACmdLine,ACmdShow){};

  virtual void InitMainWindow()

  {

  //Здесь создается главное окно программы

    MainWindow=new View(0,"Надпись в заголовке окна");

  }

};

//Аналог ф-ии main , но только для Windows

int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR

lpCmdLine, int nCmdShow)

{

  Prog prog("Название программы",hInstance,hPrevInstance,lpCmdLine,

     nCmdShow);

  prog.Run();

  return 0;

}

/*

  Настоящий шаблон Windows-приложения предлагается для

использования в качестве унифицированного образца запуска Ваших

программ и демонстрации результатов их выполнения. С этой целью

организуется диалоговое окно с 2-мя дочерними списковыми окнами,

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

списком строк , содержащих результаты  выполнения

    Это кратчайший образец создания Windows-приложения

*/

//lab50.cpp

#define STRICT

//Для строгой проверки типов Windows-функций

#define APPNAME "MAINDIALOG"

//Произвольное имя приложения,заносимое в ресурс диалога ,например,

//через  Resource Workshop

#include <bwcc.h>

//Для красивых окон и кнопок Borland'a

#include <owl.h>

//Для подключения библиотеки Object Windows

#include "c:\bc\chm\m51.h"

//Файл с описанием класса исследуемого объекта (математическая

//модель)

const

  BUF_LEN=80,//Размер используемых в программе строковых буферов

  REZ_LIST=103,//2 идентификатора списковых окон в ресурсе диалога

  FUNC_LIST=102,

  FUNC_COUNT=100;//Количество ф-ий, которые Вы, м.б., и составите..

  HWND HWind;//Копия дескриптора главного окна,которой Вы будете

                    //пользоваться в своих функциях

//Самое главное окно!

class NashFortochka : public TDialog {

 public:

  NashFortochka() : TDialog(NULL,APPNAME){}

  //Конструктор,загружающий диалог из ресурса

  virtual void WMInitDialog(RTMessage)=[WM_FIRST+WM_INITDIALOG];

                                           //инициализация диалога

  virtual ~NashFortochka(){};                 //Деструктор

  //Возвращает имя класса приложения для Windows

  virtual LPSTR GetClassName(){  return APPNAME;}

  void func_list(RTMessage)=[ID_FIRST+FUNC_LIST];//Ф-я обработки

//сообщений списка

  virtual void Cancel(RTMessage)=[ID_FIRST+IDCANCEL];

                                                //Закрывает главное

//окно программы

  virtual void Ok(RTMessage)=[ID_FIRST+IDOK];//Вызывается при

//нажатии кнопки Ок

  void dblclk(RTMessage);//Обработка двойного щелчка на элементе

//списка

};

void f00();//Ваши функции

void deft();

struct list//Массив структур с указателями на ваши функции и их

//описания

    //в строковой форме(будут в окне списка для понятности

//их назначения)

{

  void (*func)();

  char str[BUF_LEN];

}list[FUNC_COUNT]=

{     //Инициализация структур при подклдючении ваших функций

  f00,"Метод случайного поиска при решении нелинейных уравнений",

  deft,"?????????????????????",

  NULL,""//Сигнал конца списка

};

void f00()//Шаблон вашей функции для работы с нашими классами

{

  SendDlgItemMessage(HWind,REZ_LIST,LB_RESETCONTENT,0,0L);

  //Библ. функция очистки списка

  SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

       "Метод случайного поиска при решении "

    "нелинейных уравнений");//Добавляем строку в список

  //Здесь НЕШаблонный текст Вашей функции

  m516 test;//Создаем объект исследования,для примера взят класс

//m516

  double x,k;

  char buf[BUF_LEN];

  double c=test.getmin();//Получаем границы интервала

//неопределенности

  double d=test.getmax();

  double e=0.000001;//Ашипка (по Вашему выбору)

  //В цикле до достижения заданной ошибки

  while(fabs(d-c)>e)

  {

    k=(long double)rand()/(long double)RAND_MAX;

    //формируем коэффициент деления интервала

    x=c+k*(d-c);//Задаем точку внутри интервала

    if(test.sign(test.getvalue(c))==test.sign(test.getvalue(x)))

    //Переносим в эту точку границу с совпадающим знаком функции

      c=x;

    else

      d=x;

  }

  wsprintf(buf,"ЗНАЧЕНИЕ КОРНЯ : %f",(c+d)/2);

  //Форматируем строку вывода и посылаем ее в список

SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

buf);

}

//Посылаем в список, что хотим - литералами/можно и в цикле

void deft()

{

  SendDlgItemMessage(HWind,REZ_LIST,LB_RESETCONTENT,0,0L);

  SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

  "Я родился в России - родила меня мать.");

  SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

  "Бабке некогда было в то время рожать,");

  SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

  "Тетка тоже ,как назло, в отлучке была - ");

  SendDlgItemMessage(HWind,REZ_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

  "В силу этих причин меня мать родила.");

}

void NashFortochka::Cancel(RTMessage)//- по нажатию Cancel

{

  CloseWindow();//Библиотечная ф-ия закрытия окна

}

void NashFortochka::Ok(RTMessage)//- по нажатию Ок

{MessageBeep(-1);/*можете сочинить что-нибудь свое*/}

void NashFortochka::func_list(RTMessage msg)

{

  if(msg.LP.Hi==LBN_DBLCLK)//если 2 щелчка на элементе списка...

  {

    dblclk(msg);//...вызываем ф-ию обработки -СМ. НИЖЕ

  }

}

void NashFortochka::dblclk(RTMessage)

{

  int index=SendDlgItemMsg(FUNC_LIST,LB_GETCURSEL,0,0L);

  //Получаем текущий выбор (номер строки, начиная с 0)

  SetCaption(list[index].str);//Устанавливаем в заголовок окна

//строку

                              //из массива структур

  (*list[index].func)();//и вызываем соответствующую ф-ию по

//указателю

}

//Виртуальная функция начальной инициализации диалога

void NashFortochka::WMInitDialog(RTMessage )

{

  SendDlgItemMsg(FUNC_LIST,LB_RESETCONTENT,0,0L);//Чистим списки

  SendDlgItemMsg(REZ_LIST,LB_RESETCONTENT,0,0L);

  for(int i=0;lstrlen(list[i].str);i++)

    SendDlgItemMsg(FUNC_LIST,LB_ADDSTRING,0,(LPARAM)(LPCSTR)

                                         list[i].str);

  //Заносим в список строки из массива структур

  SendDlgItemMsg(FUNC_LIST,LB_SETCURSEL,0,0L);

  //Делаем помеченным первый элемент списка

  HWind=HWindow;//Копируем дескриптор главного окна

}

//Класс нашего приложения в Object Windows

class NashKlas : public TApplication {

public:

    NashKlas(LPSTR AName, HINSTANCE hInstance,

    HINSTANCE hPrevInstance, LPSTR lpCmd,

    int nCmdShow)//Вызываем базовый конструктор

         : TApplication(AName, hInstance,

          hPrevInstance, lpCmd, nCmdShow){};

    virtual void InitMainWindow()//Создаем главное окно программы

    {

      MainWindow = new NashFortochka;

    }

};

//Это аналог ф-ии main() в Windows

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

     LPSTR lpCmd, int nCmdShow)

{ //Конструируем объект прикладного класса

  NashKlas TestApp(APPNAME,hInstance,hPrevInstance,lpCmd,nCmdShow);

  TestApp.Run();//Запускаем цикл получения и диспетчеризации

//сообщений

  return TestApp.Status;//Шаблонный возврат

}

Ресурс для этой программы ://lab50.rc

MAINDIALOG DIALOG 8, 18, 262, 181

STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION ""

BEGIN

 LISTBOX 103, 13, 68, 237, 82, LBS_NOTIFY | WS_CHILD |

WS_VISIBLE | WS_BORDER | WS_VSCROLL

 CONTROL "Button", IDOK, "BorBtn", BS_PUSHBUTTON | WS_CHILD

| WS_VISIBLE | WS_TABSTOP, 75, 155, 32, 20

 CONTROL "Button", IDCANCEL, "BorBtn", BS_PUSHBUTTON |

WS_CHILD | WS_VISIBLE | WS_TABSTOP, 159, 155, 32, 20

 LTEXT "Список доступных функций", -1, 78, 1, 97, 8

 LTEXT "Результат выполнени\377", -1, 85, 54, 78, 8

 LISTBOX 102, 14, 16, 235, 33, LBS_NOTIFY | WS_CHILD |

WS_VISIBLE | WS_BORDER | WS_VSCROLL

END

ПРОЕКТ должен содержать файлы :

lab50.cpp

lab50.rc

owl.def

bwcc.lib


M51.H
Заголовочный файл к задачам определения корней m51.h

#include <iostream.h>

#include <values.h>

#include <stdlib.h>

#include <math.h>

#include <bwcc.h>

//отделение корней

class m511

{

  double min,max;

 public:

  m511(){min=-100;max=0;};    //конструктор

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m511::getvalue(double x)

{

  double r,s,e=0.0001;

  int p=2;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                   MB_OK);

    return MAXDOUBLE;

  }

  r=x/2;

  double __t=1.0;

  for(int k=1;k<=p;k++)

    __t*=r/k;

  for(k=1,s=__t;fabs(__t)>e;k++)

  {

    __t*=r/(k*(k+p));

    s+=__t;

  }

  return s;

}

class m512

{

  double min,max;

 public:

  m512(){min=0;max=1;};    //конструктор

  double getvalue(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m512::getvalue(double x)

{

  double r=1.0,r1,r2,e=0.000001;

  double p=2;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                    MB_OK);

    return MAXDOUBLE;

  }

  r1=sqrt(1-x);//x<0

  while(r-r1>e)

  {

    r2=(r+r1)/2;

    r1=sqrt(r*r1);

    r=r2;

  }

  return (r+r1)*p-M_PI;

}

class m513

{

  double min,max;

 public:

  m513(){min=1;max=2;};    //конструктор

  double getvalue(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m513::getvalue(double x)

{

  double r,r2,q,y;

  double p=-0.1;

  int n=0,k;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                       MB_OK);

    return MAXDOUBLE;

  }

  y=x;

  n=10.0-x;

  y=x+n;

  r=1.0/y;

  q=r*r;

  r=log(y)-r/2-q/12.0*(1.0-q*(0.1+q/21.0));

  for(k=0;k<n;k++)

    r-=1.0/(x+k);

  return r-p;

}

class m514

{

  double min,max;

 public:

  m514(){min=0.2;max=0.6;};    //конструктор

  double getvalue(double);

  double getvalue1(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m514::getvalue1(double x)

{

  return x-1;

}

double m514::getvalue(double x)

{

  double p1=0.1818,p2=3,q,r,s,t;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                        MB_OK);

    return MAXDOUBLE;

  }

  s=p1;

  t=sqrt(2*x/M_PI);

  r=-4*x*x;

  int k=p2;

  t*=x;

  do

  {

    q=s;

    s-=t/k;

    t*=r/((k+1)*(k+3));

    k+=4;

  }while(s!=q);

  return s;

}

class m515

{

  double min,max;

 public:

  m515(){min=0;max=3.0;};    //конструктор

  double getvalue(double);

  double getderive(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m515::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                      MB_OK);

    return MAXDOUBLE;

  }

  return x*x*x-2*x-5;

}

double m515::getderive(double x)

{

  double dx=0.000001;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getderive","Выход за границы диапазона"

                    , MB_OK);

    return MAXDOUBLE;

  }

  return (getvalue(x+dx)-getvalue(x))/dx;

}

class m516

{

  double min,max;

 public:

  m516(){min=0;max=1;};    //конструктор

  double getvalue(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m516::getvalue(double x)

{

  double r=1.0,r1,r2,e=0.000001;

  double p=2;

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                     MB_OK);

    return MAXDOUBLE;

  }

  r1=sqrt(1-x);//x<0

  while(r-r1>e)

  {

    r2=(r+r1)/2;

    r1=sqrt(r*r1);

    r=r2;

  }

  return (r+r1)*p-M_PI;

}


M52.H
Заголовочный файл к задачам поиска экстремумов m52.h

#include <iostream.h>

#include <values.h>

#include <math.h>

#include <bwcc.h>

//Методы поисковой оптимизации

class m521

{

  double min,max;

 public:

  m521(){min=0;max=15;};    //конструктор

  double getvalue(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmax(){return max;};

  double getmin(){return min;};

  void setmax(double d){max=d;};

  void setmin(double d){min=d;};

};

double m521::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                     MB_OK);

    return MAXDOUBLE;

  }

  return (27.0-2*x*(9.0-x))*exp(-x/3.0);

}

class m522

{

  double min,max;

 public:

  m522(){min=0;max=15;};    //конструктор

  double getvalue(double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  DWORD fib(int);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

DWORD m522::fib(int num)//c 0;

{

  if(num==0||num==1)

    return 1;

  DWORD fpred=1,fpredpred=1,fcur;

  for(int i=2;i<=num;i++)

  {

    fcur=fpred+fpredpred;

    fpredpred=fpred;

    fpred=fcur;

  }

  return fcur;

}

double m522::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                    MB_OK);

    return MAXDOUBLE;

  }

  return (27.0-2*x*(9.0-x))*exp(-x/3.0);

}

class m523

{

  double min,max;

 public:

  m523(){min=0;max=15;};    //конструктор

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m523::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                     MB_OK);

    return MAXDOUBLE;

  }

  return (27.0-2*x*(9.0-x))*exp(-x/3.0);

}

class m524

{

  double minx1,maxx1,minx2,maxx2;

 public:

  m524(){minx1=-1;maxx1=0;minx2=0;maxx2=1;};    //конструктор

  double getvalue(double,double);

  double getmaxx1(){return maxx1;};

  double getminx1(){return minx1;};

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  void  setmaxx1(double d){maxx1=d;};

  void  setminx1(double d){minx1=d;};

  double getmaxx2(){return maxx2;};

  double getminx2(){return minx2;};

  void  setmaxx2(double d){maxx2=d;};

  void  setminx2(double d){minx2=d;};

};

double m524::getvalue(double x1,double x2)

{

  if(x1>maxx1||x1<minx1||x2>maxx2||x2<minx2)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                    MB_OK);

    return MAXDOUBLE;

  }

  return exp(x1*x1+x2*x2)+2*x1-3.5*x2;

}

class m525

{

  double minx1,maxx1,minx2,maxx2;

  int r;

 public:

  m525(){minx1=-1;maxx1=0;minx2=0;maxx2=1;r=2;};    //конструктор

  double getvalue(double*);

  int getrange(){return r;};

  double getmax(int);

  double getmin(int);

  void setmax(int,double d);

  void setmin(int,double d);

  int sign(double x)

  {if(x>=0) return 1;return -1;}

};

void m525::setmax(int i,double d)

{ if(!i) maxx1=d;  else maxx2=d;}

void m525::setmin(int i,double d)

{if(!i) minx1=d; else minx2=d;

}

double m525::getmax(int i)

{if(!i) return maxx1;  return maxx2;}

double m525::getmin(int i)

{

  if(!i) return minx1; return minx2;}

double m525::getvalue(double *x)

{

  if(x[0]>maxx1||x[0]<minx1||x[1]>maxx2||x[1]<minx2)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                     MB_OK);

    return MAXDOUBLE;

  }

  return exp(x[0]*x[0]+x[1]*x[1])+2*x[0]-3.5*x[1];

}

class m526

{

  double minx1,maxx1,minx2,maxx2;

 public:

  m526(){minx1=-1;maxx1=0;minx2=0;maxx2=1;};    //конструктор

  double getvalue(double,double);

  int sign(double x)

  {

    if(x>=0)

      return 1;

    return -1;

  }

  double getmaxx1(){return maxx1;};

  double getminx1(){return minx1;};

  void  setmaxx1(double d){maxx1=d;};

  void  setminx1(double d){minx1=d;};

  double getmaxx2(){return maxx2;};

  double getminx2(){return minx2;};

  void  setmaxx2(double d){maxx2=d;};

  void  setminx2(double d){minx2=d;};

};

double m526::getvalue(double x1,double x2)

{

  if(x1>maxx1||x1<minx1||x2>maxx2||x2<minx2)

  {

    BWCCMessageBox(0,"Метод getvalue","Выход за границы диапазона",

                 MB_OK);

    return MAXDOUBLE;

  }

  return exp(x1*x1+x2*x2)+2*x1-3.5*x2;

}


M53.H
Заголовочный файл к задачам численного интегрирования m53.h

#include <iostream.h>

#include <values.h>

#include <math.h>

#include <bwcc.h>

//Вычисление определенных интегралов

class m531

{

  double min,max;

 public:

  m531(){min=0;max=M_PI;};    //конструктор

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m531::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

    return MAXDOUBLE;

  }

  return cos(5*sin(x)-x)/M_PI;

}

class m532

{

  double min,max;

 public:

  m532(){min=0;max=1;};    //конструктор

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m532::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

    return MAXDOUBLE;

  }

  return 2*exp(-x*x)/sqrt(M_PI);

}

class m533

{

  double min,max;

 public:

  m533(){min=0;max=M_PI/2;};    //конструктор

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m533::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

    return MAXDOUBLE;

  }

  return sqrt(1-0.9*sin(x)*sin(x));

}

class m534

{

  double min,max;

 public:

  m534(){min=0;max=M_PI;};    //конструктор

  double getvalue(double);

  double getmax(){return max;};

  double getmin(){return min;};

  void  setmax(double d){max=d;};

  void  setmin(double d){min=d;};

};

double m534::getvalue(double x)

{

  if(x>max||x<min)

  {

    BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

    return MAXDOUBLE;

  }

  return sin(x);

}


M54.H
Заголовочный файл к задачам приближения функций m54.h

#include <iostream.h>

#include <values.h>

#include <math.h>

#include <bwcc.h>

//Методы приближения функций

class m541

{

  double fx[4],shag,min,max;

 public:

  m541()

  {

    fx[0]=0;fx[1]=0.5;fx[2]=0.866;fx[3]=1;min=0;max=3;shag=0.25;

  }

  int getrange(){return 4;}

  double &getmin(){return min;};

  double &getmax(){return max;};

  double &getshag(){return shag;};

  double getvalue(int i)

  {

    if(i>=0&&i<4)

      return fx[i];

    else

    {

      BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0];

    }

  };

};

class m542

{

  double fx[4],shag,min,max;

 public:

  m542()

  {

    fx[0]=0;fx[1]=0.5;fx[2]=0.866;fx[3]=1;min=0;max=3;shag=0.25;

  }

  int getrange(){return 4;}

  double &getmin(){return min;};

  double &getmax(){return max;};

  double &getshag(){return shag;};

  double getvalue(int i)

  {

    if(i>=0&&i<4)

      return fx[i];

    else

    {

      BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0];

    }

  };

};

class m543

{

  double fx[4],shag,min,max;

 public:

  m543()

  {

    fx[0]=0;fx[1]=0.5;fx[2]=0.866;fx[3]=1;min=0;max=3;shag=0.25;

  }

  int getrange(){return 4;}

  double &getmin(){return min;};

  double &getmax(){return max;};

  double &getshag(){return shag;};

  double &getvalue(int i)

  {

    if(i>=0&&i<4)

      return fx[i];

    else

    {

      BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0];

    }

  };

};

class m544

{

  double fx[4],shag,min,max;

 public:

  m544()

  {

    fx[0]=0;fx[1]=0.5;fx[2]=0.866;fx[3]=1;min=0;max=3;shag=0.25;

  }

  int getrange(){return 4;}

  double &getmin(){return min;};

  double &getmax(){return max;};

  double &getshag(){return shag;};

  double getvalue(int i)

  {

    if(i>=0&&i<4)

      return fx[i];

    else

    {

      BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0];

    }

  };

};

class m545

{

  static double x[15][5];

 public:

  int getrange(){return 4;};

  int getexp(){return 15;};

  double gv(int i,int j)

  {

    if(i>14||j>4||i<0||j<0)

    {

      BWCCMessageBox(0,"getvalue","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return x[0][0];

    }

    else

      return x[i][j];

  };

};

double m545::x[15][5]=

{

  252.36,96.67,8.37,1,140.28,

  262.54,100.07,9.07,1,140.02,

  285.7,96.78,9.35,1,149.9,

  277.52,101.3,9.67,1,147.12,

  307.95,100.35,9.45,1,163.62,

  322.44,104.8,10.12,1,173.4,

  334.88,106.17,10.35,1,178.86,

  350.11,109.2,11.03,1,186.26,

  346.10,104.48,10.38,1,183.53,

  374.91,106.88,12.15,1,198.76,

  378.49,113.14,12.98,1,205.3,

  397.48,112.38,11.34,1,206.77,

  378.39,109.07,10.95,1,198.42,

  393.44,114.45,12.89,1,216.48,

  403.84,115.23,13.71,1,221.45

};


M55.H
Заголовочный файл к задачам линейной алгебры m55.h

#include <iostream.h>

#include <values.h>

#include <math.h>

#include <bwcc.h>

//Численные методы в задачах линейной алгебры

class m551

{

  double fx[3][4];

 public:

  m551()

  {

    fx[0][0]=1;fx[0][1]=-3;fx[0][2]=2;fx[0][3]=7;

    fx[1][0]=4;fx[1][1]=6;fx[1][2]=1;fx[1][3]=3;

    fx[2][0]=2;fx[2][1]=1;fx[2][2]=-2;fx[2][3]=1;

  }

  int getrange(){return 3;}

  //double *getstr(int i){return fx[i];};

  double *getfx(){return fx[0];};

  double &gv(int i,int j)

  {

    if(i>=0&&i<3&&j>=0&&j<4)

      return fx[i][j];

    else

    {

      BWCCMessageBox(0,"gv","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0][0];

    }

  };

};

class m552

{

  double fx[3][4];

 public:

  m552()

  {

    fx[0][0]=1;fx[0][1]=4;fx[0][2]=2;fx[0][3]=3;

    fx[1][0]=2;fx[1][1]=7;fx[1][2]=3;fx[1][3]=4;

    fx[2][0]=0;fx[2][1]=8;fx[2][2]=1;fx[2][3]=-5;

  }

  int getrange(){return 3;}

  double &gv(int i,int j)

  {

    if(i>=0&&i<3&&j>=0&&j<4)

      return fx[i][j];

    else

    {

      BWCCMessageBox(0,"gv","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0][0];

    }

  };

};

class m553

{

  double fx[4][4];

 public:

  m553()

  {

    fx[0][0]=1;fx[0][1]=4;fx[0][2]=2;fx[0][3]=-3;

    fx[1][0]=2;fx[1][1]=7;fx[1][2]=3;fx[1][3]=-4;

    fx[2][0]=0;fx[2][1]=8;fx[2][2]=1;fx[2][3]=5;

    fx[3][0]=0;fx[3][1]=0;fx[3][2]=0;fx[3][3]=1;

  }

  int getrange(){return 4;}

  double &gv(int i,int j)

  {

    if(i>=0&&i<4&&j>=0&&j<4)

      return fx[i][j];

    else

    {

      BWCCMessageBox(0,"gv","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0][0];

    }

  };

};

class m554

{

  double fx[3][3];

 public:

  m554()

  {

    fx[0][0]=0;fx[0][1]=3;fx[0][2]=1;

    fx[1][0]=2;fx[1][1]=8;fx[1][2]=2;

    fx[2][0]=4;fx[2][1]=-1;fx[2][2]=7;

  }

  int getrange(){return 3;}

  double &gv(int i,int j)

  {

    if(i>=0&&i<3&&j>=0&&j<3)

      return fx[i][j];

    else

    {

      BWCCMessageBox(0,"gv","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0][0];

    }

  };

};

class m555

{

  double fx[3][6];

 public:

  m555()

  {

    fx[0][0]=0.5;fx[0][1]=0;fx[0][2]=0.5;

    fx[1][0]=0;fx[1][1]=0.5;fx[1][2]=1;

    fx[2][0]=1;fx[2][1]=0.5;fx[2][2]=0;

  }

  int getrange(){return 3;}

  double &gv(int i,int j)

  {

    if(i>=0&&i<3&&j>=0&&j<6)

      return fx[i][j];

    else

    {

      BWCCMessageBox(0,"gv","Выход за пределы диапазона",

     MB_OK|MB_ICONEXCLAMATION);

      return fx[0][0];

    }

  };

};


M56.H
Заголовочный файл к задачам ЛП m56.h

#include <iostream.h>

#include <values.h>

#include <math.h>

#include <stdlib.h>

#include <conio.h>

//Линейное программирование

class m561

{

  double a[5][7];//Содержит уравнения,а впоследней строке -

//линейную форму.Столбец свободных членов - 0-вой

 int    x[6];//Для учета перестановок при выявлении зацикливания

 int    y[4];

 public:

 m561(void);

 int getrow(){return 4;};

 int getcol(){return 7;}

 double &getvalue(int r,int c){return a[r][c];};

  int &getx(int ix){return x[ix];};

  int &gety(int iy){return y[iy];};

  int sign(double ix){if(ix>=0)return 1;else return -1;};

};

m561::m561(void)

{

 //Матрицу коэффициентов ур-ний и лин. формы генерируем случайными

//числами

 randomize();

 for(int i=0;i<5;i++)

 {

   cout<<endl;

   for(int j=0;j<7;j++)

   {

     a[i][j]=random(10)-5;

     if(!a[i][j])a[i][j]=1.5;//Чтобы избежать нулевого значения

     cout.width(5);

     cout<<a[i][j]<<" ";

   }

  }

  cout<<endl;

  getch();

}

m561 t;

int r,c,//Кол-ва уравнений и переменных

rr=-1,rc=-1;//Координато-флаги разрешаюшего элемента

void zamena()

{

  double re=t.getvalue(rr,rc);//Разрешающий элемент

  //Замена по алгоритму , см. урок

  t.getvalue(rr,rc)=1/re;

  for(int i=0;i<r+1;i++)

    for(int j=0;j<c;j++)

      if(i!=rr || j!=rc)

        t.getvalue(i,j)-=t.getvalue(rr,j)*(t.getvalue(i,rc)/re);

  for(i=0;i<r+1;i++)if(i!=rr)t.getvalue(i,rc)/=-re;

  for(i=0;i<c;i++)if(i!=rc)t.getvalue(i,rc)/=re;

  //

  for( i=0;i<r+1;i++)

  {

    cout<<endl;

    for(int j=0;j<c;j++)

    {

      cout.width(5);

      cout<<t.getvalue(i,j)<<" ";

    }

  }

  cout<<endl;

  getch();

  //

 }

 void opor()

 {

  double min=MAXDOUBLE,//минимум отношения свободного члена к

//текущему элементу

  tempmin;//отношение свободного члена к текущему элементу

  for(int i=0;i<r;i++)

  {

     rc=-1;rr=-1;

    if(t.getvalue(i,0)<0)//ЕСЛИ НАШЛИ ОТРИЦ СВОБ ЧЛЕН

    {

      for(int j=1;j<c;j++)

        if(t.getvalue(i,j)<0)       //ЕСЛИ НАШЛИ ОТРИЦ КОЭФ В

//УРАВНЕНИИ

 {

   rc=j;                      //то это разреш столбец

          for(int k=0;k<r;k++)

if((t.sign(t.getvalue(k,rc))==(t.sign(t.getvalue(k,0)))))

            //если знаки одинаковы

       if((tempmin=t.getvalue(k,0)/t.getvalue(k,rc))<min)

              //если отношение меньше минимального

                {min=tempmin;rr=k;}

   if(rr==-1)//если не нашли разреш строку

      {  cout<<"Система не совместна\n";  exit(0);   }

   zamena();//Вызов процедуры замены свободной и базисных

                   //переменных

 }

     }

   }

 //Отладочная печать

   for( i=0;i<r+1;i++)

  {

    cout<<endl;

    for(int j=0;j<c;j++)

    {

      cout.width(5);

      cout<<t.getvalue(i,j)<<" ";

    }

  }

  cout<<endl;

  getch();

 //

 }

void optim()//Поиск оптимального решения

{

  int flag=0,//Оптимальное ли решение?

  flag2=0;//Ограничена ли форма?

  double min=MAXDOUBLE,tempmin;

  for(int i=1;i<c;i++)

    if(t.getvalue(r,i)>0)

    {

      flag=1;

      for(int j=0;j<r;j++)

      //если в лин. форме есть пол . эл. то ограничена

 if(t.getvalue(i,r)>0)

 {

   flag2=1;

   rc=i;//rr=j;

   for(int k=0;k<r;k++)

            //if(t.sign(t.getvalue(k,rc)==t.sign(t.getvalue(k,0))))

              if((tempmin=t.getvalue(k,0)/t.getvalue(k,rc))<min)

                {min=tempmin;rr=k;}

          zamena();

          return;

 }

      if(!flag2)

      {

 cout<<"Линейная форма не ограничена снизу";

 exit(0);

      }

    }

  if(!flag)

  {

    cout<<"Найдено оптимальное решение...\n";

    for(i=0;i<r;i++)

      cout<<t.getvalue(i,0)<<"  ";

    double sum=t.getvalue(r,0);//Вычисление формы

    for(i=c-r-1;i<r+1;i++)

      sum+=t.getvalue(i-(c-r-1),0)*t.getvalue(r,i);

    cout<<endl<<endl<<"Summa : "<<sum<<endl;

    exit(0);

  }

}

void _main(void)

{

  r=t.getrow();

  c=t.getcol();

  opor();

  for(;;)

    optim();

}


M57.H
Заголовочный файл к задачам Коши для ОДУ m57.h

#include <iostream.h>

#include <values.h>

#include <math.h>

//Задача Коши для обыкновенных дифференциальных уравнений

class m571   //Для метода Эйлера

{

  double min,max,y1,y2;

 public:

  m571(){min=0.5;max=1.0;y1=0.9384698;y2=-0.2422685;};

//конструктор

  double getvalue(int num,double x);

  double getrange(){return 2;};

  double getstart(int i);

  void setstart(int num,double value);

  double getmax(){return max;};

  double getmin(){return min;};

  void setmax(double d){max=d;};

  void setmin(double d){min=d;};

};

  double m571::getvalue(int num,double x)

  {

    double t;

    switch(num)

    {

      case 1: t=y2;break;

      case 2: t=-(y1+y2/x);break;

    }

    return t;

  }

  double m571::getstart(int i)

  {

    double t;

    switch(i)

    {

      case 1: t=y1;break;

      case 2: t=y2;break;

    }

    return t;

  }

  void m571::setstart(int num,double value)

  {

    switch(num)

    {

      case 1: y1=value;break;

      case 2: y2=value;break;

    }

  }

//Для Рунге-Кутты 2-го порядка

class m572

{

  double min,max,y1,y2;

 public:

  m572(){min=0.5;max=1;y1=0.9384698;y2=-0.2422685;};

//конструктор

  double getvalue(int num,double x);

  double getrange(){return 2;};

  double getstart(int i);

  void setstart(int num,double value);

  double getmax(){return max;};

  double getmin(){return min;};

  void setmax(double d){max=d;};

  void setmin(double d){min=d;};

};

  double m572::getvalue(int num,double x)

  {

    double t;

    switch(num)

    {

      case 1: t=y2;break;

      case 2: t=-(y1+y2/x);break;

    }

    return t;

  }

  double m572::getstart(int i)

  {

    double t;

    switch(i)

    {

      case 1:t= y1;break;

      case 2: t= y2;break;

    }

    return t;

  }

  void m572::setstart(int num,double value)

  {

    switch(num)

    {

      case 1: y1=value;break;

      case 2: y2=value;break;

    }

  }

//Для Рунге-Кутты 4-го порядка

class m573

{

  double min,max,y1,y2;

 public:

  m573(){min=0.5;max=1;y1=0.9384698;y2=-0.2422685;};

//конструктор

  double getvalue(int num,double x);

  double getrange(){return 2;};

  double getstart(int i);

  void setstart(int num,double value);

  double getmax(){return max;};

  double getmin(){return min;};

  void setmax(double d){max=d;};

  void setmin(double d){min=d;};

};

  double m573::getvalue(int num,double x)

  {

    double t;

    switch(num)

    {

      case 1: t= y1=y2;break;

      case 2: t= y2=-(y1+y2/x);break;

    }

    return t;

  }

  double m573::getstart(int i)

  {

    double t;

    if(i==1) t=y1;

    if(i==2) t=y2;

    return t;

  }

  void m573::setstart(int num,double value)

  {

    switch(num)

    {

      case 1: y1=value;break;

      case 2: y2=value;break;

    }

  }

//Для метода Адамса

class m574

{

  double min,max,y0,y1;

 public:

  m574(){min=1.0;max=10;y0=0.7651977;y1=-0.4400506;};

//конструктор

  void getvalue(double x,double *y,double *f);

  double getrange(){return 2;};

  double getstart(int i);

  void setstart(int num,double value);

  double getmax(){return max;};

  double getmin(){return min;};

  void setmax(double d){max=d;};

  void setmin(double d){min=d;};

};

void m574::getvalue(double x,double *y,double *f)

{

  f[0]=y[1];

  f[1]=-y[0]-y[1]/x;

}

double m574::getstart(int i)

{

  double t;

  switch(i)

  {

    case 0: t= y0;break;

    case 1: t= y1;break;

  }

  return t;

}

void m574::setstart(int num,double value)

{

  switch(num)

  {

    case 1: y0=value;break;

    case 2: y1=value;break;

  }

}


БД-общая постановка задачи
Общая постановка задачи

Общая постановка задачи.

Программа составления расписания занятий - не лучшая иллюстрация

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

задачами складского учета, учета автомобилей в автоинспекции,

больных в стоматологии или служащих на предприятии. Наш выбор

обусловлен с одной стороны приобретаемой вами специальностью

школьного учителя, для которого маловероятно занятие учетными

задачами приведенных классов, а с другой стороны тем, что

составление расписания относится к "неудобным" задачам например

в части формирования экранного отображения базы данных - дело в

том, что большинство практических задач отличаются от

стандартных иллюстраций именно "неудобностью" и мы хотели

использовать неклассический вариант для освоения некоторых

методов преодоления возникающих трудностей. Третий фактор -

полезность программы в вашей будущей работе; наряду с созданием

ряда компьютерных обучающих программ вам может пригодиться и эта

программа - разумеется после соответствующей доводки и надежной

защиты от неправильных действий пользователя.

Будем считать, что перед нами стоит задача создать компьютерную

программу, позволяющую составлять, изменять и оптимизировать по

некоторым критериям расписание занятий на один семестр в высшем

учебном заведении, включающем некоторое множество факультетов,

каждый из которых ведет обучение по нескольким специальностям в

течение ряда семестров. Каждая специальность на отдельно взятом

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

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

например лабораторных занятий в специализированных лабораториях.

Составить расписание для конкретного факультета - значит

определить для каждой подгруппы на каждую 2-часовку (или более

- например 4-часовое занятие) каждого дня недели изучаемый

предмет, проводящего занятие преподавателя и соответствующую

аудиторию.  При этом и преподаватель и аудитория в

соответствующей 2-часовке не могут фигурировать в расписании

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

должно быть так называемых "накладок". Оптимизация расписания

состоит в исключении "дыр" в занятиях студентов и в загрузке

преподавателей, в размещении предметов по 2-часовкам каждого дня

занятий в соответствии с категорией сложности предметов - более

трудные должны идти раньше легких, в расстановке занятий в

разных корпусах через большую перемену для обеспечения времени

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

Программа должна предоставлять серию необходимых услуг по работе

с готовым расписанием - например по запросам пользователя

выдавать на экран и принтер расписание занятий любой заданной

группы учащихся на четную или нечетную неделю, такое же

расписание для любого заданного в запросе преподавателя или

всех преподавателей кафедры, недельную аудиторную нагрузку

преподавателя или группы студентов и пр.

Совершенно очевидно, что нам предстоит обрабатывать некоторый

файл записей, каждая из которых содержит "ведущие" или ключевые

поля, одни из которых определяют "столбец" в табличной форме

расписания:

-курс;

-специальность;

-группа;

-подгруппа;

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

-день недели;

-номер 2-часовки (пары, ленты и пр );

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

-предмет;

-преподаватель;

-аудитория;

Еще одна группа полей может носить  общий характер:

-факультет;

-тип недели (четная - нечетная, числитель- знаменатель и пр)

Таким образом мы будем работать с файлом табличной структуры и

должны обеспечить возможность выполнения в этом файле ряда

стереотипных действий - создание файлов и заполнение записей,

их корректировку при необходимости, поиск записей с заданным

содержимым некоторых полей, обработку заданных

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

Очевидно, что мы могли бы обойтись в нашем случае одним файлом

с перечисленными выше полями, но в этом случае имела бы место

нерациональеая избыточность по расходу памяти - например

названия факультетов, специальностей, фамилии преподавателей и

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

неэкономичной текстовой форме. Поэтому такие повторяющиеся

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

совместно с их числовыми кодами, а в основном файле базы данных

оперируют только этими более экономичными в части расходования

памяти кодами. Таким образом база данных представляет собой

обычно совокупность многих взаимосвязанных файлов.

 Опыт применения ЭВМ для построения прикладных систем обработки

данных показывает, что более эффективным инструментом здесь

могут быть не универсальные алгоритмические языки высокого

уровня, а специализированные (проблемно - ориентированные)

системы и встроенные в них входные языки .  Такие средства

обычно включают в состав систем управления базами данных (СУБД),

но они могут существовать и отдельно. СУБД дают возможность

пользователям осуществлять непосредственное управление данными,

а программистам быстро разрабатывать более совершенные

программные средства их обработки.


Основные понятия о БД
Основные понятия

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

реляционную, иерархическую и сетевую модели.

  Реляционная модель является простейшей и наиболее привычной

формой представления данных в виде таблицы. В теории множеств

таблице соответствует термин отношение (relation), который и дал

название модели. Для нее имеется развитый математический аппарат

- реляционное исчисление и реляционная алгебра, где для баз

данных (отношений) определены такие хорошо известные теоретико -

множественные операции, как объединение, вычитание, пересечение

и т.д.

  Достоинством реляционной модели является сравнительная

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

жесткость структуры данных (невозможность, например, задания

строк таблицы произвольной длины, отсутствие списковых полей,

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

изображения) и зависимость скорости ее работы от размера базы

данных. Для многих операций, определенных в такой модели, может

оказаться просмотр всей базы данных.

  Иерархическая и сетевая модели предполагают наличие связей

между данными, имеющими какой-либо общий признак. В

иерархической модели такие связи могут быть отражены в виде

дерева - графа, где возможны только односторонние связи от

старших вершин к младшим. Это облегчает доступ к необходимой

информации, но только если все возможные запросы отражены в

структуре дерева. Никакие иные запроосы удовлетворены быть не

могут.

  Указанные недостаток снят в сетевой модели, где возможны связи

"всех со всеми". Использование ерархческой  сетевой модел

ускоряет доступ к информации в базе данных. Но поскольку каждый

элемент данных должен содержать ссылки на некоторые другие

элементы, требуются значительные ресурсы как дисковой, так и

основной памяти ЭВМ. Нехватка ОЗУ, естественно, снижает скорость

обработки данных. Кроме того,для таких моделей характерна

сложность реализации СУБД.

  FoxPro 2.0 фирмы Fox Holdings Software, ныне приобретенная

Microsoft Corp., претендует на то, чтобы быть реляционной,

однако до этого ей еще далеко - из 300 правил Кодда для

реляционных систем в ней выполняется едва ли 40, однако этот

пакет достаточно неприхотлив к системным ресурсам (для работы

достаточно обычного "Поиска-2"), имеет неплохой командный режим,

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

вообще и предоставляет в Ваше распоряжение Windows - подобный

событийно - ориентированный интерфейс.

           Терминология и структура данных в СУБД

           ======================================

  БАЗА ДАННЫХ

  База данных представляет собой файл, где хранятся Ваши

данные. К примеру, база данных "почтовой рассылки" содержит

имена и адреса людей.

  ЗАПИСЬ

  Информация базы данных организована по записям. Каждая запись

в базе данных почтовой рассылки содержит информацию по одному

человеку.

  НОМЕР ЗАПИСИ

  Каждой записи в базе данных присваивается порядковый номер

записи, начиная с первого.

  ПОЛЕ

  Записи организованы по полям. Поле базы данных описывает одну

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

данных "почтовой рассылки" могут быть поля "фамилия", "адрес",

"город", и "телефонный номер" с именами LAST_NAME, ADDRESS,

CITY и PHONE_NUM, соответственно.

  Файлы БД типа DBF являются основными носителями данных на

диске. Они имеют следующие характеристики:

  число записей в файле           - до 1,000,000,000

  размер записи (в байтах)        - до 4,000

  число полей в записи            - до 255

  число одновременно открытых баз - до 25

  Типом поля может быть символьное, числовое, поле числа с

плавающей запятой, логическое поле, поле даты или поле

примечаний (текста). Тип поля определяет тип информации,

которая хранится в этом поле. Имена и типы полей определяются

при создании базы данных.

  Типы и размеры полей (в байтах):

  символьные поля                 - до 254,     С

допускают ввод любых алфавитно - цифровых символов, знаков

препинания и т.д.

  числовые поля                   - до 20,      N

однако достоверными при вычислениях являются только 16 разрядов.

Целое число может занимать все 20 разрядов, дробное - от 0 до 18

разрядов и один разряд под точку и возможный знак "+" или "-"

  поля дат                        - 8,          D

допускают ввод, естественно, только цифр. Разрешенные даты в

FoxPro должны находиться в диапазоне о 1 января 100 года до 12

декабря 9999 года

  логические поля                 - 1,          L

и другие логические величины могут иметь только 2 значения: .Т.

(от TRUE) и .F. (от FALSE), или, что то же самое, .Y. и .N.

  memo - поля                     - ?,          М

  Файлы примечаний (файлы memo) используются для хранения текста

переменной длины. В целях эффективности, каждое поле сохраняется

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

Поле примечаний в записи базы данных имеет фиксированный размер,

равный десяти байтам, а в отдельном файле примечаний - переменный

размер. Файл примечаний имеет то же имя, что и соответствующий

файл базы данных, но другое расширение - не "DBF", а "DBT" в

dBASE и "FPT" в FoxPro. Для всех полей примечаний определенного

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

             Быстрый поиск данных. Индексные файлы

             =====================================

  В различных случаях бывает необходимым обследовать базу данных

в разном порядке. В одном случае, скажем, нужно  произвести

сортировку базы данных почтовой рассылки по полю фамилии

LAST_NAME, в другом - по полю адреса ADDRESS. Перезаписывать

базу данных каждый раз, когда требуется новый порядок

сортировки, отнимало бы много времени; эта проблема решается при

помощи создания индексных файлов.

  Индексные файлы постоянно содержат отсортированную информацию.

Один индексный файл содержит один порядок сортировки для базы

данных. Индексные файлы имеют древовидную B+ структуру, что

делает поиск отсортированной информации быстрым и эффективным.

  ВЫРАЖЕНИЕ ИНДЕКСНОГО ФАЙЛА

  Индексные файлы также содержат информацию о том, как должна

сортироваться база данных. Эта информация называется выражением.

Примером выражения является LAST_NAME; оно означает, что

индексный файл содержит сортировку по полю LAST_NAME.

  При помощи FoxPro можно сохранять и получать доступ к сотням

мегабайт информации. Эта информация сохраняется на диске в

файлах базы данных. За один раз производятся операции только над

частью всей доступной информации.

  Одновременно можно держать открытыми несколько файлов базы

данных. Однако, единовременно выбирается только один файл базы

данных. Функции базы данных работают с выбранной базой данных.

Открытие и закрытие баз данных занимает много времени, но выбор

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

  Каждая база данных может иметь набор открытых индексных

файлов. Каждый индексный файл соответствует определенной базе

данных, и когда  база данных изменяется, то автоматически

поддерживаются изменения во всех соответствующих индексных

файлах. Каждая база данных может иметь свой собственный

"выбранный" индексный файл. Высокоуровневые функции управления

базой данных, такие, как "seek" или "skip", используют текущий

выбранный индексный файл текущей выбранной базы данных.

Низкоуровневые функции управления индексными файлами, как

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

  В FoxPro можно создать 2 типа индексных файлов:

  - Обычный индексный файл. Он имеет расширение IDX и содержит

    один индексный ключ.

  - Мультииндексный файл с расширением CDX. Такой файл может

    хранить сразу несколько индексных выражений и является по

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

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

    отсутствия подходящего термина словом тег или таг (TAG -

    этикетка). Каждый тег имеет свое имя.

  Мультииндексные файлы могут быть двух видов : структурный

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

и обычный мультииндексный файл с произвольным именем.

Структурный файл всегда автоматически открывается вместе со

своей базой. Его нельзя закрыть, хотя можно сделать неглавным.

          Работа с DBF - файлами на языке Паскаль,

          ========================================

                  структура DBF - файла

                  =====================

{

********************************************************

СТPУКТУPА ФАЙЛА БАЗЫ ДАННЫХ (dbf) DBASE-3

Заголовок

Байт         Содержание       Значение

0            1 byte           Номер версии DBASE-3

                              (03Н - без .dbt файлов,

                               83Н - с .dbt файлами)

1-3          3 bytes        Последняя дата обновления

                                                                                                                        (YY MM DD)

4-7          32 bit         Число записей в файле

             numbers

8-9          16 bit         Длина заголовка структуры

             numbers

10-11        16 bit         Длина записей

             numbers

12-31        20 bytes       Зарезервировано (v.1.0)

32-n         32 bytes       Массив описания полей

             each

n+1          1 bytes        ODH - оконцовка массива

Описание поля

Байт         Содержание     Значение

0-10         11 bytes       Имя поля в коде ASCII ,

                            дополняется нулями

11            1 bytes       Тип поля в коде ASCII

                            (С,N,L,D,M)

12-15        32 bit         Адрес поля данных в памяти

             number

16            1 bytes       Длина поля

17            1 bytes       Число десятичных разрядов(only for type D)

18-32        14 bytes       Зарезервировано (v.1.0)

Расположение записей

1 Каждой записи предшествует 1 байт, который или

  пробел (20Н), если запись не удаляется , или звездочка (2АН)

  если запись удаляется.

2 Поля упакованы в записи без разделителей

3 Данные хранятся в коде ASCII в следующем формате:

Тип           Данные

Символьный    ASCII символы

Числовой      -.0123456789

Логический    YyNnTtFf

Примечания    10 цифр, представляющих

              номер блока .dbt

              Файл пpимечаний : <dbf-имя>.dbt

              0 блок - offset 000h относительно начала файла

              1 блок - offset 200h , и т.д.

              конец пpимечания in dbt-file : code 1Ah (= 26d)

Дата          8 цифр в формате YYYYMMDD

***************************************************************}

unit DB3Tool;

interface

uses

   CRT,

   Dos,

   MemChk;

type

  MaxFieldNumber             = 1..1000;

  MaxRecNumber               = 1..10000;

  BuferSize                  = 0..65534;

const

  UserErrorHandler : boolean = false;

  AddRecFactor               = 5;

                              {There is no reason to add only

                              one record to Data Base - it's

                              very difficult to do that, so we

                              add AddRecFactor records at time}

type

  Bufer     = array[BuferSize] of byte;

  CharBufer = array[BuferSize] of char;

  FieldName = string[11];

  FieldType = (C,N,L,M,D);

  CType     = string[254];

  NType     = string[19];

  LType     = string[1];

  MType     = string[10];

        DType     = string[8];

  Date      = record

                MM : byte;

                DD : byte;

                YY : byte;

              end;

  FieldD = record

             FName   : FieldName;

             FType   : FieldType;

             FLength : byte;

             FDec    : byte;

           end;

  FieldDPtr = ^FieldD;

  FieldPtr = ^CharBufer;

  FieldDArray = array[MaxFieldNumber] of FieldD;

  RecPtr      = ^bufer;

        FilePtr                 = longint;

  RecordArray = array[MaxRecNumber] of RecPtr;

        RecordFile      =       array[MaxRecNumber] of FilePtr;

  DBD = record

        LastUpdate         : Date;

        TotalRecs          : longint;

        UsedRecs           : longint;

        TotalFields        : longint;

        RecSize            : word;

        FieldDArrayPtr     : ^FieldDArray;

        MinRamUsing                              : boolean;

        case byte of

                0: (RecArrayPtr  : ^RecordArray);

                1: (CurRec       : RecPtr;

                                CurRecNumber : longint;

                                TmpFile            : file);

        end;

{

Structure of DBase III data base , loading in memory           T

    +---------------------+     +------------------------+ -+  o

DBD=¦LastUpdate =¦MM¦DD¦YY¦  +->¦FName¦FType¦FLength¦FDec¦  ¦  t

    ¦---------------------¦  ¦  ¦-----+-----+-------+----¦  ¦  a

    ¦TotalRecs            ¦  ¦  ¦     ¦     ¦       ¦    ¦  ¦  l

    ¦---------------------¦  ¦  ¦------------------------¦  +- F

    ¦UsedRecs             ¦  ¦  ¦       .  .  .  .  .    ¦  ¦  i

    ¦---------------------¦  ¦  ¦------------------------¦  ¦  e

    ¦TotalFields          ¦  ¦  ¦     ¦     ¦       ¦    ¦  ¦  l

    ¦---------------------¦  ¦  +------------------------+ -+  d

    ¦RecSize              ¦  ¦                                 s

    ¦---------------------¦  ¦

    ¦FieldDArrayPtr       ¦ -+

    ¦---------------------¦

    ¦RecArrayPtr          ¦ -+

    +---------------------+  ¦

+----------------------------+

¦               Deleted(*)/Present( ) Marker

¦              +----------------------------

¦              +----+

¦                   V

¦   +---------+    +-----------+ -+ T

+-->¦ RecPtr1 ¦ -->¦ ¦ Record1 ¦  ¦ o

    ¦---------¦    ¦-+---------¦  ¦ t

    ¦ RecPtr2 ¦ -->¦ ¦ Record2 ¦  + a

    ¦---------¦ .. ¦-+---------¦  ¦ l

    ¦---------¦ .. ¦-+---------¦  ¦ R

    ¦ RecPtrN ¦ -->¦ ¦ RecordN ¦  ¦ e

    +---------+    +-----------+ -+ c

                                    s

}

function  DBErrMsg(DBErr : word) : string;

function  DBResult : word;

procedure SetDBError (ErrN : word);

procedure ReadDB (var DBDescr : DBD;

        DBFile : string;

        MinRamUse : boolean);

procedure WriteDB(var DBDescr : DBD; DBFile : string);

procedure CreateDB{In   Memory}(var DBDescr      : DBD;

                                                                                                                                                TotRecs          : longint;

                                                                                                                                                var FieldDescr : FieldD;

                                    MinRamUse  : boolean);

procedure RemoveDB{From Memory}(var DBDescr : DBD);

procedure AddField(var DBDescr : DBD;var FieldDescr : FieldD);

procedure DeleteField(var DBDescr : DBD;FieldN : longint);

function  AddRec   (var DBDescr : DBD) : longint;

procedure DeleteRec(var DBDescr : DBD;RecN : longint);

function GetFieldPtr (var DBDescr : DBD;RecN : longint; FieldN :

longint) : FieldPtr;

function GetFieldDPtr(var DBDescr : DBD;FieldN : longint) :

FieldDPtr;

function GetMaxRec   (var DBDescr : DBD) : longint;

function GetMaxField (var DBDescr :  DBD) : longint;

function CheckFieldD (var FD : FieldD) :  boolean;

function GetField(var DBDescr : DBD;RecN : longint;FieldN :

longint) : string;

function GetRField(var DBDescr : DBD;RecN : longint;FieldN :

longint) : real;

function GetBField(var DBDescr : DBD;RecN : longint;FieldN :

longint) : boolean;

function GetDField(var DBDescr : DBD;RecN : longint;FieldN :

longint) : DType;

procedure PutField(var DBDescr : DBD;RecN : longint;FieldN :

longint;Field : string);

procedure PutRField(var DBDescr : DBD;RecN : longint;FieldN :

longint;Field : real);

procedure PutBField(var DBDescr : DBD;RecN : longint;FieldN :

longint;Field : boolean);

procedure PutDField(var DBDescr : DBD;RecN : longint;FieldN :

longint;Field : DType);

{

  Поиск для полей :

  L                 True,False

  N                 LowRange - HighRange

  C                 Equal(+WildCard)

  D                 Equal(+WildCard)

  Допустимые wildcards - ?,*

}

const

  MaxPatrnNumber = 200;

type

  Patrn = record

           CFieldN : longint;

           CFType  : FieldType;

           case byte of

             0     : (LP : boolean);

             1     : (NP : array[1..2] of real);

             2     : (CP : CType);

             3     : (DP : DType);

         end;

  PatrnArray = array[1..MaxPatrnNumber] of Patrn;

  MaskD = record

            TotalPatrn : byte;

            StartRec   : longint;

            DBPtr      : ^DBD;

            PatrnAPtr  : ^PatrnArray;

          end;

procedure CreateMask      (var MD : MaskD;var Pat : Patrn);

procedure DeleteMask      (var MD : MaskD);

procedure AddPatrn        (var MD : MaskD;var Pat : Patrn);

function  FindFirstRecord (var MD : MaskD;var DBDescr : DBD)

: longint;

function  FindNextRecord  (var MD : MaskD)       : longint;

implementation

var

  DBError : word;

const

  DB3Id                     = 3;

  EndOfFields   : byte      = $0D;

  EndOfFile     : byte      = byte(^Z);

  RecordPresent : byte      = byte(' ');

  RecordDeleted : byte      = byte('*');

  FieldStrType  : string[5] = 'CNLMD';

  SmallBlockLength          = 32;

  DeltaDate                 = 1;

  DeltaTotalRecs            = 4;

  DeltaHeaderLength         = 8;

  DeltaRecordsLength        = 10;

  DeltaFType                = 11;

  DeltaFLength              = 16;

  DeltaFDec                 = 17;

var

  HeaderLength,

  RecordsLength : word;

  TmpBufer      : array[0..pred(SmallBlockLength)] of byte;

function Min(N1,N2 : longint) : longint;

begin

  if N1 >= N2

  then Min := N2

  else Min := N1;

end;

function Int2Str(L : LongInt) : string;

{ Converts an integer to a string}

var

  S : string;

begin

  Str(L, S);

  Int2Str := S;

end; { Int2Str }

procedure RandAssign(var F : file);

var

  H,M,S,D : word;

  FN      : string;

begin

  GetTime(H,M,S,D);

  FN := Int2Str(H)+Int2Str(M)+Int2Str(S)+Int2Str(D)+'.tmp';

  assign(F,FN);

end;

function Str2Int(S : string) : LongInt;

{ Converts a string to an integer,errors ignored}

var

  L : LongInt;

  code : integer;

begin

  Val(S,L,code);

  Str2Int := L;

end; { Str2Int }

procedure SetDBError (ErrN : word);

begin

 DBError := ErrN;

  if (not UserErrorHandler) then

  begin

    TextMode(Co80);

    ClrScr;

    write(^G);

    write(^G);

    writeln('Error : ',DBErrMsg(DBError));

    Halt(1);

  end;

end;

procedure ReadRecord(var DBDescr : DBD;RecN : longint);

begin

        with DBDescr do

        begin

                Seek(TmpFile,pred(RecN)*RecSize);

                BlockRead(TmpFile,CurRec^,RecSize);

                CurRecNumber := RecN;

        end;

end;

procedure WriteRecord(var DBDescr : DBD;RecN : longint);

begin

        with DBDescr do

        begin

                Seek(TmpFile,pred(RecN)*RecSize);

                BlockWrite(TmpFile,CurRec^,RecSize);

        end;

end;

function DBResult : word;

begin

  DBResult := DBError;

  DBError := 0;

end;

function DBErrMsg(DBErr : word) : string;

begin

  case DBErr of

    1   : DBErrMsg := 'Ошибка N 1';

    100 : DBErrMsg := 'Не хватает памяти';

    101 : DBErrMsg := 'Несоответствие типов полей';

    102 : DBErrMsg := 'Not DB3 file';

    103 : DBErrMsg := 'Conversion error';

    104 : DBErrMsg := 'Field number out of range';

    105 : DBErrMsg := 'Record number out of range';

    106 : DBErrMsg := 'Try writting empty data base '+'(no

                       fields,no records)';

  else DBErrMsg := 'Неизвестная ошибка';

  end;

end;

function GetRecPtr(var DBDescr : DBD;RecN : longint) : RecPtr;

begin

        with DBDescr do

        begin

                if MinRamUsing then

                begin

                        if CurRecNumber <> RecN then

                        begin

                                ReadRecord(DBDescr,RecN);

                                CurRecNumber := RecN;

                        end;

                        GetRecPtr := CurRec;

                end

                else GetRecPtr := DBDescr.RecArrayPtr^[RecN];

        end;

end;

procedure PutRecPtr(var DBDescr:DBD;RecN:longint;RPtr:RecPtr);

begin

        with DBDescr do

        begin

                if MinRamUsing then WriteRecord(DBDescr,RecN)

                else DBDescr.RecArrayPtr^[RecN] := RPtr;

        end;

end;

function GetMarker(var DBDescr : DBD;RecN : longint) : byte;

begin

        GetMarker := GetRecPtr(DBDescr,RecN)^[0];

end;

procedure PutMarker(var DBDescr:DBD;RecN:longint;Marker:byte);

var

        CurRecPtr : RecPtr;

begin

        CurRecPtr     := GetRecPtr(DBDescr,RecN);

        CurRecPtr^[0] := Marker;

        PutRecPtr(DBDescr,RecN,CurRecPtr);

end;

procedure Sort(var DBDescr : DBD);

{

Sorting records is moving deleted records (TotalRecs-UsedRecs)

to end of RecArrayPtr

or otherwise deleting fragmentation

}

var

  FirstDel,           {First deleted record in data base}

  FirstDRB : longint; {First deleted record in the block of

                       deleted records at the end of data

                       base (deleted records block = DRB)}

        TotRecs       : longint;

        Finding       : boolean;

        SaveRecPtr    : RecPtr;

        i             : longint;

begin

  TotRecs  := GetMaxRec(DBDescr);

  FirstDel := 1;

  FirstDRB := TotRecs;

  repeat {finding fragmentation and correct problem until  no

          one encounted}

    {At the begining let's find first deleted record}

    Finding := false;

    repeat

      if GetMarker(DBDescr,FirstDel) = RecordDeleted then

         Finding := true;

      inc(FirstDel);

    until ((FirstDel > TotRecs) or Finding);

    if (not Finding) then exit;{no deleted records found}

                dec(FirstDel);

    {Next find start of DRB}

    Finding := false;

    repeat

      if GetMarker(DBDescr,FirstDRB) = RecordPresent then

         Finding := true;

      dec(FirstDRB);

    until ((FirstDRB < 1) or Finding);

        if (not Finding) then exit;{no presented records

        found}

        {We found last present record,then

          set FirstDRB to begin of DRB}

        inc(FirstDRB);

        if (FirstDRB = pred(FirstDel)) then exit; { no

        fragmentation encounted}

        if DBDescr.MinRamUsing then

        begin

          for i := 1 to FirstDRB-FirstDel do

          begin

          SaveRecPtr := GetRecPtr(DBDescr,FirstDel+i);

          PutRecPtr(DBDescr,FirstDel+pred(i),SaveRecPtr);

          end;

          PutMarker(DBDescr,FirstDRB,RecordDeleted);

        end

        else

        begin

                {now move deleted record to the begin of DRB}

                {Save moving record pointer ...}

                SaveRecPtr := GetRecPtr(DBDescr,FirstDel);

                {shift all record pointers from FirstDel

                to pred(FirstDRB) at one position up}

                move(GetRecPtr(DBDescr,succ(FirstDel))^,

                     GetRecPtr(DBDescr,FirstDel)^,

                     SizeOf(RecPtr)*(pred(FirstDRB)-FirstDel));

                {.. and put moving record pointer to new place}

                PutRecPtr(DBDescr,pred(FirstDRB),SaveRecPtr);

        end;

  until false;

end;

procedure ReadDB(var DBDescr : DBD;DBFile : string;MinRamUse :

boolean);

 var DBF    : file;

  i, j      : word;

procedure AbortWithRelease(ALevel : byte;Count : longint);

var

  i : longint;

begin

  if ALevel >= 2

  then with DBDescr do FreeMemCheck(FieldDArrayPtr,

                                    SizeOf(FieldD)*TotalFields);

   if ALevel = 3

  then with DBDescr do

  for i := 1 to Count do FreeMemCheck(RecArrayPtr^[i],RecSize);

  SetDBError(100);

end;

begin

  assign(DBF,DBFile);

  {$I-}

  reset(DBF,1);

  {$I+}

  if IOResult <> 0 then

  begin

    SetDBError(IOResult);

    exit;

  end;

  BlockRead(DBF,TmpBufer,SmallBlockLength);

  if (TmpBufer[0] and $7F) <> DB3Id then

  begin

    SetDBError(102);

    exit;

  end;

  with DBDescr do

  begin

    TotalRecs   := longint(Addr(TmpBufer[DeltaTotalRecs])^);

    MinRamUsing := MinRamUse;

    with LastUpdate do

    begin

      MM := TmpBufer[DeltaDate];

      DD := TmpBufer[DeltaDate+1];

      YY := TmpBufer[DeltaDate+2];

    end;

  end;

  HeaderLength  := word(Addr(TmpBufer[DeltaHeaderLength])^);

  RecordsLength := word(Addr(TmpBufer[DeltaRecordsLength])^);

  {Read Field Descriptors one to one}

  {Count number of record fields}

  DBDescr.TotalFields := 0;

  repeat

    BlockRead(DBF,TmpBufer,SmallBlockLength);

    inc(DBDescr.TotalFields);

  until (TmpBufer[0] = EndOfFields);

  dec(DBDescr.TotalFields);

  {Reserve Space for Field Descriptors}

  with DBDescr do

  begin

    if (not GetMemCheck(FieldDArrayPtr,

                        SizeOf(FieldD)*TotalFields)) then

    begin

      AbortWithRelease(1,0);

      exit;

    end;

  end;

  {Read Field Descriptors one to one}

  Seek(DBF,SmallBlockLength);

  with DBDescr do

  begin

    RecSize := 1;

    {Each record start with deleted(*)/present( ) marker}

    for i := 1 to TotalFields do

    with FieldDArrayPtr^[i] do

    begin

      BlockRead(DBF,TmpBufer,SmallBlockLength);

      {Get Field Name}

      FName := '';

      j := 0;

      while TmpBufer[j] <> 0 do

      begin

        FName := FName + chr(TmpBufer[j]);

        inc(j);

      end;

      FType   := FieldType(pred(Pos(chr(TmpBufer[DeltaFType]),

                                              FieldStrType)));

      FLength := TmpBufer[DeltaFLength];

      inc(RecSize,FLength);

      FDec    := TmpBufer[DeltaFDec];

    end;

  end;

  {Set file pointer on first record }

  Seek(DBF,HeaderLength);

  if (not DBDescr.MinRamUsing) then

  {...and reserve space for array of pointers on records...}

  if (not GetMemCheck(DBDescr.RecArrayPtr,

                      DBDescr.TotalRecs*SizeOf(RecPtr))) then

  begin

    AbortWithRelease(2,0);

    exit;

  end;

  with DBDescr do

  begin

    if MinRamUsing then

    begin

      RandAssign(TmpFile);

                        rewrite(TmpFile,1);

      if not GetMemCheck(CurRec,RecSize) then

      begin

        SetDBError(100);

        exit;

      end;

      CurRecNumber := -1;

    end;

    {...and for records too.}

    UsedRecs := 0;

    for i := 1 to TotalRecs do

    begin

      if MinRamUsing then

      begin

        BlockRead(DBF,CurRec^,RecSize);

                                WriteRecord(DBDescr,i);

      end

      else

      begin

        if (not GetMemCheck(RecArrayPtr^[i],RecSize)) then

        begin

          AbortWithRelease(3,i-1);

          exit;

        end;

        BlockRead(DBF,RecArrayPtr^[i]^,RecSize);

      end;

      if (GetMarker(DBDescr,i) = RecordPresent) then

         inc(UsedRecs);

    end;

  end;

  Close(DBF);

  Sort(DBDescr);

end;

procedure WriteDB(var DBDescr : DBD; DBFile : string);

const

  NullWord : word = $0;

var

  DBF     : file;

  i       : longint;

  RPtr    : RecPtr;

begin

  with DBDescr do

  begin

    if (UsedRecs = 0) and (TotalFields = 0) then

    begin

      SetDBError(106);

      exit;

    end;

    {forming header (see DBase III file structure)}

    FillChar(TmpBufer,SmallBlockLength,chr(0));

    TmpBufer[0] := DB3Id;

    with LastUpdate do

    begin

                        TmpBufer[DeltaDate  ] := YY;

                        TmpBufer[DeltaDate+1] := MM;

                        TmpBufer[DeltaDate+2] := DD;

    end;

    longint(Addr(TmpBufer[DeltaTotalRecs    ])^) := UsedRecs;

    word   (Addr(TmpBufer[DeltaHeaderLength ])^) :=

                   succ(succ(TotalFields)*32);{header length}

    word   (Addr(TmpBufer[DeltaRecordsLength])^) := RecSize;

                                            {records length}

    assign(DBF,DBFile);

                {I-}

    rewrite(DBF,1);

    {$I+}

    if IOResult <> 0 then

    begin

      SetDBError(IOResult);

      Exit;

    end;

    BlockWrite(DBF,TmpBufer,SmallBlockLength);

    for i := 1 to TotalFields do

    with FieldDArrayPtr^[i] do

    begin

      FillChar(TmpBufer,SmallBlockLength,chr(0));

      move(Addr(FName[1])^,TmpBufer,length(FName));{Field Name}

      TmpBufer[DeltaFType] :=

             byte(FieldStrType[succ(ord(FType))]); {Field Type}

      TmpBufer[DeltaFLength] := FLength;

      TmpBufer[DeltaFDec] := FDec;

      BlockWrite(DBF,TmpBufer,SmallBlockLength);

    end;

    BlockWrite(DBF,EndOfFields,1);

    {Now header is written,let write contents of records}

    for i := 1 to UsedRecs do {Not write deleted records}

    begin

      RPtr := GetRecPtr(DBDescr,i);

      BlockWrite(DBF,RPtr^,RecSize);

    end;

  end;

  BlockWrite(DBF,EndOfFile,1);

  close(DBF);

end;

procedure WriteUpDate(var DBDescr : DBD);

var

  Year,

  Month,

  Day,

  WDay : word;

begin

  GetDate(Year,Month,Day,WDay);

  with DBDescr.LastUpdate do

  begin

    YY := Year mod 100;

    MM := Month;

    DD := Day;

  end;

end;

procedure ReAssign(var F1,F2 : file);

begin

        close(F1);

        erase(F1);

        close(F2);

        assign(F1,FileRec(F2).Name);

        reset(F1,1);

end;

procedure CreateDB(var DBDescr          : DBD;

                                                                                         TotRecs                : longint;

                                                                                         var FieldDescr : FieldD;

                       MinRamUse  : boolean);

var

  i : longint;

begin

  with DBDescr do

  begin

                MinRamUsing := MinRamUse;

                WriteUpdate(DBDescr);

    TotalRecs := TotRecs; {Reserve space for recs...}

    UsedRecs  := 0;       {...but mark it like unused}

    TotalFields := 1;

    {There is one field (FieldDescr) at the beginning}

    RecSize := succ(FieldDescr.FLength);{+Del/Present marker}

    if (not GetMemCheck(FieldDArrayPtr,SizeOf(FieldD))) then

    begin

      SetDBError(100);

      exit;

    end;

    FieldDArrayPtr^[1] := FieldDescr;

       if MinRamUsing then

       begin

               RandAssign(TmpFile);

               rewrite(TmpFile,1);

               if not GetMemCheck(CurRec,RecSize) then

               begin

                       SetDBError(100);

                       exit;

               end;

               FillChar(CurRec^,RecSize,' ');

               CurRec^[0] := RecordDeleted;

               CurRecNumber := -1;

       end

       else

       if (not GetMemCheck(RecArrayPtr,TotRecs*SizeOf(RecPtr)))

          then begin

               SetDBError(100);

               exit;

       end;

       for i := 1 to TotalRecs do

       begin

          if MinRamUsing then WriteRecord(DBDescr,i)

          else

          begin

            if (not GetMemCheck(RecArrayPtr^[i],RecSize)) then

            begin

                    SetDBError(100);

                    exit;

            end;

            FillChar(RecArrayPtr^[i]^,RecSize,' ');

          end;

          PutMarker(DBDescr,i,RecordDeleted);

       end;

  end;

end;

procedure RemoveDB{From Memory}(var DBDescr : DBD);

var

  i : longint;

  RPtr : RecPtr;

begin

  with DBDescr do

        begin

          if MinRamUsing then

          begin

                  close(TmpFile);

                  erase(TmpFile);

          end

          else

          begin

            {1.Release records area}

            for i := 1 to TotalRecs do

            begin

                    RPtr := GetRecPtr(DBDescr,i);

                    FreeMemCheck(RPtr,RecSize);

            end;

            {2.Release pointers to records area}

            FreeMemCheck(RecArrayPtr,SizeOf(RecPtr)*TotalRecs);

          end;

    {3.Release Field Descriptor area}

    FreeMemCheck(FieldDArrayPtr,SizeOf(FieldD)*TotalFields);

  end;

end;

procedure AddField(var DBDescr : DBD;var FieldDescr : FieldD);

var

        TmpPtr           : pointer;

        i                : longint;

        NewTmpFile : file;

        NewRecSize : longint;

        CRec             : RecPtr;

begin

  {Adding field mean :}

  with DBDescr do

  begin

    {1.Add field descriptor to FieldDescriptorArray,i.e.:}

    {1a.Reserve space for new(increased) F.D.Array}

    if (not

       GetMemCheck(TmpPtr,succ(TotalFields)*SizeOf(FieldD)))

    then begin

      SetDBError(100);

      exit;

    end;

    {1b.Copy old F.D.Array to new}

    move(FieldDArrayPtr^,TmpPtr^,TotalFields*SizeOf(FieldD));

    {1c.Release old F.D.Array}

    FreeMemCheck(FieldDArrayPtr,TotalFields*SizeOf(FieldD));

    {1d.Reassign pointer}

    FieldDArrayPtr := TmpPtr;

    {1e.Increase TotalFields}

    inc(TotalFields);

    {1f.Add new F.D.}

    FieldDArrayPtr^[TotalFields] := FieldDescr;

                NewRecSize := RecSize+FieldDescr.FLength;

                if MinRamUsing then

                begin

                  FreeMemCheck(CurRec,RecSize);

                  if not GetMemCheck(CurRec,NewRecSize) then

                  begin

                          SetDBError(100);

                          exit;

                  end;

                  CurRecNumber := -1;

                  RandAssign(NewTmpFile);

                  rewrite(NewTmpFile,1);

                end;

{2.Increase area for each record on value FLength of new Field.

   Steps 2a-2e are equivalent of ones 1a-1e                   }

    for i := 1 to TotalRecs do

      if MinRamUsing then

      begin

              FillChar(CurRec^,NewRecSize,' ');

              CRec := GetRecPtr(DBDescr,i);

              Seek(NewTmpFile,pred(i)*(NewRecSize));

              BlockWrite(NewTmpFile,CurRec^,NewRecSize);

      end

      else

      begin

      {2a.}

             if (not GetMemCheck(TmpPtr,NewRecSize)) then

      begin

        SetDBError(100);

        exit;

      end;

      {2b.}

                        FillChar(TmpPtr^,NewRecSize,' ');

      move(RecArrayPtr^[i]^,TmpPtr^,RecSize);

      {2c.}

                        FreeMemCheck(RecArrayPtr^[i],RecSize);

      {2d.Reassign pointer}

      RecArrayPtr^[i] := TmpPtr;

      {2e.}

    end;

              inc(RecSize,FieldDescr.FLength);

              if MinRamUsing then ReAssign(TmpFile,NewTmpFile);

  end;

end;

procedure DeleteField(var DBDescr : DBD;FieldN : longint);

var

  i           : longint;

  Moving      : boolean;

  DField      : FieldD;

        ShiftLength : longint;

        CRec                            : RecPtr;

        NewTmpFile      : file;

        NewRecSize      : longint;

begin

  {Delete field mean :}

  with DBDescr do

  begin

    if FieldN > TotalFields then

    begin

      SetDBError(104);

      exit;

    end;

    Moving := (FieldN < TotalFields);

    DField := FieldDArrayPtr^[FieldN];

      ShiftLength := 0;

      for i := succ(FieldN) to GetMaxField(DBDescr) do

      inc(ShiftLength,FieldDArrayPtr^[i].FLength);

      {2.Decrease area for each record on value FLength of

      deleted Field. Steps 2a-2c are equivalent of ones 1a-1c}

                NewRecSize := RecSize - DField.FLength;

                if MinRamUsing then

                begin

                        RandAssign(NewTmpFile);

                        rewrite(NewTmpFile,1);

                end;

                for i := 1 to TotalRecs do

                if MinRamUsing then

                begin

                        CRec := GetRecPtr(DBDescr,i);

      if Moving then move(GetFieldPtr(DBDescr,i,succ(FieldN))^,

                          GetFieldPtr(DBDescr,i,FieldN)^,

                                                                                                        ShiftLength);

                     Seek(NewTmpFile,NewRecSize*pred(i));

                     BlockWrite(NewTmpFile,CRec^,NewRecSize);

                end

                else

                begin

      {2a.}

      if Moving then move(GetFieldPtr(DBDescr,i,succ(FieldN))^,

                          GetFieldPtr(DBDescr,i,FieldN)^,

                                                                                                        ShiftLength);

      {2b.}

     FreeMemCheck(RecArrayPtr^[i]^[NewRecSize],DField.FLength);

      {2c.}

    end;

           dec(RecSize,DField.FLength);

           if MinRamUsing then

           begin

             ReAssign(TmpFile,NewTmpFile);

             FreeMemCheck(CurRec^[NewRecSize],DField.FLength);

           end;

    {1.Delete field descriptor from FieldDescriptorArray,i.e.:}

    {1a.if FieldN is not last Field then move all fields to

    record begin}

    if Moving

    then move(FieldDArrayPtr^[succ(FieldN)],

              FieldDArrayPtr^[FieldN],

              SizeOf(FieldD)*(TotalFields-FieldN));

    {1b.Release deleted field space}

    FreeMemCheck(FieldDArrayPtr^[TotalFields],SizeOf(FieldD));

    {1c.Decrease TotalFields}

    dec(TotalFields);

    if (TotalFields = 0) then FieldDArrayPtr := nil;

  end;

end;

function AddRec(var DBDescr : DBD) : longint;

var

  TmpPtr : pointer;

  i      : longint;

begin

  with DBDescr do

  begin

    {If there is no space from previosly deleted record,so}

    if TotalRecs = UsedRecs then

                begin

        {heavy - we must reserve space for new record

          by the following steps :}

                        if MinRamUsing then

      begin

        FillChar(CurRec^,RecSize,' ');

        CurRec^[0] := RecordDeleted;

        CurRecNumber := -1;

        for i := 1 to AddRecFactor do

                         WriteRecord(DBDescr,UsedRecs+i);

      end

      else

      begin

        {1.Create new RecordArrayPtr}

        if (not GetMemCheck(TmpPtr,

        SizeOf(RecPtr)*(TotalRecs+AddRecFactor))) then begin

          SetDBError(100);

          exit;

        end;

        {2.copy in it contents of old RecordArrayPtr}

        move(RecArrayPtr^,TmpPtr^,SizeOf(RecPtr)*TotalRecs);

        {3.free old RecordArrayPtr}

        FreeMemCheck(RecArrayPtr,SizeOf(RecPtr)*TotalRecs);

        {4.Reassign RecArrayPtr}

        RecArrayPtr := TmpPtr;

        {5.Create and initialise AddRecFactor new records}

        for i := 1 to AddRecFactor do

        begin

         if(not GetMemCheck(RecArrayPtr^[TotalRecs+i],RecSize))

         then begin

           SetDBError(100);

           exit;

         end;

         FillChar(RecArrayPtr^[TotalRecs+i]^,RecSize,' ');

         PutMarker(DBDescr,TotalRecs+i,RecordDeleted);

        end;

      end;

      {6. Set correct TotalRecs}

      inc(TotalRecs,AddRecFactor);

                end;

    inc(UsedRecs);

    PutMarker(DBDescr,UsedRecs,RecordPresent);

    AddRec := UsedRecs;

  end;

end;

procedure DeleteRec(var DBDescr : DBD;RecN : longint);

begin

  with DBDescr do

  begin

    if RecN > UsedRecs then

    begin

      SetDBError(105);

      exit;

    end;

    PutMarker(DBDescr,RecN,RecordDeleted);

    Dec(UsedRecs);

    Sort(DBDescr);

  end;

end;

function DeltaField(var DBDescr : DBD;FieldN :longint):longint;

var

  CurDeltaField : longint;

  i : longint;

  CurFieldD : FieldD;

begin

  CurDeltaField := 1;

  { First byte of each record is marker present/deleted record}

  for i := 1 to pred(FieldN) do

  begin

    CurFieldD := GetFieldDPtr(DBDescr,i)^;

    inc(CurDeltaField,CurFieldD.FLength);

  end;

  DeltaField := CurDeltaField;

end;

function GetFieldPtr(var DBDescr : DBD;RecN : longint;FieldN :

                     longint) : FieldPtr;

var

        CRec : RecPtr;

begin

        CRec := GetRecPtr(DBDescr,RecN);

        GetFieldPtr := Addr(CRec^[DeltaField(DBDescr,FieldN)]);

end;

function GetFieldDPtr(var DBDescr:DBD;FieldN:longint):

                                                     FieldDPtr;

begin

  GetFieldDPtr := Addr(DBDescr.FieldDArrayPtr^[FieldN]);

end;

function GetMaxRec(var DBDescr : DBD) : longint;

begin

  GetMaxRec := DBDescr.TotalRecs;

end;

function GetMaxField(var DBDescr : DBD) : longint;

begin

  GetMaxField := DBDescr.TotalFields;

end;

function GetField(var DBDescr : DBD;RecN : longint;FieldN :

                                            longint) : string;

var

        TmpVar  : CType;

        FPtr    : FieldPtr;

        FDPtr   : FieldDptr;

begin

        FPtr := GetFieldPtr(DBDescr,RecN,FieldN);

        FDPtr := GetFieldDPtr(DBDescr,FieldN);

  byte(TmpVar[0]) := FDPtr^.FLength;

  move(FPtr^,addr(TmpVar[1])^,FDPtr^.FLength);

        GetField := TmpVar;

end;

function GetRField(var DBDescr : DBD;RecN : longint;FieldN :

                                               longint) : real;

var

  TmpN   : NType;

  TmpR   : real;

  code   : integer;

begin

        TmpN := GetField(DBDescr,RecN,FieldN);

  Val(TmpN,TmpR,code);

  if code <> 0 then

  begin

    SetDBError(103);

    exit;

  end;

  GetRField := TmpR;

end;

function GetBField(var DBDescr : DBD;RecN : longint;FieldN :

                                      longint) : boolean;

var

  TmpL  : LType;

begin

        TmpL := GetField(DBDescr,RecN,FieldN);

  if UpCase(TmpL[1]) in ['Y','T']

  then GetBField := true

  else GetBField := false;

end;

function GetDField(var DBDescr : DBD;RecN : longint;FieldN :

                                     longint) : DType;

var

        TmpD  : DType;

begin

        GetDField := GetField(DBDescr,RecN,FieldN);

end;

procedure PutField(var DBDescr : DBD;RecN : longint;FieldN :

                                  longint;Field : string);

var

  FPtr    : FieldPtr;

  FDPtr   : FieldDPtr;

  RPtr    : RecPtr;

begin

 RPtr := GetRecPtr(DBDescr,RecN);

 FPtr := GetFieldPtr(DBDescr,RecN,FieldN);

       FDPtr := GetFieldDPtr(DBDescr,FieldN);

       FillChar(FPtr^, FDPtr^.Flength, ' ');

 move(addr(Field[1])^,FPtr^,Min(FDPtr^.FLength,length(Field)));

 PutRecPtr(DBDescr,RecN,RPtr);

end;

procedure PutRField(var DBDescr : DBD;RecN : longint;FieldN :

                                        longint;Field : real);

var

  FDPtr   : FieldDPtr;

  TmpN    : NType;

begin

  FDPtr := GetFieldDPtr(DBDescr,FieldN);

  str(Field : FDPtr^.FLength : FDPtr^.FDec,TmpN);

        PutField(DBDescr,RecN,FieldN,TmpN);

end;

procedure PutDField(var DBDescr : DBD;RecN : longint;FieldN :

                               longint;Field : DType);

var

  FDPtr   : FieldDPtr;

        TmpD    : DType;

begin

        PutField(DBDescr,RecN,FieldN,Field);

end;

procedure PutBField(var DBDescr : DBD;RecN : longint;FieldN :

                             longint;Field : boolean);

var

  TmpL     : LType;

begin

  if Field

  then TmpL := 'T'

  else TmpL := 'F';

        PutField(DBDescr,RecN,FieldN,TmpL);

end;

function CheckFieldD(var FD : FieldD) : boolean;

var

  Res : boolean;

begin

  Res := false;

  with FD do

  begin

    if FType in [C,L,M,D] then FDec := 0;

    case FType of

      C : if FLength in [1,254] then Res := true;

      L : FLength := 1;

      M : FLength := 10;

      D : FLength := 8;

      N : if

          (FLength in [1,19])

          and

          (FDec in [1,15])

          and

          (FLength - FDec >= 2)

          then Res := true;

    end;

  end;

  CheckFieldD := Res;

end;

const

  WS = '*';

  SS = '?';

{$F+}

function Optimize(St : string) : string;external;

{$F-}

{$L DB3TOOL}

procedure OptimizePat(var SourcePat : Patrn);

{ Optimize Pattern means processing two or more contiguos

  WildCards like this :

  1) "**" is replaced by one "*"

  2) "*?" is swapped like "?*"

  Result in this procedure is that "*" can't be followed "*" or

  "?"  (It's more easy way to process By Mask Comparing) }

var

  i       : byte;

  SPtr    : ^string;

begin

  if (Not (SourcePat.CFType in [C,D])) then exit;

  SourcePat.CP := Optimize(SourcePat.CP);

(*

  SPtr := Addr(SourcePat.CP);

  i := 1;

  while (i < byte(SPtr^[0])) do

  begin

    if (SPtr^[i] = WS) then

    case SPtr^[succ(i)] of

      WS :

      begin

        delete(SPtr^,i,1);

        dec(i);

      end;

      SS :

      begin

        SPtr^[i]    := SS;

        SPtr^[succ(i)] := WS;

      end;

    end;

    inc(i);

  end;

*)

end;

procedure CreateMask(var MD : MaskD;var Pat : Patrn);

begin

  with MD do

  begin

    if (not GetMemCheck(PatrnAPtr,SizeOf(Patrn))) then

    begin

      SetDBError(100);

      exit;

    end;

    DBPtr         := nil;

    StartRec      := 1;

    TotalPatrn    := 1;

    OptimizePat(Pat);

    PatrnAPtr^[1] := Pat;

  end;

end;

procedure DeleteMask(var MD : MaskD);

begin

  with MD do

  begin

    FreeMemCheck(PatrnAPtr,SizeOf(Patrn)*TotalPatrn);

    TotalPatrn := 0;

    DBPtr      := nil;

  end;

end;

procedure AddPatrn(var MD : MaskD;var Pat : Patrn);

var

  TmpPtr : pointer;

begin

  with MD do

  begin

    if (not GetMemCheck(TmpPtr,SizeOf(Patrn)*succ(TotalPatrn)))

    then begin

      SetDBError(100);

      exit;

    end;

    move(PatrnAPtr^,TmpPtr^,SizeOf(Patrn)*TotalPatrn);

              FreeMemCheck(PatrnAPtr,SizeOf(Patrn)*TotalPatrn);

    PatrnAPtr := TmpPtr;

    inc(TotalPatrn);

    OptimizePat(Pat);

    PatrnAPtr^[TotalPatrn] := Pat;

  end;

end;

{$F+}

function WildEqStr(W,S : string) : boolean;external;

{$F-}

(*

var

  Res : boolean;

  Tmp,

  Wi,

  Si,

  Wl,

  Sl  : byte;

begin

  Res := true;

  WildEqStr := true;

  Wl  := length(W);

  Sl  := length(S);

  Wi  := 1;

  Si  := 1;

  while Res

        and

        ((Wl-Wi) <= (Sl-Si))

        and

        (Wl >= Wi) do

  begin

    if (W[Wi] = SS) then

    { В WildCard встpетился "?" }

    begin

      inc(Wi);

      inc(Si);

    end

    else

    if (W[Wi] = WS) then

    { В WildCard встpетился "*" }

    begin

      if (Wi = Wl) then exit;

      inc(Wi);

      { Возьмем из WildCard следующий за "*" знак }

      { Есть ли такой же знак в String после Si ? }

      Tmp := Pos(W[Wi],Copy(S,Si,succ(Sl-Si)));

      if Tmp = 0

      then Res := false

      else

      begin

        inc(Si,pred(Tmp));

        inc(Si);

        inc(Wi);

      end;

    end

    else

    begin

      Res := W[Wi] = S[Si];

      inc(Si);

      inc(Wi);

    end;

  end;

  if (Wl <> pred(Wi)) or (Sl <> pred(Si)) then Res := false;

  WildEqStr := Res;

end;

*)

function  FindNextRecord  (var MD : MaskD) : longint;

var

  i,

  j       : byte;

  Find     : boolean;

  FieldsOk : boolean;

  RecN     : longint;

  PatrnN   : byte;

  TestN    : real;

  TestC    : CType;

  TestD    : DType;

begin

  RecN := 0;

  Find := false;

  with MD,DBPtr^ do

  { Цикл по записям }

        while (not Find) and (UsedRecs >= StartRec) do

  begin

                FieldsOk := true;

    PatrnN  := 1;

    { Цикл по полям в записи }

    while FieldsOk and (TotalPatrn >= PatrnN) do

    begin

      { Пpовеpка записи StartRec по обpазцу PatrnN }

      with PatrnAPtr^[PatrnN] do

      case CFType of

        L :

        FieldsOk := GetBField(DBPtr^,StartRec,CFieldN) = LP;

        N :

        begin

          TestN := GetRField(DBPtr^,StartRec,CFieldN);

          FieldsOk := (TestN >= NP[1]) and (TestN <= NP[2]);

        end;

      D,C :

     FieldsOk:=WildEqStr(DP,GetField(DBPtr^,StartRec,CFieldN));

      end;

      inc(PatrnN);

    end;

    if FieldsOk then

    begin

      Find := true;

      RecN := StartRec;

    end;

    inc(StartRec);

  end;

  FindNextRecord := RecN;

end;

function FindFirstRecord (var MD : MaskD;var DBDescr : DBD) :

                                               longint;

begin

  if (DBDescr.TotalRecs < 1) or (DBDescr.TotalFields < 1)

  then FindFirstRecord := 0

  else

  with MD do

  begin

    DBPtr     := Addr(DBDescr);

    StartRec  := 1;

    FindFirstRecord := FindNextRecord(MD);

  end;

end;

end.


Командный режим СУБД FoxPro

  Запуск FoxPro осуществляется из ДОС командой fox. По этой

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

уж и редко, Вы запустите одну из двух версий FoxPro -

стандартную (foxprol) или расширенную (foxprolx).

  "Расширенность" заключается в использовании всего ОЗУ. FoxPro

Extended версии FoxPro 2.0 выполняется на ЭВМ с 386 или 486

процессорами, имеющими не менее 1.5 MБ оперативной памяти, с

некоторым снижением быстродействия. При наличии менее 1.5 MБ

оперативной памяти, версию FoxPro Extended использовать

невозможно; целесообразно пользоваться стандартной версией

Foxpro.

  Подкачка по обращению имеет  более сложную  методику замены,

чем загрузка сегмента, используемая стандартной версией; это

позволяет выполнять 1.6 MБ программу (FoxPro), в обьеме памяти

менее 1.6MB. При наличии менее 2MБ памяти доступной FoxPro

после его запуска, будет выдано сообщение, указывающее

выполнение подкачки  по  обращению. FoxPro будет выполняться

лучше при обеспечении достаточным количеством памяти. В этом

случае работа будет осуществляться без подкачки по

обращению, т.к. в этом случае FoxPro сразу будет размещен в

памяти.

  Hапример, когда доступно 2MБ памяти, память распределяется

между FoxPro кодом и памятью пользователя следующим образом:

   FoxPro код: 1.33MB

   Память Пользователя : .67MB

  При уменьшении оперативной памяти, память пользователя будет

занимать до 1/3 общего обьема оперативной памяти. Следовательно,

например, при 1.5MБ оперативной памяти программам пользователя

будет доступно 500KБ память, в то время как FoxPro задействует

1MB. Если будет доступно 1MB, пользователю будет выделяться

.33MB памяти, а FoxPro будет занимать .67MB. Уменьшение доступной

оперативной памяти  для FoxPro, приводит к замедлению

выполняемых программ в его среде. Вывод - чем больше у Вас

памяти, тем комфортнее Вы будете себя чувствовать при работе.

  После запуск Вы сразу же попадаете в командное окно, о чем

недвусмысленно говорит его заголовок Command. В любой момент,

находясь в среде БД, Вы можете вызвать его комбинацией Ctrl+F2

или через пункт меню Window - Command. В этом окне возможен

вызов почти всех конструкций языка FoxPro, за исключением циклов

и условных операторов. Набрав в окне команд слово HELP или нажав

клавишу F1, Вы получите помощь.

  FoxPro нечувствителен к регистру команд, что сближает его с

Паскалем. Состав (синтаксис) команд включает некоторые

закрепленные за ними слова (названия команд, функций и т.п.),

которые нежелательно применять в других целях, например в

качестве имен. Такие слова называтся ключевыми, и их можно

записывать сокращенно - до четырех символов. На практике,

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

учебных целях мы будем старться указывать ключевые слова

целиком.

  Исключение составляют ключевые слва NOSHADOW и NOSHOW, для

которых, как видите, следует указавть не менее пяти символов.

Кроме того, ключевые слова вводяться полностью в командах, где

на их месте могут быть указаны также имена объектов.

  Команды могут содержать следующие знаки операций:

  МАТЕМАТИЧЕСКИЕ (перечислены в порядке их приоритетов):

  1. ** или ^ - возведение в степень

  2. * - умножение, / - деление, % - остаток от деления

  3. + - сложение, - - вычитание.

  ЛОГИЧЕСКИЕ

  1. NOT или !

  2. AND

  3. OR

  ОТНОШЕНИЯ

  <

  >

  =    - равно

  #    - не равно

  <=

  >=

  Знаки отношений применимы как к числовым выражениям, так и к

датам и символьным выражениям. Если сравниваются символьные

строки разной длины, сравнение выполняется по длине второго

выражения, то есть выражения, стоящего справа от знака

отношения. Остаток левого операнда, превышающий длину правого

операнда, игнорируется.

  Таким образом, считаются истинными (.T.) отношения вида

  "Ягодка А."="Ягодка А." и 'Ягодка А.'=[Яго],

но ложными

  "Яго"='Ягодка А.' и 'Ягодка А.'=[Яго   ]

  Эта чрезвычайно важная особенность дает при необходимости

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

например фамилии по первым буквам. Тогда более короткое

выражение должно стоять справа от знака "=" и из него обычно

должны быть удалены концевые пробелы.

  Кроме того, есть операции отношения только для символьных

выражений:

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

       длине, и по содержанию, включая пробелы.

   $ - <вырC1> $ <вырC2>. Функция возвращает  значение "истина"

       (.T.), если <вырC1> содержится в <вырC2> и  значение

       "ложь" (.F.) в противном случае. Так как символьные

       строки и текстовые поля обрабатываются в FoxPro

       одинаково, то, что можно проделать с символьными

       строками или символьными полями баз данных, можно

       проделать и с  текстовыми полями. К  примеру, следующее

       использование  функции  $ является полностью допустимым

       (предполагается,   что MEMO_FLD - текстовое поле базы

       данных):  LIST FOR "FOX" $ UPPER(memo_fld)

  =  - = <выр1>[,<выр2>...,<вырN>] - оценка значения списка

       выражений. Команда = просто оценивает список выражений.

       Она  практически  полезна,  когда   некоторая функция,

       безразлично,  функция  FoxPro или определенная

       пользователем, должна  выполнить действие  с  необходимым

       эффектом,  но   нет необходимости    присваивать

       возвращаемое функцией   значение   какой либо  временной

       переменной или полю.

         Hапример, для  простого  переключения  режима

       вставки/замещения можно использовать команду:

         = INSMODE(.T.)

       Обычно функция INSMODE() возвращает логическое значение

       .T. или .F.  .  В  данном  примере  функция  выполняется,

       но возвращаемое значение подавляется. Следует заметить,

       что   знак   равенства   "="   может также использоваться

       как оператор сравнения  в логических  выражениях.  Кроме

       того,  он  может  использоваться  для  присвоения

       значений массивам и временным переменным.

\ | \\-\ | \\ <строка текста> - вывод строк текста, результатов

       выражений и функций,  содержимого переменных памяти.

       Команды \ и \\ \ осуществляют слияние текста в FoxPro.

       Слитый текст можно выводить в файл для создания писем или

       программ. Использование  команды  \  осуществляет

       возврат  каретки в конце выводимой строки и переход на

       новую строку.  Команда \\ не ставит признак  возврата

       каретки в конце  выводимой   строки  и   не осуществляет

       перевод на новую строку.

         Выводимый текст может  содержать  выражения, которые

       окружаются разделителями <<  >>. Вывод значения

       выражения происходит в том случае,  если   выражение

       окружено разделителями  (<<   >>  по умолчанию) и

       TEXTMERGE установлено по команде SET в ON.

  СЦЕПЛЕНИЯ :

 + - соединение двух или более строк в одну;

 - - то же, но пробелы в конце строки, предшествующей знаку "-",

     помещаются в конец итоговой строки.

  Команды FoxPro, ориентированные на обработку файлов базы

данных, в самом общем виде имеют следующий синтаксис :

  НАЗВАНИЕ [<границы>] [<список выражений>] [FOR <условие>]

           [WHILE <условие>]

  Здесь:

  НАЗВАНИЕ  -         имя команды

  <границы> -         границы действия команды, которые могут

                      иметь одно из следующих значений:

                      ALL        - все записи базы данных

                      REST       - все записи,начиная с текущей,

                                   до конца базы

                      NEXT <N>   - следующие N записей,начиная с

                                   текущей

                      RECORD <N> - запись номер N

  FOR <условие>   - выполнение команды только для записей,

                    отвечающих <условию>;

  WHILE <условие> - выполнение команды только до тех пор, пока

                    не перестанет выполняться <условие>.

  Слова FOR и WHILE могут присутствовать в команде одновременно.

В этом случае WHILE-условие имеет приоритет перед FOR-условием.

  Кроме того, в FoxPro имеется большая группа команд, которые

целесообразно выделить в отдельный вид:

  SET <параметр команды> TO <значение параметра>

  SET <параметр команды> OFF/ON

  Константы различных типов данных отображаются в командах

следующим образом:

Символьные константы выделяются апострофами, кавычками или

     квадратными скобками. В случае, если сами символы -

     ограничители являются элементами данных, вся строка должна

     быть заключена в другие разрешенные ограничители, например

     [Слава по прозвищу "Гений"].

Константы типа дата берутся в фигурные скобки (например,

     {06.05.76}). Пустая дата ({  .  .  }) может быть задана

     одними фигурными скобками {}.

Логические константы T и F в тексте программ обрамляются точками

     (.T.,.F.,.t.,.f.), так же как и имена логических операций

     .OR., .AND.

  При редактировании и просмотре длинных команд в окне Command

можно использовать строки продолжения. Для продолжения команды в

следующей строке между ключевыми словами или фразами нужно

поставить символ ";", затем нажать "Enter" и продолжить ввод

команды в следующей строке. Следует отметить, что таким же

образом можно представлять длинные командные строки в

программах.

  Если команда состоит из нескольких строк, то комментарии &&

нельзя размещать после символа ";". Комментарий && может

размещаться только в последней строке команды.

  При выполнении команды, состоящей из нескольких строк курсор

устанавливается на любую строку команды и нажимается Enter.  Для

вставки новой строки в команду, состоящую из нескольких строк

необходимо нажать Ctrl+Enter.

  Использование отступов улучшает читабельность команд,

состоящих из нескольких строк.  Для получения отступа в окне

Command необходимо перед вводом команды нажать клавишу Tab.

Если строка в окне Command введена с отступом, можно

автоматически устанавливать курсор в следующей строке в такую же

начальную позицию по нажатию Ctrl+Enter.

  Максимальное количество символов в командной строке - 2048.

При использовании макроподстановок максимальное число символов в

макро - 255 (после расширения).  В команде может использоваться

несколько макроподстановок в одной командной строке, но длина

строки при этом не должна превышать 2048 символов.


Создание файлов БД
Создание файлов базы данных

  Создание файла базы данных включает два этапа: создание

структуры файла и его заполнение данными.

  В FoxPro можно оюрабатывать сразу несколько файлов баз данных

(до 25). Каждый такой файл и все его индексы открываются в своей

отдельной рабочей области. Переход из области в область

осуществляется командой

  SELECT <рабочая область | псевдоним>

  Рабочие области идентифицируются номерами с 1 до  25  или

буквами  А - J (первые десять). Если в рабочей области  уже

открыт  файл  базы  данных,  то выбрать  рабочую  область

можно  и  по   ее псевдониму.

  По умолчанию активна рабочая область 1. Если в  команде SELECT

в качестве  параметра указать 0,  активизируется свободная

рабочая область с наименьшим номером.

  Команду SELECT можно использовать для переходов между рабочими

областями, так что файл базы данных, открытый в заданной рабочей

области, становится активным. Можно изменить текущую  рабочую

область,  задав   псевдоним имени  файла,  открытого  в  другой

рабочей области.  Если  при  открытии  файла не задан псевдоним,

имя  файла  (без расширения) по умолчанию принимается в качестве

псевдонима.

  Обычно команды выводят или изменяют  значения полей  активного

файла  базы  данных. Тем не менее, можно  задать поля  из

других  рабочих областей,   содержащих    связанные    файлы,

используя следующий синтаксис:

     <псевдоним> -> имя поля

или

     <псевдоним>.имя поля

  В каждой рабочей области поддерживается  свой указатель

записи.  Переходы  между  рабочими областями  не  влияют  на

позиции указателей записи.      Команды,   изменяющие    позицию

указателя   записи,   действуют   только  для активного   файла

базы  данных,   если  не установлены   отношения   файлов   с

помощью команды SET RELATION TO.

  Для создания структуры файла БД попробуйте в командном окне

ввести команду CREATE. Ее синтаксис очень прост:

  CREATE [<файл> | ?]

  Если в команде не указано <имя файла>, или если в команду

включена опция  ?, то на экране  воспроизводится  дивлог Save

as.  Затем система  запрашивает ввод  имени, типа, длины и

количества десятичных знаков  каждого из  полей  записи  нового

файла базы данных.

  При необходимости командой MODIFY STRUCTURE можно

модифицировать структуру файла базы данных. Эта команда

отображает на экране структуру активного файла  базы данных  и

позволяет  ее полноэкранное редактирование, включая добавление

и удаление полей записи, изменение имен,  длин  и  типов

полей. Модификация структуры баз данных может также выполняться

через опцию Setup... вертикального меню Database.

  Перед модификацией структуры файла, FoxPro автоматически

создает страховую копию этого файла. После завершения

редактирования выполняется перенос данных из страховой копии (с

расширением .BAK) в файл с модифицированной структурой. Также

создается страховая копия (с расширением .TBK) файла

текстовых полей (.DBT).

  Просмотреть структуру любой БД Вы можете с помощью команды

DISPLAY STRUCTURE:

  DISPLAY STRUCTURE [IN <псевдоним>]

  [TO PRINTER | TO FILE <файл>]

  [NOCONSOLE]                          - обеспечивает вывод

                                         структуры активного

                                         файла базы данных.

  Если перед  описываемой командой  была выдана команда  SET

FIELDS  TO,   то  с полями, доступными к  выводу по  этой

команде,  будет выведен символ > "больше, чем". После каждого

поля выводятся его тип (символьный, числовой, дата, логический

или memo) и размер. Если поле числовое, выводится количество

десятичных знаков.

  Отображаются также количество записей в базе и дата и дата

последней корректировки. Если база содержит memo поле, выводится

размер его блока.  Также отображается общая длина всех полей.

  База может иметь структурный индекс, который открывается

вместе с ней.  Если тэг в структурном индексе имеет то же имя,

что и поле в базе данных и индексация выполнена по этому полю,

после поля признак упорядочения (по возрастанию (asc) или

убыванию (disc)).

  Фраза NOCONSOLE подавляет вывод на экран или в активное окно.

  Также можно использовать команду

  LIST STRUCTURE

        [NOCONSOLE]

        [TO PRINTER | TO FILE <файл>]

  Команда LIST эквивалентна DISPLAY за исключением:

               <сфера> по умолчанию ALL.

               не выдается для продвижения вперед при

                переполнении экрана или окна.

               не выводятся записи, помеченные для удаления при

                SET DELETED ON.

  Файл после создания структуры остается открытым, т.е.

доступным для команд ввода, просмотра и изменения. Однако, если

СУБД только загружена в память, должно быть выполнено открытие

нужного файла базы данных командой открытия USE:

USE [<файл> | ?]

        [IN <рабочая область>]

        [AGAIN]

        [INDEX <список индексных файлов> | ?

        [ORDER [<вырN>

        | <индексный файл .idx>

        | [TAG] <имя тэга>

                [OF <файл .cdx>]

        [ASCENDING | DESCENDING]]]]

        [ALIAS <псевдоним>]

        [EXCLUSIVE]

        [NOUPDATE]

<файл> | ?

        <файл> - имя базы данных, которую необходимо открыть.

        Если указан ?, будет выдано окно открытия файлов со

        списком предагаемых баз данных. Если команда USE указана

        без <файл>, а в указанной рабочей области открыта база

        данных, эта база данных будет закрыта  База данных будет

        также закрыта в случае, когда в этой же рабочей области

        будет открыта другая база данных.

IN <рабочая область>

        База данных открывается в указанной рабочей области. Для

        закрытия базы данных в другой рабочей области,

        необходимо выполнить команду USE без имени базы данных,

        но с указанием рабочей области в IN <рабочая область>.

AGAIN

        Повторное открытие базы данных в другой рабочей области.

        При повторном открытии базы данных, индексные файлы,

        открытые с базой данных вначале, являются доступными и

        для базы данных, открытой в новой рабочей области, а

        база данных, открытая вначале, получает атрибуты (только

        чтение, эксклюзивно)

INDEX <список индексных файл> | ?

        Открывает вместе с базой данных индексные файлы. Файлы в

        <списке индексных файлов> должны разделяться запятыми.

        Hет необходимости указывать расширение индексных файлов.

        Для открытия одного индексного файла, его имя необходимо

        указать в  <списке индексных файлов> или указать INDEX ?

        для выдачи окна открытия файлов.

        Первый файл в <списке индексных файлов> становится

        главным индексным файлом, управляющим порядком доступа к

        записям.  Если первый индексный файл является

        компаундным индексным файлом .CDX, записи файла базы

        данных будут доступными в поря дке их физических

        номеров.

ORDER [<вырN>]

        <вырN> относится к индексным файлам из <списка индексных

        файлов> и указывает на главный тэг или главный индексный

        файл, отличный от первого индексного файла из <списка

        индексных файлов>. Индексные файлы .IDX пронумерованы в

        порядке нахождения в <списке индекcных файлов>. Тэги в

        структурном индексном файле (если такой существует)

        пронумерованы по порядку их создания.  Тэги любого

        независимого компаундного индексного файла .CDX

        пронумерованы по порядку их создания.

        Если <вырN> равно 1, записи файла базы данных будут

        доступными по физическому номеру, но индексные файлы

        остаются открытыми.  ORDER 0 позволяют открывать

        индексные файлы, которые будут изменяться, но записи

        файла будут доступными в поряд ке номеров. ORDER без

        дополнительных аргументов - то же, что и ORDER 0.

ORDER [<индексный файл.idx>]

ORDER [[TAG <имя тэга>] [OF <файл .cdx>]]

        Для точного указания индексного файла .IDX в качестве

        главного индекса, его имя необходимо указать в

        <индексный файл .idx>. Для точного указания тэга в

        индексе .CDX в качестве главного тэга, неоходимо

        использовать фразу <имя тэга>.  <Имя тэга> может быть из

        любого открытого файла .CDX.  Если имя тэга имеется в

        нескольких файлах .CDX, необходимо указать в OF <файл

        .cdx> файл .CDX, содержащий нужный тэг.

ASCENDING | DESCENDING

        Указывает порядок размещения записей в базе данных -

        возрастающий или убывающий. Индексный файл или тэг не

        изменяется, меняется только порядок доступа к записям.

ALIAS <псевдоним>

        Создает <псевдоним> для файла базы данных, открытого с

        помощью команды USE. Если ALIAS не указан в USE, по

        умолчанию присваивается псевдоним аналогичный имени

        файла базы данных.  Псевдоним может содержать до 10

        букв, цифр или знака подчеркивания и должен начинаться с

        буквы или знака подчеркивания. Если псевдоним не

        указывается каждый раз при открытии базы данных с

        помощью команды USE AGAIN, псевдоним по умолчанию будет

        присваиваться автоматически.

EXCLUSIVE

        Для пользователей FoxPro/LAN в сети, эксклюзивно

        открывающих базу данных.

NOUPDATE

        Запрещает изменения базы данных, включая изменения

        структуры командой MODIFY STRUCTURE. Hо просмотреть

        структуру базы данных можно всегда.

  Экспериментируя в командном окне, где и что Вы уже открыли,

поэтому можно использовать удобную функцию USED([<выр>]),

которая возвращает истину, если в рабочей области,

специфицированной необязательным выражением,  имеется открытая

база данных. В качестве выражения может использоваться номер

рабочей области, буква или   псевдоним.   Если   выражение

опущено, подразумевается текущая рабочая область.

  Если в указанной рабочей области нет открытых баз данных,

функция возвращает ложь .F.

  В отличие от команд, функции так просто Вы в командном окне

вызвать не можете. Для получения ее значения можно проделать

следующие шаги.

  1.

  SET TALK ON - если значение параметра SET TALK равно ON, на

                экран выдается протокол работы  команд (таких,

                как SORT, PACK и  т.д.).   Если значение

                параметра SET TALK равно OFF, протокол не

                выдается. Установка  значения  SET  TALK  ON

                существенно  снижает скорость выполнения команд

                за счет выдачи информации на экран.

                  По умолчанию принято SET TALK ON.

  =USED(a)    - на экран выведется значение ф-ии. Если оно

                закрыто каким-то окном, Вы его не увидите до тех

                пор, пока не нажмете комбинацию из трех клавиш

                Ctrl+Alt+Shift (не Del!)

  2.

  ?USED(a)      приведет к тому же эффекту, но при этом

                дополнительно используется команда ?. Дополняет

                ее ??, а формат обеих команд приведен ниже:

 ? | ?? [<выр1> [PICTURE <вырC1>] [FUNCTION <вырС2>]

        [AT <вырN>] [STYLE <выр>] [, <выр2>]] ... - вычисляют и

                                                    выводят

                                                    значения

                                                    заданных

                                                    выражений.

  Команда "?" обозначает "Чему равно значение этого выражения?".

Обе команды оценивают перечисленные выражения и выводят

результирующее значение. Если установлено  SET PRINT  ON, то

вывод из  этих обеих  команд направляется как на экран, так и на

принтер. Если же  установлено SET CONSOLE OFF и SET  PRINT ON,

то вывод направляется  только на принтер.

 ?    Команда из одного знака вопроса вызывает возврат каретки и

      перевод строки перед выводом результата выражения. При

      этом вывод размещается в следующей строке на экране  или

      принтере  в  крайней левой позиции, если не определено

      иного   фразой FUNCTION или системной  временной

      переменной  _ALIGNMENT.  Если  не определено  никакого

      выражения,  -  просто выводится пустая строка.

??    Команда из двух последовательных знаков вопроса выводит

      результат в текущей строке на текущей  позиции  экрана

      или принтера.   Hе возникает  ни  перевода  строки,  ни

      возврата каретки.

PICTURE

    Если присутствует фраза PICTURE, то значение выражения

    выводится в   соответствии   с   форматом, определенным

    через  <выр1>. Это выражение может содержать коды  функций,

    шаблонов и их комбинацию. Коды функций влияют на весь формат

    выражения, тогда как коды шаблона - только на отдельные

    символы. Если в <вырС1> используются коды функций, то они

    должны  приводиться в этом выражении перед кодами  шаблона

    со знаком  @ перед ними.  Столько кодов  функций,   сколько

    это   необходимо,  должны    следовать непосредственно  за

    символом  @  без  пробела  между ними. После последнего

    кода  функции  ставится  один или несколько пробелов. Эти

    пробелы  сигнализируют о  завершении кодов функций и  начале

    кодов шаблона.

 FUNCTION

    Выражение <вырС2> во фразе FUNCTION  дает   альтернативную

    возможность   включения   кодов функций для управления

    выводом команд ?  | ??  . Использование фразы   FUNCTION

    эквивалентно  определению кодов функций через фразу

    PICTURE, за исключением того, что отпадает  потребность  в

    символе @.

  С  командами  ?  и   ??  может  быть использован специальный

код функции:

 V<n>

    Реализует вывод на экран в форме вертикальной полосы текста,

    ограниченной заданным  числом  горизонтальных позиций по

    ширине. Эта  ширина  задается числом <n>.

 AT

    Hеобязательная фраза AT может использоваться для определения

    номера   колонки   <вырN>,с которой производить вывод.  Эту

    опцию  удобно  использовать для  реализации табличного

    вывода.

STYLE <выр>

    Hеобязательная фраза, позволяющая указать шрифт, который

    будет использоваться при выводе на печать с помощью ? и ??.

    При выводе на экран или в окно фраза игнорируется.

  Как видите, даже такая небольшая команда имеет целый вагон

параметров, а дальше - больше... Однако знать все их Вам совсем

необязательно; как правило, нужда в большинстве из них возникает

нечасто.

  Поработав с базой, необходимо ее закрыть командой

  CLOSE <тип файла>  -   Используется для  закрытия файлов

                         журнала,  файлов  базы  данных, файлов

                         формата экрана, индексов и файлов

                         процедур.

  Команда закрывает все файлы всех типов, не затрагивая память.

Для  закрытия   файлов определенного типа,  следует поместить

после команды одно из ключевых слов:

ALL       (все типы)

ALTERNATE (файлы журнала)

DATABASES (файлы базы данных)

FORMAT    (файлы формата экрана)

INDEX     (индексы)

PROCEDURE (файлы процедур).

  Команда CLOSE ALL закрывает файлы всех  типов и выбирает

рабочую  область  1.  Эта  форма команды CLOSE закрывает также

все файлы, открытые функциями ввода/вывода низкого уровня

FCREATE() и FOPEN().

  Команда  CLOSE  ALTERNATE  закрывает открытый файл журнала.

  Команда CLOSE DATABASES закрывает все открытые файлы базы

данных, связанные с ними индексы и файлы формата экрана и

выбирает рабочую область 1.

  Команда  CLOSE  FORMAT  закрывает  открытый в текущей рабочей

области файл формата экрана.

  Команда CLOSE INDEX закрывает все открытые  в текущей рабочей

области индексы.

  Команда  CLOSE  PROCEDURE  закрывает открытый файл процедур.

  Но самой важной командой в FoxPro является команда QUIT. Она

закрывает все открытые файлы, завершает сеанс работы   системы

FoxPro и возвращает управление операционной системе. Данная

команда   обеспечивает   единственный надежный  метод  выхода

из  системы  FoxPro. Выход путем перезагрузки ПЭВМ   может

вызвать разрушение  открытых файлов  и потерю данных.

  После того, как структура  нового   файла определена,  имеется

возможность добавить записи в файл. Команда APPEND [BLANK]

добавляет  новые  записи  в  конец  активного файла базы данных.

Это основная команда  для полноэкранного ввода данных.

  Имеется две формы команы APPEND. Обе они добавляют записи в

конец активного файла базы данных. Если встретилась команда

APPEND или APPEND BLANK, а база данных не открыта, выводится

диалоговое окно открытия файла. Из него выбирается база данных,

в конец  которой необходимо добавить записи.

  Команда APPEND открывает окно редактирования, в которое

вводятся данные для новой записи. Все открытые индексные файлы

обновляются в момент добавления записи.

  Команда APPEND BLANK добавляет одну незаполненную данными

запись в конец текущей выбранной базы данных. При этом окно

редактирования не используется. Редактирование записи может быть

выполнено  посредством  команд  BROWSE,  CHANGE  или  EDIT.  При

добавлении пустой записи все открытые индексные файлы

обновляются.

  При формировании пустой записи по команде APPEND BLANK

инициализируются все поля этой записи. Символьные поля

заполняются пробелами, числовым полям присваивается нулевое

значение. Поля даты инициализируются значением "  /   /  ".

Логические поля принимают значение .F., а текстовые поля

остаются пустыми.


Заполнение файлов БД

  Осуществляется эта операция командой BROWSE:

BROWSE

        [FIELDS <список полей>]

        [FOR <вырL1>]

        [FORMAT]

        [FREEZE <поле>]

        [KEY <выраж1> [, <выраж2>]]

        [LAST]

        [LEDIT] [REDIT]

        [LOCK <вырN1>]

        [LPARTITION]

        [NOAPPEND]

        [NOCLEAR]

        [NODELETE]

        [NOEDIT | NOMODIFY]

        [NOLGRID] [NORGRID]

        [NOLINK]

        [NOMENU]

        [NOOPTIMIZE]

        [NOREFRESH]

        [NORMAL]

        [NOWAIT]

        [PARTITION <вырN2>]

        [PREFERENCE <вырC1>]

        [REST]

        [SAVE]

        [TIMEOUT <вырN3>]

        [TITLE <вырC2>]

        [VALID [F:] <вырL2>

                [ERROR <вырC3>]]

        [WHEN <вырL3>]

        [WIDTH <вырN4>]

        [[WINDOW <имя окна1>]

        [IN [WINDOW] <имя окна2>

                | IN SCREEN]]

        [COLOR SCHEME <вырN5>

        | COLOR <список пар цветов>]

  BROWSE - одна из наиболее полезных  команд, имеющихся в

FoxPro. Команда  BROWSE  используется  для  открытия  окна и

высвечивания записей из файла базы данных. Имеется возможность

редактирования и добавления записей. Если памяти достаточно, то

окно Browse может быть открыто для каждой рабочей области.

Перемещение в окне Browse

-------------------------

         Передвигаться от записи к записи можно при помощи

         клавиш Стрелка вверх  и  Стрелка  вниз,  а

         передвигаться постранично можно при помощи клавиш Page

         Up или Page  Down.  Передвижение на следующее или

         предыдущее поле осуществляется при помощи клавиш Tab

         или Shift+Tab.

Сохранение внесенных изменений

-------------------

         Сохранить все  внесенные изменения  и выйти  из BROWSE

         можно при помощи клавиш  CTRL+W или  CTRL+End, или

         нажатием кнопки "мыши", когда ее курсор установлен на

         индикаторе закрытия (close). Если нажать CTRL+Q или

         Esc, то можно выйти из BROWSE без сохранения внесенных

         в  текущее  поле   изменений.  В  окне  Browse   можно

         редактировать и текстовые поля. Для этого курсор нужно

         установить на  текстовое поле  и нажать  CTRL+PgDn. При

         наличии манипулятора  "мышь"  для  редактирования

         текстового поля можно дважды  нажать  кнопку  "мыши",

         когда  ее курсор установлен на текстовом поле.  Для

         выхода из редактирования т екстового  поля и сохранения

         внесенных изменений  необходим о нажать  CTRL+W  и ли

         кнопку "мыши", когда ее курсор установлен на индикаторе

         закратия.  Для  выхода  из редактирования текстового

         поля  без сохранения внесенных изменений необходимо

         нажать Esc.

Поддержка SET SKIP

----------------

         Команда  SET  SKIP  позволяет  устанавливать  связь

         типа одна ко многим между двумя базами данных. Для

         каждой записи в порождающей базе  данных существуют

         множественно соотнесенные записи в дочерней базе

         данных. Если создается связь одна ко многим, то для

         просмотра записей как из порождающей, так и дочерней

         баз данных можно  использовать  команду BROWSE. Для

         каждой  записи   из порождающей  базы  данных,

         появляющейся в окне  Browse, будут появляться  все

         соответствующие записи  из дочерней базы данных (если

         они существуют). Порождающая запись появляется один

         раз с первой из соответствующих  записей   дочерней

         базы данных. Последующие  соответствующие  записи (если

         такие существуют) появляются  в   следующих строках.

         Затемненные блоки   могут появляться позади первой

         соответствующей записи в любой  колонке, содержащей

         информацию из порождающей базы данных.  Передвигаться

         между порождающими записями в окне Browse можно при

         помощи клавиш СТRL+Стрелка вниз (движение к  следующей

         записи) и CTRL+Стрелка вверх (движение к предыдущей

         записи).  Более подробную информацию о  создании  связи

         одна  ко  многим соответствий  мо жно найти в описании

         команды SET SKIP.

Поддержка COL() и ROW()

-----------------------

         Функции COL()  и ROW()  возвращают текущие  координаты

         курсора  в окне  Browse.  Если  окно  Browse  открыто

         на экране, то позиция курсора возвращается относительно

         экрана, а не самого окна.  Если окно Browse открыто в

         окне, то COL() и ROW() возвращают позицию курсора

         относительно окна.  Команда BROWSE поддерживает

         множество опций.  Ниже приводится список  имеющихся

         опций  и  их краткое описание.

Фразы

-------

FIELDS   <список  полей>  содержит  список  отображаемых  полей.

         <список полей>  может  содержать  поля  базы  данных  и

         вычисляемые поля. Формат <списка полей> :

<поле1> [:R] [:<ширина колонки>]

        [:V = <вырL1> [:F] [:E = <вырC1>]]

        [:P = <вырC2>]

        [:B = <выр1>, <выр2> [:F]]

        [:H = <вырC3>]

        [:W = <вырL2>]

[, <поле2> [:R]  ... ]

Вычисляемые Поля

Формат команды создания вычисляемого поля:

<имя вычисляемого поля > = <выр>

  Опции для специальной обработки полей BROWSE.  Этим опциям

должны предшествовать : или /.

Опция          Обработка

--------------------

R                 Только чтение

<ширина колонки>  Размер поля

V                 Контроль данных - не используется для

                  текстовых полей

F                 Определяет порядок выполнения контроля данных

                  - не используется для текстовых полей

E                 Сообщение об ошибках

P                 PICTURE для поля

B                 Диапазон номеров

H                 Заголовок поля

W                 Разрешение/Запрет ввода данных -

                  не используется для текстовых полей

FOR <вырL1>

          В   окне   Browse   высвечиваются   только   те

          записи, которые удовлетворяют  условию.   Для  лучшей

          работы в FOR используется оптимизируемое выражение.

FORMAT

          Вывод  данных  с  использованием  файла  формата.   В

          окне Вrowse используется следующая информация из файла

          формата:

           список полей для BROWSE

           любые заданные фразы VALID

           любые заданные фразы  WHEN

           любые заданные фразы RANGE

           вывод полей в формате PICTURE

           все выражения SAY рассматриваются как вычисляемые

            поля BROWSE

FREEZE <поле>

          Позволяет вносить изменения только в одно <поле> в

          окне Browse.  Несмотря на наличие фразы FIELDS

          выводятся все поля.

KEY <выр1> [, <выр2>]

          Ограничивает количество высвечиваемых записей. <выр1>

          - значение или диапазон значений индексного ключа;

          <выр2> определяет текущую запись.

LAST

          Открывает окно BROWSE в той же конфигурации, что

          сохранена в файле FOXUSER,  если значение  установки

          RESOURCE  - ON.   Список полей, размер  каждого поля,

          расположение и  размер окна  Browse хранятся в файле

          FOXUSER.

            Если последнее окно Browse было открыто с именем

          PREFERENCE, то фраза LAST команды BROWSE будет

          игнорироваться.

LEDIT | REDIT

          При выводе окна  Browse левая и  правая части

          выводятся в режиме редактирования.

NOCLEAR

          После выхода из окна Browse  сохраняет его образ на

          экране или в окне.

NODELETE

          Предотвращает маркирование записей для удаления.

          Обычно записи в окне Browse  маркируются для  удаления

          при помощи нажатия клавиш CTRL+T, выбора опции Toggle

          Delete из вертикального меню Browse, или по нажатию

          кнопки "мыши",  когда ее курсор находится в  самой

          левой колонке удаляемой записи.

NOEDIT | NOMODIFY

          Запрещает изменения базы данных.

NOLGRID | NORGRID

          Удаляет вертикальные  разделители между  полями в

          левой и правой частях.

NOLINK

          Разрывает связи между частями.

NOMENU

          Запрещает доступ к вертикальному меню Browse и его

          опциям.

NOOPTIMIZE

          Отключает режим быстрого доступа Rushmore.

NOREFRESH

          Запрещает изменения окна Browse. Окно команды Browse

          обновляется со  скоростью,  установленной  при  помощи

          команды  SET REFRESH.  Фраза NOREFRESH  полезна при

          работе с  файлами, предназначенными только для чтения.

NORMAL

          Если имеется открытое окно пользователя, в котором

          выводится окно Browse, окно Browse будет  иметь те же

          цвета, размер, координаты вывода, и аттрибуты (GROW,

          FLOAT, ZOOM и т.п.).  Если  задана опция NORMAL, то

          окно Browse открывается нормально - т.е.  так, как

          будто нет других открытых окон вывода. Окно Browse

          принимает свои "нормальные" атрибуты. Фраза  NORMAL

          используется только  в  том  случае, если имеется

          открытое окно пользователя.

NOWAIT

          После открытия окна Browse продолжает выполнение

          программы.

PARTITION <вырN2>

          Разделяет окно Browse.  <expN2> определяет позицию

          границы.

PREFERENCE <вырC1>

          Сохраняет аттрибуты и опции окнах Browse в исходном

          файле для  их дальнейшего   использования.   Если

          имеется   открытое    окно пользователя,  окно  Browse

          будет  иметь  те  же  цвета, размер, координаты

          вывода, и аттрибуты (GROW, FLOAT, ZOOM и т.  п.).

REST

          Используется  совместно с  фразой   FOR  команды

          BROWSE   для предотвращения  установки  указателя

          записи  в начало файла базы данных.

SAVE

          После выхода из окна Browse сохраняет его и любые

          другие открытые временные окна.

TIMEOUT <вырN3>

          Задает время  ожидания  ввода  в  окне  Browse.

          <вырN3> задает, сколько  секунд  прохоит  перед

          автоматическим  закрытием   окна Browse, если не был

          осуществлен ввод.

TITLE <вырC2>

          Задает заголовок <вырC2>, который появляется вверху

          окна Browse.

VALID <вырL2> [ERROR <вырC3>]

          Вычисляется при выборе записи. Обычно <вырL2> является

          определяемой пользователем функцией. Включение  опции

          VALID  позволяет  осуществлять  контроль данных строки

          (записи) в окне Browse . Значение VALID вычисляется

          после изменения записи при переходе на  другую запись.

          Опция VALID  Е выполняется для текстовых полей. Если

          опция  VALID  возвращает  значение логической истины

          (.T.), можно осуществлять переход на другую запись.

          Если опция VALID возвращает логическую ложь, то курсор

          остается в текущем поле текущей записи, и появляется

          системное сообщение "Hеверный ввод" ("Invalid Input").

          Используя опцию ERROR <вырC3> можно  вывести  и

          собственное  сообщение  об ошибке.  Символьное

          выражение <вырC3> отображается в системном окне.

          Если опция VALID возвращает 0, то курсор остается в

          текущем  поле текущей запмси, но сообщения об ошибке

          не выдается. Если после ключевого  слова VALID

          поставить :F или /F,  то опция VALID  будет

          выполняться  перд переходом  на  каждую  следующую

          запись. Опция VALID в этом случае выполняется даже

          если изменений в записи не производило сь.

WHEN <вырL3>

          Позволяет  либо  запрещает  выбор  записи. Часто

          <вырL3> является определяемой пользователем функцией.

          Если опция WHEN возвращает ложь или 0, то  выбранная

          запись становится доступной только для чтения.  Опция

          WHEN Е выполняется для текстовых полей.

WIDTH <вырN4>

          Ограничивает количество отображаемых символов для всех

          полей. Опция WIDTH не изменяет размер полей в самой

          базе данных, а изменяет  только  их  отображение  в

          окне Browse  .  Если ширина конкретного поля была

          задана в опции FIELDS , то для  этого поля она имеет

          приоритет по сравнению с заданной в WIDTH.

WINDOW  <имя окна>

IN WINDOW <имя окна2>

          Oткрывает окно Browse в другом окне. Окно, в котором

          будет открыто окно Browse  , сначала должно  быть

          определено в команде DEFINE WINDOW . Опция WINDOW

          активизирует окно и помещает в  него окно Browse  .

          Окно  Browse должно полностью занять  то окно,  в

          которое оно помещено  , и  принять  все  его

          характеристики.  Hапример, если в команде DEFINE

          WINDOW окно определено с опциями FLOAT и GROW, то окно

          Browse также можно передвигать и изменять его размер.

          Если используется опция IN  WINDOW <имя  окна2>, то

          окно Browse открывается в порождающем окне независимо.

          Оно не принимает характеристик порождающего окна.

          Окно Browse, активизированное внутри порождающего

          окна, не может перемещаться из него, а  также не может

          быть больше  порождающего окна.  Если порождающее

          окно перемещается, то окно Browse перемещается вместе

          с ним.

          Порождающее окно, в которое  помещается окно Browse,

          должно  быть сначала определено в команде DEFINE

          WINDOW.

          Используя опции IN WINDOW  или  IN SCREEN, можно

          специфическим образом поместить  окно Вrowse  на

          экране,  а не  внутри окна. По умолчанию окна Browse

          выводятся на экран.

COLOR SCHEME <вырN5>| COLOR <список пар цветов>

          Изменяет текущую схему цветов путем задания другой

          цветовой схемы или списка пар цветов. По умолчанию

          окно Browse использует цвета, заданные в COLOR SCHEME

          10 (Browse).

  В FoxPro имеются средства как непосредственного указания

цветов в командах ввода - вывода, описания окон, меню, так и

определения их по умолчанию. При этом приняты следующие

обозначения цветов (допускаются как прописные, так и строчные

буквы):

  черный     - N    зеленый    - G   коричневый - GR

  синий      - B    бирюзовый  - BG  красный    - R

  желтый     - GR+  лиловый    - RB  белый      - W

  После символа цвета можно указать звездочку "*" для мерцания и

"+" для повышенной яркости ("+" действует только на цвет текста

и не действует на цвет фона).

  Установка цветов окон, меню и т.д. осуществляется с помощью

так называемых цветовых схем командой SET COLOR OF SCHEME :

SET COLOR OF SCHEME <вырN1>

TO [[<список пар цветов>]

     | [SCHEME <вырN2>]]

  Эта   команда   позволяет  определить цвета в цветовой  схеме,

специфицируя  список   пар цветов. В цветовой схеме можно

определить  от 1 до 10 пар цветов.

  Hомер  цветовой  схемы  указывается в <вырN1>. Его значение

может находиться в диапазоне от 1 до 24. Таблица пар цветов,

назначенных каждой  схеме,  приведена   в  соответствующем

разделе Help.

  В помещаемом в команду списке можно указывать до 10  пар

цветов.  Не обязательно  приводить все 10 пар. Можно выборочно

поменять цвета  в схеме в  требуемых позициях,  отмечая запятой

те  позиции,  в которых  цветовая  пара  не меняется.

  Эта   команда также  позволяет  определить цвета в цветовой

схеме  <вырN1>,  копируя   в   нее установку цветов схемы

<вырN2>.

  Примеры:

  Для  изменения только  цвета рамок  (пара цветов  3) у окон,

определяемых  пользователем  (цветовая  схема 1), на белый цвет

на фоне красного, и  чтобы  при этом не затронуть другие

элементы, следует сформировать следующую команду:

  SET COLOR OF SCHEME 1 TO ,,W/R

  Для  определения  установки  всех  десяти  пар  цветов  схемы

8 (системные окна) команда должна выглядеть так:

  SET COLOR OF SCHEME 8 TO;

      W+/B,W+/N,W+/N*,G+/N*,G+/N*,W+/B*,GR+/B,N+/N,W+/B,W/B

  Для получения списка пар цветов цветовой схемы можно

использовать функцию SCHEME().

  SCHEME(<вырN1>[,<вырN2>]) - возвращает цветовую пару или

                              список пар цветов из цветовой

                              схемы.

  Для  получения  всего  списка  пар  цветов  в <вырN1>

указывается номер цветовой  схемы, а второй необязательный

аргумент не приводится.  Для получения же  отдельной цветовой

пары  из списка в    качестве   второго аргумента указывается

номер позиции этой пары в списке.

  CREATE COLOR SET <имя множества цветов>

Создает новое множество цветов. Команда CREATE COLOR SET

создает новое множество цветов, исходя из текущих  установок

цветов.   В  создаваемое множество цветов записывается  каждая

пара  цветов из  каждой  схемы  цветов.  Имя множества цветов

может иметь до десяти символов, содержать цифры и символы

подчеркивания, но не может начинаться с цифры. Созданное

множество цветов можно загружать при помощи опции  меню Color

Picker, или при помощи команды SET COLOR SET. Множества  цветов

записываются  в  файле  ресурсов  FoxPro. Если множество

цветов с указанным именем существует, оно перезаписывается.


Перемещение в БД
Перемещения в БД

  При работе с базой данных необходимы средства перемещения

внутри нее. Запись, на которой находится указатель записей,

является текущей, и только к ней в данный момент возможен

непосредственный доступ.

  Имеется несколько разновидностей команд, изменяющих положение

указателя записей:

  GO - позиционирование указателя записи.

GO [RECORD] <вырN1>

        [IN <вырN2> | <вырC>]

GO TOP | BOTTOM

        [IN <вырN2> | <вырC>]

[RECORD] <вырN1>

        Устанавливает указатель записи на запись с физическим

        номером <вырN1>.

TOP | BOTTOM

        Установка указателя записи на первую и последнюю в

        выбранной базе данных.  Если база используется с

        индексом, то первой будет запись с минимальным значение

        ключа, последней -- запись с максимальным значением

        ключа.

IN <вырN2> | <вырC>

        Перемещение указателя в базе, открытой в другой рабочей

        области.

  SKIP   Перемещает указатель записи в активном  файле

         базы данных вперед или назад.

  SKIP [<вырN>] [IN <псевдоним>]

  Если с файлом базы данных используется индекс, команда  SKIP

работает в порядке записей индекса. Если файл базы данных не

индексирован, указатель перемещается последовательно в

соответствии с номерами записей.

  Указатель записи  перемещается вперед,  кроме случая, когда

результат числового  выражения отрицательный.  Если <выражение>

не  указано, указатель перемещается вперед на одну запись.

  Если  команда  SKIP  указана, когда указатель установлен   на

последнюю   запись   файла, значение RECNO() будет на единицу

больше, чем номер последней записи файла и значение EOF()

(конец файла) = .T. .

  Если  указатель  установлен  на первую запись файла  и

указана команда  SKIP - 1, значение RECNO() остается равным  1,

а значение  BOF() (начало файла) = .T. .

  Команда  SKIP   может  перемещать   указатель записей  в

рабочей   области,  отличной   от текущей. Для этого

предусмотрена фраза SKIP.

  Для контроля положения указателя и наличия записей в файле

предусмотрены функции:

  RECNO([<вырN> | <вырC>]) - Возвращает номер текущей записи

                             активного файла базы  данных.

                             Возвращаемое значение - числовое

  Если  в  файле  базы  данных  нет записей, то функция  RECNO()

возвращает  значение  1,   а функция EOF() - значение "истина".

  Если указатель записи установлен после последней записи файла,

то функция RECNO() возвращает  значение  на  единицу больше, чем

число записей в файле.  Если указатель записи расположен  перед

первой записью файла, то функция RECNO() возвращает значение 1.

  В функции RECNO() можно задать необязательный аргумент,

указывающий к какой рабочей области относится действие функции.

Если в этой рабочей области нет открытого файла базы данных,

функция возвращает нулевое значение. Если аргумент отсутствует,

функция  RECNO() возвращает информацию для файла в текущей

рабочей области.

  RECNO(0) можно использовать после неудачного поиска записи в

индексированной базе данных командой SEEK. RECNO(0) возвращает

номер записи, индекс которой наиболее согласуется с аргументом

SEEK. Eсли аргумент SEEK находится вне пределов индексных ключей

базы данных, то RECNO(0) возвращает 0.

  RECCOUNT([<вырN> | <вырC>]) - Возвращает число записей

                                активного файла базы данных.

                                Возвращаемое значение -

                                числовое

 вырN | вырC - номер или алиас рабочей области.

  Если в текущей рабочей области нет активного файла базы

данных,то  функция RECCOUNT() возвращает нулевое значение.

  Функция  RECCOUNT()  учитывает   все   записи активного  файла

базы  данных, независимо от значений параметров SET DELETED и

SET FILTER.

 В функции RECCOUNT() можно задать необязательный аргумент,

указывающий к какой рабочей области относится действие функции.

Если аргумент отсутствует, функция RECCOUNT() возвращает

информацию для файла в текущей рабочей области.

  EOF([<псевдоним>]) - Проверяет  выполнение   условия

                       "конец файла" для файла базы данных.

  Функция возвращает логическое значение "истина" (.T.), если

указатель записи установлен после последней записи файла или,

если предшествующие команды FIND, SEEK или  LOCATE не были

успешными. В противном случае, функция EOF() возвращает

логическое  значение "ложь" (.F.).

  В функции EOF() можно задать необязательный аргумент,

указывающий к какой рабочей области относится действие  функции.

Если в этой рабочей области нет открытого файла базы данных,

функция возвращает логическое значение "ложь" (.F.). Если

аргумент отсутствует, функция  возвращает информацию для файла в

текущей рабочей области. Рабочая  область  может  задаваться

номером, буквой или псевдонимом.

  BOF([<псевдоним>]) - Проверяет выполнение условия

                       "начало файла" для файла базы данных.

  Функция возвращает значение "истина" (.T.) при попытке

установки указателя записи перед первой логической записью

файла базы данных.


Просмотр данных в БД

  Просмотр данных в FoxPro осуществляется очень близкими по

смыслу и синтаксису командами LIST и DISPLAY:

  DISPLAY [<сфера>]

  [[FIELDS] <спис.пол>]           Используется для просмотра

  [FOR <вырL1>]                   содержимого файла базы данных

  [WHILE <вырL2>]                 или значения выражений.

  [OFF]

  [TO PRINT | TO FILE <имя>]

  [NOCONSOLE]

  [NOOPTIMIZE]

  Команда выводит содержимое текущей выбранной базы данных. Она

может также использоваться для  вывода  результатов  оценки

выражений, включающих литералы,  временные переменные  и

массивы, поля баз данных и текстовые поля.

  Команда DISPLAY почти полностью аналогична команде LIST.

Описываемая  команда  после  заполнения  экрана делает паузу,

для продолжения  вывода  необходимо  нажать  любую клавишу или

кнопку мыши. Команда LIST выводит данные непрерывным потоком,

без пауз.

  Значение параметра <сфера> по умолчанию равно NEXT 1, поэтому

для вывода диапазона  записей необходимо указать сферу

действия.

  Перед каждой записью,  если не указана опция OFF, выводится ее

номер.

  Для каждой записи, попадающей в заданный <сферой> интервал,

выполняется оценка выражений, указанных в <списке> и результаты

выводятся на экран. При этом заметим следующее. Если указана

опция FOR, выполняется  вывод   результатов   только  для

отвечающих условию, заданному логическим выражением, записей.

Если указана опция WHILE, вывод информации продолжается до тех

пор, пока заданное логическим  выражением условие остается

"истинным".  Если опции  FOR  и   WHILE   отсутствуют,   вывод

информации выполняется  для  всех  записей, попадающих в

заданный <сферой> интервал.

  Если  не  указать  явно  список  полей   базы данных,

выводятся все поля, кроме текстовых. Содержимое текстовых  полей

не   выводится,  если  имена  таких  полей  явно  не указаны в

<списке  выражений>  опции  FIELDS. Текстовые поля выводятся с

шириной текста, заданного  в команде SET MEMOWIDTH TO.

  Если указать опцию TO PRINT, вывод информации будет  выполнен

на  экран  и  на  печатающее устройство.  Опция  TO  FILE  <имя>

позволяет осуществить вывод в файл.

  Опция NOCONSOLE подавляет вывод на экран или в активное окно.

  Опция NOOPTIMIZE отключает технологию Rushmore.

  SET HEADING ON | OFF - определяет вывод/подавление вывода

                         заголовков колонок  над  каждым  из

                         полей в командах AVERAGE, CALCULATE,

                         DISPLAY, LIST, SUM.

  Если  установлено SET  HEADING ON,  заголовки полей в

перечисленных командах выводятся. В противном случае вывод

заголовков подавляется. По умолчанию установлено SET HEADING ON.

  Приведенные команды удобны скорее для просмотра данных при

отладке программ. Для включения их в программы они слишком

грубы, и здесь лучше использовать более гибкие команды ? и

@ ... SAY.


Удаление записей в БД

  В FoxPro имеется несколько команд удаления данных:

  ERASE <файл>| ? - удаляет файл с диска. Использование фразы

                    ?  позволяет  устанавливать  имя удаляемого

                    файла в диалоге.  В  команде  не допускается

                    применение шаблона имени с включением

                    символов * и ?. Имя файла должно включать

                    расширение имени. Если  файл  находится на

                    диске или в директории,  отличном от

                    активного  по  умолчанию, имя файла должно

                    включать идентификатор диска и/или маршрут.

  ZAP             - удаляет все записи активного файла базы

                    данных.  Данная команда эквивалентна

                    последовательным командам DELETE ALL и PACK,

                    но работает быстрее. После выполнения

                    команды ZAP в файле не остается записей,

                    сохраняется только структура полей. Если

                    установлено  SET  SAFETY  ON,  то будет

                    запрошено подтверждение намерения удалить

                    все записи файла базы данных. Записи,

                    удаленные  с  помощью  команды  ZAP,

                    восстановить нельзя.

DELETE [<сфера>]  - Помечает для исключения записи активного

  [FOR <вырL1>]     файла базы данных. Если не определено иначе

  [WHILE <вырL2>]   <сферой> или фразами WHILE  или  FOR,  для

  [NOOPTIMIZE]      исключения  помечается только текущая

                    запись.

                    <сфера>        Если указана  сфера  действия

                                   команды, будут помечены  все

                                   записи в пределах,  заданных

                                   <сферой>.

                    FOR            Если указана фраза FOR, то

                                   будут помечены все записи,

                                   для которых <вырL1>

                                   "истинно".

                    WHILE          Если указана фраза WHILE,

                                   будут помечены  все  записи,

                                   для которых <вырL2>

                                   "истинно".

                      Команда  DELETE   не  выполняет

                    физического удаления  записей,   а  лишь

                    помечает   их. Физическое удаление записей

                    выполняет команда PACK.  Сброс   признака

                    исключенной   записи выполняет команда

                    RECALL.

                      Записи могут быть также помечены для

                    удаления через   окно просмотра  или

                    посредством  меню  Record  (запись).

PACK [MEMO] [DBF] -   Физически удаляет записи, помеченные для

                    удаления в базе  данных, уменьшает размер

                    текстового файла.

                      Команда необратимо удаляет  все записи,

                    помеченные  для удаления, в  текущем

                    выбранном   файле  базы  данных.   Она

                    также   может использоваться   для

                    уменьшения   размера   текстового   файла,

                    связанного с файлом базы данных.

                      Записи могут быть помечены для удаления

                    командой DELETE, посредством опции

                    Delete...  из меню Record, а также из окна

                    просмотра Browse.  Если команда выдана без

                    обоих операндов MEMO и DBF, то выполняется

                    удаление и сжатие файла базы данных и

                    текстового файла.

                      После  выполнения  команды  PACK

                    удаленные записи  восстановить невозможно,

                    они утрачиваются навсегда.

                      При выполнении сжатия все записи базы

                    данных, не помеченные для удаления,

                    копируются во временный файл базы данных.

                    После завершения просмотра оригинального

                    файла последний удаляется с диска, а

                    временному присваивается его имя. Выполнение

                    команды может быть прервано нажатием клавиши

                    Esc.  При этом временный файл удаляется,  а

                    оригинальный остается   неизменным.  Если

                    при выполнении команды исчерпывается

                    пространство на диске, автоматически

                    восстанавливается оригинальный файл.

                      Если сжимаемая база данных имеет индексы,

                    выполняется переиндексация.

                      Фразы:

                      MEMO Если в базе данных есть одно или

                           несколько текстовых  полей, то  с

                           ней связывается  текстовый  файл

                           (memo).  Информация текстовых полей

                           размещается в этом файле  Текстовый

                           файл имеет то же имя, что и  файл

                           базы данных, но с расширением .FPT.

                             Если  в  команду  включить

                           ключевое слово  MEMO, то сжатию

                           подвергнется только текстовый  файл.

                           При этом утилизируется неиспользуемое

                           пространство только в текстовом

                           файле, записи из DBF файла не будут

                           удалены.

                      DBF  Если включить только это ключевое

                           слово, то сжимается только файл базы

                           данных без сжатия текстового файла.

RECALL [<сфера>]  - Снимает маркировку на удаление записей

  [FOR<вырL1>]      активного файла базы данных, установленную

  [WHILE<вырL2>]    командой DELETE.

  [NOOPTIMIZE]        Команда RECALL  отменяет признак

                    логического исключения записей.

                      Команда RECALL не восстанавливает записи,

                    физически удаленные из файла с помощью

                    команд PACK или ZAP.

                      Восстановление только определенных записей

                    можно выполнить с помощью опций <сфера>,

                    FOR и WHILE. Фраза FOR позволяет

                    восстановить записи, для которых истинно

                    <вырL1>. Фраза WHILE прерывает

                    восстановление как только <вырL2> станет

                    ложным.

  Команда сжатия PACK в базе реальных размеров выполняется

медленно, и поэтому лучше ее делать один раз в день или даже раз

в несколько дней. Чтобы помеченные к удалению записи до их

уничтожения не учавствовали далее в обработке данных, можно

использовать команду SET DELETED ON. Полный синтаксис этой

команды следующий :

  SET DELETED ON | OFF - Определяет обработку/пропуск командами

                         системы помеченных для удаления

                         записей.

  Если параметр SET DELETED имеет значение ON, все команды

выборки записей с  использованием<сферы> игнорируют  помеченные

для удаления записи.

  Команды, работающие с текущей записью или содержащие в

качестве сферы действия единственную  запись, игнорируют

значение параметра SET DELETED.

  Если значение параметра SET DELETED равно OFF, команды выборки

будут обрабатывать все записи, независимо от того, помечены они

для удаления или нет.

  Команды INDEX, REINDEX, GO, COPY TO ARRAY и другие

обрабатывают все записи, независимо от значения параметра  SET

DELETED.

  Для того, чтобы отличить помеченую к удалению запись от

"живой", имеет смысл использовать функцию DELETED():

  DELETED([<псевдоним>]) - возвращает логическое значение

                           "истина" (.T.), если текущая запись

                           помечена для удаления и значение

                           "ложь" (.F.) в противном случае.

  В функции DELETED() можно задать необязательный аргумент,

указывающий к какой рабочей области относится действие функции.

Если в этой рабочей области нет открытого файла базы данных,

функция возвращает значение  "ложь"  (.F.). Если аргумент

отсутствует, функция возвращает информацию для файла в текущей

рабочей области.

  Рабочую область можно задать ее номером, буквой или

псевдонимом.


Изменение данных в БД
Изменение данных

  В FoxPro имеется возможность не только вручную редактировать

данные, но и изменять их путем присвоений и вычислений.

  REPLACE [<сфера>]

    <поле1> WITH <выр1> [ADDITIVE]

    [,<поле2> WITH <выр2>...]

    [FOR <вырL1>]

    [WHILE <вырL2>] [NOOPTIMIZE]

  Команда обновляет значения заданных полей записей активного

файла базы данных.

  Если не определено иначе <границами> или фразами WHILE или

FOR, обновляется значение только текущей записи.

  Команда REPLACE замещает значение указанного <поля> значением,

указанным в виде <выражения>.

  Фраза ADDITIVE используется только для текстовых полей.

При ее включении в команду содержание соответствующего поля

не заменяется новым, а дополняется им в конце существующего

текста.

  ЗАМЕЧАHИЕ. Поскольку команда REPLACE  изменяет  активные

индексы при каждом выполнении команды, нельзя пользовать-

ся ею для  обновления ключевых  полей  индексированного

файла, если заданы <сфера> или фразы FOR или WHILE. Если данное

замечание игнорируется,  возможно обновление только части

данных.

  Для числовых полей <выражение>, указанное  во фразе WITH, не

может превышать длину поля. Если это  происходит, то  "лишние"

десятичные знаки отбрасываются и выполняется округление; в том

случае, когда это не помогает, то для научного способа записи

чисел в виде коэффициента и степени десяти выполняется замена

содержания поля указанным с утратой точности; когда и это не

помогает, значение поля замещается звездочками (*), и некоторые

данные могут быть потеряны.

  Практически (за некоторыми исключениями) только командой

REPLACE в FoxPro можно изменять значения полей файла базы

данных. В этом смысле она эквивалентна знаку равества в операции

присвоения переменных. Если Вы попытаетесь напрямую присвоить

какое-либо значение полю базы, то значение поля не измениться.


Локализация и поиск в БД-фильтрация
Локализация и поиск - фильтрация

  Хотя в команде BROWSE, например, имеется возможность

осуществить отбор записей из базы с помощью опций FOR и KEY, в

других командах это может оказаться невозможным или неудобным. В

FoxPro предусмотрена специальная команда вида

  SET FILTER TO [<вырL>], которая позволяет выводить и

                          обрабатывать только те записи файла

                          базы данных, которые удовлетворяют

                          заданному условию.

  Команда SET FILTER  работает только с  файлом базы данных,

открытым в той рабочей  области, где указана команда. Поэтому

соответствующий фильтр может  быть  установлен  для  каждого

открытого файла базы данных.

  При активной команде SET FILTER для вывода и обработки

доступны только те записи базы данных, которые удовлетворяют

заданному выражению. Создается иллюзия, что в базе данных

присутствуют только эти записи. Выражение обычно ссылается на

значения  полей записей базы данных.

  Команда SET FILTER TO (без параметров) отключает фильтр

активного файла базы данных. Фильтр активизируется только

после перемещения в файле указателя записи.

  Функция FILTER([<псевдоним>]) возвращает выражение фильтра для

рабочей области, указанной псевдонимом. Рабочая  область  может

задаваться  номером, буквой или псевдонимом. Если аргумент

функции отсутствует, предполагается текущая рабочая область.

  Если нет действующего фильтра, возвращается пустая строка.

  По возможности следует применять более быстрый способ

фильтрации с использованием индексного файла. Для этого

используется параметр KEY в команде BROWSE, или индексация с

FOR-условием, или технология Rushmore.


Локализация и поиск в БД-последовательный поиск
Локализация и поиск - последовательный поиск

  При последовательном поиске выпоняется сплошной перебор

записей файла базы данных до установки указателя записей на

искомую запись.

  Следующая команда осуществляет последовательный поиск одной

самой первой записи, удовлетворяющей заданному FOR-условию,

среди записей, находящихся в заданных границах, и до тех пор,

пока соблюдается WHILE-условие.

   LOCATE [FOR <вырL1>]

          [<сфера>]                   Выполняет в активном файле

          [WHILE <вырL>]              базы данных поиск по

             <команды>                заданному условию.

          [CONTINUE] [NOOPTIMIZE]

  Если  не  определено  иначе <сферой>, команда LOCATE

просматривает  весь  файл,  начиная с первой записи.

  Для обработки записи, найденной с помощью команды LOCATE,

можно поместить между командами LOCATE...CONTINUE команды

FoxPro. Затем команда CONTINUE возобновляет процесс поиска

следующей записи, удовлетворяющей заданному условию. Команду

CONTINUE можно повторять до тех пор, пока не перестанет

действовать <сфера> или не будет обнаружен конец файла или

другая команда LOCATE для того же файла не установит новые

критерии поиска.

  Если  искомая  запись  найдена,  то   RECNO() вернет номер

этой записи, FOUND() вернет  .T. и EOF() вернет .F. . Если не

установлено  SET TALK  OFF,  то  также  будет  выведен   номер

записи.

  Если искомая запись не найдена, RECNO() вернет номер текущей

записи в базе данных плюс  единица,  FOUND()  вернет  .F.  и

EOF() вернет .T. .

  Команды  LOCATE и CONTINUE относятся к конкретной рабочей

области, где они указаны. Каждая  рабочая  область  может  иметь

свои команды LOCATE и  CONTINUE.  После  выхода из рабочей

области  и  возврата  в нее, команды продолжают действовать.

  Фуекция FOUND([<псевдоним>]) возвращает логическое значение

"истина"   (.T.),  если   последняя команда    FIND,  LOCATE,

SEEK или CONTINUE завершилась успешно. Значение "ложь"  (.F.)

функция  возвращает, если запись не  найдена или указатель

записи перемещен  не  поисковой  командой  (например GOTO).

  В каждой рабочей области поддерживается  свое значение функции

FOUND.

  В функции FOUND можно задать необязательный аргумент,

указывающий, к какой рабочей области относится действие

функции.  Если  в этой рабочей области нет открытого файла базы

данных,  функция  возвращает  значение "ложь" (.F.).   Если

аргумент  отсутствует, функция возвращает  информацию  для

текущей  рабочей области. Это  особенно  полезно  при  работе с

файлами базы данных, связанными отношением.

  CONTINUE продолжает процесс локализации записей, начатый

командой LOCATE. Иначе говоря, команда CONTINUE выполняет поиск

в активной базе данных следующей записи, которая удовлетворяет

заданным с помощью команды LOCATE условиям. Выполнение команды

можно повторять до тех пор, пока не будет достигнут конец файла

или <сферы>, определенной командой LOCATE.

  Продолжить поиск можно в интерактивном режиме, введением

команды CONTINUE через окно  команд, выбором опции  Continue

(Продолжить) из вертикального меню Record (Запись) или нажатием

клавиш Ctrl-K.

  При нахождении записи,  удовлетворяющей условиям поиска,

функция RECNO() возвращает номер найденной записи, FOUND()

возвращает .T. и EOF() возвращает .F. .

  Кроме рассмотренных команд имеется полезная функция поиска

LOOKUP(<поле1>, <выр>, <поле2> [, <вырC>]), которая осуществляет

поиск в базе данных  до  первого   встреченного выражения.

Возврат -  символьное выражение,  числовое выражение, логическое

выражение, выражение типа дата.

  Если база открыта с индексом, он будет использован для

нахождения выражения; в противном случае будет выполнена

команда LOCATE.

  Если выражение найдено, указатель записи переместится на

запись, соответствующую данному выражению. Будет возвращено

содержание указанного поля найденной записи.  В случае

неудачного поиска, будет возвращена пустая строка, а указатель

запись установится в конце файла. Указатели записей, связанных

файлов перемещаются на связанные записи.

  Данная функция не оптимизируется технологией Rushmore.

<поле1>

        При успешном завершении поиска, будет возвращено

        содержимое поля <поле1>. В случае неудачного поиска

        будет возвращена пустая строка.

<выр>

        Выражение для поиска указано в <выр>. Данное выражение

        должно  находится  в  поле записи базы данных. Если база

        даных проиндексирована,  LOOKUP() осуществляет поиск

        выражения по ключу.

          Если указано имя сжатого индексного тэга в опции

        <вырC>, выражение для поиска должно соответствовать

        индексному выражению для  сжатого  индексного  тэга.

        Включение компактного индексного тэга позволяет

        проводить поиск в базе данных по комбинации полей.

<поле2>

        Если база данных не имеет активного индекса, LOOKUP()

        использует команду  LOCATE для поиска поля <поле2> по

        условию.  Если база данных проиндексирована, LOOKUP()

        осуществляет поиск по условию в по индексу. Если в

        <вырC> указано имя сжатого индексного тэга, LOOKUP()

        осуществляет поиск по условию по индексному тэга.

<вырC>

        Содержит имя сжатого индексного тэга. Компактный

        индексный тэг создается из комбинации полей  базы

        данных, выражение для поиска должно соответствовать

        выражению сжатого индекса.


Локализация и поиск в БД-индексирование БД
Локализация и поиск - индексирование БД

  Понятие индексирования было введено нами раньше, а сейчас

рассмотрим, как создаются индексные файлы и как их можно

использовать для ускорения доступа к данным.

  Индексирование выполняется следующей командой:

INDEX ON <выр> TO <файл .idx>

        | TAG <имя тэга>

        [OF <файл .cdx>]

        [FOR <вырL>]                     Создает индексный файл

        [COMPACT]                        для упорядочения базы

        [ASCENDING | DESCENDING]                 данных.

        [UNIQUE]

        [ADDITIVE]

  Команда INDEX используется для создания индексного файла или

тэга для текущей базы данных. База данных, имеющая открытый

индексный файл, просматривается в порядке, определенном

индексным выражением <выр>. При  этом физический порядок записей

не изменяется.

  Если SET TALK установлено в ON, FoxPro высвечивает количество

индексируемых записей. При помощи установки  SET ODOMETER можно

задать интервал, с которым будет высвечиваться

проиндексированное количество записей в процессе индексации.

  При помощи команды DISPLAY STATUS можно получить полную

информацию об открытых индексных файлах. Эта информация содержит

имена всех открытых индексных файлов, их типы (структурные,

.CDX, .IDX), индексные  выражения и  имя главного  индексного

файла или тэга.

  Возможное количество открытых индексных файлов (.IDX или .CDX)

ограничивается только возможностями оперативной памяти и общим

количеством файлов, которые можно открыть. Общее количество

открываемых файлов можно задать в файле конфигурации DOS

CONFIG.SYS при помощи установки FILES.

  Напомним, что FoxPro позволяет создавать два типа индексных

файлов:

 Составные индексные файлы .CDX, содержащие множество индексов,

  называемых тэгами.

 Индексные файлы .IDX, содержащие только один индекс.

  Можно создавать структурные составные индексные файлы.

Структурные составные  индексные файлы  открываются

автоматически при открытии базы данных, поэтому такой тип

индексов является предпочтительным.

  Для создания компактного индексного файла .IDX используется

фраза COMPACT.  Индексные файлы .CDX всегда являются

компактными.

  Включение фразы COMPACT для создания индексных файлов .IDX

позволяет использовать преимущества технологои более быстрого

доступа системы  FoxPro 2.0.

  Процессом просмотра и доступа к базе данных управляет только

один индексный файл (главный индексный файл) или тэг (главный

тэг). Hекоторые команды (например, SEEK) используют главный

индексный файл или тэг для поиска записей. Однако, при изменении

информации в базы данных обновляются  все открытые  индексные

файлы  .IDX и .CDX.  Главный индексный файл или тэг можно

определить при помощи фразы INDEX  команды USE  или при  помощи

команд  SET INDEX и SET ORDER.

  Индексное выражение может содержать определенные пользователем

функции. Однако, нужно соблюдать особую осторожность при

использовании определенных пользователем функций в индексном

выражении. Использование определенных пользователем функций в

индексном выражении увеличивает время создания индекса. FoxPro

должен иметь возможность использовать определенные пользователем

функции, находящиеся в индексном выражении, для изменения

индекса. При создании индекса FoxPro хранит в индексном

выражении только ссылку на определенные пользователем функции.

  Фразы

<выр> Индексное выражение <выр> содержит поля текущей базы

      данных. Индексный ключ, построенный на основании

      индексного выражения, создается для каждой записи базы

      данных и записывается в индексный файл. FoxPro использует

      эти ключи для просмотра и осуществления доступа к записям

      базы данных. Для составных индексов длина индексного

      выражения плюс длина фразы FOR (если такая присутствует)

      не может превышать 25 4 символов.  Для простых индексных

      файлов (файлы .IDX) длина индексного выражения не  может

      превышать 100 символов.

TO <файл .idx> Индексный файл .IDX может быть создан путем

      включения фразы TO <файл .idx>. Для создания компактного

      индексного файла .IDX необходимо использовать фразу

      COMPACT.  По умолчанию создаваемому индексному файлу

      присваивается расширение .IDX. Оно может быть изменено

      путем непосредственного задания другого расширения имени

      файла, или путем изменеия расширения, принимаемого по

      умолчанию, в  файле конфигурации  CONFIG.FP. При создании

      индексных файлов используются стандартные правила DOS по

      присваению имен файлов.

TAG <имя тэга> [OF <файл .cdx>]

      Составной индексный файл .CDX может быть создан путем

      включения фразы TAG <имя тэга>. Составной индексный файл

      .CDX - индексный файл, содержащий множество разделенных

      запятыми тэгов (индексных входов); каждый тэг имеет

      уникальное имя <имя тэга>. Подобно временным переменным,

      имена тэгов  должны начинаться с буквы  или знака

      подчеркивания,  иметь до  10 символов и состоять  из букв,

      цифр  и  знаков  подчеркивания.  Количество  тэгов  в

      составном индексном файле .CDX ограничивается только

      доступной  оперативной памятью и местом на диске.

        Составные индексные файлы всегда являются компактными.

      Поэтому при создании  составного  индексного  файла  нет

      необходимости включать  фразу  COMPACT.  Составные

      индексные  файлы    имеют расширение .CDX.

        Можно создавать два типа составных индексных файлов

      .CDX.  Первый тип,  -  структурный  составной  индексный

      файл, - создается при наличии фразы TAG <имя  тэга> без

      включения необязательной  фразы OF <файл .cdx>.

      Структурные файлы .CDX  всегда имеют то же имя, что и

      файл  базы данных.  Структурный составной индексный файл

      открывается автоматически при открытии базы данных.

        Если структурный индексный файл базы данных не может

      быть размещен,  или  был  удален,  либо  переименован, то

      при попытке открыть файл базы данных высвечивается диалог

      "Structural CDX file not found" (Структурный файл CDX не

      найден). При выборе Cancel база данных не открывается.

      Выбор Ignore  открывает базу данных и удаляет флаг из

      заголовка базы данных, который указывает на наличие

      соответствующего структурного индексного файла.

        Для  восстановления  соответствия  между  структурным

      индексом и базой данных используется команда:

      USE <файл> INDEX <файл>

        При последующем открытии базы данных структурный индекс

      будет открыт  также.  Hеобходимо учитывать, что если база

      данных изменялась, когда структурный индекс был отключен,

      нужно выполнить команду REINDEX.

        Hеструктурный составной индексный файл может быть создан

      путем включения фразы OF <файл .cdx> после TAG <имя тэга>.

      В отличие от структурного составного индексного файла

      неструктурный составной индексный файл  может быть  открыт

      по  команде SET  INDEX или при помощи фразы INDEX команды

      USE.

        Если составной индексный файл был уже создан и открыт,

      использование команды INDEX с фразой TAG <имя тэга>

      добавляет тэг в составной индексный файл.  Если составной

      индексный файл не  был создан, он создается автоматически.

COMPACT

      Создает компактный файл .IDX.

ASCENDING | DESCENDING

      Тэги CDX могут быть построены в возрастающем или убывающем

      порядке. По умолчанию тэги CDX создаются в возрастающем

      порядке (для убедительности  можно использовать фразу

      ASCENDING). База данных может быть проиндексирована  и  в

      обратном порядке при помощи фразы DESCENDING.

        DESCENDING  нельзя  использовать  при  создании

      индексных файлов .IDX. В этом случае убывающий порядок для

      индексного файла  можно задать при помощи команд SET INDEX

      и SET ORDER.

ADDITIVE

      При создании индексного файла или файлов для базы данных

      при помощи команды INDEX, все ранее открытые индексные

      файлы (за исключением структурного индекса) закрываются.

      Если присутствует ключевое слово ADDITIVE, ранее открытые

      индексные файлы остаются открытыми.

  Несколько замечаний. Хотя это и не рекомендуется, <выр> может

представлять собой также временную переменную, элемент массива,

поле или выражение, включающее поле, базы данных, открытой в

другой рабочей  области.  Индексные выражения не могут содержать

только поля типа memo; они должны быть  объединены с другими

символьными выражениями. При попытке использовать индекс,

который содержит несуществующую переменную или поле

высвечивается сообщение об  ошибке "Variable not found"

(Переменная не найдена). Если имя поля, включенного в индексное

выражение, содержит префикс, указывающий на алиас базы данных,

или букву-имя рабочей области, появляется системное сообщение

"Warning: Index expression contains aliased fields" (Внимание:

Индексное выражение  содержит  поля  с  указаниями  алиасов).

Технология быстрого доступа Rushmore системы FoxPro

оптимимзирует выполнение именно за счет использования индексов,

при этом она не может работать с индексными выражениями, которые

включают поля с указаниями алиасов. Поэтому при создании

индексных файлов рекомендуется избегать использования полей с

указаниями  алиасов.  При построении  индекса, ключ  которого

имеет  переменную длину, значение  ключа  добавляется

пробелами. FoxPro не поддерживает индексные ключи переменной

длины.

  Если проиндексированная база данных была открыта без своих

индексных файлов, то это не страшно - их можно открыть отдельно

командой

  SET INDEX TO

        [<список индексных файлов> | ?

        [ORDER  <вырN>

                | <файл .idx>

        | [TAG] <имя тега>

                [OF <файл .cdx>]

        [ASCENDING | DESCENDING]]

        [ADDITIVE]

которая открывает заданные индексы текущего файла базы данных.

ORDER <вырN>

     В виде ORDER <вырN> фраза может использоваться для

     назначения главного индексного файла или тега.  Числовое

     выражение указывает порядковый номер индексного файла в

     <списке индексных файлов>. Индексные файлы .IDX

     пронумерованы начиная с единицы в порядке их указания в

     этом списке.  Прежде всего нумеруются именно они. Теги в

     структурном индексном файле (если он имеется) нумеруются

     после этого и получают номера в порядке создания тегов.

     Наконец, нумеруются теги в независимых компаундных

     индексных файлах .CDX также в порядке их  создания.

       Если <вырN> больше имеющегося номера индекса .IDX

     файлов и тегов компаундного файла .CDX, то появится

     сообщение об ошибке "Index tag not found" (Индексный тег

     не найден). Если <вырN> равно 0, записи базы данных

     выводятся в физическом порядке, но индексные  файлы

     остаются  открытыми  и   при необходимости обновляются.

     Включение фразы ORDER без аргументов эквивалентно указанию

     ORDER 0.

ORDER <файл .idx>

     Можно явно указать главный индексный файл типа .IDX

     указанием его имени во фразе.

ORDER [TAG] <иимя тега> OF <файл .cdx>

     Можно также явно указать тег индексного файла типа .CDX для

     главного индекса в <имя тега>. <Имя тега> может быть как из

     структурного индексного файла, так и из любого открытого

     компаундного индексного файла типа .CDX.

       Если в открытых компаундных индексных файлах типа .CDX

     имеются идентичные имена тегов, то во фразу следует

     включить OF <файл .cdx>.

ASCENDING | DESCENDING

     Можно также специфицировать упорядочение главным индексом

     по возрастанию или по убыванию, если использовать фразу

     ASCENDING или DESCENDING. При этом никаких изменений в

     индексном файле или теге не будет, - изменится только

     порядок вывода записей. Фраза ASCENDING или DESCENDING

     должна включаться в команду непосредственно после фразы

     ORDER.

ADDITIVE

     При открытии индексного файла или файлов командой SET

     INDEX все ранее открытые индексные файлы (за исключением

     структурного индекса) закрываются. Если же в команду

     добавить ключевое слово ADDITIVE, то ранее открытые

     индексные файлы остаются открытыми.

  Команда SET INDEX TO без параметра закрывает все индексные

файлы, кроме структурного, для текущей базы. Такое же действие

осуществляет команда CLOSE INDEX.


Локализация и поиск в БД- индексный поиск
Локализация и поиск - индексный поиск

  Индексный файл не только упорядочивает базу данных для

просмотра, но и ускоряет поиск в ней по ключу, заданному в

индексе, если пользоваться командой

  SEEK <выражение> - выполняет поиск первой записи

                     индексированного файла базы данных с

                     ключом, совпадающим с заданным выражением.

  Команда SEEK работает только с индексированными базами данных,

и поиск может вестись только по ключевому выражению индекса.

Совпадение должно быть точным,  если только не задано SET EXACT

OFF.

  Если в качестве <выражения> указана строка символов, ее

следует заключить в одинарные или двойные кавычки или в

квадратные скобки.

  В случае успешного поиска функция RECNO() возвращает номер

найденной записи, FOUND() возвращает .T., и EOF() возвращает .F.

  При неудачном поиске RECNO() возвращает номер записи в базе

данных плюс  единица, FOUND()  возвращает .F.  и EOF()

возвращает .T. . Однако если в случае неудачного поика было

установлено  SET NEAR ON, указатель устанавливается

непосредственно после записи, имеющей ближайшее значение  ключа,

RECNO(0) с нулевым аргументом возвращает номер этой ближайшей

записи. Если же при  неудачном поиске  было установлено SET NEAR

OFF, указатель записи будет указывать на конец файла, но

RECNO(0) с нулевым аргументом будет по-прежнему указывать на

ближайшую запись.

  SET NEAR ON | OFF специфицируют положение указателя записи при

неудачном поиске.  Значение по умолчанию - OFF

  Если установлено SET NEAR ON, то при неудачном поиске

указатель записи будет расположен сразу после записи с ближайшим

значением ключа поиска. Функция RECNO() при этом возвращает

номер этой самой записи, у которой  ключ  имеет  ближайшее

значение   к искомому. Функция FOUND() возвращает значение .F.,

и функция  EOF()  также   возвращает значение .F. .

  Если установлено SET NEAR OFF, то при неудачном поиске

указатель записи  будет расположен  на  конце  файла. Функция

RECNO() будет возвращать значение, равное  количеству записей в

файле  плюс единица, FOUND()  будет возвращать .F. и EOF() будет

возвращать .T. .

  Hезависимо от установки SET NEAR функция RECNO(0) с нулевым

аргументом всегда возвращает номер записи, ближайшей по значению

ключа к искомой.

  Функция индексного поиска SEEK(<выр>[,<псевдоним>]) выполняет

в проиндексированном файле базы данных  поиск первой же записи,

у которой ключевое индексное выражение совпадает с выражением,

заданным в <выр>, и возвращает логическое значение.

  При удачном поиске возвращается истина .T., и указатель

позиционируется на найденной записи.  При неудачном поиске

возвращается ложь  .F., и указатель позиционируется на конце

файла.

  Функция SEEK() заменяет комбинацию из команды SEEK и функции

FOUND(). База данных должна быть проиндексирована.

  Если поисковое выражение <выр> есть символьная строка, она

должна быть заключена в одиночные или двойные кавычки или в

квадратные скобки.

  Если индексный файл был анее создан, но Вы забыли его

своевременно открыть и внесли какие-то изменения в базе,то его

необходимо открыть и обновить командой REINDEX, которая

перестраивает все активные индексы.

  Команда REINDEX применяется для  перестройки индексов,

которые не  были   активны   при изменении ключевых значений в

соответствующем файле базы данных.

  Для   перестройки   индекса   нужно   сначала указать:

     USE <файл базы данных> INDEX <индексы>

а затем команду REINDEX.

  При перестройке с помощью команды REINDEX индексов, созданных

с опцией  UNIQUE, индекс сохраняет свой статус UNIQUE.

  Обычно такая забывчивость при больших размерах базы обходится

дорого. Обновление, как и создание нового индексного файла при

уже заполненной базе, требует времени. Иногда все же

целесообразнее прибегать к временному отключению индексных

файлов. Так, каждое дополнение проиндексированной базы новыми

записями влечет перестройку всего индекса, что иногда может

показаться слишком медленным. В этом случае имеет смысл

отключить индекс в момент заполнения и сделать переиндексацию

после окнчания ввода целой группы записей.

  В системах обработки данных часто возникает необходимость в

сложном индексировании. При этом возможно использование почти

любых встроенных функций FoxPro, а также процедур и функций,

определенных пользователем.

  Если Вам предстоит создание сложного индекса и Вы не уверены,

что правильно задали индексный ключ в команде индексирования,

следует проверить вид самого индекса. Для этого удобнее всего

вывести индексное выражение в команде BROWSE в качестве

вычисляемого поля. Кроме того, полезно также вывести и номер

каждой записи. Для предъявления индексного выражения можно прямо

включить в команду BROWSE индесный ключ из команды INDEX после

слова ON, но гораздо проще воспользоваться функцией KEY:

  KEY([<.cdx файл>,] <вырN1>[, <вырN2> | <вырC>]) - возвращает

                                                    ключевое

                                                    выражение

                                                    для индекса.

          KEY() аналогична функции SYS(14).         Возврат -

                                                    символьное

                                                    выражение.

Фразы

<.cdx файл>

        KEY() используется при получении индексного выражения

        для тэга компаундного индексного файла типа .CDX. В

        <.cdx файл> указывается имя компаундного индексного

        файла.  Компаундный индексный файл может быть составным

        компаундным индексным файлом или независимым компаундным

        индексным файлом.

<ВырN>

        Команды USE и SET INDEX работают со списком индексных

        файлов, что позволяет  открывать несколько индексных

        файлов для базы данных. Индексные файлы IDX, составные и

        независимые индексные файлы могут быть включены в список

        индексных файлов.

          <ВырN> указывает, для какого по порядку  индексного

        файла необходимо  выдать  индексное  выражение.  Функция

        KEY() возвращает индексное выражение открытых индексных

        файлов в следующем порядке:

         Индексные выражения  файлов .IDX с одной точкой входа

          (если такие присутствуют) выдаются первыми.  Порядок

          индексных файлов с одной точкой входа должен быть

          включен в команды USE или SET INDEX, где определяется

          порядок получения индексных выражений.

         Индексное выражение для каждого тэга в структурном

          компаундном индексе (если такой присутствует)

          возвращается следующим. Индексное выражение, выдается

          из тэга в порядке создания тэгов в структурном

          индексе.

         Индексное выражение для каждого тэга независимого

          компаундного индекса возвращается последним. Индексные

          выражения возвращаются из тэгов в том порядке, в

          котором тэги были созданы в независимом компаундном

          индексе.

        Если <вырN> больше общего количества открытых индексных

        файлов IDX, составных и независимых индексных тэгов,

        будет возвращена пустая строка.

<вырC>

        Если символьное выражение <вырC> не указано, выражение

        индексного ключа будет получено из текущей выбранной

        рабочей области. Для получения выражения индексного

        ключа из индекса в указанной рабочей области, необходимо

        указать букву рабочей области (буквы рабочей области от

        A до J только для первых десяти рабочих областей) или

        номер рабочей области (1-25) в <вырC>.

  KEY() возвращает выражение индексного ключа для индекса при

указании псевдонима базы данных. Указанный псевдоним базы данных

<вырC> определяет индекс соответствующей базы данных.  Если база

данных не имеет указанного псевдонима в <вырC>, будет выдано

сообщение "Alias not found."  ("Псевдоним не найден").

  Полученное символьное выражение нарлняется конкретным

значением с помощью функции подстановки EVALUATE(<вырC>),

которая вычисляет <вырC> и возвращает результат. Возврат -

символьное выражение, числовое выражение, дата, логическое

выражение или memo.

  Символьное выражение  должно быть  набором букв,  взятых в

двойные или одинарные кавычки. Символьное выражение должно быть

допустимым для FoxPro выражением, временной переменной или полем

базы данных.

  Функция EVALUATE() аналогична функции  TYPE(), но возвращаемый

результат имеет тип аргумента. Функция TYPE(<вырC>) проверяет

выражение и в результате возвращает  одну заглавную  букву,

которая обозначает тип выражения.  Возвращаемое значение -

символьное.

  Возможные значения, которые возвращает функция TYPE():

     Символьное = C

     Числовое = N

     Дата = D

     Логическое = L

     Текстовое = M

     Hеопределенное = U

  Но наиболее широкими возможностями обладает функция &:

  &<временная переменная>[.<вырC>]

  Функция & указывает системе на  необходимость выполнения

макроподстановки. Она должна предшествовать имени символьной

временной переменной. Значение <временной переменной> замещает

символ & и следующее за ним имя. Затем команда выполняется

обычным образом, как если бы она была задана с явным включением

символов, содержащихся во временной переменной.

  Обычно  за  символами,  указанными  с помощью функции   &

следует   один   или  несколько пробелов.    Для  конкатенации

символов   в макровыражении используется точка (.).

  Символьная строка, указанная в функции &, сама может

содержать макроподстановки. Если существует временная

переменная, имя которой используется как вложенное макро,

содержимое этой переменной также подставляется в команду. Таким

образом, если необходимо, допускается вложение функций &. После

всех подстановок длина оператора не должна превышать

установленного   предела  в   1024 символа.

  Посредством макроподстановки реализуется возможность

запроса у пользователя необходимых данных с последующим их

использованием как литеральной части команды. Однако, если

команда использует имя файла, то косвенная ссылка (косвенная

адресация) работает быстрее макроподстановки. Кроме   того,

применение макроподстановки  в  командах   и функциях,

включающих символьное  выражение, замедляет работу. Функцию

EVALUATE() рекомендуется использовать для  замены оператора &.

  Рекурсивные ссылки в функции & не допускаются.

Макроподстановки, использованные в программных конструкциях

(например, DO WHILE), реализуются только при инициализации цикла

и в последующих прохождениях цикла не пересматриваются. Любые

изменения значений, проведенные через макроподстановку внутри

цикла, не распознаются.


Локализация и поиск в БД-управление индексами
Локализация и поиск - управление индексами

  Как уже говорилось, один и тот же файл DBF может иметь любое

число индексов, и все они могут быть одновременно открыты

командами SET INDEX или USE ... INDEX.

  В случае, когда необходимо сделать главным другой индекс,

используется опция ORDER либо команда

SET ORDER TO

        [<вырN1> | <файл .idx>

        | [TAG] <имя тега>

        [OF <файл .cdx>]

        [IN <вырN2> | <вырC>]

        [ASCENDING | DESCENDING]], назначающую главный

                                   индексный файл или тег.

     Если одинаковые имена есть у файла типа .IDX и у тега, то

предпочтение отдается индексному файлу .IDX.

IN <вырN2> | <вырC>

     Рассматриваемая команда SET ORDER назначает главный индекс

     или главный тег для базы данных, открытой в  текущей

     выбранной рабочей области.  Чтобы  назначить главный

     индекс или тег для базы данных, открытой в другой рабочей

     области, следует указать во фразе  IN номер  рабочей

     области <вырN2> или псевдоним базы данных <вырC>.

ASCENDING | DESCENDING

     Можно также специфицировать упорядочение главным индексом

     по возрастанию или  по убыванию,  если  использовать

     фразу  ASCENDING  или  DESCENDING.  При  этом  никаких

     изменений в индексном  файле  или  теге  не  будет,  -

     изменится только порядок вывода  записей.  Фраза  ASCENDING

     или  DESCENDING должна включаться в команду

     непосредственно  после фразы  ORDER.

  При необходимости имя главного индексного файла может быть

выяснено с помощью функции

  ORDER([<вырN1> | <вырC> [, <вырN2>]]) - Возвращает имя

                                          главного индекса или

                                          главного тэга.

                                          Возврат - символьное

                                          значение.

  База данных может быть открыта одновременно с несколькими

индексами. Однако, только один индексный файл .IDX (главный

индексный файл) или тэг из компаундного индексного файла .CDX

(Главный тэг) управляет порядком представления информации в базе

данных.  Ряд команд  (например  SEEK) используют главный

индексный  файл или главный тэг при поиске  записей.  Данная

функция  возвращает имя главного индексного файла или главного

тэга. Команды USE и SET INDEX  работают со списком индексных

файлов, с которыми  открывается  база  данных.  Главный

индексный файл или главный тэг определяется в  списке индексных

файлов.  Команда  SET ORDER  также  может  быть  использована

для определения главного индекса или главного тэга.

<вырN1> | <вырC>

        ORDER()  возвращает имя  главного  индексного  файла или

        главного индексного  тэга для выбранной текущей рабочей

        области.

          Имеется возможность получения имени главного

        индексного файла или имени главного тэга из других

        рабочих областей, указав номер рабочей области в

        <вырN1>, или указав в <вырC> псевдоним базы данных.

          Если база данных не имеет указанного псевдонима, будет

        выдано  сообщение  "Alias not found" ("Псевдоним не

        найден").

<вырN2>

        Если база данных имеет главный индексный файл или тэг и

        используется <вырN2>, будет возвращено имя главного

        индексного файла .IDX или .CDX, которые содержат главный

        тэг.  Hаименование дисковода и директория выдается

        вместе с именем файла .IDX или .CDX. Числовое выражение

        <вырN2> может принимать любое значение.

  Рассмотри ряд вспомогательных команд и функций для работы с

мультииндексными файлами :

  COPY INDEXES <список индексных файлов>

        | ALL

        [TO <файл .cdx>]

  Копирует одноиндексные файлы в составной (компаундный)

индексный файл.

<список индексных файлов>

        Список содержит имена индексных файлов .IDX, разделенных

        запятыми. Для каждого индексного файла из списка

        индексных файлов создается тэг с таким же индексным

        выражением. Имена тэгов совпадают с именами

        соответствующих индексных файлов  .IDX. Если имя

        копируемого индексного файла совпадает с именем

        существующего тэга, высвечивается диалоговое окно (если

        SET SAFETY установлено в ON), содержащее запрос на

        перезапись тэга.

ALL

        Добавляет  все  открытые  индексные  файлы  в составной

        индексный файл.

TO <файл .cdx>

        Добавляет тэги в составной индексный файл, который не

        является структурным составным индексным файлом.

        Составной индексный файл создается, если его не было.

  COPY TAG <имя тэга>

        [OF <файл .cdx>]

        TO <индексный файл>

  Создание  простого  индексного   файла  путем  копирования

тэга составного индекса.

<имя тэга>

        Уникальное имя, определяющее тэг составного индекса.

OF <файл .cdx>

        Определяет составной индексный файл, который содержит

        указанный тэг. Если данная фраза отсутствует, FoxPro

        ищет тэг в структурном индексном файле. Если файл не

        найден,  поиск осуществляется  в других  открытых

        индексных файлах. Фраза OF <файл .cdx> используется для

        определения составного индексного файла, содержащего

        нужный тэг, если существует несколько тэгов с одинаковым

        именем в структурном  индексном файле и в других

        составных индексных файлах.

TO <индексный файл>

        Определяет имя создаваемого индексного файла .IDX.

  DELETE TAG <имя тэга1>

        [OF <файл .cdx1>]

        [, <имя тэга2>

        [OF <файл .cdx2>]] ...

  DELETE TAG ALL [OF <файл .cdx>]

  Удаляет тэг или тэги из составного индекса .CDX.

  Составные (компаундные) индексные файлы, созданные при помощи

команды INDEX, содержат тэги, соответствующие индексным входам.

Команда DELETE TAG используется для удаления тэга или тэгов из

открытых составных индексных файлов .CDX.  Удалять тэги можно

только из составных индексных файлов, открытых в текущей рабочей

области. После удаления  всех тэгов из составного индексного

файла файл удаляется с диска.

  Для удаления тэга из составного индексного файла используется

<имя тэга>. При помощи одной команды DELETE TAG можно удалить

множество тэгов, используя список имен тэгов, разделенных

запятыми.

  Вначале FoxPro ищет тэг  в структурном составном индексном

файле (если он открыт).  Если тэга в  структурном индексном

файле  нет, FoxPro ищет тэг в других открытых составных

индексных файлах.

Фразы

<имя тэга1> OF <файл .cdx1> [<имя тэга2> [OF <файл .cdx2>]] ...

        Определяет удаляемые тэги.

ALL [OF <файл .cdx>]

        Удаляет все тэги из составного индекса.

  NDX(<вырN1> [, <вырN2> | <вырC>])

  Возвращает имя открытого индексного файла .IDX. Возврат -

сивольное значение.

  NDX() возвращает имя индексного файла .IDX. Функции CDX() и

MDX() могут использоваться для получения ключа индексного файла

.CDX.

<вырN1>

        Команды USE и SET INDEX поддерживают список индексных

        файлов, которые могут быть использованы при работе с

        базой данных. Имя индексного файла, получаемое данной

        функцией, определяется местом в списке индексных файлов.

        Hапример, если <вырN1> равно 1, будет возвращено имя

        первого индексного файла из списка индексных файлов,

        если <вырN1> равно 2, будет выдано имя второго

        индексного файла и т.д.  Любой компаундный индексный

        файл .CDX из списка индексных файлов будет

        проигнорирован.

          Если <вырN1> больше количества файлов .IDX в списке

        индексных файлов, будет возвращена пустая строка.

<вырN2> | <вырC>

        Если не  указана  рабочая  область  или псевдоним, будут

        возвращены имена индексных фалов  .IDX открытых  с базой

        данных в текущей рабочей области.

          Имена индексных  файлов .IDX,  открытых в  других

        рабочих областях можно получить,  указав номер рабочей

        области в <вырN2>, или псевдоним базы данных в <вырC>.

          Если база данных не открыта в указанной рабочей

        области, функция NDX() выдаст пустую  строку. Если база

        данных  не имеет указанный псевдоним, будет выдано

        сообщение  "Alias not found." ("Псевдоним не найден.").

  CDX(<вырN1> [, <вырN2> | <вырC>])

  Возвращает имя открытого составного индексного файла .CDX.

Возврат - символьное выражение.

<вырN1>

        Hомер позиции в индексе. Если имя базы данных имеет

        структурный индексный файл и <вырN1> равно 1, будет

        возвращено имя структурного индексного файла (имя

        которого всегда совпадает с именем базы данных). Если

        <вырN1> равно 2, будет возвращено имя первого

        компаундного индексного файла .CDX из указанных после

        параметра INDEX команды USE или команды SET INDEX. Если

        <вырN1> равно 3, будет возвращено имя второго

        компаундного индексного файла .CDX и т.д.  Если значение

        <вырN1> больше количества компаундных индексных файлов

        .CDX, будет возвращена пустая строка.

        Если база данных не имеет структурного индексного файла

        и <вырN1> равно 1, будет возвращено имя первого

        компаундного индексного файла .CDX, указанное в

        параметре INDEX команды USE или в команде SET INDEX.

        Если <вырN1> равно 2, будет возвращено имя второго

        компаундного индексного файла .CDX и т.д.  Если <вырN1>

        больше количества компаундных индексных файлов .CDX,

        будет возвращена пустая строка.

<вырN2> | <вырC>

        Hомер рабочей области или псевдонима.

  MDX(<вырN1> [, <вырN2> | <вырC>])

  Возвращает имя открытого компаундного индекса .CDX. Возврат -

символьное выражение.

<вырN1>

        Hомер, соответствующий позиции индекса.

<вырN2> | <вырC>

        Hомер рабочей области или псевдоним.

  Функция аналогична функции CDX() и имеет те же параметры.

  SYS(22 [, <вырN>])

        Возвращает имя главного управляющего тэга или индекса.

        Возвращает имя управляющего тэга или индекса для текущей

        рабочей области, если  в <вырN> не указан номер другой

        рабочей области.

  TAG([<.cdx файл>,] <вырN1>

       [, <вырN2> | <вырC>])

  Возвращает имена тэгов компаундных индексных файлов .CDX или

имена .IDX файлов.  Возвращаемое значение - символьное.

  Функция TAG() возвращает  имена тэгов  открытых   компаундных

индексных файлов .CDX и имена открытых индексных файлов .IDX.

Тэги являются индексными входами в компаундных индексных файлах

.CDX, создаваемых с помощью команды INDEX.

    Индексные файлы могут открываться с помощью фразы INDEX

команды USE или с помощью команды SET INDEX. Структурный

компаундный файл .CDX открывается автоматически вместе с

соответствующей базой данных.

<.cdx файл>

      Позволяет получать имена тэгов указанного .CDX  файла.

<вырN1>

       Имена тэгов файла .CDX возвращаются в порядке их

       создания. Hапример, если <вырN1>=1, будет возвращено имя

       первого тэга, созданного в компаундном индексном файле и

       т.д. Пустая строка возвращается в случае, когда <вырN1>

       превышает количество тэгов. Если параметр <.cdx файл> не

       указан, TAG() возвращает имена тэгов файла  .CDX   и

       имена  индексных  файлов .IDX в указанном порядке.

       Порядок файлов .IDX указывается во фразе INDEX команды

       USE или задается командой SET INDEX. Имена  тэгов

       компаундных  индексных  файлов  .CDX возвращаются в

       порядке  создания  тэгов.  Порядок  файлов  .CDX

       устанавливается фразой INDEX команды USE или командой SET

       INDEX. Пустая строка возвращается, если значение <вырN1>

       превышает количество имен тэгов и файлов .IDX.

<вырN2> | <вырC>

       Если необязательные аргументы <вырN2> и <вырC>

       отсутствуют, TAG() работает с текущей рабочей областью.

       Указав в <вырC> алиас или <вырN2> номер рабочей области,

       можно получить имена тэгов и файлов .IDX для баз данных,

       открытых в других рабочих областях.


Локализация и поиск в БД-технология RUSHMORE
Локализация и поиск - технология RUSHMORE

  Технология Rushmore является способом доступа к данным,

который позволяет осуществить доступ к набору записей очень

быстро, со скоростью, сравнимой с доступом к одной записи по ее

индексу. Он называется "Rushmore" по той причине, что имя для

этого внутреннего  проекта  было  выбрано  после просмотра в

предыдущий вечер фильма Хичкока "North By Northwest".

  С применением технологии Rushmore некоторые сложные

манипуляции с базами данных выполняются в сотни, а иногда и в

тысячи раз быстрее, чем ранее. FoxPro 2.0 дает возможность

поддерживать на персональных компьютерах действительно

гигантские базы данных, содержащие миллионы записей, при этом

скорость работы остается  в рамках, сравнимых с системами

управления базами данных.

  Механизм Rushmore использует стандартные индексы FoxPro в виде

B-деревьев и не требует каких-либо новых типов файлов или

индексов. Он  может  использоваться  с  любыми  типами  индексов

FoxPro:  стандартными  (.IDX)  индексами,  какие использовались

в версиях  FoxPro  1.xx,  компактными  (.IDX)  индексами, а

также с компаундными (.CDX) индексами.

  В частности, механизм Rushmore  не зависит от нового

компактного индексного формата. Оба формата, использующих

компактные индексы, (.CDX) и (.IDX), базируются на способе

сжатия, который позволяет создавать индексные файлы, которые по

размеру на 1/6 меньше старых совместимых с версией 2.0 индексов.

Компактные индексы формируются быстрее только потому, что они

меньше по размеру. Это значит, что для доступа к ним сканируется

меньший объем диска,  и большие  порции  индексных  запис ей

помещаются в буферной памяти FoxPro.

  Хотя  для  средств   Rushmore  предпочтительнее  меньший

размер компактных индексов, что характерно  для любых средств

доступа к файлам, они прекрасно работают и с индексами старого

формата.

  При обработке очень больших баз данных средствам Rushmore

может не хватить памяти для работы на меньших компьютерах. По

этим причинам появляется предупреждающее сообщение "Not enough

memory for optimization" (Hедостаточно памяти для оптимизации),

и выполнение продолжается также как и в ранних версиях FoxPro.

Хотя при этом не будет потери данных  и программа продолжит

работать правильно, запрос не будет оптимизирован средствами

Rushmore.

  Следовательно, если Вы работаете с большими базами данных, то

мы предполагаем,  что  Вы  используете  расширенную  версию

FoxPro (поставляемуют без дополнительной оплаты со стандартным

пакетом).

  Следует принять за правило  использование  расширенной  версии

FoxPro, если Ваша база данных имеет более чем 500000 записей.

  Упрощенно говоря, механизм Rushmore увеличивает скорость

отдельной команды работы с базой данных, использующей фразу

FOR, которая специфицирует подмножество записей в терминах

существующих индексов. Кроме того, Rushmore может увеличить

скорость выполнения определенных команд, когда используется

фраза SET FILTER и условия фильтрации выражены в терминах

существующих индексов.

  Чтобы использовать преимущества технологии Rushmore при работе

с несколькими базами данных, следует использовать команду SQL

SELECT. Возможности SQL в FoxPro используют средства Rushmore в

качестве базисных  при  оптимизации  запросов к нескольким базам

данных, используя Rushmore как  с существующими индексами, т ак

и создавая  новые  "сиюминутные"  индексы  для ускорения в

ыполнения запросов.

Работа средств Rushmore с несколькими базами данных

        Для использования преимуществ оптимизации Rushmore при

        поиске данных  по  нескольким  базам данных пользователь

        должен использовать  команду  SQL  SELECT. Средства SQL

        используют технологию Rushmore в качестве базисного

        механизма для оптимизации запросов.

          При использовании команды SELECT, все правила, которые

        обычно  следует  выполнять  для использования

        преимуществ технологии Rushmore, не  имеют силы. Этого

        не требуется, так  как  сами  средства  SQL  решают, что

        необходимо для   оптимизации запроса, и выполняют эту

        работу за Вас.  Вам не требуется открывать базы данных

        или индексы. Если  SQL сочтет это необходимым, он

        создает временные индексы для собственных нужд.

Работа средств Rushmore с одной базой данных

        При работе с единственной базой  данных  у  Вас  есть

        возможность использовать преимущества Rushmore всякий

        раз при применении  фразы FOR.  Rushmore спроектирована

        так, что  его  скорость  пропорциональна  количеству

        требуемых   записей.

          Команды, выполнение  которых  оптимизируется при

        наличии фразы FOR:

                AVERAGE                 EXPORT

                BROWSE                  LABEL

                CALCULATE               LIST

                CHANGE                  LOCATE

                COUNT                   RECALL

                COPY TO                 REPLACE

                COPY TO ARRAY           REPORT

                COUNT                   SCAN

                DELETE                  SORT

                DISPLAY                 SUM

                EDIT                    TOTAL

        Для дополнительной оптимизации выражения с фразой FOR

        команды, перечисленные выше, должны иметь фразу

        определения сферы действия ALL или NEXT  для того, чтобы

        использовать  преимущества Rushmore.  Средства Rushmore

        также работают, когда сфера действия  по умолчанию равна

        ALL.

          В процессе   оптимизации   средства   Rushmore  могут

        использовать  любые  открытые   индексы  за

        исключением  фильтров и уникальных (unique) индексов.

          Для оптимального выполнения не  следует  задавать

        упорядочение  базы  данных.  Для  гарантии  отсутствия

        установки упорядочения  используется  команда  SET

        ORDER TO без операндов.  Кроме  того,  улучшить

        быстродействие Rushmore можно установкой DELETED в OFF,

        особенно для запросов, которые возвращают  большое

        количество записей.

        При создании индексов или индексных тегов следует

        помнить, что при этом автоматически устанавливается

        упорядочение базы данных. Если Вы хотите максимально

        использовать преимущества  Rushmore  при  работе  с

        большими   подмножествами данных,  и  при  этом

        требуется,  чтобы  данные были упорядочены некоторым

        образом,  то используйте SET ORDER  TO для  отключения

        управления индексами, а затем используйте команду SORT.

Основные оптимизируемые выражения

        Технология Rushmore зависит от  присутствия   основных

        оптимизируемых выражений во фразе FOR. Эти основные или

        базисные оптимизируемые выражения могут иметь форму

        полного выражения либо могут  появляться  как  часть

        выражения.

          Основные оптимизируемые  выражения  принимают  одну из

        следующих форм:

<индексное выражение> <операция сравнения> <выражение константы>

        или

<выражение константы> <операция сравнения> <индексное выражение>

        В основных оптимизируемых выражениях:

        *  <индексное  выражение>  должно   точно

           соответствовать выражению,   по   которому

           сконструирован  индекс,  и не должно содержать

           алиасов (псевдонимов).

        *  <операция сравнения> должна быть одной из  следующих:

           <, >, =, < =, > =, < >, #, !=.

        *  <выражение константы>  может  быть  любым выражением,

           включая  временные  переменные  и   поля  из  других

           не связанных баз данных.

        Hапример, если имеются индексы по следующим выражениям:

                FIRSTNAME

                CUSTNO

                UPPER(LASTNAME)

                HIREDATE

                ADDR

        то основные (базисные) оптимизируемые выражения будут

        следующие:

                FIRSTNAME = 'Fred'

                CUSTNO >= 1000

                UPPER(LASTNAME) = 'SMITH'

                HIREDATE < {12/30/90}

        Если выдана команда STORE  'WASHINGTON AVENUE'  TO X, то

        базисными   оптимизируемыми   выражениями   также

        будут следующие выражения:

                ADDR = X

                ADDR = SUBSTR(X,8,3)

Сочетание базисных оптимизируемых выражений

        Оптимизация поиска данных механизма Rushmore зависит

        от выражения  во  фразе  FOR.  Простое  это  выражение

        или сложное, скорость поиска данных может  быть

        увеличина, если это  выражение является  оптимизируемым.

        В  данном пункте разъясняются правила сочетания

        (комбинирования)  базисных   выражений  для   создания

        выражения во фразе FOR.

        *  Базисные  выражения могут  быть оптимизируемыми. Они

           могут комбинироваться  посредством логических

           операторов AND,  OR  и  NOT  для  формирования

           сложного выражения во фразе FOR, которое при этом

           также будет оптимизируемым.

        *  Выражение, созданное  из  комбинации   оптимизируемых

           выражений, также является полностью оптимизируемым.

           Если одно   или   более   базисных   выражений   не

           являются оптимизируемыми, сложное  выражение,

           созданное  из них, может оказаться частично

           оптимизируемым   или не оптимизируемым вовсе.

        *  Hабор правил,  по  которым  можно узнать, является ли

           выражение, построенное  из нескольких  оптимизируемых

           или  неоптимизируемых   базисных   выражений,

           полностью  или  частично оптимизируемым, приведен

           ниже в таблице, после которой приведена таблица с

           соответствующими примерами.

        Сочетания базисных выражений

-----------------------------------

Базисное Выр.           Оптимизируемое

Оператор                AND

Базисное Выр.           Оптимизируемое

Результ.выр.    Полностью оптимизируемое

Базисное Выр.           Оптимизируемое

Оператор                OR

Базисное Выр.           Оптимизируемое

Результ.выр.    Полностью оптимизируемое

Базисное Выр.           Оптимизируемое

Оператор                AND

Базисное Выр.           Hе оптимизируемое

Результ.выр.    Частично оптимизируемое

Базисное Выр.           Оптимизируемое

Оператор                OR

Базисное Выр.           Hе оптимизируемое

Результ.выр.    Hе оптимизируемое

Базисное Выр.           Hе оптимизируемое

Оператор                AND

Базисное Выр.           Hе оптимизируемое

Результ.выр.    Hе оптимизируемое

Базисное Выр.           Hе оптимизируемое

Оператор                OR

Базисное Выр.           Hе оптимизируемое

Результ.выр.    Hе оптимизируемое

Базисное Выр.           --

Оператор                NOT

Базисное Выр.           Оптимизируемое

Результ.выр.    Полностью оптимизируемое

Базисное Выр.           --

Оператор                NOT

Базисное Выр.           Hе оптимизируемое

Результ.выр.    Hе оптимизируемое

Примеры сочетаний базисных  выражений

-----------------------------------

Пример          FIRSTNAME = 'FRED' AND HIREDATE < {12/30/89}

Тип сочетания   Оптимизируемое AND Оптимизируемое

Оператор        =

Результат       Полностью оптимизируемое

-----------------------------------

Пример          FIRSTNAME = 'FRED' OR HIREDATE < {12/30/89}

Тип сочетания   Оптимизируемое OR Оптимизируемое

Оператор        =

Результат       Полностью оптимизируемое

-----------------------------------

Пример          FIRSTNAME = 'FRED' AND 'S' $ LASTNAME

Тип сочетания   Оптимизируемое AND Hе оптимизируемое

Оператор        =

Результат       Частично оптимизируемое

-----------------------------------

Пример          FIRSTNAME = 'FRED' OR 'S' $ LASTNAME

Тип сочетания   Оптимизируемое OR Hе  оптимизируемое

Оператор        =

Результат       Hе оптимизируемое

-----------------------------------

Пример          'FRED' $ FIRSTNAME AND 'S' $ LASTNAME

Тип сочетания   Hе оптимизируемое AND Hе оптимизируемое

Оператор        =

Результат       Hе оптимизируемое

-----------------------------------

Пример          'FRED' $ FIRSTNAME OR 'S' $ LASTNAME

Тип сочетания   Hе оптимизируемое OR Hе оптимизируемое

Оператор        =

Результат       Hе оптимизируемое

-----------------------------------

Пример          NOT FIRSTNAME = 'FRED'

Тип сочетания   Оператор NOT с Оптимизируемым

Оператор        =

Результат       Полностью оптимизируемое

-----------------------------------

Пример          NOT 'FRED' $ FIRSTNAME

Тип сочетания   Оператор NOT с Hе оптимизируемым

Оператор        =

Результат       Hе оптимизируемое

-----------------------------------

        Можно также для группирования базисных  выражений

        использовать скобки.  Приведенные выше  правила

        применимы также и к комбинации выражений,

        сгруппированных с помощью скобок.

Сочетания сложных выражений

        Для создания весьма сложных полностью оптимизируемых,

        частично оптимизируемых или не оптимизируемых выражений

        можно комбинировать сложные выражения так, как это

        показано в таблице ниже. В свою очередь, эти весьма

        сложные выражения можно комбинировать для создания еще

        более сложных, которые могут оказаться   оптимизируемыми

        полностью,   частично   или быть неоптимизируемыми.

        Результаты  комбинирования  сложных выражений приведены

        в   таблице  ниже.   Эти  правила применимы и для

        комбинирования с группированием с помощью скобок.

        Сочетания сложных выражений

-----------------------------------

Выражение       Полностью оптимизируемое

Оператор        AND

Выражение       Полностью оптимизируемое

Результат       Полностью оптимизируемое

Выражение       Полностью оптимизируемое

Оператор        OR

Выражение       Полностью оптимизируемое

Результат       Полностью оптимизируемое

Выражение       Полностью оптимизируемое

Оператор        AND

Выражение       Частично оптимизируемое

Результат       Частично оптимизируемое

Выражение       Полностью оптимизируемое

Оператор        OR

Выражение       Частично оптимизируемое

Результат       Частично оптимизируемое

Выражение       Полностью оптимизируемое

Оператор        AND

Выражение       Hе оптимизируемое

Результат       Частично оптимизируемое

Выражение       Полностью оптимизируемое

Оператор        OR

Выражение       Hе оптимизируемое

Результат       Hе оптимизируемое

Выражение       --

Оператор        NOT

Выражение       Полностью оптимизируемое

Результат       Полностью оптимизируемое

Выражение       Частично оптимизируемое

Оператор        AND

Выражение       Частично оптимизируемое

Результат       Частично оптимизируемое

Выражение       Частично оптимизируемое

Оператор        OR

Выражение       Частично оптимизируемое

Результат       Частично оптимизируемое

Выражение       Частично оптимизируемое

Оператор        AND

Выражение       Hе оптимизируемое

Результат       Частично оптимизируемое

Выражение       Частично оптимизируемое

Оператор        OR

Выражение       Hе оптимизируемое

Результат       Hе оптимизируемое

Выражение       --

Оператор        NOT

Выражение       Частично оптимизируемое

Результат       Частично оптимизируемое

Выражение       Hе оптимизируемое

Оператор        AND

Выражение       Hе оптимизируемое

Результат       Hе оптимизируемое

Выражение       Hе оптимизируемое

Оператор        OR

Выражение       Hе оптимизируемое

Результат       Hе оптимизируемое

Выражение       --

Оператор        NOT

Выражение       Hе оптимизируемое

Результат       Hе оптимизируемое

        Таблица, приведенная  ниже,  показывает  как  могут быть

        скомбинированы сложные выражения и насколько

        оптимизируем при этом результат.

 Примеры сочетаний сложных  выражений

-----------------------------------

Пример          (FIRSTNAME = 'FRED' AND HIREDATE <{12/30/89})

                OR (LASTNAME = '' AND HIREDATE > {12/30/88})

Тип сочетания   Полностью оптимиз. OR Полностью оптимиз.

Оператор        =

Результат       Полностью оптимизируемое

-----------------------------------

Пример          (FIRSTNAME = 'FRED' AND HIREDATE < {12/30/89})

                AND 'S' $ LASTNAME

Тип сочетания   Полностью оптимизируемое AND Hе оптимизируемое

Оператор        =

Результат       Частично оптимизируемое

-----------------------------------

Пример          (FIRSTNAME = 'FRED' AND 'S' $ LASTNAME)

                OR (FIRSTNAME = 'DAVE' AND 'T' $ LASTNAME)

Тип сочетания   Частично оптимизируемое OR Частично оптимизируемое

Оператор        =

Результат       Частично оптимизируемое

-----------------------------------

Пример          ('FRED' $ FIRSTNAME OR 'S' $ LASTNAME)

                OR ('MAIN' $ STREET OR 'AVE' $ STREET)

Тип сочетания   Hе оптимизируемое OR Hе оптимизируемое

Оператор        =

Результат       Hе оптимизируемое

-----------------------------------

Случаи, когда Rushmore не применимо

        В  редких случаях  механизм  Rushmore  может   оказаться

        неприменимым в операциях расширенного поиска данных.

        При этом процесс поиска выполняется  так, как это

        делается  в более ранних версиях FoxPro.

          Rushmore не выполняется  в   каждом  случае,  когда

        в потенциально оптимизируемой  команде нельзя

        оптимизировать фразу FOR.  Понятия  о  создании

        оптимизируемых  фраз  FOR приведены в предыдущих

        пунктах.

          Rushmore также не выполняется, если в потенциально

        оптимизируемую команду включена фраза WHILE.

          В стандартной версии FoxPro сама система FoxPro может

        отключать  Rushmore  когда  общее  количество  записей

        во   всех открытых  базах  данных  превышает  500 000.

        Однако расширенная версия  (FoxPro   Extended)  может

        использовать  Rushmore для увеличения производительности

        при работе с базами данных с  общим числом записей более

        чем  500 000  так, как  это делается  и при работе с

        базами данных, когда число записей меньше 500 000.

          В ситуации дефицита оперативной памяти Rushmore не

        может  оптимизировать поиск данных. о поиск данных

        выполняется как в более ранних версиях.

Отключение Rushmore

        В редких случаях может возникнуть потребность отключения

        Rushmore.  При  выдаче  команды,  которая  использует

        Rushmore, средства Rushmore    немедленно   определяют

        какая запись соответствует выражению во  фразе FOR. С

        этими записями затем  и осуществляются манипуляции в

        команде.

          Если потенциально оптимизируемая команда модифицирует

        индексный ключ, используемый во фразе FOR, подмножество

        записей, захваченных механизмом Rushmore, оказывается

        вне пределов запланированной  обработки. В случаях,

        подобных описанному, механизм Rushmore можно отключить

        для гарантии полноты  обработки действительно текущих

        записей.

          Для отключения Rushmore для отдельной команды в нее

        включается ключевое слово NOOPTIMIZE.  Для глобального

        отключения (или включения) механизма Rushmore для всех

        команд, использующих преимущества Rushmore, используется

        команда SET OPTIMIZE. Вариант SET OPTIMIZE OFF отключает

        Rushmore, а вариант SET OPTIMIZE ON  - включает.  По

        умолчанию предполагается ON.


Связи между БД

  В FoxPro допускается работа сразу со многими базами данных и

при этом возможно установление разнообразныхх связей между ними.

Указатели записей в таких связанных базах будут двигаться

синхронно. База, в которой указатель движется произвольно,

считается старшей, а база/базы, в которой указатель следует за

указателем старшей базы, - младшей. В старшей и младшихх базах

должны быть поля, несущие какой-то общий признак, иначе хотя

связь и возможна, она будет бессмысленна. Допускается сцепление

одной базы с несколькими другими. Младшие базы, в свою очередь,

могут быть связаны с базами следующего уровня и т.д.

  Возможно установление двух типов связей между записями двух

сцепленных баз данных. Связь типа одна_запись-к-одной перемещает

указатель в младшей базе таким образом, что он всегда

устанавливается на первую встреченную им запись с совпадающим

признаком. Остальные такие записи (если они есть) остаются

"незамеченными". Эта связь устанавливается просто командой SET

RELATION. Связь типа одна_запись-ко-многим позволяет обратиться

ко всем записям младшей базы с совпадающим признаком (команды

SET RELATION и SET SKIP TO).

  Оба типа связей могут быть распространены на несколько баз

сразу.

SET RELATION TO [<выражение> INTO

         <псевдоним>] [ADDITIVE]

SET RELATION TO [<выр1> INTO

         <псевдоним>] [ADDITIVE]

[,<выр2> INTO <псевдоним>]

[ADDITIVE]...

  Связывает два открытых файла базы данных  по вычисленному

значению   <выражения>   через ключевое поле или по номеру

записи.

  Команда SET RELATION связывает активный файл базы данных с

открытым файлом базы данных  из другой   рабочей   области.

Второй файл идентифицируется фразой INTO <псевдоним>.

  При перемещении указателя записи  в активном файле,

указатель в связанном файле перемещается на соответствующую

связанную запись. Если в связанном файле такая запись не

найдена,  указатель  устанавливается   на конец файла.

  Связь  файлов  может  быть  установлена двумя способами:

  Если файл, имя которого задано <псевдонимом> проиндексирован,

тогда <выражение> может включать константы и значения полей

активного файла для поиска соответствующего значения ключа

индекса второго файла.  Индекс должен быть построен по

<выражению> символьного или числового типа или типа даты; иначе

возникает ошибка.

  Если файл, имя которого задано <псевдонимом> не индексирован,

тогда <выражение> должно быть числовым.  Выражение вычисляется

для текущей записи активного файла и результат используется в

качестве номера записи второго файла.

  Можно построить и множественную связь, используя вторую форму

команды. При этом текущий выбранный файл связывается с

несколькими файлами в других рабочих областях.  Выражения связи

в команде разделяются при этом запятыми.

  В команде SET RELATION  TO... можно задать ключевое слово

ADDITIVE, которое обеспечивает сохранение ранее установленных

связей файлов.

  Команда без аргументов отменяет все связи, установленные из

текущей рабочей области.

SET RELATION OFF INTO <вырN> | <вырC> -  Отмена связи между

                                         двумя базами данных.

                                         Должен быть выполнен

                                         переход в рабочую

                                         область главной

                                         (perent) базы.

                                         Связанная с ней база

                                         данных указывается

                                         номером рабочей области

                                         <вырN> или алиасом

                                         <вырС>.

SET SKIP TO [<псевдоним1> [, <псевдоним2>] ... ] - Создает между

                                                  базами данных

                                                 отношения типа

                                               "один-ко-многим".

  Отношение "один-ко-многим" устанавливается наподобие отношения

"один-к-одному".  Прежде всего отношения между исходной и

порожденными базами данных устанавливается посредством SET

RELATION.  Затем выдается команда SET SKIP, что формирует

отношение "один-ко-многим".

[<псевдоним1> [, <псевдоним2>] ... ]

     Если исходная база данных связывается снесколькими

     порожденными,  то  необходимо  включить  список

     псевдонимов, разделенных запятыми (<псевдоним1>,

     <псевдоним2>, ... ), для формирования отношения

     "один-ко-многим".

     В командах, поддерживающих специфицирование сферы действия

     (DISPLAY,  LIST, и  т.д.), записи  в исходной  базе данных

     будут повторяться для каждой соответствующей записи в

     порожденной базе данных.

     Для удаления отношения типа "один-ко-многим" из текущей

     выбранной рабочей области используется команда SET SKIP TO

     без аргументов.  При этом составляющие его отношения типа

     "один-к-одному" продолжают еще существовать и могут быть

     удалены командой SET RELATION TO.

  Функция RELATION(<вырN>[,<псевдоним>]) возвращает выражение,

использованное для установления <вырN>-ого по счету отношения из

рабочей  области, определенной псевдонимом.

  Если необязательный второй аргумент (псевдоним) не указан,

подразумевается текущая рабочая область. Если специфицированного

отношения нет, возвращается пустая строка.

  Другая функция - TARGET(<вырN>[,<выр>]) - возвращает псевдоним

рабочей области,  на  которую  ссылается <вырN>-ое по счету

отношение,  установленное  из  рабочей области, определяемой

необязательным аргументом  <выр>.


Создание командных файлов в FoxPro
Создание командных файлов

  К настоящему моменту нами были рассмотрены некоторые основные

команды языка FoxPro. Их практическое освоение может дать Вам

возможность почувствовать власть над данными, однако для

построения полноценных систем обработки данных этого, конечно,

недостаточно. Очевидно,что пока не хватает средств управления

вычислительным процессом, организации диалога, структуирования

программ, не обсуждался вопрос о временных перменных и массивах

и т.д.

  Дальнейший материал посвящен изученю команд, ориентированных

на прямое прграммирование процессов обработки данных. Такие

программы создаются с помощью внутренннего встроенного

текстового редактора FoxPro, вызываемого командой

MODIFY COMMAND <file>

        | MODIFY FILE [<file>]

        [NOEDIT]

        [NOWAIT]

        [RANGE <expN1>, <expN2>]

        [[WINDOW <window name1>]

        [IN [WINDOW] <window name2>

        | SCREEN]]

        [SAVE]

  Команда вызывает встроенный текстовый редактор системы

FoxPro, который может использоваться для создания и модификации

файлов.

  Если используется опция COMMAND, по умолчанию предполагается

расширение   имени   <файла> (.PRG).

  В форме команды MODIFY FILE не предполагается расширение имени

<файла> (.PRG).  Команды MODIFY COMMAND и MODIFY FILE

поддерживают шаблоны, позволяющие открыть окна редактирования

для всех файлов, удовлетворяющих указанному шаблону.

  По завершении редактирования файла, он помещается на диск.

Копия предыдущего состояния файла с расширением .BAK при этом

создается в том случае, если из диалогового окна, выводимого при

выборе опции Prefrences (предыдущие) из меню Edit

(редактирование), выбран индикатор Make backup  (создание

предыдущей версии).

Фразы

-----

MODIFY COMMAND <файл>

        Редактирование программного файла.

MODIFY FILE <файл>

        Редактирование текстового файла.

NOEDIT

        Опция запрещает модификацию текста или информации в окне

        редактирования, т.е. текст может только просматриваться.

NOWAIT

        При использовании MODIFY COMMAND или MODIFY FILE внутри

        программы, после открытия окна редактирования выполнение

        программы приостанавливается до закрытия окна.  Опция

        NOWAIT позволяет продолжать выполнение программы после

        открытия окна редактирования, т.е. программа не ждет

        закрытия окна , а продолжает выполнение с команды,

        следующей за MODIFY COMMAND/FILE.

RANGE <вырN1>, <вырN2>

        The RANGE позволяет открыть окно редактирования, в

        котором указанный диапазон символов уже будет выбран

        (выделен).  Выделяются символы с позиции <вырN1> и до

        позиции <вырN2> (не включая ее).  Если <вырN1>=<вырN2>

        символв не выбираются и курсор остается в позиции

        <вырN1>.

WINDOW <имя окна1>

        Можно указать окно, которое редактор FoxPro будет

        использовать для редактирования.

IN [WINDOW] <имя окна2>

        Если используется фраза IN WINDOW <имя окна>, редактор

        откроет окно редактирования внутри родительского окна.

          Родительское окно должно быть предварительно описано с

        помощью DEFINE WINDOW.

IN SCREEN

        Можно указать местоположение окна редактирования на

        экране с помощью фраз IN WINDOW SCREEN или IN SCREEN.

SAVE

        Опция позволяет сохранить текст на экране после выхода

        из окна редактирования. Обычно после окна редактирования

        после выхода из них удаляются с экрана.  Опция доступна

        только внутри программы (не работает при выполнении

        команды в окне Command).

  Созданная в редакторе программа запоминается в указанном файле

и может быть в дальнейшем вызвана на исполнение командой

DO <файл> [WITH <список парам>] [IN <файл>]

  Команда DO выполняет программу. Программный файл может

содержать дополнительные команды DO.  Глубина вложения команд DO

ограничивается 32.

Выполнение программы завершается в следующих случаях:

         выполнена команда RETURN

         выполнена команда CANCEL

         выполнена команда QUIT

         достигнут конец файла

         выпоняется другая команда DO

  Для вызова по команде DO специальных программ меню, экранов

или запросов необходимо  использовать  расширение имени

программного файла (.MPR, .SPR and .QPR, соответственно).

Фразы

-------

IN <файл>

  Выполняет процедуру из заданного программного файла <файл>.

Если расширение не  используется FoxPro  ищет и  выполняет

программы в следующем порядке:

.EXE - Выполнимый файл.

.APP - Приложение.

.FXP - Откомпилированный файл.

.PRG - Программный файл.

  SET ESCAPE OFF блокирует использование Escape для прерывания

выполнения программы.  SET ESCAPE ON позволяет прервать

выполнение по нажатию Escape.

  Если SET ESCAPE OFF, подпрограмма, вызываемая по ON ESCAPE не

будет выполняться.

  Команда COMPILE <файл> предназначена для компиляции  файла с

исходным текстом программы  для создания соответствующего

объектного файла.

  При компиляции выявляются все  синтаксические ошибки  в

исходном тексте программы. Средства FoxPro только загружают и

выполняют объектные файлы, поэтому перед запуском программы

должны быть откомпилированы. Если программа  еще не была

откомпилирована, то при попытке запуска компиляция выполняется

автоматически.

  Как исходные тексты программ, так и файлы форматов, могут быть

откомпилированы командой COMPILE. Файлы исходных текстов

программ имеют расширение имени .PRG , а откомпилированные

объектные файлы имеют то  же имя,  но с расширением .FPX. Файлы

формата имеют расширение имени .FMT,  а соответствующие  им

откомпилированные файлы - .PRX .

  Можно компилировать файлы  программ   с  другим   расширением,

откомпилированному файлу  при  этом  будет  присвоено расширение

.FPX. Hапример,  при  создании  программы  средствами внешнего

текстового редактора последний может присвоить файлу  расширение

.TXT . Этот файл можно компилировать без переименования, хотя

его расширение не .PRG .

  С помощью опции Compile...  (Компилировать...) из

вертикального меню Program  (Программа) можно  откомпилировать

набор программ, можно  также  защитить   откомпилированный

объектный файл   для предотвращения изменений исходной

программы.

  Если  установлено   SET  LOGERRORS  ON,  то   диагностика

ошибок компиляции записывается в текстовый файл  с тем же

именем, что  и у исходной программы,  но с расширением  .ERR .

Если  установлено SET LOGERRORS OFF, этот файл не создается.

  Компилирует FoxPro, можно сказать, понарошку. Результатом

компиляции является некоторый пи-код, удобный для интерпритации.

  Для ориентирования в текстах программ необходимы комментарии.

Отдельная строка комментариев должна начинаться со звездочки

(*). Комментарии могут быть внесены и в строку, где уже есть

команда. Для этого они предваряются двумя знаками &&.

  Выделение данных        - Shift + курсорные клавиши,

  копирование в буфер

      без удаления        - Ctrl+C,

      с удалением         - Ctrl+X,

  копирование из буфера   - Ctrl+V

  Теперь поговорим немного о постановке задачи, ради которой Вы,

собственно, и изучали весь предыдущий материал - составление

расписания.

  Как показывает практика, в СУБД самое важное - это продуманные

структуры данных, поэтому проектирование системы для

обслуживания расписания мы начнем с составления структур баз

данных. Следующим этапом будет заполнение этих баз, и затем -

самая нужная часть, ради которой делается львиная доля работы:

обслуживание базы данных, под которым мы будем понимать

удовлетворение различных запросов к ней.

  Вспомним, как выглядит обычное расписание. На большом листе

чертится или отпечатывется большое количество клеточек. Строки в

расписании соответствуют номеру пары в какой-то из дней недели,

столбцы определяют соответственно потоки, группы и подгруппы. А

в клеточке мы видим название предмета, имя преподавателя и номер

аудитории. Таким образом, для того, чтобы однозначно определить

эту клеточку на стене в базе данных, нам необходимо пять полей:

группа, пара, предмет, аудитория и преподаватель.

  Итак, в базе расписания мы введем 5 числовых полей, в которые

будем заносить коды групп, пар, предметов, аудиторий и

преподавателей.

  Код группы мы будем формировать следующим образом :

  код_группы=код_потока*100+номер_группы*10+номер_подгруппы.

При этом предполагается, что групп на потоке не может быть

больше 9 (в противном случае код теряет смысл или, что еще

хуже, приобретает неверный), а код потока формируется в

отдельной базе потоков.

  Чем удобен именно такой код? Легкостью в поиске - если номер

группы в коде равен нулю, то эта пара соответствует лекции, если

номер группы ненулевой, а номер подгруппы нулевой, то это

практичка, иначе - лабораторка. Следовательно, общий код мы

никогда не будем делать нулевым, и 0 в коде рассматривать как

ошибку.

  Обозначив числитель через 1, знаменатель - через 2 и

пронумеровав дни недели от 1 до 5, мы получим удобный код пары:

  код=номер_недели*100+номер_дня*10+номер_пары

  Коды предметов, аудиторий и преподавателей мы будем

формировать в соответствующих базах.

  База данных по предмету будет, естественно, содержать код и

название предмета; кроме того, для удобства возложим на нашу

программу обязанность старост - подсчитывать запланированное и

реальное число часов, в неделю и общее. Дополнительно введем

признак связи предмета с аудиторией, необходимый для того,

чтобы, скажем, лабораторные работы по химии проводились в хим.

лаборатории, а не в спортзале.

  Аудиторию мы будем характеризовать номером корпуса, номером

аудитории, наличием какой-либо специализации (например,

компьютерный класс) и емкостью - поток, группа, подгруппа.

Некоторые аудитории настолько специфичны, что этих данных для

кодирования недостаточно; не каждый догадается, что аудитория

номер 1 в шестом корпусе - это мастерская ОТД, поэтому введем

необязательное (то есть memo) поле, в котором и прокомментируем

эти особенности.

  Поток традиционно характеризуется специальностью - кодом,

номером курса и количеством групп. У потоков филфака есть

интересная особенность: часть групп занимается с часу, часть - с

7.30 и т.д., причем те, кто занимаются с часу, считают свою пару

первой, хотя для нас она - третья. Для таких вот экзотических

специальностей введем особое поле смещения, показывающее, на

сколько пар они отстали от жизни.

  Каждый преподаватель имеет имя, звание, приписан к какой-то

кафедре; кроме этого, в институте он занимает какую-то

должность, что тоже надо учитывать при составлении расписания

(когда у ректора в последний раз была четвертая пара?).

  Факультет, специальность и кафедру можно охарактеризовать

кодом и именем.

  Это - основные структуры данных. А процедуры мы к ним добавим

в следующих разделах.


Set - команды в FoxPro
Set - команды

  В начале каждой программы обычно требуется выполнить некоторые

установки, чтобы Ваша программа работала так, как хотите Вы, а

не так, как это обычно делается FoxPro. Рассмотрим некоторые из

них:

       SET ALTERNATE

SET ALTERNATE ON | OFF

SET ALTERNATE TO [<файл> [ADDITIVE]]

  Hаправление вывода, ведущегося на экран или в

окно, и в текстовый файл.

  Команда SET ALTERNATE ON | OFF  используется для включения и

отмены записи всего экранного вывода  в   файл  журнализации,

создаваемый командой SET  ALTERNATE TO  <файл>.  Опция  ON

включает запись, а OFF отменяет ее.

  Команда SET  ALTERNATE  TO  <файл> создает и открывает этот

текстовый  файл.  Если  файл с заданным именем уже существует,

он замещается новым.  Если не определено иначе, имя <файла>

имеет расширение .TXT по умолчанию.

  Если в команду включена  фраза ADDITIVE,  то вывод

дописывается в  конец   существующего файла журнализации.

  Команда SET   ALTERNATE   TO  без  указания имени файла

закрывает текущий файл ALTERNATE.

  По  умолчанию SET  ALTERNATE  имеет значение  OFF.

         SET BELL

SET BELL ON | OFF   SET BELL TO [<частота>, <длительность>]

  Определяет включение предупреждающего звукового сигнала при

вводе неверного значения  поля  или   при  достижении   конца

области ввода. Устанавливает параметры  этого сигнала.

  Команда SET  BELL   позволяет  включить или выключить подачу

звукового сигнала в процессе редактирования значений полей, если

достигнут конец поля  или введены  неправильные данные. SET BELL

ON включает сигнал, тогда как SET BELL OFF выключает его.

Имеется   также возможность установить частоту (высоту  тона) и

длительность  этого  сигнала  командой SET BELL  TO.  Частоту

можно  задавать  от 19 до 10000 герц, а длительность от 1 до 19.

  По умолчанию используется установка SET BELL ON с частотой

512 герц и  продолжительностью 2.

          SET BLINK

 SET BLINK ON | OFF

  Установление  возможности мерцания  элементов экрана.

  Команда SET  BLINK работоспособна только при работе с

адаптерами EGA и VGA. Она игнорируется другими адаптерами.

  Если установлено  SET  BLINK  ON,   элементы экрана (границы,

тени, текст  и т.д.)  можно ввести в режим мерцания. Если

установлено SET BLINK OFF, появляется возможность  увеличения

яркости фона и удвоения количества  доступных цветов.

  По умолчанию принимается SET BLINK ON.

         SET BORDER

SET BORDER TO [SINGLE | DOUBLE | PANEL | NONE

   | <строка границы1> [,<строка границы2>]]

  Определение вида ограничительной  рамки для прямоугольных

рамок,    горизонтальных    и вертикальных меню, окон.

  Команда SET BORDER позволяет определить границы прямоугольных

рамок, горизонтальных и вертикальных  меню  и  окон. Рамки

создаются командой   @...TO,   горизонтальные   меню  - командой

DEFINE  MENU,  вертикальные  меню - командой  DEFINE  POPUP и

окна  -  командой DEFINE WIDOW.

  Граница может быть определена одной из  опций команды SET

BORDER TO. По умолчанию  граница образуется одиночной  линией

(SINGLE).  Опция DOUBLE  определяет  границу  в  виде  двойной

линии, а опция PANEL - в виде широкой  линии. Опция NONE

подавляет вывод границы.

  Можно определить и собственные символы для построения границ с

помощью опций <строка границы1>   и   <строка   границы2>.

Первая последовательность символов используется  для построения

границ  меню  и  рамок. Она также используется для активных

окон.

  Hеобязательная вторая    последовательность символов

используется  для  неактивных окон. Она не влияет на рамки и

меню.

  Строка  определения  границы  -  это  последовательность

символов ASCII в виде:

  [<1>][,<2>][,<3>][,<4>][,<5>][,<6>][,<7>][,<8>]]]]]]]]

  Определение может содержать от одного до восьми символов,

соответственно определяющих:

     1 - верхняя сторона;

     2 - нижняя сторона;

     3 - левая сторона;

     4 - правая сторона;

     5 - верхний левый угол;

     6 - верхний правый угол;

     7 - нижний левый угол;

     8 - нижний правый угол.

  Если в определении  задать только  один   символ,  он   будет

использован  для  построения   всех  восьми  элементов

границы. Hапример, чтобы определить границу из

ромбиков,необходимо задать SET BORDER TO CHR(4).

  Если изменение  определения  границы  выполнено  после того,

как определены   рамки, меню и окна,   то   последние  сохранят

первоначальный  вид  границ. Для назначения  им  новых   границ

придется их переопределить вновь.

        SET CARRY

  SET CARRY ON | OFF

  SET CARRY TO [<список полей> [ADDITIVE]]

  Определяет  необходимость копирования данных из  предыдущей

записи  в  новую  запись  при использовании команд APPEND или

INSERT.

  Эта команда включает    или   выключает возможность переноса

данных из текущей записи в новую  запись, создаваемую  командой

APPEND или  INSERT.  Поля, значения  которых   нет необходимости

менять в процессе данного сеанса работы, могут переносится во

все новые записи.  Hапример,  поле,  содержащее текущую дату,

может  переноситься  во  все записи, создаваемые в этот  день,

чтобы не  повторять этот ввод раз за разом.

  SET CARRY  ON  переносит  в новые записи все поля текущей

записи.  SET CARRY OFF  отменяет перенос значений полей. SET

CARRY TO <список полей> позволяет специфицировать только

определенные поля для переноса значений. Если в команде

присутствует опция ADDITIVE, то к предыдущему списку полей

добавляются новые поля. Использование команды SET CARRY TO без

аргументов позволяет восстановить принимаемый по умолчанию

перенос всех полей.

  По умолчанию принимается состояние SET CARRY OFF.

         SET CENTURY

SET CENTURY ON | OFF

  Позволяет  определить  формат  вывода  года в датах.

  Если значение параметра SET CENTURY равно ON, год  при  выводе

даты  отображается четырьмя цифрами. Если значение параметра SET

CENTURY равно  OFF, год отображается двумя цифрами. При этом

подразумевается год 20-го века.

  При значении параметра SET CENTURY равном ON, год

отображается четырьмя  цифрами  и  дата выводится длиной 10

символов, а при значении OFF - длиной 8 символов.

  По   умолчанию   принимается   установка  SET CENTURY OFF.

          SET CLOCK

SET CLOCK ON | OFF

SET CLOCK TO [<ряд>,<кол>]

  Определяет необходимость вывода  на экран текущего времени и

позицию этих часов.

  Эта команда позволяет вывести на экран текущее время и

специфицировать позицию вывода времени. Для вывода времени в

правом верхнем углу экрана используется команда SET CLOCK ON.

Команда SET  CLOCK OFF подавляет вывод времени. Изменить

положение этих  часов можно  командой  SET  CLOCK  TO  с

указанием требуемых координат. Команда SET CLOCK TO без

аргументов восстанавливает принятую    по умолчанию  позицию  в

правом  верхнем   углу экрана.

  По   умолчанию   принято SET   CLOCK   OFF. Координаты SET

CLOCK TO по умолчанию (0,69).

       SET CONFIRM

 SET CONFIRM ON | OFF

  Используется при полноэкранном редактировании для определения

перехода курсора на следующее поле  ввода  данных  либо

автоматически, при заполнении  текущего  поля,  либо  по нажатию

ключа <ВВОД>.

  Если значение  параметра  SET  CONFIRM равно OFF, то  при

достижении  конца текущего  поля ввода данных, курсор

автоматически  переходит на  следующее  поле.  Если значение

параметра SET BELL  равно ON,  воспроизводится звуковой сигнал.

Такой режим подходит для заполнения полей,  содержащих  данные

одинаковой  длины (коды, даты, номера телефонов и т. п.).

  Если значение параметра SET CONFIRM равно ON, то курсор

остается в последней   позиции текущего   поля   до   явного

подтверждения завершения  ввода  нажатием  соответствующего

ключа (ВВОД, Tab и  др.). Такой  режим больше подходит при вводе

значений переменной длины, поскольку позволяет перед

подтверждением выполнить проверку корректности введенных данных.

  По умолчанию принято SET CONFIRM OFF.

         SET CONSOLE

 SET CONSOLE  ON | OFF

  Команда включает или подавляет вывод на экран или в окно.

Используется только из  программ. Если  установлено  SET

CONSOLE  ON, то вывод направляется на экран или в окно вывода.

  Если установлено SET CONSOLE OFF, вывод на экран подавляется.

  Команды @...SAY...GET сохраняют свое действие независимо от

значения параметра SET CONSOLE, поскольку управляются значением

параметра DEVICE.

  По умолчанию принято SET CONSOLE ON.

           SET CURSOR

SET CURSOR ON | OFF

  Включает / отключает курсор. Значение по умолчанию - ON

  Курсор может отключаться (off) командой SET CURSOR OFF и

SYS(2002) только в следующих трех случаях - при ожидающих GET

или INKEY() или  в течение WAIT.

           SET DATE

 SET DATE [TO]

    AMERICAN | ANSI | BRITISH | FRENCH | GERMAN | ITALIAN |

    JAPAN | USA | MDY | DMY | YMD

+---------------------------+

¦     Тип даты   ¦  Формат  ¦

+----------------+----------¦

¦ AMERICAN       ¦ ММ/ДД/ГГ ¦

¦ ANSI           ¦ ГГ.ММ.ДД ¦

¦ BRITISH/FRENCH ¦ ДД/ММ/ГГ ¦

¦ GERMAN         ¦ ДД.ММ.ГГ ¦

¦ ITALIAN        ¦ ДД-ММ-ГГ ¦

¦ JAPAN          ¦ ГГ/ММ/ДД ¦

¦ USA            ¦ ММ-ДД-ГГ ¦

¦ MDY            ¦ ММ/ДД/ГГ ¦

¦ DMY            ¦ ДД/ММ/ГГ ¦

¦ YMD            ¦ ГГ/ММ/ДД ¦

+---------------------------+

По умолчанию действует формат AMERICAN.

       SET DECIMALS

 SET DECIMALS TO <вырN>

  Устанавливает  количество  десятичных  знаков после запятой

при выдаче результатов числовых функций и вычислений.

  По умолчанию  дробная  часть  содержит   два знака.

Максимальное значение - восемь знаков, минимальное - ноль.

  Команда действует при выполнении операций деления, умножения,

математических, тригонометрических и финансовых функций.

         SET DEFAULT

 SET DEFAULT TO [<вырС>]

  Определяет  заданный диск и/или  директорий в качестве

используемых по умолчанию.

  По этой команде выполняется команда DOS CD (смена

директория), и  весь  обмен с диском выполняется в соответствии

с устройством и/или  директорием,  определенным в <вырС>. Можно

специфицировать   только    дисковод, дисковод и маршрут  к

директорию, только  имя порожденного директория,  а также

сокращения (\ и ..) принятые в DOS.

  При выходе из FoxPro текущим будет дисковод, который был

таковым при запуске системы, но текущим будет последний

директорий, указанный в <вырС> устройства по умолчанию.

Устройством же по умолчанию будет то, с которого стартовал

FoxPro.

  ЗАМЕЧАHИЕ. Система FoxPro сохраняет командные файлы в памяти

для повторного выполнения. Поэтому хранящийся в памяти командный

файл будет выполнен независимо от переназначения активного по

умолчанию устройства. Для удаления командного файла из памяти

необходимо  использовать команду CLEAR PROGRAM. После этого

устройство и дисковод, принимаеиые по умолчанию, становятся

действительными и для командных файлов.

         SET DEVICE

SET DEVICE TO SCREEN | PRINTER | FILE <файл>

  Задает устройство вывода результатов выполнения команд

@...SAY (экран,  окно, файл или принтер).

  Данная команда управляет выводом, порождаемым командами

@...SAY.  Вывод может быть направлен на экран, на принтер  или в

файл с указанным именем.   Вывод   от   команд   @...GET    не

переадресуется описываемой командой.

  Если указана команда SET DEVICE TO  PRINTER, вывод результатов

команд @...SAY выполняется на  принтер. Если  команда  @...SAY

задает координаты, которые меньше значений, заданных предыдущей

командой @...SAY,   выполняется переход на новую страницу.

  Когда указано SET DEVICE TO FILE  <файл>, то вывод

направляется в специфицированный файл.

           SET DISPLAY

SET DISPLAY TO  CGA | COLOR | EGA25

        | EGA43 | MONO | VGA25 | VGA50

  Переключение режимов отображения. Значение по умолчанию -

начальная установка.

CGA     Переключение в режим CGA.

COLOR   Переключение в цветной режим отображения.

EGA25   Переключение в режим EGA  с 25 строками.

EGA43   Переключение в режим EGA с 43 строками.

MONO    Переключение в монохромный режим.

VGA25   Переключение в режим VGA с 25 строками.

VGA50   Переключение в режим VGA с 50 строками.

  Если опция не поддерживается, выдается предупреждающее

сообщение "Display mode not available" (Режим отображения не

доступен).

          SET EXACT

SET EXACT ON | OFF

  Определяет необходимость одинаковой длины символьных строк при

их сравнении.

  Если значение параметра SET EXACT равно  OFF, сравнение двух

символьных строк  выполняется, даже  если  они  имеют  различную

длину. Оно начинается  с  самого  левого  символа каждой строки

и  продолжается  посимвольно  пока не закончится более короткая

из двух строк. Если к этому  моменту нет  расхождений, то

строки считаются равными.

  Если значение параметра SET EXACT равно  ON, сравнение  строк

выполняется  аналогично, за исключением  необходимости

одинаковой  длины строк.

  По умолчанию принято SET EXACT OFF.

         SET FIELDS

 SET FIELDS ON | OFF

 SET FIELDS TO [<поле1>[,<поле2>...]]

 SET FIELDS TO ALL

  Эти команды определяют, какие поля могут быть доступны в

текущей используемой базе  данных. Эта спецификация   полей

активна,   когда установлено SET FIELDS ON, хотя использование

SET FIELDS TO со списком полей  автоматически предполагает SET

FIELDS ON.

  Если значение параметра SET FIELDS равно OFF, доступны все

поля активной базы данных.

  Формат SET  FIELDS  TO  <поле1>,<поле2>, ... <полеN>

специфицирует, какие поля доступны  в текущей  базе  данных.

Именам  полей  может предшествовать псевдоним в двух случаях:

- при ссылке на поля в базе данных,  открытой  не в текущей

  выбранной рабочей области;

- при  одинаковых  именах  полей  в связанных  файлах.

  Последовательное использование нескольких команд SET FIELDS TO

со списком полей наращивает список доступных полей, не отменяя

ранее специфицированных.  Вариант SET  FIELDS ALL  делает

доступными   все  поля   текущей выбранной базы данных.

  Список полей очищается (из него удаляются все поля) при

выдаче команды  SET FIELDS  TO без аргументов или в варианте с

опцией ALL.

  По умолчанию принято SET FIELDS OFF.

         SET FIXED

SET FIXED ON | OFF

  Определяет выдачу фиксированного количества десятичных знаков

в дробной части чисел.

  Если значение параметра SET FIXED равно  OFF, количество

десятичных   знаков при выдаче результатов  вычислений зависит

от точности элементов,  участвующих  в  вычислениях. Поля базы

данных визуализируются с количеством десятичных знаков,

объявленных при определении полей базы данных.

  Если значение параметра SET FIXED равно ON, все числа выдаются

с количеством десятичных знаков, определенным  командой SET

DECIMALS. По  умолчанию  количество  десятичных  знаков всегда

два.

        SET FULLPATH

SET FULLPATH ON | OFF

  Позволяет выводить или не выводить с именами файлов маршрут

доступа.

  Если установлено SET FULLPATH ON, то функции DBF() и NDX()

возвращают дисковод, полный маршрут и имя файла. Если же

установлено SET FULLPATH OFF, то возвращается только дисковод и

имя файла.

  По умолчанию установлено SET FULLPATH ON.

       SET FUNCTION

SET FUNCTION <вырN> | <вырС> | <метка клавиши> TO <выр>

  Обеспечивает перепрограммирование функциональных клавиш.

  Эта команда позволяет создавать макросы клавиатуры и назначать

их функциональным клавишам. Серия нажатий клавиш специфицируется

в выражении,  включаемом  в команде. Точка  с запятой  (;),

включенная  в выражение, присвоенное функциональному ключу,

генерирует возврат каретки.

  Специфицирование функциональной клавиши, которой   назначается

макрос,   может  быть выполнено  посредством  <вырN>,  <вырС>

или <метки   клавиши>.   Hомер     функциональной клавиши

используется  в <вырN>.  Hапример, SET FUNCTION  2  TO  ...

обозначает клавишу F2. Функция   FKMAX()   возвращает

максимальное количество   программируемых   функциональных

клавиш.

  Функциональная клавиша может быть специфицирована  и  по  ее

имени  с  помощью <вырС>. Hапример, клавишу F4 специфицирует

команда  SET  FUNCTION  F4  TO ... .  Функция FKLABEL()

возвращает   имя  функциональной клавиши.

  Кроме того, можно специфицировать комбинацию функциональных

клавиш с клавишами Ctrl или Shift. Для этого используются <метки

клавиш>.

  Хотя функциональную клавишу F1 и нельзя переназначить,  она

может  быть назначена в качестве "горячей клавиши" посредством

обхода ее нормальной   функции.

            SET HELP

SET HELP ON | OFF

SET HELP TO [<файл>]

  Отключает/подключает средство диалоговой помощи или назначает

другой файл помощи. Значение по умолчанию - ON. Значение по

умолчанию - FOXHELP.

  SET HELP ON или OFF подключает или диалоговую помощь FoxPro.

Если SET HELP OFF, диалоговая помощь недоступна.

TO <файл>

        С помощью команды with SET HELP TO <файл> можно указать

        другой файл помощи. Это удобно при создании диалоговой

        помощи в прикладной программе.

          Hа базу помощи накладывается четыре ограничения:

         она должна содержать минимум два поля

         первое поле должно быть символьным

         второе поле должно быть memo полем

         база может иметь до 32,776 записей

  Если выполнена команда SET HELP TO без указания файла, FocPro

будет подключать файл FOXHELP.DBF.

         SET HELPFILTER

SET HELPFILTER [AUTOMATIC] TO [<вырL>]

  Выводит поднабор текстов помощи в окно помощи Help.

AUTOMATIC

     Автоматически удаляет критерий  HELPFILTER  после закрытия

     окна  помощи.  Включение  фразы  AUTOMATIC  идентично

     выдаче команды  SET  HELPFILTER  TO  немедленно после

     закрытия окна помощи. Фраза AUTOMATIC должна располагаться

     непосредственно перед выражением фильтра  TO <вырL>.

TO <вырL>

       Фильтрация  текстов тем  помощи  выполняется  на

     основании <вырL>.  В  окне  помощи  появятся  только те

     записи из базы данных помощи, которые  соответствуют

     условиям, указанным  в <вырL>.

       Выражение фильтра  <вырL>  включает  поле из базы данных

     помощи. В это выражение могут быть включены любые поля из

     этой базы данных.

        Следующая таблица содержит общие категории помощи и

     соответствующие им  двухсимвольные обозначения,

     использованные в поле CLASS базы данных  помощи

     FOXHELP.DBF.  Эти сокращения могут использоваться для

     фильтрации тем помощи. Сокращенное обозначение не

     обязательно соответствует только одной теме.

  Отметим, что если HELPFILTER установлен по данной команде SET,

то темы на покрывающем меню See Also должны соответствовать

условиям фильтрации  для  подсоединенных  текстов,  которые

доступны  для вывода. Если какая-то тема  в этом меню не

соответствует условию фильтрации, но была выбрана, то появится

сообщение "No help found for..."  (Hет помощи для ...).

Категория               Обозначение

-----------------------------------

Общее:                        General

Hовое                           wn

Совместимость                   cm

Конфигурация                    cf

Сообщения об ошибках            em

Управление файлами              fm

Операции Интерфейса             in

Hовое в 2.0                     nx

Команды/Функции:              Command/Function

Символьные данные               ch

Преобразования данных           dc

База данных                     db

Данные Дата и Время             dt

Улучшения в 2.0                 ex

Среда выполнения                en

Ошибки и Отладка                er

Управление по событиям          eh

Ввод/Вывод файлов               fi

Управление файлами              fm

Индексирование                  ix

Операции Интерфейса             in

Клавиатура и "мышь"             km

Логические данные               lo

Функции низкого уровня          ll

Временные переменные

        и массивы               mv

Меню типов Menu и Popup

      (Системные имена)         mp

Многопользовательская среда     mu

Hовое в 2.0                     nx

Числовые данные                 nu

Печать                          pr

Выполнение программы            pe

Структурное Программиро-

                 вание          sp

SQL                             sq

Слияние текста                  tm

Окна                            wi

Системная память

        Переменные:             Sysmemvar

Оффисные функции                da

Печать                          pr

Имена системных меню            sn

[плюс сокращения для

Команд/Функций, приведенные

         выше]

Интерфейс:                       Interface

Диалоги                          di

Общее                            ge

Меню                             me

Окна                             wi

  Для удаления  установки  HELPFILTER  следует  выдать команду

SET HELPFILTER TO без аргументов.

        SET LOGERRORS

SET LOGERRORS ON | OFF

  При компиляции программы диагностические сообщения компилятора

об ошибках могут быть записаны в текстовый файл. Применение

команды SET LOGERRORS  ON  позволяет  создать  такой файл. Он

имеет то же имя, что и компилируемая программа, но с расширением

.ERR . Если  файл диагностики с таким именем уже существует, он

будет перекрыт.

  Если в программе  нет ошибок или  если выдана команда SET

LOGERRORS OFF,  файл диагностики не создается. Если программа

откомпилирована без   ошибок,   но    файл   диагностики    с

соответствующим именем  уже существует,  этот файл стирается.

  По умолчанию принято SET LOGERRORS ON.

          SET MARGIN

SET MARGIN TO <вырN>

  Устанавливает размеры левой  границы  при выдаче  на  печать

любой  информации. Выдача информации на экран не затрагивается.

Значение по умолчанию - 0.

  При употреблении команды  позиция   левой границы

записывается в  системную временную переменную _PLOFFSET. Такого

же эффекта можно достичь непосредственным помещением требуемой

величины в _PLOFFSET.

  Кроме того, установить дополнительное смещение левой границы

можно с помощью системной временной переменной _LMARGIN.

         SET MESSAGE

SET MESSAGE TO [<вырC>]

SET MESSAGE TO [<вырN> [LEFT | CENTER | RIGHT]]

SET MESSAGE WINDOW [<имя окна>]

  Обеспечивает определение сообщения и его местоположения на

экране или в окне.

  Формат команды с символьным выражением позволяет определить

сообщение для вывода.

  Символьная строка, указанная в команде, может быть  длиной  до

79  символов.  Если указана символьная строка, ее необходимо

заключить  в одинарные  или  двойные апострофы,  либо    в

квадратные скобки.

  Команда SET MESSAGE TO <вырN> определяет строку экрана или

окна, на которой будут выданы дополнительные сообщения при

выполнении команды MENU TO. Если числовое выражение равно нулю,

то сообщение не выводится.   Позиционировать   сообщение  по

горизонтали для команд @...PROMPT в указанной строке можно

необязательных опций LEFT, CENTER, RIGHT.

  Команда  SET MESSAGE  TO   без   параметров устанавливает  в

качестве  сообщения  пустую строку и  присваивает   номеру

строки  вывода значение последней  физической строки  экрана или

окна.

SET MESSAGE WINDOW [<имя окна>]

        Сообщение выводится в указанном окне. Убрать выдачу

        сообщения из окна и направить на экран можно с помощью

        команды  SET MESSAGE WINDOW.

            SET MOUSE

SET MOUSE ON | OFF

SET MOUSE TO [<вырN>]

  Управление чувствительностью манипулятора "мышь".

  Значение по умолчанию - ON

  Значение по умолчанию - 5

ON           Подключение манипулятора.

OFF          Блокировка манипулятора.

TO [<вырN>]  Регулировка чувствительности манипулятора  "мышь" к

             перемещению  .   Значение числового выражения

             может меняться от 1  до 10,  при  этом  1

             соответствует минимальной чувствительности, а 10 -

             максимальной.

           SET NOTIFY

SET NOTIFY ON | OFF

  Включает/выключает режим вывода ряда системных сообщений.

Значение по умолчанию - ON.

  Команда SET  NOTIFY  позволяет  подавить вывод системных

сообщений.  Если  установлено  SET   NOTIFY  ON  -   системные

сообщения выводятся. Если установлено  SET NOTIFY OFF -  вывод

большинства системных сообщений будет подавлен. К  сообщениям,

которые нельзя подавить, относятся сообщение "Press any key to

contunue..."  (Для  продолжения  нажмите  любую  клавишу...) и

сообщения, получаемые при создании макроса клавиш.

         SET ODOMETER

SET ODOMETER TO <вырN>

  Задает интервал изменения значений  счетчика записей  в

командах,  которые выводят номера записей.  Значение по

умолчанию - 100.

          SET OPTIMIZE

SET OPTIMIZE ON | OFF

  Устанавливает и отключает режим ускоренной работы Rushmore.

Значение по умолчанию - ON.

  Команды  работы  с  базой  данных,  поддерживающие  фразу  FOR

(и входящие в сферу воздействия команды SET FILTER) используют

режим ускоренного  поиска  Rushmore   для  увеличения

быстродействия.  Ситуация, когда может  потребоваться отключить

этот  режим, может возникнуть очень редко.

  Глобальное отключение (или включение) режима Rushmore может

быть выполнено сразу  для  всех  команд,  использующих  этот

режим, рассматриваемой командой SET OPTIMIZE. Указание SET

OPTIMIZE OFF отключает режим  Rushmore, а  указание SET

OPTIMIZE ON  включает ускоренный поиск. Все  команды, которые

используют  данный режим, расширены  опцией  NOOPTIMIZE,

которая  может использоваться для отключения ускоренного  поиска

Rushmore  только в  пределах одной команды.

  Hиже приведен список команд, использующих режим ускоренного

поиска Rushmore при использовании в них фразы FOR. Команда SET

OPTIMIZE влияет на каждую из этих команд. Каждая из

перечисленных команд  поддерживает  опцию  NOOPTIMIZE  для

отключения   режима ускоренного поиска Rushmore в пределах

команды.

AVERAGE       DISPLAY  REPLACE

BROWSE        EDIT     REPORT

CALCULATE     EXPORT   SCAN

CHANGE        JOIN     SORT

COPY TO       LABEL    SUM

COPY TO ARRAY LIST     TOTAL

COUNT         LOCATE

DELETE        RECALL

           SET PATH

SET PATH TO [<список путей>]

  Команда указывает путь поиска файлов, которые  не найдены в

текущем рабочем директории. <Список путей> представляет собой

последовательность  путей,  разделенных  запятой   или точкой с

запятой.

            SET POINT

SET POINT TO [<вырC>]

  Заменяет десятичную точку на другой символ. Значение по

умолчанию - .

<вырC> может быть символьной строкой, взятой в кавычки или

скобки, или символьной переменной памяти. Команда SET POINT TO

восстанавливает значение по умолчанию.

          SET PROCEDURE

SET PROCEDURE TO [<файл>]

  Открывает заданный файл процедур. Если в имени файла  процедур

не  указано расширение  имени,  предполагается расширение .PRG .

Может быть открыт только 1 файл процедур одновременно. Команда

SET  PROCEDURE  TO  без параметров закрывает текущий открытый

файл процедур. Поиск программ, вызываемых командой DO, и UDF

выполняется в файле процедур, если они не найдены в текущей выпо

лняемой программе.

          SET RESOURSE

SET RESOURSE ON | OFF

SET RESOURSE TO [<файл>]

  С  помощью   показанных  двух  команд  можно обновить  файл

ресурсов  или  выбрать другой файл ресурсов.

  Файл ресурсов - это файл формата базы  данных FoxPro,

содержащий информацию о  системных и определенных  пользователем

ресурсах, таких, как цветовые наборы, макросы, расположение  и

размер системных окон и т.д. Любые  изменения среды  фиксируются

в  файле  ресурсов, если установлено   SET   RESOURSE   ON.

Если  же установлено SET  RESOURSE OFF,  все изменения среды не

сохраняются.

  Можно назначить  другой  файл ресурсов, если использовать

команду  SET  RESOURSE  TO. Это позволяет сохранить  состояние

среды  в файле ресурсов,   отличном   от   принимаемого   по

умолчанию  файла.  Позднее  состояние   среды можно восстановить

из нужного файла ресурсов.

  По умолчанию принято SET RESOURSE ON, а файл ресурсов по

умолчанию - FOXUSER.DBF.

         SET SEPARATOR

SET SEPARATOR TO <вырС>

  Определяет разделитель разрядов  больших чисел. Значение по

умолчанию - ,

  Команда позволяет заменить разделитель разрядов  больших

чисел, формируемый фразой PICTURE, на другой символ. Аргументом

команды может быть  символьная строка,  заключенная в одиночные

или двойные кавычки, либо в скобки. Можно использовать ссылку

на   временную переменную  символьного типа. Для восстановления

используемого  по   умолчанию символа запятой (,) используется

команда  без аргументов.

        SET SHADOWS

SET SHADOWS ON | OFF

  Специфицирует наличие или отсутствие имитации тени позади окон

на экране.

  Команда  действует глобально, и  включает или выключает  вывод

тени  позади  системных   и пользовательских  окон  и

вертикальных меню. Любой текст  или объекты,  покрываемые тенью,

остаются видимыми.

  Команда SET SHADOWS ON включает вывод тени, а SET SHADOWS OFF

- выключает. Данная команда не действует на пользовательские

окна, определенные посредством DEFINE WINDOW, если только  окно

не  определено  с употреблением фразы SHADOW.

  По умолчанию принято SET SHADOWS ON.

           SET SKIP OF

SET SKIP OF MENU <имя меню типа menu> <вырL>

SET SKIP OF PAD <имя опции> OF <имя меню типа menu> <вырL>

SET SKIP OF POPUP <имя меню типа popup> <вырL>

SET SKIP OF BAR <вырN> | <имя системной опции>

        OF <имя меню типа popup> <вырL>

  Команда SET SKIP  OF  дает  возможность  выборочно  включать и

блокировать меню типа menu, опции этих горизонтальных меню

(pad),вертикальные меню типа popup и их опции (bar). Команда

SET  SKIP OF может использоваться с системным меню FoxPro и с

определенными пользователем меню.

  Если оценка логического выражения <вырL> дает "истину" (.T.),

то меню типа menu, их опции (pad), меню типа popup и их опции

(bar), включенные  в   команду  SET   SKIP  OF,   блокируются

(делаются недоступными для  выбора). Если  же оценка  этого

выражения дает "ложь" (.F.), то эти меню и их элементы

становятся доступными для выбора и выводятся соответствующим

цветом.

SET SKIP OF MENU <имя меню типа menu> <вырL>

     SET SKIP OF MENU позволяет блокировать и деблокировать

     горизонтальные меню типа menu.

       SET SKIP OF MENU можно также использовать для

     блокирования и деблокирования  пользовательских  меню,

     созданных командой DEFINE MENU.

SET  SKIP  OF  PAD  <имя  опции>  OF  <имя меню типа menu> <вырL>

     SET SKIP OF  PAD  позволяет  деблокировать  и  блокировать

     конкретные опции (pad) горизонтального меню типа menu.

     Можно использовать эту команду как по  отношению   к

     опциям системного  меню  FoxPro,  так  и к опциям

     пользовательского меню, определенным командой DEFINE PAD.

SET SKIP OF POPUP <имя меню типа popup> <вырL>

     SET SKIP OF POPUP позволяет деблокировать и блокировать

     меню типа popup.  Эту  команду можно использовать по

     отношению к   системным вертикальным  меню, а  также к

     меню, определенным  пользователем командой DEFINE POPUP.

SET SKIP OF BAR <вырN> | <имя системной опции>

                          OF <имя меню типа popup> <вырL>

     SET SKIP OF BAR  позволяет  блокировать  и  деблокировать

     конкретные  опции вертикальных меню типа  popup. Можно

     использовать  эту   команду  как   по  отношению к опциям

     системных  вертикальных   меню  FoxPro, так  и   к  опциям

     пользовательских меню, определенным командой DEFINE BAR.

           SET SPACE

SET SPACE ON | OFF

  Вставляет или не вставляет пробел между значениями полей,

выводимых командами ? и ??.

  SET SPACE ON вызывает вставку пробела  между значениями полей

или выражений, тогда как SET SPACE   OFF   выводит   поля   и

выражения "склеенными".

  По умолчанию принято SET SPACE ON.

           SET SYSMENU

SET SYSMENU ON | OFF | AUTOMATIC

        | TO [<список системных меню типа popup

        | список опций (pad)>] | TO [DEFAULT]

  Управление доступом к системному горизонтальному меню.

Значение по умолчанию - ON.

ON

     Системное горизонтальное меню является доступным в

     процессе выполнения программы, когда SET  SYSMENU

     установлено в ON  и FoxPro  ожидает  ввода  с  клавиатуры

     (при выполнении команд BROWSE,  READ,  MODIFY  COMMAND  и

     т.п.). Системное меню не высвечивается, но может быть

     выведено и стать доступным  при нажатии  клавиш  Alt  и

     F10  или при двойном нажатии кнопки мыши.

OFF

     Доступ к  системному  меню  в процессе выполнения программы

     невозможен.

            SET TALK

SET TALK  ON | OFF |

        WINDOW [<имя окна>]

        | NOWINDOW

  Направление информации о выполнении  команд на экран или  в

окно.  Значение по умолчанию - ON.

  Если значение параметра SET TALK равно ON, на экран выдается

протокол работы  команд (таких,  как SORT,  PACK и  т.д.).

Если значение параметра SET TALK равно OFF, протокол не

выдается.

  По умолчанию данные выдаются после обработки каждых 100

записей, однако  этот  интервал  можно  изменить  с  помощью

команды  SET ODOMETER.

  Установка значения  SET  TALK  ON  существенно  снижает

скорость выполнения команд за счет выдачи информации на экран.

  По умолчанию принято SET TALK ON.

WINDOW [<имя окна>]

     Если Вы специфицировали SET  TALK WINDOW,  то информация  о

     выполнении команд выводится в  верхнем правом углу экрана

     в окне системных сообщений, если установлено SET NOTIFY в

     ON.

       Если начать ввод с  клавиатуры или  сместить мышь,  то

     окно системных сообщений исчезнет.

       Можно также направить эту информацию в окно,определенное

     пользователем, если указать имя этого окна в <имя окна>

     после ключевого слова WINDOW. Это окно  обязательно должно

     быть создано командой DEFINE WINDOW до того, как вывод

     будет направлен  в  него.  Это  окно  не  обязательно

     должно быть активным или  видимым, для  того, чтобы  вывод

     адресовался в него.

NOWINDOW

     Указание SET TALK NOWINDOW направляет вывод на экран.

          SET TYPEAHEAD

SET  TYPEAHEAD TO <вырN>

  Определяет количество символов, сохраняемых в буфере

клавиатуры.  По умолчанию - 20.

  Допустимый диапазон <вырN> - целое число от 0 до 128. Для

исключения  буфера ввода  следует указать SET  TYPEAHEAD  TO

0.  Это   деактивизирует команду ON KEY и функцию INKEY().

          SET UDFPARMS

SET UDFPARMS TO VALUE | REFERENCE

  Управляет передачей параметров в функцию пользователя (UDF).

  По умолчанию переменные передаются в UDF по значению. (В

процедуры переменные всегда передаются по ссылке, для передачи

по значению они берутся в скобки). При передаче по значению

переменная может быть изменена в процедурк или UDF, но ее

значение в вызывающей программе не изменяется.

  При передаче переменной по ссылке и изменении ее значения в

UDF значение переменной в вызывающей программе также изменится.

  В процедуру или UDF можно передать целый массив. Если SET

UDFPARMS TO REFERENCE или имени массива предшествует @, будет

передан весь массив. Если SET UDFPARMS TO VALUE или имя массива

взято в скобки, по значению будет передан первый элемент

массива. Элементы массива всегда передаются по значению.

          SET UNIQUE

SET UNIQUE ON | OFF

  По умолчанию принято SET UNIQUE OFF, при этом индекс

отображает все записи с одинаковыми ключами.

  При перестройке индекса, созданного с  опцией UNIQUE,  с

помощью  команды   REINDEX,   он сохраняет   статус   UNIQUE,

независимо  от значения параметра SET UNIQUE (ON или OFF).

               SET

SET(<вырС>[,1])

  Возвращает текущий статус различных команд SET...TO  и

SET...ON | OFF.

  Применение функции с необязательным аргументом [,1] возвращает

установки SET  TO, применение же  без этого  аргумента позволяет

получить  состояние  ON  |  OFF. Подробно эти опции описаны в

соответствующих командах SET.

  Функция распознает четырехбуквенные сокращения ключевых слов,

например, RELA  или PRIN.


Ввод - вывод и команда Read

  В конце этого раздела Вы найдете маленький пример. Такие

примеры будут и во всех последующих разделах, а все вместе они

составят программу для обслуживания расписания.

  С помощью клманды KEYBOARD <вырC> [PLAIN] можно имитировать

ввод данных с клавиатуры, не делая его фактически.

  KEYBOARD позволяет помещать в буфер клавиатуры произвольную

строку символов.  Данные  сохраняются  в  буфере до тех пор,

пока FoxPro  не  перейдет  в  режим   ввода.  В  этот  момент

данные считываются из  буфера клавиатуры  и обрабатываются  так

же,  как если бы они были введены при помощи клавиатуры.

<вырC>    Выражение, помещаемое в буфер клавиатуры. Выражение

          может представлять собой символьную строку, метку

          клавиши или множество меток  клавиш,   определенную

          пользователем функцию,   которая возвращает

          символьную  строку.  Если  <вырC>  является   меткой

          клавиши, метка клавиши должна задаваться в кавычках и

          в  скобках.

            В буфер клавиатуры  можно поместить до  128 нажатий

          клавиш.  Если буфер  клавиатуры  заполнен,  в  него

          нельзя  добавить ни одного нажатия клавиши.

PLAIN     Если существует макрос нажатия клавиш или активны

          команды ON  KEY LABEL, то при помощи фразы PLAIN можно

          передать непосредственное назначение заданной клавиши.

          PLAIN помещает в буфер клавиатуры символьное название

          клавиши, а не назначение клавиши.  Hапример, если по

          команде ON  KEY LABEL  за клавишей  "A" закреплен

          вызов подпрограммы и символ "A"  включен в <вырC>,

          использование  фразы PLAIN приведет  к ттому,  что в

          буфер клавиатуры  будет помещена буква "A".

          Процедура, закрепленная за "A" выполняться не будет.

          Метки специальных клавиш

          Клавиша          <метка клавиши>

          -------------------------------

          Стрелка влево          LEFTARROW

          Стрелка вправо         RIGHTARROW

          Стрелка вверх          UPARROW

          Стрелка вниз           DNARROW

          Home                   HOME

          End                    END

          PgUp                   PGUP

          PgDn                   PGDN

          Del                    DEL

          Backspace              BACKSPACE

          Пробел                 SPACEBAR

          Ins                    INS

          Tab                    TAB

          Shift Tab              BACKTAB

          Enter                  ENTER

          F1 - F10               F1, F2 ...

          Ctrl+F1 - Ctrl+F10     Ctrl+F1, Ctrl+F2 ...

          Shift+F1 - Shift+F9    Shift+F1, Shift+F9...

          Alt+F1 - Alt+F10       Alt+F1, Alt+F2 ...

          Alt+0 - Alt-9          Alt+0, Alt+1 ...

          Alt+A - Alt+Z          Alt+A, Alt+B ...

          Ctrl+Стрелка влево     Ctrl+LEFTARROW

          Ctrl+\стрелка вправо   Ctrl+RIGHTARROW

          Ctrl+Home              Ctrl+HOME

          Ctrl+End               Ctrl+END

          Ctrl+PgUp              Ctrl+PGUP

          Ctrl+PgDn              Ctrl+PGDN

          Ctrl+A - Ctrl+Z        Ctrl+A, Ctrl+B ...

          Правая кнопка мыши     RIGHTMOUSE

          Левая кнопка мыши      LEFTMOUSE

          Мышь                   MOUSE

          Esc                    ESC

  Команда CLEAR имеет такие основные формы:

CLEAR

CLEAR ALL

CLEAR FIELDS

CLEAR GETS

CLEAR MACROS

CLEAR MEMORY

CLEAR MENUS

CLEAR POPUPS

CLEAR PROMPT

CLEAR READ

CLEAR TYPEAHEAD

CLEAR WINDOWS

CLEAR

  Очищает экран или текущее окно и удаляет все определенные

команды GET.

ALL

  Удаляет все   временные   переменные   и   массивы  из

памяти, осуществляет переход  в первую  рабочую область  и

закрывает все открытые базы  данных, включая  соответствующие

файлы индексов и форматов, а также текстовые файлы.   CLEAR ALL

также удаляет все определенные  пользователем  горизонтальные  и

вертикальные меню, окна.  CLEAR ALL не удаляет системные

временные переменные.

FIELDS

  Удаляет список полей, созданный по команде SET FIELDS TO. При

этом автоматически выполняется команда SET FIELDS OFF.  В

отличие от команды  SET FIELDS  TO команда  CLEAR FIELDS

освобождает все списки полей  во всех  рабочих областях,  а не

только в текущей рабочей области.

GETS

  Удаляет все определенные команды GET.

MACROS

  Удаляет из памяти все макросы клавиш, включая все назначения

функциональных клавиш.

MEMORY

  Удаляет из памяти все глабальные и локальные временные

переменные и массивы. Системные временные переменные не

удаляются.

MENUS

  Удаляет из памяти все горизонтальные меню, а также очищает от

них экран или окна.

POPUPS

  Удаляет из памяти все вертикальные  меню, а также очищает от

них экран или окна.

PROMPT

  Удаляет все опции меню, созданные при помощи команд @ ...

PROMPT и необработанные командой MENU TO.

READ [ALL]

  Прекращает выполнение текущей команды READ и передает

управление команде READ предыдущего уровня (если такая

существует).  Использование фразы ALL прекращает выполнение всех

команд READ на всех уровнях.

TYPEAHEAD

  Освобождает буфер ввода с клавиатуры.

WINDOWS

  Удаляет из памяти и экрана все определенные пользователем

окна.

  Близка к ней по смыслу команда

 @ <ряд1,кол1> [CLEAR | CLEAR TO <ряд2,кол2>].

  Эта форма команды @ очищает область экрана или активного окна.

Если в команду не включать опцию   CLEAR  или  CLEAR TO, то

очищается только одна строка <ряд1>, начиная с позиции <кол1>.

  Если указать опцию CLEAR, то очищается прямоугольная область

экрана или окна, ее левый верхний угол размещается по

координатам <ряд1,кол1>, а нижний правый угол совпадает с нижним

правым углом экрана или окна.

  Hаконец, если использовать опцию CLEAR TO, то очищается

прямоугольная область как и в приведенном выше случае, за тем

исключением, что правый нижний угол этой области определяется

координатами <ряд2,кол2>.

  Команда @ ... SAY/GET является наиболее универсальной командой

такого типа. Она используется для форматированного ввода -

вывода данных на экран или принтер.

@ <ряд, столбец>

        [SAY <выр1>

                [PICTURE <вырC1>]

                [FUNCTION <функция1>]

                [COLOR SCHEME <вырN1>

                |COLOR <список цвтовых пар1>]]

        [GET <пер>

                [PICTURE <вырC2>]

                [FUNCTION <функция2>]

                [DEFAULT <выр2>]

                [ENABLE | DISABLE]

                [MESSAGE <вырC3>]

                [[OPEN]

                        WINDOW <имя окна>]

                [RANGE [<выр3>]

                        [, <выр4>]]

                [SIZE <вырN2>, <вырN3>]

                [VALID <вырL1> | <вырN4>

                        [ERROR <вырC4>]]

                [WHEN <вырL2>]

                [COLOR SCHEME <вырN5>

                |COLOR <список цветовых пар2>]]

<ряд, столбец>       экрана/принтера представляют собой пару

                     чисел X и Y, где X определяет строку экрана

                     / принтера, а Y  - колонку. Для

                     видеотерминалов с размером экрана 25 строк

                     х 80 колонок, диапазон X равен 0-24, а

                     диапазон Y равен 0-79. Диапазоны координат

                     для печатающего устройства определяются

                     размером страницы.

SAY <выраж>      Если  в  команде присутствует фраза  SAY,

                 указанное в ней <выр1> оценивается и выдается в

                 заданных значениях координат. Этим выражением

                 можеть быть и определенная пользователем

                 функция. Если значение параметра SET DEVICE TO

                 равно SCREEN, то информация будет выведена на

                 экран.  При значении SET DEVICE TO PRINT

                 информация будет выведена на печатающее

                 устройство.

SAY...PICTURE <вырС1>  Если в команде присутствует фраза PICTURE,

                       значение <выр1> редактируется в

                       соответствии с заданным шаблоном <вырС1>

                       перед выдачей.  Эта символьная строка

                       может содержать коды функций опции

                       FUNCTION, коды шаблона опции PICTURE или

                       их комбинацию.

                         Если используются коды функций, то при

                       первом их появлении в <вырС1> им должен

                       предшествовать символ @. После символа @

                       можно указать любое нужное количество

                       кодов (без пробелов между  ними).  После

                       последнего кода в строке должен  быть

                       указан  хотя бы один  пробел. Это

                       означает конец кодов FUNCTION и переход к

                       символам <шаблона> PICTURE.

                         Возможные значения кодов FUNCTION и

                       PICTURE приведены ниже.

SAY...FUNCTION <функция1 > Фраза FUNCTION  представляет

                           альтернативную возможность включать в

                           строку   коды  для редактирования

                           значений, выведенных  командой SAY.

                           Указывать символ "@" перед кодами не

                           нужно.  Указание кодов во фразе

                           FUNCTION эквивалентно использованию

                           их в PICTURE.

GET                         Если присутствует фраза GET, можно

                            выполнить полноэкранное

                            редактирование   <пер>    либо

                            начиная с  <ряд,кол>, либо  со

                            следующей позиции экрана после

                            информации, выведенной  командой

                            SAY.

                              Если присутствуют обе фразы  - SAY

                            и GET, между  информацией,

                            выведенной командой   SAY   и

                            информацией, выведенной командой GET

                            автоматически  включается пробел.

                              Для полноэкранного редактирования

                            значений   полей   GET следует

                            вместе с GET использовать команду

                            READ.

                              Если значение параметра SET DEVICE

                            равно  TO PRINT,  фраза GET

                            игнорируется.

GET...PICTURE <вырС2> и

      FUNCTION <функция2>  Эти фразы имеют здесь то  же

                           назначение, что в SAY,  а кроме того,

                           используются для редактирования

                           данных, введенных в ответ на GET.

GET...RANGE [<вырN3>]

            [,<вырN4>]     Опция RANGE может использоваться с

                           символьными и числовыми переменными и

                           переменными типа  даты. Она позволяет

                           задавать диапазон значений, в который

                           должны попадать вводимые величины.

                           Вводимые данные должны быть либо

                           символьными, либо датами, либо

                           числами в зависимости от типа данных,

                           заданных в <пер>.

                             Если введенное значение не попадает

                           в заданный  диапазон, на   экране

                           воспроизводится соответствующее

                           сообщение, подсказывающее

                           допустимый диапазон.

                             Верхняя или нижняя граница

                           диапазона может отсутствовать. В

                           этом случае    проверка соответствия

                           одной  из границ диапазона не

                           выполняется. Если нажать ВВОД, не

                           изменив <пер>, то диапазон не

                           контролируется.

                             Для изменения стандартного

                           сообщения о нарушении диапазона

                           следует использовать команду ON

                           READERROR.

GET...VALID<вырL1> |

   <вырN4> [ERROR <вырC4>]]    Фраза VALID позволяет определить

                               правила контроля вводимых

                               значений в поля GET. Фраза VALID

                               максимально упрощает контроль

                               вводимых значений при

                               использовании определенных

                               пользователем функций. Если  во

                               фразе VALID вызывается функция

                               пользователя,  то  эта функция

                               возвращает значение логического

                               или числового типа.  При попытке

                               выйти из поля, принимаемого  по

                               GET, выражение в VALID

                               оценивается с использованием

                               значения <пер>.

ВНИМАНИЕ!  В  отличие  от  контроля  по  RANGE, контроль по

VALID выполняется всегда, независимо от изменения поля перед

выходом из него.

 Вариант VALID <вырL1>

 [ERROR<вырC4>]           При выполнении команды READ, введенные значения

                          полей проверяются на соответствие

                          правилам, заданным <вырL1>. Если

                          <вырL1> "истинно"   (.Т.), введенное

                          значение считается новым и    команда

                          READ переходит к обработке следующего

                          поля.

                            Если <вырL1> "ложно" (.F.),

                          введенное значение считается неверным

                          и на экране воспроизводится сообщение

                          об ошибке. Пользователь должен нажать

                          клавишу "пробел" и повторить ввод.

                            Вместо стандартного сообщения об

                          ошибке можно специфицировать

                          пользовательское, используя фразу

                          ERROR.  Этот процесс продолжается до

                          тех пор, пока <вырL> "истинно" (.Т.)

                          или до нажатия ключа <Esc>.

Вариант  VALID  <вырN3>     Если вместо логического выражения во

                          фразе VALID специфицировано числовое,

                          то выполняемые действия зависят от

                          значения, возвращаемого <вырN3>. В

                          общем случае числовое значение фразы

                          VALID используется для определения,

                          какое следующее поле GET должно

                          обрабатываться командой READ. Hулевое

                          значение обозначает ошибку в введенной

                          величине для данного поля. Курсор при

                          этом  остается в  том же  самом GET

                          поле (как и в случае с .F.).  Как

                          стандартное сообщение об ошибке,  так

                          и сообщение, заданное фразой  ERROR

                          <вырC3>, подавляются при использовании

                          числового выражения.  Специальное

                          сообщение   об   ошибке можно включить

                          в функцию пользователя, вызываемую в

                          VALID.

  Положительное значение определяет относительный номер

расположенного   далее поля, которое должна обработать команда

READ перед вводом следующего по порядку поля  GET. Например,

если выполняется ввод пятого  поля, а  фраза   VALID  вернула

значение 2,   то следующим вводимым полем станет седьмое поле.

  Отрицательное значение определяет относительный номер

расположенного ранее поля, которое должна обработать команда

READ перед вводом следующего по порядку поля GET.  Например,

если выполняется ввод пятого  поля, а фраза VALID вернула

значение - 2,  то следующим вводимым полем станет третье поле.

  Если в результате получен номер поля, которого не существует,

выполнение команды READ завершается.

WHEN <вырL2>      Эта фраза допускает или запрещает

                  редактирование поля GET на основании

                  результата, заданного условием. Выражение

                  <вырL2> должно принять значение "истина"

                  (.T.), прежде чем курсор сможет попасть в поле

                  для редактирования.  Если фраза WHILE

                  присутствует, и значение <вырL2> равно .F., то

                  ввод в поле невозможен, и курсор перемещается

                  в следующее  по порядку поле.

DEFAULT <выр2>    Эта фраза специфицирует значение, которое

                  заранее помещается в поле, принимаемое по GET,

                  если выполняется добавление записи в базу

                  данных.  Если при этом  просто нажать  ВВОД,

                  то переменной будет присвоено значение <выр2>.

                    Значение, помещаемое в <выр2>, должно быть

                  такого же типа, что и переменная в GET. Если

                  установлено SET CARRY ON, то все значения по

                  DEFAULT вытесняются значениями из предыдущей

                  записи.

MESSAGE <вырC4>     Эта фраза позволяет специфицировать

                  сообщение, которое выводится,  когда курсор

                  попадает в описываемое поле.  Сообщение

                  выводится  на последней  строке экрана   или

                  окна и  временно отменяет  все сообщения по

                  SET MESSAGE. Если установлено  SET STATUS

                  OFF, сообщение не выводится.

[OPEN] WINDOW <имя окна>     Эта фраза позволяет редактировать

                             текстовое поле в предварительно

                             определенном окне с именем <имя>.

                             Для открытия окна нажимается

                             комбинация клавиш Ctrl+Home. Для

                             выхода из окна нажимаются клавиши

                             Ctrl+End.  Если присутствует

                             необязательное ключевое  слово

                             OPEN,   окно откроется по

                             умолчанию, но все равно надо будет

                             нажать клавиши Ctrl+Home для входа

                             в него и Ctrl+End для выхода.

[COLOR SCHEME <вырN5>

COLOR <список цветовых пар2>]]      Эта фраза позволяет изменить

                                    атрибуты основного и/или

                                    измененного цветов для обоих

                                    полей SAY и GET. Можно также

                                    специфицировать атрибуты

                                    цвета посредством

                                    определяемой COLOR SCHEME.

                                    Фраза COLOR работоспособна

                                    с  цветными  и черно-белыми

                                    мониторами.  Все назначения

                                    в этой фразе перекрывают

                                    назначения по команде SET

                                    COLOR, но только для текущей

                                    команды @...SAY...GET.

COLOR SCHEME <вырN5>     также позволяет изменить основной и

                         измененный цвета. Указанный  в COLOR

                         SCHEME номер устанавливает

                         соответствующие основной и измененный

                         цвета.

SIZE <вырN2>, <вырN3>

<вырN2> - определяет высоту редактируемой области в рядах;

<вырN3> - определяет ширину редактируемой области в колонках.

  По умолчанию FoxPro по команде GET создает область

редактирования из одной строки. Длина редактируемой области

определяется размером редактируемой временной переменной или

поля базы данных, либо при помощи фразы PICTURE. Использование

необязательной фразы SIZE позволяет  создавать  область

редактирования высотой более одного ряда. Высота (в рядах)

области редактирования, создаваемой по команде GET указывается в

<вырN2>, а ширина (в колонках) - в <вырN3>. Размер создаваемой

области редактирования может быть меньше или больше размера

редактируемой временной переменной или поля базы данных. Размер

области редактирования и использование фразы PICTURE в

синтаксисе команды  GET определяют режим редактирования. Если

фраза PICTURE не используется, то:

- При редактировании поля базы данных, если заданный при помощи

  фразы SIZE размер области редактирования больше размера поля

  базы данных,редактирование  будет  выполняться  в области,

  равной по размеру полю базы данных.

- При редактировании временной переменной, если размер

  создаваемой  области  редактирования  больше  размера

  временной переменной,  процесс  редактирования  временной

  переменной будет выполняться во  всей области  редактирования

  команды GET. Размер временной  переменной  может  быть

  увеличен  до  размера области редактирования команды GET.

- При редактировании временной переменной, если размер области

  редактирования меньше размера временной переменной,

  можно редактировать всю переменную, при этом будет

  выполняться прокручивание значения временной переменной внутри

  редактируемой области команды GET.

Если фраза PICTURE команды GET используется:

-  Если шаблон PICTURE меньше размера области редактирования,

   фраза PICTURE  имеет преимущество.  Hапример, если  PICTURE

   имеет шаблон "AAA" (три буквы) и значение фразы SIZE равно

   2,10 (высота - два ряда, ширина - 10 колонок), в области

   редактирования команды  GET  можно  редактировать  первые

   три  буквы  временной переменной или поля базы данных.

- Если шаблон PICTURE больше размера области редактирования, в

  области редактирования можно редактировать всю

  временную переменную или поле базы данных, при этом будет

  выполняться прокручивание внутри редактируемой области команды

  GET.

VALID <вырL1> | <вырN4>

  Позволяет определить правила контроля вводимого значения  в

поле команды  @...  GET. Фраза VALID позволяет значительно

упростить контроль экранного ввода, если она используется в

сочетании с определенными пользователем функциями. Если

определенная пользователем функция вызывается внутри фразы

VALID, функция должна возвращать логическое или числовое

значение. При попытке покинуть поле GET вычисляется значение

выражения VALID на основании значения <пер>.

  В отличие от контроля RANGE контроль VALID выполняется всегда,

если процесс редактирования поля не прерывается нажатием

клавиши Esc.

Логическое выражение.

  Если значение <вырL1> является истиной (.T.), ввод считается

корректным и команда READ переходит к вводу следующего поля.

Если ложь (.F.), вводимое значение считается некорректным и

высвечивается предупреждающее сообщение об ошибке, которое

предлагает повторить ввод после нажатия пробела.  Используемое

по умолчанию сообщением об ошибке можно изменить, используя

необязательную фразу ERROR. Процесс контроля VALID выполняется

пока <вырL1> является истиной (.T.) или не нажата клавиша Esc.

Числовое выражение.

  Будут выполняться действия в зависимости от возвращаемого

<вырN4> значения:

0 - Ошибка контроля: курсор остается в том же поле GET.

+ - Количество полей, на которое надо перейти вперед перед

    вводом следующего поля GET.

- - Количество полей, на которое необходимо вернуться назад

    перед вводом следующего поля GET.

ERROR <вырC4>

  Выдаваемое по умолчанию сообщение об ошибке заменяется на

<вырC4>.

WHEN <вырL2>

  Позволяет или запрещает редактирование поля GET в соответствии

с результатом  определенного  условия.  Выражение  <вырL2>

должно принять значение истины (.T.), прежде чем курсор будет

установлен на редактируемое поле.  Если фраза WHEN присутствует

и значение <вырL2>  равно  лжи  (.F.),  ввод  в  поле

невозможен, и курсор перемещается в следующее по порядку поле

GET.

COLOR SCHEME <вырN5> | COLOR <список цветовых пар2>

                       Позволяет изменить атрибуты основного

                       и/или измененного цветов для полей

                       команды GET. Стандартный цвет

                       используется для части SAY команды @, а

                       измененный цвет используется для GET.

Коды фразы FUNCTION:

A - Только буквы.

B - Выравнивает числа по левой границе.

C - Выводит символ CR (кредит) после положительного числа.

D - Редактирует дату в соответствии с форматом, установленным

    по команде SET DATE.

E - Редактирует дату в Европейском (Британском) формате.

I - Центрирует текст внутри поля.

J - Выравнивает текст по правой границе поля.

K - При установке курсора на поле делает все поле редактируемым.

L - Выводит незначащие нули (вместо пробелов) для чисел.

M <спис> - Определяет список возможных значений.

    <спис> представляет собой набор элементов, разделенных

    запятыми.

R - Вывод в шаблоне литеральных символов, не являющихся частью

    данных и не запоминающихся.

S  <n>  - Ограничивает ширину вывода <n> символами, где <n> -

    положительное число.

T - Обрезает конечные пробелы.

X - Выводит символ DB (дебет) после отрицательного числа.

Z - Подавляет незначащие нули пробелами.

( - Заключает отрицательные числа в скобки.

! - Допускает ввод любого символа, при этом маленькие буквы

    будут преобразовываться в большие.

^ - Вывод числовых данных с использованием научного синтаксиса.

$ - Вывод данных в формате денежной суммы.

Коды шаблонов PICTURE:

A - Допускает ввод только букв.

L - Допускает ввод только логических значений.

N - Допускает ввод только букв и цифр.

X - Допускает ввод любого символа.

Y - Допускает ввод только логических Y, y, N, n, преобразовывая

    y  и n в Y и N, соответственно.

9 - Допускает ввод только цифр в символьных данных, цифр и

    знаков в числовых данных.

# - Допускает ввод только цифр, пробелов и знаков.

! - Преобразовывает маленькие буквы в большие.

$ - Высвечивает денежный знак (установленный по команде SET

    CURRENCY).

* - Высвечивает символы звездочки (*) перед числовыми

    значениями.

. - Определяет позицию десятичной точки.

, - Разделяет цифры слева от десятичной точки.

  READ обычно стоит всегда за командами @ ... GET:

READ

        [CYCLE]

        [ACTIVATE <вырL1>]

        [DEACTIVATE <вырL2>]

        [MODAL]

        [WITH список названий окон]

        [SHOW <вырL3>]

        [VALID <вырL4 | вырN1>]

        [WHEN <вырL5>]

        [OBJECT <вырN2>]

        [TIMEOUT <вырN3>]

        [SAVE]

        [NOMOUSE]

        [LOCK | NOLOCK]

        [COLOR <список пар цветов>

        | COLOR SCHEME <вырN4>]

  Команда READ  активизирует  объекты  GET. Объектами GET

являются поля  в  команде  GET,  области редактируемого текста,

диалоговые индикаторы (Push Button), индикаторы группы (Radio

Button),  меню типа Popup,  двоичные индикаторы  (Check Box),  и

списки (List). Активизируются все объекты GET, созданные после

последней  выдачи команды READ или CLEAR GETS.

Позиционирование

  После выдачи команды READ для перемещения от объекта к объекту

в прямом  направлении  следует  использовать  клавиши  ВВОД,

Tab, Стрелка  ВHИЗ.  Для  движения  в обратном направлении

используются клавиши Shift+Tab или Стрелка ВВЕРХ. Порядок

перемещения полностью определяется порядком выдачи команд GET.

  Если используется мышь, объект можно выбрать, нажимая кнопку

мыши при позиционировании  на нужном  объекте.

Ввод данных

  После захода в поле GET или область редактирования  текста

можно вводить  новый  текст  или  редактировать  имеющийся.  При

этом доступны все стандартные  возможности FoxPro по

редактированию - текст можно урезать, скопировать или

подставить.

Выход из READ

  Существует несколько способов выхода из READ. Перемещение

вперед за последний  объект или  назад за  первый приводит  к

выходу  из READ, если только в команду не включена опция CYCLE.

Hажатие Esc, Ctrl+W  или  выбор  воздействия,  определенного

как завершающего READ, также приводят к выходу из READ.

Многооконный READ

  READ способен поддерживать несколько окон. Объекты GET могут

быть расположены в  различных окнах,  но активизироваться  одним

READ. При этом  перемещение от  объекта к  объекту определяется

только последовательностью выдачи GET, независимо от их

размещения по окнам.

  Окно активизируется и становится текущим окном вывода когда

текущим становится объект в  данном окне.  При нажатии Tab,

ВВОДа или Стрелки ВHИЗ в момент позиционирования на последнем

объекте в окне выполняется позиционирование на первый объект GET

в следующем окне. При нажатии Shift+tab или Стрелки ВВЕРХ в

момент позиционирования на первом объекте в окне выполняется

позиционирование на последний объект в предыдущем окне.

Вложенные READ

  Вложенные READ образуются помещением  GET и  относящегося к

ним READ  в  подпрограмме,  вызываемой  внутри  другого READ.

Глубина вложения READ может достигать пяти.

Порядок выполнения установок

  Hиже перечислена последовательность, в которой выполняются

установки и фразы команды READ при первой выдаче команды:

  - Выполнение фразы WHEN уровня READ

  - Активизация окна первого GET

  - Выполнение фразы ACTIVATE уровня READ

  - Выполнение фразы SHOW уровня READ

  - Выполнение фразы WHEN уровня GET для первого GET

  Hиже приведена последовательность,  в  которой вызываются

фразы READ при активизации нового окна:

   - Выполнение фразы VALID для поля, из которого  перемещается

     позиционирование

   - Выполнение фразы DEACTIVATE уровня READ

   - Деактивизация окна для поля, из которого переносится

     позиционирование

   - Активизация окна для новых полей GET

   - Выполнение фразы ACTIVATE уровня READ

   - Выполнение фразы WHEN для нового поля

Фразы

CYCLE

     Если в команде READ есть эта фраза, то READ не  завершается

     при  перемещении  вперед  за  последний  объект или назад

     за  первый.  Позиционорование  зацикливается.  С последнего

     объекта при движении вперед выполняется переход на первый,

     а с первого, при  движении назад, на  последний.  Тем не

     менее индикатор завершения READ, Esc, Ctrl+W, CLEAR READ

     или фраза TIMEOUT вызывают завершение READ.

ACTIVATE <вырL1>

     Фраза ACTIVATE выполняется при выдаче READ всякий раз при

     смене текущего окна READ. Ее можно рассматривать как  фразу

     WHEN  уровня  окна. Логическое  выражение  во  фразе обычно

     является пользовательской функцией.  Для определения

     текущего окна в этой функции  можно использовать  функцию

     WOUTPUT().  Функция пользователя может отключать

     (блокировать) объекты GET в других окнах, скрывать окна,

     выводить сообщения и т.д.

DEACTIVATE <вырL2>

     Фраза  DEACTIVATE выполняется  при  всякой  попытке перейти

     вперед  в  другое  окно  (сюда  относятся любые изменения

     по WONTOP()).  Эту  фразу  можно  рассматривать как фразу

     VALID уровня окна.

       Логическое выражение во фразе обычно является

     пользовательской  функцией.  Она  может  использоваться для

     проверки  правильности  содержимого   полей  в  окне

     перед переходом в другое окно. Если эта функция возвращает

     "ложь" .F., то READ не  завершается. При  возврате .T.

     выполняется завершение READ.

MODAL

     Включение в команду  этого  ключевого  слова предотвращает

     активизацию всех окон, за исключением окон, содержащих

     READ. Если окно не входит в READ, определенный с MODAL, то

     оно  не может быть активизировано и доступ в него

     невозможен.

WITH <список названий окон>

       Окна, не включенные в READ, могут быть доступны, если

     READ содержит фразу WITH. Окна просмотра Browse, текстовых

     полей, редактирования текста, системные окна FoxPro и

     другие  могут быть доступны в READ, если включить их

     названия в список.

       Включение WITH создает неявный MODAL READ. Только окна,

     включенные в список, могут использоваться в процессе READ.

     - Чтобы сделать доступным  окно просмотра  Browse, в список

       включается его имя. По умолчанию это псевдоним базы

       данных.

     - Чтобы сделать доступным  окно работы  с текстовыми полями

       memo, в список включается псевдоним базы данных.

       Если заголовок окна содержит символы, не являющиеся

     буквой, цифрой или подчеркиванием, включайте  в список его

     часть  до первого такого символа. Hапример, если заголовок

     окна Client List,  то  в  список  заносится  Client,  так

     как пробел не является буквой, цифрой или подчеркивинием.

       Если заголовок окна содержит символы, не являющиеся

     буквой, цифрой или  подчеркиванием, можно  поместить его  в

     список в кавычках.  Для  рассмотренного  примера  это

     будет  "Client  List".

SHOW <вырL3>

       Фраза SHOW выполняется всякий раз при выдаче SHOW GETS.

     Значение, возвращаемое подпрограммой SHOW, игнорируется.

     Эта  подпрограмма может  использоваться для  очистки

     @...SAY  или включения/отключения объектов GET.

VALID <вырL4> | <вырN1>

       Эта фраза выполняется при попытке выхода из READ. Она

     может быть  логическим  или  числовым  выражением  либо

     функцией пользователя,   возвращающей    логическое   или

     числовое выражение.

       Если <вырL4> дает "истину", READ завершается. В противном

     случае курсор остается в том же поле или объекте GET, если

     это возможно.

       Если курсор не может оставаться в том же поле GET или

     объекте GET (возможно, что подпрограмма VALID

     деактивировала поле  или  объект),  выполняется

     позиционирование на первый объект GET.

       Если фраза возвращает числовое значение, выполняется

     переход на  соответствующий  объект.  Если объекта,

     соответствующего такому  числу,  нет,  READ  завершается.

     Возврат  значения, отличного от логического или числового

     расценивается как .T.

WHEN <вырL5>

       Фраза WHEN определяет  выполнение READ на  основе

     результата  оценки логического  выражения. Это  выражение

     должно  давать "истину",  когда  READ  должен   быть

     выполнен.  Если   оно возвращает   "ложь",   READ

     игнорируется,   и  выполняется следующая за ним команда.

OBJECT <вырN2>

       С помощью этой фразы определяется,  какой  объект   GET

     выбирается первым. Числовое значение показывает номер

     этого объекта в порядке выдачи GET.

       Каждый диалоговый, групповой или невидимый индикатор

     (button) рассматривается как объект.

TIMEOUT <вырN3>

       TIMEOUT  задает  отрезок  времени  ожидания READ в

     секундах. Выражение  <вырN3>  определяет  это  время.  Если

     в течение этого  времени   не  будет   ввода  пользователя,

     то  READ  завершается.   При  таком  завершении  READKEY()

     возвращает 20, если  не   было  изменений,  и   276,  если

     изменения текущего поля были.

       При завершении по TIMEOUT все изменения для поля,

     редактирование которого было прервано из-за истечения

     времени, отменяются. Изменения для других полей

     сохраняются.

SAVE

       Все операторы GET очищаются автоматически следом за

     командой READ. Если использовать фразу  SAVE, то можно

     заново  выдать READ, не вводя снова GET.

NOMOUSE

       Фраза NOMOUSE предотвращает выбор объектов с  помощью

     мыши.  Это можно будет сделать только клавиатурой. Но

     использование мыши  внутри  полей  для  усечения,

     копирования,  вставки и позиционирования курсора

     сохраняется.

COLOR [<список пар цветов>]

       Цвет текущего объекта GET  можно задать  посредством

     данной фразы.  Когда  объект  выбран,  подразумевается

     цвет второй цветовой  пары  в  списке.  Все  другие  цвета

     в этом списке игнорируются. Можно  не указывать  первую

     пару, но ее  надо отметить запятой.

COLOR SCHEME <вырN4>

       Если использовать фразу COLOR SCHEME, то цвет второй пары

     из этой схемы  определяет цвет  текущего поля.  По

     умолчанию же для текущего  поля GET  используется вторая

     пара цветов  из схемы 1 (Окна пользователя).  Цвет объектов

     GET этой  фразой  не определяется.

Активизация и деактивизация READ

  Когда окно, включенное в READ, выдвигается вперед,

предпринимаемые действия могут управляться фразами команды  READ

ACTIVATE  и  DEACTIVATE.  Окно  можно  выдвинуть вперед нажатием

кнопки мыши, циклическим переходом к нему по Ctrl+F1 или выбором

управления или опции меню, управляющего активизацией окна.

  Когда на переднем плане окно, содержащее @...GET, и

выполняется попытка  выдвинуть  вперед  (наверх)  другое окно,

то выдвигается новое окно, затем выполняется фраза DEACTIVATE.

  Значение, возвращаемое READ DEACTIVATE, отвечает на вопрос:

"Можно ли завершить READ?". Если возвращена логическая "истина",

то READ завершается. В противном случае - нет.

  При выполнении фразы DEACTIVATE  функция WONOP()  возвращает

имя нового  окна,  выдвинутого  вперед,  а  функция  WLAST(),

новая в FoxPro 2.0,  возвращает имя  окна, которое  прежде было

верхним.

  Если пользовательская фраза DEACTIVATE позволяет остаться

верхним новому окну, и оно содержит @...GET, выполняется

фраза READ ACTIVATE.

РЕКОМЕHДАЦИЯ

     Когда окно выдвинуто вперед, то вернуть его в прежнюю

     позицию можно командой

        ACTIVATE WINDOW (WLAST())

     во фразе READ DEACTIVATE. Обратите внимание на

     использование WLAST() в выражении имени.

Функция READKEY() в FoxPro 2.0 была усовершенствована с целью

возможности управления порожденными  READ. Путем указания  в

этой функции в качестве аргумента числа 1 или любого другого

числового аргумента можно определить как был завершен последний

READ.  Возвращаемая функцией  величина может  обозначать, что

последний READ был  завершен закрытием  окна, командой CLEAR

READ,  фразой READ VALID или READ DEACTIVATE, вернувшей .T., и

т.д.

  Hиже перечислены  значения,  возвращаемые  функцией READKEY(1)

с указанием причины завершения READ:

Значение      Причина

_______________________

 1            Ничего из предусмотренного ниже

 2            Выдана команда CLEAR READ

 3            Выбрано управляющее завершение

 4            Закрыто окно READ

 5            Фраза DEACTIVATE вернула .T.

 6            Истекло время READ

  Другая команда вывода, ???<вырC>, осуществляет направление

вывода непосредственно на принтер.

  Три знака вопроса направляют содержание <вырC> непосредственно

на принтер  без  его  обработки  драйвером  принтера.  Эта

команда позволяет  использовать  управляющие  коды  принтера,

которые не поддерживаются имеющимся драйвером принтера.

  Управляющие коды позволяют переустановить состояние принтера,

изменить тип и размер шрифта, включить и выключить жирную печать

и т.д. Эти коды могут состоять из любой комбинации

воспроизводимых или не воспроизводимых выводом символов,

специфичных для конкретного используемого принтера. Управляющие

коды могут быть направлены на принтер несколькими различными

способами.

-    С использованием функции CHR() для вывода символов ASCII

     непосредственно на принтер.

-    Используя круглые скобки ()  для посылки  кодов принтера  и

     символов ASCII.

-    Используя комбинацию функции CHR() и круглых скобок.

-    Коды могут быть засланы  в принтер  перед началом  печати и

     после ее завершения с помощию системных временных

     переменных _PSCODE и _PECODE.

TEXT

        <текст>

ENDTEXT

осуществляют выдачу текста на текущее устройство вывода.

<текст>

        Может состоять из текста, временных переменных, функций,

        или любых их комбинаций. Выражения, функции и временные

        переменные, указанные в <текст>  будут обработаны только

        в случае установки параметра TEXTMERGE, а выражения,

        функции и временные переменные должны быть заключены в

        ограничители, указанные в команде SET TEXTMERGE

        DELIMITERS. Если параметр TEXTMERGE не установлен,

        выражения, функции и временные переменные  будут выданы

        как набор литералов в ограничителях.

          Команда TEXT ... ENDTEXT используется для вывода на

        экран или в активное окно выдачи по умолчанию, но

        выдача может быть отменена в программном файле с помощью

        команды SET CONSOLE OFF. Для осуществления выдачи на

        принтер или в текстовый файл, необходимо использовать

        команду SET PRINTER TO <принтер> или SET PRINTER TO

        <файл> после установки SET PRINT ON.

          Выдача по команде TEXT ... ENDTEXT может

        осуществляться в файл, созданный или открытый функциями

        FCREATE() или FOPEN(). Если файл активизирован функциями

        FCREATE() или FOPEN(), его файл настройки записывается в

        системную переменную _TEXT, выдача направляется в

        соответствующий файл нижнего уровня. В команде TEXT и

        ENDTEXT нет возможности писать комментарии и

        использовать \ или \\  для их написания.

  Команда INPUT [<вырС>] TO <пер> обеспечивает  ввод  данных  с

клавиатуры  во временную переменную.

  Если <временная переменная> отсутствует, команда INPUT создает

ее.

  Если в команде указано <вырС>, на экране воспроизводится

заданное сообщение, запрашивающее ввод данных с клавиатуры.

  Если выражение представляет собой символьную строку, а не имя

временной переменной символьного  типа,  его  следует  заключить

в одинарные/двойные  кавычки  или в  квадратные скобки.

  Тип вводимых данных определяет тип создаваемой временной

переменной.

  ACCEPT [<вырС>] TO <пер> используется для ввода с клавиатуры

                           символьных  данных,   которые

                           заносятся во временную переменную.

                           Имя временной переменной должно  быть

                           задано  в команде. Ввод данных

                           завершается нажатием  ключа <ВВОД>.

  Если в команде указано <вырС>, на экране воспроизводится

сообщение-подсказка или запрос, какие именно данные пользователь

должен ввести. Если <вырС> представляет собой строку символов,

она должна быть ограничена одиночными или двойными кавычками или

квадратными скобками. Ввод выполняется со следующей доступной

строки.

  Ограничение разделителями вводимых с клавиатуры данных не

требуется, поскольку команда АCCЕРТ автоматически устанавливает

символьный тип данных.

  Если временная переменная <пер> не существует, она будет

создана командой ACCEPT. Если нажать ВВОД без ввода данных, то

переменная будет иметь пустое значение.  Если в ответ на запрос

команды ACCEPT нажать Esc и при  этом  установлено  SET  ESCAPE

ON, то выполнение программы прекратится. Если же SET ESCAPE

равно OFF, переменная будет иметь пустое значение.

  Если необходимо выполнять проверку данных или блокировать

ошибки ввода, то следует использовать команду @...GET.

  Команда ACCEPT отличается от команды INPUT следующими своими

свойствами:

     - Данные всегда рассматриваются как символьные независимо

       от содержания ввода.

     - Вводимые символьные данные не требуется заключать в

       разделители.

  После @ ... SAY/GET команда WAIT, на мой неискушенный взгляд,

одна из самых красивых:

WAIT [<вырC>] [TO <временная переменная>]

        [WINDOW [NOWAIT]]

        [TIMEOUT <вырN>] [CLEAR]

  пауза в работе системы до нажатия любой клавиши.

<вырC>

        Определяет сообщение, возникающее на экране после

        выполнения команды WAIT. Если <вырC> не указано, будет

        выдан запрос системы "Press any key to continue..."

        ("Для продолжения нажмите любую клавишу").  Выражение

        <вырC> не может быть функцией, определенной

        пользователем.

TO <временная переменная>

        Сохраняется введенное с клавиатуры значение. Если указан

        символ возврата каретки или непечатаемый принтером

        символ, во <временнау переменнаю> будет помещена нулевая

        строка.

WINDOW

        Вывод сообщения команды WAIT в системное окно выдачи

        сообщений в правом верхнем угле экрана.

NOWAIT

        Создание сообщений в окне задержки на подобии системных

        сообщений FoxPro. Как и для системных сообщений,

        возможно перемещение манипулятора "мышь" или нажатие

        любой клавиши для удаления сообщения. При фразе NOWAIT

        не теряется значение нажатого ключа, который был нажат

        для  удаления сообщения с экрана (значение будет

        потеряно, если NOWAIT не указано).

TIMEOUT <вырN>

        Указывает, какое количество секунд будет выполняться

        команда WAIT. Опция TIMEOUT должна быть последней фразой

        в команде WAIT, иначе будет выдано сообщение о

        синтаксической ошибке.

CLEAR

        Удаление системного окна или удаление с экрана

        сообщения, выданного командой WAIT <окно> NOWAIT.

          Hапример, по команде SET TALK WINDOW, сообщения об

        индексации, сортировке и т.д. выдаются в системном окне

        в правом верхнем углу экрана. Окно будет удалено с

        экрана после нажатия клавиши или перемещения

        манипулятора "мышь". Для удаления окна с экрана в

        программе, необходимо ознакомиться с командой WAIT

        CLEAR.

          Остальные опции команды WAIT (WINDOW, NOWAIT,

        TIMEOUT...) не могут быть использованы с командой WAIT

        CLEAR.

+---------------------------------+

¦         Пример программы        ¦

+---------------------------------+

  Итак, очистим экран и память, сделаем необходимые установки и

выведем в системном окошке сообщение:

wait window nowait [Hачальная инициализация]

clear all

set talk off

set deleted on

set resource on

set date british

clear


Функции проверки файлов и дисков в FoxPro
Функции проверки файлов и дисков

  Следующие функции позволяют анализировать наличие нужных

файлов, текущее положение указателя в файле базы данных, размер

свободного дискового пространства и т.п.

  DBF([<псевдоним>]) - возвращает имя открытого в заданной

                       рабочей области файла базы данных.

  В функции DBF() можно задать необязательный аргумент,

указывающий к какой рабочей области относится  действие функции.

Если в этой рабочей области нет открытого файла базы данных,

функция возвращает нулевую строку. Если аргумент отсутствует,

функция возвращает информацию для файла в текущей рабочей

области.

  Рабочую область можно задать ее номером, буквой или

псевдонимом.

  DISKSPACE() - возвращает объем свободного пространства (в

                байтах) на дисковом устройстве, активном по

                умолчанию.

  Функция особенно полезна для определения наличия достаточного

объема свободной дисковой памяти при выполнении процедур

страхового копирования или команды SORT.

  FIELD(<вырN1>[,<псевдоним>])

  Возвращает имя поля, соответствующее его номеру, заданному

<вырN1> в структуре активного файла базы данных.

  Если <вырN1> относится к несуществующему в текущем активном

файле базы данных полю, функция возвращает нулевую строку.

  Имя поля функция выдает прописными буквами.

  В функции FIELD() можно задать дополнительный второй аргумент,

указывающий к какой  рабочей области относится действие

функции.  Если  в этой рабочей области нет открытого файла базы

данных, функция возвращает нулевую строку. Если второй аргумент

отсутствует,  функция возвращает  информацию  для  файла  в

текущей рабочей области.

  FILE(<файл>) - возвращает логическое значение "истина" (.T.),

                 если символьное выражение <файл> является

                 именем существующего файла.  Если такого файла

                 нет, функция возвращает значение "ложь" (.F.).

  Имя файла должно быть указано с расширением.

  Функция выполняет поиск <файла> только в текущем директории.

Для поиска в других директориях, имени файла должен

предшествовать полный маршрут поиска в соответствии с правилами,

установленными в MS-DOS.

  Имя <файла> может быть строковой константой (должно быть

заключено в кавычки), значением временной переменной символьного

типа, символьным выражением.

  FCOUNT(<псевдоним>) - возвращает число полей открытого файла

                        базы данных.

  В функции FCOUNT() можно задать необязательный аргумент,

указывающий, к какой рабочей области относится действие функции.

Если в этой рабочей области нет открытого файла базы данных,

функция возвращает нулевое значение.

  LUPDATE([<вырN> | <вырC>]) - возвращает дату последнего

                               обновления текущего активного

                               файла базы данных.

  Эта функция полезна, когда контроль обновления базы данных

выполняется не чаще одного раза в сутки.

  <вырN> | <вырC> - номер области или алиас.

  Функция без аргумента возвращает дату обновления базы данных в

текущей рабочей области. Для получения этих данных из другой

рабочей области используется аргумент, который может быть

номером рабочей области, буквой или псевдонимом.

  RECSIZE([<псевдоним>]) - возвращает размер записи файла базы

                           данных, открытого в текущей рабочей

                           области.

  Если в текущей рабочей области открытого файла базы данных

нет, функция возвращает нулевое значение.

  HEADER([<псевдоним>]) - возвращает количество  байт,

                          занимаемое заголовком файла базы

                          данных.  Возвращаемое значение -

                          числовое.

  Заголовок содержит служебную информацию, в частности, о

структуре базы данных. По умолчанию подразумевается текущая

рабочая область.

  Данная функция полезна для определения точного объема файла

базы данных, складывающегося из объема префикса, размера записи,

умноженного на число записей, и плюс 1 байт. Это важно при

выполнении таких команд, как SORT, INDEX, COPY.

  UPDATE ON <ключ> FROM <псевдоним>

                   REPLACE <поле1> WITH <выражение1>

                   [,<поле2> WITH <выр2>...]

                   [RANDOM]

  Использует данные существующего файла для замещения значений

полей активного файла базы данных. Изменения выполняются при

совпадении значения ключа в записях обоих файлов.

  Обновляемый файл базы данных должен быть активным файлом;

файл, указанный во фразе FROM является открытым файлом базы

данных, расположенным в другой рабочей области, заданной

<псевдонимом>.

  <Ключ> должен иметь одно и то же имя в обоих файлах.

Обновление управляется этим полем.

  Оба файла должны быть либо отсортированы, либо

проиндексированы по <ключу>, если не задана опция RANDOM.

  Если задана опция RANDOM, файл, указанный во фразе FROM,

по-прежнему должен быть проиндексирован или отсортирован по

ключу, однако записи активного файла базы данных могут быть в

любом порядке.

  UPDATED() - возвращает значение "истина" (.T.), если последняя

              команда READ изменяла какие-то данные в связанных

              полях GET.

  CURDIR([<вырC>]) - возвращает в виде символьной строки имя

                     текущего директория DOS на дисководе,

                     заданном в <вырС>.

  Если <вырС> опущено, подразумевается текущий дисковод по

умолчанию, задаваемый по SET DEFAULT. Если же задан

несуществующий дисковод, функция возвращает пустую строку.

  Функция CURDIR() игнорирует установку SET PATH.

  SYS(5) - возвращает идентификатор активного по умолчанию

           устройства (диска). Идентификатор устройства

           устанавливается с помощью команды SET DEFAULT TO

           <диск>.

  SYS(2003) - возвращает имя текущего директория на диске по

              умолчанию.

  SYS(2004) - возвращает имя директория, где находится FoxPro.

              Возвращаемое значение - символьное.

  FULLPATH(<файл1> [, <вырN> | <файл2>]) - возвращает полный

                                           маршрут DOS для

                                           заданного файла.

                                           Возврат - символьное

                                           выражение.

  Функция FULLPATH() возвращает полный маршрут DOS для файла или

полный маршрут связи с другим файлом.

  Если FULLPATH() используется с SYS(2014), это позволяет

создавать приложения более компактными. Так, передавая

расположение текущей программы и расположение других файлов,

используемых в программе, функции FULLPATH() и SYS(2014)

используются для получения полного маршрута для этих файлов.

Если определен маршрут для файлов, программы могут эти файлы

вызывать.

<файл1>

        Имя файла. Поиск <файла> выполняется по маршруту FoxPro.

        Hеобходимо обязательно указываать расширение файла. Если

        файл находится по маршруту FoxPro, возвращается его

        полное имя с маршрутом. Маршрут FoxPro устанавливается

        командой SET PATH.

          Если файл не найден по маршруту FoxPro, полный маршрут

        и имя файла будут возвращены в случае нахождения файла в

        текущем директории по умолчанию. Директорий по умолчанию

        указывается в команде SET DEFAULT.

<вырN>

        Указывает на то, что FULLPATH() выполняет поиск по

        маршруту DOS перед поиском по маршруту FoxPro. Выражение

        <вырN> может принимать любое значение.

<файл2>

        Имя второго файла. Если <файл2> указан, FULLPATH()

        возвращает полный маршрут связи <файла1> с <файлом2>.


Команды языка запросов SQL
Команды языка запросов к БД SQL

  В язык FoxPro включен ряд команд из языка запросов SQL

(Structured Query Language). Он хорошо известен многим

пользователям, работавшим на больших ЭВМ, и считается почти

стандартом. Команды SQL могут непосредственно включаться в

программы наряду с собственными командами FoxPro.

  Команды SQL позволяют с максимальной эффективностью

использовать технологию Rushmore. Одна SQL команда позволяет

заменить группу команд FoxPro.

  FoxPro поддерживает следующие команды SQL:

SELECT

        Используется для выборки данных из одной или нескольких

    баз. При использовании команды SELECT FoxPro интерпретирует

    запрос и выполняет выборку указанных данных из баз данных.

    Команда SELECT используется в FoxPro как любая другая

    команда. Для запроса команду SQL SELECT можно использовать:

               В окне Command.

               В программе FoxPro как любую другую команду.

               В окне  RQBE.

CREATE TABLE

        Создание базы данных. Для каждого нового поля базы

    указываются имя, тип, размер и точность.  Эти параметры

    можно указывать в самой команде или брать из массива.

INSERT

    Вставка записи в конец существующей базы данных. Hовая

    запись содержит данные, указанные в самой команде или взятые

    из массива.

  Рассмотри эти команды подробнее:

CREATE TABLE | DBF <имя БД>

        (<имя поля1> <тип> [(<ширина>

        [, <точность>])

        [, <имя поля2> ... ]])

        | FROM ARRAY <массив>

  Каждое новое поле базы данных характеризуется именем, типом,

шириной и точностью. Эти характеристики должны содержаться в

самой команде или браться из массива. Hовая база данных

открывается в режиме индивидульного использования, независимо от

значения установки SET EXCLUSIVE.

CREATE TABLE | DBF <имя БД>

<имя БД> - имя создаваемой базы данных, которое может содержать

           путь и являться выражением.

(<имя поля1> <тип> [(<ширина> [, <точность>] [, <имя поля2>...])

<имя поля1> и <имя поля2> - имена полей новой базы данных.

                            Каждое <имя поля> может представлять

                            собой выражение.

<тип> - буква, означающая тип данных поля. Hекоторые типы данных

        требуют обязательного задания <ширины> поля и <точности>

        (количество символов после запятой).

<тип>, <ширина> и <точность> могут быть следующими:

Тип     Ширина   Точ-ть Описание

----------------------------------

C   ¦         n ¦  -   ¦Строка

    ¦           ¦      ¦символов

    ¦           ¦      ¦длиной n

----+-----------+------+---------

D   ¦         - ¦  -   ¦Дата

----+-----------+------+---------

F   ¦         n ¦  d   ¦Плавающее

    ¦           ¦      ¦длиной n

    ¦           ¦      ¦с точностью

    ¦           ¦      ¦d

----+-----------+------+---------

L   ¦         - ¦  -   ¦Логика

----+-----------+------+---------

M   ¦         - ¦  -   ¦Memo

----+-----------+------+---------

N   ¦         n ¦  d   ¦Число

    ¦           ¦      ¦длиной n

    ¦           ¦      ¦с точностью d

----+-----------+------+---------

P   ¦         - ¦  -   ¦Шаблон

---------------------------------

FROM ARRAY <массив>

<массив> - имя существующего массива, содержащего значения

           имени, типа, ширины и точности для каждого поля базы

           данных.  Значения элементов массива могут быть

           определены при помощи функции AFIELDS().

INSERT INTO <имя БД>

        [(<имяп1> [, <имяп2>

                [, ...]])]

        VALUES (<выр1> [, <выр2>

                [, ...]])

INSERT INTO <имя БД>

        FROM ARRAY <массив>

        FROM MEMVAR

  INSERT добавляет записи в конец существующей базы данных.

Hовые записи включают данные, перечисленные в команде INSERT,

данные, содержащиеся в указанном массиве или данные из временных

переменных.

INSERT INTO <имя БД>

<имя БД>  - имя базы данных, в которую будут добавлены записи.

            <имя БД> может содержать путь или являться

            выражением.

[(<имяf1> [, <имяf2> [, ...]])]

VALUES (<выр1> [, <выр2>[, ...]])

  Определяет значения (<выр1>, <выр2>, ... ), которые будут

помещены в запись, добавляемую в базу данных. Если не указаны

имена полей, данные необходимо размещать в соответствии со

структурой базы данных.

  Если указаны имена полей <имяп1>, <имяп2>, и т.д., имена полей

могут следовать в произвольном порядке. Каждое <имяп> может

являться выражением.

FROM ARRAY <массив>

<массив> - имя существующего массива, в котором содержатся

           данные для добавляемой записи.

FROM MEMVAR

  FROM MEMVAR копирует данные из временных переменных в новую

запись базы данных, определенной при помощи <имя БД>. Данные

копируются только в те поля, которые имеют соответствующие

временные переменные.  Если временные переменные существуют

только для некоторых полей базы данных <имя БД>, остальные поля

будут пустыми.

  Команда SELECT является мощным средством обработки запросов. С

ее помощью из базы - источника выделяются данные и пересылаются

на экран или в файл - приемник. Данные могут быть извлечены из

разных баз, а также сгруппированы и упорядочены желаемым

образом.

SELECT [ALL | DISTINCT]

        [<псевдоним>.]<искомый_элемент>

                [AS <имя_столбца>]

                [, [<псевдоним>.]<искомый_элемент>

                [AS <имя_столбца>] ...]

        FROM <база_данных> [<локальный_псевдоним>]

                [, <база_данных>

                [<локальный_псевдоним>] ...]

        [[INTO <назначение>]

                | [TO FILE <файл>

                [ADDITIVE] | TO PRINTER]]

        [NOCONSOLE]

        [PLAIN]

        [NOWAIT]

        [WHERE <условие_соединения>

                [AND <условие_соединения> ...]

                        [AND | OR

                        <условие_фильтрации>

                        [AND | OR

                        <условие_фильтрации>

        ...]]] [GROUP BY <столбец_группировки>

               [, <столбец_группировки> ...]]

       [HAVING <условие_фильтра>] [UNION [ALL] <команда SELECT>]

       [ORDER BY <элемент_упорядочения> [ASC | DESC]

               [, <элемент_упорядочения> [ASC | DESC]...]]

SELECT [ALL | DISTINCT]

  [<псевдоним>.]<искомый_элемент> [AS <имя_столбца>]

  [, [<псевдоним>.]<искомый_элемент> [AS <имя_столбца>] ...]

     Каждый <искомый_элемент> может быть:

     -   Полем базы данных, указанной во фразе FROM

     -   Константой, указанное значение константы появится в

         каждом ряду результата запроса.

     -   Выражением, которое может включать пользовательскую

         функцию.

Предупреждения по поводу применения функций пользователя

       Хотя применение функций пользователя имеет очевидные

     преимущества, имеется несколько объективных ограничений,

     перечисленных ниже.

1.  Скорость выполнения операции SELECT может быть ограничена

    скоростью выполнения пользовательской функции. При большом

    объеме манипуляций, выполняемых вызываемой пользовательской

    функцией, лучше всего писать последние на Ассемблере или на

    Си.

2.  В пользовательской функции, вызванной из SELECT, можно

    ничего не знать о среде ввода-вывода FoxPro или среде баз

    данных. Вообще говоря, Вы не сможете узнать, какая рабочая

    область выбрана, имя текущей базы данных или даже имена

    полей, участвующих в процессе. Все эти переменные прямо

    зависят от процессов оптимизации в момент вызова

    пользовательской функции.

3.  Изменения среды ввода-вывода или среды баз данных,

    выполненные в вызванной из SELECT функции, не сохраняются.

    Вообще говоря, результат в этом случае непредсказуем.

4.  Единственным реальным путем передать значения в

    пользовательскую функцию, вызываемую из SELECT, является

    список аргументов, пересылаемый в функцию в момент ее

    вызова.

5.  Если путем экспериментов Вы определите, что какое-то

    запрещенное действие выполняется в данной версии правильно,

    нет никакой гарантии, что оно будет работать правильно в

    последующих версиях.

  С соблюдением этих ограничений можно использовать определенные

пользователем функции в SELECT произвольным требуемым образом.

Но не забывайте пункт 1 о быстродействии.

     Для использования с <искомыми_элементами> доступны функции

     работы с полями так, что это поле или выражение,

     включающее поле:

     - AVG(<искомый_элемент>) - среднее значение по колонке

       числовых данных.

     - COUNT(<искомый_элемент>) - число искомых элементов в

       колонке.  COUNT(*) определяет число рядов в выводе

       запроса.

     - MIN(<искомый_элемент>) - определяет наименьшее значение

       искомого элемента в колонке.

     - MAX(<искомый_элемент>) - определяет наибольшее значение

       искомого элемента в колонке.

     - SUM(<искомый_элемент>) - определяет суммарный итог по

       колонке числовых данных.

  Функции работы с полями не могут быть вложенными.

  По умолчанию выводятся вся ряды результата запроса (опция

ALL). Для исключения  дубликатов рядов указывается ключевое

слово DISTINCT.  В команде  SELECT можно указывать DISTINCT

только однократно.

  Hеобязательная фраза AS <имя_столбца> специфицирует заголовок

колонки в выводе результата запроса.  Это полезно, когда имеются

такие (<искомые_элементы>), которые являются выражением или

содержат функции работы с полями, и требуется присвоить колонке

в результате соответствующее имя. <имя_столбца>  может быть

выражением.

FROM <база_данных> [<локальный_псевдоним>]

  [, <база_данных> [<локальный_псевдоним>] ...]

     Перечисляются все <базы_данных>, которые содержат данные,

     извлекаемые из них при поиске. <Локальный_псевдоним> это

     временное имя для <базы_данных>. Если специфицирован

     <локальный_псевдоним>, то обязательно следует использовать

     его вместо имени базы данных во всей команде SELECT.

INTO <назначение>

     Если специфицирована фраза INTO, то генерируемый

     результирующий вывод не выводится. Если в одном запросе

     специфицировано как INTO, так и TO, то TO игнорируется.

     Если же фраза INTO не использована то результат запроса

     появится  на экране, при этом его можно направить на

     принтер или в файл фразой TO.

        <назначением> может быть:

        -  ARRAY  <массив>  -  записывает результаты запроса во

                               временную переменную <массив>.

        -  CURSOR  <курсор> -  Записывает результаты запроса в

                               так называемый <курсор>. Если

                               указанный <курсор> является

                               именем открытой базы данных, то

                               FoxPro закрывает эту базу данных

                               и создает вместо нее другую. Это

                               будет сделано без предупреждения,

                               если установлено SET  SAFETY OFF.

                               После выполнения команды SELECT

                               временный "курсор" остается

                               открытым, но он доступен только

                               для чтения. После закрытия

                               "курсора" последний удаляется.

       Курсоры - это HЕ базы данных. Hа самом деле, оны могут не

     иметь никакого дискового представления, т.е. может быть,

     или может не быть соответствующего рабочего файла.

       Иногда курсоры являются целиком резидентными, особенно в

     запросах. Иногда курсоры являются просто фильтрованным

     представлением некоторых, или всех полей в таблице,

     используемой для создания курсора (представленной через

     внутреннее использование механизма Rushmore и USE AGAIN).

       Курсоры всегда имеют статус только-для-чтения.

       Курсоры доступны только посредством выбора их псевдонима

     (ALIAS). После их выбора к ним применимы все обычные

     операции только-для-чтения баз данных.

       При закрытии курсоры исчезают.

       Т.к. курсоры прекращают своё существование при закрытии,

     их нельзя открыть (USE). (Hельзя открыть что-либо, что уже

     открыто.)

       При создании курсора его имя становится псевдонимом той

     рабочей области, в которой нет открытой базы данных DBF.

       Hе требуется выполнять никаких действий для выбора

     рабочей области для курсора. Выбор рабочей области всецело

     зависит от действий, предпринимаемых оптимизатором запроса.

       Вообще, курсоры создаются быстрее, чем базы данных DBF.

       Если необходимо, чтобы выбранные значения были

     постоянными, или условно-постоянными, используйте SELECT в

     TABLE/DBF, а не в курсор. Таблицы и базы данных DBF,

     созданные SELECT, являются обычными базами данных и, таким

     образом, их существование отдельно от запроса и помещение

     на диск гарантировано.

       Вообще, если значения в таблицах, использованные для

     создания курсора, меняются после создания курсора, начения

     в курсоре МОГУТ меняться. Если вы не хотите, чтобы это

     случилось, вы должны:

     -     выполнить SELECT в DBF или в массив

     -     воздержаться от модификации исходных таблиц

       Чтобы предотвратить модификацию исходных таблиц кем-либо,

     можно

     -     заблокировать таблицы, используемые для создания

           курсора

     -     открыть таблицы, используемые для создания курсора,

           монопольно

       Курсоры "проживают" на устройстве SORTWORK.

        - DBF <база_данных> | TABLE <база_данных> -

                              записывает результаты запроса в

                              <базу_данных>. Если указать уже

                              открытую базу данных, то FoxPro

                              закроет ее и откроет вновь. Если

                              при этом установлено SET SAFETY

                              OFF, то предупреждающего сообщения

                              не будет. Если  в имени не указать

                              расширения, то  будет присвоено

                              расширение  .DBF. После

                              завершения  команды  SELECT  база

                              данных остается открытой и

                              активной.

[TO FILE <файл> [ADDITIVE]

  | TO PRINTER]]

     Если фраза INTO не указывалась, и использована фраза TO, то

     с ее помощью можно направить результаты выполнения запроса

     в текстовый файл ASCII <файл> или на принтер, в дополнение

     к выводу на экран. Фраза ADDITIVE добавляет вывод к

     существующему содержанию файла <файл>. Если не использована

     ни фраза TO, ни фраза INTO, результаты запроса будут

     выведены на экран, если только не специфицировано

     NOCONSOLE.

      В результатах выполнения запроса колонки именуются

      следующим образом:

        - Если <искомый_элемент> является полем с уникальным

          именем, колонке присваивается имя этого поля.

        - Если более одного <искомого_элемента> имеют одно и то

          же имя, например, CUST.ZIP и STATE.ZIP, то колонки

          получат наименования ZIP_A и ZIP_B. Для

          <искомого_элемента> с десятью символами в имени имя

          будет усечено для возможности  добавления

          подчеркивания и символа.

        - Если <искомый_элемент> является выражением, колонка в

          результате выполнения запроса получит имя EXP_A.  Все

          другие выражения получат имена EXP_B, EXP_C, и т.д.

        - Если <искомый элемент> содержит функцию работы с

          полем, такую, как, например, COUNT(), то колонка в

          результатах получит имя COUNT_A.

NOCONSOLE

     Предотвращает вывод результатов запроса на экран.

     Игнорируется, если использована фраза INTO.

PLAIN

     Предотвращает появление наименований колонок в результатах.

     Игнорируется, если использована фраза INTO.

NOWAIT

     Прокручивает результаты выполнения запроса на экране после

     полного его заполнения. При этом не выдается запрос на

     нажатие какой-либо клавиши для продолжения просмотра.

     Фраза игнорируется при наличии фразы INTO.

[WHERE <условие_соединения>

  [AND <условие_соединения> ...]

  [AND | OR <условие_фильтрации>

  [AND | OR <условие_фильтрации>...]]]

     Позволяет включить в результаты запроса только определенные

     записи. <Условие_соединения> специфицируетполя, которые

     соединяют базы данных во фразе FROM. Если в запросе имеется

     более одной базы данных, то необходимо специфицировать

     условие соединения для каждой базы данных после первой.

          ПРЕДУПРЕЖДЕHИЕ

       Если включить в запрос две базы данных и не

     специфицировать условие соединения, то каждая запись из

     первой базы данных будет соединяться с каждой записью из

     второй базы данных при выполнении условия фильтрации.

       Будьте осторожны при соединении баз данных с пустыми

     полями:  FoxPro сравнивает и пустые поля на совпадение.

     Чтобы избежать этого, используется функция EMPTY().

       Множество условий соединения можно объединить посредством

     оператора AND. <Условия_соединения> могут содержать

     функции работы с полями и иметь следующий формат:

        <поле1> <сравнение> <поле2>

        где <поле1> - это  поле из одной  базы данных, а

        <поле2> - поле из другой базы  данных. При этом

        <сравнение> имеет один из следующих видов:

        <сравнение>

        -----------

         =

        < >, !=, #

           = =

           >

          > =

           <

          < =

          Когда для сравнения строк используется оператор =, то

        его действие по-разному зависит от установки команды

        SET ANSI.

          <Условие_фильтрации> специфицирует критерий, которому

        должны удовлетворять записи, чтобы попасть в результат

        действия запроса. Условия фильтрации можно соединять

        операторами AND и OR. Можно также использовать оператор

        NOT для реверса значения логического выражения, а также

        использовать EMPTY() для выявления пустых полей.

        <Условие_фильтрации> может принимать один из следующих

        форматов:

        Формат: <поле1> <сравнение> <поле2>

        Формат: <поле> <сравнение> <выражение>

        Формат: <поле> <сравнение> ALL (<подзапрос>)

                Посредством ALL задается условие соответствия

                <поля> условиям сравнения для ВСЕХ значений,

                сгенерированных подзапросом, чтобы записи, где

                содержатся эти поля, попали бы в результат

                запроса.

        Формат: <поле> <сравнение> ANY | SOME (<подзапрос>)

                Посредством ANY или SOME задается условие

                соответствия для <поля> по крайней мере одному

                из значений условий сравнения, сгенерированных

                подзапросом.

        Формат: <поле> [NOT] BETWEEN <начало> AND <конец>

                Проверка на то, что <поле> имеет значение в

                заданном диапазоне.

        Формат: [NOT] EXISTS (<подзапрос>)

                Проверка на существование по крайней мере одного

                ряда, удовлетворяющему условию подзапроса.

                Посредством EXISTS условие фильтрации

                оценивается как "истина", если только результат

                подзапроса не является пустым множеством.

        Формат: <поле> [NOT] IN <набор_значений>

                Посредством IN задается условие, что <поле>

                должно быть частью заданного набора значений,

                чтобы соответствующая ему запись попала в

                результат работы запроса.

        Формат: <поле> [NOT] IN (<подзапрос>)

                В данном случае <поле> должно быть частью

                набора значений,   возвращенных подзапросом,

                чтобы соответствующая  ему  запись  попала  в

                результат работы запроса.

        Формат: <поле> [NOT] LIKE <высC>

                Это условие фильтрации проверяет для каждого

                <поля>, что оно соответствует заданному <вырC>.

                В качестве части этого  выражения можно

                использовать специальные символы % (процент) и

                _ <(подчеркивание)>. Подчеркивание _ означает

                подстановку одного любого символа, а процент % -

                произвольную последовательность таких символов.

        Подзапрос является командой SELECT внутри внешней

        команды SELECT (запроса) и заключается в скобки. а одном

        уровне можно иметь множество подзапросов для фразы WHERE

        однако вложение подзапросов не допускается. Подзапросы

        могут содержать множественные условия соединения.

          В условиях соединения фразы WHERE могут быть

        использованы следующие функции: UPPER(), VAL(), DTOC(),

        PADR(), PADL() и PADC().

[GROUP BY <столбец_группировки>

  [, <столбец группировки> ...]]

       Группирование рядов в запросе  базируется на значениях

     их столбца или столбцов. <Столбец_группировки> может быть

     полем  базы данных, полем базы  данных, которое включено в

     функцию работы с полями SQL, или числовым выражением,

     указывающим на положение столбца в результирующей таблице

     (самый левый столбец имеет номер 1).

HAVING <условие_фильтрации>

       Сообщает FoxPro о необходимости включения групп в

     результаты запроса по  мере их  соответствия

     <условию_фильтрации>.  Это условие не может содержать

     подзапрос.

       HAVING <условие_фильтрации> следует использовать

     совместно с GROUP  BY  для  специфицирования  условий,

     согласно которым группы могут включаться в результаты

     запроса.  HAVING может включать столько условий фильтрации,

     сколько требуется, эти условия соединяются операторами AND

     или OR. Можно также использовать и NOT для реверса значения

     логических  выражений.

        HAVING без GROUP BY действует подобно фразе WHERE.

     Во фразе HAVING можно использовать локальные псевдонимы и

     функции работы с полями.

[UNION [ALL] <команда SELECT>]

       Выполняет объединение результата одной команды SELECT с

     результатом  другой  <команды  SELECT>.  По  умолчанию

     UNION проверяет комбинации результатов и устраняет

     ряды-дубликаты. Для предотвращения этого устранения

     используется фраза ALL.

        Для комбинации нескольких фраз UNION используются

        скобки.

        Правила выполнения UNION:

        -    Hельзя использовать UNION для комбинации

             подзапросов.

        -    Обе команды SELECT должны выдавать одинаковое

             количество столбцов.

        -    Каждая колонка в результатах  запроса должна  иметь

             такой  же  тип  данных,  что  и соответствующая

             колонка в другой <команде SELECT>.

        -    Только последняя <команда SELECT> может содержать

             фразу ORDER BY, и ORDER  BY должна ссылаться на

             вывод по номеру колонки. Если ORDER BY включена,

             она влияет на общий результат.

[ORDER BY <элемент_упорядочения>

  [ASC | DESC][, <элемент_упорядочения>

  [ASC | DESC]...]]

       Сортирует результаты запроса на основании значений данных

     в колонке или  колонках. Каждый  <элемент_упорядочения>

     должен соответствовать  колонке  в  результате  действия

     запроса и может при этом являться:

        -  Полем в базе данных из фразы FROM, которое также

           является <искомым_элементом> в главной фразе SELECT

           (но не в подзапросе).

        -  Числовым выражением, указывающим на положение

           столбца  в результирующей таблице. Самый левый

           столбец имеет номер 1.

  Фраза DESC вызывает упорядочение по убыванию значения

соответствующего <элемента_упорядочения>.  Фраза ASC

специфицирует упорядочение по возрастанию и принимается по

умолчанию.


Команды управления в FoxPro
Команды управления

  Команды управления являются важнейшим средством построения

программ. Эти команды не могут быть опробаваны и использованы в

интерактивном режиме, а только в программах.

      IF...ELSE...ENDIF

IF <вырL>

   <команды>

[[ELSE]

   <команды>]

ENDIF

  Команда структурного программирования, обеспечивающая

выполнение команд по условию.

  Команда устанавливает условие (логическое выражение) для

последующей оценки его "истинности".

  Если условие оценивается как "истинное", выполняются все

команды, следующие за IF до ELSE или до ENDIF, если команда ELSE

не указана.

  Если условие "ложно" и не указана команда ELSE, все команды

между IF и ENDIF игнорируются. Выполнение программы продолжается

с первой после ENDIF команды.

  Если условие "ложно" и указана команда ELSE, то выполняются

все команды между ELSE и ENDIF.

  Допускается вложение команд IF, при условии наличия для каждой

из них своей команды ENDIF.

  Hа одной строке с ELSE и ENDIF можно размещать комментарии,

которые игнорируются при выполнении.

          DO CASE

  DO CASE

    CASE <вырL1>

       <команды>

       ...

    [CASE <вырL2>

       <команды>]

       ...

    [CASE <вырLN>]

        <команды>

    [OTHERWISE

       <команды>]

  ENDCASE

  Это команда структурного программирования, обеспечивающая

выбор одной последовательности команд из набора возможных

альтернатив.

  Фраза CASE <вырL> устанавливает условное выражение для

последующей оценки  его "истинности".  Это логическое выражение,

такое, например, как: A=B или <числовая переменная><11.

  Если выражение оценено как "ложное", все последующие команды

до следующей команды CASE будут пропущены и не выполняются.

  Когда условие "истинно" (.Т.), выполняются все последующие

команды до появления одной из команд CASE, OTHERWISE или

ENDCASE.

  После выполнения всех команд первой фразы CASE, для которой

условие "истинно", остальные фразы CASE пропускаются и система

переходит на первую  команду, следующую за ENDCASE.

  Если все фразы CASE "ложны" и не указана фраза OTHERWISE, не

выполняется никаких команд и система переходит на первую

команду, следующую за фразой ENDCASE.

  Фраза OTHERWISE определяет выполнение альтернативной

последовательности команд, когда все фразы CASE "ложны".

+--------------------------------+

¦         Пример программы       ¦

+--------------------------------+

  В качестве примера к этому и двум предыдущим разделам приведем

продолжение программы, в котором в различных рабочих областях

откроем необходимые нам при работе базы данных вместе со своими

структурными индексными файлами, индицируя каждое действие

соотвествующим сообщением на экране. Если файл базы данных не

существует, то мыего создаем средствами SQL, попутно индексируя

необходимые нам поля.

select a&&

*Вспомогательный файл предметов

wait window nowait [Открытие базы предметов]

if !file('predmet.dbf')

  CREATE TABLE predmet (code N(4,0),name C(65),link L,;

                        chas_week N(3,0),chas_all N(4,0))

  index on code tag code

  index on name tag name

  index on link tag link

  index on chas_week tag chas_week

  index on chas_all tag chas_all

endif

use predmet index predmet

select b&&&

wait window nowait [Открытие базы аудиторий]

*Вспомогательный файл аудиторий

if !file('auditor.dbf')

  create table auditor (code n(4,0),korpus n(2,0),aud n(3,0),;

                        emkost n(3,0),special L,comment M)

  index on code tag code

  index on korpus tag korpus

  index on aud tag aud

  index on emkost tag emkost

  index on special tag special

endif

use auditor index auditor

select d&&

wait window nowait [Открытие базы потоков]

if !file("potok.dbf")

  create table potok (code n(5,0),special n(3,0),;

                     kurs n(1,0),delta n(2,0),grpcount n(2,0))

  index on code tag code

  index on special tag special

  index on kurs tag kurs

  index on delta tag delta

  index on grpcount tag grpcount

endif

use potok index potok

select c&&

wait window nowait [Открытие базы преподавателей]

*Вспомогательный файл преподавателей

if !file("prepod.dbf")

  CREATE TABLE prepod (code N(4,0), kafedra N(3,0),;

                       fio C(65), rank N(2,0), zvan N(2,0))

  index on code tag code

  index on kafedra tag kafedra

  index on fio tag fio

  index on rank tag rank

  index on zvan tag zvan

endif

use prepod index prepod

select e

* Эта область пока свободна -м.б., под что-то временное

select f&&

wait window nowait [Открытие базы факультетов]

*Вспомогательный файл факультетов - список их

if !file('fuc.dbf')

  CREATE TABLE fuc (name C(65),code N(3,0))

  index on name tag name

  index on code tag code

endif

use   fuc   index   fuc

select g&&

wait window nowait [Открытие базы специальностей]

*Вспомогательный файл специальностей - список их

if !file('spec.dbf')

  CREATE TABLE spec (name C(65),code N(3,0),fac N(3,0))

  index on name tag name

  index on code tag code

endif

use   spec  index spec

select h&&

wait window nowait [Открытие базы кафедр]

*Вспомогательный файл кафедр - список их

if !file('kuf.dbf')

  CREATE TABLE kuf (name C(65),code N(3,0))

  index on name tag name

  index on code tag code

endif

use kuf     index kuf

select i

wait window nowait [Инициализация расписания]

*Главный файл расписания

if !file('raspisan.dbf')

  CREATE TABLE raspisan (gruppa N(5,0), para N(3,0),;

                         predmet N(4,0), audit N(4,0),;

                         prepod N(4,0))

  index on gruppa tag gruppa

  index on para tag para

  index on predmet tag predmet

  index on audit tag audit

  index on prepod tag prepod

endif

use raspisan

select j


Работа с переменными и массивами в FoxPro
Работа с переменными и массивами

  Эффективное программирование возможно только при наличии

аппарата временных переменных и массивов переменных.

  В FoxPro разрешается иемть переменные тех же типов (кроме

мемо), что и поля. Однако символьные переменные допускают

гораздо большую длину - до 64 Кбайт, а числовые - и

представление с плавающей точкой. Переменным и массивам

разрешается иметь имена по тем же правилам, что и полям.

  Кроме "обычных" переменных в FoxPro введены так называемые

системные переменные, которые являются резидентными и не могут

быть уничтожены. Такие переменные имеют специальные имена,

начинающиеся с символа подчеркивания.

   Следующая команда создает переменные и присваивает им

значения:

  STORE <выр> TO <список врем. переменных> | <массив>

  Можно использовать также альтернативный, но более привычный

для программиста синтаксис:

  <врем. переменная>|<массив> = <выр>

  С помощью одной команды STORE можно занести одно и то же

<выражение> в несколько временных переменных, указанных в

<списке>.  Если уже существует временная переменная с тем же

именем, ее значение заменяется новым.

  Если временная переменная не существует, она будет создана и

инициализирована заданным <выражением>.

  Команда STORE может использоваться для инициализации всех

элементов массива временных переменных заданным <выражением>.

Перед выполнением команды STORE массив временных переменных

должен быть создан с помощью команды DIMENSION или DECLARE.

  Для определения значений временных переменных или элементов

массивов можно использовать и знак равенства (=). При этом имя

переменной или массива должно помещаться с левой стороны от

знака равенства, а записываемое значение - справа.

  Заметим, что даты могут быть записаны во временные переменные

непосредственно, если использовать круглые скобки.

 Следующая команда сохраняет временные переменные и массивы в

файле временных переменных.

  SAVE TO <файл> [ALL LIKE | EXCEPT <шаблон>]

  Если файл расположен на отличном от активного по умолчанию

диска, имя <файла> должно включать идентификатор диска и

маршрут.

  Если не  определено  иначе, расширение имени любого  файла,

создаваемого  командой   SAVE TO, принято .MEM.

  Если опция ALL LIKE/EXCEPT не используется, в файл

помещаются все   текущие   временные переменные и массивы. Фраза

ALL LIKE <шаблон> позволяет  сохранить   только  те   временные

переменныеи массивы,  имена которых  отвечают шаблону. Фраза

ALL EXEPT <шаблон> сохраняет все  временные  переменные  и

массивы, кроме отвечающих шаблону.

  Использование в <маске>  знака  вопроса (?) маскирует один

символ, а звездочки (*) - один или более символов.

  Для загрузки в память  временных  переменных и массивов,

сохраненных   в  файле,   следует использовать команду RESTORE

FROM:

 RESTORE FROM <файл> [ADDITIVE] - выбирает и активизирует

                                  временные переменные и массивы

                                  из файла временных переменных.

  При восстановлении временных переменных в программе системы

FoxPro с помощью команды RESTORE, все  текущие  временные

переменные удаляются, если не указана опция ADDITIVE.

Если команда RESTORE FROM выдана в программном файле, временные

переменные и массивы восстанавливаются как локальные (PRIVATE).

Временные переменные, восстанавливаемые через  окно  команд,

всегда имеют статус PUBLIC.

  Если число добавляемых временных переменных (в команде указана

опция ADDITIVE) превышает в сумме с текущими переменными

максимальное допустимое число временных переменных,

восстанавливается столько переменных, сколько возможно.

  Команды RESTORE FROM и SAVE TO не затрагивают системные

временные переменные.

  Активное использование временных переменных позволяет строить

эффективные и быстрые системы обработки данных. Однако

возможности сохранения в памяти компьютера значительного числа

таких переменных ограничены ее емкостью. Ввиду этого память

следует при необходимости периодически очищать от ненужных в

данный момент переменных командой

RELEASE <список переменных>

RELEASE ALL

   [LIKE <шаблон> | EXCEPT <шаблон>]

RELEASE MENUS [<список меню типа menu>

   [EXTENDED]]

RELEASE PAD <имя опции>

   OF <имя меню типа menu>

  | RELEASE PAD ALL OF <имя меню типа menu>

RELEASE POPUPS [<список меню типа popup>

        [EXTENDED]]

RELEASE BAR <вырN> OF <имя меню типа popup>

| RELEASE PAD ALL OF <имя меню типа menu>

       Команда RELEASE PAD освобождает опции в горизонтальном

     меню типа  menu.  Можни  избирательно  освободить  опцию

     как   в     пользовательских,  так  и  в  системном

     горизонтальном меню (Edit,  File,  Database  ...  ),

     включением опции в <имя опции>. Hапример, командой  RELEASE

     PAD _MEDIT  OF _MSYSMENU освобождает опцию Edit и ее

     вертикальное меню типа popup  из системного горизонтального

     меню.

       Если использовать опцию ALL, то все опции из

     определенного пользователем меню  будут освобождены.  Эта

     опция  не мсожет использоваться по отношению к системному

     меню.

RELEASE POPUPS [<список меню типа popup> [EXTENDED]]

       Команда RELEASE POPUPS  освобождает указанные

     вертикальные меню типа  popup, определенные  пользователем,

     из  памяти, а также удаляет их с экрана или из окна.

     Активное меню сначала должно быть деактивировано командой

     DEACTIVATE POPUP.

       Освобождаемые меню должны быть  перечислены в  <списке

     меню типа popup>. Если команда RELEASE POPUPS выдается без

     этого списка,   то    освобождаются   все    меню,

     определенные пользователем к этому моменту.

       Системные вертикальные меню типа popup, которые

     появляются под  системным   горизонтальным  меню,   также

     могут быть освобождены по этой команде.  Для этого в список

     включается имя такого меню (_MSYSTEM, _MFILE, _MEDIT,

     _MDATA, _MRECORD, _MPROG  or  _MWINDOW).  Для

     восстановления  принятой   по умолчанию конфигурации

     системных меню  используется команда SET SYSMENU TO

     DEFAULT.

       Hе только вертикальное меню, но и все связанные с ним

     меню и подпрограммы, подсоединенные по ON, могут быть

     освобождены, если использовать ключевое слово EXTENDED в

     команде  RELEASE  POPUP.

RELEASE BAR <вырN> OF <имя меню типа popup>

RELEASE BAR ALL OF <имя меню типа popup>

       Команда RELEASE BAR удаляет опции из вертикальных меню

     типа popup.  Можно   избирательно  удалить   одну  из

     опций  из определенного  пользователем   меню,  включением

     порядкого  номенр  этой  опции  в  команду  в виде <вырN>.

     Для удаления опции  из  системного  меню  (Edit,  File,

     Database ...  ), числовое выражение  формируется в

     соответствии с  требуемым  именем опции.

       Если в команду включить  фразу ALL, то из

     пользовательского  меню удаляются все опции.  Эту фразу

     нельзя использовать  по отношению к системным меню.

  В любой момент при отладке программ Вам может понадобиться

просмотр переменных памяти. Использование команды "?" слишком

трудоемко при просмотре большого количества переменных.

Следующая команда предъявляет сразу все нужные переменные

памяти:

 DISPLAY MEMORY [LIKE <шаблон>]

 [TO PRINTER | TO FILE <файл>]

        [NOCONSOLE]

  Команда обеспечивает вывод имени, типа, размера и статуса

каждой активной временной переменной.

  Указывается также количество активных временных переменных,

количество новых временных переменных, которые могут быть

созданы, объем памяти, используемый под временные переменные, а

также объем памяти, доступный для организации новых временных

переменных. Кроме того, выводятся данные об определенных меню

всех типов и выведенных окнах.

  Если не определено иначе в файле конфигурации, под временные

переменные используется максимум 6000 байт памяти и допускается

одновременно до 256 активных временных переменных.

  Посредством фразы LIKE <шаблон> можно вывести сведения о

конкретных временных переменных и массивах. При этом

поддерживаются специальные символы * и ?. Hапример, для вывода

информации о временных переменных, начинающихся с A, вводится

команда DISPLAY MEMORY LIKE A* .

  Если указать опцию TO PRINT, вывод информации будет  выполнен

на  экран  и  на  печатающее устройство.  Опция  TO  FILE <файл>

позволяет записать эту информацию в файле на диске.

  Опция NOCONSOLE подавляет вывод на экран или в активное окно.

  Команда

DIMENSION <массив1> (<вырN1>

        [, <вырN2>])

        [, <массив2> (<вырN3>

        [, <вырN4>])] ...

создает массив временных переменных. Размеры  массива могут быть

изменены без потери данных.

  Размеры массива можно изменить при помощи повторного

использования команды DIMENSION. Размеры массива могут быть

увеличены или уменьшены, одномерный массив может быть

преобразован в двумерный и наоборот.

  Команда DIMENSION должна содержать имя массива, размеры

которого будут  изменены.  Если  количество  элементов

увеличивается, все элементы  оригинального  массива копируются

во  вновь созданный массив.  Остальным  элементам  массива

присваивается логическое значение ложь (.F.). Если количество

элементов  в массиве уменьшается, "лишние" элементы удаляются.

  Если одномерный массив преобразовывается в двумерный,

содержимое оригинального  одномерного  массива  копируется  в

новый массив, поэлементно  заполняя  каждую   строку.  Если

двумерный массив преобразовывается   в   одномерный,

содержимое оригинального двумерного массива копируется в новый

массив в порядке следования строк. Первый элемент  первой строки

становится первым элементом одномерного  массива,  второй

элемент первой строки становится вторым элементом и т. д.

  При помощи функций ADEL() и AINS() можно удалять или вставлять

в одномерные и двумерные массивы элементы, строки и колонки.

  Команды APPEND FROM ARRAY, COPY TO ARRAY,  SCATTER и GATHER

позволяют  осуществлять  пересылку  данных  между  записями

базы данных и массивами.

  <массив1>               Имя создаваемого массива.

  <вырN1> [, <вырN2>]     Размер массива.

  Следующая команда копирует  данные  из  записи  базы  данных

в  массив или в набор временных переменных:

SCATTER [FIELDS <список полей>]

        [MEMO]

        TO <массив> | TO <массив> BLANK

        | MEMVAR | MEMVAR BLANK

  Команда  SCATTER переносит  значения  полей текущей записи

файла базы данных  в <массив>  или в  набор временных

переменных.  Этот массив или набор временных переменных может

быть отредактирован, и  содержимое  записи  может  быть

изменено откорректированными значениями. Команда GATHER

обеспечивает такое перенесение значений из массива или набора

временных переменных в поля записи базы данных.

  Значения полей записи сохраняются в элементах массива

последовательно, начиная с  первого поля, указанного  в

<списке>. Если опция  FIELDS не  указана, в  массиве

запоминаются  значения всех полей записи.

  Тип данных значения поля сохраняется и для элемента массива.

  Если число элементов массива превышает число полей записи,то

значения "лишних" элементов  после выполнения команды  SCATTER

не изменяются.

  Если указанный <массив> не объявлен или его размер  меньше

числа полей,   автоматически   создается    новый   массив

требуемой размерности.

  Если вместо специфицирования массива в команде использована

фраза MEMVAR, то создается набор временных переменных. Для

каждого поля создается одна временная переменная. Если

присутствует <список полей>, то переменные создаются только для

полей, перечисленных в списке. Каждая переменная имеет тот же

размер, тип данных и имя, что и соответствующее поле.  Следует

заметить, что для ссылки на временные переменные, которые имеют

то же имя, что и поля текущей выбранной базы данных, необходимо

перед именами этих переменных помещать квалификатор M-> или M.

Каждая временная переменная заполняется значением из

соответствующего ей поля.

  Если  вместо  ссылки  на  массив  или  фразы  MEMVAR  в

команде использована  фраза  BLANK,  создается  набор пустых

временных переменных.   Как  и  в  случае  фразы  MEMVAR, для

каждого поля создается одна  переменная.  Если присутствует

список полей, то создается набор переменных по этому списку. Тип

данных, размер и имя временных переменных  заимствуются из

соответствующих  полей. Перенос данных не выполняется.

MEMO

     По умолчанию  текстовые  поля  memo  игнорируются в команде

     SCATTER. Для  включения этих  полей в  процесс копирования

     в команде указывается ключевое слово MEMO.

       При копировании текстовых полей необходимо иметь

     достаточный запас памяти, чтобы разместить большие поля в

     массиве или во  временных  переменных.  Если  памяти  не

     хватит, то появится  сообщение "Insufficient memory"

     (недостаточно памяти). Если текстовое поле слишком велико,

     чтобы уместиться в памяти, то оно не копируется, не

     копируются и все следующие за ним  текстовые  поля.  Если

     текстовое  поле  не  копируется, то соответствующий ему

     элемент массива или временная переменная устанавливаются в

     логическую "ложь" (.F.).

  Действие команды GATHER обратно действию команды SCATTER:

GATHER FROM <массив> | MEMVAR

        [FIELDS <список имен полей>]

        [MEMO]

  Команда помещает содержимое временных переменных или элементов

массива в поля базы данных.

MEMO - позволяет обрабатывать при помощи команды GATHER поля

       типа  memo. Если  фраза  MEMO  отсутствует,  поля  типа

       memo при копировании данных по команде  GATHER из массива

       или временных переменных  в текущую запись игнорируются.

PUBLIC <список временных переменных>

или

PUBLIC <массив1> (<вырN1>[,<вырN2>])

[,<массивN> (<вырN>[,<вырN>]),...]

или

PUBLIC ARRAY <массив1> (<вырN1>[,<вырN2>])

[,<массивN> (<вырN>[,<вырN>]),...]

  Команда производит определение глобальных временных переменных

или массивов.

  Временные переменные и массивы, созданные с помощью команды

PUBLIC, инициализируются значением (.F.).  Глобальные временные

переменные системы FOX позволяют командам и функциям, не

поддерживаемым системой FoxPro, тем не менее присутствовать в

командном файле FoxPro.

  Глобальные (PUBLIC) переменные должны быть объявлены таковыми

до того, как им присвоены значения. Значения глобальных

временных переменных могут быть изменены любой использующей их

программой.

  Если переменной сначала присвоить значение, а потом объявить

ее глобальной (PUBLIC), возникает ошибка.

PRIVATE <список временных переменных>

или

PRIVATE ALL [LIKE | EXCEPT <шаблон>]

обеспечивает объявление в программах локальных временных

переменных или массивов.

  Команда PRIVATE позволяет повторно использовать имена

временных переменных, созданных в вызывающей программе или ранее

объявленных глобальными (PUBLIC), без разрушения их значений.

  При выполнении программы, содержащей локальные временные

переменные, все переменные с такими же именами будут "скрыты" от

пользователя до завершения программы.

  При завершении программы значения временных переменных,

"скрытых" командой PRIVATE, восстанавливаются.

  При описании локальных временных переменных может быть указан

их список (1-я форма команды) или использован <шаблон> (2-я

форма команды).

  Вариант PRIVATE  ALL LIKE  <шаблон> позволяет объявить

локальными временные  переменные и массивы, имена которых

соответствуют шаблону. Вариант PRIVATE ALL EXCEPT <шаблон>

объявляет локальными  все  переменные  и массивы, имена которых

не соответствуют шаблону. Шаблон может содержать специальные

символы * и ?.

  В отличие от команды PUBLIC команда PRIVATE не создает

временных переменных или массивов, перечисленных в команде. Они

сначала должны быть созданы командами STORE, DECLARE или

DIMENSION.

  Применение команды PRIVATE через окно команд не влияет на

временные переменные или массивы.

  Следующая пара команд осуществляет взаимодействие с двумерными

массивами :

 COPY TO ARRAY <массив> [FIELDS <спис.пол>]

 [<сфера>] [FOR <вырL1>] [WHILE <вырL2>]

 [NOOPTIMIZE]                               - обеспечивает

                                              запись данных из

                                              записей базы

                                              данных в массив.

  Команда COPY TO ARRAY перемещает данные из текущей выбранной

базы данных в массив временных переменных.  Команды COPY TO

ARRAY и SCATTER подобны, разница состоит в том, что COPY TO

ARRAY перемещает в массив данные из множества записей, тогда

как SCATTER - записывает  данные  только  одной  записи   в

массив или в  набор временных переменных.  До выполнения команды

COPY TO ARRAY необходимо определить массив посредством команд

DIMENSION, DECLARE или PUBLIC. Команда SCATTER сама создает

массив или набор временных  переменных, если они не были

определены ранее.

  В случае  одномерного массива  первое поле  записи

записывается в первый элемент массива, второе поле  - во второй

элемент, и т.д.  Если  в  одномерном  массиве  больше

элементов, чем полей в базе данных, лишние элементы не

изменяются. Если же в массиве меньше элементов, чем  полей  в

базе  данных,  не помещающиеся поля игноригуются.

  Двумерный массив имеет формат матрицы со строками и столбцами.

С помощью команды COPY TO ARRAY запись записывается в строку

массива, причем каждое поле структуру базы данных - в свой

столбец. Для каждой записи первое поле записывается в первый

столбец массива, второе - во второй и т.д. Если в массиве больше

столбцов, чем полей в базе данных, излишние столбцы не меняются.

Если же колонок меньше, чем полей, избыточные поля не

записываются в массив.

  Команда COPY TO ARRAY игнорирует текстовые поля.

  Перезапись прекращается, когда заканчиваются строки массива,

или когда достигается конец базы данных.

  Команда COPY TO ARRAY поддерживает опцию FIELDS <список

полей>.  Если она присутствует, то только перечисленные поля

переписываются в массив. В противном случае переписываются все

поля, если в массиве достаточно элементов.

          <сфера>   Если указана фраза <сфера>, то

                    переписываются только записи, входящие в

                    заданный диапазон.  в  противном  случае

                    перезапись начинается с начала базы данных.

           FOR, WHILE позволяют выделить для копирования только

                    те записи, которые удовлетворяют условию

                    <вырL1>.  Команда COPY TO ARRAY выполняется

                    пока не становится ложным условие <вырL2>.

  Данные из массива могут быть записаны в базу  данных

посредством команды APPEND FROM ARRAY:

APPEND FROM ARRAY <массив>

        [FOR <вырL>]

        [FIELDS <список полей>]

<массив>        Имя массива.

<вырL>          Условие добавления записей из массива.

<список полей>  Список полей, в которые будет осуществляться

                запись из  массива. Поля типа Memo будут

                игнорироваться.

  Приведенная ниже таблица содержит значения, которые будут

присваиваться полям по умолчанию, если база данных содержит

больше полей, чем количество элементов массива.

Тип поля       Значение по умолчанию

------------------------------------

Символьный           Пробелы

Числовой             0

Дата                 Пусто

Логический           Ложь (.F.)

Приведенная  ниже  таблица  иллюстрирует,  каким  образом   будет

осуществляться запись при несовпадении типов данных.

Тип элемента    C       N       D       L

-----------------------------------------

Символьный      X

Числовой        X       X               X

Дата            X       X       X

Логический      X                       X

  Функция ASUBSCRIPT(<массив>, <вырN1>, <вырN2>) возвращает

номер строки или  столбца  элемента  массива по его номеру.

<массив>   Имя одно- или двухмерного массива.

<вырN1>    Hомер элемента массива.

<вырN2> Если 1, возврат номера строки.

        Если 2, возврат номера столбца.

  Функция AELEMENT(<массив>, <вырN1> [, <вырN2>]) возвращает

элемент массива с указанным номером.

  Для некоторых функций FoxPro (ACOPY(), ASCAN(), ASORT() ...),

работающих с двухмерными массивами, необходимо, чтоб обращение к

элементу массива осуществлялось по его номеру.  Функция

AELEMENT() облегчает преобразование индекса элемента в его номер

для таких функций. Соответствующий номер столбца и строки для

элемента массива может быть получен с помощью функции

ASUBSCRIPT().

<массив>  Имя одно- или двухмерного массива.

<вырN1>   Строка.

<вырN2>   Столбец.

  AFIELDS(<массив>) осуществляет занесение информации о

структуре базы данных в массив; возвращает количество полей в

записи.

<массив>  Имя  массива. Если массив, указанный в AFIELDS()

          не существует, он будет cоздан. Если массив

          существует, но его размерность не достаточна, он будет

          расширен; если массив больше, чем необходимо, он будет

          сокращен.

    Информация в массиве и типы данных

Строка #        Информация              Тип

-----------------------------------

1               Имя поля             Символьный

2               Тип поля             Символьный

                C = Символьный

                D = Дата

                L = Логический

                M = Memo

                N = Числовой

                F = Плавающий

3               Длина поля           Числовой

4               Позиция десятичной

                точки                Числовой

  AINS(<массив>, <вырN> [,2]) производит вставку неопределенного

элемента в массив.

  Функция вставляет элемент в одномерный массив,  либо строку

или столбец в двумерный массив. Вставка элемента массива,

строки или столбца в массив не изменяет размерность массива -

сдвинутые элементы, строки или столбцы сдвигаются в направлении

конца массива, а последний элемент, строка или столбец будут

потеряны. Вставленный  элемент  массива  получает  значение

логической лжи (.F.).

  После удачной вставки элемента, строки или столбца

возвращается числовое значение 1.

<массив>

        Имя массива, в который осуществляется вставка.

<вырN>

        1-мерный массив         номер элемента

        2-мерный массив         номер элемента

2

        Указывает, что <вырN> - столбец.

  ADEL(<массив>, <вырN> [,2]) удаляет элемент, строку или

столбец из массива.

  Данная функция удаляет элемент одномерного массива и строку

или столбец из  двухмерного массива. Удаление элемента, строки

или столбца из массива не изменяет размеры массива - элементы,

строки или столбцы перемещаются  в  направлениии  начала

массива, а последний элемент, строка или столбец в массиве

получают значение логической лжи (.F.).

  После удачного удаления элемента, строки или столбца

возвращается значение 1.

<массив>

        Имя массива.

<вырN>

        1-мерный массив     Hомер элемента

        2-мерный массив     Hомер строки

2

        Указывает, что <вырN> - номер столбца.

ADIR(<массив>  [, <вырC1> [, <вырC2>]]) - заполнение массива

                                          информацией о файлах.

                                          Возврат - числовое

                                          значение.

  Возвращает количество файлов, которые соответствуют маске и

заполняет информацией о файлах - имя, размер, дата, время,

атрибуты DOS - в массив.

<массив>

        Массив, в который заполняется информация о файлах.

<вырC1>

        Маска.

<вырC2>

        Расширяет поиск для включения дополнительных файлов.

        <вырC2> может быть следующим:

        D для поддиректориев

        H для скрытых файлов

        S для системных файлов

        V для имени тома

  Представленная таблица указывает структуру заполнямого

массива:

Столбец  Информация     Дата Тип

-------------------------------

1        Имя файла           Символьный

2        Размер файла        Числовой

3        Дата файла          Дата

4        Время файла         Символьный

5        Аттрибуты файла     Символьный

  Последний столбец массива содержит аттрибуты DOS указанных

файлов. Данные аттрибуты представляют собой букву или буквы.

Представленная таблица содержит значение выдаваемых букв.

Буква          Аттрибуты

-------------------------------

A              Архив (Чтение & Запись)

H              Скрытый

R              Только чтение

S              Системный

D              Директорий

  Если массив, указанный в ADIR() не существует, он будет

создан.  Если массив существует, но недостаточного размера, он

будет расширен; если размер массива больше необходимого, он

будет уменьшен.

ASCAN(<массив>, <выр> [, <вырN1>

        [, <вырN2>]])

  Функция осуществляет поиск в массиве по условию, заданном в

общем выражении <выр>. Если элемент массива найден, функция

выдаст номер этого элемента; в противном случае - будет выдан 0.

  Критерий успешного поиска по совпадению значения определяется

установкой (ON или OFF) в SET EXACT. Если EXACT установлен с ON,

элемент должен совпадать посимвольно с условием поиска и иметь

одинаковую длину. Если EXACT установлен с параметром OFF,

элемент и условие поиска должны совпадать частично, результат

сравнения в этом случае будет считаться успешным. Более полную

информацию о критерии совпадения, можно прочитать в информации о

команде SET EXACT.

  Функцию AELEMENT() можно использовать для получения номера

элемента для двухмерного массива по номеру строки и столбца.

<массив>

        Имя массива временных переменных, в котором осуществить поиск.

<выр>

        Выражение критерия поиска.

<вырN1>

        Hомер элемента, с которого начать поиск.

<вырN2>

        Hомер элемента, которым закончить поиск.

  ASORT(<массив> [, <вырN1> [, <вырN2> [, <вырN3>]]])

  Сортировка элементов массива в возрастающем или убывающем

порядке.  Возврат - числовое выражение.

<массив>

        Имя массива временных переменных для обработки.

<вырN1>

        При использовании без параметров:  элемент, с которого

        начать сортировку; При использовании <вырN2>: строка, с

        которой начать сортировку.

<вырN2>

        Hомер элемента или строки, с которой начать сортировку.

<вырN3>

        Порядок сортировки:

        0  - возрастание

        1  - убывание

        После завершения сортировки возвращается 1; при

        неблагоприятном исходе возвращается -1.

          Hеобходимо помнить, что элемент, с которого начинается

        сортировка в двухмерном массиве может быть задан двумя

        способами. В первом, указывается позиция элемента

        массива; в другом - номер элемента массива. Для этой

        функции, а также для других функций, работающих с

        двухмерными массивами, необходимо указывать номер

        элемента (в ASORT() числовые выражения <вырN1> и

        <вырN2>). Функцию AELEMENT() необходимо использовать для

        получения действительного номера элемента двухмерного массива.

+---------------------------------+

¦         Пример программы        ¦

+---------------------------------+

  Следующий фрагмент программы, предлагаемый Вашему вниманию,

объявляет несколько глобальных массивов и переменных, проводя

заполнение части их:

public dimension list_pred(50,2),WasChanged(10),;

                 tempos(10),list_spec(50,2),list_kuf(50,2),;

                 list_prep(50,2),days_arr(5),week_arr(2),;

                 list_fac(50,2),list_aud(50,2),;

                 list_pot(50,2),marazm(30,3)

public curaud,curprep,curkuf,curfac,curspec,curpot,;

       curpred,curgrp,curpg,first,pottxt,factxt,;

       spectxt,predtxt,preptxt,audtxt,kuftxt

  days_arr(1)=[Понедельник]

  days_arr(2)=[Вторник    ]

  days_arr(3)=[Среда      ]

  days_arr(4)=[Четверг    ]

  days_arr(5)=[Пятница    ]

  week_arr(1)='Числитель  '

  week_arr(2)='Знаменатель'

IsQuitFlag=.f.

first=.t.

STORE 0 TO curaud,curgrp,curprep,curfac,curspec,curpot,curpred

curkuf=0

curpg=0

      tempos(1)=[Первая]

      tempos(2)=[Вторая]

      tempos(3)='Третья'

      tempos(4)=[Четвертая]

      tempos(5)=[Пятая]

      tempos(6)=[Шестая]

      tempos(7)=[Седьмая]

      tempos(8)=[Восьмая]

      tempos(9)=[Девятая]

  Рассмотрим подробнее некоторые соглашения об именах.

  Если в начале массива стоит слово list_, это означает, что

данный массив будет использоаться для хранения некоторого

списка. Если переменная начинается с cur - то в ней будет

содержаться некоторый выбор (обычно код) или 0 в случае его

отсутствия. Переменная, заканчивающаяся на txt, содержит

строку, соответствующую этому коду.

  Конкретный вид данных определяется фразами

  aud  - аудитория

  prep - преподаватель

  kuf  - кафедра

  fac  - факультет

  spec - специальность

  pot  - поток

  pred - предмет

  grp  - группа

  pg   - подгруппа

  Кроме того, есть переменные и массивы, не укладывающиеся в эту

схему :

  WasChanged(10) - каждый элемент этого массива отражает

                   состояние базы данных в рабочей области с

                   соответствующим номером. Если в базе данных в

                   области <number> что-то изменяется, в элемент

                   WasChanged(<number>)=.T. . В конце программы

                   эта информация используется для определения

                   того, какую базу данных необходимо упаковать.

  tempos(10)     - как можно заметить из действий,

                   производящихся над элементами этого массива,

                   в нем храняться числительные от 1 до 9 -

                   номера групп в символьном формате. Они

                   понадобяться для "красивого вывода" номера

                   группы.

  days_arr(5)    - содержит названия учебных дней недели, а

  week_arr(2)    - названия самих недель.

  marazm(30,3)   - массив с таким "оригинальным" именем является

                   одним из самых важных. Как Вы, быть может,

                   помните, в расписании основными данными у нас

                   являются предмет, преподаватель и аудитория -

                   3 ячейки на одну пару. Максимальное число пар

                   в день - 5, подгрупп - 2, 3х5х2==30 - именно

                   таким числом полей мы можем представить на

                   экране данные по расписанию для одной группы

                   на один день, причем элементы с номерами вида

                   1+3*n отвечают за данные по предмету, 2+3*n -

                   по преподавателю, и 3+3*n - по аудитории.

                   Первая ячейка каждой строки массива содержит

                   код, вторая - соответствующий ему текст

                   (списки, кстати, организованы наоборот -

                   сначала текст, а затем уже код), третья имеет

                   смысл только для поля предмета и содержит

                   уровень дробления потока:

                   1 - поток,

                   2 - группа,

                   3 - подгруппа.

  first          - это всего лишь флаг первого вхождения в окно

                   расписания. Он необходим для того, чтобы

                   отличит инициализационные действия от

                   многократно повторяющихся.

  IsQuitFlag     - определяет условие выхода из цикла обработки

                   сообщений от меню. VALID-функция для этого

                   цикла возвращает значение этого флага. Если

                   он очищен, цикл продолжается, при взводе -

                   завершается.


Работа с окнами в FoxPro
Работа с окнами

  Очень важным для СУБД является возможность оаботы с окнами.

Каждое окно - это, по существу, автономный экран системы.

Одновременно может быть доступно несколько не мешающих друг

другу окон, что позволяет создавть удобный многооконный

пользовательский интерфейс.

  Окна можно открывать, закрывать, изменять размеры и положение

с помощью команд, клавиатуры и мыши. Окна могут существовать

отдельно и быть вложенными друг в друга.

  Различают три размера окон - нормальный, минимальный и

максимальный.

  Нормальный размер окна определяется при описании окна командой

DEFINE WINDOW и может быть изменен командой ZOOM WINDOW.

  Максимальное окно занимает весь экран или всю площадь другого

"старшего" окна, в котором оно находится.

  Минимальное окно стягивается в одну строку, в которой виден

только его заголовок (если есть) или имя окна.

  Вид окна определяется опциями команды описания окна DEFINE

WINDOW, которая будет рассмотрена ниже. По умолчанию

(необязательных опций нет) окно обрамлено одинарной рамкой и не

имеет никаких средств управления.

  На верхней ограничивающей линии окна отображается <заголовок

окна>, который является именем базы данных или любой строкой,

указанной в качестве заголовка (TITLE) в команде описания окна.

  Для перемещения всего окна нужно поставить маркер мыши на

верхнюю горизонтальную границу окна, содержащую заголовок, и

"отбуксироввать" его на новое место. С клавиатуры режим

перемещения окна устанавливается нажатием клавиш Ctrl+F7, при

этом рамка мигает (FLOAT).

  Здесь же можно осуществить переход к минимальному окну, нажав

два раза на кнопку мыши. Если перед этим еще нажать и удерживать

клавишу Shift, то минимальное окно переместиться в правый нижний

угол экрана (MINIMIZE). Такой механизм позволяет пользователю

организовать в одном месте экрана компактный "склад" всех нужных

окон и при необходимости быстро их открывать.

  С клавиатуры переход к минимальному окну осуществляется

нажатием клавиш Ctrl+F9 (с перемещением в нижний угол клавишами

Shift+Ctrl+F9). Возврат из минимального окна в исходное

состояние выполняется теми же действиями (можно без клавиши

Shift).

  На экране может быть открыто сразу несколько окон. Переход

между ними может быть выполнен последовательно клавишами

Ctrl+F10 или произвольно мышью.

  Практически все указанные действия могут быть выполнены

программно с помощью команд управления окнами.

  Каждое окно прикладной системы должно быть предварительно

определено, а затем при необходимости активировано. Ненужные

окна отключаются или удаляются. В процессе работы с окнами

использутся следующие команды:

DEFINE WINDOW <имя окна1>

    FROM <ряд1>, <кол1>

    TO <ряд2>, <кол2>

        [IN [WINDOW] <имя окна2>

        | IN SCREEN]

        [FOOTER <вырC1>]

        [TITLE <вырC2>]

        [DOUBLE | PANEL | NONE

        | SYSTEM | <символы рамки]

        [CLOSE | NOCLOSE]

        [FLOAT | NOFLOAT]

        [GROW | NOGROW]

        [MINIMIZE]

        [SHADOW]

        [ZOOM | NOZOOM]

        [FILL <вырC3>]

        [COLOR SCHEME <вырN>

        | COLOR <список пар цветов>]

  Команда DEFINE WINDOW создает окно пользователя и определяет

его атрибуты. После определения окон их можно высветить на экран

при помощи  команда  ACTIVATE  WINDOW  или  SHOW  WINDOW.

Количество определяемых при помощи команды DEFINE окон

ограничивается только размером доступной памяти.

  Определения  окон   хранятся  в   памяти,  а   список  их

имен высвечивается в  нижней части  вертикального меню  Window

(Окно). Окно, определенное первым, стоит в списке на первом

месте, за ним следуют имена  остальных определенных  окон.

Текущее окно вывода помечается символом  маленького ромба.  При

отсутствии активного окна вывод направляется на экран.

Активизированное окно  остается на экране,  пока не  будет

удалено  с него  по команде DEACTIVATE WINDOW или HIDE WINDOW.

Эти две команды используютсядля удаления окна с экрана, но не из

памяти. Окно можно восстановить на экране при помощи команд

ACTIVATE WINDOW или SHOW WINDOW.

  Для удаления окон одновременно с экрана и из памяти

используются команды CLEAR WINDOWS или  RELEASE WINDOWS. Для

работы  с окнами, удаленными из памяти, их необходимо снова

определить.

<имя окна1>

 Имя создаваемого окна. Имя  окна может содержать до  10

 символов, начинаться с буквы или  символа подчеркивания и

 содержать  буквы, цифры  и  символы  подчеркивания.

FROM <ряд1>, <кол1> TO <ряд2>, <кол2>

 Определяют координаты левого верхнего и правого нижнего углов

 окна на экране. Координаты окна могут выходить за пределы

 экрана. Окно может быть также расположено внутри другого окна.

IN [WINDOW] <имя окна2>

 Определяет  окно,  порождаемое   другим  окном.  Например,

 если существует активное  окно, порожденное  другим окном,  его

 нельзя переместить за пределы порождающего окна, а также его

 размеры  не могут  превышать  размеры  порождающего  окна.  При

 перемещении порождающего окна порождаемое окно перемещается

 вместе с ним.

   При  определении  окна,  порождаемого  другим  окном,

 координаты, заданные  в  команде  DEFINE  WINDOW  относятся  не

 к экрану, а к порождающему окну.

IN SCREEN

 Вывод окна на экран, а не в окно.

FOOTER

 Определение подвала,  который центрируется  и выводится  в

 нижней рамке  окна.  Если  длина  подвала  больше  ширины

 окна,  подвал обрезается.

TITLE <вырC2>

 Определяет  заголовок.  Заголовок  центрируется  и  выводится

 в верхней рамке окна. Если  длина заголовка больше ширины

 окна, он обрезается.

DOUBLE | PANEL | NONE | SYSTEM | <символы рамки>

 Определяет рамку окна. Использование ключевого слова SYSTEM

 позволяет создать окно, внешний вид которого аналогичен

 системным окнам.  Если  при  определении   окна  наряду  с

 фразой   SYSTEM используются фразы  CLOSE, GROW  или ZOOM,

 созданное окно будет иметь соответсвующие атрибуты (удаление,

 изменение размера, раскрытие). Использование  фразы SYSTEM  не

 требует задания типа рамки (DOUBLE - двойной, NONE - рамки

 нет, определенной пользователем строки символов рамки и т.п.).

   При помощи <символов  рамки> можно определить  собственную

 рамку.

CLOSE | NOCLOSE

 Позволяет или запрещает закрытие окна.

FLOAT | NOFLOAT

 Позволяет или запрещает перемещение окна.

GROW | NOGROW

 Позволяет или запрещает изменение размеров окна.

MINIMIZE

 Позволяет или запрещает уменьшение окна до минимального

 размера. Размер окна может быть уменьшен до минимального и окно

 помещено в правый нижний угол экрана  нажатием клавиши Shift

 после  двойного нажатия кнопки "мыши", когда  курсор находится

 в верхней границе окна.

SHADOW

 Создание тени окна.

ZOOM | NOZOOM

 Позволяет или запрещает возможность раскрытия окна.

FILL <вырC3>

 Заполняет фон окна заданными символами.

COLOR SCHEME <вырN> | COLOR <список пар цветов>

 Изменяет установки  цветов, принятые  по умолчанию.  По

 умолчанию используются цвета из цветовой схемы 1.

ACTIVATE WINDOW [[<имя1>]

 [,<имя2>][, ...]] | ALL

 [BOTTOM | TOP | SAME][NOSHOW]

  Данная команда выводит и активизирует предварительно

определенное окно или окна. Активизация окна приводит к

направлению всего экранного вывода в это окно.

  Окна создаются и определяются пользователем посредством

команды DEFINE  WINDOW.  Когда  окна  определены,  их  имена

появляются в нижней  части  вертикального  меню   Window

(Окна).  Первое из определявшихся  окон  появится  в  верху

списка, за ним будут следовать имена последующих правильно

определенных окон.  Текущее окно, в  которое осуществляется

вывод, отмечается  в этом списке ромбиком.

  Hа экране может располагаться  одновременно несколько окон, но

вывод  будет  направляться  только  в последнее,

активизированное командой ACTIVATE WINDOW. Когда на экране

присутствует  несколько окон,  деактивизация  текущего  окна

приводит к удалению его с экрана, а последующий вывод будет

направляться в активизированное до него окно, а при отсутствии

такового - на экран.

  Все активизированные окна остаются на экране до тех пор, пока

для их удаления  не использована  команда DEACTIVATE  WINDOW или

HIDE WINDOW. Использование любой  из этих двух  команд удаляет

окно  с экрана,  но  не  из  памяти.  Вновь  вывести  окно на эк

ран можно командой ACTIVATE WINDOW или SHOW WINDOW. Для удаления

окна как с экрана, так и  из памяти, используется  команда CLEAR

WINDOW  или RELEASE WINDOW. Окна,  удаленные  из памяти,  должны

определяться еще раз перед их выводом на экран.

     ALL            Если использовано небязательное ключевое

                    слово ALL, то будут активизированы все

                    определенные окна. Окна будут выводиться в

                    том порядке, в каком они были определены,

                    последнее определенное окно становится

                    активным окном для вывода.

    BOTTOM          Можно также   специфицировать,  где   окно

                    TOP, должно активизироваться, относительно

                    других  SAME уже   активизированных окон.

                    Опции  BOTTOM и TOP используются для  вывода

                    окон в  обход их стековой

                    последовательности.   Опция   SAME влияет

                    на  расположение окон,  которые были

                    выведены на экран, а позднее скрыты.

  Опция BOTTOM включается в команду, для активизации окна и

расположения его позади всех других окон. Опция TOP

активизирует окна и помещает их перед всеми другими окнами.

  Опция SAME влияет только на те окна, которые были

предварительно выведены командой ACTIVATE WINDOW или SHOW

WINDOW, а затем скрыты командой DEACTIVATE WINDOW или HIDE

WINDOW. Применение опции SAME в описываеиой команде позволяет

вывести и активизировать окно в той  же  позиции  стековой

последовательности  окон,  какую  оно занимало перед скрытием.

     NOSHOW         Активизация окна с  использованием   опции

                    NOSHOW направляет вывод в указанное окно без

                    изменения статуса этого окна. Если окно

                    скрыто, то оно остается скрытым, и вывод

                    направляется в него. Если окно не

                    активизировалось, то вывод направляется в

                    окно, но не выводится на экран.

SHOW WINDOW <имя1>

        [, <имя2> ... ] | ALL

        [IN [WINDOW] <имяN>

        | IN SCREEN]

        [REFRESH]

        [TOP | BOTTOM | SAME]

        [SAVE]

  SHOW WINDOW управляет выдачей и порядком размещения окон на

экране. Если окно или  окна определены,  но не активизированы,

команда SHOW  WINDOW выводит  их на  экран. Если окно  или окна

скрыты, команда SHOW WINDOW размещает их на экране.

  Можно также выдавать системные  окна, такие  как командное

окно, Калькулятор, Календарь/Дневник (Filer, Calculator) и др.

Desk accessories (Канцелярские принадлежности) должны быть

активизированы раньше их выдачи. Если окно или окна выведены на

экран, команда SHOW WINDOW позволяет изменить последовательность

появления окон на экране.

  Командой SHOW WINDOW нельзя указать, куда направлять вывод.

Hазначение окна вывода осуществляется командой ACTIVATE WINDOW.

<имя1> [,<имя2>...]

        Одно или несколько окон выдаваемых на экран.

ALL

        Вывод всех окон.

IN [WINDOW] <имяN>

         Если указана фраза IN WINDOW <имяN>, будет открыто

         окно внутри  основного  окна. Hовому  окну  не

         передаются характеристики  окна, в  котором  оно

         размещено. Окно, открытое внутри основного окна не

         может быть перемещено за пределы основного окна.  При

         перемещении основного окна, окно, открытое внутри будет

         перемещаться вместе с ним. Основное окно  <имяN> должно

         быть  определно первым по команде DEFINE WINDOW.

IN SCREEN

        С помощью данного параметра можно точно указать

        расположение окна на экране, не размещая его внутри

        другого окна. Окна выдаются на экран по умолчанию.

REFRESH

        REFRESH рекомендуется использовать для  повторной выдачи

        окна Browse. Это приходится  выполнять при работе в

        сети для того,  чтобы   убедится,  что  каждый

        пользователь просматривает последнюю версию базы данных.

TOP

        Размещение окна поверх всех открытых окон.

BOTTOM

        Размещение окна под всеми открытыми окнами.

SAME

        Фраза имеет смысл, если окно ранее  было выведено  или

        активизировано,  а  затем  удалено с экрана командой

        DEACTIVATE  WINDOW. В команде SHOW WINDOW фразой SAME

        размещаются  окна, выдаваемые  из  стэка  окон в ту же

        позицию, откуда они были убраны.

SAVE

         Сохраняет содержимое окна на  экране или в другом окне

         после того, как оно было скрыто. Обычно, окна

         удаляются с экрана  после их скрытия. Содержимое  окна

         может быть очищено  командой  CLEAR. Текущий файл

         выдачи сохранен быть не может.

DEACTIVATE WINDOW <имя1> [,<имя2>] [,...] | ALL

  С помощью данной команды активное окно или набор активных окон

деактивизируются и удаляются с  экрана. Эти окна  не удаляются

из  памяти  и   могут  быть  выведены   снова командой ACTIVATE

WINDOW или SHOW WINDOW.

  Одновременно на экране может находиться несколько окон, но

вывод производится только в одно - активизированное самым

последним. При наличии на экране нескольких окон деактивизация

активного окна приводит к удалению его с экрана и активизации с

направлением в него вывода предыдущего активизированного окна.

Если больше окон нет, вывод направляется на весь экран.

  Можно деактивизировать единственное окно  или несколько окон

посредством указания в команде одного имени окна или списка

имен. При использовании команды DEACTIVATE WINDOW ALL

деактивизируются все окна.

  Активные окна можно удалить не только  с экрана, но  и из

памяти командами CLEAR WINDOWS или RELEASE WINDOWS.

 MOVE WINDOW <имя> TO <ряд>,<кол> | BY <вырN1>,<вырN2>

  Команда позволяет перемещать ранее определенное окно в другую

позицию экрана или другого окна. Можно определить как абсолютные

координаты для новой позиции, так и относительные - по отношению

к текущей позиции. Можно переместить уже определенное окно,

которое не обязательно должно быть активизировано или выведено

командами ACTIVATE или SHOW.

  Для перемещения в конкретную позицию на экране или внутри

другого окна используется вариант  TO  <ряд>,<кол>.  Для

относительного перемещения используется вариант BY.

 SAVE WINDOW <список окон> | ALL TO <файл>

  Команда сохраняет текущие определения окон на дисковом файле.

Позднее эти определения могут быть восстановлены командой

RESTORE WINDOW.

  Можно сохранить определения только  требуемых окон,    если

привести    в команде   список их    имен,    или   сохранить

определения всех текущих окон,  если включить фразу  ALL. При

сохранении  фиксируется  текущий  статус (скрытое, активное и

т.д.).

  Если в имени файла  не указано расширение,  по умолчанию

подразумевается  расширение .WIN  . Если же  указать расширение,

отличающееся от .WIN,  то  это  иное  расширение   необходимо

указывать и в команде RESTORE WINDOW.

RESTORE WINDOW <список окон> | ALL FROM <файл>

  Команда восстанавливает в памяти определения окон,  ранее

сохраненные  в  дисковом  файле окон. Этот  файл содержит  окна,

определенные командой DEFINE WINDOW и сохраненные командой SAVE

WINDOW. Этот файл имеет расширение .WIN.

  Можно восстановить в памяти определения только требуемых

окон,   если  привести   в команде  список  их  имен,  или

восстановить определения  всех  окон,  сохраненных  в этом

файле, если включить фразу ALL. Если в памяти имеются

определения окон с такими же именами, что и  у восстанавливаемых

из файла, то они перекрываются восстанавливаемыми. При

восстановлении окна имеют тот статус (скрытое, активное

и т.д.), что был в  момент сохранения.

ZOOM WINDOW <имя окна>

        MIN [AUTO] | MAX | NORM

        [AT <строка1, столбец1>

        | FROM <строка1, столбец1>

        [SIZE <строка2, столбец2>

        | TO <строка2, столбец2>]]

  Команда позволяет изменять размер окна System или окон,

определенных пользователем. Окна можно сжимать до минимального

размера, оставляя видимым только имя окна или заголовок, они

могут быть увеличеными до размера полного экрана или изменены в

размере этих двух величин. Окна могут быть раскрыты на весь

экран с минимального размера и наоборот. Можно также указывать

место на экране или в окне, где буде расположено окно с новыми

размерами.

  Примеры с окнами System можно продемонстрировать для окон

Command, Browse, редактирования текстов и др. (размер окон

Filer, Calculator и Puzzle не увеличиваются). Окна, определенные

пользователем, создаются с помощью команды DEFINE WINDOW.

  FoxPro позволяет устанавливать между  окнами связи  типа

родитель-ребенок.  Окна (дети) могут быть размещены в других

окнах (родителях) по фразе IN [WINDOW] команды DEFINE и ACTIVATE

WINDOW. Окно Browse, окна редактирования текста, экраны отчетов

и экраны формата метки, Filer и окна помощи могут быть также

размещены внутри окна (родителя).  Окно (ребенок) должно

размещаться в окне-родителе. При использовании ZOOM WINDOW для

изменения размера окна-ребенка, максимальный размер не может

превышать размер окна-родителя.

<имя окна>

        Окно, размер которого необходимо изменить.

MIN

        Изменение размера окна до минимального размера, остается

        место для имени окна или заголовка на экране или окне.

          Изменение размера окна до минимального размера делает

        возможным одновременное размещения на экране или окне

        много окон, оставляя достаточно много места для вывода.

        Hапример, можно разместить канцелярские принадлежности

        FoxPro (Calculator, Calendar/Diary, ASCII Chart, и др.)

        в их минимальном размере вдоль верхней строки экрана для

        легкого к ним доступа и использования.

          Все окна System могут быть сокращены до минимального

        размера. Окна System должны быть открыты на экране или в

        окне перед тем, как их размеры будут изменять. Могут

        быть уменьшены только те окна, которые определены

        пользователем, которые созданы с фразой MINIMIZE в

        описании. Окна, определенные пользователем, могут быть

        минимизированы после их описания - но не обязательно,

        чтоб они было активизированы.

          Активное окно System или описанное пользователем может

        быть интерактивно минимизировано двойным нажатием

        клавиши манипулятора "мышь" в верхней границе окна,

        нажатием клавиш Ctrl+F9 или выбором опции Zoom

        вертикального меню Window.  Если также была нажата

        клавиша Shift при двойном нажатии манипулятора, или

        нажаты клавиши Shift+Ctrl+F9, или выбрана опция Zoom

        при нажатой клавише Shift, окно будет минимизировано и

        установлено в нижнем правом углу экрана. Для

        восстановления окна в прежних размерах, необходимо

        повторить любой шаг, описанный выше (с или без нажатия

        клавиши Shift).

MAX

        Увеличение размера окна или экрана до максимального

        размера. Если это окно-ребенок, оно будет размещено в

        окне-родителе и будет увеличено до максимального

        размера,  увеличивая при этом окно-родитель.  Если

        включена любая дополнительная фраза команды ZOOM WINDOW

        (AT, SIZE, TO или FROM), она будет проигнорирована.

          Окна, описанные пользователем, созданные с помощью

        ZOOM, могут быть раскрыты до максимального размера.

        Активное окно System или окно, определенное

        пользователем, может быть интерактивно  максимизировано

        двойным нажатием клавиши манипулятора "мышь" в правом

        верхнем углу окна, нажатием клавиш  Ctrl+F10 или выбором

        опцию Zoom  вертикального меню  Window. Для

        восстановления окна в прежних размерах, необходимо

        повторить любой шаг, описанный выше.

NORM

          Возвращение окна в прежних размерах после минимизации

        или максимизации размероа; NORM может также

        использоваться для перемещения или изменения размеров

        окон. Для восстановления расположения  и размеров окна

        после их минимизации или максимизации необходимо

        использовать ZOOM WINDOW NORM <имя окна>.

AUTO

        Если AUTO используется без MIN, размер окна будет

        минимизирован,а окно будет установлено в нижнем правом

        углу экрана.  Если AUTO используется с MAX или NORM, они

        будут проигнорированы.

AT <строка1, столбец1> | FROM <строка1, столбец1>

        Восстановление в прежних размерах максимизированного или

        минимизированного окна, но в другом месте. Координаты

        <строка1,столбец1> определяеют верхний левый угол окна,

        в котором оно должно быть размещено. Расположение окна

        можно также менять командой MOVE WINDOW.

          Можно указывать расположение минимизированного окна,

        используя AT и FROM. Левая сторона уменьшаемого окна

        появится на экране или в окне с координатами  <строка1,

        столбец1>.  Если указаны опции SIZE или TO, они будут

        проигнорированы.

          Следует помнить, что при выходе из FoxPro

        минимизированная позиция окна System сохраняется в файле

        FOXUSER.DBF. В следующий раз при минимизации окон

        System, они переместятся на место, определенное в

        предыдущем сеансе работы FoxPro.

SIZE <строка2, столбец2> | TO <строка2, столбец2>

          Можно также указывать размер окна, который отличается

        от оригинального, с помощью SIZE или TO. Если SIZE

        указано, окно будет состоять из <строка2> строк и иметь

        ширину <столбец2>.  Если указано TO, правый нижний угол

        окна будет установлен в позицию экрана или окна

        <строка2, столбец2>. Окно будет состоять из <строка2> -

        <строка1> строк и <столбец2> - <столбец1> столбцов.

  Поскольку пользователь обычно может произвольно перемещаться

по всем открытым окнам, чтобы правильно реагировать на его

действия, программа должна иметь возможность распознавать имя

текущего / нового окна, его статус и размер. Следующие функции

позволяют выяснить эту информацию:

WBORDER([<имя окна>]) - возвращает значение "истина" (.T.),

                        если окно имеет рамку.

WCHILD([<имя окна1> | <вырN1>]) - возвращает количество и имена

                                  порожденных окон. Возвращаемое

                                  значение - символьное или

                                  числовое.

<имя окна1>

       Имя порождающего (родительского) окна.

<вырN1>

       0 для нижнего в стеке порожденного окна. Hенулевое

       значение для оставшихся порожденных окон.

  Если  повторно использовать WCHILD() с ненулевым значением,

будет возвращено  имя следующего  в  стеке  порожденного  окна.

При выполнении WCHILD() большее  количество  раз,  чем   число

порожденных окон будет возвращаться пустая  строка.

  Если активным является экран, все другие окна являются

порожденными. Группы функций WCHILD() с ненулевыми  значениями

возвращают имена всех активных окон.

  При включении в функцию <имя окна> и <вырN1> WCHILD()

возвращает имена порожденных  окон относительно  указанного в

функции окна.  Если  в  функции  присутствуют  оба  параметра,

они  разделяются запятой.

WCOLS([<вырС>]) - Возвращает количество колонок позиций

                  символов, доступных в окне, имя которого

                  приведено в <вырС>. Если аргумент отсутствует,

                  подразумевается текущее активное окно.

WEXIST(<вырС>)  - Возвращает истину .T., если окно, имя которого

                  указано в качестве аргумента, было определено.

                  Hе обязательно, чтобы это окно было активным.

WLAST([<имя окна>])

  Функция WLAST() возвращает значение "истина" (.T.), если окно

с указанным именем было активно перед текущим окном, в противном

случае возвращается значение "ложь" (.F.).

  С помощью этой функции можно также определить окно, которое

было активно перед текущим окном.

<имя окна>

      Используется для того, чтобы определить, было ли указанное

      окно активно перед текущим окном.

  Если <имя окна> не указано в функции WLAST(), возвращается имя

последнего активного перед текущим окна. Если указанное в

функции окно не существует, возвращается значение "ложь" (.F.).

WLCOL([<имя окна>])

  WLCOL() возвращает номер левой крайней колонки окна

относительно экрана.

  Возвращаемое значение зависит от текущего видео режима. Режим

изменяется с помощью команды SET DISPLAY. Следует заметить, что

окно может быть позиционировано вне экрана. В этом случае, если

окно находится слева от экрана, будут возвращаться отрицательные

значения, если левый край справа от экрана -- значения, большие

ширины экрана.

<имя окна>

        Если <имя окна> не указано, будет возвращать значение

        для активного окна вывода. Если активного окна нет или

        вместо имени окна указана пустая строка, возвращается

        значение 0.  Следует отметить, что пустая строка может

        использоваться для указания экрана в функциях,

        возвращающих информацию о размещении окна или его

        размерах (WLCOL(), WLROW(), WCOLS(), WROWS(), и т.д.).

          Если имя окна указано в функции, возвращается номер

        крайней левой колонки этого окна.

    Если системное окно (окно Command, окно View window, ASCII

    Chart, окно Browse ... ) открыто и видимо или скрыто (его

    имя появляется в меню Window ), его имя можно указывать в

    WLCOL(). В противном случае будет выдано сообщение "WINDOW

    has not been defined." (ОКHО не описано). Исключение

    составляет окно Debug. Его имя можно включать в команду,

    если окно видимо, скрыто или закрыто.

WLROW([<имя окна>]) -    Возвращает номер верхней строки окна.

                         Возвращаемое значение - числовое.

WMAXIMUM([<имя окна>]) - Возвращает значение "истина" (.T.),

                         если указанное окно максимизировано.

                         Возвращаемое значение - логическое.

  Размеры окон могут увеличиваться до размеров экрана при выборе

опции Zoom    вертикального  меню  Window, по нажатию Ctrl+F10,

при нажатии клавиши "мыши" при установке курсора на символ

увеличения или с помощью ZOOM WINDOW. Созданные пользователем  с

помощью команды DEFINE WINDOW окна могут быть максимизированы

только, если в их описании присутствует ключевое слово ZOOM.

  Функция WMAXIMUM() возвращает значение "истина", если

указанное окно максимизировано,  в противном  случае

возвращается значение "ложь" (.F.).

  Если <имя окна> неуказано в WMAXIMUM(), возвращается

логическое значение для активного окна.

  В функции WMAXIMUM() можно указывать имена системных окон

FoxPro (Command, View, ASCII, Browse window, etc.)

WMINIMUM([<window name>])

  Размеры окон могут минимизироваться. В этом случае на экране

остаются видимыми только имя окна или заголовок. Минимизация

выполняется при выборе опции Zoom  вертикального меню Window,

по нажатию Ctrl+F9, при двойном нажатии клавиши "мыши" при

установке курсора на границе окна или с помощью ZOOM WINDOW.

Созданные пользователем с помощью команды DEFINE WINDOW окна

могут быть минимизированы только, если в их описании

присутствует ключевое слово MINIMIZE.

  Функция WMINIMUM() возвращает значение "истина", если

указанное окно минимизировано,  в противном  случае возвращается

значение "ложь" (.F.).

  Если <имя окна> не указано в WMINIMUM(), возвращается

логическое значение для активного окна.

WONTOP([<вырС>]) -  Возвращает истину .T., если окно, имя

                    которого указано  в качестве аргумента,

                    находится на переднем плане поверх всех

                    других окон.  Если имя окна  не указано, то

                    функция возвращает имя окна,  находящегося

                    на переднем плане.

WOUTPUT([<вырС>]) - Возвращает истину .T., если в окно, имя

                    которого указано в качестве аргумента, в

                    настоящее время направляется вывод.  Если

                    имя окна не указано в функции, то она

                    возвращает имя окна, в которое направляется

                    вывод, но если вывод направляется на экран,

                    она возвращает пустую строку.

WPARENT([<имя окна>]) - Возвращает имя порождающего

                        ("родительского") окна. Возвращаемое

                        значение - символьное. Если указанное

                        окно не имеет порождающего окна,

                        возвращается пустая строка.

WREAD([<имя окна>])

  WREAD() возвращает логическое значение "истина" (.T.), если

указанное окно используется текущей READ, в противном случае

возвращается значение "ложь" (.F.).

  Окно используется командой READ в следующих случаях:

         Заголовок окна указан во фразе WITH команды READ.

          Окна, которые  обычно  не  используются  командой

          READ, могут подключаться с помощью  фразы WITH. Это

          может быть окно Browse, окна редактирования текста и

          memo полей, системные окна FoxPro и т.д.

        При выдаче  @ ... GET в окне. Команды  @ ... GET  могут

         быть выданы в нескольких окнах, а затем указана команда

         READ.

<имя окна>

       Указывается для определения использования окна <имя окна>

       командой READ. Если имя  окна не  указано, WREAD()

       возвращает логическое значение для окна, которое

       находится на переднем плане в текущий момент( имя этого

       окна возвращается функцией WONTOP()).

WROWS([<вырС>]) - Возвращает количество строк (рядов), доступных

                  в окне, имя которого приведено в <вырС>.  Если

                  аргумент отсутствует, подразумевается текущее

                  активное окно.

WTITLE([<имя окна>])

  Описанные пользователем окна могут иметь заголовок, который

появляется сверху на рамке окна. Для описания окна с заголовком

нужно указать заголовок во фразе TITLE команды DEFINE WINDOW.

Системные окна FoxPro (окно Browse,окно Command, ASCII символы,

... ) все по умолчанию имеют заголовки.

  WTITLE() используется для возврата заголовка окна. Если <имя

окна> не указано в функции, возвращается заголовок окна

переднего плана.

<имя окна> - Для получения заголовка конкретного окна в

             WTITLE() указывается <имя окна>. Если окно описано

             с помощью команды DEFINE WINDOW, будет возвращен

             заголовок, указанный во фразе TITLE.

  Когда выполнена команда BROWSE WINDOW для создания окна Browse

с характеристиками описанного пользователем окна, WTITLE() будет

возвращать заголовок описанного пользователем окна. Если это

окно не имеет заголовка, будет возвращен заголовок окна Browse.

  Имена окнам присваиваются следующим образом:

  Имена окон, определяемых пользователем, берутся из команды

DEFINE WINDOW. Следует различать имя окна и заголовок. По

умолчанию  окна, определяемые пользователем , не имеют

заголовка.  Если заголовок указан DEFINE WINDOW <имя окна> TITLE

<вырC> - он будет появляться на верхней границе окна, но не

будет являться именем окна.

  Для системных окон имя и заголовок совпадают. Они являются

частью интерфейса FoxPro. Примерами системных окон являются окно

Command, окно View, окно Trace и т.д.

  Окна CREATE, MODIFY COMMAND, REPORT, LABEL, FILE именуются по

имени файла, который будет создаваться или модифицироваться. Имя

будет включать и расширение, если таковое есть.

WVISIBLE(<вырС>) - Возвращает истину .T., если окно, имя

                   которого указано в качестве аргумента, было

                   активизировано и не скрыто.  Функция

                   возвращает ложь .F., если упомянутое окно

                   было после активизации деактивизировано или

                   скрыто.  Если указанное окно не существует

                   (не было определено, либо было освобождено

                   или закрыто), появляется сообщение об ошибке

                   "WINDOW has not been defined" (окно не было

                   определено).

SCOLS() - Возвращает количество колонок, доступное для текущего

          вывода на экран. Функция очень полезна для определения

          размера и расположения объектов интерфейса или

          специальных объектов вывода. Она также возвращает

          количество доступных колонок и для расширенного

          видеорежима.

SROWS() - Возвращает количество строк (рядов) на экране,

          доступных для вывода в текущий момент.

 +--------------------------------+

 ¦         Пример программы       ¦

 +--------------------------------+

  Имена и заголовки окон Browse совпадают. Они могут

присваиваться несколькими путями. Если команда BROWSE

выполняется с фразой WINDOW, имя окна Browse будет совпадать с

заголовком указанного описанного пользователем окна (не с

именем). Если в команде BROWSE присутствует фраза TITLE, имя,

указанное в этой фразе будет именем окна BROWSE.

  В связи с этим (в продолжение "публикации" текста нашей

программы) мы вначале проверяем, не было ли уже ранее определено

окно для команды Browse, и если оно не определено, то -

определяем его как способным изменять размеры, закрываться и

минимизироваться; в качестве атрибутов даем ему тень, двойную

рамку и цветовую схему команды Browse - 10. Обратите внимание на

прием, использованный для того, чтобы центрировать это окно на

любом экране, не только 80х25.

wait window nowait [Определение окон]

IF NOT WEXIST("browsewin")

        DEFINE WINDOW browsewin ;

                FROM INT((SROW()-17)/2),INT((SCOL()-60)/2) ;

                TO INT((SROW()-17)/2)+16,INT((SCOL()-60)/2)+59 ;

                FLOAT ;

                CLOSE ;

                SHADOW ;

                MINIMIZE ;

                DOUBLE ;

                COLOR SCHEME 10

ENDIF


Меню в программах на FoxPro
Меню в прикладных программах

  В общем случае для создания dBASE - меню и работы с ним

программисту необходимо предусмотреть следующие элементы:

  - Определение меню. Здесь описывается содержание, "горячие"

    клавиши, клавиши быстрого доступа, форма и реакции меню.

    Определение меню может быть сделано один раз в начале

    программы и далее только использоваться. Разделение

    процессов определения меню и его активации очень удобно и

    делает программу более "прозрачной".

  - Активация меню. Команды / клавиши активации предъявляют меню

    на экране и делают его чувствительным к выбору пользователя.

  - Деактивация меню удаляет его с экрана, сохраняя впамяти для

    последующего возможного использования.

  - Удаление меню. Это действие очищает память от определения

    меню, и более оно не может быть использовано без нового

    определения. Конечно, удобно, когда меню постоянно находится

    в памяти и всегда доступно. Однако ограничивающим фактором

    здесь является память компьютера, и поэтому иногда

    приходитсся прибегать к временному удалению меню с

    повторным, если необходимо, его определением.

  "Всплывающее" POPUP - меню предъявляется в виде прямоугольника

в указанном месте окна / экрана. Содержанием меню могут быть

имена файлов, их компоненты или произвольные строки. Меню

допускает "прокручивание", а также выбор по первой (для всех

видов меню) или назначенной букве строки меню (исключая меню из

компонентов файлов).

  Рассмотрим команды организации меню:

  Описание POPUP - меню.

DEFINE POPUP <имя меню>

        [FROM <ряд1>, <кол1>]

        [TO <ряд2>, <кол2>]

        [IN [WINDOW] <имя окна>

        | IN SCREEN]

        [FOOTER <вырC1>]

        [KEY <метка клавиши>]

        [MARGIN]

        [MARK <вырC2>]

        [MESSAGE <вырC3>]

        [MOVER]

        [MULTI]

        [PROMPT FIELD <выраж>

        | PROMPT FILES [LIKE <шаблон>]

        | PROMPT STRUCTURE]

        [RELATIVE]

        [SCROLL]

        [SHADOW]

        [TITLE <вырC4>]

        [COLOR SCHEME <вырN>

        | COLOR  <список пар цветов>]

  С помощью  команды DEFINE  POPUP создается  вертикальное меню.

В качестве опций этого меню могут использоваться значения поля

базы данных,  список  файлов  или  полей  в структуре базы

данных. При помощи ряда  команд DEFINE  BAR определяются  опции

вертикального меню. Для размещения записей,  файлов или  полей в

вертикальном меню используются ключевые слова  PROMPT FIELD,

PROMPT FILES  или PROMPT STRUCTURE этой команды.

  При активизации вертикального меню по команде ACTIVATE POPUP

меню высвечивается на экран и дается возможность выбора опции. В

зависимости от выбранной опции, может выполняться программа или

высвечиваться и активизироваться другое вертикальное меню. Если

при выборе опции вертикального меню высвечивается другое

вертикальное или горизонтальное меню, то это вертикальное меню

называется иерархическим вертикальным меню.

<имя меню>

Имя создаваемого вертикального меню.

FROM <ряд1>, <кол1> TO <ряд2>, <кол2>

Определяет координаты вывода вертикального меню. Если указана

фраза FROM  <ряд1>,<кол1>, а фраза  TO <row2>,  <col2> опущена,

FoxPro автоматически устанавливает  размер вертикального меню.

Ширина меню будет равна длине самой длинной опции, а высота меню

будет равна количеству всех опций меню.  Высота меню

ограничивается размером экрана или окна. Если размер меню

недостаточен для вывода всех опций, организовывается

прокручивание опций внутри меню.  Прокручивание опций внутри

меню осуществляется нажатием клавиш Стрелка вверх и Стрелка вниз

либо нажатием кнопки  "мыши", когда  курсор расположен  на

верхней или нижней границе вертикального меню.

IN [WINDOW] <имя окна> | IN SCREEN

Задает окно, в котором размещается вертикальное меню. По

умолчанию вертикальные меню размещаются на экране, пока не

встретится активное окно вывода.

FOOTER <вырC1>

Задает ПОДВАЛ в центре нижней рамки вертикального меню.

KEY <метка клавиши>

Активизирует вертикальное меню по нажатию клавиши или

комбинации клавиш, соответствующих <метке клавиши>.

Включение фразы KEY эквивалентно использованию команд ON KEY

LABEL <метка клавиши> и ACTIVATE  POPUP <имя меню>. Если <метка

клавиши> используется для макроса,  то макрос имеет приоритет,

и использовать эту клавишу или  комбинацию клавиш для выбора

опции меню нельзя.

  Символ метки может быть помещен перед каждой опцией

вертикального меню при помощи ключевых слов MARGIN и MARK, а

также команды  SET MARK  OF.  Метка  может   использоваться  для

указания   наличия определенных условий - опция доступна, опция

недоступна и т. п.

MARGIN

Добавляет каждую опцию вертикального меню слева и справа

дополнительными пробелами. Эти пробелы могут использоваться

следующим образом: слева высвечивается метка опции, а справа -

стрелка, означающая наличие иерархического меню. Если данная

опция не используется, символ метки высвечивается поверх первого

символа названия опции,а иерархическая стрелка - поверх

последнего символа.

MARK <вырC2>

  Помещает символ метки перед каждой опцией вертикального меню.

По умолчанию в  качестве символа  метки используется маленький

ромб (set sysmenu automatic . Символ метки  <вырC2> должен быть  заключен в

одинарные  или двойные кавычки.  Если  в качестве метки

используется символьная строка, состоящая более, чем из одного

символа, только первый  ее символ используется  в качестве

метки. Для  задания метки  можно использовать функцию CHR().

  Ключевое слово MARK определяет метку для всех опций

вертикального меню. Метка,  заданная в  команде DEFINE  BAR

имеет  приоритет по отношению к метке, определенной  в команде

DEFINE POPUP.  Команда SET MARK OF используется для

включения/выключения меток, а также может использоваться при

задании меток для индивидуальных опций.

MESSAGE <вырC3>

Выводит сообщение при активизации меню. Сообщение центрируется и

выводится на экран в строке, указанной в команде SET MESSAGE.

MOVER

Изменяет порядок опций вертикального меню. Слева от каждой опции

вертикального меню ставится сивол . Порядок опций вертикального

меню можно изменить, выбрав требуемую опцию и поместив ее в

нужное место. Для перемещения опции с помощью "мыши" нужно

установить ее курсор на символ двойной стрелки, нажать кнопку

"мыши" и переместить опцию вверх или вниз. При работе с

клавиатурой нужно вначале выбрать опцию, а затем нажать клавишу

<Ctrl>+<стрелка вверх> или  <Ctrl>+<стрелка вниз> для

перемещения опции вверх или вниз, соответственно.  Функция

GETBAR() возвращает номер опции вертикального меню, в

зависимости от ее расположения и может использоваться для

определения порядка следования опций.

MULTI

Позволяет выбрать одновременно несколько опций вертикального

меню. При выборе опции вертикального меню слева от нее

высвечивается символ метки (по умолчанию символ set sysmenu automatic .

  Для  выбора  нескольких  опций  вертикального  меню  при

помощи клавиатуры  нужно  нажать  клавишу  Shift  и  выбрать

опцию меню нажатием клавиши Enter или Пробел.  Для

последовательного  выбора опций нужно нажать клавишу  Shift, а

затем клавишу  Стрелка вверх или Стрелка вниз.

  Для одновременного  выбора  нескольких  опций  при помощи

"мыши" необходимо  нажать  клавишу  Shift,  а  затем  кнопку

"мыши".  Для последовательного  выбора  опций  нужно  нажать

клавишу Shift и пройтись "мышью" по нужным опциям.

  Функция MRKBAR() может использоваться для определения того,

какие опции вертикального меню выбраны.  MRKBAR() возвращает

логическое значение истина (.T.), если опция помечена. Т.к. при

выборе опций, созданных при помощи ключевого слова MULTI,

осуществляется пометка опции, функция MRKBAR() может

использоваться для определения выбранных опций.

  Создавая вертикальное меню при помощи ключевого слова

MULTI, можно использовать ключевое слово MARGIN для

резервирования места для метки в каждой опции.  Если фраза

MARGIN не включена,  символ метки высвечивается поверх первого

символа названия опции.

  Hельзя осуществлять выбор множества опций из вертикального

меню, созданного при  помощи ключевого  слова PROMPT  (FIELD,

FILES или STRUCTURE).

PROMPT FIELD <выраж>

  Определяет поле базы  данных, значения которого  являются

опциями вертикального   меню.   Вертикальное    меню будет

содержать соответвующую  опцию для  каждой   записи  базы

данных.   При активизации  вертикального  меню рабочая область,

в которой находится база данных, становится текущей.

  Если на базу данных наложен фильтр, можно использовать

преимущества метода быстрого доступа Rushmore.

  <выраж> может  содержать  несколько  полей  и  выражений,

сцепленных при  помощи оператора  (+). В  качестве <выраж>

может также использоваться поле базы  данных из другой рабочей

области или определенная пользователем функция.

  Максимальное количество опций вертикального меню, созданного

при помощи PROMPT FIELD, в системе FoxPro равно 32,767. Если

база данных содержит большее количество записей, высвечивается

предупреждающее сообщение "Popup too big, first nnnnn entries

shown" (Вертикальное меню слишком большое, высвечиваются  первые

nnnnn  опций). В расширенной  версии  FoxPro  количество  опций

вертикального  меню,  созданного  при  помощи  PROMPT  FIELD,

не ограничено.

PROMPT FILES [LIKE <шаблон>]

  Создает вертикальное  меню,  опциями  которого  является

список доступных  файлов.  Список  файлов  может определяться

при помощи шаблона <шаблон>, содержащего стандартные обозначения

DOS.

  Hапример,  для  создания  вертикального  меню, содержащего

список файлов баз  данных из  текущего директория,  нужно

указать PROMPT FILES  LIKE  *.DBF.  Можно  создать вертикальное

меню, содержащее список  файов  из  другого  директория или

диска, включив путь. Hапример, для создания  вертикального

меню, содержащего список программных  файлов  из  директория

PROGRAMS  на  диске A, нужно использовать фразу PROMPT FILES LI

KE A:\PROGRAMS\*.PRG.

PROMPT STRUCTURE

Создает вертикальное меню, содержащее структуру базы данных. При

активизации  меню  рабочая  область,  в  которой  находится

база данных, становится текущей.

RELATIVE

  Управляет порядком включения опций в вертикальное  меню.

Если вертикальное  меню  создано  без  использования  ключевого

слова RELATIVE, то включение опций  в него происходит в

соответствии с номерами строк (как  указано в команде  DEFINE

BAR). Для еще не определенных опций резервируется место.

Hапример, если определены первая и третья опции (DEFINE BAR  1

OF <имя меню>, DEFINE BAR  3 OF <имя меню>), то при активизации

вертикального меню для второй опции в меню резервируется пустая

строка.

  Использование ключевого слова RELATIVE приводит к тому, что

опции появляются   в меню   в   порядке   их   определения.  Для

еще неопределенных опций место  в меню не  резервируется.

Определение вертикального меню с фразой RELATIVE позволяет также

использовать фразы  BEFORE и AFTER  в  команде   DEFINE  BAR   -

задание относительного  расположения  опций.  Создание

вертикального меню без использования фразы RELATIVE  приведет к

тому, что  включение фраз BEFORE или AFTER в команду DEFINE BAR

вызовет синтаксическую ошибку.

SCROLL

  Вывод рамки прокручивания справа от вертикального меню. Рамка

прокручивания высвечивается только в том случае, если

количество опций   превышает   размер   вертикального   меню,

либо  размер вертикального меню превышает размер окна или

экрана.

SHADOW

  Высвечивает тень вертикального меню. Команда SET SHADOWS

используется для вывода или перемещения тени,

TITLE <вырC4>

  Определяет заголовок, высвечиваемый в центре верхней рамки

вертикального меню.

COLOR SCHEME <вырN> | COLOR <список пар цветов>

  Изменяет установки цветов, принятые по умолчанию. По умолчанию

для вертикальных меню, созданных при помощи команды DEFINE

POPUP, используются цвета цветовой схемы 2 пользовательского

меню. Можно задать цвета всех элементов меню непосредственно,

или использовать цвета из другой цветовой схемы.

  Элементы POPUP - меню описываются командой

DEFINE BAR <вырN1> |

        <имя системной опции>

        OF <вертикальное меню> PROMPT <вырC1>

        [BEFORE <вырN2>

        | AFTER <вырN2>]

        [KEY <метка клавиши> [, <вырC2>]]

        [MARK <вырC3>]

        [MESSAGE <вырC4>]

        [SKIP [FOR <вырL>]]

        [COLOR <список пар цветов>

        | COLOR SCHEME <вырN2>]

  Команда DEFINE BAR используется совместно с командой DEFINE

POPUP для создания вертикального меню. Вертикальное меню

создается  при помощи команды DEFINE POPUP.  Затем при помощи

последовательности команд DEFINE BAR определяются его опции.

  После выбора опции меню может быть высвечено другое меню или

вызвана подпрограмма. Вертикальное меню, которое высвечивает

другое вертикальное или горизонтальное меню, называется

иерархическим.

  Вертикальное меню можно наполнить опциями, созданными при

помощи последовательности команд DEFINE BAR. Команда DEFINE BAR

является обязательной для каждой опции вертикального меню.

Команда DEFINE BAR должна содержать <вертикальное меню>, для

которого определяется опция.

  Можно определять больше опций, чем может поместиться в меню,

при этом  будет  обеспечена  вертикальная  их  прокрутка

(скроллинг). Перемещение  по  опциям меню реализуется нажатием

клавиш Стрелка вверх и Стрелка вниз либо при нажатии кнопок

"мыши", когда курсор расположен на верхней или нижней границе

рамки меню.

  Создаваемое вертикальное меню может содержать записи,  поля

базы данных или список доступных файлов на диске.

<вырN1>

Присваивает номер опции.

<имя системной опции>

  Помещает  опцию  системного  вертикального  меню  в

создаваемое. Функция SYS(2013) возвращает список системных имен.

OF <вертикальное меню>

Имя вертикального меню, для которого создается опция.

PROMPT <вырC1>

Определяет текст названия опции. Между опциями могут

располагаться разделительные   линии,  задаваемые   при   помощи

символов обратной черты и дефиса (\-) в выражении <вырC1> фразы

PROMPT. Это позволяет группировать опции.

   Можно также задавать "горячие ключи" для опций меню, поместив

перед нужным символом символ  обратной черты и знак  меньше

(\<).  "Горячий ключ" - буква, по нажатию которой происходит

выбор соответствующей опции. При наличии "горячих ключей" нет

необходимости перемещать курсор к нужной опции и затем нажимать

клавишу Пробел или Enter.

BEFORE  <вырN2>   |  AFTER   <вырN2>

Размещает опцию в  меню относительно других опций. Два

специальных имени, _MFIRST и _MLAST, позволяют обращаться  к

первому и последнему элементам меню.

KEY <метка клавиши> [, <вырC2>]

  Помимо нажатия "горячего ключа" опция может быть выбрана

нажатием другой клавиши или комбинации клавиши. При помощи опции

KEY можно назначить <метку клавиши> для  клавиши или комбинации

клавиш,  по нажатию  которой   будет  производиться   выбор

опции.

  Если  уже определен  макрос  нажатия  клавиш  с именем <метка

   клавиши>, он имеет преимущество  и опцию нельзя будет

   выбрать по нажатию указанной клавиши или комбинации клавиш.

  <Метка  клавиши>   располагается  справа   от  опции.

Включение необязательного  символьного  выражения  <вырC2>

позволяет вместо <метки клавиши> выводить  другой текст.

Hапример, включение  фразы KEY  Ctrl+B  поместит  текст

"Ctrl+B"  после  названия опции. А включение фразы KEY Ctrl+B,

"^B" справа от названия опции выведет текст  "^B".   Вывод

<Метки  клавиши>  может быть подавлен, если значением <вырC2>

является пустая строка.

MARK <вырC3>

  Помещает  символьную  метку  перед  опцией вертикального меню.

По умолчанию в качестве метки  используется символ (set sysmenu automatic .   Фраза

MARK <вырC1>  может  использоваться   для  изменения  символа

метки, принимаемого  по  умолчанию.   Определение   символа

метки не используется  для  снятия  метки  опции  - для снятия

метки нужно применять команду SET MARK OF.

  Если выражение <вырC1> содержит более  одного символа, только

первый символ используется в качестве сивола метки.

  Метки, определенные в команде DEFINE BAR имеют преимущество

перед метками, определенными  при помощи  фразы MARK  в команде

DEFINE POPUP.  Команда SET MARK OF используется для

включения/выключения меток и может быть также задействована для

определения метки  для одной или всезх опций вертикального меню.

  Следующие команды указывают, что произойдет, если выбор в меню

сделан:

ON SELECTION POPUP <имя вертикального меню> | ALL [<команда>]

  Команда ON SELECTION POPUP назначает выполнение конкретной

подпрограммы при выборе опции вертикального меню. Эта

подпрограмма может быть командой, программой или процедурой. В

подпрограмме с помощью функции BAR() и команды DO CASE каждой

отдельной опции вертикального меню назначаются конкретные

действия.

  Команда ON  SELECTION POPUP должна следовать после  команды

DEFINE  POPUP,  но до команды ACTIVATE POPUP.

  В команде ON SELECTION POPUP указывается имя меню для

назначения впоследствии индивидуальных действий. Вместо имени

можно указать фразу ALL, тогда всем опциям всех вертикальных

меню   будет   назначено одно действие, реализуемое командой,

включенной  в  ON SELECTION POPUP.

  Команда ON SELECTION POPUP без включения в нее исполняемой

команды отменяет ранее назначенную подпрограмму.

  Следующие команды при необходимости позводяют назначить

пунктам POPUP - меню индивидуальные реакции (команды, процедуры,

другие меню).

ON SELECTION BAR <вырN> OF <имя меню типа popup> [<команда>]

  ON SELECTION BAR позволяет назначить подпрограмму, которая

должна выполниться при  выборе опции  меню типа  popup. Эта

подпрограмма может  быть командой,  программой  или  процедурой.

Команда  ON SELECTION  BAR  должна  быть  расположена  между

командами DEFINE POPUP и ACTIVATE POPUP.

<вырN> OF <имя меню типа popup> [<команда>]

     Для выполнения <команды> при выборе заданной опции в

     команду включаются  номер  опции  <вырN>, <имя меню типа

     popup> и  <команда>.  Для  выполнения  подпрограммы,

     назначенной опции меню, в команду не включается <команда>.

       С помощью команды ON SELECTION POPUP подпрограмма может

     быть назначена  всем  опциям  в  меню  типа popup.

     Горизонтальное (полосовое)  меню  типа  bar  и  меню  типа

     popup может быть назначено  индивидуальной  опции  командой

     ON  BAR  с целью создания иерархической системы меню.

ON BAR <вырN> OF <имя меню popup1>

        [ACTIVATE POPUP <имя меню popup2>

        | ACTIVATE MENU <имя меню типа menu>]

  Активизирует меню типа popup или полосу меню типа menu при

выборе опции в меню типа popup.

  При выборе опции из меню типа popup (вертикальное меню)

могут инициироваться различные действия - может быть выведено

и активизировано  другое такое же меню, может быть выведена и

активизирована  полоса горизонтального меню (меню  типа  menu),

может  быть  запущена подпрограмма. Данная команда  позволяет

при  выборе  опции вывести  и активизировать другое меню (popup

или menu). Меню, в котором выбирается опция, после чего

активизируется другое меню, называется  иерархическим. Если при

выборе опции следует выполнить подпрограмму, необходимо

применять в зависимости от типа меню  команду ON SELECTION BAR

или ON SELECTION POPUP.

<вырN> OF <имя меню popup1>

     Каждая опция может иметь подсоединенное меню типа popup

     или  menu. Выбираемая опция BAR специфицируется номером

     <вырN>  и именем  меню  <имя  меню   popup1>,  содержащим

     ее.   Меню, выводимое   и   активизируемое   при   выборе

     этой  опции, специфицируется через <имя меню popup2> или

     через <имя меню типа menu>.

       Опция  меню, к которой присоединено другое  меню, имеет

     при визуализации   справа   от названия стрелку, которая

     сигнализирует о наличии присоединенного меню. Если при

     создании меню не использовалась опция MARGIN, то последний

     символ названия опции может перекрыть символ иерархии меню

     - стрелку.

ACTIVATE POPUP <имя меню popup2>

 | ACTIVATE MENU <имя меню типа menu>

        Чтобы освободить меню типа popup или полосовое меню

        типа menu при выборе опции следует использовать

        команду ON BAR <вырN> OF <имя меню popup1> без фразы

        ACTIVATE POPUP <имя меню popup2> или ACTIVATE MENU <имя

        меню типа menu>.

  Команда

  ACTIVATE POPUP <вертикальное меню>

        [AT <ряд>, <колонка>]

        [BAR <вырN>]

        [NOWAIT]

        [REST]

  предъявляет меню на экране и активирует его.

  Команда ACTIVATE POPUP используется совместно с командами

DEFINE POPUP и DEFINE BAR, которые определяют и создают

вертикальное меню. Команда ACTIVATE POPUP осуществляет вывод на

экран или в окно вертикального меню с именем <вертикальное меню>

и активизирует его для использования.

<вертикальное меню>

Имя активизируемого вертикального меню.

AT <ряд>, <колонка>

Определяет координаты экрана или окна для вывода активизируемого

вертикального меню. Координаты, заданные в этой команде, имеют

преимущество над координатами, указанными во фразе FROM команды

DEFINE POPUP.

BAR <вырN>

Определяет номер текущей опции вертикального меню при его

выводе.

NOWAIT

Продолжает выполнение программы после вывода и активизации

вертикального меню.

REST

Устанавливает начальную опцию вертикального меню в соответствии

с текущим положением указателя записи в базе данных.

  Чтобы заполнить выбор в меню, сделанный пользователем, в

процедуру можно передать параметры выбора с помощью следующих

функций:

BAR()    - После выбора опции из меню функция BAR() возвращает

           соответствующий номер. После этого, в зависимости от

           полученного номера, в программе выполняется требуемый

           переход.  Функция BAR() возвращает ноль, если нет

           активного вертикального меню или если была нажата

           клавиша Esc.

POPUP()  - Возвращает в виде символьной строки имя активного

           вертикального меню.  Функция вернет имя, если меню

           было ранее определено командой DEFINE POPUP и

           активизировано командой ACTIVATE POPUP. Если нет

           активного вертикального меню, а также при

           использовании через окно команд, функция возвращает

           пустую строку.

PROMPT() - Возвращает текст опции, выбранной из активного

           вертикального или горизонтального меню.  Если при

           определении меню командой DEFINE POPUP использовалась

           фраза FIELD, функция вернет содержимое выбранного

           поля. Если же использовалась фраза FILES, функция

           вернет имя выбранного файла, включая маршрут и

           расширение. При использовании же фразы STRUCTURE

           будет возвращено имя выбранного поля символами

           верхнего регистра.  Если нет активных меню или если

           была нажата клавиша Esc для выхода из меню, то

           функция возвращает пустую строку.

CNTBAR(<вырC>) - Hомер элемента вертикального меню.

MRKBAR(<вырC>, <вырN>) - Возвращает логическую  истину,  если

                         опция  вертикального меню помечена.

                         Возврат - Логическое значение.

                         <вырC>

                                 Имя вертикального меню.

                         <вырN>

                                 Hомер проверяемой опции.

  Нижеперечисленные команды деактивируют / удаляют меню или его

элементы:

DEACTIVATE POPUP - С помощью данной команды активное

                   вертикальное меню деактивизируется и

                   удаляется с экрана, но не из памяти. В каждый

                   момент времени может быть  активным только

                   одно вертикальное меню, поэтому нет

                   необходимости указывать в команде имя меню.

                   Меню может быть активизировано вновь с

                   помощью  команды ACTIVATE POPUP с указанием

                   имени меню.

                     Для  удаления  меню из  памяти применяется

                   команда  RELEASE POPUPS со  специфицированием

                   имени требуемого меню. Удалить из памяти  все

                   меню  можно  с  помощью  CLEAR  ALL  и  CLEAR

                   POPUPS.

                     DEACTIVATE POPUP выполнима только в составе

                   программного файла. Через окно команд данная

                   команда не выполняет никаких действий.

HIDE POPUP <имя меню1>

        [, <имя меню2> ... ]

        | ALL [SAVE]

  HIDE POPUP удаляет указанное вертикальное меню, активное

вертикальное меню или все вертикальные меню с экрана или окна,

но не из памяти. Перед тем, как быть скрытым, вертикаотное меню

должно быть создано с помощью DEFINE POPUP. Скрытие меню не

адекватно деактивизации. Скрытое меню продолжает резидентно

находиться в памяти и может быть отображено на экране или в окне

с помощью команд ACTIVATE POPUP или SHOW POPUP.

  Скрыть меню можно, указав его имя.

  Можно разместить образ меню на экране или в окне, указав фразу

SAVE. Это полезно при тестировании и доработке программ.

Удалить образ меню с экрана можно командой CLEAR.

<имя меню1> [, <имя меню2> ... ]

    Список имен меню для скрытия.

ALL

        Будут скрыты все описанные вертикальные меню.

SAVE

    Размещение образа вертикального меню на экране или в окне.

  Хотя BAR - меню по умолчанию - горизонтальное, его компонеты

могут быть размещены в любых местах экрана. При его создании

используются следующие команды:

DEFINE MENU <имя меню>

        [BAR [AT LINE <вырN1>]]

        [IN [WINDOW] <имя окна>

        | IN SCREEN]

        [KEY <метка клавиши>]

        [MARK <вырC1>]

        [MESSAGE <вырC2>]

        [NOMARGIN]

        [COLOR <список пар цветов>

        | COLOR SCHEME <вырN2>]

  Команда  DEFINE  MENU  используется  для  создания  системы

меню. Система  меню  обычно  содержит горизонтальные меню,

каждой опции которого соответствует вертикальные  меню. При

продвижении  вдоль опций  горизонтального   меню  под   каждой

опцией появляется вертикальное меню. При этом можно выбрать

необходимую опцию из вертикального меню.

  Для создания системы меню вначале необходимо создать

горизонтальное меню при помощи команды DEFINE MENU. Создаваемому

горизонтальному меню присваивается <имя меню>. Затем при помощи

команд DEFINE PAD определяется каждая из опций горизонтального

меню. При помощи команд ON PAD ... ACTIVATE POPUP задается,

какое вертикальное меню будет высвечиваться под каждой опцией

горизонтального меню. После этого при помощи команд DEFINE POPUP

создается  вертикальное  меню,  которое будет высвечиваться под

каждой   опцией   горизонтального меню.  Вся система    меню

активизируется при помощи команды ACTIVATE MENU.

  Выбор опции горизонтального меню не должен активизировать

вертикальное меню (ACTIVATE POPUP). При выборе опции

горизонтального меню может быть активизировано другое

горизонтальные меню при помощи команд ON PAD ... ACTIVATE MENU

, либо при помощи команды ON SELECTION PAD ... может

осуществляться вызов программы.

<имя меню>

Имя создаваемого горизонтального меню.

ВAR [AT LINE <вырN1>]

  Создается горизонтальное меню, аналогичное системному

горизонтальному меню FoxPro. Если размер или количество опций

горизонтального меню превышает размер экрана или окна, в котором

высвечивается это горизонтальное меню, то осуществляется

прокрутка меню. Строка экрана или окна, где размещается

горизонтальное меню, задается опцией AT LINE команды BAR.

<вырN1> - номер строки экрана или окна, где будет высвечиваться

горизонтальное меню.

[WINDOW] <имя окна> | IN SCREEN

  Помещает горизонтальное меню в  заданное окно или выводит его

на весь экран.  По  умолчанию горизонтальные меню выводится  на

весь экран, если не указано активное окно вывода. Если оно

существует, то горизонтальное меню помещается в окно вывода.

KEY <метка клавиши>

  Активизирует горизонтальное меню при нажатии одной или

нескольких клавиш. Фраза KEY <метка  клавиши> задает клавишу или

нессколько клавиш, активизирующих  горизонтальное меню.  См.

команду  ON KEY LABEL, описание  которой содержит  набор

допустимых клавиш и их меток. Включение в команду фразы  KEY

эквивалентно последовательности команд ON KEY LABEL <метка

клавиши> и ACTIVATE MENU  <имя  меню>.  Если  <метка  клавиши>

уже  используется для макроса, то приоритет макроса  выше, при

горизонтальное меню уже не может быть активизировано  нажатием

данной клавиши или  группы клавиш.

MARK <вырC1>

  Помещает  перед  каждой  опцией  горизонтального  меню

символьную метку. Может  использоваться   метка,  задающая

определенное условие:  для опции имеется вертикальное меню,

задействованы  или не задействованы режимы данной опции и т. д.

  По  умолчанию  в  качестве  метки для опции горизонтального

меню используется символ (set sysmenu automatic .  При помощи опции MARK, задав в

<вырC1> символ  метки,  можно  создать  собственную метку для

всех опций горизонтального  меню.   Символ  метки  в  <вырC1>

должен быть заключен в одинарные или двойные кавычки. Кавычки не

нужны, если <вырC1> представляет собой переменную памяти или

элемент массива, являющийся  символьной  строкой. Если  <вырC1>

содержит больше одного символа, то только  первый символ

используется в качестве метки. Для задания метки можно

использовать функцию CHR().

  Опция  MARK  устанавливает  метки  для всех опций

горизонтального меню. Метки, заданные командами DEFINE PAD,

имеют приоритет перед метками, указанными в опции MARK команды

DEFINE MENU. Команда SET MARK используется для включения /

выключения меток, а также может использоваться для задания меток

для одной или всех опций горизонтального меню.

MESSAGE <вырC2>

  Высвечивает <вырC2> при выборе опции меню. Отцентрированное

сообщение выводится в строке, определенной при помощи команды

SET MESSAGE.  Сообщение,  заданное  в  команде DEFINE MENU,

перекрывается сообщением, указанным в команде DEFINE PAD.

NOMARGIN

Удаляет в опции меню пробелы справа и слева.

COLOR SCHEME <вырN2> |

COLOR <список пар цветов>

  Изменяет установки цветов, принятые по умолчанию. По умолчанию

цвета горизонтального меню, создаваемого по команде DEFINE MENU,

соответствуют цветовой схеме  2 меню пользователя. Цвета

горизонтального меню можно задать и непосредственно, либо

использовать другую цветовую схему.

  С помощью следующей команды BAR - меню может быть наполнено:

DEFINE PAD <имя опции>

        OF <имя меню> PROMPT <вырC1>

        [AT <ряд>, <колонка>]

        [BEFORE <имя опции>

        | AFTER <имя опции>]

        [KEY <метка клавиши> [, <вырC2>]]

        [MARK <вырC3>]

        [SKIP [FOR <вырL>]]

        [MESSAGE <вырC4>]

        [COLOR <список пар цветов>

        | COLOR SCHEME <вырN>]

  Команда DEFINE PAD используется совместно с командой DEFINE

MENU для  создании  системы  меню.    DEFINE  PAD  предназначена

для определения   опции   горизонтального   меню.    Созданные

опции горизонтального  меню  можно  выбирать (высвечивать). Если

опция горизонтального  меню  выбрана,  высвечивается

соответствующее ей вертикальное  меню,   либо  другое

горизонтальное  меню,   либо осуществляется вызов подпрограммы.

<имя   опции>    определяет   имя   опции   меню.    Каждая

                 опция горизонтального меню  определяется и

                 позиционируется при помощи команды DEFINE PAD.

                 Горизонтальное меню должно быть определено до

                 создания опций, и в DEFINE PAD должно быть

                 указано <имя меню>.

<имя опции>

Имя создаваемой опции горизонтального меню.

OF <имя меню>

Горизонтальное меню, которому принадлежит опция.

PROMPT <вырC1>

Определяет название опции.  Для опции горизонтального  меню

можно задавать  горячий  ключ,  указав  перед  символом

горячего ключа символ обратной черты и знак меньше или равно

(\<) . Горячий ключ служит для выбора соответствующей опции меню

прямо с клавиатуры. При  использовании  горячих  ключей  нет

необходимости подводить курсор к  выбранной опции,  а затем

нажимать клавишу  Пробел или ENTER.

AT <ряд>, <колонка>

Определяет  координаты вывода опции.  Для горизонтального меню,

созданного при помощи команды DEFINE  MENU с фразой BAR можно

не указывать фразу AT,  использующуюся для задания координат

вывода опции на экране или в окне. Опции горизонтального меню,

созданные при помощи ключевого слова  BAR, образуют

горизонтальную строку меню - строку меню можно разместить в

заданной строке экрана  или окна, включив в команду DEFINE MENU

фразу AT LINE.

BEFORE <имя опции>

| AFTER <имя опции>

Задает расположение и порядок активации опций.

KEY <метка клавиши> [, <вырC2>]

Выбор  опции  нажатием  одной  или  нескольких  клавиш. <метка

клавиши>  опции  КЕY  определяет   одну  или  несколько

клавиш, используемых для выбора опции.  Если <метка клавиши>

используется  для   макроса,  то   макрос  имеет приоритет,   и

использовать эту клавишу или группу клавиш для выбора опции

меню нельзя.

  <метка клавиши> помещается справа от опции горизонтального

меню, созданной без помощи опции BAR. В горизонтальных меню,

созданных при помощи опции BAR,  <метка клавиши> не

высвечивается. <метку клавиши> можно заменить собственным

текстом.

MARK <вырC3>

Помещает  перед  каждой  опцией  горизонтального  меню

символьную метку. Может  использоваться   метка,  задающая

определенное условие:  для опции имеется вертикальное меню,

задействованы  или не задействованы режимы данной опции и т. д.

  По  умолчанию  в  качестве  метки  для опции горизонтального

меню используется символ (set sysmenu automatic .  При помощи опции MARK, задав в

<вырC1> символ  метки,  можно  создать  собственную  метку для

всех опций горизонтального  меню.   Символ  метки  в  <вырC3>

должен   быть заключен в одинарные или двойные кавычки.  Кавычки

не нужны, если <вырC3> представляет собой переменную памяти или

элемент массива, являющийся  символьной  строкой.   Если

<вырC3>  содержит больше одного символа, то только  первый

символ используется в качестве метки. Для задания метки можно

использовать функцию CHR().

  Заметим,  что  метки, созданные  при  помощи команды DEFINE

PAD, имеют приоритет  перед контрольными  метками, заданными

командой DEFINE   MENU.      Команда    SET    MARK

используется     для включения/выключения  меток.

SKIP [FOR <вырL>]

  Включает/выключает опцию  в зависимости  от значения

логического условия  <вырL>.   Если  используется  фраза  SKIP

FOR   <вырL>, вычисляется значение логического условия <вырL> и,

в  зависимости от результата (истина или  ложь), опция

становится доступной или нет. Если значением <вырL> является

истина (.T.) опция становится недоступной  и  не  может  быть

выбрана.   Если значением <вырL> является ложь (.F.) - опция

доступна.

  Другим методом отключения опции  меню является  размещение

перед текстом опции символа обратной черты (\).

MESSAGE <вырC4>

Высвечивает сообщение при выборе опции. После выбора опции

текст сообщения  центрируется  и  высвечивается  в строке,

заданной при помощи команды SET MESSAGE.

COLOR SCHEME <вырN> |

COLOR <список пар цветов>

Изменяет  установки  цветов, принятые  по умолчанию. По уолчанию

используется цветовая схема 2.

ON SELECTION MENU <имя меню>

        | ALL  [<команда>]

  ON  SELECTION   MENU  позволяет   назначить  подпрограмму

опции меню. Эта подпрограмма  будет  выполняться  при выборе

опции из горизонтального  меню  типа  menu.  Эта  подпрограмма

может быть командой,  программой  или   процедурой.  Команда  ON

SELECTION MENU должна размещаться  между командами DEFINE MENU

и ACTIVATE MENU.

<имя меню>

     Для  выполнения <команды>  в  команду  ON  SELECTION   MENU

     включается <имя меню> для конкретной опции.

ALL

     Ключевое  слово  ALL  включается  в команду, если

     необходимо выполнить <команду>    для каждой опции

     активного горизонтального меню.

<команда>

     Команда ON SELECTION MENU <имя меню> без <команды>

     используется для освобождения подпрограммы, назначенной

     конкретной опции меню. Можно для этой же цели использовать

     команду ON SELECTION MENU ALL без <команды>, только будет

     освобождена подпрограмма, назначенная всем опциям.

       Подпрограммы могут быть назначены конкретным опциям меню

     посредством команды ON SELECTION PAD; меню типов menu и

     popup могут быть назначены опциям меню командой ON PAD.

ON SELECTION PAD <имя опции> OF <имя меню> [<команда>]

  Команда ON SELECTION PAD назначает выполнение конкретного

действия  при  выборе   опции горизонтального меню. Это может

быть команда, подпрограмма или процедура.

  При назначении действия опции горизонтального меню в   команде

ON SELECTION  PAD необходимо указать имя этой опции  в <имя

опции>, и  имя горизонтального меню  в <имя меню>.

  Действие опции  не  назначается,  если к ней присоединено

вертикальное  меню  командой ON PAD.

ON PAD <имя опции> OF <имя меню1>

 [ACTIVATE POPUP <имя меню типа popup>

 | ACTIVATE MENU <имя меню2>]

  Активизирует меню типа popup  или полосовое  меню типа  menu

при выборе опции горизонтального (полосового) меню.

  Команда  ON  PAD  используется  с  опциями  горизонтального

меню. Элементы горизонтального  меню должны  быть определены

командами DEFINE MENU  и DEFINE  PAD.   Командой ON  PAD можно

присоединить вертикальное  меню  к  опции   горизонтального.

При этом при подсвечивании этой  опции горизонтального  меню

будет  выводиться вертикальное меню.

  При присоединении вертикального меню  к опции  горизонтального

в команде ON PAD необходимо указать имя этой опции в <имя

опции>, и имя  горизонтального   меню  в   <имя  меню>.

Специфицирование горизонтального  меню  выполняется  посредством

фразы ACTIVATE POPUP, в  которой  указывается  имя

вертикального  меню. Это вертикальное  меню  должно  быть

определено  до ссылки на него в команде ON PAD.

  С   помощью   фразы ACTIVATE   MENU   можно   активизировать

и горизонтальное меню.

ACTIVATE POPUP <popup name>

 | ACTIVATE MENU <menu name2>

     Для освобождения меню при выборе опции следует использовать

     настоящую  команду  без  фразы  ACTIVATE  POPUP  <имя

     меню типа popup> или ACTIVATE MENU <имя меню2>.

  Команду ON PAD нельзя использовать одновременно с командой ON

SELECTION PAD.

  Для передачи в процедуры параметров могут использоваться

функции:

MENU() - Возвращает имя текущего активного горизонтального меню.

         Если активного меню нет - возвращается пустая строка.

         Имя меню будет возвращено функцией, если меню перед

         этим определено командой DEFINE MENU  и активизировано

         командой ACTIVATE MENU.

PAD()  - Возвращает в виде символьной строки имя последней

         выбранной опции из горизонтального меню. Функция

         возвращает имя, если  меню   ранее определено и

         активизировано командами  DEFINE MENU и ACTIVATE MENU.

         Если меню не активно, а также при использовании функции

         через окно команд, функция возвращает пустую строку.

CNTPAD(<вырC>) - Возвращает номер опции горизонтального меню.

MRKPAD(<вырC1>, <вырC2>) - Возвращает логическую истину в случае

                           пометки опции горизонтального меню.

                           <вырC1>

                                   Имя горизонтального меню.

                           <вырC2>

                                   Имя запрашиваемой опции

                                   горизонтального меню.

  Следующие команды деактивируют / удаляют меню:

DEACTIVATE MENU

С помощью данной команды активное горизонтальное   меню

деактивизируется    и удаляется с экрана, но не из памяти. В

каждый момент  времени  может  быть  активным только одно

горизонтальное   меню,   поэтому   нет необходимости указывать в

команде имя меню. Меню  может  быть активизировано  вновь   с

помощью  команды  ACTIVATE  MENU  с указанием имени меню.

  Для  удаления  меню  из  памяти применяется команда  RELEASE

MENUS со  специфицированием имени требуемого меню. Удалить из

памяти  все меню можно с помощью CLEAR ALL и CLEAR MENUS.

HIDE MENU <имя меню1>

        [, <имя меню2> ... ]

        | ALL [SAVE]

  HIDE MENU удаляет указанное горизонтальное меню, активное

горизонтальное меню или все горизонтальные меню с экрана или

окна, но не из памяти. Перед тем, как быть скрытым,

горизонтальное меню должно быть создано с помощью DEFINE MENU.

Скрытие меню не адекватно деактивизации. Скрытое меню находится

резидентно  в памяти и может быть отображено на экране или в

окне с помощью команд ACTIVATE MENU или SHOW MENU.

  Скрыть меню можно, указав его имя.

  Можно разместить образ меню на экране или в окне, указав фразу

SAVE. Это полезно при тестировании и доработке программ.

Удалить образ меню с экрана можно командой CLEAR.

<имя меню1> [, <имя меню2> ... ]

    Список имен меню для скрытия.

ALL

        Будут скрыты все описанные горизонтальные меню.

SAVE

        Размещение образа горизонтального меню на экране или в

        окне.

+---------------------------------+

¦        Пример программы         ¦

+---------------------------------+

  Hаиболее предпочтительным методом  включения вертикальных меню

в приложение является использование системного горизонтального

меню FoxPro и его вертикальных меню. При использовании

вертикальных меню системного горизонтального меню нет

необходимости вначале определять вертикальные меню, более того

это приведет к ошибке. _MSYSMENU - это название системного меню.

  В приведенном ниже фрагменте мы вначале очищаем системное меню

от всех его BAR - и PAD - меню, заставляем отобразиться эту

пустую полосочку и начинаем ее заполнять PAD - элементами, к

которым далее привязываем вертикальные POPUP - меню, три из

которых наполнены BAR - элементами. После всего этого фразами

ON SELECTION BAR номер_пункта_меню OF имя_меню DO Имя_процедуры

подвешиваем процедуры, реагирующие на выбор соответствующих

пунктов меню.

  Определив меню и процедуры - реагенты, мы организуем

безоконный цикл чтения состояния меню до тех пор, пока

VALID-функция IsQuit не прервет его. Такого рода цикл обработки

сообщений - относительная новинка в FoxPro; подробнее Вы можете

об этом прочитать в книге Les Pinter [FoxPro Application

Programming] (Chapter 'Foundation Read').

wait window nowait [Инициализация меню]

SET SYSMENU TO

SET SYSMENU AUTOMATIC

DEFINE PAD helpquit OF _MSYSMENU PROMPT [\<Ё] COLOR SCHEME 3

DEFINE PAD listmake OF _MSYSMENU PROMPT "\<Составление "+;

                                   "списков " COLOR SCHEME 3

DEFINE PAD queryes OF _MSYSMENU PROMPT "\<Запросы" ;

                                              COLOR SCHEME 3

DEFINE PAD configur OF _MSYSMENU PROMPT "\<?" COLOR SCHEME 3

*                   "\<Конфигурация"

DEFINE PAD print_conf OF _MSYSMENU PROMPT "\<Hастройки "+;

                                    "печати " COLOR SCHEME 3

ON PAD helpquit OF _MSYSMENU ACTIVATE POPUP hq_popup

ON PAD listmake OF _MSYSMENU ACTIVATE POPUP lm_popup

ON PAD queryes OF _MSYSMENU ACTIVATE POPUP qr_popup

DEFINE POPUP hq_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF hq_popup PROMPT "\<Help"

DEFINE BAR 2 OF hq_popup PROMPT "\<Quit"

ON SELECTION BAR 1 OF hq_popup DO help_proc &&in "pogruz.prg"

ON SELECTION BAR 2 OF hq_popup DO quit_proc &&in "pogruz.prg"

DEFINE POPUP lm_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF lm_popup PROMPT "\<Факультетов"

DEFINE BAR 2 OF lm_popup PROMPT "\<Кафедр"

DEFINE BAR 3 OF lm_popup PROMPT "\<Специальностей"

DEFINE BAR 4 OF lm_popup PROMPT "\<Преподавателей"

DEFINE BAR 5 OF lm_popup PROMPT "\<Потоков"

DEFINE BAR 6 OF lm_popup PROMPT "\<Предметов"

DEFINE BAR 7 OF lm_popup PROMPT "\<Аудиторий"

DEFINE BAR 8 OF lm_popup PROMPT "\<Пар расписания"

ON SELECTION BAR 1 OF lm_popup DO lm_fuc_proc

&&in "pogruz.prg"

ON SELECTION BAR 2 OF lm_popup DO lm_kaf_proc

&&in "pogruz.prg"

ON SELECTION BAR 3 OF lm_popup DO lm_spec_proc

&&in "pogruz.prg"

ON SELECTION BAR 4 OF lm_popup DO lm_prep_proc

&&in "pogruz.prg"

ON SELECTION BAR 5 OF lm_popup DO lm_grup_proc

&&in "pogruz.prg"

ON SELECTION BAR 6 OF lm_popup DO lm_pred_proc

&&in "pogruz.prg"

ON SELECTION BAR 7 OF lm_popup DO lm_aud_proc

&&in "pogruz.prg"

ON SELECTION BAR 8 OF lm_popup DO lm_par_proc

&&in "pogruz.prg"

DEFINE POPUP qr_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF qr_popup PROMPT "\<Расписание преподавателя"

*DEFINE BAR 2 OF qr_popup PROMPT "\<Свободной пары"

ON SELECTION BAR 1 OF qr_popup DO prep_rasp &&in "pogruz.prg"

*ON SELECTION BAR 2 OF qr_popup DO frpar_proc

&&in "pogruz.prg"

wait clear

read cycle valid IsQuit()

clear all

return


Модульность программв FoxPro
Модульность программ

  В FoxPro предусмотрена широкая возможность использования

процедур, которые могут быть как внешними (в виде отдельных

программных файлов), так и внутренними (внутри программы).

Частным случаем процедуры является процедура - функция.

  Эти средства удобно использовать для реализации некоторых

одинаковых процессов обработки данных. Тогда такой процесс

программируется один раз в виде одного из перечисленных выше

средств и вызывается в системе по мере необходимости.

  Кроме того, модульное программирование позволяет разработчику

гораздо лучше ориентироваться в своей системе, а также сделать

более эффективным процесс ее написания и отладки.

  Встречая обращение к процедуре, FoxPro ищет ее в следующей

последовательности:

  1. В текущей процедуре;

  2. В процедурном файле, если он подключен;

  3. Снизу вверх в старших процедурах относительно текущей, если

     они есть;

  4. На диске в виде отдельной программы.

  Рассмотрим средства структуирования.

  Обращение к процедуре выполняется командой

  DO имя_процедуры WITH СПИСОК_ПАРАМЕТРОВ IN файл

  В процедуру могут быть переданы и из нее получены некоторые

величины, указанные в списке параметров после слова WITH. Этими

величинами могут быть не только переменные и константы, но любые

разрешенные величины. В таком случае первой командой в

вызываемой процедуре должна быть команда, воспринимающая их:

PARAMETERS <список параметров> - определяет временные переменные

                                 в виде списка параметров.

  По умолчанию параметры передаются в программы и процедуры по

ссылке, а в функции по значению. Можно передать параметры в

программу или процедуру по значению, если заключить их в скобки.

  По ссылке можно передать в процедуру или функцию целые

массивы. Если UDFPARMS установлено командой SET в REFERENCE, то

можно передать целый массив. Если же эта установка равна VALUE,

будет передан только первый элемент массива.

  Параметры внутри <списка параметров> могут быть именами любых

допустимых временных переменных или массивов. Если значение

переменной или элемента массива изменено в вызываемой программе,

новое значение может быть передано обратно в вызывающую.

  Параметры в <списке параметров> разделяются запятыми. Если в

списке перечислено больше элементов, чем передано, то

оставшимся присваивается значение "ложь" (.F.).

  Функция PARAMETERS() возвращает количество параметров,

переданных в последнюю подпрограмму. Функция полезна, когда

число параметров, передаваемых в процедуру, переменно.

  RETURN [TO MASTER | <выр> TO <имя процедуры>]

  Используется в программах для возврата управления вызывающей

программе или в режим интерпретации команд через окно команд,

либо в MS-DOS.

  Использование команды RETURN в конце программ не обязательно,

поскольку управление автоматически возвращается в место вызова

после выполнения последней команды в программе. При отсутствии

RETURN в функциях пользователя возвращается значение .T.

  Команда RETURN освобождает все локальные (PRIVATE) временные

переменные.

  Если в программе используется команда RETURN с опцией MASTER,

выполняется передача управления вызывающей программе наивысшего

уровня.

  Если используется опция TO <имя процедуры>, то управление

передается названной процедуре.

  Если программа представляет собой пользовательскую функцию,

после команды RETURN следует указать <выражение>. Если

программа, являющаяся пользовательской функцией, вызывается с

помощью команды DO, указанное <выражение> игнорируется.

  PROCEDURE <имя процедуры> указывает начало подпрограммы.

  Каждая отдельная процедура должна начинаться с команды

PROCEDURE <имя процедуры>. Процедуры могут помещаться как в

самой программе, так и в отдельном файле процедур. Программный

файл может содержать до 1170 отдельных подпрограмм.

  Если подпрограммы выделены в отдельный файл, то он должен

открываться до первого обращения к какой-либо процедуре в нем

командой SET PROCEDURE TO <имя>. В каждый момент времени может

быть открыт только один процедурный файл. Для закрытия

процедурного файла используется команда SET PROCEDURE TO без

аргумента.

  Имена процедур могут быть длиной до 8 символов и содержать

буквы, цифры и символы подчеркивания. Имена процедур должны

начинаться с буквы.

  В конце подпрограммы не обязательно помещать оператор RETURN.

Эта команда выдается автоматически при  достижении конца

процедуры, определяемом по концу файла  или по встретившемуся

началу  другой подпрограммы.

PROGRAM([<вырN>]) - возвращает имя текущей выполняемой программы

                    или имя программы, при выполнении которой

                    встретилась ошибка.

  Функция полезна при обработке ошибок. Если установлено SET

FULLPATH OFF, функция PROGRAM() возвращает только имя программы,

если же установлено SET FULLPATH ON, функция PROGRAM()

возвращает имя вместе с маршрутом.

  Hеобязательный числовой аргумент показывает, на сколько

уровней надо вернуться для определения имени выполнявшейся

программы.  Значение этого параметра может изменяться от 1 до N,

где N - допустимый в DO уровень вложенных вызовов программ.

Значение 1 или 0 приведет к возвращению имени головной

программы, вызванной по DO. При отсутствии параметра

возвращается имя текущей выполняемой программы. Если <вырN> не

соответствует числу вложенных уровней, возвращается пустая

строка.

FUNCTION <имя>

  Команда FUNCTION <имя> является оператором внутри программного

или процедурного файла. Она специфицирует начало каждой функции

в программе и идентифицирует ее имя. Имена функций могут

включать до 8 символов. Они должны начинаться с буквы и могут

включать любую комбинацию букв, цифр и символов подчеркивания.

  После описываемой команды должны следовать команды,

составляющие собственно функцию.  После этих  команд можно,  но

не обязательно помещать команду RETURN. Эта команда выполняется

автоматически.

  Функции, определенные пользователем, (User Defined Functions,

UDFs) - это программы, которые возвращают значение в вызывающую

программу (которая выполняет функцию). UDF могут оформляться как

отдельная программа или  как процедура или функция в программе.

UDF не должны иметь те же имена, что и встроенные функции, т.к.

встроенные функции имеют приоритет.

Возвращаемое значение

    Значение может возвращаться как RETURN <выр>. RETURN

    посылает значение обратно в вызывающую программу и является

    последней в UDF выполняемой командой.

Передача параметров

    Данные в функцию могут передаваться в форме параметров.

    Оператор PARAMETERS в UDF определяет локальные имена

    передаваемых данных. Могут передаваться массивы и элементы

    массивов. По умолчанию параметры передаются в UDF по

    значению, но эту установку можно менять с помощью команды

    SET UDFPARMS TO REFERENCE.

Вызов UDF

    UDF вызываются по имени, после которого идут скобки. В

    скобках могут находиться передаваемые функции параметры.

UDF в логических выражениях

    Если одна или несколько UDF используются в логическом

    выражении, они вычисляются слева направо.

UDF в командах

    Ряд команд FoxPro позволяют использовать UDF в своих фразах.

    Используемые во фразах UDF позволяют проверять данные,

    выводить сообщения и т.д.

+---------------------------------+

¦        Пример программы         ¦

+---------------------------------+

  В качестве примера приведем функцию и процедуру. Функция

IsQuit нигде в программе непосредственно не вызывается; она

является VALID - условием, зацикливающим объединяющий READ,

обрабатывающий сообщения от меню. Процедура help_proc, как видно

из комментария к ней, вызывается при выборе пункта меню Help по

команде ON SELECTION BAR 1 OF POPUP hq_popup DO help_proc.

function IsQuit

return IsQuitFlag

*       +---------------------------------------------------+

*       ¦

*       ¦help_proc  ON SELECTION BAR 1 OF POPUP hq_popup    ¦

*       ¦                                                   ¦

*       ¦ Procedure Origin:                                 ¦

*       ¦                                                   ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:    5   ¦

*       ¦ Called By:  ON SELECTION BAR 1 OF POPUP hq_popup  ¦

*       ¦ Prompt:     Help                                  ¦

*       ¦ SniPPet:    1                                     ¦

*       ¦                                                   ¦

*       +---------------------------------------------------+

*

PROCEDURE help_proc

   wait "Help isn't availaible yet.." WINDOW NOWAIT

RETURN


Циклы в FoxPro

                     Организация циклов

                     ==================

  DO WHILE <вырL>

     <команды>

        [LOOP]

     <команды>

        [EXIT]

  ENDDO

  Это команда структурного программирования, обеспечивающая

повторение последовательности команд, заключенной в конструкции

DO WHILE...ENDDO до тех пор, пока заданное условие "истинно".

  Команда DO WHILE открывает структурную процедуру, которая

выполняет последующие команды только пока логическое выражение

оценивается как "истинное".

  Если условие "истинно", выполняются все последующие команды до

ENDDO, LOOP или EXIT. Команды ENDDO и LOOP возвращают управление

команде DO WHILE для следующей оценки "истинности" условия.

Команда EXIT передает управление следующей за ENDDO команде.

Если условие оценивается как "ложное" (.F.), система FoxPro

пропускает все команды, заключенные между DO WHILE и ENDDO и

переходит на выполнение следующей за ENDDO команды.

 FOR <пер> = <вырN1> TO <вырN2> [STEP <вырN3>]

 <команды>

 [EXIT]

 [LOOP]

 ENDFOR

  Выполняет включенные в конструкцию команды указанное число

раз. Временная переменная <пер> используется как счетчик,

определяющий число выполнений.

  Hет необходимости определять эту переменную до команды

FOR...ENDFOR. Числовое выражение <вырN1> задает начальное

значение счетчика, а числовое выражение <вырN2> - окончательное

значение.

  Включенные в конструкцию команды выполняются последовательно,

пока не встретится команда ENDFOR. При этом значение <пер>

увеличивается на значение, задаваемое фразой STEP <вырN3>. Если

эта фраза опущена, счетчик увеличивается на единицу.

  Затем значение счетчика сравнивается с <вырN2>. Если счетчик

МЕHЬШЕ ИЛИ РАВЕH значению <вырN3>, цикл выполняется еще раз. В

противном случае управление передается команде, следующей за

ENDFOR.

  Используемые в команде числовые выражения <вырN1>, <вырN2>,

<вырN3> могут быть как допустимыми числовыми выражениями, так и

числовыми временными переменными. Их значения считываются при

инициализации команды. Их можно менять и внутри цикла, но это не

приведет к изменению числа выполнений цикла. Но следует очень

осторожно менять внутри цикла значение счетчика <пер>.

  Если значение STEP <вырN3> отрицательное, то значение счетчика

всякий раз УМЕHЬШАЕТСЯ на эту величину, и исходное значение

<вырN1> должно быть больше значения TO <вырN2>.

  Команда EXIT прерывает выполнение цикла с передачей управления

команде, следующей за ENDFOR. Она может располагаться в любом

месте между FOR и ENDFOR. Команда LOOP также может быть помещена

в любом месте между FOR и ENDFOR. Она передает управление

команде FOR не дожидаясь команды ENDFOR. Конструкция

FOR...ENDFOR может быть вложенной.

 SCAN [<сфера>] [FOR <вырL1>

  [WHILE <вырL2>]

  [<команды>]

  [LOOP]

  [NOOPTIMIZE]

  [EXIT]

 ENDSCAN

  Команда SCAN просматривает всю базу данных и для каждой

записи, отвечающей заданным условиям, выполняет команды,

заключенные в программную конструкцию, аналогично циклу DO

WHILE. Команда SCAN автоматически наращивает значение указателя

записи и анализирует соответствующую ему запись на

удовлетворение условий. Перед использованием команды SCAN база

данных должна быть открыта.

  По умолчанию  значение <сферы>  ALL. Указание сферы  задает

диапазон  сканируемых записей. Можно включить и  необязательные

фразы FOR  и WHILE для специфицирования условий выполнения

заданных в конструкции команд.

  Если задана фраза FOR, то из записей, входящих в диапазон,

выполняются только те, для которых истинно условие <вырL1>. Если

включена фраза WHILE, то команда SCAN выполняется, пока истинно

<вырL2>.

  Фраза LOOP  может  помещатся  в  любом месте между SCAN и

ENDSCAN. Этот оператор  вызывает немедленный возврат к  команде

SCAN в начала цикла.

  Фраза EXIT  вызывает  прекращение выполнения цикла  и  переход

к команде, непосредственно следующей за ENDSCAN.

  В заключение рассмотри одну специфическую команду:

  RUN  <команда>

  или

  ! <команда>

  Команда RUN выполняет заданную  <команду> ОС или программу с

заданным названием и после их завершения  возвращает

управление   системе FoxPro.  Команду  RUN  можно  применять как

в программах, так и через окно команд.


Работа со строками и преобразование типов в Foxpro
Работа со строками и преобразование типов

    Cтроковые функции. Функции преобразования типов данных

    ======================================================

  Функции анализа:

AT(<вырC1>,<вырC2>[,<вырN>])

  Выполняет поиск позиции, с которой <вырC1> начинается в

<вырC2> и возвращает номер позиции. Если <вырC1> не встречается

в <вырC2>, функция возвращает нулевое значение.

  Если задан третий (необязательный) параметр в виде числового

выражения, то ведется поиск соответствующей по порядку

реализации <вырС1> в <вырС2>.

 <вырС2> может быть текстовым полем.

 ATC(<вырC1>,<вырC2>[,<вырN>])

  Выполняет поиск позиции, с которой <вырC1> начинается в

<вырC2> и возвращает номер позиции. Если <вырC1> не встречается

в <вырC2>, функция возвращает нулевое значение. В отличие от

функции AT(), функция ATC() не чувствительна к регистру

буквенных символов.

  RAT(<вырС1>,<вырС2>[,<вырN>]) - (реверсивное AT) просматривает

                                  <ВырС2>, начиная справа и

                                  двигаясь налево, пытаясь при

                                  этом обнаружить <вырN>-ную по

                                  счету реализацию подстроки,

                                  заданной в <вырС1>.

  Функция возвращает в виде целого числа позицию этой

реализации, если она была найдена. Если <вырС1> не найдено в

<вырС2> заданного числа <вырN> раз, функция возвращает ноль.

  Если <вырN> не задано, по умолчанию принимается 1.

INLIST(<выр1>,<выр2>[,<выр3>...]) - Определяет, содержится ли

                                    выражение <выр1> в перечне

                                    приложений <выр2>, <выр3> и

                                    т.д.  Функция возвращает

                                    истину .T., если <выр1>

                                    обнаружено, и .F. в

                                    противном случае. Все

                                    выражения должны быть одного

                                    типа.

ISDIGIT(<вырC>) - Возвращает .T., если <вырC> начинается с цифры

LEN(<вырC>) - Возвращает длину символьного выражения <вырС>,

              которое может быть символьной строкой, текстовым

              или символьным полем.  Если выражение содержит

              пустую строку, функция возвращает 0.

LIKE(<вырC1>,<вырC2>)

  Функция LIKE() возвращает логическое значение истины .T., если

символьное выражение <вырС1>, содержится в символьном выражении

<вырС2>.

  При применении специальных символов ? обозначает один любой

символ и * обозначает любое число любых символов. Можно

использовать любое число специальных символов в любых позициях

шаблона.

OCCURS(<вырC1>, <вырC2>) - возвращает число, означающее, сколько

                           раз одно символьное выражение

                           встретилость в другом.

  Функции выделения:

LEFT(<вырC>,<вырN>)

  Возвращает указанное число символов, начиная с самого левого

символа в <вырC>.

  Числовое выражение определяет количество символов, которое

нужно выделить из символьного выражения.  Функция LEFT()

позволяет выделить первую часть символьной строки. Если числовое

выражение равно 0 или отрицательно, то функция возвращает

нулевую строку.  Если цифровое значение больше, чем длина

символьного выражения, то функция возвращает всю строку.

RIGHT(<вырC> | <пер>,<вырN>) - возвращает указанное в <вырN>

                               число символов, начиная с самого

                               правого символа в <вырC> или

                               символьной временной переменной.

SUBSTR(<вырC>),<вырN1>[,<вырN2>]) - Функция SUBSTR() "вырезает"

                                    из <вырC> заданное в <вырN2>

                                    число символов, начиная с

                                    номера символа, указанного

                                    значением аргумента <вырN1>.

                                    Первый символ <вырC> имеет

                                    номер 1.  Если число

                                    символов <вырN2> не указано,

                                    то выходная строка

                                    начинается с указанной

                                    позиции и заканчивается

                                    последним символом исходной

                                    строки.

  Функции преобразования:

CHRTRAN(<вырC1>, <вырС2>, <вырС3>)

  Выполняет перекодировку выражения, заданного в <вырС1>,

используя <вырС2> и <вырС3> в качестве таблиц перекодировки.

  Все позиции, занимаемые в <вырС1> первым символом из <вырС2>

заменяются первым символом из <вырС3>, второй символ из <вырС2>

заменяется в <вырС1> вторым символом из <вырС3> и т.д.

  Если <вырС3> содержит меньше символов, чем <вырС2>, то

"лишние" символы из <вырС2> заменяются в <вырС1> пустыми

символами (удаляются).

  Если <вырС3> содержит больше символов, чем <вырС2>, то

"лишние" символы в <вырС2> игнорируются.

LTRIM(<вырC>) - удаляет ведущие пробелы в заданном символьном

                выражении.

TRIM(<вырC>)  - Обрезает хвостовые пробелы из заданной строки

                символов.  Функция идентична функции RTRIM().

ALLTRIM([<вырC>]) - возвращает заданное символьное выражение, у

                    которого отсечены как ведущие, так и

                    завершающие пробелы.

REPLICATE(<вырC>,<вырN>) - повторяет заданное <вырC> столько

                           раз, сколько указано в <вырN>.

SPACE(<вырN>) - возвращает строку символов, состоящую из

                заданного <вырN> числа пробелов.

STRTRAN(<вырC1>,<вырС2>[,<вырС3>][,<вырN1>][,<вырN2>])

  Функция STRTRAN() возвращает строку символов, полученную из

заданного выражения <вырС1>, в котором одна или несколько

реализаций подстроки <вырС2> заменены подстрокой <вырС3>.

  Замена начинается с <вырN1>-ой по счету реализации и

производится <вырN2> раз. Если <вырС3> отсутствует, <вырС2>

заменяется пустой строкой (удаляется). Если не задано <вырN1>,

подразумевается замена с 1 реализации. Отсутствие <вырN2>

подразумевает замену всех реализаций.

STUFF(<вырC1>,<вырN1>,<вырN2>,<вырC2>)

  Модифицирует любую часть строки символов, не требуя изменения

всей строки.

<ВырC1>  - текущая обрабатываемая строка.

<ВырN1>  - номер символа, начиная с которого надо начать замену.

<ВырN2>  - длина подстроки, которая удаляется из исходной строки

           символов и заменяется на <вырC2>.

  Если <вырN2> равно 0, то второе символьное выражение

вставляется в указанное место, и ни один символ из <вырC1> не

удаляется.

  Если <вырC2> является пустой строкой, из <вырC1> будет удалено

указанное <вырN2> число символов без добавления.

TRANSFORM(<выр>,<вырC>) - Позволяет задавать шаблон формата

                          вывода символов и чисел или полей без

                          команды @...SAY.

                            Функция возвращает отформатированные

                          данные.

                          <Выр> - переменная, которую нужно

                                  форматировать.

                          <ВырC> - определение формата для

                                   переменной.

                            Дополнительную информацию можно

                          найти в описании команды @.

PADC(<выр>,<вырN>[,<вырС>])

или

PADL(<выр>,<вырN>[,<вырС>])

или

PADR(<выр>,<вырN>[,<вырС>])

  Возвращают в виде символьной строки заданное первым аргументом

выражение, дополненное третьим аргументом до длины, заданной

вторым аргументом.

  Если длина выражения больше <вырN>, выражение усекается справа

до  заданной величины. Если задан третий аргумент <вырС>, то его

повторения используются для дополнения исходного выражения до

требуемой длины, иначе используется символ пробела.

  PADL() вставляет символы - заполнители слева, PADR() - справа,

а PADC() с обеих сторон.

  Функции обработки мемо - полей:

ATCLINE(<вырC1>,<вырC2>)

  Выполняет поиск <вырC1> в <вырC2> и возвращает номер строки в

виде целого цисла. Если <вырC1> не встречается в <вырC2>,

функция возвращает нулевое значение. В отличие от функции

ATLINE(), функция ATCLINE() не чувствительна к регистру

буквенных символов.

  <вырС2> может быть текстовым полем произвольной длины. Если

<вырС2> текстовое поле, то строка, содержащая искомое символьное

выражение, может быть выделена из него функцией MLINE().

  Существенно, что на число строк в текстовом поле

непосредственно влияет ширина, заданная текстовым полям.

Следовательно, она влияет и на возврат функции ATCLINE(). Ширина

текстовых полей задается командой SET MEMOWIDTH.

ATLINE(<вырC1>,<вырC2>)   - выполняет поиск <вырC1> в <вырC2> и

                            возвращает номер строки в виде

                            целого числа.  Если <вырC1> не

                            встречается в <вырC2>, функция

                            возвращает нулевое значение.

RATLINE(<вырС1>,<вырС2>)  - (реверсивное ATLINE) просматривает

                            <вырС2>, начиная с конца и двигаясь

                            к началу, пытаясь найти последнюю

                            реализацию <вырС1>.

MEMLINES(<текстовое поле>)- возвращает количество строк в memo

                            (текстовом) поле. Это количество

                            строк зависит от текущей установки

                            SET MEMOWIDTH, которое по умолчанию

                            равно 50.

MLINE(<memo поле>, <вырN1> [, <вырN2>]

  Функция MLINE() возвращает указанную строку memo поля. Длина

строки и номер строки memo поля определяется текущим значением

SET MEMOWIDTH.

<вырN1>

        Числовое выражение <вырN1> представляет собой номер

        строки, которую необходимо выдать из memo поля. Если

        <вырN1> равно нулю или больше количества строк memo

        поля, будет получена пустая строка.

<вырN2>

        Числовым выражением <вырN2> является смещение от начала

        строки memo поля. Системная временная переменная _MLINE

        применяется для <вырN2>. _MLINE автоматически

        увеличивается после каждого вызова MLINE().

  Функции конвертирования и распознавания букв:

ISALPHA(<вырC>) - возвращает логическое значение "истина" (.T.),

                  если заданное <вырC> начинается с прописной

                  или строчной буквы.

ISLOWER(<вырC>) - возвращает .T., если <вырC> начинается со

                  строчной (нижнего регистра) буквы.

ISUPPER(<вырC>) - возвращает .T., если <вырC> начинается

                  с прописной (верхнего регистра) буквы.

LOWER(<вырC>)   - Преобразует все прописные буквы <вырC> в

                  строчные. Возвращаемое значение - символьное.

                  Функция не влияет на другие символы.

PROPER(<вырС>)  - возвращает заданное <вырС>, преобразуя в нем

                  первую букву каждого слова в прописную, а

                  остальные в строчные.

UPPER(<вырC>)   - конвертирует строчные буквы в прописные в

                  заданном символьном выражении.

  Функции преобразования типов данных:

ASC(<вырC>) - возвращает код ASCII первого слева символа строки.

CHR(<вырN>) - оценивает заданное <числовое выражение> и

              возвращает символ, у которого код ASCII

              соответствует этому числу (результату выражения).

CTOD(<вырC>)- преобразовывает дату из символьного формата в

              формат даты.

DTOC(<вырD>[,<1>]) - Преобразует заданное выражение в символьную

                     строку.

                       В функции DTOC() можно задать

                     необязательный аргумент, обеспечивающий

                     возврат значения в формате, пригодном  для

                     построения индекса. Это может быть особенно

                     полезно в комбинации с функцией TIME() для

                     упорядочения файла базы данных в

                     хронологическом порядке.

  STR(<вырN1>[,<вырN2>[,<вырN3>]])

  Преобразует числовое выражение в символьную строку.

  Функция конвертирует <вырN1> в строку заданной в <вырN2>

длины.  Если указан аргумент <вырN3>, в символьную строку

включается заданное этим аргументом число десятичных знаков.

Длина строки <вырN2> в этом случае должна учитывать десятичную

точку и соответствующее число десятичных знаков.

  Если в <вырN2> указать длину, которая больше, чем количество

цифр слева от десятичной точки, функция заполняет старшие

позиции пробелами. Если же указанная длина меньше этого числа

цифр, функция возвращает строку звездочек (*), сигнализируя этим

переполнение.

VAL(<вырC>) - Преобразует <вырC> в числовое выражение.

              Преобразование начинается с самого левого символа

              и продолжается до тех пор, пока не встретится

              нецифровой символ или не закончится строка

              символов. Ведущие пробелы игнорируются. Если

              заданное выражение не изображает число,

              возвращается 0.

+-----------------------------+

¦     Пример  программы       ¦

+-----------------------------+

  Уйти красиво в программе бывает даже важнее, чем в жизни.

Поэтому мы и завели процедуру, реагирующую на выбор в меню

пункта Quit. Как Вы, быть может, еще не забыли, у нас 9 баз

данных, размещенных в 10 рабочих областях, где 5 - пустая. Перед

выходом мы, естественно должны их закрыть; если в базе были

сделаны какие - либо изменения, по правилам хорошего тона ее

желательно упаковать.

  Изменялась ли база, мы можем узнать, просмотрев массив

WasChanged, поэтому организуем цикл Просмотр массива - Упаковка

(только если были изменения) - Закрытие базы данных.

  Команда SELECT не может принять в качестве параметра временную

переменную, поэтому воспользуемся описанной ранее функцией

макроподстановки. Для этого переведем номер рабочей области в

строку и подставим в SELECT содержимое этой строки.

  Затем мы восстановим стандартное системное меню, выведем

системной функцией на экран заставку FoxPro, и очистим все READ

- циклы. Для Foundation READ, обрабатывавшего наше меню, мы

взведем флажок выхода - мол, хватит работать, после чего наша

программа благополучно завершится.

*       +----------------------------------------------------+

*

*       є quit_proc  ON SELECTION BAR 2 OF POPUP hq_popup    є

*       є

*       є Procedure Origin:                                  є

*       є                                                    є

*       ¦ From Menu:  POGRUZ.MPR,            Record:    6    ¦

*       ¦ Called By:  ON SELECTION BAR 2 OF POPUP hq_popup   ¦

*       ¦ Prompt:     Quit

*

*       є                                                    є

*

*

PROCEDURE quit_proc

  wait window nowait [ЋзЁбвЄ  а Ў®зЁе ®Ў« бвҐ©]

  for num=1 to 10

    pol=str(num)

    select &pol

    if WasChanged(num)

      pack

      wait window nowait [Упаковка...]

    endif

    use

  endfor

  select 1

  wait window nowait [Подготовка к выходу]

  clear

  set sysmenu to default

  IsQuitFlag=.t.

  clear read

  =sys(2017)

  wait clear

return


Средства управления в стиле Windows в FoxPro
Средства управления в стиле Windows

  В FoxPro появилась группа команд вида @...GET, которые

позволяют создавать совершенно новые объекты пользовательского

интерфейса. С их помощью можно реализовать средства управления и

доступа к данным, аналогичные по форме тем, которые применяются

в среде Microsoft Windows.

  Все объекты в стиле Windows активируются командой READ, что

дает возможность органично соединять GET - объекты всех видов

между собой и сокнами редактирования, реализованными обычными

командами GET, создавая гибкий, удобный и насыщенный

пользовательский интерфейс, сочетающий сразу многие виды

управления и доступа к данным. Это позволяет проектировать очень

компактный интерфейс, когда все средстваа управления находятся

"под рукой" пользователя и он во многом освобожден от

"блуждания" по меню, находящихся в разных экранах и на разных

уровнях прикладной системы.

  Все GET - объекты чувствительны к мыши, и именно при работе с

ней они наиболее эффективны. Далее приводится краткое описание

шаблонов для GET / EDIT - объектов, используемых в программе:

  В PICTURE - шаблонах первым знаком должен быть "@". Сразу за

ним может стоять один из ключей:

  N - команда не завершает READ

  T - команда завершает READ

  H - элементы располагаются горизонтально

  V - элементы располагаются вертикально

  Кнопки - переключатели (Check Boxes)             "*C"

  Невидимые кнопки (Invisible Buttons)             "*I"

  Списки (Lists)                                   "&"

  Кнопки - меню (Popups)                           "^"

  Просто кнопки (Push Buttons)                     "*"

  Радио - кнопки (Radio Buttons)                   "*R"

  Области редактирования (Text Edit Regions)       EDIT

+-----------------------------+

¦     Пример  программы       ¦

+-----------------------------+

  Давать теоретический материал по Windows - подобным элементам

управления мы больше не будем, а лучше покажем их в действии на

конкретном примере.

  Нижеприведенная процедура позволит Вам заполнить /

модифицировать базу данных по преподавателям.

  Перейдя в рабочую область БД по преподавателям, мы для

удобства редактирования добавим в конец пустую запись. Затем,

после необходимых проверок, заполним список названиями и кодами

кафедр. При этом, если БД по кафедрам уже изменялась, ее надо

упаковать, так как команда копирования базы в массив не обращает

внимания на то, что запись может быть пустой или помеченной к

удалению, а занесение такой записи в список кафедр (обычно

имеющей вид .F.) нежелательно. Далее, найдя в списке кафедр ту,

которая соответствует коду в первой записи БД по преподавателям,

мы запоминаем этот номер, чтобы в дальнейшем подсветить его в

списке, но уже на экране.

  Сконструировав список на экране из элементов массива list_kuf,

мы будем заносить выбор в переменную ch_kuf. VALID - функция для

списка будет вызываться при выборе его элемента с двумя

параметрами: кодом кафедры и ее названием. Эта функция код

занесет в базу данных, а название отобразит на экране.

  В поля БД с именами rank и zvan мы будем заносить номер

элемента в соответствующих кнопках - меню, а в поле fio -

информацию из окна редактирования.

  Сбоку экрана создадим серию из 5 кнопок, одна из которых будет

по умолчанию (\!), то есть она будет "нажиматься" по комбинации

Ctrl+Enter, а другая (\?) будет реагировать на нажатие клавиши

Esc. Для отдельной обработки каждой кнопки в VALID - функцию мы

передадим ее номер.

  Свои действия в этой функции мы будем сопровождать

разнообразными проверками: на выход за пределы базы, на пустоту

записи и т.д.

  После формирования всех полей на экране и заполнения их

информацией из первой записи мы отключим системное меню и

организуем цикл обработки сообщений, прерываемый по кнопке

"Выход". Восстановив и отбразив меню, мы очищаем экран и

занимаемся гигиеной: ищем пустые записи и удаляем их из базы.

  Казалось бы, мы сделали уже все, однако фамилию преподавателя

мы ввели, а код - нет! И не надо: код мы сформируем программно,

по одному и тому же алгоритму во всех местах, суть которого

заключается в следующем.

  Если код у нас не сформирован, то в соответствующем поле базы

стоит нолик, поэтому мы ищем в базе запись с нулевым кодом.

Найдя ее, заводим переменную с начальным значением 1 и

наращиваем ее до тех пор, пока она совпадает хотя бы с одним

кодом из уже имеющихся в базе. Главный критерий кода -

уникальность, поэтому, найдя число, для которого нет совпадений

среди уже имеющихся кодов, мы можем его принять за handle для

данной записи, и заменить пустой (нулевой) код этим числом.

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_prep_proc  ON SELECTION BAR 4 OF POPUP lm_popup ¦

*       ¦

*       є Procedure Origin:                                  є

*       є                                                    є

*       є From Menu:  POGRUZ.MPR,            Record:    9    є

*       є Called By:  ON SELECTION BAR 4 OF POPUP lm_popup   є

*       є Prompt:     ЏаҐЇ®¤ ў вҐ«Ґ©                         є

*       є Snippet:

*       є                                                    є

*       УДДДДДДДДДДДДДДДДДДДДДД------------------------------+

*

PROCEDURE lm_prep_proc

  wait window nowait [Инициализация экранных объектов]

  select c

  go bottom

  insert blank

  go top

  *Ввод данных по преподавателю

  *Объявим массив, куда считаем кафедры и их коды

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         "кафедрам" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

  *Поиск кода кафедры

  for _i=1 to reccount()

    if c.kafedra==list_kuf(_i,2)

      ch_kuf=_i

    endif

  endfor

  select c

  WasChanged(3)=.T.

  @1,1  SAY [             Cписок  кафедр             ]

  @3,1  GET ch_kuf FROM list_kuf SIZE 20,40 DEFAULT 1 ;

        VALID funchkuf(list_kuf(ch_kuf,2),list_kuf(ch_kuf,1)) ;

            MESSAGE [Двойным щелчком или нажатием клавиши]+;

            [ Ввод в этом списке производят выбор]

            &&Кидаем код кафедры

  if empty(kafedra)

  else

    @23,1 SAY "ђ Ў®в Ґв ­  Є дҐ¤аҐ : ";

         +list_kuf(ch_kuf,1)+SPACE(65)

  endif

  @1,50 SAY [      „®«¦­oбвм    ]

  @2,50 GET rank ;

  FUNCTION "^ \<ђҐЄв®а;\<Џа®аҐЄв®а;\<„ҐЄ ­;\<‡ ў. Є дҐ¤а®©;";

           +"\<Џа®дҐбб®а;\<„®Єв®а;\<„®жҐ­в;\<в. ЇаҐЇ®¤ ў вҐ«м;";

            +"\<ЂббЁбвҐ­в;\<‹ Ў®а ­в" DEFAULT 10;

            MESSAGE [H ¦ вЁҐ¬ Їа®ЎҐ«  Ё ‚ў®¤  Ё«Ё ]+;

            [г¤Ґа¦Ёў ­ЁҐ¬ ¬лиЁ ў нв®¬ ¬Ґ­о - выбор]

  @5,50 SAY [     Ученое звание  ]

  @6,50 GET zvan FUNCTION "^ \<Профессор;\<Доцент;\<Ст."+;

            " преподаватель;";

            +"\<Ассистент" DEFAULT 4;

            MESSAGE [Hажатием пробела и Ввода или ]+;

            [удерживанием мыши в этом меню - выбор]

  @9,50 SAY "Ф.И.О. преподавателя"

  @10,50 EDIT fio SIZE 2,20,65 SCROLL;

              MESSAGE [Окно редактирования для ввода данных]+;

              [ о преподавателе]

  @13,50 GET knopka FUNCTION "*N \?Выход;\!Следующий;"+;

                             "Предыдущий;Вставить"+;

                             ";Удалить";

        SIZE 1,10,1 VALID fnknopka(knopka) DEFAULT 1

  wait clear

  set sysmenu off

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  *ищем пустые записи и удаляем

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(fio) .or. empty(kafedra) .or. empty(rank) .or.;

       empty(zvan)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

FUNCTION funchkuf

parameters ch_kuf,p2

  replace kafedra with ch_kuf

  @23,1 SAY "Работает на кафедре : "+p2+SPACE(65)

RETURN

FUNCTION fnknopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

      *locate for code=0

      *skip

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select h

  *Поиск кода кафедры

  ch_kuf=0

  for _i=1 to reccount()

    if c.kafedra==list_kuf(_i,2)

      ch_kuf=_i

    endif

  endfor

  select c

  show gets

  if ch_kuf#0

    @23,1 SAY "Работает на кафедре : "+;

              list_kuf(ch_kuf,1)+SPACE(65)

  else

    @23,1 SAY "Работает на кафедре : ????"+SPACE(65)

  endif

RETURN


Процедуры заполнения БД "Расписание занятий
Процедуры заполнения БД "Расписание занятий"

  Для каждого пункта меню нам необходимо определить выполняемые

действия. В нижеследующих процедурах мы будем обслуживать все

вспомогательные базы данных - по преподавателям, предметам и так

далее.

+-----------------------------+

¦     Пример  программы       ¦

+-----------------------------+

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_fuc_proc  ON SELECTION BAR 1 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   10    ¦

*       ¦ Called By:  ON SELECTION BAR 1 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Факультетов                            ¦

*       ¦ Snippet:    4                                      ¦

*       ¦

*       УДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЅ

*

  ќв  Їа®жҐ¤га  ўҐ¤Ґв ®Ўб«г¦Ёў ­ЁҐ Ў §л ¤ ­­ле Ї® д Єг«мвҐв ¬

®зҐ­м ЇаЁ¬ЁвЁў­л¬ ®Ўа §®¬: ¬л ў­ з «Ґ ЇҐаҐе®¤Ё¬ ў а Ў®зго

®Ў« бвм, Ј¤Ґ ®вЄалв  нв  Ў § ,   §атем мы открываем BROWSE -

окно для ее модификации. Изменять мы позволяем только одно поле

- name (название кафедры), так как ее код мы сформируем

программно. Вместо имени поля, как это делает BROWSE по

умолчанию, мы выводим надпись "Название факультета", а сама

команда BROWSE выполняется в специально определенном ранее окне

с именем BrowseWin. По окончании сеанса редактирования записей

мы проделываем традиционные действия - помечаем к удалению

имеющиеся, но незаполненные записи и формируем коды для новых

записей.

PROCEDURE lm_fuc_proc

  select f

  WasChanged(6)=.T.

  browse fields name :h="Hазвание факультета" WINDOW BrowseWin;

         TITLE "База данных по факультетам"+;

         " Cегодня - "+DTOC(DATE())

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_kaf_proc  ON SELECTION BAR 2 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,

*       є Called By:  ON SELECTION BAR 2 OF POPUP lm_popup   є

*       є Prompt:     Љ дҐ¤а                                 є

*       є Snippet:    5                                      є

*       є

*       +----------------------------------------------------+

*

  Структура базы данных по кафедрам ничем не отличается от

структуры БД по факультетам, поэтому и процедуры их обслуживания

практически идентичны.

PROCEDURE lm_kaf_proc

  select h

  WasChanged(8)=.T.

  browse fields name :h="Hазвание кафедры" WINDOW BrowseWin;

         TITLE "База данных по кафедрам"+;

         " Cегодня - "+DTOC(DATE())

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_spec_proc  ON SELECTION BAR 3 OF POPUP lm_popup ¦

*

*       є Procedure Origin:                                  є

*       є                                                    є

*       є From Menu:  POGRUZ.MPR,            Record:   12    є

*       є Called By:  ON SELECTION BAR 3 OF POPUP lm_popup   ¦

*       ¦ Prompt:    ЇҐжЁ «м­®бвҐ©                         є

*       є Snippet:    6                                      є

*       є                                                    є

*       УДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЅ

*

  Љ ¦¤ п бЇҐжЁ «м­®бвм ЇаЁўп§ ­  Є Є Є®¬г - «ЁЎ® д Єг«мвҐвг,

Ї®нв®¬г ¬л ¤®«¦­л нв® б¤Ґ« вм Ё ў Ў §Ґ. „«п нв®Ј® ў Ѓ„ Ї®

бЇҐжЁ «м­®бвп¬ § ­®бЁвбп Є®¤ д Єг«мвҐв  Ё ­ §ў ­ЁҐ

бЇҐжЁ «м­®бвЁ.

  ЏҐаҐ©¤п ў а Ў®зго ®Ў« бвм Ѓ„ Ї® бЇҐжЁ «м­®бвп¬, ¬л ¤«п

гдобства редактирования добавим в конец пустую запись. Затем,

после необходимых проверок, заполним массив названиями и кодами

факультетов. При этом, если БД по факультетам уже изменялась, ее

надо упаковать, так как команда копирования базы в массив не

обращает внимания на то, что запись может быть пустой или

помеченной к удалению, а занесение такой записи в список

факультетов (обычно имеющей вид .F.) нежелательно. Далее, найдя

в списке факультетов тот, который соответствует коду в первой

записи БД по специальностям, мы запоминаем этот номер, чтобы в

дальнейшем подсветить его в списке на экране.

  Все наши действия мы будем проводить не на экране, а в окне,

которое мы определим и активизируем так, чтобы создаваемы нами

экранные объекты отображались на экране только после повторной

активации окна. Это делается для того, чтобы прорисовка окна,

кнопок, списка и т.д., выполняемые FoxPro в процессе создания

экранных объектов очень медленно, не были видны.

  Сконструировав список на экране из элементов массива list_fас,

мы будем заносить выбор в переменную ch_fас. VALID - функция для

списка будет вызываться при выборе его элемента с двумя

параметрами: кодом факультета и его названием. Эта функция код

занесет в базу данных, а название отобразит на экране.

  В поле БД с именем name занесем информацию из окна

редактирования.

  Сбоку экрана создадим серию из 5 кнопок, одна из которых будет

по умолчанию (\!), то есть она будет "нажиматься" по комбинации

Ctrl+Enter, а другая (\?) будет реагировать на нажатие клавиши

Esc. Для отдельной обработки каждой кнопки в VALID - функцию мы

передадим ее номер. Свои действия в этой функции мы будем

сопровождать разнообразными проверками: на выход за пределы

базы, на пустоту записи и т.д.

  После формирования всех полей на экране и заполнения их

информацией из первой записи мы отключим системное меню и

организуем цикл обработки сообщений, прерываемый по кнопке

"Выход". Восстановив и отбразив меню, мы удаляем окно и

занимаемся гигиеной: ищем пустые записи и удаляем их из базы.

PROCEDURE lm_spec_proc

  select g

  WasChanged(7)=.T.

  *browse fields name :h="Hазвание специальности";

*    WINDOW BrowseWin;

        * TITLE "База данных по специальностям"+" Cегодня -

        *"+DTOC(DATE())

  wait window nowait [Инициализация оконной подсистемы...]

  append blank

  go top

  select f&&6

  if WasChanged(6)

    pack

    WasChanged(6)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " факультетам" window;

     NOWAIT

    return

  endif

  dimension list_fac(reccount(),2)

  copy to array list_fac

  *Поиск кода

  for _i=1 to reccount()

    if g.fac==list_fac(_i,2)

      ch_fac=_i

    endif

  endfor

  select g

        DEFINE WINDOW sd3 ;

                FROM INT((SROW()-19)/2),INT((SCOL()-50)/2) ;

                TO INT((SROW()-19)/2)+18,INT((SCOL()-50)/2)+49 ;

                TITLE "База данных по специальностям" ;

                FLOAT NOCLOSE SHADOW DOUBLE COLOR SCHEME 1

        ACTIVATE WINDOW sd3 NOSHOW

@ 2,1 GET ch_fac PICTURE "@&N" FROM list_fac SIZE 13,27;

                 DEFAULT 1 VALID funchfac(list_fac(ch_fac,2),;

                 list_fac(ch_fac,1)) ;

            MESSAGE [Двойным щелчком или нажатием клавиши]+;

            [ Ввод в этом списке производят выбор];

                COLOR SCHEME 9

@ 0,5 SAY "Список факультетов"

@ 16,1 SAY "Факультет :"

  if empty(fac)

  else

    @16,13 SAY list_fac(ch_fac,1)+SPACE(65)

  endif

@ 0,33 SAY "Cпециальность"

@ 2,32 EDIT name ;

        SIZE 2,15,65 ;

        DEFAULT "" SCROLL;

        MESSAGE [Окно редактирования для ввода данных о]+;

        [ специальности]

@ 5,33 GET knopka ;

        PICTURE "@*VN \?Выход;\!Следующий;Предыдущий;Вставить"+;

        " ;Удалить" ;

        SIZE 1,12,1 VALID f5knopka(knopka) DEFAULT 1

set sysmenu off

        ACTIVATE WINDOW sd3

  wait clear

READ CYCLE MODAL

RELEASE WINDOW sd3

set sysmenu on

set sysmenu automatic

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name) .or. empty(fac)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

FUNCTION funchfac

parameters ch,p2

  replace fac with ch

  @16,13 SAY p2+SPACE(65)

RETURN

FUNCTION f5knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

      ch_fac=fac

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select f&&6

  ch_fac=0

  *Поиск кода

  for _i=1 to reccount()

    if g.fac==list_fac(_i,2)

      ch_fac=_i

    endif

  endfor

  select g

  show gets

  if ch_fac#0

    @16,13 SAY list_fac(ch_fac,1)+SPACE(65)

  else

    @16,13 SAY "????"+SPACE(65)

  endif

RETURN

*       +----------------------------------------------------+

*       ¦

*       є lm_pred_proc  ON SELECTION BAR 6 OF POPUP lm_popup є

*       є                                                    є

*       є Procedure Origin:                                  є

*       є                                                    є

*       є From Menu:  POGRUZ.MPR,            Record:   13    є

*       є Called By:  ON SELECTION BAR 6 OF POPUP lm_popup   є

*       є Prompt:     ЏаҐ¤¬Ґв®ў                              ¦

*       ¦ Snippet:    7                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

  Для заполнения БД по предметам мы могли бы воспользоваться и

@...GET - объектами, но не менее красиво это можно сделать одной

из разновидностей команды BROWSE - CHANGE. Если BROWSE

предъявляет данные в виде таблицы, то CHANGE - в виде своего

рода набора карточек: каждая запись выводится в несколько строк,

в каждой строке находится поле в виде "название - содержимое".

  По закрытию окна редактирования мы, как обычно, удаляем пустые

записи (пустой будем в дальнейшем считать запись, у которой

незаполнено хотя бы одно поле) и формируем коды для новых

специальностей.

PROCEDURE lm_pred_proc

  *(code N(4,0),name C(65),link L,chas_week N(3,1),chas_all

  *N(4,1))

  select a

  WasChanged(1)=.T.

  change title [База данных по предметам Сегодня - ]+;

         DTOC(DATE());

         fields name;

                :h='Hазвание предмета',;

                chas_week:h=[Часов в неделю],;

                chas_all:h="Общее число часов",;

                link:h="Связь с аудиторией" WINDOW BrowseWin

  *ищем пуcтые записи и удаляем

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(name) .OR. empty(chas_week) .OR. empty(chas_all)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_aud_proc  ON SELECTION BAR 7 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Orig@n:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   14    ¦

*       ¦ Called By:  ON SELECTION BAR 7 OB POPUP lm_popup   ¦

*       ¦ Prompt:     Аудиторий                              ¦

*       ¦ Snippet:    8                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

  Окно, в общем то, вещь достаточно условная - с помощью

"изобразительных средств" самого FoxPro (@... - команд) его

можно сымитировать с достаточно высокой степенью достоверности,

что мы и попробуем сделать на примере обслуживания БД по

аудиториям.

  Вначале мы перейдем в рабочую область, где расположена база

данных по аудиториям, добавим в конец пустую запись (для

удобства редактирования) и установим признак изменения данных в

массиве WasChanged. Заметим, что признак специализации аудитории

у нас поле логическое, а в случае использования управляющих

элементов в стиле Windows мы вынуждены использовать только

числовые поля; поэтому заведем в программе временную переменную,

в которой будем помещать 1, если аудитория не специализирована и

двойку при специализации.

  На экране командой @1,5 TO 23,69 DOUBLE мы начертим окно без

тени с двойной рамкой, в котором расположим поля ввода номеров

корпуса и аудитории, выпадающее меню для выбора емкости

аудитории и две радио - кнопочки для выбора типа аудитории

(признака специализации). Для корректной замены данных по

специализации в базе мы придадим этим кнопкам VALID - функцию

swap, преобрзующих число из переменной temp в логическое

значение по описанному выше признаку.

  В отдельном окошке у нас будет особое мемо - поле, содержимое

которого не контролируется, но всегда отображается.

  После отработки цикла сообщений мы, как обычно, очищаем базу

от пустых записей и формируем код.

PROCEDURE lm_aud_proc

  *(code n(4,0),korpus n(2,0),aud n(3,0),emkost n(3,0),

  *special L,comment M)

  wait window nowait [Инициализация экрана...]

  select b

  go bottom

  insert blank

  go top

  WasChanged(2)=.T.

  if special=.t.

    temp=2

  else

    temp=1

  endif

  @1,5 TO 23,69 DOUBLE

  @3,11 SAY [Hомер корпуса]

  @3,31 GET korpus MESSAGE [Здесь вводится номер корпуса,]+;

                           [не равный нулю]

  @3,41  SAY [Hомер аудитории]

  @3,61 GET aud MESSAGE [Здесь вводится номер аудитории,]+;

                        [не равный нулю]

  @5,12 SAY [    Емкость]

  @6,13 GET emkost FUNCTION "^ \<Поток;\<Группа;\<Подгруппа";

              DEFAULT 3;

              MESSAGE [Hажатием пробела и Ввода или ]+;

              [удерживанием мыши в этом меню - выбор]

  @9,8 SAY [      Специализация :]

  @11,9 GET temp FUNCTION '*R \<Hе'+;

        ' специализирована;\<Специализирована';

        DEFAULT 1 VALID swap(temp);

        MESSAGE [Hажатием мыши или движением курсора - выбор]

  @14,8 SAY "Описание аудитории (если нужно)"

  @16,7 edit comment SIZE 6,40,3000 SCROLL;

            MESSAGE [Можете описать здесь специфические]+;

            [ особенности данной аудитории]

  @7,55 GET knopka FUNCTION "*N \?Выход;\!Следующий;"+;

        "Предыдущий;Вставить;Удалить";

        SIZE 3,10,2 VALID f3knopka(knopka) DEFAULT 1

  set sysmenu off

  wait clear

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(korpus) .or. empty(aud) .or. empty(emkost)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

    for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

  wait clear

RETURN

FUNCTION swap

parameters t

  if t=1

    replace special with .f.

  else

    replace special with .t.

  endif

RETURN

FUNCTION f3knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  if special=.f.

    temp=1

  else

    temp=2

  endif

  show gets

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_grup_proc  ON SELECTION BAR 5 OF POPUP lm_popup ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   16    ¦

*       ¦ Called By:  ON SELECTION BAR 5 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Потоков                                ¦

*       ¦ Snippet:    10                                     ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

  Пускай название нижеследующей процедуры Вас не обманывает -

иметь дело мы будем с базой данных по потокам. Ее экранное

представление будет достаточно традиционно - список, три

выпадающих меню и набор стандартных кнопок для перемещения по

базе.

PROCEDURE lm_grup_proc

  select d

  WasChanged(4)=.t.

  *(code n(5,0),special n(3,0),kurs n(1,0),delta n(2,0))

  *действия по вводу

  * специальность - списками, курс и смещение - popup

  wait window nowait [Инициализация экрана...]

  append blank

  go top

  *Ввод данных по группе

  *Объявим массивы, куда считаем специальности и их коды

  select g&&7

  if WasChanged(7)

    pack

    WasChanged(7)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " специальностям" window;

     NOWAIT

    return

  endif

  dimension list_spec(reccount(),2)

  copy to array list_spec

  *Поиск кода специальности

  for _i=1 to reccount()

    if d.special==list_spec(_i,2)

      ch_spec=_i

    endif

  endfor

  select d

  @1,1  SAY [         Cписок  специальностей             ]

  @2,1  GET ch_spec FROM list_spec SIZE 20,40 DEFAULT 1 ;

  VALID funchspec(list_spec(ch_spec,2),list_spec(ch_spec,1)) ;

            MESSAGE [Hажатием Ввода или щелчком мыши в этом]+;

            [ списке - выбор]

            &&Кидаем код специальности

  if empty(special)

    @23,1 SAY "Специальность : ????"+SPACE(65)

  else

    @23,1 SAY "Специальность : "+list_spec(ch_spec,1)+SPACE(65)

  endif

  @2,48 SAY [Курс]

  @1,53 GET kurs;

  FUNCTION "^ \<Первый;\<Второй;\<Третий;\<Четвертый;\<Пятый";

              DEFAULT 1 MESSAGE [Hажатием пробела и Ввода ]+;

              [или удерживанием мыши в этом меню - выбор]

  @4,48 SAY [Групп в потоке]

  @5,50 GET grpcount ;

      FUNCTION "^ \<Одна;\<Две;\<Три;\<Четыре;\<Пять;"+;

            "\<Шесть;\<Семь;\<Восемь;\<Девять" DEFAULT 1;

        MESSAGE [Hажатием пробела и Ввода или удерживанием ]+;

                [мыши в этом меню - выбор]

  @8,48 SAY "Cмещение по времени"

  @9,50 GET delta ;

  FUNCTION "^ \<Отсутствует;\<Одна пара;\<Две пары;"+;

            "\<Три пары;\<Четыре пары;\<Пять пар" DEFAULT 1;

    MESSAGE [Hажатием пробела и Ввода или удерживанием мыши ]+;

            [в этом меню - выбор]

  @14,50 GET knopka FUNCTION;

         "*N \?Выход;\!Следующий;Предыдущий;Вставить;Удалить";

        SIZE 1,10,1 VALID f2knopka(knopka) DEFAULT 1

  set sysmenu off

  wait clear

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(special) .or. empty(kurs) .or. empty(grpcount);

       .or. empty(delta)

      delete

    endif

    skip

  enddo

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      num2=1

      tr=.f.&&.t. - когда найдем

      do while !tr

        locate for code==num2

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num2=num2+1

      enddo

      go _i

      replace code with num2

    endif

  endfor

  wait clear

RETURN

FUNCTION funchspec

parameters ch_s,p2

  replace special with ch_s

  @23,1 SAY "Специальность : "+p2+SPACE(65)

RETURN

FUNCTION f2knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

   CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select g&&7

  ch_spec=0

  *Поиск кода специальности

  for _i=1 to reccount()

    if d.special==list_spec(_i,2)

      ch_spec=_i

    endif

  endfor

  select d

  if ch_spec#0

    @23,1 SAY "Специальность : "+;

                  list_spec(ch_spec,1)+SPACE(65)

  else

    @23,1 SAY "Специальность : ????"+SPACE(65)

  endif

  show gets

RETURN


Обслуживание БД "Расписание занятий
Обслуживание основной БД "Расписание занятий"

  Основная база данных, содержащая расписание занятий, своими

полями имеет только коды, и это не удивительно - текстовое

представление этих полей хранится во вспомогательных базах.

+-----------------------------+

¦     Пример  программы       ¦

+-----------------------------+

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_par_proc  ON SELECTION BAR 8 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   15    ¦

*       ¦ Called By:  ON SELECTION BAR 8 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Пар расписания                         ¦

*       ¦ Snippet:    9                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

  Нижеследующая процедура - ключевая во всей нашей работе. При

заполнении расписания очень желательно, чтобы вид экрана был

максимально приближен к привычной нам (а особенно диспетчеру)

форме; это позволит, возможно, сделать рассматриваемую нами

программу используемой практически, а не только в качестве

учебного пособия. Для этого мы сделаем наш экран рамкой,

"ползущей" вверх/вниз по дням и влево/вправо по группам; тогда

мы сможем на нем видеть обе подгруппы для одной группы и один

день, то есть в общей сложности 2*5=10 клеточек расписания

(если брать в расчет только одну неделю). В каждой клеточке у

нас будет 3 поля: предмет, преподаватель и аудитория.

  За единицу редактирования возьмем один поток. Тогда нам

понадобится предварительно ввести сведения о факультете,

специальности на этом факультете, потоке по специальности и виде

недели (числитель/знаменатель).

  После того, как все эти данные нам известны, мы можем выдать

на экран окно с EDIT - полями и запустить цикл обработки

сообщений от кнопок. В процессе работы с окном расписания мы

производим переопределение функциональных клавиш F4 - для

изменения данных в поле и F8 - для удаления нескольких полей.

Для того, чтобы по окончания программы у нас по этим клавишам

выполнялись стандартные действия, нам необходимо перед командой

READ сохранить значения функциональных клавиш во внутреннем

стеке FoxPro, а после того, как она отработает - восстановить.

PROCEDURE lm_par_proc

  *копируем список кафедр

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "База данных по кафедрам пуста" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

  WasChanged(9)=.t.&&Признак необходимости упаковки в связи с

                   &&изменением расписания

  *потом это надо будет убрать и заполнить нормально

  *Вначале запросим факультет, специальность и поток

  *копируем список факультетов

  select f&&6

  if WasChanged(6)

    pack

    WasChanged(6)=.f.

  endif

  if reccount()==0

    =bell()

    wait "База данных по факультетам пуста" window NOWAIT

    return

  endif

  dimension list_fac(reccount(),2)

  copy to array list_fac

  curfac=0

  do while curfac==0

    =facfn()

  enddo

  *скопируем список специальностей на данном факультете

  select g

  if WasChanged(7)

    pack

    WasChanged(7)=.f.

  endif

  mcount=0

  locate for fac==curfac

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа этом факультете специальностей нет; заполните]+;

    [ базу ] window;

     NOWAIT

    curspec=0

    spectxt=[]

    curfac=0

    factxt=''

    return

  endif

  dimension list_spec(mcount,2)

  mcount=0

  locate for fac==curfac

  DO WHILE FOUND()

    mcount=mcount+1

    list_spec(mcount,1)=name

    list_spec(mcount,2)=code

    CONTINUE

  ENDDO

  curspec=0

  do while curspec==0

    =specfn()

  enddo

  *В список потоков занесем только те, которые есть на

  *факультете по данной специальности

  select d&&4

  if WasChanged(4)

    pack

    WasChanged(4)=.f.

  endif

  mcount=0

  locate for special==curspec

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [По данной специальности на этом факультете ]+;

         [потоков нет!] window NOWAIT

    curspec=0

    spectxt=[]

    curfac=0

    factxt=''

    return

  endif

  dimension list_pot(mcount,2)

  mcount=0

  locate for special==curspec

  DO WHILE FOUND()

    mcount=mcount+1

    list_pot(mcount,1)=STR(kurs,1)+' курс'

    list_pot(mcount,2)=code

    CONTINUE

  ENDDO

  curpot=0

  do while curpot==0

    =potfn()

  enddo

  *Запоmним количество групп

  locate for code==curpot

  m.grpcount=grpcount

  select i

  week=0

  do while !inlist(week,1,2)

    wait [Выбирайте : 1 - числитель, 2 - знаменатель] window;

         to str_buf

    week=val(str_buf)

  enddo

  wait window nowait [Инициализация расписания...]

  day=1

  curgrp=1

  for _i=1 to 30

    marazm(_i,1)=0

    marazm(_i,2)=[]

  endfor

        DEFINE WINDOW mainwindow ;

                FROM 1, 0 ;

                TO 24,77;

                TITLE ALLTRIM(spectxt)+[ , ]+ALLTRIM(pottxt)+[ ]+;

                ALLTRIM(factxt)+[ факультет] ;

                FOOTER "F4 - Изменить, F8 - Удалить пару" ;

                NOCLOSE ;

                SHADOW ;

                SYSTEM ;

                COLOR SCHEME 8

        ACTIVATE WINDOW mainwindow NOSHOW

    @ 0,16+22 SAY [Группа]

    @ 1,16+5  SAY [Подгруппа 1]

    @ 2,16+23 to 20,16+23

    @ 1,16+35  SAY [Подгруппа 2]

  _j=1

  for _i=0 to 4

    sho=1+_i-5*int(_i/5)

    @ _i*4+3,10 SAY str(sho,1)

    @ _i*4+2,16 EDIT marazm(_j,2) ;

        SIZE 1,21,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+3,22 EDIT marazm(_j,2) ;

        SIZE 1,15,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+4,18 EDIT marazm(_j,2) ;

        SIZE 1,19,65 ;

        DEFAULT []

    _j=_j+1

  endfor

  _j=16

  for _i=0 to 4

    sho=1+_i-5*int(_i/5)

    @ _i*4+3,10+35 SAY str(sho,1)

    @ _i*4+2,16+35 EDIT marazm(_j,2) ;

        SIZE 1,21,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+3,22+35 EDIT marazm(_j,2) ;

        SIZE 1,15,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+4,18+35 EDIT marazm(_j,2) ;

        SIZE 1,19,65 ;

        DEFAULT []

    _j=_j+1

  endfor

  @ 21,0 GET knopka ;

        PICTURE "@*HN Влево;Вправо;Вверх;Вниз;\?Выход" ;

        SIZE 1,14,1 ;

        DEFAULT 1 VALID knopkafn(knopka) DISABLE

    @ 0,16+30 SAY str(curgrp,1)

    @ 0,0 SAY days_arr(day)

        ACTIVATE WINDOW mainwindow

  push key

  set sysmenu off

  first=.t.

  READ CYCLE MODAL ACTIVATE read_activate()

  set sysmenu on

  set sysmenu automatic

  pop key

  RELEASE WINDOW mainwindow

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(gruppa) .or. empty(para) .or. empty(predmet) ;

       .or. empty(audit) .or. empty(prepod)

      delete

    endif

    skip

  enddo

  wait clear

RETURN

  Команда READ в предыдущей процедуре имеет у нас три параметра:

CYCLE для многократного обхода GET / EDIT - полей, MODAL для

того, чтобы из этог окна нельзя было перейти в меню или другое

окно и ACTIVATE, используемый в данном случае для начального

занесения данных в поля, включения кнопок и переопределения

функциональных клавиш. Кроме того, для быстрого перемещения

определим 4 клавиши - акселератора, по которым выполняется

функция обработки сообщений от кнопок с различными параметрами.

function read_activate

  if first

    =refresh()

    on key label f4 do f4proc

    on key label f8 do f8proc&&erasing

    on key label ctrl+LEFTARROW do knopkafn with 1

    on key label ctrl+RIGHTARROW do knopkafn with 2

    on key label PgUp do knopkafn with 3

    on key label PgDn do knopkafn with 4

    show get knopka enable

    wait clear

    first=.f.

  endif

return

  Процедура освежения экрана, вызывающаяся при начальной

инициализации и любых других изменениях базы / экрана, имеет

следующий алгоритм:

  Прежде всего мы заносим на экран номер группы,день недели и

очищаем все поля нашего массива, отвечающего за EDIT - поля в

окне, после чего начинаем заносить данные по лабораторным

занятиям, делая это в очень простом цикле по номеру подгруппы.

Найдя запись по номеру подгруппы, мы вычислем ее номер и

проверяем, в этот ли день проходит данная пара. Если день и

неделя совпали, то мы переписываем все необходимые нам коды во

временные переменные и по этим кодам в соответствующих базах

данных формируем текстовые поля.

  Текстовое поле для аудитории мы формируем из номера этой

аудитории и номера корпуса, в которой она располагается, а текст

для преподавателя - с помощью специальной функции, которая

формирует строку вида "Доц. Полищук А.П." из полного имени и

кода ученого звания.

  Коды и текстовые поля мы заносим в соответствующие элементы

массива. Между номером подгруппы, номером пары, номером EDIT -

поля в паре (фактически определяющим, кокого рода данные

находятся в данном поле) и индексом элемента в массиве

существует взаимнооднозначное соответствие, устанавливаемое

функцией GetObject. В дополнительный, третий столбец строки

предмета мы заносим цифру 3 - признак лабораторного занятия.

Сформировав полностью данные в массиве, мы командой show get

принудительно отображаем их в окне.

  Данные по практичкам и лекциям заносятся также, за одним

исключением: на экране светятся у нас две подгруппы, а данные по

практичкам / лекциям идут на группу / поток, поэтому для

редактирования мы делаем доступными данные лишь в одной

подгруппе - во второй они будут изменяться автоматически. Во

избежание случайного ручного изменнения в поля - дубликаты

заносятся только тексты, без кодов, и редактирование их

запрещено.

procedure refresh

  wait window nowait [Освежение экрана...]

  @ 0,16+30 SAY str(curgrp,1)

  @ 0,0 SAY days_arr(day)

  for _i=1 to 30

    marazm(_i,1)=0

    marazm(_i,2)=[]

    marazm(_i,3)=0

    show get marazm(_i,2) enable

  endfor

  *отобразим сделанные изменения

  *(gruppa N(5,0),para N(3,0),predmet N(4,0),audit *

  *N(4,0),prepod N(4,0))

  *код пары формируется следующим образом :

  * неделя - 1 числитель,2 знаменатель

  * день -1 .. 5

  * code=week*100+day*10+number

  * номер пары - 1..5

  *код группы = код потока*100+номер группы*10+номер подгруппы

  *1 - занесем все лабораторки

  for _i=1 to 2 &&для обеиx подгрупп

    locate for gruppa==curpot*100+curgrp*10+_i

    DO WHILE FOUND()

      *Выделим номера недели,дня и пары

      number=para-int(para/10)*10

      if (para-int(para/100)*100-number)/10==day ;

         .and. week==int(para/100)

        curpred=predmet

        curaud=audit

        curprep=prepod

        *Занесем данные по предмету

        select a

        locate for code==curpred

        predtxt=alltrim(name)

        select b

        locate for code==curaud

          if aud#0 .and. korpus#0

            audtxt=[Aуд.]+str(aud,3)+[, корп.]+str(korpus,2)

          else

            audtxt=[]

          endif

        select c

        locate for code==curprep

        preptxt=getprepname(fio,zvan)

        select i

        marazm(getobject(_i,number,1),1)=curpred

        &&Предмет

        marazm(getobject(_i,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(_i,number,1),2) enable

        marazm(getobject(_i,number,1),3)=3 &&Признак лабы

        marazm(getobject(_i,number,2),1)=curprep

        &&Преподававтель

        marazm(getobject(_i,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(_i,number,2),2) enable

        marazm(getobject(_i,number,3),1)=curaud

        &&Аудитория

        marazm(getobject(_i,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(_i,number,3),2) enable

      endif

      CONTINUE

    enddo

  endfor

  *2 занесем практички - на обе подгруппы

  && and lections too

  for _j=1 to 0 step -1

    locate for gruppa==curpot*100+iif(_j==1,curgrp,0)*10

    DO WHILE FOUND()

      *Выделим номера недели,дня и пары

      number=para-int(para/10)*10

      if (para-int(para/100)*100-number)/10==day ;

         .and. week==int(para/100)

        * - заносим в расписание предмет препод и аудиторию

        curpred=predmet

        curaud=audit

        curprep=prepod

        *Занесем данные по предмету

        select a

        locate for code==curpred

        predtxt=alltrim(name)

        select b

        locate for code==curaud

          if aud#0 .and. korpus#0

            audtxt=[Aуд.]+str(aud,3)+[, корп.]+str(korpus,2)

          else

            audtxt=[]

          endif

        select c

        locate for code==curprep

        preptxt=getprepname(fio,zvan)

        select i

        marazm(getobject(1,number,1),1)=curpred

        &&Предмет

        marazm(getobject(1,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(1,number,1),2) enable

        marazm(getobject(1,number,1),3)=1+_j

        &&Признак практички или лекции

        marazm(getobject(1,number,2),1)=curprep

        &&Преподававтель

        marazm(getobject(1,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(1,number,2),2) enable

        marazm(getobject(1,number,3),1)=curaud

        &&Аудитория

        marazm(getobject(1,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(1,number,3),2) enable

        *отобразим дубликат серым цветом

        marazm(getobject(2,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(2,number,1),2) disable

        marazm(getobject(2,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(2,number,2),2) disable

        marazm(getobject(2,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(2,number,3),2) disable

      endif

      CONTINUE

    enddo

  endfor

  wait clear

return

  Думаю, что нижеследующая функция достаточно прозрачна, чтобы

ее комментировать.

function knopkafn

PARAMETERS kn

  DO CASE

    case kn=1&&Влево

      if curgrp#1

        curgrp=curgrp-1

      endif

    case kn=2&&Вправо

      if curgrp#m.grpcount

        curgrp=curgrp+1

      endif

    case kn=3&&Вверх

      if day#1

        day=day-1

      endif

    case kn=4&&Вниз

      if day#5

        day=day+1

      endif

    CASE kn=5&&Выход

      clear read

      return

  endcase

  =refresh()

return

  По нажатию клавиши F8 мы удаляем данные по паре. Делать будем

это так:

  В FoxPro есть специальная системная переменная _CUROBJ,

в которой хранится номер текущего GET - объекта. Всего на экране

5*2*3==30 EDIT - полей и 5 кнопок. Нам интересен лишь тот

случай, когда данная функциональная клавиша нажата на одном из

полей редактирования. По его номеру мы с помощью функции GetWhat

определяем тип поля, в котором мы находимся - то ли это поле с

данными по предмету, то ли по преподавателю, то ли по аудитории,

и в зависимости от типа поля корректируем переменную _i так,

чтобы она указывала на поле предмета.

  Сфорировав индекс поля предмета в массиве, мы проверяем код на

пустоту - а занесены ли там данные по предмету, и, если они

присутствуют, а пользователь уверен в том, что удалить эту пару

ему жизненно необходимо, мы, в зависимости от типа пары

формируем код группы и пары для поиска. Найдя искомую пару в БД,

мы ее удаляем, освежаем экран и восстанавливаем позицию курсора

на том же месте, с которого начали.

procedure f8proc

  if _CUROBJ<31

    SAVEOBJ=_CUROBJ

    DO CASE

      CASE getwhat(SAVEOBJ)=1           &&Предмет

        _i=SAVEOBJ

      CASE getwhat(SAVEOBJ)=2           &&Преподаватель

        _i=SAVEOBJ-1

      CASE getwhat(SAVEOBJ)=3           &&Аудитория

        _i=SAVEOBJ-2

    ENDCASE

    if marazm(_i,1)#0

      if erase_ch()

        DO CASE

          CASE marazm(_i,3)==1

            _j=curpot*100

          CASE marazm(_i,3)==2

            _j=curpot*100+curgrp*10

          CASE marazm(_i,3)==3

            _j=curpot*100+curgrp*10+getpg(SAVEOBJ)

        ENDCASE

        select i

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

                  .and. gruppa==_j

        delete

        *обновим экран

        =refresh()

      endif

    endif

    _CUROBJ=SAVEOBJ

  endif

return

  Эта очень простая функция фомирует на экране окно с запросом

об удалении и возвращает решение пользователя, преобразовав код

выбранной клавиши в логическое значение.

function erase_ch

        DEFINE WINDOW werase_ch ;

                FROM INT((SROW()-11)/2),INT((SCOL()-29)/2) ;

                TO INT((SROW()-11)/2)+10,INT((SCOL()-29)/2)+28 ;

                TITLE "Вы уверены?" ;

                FLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 7

        ACTIVATE WINDOW werase_ch NOSHOW

  @ 7,2 GET knopka ;

        PICTURE "@*HT \!Удалить;Отставить" ;

        SIZE 1,11,2 ;

        DEFAULT 2

  @ 0,4 SAY "Выбранная Вами пара"

  @ 1,1 SAY "будет удалена не только с"

  @ 2,3 SAY "экрана, но и из базы"

  @ 3,3 SAY "данных по расписанию."

  @ 4,4 SAY "Действительно ли Вы"

  @ 5,3 SAY "хотите именно этого?"

  @ 6,0 TO 8,14

  @ 6,14 TO 8,26

  @ 6,14 SAY "-"

  @ 8,14 SAY "-"

        ACTIVATE WINDOW werase_ch

  READ CYCLE MODAL

  RELEASE WINDOW werase_ch

RETURN IIF(knopka==1,.t.,.f.)

  По клавише F4 в зависимости от того, на поле какого типа

находится курсор, мы соответственно вызываем функции для ввода

данных по предмету, преподавателю и аудитории.

procedure f4proc

  if _CUROBJ<31

    SAVEOBJ=_CUROBJ

    DO CASE

      CASE getwhat(SAVEOBJ)=1           &&Предмет

        =predfn()

      CASE getwhat(SAVEOBJ)=2           &&Преподаватель

        =kuffn()

      CASE getwhat(SAVEOBJ)=3           &&Аудитория

        =audfn()

    ENDCASE

    _CUROBJ=SAVEOBJ

  endif

return

  Эта функция по номеру подгруппы, номеру пары и типу поля

возвращает индекс строки в массиве.

function getobject

parameters podgruppa,number,chto

return (podgruppa-1)*15+(number-1)*3+chto

procedure getall

parameters object,podgruppa,number,chto

*из объекта получим все данные

  chto=iif(mod(object,3)==0,3,mod(object,3))

  podgruppa=iif(mod(object,15)==0,object/15,int(object/15)+1)

  number=iif(mod(object,3)==0,object/3,int(object/3)+1)

  &&абсолютный номер

  number=iif(mod(number,5)==0,5,mod(number,5))

return

  В этой функции по номеру строки в массиве мы можем определить

ее тип, то есть какого рода данные находятся в этой строке - по

предмету, по преподавателю или аудитории.

function getwhat

parameters object

return iif(mod(object,3)==0,3,mod(object,3))

  По номеру строки определяем номер подгруппы

function getpg

parameters object

return iif(mod(object,15)==0,object/15,int(object/15)+1)

  По номеру строки определяем номер пары

function getnumber

parameters object

  number=iif(mod(object,3)==0,object/3,int(object/3)+1)

  &&абсолютный номер

  number=iif(mod(number,5)==0,5,mod(number,5))

return number

  Функции potfn, specfn и facfn отображают на экране списки

потоков, специальностей и факультетов и возвращают и выбор в

cur... и ...txt - переменных.

function potfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW potwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор потока" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW potwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_pot ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lpothundle(choice)

  ACTIVATE WINDOW potwindow

  READ CYCLE MODAL

  RELEASE WINDOW potwindow

return

Function lpothundle

parameters vib

  clear read

  curpot=list_pot(vib,2)

  *занесем выбор в полосу редактирования

  pottxt=list_pot(vib,1)

RETURN

function specfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW specwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор специальности" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW specwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_spec;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lspechundle(choice)

        ACTIVATE WINDOW specwindow

  READ CYCLE MODAL

  RELEASE WINDOW specwindow

Return

Function lspechundle

parameters vib

  clear read

  curspec=list_spec(vib,2)

  *занесем выбор в полосу редактирования

  spectxt=list_spec(vib,1)

  *откроем специальность и очистим его выборы

  pottxt=''

  curpot=0

RETURN

function facfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW  facwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Окно выбора факультета" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW facwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_fac ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lfachundle(choice)

  ACTIVATE WINDOW facwindow

  READ CYCLE MODAL

  RELEASE WINDOW facwindow

Return

Function lfachundle

parameters vib

  clear read

  curfac=list_fac(vib,2)

  *занесем выбор в полосу редактирования

  factxt=list_fac(vib,1)

  *откроем специальность и очистим его выборы

  spectxt=''

  curspec=0

  pottxt=[]

  curpot=0

RETURN

  Эта функция возвращает случайное целое число, лежащее в

заданном диапазоне значений.

FUNCTION irand

parameters i, j

RETURN int((j-i+1)*rand()+i)

NOTE Дзвоник

PROCEDURE bell

  *set bell to irand(19,10000),irand(1,19)

  ?CHR(7)

RETURN

FUNCTION getprepname

PARAMETERS f,r

  string=ALLTRIM(f)

  pos=AT([ ],string,1)

  if pos#0

    tempstr=LEFT(string,pos)+SUBSTR(string,pos+1,1)+[.]

    pos=AT([ ],string,2)

    if pos#0

      string=tempstr+SUBSTR(string,pos+1,1)+[.]

    endif

  endif

  DO CASE

*  "^ \<Профессор;\<Доцент;\<Ст. преподаватель;\<Ассистент"

    CASE r=1

      string="Проф. "+string

    CASE r=2

      string="Доц. "+string

    CASE r=3

      string="Ст.преп. "+string

    CASE r=4

      string="Асс. "+string

  ENDCASE

return string

function audfn

  *Список аудиторий сформируем так : если лекция или

  *практичка, то соответственно заносим лекционные или

  *практический аудитории

  *Если лабораторка не специализированная - заносим все

  *лабораторные, иначе - только лабораторные

  *специализированные

  *  Подумать, нельзя ли отдавать под лабы пр и лек ауд, под

  *практику лекционнные ?

  if WasChanged(2)&&b

    pack

    WasChanged(2)=.f.

  endif

  if marazm(SAVEOBJ-2,1)=0

    wait window nowait [Вначале необходимо выбрать предмет]

    return

  endif

  if marazm(SAVEOBJ-1,1)=0

    wait window nowait [Пожалуйста, выберите преподавателя]

    return

  endif

  *if marazm(SAVEOBJ-2,1)#0

  select a

  locate for code==marazm(SAVEOBJ-2,1)

  select b

  mcount=0

  if marazm(SAVEOBJ-2,3)==3

  *поиск аудитории, связанной с предметом, только если labor

    locate for emkost==marazm(SAVEOBJ-2,3) ;

          .and. special==a.link

  else

    locate for emkost==marazm(SAVEOBJ-2,3) ;

          .or. emkost==marazm(SAVEOBJ-2,3)-1

  endif

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Аудиторий требуемой емкости нет - заполните базу!];

        window

    RETURN

  endif

  dimension list_aud(mcount,2)

  mcount=0

  if marazm(SAVEOBJ-2,3)==3

    locate for emkost==marazm(SAVEOBJ-2,3) .and. ;

          special==a.link

  else

    locate for emkost==marazm(SAVEOBJ-2,3) ;

        .or. emkost==marazm(SAVEOBJ-2,3)-1

  endif

  DO WHILE FOUND()

    mcount=mcount+1

    list_aud(mcount,1)=[Aуд.]+STR(aud,4)+',кoрп.'+;

                       STR(korpus,2)

    list_aud(mcount,2)=code

    CONTINUE

  ENDDO

*еще один штрих - ищем в расписании

*только те аудитории, которые на данную пару свободны

  select i

  xcount=0

  for _i=1 to mcount

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

          .and. audit==list_aud(_i,2)

    if found()&&Hа эту пару аудитория занята

      list_aud(_i,2)=0

      list_aud(_i,1)=[Занята !]+list_aud(_i,1)

      xcount=xcount+1

    endif

  endfor

  if xcount==mcount

    =bell()

    wait [Аудиторий требуемой емкости нет - все заняты] ;

         window NOWAIT

    return

  endif

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW audwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор аудитории" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW audwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_aud ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID laudhundle(choice)

  ACTIVATE WINDOW audwindow

  READ CYCLE MODAL

  RELEASE WINDOW audwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбрана аудитория, то находим эту пару в базе и

    &&меняем в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ-2,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ-2,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ-2,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace audit with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2)

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ-2,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

      endif

    endif

  endif

  select i

return

Function laudhundle

Parameter vib

  if list_aud(vib,2)#0

    clear read

    marazm(SAVEOBJ,1)=list_aud(vib,2)

    *занесем выбор в полосу редактирования

    marazm(SAVEOBJ,2)=list_aud(vib,1)

  endif

RETURN

function prepfn

  *В список преподавателей занесем только тех, которые

  *работают на данной кафедре

  if WasChanged(3)

    pack

    WasChanged(3)=.f.

  endif

  select c

  * prepod (code N(4,0), kafedra N(3,0), fio C(65), rank

  *N(2,0), zvan N(2,0))

  mcount=0

  locate for kafedra==curkuf

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа данной кафедре никто не работает! Заполните ]+;

         [базу] window NOWAIT

    curkuf=0

    kuftxt=[]

    return

  endif

  dimension list_prep(mcount,2)

  locate for kafedra==curkuf

  mcount=0

  DO WHILE FOUND()

    mcount=mcount+1

    list_prep(mcount,1)=getprepname(fio,zvan)

    list_prep(mcount,2)=code

    CONTINUE

  ENDDO

  select i

  *cвободен ли преподаватель?

  xcount=0

  for _i=1 to mcount

    locate for para==week*100+day*10+getnumber(SAVEOBJ);

               .and. prepod==list_prep(_i,2)

    if found()&&Hа эту пару преподаватель занят

      list_prep(_i,2)=0

      list_prep(_i,1)=[Занят !]+list_prep(_i,1)

      xcount=xcount+1

    endif

  endfor

  if xcount==mcount

    =bell()

    wait [Все преподаватели кафедры на этой паре заняты];

          window NOWAIT

    return

  endif

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW prepwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор преподавателя" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW prepwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_prep ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lprephundle(choice)

  ACTIVATE WINDOW prepwindow

  READ CYCLE MODAL

  RELEASE WINDOW prepwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбран, то находим эту пару в базе и меняем

    &&в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ-1,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ-1,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ-1,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace prepod with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2) enable

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ-1,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

        show get marazm(SAVEOBJ+16,2) disable

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

        show get marazm(SAVEOBJ-14,2) disable

      endif

    endif

  endif

return

Function lprephundle

Parameter vib

  if list_prep(vib,2)#0

    clear read

    marazm(SAVEOBJ,1)=list_prep(vib,2)

    *занесем выбор в полосу редактирования

    marazm(SAVEOBJ,2)=list_prep(vib,1)

  endif

RETURN

function kuffn

  aa=irand(1,5)

  cc=irand(1,50)

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if marazm(SAVEOBJ-1,1)==0

    wait window nowait [Вначале необходимо выбрать предмет]

    return

  endif

        DEFINE WINDOW  kufwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Окно выбора кафедры" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW  kufwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_kuf ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID Lkufhundle(choice)

        ACTIVATE WINDOW kufwindow

  READ CYCLE MODAL

  RELEASE WINDOW  kufwindow

  if curkuf#0

    =prepfn()

  endif

Return

FUNCTION Lkufhundle

Parameter vib

  clear read

  curkuf=list_kuf(vib,2)

  *занесем выбор в полосу редактирования

  kuftxt=list_kuf(vib,1)

  *откроем преподавателя и очистим его выборы

  marazm(SAVEOBJ,2)=''

  marazm(SAVEOBJ,1)=0

*  predtxt=[]

*  curpred=0

RETURN

function predfn

  *Создадим и заполним список предметов по формату имя-код

  select a

  if WasChanged(1)

    pack

    WasChanged(1)=.f.

  endif

  if reccount()=0

    =bell()

    wait [База данных по предметам пуста! Заполните] window;

         NOWAIT

  endif

  dimension list_pred(reccount(),2)

  for _i=1 to reccount()

    go _i

    list_pred(_i,1)=name

    list_pred(_i,2)=code

  endfor

  select i

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW predwindow ;

                FROM INT((SROW()-19)/2),INT((SCOL()-49)/2) ;

                TO INT((SROW()-19)/2)+18,INT((SCOL()-49)/2)+48 ;

                TITLE "Окно выбора предмета" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 4

        ACTIVATE WINDOW predwindow NOSHOW

  @ 3,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_pred ;

        SIZE 14,45 ;

        DEFAULT 1 ;

        COLOR SCHEME 9 VALID Lpredhundle(choice)

  @ 0,16 GET droblevel ;

        PICTURE "@*RVN   Лекция; Практичка;Лабораторка" ;

        SIZE 1,15,0 ;

        DEFAULT 3 ;

        COLOR SCHEME 8

        ACTIVATE WINDOW predwindow

  READ CYCLE MODAL

  RELEASE WINDOW predwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбран, то находим эту пару в базе и меняем

    &&в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace predmet with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2)

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

        &&*   +-----------+

        show get marazm(SAVEOBJ+16,2) disable

        &&*   ¦-----+-----¦

        show get marazm(SAVEOBJ+17,2) disable

        &&*   +-----------+

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

        show get marazm(SAVEOBJ-14,2) disable

        show get marazm(SAVEOBJ-13,2) disable

      endif

    else

      if getpg(SAVEOBJ)==1

        if marazm(SAVEOBJ+15,1)=0

          for _i=15 to 17

            marazm(SAVEOBJ+_i,2)=[]

            show get marazm(SAVEOBJ+_i,2) enable

          endfor

        endif

      else

        if marazm(SAVEOBJ-15,1)=0

          for _i=15 to 13 step -1

            marazm(SAVEOBJ-_i,2)=[]

            show get marazm(SAVEOBJ-_i,2) enable

          endfor

        endif

      endif

    endif

  endif

Return

  *а не перебрали ли мы часов - вставить обработку потом

Function lpredhundle

Parameter vib

  *Прежде всего - несколько проверок

  *1 - если это лекция, имеем ли мы право на

  *замену - нет ли пр или лаб

  *в этот день на этой паре на этой неделе

  *(gruppa N(5,0),para N(3,0),predmet N(4,0),

  *audit N(4,0),prepod N(4,0))

  *код пары формируется следующим образом :

  * неделя - 1 числитель,2 знаменатель

  * день -1 .. 5

  * номер пары - 1..5

  * code=week*100+day*10+number

  *код группы = код потока*100+номер группы*10+номер подгруппы

  select i

  *Один очень нескромный вопрос - а если мы изменяемудаляя -

  *что делать ? Вероятно, надо удалить старое и вставить новое

  DO CASE

    CASE droblevel==1

      *поищем лекцию

      locate for curpot*100==gruppa .and. ;

                 para==week*100+day*10+getnumber(SAVEOBJ)

      if FOUND()

        *заменяем одну лекцию другой

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        *marazm(SAVEOBJ,3)=1

      else

        *лекции нет - это новая и есть опасность наложения на

        *практички или лабораторки

        for _i=1 to m.grpcount

          for _j=0 to 2

            locate for para==week*100+day*10+;

                       getnumber(SAVEOBJ) .and. ;

                       gruppa==curpot*100+_i*10+_j

            if FOUND()

              wait window nowait [Это не может быть лекция]+;

                   [ - есть подгруппы, ]+;

                   [в которых эта пара занята]

              droblevel=2

              show get droblevel

              return

            endif

          endfor

        endfor

        *если нет пересечений - заполняем  !

        *заносим в базу пустышулечку not a'la SQL

        insert blank

        replace para with week*100+day*10+getnumber(SAVEOBJ)

        replace gruppa with curpot*100

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        marazm(SAVEOBJ,3)=1

        *заставим выбрать заново аудиторию?   -   уточнить

        *.........

      endif

    CASE droblevel==2

      *поищем практичку

      locate for curpot*100+curgrp*10==gruppa .and. ;

                 para==week*100+day*10+getnumber(SAVEOBJ)

      if FOUND()

        *заменяем одну практичку другой

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        *marazm(SAVEOBJ,3)=2

      else

        *практики нет - это новая и есть опасность

        *     наложения на лабораторки

        for _j=1 to 2

          locate for para==week*100+day*10+getnumber(SAVEOBJ);

                    .and. gruppa==curpot*100+curgrp*10+_j

          if FOUND()

            wait window nowait [Это не может быть практика]+;

                 [ - есть подгруппы, ]+;

                 [в которых эта пара занята]

            droblevel=3

            show get droblevel

            return

          endif

        endfor

        *А вдруг на этом месте была лекция?

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

                   .and. gruppa==curpot*100

        if !FOUND()

          *если нет пересечений - заполняем  !

          insert blank

          replace para with week*100+day*10+getnumber(SAVEOBJ)

        endif

        replace gruppa with curpot*100+curgrp*10

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        marazm(SAVEOBJ,3)=2

        if FOUND()

          SAVEOBJ=SAVEOBJ+2

          =audfn()

          SAVEOBJ=SAVEOBJ-2

        endif

      endif

    CASE droblevel==3

      locate for curpot*100+curgrp*10+getpg(SAVEOBJ)==gruppa;

                .and. para==week*100+day*10+getnumber(SAVEOBJ)

      if !FOUND()

        *А вдруг на этом месте была лекция?

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

              .and. gruppa==curpot*100

        if FOUND()

          replace gruppa with curpot*100+curgrp*10+;

                  getpg(SAVEOBJ)

        else

          *А вдруг на этом месте была практичка?

          locate for para==week*100+day*10+getnumber(SAVEOBJ);

                    .and. gruppa==curpot*100+curgrp*10

          if FOUND()

            replace gruppa with curpot*100+curgrp*10+;

                    getpg(SAVEOBJ)

          else

            *если нет пересечений - заполняем  !

            insert blank

            replace para with week*100+day*10+getnumber(SAVEOBJ)

            replace gruppa with curpot*100+curgrp*10+;

                    getpg(SAVEOBJ)

          endif

        endif

      endif

      clear read

      marazm(SAVEOBJ,1)=list_pred(vib,2)

      *занесем выбор в полосу редактирования

      marazm(SAVEOBJ,2)=list_pred(vib,1)

      marazm(SAVEOBJ,3)=3

      if FOUND()

        SAVEOBJ=SAVEOBJ+2

        =audfn()

        SAVEOBJ=SAVEOBJ-2

      endif

  endcase

RETURN

PROCEDURE prep_rasp

*Вывод расписания для преподавателя в файл

  emoe=getfilename()

  handle = FCREATE(emoe) && Открытие  файла

  IF handle < 0               && Ошибка при создании файла

    DO CASE                     && Определение ошибки

      CASE FERROR() = 4

                reason = 'Много файлов открыто'

          CASE FERROR() = 5

                reason = 'Доступ невозможен'

          CASE FERROR() = 8

                reason = 'Hет памяти'

      CASE FERROR() = 29

                reason = 'Hет места на диске'

          CASE FERROR() = 31

                reason = 'Общий сбой'

        ENDCASE

        *** Вывод ошибки ***

        WAIT WINDOW 'Файл не создан: '+ reason NOWAIT

        RETURN

  ENDIF

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " кафедрам" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

        DEFINE WINDOW  kufwindow ;

                FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

                TITLE "Окно выбора кафедры" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

        ACTIVATE WINDOW  kufwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_kuf ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID L2kufhundle(choice)

        ACTIVATE WINDOW kufwindow

  READ CYCLE MODAL

  if curkuf=0

    return

  endif

  *В список преподавателей занесем только тех, которые

  *работают на данной кафедре

  select c

  * prepod (code N(4,0), kafedra N(3,0), fio C(65), rank

  * N(2,0), zvan N(2,0))

  mcount=0

  locate for kafedra==curkuf

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа данной кафедре никто не работает! ]+;

         [Заполните базу] window NOWAIT

    curkuf=0

    kuftxt=[]

    return

  endif

  dimension list_prep(mcount,2)

  locate for kafedra==curkuf

  mcount=0

  DO WHILE FOUND()

    mcount=mcount+1

    list_prep(mcount,1)=getprepname(fio,zvan)

    list_prep(mcount,2)=code

    CONTINUE

  ENDDO

  select i

        DEFINE WINDOW prepwindow ;

                FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

                TITLE "Выбор преподавателя" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

  RELEASE WINDOW  kufwindow

        ACTIVATE WINDOW prepwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_prep ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID l2prephundle(choice)

  ACTIVATE WINDOW prepwindow

  READ CYCLE MODAL

  RELEASE WINDOW prepwindow

  if curprep=0

    return

  endif

  wait window nowait [Генерация расписания...]

  =FPUTS(handle,[             Расписание])

  =FPUTS(handle,[Кафедра       : ]+ALLTRIM(kuftxt))

  =FPUTS(handle,[Преподаватель : ]+ALLTRIM(preptxt))

  select i

  for _d=1 to 5

    =FPUTS(handle,[])

    =FPUTS(handle,[])

    =FPUTS(handle,[       ]+days_arr(_d))

    for _p=1 to 5

      =FPUTS(handle,[])

      =FPUTS(handle,[       ]+STR(_p,1)+[ пара])

      for _w=1 to 2

        =FPUTS(handle,week_arr(_w))

        select i

        locate for prepod==curprep .and. para==_w*100+_d*10+_p

        if FOUND()

          select a

          locate for code==i.predmet

          mmm=alltrim(name)

          select d

          locate for code==int(i.gruppa/100)

          select g

          locate for code==d.special

          nnn=alltrim(name)+[,]+STR(d.kurs,1)+[ курс]

          select i

          if mod(gruppa,100)==0&&lect

            mmm=mmm+[, лекция]

          else

            if mod(gruppa,10)==0&&pract

              mmm=mmm+[, практичка]

              nnn=nnn+[, ]+STR(int(mod(gruppa,100)/10),1)+;

                  [ группа]

            else

              mmm=mmm+[, лабораторка]

              nnn=nnn+[, ]+STR(int(mod(gruppa,100)/10),1)+;

                      [ группа, ]+;

                      STR(mod(gruppa,10),1)+[ подгруппа]

            endif

          endif

          =FPUTS(handle,mmm)

          =FPUTS(handle,nnn)

          select b

          locate for code==i.audit

          =FPUTS(handle,"Ауд. "+STR(aud,3)+[, корп. ]+;

                        STR(korpus,1))

        else

          =FPUTS(handle,[])

          =FPUTS(handle,[])

          =FPUTS(handle,[])

        endif

      endfor

    endfor

  endfor

  select i

  =FCLOSE(handle)       && Close the file

  wait clear

  DEFINE WINDOW wingets FROM 1,0 TO 24,79 ;

                CLOSE FLOAT SHADOW SYSTEM ;

                COLOR SCHEME 8 TITLE ' Расписание для преподавателя '

  MODIFY FILE (emoe) WINDOW wingets && View the file

  RELEASE WINDOW  &&Удаление окна

RETURN

FUNCTION getfilename

        DEFINE WINDOW wfilename ;

                FROM INT((SROW()-11)/2),INT((SCOL()-57)/2) ;

                TO INT((SROW()-11)/2)+10,INT((SCOL()-57)/2)+56 ;

                FLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 5

        ACTIVATE WINDOW wfilename NOSHOW

@ 6,13 EDIT filename ;

        SIZE 1,28,65 ;

        DEFAULT " "

@ 8,24 GET knopka ;

        PICTURE "@*HT \<Ok" ;

        SIZE 1,6,1 ;

        DEFAULT 1

@ 0,13 SAY "Введите имя файла, в который"

@ 1,16 SAY "будет осуществлен вывод"

@ 2,13 SAY "расписания для корректировки"

@ 3,16 SAY "и дальнейшей распечатки"

@ 4,13 SAY "Файл не должен существовать."

        ACTIVATE WINDOW wfilename

READ CYCLE MODAL

RELEASE WINDOW wfilename

RETURN alltrim(filename)

FUNCTION L2kufhundle

Parameter vib

  clear read

  curkuf=list_kuf(vib,2)

  *занесем выбор в полосу редактирования

  kuftxt=list_kuf(vib,1)

  *откроем преподавателя и очистим его выборы

  preptxt=[]

  curprep=0

RETURN

Function l2prephundle

Parameter vib

  clear read

  curprep=list_prep(vib,2)

  *занесем выбор в полосу редактирования

  preptxt=list_prep(vib,1)

RETURN

*Hу вот и всё................................


Текст программы "Расписание занятий"
Полный текст программы "Расписание занятий"

*  Теперь поговорим немного о постановке задачи, ради которой Вы,

*собственно, и изучали весь предыдущий материал - составление

*расписания.

*  Как показывает практика, в СУБД самое важное - это продуманные

*структуры данных, поэтому проектирование системы для

*обслуживания расписания мы начнем с составления структур баз

*данных. Следующим этапом будет заполнение этих баз, и затем -

*самая нужная часть, ради которой делается львиная доля работы:

*обслуживание базы данных, под которым мы будем понимать

*удовлетворение различных запросов к ней.

*  Вспомним, как выглядит обычное расписание. На большом листе

*чертится или отпечатывется большое количество клеточек. Строки в

*расписании соответствуют номеру пары в какой-то из дней недели,

*столбцы определяют соответственно потоки, группы и подгруппы. А

*в клеточке мы видим название предмета, имя преподавателя и номер

*аудитории. Таким образом, для того, чтобы однозначно определить

*эту клеточку на стене в базе данных, нам необходимо пять полей:

*группа, пара, предмет, аудитория и преподаватель.

*  Естественно, заносить эти данные в базу так как это делает

*диспетчер, по меньшей мере нерационально - может быть, ей и приятно

*раз 15 написать чьи-то реквизиты, но я бы предпочел обозначить их

*каким нибудь-кодом и заносить в расписание не строку "Великий

*инквизитор", а какое-то однозначно определяемое число, и так во

*всем. Конечно, где-то придется держать список пар типа код -

*преподаватель (а может и не пар) - в другой базе, к примеру.

*  Итак, в базе расписания мы введем 5 числовых полей, в которые

*будем заносить коды групп, пар, предметов, аудиторий и

*преподавателей.

*  Код группы мы будем формировать следующим образом :

*  код_группы=код_потока*100+номер_группы*10+номер_подгруппы.

*При этом предполагается, что групп на потоке не может быть

*больше 9 (в противном случае код теряет смысл или, что еще

*хуже, приобретает неверный), а код потока формируется в

*отдельной базе потоков.

*  Чем удобен именно такой код? Легкостью в поиске - если номер

*группы в коде равен нулю, то эта пара соответствует лекции, если

*номер группы ненулевой, а номер подгруппы нулевой, то это

*практичка, иначе - лабораторка. Следовательно, общий код мы

*никогда не будем делать нулевым, и 0 в коде рассматривать как

*ошибку.

*  Обозначив числитель через 1, знаменатель - через 2 и

*пронумеровав дни недели от 1 до 5, мы получим удобный код пары:

*  код=номер_недели*100+номер_дня*10+номер_пары

*  Коды предметов, аудиторий и преподавателей мы будем

*формировать в соответствующих базах.

*  База данных по предмету будет, естественно, содержать код и

*название предмета; кроме того, для удобства возложим на нашу

*программу обязанность старост - подсчитывать запланированное и

*реальное число часов, в неделю и общее. Дополнительно введем

*признак связи предмета с аудиторией, необходимый для того,

*чтобы, скажем, лабораторные работы по химии проводились в хим.

*лаборатории, а не в спортзале.

*  Аудиторию мы будем характеризовать номером корпуса, номером

*аудитории, наличием какой-либо специализации (например,

*компьютерный класс) и емкостью - поток, группа, подгруппа.

*Некоторые аудитории настолько специфичны, что этих данных для

*кодирования недостаточно; не каждый догадается, что аудитория

*номер 1 в шестом корпусе - это мастерская ОТД, поэтому введем

*необязательное (то есть memo) поле, в котором и прокомментируем

*эти особенности.

*  Поток традиционно характеризуется специальностью - кодом,

*номером курса и количеством групп. У потоков филфака есть

*интересная особенность: часть групп занимается с часу, часть - с

*7.30 и т.д., причем те, кто занимаются с часу, считают свою пару

*первой, хотя для нас она - третья. Для таких вот экзотических

*специальностей введем особое поле смещения, показывающее, на

*сколько пар они отстали от жизни.

*  Каждый преподаватель имеет имя, звание, приписан к какой-то

*кафедре; кроме этого, в институте он занимает какую-то

*должность, что тоже надо учитывать при составлении расписания

*  Факультет, специальность и кафедру можно охарактеризовать

*кодом и именем.

*  Итак, очистим экран и память, сделаем необходимые установки и

*выведем в системном окошке сообщение:

wait window nowait [Hачальная инициализация]

clear all

set talk off

set deleted on

set resource on

set date british

clear

*  B различных рабочих областях откроем необходимые нам при

*работе базы данных вместе со своими структурными индексными

*файлами, индицируя каждое действие соотвествующим сообщением на

*экране. Если файл базы данных не существует, то мы его создаем

*средствами SQL, попутно индексируя необходимые нам поля.

select a&&

*Вспомогательный файл предметов

wait window nowait [Открытие базы предметов]

if !file('predmet.dbf')

  CREATE TABLE predmet (code N(4,0),name C(65),link L,;

                        chas_week N(3,0),chas_all N(4,0))

  index on code tag code

  index on name tag name

  index on link tag link

  index on chas_week tag chas_week

  index on chas_all tag chas_all

endif

use predmet index predmet

select b&&&

wait window nowait [Открытие базы аудиторий]

*Вспомогательный файл аудиторий

if !file('auditor.dbf')

  create table auditor (code n(4,0),korpus n(2,0),aud n(3,0),;

                        emkost n(3,0),special L,comment M)

  index on code tag code

  index on korpus tag korpus

  index on aud tag aud

  index on emkost tag emkost

  index on special tag special

endif

use auditor index auditor

select d&&

wait window nowait [Открытие базы потоков]

if !file("potok.dbf")

  create table potok (code n(5,0),special n(3,0),;

                     kurs n(1,0),delta n(2,0),grpcount n(2,0))

  index on code tag code

  index on special tag special

  index on kurs tag kurs

  index on delta tag delta

  index on grpcount tag grpcount

endif

use potok index potok

select c&&

wait window nowait [Открытие базы преподавателей]

*Вспомогательный файл преподавателей

if !file("prepod.dbf")

  CREATE TABLE prepod (code N(4,0), kafedra N(3,0),;

                       fio C(65), rank N(2,0), zvan N(2,0))

  index on code tag code

  index on kafedra tag kafedra

  index on fio tag fio

  index on rank tag rank

  index on zvan tag zvan

endif

use prepod index prepod

select e

* Эта область пока свободна -м.б., под что-то временное

select f&&

wait window nowait [Открытие базы факультетов]

*Вспомогательный файл факультетов - список их

if !file('fuc.dbf')

  CREATE TABLE fuc (name C(65),code N(3,0))

  index on name tag name

  index on code tag code

endif

use   fuc   index   fuc

select g&&

wait window nowait [Открытие базы специальностей]

*Вспомогательный файл специальностей - список их

if !file('spec.dbf')

  CREATE TABLE spec (name C(65),code N(3,0),fac N(3,0))

  index on name tag name

  index on code tag code

endif

use   spec  index spec

select h&&

wait window nowait [Открытие базы кафедр]

*Вспомогательный файл кафедр - список их

if !file('kuf.dbf')

  CREATE TABLE kuf (name C(65),code N(3,0))

  index on name tag name

  index on code tag code

endif

use kuf     index kuf

select i

wait window nowait [Инициализация расписания]

*Главный файл расписания

if !file('raspisan.dbf')

  CREATE TABLE raspisan (gruppa N(5,0), para N(3,0),;

                         predmet N(4,0), audit N(4,0),;

                         prepod N(4,0))

  index on gruppa tag gruppa

  index on para tag para

  index on predmet tag predmet

  index on audit tag audit

  index on prepod tag prepod

endif

use raspisan

select j

*  Следующий фрагмент программы, предлагаемый Вашему вниманию,

*объявляет несколько глобальных массивов и переменных, проводя

*заполнение части их:

public dimension list_pred(50,2),WasChanged(10),;

                 tempos(10),list_spec(50,2),list_kuf(50,2),;

                 list_prep(50,2),days_arr(5),week_arr(2),;

                 list_fac(50,2),list_aud(50,2),;

                 list_pot(50,2),marazm(30,3)

public curaud,curprep,curkuf,curfac,curspec,curpot,;

       curpred,curgrp,curpg,first,pottxt,factxt,;

       spectxt,predtxt,preptxt,audtxt,kuftxt

  days_arr(1)=[Понедельник]

  days_arr(2)=[Вторник    ]

  days_arr(3)=[Среда      ]

  days_arr(4)=[Четверг    ]

  days_arr(5)=[Пятница    ]

  week_arr(1)='Числитель  '

  week_arr(2)='Знаменатель'

IsQuitFlag=.f.

first=.t.

STORE 0 TO curaud,curgrp,curprep,curfac,curspec,curpot,curpred

curkuf=0

curpg=0

      tempos(1)=[Первая]

      tempos(2)=[Вторая]

      tempos(3)='Третья'

      tempos(4)=[Четвертая]

      tempos(5)=[Пятая]

      tempos(6)=[Шестая]

      tempos(7)=[Седьмая]

      tempos(8)=[Восьмая]

      tempos(9)=[Девятая]

*  Рассмотрим подробнее некоторые соглашения об именах.

*  Если в начале массива стоит слово list_, это означает, что

*данный массив будет использоаться для хранения некоторого

*списка. Если переменная начинается с cur - то в ней будет

*содержаться некоторый выбор (обычно код) или 0 в случае его

*отсутствия. Переменная, заканчивающаяся на txt, содержит

*строку, соответствующую этому коду.

*  Конкретный вид данных определяется фразами

*

*  aud  - аудитория

*  prep - преподаватель

*  kuf  - кафедра

*  fac  - факультет

*  spec - специальность

*  pot  - поток

*  pred - предмет

*  grp  - группа

*  pg   - подгруппа

*

*  Кроме того, есть переменные и массивы, не укладывающиеся в эту

*схему :

*

*  WasChanged(10) - каждый элемент этого массива отражает

*                   состояние базы данных в рабочей области с

*                   соответствующим номером. Если в базе данных в

*                   области <number> что-то изменяется, в элемент

*                   WasChanged(<number>)=.T. . В конце программы

*                   эта информация используется для определения

*                   того, какую базу данных необходимо упаковать.

*

*  tempos(10)     - как можно заметить из действий,

*                   производящихся над элементами этого массива,

*                   в нем храняться числительные от 1 до 9 -

*                   номера групп в символьном формате. Они

*                   понадобяться для "красивого вывода" номера

*                   группы.

*

*  days_arr(5)    - содержит названия учебных дней недели, а

*

*  week_arr(2)    - названия самих недель.

*

*  marazm(30,3)   - массив с таким "оригинальным" именем является

*                   одним из самых важных. Как Вы, быть может,

*                   помните, в расписании основными данными у нас

*                   являются предмет, преподаватель и аудитория -

*                   3 ячейки на одну пару. Максимальное число пар

*                   в день - 5, подгрупп - 2, 3х5х2==30 - именно

*                   таким числом полей мы можем представить на

*                   экране данные по расписанию для одной группы

*                   на один день, причем элементы с номерами вида

*                   1+3*n отвечают за данные по предмету, 2+3*n -

*                   по преподавателю, и 3+3*n - по аудитории.

*                   Первая ячейка каждой строки массива содержит

*                   код, вторая - соответствующий ему текст

*                   (списки, кстати, организованы наоборот -

*                   сначала текст, а затем уже код), третья имеет

*                   смысл только для поля предмета и содержит

*                   уровень дробления потока:

*

*                   1 - поток,

*                   2 - группа,

*                   3 - подгруппа.

*

*  first          - это всего лишь флаг первого вхождения в окно

*                   расписания. Он необходим для того, чтобы

*                   отличит инициализационные действия от

*                   многократно повторяющихся.

*

*  IsQuitFlag     - определяет условие выхода из цикла обработки

*                   сообщений от меню. VALID-функция для этого

*                   цикла возвращает значение этого флага. Если

*                   он очищен, цикл продолжается, при взводе -

*                   завершается.

*

*  Имена и заголовки окон Browse совпадают. Они могут

*присваиваться несколькими путями. Если команда BROWSE

*выполняется с фразой WINDOW, имя окна Browse будет совпадать с

*заголовком указанного описанного пользователем окна (не с

*именем). Если в команде BROWSE присутствует фраза TITLE, имя,

*указанное в этой фразе будет именем окна BROWSE.

*  В связи с этим (в продолжение "публикации" текста нашей

*программы) мы вначале проверяем, не было ли уже ранее определено

*окно для команды Browse, и если оно не определено, то -

*определяем его как способным изменять размеры, закрываться и

*минимизироваться; в качестве атрибутов даем ему тень, двойную

*рамку и цветовую схему команды Browse - 10. Обратите внимание на

*прием, использованный для того, чтобы центрировать это окно на

*любом экране, не только 80х25.

wait window nowait [Определение окон]

IF NOT WEXIST("browsewin")

        DEFINE WINDOW browsewin ;

                FROM INT((SROW()-17)/2),INT((SCOL()-60)/2) ;

                TO INT((SROW()-17)/2)+16,INT((SCOL()-60)/2)+59 ;

                FLOAT ;

                CLOSE ;

                SHADOW ;

                MINIMIZE ;

                DOUBLE ;

                COLOR SCHEME 10

ENDIF

*  Hаиболее предпочтительным методом  включения вертикальных меню

*в приложение является использование системного горизонтального

*меню FoxPro и его вертикальных меню. При использовании

*вертикальных меню системного горизонтального меню нет

*необходимости вначале определять вертикальные меню, более того

*это приведет к ошибке. _MSYSMENU - это название системного меню.

*  В приведенном ниже фрагменте мы вначале очищаем системное меню

*от всех его BAR - и PAD - меню, заставляем отобразиться эту

*пустую полосочку и начинаем ее заполнять PAD - элементами, к

*которым далее привязываем вертикальные POPUP - меню, три из

*которых наполнены BAR - элементами. После всего этого фразами

*ON SELECTION BAR номер_пункта_меню OF имя_меню DO Имя_процедуры

*подвешиваем процедуры, реагирующие на выбор соответствующих

*пунктов меню.

*  Определив меню и процедуры - реагенты, мы организуем

*безоконный цикл чтения состояния меню до тех пор, пока

*VALID-функция IsQuit не прервет его. Такого рода цикл обработки

*сообщений - относительная новинка в FoxPro; подробнее Вы можете

*об этом прочитать в книге Les Pinter [FoxPro Application

*Programming] (Chapter 'Foundation Read').

wait window nowait [Инициализация меню]

SET SYSMENU TO

SET SYSMENU AUTOMATIC

DEFINE PAD helpquit OF _MSYSMENU PROMPT [\<Ё] COLOR SCHEME 3

DEFINE PAD listmake OF _MSYSMENU PROMPT "\<Составление "+;

                                   "списков " COLOR SCHEME 3

DEFINE PAD queryes OF _MSYSMENU PROMPT "\<Запросы" ;

                                              COLOR SCHEME 3

DEFINE PAD configur OF _MSYSMENU PROMPT "\<?" COLOR SCHEME 3

*                   "\<Конфигурация"

DEFINE PAD print_conf OF _MSYSMENU PROMPT "\<Hастройки "+;

                                    "печати " COLOR SCHEME 3

ON PAD helpquit OF _MSYSMENU ACTIVATE POPUP hq_popup

ON PAD listmake OF _MSYSMENU ACTIVATE POPUP lm_popup

ON PAD queryes OF _MSYSMENU ACTIVATE POPUP qr_popup

DEFINE POPUP hq_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF hq_popup PROMPT "\<Help"

DEFINE BAR 2 OF hq_popup PROMPT "\<Quit"

ON SELECTION BAR 1 OF hq_popup DO help_proc &&in "pogruz.prg"

ON SELECTION BAR 2 OF hq_popup DO quit_proc &&in "pogruz.prg"

DEFINE POPUP lm_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF lm_popup PROMPT "\<Факультетов"

DEFINE BAR 2 OF lm_popup PROMPT "\<Кафедр"

DEFINE BAR 3 OF lm_popup PROMPT "\<Специальностей"

DEFINE BAR 4 OF lm_popup PROMPT "\<Преподавателей"

DEFINE BAR 5 OF lm_popup PROMPT "\<Потоков"

DEFINE BAR 6 OF lm_popup PROMPT "\<Предметов"

DEFINE BAR 7 OF lm_popup PROMPT "\<Аудиторий"

DEFINE BAR 8 OF lm_popup PROMPT "\<Пар расписания"

ON SELECTION BAR 1 OF lm_popup DO lm_fuc_proc

&&in "pogruz.prg"

ON SELECTION BAR 2 OF lm_popup DO lm_kaf_proc

&&in "pogruz.prg"

ON SELECTION BAR 3 OF lm_popup DO lm_spec_proc

&&in "pogruz.prg"

ON SELECTION BAR 4 OF lm_popup DO lm_prep_proc

&&in "pogruz.prg"

ON SELECTION BAR 5 OF lm_popup DO lm_grup_proc

&&in "pogruz.prg"

ON SELECTION BAR 6 OF lm_popup DO lm_pred_proc

&&in "pogruz.prg"

ON SELECTION BAR 7 OF lm_popup DO lm_aud_proc

&&in "pogruz.prg"

ON SELECTION BAR 8 OF lm_popup DO lm_par_proc

&&in "pogruz.prg"

DEFINE POPUP qr_popup MARGIN RELATIVE SHADOW COLOR SCHEME 4

DEFINE BAR 1 OF qr_popup PROMPT "\<Расписание преподавателя"

*DEFINE BAR 2 OF qr_popup PROMPT "\<Свободной пары"

ON SELECTION BAR 1 OF qr_popup DO prep_rasp &&in "pogruz.prg"

*ON SELECTION BAR 2 OF qr_popup DO frpar_proc

&&in "pogruz.prg"

wait clear

read cycle valid IsQuit()

clear all

return

*  Функция IsQuit нигде в программе непосредственно не

*вызывается; она является VALID - условием, зацикливающим

*объединяющий READ, обрабатывающий сообщения от меню. Процедура

*help_proc, как видно из комментария к ней, вызывается при выборе

*пункта меню Help по команде ON SELECTION BAR 1 OF POPUP hq_popup

*DO help_proc.

function IsQuit

return IsQuitFlag

*       +---------------------------------------------------+

*       ¦

*       єhelp_proc  ON SELECTION BAR 1 OF POPUP hq_popup    є

*       є                                                   є

*       є Procedure Origin:                                 є

*       є                                                   є

*       є From Menu:  POGRUZ.MPR,            Record:    5   є

*       є Called By:  ON SELECTION BAR 1 OF POPUP hq_popup  ¦

*       ¦ Prompt:     Help                                  ¦

*       ¦ SniPPet:    1                                     ¦

*       ¦                                                   ¦

*       +---------------------------------------------------+

*

PROCEDURE help_proc

   wait "Help isn't availaible yet.." WINDOW NOWAIT

RETURN

*  Уйти красиво в программе бывает даже важнее, чем в жизни.

*Поэтому мы и завели процедуру, реагирующую на выбор в меню

*пункта Quit. Как Вы, быть может, еще не забыли, у нас 9 баз

*данных, размещенных в 10 рабочих областях, где 5 - пустая. Перед

*выходом мы, естественно должны их закрыть; если в базе были

*сделаны какие - либо изменения, по правилам хорошего тона ее

*желательно упаковать.

*  Изменялась ли база, мы можем узнать, просмотрев массив

*WasChanged, поэтому организуем цикл Просмотр массива - Упаковка

*(только если были изменения) - Закрытие базы данных.

*  Команда SELECT не может принять в качестве параметра временную

*переменную, поэтому воспользуемся описанной ранее функцией

*макроподстановки. Для этого переведем номер рабочей области в

*строку и подставим в SELECT содержимое этой строки.

*  Затем мы восстановим стандартное системное меню, выведем

*системной функцией на экран заставку FoxPro, и очистим все READ

*- циклы. Для Foundation READ, обрабатывавшего наше меню, мы

*взведем флажок выхода - мол, хватит работать, после чего наша

*программа благополучно завершится.

*

*       +----------------------------------------------------+

*

*       є quit_proc  ON SELECTION BAR 2 OF POPUP hq_popup    є

*       є

*       є Procedure Origin:                                  є

*       є                                                    є

*       є From Menu:  POGRUZ.MPR,            Record:    6    є

*       є Called By:  ON SELECTION BAR 2 OF POPUP hq_popup   є

*       є Prompt:     Quit

*

*       є                                                    є

*

*

PROCEDURE quit_proc

  wait window nowait [ЋзЁбвЄ  а Ў®зЁе ®Ў« бвҐ©]

  for num=1 to 10

    pol=str(num)

    select &pol

    if WasChanged(num)

      pack

      wait window nowait [Ї Є®ўЄ ...]

    endif

    use

  endfor

  select 1

  wait window nowait [Џ®¤Ј®в®ўЄ  Є ўле®¤г]

  clear

  set sysmenu to default

  IsQuitFlag=.t.

  clear read

  =sys(2017)

  wait clear

return

*  Нижеприведенная процедура позволит Вам заполнить /

*модифицировать базу данных по преподавателям.

*  Перейдя в рабочую область БД по преподавателям, мы для

*удобства редактирования добавим в конец пустую запись. Затем,

*после необходимых проверок, заполним список названиями и кодами

*кафедр. При этом, если БД по кафедрам уже изменялась, ее надо

*упаковать, так как команда копирования базы в массив не обращает

*внимания на то, что запись может быть пустой или помеченной к

*удалению, а занесение такой записи в список кафедр (обычно

*имеющей вид .F.) нежелательно. Далее, найдя в списке кафедр ту,

*которая соответствует коду в первой записи БД по преподавателям,

*мы запоминаем этот номер, чтобы в дальнейшем подсветить его в

*списке, но уже на экране.

*  Сконструировав список на экране из элементов массива list_kuf,

*мы будем заносить выбор в переменную ch_kuf. VALID - функция для

*списка будет вызываться при выборе его элемента с двумя

*параметрами: кодом кафедры и ее названием. Эта функция код

*занесет в базу данных, а название отобразит на экране.

*  В поля БД с именами rank и zvan мы будем заносить номер

*элемента в соответствующих кнопках - меню, а в поле fio -

*информацию из окна редактирования.

*  Сбоку экрана создадим серию из 5 кнопок, одна из которых будет

*по умолчанию (\!), то есть она будет "нажиматься" по комбинации

*Ctrl+Enter, а другая (\?) будет реагировать на нажатие клавиши

*Esc. Для отдельной обработки каждой кнопки в VALID - функцию мы

*передадим ее номер.

*  Свои действия в этой функции мы будем сопровождать

*разнообразными проверками: на выход за пределы базы, на пустоту

*записи и т.д.

*  После формирования всех полей на экране и заполнения их

*информацией из первой записи мы отключим системное меню и

*организуем цикл обработки сообщений, прерываемый по кнопке

*"Выход". Восстановив и отбразив меню, мы очищаем экран и

*занимаемся гигиеной: ищем пустые записи и удаляем их из базы.

*  Казалось бы, мы сделали уже все, однако фамилию преподавателя

*мы ввели, а код - нет! И не надо: код мы сформируем программно,

*по одному и тому же алгоритму во всех местах, суть которого

*заключается в следующем.

*  Если код у нас не сформирован, то в соответствующем поле базы

*стоит нолик, поэтому мы ищем в базе запись с нулевым кодом.

*Найдя ее, заводим переменную с начальным значением 1 и

*наращиваем ее до тех пор, пока она совпадает хотя бы с одним

*кодом из уже имеющихся в базе. Главный критерий кода -

*уникальность, поэтому, найдя число, для которого нет совпадений

*среди уже имеющихся кодов, мы можем его принять за handle для

*данной записи, и заменить пустой (нулевой) код этим числом.

*

*

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_prep_proc  ON SELECTION BAR 4 OF POPUP lm_popup ¦

*       ¦

*       є Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:    9    ¦

*       ¦ Called By:  ON SELECTION BAR 4 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Преподавателей                         ¦

*       ¦ Snippet:

*       є                                                    є

*       УДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЅ

*

PROCEDURE lm_prep_proc

  wait window nowait [Инициализация экранных объектов]

  select c

  go bottom

  insert blank

  go top

  *Ввод данных по преподавателю

  *Объявим массив, куда считаем кафедры и их коды

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         "кафедрам" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

  *Поиск кода кафедры

  for _i=1 to reccount()

    if c.kafedra==list_kuf(_i,2)

      ch_kuf=_i

    endif

  endfor

  select c

  WasChanged(3)=.T.

  @1,1  SAY [             Cписок  кафедр             ]

  @3,1  GET ch_kuf FROM list_kuf SIZE 20,40 DEFAULT 1 ;

        VALID funchkuf(list_kuf(ch_kuf,2),list_kuf(ch_kuf,1)) ;

            MESSAGE [Двойным щелчком или нажатием клавиши]+;

            [ Ввод в этом списке производят выбор]

            &&Кидаем код кафедры

  if empty(kafedra)

  else

    @23,1 SAY "Работает на кафедре : ";

         +list_kuf(ch_kuf,1)+SPACE(65)

  endif

  @1,50 SAY [      Должнoсть    ]

  @2,50 GET rank ;

  FUNCTION "^ \<Ректор;\<Проректор;\<Декан;\<Зав. кафедрой;";

           +"\<Профессор;\<Доктор;\<Доцент;\<Ст. преподаватель;";

            +"\<Ассистент;\<Лаборант" DEFAULT 10;

            MESSAGE [Hажатием пробела и Ввода или ]+;

            [удерживанием мыши в этом меню - выбор]

  @5,50 SAY [     Ученое звание  ]

  @6,50 GET zvan FUNCTION "^ \<Профессор;\<Доцент;\<Ст."+;

            " преподаватель;";

            +"\<Ассистент" DEFAULT 4;

            MESSAGE [Hажатием пробела и Ввода или ]+;

            [удерживанием мыши в этом меню - выбор]

  @9,50 SAY "Ф.И.О. преподавателя"

  @10,50 EDIT fio SIZE 2,20,65 SCROLL;

              MESSAGE [Окно редактирования для ввода данных]+;

              [ о преподавателе]

  @13,50 GET knopka FUNCTION "*N \?Выход;\!Следующий;"+;

                             "Предыдущий;Вставить"+;

                             ";Удалить";

        SIZE 1,10,1 VALID fnknopka(knopka) DEFAULT 1

  wait clear

  set sysmenu off

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  *ищем пустые записи и удаляем

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(fio) .or. empty(kafedra) .or. empty(rank) .or.;

       empty(zvan)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

FUNCTION funchkuf

parameters ch_kuf,p2

  replace kafedra with ch_kuf

  @23,1 SAY "Работает на кафедре : "+p2+SPACE(65)

RETURN

FUNCTION fnknopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

      *locate for code=0

      *skip

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select h

  *Поиск кода кафедры

  ch_kuf=0

  for _i=1 to reccount()

    if c.kafedra==list_kuf(_i,2)

      ch_kuf=_i

    endif

  endfor

  select c

  show gets

  if ch_kuf#0

    @23,1 SAY "Работает на кафедре : "+;

              list_kuf(ch_kuf,1)+SPACE(65)

  else

    @23,1 SAY "Работает на кафедре : ????"+SPACE(65)

  endif

RETURN

*  Для каждого пункта меню нам необходимо определить выполняемые

*действия. В нижеследующих процедурах мы будем обслуживать все

*вспомогательные базы данных - по преподавателям, предметам и так

*далее.

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_fuc_proc  ON SELECTION BAR 1 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   10    ¦

*       ¦ Called By:  ON SELECTION BAR 1 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Факультетов                            ¦

*       ¦ Snippet:    4                                      ¦

*       ¦        є

*       УДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЅ

*

*  ќв  Їа®жҐ¤га  ўҐ¤Ґв ®Ўб«г¦Ёў ­ЁҐ Ў §л ¤ ­­ле Ї® д Єг«мвҐв ¬

*®зҐ­м ЇаЁ¬ЁвЁў­л¬ ®Ўа §®¬: ¬л ў­ з «Ґ ЇҐаҐе®¤им в рабочую

*область, где открыта эта база, а затем мы открываем BROWSE -

*окно для ее модификации. Изменять мы позволяем только одно поле

*- name (название кафедры), так как ее код мы сформируем

*программно. Вместо имени поля, как это делает BROWSE по

*умолчанию, мы выводим надпись "Название факультета", а сама

*команда BROWSE выполняется в специально определенном ранее окне

*с именем BrowseWin. По окончании сеанса редактирования записей

*мы проделываем традиционные действия - помечаем к удалению

*имеющиеся, но незаполненные записи и формируем коды для новых

*записей.

PROCEDURE lm_fuc_proc

  select f

  WasChanged(6)=.T.

  browse fields name :h="Hазвание факультета" WINDOW BrowseWin;

         TITLE "База данных по факультетам"+;

         " Cегодня - "+DTOC(DATE())

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_kaf_proc  ON SELECTION BAR 2 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,

*       є Called By:  ON SELECTION BAR 2 OF POPUP lm_popup   є

*       є Prompt:     Кафедр                                 ¦

*       ¦ Snippet:    5                                      ¦

*       ¦

*       УДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЅ

*

*  вагЄвга  Ў §л ¤ ­­ле Ї® Є дҐ¤а ¬ ­ЁзҐ¬ ­Ґ ®в«Ёз Ґвбп ®в

*бвагЄвгал Ѓ„ Ї® д Єг«мвҐв ¬, Ї®нв®¬г Ё Їа®жҐ¤гал Ёе ®Ўб«г¦Ёў ­Ёп

*Їа ЄвЁзҐбЄЁ Ё¤Ґ­вЁз­л.

*

PROCEDURE lm_kaf_proc

  select h

  WasChanged(8)=.T.

  browse fields name :h="H §ў ­ЁҐ Є дҐ¤ал" WINDOW BrowseWin;

         TITLE "База данных по кафедрам"+;

         " Cегодня - "+DTOC(DATE())

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_spec_proc  ON SELECTION BAR 3 OF POPUP lm_popup ¦

*

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   12    ¦

*       ¦ Called By:  ON SELECTION BAR 3 OF POPUP lm_popup   ¦

*       ¦ Prompt:    ЇҐжЁ «м­®бвҐ©                         є

*       є Snippet:    6                                      є

*       є                                                    є

*       УДДДД------------------------------------------------+

*

*  Каждая специальность привязана к какому - либо факультету,

*поэтому мы должны это сделать и в базе. Для этого в БД по

*специальностям заносится код факультета и название

*специальности.

*  Перейдя в рабочую область БД по специальностям, мы для

*удобства редактирования добавим в конец пустую запись. Затем,

*после необходимых проверок, заполним массив названиями и кодами

*факультетов. При этом, если БД по факультетам уже изменялась, ее

*надо упаковать, так как команда копирования базы в массив не

*обращает внимания на то, что запись может быть пустой или

*помеченной к удалению, а занесение такой записи в список

*факультетов (обычно имеющей вид .F.) нежелательно. Далее, найдя

*в списке факультетов тот, который соответствует коду в первой

*записи БД по специальностям, мы запоминаем этот номер, чтобы в

*дальнейшем подсветить его в списке на экране.

*  Все наши действия мы будем проводить не на экране, а в окне,

*которое мы определим и активизируем так, чтобы создаваемы нами

*экранные объекты отображались на экране только после повторной

*активации окна. Это делается для того, чтобы прорисовка окна,

*кнопок, списка и т.д., выполняемые FoxPro в процессе создания

*экранных объектов очень медленно, не были видны.

*  Сконструировав список на экране из элементов массива list_fас,

*мы будем заносить выбор в переменную ch_fас. VALID - функция для

*списка будет вызываться при выборе его элемента с двумя

*параметрами: кодом факультета и его названием. Эта функция код

*занесет в базу данных, а название отобразит на экране.

*  В поле БД с именем name занесем информацию из окна

*редактирования.

*  Сбоку экрана создадим серию из 5 кнопок, одна из которых будет

*по умолчанию (\!), то есть она будет "нажиматься" по комбинации

*Ctrl+Enter, а другая (\?) будет реагировать на нажатие клавиши

*Esc. Для отдельной обработки каждой кнопки в VALID - функцию мы

*передадим ее номер. Свои действия в этой функции мы будем

*сопровождать разнообразными проверками: на выход за пределы

*базы, на пустоту записи и т.д.

*  После формирования всех полей на экране и заполнения их

*информацией из первой записи мы отключим системное меню и

*организуем цикл обработки сообщений, прерываемый по кнопке

*"Выход". Восстановив и отбразив меню, мы удаляем окно и

*занимаемся гигиеной: ищем пустые записи и удаляем их из базы.

PROCEDURE lm_spec_proc

  select g

  WasChanged(7)=.T.

  *browse fields name :h="Hазвание специальности";

*    WINDOW BrowseWin;

        * TITLE "База данных по специальностям"+" Cегодня -

        *"+DTOC(DATE())

  wait window nowait [Инициализация оконной подсистемы...]

  append blank

  go top

  select f&&6

  if WasChanged(6)

    pack

    WasChanged(6)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " факультетам" window;

     NOWAIT

    return

  endif

  dimension list_fac(reccount(),2)

  copy to array list_fac

  *Поиск кода

  for _i=1 to reccount()

    if g.fac==list_fac(_i,2)

      ch_fac=_i

    endif

  endfor

  select g

        DEFINE WINDOW sd3 ;

                FROM INT((SROW()-19)/2),INT((SCOL()-50)/2) ;

                TO INT((SROW()-19)/2)+18,INT((SCOL()-50)/2)+49 ;

                TITLE "База данных по специальностям" ;

                FLOAT NOCLOSE SHADOW DOUBLE COLOR SCHEME 1

        ACTIVATE WINDOW sd3 NOSHOW

@ 2,1 GET ch_fac PICTURE "@&N" FROM list_fac SIZE 13,27;

                 DEFAULT 1 VALID funchfac(list_fac(ch_fac,2),;

                 list_fac(ch_fac,1)) ;

            MESSAGE [Двойным щелчком или нажатием клавиши]+;

            [ Ввод в этом списке производят выбор];

                COLOR SCHEME 9

@ 0,5 SAY "Список факультетов"

@ 16,1 SAY "Факультет :"

  if empty(fac)

  else

    @16,13 SAY list_fac(ch_fac,1)+SPACE(65)

  endif

@ 0,33 SAY "CЇҐжЁ «м­®бвм"

@ 2,32 EDIT name ;

        SIZE 2,15,65 ;

        DEFAULT "" SCROLL;

        MESSAGE [ЋЄ­® аҐ¤ ЄвЁа®ў ­Ёп ¤«п ўў®¤  ¤ ­­ле ®]+;

        [ специальности]

@ 5,33 GET knopka ;

        PICTURE "@*VN \?Выход;\!Следующий;Предыдущий;Вставить"+;

        " ;Удалить" ;

        SIZE 1,12,1 VALID f5knopka(knopka) DEFAULT 1

set sysmenu off

        ACTIVATE WINDOW sd3

  wait clear

READ CYCLE MODAL

RELEASE WINDOW sd3

set sysmenu on

set sysmenu automatic

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(name) .or. empty(fac)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

FUNCTION funchfac

parameters ch,p2

  replace fac with ch

  @16,13 SAY p2+SPACE(65)

RETURN

FUNCTION f5knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

      ch_fac=fac

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select f&&6

  ch_fac=0

  *Поиск кода

  for _i=1 to reccount()

    if g.fac==list_fac(_i,2)

      ch_fac=_i

    endif

  endfor

  select g

  show gets

  if ch_fac#0

    @16,13 SAY list_fac(ch_fac,1)+SPACE(65)

  else

    @16,13 SAY "????"+SPACE(65)

  endif

RETURN

*       +----------------------------------------------------+

*       ¦

*       є lm_pred_proc  ON SELECTION BAR 6 OF POPUP lm_popup є

*       є                                                    є

*       є Procedure Origin:                                  є

*       є                                                    є

*       є From Menu:  POGRUZ.MPR,            Record:   13    є

*       є Called By:  ON SELECTION BAR 6 OF POPUP lm_popup   є

*       є Prompt:     ЏаҐ¤¬Ґв®ў                              є

*       є Snippet:    7                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

*  Для заполнения БД по предметам мы могли бы воспользоваться и

*@...GET - объектами, но не менее красиво это можно сделать одной

*из разновидностей команды BROWSE - CHANGE. Если BROWSE

*предъявляет данные в виде таблицы, то CHANGE - в виде своего

*рода набора карточек: каждая запись выводится в несколько строк,

*в каждой строке находится поле в виде "название - содержимое".

*  По закрытию окна редактирования мы, как обычно, удаляем пустые

*записи (пустой будем в дальнейшем считать запись, у которой

*незаполнено хотя бы одно поле) и формируем коды для новых

*специальностей.

*

*

PROCEDURE lm_pred_proc

  *(code N(4,0),name C(65),link L,chas_week N(3,1),chas_all

  *N(4,1))

  select a

  WasChanged(1)=.T.

  change title [База данных по предметам Сегодня - ]+;

         DTOC(DATE());

         fields name;

                :h='Hазвание предмета',;

                chas_week:h=[Часов в неделю],;

                chas_all:h="Общее число часов",;

                link:h="Связь с аудиторией" WINDOW BrowseWin

  *ищем пуcтые записи и удаляем

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(name) .OR. empty(chas_week) .OR. empty(chas_all)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_aud_proc  ON SELECTION BAR 7 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Orig@n:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   14    ¦

*       ¦ Called By:  ON SELECTION BAR 7 OB POPUP lm_popup   ¦

*       ¦ Prompt:     Аудиторий                              ¦

*       ¦ Snippet:    8                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

*  Окно, в общем то, вещь достаточно условная - с помощью

*"изобразительных средств" самого FoxPro (@... - команд) его

*можно сымитировать с достаточно высокой степенью достоверности,

*что мы и попробуем сделать на примере обслуживания БД по

*аудиториям.

*  Вначале мы перейдем в рабочую область, где расположена база

*данных по аудиториям, добавим в конец пустую запись (для

*удобства редактирования) и установим признак изменения данных в

*массиве WasChanged. Заметим, что признак специализации аудитории

*у нас поле логическое, а в случае использования управляющих

*элементов в стиле Windows мы вынуждены использовать только

*числовые поля; поэтому заведем в программе временную переменную,

*в которой будем помещать 1, если аудитория не специализирована и

*двойку при специализации.

*  На экране командой @1,5 TO 23,69 DOUBLE мы начертим окно без

*тени с двойной рамкой, в котором расположим поля ввода номеров

*корпуса и аудитории, выпадающее меню для выбора емкости

*аудитории и две радио - кнопочки для выбора типа аудитории

*(признака специализации). Для корректной замены данных по

*специализации в базе мы придадим этим кнопкам VALID - функцию

*swap, преобрзующих число из переменной temp в логическое

*значение по описанному выше признаку.

*  В отдельном окошке у нас будет особое мемо - поле, содержимое

*которого не контролируется, но всегда отображается.

*  После отработки цикла сообщений мы, как обычно, очищаем базу

*от пустых записей и формируем код.

*

*

PROCEDURE lm_aud_proc

  *(code n(4,0),korpus n(2,0),aud n(3,0),emkost n(3,0),

  *special L,comment M)

  wait window nowait [Инициализация экрана...]

  select b

  go bottom

  insert blank

  go top

  WasChanged(2)=.T.

  if special=.t.

    temp=2

  else

    temp=1

  endif

  @1,5 TO 23,69 DOUBLE

  @3,11 SAY [Hомер корпуса]

  @3,31 GET korpus MESSAGE [Здесь вводится номер корпуса,]+;

                           [не равный нулю]

  @3,41  SAY [Hомер аудитории]

  @3,61 GET aud MESSAGE [Здесь вводится номер аудитории,]+;

                        [не равный нулю]

  @5,12 SAY [    Емкость]

  @6,13 GET emkost FUNCTION "^ \<Поток;\<Группа;\<Подгруппа";

              DEFAULT 3;

              MESSAGE [Hажатием пробела и Ввода или ]+;

              [удерживанием мыши в этом меню - выбор]

  @9,8 SAY [      Специализация :]

  @11,9 GET temp FUNCTION '*R \<Hе'+;

        ' специализирована;\<Специализирована';

        DEFAULT 1 VALID swap(temp);

        MESSAGE [Hажатием мыши или движением курсора - выбор]

  @14,8 SAY "Описание аудитории (если нужно)"

  @16,7 edit comment SIZE 6,40,3000 SCROLL;

            MESSAGE [Можете описать здесь специфические]+;

            [ особенности данной аудитории]

  @7,55 GET knopka FUNCTION "*N \?Выход;\!Следующий;"+;

        "Предыдущий;Вставить;Удалить";

        SIZE 3,10,2 VALID f3knopka(knopka) DEFAULT 1

  set sysmenu off

  wait clear

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(korpus) .or. empty(aud) .or. empty(emkost)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

FUNCTION swap

parameters t

  if t=1

    replace special with .f.

  else

    replace special with .t.

  endif

RETURN

FUNCTION f3knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

    CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  if special=.f.

    temp=1

  else

    temp=2

  endif

  show gets

RETURN

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_grup_proc  ON SELECTION BAR 5 OF POPUP lm_popup ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   16    ¦

*       ¦ Called By:  ON SELECTION BAR 5 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Потоков                                ¦

*       ¦ Snippet:    10                                     ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

*  Пускай название нижеследующей процедуры Вас не обманывает -

*иметь дело мы будем с базой данных по потокам. Ее экранное

*представление будет достаточно традиционно - список, три

*выпадающих меню и набор стандартных кнопок для перемещения по

*базе.

PROCEDURE lm_grup_proc

  select d

  WasChanged(4)=.t.

  *(code n(5,0),special n(3,0),kurs n(1,0),delta n(2,0))

  *действия по вводу

  * специальность - списками, курс и смещение - popup

  wait window nowait [Инициализация экрана...]

  append blank

  go top

  *Ввод данных по группе

  *Объявим массивы, куда считаем специальности и их коды

  select g&&7

  if WasChanged(7)

    pack

    WasChanged(7)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " специальностям" window;

     NOWAIT

    return

  endif

  dimension list_spec(reccount(),2)

  copy to array list_spec

  *Поиск кода специальности

  for _i=1 to reccount()

    if d.special==list_spec(_i,2)

      ch_spec=_i

    endif

  endfor

  select d

  @1,1  SAY [         Cписок  специальностей             ]

  @2,1  GET ch_spec FROM list_spec SIZE 20,40 DEFAULT 1 ;

  VALID funchspec(list_spec(ch_spec,2),list_spec(ch_spec,1)) ;

            MESSAGE [Hажатием Ввода или щелчком мыши в этом]+;

            [ списке - выбор]

            &&Кидаем код специальности

  if empty(special)

    @23,1 SAY "Специальность : ????"+SPACE(65)

  else

    @23,1 SAY "Специальность : "+list_spec(ch_spec,1)+SPACE(65)

  endif

  @2,48 SAY [Курс]

  @1,53 GET kurs;

  FUNCTION "^ \<Первый;\<Второй;\<Третий;\<Четвертый;\<Пятый";

              DEFAULT 1 MESSAGE [Hажатием пробела и Ввода ]+;

              [или удерживанием мыши в этом меню - выбор]

  @4,48 SAY [Групп в потоке]

  @5,50 GET grpcount ;

      FUNCTION "^ \<Одна;\<Две;\<Три;\<Четыре;\<Пять;"+;

            "\<Шесть;\<Семь;\<Восемь;\<Девять" DEFAULT 1;

        MESSAGE [Hажатием пробела и Ввода или удерживанием ]+;

                [мыши в этом меню - выбор]

  @8,48 SAY "Cмещение по времени"

  @9,50 GET delta ;

  FUNCTION "^ \<Отсутствует;\<Одна пара;\<Две пары;"+;

            "\<Три пары;\<Четыре пары;\<Пять пар" DEFAULT 1;

    MESSAGE [Hажатием пробела и Ввода или удерживанием мыши ]+;

            [в этом меню - выбор]

  @14,50 GET knopka FUNCTION;

         "*N \?Выход;\!Следующий;Предыдущий;Вставить;Удалить";

        SIZE 1,10,1 VALID f2knopka(knopka) DEFAULT 1

  set sysmenu off

  wait clear

  read cycle

  set sysmenu on

  set sysmenu automatic

  clear

  wait window nowait [Корректировка базы...]

  *ищем пустые записи и удаляем

  go top

  do while !eof()

    if empty(special) .or. empty(kurs) .or. empty(grpcount);

       .or. empty(delta)

      delete

    endif

    skip

  enddo

  =codeguard()

  wait clear

RETURN

FUNCTION funchspec

parameters ch_s,p2

  replace special with ch_s

  @23,1 SAY "Специальность : "+p2+SPACE(65)

RETURN

FUNCTION f2knopka

parameters kn

  DO CASE

    CASE kn=1&&quit

      clear read

      return

    CASE kn=2&&next

      if !eof()

        skip

      else

        =bell()

      endif

   CASE kn=3&&prev

      if !bof()

        skip -1

      else

        =bell()

      endif

    CASE kn=4

      *new

      insert blank

    CASE kn=5

      *delete

      delete

      if !eof()

        skip

      else

        =bell()

      endif

  ENDCASE

  select g&&7

  ch_spec=0

  *Поиск кода специальности

  for _i=1 to reccount()

    if d.special==list_spec(_i,2)

      ch_spec=_i

    endif

  endfor

  select d

  if ch_spec#0

    @23,1 SAY "Специальность : "+;

                  list_spec(ch_spec,1)+SPACE(65)

  else

    @23,1 SAY "Специальность : ????"+SPACE(65)

  endif

  show gets

RETURN

*  Основная база данных, содержащая расписание занятий, своими

*полями имеет только коды, и это не удивительно - текстовое

*представление этих полей хранится во вспомогательных базах.

*

*       +----------------------------------------------------+

*       ¦                                                    ¦

*       ¦ lm_par_proc  ON SELECTION BAR 8 OF POPUP lm_popup  ¦

*       ¦                                                    ¦

*       ¦ Procedure Origin:                                  ¦

*       ¦                                                    ¦

*       ¦ From Menu:  POGRUZ.MPR,            Record:   15    ¦

*       ¦ Called By:  ON SELECTION BAR 8 OF POPUP lm_popup   ¦

*       ¦ Prompt:     Пар расписания                         ¦

*       ¦ Snippet:    9                                      ¦

*       ¦                                                    ¦

*       +----------------------------------------------------+

*

*  Нижеследующая процедура - ключевая во всей нашей работе. При

*заполнении расписания очень желательно, чтобы вид экрана был

*максимально приближен к привычной нам (а особенно диспетчеру)

*форме; это позволит, возможно, сделать рассматриваемую нами

*программу используемой практически, а не только в качестве

*учебного пособия. Для этого мы сделаем наш экран рамкой,

*"ползущей" вверх/вниз по дням и влево/вправо по группам; тогда

*мы сможем на нем видеть обе подгруппы для одной группы и один

*день, то есть в общей сложности 2*5=10 клеточек расписания

*(если брать в расчет только одну неделю). В каждой клеточке у

*нас будет 3 поля: предмет, преподаватель и аудитория.

*  За единицу редактирования возьмем один поток. Тогда нам

*понадобится предварительно ввести сведения о факультете,

*специальности на этом факультете, потоке по специальности и виде

*недели (числитель/знаменатель).

*  После того, как все эти данные нам известны, мы можем выдать

*на экран окно с EDIT - полями и запустить цикл обработки

*сообщений от кнопок. В процессе работы с окном расписания мы

*производим переопределение функциональных клавиш F4 - для

*изменения данных в поле и F8 - для удаления нескольких полей.

*Для того, чтобы по окончания программы у нас по этим клавишам

*выполнялись стандартные действия, нам необходимо перед командой

*READ сохранить значения функциональных клавиш во внутреннем

*стеке FoxPro, а после того, как она отработает - восстановить.

PROCEDURE lm_par_proc

  *копируем список кафедр

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "База данных по кафедрам пуста" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

  WasChanged(9)=.t.&&Признак необходимости упаковки в связи с

                   &&изменением расписания

  *потом это надо будет убрать и заполнить нормально

  *Вначале запросим факультет, специальность и поток

  *копируем список факультетов

  select f&&6

  if WasChanged(6)

    pack

    WasChanged(6)=.f.

  endif

  if reccount()==0

    =bell()

    wait "База данных по факультетам пуста" window NOWAIT

    return

  endif

  dimension list_fac(reccount(),2)

  copy to array list_fac

  curfac=0

  do while curfac==0

    =facfn()

  enddo

  *скопируем список специальностей на данном факультете

  select g

  if WasChanged(7)

    pack

    WasChanged(7)=.f.

  endif

  mcount=0

  locate for fac==curfac

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа этом факультете специальностей нет; заполните]+;

    [ базу ] window;

     NOWAIT

    curspec=0

    spectxt=[]

    curfac=0

    factxt=''

    return

  endif

  dimension list_spec(mcount,2)

  mcount=0

  locate for fac==curfac

  DO WHILE FOUND()

    mcount=mcount+1

    list_spec(mcount,1)=name

    list_spec(mcount,2)=code

    CONTINUE

  ENDDO

  curspec=0

  do while curspec==0

    =specfn()

  enddo

  *В список потоков занесем только те, которые есть на

  *факультете по данной специальности

  select d&&4

  if WasChanged(4)

    pack

    WasChanged(4)=.f.

  endif

  mcount=0

  locate for special==curspec

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [По данной специальности на этом факультете ]+;

         [потоков нет!] window NOWAIT

    curspec=0

    spectxt=[]

    curfac=0

    factxt=''

    return

  endif

  dimension list_pot(mcount,2)

  mcount=0

  locate for special==curspec

  DO WHILE FOUND()

    mcount=mcount+1

    list_pot(mcount,1)=STR(kurs,1)+' курс'

    list_pot(mcount,2)=code

    CONTINUE

  ENDDO

  curpot=0

  do while curpot==0

    =potfn()

  enddo

  *Запоmним количество групп

  locate for code==curpot

  m.grpcount=grpcount

  select i

  week=0

  do while !inlist(week,1,2)

    wait [Выбирайте : 1 - числитель, 2 - знаменатель] window;

         to str_buf

    week=val(str_buf)

  enddo

  wait window nowait [Инициализация расписания...]

  day=1

  curgrp=1

  for _i=1 to 30

    marazm(_i,1)=0

    marazm(_i,2)=[]

  endfor

        DEFINE WINDOW mainwindow ;

                FROM 1, 0 ;

                TO 24,77;

                TITLE ALLTRIM(spectxt)+[ , ]+ALLTRIM(pottxt)+[ ]+;

                ALLTRIM(factxt)+[ факультет] ;

                FOOTER "F4 - Изменить, F8 - Удалить пару" ;

                NOCLOSE ;

                SHADOW ;

                SYSTEM ;

                COLOR SCHEME 8

        ACTIVATE WINDOW mainwindow NOSHOW

    @ 0,16+22 SAY [Группа]

    @ 1,16+5  SAY [Подгруппа 1]

    @ 2,16+23 to 20,16+23

    @ 1,16+35  SAY [Подгруппа 2]

  _j=1

  for _i=0 to 4

    sho=1+_i-5*int(_i/5)

    @ _i*4+3,10 SAY str(sho,1)

    @ _i*4+2,16 EDIT marazm(_j,2) ;

        SIZE 1,21,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+3,22 EDIT marazm(_j,2) ;

        SIZE 1,15,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+4,18 EDIT marazm(_j,2) ;

        SIZE 1,19,65 ;

        DEFAULT []

    _j=_j+1

  endfor

  _j=16

  for _i=0 to 4

    sho=1+_i-5*int(_i/5)

    @ _i*4+3,10+35 SAY str(sho,1)

    @ _i*4+2,16+35 EDIT marazm(_j,2) ;

        SIZE 1,21,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+3,22+35 EDIT marazm(_j,2) ;

        SIZE 1,15,65 ;

        DEFAULT []

    _j=_j+1

    @ _i*4+4,18+35 EDIT marazm(_j,2) ;

        SIZE 1,19,65 ;

        DEFAULT []

    _j=_j+1

  endfor

  @ 21,0 GET knopka ;

        PICTURE "@*HN Влево;Вправо;Вверх;Вниз;\?Выход" ;

        SIZE 1,14,1 ;

        DEFAULT 1 VALID knopkafn(knopka) DISABLE

    @ 0,16+30 SAY str(curgrp,1)

    @ 0,0 SAY days_arr(day)

        ACTIVATE WINDOW mainwindow

  push key

  set sysmenu off

  first=.t.

  READ CYCLE MODAL ACTIVATE read_activate()

  set sysmenu on

  set sysmenu automatic

  pop key

  RELEASE WINDOW mainwindow

  wait window nowait [Корректировка базы...]

  go top

  do while !eof()

    if empty(gruppa) .or. empty(para) .or. empty(predmet) ;

       .or. empty(audit) .or. empty(prepod)

      delete

    endif

    skip

  enddo

  wait clear

RETURN

*  Команда READ в предыдущей процедуре имеет у нас три параметра:

*CYCLE для многократного обхода GET / EDIT - полей, MODAL для

*того, чтобы из этог окна нельзя было перейти в меню или другое

*окно и ACTIVATE, используемый в данном случае для начального

*занесения данных в поля, включения кнопок и переопределения

*функциональных клавиш. Кроме того, для быстрого перемещения

*определим 4 клавиши - акселератора, по которым выполняется

*функция обработки сообщений от кнопок с различными параметрами.

function read_activate

  if first

    =refresh()

    on key label f4 do f4proc

    on key label f8 do f8proc&&erasing

    on key label ctrl+LEFTARROW do knopkafn with 1

    on key label ctrl+RIGHTARROW do knopkafn with 2

    on key label PgUp do knopkafn with 3

    on key label PgDn do knopkafn with 4

    show get knopka enable

    wait clear

    first=.f.

  endif

return

*  Процедура освежения экрана, вызывающаяся при начальной

*инициализации и любых других изменениях базы / экрана, имеет

*следующий алгоритм:

*  Прежде всего мы заносим на экран номер группы,день недели и

*очищаем все поля нашего массива, отвечающего за EDIT - поля в

*окне, после чего начинаем заносить данные по лабораторным

*занятиям, делая это в очень простом цикле по номеру подгруппы.

*Найдя запись по номеру подгруппы, мы вычислем ее номер и

*проверяем, в этот ли день проходит данная пара. Если день и

*неделя совпали, то мы переписываем все необходимые нам коды во

*временные переменные и по этим кодам в соответствующих базах

*данных формируем текстовые поля.

*  Текстовое поле для аудитории мы формируем из номера этой

*аудитории и номера корпуса, в которой она располагается, а текст

*для преподавателя - с помощью специальной функции, которая

*формирует строку вида "Доц. Полищук А.П." из полного имени и

*кода ученого звания.

*  Коды и текстовые поля мы заносим в соответствующие элементы

*массива. Между номером подгруппы, номером пары, номером EDIT -

*поля в паре (фактически определяющим, кокого рода данные

*находятся в данном поле) и индексом элемента в массиве

*существует взаимнооднозначное соответствие, устанавливаемое

*функцией GetObject. В дополнительный, третий столбец строки

*предмета мы заносим цифру 3 - признак лабораторного занятия.

*Сформировав полностью данные в массиве, мы командой show get

*принудительно отображаем их в окне.

*  Данные по практичкам и лекциям заносятся также, за одним

*исключением: на экране светятся у нас две подгруппы, а данные по

*практичкам / лекциям идут на группу / поток, поэтому для

*редактирования мы делаем доступными данные лишь в одной

*подгруппе - во второй они будут изменяться автоматически. Во

*избежание случайного ручного изменнения в поля - дубликаты

*заносятся только тексты, без кодов, и редактирование их

*запрещено.

*

*

procedure refresh

  wait window nowait [Освежение экрана...]

  @ 0,16+30 SAY str(curgrp,1)

  @ 0,0 SAY days_arr(day)

  for _i=1 to 30

    marazm(_i,1)=0

    marazm(_i,2)=[]

    marazm(_i,3)=0

    show get marazm(_i,2) enable

  endfor

  *отобразим сделанные изменения

  *(gruppa N(5,0),para N(3,0),predmet N(4,0),audit *

  *N(4,0),prepod N(4,0))

  *код пары формируется следующим образом :

  * неделя - 1 числитель,2 знаменатель

  * день -1 .. 5

  * code=week*100+day*10+number

  * номер пары - 1..5

  *код группы = код потока*100+номер группы*10+номер подгруппы

  *1 - занесем все лабораторки

  for _i=1 to 2 &&для обеиx подгрупп

    locate for gruppa==curpot*100+curgrp*10+_i

    DO WHILE FOUND()

      *Выделим номера недели,дня и пары

      number=para-int(para/10)*10

      if (para-int(para/100)*100-number)/10==day ;

         .and. week==int(para/100)

        curpred=predmet

        curaud=audit

        curprep=prepod

        *Занесем данные по предмету

        select a

        locate for code==curpred

        predtxt=alltrim(name)

        select b

        locate for code==curaud

          if aud#0 .and. korpus#0

            audtxt=[Aуд.]+str(aud,3)+[, корп.]+str(korpus,2)

          else

            audtxt=[]

          endif

        select c

        locate for code==curprep

        preptxt=getprepname(fio,zvan)

        select i

        marazm(getobject(_i,number,1),1)=curpred

        &&Предмет

        marazm(getobject(_i,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(_i,number,1),2) enable

        marazm(getobject(_i,number,1),3)=3 &&Признак лабы

        marazm(getobject(_i,number,2),1)=curprep

        &&Преподававтель

        marazm(getobject(_i,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(_i,number,2),2) enable

        marazm(getobject(_i,number,3),1)=curaud

        &&Аудитория

        marazm(getobject(_i,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(_i,number,3),2) enable

      endif

      CONTINUE

    enddo

  endfor

  *2 занесем практички - на обе подгруппы

  && and lections too

  for _j=1 to 0 step -1

    locate for gruppa==curpot*100+iif(_j==1,curgrp,0)*10

    DO WHILE FOUND()

      *Выделим номера недели,дня и пары

      number=para-int(para/10)*10

      if (para-int(para/100)*100-number)/10==day ;

         .and. week==int(para/100)

        * - заносим в расписание предмет препод и аудиторию

        curpred=predmet

        curaud=audit

        curprep=prepod

        *Занесем данные по предмету

        select a

        locate for code==curpred

        predtxt=alltrim(name)

        select b

        locate for code==curaud

          if aud#0 .and. korpus#0

            audtxt=[Aуд.]+str(aud,3)+[, корп.]+str(korpus,2)

          else

            audtxt=[]

          endif

        select c

        locate for code==curprep

        preptxt=getprepname(fio,zvan)

        select i

        marazm(getobject(1,number,1),1)=curpred

        &&Предмет

        marazm(getobject(1,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(1,number,1),2) enable

        marazm(getobject(1,number,1),3)=1+_j

        &&Признак практички или лекции

        marazm(getobject(1,number,2),1)=curprep

        &&Преподававтель

        marazm(getobject(1,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(1,number,2),2) enable

        marazm(getobject(1,number,3),1)=curaud

        &&Аудитория

        marazm(getobject(1,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(1,number,3),2) enable

        *отобразим дубликат серым цветом

        marazm(getobject(2,number,1),2)=predtxt

        &&Предмет

        show get marazm(getobject(2,number,1),2) disable

        marazm(getobject(2,number,2),2)=preptxt

        &&Преподававтель

        show get marazm(getobject(2,number,2),2) disable

        marazm(getobject(2,number,3),2)=audtxt

        &&Аудитория

        show get marazm(getobject(2,number,3),2) disable

      endif

      CONTINUE

    enddo

  endfor

  wait clear

return

*

*  Думаю, что нижеследующая функция достаточно прозрачна, чтобы

*ее комментировать.

*

function knopkafn

PARAMETERS kn

  DO CASE

    case kn=1&&Влево

      if curgrp#1

        curgrp=curgrp-1

      endif

    case kn=2&&Вправо

      if curgrp#m.grpcount

        curgrp=curgrp+1

      endif

    case kn=3&&Вверх

      if day#1

        day=day-1

      endif

    case kn=4&&Вниз

      if day#5

        day=day+1

      endif

    CASE kn=5&&Выход

      clear read

      return

  endcase

  =refresh()

return

*  По нажатию клавиши F8 мы удаляем данные по паре. Делать будем

*это так:

*  В FoxPro есть специальная системная переменная _CUROBJ,

*в которой хранится номер текущего GET - объекта. Всего на экране

*5*2*3==30 EDIT - полей и 5 кнопок. Нам интересен лишь тот

*случай, когда данная функциональная клавиша нажата на одном из

*полей редактирования. По его номеру мы с помощью функции GetWhat

*определяем тип поля, в котором мы находимся - то ли это поле с

*данными по предмету, то ли по преподавателю, то ли по аудитории,

*и в зависимости от типа поля корректируем переменную _i так,

*чтобы она указывала на поле предмета.

*  Сфорировав индекс поля предмета в массиве, мы проверяем код на

*пустоту - а занесены ли там данные по предмету, и, если они

*присутствуют, а пользователь уверен в том, что удалить эту пару

*ему жизненно необходимо, мы, в зависимости от типа пары

*формируем код группы и пары для поиска. Найдя искомую пару в БД,

*мы ее удаляем, освежаем экран и восстанавливаем позицию курсора

*на том же месте, с которого начали.

*

*

procedure f8proc

  if _CUROBJ<31

    SAVEOBJ=_CUROBJ

    DO CASE

      CASE getwhat(SAVEOBJ)=1           &&Предмет

        _i=SAVEOBJ

      CASE getwhat(SAVEOBJ)=2           &&Преподаватель

        _i=SAVEOBJ-1

      CASE getwhat(SAVEOBJ)=3           &&Аудитория

        _i=SAVEOBJ-2

    ENDCASE

    if marazm(_i,1)#0

      if erase_ch()

        DO CASE

          CASE marazm(_i,3)==1

            _j=curpot*100

          CASE marazm(_i,3)==2

            _j=curpot*100+curgrp*10

          CASE marazm(_i,3)==3

            _j=curpot*100+curgrp*10+getpg(SAVEOBJ)

        ENDCASE

        select i

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

                  .and. gruppa==_j

        delete

        *обновим экран

        =refresh()

      endif

    endif

    _CUROBJ=SAVEOBJ

  endif

return

*  Эта очень простая функция фомирует на экране окно с запросом

*об удалении и возвращает решение пользователя, преобразовав код

*выбранной клавиши в логическое значение.

*

function erase_ch

        DEFINE WINDOW werase_ch ;

                FROM INT((SROW()-11)/2),INT((SCOL()-29)/2) ;

                TO INT((SROW()-11)/2)+10,INT((SCOL()-29)/2)+28 ;

                TITLE "Вы уверены?" ;

                FLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 7

        ACTIVATE WINDOW werase_ch NOSHOW

  @ 7,2 GET knopka ;

        PICTURE "@*HT \!Удалить;Отставить" ;

        SIZE 1,11,2 ;

        DEFAULT 2

  @ 0,4 SAY "Выбранная Вами пара"

  @ 1,1 SAY "будет удалена не только с"

  @ 2,3 SAY "экрана, но и из базы"

  @ 3,3 SAY "данных по расписанию."

  @ 4,4 SAY "Действительно ли Вы"

  @ 5,3 SAY "хотите именно этого?"

  @ 6,0 TO 8,14

  @ 6,14 TO 8,26

  @ 6,14 SAY "-"

  @ 8,14 SAY "-"

        ACTIVATE WINDOW werase_ch

  READ CYCLE MODAL

  RELEASE WINDOW werase_ch

RETURN IIF(knopka==1,.t.,.f.)

*  По клавише F4 в зависимости от того, на поле какого типа

*находится курсор, мы соответственно вызываем функции для ввода

*данных по предмету, преподавателю и аудитории.

*

procedure f4proc

  if _CUROBJ<31

    SAVEOBJ=_CUROBJ

    DO CASE

      CASE getwhat(SAVEOBJ)=1           &&Предмет

        =predfn()

      CASE getwhat(SAVEOBJ)=2           &&Преподаватель

        =kuffn()

      CASE getwhat(SAVEOBJ)=3           &&Аудитория

        =audfn()

    ENDCASE

    _CUROBJ=SAVEOBJ

  endif

return

*  Эта функция по номеру подгруппы, номеру пары и типу поля

*возвращает индекс строки в массиве.

*

*

function getobject

parameters podgruppa,number,chto

return (podgruppa-1)*15+(number-1)*3+chto

procedure getall

parameters object,podgruppa,number,chto

*из объекта получим все данные

  chto=iif(mod(object,3)==0,3,mod(object,3))

  podgruppa=iif(mod(object,15)==0,object/15,int(object/15)+1)

  number=iif(mod(object,3)==0,object/3,int(object/3)+1)

  &&абсолютный номер

  number=iif(mod(number,5)==0,5,mod(number,5))

return

*

*  В этой функции по номеру строки в массиве мы можем определить

*ее тип, то есть какого рода данные находятся в этой строке - по

*предмету, по преподавателю или аудитории.

function getwhat

parameters object

return iif(mod(object,3)==0,3,mod(object,3))

*  По номеру строки определяем номер подгруппы

function getpg

parameters object

return iif(mod(object,15)==0,object/15,int(object/15)+1)

*  По номеру строки определяем номер пары

function getnumber

parameters object

  number=iif(mod(object,3)==0,object/3,int(object/3)+1)

  &&абсолютный номер

  number=iif(mod(number,5)==0,5,mod(number,5))

return number

*  Функции potfn, specfn и facfn отображают на экране списки

*потоков, специальностей и факультетов и возвращают и выбор в

*cur... и ...txt - переменных.

*

function potfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW potwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор потока" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW potwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_pot ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lpothundle(choice)

  ACTIVATE WINDOW potwindow

  READ CYCLE MODAL

  RELEASE WINDOW potwindow

return

Function lpothundle

parameters vib

  clear read

  curpot=list_pot(vib,2)

  *занесем выбор в полосу редактирования

  pottxt=list_pot(vib,1)

RETURN

function specfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW specwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор специальности" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW specwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_spec;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lspechundle(choice)

        ACTIVATE WINDOW specwindow

  READ CYCLE MODAL

  RELEASE WINDOW specwindow

Return

Function lspechundle

parameters vib

  clear read

  curspec=list_spec(vib,2)

  *занесем выбор в полосу редактирования

  spectxt=list_spec(vib,1)

  *откроем специальность и очистим его выборы

  pottxt=''

  curpot=0

RETURN

function facfn

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW  facwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Окно выбора факультета" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW facwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_fac ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lfachundle(choice)

  ACTIVATE WINDOW facwindow

  READ CYCLE MODAL

  RELEASE WINDOW facwindow

Return

Function lfachundle

parameters vib

  clear read

  curfac=list_fac(vib,2)

  *занесем выбор в полосу редактирования

  factxt=list_fac(vib,1)

  *откроем специальность и очистим его выборы

  spectxt=''

  curspec=0

  pottxt=[]

  curpot=0

RETURN

*

*  Эта функция возвращает случайное целое число, лежащее в

*заданном диапазоне значений.

*

FUNCTION irand

parameters i, j

RETURN int((j-i+1)*rand()+i)

NOTE Дзвоник

PROCEDURE bell

  *set bell to irand(19,10000),irand(1,19)

  ?CHR(7)

RETURN

FUNCTION getprepname

PARAMETERS f,r

  string=ALLTRIM(f)

  pos=AT([ ],string,1)

  if pos#0

    tempstr=LEFT(string,pos)+SUBSTR(string,pos+1,1)+[.]

    pos=AT([ ],string,2)

    if pos#0

      string=tempstr+SUBSTR(string,pos+1,1)+[.]

    endif

  endif

  DO CASE

*  "^ \<Профессор;\<Доцент;\<Ст. преподаватель;\<Ассистент"

    CASE r=1

      string="Проф. "+string

    CASE r=2

      string="Доц. "+string

    CASE r=3

      string="Ст.преп. "+string

    CASE r=4

      string="Асс. "+string

  ENDCASE

return string

function audfn

  *Список аудиторий сформируем так : если лекция или

  *практичка, то соответственно заносим лекционные или

  *практический аудитории

  *Если лабораторка не специализированная - заносим все

  *лабораторные, иначе - только лабораторные

  *специализированные

  *  Подумать, нельзя ли отдавать под лабы пр и лек ауд, под

  *практику лекционнные ?

  if WasChanged(2)&&b

    pack

    WasChanged(2)=.f.

  endif

  if marazm(SAVEOBJ-2,1)=0

    wait window nowait [Вначале необходимо выбрать предмет]

    return

  endif

  if marazm(SAVEOBJ-1,1)=0

    wait window nowait [Пожалуйста, выберите преподавателя]

    return

  endif

  *if marazm(SAVEOBJ-2,1)#0

  select a

  locate for code==marazm(SAVEOBJ-2,1)

  select b

  mcount=0

  if marazm(SAVEOBJ-2,3)==3

  *поиск аудитории, связанной с предметом, только если labor

    locate for emkost==marazm(SAVEOBJ-2,3) ;

          .and. special==a.link

  else

    locate for emkost==marazm(SAVEOBJ-2,3) ;

          .or. emkost==marazm(SAVEOBJ-2,3)-1

  endif

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Аудиторий требуемой емкости нет - заполните базу!];

        window

    RETURN

  endif

  dimension list_aud(mcount,2)

  mcount=0

  if marazm(SAVEOBJ-2,3)==3

    locate for emkost==marazm(SAVEOBJ-2,3) .and. ;

          special==a.link

  else

    locate for emkost==marazm(SAVEOBJ-2,3) ;

        .or. emkost==marazm(SAVEOBJ-2,3)-1

  endif

  DO WHILE FOUND()

    mcount=mcount+1

    list_aud(mcount,1)=[Aуд.]+STR(aud,4)+',кoрп.'+;

                       STR(korpus,2)

    list_aud(mcount,2)=code

    CONTINUE

  ENDDO

*еще один штрих - ищем в расписании

*только те аудитории, которые на данную пару свободны

  select i

  xcount=0

  for _i=1 to mcount

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

          .and. audit==list_aud(_i,2)

    if found()&&Hа эту пару аудитория занята

      list_aud(_i,2)=0

      list_aud(_i,1)=[Занята !]+list_aud(_i,1)

      xcount=xcount+1

    endif

  endfor

  if xcount==mcount

    =bell()

    wait [Аудиторий требуемой емкости нет - все заняты] ;

         window NOWAIT

    return

  endif

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW audwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор аудитории" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW audwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_aud ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID laudhundle(choice)

  ACTIVATE WINDOW audwindow

  READ CYCLE MODAL

  RELEASE WINDOW audwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбрана аудитория, то находим эту пару в базе и

    &&меняем в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ-2,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ-2,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ-2,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace audit with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2)

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ-2,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

      endif

    endif

  endif

  select i

return

Function laudhundle

Parameter vib

  if list_aud(vib,2)#0

    clear read

    marazm(SAVEOBJ,1)=list_aud(vib,2)

    *занесем выбор в полосу редактирования

    marazm(SAVEOBJ,2)=list_aud(vib,1)

  endif

RETURN

function prepfn

  *В список преподавателей занесем только тех, которые

  *работают на данной кафедре

  if WasChanged(3)

    pack

    WasChanged(3)=.f.

  endif

  select c

  * prepod (code N(4,0), kafedra N(3,0), fio C(65), rank

  *N(2,0), zvan N(2,0))

  mcount=0

  locate for kafedra==curkuf

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа данной кафедре никто не работает! Заполните ]+;

         [базу] window NOWAIT

    curkuf=0

    kuftxt=[]

    return

  endif

  dimension list_prep(mcount,2)

  locate for kafedra==curkuf

  mcount=0

  DO WHILE FOUND()

    mcount=mcount+1

    list_prep(mcount,1)=getprepname(fio,zvan)

    list_prep(mcount,2)=code

    CONTINUE

  ENDDO

  select i

  *cвободен ли преподаватель?

  xcount=0

  for _i=1 to mcount

    locate for para==week*100+day*10+getnumber(SAVEOBJ);

               .and. prepod==list_prep(_i,2)

    if found()&&Hа эту пару преподаватель занят

      list_prep(_i,2)=0

      list_prep(_i,1)=[Занят !]+list_prep(_i,1)

      xcount=xcount+1

    endif

  endfor

  if xcount==mcount

    =bell()

    wait [Все преподаватели кафедры на этой паре заняты];

          window NOWAIT

    return

  endif

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW prepwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Выбор преподавателя" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW prepwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_prep ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID lprephundle(choice)

  ACTIVATE WINDOW prepwindow

  READ CYCLE MODAL

  RELEASE WINDOW prepwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбран, то находим эту пару в базе и меняем

    &&в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ-1,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ-1,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ-1,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace prepod with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2) enable

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ-1,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

        show get marazm(SAVEOBJ+16,2) disable

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

        show get marazm(SAVEOBJ-14,2) disable

      endif

    endif

  endif

return

Function lprephundle

Parameter vib

  if list_prep(vib,2)#0

    clear read

    marazm(SAVEOBJ,1)=list_prep(vib,2)

    *занесем выбор в полосу редактирования

    marazm(SAVEOBJ,2)=list_prep(vib,1)

  endif

RETURN

function kuffn

  aa=irand(1,5)

  cc=irand(1,50)

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if marazm(SAVEOBJ-1,1)==0

    wait window nowait [Вначале необходимо выбрать предмет]

    return

  endif

        DEFINE WINDOW  kufwindow ;

                from aa,cc to aa+19,cc+29;

                TITLE "Окно выбора кафедры" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

                *FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                *TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

        ACTIVATE WINDOW  kufwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_kuf ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID Lkufhundle(choice)

        ACTIVATE WINDOW kufwindow

  READ CYCLE MODAL

  RELEASE WINDOW  kufwindow

  if curkuf#0

    =prepfn()

  endif

Return

FUNCTION Lkufhundle

Parameter vib

  clear read

  curkuf=list_kuf(vib,2)

  *занесем выбор в полосу редактирования

  kuftxt=list_kuf(vib,1)

  *откроем преподавателя и очистим его выборы

  marazm(SAVEOBJ,2)=''

  marazm(SAVEOBJ,1)=0

*  predtxt=[]

*  curpred=0

RETURN

function predfn

  *Создадим и заполним список предметов по формату имя-код

  select a

  if WasChanged(1)

    pack

    WasChanged(1)=.f.

  endif

  if reccount()=0

    =bell()

    wait [База данных по предметам пуста! Заполните] window;

         NOWAIT

  endif

  dimension list_pred(reccount(),2)

  for _i=1 to reccount()

    go _i

    list_pred(_i,1)=name

    list_pred(_i,2)=code

  endfor

  select i

  aa=irand(1,5)

  cc=irand(1,50)

        DEFINE WINDOW predwindow ;

                FROM INT((SROW()-19)/2),INT((SCOL()-49)/2) ;

                TO INT((SROW()-19)/2)+18,INT((SCOL()-49)/2)+48 ;

                TITLE "Окно выбора предмета" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 4

        ACTIVATE WINDOW predwindow NOSHOW

  @ 3,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_pred ;

        SIZE 14,45 ;

        DEFAULT 1 ;

        COLOR SCHEME 9 VALID Lpredhundle(choice)

  @ 0,16 GET droblevel ;

        PICTURE "@*RVN   Лекция; Практичка;Лабораторка" ;

        SIZE 1,15,0 ;

        DEFAULT 3 ;

        COLOR SCHEME 8

        ACTIVATE WINDOW predwindow

  READ CYCLE MODAL

  RELEASE WINDOW predwindow

  if marazm(SAVEOBJ,1)#0

    &&если выбран, то находим эту пару в базе и меняем

    &&в базе, как и на экране

    select i

    DO CASE

      CASE marazm(SAVEOBJ,3)==1&&лекция

        findpar=curpot*100

      CASE marazm(SAVEOBJ,3)==2&&практ

        findpar=curpot*100+curgrp*10

      CASE marazm(SAVEOBJ,3)==3&&лаба

        findpar=curpot*100+curgrp*10+getpg(SAVEOBJ)

    ENDCASE

    locate for para==week*100+day*10+getnumber(SAVEOBJ) ;

               .and. gruppa==findpar

    replace predmet with marazm(SAVEOBJ,1)

    show get marazm(SAVEOBJ,2)

    *не надо ли отобразить в заблокированном окошке?

    if marazm(SAVEOBJ,3)#3&& если это не лаборатоpка

      if getpg(SAVEOBJ)==1

        marazm(SAVEOBJ+15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ+15,2) disable

        &&*   +-----------+

        show get marazm(SAVEOBJ+16,2) disable

        &&*   ¦-----+-----¦

        show get marazm(SAVEOBJ+17,2) disable

        &&*   +-----------+

      else

        marazm(SAVEOBJ-15,2)=marazm(SAVEOBJ,2)

        show get marazm(SAVEOBJ-15,2) disable

        show get marazm(SAVEOBJ-14,2) disable

        show get marazm(SAVEOBJ-13,2) disable

      endif

    else

      if getpg(SAVEOBJ)==1

        if marazm(SAVEOBJ+15,1)=0

          for _i=15 to 17

            marazm(SAVEOBJ+_i,2)=[]

            show get marazm(SAVEOBJ+_i,2) enable

          endfor

        endif

      else

        if marazm(SAVEOBJ-15,1)=0

          for _i=15 to 13 step -1

            marazm(SAVEOBJ-_i,2)=[]

            show get marazm(SAVEOBJ-_i,2) enable

          endfor

        endif

      endif

    endif

  endif

Return

  *а не перебрали ли мы часов - вставить обработку потом

Function lpredhundle

Parameter vib

  *Прежде всего - несколько проверок

  *1 - если это лекция, имеем ли мы право на

  *замену - нет ли пр или лаб

  *в этот день на этой паре на этой неделе

  *(gruppa N(5,0),para N(3,0),predmet N(4,0),

  *audit N(4,0),prepod N(4,0))

  *код пары формируется следующим образом :

  * неделя - 1 числитель,2 знаменатель

  * день -1 .. 5

  * номер пары - 1..5

  * code=week*100+day*10+number

  *код группы = код потока*100+номер группы*10+номер подгруппы

  select i

  *Один очень нескромный вопрос - а если мы изменяемудаляя -

  *что делать ? Вероятно, надо удалить старое и вставить новое

  DO CASE

    CASE droblevel==1

      *поищем лекцию

      locate for curpot*100==gruppa .and. ;

                 para==week*100+day*10+getnumber(SAVEOBJ)

      if FOUND()

        *заменяем одну лекцию другой

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        *marazm(SAVEOBJ,3)=1

      else

        *лекции нет - это новая и есть опасность наложения на

        *практички или лабораторки

        for _i=1 to m.grpcount

          for _j=0 to 2

            locate for para==week*100+day*10+;

                       getnumber(SAVEOBJ) .and. ;

                       gruppa==curpot*100+_i*10+_j

            if FOUND()

              wait window nowait [Это не может быть лекция]+;

                   [ - есть подгруппы, ]+;

                   [в которых эта пара занята]

              droblevel=2

              show get droblevel

              return

            endif

          endfor

        endfor

        *если нет пересечений - заполняем  !

        *заносим в базу пустышулечку not a'la SQL

        insert blank

        replace para with week*100+day*10+getnumber(SAVEOBJ)

        replace gruppa with curpot*100

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        marazm(SAVEOBJ,3)=1

        *заставим выбрать заново аудиторию?   -   уточнить

        *.........

      endif

    CASE droblevel==2

      *поищем практичку

      locate for curpot*100+curgrp*10==gruppa .and. ;

                 para==week*100+day*10+getnumber(SAVEOBJ)

      if FOUND()

        *заменяем одну практичку другой

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        *marazm(SAVEOBJ,3)=2

      else

        *практики нет - это новая и есть опасность

        *     наложения на лабораторки

        for _j=1 to 2

          locate for para==week*100+day*10+getnumber(SAVEOBJ);

                    .and. gruppa==curpot*100+curgrp*10+_j

          if FOUND()

            wait window nowait [Это не может быть практика]+;

                 [ - есть подгруппы, ]+;

                 [в которых эта пара занята]

            droblevel=3

            show get droblevel

            return

          endif

        endfor

        *А вдруг на этом месте была лекция?

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

                   .and. gruppa==curpot*100

        if !FOUND()

          *если нет пересечений - заполняем  !

          insert blank

          replace para with week*100+day*10+getnumber(SAVEOBJ)

        endif

        replace gruppa with curpot*100+curgrp*10

        clear read

        marazm(SAVEOBJ,1)=list_pred(vib,2)

        *занесем выбор в полосу редактирования

        marazm(SAVEOBJ,2)=list_pred(vib,1)

        marazm(SAVEOBJ,3)=2

        if FOUND()

          SAVEOBJ=SAVEOBJ+2

          =audfn()

          SAVEOBJ=SAVEOBJ-2

        endif

      endif

    CASE droblevel==3

      locate for curpot*100+curgrp*10+getpg(SAVEOBJ)==gruppa;

                .and. para==week*100+day*10+getnumber(SAVEOBJ)

      if !FOUND()

        *А вдруг на этом месте была лекция?

        locate for para==week*100+day*10+getnumber(SAVEOBJ);

              .and. gruppa==curpot*100

        if FOUND()

          replace gruppa with curpot*100+curgrp*10+;

                  getpg(SAVEOBJ)

        else

          *А вдруг на этом месте была практичка?

          locate for para==week*100+day*10+getnumber(SAVEOBJ);

                    .and. gruppa==curpot*100+curgrp*10

          if FOUND()

            replace gruppa with curpot*100+curgrp*10+;

                    getpg(SAVEOBJ)

          else

            *если нет пересечений - заполняем  !

            insert blank

            replace para with week*100+day*10+getnumber(SAVEOBJ)

            replace gruppa with curpot*100+curgrp*10+;

                    getpg(SAVEOBJ)

          endif

        endif

      endif

      clear read

      marazm(SAVEOBJ,1)=list_pred(vib,2)

      *занесем выбор в полосу редактирования

      marazm(SAVEOBJ,2)=list_pred(vib,1)

      marazm(SAVEOBJ,3)=3

      if FOUND()

        SAVEOBJ=SAVEOBJ+2

        =audfn()

        SAVEOBJ=SAVEOBJ-2

      endif

  endcase

RETURN

PROCEDURE prep_rasp

*Вывод расписания для преподавателя в файл

  emoe=getfilename()

  handle = FCREATE(emoe) && Открытие  файла

  IF handle < 0               && Ошибка при создании файла

    DO CASE                     && Определение ошибки

      CASE FERROR() = 4

                reason = 'Много файлов открыто'

          CASE FERROR() = 5

                reason = 'Доступ невозможен'

          CASE FERROR() = 8

                reason = 'Hет памяти'

      CASE FERROR() = 29

                reason = 'Hет места на диске'

          CASE FERROR() = 31

                reason = 'Общий сбой'

        ENDCASE

        *** Вывод ошибки ***

        WAIT WINDOW 'Файл не создан: '+ reason NOWAIT

        RETURN

  ENDIF

  select h&&8

  if WasChanged(8)

    pack

    WasChanged(8)=.f.

  endif

  if reccount()==0

    =bell()

    wait "Вначале необходимо заполнить базу данных по"+;

         " кафедрам" window NOWAIT

    return

  endif

  dimension list_kuf(reccount(),2)

  copy to array list_kuf

        DEFINE WINDOW  kufwindow ;

                FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

                TITLE "Окно выбора кафедры" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

        ACTIVATE WINDOW  kufwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_kuf ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID L2kufhundle(choice)

        ACTIVATE WINDOW kufwindow

  READ CYCLE MODAL

  if curkuf=0

    return

  endif

  *В список преподавателей занесем только тех, которые

  *работают на данной кафедре

  select c

  * prepod (code N(4,0), kafedra N(3,0), fio C(65), rank

  * N(2,0), zvan N(2,0))

  mcount=0

  locate for kafedra==curkuf

  DO WHILE FOUND()

    mcount=mcount+1

    CONTINUE

  ENDDO

  if mcount==0

    =bell()

    wait [Hа данной кафедре никто не работает! ]+;

         [Заполните базу] window NOWAIT

    curkuf=0

    kuftxt=[]

    return

  endif

  dimension list_prep(mcount,2)

  locate for kafedra==curkuf

  mcount=0

  DO WHILE FOUND()

    mcount=mcount+1

    list_prep(mcount,1)=getprepname(fio,zvan)

    list_prep(mcount,2)=code

    CONTINUE

  ENDDO

  select i

        DEFINE WINDOW prepwindow ;

                FROM INT((SROW()-20)/2),INT((SCOL()-30)/2) ;

                TO INT((SROW()-20)/2)+19,INT((SCOL()-30)/2)+29 ;

                TITLE "Выбор преподавателя" ;

                FOOTER "Click there" ;

                NOFLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 1

  RELEASE WINDOW  kufwindow

        ACTIVATE WINDOW prepwindow NOSHOW

  @ 0,1 GET choice ;

        PICTURE "@&N" ;

        FROM list_prep ;

        SIZE 18,26 ;

        DEFAULT 1 ;

        COLOR SCHEME 2 VALID l2prephundle(choice)

  ACTIVATE WINDOW prepwindow

  READ CYCLE MODAL

  RELEASE WINDOW prepwindow

  if curprep=0

    return

  endif

  wait window nowait [Генерация расписания...]

  =FPUTS(handle,[             Расписание])

  =FPUTS(handle,[Кафедра       : ]+ALLTRIM(kuftxt))

  =FPUTS(handle,[Преподаватель : ]+ALLTRIM(preptxt))

  select i

  for _d=1 to 5

    =FPUTS(handle,[])

    =FPUTS(handle,[])

    =FPUTS(handle,[       ]+days_arr(_d))

    for _p=1 to 5

      =FPUTS(handle,[])

      =FPUTS(handle,[       ]+STR(_p,1)+[ пара])

      for _w=1 to 2

        =FPUTS(handle,week_arr(_w))

        select i

        locate for prepod==curprep .and. para==_w*100+_d*10+_p

        if FOUND()

          select a

          locate for code==i.predmet

          mmm=alltrim(name)

          select d

          locate for code==int(i.gruppa/100)

          select g

          locate for code==d.special

          nnn=alltrim(name)+[,]+STR(d.kurs,1)+[ курс]

          select i

          if mod(gruppa,100)==0&&lect

            mmm=mmm+[, лекция]

          else

            if mod(gruppa,10)==0&&pract

              mmm=mmm+[, практичка]

              nnn=nnn+[, ]+STR(int(mod(gruppa,100)/10),1)+;

                  [ группа]

            else

              mmm=mmm+[, лабораторка]

              nnn=nnn+[, ]+STR(int(mod(gruppa,100)/10),1)+;

                      [ группа, ]+;

                      STR(mod(gruppa,10),1)+[ подгруппа]

            endif

          endif

          =FPUTS(handle,mmm)

          =FPUTS(handle,nnn)

          select b

          locate for code==i.audit

          =FPUTS(handle,"Ауд. "+STR(aud,3)+[, корп. ]+;

                        STR(korpus,1))

        else

          =FPUTS(handle,[])

          =FPUTS(handle,[])

          =FPUTS(handle,[])

        endif

      endfor

    endfor

  endfor

  select i

  =FCLOSE(handle)       && Close the file

  wait clear

  DEFINE WINDOW wingets FROM 1,0 TO 24,79 ;

                CLOSE FLOAT SHADOW SYSTEM ;

                COLOR SCHEME 8 TITLE ' Расписание для преподавателя '

  MODIFY FILE (emoe) WINDOW wingets && View the file

  RELEASE WINDOW  &&Удаление окна

RETURN

FUNCTION getfilename

        DEFINE WINDOW wfilename ;

                FROM INT((SROW()-11)/2),INT((SCOL()-57)/2) ;

                TO INT((SROW()-11)/2)+10,INT((SCOL()-57)/2)+56 ;

                FLOAT ;

                NOCLOSE ;

                SHADOW ;

                DOUBLE ;

                COLOR SCHEME 5

        ACTIVATE WINDOW wfilename NOSHOW

@ 6,13 EDIT filename ;

        SIZE 1,28,65 ;

        DEFAULT " "

@ 8,24 GET knopka ;

        PICTURE "@*HT \<Ok" ;

        SIZE 1,6,1 ;

        DEFAULT 1

@ 0,13 SAY "Введите имя файла, в который"

@ 1,16 SAY "будет осуществлен вывод"

@ 2,13 SAY "расписания для корректировки"

@ 3,16 SAY "и дальнейшей распечатки"

@ 4,13 SAY "Файл не должен существовать."

        ACTIVATE WINDOW wfilename

READ CYCLE MODAL

RELEASE WINDOW wfilename

RETURN alltrim(filename)

FUNCTION L2kufhundle

Parameter vib

  clear read

  curkuf=list_kuf(vib,2)

  *занесем выбор в полосу редактирования

  kuftxt=list_kuf(vib,1)

  *откроем преподавателя и очистим его выборы

  preptxt=[]

  curprep=0

RETURN

Function l2prephundle

Parameter vib

  clear read

  curprep=list_prep(vib,2)

  *занесем выбор в полосу редактирования

  preptxt=list_prep(vib,1)

RETURN

*Формирование кода

procedure codeguard

  *ищем запись с нулевым кодом

  for _i=1 to reccount()

    go _i

    if empty(code)

      *ищем по всей базе незанятый код

      tr=.f.&&.t. - когда найдем

      num=1

      do while !tr

        locate for code==num

        if !found() &&код уникальный

          tr=.t.

          loop

        endif

        num=num+1

      enddo

      go _i

      replace code with num

    endif

  endfor

Return

*Hу вот и всё................................

*    В качестве задания на погружение Вам предлагается

*    воспользоваться процедурой prep_rasp в качестве образца и

*    составить подпрограммы для удовлетворения следующих

*    запросов:

*      1. Расписание для кафедры, дополнительно предполагает

*         цикл по преподавателям.

*      2. Нагрузка для данной аудитории

*      3. Вывод расписания для факультета на экран / принтер