Hlavní navigace

GLib: Práce s pamětí, typová konverze

21. 3. 2000
Doba čtení: 4 minuty

Sdílet

Tvůrcům knihovny GLib (šířené podle licence LGPL) patrně nestačily funkce standardního Céčka pro alokování a uvolňování paměti, tak si dodefinovali pár vlastních a my se je dnes naučíme používat. Kromě toho si také povíme něco o typové konverzi z pointeru na integer a naopak.

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, guintgpointer

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

CS24_early

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

Byl pro vás článek přínosný?

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.