Hlavní navigace

GLib: Pole

23. 5. 2000
Doba čtení: 7 minut

Sdílet

Pole (arrays) knihovny GLib jsou podobné polím ze standardního jazyka C. Jejich předností však je, že se automaticky zvětšují s tím, jak do nich přidáváte nové prvky.

Přístup k těmto polím je velice podobný přístupu k automaticky rostoucím řetězcům GString  – vždyť také mají mnoho společného. „Nafukovací“ pole knihovny GLib jsou definovány strukturou GArray:

struct GArray
{
  gchar *data;
  guint len;
}

Přístup k údajům v této struktuře není omezován, měli byste však být opatrní, chcete-li na nich cokoliv měnit.

Prvek data je pointer na informace uložené v poli a můžete k němu přistupovat jako ke standardnímu céčkovskému poli. Jak se však bude pole zvětšovat, bude se také realokovat a proto se tento pointer může měnit. len uchovává počet položek v poli.

Vytvoření pole GArray

Nové, dynamicky se zvětšující pole GArray vytvoříte funkcí

GArray* g_array_new(gboolean zero_terminated, gboolean clear,
                    guint element_size);

Argument zero_terminated nastavte na TRUE, chcete-li, aby pole mělo vždy na svém konci vynulovaný prvek (aby bylo ukončeno nulovým (‚0‘) prvkem). Jinak použijte hodnotu  FALSE.

Chcete-li, aby byly všechny nově alokované položky vynulovány (nastaveny na samé ‚0‘) zvolte jako argument clear hodnotu TRUE.

element_size 

Funkce vrátí pointer na nově alokovaný GArray, který by měl být nakonec zrušen voláním  g_array_free().

Přidávání položek do pole GArray

#define g_array_append_val(a,v) 

…vytvoří na konci pole a nový prvek a zkopíruje do něj hodnotu v. GArray a svou velikost podle potřeby zvětší. Makro vrátí hodnotu argumentu  a.

Důležité. g_array_appen­d_val() je makro, kterým ve skutečnosti voláte funkci g_array_appen­d_vals(). Používá se v něm reference na v, což znamená, že v  MUSÍ být proměnná (a ne konstanta, jako např. „ 96“).

GArray* g_array_append_vals(GArray *array, gconstpointer data,
                            guint len);

Tato funkce vloží len prvků pole data na konec dynamického pole array. data je tedy pointer na první položku, která se má do pole přidat a len je počet těchto položek. GArray array se podle potřeby automaticky zvětší.

Obdobou k této dvojici jsou:

#define g_array_prepend_val(a,v)

GArray* g_array_prepend_vals(GArray *array, gconstpointer data,
                             guint len);

…které dělají totéž co makro g_array_appen­d_val() a funkce g_array_appen­d_vals() s tím rozdílem, že položky nepřidávají na konec, ale na začátek pole a ( array). Je třeba znovu upozornit na fakt, že argument v (makra g_array_prepend_val) musí být proměnná a ne jen konstanta (jako např. „ 96“), protože po rozvinutí makra se žádá reference ( &) na něj.

Operace přidání prvku na začátek GArray je pomalejší než přidávání na konec, protože se všechny prvky pole musí v paměti posunout, aby uvolnily místo první, nově vkládané, položce.

#define g_array_insert_val(a,i,v) 

…vytvoří novou položku v dynamickém poli a na pozici i  a zkopíruje do ní hodnotu argumentu v. Že parametr v  musí být proměnná, už snad nemusím zdůrazňovat.

GArray* g_array_insert_vals(GArray *array, guint index,
                            gconstpointer data, guint len);

Tato funkce se postará o vložení len prvků z adresy data na index-tou pozici v dynamickém poli array. data je tedy pointer na první vkládaný prvek, len je počet prvků a index je místo v poli array, kam se položky umístí. Prvky pole array za tímto indexem se pochopitelně automaticky posunou, aby udělaly pro nově vkládané položky místo a GArray array podle potřeby zvětší svou velikost tak, aby mohl pojmout všechny údaje.

GArray* g_array_set_size(GArray *array, guint length); 

…nastaví velikost pole array na hodnotu length. Pole se podle potřeby realokuje. Bylo-li pole array vytvořeno s argumentem clear nastaveným na TRUE, budou všechny nové položky vynulovány (všechny jejich bajty nastaveny na 0) – viz funkce g_array_create(). Funkce vrátí hodnotu argumentu array.

Odstraňování položek z  GArray

GArray* g_array_remove_index(GArray *array, guint index); 

…odstraní z dynamického pole array položku s indexem index. Vzniklá mezera se vyplní posunutím zbývajících prvků pole na její místo. Funkce zachovává pořadí prvků. Vrácenou hodnotou je pointer na GArray.

GArray* g_array_remove_index_fast(GArray *array, guint index); 

Jak již název napovídá, půjde o rychlejší variantu funkce na odstranění položky z pole array. Čas na vykonání operace se ušetří tak, že po odstranění položky s indexem index se místo posouvání zbývajících prvků do vzniklé mezery přesune poslední prvek pole. Funkce g_array_remove_index_fast() proběhne o něco rychleji než funkce g_array_remove_index(), její nevýhodou však je, že nezachovává pořadí prvků v poli. To ji tedy předurčuje k použití pouze tam, kde tato vlastnost nevadí (kde nezáleží na pořadí položek v poli a je jedno, jaký index prvky mají).

Odkazování se na položky pole

K přístupu k položkám dynamických polí můžete použít makro

#define g_array_index(a,t,i)

…které vrátí položku pole a s indexem i. Výsledek je přetypován na typ  t.

Podívejme se však blíže, jak je makro g_array_index() definováno:

#define g_array_index(a,t,i)     (((t*) (a)->data) [(i)])

To, že v podstatě pouze zastřešuje práci s položkou data struktury GArray, jej předurčuje i k poněkud kurióznějším použitím, které by v případě, že by g_array_index() byla funkce, nebylo možné. Následující použití jsou plně v souladu se syntaxí jazyka C:

/* prirazeni hodnoty 5. polozce pole */
g_array_index(array, gint, 5) = 0;

/* prace s polozkou number struktury MyStruct,
    ktera je 7. prvkem pole */
g_array_index(array, struct MyStruct, 7).number = 12;

/* ziskani pointeru na 3. polozku pole */
p = &(g_array_index(array, gfloat, 3));

Příklad:

/* ukazka prace s makrem g_array_index() */

#include <glib.h>

/* Definice nasi struktury */
struct MyStruct {
  gint number;
  gfloat value;
  gchar *name;
};

gint main(void)
{
  GArray *array;
  struct MyStruct item, *item2;
  gint x;

  /* vytvoreni noveho GArray */
  array = g_array_new(FALSE, FALSE, sizeof(struct MyStruct));
  /* vytvoreni nekolika polozek pole s nejakymi hodnotami */   for (x = 0; x < 20; x++) {     item.number = x;     item.value = x * 134.28;     g_array_append_val(array, item);   }
  /* obsah 15. prvku se zkopiruje do promenne item */
  item = g_array_index(array,  struct MyStruct, 15);
  item.number = 30; /* s prvkem pole se nic nestane */
  printf("%d-ta polozka: number = %d\n", 15,
    g_array_index(array, struct MyStruct, 15).number);

  /* prepisujeme hodnoty 15. prvku pole */
  g_array_index(array,  struct MyStruct, 15).number = 333;
  printf("%d-ta polozka: number = %d\n", 15,
    g_array_index(array, struct MyStruct, 15).number);

  /* nyni chceme pointer na 15. polozku pole */
  item2 = &(g_array_index(array,  struct MyStruct, 15));
  item2->number = 100;
  printf("%d-ta polozka: number = %d\n", 15,
    g_array_index(array, struct MyStruct, 15).number);

  /* znovu prepisujeme hodnoty 15. prvku pole */
  g_array_index(array,  struct MyStruct, 15).number = 500;

  /* dukaz, ze jsme pointer vytvorili spravne */
  printf("%d-ta polozka: number = %d\n", 15,
    item2->number);

  /* dealokace pole vcetne jeho polozek z pameti */
  g_array_free(array, TRUE);
  return(0);
}

Pro informaci, tento příklad by měl po spuštění vypsat:

15-ta polozka: number = 15
15-ta polozka: number = 333
15-ta polozka: number = 100
15-ta polozka: number = 500

Pečlivý čtenář si jistě prostuduje, co to znamená.

Uvolnění GArray z paměti

Ukončíte-li práci s poli automatické velikosti GArray, musíte je dealokovat. K tomu slouží funkce

void g_array_free(GArray *array, gboolean free_segment); 

Je-li free_segment nastaven na TRUE, uvolní se také data udržované polem array. Je-li naopak free_segment roven FALSE, můžete data i nadále používat jako obyčejné céčkovské pole. (Nesmíte však zapomenout je nakonec uvolnit voláním  g_free()!)

Příklad:

Na závěr ještě jeden příklad na použití „nafukovacího“ pole GArray.

/* ukazka prace s GArray */

#include <glib.h>

gint main(void)
{
  GArray *array;
  gint item;
  gint items[20];
  gint *my_data;
  gint x;

  /* vytvoreni noveho GArray (ktery bude zero-terminated) */
  array = g_array_new(TRUE, FALSE, sizeof(gint));
  /* vytvoreni nekolika polozek pole s nejakymi hodnotami */
  for (x = 0; x < 1000; x++) {
    item = x + 1;
    g_array_append_val(array, item);
  }

  /* vlozeni pole items do "nafukovaciho" pole array */
  for (x = 0; x < 20; x++) {
    items[x] = x + 1001;
  }
  g_array_append_vals(array, items, sizeof(items) / sizeof(gint));
  my_data = (gint *) array->data;

  /* dealokace pole (polozky v pameti zustavaji) */
  g_array_free(array, FALSE);

  /* kontrolni vypis */
  x = 0;
  while (my_data[x] != 0) {
    printf("%d  ", my_data[x]);
    x++;
  }
  puts("");

  /* dealokace polozek pole */
  g_free(my_data);

  return(0);
}

Tento příklad ukazuje možné použití pole GArray. Na začátku je pole vytvořeno s příznakem zero-terminated nastaveným na TRUE, abychom mohli jednoznačně identifikovat jeho konec. To znamená, že bude obsahovat vždy o jednu položku navíc, která bude vynulována. Pak jsou do něj přidávána nějaká data a nakonec je pole dealokováno funkcí g_array_free(). Její parametr free_segment je však nastaven na FALSE, což znamená že se dealokuje pouze pole array  – jeho data zůstanou v paměti nedotčena. Proto také s nimi můžeme ještě pracovat a vypsat kontrolní výpis. Nakonec je však třeba i tyto data dealokovat.

Vše samozřejmě mohlo proběhnout i jinak. Kontrolní výpis jsme mohli provést ještě před voláním funkce g_array_free() a pak jejím voláním dealokovat všechno najednou:

root_podpora

g_array_free(array, TRUE)

K výpisu celého obsahu pole jsme mohli místo zavedení posledního nulového prvku ( zero-terminated nastaveno na TRUE) využít skutečnosti, že array->len obsahuje počet prvků v poli a podobně.

A to je pro dnešek z mé strany vše. U dalšího povídání o knihovně GLib se těší

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.