Каждый параметр, описанный в списке формальных параметров, рассматривается в процедуре или функции как некая внутренняя переменная с заданным начальным значением.
Описываются формальные параметры следующим образом:
( <тип параметра> <список параметров> : <тип параметров> )
Тип параметра определяется с помощью ключевых слов const, var или вообще не указывается. Подробнее данный пункт будет рассмотрен позже.
Список параметров представляет собой обычный список имен переменных (формальных параметров), записанных через запятую (аналогично разделу описания переменных var).
Если имеются параметры разных типов, то представленная конструкция записывается несколько раз через точку с запятой.
Пример: возможные списки формальных параметров
procedure primer1 ( x: integer );
procedure primer2 ( var x, y, z: integer );
procedure primer3 ( var a: real; const b, testconst: integer; c: char );
function primer4 ( formal: boolean ): integer;
Следует отметить, что тип параметра не может быть составным. Если же в качестве типа параметра должен быть все-таки, например, массив, то для него следует предварительно создать запись в разделе type, а затем использовать заданное имя.
Пример: типы параметров
неправильно:
procedure primer1 ( vector: array [1..10] of integer );
правильно:
type TVector: array [1..10] of integer;
procedure primer1 ( vector:TVector );
При вызове подпрограмм с параметрами в нее передаются фактические параметры или аргументы, записанные в круглых скобках после имени программы через запятую. Аргументами могут быть переменные, константы и выражения.
Пример: вызов подпрограмм с параметрами
primer;
primer2 ( a, b, c );
primer3 ( x, b, test, ‘q’ );
n := primer4 ( true );
Количество и типы передаваемых в подпрограмму фактических параметров должны соответствовать количеству и типам указанных при её описании формальных параметров. Другими словами, если функция ожидает получить на входе два целых числа, то при ее вызове в нее и должны быть переданы два целых числа.
Далее рассмотрим существующие типы параметров.
1. Группа параметров без предшествующего ключевого слова является списком параметров-значений.
Пример: параметр-значение
procedure primer ( x: integer );
2. Группа параметров, перед которыми следует ключевое слово const и за которыми следует тип, является списком параметров-констант.
Пример: параметры-константы
procedure primer ( const x, y: integer );
3. Группа параметров, перед которыми стоит ключевое слово var и за которыми следует тип, является списком типизированных параметров-переменных.
Пример: типизированный параметр-переменная
procedure primer ( var x: integer );
4. Группа параметров, перед которыми стоит ключевое слово var или const, за которыми не следует тип, является списком нетипизированных параметров.
Пример: нетипизированный параметр-переменная
procedure primer ( var x );
Рассмотрим разницу между этими видами параметров.
Формальный параметр-значение обрабатывается, как локальная (внутренняя) по отношению к процедуре или функции переменная, за исключением того, что получает свое начальное значение из соответствующего фактического параметра при вызове процедуры или функции. Изменения, которые претерпевает формальный параметр-значение, не влияют на значение фактического параметра.
Пример: параметр-значение
procedure primer ( x: integer );
begin
x:=5;
end;
var k: integer;
begin
k:=10;
primer ( k );
writeln ( k ); { на экране напечатается 10 }
end.
В качестве аргумента параметра-значения могут выступать как переменные, так и константы и выражения.
Пример: параметр-значение
procedure primer ( x: integer );
…
primer ( 32+23 );
Формальные параметры-константы работают аналогично локальной (внутренней) константе, которая получает свое значение при вызове подпрограммы от соответствующего фактического параметра. Присваивания формальному параметру-константе не допускаются. Формальный параметр-константа также не может передаваться в качестве фактического параметра другой подпрограмме.
Пример: параметр-константа
procedure primer ( const x: integer );
begin
x:=5; – недопустимо!!!
end;
Параметр-переменная используется, когда значение должно передаваться из подпрограммы вызывающей программе. При вызове подпрограммы формальный параметр-переменная замещается фактической переменной, а следовательно, любые изменения в значении формального параметра-переменной отражаются и на фактическом параметре. Другими словами, любое обращение к формальному параметру-переменной приводит к доступу к самому фактическому параметру.
Пример: рассмотрим еще раз представленный выше пример, но уже для параметра-переменной
procedure primer ( var x: integer );
begin
x:=5;
end;
var k: integer;
begin
k:=10;
primer ( k );
writeln ( k ); { на экране напечатается 5 }
end.
Соответствующий фактический параметр в операторе вызова подпрограммы должен быть переменной (использование констант и выражений недопустимо).
В первых двух случаях (параметры-значения и параметры-константы) говорят, что фактические параметры передаются в процедуру по значению (т. е. их значение в вызывающей процедуре не изменяется), в третьем (параметр-переменная) – по адресу (т. е. при изменении значений внутри процедуры их значение изменяется и в вызывающей процедуре).
Когда формальный параметр является нетипизированным параметром, то соответствующий фактический параметр может представлять собой любую переменную или константу, независимо от ее типа. Нетипизированный параметр, описанный с ключевым словом var, может модифицироваться, а нетипизированный параметр, описанный с ключевым словом const, доступен только для чтения.
Внутри подпрограммы над нетепизированным параметром может быть осуществлено явное преобразование типа: параметр записывается в скобках, перед которым указывается имя типа данных, к которому осуществляется преобразование.
Пример: нетипизированный параметр – явное преобразование типа
procedure test ( var x );
type
TArr = array[1..10] of byte;
var
y: integer;
z: real;
m: arr;
begin
...
y:= integer(x); { преобразование к типу integer }
z:= real(x); { преобразование к типу real }
m:= arr(x); { преобразование к массиву }
...
end;
Другой способ работы с нетипизированной переменной – описать локальную переменную, которая физически будет совпадать с передаваемой в подпрограмму фактической переменной.
Глобальные и локальные объекты
Константы, типы данных и переменные, объявленные в начале программы до описания любых процедур и функций, называют глобальными. Глобальные элементы существуют в памяти на протяжении всей работы программы и доступны как в самой программе, так и внутри всех ее подпрограмм.
Внутри подпрограммы могут быть описаны свои собственные константы, типы данных и переменные. Такие элементы называют локальными. Локальные элементы могут использоваться только в той подпрограмме, в которой они определены, а также в процедурах и функциях, которые определены внутри нее. Они не существуют до тех пор, пока подпрограмма, в которой они описаны, не вызвана, а также после завершения ее работы.
Пример: области видимости переменных t, x и y.
program primer;
var
t: boolean;
procedure A
var
x: integer;
procedure B;
var
y: real;
begin
{ здесь доступны переменные t, x и y }
end;
begin
{ здесь доступны переменные t и x }
end;
procedure C;
begin
{ здесь доступна только переменная t }
end;
begin
{ здесь доступна только переменная t }
end.
Значение глобальной переменной может быть изменено внутри любой из подпрограмм. Такой эффект иногда называют «побочным», и его использование является нежелательным, т. к. может привести к появлению непонятных ошибок в программе. Чтобы избежать такого эффекта, необходимо следить, чтобы внутри подпрограмм изменялись значения только локальных переменных и формальных параметров.
Пример: глобальная и локальная переменная
program primer;
var
global: integer;
procedure A;
var
local: integer;
begin
local:=5; { допустимо }
global:=10; { допустимо, но не желательно }
end;
begin
global:=7; { допустимо }
{ local:=3; - нельзя, т. к. это локальная переменная }
end.
Переменная global видна во всей программе, а local – только в процедуре A.
Допустима ситуация, когда имена локальных и глобальных переменных совпадают. В этом случае внутри подпрограммы локальная переменная «перекрывает» глобальную. К глобальной же переменной можно обратиться, приписав перед ее именем название программы:
<имя программы>.<имя переменной>
Пример: совпадение имен глобальной и локальной переменных
program primer;
var
test: real;
procedure AAA;
var
test: integer;
begin
test := primer.test ; { локальной переменной присваивается
значение глобальной }
end;
…
Вопросы для самопроверки
1. Что такое подпрограмма?
2. Чем отличается процедура от функции?
3. Что такое формальные параметры? Фактические параметры?
4. Чем отличаются параметры-переменные от параметров-значений?
5. Sqr – это функция или переменная?
6. Что понимают под глобальными и локальными объектами?
§16. Модули
Нередко разработкой крупных программ занимается не один человек, а целый коллектив программистов. В такой ситуации они не могут одновременно редактировать один и тот же файл. Одним из возможных вариантов решения такой проблемы является разбиение программы на модули, которые хранятся в отдельных файлах и могут быть отредактированы, откомпилированы и протестированы независимо от остальных частей программы.
Понятие модуля
Турбо Паскаль обеспечивает доступ к большому числу встроенных констант, типов данных, переменных, процедур и функций. Их количество велико, однако, в программе они редко используются все сразу. Поэтому они разделены на связанные группы, называемые модулями. Это позволяет использовать только те модули, которые необходимы в программе.
Под модулем понимают кусок программы, компилируемый отдельно от остальных ее частей.
Программный модуль представляет собой набор констант, типов данных, переменных, процедур и функций. Каждый модуль аналогичен отдельной программе на Паскале: он может иметь основное тело, которое вызывается перед запуском программы и осуществляет необходимую инициализацию.
Возможность раздельной компиляции является одним из основных преимуществ модулей. Если все подпрограммы содержатся в одном файле, то исправление единственной ошибки в какой-либо подпрограмме приведет к неизбежной перекомпиляции всего кода, что не всегда удобно.
Стандартные модули в Турбо Паскаль
Турбо Паскаль предоставляет пользователю семь стандартных модулей. Пять из них – System, Graph, DOS, Crt и Printer – обеспечивают поддержку обычных программ на Паскале. Два других – Тurbo3 и Graph3 – предназначены для обеспечения совместимости с программами и файлами данных, созданными под версией 3.0 Турбо Паскаля. Все семь модулей хранятся в файле TURBO. TPL.
Модуль System является основным и не требует специального подключения в программе (его содержимым можно пользоваться по умолчанию). Он содержит в себе следующие типы подпрограмм:
a) подпрограммы для обработки величин порядковых типов данных (dec, inc, odd, pred, succ);
b) арифметические функции;
c) функции преобразования типов данных (chr, ord, round, trunc);
d) процедуры управления процессом выполнения программы (break, continue, exit, halt);
e) подпрограммы обработки строк (concat, copy, delete, insert, length, pos, str, val);
f) подпрограммы файлового ввода и вывода;
g) подпрограммы динамического распределения памяти (dispose, freemem, getmem, new);
h) функции для работы с указателями и адресами (addr);
i) некоторые другие подпрограммы (например: exclude, include, random, randomize, upcase).
Модуль Graph содержит разнообразнейшие подпрограммы, которые позволяют создавать на экране различные рисунки из многоцветных геометрических фигур, а также управляет палитрами, фактурами фона и шрифтами.
Модуль Dos позволяет обмениваться информацией с операционной системой (системное время, прерывания, состояния параметров окружения, процедуры обработки процессов, работа с дисковым пространством).
Модуль Crt служит для организации "хорошего" вывода на экран.
Модуль Printer позволяет производить вывод информации не на консоль, а на принтер (под операционной системой DOS).
Заметим, что для программ на языке Паскаль доступен и ряд других распространенных модулей.
Подключение модулей
Для того чтобы подключить к программе один или несколько модулей, необходимо сразу после заголовка программы поместить следующую строку:
uses <имя_модуля_1>, …, <имя_модуля_N>;
Все константы, типы данных, переменные, процедуры и функции, описанные в модуле, после его подключения к программе становятся доступными ей без дополнительных объявлений.
Например, можно пользоваться функцией abs, не объявляя и не описывая ее, поскольку эта функция включена в состав стандартного модуля System, автоматически подключаемого к любой программе на языке Паскаль. Если же требуется очистить экран монитора перед выдачей результатов, то придется подключить к программе модуль crt и воспользоваться содержащейся в нем процедурой clrscr.
Структура модуля
Модуль обеспечивает набор средств благодаря процедурам и функциям при поддержке констант, типов данных и переменных, однако действительная реализация этих средств скрыта в силу того, что модуль разделен на две секции: интерфейса и реализации. Если программа использует модуль, то все описания модуля становятся доступными этой программе, как если бы они были определены в ней самой.
Структура модуля аналогична структуре программы, однако есть несколько существенных различий. Например, рассмотрим модуль:
unit <идентификатор>;
interface
uses <список модулей>; { Необязательный }
{ открытые описания }
implementation
{ закрытые описания }
{ процедуры и функции }
begin
{ код инициализации }
end.
Заголовок модуля начинается зарезервированным словом unit, за которым следует имя модуля (идентификатор). Отметим, что модуль должен иметь такое же имя, что и файл, в котором он находится.
Интерфейсный раздел – «общедоступная» часть в модуле, следует сразу после заголовка модуля и заканчивается перед зарезервированным словом implementation. Интерфейс определяет, что является "видимым" для любой программы (или модуля), использующей данный модуль. Любая программа, использующая этот модуль, имеет доступ к этим «видимым» элементам.
Программный модуль может использовать другие модули, для этого они определяются в предложении uses. Предложение uses, если имеет место, то следует сразу после ключевого слова interface.
В интерфейсе модуля можно определять константы, типы данных, переменные, процедуры и функции. Как и в программе, они могут быть расположены в любом порядке, и секции могут встречаться повторно (например, type … var … <процедуры> … const … type).
Процедуры и функции, видимые для любой программы, использующей данный модуль, описываются в секции интерфейса, однако их действительные тела – реализации – находятся в секции реализации.
Секция реализации – «приватная» часть – начинается зарезервированным словом implementation. Все, что описано в секции интерфейса, является видимым в секции реализации: константы, типы, переменные, процедуры и функции. Кроме того, в секции реализации могут быть свои дополнительные описания, которые не являются видимыми для программ, использующих этот модуль. Программа не знает о их существовании и не может ссылаться на них или обращаться к ним. Однако эти спрятанные элементы могут использоваться (и, как правило, используются) «видимыми» процедурами и функциями, то есть теми подпрограммами, чьи заголовки указаны в секции интерфейса.
Обычные процедуры и функции, описанные в интерфейсной секции, должны быть повторно указаны в секции реализации. Заголовок procedure/function должен быть или идентичным тому, который указан в секции интерфейса, или иметь более краткую форму (печатается лишь ключевое слово procedure или function, а за ним указывается имя подпрограммы). Затем подпрограмма должна содержать все свои локальные описания (метки, константы, типы, переменные и вложенные процедуры и функции), за которыми должно находиться основное тело самой подпрограммы.
Пример: Пусть в интерфейсной секции указаны следующие описания:
procedure ISwap(var V1,V2 : integer);
function IMax(V1,V2 : integer) : integer;
Секция реализации тогда должна будет содержать реализацию указанных подпрограмм:
procedure ISwap;
var Temp : integer;
begin
Temp := V1;
V1 := V2;
V2 := Temp
end; { конец процедуры Swap }
function IMax(V1,V2 : integer): integer;
begin
if V1 > V2 then IMax := V1 else IMax := V2
end; { конец функции Max }
Подпрограммы, локальные для секции реализации (то есть не описанные в секции интерфейса), должны иметь полный, несокращенный заголовок procedure/function.
Обычно вся секция реализации модуля заключена между зарезервированными словами implementation и end. Однако если перед end поместить зарезервированное слово begin, а между ними – операторы, то получившийся составной оператор, очень похожий на основное тело программы, становится секцией инициализации модуля.
Секция инициализации представляет собой место, где инициализируются структуры данных (переменных), которые использует программный модуль или которые он делает доступными вызывающей программе.
Пример: стандартный модуль Printer использует секцию инициализации для выполнения запросов на открытие (для вывода) текстового файла Lst, который затем можно использовать в операторах Write и Writeln в программе.
При выполнении программы, использующей некоторый модуль, секция инициализации этого модуля вызывается перед запуском основного тела программы. Если программа использует более одного модуля, то секции инициализации всех модулей вызываются перед тем, как выполнить основное тело программы, в порядке, указанном в операторе программы uses.
Модули, которые использует программа, уже оттранслированы и хранятся, как машинный код, а не как исходный код на Паскале. Когда компилятор встречает это предложение uses, он присоединяет машинный код, представленный в секции реализации, к самой программе.
Вопросы для самопроверки
1. Что такое модуль?
2. Чем модуль отличается от процедуры? От программы?
3. Из каких разделов состоит модуль?
4. Какие стандартные модули языка Паскль Вы знаете?
§17. Файлы
Файлы в языке Паскаль обеспечивают возможность работы с внешними устройствами ЭВМ, предназначенными для ввода-вывода и хранения информации.
Понятие файла
Файловый тип данных (файл) определяет упорядоченную совокупность произвольного числа однотипных компонент.
Понятие файла используется достаточно широко. Это может быть как обычный файл на диске, так и коммуникационный порт ЭВМ, устройство печати, клавиатура или другие устройства.
При работе с файлами выполняются операции ввода-вывода. Операция ввода означает чтение данных с внешнего устройства (т. е. из входного файла) в основную память ЭВМ, а операция вывода – запись данных из основной памяти на внешнее устройство (т. е. в выходной файл).
Файлы на внешних устройствах часто называют физическими файлами. Их имена определяются операционной системой. В программах на языке Паскаль имена файлов задаются с помощью строк.
Пример: имя файла на диске может иметь вид:
‘a:\readme. txt’
‘c:\temp\abc. dat’
‘qwerty.doc’
Кроме того, в Паскаль могут использоваться имена устройств и портов, определенные в MS-DOS. Например: 'CON', 'LPT1', 'PRN', 'COM1', 'AUX', 'NUL'.
Для работы с файлами в программе необходимо определить файловую переменную. В Паскале поддерживается три файловых типа: типизированные и нетипизированные и текстовые файлы.
Описание типизированных файлов имеет вид:
var <имя переменной>: file of T;
где T – тип компоненты файла.
Пример: описание типизированной файловой переменной:
type
M = array[1..500] of Integer;
var
f1: file of Real;
f2: file of char;
f3: file of M;
Нетипизированные файлы описываются с помощью служебного слова file:
var f: file;
Описание файловых переменных текстового типа производится с помощью служебного слова text, например:
var <имя переменной>: text;
Процедуры и функции для работы с файлами
Файловые переменные, которые описаны в программе, называют логическими файлами. Все основные процедуры и функции, обеспечивающие ввод-вывод данных, работают только с логическими файлами.
Работу с файлом можно разбить на следующие этапы:
1) связывание логического файла с физическим;
2) открытие файла в нужном режиме;
3) работа с файлом;
4) закрытие файла.
В языке Паскаль имеется ряд процедур и функций, применимых для всех типов файлов (типизированных, нетипизированных, текстовых): Assign, Reset, Rewrite, Close, Rename, Erase, Eof, IOResult. Рассмотрим их подробнее.
Процедура Assign(var f; FileName: String) связывает логический файл f с физическим файлом, полное имя которого задано в строке FileName. При работе с файлами на дисках данная процедура должна быть вызвана раньше, чем будут выполняться какие-либо операции над ними.
Процедура Reset(var f) открывает логический файл f для последующего чтения данных. После успешного выполнения процедуры Reset файл готов к чтению из него первого элемента.
Процедура Rewrite(var f) открывает логический файл f для последующей записи данных. Если файл не существует, то он создается. После успешного выполнения этой процедуры файл готов к записи в него первого элемента. Естественно, что файл нельзя одновременно открыть для чтения и записи.
Процедура Close(var f) закрывает открытый до этого процедурами Reset или Rewrite логический файл f. Вызов процедуры Close необходим при завершении работы с файлом, открытым в режиме для записи.
Логическая функция EOF(var f): Boolean возвращает значение True, когда при чтении достигнут конец файла. Это означает, что уже прочитан последний элемент в файле или файл после открытия оказался пуст.
Пример: проверить, является ли файл c:\test.txt пустым
var f: text;
begin
assign(f, ‘c:\test. txt’);
reset(f);
if eof(f) then writeln(‘файл пуст’)
else writeln(‘файл не пуст’);
end.
Процедура Rename(var f; NewName: String) позволяет переименовать физический файл на диске, связанный с логическим файлом f. Переименование возможно лишь для закрытого файла.
Пример: переименовать файл c:\a.txt в c:\test.txt
var f: file;
begin
assign(f, ‘c:\a. txt’);
rename(f, ‘c:\test. txt’);
end.
Процедура Erase(var f) уничтожает физический файл на диске, который был связан с файловой переменной f. Файл к моменту вызова процедуры Erase должен быть закрыт.
Пример: удалить файл c:\test.txt
var f: file;
begin
assign(f, ‘c:\test. txt’);
erase(f);
end.
Функция IOResult: Integer возвращает целое число, соответствующее коду последней ошибки ввода-вывода. При нормальном завершении операции функция вернет значение 0. Значение функции IOResult необходимо присваивать какой-либо переменной, так как при каждом вызове функция обнуляет свое значение. Функция IOResult работает только при выключенном режиме проверок ошибок ввода-вывода или с ключом компиляции {$I-}.
Пример: проверить, существует ли файл с именем c:\test.txt
var
f: file;
x: integer;
begin
assign(f, ‘c:\a. txt’);
{$I-}
reset(f);
x := ioresult;
{$I-}
if x=0 then writeln(‘файл существует’)
else writeln(‘файл отсутствует’);
end.
Понятие буфера ввода-вывода
Следует отметить, что с файловой системой в Паскале связано понятие буфера ввода-вывода.
Буфер – это область в памяти, которая выделяется для каждого файла. При записи в файл вся информация сначала направляется в буфер и накапливается там до тех пор, пока весь объем буфера не будет заполнен. Только после этого (или после специальной команды) происходит передача данных на внешнее устройство. При чтении из файла данные вначале считываются в буфер, причем не сколько запрашивается, а сколько поместится в буфер.
Механизм буферизации позволяет более быстро и эффективно обмениваться информацией с внешними устройствами. Однако следует отметить, что если по какой-то причине при завершении работы с выходным файлом не будет выполнена процедура Close, то файл хотя и будет создан на внешнем устройстве, но содержимое последнего буфера в него не перенесется.
Вопросы для самопроверки
1. Что понимают под файлами в языке Паскаль?
2. Какие типы файловых переменных существуют в языке Паскаль?
3. Какие основные операции над файлами существуют в языке Паскаль?
4. Что такое буфер ввода-вывода?
§18. Типизированные файлы
Типизированный файл – это файл с объявленным типом его компонент.
Типизированные файлы хранят информацию в том виде, в котором она представлена в памяти компьютера. В этом плане они неудобны для человека: его нельзя просмотреть, создать или отредактировать, например, с помощью текстового редактора. Однако все эти неудобства компенсируются скоростью работы с данными.
Элементы типизированных файлов могут относиться к любому базовому или структурированному типу данных. Единственное ограничение: все элементы должны быть одного и того же типа.
Описание типизированных файлов
Описание типизированных файлов может иметь вид:
type <имя файлового типа> = file of <тип компонент>;
Пример: описать переменную - типизированный файл
type
FIO = String[20];
Spisok = file of FIO;
var
Stud: Spisok;
Prep: Spisok;
Здесь Stud, Prep – имена файлов, компонентами которых являются строки.
Описание файлов можно задавать и непосредственно в разделе описания переменных.
Пример: описание файловых переменных в разделе var
var
fsimv: file of char;
fr: file of real;
Как уже упоминалось выше, компонентами файла могут быть все скалярные типы, а из структурированных – массивы, множества, записи. Практически во всех конкретных реализациях языка Паскаль конструкция "файл файлов" недопустима.
Никакая файловая переменная не может быть задана константой.
Операции над типизированными файлами
Для типизированных файлов применимы процедуры Assign, Reset, Rewrite, Close, Rename, Erase, Eof, IOResult, рассмотренные ранее. Именно с помощью указанных процедур над типизированными файлами производятся такие операции, как связывание с физическим файлом, открытие и закрытие файла и т. п.
Для организации же ввода-вывода в типизированных файлах используются процедуры Read и Write:
Read(f, X);
Write(f, X);
где f – имя типизированного файла, а Х – переменная с таким же описанием, какое имеет компонента файла.
Выполнение процедуры Read(f, X) состоит в чтении с внешнего устройства одной компоненты файла и записи ее в X. Повторное применение процедуры Read(f, X) обеспечит чтение следующей компоненты файла и запись ее в X.
Пример: напечатать на экране все числа из файла
var
f: file of integer;
x: integer;
begin
assign(f, ‘c:\test. dat’);
reset(f);
while not eof(f) do
begin
read(f, x);
writeln(x);
end;
close(f);
end.
Выполнение процедуры Write(f, X) состоит в записи X в файл как одной компоненты. Повторное применение этой процедуры обеспечит запись X как следующей компоненты файла.
Пример: записать в файл 5 чисел, заданных пользователем
var
f: file of integer;
x: integer;
begin
assign(f, ‘c:\test. dat’);
rewrite(f);
for i:=1 to 5 do
begin
write(‘Введите число для записи в файл: ’);
readln(x);
write(f, x);
end;
close(f);
end.
Для работы с типизированными файлами можно использовать и расширенную форму записи операторов ввода и вывода:
Read(f, X1, X2, …, XK);
Write(f, X1, X2, …, XK);
Здесь f – типизированный файл, а переменные Х1, Х2, … ХК должны иметь тот же тип, что и объявленный тип компонент файла f.
Пример: записать в файл 5 чисел
var
f: file of integer;
x: integer;
begin
assign(f, ‘c:\test. dat’);
rewrite(f);
write(f, 10, 12, 32, 43, 24);
close(f);
end.
Последовательный и прямой доступ
К типизированным файлам можно организовать не только последовательный, но и прямой доступ.
Смысл последовательного доступа заключается в том, что в каждый момент времени доступна лишь одна компонента из всей последовательности. Для того, чтобы обратиться (получить доступ) к компоненте с номером К, необходимо просмотреть от начала файла К-1 предшествующую компоненту. После обращения к компоненте с номером К можно обращаться к компоненте с номером К+1. Отсюда следует, что процессы записи и чтения компонент файла не могут произвольно чередоваться. Файл вначале строится при помощи последовательного добавления компонент в конец, а затем может последовательно просматриваться от начала до конца.
Рассмотренные ранее средства работы с файлами обеспечивают именно последовательный доступ.
Прямой доступ означает возможность заранее определить в файле блок, к которому будет применена операция ввода-вывода. В случае нетипизированных файлов блок равен размеру буфера, для компонентных файлов блок – это одна компонента файла.
Прямой доступ предполагает, что файл представляет собой линейную последовательность блоков. Если файл содержит n блоков, то они нумеруются от 1 до n. Кроме того, вводится понятие условной границы между блоками, при этом условная граница с номером 0 расположена перед блоком с номером 1, граница с номером 1 расположена после блока с номером 1 и перед блоком с номером 2. Наконец, условная граница с номером n находится после блока с номером n.
Реализация прямого доступа осуществляется с помощью функций и процедур FileSize, FilePos, Seek и Truncate.
Функция FileSize(var f): Longint возвращает количество блоков в открытом файле f (не путайте с размером файла в байтах – каждый блок может занимать несколько байтов памяти).
Функция FilePos(var f): Longint возвращает текущую позицию (номер условной границы) в файле f. Для только что открытого файла текущей позицией будет граница с номером 0. Это значит, что можно записать или прочесть блок с номером 1. После чтения или записи первого блока текущая позиция переместится на границу с номером 1, и можно будет обращаться к блоку с номером 2. После прочтения последней записи значение FilePos равно значению FileSize.
Процедура Seek(var f; N: Longint) обеспечивает назначение текущей позиции в файле (позиционирование). В параметре N должен быть задан номер условной границы, которая должна стать текущей позицией. Естественно, что процедура Seek работает с открытыми файлами.
Процедура Truncate(var f) устанавливает в текущей позиции признак конца файла и удаляет (стирает) все последующие блоки.
Пример: если файл содержит более 10 компонент, то удалить все компоненты, следующие после 10-ой.
var
f: file of real;
begin
assign(f, ‘c:\test. dat’);
reset(f);
if filesize(f)>10 then
begin
seek(f, 10);
truncate(f);
end;
close(f);
end.
Вопросы для самопроверки
1. Что такое типизированные файлы?
2. Как описываются типизированные файлы в языке Паскаль?
3. Какие операции над типизированными файлами Вы знаете?
4. Что такое прямой и последовательный доступ?
§19. Нетипизированные файлы
Нетипизированный файл – это файл, для которого не объявлен тип его компонент.
Одним из преимуществ нетипизированных файлов является высокая скорость их обработки. Такие файлы нередко применяются тогда, когда нужно, например, скопировать крупный кусок одного файла в другой без изменений.
Описание типизированных файлов
Описание нетипизированных файлов может иметь вид:
var <имя файловой переменной> : file;
Пример: описание файловых переменных в разделе var
var
f: file;
Как видно, во время описания такой переменной тип компонентов не указывается. Здесь происходит считывание или запись обычного массива байтов.
Операции над типизированными файлами
Для нетипизированных файлов применимы процедуры Assign, Reset, Rewrite, Close, Rename, Erase, Eof, IOResult, рассмотренные ранее. Именно с помощью указанных процедур над типизированными файлами производятся такие операции, как связывание с физическим файлом, открытие и закрытие файла и т. п.
По умолчанию, при открытии файлов для чтения (Reset) и для записи (Rewrite) для файлов устанавливается, что одновременно могут быть считаны или записаны 128 байт. Вы можете указать и другой размер таких «кусков», используя конструкции Reset(f, size) и Rewrite(f, size).
Для организации же ввода-вывода в нетипизированных файлах используются процедуры BlockRead и BlockWrite:
BlockRead(f, buf, count, result);
BlockWrite(f, buf, count, result);
где f – имя типизированного файла, buf – переменная (буфер), в которую будет производиться чтение или из которой произойдет запись, count – количество элементов для одновременного чтения или записи, а result – хранит количество элементов, фактически считанных или записанных (является необязательным).
Вопросы для самопроверки
1. Что такое нетипизированные файлы?
2. Как описываются нетипизированные файлы в языке Паскаль?
3. Какие операции над нетипизированными файлами Вы знаете?
§20. Текстовые файлы
Текстовые файлы представляют собой последовательность строк произвольной длины.
Если Ваша программа считывает данные из текстового файла, то входной файл для такой программы можно создать, например, в Блокноте.
В текстовом файле можно хранить и целочисленные, и вещественные значения. При этом в отличие от типизированных файлов, открыв такой файл в том же Блокноте, мы увидим числа в привычной десятеричной системе.
Описание типизированных файлов
Для описания текстовых файлов в языке определен стандартный тип Тext:
var f1, f2: text;
Пример: описание текстового файла в разделе var
var
t: text;
Чтение и запись
Для операций над текстовыми файлами, кроме перечисленных в параграфе «Файлы», определены также следующие процедуры:
Readln(T, x1, x2, …, xN) – реализует чтение из файла T N значений исходных данных и пропуск остальных значений до начала следующей строки. Считанные значения присваиваются переменным x1, x2, ..., xN.
Readln(T) – пропускает в файле T одну строку данных.
Writeln(T, x1, x2, …, xN) – реализует вывод в файл T значений x1, x2, ..., xN и переход к началу следующей строки.
Writeln(T) – вставляет признак конца строки в текущую позицию файла T.
Здесь Т –текстовый файл, а переменные x1, x2, …, xN могут быть целого, действительного, символьного, логического типа или строкой. При записи значений переменных в файл они преобразуются из внутреннего представления в текстовый.
Конец строки и конец файла
Строки в тестовых файлах имеют переменную длину.
Каждая строка завершается признаком конца строки. Проверить, достигнут ли признак конца строки, можно с помощью функции:
EOLn (var T: Text): Boolean;
где Т – имя текстового файла. Функция возвращает значение True, если конец строки достигнут, и значение False, если конец строки не достигнут.
При работе с текстовыми файлами бывает удобно также использовать функцию
SeekEOLn(var T: Text): Boolean;
которая возвращает значение True даже в том случае, если конец строки еще не достигнут, но до него остались лишь пробелы.
Аналогичная функция существует и для проверки признака конца файла:
SeekEOF(var f: Text): Boolean;
Функция возвращает значение True, если до конца файла остались лишь пустые строки и строки, заполненные пробелами.
Дополнительные процедуры для работы с текстовыми файлами
В Паскале существуют и дополнительные процедуры и функции, применимые только к текстовым файлам. Рассмотрим их подробнее.
Процедура SetTextBuf(var T: Text; var Buf; BufSize: Word) служит для увеличения или уменьшения буфера ввода-вывода текстового файла T. Значение размера буфера для текстовых файлов по умолчанию равно 128 байтам. Изменять размер буфера рекомендуется до открытия файла. Буфер файла начнется с первого байта переменной Buf. Размер буфера задается в необязательном параметре BufSize, а если этот параметр отсутствует, то определяется длиной переменной Buf.
Процедура Append(var T: Text) служит для специального открытия выходных файлов. Она применима к уже существующим физическим файлам и открывает их для дозаписи в конец файла.
Процедура Flush(var T: Text) применяется к открытым выходным файлам. Она принудительно записывает данные из буфера в файл независимо от степени его заполнения.
Файлы Input и Output
К текстовым файлам относятся и стандартные файлы INPUT и OUTPUT. Рассмотренные ранее операторы ввода-вывода с консоли Readln и Writeln являются частным случаем операторов обмена с текстовыми файлами, когда используются стандартные файлы ввода-вывода INPUT и OUTPUT.
Работа с указанными файлами имеет свои особенности, такие как:
· имена этих файлов в списках ввода-вывода не указываются;
· применение процедур Reset, Rewrite и Close к стандартным файлам ввода-вывода запрещено.
Вопросы для самопроверки
1. Что такое текстовые файлы?
2. Как описываются текстовые файлы в языке Паскаль?
3. Какие операции над текстовыми файлами Вы знаете?
4. Какие дополнительные процедуры и функции для работы с текстовыми файлами Вы знаете?
5. Что такое файлы Input и Output?
§21. Ссылки и указатели
Каждая переменная, описанная в разделе var, имеет свое имя и описание. Описание, среди прочего, позволяет компилятору определить, сколько байтов памяти необходимо выделить в памяти для переменной. Имя же позволяет обратиться к зарезервированной под переменную ячейке памяти.
Однако каждая переменная, кроме своего имени, имеет и физический адрес: номер ячейки памяти, в которой хранится значение переменной.
Понятие указателя
Указатель представляет собой переменную целого типа, которая интерпретируется как адрес байта памяти, содержащей некоторый элемент данных. Этим элементом может быть переменная, константа, адрес другой переменной и т. д.
Адрес интерпретируется двумя шестнадцатибитными словами BA:BS, где BA – сегментный адрес, BS – смещение. Сегмент – участок памяти длиной 64 Кбайта.
При обращении к переменной по имени программа сама определяет, по какому адресу находится ее значение. Указатель же позволяет хранить адрес произвольной ячейки памяти, в которой могут храниться те или иные данные.
Описание указателей
Существует два типа указателей: типизированные и нетипизированные.
Типизированные указатели хранят адрес данных первого байта какого-либо конкретного типа. Для объявления таких указателей обычно используется символ ^, который размещается непосредственно перед типом данных, на значения которого ссылается типизированный указатель:
Пример:
type
IntPtr = ^Integer;
var
P1 = IntPtr;
P2 = ^Integer;
Отметим, что типизированные указатели – это единственный тип в языке Паскаль, который может ссылаться на еще не описанный тип.
Пример:
type
Link = ^Node; {тип Node будет описан только в следующей строке!}
Node = record
Key: Integer;
Next: Link;
end;
Нетипизированные указатели хранят адрес данных, не связанные с каким-либо конкретным типом данных. Они могут ссылаться и на целочисленные данные, и на символьные, и т. д. В этом случае используется ключевое слово Pointer.
Пример:
var
P: Pointer;
Такая переменная представляет собой адрес байта данных, начиная с которого хранятся какие-либо данные произвольной длины.
Операции с указателями
Определение адреса
Физический адрес любой переменной можно узнать при помощи стандартной функции:
addr(<имя_переменной>): <указатель>
или унарной операции
@<имя_переменной>.
Пример:
var
P: Pointer;
intP: ^integer;
x: integer;
…
P := addr(x);
P := @x;
intP := addr(x);
intP := @x;
Разыменование
Указатель – это лишь адрес ячейки памяти. Для обращения к содержимому такой ячейки используют операцию, называемую разыменованием. Записывается такая операция следующим образом:
<имя_указателя>^
Отметим, что операция разыменования применима лишь к типизированным указателям.
Пример:
var
intP: ^integer;
x: integer;
…
intP := @x;
intP^ := 10;
writeln(intP^);
Присваивание
В случае указателей при присваивании получают лишь новый адрес памяти. Содержимое самих ячеек памяти остается неизменным.
Пример:
var
intP1: ^integer;
intP2: ^integer;
…
x := 3;
intP1 := @x;
y := 4;
intP2 := @y;
intP1 := intP2; {оба указателя ссылаются на значение 4}
intP2^ := 7; {оба указателя ссылаются на значение 7}
В операции присваивания могут фигурировать только указатели, адресующие переменные одинаковых типов данных. Обойти эти ограничения позволяет универсальность нетипизированного указателя Pointer, совместимого с указателями любых типов:
Пример:
var
intP1: ^integer;
intP2: ^integer;
realP: ^real;
P: Pointer;
…
intP1 := intP2;
{нельзя realP := intP1}
P := intP1;
realP := P;
У указателей также существует свой "ноль", который означает, что указатель не указывает никуда:
p:= nil;
Сравнение
Для указателей определены две операции сравнения: равно (=) и не равно (<>).
Две переменные, описанные как указатели на один и тот же тип данных, считаются совпадающими, если они указывают на одну и ту же область памяти.
Для разнотипных указателей сравнения невозможны. Однако сравнивать типизированный и нетипизированный указатели можно.
Выделение и освобождение динамической памяти
Указатели – это лишь адрес ячейки памяти, а о состоянии самой ячейки должен позаботиться программист.
Вся динамическая память может располагаться как сплошной массив, состоящий из байтов и называемый кучей. Куча размещается в памяти компьютера следом за областью памяти, которую занимает тело программы.
Перед помещением данных в кучу программист должен подготовить участок памяти, куда данные собственно будут помещены. После завершения использования данных использованное место необходимо за собой очистить.
Процедура New(P) резервирует в памяти участок для значения того типа, на который ссылается указатель P, и устанавливает в указатель адрес зарезервированного участка.
Пример:
var
P1: ^Integer;
...
New(P1);
P1^:=5;
Процедура Dispose(P) освобождает участок памяти, на которую указывает типизированный указатель P.
Пример:
Dispose(P1);
P1:=nil;
Процедуры New и Dispose применимы только для типизированных указателей. При работе с нетипизированными указателями можно воспользоваться следующими процедурами.
Процедура GetMem(P, Size) выделяет из кучи блок памяти размером Size (максимум 64 Кбайта) и устанавливает в указатель P ссылку на неё.
Процедура FreeMem(P, Size) уничтожает в куче блок памяти размером Size по адресу P.
Вопросы для самопроверки
1. Что такое указатель в языке Паскаль?
2. В чем различие типизированных и нетипизированных указателей?
3. Какие операции над типизированными указателями Вы знаете?
4. В каких случаях при работе с указателями необходимо предварительно выделить память?
5. В каких случаях при обращении к указателю необходимо выполнить операцию разыменования?
Рекомендуемая литература
1. Фаронов Паскаль 7.0. Начальный курс: Учебное пособие. – М.: Нолидж, 1998. – 613 с.
2. Фаронов Паскаль 7.0. Практика программирования. – М.: Нолидж, 1997. – 430 с.
3. Пильщиков упражнений по языку Паскаль: Учебное пособие для вузов. – М.: Наука, 1989.
4. Васильев Паскаль в примерах и задачах. Освой самостоятельно: Учебное пособие. – М.: Финансы и статистика, 2002. –496 с.
5. Джонс Ж. Решение задач в системе Турбо Паскаль: Пер. с англ. – М.: Финансы. – 720 с.
6. Немнюгин С. А. Turbo Pascal. Программирование на языке высокого уровня: Учебник для студентов вузов, обучающихся по направлению подготовки дипломированных специалистов «Информатика и вычислительная техника». – М.; СПб.; Нижний Новгород: Питер, 2005. – 544с.
7. Аляев и языки программирования Pascal, C++, Visual Basic: Учебно-справочное пособие для курсантов военно-учебных заведений и училищ, студентов технических вузов. – М: Финансы и статистика, 2002. – 320с.
8. Мизрохи С. В. TURBO PASCAL и объективно-ориентированное программирование. – М.: Финансы и статистика, 1992. – 192 с.
9. Попов и Дельфи: Учебный курс. – М.; СПб.; Нижний Новгород: ПИТЕР, 2005. – 576с.
Технология программирования
Учебное пособие
Редактор:
Подписано к печати Заказ Формат 60 / 90 1 /16 Отпечатано на RISO GR 3750 | Бум. ГОЗНАК Уч. изд. л. Усл. печ. л. Тираж 100 экз. |
Издательство «Нефтегазовый университет» Государственного образовательного учреждения высшего профессионального образования |
«Тюменский государственный нефтегазовый университет»
Тюмень,
Отдел полиграфии издательства «Нефтегазовый университет»
Тюмень, ул. Киевская, 52
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


