3.5. Práce s nepřemístitelnými bloky paměti
Manipulace s nepřemístitelnými bloky je jednoduchá. Pomocí funkce MemPtrNew() požádáme operační systém o přidělení bloku paměti požadované velikosti. Pokud tato funkce vrátí adresu bloku paměti, byl v dynamické paměti inicializován nepřemístitelný blok paměti dané velikosti, patřící naší aplikaci, a tento blok můžeme okamžitě používat. Po skončení práce s blokem paměti jej musíme vrátit operačnímu systému voláním funkce MemPtrFree().
MemPtr MemPtrNew(UInt32 size) Požadavek na přidělení bloku nepřemístitelné paměti zadané velikosti. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Vrátí-li funkce MemPtrNew() nenulovou hodnotu, bylo volání úspěšné a blok paměti můžeme používat. Datový typ MemPtr je definován podobně jako typ void* v jazyce C - jedná se o obecnou adresu v paměti.
Err MemPtrFree(MemPtr blok) Vrátí přidělený blok paměti zpět operačnímu systému. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Manažer paměti PalmOS udržuje přehled i o tom, kterým aplikacím jednotlivé bloky paměti patří. Pokud při ukončení aplikace zjistí, že tato aplikace operačnímu systému nevrátila všechny bloky paměti, tak zbývající bloky paměti uvolní pro potřeby dalších aplikací sám. Přesto však patří k dobrým zvyklostem programátorů vrátit na konci aplikace operačnímu systému všechny bloky paměti, které aplikace od systému získala.
UInt32 MemPtrSize(MemPtr blok) Zjistí velikost nepřemístitelného bloku paměti. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Pomocí funkce MemPtrSize() můžeme zjistit velikost nepřemístitelného bloku paměti, známe-li jeho adresu. Je tedy možné napsat například následující fragment programu, který vyplní celý blok střídavě nulami a jedničkami
MemPtr blok = MemPtrNew(DELKA_BLOKU); for(i = 0; i < MemPtrSize(blok); i++) { ((Char*)blok)[i] = i & 0x01; } ... MemPtrFree(blok); |
Pokud si přejeme změnit velikost již získaného nepřemístitelného bloku paměti, můžeme použít funkci MemPtrResize(). Pokud si přejeme blok zvětšit, musíme počítat s tím, že se to nepodaří. Naopak pokud bloky paměti zmenšujeme, je tato funkce vždy úspěšná.
Err MemPtrResize(MemPtr blok, UInt32 newSize) Změní velikost nepřemístitelného bloku paměti. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Vrátí-li funkce MemPtrResize() nulovou hodnotu, tak byla velikost bloku úspěšně změněna. Na rozdíl od funkce realloc() jazyka C/C++ je původní adresa bloku, jehož velikost touto funkcí měníme, stále platná.
Pokud plánujeme v naší aplikaci používat bloky, jejichž velikost budeme měnit, je vhodnější používat bloky přemístitelné.
Typický příklad použití funkcí MemPtrNew() a MemPtrFree() pro získání velkého pole je v následujícím rámečku. Nesmíme nikdy zapomenout na kontrolu, zda je hodnota vrácená funkcí MemPtrNew() nenulová. S nedostatkem paměti a tedy nemožností alokovat blok paměti se na starších modelech počítačů Palm Pilot a kompatibilních můžeme setkat docela často.
UInt16* pole = (UInt16*)MemPtrNew(DELKA * sizeof(UInt16)); if(pole != NULL) { pole[0] = pole[DELKA - 1] = 0; // ... použití bloku ... MemPtrFree((MemPtr)(pole)); } |
3.6. Přemístitelné bloky paměti
V podmínkách omezeného množství paměti, které panují na počítačích Palm Pilot a kompatibilních, je výhodnější používat přemístitelné bloky paměti. Používání přemístitelných bloků paměti je z hlediska programátora o málo obtížnější, oproti tomu však tato technika umožní̈snížit nároky na paměť. Manažer paměti PalmOS optimalizuje využití paměti (uspořádává přemístitelné bloky v paměti dle aktuální potřeby) a dovolí měnit jejich velikost.
Žádáme-li systém o nepřemístitelný blok paměti, tak funkce MemPtrNew() vrátí přímo jeho adresu v paměti. Tento přístup u přemístitelných bloků zjevně nemůže fungovat, protože jejich adresa se může bez varování změnit. Místo toho používáme ovladače bloků paměti. Požadujeme-li od operačního systému blok přemístitelné paměti, tak manažer paměti tento blok vytvoří a vrátí ovladač bloku paměti (datový typ MemHandle).
Jak se však dostaneme k obsahu tohoto bloku paměti? Velmi jednoduše. Ve chvíli, kdy si přejeme z tohoto bloku číst nebo do něj zapisovat, tak jej uzamkneme na místě. Je-li přemístitelný blok paměti uzamčen na místě, tak jej manažer paměti po dobu uzamčení nemůže přemisťovat. Po skončení práce blok paměti odemkneme, tím opět dovolíme operačnímu systému tento blok posouvat po operační paměti.
Mnoho funkcí operačního systému vyžaduje jako parametry nebo vrací jako návratovou hodnotu právě ovladač bloku paměti, ve kterém jsou uložena požadovaná data.
MemHandle MemHandleNew(UInt32 size) Vytvoří přemístitelný blok dynamické paměti a vrátí jeho ovladač. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Pomocí funkce MemHandleNew() vytvoříme v dynamické paměti přemístitelný blok, patřící naší aplikaci. Vrátí-li tato funkce nenulovou hodnotu, byl blok vytvořen úspěšně. Vzhledem k tomu, že nevíme, jaké bude množství dynamické paměti na počítači, na kterém naše aplikace poběží, musíme návratovou hodnotu této funkce vždy testovat, zda není rovna nule. Před použitím tohoto bloku paměti jej musíme uzamknout funkcí MemHandleLock() a po skončení používání jej musíme vrátit operačnímu systému voláním funkce MemHandleFree().
Err MemHandleFree(MemHandle ovladač) Vrátí přemístitelný blok paměti zpět operačnímu systému. | |
Vstupní parametry |
|
Vrácená hodnota |
|
I u přemístitelných bloků platí, že bychom se měli snažit vrátit operačnímu systému všechny bloky paměti, které jsme získali. Pokud to neuděláme, tak operační systém (podobně jako u nepřemístitelných bloků paměti) po skončení naší aplikace automaticky zruší všechny zbývající bloky paměti, které aplikace vytvořila.
MemPtr MemHandleLock(MemHandle ovladač) Uzamkne přemístitelný blok paměti na místě a vrátí jeho adresu. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Pomocí funkce MemHandleLock() můžeme blok uzamknout i opakovaně. Operační systém udržuje pro každý blok paměti hodnotu kolikrát byl uzamčen. Kdykoliv blok uzamkneme, tato hodnota se o jedničku zvýší a pokud blok odemkneme, tak se tato hodnota naopak sníží. Musíme si však pamatovat, že aby mohl operační systém s blokem zacházet (přemístit jej na jiné místo v paměti z důvodu optimalizace využití paměti), musíme jej odemknout (voláním funkce MemHandleUnlock()) právě tolikrát, kolikrát jsme jej uzamkli (počet uzamčení musí být roven nule).
Hodnota vrácená funkcí MemHandleLock() (typ MemPtr) může být bezpečně přetypována na libovolný ukazatel.
Err MemHandleUnlock(MemHandle ovladač) Zruší uzamčení bloku paměti. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Funkce MemHandleLock() a MemHandleUnlock() nemění obsah bloku paměti. Tyto funkce pouze sdělují operačnímu systému, že si s blokem přejeme pracovat (MemHandleLock()) nebo že s blokem pracovat přestáváme (MemHandleUnlock()). Abychom mohli využít výhod přemístitelných bloků, musí být mezi uzamčením (voláním funkce MemHandleLock()) a odemčením (MemHandleUnlock()) co nejméně příkazů. Správné použití bloků paměti je v následujícím příkladu:
typedef struct { UInt32 cislo; ... } typBlokuPameti; typBlokuPameti* blok; MemHandle hText = MemHandleNew(sizeof(typBlokuPameti)); if(hText) { // abychom mohli blok použít, musíme jej uzamknout na místě // teprve pak jej můžeme používat blok = (typBlokuPameti*)MemHandleLock(hText); blok->cislo = 0L; .... MemHandleUnlock(hText); // v tomto okamžiku již proměnná blok neobsahuje platnou adresu ... // před čtením z bloku paměti jej opět musíme na chvíli // uzamknout na místě blok = (typBlokuPameti*)MemHandleLock(hText); soucet += blok->cislo; .... MemHandleUnlock(hText); ... // Na konci musíme blok paměti vrátit operačnímu systému MemHandleFree(hText); } |
I u přemístitelných bloků paměti můžeme měnit jejich velikost pomocí funkce MemHandleResize(). Zmenšení velikosti proběhne vždy bezchybně. Důležité však je, že pokud si přejeme zvětšit velikost přiděleného bloku paměti, tak je mnohem pravděpodobnější, že se to podaří u přemístitelného bloku paměti (operační systém se jej pokusí přesunout do místa, kde může zvětšit jeho velikost).
Err MemHandleResize(MemHandle ovladač, UInt32 newSize)
| |
Vstupní parametry |
|
Vrácená hodnota |
|
I u přemístitelných bloků paměti můžeme zjišťovat jejich velikost. Funkce, kterou se dotazujeme na velikost přemístitelného bloku paměti, se jmenuje MemHandleSize() a můžeme ji použít pro zjišťování velikosti uzamčeného i neuzamčeného bloku paměti.
UInt32 MemHandleSize(MemHandle ovladač) Zjistí velikost přemístitelného bloku paměti. | |
Vstupní parametry |
|
Vrácená hodnota |
|
Je užitečné si uvědomit, že uzamkneme-li přemístitelný blok paměti, tak se po dobu svého uzamčení (mezi voláním funkce MemHandleLock() a MemHandleUnlock()) chová úplně stejně jako nepřemístitelný blok paměti. Můžeme tedy například zjistit jeho velikost voláním funkce MemPtrSize().
V příštím díle dokončíme popis bloků paměti a povíme si o práci s řetězci v prostředí PalmOS.