Новости

динамічний масив

  1. Підтримка в мовах програмування [ правити | правити код ]
  2. Масиви змінної довжини [ правити | правити код ]
  3. Переміщення масиву в пам'яті [ правити | правити код ]
  4. Інші динамічні алгоритми розміщення [ правити | правити код ]
  5. Паскаль [ правити | правити код ]
  6. Сі [ правити | правити код ]
  7. С ++ [ правити | правити код ]

динамічним називається масив , Розмір якого може змінюватися під час виконання програми. Можливість зміни розміру відрізняє динамічний масив від статичного, розмір якого задається на момент компіляції програми. Для зміни розміру динамічного масиву мова програмування , Що підтримує такі масиви, повинен надавати вбудовану функцію або оператор. Динамічні масиви дають можливість більш гнучкої роботи з даними, так як дозволяють не прогнозувати збережені обсяги даних, а регулювати розмір масиву відповідно до реально необхідними обсягами.

Також іноді до динамічних відносять масиви змінної довжини, розмір яких не фіксується при компіляції, а задається при створенні або ініціалізації масиву під час виконання програми. Від «справжніх» динамічних масивів вони відрізняються тим, що для них не надаються кошти автоматичної зміни розміру зі збереженням вмісту, так що при необхідності програміст повинен реалізувати такі кошти самостійно.

Підтримка в мовах програмування [ правити | правити код ]

Динамічні масиви можуть підтримуватися або на рівні синтаксису самої мови програмування, або на рівні системних бібліотек. Опис динамічного масиву може синтаксично відрізнятися від опису статичного, але може і бути таким же. У другому випадку, як правило, все масиви в мові є потенційно динамічними, і тільки від програміста залежить, чи використовувати цю властивість в кожному конкретному випадку. За підтримки динамічних масивів за допомогою системних бібліотек вони зазвичай реалізуються у вигляді класів (в сенсі ООП ) або узагальнених типів (Так званих «шаблонів» або «дженериків»); опис масиву в цьому випадку представляє собою інстанцірованія класу або конкретизацію узагальненого типу. Залежно від мови, динамічні масиви можуть бути тільки одновимірними масивами або мати розмірність два і більше.

Підтримка динамічних масивів передбачає обов'язкову наявність вбудованої операції визначення поточного розміру масиву. Початковий розмір динамічного масиву або дорівнює нулю, або задається при описі чи ініціалізації. Подальші зміни розміру виконуються спеціальної вбудованої операцією (процедурою). У деяких мовах (наприклад, JavaScript , Lua ) Розширення масиву відбувається автоматично, коли робиться спроба запису в неіснуючу комірку.

Для масивів з можливістю динамічної зміни розміру при реалізації доводиться знаходити «золоту середину» між кількома суперечливими вимогами.

  1. Ефективність по пам'яті - реалізація не повинна приводити до істотного перевитрати пам'яті.
  2. Ефективність по продуктивності, яка включає в себе:
    • мінімальні накладні витрати на зміну розміру масиву;
    • збереження, по можливості, константного часу доступу на читання / запис до елементів масиву.
  3. Сумісність зі звичайними статичними масивами на низькому рівні. Наприклад, необхідною умовою для застосування динамічного масиву у викликах функцій API операційної системи може бути обов'язкове розміщення елементів масиву в безперервному блоці фізичної оперативної пам'яті в порядку, відповідному індексації масиву. Якщо така вимога не виконується, динамічні масиви виявиться неможливо використовувати в поєднанні з низькорівневим кодом.

Залежно від пріоритету тих чи інших вимог вибирається відповідає їм спосіб реалізації.

Масиви змінної довжини [ правити | правити код ]

Реалізація масивів змінної довжини мало відрізняється від реалізації звичайних статичних масивів. Масив елементів типу T змінної довжини зазвичай зберігається в безперервному блоці оперативної пам'яті розміром s i z e o f (T) ⋅ N {\ displaystyle sizeof (T) \ cdot N} Реалізація масивів змінної довжини мало відрізняється від реалізації звичайних статичних масивів , Де N - число елементів, що визначене при описі (створення) масиву. Тобто опис масиву змінної довжини, фактично, просто маскує динамічне виділення пам'яті. Різниця може полягати в тому, що (наприклад, як в Сі стандарту C99 і пізніше) масиву змінної довжини виділяється пам'ять на стеку, як іншим автоматичним змінним, в той час як типовий спосіб динамічного виділення пам'яті (в Сі - функція malloc) виділяє пам'ять в купі . Крім того, для масиву змінної довжини компілятор, як правило, автоматично генерує код звільнення пам'яті при виході оголошеного масиву з області видимості.

Переміщення масиву в пам'яті [ правити | правити код ]

Найбільш поширеною для типових процедурних компільованих мов є реалізація зміни розміру масиву шляхом переміщення його в динамічної пам'яті.

  • Під масив виділяється фрагмент ОЗУ, розмір якого більше необхідного т. Н. логічного розміру (size або length). Кількість елементів, яке фактично може бути розміщено в цій пам'яті, називається ємністю (capacity) динамічного масиву. Поточна довжина масиву зберігається в окремому лічильнику. Вона може використовуватися для визначення поточного розміру, а також для контролю виходу за межі масиву, якщо мова підтримує такий контроль. Таким чином, в дійсності динамічний масив - це масив фіксованого розміру, в якому зайнята тільки частина осередків.
  • Команда збільшення розміру масиву, якщо новий розмір не перевищує ємності, просто змінює лічильник довжини масиву до потрібного розміру. З самим масивом ніяких змін при цьому не відбувається.
  • Команда збільшення розміру, в якій новий розмір перевищує ємність, призводить до переміщення масиву в пам'яті:
  1. виділяється новий фрагмент ОЗУ, розмір якого перевищує розмір масиву;
  2. вміст масиву копіюється у знову виділену пам'ять;
  3. розмір і ємність масиву змінюються встановлюються в нові значення;
  4. у службовій структурі, що зберігає дані масиву, значення покажчика на дані масиву змінюється на нове;
  5. запускається команда звільнення раніше виділеної під масив фрагмент ОЗУ; якщо мова підтримує автоматичне керування пам'яттю , То звільнення раніше виділеної під масив пам'яті зазвичай залишається за збирачем сміття.
  • Команда зменшення розміру масиву може призводити до переміщення його в пам'яті, якщо в результаті її виконання відсоток зайнятих осередків в масиві падає нижче певного значення. Переміщення при цьому проводиться за тією ж схемою, що і в разі команди збільшення масиву.

Дана реалізація є найбільш ефективною за швидкістю доступу до вже виділених клітинок масиву. Крім того, вона забезпечує константне час доступу до будь-якого елементу, незалежно від його індексу. Однак додаткові накладні витрати на операції зміни розміру можуть виявитися значними. Величина цих витрат залежить від параметрів алгоритму переміщення масиву.

  • Початкова місткість і збільшення ємності при збільшенні розміру. Може здаватися або відносною величиною (певна частка від логічної довжини масиву), або абсолютним приростом понад необхідної довжини масиву. Чим більше ці параметри, тим пізніше, при тому ж режимі заповнення масиву, потрібно наступне переміщення, але і тим більше пам'яті потенційно залишиться невикористаної. Розширення масиву на будь-який постійний коефіцієнт гарантує, що вставка n-елементів займе O (n) часу, це означає, що кожна вставка займає конкретне, певний час. Чисельне значення цього коефіцієнта призводить до різними показниками: середній час вставки операції становить a / (a-1), в той час як число витрачених даремно осередків становить (a-1) n. Значення цієї константи в різних додатках і бібліотеках не завжди однаковий: у багатьох підручниках використовують значення 2, але в реалізації ArrayList мови Java використовується коефіцієнт 3/2, в деяких інших випадках використовують a = 9/8.
  • Мінімальна ємність. Зазвичай задається з міркувань ефективності, щоб не втрачати продуктивність при частих змінах розмірів невеликих масивів. Практично її установка означає, що всі динамічні масиви розміром менше мінімальної ємності в дійсності будуть втрачати пам'ять.
  • Відсоток мінімального заповнення масиву. Визначає, коли буде відбуватися переміщення після скорочення розміру масиву. Чим більше ця величина, тим частіше при зменшенні розміру масиву буде відбуватися переміщення. Чим вона менша, тим більше пам'яті при зменшенні розміру масиву буде залишатися зайнятої, але не використовується.

Існують різні оцінки оптимальних величин для параметрів алгоритму переміщення, але з загальних міркувань ясно, що жоден із способів їх визначення не гарантує максимальної ефективності в усіх випадках і для будь-якого можна підібрати алгоритм роботи з масивом, при якому переміщення буде неефективним. Для компенсації негативних ефектів багатьма мовами, які підтримують динамічні масиви, в командах збільшення / зменшення масиву мають додаткові параметри, що дозволяють прямо управляти ємністю масиву. Якщо програміст свідомо знає, до яких розмірів збільшиться / зменшиться масив в результаті тієї чи іншої операції, і як надалі буде відбуватися робота з масивом, він має можливість прямо вказати необхідну кінцеву ємність в команді зміни розміру, уникнувши таким чином великої кількості безглуздих переміщень.

Інші динамічні алгоритми розміщення [ правити | правити код ]

Існує безліч алгоритмів реалізації динамічних масивів, крім вищеописаного. Так, можлива реалізація за допомогою динамічних елементів пам'яті, пов'язаних посиланнями, інших різних структур даних. Більшість таких методів забезпечують перевагу в якихось певних умовах, але або не забезпечує константного часу доступу, або несумісне зі статичними масивами і тому не може працювати з низькорівневим кодом.

Динамічні масиви використовуються для обробки наборів однорідних даних, розмір яких невідомий точно на момент написання програми, але які потенційно можуть розміститися в доступній пам'яті. Без використання динамічних масивів рішення таких задач зводиться до однієї зі стратегій:

  • виділення статичного масиву великого розміру, свідомо достатнього для повного розміщення даних;
  • використання статичного масиву в якості буфера, в який дані завантажуються частинами;
  • застосування інших динамічних структур даних (наприклад, списків).

Перший варіант застосовується лише коли розмір набору даних змінюється в невеликому, жорстко обмеженому діапазоні (наприклад, в текстовій обробці обмеження в 1000 знаків на рядок цілком прийнятно). В іншому випадку вибір маленького масиву буде обмежувати функціональність програми, а великого (так, щоб свідомо вистачило для будь-яких вхідних даних) призведе до неефективного витрачання пам'яті. Буферизація обробки ускладнює алгоритм, а інші динамічні структури в задачах обробки великих послідовностей простих даних по ефективності не можуть зрівнятися з масивом.

Використання динамічних масивів дозволяє виділити рівно стільки пам'яті, скільки реально необхідно (відразу, якщо завдання дозволяє визначити обсяг до завантаження даних, або в процесі завантаження, розширюючи масив у міру необхідності), завантажити всі дані в один масив і одноманітно їх обробити. Недоліки, однак, є і у цієї стратегії:

  • зниження швидкості роботи через накладних витрат на зміну розміру динамічного масиву;
  • потенційне зниження надійності: при екстремально великому обсязі вхідних даних спроба збільшити масив до відповідних розмірів може привести до раптового істотного уповільнення або навіть відмови програми через брак вільної пам'яті.

В цілому можна помітити, що підтримка динамічних масивів підвищує зручність розробки, але не звільняє програміста від необхідності проводити оцінку потреб програми в пам'яті; також вона вимагає розуміння особливостей конкретної реалізації динамічних масивів і узгодження алгоритмів обробки даних з цими особливостями.

Паскаль [ правити | правити код ]

Динамічні масиви підтримуються Delphi , FreePascal , але не Turbo Pascal .

var byteArray: Array of Byte; // Одновимірна масив multiArray: Array of Array of string; // Багатомірний масив ... begin ... // Встановити розмір одновимірного масиву в n елементів SetLength (byteArray, n); // Доступ до динамічного масиву аналогічний доступу до звичайного. // Індексація завжди починається з нуля, індекси - завжди цілі. byteArray [0]: = 10; // Змінити розмір до m елементів. SetLength (byteArray, m); ... // Встановити розмір двовимірного масиву в X * Y елементів SetLength (multiArray, X, Y); muliArray [7, 35]: = 'ru.wikipedia.org'; ... end.

Сі [ правити | правити код ]

У самій мові Сі немає динамічних масивів, але функції стандартної бібліотеки malloc, free і realloc дозволяють реалізувати масив змінного розміру:

int * mas = (int *) malloc (sizeof (int) * n); // Створення масиву з n елементів типу int ... mas = (int *) realloc (mas, sizeof (int) * m); // Зміна розміру масиву з n на m зі збереженням вмісту ... free (mas); // Звільнення пам'яті після використання масиву

Незручність такого підходу полягає в необхідності обчислювати розміри виділеної пам'яті, застосовувати явне перетворення типу і ретельно відслідковувати час життя масиву (як і завжди при роботі з динамічно виділеною пам'яттю в Сі).

Багатовимірний динамічний масив може бути створений як масив покажчиків на масиви:

int ** A = (int **) malloc (N * sizeof (int *)); for (int i = 0; i <N; i ++) {A [i] = (int *) malloc (M * sizeof (int)); }

Однак зростання розмірності істотно ускладнює процедури створення масиву і звільнення пам'яті після завершення його використання. Ще більш ускладнюється завдання зміни розміру масиву по одній або декількох координатах.

Деякі компілятори Сі надають нестандартну бібліотечну функцію void * alloca (size_t size), кілька спрощує роботу з динамічними масивами. Ця функція виділяє пам'ять не в купі, як malloc, а на стек, і ця пам'ять автоматично звільняється при досягненні оператора return. Тобто при виділенні пам'яті динамічного масиву цією функцією його не потрібно видаляти вручну, але такий масив неможливо повернути з функції в точку виклику.

Починаючи з версії стандарту C99 в мову введено масиви змінної довжини. У звичайному синтаксисі опису масиву Сі на місці розміру масиву може вказуватися не тільки константа, але і змінна цілого типу:

void func (int arraySize) {int mas [arraySize]; // Опис масиву змінної довжини for (int i = 0; i <arraySize; ++ i) {mas [i] = anotherFunc (i); // Звернення до елементів масиву} ...}

Масив змінної довжини може отримати будь-який необхідний розмір в момент створення. Пам'ять під нього виділяється на стеку. Масив змінної довжини існує до виходу з області видимості, в якій він був оголошений, після чого його пам'ять автоматично звільняється. Як і в попередньому випадку, масив змінної довжини неможливо повернути з функції в точку виклику.

С ++ [ правити | правити код ]

У C ++ підтримуються функції роботи з пам'яттю зі стандартної бібліотеки Сі, але їх використання не рекомендується. Масив змінної довжини тут також можна виділити за допомогою стандартних команд роботи з динамічною пам'яттю new і delete:

// Створення масиву довжиною n int * mas = new int [n]; ... // Звільнення пам'яті масиву delete [] mas;

Як і у випадку з Сі, тут потрібно відстежувати час життя масиву, щоб уникнути витоку пам'яті або, навпаки, передчасного звільнення пам'яті. Аналога realloc тут немає, так що змінити розмір масиву можна тільки вручну, виділивши нову пам'ять потрібного розміру і перенісши в неї дані.

Бібліотечним рішенням є шаблонний клас std :: vector:

std :: vector <int> mas; // Створити порожній вектор mas. reserve (100); // Виділити вектору пам'ять під 100 елементів (розмір залишиться нульовим) mas. resize (50); // Задати розмір вектора в 50 елементів mas [i] = i; // Звернення до елементу за індексом mas. push_back (100); // Додати елемент x = mas. back; // Звернення до останнього елемента mas. pop_back (); // Видалити останній елемент std :: cout << mas. size () << "" << mas. capacity () << "\ n"; // Вивести ємність і розмір mas. shrink_to_fit (); // Зменшити ємність до розміру

std :: vector має безліч методів та перевизначених операторів, частина з яких показана вище на прикладі. Вони дозволяють звертатися за індексом, змінювати в будь-яку сторону розмір масиву, використовувати його як стек. Керованим є не тільки актуальний розмір, але і ємність вектора, що дозволяє оптимізувати процес виділення пам'яті. Стандарт C ++ вимагає від реалізації обов'язкового виконання умов:

  • всі елементи вектора повинні зберігатися поспіль в порядку збільшення індексу в цілісному блоці оперативної пам'яті;
  • має бути гарантовано константне час доступу до елементу вектора.

Уважаемые партнеры, если Вас заинтересовала наша продукция, мы готовы с Вами сотрудничать. Вам необходимо заполнить эту форму и отправить нам. Наши менеджеры в оперативном режиме обработают Вашу заявку, свяжутся с Вами и ответят на все интересующее Вас вопросы.

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: