- •Адреси та вказівники
- •Вільна пам'ять
- •Динамічні масиви
- •Поняття динамічного лінійного списку
- •If not (s належить sqs) then
- •V : string;
- •Створення та обробка списку
- •Var p : pElem; begin
- •Var f:text; {вхідний текст}
- •If not isIn(s,h) {якщо рядка в списку немає}
- •Var sqs:pElem; {вказівник на голову списку}
- •Вилучення елемента списку
- •Var p:pElem; {допоміжний вказівник}
- •Var p:pElem; {допоміжний вказівник}
Поняття динамічного лінійного списку
Лінійні списки у вільній пам'яті та найпростіші операції з ними (додавання й вилучення елементів) розглянемо за допомогою задачі.
Задача.
Кожен рядок текстового файлу є прізвищем; прізвища можуть повторюватися. Треба прочитати текст і надрукувати кожне прізвище по одному разу. Порядок прізвищ не має значення.
Пояснення до розв'язання
Позначимо текстовий файл ім'ям f, черговий рядок — s, а послідовність прочитаних рядків — sqs. Спочатку sqs порожня — позначимо це символами < >. Алгоритм розв'язання очевидний:
підготувати f до обробки
sqs : = < >;
while not eof(f) do begin
readln(f,s)
If not (s належить sqs) then
додати s до sqs;
end;
надрукувати елементи sqs.
Умова задачі не обмежує кількості елементів у послідовності sqs, тому для її збереження масив непридатний. Використаємо лінійний зв'язаний список рядків у вільній пам'яті. Елементами списку є структури (записи) такого типу.
-
Рядок
Вказівник на наступний елемент
Ці структури утворюють послідовність, подану на рис. 3. Перший елемент називається головним елементом, або головою. Поле-вказівник кожного елемента списку (крім останнього) визначає наступний елемент, «прив'язуючи» його до попереднього. За останнім елементом списку наступного немає, тому його вказівник має значення nil.
Рис. 3. Зв'язаний список рядків
• Якщо розірвати зв'язок із попереднім елементом, змінивши значення вказівника в ньому, втрачається посилання на наступний елемент списку. Цей елемент і всі після нього стають недоступними у програмі, тобто «сміттям» у вільній пам'яті.
• Для доступу до першого елемента й усього списку потрібен вказівник на голову списку, оголошений у програмі й розташований у її статичній або автоматичній пам'яті.
Розглянемо оголошення й операції, потрібні для обробки списку.
Елементи списку є структурами, в яких одне поле є вказівником на структури цього ж типу. Що оголосити спочатку — тип структур чи тип вказівників на них? Вказівники будь-якого типу мають розмір 4 байти, тому спочатку оголошується тип вказівників на структури, а потім — тип структур.
type Pstr=^Lstr; (тип вказівників}
Lstr=record {тип елементів списку рядків}
V : string;
next : Pstr;
end;
Елемент списку, на який встановлено вказівник р типу Pstr, позначається виразом р^. Вирази р^. vi р^. next позначають поля елемента — рядок типу string і адресу наступного елемента типу Pstr.
Проте список рядків або інших даних, особливо великого розміру, краще організувати інакше. Замість даних, наприклад рядків, елемент списку містить вказівник на них, а самі вони зберігаються в купі окремо (рис. 4).
Рис. 4. Список вказівників на рядки
Така організація особливо зручна, якщо дані в списку треба обмінювати місцями.
Наприклад, нехай список має елементи типу Lstr, оголошеного вище. На два елементи списку встановлено вказівники р1 і р2 типу Pstr, і ці елементи треба поміняти місцями. Для цього можна поміняти місцями рядки, як у таких операторах (s — допоміжна змінна типу string).
s:=pl^.v; pl^.v:=p2^.v; p2^.v:=s
Проте тут тричі переміщуються рядки розміром по 256 байт, а таких переміщень краще уникати.
Оголосимо типи по-іншому.
type Pstring=^string; {тип вказівників на рядки} PElem=^Elem; {та на елементи списку}
Elem=record {тип елементів списку)
pval: Pstring;
next: Pelem;
end;
За цих оголошень, якщо вказівник р типу PElem установлено на елемент списку, то вирази р^ .pval і р^.next позначають поля елемента — вказівники на рядок і на наступний елемент типу PElem, а р^. pval^ — сам рядок.
Якщо вказівники рі і р2 типу PElem встановлено на два елементи списку, то для обміну рядків досить обміняти 4-байтові вказівники на рядки (р — допоміжний вказівник типу Pstring).
p:=pl^.pval;
pl^.pval:=р2^.pval;
p2^.pval := р;
Оголошення типів Pstring, PEelem і Elem використовуються нижче.