- •1.1. Как начать работу с турбо паскалем
- •1.2. Функциональные клавиши
- •1.3 Текстовый редактор
- •1.4. Основные приемы работы в среде турбо паскаля
- •Глава2. Знакомство с языком турбо паскаля
- •Глава 2 знакомство с языком турбо паскаля
- •2.1. Ваша первая программа
- •2.2. Типы данных
- •2.3. Преобразования типов и действия над ними
- •2.4. Операторы языка
- •2.5. Массивы
- •2.6. Процедуры и функции
- •2.7. Примеры программ
- •Глава3.Элементы языка
- •Глава 3
- •3.1. Алфавит
- •3.2. Идентификаторы
- •3.3. Константы
- •3.4. Выражения
- •3.5. Операции
- •3.6. Структура программы
- •Глава 4. Типы данных
- •4.1. Простые типы
- •4.2. Структурированные типы
- •4.3. Строки
- •4.4. Совместимость и преобразование типов
- •Глава 5. Файлы
- •Глава 5
- •5.1. Доступ к файлам
- •5.2. Процедуры и функции для работы с файлами
- •5.3. Текстовые файлы
- •5.4. Типизированные файлы
- •5.5. Нетипизированные файлы
- •Глава 6. Указатели и динамическая память
- •6.1. Динамическая память
- •6.2. Адреса и указатели
- •6.4. Выделение и освобождение динамической памяти
- •6.5. Использование указателей
- •6.6. Процедуры и функции для работы с динамической памятью
- •6.7. Администратор кучи
- •Глава 7. Типизированные константы
- •7.1. Константы простых типов и типа string
- •7.2. Константы-массивы
- •7.3. Константы-записи
- •7.4. Константы-множества
- •7.5. Константы-указатели
- •Глава 8. Процедуры и функции
- •Глава 8
- •8.1. Локализация имен
- •8.2. Описание подпрограммы
- •8.3. Параметры-массивы и параметры-строки
- •8.4. Процедурные типы. Параметры-функции и параметры-процедуры
- •8.5. Нетипизированные параметры-переменные
- •8.6. Рекурсия и опережающее описание
- •8.7. Расширенный синтаксис вызова функций
- •Глава 9. Модули
- •Глава 9
- •9.1. Структура модулей
- •9.2. Заголовок модуля и связь модулей друг с другом
- •9.3. Интерфейсная часть
- •9.4. Исполняемая часть
- •9.5. Инициирующая часть
- •9.6. Компиляция модулей
- •9.7. Доступ к объявленным в модуле объектам
- •9.8. Стандартные модули
- •Глава 10. Объекты
- •Глава 10
- •10.1. Основные принципы ооп
- •10.2. Постановка учебной задачи
- •10.3. Создание объектов
- •10.4. Использование объектов
- •Глава 11. Другие возможности турбо паскаля
- •Глава 11
- •11.1. Внешние процедуры (функции)
- •11.2. Использование встроенных машинных кодов
- •11.3. Обращение к функциям операционной системы
- •11.4. Поддержка процедур обработки прерываний
- •11.5. Запуск внешних программ
- •11.6. Оверлей
- •11.7. Прямое обращение к памяти и портам ввода-вывода
- •11.8. Длинные строки
- •Глава 12. Встроенный ассемблер
- •Глава 12
- •12.1. Общее описание мп 8086/8088
- •12.2. Специфика встроенного ассемблера
- •Глава 13. Использование библиотеки crt
- •Глава 13
- •13.1. Программирование клавиатуры
- •13.2. Текстовый вывод на экран
- •13.3. Программирование звукового генератора
- •Глава 14. Использование библиотеки graph
- •Глава 14
- •14.1. Переход в графический режим и возврат в текстовый
- •14.2. Координаты, окна, страницы
- •14.3. Линии и точки
- •14.4. Многоугольники
- •14.5. Дуги, окружности, эллипсы
- •14.6. Краски, палитры, заполнения
- •14.7. Сохранение и выдача изображений
- •14.8. Вывод текста
- •14.9. Включение драйвера и шрифтов в тело программы
6.5. Использование указателей
Подведем некоторые итоги. Итак, динамическая память составляет 200...300 Кбайт или больше, ее начало хранится в переменной HEAPORG, а конец соответствует адресу переменной HEAPEND. Текущий адрес свободного участка динамической памяти хранится в указателе HEAPPTR.
Посмотрим, как можно использовать динамическую память для размещения крупных массивов данных. Пусть, например, требуется обеспечить доступ к элементам прямоугольной матрицы 100x200 типа EXTENDED. Для размещения такого массива требуется память 200000 байт (100*200*10).
Казалось бы, эту проблему можно решить следующим образом:
var
i,j : Integer;
PtrArr : array [1..100, 1...200] of ^Real;
begin
for i := 1 to 100 do
for j := 1 to 200 do
new(PtrArr[i,j]);
.......
end.
Теперь к любому элементу вновь созданного динамического массива можно обратиться по адресу, например:
PtrArr[1,1]^ := 0;
if PtrArr[i,j*2]^ > 1 then ......
Вспомним, однако, что длина внутреннего представления указателя составляет 4 байта, поэтому для размещения массива PTRARR потребуется 100*200*4 = 80000 байт, что превышает размер сегмента данных (65536 байт), доступный, как уже отмечалось, программе для статического размещения данных.
Выходом из положения могла бы послужить адресная арифметика, т.е. арифметика над указателями, потому что в этом случае можно было бы отказаться от создания массива указателей PTRARR. и вычислять адрес любого элемента прямоугольной матрицы непосредственно перед обращением к нему. Однако в Турбо Паскале над указателями не определены никакие операции, кроме операций присвоения и отношения.
Тем не менее, решить указанную задачу все-таки можно. Как мы уже знаем, любой указатель состоит из двух слов типа WORD, в которых хранятся сегмент и смещение. В Турбо Паскале определены две встроенные функции типа WORD, позволяющие получить содержимое этих слов:
SEG(X) - возвращает сегментную часть адреса;
OFS(X) - возвращает смещение.
Аргументом X при обращении к этим функциям может служить любая переменная, з том числе и та, на которую указывает указатель. Например, если имеем
var
р : ^Real;
begin
......
new(p);
р^ := 3.14;
.......
end.
то функция SEG(P) вернет сегментную часть адреса, по которому располагается 4-байтный указатель Р, в то время как SEG(P^) - сегмент 6-байтного участка кучи, в котором хранится число 3.14 .
С другой стороны, с помощью встроенной функции
PTR(SEG,OFS: WORD): POINTER
можно создать значение указателя, совместимое с указателями любого типа. Таким образом возможна такая последовательность действий. Вначале процедурой GETMEM из кучи забираются несколько фрагментов подходящей длины (напомню, что за одно обращение к процедуре можно зарезервировать не более 65521 байт динамической памяти). Для рассматриваемого примера удобно резервировать фрагменты такой длины, чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е. 100 * 10 = 2000 байт. Начало каждого фрагмента, т.е. фактически начало размещения в памяти каждой строки, запоминается в массиве PTRSTR, состоящем из 100 указателей, теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель:
var
i,j:Integer;
PtrStr : array [1..100] of pointer;
const
SizeOfReal = 6;
begin
for i := 1 to 100 do
GetMem (PtrStr [i] , SizeOfReal*200) ;
.......
{Обращение к элементу матрицы [i,j]}
pr := ptr(seg(PtrStr[i]^),
ofs(PtrStr[i]^)+(j-1)*SizeOfReal);
if рr^ > 1 then
.......
end.
Поскольку оператор вычисления адреса PR := PTR... будет, судя по всему, использоваться в программе неоднократно, полезно ввести вспомогательную функцию GETR, возвращающую значение элемента матрицы, и процедуру PUTR, устанавливающую новое значение элемента (правила объявления процедур и функций изложены в гл. 8). Каждая из них, в свою очередь, обращается к функции ADDRR для вычисления адреса. В примере 6.1 приводится программа, создающая в памяти матрицу из NxM случайных чисел и вычисляющая их среднее значение.
Пример 6.1
const
SizeOfReal = 6; {Длина переменной типа REAL}
N = 100; {Количество столбцов}
М = 200; {Количество строк}
var
i,j : Integer;
PtrStr: array [1..N] of pointer;.
s : Real ;
type
RealPoint =^Real;
{-------------------}
Function AddrR(i,j: word): RealPoint;
{По сегменту i и смещению j выдает адрес вещественной переменной}
begin
AddrR := ptr(seg (PtrStr [i]^),
ofs{ PtrStr [i]^) + (j -1) * SizeOfReal)
end {AddrR} ;
{-------------------}
Function GetR(i,j: Integer): Real;
{Выдает значение вещественной переменной по сегменту i и смещению j ее адреса}
begin
GetR := AddrR(i,j)^
end {GetR};
{-------------------}
Procepure PutR(i,j : Integer; x: Real);
{Помещает в переменную, адрес которой имеет сегмент i и смещение j, вещественное значение x}
begin
AddrR ( i , j )^ : = x
end {PutR};
{-------------------}
begin {Main}
for i :=1 to N do
begin
GetMem (PtrStr [i] , M*SizeOfReal) ;
for j := 1 to M do PutR(i, j, Random)
end;
S := 0;
for i := 1 to N do
for j := 1 to M do
s := s +GetR(i,j);
WriteLn(s/(N * М) :12:10)
end {Main} .
В рассмотренном примере предполагается, что каждая строка размещается в куче, начиная с границы параграфа, и смещение для каждого указателя PTRSTR равно нулю. В действительности при последовательных обращениях к процедуре GETMEM начало очередного фрагмента следует сразу за концом предыдущего и может не попасть на границу сегмента. В результате, при размещении фрагментов максимальной длины (65521 байт) может возникнуть переполнение при вычислении смещения последнего байта.