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í.