Основы программирования — второй семестр 08-09; Михалкович С.С.; II часть
Лекция 3
Содержание
Указатели
Адрес
Оперативная память состоит из последовательный ячеек. Каждая ячейка имеет номер, называемый адресом.
В 32-битных системах можно адресовать 232 байт (<math>\approx \;</math> 4Гб) памяти, в 64-битных — 2 64 соответственно.
Переменная (или константа), хранящая адрес, называется указателем.
Для чего нужны указатели
Указатели повышают гибкость доступа к данным:
- Вместо самих данных можно хранить указатель на них. Это позволяет хранить данные в одном экземпляре и множество указателей на эти данные. Через разные указатели эти данные можно обновлять (пример — корпоративная БД).
- Указателю можно присвоить адрес другого объекта (вместо старого появился новый телефонный справочник).
- С помощью указателей можно создавать сложные структуры данных.
Подробнее об указателях
Указатели делятся на:
- Типизированные (указывают на объект некоторого типа)
Имеют тип: ^<тип>
Пример. ^integer — указатель на integer - Бестиповые (хранят адрес ячейки памяти неизвестного типа)
Преимущество: могут хранить что угодно
Имеют тип: pointer
Пример кода.
var
i: integer := 5;
r: real := 6.14;
pi: ^integer;
pr: ^real;
begin
pi := @i;
pr := @r;
pi := @r; // ОШИБКА компиляции
end.
@ — унарная операция взятия адреса <xh4>Операция разадресации (разыменования)</xh4>
var
i: integer := 5;
pi: ^integer;
begin
pi := @i;
pi^ := 8 - pi^;
writeln(i); // 3
end.
^ — операция разыменования
pi^ — то, на что указывает pi, т.е. другое имя i или ссылка на i.
Тут надо вспомнить определение ссылки:
Ссылка — другое имя объекта.
<xh4>Нулевой указатель</xh4>
Все глобальные неинициализированные указатели хранят специальное значение nil, что говорит о том, что они никуда не указывают.
Указатель, хранящий значение nil называется нулевым.
var
pi: ^integer; //указатель pi хранит значение nil
i: integer;
begin
pi := @i; //pi хранит адрес переменной i
pi := nil; //pi снова никуда не указывает
pi^ := 7; //ОШИБКА времени выполнения:
//попытка разыменовать нулевой указатель
Попытка разыменовать нулевой указатель приводит к ошибке времени выполнения. <xh4>Бестиповые указатели</xh4>
var
p: pointer;
i: integer;
begin
p := @i;
end.
Бестиповому указателю можно присвоить адрес переменной любого типа, т.е. бестиповой указатель совместим по присваиванию с любым типовым указателем.
Попытка разыменовать бестиповой указатель приводит к ошибке компиляции. Т.е. он может только хранить адреса.
Оказывается, любой типизированный указатель совместим по присваиванию с бестиповым, т.е. следующий код верен:
var
pi: ^integer;
i: integer;
p: pointer;
begin
p := @i;
pi := p;
pi^ += 2;
end.
Вопрос. Нельзя ли интерпретировать память, на которую указывает p, как принадлежащую к определенному типу?
Ответ — да, можно. Вот как это сделать:
type
pinteger = ^integer;
var
i, j: integer;
p: pointer;
begin
p := @i;
pinteger(p)^ := 7; //используем явное приведение типа
writeln(i); // 7
end.
Запись
<тип>( <переменная> )
показывает, что используется явное приведение типов.
Внимание! Неконтролируемая ошибка!
type
pinteger = ^integer;
var
i, j: integer;
p: pointer;
begin
p := @i;
preal(p)^ := 3.14; //ОШИБКА!
end.
Область памяти, на которую указывает p трактуется как область, хранящее вещественное число (8 байт), и потому константа 3.14 записывается в эти 8 байт. Однако, переменная i занимает только 4 байта, поэтому затираются еще 4 соседних байта (в данном случае они принадлежат переменной j).
Доступ к памяти, имеющей другое внутреннее представление
Динамическая память. Явное выделение динамической памяти. Процедуры New и Dispose.
Ошибки при работе с динамической памятью
- Использование неинициализированного указателя
- Висячие указатели
- Утечка памяти
<xh4></xh4>
Классы-начало
Переменная типа класс как ссылка. Сравнение с записями.
Вызов конструктора и выделение динамической памяти.
Шаблоны классов.
Решение проблемы освобождения памяти, занимаемой объектами классов: сборка мусора (.NET, Java).
Управляемая динамическая память и ее возврат. Отсутствие утечки памяти.
Динамические структуры данных. Списки
Виды списков. Рисунки.
Односвязные линейные списки
Класс узла списка (шаблонный)
Стандартные операции с односвязными линейными списками
- Вставка элемента в начало
- Удаление элемента из начала
- Вставка после текущего
- Удаление после текущего
- Проход по списку
Двусвязные линейные списки
Стандартные операции с двусвязными линейными списками
- Инициализация
- Добавление в начало, конец
- Удаление из начала, конца
- Вставка элемента перед текущим, после текущего
- Удаление текущего
- Объединение двух списков
Помещение операций по работе со списком внутрь класса