Мови програмування С, С - Стор 20

програми

Довідник по роботі з DOS

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

● Сегментні покажчики можуть присвоюватися, ініціалізуватися, передаватися до функцій та з функцій, порівнюватись, тощо. (Сегментні покажчики порівнюються за правилами для unsigned int). Інакше кажучи, крім перелічених вище обмежень, вони обробляються як і, як і будь-які інші покажчики.

Оголошення далеких об'єктів

Borland С++ дозволяє оголошувати далекі (far) об'єкти. Наприклад:

int far x = 5; int far z;

extern int far y = 4; static long j;

#pragma option zEmysegment zHmygroup zFmyclass int far x;

#pragma option zE* =zH* zF*

Таким чином, x буде поміщений у сегмент MYSEGMENT з класом MYCLASS у групі MYGROUP , після чого всі далекі об'єкти будуть скинуті у значення, що використовуються за умовчанням. Зазначимо, що при використанні цих параметрів можна помістити кілька далеких об'єктів в один сегмент:

#pragma option zEcombined zFmyclass int far x;

мови

Довідник по роботі з DOS

#pragma option zE* zF*

І x , і y опиняться у сегменті COMBINED 'MYCLASS' без групи.

Оголошення ближніх чи далеких функцій

У деяких випадках вам може знадобитися перевизначити значення типу функції для моделі пам'яті. Наприклад, ви використовуєте модель пам'яті large і в програмі є рекурсивна функція:

double power(double x,int exp)

if (exp power викликає сама себе, вона повинна виконати дальній виклик, причому використовується додатковий простір стека і число тактових циклів. Оголосивши power як near, можна прискорити виконання її завдяки тому, що виклики цієї функції будуть ближніми:

double __near power(double x,int exp)

Це гарантує, що функція power може викликатися тільки з того кодового сегмента, в якому вона компілювалася, і всі звернення до неї будуть ближніми.

Це означає, що при використанні великої моделі пам'яті (medium, large або huge) функцію power можна викликати лише з того модуля, в якому вона визначена. Інші модулі мають власні кодові сегменти і не можуть викликати функції near з інших модулів. Більш того, ближня функція до першого до неї звернення повинна бути визначена або оголошена, інакше компілятор не знає про необхідність генерувати ближній виклик.

мови

Довідник по роботі з DOS

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

Оголошення покажчиків near, far або huge

Щойно було розглянуто випадки, у яких може знадобитися оголосити функцію з іншою моделлю пам'яті, ніж решта програми. Навіщо те саме може знадобитися для покажчиків? З тих же причин, що й для функцій: або поліпшення характеристик швидкодії (оголосивши __near там, де за умовчанням було б __far ), або посилання за межі сегмента за умовчанням (оголосивши __far чи __huge там, де за умовчанням буває __near ).

void myputs(s) char *s;

for (i = 0; s [i]! = 0;i++) putc(s[i]);

char near * mystr;

mystr = "Hello, world\n"; myputs(mystr);

Однак, що станеться, якщо перекомпілювати цю програму з моделлю пам'яті compact (або large чи huge)? Покажчик mystr у функції main залишиться ближнім (16 бітовим). Однак, покажчик s функції myputs тепер буде далеким ( far ), оскільки за умовчанням тепер використовується far . Це означає, що спроба створення далекого покажчика приведе

програмування

Довідник по роботі з DOS

Як уникнути цієї проблеми? Рішення полягає в тому, щоб визначити myputs в сучасному стилі Сі:

void myputs (char * s)

Тепер при компіляції вашої програми Borland C++ знає, що на myputs чекає покажчик на char . Оскільки компіляція виконується з моделлю large , відомо, що покажчик має бути __far . Внаслідок цього Borland C++ помістить у стек регістр сегмента даних (DS) і 16 бітове значення mystr, утворюючи тим самим далекий покажчик.

Якщо ви збираєтеся явно оголошувати покажчики як far або near, не забувайте використовувати прототипи тих функцій, які можуть працювати з цими покажчиками.

Використання файлів бібліотеки

програмування

Довідник по роботі з DOS

Borland C++ пропонує кожній із шести моделей пам'яті власну версію бібліотеки стандартних підпрограм. Компілятор Borland C++ при цьому виявляє достатньо «інтелекту», щоб при подальшому компонуванні брати потрібні бібліотеки і в потрібній послідовності, залежно від вибраної моделі пам'яті. Однак, при безпосередньому використанні компонувальника Borland C++ TLINK (як автономного компонувальника) ви повинні явно вказувати бібліотеки, що використовуються.

Компонування змішаних модулів

Що станеться, якщо ви компілюєте одинмодуль із використанням моделі пам'яті small (мала), другий — моделі large (велика), а потім хочете скомпонувати їх? Що при цьому станеться?

Файли скомпонуються задовільно, але ви зіткнетесь з проблемами. Якщо функція модуля з моделлю small викликає функцію в модулі з моделлю large , вона використовуватиме ближній виклик, що дасть абсолютно невірні результати. Крім того, у вас виникнуть проблеми з покажчиками, оскільки функція в модулі small очікує, що вказівники, що приймаються і передаються, будуть __near , тоді як функція в модулі large очікує роботу з покажчиками __far .

І знову рішення полягає у використанні функцій прототипів. Припустимо, що ви помістили myputs в окремий модуль і скомпілювали його з моделлю large . Потім ви створюєте файл заголовка myputs.h (або з будь-яким іншим ім'ям та розширенням .h), який містить наступний прототип функції:

void far myputs (char far * s);

Тепер, якщо помістити функцію main в окремий модуль (MYMAIN.C) і виконати такі установки:

#include #include "myputs.h" main()

char near * mystr;

mystr = "Hello, world\n"; myputs(mystr);

програмування

Довідник по роботі з DOS

то при компіляції даної програми Borland C ++ вважає прототип функції з файлу MYPUTS.H і побачить, що це функція __far, що чекає покажчик __far. В результаті цього навіть при моделі пам'яті small при компіляції буде згенеровано правильний код, що викликає.

Як бути, якщо вам потрібно компонування з бібліотечними підпрограмами? Кращий підхід тут полягає в тому, щоб вибрати одну з бібліотек з моделлю large і оголосити все як far. Для цього зробіть копії всіх файлів заголовка, які ви зазвичай включаєте (таких,як stdio.h ) та перейменуйте ці копії (наприклад, fstdio.h ).

Відредагуйте копії прототипів функцій таким чином, щоб там було явно вказано far , наприклад:

int far cdecl printf(char far* format, . );

Тим самим, не тільки виклики підпрограм будуть дальніми, а й передавальні покажчики також будуть дальніми. Модифікуйте вашу програму таким чином, щоб вона включала новий заголовковий файл:

char near * mystr;

mystr = "Hello, world\n"; printf(mystr);

Скомпілюйте вашу програму за допомогою компілятора BCC, потім скомпонуйте її за допомогою утиліти TLINK, вказавши бібліотеки з моделлю large, наприклад CL.LIB.

Змішування модулів із різними моделями — річ екстравагантна, але допустима. Будьте готові до того, що будь-які неточності тут призводять до помилок, які дуже важко знайти і виправити при налагодженні.

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

програмування

Довідник по роботі з DOS

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

Оверлеї використовуються лише у 16 ​​розрядних програмах DOS. У програмах Windows для скорочення обсягу пам'яті, що використовується, ви можете позначити сегменти як Discardable (вивантажувані).

Робота програм із оверлеями

Програма управління оверлеями (VROOMM, або Virtual Run time Object Oriented Memory Manager) виконує за вас більшу частинуроботи з організації оверлєєв. У звичайних оверлейних системах модулі групуються в базовий та набір оверлейних модулів. Підпрограми в даному оверлейному модулі можуть викликати підпрограми з цього ж модуля і базового модуля, але не з інших модулів. Оверлейні модулі перекривають одне одного, тобто. одночасно в пам'яті може бути тільки один оверлейний модуль, і всі вони при активізації займають одну і ту ж ділянку фізичної пам'яті. Загальний обсяг пам'яті, необхідної для запуску цієї програми, визначається розміром базового плюс максимального оверлейного модуля. Ця нормальна схема не забезпечує достатньої гнучкості. Вона вимагає повного обліку всіх можливих звернень між модулями програми та, відповідно, запланованого вами, угруповання оверлєїв. Якщо ви не можете розбити вашу програму відповідно до взаємозалежності звернень між її модулями, то ви не зможете розбити її на оверлеї.

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

програмування

Довідник по роботі з DOS

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

Що відбувається, коли виникає потреба помістити сегмент у область свопінгу? Якщо цяобласть має досить вільного місця, то це завдання виконується просто. Якщо ж ні, то з області свопінгу, щоб вільна область, що шукається, звільнилася, повинен бути вивантажений один або більше сегментів. Як вибрати сегменти для розвантаження? Алгоритм, що діє тут, дуже складний. Спрощена версія така: якщо в області свопінгу є неактивний сегмент, то для вивантаження вибирається він. Неактивними вважаються сегменти, в яких в даний момент немає функцій, що виконуються. В іншому випадку береться активний сегмент. Видалення сегментів з пам'яті триває до тих пір, поки в області свопінгу не утворюється достатньо вільної пам'яті для розміщення необхідного сегмента. Такий метод називається динамічним свопінгом.

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

Після завантаження оверлея в пам'ять він поміщається в оверлейний буфер, який розташований у пам'яті між сегментом стека і далекою областю, що динамічно розподіляється. За замовчуванням розмір оверлейного буфера обчислюється і встановлюється під час завантаження програми, але можна змінити за допомогою глобальної змінної _ovrbuffer . Якщо достатній розмір пам'яті недоступний, то з'являється повідомлення про помилку DOS ("Program too big to fit in memory" - "Програма занадто велика для наявної пам'яті").

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

мови

Довідник по роботі з DOS

При використанні оверлєєв пам'ять розподіляється, як показано на наступному малюнку:

Розподіл пам'яті для оверлейних структур

Оптимальне використання оверлеїв Borland C++