Теория
Давайте представим, что нам нужно создать базу данных с книгами. При этом о каждой книге нам надо знать следующее: имя автора, название, количество страниц. Что тут делать? Нам помогут записи.
Запись - это структура данных, состоящая из некоторого числа компонентов, которые называются полями. Записи объявляются в разделе типов. Так сами являются типом данных. Потом мы можем создавать переменные таких типов.
Запись объявляется таким образом:
ИМЯ = RECORD поля END
Поля записей - это переменные любого типа (в том числе и записи !). Объявляются они так же как и в разделе переменных:
type
TBook = record
author, name : string;
page : integer
end;
var
book, b1 : TBook;
Таким образом в записи TBook у нас 2 поля типа string и одно поле типа integer. Для одинаковых записей возможно применение операции := Наприер мы можем сделать так book := b1;
доступ к полям осуществляется таким образом: имя_переменной.имя_поля Т.е. через точку! Например так:
begin
book.author := '****';
book.name := 'Pascal-Tutorial';
book.page := 17;
В отладчике вы можете просматривать как значения отдельных полей, так и значение всей записи сразу (отобразятся все поля сразу).
Что бы упростить доступ к полям записи можно использовать оператор with:
with переменная do оператор
Сделано это, что бы каждый раз не указывать имя переменной. Например мы можем написать так:
with book do
begin
author := '****';
name := 'Pascal-tutorial';
page := 17;
writeLn ('Author - ', author, ' Name - ', name, ' # - ', page)
end
согласитесь удобно. Однако этот способ хорош только когда либо полей действительно много, либо когда с полями нужно совершать много действий.
Думаю, вы ещё не считаете байты, которые занимают ваши данные, однако скоро это может понадобится. Поэтому нам нужна функция
function SizeOf(X): Integer;
возвращает размер аргумента Х в байтах. Например: writeLn (sizeof (book) ) выведет на экран 514 - можно подсчитать, что это правда: сосчитайте отдельно размер каждого поля - author и name по 256 байт (так как строки это array [0 .. 255] of char - а char занимает 1 байт => размер равен числу элементов массива - 256). Размер page = 2 байта. Так мы получаем: 256 + 256 + 2 = 514.
|
Программа
Сегодня мы напишем примитивную базу данных для библиотеки :) Наша база из-за нехватки средств и времени на разработку будет делать всего два действия: добавлять книгу и показывать список книг. Так как сегодня мы учимся использовать записи, то будем их активно использовать. Давайте прикинем, что нужно знать библиотеке о книгах. Я решил, что просто необходимы следующие данные:
- Имя автора
- Название книги
- Число страниц
- Цена
- Дата издания
конечно можно было бы добавить и ещё что-то, но нам и этого хватит. Кстати дату тоже можно организовать в виде записи. С этого и начнём:
type
Tdate = record
d, m, y : integer
end;
Чудненько! Теперь ничто нам не мешает создать запись, которая будет отвечать за книгу:
Tbook = record
author, name : string;
page, cost : integer;
date : tdate
end;
Итак заметьте, что поле date в свою очередь является записью. Пусть у нас есть переменная с именем book типа tbook. Тогда что бы обратиться к месяцу нужно написать такое: book.date.m !
Ну и что бы было совсем круто :) опишем запись библиотеки, в которой будут массив из книг (то бишь типа Tbook) и число книг в библиотеке.
Tlib = record
book : array [1 .. Max] of Tbook;
num : byte
end;
где Max - константа, отвечающая за вместимость библиотеки. Напишем програмку для малюсенькой библиотеки книг этак на 50.
program homelib;
uses CRT;
const
Max = 50;
type
Tdate = record
d, m, y : integer
end;
Tbook = record
author, name : string;
page, cost : integer;
date : tdate
end;
Tlib = record
book : array [1 .. Max] of Tbook;
num : byte
end;
function Menu : char;
begin
ClrScr;
writeLn (' БИБЛИОТЕКА.');
writeLn (' 1 - добавить книгу.');
writeLn (' 2 - показать книги.');
writeLn ('ESC - выход.');
Menu := readkey
end;
procedure Add (var new : tlib);
begin
if new.num = Max then
begin
writeLn ('Нет места на полках :(');
readLn;
exit
end;
new.num := new.num + 1;
with new.book[new.num] do
begin
Write ('Введите имя автора: ');
readLn (author);
write ('Введите название: ');
readLn (name);
write ('Введите число страниц: ');
readLn (page);
write ('Введите цену: ');
readLn (cost);
write ('Введите дату ДД.ММ.ГГГГ: ');
with date do
begin
readLn (d, m, y)
end
end
end;
procedure Show (lib : tlib);
var
i : integer;
begin
for i := 1 to lib.num do
begin
writeLn ('===========================');
writeLn ('Книга номер ', i);
with lib.book[i] do
begin
writeLn ('Автор ', author);
writeLn ('Назвние ', name);
writeLn ('Число страниц ', page);
writeLn ('Цена = ', cost);
with date do
writeLn ('Дата ', d, '.', m,'.', y)
end
end;
readLn
end;
var
lib : tlib;
key : char;
begin
lib.num := 0;
while key <> #27 do
begin
key := Menu;
case key of
'1': Add (lib);
'2': show (lib)
end
end
end.
Итак быстренько копируем запускаем... круто ошибок нет ! ... нажимаем 1, вводим все поля... жмём 2... чёрт ! А это что ещё такое:
Error 202: Stack overflow error. |
Итак перед вами сообщение о переполнении стека (Stack). Где же оно возникает? давайте выполним пошагово с заходом в функции: жмём F7.... вводим данные... опа! вот оно! На входе в процедуру show.
Вы помните, что существуют два типа передачи параметров: адрессный и простой. При простом способе в памяти создаётся копия переменной и используется. После выхода из процедуры (функции) эта память освобождается. Так же в этой памяти создаются локальные переменные и хранятся некоторые данные о программе. Эта область памяти и называется стеком. Давайте разберёмся почему происходит её переполнение.
Нажмите Ctrl+F4 (из меню Debug -> Evaluate/modify...) перед вами возникнет окошко. В поле Expression можно вводить выражение, которое надо подсчитать. Давайте подсчитаем размер нашей структуры tlib: введём в это поле sizeof (tlib) и посмотрим на результат (поле Result) - там будет размер записи tlib в байтах, а именно 26 101.
Какой же у нас размер стека ? Это можно узнать таким образом: Options -> Memory sizes...
Перед вами открылось окно с непонятными названиями и цифрами. Причём подсвечен именно размер стека (Stack size). Если вы ничего не меняли, то он должен быть равен 16 384. Кстати обратите внимание, что слова Stack Size упоминаются 3 раза: первый раз в разделе Real Target, второй - Protected Target, и наконец третий - Windows Target. Наши программы пока относятся к Real (ведь они такие реальные:). Про остальные две цели :) (target - цель англ.) я раскажу позже. Сейчас запомните, что мы пишем программы для реального режима (Real mode) процессора (не правда ли звучит гордо :) Поэтому нам и надо Real target. Подробности как всегда будут позже. Такой я нехороший.
Так вот вернёмся к нашим баранам. Размер стека стоит 16 384, а размер одной нашей структуры уже 26 101. Не правда ли большая разница ? Давайте увеличим размер стека. Для этого просто введите его размер. Давайте и введём 26101. Запускаем программу .... (для этого вместо привычных Ctrl+F9 нужно нажать сначала F9, а уж потом Ctrl+F9) досада! опять та же ошибка. Однако давайте вспомним, что я написал немного выше - в стеке хранятся и локальные переменные и некоторые служебные данные. Поэтому нам нужно увеличить его размер ещё немного. Давайте не пожадничаем и увеличим размер на 1 килобайт.
После этих действий программа должна работать нормально. Вернёмся теперь к сути программы.
Итак у нас программа состоит из 2-х процедур и одной функции. Функция Menu у нас "рисует" на экране меню и возвращает нажатую клавишу. В главной программе в зависимости от этого добавляем книгу, выводим данные о всех книах или выходим (этот случай когда key = #27. 27 - код клавиши ESC).
Давайте теперь обратим наш взгяляд на процедуру Add. В ней мы заполняем запись new (обратите внимание на вложенность операторов with). Обращать внимание больше не что :) Думаю, что исходник довольно прозрачный, однако если вам что-то не понятно, то обязательно напишите.
|
Как вы яхту назовёте, так она и поплывёт!
Думаю вы уже обратили внимание, что я начинаю название всех типов с буквы t. Это сокращение от слова Type. Так ведь удобнее и сразу поймёшь, где у тебя тип, а где что-то ещё. Однако некоторые пошли дальше и используют подобные сокращения у всех имён. Например названия функций начинают с букв fn, переменных целого типа с i, строк - sz и т.д. Это так называемая Венгерская нотация. Она активно используется при программирование в Windows, т.к. была разработана Чарзом Симони (программист Microsoft). Она имеет свои выгоды: вы всегда по имени переменной понимаете, какого она типа. Однако никто не заставляет ей пользоваться. Хотя я и перенял вредную привычку начинать новые типы с буквы t.
|