- •Динамічний розподіл пам’яті Статичні і динамічні змінні
- •Покажчики Поняття покажчика
- •Розподіл динамічної пам’яті Породження динамічних об'єктів
- •Доступ до динамічних об'єктів
- •Обробка динамічних об'єктів
- •Знищення динамічних об'єктів
- •Методи динамічного розподілу пам’яті
- •Стандартні засоби аналізу стану Heap-області
- •Динамічний розподіл пам’яті
- •Динамічні структури даних
Динамічний розподіл пам’яті
Виділенням та звільненням динамічної пам’яті керують оператори new та delete.
Формати оператора new:
new <тип>;
new (<тип>);
new <тип>[<вираз>];
Вираз визначає розмір виділеної пам’яті заданого типу даних
int *r=new int;
float *r=new (float);
float *r=new float [20];
Якщо пам’ять виділена оператором new, то вона звільняється оператором
delete <покажчик> або delete []<покажчик>
Застосування delete до покажчика, отриманого не за допомогою new, не визначено.
Наприклад,
#include <stdio.h>
#include <conio.h>
main ( )
{ int *pi=new int;
*pi=30;
printf("adres %p, content %d\n",pi,*pi);
delete pi;
pi=new int[5];
for(int i=0;i<5;i++)
{ pi[i]=i*4;
printf("adres %p, content %d\n",pi[i],pi[i]);
}
delete[]pi;
getch();
}
Динамічні структури даних
Динамічні структури - структури даних, розмір яких (об'єм пам'яті, що вони займають) може змінюватись у процесі виконання програми. Для організації таких структур використовуються динамічні змінні, які створюються та знищуються в процесі виконання програми.
Списки
Важливим аспектом використання динамічних змінних є обробка спискових структур даних.
Список – це сукупність логічно зв'язаних елементів, кожен з яких окрім інформаційної частини містить посилання на наступний або (та) попередній елемент списку.
Списки представляють собою спосіб організації структури даних, при якій елементи деякого типу утворюють ланцюжок. Для зв'язування елементів у списку використовують систему покажчиків. У мінімальному випадку, будь-який елемент списку має один покажчик, який вказує на наступний елемент у списку або є порожнім покажчиком, що інтерпретується як кінець списку.
Розрізняють:
-
Лінійний однозв'язний список - структура, кожен елемент якої містить посилання, що зв'язує його з наступним елементом.
Різновиди:
-
Черга - структура, у якій елементи обробляються за принципом FIFO («першим прийшов - першим пішов»). Початок списку зберігається в окремій змінній (first). Критерієм закінчення списку є наявність у полі Next останнього елемента значення nil.
-
Стек - структура, у якій елементи обробляються за принципом LIFO («останнім прийшов - першим пішов»).
-
Лінійний двозв'язний список - структура, кожен елемент якої містить два посилання: на наступний і на попередній елементи списку.
-
Кільцевий список - структура, у якої останній елемент зв'язний з першим.
Дерева
Дерево (нелінійний список)- структура, кожен елемент якої зв'язний з декількома іншими.
Розрізняють
-
бінарні дерева – з кожним вузлом зв'язано два елемента;
-
збалансовані дерева – з кожним вузлом зв'язана однакова кількість елементів;
-
незбалансовані дерева – з кожним вузлом зв'язана різна кількість елементів.
Зазвичай, елементами списку є записи.
Оголошення списку може мати вигляд:
Type
TPtr = ^ Elem;
Elem = record
Inf : string [30];
Next : TPtr;
end;
Var First, Nov, Tek : TPtr;
Введення
У будь-якій обчислювальній системі пам'ять відноситься до таких ресурсів, яких завжди не вистачає. Управління пам'яттю - одна із головних турбот програміста, тому що для нього дуже важливо створювати програми, які ефективно використовують пам'ять, адже під час виконання програми пам'ять необхідна для наступних елементів програм і даних:
-
сама програма користувача;
-
системні програми часу виконання, які здійснюють допоміжні дії при роботі програми користувача;
-
визначені користувачем структури даних і константи;
-
точки повернення для програм;
-
тимчасова пам'ять для зберігання проміжних результатів при обчисленні виразів;
-
тимчасова пам'ять при передачі параметрів;
-
буфери вводу-виводу, що використовуються як тимчасові області пам'яті, в яких зберігаються дані між моментом їх реальної фізичної передачі з зовнішнього пристрою або на нього і моментом ініціалізації в програмі операції введення або виведення;
-
різні системні дані (інформація про статус пристроїв введення-виведення та ін.)
З цього переліку видно, що управління пам'яттю стосується широкого класу об'єктів.
У Turbo Pascal існує можливість прямого доступу до будь-якого байту оперативної пам'яті на його адресу за допомогою визначених у модулі system масивів Mem, MemW і MemL, які дозволяють записати інформацію або прочитати її безпосередньо з комірок пам'яті (один, два або чотири байти). Це дуже небезпечні дії, тому вони виключені в 32 - розрядних системах програмування. Все ж таки дамо короткі пояснення для тих, хто працює в середовищі Borland (Turbo) Pascal.
Наприклад, початкова адреса відеобуфера запишеться у вигляді $ B800: $ 000, а звернутися до самого першу його байту можна так: Mem [$ В800: $ 0000], до перших двох байтам - MemW [$ B800: $ 0000], до перших чотирьох байтам - MemL [$ B800: $ 0000]. Абсолютний адреса, що відповідає даній парі, -- $ B8000.
Ще один приклад для допитливих - оператор mem [0: $ 41C]: = mem [0: $ 41А]; можна застосувати для примусової очищення буфера клавіатури. Тут адреса маркера кінця буфера клавіатури прирівнюється до адреси його початку. Звичайно, в даному випадку краще скористатися засобами модуля crt.
Є ще один спосіб звернення до оперативної пам'яті - Використання службового слова absolute при описі змінної. У цьому випадку змінна буде розташовуватись саме за тією адресою в оперативній пам'яті, який вказаний після absolute. Зрозуміло, використання службового слова absolute - настільки ж небезпечний спосіб, як і звернення до пам'яті через зумовлені масиви.
Однак absolute може використовуватися і більше безпечним способом, дозволяючи поєднувати в пам'яті дві змінні з різними іменами.
Приклад. Дан текстовий файл розміром не більше 64 Кб, що містить дійсні числа, за одному в кожному рядку. Переписати вміст файлу в масив, розмістивши його в динамічно розподіляє пам'яті. Обчислити середнє значення елементів масиву. Очистити динамічну пам'ять. Створити цілий масив розміром 10000, заповнити його випадковими цілими числами в діапазоні від -100 до 100 і обчислити його середнє значення.
(Мова Turbo Pascal)
Program Srednee;
Const NMax = 10000;
Type Diapazon = 1 .. NMax;
MasInt = Array [Diapazon] Of Integer;
MasReal = Array [Diapazon] Of Real;
Var PIint: ^ MasInt; PReal: ^ MasReal;
I, Midint: longInt; MidReal: Real; T: Text; S: string;
Begin
Write ( 'Введіть ім'я файлу:'); ReadLn (S);
Assign (T, S); Reset (T); MidReal: = 0; MidInt: = 0;
Randomize;
NEW (PReal); (Виділення пам'яті під речовинний масив)
(Введення і підсумовування речового масиву)
While Not Eof (T) Do
Begin ReadLn (T, PReal ^ [I]); MidReal: = MidReal + PReal ^ [I] End;
DISPOSE (PReal); (Видалення речового масиву)
NEW (PInt); (Виділення пам'яті під цілий масив)
(Обчислення і підсумовування цілого масиву)
For I: = 1 To NMax Do
Begin PInt ^ [I] : = -100 + Random (201); MidInt: = MidInt + PInt ^ [I] End;
(Виведення середніх значень)
WriteLn ( 'середнє ціле одно:', MidInt Div NMax);
WriteLn ( 'середнє речовий одно:', (MidReal/NMax): 10: 6)
End.
// Мова C + +
# include
# include
# include
# include
# define NMax 10000
typedef int MasInt;
typedef float MasReal;
MasInt * PInt; MasReal * PReal;
int I, n, MidInt; float MidReal; char S [255];
FILE * t; char * endptr;
void main ()
(cout << "Введіть ім'я файлу:"; cin>> S;
t = fopen (S, "r ");
MidReal = 0; MidInt = 0;
randomize (); I = 0;
/* Виділення пам'яті під речовинний масив */
PReal = (MasReal *) malloc (sizeof (MasReal ));
/* Введення і підсумовування речового масиву */
while (! feof (t))
(fgets (S, 255, t);// вводимо з файлу рядок
PReal [I] = strtod (S, & endptr);// перетворимо введену рядок у дійсне число
MidReal + = PReal [I]; I ++;}
n = I +1;
free (PReal);/* Видалення речового масиву */
PInt = (MasInt *) malloc (sizeof (MasInt)); / * Виділення пам'яті під цілий масив */
/* Обчислення і підсумовування цілого масиву */
for (I = 0; I < NMax; I ++)
( PInt [I] = -100 + random (201);
MidInt + = PInt [I ];}
/* Висновок середніх значень */
cout << "nсреднее ціле одно" <<< "n";
cout << "середнє речовий одно: "<<<" n ";
fclose (t);
)
Функція Ptr перетворить свій аргумент-адресу в значення вказівника, наприклад:
P1:=Ptrt($1234,$4567);
При програмуванні на мові Pascal для розміщення динамічних змінних використовується область пам'яті з назвою Heap або «куча». Розмір Heap можна регулювати за допомогою директиви компілятору $М або задати при налагодженні середовища (Option/Compiler/Memory size) від 0 до 65535 байт.
Приклад директиви:
{$М 20000,10000,200000}
Перше число показує розмір сегмента стеку (від 1024 до 655356). Це число вибирається в залежності від наявності та виду підпрограм. Друге число визначає найменш допустимий в даній програмі розмір Heap, а останнє число - найбільший розмір цієї пам'яті. Конкретні розміри Heap вибираються в залежності від задачі та кількості динамічних даних.
Адреса задаются двумя 16-тиразрядными словами (тип word) - сегментом и смещением. Каждое из них способно адресовать 216=65536 байт (64 Кбайт). Для адресации пространства размером в 1 Мбайт требуется 20 разрядов. Сегменты адресуют память с точностью до параграфа - фрагмента памяти в 16 байт. Смещение адресует память с точностью до байта, но впределах сегмента. Реальный (абсолютный) адрес складывается из значения сегмента, сдвинутого на 4 разряда влево (умноженного на 16), и смещения. Фрагмент программы вычисления абсолютного адреса в реальном режиме:
Var Segment, Offset : word; Address : LongInt; . . . Address := Segment*16+Offset; |
Программа на Паскале получает один сегмент данных, поэтому область памяти, в которой могут быть размещены статические переменные Вашей программы, ограничена 64 Кбайтами. При попытке исполнить программу, требующую большего размера памяти, будет выдана ошибка: