СРАВНИТЕЛЬНЫЙ КУРС
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
FORTRAN И С
Калининград
1998
3
КАЛИНИНГРАДСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
СРАВНИТЕЛЬНЫЙ КУРС
ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
FORTRAN И С
Учебное пособие
Калининград
1998
4
УДК 681.3. 06
5
Сергей Анатольевич Григорьев,
Сергей Александрович Ишанов
Учебное пособие
СОДЕРЖАНИЕ
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
8
ВВЕДЕНИЕ
Эта книга состоит из трех глав. Первые две представляют собой крат-
кие описания языков программирования C и FORTRAN 77, а в третьей гла-
ве приведены решения некоторых часто встречающихся задач на этих двух
языках и на языке PASCAL. Авторы предполагают, что читатели этой кни-
ги уже в достаточной мере знакомы с языком программирования PASCAL,
и в ряде мест ссылаются на соответствующие конструкции этого языка. Но
владение языком PASCAL не является абсолютно необходимым для поль-
зования этой книгой, читатель должен иметь лишь некоторое представле-
ние о языках программирования высокого уровня, поскольку из-за ограни-
ченного объема книги авторы вынуждены излагать материал очень лако-
нично. Тем, кого интересует только один из представленных языков, мож-
но порекомендовать читать только первую или только вторую главу.
9
вращать никакого значения, то ее тип - void ("пустой" тип). В языке С нет
понятия процедуры.
Каждый оператор заканчивается символом ; .
Идентификаторы могут иметь длину до 31 символа, компилятор разли-
чает большие и малые латинские буквы, откуда, в частности, следует, что
написание Main или MAIN имени главной функции является ошибкой.
Комментарии заключаются в составные символы /* */ . В языке С++
появился еще один вид комментариев: так называемый однострочный
комментарий - строка, начинающаяся парой символов // .
Директивы препроцессора начинаются с символа # и записываются в
отдельной строке, они могут располагаться в любом месте программы, но
не внутри функции. Директивы действуют от места появления до конца
файла или до их отмены. Препроцессор языка С - это программа, которая,
обрабатывая исходный текст до компилятора, выполняет подстановки для
макровызовов, осуществляет условную компиляцию, подключает необхо-
димые файлы. Директива #include<файл> или #include"файл" присоеди-
няет к программе внешний файл, "" означают поиск файла в текущем ка-
талоге (пользовательский файл), а <> - поиск в системном каталоге (сис-
темный файл). Директиву #include следует использовать в любой про-
грамме, вызывающей внешние функции, для подключения т.н. заголовоч-
ных файлов - файлов с расширением h (header). В заголовочных файлах
содержатся описания (прототипы) функций, необходимые для успешной
компиляции программы.
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 всегда соединяет
строковые константы, не разделенные в тексте программы ничем, кроме
пробелов.
11
Простейшая форма директивы препроцессора #define используется для
определения символических констант:
#define имя строка_замещения
Строка замещения, вообще говоря, есть любая последовательность
символов. Препроцессор до начала компиляции замещает каждое вхожде-
ние имени константы в тексте программы на строку замещения, но только
не внутри "". Строка замещения может быть, например, числовой кон-
стантой: #define PI 3.141592653589793 , тогда идентификатор PI можно
рассматривать как именованную константу в языке PASCAL (хотя это и не
тождественные вещи). У пользователей языка С существует традиция за-
писывать имена символических констант заглавными буквами, хотя это
никак не регламентируется в самом языке.
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.6. Ввод-вывод
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. При втором способе форматиро-
вания употребляется специальный вид функций, называемых манипулято-
рами.
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. Массивы
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. Функции
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
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), она лишь сообщает новое имя уже существующего типа.
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. Макроопределения
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. Внешние файлы
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.
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);
В арифметических выражениях элементы битовых полей рассматрива-
ются как целые числа.
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. Графика
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
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) .
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 или операторах описания.
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.
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. Ввод-вывод
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
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, круглые
скобки и апострофы обязательны.
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. После выполнения этих
операций выполнение программы продолжается обычным образом.
55
Во всех случаях можно последним оператором цикла записывать оператор
CONTINUE. Допускаются вложенные циклы, они могут заканчиваться
одним и тем же оператором, т.е. иметь одну и ту же метку цикла. Запреща-
ется изменять значение переменной цикла в теле цикла, такая попытка рас-
сматривается как синтаксическая ошибка.
Оператор цикла DO WHILE полностью аналогичен по своему дейст-
вию оператору While в языке PASCAL :
DO [метка] WHILE(логическое выражение)
Такой цикл может заканчиваться либо меткой, либо оператором END DO.
Для выхода из любого типа цикла служит оператор EXIT, выполняющий
те же действия, что оператор break в языках C и PASCAL.
Оператор CYCLE завершает текущий шаг цикла и передает управле-
ние оператору DO или DO WHILE, он аналогичен оператору continue в
языках C и PASCAL.
2.10. Массивы
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 должна находиться в том же файле, что и
вызывающая программная единица.
60
BLOCK DATA [имя]
описания общих областей
операторы DATA
END
К подпрограммам BLOCK DATA не нужно специально обращаться, они
выполняются автоматически. В программе может быть сколько угодно
подпрограмм BLOCK DATA, но только одна неименованная.
Оператор EQUIVALENCE позволяет совмещать по памяти различные
переменные и массивы, в том числе и данные разных типов. Оператор за-
писывается в виде:
EQUIVALENCE (список объектов)[,...]
Здесь список объектов - это разделенные запятыми имена переменных,
имена массивов и элементы массивов. Количество совмещаемых объектов
не ограничено. Существует несколько ограничений на использование опе-
ратора EQUIVALENCE :
1) совмещения не должны противоречить друг другу;
2) совмещаемые переменные и массивы нельзя инициализировать в
операторе описания типа, но только в операторе DATA;
3) нельзя совмещать два объекта, если оба они входят в общие области;
4) не рекомендуется совмещать символьные и несимвольные объекты.
61
символьные значения следует задавать в апострофах. При форматном вво-
де-выводе используется формат A.
Существует только одна символьная операция - // - конкатенация или
сцепление строк. Кроме того, к символьным данным применимы операции
сравнения .EQ., .NE., .LT., .LE., .GT., .GE.
Имеется возможность непосредственного обращения к любой подстро-
ке символьной переменной, для этого используется псевдопеременная
“подстрока”:
имя строки([начало]:[конец])
где начало и конец - целочисленные выражения, задающие номера первого
и последнего символов, образующих подстроку. Если не задано начало, то
подстрока начинается с первого символа строки, если не задан конец, то
подстрока включает весь остаток строки ( но символ : обязательно должен
быть записан). Точно так же можно обратиться к подстроке элемента сим-
вольного массива. Подстрока может не только быть операндом выражения,
но также стоять в левой части оператора присваивания, вводиться операто-
ром READ и инициализироваться в операторе DATA, т.е. подстрока может
использоваться точно так же, как и настоящая символьная переменная.
Для обработки символьных данных в языке FORTRAN есть две встро-
енные функции:
LEN(character) INTEGER - возвращает длину
строки, но, поскольку строки в языке
FORTRAN имеют фиксированную дли-
ну, возвращаемое значение всегда есть
максимальная длина строки или длина
строки по описанию.
INDEX(character,character) INTEGER - возвращает номер символа
в первом аргументе, начиная с которого
второй аргумент входит в первый или
0.
Параметрами подпрограмм могут быть символьные переменные или
символьные массивы. Они должны быть соответствующим образом описа-
ны в процедуре. Длину строки можно задавать константой, переменной
или использовать описатель неопределенной длины (*). Отметим, что
именно для таким образом описанных строк-параметров и используется
функция LEN.
62
Операторная функция записывается в виде:
имя(список параметров)=выражение
Она аналогична по своему назначению макроопределению в языке C. Опе-
раторную функцию можно рассматривать как подпрограмму-функцию, со-
стоящую из одного оператора. Тип значения, вычисляемого операторной
функцией, можно определить в операторе описания, сама операторная
функция должна размещаться после операторов описания и перед выпол-
няемыми операторами. Типы параметров операторной функции никак не
определяются, что придает этому средству дополнительную гибкость.
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. Структуры
66
RECORD/имя типа/ имя переменной[описатель измерений]...
Допустимы вложенные структуры и массивы структур, структуры мо-
гут быть параметрами подпрограмм. Элемент структуры записывается в
виде:
имя структуры.имя элемента
точно так же, как в языках PASCAL и C. Целые структуры нельзя вводить
или выводить, подобно массивам; для них не работает оператор DATA.
2.18. Графика
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 - номер шрифта.
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
74
for(shift=19;shift>=0;shift--)printf("%ld",(n&(1L<<shift))>>shift);
printf("\nЧисло в 8-ричном виде : %lo",n);
printf("\nЧисло в 16-ричном виде : %lX",n); }
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));}
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.
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
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.
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 и найти самое длинное из них.
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.
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);}
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.
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.
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();}
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
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.
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();}
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();}
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.
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
Комментарии к задачам
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-
программы и шрифтовые файлы для всех трех программ).
96
виде исключения приведено четыре решения - на языках Pascal, C, Fortran
и C++. Языки Fortran и C++ поддерживают комплексный тип данных, по-
этому алгоритм записывается в этих двух программах просто и естествен-
но. В Pascal-программе и C-программе пришлось моделировать комплекс-
ные числа записями (структурами) и написать несколько функций для об-
работки таких данных.
97