Вы находитесь на странице: 1из 95

КАЛИНИНГРАДСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

С.А. Григорьев, С.А. Ишанов

СРАВНИТЕЛЬНЫЙ КУРС
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
FORTRAN И С

Калининград
1998

3
КАЛИНИНГРАДСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

С.А. Григорьев, С.А. Ишанов

СРАВНИТЕЛЬНЫЙ КУРС
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
FORTRAN И С

Учебное пособие

Калининград
1998

4
УДК 681.3. 06

Григорьев С.А., Ишанов С.А. Сравнительный курс языков программи-


рования Fortran и С: Учебное пособие / Калинингр. ун-т; Калининград,
1998. - 94 с. - ISBN 5-88874-097-7.

Учебное пособие написано на основе курса лекций “Языки программи-


рования и методы трансляции” и спецкурса “Язык программирования C”,
читавшихся авторами на математическом факультете КГУ. Пособие со-
держит достаточно полное описание языков С и Fortran и сравнение основ-
ных конструкций этих языков, а также языка Pascal. Отдельная глава по-
священа решению практических задач на языках С, Fortran и Pascal. Учеб-
ное пособие предназначено для студентов-математиков и студентов других
специальностей, углубленно изучающих программирование.

Рецензент: к.ф.-м.н., доцент кафедры прикладной математики Калинин-


градского государственного технического университета В.М. Смертин.

Печатается по решению редакционно-издательского Совета Калинин-


градского государственного университета.

ISBN 5-88874-097-7 © Калининградский государствен-


ный университет, 1998

5
Сергей Анатольевич Григорьев,
Сергей Александрович Ишанов

СРАВНИТЕЛЬНЫЙ КУРС ЯЗЫКОВ ПРОГРАММИРОВАНИЯ


FORTRAN И С

Учебное пособие

Лицензия № 020345 от 14.01.1997 г.


Редактор Л.Г. Ванцева. Оригинал-макет Д.В. Голубина.
Подписано в печать 25.05.1998 г. Формат 60×90 1/16.
Бумага для множительных аппаратов. Ризограф. Усл. печ. л. 5,9.
Уч.-изд. л. 6,5. Тираж 120 экз. Заказ .

Калининградский государственный университет,


236041, г. Калининград, ул. А. Невского, 14

СОДЕРЖАНИЕ

6
Введение ......................................................................................................... 5
Глава 1. Описание языка C ......................................................................... 5
1.1. Структура C-программы .......................................................................... 5
1.2. Скалярные типы данных и константы, строковые константы ............... 6
1.3. Описание скалярных переменных. Директива #define ........................... 7
1.4. Операции. Выражения ............................................................................. 8
1.5. Стандартные математические функции ................................................ 10
1.6. Ввод-вывод ............................................................................................. 10
1.7. Метки, оператор goto, условные конструкции, оператор break,
функция exit ............................................................................................. 14
1.8. Циклические конструкции ..................................................................... 16
1.9. Указатели, адресная арифметика .......................................................... 17
1.10. Массивы ................................................................................................ 18
1.11. Функции ................................................................................................ 19
1.12. Классы памяти. Общие правила описания. Оператор typedef ........... 23
1.13. Строковые переменные, ввод-вывод строк, стандартные функции
обработки строк. Функции проверки символов .................................. 25
1.14. Макроопределения ............................................................................... 28
1.15. Внешние файлы .................................................................................... 30
1.16. Структуры, объединения, битовые поля ............................................. 31
1.17. Динамическое распределение памяти ................................................. 33
1.18. Графика ................................................................................................. 34
1.19. Дополнительные возможности языка: тип enum, использование
командной строки, функции с переменным числом параметров,
средства консольного ввода-вывода, системное время, случайные
числа ...................................................................................................... 35
Глава 2. Описание языка FORTRAN ....................................................... 38
2.1. Структура FORTRAN-программы ......................................................... 38
2.2. Типы данных. Константы ...................................................................... 39
2.3. Описание переменных, правила умолчания, операторы
IMPLICIT, DATA, PARAMETER ......................................................... 40
2.4. Оператор присваивания. Операции ....................................................... 41

7
2.5. Стандартные математические функции ................................................ 42
2.6. Ввод-вывод ............................................................................................. 46
2.7. Оператор СОNTINUE. Операторы перехода. Условные операторы ... 49
2.8. Операторы цикла .................................................................................... 51
2.9. Функции LOC, LOCFAR, LOCNEAR .................................................... 52
2.10. Массивы ................................................................................................ 52
2.11. Подпрограммы SUBROUTINE, FUNCTION и INTERFACE ............. 54
2.12. Общие области, подпрограмма BLOCK DATA.
Оператор EQUIVALENCE .................................................................... 56
2.13. Символьные переменные ..................................................................... 57
2.14. Операторные функции ......................................................................... 59
2.15. Внешние файлы .................................................................................... 59
2.16. Структуры ............................................................................................. 62
2.17. Динамические массивы ........................................................................ 63
2.18. Графика ................................................................................................. 63
2.19. Дополнительные возможности языка: дополнительные точки входа,
свободный формат, строки отладки, средства консольного
ввода-вывода, системное время, случайные числа ............................. 67

Глава 3. Решение задач на языках C и FORTRAN ................................ 70


Задача 1. Вывод натурального числа ........................................................... 70
Задача 2. Сумма ряда .................................................................................... 71
Задача 3. Медленная сортировка .................................................................. 72
Задача 4. Быстрая сортировка ...................................................................... 74
Задача 5. Слова .............................................................................................. 77
Задача 6. График ........................................................................................... 81
Задача 7. Кубическое уравнение .................................................................. 85
Задача 8. Собственное число ........................................................................ 89
Комментарии к задачам ................................................................................ 91

8
ВВЕДЕНИЕ

Эта книга состоит из трех глав. Первые две представляют собой крат-
кие описания языков программирования C и FORTRAN 77, а в третьей гла-
ве приведены решения некоторых часто встречающихся задач на этих двух
языках и на языке PASCAL. Авторы предполагают, что читатели этой кни-
ги уже в достаточной мере знакомы с языком программирования PASCAL,
и в ряде мест ссылаются на соответствующие конструкции этого языка. Но
владение языком PASCAL не является абсолютно необходимым для поль-
зования этой книгой, читатель должен иметь лишь некоторое представле-
ние о языках программирования высокого уровня, поскольку из-за ограни-
ченного объема книги авторы вынуждены излагать материал очень лако-
нично. Тем, кого интересует только один из представленных языков, мож-
но порекомендовать читать только первую или только вторую главу.

Глава 1. ОПИСАНИЕ ЯЗЫКА C

Приведенное ниже описание языка C полностью соответствует среде


программирования Borland C++ версий 2.0 и 3.0. Многие программисты
предпочитают пользоваться языком C++, который является расширением
языка C. Оба языка в основном используют один и тот же синтаксис, опе-
раторы, выражения, встроенные типы данных, структуры, массивы, объе-
динения, циклы, функции и указатели. Основные расширения языка С++
также описаны и специально оговорены в тексте главы.

1.1. Структура C-программы

С-программа размещается в одном или нескольких файлах. Она состо-


ит из оболочки, включающей описания и директивы препроцессора, и од-
ной или нескольких функций. Одна из функций всегда имеет имя main, с
нее начинается выполнение программы. Функция состоит из заголовка
функции:
тип имя ([список параметров])
и тела функции. Тело функции - это блок (последовательность операторов
в фигурных скобках). Символы {} в языке С являются логическими скоб-
ками. Список параметров может быть пустым, но ( ) должны присутство-
вать и в этом случае. Для функций, не имеющих параметров, можно указы-
вать список параметров в виде (void) . Если список параметров не пуст, то
для каждого параметра указывается его тип. Если функция не должна воз-

9
вращать никакого значения, то ее тип - void ("пустой" тип). В языке С нет
понятия процедуры.
Каждый оператор заканчивается символом ; .
Идентификаторы могут иметь длину до 31 символа, компилятор разли-
чает большие и малые латинские буквы, откуда, в частности, следует, что
написание Main или MAIN имени главной функции является ошибкой.
Комментарии заключаются в составные символы /* */ . В языке С++
появился еще один вид комментариев: так называемый однострочный
комментарий - строка, начинающаяся парой символов // .
Директивы препроцессора начинаются с символа # и записываются в
отдельной строке, они могут располагаться в любом месте программы, но
не внутри функции. Директивы действуют от места появления до конца
файла или до их отмены. Препроцессор языка С - это программа, которая,
обрабатывая исходный текст до компилятора, выполняет подстановки для
макровызовов, осуществляет условную компиляцию, подключает необхо-
димые файлы. Директива #include<файл> или #include"файл" присоеди-
няет к программе внешний файл, "" означают поиск файла в текущем ка-
талоге (пользовательский файл), а <> - поиск в системном каталоге (сис-
темный файл). Директиву #include следует использовать в любой про-
грамме, вызывающей внешние функции, для подключения т.н. заголовоч-
ных файлов - файлов с расширением h (header). В заголовочных файлах
содержатся описания (прототипы) функций, необходимые для успешной
компиляции программы.

1.2. Скалярные типы данных и константы,


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

В языке С определены следующие арифметические типы данных:

Тип данных Размер Диапазон значений


char 1 байт от -128 до +127
unsigned char 1 байт от 0 до 255
int 2 байта от -32768 до +32767
unsigned 2 байта от 0 до 65535
long 4 байта от -2147483648 до +2147483647
float 4 байта от ± 3.4Е-38 до ±3.4Е+38 (7 верных цифр)
double 8 байтов от ±1.7Е-308 до ±1.7Е+308 (16 верных цифр)

Все арифметические типы данных имеют аналогии в языке PASCAL:


char=ShortInt, unsigned char=Byte, int=Integer, unsigned=Word,
long=LongInt, float=Single, double=Double. Целые константы можно запи-

10
сывать в десятичном, восьмеричном и шестнадцатеричном виде, например,
255 или 0377, или 0XFF. Константа, начинающаяся цифрой 0, считается
восьмеричной, а константа, начинающаяся символами 0X или 0x, - шест-
надцатеричной. По умолчанию десятичные целые константы имеют тип
int, константы типов unsigned и long образуются добавлением справа бук-
вы U (u) или L (l) : -1L, 65535U .
Вещественные константы записываются в виде с фиксированной точ-
кой: -2. , 1.3 , .45, или с плавающей точкой: -2e-2, 1.33e0 - и по умолчанию
имеют тип double. Константы типа float образуются добавлением справа
буквы F (f) : -2.f, 1.22e0f .
Символьные константы (они являются в языке C целыми числами и
имеют тип int) записываются в одном из четырех видов:
- ' символ ' ( например, '!' )
- ' \8-ричная константа ' ( например, '\041' )
- ' \x одна или две 16-ричные цифры ' ( например, '\x21' )
- специальные символы '\a' - звук, '\b' - backspace, '\t' - горизонтальная
табуляция, '\n' - новая строка, '\v' - вертикальная табуляция, '\f' - новая
страница, '\r' - возврат каретки, '\\' - символ \, '\''- апостроф, '\”' - кавычка,
'\%' - символ %.
Строковые константы задаются в виде “символы”, в т.ч. могут содер-
жать и специальные символы, например : "abcdef..." или "\n\a\n\t\\\b" .
Символы \,'," и в ряде случаев % являются специальными управляющими
символами в языке C и не должны входить в строки непосредственно, но
лишь в виде составного символа. Компилятор языка C всегда соединяет
строковые константы, не разделенные в тексте программы ничем, кроме
пробелов.

1.3. Описание скалярных переменных. Директива #define

Переменные описываются и инициализируются в виде:


тип имя [ = выражение ] , имя [ = выражение] , ... ;
Например:
int a,b='\020',c=-11; long d=-1L; double e,f=.003,g=1.2E40;
Каждая переменная, использованная в программе, должна быть явно
описана. Инициализация переменных не является обязательной. Все пере-
менные должны быть описаны раньше, чем будут использоваться. Язык С
требует, чтобы все описания локальных переменных внутри блока поме-
щались перед первым исполняемым оператором. В С++ можно описывать
переменные в любой точке блока перед их использованием.

11
Простейшая форма директивы препроцессора #define используется для
определения символических констант:
#define имя строка_замещения
Строка замещения, вообще говоря, есть любая последовательность
символов. Препроцессор до начала компиляции замещает каждое вхожде-
ние имени константы в тексте программы на строку замещения, но только
не внутри "". Строка замещения может быть, например, числовой кон-
стантой: #define PI 3.141592653589793 , тогда идентификатор PI можно
рассматривать как именованную константу в языке PASCAL (хотя это и не
тождественные вещи). У пользователей языка С существует традиция за-
писывать имена символических констант заглавными буквами, хотя это
никак не регламентируется в самом языке.

1.4. Операции. Выражения

В языке С определены следующие операции:


a) арифметические операции:
унарный +, унарный -, сложение +, вычитание - , умножение *, деление
/, остаток от деления %, инкремент ++, декремент --. Операция / определе-
на как для вещественных операндов, так и для целых, но для целых опе-
рандов результат всегда целый и равен целой части частного. Инкремент-
ная и декрементная операции применимы только к переменным, выраже-
ние x++ эквивалентно выражению x=x+1 . Возможны постфиксная и пре-
фиксная формы этих операций. При использовании префиксной формы
значение переменной изменяется до его подстановки в выражение, а при
использовании постфиксной формы - после вычисления выражения. На-
пример, пусть переменная x описана как int x=5; тогда значение выраже-
ния ++x*2 будет равно 12, а значение выражения x++*2 равно 10, значение
переменной в обоих случаях станет равным 6.
б) операции сравнения:
равно == , не равно != , больше > , больше либо равно >= , меньше < ,
меньше либо равно <= . Результат операции сравнения есть целое число 0
или 1.
в) логические операции:
отрицание ! , логическое “и” && , логическое “или” ||. При вычислении
логических выражений любое ненулевое значение считается истинным,
результат логической операции - целое число 0 или 1. Таким образом, ло-
гические операции применимы к любым скалярным величинам, в языке С
вообще нет специального типа данных для логических, или булевских, ве-
личин. Логические выражения всегда вычисляются только до получения

12
результата; так в выражении x!=2||1/x>3 при x=0 не произойдет ошибки,
т.к. выражение 1/x не будет вычисляться.
г) операция sizeof:
sizeof ( тип ) или sizeof выражение .
Результат операции - размер объекта в байтах, тип результата всегда
unsigned. В языке PASCAL эта операция реализована встроенной функци-
ей SizeOf.
д) операция явного преобразования типа:
( тип ) выражение
Если операнды некоторого выражения имеют разный тип, то они авто-
матически приводятся к одному (старшему) типу. Старшинство типов воз-
растает в такой последовательности : char < unsigned char < int < unsigned
< long < float < double .
е) битовые операции:
побитовое отрицание ~ , побитовое "и" & , побитовое исключающее
"или" ^ , побитовое "или" | , сдвиг битов влево << , сдвиг битов вправо
>> .
Битовые операции применимы только к целым операндам, все они
имеют аналоги в языке PASCAL : ~ = NOT, & = AND, ^ = XOR, | = OR, <<
= ShL, >> = ShR.
ж) операции присваивания:
в отличие от языка PASCAL в языке С конструкция, состоящая из двух
операндов и операции присваивания, является выражением, его значение
равно значению, полученному переменной в левой части выражения. Кро-
ме обычной операции присваивания = существует еще 10 операций: += , -
=, *= , /= , %= , <<= , >>= , &= , ^= , |= , смысл которых очевиден из про-
стейшего примера : x+=a означает x=x+a .
з) операция адрес:
& переменная
Эта унарная операция применима только к переменным, ее аналог в
языке PASCAL - операция @.
и) операция "запятая" - довольно необычная операция, имеющаяся
только в языке C. Несколько выражений, разделенных запятыми, вычис-
ляются последовательно слева направо. В качестве результата сохраняются
тип и значение самого правого выражения.
Приоритеты операций определены следующим образом:
унарные операции > {*, /, %} > {+, -} > {<<, >>} > {>, >=, <, <=}> {==,
!=} > & > ^ > | > && > || > операции присваивания > операция "запятая".
Операции одного приоритета выполняются в последовательности слева
направо, кроме унарных операций и операций присваивания, которые вы-

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

1.5. Стандартные математические функции

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


в файле math.h) включает :
int abs(int x) - |x| для int
long labs(long x) - |x| для long
double fabs(double x) - |x| для double
double ceil(double x) - наименьшее целое, не меньшее x
double floor(double x) - наибольшее целое, не большее x
double fmod(double x,double y) - остаток от деления x на y
double sqrt(double x) - √x
double hypot(double x,double y) - √(x2 +y2 )
double ldexp(double x,int n) - x⋅2n
double pow(double x,double y) - xy
double pow10(int p) - 10p
double exp(double x) - ex
double log(double x) - ln(x)
double log10(double x) - log(x)
double sin(double x) - sin(x)
double cos(double x) - cos(x)
double tan(double x) - tg(x)
double asin(double x) - arcsin(x)
double acos(double x) - arccos(x)
double atan(double x) - arctg(x)
double atan2(double x,double y) - arctg(y/x), возвращает значение от-π до π
double sinh(double x) - sh(x)
double cosh(double x) - ch(x)
double tanh(double x) - th(x)

1.6. Ввод-вывод

Форматный вывод данных осуществляется функцией printf :


int printf ( строка формата [ , список вывода ] )
которая возвращает количество выведенных символов. Список вывода мо-
жет содержать любые выражения, разделенные запятыми (полностью ана-
логичен списку вывода оператора WRITE в языке PASCAL). Строка фор-
мата заключается в “ “ и может содержать :
1) обычные символы, которые просто выводятся на экран;

14
2) специальные символы, управляющие размещением данных на экра-
не;
3) элементы формата данных.
Для вывода строки на экран элементы формата можно не использовать,
в этом случае список вывода пуст :
printf("\n\tВыведем что-нибудь...\a");
Элемент формата представляет собой последовательность :
% [ - ] [ длина поля ] [ . точность ] [ l ] спецификация типа
“ - “ означает сдвиг выводимых данных к левому краю поля ( по умолча-
нию данные сдвигаются вправо); длина поля - количество позиций для вы-
вода; точность - количество дробных цифр для чисел или количество вы-
водимых символов строки для строк; “ . ” - разделитель; l - означает, что
число должно рассматриваться как long или double. Могут использоваться
следующие спецификации типов :
d,i - для данных типа int;
u - для данных типа unsigned;
o - для данных типа unsigned (выводятся в восьмеричном виде);
x,X - для данных типа unsigned (выводятся в шестнадцатеричном виде),
шестнадцатеричные цифры A,B,C,D,E,F изображаются большими буквами
при использовании типа X и малыми буквами при использовании x;
c - для данных типа char, unsigned char (выводятся в символьном ви-
де);
f - для данных типа float,double (выводятся в виде с фиксированной
точкой, по умолчанию выводится 6 дробных цифр);
e,E - для данных типа float,double (выводятся в виде с плавающей точ-
кой, по умолчанию выводится 7 цифр мантиссы и две цифры порядка, все-
го 13 позиций), отличаются изображением буквы E;
g,G - для данных типа float,double - величины 0.0001<|x|<1000000 вы-
водятся в виде с фиксированной точкой с шестью значащими цифрами,
другие величины выводятся с плавающей точкой с шестью значащими
цифрами (всего занимают 12 позиций), спецификации g и G различаются
только изображением буквы e(E);
s - для строк;
p - для данных типа указатель.
Перед выводом всегда осуществляется преобразование данных к типу,
определенному спецификацией, что может приводить к изменению выво-
димого значения. Программист сам должен заботиться о правильном вы-
боре формата, поскольку компилятор не обращает на это никакого внима-
ния. Несоответствие количества элементов формата и количества выводи-
мых данных также может приводить к неприятным последствиям.
Форматный ввод данных осуществляется функцией scanf :

15
int scanf ( строка формата , список ввода )
которая возвращает количество введенных значений. Список ввода должен
содержать адреса переменных, описанных в программе, строка формата
должна содержать только элементы формата данных, которые интерпрети-
руются следующим образом :
d,u - для ввода целых десятичных чисел;
o - для ввода восьмеричных чисел (можно со знаком);
x,X - для ввода шестнадцатеричных чисел (можно со знаком);
i - для ввода целых чисел, которые могут задаваться любой целой кон-
стантой языка С (т.е. восьмеричные константы начинаются с 0, а шестна-
дцатеричные - с 0x);
c - для ввода символов;
f,e,E,g,G - для ввода вещественных чисел, которые могут изображаться
любой десятичной константой с фиксированной или плавающей точкой.
Модификаторы “ - ”, длина поля и точность при вводе игнорируются.
Введенное значение затем преобразуется к типу вводимой переменной.
Для данных типа int и unsigned можно использовать спецификации
d,u,o,x,X,i, но для переменных типа long обязательно следует указать мо-
дификатор l, т.е. использовать спецификации ld,lu и т.д. Данные типа char
и unsigned char можно вводить по формату c. Спецификации f,e,E,g,G
применимы для ввода переменных типа float, для переменных типа double
добавляется модификатор l. Вводимые числа разделяются любым количе-
ством пробелов. Символы считываются подряд, если перед каким-либо из
элементов формата %с не стоит пробел; в противном случае пробелы счи-
таются разделителями. Строка формата может содержать перед первым %
некоторую последовательность символов, тогда вводимая строка данных
должна начинаться этой последовательностью, которая пропускается
функцией scanf и лишь со следующего символа начинается ввод данных.
Функция scanf осуществляет ввод до обнаружения первого недопустимого
символа во входном потоке и возвращает количество успешно введенных
значений (аварийного прерывания при обнаружении неверных входных
данных не происходит). Однако функция никак не контролирует соответ-
ствие типа вводимых переменных и используемых элементов формата.
Для ввода-вывода символов используются функции getchar и putchar.
Функция int getchar() возвращает введенный символ, преобразованный к
типу int. Функция int putchar(int c) выводит в символьном виде значение c
и возвращает выведенное значение. Прототипы функций scanf, printf,
getchar и putchar находятся в файле stdio.h . Недостатком функции
getchar является необходимость после набора символа нажимать клавишу
Enter. В большинстве случаев вместо нее удобно пользоваться функциями,
прототипы которых находятся в conio.h : int getch() и int getche() . Эти

16
функции возвращают значение, соответствующее нажатой клавише (анало-
гичны функции ReadKey в языке PASCAL). При этом getche высвечивает
на экране введенный символ, а getch - нет. С помощью этих функций мож-
но опрашивать и клавиши, генерирующие два кода, - стрелки, функцио-
нальные клавиши и т.п.
Язык С++ полностью поддерживает средства ввода-вывода языка С.
Вместе с тем в С++ введен новый способ ввода-вывода, который носит на-
звание ввода-вывода потоком. Поток определяется как некоторый абст-
рактный тип данных, представляющий последовательность элементов дан-
ных, направленную от источника (source) к потребителю (consumer). Коли-
чество элементов в потоке называется его длиной, порядковый номер дос-
тупного в некоторый момент элемента называют текущей позицией
(current position). Используемые в программах потоки логически делятся на
три типа:
1) входные, из которых читается информация;
2) выходные, в которые передаются данные;
3) двунаправленные, допускающие как чтение, так и запись.
В начале выполнения программы автоматически открываются четыре
потока: cin, cout, cerr и clog, описанные в файле iostream.h . Поток cin
связан со стандартным вводом (по умолчанию с клавиатурой), cout - со
стандартным выводом (по умолчанию с дисплеем). Вместо записи:
#include <stdio.h>
printf("Вывод = %d,%f\n",i,r); scanf("%d",&x);
на С++ можно записать:
#include <iostream.h>
cout<<"Вывод="<<i<<","<<r<<endl; cin>>element;
Выполнение операции >> (извлечение из потока) заключается в преоб-
разовании последовательности символов потока в значение типизирован-
ного объекта. При выполнении операции << (включение в поток) соверша-
ется обратное преобразование - типизированное значение выражения пре-
образуется в последовательность символов потока. Отметим, что операции
<< и >> распознаются как операции потокового ввода-вывода, если они
употребляются справа от имен потоковых объектов
cin >> имя_объекта_базового типа
cout << выражение_базового_типа
Приоритет этих операций такой же, как у операций сдвига, поэтому,
чтобы вывести в поток значение выражения, содержащего операции более
низкого приоритета, требуется применение скобок:
cout << (x+y<z);
Операции сдвига также можно использовать в выводимых выражениях,
но обязательно должны быть использованы скобки. Оператор

17
cout<<(2<<1); выведет в поток значение 4. В библиотеке потокового вво-
да-вывода С++ предусмотрена возможность форматирования передавае-
мых данных, для этого применяются два способа. Первый состоит в ис-
пользовании флагов форматирования, которые унаследованы всеми пото-
ками библиотеки из базового класса ios. При втором способе форматиро-
вания употребляется специальный вид функций, называемых манипулято-
рами.

1.7. Метки, оператор goto, условные конструкции,


оператор break, функция exit

Оператор безусловного перехода записывается в виде


goto метка ;
где метка - это некоторый идентификатор, использованный в данной
функции в виде метка : оператор . Имена меток никак не описываются в
программе и распознаются компилятором по контексту. Передача управ-
ления разрешена на любой помеченный оператор в теле функции. Однако в
С++ существует одно ограничение : запрещено обходить описания, содер-
жащие инициализацию объектов. Это ограничение не распространяется на
вложенные блоки, которые можно обходить целиком.
Перед рассмотрением условных конструкций еще раз отметим, что в
языке С в явном виде булевский, или логический, тип данных отсутствует,
а значения true и false обозначаются соответственно ненулевым и нулевым
значениями. Такой подход лишает компилятор языка С возможности вы-
явления определенного класса ошибок. Например, с точки зрения языка С,
каждое из следующих двух выражений корректно и имеет значение true:
1<10<100 , 100<10<1 . Для второго выражения: 100<10 дает 0 (false) и 0<1
дает true. Другой пример : если i и k -целые переменные, то когда вместо
выражения i<k по ошибке будет записано выражение i+k , то компилятор
не в состоянии будет обнаружить эту ошибку.
В языке C есть особая операция, которая называется “операция выбора”
( обозначается как ?: ), она применяется к трем операндам и записывается
в виде
( логическое выражение ) ? выражение1 : выражение2
В данном случае в качестве логического может использоваться любое
выражение; оно интерпретируется как ложное, если его значение равно ну-
лю, и как истинное во всех остальных случаях. Результат операции выбора
равен значению выражение1, если логическое выражение истинно, и зна-
чению выражение2, если логическое выражение ложно. Тип результата
операции равен старшему из типов выражения1 и выражения2. Операция

18
выбора старше операций присваивания и младше всех других операций,
несколько записанных подряд операций выбора выполняются в последова-
тельности справа налево. Например, выражение (a<0)?-a:a дает абсолют-
ную величину a.
Условный оператор if в языке С записывается в виде:
if ( логическое выражение ) оператор/блок [ else оператор/блок ]
Логическое выражение (обязательно заключенное в круглые скобки) -
это любое выражение, считающееся истинным, если оно не равно нулю.
Условный оператор в свою очередь может включать условный оператор,
таким образом допустимы вложенные условные операторы. Синтаксис
языка предполагает, что при вложениях условных операторов каждое else
соответствует ближайшему к нему предшествующему if. Отличие от языка
PASCAL состоит лишь в отсутствии ключевого слова THEN и в том, что
перед else ставится ; .
Условный оператор switch имеет вид :
switch ( выражение ) {
case кв1 : [ операторы1 ]
case кв2 : [ операторы2 ]
..........................
default : [ операторы ]
}
выражение должно иметь целый тип ; кв1 , кв2 и т.д. - константные вы-
ражения целого типа. Порядок выполнения оператора switch следующий:
вычисляется выражение в скобках, если его значение равно значению ка-
кого либо из константных выражений, происходит переход к первому из
операторов, стоящих после этого константного выражения, в противном
случае - к оператору, стоящему после метки default, затем операторы вы-
полняются последовательно. Метку default не обязательно включать в
оператор. Выхода из оператора switch при достижении следующего case
или метки default не происходит - в отличие от аналогичного оператора
CASE языка PASCAL. Однако эти действия можно задать явно, используя
специальный оператор выхода break; , который можно включить в после-
довательности выполняемых операторов. Оператор break аналогичен по
своему действию процедуре Break в языке PASCAL, он вызывает немед-
ленное завершение выполнения условной или циклической конструкции, в
частности, оператора switch.
В любом месте любой функции для остановки программы можно ис-
пользовать функцию void exit(int status) , где status - код завершения, пе-
редаваемый операционной системе. Функция exit аналогична процедуре
Halt в языке PASCAL.
1.8. Циклические конструкции

19
В языке С существует три вида циклов : while , do while и for , являю-
щихся аналогами циклов WHILE, REPEAT и FOR (не вполне) в языке
PASCAL.
Оператор цикла while записывается в виде:
while ( логическое выражение ) оператор/блок
- и выполняется до тех пор, пока логическое выражение в скобках истинно,
т.е. не равно нулю.
Оператор цикла do while записывается в виде:
do оператор/блок while ( логическое выражение );
- и выполняется, пока истинно логическое выражение в скобках, но в отли-
чие от цикла while логическое выражение вычисляется после выполнения
тела цикла.
Цикл for является наиболее сложным из трех видов циклических опе-
раторов языка С :
for( [ инициализирующее выражение ] ; [ логическое выражение ] ;
[ корректирующее выражение ] ) [ оператор/блок ]
Цикл for выполняется следующим образом: перед началом цикла (толь-
ко один раз) вычисляется инициализирующее выражение, затем вычисляет-
ся логическое выражение и, если оно истинно, выполняется тело цикла, и
после каждого шага цикла вычисляется корректирующее выражение. В
цикле for все три выражения могут отсутствовать, но два символа “ ; ”
должны быть записаны в скобках. При отсутствии логического выражения
предполагается, что его значение всегда истинно. Никаких специальных
требований к выражениям, используемым в цикле for, не предъявляется,
это совершенно произвольные выражения, а их названия лишь обозначают
порядок записи этих выражений в скобках.
Запишем, используя три типа цикла, алгоритм вычисления суммы
квадратов натуральных чисел от 1 до n :
s=0; i=1; while (i<=n) {s+=i*i; i++;}
s=0; i=1; do s+=i*i; i++; while(i<=n);
s=0; for(i=1; i<=n; i++) s+=i*i;
Третий вариант записи можно упростить, если в инициализирующем и
в корректирующем выражениях использовать операцию “запятая”.
for(i=1,s=0; i<=n; s+=i*i,i++);
Таким образом, тело цикла оказалось пустым (но ; должна остаться).
Для немедленного выхода из цикла любого типа можно использовать
оператор break. Для пропуска части операторов тела цикла используется
оператор continue; , полностью аналогичный процедуре Continue в языке
PASCAL.
1.9. Указатели, адресная арифметика

20
Указателями называются переменные и константы, значениями кото-
рых являются адреса участков памяти, выделенных для объектов конкрет-
ных типов. В языке C так же, как в языке PASCAL, существуют типизиро-
ванные и обобщенные указатели. Типизированные указатели описываются
в виде:
базовый тип * имя указателя , * имя указателя , ... ;
* относится только к тому имени, непосредственно перед которым она
стоит. В одном операторе описания можно описать как переменные неко-
торого типа, так и указатели на такой тип. Символ * , меняющий тип пере-
менной, называется в таком контексте модификатором. Переменные типа
«указатель» можно инициализировать так же, как и числовые переменные,
например :
int a=0, *b=&a, **c=&b;
Обобщенный указатель имеет тип void* , в нем можно хранить адреса
любых объектов, но его использование ограничено по сравнению с типи-
зированными указателями. Отметим, что любой адрес можно проверить на
равенство или неравенство со специальным значением NULL (аналогич-
ным константе NIL в языке PASCAL), которое записывается вместо нуля.
Слово NULL позволяет определить указатель, который ничего не адресует.
Для указателей определены следующие операции.
1. Операция “значение”, записывающаяся в виде * указатель , резуль-
тат операции есть значение, записанное по адресу, хранящемуся в данном
указателе. В языке PASCAL такая операция записывается в виде указа-
тель^ . Операция “значение” применима только к типизированным указа-
телям. Она имеет такой же приоритет, как и все другие унарные операции.
2. К указателям применима операция явного преобразования типа, при-
чем указатель можно преобразовать не только в указатель другого типа, но
и в число, и число можно преобразовать в указатель.
3. Операция присваивания = , при этом оба операнда должны быть ука-
зателями одного типа, либо один из них должен иметь тип void* , либо
правый операнд должен быть числом 0.
4. Операции сравнения == , != , < , <= , > , >= , при этом оба операнда
должны быть указателями одного типа, либо один из них должен иметь
тип void* , либо один из операндов должен быть числом 0.
5. Операция вычитания - , оба операнда должны быть указателями од-
ного типа, либо второй операнд должен быть целым числом.
6. Операция сложения + , при этом второй операнд должен быть целым
числом.
7. Увеличение и уменьшение указателя ++ , -- .
8. Операции присваивания += и -= , второй операнд в этом случае
должен быть целым числом.

21
9. Операция “адрес” & .
При выполнении операций 5-8 используется так называемая адресная
арифметика, т.е. за 1 принимается размер в байтах типа данных, базового
для этого указателя. Это свойство арифметических операций с указателями
особенно часто используется при работе с массивами.

1.10. Массивы

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


тип имя массива [ длина ] [ = { список значений } ] ;
Здесь тип - тип элемента массива, имя массива - идентификатор, дли-
на - количество элементов массива, заданное константным выражением,
список значений - последовательность разделенных запятыми константных
выражений, например:
int a[7]={-1,147/3+26/7,33,5,6,0,-11};
Индексы элементов любого массива всегда начинаются с 0, так что по-
следний элемент имеет индекс длина-1. Так, в приведенном примере ин-
декс последнего (седьмого по счету) элемента массива a равен 6. Инициа-
лизирующая часть оператора не обязательна; кроме того, длина списка
значений может быть меньше длины массива, тогда оставшиеся элементы
массива не инициализируются. Если задан полный список значений, то
можно не указывать длину массива - память под массив будет распределе-
на в соответствии с количеством инициализирующих выражений, но мо-
дификатор [ ] , означающий массив, опускать нельзя.
Имя массива считается константой-указателем, равной адресу нулевого
элемента массива, т.е. a и &a[0] - это одно и то же. Обращение к элементам
массива возможно либо с помощью индексов (точно так же, как в языке
PASCAL) : a[i] , либо по адресу : *(a+i) - причем в последнем случае ис-
пользуется адресная арифметика. Фактически в программе всегда проис-
ходит обращение к элементу массива по адресу, а использование индексов
допускается лишь для удобства программирования. Выход индекса за гра-
ницы массива никак не отслеживается в языке С и не считается ошибкой.
Описание многомерного массива в общем случае имеет вид:
тип имя массива [ длина1 ] [ длина2 ] ... [ ={ список значений } ] ;
Например, двумерный массив b можно описать в виде
int b[3][4]={{1,2,3,4},{3,7,0,2},{3,7,4,8}};
Массив b является массивом из трех элементов, каждый из которых есть
массив из четырех элементов типа int, это обстоятельство явным образом
отражено в инициализирующей части оператора. Но если инициализиру-

22
ются все элементы массива, как в приведенном примере, внутренние {}
можно опускать:
int b[3][4]={1,2,3,4,3,7,0,2,3,7,4,8};
Внутренние {} необходимы, если часть элементов не инициализируются:
int b[3][4]={{1,2},{4,3,7},{0}};
Здесь инициализированы элементы b00,b01,b10,b11,b12,b20. В случае, если за-
дан полный список значений, можно опускать первую из длин измерений,
она будет неявно определена как длина списка значений, последовательно
поделенная на явно указанные длины измерений. Так, массив b можно
описать как
int b[][4]={1,2,3,4,3,7,0,2,3,7,4,8};
Имя многомерного массива также является константой - указателем на са-
мый первый элемент массива (т.е. элемент, все индексы которого - нули).
Элементы многомерного массива - массивы - являются указателями на со-
ответствующие подмножества элементов. Обращение к элементам много-
мерных массивов возможно с помощью индексов: b[i][j] или по адресу:
*(b[i]+j) . Каждый индекс элемента многомерного массива должен быть
обязательно заключен в собственные [ ] . Запись вида b[i,j] не является
ошибкой (вспомним об операции “запятая”), но она определяет не элемент
массива - число типа int, а адрес подмножества элементов, начинающегося
с элемента bj0 .

1.11. Функции

Если в таких языках, как FORTRAN и PASCAL, делается различие ме-


жду подпрограммами-процедурами и подпрограммами-функциями, то в
языках С и С++ используются только функции. Программы на языке С
(C++) - это совокупность следующих одна за другой функций, одна из ко-
торых main (главная функция). Функция main обеспечивает создание точ-
ки входа в откомпилированную программу. Каждая функция имеет стати-
ческую продолжительность существования и внешний тип компоновки: в
С запрещено вложение одной функции в другую.
Функция в языке С состоит из заголовка функции и тела функции. Тело
функции - это блок (последовательность операторов в {}), а заголовок в
общем случае имеет вид:
тип функции имя функции ( тип параметра имя параметра ,
[ [ тип параметра имя параметра , ... ] )
Заголовок функции не является оператором, поэтому он не заканчивается
“;”. Функция может иметь скалярный тип (число, указатель) или тип void
(не возвращает никакого значения). Все программные единицы, не вычис-
ляющие какого-либо одного скалярного значения, являются функциями

23
типа void. Каждый параметр в заголовке функции описывается отдельно,
даже если параметры имеют один и тот же тип. Допускаются функции без
параметров, в этом случае список параметров имеет вид () или (void) .
Выход из функции осуществляется оператором
return [ выражение ] ;
Если выражение задано в операторе return, то его значение преобразуется
к типу функции, в противном случае значение, возвращаемое функцией,
остается неопределенным. Функция может содержать любое количество
операторов return. Присваивать имени функции какое-либо значение, как
в языках PASCAL и FORTRAN, не нужно.
В большинстве языков программирования параметры передаются либо
по значению, либо по адресу. В первом случае подпрограмме доступна не
сама переменная, а только ее значение, во втором же случае подпрограмма
работает непосредственно с переменной, переданной ей в качестве пара-
метра. Таким образом, переменную, переданную по адресу, подпрограмма
может модифицировать, а переданную по значению - нет. В С++, как и в
Паскале, реализованы оба способа передачи параметров. В языке С пара-
метры передаются только по значению, функция получает лишь их значе-
ние, но не адрес, и поэтому не может изменить значение аргумента. Обще-
принятый способ обеспечить функции доступ к какой-либо переменной
вызвавшей программы состоит в том, что вместо самой переменной в каче-
стве параметра передается указатель на нее.
Параметры-массивы допускаются, но в качестве аргумента, соответст-
вующего такому параметру, задается имя массива, т.е. адрес, таким обра-
зом параметр-массив есть на самом деле параметр-указатель. Для парамет-
ров-одномерных массивов нет необходимости указывать длину. Запишем,
например, функцию, вычисляющую наибольший элемент массива целых
чисел :
int max_el(int x[],int n) {int i,m;
for(m=x[0],i=0;i<n;i++) m=(x[i]>m)?x[i]:m; return m;}
Такая функция может обрабатывать массивы любой длины, но при каждом
вызове следует определять фактическую длину с помощью аргумента n.
Описание параметра int x[] практически эквивалентно описанию int* x, так
что в функции можно отказаться от использования индексов, которые вве-
дены лишь для удобства записи :
int max_el(int* x,int n) {int i,m;
for(m=*x,i=1;i<n;i++) m=(*(x+i)>m)?*(x+i):m; return m;}
При использовании параметров-многомерных массивов возможно не-
сколько вариантов их передачи в функцию. Например, запишем функцию
нахождения наибольшего элемента квадратной матрицы. Пусть матрица
описана в программе в виде:

24
#define SIZE 3
void main(void) { int b[][SIZE]={1,2,3,4,5,6,7,8,9};
Тогда соответствующую функцию можно записать в трех вариантах :
1)
int max_el(int x[][SIZE],int n) {int i,j,m;
for(m=x[0][0],i=0;i<n;i++)for(j=0;j<n;j++) m=(x[i][j]>m)?x[i][j]:m;
return m;}
2)
int max_el(int x[][SIZE],int n) {int i,j,m;
for(m=*x[0],i=0;i<n;i++)
for(j=0;j<n;j++) m=(*(x[i]+j)>m)?*(x[i]+j):m;
return m;}
3)
int max_el(int*x,int size,int n) {int i,j,m;
for(m=*x,i=0;i<n;i++)
for(j=0;j<n;j++) m=(*(x+i*size+j)>m)?*(x+i*size+j):m;
return m;}
Вызов функции будет соответственно иметь вид:
1) max_el(b,SIZE)
2) max_el(b,SIZE)
3) max_el((int*)b,SIZE,SIZE)
Все функции в С являются рекурсивными, т.е. любая функция может
вызвать любую функцию, в том числе и функцию main, а также саму себя.
В вызывающей функции (или вне функций) функцию можно описать
как
тип имя ( список параметров ) ;
или
тип имя ( список типов параметров ) ;
Первый вариант описания отличается от заголовка функции только нали-
чием “;” и называется прототипом функции, имена параметров могут быть
произвольными, т.к. они никак не используются компилятором. Во втором
варианте эти имена вообще опущены, например:
int max_el(int*a,int b,int c);
int max_el(int*,int,int);
Для полного описания функций, не имеющих параметров, список типов
задается в виде (void) . Функции, расположенные в том же файле, до их
вызова можно вообще не описывать, описанием будет служить заголовок
функции. Если функция расположена после вызывающей функции или в
другом файле, то ее можно описывать или не описывать. Но для неописан-
ной функции компилятор не проверяет соответствие типов параметров и
аргументов и соответствие количества параметров и аргументов, кроме то-

25
го, тип неописанной функции всегда полагается равным int. Если функция
описана, компилятор следит за правильностью списка аргументов.
Язык С++ не предусматривает автоматического преобразования в тех
случаях, когда аргументы не совпадают по типам с соответствующими им
параметрам. С++ требует, чтобы в модуле, где происходит обращение к
функции (причем обязательно до соответствующего обращения) присутст-
вовало либо определение этой функции, либо ее прототип. Таким образом,
проверка соответствия типов параметров и аргументов всегда выполняется
в С++ на этапе компиляции.
В С++ тип функции столь же важен, как и ее имя. Две функции могут
иметь одинаковое имя, если имеется возможность различить их по сигна-
туре. Сигнатура функции задается числом, порядком следования и типами
ее параметров. Определение двух или более функций с одним и тем же
именем называется перегрузкой функций, поскольку более поздние объяв-
ления перегружают, или перекрывают, более ранние. Рассмотрим следую-
щую программу:
#include <stdio.h>
#include <string.h>
int func (int first){ return first*first;}
int func (unsigned first){ return -first*first;}
char func (char first){ return first+3;}
float func(float r){ return r*r;}
void main() { printf("%d ",func(4)); printf("%d ",func((unsigned)4));
printf("%c ",func('a')); printf("%f\n",func((float)1.2));}
Эта программа, в которой использованы четыре разные функции с именем
func, является совершенно корректной и выведет на экран строку
16 -16 d 1.44
В языке С допускаются указатели на функции, им можно присвоить ад-
рес функции и вызвать функцию по адресу, например:
int (*p)(int*,int,int)=&max_el; printf("%d",(*p)((int*)b,SIZE,SIZE));
Операции адрес & и значение * можно не записывать явно, они автомати-
чески генерируются компилятором, т.е. приведенный выше фрагмент
можно переписать в виде:
int (*p)(int*,int,int)=max_el; printf("%d",p((int*)b,SIZE,SIZE));
Используя указатели на функции, можно решать два типа задач:
1) присвоение указателю функции и использование указателя в качест-
ве косвенной ссылки на функцию;
2) передача функций в качестве аргументов для других функций. Это
позволяет создавать мощную систему функций, где частью функции явля-
ется другая функция, выбранная из библиотеки подпрограмм.

26
1.12. Классы памяти. Общие правила описания. Оператор typedef

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


классом памяти. Существует четыре класса памяти :
auto - автоматические переменные;
extern - внешние переменные;
static - статические переменные;
register - регистровые переменные.
Описатель класса памяти может указываться в описаниях переменных
слева от типа:
класс памяти тип имя = { список значений } ;
Автоматические переменные определены в той функции или блоке, где
они описаны и инициализируются при каждом выполнении функции (бло-
ка). Такие объекты “живут” лишь во время работы блока (функции).
Внешние переменные описываются вне функций (без описателя extern)
и определены во всех функциях данного файла, следующих за этим описа-
нием, а также во всех функциях, где эта переменная описана с описателем
extern. Внешние переменные инициализируются один раз в начале выпол-
нения программы. Внутри функций внешним переменным нельзя присваи-
вать начальные значения, это разрешено только в главном описании пере-
менной.
Статические переменные во всем аналогичны автоматическим, но ини-
циализируются лишь при первом выполнении функции (блока), в которой
они описаны, и “время жизни” таких переменных равно времени работы
программы.
Регистровые переменные могут храниться в регистрах центрального
процессора (что приводит к исключительно быстрой обработке этих пере-
менных), в остальном они аналогичны автоматическим. При недостатке
регистровой памяти описатель register игнорируется, но к любым перемен-
ным, описанным как регистровые, неприменима операция “адрес”.
По умолчанию переменные, описанные в функциях, имеют класс памя-
ти auto, описанные вне функций - extern. Внутри функции в случае совпа-
дения имен глобальной и локальной переменных действовать будет только
локальная переменная.
В С++ предусмотрен доступ к глобальным переменным, скрытым ло-
кальными переменными с тем же именем. Операция разрешения области
видимости :: позволяет в таких ситуациях воспользоваться глобальной
переменной. Например:
int x=5;
void main() { int x=3;
printf("Локальная x= %d\n",x); printf("Глобальная x= %d\n",::x); }

27
В описаниях кроме имен переменных и имен типов используются мо-
дификаторы : * - указатель, () - функция и [] - массив, причем в описаниях
сложных объектов модификаторов может быть много, что затрудняет чте-
ние и запись таких конструкций. Для правильной интерпретации описаний
используются следующие правила приоритетов :
1) чем ближе модификатор к имени, тем выше его приоритет;
2) [] и () имеют более высокий приоритет, чем *;
3) можно использовать круглые скобки для повышения приоритета
модификатора *.
Модификаторы доступа volatile и const позволяют сообщить компиля-
тору об изменчивости или постоянстве определяемого объекта. Если пере-
менная описана как const, то она недоступна в других модулях проекта,
подобно статическим переменным, и не может быть изменена в процессе
выполнения программы. Константа должна быть инициализирована при
описании. С помощью модификатора const создаются типизированные
именованные константы, которые в отличие от символических констант,
определенных директивой #define, подлежат контролю типов. Например:
const double PI=3.141528; const char yes='Y';
Одно из важных применений переменных типа const - защита параметров
функции от модификации, если аргумент передается по ссылке. Модифи-
катор volatile сообщает компилятору, что значение таких подвижных объ-
ектов может быть изменено скрытно от компилятора каким-либо фоновым
процессом. Например, при обработке прерывания глобальная переменная,
содержащая системное время компьютера, может изменить свое значение.
Компилятор не должен помещать такие переменные в регистровую память.
Пример объявления : volatile unsigned timer;
Так же, как и в языке PASCAL, в языке С программист имеет возмож-
ность создавать собственные именованные типы, для чего используется
оператор typedef . Запись оператора совпадает с записью описания пере-
менной, только вместо имени переменной задается имя типа и описание
предваряется ключевым словом typedef. Оператор typedef может нахо-
диться внутри функции - тогда он действует только в данной функции, ли-
бо вне функций, и тогда он действует до конца файла. Идентификатор, за-
данный в операторе typedef, становится именем типа и в дальнейшем мо-
жет использоваться в описаниях переменных. Например, вместо описания
#define SIZE 3
int b[][SIZE]={1,2,3,4,5,6,7,8,9};
можно использовать описание
#define SIZE 3
typedef int ARRAY[][SIZE]; ARRAY b={1,2,3,4,5,6,7,8,9};

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

1.13. Строковые переменные, ввод-вывод строк, стандартные функции


обработки строк. Функции проверки символов

Символьные строки в языке С - это массивы типа char, которые закан-


чиваются нуль-символом '\0' (отметим, что в языке PASCAL есть полный
аналог таких строк - открытые строки или null-terminated strings). Строко-
вые константы (последовательности символов в “”) размещаются в стати-
ческой памяти и всегда заканчиваются нуль-символом, который явно не
задается. Сама строковая константа является константой-указателем типа
char* . Строковую переменную можно описать в форме:
сhar имя [ длина ] = “ строка “ ;
При этом длину строки можно опустить, память будет выделена по факти-
ческой длине строковой константы + 1 байт для нуль-символа. Поскольку
имя строки является указателем, возможен альтернативный вариант описа-
ния:
char* имя = “ строка “ ;
Различие между двумя описаниями состоит в том, что имя массива есть
константа-указатель, ее значение нельзя изменить, а во втором случае опи-
сана переменная-указатель, которая лишь первоначально указывает на ме-
сто в памяти, занимаемое константой “ строка “, но впоследствии может
принять любое другое значение.
Массив символьных строк можно описать в виде :
char* имя [ длина ] = { “ строка1 “ , “ строка2 ” , ... } ;
или
char имя [ длина массива ] [ длина строки ] = { “ строка1 “ , “ строка2 “ ,
... };
Все элементы массива строк в любом случае являются указателями на со-
ответствующую строку, но в первом случае под каждый элемент массива
будет отведена память по фактической длине строковой константы, а во
втором - количество байт, равное описателю длина строки + 1 байт. По-
этому говорят, что первое описание создает непрямоугольный массив
строк, а второе - прямоугольный.
Для ввода и вывод строк служат функции
char* gets(char* строка )
int puts(char* строка )
Функция gets читает строку до символа '\n' и заменяет его нуль-символом.
Она возвращает адрес введенной строки или нулевой адрес NULL при воз-

29
никновении ошибки. Функция puts выводит символы строки до достиже-
ния нуль-символа, который заменяется на '\n', возвращает 0 при успешном
завершении или EOF ( число, равное -1 ) при возникновении ошибки.
Функция gets “не интересуется” размером вводимой переменной и записы-
вает в память, начиная с адреса строка, все введенные символы, так что
ввод слишком длинной строки, вероятнее всего, приведет к порче значений
других переменных, расположенных в памяти после вводимой перемен-
ной; кроме того, в этом случае в переменной не уместится нуль-символ и
она будет непригодна для дальнейшего использования. Функция puts так-
же устроена весьма просто и выводит сколь угодно большое количество
символов, пока не встретит в памяти нулевой байт или не выйдет за преде-
лы доступной памяти.
Для ввода и вывода строк можно использовать и функции scanf и printf
с форматом %s. Функция scanf считывает строку до первого пробела или
специального символа. Функция printf в отличие от puts не осуществляет
переход на новую строку. Полное описание функций scanf и printf имеет
вид:
int scanf(char *format, список ввода );
int printf(char *format [ , список вывода ] );
Эти функции можно использовать не только со строкой формата, заданной
строковой константой, но и с любой строковой переменной, что делает
форматный вывод весьма гибким.
Для работы со строками существует специальная библиотека функций,
прототипы которых описаны в файле string.h . Эта библиотека включает:
1) char* strcat(char* s1,char* s2) - конкатенирует строку s2 в конец
строки s1, возвращает s1;
2) char* strncat(char* s1,char* s2,size_t maxlen) - конкатенирует не
более maxlen символов строки s2 в конец строки s1, возвращает s1 (здесь и
далее size_t - это тип размера объектов в языке С, он определен как typedef
unsigned size_t; );
3) size_t strlen(char* s) - возвращает длину строки s не считая нулевого
символа;
4) char* strcpy(char* s1,char* s2) - копирует s2 в s1, возвращает s1;
5) char* strncpy(char* s1,char* s2,size_t maxlen) - копирует не более
maxlen символов s2 в s1, возвращает s1;
6) int strcmp(char* s1,char* s2) - возвращает разницу в ASCII кодах
первой пары несовпадающих символов строк s1 и s2;
7) int strncmp(char* s1,char* s2,size_t maxlen) - strcmp, учитывающая
не более maxlen первых символов.
8) int stricmp(char* s1,char* s2) - strcmp, не учитывающая регистр;

30
9) int strncmpi(char* s1,char* s2,size_t maxlen) - strncmp, не учиты-
вающая регистр;
10) char* strlwr(char* s) - переводит строку s в нижний регистр, воз-
вращает указатель на s;
11) char* strupr(char* s) - переводит строку s в верхний регистр, воз-
вращает указатель на s;
12) char* strchr(char* s,int c) - возвращает указатель на первое вхож-
дение символа c в строку s или NULL;
13) char* strrchr(char* s,int c) - возвращает указатель на последнее
вхождение символа c в строку s или NULL;
14) size_t strspn(char* s1,char* s2) - возвращает длину начального сег-
мента s1, который содержит только символы из строки s2;
15) size_t strcspn(char* s1,char* s2) - возвращает длину начального
сегмента s1, который не содержит ни одного символа из s2;
16) char* strdup(char* s) - распределяет память и возвращает указатель
на копию строки s;
17) char* strset(char* s,int c) - заполняет строку s символом c, возвра-
щает s;
18) char* strnset(char* s,int c,size_t n) - заполняет не более n первых
символов строки s символом c, возвращает s;
19) char* strpbrk(char* s1,char* s2) - возвращает указатель на первое
вхождение какого-либо символа строки s2 в строку s1 или NULL;
20) char* strrev(char* s) - переставляет символы строки s в обратном
порядке, возвращает s;
21) char* strstr(char* s1,char* s2) - возвращает указатель на первое
вхождение подстроки s2 в строку s1 или NULL.
Для преобразования чисел и других данных в строку удобно пользо-
ваться функцией sprintf , описанной в файле stdio.h :
int sprintf(char* s,char* формат [ , список вывода ] )
которая работает подобно функции printf, но вместо устройства вывода
использует строку s.
Для преобразования строки в число служат функции, описанные в фай-
ле stdlib.h :
double atof(char* s) - преобразует строку s в вещественное число;
int atoi(char* s) - преобразует строку s в целое число;
long atol(char* s) - преобразует строку s в длинное целое число.
Функции возвращают результат преобразования строки до первого
ошибочного символа или до нуль-символа. Они не предоставляют средств
контроля за правильностью записи числа в исходной строке.

31
Для проверки принадлежности отдельного символа некоторому множе-
ству символов и преобразования символов используются макроопределе-
ния (см. 1.14), описанные в файле ctype.h . Макроопределения проверки
символов возвращают ненулевое значение, если символ входит в множест-
во, и 0 - в противном случае:
isalpha(c) - латинская буква
islower(c) - малая латинская буква
isupper(c) - заглавная латинская буква
isdigit(c) - цифра
isxdigit(c) - 16-ричная цифра '0'..'9','A'..'F','a'..'f'
isalnum(c) - латинская буква или цифра
isspace(c) - пробел или управляющий символ \f,\n,\r,\t,\v
iscntrl(c) - невидимые символы
ispunct(c) - печатаемый символ, кроме пробела, букв и цифр
isgraph(c) - печатаемый символ, кроме пробела
isprint(c) - печатаемый символ, включая пробел
Макроопределения преобразования символов:
toupper(c) - для малой латинской буквы возвращает соответствующую
большую букву, в остальных случаях возвращает c ;
tolower(c) - для большой латинской буквы возвращает соответствую-
щую малую букву, в остальных случаях возвращает c .

1.14. Макроопределения

Макроопределения задаются, подобно символическим константам (ко-


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

32
2) параметры, предваряемые символом # в строке замещения макрооп-
ределения, заменяются на соответствующий аргумент в “ “;
3) подготовленная таким образом строка замещения подставляется в
текст программы.
В отличие от функций, предварительного вычисления значений аргу-
ментов не происходит, все операции выполняются только над текстом. В
качестве примера запишем макроопределение SQR для вычисления квад-
рата числа :
#define SQR(x) ((x)*(x))
и макроопределение, предоставляющее удобное средство отладочной пе-
чати:
#define OUTPUT(x,f) printf("\n"#x"=%"#f,(x))
Отметим, что использование макроопределений иногда приводит к побоч-
ным эффектам, которые возникают при многократном использовании ар-
гументов-выражений. Например, int x=2; y=SQR(x++); В результате вы-
ражение x++ * x++ установит x в 4 вместо 3, а y в 6=2*3 вместо ожидаемо-
го значения 4. Кроме того, текстовые подстановки не позволяют применять
в качестве аргументов арифметические выражения, поскольку они вычис-
ляются в теле макроподстановки многократно, что замедляет выполнение
программы. Еще один недостаток макроопределений - невозможность от-
личить вызовы функций от вызовов макроопределений в тексте програм-
мы.
В языке C++ существует возможность вставки фрагмента кода (а не
текста) функции, чтобы не выполнять переход к коду функции при ее вы-
зове. Для этого перед описанием функции ставится ключевое слово inline.
Например, inline int sqr(int x) {return x*x;} Наиболее эффективно исполь-
зовать подставляемые функции в тех случаях, когда тело функции состоит
всего из нескольких операторов, хотя при многократных вызовах подстав-
ляемой функции размеры программы могут увеличиться, однако исключа-
ются затраты на передачу управления к вызываемой функции и возврат из
нее. Так как компилятор встраивает код подставляемой функции вместо ее
вызова, то определение функции со спецификатором inline должно нахо-
диться в том же файле, что и обращение к ней, и размещаться до первого
вызова. Функция со спецификатором inline не может быть рекурсивной и
не может содержать операторов goto , switch , do , for , while.
1.15. Внешние файлы

Для работы с внешними файлами в языке C используются указатели на


файл, которые имеют тип FILE* , они аналогичны по своему назначению
переменным типа FILE, TEXT или FILE OF тип в языке PASCAL.
Для открытия файла служит функция

33
FILE* fopen(char * имя файла , char* режим доступа )
которая возвращает указатель на файл или нулевой адрес NULL при воз-
никновении ошибки. Существуют следующие режимы доступа:
“r” - текстовый файл для чтения;
“w” - текстовый файл для записи;
“a” - текстовый файл для записи в конец файла;
“r+” - текстовый файл для чтения и записи;
“w+” - новый текстовый файл для записи и чтения;
“a+” - текстовый файл с любым режимом доступа.
Модификатор b в конце режима доступа означает бинарный файл с соот-
ветствующим режимом доступа. Функция fopen выполняет те же действия,
что пара процедур Assign и Reset/Rewrite/Append в языке PASCAL.
Закрытие файла осуществляется функцией
int fclose(FILE* указатель на файл )
которая возвращает 0 при успешном завершении или EOF (=-1) при ошиб-
ке.
Функция
int feof(FILE* файл )
возвращает ненулевое значение, если достигнут конец файла.
Посимвольный ввод-вывод в файл осуществляется функциями:
int fgetc(FILE* указатель на файл ) - возвращает введенный символ
или EOF;
int fputc(int символ , FILE* указатель на файл ) - возвращает запи-
санный символ или EOF.
Форматный ввод-вывод в файл осуществляется функциями:
int fscanf(FILE* указатель на файл , char* формат , список ввода ) -
возвращает количество введенных значений или EOF;
int fprintf(FILE* указатель на файл , char* формат [ , список выво-
да ] ) - возвращает количество записанных символов или EOF.
Без всяких затруднений, так же, как в языке PASCAL, можно читать из
текстового файла числа функцией fscanf.
Строковый ввод-вывод в файл осуществляется функциями:
char* fgets(char* строка , int n, FILE* указатель на файл ) - читает
не более n-1 символов, прекращает чтение при достижении символа '\n',
добавляет в строку символ '\0', возвращает введенную строку или NULL ,
если произошла ошибка или достигнут конец файла;
int fputs(char* строка , FILE* указатель на файл ) - записывает
строку в файл, возвращает последний записанный символ или EOF в слу-
чае ошибки. Функция не записывает символ '\n'.

34
Указатели на стандартные файлы ввода и вывода stdin и stdout автома-
тически инициализируются в начале выполнения программы и могут быть
использованы без каких-либо дополнительных описаний. Задав в качестве
указателя на файл stdin в функциях fgetc, fscanf, fgets, мы получим соот-
ветственно функции getchar, scanf и gets для ввода с клавиатуры; исполь-
зовав stdout в fputc, fprintf и fputs, получим соответственно putchar,
printf и puts для вывода на экран.
С любыми файлами, в том числе и с текстовыми, можно работать как с
файлами прямого доступа, используя функции:
void rewind(FILE* файл ) - устанавливает текущую позицию на нача-
ло файла, аналогична процедуре Reset в языке PASCAL;
long ftell(FILE* файл ) - возвращает текущую позицию в файле, анало-
гична функции FilePos в языке PASCAL;
int fseek(FILE* файл , long смещение , int начало ) - позиционирует
файл, аргумент начало принимает одно из трех значений : SEEK_SET - от
начала файла, SEEK_CUR - от текущей позиции, SEEK_END - от конца
файла (смещение можно задавать со знаком). Возвращает 0 при успешном
завершении и ненулевое значение при ошибке. Аналогична процедуре Seek
в языке PASCAL.
Ввод-вывод в бинарные файлы осуществляется функциями:
size_t fread(void* буфер , size_t r, size_t n, FILE* файл ) - читает не
более n записей длиной r , помещая их в память начиная с адреса буфер,
возвращает количество считанных записей, аналогична процедуре
BlockRead в языке PASCAL;
size_t fwrite(void* буфер , size_t r, size_t n, FILE* файл ) - записывает
из памяти начиная с адреса буфер n записей длиной r в файл, возвращает
количество записанных записей, аналогична процедуре BlockWrite в языке
PASCAL.

1.16. Структуры, объединения, битовые поля

Структура - это объединенное в единое целое множество семантически


связанных именованных элементов разных типов (аналог записей в языке
PASCAL). Существует несколько вариантов описания структур, во многих
случаях при этом используется структурный шаблон. Структурный шаблон
описывается в виде:
struct имя шаблона { описания элементов } ;
Элементы структуры, описываемые в таком операторе - это то же самое,
что поля записей в языке PASCAL. Структурную переменную можно опи-
сать, используя предварительно описанный шаблон:

35
struct имя шаблона имена переменных ;
Можно объединить описания шаблона и переменной:
struct имя шаблона { описания элементов } имена переменных ;
или
struct { описания элементов } имена переменных ;
В этом случае шаблон может и не иметь имени. Имя шаблона не является
именем типа, его нельзя использовать в описаниях без слова struct, но
можно дать имя структурному типу, используя оператор typedef.
Структуры, как и любые другие переменные, можно инициализировать.
Допускаются массивы структур, вложенные структуры и указатели на
структуры. Пример описания структурных переменных:
struct st1 {int Num; unsigned char Ch; char St[64];};
struct st1 a={-11,'H',"123456789"}, b={0,'U',""},
c={777,'\a',"This is C structure"}, *p1=&c;
Обращение к элементу структуры осуществляется так же, как в языке
PASCAL :
имя структуры . имя элемента
Для указателей на структуры определена специальная операция кос-
венной адресации: -> , которая имеет самый высокий приоритет среди всех
операций, она используется для обращения к элементу структуры по адре-
су:
указатель -> имя элемента
Например: p1->Ch , p1->St[0] . Можно заменить операцию косвенной ад-
ресации операцией "значение": (*p1).Ch , (*p1).St[0] .
В смысле взаимодействия с функциями структуры рассматриваются
как скалярные объекты, поэтому допускаются параметры функций - струк-
туры и функции, возвращающие структуры. Для однотипных структур оп-
ределена операция присваивания = . К структурным переменным приме-
нима операция “адрес” &.
Объединение - это объект, который может содержать (попеременно)
данные разных типов (фактически объединение является структурой, все
элементы которой имеют нулевое смещение относительно ее базового ад-
реса). Для объединения резервируется память по самому большому эле-
менту. Полным аналогом объединений являются вариантные поля записей
в языке PASCAL.
Описание объединений и обращение к их элементам аналогично соот-
ветствующим действиям со структурами, но при этом ключевое слово
struct заменяется на union. Инициализировать объединение можно только
значением, имеющим тип первого из его элементов.
Битовое поле - это последовательность битов, лежащих внутри одного
или нескольких машинных слов. Описание поля имеет вид:

36
struct { [ тип ] [ имя ] : длина ; .................... } ;
Тип элемента поля всегда int или unsigned. Неименованные элементы
можно использовать для выравнивания (пропуска битов). Например, опи-
шем объект, позволяющий осуществлять прямой доступ к любому биту
младшего байта слова и к старшему биту:
struct BitStr { unsigned b1:1; unsigned b2:1; unsigned b3:1; unsigned
b4:1;
unsigned b5:1; unsigned b6:1; unsigned b7:1; unsigned b8:1; :7; un-
signed UP:1; };
union Word { unsigned N; struct BitStr b;};
union Word Number={0X3FE6};
printf("%u%u%u%u%u%u%u%u",Number.b.b1,Number.b.b2,
Number.b.b3, Num-
ber.b.b4,Number.b.b5,Number.b.b6,Number.b.b7,Number.b.b8);
printf(" Up bit=%u",Number.b.UP);
В арифметических выражениях элементы битовых полей рассматрива-
ются как целые числа.

1.17. Динамическое распределение памяти

Построение динамических объектов (списков, деревьев) выполняется


так же, как в языке PASCAL, т.е. для этого используются структуры.
Структуры могут включать в качестве элементов указатели на себя, на-
пример, элемент односвязного списка символьных строк можно описать в
виде:
struct Element { char *String; struct Element *Next;};
Для распределения и освобождения динамической памяти используют-
ся функции, описанные в stdlib.h :
void* malloc(size_t r) - возвращает указатель на место в памяти, отве-
денное для объекта размера r ; если память недоступна - возвращает
NULL;
void* calloc(size_t n,size_t r) - возвращает указатель на место в памяти,
отведенное для массива из n объектов размера r ; если память недоступна
- возвращает NULL;
void* realloc(void* p,size_t r) - изменяет размер объекта, на который
указывает p , на r , возвращает новый указатель или NULL;
void free(void* p) - освобождает память, распределенную функциями
calloc , malloc или realloc .
В С++ введены две унарные операции - new и delete - для динамиче-
ского распределения памяти, освобождающие программиста от необходи-

37
мости явно использовать библиотечные функции malloc, calloc и free. Опе-
рации new имя типа или new имя типа [ инициализатор ] позволяют
выделить и сделать доступным свободный участок памяти и возвращают
адрес выделенного места. Если память недоступна - возвращают NULL. В
выделенный участок заносится значение, определяемое инициализатором,
который не является обязательным элементом и представляет собой выра-
жение в круглых скобках. Например:
point=new int(15); h=new double(3.1415); string=new char[80];
Последний оператор отводит место в свободной памяти под массив из 80
элементов типа char . Указатель string содержит теперь адрес нулевого
элемента массива. Для явного освобождения выделенного операцией new
фрагмента памяти используется операция delete указатель , где указатель
адресует освобождаемый участок памяти. Например, операторами
delete point; delete string;
освобождаются участки памяти, связанные с указателями point и string.

1.18. Графика

Графические функции описаны в файле graphics.h . Эти функции прак-


тически полностью аналогичны процедурам и функциям модуля Graph
системы Turbo Pascal, но все имена функций записываются строчными бу-
квами, а имена констант - прописными. Некоторые отличия в вызове гра-
фических функций обусловлены свойствами языка С. Так, функция
initgraph описана следующим образом:
void initgraph(int *graphdriver,int *graphmode,char *pathtodriver);
Она используется, например, так:
int gdriver=DETECT,gmode;
initgraph(&gdriver,&gmode,"D:\\TP\\BGI");
Здесь в качестве аргументов задаются адреса переменных gdriver и
gmode, поскольку это выходные параметры; путь к драйверу заключается в
“ ” по правилам С и вместо одного символа \ записывается пара таких сим-
волов.
1.19. Дополнительные возможности языка:
тип enum, использование командной строки, функции
с переменным числом параметров, средства консольного
ввода-вывода, системное время, случайные числа

38
Оператор enum в языке С предназначен для объявления переменных
типа перечисление, а также для объявления именованных целочисленных
констант (типа int). Оператор enum записывается в виде
enum [ имя перечисления ] { K-имя1 [ = значение1 ] , K-имя2 [ = значе-
ние2 ] , ... };
enum имя перечисления имя1 [ , имя2 , ... ] ;
Первый оператор определяет набор именованных констант, перечис-
ленных в {}. Если какое-либо значение опущено, то оно больше предыду-
щей константы на 1, если опущено первое значение, то оно равно 0. Кон-
станты типа enum могут использоваться как обычные величины типа int.
Второй оператор определяет переменные перечислимого типа, такие пере-
менные могут принимать значения только из своего перечисления. Оба
действия можно объединить в одном операторе. Тип enum очень похож на
тип “перечисление” в языке PASCAL, но в С данные такого типа могут ис-
пользоваться как обычные числа.
Функция main может иметь два параметра : количество аргументов (ти-
па int) и массив строк-аргументов (типа char*[] ), которые позволяют ей
считывать информацию из командной строки. Эти параметры обеспечива-
ют те же возможности, что и функции ParamCount и ParamStr в языке
PASCAL. Для того чтобы в программе получить аргументы, переданные
через командную строку, достаточно записать список параметров функции
main, например, так:
void main(int NumberOfArgs, char *Args[])
Выведем на экран аргументы, полученные программой :
for(i=0;i<n;i++) printf("\n аргумент #%d = %s",i,Args[i]);
или
for(i=1;Args[i]!=NULL;i++) printf("\n аргумент #%d = %s",i,Args[i]);
В языке С допускаются функции, число параметров которых заранее не
определено. Список параметров таких функций имеет вид:
( список фиксированных параметров , ... )
При этом многоточие должно стоять только в конце списка и хотя бы один
параметр должен быть явно указан. Многоточие означает, что далее может
следовать любое количество параметров любого типа. Для работы с не-
именованными параметрами в такой функции используются макроопреде-
ления, описанные в stdarg.h :
va_start(va_list указатель,имя последнего фиксированного параметра)
- инициализирует указатель на список неименованных параметров;
va_arg(va_list указатель , тип ) - возвращает очередной параметр ука-
занного типа;

39
va_end(va_list указатель ); - закрывает список неименованных пара-
метров, его использование в функции обязательно.
Функции для организации интерфейса с пользователем (аналогичные
средствам модуля Crt в языке PASCAL) описаны в файле conio.h :
1) void window(int left, int top, int right, int bottom) - распределяет тек-
стовое окно;
2) void textbackground(int color) - устанавливает цвет фона;
3) void textcolor(int color) - устанавливает цвет символов;
4) void textattr(int attr) - устанавливает цветовой атрибут;
5) void clrscr(void) - очищает окно;
6) void gotoxy(int x, int y) - перемещает курсор;
7) void _setcursortype(int cur_type) - устанавливает тип курсора, тип
принимает значения: _NOCURSOR, _SOLIDCURSOR и _NORMALCUR-
SOR
8) int kbhit(void) - проверяет, нажата ли какая-нибудь клавиша;
9) int putch(int ch) - выводит символ на экран;
10) int cprintf(char * формат [ , список вывода ] ) - осуществляет фор-
матный вывод на экран;
11) int cputs(char *str) - выводит строку на экран;
12) int getch(void)
int getche(void) - читают символ с клавиатуры;
13) char *cgets(char *str) - читает строку с клавиатуры, str[0] должен
содержать максимально допустимую длину вводимой строки, через str[1]
функция возвращает длину введенной строки, сама строка начинается с
str[2] , функция возвращает &str[2] ;
14) int cscanf(char * формат [ , список ввода ] ) - осуществляет фор-
матный ввод с клавиатуры.
Функции, предназначенные для работы с временем, описаны в файле
time.h :
1) clock_t clock(void) - возвращает процессорное время в тактах, про-
шедшее с начала выполнения программы; clock_t - псевдоним типа long;
2) CLK_TCK - вещественная символическая константа, равная количе-
ству тактов процессора в секунду;
3) time_t time(time_t* timer) - возвращает время в секундах, прошед-
шее с 0 часов 1 января 1970 года; time_t - псевдоним типа long;
4) struct tm* localtime(time_t* timer) - возвращает структуру, содер-
жащую дату и время, соответствующие значению переменной timer;
структурный шаблон tm описан в файле time.h ;
5) char* asctime(struct tm* dt) - преобразует дату и время в символь-
ную строку и возвращает указатель на нее.

40
Функции для получения случайных чисел описаны в файле stdlib.h :
1) void randomize(void) - инициализирует генератор случайных чисел,
используя функцию time ;
2) void srand(unsigned seed) - инициализирует генератор случайных
чисел, используя значение параметра seed ;
3) int rand(void) - возвращает случайное число в диапазоне от 0 до
32767;
4) int random(int range) - возвращает случайное число в диапазоне от 0
до range-1.

41
Глава 2. ОПИСАНИЕ ЯЗЫКА FORTRAN

В этой главе содержится краткое описание языка FORTRAN 77, соот-


ветствующее компилятору Microsoft FORTRAN Optimizing Compiler
Version 5.00.

2.1. Структура FORTRAN-программы

FORTRAN - позиционный язык; это означает, во-первых, что операто-


ры языка должны располагаться в строго определенных позициях, и, во-
вторых, что значение символа может зависеть от его местонахождения.
Каждая строка FORTRAN-программы может содержать не более одного
оператора. Операторы записываются в позициях с 7-й по 72-ю. Если опе-
ратор не умещается на одной строке, его можно продолжить на следующей
строке, записав в 6-й позиции любой символ, кроме пробела и символа 0. В
6-й позиции начальной строки допускается символ 0. Позиции с 1-й по 5-ю
могут содержать метки. Метка в языке FORTRAN - это натуральное число
(можно с ведущими нулями). Пустые строки допускаются. Комментарий
занимает целую строку, в первой позиции строки комментария должен сто-
ять один из символов *, C или c. Второй тип комментария - комментарий в
остатке строки, он отделяется от оператора символом “ ! ”. И наконец, в
любой строке позиции с 73-й и до конца строки есть позиции комментария.
Пробелы допускаются в любом месте оператора, компилятор считает про-
белы значащими только внутри символьных констант, в других местах они
игнорируются. Это означает, в частности, что можно вставлять пробелы в
идентификаторы : MYNUMBER и MY NUMBER и M Y N U M B E R -
это корректная запись одного и того же идентификатора. Компилятор не
различает в идентификаторах строчные и прописные буквы. Длина иден-
тификатора может быть любой, но учитываются только шесть первых
символов. Ключевые слова языка FORTRAN не резервируются никогда, их
можно использовать в любых целях.
Последним оператором программы должен быть оператор END , пер-
вым оператором может быть (но не обязательно) оператор
PROGRAM имя программы
Кроме операторов текст программы может содержать так называемые
метакоманды, которые дают возможность устанавливать и менять режимы
компиляции. Метакоманда начинается с символа $, который записывается
в первой позиции. Одна из метакоманд - $INCLUDE - включает в текст
программы внешний файл, она записывается в виде $INCLUDE:'имя фай-
ла'
2.2. Типы данных. Константы

42
В языке FORTRAN есть арифметические, логические и символьные
данные. Арифметические типы данных:
INTEGER*1 - аналогичен char в языке C и ShortInt в языке PASCAL;
INTEGER*2 - аналогичен int в языке C и Integer в языке PASCAL;
INTEGER*4 - аналогичен long в языке C и LongInt в языке PASCAL;
INTEGER - соответствует либо INTEGER*2, либо INTEGER*4 - в за-
висимости от настройки компилятора, по умолчанию это INTEGER*4.
Явно задать длину данных типа INTEGER можно с помощью метакоман-
ды $STORAGE:2 или $STORAGE:4
REAL*4 (или REAL) - аналогичен float в языке C и Single в языке
PASCAL;
REAL*8 (или DOUBLE PRECISION) - аналогичен double в языке C и
Double в языке PASCAL;
COMPLEX*8 (или COMPLEX) - комплексные числа, действительная
и мнимая части которых имеют тип REAL*4
COMPLEX*16 - комплексные числа, действительная и мнимая части
которых имеют тип REAL*8
Логические типы данных:
LOGICAL*1
LOGICAL*2
LOGICAL*4
LOGICAL - соответствует либо LOGICAL*2, либо LOGICAL*4, в за-
висимости от настройки компилятора, по умолчанию это LOGICAL*4.
Длина типа LOGICAL также определяется метакомандой $STORAGE.
Символьные типы данных:
CHARACTER - символ,
CHARACTER*длина - строка символов.
Вообще говоря, различие между символом и символьной строкой в
языке FORTRAN не проводится, CHARACTER или CHARACTER*1 - это
строка из одного символа.
Целые константы в языке FORTRAN могут иметь основание от 2 до 36
и записываются в виде ± [ [ основание ] # ] константа . Если опущены ос-
нование и символ #, то константа считается десятичной, если опущено
только основание, то константа 16-ричная. Например: 2#11111111,
3#100110, 255, #FF, 19#D8. В целых константах с основанием, большим 10,
в качестве дополнительных цифр используются латинские буквы A=10,
B=11, ... ,Z=35. Все целые константы имеют тип INTEGER.

43
Вещественные константы записываются с фиксированной или плаваю-
щей точкой и имеют по умолчанию тип REAL : 2. , -.05 , 1E0 , -.3E-7 . В
константах двойной точности вместо буквы E пишется D : 1D0 , -67.3D-7 .
Логические константы записываются в виде .FALSE. или .TRUE.
Строковые константы записываются либо в виде 'символы' , либо в ви-
де 'символы' C. Последняя запись называется C-строкой (т.е. это строка,
организованная так же, как в языке C), она всегда заканчивается 0-
символом (который явно не записывается) и может содержать специальные
символы \n, \t, \v, \b, \r, \f, \a, \', \”, \\, \8-ричный номер , \x16-ричный номер.
Например, 'это - FORTRAN-строка' или 'это - \"C-строка\"\n\a'C .
Комплексные константы записываются в виде:
( действительная часть , мнимая часть )
Для записи действительной и мнимой частей можно использовать любые
константы, в том числе и целые : (-1,0.5) , (2D0,-3.5) .

2.3. Описание переменных, правила умолчания, операторы


IMPLICIT, DATA, PARAMETER

Переменные в языке FORTRAN описываются в виде:


тип имя1 , имя2 , ...
Язык FORTRAN допускает использование нигде не описанных перемен-
ных, в этом случае их тип определяется правилами умолчания: идентифи-
каторы, начинающиеся с I, J, K, L, M, N, имеют тип INTEGER, все осталь-
ные - тип REAL. Стандартные правила умолчания для неописанных пере-
менных можно изменить с помощью оператора IMPLICIT:
IMPLICIT тип ( буквы ) , ...
Здесь тип - описание типа, буквы - список латинских букв. В списке букв
можно использовать конструкцию буква - буква , задающую диапазон, на-
пример:
IMPLICIT REAL*8 (D,F-H,W-Z,A), INTEGER*1 (B,C,I-K)
Явное описание типа переменной во всех случаях отменяет правило умол-
чания для этой переменной. Правила умолчания можно полностью отме-
нить оператором IMPLICIT NONE. Если в программе есть такой опера-
тор, то каждая переменная должна быть явно описана.
Инициализировать переменные, т.е. присвоить им начальные значения
можно в операторе DATA :
DATA список имен / список констант /
список имен - это имена переменных, разделенные запятыми, список кон-
стант - константы, разделенные запятыми. Несколько повторяющихся

44
значений можно записать в виде повторитель * значение , где повтори-
тель - целая константа, например:
DATA a,b,i,j,k,c /1.2,3.141,2*5,-100,0/,x,y/2*0/
Операторы DATA должны располагаться в тексте программы после всех
операторов описания и до первого исполняемого оператора. Переменные
также могут инициализироваться непосредственно в операторе описания,
например:
REAL a/1.2/, b/3.141/, c/0/, x/0/, y/0/
INTEGER i/5/, j/5/, k/-100/
но в этом случае нельзя инициализировать список имен, каждая перемен-
ная должна быть инициализирована отдельно (но повторители при ини-
циализации массивов использовать можно).
Для определения именованных констант служит оператор
PARAMETER, аналогичный оператору CONST в языке PASCAL:
PARAMETER ( имя = константное выражение , ... )
Можно предварительно описать тип константы в операторе описания
типа, например:
LOGICAL*1 TRUE
REAL*4 b,c
PARAMETER(TRUE=.TRUE.,b=-1.5353,c=2)
PARAMETER(Number=#FF)
Именованные константы могут использоваться точно так же, как и неиме-
нованные, в частности, при инициализации переменных в операторах
DATA или операторах описания.

2.4. Оператор присваивания. Операции

Оператор присваивания записывается в виде:


имя = выражение
Типы переменной в левой части и выражения в правой части должны
быть или оба арифметическими, или оба логическими, или оба символь-
ными. Причем в арифметическом операторе присваивания нет больше ни-
каких ограничений, например, целой переменной можно присвоить ком-
плексное выражение, которое автоматически будет преобразовано к цело-
му типу.
В языке FORTRAN существует пять арифметических операций: сложе-
ние + , вычитание - , умножение * , деление / , возведение в степень ** .
Операция ** имеет наивысший приоритет и выполняется в последователь-
ности справа налево, остальные операции выполняются слева направо.
Операция деления / для вещественных операндов означает вещественное
деление, для целых операндов - деление нацело (точно так же, как в языке

45
C). Если операнды арифметической операции имеют разные типы, то они
приводятся к одному типу согласно правилу старшинства типов :
INTEGER*1 < INTEGER*2 < INTEGER*4 < REAL*4 < REAL*8 <
COMPLEX*8 < COMPLEX*16 . Если один операнд имеет тип REAL*8, а
второй - COMPLEX*8, то оба они приводятся к типу COMPLEX*16.
Кроме того, компилятор следит за корректностью выполнения целочис-
ленных вычислений, поэтому результат арифметической операции над
операндами INTEGER*1 будет иметь тип INTEGER*2, а результат ариф-
метической операции над операндами INTEGER*2 будет иметь тип
INTEGER*4.
Операции сравнения записываются в виде .LT. - меньше, .LE. - мень-
ше либо равно, .GT. - больше, .GE. - больше либо равно, .EQ. - равно, .NE.
- не равно. Они применимы к арифметическим и символьным операндам.
Для комплексных операндов определены лишь операции .EQ. и .NE. Ре-
зультат операции сравнения имеет тип LOGICAL.
Логические операции применимы к логическим операндам и записы-
ваются в виде .NOT. - логическое отрицание, .AND. - логическое “и”, .OR.
- логическое “или” , .EQV. - эквивалентно, .NEQV. - не эквивалентно; об-
ратите внимание, что ни одна операция сравнения, в том числе .EQ. и .NE.,
к логическим операндам неприменима.
Для символьных операндов определена только одна операция - опера-
ция сцепления // .
Приоритеты операций в языке FORTRAN определены следующим об-
разом : ** > { * , / } > { + , - } > // > {.EQ. , .NE. , .LT. , .LE. , .GT. ,
.GE.} > .NOT. > .AND. > .OR. > .EQV. > .NEQV.

2.5. Стандартные математические функции

В языке FORTRAN есть большое количество стандартных арифметиче-


ских функций, причем почти каждая из них имеет несколько разновидно-
стей, отличающихся типом аргумента и типом возвращаемого значения.
Здесь приводится лишь часть из них. Запись вида
INT(integer/real/complex) INTEGER
означает, что функция INT может иметь аргумент любого целого, любого
вещественного и любого комплексного типа, а результат, возвращаемый
этой функцией, имеет тип INTEGER. Запись вида
ABS(integer/real/complex) integer/real
означает, что функция ABS может иметь аргумент любого числового типа
и возвращает значение того же типа для целых и вещественных аргумен-
тов, значение типа REAL*4 для аргумента COMPLEX*8 и значение типа

46
REAL*8 для аргумента COMPLEX*16. Во всех случаях имя типа, запи-
санное большими буквами, означает только этот конкретный тип, а имя
типа, записанное малыми буквами, - любой из подходящих типов:
а) преобразование к целому типу:
1. INT(integer/real/complex) INTEGER
2. INT1(integer/real/complex) INTEGER*1
3. INT2(integer/real/complex) INTEGER*2
4. INT4(integer/real/complex) INTEGER*4
б) преобразование к вещественному типу:
5. REAL(integer/real/complex) REAL*4
6. DREAL(integer/real/complex) REAL*8
в) преобразования символа в число:
7. ICHAR(character) INTEGER*1
г) преобразование числа в символ:
8. CHAR(integer) CHARACTER
д) целая часть числа:
9. AINT(real) real
10. DINT(real) REAL*8
е) округление:
11. ANINT(real) real
12. DNINT(real) REAL*8
13. NINT(real) INTEGER
ж) абсолютная величина числа:
14. ABS(integer/real/complex) integer/real
15. IABS(integer/real/complex) integer
16. DABS(integer/real/complex) REAL*8
з) знак числа (функции возвращают абсолютную величину первого ар-
гумента, умноженную на знак второго аргумента):
17. SIGN(integer/real,integer/real) integer/real
18. ISIGN(integer/real,integer/real) INTEGER
19. DSIGN(integer/real,integer/real) REAL*8
и) остаток от деления:
20. MOD(integer/real,integer/real) integer/real
21. AMOD(integer/real,integer/real) REAL*4
22. DMOD(integer/real,integer/real) REAL*8
к) положительная разность (функции возвращают разность первого и
второго аргумента, если она положительна, или 0):
23. DIM(integer/real,integer/real) integer/real
24. IDIM(integer/real,integer/real) INTEGER

47
25. DDIM(integer/real,integer/real) REAL*8
л) максимальное и минимальное значение (функции допускают любое
количество аргументов):
26. MAX(integer/real,...) integer/real
27. MAX1(integer/real,...) INTEGER
28. AMAX1(integer/real,...) REAL*4
29. DMAX1(integer/real,...) REAL*8
30. MIN(integer/real,...) integer/real
31. MIN1(integer/real,...) INTEGER
32. AMIN1(integer/real,...) REAL*4
33. DMIN1(integer/real,...) REAL*8
м) произведение с двойной точностью:
34. DPROD(integer/real,integer/real) REAL*8
н) мнимая часть и комплексно сопряженное число:
35. IMAG(complex) real
36. DIMAG(complex) REAL*8
37. CONJG(complex) complex
38. DCONJG(complex) COMPLEX*16
о) квадратный корень:
39. SQRT(integer/real/complex) real/complex
40. DSQRT(integer/real) REAL*8
41. CSQRT(integer/real/complex) COMPLEX*8
42. CDSQRT(integer/real/complex) COMPLEX*16
п) экспонента:
43. EXP(integer/real/complex) real/complex
44. DEXP(integer/real) REAL*8
45. CEXP(integer/real/complex) COMPLEX*8
46. CDEXP(integer/real/complex) COMPLEX*16
р) натуральный логарифм:
47. LOG(integer/real/complex) real/complex
48. ALOG(integer/real) REAL*4
49. DLOG(integer/real) REAL*8
50. CLOG(integer/real/complex) COMPLEX*8
51. CDLOG(integer/real/complex) COMPLEX*16
с) десятичный логарифм:
52. LOG10(real) real
53. ALOG10(integer/real) REAL*4
54. DLOG10(integer/real) REAL*8
т) тригонометрические функции:

48
55. SIN(integer/real/complex) integer/real/complex
56. DSIN(integer/real) REAL*8
57. CSIN(integer/real/complex) COMPLEX*8
58. CDSIN(integer/real/complex) COMPLEX*16
59. COS(integer/real/complex) integer/real/complex
60. DCOS(integer/real) REAL*8
61. CCOS(integer/real/complex) COMPLEX*8
62. CDCOS(integer/real/complex) COMPLEX*16
63. TAN(real) real
64. DTAN(integer/real) REAL*8
65. COTAN(real) real
66. DCOTAN(integer/real) REAL*8
у) обратные тригонометрические функции:
67. ASIN(real) real
68. DASIN(integer/real) REAL*8
69. ACOS(real) real
70. DACOS(integer/real) REAL*8
71. ATAN(real) real
72. DATAN(integer/real) REAL*8
73. ATAN2(real,real) real
74. DATAN2(integer/real,integer/real) REAL*8
ф) гиперболические функции:
75. SINH(real/complex) real
76. DSINH(integer/real/complex) REAL*8
77. COSH(real/complex) real
78. DCOSH(integer/real/complex) REAL*8
79. TANH(real/complex) real
80. DTANH(integer/real/complex) REAL*8
х) битовые функции:
81. NOT(integer) integer - битовое отрицание
82. IAND(integer,integer) integer - битовое “и”
83. IOR(integer,integer) integer - битовое “или”
84. IEOR(integer,integer) integer - битовое исключающее “или”
85. ISHL(integer,integer) integer - сдвиг влево, если второй аргу-
мент положителен, или вправо, если второй аргумент отрицателен. Функ-
ция полностью аналогична операциям ShL и ShR в языке PASCAL и опе-
рациям << и >> в языке C

49
86. ISHA(integer,integer) integer - “арифметический” сдвиг, в от-
личие от ISHL при сдвиге вправо старший - знаковый бит заполняет все
освободившиеся позиции
87. ISHC(integer,integer) integer - циклический сдвиг, осуществ-
ляет циклическую перестановку битов, ни один бит при этом не теряется
88. BTEST(integer,integer) LOGICAL - возвращает .TRUE., если
бит первого аргумента с номером, заданным вторым аргументом, единич-
ный. Биты нумеруются от младшего к старшему, номер самого младшего
бита равен 0.
89. IBCLR(integer,integer) integer - зануляет бит первого аргумен-
та с номером, равным второму аргументу
90. IBSET(integer,integer)integer заединичивает бит первого аргумента с
номером, равным второму аргументу
91. IBCHNG(integer,integer) integer - меняет значение бита на
противоположное

2.6. Ввод-вывод

Язык FORTRAN предоставляет средства для бесформатного и формат-


ного ввода-вывода данных. Вывод на экран осуществляется оператором
PRINT формат , список вывода
формат - это или строка формата, или метка формата, или символ *, что
означает вывод без форматирования; список вывода - это любое количест-
во произвольных выражений, разделенных запятыми. Логические данные
при выводе изображаются буквами T и F. Для ввода с клавиатуры служит
оператор
READ формат , список ввода
В списке ввода могут быть только имена переменных. Вводимые с кла-
виатуры данные могут разделяться пробелами и (или) запятыми. Вводимые
логические значения обозначаются буквами T и F (любой последователь-
ностью символов, начинающейся с T или F).
Метка формата - это метка специального оператора FORMAT :
метка FORMAT( список форматов )
Этот оператор является невыполняемым и может стоять в любом месте
программы. Список форматов есть последовательность элементов формата
(называемых также для краткости форматами), разделенных запятыми.
Список форматов очень похож по своей структуре на строку формата
функции printf в языке C. Он может содержать форматы данных и управ-
ляющие элементы формата. Существуют следующие форматы данных:

50
Формат Тип данных Синтаксис
I целые In / In.c
F вещественные Fn.d
E вещественные En.d / En.d.e
D REAL*8 Dn.d
G вещественные Gn.d / Gd.e
A символьные A / An
L логические Ln
Z любые данные Z / Zn

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


вывода значения, c - количество выводимых цифр числа, d - количество
дробных цифр, e - количество цифр порядка. Если в формате I задано ко-
личество цифр и оно больше, чем количество цифр в выводимом значении,
то число дополняется слева нулями, если оно меньше, чем необходимо, то
данный параметр игнорируется. Если при выводе по форматам I,F,E,D,G
задан слишком маленький размер поля, то выводятся только символы *.
Вещественные числа с плавающей точкой представляются при выводе в
виде: знак. мантисса E знак порядка порядок . По формату Z все данные
выводятся в их внутреннем представлении и изображаются 16-ричным
числом.
Форматов в списке может быть меньше или больше, чем выводимых
значений. Если форматов больше, то неиспользованные форматы игнори-
руются. Если форматов меньше, то список форматов используется много-
кратно, но каждое исчерпание списка форматов означает переход на новую
строку.
Любой формат данных может иметь повторитель - целую константу,
которая записывается непосредственно перед форматом, например, 5F12.7.
Можно записывать повторители для групп форматов, тогда форматы, вхо-
дящие в группу, заключаются в круглые скобки, такие скобочные конст-
рукции могут быть вложенными, например, 3(2F10.2,E12.3E1,4(I6.5))
Существуют следующие управляющие форматы :
‘строка’ - “строка” - вывод строки,
nHсимволы - “литерал” - вывод n символов,
/ - переход на следующую строку,
nX - вывод n пробелов,
Tn - переход на n-ю позицию,
TLn - смещение влево на n позиций,
TRn - смещение вправо на n позиций,
SP - включение режима вывода знака + для чисел,
SS - выключение режима вывода знака + для чисел.

51
Символ / сам служит разделителем, поэтому в списке форматов его можно
не отделять запятыми. Кроме того, первый символ выводимой строки все-
гда считается управляющим и не изображается на экране. Значение управ-
ляющего символа таково:
0 - пропуск строки,
+ - вывод на ту же строку,
остальные - переход на новую строку.
Каждый новый оператор PRINT всегда начинает вывод с начала новой
строки.
Форматный ввод с клавиатуры не очень удобен и используется редко
(за исключением формата A), главным образом форматный ввод применя-
ется при чтении файлов. При вводе запрещены управляющие форматы
“строка” и “литерал”, управляющие форматы SP и SS игнорируются. Фор-
маты Tn, TLn, TRn, / действуют точно так же, как и при выводе, формат
nX пропускает n позиций во входном потоке. При вводе чисел можно ис-
пользовать управляющие форматы:
BN - игнорирование пробелов внутри числа (действует по умолчанию),
BZ - интерпретация пробелов внутри числа как нулей,
kP - умножение введенного значения на 10-k.
Формат kP также используется при выводе чисел по формату F, в этом
случае он означает умножение выводимого значения на 10k. Действие
форматов данных при вводе таково:
- формат In (второй параметр игнорируется) - целое число вводится из
очередных n позиций;
- форматы Fn.d , En.d , Dn.d , Gn.d (все форматы при вводе идентичны,
параметр e игнорируется) - вещественное число вводится из очередных n
позиций, если в константе нет десятичной точки, то последние d цифр ин-
терпретируются как дробные;
- формат A - значение символьной переменной (без апострофов) считы-
вается до конца строки, при необходимости усекается справа;
- формат An - значение символьной переменной (без апострофов) счи-
тывается из очередных n позиций, при необходимости усекается слева;
- формат Ln - логическое значение считывается из очередных n пози-
ций, оно может представляться любой последовательностью символов,
начинающейся буквой T(t) или F(f), перед этой буквой может быть символ
“.” и любое количество пробелов.
- форматы Z , Zn - 16-ричное значение вводится до конца строки или из
очередных n позиций и передается в память, занимаемую переменной; так
же, как для формата A, если n не задано, то вводимое значение усекается
справа, если задано, то слева. Если, напротив, вводимое значение слишком
короткое, то оно дополняется слева нулями.

52
Бесформатный ввод осуществляется почти так же, как в языке
PASCAL: данные во входном потоке разделяются любым количеством
пробелов или запятыми, числовые константы представляются в любом
корректном виде, но символьные константы нужно заключать в апостро-
фы. Каждый оператор READ всегда начинает ввод с новой строки.
Формат можно задать и непосредственно в операторах PRINT и READ,
для этого вместо метки формата записывается строка ‘(список форматов)’,
список форматов - точно такой же, как в операторе FORMAT, круглые
скобки и апострофы обязательны.

2.7. Оператор СОNTINUE. Операторы перехода.


Условные операторы

Оператор CONTINUE - пустой оператор, он не выполняет никаких


действий и используется, как правило, для улучшения структуры програм-
мы. Он практически всегда имеет метку.
Оператор перехода в языке FORTRAN имеет три разновидности: без-
условный, вычисляемый и по предписанию. Безусловный оператор пере-
хода записывается в виде:
GOTO метка
Вычисляемый оператор перехода:
GOTO (список меток)целое выражение
передает управление на метку из списка меток с порядковым номером,
равным значению целого выражения. Оператор перехода по предписанию
имеет вид
GOTO целая переменная
Этой целой переменной предварительно должна быть присвоена одна из
меток программы специальным оператором:
ASSIGN метка TO переменная
Целочисленная переменная, которой оператором ASSIGN была присвоена
метка, допускает еще одно применение - ее можно использовать в операто-
рах READ и PRINT вместо метки формата. Обратите внимание, что обыч-
ный оператор присваивания переменная=метка в этом случае не годится.
Условный оператор IF в языке FORTRAN также имеет три формы: ло-
гический IF, арифметический IF и блочный IF. Логический оператор IF за-
писывается в виде:
IF (логическое выражение)оператор
Здесь оператор - это любой выполняемый оператор, кроме логического и
блочного IF. Логический IF содержит только один исполняемый оператор
и не имеет ELSE-конструкции, его используют в самых простых условных

53
алгоритмах. Вторая форма условного оператора - арифметический опера-
тор IF :
IF (арифметическое выражение)метка1,метка2,метка3
Он выполняется следующим образом: вычисляется арифметическое выра-
жение (оно не должно быть комплексным), если его значение отрицатель-
но, то осуществляется переход на метку метка1, если оно равно 0, то осу-
ществляется переход на метку метка2, и если оно положительно - на метку
метка3.
Блочный оператор IF является обобщением логического условного опе-
ратора и соответствует по своим возможностям и структуре условным
операторам в языках PASCAL и C :
IF(логическое выражение)THEN
операторы
ELSEIF(логическое выражение)THEN
операторы
...................................
ELSE
операторы
ENDIF
Формально ELSEIF, ELSE и ENDIF являются отдельными операторами и
обязательно должны записываться в отдельной строке. Последовательно-
сти операторов после IF, ELSEIF, ELSE называют соответственно IF-
блоком, ELSEIF-блоком и ELSE-блоком. В любом из блоков можно запи-
сать любое количество операторов. Операторы ELSEIF и ELSE могут от-
сутствовать, но оператор ENDIF обязателен. Операторы, входящие в бло-
ки, сами могут быть условными, что позволяет записывать сколь угодно
сложные условные конструкции.
Оператор выбора SELECT CASE аналогичен оператору CASE в языке
PASCAL:
SELECT CASE(выражение)
CASE(список значений)
операторы
........................
CASE DEFAULT
операторы
END SELECT
Выражение должно иметь целый, логический тип или тип
CHARACTER*1. Список значений состоит из константных выражений
соответствующего типа, разделенных запятыми или двоеточиями, напри-
мер:
SELECT CASE (i**2+3-m)

54
CASE (-10:1,3,12,22:40)
PRINT*,'A'
CASE (-30:-15,8:10,19,-12)
GOTO 111
CASE DEFAULT
GOTO 222
END SELECT
В любом месте программы можно использовать оператор
STOP [параметр]
который прекращает выполнение программы. Здесь параметр - либо пус-
то, либо строка символов, либо целая константа в диапазоне от 0 до 99999.
Если оператор используется без параметра, то на экран выдается сообще-
ние STOP - Program terminated. Если параметр задан строкой символов, то
на экран выводится только эта строка. Если параметр задан целой констан-
той, то выводится сообщение Return code константа.
Оператор PAUSE служит для временной приостановки выполнения
программы и имеет точно такой же синтаксис, как и оператор STOP. Если
оператор выполнен без параметра, то на экран выводится сообщение Pause
- Please enter a blank line (to continue) or a DOS command . Если задан па-
раметр - символьная строка, то выводится эта строка, если задан параметр -
константа, то выводится сообщение Pause - константа. После этого поль-
зователь может совершить одно из трех действий: ввести пустую строку,
ввести любую команду DOS или ввести слово COMMAND, затем любое
количество команд DOS, а затем команду EXIT. После выполнения этих
операций выполнение программы продолжается обычным образом.

2.8. Операторы цикла

Оператор цикла DO (аналогичный оператору цикла FOR в языке


PASCAL) записывается в виде:
DO[метка]переменная=начальное значение,конечное
значение[,приращение]
Если в операторе задана метка, то последним оператором тела цикла явля-
ется оператор с такой меткой, если метка не задана, то тело цикла должно
заканчиваться специальным оператором END DO. Переменная цикла мо-
жет иметь любой арифметический тип, кроме комплексного. Начальное,
конечное значение и приращение - любые арифметические выражения,
кроме комплексных. Если приращение не задано, оно считается равным 1.
Последний оператор тела цикла не может быть: безусловным GOTO,
GOTO по предписанию, арифметическим IF, блочным IF, оператором DO.

55
Во всех случаях можно последним оператором цикла записывать оператор
CONTINUE. Допускаются вложенные циклы, они могут заканчиваться
одним и тем же оператором, т.е. иметь одну и ту же метку цикла. Запреща-
ется изменять значение переменной цикла в теле цикла, такая попытка рас-
сматривается как синтаксическая ошибка.
Оператор цикла DO WHILE полностью аналогичен по своему дейст-
вию оператору While в языке PASCAL :
DO [метка] WHILE(логическое выражение)
Такой цикл может заканчиваться либо меткой, либо оператором END DO.
Для выхода из любого типа цикла служит оператор EXIT, выполняющий
те же действия, что оператор break в языках C и PASCAL.
Оператор CYCLE завершает текущий шаг цикла и передает управле-
ние оператору DO или DO WHILE, он аналогичен оператору continue в
языках C и PASCAL.

2.9. Функции LOC, LOCFAR, LOCNEAR

Язык FORTRAN не дает возможности непосредственно работать с ад-


ресами переменных, как в языке PASCAL и, особенно, C. Единственные
средства языка, связанные с адресами - это встроенные функции LOC,
LOCFAR и LOCNEAR. Они возвращают адрес аргумента (но это может
быть не только переменная). Функция LOCFAR возвращает дальний адрес
(тип INTEGER*4), функция LOCNEAR - ближний адрес (тип
INTEGER*2), а функция LOC эквивалентна в дальней модели памяти
LOCFAR, а в ближней модели - LOCNEAR.

2.10. Массивы

Существуют два способа описания массива - описание измерений мас-


сива в операторе DIMENSION плюс описание типа элементов массива,
или описание измерений массива непосредственно в операторе описания
типа. Оператор DIMENSION записывается следующим образом :
DIMENSION имя массива([нижняя граница:]верхняя граница[,...])
Нижняя граница измерения массива по умолчанию равна 1. Оператор
DIMENSION никак не определяет тип элемента массива, но только коли-
чество и длины его измерений. Границы измерений могут задаваться лю-
быми арифметическими константными выражениями. Если какая-либо
граница задана вещественным значением, оно преобразуется к целому от-
брасыванием дробной части. Язык FORTRAN не задает никаких ограниче-

56
ний на размер массива и суммарный размер данных программы. Если тип
элемента массива должен отличаться от типа, определенного по умолча-
нию, необходимо явно определить этот тип обычным образом:
тип имя массива
Можно не пользоваться оператором DIMENSION для описания массива,
одновременно описывая тип элементов и измерения массива:
тип имя массива([нижняя граница:]верхняя граница[,...])
Инициализировать массивы можно в операторе DATA, так же, как и про-
стые переменные (можно использовать повторители). Элементы много-
мерных массивов располагаются в памяти не так, как в языках Паскаль и C:
первым меняется самый левый индекс. Пусть массив описан как
DIMENSION x(3,3), тогда его элементы будут записаны в памяти в таком
порядке : x11, x21, x31, x12, x22, x32, x13, x23, x33. Для сравнения такой же мас-
сив в программе на Паскале или C : x11, x12, x13, x21, x22, x23, x31, x32, x33.
Элементы массива в программе можно использовать так же, как простые
переменные соответствующего типа (кроме оператора DO),обращение к
элементу массива имеет вид:
имя массива(индексное выражение,...)
Индексное выражение может быть целого или вещественного типа. Масси-
вы можно целиком вводить оператором READ и выводить оператором
PRINT. Массиву можно присвоить массив с таким же описателем измере-
ний, тип элементов при этом может быть разным. Массиву можно присво-
ить скалярное выражение - это присваивание будет выполнено для каждого
элемента массива. Массивы могут быть операндами выражений, например,
запись PRINT*,SIN(a) вполне корректна. Операция над массивом означает
последовательное выполнение этой операции над каждым элементом мас-
сива, результат выражения есть массив такой же структуры с элементами
соответствующего типа.
При вводе и выводе массивов (и не только массивов) можно задавать
так называемые неявные циклы. Неявный цикл ввода-вывода записывается
в виде:
(список ввода-вывода,переменная цикла=начало,конец[,приращение])
Переменная цикла, параметры начало, конец и приращение имеют тот же
смысл, что и в операторе DO. Список ввода-вывода вводится или выводит-
ся столько раз, сколько раз выполняется неявный цикл. Если переменная
цикла каким-то образом входит в список ввода-вывода, ее значение изме-
няется на каждом шаге цикла. Сам список ввода-вывода, в свою очередь,
может включать неявные циклы.
2.11. Подпрограммы SUBROUTINE, FUNCTION и INTERFACE

57
В языке FORTRAN так же, как в языке PASCAL, есть подпрограммы-
процедуры (SUBROUTINE) и подпрограммы-функции (FUNCTION).
Оператор заголовка процедуры имеет вид:
SUBROUTINE имя процедуры(параметр[[атрибуты]][,...])
параметр - это или имя параметра (допускаются параметры - переменные,
массивы и подпрограммы), или символ *. Тип параметра в операторе
SUBROUTINE не указывается; если это необходимо, параметры описы-
ваются внутри процедуры. Процедура заканчивается оператором END и
располагается вне любой другой подпрограммы и главной программы так
же, как в языке C. Подпрограммы могут находиться в том же файле, что и
главная программа, или в других файлах. Процедура вызывается в главной
программе или в другой подпрограмме специальным оператором вызова
CALL:
CALL имя(список аргументов)
Рекурсия в языке FORTRAN запрещена. Количество и типы аргументов
в операторе CALL должны точно соответствовать количеству и типам па-
раметров в операторе SUBROUTINE. Все параметры по умолчанию пере-
даются в процедуры по адресу, это означает, что все действия над пара-
метрами в процедуре фактически осуществляются над аргументами. Чтобы
передать аргумент по значению, необходимо в операторе SUBROUTINE
для соответствующего параметра задать атрибут VALUE. Атрибуты зада-
ются в квадратных скобках после имени параметра. Параметр процеду-
ры может быть массивом любой размерности. Такой параметр должен
быть обязательно описан внутри процедуры. В описании измерений мас-
сива-параметра можно использовать не только константы, но и параметры-
переменные данной процедуры, что позволяет записывать процедуры, ко-
торые могут обрабатывать массивы переменного размера. Кроме того, раз-
решается верхнюю границу последнего измерения параметра-массива за-
давать символом *. Если предполагается, что нижняя граница индекса все-
гда равна 1, то одномерный массив-параметр описывается в виде имя(*).
Параметры-массивы также можно объявлять с атрибутом VALUE, но в
этом случае они не могут иметь переменный размер.
Параметрами процедур могут быть подпрограммы (процедуры и функ-
ции). Внутри процедуры такой параметр не нужно как-либо описывать, но
в вызывающей программной единице соответствующий аргумент должен
быть описан специальным образом. Если аргумент является именем стан-
дартной функции, то он описывается в операторе
INTRINSIC список имен
если это любая другая функция или процедура, то следует использовать
оператор

58
EXTERNAL список имен
Некоторые стандартные функции запрещено использовать в качестве ар-
гументов.
Формальным параметром процедуры может быть *. Аргументом, соот-
ветствующим такому параметру, должна быть конструкция *метка. Пара-
метры такого типа используются следующим образом : внутри процедуры
можно записывать любое количество операторов
RETURN
или
RETURN целое выражение
Первый оператор приводит к завершению процедуры и передаче управле-
ния оператору, следующему за оператором CALL в вызывающей про-
граммной единице. Второй оператор также завершает выполнение проце-
дуры, но управление передается оператору с меткой, равной значению од-
ного из аргументов. Значение целочисленного выражения после RETURN
определяет порядковый номер метки среди аргументов-меток. Например,
программа
SUBROUTINE A ( *,*,x,*,* )
INTEGER x
IF(x.LT.0) RETURN 1
IF(x.EQ.0) RETURN 2
IF(x.LE.10) RETURN 3
RETURN 4
END
CALL A( *100,*200,5,*300,*400)
STOP '0'
100 STOP '1'
200 STOP '2'
300 STOP '3'
400 STOP '4'
END
выведет на экран символ 3.
Оператор заголовка подпрограммы-функции записывается в виде:
[тип] FUNCTION имя(список параметров)
Если тип функции явно не указан, он определяется правилами умолчания.
Список параметров функции аналогичен списку параметров процедуры, но
не допускаются параметры - метки. Тип функции может быть целым, ве-
щественным, комплексным, логическим и символьным. Если тип функции
отличается от предполагаемого по умолчанию, то его нужно описать в вы-
зывающей программной единице. Список параметров функции может быть

59
пустым, в этом случае в заголовке функции можно не записывать и ( ), но
при вызове функции даже с пустым списком параметров список аргумен-
тов задается обязательно в виде ( ). Внутри функции необходимо присво-
ить имени функции возвращаемое значение, точно так же, как в языке
PASCAL. Можно использовать оператор RETURN, но без параметра (так
как запрещены параметры-метки).
Подпрограммы SUBROUTINE и FUNCTION, находящиеся в других
файлах или в библиотеках, можно описать в специальных подпрограммах
INTERFACE. Такое описание дает возможность компилятору проверить
соответствие типов аргументов и параметров при вызове подпрограмм.
Подпрограмма INTERFACE имеет следующую структуру :
INTERFACE TO заголовок процедуры или функции
описания параметров
END
Подпрограмма INTERFACE должна находиться в том же файле, что и
вызывающая программная единица.

2.12. Общие области, подпрограмма BLOCK DATA.


Оператор EQUIVALENCE

В языке FORTRAN отсутствует возможность описания глобальных


имен, все имена переменных и массивов являются локальными и опреде-
лены только в той подпрограмме, где они описаны. Взамен FORTRAN
предоставляет аппарат общих областей. Общая область описывается в ви-
де:
COMMON [/имя/] список
где имя - имя общей области, которое может быть опущено (но в програм-
ме может быть только одна неименованная общая область), список - это
разделенные запятыми имена переменных и массивов. Для массивов мож-
но задавать описатели измерений. Общие области используются в про-
грамме для передачи информации между подпрограммами, минуя списки
параметров. Общая область имеет смысл, если она описана более чем в од-
ной подпрограмме, при этом имена переменных, входящих в общую об-
ласть, совпадать не обязаны. Любая именованная общая область должна
иметь одинаковый размер во всех подпрограммах, для неименованной об-
щей области это необязательно.
Для инициализации переменных из именованных общих областей слу-
жит специальный тип подпрограммы - BLOCK DATA. Подпрограмма
BLOCK DATA имеет такую структуру:

60
BLOCK DATA [имя]
описания общих областей
операторы DATA
END
К подпрограммам BLOCK DATA не нужно специально обращаться, они
выполняются автоматически. В программе может быть сколько угодно
подпрограмм BLOCK DATA, но только одна неименованная.
Оператор EQUIVALENCE позволяет совмещать по памяти различные
переменные и массивы, в том числе и данные разных типов. Оператор за-
писывается в виде:
EQUIVALENCE (список объектов)[,...]
Здесь список объектов - это разделенные запятыми имена переменных,
имена массивов и элементы массивов. Количество совмещаемых объектов
не ограничено. Существует несколько ограничений на использование опе-
ратора EQUIVALENCE :
1) совмещения не должны противоречить друг другу;
2) совмещаемые переменные и массивы нельзя инициализировать в
операторе описания типа, но только в операторе DATA;
3) нельзя совмещать два объекта, если оба они входят в общие области;
4) не рекомендуется совмещать символьные и несимвольные объекты.

2.13. Символьные переменные

Символьные, или строковые, переменные описываются в виде:


CHARACTER[*общая длина] имя[*длина] , ...
Если задан описатель длина для какой-либо переменной, то он отменяет
для этой переменной действие описателя общая длина. Если длина вообще
не задана, она считается равной единице. Наибольшая возможная длина
строки равна 32767. Символьные переменные можно инициализировать в
операторе DATA и операторе присваивания. Если присваиваемое значение
длиннее, чем переменная, то оно усекается справа, если короче, то допол-
няется справа пробелами. Таким образом, символьные переменные в языке
FORTRAN имеют фиксированную длину. Неинициализированные сим-
вольные переменные заполнены 0-символами. Допускаются символьные
массивы любой размерности.
Именованные символьные константы задаются обычным образом в
операторе PARAMETER, но такие константы должны быть дополнитель-
но описаны в операторе CHARACTER. Символьные переменные можно
выводить оператором PRINT* и вводить оператором READ*, но при вводе

61
символьные значения следует задавать в апострофах. При форматном вво-
де-выводе используется формат A.
Существует только одна символьная операция - // - конкатенация или
сцепление строк. Кроме того, к символьным данным применимы операции
сравнения .EQ., .NE., .LT., .LE., .GT., .GE.
Имеется возможность непосредственного обращения к любой подстро-
ке символьной переменной, для этого используется псевдопеременная
“подстрока”:
имя строки([начало]:[конец])
где начало и конец - целочисленные выражения, задающие номера первого
и последнего символов, образующих подстроку. Если не задано начало, то
подстрока начинается с первого символа строки, если не задан конец, то
подстрока включает весь остаток строки ( но символ : обязательно должен
быть записан). Точно так же можно обратиться к подстроке элемента сим-
вольного массива. Подстрока может не только быть операндом выражения,
но также стоять в левой части оператора присваивания, вводиться операто-
ром READ и инициализироваться в операторе DATA, т.е. подстрока может
использоваться точно так же, как и настоящая символьная переменная.
Для обработки символьных данных в языке FORTRAN есть две встро-
енные функции:
LEN(character) INTEGER - возвращает длину
строки, но, поскольку строки в языке
FORTRAN имеют фиксированную дли-
ну, возвращаемое значение всегда есть
максимальная длина строки или длина
строки по описанию.
INDEX(character,character) INTEGER - возвращает номер символа
в первом аргументе, начиная с которого
второй аргумент входит в первый или
0.
Параметрами подпрограмм могут быть символьные переменные или
символьные массивы. Они должны быть соответствующим образом описа-
ны в процедуре. Длину строки можно задавать константой, переменной
или использовать описатель неопределенной длины (*). Отметим, что
именно для таким образом описанных строк-параметров и используется
функция LEN.

2.14. Операторные функции

62
Операторная функция записывается в виде:
имя(список параметров)=выражение
Она аналогична по своему назначению макроопределению в языке C. Опе-
раторную функцию можно рассматривать как подпрограмму-функцию, со-
стоящую из одного оператора. Тип значения, вычисляемого операторной
функцией, можно определить в операторе описания, сама операторная
функция должна размещаться после операторов описания и перед выпол-
няемыми операторами. Типы параметров операторной функции никак не
определяются, что придает этому средству дополнительную гибкость.

2.15. Внешние файлы


Для обработки внешних файлов в языке FORTRAN существуют опера-
торы:
OPEN - открытие файла;
CLOSE - закрытие файла;
READ - чтение из файла;
WRITE - запись в файл;
INQUIRE - опрос файла;
BACKSPACE - переход к предыдущей записи;
REWIND - переход в начало файла;
ENDFILE - запись признака конца файла
и встроенная функция EOF.
Синтаксис оператора OPEN таков:
OPEN([UNIT=]устройство [,FILE=имя файла] [,MODE=режим]
[,ACCESS=способ доступа] [,STATUS=статус файла]
[,FORM=тип записей] [,RECL=длина записи]
[,BLANK=интерпретация пробелов]
[,BLOCKSIZE=размер буфера][,ERR=метка]
[,IOSTAT=код ошибки])
устройство- целая константа от -32767 до 32767, или целое выраже-
ние, или *. Задает номер устройства, к которому присоединяется файл.
Устройство * всегда связано с клавиатурой и экраном, его переопределить
нельзя. Устройство 0 по умолчанию связано с клавиатурой и экраном, 5 - с
клавиатурой, 6 - с экраном, но эти устройства можно использовать и для
других целей;
имя файла - символьное выражение, можно задать ‘’, тогда программа
во время выполнения самостоятельно попросит ввести имя файла;
режим - ‘READ’, ‘WRITE’ или ‘READWRITE’ (по умолчанию);
способ доступа - ‘DIRECT’ или ‘SEQUENTIAL’ (по умолчанию);

63
статус файла - ‘OLD’, ‘NEW’, ‘UNKNOWN’ (по умолчанию) или
‘SCRATCH’. ‘SCRATH’ определяет временный файл, который будет
уничтожен перед окончанием программы, такой файл не должен иметь
имени;
тип записей - ‘FORMATTED’ (по умолчанию для ‘SEQUENTIAL’),
‘UNFORMATTED’ (по умолчанию для ‘DIRECT’) или ‘BINARY’;
длина записи - целое выражение, игнорируется для ‘SEQUENTIAL’;
интерпретация пробелов - ‘ZERO’ или ‘NULL’ (по умолчанию);
размер буфера - целое выражение, автоматически округляется до чис-
ла, кратного 512;
метка - метка оператора, которому передается управление при возник-
новении ошибки. Если этот параметр не задан, то ошибка открытия файла
приводит к аварийному завершению программы;
код ошибки - целая переменная, которая возвращает 0, если операция
завершилась успешно.
Оператор CLOSE записывается так:
CLOSE([UNIT=]устройство[,STATUS=статус файла]
[,ERR=метка] [,IOSTAT=код ошибки])
статус файла - ‘DELETE’ (по умолчанию для временных файлов)
или ‘KEEP’(по умолчанию для всех остальных);
метка - метка оператора, которому передается управление при возник-
новении ошибки;
код ошибки - целая переменная, которая возвращает 0, если операция
завершилась успешно.
Оператор READ для ввода из внешнего файла:
READ([UNIT=]устройство[,[FMT=]формат][,REC=номер записи]
[,END=метка-1][,ERR=метка-2][,IOSTAT=код ошибки])
номер записи - целое выражение, задается только для файлов прямого
доступа, записи нумеруются начиная с 1;
код ошибки - возвращает отрицательное значение, если достигнут ко-
нец файла, положительное значение, если произошла ошибка чтения, и ну-
левое значение при успешном завершении операции.
Оператор WRITE записывается в виде :
WRITE([UNIT=]устройство [,[FMT=]формат] [,REC=номер записи]
[,ERR=метка] [,IOSTAT=код ошибки])
В файл прямого доступа можно записывать в любое место, даже если это
вновь созданный файл; таким образом, некоторые записи в файле могут
физически существовать, но быть неопределенными. Переменная код
ошибки возвращает нулевое значение при успешном завершении операции.

64
Оператор INQUIRE позволяет получить все характеристики файла или
устройства. Он записывается в форме запроса об устройстве:
INQUIRE([UNIT=]устройство [,NAME=name] [,NUMBER=number]
[,EXIST=exist] [,NAMED=named] [,OPENED=opened] [,MODE=mode]
[,ACCESS=access] [,DIRECT=direct] [,SEQUENTIAL=sequential]
[,FORM=form] [,FORMATTED=formatted]
[,UNFORMATTED=unformatted] [,BINARY=binary] [,RECL=recl]
[,BLANK=blank] [,BLOCKSIZE=blocksize] [,ERR=метка]
[,IOSTAT=код ошибки])
или в форме запроса о файле:
INQUIRE(FILE=имя файла ... )
Здесь параметры устройство, имя файла, метка и код ошибки имеют
тот же смысл, что и раньше. Все остальные параметры - это переменные
целого, логического и символьного типа, которые возвращают характери-
стики устройства или файла. Они возвращают следующие значения:
name - имя файла,
number - номер устройства,
exist - .TRUE. , если файл существует,
named - .TRUE. , если файл имеет имя,
opened - .TRUE. , если файл присоединен к устройству,
mode - ‘READ’, ‘WRITE’, ‘READWRITE’ или ‘UNKNOWN’
access - ‘SEQUENTIAL’, ‘DIRECT’ или ‘UNKNOWN’ ,
direct - ‘YES’, ‘NO’ или ‘UNKNOWN’ ,
sequential - ‘YES’, ‘NO’ или ‘UNKNOWN’ ,
form - ‘FORMATTED’, ‘UNFORMATTED’ или
‘UNKNOWN’ ,
formatted - ‘YES’, ‘NO’ или ‘UNKNOWN’ ,
unformatted - ‘YES’, ‘NO’ или ‘UNKNOWN’ ,
binary - ‘YES’, ‘NO’ или ‘UNKNOWN’ ,
recl - длина записи в байтах,
blank - ‘NULL’, ‘ZERO’ или ‘UNKNOWN’ ,
blocksize - размер буфера в байтах.
Оператор BACKSPACE устанавливает файловый указатель на начало
предыдущей записи и может использоваться в двух формах :
BACKSPACE устройство
или
BACKSPACE([UNIT=]устройство[,ERR=метка]
[,IOSTAT=код ошибки])
Оператор REWIND устанавливает файловый указатель на начало фай-
ла:

65
REWIND устройство
или
REWIND([UNIT=]устройство[,ERR=метка][,IOSTAT=код ошибки])
Оператор ENDFILE записывает в файл признак конца файла, усекая
таким образом файл:
ENDFILE устройство
или
ENDFILE([UNIT=]устройство[,ERR=метка][,IOSTAT=код ошибки])
Эти три оператора работают и для текстовых файлов.
В языке FORTRAN есть всего одна встроенная функция для обработки
файлов
EOF(integer)
Функция возвращает истинное значение, если при вводе достигнут конец
файла.
Из текстового файла можно читать числа в свободном формате, т.е.
READ(номер устройства,*, ... ) ... Но если числа записаны в файле совер-
шенно произвольным образом, то прочесть их не так просто, как в языках
PASCAL и С, потому что каждое выполнение оператора READ - это чте-
ние новой строки.
Кроме внешних устройств, которые определяются своим номером и
связываются с каким-либо внешним файлом, можно использовать так на-
зываемые внутренние устройства - символьные переменные или массивы.
Открывать и закрывать такие устройства не нужно, а операторы READ и
WRITE записываются в виде
READ/WRITE(UNIT=имя,FMT=формат)список ввода/вывода
Внутренние устройства используются для преобразования числа в
строку и наоборот. Если в качестве устройства используется символьный
массив, то каждый элемент массива считается записью. Можно использо-
вать подстроку в качестве внутреннего устройства.

2.16. Структуры

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


структурам в языке C. Структурные переменные описываются в программе
в два этапа: сначала описывается структурный тип в операторе
STRUCTURE :
STRUCTURE /имя типа/
описания элементов
END STRUCTURE
а затем описываются переменные в специальном операторе RECORD :

66
RECORD/имя типа/ имя переменной[описатель измерений]...
Допустимы вложенные структуры и массивы структур, структуры мо-
гут быть параметрами подпрограмм. Элемент структуры записывается в
виде:
имя структуры.имя элемента
точно так же, как в языках PASCAL и C. Целые структуры нельзя вводить
или выводить, подобно массивам; для них не работает оператор DATA.

2.17. Динамические массивы

В языке FORTRAN отсутствуют средства для произвольных операций с


динамической памятью, как в языках PASCAL и C. Вместо них можно ис-
пользовать динамически распределяемые (или просто динамические) мас-
сивы. Память под такие массивы отводится не при компиляции програм-
мы, а по мере необходимости во время выполнения, а после использования
может быть освобождена. Динамические массивы описываются со специ-
альным атрибутом ALLOCATABLE :
имя[ALLOCATABLE] описатель измерений
Причем описатель измерений динамического массива имеет вид
( : , : , ... , : )
и задает только размерность массива, которая равна количеству двоеточий
(двоеточия разделяются запятыми). Выделяется память для динамического
массива оператором ALLOCATE :
ALLOCATE (имя описатель измерений [,STAT=код ошибки])
Описатель измерений в операторе ALLOCATE записывается уже полно-
стью, но, в отличие от обычных массивов, границы измерений могут зада-
ваться любыми выражениями. Существует одно ограничение на использо-
вание динамических массивов - их можно использовать как скалярные пе-
ременные только в списках ввода-вывода, но не в операторе присваивания
и не в арифметических выражениях. Память, отведенная под динамический
массив, освобождается оператором DEALLOCATE :
DEALLOCATE (имя[,STAT=код ошибки])

2.18. Графика

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


включить в программу операторы:
INCLUDE ‘FGRAPH.FI’
INCLUDE ‘FGRAPH.FD‘
или метакоманды
$INCLUDE:’FGRAPH.FI’

67
$INCLUDE:’FGRAPH.FD’
для подключения файлов, содержащих описания графических процедур и
функций (подпрограммы INTERFACE), а также для описания констант и
переменных. Графический режим устанавливается функцией
INTEGER*2 FUNCTION SetVideoMode (mode)
INTEGER*2 mode
Аргумент mode может быть, например, одной из констант:
$MAXRESMODE =-3 - графический режим максимального разрешения;
$MAXCOLORMODE =-2 - графический режим максимальной цветности;
$DEFAULTMODE =-1 - возврат к оригинальному режиму.
Функция возвращает 0 в случае неудачи. Аналогична InitGraph в языках
PASCAL и C.
Получить информацию о текущем экранном режиме можно с помощью
процедуры:
SUBROUTINE GetVideoConfig(s)
STRUCTURE/videoconfig/
INTEGER*2 numxpixels ! число пикселов по X
INTEGER*2 numypixels ! число пикселов по Y
INTEGER*2 numtextcols ! число текстовых колонок
INTEGER*2 numtextrows ! число текстовых строк
INTEGER*2 numcolors ! число цветов
INTEGER*2 bitsperpixel ! число битов на пиксел
INTEGER*2 numvideopages ! число доступных видеостраниц
INTEGER*2 mode ! текущий видеорежим
INTEGER*2 adapter ! активный адаптер дисплея
INTEGER*2 monitor ! активный монитор
INTEGER*2 memory ! видеопамять адаптера в килобайтах
END STRUCTURE
RECORD/videoconfig/s
Процедура возвращает текущую графическую конфигурацию через
структуру s. Поле mode возвращает установленный графический режим.
Поле adapter возвращает тип графического адаптера, например, $VGA = 8 .
Поле monitor возвращает тип монитора, например, $COLOR или
$ENHCOLOR .Фоновый цвет экрана устанавливается функцией
INTEGER*4 FUNCTION SetBkColor (color)
INTEGER*4 color
Подпрограмма
SUBROUTINE ClearScreen(area)
INTEGER*2 area

68
очищает некоторую область экрана, заполняя ее текущим цветом фона.
Параметр area может быть одной из следующих констант:
$GCLEARSCREEN - весь экран,
$GVIEWPORT - текущий видеопорт,
$GWINDOW - текущее текстовое окно.
Текущий цвет для линий и закраски устанавливает функция
INTEGER*2 FUNCTION SetColor(color)
INTEGER*2 color
Вывод пиксела на экран (с использованием текущего цвета) выполняется
функцией
INTEGER*2 FUNCTION SetPixel (x,y)
INTEGER*2 x,y
Отрезок прямой от текущего положения графического курсора до точки с
координатами x,y рисует функция
INTEGER*2 FUNCTION LineTo (x,y)
INTEGER*2 x,y
Графический курсор перемещается процедурой
SUBROUTINE MoveTo(x,y,s)
INTEGER*2 x,y
STRUCTURE/xycoord/
INTEGER*2 xcoord
INTEGER*2 ycoord
END STRUCTURE
RECORD/xycoord/s
Процедура возвращает через параметр s предыдущее положение графиче-
ского курсора. Шаблон линии задается процедурой
SUBROUTINE SetLineStyle (mask)
INTEGER*2 mask
Эллиптическая дуга рисуется функцией
INTEGER*2 FUNCTION Arc(x1,y1,x2,y2,x3,y3,x4,y4)
INTEGER*2 x1,y1,x2,y2,x3,y3,x4,y4
Центром эллипса является центр прямоугольника, который определяется
точками (x1,y1) и (x2,y2), дуга начинается в точке, (x3,y3), и заканчивается
в точке (x4,y4). Эллипс рисуется функцией
INTEGER*2 FUNCTION Ellipse(control,x1,y1,x2,y2)
INTEGER*2 control,x1,y1,x2,y2
Центром эллипса является центр ограничивающего прямоугольника,
определяемого точками (x1,y1) и (x2,y2). Аргумент control может быть од-
ной из двух констант:
$GFILLINTERIOR - эллипс закрашивается,
$GBORDER - эллипс не закрашивается.

69
Прямоугольник рисуется функцией
INTEGER*2 FUNCTION Rectangle (control,x1,y1,x2,y2)
INTEGER*2 control,x1,y1,x2,y2
Замкнутая область закрашивается функцией
INTEGER*2 FUNCTION FloodFill(x,y,boundary)
INTEGER*2 x,y,boundary
Функция аналогична FloodFill в языках PASCAL и C. Способ закрашива-
ния устанавливается процедурой
SUBROUTINE SetFillMask(mask)
INTEGER*1 mask
где mask - массив из 8 элементов типа INTEGER*1, который определяет
шаблон закраски области 8 на 8 пикселов. Если маска заполнения не уста-
новлена или массив нулевой, то для заполнения используется текущий
цвет. Текст на графический экран выводится процедурой
SUBROUTINE OutGText(text)
CHARACTER*(*) text
Текст выводится с использованием текущего шрифта, текущей графи-
ческой позиции и текущего цвета. После вывода текста сохраняется теку-
щая графическая позиция. Текст позиционируется по левому верхнему
краю. Кроме того язык FORTRAN позволяет пользоваться в графическом
режиме и обычными операторами PRINT и WRITE. Текущее положение
графического курсора возвращает процедура
SUBROUTINE GetCurrentPosition (s)
RECORD/xycoord/s
Ширину текста в пикселах возвращает функция
INTEGER*2 FUNCTION GetGTextExtent(text)
CHARACTER *(*) text
Подключение графических шрифтов осуществляет функция
INTEGER*2 FUNCTION RegisterFonts (PathToFiles)
CHARACTER*(*) PathToFiles
Файлы с графическими шрифтами имеют расширение fon и должны
быть доступны программе. Функция возвращает положительное число,
равное номеру последнего зарегистрированного шрифта при успешной ре-
гистрации. Отрицательное число означает : -1 - файл не найден; -2,-3 -
один или несколько файлов ошибочны. Графические шрифты отключают-
ся процедурой
SUBROUTINE UnRegisterFonts()
Текущий шрифт устанавливается функцией
INTEGER*2 FUNCTION SetFont (options)
CHARACTER *(*) options

70
Она устанавливает один из зарегистрированных шрифтов, который харак-
теризуется параметром options. Строка options может содержать подстро-
ки:
t’fontname’ - имя шрифта;
hx - высота символа в пикселах для масштабируемых шрифтов;
wx - ширина символа в пикселах для масштабируемых шрифтов;
nx - номер шрифта.

2.19. Дополнительные возможности языка:


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

В подпрограммах SUBROUTINE и FUNCTION могут быть дополни-


тельные точки входа, которые задаются оператором ENTRY :
ENTRY имя точки входа(список параметров)
Операторы ENTRY могут записываться в любом месте подпрограммы,
но не внутри цикла и не внутри условного оператора. Список параметров
дополнительной точки входа может не совпадать со списком параметров в
заголовке подпрограммы. Тип вычисляемого значения (если это функция)
также может отличаться от типа, указанного в заголовке. Но в самом опе-
раторе ENTRY задать вычисляемый тип нельзя, это нужно сделать в опе-
раторе описания в начале подпрограммы. Подпрограмму, имеющую до-
полнительные точки входа, можно вызвать через любую из этих точек,
точно так же, как и по основному имени. Но дополнительные точки входа
нельзя передавать другим подпрограммам как аргументы.
Метакоманда $FREEFORM позволяет записывать текст программы в
так называемом свободном формате. При этом операторы можно разме-
щать в позициях с 1-й по 80-ю; метки записываются начиная с 1-й позиции
и могут не отделяться от оператора пробелом; если последний символ
строки “-”, то следующая строка считается строкой продолжения; признак
комментария -символ “ в 1-й позиции.
Строки отладки позволяют осуществлять так называемую условную
компиляцию, т.е. компилировать не все операторы программы, а лишь не-
которое их подмножество. Строка отладки - это любая строка программы,
в первой позиции которой записана какая-нибудь латинская буква (кроме
буквы C). Для включения режима условной компиляции используется ме-
такоманда $DEBUG. Она может быть задана (в любом месте программы)
либо без параметров : $DEBUG , либо с параметром - строкой, состоящей
из латинских букв : $DEBUG:’буквы’. В первом случае метакоманда

71
включает расширенную диагностику ошибок времени выполнения (цело-
численное переполнение, проверка границ подстрок, проверка индексных
выражений элементов массивов и т.д.). Во втором случае метакоманда
включает режим условной компиляции - компилируются только те из
строк отладки, которые начинаются буквой, входящей в строку, заданную
в $DEBUG, остальные строки отладки считаются комментариями. Отме-
нить действие метакоманды $DEBUG можно либо другой метакомандой
$DEBUG, либо метакомандой $NODEBUG.
В языке FORTRAN есть некоторые средства для работы с текстовыми
окнами на экране. Они находятся в той же библиотеке, что и графические
процедуры и функции, поэтому для их использования необходимо под-
ключить к программе файлы fgraph.fi и fgraph.fd. Процедура
SetTextWindow (y1,x1,y2,x2) распределяет текстовое окно на экране, y1,y2
- номера строк, x1,x2 - номера позиций. Для очистки текстового окна ис-
пользуются функция SetBkColor и процедура ClearScreen, которые рабо-
тают как в графическом, так и в текстовом режиме. Положение курсора
устанавливает процедура SetTextPosition(y,x,OlpPos), где y,x - номер стро-
ки и номер позиции (координаты отсчитываются от левого верхнего угла
окна), OldPos - структурная переменная, через которую возвращаются пре-
дыдущие координаты курсора, она должна быть описана как
RECORD/rccoord/TextPos. Цвет текста устанавливает функция
SetTextColor(Color). Вывод в текстовое окно осуществляется специальной
процедурой OutText(text). Процедура выводит только символьные значе-
ния, числа должны быть предварительно преобразованы в строку. Не сле-
дует использовать совместно с процедурой OutText операторы PRINT и
WRITE. Форму курсора можно установить функцией SetTextCursor. Она
имеет один параметр типа INTEGER*2, значение #0707 задает тонкий кур-
сор, #0007 - максимально большой (“блочный”) курсор, #0607 - нормаль-
ный курсор, #2000 - невидимый курсор. При работе с текстовыми окнами
нежелательно использование оператора PAUSE, вместо него можно ис-
пользовать оператор READ(*,*), который ожидает нажатия клавиши Enter
(как ReadLn в языке PASCAL). Этот оператор работает и во всех других
случаях.
Для получения системного времени служит стандартная подпрограмма-
процедура
SUBROUTINE GetTim(h,m,s,s100)
INTEGER*2 h,m,s,s100
где h-часы, m-минуты, s-секунды и s100-сотые доли секунды.
Случайное вещественное число из отрезка (0,1) возвращает подпро-
грамма-процедура

72
SUBROUTINE Random(x)
REAL*4 x
Генератор случайных чисел можно инициализировать с помощью под-
программы-процедуры
SUBROUTINE Seed(Init)
INTEGER*2 Init
При возрастании параметра Init от 0 до примерно 10000 первое значе-
ние, возвращаемое генератором случайных чисел, растет от почти нуля до
почти единицы, а для больших значений Init этот цикл повторяется. В ка-
честве хорошего инициализатора можно брать величину s100*100, где s100
- сотые доли секунды текущего системного времени.

73
Глава 3. РЕШЕНИЕ ЗАДАЧ НА ЯЗЫКАХ C И FORTRAN

В этой главе приведены тексты нескольких программ на языках C,


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

Задача 1. Вывод натурального числа


Ввести натуральное число, не превосходящее 1000000, и вывести его в
десятичном, двоичном, 8-ричном и 16-ричном виде.

{ Решение Задачи 1 на языке PASCAL }


VAR N,Divider : LongInt; Shift,Num : Byte;
BEGIN
Write(#10,#13,'Введите число '); Read(N);
WriteLn('Число в десятичном виде : ',N);
Write('Число в двоичном виде : ');
FOR Shift:=19 DOWNTO 0 DO
Write(N AND(LongInt(1) ShL Shift)ShR Shift);
WriteLn(#10,#13,'Число в 8-ричном виде : ',N AND $C0000 ShR 18,
N AND $38000 ShR 15,N AND $07000 ShR 12,N AND $00E00 ShR 9,
N AND $001C0 ShR 6, N AND $00038 ShR 3,N AND $00007);
Write('Число в 16-ричном виде : '); Divider:=$F0000;
FOR Shift:=4 DOWNTO 0 DO BEGIN
Num:=N AND Divider ShR (Shift*4);
CASE Num OF 0..9 : Write(Num);
ELSE Write(Chr(Ord('A')+Num-10));
END;
Divider:=Divider ShR 4;
END;
END.

/* Решение Задачи 1 на языке C */


#include<stdio.h>
void main(void) { long n; int shift;
printf("\nВведите число "); scanf("%li",&n);
printf("\nЧисло в десятичном виде : %ld",n);
printf("\nЧисло в двоичном виде : ");

74
for(shift=19;shift>=0;shift--)printf("%ld",(n&(1L<<shift))>>shift);
printf("\nЧисло в 8-ричном виде : %lo",n);
printf("\nЧисло в 16-ричном виде : %lX",n); }

C Решение Задачи 1 на языке FORTRAN


$STORAGE:4
$DEBUG
INTEGER*1 SHIFT
PRINT *,'Введите число '
READ*,N
PRINT'(1X,A,I7)','Число в десятичном виде : ',N
0PRINT'(1X,A,20I1)','Число в двоичном виде : ',
1(ISHL(IAND(N,INT4(2)**SHIFT),-SHIFT),SHIFT=19,0,-1)
0PRINT'(1X,A,7I1)','Число в 8-ричном виде : ',
1ISHL(IAND(N,8#7000000),-18),ISHL(IAND(N,8#0700000),-15),
2ISHL(IAND(N,8#0070000),-12),ISHL(IAND(N,8#0007000),-9),
3ISHL(IAND(N,8#0000700),-6),ISHL(IAND(N,8#0000070),-3),
4IAND(N,8#0000007)
PRINT'(1X,A,Z5)','Число в 16-ричном виде : ',N
END

Задача 2. Сумма ряда


Ввести вещественное число x из отрезка [0,10]. Вычислить еx двумя
способами: разложением в ряд и стандартной функцией. Сравнить два
вычисленных значения.

{ Решение задачи 2 на языке PASCAL }


CONST Xmin=0; Xmax=10;
CONST S2:Real=1; a:Real=1; n:LongInt=1;
VAR x,S1 : Real;
BEGIN Write('Введите вещественное число из отрезка [',Xmin,',',Xmax,'] ');
Read(x);
IF (x<Xmin)OR(x>Xmax) THEN BEGIN
WriteLn('Неверное число'); Halt; END;
REPEAT S1:=S2; a:=a*x/n; S2:=S1+a; Inc(n); UNTIL S1=S2;
WriteLn('Exp(',x:0:2,'), вычисленная разложением в ряд : ',S2:0:3,
#10,#13,'Exp(',x:0:2,'), вычисленная стандартной функцией :' ,
Exp(x):0:3,#10,#13,'Погрешность : ',S2-Exp(x));
END.

/*Решение задачи 2 на языке C*/


#include<stdio.h>
#include<math.h>

75
#define XMIN 0.
#define XMAX 10.
void main(void) { double S2=1,a=1,x,S1; long n=1;
printf("\nВведите вещественное число из отрезка [%g,%g] ",XMIN,XMAX);
scanf("%lg",&x);
if(x<XMIN||x>XMAX){printf("\nНеверное число"); exit(1);}
do{S1=S2; a*=x/n++; S2=S1+a;}while(S1-S2);
printf("Exp(%g), вычисленная разложением в ряд : %g\n"
"Exp(%g), вычисленная стандартной функцией : %g\n"
"Погрешность : %g\n",x,S2,x,exp(x),S2-exp(x));}

C Решение задачи 2 на языке FORTRAN


PARAMETER(Xmin=0,Xmax=10)
REAL*8 x,S1/0/,S2/1/,a/1/
INTEGER*4 n/1/
PRINT'(1X,A,F4.1,A,F4.1,A)','Введите вещественное число из отрезка
* [',Xmin,',',Xmax,']'
READ*,x
IF (x.LT.Xmin.OR.x.GT.Xmax) STOP'Неверное число'
DO WHILE(S1.NE.S2)
S1=S2
a=a*x/n
S2=S1+a
n=n+1
END DO
PRINT'(2(1X,A,F4.1,A,F9.3/),A,E12.5)','Exp(', x, '), вычисленная р
*азложением в ряд : ', S2,'Exp(', x, '), вычисленная стандартной
* функцией : ', Exp(x),' Погрешность : ', S2-Exp(x)
END

Задача 3. Медленная сортировка


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

{ Решение задачи 3 на языке PASCAL }


CONST Nmax=100;
TYPE TArray=ARRAY[1..Nmax] OF Real;
VAR A : TArray; n,i,j,Imin : Byte; Min : Real;
BEGIN Write('Введите длину последовательности ');
Read(n);
IF (n=0)OR(n>Nmax) THEN BEGIN
WriteLn('Неверная длина'); Halt; END;

76
WriteLn('Введите последовательность');
FOR i:=1 TO n DO Read(A[i]);
FOR i:=1 TO n-1 DO BEGIN
Imin:=i; Min:=A[i];
FOR j:=i+1 TO n DO
IF A[j]<Min THEN BEGIN Min:=A[j]; Imin:=j; END;
IF Imin<>i THEN BEGIN A[Imin]:=A[i]; A[i]:=Min; END;
END;
WriteLn('Упорядоченная последовательность :');
FOR i:=1 TO n DO Write(A[i]:16); WriteLn;
END.

/* Решение задачи 3 на языке C */


#define NMAX 100
typedef double TArray[NMAX];
void main(void) {TArray a; char n,i,j,Imin; double Min;
printf("\nВведите длину последовательности "); scanf("%i",&n);
if(n<=0||n>NMAX){printf("\nНеверная длина"); exit(1);}
printf("\nВведите последовательность\n");
for(i=0;i<n;i++)scanf("%lg",a+i);
for(i=0;i<n-1;i++){
for(Imin=i,Min=a[i],j=i+1;j<n;j++)if(a[j]<Min){Min=a[j];Imin=j;}
if(Imin!=i){a[Imin]=a[i];a[i]=Min;}}
printf("\nУпорядоченная последовательность :\n");
for(i=0;i<n;i++)printf("%16lg",a[i]);}

C Решение задачи 3 на языке FORTRAN


PARAMETER(Nmax=100)
REAL*8 A(Nmax),Min
INTEGER*1 n,i,j,Imin
PRINT*,'Введите длину последовательности '
READ*,n
IF (n.LE.0.OR.n.GT.Nmax) STOP'Неверная длина'
PRINT*,'Введите последовательность'
READ*,(A(i),i=1,n)
DO 1 i=1,n-1
Imin=i
Min=A(i)
DO 2 j=i+1,n
IF(A(j).LT.Min) THEN
Min=A(j)
Imin=j
END IF
2 CONTINUE

77
IF(Imin.NE.i) THEN
A(Imin)=A(i)
A(i)=Min
END IF
1 CONTINUE
PRINT*,'Упорядоченная последовательность :'
PRINT 99,(a(i),i=1,n)
99 FORMAT(5G16.3)
END

Задача 4. Быстрая сортировка


Ввести натуральное число N, не превосходящее 10000. Получить N
случайных натуральных чисел, не превосходящих 999. Упорядочить числа
по неубыванию, используя алгоритм сортировки бинарными вставками.

{ Решение задачи 4 на языке PASCAL }


{$X+}
USES Crt;
CONST Nmax=10000; Range=1000;
TYPE TArray=ARRAY[1..Nmax] OF Word;
PROCEDURE Print(n:Word; VAR A:TArray);
VAR i : Word;
BEGIN i:=1;
WHILE i<=n DO BEGIN
Write(A[i]:4);
IF i MOD 480=0 THEN BEGIN
Write('Нажмите клавишу'); ReadKey; WriteLn; END;
Inc(i);
END;
IF i MOD 480<>0 THEN BEGIN
Write(#10,#13,'Нажмите клавишу'); ReadKey; WriteLn; END;
END;
PROCEDURE BinarySort(n:Word; VAR a:TArray);
VAR i,Nsort,as,bs,xs,Tmp : Word;
BEGIN Nsort:=1;
WHILE Nsort<n DO BEGIN
Tmp:=a[Nsort+1];
IF Tmp>=a[Nsort] THEN BEGIN Inc(Nsort); Continue; END;
IF Tmp<=a[1] THEN BEGIN
FOR i:=Nsort+1 DOWNTO 2 DO a[i]:=a[i-1];
a[1]:=Tmp; Inc(Nsort); Continue; END;
as:=1; bs:=Nsort; xs:=(as+bs)DIV 2;
WHILE (xs<>as)AND(xs<>bs) DO BEGIN

78
IF Tmp=a[xs] THEN BEGIN bs:=xs+1; Break; END;
IF Tmp<a[xs] THEN bs:=xs ELSE as:=xs;
xs:=(as+bs)DIV 2;
END;
FOR i:=Nsort+1 DOWNTO bs+1 DO a[i]:=a[i-1];
a[bs]:=Tmp; Inc(Nsort);
END;
END;
VAR A : TArray; n,i : Word;
BEGIN Write('Введите длину последовательности '); Read(n);
Randomize; FOR i:=1 TO n DO A[i]:=Random(Range);
Print(n,A);
WriteLn('Упорядоченная последовательность :');
BinarySort(n,A); Print(n,A);
END.

/* Решение задачи 4 на языке C */


#include<stdio.h>
#include<conio.h>
#include<time.h>
#include<stdlib.h>
#define NMAX 10000
#define RANGE 1000
void Print(unsigned n,unsigned a[]){ unsigned i;
i=1; while(i<=n){printf("%4u",a[i]);
if(!(i%480)){printf("Нажмите клавишу"); getch(); printf("\n");}i++;}
if(i%480){printf("\nНажмите клавишу"); getch(); printf("\n");}}
void BinarySort(unsigned n,unsigned a[]){ unsigned i,Nsort=0,as,bs,xs,Tmp;
while(++Nsort<n){Tmp=a[Nsort+1];
if(Tmp>=a[Nsort])continue;
if(Tmp<=a[1]){for(i=Nsort+1;i>=2;i--)a[i]=a[i-1];a[1]=Tmp;continue;}
as=1; bs=Nsort; xs=(as+bs)/2;
while(xs!=as&&xs!=bs){if(Tmp==a[xs]){bs=xs+1;break;}
if(Tmp<a[xs])bs=xs;else as=xs; xs=(as+bs)/2;}
for(i=Nsort+1;i>bs;i--)a[i]=a[i-1]; a[bs]=Tmp;}}
void main(void) { unsigned A[NMAX+1],n,i;
printf("\nВведите длину последовательности "); scanf("%i",&n);
randomize(); for(i=1;i<=n;i++)A[i]=random(RANGE);
Print(n,A); printf("Упорядоченная последовательность :\n");
BinarySort(n,A); Print(n,A);}

C Решение Задачи 4 на языке FORTRAN


SUBROUTINE $Print(n,A)
INTEGER*2 A(n)

79
nn=n/460
DO 1 i1=1,nn
PRINT 999,(A(i),i=(i1-1)*460+1,i1*460)
1 PAUSE'Нажмите ENTER'
IF(MOD(n,460).GT.0) THEN
PRINT 999,(A(i),i=nn*460+1,n)
PAUSE'Нажмите ENTER'
ENDIF
999 FORMAT(20I4)
END
SUBROUTINE BinarySort(n,a)
INTEGER*2 a(n),as,bs,xs,Tmp
Nsort=1
DO 99 WHILE(Nsort.LT.n)
Tmp=a(Nsort+1)
IF(Tmp-a(Nsort))1,99,99
1 IF(Tmp-a(1))2,2,3
2 DO 100 i=Nsort+1,2,-1
100 a(i)=a(i-1)
a(1)=Tmp
GOTO 99
3 as=1
bs=Nsort
xs=(as+bs)/2
DO WHILE(xs.NE.as.AND.xs.NE.bs)
IF (Tmp.EQ.a(xs)) THEN
bs=xs+1
EXIT
ENDIF
IF(Tmp.LT.a(xs)) THEN
bs=xs
ELSE
as=xs
ENDIF
xs=(as+bs)/2
END DO
DO 101 i=Nsort+1,bs+1,-1
101 a(i)=a(i-1)
a(bs)=Tmp
99 Nsort=Nsort+1
END
PARAMETER(Nmax=10000,Range=1000)
INTEGER*2 A(Nmax)
PRINT *,'Введите длину последовательности '

80
READ*,n
CALL GetTim(ih,im,is,is100)
CALL Seed(is*100)
DO 1 i=1,n
CALL Random(x)
1 A(i)=x*Range
CALL $Print(n,A)
PRINT*,'Упорядоченная последовательность :'
CALL BinarySort(n,A)
CALL $Print(n,A)
END

Задача 5. Слова
В файле INPUT.TXT записан некоторый русский текст. Переписать
все слова текста, содержащие наиболее часто встречающуюся в тексте
букву, в файл OUTPUT.TXT и найти самое длинное из них.

{ Решение задачи 5 на языке PASCAL }


FUNCTION RusUpCase(c:Char):Char;
BEGIN
IF c IN ['а'..'п'] THEN RusUpCase:=Chr(Ord(c)-Ord('а')+Ord('А'))
ELSE
IF c IN ['р'..'я'] THEN RusUpCase:=Chr(Ord(c)-Ord('р')+Ord('Р'))
ELSE RusUpCase:=c;
END;
VAR f1,f2 : Text;
c,m : Char;
s,ss : String;
Flag : Boolean;
L : Array['А'..'Я'] Of LongInt;
CONST Letters=['а'..'п','р'..'я','А'..'Я'];
BEGIN Assign(f1,'input.txt');
{$I-}
Reset(f1);
IF IOResult<>0 THEN BEGIN
WriteLn('Файл input.txt не найден'); Halt; END;
{$I+}
Assign(f2,'output.txt'); Rewrite(f2); FillChar(L,SizeOf(L),0);
WHILE NOT EOF(f1) DO BEGIN
Read(f1,c);
IF c IN Letters THEN Inc(L[RusUpCase(c)]);
END;
m:='А'; FOR c:='Б' TO 'Я' DO IF L[c]>L[m] THEN m:=c;

81
WriteLn('наиболее часто встречается в тексте буква "',m,'"');
Reset(f1);
WHILE NOT EOF(f1) DO BEGIN
Read(f1,c);
IF c IN Letters THEN BEGIN
s:=''; Flag:=FALSE;
WHILE c IN Letters DO BEGIN
IF RusUpCase(c)=m THEN Flag:=TRUE;
s:=s+c; Read(f1,c);
END;
IF Flag THEN WriteLn(f2,s);
END;
END;
Close(f2);
Writeln('слова текста, содержащие букву "',m,'", записаны в файл
output.txt');
Reset(f2); ss:='';
WHILE NOT EOF(f2) DO BEGIN
ReadLn(f2,s);
IF Length(s)>Length(ss) THEN ss:=s;
END;
Writeln('самое длинное из таких слов :"',ss,'"');
END.

/* Решение задачи 5 на языке C */


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char *LowRussian="абвгдежзийклмнопрстуфхцчшщъыьэюя";
char *UpRussian ="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
int RusUpCase(char c){char*p;
if(p=strchr(LowRussian,c))return *(UpRussian+(p-LowRussian)); return c;}
int Letter(char c){return strchr(LowRussian,c)||strchr(UpRussian,c);}
void main(void) {
FILE*f1,*f2; char c,m,flag,i;
long L[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char*s=malloc(81),*ss=malloc(81);
if((f1=fopen("input.txt","r"))==NULL){
printf("\nФайл input.txt не найден\n"); exit(7);}
f2=fopen("output.txt","w+");
do{c=fgetc(f1);if(Letter(c))L[RusUpCase(c)-'А']++;}while(c!=EOF);
m=0;for(c=1;c<=31;c++)if(L[c]>L[m])m=c; m+='А';
printf("\nнаиболее часто встречается в тексте буква %c",m);
fseek(f1,0,SEEK_SET);

82
while(!feof(f1)){c=fgetc(f1);
if(Letter(c)){i=-1;flag=0;
while(Letter(c)){if(RusUpCase(c)==m)flag=1;s[++i]=c;c=fgetc(f1);}
s[++i]='\0';if(flag)fprintf(f2,"%s\n",s);}}
printf("\nслова текста, содержащие букву \"%c\", записаны в файл
output.txt",m);
fseek(f2,0,SEEK_SET); strcpy(ss,"");
do{i=fscanf(f2,"%s",s);if(strlen(s)>strlen(ss))strcpy(ss,s);}while(i!=EOF);
printf("\nсамое длинное из таких слов :\"%s\"",ss);
fclose(f1); fclose(f2);}

C Решение задачи 5 на языке FORTRAN


BLOCK DATA
CHARACTER*32 Low,Up
COMMON/Russian/Low,Up
DATA Low,Up/'абвгдежзийклмнопрстуфхцчшщъыьэюя',
* 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'/
END
INTEGER FUNCTION Number(c)
COMMON/Russian/Low,Up
CHARACTER c,Low*32,Up*32
i=INDEX(Low,c)
IF(i)2,2,1
1 Number=i
RETURN
2 Number=INDEX(Up,c)
END
LOGICAL FUNCTION Letter(c)
COMMON/Russian/Low,Up
CHARACTER c,Low*32,Up*32
Letter=INDEX(Low,c).GT.0.OR.INDEX(Up,c).GT.0
END
INTEGER FUNCTION Length(s)
CHARACTER*(*) s
L=LEN(s)
DO WHILE(L.GT.0.AND.(s(L:L).EQ.' '.OR.s(L:L).EQ.CHAR(0)))
L=L-1
ENDDO
Length=L
END
LOGICAL Flag,Letter
INTEGER*4 L(32)/32*0/
CHARACTER*79 s/' '/,ss,Low*32,Up*32
COMMON/Russian/Low,Up

83
OPEN(1,FILE='input.txt',MODE='READ',STATUS='OLD',ERR=101)
GOTO 102
101 STOP'файл input.txt не найден'
102 OPEN(2,FILE='output.txt',MODE='READWRITE',STATUS='UNKNOWN')
103 READ(1,FMT='(A)',END=105)s
DO 104 i=1,Length(s)
IF(Letter(s(i:i))) THEN
ip=Number(s(i:i))
L(ip)=L(ip)+1
ENDIF
104 CONTINUE
GOTO 103
105 m=1
DO 106 i=2,32
IF(L(i).GT.L(m))m=i
106 CONTINUE
PRINT*,'наиболее часто встречается в тексте буква "',Up(m:m),'"'
REWIND 1
107 READ(1,FMT='(A)',END=108)s
i=1
DO WHILE(i.LE.Length(s))
IF(Letter(s(i:i))) THEN
Flag=.FALSE.
j=0
ss=' '
DO WHILE(i.LE.Length(s).AND.Letter(s(i:i)))
IF(Number(s(i:i)).EQ.m)Flag=.TRUE.
j=j+1
ss(j:j)=s(i:i)
i=i+1
ENDDO
IF(Flag)WRITE(2,FMT='(79A1)')(ss(j:j),j=1,Length(ss))
ELSE
i=i+1
ENDIF
ENDDO
GOTO 107
108 PRINT*,'слова текста, содержащие букву "',Up(m:m),'", записаны в ф
*айл output.txt'
REWIND 2
ss=' '
109 READ(2,FMT='(A)',END=110)s
IF(Length(s).GT.Length(ss))ss=s
GOTO 109

84
110 PRINT*,'самое длинное из таких слов :"',(ss(i:i),i=1,Length(ss)),
*'"'
CLOSE(1)
CLOSE(2)
END

Задача 6. График
Нарисовать на графическом экране график функции y=sin2(2x)+
+√x·cos2(x/2), 1≤x≤10.

{ Решение задачи 6 на языке PASCAL }


{$X+}
USES Graph,Crt;
CONST x1=1.0; x2=10.0;
FUNCTION f(x:Real):Real; BEGIN f:=Sqr(Sin(2*x))+Sqrt(x*Sqr(Cos(x/2))); END;
CONST X0=100; Y0=400; Lx=400; Ly=300; Blank=40; TicX=9; TicY=10;
TicSize=5;
ScreenColor=9; FieldColor=7; AxisColor=15; LineColor=14; TextColor=9;
TextStyle=1; TextSize=4; N=100; h=(x2-x1)/N;
VAR GraphDriver,GraphMode,i : Integer;
x,y,Ymin,Ymax,Mx,My : Real;
s : String;
BEGIN Ymin:=+1.7E38; Ymax:=-1.7E38;
FOR i:=0 TO N DO BEGIN
y:=f(x1+i*h);
IF y<Ymin THEN Ymin:=y;
IF y>Ymax THEN Ymax:=y;
END;
Mx:=Lx/(x2-x1); My:=Ly/(Ymax-Ymin);
GraphDriver:=Detect; InitGraph(GraphDriver,GraphMode,'');
SetFillStyle(8,ScreenColor); Bar(0,0,GetMaxX,GetMaxY);
SetFillStyle(SolidFill,FieldColor);
Bar(Blank,Blank,GetMaxX-Blank,GetMaxY-Blank);
SetColor(AxisColor); SetTextStyle(TextStyle,0,TextSize);
MoveTo(X0,Y0-Ly-TicSize);
SetTextJustify(CenterText,BottomText); OutText('Y');
MoveTo(X0,Y0-Ly); LineRel(0,Ly); LineRel(Lx,0);
SetTextJustify(LeftText,BottomText); MoveRel(TicSize,0); OutText('X');
SetTextStyle(TextStyle,0,1); SetTextJustify(CenterText,TopText);
FOR i:=1 TO TicX DO BEGIN
x:=x1+i*(x2-x1)/TicX; Str(x:0:1,s);
MoveTo(X0+Round((x-x1)*Mx),Y0);
LineRel(0,TicSize); OutText(s);

85
END;
SetTextJustify(RightText,CenterText);
FOR i:=1 TO TicY DO BEGIN
y:=Ymin+i*(Ymax-Ymin)/TicY; Str(y:0:2,s);
MoveTo(X0,Y0-Round((y-Ymin)*My));
LineRel(-TicSize,0); OutText(s);
END;
SetColor(TextColor); SetTextStyle(TextStyle,0,TextSize);
SetTextJustify(CenterText,BottomText);
OutTextXY(X0+Lx DIV 2,Y0-Ly-TextHeight(''),'y=sin2(2x)+√x⋅ cos2(x/2)');
SetColor(LineColor); SetLineStyle(0,0,3);
SetViewPort(X0,Y0,GetMaxX,GetMaxY,FALSE);
MoveTo(0,-Round((f(x1)-Ymin)*My));
FOR i:=1 TO N DO BEGIN
x:=x1+i*h;
LineTo(Round((x-x1)*Mx),-Round((f(x)-Ymin)*My));
END;
ReadKey; CloseGraph;
END.

/* Решение задачи 6 на языке C */


#include<stdio.h>
#include<conio.h>
#include<graphics.h>
#include<math.h>
#define x1 1.0
#define x2 10.0
#define F(x) (pow(sin(2*(x)),2)+sqrt((x)*pow(cos((x)/2),2)))
#define ROUND(x) (floor((x)+.5))
void main(void){
int X0=100,Y0=400,Lx=400,Ly=300,Blank=40,TicX=9,TicY=10,TicSize=5,
ScreenColor=9,FieldColor=7,AxisColor=15,LineColor=14,TextColor=9,
TextStyle=1,TextSize=4,N=100,GraphDriver,GraphMode,i;
float h=(x2-x1)/N,x,y,Ymin=+1.7E38,Ymax=-1.7E38,Mx,My;
char*s=(char*)malloc(10);
for(i=0;i<=N;i++){y=F(x1+i*h);Ymin=(y<Ymin)?y:Ymin;
Ymax=(y>Ymax)?y:Ymax;}
Mx=Lx/(x2-x1); My=Ly/(Ymax-Ymin); GraphDriver=DETECT;
initgraph(&GraphDriver,&GraphMode,""); setfillstyle(8,ScreenColor);
bar(0,0,getmaxx(),getmaxy()); setfillstyle(SOLID_FILL,FieldColor);
bar(Blank,Blank,getmaxx()-Blank,getmaxy()-Blank); setcolor(AxisColor);
settextstyle(TextStyle,0,TextSize); moveto(X0,Y0-Ly-TicSize);
settextjustify(CENTER_TEXT,BOTTOM_TEXT); outtext("Y");
moveto(X0,Y0-Ly); linerel(0,Ly); linerel(Lx,0);

86
settextjustify(LEFT_TEXT,BOTTOM_TEXT);
moverel(TicSize,0); outtext("X");
settextstyle(TextStyle,0,1); settextjustify(CENTER_TEXT,TOP_TEXT);
for(i=1;i<=TicX;i++){x=x1+i*(x2-x1)/TicX;sprintf(s,"%.1f",x);
moveto(X0+ROUND((x-x1)*Mx),Y0);linerel(0,TicSize);outtext(s);}
settextjustify(RIGHT_TEXT,CENTER_TEXT);
for(i=1;i<=TicY;i++){y=Ymin+i*(Ymax-Ymin)/TicY;sprintf(s,"%.2f",y);
moveto(X0,Y0-ROUND((y-Ymin)*My));linerel(-TicSize,0);outtext(s);}
setcolor(TextColor); settextstyle(TextStyle,0,TextSize);
settextjustify(CENTER_TEXT,BOTTOM_TEXT);
outtextxy(X0+Lx/2,Y0-Ly-textheight(""),"y=sin2(2x)+√x⋅ cos2(x/2)");
setcolor(LineColor); setlinestyle(0,0,3);
setviewport(X0,Y0,getmaxx(),getmaxy(),0);
moveto(0,-ROUND((F(x1)-Ymin)*My));
for(i=1;i<=N;i++) { x=x1+i*h;
lineto(ROUND((x-x1)*Mx),-ROUND((F(x)-Ymin)*My));}
getch(); closegraph();}

C Решение задачи 6 на языке FORTRAN


$INCLUDE:'FGRAPH.FI'
$INCLUDE:'FGRAPH.FD'
PARAMETER(X1=1.,X2=10.,N=100,X0=100,Lx=400,Y0=400,Ly=300,
* Blank=40,TicX=9,TicY=10,TicSize=5,ScreenColor=9,
* FieldColor=7,AxisColor=15,LineColor=14,TextColor=9,
* LineStyle=#FFFF)
RECORD /XYCoord/ xy
CHARACTER*4 s,Font/'n4'/,FontXY/'n2'/,Path*80/'*.fon'/
RECORD /FontInfo/ FInf
RECORD/videoconfig/video
REAL mx,my
INTEGER*1 Mask0(8)/#FF,#FF,#FF,#FF,#FF,#FF,#FF,#FF/,
* Mask(8) /#0F,#0F,#0F,#0F,#0F,#0F,#0F,#0F/
f(x)=SIN(2*x)**2+SQRT(x*Cos(x/2)**2)
h=(X2-X1)/n
Ymin=+1.7e38
Ymax=-1.7e38
DO 1 i=0,n
x=X1+i*h
y=f(x)
IF(Ymin.GT.y) Ymin=y
IF(Ymax.LT.y) Ymax=y
1 CONTINUE
mx=Lx/(X2-X1)
my=Ly/(Ymax-Ymin)

87
idummy=SetVideoMode($MAXRESMODE)
IF(idummy.EQ.0) STOP 'Graphics error'
CALL GetVideoConfig(video)
idummy=SetColor(ScreenColor)
CALL SetFillMask(Mask)
idummy=Rectangle($GFILLINTERIOR,0,0,
* video.NumXPixels-1,video.NumYPixels-1)
CALL SetFillMask(Mask0)
idummy=SetColor(FieldColor)
idummy=Rectangle($GFILLINTERIOR,Blank,Blank,
* video.NumXPixels-1-Blank,video.NumYPixels-1-Blank)
NumFonts=RegisterFonts(Path)
idummy=SetFont(Font)
idummy=GetFontInfo(FInf)
idummy=SetColor(AxisColor)
CALL MoveTo(X0,Y0-LY,xy)
idummy=LineTo(X0,Y0)
idummy=LineTo(X0+LX,Y0)
DO 11 i=1,TicX
x=X1+i*(X2-X1)/TicX
CALL MoveTo(X0+NINT(mx*(x-X1)),Y0,xy)
idummy=LineTo(X0+NINT(mx*(x-X1)),Y0+TicSize)
WRITE(s,1000) x
CALL MoveTo(X0+NINT(mx*(x-X1))-GetGTextExtent(s)/2,Y0+TicSize,
* xy)
CALL OutGText(s)
11 CONTINUE
DO 12 i=1,TicY
y=Ymin+i*(Ymax-Ymin)/TicY
CALL MoveTo(X0,Y0-NINT(my*(y-Ymin)),xy)
idummy=LineTo(X0-TicSize,Y0-NINT(my*(y-Ymin)))
WRITE(s,1000) y
CALL MoveTo(X0-GetGTextExtent(s)-TicSize,
* Y0-NINT(my*(y-Ymin))-FInf.PixHeight/2,xy)
CALL OutGText(s)
12 CONTINUE
1000 FORMAT(F4.1)
idummy=SetFont(FontXY)
idummy=GetFontInfo(FInf)
CALL MoveTo(X0,Y0-Ly-FInf.PixHeight,xy)
CALL OutGText('Y')
CALL MoveTo(X0+Lx,Y0-FInf.PixHeight,xy)
CALL OutGText('X')
CALL SetLineStyle(LineStyle)

88
idummy=SetColor(LineColor)
CALL MoveTo(X0,Y0-NINT(my*(f(X1)-Ymin)),xy)
DO 2 i=1,n
x=X1+i*h
y=f(x)
idummy=LineTo(X0+NINT(mx*(x-X1)),Y0-NINT(my*(y-Ymin)))
2 CONTINUE
idummy=SetFont(FontXY)
idummy=GetFontInfo(FInf)
idummy=SetColor(TextColor)
CALL MoveTo(X0-GetGTextExtent('y=sin2(2x)+√x⋅cos2(x/2)')/2+Lx/2,
* Y0-Ly-FInf.PixHeight-TicSize,xy)
CALL OutGText('y=sin2(2x)+√x⋅cos2(x/2)')
PAUSE 'Press ENTER for exit'
idummy=SetVideoMode($DEFAULTMODE)
END

Задача 7. Кубическое уравнение


Найти все корни (в том числе и комплексные) уравнения x3+ax2+bx+c=
=0, где a, b, c - заданные вещественные коэффициенты. Использовать
решение Кардано.

{ Решение задачи 7 на языке PASCAL }


TYPE Real = Extended;
Complex = RECORD Re,Im : Real; END;
Troot = ARRAY[1..3] OF Complex;
PROCEDURE ComplexRoot2(x:Real; VAR x1_2:Complex);
BEGIN WITH x1_2 DO
IF x>=0 THEN BEGIN Re:=Sqrt(x); Im:=0; END
ELSE BEGIN Re:=0; Im:=Sqrt(Abs(x)); END;
END;
PROCEDURE ComplexAdd(x1,x2:Complex; VAR y:Complex);
BEGIN WITH y DO BEGIN Re:=x1.Re+x2.Re; Im:=x1.Im+x2.Im; END;
END;
PROCEDURE ComplexRoot3(x:Complex; VAR x1_3:Complex);
VAR z,f : Real;
BEGIN
WITH x DO BEGIN
z:=Sqrt(Sqr(Re)+Sqr(Im));
IF Re=0 THEN BEGIN IF Im>=0 THEN f:=0.5*Pi ELSE f:=1.5*Pi END
ELSE BEGIN f:=Arctan(Im/Re);
IF Re<0 THEN f:=Pi+f ELSE IF Im<0 THEN f:=2*Pi+f;
END;

89
END;
IF z>0 THEN z:=Exp(Ln(z)/3);
WITH x1_3 DO BEGIN Re:=z*Cos(f/3); x1_3.Im:=z*Sin(f/3); END;
END;
PROCEDURE ComplexMult(x1,x2:Complex; VAR y:Complex);
BEGIN
WITH y DO BEGIN
Re:=x1.Re*x2.Re-x1.Im*x2.Im; Im:=x1.Re*x2.Im+x1.Im*x2.Re; END;
END;
PROCEDURE PrintComplex(x:Complex);
BEGIN Write(x.Re:10:5);
IF x.Im>=0 THEN Write('+',x.Im:8:5) ELSE Write(x.Im:9:5); Write('i');
END;
FUNCTION Min3(x1,x2,x3:Real):Byte;
BEGIN IF x1<x2 THEN IF x1<x3 THEN Min3:=1 ELSE Min3:=3
ELSE IF x2<x3 THEN Min3:=2 ELSE Min3:=3;
END;
VAR a,b,c,p,q,QQ : Real;
AA,BB,QQ1_2,Tmp,Tmp1,Tmp2,d,One_1_3_p,One_1_3_m : Complex;
y,x : TRoot;
i,j : Byte;
BEGIN
WITH One_1_3_p DO BEGIN Re:=-0.5; Im:= Sqrt(3)/2; END;
WITH One_1_3_m DO BEGIN Re:=-0.5; Im:=-Sqrt(3)/2; END;
Write('Введите коэффициенты a,b,c '); ReadLn(a,b,c);
p:=-Sqr(a)/3+b; q:=2*Sqr(a/3)*(a/3)-a*b/3+c;
QQ:=Sqr(p/3)*(p/3)+Sqr(q/2); ComplexRoot2(QQ,QQ1_2);
WITH AA DO BEGIN Re:=QQ1_2.Re-q/2; Im:=QQ1_2.Im; END;
WITH BB DO BEGIN Re:=-QQ1_2.Re-q/2; Im:=-QQ1_2.Im; END;
ComplexRoot3(AA,AA); ComplexRoot3(BB,BB);
ComplexMult(AA,BB,Tmp);
CASE Min3(Abs(p/3+Tmp.Re),Abs(p/3-Tmp.Re/2-Sqrt(3)*Tmp.Im/2),
Abs(p/3-Tmp.Re/2+Sqrt(3)*Tmp.Im/2)) OF
2:ComplexMult(AA,One_1_3_p,AA);
3:ComplexMult(AA,One_1_3_m,AA);
END;
ComplexAdd(AA,BB,y[1]);
With Tmp1 DO BEGIN
Re:=(AA.Re+BB.Re)/2; Im:=(AA.Im+BB.Im)/2; END;
With Tmp2 DO BEGIN Re:=AA.Re-BB.Re; Im:=AA.Im-BB.Im; END;
With Tmp DO BEGIN Re:=0; Im:=Sqrt(3)/2; END;
ComplexMult(Tmp,Tmp2,Tmp2);
With y[2] DO BEGIN
Re:=-Tmp1.Re+Tmp2.Re; Im:=-Tmp1.Im+Tmp2.Im; END;

90
With y[3] DO BEGIN
Re:=-Tmp1.Re-Tmp2.Re; Im:=-Tmp1.Im-Tmp2.Im; END;
WriteLn(' Корни уравнения и контроль :');
FOR i:=1 TO 3 DO BEGIN
x[i].Re:=y[i].Re-a/3; x[i].Im:=y[i].Im;
ComplexMult(x[i],x[i],Tmp1); ComplexMult(x[i],Tmp1,Tmp2);
d.Re:=Tmp2.Re+a*Tmp1.Re+b*x[i].Re+c;
d.Im:=Tmp2.Im+a*Tmp1.Im+b*x[i].Im;
PrintComplex(x[i]); Write(' ----> '); PrintComplex(d);WriteLn(' = 0');
END;
WriteLn('Нажмите Enter'); ReadLn;
END.

/*Решение задачи 7 на языке С*/


#include<math.h>
#include<stdio.h>
#include<conio.h>
struct s_complex{double Re,Im;};
typedef struct s_complex complex;
complex ComplexRoot2(double x) { complex x_1_2;
if(x>=0){x_1_2.Re=sqrt(x); x_1_2.Im=0;}
else{x_1_2.Re=0; x_1_2.Im=sqrt(fabs(x));}return x_1_2;}
complex ComplexAdd(complex x1,complex x2) {complex y;
y.Re=x1.Re+x2.Re; y.Im=x1.Im+x2.Im; return y;}
complex ComplexRoot3(complex x){complex x1_3; double z,f;
z=sqrt(pow(x.Re,2)+pow(x.Im,2)); f=atan2(x.Im,x.Re); if(z) z=pow(z,1./3);
x1_3.Re=z*cos(f/3); x1_3.Im=z*sin(f/3); return x1_3;}
complex ComplexMult(complex x1,complex x2){complex y;
y.Re=x1.Re*x2.Re-x1.Im*x2.Im; y.Im=x1.Re*x2.Im+x1.Im*x2.Re;
return y;}
int Min3(double x1,double x2,double x3)
{if(x1<x2)if(x1<x3)return 1;else return 3;
else if(x2<x3)return 2;else return 3;}
void PrintComplex(complex x){ printf("%10.5f",x.Re);
if(x.Im>0)printf("+%7.5fi",x.Im);else printf("%8.5fi",x.Im);}
void main(void) { double a,b,c,p,q,Q; int i,j; complex A,B,Q1_2,Tmp,Tmp1,
Tmp2,y[3],x[3],d,One_1_3_p={-.5,.5},One_1_3_m={-.5,-.5};
One_1_3_p.Im*=sqrt(3); One_1_3_m.Im*=sqrt(3);
printf("\nВведите коэффициенты a,b,c "); scanf("%lg%lg%lg",&a,&b,&c);
p=-a*a/3+b; q=2*pow(a/3,3)-a*b/3+c; Q=pow(p/3,3)+pow(q/2,2);
Q1_2=ComplexRoot2(Q); A.Re=Q1_2.Re-q/2; A.Im=Q1_2.Im;
B.Re=-Q1_2.Re-q/2; B.Im=-Q1_2.Im; A=ComplexRoot3(A);
B=ComplexRoot3(B); Tmp=ComplexMult(A,B);
switch(Min3(fabs(p/3+Tmp.Re), fabs(p/3-Tmp.Re/2-sqrt(3)*Tmp.Im/2),

91
fabs(p/3-Tmp.Re/2+sqrt(3)*Tmp.Im/2))){
case 2:A=ComplexMult(A,One_1_3_p); break;
case 3:A=ComplexMult(A,One_1_3_m);}
y[0]=ComplexAdd(A,B);
Tmp1.Re=(A.Re+B.Re)/2; Tmp1.Im=(A.Im+B.Im)/2;
Tmp2.Re=A.Re-B.Re; Tmp2.Im=A.Im-B.Im;
Tmp.Re=0; Tmp.Im=sqrt(3)/2;
Tmp2=ComplexMult(Tmp,Tmp2);
y[1].Re=-Tmp1.Re+Tmp2.Re; y[1].Im=-Tmp1.Im+Tmp2.Im;
y[2].Re=-Tmp1.Re-Tmp2.Re; y[2].Im=-Tmp1.Im-Tmp2.Im;
printf("\nКорни уравнения и контроль :\n");
for(i=0;i<3;i++){x[i].Re=y[i].Re-a/3; x[i].Im=y[i].Im;
Tmp1=ComplexMult(x[i],x[i]); Tmp2=ComplexMult(x[i],Tmp1);
d.Re=Tmp2.Re+a*Tmp1.Re+b*x[i].Re+c;
d.Im=Tmp2.Im+a*Tmp1.Im+b*x[i].Im;
PrintComplex(x[i]);printf(" ----> ");PrintComplex(d);printf(" == 0\n");}
printf("\nНажмите любую клавишу\n"); getch();}

// Решение задачи 7 на языке C++


#include<stdio.h>
#include<iostream.h>
#include<iomanip.h>
#include<conio.h>
#include<math.h>
#include<complex.h>
void main(void){
double a,b,c,p,d1,d2,d3; int i,j;
complex q,Q,tmp,A,B,y[3],x[3],
One_1_3_p=complex(-.5,.5*sqrt(3)),One_1_3_m=complex(-.5,-.5*sqrt(3));
cout<<setfill(' ' )<<setprecision(5)<<
setiosflags(ios::showpoint | ios::fixed | ios::right);
cout<<"Введите коэффициенты a,b,c "; cin>>a>>b>>c;
p=-a*a/3+b; q=2*pow(a/3,3)-a*b/3+c; Q=pow(p/3,3)+pow(q/2,2);
A=pow(-q/2+sqrt(Q),complex(1./3,0));
B=pow(-q/2-sqrt(Q),complex(1./3,0));
tmp=A*B; d1=fabs(p/3+real(tmp));
d2=fabs(p/3-real(tmp)/2-sqrt(3)*imag(tmp)/2);
d3=fabs(p/3-real(tmp)/2+sqrt(3)*imag(tmp)/2);
if(d2<d3){if(d2<d1)A*=One_1_3_p;}else if(d3<d1)A*=One_1_3_m;
y[0]=A+B; y[1]=-(A+B)/2+complex(0,sqrt(3)/2)*(A-B);
y[2]=-(A+B)/2-complex(0,sqrt(3)/2)*(A-B);
for(i=0;i<3;i++)x[i]=y[i]-a/3;
cout <<"\nКорни уравнения и контроль:\n";
for(i=0;i<3;i++){ tmp=pow(x[i],3)+a*x[i]*x[i]+b*x[i]+c;

92
cout<<setw(10)<<real(x[i])<<setw(10)<<imag(x[i])<<"i ---->"<<setw(10)
<<real(tmp)<<setw(10)<<imag(tmp)<<"i = 0"<<endl;}
cout <<"\nНажмите любую клавишу\n"; getch();}

C Решение задачи 7 на языке FORTRAN


DOUBLE PRECISION a,b,c,p,q,QQ,d1,d2,d3
COMPLEX*16 tmp,AA,BB,y(3),x(3),One_1_3_p,One_1_3_m
PRINT*,'Введите коэффициенты a,b,c '
READ*,a,b,c
One_1_3_p=(-.5,0)+(0,.5)*DSQRT(3)
One_1_3_m=CONJG(One_1_3_p)
p=-a**2/3+b
q=2*(a/3)**3-a*b/3+c
QQ=(p/3)**3+(q/2)**2
AA=(-q/2+CDSQRT(QQ))**(1./3)
BB=(-q/2-CDSQRT(QQ))**(1./3)
tmp=AA*BB
d1=DABS(p/3+DREAL(tmp))
d2=DABS(p/3-DREAL(tmp)/2-DSQRT(3)*DIMAG(tmp)/2)
d3=DABS(p/3-DREAL(tmp)/2+DSQRT(3)*DIMAG(tmp)/2)
IF(d2.EQ.MIN(d1,d2,d3)) AA=AA*One_1_3_p
IF(d3.EQ.MIN(d1,d2,d3)) AA=AA*One_1_3_m
y(1)=AA+BB
y(2)=-(AA+BB)/2+(0,.5)*DSQRT(3)*(AA-BB)
y(3)=-(AA+BB)/2-(0,.5)*DSQRT(3)*(AA-BB)
x=y-a/3
y=x**3+a*x**2+b*x+c
PRINT*,'Корни уравнения и контроль'
PRINT 999,(x(i),y(i),i=1,3)
999 FORMAT(1X,SP,2G12.6,'i ---->',2G12.6,'i = 0')
PAUSE 'Нажмите ENTER'
END

Задача 8. Собственное число


Найти наибольшее собственное число заданной квадратной матрицы.

{ Решение задачи 8 на языке PASCAL }


CONST N=3;
TYPE TVector=ARRAY[1..N] OF Real;
TMatrix=ARRAY[1..N] OF TVector;
CONST a : TMatrix = ((11,-6,2),(-6,10,-4),(2,-4,6));
x : TVector = (1,1,1);
Error = 1E-10;

93
Limit = 100;
VAR Iteration : Word;
L,L0,s : Real;
i,j : Byte;
y : TVector;
BEGIN Iteration:=0; L:=0;
REPEAT
Inc(Iteration); L0:=L; L:=0;
FOR i:= 1 TO N DO BEGIN
s:=0;
FOR j:=1 TO N DO s:=s+a[i,j]*x[j];
IF Abs(s)>Abs(L) THEN L:=s; y[i]:=s;
END;
FOR i:=1 TO N DO x[i]:=y[i]/L;
UNTIL (Iteration=Limit)OR(Abs(1-L0/L)<Error);
WriteLn('Наибольшее собственное значение матрицы=',L,#10,#13,
'Достигнута относительная погрешность ',Abs(1-L0/L):7,
' за ',Iteration:3,' итераций',#10,#13,
'Соответствующий собственный вектор :');
FOR i:=1 TO N DO WriteLn(' x[',i:2,']=',x[i]);
END.

// Решение задачи 8 на языке C


#include<stdio.h>
#include<math.h>
#define N 3
void main(void){
double a[][N]={11,-6,2,-6,10,-4,2,-4,6}, x[]={1,1,1},
Error=1E-10,L=0,L0,s,y[N];
int Limit=100,Iteration=0,i,j;
do{
for(L0=L,L=0,i=0;i<N;i++)
for(s=0,j=0;j<N;j++){s+=a[i][j]*x[j];if(fabs(s)>fabs(L))L=s;y[i]=s;}
for(i=0;i<N;i++)x[i]=y[i]/L;}
while(++Iteration<Limit&&fabs(1-L0/L)>=Error);
printf("\nНаибольшее собственное значение матрицы=%g\n"
"Достигнута относительная погрешность %g за %d итераций\n"
"Соответствующий собственный вектор :\n",L,fabs(1-
L0/L),Iteration);
for(i=0;i<N;i++)printf("\tx[%2d]=%g\n",i+1,x[i]);}

C Решение задачи 8 на языке FORTRAN


PARAMETER(N=3)
REAL*8 a(N,N)/11,-6,2,-6,10,-4,2,-4,6/,x(N)/1,1,1/,Error/1E-10/,

94
& L/0/,L0,s,y(N)
INTEGER Limit/100/
Iteration=0
1 Iteration=Iteration+1
L0=L
L=0
DO 2 i=1,N
s=0
DO 3 j=1,N
3 s=s+a(i,j)*x(j)
IF(ABS(s).GT.ABS(L))L=s
2 y(i)=s
x=y/L
IF(Iteration.LT.Limit.AND.ABS(1-L0/L).GE.Error)GOTO 1
PRINT 998,L,ABS(1-L0/L),Iteration
998 0FORMAT(' Наибольшее собственное значение матрицы='G8.2/
1 ' Достигнута относительная погрешность 'G7.1' за 'I3
2 ' итераций'/' Соответствующий собственный вектор :')
PRINT 999,(i,x(i),i=1,N)
999 FORMAT(' x['I2']='G10.2)
END

Комментарии к задачам

Задача 1. Вывод натурального числа


Вывод числа в десятичном виде очевиден. При выводе числа в двоич-
ном виде используются битовые операции AND,ShL,ShR (Pascal), &,<<,>>
(C), или битовые функции IAND,ISHL (FORTRAN). Очевидно, что число
содержит не более 20 значащих двоичных цифр. Восьмеричное представ-
ление числа получено в Pascal-программе и Fortran-программе точно так
же, а в языке C есть специальный восьмеричный формат. Шестнадцате-
ричное представление числа получено в C-программе и FORTRAN-про-
грамме средствами формата, а на языке Pascal для этого пришлось записать
специальный алгоритм.

Задача 2. Сумма ряда


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

Задача 3. Медленная сортировка

95
Алгоритм сортировки очевиден. Программы иллюстрируют обработку
одномерных массивов.
Задача 4. Быстрая сортировка
Алгоритм сортировки бинарными вставками состоит в следующем:
первый элемент массива есть упорядоченный массив длиной 1. Возьмем
первый элемент неупорядоченного массива (второй элемент исходного
массива) и вставим его в упорядоченный массив, не нарушая упорядочен-
ности. Получим упорядоченный массив длиной 2 и неупорядоченный мас-
сив длиной n-2. Будем повторять эту операцию до тех пор, пока длина не-
упорядоченного массива не станет равной нулю. Место вставки нового
элемента в упорядоченный массив ищется методом бисекции, это позволя-
ет понизить сложность всего алгоритма до n⋅ln(n). Программы иллюстри-
руют использование подпрограмм и встроенного генератора случайных
чисел.

Задача 5. Слова
Программы иллюстрируют способы обработки текстовых файлов и ра-
боту с символьными строками. В этих программах заметны отличия в
представлении символьных данных в языках Pascal, C и Fortran. В Fortran-
программе пришлось записать функцию Length для вычисления фактиче-
ской длины строки, так ка язык не предоставляет таких средств. Обратите
внимание, что остаток строки может быть заполнен пробелами или нуль-
символами. Pascal-программа очень продуктивно использует возможность
описания массива с символьными индексами и переменные типа “множе-
ство”.

Задача 6. График
Pascal-программа и C-программа практически идентичны, так как ис-
пользуют один и тот же графический интерфейс, графические средства
языка Fortran существенно отличаются. В C-программе использовано мак-
роопределение F(x), а в Fortran-программе - оператор-функция F(x). Все
три программы предполагают, что в текущем каталоге есть все необходи-
мые файлы поддержки (графический драйвер для Pascal-программы и C-
программы и шрифтовые файлы для всех трех программ).

Задача 7. Кубическое уравнение


Программы реализуют решение Кардано кубического уравнения с ве-
щественными коэффициентами (см.: Корн Г., Корн Т. Справочник по ма-
тематике для научных работников и инженеров. М.: Наука, 1970. С.43). В

96
виде исключения приведено четыре решения - на языках Pascal, C, Fortran
и C++. Языки Fortran и C++ поддерживают комплексный тип данных, по-
этому алгоритм записывается в этих двух программах просто и естествен-
но. В Pascal-программе и C-программе пришлось моделировать комплекс-
ные числа записями (структурами) и написать несколько функций для об-
работки таких данных.

Задача 8. Собственное число


Использованный в программах итерационный метод вычисления наи-
большего собственного числа L и соответствующего собственного вектора
X квадратной матрицы А заключается в следующем : задается некоторый
достаточно произвольный вектор X0. Вычисляется вектор AX0 и его норма
(максимум из абсолютных величин компонентов), которая дает первое
приближение L1 для собственного числа, а первое приближение для собст-
венного вектора X1 получается нормировкой вектора AX0. Вторая и все ос-
тальные итерации выполняются аналогично. Итерационный процесс за-
канчивается, когда либо относительная погрешность собственного числа
станет меньше заданной предельно допустимой ошибки, либо количество
итераций достигнет заданного предельного значения.

97