Текст
                    Я. Белецкии
ЭНЦИКЛОПЕДИЯ
ЯЗЫКА
Си
Перевод с польского
канд. техн, наук А. Д. Плитмана,
канд. техн, наук М. Ю. Рачкова, А. В. Стрельцова
под редакцией
канд. техн, наук Ф. Ф. Пащенко
Москва “Мир“ 1992

ББК 22.19 Б43 УДК 681.3.06 Белецкий Я. Б43 Энциклопедия языка Си: Пер. с польск.- М.: Мир, 1992.- 687 с.,1 ISBN 5-03-002113-2 Книга известного польского ученого посвящена подробному описанию языка Си, графических возможностей, использованию Турбо ассемблера и Турбо отладчика. Изло нме сопровождается многочисленными примерами. Для квалифицированных пользователей персональных компьютеров и программиста 2404010000 - 035 Б------------------111-92 041(01) - 92 ББК 2 i ! Редакция литературы по информатике и робототехнике ISBN 5-03-002113-2 (русск.) ISBN 83-206-0919-4 (польск.) © Copyright by Wydawnictwa Komunikacjl i Lacznosci, Warszawa 1989 * © перевод на русский язык, А.Д. Плитман, М.Ю. Pant А.В. Стрельцов, 1992 I
Содержание Предисловие. Перевод М. Ю. Рачкова.............................................5 ЧАСТЬ I. ОПЕРАЦИОННАЯ СРЕДА. Перевод М. Ю. Рачкова....................................6 1. Введение..................................................................6 1.1. Основные понятия....................................................6 1.2. Использование меню.................................................12 1.3. Составление программ............................................. 14 1.4. Компиляция модулей и программ......................................16 1.5. Исправление ошибок............................................... 19 1.6. Отладка программ...................................................20 2. Системы..................................................................30 2.1. Интегрированная система............................................30 2.2. Система загрузки................................................. 43 ЧАСТЬ II. ЯЗЫК. Перевод М Ю. Рачкова..............................................46 3- Лексические понятия.......................-............................ 46 3.1. Комментарии...................................................... 47 3.2. Идентификаторы.....................................................47 3.3. Ключевые слова............................... ...................48 3.4. Литералы...........................................................48 4. Описания..................................................................51 4.1. Описание простых переменных.......................................51 4.2. Описание блоков переменных........................................54 4.3. Определение класса памяти.........................................59 4.4. Присвоение начальных значений (инициализация объекта).............60 4.5. Описание типов....................................................62 4.6. Области действия и структура программы............................63 5. Выражения.............................................................. 64 5.1 • Первичные выражения и 1-выражения................................66 5.2. Арифметические операторы....................................... 71 5.3. Операторы сравнения...............................................73 5.4. Логические операторы..............................................74 5.5. Битовые операторы.................................................75 5.6. Операторы сдвига................................................ 76 5.7. Операторы “доступа по указателю (*)“ и “указатель (&)“............76 5.8. Операторы присваивания............................................77 5.9. Условный оператор.................................................79 5.10. Оператор соединения.............................................79 5.11. Оператор преобразования.........................................80 5.12. Оператор размера................................................82 5.13. Обработка выражений..............................................82 6. Операторы.................................................................85 6.1. Оператор-выражение................................................85 6.2. Пустой оператор............................................... 86 6.3. Оператор перехода............................................... 8б 6.4. Блок.......................................................... 86 6.5. Условный оператор............................................... 87 6.6. Операторы цикла................................................. 88 6.7. Оператор продолжения..............................................90 6.8. Оператор завершения...............................................91 6.9. Оператор-переключатель............................................91 6.10. Оператор возврата............................................у----93 7- Функции...................................................................94 7-1. Связь параметров с аргументами.....................................96 7.2. Получение результата................................. -..........99 7.3. Рекурсия............................................................. 7.4. Параметры функции main ...л........-..............................Ю2 8. Принципы выполнения операции ввода-вывода................................... 104
9. Управление оперативной памятью............................................114 10. Препроцессор............................................................ 116 10.1. Включающие директивы.............................................116 10.2. Определяющие директивы...........................................117 10.3. Директивы условного включения....................................120 11. Примеры написания программ с использованием функций.......................123 12. Стандартные расширения.................................................. 136 12.1. Ключевые слова...................................................136 12.2. Литералы...................................................... 136 12.3. Операторы........................................................1^9 12.4. Типы данных......................................................139 12.5. Модификаторы................................................... 141 12.6. Структурные выражения.......................................... 144 12.7. Функции с переменным числом аргументов...........................145 13. Нестандартные расширения..................................................148 13.1. Модификаторы pascal и decl.......................................148 13.2. Модификатор Interrupt............................................149 13.3. Модели памяти.....'............................................ 150 *13-4. Ассемблерные вставки..............................................154 ЧАСТЬ Ш. ГРАФИКА. Перевод М. Ю. Рачкова...............................................158 14. Программирование графики.................................................. 158 14.1. Текстовый режим..................................................159 14.2. Графический режим.............................................. 160 14.3. Графика BGI.................................................... 165 ЧАСТЬ IV. ТУРБО АССЕМБЛЕР. Перевод А. В. Стрельцова.............................'.....218 15. Ассемблер................................................................ 218 15.1. Процессоры.......................................................221 15.2. Программы.................................................... 233 15.3. Процедуры........................................................273 15.4. Команды........................................................ 288 15.5. Включение файлов, условная компиляция............................324 15.6 Макроопределения и макровызовы....................................328 15.7 Организационные директивы.........................................335 15.8. Версии Masm и Ideal.......................................... 339 ЧАСТЬ V. ТУРБО ОТЛАДЧИК. Перевод А. В. Стрельцова................................... 346 16. Отладчик...........................................................я*.....346 16.1. Система меню................................................J....349 16.2. Классификация окон...............................................352 16.3. Информационные окна..............................................362 16.4. Инспекционные окна................................................367 16.5. Составление выражений........................................... 389 16.6. Опции меню...................................................... 392 ЧАСТЬ VI. БИБЛИОТЕКИ. Перевод А. Д Плитмана ......................................................... 409 17. Принципы описания.........................................................409 17.1. Графическая библиотека............................................409 17.2. Системная библиотека......................................... 501 Литература.....................................................................636 ПРИЛОЖЕНИЕ А. КОДЫ. Перевод А. Д. Плитмана.............................................................. 637 ПРИЛОЖЕНИЕ Б. РЕДАКТОР. Перевод А. Д. Плитмана........................................638 ПРИЛОЖЕНИЕ В. СИНТАКСИС. Перевод А. Д. Плитмана.............................645 ПРИЛОЖЕНИЕ Г. ГРАФИКА. Перевод А. Д. Плитмана................................660 ПРИЛОЖЕНИЕ Д. АССЕМБЛЕР. Перевод А. Д. Плитмана.............................665 ПРИЛОЖЕНИЕ Е. СТАРТЕР (ПРОГРАММА ЗАПУСКА). Перевод А. Д. Плитмана...........667 ПРИЛОЖЕНИЕ Ж. РЕДАКТОР СВЯЗЕЙ. Перевод. А. Д. Плитмана................................669 ПРИЛОЖЕНИЕ 3. ОРГАНИЗАТОР (МЕНЕДЖЕР). Перевод А. Д. Плитмана...................... 671 ПРИЛОЖЕНИЕ И. ГЕНЕРАТОР. Перевод А. Д. Плитмана.......................................678
редисловие Моим друзьям Ганне и Райнеру Лангмаак I Язык программирования Си был разработан в 1972 г. Деннисом Ритчи, оторый является одним из авторов операционной системы UNIX. Язык Си последствии был использован для программирования этой системы, а также югатой библиотеки обслуживающих программ, поскольку, являясь универ- альным языком общего назначения, язык Си удобен для программирования истемных задач. Появление микрокомпьютеров укрепило позиции языка Си. эыло создано более 30 его новых компиляторов, а после проведения Амери- канским национальным институтом стандартов ANSI (American National Standards Institute) работ по стандартизации в области программирования на- 1али разрабатываться компиляторы, соответствующие опубликованному вес- тей 1986 г. проекту стандарта. Первым компилятором по стандарту ANSI явилась система Турбо Си вер- сии 1.0 фирмы Borland International. Эта система, состоящая из компилятора языка Си, связанного с ним редактора, компоновщика и библиотек, обеспе- чила пользователям удобную интегрированную операционную среду, а также существенно облегчила профессиональное программирование, в котором опре- деляющими параметрами являются высокая скорость компиляции, высокое качество генерируемого кода и малая потребность в оперативной памяти. Несмотря на то что Турбо Си версии 1.0 оправдал ожидания большинства профессиональных программистов, он имел ряд недоработок из-за поспешно- сти его внедрения на рынок. К таким недоработкам относились ошибки в некоторых библиотечных функциях и в редакторе, отсутствие графической библиотеки и отладчика. Весной 1988 г. фирма Borland представила пользо- вателям усовершенствованную версию системы Турбо Си под номером 1.5. В ней исключены ошибки версии 1.0 и добавлена обширная графическая биб- лиотека, насчитывающая около 100 новых функций, а также заложена раз- работка отладчика. В конце 1988 г. появилась версия Турбо Си 2.0 Professional. В ее состав, кроме интегрированного компилятора и компилято- ра с использованием команд строки, входят расширенная графическая библи- отека, встроенная система запуска программ, а также ассемблер с высоким быстродействием и новый символьный отладчик. Ян Белецкий
ЧАСТЬ I. ОПЕРАЦИОННАЯ СРЕДА 1. Введение Операционная среда, состоящая из компилятора языка Турбо Си 2.0, свя- занного с ним редактора, компоновщика и библиотек, разработанная фирмой Borland International для микрокомпьютеров семейства IBM PC, называется интегрированной' системой Турбо Си. Из многочисленных возможностей про- фессиональной версии языка, известной как система Турбо Си 2.0, необходи- мо отметить следующие: - доступность очень удобной и значительно расширенной операционной среды; - значительное увеличение быстродействия компилирования программ; - возможность компилирования программ, код или данные которых зани- мают объем больше 64 Кбайт; - наличие средств условной компиляции и управления способом генериро- вания кода с помощью директив процессора; - возможность деления больших программ на независимо компилируемые модули; - доступность новых стандартных средств, облегчающих системное про- граммирование; - доступность обширной, предварительно составленной библиотеки графи- ческих операций; - доступность средств для эмуляции переменных операций; - наличие механизмов создания программ из подпрограмм, написанных на разных языках программирования; - улучшение качества генерируемого кода путем его оптимизации и иск- лючения из программы библиотечных подпрограмм. 1.1. Основные понятия Специфика языка Си проявляется уже в структуре программы. Написан- ная на этом языке программа включает в себя ряд описаний: переменных, типов и функций, которая имеет название main. Выполнение программы на- чинается именно с этой функции. Примеры а) Простейшая программа main( ) { return 0; }
1. Введение 7 Программа содержит описание функции main. Непосредственно после названия функции в круглых скобках следует спи- сок параметров функции. В рассматриваемом примере этот список пустой. Тело функции состоит из совокупности операторов (блока), ограниченных фигурными скобками. В рассматриваемом примере имеется только одна фун- кция return. Выполнение программы не приводит ни к каким результатам. б) Программа, состоящая из трех описаний finclude <stdio. h> int var; out(par) int par; { printf("%d", par); } main( ) { var « 3; File Edit Run Compile Project Options Debug Break/walch -— ---------------------------------Edit-------------------------------- Line 1 Col 1 Insert Indent Ial Fill Unindent C:NONAME. PAS Turbo C Version 2.0 Copyright (c> 1S87, 1988 by Borland International, lac. Message Fl-Help F5-Zoon FG-Switch F7-Trace F8-Step F9-Hake FlO-Menu Рис. 1. Визитная карточка системы.
S Часть I. Операционная среда outfvar + 2); return 0; } Программа состоит из описаний переменной var, а также функций out и main. int - ключевое слово для описания переменной типа integer (целое) с идентификатором var. Функция out имеет один параметр par, заключенный в круглые скобки. В данном случае тип параметра (integer), а телом функции является функция printf. Функция printf принадлежит к группе стандартных функций языка. Ее выполнение приводит к выводу аргумента par по форма- ту, определяемому аргументом “%d“. Запись %d означает, что данные вы- водятся в десятичном виде. Выполнение приведенной программы начинается с функции main. Далее переменной var присваивается значение 3 и вызыва- ется функция var, которой передается параметр, принимающий значение var + 2. После выполнения программы будет выведено число 5. В произвольном месте программы могут присутствовать директивы препроцессора. Они начи- наются знаком #(.hash), который должен быть первым знаком новой строки. К основным директивам процессора относятся: #indude и #define, а также директивы условной компиляции #if, #else, #elif и #endif. Определение директивы # include приводит к включению в месте ее по- явления файла с именем, обозначенным в этой директиве. Директива #define дает возможность определения констант и связывания произвольного идентификатора с рядом лексических единиц. Примеры а) Использование директивы # include finclude «stdio. h> main( ) { putchar('j'); return 0; } Интерпретация директивы #include приводит к включению в программу в месте ее появления содержимого файла с именем stdio.h Во многих системных реализациях, в том числе в Турбо Си, файл stdio.h включает определение функции putchar. В рассматриваемой программе вызов функции putchar('j') приводит к выводу символа j на устройство стандартно- го вывода. б) Программа с параметризацией fdefine ZERO finclude «stdio.h» main( ) { #if defined (ZERO) printf("%05d", 13);
I. Введение 9 felse printf(”%d," 13); fendif return 0, } Приведенный текст включает функции и директивы препроцессора, начи- нающиеся знаком #(hash). Директива #define определяет константу ZERO. Поскольку при интерпре- тации директивы условной компиляции эта константа уже определена, в ос- новной программе будет выполнена функция printf(”%05d", 13); Если из основного текста убрать директиву #define, то в момент выпол- нения директивы #if константа ZERO не будет определена, что приведет к включению в основную программу альтернативной функции printf (“%d“, 13); Таким образом, если основной текст программы содержит директиву #define, то компиляции принадлежит программа finclude <stdio.h> main( ) { printf("%05d", 13); return 0; } Ее выполнение приводит к выводу значения 00013, т.е. числа 13, пред- ставленного в поле размерностью 5 с нулями впереди. Если из приведенного основного текста убрать директиву #define, то бу- дет скомпилирована программа finclude <stdio.h> main( ) { printff'Xd, 13); return 0; } После выполнения этой части программы будет выведено число 13. Другим способом параметризации рассматриваемой программы является придание ей, например, следующего вида: \ fdefine ZERO main( ) { printf( #if defined(ZERO) "%05" felse "%d"
10 Часть I. Операционная среда fendif . 13); return 0; } Такая запись допустима, поскольку в языке Си разрешены произвольные промежутки между лексическими единицами. в) Программа с определением ряда лексических единиц #include<stdio.h> «define MAIN main( ){ MAIN «define END} «define Two 2 printf("%d". Two + Two); return 0; END Первая директива #define отождествляет с идентификатором MAIN ряд лексических единиц main( ){. Каждое проявление идентификатора MAIN при компиляции программы будет заменено на main! , ) и {. Аналогичным образом вторая директива #define отождествляет с иденти- фикатором END замыкающую фигурную скобку, а третья - отождествляет с идентификатором Two целое число 2. Преобразованная процессором данная программа эквивалентна следующей: «include <stdio.h> main( ){ printf(”%d", 2 + 2); return 0; } Выполнение этой программы приводит к выводу числа 4. Структура программы Анализируя программу, можно сказать, что на верхнем уровне она со- стоит из описаний и заключенных между ними директив препроцессора. На более низком уровне можно выделить операторы, на самом нижнем - знаки алфавита, а на несколько более высоком - комментарии и лексические еди- ницы. Лексические единицы и комментарии могут разделяться пробелами, знаками табуляции и перевода на новую строку. Пробелы принимаются во внимание только как разделители лексических единиц, а также в случае, когда они находятся в пределах знаковых и строчных литералов. Во всех ос- тальных случаях пробелы игнорируются, что делает возможным свободную форму записи программ. Однако это требует, чтобы знаки #(.hash), с кото- рых начинаются директивы препроцессора, были размещены в первых колон- ках соответствующей строки или по крайней мере перед ними стояли знаки пробела или табуляции. Использование пробелов в качестве разделителей лексических единиц необходимо только тогда, когда пара соседних единиц может быть принята за другую единицу (например, когда рядом находятся два идентификатора или ключевых слова).
1. Введение И File Edit Run Compile Project Options Debug Break/Hatch -----------------------;------------Edit ------------------------------- Line 9 Col 1 Insert Indent lab Fill Unindent C:HATCH.C int Рои; PouertBase,Exp) < if(Exp -- 1) return Base; else return.Base * Poner(Base, nainO t fort Рои = 1; Рои < 6; Powt) printffzd\n“, Роиег(2,Рои)); return 0; 1 ------------------------------------ Watch Exp: 3 Base: Z Pow: 3 Fl-Help FS-Zoom FG-Switch F7-Trace FB-Step F9-Wake FlO-Menu Рис. 2. Окно редактирования и наблюдения. Пример int F1ve( ) { return 5; Приведенная запись определяет непараметрическую функцию Five. Эта функция, согласно первой строке, имеет значение типа integer. Отсутствие пробела между ключевым словом int и именем Five привело бы к определению функции с другим именем int Five вместо функции Five. Поскольку в этом новом описании тип результата функции не был бы опре- делен явно, то он по умолчанию был бы принят как integer.
12 Часть I. Операционная среда File Edit Sun Compile Project Options Debug Break/watch ------------------------------------- Edit ------------------------------ Line 9 Col 1 Insert Indent lab Fill Unindenl C: HATCH.C ^include (stdin. h> ini Pon; PowertBase, Exp) 1 if(Exp -- 1) return Base; else | return Base * PoweirCBase.Exp - 1); > nainO 1 for(PoH = 1; Poh < 6; Powt) prinlf(“Xd\n”, Роиег(2,Рон>); return B; > Linking C’\TC2 EMwftkc: 1 ' 1 \£ХЕ\ИАТСЯ.Ш: Fl-Help F5-Zoon FG-Switch F7-Trace F8-Slep FB-Make Fid-Menu Рис. 3. Окно редактирования и сообщения. 1.2. Использование меню Запуск интегрированной системы Турбо Си производится командой ТС. записанной строчными или прописными буквами. После этого на экране по- является главное меню,- а на его фоне - информация о номере версии систе- мы. Нажатие произвольной клавиши приводит к исчезновению этой инфор- мации и появлению экрана, разделенного на следующие части; строка меню, состоящая из полей File, Edit, Run, Compile, Project, Options, Debug, Break/Watch; окно редактора, озаглавленное Edit;
1. Введение 13 окно коммуникационное, первоначально озаглавленное Message; строка, содержащая информацию о функциональных клавишах Fn. При работе системы Турбо Си на экране имеется строка меню или одно из перечисленных окон. Кроме этого, на экране могут быть вызваны вспомо- гательные окна, связанные с реализацией функций системы. В частности, нажатие в произвольный момент клавиши Shift-Fl0 приводит к появлению на экране вспомогательного окна, содержащего информацию о номере версии системы.Выделение строки меню происходит при нажатии клавиш F10, что может быть произведено в произвольный момент работы системы.При этом изменяется цвет одного из его полей. Выделение редакционного или комму- никационного окна осуществляется нажатием клавиши F6, что приводит к увеличению яркости озаглавливающего окно слова (соответственно Edit, Watch или Message), а также сменой одиночной линии, на которой находит- ся данное слово, на двойную. Выбор функции, определенной главным меню, может быть осуществлен несколькими способами. Простейшим способом является введение с клавиату- ры первого символа названия поля, данного в меню. Другой способ заключа- ется в перемещении с помощью горизонтальных стрелок на дополнительной клавиатуре на выбранное поле меню с последующим нажатием клавиши Enter. В каждом из описанных случаев (за исключением Edit) произойдет вывод подменю, содержащего опции (подразделы).Выбор функции, определен- ной опцией подменю, происходит способом, аналогичным выбору функции из меню (в этом случае используются клавиши вертикальных стрелок), и мо- жет привести к вызову следующего подменю. Следует отметить, что если после выбора первого подменю использовать клавиши горизонтальных стре- лок, то на экране будут выведены соседние подменю главного меню. Эта особенность очень полезна в начальной стадии знакомства с системой, когда размещение ее функций между подменю еще хорошо не известно.В случае ошибки в выборе подменю можно вернуться на высший уровень менй) нажа- тием клавиш Esc. Нажатие этой клавиши- в главном меню приводит/ к выво- ду на экран последнего активного окна (редакционного или коммуникацион- ного). Выбор функции главного меню может быть значительно усжорен с по- мощью клавиши Alt. Ее нажатие в произвольный момент вместе с первой буквой поля главного меню приводит к немедленному выполнению функции, связанной с этим полем. В частности, это означает, что при выборе поля Edit главного меню, достаточно ввести с клавиатуры знак Alt-E (вместо то- го, чтобы сначала нажать клавишу F10, а потом Е).Кроме перечисленных клавиш FIO, Alt-Fl О и Alt-p (где р - первая буква поля главного меню), значительное количество используемых функций выполняют клавиши Fn, Alt-Fn и Ctrl-n. « Fl - вывод на экран справочной информации, связанной с текущим контекстом использования системы; F2 - сохранение на диске текста, находящегося в редакционном окне; F3 - вызов в редакционное окно файла с нужным именем (подразуме- вается, что этот файл имеет расширение .С); F4 - выполнение программы до строки, в которой находится курсор; F5 - увеличение или уменьшение активного окна;
И Часть 1. Операционная среда F6 - переключение активности между редакционным и коммуникацион- ным окнами, а также возврат к активности окна из произвольного меню; F7 - пошаговое выполнение программы со входом в процедуру; F8 - пошаговое выполнение программы без входа в процедуру; F9 - компилирование программы в режиме Маке (разд. 1.3) (имеется в виду программа, находящаяся в редакционном окне); F10 - переключение между главным меню и активным окном; Alt-Fl - вывод на экран последней рассматриваемой справочной информа- ции; AH-F3 - занесение в редакционное окно последнего использованного файла; Alt-F5 - просмотр экрана, на который выведены результаты выполнения программ; AH-F6 - замена содержания окна (например, в коммуникационном окне пе- реключение с просмотрового окна на окно связей и наоборот); Alt-F7 - перемещение к месту последней ошибки; Alt-F8 - перемещение к месту следующей ошибки; Alt-F9 - компилирование программы или основного модуля, находящегося в редакционном окне; Ctrl-Fl - вывод справочной информации относительно идентификатора, выде- ленного курсором; Ctrl-F2- окончание выполнения программы; Clrl-F3 - высвечивание строки вызванных процедур; Ctrl-F4 - вычисление или модификация переменной; Ctrl-F7 - занесение выражения в просмотровое окно; Ctrl-F8- установка/снятие точки останова; Ctrl-F9 - выполнение программы. Каждая из перечисленных функций может быть выполнена в произволь- ном контексте, но ие во время выполнения программы.Для окончания рабо- ты с системой Турбо Сн можно воспользоваться клавишами Alt-X. Это при- ведет к завершению функционирования этой системы и к вызову DOS. 1.3. Составление программ Составной частью системы Турбо Си является экранный редактор. Он вызывается из главного меню (клавиша Е) или из другого произвольного контекста (клавишами A1I-E). Подробное описание редактора на примере редактора Word Star приведено в Приложении Б. Тем, кто не знаком с редактором Word Star, достаточно знать, что перемещение курсора вверх, вниз, влево и вправо производится с помощью стрелочных клавишей, стирание знаков - с помощью клавиши Backspace (<-) и Delete (Del), стирание строки - с помощью клавиш Ctrl-Y, вставка пустых строк - с помощью клавиш Ctrl-N. Кроме этого, часто используемые функции осуществляются следующими клавишами: Ноше - перемещение курсора в начало строки; End - перемещение курсора в конец строки; Ctrl-Home - перемещение курсора в начало первой строки экрана; Ctrl-End - перемещение курсора в конец последней строки экрана; PgUp " - вызов на экран предыдущей страницы; PgDn - вызов на экран следующей страницы; Ctrl-PgUp - перемещение курсора в начало текста;
1. Введение Ctrl-PgDn - перемещение курсора в конец текста; Ctrl-T - удаление последних знаков слова; Ctrl-QY - удаление последних знаков строки; Ins - переключение с режима вставки в режим замены знаков и наоборот. Если во время составления основного текста (программы или модуля) нажать клавиши Ctrl-Fl, то на экране появится справочная информация в выделенном курсором элементе языка. В частности, если это будет слово initgraph, которое является названием одной из функций графической библиотеки, то на экране появится краткое описание этой функции, а также обозначения близких ей функций. Окончание редакционных операций не требует специальных действий. Для того чтобы произвести запись подготовительного текста на диск, достаточно нажать клавишу F2 или после вызова главного меню (клавиша F10) выбрать поле File (клавиша F), а затем опцию S(Save) или W(Write). Рекомендуемым способом создания файлов является ускоренный выбор поля File (клавиши Alt-F) и затем опции File Edit Run Compile Project Options Debug Break/watch Line 13 Col 4 Insert Indent lab Fill Unindenl C:CIRCLE.C ftinclude Cgraphics.h) ^include <dos Help naint) circle: draws a circle at <x, y) of the given < int. Driver, Driver - DE radius void far circletinl x, int y, int radius); inilgrapMd Prototype in 5ПН!1|П|М1П circle(geln delay(ZOOO) closegrapht return 0; 1 See also are Fl-Help index -Pick topic <~*-Go to topic Esc-Exit Help Рис. 4. Разъяснительное окно.
16 Часть L Операционная среда Load (клавиша L). После выполнения этих действий на экране появляется диалоговое окно, в котором с клавиатуры необходимо задать имя файла и нажать клавишу Enter. Если файл под таким именем уже существует, то в редакционном окне появится содержимое данного файла для дальнейшего редактирования. Если файл с таким именем не существует, то редакционное окно будет очищено и можно вводить требуемый текст, который при записи на диск (клавиша F2) будет занесен в файл с данным именем. Другим способом создания текста является ускоренный набор поля File (клавиши Alt-F) с последующим выбором опции New (клавиша N). Это приведет к очищению редакционного окна и к присвоению файлу имени NONAME.C. Если после окончания редактирования текст запоминается, то задается вопрос: не требуется ли изменить название NONAME.C на другое? После получения ответа процесс заканчивается. Примеры. Составление и модификация программ а) Составление новой программы и запись ее на диск find ude<s td i о. h> main( ) { printf(''Hel)o, I am JanB"); return 0; } Порядок действий: - выбор поля File главного меню (клавиши Alt-F); - выбор опции Load (клавиша L); - определение имени файла; - ввод текста программы; - запись информации на диск (клавиша F2). б) Составление программы без записи ее на диск Порядок действий: - выбор поля File главного меню (клавиши Alt-F); - выбор опции New (клавиша N); . - ввод текста программы. Введенная программа может быть модифицирована, скомпилирована и выполнена. 1.4. Компиляция модулей и программ Компиляция модулей и программ может осуществляться в режимах Compile, Make и Build. Компиляция в режиме Compile заканчивается созданием файла с расширением .OBJ, а компиляция в режимах Маке и Build заканчивается созданием выполняемого файла с расширением .EXE. Режим Compile Вызов компилятора в режиме Compile (клавиши Alt-F9 или Alt-С, С) приводит к компиляции модуля, находящегося в редакционном окне.
1. Введение 17 Режим Make Вызов компилятора в режиме Маке (клавиша F9 или Alt-С, М) приводит к компиляции- программы следующим образом: - если при выборе опции Project определено имя проекта (Project name), которое является именем файла с расширением .PRJ, то программа, складывается . из модулей, имена которых выделены в этом файле, дополненных библиотечными функциями из библиотек с расширением .LIB, а также функциями из стандартной библиотеки; - если имя проекта не определено (подменю Project пусто), но определено имя первичного файла (Primary С file), которое является именем файла с расширением .С, то программа I состоит из модуля, определенного этим файлом; \ - если не определено ни имя проекта, ни имя первичного файла, то программа складывается из модуля, находящегося в редакционном окне. В любом случае система ограничивается проведением только необходимых компиляций. В частности, не компилируется повторно основной модуль, если существует соответствующий ему скомпилйрованный модуль с расширением .OBJ. Благодаря этому количество необходимых компиляций модулей сводится к минимуму. Режим Build Вызов компилятора в режиме Build (клавиши Alt-C, В) отличается от вызова в режиме Маке тем, что проводятся все компиляции, даже при существовании соответствующих скомпилированных основных файлов. Внимание: непосредственно после запуска системы Турбо Си как имя проекта, так и имя первичного файла являются пустыми. Это приводит к тому, что требуется компилировать модуль, находящийся в редакционном окне. В случае простых программ такая конфигурация системы вполне приемлема. Однако, если программа содержит директивы #include, включающие не задействованные еще фрагменты программы, то целесообразно определить имя первичного файла. В случае многомодульных программ необходимо также определение имени проекта. Примеры а) Выполнение одномодульной программы, находящейся в файле HELLO.С. Порядок действий: - выбор опции File (клавиши Alt-F); - выбор опции Load (клавиша L); - определение имени файла (имя HELLO); - выбор опции Run (клавиши Alt-R); - выбор подопции Run (клавиша R); б) Выполнение одномодульной программы, находящейся в файлах TatLC и HEAD.H, независимо от того, какой из этих файлов занесен в редакционное окно. ...—
18 Часть I. Операционная среда TAIL.С f i nc 1 ude<stad i о. h> main! ) HEAD.H printf(”Hellol”) return 0; { ^include "HEAD.H" } Порядок действий: - выбор опции Compile (клавиши Alt-C); - выбор опции Primary file (клавиша Р) с целью определения имени первичного файла; - определение имени первичного файла (имя TAIL); - выбор опции Run (клавиши Alt-R); - выбор подопции Run (клавиша R). в) Выполнение двухмодульной программы, находящейся в файлах FIRST.C и SECOND.C. FIRST.С #include<stdio.h> main! ) { display! ); return 0; } } SECOND.C display! ) { printf("That's all") Порядок действий: - образование файла name PRJ (например, PROGRAM PRJ), содержащего описание проекта или, в простейшем случае указание имен составляющих его файлов. PROGRAM PRJ FIRST SECOND - объявление системе Турбо Си имени проекта: 1) выбор поля Project (клавиши Alt-P); 2) выбор опции Project name (клавиша Р); - определение имени проекта (PROGRAM); - компиляция и выполнение программы (клавиши Alt-R, R). г) Выполнение программы вне системы Турбо Си Порядок действий: - составление выполняемой программы (клавиша F9); - временный выход системы Турбо Си и вызов системы DOS (клавиши Alt-F, а затем клавиша О); - выполнение программы в системе DOS; - возврат к системе Турбо Си (директива EXIT).
I. Введение 19 1.5. Исправление ошибок При разработке программы возникает необходимость исправления имеющихся в ней синтаксических ошибок. Для этого после составления программы следует произвести ее компиляцию (клавиша Alt-F9 для основного модуля в редакционном окце) .Компиляция правильной программы заканчивается сообщением Success: Press any key, а компиляция программы, содержащей ошибки, заканчивается сообщением Errors: Piess- any key. Для нахождения ) места ошибки удобно пользоваться клавишами вертикальных стрелок. После локализации ошибки следует нажать клавишу Enter. При этом активным окном становится редакционное окно, а курсор расположится вблизи локализованной ошибки. После внесения поправок можно вернуть активность коммуникационного окна нажатием клавиши F6. Можно также, находясь в редакционном окне, воспользоваться функциональными клавишами для локализации предыдущей (Alt-F7) и последующей (Alt-F8) ошибок. После исправления всех ошибок можно повторить компиляцию или провести компиляцию и выполнение программы (клавиши Alt-R,R). Перед этим можно также (с помощью клавиши F6) с произвольной периодичностью переключаться между редакционным и коммуникационным окнами. Процесс идентификации ошибок значительно облегчается благодаря тому, что перемещению со строки на строку в коммуникационном окне автоматически сопутствует перемещение выделенной строки в редакционном окне. Некоторые проблемы возникают тогда, когда в коммуникационном окне появляется ошибка, которая находится в файле, не находящемся в данный момент в редакционном окне. В этом случае перемещение выделенной строки в редакционном окне прекращается, а загрузка в него файла, содержащего ошибку, произойдет только после выбора сообщения, касающегося этой ошибки (клавиша Enter). Для автоматического размещения в редакционное окно требуемой информации необходимо из главного меню перед компилированием программы выбрать следующую опцию: Options / Environment / Message Tracking, а затем нажать клавишу Enter столько раз, чтобы рядом с именем опции появилась надпись All files (все файлы). После этого можно вернуться к активному окну (клавиша F10). Пример Компиляция программы, в которой перед словом Hello ошибочно пропущены кавычки finclude<stdio.h> ma1n( ) { printf(Hello, I am Jan В”): return 0; приведет к появлению в коммуникационном окне следующих сообщений об ошибках:
20 Часть I. Операционная среда Undefined symbol 'Hello' in function main Undefined symbol T in function main Function call missing) in function main Unterminated string or character constant ... Если выбрать первое описание ошибки (клавиша Enter), то в редакционном окне локализуется первая ошибка. Локализация остальных ошибок может быть произведена с помощью клавиш F6, F7 и F8. 1.6. Отладка программ Символьная программа отладки (отладчик), входящая в состав интегрированной системы, существенно облегчает процесс создания правильной программы. К важным особенностям отладчика следует отнести: - возможность слежения за ходом выполнения программы; - возможность анализа результатов выполнения программы по ходу их получения; - возможность наблюдения за изменениями величин переменных и выражений; - возможность динамической модификации величины переменных в процессе выполнения программы. Поскольку отладчик является такой же частью интегрированной системы, как, например, редактор или компилятор, типичный цикл отладки, состоящий из редактирования, компиляции и непосредственно отладки, может быть выполнен внутри системы. В тех случаях, когда желательно использовать дополнительные средства отладки программы, можно воспользоваться автономной программой отладки, описанной ниже. Запуск отладчика Для того, чтобы во время выполнения программы имелась возможность символьного обращения к объектам основной программы, требуется уже при компиляции программы обеспечить выработку соответствующей отладочной информации. Проще всего это сделать, задав в интегрированной операционной среде опции Debug/Source Debugging On и Options/Compi ler/Code Generation/OBJ Debug Info. On После этого инициирование отладки возможно с помощью меню Run (опция Go to Cursor, Trace Into, Step over) или ускоренным способом (соответственно клавишами F4, F7, F8). Экран монитора Среднюю часть экрана монитора заполняют два окна: редакционное окно и коммуникационное окно. В редакционном окне находится фрагмент текста отлаживаемой программы, а в коммуникационном окне имеются или
1. Введение 21 просматриваемые переменные и выражения или сообщения, связанные с компиляцией или компоновкой программы (результаты выполнения программы находятся в окне, высвечиваемом при нажатии клавиш A11-F5). Если коммуникационное окно обозначено надписью Watch, то оно называется просмотровым, а если надписью Output, то выходным. Если переключение между редакционным и коммуникационным окнами производится с помощью клавиши F6, то переключение между просмотровым окном и окном связей - клавишами A11-F6. Как просмотровое, так и выходное окно может быть увеличено до полного размера экрана (клавиша F5). Повторное нажатие этой клавиши восстанавливает прежние размеры окна. Слежение за ходом выполнения программы Простейшим способом локализации ошибок программы являются ее пошаговое выполнение и анализ очередности и результатов исполнения отдельных операторов (трассировка). Для этого необходимо выбрать поле Run из главного меню, а затем опции Trace Into (клавиша F7) или Step Over (клавиша F8). Если программа не скомпилирована, то это производится в режиме Маке. По окончании компиляции в редакционном окне появится фрагмент главного модуля, в котором будет выделена строка, содержащая имя функции main. File Edit Run Compile Project Options Debug Break/natch P-—--------------——-----------------— Edit —------------------------------- Line Z Col 1 Insert Indent Tab Fill Unindent CiDATE. C ttinclude <dos.h> ttinclude <stdio.h> struct date Today; maint) < gefdatet&Today); printf(“Zd.ZOZd.zOZd“, Today.da_year. Today.da mon. Today. da_day); return 0; > Hatch Fl-Help F5-Zoon F6-Switch F7-Trace FR-Step F9-Make FIO-Menu Рис. 5. Программа с установкой точек останова.
22 Часть 1. Операционная среда Выполнение очередных инструкций программы может осуществляться нажатием клавиш F7, F8 и F4. Они имеют следующие значения: F7 - выполнить инструкцию в выделенной строке программы; если в этой строке содержится вызов функции, то остановиться перед ее первой инструкцией; F8 - выполнить инструкцию в выделенной строке программы; если в ней имеется вызов функции, то выполнить эту функцию целиком; F4 - поставить временную точку останова в строке, содержащей ’курсор. Выполнение программы будет приостановлено в точке останова. Внимание: для повторного выполнения программы с самого начала можно воспользоваться опцией Run/Program Reset (клавиши Ctrl-F2). После этого можно возобновить отладку с помощью клавиши F7 или F8. Установка точек останова Поскольку пошаговое выполнение программы может занимать много времени, перед выбранными инструкциями программы устанавливаются точки останова, а затем начинают выполнять программу до попадания на точку останова. Для установки или снятия точки останова требуется переместить курсор на нужную строку программы, а затем выбрать опцию Break/Watch/Toggle Breakpoint (клавиши Ctrl-F8). Для снятия всех имеющихся точек останова необходимо выбрать опцию Break/Watch/Clear All Breakpoints. Можно также провести просмотр всех расставленных точек останова с помощью опции Break/Watch/View Next Breakpoint. Если выполнение программы не приводит к ее останову, поскольку программа обходит точки останова, можно использовать клавишу Ctrl-Break. При этом выполнение программы приостанавливается. Определение значений переменных и выражений Во время останова программы можно ознакомиться с текущими значениями переменных и выражений, выбрав опцию Debug/Evaluate (клавиши Ctrl-F4). При этом высвечивается диалоговое окно, представленное тремя рамками.- В первую рамку (Evaluate) можно заносить выражения, значения которых необходимо узнать, во второй (Result) будут представлены значения этих выражений, а в третьей (New Value) можно -определять новые величины переменных, представленных в выражениях. Окно закрывается клавишей ESC.
I Введение 23 Квалификация идентификаторов Когда два идентификатора (с различными областями действия) являются идентичными и вступают в противоречие, преимущество имеет идентификатор наиболее локальный или наиболее вложенный. Обращение к локальным идентификаторам, определенным внутри функции или процедуры, требует квалификации. В общем случае квалифицированный идентификатор имеет вид .mod.fun.id, где mod - имя модуля, fun - имя функции, a id - имя идентификатора внутри функции или процедуры. Если идентификатор однозначно идентифицирует переменную, то предварительные его квалификации могут быть опущены. mainO < return 0; 1 File Edit Run Compile Project Options Debug Break/watch r— ------——--------——•———— Edit----------------------------——_-------------- Line 8 Col 1 Insert Indent Tab Fill Unindent C: VALUE. C tlinelude <stdio.h> int Arrf3J - < 20,30,40 riutiC-ZdXn" arrinl Hatch Fl-Help F7-Trace F8-Step FLO-Menu TftB-Cycle <-"-Modify Рис. 6. Определение значения выражений.
24 Часть 1. Операционная среда Пример. Квалификация идентификатора в основной программе. Если исполнение следующей программы: #include<stdio.h> main( ) { Int Fix; Fix - 10; Add(& Fix); printf("Xd, Fix); return 0; } Add(int * Par) { *Par + = 3 } будет приостановлено перед выполнением предписания в рамках функции Add, то к переменной Fix можно обращаться с помощью квалифицированного имени main.fix. Рамка Evaluate Если в рамку Evaluate занести выражение (в частности, имя переменной), то в рамке Result высветится текущее значение этого выражения. Если значение выражения присваивается некоторой переменной, то, пользуясь клавишами вертикальных стрелок, можно переместиться до рамки New Value и в ней указать новое значение переменной. Обычно в рамке Evaluate указывается имя переменной. Если после этого- имени дополнительно использовать шаблон (отделенный от него запятой), то вывод значения произойдет согласно заданному шаблону. В простейшем случае шаблон может быть числом, которое определяет общее количество вводимых значений. При этом выводятся значение данной переменной, а также значения переменных того же типа, находящихся в соседних с данной переменной ячейках оперативной памяти. В частности, если Атт - массив с приведенными ниже величинами элементов static int Arr[5] = {10, 20. 30. 40, 50}; то указание в окне Evaluate Arr[l], 3 приведет к появлению в окне Result чисел 20, 30, 40 В общем случае шаблон может состоять из числа, а также из одного из ниже перечисленных знаков. Если число опущено, то по умолчанию оно принимается равным 1. После знака F может следовать число. Знак Интерпретация С вывод в десятичном виде
1. Введение 25 File Edit Run Compile Project Options Debug Break/watch । —----- -------------------Edit ---------------------— Line 1 Col 1 Insert Indent Tab Fill Unindenl C:CHECK. C int Arr(3J = < 20,30.40 >; struct! char Vec(3J; int Fix; > Rec = < < *I*,*s*,*a* 1.13 }; int Fix - -13; enumt Fiat,Audi.uyel 1 Car = Audi; ТПЛИЙЫВвВИИИИИ1ИВИВ1НИ^ИИ1ИЯМ1ИИИВИИЯвВ1ИИЯЯ -------------------------------------- Hatch Arrill.z: 30, 40 Car: 1 /* Audi »/ Rec.Fix: 13 Rec: ! "Isa\r“, 13 1 Arr: < 20, 30, 40 1 Rec.Vecill: 's' Fl-Help F5-2oom FG-Switch F7-Trace F8-Step F9-Make FlO-Menu Рис. 7. Просмотр переменных и выражений. Н вывод в шестнадцатеричном виде Fn вывод в виде числа с плавающей точкой с и знаками М вывод каждого байта переменной в виде пары шестнадца- теричных цифр Мс выводы каждого байта переменной согласно значению с (для с: Н, X - в шестнадцатеричном виде; С, S - в символьном виде) Р вывод адреса указателями в виде сегмент : смещение segment: off/set R вывод величины полей структуры, отмеченных идентифика- торами полей S вывод величины элементов строки в шестнадцатеричном виде со знаком #(hash) X вывод в шестнадцатеричном виде
26 Часть I. Операционная среда Примеры. Вывод значений выражений В пределах описания typedef struct{ char Name[20]; int Age; enum{Black, Blond} Hair; }ChiId; Child Isa = {"Isa", 6, Black}; int Arr[3] = {0, 20, 30}; Child * Ref = &Isa; справедлива следующая запись; Evaluate Arr Arrfl], 2H Ref Ref,P Isa Isa, R RefAge, D (*Ref). Age, 2M Result {10, 20, 30>f 0x14, OxlE &Isa 6dE7(DS):0094 &Isa {“Isa“, 6, 0/*Black*/} {Nawe:“Isa“, Age: 6, Hair:0/*Black*/} 06 00 Просмотр значений переменных и выражений Очень удобной особенностью отладчика является возможность непрерывно- го просмотра значений выбранных переменных и выражений без необходи- мости многократного использования диалогового окна. Просматриваемое непрерывно выражение помещается в просмотровое окно. Каждое программное действие, которое приводит к смене значения выбранно- го выражения (в частности, к смене значения переменной), сразу же отобра- жается в этом окне. Занесение выражений в просмотровое окно, а также их модификация и удаление могут производиться двумя способами: через меню или непосред- ственно с помощью операций в просмотровом окне. Занесение выражения в окно - выбор опции Break/Watch/Add Watch (клавиши Ctrl-F7), а затем запись выражения в рамке Evaluate или - активизация просмотрового окна с последующим нажатием клавиши Ins и запись выражения. Удаление выражения из окна - выбор опции
1. Введение 27 Break/Watch/Delete Watch; (удалению подлежит выражение, выделенное подсветкой или знаком жирной точки); или - активизация просмотрового окна и (после выделения нужной его пози- ции) нажатие клавиши Del. Модификация выражения в окне - выбор опции Break/Watch/Edit Watch, а затем внесение изменений в нажатие клавиши Enter; или - активизация просмотрового окна и (после выделения необходимой пози- ции) нажатие клавиши Enter. Внесение изменений и повторное нажатие этой клавиши. Рис. 8. Слежение за вызовами.
28 Часть 1. Операционная среда Удаление всех выражений из окна - выбор опции Break/Watch/Remove АП Watches; - активизация просмотрового окна и удаление всех просматриваемых вы- ражений с помощью клавиши Del. Внимание: просматриваемые выражения могут быть введены вместе с шаблонами, аналогичными описанным выше. Слежение за вызовами Каждый вызов процедур или функции приводит к занесению информации об их месте вызова в стеке вызовов. Для просмотра этого стека необходимо выбрать опцию Debug/Call Stack (клавиши Ctrl-F3). После этого можно ознакомиться с местом вызова каждой , активной процедуры или функции, выбирая их с помощью клавиш с верти- кальными стрелками и нажимая затем клавишу Enter. Пример. Стек вызовов Если в стеке вызовов находятся имена Local, Global, main, то это означает, что остановка выполнения программы наступила после того, как из функции main была вызвана функция Global, из нее - функция Local. Если в этой ситуации выделить позицию Global и нажать клавишу Enter, то высветится тот фрагмент функции Global, который заключает вызов функции Local. После этого обычным образом можно обращаться к переменным функции Global. Окончание выполнения программы Окончание выполнения программы (сброс программы) наступает после выбора опции Run/Program Reset или после нажатия клавиш Ctrl-F2. Это приводит к очистке памяти, закрытию всех открытых файлов и подготовке к новому запуску. Поскольку при этом точки останова не удаляются, легко можно анализировать многократное выполнение одной .программы (например, после Ctrl-F2, нажимая клавиши F7 или F4).
I. Введение Заключение Клавиши F4 F5 F6 F7 F8 Ctrl-F2 Ctrl-F3 Ctrl-F4 Ctrl-F7 Ctrl-F8 Ctrl-F9 Alt-F5 Alt-F6 Функции Выполнить до курсора Увеличить окно/уменьшить окно Переключить активность окон Отследить по одной инструкции Отследить инструкции, минуя вызов процедур Закончить выполнение программы Вывести стек вызовов Вычислить значение выражения Внести выражение в просмотровое окно Расставить точки останова/удалить остановы Выполнить программу Вывести выходной экран Выполнить замену содержания окна
2. Системы В состав пакета Турбо Си 2.0 входят две системы программирования: - интегрированная система, включающая встроенные редактор, компилятор, компоновщик и отладчик; - система загрузки с каким-либо текстовым редактором и обслуживающими программами, а также автономные обслуживающие программы, ассемблер и программа запуска. Вызов интегрированной системы производится командой ТС options пате, а системы загрузки - командой ТСС options пате, где options - опции вызова, а пате - имя программы. Когда определение опции, а также выбор программы для компиляции производятся посредством системы меню, вызов интегрированной системы упрощается до вида ТС. Использование системы загрузки менее удобно, чем интегрированной системы. Эту систему целесообразно запускать при недостаточном размере оперативной памяти, а также при желании использовать другой текстовой редактор, а не встроенный в операционную среду. 2.1. Интегрированная система Для запуска интегрированной системы достаточно задать команду ТС, после чего можно пользоваться системой меню, дающей возможность составления, загрузки, редакции, запуска и выполнения программ, а также установку параметров среды и сохранение их в файле конфигурации. В этом файле, кроме всего прочего, запоминается информация о программе, находящейся в редакционном окне. Благодаря этому можно вызвать интегрированную систему, обеспечить занесение в редакционное окно последней обработанной перед этим программы. Если в файле конфигурации нет названия проекта, т.е. имени файла, с которого должна начаться компиляция, то можно его указать непосредственно в команде ТС. Другими опциями, которые можно указать в этой команде, являются следующие: /т выполнить компиляцию в режиме Маке' /Ъ выполнить компиляцию в режиме Build', /d использовать два монитора (результаты выполнения программы выдаются на неактивный монитор);
2. Системы 31 /спате использовать файл конфигурации name (по умолчанию принимается, что им является TCCONFIG.TC). Пример. Составление программы с помощью системы ТС Если во время выполнения файла GREET.C в файле MYCONFIG.TC заполнена конфигурация интегрированной системы, то для составления выполняемой программы и помещения ее в файл GREET.EXE можно воспользоваться, например, директивой tc/cmyconf 1 g. tc/b Типовой вызов системы Типовым вызовом интегрированной системы является такой ее вызов, после которого появляется главное меню, состоящее из полей File Edit Run Compile Project Options Debug Break/Watch Выбор любого из них (за исключением Edit) приводит к появлению подменю, состоящего из опций. Значения отдельных полей и опций описаны во вспомогательных окнах, выводимых на экран нажатием клавиши F1. Принимая это во внимание, указанные ранее описания будут ограничены до минимума. Поле File Выбор поля File (клавиша Alt-F) приводит к высвечиванию следующих опций; Load F3 Pick Alt-F3 New Save F2 Write to Directory Change Dir OS shell Quit Alt-X Load Выбор опции Load (клавиши L или F3) дает возможность загрузки файла для его редакции. Система предлагает имя файла, но оно может быть заменено. Если имя будет дано неоднозначно (с наличием звездочек или
32 Часть I. Операционная среда вопросительных знаков), то на экран выведется список имен файлов, из которого можно сделать выбор (с помощью клавиш со стрелками и клавиши Enter). Pick Выбор опции Pick (клавиши Р или Alt-F3) также дает возможность загрузки файла, но при этом система предлагает провести выбор не более чем среди восьми файлов, которые были загружены последними. Среди предлагаемых имен имеется “-loadfile-". Выбор этого имени приводит к такому же результату, что и выбор опции Load. New Выбор опции New (клавиша N) приводит к возможности создания нового файла. Предлагаемое имя файла NONAME.C) можно изменить во время записи файла на диск. Save Выбор опции Save (клавиши S или F2) приводит к записи на диск файла, находящегося в окне редактора. Если файл имеет имя NONAME.C, то система удостоверится, что именно это имя должно иметь дисковый файл. Write to Выбор опции Write to (клавиша W) приводит к записи на диск информации из окна редактора. В случае существования файла с идентичным именем старый файл на диске стирается. Directory Выбор опции Directory (клавиша D) дает возможность вывода на экран имен файлов выбранного каталога. Поскольку предлагаемым неоднозначным именем является *.*, то нажатие клавиши Enter приводит к выводу всех имен текущего каталога. Change dir Выбор опции Change dir (клавиша С) дает информацию об имени текущего каталога и возможность выбора произвольного каталога (на жестком диске или дискете). OS shell Выбор опции OS shell (клавиша О) приводит к временному переходу в состояние, в котором возможно использование команд системы DOS. Выпол- нение в этом состоянии команды приводит к возврату в систему Турбо Си. Quit Выбор опции Quit (клавиши Q или Alt-X) приводит к окончанию работы
2. Системы 33 с системой Турбо Си и возврату в операционную систему DOS. Поле Edit Выбор поля Edit (клавиша Alt-E) приводит к вызову редактора в виде редакционного окна на экране. Поле Run Выбор поля Run (клавиша A1t-R) приводит к высвечиванию следующих опций: Run Ctrl-F9 Program reset Ctrl-F2 Goto cursor F4 Trace into F7 Step over F8 User screen Alt-F5 Run Выбор опции Run (клавиши R или Ctrl-F9) приводит к компиляции программы в режиме Маке с последующим выполнением программы. Аргументы программы могут быть определены с помощью поля Options и опции Arguments. После окончания программы на экране появится надпись Press any key (нажми произвольную клавишу). Program reset Выбор опции Program reset (клавиши Р или Ctrl-F2) приводит к окончанию выполнения программы. После этого очищается оперативная память и закрываются все открытые файлы. Goto cursor Выбор опции Goto cursor (клавиши G или F4) приводит к установке временной точки останова в выделенной курсором строке (после его перемещения в редакционном окне), а затем к выполнению программы до точки останова. Trace into Выбор опции Trace into (клавиши Т или F7) приводит к выполнению ближайшего оператора основной программы с возможным слежением за операторами тела функции. Step over Выбор опции Step over (клавиши S или F8) приводит к выполнению ближайшего оператора основной программы, но без слежения за операторами тела функции. 3—764
34 Часть I. Операционная среда User screen Выбор опции User screen (клавиши U или Alt-F5) приводит к высвечиванию выходного экрана (экрана пользователя). Поле Compile Выбор поля Compile (клавиша Alt-C) приводит к высвечиванию следующих опций: Compile to OBJ Make EXE file Link EXE file Build all Primary C file Get info s Compile Выбор опции Compile OBJ (клавиша С) приводит к компиляции файла Name.C и созданию файла Atame.OBJ. Имя Name идентично имени файла опции Primary С file. Если это имя отсутствует, то присваивается имя файла, находящегося в окне редактора. Make EXE file Выбор опции Make EXE file (клавиша М) приводит к выполнению компиляции в режиме Маке и созданию файла Afarne.EXE, содержащего выполняемую программу. Имя Name идентично имени проекта, определенного в опции Project, в противном случае компилируется последний файл, загруженный в редакционное окно. Link EXE file Выбор опции Link EXE file (клавиша L) приводит к объединению текущих файлов с расширениями OBJ и LIB и созданию файла с расширением .EXE (без предварительного выполнения компиляции в режиме Маке). Build all Выбор опции Build all (клавиша В) приводит к созданию файла с расширением .EXE в режиме компиляции Build. Primary С file Выбор опции Primary С file (клавиша Р) дает возможность определения имени первичного файла. Get info Выбор опции Get info (клавиша G) приводит к высвечиванию
2. Системы 35 информационного окна, содержащего имя первичного файла, имя основного файла, размер основной программы в байтах, код возврата программы, размер доступной оперативной памяти. Поле Project Выбор поля Project (клавиша Alt-P) приводит к высвечиванию следующих опций: Project name Break name on Errors (Warnings/... /Fatal/Link) Auto dependencies On /Off Clear project Remove messages Project name Выбор опции Project name (клавиша P) дает возможность определения имени проекта. Если именем проекта является Name, то созданные после компиляции проекта файлы будут иметь имена Atame.MAP и Afame.EXE. Break make on Выбор опции Break make on (клавиша В) дает возможность определения момента окончания компиляции проекта. Это может наступить после предупреждающих сообщений (Warnings), ошибок (Errors), фатальных ошибок (Fatal errors) или перед компоновкой (Link). Auto dependencies При выборе опции Auto dependencies (клавиша А) в режиме Маке определяется возможность своевременного выполнения компиляции множества источников (On) либо невозможность такого выполнения (Off). Clear project Выбор опции Clear project (клавиша С) приводит к аннулированию имени проекта и очистке коммуникационного окна. Remove message Выбор опции Remove message (клавиша R) приводит к очистке окна связей. Поле Options Выбор поля Options (клавиша Alt-O) приводит к появлению следующих опций: Compiler Linker Environment 3*
36 Часть I. Операционная среда Directories Arguments Save options Retrieve options Опция Compiler Выбор опции Compiler (клавиша С) дает возможность определения способа компилирования программы. С опцией Compiler связано меню, содержащее следующие подопции: . Model Tiny/Small/.../Large/Huge Defines Code generation Optimization Source Errors Names Model Выбор подопции Model (клавиша M) дает возможность определения модели памяти. По умолчанию принимается модель Small (малая), а всего имеется 6 моделей: Tiny, Small, Medium, Compact, Large и Huge. Defines Выбор подопции Defines (клавиша D) дает возможность определения символов, используемых препроцессором, например запись Alfa; Beta = 3; Gamma = Jan. определяет символы Alfa, Beta и Gamma, и в частности отождествляет символ Beta с числом 3, а символ Gamma - со строкой знаков Jan. Code generation Выбор опции Code generation (клавиша С) дает возможность определения типа генерирования кода. Допустимыми опциями подопции Code generation являются: Calling convention - вызов функции в терминах языка Си или Паскаль. Instruction set - выбор набора машинных инструкций. Floating point - использование сопроцессора или эмулятора. Default char type - проверка влияния бита знака на операции с переменными типа (char). Aligment - определение способа размещения полей структур и объединений. Generate underbars - дополнение идентификаторов знаком подчеркивания.
2. Системы 37 Merge duplicate strings Standard stack frame Test stack overflow Line numbers Optimization - однократное резервирование места для идентичных знаковых строк. - генерация дополнительного кода в прологе и эпилоге функции. - проверка на переполнение стека во время выполнения программы. - генерация номеров строк основной программы в выходном файле. Выбор подопции Optimization (клавиша О) дает возможность определения способа оптимизации результирующей программы. Допустимыми опциями подопции Optimization являются: Optimize for Use register variables Register optimization Jump optimization Source - оптимизация по быстродействию или времени выполнения. - возможность использования регистровых переменных. - использование информации о данных, находящихся в регистрах процессора. - удаление ошибочных команд, связанных с организацией цикла. Выбор подопции Source (клавиша S) определяет способ трактования основной программы во время ее компиляции. Допустимыми опциями под- опции Source являются: Identifier length Nested comments ANSI keywords only - размер идентификатора. - вложенные комментарии. - восприятие только ключевых слов, определенных ANSI. Errors Выбор подопции Errors (клавиша Е) определяет способ реагирования системы Турбо Си на ошибки, содержащиеся в основной программе. Допустимыми опциями подопции Errors являются: Errors - количество ошибок, после обнаружения которых наступит прекращение компиляции. Warnings - количество предупреждений, после которых наступит прекращение Display warnings компиляции. - определение того, должна ли происходить сигнализация Portability warnings предупреждений. - определение того, должна ли происходить сигнализация предупреждений, связанных с такими ошибками, как
38 Часть I. Операционная среда А: Непереносимое преобразование указателя. В: Непереносимое сравнение указателей. С: Непереносимое сравнение данных указателей. D: Непереносимое сравнение с литералом. Е: Чрезмерное количество цифр константы. F: Возможная утрата значащих цифр во время преобразования. G: Неотличимость указателей типов (signed char) и (unsigned char). ANSI violations - определение того, должна ли происходить сигнализация таких нарушений требований ANSI, как А: Обращение к несуществующему полю структуры. В: Использование структуры, не содержащей полей. С: Наличие в определенной функции, не дающей результата, функции return, содержащей выражение. D: Наличие в определении функции как функции return, содержащей выражение, так и функции return, не содержащей выражения. Е: Ненадежное преобразование указателей. F: Отсутствие определения структуры с использованным именем. G: Неидентичное переопределение идентификатора. Common errors - определение того, должна ли происходить сигнализация предупреждений перед такими ошибками, как А: Отсутствие результата функции. В: Определение функции, содержащей операторы, которые никогда не будут выполнены. С: Определение функции, содержащей оператор, выполнение которого не будет иметь никакого результата. D: Использование идентификатора без его предварительного объявления. Е: Определение данного, которое никогда не. будет использовано. F: Объявление параметра функции, который никогда не будет использован. G: Возможное использование неправильного присвоения. Less common errors - определение того, должна ли происходить сигнализация предупреждения перед такими нетипичными ошибками, как А: Использование оператора указания перед идентификатором массива или функции. В: Объявление переменной, которая никогда не будет использована. С: Отказ от явного использования скобок в месте источника ошибок. D: Использование в качестве аргумента имени структуры, а не указания структуры. Е: Отсутствие объявления функции с указаньем именем. F: Вызов функции, имеющей неизвестные типы ее параметров.
2 Системы 39 Names Выбор подопции Names (клавиша N) дает возможность замены имен сег- ментов, в которых будут размещены код, а также данные программы. До- пустимыми подопциями подопции Names являются Code names - определение имени сегмента кода. Data names - определение имени сегмента данных. BSS names - определение имени сегмента BSS. Опция Linker Выбор опции Linker (клавиша L) определяет способ компоновки программы. Допустимыми подопциями опции Linker являются: Map file - определение типа карты распределения памяти. Initialize segments - определение необходимости инициализации сегментов. Default libraries - определение необходимости включения при компоновке библиотек, не принадлежащих системе Турбо Си. Graphics library - определение необходимости автоматического поиска в графической библиотеке. Warn duplicate symbols - определение необходимости сигнализации в по- вторении символов в объективных файлах и библиотеках. Stack warnings - определение необходимости генерации компоновщиком сообщения, информирующего об отсутствии зарезервированного места для стека. Case sensitive link - определение необходимости различать компоновщиком строчные и заглавные буквы символов. Опция Environment Выбор опции Environment (клавиша Е) определяет каталоги, в которых система Турбо Си может искать необходимые ей файлы. Кроме того, с помощью этой опции можно определить некоторые особенности системы. До- пустимыми подопциями опции Environment являются: Massage tracking - определение способа отслеживания ошибок в редакционном окне во время перемещения курсора в коммуникационном окне (Track ... current file - только в редакционном окне; Track ... all files - во всех файлах). Keep message - определение способа трактования сообщений, высвеченных в коммуникационном окне (On - сохранение сообщений с предыдущих компиляций; Off - очищение окна перед компиляцией). Config auto save - определение условий запоминания действующей конфигурации системы Турбо Си (On - перед каждым выполнением программы, а также при вызове системы DOS; Off - только при задании опции О/Save Options). Edit auto save - определение необходимости автоматической записи на диск файла, находящегося в редакционном окне, перед каждым выбором поля Run или опции OS shell. Backup files - определение необходимости выбора опции Save после запо- минания предыдущей версии основного файла как файла с расши- рением .ВАК.
40 Часть I. Операционная среда Tab size - определение шага табуляции. Zoomed windows - определение необходимости занятия окном (редакцион- ным или коммуникационным) целого экрана. Screen lines - определение числа строк текста, высвечиваемых на экране (по умолчанию 25). Опция Directories Выбор опции Directories (клавиша D) дает возможность определения каталогов, в которых будет осуществлен поиск файлов данных. Допустимыми подопциями опции Directories являются: Include directories - определение каталогов, в которых находятся файлы, такие, как, например, stdio.h (имена каталогов отделяются точкой с запятой). Library directories - определение каталогов, в которых находятся библиотечные файлы - с расширением .LIB, а также стартовые файлы - СО?.OBJ (имена каталогов отделяются точкой с запятой). Output directory - определение каталога, в котором помещены файлы с расширениями .OBJ .EXE и .МАР. Turbo С directory - определение каталога, в -котором находятся файлы: « информационный (TCHELP.TCH) и конфигурационный (TCCONFIG.TC). Pick file name - определение имени файла указания, в котором хранятся имена файлов, выбираемых с помощью опции File/Pick file. Внимание: Поле Current pick file дает текущее имя файла Pick (определенного подопцией Pick file name). Опция Arguments Выбор опции Arguments (клавиша А) дает возможность передачи системе Турбо Си аргументов, с которыми будет вызвана текущая программа. Это позволяет выполнять программу в тех же самых условиях, какие создаются при вызове программы из системы DOS. Опция Save options Выбор опции Save options (клавиша S) приводит к запоминанию в конфигурационном файле TCCONFIG.TC опций системы Турбо Си. Опция Retrieve options Выбор опции Retrieve options (клавиша R) приводит к загрузке конфигурационного файла TECONFIG.TC последующими действиями, согласно опциям, записанным в этом файле. Поле Debug
2. Системы 41 Выбор поля Debug (клавиша Alt-D) следующих опций: приводит к высвечиванию Evaluate . Call stack Find function Refresh display Display swapping Source debugging Ctrl-F4 Ctrl-F3 Smart/Always /None On/Off Evaluate Выбор опции Evaluate (клавиша Е или Ctrl-F4) приводит к высвечиванию диалогового окна, состоящего из трех рамок: Evaluate, Result и New Value. Перемещение между рамками производится с помощью клавиш вертикальных стрелок. Помещение в рамку Evaluate некоторого выражения приводит к высвечиванию его значения в рамке Result. Если выражение является именем переменной, то в рамке New Value можно задать новое значение этой переменной. В соответствии с приведенным ранее описанием, если после выражения будет дан шаблон (С, S, D, Н, X, Fn, М, Р), то осуществится вывод значения выражения по этому шаблону (например, Агг [4], 2Х приводит к выводу Агг [4], Агг [5] и Агг [6] в шестнадцатеричной форме). Call stack Выбор опции Call stack (клавиши С или Ctrl-F3) приводит к высвечиванию стека вызовов функций. Find function Выбор опции Find function (клавиша F) приводит к высвечиванию текста функции с указанным именем. Refresh display Выбор опции Refresh display (клавиша R) приводит к очищению экрана монитора. Display swapping Выбор опции Display swapping (клавиша D) определяет способ переключе- ния между экраном программы запуска и выходным экраном (Smart - толь- ко перед ожидаемым выводом на экран, Always - всегда, None - никогда). Source debugging Выбор опции Source debugging (клавиша S) определяет способ компиляции программы относительно ее запуска (On - с помощью интегрированного запуска, Standalone - с помощью автономной программы запуска, None - без использования программы запуска).
42 Часть I. Операционная среда Поле Break/Watch Выбор поля Break/Watch (клавиша Alt-B) приводит к высвечиванию следующих опций: Ctrl-F7 Add watch Delete watch Edit watch Remove all watches Toggle breakpoint Ctrl-F8 Clear all breakpoints View next breakpoint Add watch Выбор опции Add watch (клавиша А или Ctii-F7) приводит к размещению в просмотровом окне данного выражения (в том числе имени переменной). Это также можно сделать с помощью клавиши Ins или Ctrl-N, нажатой при активности просмотрового окна. Delete watch Выбор опции Delete watch (клавиша D) приводит к удалению выделенной строки в просмотровом окне. Это может быть сделано также клавишей Del при активности просмотрового окна. Edit watch Выбор опции Edit watch (клавиша Е) дает возможность редактирования выделенного выражения, находящегося в просмотровом окне. Это также можно выполнить с помощью клавиши Enter, нажимая ее при активности просмотрового окна. Remove all watches Выбор опции Remove all watches (клавиша R) приводит к очистке просмотрового окна. Toggle breakpoint Выбор опции Toggle breakpoint (клавиши Т или Ctrl-F8) приводит к установке или снятию точки останова в строке, выделенной курсором. Clear all breakpoints Выбор опции Clear all breakpoint (клавиша С) удаляет все расставленные точки останова. View next breakpoint Выбор опции View next breakpoint (клавиша V) перемещает курсор до строки, в которой находится следующая точка останова.
2. Системы 43 2.2. Система загрузки Вызов системы загрузки имеет вид ТСС options names, где options - опции, a names - имена основных файлов, которые должны быть обработаны. Каждая опция должна начинаться со знака минус и отделяться от другой опции или имени файла по крайней мере одним пробелом. Если имя файла имеет расширение .ASM, то содержащийся в этом файле текст компилируется посредством Turbo Assembler (TASM). Созданный таким способом файл с расширением .OBJ, как и другие файлы с таким расширением (перечисленные в команде или возникшие в результате компиляции) после объединения с библиотеками (расширение .LIB), образует исполнительную программу с именем name .EXE, в котором name совпадает с именем первого файла из последовательности names. Пример. Создание загрузочного модуля Вызов tcc greet -ms hello ward.obj libra.lib организует компиляцию модуля HELLO.C, а затем объединение модулей HELLO.OBJ и WORLD.OBJ с библиотекой LIBRA.LIB и создание в модели SMALL (опция -ms) исполнительной программы HELLO.EXE. Опции, которые могут быть перечислены в команде ТСС, находятся в тесной связи с опциями, используемыми в интегрированной системе. С этой точки зрения достаточно ограничиться следующим списком, в котором имеется аналогия с интегрированной системой (словесные описания, записанные курсивом, не имеют эквивалентов в интегрированной системе). Внимание: Исключение опции реализуется записью после нее знака минус. Опции, обозначенные звездочкой, являются принимаемыми. Опция Опция меню Значение -A O/C/Source/ANSI keywords On -a O/C Code generation/Alignment Word * -a- O/C Code generation/Alignment Byte -B Возможность преобразования ассемблерных вставок -C O/C/Source/Nested comments On -Dname О/С/Defines -Dname=str О/С/Defines -d O/C/Code generation/Merge duplicate strings On * -d- O/C/Code generation/Merge duplicate strings Off -Ename Определение имени ассемблера no умолчанию (TASM) -ename Определение имени файла, в котором должна размещаться выполняемая программа * -f О/С/Code generation/Floating point Emulation -f- O/C/Code generation/Floating point None
44 Часть I. Операционная среда -М7 0/C/Code generation/Floating point 8087 -G О/C/Optimization/Optimize for Speed -g# 0/C/Errors/Warnings: stop after # -Ipalh O/C/Include directories -i# 0/C/S/Identifier length # -)# O/C/Errors/Errors: stop after # -К O/C/Code generation/Default char type Unsigned -К- 0/C/Code generation/Default char type Signed -k O/C/Code generation/Standard stack Frame On -Lpath О/D Library directory -\opt Передача опции компоновщику -M O/L/Map file -me 0/C/Model Compact -mh 0/C/Model Huge -ml О/C/Model Large -mm О/C/Model Medium -ms О/C/Model Small -mt O/C/Model Tiny -N O/C/Code generation/Test stack overflow On -npath О/D/Output directory -0 O/C/Optimization/Jamp optimization On -0 Определение имени файла, в котором должен быть размещен скомпилированный файл -P O/C/Code generation/Calling convention Pascal -p- O/C/Code generation/Calling convention C -r O/C/Optimization/Use register variables On -S Генерация кода ассемблера -Vname Стирание предыдущего определения символа пате -u O/C/Code generation/Generate underbars On -V Debug/Source debugging On -w 0/C/Errors/Display warnings On -w- 0 / C / Errors / Display warnings Off ~WXXX О/C/Errors/Portability warnings, ANSI violations. Common errors. Less Common errors On -w-xxx 0/C/Errors/Portability warnings, ANSI violations. Common errors. Less Common errors Off 'У O/C/Code generation/Line numbers On -Z 0 / C / Optimization /Register optimization On -ikname 0/C/Names/Code/Class -?£name 0 / C / Names / BSS / Class -zC name О/C/Names/ Code / Segment -zDname 0 / C / Names / Code / BSS / Segment -iGname 0 / C / Names / BSS / Group -тРпате 0 / C / Names / Code / Group -zRname О/C/Names/Data Segment -zSname 0 / C / Names / Data / Group -zT name 0/ C / Names / Data / Class -1 O/C/Code generation 80186 /80286 -1- O/C/Code generation 8088/8086 Произвольный набор перечисленных опций может быть расположен в текстовом файле TURBOC.CFG. Удобнее всего это сделать, размещая по
2. Системы 45 одной опции в строке. При вызове системы загрузки командой ТСС эти опции трактуются, как если бы они находились непосредственно после имени команды. Если имеются взаимоисключающие опции, то правильной считается последняя. Файл TCC.CFG отыскивается последовательно: а) в текущем каталоге, б) в каталоге, в котором находится ТСС. Пример. Вызов системы загрузки Выполнение команды tcc -mm -Libra-egreeting greet greet.asm mylib. lib приводит к выполнению исполнительной программы GREETING. EXE (- egreeting), скомпилированной в модели MEDIUM (-mm) и составленной из программы на языке Си (greet) и в ассемблере (greet, asm) послр объединения их с библиотеками MYLIB.LIB и LIBRA (-Libra)
ЧАСТЬ II. ЯЗЫК 3. Лексические понятия В программах, написанных на языке Си, можно выделить следующие лексические единицы: идентификаторы, ключевые слова, литералы, операторы и разделители. Лексический анализ проводится в порядке расположения знаков в программе. За очередную лексическую единицу принимается наибольший ряд знаков, который может образовывать лексическую единицу. С этой точки зрения, поскольку в языке Си операторами являются как двойной минус, так и просто минус, такие выражения, как, например, а — Ь, трактуются как а----Ь, а не как отличное от него выражение а----Ь. Пример ♦include <stdio.h> main( ) { char chr = 'b' putchar(— chr); return 0; } В приведенной программе имеются, в частности, следующие лексические единицы: идентификаторы (chr, putchar), ключевые слова (char, return), литералы ('b'), операторы (=) и разделители (;) Функция putchar(— chr); не интерпретируется как putchar (-(- char)); поскольку за лексической единицей, следующей после открывающей скобки, распознается наибольший ряд знаков, который может быть принят за
3. Лексические понятия 47 лексическую единицу (двойной минус). По аналогичным причинам не является ключевым словом запись char, содержащаяся в putchar. 3.1. Комментарии Комментарий не является лексической единицей. Каждый комментарий начинается парой последовательных знаков /* (косая черта, звездочка) и кончается также парой знаков */ (звездочка, косая черта). В комментариях не могут быть заключены другие комментарии. Комментарий трактуется как пробел. Это означает, например, что описание int/**/Six; эквивалентно описанию int Six; Пример /* EUA /* IZA*/ JAN*/ Приведенная запись не является комментарием, поскольку состоит из комментария /*EWA /* IZA * / и последовательности символов JAN * / Для сравнения запись /* EUA /* IZA*/ /*JAN*/ представляет собой два последовательных комментария. 3.2. Идентификаторы Идентификатором является ряд букв и цифр, начинающихся с букв. В языке Си считается также знак подчеркивания. Строчные и прописные буквы воспринимаются отдельно. Хотя число знаков идентификатора не ограничено, идентификаторы, имеющие одинаковые 32 первых знака, считаются идентичными. Примеры Идентификаторы: Alpha —beta— Very Long R2D2. He являются идентификаторами: 4sale - начинается с цифры; #23 - не начинается с буквы; Str$ - содержит неразрешенный знак.
48 Часть II. Язык 3.3. Ключевые слова Ключевыми словами являются лексические единицы, имеющие вид постоянных идентификаторов. Эти слова зарезервированы и не могут быть использованы в качестве идентификаторов объектов. Ниже приведен список ключевых слов: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typed ef union unsigned void я Пример fun(RETURN) int RETURN { return RETURN; } volatile while RETURN является идентификатором, a return - ключевым словом. Эти слова имеют разные значения и не противоречат друг другу. 3.4. Литералы Как и в других языках программирования, литералами являются такие символы, которые в рамках принятой интерпретации определяют все свои особенности, в том числе и собственное значение. Литералы подразделяются на числовые, знаковые, с плавающей точкой и строки (строчные литералы). Числовые литералы Числовые литералы могут быть в виде десятичных, восьмеричных и шест- надцатеричных чисел. Десятичные числа состоят из десятичных цифр (на- пример, 255). Восьмеричные числа состоят из восьмеричных цифр, перед ко- торыми стоит цифра 0 (например, 0377). Шестнадцатеричные числа пред- ставляются шестнадцатеричными цифрами, перед которыми записывается пара знаков Ох или ОХ (например, OxFF). Шестнадцатеричные цифры больше 9 (от 10 до 15) представляются строчными или прописными буквами от А до F соответственно. Если в данной реализации возможно несколько вариантов представления целых чисел, то по количеству значащих цифр целые литералы могут быть числами данного типа (long int) .В записи таких чисел в конце прибавляется строчная или прописная буква L (например, OxFFL). Пример var ” 5L - ОхА;
3. Лексические понятия 49 В приведенном выражении представлены целые литералы 5L и ОхА. Ли- терал 5L представляет число типа (long int) величиной 5, а литерал ОхА - шестнадцатеричное число типа (int) величиной 10. Знаковые литералы Знаковые литералы обозначаются в виде 'с , где с - произвольный знак, за исключением знаков (апостроф) и \ (косая черта). Для некоторых символов принято обозначение с помощью обратной дробной черты \L, где L может быть: а) буквой; б) числом из одной, двух или трех восьмеричных цифр (DDD), определяющих код знака процессора; в) числом из одной, двух или трех шестнадцатеричных цифр с предшествующей буквой х или X (xDDD), определяющей код знака процессора. Следующий список содержит знаковые литералы, которые имеют установленную интерпретацию: Литерал Интерпретация \п Новая строка (new line) \г Горизонтальная табуляция (tabulation) \Ь' Возврат на один шаг (backspace) \г' Возврат каретки (carriage return) \Г Перевод формата (form feed) W' Обратная дробная черта '\а' Звонок Апостроф \о- Знак кода 0 (ноль) Пример putchar ('\”1 Выполнение этой функции приводит к выводу одного апострофа. Поскольку в ASCII знак ' (апостроф) имеет код 47s или 2716, то указанную функцию можно представить в виде putchar (’\47’1 или putchar С\х27') Литералы с плавающей точкой Литералы с плавающей точкой представляют данные с двойной точностью. Они состоят из целой части, точки, дробной части, а также из показателя, имеющего вид целого числа со знаком, перед которым находится прописная 4—764
50 Часть 11. Язык или строчная буква Е. В записанном таким образом литерале можно опустить целую или дробную часть, а также знак показателя, если он положительный. Кроме этого, можно не указывать точку или показатель вместе с буквой Е. Пример 23 23 Зе2 2.ЕЗ-2 4.0 0.5 0.0 ОеО Все приведенные лексические единицы представляют собой литералы с плавающей точкой. Строки (строчные литералы) Строки - ^это последовательность символов, заключенная в кавычки. Строка соответствует типу данных “массив символов". Строка может содержать любые определенные в конкретной реализации символы. Если в литерале использовано описание знака, не имеющего интерпретации, то начинающий его знак \ (косая черта) будет пропущен. В конец строки компилятор добавляет нулевой символ “0“, что является признаком конца строки. Если текстовый литерал должен включать знак “ (кавычки), то этот знак необходимо записать в виде \“ (косая черта, кавычки). Пример printf С"\"') Аргументом функции printf является литерал представляющий пару знаков:апостроф и знак нуль. Литерал имеет отличный тип от литерала первый из них - строка, а второй - знаковый литерал. Внимание: Каждая пара соседних строк трактуется как строка, состоящая из всех знаков первого литерала и следующих после них всех знаков второго. Таким образом, например, запись “Hello" “ “ “world" трактуется как запись “Hello World"
4. Описания Объекты, подлежащие преобразованию, представляются посредством имен. Простейшими именами объектов являются литералы и идентификаторы. Первые представляют в программах постоянные, а вторые - переменные величины. Кроме этого, объекты могут быть представлены соответствующим образом составленными выражениями. Поскольку об особенностях объектов обычно судят на основе их имен, важное значение приобретает описание объектов. Описание является излишним при использовании литералов, поскольку все атрибуты объекта, представленного через литерал, могут быть поняты на основе записи литерала, однако описание необходимо для идентификаторов переменных. Хотя основным атрибутом идентификатора является тип, который определяет допустимый список операций и значений, в описании помешается обычно некоторые дополнительные атрибуты, связанные с областью действия, классом описываемого объекта н т. д. 4.1. Описание простых переменных Простой переменной называется переменная, которой могут быть приписа- ны только неделимые данные. Такими данными являются, в частности, дан- ные следующих основных типов: char, int, short, long, float, double и long double. Типы char (символьный), int (целый), short (короткий целый), long (длинный целый) могут быть модифицированы ключевыми словами signed и unsigned. Данные, имя типа которых включает модификатор signed, являются данными со знаком, а данные, имя типа которых содержит модификатор unsigned, - данными без знака. При отсутствии модификатора по умолчанию принимается модификатор signed. Имя типа, состоящее из одного модификатора, эквивалентно имени, содержащему ключевое слово int. В системе Турбо Си приняты следующие диапазоны величин данных отдельных типов: Типы Диапазоны char, signed char unsigned char short, signed short unsigned short int, signed int unsigned, unsigned int long, signed long unsigned long -128 .. 127 ' л у-. . 0 .. 255 ~ -32768 .. 32767 0 .. 65535 > S1 S ’• -32768 .. 32767 0 .. 65535 -2147483648 .. 2147483647 7 \ S. ft.v ‘ Й 0 .. 4294967295 C 1 U float doubie long doubie -3.4e38 .. 3.4e38 J / -1.7e308 .. 1.7e308 ' ' . - 3.4e4932 .. 3.4e4932 " ’ C. ? (O''
52 Часть II. Язык Данные типа (char) - 1-байтовые, данные типа (short) и (int) - 2- «байтовые, данные типа (long) и (float) - 4-байтовые, данные типа (double) - 8-байтовые, а типа (long double) - 10-байтовые. Отдельным типом данных является перечислимый тип данных - упорядоченное множество элементов, доступ к которым осуществляется по имени. Над элементами перечислимого типа разрешены операции присваивания и сравнения. Перечислимый тип описывается с помощью служебного слова enum, за которым в фигурных скобках следует список элементов перечисления. Каждый элемент перечисления является константой данного перечислимого типа. Элементы . перечисления обозначаются идентификаторами и представляются значениями целого типа (int), начиная с нуля, с последующим увеличением значения на 1. Если в стеке используется форма с присваиванием, то значение константного выражения устанавливает значение данного элемента списка и последующие элементы увеличиваются, начиная от этого нового значения, v Пример enum Colour {Red, Green = 3, Blue} Hue; include «stdio.h» main( ) { Hue = Green; printf("%d", Hue - Blue); return 0; } В программе описана переменная Hue перечислимого типа (enum Colour), а также три идентификатора: Red, Green и Blue. Приведенная программа эквивалентна следующей: enum Colour {red. Green = 3, Blue} Hue; ^include «stdio.h» main{ ) { Hue = 3; printf(”%d", Hue - 4); return 0; } Выполнение программы приведет к выводу числа -1. Кроме перечисленных типов данных в языке Си существуют также указатели. Описание указателей идентифицируется знаком * (звездочка, которая стоит . перед переменной). Указатель - это переменная, значением которой является ссылка на другой объект. Указатели делятся на указатели переменных и адресные указатели. Первые из них указывают- на переменные, а вторые - локализуют объемы памяти. Адресные указатели типа (void*) отличаются от других указателей тем, что существует возможность выполнения преобразования указывающего данного, используя оператор указания, записываемый с помощью знака &
4. Описания 53 (амперсанд). Предполагается, что если ехр - выражение для имени переменной, то &ехр - указатель этой переменной. Из этого правила существует только одно исключение: если ехр - переменная, которая является именем массива, то принимается, что уже сама эта запись указывает на первый элемент массива. Обратной операции указания (&) является операция нахождения (*). Если ехр - указатель и, следовательно представляет данное, которое указывает на переменную, то *(ехр) - имя указателя. В частности, если id - идентификатор указателя, то *id - имя переменной, указываемой посредством данного, которое приписано переменной id. Примеры а) прямое указание include <stdio.h> int Dec - 10; int *Ptr = &Dec; main ( ) { *Ptr = *Ptr + 3; printf(" % d", *Ptr); return 0; } В программе описаны'переменная Dec типа (int) и указатель Ptr типа (int*). Переменной Dec присвоено начальное значение 10, а переменной Ptr- указание переменной Dec. Запись &Dec представляет указатель переменной Dec, a *Ptr является именем указателя через присвоенное ей значение и, следовательно, в приведенной программе выполняет роль имени переменной Dec. С учетом этого выполнение оператора *Prt = *Prt + 3; дает такой же результат, как и выполнение оператора Dec = Dec + 3; а выполнение программы приводит к выводу числа 13. б) Косвенное указание include <stdio.h> ' int Dec = 13; *Ref = &Dec, **Prt = &Ref; main( )
54 Часть II. Язык { printf("%d", **Prt); return 0; } В программе описаны переменные Dec, Ref и Prt соответственно типов (int), (int*) и (int**). Переменной Prt присвоено начальное значение указателя Ref, а переменной Ref - начальное значение указателя Dec. Поскольку после этих присвоений *Prt - имя переменной Ref, a **Ptr - имя переменной Dec, то выполнение программы приведет к выводу числа 13. в) Адресное указание int Dec = 13; void *Ptr = #Dec; Указатель Ptr имеет тип (void*). Этой переменной приписано адресное указание, локализующее ту часть оперативной памяти, откуда начинается резервирование места для переменной Dec. 4.2. Описание, блоков переменных Блоками переменных являются массивы, структуры и объединения. Массивы состоят из компонентов одинакового типа, а структуры и объединения включают в общем случае различные типы компонент. Компоненты массива называются ее элементами, а компоненты структуры и объединения - полями. Поскольку компонентами блоков могут быть не только простые переменные, но также массивные структуры и объединения, возможно описание блоков данных большой сложности. Массивы Описание массива отличается от описания простой переменной заданием числа его элементов, взятым в квадратные скобки. Это обычно целое число, хотя в общем случае может быть и произвольным константным выражением, величина которого определена перед началом выполнения программы. Пример int vec[3]; int arr[3] [2]; Описан массив vec, который является вектором с элементами vec[0], vecfl ] и vec[2] типа (int). Кроме того, описан массив агг, элементами которого являются векторы, состоящие из двух элементов типа (int). Массив агг можно трактовать как двумерную таблицу (матрицу) с тремя строками и двумя колонками. Обращения к элементам этой таблицы могут, например, иметь вид агг[2][1], а не типичный для многих других языков программирования вид агг [2,1].
4. Описания 55 На языке Си запись arr[i,j] эквивалентна записи arr[j] и представляет двухэлементный вектор, образующий j-й элемент вектора агг. Поскольку образование массивов часто производится с помощью указателей, надо помнить, что если vec - произвольный вектор, то имя его i-ro элемента vec[i] „ трактуется точно так, как выражение *(vec + 1), которое также может быть принято за имя элемента. Имея это в виду, легко понять, что если агг, например, массив short int arr[2] [3]; то выражение *(*(arr+1)+j) является элементом arr[i][j], а выражение **агг - элементом агг[0] [0]. Другой важной особенностью является принятие имени массива за указа- тель на первый элемент этого массива (исключением является только опи- санный ниже оператор sizeof). Поэтому, например, если агг - массив с эле- ментами типа (int), то запись агг является указателем, эквивалентным выра- жению &агг[0] типа (int*), т. е. типа “указание переменной типа int“. Принимая во внимание вышеизложенное, можно заключить, что если не- которое данное указывает элемент массива с индексом i, то добавление к нему целого данного величиной j приведет к образованию данного, указыва- ющего на элемент таблицы с индексом i + j. Это подтверждает тесную связь операций с указателями и массивами на языке Си. Пример include «stdio.h» int arr[2] [2]; int (*ref)[2j; main( )
56 Часть II. Язык { arr[l][l] = 13; ref = arr + 1; printf("%d”. (*ref) [1]); return 0; } Поскольку запись arr в выражении ref = arr + 1; эквивалентна записи &arr[0], а выражение &arr[0] + 1 эквивалентно выра- жению &arr[l ], то переменной ref будет приписано значение, величина ко- торого является указанием на вектор, образующий вторую строку таблицы arr. # После выполнения присвоения выражение *ref представляет собой вектор, указанный через ref, а выражение (*ref) [1 ] представляет собой второй эле- мент этого вектора, т. е. элемент arr [1] [1] массива arr. В описанном случае вызов функции printf эквивалентен вызову printf("%d”, arr[l] [1]); а в результате выполнения программы выводится число 13. Можно убедиться, что тот же результат дало бы выполнение программы, в которой вызов функции printf имел бы вид printf("%d", *(*ref + 1)); или printf("%d", ref[0] [1]); Во всех приведенных выше примерах элементами массивов были перемен- ные типа (int), а также векторы элементов типа (int). Конечно, элементами массивов могут быть переменные и других типов, в том числе указатели. Это иллюстрирует, например, описание float *(ptr[4]); массива ptr, каждый из 4 элементов которого является указателем типа (float). Поскольку приоритет квадратных скобок выше приоритета оператора * (звездочка), то использованные в приведенном описании круглые скобки могли бы быть опущены. Однако их нельзя опустить, например, в описании простой переменной ref float(*ref)[4]; так как это приведет к описанию массива, что превратило бы ptr в массив с рассмотренными особенностями.
4. Описания 57 Структуры и объединения Особый тип блоков образуют структуры н объединения. В отличие от массивов эти объекты состоят в общем случае из компонент различных типов. Ими могут быть простые переменные, массивы, структуры и объеди- нения, а также рассматриваемые ниже поля битов. Поля структуры разме- щаются в оперативной памяти одно за другим в той последовательности, в которой перечислены в описании. Поля объединений размещаются, начиная с одного места памяти, и, следовательно, накладываются друг на друга. Поскольку разница между структурами и объединениями ограничивается способом размещения их полей, в дальнейшем будут рассматриваться только структуры. Типичное описание структуры имеет вид struct{ fields }list; где fields - набор полей структуры, a list - список определяемых одновременно структур, например struct{ char arr[3][2], *ref, var; float (*ptr)[4]; }Str, Vec[5], *Ref; В приведенном описании поле агг - двумерный массив, поле ref - указа- тель типа (char), поле (ptr) - указатель на вектор из 4 переменных типа (float). Из этих полей состоит структура Str, а также каждый элемент мас- сива Vec. Идентификатор Ref определяется как указатель на структуру с данными полями. Это означает, что правильно было бы, например, присво- ение Ref = &Str в котором & (.амперсанд) - оператор указания, используемый для определе- ния указания переменной Str. Поскольку не имеется ограничений на вид полей структур и объединений, правильным, в частности, является следующее описание двух структур Strl и Str2: struct{ char chr; struct{ float CompF; char CompC; }max, min; short, en; }Strl, Str2 полями которых соответственно являются: переменная типа (char), две структуры с полями типа (float) и (char), а также переменная типа (short int). Для обращения к переменным типа (float), находящимся в структуре Strl, требуется два раза воспользоваться оператором выбора, например
SS Часть И. Язык S tri. max. CompF=Str1. m < n. CompF-3e2; В некоторых случаях может быть удобным связывание с определенной структурой некоторого идентификатора без описания при этом структуры. Для этого требуется воспользоваться записью struct пате{ fields }; где пате - имя структуры, идентифицирующее ее поля fields. В рамках такого описания запись struct name list; я эквивалентна записи struct{ fields }list; Этот способ часто используется для описания тех полей структур, которые определяют указатели идентично описанной структуры, например: struct tree{ char name [30]; int count; struct tree *east; struct tree *west; str, *ptr; } В приведенном описании tree - имя структуры с полями name, count, east и west, str - структура типа (struct tree), a ptr - указатель на структуру типа (struct tree). Если будет выполнен оператор ptr = &str; то указателю ptr будет приписан адрес структуры str. В этом случае обра- щение к полю может иметь уже известный вид (*ptr).count или эквивалентный ему вид ptr-> count В последнем случае используется двухаргументный оператор выбора -> (минус, больше) с целью идентификации полей структуры посредством ука- зателя.
4. Описания 59 Поля битов Как отмечалось выше, полями структур могут быть, в частности, поля битов. Хотя правила языка не имеют ограничений на характер этих полей, кроме требования, чтобы они помещались в объеме машинного слова, в типичных применениях поля битов служат для хранения целых данных - чаще типа (unsigned). Описание поля битов состоит из описания поля, его имени и указанного после двоеточия размера поля, например unsigned status:6: Если имя поля опущено, то создается скрытое поле. Если размер поля битов представлен числом 0, то следующее поле битов начнется с границы слова. Примером описания структуры, у которой одно из полей является полем битов, может служить следующее описание: struct{ char chr; unsigned flag: 1; int value; }str; 4.3. Определение класса памяти Одним из важных атрибутов объекта языка Си является класс памяти. Он характеризует время существования объема памяти и место хранения объекта в программе. В языке существуют четыре класса памяти объектов: auto, static, extern и register. Если класс не определен явно, то по умолчанию принимаются класс extern для объектов, описанных вне функций, и класс auto в пределах опи- сания функций. Объекты, описанные внутри блока с классом памяти auto либо без указания класса памяти, относятся к автоматическим объектам. Последние являются локальными по отношению к блоку объектами и хранятся внутри того блока, где они описаны. Автоматические объекты существуют только во время выполнения данного блока и при выходе из блока теряют свои значения и освобождают занимаемую или физическую память. Классы static и extern касаются объектов, существующих в течение всего времени выполнения программы и хранятся вне любой функции, входящей в состав программы. Разница между ними заключается в том, что объекты класса extern (внешние, глобальные) доступны из всех модулей программы, в то время как объекты класса static (внешние, статические) доступны толь- ко в пределах того модуля или блока, в котором были описаны. Константы являются объектами статического класса памяти. Объекты, описанные с клас- сом памяти extern либо описанные вне функции без указания класса памя- ти, относятся к внешним объектам. Объекты, описанные с классом памяти static, относятся к статическим объектам. Объекты, описанные внутри блока с классом памяти register, называются регистровыми переменными. Класс register касается тех автоматических переменных, которые должны быть сохранены в регистрах компьютера, а не в оперативной памяти, что ведет к ускорению операций под этими переменными. Поскольку количество
60 Часть 1Г. Язык регистров, как правило, ограничено, это требование касается обычно только нескольких переменных. Остальные трактуются как переменные класса auto. В связи с понятием класса переменной, требуется знать, что описания имеют в общем случае характер обращения или определения. Это имеет, особое значение в отношении переменных класса extern, так как они доступны из всех модулей, в которых были описаны. В этом случае принимается во внимание, что все описания идентификатора связаны с одной переменной. Среди этих описаний в программе должны присутствовать одно главное описание (без слова extern) после определения функций и произвольное число описаний обращения. Пример struct list{ char *first_name; char *lastjpame; struct list *next; }; static struct list *var; Переменная var - простая переменная класса static, указатель на структуру типа (struct list). Эта переменная создается перед выполнением программы и доступна только в блоке или в модуле, в котором описана. Если переменная var должна быть доступна в нескольких несвязанных модулях, то в одном из них должно присутствовать ее главное описание, например struct list *var; а в других - описание обращения extern struct list *var; 4.4. Присвоение начальных значений (инициализация объекта) Одной из особенностей языка является возможность задания в описаниях инициирующих присваивакпцих выражений, присваивающих начальные значения переменным во время их описания. Начальные значения, присваиваемые арифметическим и знаковым переменным, могут быть определены только с помощью арифметических вы- ражений. Начальные значения, присваиваемые указателям, могут быть опре- делены только с помощью числа 0 или указывающих выражений, которые включают в себя указатели на предварительно описанные внутренние или внешние переменные или указатели, отличающиеся от них на константу. Если начальное значение не будет определено явно, то внутренним и внешним переменным присваиваются нулевые начальные значения. Остальным переменным будут присвоены неопределенные начальные зна- чения. Следует отметить, что внутренним и внешним переменным начальные значения присваиваются однократно, непосредственно перед выполнением программы, а автоматическим и регистровым переменным начальные значения присваиваются во время каждого описания переменных.
4. Описания 61 Пример static char chr[5]; int var = 5; fun(par) int per; { register int loc = par * par; return loc; } ♦Include <std1o.h> main { auto Int val=fun(var-2)+2; char *ptr=chr+3; static char *ref= "EVA", return 0; { Непосредственно перед выполнением программы каждому элементу внутреннего массива chr будет присвоено значение '\0'. Непосредственно перед выполнением программы внешней переменной var будет присвоено зна- чение 5. Непосредственно после вызова функции fun регистровой переменной loc будет присвоено начальное значение, равное квадрату аргумента. Непосредственно после вызова функции main автоматической переменной ptr будет присвоено значение, указывающее на переменную chr[3]. Непосредственно перед выполнением программы внутренней переменной ref будет присвоено значение адреса, указывающего на строку “EWA“. Начальные значения могут присваиваться не только простым переменным, но также массивам и структурам. В этом случае начальные значения компонент определяются с помощью взятого в фигурные скобки списка выражений, разделенных запятыми. Если компонент блока, которому присваивается начальное значение, также представляет собой блок, то его элементы в свою очередь заключаются в фигурные скобки подсписка. Если такой подсписок определяет значения, дающие начальные величины всех составляющих компонент, то их фигурные скобки могут быть опущены. Эти скобки также могут быть опущены, если подсписок не является полным, но за ним нет ни одного выражения, определяющего начальное значение. Однако требуется, чтобы число начальных значений не превышало числа компонент. Если же оно меньше, то остальным компонентам присваиваются нулевые значения. Пример int vec[ ] = {2,3,4}; float arr[3] [2] = {{1, 2} {3, 4}}; float brr[3] [2] = {1,2,3,4}; short crr[3] [2] = {{1}, {2}, {3}};
62 Часть II. Язык Массив vec описывается по умолчанию (по числу инициирующих зна- чений) как vec[3]. Массив агг состоит из трех элементов, которые являются векторами. Начальные значения 1 и 2 будут присвоены элементам первого из векторов, а начальные значения 3 и 4 - элементам второго вектора. Эле- ментам третьего вектора будут присвоены нулевые значения. Отсутствие в описании массива агг внутренних фигурных скобок не изменило бы смысла описания. Этот факт использован в описании массива Ьгг. Во время инициализации массива сгг начальные значения 1, 2 и 3 будут присвоены первым элементам, составляющим вектор сгг. Остальным элементам будут присвоены нулевые начальные значения. Требуется обратить внимание, что проведенное описание массива сгг аналогично описанию short сгг[3] [2] = {{1, 0}, {2, 0}, {3, 0}}; а отсутствиеs в первоначальном описании внутренних фигурных скобок недопустимо, поскольку привело бы к описанию short сгг[3] [2] = {{1,2}, {3,0}, {0,0}}; эквивалентному short сгг [3] [2] = {{1,2}, 3}; 4.5. Описание 'типов Для описания идентификатора как имени типа необходимо пользоваться обычным описанием с заменой в ней описателя класса ключевым словом typeclef. В этом смысле, например, в описании typedef short Int fixed, * pointer; идентификаторы fixed и pointer могут трактоваться соответственно как ключевые слова, означающие тип (short int) и указывающей тип (short int*). Благодаря этому такое описание, как, например, short int arr [3], *ref; может быть представлено в виде fixed arr[3]; pointer ref; Описание типов не ограничивается простыми переменными. Легко видеть, что если идентификатор complex означает тип структуры typedef struct {float re, Im;} complex; то описание complex arr[3]; эквивалентно описанию struct{float re, im;} arr[3]
4. Описания 63 4.6. Области действия и структура программы В языке Си с каждым объектом (константы, переменные, массивы, структуры, функции, имена типов и метки) связывается область действия - часть программы, в которой этот объект доступен. С любым объектом языка, кроме константы, связывается идентификатор, обозначающий этот объект. В языке действуют правила, согласно которым любому идентификатору, используемому в некоторой точке программы, однозначно соответствует один объект. Эти правила называются правилами - областей действия объектов. Существует связь между областями действий объектов и структурой программы. Область действия зависит от класса памяти и местоположения описания объекта в программе. Так, область действия объекта класса extern выходит за рамки того модуля, где он был описан, и распространяется на всю программу. Пример Программа’ размещена в двух модулях, каждый из которых начинается с комментария / * nodul main * / char chr; main( } { int chr; • return 0; } / * modui fun * I fun( ) { extern char chr; } Переменная с идентификатором chr типа (char) описана в модуле main, a ее областью действия является тело функции fun. Переменная с идентификатором chr типа (int) описана в теле функции (main) модуля main. Областью действия первой из этих переменных является внешняя область модуля main, а именно тело функции fun. Областью действия второй из этих переменных является тело функции main. '
5. Выражения Язык Си в значительной степени является языком выражений. Это следует из того факта, что некоторые операции, например присваивания, не реализуются с помощью операторов, а трактуются как операции с двумя аргументами. Важной особенностью языка является возможность превращения произвольного выражения в оператор. Для этого достаточно закончить выражение точкой с запятой. Такой способ чаще всего используется в отношении операции присваивания, которая в записи, заканчивающейся точкой с запятой, образует оператор, известный из других языков программирования как оператор присваивания. Принципы вычисления значений выражений вытекают из семантики и приоритетов, содержащихся в них операторов, а также из видов использованных аргументов. Если данный аргумент находится в границах более чем одного оператора с одинаковыми приоритетами, то оператбры являются связанными. Большинство операторов связывает аргументы слева направо. Исключения составляют только операторы с одним аргументом и операторы присваивания, а также трехаргументные операторы выбора. Связывание и упорядочивание операторов в очередности уменьшения приоритетов представлены в следующей таблице: Приоритет 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Связывание Оператор Левое Правое Левое ()[]->. ! + + ---+ (typ) * & sizeof * / % Правое Левое I & & I I ? : ^L-=*=/=%=»=«=&=-=:=, Если приоритеты и связывания однозначно не определяют очередности выполнения операции, то она может быть произвольной, а выражения, записанные с помощью действий (*, +, ", I), могут быть преобразованы в эквивалентные математические выражения даже с опусканием скобок. Решение вопросов, связанных с неопределенностями, как, например, деление на нуль, осуществляется в процессе реализации программы. При составлении правильного выражения необходимо определить способ его интерпретации. Это зависит от принятых в языке приоритетов и связей операторов.
5. Выражения 65 Операции, связанные с операторами внешнего, приоритета, выполняются в первую очередь. Если некоторый аргумент операции касается двух операторов одинакового приоритета, то очередность выполнения операции определяется по связыванию. Использование приоритетов и связываний операторов дает возможность во многих случаях убрать из выражений лишние круглые скобки, в частности правильным является выражение а - = Ь = - C++ + d - е. В этом выражении с аргументом “с“ связаны два оператора одинакового приоритета: оператор смены знака (-) и оператор постинкремента (+ +). Поскольку связывание этих операторов правостороннее, рассматриваемое выражение будет трактоваться как а -= b = (-(с++}) + d - е. Поскольку приоритеты операторов сложения (+) и вычитания (-) выше, чем у оператора присваивания (=), можно сделать запись с дополнительной парой скобок а -= b = ((-(c++))+d - е}. Операции сложения (+) и вычитания (-), входящие во внутренние скобки, имеют равный приоритет. Поскольку операторы сложения (+) и вычитания (-) связывают аргументы левосторонне, то рассматриваемое выражение интерпретируется как выражение а -= b = (((-(с++П + d)- е). Операторы присваивания связывают свои аргументы правосторонне; следовательно, окончательный вид первоначального выражения а -= Ь = -с-н- + d - е интерпретируется как (а -= (Ь = (((-(C++}} + d} - е}}}. Примеры i а) Поскольку связывание двухаргументных операторов + (плюс) и - (минус) левостороннее, то выражение а - Ь + с трактуется как выражение (а - Ь} + с, а не как выражение 5—764
66 Часть II. Язык а - (b + с) б) Поскольку связывание трехаргументного оператора ?: (вопросительный знак, двоеточие) правостороннее, то выражение a?b:c?d:e:f трактуется как выражение а ? Ь : (с ? d : е) а не как выражение (а ? Ь : с) ? d : е 5.1. Первичные "выражения и /-выражения Выражения состоят из аргументов и операторов. Операторы делятся на одно-, двух- и трехаргументные, а грамматический разбор выражения приводит к выделению в нем первичных выражений, к которым относятся Идентификатор Литерал • ( Выражение) Первичное выражение [выражение] Первичное выражение (список аргументов) Первичное 1-выражение. Идентификатор Первичное выражение -> идентификатор Использованное в приведенном списке определение 1-выражения касается выражений, представляющих объекты, которым могут быть присвоены значения. Согласно определению языка /-выражениями являются Идентификатор Первичное выражение [выражение] Первичное 1-выражение. Идентификатор Первичное выражение -> идентификатор ♦ выражение (1-выражение ) Выражениями в широком значении являются Первичное выражение * выражение 1-выражение - выражение + выражение ! выражение - выражение ++ 1-выражение — 1-выражение 1-выражение ++
5. Выражения 67 sizeof (имя типа) sizeof выражение (имя типа) выражение Выражение оператор - двухаргументное выражение Выражение ? выражение : выражение В данном определении двухаргументный оператор относится к операторам, выражающим операции Арифметические Сравнения Логические Битовые Переносов Присваивания Связи * / % + - < > < - > - «= - ! - ! && I I & * \ « » = += — ♦_ /= %= »= «= &= - :•= На основе приведенного списка, а также принимая во внимание факт, что лексической единицей признается наибольший ряд знаков, можно сделать вывод о -правильности и значении отдельных выражений, если только известна интерпретация первичных выражений. Идентификатор Идентификатор является первичным выражением, тип которого следует из описания. Если идентификатор представляет собой имя массива, то он является не /-выражением, а выражением указывающего типа, представ- ляющего указатель на первый элемент массива. Аналогично трактуется идентификатор функции, который является не /-выражением, а выражением, указывающим на функции. Пример extern fun( ) 1fun( ) int arr[3]; *ref=arr; ^include <stdio.h> ma1n( ) { Int var «fun (ifun); return 0; } Начальное значение переменной ref есть указатель на первый элемент массива агг. Если имя массива не было отлично от других идентификаторов, то правая сторона присваивания должна была иметь вид &агг[О]. В описании 5*
68 Часть II. Язык int var = fun(ifun) первичное выражение ifun трактуется аналогично arr, т. е. запрещено использование перед ним оператора & (амперсанд!. Арифметический литерал Арифметический литерал является первичным выражением, тип которого следует из записи литерала. Знаковые литералы - это выражения типа (int), а литералы с плавающей точкой без приращений - выражения типа (double). Пример float var = 5 + '\20' + 2е0; Литерал 5 'имеет тип (int). Литерал '\20' имеет тип (int). Литерал 2е0 имеет тип (double). Выражение с правой стороны символа присваивания имеет тип (double) и величину 23. Строчный литерал Строчный литерал является первичным выражением, которое имеет тип (chart ]), но аналогично идентификатору, который представляет имя массива, трактуется как указатель на элемент типа (char). Пример char ewa[ ] = “izunia”, *jan = "Izunia”; Литерал “izunia" при инициализации переменной ewa образует удобное сокращение от { 'i', 'z', 'и', 'п', Т, 'а', '\0'} Литерал “izunia" при инициализации указателя jan дает указание на первый знак строки, состоящей из знаков izunia и знака нуль. (Выражение) - первичное выражение того же типа, что и выражение в скобках. Если выражение, находящееся внутри скобок, - /-выражение, то и рассматриваемое первичное выражение является /-выражением Пример int var— х, var— у *ptr =&var—х; *ref =&var—у; *(ref = ptr?ref:ptr)=5; Выражение в скобках не является первичным. После заключения в скобки оно становится первичным, но не /-выражением.
5. Выражения 69 После использования операции * (звездочка) левая сторона оператора присваивания становится /-выражением. Выполнение приведенного оператора дает в данном случае такой же результат, как выполнение оператора var_x=5; Первичное выражение [выражение ] . эквивалентно по определению выражению * ((первичное выражение + (выражение)) и, следовательно, несмотря на асимметрию, индексация является переменной операцией. Необходимо, чтобы выражение во внутренних круглых скобках было указателем. Л Пример char arr[4] [4]; агг[2] [3] = 'Е'; Выражение оператора присваивания интерпретируется как (arr[2j) [3] = 'Е', а затем как выражение (*(агм-2))[3] ='Е', а также как выражение *(*arr+2)+3) = ’Е'. В каждом из рассмотренных примеров выражение с левой стороны оператора присваивания является /-выражением. Первичное выражение (список аргументов) первичное выражение, представляющее название функции. Первичное выражение в названии должно быть указателем функции, а взятый в круглые скобки список выражений в общем случае может состоять из стольких выражений, сколько параметров насчитывает вызываемая функция. Пример extern fun( ); struct{ int (*sub}( ); char chr; , }str = {fun, 'A’}
70 Часть II. Язык str.chr = (*str.sub) (2+str.chr); В операторе присваивания имеется вызов функции fun с аргументом 2 + str.chr. В этом операторе str.sub представляет поле структуры str, которому могут быть присвоены показатели функции, дающие результаты типа (int), а следовательно, указатель функции fun. Первичное 1-выражение . идентификатор первичное выражение, представляющее поле структуры или объединения. Выражение перед знаком . (точка) должно представлять структуру или объединение, а идентификатор должен быть идентификатором их поля. Пример struct { char chr; int tally; }str = {'j'}; *ptr = &str; putchar((*ptf). chr); В рассматриваемом случае (*ptr) - /-выражение, представляющее структу- ру str, a chr - идентификатор ее поля. Выполнение оператора приводит к выводу знака 'j'. Первичное выражение -> идентификатор первичное выражение, представляющее поле структуры или объединения. Вы- ражение перед символом -> должно представлять указатель структуры или объединения, а идентификатор должен быть идентификатором их поля. По определению принимается, что выражение Указатель -> Поле эквивалентно выражению (* Указатель) . Поле Пример union{ char nam, fam; }var, *ptr = &var; ptr -> nam = 'j'; ptr -> fam = 'b'; Выражение ptr->nam представляет поле nam объединения var. После вы- полнения второго присвоения данное, находящееся в поле, имеет величину Ь’.
5. Выражения 71 5.2. Арифметические операторы Арифметические операторы служат для описания арифметических дейст- вий: замена знака (-), инкремент (++), декремент сложение (+), вычи- тание (-), умножение (*), деление (/), а также обозначение остатка от де- ления (%). Операторы замены знака, инкремента и декремента - одноаргу- ментные (унарные), а их использование приводит соответственно к смене знака аргумента, добавлению к аргументу числа 1 и вычитанию из аргумен- та числа 1. Если аргумент операции замены знака имеет тип (unsigned), то замена знака реализуется путем вычитания аргумента из числа 2П, где п - число битов, используемых для представления данных типа (int). Инкремент и декремент означают соответственно увеличение и уменьшение на 1 и от- носятся только к аргументу, имеющему вид /-выражения. В случае преин- кремента + + arg результатом операции является arg + 1. В случае постин- кремента arg + + результатом операции является arg. Аналогичные правила относятся к декременту. Примеры а) Замена знака int var_i=-536; . unsigned int var_u = -536; Переменной var_i будет присвоено начальное значение -536. Переменной var_u будет присвоено начальное значение 65000. б) Инкремент и декремент арифметических переменных finclude <stdio.h> int a,b: main( ) { a=3; b=6; printf("%d", ++a+b—); printf(”%d", a); printf("%d”, b); return 0; } » Результатом операции ++a будет величина 4, а операции b— - величина 6. После выполнения программы будут выведены числа 10, 4 и 5. Если первому вызову функции printf придать вид printf(”%d", а++ + --Ь); то в результате выполнения программы будут выведены числа 8, 4 и 5. Остальные арифметические операторы являются двухаргументными и служат для выполнения основных арифметических действий, а также для обозначения остатка от деления первого аргумента на второй. Деление целых аргументов приводит к отбрасыванию дробной части результата. В случае обозначения остатка от деления знак результата совпадает со знаком делимого и, кроме этого, справедливо равенство
72 Часть II. Язык (а/Ь) * Ь+ а%Ь=а Важной особенностью языка является возможность выполнения арифметических действий (сложения и вычитания, в особенности инкремента и декремента) с указателями. В общем случае эти действия имеют смысл тогда, когда указатели относятся к одному и тому же массиву. Если величиной ref является указатель на l-й элемент некоторого массива, а и - целое арифметическое выражение, то значением суммы ref + п является указатель на (Z + п)-й элемент этого массива. Если ref и ptr - соответственно указатели на элементы I и j некоторого массива, то величина разности ref - ptr равна разности I - f Следует отметить, что вычитание указателей является правильным действием, а сложение не имеет смысла, когда отсутствуют данные типа сумма указателей. С этой точки зрения, если необходимо, например, найти указатель на элемент массива, равноудаленный от элементов, представленных указателями ref и ptr, то вместо неправильного выражения (ptr + ref) /2 надо воспользоваться выражением (ptr - ref)/2 + ref (естественно, такой элемент будет равноудаленным только в том случае, когда разница в скобках четная). Примеры а) Инкремент и декремент указателей int агг[2] = {2, 3}, * ref = агг; — *ref ++; Приведенный оператор эквивалентен оператору — (*ref++ )); Его можно также представить в виде пары операторов —(*ref); ref++;
5. Выражения 73 б) Двухаргументные операции над указателями float err[8J; *ptr = агг+3; fun(*ptr+2)); Если переменная ptr сохранила присвоенное ей начальное значение, то рассматриваемый оператор выполняется как оператор fun(arr[5]); При тех же условиях выражение агг - ptr (в котором агг представляет указатель на элемент arr[O], a ptr - указатель на элемент arr[3J) имеет значение -3. в) Операции с двумерными массивами ~~ float arr[2] [2] = { {1, 2}, {3,4}}, (*ref)[2] = arr, var; var » (*++ref)[0]; , Переменная ref описывается как указатель на первый элемент массива (*ref)2 типа (float). Начальным значением, присвоенным переменной ref,, является указатель на первый из двух векторов массива агг [2]. Оператор присваивания может быть представлен посредством двух операторов ++ref; var » (*ref)[0]; В данном случае второй из этих операторов может быть заменен оператором var •= arr[l] [0]; и, следовательно, оператором var = 3; 5.3. Операторы сравнения Операторы сравнения служат для описания следующих действий: равно (==), не равно (!=), меньше (<), больше (>), меньше или равно (<=) и больше или равно (>=). Результатом сравнения является данное типа (int), принимающее значение 0 при невыполнении условия сравнения (условие ложно) и значение 1 при выполнении условия (условие истинно). Если какой-либо из аргументов сравнения имеет тип (char), то он подвергается преобразованию в тип (int). Если какой-либо из аргументов является
74 Часть II. Язык указателем, то он может сравниваться только с аргументом того же типа, указывающим на некоторый элемент того же массива или с литералом О, представляющим пустой указатель. Примеры а) Сравнение арифметических величин float var_ s “ 1.0/3.0; double var_ d “1.0/3.0; short int flag =var_d> var_ s; В случаях когда величины типа (float) и (double) имеют разные представления (как это, в частности, имеет место в Турбо Си), переменной flag будет присвоено начальное значение 1. В остальных случаях этой переменной будет присвоено начальное значение 0. б) Сравнение указателей float arr[4]; *ptr =arr+2; short int flag = ptr l=arr; Переменной flag будет присвоено начальное значение 1. 5.4. Логические операторы Логические операторы служат для описания следующих действий: инверсии (1), конъюнкции (&&) и дизъюнкции ("). Первый из перечисленных операторов является одноаргументным, а два других - двухаргументными. Во всех случаях результатом операции будет данное типа (int) со значением 0 (нуль) или 1 (истина). Результатом инверсии будет значение 1, если аргумент имел значение 0, и наоборот. Результатом конъюнкции будет 1, если аргументы имеют значения, отличные от нуля, или 0 в противном случае. Результатом дизъюнкции является значение 1, если по крайней мере один из аргументов отличен от нуля, или 0 в противном случае. Следующий список дает определение логических операторов: Операция Определение ! а а == 0 а && b (!!а) + (!!b) > 1 а 11 b (!!а) + (!!Ь) > 0 В отличие от рассмотренных двухаргументных операций, операции конъюнкции и дизъюнкции всегда выполняются в очередности слева направо, значение аргумента b обозначается только тогда, когда на основе значения аргумента а нельзя сделать вывод о результате операции. В отношении оператора конъюнкции можно показать, что оператор if(expl)if(exp2)Ins
5. Выражения 75 по существу выполняется как команда. Пример int var, * ptr; var 1 I ptr = var ! ] var== 0; Приведенный оператор интерпретируется как оператор var = ((( I ptr == var) !! (var ==0)); 5.5. Битовые операторы Битовые операторы служат для описания следующих действий: отрицания битов (~), конъюнкции битов (&), “Исключающие ИЛИ" (*) и дизъюнкции (I). Первый из перечисленных операторов - одноаргументный, а остальные - двухаргументные. Аргументы битовых операций могут быть только целого типа. Результат операции также имеет целый тип. Результатом отрицания битов является единичное дополнение аргумента. Результаты конъюнкции “Исключающее ИЛИ“ и дизъюнкции получаются путем поразрядных (побитовых) действий. Результатом конъюнкции является логическое произведение битов, результатом “Исключающего ИЛИ" - сумма битов по модулю два, а результатом дизъюнкции - логическая сумма битов. Примеры а) Отрицание битов int а = -2; /* 1111...1110*/ int res = _а; /* 0000...0001*/ Переменной res присваивается начальное значение 1. б) Конъюнкция битов int а = 10 /* 0000...1010 */ b = 12; /* 0000...1100 */ int res = a&b; /* 0000...1000 */ Переменной res присваивается начальное значение 8. в) Исключающее ИЛИ int а = 10 /* 0000 ... 1010 */ b = 12; /* 0000 ... 1100 */ int res = a"b /* 0000 ... ОНО */ Переменной res присваивается начальное значение 6. г) Дизъюнкция битов int а = 10 /* 0000... 1010 */
76 Часть II. Язык b = 12; Int res = a|b; /* 0000... 1100 */ /* 0000... 1110 */ Переменной res присваивается начальное значение 14. 5.6. Операторы сдвига Операторы сдвига служат для сдвига битов аргумента влево (<<) или вправо (») на заданное число позиций. Операции сдвига являются двухаргументными. Левый аргумент подлежит сдвигу, а правый - определяет число битов, на которое необходимо произвести сдвиг. Оба аргумента должны быть целыми, а результат - того же типа, что и левый аргумент. Результат операции неопределен, если правый аргумент имеет отрицательную величину или превышает число битов, представляющих левый аргумент. Во время сдвига влево происходит дополнение нулями справа. Во время сдвига» вправо дополнение нулями происходит только в том случае, когда левый аргумент- имеет тип (unsigned). В остальных случаях определение того, какого вида сдвиг: логический (дополнение нулями) или арифметический (дополнение битом знака), производит система. Внимание: В Турбо Си используется арифметический сдвиг! Примеры (данные типа (int) занимают по 2 байта). а) Сдвиг вправо unsigned а = -2; /* 1111 ... 1110 */ int res = а » 14; /* 000 ... ООН */ Переменной res будет присвоено начальное значение 3. б) Сдвиг влево int а = -2; int res = а « -а; /* 1111 /* 1111 1110 */ 1000 */ Переменной res будет присвоено начальное значение -8. 5.7. Операторы "доступа по указателю (*)" и "указатель (&)" Операторы (*) и (&) являются одноаргументными. Первый из них относится только к указателям, а второй - только к 1-выражениям. Результатом операции (*) является значение переменной, на которую ссылается данный указатель, результатом операции является получение указателя на некоторый объект (адрес объекта). Примеры а) Использование оператора (*) finclude «stdio.h» main( ) {
5. Выражения 77 static char arr[ ] = "123456789", *ptr = "123456789"; printf("%d", *(arr +2)); printf("%d", *ptr); return 0; } Запись arr+2 представляет собой указатель на элемент агг [2] массива агг. Запись *(агг+2) представляет собой само значение этого элемента. Запись ptr указывает на первый элемент массива, состоящего из знаков 'Г, '2', '3' и т.д. Запись *ptr предствляет данное 'Г. Выполнение программы приводит к выводу кодов знаков '3' и 'Г. Кодами ASCII этих знаков являются числа 51 и 49. б) Использование оператора (&) int arr[2] [2]; В приведенном описании выражение **агг эквивалентно выражению агг[0] [0] и представляет элемент агг [0] [0] массива агг. В отличие от правильного выражения **агг выражение &*агг неправильное. Это следует из того, что запись *агг представляет собой массив, а следовательно, не является 1-выражением и не может быть аргументом оператора <&). 5.8. Операторы присваивания \ Оператор присваивания может быть простым и составным. Простая опера- ция присваивания - двухаргументная операция вида Левая сторона = Правая сторона / В этой операции левая сторона является /-выражением, а правая - выра- жением, которое всегда преобразуется к типу левой стороны. Выполнение операции присваивания приводит к присваиванию представленной /-выраже- нием переменной значения выражения, стоящего с правой стороны оператора присваивания. Результатом операции является значение правой стороны опе- ратора. Если при выполнении операции присваивания необходимо преобразо- вание данных, то вступают в силу следующие правила: - преобразование короткого целого данного со знаком в длинное целое данное со знаком (например, signed char в long int) производится с сохранением значения данного; - преобразование короткого целого данного со знаком в длинное целое данное без знака (например, signed char в unsigned long) производится в два этапа - сначала в длинное целое (long int), а затем в длинное целое без знака; - преобразование короткого целого данного без знака в длинное целое (например, unsigned char в long int) производится с сохранением значения данного; - преобразование целого длинного данного в целое короткое производится с потерей не помещающихся старших битов; - преобразование данного с плавающей точкой (например, float) в данное целого типа (например, unsigned char) производится с сохранением значения,
78 Часть II. Язык но после отбрасывания дробной части; - преобразование одного данного с плавающей точкой в другое данное этого же типа производится с сохранением значения; - преобразование одного указателя в другое данное этого же типа возможно только тогда, когда одно из данных имеет тип (void*) или когда преобразуемым данным является пустой указатель; в остальных случаях следует применять явные операторы преобразования. Примеры При следующих описаниях: int i; short si; unsigned ui; float f; » double d; список операторов присваивания эквивалентен списку i = -12,9; i = -12; f = 5; f = 5,0; si = '\377'; si = -1; si = ’\177'; si = 127; ui = -32768; ui « 32768; Кроме простого присваивания, в языке Си есть составные присваивания вида el ор= е2; в которых el и е2 - выражения, а ор= - один из следующих двухаргументных операторов: += — *= /= %= »= «= &= *= |= Составное присваивание выполняется как присваивание (el)=((el) op (е2)). Оператор arr[var++] +=2; реализуется не как оператор arr[var++] = arr[var++] + 2, а как пара операторов arr[var] += 2; var++;
5. Выражения 79 (принимается, что var - имя простой переменной, а агг - имя массива). Пример float arr[4]' *ptr=arr +2; *ref; ref “ ptr +» 1; Приведенный оператор присваивания эквивалентен оператору ref “ (ptr “ ptr+1); В результате выполнения этого оператора переменной ref, как и переменной ptr, будет присвоен указатель на последний элемент массива агг. 5.9. Условный оператор Условная операция выражается с помощью трехаргументного оператора ? : и в общем случае имеет вид el 1 е2 : еЗ; где el, е2 и еЗ - выражения. Выполнение условной операции начинается с анализа значения выражения el. Если оно отлично от нуля (условие истинно), то вычисляется выражение е2. Если значение условия (выражение el) равно нулю (ложно), то в качестве значения всего условного выражения вычисляется третье выражение (еЗ). Всегда вычисляется только одно из двух выражений, разделенных двоеточием. Поскольку результат условной операции не представляет собой I- выражение, то условная операция не может выступать в качестве левой стороны оператора присваивания. Пример int a.b.c, var; var = a>b ? (а>с ? а : c):(b>c? Ь:с); Переменной var будет присвоено значение mox(a,b,c). 5.10. Оператор соединения Операция соединения выражается с помощью двухаргументного оператора соединения и в общем случае имеет вид el, е2, где el и е2 - выражения.
80 Часть II. Язык Сначала вычисляется левое выражение, затем правое. Результатом операции соединения являются значения и тип второго (правого) выражения е2. В контексте, где лексическая единица (.запятая) имеет специальное значение, как, например, в списке аргументов обращения к функциям, она не принимается в качестве оператора соединения. Пример int a.b.c.d; extern fun( ); fun(2+ (a == b?b : -a),(c = 5, d + 2)); В приведенном операторе имеет место выход функции fun с двумя аргументами. Если во время выполнения оператора, например, выражение а == b истинно, то результат выполнения равен выполнению двух операторов с = 5; fun(2 + b, d + 2); Если в рассматриваемом операторе были опущены внутренние круглые скобки, т. е. если вызов имел вид fun(2 + а == b 1 b : -а, с = 5, d + 2); то была вызвана функция трех аргументов. Если во время выполнения такого оператора выполнялось действие а == Ь, то результат равнялся выполнению двух операторов с = 5; fun(-a, 5, d + 2); 5.11. Оператор преобразования Операция преобразования выражается с помощью одноаргументного оператора преобразования и в общем случае имеет вид ( type)exp, где type - имя типа, а ехр - выражение. Результатом преобразования явля- ется значение выражения ехр, преобразованное к типу (type). Примеры. Обозначения типов Имя типа Интерпретация int int [4 ] int * int *[4] int (*)[4] int *( ) Тип (int) Массив переменных типа (int) Указатель типа (int) Массив указателей типа (int) Указатель на массив переменных типа (int) Функция, результатом которой является указатель
5. Выражения 81 int (*)( ) int *(*)() типа (int) Указатель на функцию, результатом которой является переменная типа (int) Указатель на функцию, результатом которой являются данные типа (int) Способ интерпретации некоторых преобразований, касающихся указателей, зависит ' от реализации системы. Определение языка гарантирует выполнимость преобразования указателя в целую величину соответствующего размера (например, long int). Целое данное также может быть подвергнуто преобразованию в указатель. Всегда можно преобразовать указатель некоторого объекта, имеющий особые требования к способу размещения в памяти, в указатель объекта с меньшими требованиями к размеру памяти. В этом случае выполнимо также обратное преобразование, которое восстанавливает первоначальный указатель. Имея в виду эти принципы, можно, в частности, определить функцию set(adr, chr) unsigned adr; char chr; { *(char *)adr = chr; } которая, получая адрес байта в памяти и символ, помещает байте. Например, выполнение инструкции set(200, 'j'); приводит к размещению символа 'j' в байте с адресом 200. каждой системе такое действие будет правильным. В Турбо действие справедливо. его в данном Однако не в Си указанное Примеры а) Преобразование аргумента в вызове функции int fun(par) float par; { return par+2; } auto var =fun((float)5); Переменной var будет присвоено значение 7. Отсутствие оператора преобразования перед литералом 5 привело бы к совпадению с параметром типа (float) аргумента типа (int), что недопустимо. б) Преобразование указателей int агг[ ] [2] = {1,2,3,4}; float var: 6—764
82 Часть II. Язык var - -*(int *) arr; Переменной var будет присвоено значение -1,0. Массив агг имеет тип (int [2] [2], а идентификатор агг в выражении представляет данное типа (int (*) [2]). Отсутствие оператора преобразования недопустимо, поскольку это превращает оператор присваивания в ошибочный. var » -&arr[0] [0]; 5.12. Оператор размера В общем случае одноаргументный оператор размера sizeof имеет вид $ sizeof exp или sizeof (type), где exp - выражение, a type - имя типа. Результатом оператора размера является значение, которое определяет размер аргумента. Байт - это единица размера данных типа (char). Примеры а) Определение размера массива int arr[4], var = sizeof arr; Если переменная типа (int) представлена в 2 байт каждая, то весь массив агг занимает 8 байт, и таким образом переменной var будет присвоено значение 8. б) Определение размера данных принятого типа int var = sizeof(char) + 5; Переменной var будет присвоено значение 6. Поскольку оператор sizeof(char) имеет более высокий приоритет, чем оператор + (плюс), то приведенное описание не интерпретируется как int var = sizeof((char)+5); 5.13. Обработка выражений Принципы обработки выражений следуют из приоритетов и связей опе- раторов, а также из семантики отдельных операций. Необходимо принимать во внимание неявные преобразования, которые могут возникнуть при - выполнении операций с указателями, например
5. Выражения 83 int arr[4], *ptr = агг; ptr++; - выполнении присваиваний, например float f - 12.4; int i; 1 = f; - обозначении аргументов в обращениях к функции, а также приведении в соответствие с типом функции в операторе return; - выполнении арифметических операций с данными различных типов, например putchar(*(”Jan ” +2)); Поскольку преобразования в двух первых случаях уже были рассмотрены, а преобразования, связанные с вызовами функции, будут рассматриваться особо, рассмотрим преобразования при выполнении арифметических операций. Эти преобразования, называемые типовыми арифметическими преобразовани- ями, выполняются в два шага: - сначала аргументы типов (char), (unsigned char), (signed char) и (short) преобразуются в тип (int), а аргументы типа (float) - в тип (double); - затем выполняются преобразования, определяемые списком Тип аргумента Преобразование второго аргумента (long double) (double) (unsigned long) (long) (unsigned int) (long double) (double) (unsigned long) (long) (unsigned int) Внимание: Принятые в Турбо Си принципы выполнения приведенных ти- повых арифметических преобразований несколько отличаются от принципа стандарта ANSI. В частности, преобразование аргумента типа (float) к типу (double) выполняется не всегда, а только тогда, когда второй аргумент выра- жения имеет тип (double). Однако в стандартных программах эта разница не имеет существенного значения. Примеры а) Использование связей struct { int *val; int (*ptr)[2J; } str[4], *ref; *str[3J. val = *ref -> ptr ++ 11 *(ref++)-> ptr;
, 84 Часть II. Язык Приведенный оператор трактуется как *((str[3]). val)- ((*((ref-> ptr)++))|| (*((ref ++)-> ptr))); Обработка выражения левого аргумента дизъюнкции приводит к инкре- менту переменной ref-> ptr после предварительного доступа к величине, указанной этой переменной. Обработка выражения правого аргумента дизъюнкции приводит к инкре- менту переменной ref после предварительного доступа к величине, указанной посредством ref -> ptr. б) Использование преобразования char Chr; short Int Sttf; unsigned Uns; double Dbl; extern fun ( ); fun(12.4, (Chr + Sh<)* Uns - Dbl /12L); Требуемые преобразования могут быть выражены явно с помощью операторов преобразования fun((double)12.4, (double)(unsigned)((int)Chr+(int)Shi)* Uns) - Dbl / (double)12L); Как можно убедиться, аргументы суммы будут преобразованы к данным типа (int), а поскольку правый аргумент умножения имеет тип (unsigned int), то сумма будет преобразована к данному типу (unsigned int). Поскольку левый аргумент операции деления имеет тип (double), то правый аргумент будет преобразован к данному типа (double). В операции вычитания вычитаемое преобразуется к величине типа (double). Если принять, что последним выполняемым оператором функции fun является return par; где par имеет тип (double), то, поскольку подразумеваемым типом результата функции является (int), этот оператор будет выполнен как return (int)par; Поскольку вызову функции придан вид оператора выражения, это данное во внимание не принимается.
6. Операторы Операторы делятся на три группы: - операторы-выражения; - пустые операторы и блоки; - операторы, начинающиеся с ключевого слова. Перед каждым оператором может находиться метка или список меток. Метка от оператора или от другой метки отделяется знаком (двоеточие). Поскольку использование метки связано с применением оператора перехода (goto), которого из-за ухудшения читаемости программы следует избегать, в приведенных ниже примерах метки не будут обозначены. Примеры а) Оператор-выражение а = Ь + 2, Ь = с - 3; б) Оператор, начинающийся с ключевого слова if(а = Ь) а = 2; else b 2; 6.1. Оператор-выражение Оператор-выражение имеет вид Ехр; где Ехр - произвольное выражение. Выполнение оператора-выражения состоит в вычислении выражения с последующим отбрасыванием вычисленного значения. Частным случаем оператора-выражения является оператор присваивания, например а - Ь = 13; Пример 2 + (Count = printf("Hello”): Выполнение оператора-выражения приводит к вычислению величины 7 и к ее отбрасыванию. Побочным результатом выполнения приведенного оператора является вывод надписи “Не11о“ и присвоение переменной Count величины 5 (равной числу выведенных знаков).
86 Часть IL Язык 6.2. Пустой оператор Этот оператор имеет вид а его выполнение ие дает никаких результатов. Пустой оператор удобно использовать для помещения метки в конец блока при использовании его вместо более сложного оператора, который будет добавлен в программу в будущем и т.д. Пример Простая программа ma1n( ) { * return 0; } 6.3. Оператор перехода Этот оператор имеет вид goto Lab', где Lab - метка оператора. Поскольку диапазоном действия метки является функция, в которой описана данная метка, выполнение оператора не приведет к переходу вне границ функции. Пример main( ) { {goto Lab;} lab:; return 0; } Выполнение оператора goto приводит к переходу на выполнение пустой инструкции, обозначенной меткой lab. 6.4. Блок Блок - составной оператор, имеющий вид {Decl Ins} Операторы, содержащиеся в списке, выполняются один раз в порядке их следования по списку. При обработке списка описания блоков происходит описание переменных класса auto и register, а также присваивание им начальных значений с помощью инициирующих выражений. Во время выполнения операторов блока до момента завершения переменные,
6- Операторы 87 описанные вне данного блока, с идентичными именами будут недоступны в этом блоке. Пример finclude <stdio.h> main( ) { char Chr = ’j’; { char Chr = 'b'; putchar(Chr); } putchar(Chr); return 0; } Тело функции main состоит из блока, в котором находятся описание переменной Chr, внутренний блок, вызов функции putchar(Chr) и оператор return. Во внутреннем блоке описывается переменная Chr, и далее выполняется функция putchar(Chr); Во время обработки описания происходит создание автоматической переменной Chr и присвоение ей начального значения 'Ь'. Поскольку имя Chr, использованное во внутреннем блоке, совпадает с именем Chr, описанным в блоке, охватывающем этот блок, то переменная Chr недоступна в пределах внутреннего блока. После выполнения действий во внутреннем блоке происходит удаление локальной переменной и открывается доступ к переменной, описанной во внешнем блоке. В результате выполнения про- граммы выводится надпись bj. 6.5. Условный оператор Этот оператор имеет вид (I форма) if(£xp) /nsp else Ins^ или (II форма) If(jExp) /и$р что эквивалентно оператору if(£xp) /И5р else
88 Часть II. Язык Выражение в скобках Ехр является условием. Если его значение не равно нулю (истинно), то выполняется оператор Insp, в противном случае если условие равно нулю (ложно), то выполняется оператор Insj (для I формы оператора) или выполняется очередной оператор программы (для II формы). Если условный оператор содержит другие условные операторы, то слово else соотносится с ближайшим ключевым словом if, еще не связанным ни с каким ключевым словом else. Пример if(а == b), if(а = О) b • 2; else а = 2, Приведенный условный оператор выполняется не как оператор if (a == b), { if(а == 0) Ь = 2; } else а = 2; а как оператор if(а == Ь). { if (а — 0) Ь = 2; else а = 2 } 6.6.. Операторы цикла Существуют три формы оператора цикла. Их семантика выражается с помощью условных операторов и операторов перехода. Оператор while Этот оператор имеет вид while (Ехр) Ins где Ехр - выражение, a Ins - оператор и выполняется как Lab: if(Exp){ Ins goto Lab; } Иначе говоря, оператор Ins выполняется до тех пор, пока условие, определяемое выражением Ехр, отлично от нуля, т. е. истинно. Вычисление значения выражения производится перед каждым выполнением оператора.
б. Операторы 89 Пример char *Ptr = "Jan Bielecki"; #iinclude <stdio.h> main( ) { while(*Ptr) printf(”%c, *Ptr++); return 0; } Выполнение программы приводит к выводу надписи Jan Bielecki. Оператор do Этот оператор имеет вид do Ins while (Ехр)-, где Ins - оператор, а Ехр - выражение. Он выполняется следующим образом: Lair. Ins if (Ехр) goto Lab; Оператор Ins выполняется до тех пор, пока выражение Ехр истинно. Выражение и анализ значения выражения производятся после каждого выполнения оператора. Пример char *Ptr = "Jan Bielecki" # i nc1ude<std io. h> main( ) { do printf("%c",*Ptr); while(*++Ptr); return 0; } Выполнение программы приводит к вводу надписи Jan Bielecki. Оператор for Этот оператор имеет вид forCExpa; Ехр; Ехрь) Ins
90 Часть II. Язык где Ехр - выражение, a Ins - оператор. Он выполняется следующим образом: Ехра: Lab: if (Ехр) Ins Expb: goto Lab: } Иначе говоря, непосредственно после выполнения оператора-выражения Ехра выполняются оператор Ins и оператор-выражение Ехрь до тех пор, пока выражение Ехр истинно. Любое из перечисленных выражений может быть пустым. Если среднее выражение Ехр отсутствует, то по умолчанию оно принимается равным 1, считается всегда истинным, и в этом случае цикл считаете# бесконечным. Пример char *Ptr; finclude «stdio.h» main( ) { for(Ptr = "Jan Bielecki”, *Ptr;) printf(”%c”, *Ptr ++); return 0; } Выполнение программы приводит к выводу надписи Jan Bielecki. 6.7. Оператор продолжения Этот оператор имеет вид continue; и может находиться в пределах оператора цикла. Выполнение этого оператора приводит к продолжению выполнения непосредственно после этого оператора следующего шага цикла. Это означает, что в каждом из последующих примеров выполнение оператора continue в месте, обозначенном комментарием, могло бы быть заменено выполнением оператора goto Next: Оператор while while( ...){ /* ... */ Next:; }
6. Операторы 91 Оператор do do { /* ... */ Next-.; } while( ... ); Оператор for for(... ){ /* ... */ Next-.-, } Использование оператора продолжения в программах должно быть сведено к минимуму, чтобы не усложнять их. 6.8. Оператор завершения Этот оператор имеет вид break; и может находиться в пределах оператора цикла или условного оператора. Оператор завершения вызывает преждевременное завершение выполнения охватывающего оператора (цикла или условного) и переход на выполнение следующего за ним оператора. Пример char *Ptr = "Jan Bielecki"; finclude «stdio.h» main( ) { for(;;){ printf("Xc", *Ptr); if(!*++Ptr) break; } return 0; } Выполнение программы приводит к выводу надписи Jan Bielecki. 6.9. Оператор-переключатель Этот оператор имеет вид switch(-Exp) Ins где Ехр - выражение типа (int, char, short int, insigned int), a J ns
92 Часть П. Язык произвольный оператор (чаще всего блок). В рамках оператора-переключателя могут находиться приставки вида case сЕхр: а также по крайней мере одна приставка вида default: Постоянные выражения сЕхр в приставках должны иметь такой же тип, как и выражение Ехр. Выполнение оператора-переключателя начинается с вычисления значения выражения сЕхр. Это значение в дальнейшем последовательно сравнивается со значениями выражений Ехр. В случае равенства выполняется оператор, стоящий после приставки case, а также все последующие за ним операторы вплоть до выполнения оператора goto или break или До выполнения последнего оператора в пределах оператора- переключателя. В противном случае выполняется оператор после приставки default: и возможно - последующий оператор. Если равенство не выполняется и ие используется приставка default:, то выполнение оператора- переключателя считается законным. Примеры а) Оператор-переключатель, содержащий блок char *ptr = "janb", chr; whilefchr = *ptr ++) switch (chr){ case 'j': case 'a': putchar ('e'); default: putchar ('w'); break; case 'n': putchar (’a’): } Выполнение оператора while приводит к выводу надписи ewewaw. В частности, если chr — 'j', то будут выданы символы ew, а при chr == 'b' - символ w. б) Лары эквивалентных операторов-переключателей, не содержащих блоки: switch (chr) case ’j' putchar(chr); if(chr == ’j') putchar(chr) switch (chr) default: putchar(chr); putchar(chr) switch (chr) switch (chr) putchar(chr)
6. Операторы 93 6.10. Оператор возврата Этот оператор имеет вид return или return Ехр\ где Ехр - выражение. Выполнение оператора возврата приводит к окончанию выполнения содержащей ее функции. Если оператор возврата включает выражение Ехр, то значение этого выражения является результатом функции. Требуется, чтобы этим не были массив, структура, объединение или функция. Пример char *Ptr = "Jan Bielecki”; /include «stdio.h» main( ) { while(l){ printf(“Xc”, *Ptr++); if(!Ptr[OJ) return 0; } } Выполнение программы приводит к выводу надписи Jan Bielecki.
7. Функции Функции являются внешними объектами. Так же как для переменных, описания функций могут иметь характер описания или обращения. Вложенность функций запрещается. В общем случае описание функции состоит из заголовка и тела функции. Заголовок соДЁржит описание типа результата функции и имя функции, а также взятый в круглые скобки список параметров функции и описание их типов. Типом результата функции по умолчанию является (int), а классом функции - extern. Описание параметров необходимо только тогда, когда параметры имеют тип, отличный от (int). Явно заданным классом параметров может быть только класс регистровых переменных, несмотря на то что параметры по существу трактуются как простые переменные класса auto. Описания параметров должны находиться либо в списках параметров функции, либо после него. В первом случае они образуют список, последним элементом которого может быть параметр “...“ (многоточие). Использование этого параметра делает возможным описание функций, которые могут быть вызваны с переменным числом аргументов. Внимание: Такие описания используются в расширенном языке Си и Турбо Си. Они рекомендуются к употреблению, поскольку обеспечивают контроль параметров функции посредством компилятора. Примеры а) Классическое описание float Sum (a,b,c) int а; char Ь, с; { return а + Ь + с; } б) Описание в списке параметров float Sum (int a, char b, char c) { return a + b + c; }
7. Функции 95 в) Описание обращений к функциям extern float Sum( ); extern float Sum (int a, char b, char c); Функции класса static Функции класса static доступны через свои идентификаторы только в рамках модулей, где имеются их описания. Остальные функции доступны из всех модулей, но если только они в них описаны. Примеры а) Функция класса static static double sqr(par) int par; { return par *par; } Функция sqr возвращает квадрат аргумента. Аргумент функции - целое число типа (int), а результат - типа (double). б) Функция, доступная через выражение, не являющееся идентификатором fun(name, val) int (*name) ( ); { return (*name) (val + 3); } int abs(par) { return par < 0? - par: par; } finclude «stdio.h» main( ) { int var = fun(abs, -5); printf("Xd", var); return 0; } Программа состоит из трех функций: fun, abs и main. При иницйализации переменной var происходит вызов функции fun с аргументами abs и -5. Поскольку abs трактуется, как указатель на функцию, входящую в оператор return, выражение name представляет в рассматриваемом контексте выход функции abs. В результате выполнения программы будет выведено число 2.
96 Часть II. Язык в) Составной вызов функции Int var « ( *(*(*name) [1][j])(a,b))(c); С правой стороны оператора присваивания находится вызов функции, где name является указателем на двумерный массив, элементами которого являются указатели на функции с двумя параметрами i и j, результатом которой .является в свою очередь ссылка (указатель) иа функцию с параметром с. 7.1. Связь параметров с аргументами Некоторым неудобством языка Си является то, что связь параметра с ар- гументом может быть осуществлена только по значению. Это приводит к то- му, что для связи через переменную требуется передавать параметры в фун- кции в виде Указателей на переменные. Приведенная ниже программа иллю- стрирует различие между связью через переменные и связью через значения. ftnclude <stdio.h> main( ) { char Alfa = 'e'. Beta - "b'; fun (&Alfa, Beta); printf("XcXc”, Alfa, Beta); return 0; } fun (Ref, Vai) char *Ref, Vai; { *Ref = ’j’; Vai = '*; } В функции fun имеются две различные переменные Ref и Vai. В прологе функции переменной Ref приписывается значение Alfa, а переменной Vai - значение Beta. Обе переменные представляют собой переменные функции fun. Выполнение операции *Ref = 'j' позволяет переменной Alfa приписать значение 'j', а выполнение операции Vai = - переменной Vai - значение Следовательно, последняя операция не затрагивает переменной Beta. Впоследствии выполнение программы приводит к выводу надписи jb. Сущность связи через значения заключается в том, что с каждым параметром функции fun связана локальная переменная, которой в момент вызова приписывается начальное значение. При условии правильности выбора начального значения тип параметра соответствует аргументу функции. Однако такое соответствие - лишь испытание неявного преобразования аргументов и параметров. Неявное преобразование зависит от местонахождения вызова функции. Если вызов функции находится в пределах описания, то каждый аргумент' поддается преобразованию к типу параметра (следующего из описания). После этого другие преобразования аргументов уже ие проводятся. Это означает, что две следующие программы эквивалентны:
7. Функции 97 Вызов в пределах описания ♦include <stdio.h> ma1n( ) { void fun(double Arg);. fun(3) return 0; } void fun(double Par) { ' . printfC'Xe", Рат); } Вызов без использования описания ♦include <stdio.h> main( ) { void fun( ); fun((double) 3); return 0; void fun(double Par) { printf("Xe”, Par; } Если вызов находится вне пределов описания, то проводятся только неявные преобразования аргументов. Они основаны на том, что аргументы типа (char) и (short int) неявно заменяются аргументами типа (int) а аргументы типа (float) - аргументами типа (double). Это означает, что две следующие программы эквивалентны: Программа с неявными преобразованиями аргументов ♦Include «stdio.h'* main( ) { char Chr = ’j’, fun(Chr, 45.0); return 0; } fun (Letter, Number) int Letter; double Number; { printfC'Xe = X Of”, Letter, Number); } 7—764
98 Часть II. Язык Программа с явными преобразованиями аргументов #inchide <stdio.h> main( ) char Chr - 'j' fun (int) Chr, (double) 45.0); { fun (Letter, Number) int Letter; double Number; { printf (“ % c = % Of“, Letter, Number); } Если параметр функции описан как массив, то он трактуется как объект того же типц, что и элементы этого массива. Следовательно, две следующие программы эквивалентны: Программа с параметрами в виде массивов ^include <stdto.h> char Letters [2][3] = {{'j',}, {'b'}}; main( ){ fun(Letters); return 0; } fun(Arr) char Arr [2][3]; { prtntfC'XcXc”, Arr[0][0], (Arr + 1) [0][0]; } Программа с параметрами в виде указателей ftnclude <stdto.h> char Letters [2][3] = {{'j’,}. {'b'}}; matn( ) { fun(Letters); return 0; } fun(Ptr) char(*Ptr)[3]; { prtntfC'XcXc", Ptr[O][O], (Ptr + 1) [0][0]; } Если параметр описывается как переменная типа (char) или (short int), то он трактуется по типу (int), а если описывается как переменная типа (float), то - по типу (double). Каждому из преобразованных параметров присваивается начальное значение, полученное от преобразований (в случае необходимости) аргумента к первоначальному типу параметра. Это означает, что две следующие программы эквивалентны:
7. Функции 99 Неявное преобразование параметров ^include <stdio.h> main( ) { void fun(char, float); fun(259, 1.0/3); return 0; } void fun(Char, Real) char Char; float Real; { printfC’% 6f", Char * Real); } Явное преобразование параметров /include <stdio.h> main( ) { void fun( ); fun(259, 1.0/3); return 0; void fun(Char, Real) int Char; double Real; { Char = (char)Char; Real = (float) Real; printf("% 6f", Char * Real); } Во всех рассмотренных преобразованиях аргументов и параметров требуется, чтобы соответствующие друг другу аргументы и параметры были одного типа. Это, в частности, означает, что аргументу типа (char) может, например, соответствовать параметр типа (short int), но не может соответствовать, например, параметр типа (float). 7.2. Получение результата Если последним выполняемым оператором функции является return Ехр-, где Ехр - выражение, то в месте вызова функции возможно получение результата, представленного выражением Ехр. Если тип входящего в оператор return выражения не идентичен описанному или принятому по умолчанию типу идентификатора функции, то выражение будет преобразовано к данному типу.
JOO Часть II. Язык Внимание: Если функция не дает результата, то ее последним выполняемым оператором должен быть оператор return; Тип “результата" такого оператора должен быть представлен как void. Примеры а) Функция, дающая результат char агг[3][2] = {'J'}; ^include <stdio.h> main( ) { s char *fun( ); putchar(*fun( )); return 0; } char; fun( ) { return arr; } Выполнение программы приводит к выводу буквы j. Поскольку агг представляет собой массив типа (char [3] [2]), то оператор return будет неявно преобразован к виду return (char(*)[2])arr; Поскольку после этого образования выражение, входящее в оператор return, имеет тип (chart*) [2]), а результат функции должен быть типа (char*), то выполняется неявное преобразование выражения &агг[0], а рассматриваемый оператор return выполняется, как если бы имел вид return (char*)(char(*)[2])arr; эквивалентный в рассматриваемом случае оператору return &arr[O][OJ; б) Функция, не дающая результата /include <stdio.h> main( ) { void Greet( ); Greet( ); return 0;
7. Функции 10i void Greet( ) { print("Hello"); return; } Выполнение программы приводит к выводу надписи Hello. Ключевое слово void перед именем функции означает, что эта функция не дает результата. Использование оператора return по существу излишне, поскольку он выполняется по умолчанию в месте нахождения фигурной скобки, заканчивающей описание функции. 7.3. Рекурсия Функции языка Си могут быть вызваны рекуррентно. При каждом вызове образуются последующие автоматические переменные. Примеры а) Обращение строки символов mirror(par) char*par; { char chr =*раг ++; if(*par) mirror(par); putchar(chr); } Вызов функции mirror в виде mirrorf'ave"); приведет к выводу надписи “аме“, а очередность действий при выборе функции mirror будет следующей. На первом этапе параметру par будет присвоено значение указателя на знак 'а', а переменной chr - значение jf, после чего произойдет инкремент параметра , Поскольку *раг! = 0, то произойдет рекуррентный вызов функции mirror. На втором этапе параметру par будет присвоено значение указателя на знак V, а переменной chr - значение V и вновь произойдет инкремент параметра par. Поскольку *раг! = 0, то произойдет рекуррентный вызов функции mirror. На третьем этапе параметру par будет присвоено значение указателя на а переменной chr - значение 'е', и после очередного инкремента параметра par он получит значение указателя на знак nul, заканчивающий строку ave. Поскольку *раг{ = 0, произойдет переход к выполнению стандартной функции putchar, что приведет к выводу значения переменной chr, присвоенного на третьем этапе, т.е. символа 'е', и закончится выполнение третьего этапа вызова функции mirror.
102 Часть II. Язык При возврате с третьего этапа вызова функции mirror на второй произойдет вывод значения переменной chr, присвоенного на втором этапе, т. е. символа V. При возврате со второго этапа вызова функции mirror на первый будет выведено значение переменной chr, присвоенное на первом этапе, т. е. символа 'а', и завершится выполнение первого этапа функции mirror. На этом заканчивается выполнение функции mirror(”ave"); б) Вывод значения величины, представленной через аргумент функции в виде ряда знаков void outdec(par) int par; a { int var; if(par<0) putcharf’-'), par = - par; iffvar = par /10) outdec(var); putchar(par % 10 + ’O'); } Функция не дает результата, так как ее выполнение заканчивается неявным оператором return; Если заголовку outdec придать вид static void outdec(par) register int par; то функция outdec будет доступна только в модуле, содержащем ее определение. Кроме того, во время выполнения функции ее параметр par был бы сохранен не в оперативной памяти, а в одном из регистров процессора. 7.4. Параметры функции main Во всех приведенных выше примерах функция main, определение которой должна содержать каждая программа, была представлена как функция без параметров. Однако по существу она является двухпараметрической с параметрами argc и argv. Параметры, передаваемые программе, написанной на языке Си, выступают в роли аргументов функции main. Параметр argc имеет тип (int) и определяет число передаваемых параметров, которые могут быть приняты за имя и аргументы программы, а параметр argv имеет тип (char*[ ]) и указывает на начало массива строк, являющихся передаваемыми параметрами, каждая из которых обычно заканчивается знаком nut. Первая из этих строк образует имя программы в системе.
7. Функции 103 Пример Программа, выявляющая аргументы, с которыми она была вызвана finclude <stdio.h> main(argc, argv) int argc; char*argv[ ]; { while (argc —) printf("%s%c",*argv ++, argc ? ''x'Xn'); return 0; } Если программа будет вызвана с использованием имени Jan с добавочным аргументом Bielecki, то argc будет присвоено значение 2 и цикл while выполнится два раза с выдачей надписи Jan Bielecki.
8. Принципы выполнения операции ввода-вывода Операции ввода-вывода касаются файлов данных или устройств. В программах как -файлы данных, так и устройства представляются посредством некоторых символических указателей, которые не являются физическим объектом, а его логической интерпретацией, такой, как файл данных в памяти или файл данных от знакового устройства, например клавиатуры.При написании программ на языке высокого уровня и при кодировании алгоритма удобно пользоваться абстрактным понятием “канал1*. Тем не менее необходимо гймнить, что содержащиеся в алгоритме операции с каналами будут реализованы во время выполнения программы как операции с файлами и устройствами. Поэтому при выполнении операций с каналами предварительно должно быть установлено соответствие канала с определенным файлом данных или устройством. Это соответствие устанавливается сразу после создания абстрактного объекта, каким является канал. Для связи канала с файлом или с устройством служит функция fopen, а для * устранения такой связи служит функция fclose. Результатом функции fopen является указатель на файл типа FILE, определенный в файле stdio.h. Аргументом функции fclose является указатель, полученный посредством функции fopen. Каждый из перечисленных указателей является указателем файла и дает возможность идентификации файла. Кроме указателя, полученного с помощью функции fopen в файле stdio.h, определены три дополнительных указателя, представленные символами stdin, stdout и stderr. Эти указатели служат соответственно для идентификации стандартных входного и выходного устройств и стандартного файла для обнаружения ошибок. Внимание: По умолчанию принимается, что стандартный входной канал связан с клавиатурой, а стандартный выходной канал и стандартный файл для обнаружения ошибок связаны с экраном монитора. Функция fopen Заголовок: FILE* fopen(char*Name, char*Mode) Выполнение функции fopen приводит к образованию канала, связи его с файлом или с устройством с именем Name и к появлению указателя на файл. Если Mode представлено символом “г“, то файл будет открыт в режиме ввода, а если символом “w“, то - в режиме вывода. Если файл открывается в режиме вывода, а файл с данным именем уже существует, то он будет удален перед открытием. Если открытие файла окажется невозможным (например, файл открывается в режиме ввода, а файл с указанным именем не существует), то результатом функции будет пустой указатель, представленный символом NULL.
8. Принципы выполнения операции ввода-вывода 1G5 Функция fclose Заголовок: int fclose(FILE*Stream) Выполнение функции fclose приводит к закрытию файла, идентифицируемого аргументом stream. Результатом функции является величина, равная 0, если закрытие файла реализовано, или величина, равная EOF, в противном случае. Во время выполнения операции ввода-вывода с файлами, идентифицированными посредством указателей, могут быть два вида исключительных ситуаций: подтверждение ошибки операции, а также попытка ввода данных из файла, когда он находится в конечной позиции. В приведенном примере в идентифицированной посредством указателя файла структуре типа FILE будет записана информация, к которой можно обращаться с помощью функции ferror и feof. Эта функция может быть удалена посредством функции clearerr. Функция ferror Заголовок: tnt ferror(FILE*Stream) Выполнение функции ferror приводит к возможности получения информации о появлении ошибки во время проведения операции с файлом, идентифицируемым аргументом Stream. Результатом функции является величина, отличная от 0 в случае подтверждения ошибки и равная 0 в противном случае. Функция feof Заголовок: int feof(FILE*Stream) Выполнение функции feof приводит к возможности получения информации о том,- предпринята ли попытка ввода данных из файла, идентифицируемого аргументом Stream, когда файл находится в конечной позиции. При подтверждении результатом функции является величина, отличная от 0 и равная 0 в противном случае. Функция clearerr Заголовок: void clearerr(FILE*Stream) Выполнение функции clearerr приводит к удалению из структуры, указанной аргументом Stream, информации о подтверждении ошибки операции, а также о попытке ввода данных из файла, когда он находился в конечной позиции. Функция fgefc
106 Часть II. Язык Заголовок: int fgetc(FILE*Stream) Выполнение функции fgetc приводит к вводу очередного символа из файла, идентифицируемого аргументом Stream. Результатом функции является величина со значением, равным коду вводимого символа. Если перед вызовом функции fgetc файл находился в конечной позиции, то результатом функции будет величина со значением EOF. Функция fputc Заголовок: int fputc(int Chr, FILE*Stream) Выполнений: функции fputc приводит к занесению в файл, идентифицируемый аргументом Stream, символа с кодом Chr. Результатом функции является величина, значение которой представляет собой код заносимого символа. Если ввод символа не состоялся, то результатом функции будет величина со значением EOF. Внимание: Вызов fputcfc,stdout) может быть заменен вызовом putchar(c). Пример , Следующая программа иллюстрирует использование описанных функций: #indude <stdio.h> main( ) { FILE *inp; int ch; inp = fopen("JANB.DOC", ”r”); if(inp){ while((ch = fgetc(inp))! = EOF) fputcfch, stdout); close(inp); } else; fputcC?', stderr); return 0; } Выполнение программы приводит к копированию текста из файла JANB.DOC на стандартном устройстве ввода. Функция fread Заголовок: unsigned fread(void *Buf, int Size int Count, FILE ‘Stream)
8. Принципы выполнения операции ввода-вывода 107 Выполнение функции fread приводит к вводу из файла, иденти- фицируемого аргументом Stream, порции информации, местоположение But и размер Count которой в оперативной памяти указан посредством Size Результатом функции является величина со значением, равным количеству вводимых объектов информации. Если данное количество меньше, чем Count, то это означает наличие ошибки выполнения функции или ввод данных из файла, находящегося в конечной позиции. Внимание: Локализация объема памяти производится с помощью величины типа (void*). Эта величина может быть принята за указатель переменной типа (void) с размером 0, находящейся в начале локализованного объема. Функция fwrite Заголовок: unsigned fwrite(void *Buf, int Size, int Count, FILE ‘Stream) Выполнение функции fwrite приводит к выводу в файл, идентифицируемый аргументом Stream, объектов информации размером Size каждый из оперативной памяти, указанный с помощью Buf, Count. Результатом функции является величина со значением, равным числу выводимых объектов. Если указанное- число меньше, чем Count, то это означает наличие ошибки выполнения функции. Функция fprintf Заголовок: int fprintf(FILE ‘Stream, char ‘Format Обращение к библиотечной функции fprintf требует в качестве первого аргумента использовать указатель на файл. Библиотечная функция fprintf обеспечивает вывод в произвольный файл, открытый как поток данных для вывода. Выполнение функции fprintf приводит к интерпретации и к выводу в файл, идентифицируемый аргументом Stream, последовательности знаков, определенных через Format, а также аргументов, связанных с параметром “...“ (многоточие). Результатом функции является число выведенных знаков. Принимается, что строка, указанная через Format, состоит из знаков и из шаблонов преобразования. Требуется, чтобы каждому шаблону преобразования соответствовал один аргумент вызова функции. Обязательными являются следующие принципы интерпретации: а) Интерпретация следующего после шаблона преобразования знака приводит к выводу такого же знака. б) Интерпретация шаблона преобразования приводит к выводу строки .знаков, выражающей значение аргумента, связанного с этим шаблоном. Внимание: Если вывод производится на стандартный выход (представлен- ный через stdout), то вызов функции fprintf может быть представлен в виде
108 Часть II. Язык fprintf(format, al, aZ, .... an) Уточнения Функции fprintf и printf могут быть вызваны с переменным числом аргументов al, а2, .... ап, в общем случае равным числу шаблонов, содержащихся в аргументе format. Каждый шаблон, если он имеется, состоит из строки знаков, начинающегося со знака % (процент) и кончающегося буквой (или парой букв), принадлежащей к следующему списку кодов преобразования d о х Id 1е 1х с s е f g и определяющей тип выводимой величины, а также способ преобразования ее значения в строку выводимых знаков. Непосредственно перед кодом преобразования могут быть слова fleld.count или просто field, где field - число, определяющее ширину выходного поля знаков, a count числовой счетчик, который определяет количество выводимых цифр или знаков. Если указанная ширина поля слишком мала, то она неявно расширяется (например, % 2.5 заменяется на % 5.5). Значения кодов преобразования и других знаков следующие: % начинает шаблон преобразования. означает, что выводимая строка знаков должна быть выоовнена по левой стороне выходного поля. характеризует строку, состоящую из цифр, специфицирует число знаков в поле формата от другой строки, состоящей из цифр. Если ширина поля является числом, начинающимся с цифры 0, а d о X Id 1о 1х с S е f g выводимая строка знаков не заполняет поля, то на излишних позициях поля помещаются не пробелы, а цифры 0. приводит к выводу аргумента типа приводит к. выводу аргумента типа (без начального нуля). приводит к выводу аргумента типа числа (без начальных знаков Ох). приводит к выводу аргумента типа приводит к выводу аргумента типа (без начальных знаков Ох). приводит к выводу аргумента типа числа (без начальных знаков Ох), приводит к выводу аргумента типа (char). (int) в виде десятичного числа. (int) в виде восьмеричного числа (int) в виде шестнадцатеричного (long) в виде десятичного числа. (long) в виде восьмеричного числа (long) в виде шестнадцатеричного (int) после преобразования в тип приводит к выводу строки знаков, первый из которых указан через аргумент типа (char *), а их число определено счетчиком. приводит к выводу аргумента типа (double) в виде десятичного числа с плавающей точкой и показателем; число цифр мантиссы определяется счетчиком. приводит к выводу аргумента типа (double) в виде десятичного числа с плавающей точкой без показателя; число цифр мантиссы определяется счетчиком. приводит к выводу аргумента типа (double) в виде десятичного числа с плавающей точкой и показателем или без показателя в зависимости
8. Принципы выполнения операции ввода-вывода 109 от того, какой вид можно выразить с помощью меньшего числа знаков; число цифр мантиссы определяется счетчиком. Примеры а) Функции и выводимые строки знаков Функция Выводимая строка printff’Any text"); Any text printf("(X-2d)",5); (5) printff'Xs", "JanB"); JanB printf("(&)", "JanB"); (JanB) printf("X-4.3s); "JanB"); (Jan) printf("(X4,3s)", "JanB"); (Jan) printf("Xc",'B'); В printff'Xu", - 1); 65535 printf("Xo", - 1) mm printff'Xx”, - 1); FFFF printf("Xd, Xd", 2,5); 2,5 printf("X4,2f”, 6.232); 6,23 б) Программа с вызовом функции printf ^include «stdio.h» main( ) { printff'Jan Xc is Xd Xs", 'B', 47, “now"); return 0; } Выполнение программы приводит к выводу надписи JanB is 47 now в) Программа с вызовом функции fprintf finclude «stdio.h» FILE *out; main( ) { if((out = fopen("JanB.DOC", "w")!=NULL fprintf(out, ”1 am Xd now", 47); return 0; } Выполнение программы приводит к созданию файла JANB.DOC и размещения в нем строки знаков I ат 47 now. Функция fscanf Заголовок: int fscanf(FILE ‘Stream,
но Часть II. Язык char ‘Format, ...) Библиотечная функция fscanf обеспечивает ввод из производного файла, открытого как поток данных для ввода. Выполнение функции fscanf приводит к выводу файла, идентифицируемого аргументом Stream, последовательности знаков, определенных через Format, а также аргументов, связанных с параметром (многоточие}, к трактованию их в виде записей значений величин и к последующему присвоению этих значений переменным, указанным через аргументы, связанные с параметрами. Принимается, что строка, указанная через Format, состоит из пробелов, шаблонов преобразования и других знаков. Требуется, чтобы каждому шаблону преобразования, не содержащему знака * (звездочка}, соответствовал один аргумент. Внимание: Если ввод производится со стандартного входа, то вызов функции fscanf может быть представлен в виде scant (format, al, а2, ..., an) Уточнения Интерпретация пробела приводит к его опусканию. Интерпретация шаблона преобразования приводит к игнорированию ближайшего пробела (интерпретация относится к шаблону, заканчивающемуся буквой с) с последующим введением одного поля знаков, образованием на основе этого поля величины и присвоением ей имени переменной, указанной через очередной аргумент. Внимание: Если непосредственно после знака % (процент}, с которого начинается шаблон преобразования, следует знак * (звездочка}, то образованная величина игнорируется. Интерпретация знака, не принадлежащего шаблону преобразования, приводит к опусканию этого знака. Интерпретация управляющего аргумента заканчивается в момент подтверждения, что шаблон преобразования не соответствует требуемому по этому шаблону входному полю, а также когда знаку управляющего аргумента не соответствует идентичный знак в потоке данных. Каждый шаблон состоит из строки знаков, начинающейся со знака % (процент} и кончающейся буквой или парой букв, принадлежащих к следующему набору кодов преобразования: d о х Id 1е 1х с s е f g и определяющих способ преобразования к величине ряда вводимых знаков. Непосредственно перед кодом преобразования может быть слово field, которое является числом, определяющим ширину входного поля знаков. За ширину поля принимается минимум из field и width, где width - число знаков перед ближайшим пробелом. Если в шаблоне нет слова field, то за ширину поля принимается width. Значение отдельных букв или пар букв, заканчивающих шаблон, приведено ниже:
S. Принципы выполнения операции ввода-вывода Н1 d в поле ожидается ввод десятичного числа; величина типа (int) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. о в поле ожидается ввод восьмеричного числа; величина типа (int) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. х в поле ожидается шестнадцатеричное число; величина типа (int) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. п в поле ожидается десятичное число; величина типа (short) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. Id в поле ожидается десятичное число; величина типа (long) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. 1о в поле ожидается восьмеричное число; величина типа (long) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. 1х в поле ожидается шестнадцатеричное число (с наличием или без предшествующих знаков Ох); величина типа (long) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. с в поле ожидается символ; величина типа (char) со значением кода этого символа будет присвоена переменной, указанной через аргумент. s в поле ожидается строка знаков; эти знаки (вместе с последним знаком 0) будут размещены в массиве типа (char[l]), первый элемент которого указан через аргумент. f,e,g в поле ожидается число с плавающей точкой; величина типа (float) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. le, If в поле ожидается число с плавающей точкой; величина типа (double) со значением, равным этому числу, будет присвоена переменной, указанной через аргумент. Выполнение функции fscanf заканчивается после интерпретации всего ряда, введения неправильного знака илн знака, не позволяющего осуществить преобразование, а также при окончании файла. Результатом функции является величина со значением, равным числу правильно выполненных присвоений, или величине со значением EOF в случае окончания файла. Пример. Программа, содержащая вызов функции scanf ♦include <stdio.h> main( ) { char Initial; int Age; Char Name [4]; scanf("Xs XcX*s is XdX*s", Name, &Initial. &Age); printf("XsXc is Xd now". Name, Initial, Age);
112 Часть II. Язык return 0; } Выполнение приведенной программы для данных Jan Bielecki is 47 now даст вывод надписи Jan is 47 now Функции с переменным числом аргументов Отличительной особенностью функций типа printf и scanf является то, что они могут быть вызваны с переменным числом аргументов. В первом варианте языка определение таких функций не было возможно. Только в стандарте ANSI и вслед за ним в языке Турбо Си появились средства, позволяющие определять эти функции. Следующая программа содержит пример описания упрощенной функции printf. В ней, в частности, содержатся описание вспомогательной переменной Ptr типа va—list, а также вызовы макроопределений va—start, va—arg и va—end. finc1ude<stdto.h> #i nc lude<stdarg. h> main( ) { int printf(char*. ...); printf(”Jan Xc is Xd Xs\n”,'B', 45, "noW); return 0; } int Count = 0; int printf(char ’Format. ...) { char ch; void outdec(inf), outstr(char); int ourchar(char); va-list Ptr; va-start(ptr. Format); while((ch = ’Format ++) ! = 0){ switch(ch){ case 'X': switch(ch=*Format ++){ case 'd': outdec(va_arg(ptr, int)); break; case 'c': outchar(va_arg(Ptr,int)); break; case 's': outstr(va_arg(Ptr, char *)); } break;
8. Принципы выполнения операции ввода-вывода ИЗ case ’ \V.: •Format ++, ch = '\n'; default: outchar (ch); } va_end(Prt); return Count; } void outdec(dec) int dec; { int num; if(dec<0) outchar(' - '),dec = - dec; if(num = dec \ 10) ! = 0) outdec(num); outchar(dec X 10 + ’O'); } void outstr(str) char*str; { char ch; whi1e((ch = *str ++)! = 0 outchar (ch); - } int outchar(char chr) { (void)putchar(chr) { (void) putchar(chr): Count++; } Тип va—list, а также все макроопределения даны в заглавном файле stdarg.h. Упрощенная функция printf определена на основе следующих принципов: - в заголовке функции обозначено, что функция будет вызвана с переменным числом аргументов (лексический символ “...“); - описан и связан с упреждающим параметром символ (многоточие) вспомогательной переменной типа va—list (макровызов va—start); выполнено обращение к макроопределению, дающему доступ к аргументам функции указанного типа (макровызов va—arg); - объявлено окончание обслуживания неявных аргументов функции (макровызов va—end). Я. Hi,
9. Управление оперативной памятью Управление оперативной памятью реализуется посредством библиотечных функций malloc, calloc и free. Функции malloc и calloc осуществляют разделение памяти, а функция free - ее освобождение. Описания этих функций находятся в файле заголовка stdio.h. Функция malloc * Заголовок: void * malloc(unsigned size) Функция malloc возвращает в качестве своего значения указатель на область памяти, начинающуюся на границе слова. Размер этой области в байтах определяется с помощью аргумента. Если разделение объема памяти невозможно, то результатом функции будет пустой адресный указатель. Пример #inc1ude«stdiо.h> finc1ude<std11b.h> double cabs(re, im) double re, in; { return sqrt(re*re +im * im); } typedef struct{ double Re, Im; } COMPLEX; Comtlex *Ptr; main( ) { Ptr=(COMPLEX*) malloc(sizeof(Complex)); Ptr -> Re = 3.0; Ptr -> Im = 4.0; printf("Xf”, cabs(Ptr -> Re, Ptr -> Im); return 0; } Выполнение программы приводит к выводу пары чисел (3.0, 4.0). Функция calloc Заголовок: void * calloc(unsigned count, unsigned size)
9. Управление оперативной памятью ИЗ Выполнение функции calloc приводит к увеличению объема оперативной памяти на величину count*size. Результатом функции является указание адреса увеличенного объема. Если увеличение объема не происходит, то результатом функции будет указание на пустой адрес. Пример finclude «stdio.h» finclude <stdlib.fi» #include <№ith.h> struct complex *Ptr; main( ) { Ptr = (struct complex *) callocfl, sizeof(struct complex)); Ptr -» x « 3.0; Ptr -> у = 4.0; printf("%f", cabs(*Ptr)); return 0; } Выполнение программы приводит к выводу пары чисел (3.0, 4.0). Используется то, что в файле заголовка math.h определена структура struct complex{ double х, у; } Функция free Заголовок: void free(void *ptr) Функция free обеспечивает в результате своего выполнения освобождение выделенной ранее с помощью функции области памяти, определяемой с помощью аргумента ptr. Пример #include<stdio.h> #include<std1ib.h> main( ) int Count; void*Ref; for(Count =1;; Count++){ Ref = ca11oc(Count, 1024); if(Ref = NULL) break; free(Ref); } printf("Available heap: % dK bytes". Count - 1); return 0; } Выполнение программы приводит к оцениванию размера и к выражению его в килобайтах.
10. Препроцессор Действия, основанные на включении в основную программу текстов нз файлов данных, формировании макроопределений и на условных переходах, называются входными преобразованиями, а программа, реализующая эти преобразования и обычно являющаяся составной частью компилятора, называется препроцессором. Директивы препроцессора отличаются от других выражений, образующих основной текст, тем, что начинаются знаком #(hash), перед которым в строке имеются знаки пробела и табуляции. Те директивы, которые не умещаются на одной строке, могут быть продолжены. Знаком продолжения является косая черта в конце продолжаемой строки. Границы директивы препроцессора определяются основным модулем, в котором эта директива была использована. Пример ♦define шах(а.Ь) а > Ь? \ а : b Приведенная директива эквивалентна директиве ♦define nax(a.b) а > b? а : b и записана на двух строках, первая из которых продолжена. 10.1. Включающие директивы Этн директивы имеют вид #include “Name“ или #include <Name> где Name - нмя файла Интерпретация директивы #include приводит к включению в месте ее появления содержимого файла Name. Если имя файла заключено в угловые скобки, то поиск файла ограничивается каталогом файлов, определенным с помощью опции компилятора. Если имя файла заключено в кавычки, то предварительно проводится его поиск в текущем каталоге. Если будет указано сложное имя файла, например C:\JB-TC\JB-HEAD.H, то поиск, естественно, не проводится, а сразу включается содержимое указанного файла. Остается добавить, что допускаются вложенные директивы #include.
10. Препроцессор 117 В этом случае требуется, чтобы только был закончен процесс встроенных включений. Пример Если файл EWA содержит текст main( ) ♦include<IZA> } файл IZA - текст { ♦include "JAN" a JAN - текст printf("Hello, I am JanB"); то передача препроцессору файла EWA приведет к образованию основного модуля main( ) { printf("Hello, I am JanB"); } 10.2. Определяющие директивы Эти деректнвы имеют вид ♦define Id Seq или ♦define Id(List)Seq тде Id - идентификатор, Seq - произвольная строка лексических единиц, а List - список идентификаторов параметров макроопределения. Интерпретация первой из упомянутых выше директив приводит к связи с идентификатором Id строки лексических единиц и к генерации этой строки при каждом появлении указанного идентификатора. Во второй директиве список параметров List состоит из идентификаторов, разделенных запятыми. Если во время интерпретации очередных строк основного текста (не содержащих директив #define) встретится идентификатор Id, а за ним список аргументов в круглых скобках, то вся эта запись, называемая далее макровызовом, будет генерировать запись, образованную из строки лексических единиц Seq, в которой каждый из параметров заменен соответствующим (может быть пустым) аргументом. Описанные действия, называемые развитием макровызовов, продолжаются столько, сколько выполняются замены, определенные директивой #define. После их окончания генерируется результирующая запись.
118 Часть 11. Язык Если среди лексических единиц, содержащихся в макроопределении, нахо- дится пара стоящих рядом знаков #(hash), которые могут иметь пробелы с каждой стороны, то после выполнения всех преобразований макроопределе- ния, оба знака #(hash) вместе с пробелами будут удалены. Эта особенность часто используется для составления новых идентификаторов. Кроме того, если некоторый параметр макроопределения имеет впереди знак #(hash), то во время преобразования мароопределения соответствующий этому параметру аргумент будет освобожден от пробелов спереди и сзади, а затем заключен в кавычки. Если преобразованный таким образом аргумент содержит знаки “ (кавычки), то перед каждым из них будет стоять косая черта. Макроопределения, образованные с помощью директивы #define, могут быть аннулированы директивой ♦undef Id н Такое аннулирование необходимо при образовании нового макроопределения, связанного с аналогичным идентификатором, когда ряды лексических единиц в старом и новом определении не идентичны. Примеры а) Использование директивы без параметров ♦define SIZE 200 Int Arr[SIZE] [SIZE]; Интерпретация приведенной записи приведет к генерации выражения int Агг[200] [200]; б) Использование директивы #deflne с параметрами ♦define шах(а, Ь) а>Ь? а:Ь main( ) { int х = 5; prinff'Xd”, nax(x + 3, x - 2)); return 0; } Интерпретация приведенной записи приведет к генерации выражения main( ) int х = 5; printf(”%d", х + 3 > х - 2? х + 3: х - 2); } в) Использование директивы ttindef ♦define Kaja 13 □ut(Kaja) ♦define Kaja 13
1Q. Препроцессор 119 ♦undef Kaja □ut(Kaja) ♦define Kaja 14 □ut(Kaja) Интерпретация приведенной записи приведет к генерации выражения out(13) out(Kaja) out(14) Использование директивы #undef является обязательным, так как лексическая единица в третьей директиве #define отличается от лексической единицы в первой и второй директивах. г) Игнорирование макровызовов в директивах #define ♦define Jan Ewa ♦define Kaja Jan ♦undef Jan Kaja Интерпретация приведенной записи приведет к генерации надписи Jan а не (как можно было бы предположить) надписи Ewa д) Игнорирование макровызовов в знаковых и строчных литералах ♦define j е ♦define Ь wa ♦1nc1ude<stdiо.h> main( ) { printf("XcXs. ’j', "b”); return 0; } Интерпретация приведенной записи приводит к генерации программы, выполнение которой приводит к выводу надписи jb, а не (как можно было бы предположить) надписи ewa. е) Многократное развитие макровызовов ♦define alfa 5 + beta ♦define beta gamma + 3 alfa - beta Интерпретация приведенной записи приведет к генерации 5 + gamma + 3 - gamma + 3
120 Часть II. Язык ж) Использование оператора ## fdefint Jan Ка fdefine Ewa ja fdefine Kaja Iza ^define Ka Ve ^define ja ra fdefine Name(x, y) x у Narae(Jan, Ewa) Интерпретация приведенной записи приведет к генерации Vera з) Испольурвание оператора # fdefine Show (var, frat) printf(#var" = ” frat, var) Show(0ne, "Xd"): Show("Two", “Xa"); Интерпретация приведенной записи приводит к генерации printf("One"" = ""Xd", One); printf("V'Two """=""% s". "Two"); 10.3. Директивы условного включения В. соответствии со своим названием директивы условного включения дают возможность генерирования текстов, идентичных выбранным фрагментам основного текста. В общем случае директива условного включения имеет вид #if Ехр Text felif Expy Texty #elif Ехр2 Text2 #elif Ехрп Texln false Textf fendif Эта директива начинается с фразы #if и заканчивается фразой #endif. Между этой парой фраз может произвольное число раз находиться фраза #elif и только один раз - фраза #else. Выражения Ехр и Ехр, во фразах #if и #endif являются выражениями, определяющими способ генерации текста. Эти выражения должны быть целыми и постоянными, т. е. каждое из них может быть заменено числом, но не может содержать ни оператора sizeof, ни оператора преобразования. Однако они могут содержать подвыражения вида
10. Препроцессор 121 defined id или defined(ZtZ) представляющие число 1 или 0 в зависимости от того, определено или нет макроопределение, идентификатор которого совпадает с id в момент появле- ния этих подвыражений. Интерпретация директивы условного включения на- чинается с анализа выражения Ехр. Если значение этого выражения отлично от 0, то всю директиву заменяет Textp. В противном случае по очередности определяются значения выражений Expt. Если значение выражения, например Ехр,, отлично от 0, то вся директива заменяется на Texty Если значения всех выражений равны 0, то директиву заменяет Textf. Допускается, чтобы любой из перечисленных текстов содержал директивы условного включения. Директива условного включения может быть также записана в упрощенном виде, не содержащем фразы #else. Для обеспечения соответствия с основной версией языка допускается также, чтобы фраза #if defined(Id) была представлена в виде #ifdef Id а также, чтобы фраза #if Idefined(Id) была представлена в виде fifndef Id Примеры а) Простая директива условного включения #if defined(ArrFlg) int Агг[30]; fendif Если во время интерпретации директивы определено макроопределение ArrFIg, то приведенная запись дает генерацию выражения int Агг[30]; В противном случае не будет генерировано ни одно выражение. б) Директива условного включения с альтернативой #if а + Ь == 5 pritnf("%d, 5);
122 Часть II. Язык ♦else printfC'Xd", 13); ♦endif Если выражение а + b = 5 представляет величину, отличную от 0, то будет сгенерировано printfC'Xd", 5); В противном случае printf("Xd”, 13); в) Составная директива условного включения ♦define Alfa 3 ♦define Beta(x) x - 4 ♦if Alfa -= 6 int One; ♦el if Beta(2) + Alfa printf(”Izabela"); ♦endif Интерпретация приведенной записи приведет к генерации pr1ntf("Izabela") г) Встроенные директивы ♦define Alfa 5 ♦if Alfa * 5 > 20 main( ) ♦if Alfa == 4 int Arr [2]; ' ♦elif Alfa == 3 char Arr [2]; ♦else { ♦endif ♦if 0 printf("Iza”); ♦else printf("Kaja") ♦endif ♦else printf(Ewa"); ♦endif } Интерпретация приведенной записи приведет к генерации main( ) { printf(Kaja");
11. Примеры написания программ с использованием функций В данном разделе приведены варианты решения некоторых простых задач, которые основаны не на оптимальном программировании, а на использовании различных языковых средств. Задача Определить функцию для нахождения величины аргумента типа (int). int abs(par) int par; { return par<0? -par: par; } #include<stdio.h> int vec[3]={30, 0, -30}, ent = 3; main( ) { while(ent --){ printf("%3d, abs(vec[cnt])); } return 0; } Выполнение программы приводит к выводу чисел 30, 0, 30. Задача Определить функцию для нахождения квадратного корня аргумента типа (int). int root(par) { int sum, i, j; for(sum = j = 0, i = 1; sum<par; j ++) sum + = (i + = 2) - 2; return, j; } f inc!ude<stdio.h> main( ) { int num;
124 Часть II. Язык char ch; do{ t scanf(”%d%c,&num,&ch); printf("\n%d%d\n", num, root(num)); } while(ch == ','); T return 0; } Выполнение программы приводит к нахождению квадратных корней введенных с клавиатуры чисел. Принимается, что отдельные числа разделяются запятыми, а после последнего числа ставится точка. Задача Определите! функцию для нахождения максимального элемента таблицы с элементами типа (int). int max(arr, len) int len, arr[ ]; { int max = *32768; while(len —) if(arr[len]>max) max = arr[len]; return max; } finclude<stdio.h> int vec[6] = {2, - 3, 5, - 4, 1, 8}; main( ) { ' printfC'Xd", max(vec,6)); return 0; } Выполнение программы приводит к выводу числа 8. Задача Определить функцию для нахождения целой степени аргумента типа (int). int power(base, ехр) int base, exp; { register int res; for(res = 1; exp > 0 — exp) res *= base return res; } #include<stdio.h> main( )
11. Написание программ с использованием функций 125 { int pwr; for(pwr = 0; pwr < 5; pwr ++) printf("\n%d%2d”, pwr, power(2, pwr)); return 0; } Выполнение программы приведет к выводу последовательных степеней числа 2. Задача Определить функцию для подсчета количества ненулевых битов в аргументе типа (int). int count(bits) insigned bits; { < int tally; 7/ for(tally = Qfbits; bits»=l) tally += brts & 1; return tally; } #1nclude<stdio.h> main( ) { int num; char ch; do{ scanf("ZdZc", &num,&ch); pr i ntf(”\n%d%d\n",num,count(num)); } while(ch == ’/); return 0; } Выполнение программы приведет к подсчету битов в двоичном исчислении введенного ряда чисел. Числа при вводе разделяются запятыми, а последнее число заканчивается точкой. Задача / Определить функцию, которая оценивает, представлен ли ее аргумент типа (char) прописной буквой английского алфавита. I int isupper(c) char с; { register char cc; return(cc « c)>=’A' && cc<='Z'; }
126 Часть II. Язык finelude<std io. h> char name[]="Jan B”, arr[5], pos; main() { for(; pos<4; pos ++) arr[pos] = 1supper(name[pos])?'I printf("%s\n%s”, name, arr); return 0; } Выполнение программы приводит к выводу надписи JanB и непосредственно под ним записи ! . Восклицательные знаки помещаются под прописными буквами, а точка - под строчными буквами. Задача Определить функцию для преобразования строчных букв английского алфавита в прописные. char toupper(c) • char с; { register char cc; if((cc = c)> = 'a' && cc < = 'z') return cc - ’ ’; return cc; } finclude<stdio.h> char pos, name[6]={'J' ,’a’,'n’,’e',’k’}; main() { for(pos = 0; pos<5; pos ++) printf("%c”, toupper(name[pos])); printf ("’\n”); return 0; } Выполнение программы приводит к выводу надписи JANEK. Задача Определить функцию, аргументом которой является адрес байта оператив- ной памяти, а результатом - величина типа (char), находящаяся в этом байте. char peek(addr) unsigned addr;
II. Написание программ с использованием функций 127 { return*(char *)addr; } finc1ude<stdiо.h> char letter » 'j'; main() { pri ntf ("%c", peek((uns igned)Metter)); return 0; } Выполнение программы приводит к выводу бу» вы j. Задача Определить функцию, которая в указанный адрес помещает величину типа (char). poke(addr, data) unsigned addr; char data; < { _ *(char *)addrje_data; } #include<stdio.h> char name[3]; main() { poke((unsigned)name, 'j')j poke((unsigned)(name + 1), 'b'): printf("Xs", name); return 0; } Выполнение программы приводит к выводу надписи jb. Задача Определить функцию для обозначения длины строки знаков, указанной через аргумент. int strlen(s) char*s; { register char *x; for(x = s; *x++;); return x - s- 1; } #include<stdio.h> char buffer[81]; main()
128 Часть II, Язык { scanf("X80s", buffer); printf("Length of Xs Is %d, buffer, strlen buffer)); return 0; } Выполнение программы приведет к определению длины строки текста, введенного с клавиатуры. Задача Определить функцию для копирования знаковых строк. char* strcpy(d.s) char *d, 5s; { wh11e((*d++ = *s++) I = 0; return — d; } #1nclude<std1o.h> char First Name[] = "Jan", Last Name[] = "Bielecki", Full Name[13]; main() { *strcpy(strcpy(Full Name, First Name), Last Name) = '\0'; pr1ntf("Xs", FullName); return 0; } Выполнение программы приводит к выводу надписи Jan Bielecki. Задача Определение формулы для сравнения знаковых рядов. int strcmp(s.t) char*s,*t; { wh1le(*s == *t ++) 1f(l*s ++) return 0; return*s - t[- 1]; } #1nclude<stdio.h> char First [80], Second[80]; ma1n() { scanf(”Xs Xs", First, Second); pr1ntf("\nXs%s%s",
11. Написание программ с использованием функций 129 First, ’’NOT EQUAL *'+( !strcmp( First, Second)« 2). Second); return 0; }" -_____ Выполнение программы' приводит к вводу двух строк знаков сравнению. В зависимости от результата сравнения строк появляется EQUAL или NOT EQUAL. и их надпись Задача Определить функцию для преобразования строки знаков в виде числа к величине типа (int). 1nt atoi(s) char*s: { int c, val, sign; while(isSpace(*s)) ++s; val = 0, sign = 1; if(*s =='-') ++s, sign = -1; else if (*s == '+') ++s; while(isDigit(c = *s++)) val = 10*val + c - 'O', return sign » 0?val: -val; } int isSpace(c) char c; {, return c == ''; } int isDigit(C) char c; { return c » = ' 0 ' && c < = '9'; } #include<stdiо.h> char number[81]; main() { scanf("%80s", number); printf("%d”, atoi(number)); return 0; } Выполнение программы приведет к вводу знакового ряда в виде числа и к выводу числа с таким значением.
130 Часть II. Язык Задача Определить функцию для реализации преобразования величины типа (int) в строку знаков в виде числа. itoa(val.str) int val; char*str; { extern char *extptr; if(val < 0) *str ++='-' val = -val; extptr = str; digit(val); *extptr = '\0'; char *extptr; digit(va’i) Int val; { Int head; If((head = val/10)!= 0) digit(hejd); *extptr++ = val % 10 'O'; } # 1 nc 1 ude<std io. h> main() { char mumber[7]; itoa(-40,number); printf("Xs”, number); return 0; } Выполнение программы приведет к преобразованию величины со значением -40 на строку знаков в виде числа и к выводу этой строки. Задача Составить программу, содержащую функции для динамического управле- ния оперативной памятью. Реализовать редактор строк, дающий возможность составления текста, состоящего из строк с целыми числами без знаков. Принимается, что если текст строки пустой, то строка удаляется, а если не будут заданы ни число, ни текст, то -будут введены все строки. Принимается также, что каждая введенная строка заканчивается нажатием клавиши Enter, а определение конца файла данных приводит к окончанию определения программы. finclude<stdio.h> typedef struct Str{ struct Str *next; int lab;
11. Написание программ с использованием функций } LIST; LIST head = {NULL}; main() { LIST *find(); int num, del; char line [81]; LIST *pos; num = del = 0; whilefdel 1= E0F){ if(!num) displayO; else{ pos = find(num); if(pos-> next && pos -> next -> lab == num) delete(pos); if(del! = ’\n'){ getLine(line, 81); insert(pos, num, line); ' } } getNumber(&num,&de1); } return 0; } LIST * find(num) int num; { LIST *ref; ref = &head; while(ref -> next && ref ->next lab<num) ref = ref -> next; return ref; } delete(pos) LIST *pos; { LIST *ptr ptr = pos ->next; pos->next = ptr ->next; free((char *)ptr); } getLinefptr, max) char *ptr; int max; { int i, c; for(i =0; i < max -1 && (c = get char()) !='\n' 1 ++) *ptr ++ = c; if(c !='\n') 9*
Часть II. Язык while(getchar() !='\n'); *ptr - 0; } length(line) char*line; { int len; len = 0; whi!e(*line++) len++ return len; } insert(pos num.line) LIST*pos; int num; char line[]; { LIST*ptr, *ref; int size; —, size - sizeof(LIST*) + sizeof(int) + length(line) + 1; if((ptr - (LIST*)calloc(l, size)) I - NULL){ ref = pos ->next; pos->ne»t - ptr; ptr->next - ref; ptr->lab - num; fill((char*)(ptr + 1), line); } else printf("Ignored. -Out of memory); } displayO { LIST*ref; ref - head.next; while(ref){ print("X3dXs\n". ref->1ab, (char *)(ref + 1)); ref = ref->next; } } filKref, line) char *ref, *line; { while((*ref ++ *line ++) I - 0; } void getNumber(num,del) int *num,*del); { int c. val. val - 0; while((c - getchar ())> = 'o' && c<='9') val = 10 *val + c - 'O'; *num - val;
11. Написание программ с использованием функций 133 *del = с; Если входной файл связан с клавиатурой, с которой будет введен ряд строк 30 Еиа 20 IZa 40 Jan 10 Kaja, то после введения строки будет выведена запись 10 Kaja 20 Iza 30 Ewa 40 Jan Если затем будет введен ряд строк 20 30 40 Vera, то после введения пустой строки будет выведена надпись 10 Kaja 40 Vera Задача Составить программу, выполнение которой анализирует файл данных и подсчитывает количество знаков в файле, которые не являются пробелами. finclude<stdio.h> main() { int count; char name [20]; FILE*fopen(); int chr; FILE*inp; getName(name); if((inp = fopen(name, "r”))==NULL){ printf("Can't open file Xs", name); exit (1); } for(count = 0; (chr = getc(inp)) f = EOF;) if(nonwhite(chr)) count ++; printf("File Xs contains Xd nonwhites”, name, count); return 0;
134 Часть II. Язык } Int nonwhite(chr) char chr; ‘ { return!(chr " ' '!] chr =- '\t';; chr == '\n'j] chr == '\r'|| chr == 'Xv’l J chr == '\f’j; } getName(name) char*name; { int c; w while((c =get char())I = '\n' && c! = EOF) *name++ • c; *name=’\0'; } Выполнение программы приводит к вводу имени файла, определению числа знаков данного файла, исключая пробелы, и к выводу строки вида File SOURCE.DOC contains 234 nonwhites Задача Составить программу, выполнение которой приводит к соединению группы файлов в один файл. Принимается, что первым аргументом вызова программы является имя выходного файла, а остальными аргументами являются имена входных файлов. ficlude<stdio.h> main(argc, argv) int argc; char *argv[); FILE *fopen() *inp, *out; if(argc-- = 1) exit(O); if((out = fopen(*++ argv, "w")) = = NULL){ printf("Can't open output file"); exit(l); } while(—argc){ if((inp = fopen(*++ argv, "r”)) == NULL){ printf ("Can't open input file %s", *argv); exit (1); } append (out, inp); } fclose(out); printf("Done");
11. Написание программ с использованием функций return 0; } append(out, inp) FILE *out, *inp; { int chr; while((chr, fgetc(inp))l=E0F) fputcfchr, out); } Если в файле SOURCE.DOC находится надпись Jan Bielecki, то выполнение' программы с аргументами TARGET.DOC SOURCE.DOC SOURCE.DOC приводит к размещению в файле TARGET.DOC надписи Jan Bielecki Jan Bielecki
12. Стандартные расширения Язык Турбо Си имеет расширение основного языка, которое описано в книге В. Kernighan, D. Ritchi “The С Programming Language**1). Большинство его новых элементов следуют из стандарта расширения языка Си, разрабо- танного ANSI, и описаны ниже. Некоторые из них уже упоминались ранее. 12.1. Ключевые слова В качестве*1 ключевых слов основного языка могут быть использованы следующие слова: const, enum, signed, void и volatile. 12.2. Литералы Расширения языка касаются целых, знаковых литералов, литералов с плавающей точкой и строчных литералов. Целые литералы Целые литералы могут быть записаны в десятичном, восьмеричном или шестнадцатеричном виде. Если после записи литерала следует буква U, то литерал представляет целое данное без знака. Если после записи литерала следует буква L, то литерал представляет длинное целое данное. Если имеются обе буквы, то литералы представляют длинное целое данное без знака. Буквы U и L могут быть также прописными. О типе литерала судят на основе его значения. В качестве типа литерала берется первый из уста- новленных списков типов, к которому принадлежит величина со значением литерала. Списки типов могут быть следующими: - для десятичных литералов, не содержащих дополнительных букв (int) (long int) (unsigned long int) - для восьмеричных и шестнадцатеричных литералов, не содержащих дополнительных букв (int) (unsigned int) (long int) (unsigned long) Ч Имеется русский перевод: Керннган Б., Ритчи Д., Рьюэр А. Язык программирования Си. Задачи по языку Си. - Прим, перев.
12. Стандартные расширения 137 - для литералов с дополнительной буквой U (unsigned int) (unsigned long int) - для литералов с дополнительной буквой L (long int) (unsigned long int) Примеры Литерал Значение Тип литерала 32767 32767 (int) 32768 32768 (long int) Gimi 32767 (int) 0100000 32768 (unsigned int) 13L 13 (long int) OxF 15 (unsigned int) Oxful 15 (unsigned long int) Знаковые литералы Знаковые литералы могут состоять из одного или двух знаков. Двухзна- ковые литералы трактуются как величины типа (int), в представлении которых первый знак занимает байт с меньшим значением, а второй - с большим. Однознаковые литералы также являются величинами типа (int). В этом случае по умолчанию принимается, что при преобразовании величины типа (char) к величине типа (int) происходит увеличение бита знака. Это приводит к тому, что знаковые литералы, представляющие знаки с кодами, большими, чем 127, трактуются как данные типа (int) с отрицательными значениями. Список описаний знаков дополнился описанием \а, представляющим знак звонка, а также описанием \?, представляющим знак вопроса. Кроме этого, непосредственно после косой черты можно указать не только восьмеричный, но и шестнадцатеричный код знака. В последнем случае перед шестнадцате- ричными цифрами должна стоять буква х. Пример #include<stdio.h> main() { putchar('\a'); putchar('\x4A'); putchar('\x42'); printf(”\n=%d, ’JB‘); return 0; } Выполнение программы приводит к выводу знака '\а' (звукового сиг- нала), пары знаков JB, а также значения величины типа (int), представ-
138 Часть II. Язык ленной в программе посредством двухзнакового литерала JB. Результат, выполнения программы был бы таким же, если двухзнаковый литерал заменить выражением СВ' « 8) + Т Литерал с плавающей точкой В основном языке все литералы с плавающей точкой имеют тип (double). В расширенном языке литерал с плавающей точкой, имеющей дополнительную букву F, представляет величину типа (float), а букву L - величину типа (long double). Буквы F и L могут записываться также в виде прописных букв. Величины типа (ling double) являются величинами с плавающей точкой удвоенной точности. Ввод и вывод таких величин может производиться с помощью шаблонов преобразования, содержащих букву (прописную или строчную). Пример f1nclude<std1o.h> main() { long double Number = 1.23456789L; printf("Number = %lf". Number); return 0; } Выполнение программы приводит к присвоению переменной Number величины с плавающей точкой типа (long double), а затем к выводу надписи Number = 1.234568 Строчные литералы Расширенный язык дает возможность сегментирования строчных литералов. Это основано на том, что если непосредственно друг за другом следуют два строчных литерала, то они трактуются как один литерал, состоящий из всех знаков первого литерала и всех знаков второго. Указанное расширение облегчает запись строчных литералов, которые занимают более одной строки программы. Пример #1nclude<std1o.h> matn{) { pr1ntf("Jan" "Andrzej" "Bielecki"); return 0; } Выполнение программы приводит к выводу надписи Jan Andrzej Bielecki.-
12.Стандартные расширения 139 12.3. Операторы Список операторов дополнился одноаргументным оператором + (плюс). Этот оператор дает возможность использования скобок и исключает трактование, например, выражения а + (Ь + с) как выражения (а + Ь) + с Результатом операции + (плюс) является величина, представленная через ее аргумент Пример #1nclude<std1o.h> matn() { double Big = 2e20, Small = le-5; pr1ntf("f" - Big + + (Big + Smell)); return 0; } Выполнение программы приводит к выводу приближения числа 0. Если убрать из программы одноаргументный оператор + (плюс), то мог бы произойти вывод приближения числа 0.00001. 12.4. Типы данных Расширенный язык содержит новые первичные типы: дополнительный арифметический тип с плавающей точкой (long double), перечислимый тип (enum) и тип, связанный с пустым файлом данных (void). Дополнительными первичными типами являются типы, имена которых содержат модификаторы signed, unsigned, const и volatile. Два первых типа дают возможность описания переменных, которым соответственно могут быть присвоены величины со знаком (signed) или без знака ( unsigned). Два остальных типа дают соответственно возможность описания переменных, которым в программе могут быть присвоены начальные значения (const), а также таких переменных, присвоение значений которым может производиться неконтролируемым программой способом (volatile). Тип (void) В отличие от остальных типов тип (void) связан с пустым файлом данных. Поэтому нет смысла и не существует возможности описания пере- менных типа (void), поскольку нет величин, которые можно им присвоить. Тем не менее есть ряд важных применений этого типа и связанное с ним ключевое слово. Простейшим использованием ключевого слова void является
140 Часть 11. Язык его помещение в описание функции. Принимается, что, если в месте списка параметров находится ключевое слово void, то функция - без параметров. Пример #include<stdio.h> main() { fun(); return 0; } fun (void) { printf("Empty"); } P Ключевое слово void может быть также использовано для обозначения того, что функция не дает результата, и для преобразования, приводящего к явному исключению результата оператора-выражения. Пример finclude<stdio.h> main() { void fun(); fun(); } void fund { char *Name; (void)(Name - "Izabela"); printf("Xs“, Name); return 0; } Выполнение программы приводит к выводу надписи Izabela. Выполнение преобразования (void), стоящего перед присваиванием, приводит к исключению результата присваивания (значение указателя на первый знак выражения Izabela). Тип (void ”) Важнейшим применением типа (void) является возможность использования величин типа (void *). Указатели этого типа, естественно, не могут быть очищены, так как величин типа (void) не существует. Однако они могут использоваться во всех тех контекстах, где производится локализация переменной' в оперативной памяти вне зависимости от типа этой переменной. Другими словами, насколько обычные указатели применяются для локализации объектов, настолько указатели типа (void*), называемые адресными указателями, служат для локализации объемов оперативной памяти.
12, Стандартные расширения 141 Важной особенностью расширенного языка является возможность преобразования величины типа (void*) в величину любого другого указывающего типа, а выполнение условия ехр - в выражение (typ) (void*) (ехр) = (ехр) где ехр - произвольный указатель выражения типа (typ). Внимание: Кроме перечисленных преобразований величин указывающего типа независимо от реализации существуют преобразования пустых указателей на пустые указатели других типов. Преобразование остальных указателей зависит от реализации, но если не происходит изменение размера указателя, то производится изменение представления величины. Такой способ выполнения преобразования принят, в частности, в Турбо Си. Пример #1nclude<std1o.h> main() { void *calloc(uns1gned, unsigned); void *Ref = calloc(3.1); ((char*)Ref[0] = 'j'; ((char*)Ref)[1] = 'b'; printf("Xs, (char*)Ref); return 0; } Выполнение программы приводит к выводу надписи jb. Результатом функции calloc является адресный указатель. ' 12.5. Модификаторы Модификатором является ключевое слово, которое определяет дополнительные возможности переменной указанного или принимаемого по умолчанию типа. Модификаторы signed и unsigned позволяют присвоить описанной переменной значение со знаком или без знака. Модификатор const определяет, что после описания переменной ей не будут присваиваться другие значения, а использование модификатора volatile означает, что присваивание значений описанной переменной может происходить не контролируемым программой способом. Модификатор signed Модификатор signed (со знаком) может стоять перед следующими ключевыми словами: char, int, short int, long int. Использование его перед int, short int и long int излишне, поскольку величины этих типов всегда имеют знак. Внимание: Так как в языке Турбо Си принимается по умолчанию, что тип (char) идентичен типу (signed char), то и в этом случае использование Модификатора излишне.
142 Часть II. Язык Пример #include<stdio.h> main() { signed char Var = '\377'; printf("%d”, Var); return 0; } Выполнение программы приводит к выводу числа -1. Если исключить из программы модификатор signed, то с точки зрения стандартного языка программа была бы неоднозначной, поскольку ее выполнение могло бы привести к выводу как числа -1, так и числа 255. Модификатор unsigned Модификатор unsigned (без знака) может стоять перед следующими ключевыми словами: char, int, short int, long int. Его использование перед словом char приводит к тому, что во время преобразования величины типа (unsigned char) к величине типа (int) не происходит увеличение бита знака. Пример #1nclude<stdio.h> main() { unsigned char Var = '\377', printf("%d”Var); return 0; }. Выполнение программы приводит к выводу числа 255. Если убрать из программы модификатор unsigned, то ее выполнение в системе Турбо Си привело бы к выводу числа -1. Модификатор const Использование модификатора const приводит к тому, что выражение, имя типа которого начинается с этого модификатора, представляет переменную, которая с точки зрения программы сохраняется как постоянная. Имя такой переменной не может находиться в левой части оператора присваивания, а также быть аргументом операторов инкремента, декремента и указания. Единственно, где переменной, описанной с атрибутом const, можно присвоить значение, является ее описание. Это объясняет использование для такой переменной названия постоянной переменной. Такой переменной является, например, переменная Min, описываемая как int const min « -13; а также указатель Greet, описываемый как char * const Greet = "Hello";
12. Стандартные расширения 143 Поскольку описанная таким образом переменная Min является постоянной переменной, для нее невозможна, например, операция . Жп ++ По аналогичным причинам невозможно выполнение с переменной Greet, например, операции Greet = "Good bye" В то же время ничто не мешает выполнить, например, операцию Greet [4] ='!' Пример #include<stdio.h> const int Arr[2] = {12, 13}, *Ptr = Arr; main() { const int "const Ref = Ptr; printf("%d“, *Ref); return 0; } • Выполнение программы приводит к выводу числа 12. В программе описаны двухэлементный массив постоянных переменных типа <int), переменная Ptr, указывающая на постоянную переменную типа (int), а также переменная Ref, указывающая на переменную типа (int). Величина, присвоенная переменной Ref, подобрана таким образом, что она указывает иа тот же объект, что и Ptr, т.е. на первый элемент массива Агг. Следует обратить внимание, что в приведенных описаниях можно поль- зоваться, например, операцией Ptr++, однако нельзя пользоваться операцией Ref++, так как Ref является постоянной переменной. Модификатор volatile Использование модификатора volatile дает возможность описания временных переменных, т.е. переменных, которым значения могут присваиваться неконтролируемым со стороны программы способом и даже в том случае, когда эти переменные описаны с модификатором const. Программа, содержащая обращения к временным переменным, может быть оптимизирована только в ограниченных пределах. Это приводит к - тому, что в характеристических точках программы значения, присвоенные временным переменным, будут точно такими же, как и значения, которые были бы присвоены этим переменным в случае, когда программа не подлежала бы оптимизации и выполнялась строго в последовательности расположения ее операторов. Принимается, что характеристической точкой программы является: - место, в котором оканчивается обработка аргументов функции; ,
144 Часть II. Язык - место нахождения любого из следующих операторов: конъюнкции (&&), альтернативы (II), условия (?) и соединения (,); - место, в котором завершена обработка сложного выражения, в том числе выражения, инициирующего автоматическую переменную; выражения, образующего оператор-выражение; выражения, идущего после ключевых слов if или switch, а также выражения, определяющего ход выполнения операторов цикла while, do или for. Пример #include<stdio.h> volatile Int *Ptr = (int *) ... ; tnain() { register FJx = *Ptr; printf(”Xd", *Ptr); printf(“Xd", *Ptr); return 0; } Выполнение программы приводит к двухкратному выводу значения, присвоенного „переменной, указанной через Ptr. Поскольку указываемая переменная является временной, то выводимые значения могут быть разными. В связи с этим неправильной была бы оптимизация пары операторов printf к виду printf("Xd”, Fix); printf("Xd", Fix); (Это было бы допустимо, если бы в программе не находился модификатор volatile.) 12.6 . Структурные выражения Структурное выражение представляет структуру или объединение. Обычно оно имеет вид идентификатора, но может иметь также вид присваивания или вызова функции. Структурное выражение может быть только левым аргументом структурного оператора, правым аргументом оператора присваивания и может быть заключено в круглые скобки. Пример #include<stdio.h> “ struct COMPLEX{ double Re, Im; } Str = {- 3.0, 4.0}; main() { struct COMPLEX Cut(struct COMPLEX); struct COMPLEX Tmp; Tmp « Cut(Str); printf("X If, X, If)", Tmp.Re Tmp.Im);
12,Стандартные расширения 145 return 0; } Struct COMPLEX Cut(Struct COMPLEX Par) { Par.Re = fabs(Par.Re) Par.Im = fabs(Par.Im); return Par; } 12.7 . Функции с переменным числом аргументов Функция, которая может быть вызвана с переменным числом аргументов, характеризуется тем, что в месте ее последнего параметра находится параметр ... (многоточие). Нельзя вызвать такую функцию с числом аргументов меньше числа параметров перед этим лексическим символом. Обращения к аргументам, представленным через параметр многоточие, должны проводиться посредством макровызова va—arg, первым аргументом которого является имя вспомогательной переменной типа va—list, а вторым - имя типа аргумента. Первому (в данной функции) обращению к макроопределению va—arg должно предшествовать макроопределение va—start, а после последнего обращения должен находиться макровызов va—end. Первым аргументом va—start и первым аргументом va—end является имя упомянутой вспомогательной переменной, а вторым аргументом va—start является имя параметра функции, стоящего перед параметром многоточие. Все оговоренные макроопределения имеются в файле заголовка stdarg.h. Пример #include<stdio.h> enum Type {Char.-Int, Float}; main() { void Out(int, enum Type, ...); 0ut(3, Char, 'E', 'w', 'a'); 0ut(2, Int, 1, 3); Out(l, Float, 4, 8f); return 0; } # i nclude<s tdarg.h> void Outfint Count, enum Type Sort. ... ) { va_11st Area; va_start(Area, Sort); printf("\n"); while(Count —) switch(Sort){ case Char: printf("Xc, va_arg(Area, int); break; in rase Int;
146 Часть II. Язык printf("Xd”, va_arg(Area, int)); break; case Float; printf("Xf", va_arg(Area, double)); } va_end(Area); } Выполнение программы приводит к выводу Ewa 13 4,800000
13. Нестандартные расширения Описанные здесь расширения выходят за пределы стандарта ANSI, что приводит к непереносимости, где используются эти расширения. Использование нестандартных расширений связано с использованием в программе следующих ключевых слов: pascal cdecl interrupt near аг huge asm AX _AH _AL _BX _BH _BL _CX _CH cl _DX _DH _DL _CS _DS _SS _ES _SP _BP _DI _si Ключевые слова pascal, cdecl и interrupt дают возможность смены способа вызова и генерации кода функции, а слова near, far и huge дают возможность оптимального использования указателей. Слово asm дает возможность получения в программе ассемблерных вставок, а слова, начинающиеся со знака подчеркивания, дают возможность обращения в основной программе к регистрам процессора. Поскольку перечисленные слова не принадлежат к стандарту ANSI, каждый модуль, который содержит по крайней мере одно из них, должен быть компилирован с опцией O/C/S ANSI keywords only Off 13.1. Модификаторы pascal и decl В большинстве реализаций языка Си аргументы функции обрабатываются справа налево и размещаются в стеке, увеличивающемся в направлении низ- ших адресов. Кроме этого, извлечение аргументов из стека производится вы- зывающей функцией, а не вызываемой. Эта особенность очень облегчает ис- пользование функций с переменным количеством аргументов. То же самое свойство присутствует и в Турбо Си. Тем не менее благодаря использованию модификатора pascal можно сделать так, что накопление аргументов в стеке будет происходить слева направо, а извлечение аргументов из стека будет производиться вызываемой функцией. Побочным результатом использования рассматриваемого модификатора является передача компоновщику имени функции, перед которой нет знака подчеркивания, и преобразование ее про- исходит путем замены строчных букв на заглавные. Использование модифи- катора pascal в описании переменной приводит только к замене строчных букв идентификатора на заглавные, а также к снятию перед ним знака под- черкивания. I л*
148 Часть 11. Язык Если требуется, чтобы вся программа была скомпонована так, чтобы в описании каждого идентификатора переменной и функции выступал модификатор pascal, то используют опцию компилятора O/C/Code generetion/Calling convention Pascal Пример #include<stdio.h> main() { int pascal sum(int a, int b); printf(”sum = %d", sum(2,3)); return 0; } int pascal sCmlint x, int y) { return x+y; } Выполнение программы приводит к выводу числа 5. Идентификатор sum будет передан компоновщику в виде SUM, а аргументы 2 Ъ 3 разместятся в стеке в порядке их нахождения в вызове функции. Извлечение аргументов из стека производит функция sum. Если программа была скомпилирована с опцией Calling convention Pascal, то ее отдельные функции могут быть скомпилированы, как в языке Си при условии, что перед их идентификаторами стоят модификаторы cdecl. Если программа была скомпилирована с опцией Calling convention С, то использование модификатора cdecl не будет иметь никаких последствий. Надо подчеркнуть, что если программа скомпилирована с опцией Calling convention Pascal, то все библиотечные функции должны быть описаны с модификатором cdecl. Это касается не только таких функций, как printf, но также функции main, которая всегда вызывается в языке Си. Пример #include<stdio.h> cdecl main() { pr i ntf (" I zabe la"); return 0; } Выполнение программы приводит к выводу надписи Izabela. Программа будет выполнена правильно независимо от того, была ли она скомпилирована как программа на языке Паскаль или Си. Использование описания int cdecl printff ... излишне, так как оно находится в файле заголовка stdio.h
13- Нестандартные расширения 149 13.2. Модификатор interrupt Функции, предназначенные для обслуживания прерываний, должны описываться с модификатором interrupt. От остальных функций они отличаются тем, что в стеке размещаются не только регистры BP, SP, SS, CS и IP, но и регистры АХ, ВХ, СХ, DX, SI, DI, ES и DS. Пример #include«stdio.h> #include<dos.h> idefine INT OxlC static Flag: static Count; void interrupt far timer() { if(Count ++ %18) return; Flag ++; } main() { void interrupt (far *Ref)(); auto i = 0; Ref = getvect(INT); setvect(Int, timer); while(! kbhit()){ printf(”\n%d’', i++); if(Flag) Flag — , putchar('\a’); } setvect(INT, Ref); return 0; } Выполнение программы приводит к выводу последовательных целых чисел. Приблизительно каждую секунду раздается звуковой сигнал. Программа составлена таким способом, что временной перерыв приводит к асинхронной активизации функции timer. Функции setvect и getvect использованы для изменения вектора прерываний. 13.3. Модели памяти В микрокомпьютерах IBM PC адреса оперативной памяти имеют 20 бит и составлены из двух 1б-бит частей: номера сегмента и смещения в сегменте. Если принять, что номер сегмента обозначен символом seg, а смещение символом off, то адрес определяется по формуле addr = (seg « 4) + off
ISO Часть JI. Язык Выполняя трансляцию программы на машинный язык, было бы удобно пользоваться адресами, состоящими из двух 1б-бит величин: номера сегмента и смещения в сегменте. Однако это не является оптимальным вариантом, и чаще всего сегментная часть хранится в регистре микропроцессора, а адреса данных и операторов определяются адресом, обозначенным сегментной частью. Благодаря этому адреса данных и операторов результирующей про- граммы являются 1б-бит. Ограничения на использование смещений вполне удовлетворяются для программ, у которых ни код, ни данные не превышают 64 Кбайт памяти. В случае более длинных программ необходимо пользовать- ся полными 32-бит адресами, касающимися только данных, только кода или как кода, так и данных. В зависимости от размера кода и данных программы можно выбрать одну из 6 моделей памяти: 1) модель Tiny - данные и код вместе не могут занимать более 64 Кбайт памяти. Адресация данных и кода производится с помощью 16-бит указаний. Только программы, основанные на модели Tiny, могут быть пре- образованы из вида .EXE к виду .СОМ; 2) модель Small - ни данные, ни код не могут занимать более 64 Кбайт памяти. Адресация данных и кода производится с помощью 16-бит указаний; 3) модель Medium - код может занимать более 64 Кбайт памяти. Адресация кода производится с помощью 32-бит указаний, а адресация данных - с помощью 16-бит; 4) модель 'Compact - данные могут занимать более 64 Кбайт памяти. Код не может занимать более 64 Кбайт памяти. Адресация данных производится с помощью 32-бит указаний, а адресация кода - с помощью 16-бит; 5) модель Large - как данные, так и код могут занимать более 64 Кбайт памяти. Адресация данных и кода производится с помощью 32-бит указаний; 6) модель Huge - разновидность модели Large. Отличается тем, что во время обработки сравнений и соотношений выражений указания нормализованы. Это основано на тождественном преобразовании адреса, при котором его смещение имеет значение в пределах от 0 до 15. Выбор модели памяти и, следовательно, способа адресации данных и кода производится вне программы (в интегрированном компиляторе с помощью опции Options/Compiler/Model, а в загруженном компиляторе с помощью опции -т). По умолчанию принимается модель Small. Задействование конкретной модели приводит к однозначному трактованию всех указаний переменных и функций. Если по некоторым причинам надо трактовать выбранное указание по-другому, то можно воспользоваться модификаторами адресации, выраженными с помощью ключевых слов near, far и huge. Каждый из модификаторов near, far и huge может выступать только в описании или в псевдоописании (т.е. в описании, с которого снят идентификатор). Если модификатор находится- в описании, то он может стоять только перед идентификатором функции или оператором * {звездочка). В первом случае явно или по умолчанию появление модификатора определяет, будет ли выделено место для функции в сегменте данных (near) по умолчанию или за ним (far и huge). Во втором случае использование модификатора определяет реализацию связанного с ним указания как near, far или huge. В частности, обработка описания char huge * Arr[3];
13- Нестандартные расширения 131 приведет к созданию трехэлементного массива указаний типа (char huge *). Если находящийся в описании идентификатор функции не имеет перед собой модификатора адресации, то принимается по умолчанию: near - для модели Tiny, Small, Compact far - для модели Medium, Large, Huge Аналогично для операторов ♦ (звездочка), находящихся в описаниях и псевдоописаниях, по умолчанию принимается near - для модели Tiny, Small, Medium far - для модели Compact, Large, Huge Модификатор near Указатель, описанный с модификатором near, представляется в 16-бит па- мяти. Указания переменной состоят только из смещения адреса в сегменте. Номер сегмента хранится в регистре процессора и является общим для нескольких переменных. Поскольку для указателей категории near номер сегмента всегда один, то операции сравнения и арифметические операции, относящиеся к этим указаниям, ограничиваются смещением. Пример #include<stdio.h> main() { int Fix = 13, *Ptr = & Fix, near *Ref = Ptr; printf("%d", *Ref); return 0; } Если программа будет скомпилирована в модели Small, то по умолчанию принимается, что переменная Ptr имеет тип (int near*), и выполнение программы приведет к выводу числа 13. Если программа будет скомпилирована в модели Large, то по умолчанию принимается, что переменная Ptr имеет тип (int far*), и программа будет ошибочной, так как Ptr будет представлять далекие указания (far), а переменной Ref могут быть присвоены только близкие указания (near). Модификатор far Указатель, описанный модификатором far, представляется в 32-бит памя- ти. Указания, присваемые этой переменной, состоят как из номера сегмента (более значимая часть), так и из смешения в сегменте (менее значимая часть). Использование указаний категории far дает возможность обращения к переменным и функциям в программах, где соответственно данные или код
152 Часть II. Язык занимают 64 Кбайт памяти. Требуется, однако, иметь в виду, что сравнение таких указаний не всегда дает ожидаемые результаты. Это следует из того, что при сравнении величин с помощью операторов равно (=) и не равно (! =) равенство подтверждается только, когда указания идентичны, а не только эквивалентны. В сравнениях, реализуемых с помощью остальных операторов отношения (например, >=), соотносятся между собой только смещения. Требуется также- обратить внимание, что в арифметических операциях с указателями изменению подлежит только смешение, что может привести к преобразованию указания в указание, что не эквивалентно. Пример #inc'lude<stdio.h> union{ unsigned Arr[2]; int far * Var; } Ref = {0x6e, 0x40}, Ptr = {OxOe, 0x46}; main(] { printf("%d", Xd, Xd, *Ref.Var, Ref.Var == Ptr.Var, *Ptr.Var); return 0; } Выполнение программы приводит к выводу надписи h,O,h, где h - теку- щее время. Несмотря на то что значения, присвоенные переменным Ref.Var и Ptr.Var, указывают на одну переменную (а следовательно, эквивалентны), отношение в вызове функции printf не справедливо. Это объясняется тем, что сравнение касается только смещений. Модификатор huge Указатель, описанный модификатором huge, представляется так же, как указатель, описанный модификатором far. В отличие от указаний категории far указания категории huge нормализованы, а сравнения и арифметические операции, принадлежащие этим указаниям, не ограничиваются смещениями, а касаются также номеров сегментов. Благодаря этому результаты сравнений всегда соответствуют ожидаемым. Пример ^include «stdio. h> #include<alloc. h> define COUNT 20000 long int Sum; long huge * Ptr; main( ) {
13. Нестандартные расширения 153 1f((Ptr=farea11ос(Count, sizeof (long)))==NULL){, printf("\nFailure!”); exit(13); { Ptr[Count - 1] = 13; for(i = 0; i<C0UNT;i++) Sum += Prt[i); printf("\n%ld", Sum); return o; } Выполнение программы приводит к выводу числа 10000. Если заменить модификатор huge на far, то произошел бы вывод непределенного числа. Несколько замечаний: 1. Если выбрана модель памяти Small, то Ptr - указатель, которому могут быть присвоены указания в виде переменных типа (char), a Ref - указатель, которому могут быть присвоены близкие указания таких переменных, которым могут присваиваться далекие указания переменных типа (int). char huge * Ptr; int far ** Ref; 2. Если выбрана модель памяти Large, то Ptr - указатель, которому могут быть присвоены далекие указания таких переменных, которым могут быть присвоены удаленные указания переменных типа (char). char huge ** Ptr; 3. Независимо от выбранной модели Ptr - указатель, которому могут быть присвоены близкие указания таких переменных, которым могут присваиваться далекие переменные типа (char), a Ref - переменная, которой могут быть присвоены далекие указания таких переменных, которым могут быть присвоены далекие указания переменных типа (unsigned int). char far * near * Ptr; unsigned far * far * Ref; 4. В результате принятых ограничений на расположение модификаторов near, far и huge в Турбо Си ошибочными будут следующие описания: float Arr[20000]; - поскольку (обширный) массив занимает более 64 Кбайт памяти; char far Brr[20]; - поскольку не реализованы далекие переменные, а только далекие указания.
154 Часть II. Язык Внимание: В отличие от переменных функции могут быть описаны как близкие, далекие и удаленные. Описание функции как близкой ускоряет обращение к ней, но ограничивает ее использование модулем, в котором она была описана. 13.4. Ассемблерные вставки Каждая ассемблерная вставка начинается ключевым словом asm, а заканчивается знаком; (точка с запятой}. Если точка с запятой является последним знаком строки, то этот знак может быть опущен. В строке можно записать несколько ассемблерных вставок. Ассемблерная вставка трактуется как оператор основной программы. Модуль, содержащий ассемблерные вставки, должен начинаться с директивы #pragma inline или должен компилироваться с опцией -В. Внимание: Ассемблерные вставки могут находиться только в программах, использующих загрузочный компилятор (ТСС). Пример • if(а > 5){ asm mov ah, 1; asm mov a), 2 } else asm mov ah, 3 asm mov a), 4 Опускание фигурных скобок невозможно. Последняя вставка выполняется всегда, если не относится к условному оператору. Ассемблерная вставка может иметь вид оператора или описания величины. Если она имеет вид оператора, то после ключевого слова asm идет код операции и аргументы, а если вид описания, то вместо кода операции выступает директива макроассемблера, такая, как db, dd, dw и extern. Ассемблерные вставки, имеющие вид оператора, размещаются в сегменте кода, а имеющие вид описания - в сегменте данных. Пример asm Fix db 13 asm TRUE equ -1 asm.286 asm Lab call Fun asm mov ax, word ptr[bx + 2] Ассемблерные вставки могут содержать обращения к произвольным переменным программы, но если ассемблерной вставкой является команда перехода, то нельзя обращаться к метке, содержащейся в другой ассемблерной вставке.
13. Нестандартные расширения 155 Если ассемблерная вставка содержит обращение к параметру или переменной локальной функции, то оно заменяется обращением через регистр ВР (гл. 15). Если ассемблерная вставка содержит обращения к полю структуры, то его идентификатор трактуется как литерал со значением, равным разности адреса поля и адреса структуры. В этом случае, если идентификация поля структуры не однозначна (так как существуют две структуры с одинаковыми полями), то имя поля должно быть подвергнуто преобразованию. Иногда вместо ассемблерной вставки достаточно воспользоваться символьным обозначением регистра процессора. В таком случае компиляция программы может быть выполнена также в интегрированной системе. Полный список допустимых символов регистров J6-6um регистры, тип (unsigned int) -hX, -ВХ, -СХ, -DX, -CS, —DS, -SS, -SS —SP, -BP, -DI, -SI 8-бит регистры, тип (unsigned char) -АН, -AL, -ВН, -BL -CH, -CL, -DH, -DL Примеры В комментариях дан способ трактования вставок. а) Обращения к параметрам и переменным локальных функций ♦pragma inline ♦include <stdio.h> int Global; main(argc, argv] int argc; char *argv[ ]; { int Local; asm mov ax, argc /* mov ax,[bp + 4] */ asm mov Global, ax /* mov DGROUP: -Global, ax */ asm mov ax. Global /* mov, ax, DGROUP: Global */ asm mov Local, ax /* mov [bp - 2], ax */ printf("%s = %d", argv[0]. Local];
156 Часть II. Язык return 0; } б) Обращения к полям структуры ♦pragma inline struct Girl{ char Name[10]; int Age; enum Hair {Bion, Black} Color; } Isa = {"Isabel”, 0,Black}; struct Boy{ char Name[10]; int Age; } Bob '^{"Robert"}; #include<stdio.h> main( ] { Isa.Age = 6; /* mov wordptr DGR0UP:_Isa + 10,6 */ asm mov bx, OFFSET Isa; /* mov dx, OFFSET DGR0UP:_Isa*/ asm mov ax, [bx]. (struct Girl)Age /* mov ax, [bx] + 10 */ _AX += 4; /* add ax, 4 */ asm mov Bob. (struct Boy)Age, ax /*mov DGROUP: _Bob + 10, ax */ printf("Bob is Xd", Bob.Age); return 0; } в) Использование ключевых слов, обозначающих регистры процессора int max(int х, int у) { _АХ - х asm стр ах, у asm jge done asm mov ax, у done: return AX; }
13. Нестандартные расширения 157 Внимание: Для запуска программ, содержащих ассемблерные вставки, эффективно использование опции -S, так как это приводит к получению в явном виде кода ассемблерной программы.
ЧАСТЬ III. ГРАФИКА 14. Программирование графики Для использования графической библиотеки системы Турбо Си требуется в каждом из основных модулей программы, где находится обращение к графической функции, поместить директиву include, задействующую содержание файла заголовка с прототипом функции: a) conio.h для функции текстовой графики, б) grafics.h для функции пикселной*) графики, (выбранные описания, содержащиеся в этих файлах, находятся в приложении Г). Пример. Составление и выполнение графической программы #1nc1ude<coni о.h> main( ){ { clrscr( ); gotoxy(20, 13]; cprintf("Hello, I am Jan B"J; return 0; } Порядок действий: 1) Составление файла (например, HELLO.C), содержащего текст. приведенной программы. 2) Компиляция программы в режиме Маке и ее последующее выполнение (например, клавиша Alt - R,R). Функции, находящиеся в автоматически подключаемой к программе библиотеке Graphics, обеспечивают распознавание графической среды (например, graph), инициацию пикселной и знаковой графики (например, initgraph, textmode), переключение между графическим и текстовым режимами (например, restorecrtmode), установку графических и текстовых окон (например, setviewport и window), а также вывод текстов (например, cprintf), выделение текстов (например, outtext) и выделение основных графических объектов (например, circle). Если аргументы функции не нарушают структурных требований, но подобраны несоответственно, то выполнение функции не дает никаких результатов. Если аргументы подобраны правильно, но функция не будет выполнена, то работа программы продолжается. Выяснение причины *) Пиксел - один элемент из большого массива графической информации picture element (англ.) - элемент изображения. - Прим. ред.
14. Программирование графики 159 неправильного выполнения графической операции возлагается на программиста. Эту задачу облегчает представленная далее функция graphresult. Результатом функции graphresult является величина типа integer. Если выполнение графической функции было правильным, то эта величина имеет значение 0. В противном случае она имеет отрицательную величину, идентифицирующую причину ошибки. Поскольку двукратный вызов функции graphresult (без выполнения между этими вызовами графической операции) приводит к тому, что ее вторым результатом всегда будет величина со значением 0, рекомендуется присваивание первого результата вспомогательной переменной. 14.1. Текстовый режим Выполнение каждой программы начинается в текстовом режиме. Знаки, выводимые на экране, могут быть белыми на черном фоне, черными на белом фоне, мерцающими и цветными. Вывод знаков производится с помощью каналов, связанных с пультом. Текстовое окно После начала программы по умолчанию за текстовое окно принимается весь экран. Левый верхний угол экрана с текстом имеет координаты (1,1), а правый нижний (col, lin) где col - число колонок, a lin - число строк экрана. Например, для графической платы CGA в режиме С40 координатами этой точки будут (40, 25). С окном связан курсор, положение которого может изменяться с помощью функции gotoxy. Смена текстового окна выполняется посредством функции window. Аргументами этой функции являются координаты экрана. Наименьшее окно имеет размеры 1x1 знак. Все текстовые операции относятся к текущему текстовому окну, а координаты текстового курсора всегда отсчитываются от левого верхнего угла окна. Окно имеет все особенности экрана монитора, в частности вертикальное перемещение. Пример. Вертикальное перемещение /* Scrolling */ #include<conio.h> #include<stdio.h> main(); { clrscr(); window(l, 1, 5, 3]; cprintf(”Jan") cprintf("Izabela"); return 0; Программа иллюстрирует вертикальное перемещение. На экране появляется строка с надписью Izabe и строка с завершающей надписью 1а. В результате перемещения надпись Jan исчезает из текстового окна.
160 Часть III. Графика Цвета Если монитор обеспечивает цветной вывод, то независимо могут определяться атрибуты цвета знаков и цвета фона (например, красный знак на зеленом фоне). Кроме этого, можно организовать мерцание знака и увеличить его яркость. Атрибуты мерцания, цвета фона, яркости и цвета знаков для каждого выводимого знака определяются на основе битовых полей внутренней переменной системы, называемой TextAttr. Поля мерцания и яркости однобитовые, а поля цвета фона и цвета знаков трехбитовые. Установка переменных полей может производиться с помощью функции textattr (аргумент которой присвоен переменной TextAttr) или посредством функции textbackground и textcolor. Первая из этих функций обеспечивает определение цвета фона, а вторая - цвета знаков, их яркости и мерцания. Аргументы переменных функций обычно выражаются с помощью символов, таких, как, например, RED (красный), BLINK (мерцание) и т.д. Если пользоваться платой Hercules и монохроматическим монитором, то установка атрибутов должна производиться переменной TextAttr. В рассматриваемом примере ее наиболее значимый бит определяет мерцание, три следущих - фон, еще один бит - яркость, а три наименее значимых - знаки. Кроме мерцания и увеличения яркости, имеет смысл следующий список атрибутов: Первый giun Фон Результат 7 0 Белые знаки на черном фоне 1 0 Белые подчеркнутые знаки на черном фоне 0 7 Черные знаки на белом фоне 0 0 Черные знаки на черном фоне (невидимы) Пример. Вывод знаков в цвете /* Text Colors */ #1nc1ude<con1о.h> main() { clrscr(); textcolor(RED); textbackground(GREEN); cprintff'Kajusia") return 0; На цветном мониторе, управляемом соответствующей графической платой (например, EGA или CGA), появляется красная надпись Kajusia на зеленом фоне. 14.2. Графический режим Переключение системы на графический режим производится с помощью функции initgraph.
14. Программирование графики 161 Пример. Установка графического режима /* Initialize */ # i nc1ude<graph i cs. h> # inc1ude<coniо.h> main() { int Driver, Mode; Driver - DETECT; initgraph(&Driver, &Mode,'"'); putpixel (0,0,1); wh1le(!kbhit()); closegraph(); return 0; } Выполнение программы приводит к автоматическому выходу на графический режим, переключению системы на графический режим и к выделению на экране одного пиксела. Графическое окно После установки графического режима по умолчанию графическим окном является весь экран. Левый верхний угол графического экрана имеет координаты (0,0), а правый нижний угол имеет координаты (getmaxxO, getmaxyO). Смена графического окна может быть выполнена с помощью функции setviewport. Ее аргументами являются координаты окна, а также логическое выражение, определяющее, требуется ли отсекать части текста, выходящие за пределы окна или нет. Все графические операции касаются текущего графического окна, а координаты графического курсора всегда отсчитываются от левого верхнего угла окна. Графический курсор невидим, но его текущие координаты могут быть определены с помощью функции gefx и gety. Эти координаты имеют значения величин типа (int) и могут быть как положительными, так и отрицательными. Если координата X не лежит в области 0., getxO, а координата Y - в области 0.. getyO, то курсор находится вне пределов графического окна. Пример. Отсечение на границах окна /‘Clipping*/ # i nc1ude<graph i cs.h> # inc1ude<coniо.h> fdefine clipon I main { int Driver, Mode; Driver = HERCMONO; Mode = HERCMONOHI initgraph(&Driver, SMode, ".."); setviewport(1,1,getmaxx(), getmaxyO, clipon); putpixel(-1, -1, 1); while(lkbhitO); H—764
162 Часть III. Графика closegraph(); return 0; } Поскольку функция SetViewPort вызвана с последним аргументом, имеющим значение 1 (отсечь), выполнение функции putpixel ие дает ника- ких результатов. Если этот аргумент заменить аргументом со значением О (не отсекать), то рядом с левым верхним углом графического окна (или за ним) был бы изображен один пиксел. Изображение текстов Выводимые на графический экран тексты изображаются на нем. Тексты могут быть двух видов: битовые и штриховые. Каждый знак битового текста представляет собой прямоугольник размером 8x8 бит. Качество такого знака при увеличении ухудшается. Значительно лучший визуальный эффект дает увеличенный штриховой знак. Он образуется векторным способом, и его читаемость не зависит от его размера. Имеются четыре вида штриховых знаков: индексный, тройной, простой и готический. Эти знаки могут быть не только увеличены, ио и растянуты по горизонтали и вертикали. Тексты из знаков любого вида могут выводиться по горизонтали (слева направо) или по вертикали (снизу вверх). Алфавиты штриховых видов охватывают знаки основного кода ASCII, а алфавит битового вида содержит алфавит расширенного кода ASCII. Штриховые знаки хранятся на диске и динамически загружаются в оперативную память. Одновременно в памяти может находится только один автоматически загруженный штриховой вид. Предусмотрены средства, обеспечивающие загрузку знаков по выбору (функция registerbgifont), а также их установку на постоянное использование в программе. Пример. Изображение текста 7* Texts */ #inc1ude<graph1cs.h> #include<stdio.h> main() { int Driver, Mode; Driver = HERCMOND; Mode - HERCMONOHI; initgraphf&Driver, Mode, ".."); settextsty1e(SMALL_F0NT, HORIZ_DIR. 10); outtext("Jan"); settextsty1e(G0THIC_F0NT, HORIZJHR, 8); outtext(”Ewa"); settextsty1e(TRIPLEX_F0NT, HORIZ_DIR, 8); outtextf'Iza); sleep(2); closegraphf); return 0 }
14. Прогоаммирование графики 163 Выполнение программы приводит к изображению трех текстов: первый выводится шрифтом индексного вида (SMALL-FONT), а следующие соответственно готическим (GOTHIC-FONT) и тройным (TRIPLEX—FONT) видами. Знаки текста Jan увеличиваются 10-кратио, а остальные знаки - 8- кратно. Тексты выводятся по горизонтали (HORIZ-DIR). Изображение графических объектов Библиотека графических подпрограмм содержит функции для изображения пикселов (putpixel), обрезков (line), дуг (аг), окружностей (circle), эллипсов (ellipse), а кроме того, ломаных, многоугольников, столбцов и т.д. Линии могут быть тонкими и жирными, непрерывными и прерывистыми. Пример. Изображение окружности /* Objects */ finc1ude<graphics.h> #include<conio.h> main() { int Driver, Mode; Driver - HERCHMONO; Mode - HERCMONOHI; initgraph(&Driver, &Mode, circ1e(getmaxx()/2, getmaxy()/2, 300); floodfil1(getmaxx( ), getmaxy ( )»1, getmaxcolor( )); while(ikbhit()); closegrphf); return 0; } Выполнение программы приводит к изображению окружности с радиусом 200 пикселов с центром в середине экрана с последующим его заполнением по умолчанию. Изображенный объект отсечен на границах графического окна. Цвета Изображение графических объектов может быть черно-белым или цветным. Черно-белое изображение относится обычно к бесцветным графи- ческим платам, например Hercules. Естественно, в этом случае “белый** цвет зависит от используемого люминофора и по существу может быть зеленым или янтарным. Если имеется цветное изображение (например, плата EGA или CGA), то во время установки управления графикой определяется режим работы и доступная палитра цветов. Цвета нумеруются от 0 до getmaxcolor О. Эти цвета могут быть постоянными (плата CGA) или произвольно изменяемыми (плата EGA). Если цвета могут меняться, то присваивание позиции выбранного цвета производится с помощью функции setpalette. Выбор номера цвета для изображения объектов обеспечивает функция setcolor.
164 Часть III. Графика После выполнения функции setcolor(n) все объекты изображаются в цвете, связанном (аппаратно или программно) с позицией п палитры цветов. Программное присваивание этой позиции другого цвета приводит к смене цвета рассматриваемых объектов. Пример. Цветное изображение /* GraphColors */ #inc1ude«graphics.h> finclude«conio.h> main() { int Driver, Mode; strict palettetype Palette; short int 1; Driver =«EGA; Mode EGALO; initgraph(&Driver; &Mode, if(graphresult()l - grOk) exit(13); setpalette(l, RED); setcolor(l); line(0, 0, getmaxx(), 0); whiled kbhitO); (vold)getch(); setpalettefl, GREEN); outtextxy(0, 0, "Press a key"); while(lkbhirO); (void)getch(); getpalette(&Palette); restorecrtmode(); cprintf("Palette"); for(i - 0; i«Palette.size - 1; 1 ++) cprintff'No. X 2d => X d", Palette. colors[i]); return 0; } Программа иллюстрирует принцип использования пакета цветов платы EGA. После установки графического режима и связи с позицией палитры номер 1 красного цвета (RED) изображается горизонтальный отрезок прямой линии. Нажатием произвольной клавиши клавиатуры производится смена цвета, связанного с позицией номер 1, на зеленый GREEN. Это приводит к тому, что как указанный отрезок, так и текст Press a key изображаются в этом цвете. В конце выполнения программы происходит переключение системы на текстовый режим и вывод таблицы, определяющей идентификаторы цветов, соответствующие отдельным позициям палитры. Анализ программы облегчает понимание списков библиотечных функций, размещенных в разд. 17.1.
14. Программирование графики 165 14.3. Графика BGI НаиЛучшей иллюстрацией графики BGI (Borland Graphics Interface) является специально подобранная программа. К такой категории принадлежит программа BGIdemo, содержащаяся на дистрибутивной дискетке системы Турбо Си. В несколько измененном и дополненном виде она приводится здесь с согласия фирмы Borland International. Дана ее полная распечатка и отдельные фрагменты экрана. Списки графических функций, которые использованы в программе, находятся в разделе, посвященном библиотекам: графической (разд. 17.1) и системной (разд. 17.2). SetAspectRatio denonstration Esc aborts or press a key... Рис. 9. Демонстрация SetAcpect Ratio. /‘Turbo C 2.0 */ /‘Borland graphics demo */ /‘Copyright Borland International */ /* & jb */ ♦include «dos.h» ♦include <math.h> ♦include «conio.h» ♦include «stdio.h» ♦include «stdlib.h» ♦include «stdarg.h» ♦include «graphics.h> ♦define ESC Oxlb ♦define TRUE 1 ♦define FALSE D ♦define PI 3.14159 ♦define ON 1
FillEllipse demonstration Рис. 10. Демонстрация FillEllipse. Sector demonstration Esc aborts or press a key... Рис. 11 .Демонстрация секторов.
14. Программирование графики 167 fdefine OFF О char ‘Driver Names[] = {"Detect","CGA","HCGA", "EGA", "EGA64","EGAMono", "IBH8514", "HercMono","ATT400", "VGA","PC3270"}; char*Fonts[] = {"Default Font","Tr1plexFont","SmallFont", "SansSerifFont", "GothicFont"}; char *L1neStyles[] « {"SolidLn", "DottedLn", "CenterLn", "DashedLn". "UserBitLn"}; char *F1llStyles[] = {"EmptyFill", "SolidFill", "LineFill", "LtSlashFill", "SlashFill", "BkSlashFill", "LtBkSlashFill". "HatchFill", "XHatchFill", "InterleaveFi11", "WideDotFi11", "CloseDotFi11"}; char *TextDirect[] = {"HorizDir", "VertDir"}; char *HorizJust[] = {“LeftText", "Center Text", "Right Text"}; char *VertJust[] = {"BottomText", "CenterText", “TopText"}; struct PTS{ int x, y; }; /*strucrure to hold vertex points*/ int GraphDriver; int GraphMode; double AspectRatio; int HaxX, HaxY; /* the maximum resolution of the screen */ int MaxColors; /* the maximum number of colors available */ int ErrorCode; struct palettetype palette; /* */ /* Function prototypes */ /* */ void Initialize(); void keportStatus(void); void TextDump(void); void Bar3DDemo(void); void RandomBars(void); void TextDemo(void); void Color Demo(void); void ArcDemo(void); void CircleDemo(void);
168 Часть 1П. Графика void PieDen’o(void); void BarDento(void); void LineRelDemo(void) void PutPixelDemo(void); void PutlmageDemo(void); void LineToDemo(void); void LineStyleDemo(void); void CRTModeDemo(void); void UserLineStyleDemo(void); void FillStyleDemo(void); void FillPatternDemo(void); void PaletteDemo(void); void PolyDemo(void); void SayGoodbye(void); void Pause(void); void HainWMow(char *header); void StatusLine(char *msg); void DrawBorder(void); char *GetMode(void); void ChangeTextStyle(int font, int direction, int charsize); int Display (int *xloc, int *y1oc, char *fmt. void Aspect Demo(void); void FillElJipseDemo(void); void Sector Demo(void); /* */ /* Begin main function*/ /* */ static void (*Demo[])(void) = Aspect Deme, Report Status, RandomBars, ArcDemo, FillEllilpseDemo, TextDump, TexDemo, CircleDemo, Sector Demo, Bar3DDemo, ColorDemo, PieDemo, BarDemo, LineRelDemo, PutPixelDemo, PutlmageDemo, LineToDemo, LineStyleDemo, CRTNodeDemo, UserL i neStyleDemo, FillStyleDemo, FillPatternDemo, PaletteDemo. PolyDemo, SayGoodbye int Number, Margin = 5; int main(int arse, char *argv[]) { void (*Select)(void); char *Last; Number » strtol(argv[l], &Last, 10); . if(Number = 0 &Last = argv[l]| argc == 1){ pr1ntf("\nError, try again”); exit(l); } if(I(Number > 0 &
14. Программирование графики Number<sizeof Demo /sizeof(void (*) ()))){ printf("\nWrong demo!!"}; exit (2); . } if(argc>2) Margin = strtol(argv[2], eLast, 10); if(Margin == 0 &Last == argv [2]|| Margin <0 | Margin > 15, argc > 3){ printf("XnMargin error, try again"); exit(3); } Select = DemofNumber]; Initialized; 1f(Select == PaletteDemo){ if(GraphDriver == EGA)| GraphDriver == EGA64| GraphDriver == VGA) PaletteDemof); else Number = - 1 } else Select(); closegraph(); if(Number == - 1) printf("Sorry, EGA & VGA only"); closegraph() return(O); } char Buff1200]; int Ind; void UriteBuf(char Ch) { Buf[Ind++] = Ch; } void PrintScreen(char Over) { extern void 0neL1ne(int, Int); int yL; extern FILE *Lst; char Namef] = "SCREEN**"; Name[6] = Number /10 + 'O'; Name[7] = Number % 10 + 'O'; NamefB] = '\0';
170 Часть III. Графика Lst = fopen(Name, "wb”); setviewport(0, 0, MaxX MaxY, 1); Ind = 0; Write Buf ('\xlB'); WriteBuf(*3'): WriteBuf(24); fwrite(Buf, 3, 1, Lst); forlyL -0; yL < ((getmaxyO + 8)» 3) - 1; yL++) 0neLine(yL, Over); Ind « 0; Write Buf('\x1B'); WriteBuf('0'); fwrite(Buf, 2. 1, Lst); fclose(Let); } FILE *Lst; char OneChar(int x, int yL) { static char Bits[] = {128, 64, 32, 16, 8, 4, 2, 1}; char OneByte, i; yL « = 3,- One Byte = 0; for(i = 0; 1 « == 7; i++) if(getpixel(x, yL + i)) OneByte |= Bits[i]; return OneByte; } void OneLinefint yL, int Over) { int xx, m, n; for(m = 1; m< = Over; m ++){ Ind = 0; for(n =1; n< = Margin; n++) Write Buf(' '): WriteBuf('\xlB'); WriteBuf('L'); WriteBuf(getmaxx()+1); WriteBuf((getmaxx() + »8); for(xx = 0; xx<=getmaxx(); xx++) WriteBuf(OneChar (xx, yL)); if (m < Over) WriteBuf ('\r');
14. Программирование графики 171 Status report after InitGraph Graphics device EGA (3) Graphics node EGALo (0) Screen resolution < 0, 0, 639, 199 ) Current view port < 0, 0, 639, 199 ) Clipping ON Current position < 0, 0 > Colors available 16 Current color 15 Line style SolidLn Line thickness 1 Current fill style SolidFill Current fill color 15 Current font DefaultFont Text direction HorizDir Character size 1 Horizontal justify LeftText Vertical justify TopText Esc aborts or press a key... Рис. 12. Запись состояния после InitiGraph. DefaultFont Character Set 1 "#$7Л-«/0123456789: ;< = >?0ABC[)EFGHIJKLMNO PQRSTLRWXYZ[\J''_' abcdefghijk ImopqrstuwxyzfJ }"6$ue^aA^WeiTifcE^obbuuyC>u<£¥h €n=±>< Г1*! Esc aborts or press a key... Рис. 13. Набор символов DefaultFont.
172 Часть III. Графика fwrite(Buf, Ind, 1, Lst); Ind = 0; UriteBuf(13): WriteBuf(10); fwrite(Buf, 2, 1, Lst); */ INITIALIZE: Initialize the graphics system */ */ void InitializeCvoid) { int xasp, yasp; GraphDriver = DETECT; initgraphf&GraphDriver, &GraphMode, ""); /*activ£te graphics*/ Error Code = graphresultO; iffError Code! = gr 0k){ printf(”Graphics System Error; Xs\n”, TriplexFont Character Set г'#Ш)*+г/йЖбж<=>?втЕЕСнт1К0Р0К5г LTOY^<abe(tefghijklma>pqrstuvwx)7jf Esc aborts or press a key,.. Рис. 14. Набор символов TriplexFont.
14. Программирование графики grapherrormsg(Error Code)); exit(l); } getpalette(&palette); Ma.xColors 1 getmaxcolor() + 1; MaxX = getmaxxO; MaxY = getmaxyO; /*read size of screen */ getaspectratio(&xasp, &yasp); /* read the hardware aspect */ Aspect Ratio = (double)xasp / (double)yasp; Z*get correction factor*/ ) /* /* REPORSTATUS: Report the current /* configuration of the system /* void Reportstatus(void) { */ */ */ */ struct viewporttype viewinfo; struct linesettingstype fillinfo; struct fillsettingstype fillinfo; struct textsettingstype textinfo; struct palettetype palette; char, ‘driver, ‘mode; int x, y; getviewsettings(&viewinfо); getlinesettingsf&lineinfo); getf11Isett1ngs(&fi11infо); gettextsettins(&textinfo); getpalette(&palette); x - 10; у 1 4; MainWindow("Status report after InitGraph"); settextjustify(LEFT TEXT, TOP_TEXT); driver = DriverNames[GraphDriver]; mode = GetMode; Display(&x, Ay, "Graphics device : driver, GraphDriver); Display(&x, &y, "Graphics mode : mode, GraphMode); Display(&x, by, "Screen resolution : % - 20s (Xd)". X - 20s (Xd)", (0,0, Xd, Xd)",
174 Часть III. Графика getmaxx(), getraaxyO); Display(bx, by, "Current view port : viewinfo.left, viewinfo.top, viewinfo.right, viewinfo, bottom); Disp1ay(&x, by. "Clipping : viewinfo.clip? "ON" : "OFF”); Display(&x, by, "Current position : getx(), gety(J); Display(bx, by, "Colors available Display(bx, by, "Current color Display(bx, by, "Line style : L i neStyles[1i nei nfо.11nestyle]); D1splay(bxV by, "Line thickness : lineinfo, thickness); Display(bx, by, "Current fill style : F111Styles[fiTlinfo, pattern]); Display(bx, by, ("Current fill color : Display(bx,» by, "Current font : Fonts[text1nfо.font]); Display(bx, by, "Text direction : TextD1rect[text1nfо.direct ion]); Display(bx, by, "Character size : text1nfо.chars1ze); Displayfbx, by, "Horizontal justify : HorizJust[textinfo.horiz]); Displayfbx, by, "Vertical justify : VertJust[text1nfo.vert]); Paused; } /* /* TEXTDUMP: Display all the characters /* in each of the available fonts /* void Text DumpO { static int CGASizes[] 1 {1. 3, 7. 3. 3}; static int NormSizes]] 1 {1. 4. 7. 4, 4}: char buffer[80]; inf font, ch, wwidth, Iwidth, size; struct viewporttype vp; (Xd, Xd, Xd, Xd), Xs". (Xd, Xd)”, Xd”, MaxColors); Xd”, getcolord); Xs”, Xd”, Xs", Xd, fillinfo.color); Xs", Xs", Xd”, Xs". Xs". */ */ */ */
14. Программирование графики forffont - 0; font <5; ++ font){ /* for each available font */ sprintffbuffer, "Xs Character Set", Fonts[font]); MainVindow(bufer); /* display fonthame as banner */ getviewsettings(&vp); /* read current viewport */ setttextjustify(LEFT_TEXT, T0P_TEXT); moveto(2, 3); buffer[l] - *\0'; /* terminate string */ wwidth 1 vp.right - vp.left; /* determine the window width */ Iwidth 1 textwidthf'H”); /* get average letter width */ if(font — DEFAULT_F0NT){ ChangeTextStyleffont, HORIZJJIR, 1); ch 0; while(ch<256){ /* for each possible character */ buffer[0] = ch; /* put character info a string */ outtext(buffer); /* send string to screen */ if((get x() + lwidth)>wwidth) moveto(2,gety() + textheight )"H) +3 ); ++ch /* goto the next character */ } } else{ size 1 (MaxY <200)? CGASize[font] : NormSize[font]; ChangeTextStyle (font, HORIZ IR, size); ch 1 ’I'; /* begin at first printable*/ while(ch<127){ /* for each printable character*/ buffer [0] 1 ch; /* put character into a string */ outtext(buffer); /* send string to screen */ if((get x() + lwidth)>wwidth) /* are we still in windiw? */ moveto(2, gety( ) + textheight(”H'') + 3); ++ch; /* goto the next character */ }
176 Часть IIL Графика' SnailPent Character Set |n *Z&’ () *+, /0123456789:; <=> WCDEFGHIJKLHNOPQRSTUUU XYZt\]л_' abedef ghi j klmnopqr st uvuxyzd)x Esc aborts or press a key... Рис. 15. Набор символов Smallfont. SansSerifFont Character Set !'’#$%&'()*+)-./0123456789:;<=>?@ABCDEFGHIJKLMN0PQR StUVWXYZ[\]A_'obcdefghiiklmnopqrstuvwxyzJJ~ Esc aborts or press a key... Рис. 16. Набор символов SanSerifFont.
14. Программирование графики 177 GothicFont Character Set WMTO^W'nbrilrf9l|iikbnnBpi|rstowa)z5^" Esc aborts or press a key... Рис. 17. Набор символов GothicFont. } Pause(); /* /* /* BAR3DDEM0: Display a 3-D bar chart */ */ void Bar 3DDemo (void) { static int barheight[] = {1, 3, 5. 4, 3, 2, 1, 5, 4. 2, 3}; struct viewporttype vp; int xstep, ystep; int 1, j, h, color, bheight; char buffer[10]; MainVindow("Bar 3-D/Rectangle Demonstration''); h1 3 *textheight(“H''); 12—764
178 Часть 111. Графика getviewsettings(&vp); settextjust1fy(CENTER_TEXT, TOP_TEXT); ChangeTextStyle(TRIPLEX_FONT, HORIZ_DIR, 4); outtextxy(MaxX/2, 6, "These are 3-D Bars"); ChageTextStyle(DEFAULT_FONT, HORIZJHR, 1); setviewport(vp.left + 50, vp.top + 40, vp.right-50, vp.bottom-10, 1); get v i ewset 11 igs (&vp); line(h, h, h, vp.bottom-vp.top-h); line(h, (vp.bottom-vp.top)-h, (vp.right-vp.left)-h, (vp.bottom-vp.top)-h); xstep = ((vp.right-vp.left) - (2*h)) /10; ystep = ((vp.bottom-vp.top)) - (2*h))/5; j ” (vp.bottom-vp.top) - h; settextjust1fу(CENTER_TEXT, CENTER_TEXT); for(i = o; i<6; ++ 1){ 11ne(h/2, j, h, j); itoa(i, buffer, 10); outtextxy(0, j, buffer); j -= ystep; ) j = h; settextjustify(CENTER_TEXT, TOP_TEXT); for (1=0, 1 < 11; ++i){ color = random(MaxColors); setflllstyle(i + 1, color); linefj, (vp.bottom-vp.top)-h, j, (vp.bottom-vp.top-3)-(h/2)); 1toa(i,buffer, 10); outtextxyfj, (vp.bottom-vp.top)-(h/2), buffer); if (1 != 10){ bheight = (vp.bottom-vp.top) - h - 1; bar 3d (j. (vp. bottom-vp. top-h) - (barhe-i ght [ 1 ]*ys tep), j+xstep, bheight, 15, 1); } j += xstep; } Paused; /* */ /* RANDOMBARS: Display random bars */ /* */ void RandomBars(void) { Int color;
14. Программирование графики HainVindowf'Random Bars"); StatusLinef'Esc aborts or press a key..."); /* put msg at bottom of screen*/ while(lkbhit()){ /* until user enters a key ...*/ color = random(HaxColors-l) + 1; setcolor(color); setfillstyle(random(ll) + 1, color); bar3d(random(getmaxx()), random(getmaxy()), random(getmaxx()), random(grtmaxy()), 0, OFF); } Paused; TEXTDEMO: Show each font in several sizes to the user void TextDemo(void) { int charsize[] = (1, 3, 7, 3, 4); int font, size; int h, x, у, 1; struct viewporttype vp; char buffer[80j; for(font = 0; font <5; ++ font){ /* for each of the four fonts */ sprintf(buffer, "%s Demonstration", Fonts[fontJ); HairWindow(buffer); getviewsettings(&vp); ChangeTextStyle(font, VERT_DIR, charsize[font]); settextjust ify(CENTER_TEXT, BOTTOM_TEXT); outtextxy(2 * textwidthf'H"), vp.bottom - 2 * textheight ("M”), "Vertical —> ”); ChangeTextStyle(font, HORIZDIR, charsize[font]); settextjustily(LEFT_TEXT, TOP_TEXT); outtextxy(2 * textwidthf'H"), 2, "Horizontal -->"); settextjustify(CENTER_TEXT, CENTERTEXT); x = (vp.riht - vp.left) /2; у = textheight f'H");
180 Часть 1П. Графика for (1 - 1; 1 < 5; ++1){ /* for each of the size */ size - (font — SMALL.FONT) 1 1 + 3: 1; ChangeTextStyle(font, HORIZ_DIR, size); h - textheight("H"); у + 1 h; sprintf(buffer, "Size Xd”, size); outtextyfx, y, buffer); } if(font !- DEFAULT-FONT){ /* show user declared font size */ у + « h/2; /* mpve down the screen */ settext just if у (CENTERJEXT, T0P_TEXT); setusercharsize(5, 6, 3, 2); ChangeTextStyleffont, HORIZJHR, USER_CHAR_SIZE); outtextxy(vp.right-vp.left)/2, y, "User Defined Size"); } Paused;» } } /* */ /* COLORDEMO: Display the current */ /* color palette on the screen */ /* */ void Color Demo(void) { struct viewporttype vp; int color, height, width; int x, y, 1. j; char cnum[5); MainWindow("Color Demonstration"); /* show demonstration name */ color 1 1; getviewsettings(&vp); /* get the current window size */ width - 2 * ((vp.right + 1) / 16); /* get box dimensions */ height 1 2 * ((vp.bottom-10) / 10); x width /2; у - height /2; /* leave 1/2 box border */ for (j - 0; j « 3; ++ j){
14. Программирование графики 181 Ваг 3-D / Rectangle Denonstration These are 3-D Bars 5 4 3 2 1 0 Esc aborts or press a keg... Рис. 18. Демонстрация трехмерной прямоугольной диаграммы Randon Bars Esc aborts or press a keg... Рис. 19. Случайные диаграммы
182 Часть III. Графика Horizontal —> DefaultFont Denonstration Size 1 Size 2 Size 3 Size 4 n й Esc aborts or press a key... Рис. 20. Демонстрация DefaultFont. /* row loop */ for i = 0; i < 5; ++ i){ /* column loop */ setfillstyle(SOLID_FILL, color); setcolor(color); bar(x, y, x+width, y+height); /* draw the rectangle */ rectangle(x, y, x+width, y+height); /* outline the rectangle */ if(color == BLACK){ setcolor(WHITE); rectangle(x, y, x+width, y+height); /* outline black in white */ itoa(color, cnum, 10); outtextxyfx + (width/2), y+height + 4; cnum); /* show color # */ color » ++ color X HaxColors; /* advance to the next color */ x + = (width /2) * 3; /* move the column base */
14. Программирование графики } /* end of column loop */ у + = (height /2) * 3; /* move the row base */ x = width /2; /* reset column base */ } /* end of row loop */ Pausef) } ARCDEHO: Display a random pattern of arcs void ArcDemo(void) { int mradius; /* maximum radius allowed * int aengle; struct arccoordstype ai; Ma 1 nWindow ("Ar c Demonstration"); StatusLine("ESC Aborts - Press a Key to stop"); mradius = MaxY /10; /* determine the maximum radius */ while(!kbhit()){ /* repeat until a key is hit */ setcolor (random(MaxColors - 1) + 1); 7* randomly select a color */ eangle = random (358) + 1; /* select an end angle */ arc(random(MaxX), random (MaxY), random(eangle), eangle, mradius); getarccoords(& ai); /* read Cord data */ Hne(ai. x,ai.y.xstart, ai.ystart); /* line from start to cernter */ line(ai. x, ai.y, ai.xend, ai.yend); /* line from end to center */ } Pause(); } /* /* CIRCLEDEMO: Display a random /* pattern of circles /* void CircleDemo(void)
184 Часть П1. Графика int inradius; /* maximum radius allowed * Ma i nWi ndow("C i rcle Demonstrati on"); StatusLinef'ESC Aborts - Press a Key to stop"); mradius = MaxY /10; /* determine the maximum radius */ whi1e(lkbhit()){ setcolor (random(MaxColors - 1) + 1); /* randomly select a color */ circle(random(MaxX), random(MaxY), random(mradius) ); } Paused; /* /* /* PIEDEMO: Display a pie chart */ */ */ /define adjasp(y) ((int)(AspectRatio * (double)(y))) /define torad(d) (((double)(d) * PI) /180.0) void PieDemo(void) struct viewporttype vp; int xcenter, ycenter, radius, Iradius; int x, y; double radians; MainWindow(”Pie Chart Demonstration"); getv i ewsett i ngs (&vp) ; I* get the current viewport */ xcenter = (vp.right - vp.left)/?; /* center the Pie */ ycenter = (vp.bottom - vp.top)/?+20; radius = vp.bottom - vp.top)/3; Iradius = radius + (radius /5); /* labels placed 20% farther */ if (radius < 75){ radius = (radius*2)/3; Iradius - = radius/4; ChangeTextStyle(TRIPLEX_FONT, H0RIZ_DIR, 4); settextjustify(CENTER_TEXT, TOPTEXT); outtextxy(MaxX/2, 6, "This is a Pie Chart"); if (raunds <75) ChangeTextStyle(TRIPLEX, HORIZDIR, 2); else
14. Программирование графики ChangeTextStyle(TRIPLEX, HORIZJHR, 3); settextjustify(CENTER_TEXT, TOP_TEXT); setflllstyle(SOLID_FILL, RED); pieslice(xcenter + 10, ycenter-adjasp(lO), 0, 90, radius); radians = torad(45); x * xcenter + (int)(cos(radians) ‘(double) Iradius); у « ycenter - (int)(sin(radians) * (double)Iradians * Aspect Ratio); settextjustify(LEFT_TEXT, BOTTOM_TEXT); outtextxy(x, y, ”25 %"); setfillstyle(WIDE_DOT_FILL, GREEN); pieslice(xcenter, ycenter, 90, 135, radius); radians = torad(113); x = xcenter + (int)(cos(radians) ‘(double) Iradius); у = ycenter - (int)(sin(radians) * (double)Iradians * Aspect Ratio); settextjustily(RICHT_TEXT, BOTTOHTEXT); outtextxyfx, y, ”12.5 %”); setfi1IstylefINTERLEAVEFILL, YELLOW); settextjustily(RIGHT_TEXT, CENTER_TEXT); pieslice(xcenter-10, ycenter, 135, 225, radius); radians = torad(lBO); x = xcenter + (int)(cos(radians) ‘(double) Iradius); у = ycenter - (int)(sin(radians) * (double)Iradius * Aspect Ratio); settextjustify(RICHT_TEXT. CENTERTEXT); outtextxy(x, y, ”25 %"); setfillstyle(HATCH_FILL, BLUE); pieslice(xcenter, ycenter, 225, 360, radius); radians = torad(293); x = xcenter + (int)(cos(radians) * (double)Iradius); у = ycenter - (int)(sin(radians) * (double)Iradius) * Aspect Ratio); settextjustify(LEFT_TEXT. TOP_TEXT); outtextxy(x. y, ”37.5%”); Paused; } /* */ /* BARDEMO: Draw a 2-D bar chart */ /* using Bar and Rectangle */ /* */ void BarDeino(void) { int barheight[] = {1, 3, 5, 2, 4 }; int styles[] =
186 Часть III. Графика {1, 3, 10, 5, 9, 1 }; int xstep, ystep; int sheight, swidth; int i, j, h; struct viewporttype vp; char buffer[40]; MainWindow("Bar/Rectangle demonstration”); h = 3 * textheight ("H"); getv1ewsettings(&vp); settext justify (CENTER JEXT, TOP_TEXT); ChangeTextStyle(TRIPLEX FONT, H0RIZJ5IR, 4); outtextxy(MaxX/2, 6, "These are 2-D Bars"); ChangeTextStyle(DEFAULT_FONT, H0RIZ_DIR, 1); setviewportjvp. left + 50, vp. top+30, vp.right-50, vp.bottom-10.1); getv i ewsett1ngs(&vp); sheight - vp.bottom - vp.top; swidth = vp.right - vp.left; line(h, h, h. sheight - h); line(h, sheight-h, sheight-h, sheight-h); ystep » (slieighr - (2*h))/5; xstep = (swidth - (2*h))/5; j - sheight - h; settext justify(CENTERJEXT, CENTERJEXT); for(i = 0, i<6; ++i){ Hne(h/2, j, h, j); itoa(i, buffer, 10); outtextxy(0, j, buffer); j -= ystep; } j - h; settext just if у (CENTERJEXT, TOPJEXT); for(i = 0, i<6; ++i){ setfil1style(styles[i], random(MaxColors)); line(j, sheight-h, j,sheight-3-(h/2)); itoafi, buffer, 10); outtextxy(j, sheight-(h/2), buffer); if (i! = 5){ bar(j, (sheight-h)-(j>arheight[i] * ystep); j+xstep, sheight - h - 1); regtangle(j, (sheight-h)-(barheight[i] * ystep), j + xstep, sheight - h); } j + = xstep; } Paused; }
14. Программирование графики ___________________TriplexFont Demonstration horizontal —> I Size 1 н Size 2 о Size 3 £ Size 4 fer DeW Size Esc aborts or press a key... Рис. 21. Демонстрация TriplexFont SnallFont Demonstration Horizontal —> Л Size t [ Size 5 I Size 6 Size 7 Ф User Dffin?d йи U L (D Э Esc aborts or press a key... Рис. 22. Демонстрация SmallFont
188 Часть IH. Графика /* */ /* LINERELDEMO: Display pattern using */ /* moverel and Hnerel ends */ /* */ void LineRel(void) { struct viewporttype vp; int h, w, dx, dy, ex, cy; struct PTS outs[7]; MainWindowf'MoveRel/LineRel Demonstration"); StatusLine("Press any key to continue, ESC to Abort"); getviewsettings(&vp); ex = (vp.right - vp.left)/2; /* center of the screen coords */ cy = (vp.bottom vp.top)/2; h » (vp.bottom - vp.top)/8; w » (vp.right - vp.left)/9; dx = 2 " w; dy = 2 * h; setcolor(BLACK);
14. Программирование графики setfi11style(S0LID_FILL, BLUE); bar(O, O,vp.right - vp.left, vp.bottom - vp.top); f* draw background */ outs[0].x - ex - dx; outs[0].y “ cy - dy; outs[l].x = ex - (dx-w); outs[l].y - cy - (dy + h); outs[2].x « ex + dx; outs[2].y = cy - (dy + h); outs[3].x = ex + dx; outs[3].y = cy + dy; outs[4].x = ex + (dx-w); outs[4].y = cy + (dy + h); outs[5].x = ex - dx; outs[5].y = cy + (dy + h); outs[6].x “ ex - dx; outs[6].y = cy - dy; setfillstyle(SOLID_FILL, WHITE); fillpoly(7, (int far *) outs); outs[0].x = ex - (w/2); outs[0].y = cy + h; outs[l].x = ex + (w/2); outs[l].y = cy + h; outs[2].x = ex + (w/2); outs[2].y = cy - h; outs[3].x = ex - (w/2); outs[3].y = cy - h; outs[4].x = ex - (w/2); outs[4].y = cy + h; setfl11style(S0LID_FILL, BLUE); fillpoly(5, (int far *)outs); /* /* Draw a Tesseract object using */ linerel and moverel */ moveto(cx - dx, cy - dy); linerel(w, - h); linerel(3*w, 0); linerel(0, 5*h); linere1((-w, h); linerel(-3*w, 0); linerel(0, -5*h); moverel(w, - h); linerel(D, 5*h); 1inerel(w+(w/2), D); 11nerel(0, -3*h); Hnerel(w/2, - h); linerel(0, 5*h);
190 Часть III. Графика moverel(0, -5*h); Нпеге1(-(»н-(и/2)), 0); linerel(0, 3*h); linere1(-w/2, h); moverel(w/2, -h); linerel(w, o); moverel(0, -2*h); linerel (-w. 0); Pause(]; } /* » /* PUTPIXELDEMO: Display a pattern of random dots /* and pick them back up again void PutPixel Demo(void) { int seed = »195B; int i, x, y, h, w, color; struct viewporttype vp; MainWindow("PutPixel / GetPixel Demonstration"); getviewsettings(&vp); h = vp.bottom-vp.top; w - vp.right - vp.left; srand(seed); /* restart random number function */ for(i = 0; i < 5000; ++i){ /* put 5000 pixels on screen */ x = 1 + random(w - 1); /* generate a random location */ у = 1 + random(h - 1); color = random(MaxColors); putpixel(x, y, color); } srand(seed); /* restart random number at same number.*/ for(i - 0; i < 5000; ++i){ /* take the 5000 pixels off */ x = 1 randomfw -1); /* generate a random location */ У = 1 random(h -1 );
14. Программирование графики 191 Рис. 24. Демонстрация готического шрифта. Color Denonstration Рис. 25. Демонстрация цвета.
192 Часть HI. Графика color + getpixel(x, у); /* read the color pixel */ iffcolor == random(MaxColors)) /* used to keep RANDOM in sync */ putpixel(x, y, 0); /* write pixel to BLACK */ } Pause(); } /* */ /* iPUTIMAGEDEMO */ /* */ void PutlmageDemo(void) { static int r = 20; static int StartX = 100; static int StartY = 50; struct viewporttype vp; int PauseTime, x, y, ulx, uly, Irx, Iry, size, i, width, height, step; void *Saucer; Рис. 26. Демонстрация дуг.
14, Программирование графики Main WindowCGetlmage/Putlmage Demonstration"); getv i ewsett i ngs(&vp); /* Draw Saucer */ setcolor(WHITE); ellipsefStartX, StartY, 0, 360, r, (r/3)+2); ellipse(StartX, StartY-4, 190, 357, r, r/3); circle(StartX+10, StartY-12, 2); circle(StartX-10, StartY-12, 2); setcolor(WHITE); line(Start+7, StartY-6, StartX+10, StartY-12); line(Start-7,'StartY-6, StartX-10, StartY-12); setfi11style(S0LID_FILL, WHITE); floodfill(StartX+l, StartY+4, WHITE) /* Read saucer image */ ulx = StartX-(r+l); ulx = StartY-14; Irx = StartX+(r+l); Iry = StartY+(r/3)+3; width = Irx - ulx + 1; height = Iry - uly + 1; size = 1magesize(ulx, uly, Irx, Iry); Saucer - malloc(size); getimage(ulx, uly, Irx, Iry, Saucer); putimage(ulx, uly. Saucer, X0R_PUT); /* Plot some "stars” */ for(i = 0; i < 1000; ++i) putp ixe1(random(MaxX), random(MaxY), random(MaxColors-l) +1); x - MaxX /2; у = MaxY /2; PauseTime = 70; putimage(x, y, Saucer, X0R_PUT); /* draw the saucer */ while(! kbhit() ){ delayfPause Time); putimage(x, y. Saucer, XORPUT); /* erase the saucer */ /* Move Saucer */ step = random(2*r); if((step/2)X 2! = 0)
194 Часть III. Графика step = -1, * step; x = x + step; step = random(r); if((step/2) *21 = 0) step - 1 • step; у = У + step; If(vp.left + x + width - 1 > vp.right) x = vp.right-vp.left-width + 1; else if(x < 0) x = 0; iffvp.top + у + height - 1 > vp.bottom) у = vp.bottom-vp.top-height + 1; else ifСУ < fi) у = 0; putimage(x, y. Saucer, X0R_PUT); } free(Saucer); Pause(); } /* /* LINETODEMO: Display a pattern /* using moveto and lineto commands /* «define MAXPTS 15 void LineToDemo(void) { struct viewopttype vp; struct PTS point[MAXPTS] int i, j, h, w, xcenter, ycenter; int radius, angle, step; double rads; Ma i nWi ndow("MoveTo/L i neTо Demonstrat1on"); getviewsettings(&vp); h - vp.bottom - vp.top; w - vp.right - vp.left; xcenter - w/2; /* determine the center of circle */ ycenter - h/2; radius(h~30)/(AspectRatio * 2); step - 360/MAXPTS; /* determine number of increments */
14. Программирование графики angle = 0; /* begin at zero degrees */ for(i - 0; i < MAXPTS; ++i){ /* determine circle intercepts */ rads « (double)angle * PI /180.0; /* convert angle to radians */ points{i].x » xcenter + (int)(cos(rads) * radius); points{i].y = ycenter - (int)sin(rads) * radius * Aspect Ratio); angle += step; /* move to next increment */ } circle(xcenter, ycenter, radius); /* draw bounding circle */ for(i = 0; i< MAXPTS; ++i){ /* draw the cords to the circle */ for(j=i; j<MAXPTS; ++j){ /* for each remaining intersect */ moveto(points[i].x, points[i].y); /* move to beginning of cord*/ lineto(points[j].x, points[j].y); /* draw the cord */ } } Pause(); } /* */ /* LINESTYLEDEMO: Display a pattern */ /* using all of the standard linestyles */ /* */ void LineStyleDemo(void) { int,style,step; int x, y, w; struct viewporttype vp; char buffer[40]; Main Window("Pre-defined line styles”); getviewsettings(&vp); w = vp.right - vp.left; x - 35; У • 10; step = w/11;
196 Часть III- Графика Рис. 27. Демонстрация кругов.
197 14. Программирование графики settextjustify(LEFT_TEXT, ТОР_ТЕХТ); outtextxy(x, у, "Normal Width”); settextjusti fу(CENTER_TEXT, T0P_TEXT); for(style=0; style<4; ++style){ setlinestyle(style, 0, NORM WIDTH); line(x, y+20, x, vp.bottom-40); itoa(style, buffer, 10); outtextxyfx, vp.bottom-30, buffer); x += step; } x += 2 * step; settextjustify(LEFT_TEXT,TOP_TEXT); outtextxy(x, y, "Thick Width"); settextjustify(CENTER_TEXT,TOP_TEXT); for(style=0; style<4; ++sty1e){ setlinestyle(style, 0, THICK_WIDTH); 1ine(x, y+20, x, vp.bottom-40); itoa(style, buffer, 10); outtextxy(x, vp.bottom-30, buffer); x += step; } Bar / Rectangle demonstration These are 2-D Bars Рис. 29. Демонстрация двумерной прямоугольной диаграммы.
198 Часть HI. Графика settextjustify(LEFT_TEXT,TOP_TEXT); Pause(); } /* */ /* CRTMODEDEMO: Demonstrate the effects */ /* of the change mode commands */ /* on the current screen */ /* */ void CRTHodeDemo(void) { struct viewporttype vp; Main Windok(("SetGraphMode/ RestoreCRTMode demo"); getviewsettings(&vp); settextjust ifу(CENTER_TEXT,CENTER_TEXT); outtextxy(vp.right-vp.left)/?,(vp.bottom-vp.top)/?, "Now you are in graphics mode..."); StatusLine("Press any key for text mode...”); getch(); res torecrtmode(); printff'Now you are in text mode.\n\n"); printf("Press any key to go back to graphics...”); getch(); setgraphmode(getgraphmode()); NainWindow("SetGraphMode/ RestoreCRTMode demo”); settextjusti fу(CENTERTEXT,CENTER_TEXT); outtextxy((vp.right-vp.left)/?, (vp.bottom-vp.top)/?, "Back in Graphics Mode..."); Pause(); /* */ /* USERLINESTYLEDEMO: Display line styles */ /* showing the user defined line style functions */ /* */ void UserLineStyleDemo(void) { int x, y, i, h, flag; unsigned int style; struct viewporttype vp; MainWindow("User defined line styles”);
14. Программирование графики getv iewsettIngs(&vp); h = vp.bottom - vp.top; x = 4; У = 10; style « 0; i =0; settextjustify(CENTER_TEXT,TDP_TEXT); flag = TRUE; /* set the bits in this pass */ while (x< vp.right - 2){ /* draw lines across the screen */ if(flag) style = style| (1 « i); /* set the Ith bit in word */ else style = style &l (0x8000) » i); /* clear the Ith bit in word */ setlinestyle(USERBIT_LINE, style, NORH_WIDTH); 1ine(x, y, x, h-y); /* draw the new line pattern */ x += 5; /* move the X location of line */ i = ++i % 16; /* advance to next bit pattern */ if (style == Oxfff){ /* are all bits set ? */ flag = FALSE; /* begin removing bits */ 1 = 0; /* start with whole pattern */ } else{ if (style == 0) /* are all bits clear? */ flag = TRUE; /* begin setting bits */ } } settextjust ify(LEFT_TEXT,T0P_TEXT); Pause(); }
200 Часть III. Графика /* */ /* FILLSTYLEDEMO: Display the standard */ /* fill patterns */ /* */ void FillStyleDemo(void) { int h, w, style; int i, j, x, y; struct viewporttype vp; char buffer[40]; W MainWindow(”Pre-defined Fill Styles”); getv iewsett i ngs(&vp); w * 2 * ((vp.right + 1) /13); n = 2 * ((vp.bottom - 10)/10); • x = w/2; У= h/2; style = 0; HoveRel / LineRel Demonstration Рис. 30. Демонстрация MoveRel/LineRel.
14. Программирование графики 201 PutPixel / GetPixel Denonstration Esc aborts or press a key... Рис. 31. Демонстрация PutPixel/GetPixel. Рис. 32. Демонстрация Getlmage/Putlmage.
202 Часть III. Графика for(j = 0; j<3; ++j){ /* three rows of boxes */ for(i = 0; i<4; ++i){ /* four column of boxes */ setfillstyle(style, MaxColors-1); /* set the fill style and WHITE */ bar(x, y, x+w, y+h); /* draw the actual box */ rectangle(x, y, x+w, y+h); /* outline the box */ itoa(style, buffer, 10); /* convert style 3 to ASCII */ outtextxy(x+(w/2), y+h+4, buffer); ++style; /* gg, on to next style number */ x += (w/2)*3; /* go to next column */ } /* end of column loop */ x = w/2; /* put „base back to 1st column */ у += (h/2)*3; /* advance to next row */ } /* end of row loop */ settextjustify{LEFT_TEXT,TOP_TEXT); Paused; } /* */ /* FILLPATTERNDEMO: Demonstrate how to use */ /* the user definable fill patterns */ /* */ void Fill PatternDemoc(void) { int style int h, w; int x, у, 1, j; char buffer[40]; struct viewopttype vp; static char patterns[][8] = { { OxAA, 0x55, OxAA, 0x55, OxAA. 0x55, OxAA, 0x55 }, { 0x33, 0x33, OxCC, OxCC, 0x33, 0x33, OxCC, OxCC }, { OxFO, OxFO, OxFO, OxFO, OxOF, OxOF, OxOF, OxOF }, { 0x00, 0x10, 0x28, 0x44, 0x28, 0x10, 0x00, 0x00 }, { 0x00, 0x70, 0x20, 0x27, 0x24, 0x24, 0x07, 0x00 },
14. Программирование графики { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00 } { 0x00, 0x00, ОхЗС, ОхЗС, ОхЗС, ОхЗС, 0x00, 0x00 } { 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00 } { 0x00, 0x00, 0x22, 0x08, 0x00 0x22, OxlC, 0x00 } { OxFF, 0x7E, ОхЗС, 0x18, 0x18, ОхЗС, Ox7E, OxFF } { 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00 } { 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00 } MainWindow("User Defined Fill Style"); getviewsettings(&vp); w • 2 *((vp.right+l) /13); h - 2 *((vp.bottom -10)/10); x • w/2; у - h/2; style = 0 for(j=0; j<3; ++j){ /* three rows of boxes */ for(i = 0; 1<4; ++i){ /* four column of boxes */ setfillpettern(8patterns[style][0], MaxColors-1); bar(x, y, x+w, y+h)); /* draw the actual box */ rectanglefx, y, x+w, y+h); /* outline the box */ itoa(sty1e, buffer, 10); /* convert style 3 to ASCII */ outtextxy(x+(w/2), y+h+4, buffer); ++style; /* go on to next style number */ x += (w/2)*3; /* go to next column */ } ' /* end of column loop */ x = w/2; /* put base back to 1st column */ у += (h/2)*3; /* advance to next row */ } /* end of row loop */ settextjustify(LEFT_TEXT, TOP_TEXT); Paused; } /* . */ /* POLYDEMO: Display a random pattern of polygons */ /* »/ void PalletteDemo(void) { int 1, j, x, y, color;
204 Часть III. Графика struct viewporttype vp; int height, width; Ha1nW1ndow("Palette Demonstrat1on"); StatusLine("Press and key to continue, ESC to Abort"); getviewsett1ngs(&vp); width « (vp.right - vp.left)/15; /* get width of the box */ height » (vp.bottom - vp.top /10; /* get the height of the box */ x « у « 0; /* start In upper corner */ color • 1;., / begin at 1st color */ for(j - 0; j<10; ++j){ /* for 10 rows of boxes */ for(1 » 0; 1<15; ++1){ /* for 15 columns of boxes */ setflllstyle(SOLID_FILL, color++); /* set the color of box */ bar(x, y, x+wldth, y+helght); /* draw the box */ x += width + 1; /* advance to next col */ color • 1 + (color X (HaxColors - 2)); /* set new color */ } /* end of column loop */ x - 0; /* goto 1st column */ у += height + 1; /* goto next row */ } /* end of row loop */ whllefl kbh1t()){ setpa1ette(l+random(HaxColors - 2), random(65)); } setalipalette(&palette); Paused; } /* */ /* POLYDEHO: Display a random pattern of polygons */ /* . */ ♦define HaxPts 6 /* maximum number of points */
14. Программирование графики 205 void PolyDemo(void) { struct PTS poly{MaxPts]; Int color; int i; Ma1 nW1ndow(”DrawPoly/F11IPoly Demonstrat1on”); StatusLine("ESC Aborts - Press a Key to stop”); whi!e(l kbhit()){ color = 1 + random(MaxColors-l); /* get a random color number (no black) */ setfillstyle(random(10), color); /* set a random line style */ setcolor(color); /* set the desired color */ for(i = 0; 1<(MaxPts-l); 1 ++) { /* determine a random polygon */ poly[i].x = random(MaxX); /* set coords */ poly[i].y “ random(MaxY); } poly[i].x = poly[0].x; /* last point = first point */ poly[i].y = poly[i].y; HoueTo / Line!о Denonstration Esc aborts or press a keg... Рис. 33. Демонстрация MoveTo/LineTo.
206 Часть 111. Графика Pre-defined line stales Kamal Width Thick Width О Д 2 3 0 1 2 3 ~~~ Esc abo ts or press а кем. . Рис. 34. Демонстрация типа линий. SetGraphHode / RestoreCRTMode dena Back in Graphics Mode, Esc aborts or press a keg.. Рис. 35. Демонстрация SetGraphMode/RestoreCRTMode.
14. Программирование графики fillpoly(MaxPts, (Int far *) poly); /* Draw Polygon */ } Pause(); } /* */ /* SAYGOODBYE: Give a closing screen */ /* */ void SayGoodbay(void) { struct viewporttype viewinfo; int h, w; MainWindow( ” — Finale ==”); ge tv i ewset 11 ngs (&v 1 ew 1 nf о); /* read viewport settings */ ChangeTextStyle(TRIPLEX_FONT, HORIZ_DIR. 4); settextjustify(CENTER_TEXT, CENTERTEXT); h-* view info, bottom - viewinfo.top; w • viewinfo.right - viewinfo.left; outtextxy(w/2, h/2, "That's all folks!"); StatusLine("Press any key to EXIT”); getch(); Pause)); } /* */ /* GETHODE: Convert the current device mode */ /* into a string for Displaying in status */ /* */ char * GetMode(void) { static char buffer[40]; sw1tch(GraphDriver){ /* Determine the mode setting */ case CGA: 1f(GraphMode ==CGAHI) return ("CGAHI"); else sprintf(buffer, "CGACXd", GraphMode); return(buffer); }
208 Часть III. Графика case MCGA; switch!GraphMode){ case MCGAMED: returnC'MCGANed); case HCGAHI: returnC'MCGAHi”); default: sprintf(buffer, "MCGACXd", GraphMode); return(buffer); } case EGA: sw1tch(GraphMode) case EGALO: return("EGALo”); case EGAHlf returnf'EGAHi”); } break; case EGA64: sw1tch(GraphMode){ case EGA64L0: return("EGA64Lo"); case EGA64HI: return(”EGA64Hi"); } break; case HERCMONO: returnC'HercMonoHi"); case EGAMONO: returnC'EGAMonoHi"); case PC327O: return("PC3270Hr'); case ATT400: sw 1tch(GraphMode){ case ATT400MED: return("ATT400Med"); case ATT400HI: return("AT400H1"); default: sprintf(buffer, "ATT400C%d", GraphMode); return(buffer); } case IBM8514: sw i tch(GraphMode){ case IBM8514L0: return(’’IBM8514Lo");
14. Программирование графики case IBM8514HI: return("IBM8514Hi“); } break; case VGA: sw1tch(GraphMode){ case VGALO: return("VGALo"); case VGAMED: return ("VGAMed"); case VGAHI: return("VGAHi"); } break; } returnC'Unknown"); /* return as a default value */ } /* /* PAUSE: Pause until the user enters a keystroke /* void Pause(void) { static char msg[] • "Esc aborts or press a key..."; StatusLine(mgs); /* put msg at bottom of screen */ switch(getch()){ case ESC: { closegraph(); exit(l); /* return to DOS*/ } case ' ' ; if(getch() = = ' ' ) Print Screen(l); } cleardevice } /* /* MAINWINDOW: Establish the main window /* void HainWindow(char * header) { int height: —764
210 Часть III. Графика cleardeviceO; /* clear graphics screen */ setcolor(MaxColors - 1); /* set current color to white */ setviewport(0, 0, MaxX, MaxY, 1); /* open port to full screen */ height • textheight("H"); /* get basic text height */ ChangeTextStyle(DEFAULT_FONT, HORIZ.DIR, 1); settextjustify(CENTER_TEXT,TOP_TEXT); outtextxy(MaxX/2, 2, header); setviewport(0. height + 4, MaxX, MaxY -(height + 4), 1); DrawBorder(); setviewport(l, height+5, MaxX-1, MaxY-(height+5), 1); } /* */ /* STATUSLINE: Display a status line */ /* at the bottom of the screen */ /* */ void StatusLine(char *msg) { int height; setviewport(0, 0, MaxX, MaxY, 1); /* open port to full screen */ setcolor(MaxColors-l); /* set current color to white */ Change TextStyle(DEFAULT_FONT, H0RIZ_DIR, 1); settextjust 1fу(CENTER_TEXT, TOP_TEXT); setlinestyle(SOLID_LINE, 0, NORM-WIDTH); setfillstyle(EMPTY_FILL, 0); height = textheightC'H"); /* determine current height */ bar(0, MaxY-(height+4), MaxX, MaxY); rectang1e(0, MaxY-(height+4), MaxX, MaxY); outtextxy(MaxX/2, MaxY-(height+2), msg); setviewport(l, height+5, MaxX-1, MaxY-(height+5), 1); } /* */ /* DRAWBORDER: Draw a solid single line */ /* around the current viewport */ /* */ void DrawBorder(void) { struct viewporttype vp;
14. Программирование графики 211 User defined line styles "IIIIIII IIIIII Illi Illi '"""IIIIII IIIIII "IIIIIII '"'""Illll """IIIIIII IIIIII Illll '"""Hill "IIIIIII ""'IIIIIII """IIIIIII "IIIIIII Illi """IIIIIII '"""Illll ••uiiiiiiii """"IIIIII '"""Hill IIIIII IIIIII IIIIIII """IIIIII "IIIIIII '"IIIIIII """"Hill """IIIIIII IIIIII "IIIIIII """"Illll '""IIIIIII "'""IIIIII 'IIIIIII """IIIIIII Illll """IIIIIII """"Hill "Illll IIIIII '""IIIIIII ""'"l|||| "IIIIIII IIIIII IIIIII ‘"""Illll •’Illll "IIIIIII """IIIIII ‘"'"IIIIII "IIIIIII Illi "IIIIIII '"""Illll •'""IIIIIII " ""IIIIIII '"""IIIIII """IIIIIII •"""IIIIII """IIIIIII "IIIIII "IIIIIII '"IIIIIII '"IIIIIII "IIIIIII """IIIIIII IIIIIII '"IIIIIII Esc aborts or press a key... Рис. 36. Линейные матрицы, определяемые пользователем. Pre-defined Fill Styles Рис. 37. Матрицы заполнения, определенные заранее.
212 Часть III. Графика setcolor(MaxColors - 1); /* set current color to white */ setlinesty1e(S0LID_LINE, 0, NORM WIDTH); getv iewsett i ngs(8vp); rectangle(0, 0, vp.right - vp.left, vp.bottom - vp.top); /* */ /* CHANGETEXTSTYLE: similar to settextstyle, */ /* but checks for errors that might occur */ /* while loading the font file */ /* */ void ChangeTextSty1e(int font, int direction, int charsize) { int ErrorCode; graphresultf); /* clear error code */ settextstyle(font, direction, charsize); User Defined Fill Styles Рис. 38. Матрицы заполнения, определенные пользователем.
14. Программирование графики Error Code = graphresult(); /* check result */ if(ErrorCode ! = grOk){ /* if error occured */ dosegraph(); printf("Graphics System Error: %s\n", grapherrormsg(ErrorCode)); exit(l); } } /* */ /* DISPLAY: Used like PRINTF */ /* except the output is sent to the screen */ /* in graphics mode at the specified co-ordinate */ /* */ int Display(int *xloc, int *yloc, char *fmt, ...) valist argptr; /* argument list pointer */ char str[140j; /* buffer to build sting into */ int ent; va_start(argptr, format); /* initialize va_functions */ ent = vsprint(str, fmt, argptr); /* prints string to buffer */ outtextxy(*xloc, , *y1oc, str); /* send string in graphics mode */ *yloc += textheight(”H") +2 ; /* Advance to text line */ va_end(argptr); /* close va_functions */ return(cnt); /* return the conversion count */ } /* */ /* ASPECTDEMO: Demonstrate */ /* setaspectratio function */ /* jb */ void AspectDemo(void) { struct viewporttype viewinfo; int centerx, centery, i, radius, xasp, yasp, step;
214 Часть III. Графика MalnUindow(”setAspectRatio demonstration"); getv iewsetti ngs(&v iewi nfo); centerx - (viewinfo.right - viewinfo.left) » 1; centery (viewinfo.bottom - viewinfo.top) » 1; radius 3 * ((viewinfo.bottom - viewinfo.top)/5); step > radius/30; circ1e(centerx, centery, radius); getaspectrat i о(&xasp, &yasp); for(i - 1. i < 31. i++){ setaspectratio(xasp, yasp + i, * getmaxxd); circle(centerx, centery, radius); radius -= step; } radius += step * 30; for(i=l; l<81; 1++) setaspectratio(xasp * I * getmaxx(), yasp); lf(radius > step) radius -= step; cirde(centerx, centery, radius); } Paused; } /* /* FILLELLIPSEDEMO: Demonstrate /* fi Hell Ipse function /* jb void FillEllipseDemo(void) { Int maxradius, fillcolor, fillstyle, maxx, maxy; MalnWindow(”FillE11ipse demonstration"); StatusLinef'Esc aborts or press a key to stop"); maxradius = getmaxyd /10; maxx == getmaxxd - maxradius; maxy = getmaxy() - maxradius; setlinesty1e(S0LID_LINE, 0, NORM WIDTH): while( ! kbhit()){ fillcolor = random(get maxcolord + 1); setco1or(random(flllcolor + 1)); fillstyle = random(12); setf111sty1e(fillstyle, fillcolor); fIllellipse(random(maxx), random(maxy), random(maxrad i us), random(maxrad i us)); } Paused; - }
14. Программирование графики 215 Palette Demonstration Рис. 39. Демонстрация Palette. DrauPoly / FillPoly Denonstration Рис. 40. Демонстрация DrawPoly/FillPoiy.
216 Часть III. Графика /* */ /* SECTORDEMO: Demonstrate */ /* sector function */ /* jb */ void SectorDemo(void) { int maxradius, fillcolor, endangle, maxx, maxy, fillstyle; MainWindow("Sector demonstration"); StatusLinefEsc aborts or press a key to stop”); maxradius = getmaxy()/5; maxx = getmaxx() - maxradius; maxy = getmaxyO - maxradius; while( I kbhit ()){ fillcolor = getmaxcolorO; setcolor«(fillcolor); fillstyle = random(lZ); if(fillsty1e == EMPTY_FILL) fillstyle = LINE_FILL; setfi1Istyle(fi1Istyle, fi1Icolor); endangle = random(360); sector(random(maxx), random(maxy), random(endangle), endangle. — Finale =- That’s all, folks! Esc aborts or press a key... Рис. 41. Конец.
14. Программирование графики random(endangle), endangle, maxradius/2 + random(maxradius/2), itiaxradius/2 + random(maxradius/2)); } Pause(); }
Часть 1Y. Турбо Ассемблер 15. Ассемблер Несмотря на то что современные языки программирования высокого уровня обеспечивают не только удобное, но и эффективное системное программирование, в тех редких случаях, когда особенно важно получить оптимальный объективный код, необходимо использовать ассемблер. Представленный здесь Турбо Ассемблер разработан фирмой “Борланд Интсрнэйшнл" для семейства микрокомпьютеров IBM PC, оборудованных микропроцессорами Intel 8088, 8086, 80286 и т. д. Он похож на ассемблер MASM фирмы “Майкрософт Корпорэйшн", но при этом содержит целый ряд дополнений и усовершенствований и, кроме того, значительно более быстрый. Ассемблер активирован в версии MASM (совместимой с версией MASM 4.*), но с помощью специальных команд может быть переключен к версии MASM S1 (совместимой с версией 5.1) или IDEAL (версия фирмы “Бор- ланд"). Компилятор Компилятор Турбо Ассемблер является программой в виде загрузочного файла TASM.EXE. Компилятор вызывается командой TASM пате, в которой пате является именем компилируемого файла name.ASM и создает перемещаемый объектный модуль name.OBJ. Вызов компилятора TASM в общем случае имеет вид TASM fileset, fileset-, ...; fileset, в котором каждый fileset представляет собой options sources, object, list, cross. Options является набором опций, касающихся каждого fileset, sources - имена файлов, включающих исходные модули, object - имя файла, в который будет помещен созданный ассемблером объектный модуль, list - имя файла инстинга, cross - имя файла таблицы перекрестных ссылок. Опции обычно отделяются пробелами, а имена файлов должны быть отделены пробелами либо знаками + (плюс). Описание правил пользования компилятором TASM дано в приложении Д. Команда TASM/Zi test 1 + test 2 test; 1 greet
15. Ассемблер 219 вызовет создание из исходных модулей test l.asm и test 2,asm, связанных опцией/Zi, итогового модуля test.obj. и, кроме того, создание из исходного модуля greet.asm объектного модуля greet.obj и файла листинга greet. 1st (опция/Zi позволит осуществить отладку исполнительной программы). Компоновщик С целью создания исполнительной программы необходимо провести компоновку исполнительных модулей с библиотечными модулями. Это можно выполнить с помощью компоновщика Турбо Линк фирмы Borland. Компоновщик Турбо Линк является программой в виде файла TLINK.EXE. Его вызывают командой TLINK пате, в которой пате - имя компонуемого объектного файла name.OBJ. После компоновки создается загрузочный файл name.EXE. Вызов компоновщика TLINK в общем случае имеет вид TLINK options object, exec, тар, libraries, где options - набор опций, object - имена файлов, содержащих объектные модули, ехёс - имя файла, содержащего загрузочную программу, тар ~ имя файла, в котором будет помещена карта компоновки, a libraries - имена файлов объектных библиотек. Опции обычно отделяются пробелами, имена файлов должны быть отделены пробелами или знаками + (плюс). Пример. Первая программа В соответствии с принятой традицией исполнение первой программы должно заканчиваться выводом надписи Hello world. Такая программа написана на языке Турбо Ассемблер и предусмотрена для исполнения в операционной системе DOS. Она имеет вид Data SEGMENT Greet DB 'Hello world', 13,10, '$' Data ENDS Assume CS: Code, DS:Data Code SEGMENT Start: mov ax. Data ; загрузка номера сегмента в регистр DS mov ds, ах; mov dx, OFFSET Greet ; выведение надписи mov ah, 9 int 21.h mov al, 0 ; возвращение в систему mov ah, 4 ch int 21h Code ENDS
220 Часть IY. Турбо Ассемблер Stack - SEGMENT Stack DB lOOh DUP(?) ; стек размером 256 байт Stack-ENDS END Start ; адрес начала программы Указанная программа состоит из следующих частей: сегмент данных Data SEGMENT Data ENDS сегмент команд Code SEGMENT » Code ENDS сегмент стека Stack_SEGMENT STACK * Stack_ENDS а также из директив ASSUME и END. Сегменты означают разделение программы на логически разъединенные части, в которых сгруппированы данные и команды, а также зарезервировано место для стека. Директива ASSUME объявляет, что во время выполнения тех команд программы, которые следуют за ASSUME и неявно обращаются к указанным в ней регистрам процессора (здесь CS и DS), эти регистры будут содержать номера сегментов с указанными именами (здесь Code и Data). Директива END заканчивает запись текста программы и предписывает, чтобы ее исполнение начиналось с команды, снабженной меткой, указанной в директиве (здесь Start). Так как загрузка регистра CS номером сегмента, в котором находится команда, снабженная меткой, указанной в директиве END, выполняется операционной системой, то остается только загрузить регистр DS. Это выполняется с помощью команд mov ах, Data ; поместить в АХ номер сегмента Data mov ds, ах ; переслать АХ в DS. Поэтапное выполнение этой функции вызвано тем, что в процессоре 8086 нет команды, позволяющей непосредственно загрузить регистр DS. После загрузки регистров происходит двукратное обращение к функции системы DOS: вначале для вывода надписи Hello world, а затем с целью окончания выполнения программы. В каждом из этих случаев происходит выполнение системной функции, реализуемое прерыванием программы по ко- манде int 21h.
15. Ассемблер 221 Перед исполнением прерывания в регистрах процессора помещаются следующие параметры: с целью выведения надписи Hello world в АН - номер функции “выведи надпись": mov ah, 9 в DX - смещение надписи относительно DS: mov dx, OFF SET Greet с целью окончания выполнения программы: в АН - номер функции “возврат в систему": mov ah, 4ch в AL - код возврата: mov al, О (что можно выразить проще командой: mov ах, 4c00h). С целью преобразования приведенной программы к исполняемому виду необходимо - поместить ее в файл Greet.ASK; - скомпилировать, выдав команду tasm greet; - скомпоновать, выдав команду tllnk greet. После выполнения этих действий, которые приведут к созданию файла Greet.EXE, программу можно выполнять, вводя имя greet (в системе DOS маленькие и большие буквы в именах файлов не различаются). 15.1. Процессоры Наиболее распространенными процессорами микрокомпьютеров IBM PC и PC/XT являются Intel 8088 и Intel 8086. Эти процессоры установлены более чем в 10 миллионах компьютеров типа IBM PC, и, хотя минуло уже более 10 лет с момента их появления на рынке, они все еще доминируют на нем. Типичным представителем этого семейства микропроцессоров является микропроцессор 8088, список команд которого идентичен микропроцессору 8086. Именно поэтому данный микропроцессор выбран за основу для программирования на языке Турбо Ассемблер. Область адресов микрокомпьютера с микропроцессором 8088 имеет размер 1 Мбайт. Доступ к любому из 1.048.576 байт памяти осуществляется с помощью 20-бит адресов, состоящих из частей, называемых номером сегмента и смещением в сегменте. Формирование адреса памяти основано на умножении номера сегмента на 16 и прибавлением к нему смещения. В этом смысле адрес, представляемый условно как segment: offset, является по сути адресом байта с номером segment* 16 + offset. Так как использование адресов, получающихся сложением 16-бит номера сегмента и 16-бит смещения, очень удлинило бы код машинных команд,
222 Часть IY. Турбо Ассемблер применяется упрощение, основанное на том, что в большинстве команд предполагается, что сегментная часть команды находится в одном из сегментных регистров. Благодаря этому предположению коды команд значительно сокращаются, но применение такой команды должно предваряться соответствующей загрузкой сегментных регистров. Если программа короткая, т. е. ни ее команды, ни данные не занимают более 64 Кбайт памяти, загрузка регистров может быть одноразовой и выполняться, как правило, после начала выполнения программы. В противном случае может появиться потребность многократной загрузки сегментных регистров. В микропроцессоре 80386 это затруднение практически устранено введением возможности использовать сегменты размером до 4 Гбайт. Классификация регистров Совокупность регистров микропроцессора 8088 можно разделить на следующие группы: регистр фланков: FL универсальные регистры: АХ. BX, СХ. DX. SI, DI. BP, SP регистр счетчика: IP сегментные регистры: CS, DS, ES, SS Каждый из приведенных регистров имеет 16 бит. Независимо от этого регистр FL может быть представлен как комплекс из 16 регистров по 1 бит каждый, а каждый из регистров АХ, BX, СХ и DX - как комплекс двух 8- бит регистров (например, старшая часть регистра АХ является регистром АН, а младшая - регистром AL) и т. д. Независимо от проведенного разделения регистры ВХ и ВР называются базовыми, а регистры SI и DI - индексными.
IS. Ассемблер 223 Начальные значения регистров Необходимо принять, что непосредственно после начала выполнения программы, осуществляемого интерпретатором команд, начальные значения универсальных регистров ие определены. Единственным исключением является регистр SP. В этом регистре помещается число байтов стека, зарезервированного программой. Значения регистра счетчика и сегментных регистров определены однозначно. Если программа, записанная на языке Турбо Ассемблер, содержит директиву END, в которой есть метка команды (например, Start), то регистр CS загружен номером сегмента, в котором находится эта команда (SEG Start), а регистр IP загружен смещением в сегменте именно этой команды (OFFSET Start). Благодаря этому выполнение программы начинается с указанной меткой команды. Именно в этот момент в регистрах DS и ES будет находиться номер 256-байт сегмента PSP, помещенного в оперативной памяти непосредственно перед первым байтом программы, а в регистре SS будет находиться номер сегмента, предназначенного для стека. Внимание: В случае появления в программе директивы END без метки первоначальные значения указанных регистров следующие: DS - ES - SEG PSP; CS - DS + 10h; IP - 0. Регистр флажков (слово состояния процессора) В регистре флажков хранятся данные о состоянии процессора, а также о результатах выполнения некоторых команд. Среди 16 бит этого регистра только 9 имеют определенную интерпретацию О D I Т S Z А Р С О - Overflow (бит переполнения) D - Direction (бит направления) I - Interrupt (бит прерывания) Т - Trap (бит ловушки) S - Sign (бит знака) Z - Zero (бит нуля) А - Auxiliary (бит переноса тетрады) Р - Parity (бит четности) С - Carry (бит переноса) В момент начала выполнения программы I = 1, Т = 0. Значения остальных битов не определены. Установка каждого из указанных битов в состояние 1 означает следующее:
224 Часть 1Y. Турбо Ассемблер О - результат операции не помещается в регистре; D - операции на числовых цепочках будут выполнены в направлении уменьшающихся адресов; I - процессор реагирует на прерывание; Т - после каждого выполнения команд происходит попадание в ловушку; S - старший бит результата операции имеет значение 1; Z - результатом операции является 0; А - выполнение операции вызвало перенос из младшей тетрады результата; Р - младшие 8 бит результата содержат четное число единиц; С - выполнение операции вызвало перенос. Универсальные регистры Каждый из универсальных регистров может хранить 16-бит данные. Универсальны^ регистры АХ, ВХ, СХ и DX могут хранить пары 8-бит данных. АХ Регистр АХ является аккумулятором. Чаще всего он служит для выполнения арифметических, логических операций пересылки данных. Операции в гуом регистре оптимизированы и обычно выполняются быстрее, чем в остальных регистрах. Примеры mov ах, 13 ; поместить в АХ число 13 mov al,0 ; обнулить младший байт регистра АХ mov ах,[Fix] ; загрузить данные из ячейки с адресом DS:Fix ВХ Регистр ВХ - базовый регистр. Содержит обычно смещение относительно начала сегмента, номер которого находится в выбранном сегментном регистре (согласно предположению в DS). Пример mov bx,3 mov ах, [Ьх ] ; загрузить данные из ячейки с адресом DS:3 СХ Регистр СХ является регистром счетчика, обычно определяющим количество выполнений операций цикла.
/5. Ассемблер 225 Пример хог Ьх, Ьх nov cx,s Again: mov [word ptr bx],13 inc bx loop Again обнуление регистра BX установка счетчика BX - адрес слева увеличение ВХ на 1 выполнить согласно счетчику (5 раз) DX Регистр DX является регистром, используемым в операциях умножения и деления, а также единственным регистром, в котором можно указать адрес порта в командах ввода и вывода. Пример mov ах, 300 mill bx ; поместить в DX-AX величину АХ*ВХ SI и DI Регистры SI и DI являются индексными регистрами. Они содержат обычно смещения относительно начала выбранных сегментов оперативной памяти и тем самым позволяют выполнять операции на последовательности данных, чаще числовых. Пример mov ах, OFFSET String mov si, ax cld mov ex, -1 Again mov al, [Si] inc ex inc si and al, al jnz Again BP обнуление значения направления (DF) установка счетчика ненулевых байтов загрузка байта из DS:SI увеличение СХ увеличение SI проверка на нуль повторение если не нуль Регистр ВР является базовым регистром. Он, как и регистр ВХ, содер- жит обычно смещение относительно начала сегмента с той разницей, что относится к сегменту стека, определяемому с помощью регистра SS.
226 Часть IT. Турбо Ассемблер Пример mov Ьр, sp push ах add ах,[bp-2] пересылка SP в ВР поместить в стеке данные из АХ прибавление к АХ данных с верхушки стека SP Регистр SP является указателем стека. Он содержит смещение в сегмен- те, определенном регистром SS, младшего байта данных, отсылаемого в стек. Пример push ах ; неявное уменьшение SP на 2 Регистр счетчика IP Регистр IP является счетчиком команд. Он содержит смещение в сегменте, определяемом регистром CS, команды, которая будет выполнена следующей. Каждое выполнение команды процессора вызывает увеличение счетчика IP на число байтов, необходимое для представления данной команды. Исключения из этого правила касаются команд перехода (см. дальше), указанных в описании регистра CS. mov ах,1 mov сх,5 Again add ах,ах loop Again ; вычисление числа 25 ; изменение IP Сегментные регистры Сегментными регистрами являются CS, DS, ES, SS. Значение каждого из них (в сочетании со смещением в сегменте) определяет адрес байта оперативной памяти. Если принять, что seg представляет значение сегментного регистра, a off - смещение, то адресом байта является seg:o//. Внимание: С точки зрения принятого способа обозначения адреса (сдвиг seg и прибавление off) каждый адрес может быть представлен несколькими способами. В частности, адресом одного и того же байта является (выражая в шестнадцатеричной записи) 40:6 С и 46:0 С. Поэтому принято говорить об адресе нормализованном, т.е. о таком адресе, в котором смещение пред- ставлено числом меньше 16. Регистр CS Регистр CS содержит номер сегмента, из которого по адресу CS: IP будет выбрана и исполнена следующая команда. Начальные значения регистров CS
15. Ассемблер 227 и IP установлены на основе номера сегмента и смещения для метки, ука- занной в директиве END. Изменение значения регистра CS может произойти только в результате выполнения таких команд, как CALL (вызов процедуры), JMP (переход в другой сегмент), RET (возвращение из процедуры) и INT (программное прерывание). Пример ASSUME CS: Codel Code SEGMENT Start: jmp far ptr Lab Codel ENDS ASSUME CS: Code? Code? SEGMENT Lab: mov ax, 4c00h int Zlh Code? ENDS StackSEGMENT STACK DB 100H DUP(?) StackENDS здесь CS = SEG Codel, IP переход на метку Lab- = OFFSET Start ; здесь CS = SEG Code2, IP = OFFSET Lab ; код возврата 0 \ END Start Регистр DS Регистр DS содержит номер сегмента данных, т. е. именно того сегмента, в который, согласно предположению, отсылается большинство команд взаимодействия с памятью. Пример Data SEGMENT Fix DB 13 Data ENDS ASSUME CS: Code, DS:Data Code SEGMENT Start: ; mov ax. Data mov ds, ax mov al,[Fix] ; mov ah, 4ch int ?lh Code ENDS Stack SEGMENT STACK 15* здесь DS = SEG PSP здесь DS •= SEG Data
228 Часть 1Y. Турбо Ассемблер db 100Н DUP(?) StackENDS END Start Регистр ES Регистр ES содержит номер добавочного сегмента, т. е. того сегмента, в который, согласно предположению, отсылаются некоторые команды для выполнения операции на цепочках данных. Пример DataScr SEGMENT Src DB 13 № DataSrc ENDS DataDst SEGMENT Dst DB? DataDst ENDS ASSUME CS: Cjde, DS: DataSrc, ES: DataDst Code SEGMENT Start: mov ax, DataSrc mov ds, ax mov ax, DataDst mov es, ax mov si, OFFSET Src mov di, OFFSET Dst mov sb ; пересылка байта из DS: SI в ES:DI mov ds, ax mov al,[Dst] ; получение пересланного байта mov ah, 4ch int 21 h Code ENDS Stack_SEGMENT STACK DB lOOh DUP(?) Stack_ENDS END Start Регистр SS Регистр SS содержит номер сегмента стека, т. е. того сегмента, к которому относятся все команды взаимодействия со стеком. Среди них push (положить в стек), pop (извлечь из стека), а также типичные команды, относящиеся к регистру ВР. ,
/5. Ассемблер 229 Пример ASSUME CS: Code Code SEGMENT Start: mov ax, 7 push ax mov ax, 6 push ax mov bp, sp mov al, [bp+2] add al, [bp] mov ah, 4ch Int 21h Code ENDS ; здесь SS = SEG Stack, SP c 100 ; возьми 7 ; добавь 6 Stack_SEGMENT STACK DB lOOh DUP(?) Stack_ENDS END Start Использование префикса Результатом отказа от использования в большинстве команд, обращающихся к памяти, явного номера сегмента является значительное сокращение кода этих команд. Сокращение также достигается благодаря договоренности, что для конкретной команды требуемый номер сегмента будет находиться в определенном регистре. В тех редких случаях, когда принятый порядок соответствия сегментных регистров команды оказывается неудобным, его можно изменить, проверяя адрес команды префиксом, имеющим вид названия сегментного регистра. Использование такого префикса (удлиняющего код команды на один байт) приводит к тому, что предполагаемый сегментный регистр заменяется сегментным регистром, определяемым префиксом. Пользуясь префиксами, необходимо помнить о некоторых ограничениях: - использование префикса касается только аргументов команды; - не допускается, чтобы команды операций со стеком (push и pop) были реализованы с помощью пересылки в иной регистр, кроме SS; - не допускается, чтобы применяемые в некоторых командах над цепочками данных (movs, cmps, seas, stos) пересылки в регистр ES были заменены пересылками в любой другой регистр. Пример ASSUME CS: Code Code SEGMENT Fix DB 13 Start: mov al, [cs:Fix]; адресация относительно CS
230 Часть /У. Турбо Ассемблер mov ah, 4ch int 21h Code ENDS Stack_SEGMENT STACK DB lOOh DUP(?) StackENDS END Start Так как метка Fix находится в сегменте, к которому можно адресоваться только через регистр CS, использование префикса CS излишне, поскольку команда mov al, [Fix] и так была бы заменена указанной командой. Виды адресации н В микропроцессоре Intel 8086 существуют три вида адресации: - непосредственная адресация; - регистровая адресация; - адресация памяти (непосредственная и косвенная). Непосредственная адресация Непосредственная адресация связана с аргументами команд, которые являются постоянными выражениями. Значения этих выражений могут быть заданы перед началом выполнения программы. Примеры. Непосредственная адресация mov ах, 20 ; загрузка в регистр АХ числа 20 mov ах, OFFSETFix ; загрузка в регистр АХ величины, равной смещению адреса переменной Fix int 21h ; генерирование программного прерывания с номером 2111 Регистровая адресация Регистровая адресация связана с аргументами команд, которые находятся непосредственно в регистрах процессора. Примеры. Регистровая адресация Inc si ; выполнение операции SI: = SI + 1 mov ах, Ьх ; пересылка данных из регистра ВХ в регистр АХ Адресация памяти Адресация оперативной памяти может быть непосредственной или косвен- ной. В командах непосредственной адресации присутствуют атрибуты, пред- ставляющие адреса переменных и команд. В командах непосредственной ад- ресации, кроме того, присутствуют атрибуты пересылки в базовые и индекс
15. Ассемблер 231 ные регистры. В каждом случае имеются также неявные пересылки в сег- ментные регистры. Непосредственная адресация переменных Команды непосредственной адресации переменных опеределяют их адреса в виде [<л или d, где d является постоянным выражением, содержащим символ переменной и определяющим смещение адреса переменной в сегменте. Непосредственная адресация команд При непосредственной адресации команд (переходы и вызовы процедур) адреса определяются в общем виде d, где d является постоянным выражением. Примеры непосредственной адресации памяти mov ах, [Fix] ; пересылка значения переменной Fix в регистр АХ call Fun ; вызов процедуры Fun Косвенная адресация Команды с косвенной адресацией определяют адреса переменных и команд, используя базовые (ВХ, ВР) и индексные (SI, DI) регистры. В общем случае адрес может быть выражен с помощью одного из 16 следующих выражений: [Ьх] [bx + si] Id + bx] [d + bx + si] [Ьр] [bx + di] [d + bp] Id + bx + di] [si] [bp + si] Id + si] [d + bp + si] [di] [bp + di] [d + di] Id + bp + di] в которых <1 представляет собой постоянное выражение. Примеры. Косвенная адресация памяти mov ах, [Ьх] ; пересылка в регистр АХ значения переменной, адрес которой находится в регистре ВХ jmp[si] ; переход на выполнение команды, адрес которой находится в регистре SI
232 Часть 1Y. Турбо Ассемблер Вычисление эффективного адреса (непосредственного или косвенного) основано на суммировании всех его составляющих, определенных как постоянным выражением d, так и находящихся в указанных или предполагаемых регистрах процессора. Пример. Вычисление эффективного адреса mov ах, 5; ВХ : = 5 mov SI, -7; SI ; = -1 mov ax, [Fix + bx +si + 2] Поскольку BX + SI + 2 = 0, то выполнение данной команды с косвенной адресацией имеет такой же результат, как и команды с непосредственной адрсацией mov ах, [FixiJ. Внимание: Если явно не указано в описании команд (разд. 15.3), то необходимо принять, что косвенная адресация с использованием регистра ВР касается сегмента с номером, находящимся в регистре SS, а косвенная адресация с использованием регистров BX, SI и DI касается сегмента с номером, находящимся в регистре DS. ♦ Представление адресов Записывая адреса, относящиеся к меткам переменных и команд, необходимо помнить о соответствии размерности данных. В частности, если Fix является меткой двухбайтовой переменной, например Fix DW 258, то для загрузки регистра АХ словом данных из адреса Fix можно пользо- ваться командой mov ах,[Fix]. Но для загрузки регистра AL байтом данных из адреса Fix необходимо преобразовать адрес слова Fix к адресу байта. Такое преобразование можно осуществить оператором BYTE PRT, при этом команда пересылки младшего байта переменной Fix в регистр AL принимает вид mov al, [byte ptr Fix]. В общем случае адресные части команд могут быть образованы не только из символов и названий регистров, но и сложных выражений и префиксов. Существует довольно произвольный способ записи команд, обращающихся к памяти. Подлежат исправлению следующие равнозначные1 команды: mov ах, [word ptr ds : bp + si + 2] (предпочтительная версия) mov ax,ds : word ptr[bp + si + 2] mov ax, word ptr[ds : bp + si + 2] mov ax, ds : [word ptr bp + si + 2] mov ax, dx : [word ptr bp] [si + 2]
75. Ассемблер 233 15.2. Программы Каждая программа, записанная иа языке Турбо Ассемблер, состоит из строк вида label directive comment или label : command comment, где label - метка; directive и command - соответственно директива или команда; a comment - комментарий. Метка, упреждающая команду, вместе с отделяющим ее знаком : (двоеточие) может быть записана в отдельной строке. Комментарий является не транслируемой комбинацией любых символов, начинающихся знаком ; (точка с запятой). Наличие метки или комментария необязательно. Не является меткой идентификатор, с которого начинается последняя строка структурной директивы. Пример. Директивы и команды ASSUME CS : Code Code SEGMENT Fix DB 13 Start:; метка Start mov ax, Code mov ds, ax mov al, [Fix ] mov ax, 4c00h int 21h Code ENDS ; метка Code ; директива DB, снабженная меткой ; обращение к метке Code ; команда, не снабженная меткой ; обращение к метке Fix ; команда, не снабженная меткой ; команда, не снабженная меткой ; обращение к метке Code Stack—SEGMENT STACK ; DB lOOh DUP (?) ; Stack—ENDS ; END Start ; начало структурной директивы директива, не снабженная меткой конец структурной директивы обращение к метке Start Ключевые слова Ключевым словом является слово определенного значения, с которого начинается каждая директива, а также команда. Ключевые слова, записанные с помощью строчных и прописных букв (например, mov,MOV, Mov), не различаются. Перечень ключевых слов языка Турбо Ассемблер довольно широк. Большинство из них имеет вид буквенно-числового ряда, начинающегося с буквы, например, ASSUME, CS, SEGMENT, DB, mov, int, STACK, ENDS, END,
234 Часть IY. Турбо Ассемблер но некоторые из них, например MODEL, X LIST начинаются со знака . (точка) или % (процент). Метки Метка - это идентификатор, сопровождающий директиву или команду. Метка состоит из ряда букв и цифр, начинающегося с буквы. Буквами, входящими в состав метки, могут быть большие и маленькие буквы английского алфавита, а также знаки $, ?, -• Запрещаемся, чтобы метка имела вид ключевого слова или чтобы она состояла только из знака ? (вопросительный знак) или $ (доллар). Все метки можно разделить на - метки констант, например Fatal EQU 13 - метки переменных, например Value DB -13 - метки команд, например Start : mov ах, 13 - метки сегментов, например Data SEGMENT ... ENDS - метки процедур, например Fun PROC ... ENDP - метки макроопределения Shift MACRO ... ... ENDM Если метки стоят перед командой, то они отделяются от нее знаком : (двоеточие). В каждом модуле данная метка может появляться только один раз. Однако ничто не препятствует многократному обращению к ней.
15. Ассемблер 235 Пример. Метки Code SEGMENT Plus DB •+’ Minus DB метка плюс метка минус Start: mov al, [Plus] ; обращение к метке плюс sub al, [Minus] ; обращение к метке минус mov ah, 4 ch int 2Ih Code ENDS В сегменте Code используются метки Plus и Start. Меткой не является символ Code перед ключевым словом ENDS. Локальные метки Значительное облегчение программирования обеспечивают локальные >/ метки. Область действия каждой из них ограничена тем фрагментом модуля, который содержится между ближайшими нелокальными метками команд. Благодаря этому возможно использование в одном и том же модуле нескольких ие противоречащих друг другу, не идентичных меток. Условием применения локальных меток является использование директивы LOCALS. В области ее действия каждая метка, начинающаяся двумя знаками @ (at), считается локальной. Интерпретация директивы NOLOCALS вызывает прекращение реагирования на локальные метки. В момент начала компиляции предполагается директива NOLOCALS. Пример. Использование локальных директив LOCALS mov ах, [Alpha] Labi cmp ax, [Beta] jg OSSkip inc bx 60Skip: cmp ax, [Gamma] Lab2: jg eesktp inc bx eeskip: mov [Delta], bx Lab3: NOLOCALS ; переход вперед
236 Часть IY. Турбо Ассемблер Областью действия первой метки @@Skip является фрагмент программы между нелокальными метками Labi и Lab2. Областью действия директивы LOCALS является фрагмент программы между этой директивой и строкой, содержащей директиву NOLOCALS. Литералы Литералы - это имена констант. Они делятся на арифметические, знаковые и цепные. Арифметические литералы делятся на целые и действительные. Целые в свою очередь делятся на десятичные, шестнадцатеричные и двоичные. Обязательны следующие правила: - десятичный литерал состоит из ряда десятичных цифр; - шестнадцатеричный литерал состоит из ряда шестнадцатеричных цифр, заканчивающегося буквой Н (первым знаком литерала может быть десятич- ная цифра; цифры больше 9 записываются с помощью букв от А до F); - двоичный литерал состоит из ряда двоичных цифр, заканчивающегося буквой В; - действительный литерал состоит из целой части, точки, дробной части, буквы Е и показателя; мантисса состоит из ряда десятичных цифр, а пока- затель состоит из ряда цифр, предваряемого знаком + (плюс) или - (минус); - знаковый литерал состоит из знака кода ASCII, заключенного в кавычки или ‘апострофы. - цепной литерал состоит из знаков кода ASCII, заключенных в кавычки или апострофы; - строчные и прописные буквы, применяющиеся в арифметических литералах, не различаются. Примеры 30 leh 11110b З.ОЕ + 1 44* €4 'Hello' - литерал целый десятичный - литерал целый шестнадцатеричный - литерал целый двоичный - литерал действительный - знаковый литерал - цепной литерал Первые четыре литерала представляют данные величиной 30. Команды Команда определяет действия процесора. Команды делятся на команды без аргументов, одноаргументные и двухаргументные. В соответствии с этим делением они записываются в одном из следующих видов: action ; mov sb action arg ; int 21h action dst, src ; mov ax, 13 Обозначения (мнемоника) команд являются ключевыми словами и поэто- му могут быть записаны как прописными, так и строчными буквами. В ко- мандах двухаргументных первый аргумент является приемником, а второй -
15. Ассемблер 237 источником. Некоторые команды (например, mov sb) имеют условные аргументы. В этом случае деление на источник и приемник следует из описания команды. В момент начала исполнения программы распознаются только команды микропроцессора Intel 8086 и сопроцессора 8087. Прием команд от иного микропроцессора требует использования директив выбора процессора. Каждая из иих состоит из знака . (точка), после которого следует номер процессора, например 80286, 80287 и т. д. Примеры. Команды хог al, al mov al, bh jmp Lab push ax ret ; обнуление AL ; пересылка BH в AL ; переход на Lab ; поместить AX в стеке ; возврат из процедуры Директивы Интерпретация директивы вызывает декларирование постоянной или переменной, декларирование макроопределения или определение способа дальнейшей компиляции исходного модуля. Обозначения (мнемоника) директив ' (например, SEGMENT и DB) являются ключевыми словами, как и мнемоника команд, поэтому они могут быть записаны как строчными, так и прописными буквами. Некоторые директивы имеют структурный характер и должны выступать с другими родственными им директивами. К такой группе относятся среди прочих директивы условной компиляции, макродирективы, а также сегментные и процедурные директивы. Директива SEGMENT Директива SEGMENT имеет общий вид lab SEGMENT align combine class body lab ENDS где lab - метка сегмента, allgh определяет способ помещения сегмента в опе- ративную память, combine определяет способ соединения нескольких сегмен- тов, объединенных одной и той же меткой (но находящихся в разных исходных модулях) в один сегмент, a class обеспечивает включение соединенного сегмента в соответствующую группу сегментов. Интерпретация сегментной директивы вызывает образование сегмента с названием lab. Если в первом модуле появляется несколько сегментных ди- ректив, предваряемых меткой lab, то все они неявно связаны в одну сег- ментную директиву. В таком случае аргументы align, combine и class должны выступать только в первой из директив, а если они появляются в последующих, то их игнорируют. Если сегментные директивы с одной и той же меткой появляются более чем в одном исходном модуле, то явный или предполагаемый состав их аргументов должен быть идентичен.
238 Часть IY. Турбо Ассемблер Допускаются следующие значения указанных аргументов: align BYTE - размещение сегмента на границе байта; WORD - размещение сегмента на границе слова; DWORD - размещение сегмента на границе двойного слова; PARA - размещение сегмента на границе параграфа, т.е. такой области, адрес которой кратен 16 (такой способ размещения принят по умолчанию); PAGE - размещение сегмента на границе страницы, т.е. такой области, адрес которой кратен 256; combine PRIVATE* - запрещение сочетания с сегментами иных модулей (такой способ принят по умолчанию); PUBLIC - соединение сегментов таким способом, чтобы размер созданного сегмента был равен сумме размеров составляющих его сегментов; COMMON - соединение сегментов таким способом, чтобы первые байты каждого из сочетаемых сегментов перекрывались » бы и имели один начальный адрес, а размер созданного сегмента был равен размеру максимального сегмента; STACK - соединение сегментов, как и в случае PUBLIC, для образования стека; MEMORY - соединение сегментов, как и в случае PUBLIC, с размещением в оперативной памяти в конце загрузочного модуля; АТ ехр - размещение сегмента, начиная с вычисленного сегментного адреса - константы ехр. class Аргумент class имеет вид символа, заключенного в скобки или в апостро- фы. Этот символ является именем класса, в который будет зачислен данный сегмент. Примеры. Директива SEGMENT а) Программа одномодульная ASSUME CS: Code, DS: Data Data SEGMENT Pair DB 13 Data ENDS Code SEGMENT Start: mov ax. Data mov ds, ax
15. Ассемблер mov ах, [word prt Pair] Code ENDS Data SEGMENT DB 4ch Data ENDS Code SEGMENT int 21h Code ENDS Stack_SEGMENT STACK DB lOOh DUP[?) Stack_ENDS END Start Приведенная программа трактуется как программа ASSUME CS: Code, DS: Data Data SEGMENT PARA PRIVATE Pair DB 13, 4ch Data ENDS Code SEGMENT PARA PRIVATE Start: mov ax, Data mov ds, ax mov ax, [word prt Pair] int 21h Code ENDS StackSEGMENT PARA STACK DB lOOh DUP(?) Stack_ENDS END Start б) Программа двухмодульная Первый модуль ASSUME CS: Code, DS: Data Data SEGMENT WORD PUBLIC Pair DB 0 Data ENDS Code SEGMENT BYTE PUBLIC Start: mov ax. Data
240 Часть IY. Турбо Ассемблер mov ds, ах mov ах, [word ptr Pair] Code ENDS END Start Второй модуль ASSUME CS: Code, DS: Data Data SEGMENT WORD PUBLIC DB 4ch Data ENDS Code SEGMENT BYTE PUBLIC int 21h ? Code ENDS Stack_SEGMENT STACK DB lOOh DUP(?) Stack_ENDS END * Приведенная программа (после компоновки в очередности указанных модулей) трактуется как программа ASSUME CS: Code, DS: Data Data SEGMENT WORD Pair DB 0, ?, 4ch Data ENDS Code SEGMENT BYTE Start: mov ax. Data mov ds, ax mov ax, [word ptr Pair] int 21h Code ENDS Stack_SEGMENT PARA STACK DB IDOh DUP(?) Stack_ENDS END Start Поскольку первый байт сегмента Data второго модуля должен быть раз- мещен на границе слова, между байтами 0 и 4ch появляется байт неопреде- ленного значения. Поэтому в регистр АН пересылаются данные неопределен- ного значения, а результат выполнения команды int 21 h невозможно предви- деть. Если бы было принято размещение сегмента Data на границе байта, то программа была бы корректна.
IS. Ассемблер в) Закрытие сегментов ASSUME CS: Code. DS: Data Data SEGMENT Fix DB13 Code SEGMENT Start: mov ax. Data mov ds, ax mov al, [Fix] mov ah, 4 ch Code ENDS Data ENDS StackSEGMENT STACK DB IDOh DUP(?) StackJNDS Code SEGMENT int Zlh Code ENDS END Start Приведенная программа трактуется как программа ASSUME CS: Code, DS: Data Data SEGMENT Fix DB13 DATA ENDS Code SEGMENT Start: mov ax. Data mov ds, ax mov al, [Fix] mov ah, 4ch int Zlh Code ENDS Stack_SEGMENT STACK DB IDOh DUP(?) • Stack_ENDS END Start г) Использование директивы SEGMENT с аргументом AT StackSEGMENT STACK 16—764
242 Часть IY. Турбо Ассемблер DB lOOh DUP(?) Stack_ENDS Screen SEGMENT AT ObOOOh DW 1999 DUP(?) Corner Char DB? DB? Screen ENDS Code SEGMENT ASSUME CS: Code, DS: Screen Start: mov ax. Screen mov ds, ax mov bx, OFFSET Corner Char mov ax, IDOOOlllb SHL 8 + mov[bx], ax mov ax, 4c00h int Zlh Code ENDS END Start , Выполнение программы вызывает высвечивание мигающего знака * (.звездочка) в правом нижнем углу экрана монитора, управляемого платой монитора Hercules. Директива GROUP Директива GROUP имеет общий вид name GROUP segname, segname,..., segname где name - имя группы сегментов, состоящей из сегментов с именами segname, указанных после ключевого слова GROUP. Интерпретация директивы GROUP приводит к такому размещению сегментов с указанными именами, что они оказываются в смежном сегменте оперативной памяти размером не более 64 Кбайт. Благодаря принятому размещению доступ ко всем данным и командам этих сегментов может быть реализован с помощью одного сегментного регистра. Если директива GROUP с одним и тем же именем группы будет использована многократно (даже в различных модулях), то в данную группу будут включены все сегменты, указанные в этих директивах. Пользуясь группами сегментов, необходимо помнить, что, несмотря на принадлежность сегмента к определенной группе, выполнение команды, содержащей выражение с оператором OFFSET, например mov ах, OFFSET Fix, определяет смещение относительно начала сегмента, а не относительно начала группы. Поэтому, если адресация осуществляется с помощью регистра, который не был связан с сегментом или с группой сегментов, то
15. Ассемблер 243 для каждого из таких адресов требуется предварение адреса спецификацией группы, состоящей из названия группы и двоеточия, например mov ах, OFFSET DGROUP: Fix Пример. Директива GROUP Pair GROUP Datal, Data? ASSUME CS: Code, DS: Pair Data 1 Segment Ref DW OFFSET Pair:F1x Data 1 ENDS Data 2 SEGMENT BYTE DB3 Fix DB DataE ENDS Code SEGMENT Start: mov ax. Pair mov dx, ax mov bx,[Ref] mov al, [bx] mov bx, OFFSET Pa1r:F1x add al, [bx] mov ah, 4ch int Zlh Code ENDS Stack_SEGMENT STACK DB lOOh DUP(?) StackENDS END Start ; внимание: использовано DS: Pair ; лишний OFFSET Pair ; лишняя спецификация группы ; возьми 5 ; необходимо использовать OFFSET Pair: ; добавь 5 Программа заканчивается кодом возврата 10. Если бы из программы была убрана спецификация группы Pair, она заканчивалась бы кодом возврата 6. Директива ASSUME Директива ASSUME может иметь один из следующих видов: ASSUME reg: name,reg: name, reg : name ASSUME reg: NOTHING ASSUME NOTHING в которых каждый reg является именем сегментного регистра (CS, DS, ES, SS), а каждый пате - именем сегмента или группы сегментов. Интерпретация директивы ASSUME относительно определенного регистра reg, после имени которого следует пате, является декларацией того, что во время выполнения каждой команды, находящейся в области действия этой директивы, в регистре reg будет находиться номер сегмента пате. Если после reg следует ключевое слово NOTHING, в области действия такой директивы нельзя гарантировать, что во время выполнения программы в регистре reg находится номер сегмента.
244 Часть IY. Турбо Ассемблер Если директива ASSUME не содержит ни одного названия регистра, то отсутствие такой гарантии касается всех сегментных регистров. Поскольку отсылка к метке определенного сегмента возможна только тогда, когда в одном из регистров находится номер этого сегмента, непродуманное использование директивы ASSUME может исключить возможность доступа к снабженным меткой данным или в лучшем случае вызвать генерирование команды с префиксом (дольше выполняемой). Пример. Директива ASSUME ASSUME CS ; Code Code SEGMENT Fix DB13 Start: mov ax, Cocte mov ds, ax mov al, [Fix] ; генерируя mov al, [cs : Fix] mov ah, 4 ch int Zlh Code ENDS Stack_ SEGMENT STACK DB lOOh DUP(?) Stack_ENDS END Start Обращение к переменной Fix требует использования номера сегмента, в котором декларирована метка 'Fjx. Несмотря на то что во время выполнения программы этот номер сегмента находится в регистре DS, во время компиляции программы этот факт не учитывается (так как подразумевается ASSUME DS : NOTHING), поскольку Турбо Ассемблер генерирует команды с префиксами. Этого можно избежать, пользуясь директивой ASSUME CS: Code, DS: Code. Директивы PUBLIC, GLOBAL и EXTRN Использование указанных директив позволяет осуществлять передачу между модулями программ меток, констант, переменных и команд. Связь между модулями осуществляется таким способом, что в модуле, экспортирующем метку, используется директива PUBLIC, а в модуле импортирующем - директива EXTRN. С тем же успехом можно в каждом из связанных модулей пользоваться директивой GLOBAL. Директива PUBLIC имеет вид PUBLIC symbol, symbol, ..., symbol, где symbol является экспортируемой меткой.
15. Ассемблер 245 Директива EXTRN имеет вид EXTRN define, define, ..., define, где каждый define представляется одной из следующих форм: symbol : type symbol : type : count, где symbol - импортируемая метка, type - один из следующих ключевых слов, определяющих тип импортируемого объекта (идентичный с типом этого объекта в экспортирующем модуле): ABC - имя константы; BYTE - имя переменной типа BYTE (1 байт); WORD - имя переменной типа WORD (2 байт); DWORD - имя переменной типа DWORD (4 байт); FWORD - имя переменной типа FWORD (6 байт); QWORD - имя переменной типа QWORD (8 байт); TWORD - имя переменной типа TWORD (10 байт); NEAR - имя близкой процедуры или команды; FAR - имя далекой процедуры или команды; count относится только к переменным и определяет количество переменных указанного типа, представленных импортируемым идентификатором. Директива GLOBAL имеет вид GLOBAL define, define, ..., define, где каждый define такой же, как и директива EXTRN. Директива GLOBAL, использованная в отношении к экспортируемому объекту, трактуется как PUBLIC, а использованная в отношении к импортируемому объекту - как EXTRN. Пример. Экспортирование и импортирование объектов Первый модуль GLOBAL Result : BYTE, AddOne : FAR PUBLIC Fixed ASSUME CS : Code, DS : Data Data SEGMENT Fixed DB 12 Data ENDS Code SEGMENT Start: mov ax. Data
246 Часть IY. Турбо Ассемблер mov ds, ах call far ptv AddOne ; вызов процедуры mov al, [Result] mov ah, 4ch int Zlh Code ENDS END Start Второй модуль ASSUME CS : Code, DS : Data GLOBAL Result : BYTE, AddOne : Far » EXTRN Fixed: BYTE Data SEGMENT Result DB? Data ENDS Code SEGMENT AddONE PRdc Far mov ax, [Fixed] inc ah mov [Result], ah ret ; возврат с процедуры AddONE ENDP Code ENDS Stack_ SEGMENT STACK DB lOOh DUP(?) StackENDS END Упрощенные директивы Использование сегментных директив, а также директив GROUP и ASSUME позволяет конструировать модули с довольно сложной логической структурой, особенно модули, которые могут быть связаны с модулями, созданными компиляторами языков высокого уровня. Учитывая это, Турбо Ассемблер расширен директивами .MODEL, .CODE, .DATA, .DATA?, .FARDATA, .FARDATA?, упрощающими запись программ. Директива .MODEL Использование директивы .MODEL предваряет введение упрощенных директив. В этой директиве общего вида .MODEL model, language
15. Ассемблер 247 ИЛИ .MODEL TPASCAL model - имя модуля памяти, language - название языка программирования, с которого будут вызываться процедуры данного модуля. Аргумент language может быть следующим: С, Pascal, Basic или Prolog. Если процедура модуля, записанного на языке Турбо Ассемблер, будет вызвана из программы, написанной на языке Турбо Паскаль, то директива .MODEL имеет вид .MODEL TPASCAL . В остальных случаях необходимо указать название языка, а также допус- тимую для него модель памяти. В частности, для языка Си это могут быть модели TINY, SMALL, MEDIUM, COMPACT, LARGE и HUGE. В программах, целиком записанных на языке Турбо Ассемблер, достаточно ограничиться указанием модели памяти. . Использование директивы .MODEL позволяет заменить сегментные директивы, такие, как, например, FAR- DATA SEGMENT PARA 'FAR—DATA' (текст сегмента) FAR- DATA ENDS упрощенными директивами .FARDATA (текст сегмента), а также позволяет учесть допущения некоторых других директив, в том числе GROUP и ASSUME. В каждой из моделей памяти допускается использование упрощенных директив .CODE .DATA .DATA? .FARDATA .FARDATA name .FARDATA? .FARDATA? name .CONST •STACK а в моделях MEDIUM, LARGE и HUGE допускается, кроме того, директива .CODE name (name в директивах FARDATA, FARDATA? и CODE - имя сегмента). В целях упрощения ссылки на имена сегментов данных вводятся следующие неявные определения:
248 Часть IY. Турбо Ассемблер @ Data - имя сегмента, определяемого .DATA, @ Code - имя сегмента, определяемого .CODE, @ FarData - имя сегмента, определяемого .FarData, @ FarData? - имя сегмента, определяемого .FarData? а также несколько других определений @ FileName @ CurSeg - имя компилируемого файла; - имя текущего компилируемого сегмента; @ CodeSize - в моделях с коротким кодом TINY, SMALL и СОМРАСКТ число 0, в остальных - 1; @ DataSize - в моделях с “короткими данными": TINY и SMALL, MEDIUM - число 0, в моделях COMPACT и LARGE - г* число 1 и в модели HUGE - число 2; ??Data - дата компиляции; ??Time - время компиляции. Как уже пояснялось, каждая из упрощенных директив замещает определенную расширенную директиву, а связь между ними устанавливается из следующего соответствия упрощенным директивам аргументов директивы SEGMENT . Упрощение Модель Имя Аргументы .CODE TINY -TEXT WORD PUBLIC CODE' .CODE SMALL -TEXT WORD PUBLIC CODE' .CODE MEDIUM /i/е—TEXT WORD PUBLIC 'CODE' .CODE name MEDIUM name WORD PUBLIC CODE' .CODE LARGE file-^EXV WORD PUBLIC CODE' .CODE name LARGE name WORD PUBLIC CODE' .CODE HUGE file—'T'E.X'T WORD PUBLIC CODE' .CODE name HUGE name WORD PUBLIC CODE' Все модели .DATA -DATA WORD PUBLIC Data' .DATA? -BSS WORD PUBLIC BSS' .FARDATA FAR-DATAPARA PRIVATE 'FAR—DATA' .FARDATA name name PARA PRIVATE 'FAR—DATA' .FARDATA? FAR-BSS PARA PRIVATE FAR-BSS' .FARDATA? name name PARA PRIVATE 'FAR-BSS' •CONST CONST WORD PUBLIC CONST' •STACK STACK PARA STACK STACK' Внимание: Запись file в директивах .CODE является именем файла, содержащего эту директиву. В директиве .STACK можно указать число, . определяющее размер стека (по умолчанию 1024). Кроме того, для каждой из моделей предполагается: - директива GROUP,
IS. Ассемблер 249 которая в модели TINY имеет вид DGROUP GROUP -TEXT,-DATA, CONST,-BSS, STACK, а в остальных моделях - вид DGROUP GROUP -DATA, CONST,-BSS, STACK - директива ASSUME, в которой имеется регистр CS с сегментом, определяемым упрощенной директивой .CODE, a DS и SS связаны с группой DGROUP. Внимание: После директивы .MODEL TPASCAL могут появиться только упрощенные директивы .CODE и .DATA. Тогда обязательно следующее соответствие упрощенным директивам аргументов директивы SEGMENT: Упрощение Имя. Аргументы .CODE CODE BYTE PUBLIC .DATA DATA WORD PUBLIC Пример. Директива .MODEL а) Программа с упрощенными директивами .MODEL SHALL .DATA Fix DB 13 .CODE Start : mov ax, 0 Data mcv ds, ax mov al, [Fix] mov ah, 4 ch - Int Zlh .STACK lOOh END Start б) Равнозначная программа с расширенными директивами DGROUP GROUP DATA, STACK ASSUME CS: TEXT, DS : DGROUP, SS : DGROUP DATA SEGMENT WORD PUBLIC, 'DATA' Fix DB13 _DATA ENDS _TEXT SEGMENT WORD PUBLIC 'CODE’ Start: mov ax, DGROUP mov ds, ax
250 Часть IY. Турбо Ассемблер mov al, [Fix] mov ah, 4 ch int Zlh _TEXT ENDS STACK SEGMENT PARA STACK 'STACK' DB lOOh DUP(?) STACK ENDS END Start Директива DOSSEG Директива DOSSEG состоит из ключевого слова DOSSEG Интерпретация директивы DOSSEG вызывает упорядочение сегментов результрирующей программы соответственно соглашениям, принятым фирмой Microsoft для языков высокого уровня. Внимание: Если модули, написанные на языке Турбо Ассемблер, связаны с модулями, генерируемыми компиляторами этих языков, то использование DOSSEG излишне, поскольку и так будет обеспечено приведение к стандартным формам. Стандартное упорядочение сегментов следующее: - все сегменты класса 'CODE'; - все сегменты, не относящиеся к классу CODE и не принадлежащие группе DGROUP; - сегменты группы DGROUP в следующей последовательности сегменты класса 'BEGDATA', сегменты, не относящиеся к классу BEGDATA, BSS, 'STACK', сегменты класса “BSS", сегменты класса “STACK". Пример. Стандартное упорядочение сегментов .MODEL SMALL DOSSEG .STACK lOOh •FAR DATA? Res DW? .CODE .Start: mov ax, 8Data mov ds, ax mov al, [Ast] mov bx, SFarData? mov ds, bx ASSUME DS: BFarData? mov [Res], ax mov dx, OFFSET Res mov ah, 9 int Zlh
15. Ассемблер 251 mov ах, 4c00h int Zlh .DATA Ast DW ('$' SHL8) + END Start Приведенная программа трактуется так, как если бы она имела вид .MODEL SMALL .CODE. Start: mov ax, 6 Data mov ds, ax mov al, [Ast] mov bx, BFarData? mov ds, bx ASSUME DS: DFarData? mov [Res], ax mov dx, OFFSET Res mov ah, 9 int Zlh mov ax, 4c00h int Zlh .FARDATA? Res DW? • DATA Ast DW ('$ ' SHL8) + STACK lOOh END Start Директивы инициализации простых переменных Директивами инициализации простых переменных являются DB, DW, DD и др. Каждая из них имеет общий вид lab kind arg, arg, ..., arg, где lab - метка; kind - название одной из указанных директив, а каждый arg является выражением, знаком вопроса (равнозначным выражению с неопределенной величиной) или имеет вид count DUP(arg, arg, ..., arg), где count - количество повторений списка аргументов в скобках. Интерпретация директив инициализации простых переменных вызывает резервирование памяти для переменных определенного типа DB - для байтовых переменных (например, byte, unsigned char). DW - для слов (например, integer, short int), DD - для двух слов (например, longint, long int), DF - для трех слов (например, real), DQ - для четырех слов (например, double),
252 Часть IY. Турбо Ассемблер DT - для пяти слов (например, extended) и последующее приписывание им значений, определенных выражениями, указанными в директиве. Если директивой является DB, а приписываемыми данными являются сим- волы, то можно произвести упрощение, основанное на соединении последо- вательности символов в цепной литерал, ограниченный знаками “ (кавычки) или ' (апостроф). Примеры инициирования переменных Greet Greet DB DB *H’, 'e’t T, 'Г, Hello" ’o’ > вид развернутый вид сокращенный NwnDW 3,?„ 4, 3, 7, 4. 7. 5 вид развернутый NufflDW г DUP(3, 7, 4). 7, 5 вид сокращенны*' RefDW OFFSET Greet > гид развернутый Ref DW Greet вид сокращенный DD 100000 переменная без имени Quote DB , f W f\ переменная однознакокая Директивы инициализации структурных переменных Структурными переменными являются записи, структуры объединения. Для инициализации структурной переменной необходимо сначала описать структурный тип, а затем воспользоваться директивой инициализации переменных. Типы и переменные записи Описание записи имеет вид name Record field, field, ..., field, где name - имя записи, а каждый field - спецификация поля в одном из следующих видов: id:size ld:size « Intt, где id - идентификатор поля записи, size - размер поля, выраженный в байтах, a intt - начальное значение. Требуется, чтобы в данном модуле идентификаторы полей записей были уникальными, а общий размер полей каждой записи не превышал 16 бит. Описание переменных типа запись имеет вид lab name arg, arg, ..., arg, где lab - метка, name - имя записи, а каждый arg - начальное значение, состоящее из списка значений, взятых в треугольные скобки. Каждое из
15. Ассемблер 253 этих выражений служит для инициализации одного поля записи. Если некоторое значение отсутствует, то предполагается значение, взятое из описания записи. Поля записи упорядочены в последовательности их появления в описании записи. Записи, общий размер полей которых не превышает 8 бит, являются однобайтовыми, а остальные - двубайтовыми. Поля представлены в виде последовательности битов, помещенных в байте или в слове с правосторонним выравниванием. Примеры описания записей В области действия описания записи Triple RECORD Left: 3, Center: 4=ofh, Right:? выражение Rec Triple*?, 1>, <?, 0,0> является описанием двух записей, первая из которых имеет имя Rec. Запись Rec занимает одно слово, в котором поле Right занимает два младших бита. В записи Rec поле Left имеет начальное значение 2, поле Center - начальное значение Ofh, а поле Right - начальное значение 1. Так как в процессорах Intel 8086 нет возможности адресации определенных битов, отсылка к полям записей происходит нетипичным способом с использованием операторов MASK и WTDTH, а также имен полей записей. В целях выполнения операций на полях записей принимается, что - имя поля записи представляет константу со значением, равным общему размеру полей, следующих за этим полем; - выражение MASK id, где id является идентификатором поля записи, представляет битовую маску, позволяющую выбрать поле id (эта маска устанавливает единицы в битах указанного поля и нули в остальных байтах); - выражение WIDTH id, где id является идентификатором записи или идентификатором его поля, представляет константу, равную размеру записи или поля записи (выраженному в битах). Пример. Операции над полями записей В области действия описания записи Triple Triple RECORD Left:?, Mid:3:Right:l и описания переменной Rec этого типа
254 Часть IY. Турбо Ассемблер Rec Triple<l, 5, 0> выполнение команд mov al, [Rec] and al, MASK Mid mov al. Mid shr al, cl приводит к размещению поля Mid записи Rec в младших разрядах регистра AL. Структуры Описание структуры имеет вид пате STRUC field field field • name ENDS где name - имя описываемой структуры, а каждый field является описанием поля, идентичным описанию переменной, записи, структуры или объеди- нения. Учитывая, что описание объединения отличается от описания структуры только тем, что ключевое слово STRUC заменено в нем ключевым словом UNION, дальнейшее описание будет ограничено структурой. Необходимо однако помнить о существенной разнице структуры и объединения. В то время как структура состоит из всех полей, указанных в ее описании, следующих одно за другим, объединение состоит в данный момент только из одного поля. Поскольку объединение представляется как наложение всех его полей, ничто не мешает одновременному обращению к переменным, описанным # разных полях. Тем не менее лучше принять, что если некоторой переменной поля приписано конкретное значение, то при этом каждой переменной остальных полей приписаны неопределенные значения. Хотя такое предположение чисто гипотетическое, оно может значительно облегчить правильное использование объединений. Описание переменных структурного типа имеет вид lab name arg, arg,..., arg, где lab - метка, name - имя структуры, а каждый arg - начальное значение, состоящее из списка значений, взятых в треугольные скобки. Каждое из этих значений служит для инициализации одного поля структуры. Если некоторое значение отсутствует, то предполагается соответствующее значение, взятое из описания структуры. Не допускается, чтобы размерность явно заданной величины превышала размер
15. Ассемблер 255 соответствующего поля в описании структуры. Кроме того, запрещается использовать значение в виде текстового литерала, если описание поля не содержало подобного литерала. Примеры. Описание структур В области действия описания структуры Person Person STRUC Full Name DB 'John Walker' Age DW40 Sex DB? Person ENDS выражение Bob Person < Bob Smith является описанием структурной переменной Bob, состоящей из полей Full Name, Age и Sex, размер которых определяется описанием структуры и соответственно равных 11 (несмотря на то что 'Bob Smith' имеет размер 9), 2 и 1. Поле Age имеет значение 40, а два младших байта поля Full Name являются пробелами. Требуется, чтобы в данном модуле имена полей структур и объединений были уникальными. В целях получения возможности обращаться к отдельным полям вводится оператор . (.точка), который в сущности может трактоваться точно так же, как и оператор + (плюс). Это следует из того, что имя поля структуры или объединения требуется как имя постоянной величины, равной смещению этого поля в пределах описания структуры. Пример. Операции над полями структур В области действия описания структуры Date Date STRUC Year DW? Month DB?, -1 Day DW? Date ENDS The Day Date <1981, 12, 13> выполнение команд mov bx, OFFSET The Day mov ax, [bx. Year] ; Ax = 1981 mov ah, [TheDay.Month] ; AH = 12 mov al, [byte ptr TheDay.Day] ; AL = 13 mov al, [TheDay.(Month+1)] ; AL = -1 приводит к загрузке регистров, как указано в комментарии.
256 Часть IY. Турбо Ассемблер Директивы описания меток С каждой меткой в директиве инициализации переменных связан ее тип, а именно Директива Тип BYTE WORD DB DW DD DF DQ DI DWORD FWORD QWORD TWORD Метка, «отделенная от команды знаком : (.двоеточие), является типом NEAR, а метка, предваряющая директиву PROC (по сути описывающая процедуру), является явно определенной меткой в этой директиве (NEAR или FAR) или по умолчанию на основе модели памяти NEAR - для модели TINT, SMALL, COMPACT FAR - для модели MEDIUM, LARGE, HUGE, TPASCAL • Кроме того, тип метки может быть описан с помощью директивы LABEL. Директива LABEL имеет вид name LABEL type, где пате - идентификатор описываемой метки, type - тип. Тип метки опре- деляет, относится ли она к данным (например, BYTE, WORD, DWORD) или к командам (например, NEAR, FAR). Интерпретация директивы LABEL ве- дет к снабжению последующего байта компилируемого сегмента меткой name Таким образом, идентифицированная метка может быть экспортирована в другой модуль. Пример. Описание меток В области действия описания Lower LABEL BYTE Fix DW13 Before LABEL FAR Start: mov ax, eData After LABEL NEAR метками являются не только Fix и Start, но и Lower, Before и After. В частности, Lower является именем переменной, состоящей из младшего байта переменной Fix, Before - метка команды mov. В отличие от метки Start, которая относится к типу NEAR, метка Before относится к типу Far.
15. Ассемблер 257 Выражения Выражения могут выступать как в аргументах директив инициализации переменных, так и в аргументах команд. Требуется, чтобы каждое выражение было выражением постоянным, т.е. чтобы его значение было определено перед началом выполнения программы. Благодаря этому в каждом месте программы, где может появиться число, может также появиться и выражение. Состав выражений языка Турбо Ассемблер ближе к составу языка Турбо С, чем языку Турбо Паскаль. Проявляется это в том числе в том, что заключение о правильности и семантике выражения может быть проведено исключительно на основе приоритета использованных в нем операторов. Упорядочение операторов в очередности уменьшающихся приоритетов следующее: <> О [] LENGTH MASK SIZE WIDTH + - (одноаргументный) OFFSET type PTR SEG THIS TYPE * / MOD SHL SHR + - (двухаргументный) EQ GE GT LE LT NE NOT AND OR XOR SHORT (type означает одно из следующих ключевых слов FAR, NEAR, BYTE, WORD, DWORD, FWORD, QWORD, TBYTE). <> Оператор <text> определяет литерал независимо от символов, составляющих text. Пример В записи Count<; а+Ь> знак ; (точка с запятой) не начинает комментарий. О Придание большего приоритета выражению в скобках. Пример В выражении
258 Часть IY. Турбо Ассемблер a-(b+c) сначала выполняется суммирование. __ - Эквивалентность [ехр!][ехр2] и [ехр/ + ехр2] Пример Команда mov ах, [Ьх][2] равнозначна команде mov DX, [bx+2] LENGTH -----------f-------------—----------------------------------------------- Оператор LENGTH name, где name - идентификатор переменной, возвращает число единиц памяти, выделенной переменной, описанной с помощью ключевого слова DUP, или равно 1, если в описании не использовано это ключевое слово. Пример В области действия описания Count DE 10 DUP(20 DUP (?)),1000 DUP(O) оператор length Count возвращает значение 10. MASK Оператор MASK field, где field - имя поля записи, позволяет произвести выбор поля. Пример В области действия описания Rec RECORD West:?, East:5 ППРПЯТОП
15. Ассемблер 259 mask West возвращает значение 01100000В. SIZE Оператор SIZE пате возвращает произведение (LENGTH name} * (TYPE name). Пример В области действия описания Array DW 10 DUP(O). 30 DUP(l) оператор size Array возвращает значение 20. WIDTH —— Оператор WIDTH field, где field - имя записи или идентификатор поля записи возвращает значение, равное числу битов этой записи или поля. Пример В области действия описания Rec RECORD West:?, East:5 операторы width Rec и width East возвращают соответственно значения 7 и 5. Оператор .field, где field - идентификатор поля структуры с записью + field.
260 Часть IY. Турбо Ассемблер Пример В области действия описания Child SIRUC Name DB ' ' Age DB7 Child ENDS Isa Child <Isa, 6> команда mov ah, [Isa . Age] s эквивалентна команде mov ah, [Isa * Age], HIGH Оператор HIGH exp, где exp является выражением, возвращает старший байт постоянной, представленной ехр. Пример Оператор high 1111000000001111В возвращает значение 11110000В. LOW Оператор LOW ехр, где ехр является выражением, возвращает младший байт постоянной, представленной ехр. Пример Оператор low 1111000000001111В возвращает значение 00001111В.
15. Ассемблер 261 Оператор +ехр, где ехр является выражением, эквивалентен ехр. Пример Выражение 2 -+ 3 равно -1. Оператор -ехр, где ехр является выражением, эквивалентен 0-ехр. Пример Выражение 2+(-3) равно -1. Запись seg : ofs, где seg является именем сегментного регистра, сегмента или группы сегментов, a ofs - смещение, представляет адрес в соответствии с указанным регистром, сегментом или группой сегментов. Пример Если SI = 2, то в команде mov al, [es:si + 4] произойдет обращение к шестому байту сегмента, определенного CS. OFFSET Оператор OFFSET addr, где addr является адресом памяти, возвращает смещение этого адреса относительно начала сегмента, в котором находится addr. Пример Оператор offset DGROUP:Fix
262 Часть IY. Турбо Ассемблер представляет смещение адреса Fix относительно группы DGROUP. type PTR Оператор type PTR ехр, где type является обозначением типа для данных: BYTE, WORD, DWORD, FWORD, QWORD, TBYTE для команд: NEAR, FAR, PROC a exp является выражением, возвращает выражение типа type, локализующее то же место памяти, что и ехр. Пример В команде ц inc[byte ptr bx] регистр ВХ локализует данные типа BYTE. SEG ___________ » _____________________________________________________________ Оператор SEG addr, где addr является адресом памяти, возвращает номер сегмента, в котором находится этот адрес. Пример В области действия описания Data SEGMENT DB? Fix DW 13 Data ENDS как SEG Data, так и SEG Fix являются номером сегмента, в котором описана переменная Fix. THIS Оператор THIS type, где type является обозначением типа для данных: BYTE, WORD, DWORD, FWORD, QWORD, TBYTE для команд: NEAR, FAR, PROC возвращает полный адрес (номер сегмента и смещение) в текущем месте компилирования текущего сегмента. Пример Директива
15. Ассемблер 263 Неге EQU THIS FAR трактуется как директива Неге LABEL FAR TYPE Оператор TYPE ехр, где ехр является выражением, возвращает значение постоянной в соответствии со следующим соглашением: Тип Значение постоянной BYTE 1 WORD 2 DWORD 4 FWORD 6 QWORD 8 TBYTE 10 NEAR Offffh FAR Offfeh Пример В области действия описания Fix DW 1 оператор type Fix возвращает значение 2. * Выражение expL * expR, в котором expL и expR являются постоянными выражениями, представляет их произведение. Пример В области описания Fix DW 3 DUP(?J, D выражение type Fix * length Fix возвращает значение 6.
264 Часть IY. Турбо Ассемблер / Выражение expL / expR, в котором expL и expR являются постоянными выражениями, представляет их частное. Пример В области действия описания Fix DQ 1, 2, 3 выражение Size Fix/2 имеет значение 4. MOD Выражение expL MOD expR, в котором expL и expR являются постоянными выражениями, представляет остаток от деления expL на expR. Пример Выражение 12 mod 5 равно 2. SHL Выражение expL SHL expR, в котором expL и expR являются постоянными выражениями, определяет сдвиг двоичного представления expL на expR позиций влево. Пример Выражение 13 SHL 2 равно 52. SHR Выражение expL SHR expR, в котором expL и expR являются постоянными выражениями, определяет сдвиг двоичного представления expL
15. Ассемблер 265 на expR позиций вправо. Пример Выражение 13 SHR 2 равно 3. Выражение expL + expR, в котором expL и expR являются постоянными выражениями, представляет их сумму. Пример Выражение 13 + 2 равно 15. Выражение expL - expR, в котором expL и expR являются постоянными выражениями, представляет разность expL и expR. Пример Выражение 13-2 равно 11. EQ Выражение expL EQ expR, в котором expL и expR являются постоянными выражениями, принимает значение -1, если expL^ равно expR, или 0 в противном случае. Пример Выражение 12 EQ 2 + 10
266 Часть IY. Турбо Ассемблер равно -1. GE Выражение expL GE expR, в котором expL и expR являются постоянными выражениями, принимает значение -1, если expL больше или равно expR, или 0 в противном случае. Пример Выражение 12 GE 2 + 10 г> равно -1. GT Выражение expL GT expR, в котором expL и expR являются постоянными выражениями, равно -1, если expL больше, чем expR, или 0 в противном случае. Пример Выражение 12 GT 12 равно 0. LE Выражение expL LE expR, в котором expL и expR являются постоянными выражениями, принимает значение -1, если expL меньше или равно expR, или 0 в противном случае. Пример Выражение 6 LE 5 равно 0. LT Выражение expL LT expR, в котором expL и expR являются постоянными выражениями, равно -1, если expL меньше, чем expR, или 0 в противном случае.
15. Ассемблер 267 Пример Выражение 2 LT - 1 равно 0. NE i Выражение expL NE expR, в котором expL и expR являются постоянными выражениями, принимает значение -1, если expL не равно expR , или 0 в противном случае. Пример Выражение 12 NE 2 равно -1. NOT Выражение NOT ехр, в котором ехр является постоянным выражением, является инверсией двоичного представления ехр, т.е. в каждом бите 1 меняется на 0 и наоборот. Пример Выражение NOT -6 равно 5. AND Выражение expL AND expR, в котором expL и expR являются постоян- ными выражениями, представляет поразрядное логическое произведение expL и expR (произведение битов равно 1 Догда, когда оба аргумента равны 1). Пример Выражение 10 AND 6 равно 2.
268 Часть IY. Турбо Ассемблер OR Выражение expL OR expR, в котором expL и expR являются постоянными выражениями, представляет поразрядное логическое сложение expL и expR (логическая сумма битов равна 0 тогда, когда оба аргумента равны 0). Пример Выражение 10 0R 6 равно 14. XOR Выражение expL XOR expR, в котором expL и expR являются постоянными выражениями, представляет поразрядное логическое сложение по модулю 2 (ИСКЛЮЧАЮЩЕЕ ИЛИ) выражений expL и expR (сложение битов по модуйю 2 равно 1 только тогда, когда оба аргумента принимают различные значения). Пример Выражение 10 AND 6 равно 12. SHORT Выражение SHORT ехр, в котором ехр является постоянным выражением, представляет смещение, отличающееся от текущего смещения в компилируемом сегменте не больше чем на 127 и не меньше чем на - 128. Пример Команда jmp short Lab является командой короткого перехода. Внимание: Такие выражения, как, например, OFFSET Fix
15. Ассемблер 269 и SEG Fix, в которых Fix является меткой переменной, являются по сути постоянными выражениями. Значение первого из них (определяющее смещение в сегменте переменной Fix) и так известно уже на первом этапе компиляции программы, а значение второго (определяющее номер сегмента, в котором находится рассматриваемая переменная) известно в момент загрузки программы. Примеры. Выражения (After - Before)/? WORD PTR Fix+1 Fix EQ Res 2 + -3 Назначение имен постоянным Для назначения имен постоянным служат директивы EQU и = (равно). С помощью директивы EQU можно назначать имена символам, символьным литералам, числам и меткам, а с помощью директивы = (равно) можно назначать только имена числам. В общем случае директива EQU имеет вид пате EQU ехр, а директива = (равно) имеет вид пате = ехр, где пате является назначаемым именем постоянной, а ехр - постоянным выражением. Запрещается, чтобы идентификатор, определяющий имя постоянной, назначаемое с помощью директивы EQU, был бы описан снова. Это ограничение не касается имени постоянной, назначаемого с помощью директивы = (равно). Примеры Описание постоянных В области действия описания Fix DW 13 Lab:mov ax, 2 Могут быть изменены следующие назначения постоянных: Count = О Higher EQU OFFSET Fix + 1 Greet EQU 'Hello' Lower EQU Higher - 1 Count » ebunt + 1 Next EQU Lab + 2
270 Часть 1Y. Турбо Ассемблер Символ $ Символ $ (доллар) принадлежит к той же категории, что и указанные выше символы @Data, ©FarData и т. д. Этот символ является именем постоянной, равной по значению действительному смещению в компилируемом сегменте. Пример. Использование символа $ Greet DB 'Hello World* Span EQU $ - Greet ; Span = 11 Адресация переменных В предыдущих примерах использовались очень простые адреса данных и команд. В общем случае адрес данных может быть представлен с помощью выражения вида База + Индекс + Смещение, где база и индекс являются соответственно именами базового (ВХ, ВР) и индексного (SI, DI), регистр смещение - постоянным выражением. Пример. Адресация переменной Если принять, что в области действия упрощенной директивы .DATA находится описание Greet DB 'Hello', а в регистре DB находится номер сегмента ©Data, помещенный туда, например, с помощью двух команд mov ах, 8 Data mov ds, ах, то выполнение каждой из следующих последовательностей команд вызывает загрузку в регистр al одних и тех же данных: 1) mov al, 'О' 2) mov al, [Greet + 4] 3) mov si, (OFFSET Greet) + 4 mov al, [si] 4) mov bx, OFFSET Greet + 4 mov al, [bx] 5) mov si, OFFSET Greet mov bx, 4 mov al, [si + bx]
75. Ассемблер 271 6) mov di, 1 mov bx, 1 mov al {bx + Greet + di + 2] 7) mov bp, 1 mov di, OFFSET Greet + 2 mov al, [ds : bp + di + 1] Адрес переменной должен быть выражен таким способом, чтобы было видно соответствие типа переменной и использованной команды. В частности, это означает, что с 8-бит переменной можно обращаться только с помощью команды загрузки 8-бит данных, а пересылка данных из памяти в стек процессора может касаться только 16-бит переменной. Кроме того, требуется, чтобы на основе анализа записи команды можно было выяснить размер данных, которых касается эта команда. В целях выполнения указанных требований возникает необходимость использования операторов группы type PTR в контексте type PTR ехр, что вызывает преобразование выражения ехр в тип type. Примеры. Преобразование типа выражения а. Если в области действия директивы Fix DW 13 находится команда пересылки 8-бит данных mov al, [Fix], то она использована неверно, поскольку Fix представляет 16-бит данные. Эту ошибку можно исправить, заменив рассматриваемую команду командой mov al, [byte ptr Fix] б. Команда вида inc [bx] неверна, поскольку на основе ее анализа нельзя определить относится ли она к 8- или 16-бит переменной. В первом случае ее необходимо представить в виде inc [byte ptr bx], а во втором - в виде inc [word ptr bx], в. Команда вида mov ax, [bx]
272 Часть /У. Турбо Ассемблер верна, поскольку на основе ее анализа можно судить, каков размер данных (16 бит). г. Команда вида push Fix, где Fix является именем переменной типа BYTE, неверна, поскольку в стеке могут быть помещены только слова. Ошибка может быть исправлена заменой рассматриваемой команды командой push word ptr Fix. Адресация команд Адресация команд нарушается в связи с выполнением переходов и вызова процедур. Если в команде перехода имеется ключевое слово SHORT, это означает компиляцию короткого перехода, а если ключевое слово NEAR, то компиляцию близкого перехода. В каждом из этих случаев выполнение перехода вызывает только изменение значения регистра IP и не нарушает регистра CS (.короткие команды занимают меньше места, чем близкие, но позволяют выполнять переходы на расстояние не больше тем 128 байт от места появления команды). Если в команде имеется ключевое слово FAR, то это означает компиляцию дальнего перехода. Его выполнение вызывает загрузку как регистра CS, так и IP. Во время компилирования команд перехода, не содержащих указанных ключевых слов, подразумевается ключевое слово NEAR, поскольку переход происходит в известную уже метку, имеющую атрибут FAR. В этом случае компилируется дальний переход. Внимание: Необходимо избегать близких и коротких команд межсегментных переходов, так как их использование в большинстве случаев не имеет смысла. Пример ASSUME CS: Code 1 Codel SEGMENT Start: jmp short Labi Lab3: jmp Lab4 ; jmp short Lab4 и nop Lab6 LABEL FAR jmp near ptr Lab7 ; требование near ptr Lab8 LABEL FAR mov ax, 4cooh
15- Ассемблер 273 int Zlh Lab7 LABEL FAR jmp short Lab8 ; без short было бы far ptr Labi: jmp far ptr LabZ Lab4: jmp far ptr Lab5 Codel ENDS ASSUME CS: CodeZ ; или ASSUME CS: NOTHING CodeZ SEGMENT LabZ: jmp far ptr Lab3 Lab5 LABEL FAR jmp Lab6 ; jmp far ptr Lab6 CodeZ ENDS Stack SEGMENT STACK DBlODh DUP(?) Stack _ENDS END Start 1S .3. Процедуры Процедура является частью программы, реализирующсй отдельный фрагмент алгоритма. Она сконструирована так, чтобы могла вызываться из различных мест программы и после ее выполнения могло произойти возвращение в место ее вызова. Вызов процедуры происходит с помощью команды call. Ее выполнение имеет тот же результат, что и выполнение команды jmp, с той лишь разницей, что происходит дополнительная пересылка в стек процессора места возврата из процедуры, т.е. ближнего или дальнего адреса того байта памяти, который следует после команды call. Ближний адрес состоит только из смещения в сегменте, а дальний - из номера сегмента и смещения. В случае запоминания дальнего адреса (в стек) сначала отсылается номер сегмента, затем смещение в сегменте. Возврат из вызванной процедуры по ближнему адресу происходит с по- мощью команды retn, а по дальнему адресу - с помощью команды retf. Если будет использована процедура ret, то она будет трактоваться как retn или retf в зависимости от того, как была описана процедура: далекая или близкая. Описание процедуры упрощенно имеет вид name PROC type body name ENDP,
774 Часть IY. Турбо Ассемблер где пате является именем процедуры, type - определением типа процедуры (NEAR - близкая, FAR - далекая), a body - телом процедуры. Интерпретация описания процедуры вызывает генерирование кода, определяемого телом процедуры. Если в заголовке или теле процедуры имеются аргументы, то в модели (для процессоров 8088 и 8086) подразумевается тело процедуры вида push bp ; Пролог процедуры mov bp, sp ; body ; а каждая команда ret процедуры, находящаяся в body, заменяется командами pop bp s ; Эпилог процедуры reft size ; где size является числом, определяющим размер области, занятой аргументами. Подразумеваемый пролог и эпилог процедуры подвергаются оптимизации (например, пустое тело процедуры генерирует только команду retf). Тип процедуры Наличие в заголовке процедуры слова type определяет ее близкой, поскольку описание находится в области действия директивы .MODEL, а это для моделей TINY, SMALL, COMPACT означает признание процедуры за близкую, а для моделей MEDIUM, LARGE, HUGE, TPASCAL процедура определяется как далекая. Требуется, чтобы вызов близкой процедуры происходил по ближнему адресу, а дальней процедуры - по дальнему. Близкий адрес создается при 16-бит аргументе, а дальний - при 32-бит аргументе команды call. Примеры. Вызов процедур (эта несколько странная программа ставит целью только иллюстрацию различных вариантов команды call) MODEL LARGE STACK lOOh DATA Ref DW Rise DD BigRise .Code Rise PROC NEAR inc cl and cl, cl jz FarExit
15. Ассемблер 275 ret BigRise add cl,10 retf MOVE LABEL FAR mov al, cl FarExit retf Rise ENDP Start: mov ax. Data mov ds, ax mov cl, -1 call far ptr Rise ; далекий вызов call Rise j близкий вызов mov ax, OFFSET Rise call ax mov bx, OFFSET Ref ; близкий вызов call [word ptr bx] mov bx, Z ; близкий вызов call [dword ptr Ref+bx] ; далекий вызов call Move ; далекий вызов mov ah,4ch int Zlh END Start Выполнение программы заканчивается кодом возврата 13. Если вызов касается процедуры, тип которой еще не известен, то в моделях MEDIUM, LARGE, HUGE и TPASCAL требуется явное указание типа процедуры, например, call far ptr Routine, а в остальных случаях подразумевается тип near. Требуется, чтобы указан- ный или подразумеваемый тип процедуры соответствовал фактическому типу. При/дер •MODEL TPASCAL • CODE Start: call far ptr Nil mov ah, 4ch int Zlh Nil PROC ret Nil ENDP STACK lOOh END Start Приведенная программа правильна. Если бы в команде вызова процедуры использовался оператор far ptr, то программа стала бы ошибочной.
276 Часть IY. Турбо Ассемблер 15.3.1. Описание аргументов и результатов Способ передачи аргументов и получение результатов зависит от программиста. Аргументы обычно указываются в регистрах или стеке процессора и оттуда извлекаются. Необходимо помнить, что в стеке находится адрес возврата из процедуры. В момент выполнения команды возврата из процедуры этот адрес должен находится в верхушке стека процессора. Приведение указателя стека SP в положение, в котором он находился перед отсылкой в стек аргументов процедуры, может осуществляться как в процедуре вызывающейся, так и вызванной. В процедурах, написанных на языке Турбо Паскаль, применяется первое, а в процедурах, написанных на языке Турбо Си, - второе правило. В каждом из указанных языков аргументы указываются с помощью стека, а результаты процедур остаются в регистрах (AL, АХ, DX-AX, DX-BX-AX), в стеке или статичной области памяти. В целях облегчения выполнения операций с аргументами процедур, переданных способом, принятым в языках высокого уровня, в языке Турбо Ассемблер вводятся вспомогательные директивы ARG и LOCAL. Директива ARG Директива ARG имеет общий вид AR6 arg, arg, ..., arg = size RETURNS res, res, ..., res где каждый arg является описанием аргумента процедур, каждый res - описанием результата (который по окончании выполнения процедуры будет оставлен в стеке процессора), a size - идентификатором, с которым будет связано число, определяющее размер области, занимаемой в стеке процессора аргументами arg. В рассматриваемой директиве должно присутствовать как минимум одно описание arg, но выражение arg - size, а также RETURNS res, res, res могут быть ие упомянуты. Аргументы должны быть приведены в той последовательности, в какой выступали бы в языке высокого уровня (т. е. всегда слева направо), кроме того, принимается, что в случае модели TPASCAL первый аргумент находится в старшем адресе, в остальных случаях (в том числе при отсутствии директивы .MODEL) первый аргумент находится в младшем адресе. Принимается, что независимо от использованной модели первый результат находится в младшем адресе. Каждое из описаний аргумента и результата имеет один из следующих видов: пате пате : type
75. Ассемблер 277 name : type : count, где name является идентификатором (локальным в данной процедуре), type является обозначением типа, a count - счетчиком переменных типа type. Ес- ли тип переменной является указательным, то она начинается обычно клю- чевыми словами NEAR PTR или FAR PTR (например, FAR PTR WORD яв- ляется дальним указателем переменной типа WORD). Если описание начина- ется ключевым словом PTR, то для моделей TINY, SMALL, MEDIUM и TPASCAL подразумевается NEAR, а для моделей COMPACT, LARGE и HUGE - FAR. Если определение типа не будет упомянуто, то подразумева- ется тип WORD. Счетчик count является постоянным выражением, определяющим число переменных указанного типа. Если он будет пропущен, то подразумевается счетчик со значением I; если типом аргумента является BYTE, то подразумевается счетчик со значением 2. Это несколько странное предположение вытекает из факта, что пересылка в стек всегда осуществляется байтами по парам. Интерпретация директивы ARG вызывает - связь с каждым идентификатором пате, выражения вида [bp + offset], в которой offset является смещением младшего значащего байта, занимаемого рассматриваемым аргументом или результатом, от младшего из пары байтов, в которые в прологе процедуры было переслано содержимое регистра ВР Директивы ARG в данной процедуре могут выступать многократно. Разрешается также помещать эти директивы непосредственно в заголовки процедур, но без ключевого слова ARG. Если более чем в одной из таких директив выступает определенный идентификатор аргумента, то принимается, что вышеописанная связь касается только последнего их появления. В частности, это означает, что пара директив ARG 1: WORD,] : DWORD.к ARG j, 1 трактуется как пара директив ARG dummy: BYTE; G.k ARG j. 1, где dummy является уникальным идентификатором, не выступающим в дан- ной процедуре. Примеры. Использование директивы ARG а) Простая директива ARG в модели TINY ARG i: byte, j ; равнозначно mov al, [bp + 4] mov al, 1 add al, byte ptr j ; равнозначно add al [byte ptr bp + 6]
278 Часть IY. Турбо Ассемблер Принимается, что непосредственно после содержимого регистра ВР там находятся отсылки в стек процессора ВР - по адресу SS:SP + 0 (2 байт) адрес возврата - по адресу SS:SP + 2 (2 байт) i - по адресу SS:SP + 4 (2 байт) j - по адресу SS:SP + 6 (2 байт) б) Директива ARG в модели TPASCAL ARG a: word, Ь: far ptr qword:4, c:dword » s mov ax, a; mov ax, [bp+26] mov al, byte ptr a; mov al, [byte ptr bp+26] les si, b; les si, [bp+10] mov bx, worcfptr c; mov bx, [word ptr bp+6] mov ax, s; mov ax, 22 Принимается, что непосредственно после отсылки содержимого регистра ВР в стек там находятся ВР - по адресу SS:SP+O (2 байт) адрес возврата - по адресу SS:SP+2 (4 байт) с -по адресу SS:SP+6 (4 байт) b - по адресу SS:SP+1O (16 байт) а - по адресу SS:SP+26 (2 байт) в) Директива ARG в модели LARGE ARG a: word, b:far ptr qword:4, c:dword = s mov ax, a; mov ax, [bp+6] mov al, byte ptr a; mov al, [byte ptr bp+6] les si, b; les si, [bp+8] mov bx, word ptr c; mov bx, [word ptr bp+24] mov ax, s; mov ax, 22 Принимается, что непосредственно после отсылки в стек содержимого регистра ВР там находится ВР адрес возврата а d с - по адресу SS:SP+O (2 байт) - по адресу SS:SP+2 (4 байт) - по адресу SS:SP+6 (2 байт) - по адресу SS:SP+8 (16 байт) - по адресу SS:SP+24 (4 байт) г) Директива ARG с фразой RETURNS ARG a: byte RETURNS p:byte:l, q-.word В модели SMALL принимается следующая интерпретация содержимого стека
15. Ассемблер BP - по адресу SS:SP+O (2 байт) адрес возврата - по адресу SS:SP+2 (2 байт) a - по адресу SS:SP+4 (2 байт) P - по адресу SS:SP+6 (1 байт) q - по адресу SS:SP++7 (2 байт) д) Программа с директивой ARG (модель TPASCAL) Stack _ SEGMENT SRACK DB lOOh DUP(?) Stack _ ENDS .MODEL TPASCAL .CODE Select PROC a: WORD, b:WORD mov ax, a ; mov ax, [bp+8] shl ax, 1 add ax, b ; mov ax, [bp+6] ret; retf 4 Select ENDP Start: mov ax,2 push ax mov ax,3 push ax call Select ; return code? mov ah, 4ch int 21h END Start e) Программа с директивой ARG (модель LARGE) .MODEL LARGE .CODE Select PROC a: WORD, b:WORD push bp mov bp, sp ; mov ax, [bp+6] mov ax,a shl ax.l add ax,b ; add ax, [bp+8] pop bp ret; retf Select ENDP Start: mov ax,2 push ax mov ax,3 push ax call Select ; return code8
280 Часть IY. Турбо Ассемблер add sp,4 mov ah, 4ch int Zlh .STACK lOOh END Start Директива LOCAL Директива LOCAL имеет общий вид LOCAL arg, arg, ..., arg = size где arg является описанием локальной переменной процедуры, a size - идентификатором, с которым связан размер поля, занимаемый локальной переменной (выраженный в байтах). Каждый arg имеет один из следующих типов: пате name: type name: type: count, где name, type, count интерпретируются так же, как и в директиве ARG. Интерпретация директивы LOCAL вызывает связь с каждым идентификатором пате выражения вида [bp - offset], где offset является смещением младшего значащего байта, занимаемого рас- сматриваемым аргументом или результатом, от младшего из пары байтов, в которые в прологе процедуры было переслано содержимое регистра ВР. При- нимается, что переменные, указанные в -директиве LOCAL, размещены в сте- ке таким образом, чтобы в модели TPASCAL первая переменная находилась в младшем адресе, в остальных случаях (в том числе при отсутствии дирек- тивы .MODEL) первая переменная находится в старшем адресе. Вопреки возможному предположению, интерпретация директивы LOCAL не вызывает фактического резервирования места для локальных переменных процедуры. Исключение из этого правила составляет модель TPASCAL, в которой происходит генерирование команды sub sp,size, в которой size является явным или подразумеваемым идентификатором, с ко- торым связано число, определяющее размер области локальных переменных. Остается добавить, что директивы LOCAL могут в данной процедуре высту- пать многократно. В этом случае принимается, что каждая пара следующих друг за другом директив, например LOCAL argl, argl, ..., argl и LOCAL arg2, arg2....... arg2
75. Ассемблер равнозначны директиве LOCAL argl, argl..... arg2, arg2, arg2. Примеры. Использование директивы LOCAL а) Директива LOCAL вне зоны действия директивы .MODEL LOCAL a: near ptr word:5, b: byte:3 mov ax, a ; равнозначно mov ax, [bp-10] mov al, b+1 ; равнозначно mov al, [bp-12] б) Программа с директивой LOCAL (модель TPASCAL) StackSEGMENT STACK DB lOOh DUP(?) Stack_ENDS .MODEL TPASCAL .CODE Select PROC LOCAL a:WORD, b:WORD mov a, 2 ; mov[bp-4], 2 mov b, 3 ; mov[bp-2], 3 mov ax, [bp-2] ret ; retf4 Select ENDP Start: call Select ; return code 3 mov ah, 4ch int 21h END Start в) Программа с директивой LOCAL (модель LARGE) •MODEL LARGE • CODE Select PROC LOCAL a: WORD, b: WORD push bp mov bp, sp mov a, 2 ; mov[bp-2], 2 mov b, 3 ; mov[bp-4], 3 mov ax, [bp-2] pop bp ret ; retf Select ENDP
282 Часть ГУ. Турбо Ассемблер Start: call Select ; return code 2 mov ah, 4ch int 21h .STACK lOOh END Start 15.3.2. Гибридное программирование Ассемблер-Паскаль Программа, написанная на языке Турбо Паскаль, состоит (в порядке размещения в оперативной памяти) из следующих частей: - префикса программы (PSP); - сегмента кода главного модуля (CODE); - сегмента ^кода библиотечного модуля; - сегмента кода резидентной библиотеки; - сегмента глобальных данных (DATA); - сегмента стека; - буфера наложения; - кода. Первый б^йт префикса программы указывается описанием глобальной переменной PrefixSeg. Каждый из сегментов, конечно, не может занимать больше 64 Кбайт памяти, а их общий размер не превышает 1 Мбайт. Размер сегмента стека по умолчанию - 16 Кбайт, размер кода опре- деляется числом байтов памяти, находящихся над стеком. Размер стека и кода может быть явно определен с помощью директив процессора. Регистры Процедура вызова из наскального модуля должна сохранять содержание регистров DS, SS, SP и ВР. Принимается, что регистр SS содержит номер сегмента стека, регистр DS - номер сегмента глобальных данных, а регистр ВР (перед переходом к выполнению пролога процедуры) - указание места в стеке процессора, где хранится содержимое регистра ВР той процедуры, которая вызвала данную процедуру. Переменные, метки, процедуры Переменные и процедуры, описанные в главном модуле вне процедур, а также переменные и процедуры, описанные в общей части библиотечного модуля, могут стать доступными в модуле ассемблера. Условием доступности является применение в модуле ассемблера директивы EXTRN. Переменные, описанные в модуле ассемблера, доступны по их именам только в этом модуле. Однако ничто не мешает обращаться к ним с помощью процедур или глобальных указывающих переменных. Требуется, чтобы рассматриваемые пе- ременные были описаны без инициализации (задания начальных значений). Описанные в пасхальном модуле метки не доступны в модуле ассемблера. Описанные в модуле ассемблера метки могут быть доступны в пасхальном модуле только тогда, когда они являются метками процедур. Описанная в модуле ассемблера процедура доступна в пасхальном модуле тогда и _только
15. Ассемблер 283 тогда, когда - в модуле ассемблера идентификатор процедуры выступает в директиве PUBLIC или GLOBAL; - в наскальном модуле имеется описание, предваряющее эту процедуру ключевым словом external; - в паскальном модуле имеется директива компилятора, определяющая имя файла с расширением .OBJ, в котором находится скомпилированная связка ассемблер процедура. Описанные в главном модуле процедуры, а также процедуры, описанные в личной части библиотечных модулей, считаются близкими, описанные в общей части библиотечных модулей процедуры - далекими. Этот факт должен учитываться при вызове их из модуля ассемблера. Аргументы и результаты Аргументы процедуры Паскаль указываются в стеке процессора и сопроцессора. Результаты остаются в регистрах, в стеке процессора или в стеке сопроцессора. Если параметр связан с постоянной, то - аргумент целого типа, аргумент типа real и аргумент указательного типа указываются в стеке процессора (если есть тип byte, то перед выполнением этой операции аргумент дополняется старшим байтом неопределенного значения); - аргументы типа single, double, extended и comp указываются в стеке сопроцессора; - аргумент типа цепочки указывается с помощью указателя, помещенного в стеке процессора; - аргумент табличного типа и типа записи размерами 1, 2 либо 4 байт указываются в стеке процессора, остальные аргументы этого типа (в том числе 3-байт аргументы) указываются с помощью указателя, размещенного в стеке процессора; - аргумент многословного типа указывается с помощью указателя его 32- бит эквивалента, размещенного в стеке процессора. Если параметр связан с переменной, то независимо от типа аргумента в стеке помещается его указатель. Внимание: Однобайтовые аргументы указываются как двубайтовые с неопределенным значением старшего байта. Результат процедуры остается в следующих местах: - результат целого типа остается в регистре AL (если однобайтовый), в регистре АХ (если 2-байт), в паре регистров DX-AX (если 4-байт); - результат типа real остается в DX-BX-AX; - результат типа single, double, extended и comp остается в верхушке стека сопроцессора; - результат типа цепочки остается в стеке процессора в месте, зарезервированном перед вызовом процедуры; - результат указательного типа остается в DX-AX. Рекомендуется использование следующих сочетаний параметров и аргументов процедур:
284 Часть IY. Турбо Ассемблер Турбо Паскаль Турбо Ассемблер byte BYTE word WORD shortint BYTE integer WORD real WORD single FWORD double DWORD extended TBYTE comp QWORD pointer DWORD Примеры. Гибридные программы а) Простая гибридная программа Модуль языка Паскаль program TPASCAL {$L L:bra} function Fun : integer external; begin Writeln(Fun) end Модуль ассемблера Libra • MODEL TPASCAL PUBLIC Fun • CODE Fun PROC NEAR mov ax,13 ret Fun ENDP END Выполнение программы приводит к выводу числа 13. б) Связь программ Модуль языка Паскаль program Mixed {$L LIB.OBJ} procedure PutlFix:integer); external; function Get(var Res: integer) : integer; external; var Res : integer; begin Put(13);
13. Ассемблер 283 Writeln(Get(Res)); Writeln(Res) end Модуль ассемблера LIB . MODEL TPASCAL PUBLIC Get. Put .DATA f Loc DW? .CODE Put PROC NEAR ARG Val: WORD mov ax, Val mov Loc, ax ret Put ENDS Get PROC NEAR Ref:DWORD les di. Ref mov ax, LOC mov [es:di] ax ret Get ENDP END 15.3.3. Гибридное программирование Ассемблер-Си Программа, написанная на языке Турбо Си, состоит (в порядке размещения в оперативной памяти) из следующих частей: - префикса программы (PSP); - сегментов класса 'CODE'; - сегментов не класса 'CODE' и не принадлежащих группе DGROUP; - сегментов группы DGROUP, не являющихся классом 'STACK'; - в моделях TINY, SMALL и MEDIUM области, близкой к коду; - сегмента стека; - в моделях COMPACT, LARGE и HUGE области, далекой от кода. Регистры Процедура, вызванная из модуля на языке Си должна сохранить содержимое регистров DS, SS, SP и ВР. Если в модуле Си использованы регистры SI и DI, то их содержимое также должно быть сохранено в процедуре на языке ассемблер. В момент вызова процедуры на языке Си в регистре DS находятся следующие номера сегментов: Модель DS TINY dgroup SMALL dgroup COMPACT dgroup MEDIUM dgroup
286 Часть IY. Турбо Ассемблер LARGE dgroup HUGE cfile_DATA (dproup является именем первого сегмента группы DGROUP, a cfile - именем вызванного модуля) Принимается, что регистр SS содержит номер сегмента стека, а регистр ВР (перед выполнением пролога процедуры) - указание места в стеке процессора, где сохраняется содержимое регистра ВР вызванной процедуры. Переменные, функции, метки Те переменные и' функции в модуле языка Си, которые относятся к классу extern, могут стать доступными с помощью их идентификаторов в модуле ассемблера. Условием доступности является использование в модуле ассемблера директивы EXTRN, в которой идентификатор переменной или функции языкам Си предварен знаком — (подчеркивание). Переменные, функции и метки, описанные в модуле ассемблера, могут стать доступными с помощью их идентификаторов в модуле языка Си. Условием доступности является, чтобы каждый такой идентификатор - в модуле ассемблера начинался знаком — (подчеркивание); - в модуле ассемблера был указан в директиве PUBLIC или GLOBAL; - в модуле языка Си был указан в директиве extern. Ни один идентификатор метки, описанный в модуле языка Си и ни один идентификатор класса auto, register и static не может быть доступным в модуле ассемблера. Внимание: Компиляция модуля ассемблера должна быть выполненна с опцией /МХ или /ML, а в системе Турбо Си должен быть создан проект, в котором будет указан модуль языка Си и модуль ассемблера с расширением .OBJ. Аргументы и результаты Аргументы функции языка Си указываются в стеке процессора. Результаты остаются в регистрах. Однобайтовый результат остается в регистре AL, двухбайтовый - в регистре АХ, 4-байт результат (за исключением результата типа float) - в паре регистров DX-AX, а результат с плавающей точкой - в верхушке стека сопроцессора. Результат любого другого вида ( в том числе 3-байт структура) остается в статичной области с указателем в регистре АХ или в паре регистров DX-AX. Внимание: Аргументы однобайтовые указываются как двубайтовые с неопределенным значением старшего байта. Рекомендуется использование следующих сочетаний параметров и аргументов процедур: Турбо Си Турбо Ассемблер unsigned char char BYTE BYTE
15. Ассемблер 287 enum WORD unsigned short WORD short WORD unsigned int WORD int WORD unsigned long DWORD long DWORD float DWORD double QWORD long double TBYTE near* WORD far DWORD (также type (near*)() WORD) (также type (far)*() DWORD) Примеры. Смешанные программы a) Простая смешанная программа Модуль Си finclude <stdio.h> extern int Fun(void); main() { printfC'Xd", Fun()); return 0; } Модуль Ассемблера .MODEL SMALL PUBLICFUN CODE FUN PROC NEAR mov ax,13 ret _Fun ENDP END Выполнение программы приводит к выводу числа 13. Для ее выполнения необходимо: скомпилировать модуль ассемблера в файл LIBRA.OBJ (с опцией /МХ), а в системе Турбо Си (например, в модели SMALL) создать проект, содержащий название LIBRA.OBJ. б) Связь программ Модуль Си finclude <stdio.h> extern void Put(int Fix); extern int Get(int*Res); int Res; main()
288 Часть ГУ. Турбо Ассемблер Put(13); print("Xd", Get(&Res)); printf("%d", Res); return 0; } Модуль ассемблера •MODEL LARGE PUBLIC_Get,_Put • DATA Loc DW? • CODE Put PROC ARG VOL: WORD push bp r* mov bp, sp mov ax, Val mon Loc, ax pop bp ret .Put ENDP Get PROC Ref: DWORD push bp mov bp, sp les di. Ref mov ax, Loc mov [es:di}, ax pop bp ret _Get ENDP END 15.4. Команды В общем случае элементами приведенных здесь описаний команд являются Имя Синтаксис Мнемоника команды, состояние регистра флажков FL Описание после ее выполнения (указанные разряды установлены в 1, вопросительный знак означает неопределенное состояние данного разряда). Описание результатов выполнения команды. Замечания Дополнительный комментарий. Аргументы Структура аргументов, их возможные виды. Пример Фрагмент программы, содержащий указанную команду.
15. Ассемблер 289 Команды микропроцессоров Intel 8088 и Intel 80286 (real mode) AAA Синтаксис Описание ааа FL: а с, о=? р=? s=? z=? Коррекция суммы двух неупакованных цифр в коде BCD. Замечания Если AF = 1 или (AL AND Ofh)>9, то выполняются следующие операции: AL := AL + 6 АН := АН + 1 AF := 1, АС := 1 в противном случае операция AF := 0, АС = 0. Затем выполняется операция AL := AL AND ofh Аргументы отсутствие ааа Пример mov ax, 9 add ax, 4 ааа ; АН=1, AL=3, т.е. 9+4=13 AAD Синтаксис Описание aad FL: р s z, а=? с=? о=? Подготовка двух неупакованных цифр в коде BCD (младшая часть в AL, старшая в АН) для выполнения деления, результат которого также будет неупакован. Замечания Выполнение следующих операций: AL := 10 * АН + AL АН := 0 Аргументы отсутствие Пример aad , mov ah, 3 mov al, 2 aad ; АХ = 32 AAM Синтаксис Описание aam FL р s z, а=? с=? о=? Получение двух неупакованных цифр в коде BCD (старшая часть в АН и младшая в AL) после выпол- Замечания нения умножения двух неупакованных цифр. Выполнение следующих операций: АН := AL DIV 10 AL := AL MOD 10
290 Часть 1Y. Турбо Ассемблер Аргументы отсутствие Пример аам mov al, 25 аам ; АН - 2, AL - 5 AAS Синтаксис Описание aas FL: a c, o=? p=? s=? z=? Выполнение коррекции разности двух неупакованных цифр в коде BCD. Замечания Если AF « 1 или (AL AND Ofh)> 9, то выполняются следующие операции: AL := AL-6 АН := АН-1 AF := 1. CF := 1 в противном случае операции AF := 0, CF := 0 » Затем выполняется операция AL := AL AND Ofh Аргументы отсутствие Пример mov ах, 34h sub al, 15h aas aas ; AL = 09 h ADC Синтаксис Описание Замечания Аргументы reg, con mem, con reg, reg reg, mem mem, reg adc dst, src FL: а с о p s z Сложение двух целых чисел с учетом переноса dst := dst + src + CF adc bl, 3 adc [Fix], 300 adc ah, bl adc bh, [byte ptr Fix] adc [Fix + si + bx + 2], ex Пример mov ax, -1 shl ax, 1 adc ax, 1 ; AX = 0 ADD Синтаксис Описание Замечания add dst, src FL: а с о p s z Сложение двух целых чисел dst := dst + src
15. Ассемблер 291 Аргументы reg, con mem, con reg, reg reg, mem mem, reg add bl,3 add [byte ptr Fix + di], 300 add ax, ax add bh, [byte ptr Fix] add [byte ptr Fixt + si], al Пример mov ax, 257 mov bl, 3 add ah, Ы ; AH = 4 AND Синтаксис Описание Замечания and dst, src FL: c=0, o=0, psz, a=? Поразрядное логическое произведение dst := dst AND src CF := 0, OF := 0 Аргументы reg, con mem, con reg, reg reg, mem mem, reg Пример mov ax, -1 and al, -1 and ax, -1 and [byte ptr Fix +2], offfh and ax, ax and bx, [Fix - 2] and [Fix + si], bx ; AX = 255 BOUND 286 Синтаксис Описание Замечания bound reg, dst FL: без изменений Проверка индекса массива. Если индекс, содержащийся в указанном регистре, не помещается в интервале, ограниченном двумя последо- вательными словами, находящимися по адресу dst, то генерируется прерывание с номером 5. Аргументы reg, mem bound di, [bx] Пример mov [word ptr Arr - 4], 5 mov [word ptr Arr - 2], 7 mov bx, 2 bound bx, [Arr - 4] ; происходит прерывание CALL Синтаксис Описание call dst FL: без изменений Вызов процедуры.
292 Замечания Часть JY. Турбо Ассемблер Если аргумент является меткой (1аЫ6 - близкой, или 1аЬ32 - далекой), то происходит переход на команду, определенную этой меткой. В остальных случаях про- исходит переход на команду, адрес которой находится в регистре или в области памяти. Если аргументом команды call является 1аЬ32 или шеш32, то происходит изменение содержимого регистра CS. В случае вызова близкой процедуры адрес возврата сос- тоит из одного слова, извлекаемого из стека в регистр IP, а в случае вызова далекой процедуры - извлека- ется также слово и в регистр CS. Только вызовы с аргументами 1аЫ6 осуществляются со смещением относительно IP. Аргументы 1аЫ6 call hear ptr Here 1ab32 call far ptr There regl6 г> call ax 1№Ш16 call [word ptr bx] mem32 Пример call near ptr Lab Lab LABEL NEAR call [dword ptr Ref + bx] pop ax ; AX = OFFSET Lab CBW Синтаксис Описание Замечания Аргументы отсутствие Пример mov al, 255 cbw cbw FL: без изменений Расширение знака байта регистра AL, образуя слово в регистре АХ. В регистре АХ образуется слово в дополнительном коде. cbw ; АХ = -1 CLC Синтаксис Описание Замечания Аргументы отсутствие Пример mov al, -1 clc rd al, 1 clc FL: c = 0 Сброс флажка переноса с CF := О clc ; AL = -2
15. Ассемблер 293 CLD Синтаксис cld FL: d = 0 Описание Сброс флажка направления d. Замечания Аргументы DF := 0 отсутствие Йример mov di, 10 cld cld movsw ; DI = 12 CLI Синтаксис cli FL: 1 = 0 Описание Сброс флажка прерывания i. Замечания IF := 0 Аргументы отсутствие cli Пример (для системы DOC) mov ах, 40 h mov ds, ax mov bx, 6ch cli mov ax, [bx] mov dx, [bx + 2] ; DX - AX = время суток CMC Синтаксис Описание Замечания Аргументы отсутствие стс FL: с Инверсия флажка переноса с. CF := NOT CF стс Пример mov ах, 0 cic стс adc ах, 0 ; АХ = 1 СМР Синтаксис crop dst, src FL: а с о p s z Описание Определение разности dst - src с целью установки флажков регистра FL без размещения где-либо результата операции.
294 Часть IY. Турбо Ассемблер Замечания Аргументы reg, con mem, con reg, reg reg, mem mem, reg Пример 9 mov ax, 3 cmp ax, 4 jl Lab О результате сравнения аргументов, трактуемых как числа со знаком, можно судить с помощью команд jg, jge, JL jle, Jng, jnge, jnl, jnle, а о результате сравнения аргументов, трактуемых как числа без знака, - с помощью команд ja, jae, jb, jbe, jna, jnae, jnb, jnbe. О результате сравнения аргументов на их равенство и не равенство можно судить с помощью команд je и jne. cmp bh, 20 cmp [word ptr Fix - 2 + si], -2 cmp ax, dx cmp bx, [si] cmp [si + r], al ; переход будет выполнен CMPSB Синтаксис Описание Замечания Аргументы отсутствие Пример mov ах, ds mov es, ах mov di, OFFSET Trg mov si, di cmpsb je Lab cmpsb FL: а с о p s z Сравнение байтов на основе разности [byte ptr ds:si] - [byte ptr es:di] а затем, если OF = 0, увеличение SI и DI на 1, если DF = 1, уменьшение SI и DI на 1. О результате сравнения байтов как чисел со знаком можно судить с помощью команд jg. jge, jl, jle, jng, jnge, jnl, jnle, а о результате сравнения байтов как чисел без знака можно судить с помощью команд ja, jae, jb, jbe, jna, jnae, jnb, jnbe. О результате сравнения на равенство и не равенство можно судить с помощью команд je и jne. cmpsb ; переход будет выполнен CMPSW Синтаксис cmpsw FL: а с о р s z
15- Ассемблер 295 Описание Сравнение слов на основе разности [word ptr ds:si] - [word ptr es:di], а затем, если DF=O, увеличение SI и di на 2, если DF»1, уменьшение SI и DI на 2. Замечания О результате сравнения слов как чисел со знаком можно судить с помощью команд jg. jge, jl, jle, jng.jnge, jnl, jnle, о результате сравнения слов как чисел без знака можно судить с помощью команд ja, jae, jb, jbe, jna, jnae, jnb, jnbe. О результате сравнения на равенство и не равенство можно судить с помощью команд je и jne. Аргументы отсутствие cmpsw Пример mov ах, ds mov es, ах mov di OFFSET Trg mov si, di cmsw jz Lab ; переход будет выполнен CWD Синтаксис cwd FL: без изменений Описание Расширение знака слова регистра АХ до двойного слова в паре регистров DX - АХ. Замечания Аргументы В паре регистров DX - АХ образуется двойное слово в дополнительном коде. отсутствие Пример mov ах, -1 cwd cwd ; DX - -1, АХ » -1 DAA Синтаксис daa FL: acopcz, о = 1 Описание Выполнение коррекции после размещения в регистре AL суммы двух упакованных цифр в коде BCD. Замечания Если AF = 1 или (AL AND ofh) > 9, то выполняются следующие операции: AL := AL + 6, AF := 1. Затем, если CF = 1 или AL > 9fh, выполняются операции AF := AL + 60h, CF := 1.
296 Часть IY. Турбо Ассемблер Аргументы отсутствие Пример mov al, 19h add al, 19h daa daa ; AL - 38 h DAS Синтаксис Описание Замечания Аргументы отсутствие Пример mov al, 12h sub al, 03h das das FL := a c p s z, 0 = ? Выполнение коррекции после размещения в регистре AL разности двух упакованных цифр в коде BCD. Если AF = 1 или (AL AND ofh) > 9, то выполняются следующие операции: AL := AL-6, AF := 1. Затем, если CF = 1 или AL > 9fh, выполняются операции AL := AL - 60h, CF := 1. das ; AL = 09h DEC Синтаксис Описание Замечания Аргументы reg mem Пример mov ax, о dec al dec dst FL: a о p s z Уменьшение на единицу значения переменной, определенной аргументом. dst := dst - 1 dec ex dec [byte ptr Fix + si + di + 2] ; AX = 255 DIV Синтаксис Описание div dst FL: a=? c=? o=? p=? s=? z=? Получение частного от деления делимого в регистре АХ (или в паре регистров DX-AX) на делитель, определенный аргументом dst. Трактовка делимого, делителя, частного и остатка как данных без знака.
1,5. Ассемблер 297 Замечания В случае 8-бит аргумента выполнение операции temp := АХ АН := temp HOD dst AL := temp DIV dst. а в случае 16-бит аргумента выполнение операции temp := DX - АХ DX :« temp HOD dst АХ: = temp DIV dst. Если частное не помещается в регистре, то происходит прерывание с номером 0. Аргументы reg mem Пример mov ax, 32 mov Ы, 6 div Ы div ах div [byte ptr Fix] ; АН = 2, AL = 5 ENTER Синтаксис Описание Замечание enter con, 1 FL: без изменений Создание рамки вызова процедуры. Копирование в стек I указателей предварительных рамок и резервирование con байтов для локальных переменных. В общем виде выполнение следующих действий: lev := / HOD 32 push BP ptr := SP. Если lev > 0, выполнение lev - 1 раз операции BP := BP - 2 push [BP], Затем push ptr. В каждом случае выполнение операции BP := ptr SP := SP-con Аргументы соп16, соп8 Пример enter 4,0 enter 20,2 ; равнозначно выполнению команд push bp mov bp, sp sub sp, 4 ESC Синтаксис Описание esc id, dst FL: без изменений РазЛещение на адресной шине команд сопроцессора.
298 Часть IY. Турбо Ассемблер Замечания Аргументы con, reg con, mem Пример esc 8, cl Первый аргумент должен быть не больше 6 бит. Он определяет операционную часть команды сопроцессора. esc 2, al esc 6, [word ptr Fix + si] ; команда fid st(l) HLT Синтаксис Описание Замечания и Аргументы отсутствие Пример hit hit FL: без изменений Задержка выполнения команд. Возобновление выполнения команд наступит после устранения прерывания. IDIV hit ; без комментария Синтаксис Описание Замечания Аргументы reg mem Пример mov ах, 10 mov Ы, -4 idiv be idiv dst FL: a=? c=? o=? p=? s=? z=? Получение частного от деления делимого в регистре АХ (или в паре регистров DX-AX) на делитель, определенный аргументом dst. Трактовка делимого, делителя, частного и остатка как данных со знаком. В случае 8-бит аргумента выполнение операции temp := АХ AN := temp MOD dst AL := temp DIV dst, а в случае 16-бит аргумента выполнение операции temp := DX - АХ DX := temp MOD dst AX := temp DIV dst. Если частное не помещается в регистре, то происходит прерывание с номером 0. idiv ah idiv [word ptr Fix] ; AH = 2, AL - -2 IMUL Синтаксис imul dst FL: c o, a=? p=? s=? z=?
IS. Ассемблер 299 Описание Замечания Аргументы reg шел Пример mov al, 2 mov ah, -4 imul ah Получение произведения содержимого регистра AL (или АХ) и операнда, определенного аргументом dst. Трактовка множителей и произведения как данных со знаком. В случае 8-бит аргумента выполнение операции АХ := AL * dst, а в случае 16-бит аргумента выполнение операции DX-AX АХ * dst. Кроме того, если после выполнения операции левая половина результата повторяет старший бит правой половины, то выполняется операция CF := О, OF := О, в противном случае CF := 1, OF := 1. imul si imul [byte ptr Fix + bx] ; Ax = -8, CF = 0, OF = 0 IN Синтаксис Описание Замечания Аргументы асе, con 8 асе, DX Пример mov dx, 32 in al, dx in acc, src FL: без изменений Введение в аккумулятор (только AL или АХ) данного из порта, определенного аргументом src. Аргумент src может быть только номером порта или именем регистра DX (в котором находится номер порта). in al, 32 in ах, dx ; ввод данного из порта 32 INC Синтаксис Описание Замечания Аргументы reg mem Пример mov al, 0 inc al inc dst FL: a о p s z Увеличение на единицу значения переменной, определенной аргументом. dst: = dst + 1 inc ch inc [word ptr Fix + di + 2] ; AL = 1
300 Часть IY. Турбо Ассемблер INSB Синтаксис Описание insb FL: без изменений Введение байта данных из порта с номером в регистре DX и размещение его по адресу ES: DI. Если DF-O, DI увеличивается на 1, в противном случае DI уменьшается на 1. Замечания Команда insb может быть предварена знаком повторения, но не может быть снабжена префиксом. Аргументы отсутствие Пример les dit I^Ref] mov dx, 32 cld mov ex, 3 rep Insb insb ; ввод 3 знаков из порта с номе- ром 32 INSW 286 Синтаксис Описание insw FL: без изменений Введение слова данных из порта с номером в регистре DX и размещение его по адресу ES: DI. Если DF = 0, то DI увеличивается на 2, в противном случае 01 уменьшается на 2. Замечания Команда insb может быть предварена знаком повторения, но не может быть снабжена префиксом. Аргументы отсутствуют Пример les di, [Ref] mov dx, 32 insw insw ; ввод слова из порта с номером 32 INT Синтаксис Описание int num FL: it Генерирование программного прерывания с номером, Замечания определенным аргументом num. Пересылка в стек процессора содержимого регистра FL, обнуление IF и TF, затем пересылка в стек содержимого регистров CS и IP, а также загрузка регистров CS: IP данными из двух слов из адреса 0:4 * num. Аргументы int con 8 int 21 h
15. Ассемблер 301 Пример mov ах, 4 cOOh int 21 h ; окончание выполнения программы INTO Синтаксис Описание into FL: без изменений Если OF-1, генерирование программного прерывания с номером 4. Замечания Пересылка в стек процессора содержимого регистра FL, обнуление IF и TF, затем пересылка в стек содержимого регистров CS и IP, а также загрузка CS: IP данными из двух слов из адреса 0:16. Аргументы отсутствие Пример mov al, 127 add al, al into into ; прерывание будет выполнено ШЕТ Синтаксис Описание Замечания iret FL:??? Возврат из прерывания. Извлечение из стека трех слов и размещение их в очередности извлечения в регистрах IP, CS, FL. Аргументы отсутствие Пример iret iret ; без комментария Jcond - например, JA, JG, JE, JZ, JC Синтаксис Описание jcond lab FL: без изменений Выполнение условного перехода на команду, снабженную меткой lab. Замечания Переход реализуется как короткий и умеренный. В записи команды определено условие выполнения перехода. Если оно не выполнено, то продолжается выполнение программы. В указанных ниже сочетаниях определения выше - ниже касаются переходов, выпол няемых после сравнения данных без знака, а опреде ления больший - меньший касаются переходов, выпол няемых после сравнения данных со знаком. Команда Условие ja перейти, если выше jae перейти, если выше или равно jb перейти, если ниже jbe перейти, если ниже или равно
302 Часть IY. Турбо Ассемблер Аргументы lab Пример mov ах, 255 and al, al js Next JMP jc jcxz je jg jge jl jle jna jnae jnb jnbe jne jne jng jnge jnl jnle jno jnp jns jnz jo jp jpe jpo js iz перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если перейти, если jg short Skip есть перенос СХ равно О равно больше больше или равно меньше меньше или равно не выше не выше или равно не ниже не ниже или равно нет переноса не равно не больше не больше или равно не меньше не меньше или равно нет переполнения нечетное не отрицательное не нуль есть переполнение четное четное нечетное отрицательное нуль ; условный переход будет выполнен Синтаксис Описание Замечания Аргументы 1аЬ8 1аЫ6 1аЬ32 regie jmp dst FL: без изменений Переход на команду, адрес которой определен аргументом. Если аргумент является меткой (1аЬ8 - очень близкой, 1аЫ6 - близкой, 1аЬ32 - далекой), то происходит переход на команду, снабженную этой меткой. В остальных случаях происходит переход на команду, адрес которой находится в регистре или в области памяти. Если аргументом команды является 1аЬ32 или mem32, то происходит изменение содержимого регистра CS. Только переходы с аргументами 1аЬ8 и 1аЫб осуществляются со смещением относительно IP. jmp short Close jmp near There jmp far Here jmp ax
303 IS. Ассемблер mem!6 jmp [word ptr bx + di] mem32 jmp [dword ptr Ref + bx] Пример jmp short skip ; обход директивы DB Value DB13 Skip mov al, [cs: Value] LAHF Синтаксис Описание Замечания lahf FL: без изменений Загрузка в регистр АН содержимого регистра FL. Загрузка касается только флажков s z а р с* Они будут помещены в АН следующим способом: Аргументы отсутствие Пример stc lahf rcr ah jc Skip lahf ; переход будет выполнен LDS Синтаксис Ids reg, mem FL: без изменений Описание Загрузка в регистр reg слова данных из адреса, определенного mem и в регистр DS слова данных, непосредственно следующего за уже загруженным. Замечания Команда дает возможность быстрой загрузки дальнего адреса. Аргументы regl6, mem32 Ids si, [dword ptr Fix + bx] Пример Ids di, [dword ptr Ref] mov ax, [di] ; промежуточная загрузка слова LES Синтаксис Описание Замечания les reg, mem FL: без изменений Загрузка в регистр reg слова данных из адреса, определенного mem, и в регистр ES слова данных, непосредственно следующего за уже загруженным. Команда дает возможность быстрой загрузки дальнего адреса.
304 Часть IY. Турбо Ассемблер Аргументы -regl6, mem32 les si, [dword ptr Fix + bx] Пример les di [dword ptr Ref + bx] mov cl, [es:di + 2] ; промежуточная загрузка байта LEA Синтаксис lea reg, mem FL: без изменений Описание Загрузка в регистр reg эффективного адреса mem. Замечания Несмотря на то что выполнение, например, команды lea bx, [word ptr Fix] имеет тот же результат, что и выполнение команды н Аргументы regl6, meml8 mov bx, OFFSET Fix. команда lea более общая, поскольку, например,команда mov bx, OFFSET [byte ptr Fix + dx] ошибочна, но команда lea bx, [byte ptr Fix+dx] верна. lea si, [byte ptr Fix] regl6, nwnl6 lea di, [si] reg!6, mem32 lea ax, [qword ptr Fix+bx+1] и Т.Д. Пример lea bx, [Ref] jmp [bx] ; равнозначно jmp [word ptr Ref] - LEAVE 286 Синтаксис leave FL: без изменений Описание Возврат из процедуры высокого уровня. Замечания Выполнение операции Аргументы отсутствие mov sp, bp pop BP leave Пример enter 0,2 leave ; восстановлено состояние стека 0 LOCK Синтаксис lock FL: без изменений Описание Блокирование остальных процессов во время выполнения команды, следующей после LOCK. Замечания Выполнение команды не вызывает других результатов, кроме введения блокировки.
Аргументы отсутствие Пример mov al, 1 Check: lock xc hg Flare, test al, al jnz Check mov Flare, 0 15. Ассемблер 305 al lock ; установка семафора ; критический район LODSB Синтаксис lodsb FL: без изменений Описание Загрузка в регистр AL байта из адреса DS: SI. Если DF = 0, увеличение SI на 1, в противном случае уменьшение SI на 1. Замечания Команда lodsb может быть предварена знаком повторения, но пригодность его сомнительна. Аргументы отсутствие lodsb Пример Ids si, [dword ptr Ref] lodsb ; промежуточная загрузка байта LODSW Синтаксис lodsw FL: без изменений Описание Загрузка в регистр AL байта из адреса DS: SI. Замечания Если df = 0, увеличение si на 2, в противном случае уменьшение SI на 2. Команда lods может быть предварена знаком повторения, но пригодность его сомнительна. Аргументы отсутствие lodsw Пример Ids di, [dword ptr Ref] lodsw ; промежуточная загрузка слова LOOPcond Синтаксис Описание loopcond lab FL: без изменений Уменьшение содержимого регистра СХ на 1, затем, если содержимое СХ не равно 0, а также выполнено условие, указанное в команде, переход на команду, Замечания снабженную меткой lab. ' Переход реализуется как короткий и умеренный. В записи команды определено условие выполнения 1—764
306 Часть IY. Турбо Ассемблер перехода. Если оно не выполняется, то продолжается выполнение программы. Команда Условие loop переход, если сх # О loopne переход, если сх # О и не равно (ZF=O) loopnz переход, если СХ 1 0 и не равно (ZF=0) кюре переход, если сх # 0 и равно (ZF=1) loopz переход, если СХ # 0 и равно (ZF=1) Аргументы 1аЬ8 Пример mov сх, 2 mov ах, 1 Again: shl ах, Р loop Again nop loop Again MOV Синтаксис Описание Замечания mov dst, src FL: без изменений Пересылка содержимого src в dst. Если dst является сегментным регистром, то содержимое src не может быть представлено постоянным выражением, а также не может содержаться в сегментном регистре. Если dst является регистром SS, то происходит блокирование прерываний на время выполнения следующей команды. Аргументы reg, con mem, con reg, reg reg, mem mem, reg Пример mov ax, 12 mov ds, ax mov ah, 20 mov [word ptr Fix + 2], mov ds, di mov di, [word ptr bx] mov [bp + si], sp MOVSB ; АХ = 4 ; DS = 12 3 Синтаксис Описание Замечания Аргументы отсутствие movsb FL: без изменений Пересылка байта из адреса DS:SI в адрес ES:DI. Кроме того, если DF = 0, увеличение SI и DI на 1, а если DF = 1, уменьшение si и DI на 1. Команда movsb может быть предварена знаком повторения. movsb
307 Пример Ids si, [dword ptr SrcRef] les di, [dword ptr DstRef] old movsb ; пересылка трех movsb ; последовательных байтов movsb ; вперед MOVSW Синтаксис Описание movsw FL: без изменений Пересылка слова из адреса DS: SI по адресу ES: DI. Кроме того, если DF=0, увеличение SI и DI на 2, а если DF «= 1, уменьшение SI и DI на 2. Замечания Команда movsw может быть предварена знаком повторения. Аргументы отсутствие Пример movsw Ids si, [dword ptr SrcRef] les di, [dword ptr DstRef] std movsw movsw movsw ; перенос трех последовательных байтов назад MUL Синтаксис Описание mul dst FL: c o, a-7 p-7 s-7 z-7 Получение произведения содержимого регистра AL или АХ и операнда, определенного ар1ументом dst. Трактовка множителей и произведения как данных без знака. Замечания В случае 8-бит аргумента выполнение операции АХ :«= AL * dst, а в случае 16-бит аргумента - операции DX - АХ: - АХ * dst. Если результат операции представлен большим числом битов, чем аргумент, то CF :- 1, OF 1, в противном случае CF :- 0, 0F :- 0. Аргументы reg mem Пример mov al, 3 mul bh mul [word ptr Fix]
30S mul al Часть IY. Турбо Ассемблер ; AX-9, CF, 0F«0 NEG Синтаксис neg dst FL: а с о p s z Описание Изменение знака операнда dst на обратный. Замечания Изменение знака основано на инверсии всех битов операнда и прибавлении числа 1. Аргументы reg neg ах mem neg [ds: byte ptr Fix + 6p] Пример mov bx, -J neg bh ; BX = 511 NOP Синтаксис Описание • Замечание Аргументы отсутствие Пример пор пор Выполнение Выполнение результатам FL: без изменений пустой операции, команды не приводит к каким-либо пор ; без комментария NOT Синтаксис Описание Замечания Аргументы reg пет Пример mov bx, -2 not bx not dst FL: без изменений Инверсия (отрицание) битов операнда Бит со значением 1 меняется на 0 и not di not [byte ptr bx-3] ; BX=1 dst. наоборот. OR Синтаксис Описание Замечания or dst, src FL: psz, c=, o=0, a=? В dst записывается результат поразрядной логической суммы dst и src. Логическая сумма вычисляется в соответствии со следующей таблицей:
IS. Ассемблер 309 Bit dst Bir src Bit dst OR src Аргументы rg, con mem, con reg, mem mem, reg Пример mov ax, Och or ah, Ofh 0 0 0 0 1 1 1 0 1 1 1 1 Если src является 8-бит операндом, a dst - 16-бит, то операнд, представленный src, расширяется влево знаковым битом. or bx, Offfh or [word ptr si]. Of or ax, [word ptr Fix] or [byte ptr bx], ch ; AX = OfOch OUT Синтаксис Описание out num, acc FL: без изменений Вывод в порт с номером num данного из аккумулятора асе. Замечания Аргумент port может быть только именем регистра DX или постоянным выражением со значением меньше чем 256. В зависимости от того, является ли асе регистром AL или АХ, в порт выводится байт или слово. Аргументы con8, AL соп8, АХ DX, AL DX, АХ Пример mov dx, 32 mov ах, offfh out dx, ax out 32, al out 20, ах out 40, al out dx, ax ; вывод Offfh в порт 32 OUTSB Синтаксис Описание Замечания Аргументы отсутствие Пример Ids si, [Ref] outsb FL: без изменений Вывод в порт с номером, указанным в ОХ, байта из адреса DS: SI. Если DF = 0, увеличение SI на 1, в противном случае уменьшение SI на 1. Команда outsb может быть предварена знаком повторения. outsb
310 Часть 1Y. Турбо Ассемблер mov dx, 32 outsb ; вывод в порт 32 одного байта OUTSW Синтаксис Описание Замечания Аргументы отсутствие Пример Ids si, [Ref ] mov dx, 32 mov ex, 2 cld rep outsw • outsw FL: без изменений Вывод в порт с номером в DX слова из адреса DS: SI. Если DF « 0, увеличение SI на 2, в противном случае уменьшение SI на 2. Команда outsw может быть предварена знаком повторения. outsw ; вывод в порт 32 двух слов POP Синтаксис Описание Замечания Аргументы reg seg mem Пример mov ax, 22 push ax mov ax, 0 pop ax pop dst FL: без изменений Извлечение из стека процессора в dst одного слова. Увеличение SP на 2. Извлекаются два байта, сначала младший, затем стар- ший. Аргументом команды не может быть регистр CS. pop ах pop (Is pop [word ptr Fix] ; AX = 22 POPA Синтаксис Описание Замечания popa FL: без изменений Извлечение из стека 8 слов и размещение их в регистрах процессора. Увеличение SP на 16. Извлеченные из стека слова размещаются последовательно в следующих регистрах DI, SI. ВР, *, ВХ, DX, СХ, АХ (знаком • обозначено слово, которое, вместо того чтобы быть помещенным в SP, игнорируется).
15. Ассемблер 311 Аргументы отсутствие Пример pusha popa sub sp, 2 popa ; регистр SP без изменений POPF Синтаксис Описание popf FL: все Извлечение из стека процессора одного слова и размещение его в регистре FL. Увеличение SP на 2. Замечания Размещение флажков в регистре FL следующее: Illi l°ldliltlslzl 1а1 1₽1 Iе 1 Аргументы отсутствие Пример хот ах, ах push ах, popf 1 , ...... 1 popf ; обнуление регистра флажков PUSH Синтаксис Описание push dst FL: без изменений Пересылка в стек процессора данного, представленного dst. Уменьшение SP на 2. Замечания Пересылаются два байта. Сначала старший байт, затем младший. Выполнение команды push sp вызывает пересылку нового значения в регистр SP. Аргументы reg seg (также CS) mem Пример push es push ds pop es pop ds push bx push es push [word ptr Fix + si] ; обмен значений ES и DS. PUSH 286 Синтаксис Описание push dst FL: без изменений Пересылка в стек процессора данного, Замечания представленного dst. Уменьшение SP на 2. Пересылаются два байта. Сначала старший байт, затем младший. Выполнение команды push sp вызывает пересылку старого значения регистра SP.
312 Часть IY. Турбо Ассемблер Аргументы con reg seg (также CS) mem Пример push -2 pop ax push 30 push bx push es push [word ptr Fix + s1] ; AX - -2 PUSHA 286 Синтаксис Описание s pusha FL: без изменений Пересылка в стек процессора содержимого следующих регистров: АХ, СХ, DX, ВХ, SP, BD, SI, DI. Уменьшение SP на 16. Замечания Регистры пересылаются в указанной очередности. Пересылка касается старого значения SP. Аргументы отсутствие pusha Пример . mov ах, 2 pusha mov ax, 7 popa ; АХ=2 PUSHF Синтаксис Описание pushf FL: без изменений Пересылка в стек процессора содержимого регистра FL. Замечания Размещение флажков в регистре FL следующее: Illi |c|d|i|t|s|z| |.| |р| |с I Аргументы отсутствие Пример stc pushf pop ах and ах, lh 1 1 pushf ; АХ = 1 RCL Синтаксис Описание rcl dst, ent FL: с о Циклический сдвиг содержимого dst через перенос CF на ent позиций влево. Замечания Аргумент ent должен быть постоянным выражением
15. Ассемблер 313 Аргументы reg, 1 mem, 1 reg, CL mem, CL Пример mov ah, -128 clc rd ah, 1 co значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига старший бит dst заносится в CF, a бит из CF занимает младшую позицию dst. Если выполняется сдвиг на одну позицию, и после этого старший бит результата отличается от бита CF, то выполняется операция OF 1. В противном случае выполняется операция OF := 0. rcl ах, 1 rcl [byte ptr Fix]; 1 rcl bh, cl rcl [word ptr Fix + bx], cl ; AH = 0, CF = 1, OF = 1 RCR Синтаксис rcr dst, ent FL: с о Описание Циклический сдвиг содержимого dst через перенос CF на ent позиций вправо. Замечания Аргумент ent должен быть постоянным выражением со значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига бит CF заносится в старший разряд dst, а младший бит dst заносится в CF. Если сдвиг единичный и после его выполнения два старших бита результата не идентичны, то выполняется операция 0F := 1. В противном случае выполняется операция 0F := 0. Аргументы reg 1 rsr al, 1 mem 1 rcr [word ptr Fix], 1 reg, CL rcr bx, cl mem, CL rcr [byte ptr Fix + bx], cl Пример mov ax,0 stc rcr ax/i ; A = -32768, CF = 0, OF = 1 REP Синтаксис rep FL: без изменений
314 Часть IY. Турбо Ассемблер Описание Замечания Многократное выполнение команды, следущей за гер. Кратность повторения определяется содержимым регистра СХ. После каждого выполнения команды, следующей после гер, выполняется операция СХ := СХ - 1. Аргументы отсутствие Пример Ids si, [dword ptr SrcRef] les di, [dword ptr DstRef] old Если в момент выполнения команды гер регистр СХ имеет значение 0, то повторение имеет краткость 0. rep movsb mov ex, 20 rep movsb пересылка 20 байт REPcont/ Синтаксис Описание Замечания repcond FL: без изменений Многократное выполнение команды, следующей после repcond. Кратность повторения определяется содержимым регистра СХ. Прекращение выполнения при СХ = 0, либо когда перестанет действовать условие cond. После каждого выполнения команды, следующей после гер, выполняется операция СХ := СХ-1. Если в момент выражения команды гер регистр СХ имеет значение 0, то повторение имеет кратность 0. Условие cond определяется следующим способом. Команда гере repz герое repnz Условие повторять операцию, пока равно (ZF=1) повторять операцию, пока нуль (ZF=1) повторять операцию, пока не равно (ZF=O) повторять операцию, пока не нуль (ZF=O) Аргументы отсутствие repne scasb Пример les di, [dword ptr TrgRef] cld mov ex, 200 xov al, al repne scasb jne Fail mov al, [es:di-2] ; AL - последний символ цепочки RET Синтаксис ret ent FL: без изменений
/5. Ассемблер 315 Описание Возврат из процедуры - Замечания Возврат из близкой процедуры вызывает извлечение одного слова из стека процессора и помещение его в регистр IP. Возврат из далекой процедуры вызывает извлечение дополнительного слова из стека процессора и помещение его в регистр CS. Если имеется аргумент ent, то дополнительно выполняется операция SP := SP + ent (ent должно быть постоянным 16-бит выражением). Аргументы отсутствие ret con8 ret 2 Пример nov, ах, OFFSET Lab push ах ret Lab Lab: RETF Синтаксис Описание Замечания Аргументы отсутствие соп8 Пример Fun PROC NEAR retf 2 Fun ENDP Переход на lab. retf ent FL: без изменений Возврат из далекой процедуры Возврат из далекой процедуры вызывает извлечение одного слова из стека процессора и помещение его в регистр IP, а затем извлече- ние второго слова и помещение его в регистр CS. Если имеется аргумент ent, то дополнительно выполняется операция SP := SP + ent (ent должно быть постоянным 16-бит выражением). retf retf5 ; ret 2 было бы принято за retn2 RETN Синтаксис Описание Замечания retn ent FL: без изменений Возврат из близкой процедуры. Возврат из близкой процедуры вызывает извлечение одного слова из стека процессора и помещение его в регистр IP.
316 Часть IY. Турбо Ассемблер Если имеется аргумент ent, то дополнительно выполняется операция SP :« SP + ent (ent должно быть постоянным 16-бит выражением). Аргументы отсутствие con8 Пример Fun RROC FAR retn retn5 retn 2 ; ret 2 было бы принято за retf 2. ROL Синтаксис Описание rol dst, ent FL: с о Циклический сдвиг содержимого dst на ent позиций влево. Замечания Аргумент ent должен быть постоянным выраже- нием со значением 1 или именем регистра CL, • содержимое которого определяет число сдвигов. Во время каждого сдвига старший бит dst зано сится в CF и в младший разряд dst. Если сдвиг единичный и после его выполнения старший бит результата отличается от бита CF, то выполняется операция OF := 1. В противном случае выполняется операция OF := 0. Аргументы reg,l mem,l reg, CL mem, CL Пример mov ax, -32767 rol ax,l rol ах, 1 rol [byte ptr Fix], 1 rol bh, cl ro] [word ptr Fix + bx], cl ; AX-3, CF=1, 0F=l ROR Синтаксис Описание ror dst, ent FL: с о Циклический сдвиг содержимого dst на ent позиций вправо. Замечания Аргумент ent должен быть постоянным выраже- нием со значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига младший бит dst заносится в CF и в старший разряд dst.
15. Ассемблер 317 Если сдвиг единичный и после его выполнения старший бит результата отличается от CF, то выполняется операция OF 1. В противном случае выполняется операция OF 0. Аргументы reg.l mem.l reg, CL mem, CL Пример ror ах, 15 ror [byte ptr Fix], 1 ror bh, cl ror [word ptr Fix + bx], cl raov bh, -127 ror bhf 1 ; BH = -64, CF = 1, OF = 0 SAHF Синтаксис Описание sahf FL: а с р s z Размещение пересылаемых битов в регистре АН следующее: 1 81zl а| 1р| |с | Аргументы отсутствие Пример 1 1 sahf mov ah, 4 sahf jp Lab ; переход будет выполнен SAL Синтаксис Описание sal dst, ent FL: copsz, a=? Арифметический сдвиг содержимого dst на ent позиций влево. Замечания Аргумент ent должен быть постоянным выраже- нием со значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига старший бит dst зано- сится в CF, а в младший разряд записывается 0. Если сдвиг единичный и после его выполнения старший бит результата отличается от CF, то выполняется операция OF := 1. В противном случае выполняется операция 0F := 0. Аргументы reg.l mem.l sal ах,1 sal [byte ptr Fix], 1
318 Часть IY. Турбо Ассемблер reg, CL mem, CL Пример кач [byte ptr Fix],10 sal [byte ptr Fix],2 mov Ы, [byte ptr Fix] sal bh, cl sal [word ptr Fix + bx], cl ; BL - 40, CF - 0, DF » 0 SAR Синтаксис Описание sar dst ent FL: copsz, a-? Арифметический сдвиг содержимого dst на ent позиции вправо. Замечания Аргумент ent должен быть постоянным выражением со значением 1 или именем w регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига младший бит dst заносится в CF, а в старшем разряде дублируется знак операнда dst. Если сдвиг выполняется одни раз, то выполняется операция 0F :- 0 Аргументы reg, 1 mem.l reg, CL mem, CL Пример mov ax, -5 sar ax, 1 sar ah,l sar [word ptr Fix],l sar bx, cl sar [byte ptr Fix + di], cl ; AX - -3, CF=1, DF=O SBB Синтаксис Описание sbb dst, src FL: acopsz Вычитание из содержимого dst содержимого src, а также CF. Замечания Выполнение операции dst: = dst - src - CF Аргументы reg, con mem, com reg, reg reg, mem mem, reg Пример stc mov ax, 0 sbb ax, ax sbb Ы.З sbb [word ptr Fix + di], 300 sbb ax, ax sbb bh, [byte ptr Fix] sbb [byte ptr Fix + si], al ; AX - -1
15. Ассемблер 319 SCASB Синтаксис scasb FL: acopsz Описание Замечания Аргументы отсутствие Пример mov сх, 200 les di, [dword ptr Ref] std xor al, al repe scasb je Fail inc di inc di Сравнение байта, содержащегося в регистре байтом по адресу ES:DI путем образования разности AL-[byte ptr ES:DI], а затем, если DF • 0, увеличение DI на 1, а DF=1, уменьшение 01 на 1. Команда может быть предварена знаком повторения. scasb ; адрес первого нулевого байта AL, С если SCASW Синтаксис Описание Замечания Аргументы отсутствие Пример mov сх, 100 les di, [dword ptr Ref] cld xor ax, ax repne scasw jne Fail sub di, 2 scasw FL: asopsz Сравнение слова, содержащегося со словом по адресу ES: DI путем разности AX-[word ptr ES:DI], а затем, если DF = 0, увеличение DF = 1, уменьшение DI на 2. Команда может быть предварена повторения. scasw ; ES:DI - адрес нулевого слова в регистре АХ, । образования DI на 2, а если знаком SHL Синтаксис shl dst ent FL: copsz, a-?
320 Часть tY. Турбо Ассемблер Описание Замечания Аргументы reg,l тетД w reg, CL mein, CL Пример mov [byte ptr Fix], 10 mov cl.l shl [byte ptr Fix], cl mov bl, Cbyte ptr Fix] Логический сдвиг содержимого dst на ent позиций влево. Аргумент ent должен быть постоянным выраже- нием со значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига старший бит det зано- сится в CF, а в младший разряд записывается 0. Если сдвиг единичный и после его выполнения старший бит результата отличается от CF, то выполняется операция OF 1. В противном случае выполняется операция 0F 0. shl ахД shl[byte ptr Fix],l shl bh, cl shl [word ptr Fix + bx], cl ; BL=20, CF=0, 0F=0 SHR Синтаксис Описание Замечания Аргументы reg.l mem,l reg, CL mem, CL Пример mov ax, -1 mov cl, 1 shr ax, cl shr dst, ent FL: copsz, a=? Логический сдвиг содержимого dst на ent позиций вправо. Аргумент ent должен быть постоянным выраже- нием со значением 1 или именем регистра CL, содержимое которого определяет число сдвигов. Во время каждого сдвига младший бит dst зано- сится в CF, а в старший бит записывается 0. Если сдвиг единичный и после его выполнения два старших бита результата различаются, то выполняется операция OF := 1. В противном случае выполняется операция 0F := 0. shr dx,l shr [word ptr Fix], 1 shr ch, cl shr [word ptr Fix + bx], cl ; AX= 32767, CF=1, 0F=l
15. Ассемблер 321 STC Синтаксис Описание Замечания Аргументы отсутствие Пример stc jc Lab stc FL: c«l Установка флажка переноса с. Выполнение операции CF.-^l. stc ; переход будет выполнен • STD Синтаксис Описание Замечания std FL: d=l Установка флажка направления d. Выполнение операции DE 1. Аргументы отсутствие Пример std std xor si, si lodsw mov ах, si ; SI=-2 STI Синтаксис Описание Замечания sti FL: i“l Установка флажка прерывания i. Выполнение операции IF := 1. Аргументы отсутствие Пример sti sti ; деблокирование прерывания STOSB Синтаксис Описание stosb FL: без изменений Пересылка по адресу ЕС:О1 байта из регистра AL. Если of = 0, увеличение 01 на 1, а если DF • 1, уменьшение DI на 1. Замечания Команда может быть предварена знаком повторения. Аргументы отсутствие stosb
322 Часть ГУ. Турбо Ассемблер Пример les di [dword ptr Ref] mov al, [byte ptr Fix] std stosb stosb ; промежуточное запоминание двух байтов STOSW Синтаксис stosw FL: без изменений Описание Пересылка по адресу ES:DI слова из регистра АХ. Если DF “ 0, увеличение 01 на 2, а если OF = 1, уменьшение 01 на 2. Замечания, Команда может быть предварена знаком повторения. Аргументы отсутствие stosw Пример les di. [dword ptr Ref] mov ax,'"[word ptr Fix] stosw - , stosw ; промежуточное запоминание двух слов SUB Синтаксис sub dst, src FL: а с о р s z Описание Вычитание двух целых данных. Замечания Выполнение операции dst:» dst - src Аргументы reg, con sub Ы,3 men, con sub [byte ptr Fix + di], 30 reg, reg sub ax, ax reg, mem sub bh, [byte ptr Fix] mem, reg sub [byte ptr Fix + si], al Пример mov ax, 257 mov bl, 3 sub al, bl ; AH = -2 TEST Синтаксис Описание Замечания test dst, src FL: c=0 o=0 p s z, a=? Вычисление поразрядного логического произведения аргументов с целью установки флажков регистра FL. Выполнение операции dst AND src без пересылки куда-либо ее результатов.
15. Ассемблер 323 Аргументы reg, con test ax,-l mem, con test [word ptr Fix + 2], Offfh reg, reg test al, al reg, mem test bx, [word ptr Fix -2] mem, reg Пример mov ax, -2 test ax, ax test [word ptr Fix + si], bx js Lab ; переход будет выполнен WAIT Синтаксис wait FL: без изменений Описание Задержка выполнения команд процессора. Замечания Продолжение выполнения команд процессора происходит после получения сигнала о завершении внешней операции, выполняемой, например, сопроцессором. Аргументы отсутствие wait Пример wait ; без комментария XCHG Синтаксис xchg dst src FL: без изменений Описание Обменять содержимое dst и src. Замечания Выполнение операции temp dst dst src src temp Аргументы reg, reg xchg ax, bx reg, mem xchg ax, [sp] mem, reg xchg [byte ptr Fix], bh Пример mov ax, -33 xchg ax, bx ; BX--33 XLAT Синтаксис xlat FL: без изменений Описание Проведение преобразования массива байтов в соответствии с задаваемой таблицей. Замечания Выполнение операции AL [byte ptr ВХ + AL]. Аргументы отсутствие xlat
324 Часть iY. Турбо Ассемблер Пример Table DB 1, 3, 5, 7, 9 mov bx, OFFSET Table mov al, 3 xlat ; AL - 7 XOR Синтаксис Описание Замечания » Аргументы reg, con * mem, con reg, reg reg, mem mem, reg Пример mov ax, -2 xov al, al xor dst src FL: c=o o-O p в z, a=7 Размещение в dst результата поразрядного сложения по модулю 2 содержимого dst и src. Сложение по модулю 2 вычисляется в соответствии со следующей таблицей: Bit dst Bit src Bit dst XOR src 0 0 0 0 1 1 1 0 1 1 1 0 xor ax, -I xor [byte ptr Fix + 2] Offh xor ax, ax xor bx, [word ptr Fix -2] xor [word ptr Fix + si] bx ; AL-O 15.5. Включение файлов, условная компиляция Иногда возникает необходимость включения в компилированный модуль фрагмента предварительно подготовленного текста ассемблера. Это может быть, например, когда в несколько модулей многомодульной программы пред- полагается включить комплект директив GLOBAL. Это задание можно легко реализовать, пользуясь директивой INCLUDE. Директива. INCLUDE имеет в общем случае вид INCLUDE пате, где пате является именем текстового файла. Если название пате не содержит расширения, то подразумевается расширение .ASM. Интерпретация директивы INCLUDE вызывает включение в месте ее появления содержимого файлов name. Ничто не мешает, чтобы присоединяемый текст также содержал директиву INCLUDE. Пример. Использование директивы INCLUDE файл INSERT. ASM GLOBAL FIX : BYTE
15. Ассемблер 325 файл DATA. ASM •MODEL SMALL INCLUDE INSERT • DATA FIX DB 13 END файл CODE. ASM •MODEL SHALL INCLUDE INSERT.ASM .CODE Start: mov ax, 0Oata mov ds, ax mov al, [Fix] mov ah, 4ch int Zlh End Start Модуль DATA трактуется таким образом, как если бы имел вид .MODEL SMALL GLOBAL Fix: BYTE • DATA Fix DB 13 END а модуль CODE трактуется как •MODEL SMALL GLOBAL Fix:BYTE • CODE Start: mov ax, 0 Data mov ds, ax mov al, [Fix] mov ah, 4ch int Zlh END Start Включение текста ассемблера с помощью директивы INCLUDE является безусловным. В тех случаях, когда включение содержимого файла или определенного фрагмента основного модуля должно зависеть от определенных условий, можно пользоваться директивой условной компиляции. Директивы условной компиляции выступают в нескольких вариантах, отличающихся главным образом начинающим их ключевым словом. В общем виде каждая из таких директив имеет вид head text ELSEIF expl text! ELSEIF exp2
326 Часть IY. Турбо Ассемблер text2 ELSE IF expn textn ELSE textO ENDIF где каждый text является произвольной совокупностью команд и директив (в том числе директив условной компиляции), каждый ехр - выражением, а head - одной из следующих фраз, начинающих рассматриваемую директиву: IF ехр IFE ехр rl IFDEF sym IFNDEF sym IFB <arg> IFNB <arg> » IFIDN <argL>, <argR> IFDIF <argL>, <argR> IFWHI<argL>,<aegR> IfDin<argL>,<argR> - компилировать при условии, что выражение ехр не равно 0; - компилировать при условии, что выражение ехр равно 0; - компилировать при условии, что символ sym определен; - компилировать при условии, что символ sym не определен; - компилировать при условии, что аргумент arg пустой; - компилировать при условии, что аргумент arg не пустой; - компилировать при условии, что аргумент argL равен аргументу argR-, - компилировать при условии, что аргумент argL не равен аргументу argR-, - компилировать при условии, что без различия больших и маленьких букв аргумент argL равен аргументу argR-, - компилировать при условии, что без различия маленьких и больших букв аргумент argL отличен от аргумента argR. В приведенной общей записи фраза ELSE text трактуется как сокращенная запись фразы ELSEIF 1 text, а каждая из фраз, начинающихся ключевым словом ELSEIF (в том числе преобразованная фраза, начинающаяся ключевым словом ELSE), может быть опущена. В этом смысле верны среди прочих такие директивы, как IF ехр text ENDIF
15. Ассемблер 327 а также IF ехр textl ELSE iext2 ENDIF Если в заголовке директивы условной компиляции имеется символ sym, то принимается, что он определен, если использован в значении метки, а если имеются argL и argR, то принимается, что они являются выражениями. Интерпретирование директивы условной компиляции происходит следую- щим способом: - определяется истинность условия, выраженного head', - если условие выполнено, компилируется фрагмент text и на этом заканчивается интерпретация рассматриваемой директивы условной компиляции; - если условие не выполнено, определяются поочередно значения выраже- ний ехр, содержащихся во фразах, начинающихся ключевыми словами ELSEIF (и преобразованной фразы, начинающейся ключевым словом ELSE), до обна- ружения выражения expj со значением, отличным от 0, после чего компили- руется фрагмент textj и на этом кончается интерпретация директивы условной компиляции; - если поиск выражения expj со значением, отличным от 0, не заканчивается успешно, то завершается интерпретация директивы. Примеры. Директивы условной компиляции а) Простая директива IF а+Ь-2 One DW 13 Two DB О ELSE One DB 13 Two DW 0 END Если выражение а+Ь-г не равно 0, то интерпретация приведенной директивы вызовет компиляцию фрагмента One DW 13 Two DB О а в противном случае компиляцию One DB 13 Two DW О б) Вложенная директива IFDEF Fix
328 Часть IY. Турбо Ассемблер mov ах, [Fix} IFE Ь jmp short Skip Lab DB 13 Skip: ENDIF ELSEIF b+2 mov bx, 0 ENDIF Если символ Fix определен и переменная b равна 0, то компилируется фрагмент mov ах, [Fix] jmp short Skip Lab DB 13 # Skip: Если символ Fix определен, b отлично от 0, то компилируется mov ах, [Fix} Если символ Fix не определен, Ь+2 отлично от 0, то компилируется mov bx, О Если символ Fix не определен, Ь + 2 равно 0, то интерпретация приведенной директивы условной компиляции не будет иметь никаких результатов. 15.6 Макроопределения и макровызовы Значительным облегчением программирования на языке ассемблер является возможность обращения к макроопределениям, т.е. к тем фрагментам базового модуля, которые без изменений или с небольшими изменениями могут быть компилированы. Макроопределения могут быть размещены непосредственно в компилируемом базовом модуле или могут поступать из предварительно подготовленных файлов, включаемых в месте появления директив INCLUDE. Независимо от источника его происхождения макроопределение имеет в общем случае вид name MACRO par, par, ..., par body ENDM где name является именем макроопределения, каждый par - его параметром, a body - телом макроопределения. Каждый из параметров макроопределения является идентификатором. Тело является совокупностью команд, содержащих обращения к параметрам. Возможны макроопределения, в которых список параметров пуст. Интерпретация макроопределения вызывает только регистрацию ее нали- чия. Собственно использование макроопределения наступает только в месте
15. Ассемблер 329 вызова. Вызов макроопределения имеет вид name arg, arg, ..., arg, где пате является именем предварительно описанного макроопределения, а каждый arg - аргументом его вызова. Интерпретация вызова макроопределения осуществляет - связь каждого параметра par с соответствующим ему аргументом arg (первый параметр с первым аргументом, второй со вторым и т.д.); - интерпретацию тела макроопределения после предварительной замены каждого идентификатора параметра соответствующим этому параметру аргументом. Примеры макровызовов а) В области действия макроопределения Swap MACRO Dst, Src push Dst mov Dst Src pop Src ENDM интерпретация макровызова Swap ds. es вызывает компиляцию аргумента push ds nov ds, es pop es б) В области действия макроопределения - LongSHL MACRO clc shl ax,l rcl dx,l ENDM интерпретация макровызова LongSHL вызывает компиляцию clc shl ax,l rcl dx,l
330 Часть IY. Турбо Ассемблер в) В области действия макроопределения Swap MACRO Dst, src IFDIFI <Dst>, <Src> xchg Src, Dst ENDIF stc ENDM интерпретация макровызова Swap bx, CX вызывает компиляцию xchg CX. bx , stc а интерпретация макровызова Swap ax, ax - компиляцию команды stc Специальные макроопределения К категории специальных макроопределений здесь относят макроопределения, начинающиеся ключевыми словами REPT, IRP и irpc. Их специальный характер состоит в том, что каждое из них вызывает дополнительные предположение и интерпретацию макроопределения. Непосредственно после такой интерпретации специальное макроопределение перестает существовать, поэтому нет необходимости назначения ему имени. REPT Макроопределение rept имеет вид REPT count body ENDM где count является постоянным выражением, не содержащим обращений вперед, a body - телом макроопределения. Интерпретация макроопределения REPT вызывает включение в месте его появления с целью компиляции фрагмента, состоящего из count количества тела макроопределения. Пример. Макроопределение REPT Интерпретация макроопределения REPT 3 shr ах,1 ENDM вызывает компиляцию команд
15. Ассемблер 331 shr ax.l shr ax.l shr ax.l IRP Макроопределение IRP имеет вид IRP par, <arg, arg, ..., arg> body ENDM где par является идентификатором параметра макроопределения, каждый - аргументом, a body - телом макроопределения. Аргументы arg могут быть произвольными выражениями. Интерпретация макроопределения IRP вызывает включение в месте его появления с целью компиляции фрагмента, состоящего из такого количества body, сколько имеется аргументов, путем замены параметра par, находящегося в body, очередным аргументом arg. Пример. Макроопределение IRP Интерпретация макроопределения IRP Child «Тот, Bob, Апп> Child DB? ENDM вызывает компиляцию Tom DB? Bob DB? Ann DB? IRPC Макроопределение IRPC имеет вид IRPC par, string body ENDM где par является идентификатором параметра макроопределения, string - произвольной строкой символов, a body - телом макроопределения. Интерпретация макроопределения irpc вызывает включение в месте его появления с целью компиляции фрагмента, состоящего из такого количества появлений команд body, сколько знаков насчитывает string, путем замены параметра par, выступающего в body, очередным символом из string. Пример. Макроопределение IRPC Интерпретация макроопределения
332 Часть IY. Турбо Ассемблер IRPC Digit 1989 DB Digit ENDM вызывает компиляцию фрагмента DB1 DB9 DBS DB9 Слияние (конкатенация) параметров Если во время интерпретации тела макроопределения стречастся знак & (амперсанд) непосредственно перед или после параметра макроопределения, то после замены параметра аргументом этот знак снимается. Это правило касается также появлений рассматриваемого знака перед параметрами, находящимися в пределах знаковых литералов, позволяющих как соединение надписей и аргументов, так и использование литералов с параметрами. Пример. Конкатенация и параметризация В области Действия макроопределения Child MACRO Lab, Siz, Str Lab&ber D&Siz 13 DB '•= &Str& ENDM интерпретация макровызова Child Nun, W, Tom вызывает компиляцию аргумента Number DW 13 DB ’»= Tom оя* Директива LOCAL Для того чтобы во время очередных вызовов одного и того же макроопределения не был скомпилирован фрагмент, содержащий уже определенную метку, что, конечно же, недопустимо, необходимо использовать директиву LOCAL, предусмотренную для определения локальных символов макроопределения. Директива LOCAL имеет вид LOCAL symbol, symbol, ..., symbol, где каждый symbol является локальным символом. Интерпретация директивы LOCAL приводит к тому, что с каждым указанным в ней символом связан уникальный символ вида ?? number
13. Ассемблер 333 (number является числом), который в компилируемом модуле заменяет первичный символ, с которым его связывали. Благодаря этому можно избежать использования одних и тех же меток. Пример. Использование директивы LOCAL В области действия макроопределения Display MACRO Message LOCAL Lab .DATA Lab D8 Message DB 13, 10, $' CODE mov dx, OFFSET Lab mov ah, 9 int Zlh ENDM интерпретация вызовов Display "Hello" Display "world" вызывает компиляцию, например, следующего типа: .DATA ?? 00025 DB "Hello' DB 13, 10. .CODE mov dx, OFFSET ??00025 mov ah, 9 int Zlh • DATA 7700026 DB "world" DB 13, 10, • CODE mov dx, OFFSET 7700026 mov ah,9 int Zlh Специальные -операторы ' Кроме указанного выше оператора для конкатенации параметров (&) специальными операторами являются также О использование которого вызывает буквальное трактование символов, содержащихся в угловых скобках, например в макровызовы Show Z, <; text» знак ; (точка с запятой) не является началом комментария. ! использование которого вызывает трактовку следующего за ним символа как интервала, а не как имени, например в макровызове
334 Часть /К Турбо Ассемблер Show al, b, с имеются два аргумента :<а,Ь> и <с>. % использование которого преобразовывает аргумент в число, представляющее значение аргумента, например в макровызове Show Х2+3, 2+3 аргументы <5> и <2+3>. Директива ЕХПН Директива EXITM имеет ввд ЕХПН. Она может находится только в теле макроопределения. Интерпретация директивы ЕХПН вызывает немедленное завершение макрорасширения, не дожидаясь его конца. Пример. Директива ЕХПН FastHul HACRff' Factor IF Factor EQI cbw EXITH ENDIF Fast=0 Count=l Bit=2 * REPT16 IF Factor EQ Bit Fast=l EXITH ENDIF Count=Count+l Bit=Bit SHL1 ENDM IF Fast xor dx, dx REPT Count shl ax, 1 rcl dx,I ENDH ELSE mov dx. Factor mul dx ENDIF ENDH Макроопределение осуществляет умножение содержимого регистра АХ на постоянную. Если постоянная является степенью числа 2, то умножение выполняется ускоренным способом (с помощью сдвигов). В частности, вызов FastHul 4 вызывает компиляцию фрагмента xor dx, dx
15. Ассемблер 335 shl ax.l rel dx.l shl ax.l rcl dx.l 15.7 Организационные директивы К категории организационных директив относятся те директивы, интерпретация которых не влияет на генерацию кода и данных. Это прежде всего директивы управления листингом и генерации сообщений. Директива XPAGESIZE Директива XPAGESIZE имеет вид XPAGESIZE row’s, cols, где row's и cols являются числами. Интерпретация директивы %PAGESIZE определяет размер страницы листинга компиляции в rows строк (как минимум 10) и cols столбцов (как минимум 59). Если один из аргументов отсутствует, то новое значение придается только второму из них. Пример. Определение размеров страницы XPAGESIZE, 72 Число строк остается без изменений, число столбцов листинга равно 72. Директивы %TITLE и %SUBTTL Указанные директивы имеют соответственно вид %TITLE text и %SUBTTL text где text является производным текстом. Интерпретация директивы % TITLE определяет text как заголовок, который будет помещен в начале каждой страницы листинга компиляции (сразу же после номера страницы), а интерпретация директивы %SUBTTL - подзаголовок каждой страницы. Пример. Заголовок и подзаголовок XTITLE Sort and Merge XSUBTTL Sort Директивы %LIST и %NOLIST Указанные директивы имеют соответственно вид %LIST
336 Часть IY. Турбо Ассемблер и %NDLIST. Интерпретация директивы %LIST создает файл листинга компиляции, определенный в команде вызова компилятора, а интерпретация директивы %NOLIST вызывает прекращение вывода листинга. В начале компиляции по умолчанию предлагается директива % LIST. Пример. Выборочное создание листинга компиляции %NOLIST mov ах, 8DATA «LIST mov ds, ax w Исключение из листинга компиляции информации о первой из приведенных команд. Директивы %INCL и %NOINCL Директивы %INCL и %NOINCL имеют соответственно вид %INCL и %NOINCL. Интерпретация директивы %INCL вызывает включение в листинг компи- ляции текстов, содержащихся в файлах, включенных в директиву INCLUDE, а интерпретация директивы %NOINCL - прекращение такого включения. В начале компиляции по умолчанию предполагается директива %INCL. Пример. Включение файлов Интерпретация директив %NOINCL INCLUDE One XINCL INCLUDE Two вызывает исключение из листинга компиляции содержимого файла One и включение в листинг компиляции содержимого файла Two. Директивы %MACS и %NOMACS Директивы % MACS и %NOMACS имеют соответственно вид % MACS \ и
15. Ассемблер 337 %NOMACS. Интерпретация директивы % MACS вызывает включение в листинг компиляции также и тех директив, содержащихся в макроопределениях, которые не генерируют код. Интерпретация директивы %NOMACS вызывает прекращение дальнейшего включения таких директив в листинг компиляции. В начале компиляции по умолчанию принимается директива % OMACS. Пример. Исключение директив В области действия макроопределения Set MACRO One, Two XNOMACS First = One «MACS Second = Two ENDM интерпретация макровызова Set 10, 20 не включает в листинг компиляции строки First = 10 Директивы %CONDS и %NOCONDS - Директивы %CONDS и %NOCONDS имеют соответственно вид %CONDS и %NOCONDS. Интерпретация директивы %CONDS включает в листинг компиляции также и тех строк директив условной компиляции, которые не подлежали интерпретации, а директива %NOCONDS прекращает включение таких строк. В начале компиляции по умолчанию принимается директива %CONDS. Пример. Исключение неинтерпретируемых строк В области действия макроопределения Move MACRO Reg, Val mov ax, Val IFIDN <Reg>, <DS> mov ds, ax ENDIF ENDM
338 Часть IY. Турбо Ассемблер интерпретация %NOCONDS Mov ах, 13 не включает в листинг компиляции строку mov ds, ах. Директивы %CTLS и %NOCTLS Директивы %CTLS и %NOCTLS имеют соответственно вид %CTLS %NOCTLS. Интерпретация директивы %CTLS вызывает включение в листинг компи- ляции директив управления листингом (таких, как, например, %LIST %CONDS, и т. д.), а директива %NOCTLS вызывает прекращение такого включения. В начале компиляции по умолчанию принимается директива %NOCTLS. Пример Интерпретация %CTLS XNOLIST mov ах, 13 XNOCTLS XCONDS исключает из листинга компиляции информации о директиве %CONDS. Директивы %PUSHCTL и %Р0РСП Директивы %PUSHCTL и %POPCTL имеют соответственно вид %PUSHCTL и %POPCTL. Интерпретация директивы %PUSHCTL вызывает запоминание в локальном стеке компилятора информации о результатах использования директив, управляющих листингом компиляции (таких, например, как %LIST, %MACS, %CONDS), а директива %POPCTL сохраняет в стеке результаты использования таких директив.
15. Ассемблер 339 Пример. Использование локального стека компилятора Если файл, включенный в базовый модуль с помошью директивы INCLUDE, имеет вид %PUSHCTL body %POPCTL где body не содержит директив %PUSHCTL, %POPCTL и INCLUDE, то включение его не вызывает замены способа генерирования листинга компиляции. 15.8. Версии Masm и Ideal До сих пор описание Турбо Ассемблера ограничивалось теми его конструкциями, которые в равной степени касаются компилятора HASH (Microsoft) и TASM (Borland). Однако необходимо обратить внимание, что те программисты, которые собираются пользоваться только языком ассемблер TASM, могут лишиться некоторых новых интересных возможностей, проявляющихся при использовании директивы IDEAL. Директивы IDEAL и HASH Директивы IDEAL и HASH имеют соответственно вид IDEAL и HASH Интерпретация директивы IDEAL вызывает компиляцию в версии Ideal, а интерпретация директивы HASH - компиляцию в версии Masm. В начале компиляции подразумевается версия Masm. Использование Ideal не требует знания новых правил программирования на ассемблере, поскольку отличия ideal от Masm минимальны. Достоинствами версии ideal являются повышение уровня контроля за достоверностью программ, исключение определенных усложненных требований версии Masm и использование более естественного состава некоторых директив. Точечные директивы В версии ideal нельзя использовать директивы, начинающиеся знаком . (точка), называемые здесь точечными. Каждая из них имеет свой эквивалент, начинающийся с буквы, а именно Masm. Ideal •MODEL MODEL • CODE CODESEG • DATA DATASEG •DATA? UDATASEG •FARDATA' FARDATA FARDATA? UFARDATA
340 Часть IY. Турбо Ассемблер •STACK STACK .8086 Р8086 (и аналогично другие директивы этой группы) Пример. Программа в версии Ideal IDEAL MODEL SMALL STACK lOOh DATASEG Greet DB "Hello", CODESEG Start: mov ax, Wata mov ds, ax mov dx, OFFSET Greet mov ah, 9 int 21h mov ax, 4c00h Int Zlh END Start Директива SEGMENT В версии ideal директива SEGMENT имеет вид SEGMENT name align combine 'class' body ENDS Пример SEGMENT Data WORD PUBLIC 'DATA' Fix DB 13 Arr DW 20 DUP(T) END Директива PROC В версии ideal директива PROC имеет вид PROC name dlst arg, arg, ..., arg RETURNS res, res, ..., res body ENDP Пример PROC Sum NEAR One, Two push bp mov bp, sp mov ax. One add ax. Two pop bp
13. Ассемблер 341 ret END Директива MACRO В версии Ideal директива MACRO имеет вид MACRO name par, par, ..., par body ENDM Пример MACRO Sum One, Two mov ax, [word ptr One] add ax, [word ptr Two] ENDM Директива RECORD В версии Ideal директива RECORD имеет вид RECORD name field, field, ..., field Пример RECORD Wordl6 Left 9M1d2:2=3, R1ght4:4 Директива STRUC В версии Ideal директива STRUC имеет вид STRUC name field field field ENDS Допускается, чтобы идентификаторы полей в отдельно описанных структурах были идентичны. Пример В области действия описания STRUC Pair First DB? Last DB 0 ENDS STRUC Quartet
342 Часть IY. Турбо Ассемблер Before DW О Mid Pair After DW 0 Last DB 2 ENDS One Pair <2, 2> Two Quartet <> Struct Quartet <> верны следующие команды: mov al, [One, Last] mov al, [Two.Last] mov ax, [wor<j[ptr Struct.Mid.First] mov ah, [(Pair ptr bxJ.Last]; оператор Pair ptr mov al, [(Quartet ptr di).Last]; оператор Quartet ptr Директива UNION В версии Ideal директива UNION имеет вид UNION name field field field ENDS Допускается, чтобы идентификаторы полей в отдельно описанных объединениях были идентичны. Пример В области действия описания Union Quartet First DW О STRUCT Pair Middle DB 0.0 ENDS Last DB 5 ENDS Uni Quartet о верны следующие команды: mov al, [Uni.Last] mov ah, [Un1.M1ddle+l] Директива EQU . В версии ideal директива EQU имеет вид
15. Ассемблер 343 name EQU exp. Значение выражения exp определяется только в месте обращения к пате. Пример В области дейсгвия следующих описаний: Count»? Save EQU Count Count=3 команда mov ax. Count в версии ideal трактуется как команда mov ax, 3, а в версии Masm - как команда mov ах,2. Сегменты и группы В версии ideal смещение каждой переменной, входящей в состав группы, отсчитывается относительно начала группы, В версии Masm (в результате ошибки реализации, которую не устранила фирма “Майкрософт") выражение OFFSET symbol, где symbol является именем переменной, представляет в команде mov ах, OFFSET Fix смещение в сегменте, а в описании переменной, например Ref DW OFFSET Fix, - смещение в группе. Как в версии Ideal, так и в Masm в целях аннулирования принятых пред- ’ положений можно пользоваться префиксами для имен группы или сегмента. Пример В области действия описания переменной Fix, Fix DW 13, находящейся в группе DGROUP, команда
344 Часть IY. Турбо Ассемблер mov ах, OFFSET Fix, записанная в версии Ideal, равнозначна команде mov ах, OFFSET DGROUP:Fix. записанной в версии Masm. Выражения Способ интерпретации выражений в версии Ideal несколько отличается от способа их, интерпретации в версии Masm, поскольку отличаются приоритеты операторов, а именно (в очередности от высшего к низшему) () [] LENGTH MASK OFFSET SEG SIZE WIDTH High Low' + - (одноаргументное) * I MOD SHL SHR + - (двухаргументное) EQ GE GT LE LT NE NOT AND . OR XOR type PTR SHORT В версии Ideal способ интерпретации оператора SIZE иной, чем в Masm. Принимается, что SIZE var, где var является именем сложной или простой переменной, выступающей после таких директив, как DB и DW, представляет размер этой переменной, а не произведение TYPE var * LENGTH var, как это имеет место в версии Masm. Пример В области действия описания herName DB 'Isabel*, 6 herAge DB 6, 'Isabel' в версии Ideal: SIZE herName - B, SIZE herAge - 1; в то время как в версии Masm: SIZE herName = 1, SIZE herAge = 1. Команды В версии ideal запрещается обращаться к переменной без заключения имени переменной в квадратные скобки. Поэтому, например, допустимая в версии Masm команда
15, Ассемблер 345 mov ах. Fix; должна быть записана, как mov ах, [Fix]. Кроме того, запрещается использование таких аргументов, в которых оператор [] (пара квадратных скобок) трактуется как оператор + (плюс), а также появление префикса и оператора преобразования типа (например, BYTE PTR) перед квадратными скобками. Пример Приведенные ниже сравнения определяют допустимые виды команд в версии Masm и их эквиваленты в версии Ideal. Masm Ideal кем ах, [bx][si] mov ax, [bx+sl] mov ах, es:[bx] mov ax, [es:bx] mov ах, 6[bx] mov ax, [bx+6} mov ах, es:[bp+S][si] mov ax, [es:bp + si+8] mov ах, word ptr[bx] mov ax, [word ptr bx}
ЧАСТЬ V. ТУРБО ОТЛАДЧИК 16. Отладчик Существенным расширением возможностей, предлагаемых системой Турбо, является программа отладчика (англ, debugger). Отладчик Турбо принадлежит к категории символьных отладчиков высокого уровня. Он позволяет, кроме прочего: - следить зй итоговой программой; - выполнять программу в шаговом режиме (команду за командой, процедуру за процедурой или до останова); - расставлять ловушки, в том числе ловушки местные и глобальные; - выявлять значения переменных; - обозначать значения выражений, содержащих обращения к переменным базовой программы; - следить з5 приписыванием данных и вызовами процедур; - выявлять развитие инструкций ассемблера, содержимое ячеек оперативной памяти и регистров процессора и сопроцессора; - знакомиться с содержанием текстовых файлов; - регистрировать и воспроизводить последовательности нажатий клавиш (макроопределения и макровызовы); - преобразовать конфигурацию расположения окон отладчика; - использовать расширенную память, а также средства для вспомогательной отладки (дистанционное и техническое оборудование). Вызов отладчика Вызов отладчика имеет вид TD opt name args, где opt являются опциями, пате - именем запускаемой программы, a args - ее аргументами. Опции могут быть указаны явно в директиве TD или приняты по умолчанию из файла конфигурации TDCONFIG. Этот файл можно создать с помощью программы установки TDINST или непосредственно при отладке (Options/Save). Файл конфигурации разыскивается последовательно: 1) в текущем каталоге; 2) в каталоге, определенном с помощью TDINST; 3) в каталоге, содержащем отладчик. Если файл конфигурации не будет найден, то будут использованы предложения по умолчанию. Пример. Использование программы установки Поскольку отладчик не имеет встроенного редактора, его можно указать специально. С этой целью необходимо вызвать программу TDINST, в кото-
16. Отладчик 347 FileView Bun Breakpoints Data Hindew Options ttinclude <stdio.h) > tnainf) 1 printfCHello from Debugger"); return 0; Turbo Debugger Version 1.0 Copyright (c) 1988 Borland International |Hatches- FZ-Bkpt F3-Close М-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run FlO-Menu Рис. 42. Визитная карточка отладчика. рой послё выбора опции Options и Editor можно указать директиву системы DOS, активизирующую выбранную редакторскую программу, например C:\UTIL\SEE. Если после выполнения такой установки (ведущей к запоминанию в отладчике имени указанного редактора) требуется осуществление редактирования определенного файла name, то это реализуется с помощью директивы C:\UTIL\SEE пате и тем самым вызывается редактор. Подготовка программы Способ подготовки программы, которая должна быть отлажена, зависит от того, используются ли интегрированные средства либо внешние директивы. В первом случае необходимо выполнить компиляции со следующими опциями: Турбо Си Debug/Source debugging On, во втором - выполнить компиляции с соответственно выбранной опцией.
Часть V. Турбо отладчик 348 Турбо Паскаль Debug/Standalone debugging On Options/Compiler/Debug Information On Options/Compiler/Local Symbols On (последняя из указанных опций необходима только тогда, когда подразумева- ется символьное обращение к локальным переменным функции и процедур); Турбо Си: -v Турбо Паскаль: /v Турбо Ассемблер: -zi. Если компиляции подлежит программа на языке ассемблер, то необходимо скомпоновать ее, используя компоновщик TLINK, вызванный с опцией /v. w Поскольку условием работы отладчика является доступ к базовым моду- лям, необходимо обеспечить, чтобы эти модули находились в области дейст- вия отладчика. Имея это в виду, необходимо помнить, что файлы, содержа- щие базовые модули, разыскиваются последовательно: 1) в каталоге, в кото- File View Run Breakpoints Data Hindou Options ----------------------------—-ds: 0004 = 00f------------- ai ix*si+(K]?bl cs:0106 cs:0107 cs:0109 cs:oioc cs:010F csIOHO cslOHZ cs:0114 cs:0116 csIOllA cslOUB 5B 0000 007A04 086C00 60 0O0A 0000 0004 00066100 06 0000 or pop add add or tsil.ch bx Ibxtsil.al [bptsi+04],hh tsil,ch ax bx ex dx si di 0000 0000 0000 0000 0000 0000 j—1 c=0 z=0 s-0 o=0 p=0 a=0 Тогbo Debugger d-0 Version 1.0 Copyright (c) 1988 Borland International ds:0000 CD 20 00 --------------------- ds: 0008 ID F0 2F 01 B4 ОС ОС 00 ds:0010 9F 4C ZF 02 A8 48 BB ZE fL/OiHa- ds: 0016 01 01 01 00 0Z 05 06 FF @@@ OH -----------'D ss:0064 6E69 ss:0062 616D ss:0080)0D00 Alt: FZ-Blqpt at R-Mod FMnim FS^User FG-iindcI’П-Instr F8-Rtn F9-To Fliklocal Рис. 43. Визитная карточка отладчика на фоне окна ЦПУ.
16- Отладчик 349 ром находился их компилятор; 2) в каталоге, определенном с помощью оп- ции Optlons/Path; 3) в текущем каталоге; 4) в каталоге, из которого запуска- ется программа. Если базовый набор не будет найден, то отладка может происходить только на уровне команд ассемблера. В таком случае непосредственно после вызова отладчика на экране появляется окошко CPU, содержащее команды ассемблера, а не, как обычно, окошко Module, содержащее базовые инструкции. Остается добавить, что отладчик может быть вызван также и без указания имени файла. Тогда его визитная карточка высвечивается на фоне окна CPU. Такая визитная карточка появляется также тогда, когда запускаемая исполнительная программа не содержит информации, позволяющей осуществить отладку (поскольку, например, компоновка выполнена без опции /v). Внимание: Поскольку отладчик является универсальной программой, приспособленной для отладки программ, написанных на разных языках программирования, то в приведенном ниже описании будет использована следующая терминология: - базовые модули языка Си и ассемблера, а также библиотечные модули языка Паскаль будут называться модулями', - функции языка Си, а также функции и процедуры языка Паскаль будут называться процедурами. 16.1. Система меню Система меню организована иерархически. Непосредственно после вызова отладчика, а также после нажатия в произвольный момент клавиши F10 активизируется главное меню. Оно высвечивается на первой строке экрана, всегда видно и служит для выбора промежуточных меню. В данный момент действует, самое большое, одно меню: главное или меню, выделенное с помощью двойной обводки. Если после активизации определенного меню снова нажимается клавиша F10, то это вызывает активизацию последнего активного окна. Каждое меню состоит из полей, содержащих опции меню. Одно из этих полей, называемое текущим, выделяется с помощью инверсной подсветки. Для выбора опции, определенной таким полем, достаточно нажать клавишу Enter. Выбор другой опции требует изменения поля. В этих целях можно воспользоваться клавишами со стрелками или клавишами, обозначенными Ноте и End. Нажатие клавиши со стрелкой вызывает сдвиг на одну пози- цию, а нажатие клавиш Ноше и End - перемещение выделенного поля соответ- ственно на первую или последнюю позиции меню. Другой способ выбора опции, кроме как с помощью клавиш со стрелками и клавиши Enter, это нажатие произвольной клавиши, обозначенной светлой буквой поля меню. Выбор меню, непосредственно подчиненных главному меню, может быть значительно ускорен с помощью клавиши Alt. Нажатие ее в произвольный момент вместе с первой буквой поля главного меню сразу же вызывает высвечивание подчиненного меню. Это означает, в частности, что в целях высвечивания подчиненного' меню, которое зависит от поля File главного Меню, достаточно нажать клавишу Alt-F (вместо нажатия сначала клавиши F10, а затем клавиши F).
350 Часть V. Турбо отладчик Рис. 44. Главное меню. File View Run |7тдгм; IN File: lints Data Options ttinclude (stdio.h) ► mainO printf("Hello Ггов Debugger"); return 0; Mindow pick Alt-0 Г Hext pane Tab Move/Resiie Serik Close F3 Undo close A11-F6 Dump pane to log Restore standard Screen repaint —1 itches- -2 Fl-Help Esc-Abort Рис. 45. Второстепенное (подчиненное) меню.
16. Отладчик 351 2E View thin Breakpoints Data Hindow Options > Load... Change dir... Get info H*IM.C3] . h> 0$ shrlift Quit A itches- The OS Shell command starts a POS command processor so that you can type commands exactly as if you were at the normal DOS prompt. When you have finished entering DOS commands, type "exit" to return to Turbo Debugger. The memory occupied by your program may have to be swapped to disk to make room to run the DOS command processor. Expect a small delay at the start and end of this command if your program is swapped to and from the disk. Fl-Index t-J-Select PgUp-Previous page Ctrl PgUp-First page Esc-Exit Рис. 46. Справочное окно. as View Run_________Breakpoints Data Hindow Options > Load... Change dir.. Get info OS shell : Quit A I HaiM.C3, . h> HELP INDEX Help on Help Assembler Assembler constants Assembler expressions Assembler operators Assembler strings Assembler variables Auto variables Byte lists C constants C Debugging tips Matches- PgUp/PgDn Fl-Index f-J-Select PgUp-Previous page Ctrl PgUp-First page Esc-Exit Рис. 47. Алфавитный указатель справочного окна.
352 Часть V. Турбо отладчик В случае ошибочного выбора подменю можно попасть на высший уровень меню, нажав клавишу Esc. Нажатие этой клавиши в главном меню вызывает восстановление последнего активного информационного окна. Так же как это происходит в интегрированных средах таких систем, как Турбо Паскаль и Турбо Си, если после выбора определенного поля главного меню (например, с помощью клавиши Enter или клавиши, обозначенной стрелкой, направленной вниз) будут использованы клавиши со стрелками, то это вызовет активизацию соседних подменю главного меню. Это очень облегчает локализацию важнейших функций системы меню. Другого вида облегчением является постоянное высвечивание в нижней строчке экрана информации о важнейших функциях клавиш, а также возможность пользоваться справочными окнами. Справочное окно высвечивается после нажатия клавиши F1. Указываемые в них пояснения подбираются соответственно контексту, в котором произошло нажатие указанной клавиши. Правила перемещения между справочными «окнами аналогичны правилам перемещения между окнами меню. В частности, нажатие клавиш PgDn и PgUp позволяет ознакомиться с текстом, не помещающимся в окне, нажатие клавиши Alt-Fl позволяет ознакомится с предыдущим справочным окном, а новое нажатие клавиши F1 позволяет познакомиться с алфавитным указателем справочных окон. Для возврата в место, в котором первоначально была нажата клавиша F1, необходимо нажать клавишу Esc. 16.2. Классификация окон Окна отладчика можно разбить на следующие категории: - справочные окна; - окна меню - глобальные; - окна меню - локальные; - окна отладчика - информационные; • - окна отладчика - инспекционные. Справочными окнами являются окна, высвечиваемые после нажатия клавиши F1. Глобальными окнами меню являются окна, выбираемые непосредственно из главного меню (например, после нажатия клавиш F10 и V или Alt-V). Они характеризуются тем, что их содержание неизменно. Служат для выбора очередных подменю и опций. Локальными окнами меню являются окна, высвечиваемые после нажатия клавиши All-10 или Ctrl-FlO (или ускоренным способом нажатием клавиш Ctrl-j, где j - клавиша, обозначенная выделенной буквой опции меню). Локальные окна могут быть вызваны в произвольный момент. Информациоными окнами отладчика являются окна, выбранные из гло- бального меню View. Всего их 12. В момент активизации отладчика высве- чиваются два из них: модульное и обзора. Инспекционными окнами отлад- чика являются окна, высвечиваемыме после нажатия клавиши Ctrl-1. Они позволяют знакомиться со значениями переменных и выражений. Отдельную категорию составляют окна диалога. Они появляются тогда, когда необходимо дать ответы на поставленные вопросы или произвести выбор ответа. Типичное диалоговое окно содержит показ предварительно данных ответов. Если собираются дать ответ, идентичный одному из уже данных ответов, то достаточно осуществить его выбор с помощью клавиш со стрелками, а затем нажать клавишу Enter. В момент последующего вызова
16. Отладчик 353 Fl-Help Esc-Ahort Рис. 48. Глобальнее окно File. File ЖЕ Run Breakpoints feta Kindow Options XSES Breakpoints Stack tog Hatches Variables File... CPU Dump Registers Humeric processor User screen Alt-R Another Fl-Help Es Abort Рис. 49. Глобальное окно View.
354 Часть V. Турбо отладчик File Uiew 3® Bheakpoints Eat a Hindow Options Lffl Run pg Go to cursor F4 Trace into Г7 Step over Г8 Execute to... Ail-F9 Until return A11-F8 Animate A11-F4 Instruction trace All-t’7 Pi-Help Esc-^borl g '5 — Рис. 50. Глобальное окно Run. File View Fi-ftelp Esc-Abari Рис. 51. Глобальное окно Break.
16. Отладчик 355 File- Wien Run Breakpoints Options Inspect... Evaluate/modify... Ctrl-F4 Hatch... Ctrl-F7 fi-fieipise-Ahort' Рис. 52. Глобальнее окно Data. File Wien Kun Breakpoints Data Options •. fftndow pick Alt-0 Hext pane Tab •" tt»ve/Resize Serik Elose F3 ’• Undo close Alt-F8 ИИШИ Restore standard Screen repaint Fl—Help Ese-Aiwrt Рис. 53. Глобальное окно Window.
356 Часть V. Турбо отладчик змшэ ЦШЙ Language Source Macros ______ *??£ a’tvironmeiiT Path for source.,. Arguments... Save options Restore options... Рис. 54. Глобальное окно Options. File View Run Break) pints Data Window Options •dttle: НА1И File: HAIN.C •? ttinclude <stdio.h> ainO < ► printfC'Hello from Debugger"); Fl-Help Ese-Abort Рис. 55. Локальное окно I.
16. Отладчик 357 File View Run Breakpoints Data Window Options [Module: MAIN File: MAIN.C 5--------------------------------- Sincludetstdio. h> main( > > pri ret cs:0200 cs:0201 cs:0204 MAIH86: i cs:0205 cs:0207 MAIN87: ) cs.'OZOS cs:020A __IOERROR cs:020B Fl-Help Esc-Abort ds:0009 ds:0008 ds:0010 ds:0018 rinlf("Hello from nfQd ГПГМ1ПММ1 h ' ax Origin Follow Caller Previous Search View source Mixed 7es I jprinlf ex ax,&x MAINS? (0209) bp Kew cs:ip fissecdsl^... I/O h bp 72 62 Turb 43 6F o-C - Co 74 20 pyright 38 38 (c) 1988 6F ZB 43 20 ZB 20 70 79 72 69 67 68 28 63 29 20 31 39 ssrFFBE Ш2 ss:mc oooi sslFFBA OHB ss:FFB8^FFE4 ax 0000 bx 04BE ex 0015 dx 3327 si 0033 di 04B2 bp FFB8 sp FFBS ds 6C08 es 6C08 ss 6C06 cs 6ДВ2 ip 01FB —3a c=0 z=0 5=1 0=0 P=1 a=l i=l d=0 Рис. 56. Локальное окно II. File View Run Breakpoints Data №ndow Options [Module: MAIN File: MAIN.C 5------------------------------------ ^include (stdio.h) main( ► > pri ret |с=о|; icremeut itches— ds 6C08 es 6C08 ss 6C08 cs 602 ip 01FB 'Decrement Zero Change Registers 32-bit No [CPU 80286------------- NAIN05: printf("Hello cs:01FB)889401 cs:0200 50 csiOZOl E8BB08 -om Debugger"); ax 0000 mov 1 ax,0194 bx 04BE push ax ex 0015 call _printf dx 3327 POP ex si 0033 di 04B2 xor ax, ax bp FFD8 jmp MAIN87 (0209) sp FFD8 ds 6C08 pop bp es 6C08 ret 55 6C08 C5 6AB2 push bp ip 01FB 0 00 54 75 72 62 Turb 3 20 20 20 43 6F o-C - Co 2 69 67 68 74 20 pyrighi 9 20 31 39 38 38 (c> 1988 Fl-Help Ese-Abort <1=0 —3i c=0 z=0 s=1 o=0 )F1 a=l ss'.FFBE FFE2 sslFFBC 0001 ss:ffba оно ss:FFB8kFFE4 Рис. 57. Локальное окно III.
358 Часть V. Турбо отладчик File toother Module.. Hine main Breakpoints Stack Log Hatches Variables jir re Run Breakpoints Data Rindow Options Batches Pile... CfU Dump Registers Numeric processor User screen Alt-15 F -Help lt-t(pve f-’-Selest Letters-Hatch Ese-Abort Рис. 58. Диалоговое окно I. диалогового окна, отвечающего данному контексту, выбранный таким образом ответ окажется наверху подобранных ответов. Переллещение окон Перед обсуждением функций информационных и инспекционных окон отладчика необходимо указать их существенную особенность. Каждое окно отладчика может свободно перемещаться по экрану, а его размер - произвольно модифицироваться. В целях выполнения таких перемещений и изменений необходимо сначала нажать клавишу ScrollLock, а затем воспользоваться клавишами со стрелками. Как можно убедиться, нажатие клавиш со стрелкой вызывает перемещение края окна, а нажатие этой клавиши одновременно с клавишей Shift - изменение размера окна. Это изменение происходит таким образом, что левый верхний угол окна остается неподвижным, а в соответствии с используемыми клавишами перемещаются его правый и нижний края. После выполнения требуемых изменений необходимо снова нажать клавишу ScroULock. Это вызывает перенесение текста в измененное окно. Внимание: Аналогичные операции могут быть выполнены на справочных окнах. Окна меню могут только перемещаться.
16. Отладчик 359 Рис. 59. Диалоговое окно II. ► A11-F3 risk a module File ьВДЗ ESun Breakpoints Bata Hindon Gptions Sine main рг re Breakpoints Stack Log Hatches Variables Module... File... CPU tap Registers Numeric processor User screen A11-F5 tap File... Matches- Fl-Help H-flove f-'-Select Letters-ttaich Esc-Ahort Рис. 60. Диалоговое окно III.
360 Часть V. Турбо отладчик File View Rett Brealtpoints Data Window Options 2E0S '.le , tWlH Fl 3] ttinclude <stdio.h) > maint) Й- < g; printf("Hello from Debugger'*!; 8- return 0; > ________________________-J Рис. 61. Окно перед сдвигом. File Viets tan int Data Window Options ttinclude <sldio.li> ® raainO < print? "Hello from Debugger” ; return 0; Рис. 62. Первая фаза сдвига.
16. Отладчик 361 *Н: ffZ-ЙфГ al 'F3^Mod F^Anim fS-User FMndo ~#-Tnsir"On F3-i?o HlHocai Рис. 64- Перемещенное окно.
362 Часть V. Турбо-отладчик 16.3. Информационны» окна Информационные окна вызываются на экран из глобального меню View. Каждое из них снабжено одним из следующих названий: Breakpoints Stack Log Watches Variables Module File CPU Dump Registers Numeric processor User Screen а также порядковым номером, высвечиваемым в правом верхнем углу. В данный момент активировано как минимум одно из информационных окон. Чтобы убрать информационное окно с экрана, необходимо воспользоваться опцией Window/Close (клавиша F3). Эта акция, называемая в дальнейшем закрытием окна, может быть аннулирована с помощью опции Window/Undo Close (клавиши Alt-F6). Если произойдет закрытие окна, после чего не появится ни одно другое окно, то это означает фон окон. Существует три вида окон: окно модулей (Module), файлов (File) и процессора (CPU). Они могут появляться многократно. Это позволяет, например, знакомиться с текстом, содержащимся в разных базовых файлах. Эту дополнительную особенность отладчика можно использовать, выбирая опцию View/Another. Перемещение между информационными окнами (например, между несколькими модульными окнами) обеспечивает клавиша F6. Кроме того, окна могут выбираться на основе их номеров. В частности, в целях активизации окна с номером I достаточно нажать клавишу Alt~l. Если будет нажата клавиша Alt-O, то появится диалоговое окно, содержащее перечень всех открытых окон. После выбора одного из приведенных методов выбранное окно становится активным. Некоторые окна поделены на рамки. Перемещение между ними в направ- лении движения часовой стрелки обеспечивает клавиша Tab, а перемещение в противоположном направлении обеспечивает клавиша Shift-Tab. В данный момент активна только одна рамка. Именно та, в которой находится мигаю- щий курсор, или с выделенным заголовком. Во втором случае перемещение между поляьги рамки может происходить не только с помощью клавишей со стрелками, PgUp, PgDn и т.д., но также путем выбора заданной последо- вательности символов записи. Такой способ выбора полей особенно удобен в случаях с документами, подобранными по алфавиту, позволяя быстро пере- мещаться курсору к слову, начинающемуся рядом введенных букв. В области каждой рамки с целью высвечивания локального меню, относящегося к данному контексту, можно воспользоваться клавишей Alt-F 10 или Ctrl-FlO. Во многих случаях достаточно ограничиться нажатием клавиши Enter, обеспечивающей ускоренный выбор необходимой в данном контексте опции локального меню. Желательно также ознакомиться с реакцией отладчика на начало записи в области каждой из рамок. Окно модулей В окне модулей (Module Window) высвечивается основной текст запускае- мой программы. Перемещение по этому тексту позволяет выделить выбран- ные идентификаторы переменных, а также выражений, расставить ловушки и выдать поручения для способа дальнейшего выполнения программы.
16. Отладчик 363 File Шеи Run Breakpoints ftata Hiiwtew options Mt: F2-Bkpt at F3-lfod F^Anim F5-User F6-Unite F7-Iwstr РЯ-М-а FMo FM-Local Рис. 65. Фон окон. File Wiew Run Breakpoints Data Window Options rfradule: MAIM File: MAIM. C 3------------------------------- ^include (stdio.W ► mainO < printf(“H®llo from Bebugger”); (Module: MAIM File: MAIM.C 3- return 0; жжшж include (sldio. h) main!> 4 print!("Helle from Debugger"); return 0; ^include (stdio.b) h main!) { printf("Hello from Debugger"); return 0; ЖГЖг'р?’^ Рис. 66. Многократное окно.
364 Часть V. Турбо отладчик Окно обзора (наблюдений) В окне наблюдений (Watches Window) высвечиваются имена и значения переменных. Самым простым способом размещения имени переменной в окне наблюдений является перемещение курсора к идентификатору переменной, а затем нажатие клавиши Ctrl-W. В рассматриваемом окне могут высвечиваться также выражения и значения выражений. С целью размещения в окне наблюдений определенного выражения (в частности, имени переменной) необходимо выделить его с помощью клавиши Ins и клавиши со стрелками, а затем нажать клавишу Ctrl-W. Окно ловушек В окне ловушек (Breakpoints Window) высвечиваются действующие ловушки программы. В левой рамке окна находится список расставленных ловушек, а в правой описание ловушки, выделенной курсором. Перемещение курсора в области рамки вызывает высвечивание информации об очередных ловушках. С помощью локального меню можно модифицировать, удалять и добавлять ловушки. Удобным способом установки простой ловушки является перемещение курсора в модульное окно, а затем нажатие клавиши F2. Окно вызовов * В окне вызовов (Stack Window) высвечиваются имена активных процедур. В целях получения дополнительной информации об этих процедурах необходимо (после перемещения курсора в области рамки) вызвать локальное меню или инспекционное окно. Этим способом можно, например, познакомиться с локальными переменными вызванной процедуры, а также с аргументами и местом вызова данной процедуры. Окно сообщений В окне сообщений (Log Window) высвечивается информация, зарегистрированная во время останова программы: значения выражений, выбранных в момент, когда программа попала в ловушку; содержимое произвольно выбранных рамок, а также направленные в него комментарии. Если в окне сообщений выводится более чем 50 строк, то первые строки будут утеряны. В целях сохранения всех строк, посланных в окно сообщений, можно их записать в файл. Окно переменных В окне переменных (Variables Window) высвечиваются идентификаторы переменных, доступные в месте останова программы. В левой рамке окна находится перечень общих переменных (доступных вне модуля), а в правой - список переменных, доступных только в модуле или в области его процедур. С каждым идентификатором переменной связана информация о типе переменной и ее значении. Если переменная является структурной, то можно познакомиться с ее компонентами, вызывая локальное меню иди инспекционное окно. Подобным способом можно познакомиться ’ с переменными указательного типа.
16. Отладчик 365 File View Run Breakpoints Data Window Options 3SEQ include (stdio.h) > mainO < int Fix = 12; void Inc(int *); Inc(8Fix); printfCxd", Fix); return 0; 1 void Inc(int «Par) < (*Par)++; 1 Hatches-----------------————— -------------------------------------------------z F2-Bkpt F3-Close F4-Here F5-Zoom FO-Hext Fl-Trace F8-Step FB-Run FiO-Menu Рис. 67. Окно модулей. File View Run Breakpoints Data Hindow Options Module: PAIR File: PAIR.C (modified) 7----------------------- einclude (stdio.h) mainO ( int Fix = 12, void Inc(int *); h Inc(SFix); printf(“Xd”, Fix); return 0; ) void Inc(int xPar) ( (*Par)+a; 1 "• tches" !.v inf 12 (-вхй F2-Bkpt F3-Close F4-Here F5-Zc»om FC-Next F7-Trace F8-Step F9-Run FlO-Nenu Рис. 68. Окно обзора (наблюдений).
366 Часть V. Турбо отладчик File View Run Breakpoints Data Hi ata Options Nodule: PAIR File: PAIR.C (modified) 9---------------------- Binclud main!) Irgaltpinjlt; РА1ЕЙ9 int F void Breakpoint Always Enabled ► lucid print"»— returtt 0; } void Inclivit iSPar) {^•ParS-MJ > н latches- -2 F2-Bkpt F3-Ciose M-Hsre F5-Zcotn FB-Hext F7-Trace F8-Step F9-Run FID-Menu Рис. 69. Окно ловушек. -2- ???? int * ds:FFD6 [Hatches Fix Par F2-Bkpt F3-Close F4-Here F5-2oon> FB-Hexl F7-Trace F8 Step F9-Run F10-№nu Рис. 70. Окно вызовов.
16. Отладчик 367 Окно файлов В окне файлов (File Window) высвечиваются данные, содержащиеся в дисковых файлах. Предусмотрены также средства, позволяющие высвечивать данные в виде текстов и шестнадцатеричном виде, а также производить модификацию файлов. Осуществляется это с помощью локального меню. Окно процессора В окне процессора (CPU Window) высвечиваются данные о текущем сос- тоянии процессора. Окно процессора состоит из 5 рамок, в которых нахо- дятся: код ассемблера команд программы, данные выбранной области памяти в символьном и шестнадцатеричном виде, содержимое регистров процессора, содержимое стека процессора, а также биты регистра флажков. В верхней строке окна указан тип процессора. Знак, имеющий вид стилизованной стрелки, направленной вправо, выделяет команду, которая будет выполнена следующей. В верхней строке рамки, содержащей команды, указываются адрес памяти и значения величины, находящейся по этому адресу. Окно памяти В окне оперативной памяти (Dump Window) высвечиваются данные, соде- ржащиеся в выбранной области оперативной памяти. Эти данные могут быть представлены в шестнадцатеричном виде, в виде символов, слов и двух слов, а также как числа в различных представлениях. Использование локального меню позволяет модифицировать данные и перемещать блоки памяти. Окно регистров В окне регистров (Reislers Window) высвечивается содержимое регистров процессора. Использование локального меню позволяет модифицировать содержимое регистров. Окно сопроцессора В окне сопроцессора (Numeric Processor Window) высвечивается содержимое регистров сопроцессора. Окно сопроцессора состоит из трех рамок, в которых находятся: содержимое регистров сопроцессора, содержимое регистра флажков сопроцессора и управляющие данные. Выходное окно В выходном окне (User Screen Window) высвечивается выходной экран программы. Он точно такой же, как будто активизация программы произо- шла в системе DOS. В целях ускоренного высвечивания этого окна можно воспользоваться клавишей AII F5. После знакомства с экраном выхода можно высветить экран отладчика, нажав произвольную клавишу клавиатуры. 16.4. Инспекционные окна Инспекционные окна служат для высвечивания информации о переменных и выражениях. Они вызываются на экран нажатием клавиши Ctrl-I. Эту
368 Часть V. Турбо отладчик File View Run Brealtpoints Data Hindow Options 3ZLIi Module: PAIR File: PAIR.C (modified) 8----------------------------------------------1 Binclu.de (stdio.h) main() ( int F void Inc (ft F print retur Stopped at main Breakpoint at PAIR814 Stopped at main Breakpoint at PAIRB8 void Inctint яРаг) ( (*Par)++; > fit Alt: F2-Bkpt at F3-itod FdkAnim F5-User FB-Undo Fl-Instr F8-Rtn F9-To F10-Local Рис. 71. Окно сообщений. File Vie» Run Breakpoints feU №nd<№« Options Module: PAIR File: PAIR.C (modified) 8----------------------- ^include (stdio.h) main() < int Fix = 1Z; void аИЙШП Inc(& print relur void In War Jne _8087 -JPIITN -JOERROR __InlOVector __IntdVector __Int5Vector iSazzi ???? ???? ???? ???? ???? Till Fix 13 (QxJ) 1 latches ait; F2-Bkpt at F3-Mod F4-AiUm F5 -llser F6-«ndo FF-Inslr F8-Rtn F9-To FID-Local Рис. 72. Окно переменных.
16. Отладчик 369 file Шеи Run Breakpoints 0ala Hindow Options '(if-lM Module: PAIR File: PAIR.C (modified) 8----------------------------------------1- Sinclude (sldio. h> ) rnainO < ini Fix = 12; void Inc(inl *); > < > 3 In («Par ile oiain.c.,1 ((include (sldio. h> rnainO printf("Hello from Debugger”); return 0; Inc(&r print retur patches- ........ ............. - ---------------------------------2 PZ-Bkpl F3-Close H-Here F5-Zoom F6-Nexl 67-Trace F8-Step F9-Run F 0-Menu Рис. 73. Окно файлов. File View Run Breakpoints Data Hindow Options 2SES [Module; PAIR File: PAIR.C (modified) 7 ((include (sldio.h> rnainO ( еНЮЭД sslFFDS _ 0flflC= int ЯДШ’. Й - ’ 1 ax 0000 c-0 1 void (• Inc(S print retur 1 void In < («Par ) cs:0205)8D46FE lea ax,Ihp-021 cs:0Z08 50 push ax cs.’OZOS E81500 call _Inc cslOZOC 59 pop ex PAIRS8: printf(“Zd", Fix); cs:020D FF76FE push Ihp-OZJ cs:0210 B89401 mov ax,0194 cS:0213 50 push ax cs:0214 E8C8O8 call _prinlf cs:0217 59 pop ex cs:0Z18 53 pop ex PAI RM: return 0; bx 04AE CX 0014 dx 9D64 si 0033 di 04A2 bp FFD8 sp FFD6 ds 5BBB es 5BBB ss 5BBB cs 5A83 ip 0205 z=0 S=1 o-O p=0 d-0 [Watches— ds:0008 00 00 00 00 54 75 72 6Z Turb ds:0008 6F ZD 43 20 ZD 20 43 6F o-C - Co ds:0010 70 79 72 69 67 68 74 20 pyright ds:0018 28 63 29 Z0 31 39 38 38 (c) 1988 sslFFDC 0001 ss:FFDA 011D ss:FFB8 FFE4 ss:FFD6)OOOC —Z1 F2-Bkpt F3-Close Ffl-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run FlO-flenu Рис. 74. Окно процессора.
Часть V. Турбо отладчик 370 File Uiew Йип Breakpoints Bata Hindow Options iSiSi Module: PAI File: PAIR. C (modified) 7----------------------------1 ^include (stdio. h> main() < int Fix = 12; void Inctint *); ► Inc(iFix); printf("2d", Fix); retur ЖГИ——————————————......................-3 ) ds: 0000 00 00 00 00 54 75 72 62 Turb ds:0008 SF 2D 43 20 ZB 20 43 6F o-C - Co void In ds:0010 70 73 72 63 6? 68 74 20 pyrighl { ds:0018 28 63 29 20 31 39 38 38 (c) 1988 War™-»........... 1 . > w । patches--- —---------------------------------------------------? FZ-Bhpt F3-Close И-Here F5-Zoom F6-Next Fl-Trace F8-Slep F9-Run F10-Menu Рис. 75. Окно памяти. File Vie» Run Breafcpojnls Module: PAIR File: PAIR.C -1t®4 ((include (stdio.h) SS: r»ain() < iffi int Fix = 12; void Inc(int »); Inc(dFix); :й?й printfC'Zd”, Fix); g.g: return 0; > Ж void Inctint «Par) < Ж ) War)w; ) -ЖЙ Gala Hindow Options z~0 bx 04AE 0014 A575 0033 04A2 FFDO FFBO 5BB& 5BIIB 5BBB 5A83 0224 ex dx di bp sp ds es o.-O prO a-0 i=l| drO 05 IIP FZ-BRpt F3-Ciose F4^Here iF5-Zoom F^Nexl Fi-frace Fe-Step FS-Run FiO-Menu Рис. 76. Окно регистров.
16. Отладчик 371 File View Run Breakpoints Data Hindow Options Module: FLOAT File: FLOAT. C (modified) 8-------------------- ^include (stdio.h) |CPU 80286----------- jnain: nain<) cs:01FA 55 cs.'OLFB 8BEC FL0AT08: printfC'Zf' cs:01FD CD35069401 cs:OZ02HD35069401 cs:0207 CD3AC1 float. И mainO < print retur ) •ds: 0194 - 0000 push mov Number fid fid faddn st(l),st bp hp.sp * Number); dword ptriHu dword ptriHa 0000 0634 0014 B674 HilatoigЛPTR= 00000 pPlSsflpra Fall da.srw);. 13»S. ’ l‘ z__________ Empty 51(1) aEmpty ST(2) UEmpty 51(3) Empty ST(4) iEfflply SK5) Empty S7(6) -----sEmpty ST(7) latched»-"™—-—- ira=0 ie=0 dm=0 de=0 zra=0 ze=0 om=0 oe=0 um=l ue=0 pm=l pe=0 iem=0 ir=0 pc=3 cc=9 ax bx ex dx si 0033 di 06Z8 bp FF06 1 FS6 EE9 EES EE9 A83 202 FDC FFEO m 0001 ™ OILS I ds:0018 ZB 63 29 ZO 31 39 38 38 (c) 1988 | ss!FESS)FFE2 c=0 z=0 o-U p=l a=l 1=1 d=O ------2- FZ-Bkpt F3-Close F4-Here F5-Zoom FB-Next FMrace F8-Step F9-Run F10-fenu Рис. 77. Окно сопроцессора. C:\TC20\BUNPS>tc 27.000000 C: \TC20\SUMPS)td float Turbo Debugger Version 1.0 Copyright (c) 1988 Borland International Z7.000000 Рис. 78. Выходное окно.
372 Часть V. Турбо отладчик клавишу необходимо нажимать, когда курсор выделяет идентификатор пере- менной или когда после нажатия клавиши Ins и клавиши стрелок выделен определенный блок знаков, образующих выражение. Инспекционное окно не делится на рамки, но состоит из полей, содержащих, кроме прочего, имя переменной, ее тип, адрес, значение переменной и т. д. Если переменная является структурной, то в окне высвечиваются ее компоненты, если указательной - то высвечивается информация об указываемой переменной. Подобный способ высвечивания информации относится и к выражениям. Перемещение между полями инспекционного окна происходит с помощью клавиши вертикальных стрелок. Если после выбора определенного поля будет снова нажата клавиша Ctrl-Ц то появится инспекционное окно, касающееся выбранного компонента переменной. Очередное нажатие этой клавиши позволяет выполнить проверку компонентов более низкого уровня. Ниже приведены примеры инспекционных окон для программ на языках Турбо Си и Турбо Паскаль. Турбо Си typedef struct { unsigned Age; charName[g]; } Person, * RefPerson; float Number “ 12.4; int Fixed “ 13; unsigned char Arr[2] [2] “ {{1, 2}, {3, 4}}; Person Rec “ {5, "Isa"}; char *Str “ "Isabel”; Person People [ 'c' - 'a' + 1] “ { {40, "Eva"], { 6, "Isa"}, {46, "Jan"}}; Person *Ptr; RefPerson *RefPtr; int *Ref main( ) { Ptr &Rec; RefPtr « &Ptr; Ref “ fiFixed; /* ... */ } Турбо Паскаль type Person •= record Age : word. Name : String {5] end; RefPerson “ Person; const
16. Отладчик 373 Number : real = 12.4; Fixed : integer = 13; Arr : array [1 .. 2, boolean] of byte “ ( (1.2). (3,4)); Rec : Person = (Age : 5; Name : 'Isa'); Str : string = 'Isabel'; People : array ['a' ... c'J of Person = ( (Age : 40; Name : 'Eva'), (Age : 6; Name : 'Isa'), (Age : 46; Name : 'Jan')); var Ptr : “Person; RefPtr : “RefPerson; Ref : “integer; begin Ptr := 6 Rec; RefPtr := 0 Ptr; Ref := 0 Fixed; { ... } end. Для того чтобы убрать с экрана последнее созданное инспекционное окно, необходимо нажать клавишу ESC. Нажатие клавиши F3 вызывает закрытие всех таких окон. Как и другие окна, инспекционное окно имеет свое локальное меню. , Оно состоит из следующих полей: \ Range \ Change Inspect Descend Nfew expression... Поле Range Выбор поля Range позволяет определить размер высвеченных компонентов структурной переменной. Этот размер состоит из указания первого компонента и числа компонентов. Это особенно удобно в случае таблиц. Поле Change Выбор поля Change позволяет изменить значение переменной. Поле Inspect Выбор поля Inspect имеет такой же результат, как нажатие клавиши Ctrl-E, кроме того, вызывает открытие очередного инспекционного окна. Это позволяет углубиться в структуру массивов и записей, просматривать цепочку указаний и выявить развитие команд ассемблера.
374 Часть V. Турбо отладчик File View Run Breakpoints Data Window Options ЗЖЕЙ [Module: INSPECT File: INSPECT, C 6-------------------------——--------------------1 typedef strutH unsigned Age; char Name(5J; > Person,*RefPerson; float Number - 1Z.4; tispectitig Humber >SCO4il№4 Person [float 1Z.\ char «S'-1------------------—-------—3 Person People!'c' - 'a' + 1J - < < 40, "Eva" 1, < 6, "Isa" 1, < 46, “Jan" 1 1; Person «Ptr; RefPerson «RefPtr; int «Ref; main!) itches FZ-Bkpl F3-Close F4-Here F5-Zoom F6-Next FZ-Trace F8-Step F9-Run FlO-Menu s) File View Run Breakpoints' Data Window Options JSE9 [Nodule: PROGRAM File: INSPECT. PAS 8-----------------------------------------—----1 type Person = record Age : word; Name : string!51 end; RefPerson - APerson; const Number : real - 1Z.4; Fixed : integer = 13; Arr : array (1..Z,boolean! of byte = ( <1,Z>, (3,4) >; Rec : Person z (Age : 5; Name : 'Isa'); Str : string z 'Isabel’; People : array f'a'..'c'l of Person z ( (Age : 40; Name : 'Eva'), (Age : 6; Name : 'Isa'), (Age : 46; Name : 'Jan') ); FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run FlO-Menu 6) Рис. 79. Проверка переменной Number, а) Турбо Си; б) Турбо Паскаль.
16- Отладчик 375 File View Run Breakpoints Bala Hindow Options rflodule: INSPECT File: INSPECT. C 7-------------------------- typedef struct! unsigned Age: char Name!51; > Person,*RefPerson; float Number 12.4; int Fixed - 13; 11 int Person char *! Person < < 40, "Eva" 1, 1 6, "Isa" >, < 46, "Jan" > ); Person *Ptr; RefFerson RefPlr int xRef; nainO -2 Alt: F2-Bl<pt at F3-Mod F4-Anim F5-User FB-Undo F7-Instr F8-Rtn F9-To FlO-Local File View Run Breakpoints Plata Hindow Options Module: PROGRAM File: INSPECT. PAS S type Person - record Age : word; Name : stringI5) end; RefPerson - "Person; const Number Fixed Arr : var real - 1Z.4; : integer = 13; array II. .2,boolean) of ( (1,2), (3,4) ); Person = (Age : 5; Name string - 'Isabel’; ' .'c'J of ) 40; Name ! 6; Name : Rec Str People : array I'a' ( (Age : (Age : (Age : 46; Name byte = Isa'); Person = : 'Eva'), : 'Isa'), : 'Jan'> >; Matches 6) Alt: FZ-Bkpl at F3-Mod F4Anim F5-Bser FS-Undo F7-Instr F8-Rtn F9-To FlO-Local Рис. 80. Проверка переменной Fixed, а) Турбо Си; б) Турбо Паскаль.
376 Часть V. Турбо отладчик File View Run Breakpoints Data Hindow Options [Module: INSPECT file; INSPECT.C 8--------------------------- typedef struct! unsigned Age; char Name!5]; > Person,*RefPerson; float Number = 12.4; int Fixed = 13; unsigned char Arr{Zl! 2] = < (1,21, <3.41 1; Person Rec - I char *Str = "Is Person 5С04?005а unsigned char 12] < 6 B { 46 Person *Ptr; PelPerson «RefPtr; int *Ref I mainO latches -Z FZ-Bkpt F3*Close М-Here F5-Zoom F6-Next F7-Trace F8-Slep F9-Run FlO-henu a) 3 (1,2) jlnspectiHg Arr] P5B81:0008 [1] ARRAY (BOOLEAN] OF BYTE File View Run Breakpoints Data Hindow Options Module: PROGRAM File: INSPECT. PAS 10-------------------------------------------L-, type Person - record Age : word; Name : string! 5] end; RefPerson = APerson; const Number ! real ; 1Z. 4; Fixed : integer - 13; Arr : array LI.. Z,boolean] of byte = ( <1,Z>, (3,41 ); Rec : Person = (Age : 5; Name I 'Isa’l; Str : siring - 'Isabel'; People : array I' a’.,' c’ J of Person - ( (Age ; 40; Name : 'Eva'), (Age : 6; Name : 'Isa'), (Age : 46; Name : 'Jan') >; var Alt: F2-Bkpt at Fl-Mod F4-Anim F5-User F6-Undo F7-Instr F8Rtn F9-To FID-Local 6) Рис. 81. Проверка массива Air. а) Турбо Си; б) Турбо Паскаль.
16. Отладчик 377 File View- Run Breakpoints Data [Module: INSPECT-. File: INSPECT. C 8----- Hindow Options unsigned Age; char Name!5]; > Person,«RefPerson; float Number - 12.4; int Fixed - 13; unsigned char ArrtZHZ] - < (1,Z), <3,41 1; Person Rec = < char «Str = “Is Person People!' < < 40 1 6 < 46 Person *Ptr; Jefferson «RefPt int «Ref; main!) platches- FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next Fl-Trace F8-Step F3-Run FlO-Menu a) File View Run Breakpoints Bata Hindow Options type person - record Age : word; Name : string!5J end; kefPerson - APerson; const Numbei Fixed Arr : ; real = 1Z.4; : integer - 13; array II. .2,boolean] of byte - < (1,2), (3,4) ); Person = (Age : 5; Name ; 'Isa string - 'Isabel'; ,'c'J of 40; Name 6: Name (Age : 46; Name Rec : Str : People ." array I'a' ( (Age : (Age Person - : ' Eva'>, : 'Isa'), : ' Jan') ); 6) FZ-Bkpt F3-Close M-Неге F5-Zoom F6-Mext F7-Trace F8-Step ГЭ-Run FlO-Menu Рис. 82. Проверка элементов массива Air. а) Турбо Си; б) Турбо Паскаль.
Часть V. Турбо отладчик 378 File View Run Breakpoints Data Hindow Options (Module: INSPECT File: INSPECT.C 9--------------------------- .MM typedef struct! unsigned Age; char Name!51; > Person,*RefPerson; float Number - 1Z.4; int Fixed = 13; unsigned char ArrlZJtZJ - ( d,Z), (3,4) >; Person Rec = < 5,“Tsa'“ ); char * гсЯУЙ^ЙяТОУЙ'/Й—-——----------За Person Pe P5C04:009E lElis! » В-----------------------в Person »I char 151 RefPerson^™»™— 111 ' '" " ...." '•>1 int *Ref; isainO patches-----------------——-------------------------------------------- 2 ЛИ: FZ-Bkpt at F3-Mod F4-Anim F5-User FG-Undo F7-Instr F8-Rtn FS-To FlO-Local File View Run Breakpoints Data Hindow Options Module: PROGRAM File: INSPECT. PAS 1Z- type Person = record 11 Age : word; Name : string!51 end; RefFerson : APerson; const (?5B81:000C AGE Number : real - 1Z.4; e—™ Fixed : integer - 13; Arr : array 11..Z,boolean! of byte - < (1,Z), (3,4) ); Rec Person - (Age : 5; Name : ’Isa'); Str : string = 'Isabel';. People : array t'a'..'c'J of Person = STRING!51 ( (Age : 40; Name (Age : 6; Name (Age : 46; Name Eva'), Isa' >, Jan') ); rfdatches F2-Bkpt ГЗ-Close F4-Here F5-Zoom F6-Next Fl-Trace F8-Slep F9-Run FlO-Henu Рис. 83. Проверка записи Rec. a> Турбо Си; б) Турбо Паскаль.
- File View Sun Breakpoints Data Hindow Options [Module: INSPECT File; INSPECT. € 9---------------------------- typedef slructf unsigned Age; char NameI5I,' ) Fez-son. «RefPerson; - float Ni.imlier = 12.4; int Fixed •- 13; unsigned char ArrtZHZl - < (1,2), <3,41 ); Person Rec ~ { 5,"Isa“ ); char ifSlrtlinspecling Rec,--—-----------3- Person Ре P5C04:009E FZ-Bkpt F3-Close F4-Here F5-Zooe F6-Next F?-Trace F8-Step F9-Run FiO-Menu a) File View Run Breakpoints Data Hindow Options Module: PROGRAM File: INSPECT.PAS 12--------------------------------------------1 type Person = record {Inspecting Rec-----------3- Age : word; |P5B81:000C Name : stringlSJ AGE 5 end; INANE * Isa' RefPerson = APerson; const Number : real = 12.4; Fixed : integer - 13; Arr : array II..2.boolean] of byte ( (1,2), (3,4) ); Rec : Person = (Age : 5; Name : 'I: Str ! string : ' Isabel'; People : array l'a'..'c'J of Fersoi ( (Age : 40; Name : 'Eva'), (Age : 6; Name : 'Isa'), (Age : 46; Name : 'Jan') ); var [Matches---------------------------------------------------—-----------------------2 6) F2-Bkpt F3-Close F4-Here F5-Zoom F6-Kext, Trace F8-Step F9-Run FlO-Nenu Рис. 84. Проверка поля записи Rec. а) Турбо Си; б) Турбо Паскаль.
File View Run Breakpoints Data Hindow Options [Module: INSPECT File: INSPECT. C ID-------—--------------------------------------Ь typedef struct! unsigned Age; char Hamel 51; ) Person,*RefPerson; float Number - 1Z.4; int Fixed = 13; unsigned char ArrlZllZl = { (l.Z), Person Rec - < 5,nIsa" >; char *Str - "Isabel”; Person People!'c' - 'a' + 1] - < { 40, "Eva" ), { 6, "Isa” }, < 46, “Jan" > ); Person *Ptr; w RefPerson «RefPtr; int *Ref; rnainO <3,41 }; Inspecting StrSSESiS >5С04:00Д5 i 5C04:ОЭВЕ "Isab 3 patches 01 111 izi 131 к 4i l!51 'I' 73 's' 115 'a' 97 ’b' 98 e' 101 'I' 108 Jcnar far * —z FZ-Bkpt F3-Close fd-Here F5-Zoom F6-Next F7-Irace F8-Step F9-Run FlO-Menu a) File View Run Breakpoints Data Hindow Options [Module: PROGRAM File! INSPECT. PAS 13------------------------ type Person - record Age : word; Name ! string!51 end; RefPerson = APerson; const Number ! real = 1Z.4; Fixed Arr 1 e: : integer = 13; array (1..Z, boolean) of byte = ( (1,Z), (3,4) ); Person = (Age : 5; Name 'Isa'); string - ' Isabel'; >'c') of 40; Name 6; Name (Age : 46; Name Rec Str People : array f'a ( (Age : (Age : jlHspectittg Str] var latches Person = : 'Eva'), : 'Isa'), : ’Jan') ); ₽5B81:0014 'Isabel 101 III !ZI 141 151 CHAR FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace FS-Step F9-Run FlO-Menu Рис. 85. Проверка переменной Str. а) Турбо Си; б) Турбо Паскаль.
16. Отладчик 381 File View Run Breakpoints Bala Hindow Options Nodule: INSPECT File: INSPECT. C 11--------------------------------------------Iq typedef struct! unsigned Age; char Nane[5I; } Person,«KefPerson; float Number - 12.4; int Fixed = 13; unsigned char ArrlZHZJ : < <1 Person Kec - I 5,“Isa" }; char *Str - "Isabel”; Person People!' c' - 'a' t 11 г t < 40, “Eva" 1, { 6, "Isa” }, < 46, “Jan" 1 ); Person *Ptr; RefFerson «KefPtr; int «Ref; mainO [Watches-----------------------------------------------------------------------------Z FZ-Bkpl F3-Close F4-Here F5-Zoom F6-Next FP-Trace F8-Step F9-Run FlO-Mer.u a) File View Run Breakpoints Oata Window Options ( (1,2), (3,4) ); Rec ; Person = (Age : 5; Name : 'Isa'); Str ; string = ' Isabel'; People ; array f'a',.'c'I of Person ~ end. [Watches---------————---------------------------------------------------------------Z F2-Bkpt F3-Close F4-Here F5-Zoom F6-Next FJ-Trace F8-Step F9-Run FlO-Nenu 6) Рис. 86. Проверка таблицы структуры People, а) Турбо Си; б) Турбо Паскаль.
382 Часть V. Турбо отладчик File Viot-j Run Breakpoints Data Hindow Options [Nodule: INSPECT File: INSPECT.C 11-------------------------- typedef struct! unsigned Age; char Name!SI; > Person.*RefPerson; Ileal Number = 12.4; int Fixed ; 13; unsigned char ArrlZHZJ - 1 11.21. <3,41 I; Person Rec = 1 5,"Isa" >; char *Str - “Isabel"; Person Peoplet'c' - 'a' al] 1 1 «I, “Eva” 1, 1 6, "Isa” », < 46, “Jan" > >; Person «Tfcr; RefPerson «RefPtr; int «Ref; main!) rlnspecting People----------3- №C04:00A9 101 (40.nEva\0\0"> til <6,"Isa\0\0") tZl (46,“Jan\0\0"l Age 46 Name “JanXOMT rtlatches- struct ait: F2-Bkpt at F3-tiod F4-Anim FS-User F6-Undo F?-Instr F8-Rtn F3-To FiO-Ucal a) File Nodule: ?WpfeT,,b'’]! View Run Breakpoints Data Hindow Options PROGRAM File: INSPECT. PAS 14------------------------ ( (1,2), (3,4) ); : Person z (Age : 5; Name : 'Isa'); : string - ' Isabel'; c'l of Person = Inspecting People-----------3-jEva'), №881:0114 lisa'), t'a'l (40, 'Eva') I Jan*) ); t'b'l (6,'Isa') | 05881: one Rec Str People : array t'a' var- P R R . SSAGE RefPtrKSTRING[5] Ref ( ... ] end. latches- AM: FZ-Bkpt at F3-Ht>d F4-Amin F5-«ser F6-Vndo F?-Instr FO-Rlm F9-To HO-local 6) Рис. 87. Проверка элемента таблицы структуры People, а) Турбо Си; б) Турбо Паскаль.
16. Отладчик 383 File View Run Breakpoints Data Hindow Options ]SE0 Module: PROGRAM File: INSPECT. Pte Z3--------------------------------------------1- Rec : Person - (Age : 5; Name : 'Isa'); Str : string - 'Isabel’; People : array £'a'..'c'l of Person - ( (Age : 40; Name : 'Eva'), (Age ) 6; Name : 'Isa'), (Age : 46; Name : ’Jan*) ); var Ptr : "Person; RefPtr : "RefFerson; Ref : "integer; begin Ptr := PRec; RefPtr := OPtr; Ref : = GFixed; < ... J> end. latches- Ait; F2-Bkpt at FS-Nod F4-Anim FS-teer FS-«ndo F?-Instr Fft-Rtn F9-To FlO-Local File View Run Breakpoints Data Hindow Options MM Module: INSPECT File: INSPECT. C 15------------------------------------------i-| typedef struct! unsigned Age; char Hane£51; ) Person,«RefPerson; float Number - 12.4; int Fixed = 13; unsigned char ArrI21£21 = ( (1,2), (3,4) >; Person Rec = < 5,“Isa" }; char tetr - “Isabel"; Person People!'c' - 'a' + 11 = f < 40, "Eva” >, < 6, “Isa” >, < 46, “Jan" > >; Person tetr; RefFerson «RefPtr; int «Ref; etainO patches-----------------------------------------------------------------------2- Alt: F -Bkpt at F3-«od F4-Anin F5-User F6-Undo F7-Instr F8-Rtn F9-To HO-Local Рис. 88. Проверка поля элемента таблицы структуры People, а) Турбо Си; б) Турбо Паскаль.
384 Часть V. Турбо отладчик File View Bun Breakpoints Data Hindow Options [Module: INSPECT File: INSPECT.C 11-------------------------- typedef struct! ----11 unsigned Age; char Manet 5]; > Person,*RefPerson; float Number - 12.4; int Fixed = 13; unsigned cbar ArrlZllZl - 1 <1,21, <3,41 1; Person Bee z < 5,“Isa" 1; char »Str z "Isabel"; Person People!'c' - 'a' t 11 < ! 40, "Eva” }, { 6, “Isa" >, t 46, "Jan" 1 1; Person *Ptr; RefPerson «RefPtr; int «Ref; rnainO Inspecting People^----------3 B5C04:80A9 (81 <40,“Eva\0\0”) HI (6,“Isa\0\0"> EZJ t46,“Jan\0\0“> [Inspecting People!21--------4 05C84:00B7 patches Age 46 Name "Jan\0\0" FZ-Bkpt F3-CJpse F4-Here F5-Zoom FG-Next F7-Trace F8-Step F9-Run FlO-Nenu a) File Module: -3 4- 5 CHAR Inspecting Nd№l 121 ! 31 6 Isa' c'l of Person z Eva'), Isa'), Jan') ); View Run Breakpoints Data Hindow Options PROGRAM File! INSPECT.PAS 14-------- ( (1,2), (3,4) ); Person z (Age : 5; Name : 'Isa'); string = 'Isabel',’ ‘ : array t’a' " Inspecting People- №B81:0114 f'a'J (40,'Eva') t'b'l (6,'Isa') !'c' [Inspecting People!'b'l— ----e5B81:011C AGE NAME _________ Rec : 1 str : i People PERS var P R R beg Ptr : = RefPtr Ref : = < ... > end. STRING!5] №B81:011E 'Isa £01 itches FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8-Step F3-Run FlO-Nenu 6) Рис. 89. Проверка указательной переменной Ptr. а) Турбо Си; б) Турбо Паскаль.
16. Отладчик 385 a) 6> File View Run Breakpoints [Module: INSPECT File: INSPECT.C typedef struct! unsigned Age; char Hamel51; 1» Person, «RefFerson; float Number - 1Z. 4; int Fixed = 13; unsigned char ArrlZJlZJ - ! 11, Person Rec = < 5,"Isa“ 1; char «Str = “Isabel"; Person People!'c' - 'a' t 11 = 1 < 40, "Eva" 1, 1 6, “Isa" 1, ! 46, "Jan" 1 1; Person «Ptr; RefFerson «RefPtr; int «Ref; nainO Data Hindow Options 2Л32 15-------------------------------------------1 [Inspecting Ptr----------—*3' R5C04:014A : 5C04:009E l_Rec Age 5 Name "Isa\0\0" unsigned int Zl, <3,41 >; [Inspecting Ag< p5C0I;003E unsigned int 5 FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9-Run FlO-Menu File View Run Breakpoints Data [Module: PROGRAM File: INSPECT.PAS Z3---- Hindow Options Rec : Person - (Age : 5; Name : 'Isa'); Str : string = 'Isabel'; People : array £’a'..' с'1 oi Person = ( (Age : 40; Name : 'Eva'l, (Age : 6; Name : 'Isa'), (Age : 46; Name : 'Jan') >; var , Ptr : “Person; RefPtr : “RefFerson; Ref : “integer; begin Ptr PRec; RefPtr := BPtr; Ref := BFixed; < ... 1 end. Inspecting Ptr—----------—-----------3 1*5681:0168 : 5B81:000C [PROGRAM.REC] AGE 5 NAME 'Isa' latches FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8-Step F9 Run FlO-Menu Рис. 90. Проверка поля записи, указанного Ptr. а) Турбо Си; б) Турбо Паскаль. 25—764
386 Часть V. Турбо отладчик File View Run Breakpoints Data Hindow Options rffodule: INSPECT File: INSPECT. C 16 typedef struct< unsigned Age; char Namet 5]; 1 Person,*RefPerson; float Number - 12.4; int Fixed - 13; unsigned char ArrlZHZJ - Person Rec - { 5,uIsa*’ >; “.Inspect inij p5C03 014E :: 5C04;ОНА, t_Ptr ГОЗ 5C04:009E I Reel struct far *far « { {1,21, {3,41 H char *Str - “Isabel"; Person People!'c' - 'a' ♦ 1J = I < 40, "Eva" 1, < 6, “Isa" 1, < 46, "Jan" 1 1; Person,«Ptr; RefPerson «PelPtr; int «Pel; main!) itches Alt: F2-Bkpt at F3-Mod F4-Anim F5-tlser F6-Undo FJ-Instr F8-Rtn F9-To FlO-Local a) File View Run Breakpoints Data Hindew Options rflodule: PROGRAM File: INSPECT. PAS 24............... —-----— Rec : Person = (Age : 5; Name ! ’Isa' >; Str I string = 'Isabel'; People : array £'a' ( (Age : (Age : (Age : 40; 6; 46; с* 1 of Name Name Name Person = : ' Eva'), : 'Isa'), : 'Jan') ); var Ptr : APerson_: RefPtr : ARefPerson; Ref ; Ainteger; begin Ptr : = PRec; RefPtr :z PPtr; Ref :~ PFixed; < ... 1 end. itches- •Tnspert i tig У MP ♦ r ; 5В81:Ш68 [РШШРГ» £11 AREFPERSON 5B81.000C [ PROGRAM. MCI FZ-Bkpt F3-Close F4-Here F5-Zoorri FB-Next Fl-Trace F8-Step FO-Run Fill-Menu 6) Рис. 91. Проверка указательной переменной RefPtr. а) Турбо Си; б) Турбо Паскаль.
16. Отладчик 387 File View Run Breakpoints Data Hindow Options [Nodule: INSPECT File: INSPECT. C 16 typedef struct! unsigned Age; char Name!51; 1 Person,«RefPerson; float Number - 12.4; int Fixed - 13; unsigned char ArrlZllZl - Person Rec x { 5,"Isa” 1; char «Str = "Isabel"; Inspecting RefPtr—---------3q P5C04:014E : 5C04:014A [_Ptr [01 5C04:009E [ Reel | ns pec t i ng ! e5C04:014A : 5C04:009E [Jtec] Age 5 tame । "IsaXOMT char [51 Person People!'c' - 'a' t 11 x < ( 40, "Eva" 1, < 6, “Isa" ), f 46, "Jan" > 1; Person «Ptr; RefPerson «RefPtr; int «Ref; rnainO itches FZ-Bkpt F3-Close F4-Here FS-Zoom F6-Next Fl-Trace F8-Step F9-Run FlO-Nenu File View Run Breakpoints Data Hindow Options rNodule: PROGRAM File: INSPECT. PAS Z4- Rec : Person - (Age : -5; Name : 'Isa'); S‘r : string x 'Isabel'; People : array ['a’..’c’J of Person x ( (Age ; 40; (Age : 6: (Age : 46; var Ptr : "Person; RefPtr : "RefPerson; Ref : "integer; begin Ptr :x Iй Rec; RefPtr := PPtr; Ref : - Mixed; ( ... ) end. Name : ' Eva'), Name : ' Isa'), Name : ' Jan') >; Inspecting RefPtr-----------------------------3- t?5B81:016C : 5B81:0168 [PROGRAM. PTR] (11 5ESl:000C [PROGRAM. REC1 latches FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next Fl-Trace F8-Step F9-Run FlO-Menu 6) Рис. 92. Проверка переменной указанной RefPtr. а) Турбо Си; б) Турбо Паскаль.
388 Часть V. Турбо отладчик File View Run Module: INSPECT File: typedef struct! unsigned Age, char Namel51; 1 Person.«RefFerson; float Number - 12.4, int Fixed = 13: unsigned char ArrtZHZJ r Person Rec - 1 5,“Isa" 1; char *Str = "Isabel”; Person Peoplel'c < I 40, Breakpoints Data Hindow Options INSPECT. C 16------------------------- Inspecting RefPtr-------------3 P5C04:014E : 5C04:014A [Ptr 101 5C04: OOOE [ Peel (Inspecting «RefPtr---------------------- 05C04: 014A : 5C04; 009E I _Recl Age 5 Mane “IsaXOxO1 4- char 151 - ’a’ « 11 = Eva” ), { 46. Person »Ptr; RefPersoti «RefPt: int «Ref; nainO Jan” > >; T,m P5CC4: Mao 101 in i^H/pagtrnj 5 ’Г 73 s' 115 (Watches [31 141 char \0' 0 \0 0 FZ-Bkpt F3-Close F4-Here F5-Zoom F6-Next F7-Trace F8 Step F9-Run FlO-Menu a) 6) File View Run Breakpoints Data Hindow Options Module: PROGRAM File: INSPECT. PAS Z4------------------------- Rec : Person - (Age : 5; Name : 'Isa'); Str : string = 'Isabel'; People : array I'a’ ( (Age : (Age : (Age : ад; 6; 46; c' 1 of Name Name Name var Ptr : APerson, RefPtr : ARefPerson; Ref : Ainteger; begin Ptr := PRec, 'Inspecting en ilc 35381:000E 'Isa' 101 lil 121 CHAR Person = : ' Eva’), : 'Isa'), : 'Jan') ); Inspecting RefPti--------------------- P5B81:016C : 5B81:0168 1 PROGRAM,PIR1 [11 5B81: ЙООС I PROGRAM. RECI REFFERSON -—5 ---- 3 I’ s' Inspecting RefPtr'----------4- 05881:0168 ; 5381:000C 1PR0G AGE 5 NAME ' Isa' STRING151 FZ-Bkpt F3-Close F4-Here F5-2oon F6-Next F7-Trace F8-Step F9-Run FlO-Menu -3 -Z Рис. 93. Проверка записи, указанной косвенно, а) Турбо Си; б - Турбо Паскаль.
16. Отладчик 389 Поле Descend Выбор поля Descend имеет такой же результат, как выбор поля Inspect, вновь созданное инспекционное окно занимает место текущего инспекционного окна. Это препятствует ненужному увеличению инспекционных окон, но не позволяет простелить назад цепь указаний или вернуться на высший уровень иерархии компонентов. Поле New expression Выбор поля New expression позволяет задать новое значение указанному выражению. Информация о выражении помещается в текущем инспекционном окне. 16.5. Составление выражений Состав выражений, которые можно конструировать пользуясь отладчиком, не отличается существенно от состава выражений используемого языка. В настоящей версии отладчика можно осуществить выбор состава в соответствии с Турбо Паскалем, Турбо Си или Турбо Ассемблером. По умолчанию принимается такой состав, какой вытекает из текущего базового модуля, то есть того, в котором произошел останов выполнения программы. Однако ничто не мешает выбрать иной состав. С этой целью достаточно высветить меню Options/Language, а затем определить выбор языка. В типичных случаях для обращения к переменной достаточно указать ее идентификатор, а в целях обращения к команде программы - указать ее номер строки, предваренной знаком # IhasK). Это вытекает из того, что, идентифицируя объект базовой программы, отладчик использует те же самые правила определения области действия описания, что и компилятор. Если локализация объекта в текущем базовом модуле окажется невыпол- нимой, то принимается, что речь идет о внешнем объекте, находящемся в одном из других модулей программы. Это предположение может быть анну- лировано указанием полного имени объекта паше; которое имеет вид [ # module]# file] ] # line]# пате] [ # module [ #file ] ] [ # procedure ] #name В этой записи, где module является именем модуля, file - именем файла, line - номером строки, a procedure - именем процедуры, фрагменты, взятые в квадратные скобки, могут быть опущены. В частности, выражение #44 #Count может быть именем переменной Count, доступной из 44-й строки текущего модуля, а #Routine #Local - именем переменной Local, доступной из процедуры Routine. Турбо Си
390 Часть V. Турбо отладчик Сохраняется полный состав выражений, за исключением операции присоединения, выраженной оператором (запятая). Допускается использование следующих операторов: Приоритет Операторы О : : 1 ()[].-> sizeof (одноаргументный) 2 ♦&-+!”++ — 3 * / % 4 + - 5 « » 6 <<=>>= 7 == ! = 8 & 9 w 10 ! 11 && 12 ! 1 13 ? : 14 - += - /= %= «= »= &==:= Внимание: Дополнительный оператор : : (двойное двоеточие) может использоваться для создания далекого указания (например 2: :3 является указанием номера сегмента 2 и смещения 3). Пример. Выражения в Турбо Си В области действия описания int Fixed = 13; выражение *&Fixed имеет значение 13. Турбо Паскаль Сохраняется полный состав выражений, за исключением операций множеств и соединения цепочек. Допускается пользование следующих операторов: Приоритет Операторы 1 2 3 4 5 @ * not type( ) + - (одноаргументный) * / div nod and shl shr in + - or xor Результатом операции := (присваивание) является присваивание значения переменной.
16. Отладчик 391 Пример. Выражения в Турбо Паскале В области действия описания const Fixed : integer “ 13 выражение (SFIxed)" имеет значение 13. Турбо Ассемблер Сохраняется использованием полный состав выражений, в которых ограничиваются следующих операторов: Приоритет Операторы 1 2 3 4 5 6 7 8 9 10 И 12 ххх PTR (например, BYTE PTR) OR XOR AND NOT EQ NE LT LE GT GE ♦ / MOD SHR SHL + - (одноаргументное) OFFSET SEG < ) [ ] Внимание: Присваивание значений переменным может происходить с помощью оператора = (присваивание), например Count - [BYTE PTR DS : 20] Пример. Выражения в Турбо Ассемблере В области действия описания Fixed DW 13 выражение [WORD PTR DS: (OFFSET Fixed)] имеет значение 13. Редактирование Независимо от выбранного состава после каждого из выражений может присутствовать эталон, определяющий способ высвечивания значения,
392 Часть V. Турбо отладчик представленного выражением. Эталон отделяется от выражения знаком , (.запятая) и может иметь один из следующих видов: Эталон Интерпретация с d tn Вывод символа Вывод десятичного числа Вывод действительного числа с п цифрами (отсутствие п вызывает вывод всех цифр) m md P cs Вывод шестнадцатеричного адреса Вывод десятичного адреса Вывод указателя или значения указательной переменной Вывод строки из с символов (отсутствие с вызывает вывод строки, заканчиваемой знаком пиГ) X h Вывод шестнадцатеричного числа й Вывод шестнадцатеричного числа 16.6. Опции меню Выбор опции начинается с главного меню. Исключением из этого правила является вызов на экран локального меню или использование ускоренного выбора опции с помощью управляющих клавиш. В этом разделе будут представлены опции главного меню, опции глобальных меню и опции локальных меню. Многие из них могут быть выбраны с помощью управляющих клавиш. Хотя точное их описание будет указано несколько дальше, специально приводится их полный комплект Клавиши Функции F1 Высвечивание справочной информации F2 Расстановка ловушек F3 Закрытие окон F4 Выполнение до курсора F5 Увеличение окон до размера экрана F6 Высвечивание очередного окна F7 Выполнение одной инструкции/команды F8 Выполнение покомандное, за исключением процедур F9 Выполнение до останова F10 Активизация главного меню Alt-Fl Высвечивание предыдущей справочной информации Alt-F2 Установка адресной ловушки Alt-F3 Выбор базового модуля Alt-F4 Начало выполнения Alt-F5 Высвечивание выходного экрана A11-F6 Высвечивание закрытого окна AU-F7 Выполнение одной инструкции AU-F8 Выполнение до возвращения из процедуры Alt-F9 Выполнение до указанного адреса Alt-FlO Высвечивание локального меню Alt-0 Высвечивание списка открытых окон Alt-1 . . 9 Активизация окон с указанным номером
16. Отладчик 393 AU-F Att-V Alt-R Alt-B Alt-D AU-W Alt-O AU-X Высвечивание глобального меню File Высвечивание глобального меню View Высвечивание глобального меню Run Высвечивание глобального меню Breakpoints Высвечивание глобального меню Data Высвечивание глобального меню Windows Высвечивание глобального меню Options Возврат в систему DOS Alt-~ Alt— Начало макроопределения Окончание макроопределения Ctrl-F2 Ctrl-F4 Ctrl-F7 Ctrl-F8 Ctrl-F9 Ctrl-FlO Подготовка к слежению от начала Задание значения выражения Помещение переменной в окне обзора (смотровом) Установка/снятие ловушки Выполнение до останова Высвечивание локального меню Ctrl—> Ctrl-<- Увеличение адреса на 1 Уменьшение адреса на 1 Esc Ins Tab Shift-Tab ScroU-Lock Закрытие инспекционного окна или меню Выделение выражения Перемещение курсора в следующую рамку Перемещение курсора в предыдущую рамку Изменение конфигурации окна ГЛАВНОЕ МЕНЮ File Меню File состоит из опций, позволяющих, кроме прочего, загружать программу, выбирать каталоги, а также выполнять директивы системы DOS. Опция Load, Выбор опции Load вызывает загрузку программы, которая должна быть запущена. Если имя файла, содержащего программу, неоднозначно (содержит знак * или ?>, то нажатие клавиши Enter вызывает вывод на экран списка имен файлов, среди которых можно произвести выбор. Опция Change Dir \ Выбор опции Change Dir вызывает смену текущего каталога. \ Опция Get Info Выбор опции Get Into высвечивает информацию о программе, в частности: ее имя; размер памяти, занятой программой, отладчиком и DOS;
394 Часть V. Турбо отладчик список номеров ловушек, установленных отладчиком, способы обслуживания ловушек (аппаратно или программой) и т. д. Опция OS Shell Выбор опции OS Shell вызывает временный вызов системы DOS. После окончания действий в системе DOS можно вернуться к отладчику, выполнив директиву EXIT. Опция Quit Выбор опции Quit вызывает возврат в систему DOS. View ; Меню View состоит из опций, позволяющих высвечивать информационные окна. Эти окна делятся на рамки. Перемещение между рамками происходит в спомощью клавиши Tab и Shift-Tab, а перемещение в рамке - с помощью клавиш с горизонтальными стрелками. В момент, когда активно информа- ционное окно, в него можно заносить символы с клавиатуры. Это вызывает появление диалогового окна, соответствующего данному контексту. Прак- тически исключается такая возможность для отдельных окон и их рамок. Опция Breakpoints Выбор опции Breakpoints вызывает высвечивание и активизацию окна ловушек. Это окно состоит из двух рамок. Левая содержит список ловушек, а в правой высвечивается информация о выбранной ловушке. Ловушки могут ставиться в меню Breakpoints или с помощью клавиши F2, а затем модифицируются с помощью локального меню. Локальное меню окна ловушек может касаться только его левой рамки. Как любое локальное меню оно высвечивается нажатием клавиш Alt-FlO или Ctrl-FlO. Локальное меню Set action Condition Определение действий после попадания программы в ловушку Break останов программы; Log помещение значения выражения в окно сообщений; Execute обработка указанного выражения; Определение условия попадания в ловушку Always всегда; Changed memory после внесения изменения в указанной области памяти (например, если в области действия описания: в Турбо Си long int Fix = 13;
16. Отладчик 395 Pass count... Enable/disable Add... Global Remove Delete all Inspect в Турбо Паскале const Fix : longint -= 13; в Турбо ассемблере Fix DD 13 указывается Fix, 4, это означает определение области в 16 байт, начиная с адреса переменной Fix); Выражение истинно всегда, когда указан ное выражение имеет значение TRUE или отличное от 0. Определение количества выполнений условия, после которого наступит попадание в ловушку. Активизация/дезактивизация ловушки. Добавление ловушки с указанным адресом в перечень ловушек (например, #44 перед первой инструкцией или командой строки 44). Включение ловушки в глобальный список. (устанавли вается перед каждой инструкцией программы). Снятие рассматриваемой ловушки. Снятие всех ловушек. Высвечивание того фрагмента программы, к которому относится данная ловушка. Опция Stack Выбор опции Stack вызывает высвечивание и активизацию окна вызовов. Оно состоит из одной .рамки, в которой находится список активных процедур в очередности их вызова. Локальное меню Inspect Открытие модульного окна, расположенного в месте вызова выбранной процедуры. Locals Открытие - окна переменных и высвечивание в нем локальных переменных текущего модуля, а также локальных переменных выбранной процедуры (в случае рекуррентных вызовов высвечивание локальных переменных выбранного уровня вызова). Опциякод Выбор опции Log вызывает высвечивание и активизацию окна сообщений. Это окно состоит из одной рамки, в которой находится перечень последних сообщений (обычно 50 строк), направленных в это окно. Сообщения могут быть помещены в окно в результате попадания программы в ловушку, регистрации комментария, а также копирования содержимого активной рамки выбранного окна. Локальное меню Open log file Вывод сообщений не только в окно сообщений, но также в файл с указанным именем (после предвари-
396 Часть V. Турбо отладчик Close log file Logging Add comment Erase log тельного помещения в этом файле всех сообщений, уже находящихся в указанном окне). Закрытие файла, в который выведены сообщения и дальнейший вывод сообщений только в окно. Включение/выключение регистрации сообщений. Вывод указанного комментария в окно сообщений. Очищение окна сообщений (может оказаться полезным перед выбором опции Open log file). Опция Watches Выбор опции Watches вызывает высвечивание и активизацию окна наблюдений. Оно состоит из одной рамки и вместе с модульным окном высвечивается сразу же после активизации отладчика. В окне наблюдений помещаются вьцражсния, наблюдаемые при выполнении программы. Если выражение является именем структурной переменной, то окно содержит определение типа переменной и значение этой переменной или ее компонент. В остальных случаях окно содержит только определение типа и значение. Если выражение указательное, то приводится информация об указываемой переменной. Выражения помещаются в окно наблюдений с помощью клавиш Ctrl-F7 (после выделения с помощью клавиши Ins и клавиш с горизонтальными стрелками), а также из локального меню. В случае совпадения идентифика- торов предполагается, что речь идет об идентификаторах, находящихся в пункте выполнения программы. Локальное меню Watch Помещение указанного имени переменной или выражения в окно наблюдений. Edit Редактирование наблюдаемого выражения или имени переменной. Remove Извлечение из окна выделенного наблюдаемого объекта. Delete all Inspect Извлечение из окна всех наблюдаемых объектов. Открытие инспекционного окна для ознакомления с компонентами сложного объекта. Change Изменение значения выбранной переменной. Опция Variables Выбор опции Variables вызывает высвечивание и активизацию окна переменных. Оно состоит из двух рамок, в которых высвечиваются переменные, доступные в месте останова программы. В левой рамке высвечиваются общие переменные, а в правой - локальные переменные. Локальное меню Inspect Открытие инспекционного окна для ознакомления с компонентами сложного объекта. Change Изменение значения выбранной переменной.
76. Отладчик 397 Опция Module Выбор опции Module вызывает высвечивание списка имен модулей, из которых можно произвести выбор. После выполнения этой операции происходит высвечивание и активизация модульного окна, содержащего текст выбранного модуля. Это окно состоит из одной рамки и вместе с окном наблюдений высвечивается непосредственно после активизации отладчика. Поиск модуля с указанным именем происходит в следующей последовательности: 1) в каталоге, в котором компилятор обнаружил запускаемую программу; 2) в каталогах, определенных с помощью опции Options/Path for Source; 3) в текущем каталоге; 4) в каталоге, в котором находится собственно запускаемая программа. Локальное меню Inspect Watch Module File Previous Line Search Next Origin Goto Edit Открытие инспекционного окна для ознакомления с объектом, выделенным курсором или для определения значения выражения, выделенного с помощью клавиши 1ns и клавиш горизонтальных стрелок. Помещение переменной, выделенной курсором в окно наблюдений. Высвечивание в модульном окне другого модуля, выбранного из приведенного перечня. Высвечивание в модульном окне той части модуля, которая находится в другом файле. Перемещение курсора на позицию, которую он занимал перед последней сменой позиции. Перемещение курсора на первый символ строки с указанным номером. Поиск цепочки, состоящей из указанных символов. Если курсор находится под идентификатором или если с помощью клавиши 1ns выделена группа символов, то как искомый текст принимается идентификатор или группа символов. Если среди указанных символов имеются знаки * (звездочка) или ? (знак вопроса), то изображаются соответственно: произвольный ряд символов и произвольный символ. Поиск ряда симво- лов начинается с позиции курсора до конца модуля. Поиск идентичного ряда символов, указанного во время последнего выбора опции Search. Перемещение курсора на позицию, в которой произо- шел останов выполнения программы. Если необходимо, загрузка в модульное окно другого модуля. Перемещение курсора на указанную позицию. Позиция может быть определена с помощью номера строки (например, #44), имени процедуры или адре- са, выраженного шестнадцатеричным числом. Активизация редактора для редактирования текста, высвеченного в модульном окне (команда системы DOS, вызывающая редактор, должна быть определена во время установки отладчика).
398 Часть V. Турбо отладчик Опция File Выбор опции File вызывает высвечивание и активизацию окна файлов. Оно состоит из одной рамки, в которой высвечивается содержимое указанного файла в символьном или шестнадцатеричном виде. Переключение между этими двумя видами обеспечивает опция Display As, выбранная из локального меню (ее ускоренной версией является Ctrl-D). Локальное меню Goto Seach Next Display as File Edit Перемещение курсора на позицию с указанным номером. Если содержимое файла высвечивается в виде символов, необходимо указать номер строки, а если в шестнадцатеричном виде - номер байта. Поиск цепочки, состоящей из указанных символов. Если курсор находится под идентификатором или еии с помощью клавиши Ins выделен ряд символов, то в качестве искомого текста соответственно принимаются идентификатор или ряд символов. Если в указанном ряду символов находятся знаки * (звездочка) или ? (знак вопроса), то изображаются соответственно: произвольный ряд символов и произвольный символ. Поиск ряда символов происходит с позиции курсора до конца модуля. Поиск идентичного ряда символов, указанного во время последнего выбора опции Search. Переключение между высвечиванием файла в символьном и шестнадцатеричном видах. Высвечивание в окне файлов содержимого другого файла. Если указанное имя файла определено неоднозначно, то имеется возможность выбрать нужный файл из приведенного перечня. Если файл высвечивается в символьном виде - активизация редактора для редактирования текста, высвечиваемого в модульном окне (команда системы DOS, вызывающая редактор, должна быть определена во время установки отладчика). Если файл высвечивается в шестнадцатеричном виде - замена выделенных курсором байтов байтами, введенными с клавиатуры. Опция CPU Выбор опции CPU вызывает высвечивание и активизацию окна процессора. Окно состоит из пяти рамок: кода (Code), регистров (Register), флажков (Flags), стека (Stack) и данных (Data). Рамка Code Содержит инструкции базовой программы и команды ассемблера. Способ их высвечивания зависит от локальной опции Mixed. Общие символы имеют вид идентификаторов. Остальные символы состоят из имени модуля, после которого следует знак # (hash) или . (точка) и идентификатор. Номера
16. Отладчик 399 строк состоят из имени модуля, после которого следует один из упомянутых знаков, и числа, определяющего номер строки в модуле. Адреса команд высвечиваются в виде символов. Если выделяется команда, выполнение которой вызовет скачкообразное изменение счетчика команд, то такая команда дополняется стрелкой, определяющей направление перехода. Локальное меню рамки Code Goto Origin Follow Caller Previous Search Mixed New CS: IP Assemble I/O Высвечивание кода в указанном адресе (возврат обеспечивает опция Previos). Высвечивание кода в адресе останова программы ' (CS: IP). Высвечивание кода в адресе команды перехода (JMP, CALL и т.д.). Высвечивание кода в адресе, откуда вызвана данная процедура. Высвечивание кода в месте, в котором использовалась в последний раз опция смены адреса. Поиск указанной команды. или ряда указанных байтов Переключение между высвечиванием инструкций программы и команд ассемблера в рамке кода No высвечивание только команд; Yes высвечивание инструкций и их кода на ассемблере (предполагаемый вид в случае, если текущий модуль написан на языке высокого уровня); Both высвечивание инструкции и только тех команд, которые не являются развитиями инструкции. Изменение счетчика команд (CS: IP) таким образом, чтобы следующей выполняемой командой была команда с указанным адресом. Замена текущей команды командой, введенной с клавиатуры. Выполнение операции в порту входа/выхода In byte ввод данных из байтового порта с указанным адресом; Out byte вывод данных в байтовый порт; Read word ввод слова данных из порта с указанным адресом; Write word вывод данных в порт с указанным адресом. Рамка Reg Рамка содержит i s t е г перечень регистров процессора и их содержимое. Локальное Increment Decrement Zero Change меню рамки Register Увеличение содержимого выделенного регистра на 1. Уменьшение содержимого выделенного регистра на 1. Обнуление выделенного регистра. Изменение содержимого выделенного регистра.
400 Часть V. Турбо отладчик Registers 32-b Переключение между высвечиванием как 16-, так и 32-бит регистров (это имеет значение только в случае использования микропроцессора семейства 386). Рамка Flags Рамка содержит значения разрядов регистра флажков процессора. Локальное меню рамки Flags Toggle Изменение значения выделенного разряда регистра флажков с 0 на 1 и наоборот. Рамка Stack Рамка содержит список данных, находящихся в стеке процессора. Верхушка стека обозначена стрелкой. Локальное меню рамки Stack Goto Высвечивание стека по указанному адресу (преобразование предыдущего адреса обеспечивает опция Previous). Origin Высвечивание стека и его верхушки. Follow Высвечивание стека в выделенной позиции. Previous Высвечивание той же области стека, которая высвечи валась перед выбором последней опции изменения адреса. Change Изменение значения выделенной позиции стека (значение может быть введено непосредственно без явного выбора опции Change). Рамка Data Рамка содержит данные, находящиеся в выбранной области оперативной памяти. Данные высвечиваются в виде, определенном с помощью опции Display As. В правой части рамки высвечиваются символы, находящиеся в выбранной области памяти. Если сегментная часть адреса совпадает с содержимым регистра DS, то в качестве номера сегмента указывается DS. Локальное меню рамки Data Goto Высвечивание данных по указанному адресу. Адрес не может находиться в пределах отлаживаемой программы. Search Поиск адреса, по которому находится указанный ряд байтов. Next Высвечивание адреса, в котором находится следующий ряд таких байтов, какие указаны в опции Search. Change Изменение значения выделенной данной (значение может быть введено непосредственно без явного
16. Отладчик 401 Follow Long follow Previous Display as Block выбора опции Change). Высвечивание данных по адресу DS : addr, в котором addr является содержимым выделенного слова оперативной памяти Высвечивание данных по адресу seg : Ofs составляет пару последовательных слов, из которых первое выделено. Высвечивание той же самой области памяти, какая высвечивалась перед выбором последней опции смены адреса. Определение способа представления данных выбранной области памяти. Byte в виде целого числа типа Char или byte; Word в виде целого числа типа int или word; Long в виде целого шестнадцатеричного числа (long, longint); Comp в виде действительного числа типа comp; Float в виде действительного числа типа float или single; Real в виде действительного числа типа real; Double в виде действительного числа типа double или TBYTE; Extended в виде действительного числа типа long double или extended. Выполнение операции на блоке оперативной памяти с указанным адресом и размером Clear обнуление блока; Move копирование блока; Set однородное заполнение блока; Read ввод данных из файла с указанным именем; Write вывод блока в файл с указанным именем. Опция Dump Выбор опции Dump вызывает высвечивание и активизацию окна памяти. Оно состоит из той же рамки, что и рамка Data окна CPU. К нему относится такое же локальное меню, состоящее из следующих полей: Goto Search Next Change Follow Long follow Previous Display as 26—764
402 Часть V. Турбо отладчик Byte Word Float Real Block Read Write Long Comp Double Extended Опцию Dump удобно использовать при вызове инспекционного окна для изучения рассматриваемого объекта. Опция Registers Выбор опции Registers вызывает высвечивание и активизацию окна регистров и флажков. Оно состоит из двух рамок, идентичных с рамками регистров окна.. CPU. К нему относится то же самое локальное меню, состоящее из следующих полей: Increment Toggle Decrement Zero Change Registers-32-bit Опция Numeric Processor Выбор опции Numeric Processor вызывает высвечивание и активизацию окна сопроцессора. Оно состоит из трех рамок: рамки регистров (Register), управляющей рамки (Control) и рамки флажков (Flags). Заголовок окна информирует об использовании сопроцессора или эмулятора. Указанный в нем адрес указывается в 20-бит виде (например, адрес ЗАООВ равнозначен нормализованному адресу ЗАОО:ОООВ). IPTR означает адрес инструкции, OPCODE - вид инструкции, a OPTR - адрес памяти, которого касается инструкция. Рамка Register Рамка содержит перечень регистров сопроцессора и их содержимое. Локальное меню рамки Register Zero Обнуление выделенного регистра. Change Изменение значения выделенного регистра (значение может быть введено непосредственно, без явного выбора опции Change). Рамка Control Рамка содержит значение флажков управляющего слова сопроцессора. Локальное меню рамки Control Toggle Изменение состояния выделенного флажка.
16- Отладчик 403 Рамка Flags Рамка содержит значения флажков слова состояния сопроцессора. Локальное меню рамки Flags Toggle Изменение состояния выделенного флажка. Опция User Screen . Выбор опции User Screen вызывает высвечивание и активизацию выходного окна. Это окно имеет такой же вид, какой имел бы экран монитора, если бы программа была вызвана из системы DOS. С целью ускоренного высвечивания выходного окна необходимо нажать клавиши Alt- F5. Высвечивание выходного окна исчезает после нажатия произвольной клавиши клавиатуры. Выходное окно не имсс’'’ '>т!0го меню. Опция Another Выбор опции Another вызывает высвечивание и активизацию дополнительного окна: модульного (Module), памяти (Dump) или файла (File). В частности, использование опции Another позволяет одновременно высвечивать содержимое более чем одного файла. RUN Меню RUN состоит из опций, позволяющих следить за программой и выполнять ее до останова. Опция RUN Выбор опции RUN (клавиша F9) вызывает выполнение программы до останова. Вызов отладчика наступит в одном из следующих случаев: 1) после окончания выполнения программы; 2) после попадания программы в ловушку; 3) после нажатия клавиши Ctrl-Break. Опция Reset Выбор лщии Reset (клавиши Ctrl-F2) вызывает новую загрузку про- граммы с лека. Поскольку после выполнения этого действия не изменяется содержим;' модульного окна, можно, например (после перемещения курсора и нажат», клавиши F4), вызвать выполнение программы до места, пред- шествующего тому, в котором выбрана данная опция. Опция Go to cursor Выбоо опции Go to cursor (клавиша F4) вызывает установку временной ловушки в строке, выделенной курсором и начало выполнения программы до останова. 26*
404 Часть V. Турбо отладчик Опция Trace into Выбор опции Trace into (клавиша FT) вызывает выполнение одной базовой строки или одной команды. Выполнение команды происходит, если текущим окном является окно процессора (CPU). Если базовая строка содержит вызовы процедур, то слежение охватывает их содержимое. Опция Step over Выбор опции Step over (клавиша F8) вызывает выполнение одной базовой строки или одной команды. В отличие от опции Trace into слежение не охватывает вызванных процедур. Модификация касается также слежения некоторых команд, в частности команд CALL и INT и команд LOOP, LOOPZ, LOOPNZ (со счетчиком в регистре СХ), а также команд, предваренных префиксами REP, REPZ и REPNZ. Опция Execute to Выбор опции Execute to (клавиши Ali-F9) вызывает установку временной ловушки по указанному адресу и начало выполнения программы. Опция Until, return Выбор опции Until return (клавиши AU-F8) вызывает установку временной ловушки перед инструкцией, которая была бы выполнена по окончании выполнения текущей процедуры, а затем начало выполнения программы до останова. Опция Animate Выбор опции Animate (клавиши AU.-F4) вызывает возобновление выполнения программы до останова. Выполнение программы можно прервать нажатием произвольной клавиши клавиатуры. Опция Instruction trace Выбор опции Instruction trace (клавиши Alt-F7) вызывает выполнение одной команды. Эта опция позволяет следить за подпрограммами обслуживания прерываний и модулей, компилированных без опции, допускающей отладку программы. BREAKPOINTS Меню Breakpoints состоит из опций, позволяющих устанавливать и снимать ловушки. Модифицирование предполагаемых свойств ловушек происходит в окне ловушек. Опция Toggle Выбор опции Toggle (клавиша F2) вызывает установку ловушки перед
16. Отладчик 405 первой инструкцией базовой строки или перед выделенной командой ассемблера. Опция At... Выбор опции At... (клавиша A11-F2) вызывает установку ловушки по указанному адресу. Опция Changed memory global... Выбор опции Changed memory global... вызывает установку глобальной ловушки, в которую попадет программа, когда произойдет изменение значения указанной области памяти. Область памяти определяется указанием имен переменной и размера. Например, если в области действия описания: в Турбо Си long int Агг [10]; в Турбо Паскале var Агг : array [0..9] of longint; в Турбо Ассемблере Arr DD 10 DUP (?) указывается Агг,3 то это будет определять область в 120 байт, начиная от первого байта массива Агг. Опция Expression true global... Выбор опции Expression true global... вызывает установку глобальной ловушки, в которую попадет программа, если указанное выражение будет иметь значение, отличное от 0 или TRUE. Опция Delete all Выбор опции Delete all вызывает снятие всех поставленных ловушек. DATA Меню Data состоит из опций, позволяющих знакомиться со значениями переменных выражений, а также присваивать переменным новые значения.
406 Часть V. Турбо отладчик Опция Inspect Выбор опции Inspect вызывает высвечивание инспекционного окна, касающегося переменной с указанным именем. Если в момент выбора этой опции курсор выделяет идентификатор переменной или если с помощью клавиши Ins и клавиш со стрелками выделено определенное выражение, то их значение выдается на экран. Опция Evaluate/modify Выбор опции Evaluate/modify (клавиши Ctrl-F4) вызывает высвечивание вспомогательного окна, состоящего из трех рамок. Если в первой рамке ука- зано выражение, то во второй высвечено его значение (это значение можно отредактировать, отделяя отредактированный образец от выражения точкой с запятой). Если указанное выражение является именем переменной, то в третьей рамке можно определить новое значение этой переменной. Как и в случае опции Inspect, могут быть выделены идентификатор переменной или выражение, выделенное с помощью клавиши Ins и клавиш горизонтальных стрелок. Опция Watch Выбор опции Watch (клавиши Ctrl-F7) вызывает помещение указанного идентификатора или выражения в окно наблюдений. Опция Function return Выбор опции Function return выдает результат функции. Опция может быть выбрана только перед окончанием выполнения функции. WINDOW Меню Window состоит из опций, позволяющих выполнять операции на окнах без вникания в содержимое окна. Опция Window pick Выбор опции Window pick (клавиши Alt-O) вызывает высвечивание перечня всех открытых окон. Выбор одной из позиций этого перечня делает выбранное окно активным. Опция Next рапе Выбор опции Next рапе (клавиши Alt-Tab) вызывает выделение следующей рамки активного окошка. Опция Move/resize Выбор опции Move/resize (клавиши Alt-Scroll-Lock) вызывает изменение конфигурации активного окна (изменение его размера и положения).
16. Отладчик 407 Опция Close Выбор опции Close (клавиша F3) вызывает закрытие активного окна. Опция Undo close Выбор опции Undo close (клавиши Alt-F6) вызывает открытие последнего закрытого окошка. Опция Dump pane to log Выбор опции Dump pane to log вызывает копирование активной рамки в окно сообщений. Опция Restore standard Выбор опции Restore standard вызывает преобразование стандартного размещения окна. Опция Screen repaint Выбор опции Screen repaint вызывает обновление экрана. OPTIONS Меню Options состоит из опций, позволяющих изменить некоторые особенности отладчика. Опция Language Выбор опции Language позволяет выбрать язык, на котором будут записаны выражения. Опция Macros Выбор опции Macros позволяет вызывать и аннулировать макровызовы, приписанные специально выбранным клавишам. Create Начало регистрации макровызова после предварительного указания специальной клавиши, определяющей ее имя. Регистрация начинается до момента выбора опции Stop Recording. С этого момента нажатие специальной клавиши (например, Ctrl-Del) вызывает такой же результат, что и поочередное нажатие всех клавишей, зарегистрированных между Create (клавиши АЙ-=) и Stop Recording (клавиши Alt—). Stop Recording Окончание регистрации макровызова. Remove Аннулирование макровызова, связанного с указанной специальной клавишей.
408 Часть V. Турбо отладчик Delete all Аннулирование всех макровызовов, связанных со специальными клавишами. Опция Environment Выбор опции Environment позволяет определить способ высвечивания данных. Integer format Выбор формата записи целых данных Decimal десятичный; Hex шестнадцатеричный; Both десятичный и шестнадцатеричный. Display swapping Выбор способа переключения между экраном отладчика и выходным экраном <t None без переключения; Smart только тогда, когда можно ожидать экранной операции; Always всегда. Screen size Выбор максимально допустимого для данной платы горизонтального размера экрана (43 - EGA, 50 - VGA). Tab Size » Выбор размера табуляции. Опция Path for source Выбор опции Path for source вызывает указание каталогов, в которых будут разыскиваться базовые модули. Эти модули разыскиваются в следующей последовательности: 1) в каталоге, в котором компилятор нашел базовую программу; 2) в каталогах, определенных с помощью опций; 3) в текущем каталоге; 4) в каталоге, содержащем отлаживаемую программу. Опция Arguments Выбор опции Arguments позволяет указывать аргументы программы. Опция Save options Выбор опции Save options вызывает запоминание параметров отладчика в файле с указанным именем (в том числе его опции и макровызовы, а также способ размещения окон и рамок). Опция Restore options Выбор опции Restore options вызывает установку параметров отладчика, запомненных после выбора опции Save options.
ЧАСТЬ VI. БИБЛИОТЕКИ 17. Принципы описания В общем случае элементами приведенных здесь описаний являются: Наименование Описание Имя Заголовок Имя и краткое описание функции. Тип результата функции, а также типы и имена ее параметров. Прототип Имя файла заголовков, содержащего описание прототипа функции. Определение Имя файла заголовков, содержащего макроопределение функции. Функция Подробное описание действий, к которым приводит вызов функции Результат Замечания Пример Переменная, выдаваемая в точке вызова функции Комментарии и уточнения. Простая программа, содержащая вызов функции и написанная для IBM PC XT с операционной систе- мой DOS 3.30 и оперативной памятью 640 Кбайт. Замечание: Только немногие функции (например, abs) являются одновременно собственно функциями и макроопределениями. С целью присоединения к программе собственно функции следует воспользоваться директивой, сужающей область макроопределения (например, fundef abs). 17.1. Графическая библиотека Графическая библиотека состоит из двух частей: - библиотека для реализации текстовой графики, содержащая функции, прототипы которых находятся в файле conio.h: clreol gettext highvideo movetext textattr textmode clrscr gettextinfo insline norm video textbackground wherex window delline gotoxy lowvideo putt ext textcolor wherey
410 Часть VI. Библиотеки - графическая библиотека для реализации точечной графики, содержав функции, прототипы которых находятся в файле graphics, h: arc circle closegraph ellipse floodfill getbkcolor getdrivername getgraphmode getmaxcolor getmaxy getpalette gettextsettings gety graphresult installuserdriver linerel moveto pieslice rectangle restorecrtm&de setall palette setcolor setgraphbufsize setpalette settexstyle setvisualpage bar cleardevice detectgraph fillellipse getarccoord getcolor getfillpattem getimage getmaxmode get modename getpalettesize getviewsettings graphdefaults imagesize installuserfont lineto outtext putimage registerbgidriver sector setaspectratio setfillpattern setgraphmode sergbpalette setusercharsize setwritemode textwidth bar3d clearviewport drawpoly fillpoly getaspectratio getdefaultpalette getfillsettings getlinesettings getmaxx getmoderange getpixel getx grapherrormsg initgraph line moverel outtextxy putpixel registerbgifont setactivepage setbkcolor setfillstyle setlinestyle settextjustify setviewport textheight arc Имя Заголовок arc - вычерчивание дуги окружности, void arc(int х, int у, int StAngle, int EndAngle, int Radius) Прототип Функция graphics.h Вычерчивание дуги окружности с центром i точке (х, у) радиусом Radius, начиная с угла StAngle и заканчивая углом EndAngle. Замечания Система должна находиться в графическом режиме. Углы StAngle и EndAngle выражаются в градусах. Вычерчивание ведется против часовой стрелки. Для StAngle==D и EndAngle==360 вычерчивается полная окружность.
17. Принципы описания 411 Пример Вычерчивание дуги окружности с радиусом в 50 точек и центром, размещенным вблизи центра экрана. /* Аге */ ♦include «graphics.h> ♦include «conic.h> main( ) { int Driver, Mode; Driver = DETECT; 1nitgraph(&Driver, &Mode, ". .”); if(graphresult( ) ! = grOk){ cprintf("FAILURE"), exit(l); } arc(getmaxx( ) /2, getmaxyO/2,45,315,50); while(! kbhit( )); close graph( ); return 0; } bar Имя Заголовок bar - вычерчивание столбика, void bar(int xl, int yl, int x2, int y2) Прототип Функция graphics.h Вычерчивание столбика (составляющего, например, элемент гистограммы) в виде закрашенного прямоугольника, противоположные по диагонали вершины которого имеют координаты (xl, у!) и (х2, у2). Замечания Система должна находиться в графическом режиме. Шаблон и цвет закрашивания прямоугольника могут быть указаны с помощью функций setfillstyle или setfillpattern. Призер Вычерчивание столбика по произвольно выбранному шаблону. /* Ваг */ ♦include «graphics.h> ♦include «conio.h» char Pattern[]={"\x07\xdd\x07\x72\x50\x72\x82"}; main ( )
412 Часть VI. Библиотеки { int Driver, Mode; Driver = DETECT; 1nitgraph(&Driver, &Mode, ". ."); if(graphresult( ) 1 = grOk)) exit(l); setfillpattern(Pattern, getmaxco1or( )); i bar(50, 50 getmaxx( ) - 50, getmaxy( ) - 50); while(l kbhit( )); close graph( ); return 0; } bar 3d r* Имя Заголовок Прототип * Функция bar 3d - вычерчивание трехмерного столбика, void bar3d(int xl, int yl, int x2, int y2) int Depth, int TopFlag) graphics.h Вычерчивание трехмерного столбика (составляющего, например, элемент гистограммы) в виде прямоугольно го параллелепипеда, передняя поверхность которого представляет собой закрашенный прямоугольник. Противоположные по диагонали вершины прямоуголь ника определены как точки с координатами (xl, у2) и (х2, у2). Глубина параллелепипеда задается с помощью Depth, а необходимость вычерчивания его верхней поверхности указывается с помощью TopFlag. Система должна находиться в графическом режиме. Шаблон и цвет закрашивания прямоугольни ка могут быть указаны с помощью функций setfillstyle или setfillpattern. Оставляя невычерченной верхнюю поверхность, мы получаем возможность вычерчивать столбик, состоящий из нескольких кубиков. Неудачное выполнение функции bar 3d приводит к тому, что результатом функции graphresult является переменная со значением grNoScanMem - не хватает памяти для закрашивания области методом сканирования. Пример Вычерчивание трехмерного столбика глубиной в 20 точек, с видимой верхней поверхностью, закрашенного по одному из стандартных шаблонов. /* Bar3D */ #include «graphics.h>
17. Принципы описания /include «conio.h» /define TopOn 1 main( ) { int Driver, Mode; Driver = DETECT; initgraphf&Driver, &Mode, ". ."); if(graphresult( ) I “ grOk) exit (1); setfi11style(XHATCH_FILL, getmaxcolor( )); bar3d(50, 50, getmaxx( ) - 50, getmaxy( ) - 50, 20, TopOn); while(! kbhit( )); dosegraph() return 0; } circle Имя Заголовок circle - вычерчивание окружности, void circ1e(int х. int у, int Radius) Прототип Функция graphics.h Вычерчивание окружности с центром в точке (х, у) и радиусом Radius. Замечания Система должна находиться в графическом режиме. Пример Вычерчивание окружности, расположенной вблизи центра экрана. /* Circle */ /include «graphics.h> /include «conio.h* int Radius = 80; main( ) { int Driver, Mode; Driver = DETECT; 1nitgraph(&Driver, &Mode, ”. .”); if(graphresult( ) ! = grOk) exit(l); circle(getmaxx( ) >>1, getmaxy( ) - »1,Radius); while(! kbhit( )); closegraph( ); return 0; }
414 Часть VI. Библиотеки cleardevice Имя Заголовок cleardevice - очистка графического эрана. void cleardevice(void) Прототип Функция graphics.h Очистка графического экрана и установка графического курсора в левом верхнем углу текущего графического окна. Замечания Система должна находиться в графическом режиме. Пример Вывод в левом верхнем углу экрана надписи Kaja и в центре экрана надписи Izabela, а после нажатия любой клавиши - удаление надписи Kaja и замена надписи Izabela надписью Kajusia. /* ClearDevice */ /include «graphics.h> /include «conic.h> /define ClipOn 1 mainf ) { int Driver, Mode; Driver = DETECT; initgraphf&Driver, &Mode, ". if(graphresult( ) 1 = grOk) exit (1); outtext("Kaje”); setviewport(getmaxx( )/2, getmaxy( )/2; (getmaxx( outtextf’Izabela'’); getmaxy( ),ClipOn); while(! kbhit( )); (void)getch( ); deardevice( ); outtextf’Kajusia"); while(i kbhit( )); closegraph( ); return 0; } clearviewport Имя Заголовок clearviewport - очистка графического окна, void clearviewport Прототип Функция graphics.h Очистка текущего графического окна, а затем закрашивание его цветом,
17. Принципы описания 415 соответствующим первой позиции палитры. Замечания Система должна находиться в графическом режиме. Неудачное выполнение функции clearvtewport приводит к тому, что результатом функции graphresult является переменная со значением grNocanMem - не хватает памяти для закрашивания области методом сканирования. Пример Определение графического окна, вычерчивание в нем столбика, а после нажатия произвольной клавиши - очистка текущего окна и вывод в нем надписи Viewport cleared. /* ClearVlewPort */ ♦include «graphics.h> ♦Include «conio.h» ♦define CllpOn 1 struct vtewporttype Viewport; main( ) { lot Driver, Mode; Driver * DETECT; initgraph(&Drtver, &Mode, ". ."); if(graphresu1t( ) I = grOk) exit(l); setvieport(50, 50, getmaxx( ) — 50, getmaxy( ) - 50, CllpOn); getv1ewsett1ngs(&V1ewPort); bar(ViewPort, left,- ViewPort, top, ViewPort. right. Viewport, bottom); while(! kbhit( )); (void)getch( ); dearviewport( ); outtext("ViewPort cleared"); wh11e(! kbh1t( )); closegraph( ); return 0; } closegraph Имя Заголовок Прототип Функция closegraph - восстановление текстового режима, void closegraph graphics.h Восстановление текстового режима, очистка памяти, предоставленной автоматически программе графического драйвера и выбранному виду шрифта.
416 Часть VI. Библиотеки Замечания Система должна находиться в графическом режиме. Память, выделенная с помощью функции registerbgidriver и registerbgifont, не подлежит очистке. Пример Вычерчивание огибающей экрана, а после нажатия произвольной клавиши клавиатуры - восстановление графического режима /* Closegraph */ ♦include «graphics.h> ♦Include <con1o.h> main ( ) { int Drived, Mode; Int Hor, Ver; extern, void Box(int x, Int y); Driver - DETECT; 1nitgraph(&Driver, &Mode, ". ."); 1f(graphresult( ) ! = grOk) exit(l); Hor = getmaxxf ); Ver = getmaxy( ); Box(Hor, Ver); whi1e(l kbhit( )); dosegraph( ); return 0; } void Box(int x, int y) { 1ine(0, 0, x, 0); 11ne(x, 0, x, y); 11ne(x, y, x, y); 11ne(0, y, 0, 0); } clreol Имя Заголовок Прототип Функция Замечания clreol - удаление символов в конце строки, void clreol(void) conio.h Замена пробелами тех знаков в строке, которые расположены между курсором и концом строки. Система должна находиться в графическом режиме. Если цвет фона не является черным, то пробелы окрашиваются цветом, определяемым с помощью функции textbackground.
17. Принципы описания 417 Замена знаков пробелами заканчивается в момент достижения правого края текущего текстового окна. Положение курсора не изменяется. Пример Индикация двух имен и двух фамилий, а после нажатия клавиши Enter удаление фамилии, связанной с именем Е /* ClrEol */ ♦include «conic.h> ♦Include <stdio.h> main( ) { drscr( ); cprintf("Ewa Bielecka \n”); cprintf("Jan Bielecki \n"); while(getchar( )! = '\n'); gotoxy(5, 1); clreolj ); return 0; } clrscr Имя clrscr - очистка текстового окна. Заголовок void clrscr(void) Прототип conio.h Функция Очищает текущее текстовое окно и устанавливает знаковый курсор в левый верхний угол этого окна. Замечания Если цвет фона не является черным, то окно заполняется пробелами, цвет которых определяется с помощью функции textbackground. Пример Вывод надписи Kajusia, а после нажатия клавиши Enter замена ее надписью Izusia. /* ClrScr */ ♦include «graphics.h> ♦include «conic.h> ma1n( ) { clrscr( ); cprintf("Kajusia”); whi 1e(getchar( )1 = ‘\n'); clrscrf ); cprintfC'Izusia");
418 Часть VI. Библиотеки return 0; } delline Имя Заголовок delline - удаление строки, void dellinefvoid) Прототип Функция conio.h Удаление из текстового окна строки, указываемой курсором. Смещение строк, расположенных ниже удаляемой строки, на одну строку вверх. В нижней части окна Замечаний добавляется одна пустая строка. Если цвет фона не является черным, то пустая строка будет заполнена пробелами, цвет которых определяется функцией textbackground. Пример Вывод трех строк текста, а после нажатия произвольной клавиши удаление средней из этих строк. /* DelLine */ #include «conio.h» finclude «stdio.h» mainf ) { clrscrf ); cprintff'Ewa Bielecka \n"); cprintff'Iza Bielecki \n*'); cprintfC'Jan Bielecki \n”j; gotoxy(5, 2); whilefl kbhitf )); (void)getchf ); dellinef ); whilef! kbhitf )); return 0; detectgraph Имя Заголовок detectgraph - определение графической платы, void detectgraphfint * GraphDriver, int * GraphMode) Прототип Функция graphics.h Определение графического драйвера, а затем присвоение переменной, на которую указывает
17. Принципы описания 419 Замечания Вид драйвера DETECT CGA MCGA EGA EGA64 EGAMONO IBM8514 HERCMONO ATT 400 VGA PC3270 Вид режима CGACO CGAC1 CGAC2 CGAC3 CGAHI MCGAC MCGAC 1 MCGAC2 MCGAC3 MCGAME MCGAHI EGALO EGAHI EGA64LO EGA64HI EGAMONOHI HERCMONOHI ATT400C0 ATT400C1 ATT400C2 ATT400C3 ATT-100MED GraphDriver, значения, равного номеру драйвера, а переменной, на которую указывает GraphMode, значения, равного номеру предполагаемого графического режима. Значения, приписанные переменным, указываемым аргументами GraphDriver и GraphMode, могут быть использованы в функции 1 nitgraph для установления графического режима. На самом деле вызов этой функции с первым аргументом DETECT влечет неявный вызов функции detectgraph. Номера адаптеров и режимов определяются следующими константами: • • автоматическое определение графического режима - адаптер CGA - адаптер MCGA - адаптер EGA - адаптер EGA64 - адаптер EGAMONO - адаптер IBM8514 - адаптер Hercules - адаптер АТТ400 - адаптер VGA - адаптер РС3270 - 320x200, палитра 0 - 320x200, палитра 1 - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница - 320x200, палитра 0 - 320x200, палитра 1 - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница - 640x480, 1 страница - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x350, 16 цвета, 1 страница - 640x350, 4 цвета, 1 страница - 640x350, 64К на плате, 1 страница 256К на плате, 2 страницы - 720x348, 2 страницы - 320x200, палитра 0 - 320x200, палитра 1 - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница
420 Часть VI. Библиотеки ATT400HI VGALO VGAMED VGAHI PC3270HI IBM8514LO IBM8514HI - 640x400, 1 страниц - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x480, 16 цвета, 1 страница - 720x350, 1 страница - 640x480, 256 цветов - 1024x768, 256 цветов Неудачное выполнение функции detectgraph приводит к тому, что результатом функции graphresult является переменная со значением grNotDetected - отсутствует графический драйвер. Пример Определение графического драйвера и вывод его названия. /* DetectGraph */ ♦include <conio.h> ♦include «graphics.h> char ‘Drivers [ ] * {"GGA", "MCGA", "EGA", "EGA64", "EGAMono", IBM8514", "HercMono", "AT1400", "VGA", "PC3270"}; main( ) { int Driver, Mode; clrscr! ): detectgraph(&Dr1ver, &Mode); cprintf("%s", Drivers[Driver - 1]); return 0; drawpoly Имя Заголовок drawpoly - вычерчивание ломаной линии, void drawpoly!int NumPoints, Прототип Функция int ‘PolyPoints) Замечания graphics.h Вычерчивание ломаной линии, состоящей из точек NumPoints, координаты которых (х, у) поочередно перечисляются, а переменная, которой соответ ствует первая из этих точек, указана в аргументе PolyPoints. Система должна находиться в графическом режиме. Каждая пара координат задается двумя величинами типа (int). Для вычерчивания многоугольника следует задать последнюю пару координат идентичной первой.
17. Принципы описания 421 Пример Вычерчивание четырехугольника с произвольным образом выбранными вершинами. /* DrawPoly */ finclude «graphics.h> finclude «conio.h» struct pointtype Tetragonf ] « { {10. 10}. {80, 40}, {30, 70}. {40, 50}, {10, 10}}; main( ) { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". if(graphresult( ) I = grOk) exit(l); drawpoly(5, Tetragon); while(l kbhit( )); closegraph( ); return 0; } ellipse / Имя Заголовок ellipse - вычерчивание эллипса, void ellipse(int х, int у, int StAngle, int EndAngle, int xAxis, int yAxis) Прототип Функция graphics.h Вычерчивание эллиптической кривой с центром в точке (х, у) и полуосями xAxis и yAxis, начиная с угла StAngle и заканчивая углом EndAngle. Замечания Система должна находиться в графическом режиме. Углы StAngle и EndAngle заданы в градусах. Вычерчивание ведется против часовой стрелки. В случае StAngle==0 и EndAngle==360 вычерчивается полный эллипс. Пример Вычерчивание эллипса, расположенного в центре экрана. /* Ellipse */
422 Часть VI. Библиотеки finclude «graphics.h» finclude «conio.h» ma inf ) { int Driver, Mode; int xCen, yCen, xRad, yRad; Driver > DETECT; initgraphf&Driver, &Mode, ". if(graphresultf ) I = grOk) exitfl); xCen * getmaxxf )/2; yCen = getmaxyf )/2; xRad = 150; yRad 1 100; ell ipse (xCeh.yCen, 0,360,xRad, yRad); whilef! kbhitf )}; closegraphf ); return 0; } , fillellipse ( Иля. fillellipse - вычерчивание закрашенного эллипса. Заголовок void far fillellipsefint x, int y. int xAxis, int yAxis) Прототип graphics.h Функция Вычерчивание закрашенного эллипса с центром в точке (х, у) и полуосями xAxis и yAxis. Замечания Система должна находиться в графическом режиме. Закрашивание производится текущим цветом. Пример Вычерчивание эллипса с полуосями по 100 точек, расположенного в центре экрана. /* FillEllipse */ finclude «graphics.h» finclude <conio.h> mainf ) { int Driver, Mode; Driver = DETECT; initgraphf&Driver, &Mode, ”. .”); iffgraphresultf ) I - grOk) exitfl); fillellipsefgetmaxxf ) »1, getmaxyf ) - »»1, 100, 100);
17. Принципы описания 423 whi1e(! kbhit( )); (void)getch( ); closegraphf ); return 0; } fillpoly Имя Заголовок fillpoly - закрашивание многоугольника, void fillpolyfint Nun Points, int * PolyPoints) Прототип Функция graph!cs.h Закрашивание по заданному шаблону многоугольника c Nun Points вершинами, координаты которых поочеред но перечисляются, а первая из них приписывается переменной, указываемой аргументом PolyPoints. Замечания Система должна находиться в графическом режиме. Каждая пара координат задается двумя величинами типа (int). Шаблон и цвет закрашивания прямоугольника могут быть заданы с помощью функции setfillstyle и setfillpattern. Неудачное выполнение функции fillpoly приводит к тому, что результатом функции graphresult является переменная со значением grNoScanMan - не хватает памяти для закрашивания области методом сканирования. Пример Вычерчивание четырехугольника и его закрашивание по одному из стандартных шаблонов. /* FillPoly */ #inc1ude<graphics.h> #include<conio.h> struct pointtype Tetragon} ] = { {10, 10}, {80, 40}. {30, 70}, {40, 50}; nain( ) { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". if(graphresult} ) I = grOk) exit(l); setfillstyle(XHATCH_FILL, getmaxcolor} )); fillpoly(4, Tetragon); . while} I kbnit} ));
Часть VI. Библиотеки 424 closegraph(); return 0; } floodfill Имя Заголовок Прототип Функция Я Замечания floodfill - закрашивание области по заданному шаблону. void floodfi11(int x, int у. int Border) graphics.h Закрашивание по заданному шаблону внутренней области, охватывающей точку с координатами (х, у) и ограниченной линией, номер которой задается аргументом Border. Система должна находиться в графическом режиме. Если точка с координатами (х. у) находится за пределами области, то закрашиванию подлежит внешняя часть этой области, ограниченная краями графического окна. Выполнение функции заканчива ется преждевременно после ввода двух пустых линий. Шаблон и цвет закрашивания могут быть заданы С помощью функции setfillstyle или setfillpattern. Неудачное выполнение функции floodfill и приводит к тому, что результатом функции graphresult является переменная со значением grNoFloodMem - не хватает памяти для закрашивания области лавинным методом. Пример Закрашивание внешней области треугольника с произвольным образом выбранными вершинами. /* FloodFill */ #include<graphics.h> #include*conio.h» struct pointtype Trianglef J = { {50. 100}. {100, 100}, {150, 150}, {50, 100}; main( ) { int Driver, Mode; Driver = DETECT; initgraph(&Driver, SMode, ". ."); if(graphresult( ) I = grOk) exit(l); drawpoly(4. Triangle);
17. Принципы описания 425 floodf111(0, 0, getmaxcolorO); whilef I kbhitf )); closegraphf ); return 0; } getarccoords Имя Заголовок getarccoords - определение координат дуги, void getarccoords(struct arccoordstype *ArcCoords) Прототип Функция graphics.h Присвоение структуре, указанной в аргументе ArcCoords, значения типа (struct arccoordstype), определяющего параметры дуги окружности, вычерчиваемой с помощью функции аге. Замечания Система должна находиться в. графическом режиме. Тип (struct arccoordstype) определен следующим образом: struct arccoordstype { int х, у; int xstart, ystart; int xend, yend; -' Поля x и у определяют координаты центра окружности, поля xstart и ystart указывают координаты начала дуги, а поля xend и yend - координаты конца дуги. Пример Вычерчивание дуги окружности, соединение концов дуги отрезком прямой линии, а затем закрашивание внешней области полукруга по одному из стандартных шаблонов. /* GetArcCoords */ finc1ude<graphics.h> #include*conio.h» struct arccoordstype ArcCoords; ma inf ) { int Driver, Mode; Driver = DETECT; initgraphf&Driver, &Mode, ". .''); if(graphresultf ) ! = grOk) exitfl); arcfgetmaxxf )/2, getmaxyf )/2,0,180,100); getarccoords(&ArcCoords); 11ne(ArcCoords.xstart,ArcCoords.уstart, ArcCoords. xend, ArcCoords. yend);
426 Часть VI. Библиотеки setfillstyle(XHATCH_FILL, getmaxcolor( )); floodfi11(0,0,getmaxcolor()); wMle( I kbhitf )); closegraphf ); return 0; } getaspectratio Имя getaspectratio - определение коэффициента сжатия экрана. Заголовок void getaspectratio(int * xAsp, ft int * yAsp) Прототип graphics.h Функция Присвоение переменным, указанным в аргументах xAsp и yAsp, значений типа (int), на основе которых можно вычислить коэффициент сжатия экрана. Замечания Система должна находиться в графическом режиме. Применение коэффициента сжатия, выражаемого в * виде отношения xAsp/yAsp, позволяет вычерчивать прямоугольники, имеющие вид квадрата, и эллипсы в виде окружности. Пример Вычерчивание прямоугольника, по виду близкого к квадрату. /* GetAspectRatio */ #1nclude<graphics.h> #include<conio.h> #include<math.h> main( ) { int Driver, Mode; int xAsp, yAsp int xSide, ySide; Driver = DETECT; initgraphf&Driver, &Mode, ". if(graphresult( ) I = grOk) exit(l); getaspectratio(&xAsp. &yAsp); xSide » 100; ySide - floor((float)xAsp/yAsp * xSide + 0.5); rectangie(0, 0,xSide,ySide); whi1e( I kbhit( )); closegraphf ); return 0; }
17. Принципы описания 427 getbkcotor Имя getbkcolor - задание цвета фона. Заголовок int getbkcolor(void) Прототип graphics.h Функция Формирование величины типа (int) со значением, равным номеру той позиции палитры, которая указывает цвет фона. Замечания Система должна находиться в графическом режиме. Если результатом функции является значение, равное п, то цвет фона задается n-й позицией палитры. В случае применения драйвера Hercules фон может быть только черным, а результатом функции getbkcolor является значение 0. Пример Вычерчивание диагонали экрана, а после нажатия произвольной клавиши удаление с экрана половины этой диагонали. /* GetBkColor */ #include<graphics.h> finclude<conio.h> main( ) { int Driver, Mode; Driver > DETECT; initgraph(&Driver, SMode, ". ."); if(graphresult( ) I » grOk) exit(l); line(0,0, getmaxx( )/2 *2, getmaxyt )/2, *2); while( I kbhit( )); (void)(getch( ); setcolor(getbkcolor( )); line(0,0, getmaxx( )/2, getmaxy( )/2); while( I kbnit( )); closegraph( ); return 0; } getcolor г Имя Заголовок Прототип Функция getcolor - получение номера int getcolor(void) graphics.h Формирование значения типа цвета. (int), равного номеру
428 Часть VI. Библиотеки Замечания текущего цвета, используемого для вычерчивания линии. Система должна находиться в графическом режиме. Если результатом функции getcolor является значение, равное п, то текущий цвет задается n-й позицией палитры. В случае применения драйвера Hercules результат, равный 0, означает черный цвет, а результат, равный 1, - белый цвет. Пример Попеременное вычерчивание и стирание горизонтальной линии, расположенной вдоль верхнего края экрана, на мониторе, управляемом драйвером Hercules. s /* GetColor */ find ude<graph i cs.h> #include<conio.h> main( ) { Int Driver, Mode; int 1; * Driver = DETECT; initgraph(&Driver, SMode, ”. if((graphresult( ) I = grOk)|(Driver I = HERCMONO)) exit(l); for(i =1; 1 < = 15; i ++){ line(0,0, getmaxx( ), 0); setcolor(l-getcolor( )); sleep( ); } while( I kbhit( )); closegraph( ); return 0; } getdefaultpalette Имя Заголовок Прототип Функция Замечания getdefaultpalette - получение исходной палитры, void far * far getdefau1tpalette(vo id) graphics.h Формирование значения типа (void far *), указывающего ту палитру, которая была выбрана непосредственно после инициирования драйвера. Система должна находиться в графическом режиме. Инициирование драйвера производится с помощью функции 1 nitgraph.
17. Принципы описания 429 Пример Иллюстрация воспроизведения палитры, которая была выбрана по умолчанию при инициировании драйвера. /* GetDefaultPalette */ #include«graphics.h> f1nclude<conio.h> #1nclude<dos.h> # i nc 1 ude<std 11 b. h> main( ) { int Driver, Mode; struct palettetype far *OldPal, NewPal; Driver = DETECT; 1nitgraph(&Driver, &Mode, ". ."); if(graphresult( ) I = grOk) exit(l); setcolor(l); outtext ("He Ho"); delay(ZOOO); while( I kbhit( )){ NewPal.colors[l] = rand( ) X NewPal. size; setallpalette(&NewPal); } (void)getch(); OldPal = getdefaultpalette(); setallpalette(OldPal); delay(ZOOO); closegraph( ); return 0; } geWrivemame Имя Заголовок Прототип Функция Замечания getdr ivername - получение имени драйвера. char * far getdrivername(vo1d) graphics.h Формирование значения типа (char*), описывающего последовательность символов, представляющих имя графического драйвера. Система должна находиться в графическом режиме. Пример Вывод надписи HERC на экран монитора, управляемого драйвером Hercules. /* GetDriverName */ # i nc1ude<graph1cs.h>
430 Часть VI. Библиотеки f1nclude<con1o.h> main( ) { Int Driver, Mode; char *Name; Driver = DETECT; 1n1tgraph(&Driver, &Mode, ". ."); If(graphresult( ) I « grOk) exlt(l); Name > getdr1vername( ); closegraph( ); cprintf("%s", Name); return 0; } <» getfillpaitern Имя getflllpattem - сообщение о шаблоне закрашивания областей. Заголовок • Прототип Функция void getf1llpattern(char * Pattern) graphics.h Размещение в области памяти, на начальный адрес которой указывает аргумент Pattern, восьми значений типа (unsigned char), описывающих намеченный пользователем шаблон для закрашивания областей. Замечания Система должна находиться в графическом режиме. Если перед вызовом функции getflllpattem функция setflllpattern не вызывалась, то в указанную область памяти будут помещены значения Oxff. Пример Заполнение экрана редко расположенными горизонтальными линиями. /♦ GetFIllPattern */ t1nc1ude<graph1cs.h> #1nclude<con1o.n> char Pattern! &]; ma1n( ) { Int Driver, Mode; Int 1; Driver = DETECT; Inltgraphf&Drlver, &Mode, ". ."); 1f(graphresult( ) I = grOk) exit(l); getf1llpattern(Pattern); for(1»0; 1<8; 1++) / if (I (i & D) /
17. Принципы описания 431 Pattern[i] = 0; setfillpattern(Pattern, getmaxcolor( )); floodf111(0,0,getmaxcolor( )); closegraph( ); return 0; } getfilisettings Имя Заголовок Прототип Функция Замечания getfilisettings - сообщение о шаблоне закрашивания областей. void getf i1Isettings(struct f i1Iset tingstype *Info) graphics.h Присвоение структуре, указываемой аргументом Info, значения типа (struct fillser.tingstype), указывающего шаблон для закрашивания областей и номер цвета для вычерчивания линий. Система должна находиться в графическом режиме. Тип (struct fill settingstype) определен следующим образом: struct fillsettingstype{ int patten; int color; } Поле pattern указывает номер шаблона, выбранного с помощью функции setfillstyle или определенного с помощью функции setfillpattern, а поле color указывает номер цвета, которьй будет использован для закрашивания областей. Виды шаблонов, характеризующих способ закрашивания областей, задаются следующим образом: EMF'YJILL - цветом фона SOLID_FILt - одним и тем же цветом LINE_FIl.l. - утолщенными горизонтальными линиями ltslash_f;ll - наклонными линиями SLASH_FILL - утолщенными наклонными линиями BKSLASH_FILL - утолщенными линиями LTBKSLASHFI - диагональными линиями HATCHFILL - вертикальной сеткой XHATCH.FILL - диагональной сеткой INTERLEAVEFILL - переплетенными линиями WIDEDOT_FILL - точками CLOSEDOT_FILL - сгущенными точками.
432 Часть VI. Библиотеки Пример Сообщение о текущем шаблоне закрашивания областей. /* GetFIUSettings */ #1nclude<graphics.h> #1nclude<coni□.h> struct fillsettingstype FillSettings; main( ) { int Driver, Mode; Driver • DETECT; 1n1tgraph(&Driver, &Mode, ". ."); if(graphresult( ) ! - grOk) ex1t(ijj setflllstyle(XHATCH rILL, getmaxcolor( )); drcle(getmaxx( )/2, gei:mixy( )/2, 50)); floodfl 11(getmaxx( )/2, getmaxyf )/2); getnaxcolor( )); getf111sett1ngs(&F111Sett1ngs); 1f(FillSettings.pttern — XHATCh'J-ILL) outtextyy (0,0"xHatchF 111"); closegraph( ); return 0; } getgraphmode Имя Заголовок Прототип Функция Замечания. getgraphmode - сообщение о текущем графическом режиме» Int getgraphmode(void) graphics.h Формирование значения типа (int), определяющего номер текущего графического режима. Система должна находиться в графическом режиме. Результатом является значение типа (int), находящееся в диапазоне 0 .. 5 и указывающее номер режима, установленного с помощью функции initgraph или setgraphmode. Номера режимов выражаются следующими константами: CGACO - 320x200, палитра 0 CGAC1 - 320x200, палитра 1 CGAC2 - 320x200, палитра 2 CGAC3 - 320x200, палитра 3 CGAHI - 640x200, 1 Страница MCGACO - 320x200, палитра 0 MCGAC1 - 320x200, палитра 1 MCGAC2 - 320x200, палитра 2.
17. Принципы описания MCGAC3 MCGAMED MCGAHI EGALO EGAHI EGA64L0 EGA64HI EGAMONOHI HERCMONOHI АП400С0 АТТ400С1 АТТ400С2 АТТ400СЗ ATT400MED ATT400HI VGA1.0 VGAMED VGAHI PC3270HI IBM8514L0 IBM8514HI - 320x200, палитра 3 - 640x200, 1 страница - 640x480, 1 страница - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x350, 16 цветов, 1 страница - 640x350, 4 цвета, 1 страница - 640x350, 64К на плате, 1 страница 256К на плате, 2 страницы - 720x348, 2 страницы - 320x200, палитра 0 - 320x200, палитра 1 - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница - 640x400, 1 страница - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x480, 16 цветов, 1 страница - 720x350, 1 страница - 640x480, 256 цветов - 1024x768, 256 цветов Пример Вывод числа 1 на экран монитора, управляемого драйвером Hercules. /* GetGraphMode * / #include<graphics.h> #1nclude<conio.h> finclude<stadio.h> main( ) { int Driver, Mode; Driver = DETECT; initgraphf&Driver, &Mode, ”. ."); if(graphresult( ) I » grOk) exit(l); if(Driver == HERCMONO){ restorecrtnode( ); cprintf(”%d”. getgraphmode( ) == HERCMONOHI); /* 1 */ ' while(getchar( ) I - ’\n’); setgraphnode(Mode);
Часть VI. Библиотеки while(!kbhit()); closegraph( ); return 0; getimage Имя Заголовок Прототип Функция Замечания getimage - запоминание изображения. void getimage (int xl, int yl, int x2, int y2, void * BitMap) graphics.h Выделение в текущем графическом окне прямоугольной области, противоположные по диагонали вершины, которой имеют координаты (xl, yl) и (х2, у2), а затем запоминание заключенного в этом прямоугольнике изображения в области памяти, указанной аргументом BitMap. Система должна находиться в графическом режиме. Размер области рекомендуется указывать с помощью функции imagesize. Пример Запоминание и воспроизведение изображения /* Getimage */ finclude «graphics.h» void*Ref main() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresult()l = grOk) exit(l) line(0,0, getmaxxO, getmaxyO); Ref = malloc(imagesize(0,0 getmaxxO, getmaxyO)); getimage(0,0, getmaxxO. getmaxyO, Rei); while(lkbhitO); (void)getch(); clearviewportO; putimage(0,0 Ref,COPY_PUT); while(lkbhitO); closegraphO; return 0; getlinesettings Имя getlinesettings - сообщение о параметрах линии.
17. Принципы описания Заголовок void getlinesettings (struct linesettingstype * Info) Прототип Функция graphics.h Присвоение структуре Info значения типа (struct linesettingstype), характеризующего вид, шаблон и толщину линии. Замечания Система должна находиться в графическом режиме. Тип (struct linesettingstype) определен следующим образом: struct linesettingtype { int linestype; unsigned upattern; int thickness; 1 Поле linestype указывает номер вида линии. Если этот номер равен USERBITJ.INE,. то поле upattern указывает шаблон линии. Поле thickness толщину линии. Приписываемые полями linestyle и chickness значения выражаются следующими константами: Поле Unestyle SOLIDLINE - непрерывная линия DOTTEDLINE - точечная линия CENTERJ.INE - точечнопунктирная линия DASHEDJ.INE - пунктирная линия USERBIT_LINE - линия, определяемая пользователем, Поле thickness NORM-WIDTH - тонкая линия THICK_WIDTH - утолщенная линия В поле upattern может быть указано произвольное 16-бит обозначение шаблона. Пример Вычерчивание линии и сообщение о ее шаблоне. /* GetLineSettings */ #i пс 1 ude<graph i cs.h> #include<conio.h> struct linesettingstype LineSetting; main() { Int Driver, Mode; unsigned Mask;
436 Часть VI. Библиотеки Driver • DETECT; inigraph(&Driver, &Mode, ". if(graphresult()l « grOk) exit(l); setlinestyle(USERBIT_LINE, 0x8f, NORM_WIDTH); line(0, getmaxy()/2. getmaxx(), getmaxy()/2); get 1 i nesett i ngs (&Li neSett i ng); Mask • 1 « 15; for(1 0; 1<16; 1 ++){ if (LIneSett1ng.upattern & Mask) outtext("*"); else outtext(">") Mask » » 1; } * while(Ikbhit()); closegraphO; return 0; getmaxcolor. Имя getmaxcolor - сообщение о максимальном номере цвета. Заголовок int getmaxcolor(void) Прототип Функция graphics.h формирование значения типа (int), определяющего максимальный номер цвета. Замечания Система должна находиться в графическом режиме. Пример Вычерчивание отрезка прямой линии цветом, равным максимальному номеру цвета. /* GetMaxColor */ # 1 nc 1 ude-cgraph 1 cs. h> finclude<conio.h> main() { int Driver, Mode; Driver » DETECT; initgraph(&Driver, SMode, ". .”); if(graphresult() I < exit(l) » grOk) setcolor(getmaxcolor()); lineto(getmaxx(), 0);' whilst! kbhitO);
17. Принципы описания 437 closegraph(); return 0; } getmaxmode Имя getmaxmode - сообщение о максимальном номере графического режима. Заголовок int far getmaxmode(void) Прототип Функция graphics.h Формирование значения типа (int), определяющего максимальный номер графического режима. Замечания Система должна находиться г. графическом режиме. Пример Выдача максимального номера графического режима. /* GetMaxMode */ tinc1ude<graphics.h> #include<conio.h> main() { Int Driver, Mode, Max; Driver = DETECT; 1nitgraph(&Dr1ver, &Mode, ". if(graphresult() I = grOk) exit(l) Max - getma.xmode(); setcolor(getmaxcolor()); closegraph(); cprintf("%d", Max); return 0; } getmaxx Имя getmaxx - сообщение о максимальном значении горизонтальной координаты графического экрана. Заголовок int getmaxx(void) Прототип Функция graphics.h Формирование значения типа (int), определяющего максимальную горизонтальную кординату графического экрана. Замечания Система должна находиться в графическом режиме. Значения горизонтальной координаты принадлежат диапазону 0.. getmaxx().
438 Часть VI. Библиотеки Пример Вычерчивание отрезка прямой линии вдоль верхнего края экрана. /* GetMaxX */ # inc1ude<graph1cs.h> #include<conio.h> mainO { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ”. ."); if(graphresult() I = grOk) ex1t(ll” line(0,0, getmaxx(), 0); while( I kbhitO); closegraphO; return 0; } getmaxy Имя getmaxy - сообщение о максимальном значении вертикальной координаты графического экрана. Заголовок int Прототип graphics.h Функция Формирование значения типа (int), определяющего максимальную вертикальную координату графического экрана. Замечания Система должна находиться в графическом режиме. Значения вертикальной координаты принадлежат диапазону 0..getmaxyO. Пример Вычерчивание отрезка прямой линии вдоль левого края экрана /* GetMaxY */ # inc1ude<graphics.h> finclude<conio.h> main() { int Driver, Mode; Driver DETECT; initgraph(&Driver, &Mode, ”. if(graphresult() I = grOk) exit(l) line(0,0,J], getmaxyO); whilefl kbhitO);
17. Принципы описания 439 closegraph(),; return 0; } getmodename Имя getmodename - сообщение о наименовании графического режима. Заголовок char * far getmodename(Int Прототип Функция graphics.h Формирование значения типа (char *), задающего последовательность символов, определяющих графический режим с номером Num. Замечания Система должна находиться в графическом режиме. Пример Выдача имени режима с номером 0. Вывод надписи 720 х 348 HERCULES на экран монитора, управляемого драйвером Hercules. /* GetModeName */ # 1nc1ude<graphics.h> #1nclude<conio.h> main() { int Driver, Mode; char "Name; Driver - DETECT; Inltgraphf&Drlver, &Mode, ". if(graphresult() ! ; exit(l) •= grOk) Name « getmodename(O); closegraph(); cprintf("%S", Name); return 0; } getmoderange Имя getmoderange - сообщение о диапазоне графических режимов. Заголовок void getmoderange(1nt GraphDr1ver, int * LoMode, int * Hi lode Прототип Функция graphics.h Присвоение переменной, указанной в аргументе LoMode, наименьшего, а переменной, указанной
440 Часть VI. Библиотеки Замечания в аргументе HiMode, наибольшего номера режима для графического драйвера с номером GraphDriver- Система должна находиться в графическом режиме. Если номер драйвера не принадлежит диапазону 1..5 или 7..10, то переменным, указанным в аргументах LoMode и HiMode, будут присвоены значения -1. Номера графических драйверов описываются следующими константами: CGA - драйвер GCA MCGA - драйвер MCGA EGA - драйвер EGA EGAMONO - драйвер EGAMono IBM8514 - драйвер IBM8514 HERCMONO - драйвер Hercules ATT400 - драйвер ATT400 VGA - драйвер VGA PC3270 - драйвер PC3270. Пример Определение диапазона допустимых режимов монитора, управляемого драйвером Hercules. /* GetModeRange */ t1nc1ude<graph1cs.h> finclude<conio.h> main() { int LoMode, HiMode; getmoderange(HERCMONO, &Mode, SHiMode); cprintf("LoMode •= % d”, LoMode); /* 0 */ cprintf("HiMode - % d”, HiMode); /* 0 */ while(lkbhitO); closegraph(); return 0; } getpalette Имя Заголовок getpalette - сообщение о текущей палитре цветов, void getpalette(struct palettetype * Palette) Прототип Функция graphics.h Присвоение структуре типа (struct palettetype), указанной в аргументет Palette, значений, определяющих размер палитры цветов и идентификаторы цветов, соответствующих отдельным позициям палитры. Замечания Система должна находиться в графическом режиме.
17. Принципы описания 441 Тип (struct palettetype} определен следующим образом: struct_palettetype { unsigned char size; signed char colors[16]; } Поле size указывает размер палитры, а поле colors - идентификаторы цветов, соответствющих очередным позициям палитры. Идентификаторы цветов задаются следующими константами: BLACK BLUE GREEN CYAN RED MAGENTA BROWN LIGHTGRAY DARKGRAY LIGHTBLUE LIGHTGREEN LIGHTCYAN LIGHTRED LIGHTMAGENTA YELLOW WHITE - черный - голубой - зеленый - бирюзовый - красный - ярко-красный - коричневый - светло-серый - темно-серый - светло-голубой - светло-зеленый - светло-бирюзовый - светло-красный - светло-ярко-красный - желтый - белый. Пример Вывод названий цветов на экран в принятой палитре монитора, управляемого драйвером EGA. /* GetPalette */ #inc1ude<graphics.h> finclude<conio.h> srtuct palettetype Palette; char "Names[] {"Black", "Blue”, "Green”, "Cyan", "Red", "Magenta", "Brown", "LightGray", "DarkGray", "LightBlue", "LightGreen", "LightCyan", "LightRed", "LightMagenta", "Yellow", "White"}; main() { int Driver, Mode: int i; detectgraph(& Driver, &Mode); if((graphresult()! = grOk)|| (Driver! « EGA)){ cprintf("Failure") exit(l); }
442 Часть VI. Библиотеки getpa1ette(&Раlette); restorecrtmode(); for(i=0, i<=Palette, size-1; i ++) { if(Palette.colors[i]<16) cpri ntf("20", Names[Palette.colors[i]]); else cprintf("???:( %d", Palette.colors[i]); } return 0; getpalettesize Имя getpalettesize - сообщение о размере палитры. Заголовок# int far getpalettesize(void) Прототип graphics.h Функция Формирование значения типа (int), равного размеру палитры. Замечания • Система должна находиться в графическом режиме. В случае монитора, управляемого драйвером EGA, рассматриваемое значение равно 16. Пример Определение размера палитры. Вывод числа 1 на экран монитора, управляемого драйвером Hercules. /* GetPaletteSize */ find ude<graph i cs. h> find ude<con i о. h> main() { int Driver, Node, Size; Driver = DETECT; initgraph(& Driver, &Mode, ”. ."); if(graphresult()! = grOk) exit(l); Size = getpalettsizeO; closegraph(); cprintf(%d”. Size); return 0; } getpixel Имя Заголовок Прототип getpixel - сообщение о номере цвета точки. unsigned getpixel(int х, int у) graphics.h
17. Принципы описания 443 Функция Формирование значения типа (unsigned), равного ру цвета точки с координатами (х, у). Замечания Система должна находиться в графическом режиме. Пример Вычерчивание точки цветом, соответствующим последней позиции палитры, а затем вывод номера этого цвета. /♦ GetPixel ♦/ #inc1ude<graphics.h> #indude<conio.h> main() { int Driver, Mode; 1 int ColNum; Driver = DETECT initgraph(& Driver, &Mode, ". ."); if(graphresult()! = grOk) exit(l); putpixel(0,0, getmaxcolorO); ColNum = getpixel(O.O); restorecrtmode(); cprintf("Color number = %d". ColNum); return 0; } gettext Имя Заголовок Прототип Функция Замечания gettext - перенесение текста с экрана в память, int gettext(int xl, int yl, int x2, int y2, void * TextArea) conio.h Перенесение текста, находящегося в прямоугольнике с координатами противоположных по диагонали вершин (xl, yl) и (х2, у2), в область памяти, указанную в аргументе TextArea. Результатом функции является величина типа (int), которая в случае удачного выполнения операции принимает значение, отличное от 0. Пример Перенесение символов из произвольным образом выбранного прямоугольника в другое место экрана. /* GetText */ finclude<conio.h>
444 Часть VI. Библиотеки char Family[100]; main() { clrscr(); cpr1ntf("Ewa\r\n”); cprintf(Iza\r\rT); cpr i ntf (” Jan\r\n*’); cettext(l, 1, 4, 3, Family}; clrscrO; puttext(20, 13, 23, 15. Family); rutjrn 0; gettextinfo -------------r Имя Заголовок Прототип Функция Замечания gettextinfo - сообщение об условиях отображения текста на экране. void gettextinfo(struct te“t_info * Info) conio.h Присвоение структуре типа (struct text—info), указанной в аргументе info, значений, характеризу- ющих текущие условия отображения текстов. Тип (struct text_1nfo) определен следующим образом: struct text_1nfo { unsigned char winleft, wintop, winright, winbottom, attribute, normattr, currmode, screenhsight, screenwidth, curx, cury; } Поля winleft, wintop, winright, winbottom определяют координаты углов текстового окна, а поле attribute - признак, присвоенный переменной TextAttr, поле normattr указывает принятый по умолчанию признак этой переменной; поле currmode определяет текущий режим вывода символов, поля screenheight и screenwidth указывают соответственно вертикальный и горизонтальный размеры экрана, выраженные количеством символов, а поля curx и сигу задают текущие координаты графического курсора.
17. Принципы описания 445 Пример Определение размеров окна в текстовом режиме, принятом по умолчанию. /* GetTextlnfo */ finclude <conio.h> struct text_info Textinfo; Main() { clrscrO; gettextinfо(&TextInfо); cprintf("Screen size is Xd x Xd", Textinfo, screenwidth, Textinfo, screenheight); return 0; } gettextsettings Имя gettextsettings - сообщение о параметрах вычерчивания текстов. Заголовок void gettextsettings(struct textsettingstype *lnfo) Прототип Функция graphics.h Присвоение структуре, указанной в аргументе Info, значений записи типа (struct textsettingstypy), указывающей формат шрифта, направление вычерчивания текста и способ его выравнивания. Замечания Система должна находиться в графическом режиме. Тип (struct textsettingstype) определен следующим образом: struct text settingstype { int font; int direction; int charsize; int horiz; int vert; } Поле font указывает номер шрифта: OEFAULT_FONT - шрифт, принятый по умолчанию, TRIPLEX_FONT - тройной шрифт, SMALL_FONT - индексный шрифт, SANS_SERIF_FONT - шрифт сансериф GOTHIC_FONT - готический шрифт. Поле direction указывает направление вычерчивания текстов: HORIZ_OIR - горизонтально, VERTJHR - вертикально.
446 Часть VI. Библиотеки 3 Пример Поле charsize указывает множитель для определения размера шрифта (если charsize •— USER_CHAR_SIZE, то вертикальный и горизонтальный множители размера шрифта определяются функцией setusercharsize); поле horiz указывает способ выравнивания текста в горизонтальном направлении: LEFT_IEXT - выравнивание влево, CENTERJEXT - выравнивание по центру, RIGHT TEXT - выравнивание вправо; в вертикальном направлении: ВОТТОМ_ТЕХТ - выравнивание вниз, CENTER_TEXT - выравнивание по центру, ТОР_ТЕХТ - выравнивание вверх. Значения, приписанные полям font, direction, horiz и vert, выражаются перечисленными выше константами. Вывод на экран надписи Janek, а затем вывод символа, указывающего выбранное направление вывода этой надписи. /* GetTextSettings */ # i nc 1 ude<gra|ih i cs. h> #include<conio.h> struct textsettingstype TextSettingS; char *Names[] = { "Left Text”, "Center Text”, "Right Text”}; main() ( int Driver, Mode; Driver - DETECT; initgraph(& Driver, & Mode, ”. .”); if(graphresult()I = grOk) exit(l); settextstyle(DEFAULT_FONT, HORIZ_DIR, 5); settextjustify(LEFT TEXT, TOP-TEXT); outtextxy(getnaxx()/2, getn>axy()/2, "Janek”); whlle(!kbhit()); (void)getch(); gettextsett i ngs(&TextSett i ngs); restorecrtnodef); cprintf("Horiz • Xs”, Names[Text Settings.horiz]); return 0; } getviewsettings Имя Заголовок getviewsettings - сообщение о параметрах графического окна. void
Прототип Функция Замечания 17. Принципы описания 447 getviewsettings(struct viewporttype * Viewport) graphics.h Присвоение структуре типа (struct viewporttype), указанной в аргументе View Port, значений, определяю- щих координаты противоположных по диагонали вер- шин графического окна, а также способ отображения графических фрагментов, выходящих за пределы окна. Система должна находиться в графическом режиме. Тип (struct viewporttype) определен следующим образом: struct viewporttype { int left, top; int right, bottom; int clip; } Поля left, top и right определяют координаты противоположных вершин графического окна, а поле clip указывает, должны ли упомянутые фрагменты быть оборваны на краях окна. Пример Вывод на экран информации о параметрах окна, установленного по умолчанию. /* GetViewSettings */ # i nc1ude<graph i cs.h> #include<conio.h> struct viewporttype DefaultViewPort;' main() { int Driver, Mode; Driver = DETECT; inttgraph(& Driver, & Mode, ". .”); if(graphresult()! = grOk) exit(l); getv i ewsett i ngs(&Defau1tV i ewPort); restorecrtmodeO; cprintf("xl = %d, yl = %d\r\n”, DefaultViewPort.left. Defau1tV iewPort.top); cprintf("x2 = %d, yZ = %d\r\n”, DefaultViewPort.right, DefaultViewPort.bottom); if(DefaultViewPort.cl ip) cprintf("Clip = %d”, DefaultViewPort.cl ip); return 0; } getx Имя getx - сообщение о горизонтальной координате
448 Часть VI. Библиотеки Заголовок Прототип Функция Замечания Пример графического курсора, int getx(void) graphics.h Формирование значения типа (int), равного горизонтальной координате графического курсора. Система должна находиться в графическом режиме. Координата определяется относительно текущего графического окна. Вывод координат центра экрана /* GetX */ tinc1ude<graphics.h> finc1ude<coniо.h> i»ain() { int Driver, Mode; int xCor, yCor; Driver = DETECT; ini.tgraph(&Driver, &Mode, ". ."); if(graphresu1t() I = grOk) exit(l); moveto(getmaxx()/2, getmaxy()/2; xCor = getx(); yCor » gety(); restorecrtmode(); cprintf("(xCor, yCor) = (%d, %d)", xCor, yCor); return 0; } gety Имя Заголовок Прототип Функция Замечания gety - сообщение о вертикальной координате графического курсора. int gety(void) graphics.h Формирование значения типа (int), равного вертикальной координате графического курсора. Система должна находиться в графическом режиме. Координата определяется относительно текущего графического окна. Пример Вывод максимальных координат экрана. /* GetY */
17. Принципы описания 4 tinc1ude<graph1cs. h> #include<conio.h> main() { int Driver, Mode; int xCor, yCor; Driver « DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresult()l = grOk) exit(l); moveto(getmaxx(), getmaxyO); xCor - getx(); yCor - gety(); restorecrtmode(); cprintf(“xCor, yCor) = (Xd, Xd)", xCor, yCor); return 0; } gotoxy Имя Заголовок gotoxy - перемещение текстового курсора, void gotoxy(tnt х, int у) Прототип Функция conio.h Перемещение текстового курсора на позицию с координатами (х, у). Замечания Координаты определяются относительно текущего текстового окна. Левый верхний угол этого окна имеет координаты (1, 1). Если новые координаты указывают позицию за пределами окна, то выполнение функции ни к каким действиям не приведет. Пример Вывод буквы j в левом верхнем углу экрана и буквы Ь на позиции с координатами (10, 10). /* GotoXY */ finclude<conio.h> #include<stdio.h> main() { clrscr(); cprintf("j"); window(10, ID, 20, 20); gotoxy(l, 1); cprintf("b"); return 0; }
450 Часть VI. Библиотеки graphdefaults Имя graphdefaults - восстановление графических параметров, принятых по умолчанию. Заголовок void graphdefaults(void) Прототип Функция graphics.h Установление графического окна, охватывающего весь экран, перемещение графического курсора в его левый верхний угол, задание всех принятых по умолчанию графических параметров (палитра, шрифт, вид линий, способы закрашивания областей и выравнивания текста, размер шрифта, а также цвет фона и цвет символов). Замечания Система должна находиться в графическом режиме. Пример Вычерчивание пунктирной линии, а затем линии с параметрами,которые были установлены по умолчанию. /* GraphDefautts */ #inc1ude<graphics.h> finclude,conio.h» main() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresult() ! exit(l); - grOk) moveto(getmaxx()/2, getmaxy()/2); setlinestyle(DASHED_LINE, 0, NORMWIDTH); linerel(20, 0); /* Dashed */ graphdefaults(); 1inerel(20, 0); /* Solid */ while(! kbhit(J); closegraph(); return 0; } grapherrormsg Имя grapherrormsg - сообщение о ходе выполнения графической функции. Заголовок char * grapherrormsg(int ErrorCode) Прототип Функция graphics.h Присвоение внутренней переменной системы значения
Замечания 17. Принципы описания 451 строковой константы, состоящей из символов, образующих сообщение о номере ошибки ErrorCode, и сообщение о первом символе этой переменной. Система должна находиться в графическом режи- ме. Номера сообщений могут быть выражены сле- дующими константами: grOk grNoInitGraph grNotDetected grFileNotFound grlnvalidDriver grNoLoadMem grNoScanHem grNoFloodMem grNoFontFound grNoFontHem grlnvalidMode grError grIOError grlnvalidFont grlnvalIdFontNum grlnvalidOeviceNum - удачное выполнение графической операции, - не установлен графический режим, - не обнаружен графический адаптер, - не найден файл, содержащий графический драйвер, - неподходящий графический драйвер, - не хватает памяти для загрузки драйвера, - не хватает памяти для закрашивания области методом сканирования, - не хватает памяти для лавинного закрашивания области, - не найден файл с определением шрифта, - не хватает памяти для загрузки штрихового шрифта, - неправильно выбран графический режим, - другие ошибки (например, слишком большое число заданных штриховых шрифтов), - ошибка операции ввода -вывода, - неправильный шрифт, - неправильный номер шрифта, - неправильный номер устройства. Пример Вычерчивание отрезка прямой линии или вывод сообщения о причине неудачного инициирования графического адаптера. /* GraphErrorMsg */ finclude<graphics.h> finclude<conio.h> main() 29*
452 Часть VI. Библиотеки int Driver, Mode; int Result; Driver - DETECT; initgraph(&Driver, SMode, "DRIVERS"); Result “ graphresult(); if(Result I * grOk){ clrscrO; cprintf("Xs”, grapherrormsg(Result)); exit(l); } line(0, 0, getmaxxO, getmaxyO); while(l kbhitO); closegraphO; return 0; „ graphresult Имя Заголовок Прототип Функция Замечания graphresult - выдача кода ошибки для последней Не - удачно выполненной графической операции, int graphresult(void) graphics.h Формирование значения типа (int), равного коду ошибки последней неудачно выполненной графической операции. Система должна находиться в графическом режиме. Результат, равный 0, означает удачное, а отрицательный результат - неудачное выполнение операции. Коды ошибки выражаются следующими константами: grOk - удачное выполнение графической операции, grNoInitGraph - не установлен графический режим, grNotDetected - не обнаружен графический адаптер, grFileNotFound - не найден файл, содержащий графический драйвер, grlnvalidDriver - неподходящий графический драйвер, grNoLoadMem - не хватает памяти для загрузки драйвера, grNoScanMem - не хватает памяти для закрашивания области методом сканирования, grFloodMem - не хватает памяти для лавинного закрашивания области, grNoFontFound ~ не найден файл с определением шрифта grNoFontMem - не хватает памяти для загрузки шрифтов
17. Принципы описания 453 grlnvalidHode grError grIOError grlnvalidFont grlnvalidFontNum grlnval IdDeviceNuni - неправильно выбран графический режим, - другие ошибки (например, слишком большое число заданных штриховых шрифтов), - ошибка операции ввода-вывода, - неправильный шрифт, - неправильный номер шрифта - неправильный номер устройства. Поскольку при повторном вызове функции graphresult (без выполнения графической операции между очередными вызовами) выдается значение О, рекомендуется присваивать значение результата некоторой вспомогательной переменной. Пример Иллюстрация рекомендуемого способа использования кода ошибки графической операции. /* GraphResult */ #1 nc 1 ude<graph i cs. h> #1nclude<coniо.h> main() { int Driver, Mede; int Result; Driver = DETECT; initgraphf&Driver, fiMode, ”. .’’); if((Result = graphresult())l = grOk){ cprintf("GraphResult = d". Result); exit(l); } line(0, 0, getmaxxf), getmaxyO); while(! kbhit()); closegraph(); return 0; } highvideo Имя Заголовок Прототип Функция Замечания highvideo - увеличение яркости символов, void highvideo(void) graphics.h Установка бита яркости в переменной TextAttr, в результате чего очередные выводимые на экран символы будут иметь большую яркость. Установка бита яркости приводит к тому, что темные
454 Часть VI. Библиотеки цвета первого плана превращаются в светлые: Темный цвет Светлый цвет BLACK DARKGRAY (черный / темно-серый) BLUE LIGHTBLUE (голубой) GREEN LIGHTGREEN (зеленый) CYAN LIGHTCYAN (бирюзовый) RED LIGHTRED (красный) MAGENTA LIGHTMAGENTA (ярко-красный) BROWN LIGHTBROWN (коричневый) LIGHTGRAY WHITE (светло-серый/белый) Пример Вывод на экран затемненной надписи Jan и более яркой буквы в. /* HighVideo */ #include<conio.h> #indude<stdio.h> main() { clrscrO; cprintf("Jan"); highvideo(); cprintf(*'B"); return 0; } imagesize Имя imagesize - определение размера памяти для хранения графического изображения. Заголовок unsigned imagesize (int xl, int yl. int xZ, int yZ) Прототип Функция graphics.h Формирование значения типа (unsigned), равного числу байтов памяти, необходимых для запоминания графи ческого изображения, находящегося в прямоугольнике, противоположные по диагонали вершины которого имеют координаты (xl, yl) и (х2, у2). Замечания Система должна находиться в графическом режиме. Пример Вычерчивание надписи JanB, запоминание в “куче"1’ изображения, состо- "Куча" (англ, heap} - динамически распределяемая область - область памяти, используемая для размещения структур данных в режиме случайного формирования этих структур. - Прим. ред.
17. Принципы описания 455 ящего из этой надписи, а затем дополнительный вывод ее вблизи центра экрана. /* InageSize */ #1nc1ude<graph i cs.h> finclude<conio.h> char *MyName - ”JanB"; rnain() { int Driver, Mode; void * Ref; int xSize, ySize; Driver « DETECT; initgraph(Driver, &Mode, ”. ."); if(graphresultО I « grOk) , exit(l); xSize = textwidth(MyName); ySize = textheight(MyName); settextjustify(LEFTTEXT, TOP_TEXT); outtextxyfO, 0, MyName); Ref « nialloc(inagesize(0, 0, xSize-1, ySize-1)); getinage((0, 0, xSize-1, ySize-1, Ref); putimage(getmaxx()/2, getnaxy()/2, Ref, COPY_PUT); while(l kbhitO); closegraphO; return 0; } initgraph Имя initgraph - установка графического режима. Заголовок void initgraph(int * GraphDriver, int * GraphMode, char * PathToDriver) Прототип graphics.h Функция Поиск в каталоге, имя которого указано в аргументе PathToDriver, файла, содержащего программу графического драйвера для адаптера с номером, указанным в аргументе GraphDriver, перемещение программы в оперативную память и установка графического режима, номер которого присвоен переменной, указанной в аргументе GraphMode. Замечания Если имя каталога является пустым, то файл ищется в текущем каталоге. Если GraphDriver = 0, то производится автоматическое распознавание графической среды, а переменным, указанным в apiyментах GraphDriver и GraphMode, присваиваются
Часть VI. Библиотеки значения, определяющие номер драйвера и номер режима. Если *GraphDr1ver!“O, то *GraphDriver и *GraphMode должны правильно задавать номер адаптера и номер режима. Необходимая для драйвера оперативная память предоставляется в “куче" и освобождается при выполнении функции closegraph. Номера адаптеров и режимов могут быть определены с помощью следующих констант: Адаптеры: DETECT CGA MCGA EGA EGA64 IBM8514 EGAMONO HERCMONO ATT400 VGA PC3270 - автоматическое распознавание графического адаптера - адаптер CGA - адаптер MCGA - адаптер EGA - адаптер EGA64 - адаптер IBM8514 - адаптер EGAMono - адаптер Hercules - адаптер АТТ400 - адаптер VGA - адаптер РС3270 Режимы: CGACO - 320x200, палитра 0 CGAC1 - 320x200, палитра 1 CGAC2 - 320x200, палитра 2 CGAC3 - 320x200, палитра 3 CGAHI - 640x200, 1 страница MCGACO - 320x200, палитра 0 MCGAC1 - 320x200, палитра 1 MCGAC2 - 320x200, палитра 2 MCGAC3 - 320x200, палитра 3 MCGAMED - 640x200, 1 ‘страница MCGAHI - 640x480, 1 страница EGALD - 640x200, 16 цветов, 4 страницы EGAHI - 640x350, 16 цветов, 2 страницы EGA64LD - 640x200, 16 цветов, 1 страница EGAMONOHI - 640x350, 64К на плате, 1 страница 256К на плате, 2 страницы HERCMONOHI - 720x348, 2 страницы ATT400C0 - 320x200, палитра 0 ATT400C1 - 320x200, палитра 1 ATT400C2 - 320x200, палитра 2 ATT400C3 - 320x200, палитра 3 ATT400MED - 640x200, 1 страница
17. Принципы описания 457 ATT400HI - 640x400, 1 страница VGALO - 640x200, 16 цветов, ч» 4 страницы VGAMED - 640x350, 16 цветов, 2 страницы VGAHI - 640x480, 16 цветов, 1 страница PC3270HI - 720x350, 1 страница IBM8514L0 - 640x480, 256 цветов IBM8514HI - 1024x768, 256 цветов. Неудачное выполнение функции initgraph приводит к тому, что результат функции graphresult приобретает одно из следующих значений: grNotDetected - не обнаружен графический адаптер, grF1leNotFound - не найден файл,содержащий графический драйвер, grlnvalidDriver - неподходящий графический драйвер, grNoLoadMem - не хватает памяти для загрузки драйвера, grlnvalidMode - неправильно выбран графический режим, grlnvalidDeviceNum - неправильный номер устройства. Пример Вывод на экран одной точки вблизи центра экрана /* InitGraph */ f1nclude<graph1cs.h> tinc1ude<coniо.h> mainf) { Int Driver, Mode; Driver = DETECT; initgraphf&Driver, KHode, ". ."); if(graphresult()l= grOkf) exit(l); putpixel(getmaxx()/2, getmaxyf)/2, getmaxcolorf)); whilefl kbhitf)); closegraphf); return 0; } insline Имя insline - вставка строки.
458 Часть VI. Библиотеки Заголовок void insline(void) Прототип Функция conio.h Вставка одной пустой строки в текстовом окне непосредственно под строкой, указываемой курсором. Перемещение строк, расположенных ниже указываемой курсором строки, на одну вниз. Удаление из окна самой нижней строки. Замечания Если цвет фона не является черным, то пустая строка будет заполнена пробелами, имеющими цвет, определяемый с помощью функции textbackground. Пример Вывод на экран двух строк текста, разделенных одной пустой строкой. /* InsLine */ include<conio.h> include<stdio.h> main() { clrscr(); cpr1ntf("Ewa\r\n”); cpr1ntf("Jan\r\n"); gotoxy(l, 2); insline(); return 0; } installuserdriver Имя installuserdriver - установка нестандартного драйвера. Заголовок int far installuserdriver(char far * Name, int huge(*Detect)(void)) Прототип Функция graphics.h Установка нестандартного драйвера с названием, указанным в аргументе Name, распознавание его с помощью функции Detect и формирование значения типа (int), идентифицирующего драйвер при его инициировании. Замечания После установки драйвера сформированное значение можно передать непосредственно функции initgraph. Если вместо этого задать автоматическое распознава ние, то произойдет неявный вызов функции Detect. Функция Detect должна быть написана так, чтобы в случае опознания драйвера ее результатом было зна чение, определяющее номер режима, а в противном случае значение grError - графический адаптер не опознан.
17. Принципы описания 4. Пример Способ действий при установке нестандартного адаптера. /* InstallUserDriver */ f1nclude«graph1cs.h> #include<conio.h^ int Driver, Mods, Result; int huge DetectHerc(void) { detectgraph(&Driver, 8iHode); if(graphresult() = grNotDetected) return grError; else return 0; } main() { Result = installuserdriverC'HERCULES.BGI", DetectHerc); Driver = DETECT; initgraph(&Driver, KMode, ". if(graphresult()I = grOk) exit(l); outtextC’Hello"); closegraph(); return 0; } installuserfont Имя Заголовок Прототип Функция Замечания Пример install userfont - установка нестандартного шрифта, int far installuserfont(char far *Name) graphics.h Задание нестандартного шрифта с названием, указанным в аргументе Name, и формирование значения типа (int), равного номеру этого шрифта. Сформированное значение может быть передано непосредственно в процедуру settextstyle. Способ действий при задании нестандартного шрифта. /* InstallUserFont */ #include<graphics.h> finclude<conio.h> int Driver, Mode, Font; main() {
460 Часть VI. Библиотеки Font = installuserfont("CUSTOM.CHR”); Driver « DETECT; initgraphl&Driver, &Hode, ”. if(graphresult()l “ grOk) exit(l); settextstyle(Font, HORIZDIR, 5); outtextf'Hel 10"); closegraph(); return 0; } kbhit Имя Заголовок » kbhit - анализ буфера клавиатуры, int kbhit(void) Прототип Функция conio.h Формирование значения типа (int), если в буфере клавиатуры находится еще не прочитанный символ Замечания » Функция kbhit не распознает такие символы,как Shift, Alt, NumLock и т.д. Пример Анализ времени реакции на ввод с клавиатуры с пересчетом его в условные единицы. /* khbit */ finclude<conio.h> #include<stdio.h> nain() { long Num = 0; clrscr(); while(l kbhitO); Num ++; cprintf("Count = %d", Num); while(getchar()l = ’\n’); return 0; } line Имя Заголовок line - вычерчивание отрезка прямой линии, void linefint xl, int yl, int x2, int yZ) Прототип Функция graphics.h Вычерчивание отрезка прямой линии, соединяющего
17. Принципы описания 461 точку с координатами (xl, у1> с точкой с координатами (х2, у2). Замечания Система должна находиться в графическом режиме. Координаты графического курсора не изменяются. Пример Вычерчивание отрезка прямой линии вдоль правого края экрана. /* Line */ #inc1ude<graphics.h> #inc1ude<coniо.h> main() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, SMode, ”. ."); if(graphresu1t()! = grOk) exit(l); 1ine(getmaxx(), 0, getmaxx(), getmaxxy()); whi1e(I kbhitO); c1osegraph(); return 0; } linerel Имя Заголовок linerel - вычерчивание отрезка прямой линии, void linerel(int dx, int dy) Прототип Функция graphics.h Вычерчивание отрезка прямой линии, соединяющего точку, указываемую курсором, с точкой, удаленной от нее на расстояние dx' по горизонтали и расстояние dy по вертикали. Изменение координат графического курсора на величину dx по горизонтали и величину dy по вертикали. Замечания Система должна находиться в графическом режиме. Пример Вычерчивание линии, соединяющей центральную точку экрана с точкой с координатами (0, 0). /* LineRel */ finc1ude<ics.h> #inciude<conio.h> main() { int Driver, Mode;
462 Часть VI. Библиотеки Driver - DETECT; initgraph(&Driver, SMode, ”. if(graphresu1t()I « grOk) exit(l); moveto(getmaxx() » 1, getmaxyO » 1); 1inerel(-getraaxx()/2, -getmaxy 0/2); whi1e(I kbhitO); closegraphO: return 0; } lineto Имя lineto - вычерчивание отрезка прямой линии. Заголовок я void 1ineto(int х, int у) Прототип graph ics.h Функция Вычерчивание отрезка прямой линии, соединяющего точку, указываемую графическим курсором, с точкой с координатами (х, у). Перемещение графического курсора в точку с координатами (х, у). Замечания t Система должна находиться в графическом режиме. Пример Вычерчивание отрезка прямой линии, соединяющего центральную точку экрана с точкой с координатами (0, 0). /* LineTo */ #1 ndude<graphi cs. h> finc1ude<coniо.h> main() { int Driver, Mode; Driver - DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresultO I=grOk) exit(l); noveto(gettnaxx() » 1, getmaxyO »» 1); 1ineto(0, 0); , whi1e(I kbhitO); closegraphO; return 0; } lowvideo Имя Заголовок lowvideo - уменьшение яркости символов, void
17. Принципы описания 463 Прототип Функция Замечания lowvideo(void) conic.h Обнуление бита яркости в переменной TextAttr, в результате чего очередные выводимые на экран знаки будут иметь меньшую яркость. Обнуление бита яркости приводит к тому, что светлые цвета первого плана превращаются в темные. Светлый цвет Темный цвет DARKGRAY BLACK (.темно-серый / черный) LIGHTBLUE BLUE (голубой) LIGHTGREEN GREEN (зеленый) LIGHTCYAN CYAN (бирюзовый) LIGHTRED RED (красный) LIGHTMAGENTA MAGENTA (ярко-красный) YELLOW BROWN (коричневый) WHITE LIGHTGRAY (белый / светло-серый) Пример Вывод на экран затемненной надписи Jan и 1ч>лсе яркой буквы В. /* LowVideo */ #inciude<conio.h> #1nciude<std1o.h> main() { clrscrO; towvideof); cprintf("Jan"); highvideo(); cprintf("B"); return D; } moverel Имя Заголовок moverel - перемещение графического курсора, void movere1(int dx, int dy) Прототип Функция graphics.h Изменение горизонтальной координаты графического курсора на величину dx и вертикальной координаты на величину dy. Замечания Система должна находиться в графическом режиме. Пример Вывод на экран двух точек - одной в центральной точке экрана, а другой в его левом верхнем углу.
464 Часть VI. Библиотеки /* MoveRel */ finc1ude<graphics.h> #inc1ude<conio.h> main() { int Driver, Mode; Driver - DETECT; initgraphf&Driver, &Mode, ". if(graphresu1t()I “ grOk) exit(l); moveto(getmaxx()/2, getmaxy()/2); putpixe1(getx(), gety(), getmaxcolorO); movere1(-(getmaxx()/2, -(getmaxy()/2)); putpixe1(getx(), gety(), getmaxcolorO); whi1e(I kbhitO); c1osegraph( J*, return 0; } movetext Имя Заголовок movetext - перемещение текста, int movetext(int xl, int yl, int x2, int y2, int x, int y) Прототип Функция conio.h Перемещение текста, находящегося в прямоугольнике, противоположные (по диагонали) вершины которого имеют координаты (xl, yl) и (х2, у2), в такое место экрана, чтобы левый верхний символ перемещаемого текста имел координаты (х, у). Замечания Результатом функции является значение, которое в случае ее удачного выполнения отлично от 0. Пример Копирование символов, находящихся в произвольным образом выбранном прямоугольнике экрана. /* MoveText */ ^include <conio.h> char Family[10D]; main() A { Clrscr(); cprintf("Ewa\r\n”); cprintf("Iza\r\n"); cprintf("Jan”); movetext(l,l,4,3,5,5);
17. Принципы описания 465 return 0; } moveto Имя Заголовок Прототип Функция Замечания Пример Вывод надписи Hercules. moveto - перемещение графического курсора. void moveto(int х, int у) graphics.h Перемещение графического курсора в точку с координатами (х, у). Система должна находиться в графическом режиме. (719, 347) на экран монитора, управляемого адаптером /* MoveTo */ # i nc1ude<graph i cs.h> finc1ude<coniо.h> main() { int Driver, Mode; int xCor, yCor; Driver = DETECT; initgraph(&Driver, &Mode, ". .”); if(graphresu1t()i ж grOk) exit(l); moveto(getmaxx(), getmaxyO); xCor « getx(); yCor = gety(); putpixel(x, Cor, yCor, getmaxcolorO); whileli kbhitO); (void)getch(); restorecrtmode(); cprintf("(%d, %d)", xCor, yCor); return 0; } norm video Имя Заголовок Прототип Функция normvideo - восстановление признака индикации символов. void normvideo(void) conio.h Присвоение переменной TextAttr признака, указывающего способ индикации символов, который
466 Часть VI. Библиотеки действовал в момент начала выполнения программы. Пример Вывод на экран надписи Ема буквами повышенной яркости и надписи iza буквами повышенной яркости таким же способом, какой был принят перед выполнением программы. /* NormVideo */ #indude<conio.h> #inc1ude<stdio.h> main() { clrscr(); highvideo(); cprintf ("ЕЛ"); normvideoO; cprintf(”Iza“); return 0; outtext Имя Заголовок Прототип Функция Замечания outtext - вычерчивание текста, void outtext(char * Textstring) graphics.h Вычерчивание текста, указанного в аргументе Textstring, вблизи текущего положения графического курсора. Система должна находиться в графическом режиме. Шрифт вычерчиваемого текста определяется с помощью функции settextstyle, а его цвет - с помощью функции setcolor. Расположение текста относительно позиции курсора может быть указано с помощью функции settextjustify. Если первый аргу мент функции имеет значение LEFT_TEXT, а текст выво дится по горизонтали, то горизонтальная координата графического курсора смещается на ширину этого текста. В остальных случаях положение курсора не изменяется. Если текст не уменьшается в текущем графическом окне, то он обрезается на краях окна. Пример Вычерчивание текста Inside ViewPort в левом верхнем углу текущего графического окна. /* OutText */ tinc1ude<graphics.h>
17. Принципы описания 467 #include<conio.h> fdefine CllpOn 1 main() { Int Driver, Mode; Int xMar, yMar; Driver « DETECT; initgraph(&Driver, SMode, ". if (graphresultOl -grOk) exit(l); xMar • getmaxx()/3; yMar « getmaxy()/3; setviewport(xMar, yMar, 2 * xMar, 2 * yMar, CllpOn): outtextf'Inside ViewPort"); whi1e(l kbhitO); closegraphO; return 0; } outtextxy Имя Заголовок outtextxy - вычерчивание текста, void outtextxyOnt х, int у, char * Textstring) Прототип Функция graphics.h Вычерчивание текста, указанного в аргументе Textstring, вблизи точки с координатами (х, у). Замечания Система должна находиться в графическом режиме. Положение графического курсора не изменяется. Шрифт вычерчиваемого текста определяется с помощью функции settextstyle, а его цвет - с помощью функции setcolor. Расположение текста относительно точки с координатами (х, у) может быть указано с помощью функции settextjustify. Если текст ие умещается в текущем графическом окне, то он обрезается на краях окна. Пример Вычерчивание текста Jan Bielecki, усеченного до Jan Bi. /* OutTextXY */ finc1ude<graphics.h> tinc1ude<coniо.h> char *MyName « "Jan Bielecki"; main() { int Driver, Mode; unsigned xSize; Driver - DETECT;
468 Часть VI. Библиотеки initgraph(&Driver, SMode, ". if(graphresu1t()i “ grt ) exit(l); xSize “ textwidth(MyName); settextjustify(LEFT_TEXT, T0P_TEXT); outtextxy(getmaxx() - xSize/2, 0, MyName); /* Jan Bi */ whi1e(I kbhitO); closegraphQ; return 0; } pieslice Имя Заголовок Прототип Функция Замечания pieslice - вычерчивание и закрашивание сектора круга. void pieslice(int х, int у, int StAngle int EndAngle, int Radius) graph!cs.h Вычерчивание сектора круга с центром в точке (х, у) и радиусом Radius, начиная с угла StAngle и заканчивая углом EndAngle. Закрашивание сектора по шаблону, задаваемому с помощью функции setfillstyle или setfillpattern. Система должна находиться в графическом режиме. Если задать StAng1e=O и EndAngle=360, ТО будет вычерчен полный круг. В случае неудачного выполнения функции piesllce результатом функции graphresult является значение grNoScanMen - не хватает памяти для закрашивания области методом сканирования. Пример Вычерчивание шаблону. четверти круга и ее закрашивание по стандартному /* PieSlice */ # i nc 1 ude<graph i cs. h> finc1ude<conio.h> IMin() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresu1t()I 1 grOk) exit(l); pieslicefO,0,270,360,100);
17. Принципы описания 469 whi1e(I kbhitO); c1osegraph(); return 0; } putimage Имя Заголовок Прототип Функция Замечания Пример putimage - размещение изображения на экране, void putimagefint х, int у. void * BitMap, int Op) graphics.h Размещение на экране прямоугольного графического изображения, предварительно записанного в области памяти, указанной в аргументе BitMap. Изображение размещается таким образом, что левый верхний угол прямоугольника попадает в точку с координатами (х, у), а наложение изображения на экран происходит в соответствии со значением 0р. Система должна находиться в графическом режиме. Выполнение функции putimage не вызывает усечения изображения по краям графического окна. Если потребуется усечение, то выполнение функции не будет иметь никаких результатов. Исключение: если нижним краем окна является нижний край экрана, то изображение, выходящее за этот край (и только за него) размещается на экране и усекается. Способ наложения на экран (параметр ор) может быть задан следующими константами: COPY_PUT - операция mov, XOR PUT - операция хог, ORPUT - операция or, AND PUT - операция and, NOT_ PUT - операция not. Вычерчивание на мониторе, управляемом адаптером Hercules, двух кругов таким образом, чтобы их общая часть имела цвет фона. /* Putimage */ fincludecgraphics.h> #inciude<conio.h> main() { int Driver, Mode; int xCor, yCor; int xCen, yCen; void * Ref; Driver = DETECT; initgraph(&Driver, &Mode, ". .");
470 Часть VI. Библиотеки if (graphresultOl “ grOk) exit(l); xCen « getmaxx()/2; yCen • getmaxy()/2; circ1e(xCen, yCen, 50); floodfillfxCen, yCen, getmaxcolorO); xCor - xCen - 100; yCor « yCen - 100; Ref « iaalloc( images ize(xCor, yCor, xCor + 200, yCor + 2D0)); getimage(xCor, yCor, xCor + 200, yCor + 200, Ref); putimage(xCor + 20, yCor + 20, Ref.XOR_PUT). while(I kbhitO); closegraphQ; return 0; } putpixel Имя t putpixel - вывод точки. Заголовок void putpixeUint x, int y, int Color) Прототип graphics, h Функция Вывод на экран точки с координатами (х, у) с цветом, имеющим номер Color. Замечания Система должна находиться в графическом режиме. Пример Вычерчивание отрезка прямой линии вдоль верхнего края экрана. /* PutPixel */ finc1ude<graphics.h* #inc1ude<coniо.h> nain() { int Driver, Mode; unsigned i; Driver - DETECT; initgraph(&Driver, &Mode, ”. ."); if (graphresultOl = grOk) exit(l); for(i « D; i<= getmaxx(); i++) putpixe1(1, 0, getmaxcolorO); whi1e(I kbhitO); closegraphO; return 0; }
17. Принципы описания 471 puttext Имя puttext - размещение текста в выбранном прямоугольнике. Заголовок int puttext(int xl, int yl, int x2, int y2, void *TextString) Прототип conio.h Функция Размещение текста, указанного в аргументе Textstring, в прямоугольнике, противоположные по диагонали ве ршины которого имеют координаты (xl, yl) и (х2, у2). Замечания Результатом функции является величина типа (int), которая в случае удачного выполнения операции принимает значение, отличное от 0. Пример Запоминание, а затем вывод на экран последовательности знаков. находящихся в произвольно выбранном прямоугольнике. /* PutText */ finclude<conio.h> char Family[100]; main() { clrscr(); cprintf("Ewa \r\n"); cprintf("Iza \r\n"j; cprintf("Jan”); gettext(l, 1, 4, 3, Family); clrscrf); puttext(5, 5, 8, 7, Family); return 0; } rectangle Имя Заголовок rectangle - вычерчивание прямоугольника, void rectangle(int xl, int yl, int x2, int y2) Прототип Функция graphics.h Вычерчивание прямоугольника, противоположные по диагонали вершины которого имеют координаты (xl, yl) и (х2, у2). Замечания Система должна находиться в графическом режиме. Пример Вычерчивание прямоугольника, сторонами которого являются края экрана.
472 Часть VI. Библиотеки /* Rectangle */ f1nc1udecgraph1cs.h> finc1ude<con1о.h> main() { int Driver, Mode; Driver - DETECT; initgraph(&Driver, &Mode, ". .”); if(graphresult()l « grOk) exit(l); rectangle(O, 0, getmaxx(), getmaxyO); whiled kbhitO); closegraphO; return 0; } я registerbgidriver Имя registerbgidriver - подтверждение графического драйвера. Заголовок » int registerbgidriver(void(*Driver)(void)) Прототип graphics.h Функция Подтверждение того, что в области оперативной памяти, указанной в аргументе Driver, находится программа графического драйвера. Замечания В случае неудачного выполнения функции registerbgidriver результатом функции graphresult буде' значение grlnvalidDriver - неподходящий графический драйвер. Пример Неавтоматическое подтверждение графического драйвера. /* RegisterBGIDriver */ finclude«graphics.h> f1nclude<conio.h> #include<stdio.h> main() { int Driver, Mode; /**i/r*nfc*^r*********** *! I* BINOBJ HERC */ /* Project: */ /* 1.source.C */ /* 2.GRAPHICS.LIB */ /* 3. HERC.OBJ */ y****************** *!
473 17. Принципы описания Driver = registerbgidriver((void(*)(void))Herc_driver) if(Driver < 0){ cprintf("Error: Xs", grapherrornsg(graphresult())) exit(13); } Driver - HERCHONO; Mode - HERCMONOHI; initgraph(&Driver, &Mode, ". if(graphresult()l - grOk) exit(l); settextstyle(GOTHIC_FOHT, HORIZJJIR, 4); settextjust ify(LEFT_TEXT, TOP_TEXT); outtext("Driver reg1stered"); while(! kbhitO); closegraph(); return 0; } registerbgifont Имя Заголовок Прототип Функция Замечания registerbgifont - подтверждение шрифта, int registerbgifont(void (* Font)(void)) graphics.h Подтверждение того, что в области оперативной памяти, указанной в аргументе Font, находится определение шрифта. Формирование значения типа (int), равного внутреннему номеру этого шрифта. В случае неудачного выполнения функции registerbgifont результат функции graphresult примет одно из следующих значений: - слишком большое число заданных шрифтов, - неправильный шрифт, - неправильный иомер шрифта. grError grlnvalidFont grlnvalidFontNum Пример Неавтоматическое подтверждение шрифта. /* RegisterBGIFont */ #inc1ude«graphics.h> finclude«conio.h> #include<stdio.h> main() { int Driver, Mode; int i -0;
474 Часть V/. Библиотеки /* BINOBJ GOTH */ /* BINOBJ TRIP */ /* Project: */ /* 1. source.C */ /* 2. GRAPHICS.LIB */ /* 3. GOTH.OBJ */ /* 4. TRIP.OBJ */ reg i sterbg ifont(goth1c_font); reg1sterbg1font(tr i plex_font); Driver - DETECT; initgraph(&Driver, &Mode, ”. ."); if(graphresylt()l = grOk) exit(l); do { i++; clearviewport(); if(i ft 1){ settextstyle(GCTHIC_FONT.HORIZ_DIR 5); outtext("Gothi c"); } else { settextstyle(TRIPLEX_FONT, HORIZJJIR, 5); outtext("Triplex"); } sleep(l); } while(l kbhitO); closegraphO; return 0; restorecrtmode Имя Заголовок restorecrtmode - восстановление текстового режима, void restorecrtmode(vo i d) Прототип Функция graphics.h Восстановление текстового режима, действовавшего перед установлением графического режима. Замечание Система должна находиться в графическом режиме. Применение функции restorecrtmode поочередно с функцией setgraphmode создает возможность удобного переключения системы между текстовым и графическим режимами. Пример Вычерчивание, а затем вывод надписи Jan Bielecki.
17. Принципы описания 475 /* RestoreCrtMode */ #1ncludecgraphics.h> #include<conio.h> main() } int Driver, Mode; Driver - DETECT; initgraph(&Driver, BMode, ". if(graphresult()l » grOk) exlt(l);- outtext("Graphics node: Jan Bielecki"); while(l kbhit()); (void)getch(); restorecrtmode(); cprintf("Text mode: Jan Bielecki"); return 0; } sector Имя Заголовок sector - вычерчивание сектора эллипса, void far sector(int х, int у, int StAngle, int EndAngle, int xAxis, int yAxis) Прототип Функция graphics.h Вычерчивание закрашенного сектора эллипса с центром в точке (х. у), начальным и конечным угла- ми дуги StAngle и EndAngle и полуосями xAxis и yAxis соответственно. Замечания Система должна находиться в графическом режиме. Сектор закрашивается по шаблону, заданному с помощью процедур setfillstyle и setfillpattern. Пример Вычерчивание окружности. сектора, описанного дугой эллипса, по форме, близкой к I* Sector */ flnclude«graph i cs.h> finclude«conio.h> main() { int Driver, Mode; Driver - DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresu!t()l - grOk) exit(l); sector(getmaxx()>> 1, getmaxyO» 1,
476 Часть VI. Библиотеки 0,270, 150, 100); while(l kbhitO); (void)getch(); closegraph!); return 0; setactivepage Имя Заголовок setactivepage - выбор активной страницы, void setactivepage(int Page) Прототип » Функция graphics.h Выбор страницы с номером Page в качестве активной страницы. Замечания Система должна находиться в графическом режиме. После выполнения функции setactivepage все графические операции будут относиться к странице с указанным номером. Возможность выбора активной ♦ страницы существует только в адаптерах EGA, VGA и Hercules. Пример Вычерчивание диагонали экрана на неактивной странице, а после нажатия произвольной клавиши ее вывод на экран. /* SetActivePage */ #inc1ude«graphics.h> #include<conio.h> main!) { int Driver, Mode; Driver = HERCMONO; Mode - HERMONOHI; initgraph(&Driver, &Mode, ”. ."); if(graphresult()i = grOk) exit(l); setvisuaIpage(0); setactivepage(O); outtext(“Press any key"); setactivepage!1); line(0, 0, getmaxx(), getmaxyO); while!! kbhitO); (void)getch(); setv i sua1page(1); whilefl kbhitO); closegraphO; return 0; }
17. Принципы описания 477 setallpalette Имя Заголовок Прототип- Функция Замечания setallpalette - изменение цветов, соответствующих позициям палитры. void setallpalette(struct palettetype *Palette) graphics.h Присвоение позициям палитры идентификаторов цветов, определяемых полями структуры типа (struct palettetype), указанной в аргументе Palette. Система должна находиться в графическом режиме. Тип (struct palettetype) определен следующим образом: struct palettetype { unsigned char size; signed char colors[16]; } Поле size задает размер палитры, а поле colors - идентификаторы цветов. Если элемент массива colors имеет значение -1, то присвоенный соответствующей позиции палитры цвет не изменяется. Идентификаторы цветов могут быть следующими: BLACK - черный BLUE - голубой GREEN - зеленый CYAN - бирюзовый RED - красный MAGENTA - ярко-красный BROWN - коричневый LIGHTGRAY - светло-серый DARKGRAY - темно-серый LIGHTBLUE - светло-голубой LIGHTGREEN - светло-зеленый LIGHTCYAN - светло-бирюзовый LIGHTRED - светло-красный LIGHTMAGENTA - светло-ярко-красный YELLOW - желтый WHITE - белый Пример Вычерчивание голубой главной диагонали на экране монитора, управля- емого адаптером EGA, а затем изменение цвета диагонали на красный за счет смены палитры. /* SetAllPalette */ finclude<graphics.h> #include<conio.h>
478 Часть VI. Библиотеки struct palettetype Palette; main() { int Driver, Mode; detectgraph(&Driver, AMode); if((graphresult()l - grOk) || (Driver I - EGA)){ cprintf("Failure"); exit(l); } setpalette(l, BLUE); setcolor(l); line(0, 0, getmaxx(), getmaxyO); while(l kbhitO); (void)getch(); Palette.colors[0] = -1; Palette.co1ors[l] = RED; seta1Ipalette(&Palette); while(l kbhit()); closegraph(); return 0; setaspectratio Имя Заголовок Прототип Функция Замечания setaspectratio - задание коэффициента сжатия экрана, void far setaspectratio(int xAsp, int yAsp) graphics.h Задание коэффициента сжатия графического экрана. Система должна находиться в графическом режиме. Текущую величину коэффициента сжатия можно определить с помощью функции getaspectratio. Пример Вычерчивание неискаженной окружности на мониторе экрана, управляемого адаптером Hercules. /* SetAspectRatio */ #1nc1ude«graphics.h> #include<conio.h> main() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, AHode, ". .”); if(graphresult()l - grOk) exit(l); setaspectratio(2, 3); circle(getmaxx()>> 1, getmaxyO » 1. 100);
17. Принципы описания 479 whi 1е(» kbhitO); (void)getch(); closegraphO; return 0; } setbkcolor Имя Заголовок Прототип Функция Замечания setbkcolor - изменение цвета фона. void setbkcolor! int Color) graphics.h Изменение цвета фона на тот цвет, который соответствует позиции с номером Color в текущей палитре. Исключение: если Color =0, то цвет фона изменится на черный. Система должна находиться в графическом режиме. Пример Выбор красного, управляемого адаптером EGA. а затем черного цвета фона на экране монитора. г /* SeBkColor */ #include<graphi cs.h> #include*conio.h» main() { int Driver, Mode; detectgraph(&Driver, &Mode); if( (graphresultOl = grOk)j J (Driver! “ EGA)){ cprintf(“Failure"); exit(l); } setpalette(l, RED); setbkcolor(l); while(! kbhitO); (void)getch(); setbkcolor(O); while(! kbhitO); closegraphO; return 0; } setcolor Имя Заголовок setcolor - выбор номера цвета для вычерчивания линии. void
480 Часть VI. Библиотеки setcolor(int Color) Прототип graphics.h Функция Выбор для вычерчивания линии цвета с номером Color. Замечания Система должна находиться в графическом режиме. Пример Вычерчивание отрезка прямой линии вдоль левого края экрана. /* SetColor */ finclude«graphics.h> f1nc1ude<con1о.h> main() int Driver, liode; Driver - HERCMONO; Mode - HERCMONOHI; inttgraph(&Driver, &Mode, ". ."); if(graphresult()l - grOk) clrscr(); cprintf("Failure"); exit(l); * } setcolor(getmaxcolor()); line(0, 0, 0, getmaxyi)); while(l kbhitO); closegraph(); return 0; } setfillpattem Имя Заголовок Прототип Функция Замечания setfillpattern - задание шаблона для закрашивания областей. void setfillpattern(char * Pattern, int Color) graphics.h Задание указанного в аргументе Pattern шаблона для закрашивания областей (fillpoly, bar, floodfill, bar3D, pieslice) и номера цвета Color этого шаблона. Система должна находиться в графическом режиме. Шаблон закрашивания является битовым прямоуголь ником 8x8 точек, представленным в 8-байт таблице, первый символ которой указан в аргументе Pattern. Пример Закрашивание экрана по произвольно выбранному шаблону.
17. Принципы описания 481 * SetFillPattern */ #include<graphics.h> #include<conio.h> unsigned char Pattern[]= { "\xl2\x6c\x44\x92" "\x44\x6c\x90\x00"}; rnainO { int Driver, Mode; Driver = DETECT; initgraph(&Driver, AMode, ". ."); if(graphresult()l = grOk) exit(l); setfi1lpattern(Pattern, getmaxcolor()); floodfill(0, 0, getmaxcolorO); while(l kbhitO); closegraphO; return 0; } setfillstyle Имя Заголовок Прототип Функция Замечания setfillstyle - задание стандартного шаблона закрашивания областей. void setfillstyle(int Pattern, int Color) graphics.h Задание номера Pattern шаблона для закрашивания областей (fillpoly, floodfill, bar, bar3d, pieslice) и номера цвета закрашивания. Система должна находиться в графическом режиме. Номера шаблонов соответствуют следующим возможным способам закрашивания: EMPTYFILL SQLIDFILL LINE_FILL LTSLASHFILL SLASHFILL BKSLASH_FILL LTBKSLASH_FILL HATCH_FILL XHATCH_FILL INTERLEAVE_FILL WIDEDOT_FILL CLOSEDOT_FILL - цветом фона, - непрерывно, - утолщенными горизонтальными линиями, - наклонными линиями, - утолщенными наклонными - линиями, - утолщенными диагональными линиями, - диагональными линиями, - вертикальной сеткой, - диагональной сеткой, - переплетенными линиями, - точками, - точками с повышенной плотностью.
482 Часть VI. Библиотеки Пример Демонстрация стандартных шаблонов закрашивания областей. /* SetFillStyle */ finclude<graphics.h> #include<conio.h> main() { int Driver, Mode; int Num = XHATCHFILL; Driver « DETECT; initgraph(&Driver, &№de, ". ."); if(graphresult() != grOk) exit(l); d°{ » setfillstyle(Num, getmaxcolorO); floodfill(D, 0, getmaxcolorO); while(! kbhitO); (void)getch(); restorecrtmode(); cscanf("Xd", &Nurc); setgraphmode(Mode); } while(Numf; closegraphO; return 0; } setgraphbufsize Имя Заголовок Прото:'Un Функция Замечания setgraphbufsize - задание размера буфера, используемого для закрашивания областей, void setgraphbufsize(unsigned BufSize) graphics.h Задание размера буфера, используемого для закрашивания областей, равным BufSize байт. Система должна находиться в графическом режиме. Размер буфера должен быть задан перед вызовом функции initgraph. Место для буфера выделяется в “куче“. Принимаемый по умолчанию размер буфера составляет 4 Кбайт. Пример Выделение графического буфера для правильного закрашивания многоугольника с большим числом вершин. /* SetGraphBufSize */ t1nc1ude<graph1cs.h>
17. Принципы описания 483 #include<conio.h> #1nclude<math.h> ^define dx 4 fdefine dy 5 fdefine step 20 nain() { int Driver, Mode; int i.j; setgraphbufsize(20000); Driver - HERCMONO; Node = HERCMONOHI; initgraph(&Driver, KMode, ”. ."); if(graphresult()l = grOk) exit(l); for(j » 1; j <= floor((float)getmaxy()/siep); j++){ moveto(0,j * step); for(i=l; i <= floor(2.0/3 * getmaxx()/dx); i++){ if(i & 1) linerel(dx, +dy); else linerel(dx, -dy); } } floodfill(getmaxx(), 0, getmaxcolorO); sleep(l); closegraph(); return 0; } setgraphmode Имя Заголовок Прототип- Функция setgraphmode - переключение системы в графический режим. void setgraphmode(int Mode) graphics.h Переключение системы в графический режим с номером Mode, graphdefaults. а затем неявное выполнение функции CGACO - 320x200, палитра 0 CGAC1 - 320x200, палитра 1 CGAC2 - 320x200, палитра 2 CGAC3 - 320x200, палитра 3 CGAHI - 320x200, 1 страница MCGACO - 320x200, палитра 0 MCGAC1 - 320x200, палитра 1
484 Часть VI. Библиотеки MCGAC2 MCGAC3 MCGANED MCGAHI EGALO EGAHI EGA64L0 EGA64HI EGAMONOHI Я HERCMONOHI АТТ400С0 АТТ400С1 АТТ400С2 АТТ400СЗ » ATT400HED ATT400HI VGALO VGAMED VGAHI PC3270HI IBM8514LO IBM8514HI Пример Вывод и вычерчивание цифры. /* SetGraphMode */ f i nc 1 ude<graph i cs. h> finclude<coniо.h> main() { int Driver, Mode; int Num; char Gothic[5]; char Value[15]; Driver « DETECT; initgraph(&Driver, AMode, ". if(graphresult() != grOk) rxit(l); restorecrtmode!); - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница - 640x480, 1 страница - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x200, 16 цветов, 1 страница - 640x350, 4 цвета, 1 страница - 640x350, 64К на плате адаптера, 1 страница 256К на плате адаптера 2 страницы - 720x348, 2 страницы - 320x200, палитра 0 - 320x200, палитра 1 - 320x200, палитра 2 - 320x200, палитра 3 - 640x200, 1 страница - 640x400, 1 страница - 640x200, 16 цветов, 4 страницы - 640x350, 16 цветов, 2 страницы - 640x480, 16 цветов, 1 страница - 720x350, 1 страница - 640x480, 256 цветов - 1024x768, 256 цветов
17. Принципы описания 485 cscanf("Xd", &Num); setgraphmode(Mode); settextstyle(DEFAULT-FONT, H0RIZ_DIR, 3); settextjustify(LEFT_TEXT, TOP_TEXT); itoa(Num, Gothic, 10); strcpy(Value, "Value « "); strcat(Value, Gotic); outtext(Value); while(l kbhitO); closegraph(); return 0; } setlinestyle Имя Заголовок set linestyle - выбор вида линии, void setlinestyle(int LineStyle, unsigned Pattern, int Thickness) Прототип Функция graphics.h Выбор вида линии с помощью аргумента LineStyle и толщины линии с помощью аргумента Thickness. В случае если LineStyle—USERBIT.LINE, то вид линии определяется в соответствии со значениями битов в аргументе Pattern. Замечания Система должна находиться в графическом режиме. Номера видов и толщин линий соответствуют следу- ющим возможным вариантам: Вид линии SOLIDJ.INE - сплошная линия DOTTED-LINE - точечная линия CENTER-LINE - штрихпунктирная линия DASHED-LINE - пунктирная линия USERBIT_LINE - линия, задаваемая пользователем. Толщина линии NORHJWIDTH - тонкая линия, THICK_WIDTH - утолщенная линия. Пример Вычерчивание окружности и прямой утолщенными точечными линиями, /* SetLineStyle */ finclude<graphics.h> #1nc1ude<coniо.h> main() {
486 Часть VI. Библиотеки int Driver, Hade; Driver - DETECT; initgraph(&Driver, &Mode, ”. .”); if(graphresult()l = grOk) exit(l); setlinestyle(DOTTED_LINE, 0, THICK_WIDTH); circle(getmaxx()/2, getmaxyO/2. 100); line(0, 0, getmaxxO, 0); while(l kbhitO); closegraph(); return 0; } setpalette Имя setpalette - изменение цвета, соответствующего одной Заголовок из позиций палитры, void Прототип setpalette(int ColorNum, int Color) graphics.h Функция • Присвоение идентификатора цвета Color позиции Замечания ColorNum палитры. Система должна находиться в графическом режиме. Изменение цвета отображается на экране сразу же после выполнения функции setpalette. Идентифика- торы цветов могут иметь следующие значения: BLACK - черный BLUE - голубой GREEN - зеленый CYAN - бирюзовый RED - красный MAGENTA - ярко-красный BROWN - коричневый LIGHTGRAY - светло-серый DARKGRAY - темно-серый LIGHTBLUE - светло-голубой LIGHTGREEN - светло-зеленый LIGHTCYAN - светло-бирюзовый LIGHTRED - светло-красный LIGHTMAGENTA - светло-ярко-красный YELLOW - желтый WHITE - белый. Пример Вычерчивание отрезка прямой линии красного цвета на экране монитора, управляемого адаптером EGA, а затем изменение этого цвета на зеленый при нажатии произвольной клавиши.
17. Принципы описания 487 /* SetPalette */ #include<graphics.h> f i nclude<conio. h> main() { int Driver, Mode; detectgraph(&Driver, &Mode); if((graphresult()l « grOk)|j(Driver! = EGA)){ cprintf("Failure"); exit(l); setpalette(l, RED); setcolor(l); ,.®»'line(0, 0, getmaxx(), 0); >Bwhile(l kbhitO); (void)getch(); setpalette(l, GREEN); wh‘le(! kbhitO); closegraphO; return 0; } setrgbpalette Имя setrgbpalette - определение палитры для адаптера IBM 8514. Заголовок void setrgbpalette!int Color, int Red, int Green, int Blue) Прототип Функция graphics.h Определение компонентов цвета Color с помощью шести старших битов данных (char)Red, char(Green) и (char)Blue. Замечания Необходимо, чтобы 0 Color <« 255. Пример Вычерчивание диагонали экрана произвольного цвета на мониторе, управляемом адаптером IBM 8514. /* SetRGBPalette */ finc1ude<graphics.h> findude<conio.h> ♦define Red 12 ♦define Green 13 ♦define Blue 12 main() { int Driver, Mode; detectgraph(&Driver, &Mode); if((graphresul|t()! = grOk)]
488 Часть VI. Библиотеки (Driver! - IRM8514)){ cprintf("Icorrect grahpics environment"); exit(13); } Mode « IBM8514HI; initgraph(&Driver, KMode, ". ."); if(graphresult() 1= grOk) exit(l); setrgbpalette(l, Red «2, Green « 2, Blue « 2); line(0, 0, getmaxx(), getmaxyO); sleep(l); closegraphO; return 0; } settextjustify 9 Имя Заголовок Прототип • Функция Замечания settextjustify - задание способа выравнивания графических текстов.. void settextjustify(int Horiz, int Vert) graphics.h Задание способа выравнивания графических текстов по горизонтали с помощью аргумента Horiz и задание способа их выравнивания по вертикали с помощью аргумента Vert. Система должна находиться в графическом режиме. Выравнивание производится относительно точки, отмеченной графическим курсором (функция outtext) или относительно точки с заданными координатами (функция outtextxy). Выравнивание по горизонтали и вертикали может быть выполнено следующими способами: Выравнивание по горизонтали: LEFTTEXT - выравнивание по левому краю, CENTER_TEXT - выравнивание по центру, RIGHTJEXT - выравнивание по правому краю, Выравнивание no вертикали, ВОТТОМ_ТЕХТ - выравнивание снизу, CENTER_TEXT - выравнивание по центру, TOP_TEXT - выравнивание сверху. Пример Вывод на экран надписи Izabela с выравниванием в экрана. правый нижний угол
17. Принципы описания 489 /* SetTextJustify */ #include<graphics.h> #include<conio.h> main() { Int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresult()l = grOk) exit(l); settext justify(RIGHTTEXT, BOTTOM TEXT); settextstyle(TRIPLEX_FONT, HORIZJJIR, 5); □uttextxy(getmaxx(), getmaxyO, "Izabela"); Mhile(l kbhitO); closegraphO; return 0; } settextstyle Имя settextstyle - выбор шрифта, направления вычерчивания символов текста и их размера. Заголовок void settexStylefint Font, int Direction, int CharSize) Прототип- Функция graphics.h Выбор шрифта Font, направления вывода символов Direction и размера символов CharSize. Замечания Система должна находиться в графическом режиме. Могут быть выбраны следующие виды шрифтов: DEFAULT_FONT - шрифт, принимаемый по умолча нию и определяемый шаблоном 8x8 бит, TRIPLEX FONT - тройной штриховой шрифт, SHALL FONT - индексный штриховой шрифт, SANSSERIF_FONT - штриховой шрифт сансериф, GOThIC_FONT - готический штриховой шрифт. Штриховые шрифты характеризуются тем, что увели чение их размера не ухудшает вида литер. Можно выбрать следующие направления вывода символов: HORIZDIR - по горизонтали (слева направо), VERTJHR - по вертикали (снизу вверх). Литеры штриховых шрифтов можно растягивать по вертикали и по горизонтали (см. функцию setusercharsize). Это требует применения параметра Size со значением
490 Часть VI. Библиотеки USERCHARSIZE - программируемый размер. Поскольку виды шрифтов обычно загружаются с магнитного диска, то в случае неудачного выполнения функции settextstyle результатом функции будет одно из следующих значений: grFileFound - отсутствие файла, определяю щего шрифт, grNoFontMem - не хватает оперативной памяти для запоминания шрифта. Призер Вывод вертикальной надписи Kajusia с выравниванием в правый верхний угол экрана. /* SetTextStyle */ finclude<graphics.h> #include<conio.h> main() { int Driver, Mode; Driver = DETECT; initgraph(&Driver, &Mode, ". ."); if (graphresultOl = grOk) exit(l); settextjustify(RIGHT_TEXT, TOPTEXT); settextstyle(TRIPLEX_FONT, VERTJJIR, 5); outtextxy(getmaxx(), "Kajusia"); while(! kbhitO); closegraph(); return 0; } setusercharsize Имя setusercharsize - задание коэффициентов растяжения литер в штриховых шрифтах. Заголовок void setusercharsize(int xMul. int xDiv, int yMul, int yDiv) Прототип Функция graphics.h Задание коэффициента растяжения по горизонтали'-й виде xMul/xDiv и коэффициента растяжения по вертикали в виде yMul/yDiv. Замечания Система должна находиться в графическом режиме. После выполнения функции setusercharsize следует вызвать функцию settextstyle с третьим аргументом, имеющим значение USER CHAR SIZE.
17. Принципы описания 491 Пример Вычерчивание дважды (растянутой по горизонтали и по вертикали) надписи Jan Bielecki. /* SetUserCharSize */ #include<graphics.h> #include<conio.h> main() { / int Driver, Mode; Driver - DETECT; initgraph(&Driver, &Mode, ". ."); if(graphresult()i = grOk) exit(l); settextjustify(LEFT_TEXT, TOP_TEXT); setusercharsize(3, 1, 1, 1); settextstyle(GOTHIC_FONT, H0RIZJ3IR, USER_CHAR_SIZE); outtext(*'Jan Bielecki"); settextjustify(LEFT_TEXT, BOTTOM TEXT); setusercharsize(l, 1, 3, 1); settextstyle(GOTHIC_FONT, HORIZ_DIR, USER_CHAR_SIZE); outtextxy(0, getmaxyO, "Jan Bielecki"); while(l kbhitO); closegraph(); return 0; } setviewport Имя Заголовок setviewport - задание графического окна, void setviewport(int xl, int yl, int x2, int y2, int Clip) Прототип Функция graphics.h Задание графического окна в виде прямоугольника с координатами противоположных по диагонали вершин (xl. yl) и (х2, у2). Аргумент Clip показывает, следует ли усекать фрагменты, выходящие за пределы окна. Замечания После создания графического окна все графические операции (за исключением setviewport) выполняются в этом окне. По умолчанию принимается, что таким окном является весь экран. Пример Вычерчивание вертикального отрезка прямой линии в окне, составляющем правую половину экрана.
492 Часть VI. Библиотеки /* SetViewPort */ tinc1ude<graphics.h> #include<conio.h> fdefine ClipOn 1 main() { int Driver, Mode; Driver - HERCMONO; Mode - HERCMONOHI; initgraph(&Driver, &Mode, ". .”); if(graphresultQl ” grOk) exit(l); setviewport(getnaxx()/2, 0, getmaxxO, getmaxyO, ClipOn); line(0, 0, 0,^getmaxy()/3); while(l kbhitO); closegraph(); return 0; } setvisualpage Имя Заголовок Прототип Функция Замечания Пример Попеременная setvisualpage - выбор страницы, выводимой на экран, void setvisualpage(int Page) graphics.h Выбор для индикации на экране страницы с номером Раде. Система должна находиться в графическом режиме. Возможность выбора выводимой на экран страницы относится только к адаптерам EGA, VGA и HERCULES. индикация надписей Izunia и Kajunia. /* SetVisual Page */ finclude<graphics.h> #include<conio.h> main() { int Driver, Mode; int Num - 0; int i; Driver - DETECT; initgraph(&0river, &Mode, ”. ."); if(graphresult()l = grOk) exit(l); settextjustify(LEFT_TEXT, TOP_TEXT);
17. Принципы описания 493 settextstyle(GOTHIC_FONT, HORIZ_DIR, 6); outtextxy(0, 0, "Каjunta"); setact1vepage(l); settextjustify(RIGHT_TEXT, TOP_TEXT); outtextxy(getmaxx(), 0, "Izunia"); for(i=l; i<=20; i++){ Num » 1 - Num; setvisualpage(Num); sleep(l); } closegraphO; return 0; } setwritemode Имя Заголовок setwritemode - задание режима вычерчивания линий, void setwritemode(int Mode) Прототип Функция graphics.h Задание с помощыр аргумента Mode одного из двух режимов вычерчивания линий. Замечания Система должна находиться в графическом режиме. Могут быть заданы следующие режимы вычерчивания линий: COPY_PUT - с необратимым наложением, XOR PUT - с обратимым наложением. Пример Вычерчивание отрезка прямой линии, перемещающегося на фойе сектора эллипса. /* SetWrtteMode */ #1nc1ude<graph1cs. h> finc1ude<coniо.h> main() { int Driver, Mode; Driver » DETECT; initgraph(&Driver, &Mode, ". ."); if (graphresultO 1= grOk) exit(l); sector(getmaxyO»l, getmaxyO»l; 0, 270, ISO, 100); setwritemode(XORPUT); for(i=0; i < =144; i++){ line(0, getmaxyO, 5, * i, 0);
494 Часть VI. Библиотеки line(0, getmaxyO. 5 * i.0); } whiled kbhitO); (void)getch(); closegraph(); return 0; } textattr Имя textattr - изменение признаков символа, фона и мерцания. Заголовок void Прототип * Функция textattr(int NewAttr) conio.h Присвоение переменной TextAttr признаков цвета символа, цвета фона и мерцания, определяемых аргументом NewAttr. Замечания Переменная TextAttr является 8-бит переменной • |B|b|b|b|f|f|f|f| 1 1 Бит В связан с мерцанием, биты b указывают цвет фона, а биты f - цвет символа. Пример Вывод мерцающего негативного (inverse.video) изображения надписи Hello в левом верхнем углу экрана монитора, управляемого адаптером HERCULES. /* TextAttr */ finclude<conio.h> rnainO { int Driver Mode; clrscrf); textattr(OxFO); cprintf("Hello”); return 0; } textbackground Имя Заголовок textbackground - задание цвета фона, void textbackground(int Color) Прототип Функция conio.h Присвоение переменной TextAttr признака цвета фона Color
495 Замечания 17. Принципы описания Признак цвета фона является трехбитовым, с его помощью могут быть заданы следующие цвета: BLACK BLUE GREEN CYAN RED MAGENTA BROWN LIGHTGRAY - черный - голубой - зеленый - бирюзовый - красный - ярко-красный - коричневый - светло-серый После задания цвета фона все выводимые на экран символы (в том числе и пробелы) будут демонстрироваться на этом фоне. Пример Вывод красной управляемого адаптером EGA. надписи Jan на зеленом фоне на экран монитора, /* TextBackground */ # i nc1ude<graphics.h> finclude<conio.h> main() { int Driver. Mode; clrscrf); detectgraph(&Driver, &Mode); if((graphresult()l = grOk)j (Driver! = EGA)){ cprintf("Fa1 lure"'); exit(l); } textbackground(GREEN); textcolor(RED); cprintff'Jan"); return 0; } textcolor Имя Заголовок Прототип Функция Замечания textcolor - задание цвета символа. void textco]or(int Color) conio.h Присвоение переменной TextAttr признака цвета символа и признака мерцания, определяемых в аргументе Color. Признак цвета символа является трехбитовым, а
496 Часть VI. Библиотеки признак мерцания - однобитовым. С помощью этих признаков можно задать следующие значения: BLACK - черный BLUE - голубой GREEN - зеленый CYAN - бирюзовый RED - красный MAGENTA - ярко-красный BROWN - коричневый LIGHTGRAY - светло-серый DARKGRAY - темно-серый LIGHTBLUE - светло-голубой LIGHTGREEN - светло-зеленый s LIGHTCYAN - светло-бирюзовый LIGHTRED - светло-красный LIGHTMAGENTA - светло-ярко-красный YELLOW - желтый WHITE - белый Признак мерцания BLINK - мерцание После задания цвета символа все выводимые на экран символы будут иметь этот цвет. Пример Вывод красной мерцающей надписи Jan на зеленом фоне на экран монитора, управляемого адаптером EGA. /* TextColor */ finclude<graphics.h> #irx:lude<conio.h> main() { int Driver, Mode; clrscr(); detectgraph(&Driver, &Mode); if((graphresult()l - grOk)" (Driver! « EGA)}{ cprintf("FAILURE"); exit(l); ) textbackground(GREEN); textcolor(RED + BLINK); cprintf("Jan"); return 0; }
17. Принципы описания 497 textheight Имя Заголовок textheight - задание высоты текста, int textheight(char * Textstring) Прототип Функция graphics.h Формирование значениия типа (int), равного вертикальному размеру (измеряемому числом точек) текста, указанного в аргументе Textstring. Замечания Система должна находиться в графическом режиме. Результат учитывает коэффициент растяжения символов по вертикали. Пример Задание высоты текста, вычерчиваемого готическим шрифтом /* TextHeight */ finclude<graphics.h> #include<conio.h> main() { int Driver, Mode; int Height; Driver - DETECT; initgraph(&Driver, &Mode, ". if(graphresu1t()l exit(l); grOk) settextstyle(GOTHIC_FONT, HORIZJBIR, 1); Height » textheight (" * restorecrtmode(); cprintf("Height - Xd", Height); /* if HERC « 16 */ return 0; } textmode Имя Заголовок textmode - установка текстового режима, void textmode(int Mode) Прототип Функция conio.h Переход от текущего текстового режима к текстовому Замечания режиму Mode. Новый текстовой режим может быть указан с помощью следующих констант: BW40 - 40x80, черно-белый C40 - 40x25, цветной BW80 - 80x25, черно-белый
498 Часть VI. Библиотеки С80 MONO - 80x25, цветной - 80x25, черно-белый (на монохроматическом мониторе) Дополнительную возможность указания режима дает применение константы LASTMOOE. В случае указания ее в качестве аргумента функции textnode происходит переключение в режим, действовавший до начала выполнения программы. Следует отметить, что сразу же после вызова функции textnode текущее текстовое окно распространяется на весь экран и очищается, а переменной TextAttr присваиваются значения признаков как после вызова функции nomvideo. Пример Вывод на экран надписи Jan повышенной яркости, а после нажатия клавиши Enter - надписи Ewa в том режиме, который действовал до начала выполнения программы. /* TextMode */ #1nclude<conjo.h> #include<stdio.h> main() { clrscrO; hightvideo(); cprintff'Jan"); while(getchar() I = '\n'); textnode(LASTHODE); cprintf("EWA"); return 0; } textwidth Имя Заголовок Прототип Функция Замечания textwidth - задание ширины текста, int textwidth(char * Textstring) graphics.h Формирование значения типа (int), равного горизонтальному размеру (измеряемому числом точек) текста, указанного в аргументе Textstring. Система должна находиться в графическом режиме. Результат учитывает коэффициент растяжения символов по горизонтали. Пример /* Textwidth */
17. Принципы описания 499 tinc1ude<graphics.h> tinc1ude<coniо.h> main() { int Driver, Hode; int Width; Driver » DETECT; initgraph(&Driver, &Mode, ". if(graphresu1t()l » exit(l); grOk) settextstyle(GOTHIC_FONT, VERTDIR, 1); settextjustify(RIGHT_TEXT, BOTTOM_TEXT); outtextxy(getmaxx(), getmaxyO, "jb”); while(l kbhitO); (void)getchO; Width « textwidth("jb"); restorecrtmode(); cprintf("Width = Xd", Width); /* if HERC => 13 */ return 0; } wherex Имя wherex - сообщение о горизонтальной координате текстового курсора. Заголовок int wherex(woid) Прототип Функция. conio.h Формирование значения типа (int), равного горизонтальной координате текстового курсора. Замечания. Координата относится к текущему графическому окну. Левый верхний символ в окне имеет горизонтальную координату, равную 1. Пример .Вывод на экран надписи Jan и цифры 4. /* WhereX */ #include<conio.h> #include<stdio.h> main() { ClrSCrO; cprintf("Jan"); cprintf("Xd", wherex(J); /* 4 */ return 0; }
500 Часть VI. Библиотеки wherey Имя wherey - сообщение о вертикальной координате текстового курсора. Заголовок int wherey(void) Прототип conio.h Функция Формирование значения типа (int), равного вертикальной координате текстового курсора. Замечания Координата относится к текущему графическому «жну. Левый верхний символ в окне имеет вертикальную координату, равную 1. Пример Вывод на экран /* WhereY */ #1nclude<con1o.h> finclude<stdio.h> main() { clrscrf); cprintf("Jan"); [ надписи Jan и цифры 1. cprintf("Sd\n", return 0; } whereyO); /* 1 */ window Имя window - создание текстового окна. Заголовок void window(int xl, int yl, int x2, Int y2) Прототип conio.h Функция Формирование текстового окна в виде прямоугольника, противоположные по диагонали вершины которого имеют координаты (xl, yl) и (х2, у2) Замечания Координаты отсчитываются относительно экрана. Левый верхний угол экрана имеет координаты (1, 1). Самое малое окно состоит из одного столбца и одной строки. Окно, принимаемое по умолчанию, имеет координаты (1, 1, 80, 25) в режимах с шириной экрана 80 столбцов и координаты (1, 1, 40, 25) в режимах с шириной экрана 40 столбцов. В случае ошибочного указания координат окна выполнение функции window заканчивается безрезультатно. После создания текстового окна все текстовые координаты (за исключением упомянутых в функции window) отсчитываются относительно этого окна.
17. Принципы описания 501 Пример Создание текстового окна и размещение в нем случайно подобранных малых букв. /* Window */ #1nclude<conto.h> #include<stdio.h> tna1n() { Int 1; clrscrf); srand(O); wtndow(10, 10, 30, 20); for(1 - 1; 1 < = 200; !++){ gotoxy(rand()X81, rand() X 26); cprlntff'Xc", 'a' + rand() X 26); } return 0; } 17.2. Системная библиотека Системная библиотека состоит из функций стандарта ANSI (курсивом вы- делены функции, не реализованные в Турбо С), а также из избранных функций системы Турбо С. Функции ANSI abort abs acos asctime asin assert atan atan2 atexit atof atoi atol brearch calloc ceil clearerr clock cos cosh. crime difftime div exit exp fabs fclose feof ferror fflush fgetc fgetpos fgets floor fmod fopen fprintf fputc fputs fread free freopen frexp fscanf fseek fsetpos ftell fwrite getc getchar getenv gets gmtime isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit labs Idexp Idiv localeconv localtime log
502 Часть VI. Библиотеки loglO longjmp malloc тЫеп mbstovKs mbtowc memchr memcmp memcpy memmove memset mktlme modf onexit offsetof реггог pow printf putc putchar puts qsorl raise rand realloc remove rename rewind scanf setbuf setjmp setlocale settime setvbuf signal sin sinh sprintf sqrt srand sscanf strcat strchr strcmp strcoll strcpy ’* strcspn strerror strftime strlen strncat strncmp strncpy strpbrk strrchr strspn strstr strtod strtok strtol system tan tanh time tmpfile tmpnam tolower * toupper ungetc va-arg va-end va—start vfprintf vprintf vsprintf Функции Турбо bdos bdosptr cgets chdir coreleft cprintf cputs cscanf delay -EMIT farcalloc farcoreleft farfree fannalloc farrealloc findfirst findnext fnmerge fnsplit FP—OFF FP—SEG getch getdate gettime getvect inport inportd int86 int86x intdos intdosx intr itoa kbhit Itoa mkdir MK—FP movedata nosound outport outportb peek peekb poke poked putch putenv rmdir setdate settime setdisk setvect sleep sound ultoa ungetch unlink vfscanf vscanf vsscanf Использование системной библиотеки во многих случаях требует обраще- ния к предварительно объявленным типам и переменным. Вот наиболее важ- ные из них.
17. Принципы описания 503 Предварительно объявленные типы stdio.h typeredef long fpos_t; sdllb.h typedef unsigned size_t; typedef struct{ int quot, rem } divt; typedef struct{ long quot, retn; } ldiv_t; typedef void.(*atexit_t)(void); dme-h typedef long time_.t; typedef long clock_t; stdarg.h typedef void *va_11st; setjmp.h typedef struct{ unsigned j_sp, j_ss, j_flag, j_cs, jjp. j_bp. j_di, j_es, j_si, jds; } jmp_buf[l); Предварительно объявленные переменные errno Если выполнение функции, в описании которой упомянута переменная errno, завершится неудачно, то этой переменной будет присвоено значение, отличное от нуля и равное, например EDOM - ошибка области определения (domain error), ERANGE - ошибка диапазона (range error). -8087 Если в системе имеется арифметический сопроцессор, то этой переменной присваивается значение, отличное от нуля, а именно: 1 - для сопроцессора 8087, 2 - для сопроцессора 80287, 3 - для сопроцессора 80387.
504 Часть VI. Библиотеки В противном случае -8087 == 0. -fmode Если при вызове функции fopen не указан явно режим обра ботки файла (текстовый или бинарный), то режим определяет ся значением этой переменной (по умолчанию О—TEXT). —psp Значение переменной указывает номер сегмента, в котором находится PSP программы. -heaplen Значение переменной определяет размер ближней кучи. Ближняя “куча" существует только в моделях Tiny, Small и Medium. В моделях Small и Medium размер сегмента данных определяется по формуле w segment = global + heap + stack (глобальные данные, “куча“, стек). В модели Tiny как код, так и данные находятся в одном и том же сегменте, поэтому применяется формула * segment = 256+code+global+heap+stack (256 байт занимает PSP). —stklen * Значение переменной определяет размер стека (в байтах). По умолчанию принимается 4 Кбайт. —version Значение переменной определяет номер версии операционной системы DOS. Замечание. В стандартной версии Си ANSI интерпретацию имеет только переменная еггпо. abort ANSI Имя abort - преждевременное, аварийное завершение выполнения программы. Заголовок void abort(void) Прототип Функция stdlib.h, process.h *) Вывод сообщения в файл с идентификатором stderr и немедленное прекращение исполнения программы (без очистки буферов и закрытия файлов). Результат void Пример -------------------------------------- I Прототипы некоторых системных функций находятся в нескольких файлах заголовка. При написании программы в директиве процессора include можно использовать наиболее предпочти- тельную, исходя, например, из соображений меньшего объема оперативной памяти. - Прим. ред.
17. Принципы описания 505 Помещение последовательности символов Jan в файл DISK. /* abort */ /include «stdio.h» /include «stdlib.h» main() { FILE ‘Out - fopenf’DISK", "w"); fprintf(Out, "Jan”); fflush(Out); fprintffOut, "EWA"); abort!); return 0; } abs ANSI Имя Заголовок abs - определение абсолютного значения, int abs(1nt 1) Прототип Определение Функция Результат stdlib.h. math.h stdlib.h Формирование значения, равного lil. Величина типа (int), равная абсолютному значению аргумента. Исключение: abs(-32768) « -32768. Пример Использование макроопределения для вывода числа 13. /* abs */ /include «stdio.h» /include «stdlib.h» undef abs nain() { pr1ntf("%d", abs (-13)); retutn 0; } acos ANSI Имя Заголовок acos - вычисление арккосинуса, double acosfdouble х) Прототип Функция nath.h Формирование значения, равного arc cos х.
506 Часть VI. Библиотеки Результат Величина типа (double) со значением, равным арккосинусу аргумента. Это значение принадлежит диапазону О..р1. Замечания Если аргумент не принадлежит диапазону то результат имеет значение 0, a errno=ED0M. Пример Вывод числа 3.14. /* acos */ /include <stdio.h> /include <math.h> main() { printfC'% i?f". 3 * acos(0,5)); return 0; } a sc time ANSI Имя • Заголовок Прототип Функция Результат Замечания asctime - преобразование даты и времени в символьную форму. char * asctimefstruct tni *tm) time.h Преобразование даты и времени, выраженных с помощью полей структуры, указанной в аргументе tn, в последовательность символов вида, например, Sun Dec 13 06: 00: 00 1981 /п/ (название дня недели, название месяца, номер дня в месяце, время дня, год) с учетом того, что структура типа (struct tm) содержит следующие компоненты времени и даты: количество секунд с начала часа (min), количество часов после полуночи (hour), количество месяцев, начиная в января (mon), количество лет с начала века (year), количество дней после воскресенья (wday), количество дней с 1 января (yday), признак, указывающий, действует ли летнее время (tsdst). Значение типа (char *), указывающее на первый символ сформированной последовательности. Тип (struct tm) объявлен следующим образом: struct tm { int tm_sec, tm_min, tm_hour, tmmday.
17. Принципы описания 507 tm_mon, tnryear, tnwnday, tin fsdst; } Пример Вывод на экран надписи Sun 13 06 :00:00 1981 /* asctime */ finclude «time.h» finclude «stdio.h» struct tin TheDay = {0, 0, 6, 13, 12, 81}; main() { printf("% s”, asctime(&TheDay)); return 0; } asin ANSI Имя Заголовок asin - вычисление арксинуса, double asin(double х) Прототип math.h Функция Формирование значения, равного arc sin х. Результат Величина типа (double), равна арксинусу аргумента. Это значение принадлежит диапазону -pl/2..pi/2. Значения Если аргумент не принадлежит диапазону -1..1, то результат имеет значение 0, a errno=ED0M. Пример Вывод на экран числа 3.14. /* asin */ finclude «stdio.h» finclude «math.h» ma1n() { printf("% 2f". 2 * asin(l.O)); return 0; } assert ANSI Имя Включение assert - проверка правильности сравнения, assert.h
508 Часть VI. Библиотеки Заголовок Определение Функция /* void */ assert(/* int */ test) assert.h Проверка правильности сравнения (test) “ 1 в противном случае аварийное завершение выполнения программы, которому предшествует выдача сообщения Assertion failed: test file name, line number Замечания в котором пате - имя исходного файла, a number - номер строки в этом файле. Функция assert является макроопределением. Если ее включение произойдет в пределах макроопределе ния NDEBUG, то оно будет считаться не имевшим места. Пример Вывод надписи Assertion failed: Val -« O.file ASSERT.C, line 9 (предполагается, что программа находится в файле ASSERT.С) /* assert */ /include <assert.h> /include <stdio.h> main() { . int val » 13; /define NDEBUG assert(Val --0); printf("After first assert”); findef NDEBUG assertrt(Val ” 0); printf("After second assert"); return 0; } atan Имя Заголовок Прототип Функция Результат ANSI atan - вычисление арктангенса. double atan(double х) math.h Формирование значения, равного arc tg х. Величина типа (double) со значением, равным арктан генсу аргумента. Это значение принадлежит диапазону
17. Принципы описания 509 -pi/2..pi/2. Пример Вывод числа 3.14. /* aten */ ♦include <stdio.h> ♦Include _<math.h> raainf) { printff'X Zf”, 4 * atan(l.O)); return 0; } atanZ ANSI Имя Заголовок Прототип Функция Результат Замечания atanZ - вычисление арктангенса.. double atanZ(double y, double x) math.h Формирование значения, равного ars tg y/x. Величина типа (double) co значением, равным арктангенсу частного от деления первого аргумента на второй. Это значение принадлежит диапазону -pi/2..pi/2. Если оба аргумента имеют значение 0, то errno=ED0M. Пример Вывод числа 3.14. /* atanZ */ ♦Include <stdio.h> ♦Include <math.h> main() { printff'X Zf", 4 return 0; } * atanZ(l.l)); atexit ANSI Имя Заголовок Прототип atexit - определение действия, завершающего выполнение программы. int atexit(atexit_t fun) stdlib.h
510 Часть VI. Библиотеки Функция Результат Замечания Регистрация функции, определяющей действие, которое будет выполнено после завершения исполнения программы. Величина типа (int) со значение 0 в случае успешной регистрации действия и со значением, отличным от нуля, в противном случае. В случае регистрации более чем одного действия последовательность их выполнения будет обратной очередности их регистрации. Пример Вывод надписи Зуе. bye!, а затем надписи Second и надписи First. /* atexlt */ /* atan */ * finclude <stdl1o.h> finclude <std1o.h> void Flrst(vold) { pr!ntf("nF1rst”); } void Second(vcrtd) { prlntf'nSecond") } ma1n() { atexlt(Flrst); atexlt(Second); pr1ntf("\nBye, bye!"); return 0; } atof Имя Заголовок Прототип Функция Результат Замечания ANSI atof - преобразование последовательности символов в число с плавающей точкой. double atof(char *nptr) stdltb.h, math.h Преобразование строковой переменной, указанной в аргументе nptr, в число типа (double). Число типа (double), соответствующее данной строковой переменной. Преобразуемая последовательность символов может содержать следующие элементы: пробел, табуляция, знак числа, последовательность цифр, точка, последо- вательность цифр, малая или большая буква Е и по- казатель степени. Показатель степени может содер-
17. Принципы описания 511 жать знак + {плюс) или - {минус) и последователь- ность цифр. Преобразование заканчивается в тот мо- мент, когда встречается символ, не входящий в число. Пример Вывод числа -456.78 /* atof */ finclude <stdio.h> finclude <nath.h> main() { printf("% Zf”, atof(”-45.678e+01”)): return 0; } atoi Имя Заголовок Прототип Функция Результат Замечания ANSI atoi - преобразование строки символов в число типа (int) int atoi(const char *nptr) stdlib.h Преобразование строковой переменной, указанной в аргументе nptr, в число типа (int). Число типа (int), соответствующее данной строковой переменной. Преобразуемая строка может содержать следующие элементы: пробел и табуляция, знак числа, последовательность цифр. Преобразование заканчивается в тот момент, когда встречается символ, не входящий в число. Пример Вывод числа 45. /* atoi */ finclude <stdio.h> finclude <stlib.h> nain() { printf(”%d", atoi(" 45JB”)); return 0; }
512 Часть VI. Библиотеки atol ANSI Имя atol - преобразование строки символов в число типа (long). Заголовок long ato1(const char * nptr) Функция Преобразование строковой переменной, указанной в аргументе nptr, в число типа (long). Результат Число типа (int), соответствующей данной строковой переменной. Замечания Преобразуемая строка может содержать следующие элементы: пробел и табуляция, знак числа, последовательность цифр. Преобразование заканчиватеся в тот момент, когда встречается символ, не входящий в число. Пример Вывод числа -3. /* atol */ ♦include «stdjo.h» ♦include «stdlib.h» main() { printf(”%ld", atol(" -""Зх”)); return 0; } bdos TURBO Имя bdos - вызов функции системы DOS. Заголовок int Прототип bdosfint dosfun, unsigned dosdx, unsigned dosal) dos.h Функция Исполнение функции системы DOS с номером dosfun, связанной с прерыванием 0x21. Передача функции ар гументов, ожидаемых в регистрах DX(dosdx) и AL(dosal). Результат Число типа (int), оставляемое функцией Пример, в регистре АХ. Вывод надписи Current drive: d в которой d обозначает текущий дисковод (например, В).
17. Принципы описания 513 I* bdos */ finclude «stdio.h» finclude «dos.h» mini) { printf("Current drive: %c", 'A' + bdos(0xl9, 0, 0)); return 0; } bdosptr TURBO Имя Заголовок bdosptr - вызов системной функции, int bdosptr(int dosfun, void *arg, unsigned dosal) Прототип Функция dos.h Вызов системной функций с номером dosfun, связанной с прерыванием 0x21. Передача функции ожидаемых параметров: адреса arg и содержимого регистра Al - dosal. Результат Переменная типа (int), оставляемая функцией в регистре АХ. Пример Вывод надписи Izabela. /* bdosptr */ finclude «stdio.h» finclude «dos.h» nain() { (void)bdosptr(0x9. "Izabela$”, 0); return 0; } bsearch ANSI Имя Заголовок bsearch - бинарный поиск. void * bsearch(const void *key, const void *base, sizet nelen, sizet width, int (*fcmp)(const void *, const void *)); ' Прототип Функция stdlib.h Просмотр таблицы с целью обнаружения элемента, идентифицируемого заданным ключом. При этом
514 Часть VI. Библиотеки Результат я принимается, что base указывает первый элемент таблицы, пе!ем - число элементов в таблице, key - ключ, a width определяет размер ключа. Сравнение ключей производится с помощью функции, указыва- емой в fcmp. Эта функция должна быть подобрана таким образом, чтобы в случае ее вызова с двумя аргументами: Key (указание адреса искомого элемен- та) и Elm (указание адреса элемента таблицы) она выдавала результат, имеющий отрицательное, нулевое или положительное значение в зависимости от того, находится ли объект в области, указываемой Key, и является ли ои соответственно меиыпим, равным или большим объекта, находящегося в области, указы- ваемой Elm. Переменная типа (void *), указывающая найденный элемент таблицы или имеющая пустое значение - если такой элемент в таблице отсутствует. Пример Вывод буквы С. /* brearch */ finclude «stdio.h» finclude «stdlib.h» char Arr[3][2] == { {0, 'A’Hl, 'B'}. {2, 'C'}}; char Key[2] == {2}; main() { int Comp(); printf("%c", *(1 + (char *) bsearch(Key,Arr,3,2,Comp))); return 0; } int Comp(void *Key,void *Elm) { return *(char *)Key - *(char *)Elm; } ANSI calloc Имя Заголовок calloc - выделение памяти, void * calloc(size_t nelem, sizet elsize) Прототип Функция stdlib.h, alloc.h Выделение обнуленной области памяти, в которой можно разместить nelem объектов размером elsize каждый.
17. Принципы описания 515 Результат Переменная типа (void *), определяющая выделенную область памяти или принимающая значение NULL, если ее выделение невозможно. Замечания На самом деле выделяется область размера (unsigned)(nelen * elsize), а это ие то же самое, что nelem объектов размером elsize каждый. Пример Вывод надписи jb. /* calloc */ finclude «stdio.h» finclude «alloc.h> inain() { char *Ref = calloc(3,l); Ref[0] = 'j'; Ref[l] - 'b'; printf(”%s", Ref); return 0; } ceil ANSI Имя Заголовок ceil - округление в сторону увеличения, double ceil(double х) Прототип Функция math.h Формирование переменной типа (double), представляю щей собой наименьшее целое число, не меньшее х. Результат Число типа (double). Пример Вывод числа 5.00. /* ceil */ finclude «stdio.h» finclude «math.h» main!) { printf("%.2f", ceil(4.01)); return 0; }
516 Часть VI. Библиотеки cgets TURBO Имя. cgets - ввод последовательности символов с коисоли. Заголовок char * cgets(char *string) Прототип Функция conio.h Ввод с консоли ограниченного числа символов. Ввод прекращается после того, как встретится символ конца строки, или после ввода string [0] символов. Замена символа конца строки символом с кодом 0. Размещение введенных символов, начиная с string [2], и присвоение переменной string [1] значения, равного числу введенных знаков. Результат и Переменная типа (char *), которой соответствует первый введенный символ. Пример После ввода символов Kaja вывод надписи Kaja. /* cgets */ finclude «stdto.h» ♦include «conio.h» inain() { char Buffer[8]; ‘Buffer = 5; (void)cgets(Buffer); ‘(Buffer + 2 + Buffer[l]) '\0'; printf("\n%s". Buffer + 2); return 0; } chdir TURBO Имя Заголовок chdir - смена текущего каталога., int chdir(const char * path) Прототип Функция dir.h Назначение в качестве текущего каталога указанного диска каталога с именем path. Результат Переменная типа (int) со значением 0 в случае успе шного выполнения функции и со значением -1 в про тивном случае; при этом устанавливается также errno - ENDENT - дефектная дорожка. Замечания Если диск не указан, то по умолчанию принимается текущий диск.
17. Принципы описания 517 Пример Замена каталога текущего диска В на \TC20. /* chdir */ finclude <dir.h> finclude «stdio.h» main() { chdir(B:\\TC20"'); return 0; } ciearerr ANSI Имя ciearerr - обнуление указателей файла. Заголовок void ciearerr (FILE ‘stream) Прототип stdio.h Функция Обнуление указателя ошибки и указателя файла. Результат void конца Пример Вывод цифр 1 и 0, определяющих значение указателя конца выполнением и после выполнения функции ciearerr. файла перед /* ciearerr */ finclude «stdio.h» main() { FILE *0utIn; Outln == fopen("U0RK", ’V); fclose(Outln); Outln fopenC'WORK", "r”); (void)getc(Outln); printfC'Xd", 1Ifeof(Outln)); clearerr(Outln); printfC'Xd", 1 Ifeof (Outln)); return 0; } clock ANSI Заголовок Имя clock - сообщение о времени выполнения программы, long
518 Часть VI. Библиотеки Прототип Функция Результат Замечания clock(void) time.h Формирование переменной типа (long) со значением, равным произведению t*1000, где t - число секунд, прошедших с момента начала выполнения программы. Переменная типа (long) с указанным выше значением. Если процессор не имеет часов, то результатом функции clock являетя величина -1. Пример Определение среднего времени операции, состоящей из открытия и немедлен- ного закрытия файла. н /* clock */ finclude <time.h> finclude «stdio.h» fdefine Count ID main() { FILE *Doc; • int i; for(i = 1; i< Count; i++){ Doc = fopen(''WORK”, "w”); fclose(Doc); } printf("Time = %f ms", 100.0 * clock() / CLKTCK / Count; return 0; } coreleft Имя Заголовок Прототип Функция Результат TURBO core left - определение размера свободного места в “куче“. unsigned coreleft(void) alloc.h Оценка размера той части “кучи“, которая может быть выделена с помощью функции для динамичес кого управления оперативной памятью. Переменная типа (unsigned) со значением, равным числу свободных байтов в “куче“. Пример Определение размера свободного места в “куче“.
17. Принципы описания 519 /* coreleft */ finelode <stdio.h> finclude <alloc.h> inain() { printf("Xu", coreleftO); return 0; } COS ANSI Имя Заголовок Прототип Функция Результат Замечания cos - вычисление косинуса. double cos(double х) inath.h Формирование переменной со значением cos х. Переменная типа (double) со значением, равным косинусу аргумента. Это значение, принадлежит интервалу Аргумент функции выражается в радианах. Пример Вывод числа 0.50. /* cos */ ♦include <stdio.h> ♦include <math.h> inain() { double const Pi • 3.141593; printf ("%. 2f”, cos(Pi/3)); return 0; } cosh Имя Заголовок Прототип Функция Результат Замечания ANSI cosh - вычисление гиперболического косинуса, double coshfdouble х) inath.h Формирование переменной со значением cosh х. Переменная типа (double) со значением, равным гиперболическому косинусу аргумента. В случае возникновения переполнения результат имеет значение HUGE_VAL (с соответствующим знаком). Пример Вывод разности между непосредственно определенным и вычисленным
520 Часть VI. Библиотеки значениями гиперболического косинуса. /‘cosh */ finclude <stdio.h> finclude <math.h> main() { printf("%f". cosh(2) - (exp(2) + exp(-2))/2); retyrn 0; } cprintf TERBO Имя Заголовок Прототип Функция Результат * cprintf - редактирование и вывод иа консоль последовательности символов. int cprintf(const char ‘format, ...) conio.h Вывод на консоль в соответствии с перечнем шаблонов, указанным аргументом format, значений аргументов, представленных иа месте многоточия. Переменная типа (int) равна числу выводимых символов, или переменная со значением EOF в случае возникновения ошибки. Пример Вывод надписи Hello world. /* cprintf */ finclude <conio.h> main() { cprintf("Hello world"); return 0; } cputs TURBO Имя Заголовок Прототип Функция Результат Замечания cputs - вывод на консоль последовательности символов, int cputs(const char ‘string) conio.h Вывод на конссль последовательности символов, указанной в аргументе string. Переменная типа (int) со значением, равным коду последнего выводимого символа. Выводимая последовательность символов не дополняется символом конца строки.
17. Принципы описания 521 Пример Вывод на консоль надписи Kajusia. /* cputs */ finclude <conio.h> tnain() { cputs("Kajusia"); return 0; } cscanf TURBO Имя cscanf - ввод с клавиатуры и интерпретация последоват! дьности символов. Заголовок int csanf(const char ‘format,...) Прототип Функция conio.h Ввод с клавиатуры в соответствии с перечнем шабло- нов, указанным в аргументе format, набора литералов, находящихся в полях файла, и присвоение значений этих литералов переменным, указанным в аргументах, находящихся на месте многоточия. Результат Величина типа (int), равна числу переменных, которым присвоены значения. Пример Обращение с вопросом об имени и вывод приветствия. /* cscanf */ finclude <conio.h> main() { char Name[20j; cprintf("Your name:''); cscanf("%s”, Name); cprintf("\n Hello %s". Name); return 0; } ctime ANSI Имя ctime - преобразование даты и времени в последовательность символов. Заголовок char * ctime(const timet ‘clock)
522 Часть VI. Библиотеки Прототип Функция time.h Преобразование величины, задаваемой в аргументе •clock, созданной с помощью функции time и указывающей время (в секундах), прошедшее с 1 января 1970 г. (время по Гринвичу), в последовательность 26 символов, имеющую примерно следующий вид: Sun Dec 13 06: 00: 00 1981 \п \ 0 Результат Переменная типа (char *), определяющая описанную последовательность символов. Пример Вывод последовательности символов, указывающей наименование месяца, но- мер дня месяца, время дня и год, например: Wed Мау 24 08: 24: 44 1989. /* ctime */ /include <stdio.h> /include «timeTh» main() { long Time; (void)time(&Time); printfctime(&Time)); return 0; } delay TURBO Имя delay - временная приостановка выполнения программы. Заголовок void 7 delay(unsigned time) Прототип Функция dos.h Приостановка выполнения программы на time миллисекунд. Результат void Пример Генерация двух звуковых сигналов с интервалом в 2 с /* delay */ /include <dos.h> /include <conio.h> main() {
17. Принципы описания 523 putch('\a'); delay(2000); putch('\a'): return 0; } difftime ANSI Имя Заголовок difftime - определение разницы во времени, double difftine(time t time2, time t timel) Прототип Функция time.h Вычисление разности между закодированным временем, задаваемым функцией time 2, и временем, отсчитываемым от 1 января 1970 г. и задаваемым функцией time 1. Результат Переменная типа (double) со значением, равным вычисленной разности времени в секундах. Пример Вывод числа -2. /* difftime */ finclude «time.h» finclude «stdio.h» IMin() { long Time, Time2; (void)time(&Timel); sleep(2); (void)time(&Time2); printf("%.Of”, difftime(Timel, Time2)); return 0; } div ANSI Имя div - вычисление частного и остатка от деления двух целых чисел. Заголовок div_t div(int num, int den) Прототип Функция stdlib.h Вычисление частного и остатка от деления num на den и присвоение их значений полям структуры типа div t (частного - полю quot, а остатка - полю rem). Результат Структура типа div_t, состоящая из указанных выше полей.
524 Часть VI. Библиотеки Замечания Тип div_t объявлен следующим образом: typedef struct{ int quot, rem; } div_t; Пример Вывод числа 17. /* div */ finclude «stdlib.h» finclude «stdio.h» ma1n() { div_t Resulta= div(17,5); printf("Xd", Result, quot * 5 + Result, rem); return 0; } -EMIT- TURBO Имя Заголовок —EMIT— - генерация открытой подпрограммы. /* void */ emit (par,par,...,par) Прототип Функция dos.h Генерация машинных команд с кодами, определяемы- ми аргументами, соответствующими параметрам par. Результат Замечания Нет Аргумент, представляющий п-байтовое число, генерирует п байтов. Исключения: а) литерал типа (integer) со значением в интервале 0...255 (например, 0x20) генерирует один байт; б) выражение, задающее автоматическую переменную, перемещаемую относительно регистра ВР на число из интервала -128..127, генерирует один байт. Для генерации другого числа байтов следует применять явные преобразования (например, соответственно ("unsigned) или (void) near *)). Пример Вывод числа 13. /* _emit_ */ finclude «dos.h» finclude «stdio.h» int Fatal (void) { _emit_ (0xB8, (unsigned)13); main()
17. Принципы описания 525 { printfC'Xd", FatalQ); return 0; } exit ANSI Имя Заголовок exit - окончание выполнения программы, void exit(int status) Прототип Функция stdlib.h, process.h Окончание выполнения программы с предшествующим закрытием всех еще не закрытых файлов. Результат Замечания void Значение, которое принимает аргумент status, передается операционной системе. Обычно считается, что значение 0 обозначает нормальное завершение выполнения программы. Пример Проверка существования файла с именем SOURCE.DOC. /* exit */ #include <stdlib.h> /include <stdio.h> main() { if(fopen("SOURCE.DOC”, "r”))} printf("File exitsts"); return 0; } pr'ntf(”File does not exist”); exit(13); } exp ANSI Имя Заголовок ехр - вычисление степени по основанию е. double exp(double х) Прототип Функция Результат Замечания inath.h Формирование переменной со значением е*. Число типа (double), равное степени аргумента. В случае возникновения переполнения результат имеет значение HUGE_VAL, a errno=ERANGE. Пример Вывод числа 2.72.
526 Часть VI. Библиотеки /* ехр */ finclude «stdio.h» finclude «math.h» main() { printf("%.2f", exp(l)); return 0; } fabs TURBO Имя Заголовок Прототип Функция Результат fabs - вычисление абсолютного значения, double fabs(double х) math.h Формирование переменной со значением 1x1. Переменная типа (double), равная абсолютному значению аргумента. Пример Вывод числа 5v00. /* tabs */ finclude «stdio.h» finclude «math.h» main() { pr1ntf("%.2f”, fabs(-5)); return 0; } farcalloc TURBO Имя Заголовок Прототип Функция farcalloc - выделение памяти void far * farcalloc(unsigned long nunits, unsinged long unitsz) Выделение обнуленной области памяти, в которой Результат можно разместить nunits объектов размером unitsz каждый. Переменная типа (void far *), указывающая на Пример выделенную область памяти, или пустое значение, если выделение области невозможно. Выделение и освобождение области памяти для 10000 чисел с плавающей точкой.
17. Принципы описания 527 /* farce!loc */ finclude «stdio.h» finclude «alloc.h» main() { float huge * Ptr, Ptr « farcalloc(10000. sizeof(float)); if(Ptr -= NULL){ printf("Failure”); exit(13); } farfree(Ptr); pr i ntf (''Memory freed”); return 0; } farcoreleft TURBO Имя farcoreleft - оценка свободного места в дальней “куче“. Заголовок unsigned long farcoreleft(void) Прототип Функция alloc.h Оценка размера той части дальней “кучи“, которую можно выделить с помощью функции для динамичес- кого управления оперативной-памятью. Результат Переменная типа (long) со значением, равным числу свободных байтов дальней “кучи“. Пример Определение размера свободного места в дальней “куче“, /* farcoreleft */ finclude «stdio.h» finclude «alloc.h» main() { printf("%ld", farcoreleft()); return 0; } farfree TURBO Имя Заголовок farfree - освобождение места с дальней “куче“. void farfree (void far * block) Прототип Функция alloc.h Освобождение ранее выделенной области в дальней “куче“, указанной в аргументе block.
528 Часть VI. Библиотеки Результат Пример void Вывод трех чисел, характеризующих текущие размеры дальней “кучи". /* farfree */ ♦include «stdio.h» ♦Include «alloc.h> main() { void far * Ref; void Heap(void); Heap(); S Ref » farmelloc(lOOOO); Heap; farfree (Ref)1; Heap(); return 0; } void Heap(void) { printf("\n Heap now = X Id", farcoreleftO); } farmalloc TURBO Имя Заголовок farmalloc - выделение памяти в дальней “куче“. void far * farmalloc(unsigned long size) Прототип Функция alloc.h Выделение в дальней “куче“ области памяти размером size байтов. Результат Переменная типа (void far *), указывающая на выделенную область памяти, или пустое значение, если выделение памяти невозможно. Пример Вывод надписи Allocation Impossible. /* farmalloc */ ♦include <stdio.h> include «alloc.h> nain(); { рг 1 ntf (" Al locat ion"); If(farmalloc(1000000) = “ NULL) printf("Impossible"); else
17. Принципы описания 529 printf("possible”); return 0; } farrealloc TURBO Имя farrealloc - выделение или освобождение места в дальней “куче“. Заголовок void far * farrealloc(void far * block, unsigned long newsize) Прототип Функция alloc.h Такое увеличение или сокращение указанной в аргументе block области памяти дальней “куче“, осуществляется, чтобы размер этой области после изменения оставил newsize. Результат Переменная типа (void far *), указывающая на область памяти с именившимся размером» Замечания Изменение размера области памяти может повлечь ее перенесение в другое место. 'Пример Вывод надписи jb. /* farrealloc */ finclude «stdio.h» include «alloc.h» main(); { char far * Ref; Ref » farrealloc(Z); Ref[0] = 'j'; Ref[*J = 'b'; Ref = farrealloc(Ref, 3); Ref[3] = '\0'; printf("%Fs", Ref); return 0; } fclose ANSI Имя Заголовок fclose - закрытие файла, int fclose(FILE *streani) Прототип Функция stdio.h Закрытие файла, идентифицируемого аргументом stream. Результат Переменная типа (int) со значением 0 или
530 Часть VI. Библиотеки переменная со значением EOF в случае удачного или неудачного выполнения функции соответственно. Замечания Закрытию выходного файла предшествует неявное выполнение функции fflush. Пример В случае успешного выполнения программы вывод надписи Done. /* felose */ finclude «stdio.h» main(); { FILE *0ut; Out * fopen("TARGET","w"); putc('j'. Out); putc('b', Out); printf(fclose(Out) ? "Fail": "Done"); return 0; } feof * ANSI Имя Заголовок feof - распознавание конца файла, int feof(FILE ‘stream) Прототип Определение Функция stdio.h stdio.h Выявить, установлен ли в 1 указатель конца файла, идентифицируемого аргументом stream. Результат Переменная типа (int) со значением, отличным от 0 или равным 0, если указатель конца файла установлен в 1 или не установлен, соответственно. Замечания При открытии файла этот указатель обнуляется. Установка указателя в 1 происходит при попытке ввода из файла, находящегося в конечной позиции. Обнуление указателя происходит при выполнении функций rewind, fclose и clearerr. Пример Вывод надписи 0,1. /* feof */ finclude «stdio.h» main(); { FILE *Out; FILE *Inp; Out « fopen("TARGET","w");
17. Принципы описания 531 putcl'j*. Out); fclose(Out); inp - fopenf“TARGET", "r"); (void)getc(Inp); printfC'Xd”, feof(Inp)); (void)getc(Inp); printf(",%d”, !! feof(Inp)); return 0; } ferror ANSI Имя Заголовок ferror - определение состояния указателя ошибки, int ferrorfFILE “'streaw) Прототип. Определение Функция stdio.h stdio.h Выявить, установлен ли указатель ошибки файла, идентифицируемого аргументом stream. Результат Переменная типа (int) со значением 1 или 0 в слу- чае возникновения или отсутствия ошибки при выпол- нении операции ввода-вывода соответственно. Замечания Обнуление указателя происходит при открытии файла и при выполнении функций rewind, fclose и clearerr. Пример В случае возникновения ошибки ввода-вывода вывод надписи Fail в файл, идентифицируемый аргументом stderr. /* ferror */ ^include «stdio.h» main(); { printf("To be or not to be”); if (ferror(stdout)) fprintf(stderr. "Fail”); return 0; } fflush ANSI Имя Заголовок fflush - очистка буфера int fflush(FILE *streara) Прототип Функция в stdio.h Вывод прежнего содержимого выходного буфера в файл, идентифицируемый аргументом stream. 34*
532 Часть VI. Библиотеки Результат Переменная типа (int), равная 0 в случае неудачного выполнения функции. Пример Несмотря на аварийное завершение выполнения программы, вывод в файл TEXT надписи Kaja. /* fflush */ /include «stdio.h» main(); { FILE *0ut; Out = fopen("TEXT",”w”); fprintf (Ou^,, "Kaja"); fflush(Out); abort(); return 0; /* Fomally required */ } fgetc * ANSI Имя Заголовок fgetc - ввод одного символа. • int fgetc(FILE ‘stream) Прототип Функция. stdio.h Ввод одного символа из файла, идентифицируемого аргументом stream. Результат Переменная типа (int) со значением, равным коду вводимого символа, или со значением EOF, если установлен в 1 указатель конца файла. Пример Копирование символов из стандартного входного файла в стандартный выходной файл. !* fgetc */ /include «stdio.h» maint); { int Chr; while((Chr = fgetc(stdin)) f = EOF) putc(Chr, stdout); return 0; } fgetpos ANSI fgetpos - выдача позиции файла. Имя
17. Принципы описания 533 Заголовок int fgetpos(FILE ‘stream, fpos_t *pos) Прототип Функция stdio.h Присвоение переменной, указанной аргументом pos, значения, определяющего текущую позицию файла, идентифицируемого аргументом stream. Результат Переменная типа (int) со значением, равным 0 в случае успешного определения позиции файла, или со значением, отличным от 0 и равным EBADF или EINVAL в противном случае. Пример Вывод числа 5. /* fgetpos */ finclude <stdio.h> main(); { FILE ‘Out = Fopen("W0RK", "w"); fpos_t Pos; printf(Out, ' "Hello"); (void)fgetpos(Out, &Pos); fprintf(Out, printf("%ld" "world”); , (long)Pos); return 0; } fgets ANSI Имя Заголовок е fgets - ввод последовательности символов, char* fgets(char ‘string, int n, FILE ‘stream) Прототип Функция stdio.h Ввод из файла, идентифицируемого аргументом stream, в указанный аргументом string буфер емкостью п символов последовательности символов и дополнение ее символом с кодом 0. Результат Переменная типа (char *), указывающая на первый символ буфера. Замечания Ввод заканчивается в момент, когда возникает угроза переполнения буфера, или в момент ввода символа новой строки. Во втором случае перед символом с кодом 0 в буфере помещается символ '\п'. Пример В случае, когда первая строка стандартного входного файла содержит после- довательность символов Jan, вывод в стандартный выходной файл символов J', ’а’, 'п' и ’\п’.
534 Часть VI. Библиотеки /* fgetc */ ^include «stdio.h» main(); { char Buf[20J; printf("Xs", fgets(Buf, 20, stdin)); return 0; } findfirst TURBO Имя Заголовок <? findfirst - нахождение файла, int findfirstfconst char * name, struct ffblk *ffblk, int attr) Прототип Функция dir.h Размещение в структуре ffblk информации о первом из файлов с именем name и атрибутами attr. Результат» Переменная типа (int) со значением 0, если файл найден, и со значением -1 в противном случае; при этом errno приобретает одно из значений ENOENT - дефектная дорожка, ENMFILE - файл не найден. Замечания Структура типа (strukt ffblk) объявлена следующим образом: struct ffblk { char ff_reserved[21J; /’резервированные*/ char ffattrib; /‘атрибуты*/ int ff time; /‘время и дата*/ int ff date; /‘создания файла*/ long ff_ffsize; /‘размер файла*/ char ff_name[13]; /‘имя файла*/ } Атрибуты файла могут быть заданы в виде логической суммы следующих характеристик: FA_RDONLY - файл только для чтения, FA-HIDDEN - скрытый файл, FA_SYSTEM системный файл, FAJ.ABEL - метка тома, FA DIREC - каталог, FA_ARCH - архивированный файл.
77. Принципы описания 535 Пример Вывод метки тома диска С. /* findfirst */ finclude «dir.h* finclude «stdio.h» finclude «dos.h» main(); { struct ffblk Info; findfirst("C:\\*.*" , &Info, FAJ.ABEL); printf(”\nXs”, Info. ff_name); return 0; } findnext TURBO Имя findnext - нахождение очередного файла с именем, заданным функцией findfirst. Заголовок int findnext(struct ffblk *ffblk) Прототип. Функция dir.h Нахождение следующего файла co свойствами, заданными при вызове функции findfirst, и помещение информации о нем в структуру *ffblk. Результат Переменная тип (int) со значением 0, если файл найден, и со значением -1 в противном случае; в последнем случае еггпо приобретает одно из значений ENOENT - дефектная дорожка ENMFILE - файл не найден. Замечания Структура, задаваемая ffblk, должна быть той же структурой, к которой относился вызов функции findfirst. Пример Вывод имен подкаталогов основного каталога. /* findnext */ finclude «dir.h» finclude «stdio.h» finclude «dos.h» finclude «conioTh» main() { struct ffblk Info; clrcrf);
536 Часть VI. Библиотеки if(findf1rst("C:\\*", &Info, FA_DIREC) — 0) do printf(”\ nXs", Info. ff_name); while(ffndnext(Mnfo) =* 0; return 0; } floor ANSI Имя Заголовок floor - округление в сторону уменьшения, double floor(double х) Прототип Функция » math.h Вычисление переменной типа (double) со значением, равным наибольшему целому числу, не Результат превышающему х. Переменная типа (double), найденная указанным способом. Пример Вывод числа 4.00. /* floor */ finclude <stdio.h> finclude <math.h> main() { printf("%.2f”, floor(4.99)); return 0; } fmod ANSI —z Имя Заголовок fmod - определение остатка от деления, double fmod(double х, double у) Прототип Функция Результат math.h Определение остатка от деления х на у. Переменная типа (double) со значением, равным разности x-i*y для некоторого целого i. Пример Вывод числа 1.00. /* fmod*/ finclude <stdio.h>
17. Принципы описания 537 ^include <math.h> main() { printff'X.Zf". fmod(B.3.5)); return 0; } fnmerge TURBO Имя fnmerge - создание полного имени файла из его компонентов. Заголовок void fnmerge(char "path, const char *dir, const char “name, const char *ext) Прототип Функция dir.h Создание имени файла, состоящего из имени каталога dir, основной части имени name и расширения ext, С последующим размещением его в области памяти, на первый элемент которой указывает аргумент path. Результат void Пример Вывод надписи C:\TC20\Greet.EXE. /* fnmerge */ ^include <dir.h> ^include <stdio.h> main() { char FullName[30]; fnmerge(Ful1Name,"C:", WTC20", "GREET", ".EXE"); printf("Xs", FullName); return 0; } fnsplit TURBO Имя Заголовок fnsplit - разбиение имени файла на его компоненты, int fnsplit (const char "path, char "drive, char "dir, char "name, char *ext) Прототип Функция dir.h Разбиение имени файла, заданного в аргументом path,
538 Часть VI. Библиотеки на его компоненты: имя дисковода drive, имя каталога dir, основная часть имени name и расширение ext. Результат Переменная типа (int), состоящая из пяти однобитовых полей, указывающих, какие компоненты присутствуют в имени файла. Замечания Названия полей могут быть следующими: DRIVE - имя дисковода, DIRECTORY - каталог, FILENAME - основная часть имени, EXTENSION - расширение, WILDCARDS - символы * (звездочка) и ? (знак вопроса). Пример н Вывод надписи Greet. /* fnsplit */ finclude <dir.h> finclude «stdio.h» main() { char FullName[30] = {"C:\\tc20\\Greet.EXE”}; char Name[20];/ fnsplit(Fu11Name,NULL, NULL, Name, NULL); printff'Vflis", Name); return 0; } fopen ANSI Имя Заголовок fopen - открытие файла. FILE * fopen(const char name, const char *mode) Прототип. Функция stdio.h Открытие канала, связывание его с файлом с именем, задаваемым filename, и открытие файла в режиме, определяемом аргументом mode. Последовательность символов, содержащаяся в mode, определяет режим открытия файла: г чтение (read) w запись (write) а добавление (append) г+ чтение обновлением (read, write) w+ запись с обновлением а+ добавление с обновлением
17. Принципы описания 539 Результат Замечания Переменная типа (FILE*), идентифицирующая файл. Если последовательность символов, определяющую режим открытия, дополнить буквой t, то файл будет открыт в текстовом режиме; если же дополнить ее буквой Ь, то файл будет открыт в двоичном режиме. Открытие в текстовом режиме заключается в том, что вывод символа новой строки влечет за собой внесение в файл пары символов cr-lf, а ввод из файла пары символов cr-lf трактуется как ввод символа новой строки. По умолчанию принимается режим открытия, определяемый глобальной переменной _fmode. Если ей присвоено значение вида О_ТЕХТ, то открытие произойдет в текстовом режиме, а если значение вида OBINARY, то открытие произойдет в двоичном режиме. Переменная finode описана в файле fcntl.h, а ее начальным значением является О_ТЕХТ. Пример Вывод в файл TARGET символов j, cr. If, Ь. /* fopen */ finclude «stdio.h» finclude «fcntl.h» . main() { FILE *0ut; externfmode; Out = fopenf'TARGET", ”wt”); fprintf(Out, "j\n"); fclose(Out); fmode - oBINARY; Out - fopen("TARGET", "a+”) fprintf(Out, ”b“); fclose(Out); return 0; FP-OFF TURBO Имя Заголовок Определение Функция Результат FP_OFF - определение смещения. /* unsigned */ FPOFF (/* void far **/ farptr) dos.h Выделение той части указателя, задаваемого farptr, которая определяет смещение относительно начала сегмента. Переменная типа wnsi^ned) со значением, равным смещению в сегменте.
540 Часть VI. Библиотеки Пример Вывод символа * (звездочка). /* FP_OFF */ finclude <dos.h> finclude «stdio.h» main() { char Chr = char far *Ref = MK_FP(FP_SEG((void far *)&Chr), FP0FF((void far *)&Chr)); putchar(* Ref); return 0; } и fprintf ANSI Имя fprintf - редактирование и вывод последовательности символов. Заголовок int • fprintf(FILE * stream, const char ‘format, ...) Прототип Функция stdio.h Вывод в соответствии с перечнем шаблонов в последовательности, заданной аргументом format, в файл, идентифицируемый аргументом stream, значений аргументов, находящихся на месте многоточия. Результат Переменная типа (int) со значением, равным числу выведенных символов, или со значением EOF в случае возникновения ошибки. Замечания Перечень шаблонов содержит обычные символы и шаблоны преобразования. Обычные символы (также называемые шаблонами) передаются в файл без изме нения. Шаблоны преобразования определяют способ редактирования данных, представленных аргументами. Если число аргументов превышает число шаблонов, то избыточные аргументы не редактируются. Не допуска ется, чтобы число аргументов было меньше числа шаблонов преобразования. Шаблон преобразования Шаблон преобразования состоит в общем случае из следующих элементов: % f w р t В этой записи символ % (процент) начинает шаблон; элемент f состоит из последовательности маркеров; элемент w - это число, определяющее ми- нимальную ширину поля файла; элемент.р определяет число выводимых сим- волов символьной переменной; элемент t указывает тип аргумента и способ редактирования поля файла.
17. Принципы описания 541 Пример Вывод надписи (+123.457). Элемент 9 определяет ширину внешнего поля, элемент .3 - число цифр после десятичной точки, элементы - (минус) и + (плюс) указывают соответственно, что число в выходном поле выравнивается влево и что числу предшествует знак плюс или минус. /* fprlntf.l */ finclude <std1o.h> main() { fpr1ntf(stdout,”(X-+9.3f)", 123.4567); return 0; } Элемент t (тип аргумента) Тип аргумента задается с помощью однобуквенного кода преобразования, которому может предшествовать однобуквенный квалификатор. Код преобра- зования (определяющий способ создания надписи, составляющей поле файла) имеет следующие значения: Код Тип d (int) десятичное число 1 (int) десятичное число 0 (int) восьмеричное число без знака и (int) десятичное число без знака X (int) шестнадцатеричное чило без знака X (int) шестнадцатеричное число без знака f (double) число с фиксированной точкой е (double) число с плавающей точкой g (double) число с плавающей точкой Е (double) число с плавающей точкой G (double) число с плавающей точкой с (char) символ S (char *) последовательность символов п (int ♦) р (type ♦) адрес (ssss: оооо или оооо) X 7 /о Если код преобразования равен х, то цифры, большие 9, будут представ- лены малыми буквами, а если код преобразования равен X, то большими буквами. Если код преобразования равен f, то поле будет иметь вид число с фик- сированной точкой, в котором число цифр после точки задается элементом •р (по умолчанию .6). Если код преобразования равен е или Е, то поле будет иметь вид числа с плавающей точкой и показателем степени (содержащим соответственно бу- кву е и Е), а число цифр после точки в мантиссе будет определяться эле- ментом ,р (по умолчанию .6). Целая часть числа будет содержать одну Цифру.
542 Часть VI. Библиотеки Если код преобразования равен g или G, то он будет интерпретироваться как f или (соответственно) как е или Е. Запись с плавающей точкой выби- рается в том случае, если показатель меньше -4 или если он превышает значение, определяемое элементом .р (по умолчанию .6). Если код преобразования равен G, то он будет интерпретирован как g, при этом в возможном показателе степени будет фигурировать буква Е. Квалификатор В качестве квалификаторов могут фигурировать буквы F, N, h, 1 и L, Квалификаторы F или N не охвачены стандартом ANSI и они могут отно- ситься лишь к аргументам, являющимся указателями. F означает, что аргу- мент представляет собой близкий указатель, h означает, что аргумент пред- ставляет собой тип (short int) или (unsigned short int) 1 означает, что аргу- мент представляет собой тип (long double). 4 Примеры Аргумент Шаблон Поле файла 12.4 % f %-3f . %.3е 12.4000 12.400 1.240е+001 -458.7 %.1G -4.6Е+002 Элемент f (маркер) Маркером может быть знак мину (-), плюс (+), хеш (#) и пробел О. Очередность маркеров в элементе шаблона может быть произвольной. Маркеры имеют следующую интерпретацию: Надпись должна быть выровнена в выходном поле влево и дополнена справа пробелами. + Числовой надписи должен предшествовать знак плюс или минус в соответствии со значением аргумента. пробел Числовой надписи должен предшествовать знак минус или пробел в соответствии со значением аргумента. # Аргумент должен выводиться специальным образом (см. описание ниже). Если маркер минус (-) не использован, то надпись будет выровнена вправо и ей будут предшествоать нули или пробелы. Если маркеры плюс (+) и пробел () использованы одновременно, то пробел игнорируется. Специальный вывод В случае преобразований с, s, d, и маркер хеш (#) игнорируется. В случае преобразования о надписи будет предшествовать цифра 0, а в случае преобразований х или X надписи будут предшествовать соответствен- но символы Ох или ОХ. В случае преобразований е, Е, f надпись будет содержать точку, раздели-
Aft 1л OilMdinAiH. ющую целую и дробную части числа (обычно эта точка после нее не следуют цифры). опускается, если В случае преобразований g, G надпись будет содержать но и нули в конце числа (обычно они опускаются). Примеры (буква Ъ означает пробел) Аргумент Шаблон Поле файла 5 %d 5 % 3d ЬЬ5 %-3d 5ЬЬ %+3d b+5 %3d ЬЬ5 5.0 %f 5.000000 %5.2f Ь5.00 не только точку, Элемент w (ширина поля) Этот элемент определяет минимальную ширину поля, в котором будет размещена надпись. Ширина поля может быть указана числом или с по- мощью символа * (звездочка}. Во втором случае фактическая ширина поля будет определяться вторым аргументом. Этот аргумент должен иметь тип (Int). Если ширина поля окажется слишком малой для размещения надписи, то она будет неявно увеличена настолько, чтобы это стало возможным. Например, это произойдет в том случае, когда в шаблоне отсутствует элемент w, ибо при этом по умолчанию будет принята ширина поля, равная нулю. Если ширина поля окажется больше, чем размер надписи, то она будет размещена в поле с выравниванием, которое задает маркер - (минус). Исключением из этого правила является случай, когда ширина поля вы- ражена числом, начинающимся с цифры 0. В этом случае надпись будет до- полнена слева цифрами 0. Примеры (буква b означает пробел) Аргумент Шаблон Поле файла 13 %5d ЬЬЫЗ %-5d 13bbb % 03d 013 7od 13 (long) 13 % Id 13 “Janek" % 3s Janek % .3s Jan %4.3s bJan Элемент .р (точность) Этот элемент определяет минимальное число цифр в числах с фиксиро- ванной точкой, число цифр после точки в числах с плавающей точкой и число символов в последовательностях, созданных с помощью шаблонов, за- канчивающихся буквой s. Если ширина поля, предусматриваемая элементом Р, превышает ширину поля, предусматриваемую элементом w, то принима-
544 Часть VI. Библиотеки ется первая из этих величин. Как и для элемента w, часть р элемента .р может быть выражена с помощью символа * (звездочка). В этом случае точность определяется следующим аргументом функции. Этот аргумент должен иметь тип (int). Пример Вывод надписи (13.00). Г fprintf.2 */ /include «stdio.h» main() { fprintf(stdout, "(X-*. *f)", 6,2,13.); return 0; FP-SEG TURBO Имя Заголовок Определение Функция Результат tvj£& - определение номера сегмента. /‘unsigned */ FP_SEG(/* void far **/ farprt) dos.h Выделение той части указателя, заданного farprt, которая определяет номер сегмента. Переменная типа (unsigned) со значением, равным номеру сегмента. Пример Вывод знака * (звездочка). /* FP_SEG */ /include <dos.h> /include «stdio.h» char Chr = ’ * main() { char far * Ref = MK_FP(FP_SEG((void far *)&Chr), putchar (*Ref); return 0; FP_OFF((void far *)&Chr)); fputc ANSI Имя Заголовок Прототип fputc - вывод символа int fputc(int ch, FILE ‘stream) stdio.h
17. Принципы описания 545 Функция Вывод символа с кодом ch в файл, идентифицируемый Результат аргументом stream. Переменная типа (int) со значением ch, а в случае неудачного вывода символа - со значением EOF. Пример Вывод буквы Е в стандартный выходной файл и возможный вывод знака вопроса в стандартный файл для сообщений об ошибках, /* fputc */ finclude «stdio.h* ma1n{) { 1f(fputc('E', stdout) -> EOF){ fputcf?', stderr); exit(l); } return 0; } fputs ANSI Имя Заголовок fputs - вывод последовательности символов. Int fputs(const char *str1ng, FILE *strean) Прототип Функция stdio.h Вывод последовательности символов, определяемой аргументом string, в файл, идентифицируемый аргументом stream. Результат Переменная типа (int) со значением, равным коду последнего выводимого символа, а в случае неудачно го вывода какого-либо символа - со значением EOF. Замечания Единственное различие между функциями fputs и puts состоит в том, что после вывода заданной последова тельности символов при выполнении функции fputs не выводится автоматически символ новой строки. Пример Создание файла ONELINE, содержащего надпись Kaja. /* fputs */ finclude «stdio.h* ma1n() { FILE *0ut - fopenf'ONELINE", V); fputs(“Kaja\n", Out); exlt(l); return 0; }
546 Часть VI. Библиотеки fread ANSI Имя Заголовок fread - ввод группы блоков. s1ze_t fread(vo1d *ptr, s1ze_t size, s1ze_t ntterns. FILE ‘stream) Прототип Функция stdio.h Ввод из файла, идентифицируемого аргументом stream, в область памяти, указанную ptr, nlterns блоков данных размером size каждый. Результат н Переменная типа (size_t) со значением, равным числу успешно введенных блоков, или со значением, меньшим nlterns, если встретился конец файла или обнаружена ошибка. Пример Вывод надписи Count = 3. • /* fread */ finclude <std1o.h> char Arr[30]; ma1n() { FILE *Upd = fopen("ARRAYS", "w+"); Int Rep, Count; for(Count = 1; Count ! = 4; Count++){ Arr[0] = Count; fwr1te(Arr, sizeof Arr, 1, Upd); } rewlnd(Upd); Count = 0; do{ Rep = fread(Arr, sizeof Arr, 1, Upd); If(Rep<l) break; Count++; } whlle(l); prlntff'Count Xd' return 0; ', Count); } free ANSI free - освобождение места в “куче*1. Имя
17. Принципы описания 547 Заголовок Прототип Функция Результат void free(void *ptr) stdlib.h, alloc.h Освобождение ранее выделенной и заданной аргументом ptr области в ближней “куче“. void Пример Вывод пары чисел, представляющих собой оценки свободного места в близ- кой “куче" перед выполнением и после выполнения функции free. /* free */ f include «alloc.h* main() { void *Ref; Ref = malloc(2000); printfCXu”, coreleftO); free(Ref); printf("Xu", coreleftO); return 0; } freopen ANSI Имя Заголовок freopen - замена одного файла другим. FILE * freopen(const char ‘name, const char *type, FILE ‘stream) Прототип Функция stdio.h Закрытие файла, идентифицируемого аргументом stream, установка файла с именем name и открытие его в режиме, указанном в аргументе type таким образом, чтобы его можно было идентифицировать аргументом stream. Результат Переменная типа (FILE *), идентифицирующая файл, или пустое значение, если открытие файла окажется невозможным. Пример Вывод буквы j в стандартный выходной файл и буквы b в файл WORK. Г freopen */ ^include «stdio.h» main() { putchar(' j ’);
548 Часть VI, Библиотеки (vo1d)freopen(',U0RK”,"w”I stdout); putchar('b'); return 0; } frexp ANSI Имя Заголовок frexp - разбиение числа с плавающей точкой, double frexp(double value, Int *eptr) Прототип Функция math.h Разбиение числа value на нормализованную мантиссу п* и показатель степени по основанию 2, Присвоение значения показателя степени переменной, указанной в аргументе eptr, и вывод мантиссы. Результат Переменная типа (double), представляющая собой нормализованную мантиссу. Пример Вывод числа 0333333. /* frexp */ finclude «stdio.h* finclude «math.h* ma1n() { float const OneThlrd = 1/3.; double Man; int Exp; Man = frexp(0neTh1rd, &Exp); printf("Xf”, Man 1 k pow(2, Exp); return 0; } fscanf ANSI Имя fscanf - ввод и интерпретация последовательности символов. Заголовок Int fscanf(FILE ‘stream, const char ‘format. Прототип Функция ... J stdio.h Ввод из файла, идентифицируемого аргументом stream, в соответствии с перечнем шаблонов, заданных аргументом format, последовательности находящихся в полях файла литералов и присвоение значений этих
17. Принципы описания 549 Результат Замечания литералов переменным, находящимся на месте многоточия. Переменная типа (int) со значением, равным числу значений, присвоенных указанным переменным. Каждому значению, полученному при интерпретации шаблона, должна соответствовать переменная, зада- ваемая аргументом. Если число аргументов превышает исло таких значений, то избыточные аргументы игнорируются. Перечень шаблонов содержит интерва- лы, шаблоны преобразования и обычные символы (также называемые шаблонами). Интервалами являются пробелы, табуляции и символы новой строки, а шаблонами преобразования - описываемые ниже последовательности символов, начинающиеся с символа % (процент). Результатом интерпретации интервала является игнорирование ближайшего интервала - если он присутствует в файле. Результатом интерпретации шаблона преобразования является анализ ближайшего поля файла и присвоение значения находящегося в этом поле литерала очередной переменной, задаваемой аргументом функции. Результатом интерпретации символа, не являющегося интервалом и не принадлежащего к числу шаблонов преобразования, будет проверка того, идентичен ли следующий символ файла этому символу. При положительном исходе проверки данный символ игнорируется, в противном случае исполнение функции fscanf прекращается. Последнее имеет место также в том случае, если вид литерала в поле файла не соответствует требованиям шаблона преобразования. В обоих случаях последний рассмотренный входной символ возвращается в файл. Пример Ввод из стандартного входного файла последовательности символов в виде числа и вывод информации о его значении. Числу должна предшествовать надпись Val=. Перед словом Val, а также с обеих сторон от знака равенства могут находиться интервалы. Если входная последовательность имеет другой вид, то результатом выполнения программы будет вывод числа -13. /* fscanf.1 */ finclude «stdio.h* ma1n() { Int Val = -13; fscanf(stdin, "Val = Xd", &Val); printf("\n\nVal = Xd", Val); return 0;
550 Часть VI. Библиотеки Шаблон преобразования Шаблон преобразования состоит из следующих элементов: % f w t В этой записи символ % (процент) начинает шаблон, элемент f является маркером, элемент w - это число, определяющее максимальную ширину поля файла, а элемент t определяет тип аргумента и требуемый вид поля файла. Пример Ввод из поля с максимальной шириной 3 последовательности символов, определяющей некоторое число типа (int), преобразование его в число типа (short) и присвоение полученного значения переменнйо Vai. В использованном шаблоне преобразования элемент 3 определяет ширину поля файла, код преобразования d определяет спобоб преобразования поля, а квалификатор h показывает, что значение должно присваиваться переменной типа short. /* fscanf.2 */ flnclude «stdio.h» ma1n() { short Vai; fscanf(std1n, ”%3hd", &Val); printf ("\n\nVal = Xd", Vai); return 0; Элемент t (тип аргумента) Способ преобразования задается с помощью кода преобразования, которо- му может предшествовать однобуквенный квалификатор, или с помощью последовательности символов, заключенной в квадратные скобки. В соответствии с выбранным кодом преобразования должны соблюдаться следующие правила подготовки аргумента функции и поля файла: Код преобразования Тип аргументов Поле файла d (Int *) десятичное число D (long *) десятичное число о (Int *) восьмеричное число 0 (long *) восьмеричное число 1 (Int *) десятичное, восьмеричное, шестнадцатеричное число I (long *) десятичное, восьмеричное, шестнадцатеричное число и (unsigned *) десятичное число без знака и (uns.long *) десятичное число без знака X (Int *) шестнадцатеричное число X (long *) шестнадцатеричное число
17. Принципы описания 551 е (float *) число с плавающей точкой Е (float *) число с плавающей точкой f (float *) число с плавающей точкой F (float *) число с плавающей точкой д (float *) число с плавающей точкой G (float *) число с плавающей точкой 5 ’ (char) последовательность i символов С (char *) символ п (int *) р (void *) адрес Если код преобразования равен s, то очередные символы поля, замыкае- мые символом с кодом 0, будут размещены в массиве, первый символ кото- рой задается аргументом функции. Если код преобразования равен с, то пе- ременной, задаваемой аргументом, будет присвоено значение, состоящее из первого символа поля. Если код преобразования равен п, то очередному аргументу будет присво- ено значение, равное числу символов, введенных из файла при выполнении данной функции fscanf. Если код преобразования равен р, то адрес должен представлять собой пару шестнадцатиричных чисел, разделенных символом: (двоеточие), или шестнадцатиричное число в зависимости от того, является вводимый указатель дальним или ближним. Квалификатор Значениями квалификатора могут быть только буквы F, N, h, 1. Квалификаторы F и N не охвачены стандартом ANSI и могут относиться только к коду преобразования р. Квалификатор h может относиться только к кодам преобразования d, i, о, и, х, а квалификатор 1 может относиться только к кодам преобразования d, i, о, u, х, е, f. Квалификатор h означает, что аргумент принадлежит типу (short *). Квалификатор 1, стоящий перед кодом преобразования е или f, означает, что аргумент принадлежит типу (double *), а квалификатор 1, стоящий перед кодом преобразования d, i, о, и, х, означает, что аргумент принадлежит типу (long *). Пример (буква b означает пробел) Шаблон Поле файла Число %d Ь-13 -13 %о 12b 10 %f 13b 13f %с Е 'Е' %li 5 5L Если способ преобразования задан последовательностью символов, заклю- ченной в квадратные скобки, то ограничителем поля является не интервал, а конечный символ, уже не принадлежащий полю. Если непосредственно после открывающей квадратной скобки следует символ " (каре), то конечным сим- волом является любой из последующих символов. В противном случае конеч- ным символом является любой символ, не присутствующий в квадратных скобках. Шаблону преобразования, состоящему из символа % (процент) и
552 Часть VI. Библиотеки последовательности символов, заключенной в квадратные скобки, соответству- ет аргумент типа (char []). Очередные символы поля размещаются в массиве, указываемой аргументом. Пример Ввод из стандартного входного файла последовательности символов, заканчи- вающейся символом Enter, а затем вывод этих символов в обратном порядке. /* fscanf.3 */ finclude <stdio.h> iMin() { char Arr[80]; int Count; fscanf(stdin, "X[*\n]Xn", Arr, ACount); . * do printf{”\nXc", Arr[—Count]); while(Count); return 0; } Элемент f (маркер) Маркером может быть только символ * (звездочка). Если такой символ присутствует в шаблоне преобразования, то интерпретация шаблона сводится к игнорированию входного поля. Содержащему такой маркер шаблону не соответствует ни один из аргументов функции. Пример Для данных, имеющих вид надписи 13.0, вывод символа . (точка). /* fscanf.4 */ finclude <stdio.h> iMin() { char Chr; fscanf(stdin, "X*dXc", AChr); printf("\nXc", Chr); return 0; Элемент w (ширина поля) Этот элемент определяет максимальную ширину поля. Если он присутст- вует в шаблоне преобразования, то ближайшее поле файла состоит из самой длинной последовательности, насчитывающей * не более и символов и не со- держащей интервала. Первым символом поля является ближайший символ файла (для преобразований с и [...], а первый (после возможного интервала) символ относится к остальным преобразованиям. Если элемент и отсутствует в шаблоне, то ограничителем поля является интервал, а для преобразования [...] - символ, определяемый содержимым квадратных скобок.
17. Принципы описания 553 Пример Для данных, имеющих вид надписи j Ь, вывод числа 1. /* fscanf.5*/ ♦Include «stdio.h» mair>( ) { char Arr[3]; char Chr; fscanf(stdin, "XZsXc", Arr, AChr); printf("Xd",.Chr ' '); return 0; } fseek ANSI Имя Заголовок Прототип ' Функция Результат Замечания fseek - установка файла в заданную позицию int fseek(FILE *strean, long offset, int fronwhere) stdio.h Установка файла, идентифицируемого аргументом strean, в позицию, отстоящую на offset байтов в сторону конца файла от места, определяемого параметром fronwhere. Параметр Положение SEEK.SET Начало файла SEEK.CUR Текущая позиция файла SEEK.END Конец файла Переменная типа (int) со значением 0 при успешном выполнении функции и со значением, отличным от нуля, в противном случае. Различие между fseek(strean, 0L, SEEK.SET) и rewind(strean) заключается в том, что fseek обнуляет только указатель конца файла, в то время как rewind обнуляет также указатель ошибки. Пример Вывод надписи Iza. г /*fseek*/ "0J ♦include «stdio.h» ва1п{ ) FILE *Upd char Name[3]; bpd - fopen("WORK", "wrt");
554 Часть VI. Библиотеки fprintf(Upd, "Ewa-Iza-Jan"); fseek(Upd,-7, SEEK_CUR); fscanf(Upd, "X3s," Name); printf ("%3s", Name]; return 0; } fsetpos ANSI Имя Заголовок Прототип Функция » Результат Замечания fsetpos - установка файла в заданную позицию, int fsetpos(FILE * * * * *stream, const fpos_t *pos) stdio.h Установка файла, идентифицируемого аргументом stream, в позицию, указанную аргументом pos. Переменная типа (int) со значением 0 при успешном выполнении функции и со значением, отличным от нуля, в противном случае. Файл может быть установлен только в позицию, определяемую предшествующим выполнением функции fgetpos. После выполнения функции fgetpos с файлом можно выполнять операции как ввода, так и вывода данных. Пример Вывод надписи Hello world! /*fsetpos*/ finclude «stdio.h» main( ) { fpos_t Pos; int Chr; FILE *0ut - fopen(”W0RK”, "w"), *Inp: pfrintf(Out, "Hello"); (void)fgetpos(Out, &Pos); fprintf(Out, "*****!"); (void)fsetpos(Out, &Pos); pfrintf(Out. "world"); fclose(Out); Inp - fopen("W0RK", "r"), while((Chr = getc(Inp)) ! = EOF) putchar(Chr); return 0; }
17. Принципы описания 555 ftell TURBO Имя Заголовок ftell - определение позиции файла, long ftell(FILE *strea») Прототип Функция stdio.h Определение числа байтов, отделяющих текущую по- зицию файла, идентифицируемого аргументом stream, от его начальной позиции. Результат Переменная со значением, равным указанному числу байтов, или со значением -1L в случае возникновения ошибки. Пример Вывод числа символов, содержащихся в файле STDIO.H. /•ftell*/ finclude <stdio.h> main( ) { FILE *Inp; Inp = fopen(”D:\\tc\\include\\stdio.h". ”r“); fseek(Inp, 0. SEEKEND); printf("Xld”, , ftell(Inp)); return 0; } fwrite ANSI Имя Заголовок fwrite - вывод блоков данных. size_t fwrite(const void *ptr, sizet size, size_t niterns, FILE *stream) Прототип Функция stdio.h Вывод в файл, идентифицируемый аргументом stream, niterns - блоков размером size каждый из области оперативной памяти, указанной аргументом ptr. Результат Переменная типа (size t) со значением, равным числу успешно выведенных блоков, или со значением, меньшим ni terns в случае возникновения ошибки. Пример Вывод в файл WORK трех структур DATE. /•fwrite*/
556 Часть VI. Библиотеки ♦Include «stdio.h» struct{ int Day; char Honth[3]; int Year; } DATE [3] » {13, "Dec", 1981}; main( ) { FILE *Out - fopen(”WORK", "w"); fwrite(&Date, sizeof Date, 3, Out]; return 0; } getc ANSI Имя Заголовок Прототип Функция Результат getc - ввод символа. getc((FILE *stream) stdio.h Ввод одного символа из файла, идентифицируемого аргументом stream. Переменная типа (int) со значением, равным неотрицательному коду введенного символа, или со значением EOF, если перед выполнением функции файл находился в конечной позиции или если возникла ошибка. Пример Ввод одного символа из стадратного входного файла и его вывод в стандарт- ный выходной файл. /*getc*/ ♦include «stdio.h» main( ) { int Chr; Chr - getc(stdin); printf("Xc", Chr); return 0; } getch TURBO Имя Заголовок Прототип Функция getch - ввод символа с консоли. int getch(void) conio.h Ввод одного символа с клавиатуры без копирования его на экране.
17. Принципы описания 557 Результат Переменная типа (int) co значением, равным коду введенного символа. Пример Ввод одного символа, а затем его вывод. /*getch*/ finclude «stdio.h» finclude «conio.h» main( ) { putch(getch( )); return 0; } getchar ANSI Имя Заголовок getchar - ввод символа, int getchar(void) Определение Функция Результат stdio.h Ввод одного символа из стандартного входного файла. Переменная типа (int) со значением, равным коду введенного символа, или со значением EOF, если ввод оказался неудачным. Пример Копирование содержимого стандартного входного файла в файл TEXT. /‘getchar*/ finclude «stdio.h» main( ) { FILE *0ut; int Chr; Out = fopen("TEXT", "w"); while((Chr = getchar( ))! = EOF) putc(Chr, Out); return 0; } getdate TURBO Имя Заголовок getdate - вывод даты, void getdate(struct date *date) Прототип dos.h
558 Часть VI. Библиотеки Функция Присвоение полям структуры, указанной аргументом Результат date, значений компонент системной даты, void Замечания Структура типа (struct date) объявлена следующим Пример образом: struct date { int da_year; /*год*/ char da_day; /*день*/ char de mon; / ‘месяц* / } Вывод наименования месяца. /*getdate*/ finclude «dos.h» finclude «stdio.h» main( ) { struct date Today; char *Names[.J - {"Jan," "Feb”, "Mar', "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; getdate(&Today); printf("Xs"; Names[Today, da_mon - 1]); return 0; } getenv ANSI Имя Заголовок getenv - определение параметров окружения, char* getenv(const char *envvar) Прототип Функция stdlib. h Поиск среди параметров окружения того параметра, наименование которого указывает envvar, и вывод указателя строки символов, описывающих значение этого параметра. Результат Переменная типа (char*), определяющая последовательность символов, описывающих значение параметра. Пример Вывод надписи, \D0S33Q; \ТС. определяющей значение параметра PATH, например, \UTIL; /‘getenv*/ finclude «stdio.h» finclude «stdlib.h»
17. Принципы описания 559 main( ) { printf("Xs"; getevn("PATH"); return 0;5 } gets ANSI Имя Заголовок gets - ввод последовательности символов, char* gets(char ‘string) Прототип Функция stdio. h Ввод из стандартного входного файла одной строки текста и после замены символа конца строки символом с кодом 0 размещение введенных символов в поле памяти, на первый символ которого указывает аргумент string. Результат Переменная типа (char*), определяющая первый символ поля памяти, или пустое значение, если встретился конец файла или возникла ошибка. Пример Ввод, а затем вывод одной строки текста. /‘gets*/ finclude <stdio.h> main( ) { char Line[81]; printf("Xs”; gets(Line)); return 0; } gettime . TURBO Имя Заголовок gettime - определение компонент времени, void gettime(Struct time ‘time) Прототип Функция dos.h Присвоение полям структуры, указанной аргументом time, значений компонент системного времени. Результат Замечания void Структура типа (struct time) объявлена следующим образом: struct time { unsigned char ti_min, /‘минуты*/ ti_hour, /‘часы*/
560 Часть VI. Библиотеки } Пример Вывод текущего часа. /*gettime*/ finclude <dos.h> finclude <stdio.h> main( ) { struct time Now; gettime(&Now); printf("%d". Now. ti_hour); return 0; # } ti_hund. /*сотые секунды*/ ti_sec; /‘секунды*/ getvect TURBO Имя Заголовок Прототип Функция Результат getvect - получение вектора прерываний. void interrupt (far *getvect(int num)) ( ) dos.h Получение вектора прерываний с номером num и его интерпретация как дальнего указателя функции для обработки прерываний. Переменная типа (void (far *) ( )), указывающая функцию обработки прерываний. Пример Измерение времени, прошедшего с момента прекращения звукового сигнала до следующего нажатия клавиши клавиатуры. /*getvect*/ finclude <stdio.h> finclude <dos.h> unsigned Timer; void interrupt far Count( ); void interrupt (*Ref) ( ); main( ) { Ref - getvect(OxlC); putchar('\a*); setvect(0xlC, Count); if(kbhit( ))getch( ); while(lkbhit( )); (void)getch( ); setvect(0xlC, Ref); printfC'Xd", Timer); return 0; I1 tv u/ I l @)иЛ'
17. Принципы описания 561 } void interrupt far Count( ) { T1mer++; } gmtime ANSI Имя gmtime - преобразование даты и времени в дату и время по Гринвичу. Заголовок struct tm* girt line (long ‘clock) Прототип Функция tlme.h Преобразование времени, заданного аргументом clock, в статическую структуру типа (struct tm), содержащую следующие компоненты времени: количество секунд с начала минуты (sec), количество минут с начала часа (min), количество часов от полуночи (hour), номер дня месяца (mday), количество месяцев, начиная с января (пюп), количество лет с начала века (year), количество дней после воскресенья (wday), количество дней, прошедших с 1 января (yday) и маркер, указывающий, действует ли летнее время (isdst). Результат Переменная типа (struct tm *), указывающая описанную структуру. Замечания Тип (struct tm *), объявлен следующим образом: struct tin { Int tm_sec, tmjnln, tm_hour, tmmday, tmraon, tm_year, tmwday, tm.yday, tm_1sdst; } Пример Вывод номера дня года с соответствующим суффиксом, например, Today Is the 252nd day of the year. /‘get line*/ finclude <t1me.h> finclude <std1o.h> ma1n( ) { char Rest;
562 Часть VI. Библиотеки long Time; struct tm ‘Today; (void)time(&Time); Today ж gmtime(&Time); pr1ntf("Today is the Xd”, Today-» tm_yday + 1); switch(Rest « Today-»tm_yday X 10){ case 1: case 2: case 3: pr1ntf("X.2s”, break: "stndrd" + Rest - 1); default; prlntfC'th") } printf("day of the year”); return 0; X } inport TURBO Имя Заголовок. • inport - ввод данных с порта, int inport(int num) Прототип Определение Функция Результат dos.h dos.h Ввод слова с порта под номером num. Переменная типа int, введенная с порта. Пример Ввод номера параллельного порта, а затем вывод находящегося в этом порте слова. /‘import*/ finclude «dos.h» finclude «stdio.h» main( ) { int Num; scanf("Xd", &Num); printfC'Xx”, inport(Num)); return 0; } inportb TURBO Имя Заголовок inportb - ввод данных с порта, unsigned char inportb(int num)
17. Принципы описания 563 Прототип Определение Функция Результат dos.h dos.h Ввод байта с порта под номером num. Переменная типа (unsigned char), введенная с порта. Пример Ввод номера параллельного порта, а затем вывод находящегося в этом порте байта. /‘Importb*/ finclude <dos.h> finclude <std1o.h> ma1n( ) { Int Num; scanf("Xd". &Num); prlntfC'Xx", Inportb(Num)); return 0; } int86 TURBO Имя Заголовок Int 86 - генерация программного прерывания. Int Int 86(1nt num, union REGS *1nregs, union REGS *outregs. Прототип Функция dos.h Загрузка регистров процессора данными, содержащими- ся в группе регистров, указанной аргументом inregs, генерация прерывания с номером num, перезапись дан- ных, содержащихся в регистрах процессора, в группу регистров, указанную аргументом outregs (в частности, копирование бита переноса в поле x.cflag). Результат Переменная типа (Int), остающаяся в регистре АХ-процессора. Замечания Тип (union REGS) объявлен следующим образом: union REGS.{ struct { unsigned ax, bx, ex, dx, si, di, eflag, flags; } x; struct { unsigned char al, ah, bl, bh, cl, ch, dl, dh; } h; }; Пример Вывод на графическое печатающее устройство двух горизонтальных линий разной толщины.
564 Часть VI. Библиотеки /*1nt 86*/ finclude <dos.h> ♦define N 300 main( ) { Int nl - N X 256, n2 - N / 256; Int j • 0; void prchr(char); prchr(27); prchr('L'); prchr(nl); prchr(n2); wh11e(j++ < N) prchr(19j; prchr('\n'J; return 0; } void prchr(ch); char ch; { union REGS regs; regs.x.dx. - 0; /‘Printer no. 0*/ regs.h.ax. - 0; regs.h.al. - ch; 1nt86(0xl7,&regs, &regs); } int86x TURBO Имя Заголовок Int 86х - генерация программного прерывания. Int Int 86x(1nt nun, union REGS *1nregs, union REGS *outregs, struct SREGS *segregs) Прототип Функция dos.h Временное хранение содержимого регистра DS, загрузка регистров процессора данными из группы, указанной аргументом ingregs, и структуры segregs, генерация прерывания с номером пил, сохранение данных из регистров процессора в группе, указанной аргументом outregs. Результат Переменная типа (int), остающаяся в регистре АХ-процессора. Замечания Тип (union REGS) объявлен следующим образом: union REGS struct { unsigned ax, bx, ex, dx, si, di, eflag, flags;
17. Принципы описания 565 } х; struct { unsigned char al, ah, Ы, bh, cl, ch, dl, dh; } h; }; Тип (struct SREGS) описывается следующим образом: struct SREGS{ unsigned es, cs, ss, ds; }; Пример Вывод числа 13. /*1nt 86x*/ finclude «dos.h» finclude «alloc.h» finclude «string.h> ma1n( ) { union REGS InOut; struct SREGS Seg; char *Ref; Ref = malloc(7); (vo1d)1toa(l3, Ref, 10); Ref[strlen(Ref)] = Seg.ds = FP_SEG((vo1d far *)Ref); InOut.x.dx - FP_0FF((vo1d far *)Ref); InOut.h.ah = 0x9; Int 86x(0x21, KlnOut, KlnOut, &Seg); return 0; intdos TURBO Имя Заголовок Прототип Функция Результат Замечания Intdos - генерация системного прерывания. 1ntdos(un1on REGS *1nregs, union REGS *outregs, dos.h Загрузка регистров процессора данными из группы, указанной аргументом inregs, генерация прерывания с номером 0x21 и перезапись данных из регистров процессора в группу, указанную аргументом outregs Переменная типа (int), остающаяся в регистре АХ-процессора. Тип (union REGS) объявлен следующим образом: union REGS{ struct { unsigned ax, bx, ex, dx, si, di, eflag, flags;
566 Часть VI. Библиотеки } х; struct { unsigned char al, ah, bl, bh, cl, ch, dl, dh; } h; }; Пример Вывод надписи Ewa. /‘intdos*/ finclude <dos.h> ma inf ) { union REGS InOut; char *Name "Ewa$”; InOut.h.ah = 0x9; InOut.x.dx = FPOFF(Name); Intdosf&InOut, &InOut); return 0; } intdosx TURBO Имя Заголовок Прототип Функция Результат Замечания Intdosx - генерация системного прерывания. Int Intdosfunlon REGS *1nregs, union REGS *outregs, struct SREGS *sregs) dos.h Временное запоминание содержимого регистра OS, загрузка регистров процессора данными из группы, указанной аргументом Inregs, и структуры segregs, генерация прерывания с номером 0x21, перезапись данных из регистров процессора в группу, указанную аргументом outregs. Переменная типа (Int), остающаяся в регистре АХ-процессора. Тип (union REGS) объявлен следующим образом: union REGS{ struct { unsigned ax, bx, ex, dx, si, di, eflag, flags; } x; struct { unsigned char al, ah, bl, bh, cl, ch, dl, dh; } h; }; Тип (struct SREGS) описывается следующим образом: struct SREGS{ unssigned es, cs, ss, ds;
17. Принципы описания 567 Пример Вывод надписи Ewa. /*1ntdosx*/ finclude «dos.h» finclude «strings.h» finclude «alloc.h» ma1n( ) { union REGS InOut; struct SREGS Seg; char *Ref = farmalloc(5); (vold)strcpy(Ref, "Ewa $"); InOut.x.dx = FPOFF(Ref); Seg.ds = FP_SEG(Ref); InOut.h.ah = 0x9; 1ntdosx(&In0ut, &InOut, &Seg); return 0; } intr TURBO * Имя Заголовок Intr - генерация системного прерывания, void 1ntr(1nt Num, struct REGPACK *preg) Прототип Функция dos.h Перезапись данных в регистры процессора из структуры, указанной в аргументе pregt генерация прерывания с номером num и перезапись данных, содержащихся в регистрах, в данную структуру. Результат Замечания void Тип (struct REGPACK) объявлен следующим образом: struct REGPACK{ Insigned r_ax, r_bx, r_cx, r_dx, r_bp, r_s1, r_d1, r_ds, r_es, r_flags, } Пример Вывод текущего часа. /*1ntr*/ finclude «dos.h» finclude «sl^io.h» ma1n( ) { • struct REGPACK Regs;
568 Часть VI;Библиотеки Regs.r_ax - 0; /‘Service 0‘/ 1ntr(0xlA, AReg*); printf("Xu", return 0; } Regs. г_сх); isalnum ANSI Имя Заголовок Определение Функция Результат ( • . л'- isalnum - классификация символа, /♦int*/ 1salnun/*1nt*/ch) .. ctype.h Проверка, равно ли значение ch коду буквенно-цифрового символа, т. е. символа, представляющего собой букву или цифру ('A'..’Z ’a’..'z', 'О'..'9'). Переменная типа (int) со значением, отличным при положительном результате проверки, и со значением 0 в противном случае. от 0 Примеры , Аргумент Результат В' '3' 3 0x32 1 1 0 1 isalpha ANSI Имя Заголовок Определение Функция Результат Isalpha классификация символа. /*1nt*/ 1salpha/*1nt*/ch) ctype.h Проверка, равно ли значение ch коду буквы ('A'..’Z', 'a’..’z’>. Переменная типа (Int) со значением, отличным при положительном результате проверки, и со значением 0 в противном случае. от 0 Примеры » Аргумент Результат ’М" ’5’ 82 -27 1 0 1 0
• 17. Иринципы описания 569 iscntrl ANSI Имя Заголовок Iscntrl - классификация символа. /♦int*/ 1scntrl/*1nt*/ch) Определение Функция ctype.h Проверка, равно ли значение ch коду управляющего символа (символа delete - 0x7f или символа с кодом из интервала 0x0.. Ox If). Результат Переменная типа (Int) со значением, отличным от 0 при положительном- .результате проверки, и со значением 0 в противном случае. Примеры Аргумент ) ’А’ '\г' 255 0x10 Результат 0 ’X,:,:- 1 0 1 isdigit ANSI Имя Заголовок isdigit - классификация символа. /*1nt*/ 1sd1g1t/*1nt*/ch) Определение <. Функция ctype.h Проверка, равно ли значение ch коду цифры (0'..'9). Результат Переменная типа (Int). со значением, отличным от 0 при положительном результате проверки, и со значением 0 в противном случае. Примеры Аргумент i Результат '5' 5 ОхА ’\х30' 1 . ... 0 0 1 isgraph ANSI Имя Заголовок Isgraph - классификация символа. /*1nt*/ 1sgraph(/*1nt*/ch)
570 Часть VI. Библиотеки Определение Функция Результат ctype.h Проверка, равно ли значение ch коду видимого символа, отличного от пробела (’!’..' '). Переменная типа (Int) со значением, отличным при положительном результате проверки, и со значением 0 в противном случае. \ ОТ 0 Примеры Аргумент Результат ’5’ 0x20 г> 1 0 1 0 slower ANSI Имя Заголовок Определение* Функция Результат Is lower - классификация символа. /*int*/ Islower/*1nt*/ch) ctype.h Проверка, равно ли значение ch коду малой буквы ('a'..’z'). Переменная типа (int) со значением, отличным от при положительном результате проверки, и со значением 0 в противном случае. 0 Примеры Аргумент Результат Т 'F' 060 0101 1 0 0 0 isprint ANSI Имя Заголовок Определение Функция Результат isprint - классификация символа. Z*int*/ 1spr1nt(/*1nt*/ch) ctype.h Проверка, равно ли значение ch коду видимого символа ('!'..'-'). Переменная типа (int) со значением, отличным при положительном результате проверки, и со значением 0 в противном случае. ОТ 0
17. Принципы описания 571 Примеры Аргумент Результат 1 0 128 1 0 0 ispunct ANSI Имя Заголовок ispunct - классификация символа. /*int*/ ispunct/*!nt*/ch) Определение Функция ctype.h Проверка, равно ли значение ch коду знака препинания (пробела или управляющего символа). Результат Переменная типа (int) со значением, отличным от 0 при положительном результате проверки, и со значением 0 в противном случае. Замечания В соответствии со стандартом ANSI знаком препинания является любой символ, отличный от пробела и буквенно-цифрового символа. Примеры Аргумент Результат 0 'О’ 0x20 ЛЬ' 0 0 1 1 isspace ANSI Имя Заголовок isspace - классификация символа. /*int*/ i sspace(/*1nt*/ch) Определение Функция ctype.h Проверка, равно ли значение ch коду интервала (’ ЛУ, Лг’, Лп', ЛУ, лг). Результат Переменная типа (int) со значением, отличным от 0 при положительном результате проверки, и со значением 0 в противном случае. Примеры Аргумент Результат 0x20 1
572 Часть VL Библиотеки ’ ' 1 040 1 'А' 0 isupper ANSI Имя isupper - классификация символа. Заголовок /*int*Z 1supper(Z*1nt*/ch) Определение ctype.h Функция Проверка, равно ли значение ch коду большой буквы CA'./Z'). Результат Переменная типа (int) со значением, отличным от 0 при положительном результате проверки, и со Я значением 0 в противном случае. Замечания Функция isupper является макроопределением. Примеры Аргумент Результат ’G' 1 ’д' 0 0 \v 0 isxdigit ANSI Имя isxdigit - классификация символа. Заголовок Z*int*Z isxdigit(/*int*/ch) Определение ctype.h Функция Проверка, равно ли значение ch коду шестнадцате- ричной цифры ("О'..'9', ’A’./F'/a'./f’). Результат Переменная типа (int) со значением, отличным от 0 при положительном результате проверки, и со значением 0 в противном случае. Примеры Аргумент Результат 'А' 1 ₽а’ 1 ♦ 5 0 0x41 1
17. Принципы описания 573 itoa TURBO Имя itaa - преобразование целого числа в строковую переменную. Заголовок char* itoa(int value, char *string, int radix) Прототип Функция stdlib.h Преобразование числа, заданного аргументом value, в последовательность символов, представляющую собой число с основанием radix, и размещение этой последовательности в области памяти, указанной аргументом string. Результат Переменная типа (char*) указывающая на первый символ в области памяти. Замечания Последним символом, размещаемым в поле памяти, является символ с кодом 0, основанием может быть целое число в интервале 2. .36. Если основание десятичное, а число отрицательное, то первым символом последовательности является знак - (минус). Пример Вывод надписи 1101. /*itoa*/ finclude «stdio.h» finclude «stdlib.h» main( ) char Arr[17]; printf("Xs, itoa(13, Arr, 2)); return 0; } kbhit TURBO Имя Заголовок kbhit - убедиться в доступности символа, int kbhit(void) Прототип Функция conic.h Убедиться в том, что первое же выполнение функции getch приведет непосредственно к вводу символа с консоли. Результат Переменная типа (int) со значением, отличным от 0, если в буфере консоли находится еще не введенный символ, или со значением 0 в противном случае.
574 r Часть VI. Библиотеки Пример Вывод надписи Ready to finish, а после ввода любого символа завершение вы- полнения программы. Z*kbhit*Z finclude «stdio.h» finclude «connio.h» nain( ) { printf("Ready to finish"); while(lkbhit( )); return 0; } labs ANSI Имя Заголовок labs - определение абсолютного значения, long labs(long n) Прототип Функция * Результат stdlib.h, nath.h Определение абсолютного значения аргумента. Переменная типа (long) со значением |п|. Пример Вывод числа 13. Z*labs*Z finclude «stdio.h» finclude «stdlib.h» nain( ) { printf("%ld", labs(-13L)); return 0; } Idexp ANSI Имя Заголовок Idexp - вычисление степени, double ldexp(double value, int exp) Прототип Функция Результат nath.h Формирование переменной co значением value * 2exp. Переменная типа (double) с указанным значением. Пример Вывод числа 40.00.
17. принципы описания 575 /*ldexp*Z finclude «stdio.h» finclude «inath.h» main( ) { printf("%.2f”, ldexp(5,3)); return 0; } Idiv ANSI Имя Заголовок Idiv - вычисление остатка и частного. ldiv_t Idiv(long num, long den) Прототип Функция stdlib.h Формирование структурной переменной, компонентами которой являются две переменные типа (int): значение первой из них равно частному num/den, а значение второй - йстатку от деления num на den. Результат Структурная переменная типа (ldiv_t) с компонентами quot (частное) и rem (остаток). Пример Вывод числа 17. /*ldiv*/ finclude «stdlib.h» finclude «stdio.h» main( ) { idiv_t Result = ldiv(17,5); printf("%ld”. Result.Quot * 5 + Result.rem); return 0; J localtime ANSI Имя local time - преобразование даты и времени в их компоненты. Заголовок struct tm * localtime(const time t *clock) Прототип Функция time.h Преобразование переменной, заданной параметром *clock и полученной с помощью функции time, в структуру типа (struct tm), содержащую следующие компоненты времени: количество секунд с начала минуты (sec) количество минут с начала часа (min),
576 Часть VI. Библиотеки Результат Замечания количество часов с полуночи (hour), номер дня месяца (mday), количество месяцев, начиная с января (коп), количество лет с начала века (year), число дней с воскресенья (wday), число дней начиная с 1 января (yday) и маркер (isdst), учитывающий действие летнего времени. Преобразование производится С учетом временного пояса (time zone) и поправки на экономию энергии (daylight savings time). Структура типа (struct tm), содержащая компоненты даты и времени. Тип (struct tm) объявлен следующим образом: struct tm{ int tm_sec; tmmin; tm_hour: tm_mday; tmjnon; tm_year; tm_wday; tm_yday; tmisdst; }; Пример Вывод местного времени с точностью до 1 минуты. /*localtime*/ finclude «time.h» finclude «stdio.h» main( ) { long time; struct tm *Ref; (void)time(&Time); Ref « localtime(&Time); printf("%d: X02d", Ref -> tm_hour. Ref -> tmjain); return 0; •og ANSI Имя Заголовок Прототип Функция Результат log - вычисление натурального логарифма, double log(double х) math.h Формирование переменной со значением in х. Переменная типа (double) со значением, равным натуральному логарифму аргумента.
17. Принципы описания 577 Пример Вывод числа 3.00. /*1од*/ finclude <stdio.h> finclude <math.h> main( ) { printf("%.2f", log(exp(3))); return 0; } loglO ANSI Имя Заголовок loglO - вычисление десятичного логарифма, double loglO(double х) Прототип Функция Результат math.h Формирование переменной со значением 1од10х. Переменная типа (double) со значением, равным десятичному логарифму аргумента. Пример Вывод числа 2.00. /*1од10*/ finclude <stdio.h> finclude <math.h> main( ) { printf("%.2f", loglO(TOO)); return 0; } longjmp ANSI Имя longjmp - возвращение в точку запоминания состояния выполнения программы. Заголовок void longjmp(jmp_buf env, int val) Прототип Функция setjmp.h Возврат в точку запоминания состояния выполнения программы и его восстановление таким образом, как будто запоминание (с помощью функции setjmp) закончилось выводом переменной типа (int) со зна- чением val.
S78 Часть VI. Библиотеки Результат Замечания void Если val представляет величину, равную 0, то прини- мается, что она представляет величину, равную 1. Пример Вывод надписи Finished here. /*longjmp*/ finclude <setjmp.h> finclude <stdio.h> main( ) { jmp_buf State; void sub(^); if(setjmp(State)){ printf("Finished here"); return 0; } sub(State); printf("Not here"); } void sub(par) jmp_buf par; { longjmptpar, 13); } Itoa TURBO Имя Заголовок Itoa - преобразование целого числа в строку символов char * ltoa(long value, char ‘string, int radix) Прототип Функция stdlib.h Преобразование значения числа, заданного аргументом value, в последовательность символов в виде числа с основанием radix и размещение этой последовательно- сти в массиве, первый символ которой указывается аргументом string. Результат Переменная типа (char*), задающая первый символ массива. Пример Вывод надписи f4240. /*Поа*/
17. Принципы описания 579 finclude «stdio.h» finclude «stdlib.h» main( ) { char Arr[33J; printf(”%s", ltoa(1000000, Arr, 16)); return 0; } malloc ANSI Имя Заголовок malloc - выделение памяти, void* malloc(size_t size) Прототип Функция Результат stdlib.h, alloc.h Выделение области памяти размером size байтов. Переменная типа (void*), локализующая выделенную область памяти, или значение NULL в случае, если выделение области невозможно. Пример Вывод числа 4.50. /*malloc*/ finclude «stdio.h» finclude «alloc.h» main( ) { float *Ref = malloc(sizeof(float)); *Ref = 4.50; printf("%.2f", Ref[0]); return 0; } memchr ANSI Имя Заголовок memchr - поиск символа, void * memchr(const void *s, int *s, char ch, size t n) Прототип Функция string.h, mem.h Просмотр первых n байтов массива, указанного пара- метром s, для нахождения символа с кодом ch. Результат Переменная типа (void *), указывающая тот элемент массива, который содержит искомый символ, или пустое значение, если символ не найден.
580 Часть VI. Библиотеки Пример Вывод надписи Bielecki. /* memchr */ finclude <stdio.h> finclude <mem.h> char Arr[13] = "Jan Bielecki”; main( ) { printf("%s", (char * return 0; } )memchr(Arr + 2, 'B', 5)); memcmp » ANSI e Имя Заголовок Прототип Функция Результат Замечания memcmp - сравнение последовательностей символов, int memcmp (const void *sl, const void *s2, size_t n) string.h, mem.h Сравнение двух строк символов длиной п, указанных параметрами si и s2. Переменная типа (int) принимающая отрицательное значение, если si < s2, значение 0, если si == s2, положительное значение, если si > s2. Сравнение произодится в предположении, что символы принадлежат типу (unsigned char). Пример Вывод положительного числа. /•memcmp*/ finclude <stdio.h> finclude <mem.h> main( ) { printfC'Xd”, memcmp( return 0; } "Jan”, "Ewa”, 2)); memcpy ANSI Имя memcpy - копирование последовательности символов. Заголовок void * memcpy(void *destin.
17. Принципы описания S8I Прототип функция const void ‘source, size_t n) string.h, mem.h Копирование последовательности n символов из области, указанной параметром source, в область, Результат указанную параметром destin. Переменная типа (void *), указывающая область, в которую скопирована последовательность символов. Пример Вывод надписи Kaja. /*memcpy */ finclude «stdio.h» finclude «mem.h» main{ ) { char *Name = "Kaja”; printfC'Xs”, -1 + char *) memcpy(Name + 1, Name, 2)); return 0; memmove ANSI Имя Заголовок memmove - копирование блока памяти. memmove(void *trg, void *src, size t count) Прототип Функция string.h, mem.h Копирование count байтов из области оперативной памяти, указанной параметром src, в область памяти, указанную параметром trg. Результат Переменная типа (void *), указывающая начало области, в которую производится копирование. Пример Вывод надписи Isa. /*memmove* / finclude «string.h» finclude «stdio.h» main( ) { char Source[ ] = "Isabel”; Target[ ] = memmove(Target, Source, 3); printf("%s", Target); return 0; }
582 Часть VI. Библиотеки memset ANSI Имя Заголовок wemset - заполнение области, void * men>set(void *s, int ch, size t n) Прототип Функция string.h, mem.h Заполнение области оперативной памяти, указанной параметром s, п символами с кодом ch. Результат Указание адреса области памяти, заданной параметром s. Пример о Вывод надписи Jan—Ewa. /*memset*/ /include «stdio.h» /include «mem.h» char Arr[ ] = "Jan.. Ewa'; main{ ) { printff'Xs", (char *) memeet(Arr + 3, 2) - 3); return 0; } mkdir TURBO Ймя Заголовок mkdir - создание каталога, int nkdir(const char *path) Прототип Функция dir.h Создание каталога с именем, указанным параметром path. Результат Переменная типа (int) со значением 0 в случае успешного создания каталога и со значением -1 в противном случае; при этом переменной errno присваивается одно из следующих значений: EACCESS - доступ запрещен, ENOENT - нет такого каталога. Пример Создание каталога Isabel. /"mkdir*/ /include «dir.h» /include «stdio.h»
П. Принципы описания 583 main{ ) { if С iTnkdirC'CzWIsabel”)) printf("Isabel created"); return 0; } MK-FP TURBO Имя Заголовок MKFP - создание указателя. /*void far**/ MK_FP(/‘unsigned */ seg, /‘unsigned */ off) Прототип Функция dos.h Создание указателя, номер сегмента которого равен значению переменной, заданной параметром seg, а смещение равно значению переменной, заданной параметром off. Результат Переменная типа (void far*), представляющая собой созданный указатель. Пример Вывод символа, находящегося в нулевом байте нулевого сегмента памяти. /‘MKFP */ finclude <dos.h> finclude «stdio.h» main( ) printf("%c", *(char *) MK_FP(O,O)); return 0; } modf ANSI Имя Заголовок modf - разделение числа на целую и дробную части, double modf(double value, double *1ptr) Прототип Функция math.h Разделение числа, указанного параметром value, на целую и дробную части. Возвращение целой части и присвоение значения дробной части переменной, указанной параметром iptr. Результат Переменная типа (double) со значением, равным целой части заданного числа.
584 Часть VI. Библиотеки Пример Вывод числа 13.60. е /*modf*/ finclude «stdio.h» finclude <math.h> main( ) { double Int, Fra; Int = modf(13.6, &Fra); printf("%.2f”, Int <• Fra): return 0; } movedata TURBO Имя Заголовок movedata - копирование областей памяти, void movedata(unsigned segsrc, unsigned offsrc. • unsigned segdest, unsigned offdest size t num) Прототип Функция string.h, mem.h Копирование num байтов из области памяти с адресом segsrc:offsrc в область памяти с адресом segdst: offdst Результат void Пример Вывод надписи Jan Bielecki. /*movedata */ finclude «stdio.h» finclude «dos.h» finclude «mem.h» fdefine FAR(void far *) char Name[ ] = "Jan"; char Surname[ ] = "...Bielecki”; main{ ) { movedata(FP_SEG(FAR Name), FP_OFF(FAR Name), FP_SEG(FAR Surname), FP_OFF(FAR Surname), 3); printf("%s", Surname); return 0; }
17. Принципы описания 585 nosound TURBO Имя nosound - выключение динамика. Заголовок void nosound(void) Прототип dos.h Функция Выключение динамика ЭВМ. Результат void Пример Трансляция звукового сигнала с частотой 440 Гц, прерываемого каждые 200 мс. /*nosound*/ finclude <dos.h> finclude <conio.h> main( ) { int i; for(i - 0; 1 < 20; . sound{440); delay(200); nosound( ); delay(ZOO); } return 0; } 1++К outport TURBO Имя outport - вывод переменной на пословный порт. Заголовок void outport(int num, int word) Прототип dos.h Определение dos.h Функция Вывод на словный порт с номером num переменной, заданной параметром word. Результат Пример void Ввод номера порта и слова данных, а затем вывод этого слова на порт с указанным номером. /‘outport*/ finclude <stdio.h> finclude <dos.h>
586 Часть VI. Библиотеки main{ ) { int Port, Word; scanf ("XdXd"', SPort, Word); outport(Port, Word); return 0; } outportb TURBO Имя Заголовок outportb - вывод переменной на байтовый порт, void outportfint num. Прототип Функция unsigned char byte) dos.h Вывод на байтовый порт с номером num переменной, заданной параметром byte. Результат void Пример Ввод номера порта и байта данных, а затем вывод этого байта на порт с указанным номером. /"outportb*/ finclude <stdio.h> finclude <dos.h> main( ) { int Port, Byte; scanf{''XdXd", Wort, &Byte); outportfPort, Byte); return 0; } peek TURBO Имя Заголовок peek - вывод слова данных, int peek(unsigned seg, unsigned off) Прототип Определение Функция dos.h dos.h Вывод слова данных, находящегося в оперативной памяти по адресу seg:off. Результат Переменная типа (int), находящаяся по указанному адресу.
17. Принципы описания 587 Пример В случае монитора, управляемого адаптером Hercules, вывод слова данных из экранного буфера. /•peek */ finclude «stdio.h» finclude «dos.h» main( ) { printfC'Xd", peek(0xB000, 2)); return 0; } peekb TURBO Имя Заголовок peekb - вывод байта данных, char peekb(unsigned seg, unsigned off) Прототип Определение Функция dos.h dos.h Вывод байта данных, находящегося в оперативной памяти по адресу seg: off. Результат Переменная типа (int), находящаяся по указанному адресу. Пример В случае монитора, управляемого адаптером Hercules, вывод байта данных из экранного буфера. /•peekb */ finclude «stdio.h» finclude «dos.h» nain{ ) { printfC'Xc", peek(0xB000, 2)); return 0; } perron ANSI Имя perror - вывод сообщения об ошибке, произошедшей при выполнении программы. Заголовок void perror(const char ‘string) Прототип Функция stdio.h Вывод в файл, идентифицируемый stderr, сообщения о
588 Часть VI. Библиотеки последней ошибке, произошедшей при обращении к системе. Выдаваемое сообщение состоит из последова- тельности символов, задаваемой параметром string, символа : (двоеточие) и связанного с ошибкой текста (номер ошибки находится в глобальной переменной еггпо); текст завершается символом новой строки. Результат void Пример Вывод перечня сообщений об ошибках при выполнении программ. /*реггог*/ finclude <stdio.h> finclude <errno.h> extern char * sys_err1ist[ ]; extern sys_nerr; main( ) { int Ind; for(Ind *0; Ind < sys_nerr; Ind ++) printf("\nX3d Xs", Ind, sys_err1ist[Ind]); return 0; • } poke TURBO Имя роке - размещение слова данных в оперативной памяти. Заголовок viod poke(unsigned seg, unsigned off,' int value) Прототип Определение Функция dos.h dos.h Помещение переменной, заданной параметром value, ячейку памяти с адресом seg:off. Результат void Пример В случае монитора, надписи jb в левом управляемого адаптером Hercules, вывод мигающей верхнем углу экрана. I* poke */ finclude<stdio.h> include<dos.h> nain() { poke(0xB000,0,'j'+(0x87«8));
17. Принципы описания 589 роке(0хВ000,2,'Ь'+(0х87«8)); return 0; } pokeb TURBO Имя pokeb - размещение байта данных в оперативной памяти Заголовок void pokeb(unsigned seg, unsigned off, char value) Прототип Определение Функция dos.h dos.h Помещение переменной, заданной параметром value, байт памяти с адресом seg: off Результат Пример void В случае монитора, управляемого адаптером Hercules, вывод буквы Е в левом верхнем углу экрана. /* pokeb */ finclude<dos.h> main( ) { pokeb(0x8000,0, 'E'); return 0; } pow ANSI Имя Заголовок Прототип Функция Результат Замечания pow - вычисление степени. double pow(double х, double у) math.h Формирование переменной со значением ху. Переменная типа (double) со значением хЛ В случае переполнения возвращается значение HUGE_VAL, при ЭТОМ errno>ERANGE. Пример Вывод числа 1024. /* pow */ #include<stdio.h> #include<nath.h>
590 Часть VI. Библиотеки main{ ) { pr1ntf("%0f", pow(2,10)); return 0; } printf ANSI Имя printf - редактирование и вывод последовательности символов. Заголовок Int printf(const char *format. Прототип Функция 3 • • • ) stdio.h Вывод в стандартный выходной файл в соответствии с перечнем шаблонов, заданным параметром format, значений аргументов, стоящих на месте многоточия. Результат Переменная типа (int) со значением, равным числу выводимых символов, или со значением EOF в случае возникновения ошибки. Замечания • Вызов printf(/и/, argl, arg2, ..., argn) эквивалентен вызову fprintf(stdout, fmt, argl, arg2, ..., argn) Пример Вывод надписи Hello world. /* printf */ f1nclude<stdio.h> main{ ) { printff’Hello world"); return 0; } putc ANSI Имя Заголовок putc - вывод символа. 1nt putc{int ch, FILE * stream) Определение Функция stdio.h Вывод символа с кодом ch в файл, идентифицируемый параметром stream.
17. Принципы описания 591 Результат Переменная типа (int) co значением, равным коду выводимого символа, или со значением EOF в случае возникновения ошибки. Пример Вывод буквы К в стандартный выходной файл, а в случае возникновения ошибки вывод знака вопроса в стандартный файл сообщений об ошибках. /* putc * #1nclude<stdio.h> main( ) { if(putc('K', stdout) “ EOF putc('?', stderr); return 0; } putch TURBO Имя Заголовок putch - вывод символа на консоль, int putchar(int ch) Прототип Функция Результат conio.h Вывод символа с кодом ch на консоль. Переменная типа (int) со значением, равным коду выводимого символа. Пример Вывод на консоль надписи jb. /* putch */ finclude«conio.h> main( ) { putch('j'); putch('b'); return 0; } putchar ANSI Имя Заголовок putchar - вывод символа, int putchar(int) ch) Определение Функция stdio.h Вывод в стандартный выходной файл символа с кодом ch. Результат Переменная типа (int) со значением, равным коду
592 Часть VI. Библиотеки выводимого символа, или co значением EOF в случае возникновения ошибки. Пример Вывод в стандартный выходной файл надписи jb. /* putchar */ finclude<stdio.h> main( ) { putchar(putchar('j')—B); return 0; putenv TURBO Имя Заголовок putenv - дополнение перечня параметров окружения, int putenv(const char *envvar) Прототип Функция • stdlib.h Присоединение к перечню параметров окружения строки env = var, Результат заданной параметром envvar. Переменная типа (int) со значением 0 или -1 в случае возникновения ошибки. Пример Вывод текста, характеризующего значение параметра path. /* putenv */ finclude<stdlib.h> finclude<stdio.h> main( ) { putenv("PATH=”); putenv("PATH=\\JB-D0S3; WJB-TC"); printf("PATH = Xs", , getenvf'PATH")); return 0; } puts ANSI Имя Заголовок puts - вывод последовательности символов, int puts(const char *string) Прототип Функция stdio.h Вывод в стандартный выходной файл
17. Принципы описания 593 последовательности символов, заданной параметром string и дополненной символом новой строки. Результат Переменная типа (int) со значением, равным коду последнего выводимого символа, или со значением, равным коду последнего выводимого символа, или со значением EOF в случае возникновения ошибки. Пример Вывод надписи Helle world. /* puts */ #include<stdio.h> main( ) { putsf'Hello world"); return 0; } qsort ANSI Имя Заголовок qsort - сортировка данных. void qsort(void *base, size_t nelem, sizet width, int (*fcmp)(const void *, const void*))) Прототип Функция stdlib.h Сортировка nelem элементов данных размером width каждый, находящихся в области оперативной памяти, указанной параметром base. Для сравнения данных используется двухаргументная функция femp, подобранная таким образом, чтобы ее значение зависело от аргументов argl и arg2, соответствующих сравниваемым данным, следующим образом: * argl < *arg2 - отрицательное значение, * argl — *arg2 - значение 0, * argl > *arg2 - положительное значение. Результат void Пример Сортировка структур по полю Аде, вывод имен Ola, Ula, Ela. I* qsort */ finclude<stdio.h> #1nc Jude<std1i b.h> struct Girls{ char*Name; unsigned char Age; } Arr[3] - {"Ela"' 9
594 Часть VI. Библиотеки "01а", 5, "Ша", 7}; main ( ) { int Comp(Struct Girls *, Struct Girls *); int Ind - 0; qsort(Arr, 3, sizeof(struct Girls). Comp); while(Ind < 3) printf("\nXs", Arr[Ind ++], Name); return 0; } int Comp(elml, elm2) struct Girls *elml, *elm2; { if(elml->"Age<elm2->Age) return -1; return a 1ml -> Age } > alm2 -> Аде; £ raise J ANSI Имя Заголовок raise - генерация сигнала, int raise(int sig) Прототип Функция Результат signal.h Генерация сигнала sig. Переменная типа (int) со значением 0 в случае успешной генерации сигнала и со значением, отличным от 0, в противном случае. Замечания Примерами генерируемых сигналов являются: SIGABRT - аварийное завершение выполнения программы, SIGFPE - ошибка в операции с плавающей точкой, например, деление на нуль. Пример Если введенное число имеет значение 0, то вывод надписи: Signal #8, Division by zero /* raise */ finclude<signal.h> finclude<stdio.h> void zero(int sig) printf(”Signal #Xd, Division by zero", sig); } main( ) { float Float - 100.0;
/7, Принципы описания 595 Int Num; signal(SIGFPE, zero); scanf("Xd", &Num); if(Num “ 0) raise(SIGFPE); else printf("Xf", Float / Num); retutn 0; } rand ANSI Имя Заголовок rand - генерация случайного числа, int rand(void) Прототип Функция stdlib.h Формирование псевдослучайного числа со значением в интервале 0. .32767. Результат Замечания Псевдослучайное число типа (int). Период генератора составляет 232. Пример Генерация псевдослучайных чисел. /* rand */ #include<stdio.h> #include<stdlib.h> main( ) { while(!kbhit ( )) printf("X2d", rand( ) % 10); return 0; realloc ANSI Имя Заголовок realloc - выделение памяти, void * realloc(void ptr, size t size) Прототип Функция stdlib.h alloc.h Увеличение или сокращение области памяти, указанной параметром ptr, таким образом, чтобы ее новый размер был равен size. Результат Переменная типа (void), указывающая модифицированную область памяти. Замечания Модификация размера области может потребовать перенесения ее содержимого в другие места.
596 Часть VI. Библиотеки Пример Вывод надписи jb. /* realloc */ #include<stdio.h> finclude<alloc.h> main ( ) char *Ref - malloc(2); Ref[O] - 'j'; Ref[l] - 'b'; Ref - realloc(Ref,3); Ref[2] - ’\0’; printf("Xs", Ref); return 0; remove ANSI Имя Заголовок , Определение Функция Результат Замечания Пример Вывод числа 0. /* remove */ finclude<stdio.h> main( ) { FILE * Doc; int Res; Doc - fopen("WORK", fclose(Doc); Res - remove("WORK") printfC’Xd", Res"; return 0; } remove - удаление файла, int renove(const char "name) stdio.h Удаление файла с именем, указанным параметром name. Переменная типа (int) со значением 0 в случае успешного удаления файла или со значением -1 в противном случае. Во втором случае переменной errno присваивается одно из следующих значений: EACCESS - файл только для чтения, ENOENT - файл отсутствует.
17. Принципы описания 597 rename ANSI Имя rename - изменение имени файла. Заголовок int rename(const char *oldname, const char *newname) Прототип stdio.h Функция Замена имени файла, заданного параметром oldname, именем, заданным параметром newname. Результат Переменная типа (int) со значением 0 или -1 в случае возникновения ошибки. Во втором случае переменная errno принимает одно из следующих значений: errno=ENDENT - файл не найден, errno=EACCESS - переименование не разрешено, errno=ENOTSAM - другое устройство. Замечания Объектом переименования может быть только один файл. Указание дисковода должно быть одним и тем же в oldname и newname. Каталоги могут быть разными. Пример Перемещение и переименование файла. /* rename */ finclude«stdio.h> main( ) { printf'Xd", rename("\\ISA\\JAN", "WEVAWKAJA")); return 0; } rewind ANSI Имя rewind - установка файла в начальную позицию. Заголовок void rewindfFILE *stream) Прототип stdio.h Функция Установка файла, идентифицируемого параметром stream, в начальную позицию. Обнуление указателя конца файла и указателя ошибки. Результат Переменная типа (int) со значением 0 или со значением, отличным от 0, в случае возникновения ошибки. Пример Троекратный вывод в стандартный выходной файл содержимого файла AUTOEXEC.BAT. /* rewind */
598 Часть VI. Библиотеки finclude stdio.h main( ) { int Count “ 3; Int Chr; FILE *Inp - fopen("\\AUTOEXEC.BAT", "r") while(Count —){ while((Chr - getc(Inp))! - EOF) putc(Chr, stdout); putc('\n', stdout); rewind(Inp); } return 0; } rmdir TURBO Имя Заголовок rmdir - удаление каталога, int rmdir(const char *path) Прототип Функция • dir.h Удаление каталога с именем, указанным параметром path. Результат Переменная типа (int) со значением 0 (в случае успешного выполнения функции) или -1 (в противном случае); в последнем случае переменной errno присваивается одно из следующих значений: EACCESS - доступ запрещен, ENOENT - дефектная дорожка. Пример Удаление каталога Isabel. /* rmdir */ #include<dir.h> finclude<stdio.h> main( { rmdir(''C:\\Isabel' return 0; } scanf ANSI Имя scanf - вывод и интерпретация последовательности символов. Заголовок int scanf(const char *format, ...)
17. Принципы описания 599 Прототип Функция stdio.h Ввод из стандартного входного файла в соответствии с перечнем шаблонов в последовательности, заданной параметром format, набора литералов, находящихся в полях файла, и присвоение значений этих литералов переменным, указанным аргументами, стоящими на месте многоточия. Результат Замечания Число значений, присвоенных указанным переменным. Вызов scanf(fmt, argl, arg2, ..., argn) равнозначен вызову fscanf(stdin, fmt, argl, arg2, ..., argn) Пример Ввод из стандартного входного файла одной строки текста, а затем ее вывод в стандартный выходной файл. /* scanf */ #include<stdio.h> main( ) { char Arr[81]; scanf("Xs", Arr); printf("Xs", Arr"); return 0; } setbuf ANSI Имя Заголовок setbuf - замена буфера файла, void setbuf(FILE ‘stream, char *buf) Прототип Функция stdio.h Замена буфера, автоматически выделенного для файла, идентифицируемого параметром stream, буфером, указанным параметром buf. Результат Замечания void Необходимо, чтобы размер буфера был равен BUFSIZE (макроопределение с таким именем находится в файле stdio.h). Если параметру buf будет соответствовать пустое значение, то операции над файлом будут выполняться без использования буфера. Пример Вывод в файл WORK символа * (звездочка). Это действие выполняется, несмотря на аварийное завершение выполнения программы.
600 Часть VI. Библиотеки /* setbuf */ finclude<stdio.h> main( ) { FILE *0ut; Out = fopen("WORK", ’V); setbuf(Out, NULL); putc('*'. Out); abort( ); return 0; /* Formally required */ } setdate TURBO Имя » Заголовок Прототип Функция Результат Замечания* setdate - установка даты, void setdate(struct date *date) dos.h Установка системной даты на основе полей структуры (struct date), заданной параметром date. void Структура типа (struct date) объявлена следующим образом: struct date{ int da_year; /* год */ char da_day; /• день * / char dajnon; /* месяц * / Пример Вывод надписи 1981. /* setdate */ finclude<dos.h> struct date Savel, Save?, Date= {1981, 13, 12}; main( ) { getdate(&Savel); setdate(&Date); getdate(&Save2); printf("Xd", Save2.da_year); setdate(&Savel); return 0; > setdisk TURBO setdisk - замена текущего дисковода. Имя
17. Принципы описания 601 Заголовок Int setd1sk(1nt drive) Прототип Функция dir.h Дисковод, указанный в параметре drive, объявляется текущим. Результат Переменная типа (int) со значением, равным числу дисководов системы. Замечания Дисковод а имеет номер 0, дисковод В номер 1 и т.д. Пример Объявление текущим дисковода В. /* setdisk */ finclude<dir.h> finclude<stdio.h> main( ) { enum{ А, В, C}; setdisk(B); return 0; } setjmp ANSI Имя Заголовок settjmp - запоминание состояния программы, int setjmp(jmp buf env) Прототип- Функция setjmp.h Запоминание состояния программы в виде значения переменной env. Результат Переменная типа (int) со значением 0 (или со значением, определяемым функцией longjmp в момент возврата). Пример Вывод числа 127. / *setjmp */ flnc1ude<setjmp.h> flnc1ude<stdio.h> jmpbuf State; main( ) { register Num = 13; 1f(setjmp(State) printf("Xd", Num); return 0; } Num = 127;
602 Часть VI. Библиотеки 1ongjmp(State, 1); } С settime ANSI Имя Заголовок settime - установка времени, void settine(struct time *time) Прототип Функция dos.h Установка системного времени на основе полей структуры, указанной параметром time. Результат Замечания void Структура типа (struct tine) объявлена следующим Я образом: struct tine { unsigned char ti_m1n, /* минута */ t1_hour, /* час */ tl_hund, /* сотые часа * / t1_sec, /* секунда */ Пример * Установка часов на начало текущей минуты. /* settime */ finclude<dos.h> f includes tdio.h» main( ) struct tine Clkck; get tirne(&C lock); Clkcl.ti_sec = 0; sett1me(&Clock); return □; } setvbuf ANSI Имя Заголовок setvbuf - замена буфера файла. Inf setvbuf(FILE *strean, char *buf, Int type; size t size) Прототип Функция stdio.h Замена буфера, автоматически выделенного файлу, идентифицируемому параметром stream, буфером размера size, первый символ которого указан параметром buf. Способ использования буфера задается значением параметра type.
17. Принципы описания 603 Результат Замечания Переменная типа (int) со значением 0 или со значением, отличным от 0 в случае возникновения ошибки. Если type = I0FBF, то операции являются полностью буферизованными. Это означает, что в процессе ввода передача содержимого буфера в файл происходит только после полного заполнения буфера. Если type «-_IOLBF, то буферизация является построчной. Это означает, что в процессе ввода заполнение буфера прерывается после ввода символа новой строки, а в процессе вывода передача содержимого буфера в файл происходит сразу же после вывода такого символа. Если type “_IONBF, то буферизация прекращается. Пример Вывод в файл WORKER надписи Very long text (если бы из программы был удален символ '\п', то в файле была помещена только надпись Very long). /* setvbuf */ finclude<std1o.h> vo1d_ex1t( ); main( ) FILE *0ut = fopen("WORKER";"*"); cher Buffer[10]; setvbuf(Out, Buffer,_IOLBF, 10); fprintf(Out, "Very long text\n"); abort( ); return 0; /* Formally required */ } setvect TURBO Имя Заголовок Прототип Функция Результат setvect - изменение вектора прерывания, void setvect(1nt num, void Interrupt (*1sr)O) dos.h Изменение вектора прерывания таким образом, чтобы генерация прерывания с номером num привела к вызо- ву функции обработки прерывания, указанной параметром isr. void Пример Вывод надписи Here I ат. 'Jr»*
604 Часть VI. Библиотеки /* setvect */ finclude<stdio.h> finclude<dos.h> «define INT 0x80 main( ) { void interrupt far Display( ); void interrupt (far * Ref)(); struct REGPACK Dummy; Ref - getVect(INT); setvect(INT, Display); IntrfINT, &Dummy); setyect(Int, Ref); return 0; } void interrupt far Display! ) { printf("Here I am"); } signal ANSI Имя Заголовок Прототип Функция Результат Замечания signal - регистрация обработки сигнала. void (*signal(int sig, void (*fun)(int sig)))(int) signal.h Регистрация функции *fun, которая должна быть вызвана с аргументом sig сразу же после передачи сигнала. Переменная типа ((*)(int)>, представляющая собой второй аргумент последнего вызова функции signal с первым аргументом sig или значение S16—ERR в случае неудачного вызова функции signal. Непосредственно перед вызовом (*fun)(sig) неявно вызывается функция signal(sig, SIGDFL), что приводит к заданию обработки сигнала sig по умолчанию. Кро ме обработки по умолчанию можно потребовать также игнорирования сигнала. Для этого можно воспользо ваться вторым аргументом функции signal, придавая ему значение SIG IGN. Первый аргумент этой функции может принимать, например, следующие значения: SIGABRT - аварийное завершение выполнения программы, SIGFPE - ошибка в операции с плавающей точкой, например, вследствие деления на 0. Пример Суммирование последовательности цифр, заканчивающейся числом 0.
17. Принципы описания 605 /* signal */ finclude<s1gnal.h> finc1ude<std1о.h> char Name[20j; char Flag; void Interrupt(sig) { Flag l^sig; /* Disregard! sig */ } main( ) { int Num, Sum = 0; signal(SIGABRT, Interrupt); while(IFlag){ printf ("\n Enter a number:"); scanf("Xd", &Num); Sum += Num; if(Num == 0) raise(SIGABRT); } printf("Xd”, Sum); return 0; } sin Имя Заголовок Прототип Функция Результат Замечания ANSI sin - вычисление синуса double sin(double х) main.h Формирование переменной со значением sin х. Переменная типа (double) со значением, равным синусу аргумента. Аргумент выражен в радианах. Пример Вывод числа 0.50. /* sin */ #include<stdio.h> finclude<math.h> main( ) { const float Pi = 3.141593; printf("X.2f", s1n(Pi/6)); return 0; }
606 Часть VI. Библиотеки sinh ANSI Имя Заголовок sinh - вычисление гиперболического синуса, double sinh(double х) Прототип Функция Результат math.h Формирование переменной со значением sinh х. Переменная типа (double) со значением, равным Замечания гиперболическому синусу аргумента. В случае переполнения результатом выполнения функции будет значение HUGE_VAL, при этом errno=ERANGE. Пример „ Вывод сообщения. sinh: OVERFLOW error sinh(lOOO): Result too large /* sinh */ #1nclude<s^d1o.h> #1nclude<math.h> finclude<errno.h> main( ) { (vold)sinh(lOOO); perror(”s1nh(1000)"); return 0; } sleep TURBO Имя sleep - временное приостановление выполнения программы. Заголовок void sleep(unsigned sec) Прототип Функция dos.h Приостановление выполнения программы на период sec секунд. Результат void Пример Генерация звукового сигнала с частотой 0.5 Гц. I* sleep */ f 1 nc 1 ude<s td 1 о. h> #1nclude<dos.h> #include<conio.h>
17. Принципы описания 607 main( ) { wh11e(lkbhit( )){ puchar('\a'); sleep(2); } return 0; } sound TURBO Имя Заголовок sound - включение динамика, void sound(unsigned freq) Прототип Функция dos.h Включение динамика и трансляция непрерывного сигнала с частотой freq Герц. Результат void Пример Генерация звуков, прерываемая и возобновляемая при нажатии произвольной клавиши клавиатуры. /* sound */ #1nclude<dos.h> #1nclude<conio.h> main( ) { unsigned Hz = 300; int 1; sound (Hz); for(i = 1; 1< 6; i++){ while(tkbhitO); (void)getch( ); if(1 & 1) nosound( ); else sound(Hz); } return 0; } sprintf ANSI Имя sprintf - редактирование и размещение в оперативной памяти последовательности символов. Заголовок int sprintf(char ‘spring, const char ‘format, ...)
608 Часть VI. Библиотеки Прототип Функция Результат Замечания Пример stdio.h Размещение в области памяти, первый элемент которой указан аргументом string, в соотвествии с перечнем шаблонов (первый символ которого указан аргументом format) значений аргументов, стоящих на месте многоточия. Последовательность вводимых символов заканчивается символом новой строки. Переменная типа (int) со значением, равным числу вводимых символов, или со значением EOF в случае возникновения ошибки. Функция sprintf отличается от функции fprintf тем, что последовательность символов размещается не в файле, а в области оперативной памяти. Вывод числ^ 13. /* sprintf */ finclude<stdio.h> main( ) { char Arr[3]; sprintf fArr,"%d”, 13); retutn 0; sqrt Имя Заголовок Протокол Функция Результат Замечания ANSI sqrt - вычисление квадратного корня. double sqrt(double х) math.h Формирование переменной со значением Vx. Переменная типа (double) со значением, равным квадратному корню из аргумента. Если значение аргумента меньше 0, то результатом выполнения функции будет значение 0, при этом errno=ED0M. Пример Вывод числа 12. /* sqrt */ finclude<std1о.h> finclude<math.h> main( ) { printf("%.Of”, sqrt(144)); return 0; }
17. Принципы описания 609 srand ANSI Имя Заголовок srand - заполнение генератора псевдослучайных чисел, void srand(unsigned seed) Прототип Функция stilb.h Очередное заполнение генератора псевдослучайных чисел. Результат Замечания void Перед первым применением генератора производится его заполнение с аргументом, равным 1. При выборе другого значения генерируется другая последовательность псевдослучайных чисел. Пример Вывод двух идентичных строк, содержащих псевдослучайные числа. /* srand */ finclude<stdio.h> #include<stdlib.h> main( ) { Int Count = 0; while(Count++ <40) printf("*2d\n". Count - 0; srand(l); rand( ) % 10); while(Count ++< 40) printf("%2d\n". rand( ) % 10); return 0; } sscanf ANSI Имя sscanf -извлечение и интерпретация последовательности символов. Заголовок int sscanf(const char string, const char ‘format. Прототип Функция ...) stdio.h Извлечение из области памяти, первый элемент кото- рой указан аргументом string, в соответствии с переч- нем шаблонов, первый символ которого указан аргу- ментом format, находящихся в этой области литералов и присвоение значений этих литералов переменным, указанным аргументами, находящимися на месте многоточия.
610 Часть VI. Библиотеки Результат Замечания Число значений, присвоенных указанным переменным. Функция sscanf отличается от функции fscanf тем, что последовательность полей извлекается не из файла, а из области оперативной памяти. Пример Вывод надписи 13!. /* sscanf */ f1nclude<std1o.h> main( ) { Int Fix; char Chr; sscanff'Flx » 131", prlntfCXdXc”, Fix, "Fix = Xd%c", &F1x.&Chr); Chr); return 0; } strcat ANSI Имя Заголовок strcat - соединение последовательностей символов, char strcat(char *dest1n, const char ‘source) Прототип Функция string.h Присоединение к последовательности символов, ука- занной аргументом destin, последовательности символов, указанной аргументом source. Результат Переменная типа (char*), указывающая на первый символ объединенной последовательности. Замечания Первый символ последовательности, указанной аргу- ментом source, размещается в байте, занимаемом символом с кодом 0. Пример Вывод надписи Izabela. /* strcat */ f1nclude<std1o.h> f1nc1ude<str1ng.h> char Arr[8] = "Iza"; main( ) { pr1ntf("%s", strcat(Arr, "bela")); return 0; }
17. Принципы описания 611 strchr ANSI Имя Заголовок Прототип Функция Результат Замечания strchr - поиск символа, char * strchr(const char *str, int c) string.h Просмотр последовательности символов, указанной ар- гументом str, с тем, чтобы найти символ с кодом с. Переменная типа (char *), указывающая найденный символ, или пустое значение, если символ в после- довательности отсутствует. Просмотр ведется в направлении вперед, а при с — \0' объектом поиска будет символ с кодом 0. Пример Вывод надписи uni а. /strchr */ #include<stdio.h> #include«string.h> char Name[ ] = "Ewunia"; tnain( ) { printf("%s", strchr(Name, 'u')); return 0; } strcmp ANSI Имя Заголовок Прототип Функция Результат Замечания strcmp - сравнение последовательностей символов, int strcmp(const char *strl, const char *strZ) string.h Сравнение последовательности символов, указанной аргументом strl, с последовательностью символов, указанной аргументом strZ. Переменная типа (int) со значением, отражающим ре- зультат сравнения последовательностей, а именно при strl < strZ - отрицательное значение, strl == strZ - значение 0, strl > strZ - положительное значение. Сравнение производится в предположении, что символы принадлежат типу (signed char). Пример Вывод отрицательного числа.
612 Часть VI. Библиотеки /* strcmp */ finclude<stdio.h> #include<string.h> main( ) \ { printf("Xd”, strcmp("13jan”, "13kaja")); return 0; } strcpy ANSI Имя strcpy - копирование последовательности символов. Заголовок char* strcpy(char *destin, const char *source) Прототип string.h Функция Копирование последовательности символов, заданной параметром source, в область памяти, первый элемент которой указан аргументом destin. Результат Переменная типа (char*), указывающая на первый элемент области памяти, в которую производится копирование. Замечания* Последним копируемым символом является символ с кодом 0. Пример Вывод надписи 99. /* strcpy */ findude<stdio.h> # 1nc1ude<string.h> char Arr[ ] - "12345”; main( ) { ptintf("Xs“, strcpy(Arr + 1, “ЭЭ”)); return 0; } strcspn ANSI Имя strcspn - выделение последовательности символов, предшествующих заданному ограничителю. Заголовок size t strcspn(const char *strl, const char *str2) Прототип string.h Функция Определение числа тех начальных символов последовательности, указанной аргументом strl, ни один из которых не принадлежит множеству символов, входящих в последовательность, указанную аргументом strZ.
17. Принципы описания 613 Результат Переменная типа (int) co значением, равным числу символов, предшествующих заданному аргументу. Пример Вывод числа 3. /* strcspn */ finclude<stdio.h> iinc1ude<str1ng.h> main( ) { printf("Xd", strcspn("127, 13", return 0; } strerror ANSI Имя Заголовок strerror - вывод указателя сообщения, shar * strerror(int num) Прототип Функция string.h, stdio.h Вывод указателя последовательности символов, представляющей собой сообщение о появлении ошибки с номером num. Результат Переменная типа (char*), указывающая на первый из символов, составляющих сообщение. Замечания Указанная последовательность символов находится в статической области памяти. При каждом вызове функции strerror ранее находившаяся в этой области информация уничтожается. Пример Вывод текста следующего сообщения с номером 1: Invalid function number. /* strerror */ # inc1ude«string.h> finclude<stdio.h> main( ) { ptintf(’’Xs”, strerror(l)); return 0; } strlen ANSI Имя strlen - определение длины последовательности символов.
614 Часть VI. Библиотеки Заголовок size_t strlen(const char *str) Прототип Функция strung.h Определение числа символов в последовательности, указанной аргументом str. Результат Переменная типа (size—t) со значением, равным чис- лу символов в последовательности, завершающейся символом с кодом 0. Замечания Символ с кодом 0 не засчитывается. Пример Вывод числа 7. /* strlen */ #include<stdfo.h> #include<string.h> nain( ) { printfstrlen(”Kajusia")); return 0; } strncat ANSI Имя Заголовок strncat - соединение последовательностей символов, char* strncat(char *destin, const char *source, size t naxlen) Прототип 'Функция string.h Присоединение к последовательности символов, ука- занной аргументом destin, не более, чем naxlen симво- Замечания лов последовательности, указанной аргументом source. Первый символ присоединяемой последовательности размещается в байте, занимаемом символом с кодом 0. Пример Вывод надписи EINSTEIN. /* strncat */ #include<stdio.h> # inc1ude«string.h> char Arr[9] = "EIN"; main( ) { printf("%s", strncat(Arr. "STEINBERG”, 5)); return 0; }
17. Принципы описания 615 strncmp ANSI Имя Заголовок strncmp - сравнение последовательностей символов, int strncmp(const char *strl. const char *str2, size t maxlen) Прототип Функция string.h Сравнение последовательности символов, указанной аргументом strl, с последовательностью символов, указанной аргументом strZ. Сравнению подлежит не более, чем maxlen символов. Результат Переменная типа (int) со значением, отражающим результат сравнения последовательностей, в частности, при strl < strZ - отрицательное значение, strl == strZ - значение 0, strl > strZ - положительное значение. Замечания Сравнение производится в предположении, что символы принадлежат типу (signed char). Пример Вывод числа 0. /* strncmp */ #include<stdio.h> #1nc1ude<str1ng.h> main( ) { printg("%d", strncmp("13jan", ”13kaja", 2)); return 0; } stmcpy ANSI Имя Заголовок strncpy - копирование заданного числа символов, char* strncpy(char *destion, const char *source, size t maxlen) Прототип Функция string.h Копирование проследовательности символов, заданной аргументом source, в область памяти размером maxlen, первый символ которой указывается аргументом destin. Если число копируемых символов меньше maxlen, то остальная часть предназначенной для копирования области заполняется символами с кодом 0. Результат Переменная типа (char*), указывающая на первый
616 Часть VI. Библиотеки элемент области памяти, в которую происходило копирование. Пример Вывод надписи Kaja. /* strncpy */ tinc1ude<stdio.h> #inc1ude<string.h> char Nane[ ] = "Iza"; main( ) { printf("X.4s", strncpy(Nane, "Kaja", 4)); return 0; strpbrk ANSI Имя strpbrk - поиск первого вхождения одного из заданных символов. Заголовок* char* strpbrk(const char *strl, const char *str2) Прототип Функция string.h Поиск в последовательности символов, заданной аргу- ментом strl, такого символа, который содержится в последовательности символов, указанной аргументом str2. Результат Переменная типа (char*), указывающая на искомый символ или пустое значение, если такой символ не найден. Пример Выход числа 34. /* strpbrk */ findude<stdio.h> #1nc1ude<str1ng.h> main ( ) { printf("Xs", strpbrk("12.34",",.")+l); return 0; } strrchr V, ANSI Имя strrchr - поиск последнего вхождения заданного символа. Заголовок char*
17. Принципы описания 617 strrchr (const char *str, int c) Прототип Функция string.h Поиск в последовательности символов, заданной аргументом str, последнего символа с кодом с. Результат Переменная типа (char*), указывающая найденный символ, или пустое значение, если такой символ не Пример найден. Вывод надписи 20. /* strrchr */ fine lude<stdio.h> #include<string.h> main( ) { printf("Xs", strrchr("12:25:20",’:’)+l); return 0; } strspn ANSI Имя strspn - подсчет символов, принадлежащих заданной последовательности символов. Заголовок size_t strspn(const char *strl, const char *str2) Прототип Функция string.h Подсчет числа тех начальных символов последователь- ности, заданной аргументом strl, которые принадле- жат множеству символов, содержащихся в последова- тельности, указанной аргументом str2. Результат Переменная типа (size_t) со значением, равным числу указанных символов. Пример f Ввод целого числа, а затем вывод числа его цифр. I* strspn */ #include<stdio.h> #1nc1ude<string.h> #1nc1ude<std1ib.h> main( ) { long Nun; char Arr [33]; scanf("Xld"; &Num); printfC’Xd", strspn(ltoa(Nun, Arr, 10) "0123456789"));
618 Часть VI. Библиотеки return 0; } strstr ANSI Имя strstr - поиск последовательности. Заголовок char* strstr(const char *strl, const char *strZ) Прототип string.h Функция Поиск в последовательности символов, заданной аргу- ментом strl, первой подпоследовательности, иден- тичной последовательности, заданной аргументом str. Результагп Переменная типа (char*), указывающая на первый символ последовательности, заданной strl, с которого начинается подпоследовательность, заданная strZ, или пустое значение, если такая подпоследовательность не содержится. Пример Вывод надпйси unia. /* strstr */ #include<stdto.h> finc1ude<string.h> main( ) { printf(’’Xs", strstrf’Ewunia", ”un")); return 0; } strtod ANSI Имя strtod - преобразование последовательности символов в число с плавающей точкой. Заголовок double strtod(const char *str, char **endptr) Прототип stdlib.h Функция Интерпретация начального фрагмента последователь- ности символов, заданной аргументом str, как записи числа с плавающей точкой и формирование перемен- ной типа (double), равной этому числу. Результат Переменная типа (double) со значением, равным чис- лу, записанному в виде последовательности символов. Замечания Если endptr 1 NULL, то переменной *endptr будет при своено значение, указывающее на тот символ входной последовательности, заданной аргументом str, который " ие я^ляется элементом числа с плавающей точкой.
17. Принципы описания 619 Пример Проверка функции strtod, завершаемая вводом новой строки. /* strtod */ # 1nc1ude<std1o.h> # Inc1ude<string.h> main( ) { char Buf[81]; gets(But); wh11e(strlen(Buf)){ printf(”\n%f\n", strtod(Buf, NULL)); gets(But); } return 0; } strtok ANSI Имя Заголовок Прототип Функция Результат Замечания strtok - поиск лексических единиц char* strtok(char *strl, const char *str2) string.h Интерпретация последовательности символов, заданной аргументом argl, как последовательности лексических единиц, разделенных ограничителями, содержащимися в последовательности, заданной аргументом strZ, и возвращение очередных таких лексических единиц. Переменная типа (char*), указывающая на первую из найденных лексических единиц, или пустое значение - если такой единицы нет. После выделения лексической единицы из последова- тельности, заданной аргументом strl, непосредственно за ней размещается символ с кодом 0. Вызов функ- ции с первым аргументом, имеющим пустое значение, трактуется так, будто этот аргумент указывает на символ, следующий непосредственно после упомянутого символа с кодом 0. Пример Вывод идентификаторов, содержащихся в двух операторах присвоения (по одному в каждой строке). /* strtok */ # 1no1ude<stdiо.h> # 1nc1ude<str1ng.h> main( )
620 Часть VI. Библиотеки { char (Asg « "a = b -> • c + d;” "Ь = с + а; char *Ref = strtok(Asg, "+;") do { printf("\nXs", Ref); Ref « strtck(NULL, } while(Ref); return 0; } strtol ANSI Имя js strtol - преобразование последовательности символов в целое число. Заголовок long strtol(const char *str, char **endptr, int base) Прототип Функция , stdlib.h Интерпретация начального фрагмента последовательно- сти символов, заданной аргументом str, как записи целого числа с основанием base и формирование пере- менной типа (long) со значением, равным этому числу Результат Переменная типа (long) со значением, равным числу, записанному в виде указанной последовательности символов. Замечания Если endptri^NULL, то переменной, заданной аргументом, которому соответствует endptr, будет присвоено значение, указывающее на тот символ входной последовательности, который не является элементом целого числа. Если base — 0, то число, начинающееся с Ох или ох, рассмаривается как шестнадцатеричное число; число, которое не является шестнадцатеричным и начинается с 0, рассматривается как восьмеричное, а число, которое не начинается с 0, рассматривается как десятичное. Пример Проверка функции strtol, завершаемая вводом пустой строки. /* strtol */ finclude<stdio.h> #1nc1ude<std11b.h> main( ) { char *Ter; char Buf[81]; gets(Buf);
17. Принципы описания 621 wh11e(str1en(Buf) printf(”\nXldXc' ){ ", strtol(Buf, &Ter, 16), *Ter); gets(Buf); } return } system ANSI Имя Заголовок system - выполнение системной директивы. Int system(const char ‘command) Прототип Функция stdlib.h, process.h Выполнение программы COMMAND.COM для интерпретации директивы, заданной аргументом command. Результат Переменная типа (int), возвращаемая выполненной программой. Замечания Поиск программы COMMAND.COM осуществляется на основе маршрута, определяемого строкой среды с именем COMMAND. Пример Вывод информации о файлах текущего каталога. /* system */ #inc1ude<std1ib.h> ma1n( ) { system("DIR"); return 0; } tan ANSI Имя Заголовок tan - вычисление тангенса, double tan(doub1e х] Прототип Функция Результат math.h Формирование переменной со значением tg х. Переменная типа (double) со значением, равным тангенсу аргумента. Замечания Аргумент выражается в радианах. Если абсолютное значение аргумента близко к pl/2, то результатом выполнения функции будет 0, a errno=ERANGE. Пример Вывод числа 1.00.
622 Часть VI. Библиотеки /* tan */ #include<stdio.h> #inc1ude<math.h> main( ) { float const Pi - 3.141593; printf:“X.2f", tan(Pi/4)); return 0; } tanh ANSI Имя Заголовок $ Прототип Функция Результат tanh - вычисление гиперболического тангенса, double tanh(double х) math.h Формирование переменной со значением tanh х. Переменная типа (double) со значением, равным гиперболическому тангенсу аргумента. Пример . Вывод числа 0.00. /* tanh */ #include<stdio.h> #include<math.h> main( ) { printf:"%.2f", tanh(O)); return 0; } time Имя Заголовок Прототип Функция Результат Замечания _______ANSI ------------------------------------------------------- time - сообщение о текущем времени. time_t у time(time_t *tloc) tiine.h Присвоение переменной, указанной аргументом tloc, целочисленного значения, равного количеству секунд, прошедших от полуночи 1 января 1970 г. (время по Гринвичу). Переменная типа (time_t) со значением, равным указанному количеству секунд. Значение результата будет присвоено также переменной, указываемой аргументом функции.
17. Принципы описания 623 Пример Вывод числа 20. /* time */ #include<stdio.h> #include<tiine.h> main( ) { tiuet Before, After; (void)time(&Before); sleep(ZO); printfC'Xd", (int)(time(&After) - Before)); return 0; } tmpfile ANSI Имя Заголовок tmpfile - создание рабочего файла. FILE* tmpfile(void) Прототип Функция stdio.h Создание рабочего файла с уникальным именем, выбираемым системой, указание для него канала и открытие канала в режиме "w+b" (см. функцию fopen). Результат Переменная типа (FILE*), идентифицирующая канал. Если создание файла или открытие канала невозможно, то результатом функции является пустое значение. Замечания Нормальное завершение выполнения программы или закрытие канала влечет за собой автоматическое удаление созданного файла. Пример Вывод надписи Hello. /* tmpfile */ #include<stdio.h> main( ) { FILE *ioFILE = tmpfile( ); int Chr; fprintf(ioFile, "Hello”); rewind(ioFile); while((Chr » fgets(ioFile)) ! » EOF) putchar(Chr); return 0; 1
624 Часть VI. Библиотеки tmpnam ANSI Имя Заголовок Прототип Функция Результат Замечания tmpnam - создание уникального имени файла. char* tmpnam(char *name) stdio.h Создание уникального имени файла и помещение этого имени в область памяти, указанную аргументом name. Если name==NULL, то имя помещается в область, принятую по умолчанию. Указание первого элемента области, в которую помещено имя. При реализации принято, что имя должно содержать не более 10 символов, а число уникальных имен, которые могут быть созданы, не превышает 32767. Пример Вывод трех уникальных имен файлов. /* tmpnam */ #1nclude<stdio.h> main( ) { Int Num; for(Num « 1; Num printf("Xs\n", return 0; } < 4; NunH+) tmpnam(NULL)); tolower ANSI Имя Заголовок Прототип Функция Результат Замечания tolower - преобразование прописной буквы в строчную, int tolower(int с) ctype.h Преобразование кода с прописной буквы в код строчной буквы. Переменная типа (int) со значением, равным коду строчной буквы, соответствующей исходной прописной букве. Если значение аргумента не является кодом буквы, то выполняется тождественное преобразование. Пример Вывод надписи е!. /* tolower */
17. Принципы описания 625 #include<stdio.h> #1nc1ude<ctype.h> main( ) { putchar(tolower('E’)); putchar(tolower ('!’)); return 0; toupper ANSI Имя toupper - преобразование строчной буквы в прописную. Заголовок int toupper(int с) Прототип ctype.h Функция Преобразование кода строчной буквы в код прописной буквы. Результат Переменная типа (Int) со значением, равным коду прописной буквы, соответствующей исходной строчной букве. Если значение аргумента не является кодом буквы, то выполняется тождественное преобразование. Замечания Пример Вывод надписи El. /* toupper */ #include<std1o.h> #1nc1ude<ctype.h> main( ) { putchar(toupper('e')); putchar(toupper('!')); return 0; } ultoa TURBO Имя ultoa - преобразование целого числа без знака в последовательность символов. Заголовок char* ultoa(unsigned long valie, char *string, int radix) Прототип stdlib.h Функция Преобразование числа, представляемого аргументом value, в последовательность символов, имеющую вид числа с основанием radix, и размещение этой последовательности в области памяти, первый элемент которой указан аргументом. 40—764
626 Часть VI. Библиотеки Результат Переменная типа (char*), указывающая на первый символ сформированной последовательности. Замечания Последним помещаемым в область памяти символом является символ с кодом 0. Основанием может быть целое число в интервале 2. .36. Пример Вывод надписи ffffffff. /* ultoa */ #include<stdio.h> #include<std1ib.h> main( ) { char Arr[33]; printf(''%S*, ultoa(-l, Arr, 16)); return 0; } ungetc ANSI Имя Заголовок ungetc - возвращение символа в канал, int ungetc(int с, FILE ^stream) Прототип Функция stdio.h Возвращение символа с кодом с в буфер, связанный с каналом, идентифицируемым аргументом stream. Результат Переменная типа (int) со значением, равным коду возвращаемого символа, или со значением EOF, если возвращено более одного символа. Пример Вывод буквы j. /* ungetc */ #include<stdio.h> #inc1ude<coniо.h> main( ) { char Chr; ungetc('j', stdin); Chr » getc(stdin); putchar(Chr); return o; }
17. Принципы описания 627 ungetch TURBO Имя ungetch - возвращение символа в буфер клавиатуры. Заголовок int ungetchfint с) Прототип conio.h Функция Возвращение символа с кодом с в буфер клавиатуры. Результат Пример Вывод буквы j. Переменная типа (int) со значением, равным коду возвращаемого символа, или со значением EOF, если возвращено более одного символа. /* ungetch */ #include<stdio.h> #include<conio.h> ma inf ) { char Chr; ungetchf'j'); Chr » getchf ); putchar(Chr); return 0; } unlink TURBO Имя unlink - удаление файла. Заголовок int unlinkfconst char "name) Прототип dos.h, io.h, stdio.h Функция Удаление файла с именем, заданным аргументом name. Результат Переменная типа (int) со значением 0 при успешном выполнении функции и со значением -1, если произошла ошибка. В последнем случае переменный errno присваивается одно из следующих значений: ENOENT - файл не найден, EACCESS - доступ не разрешен. Замечания Файлы с признаком только для чтения удалению не подлежат. Пример Создание файла, а затем его удаление. /* untinr */ #include<stdio.h> #include<dos.h> mainf ) {
628 Часть VI. Библиотеки char "Name » "Work”; FILE "Out “ fopen(Name, ’V): fclose(Out); unlink(Name); return 0; } va-arg ANSI Имя Заголовок va_arg - возвращение аргумента. /* type */ va_arg(/* va_list param, type) Определение Функция » stdarg.h Возвращение очередного аргумента типа (type), находящегося на месте параметра в списке param. Результат Переменная типа (type), равная значению очередного аргумента функции. Замечания Использование функции va_arg требует предварительного выполнения функции va_start с аргументом param. Пример Вывод надписи jb. /* va_arg */ #include<stdart.h> #include<stdio.h> main( ) { void sub(char,...); sub('j','b'); return 0; } void sub(char Chr,...) { va_1ist State; va_staret(State, Chr); printf("Xc%c", Chr, } va_arg(State, int)); va-end ANSI Имя Заголовок va_end - завершение возвращения аргументов. /* void */ va end(/*va Ibst */ param) Определение Функция stdarg.h Завершение действий, связанных с возвращением аргументов, находящихся на месте параметров списка param.
17. Принципы описания 629 "Результат Замечания void Использование функции va_end требует предварительного выполнения функции va_start с аргументом param. Пример Вывод надписи jb. /* va_end */ #i nclude<stdart.h> #include<stdio.h> main( ) { char fun(int,...); printf("Xc%c", fun(2,'*', 'j'), fun(3,’*','*','b’)); return 0: } char fun(int Count,...); {va_list State: char Chr; va_start(State, Count); while(—Count) (void)va_arg(State, int); Chr » va_arg(State, int); return 0; } va—start ANSI Имя Заголовок 1 Определение Функция Результат Замечания va_start - подготовка к возвращению аргументов. /* void */ va_start(va_list param, lastfix) stdarg.h Выполнение предварительных действий, предшествующих возвращению аргументов функции, стоящих на месте параметров списка param. /* void */ Аргументами функции va_start являются: имя вспомогательной переменной типа (va_Hst) и lastfix - имя параметра, предшествующего списку параметров param. Пример Вывод суммы чисел 5, б и 7,
630 Часть VI. Библиотеки /* va_start */ #include<stdarg.h> #indude<stdio.h> main( ) { void sum(long *, int,...); long Sum « 0; sum(&Sum,3,5,6,7); printf(”%ld”, Sum); return 0; } void sum(1ong ‘Sum,int Count,...) { valist State; va-start(State, Count); while(Count —) ‘Sum += va_arg(State, int); va_end(State); vfprintf , ANSI Имя vfprintf - редактирование и вывод последовательности символов. Заголовок int vfprintf(FILE ‘stream, const char ‘format, va list param) Прототип Функция stdio.h Вывод в файл, идентифицируемый аргументом stream в соответствии с перечнем шаблонов в последовательно- сти, заданной аргументом format, значений тех аргу- ментов функции vfprintf, которые представлены списком параметров param. Результат Переменная типа (int) со значением, равным числу выведенных символов, или со значением EOF в случае возникновения ошибки. Замечания Функция vfprintf отличается от функции fprintf тем, что ее неявными аргументами являются аргументы вызываемой функции, соответствующие параметрам списка param. Пример Вывод в файл TARGET надписи Jan. /* vfprintf •/ #ino1ude<stdio.h> #i nclude<stdarg.h> main( ) {
17. Принципы описания 631 FILE ‘Out - fopen("TARGET"); void output (FILE */ . outputfOut.'j'.'ait' return 0; ...); n’); } void outputfFILE *Fid,...); { va_11st Arg; va_start(Arg,Fid); vfprintffFid, "XcXcXc", Arg); va_end(Arg); } vfscanf TURBO Имя vfscanf - ввод и интерпретация последовательности символов. Заголовок int vfscanf(FILE ‘stream, const char ‘format, va list param) Прототип Функция stdio.h Ввод из файла, идентифицируемого аргументом stream, в соответствии с перечнем шаблонов в последовательности, заданной аргументом format, набора литералов, находящихся в канале, и присвое- ние значений этих литералов переменным, указанным теми аргументами вызываемой функции vfscanf, которые представлены списком параметров param. Результат Число значений, присвоенных указанным переменным. Пример Для входных данных 131 вывод надписи 131 /* vfscanf */ finc1ude<stdio.h> # 1 nc 1ude<stdarg.h> mainf ) { void inputflnt,...); char Chr; int Fix; InputfZ.&FIx, &Chr); printf("XdXc", Fix, Chr); return 0; } void inputflnt Count,...)
632 Часть VI. Библиотеки va_list Arg; va_start(Arg, Count); vfscanf(stdin, "XdXc", Arg); va_end(Arg); }_______________________________________________________________________________________ vprintf ANSI Имя Заголовок Прототип Функция Т? Результат Замечания* Пример Вывод надписи jb. /* vprintf */ finclude<stdarg.h> finclude<stdio.h> ma inf ) { void output(char,.. oututf'j'.'b'); return 0; } void output(char Chr,...) { va_list Arg; va_start(Arg, Chr); printff'Xc”, Chr); vprintf("Xc", Arg); va_end(Arg); } vprintf - редактирование и вывод последовательности символов. int vprintf(const ‘format, valist param) stdio.h Вывод в стандартный выходной канал в соответствии с перечнем шаблонов в последовательности, заданной аргументом format, значений тех аргументов вызывае- мой функции vprintf, которые представлены списком параметров param. Переменная типа (int) со значением, равным числу выведенных символов, или со значением EOF (в случае возникновения ошибки). Вызов функции vprintf(/ли/, argl, arg2, ..., argn) равнозначен вызову функции vfprintf(stdout, fmt, argl, arg2, ..., argn). .);
17. Принципы описания 633 vscanf TURBO Имя vscanf - ввод и интерпретация последовательности символов. Заголовок < int ' vscanf(const char ‘format, • va_Ust param) Прототип stdio.h Функция Ввод из стандартного входного канала в соответствии с перечнем шаблонов в последовательности, заданной аргументом format, набора литералов и присвоение значений этих литералов переменным, указанным теми аргументами вызываемой функции vscanf, которые представлены списком параметров param. Результат Число значений, присвоенных указанных, переменным. Замечания Вызов функции vscanf(fmt, argl, arg2,..., argri) равнозначен вызову функции vscanf(stdin, fmt,argl ,arg2,...,argn) Пример Для входных данных 131 вывод надписи 131 /* vscanf */ # i nc1ude<stdarg.h> ti nclude<stdio.h> main{ ) { void input(int int Fix; char Chr; input(2,Fix, &Chr); printf("%dXc", Fix, Chr); return 0; } void input(int Ref,...) { va_list Arg; va_start(Arg, Ref); scanf( "%dX", Ref); vscanf("%c'*,Arg); } vsprintf ANSI Имя vsprintf - редактирование и вывод последовательности символов.
634 Часть VI. Библиотеки Заголовок 1nt vsprlntffchar *str1ng, const char ‘format, va_!1st param) Прототип Функция stdio.h Размещение в области памяти, первый элемент, который указан аргументом string, в соответствии с перечнем шаблонов, первый символ которого задан аргументом format, значений тех аргументов вызываемой функции vsprintf, которые представлены списком параметров param. Результат Переменная типа (1nt) со значением, равным числу выведенных символов, или со значением EOF (в случае возникновения ошибки). & Пример Вывод надписи jb. /* vsprintf */ f1nclude<stdarg.h> f1nclude<st{l1o.h> ma inf ) void output(char, ...); output('j'.'b') return 0; } void outputfchar Chr,... f ) vallst Arg; char Buf[3]; \ va_start(Arg, Chr); ' sprintf(Buf, "Xc". Chr); --- vsprintf(Buf + l,"Xc", Arg); prlntff'Xs", Buf); } vsscanf TURBO Имя vsscanf - редактирование и ввод последовательности символов. Заголовок Int vsscanf (const char ‘strong, const char ‘format, va list param) Прототип Функция stdio.h Ввод из области памяти, первый элемент которой указан аргументом string, в соответствии с перечнем
17. Принципы описания 635 шаблонов в последовательности, заданной аргументом formst, набора литералов, и присвоение значений этих литералов переменным, указанным теми аргументами вызываемой функции vsscanf, которые представлены списком параметров. Результат Число значений, присвоенных указанным переменным. Пример Вывод надписи 13!. /* vsscanf */ fine lude<s tdarg. h> #include<stdio.h> main( ) { int Fix; char Chr; void input(char input("13l", &F1x, &Chr); pr1ntf(“XdXc”, Fix, Chr); return 0; } void input(char *₽tr,...) { va_list Arg; va_start(Arg, Ptr); vsscanf(Ptr "XdXc", Arg); va_end(Arg); }
Литература 1. Banhan М., The С Book, Addison-Wesley, Menlo Park, California, 1988. 2. Harbison S., Steele G., C: A Reference Manual, Prentice Hall, Englewood Cliffs, 1988. 3. Draft Proposed American National Standard for Information Interchange - Programming Language C, ANSI, New York, 1988. 4. Turbo C 2.0 - User's Guide, Borland International, Scotts Valley, California, 1988. \ 5. Turbo C 2.0 - Reference Guide, Borland International, Scotts Valley, California, 1988. I 6. Turbo Assembler - User's Guide, Borland International, Scotts Valley, California, 1988. _ 7. Turbo Assembler - Reference Guide, Borland International, Scotts Valley, California, 1988. 8. Turbo Debugger - User's Guide, Borland International, Scotts Valley, California, 1988.
ПРИЛОЖЕНИЕ А. КОДЫ Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char 0 00 16 10 P 32 20 48 30 0 1 01 "A 17 11 Q 33 21 T 49 31 1 2 02 В 18 12 R 34 22 H 50 32 2 3 03 *C 19 13 S 35 23 # 51 33 3 4 04 “D 20 14 *T 36 24 $ 52 34 4 5 05 E 21 15 'U 37 25 % 53 35 5 6 06 *F 22 16 V 38 26 & 54 36 6 7 07 "G 23 17 *w 39 27 55 37 7 8 08 'H 24 18 "X 40 28 ( 56 38 8 9 09 I 25 19 Y 41 29 ) 57 39 9 10 OA J 26 1A *Z 42 2A * 58 ЗА 11 OB К 27 IB [ 43 2B + 59 ЗВ 12 ОС L 28 1C \ 44 2C 60 ЗС < 13 OD *M 29 ID 1 45 2D - 61 3D SB 14 OE 'N 30 IE 46 2E 62 ЗЕ > 15 OF 0 31 IF — 47 2F / 63 3F ? Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char 64 40 @ 80 50 P 96 60 112 70 P 65 41 A 81 51 Q 97 61 a 113 71 q 66 42 В 82 52 R 98 62 b 114 72 r 67 43 C 83 53 S 99 63 c 115 73 s 68 44 D 84 54 T 100 64 d 116 74 t 69 45 E 85 55 и 101 65 e 117 75 u 70 46 F 86 56 V 102 66 f 118 76 V 71 47 G 87 57 w 103 67 q 119 77 w 72 48 H 88 58 X 104 68 h 120 78 X 73 49 I 89 59 Y 105 69 i 121 79 У 74 4A J 90 5A Z 106 6A j 122 7А z 75 4B К 91 5B [ 107 6B к 123 7В { 76 4C L 92 5C \ 108 6C 1 124 7С 77 4D M 93 5D 1 109 6D m 125 7D J8 4E N 94 5E 110 6E n 126 7Е 79 4F 0 95 5F - 111 6F о 127 7F del
ПРИЛОЖЕНИЕ Б. РЕДАКТОР В отличие от других редакторов экранный редактор системы Турбо Си ориентирован на ввод текстов, а не на прием директив. В связи с этим управление операциями с текстом производится с помощью символов, не имеющих непосредственного графического представления. Такие символы называются управляющими, а их использование сводится к одновременному нажатию клавиши Ctrl и одной или двух буквенных клавиш. Поскольку при дальнейшем изложении придется очень часто ссылаться на эти символы, договоримся выделять управляющие символы знаком ' (каре), обозначающим нажатие клавиши Ctrl. Например, ввод с клавиатуры управляющего символа Ctrl-A (осуществляемый путем одновременного нажатия клавиши Ctrl и клавиши А) будет называться выполнением директивы "А, а ввод управляющего символа Ctrl-K-D (осуществляемый путем одновременного нажатия клавиши Ctrl и клавиш К и t>) будет называться выполнением директивы ‘KD. Типичный сеанс работы с редактором начинается с активизации окна редактирования. В его заголовке выводится текущая информация о ходе редактирования: Line - номер строки, содержащей курсор, Col - номер столбца, содержащего курсор, Insert . - указание способа ввода символов, Indent - указание способа размещения курсора в новой строке, Tab - признак табуляции, Name.ext - имя файла. Как можно убедиться, любое нажатие клавиши приводит к появлению на экране одного символа. Этот символ появляется в позиции, занимаемой курсором. Если ввод символов производится в режиме Insert, то перед выполнением этого действия все символы в строке, начиная с символа, выделенного курсором, сдвигаются на одну позицию вправо. В режиме без Insert происходит лишь ввод символа и перемещение курсора. Перевод редактора из режима Insert в режим без Insert и обратно производится при помощи клавиши Ins. Сразу же после нажатия клавиши Enter “комплектация" текущей строки текста заканчивается и происходит перемещение курсора на следующую строку. В режиме Indent курсор будет помещен под первым отличным от пробела символом предшествующей строки. Это очень удобно при подготовке программ, содержащих отступы. Перевод редактора из режима без Indent и обратно производится с помощью директивы 01. Если при вводе текста будет нажата клавиша Tab, то курсор переместится на ближайшую позицию табулятора. Выполнение директивы "ОТ вызывает включение и отключение табуляции. Ввод, изменение и удаление части текста С точки зрения ввода текста редактор можно рассматривать как многофункциональную пишущую машинку. Эта машинка позволяет вводить символы и слова и выполнять операции над уже введенными словами. Под “словом" понимается любая последовательность символов, заканчивающаяся одним из следующих ограничителей:
Редактор 639 пробел <>,:'><) [ ]*'* + -/ $ Перемещение курсора на одну позицию влево, вправо, вверх и вниз производится с помощью клавиш, обозначенных стрелками, или с помощью директив ‘Е, “S, 'D и 'X, соответствующие им клавиши образуют ромб t Е D S г х Нетрудно видеть, что директива 'Е перемещает курсор на одну строку вверх, директива 'X перемещает его на одну строку вниз, a “S и "D соответственно на одну позицию влево и на одну позицию вправо. Благодаря такому решению функция директивы определяется не мнемоникой наименования ее клавиши, а положением клавиши в ромбе. Перемещение курсора сразу на целое слово производится с помощью дополнительных директив, ‘А и "F (а также Ctrl*- и Ctrl-» ): Е AS D F X Использование директивы *А приводит к перемещению курсора на одно слово влево, а использование директивы “F - на одно слово вправо. Следующая пара директив "В и ‘С (а также PgUp и PgDn) служит для перемещения курсора соответственно в . предшествующий и последующий экраны. Клавиши этих директив расположены справа от оси, задаваемой клавишами Е-Х. Слева от этой оси расположена пара клавиш для сдвига текста, не умещающегося на экране, на одну строку: "W вниз и "Z вверх. Таким образом, схема размещения клавиш, используемых для управления положением курсора и определяющих отображаемую на экране часть текста, имеет следующий вид: W Е R AS D F Z X с Эта схема легко запоминается и не требует от пользователя, чтобы он идентифицировал клавиши по имеющимся на них надписям. Справа от рассмотренного набора клавиш находятся три клавиши директив 'Т, "Y и ,‘G, расположенные следующим образом:
640 Приложение Б Использование директивы 'G вызывает удаление символа, выделяемого курсором, использование директивы "Т вызывает удаление символов, начиная с символа, выделяемого курсором, и до конца слова, а использование директивы "Y - удаление строки, в которой находится курсор. Приведенный перечень директив дополняют клавиши Backspace (<— ) и Delete (Del). Нажатие первой из них приводит к удалению символа, находящегося слева от курсора, а нажатие второй - к удалению символа, выделяемого курсором. Сводка директив “Е - перемещение курсора на одну строку вверх в том же столбце, *Х - перемещение курсора на одну строку вниз в том же столбце, 'S - перемещение курсора на одну позицию влево, 'D - перемещение курсора на одну позицию вправо, "F - перемещение курсора на начало слова вправо, "R - пермещение окна, через которое пользователь видит документ, • на один экран вверх, * С - перемещение окна, через которое пользователь видит документ, на один экран вниз, "W - перемещение текста на экране на одну строку вниз, "Z - перемещение текста на экране на одну строку вверх, * G - удаление, символа, выделяемого курсором, * Т - удаление символа, выделяемого курсором, а также следующих за ним символов данного слова, 'Y - удаление строки, в которой находится символ, выделяемый курсором. Приведенный перечень директив, позволяющих отображать на экране и удалять с экрана произвольные части текста, дополнен списком директив, выполняющих функции, которые сходны с только что описанными, но выполняются с большим “шагом". Каждая их них состоит из символа Q, непосредственно за которым следует один из символов, составляющих схему вида: Е R S D X С Директива 'QE (а также Ctrl-Home) вызывает перемещение курсора на первую строку экрана, директива "QX (а также Ctrl-End) - перемещение курсора на последнюю строку экрана, директива “QS (а также Ноте) приводит к перемещнию курсора на начало, а директива "QD (а также End) - на конец той строки, в которой находится курсор. Остальные две
Редактор 641 директивы - "QR и QC (соответственно Ctrl-PgUp и Ctrl-PgDn) перемещают курсор в начало и в конец текста. Перемещением курсора с помощью произвольных сочетаний перечисленных здесь директив можно пользоваться с двоякой целью: во-первых, просмотр текста для проверки его содержания и, во-вторых, переход в область, в которой необходимо внести изменения. Эти изменения могут состоять в дополнении данного текста или замене его другим текстом. Для дополнения текста достаточно ввести необходимые дополнения с клавиатуры. Редактор вставит этот текст перед символом, выделяемым курсором, одновременно сдвигая этот и следующий за ним символы. Чтобы заменить текст, можно его сначала удалить и заменить новым - путем добавления, или старый текст вытеснить новым. Второй способ не самый лучший, потому что редко бывает, чтобы старый и новый тексты имели одну и ту же длину. Кроме того, его применение требует временного выключения режима Insert, для чего необходимо воспользоваться директивой 'V (или 1ns). Сложные операции с текстом Рассмотренные до сих пор директивы позволяют не только создавать, но и практически произвольным образом обрабатывать документы. Тем не менее в некоторых случаях полезны операции, относящиеся к более крупным фрагментам текста, например, такие как перестановка или копирование групп соседних строк, а также замена определенных слов или предложений. Для этого предназначены директивы, рассматриваемые ниже. Директива *QF После выполнения этой директивы на экране появляется надпись Find: т.е. запрос о той фразе, которую надлежит отыскать в тексте. Эта фраза представляет собой произвольную последовательность символов, завершающуюся символом Enter. Сразу же после нажатия клавиши Enter на экране появляется вопрос Options: на который следует ответить указанием режима поиска фразы. В наиболее частом случае - поиск фразы, начиная с позиции, выделяемой курсором, в направлении конца текста - достаточно в ответ на этот вопрос нажать клавишу Enter. Если искомая фраза действительно находится в тексте, то курсор будет установлен непосредственно за последним символом найденной фразы. Как легко догадаться, применяя директиву QF, мы пользуемся директивой контекстного редактора. Следует отметить, что среди опций, определяющих режим поиска фразы, имеются, в частности, следующие: В - поиск назад, W - поиск, ограниченный полными словами, U - отождествление больших и малых букв.
642 Приложение Б Кроме того, можно воспользоваться опцией, задаваемой в виде цифры. В случае указания числа и объектом поиска будет п-е вхождение заданной фразы. Директива ‘QA Директива 'QA является развитием директивы "QF. Непосредственно после ее ввода на экране также появляется вопрос о фразе, которую надлежит искать, но после нажатия клавиши Enter, завершающей ее, появляется вопрос Replace with: Ответом на него должна быть фраза, которая заменит фразу, указанную после Find: 'После ввода этой второй фразы, также завершаемого нажатием клавиши Enter, появляется вопрос об опциях, определяющих режим поиска фразы. Кроме уже упомянутых допустимы также следующие опции: G - замена фразы во всем тексте (т.е. не с той позиции, которую указывает курсор, а с первого символа текста), N - безусловная замена. • В случае выбора опции N директива исполняется вплоть до проведения всех замен. В противном случае всякий раз, когда находится очередное вхождение данной фразы, на экран выводится вопрос о том, следует ли проводить замену. Согласие на замену подтверждается ответом Y. При любом другом ответе данная замена не производится и начинается поиск следующего вхождения заменяемой фразы. Директива ‘КВ Выполнение директивы 'КВ приводит к тому, что некоторое место в тексте обозначается как начало блока. Для определения этого блока необходимо дополнительно воспользоваться директивой КК. Директива “КК Выполнение директивы “КК приводит к тому, что некоторое место в тексте обозначается как конец блока. Таким образом, эта директива вместе с директивой “КВ определяет блок. Как только блок определен, на экране выделяется последовательность символов текста, находящаяся между позициями курсора в моменты выполнения директив “КВ и “КК. Описываемые далее директивы группы “Кх относятся именно к этой части текста. Директива ‘КС Выполнение директивы ‘КС приводит к тому, что блок, заданный директивами 'КВ и ‘КК, копируется на позиции, находящиеся перед символом, выделяемым курсором. После этого курсор устанавливается на первый символ скопированного текста.
Редактор 643 Директива "KV Выполнение директивы "KV вызывает перемещение блока, заданного директивами ‘КВ и "КК, на позиции, находящиеся перед символом, выделяемым курсором. После этой операции курсор будет установлен на первый символ перемещенного блока, который на своем новом месте остается выделенным блоком. Директива 'KY Выполнение директивы *KY приводит к удалению выделенного блока. Директива 'КН Выполнение директивы КН аннулирует признаки выделения блока. Директива "KR Выполнение директивы "KR приводит к тому, что в место, выделяемое курсором, вставляется текст из файла на диске. Сразу же после ввода этой директивы на экране монитора появляется вопрос об имени этого файла Read Block From File: Директива "KW Выполнение директивы "KW приводит к тому, что блок, заданный директивами *КВ и "КК, выводится в файл на диске. Сразу же после ввода этой директивы на экране монитора появляется вопрос Write Block То File: об имени того файла, в котором будет помещен выводимый блок. Если такой файл существует, то перед размещением в нем блока он будет удален, а затем создан вновь. Вспомогательные директивы Директива "N Выполнение директивы "N приводит к тому, что в выделяемое курсором место вставляется пара символов cr-lf, т.е. вставляется новая строка. Директива 'I Выполнение директивы "I (или Tab) вызывает перемещение курсора в ближайшую позицию в режиме табуляции. Этой позицией является столбец с номером 1 + 8*п (1, 9, 17, 25.;.). 41*
644 Приложение Б Директива 'L Выполнение директивы *L вызывает повторение последней из директив QF и "QA. Директива 'U Выполнение директивы *U приостанавливает исполнение любой другой директивы (в частности "QF и *QA). Директива "Р Выполнение директивы 'Р приводит к тому, что непосредственно следующий за ней символ вносится в текст, даже если он составляет элемент директивы. В частности, при исполнении директивы "QF ввод последователнйости символов 'P'M'P'J позволяет идентифицировать пару символов конца строки. Пример Вод с клавиатуры последовательности символов QRsssss • *QA*P‘M'P'Je“P‘M'J sssssN Ge (e означает символ Enter, s означает пробел) приводит к перемещению всего редактируемого текста на пять столбцов вправо. Окончание редактирования Чтобы завершить редактирование достаточно вызвать основное меню и выбрать в нем поле File (клавиша Alt-F), а затем выбрать поле S (Save) или W (Write). В первом случае произойдет сохранение файла на диске (на- ряду с созданием файла с расширением .ВАК), а во втором случае произой- дет запись файла с выбранным названием.
ПРИЛОЖЕНИЕ В. СИНТАКСИС Определения синтаксических объектов мы будем давать с помощью простой нотации, принцип которой лучше всего рассмотреть на примере одна из букв: а b с пара-букв: буква буква последовательность-букв: буква последовательность-букв буква список-буквенных-последовательностей: последовательность-букв список буквенных-последовательностей, последовательность-букв Мета-имя буква означает одну из букв: а, Ъ или с. Мета-имя пара-букв означает одно из следующих сочетаний букв: аа, ab, ас, ba, bb, be, са, cb, сс. Мета-имя последовательность-букв обозначает произвольные последова- тельности букв, например, ааа, ccabccabbb. Мета-имя список-буквенных-по- следовательностей обозначает произвольные списки, элементами которых являются последовательности букв, например список a, bb, accbb с, ааа, сссса Замечание: В тех редких случаях, когда элемент определения мета-имени не умещается в одной строке, он продолжается в следующей строке с отступом на два символа. В соответствии с этим соглашением определение мета-имени список-буквенных-последовательностей можно было бы записать в виде список-буквенных-последовательностей: список-букв список-буквенных-последовательностей , последовательность-букв Лексические единицы Лексическая-единица: ключевое-слово идентификатор литерал оператор ограничитель
646 Приложение В Ключевые слова Одно из ключевых слов: auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while Идентификаторы Идентификатор: буква идентификатор буква идентификатор цифра Одна из букв a bed e f g h i j k 1 m n о p q r s t u V w X У z A В C D> E F G H I J к L M N О P Q R s T U V w X Y z Одна из цифр 0 1 2 3 4 5 6 7 8 9 Литералы Литералы: литерал-символьный литерал-строковый литерал-с-фиксированной-точкой литерал-перечислимый литерал-с-плавающей-точкой Литерал-символьный: символьная-последовательность' Символьная- последовательность: с-символ символьная-последовательность с-символ с-символ: произвольный символ, отличный от апострофа, обратной скобки и символа новой строки описание символа Одно из описаний-символа V V \? \\ \o \oo \ooo \xh \xhh \xhM \C \t> V \n \r \t \v
Синтаксис Замечание: о - восьмеричная цифра, h - шестнадцатеричная цифра Литерал-строковый: “ литерал-ст роковый*1 Последовательность-строковая: s-символ последовательность-строковая s-символ s-символ: произвольный символ, отличный от кавычек, обратной скобки описание-символа Литерал-с-фиксированной-точкой: литерал-с-фиксированной-точкой-без-суффикса литерал-с-фиксированной-точкой-суффикс-U-L Литерал-с-фиксированной-точкой-без-суффикса: литерал-десятичный литерал-восьмеричный литерал-шестнадцатеричный Литерал-десятичный: цифра-ненулевая литерал-десятичный цифра Одна из цифр-ненулевых: 12345678 9 Литерал-восьмеричный: О литерал-восьмеричный цифра-восьмеричная Одна из цифр-восьмеричных: 0 1 2 3 4 5 6 7 Литерал-шестнадцатеричный: ОХ цифра-шестнадцатеричная ОХ цифра-шестнадцатеричная литерал-шестнадцатеричный цифра-шестнадцатеричная Одна из цифр-шестнадцатеричных: 0123456789 а b с d е f А В С D Е F Один из суффиксов -U-L: U L u i UL ui U1 uL LU lu IU Lu Литерал-перечислимый: идентификатор Литерал-с-плавающей-точкой: литерал-с-плавающей-точкой-без-суффикса
648 Приложение В литерал-с-плавающей-точкой-без-суффикса суффикс-F-L Литерал-с-плавающей-точкой-без-суффикса: часть-дробная часть-дробная показатель-степени часть-целая показатель-степени Часть-дробная: последовательность-цифр. последовательность-цифр последовательность-цифр . последовательность-цифр Часть-целая: последовательность-цифр Последовательность-цифр: цифра последоватеЛЬность-цифр цифра Один из суффиксов-F-L F L f 1 Показатель-степени: е последовательность-цифр Е последовательность-цифр е знак последовательность-цифр Е знак последовательность-цифр Один из знаков: Операторы Один из операторов: [ ] < ! / ! && «= »= &= Выражения Примитив: идентификатор литерал (выражение) Последователь: примитив последователь [выражение] последователь (список-аргументов) последователь ( ) последователь . идентификатор последователь -> идентификатор последователь ++ последователь —
Синт. Список-аргументов: присвоение список-аргументов, присвоение Предшественник: последователь ++ предшественник — предшественник оператор-одноаргументный обращение sizeof предшественник sizeof (имя-типа) Один из операторов-одноаргументных: &* + --! Обращение: предшественник (имя-типа) обращение Сомножитель: обращение сомножитель * обращение сомножитель / обращение сомножитель % обращение Слагаемое сомножитель слагаемое + сомножитель слагаемое - сомножитель Перемещение: слагаемое перемещение « слагаемое перемещение » слагаемое Отношение: перемещение отношение < перемещение отношение > перемещение отношение <= перемещение отношение >= перемещение Сравнение: отношение сравнение — отношение сравнение != отношение Произведение-битовое сравнение произведение-битовое & сравнение Разность-битовая: произведение-битовое разность-битовая ‘ произведение-битовое Сумма-битовая: разность-битовая сумма-битовая ! разность-битовая
650 Приложение В Конъюнкция: сумма-битовая конъюнкция && сумма-битовая Дизъюнкция: конъюнкция дизъюнкция " конъюнкция Условие: дизъюнкция дизъюнкция ? выражение : условие Присвоение: условие предшественник оператор-присвоения присвоение Один из операторов-присвоения: = ♦= /= += -= «= »= & « := Выражение: присвоение выражение, присвоение Выражение-константа: условие Объявления Объявление: перечень-спецификаций; перечень-спецификаций список-деклараторов-с-инициированием Перечень-спецификаций: спецификация-класса спецификация-типа спецификация-класса перечень-спецификаций спецификация-типа перечень-спецификаций Спецификация-класса: typedef extern static auto register Спецификация-типа: char short int long signed unsigned float double const volatile void спецификация-структуры-или-группы
Синтаксис спецификация-перечисления ммя-определяемого-типа Список-деклараторов: декларатор декларатор - инициатор список-деклараторов, декларатор список-деклараторов, декларатор = инициатор Спецификация-структуры-или-группы: struct {перечень-объявлений-полей] struct обозначение {перечень-объявлений-полей} union {перечень-объявлений-полей] union обозначение {перечень-объявлений-полей] struct обозначение union обозначение Перечень-объявлений-полей: объявление-поля перечень-объявлений-полей объявление-поля Обьявление-поля: перечень-спецификаций-типа список-деклараторов-пс Список-деклараторов-полей: декларатор-поля список-деклараторов-полей , декларатор-поля Декларатор-поля: декларатор : ширина-поля декларатор : ширина-поля Ширина-поля: выражение-константа Обозначение: идентификатор Спецификация-перечисления: епшп {список-перечислений} епшп обозначение {список-перечислений} enum обозначение Список-перечислений: перечисление список-перечисление , перечисление Перечисление: литерал-перечислитель литерал-перечислитель = выражение-константа Декларатор: субдекларатор указатель Субдекларатор: идентификатор (декларатор) субдекларатор []
652 Приложение В субдекларатор [выражение-константа] субдекларатор ( ) субдекларатор (расширенный-список-типов-параметров) субдекларатор (список-имен-прараметров> Указатель ♦ * указатель * перечень-спецификаций-типа * перечень-спецификаций-типа указатель Перечень-спецификаций-типа: спецификация-типа перечень-спецификаций-типа спецификация-типа Расширенный-список-типов-параметров: void список-типов-параметров список-типов-параметров,... Список-типов-параметров: void объявление-параметра список-типов-параметров , объявление-параметр^ Объявление-параметра: перечень-спвцификаций-параметра декларатор имя-типа Список-имеи-параметров: имя-параметра список-имен-параметров , имя-параметра Имя-параметра: идентификатор Имя-типа: перечень-спецификаций-типа перечень-спецификаций-типа псевдодекларатор Псевдодекларатор: указатель псевдосубдекларатор указатель псевдосубдекларатор Псевдосубдекларатор: (псевдодекларатор) [ ] [выражение-константа] псевдосубдекларатор [выражение-константа] < ) (расширенны й'-список-типов-параметров) псевдосубдекларатор (расширенный-список-типов-параметро! Имя-определяемого-типа: идентификатор Список-инициаторов: инициатор список-инициаторов , инициатор
Синтаксис Инициатор: присвоение {список-инициаторов} {список-мнициаторов ,} Операции Операция: операция-с-меткой операция-с-выражением операция-группирующая операция-пу стая операция-с-выбором операция-итеративная операция-управляющая Операция-с-меткой: метка : операция case выражение-константа : операция default : операция Операция-с-выражением: выражение ; Операция-группирующая: {} {перечень-объявлений} (перечень-операций) {перечень-объявлений перечень-операций} Перечень-объявлений: объявление перечень-объявлений объявление Операция-пустая: Операция-с-выбором: if (выражение) операция if (выражение) операция else операция switch (выражение) операция Операция-с-циклом: while (выражение) операция do операция while (выражение); for (выражение^ выражение^ выражениео) операция Замечание: выражениео означает выражение, которое может быть опущено. Операция-управляющая: goto идентификатор; continue; break; return; return выражение;
654 Приложение В Внешние определения Модуль-исходный: объявление-внешнее модуль-исходный объявление-внешнее Объявление-внешнее: определение-функции объявление Определение-функции: заголовок-функции тело-функции Заголовок-функции: декларатор перечень-спецификаций декларатор декларатор перечень-объявлений перечень-спецификаций декларатор перечень-объявление Тело-функции: операция-группирующая Препроцессор Замечание: Символом @ (at> обозначено место завершения строки. Файл-исходный: группа файл-исходный группа Группа: подгруппа группа подгруппа Подгруппа: @ последовательность-лексических-единиц @ директива-условная директива-безусловная Последовательность-лексических-единиц: лексическая-единица последовательность-лексических-единиц лексическая-единица Директнва-условная: фраза-if фраза-endif фраза-if перечень-фраз-elif фраза-endif фраза-if фраза-else фраза-endif фраза-lf перечень фраз-eilf фраза-else фраза-endif Фраза-If: # if выражение-константа @ группа # if выражение-константа @ # if def идентификатор @ группа ttifdef выражение-константа @ # ifndef идентификатор @ группа #ifndef выражение-константа @
Синтаксис Перечень-фраз-eiif: фраза-eiif перечень-фраз-eiif фраза-elif Фраза-elif: 1 #elif выражение-константа @ группа #elif выражение-константа @ Фраза-else: #else @ группа #else @ Фраза-endif : tfendif @ Директива-безусловная: ttinclude <последовательность-!> @ tfinciude “последовательность-! “ @ # include идентификатор @ #define макропоследовательность-лексических-единиц # define макро @ #define макро(параметры) последовательность-лексических-единиц @ #define макро( )последовательность-лексических-единиц #define макро (параметры) @ #define макро( )@ #undef макро @ #iine последовательность-цифр литерал-строковый @ #line последовательность-цифр @ #pragma последовательность-! @ #@ Последовательность-! : символ-! последовательность-! символ-i Символ-! любой символ, отличный от @ Макро: идентификатор БИБЛИОТЕКИ ФУНКЦИЙ И МАКРООПРЕДЕЛЕНИЙ Типы и макроопределения <stddef.h> ptrdiff-t size-1 NULL errno Диагностика <assert.h> NDEBUG void assert(int expession);
656 Приложение В Операции над символами <ctype.h> int isalnum (int c); Int isaipha (int c); Int iscntrl (int c); Int isdigit (int c); int isgraph (int c); int islower (int c); int isprint (int c); int ispunct (int c); int isspace (int c); int isupper (int c); int isxdigit (int c); int tolower (int c); int toupper (int c); Математические функции <math.h> EDOM ERANGE HUGE—VAL double acos (double x); double asin (double x); double atan (double x); double atan2 (double y, double x); double cos (double x); double sin (double x); double tan (double x); double cosh (double x); double sinh (double x); double tanh (double x); double exp (double x); double frexp (double value, int *exp); double Idexp(double x, int exp); double log(double x); double loglO(double x); double modf(double x, value, double iptr); double pow (double x, double y); double sqrt(double x); double ceil (double x); double fabs(double x); double floor (double x); double fmod (double x, double y); Возвраты <setjmp.h> jmp-buf int setjmpfjmp—buf env); void longjmpfjmp—buf env, int vai); Сигналы <signal.h> SIGABRT
Синтаксис SIGFPE SIGILL SIGINT SIGSEGV SIGTERM SIG—DEL SIG-ERR - SIG-4GN void (*signai(int sig, void (*func)(int)))(int); int kill(int pid, int sig); Операции над аргументами <stdarg.h> va—list void va-start(va—list ap, portnN); type va-arg(va-iist ap, type)-, void va-end(va—list ap); Ввод-вывод <stdio.h> —IOFBF -IOLBF -IONBF BUFSIZ EOF FILE E-tmpnam SEEK-CUR SEEK-END SEEK-SET SYS—OPEN TMP-MAX stderr stdin stdout int remove (const char ’pathname); nt rename (const char *oid, (const char ’new); FILE *tmpfiie(void); char ’tmpnani (char *s); int fclose(FILE ’stream); int ffiush(FILE ’stream); FILE ’fopen (const char ’pathname, const char ’type); FILE freopen (const char ’pathname, const char ’type file FILE ’stream); void setbuf (FILE ’stream, char *buf); int setvbuf(FILE ’stream, char *buf, Int type, slze-t size); int fprintf(FILE ’stream, const char ’format,...); int fscanf(FILE ’stream, const char ’format,...); int printf(const char ’format,...); Int scanf(const char ’format,.-); int sprintf(char ’s, const char ’format,...); int sscanf(const char ’s, const char ’format,...); int vprintf((FILE ’stream, const char ’format, va-list arg); int vprintf(const char ’format, va—list arg); int vsprintf(char ’s, const char ’format, va-list arg); >__7АЛ
658 Приложение В int fget(FILE ’stream); char ’fgets(char *s, int n, FILE ’stream); int fputc(int c, FILE ’stream); int fputc(int c, FILE ’stream); int fputs (const char *s, FILE *stream); int getc(FILE ’stream); int getchar(void); char ’gets (char ’s); int putc(int c, FILE ’ctream); int putchar(int c); int puts (char ’s); int ungetc(int c, FILE stream); size-t fread (void *ptr, size-t size, size-t nelem, FILE ’stream); size-t fwrite (const void *ptr, size-t size, size-t neiem, FILE ’stream); int fseek(FILE ’stream, long offset, int ptmame); iong ftei1(FILE ^stream); void rewind (FILE ’stream); void ciearerr(FILE stream); int feof(FILE ’stream) ; , int ferror(FILE stream); void perror (const char *s); Служебные /функции <stdlib.h> RAND-MAX idiv—t ediv—t onexit—t doubie atof (const char *nptr); int atoi (const char ’nptr); iong atoi(const char ’nptr); double strtod (const char ’nptr, char ’’endptr); int strtoK(const char ’nptr, char ’endptr, int base); int rand (void); void srand (unsigned int seed); void ’calloc(sizet nelem, size—t size); void free (void *ptr); void ’malloc(size *ptr, size-t size); void abort (void); void exit(int status); char ’getenv(const char ’name); onexit—t onexit (onexiL-t(’func) (void)); int system (const char ’string); void ’bsearch(const void ’key, const void ’base, size-t nel, size-t keysize, int(’compar) (const void const void ’)); void qsort(void ’base, size-t nei, size-t keysize, int(’compar) (const void ’, const void ♦)); int abs(int i); idiv-t idiv (int numer, int denom); idiv—t ediv(iong numer, long denom); Операции над последовательностями символов <string.h> void *memcpy(void *sl, const void ’s2, size-t n);
Синтаксис char ’strcpy(char ’si, const char ’s2); char *stmcpy(char *sl, const char *s2, size-t n); char ’strcat(char *sl, const char *s2); char ♦strccat(char ’si, const char *s2, size—t n); int memcmp(char void ’si, const void ’s2, size-t n>; int strcpm (const char ’si, const char *s2); int strncmp(const char *sl, const char *s2, size-t n); void ’memchr (const void *s, int c, size-t n); char *strchr (const char *s, Int c); size-t strcspnr (const char *sl, const char *s2); char *strpbrk(const char *sl, const char *s2); char ’strrchr (const char *s, int c); size-t strspn(const char *sl, const char *s2); char ♦strstr (const char *sl, const char ’s2); char ♦strtok(char ’si, const char *82); void memset(void ’s, Int c, size-t n>; char *strerror(lnt errnum); size-t strlen (const char *s); Дата и время <time.h> CLK-TCK clock-t time-t struct tm clock—t clock (void); time-t time(time-t ’timer); char ’asctime (const struct tm ’timer); char ’ctime (const time-t ’timer); double dlfftime (time—t, timeOl, time—t timeO); struct tm ’gmtinie(const time-t ’timer); struct tm ’locaitime(const time-t ’timer);
ПРИЛОЖЕНИЕ Г. ГРАФИКА Некоторые объявления, находящиеся в файлах conio.h и graphics.h (при- водятся с разрешения фирмы Borland International); у************************************************************* у /* conio.h - selection * / /* Direct MSDOS console input/output. * / /* Copyright (c) Borland International 1987, 1988 * / /************************************************************* / struci text—info { unsigned char winieft; winlop; winright; winbottoni; attribute; norrnattr; cumnode; screenheight; screewidth; curx; cury; 1; enum text—modes {LASTMODE=-1. BW40=0, C40, BW80, C80, MONO=7}; enum COLORS { BLACK, /* dark colors */ BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE /* light colors */ #define BLINK 128 /* blink bit */ extern int directvideo; / *************************************************************у /* graphics.h - selection * / /* Definitions for Graphics Package. * / /* Copyright (c) Borland International 1987, 1988 */ у************************************************************* у /*Graph result error return codes */
Графика enum graphics—errors { giOk = 0, grNoIpitGraph = -1, grNotDetected = -2, grFileNotFound = -3, grlnvalidDriver = -4, grNoLoadMem = -5, grNoScanMem = -6, grNoFioodMem = -7, grFontNotFound = -8, grNoFontMem = -9, grlnvalidMode = -10, grError = -11, /* generic error ♦/ grlOerror = -12, grlnvalidFont = -13, grinvalidFontNum - -14, grin valid Version = -18 }; /* Graphics drivers */ enum graphics- drivers ( DETECT, /* requests autodetection •/ CGA, MCGA, EGA, EGA64, EGAMONO, IBM8514, HERMONO, ATT400, VGA, PC3270, CURRENT-DRIVER = -1 }; /* Graphics modes for each driver */ enum graphics-mode { CGACO = 0, /* 320x200 palette 0; 1 page */ CGAC1 1, /* 320x200 palette 1; 1 page */ CGAC2 2, /* 320x200 palette 2; 1 page */ CGAC3 3, /* 320x200 palette 3; 1 page */ CGAHI 4, /* 640x200 1 page */ MCGACO = 0, /* 320x200 palette 0; 1 page */ MCGAC1 = 1, /* 320x200 palette 1; 1 page */ MCGAC2 = 2, /* 320x200 palette 2; 1 page */ MCGAC3 = 3, /* 320x200 palette 3; 1 page */ MCGAMED = 4, /* 640x200 1 page */ MCGAHI = 5, /• 640x480 1 page */ EGALO = 0, /* 640x200 16 color 4 pages */ EGAHI 1, /* 640x350 16 color 2 pages */ EGA64LO = 0, /* 640x200 16 color 1 pages */ EGA64HI = 1, /* 640x350 4 color 1 pages */ EGAMONOHI = 0, /* 640x350 HERCMONOHI 64K on card, 1 page */ 256K on card. 4 pages */ = 0, /* 720x348 2 page */ ATT400C0 = 0, /* 320x200 palette 0; 1 page */ ATT400C1 = 1, /* 320x200 palette 1; 1 page */ ATT4O0C2 = 2, /* 320x200 palette 2; 1 page */ ATT400C3 = 3, /* 320x200 palette 3; 1 page */ ATT400MED = 4, /* 640x200 1 page */ ATT400HI = 5, /* 640x400 1 page */ VGALO = 0, /* 640x200 16 color 4 pages */ VGAMED = 1, /* 640x350 16 color 2 pages */ VGAHI 2, /* 640x480 16 color 1 pages */ PC3270HI = 0, /* 720x350 1 pages */ IBM8514LO = 0, /* 640x480 256 colors */ IBM8514HI = 1, /* 1024x768 256 colors */
662 Приложение Г /* Colors for setpalette and setallpalette */ enum COLORS { BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA. YELLOW, WHITE } enum CGA-COLORS { CGA-LIGHTGREEN = 1, /* Palette CO Color Names ♦/ CGA—LIGHTRED = 2, CGA-YELLOW = 3, CG^—LIGHTCYAN = I, /* Palette Cl Color Names ♦/ CGA—LIGHTMAGENTA = 2, CGA—WHITE = 3, CGA-GREEN = 1. /* Palette C2 Color Names */ CGA-RED = 2, CGA—BROWN = 3, CGA—CYAN = 1, /• Palette C3 Color Names ♦/ CGA—MAGENTA = 2. CGA-LIGHTGRAY V = 3 J> enum EGA-COLORS { EGA-BLACK = o. EGA-BLUE = 1. EGA-GREEN = 2, EGA-CYAN = 3, EGA—RED = 4, EGA—MAGENTA = 5, EGA-BROWN = 20, EGA—LIGHTGRAY = 7, EGA-DARKGRAY = 56, EGA—LIGHTBLUE = 57, EGA—LIGHTGREEN = 58, EGA—LIGHTCYAN, = 59, EGA—LIGHTRED = 60, EGA—LIGHTMAGENTA = 61, EGA-YELLOW = 62, EGA-WHITE }; = 63, /* Line styles for getlinestyle / setlinestyle */ enum line-styles { SOLID-LINE = 0, DOTTED-LINE = 1, CENTER-LINE = 2,
Графика DASHED-LINE = 3, USERBIT—LINE = 4, /* User defined line style */ /* Line widths for getlinestyle / setlinestyle */ enum-llne—widths{ NORM-WIDTH THICK-WIDTH }; = 1, = 3, enum font-names { DEFAULT—FONT TRIPLEX-FONT SMALL-FONT SANS-SERIF-FONT GOTHIC-FONT ); #define HORIZ—DIR = 0, /* 8x8 bit mapped font */ = 1, /* “Stroked11 fonts */ = 2, = 3, = 4 0 /* left to right */ #define VERT—DIR #define USER—CHAR—SIZE 0 /* bottom to top */ 0 /* user-defined char size */ /♦ Fill patterns for getfillstyle / setfillstyle ♦/ enum fill—patterns { EMPTY-FILL, /* in background color ♦/ SOLID-FILL, /* solid fill color ♦/ LINE-FILL, /* НН ♦/ LTSLASH—FILL /♦ /// fill ♦/ SLASH-FILL, /* /// fill with thick lines ♦/ BKSLASH—FILL, /• \\\ AH with thick lines ♦/ LTBKSLASH—FILL, /* \\\ fil* */ 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 ♦/ /* BitBlt operators for putimage ♦/ enum putimage-ops { COPY-PUT, /* MOV •/ XOR-PUT, /* XOR */ OR-PUT, /* OR */ AND-PUT, /* AND */ NOT-PUT, /* NOT */ /* Horizontal and vertical justification for settex (justify ♦/ enum text—just { LEFT-TEXT CENTER-TEXT RIGHT-TEXT = 0, = 2, = 1, BOTTOM-TEXT /* CENTER-TEXT TOP-TEXT }; = o, = 1, = 2, already defined above ♦/ #define MAXCOLORS 15
664 Приложение Г struct palettetype { unsigned char size; singed char colors [MAXCOLORS + 1]; }; struct linesettingstype { Int linestyle; unsigned upattem; Int thickness; I; struct textsettingstype { Int fond; Int direction; Int charsize; int horiz; int vert; }. » struct fillsettingstype { Int pattern; int color; h struct pointtype { int x» y; }; struct viewporttype { Int lift, top, right, bottom; Int clip; }; struct arccoordstype { Int x, y; int xstart, ystart, xend, yend; };
ПРИЛОЖЕНИЕ Д. АССЕМБЛЕР Вызов Турбо Ассемблера в общем случае имеет вид TASM filset, fileset, ...; fileset где каждый fileset имеет структуру options, sourses, object, list, xref В этой второй строке options означает возможные опции, sources - имя исходного файла или последовательность таких имен, разделенных символами + (плюс) или пробелами, object - имя результирующего объектного файла (.OBJ), list - имя файла листинга, в котором будет помещен отчет о компи- ляции, a xref - имя файла, в котором будет помещен отчет о перекрестных ссылках. В случае если ассемблер вызывается без аргументов, на экран будут вы- ведены возможные опции, в число которых входят: /А - Указание упорядочить сегменты в алфавитном порядке. Пример: /А /В - Опция игнорируется. Пример: /В /С • Внесение в отчет о компиляции таблицы перекрестных ссылок. Пример: /С /D - Определение имени. Запись /Dsymbol влечет за собой определение имени symbol без присвоения ему значения, а запись /Dsymbol-value влечет определение имени и присвоение ему значения value. Пример: /DSlze =20 /Ж - Генерирование команд, эмулирующих операции с плавающей точкой. Пример: /Е /Н - Вывод на экран перечня опций. Пример: /Н /I - Определение имени каталога включаемых файлов. При вводе /laifr производится поиск файлов, включаемых в каталог dir. Эта опция применима только к именам файлов, не составляю- щим маршрут. Опции /I мшут входить многократно. Пример: /(Headers /J - Указание директивы, которая будет выполняться до начала компиляции исходного файла. Для выполнения директивы action. следует ввести /Section. Пример: /JIdeal /КН - Создание условий для компиляции модуля, содержащего указанное число имен. Вводя /KHcount, мы указываем count имен. По умолчанию принимается count=&l9i. Пример: /КН10000 /KS - Создание условий для компиляции модуля, требующего указанной области для строк. Вводя /KSsize, мы указываем область размером size Кбайт. По умолчанию принимается 150 Кбайт. Пример: /KS200. /L - Генерация отчета о компиляции даже в том случае, если в < директиве компилятора отсутствует list. Пример: /L /LA - Вывод кода, генерируемого сегментными директивами высокого уровня, например .MODEL и .CODE. Пример: /LA /ML - Указание считать малые буквы отличными от больших. Пример: /ML /MU - Преобразование всех малых букв в большие. Пример: /MU /МХ - Указание считать малые буквы отличными от больших, но только в пределах внешних имен. Пример: /МХ
666 Приложение Д /N - Указание об исключении из отчета о компиляции сведений о таблице символов. Пример: /N /Р - Проверка факта использования в исходном модуле команд, модифицирующих сегмент кода (посредством CS). (protected mode). Пример:/Р /R - Генерация действительных (а не эмулируемых эмулятором) команд сопроцессора. Пример: /R /S - Упорядочение сегментов в порядке их появления в исходном коде. Пример: /S /Т - Отмена сообщения о правильной компиляции. Пример: /Т /V - Опция игнорируется. Пример: /V. /W - Генерация предупредительных сообщений. При вводе /W дается команда на генерацию таких сообщений, при вводе /W- команда на прерывание их генерации. Пример: /W- /X - Указание о включении в отчет о компиляции не участвовавших в компиляции ветвей условных директив. Пример: /X /Z - Указание о включении в сообщения об ошибках строк исходного текста, находящихся вблизи мест обнаружения ошибок. Пример: /Z /ZD - Указание о внесении в результирующий файл сведений о номерах строк исходного файла. Пример: /ZD /ZI - Указание о внесении в результирующий файл полных сведений о номерах строк и именах исходного модуля. Пример: /ZI В любом месте директивы компиляции может присутствовать фрагмент вида @Name9 где Name - имя файла. В этом случае находящийся в данном файле текст помещается на место фрагмента ©Name. Если текст содержит более одной строки, то символы конца строки интерпретируются как символы пробелов. Пример Если файл OPTIONS.ASM содержит текст /zi /тх то директива tasm/1 @options.asm main интерпретируется как директива tasm/1 /zi /тх main Еще одним удобством является возможность использования конфигурационного файла TASM.CFG. Заключенный в этом файле текст неявно включается в любую директиву TASM, как если бы она начиналась с ©TASM.CFG.
ПРИЛОЖЕНИЕ Е СТАРТЕР (ПРОГРАММА ЗАПУСКА) В общем случае вызов стартера имеет вид TD opt пате args где opt - опции, пате - имя запускаемой программы, a args - ее аргумен- ты. Если фрагмент args пуст, то программа активизируется без аргументов, а если пуст фрагмент opt, то применяются опции, встроенные в стартер. Каждая из опций opt начинается со знака - (.минус) и отделяется от следу- ющей опции или имени программы по крайней мере одним пробелом. Если последним символом опции также является минус /например, -vg- /, то опция исключается. Это необходимо лишь в том случае, если некоторая оп- ция включена как постоянная, а стартер нужно вызвать без этой опции. Опции -d -do Программа использует основной экран, а' стартер использует вспомогательный экран. -dp Стартер использует дополнителные страницы цветной карты (принятие этой опции имеет смысл лишь в том случае, если программа не использует дополнительных страниц). -ds Стартер запоминает в выделенной области памяти как свой экран, так и исходный экран запускаемой программы. Опции -h и -? -h Стартер выводит на экран пояснения, как правильно вызывать стартер. -? Аналогично предыдущей опции. Опция -i -i Стартер заимствует идентификаторы процессов из того же пула1', что и запускаемая программа (применение этой опции имеет смысл лишь в том случае, если Вы хотите проследить исполнение программы внутри системы DOS). Опция -1 -I Запуск программы начинается в режиме ассемблера. Опция -ш -ш п Стартеру выделяется “куча“ размером п Кбайт (по умолчанию п = 40). ' Пул, накопительный блок (англ, pooling block) - область памяти, используемая для заполнения множества коротких записей, подлежащих передаче на устройство или из устройства, для которого время доступа велико по сравнению с действительным (реальным) временем передачи данных. - Прим. ред.
668 Приложение Е Опция -г -г Запуск производится в условиях удаленного доступа через последовательный интерфейс (по умолчанию принимается СОМ1 и 9600 бод). -гр п -rs п Стартер пользуется портом СО Мп (и » 1,2). Стартер выбирает скорость передачи данных для п « 1 9600 бод, для и - 2 40 Кбод, для п = 3 115 Кбод. Опции -S -SC Стартер не отличает в идентификаторах малых букв от больших (применение этой опции по отношению к программам на Турбо Паскале является излишним). -sdd Принимается, что d - это имя каталога, в котором стартер может вести поиск исходных модулей (опция -sd может использоваться многократно; каталоги просматриваются в порядке их перечисления; если такие каталоги присутствуют также в конфигурационном файле, то они просматриваются в первую очередь). Опции -V -V Графический образ, сгенерированный программой, запоминается в оперативной памяти (для этого требуется дополнительно 8 Кбайт, но это необходимо, если работа стартера неблагоприятно сказывается на виде экрана). -vn Исключается возможность увеличения числа строк экрана (43 - EGA, 50 - VGA). -vp Запоминание палитры адаптера EGA.
ПРИЛОЖЕНИЕ Ж. РЕДАКТОР СВЯЗЕЙ Вызов редактора связей в общем случае имеет вид TLINK obj, ехе, тар, lib где obj представляет имя объективного файла или последовательность имен таких файлов, разделенных знаками + (плюс) или пробелами; ехе - имя файла, в котором будет помещена скомпонированная исполняемая програм- ма; тар - имя файла, в котором будет помещена карта сборки, a lib - имя библиотеки или последовательность таких имен, разделенных знаками + (плюс) или пробелами. Если имя того или иного файла указывается без расширения, то по умолчанию для obj принимается .ЕХЕ, для тар - .МАР, для lib - .LIB. Если опущено имя того или иного файла, то для ехе и тар по умолчанию принимается такое же имя, как в файле obj. Если редактор связей вызывается без аргументов, то на экран выводится перечень его оп- ций, к числу которых относятся: /с - Указание различать большие и малые буквы во внешних именах. Пример: /с /d - Указание сообщать о присутствии некоторого имени более чем в одной библиотеке. Пример: /d /е - Указание игнорировать расширенный словарь библиотек. Пример: /е /i - Указание о размещении в результирующем файле заключитель ных сегментов, даже если они не содержат записей данных. Пример: / i /1 - Указание о внесении в карту сборки сведений о номерах строк. Пример: /1 /ш - Указание о дополнении карты сборки списком обших имен. Пример: /ш /п - Указание об игнорировании библиотек, заданных по умолчанию. Пример: / п /s - Указание о дополнении карты сборки перечнем общих имен и картой сегментов. Пример: /s /I - Указание создать из модуля, скомпилированного в модели TINY, исполняемой программы типа .СОМ, а не .ЕХЕ. Пример: /t /v - Указание включить в файл, содержащий исполняемую программу, полных сведений о запуске. Пример: /v /х - Указание не генерировать карту сборки. Пример: / х В любом месте директивы компиляции может присутствовать фрагмент вида ©Name, где Name - имя файла. В этом случае находящийся в этом файле текст вставляется на место фрагмента ©Name. Если текст содержит более одной строки, то символы конца строки ин- терпретируются как символы пробелов.
670 Приложение Ж Пример Если файл OPTIONS.ASM содержит текст /V /с то директива tlink /1 ©options.asm main интерпретируется как директива tasm /1 /v /с main
ПРИЛОЖЕНИЕ 3. ОРГАНИЗАТОР (МЕНЕДЖЕР) Конструировать многомодульные программы легче в интегрированной сис- теме, чем в пакетном режиме, поскольку как запуск, так и компоновка ре- зультирующих моделей проводятся автоматически и в нужной очередности. В тех случаях, когда желательно пользоваться пакетным режимом, можно упростить организацию цикла запуска, состоящего из редактирования, компи- ляции и компоновки, обратившись к услугам программы, которую мы назо- вем организатором. Организатор вызывается (в простейшем случае) дирек- тивой МАКЕ или MAKE -fName где Name - имя файла, содержащего указания по организации и макроопределения. Если директива МАКЕ используется в своей первой форме, то по умолчанию принимается опция -fMAKEFILE а если файл MAKEFILE не существует, то опция -fMAKEFILE.MAK Независимо от используемой директивы в файле Name помещаются указания по организации в виде outflle, outflle, ..., outflle: Inflle infile ... tnfile action action action где outflle - имена файлов, которые должны быть созданы автоматически с помощью организатора, каждый inflle - имя файла, от которого зависит outflle, a action - директивы DOS. Организационная директива интерпретируется следующим образом: • если некоторое имя inflle присутствует в другой, явной или неявной (см. ниже) организационной директиве, то интерпретация данной директивы пре- рывается с тем, чтобы интерпретировать сначала эту другую директиву; • после возможной интерпретации таких дополнительных директив проверя- ется существование файлов outflle', • если хотя бы один файл outflle не существует, или если дата и время со- здания некоторого файла inflle оказываются более поздними, чем дата созда- ния файла outflle, то выполняются все директивы action и на этом исполне- ние данное организационной директивы завершается;
672 Приложение 3 Замечание. Поиск директив action осуществляется в текущем каталоге, а затем в каталогах, определяемых маршрутом окружения PATH. Пример. Организационная директива greet.exe: greet tasm greet.asm tlink greet.obj Если файл greet.exe “старше" файла greet, то будет выполнена компиляция файла greet.asm директивой tasm greet.asm а затем его сборка директивой tlink greet.obj Комментарии Если в одной из строк директивы организатора присутствует символ # (хеш), то остальные символы этой строки игнорируются. Полностью игнори- руются также строки, начинающиеся с такого символа. Строки Директивы, не умещающиеся на одной строке, могут быть продолжены. Последним символом продолжаемой строки должен быть символ \ (обратная косая). Однако продолжать таким образом комментарии не разрешается. Директивы Отдельные организационные директивы разделяются пустыми строками. Требуется, чтобы слово outfile начиналось с первой позиции строки и чтобы каждому из слов action предшествовал хотя бы один символ пробела или та- буляции. Пример. Директивы организатора main.exe : main.asm test.asm tasm /1 main.asm tasm /1 test.asm tlink main test,prog main.obj : main.asm tasm /t main.asm test.obj : test.asm tasm /t /ml testasm
Организатор (Менеджер) 673 Директивы по умолчанию Неявным расширением организационных директив являются директивы по умолчанию, следующие из правил вывода. В общем случае правило вывода имеет вид .fromExt .toExt action action action где .fromExt и .toExt являются расширениями имен файлов. По определению принимается, что каждое правило вывода такого вида генерирует семейство неявных организационных директив. name.toExt : name.fromExt action action action Правила, выраженные директивами по умолчанию, применяются к тем организационным директивам, которые не содержат директив системы DOS. Пример. Правила вывода Если интерпретируемый файл содержит правило вывода .pas.exe: warn или директивы main.exe : main.pas и test.exe : test.pas то эти директивы трактуются как директивы main.exe : main.pas warn test.exe : test.pas warn Макроопределения и макровызовы Использование организационных директив значительно облегчается бла- годаря возможности применения макроопределений и макровызовов. Макроопределение записывается очень просто и сводится к указанию
674 Приложение 3 Name = Text где Name - имя макроопределения, a Text - произвольный фрагмент текста, не содержащий пробелов. В результате интерпретации макроопределения текст Text связывается с именем Name. В области действия макроопределе- ния любая запись $ Warne) заменяется текстом Text. Имя Name считается определенным вплоть до отмены директивой lundef Name Предварительно объявленные макроопределения Несколько макроопределений объявлены заранее и имеют несколько от- личный синтаксис. Их вызов влечет за собой вывод на экран следующей ин- формации: $d (Name) - цифра 1, если макроопределение Name определено, и цифра 0 в противном случае; $* * - маршрут и основная часть имени файла outflle-, Пример: С:\ТР\ GREET.PAS => C:\TP\GREET $< - полное имя файла outflle (если $< присутствует в организационной директиве) или полное имя файла с расширением .fromExt (если $< присутствует в правиле вывода); Пример: С:\ТР\ GREET.PAS => C:\TP\GREET.PAS $: - маршрут, заключенный в имени outflle’, Пример: С:\ТР\ GREET.PAS => С:\ТР\ $. - полное имя файла outflle, но без маршрута; Пример: С:\ТР\ GREET.PAS => GREET.PAS $& - основная часть имени файла outflle-, Пример: С:\ТР\ GREET.PAS => GREET Пример. Макроопределения и макровызовы Если интерпретируемый файл содержит директивы .asm.obj. sm/t main.obj : main.asm то его интерпретация равнозначна интерпретации директивы main.obj : main.asm tasm/t main.asm
Организатор (Менеджер) 675 Включение файлов, условная компиляция Организатор дает возможность использования директив включения файлов и директив условной компиляции. В общем случае директива включения файла имеет вид {include “Name“ где Name - имя включаемого файла, а директива условной компиляции-вцд !if Ехр Text lelif Ехр) Text! lelif Ехр2 Text2 lelif Expn Textn {else TextO ! endif где Exp - некоторые выражения, a Text - директивы или последовательности директив. Результатом интерпретации директивы включения файла является замена этой директивы в месте ее вхождения текстом, содержащимся в файле Name. Интерпретация директивы условной компиляции влечет за собой замену фразы lelse TextO фразой lelif 1 а затем замену всей директивы в месте ее вхождения тем текстом Text, который следует после первого выражения со значением, отличным от 0. Операторы В выражениях могут использоваться следующие операторы: Оператор Функция изменение знака отрицание значения бита ! запрещение + сложение вычитание * 43* умножение
676 Приложение 3 / деление % остаток « сдвиг влево » сдвиг вправо & логическое произведение битов ! логическая сумма битов сложение по модулю 2 && конъюнкция " дизъюнкция > больше < меньше <= меньше или равно >= больше или равно = равно != не равно ?: условие (если а — 0, то условие а ? b : с имеет значение Ь; в противном случае значение с) Пример. Директивы условной интерпретации alpha = 4 Ilf alpha +11=5 .asm.obj. tasm $< else /asm.obj. tasm/t < lendtf Результат интерпретации этих директив в точности совпадает с результатом интерпретации директив alpha = 4 .asm.obj tasm/t $*.ASM Вызов организатора в общем виде производится следующим образом: MAKE options Name, Name, ..., Name где options представляет опции, а каждое Name - имя файла, к которому будут применяться директивы, находящиеся в файле, подвергающемся интерпретации. Если в директиве МАКЕ присутствуют имена Name, то для каждого из них интерпретируется директива организатора, начинающаяся с этого имени. Если в интерпретируемом файле такая организационная директива отсутствует, то обработка идет так, будто в его начале находилась организационная директива Name:
Организатор (Менеджер) 677 (без Infile и action). Это позволяет на основании правил вывода применять директивы по умолчанию. Замечание. Правила вывода, применяемые чаще других, могут быть поме- щены в файл BUILTINS.MAK. Содержимое этого файла неявно включается в начало файла, подлежащего интерпретации. Перечень имеющихся опций -а -DName указание проверить возвратные ссылки указание определить имя Name -DName=Val указание определить имя Name и присвоить ему значение Vai -(Name -h -IDir -n -s -UName указание интерпретировать имя Name указание сообщить опции указание о поиске файлов, включаемых в каталог Dir указание о выводе директив без их исполнения указание приостановить вывод исполняемых директив указание считать имя Name не определенным. Замечание, файла, можно Если возникает необходимость в обновлении даты создания воспользоваться программной TOUCH. Она вызывается с по- мощью директивы TOUCH filename, filename, ..., filename в которой filename - имена файлов. В результате с каждым из этих файлов связывается текущая дата и время. Выполнение этой операции обычно рас- сматривается как подготовка к выполнению программы-организатора МАКЕ.
ПРИЛОЖЕНИЕ И. ГЕНЕРАТОР Чтобы дать читателю возможность познакомиться с качеством кода, генерируемого компилятором, приведем простую программу на языке Турбо Си и ее аналог на языке Ассемблера. Программа на языке Турбо Си finclude «stdio.h» char Stringl[81], String2[7]; int Number; int atoi(char *), isSpace(char); void itoa(int,char *). digit(fht); main( ) { scanfC’XSOs”, Stringl); Number = atoi(Stringl); 1toa(Number,Str1ngZ); rintf("%s", StringZ); return 0; } int atoi (char *s) { int c,val,sign; while(isSpase(*s))++s; val “ 0, sign = 1; if(*s — ++s, sign “ -1; else if(*s == '+') ++S; while(isDigit(c = *s++)) val = 10 * val + c - 'O’; return sign > 0 ? val : -val; } int isSpacefchar c) return c == ' '; i int 1sDigit(char c) return c * » '0' && c < = '9'; } void itoa(int val, char *str) extern char *extptr; if(val < 0) *str++ = val » -val; exptr = str; digit(val); *extptr = '\0‘;
Генератор char *extptr; void digit(val) int val; { int head; if((head • val / 10) ! ~ 0) digit(head); *extptr++ » val % 10 + ’O'; } Программа на языке Турбо Ассемблер ifndef TTversion ?debug macro endm end if Tdebug S " disass.c" _ТЕХТ segment byte public 'CODE* DGROUP group assume cs: _DATA,_BSS _TEXT,ds:DGROUP,ss:DGROUP _ТЕХТ ends _DATA segment word public 'DATA' de label byte dew label word _DATA ends -BSS segment word public 'BSS' ье label byte bew label word Tdebug C E9704DE312086469736173732E63 . Tdebug CE900101D1117433A5C544332305C494E434C5544+ ?debug C 455C737464696F Tdebug С 2E68 Tdebug CE900101D1118433A5C544332305C494E434C5544+ Tdebug С 4S5C7374646172 Tdebug С 672E68 _BSS ends -TEXT segment byte public 'CODE' 1 Tdebug L'13 main proc near Tdebug L 15 mov ax,offset DGROUPzStringl push ax push ax,offset DGROUP:se push ax call near ptr scanf POP ex pop ex Tdebug L 16 mov ax,offset DGROUP:_Stringl push ax call near ptr _atoi pop ex mov word ptr DGROUP: Number,ax Tdebug L 17 mov ax,offset DGROUP:_String2 push ax push word ptr DGROUP:_Number call near ptr _atoi pop ex
680 Приложение И POP СХ ; Ydebug L 17 mov ax,offset DGROUP:_String2 push ax mov ax,offset DGROUP:_s6t5 push ax push word ptr DGROUP: Numbu call near ptr _it'oa pop ex POP ex ; Ydebuq L 18 mov ax,offset DGROJP:-Strinq2 push ax mov ax,offset DGROUP:_s6+5 pus ax call near ptr_prinrf н P°P ex pop ex ; Tdebug L 19 xor ax,ax Jmp short 61 81: ; Ydebug L 20 ret _main endp ; * Tdebug L 23 atoi proc near push bp mov bp.sp sub sp,4 push si push di mov si.word ptr[bp+4] ; Tdebug L 26 jmp short 63 65: ; Tdebug L 26 inc si 63: ; Tdebug L 26 push word ptr [si] call near ptr isSpace Pop ex or ax, ax jne 65 64: ; Tdebug L 27 xor di,di mov word ptr [bp-2],l ; ?debug L 28 cmp byte ptr [si],45 jne 66 ; Tdebug L 29 inc si mov word ptr [bp-2],-l ; Tdebug L 29 jmp short 67 66: ; Tdebug L 30 cmp byte ptr [si],43
Генератор jne Tdebug Inc 68 L 31 si ев: 67: ВИ: jmp short 69 Tdebug L 33 mov ax,di mov dx.10 mul dx add ax,word ptr [br-4] add ax,-48 69: mov dl.ax Tdebug L 32 mov bx.sl inc si mov cbw al.byte ptr [bx] mov word ptr [bp-4],ax push . ax call near ptr _1sDigit POP ex or ax,ax 610: jne 611 Tdebug L 34 crop word ptr [bp-2],0 jle 613 mov ax,di 613: jmp short 612 mov ax,di 612: neq ax 62: jmp short 62 Tdebug L 35 POP di pop si mov sp.bp POP ret bp _ato1 endp _1sSpace Tdebug L 38 proc near push bp mov bp. sp Tdebug L 40 cmp byte ptr[bp+4],32 jne 616 mov ax.l 616: jmp short 615 615: xor ax,ax 614: jmp short 614 Tdebug L 41
682 Приложение И POP ret bp -isSpace endp _1sD1git Tdebug L 44 proc near push ' bp mov bp.sp Tdebug L 46 cmp byte ptr [bp+4],48 jl 819 cmp byte ptr [bp+4],57 jg 819 mov ax,l jmp short 818 819: xor ax,ax 81В: r> jmp short 817 817: Tdebug L 47 pop bp _isDigit ret endp _1toa Tdebug L 50; proc near • push bp mov bp.sp push si push di mov di.word ptr [bp+6] mov si.word ptr [bp+4] Tdebug L 53 or si,si Jge 821 Tdebug L 54 mov byte ptr [di],45 Inc di mov ax,si neg ax mov si,ax 021: Tdebug L 55 mov word ptr DGROUP: extptr.di : Tdebug L 56 push si call near ptr _digit pop ex : Tdebug L 57 mov bx.word ptr DGROUP:_extptr mov byte ptr [bx],0 820: Tdebug L 58 pop di pop si pop bp itoa ret endp Tdebug L 63 _digit proc near push bp
Генератор mov bp.sp push si f Tdebug L 67 mov ax,word ptr[bp+4] -mov bx,10 cwd idiv bx mov si,ax mov ax,si or ax,ax je 023 1 ?debug L 68 push si call near ptr „digit POP ex 023: ?debug L 69 mov ax,word ptr[bp+4] mov bx,10 cwd idiv bx add dl,48 mov bx.word ptr DGROUP:„extptr mov byte ptr [bx],dl Inc word ptr DGROUP:_extptr 022: ?debug L 70 POP si POP bp ret digit endp _TEXT ends _BSS segment word public 'BSS' _Str1ngl label byte db 81 dup (?) „String? label byte db 7 dup (?) „Number label word db 2 dup (?) „extptr label word db 2 dup (?) _BSS ends ?debug С E9 „DATA segment word.public 'DATA' S0 label byte db 37 db 56 db 48 db 115 db 0 db 37 db 115 db 0 „DATA ends „TEXT segment byte public 'CODE' extrn jrlntf :near extrn „scanf:near „TEXT ends public „IsDIglt public „extptr
public public public public public public public public end _IsSpace -Number -digit _ma1n _ato1 _1toa _Str1ng2 _Str1ngl