Ačkoliv jsem v minulém díle avizoval, že se dnes budeme bavit o řetězcích, rozhodl jsem se nakonec vysvětlit nejprve funkce pro práci s pamětí, abychom v příštích dílech mohli na těchto znalostech stavět a pustit se odvážně do (relativně) obtížnějších témat.
PRÁCE S DYNAMICKOU PAMĚTÍ
Práce s dynamickou pamětí je skoro v každém programu nutností. Předpokládám, že máte znalosti funkcí standardního C starajících se o alokování a uvolňování paměti. Nebudu se proto pouštět do vysvětlování, co alokace dynamické paměti obnáší a k čemu slouží, ale přejdu rovnou k věci, tedy jaké nové funkce pro správu paměti přináší knihovna GLib.
Alokace paměti
#define g_new(type, count)
Toto makro alokuje paměť pro count prvků typu type. Vrácený pointer je přetypován na pointer na daný typ. Jestliže je count rovno 0, vrátí NULL.
Chcete-li, aby nově alokovaná paměť byla navíc inicializována samými nulami, je třeba místo toho zavolat makro
#define g_new0(type, count)
… které alokuje count prvků typu type a všechny bajty nastaví na 0. Vrácený pointer je typu pointer na type. V případě, že count je 0, vrátí NULL.
gpointer g_malloc(gulong size);
… je obdobou předchozích maker. g_malloc alokuje size bajtů paměti a vrátí pointer na ni. Je-li size rovno 0, vrátí NULL.
Jak asi čekáte,
gpointer g_malloc0(gulong size);
… alokuje size bajtů paměti, které následně všechny nastaví na hodnotu 0. Vrátí pointer na alokovanou paměť. V případě, že size je 0, vrátí NULL.
Realokace paměti
Realokace paměti je proces, kdy se alokované paměťové místo zvětší na zadanou velikost. Dělá se to v podstatě tak, že původní alokovaná paměť se uvolní a místo ní se alokuje nová, o větší velikosti. Musíte vždy počítat s tím, že touto operací může dojít (a obvykle také dochází) ke změně adresy.
#define g_renew(type, mem, count)
… realokuje paměť, na kterou ukazuje pointer mem tak, že nyní je v ní místo pro count prvků typu type. Makro vrátí novou adresu, na které se teď proměnná nachází.
gpointer g_realloc(gpointer mem, gulong size);
… tato funkce realokuje paměť reprezentovanou pointerem mem na velikost size bajtů. Vrátí novou adresu paměti, protože je pravděpodobné, že se při této operaci změní.
Dealokace paměti
K uvolnění paměti slouží jednoduchá funkce
void g_free(gpointer mem);
… která uvolní místo, na které ukazuje pointer mem. Je-li v mem uložen NULL, jednoduše skončí. Všechnu paměť, kterou jste dříve alokovali pomocí předchozích funkcí, byste po použití měli uvolnit (dealokovat) pomocí této funkce, aby přestala být rezervovaná a systém ji mohl použít k něčemu jinému.
Další funkce
#define g_memmove(d, s, n)
… toto makro zkopíruje blok paměti n bajtů dlouhý z adresy s do d. Zdrojová a cílová oblast se může překrývat.
Pozor! Na platformách, kde není k dispozici memmove(), je funkce implementována použitím bcopy(), která není schopna obsloužit překrývající se oblasti.
gpointer g_memdup(gconstpointer mem, guint byte_size);
… alokuje byte_size bajtů a zkopíruje do nich byte_size bajtů z mem. Je-li mem rovno NULL, jednoduše vrátí NULL. Jinak vrátí pointer na nově alokovanou oblast paměti.
Všechny funkce knihovny GLib při alokaci paměti testují úspěšnost operace. V případě neúspěchu aplikace okamžitě končí. Znamená to tedy, že není třeba testovat, zda-li bylo volání funkce úspěšné a paměť se správně alokovala.
Příklad:
/* Zkouska funkci pro dynamickou alokaci pameti */
#include <glib.h>
gint main(void) {
gint *pole; /* dynamicke integerove pole */
gint *pole2; /* dalsi dynamicke pole */
gint x; /* pomocna promenna */
/* vytvoreni 5ti prvkoveho pole */
pole = g_new0(gint, 5);
for (x = 0; x < 5; x++) {
/* ukazka toho, ze nove pole je opravdu vynulovano */
printf("pole[%d] = %d\n", x, pole[x]);
/* nastaveni nejakych hodnot */
pole[x] = 2 * x;
}
/* duplikace pole */ pole2 = g_memdup(pole, 5 * sizeof(gint));
for (x = 0; x < 5; x++) {
/* kontrolni vystup */
printf("pole[%d] = %d\tpole2[%d] = %d\n", x, pole[x], x, pole2[x]);
}
/* uvolneni pameti (dealokace) */ g_free(pole); g_free(pole2); return(0); }
MAKRA PRO TYPOVOU KONVERZI gint, guint ↔ gpointer
Následující makra poskytují přenositelné metody ukládání hodnot typu gint a guint do proměnných typu gpointer. Mnoho složitějších datových typů (jako hash tabulky, seznamy, stromy a podobně) jsou totiž založeny právě na uchovávání proměnných typu gpointer, které slouží jako pojítko mezi skutečnými daty.
Užitím těchto konverzních maker lze do proměnných typu gpointer ukládat hodnoty typu gint nebo guint. Můžete tak například lehce vytvořit spojový seznam gint hodnot nebo strom guint ů.
Konverzní makra jsou nutná, protože velikost proměnné typu gpointer se na různých platformách může lišit. Proto je třeba k takové typové konverzi přistupovat opatrně.
Opačnou operaci, tedy uložení gpointer u do integerových proměnných nelze provést, protože není zaručeno, že gint nebo guint bude na libovolné platformě dostatečně velký na to, aby do sebe uložil celou adresu paměti.
Pro konverzi gint u na gpointer použijte makro GINT_TO_POINTER(int_value). Zpět na gint jej zkonvertujete makrem GPOINTER_TO_INT(pointer_value).
Chcete-li provést konverzi proměnné typu guint na gpointer, zavolejte makro GUINT_TO_POINTER(unsigned_int_value). Opačnou konverzi, tedy gpointer na guint, provedete voláním GPOINTER_TO_UINT(pointer_value).
Příklad:
gpointer p; guint ui; p = GUINT_TO_POINTER(ui); ... ui = GPOINTER_TO_UINT(p); ...
A to je pro dnešek vše. Těším se u dalšího pokračování.