Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
program FBin;
uses
Crt;
var
F : file of integer;
int : integer;
begin
Assign( F, 'filo.bin');
ReSet(F);
Read( F, int);
Writeln( int);
Read( F, int);
Writeln( int);
Close(F);
end.
Вместо ReWrite - ReSet. Если файл есть, то он его откроет на чтение. Если
файла нет – всё кончится очень плохо. Так что проверьте существование
файла перед открытием с помощью FindFirstFile.
Или с помощью неупомянутой ранее функции FileExists.
Но отметим очень существенную разницу с текстовым файлом. Нам не
нужно знать, что именно было запи
чтобы его проч
каждом считывании из файла мы получаем одну строку. Единственно
важное – вовремя остановиться, то есть не попытаться прочитать чтонибудь ещё после того, как из файла всё уже, собственно, прочитано.
С бинарным файлом не так. Чтобы прочитать его содержимое, мы
абсолютно точно знать, что же именно в этот файл было записано. Какие
переменные, каких типов, какого размера, в каком порядке. Малейшая
ошибка – и на выходе куча мусора.
Так в чём преимущество бинарных файлов? А в них мож
Нет, не так – ВСЁ! И вы это ещё оцените.
итать. Достаточно знать, что этот файл текстовый. При
сано в текстовом файле, для того,
должны
но записать всё.
201

TP. В чём разница
Разница гигантская. В TP есть нетипизированные файлы, в которые
можно писать абсолютно всё и, главное , вперемешку и как попало.
Главное не забыть, чего туда записал…
Выглядит это так:
var
f : file;
int : integer;
fl : real;
stroka : string[20];
А теперь пишем:
{открыть файл}
Assign( f, ‘a.bin’);
ReWrite( f, 1);
{записать всё в файл}
BlockWrite( f, int, SizeOf(int));
BlockWrite( f, fl, SizeOf(fl));
BlockWrite( f, stroka, SizeOf(stroka));
{закрыть файл}
Close(f);
Обратите внимание на волшебную единицу в операторе ReWrite. Та
надо. Восхититесь функцией SizeOf. Благодаря ей нам (точнее нашему
программному коду) не надо знать, сколько байто
языка будет занимать тип real и сколько байт занимает строка из двадцати
символов (ответ – двадцать один).
А как всё это богатство прочесть из файла? Да почти также, как и
записывали.
{открыть файл}
Assign( f, ‘a.b
ReSet( f, 1);
in’);
в в очередной версии
к
202

{записать всё в файл}
BlockRead( f, int, SizeOf(int));
BlockRead( f, fl, SizeOf(fl));
BlockRead( f, stroka, SizeOf(stroka));
{закрыть файл}
Close(f);
203

Глава 11.
Всякие глупости,
она же
Глава очень длинная
Записи. И как мы только без них обходились!
Записи – это редкий пример простой и полезной вещи. В порядке
исключения не будем начинать с объявления записи, в которой нет ни
одного поля. Сразу объявим правильную запись и применим её к делу.
Помните программу с мигающими звёздочками?
Нам надо было хранить информацию об их координатах. Для этого мы
объявил
и по отде
……………………………………….
starX[i]:=x;
starY[i]:=y;
……………………………………….
Массив, как Вы очень хорошо усвоили, объединяет в себе несколько
(обычно много) однотипных элементов. Запись объединяет несколько
(обычно немного) разнотипны
записи.
Наш первый пример записи не вполне показателен – оба поля записи
будут однотипны.
Помните мы упоминали объявление типов, в связи с передачей массивов в
качестве параметров? В случае записей настоятельно рекомендуется
объявлять их как типы. Это значительно повышает читаемость
программы, особенно в нашем случае, когда используется не просто
переменная, объявленная как
type
TPos = record
и два массива – для координаты X и для координаты Y отдельно,
льности, но всегда рядом, этими массивами пользовались:
starX : array[1..maxStar] of integer;
starY : array[1..maxStar] of integer;
SetPixel( starX[star], starY[star], clBlack);
х элементов. Массив может объединять
Запись может включать в себя массивы.
запись, а массив записей.
204

x : integer;
y : integer;
end;
var
thestars : array[1..maxStar] of TPos;
……………………………………….
thestars[i].x:=x;
thestars[i].y:=y;
……………………………………….
SetPixel( thestars[star].x, thestars[star].y, clBlack);
Пример записи чуть сложнее. Встречавшийся уже SearchRec из поиска
файлов. Вот объявление этой записи:
Так оно выглядит в Pascal ABC:
type
SearchRec = record
name : string;
size : integer;
end;
Полей записи по прежнему только два, но типы у них уже разные..
По современным понятиям о красоте исходного кода, вс
должны начинаться с буквы T.
Повторим с самого начала, постаравшись систем
Объявление переменной типа запись начинается с нового слова record и
кончается старым словом end. Между ними объявляются поля записи.
Объявление отдельно взятого поля ничем не отличается от объявления
одной отдельно взятой переменной. Поле может быть любого разумного
типа. Надеюсь, чт
так, без объявления нового типа, можно объявить од
запись:
var
о Ваша фантазия не выйдет за пределы возможного. Вот
атизировать.
е имена типов
ну переменную как
205

st : record
x : integer;
y : integer;
end;
Соответственно, и наш массив тоже можно было объявить
непосредственно:
var
thestars : array[1..maxStar] of record
x : integer;
y : integer;
end;
Лично я от таких объявлений пугаюсь и пытаюсь спрятаться. Лучше
объявлять через тип.
Правила идентичности типов, о которых мы говорили в связи с
массивами, распространяются и на записи.
К записи, как и к массиву, мо
есть, для предыдущего объявления мы
thestars[i]:=thestars[i+1];
Это равнозначно такому коду:
thestars[i].x:=thestars[i+1].x;
thestars[i].y:=thestars[i+1].y;
Чаще происходят обращения к отдельным полям записи, как мы
наблюдали на примере SearchRec.
Обратите внимание – у нас есть переменная X и есть поле записи X и
никому это не ме
Записи пишутся в файлы на об
переменная.
шает. Главное, конечно, что это не мешает транслятору.
жно обратиться как к единому целому. То
можем написать так:
щих основаниях, то есть как любая другая
206

Указатели
Как и обещано – самое-самое важное и самое-самое ужасное. Не то, что
бы оно было тут очень нужно. Многие учебники начального уровня
обходятся без указателей. Трудности видны сразу, а польза неочевидна.
Но мне кажется, надо представлять, с чем придётся встретиться в
реальной жизни. Дальше будет много непонятных слов и загадочных
телодвижений. Смотрите и н
Как и файлы, указатели бывают типизированные и нетипизированные. И,
как и в случае с файлами, нас интересуют только нетипизированны
сожалению, разработчики Pascal ABC в своей запредельной мудрости,
хотя и оставили в языке нетипизированные указатели, но лишили их
применение всякого смысла, отменив для них операцию разыменования.
Что такое операция ра
объяснено чуть позже.
Тем не менее, начнём с того, что есть общее для обоих видов указателей.
Как объявить указатель? Очень просто.
var
p : pointer;
А присвоить указателю значение? Это сложнее. Надо всё же объяснить
сначала, что такое указатель.
Указатель, как и следует из своего имени, указ
На область памяти. Понятно? Нет? Неважно, продолжаем. Вернее,
возвращаемся к вопросу присва
Целой переменной можно присвоить другую целую переменную, вернее
значение другой целой переменной, строке можно присвоить другую
строку. Указатель не исключение. Объявляем:
var
p1,p2 : pointer;
Тогда можем написать
p1:=p2;
аслаждайтесь.
зыменования и зачем она
ивания указателю значения.
е. К
обязательно нужна, будет
ывает. На что указывает?
207

А смысл? Смысл в том, что указатель p1 имеет теперь тоже значение, что
и p2 и указывает на ту же область памяти. А на что указывает p2?
Первый способ присвоить значение указателю (сначала добавим
объявлений):
var
p1,p2 : pointer;
int1,int2,int3 : integer;
a : array[1..10] of integer;
p1:=@int1;
Теперь указатель p1 указывает на переменную int1. Разумеется, это не
означает, что указатель имеет тоже значен
указателя вообще имеет мало отношения к значению переменной, на
которую он указывает. У
p1:=@a;
p2:=@a[1];
На что указывают указатели? Первый – на массив A, второй – на первый
элемент этого массива A[1]. В результате, значения этих двух указателей
одинаковы, ведь массив начинается с первого элемента. Непонятно?
Продолжаем.
Второй способ проинициализировать указатель – выде
Выделение памяти – операция симметричная, в смы
выделили, то её надо будет освободить. И ответственный за это Вы.
Выделяем:
GetMem( p1, 10);
Мы запросили (и получили) десять байтов. На эти десять байтов теперь
указывает указатель p1. А что находится в этих десяти байтах? Да ничего.
Напоминаю – если память выделили, на
FreeMem( p1);
казатель – это адрес переменной.
до и освободить! Вот так:
ие, что и int1. Значение
лить память.
сле, если память
208

В данном конкретном случае я прямо таки вынужден похвалить авторов
ABC. В версиях Паскаля TP и Delphi в этом операторе надо самому
указывать, сколько байт памяти освободить. А у программистов память
короткая, и они к моменту освобождения памяти начисто забывают,
сколько они её выделяли. Так вот, там это было плохо. А здесь хорошо.
Но освобождать память всё равно до
Отвечаете за вс
Еще раз – в переменной типа указатель хранится вовсе не значение
переменной, на которую этот указатель указывает. Там хранится адрес
этой переменной.
Ещё одна новость. Для целых и дробных переменных есть нулевое
значение. Для строки есть пустая строка. Указ
него существует специальный как бы указатель, который никуда не
указывает. Выглядит присвое
p:=nil;
nil – заранее определённая в Паскале константа. Это указатель, который
никуда не указывает. Внутри у него банальный ноль. В случае обращения
к памяти, на которую как бы указывает этот указатель, всё кончается
очень и очень печально.
А сейчас типиз
потому, то умеет указывать не на что попало, а только на перемен
указанного в объявлении типа. Объявляем указатель, который будет
указывать на целую переменную, и, заодно, объявляем и целую
переменную.
var
pInt : ^integer;
int : integer;
А теперь вот такой не
int:=77;
pInt:=@int;
Writeln( pInt^);
ё Вы!
ние пустого указателя вот так:
ированный указатель. Типизированным он называется
хитрый код:
лжны Вы. Никакого контроля нет!
атель ничем не хуже. Для
ную
209

Что будет выведено на экран? Вы так и думали? А почему?
И главное. Вот эта самая “крышечка” pInt^ после имени указателя и есть
страшная и ужасная операция разыменования.
А что с этими указателями делать? Пока что почти ничего, необходимости
в них у Вас на данном этапе духовного развития нет. Потом они,
указател
программисты без них чудесным образом обходятся. Для таких
программистов даже созданы языки программирования, в которых
указателей нет ну вообще.
Но лично мне кажется, что с указателями жизнь как-то проще и веселее.
О технике безопасности.
Выделенная память должна быть освобождена. Сн
потом освободить. Не перепутайте.
Нельзя два раза выделять память по одному указ
её перед вторым выделением. Внешне всё будет хорошо, а на самом деле
плохо. Называется это утечка памяти. Несколько таких утечек и, когда
в следующий раз Вы попросите ещё немножко памяти, Вы её не
получите. Вс
Нельзя два раза освобождать память по одному указателю.
Итак, трудности и непонятности стоят во ве
прячется. Но это так только кажется с первого взгляда.
и, очень даже понадобятся. Но, как я уж
я кончилась. Утекла, в смысле.
Round, Ord, Chr и другие пустячки
е говорил, многие
ачала выделить,
ателю, не освобождая
сь рост, польза где-то
Вещицы разные и мелкие. О некоторых упоминалось раньше, о некоторых
нет.
Round. Как следует из названия – округляет. Округляет до целого.
Позволяет пр
совсем др
var
int : integer;
fl : real;
fl:=1.23;
int:=Round(fl);
210
исвоить целой переменной дробную переменную. Ну не
обную, а целую часть от неё. Понятно, в общем.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
