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.

src/SimpleAllocator.cpp
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;
  }
  
Úkol k textu
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.

src/StackAllocator.cpp
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íÚ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?