5.1. Přidělování na zásobníku
Nejjednodušší metodou přidělování paměti je přidělování z jediného souvislého bloku
paměti, organizovaného jako zásobník. Pro přidělení potřebujeme mít k dispozici
pouze adresu začátku a konce volné paměti. Každému požadavku na přidělení paměti
je přiřazena aktuální adresa začátku volné paměti a tato adresa je pak zvýšena
o velikost požadavku, přičemž se hlídá překročení limitu volné paměti. Uvolňování
paměti je pak implementováno jako prázdná operace, případně je možné bloky paměti
uvolňovat vždy v opačném pořadí než v jakém byly přiděleny. Při uvolňování paměti
je ukazatel začátku volné paměti opět vrácen zpět o velikost uvolňovaného bloku.
 | Příklad 5.1. Přidělování paměti na zásobníku |
|
Následující příklad ukazuje implementaci přidělování paměti z jediného souvislého
bloku, přičemž operace uvolňování je implementována jako prázdná:
class SimpleAllocator {
// Vytvoří objekt typu SimpleAllocator, přidělující volnou paměť z bloku
// na adrese memAddr o velikosti memSize
public SimpleAllocator(char* memAddr, unsigned memSize)
{
m_addr = memAddr;
m_size = memSize;
}
// Přidělí blok paměti o velikosti size a vrátí jeho adresu.
// Není-li požadovaný prostor k dispozici, aktivuje výjimku NoMemoryException
public char* alloc(unsigned size)
{
if( size > m_size )
throw new NoMemoryException();
char* addr = m_addr;
m_addr += size;
return addr;
}
// Uvolnění bloku paměti je prázdná operace.
public void free(char* addr, unsigned size) {}
// Aktuální začátek volné paměti
protected char* m_addr;
// Aktuální velikost volné paměti
protected unsigned m_size;
}
|
 |
Upravte implementaci funkce free tak, aby umožnila vrácení naposledy
přiděleného bloku paměti. Zamyslete se nad tím, jak by se dala kontrolovat podmínka, že
se bloky mohou uvolňovat pouze v obráceném pořadí jejich přidělování.
|
Metoda přidělování ze souvislého bloku paměti je velmi rychlá, a proto se často používá
například ve funkci subalokátoru pro přidělování paměti v rámci bloku
získaném jinou metodou. Můžeme se s ní také setkat v programovacích jazycích jako je C,
C++ nebo Pascal při přidělování paměti pro aktivační záznamy funkcí (obsahující lokální
proměnné, návratovou adresu a předávané parametry volání). Právě zde je totiž zajištěno to,
že se přidělené bloky uvolňují v obráceném pořadí, a to vždy při návratu z volání funkce.
Jednou z modifikací této metody je zavedení operací mark a
release. Operace mark uloží aktuální
hodnotu ukazatele začátku volné paměti do proměnné a operace release
tento ukazatel z proměnné opět obnoví. Tímto způsobem lze zajistit uvolnění veškeré
přidělené paměti od okamžiku, kdy byla provedena odpovídající operace mark.
 | Příklad 5.2. Přidělování paměti s metodami mark a release |
|
Výše uvedenou třídu SimpleAllocator můžeme rozšířit o operace
mark a release takto:
class StackAllocator: public SimpleAllocator
{
// Vytvoří objekt typu StackAllocator, přidělující volnou paměť z bloku
// na adrese memAddr o velikosti memSize
public StackAllocator(char* memAddr, unsigned memSize): SimpleAllocator(memAddr, memSize) {}
// Poznamená aktuální pozici
public void mark(char* &tr)
{
ptr = m_addr;
}
// Obnoví poznamenanou pozici
public void release(char* ptr)
{
m_addr = ptr;
}
}
|
|
|
 | Úkol k zamyšlení |
|
Jakým způsobem by bylo možné kontrolovat, zda byla adresa předaná funkci release
získána voláním funkce mark? Jaké další situace by mohly vést k nesprávné funkci
takto implementovaného přidělování paměti?
|
|
|