Korzystanie z biblioteki GLib w aplikacjach tworzonych w języku C. (artykuł)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
dział serwisuArtykuły
kategoriaInne artykuły
artykułGLib - niskopoziomowa, narzędziowa biblioteka funkcji dla programistów języka C
Autor: Badman

GLib - niskopoziomowa, narzędziowa biblioteka funkcji dla programistów języka C

[artykuł] Korzystanie z biblioteki GLib w aplikacjach tworzonych w języku C.

Wprowadzenie

GLib jest wykorzystywana przede wszystkim jako podstawa biblioteki Gtk+ jak i  graficznego środowiska GNOME, XFCE4 oraz LXDE.  Pisząc programy w oparciu o bibliotekę Gtk+ dobrze jest zapoznać się z biblioteką GLib, aby programować sprawnie i nie tworzyć swoich nowych funkcji, tylko skorzystać z gotowych zawartych w GLib, jak choćby funkcji do obsługi list, czy drzew.

Cechy biblioteki:
  • makra obsługujące kolejność bajtów
  • definicje podstawowych typów i ich limitów
  • obsługa konwersji typów
  • przydatne definicje liczbowe (np. matematyczne)
  • alokacja pamięci
  • implementacja wielowątkowości GThread
  • asynchroniczne kolejki
  • obsługa dynamicznie ładowanych modułów
  • obsługa gniazd i plików (ogólnie operacji I/O)
  • system logowania błędów i ostrzeżeń
  • obsługa Unicode i UTF-8
  • uruchamianie procesów potomnych przy użyciu fork()
  • wbudowany generator liczb losowych
  • kodowanie i dekodowanie danych w formacie Base64
  • obsługa wyrażeń regularnych
  • obsługa timer’ów
  • funkcje do manipulacji na łańcuchach tekstowych
  • obsługa XML’a

Typy danych

Ponieważ biblioteka GLib w założeniu miała być biblioteką między platformową, czyli pozwolić na  jej użycie pod różnymi systemami operacyjnymi takimi jak: Linux, Windows, twórcy biblioteki zdefiniowali nowe typy różne od tych w języku C. Aby w przyszłości napisane przez Ciebie programy (m. in. Gtk+) kompilowały się bez problemu należy używać właśnie typów zdefiniowanych w bibliotece GLib: 

Typy CTypy GLib
chargchar
unsigned chargchar
intgint
unsigned intguint
shortgshort
unsigned shortgushort
longglong
unsigned longgulong
signed chargint8
unsigned charguint8
signed shortgint16
unsigned shortguint16
signed intgint32
unsigned intguint32
signed longgint64
unsigned longguint64
floatgfloat
doublegdouble
void*gpointer
const void*gconstpointer
intgboolean

Alokacja pamięci

Dynamiczna alokacja pamięci jest nieocenionym aspektem programowania, który pozwala w czasie trwania programu przydzielać dowolne ilości pamięci wymaganej w danej chwili. Na tej właściwości bazują choćby takie struktury danych jak listy, bądź drzewa. Oczywiście biblioteka GLib zawiera odpowiedniki funkcji standardowych  w C do rezerwowania i zwalniania pamięci.

gpointer g_malloc( gsize n_bytes );

Zwraca wskaźnik gpointer na przydzieloną pamięć. Argumentem jest ilość bajtów do alokacji pamięci, jeżeli argument n_bytes ma wartość 0 to funkcja zwróci wartość  NULL.

gpointer g_malloc0( gsize n_bytes );

Działa analogicznie jak funkcja g_malloc, z tym że przydzielona pamięć inicjalizowana jest wartością 0.

Aby zmienić rozmiar przydzielonej wcześniej pamięci należy użyć funkcji:
gpointer g_realloc( gpointer mem, gsize n_bytes );

Zwraca wskaźnik gpointer  na zmieniony obszar pamięci. Argumentem jest wskaźnik na pamięć przydzieloną wcześniej. Drugi argument n_bytes określa rozmiar zmienianej pamięci w bajtach.

Dodatkowo zdefiniowane są następujące makra:
#define  g_new(struct_type, n_structs)

Alokuje n_structs elementów pamięci typu struct_type. Jeżeli n_structs ma wartość 0, to zwraca wartość NULL.

#define  g_new0(struct_type, n_structs)

Analogicznie do poprzedniego makra g_new, z tym że przydzielona pamięć jest inicjalizowana wartością 0.

#define g_renew(struct_type, mem, n_structs)

Makro analogiczne do funkcji g_realloc .

Kiedy nie potrzebujesz wcześniej zarezerwowanej pamięci skorzystaj z funkcji:
void g_free( gpointer mem );

Zwolni pamięć zalokowaną wcześniej wskazywaną przez mem. W przeciwieństwie do standardowej funkcji free  argument mem może mieć wartość NULL.

Uwaga: Nie należy mieszać funkcji alokującej pamięć biblioteki standardowej C z funkcją g_free i odwrotnie:

C/C++
pamiec =( char * ) malloc( 10 * sizeof( char ) );
g_free( pamiec );

[..]

pamiec =( gchar * ) g_malloc( 10 * sizeof( gchar ) );
free( pamiec );

Operacje na łańcucha tekstowych

Biblioteka GLib zawiera wiele podobnych do standardowych funkcji C operujących na łańcucha tekstowych. Dodatkowo zawiera funkcję operujące na UTF-8 (wszystkie łańcuch tekstowe używane w programowaniu przy użyciu biblioteki Gtk+ właśnie używają UTF-8).

gchar * g_strdup( const gchar * str );

Kopiuje łańcuch tekstowy wskazywany przez str przydzielając dla niego pamięć, dlatego należy zwolnić ją funkcją g_free, kiedy jest już zbędny. Jeśli wskaźnik str wskazuje na NULL, funkcja zwróci wartość NULL.

gchar * g_strndup( const gchar * str, gsize n );

Kopiuje n znaków łańcucha tekstowego wskazywanego przez str przydzielając dla niego pamięć dodając jeden bajt na tzw. „null terminator”. Zwrócony wskaźnik powinien być zwolniony kiedy jest nie potrzebny.

gchar * g_strdup_printf( const gchar * format,...);
Funkcja podobna do standardowej funkcji C sprintf() lecz jest od niej bezpieczniejsza, ponieważ oblicza maksymalną wymaganą przestrzeń i alokuje pamięć do przechowywania wyników. Zwrócony ciąg tekstowy kiedy nie jest już potrzebny należy zwolnić używają funkcji g_free ().
Argumenty przekazywane do funkcji takie jak w standardowej funkcji C printf ().

gchar * g_stpcpy( gchar * dest, const char * src );

Kopiuje łańcuch tekstowy wskazywany przez src do dest. Funkcja zwraca wskaźnik na dest.

int g_strcmp0( const char * str1, const char * str2 );
Porównuje łańcuchy tekstowe wskazywane przez argumenty str1 z str2. Zwraca wartość -1 kiedy str1 < str2, wartość 0 kiedy str1 == str2, wartość 1 wówczas gdy str1 > str2.

Struktury danych

Listy

Lista - struktura danych służąca do reprezentacji zbiorów dynamicznych, w której elementy ułożone są w liniowym porządku. Rozróżniane są dwa podstawowe rodzaje list: lista jednokierunkowa w której z każdego elementu możliwe jest przejście do jego następnika oraz lista dwukierunkowa w której z każdego elementu możliwe jest przejście do jego poprzednika i następnika.
Źródło: http://pl.wikipedia.org/wiki/Lista

Listy nadają się do implementacji stosów (w których elementy są dodawane i usuwane z początku listy) oraz niewielkich list. Są jednak dość kosztowne czasowo w użyciu, jeśli przechowują duże ilości danych, więc należy dodawać dane na początek listy, dzięki czemu wstawianie elementów będzie przebiegać bardzo szybko. Znalezienie danych w liście zależy proporcjonalnie do długości listy.

Lista jednokierunkowa łączona

W bibliotece GLib struktura listy jednokierunkowej łączonej to GSList, struktura ta GSList ma dwie części, zdefiniowane następująco:

Pole *data w strukturze przechowuje wskaźnik dane, najczęściej jest to wskaźnik do innej struktury. Natomiast pole *next jest wskaźnikiem do następnego elementu łączonej listy.

Jedną z metod dodawania elementów do listy jest użycie funkcji:
GSList * g_slist_append( GSList * list, gpointer data );

Dodaje element na koniec listy. Zwraca wskaźnik na nową łączoną listę, argumentami są wskaźnik na listę i element, który ma być dodany na koniec listy.

GSList * g_slist_prepend( GSList * list, gpointer data );

Dodaje element na poczatek listy. Zwraca wskaźnik na nową łączoną listę, argumentami są wskaźnik na listę i element, który ma być dodany na koniec listy.

Możliwe jest dodanie elementu w określona pozycję listy poprzez funkcję:
GSList * g_slist_insert( GSList * list, gpointer data, gint position );

Zwraca wskaźnik na nową łączoną listę. Pierwszy argumentem jest wskaźnik na listę, drugi element, który ma być dodany do listy na pozycji określonej w trzecim argumencie. Jeżeli wartość position jest ujemna lub większa niż ilość elementów w liście to element zostaje dodany na koniec listy.

Funkcja usuwająca określony element w liście wygląda następująco:
GSList * g_slist_remove( GSList * list, gconstpointer data );

Zwraca wskaźnik na listę. Pierwszym argumentem jest wskaźnik na listę, w której należy usunąć element, oraz "stały" wskaźnik na element do usunięcia. W przypadku kiedy element nie istnieje lista nie zostanie zmieniona. Jeśli w liście znajduje się więcej niż jeden takich samych elementów, tylko pierwszy napotkany zostanie usunięty.

Wprzeciwieństwie do poprzedniej funkcji, chcąc usnąć wszystkie takie same elementy w liście należy użyć funkcji:
GSList * g_slist_remove_all( GSList * list, gconstpointer data );

Argumenty takie jak w g_slist_remove.

Aby odnaleść element o określonej wartości w liście stosuje się funkcję:
GSList * g_slist_find( GSList * list, gconstpointer data );

Zwraca wskaźnik na znaleziony element, a w przeciwnym wypadku wartość NULL. Aby uzyskać zawartość elementu należy dostać się do składowej struktury listy, która zdefiniowana jest jako data. Argumentami jest wskaźnik na listę i element do znalezienia. Uwaga, funkcja g_slist_find nie posiada szczegółowej wiedzy na temat danych, przechowywanych w węzłach porównuje po prostu wartości pól danych, które najczęściej są wskaźnikami.

GSList * g_slist_last( GSList * list );

Zwraca wskaźnik na ostatni element listy, bądź wartość NULL kiedy w liście nie ma elementów. Argumentem jest wskaźnik na listę.


Chcąc pobrać element w określonej pozycji listy zastosuj:
GSList * g_slist_nth( GSList * list, guint n );

Zwraca wskaźnik na element o określonej pozycji podanej w drugim argumencie. Pierwszym argumentem jest wskaźnik do listy, z której pobierasz element. W przypadku kiedy pozycja nie istnieje funkcja zwraca wartość NULL.

Poprzednie funkcje zwracały element jako wskaźnik na obiekt GSList, więc aby dostać się do danych elementu należało się odwołać do jego składowej data, czyli element->data. Poniżej funkcja, która zamiast elementu listy zwraca wskaźnik na zawartość danych w elemencie:
gpointer g_slist_nth_data( GSList * list, guint n );

Argumentami są wskaźnik na listę i pozycja elementu, z którego ma być pobrana zawartośc danych. Jeżeli pozycja  jest ujemna lub większa niż ilość elementów w liście to funkcja zwróci wartość NULL.

Poniżej makro służące do pobrania następnego elementu listy:
#define g_slist_next(slist)

Ustawia wskaźnik na następny element elementu wskazanego jako slist. Jeśli nie istnieje następny element zwraca wartość NULL.

Gdy zachodzi potrzeba wstawiania elementów w uporządkowanej kolejności do listy jednokierunkowej łączonej należy użyć funkcji:
GSList * g_slist_insert_sorted( GSList * list, gpointer data, GCompareFunc func );

Zwraca wskaźnik na listę po wstawieniu elementu w odpowiednie miejsce. Pierwszym argumentem jest wskaźnik na listę do, której wstawiany jest element. Drugim jest wskaźnik na element do wstawienia. Ponieważ przechowywane w liście łączonej dane mogą być dowolnego typu, a lista nie posiada żadnej wiedzy na temat ich wewnętrznej struktury funkcja ta jako trzeci parametr potrzebuje nazwy funkcji, która odpowiednio porówna wstawiany element z istniejącymi w liście.
Prototyp funkcji porównowywującej wygląda tak:
gint( * GCompareFunc )( gconstpointer a, gconstpointer b );

Funkcja porównująca przyjmuje dwa elementy, porównuje je i zwraca 1, jeśli pierwszy element jest większy niż drugi, 0, jeśli są takie same, a -1, jeśli pierwszy element jest niejszy niż drugi.

Kiedy lista jest już zbędna należy wykonać funkcję:
void g_slist_free( GSList * list );

Zwalnia całą pamięć zajmowaną przez listę. Uwaga: w przypadku kiedy elementy listy miały dynamicznie alokowaną pamięć (g_malloc, g_new, ...) należy najpierw samodzielnie zwolnić pamięć zajmowaną przez te elementy ! Programiści pisząc bibliotekę GLib pomyśleli o funkcji, która ułatwi to zadanie tworząc funkcję:

void g_slist_free_full( GSList * list, GDestroyNotify free_func );

Funkcja dla każdego elementu listy wywoła funkcję free_func przekazując tej funkcji właśnie ten element. Argumentami są wskaźnik na listę oraz nazwa funkcji, która będzie wywoływana.
Prototyp funkcji zwalniającej pamięć każdego elementu wygląda następująco:

void( * GDestroyNotify )( gpointer data );

GDestroyNotify to nazwa funkcji. Przekazywany tej funkcji jest wskaźnik na dane, którym należy zwolnijć zalokowaną pamięć.

Poniżej przykład wykorzystujący poznane funkcje:
C/C++
#include <glib.h>

typedef struct {
    gchar * imie;
    guint wiek;
} Osoba;

void menu( void )
{
    g_print( "0. Wyjscie\n" );
    g_print( "1. Dodaj element na koniec listy\n" );
    g_print( "2. Dodaj element na poczatek listy\n" );
    g_print( "3. Dodaj element w okreslonej pozycji listy\n" );
    g_print( "4. Dodaj element w uporzadkowanej kolejnosci\n" );
    g_print( "5. Uporzadkuj liste\n" );
    g_print( "6. Znajdz element\n" );
    g_print( "7. Wyswietl elementy listy 'foreach'\n" );
    g_print( "8. Wyswietl elementy listy iteracyjnie\n" );
}

Osoba * utworz_element( void )
{
    Osoba * student = g_new( Osoba, 1 ); // // Przydzielenie pamieci dla 1-nej struktury Osoba
    gchar imie[ 20 ];
    guint wiek;
    guint ilosc_znakow;
   
    g_print( "Podaj imie: " );
    scanf( "%s", imie );
    g_print( "Podaj wiek: " );
    scanf( "%d", & wiek );
   
    ilosc_znakow = strlen( imie );
    student->imie =( gchar * ) g_malloc( ilosc_znakow * sizeof( gchar ) ); // Przydzielenie ile_znakow elementow typu gchar
    g_stpcpy( student->imie, imie );
    student->wiek = wiek;
   
    return student;
}

gint porownaj_imiona( gconstpointer dane1, gconstpointer dane2 )
{
    Osoba * element1 =( Osoba * ) dane1;
    Osoba * element2 =( Osoba * ) dane2;
   
    return(( gint ) g_ascii_strcasecmp(( gchar * ) element1->imie,( gchar * ) element2->imie ) );
}

void znajdz_element( GSList * lista )
{
    Osoba * osoba = g_new( Osoba, 1 ); // // Przydzielenie pamieci dla jednej struktury Osoba
    GSList * element;
    gchar imie[ 20 ];
   
    g_print( "Podaj imie do znalezienia: " );
    scanf( "%s", imie );
    osoba->imie = g_strdup( imie );
   
    element = g_slist_find_custom( lista, osoba, porownaj_imiona );
    if( element )
         g_message( "Znaleziono na pozycji %d\n", g_slist_position( lista, element ) + 1 );
    else
         g_warning( "Nie znaleziono !!!\n" );
   
    g_free( osoba );
}

void wyswietl_elementy( gpointer data, gpointer prefiks )
{
    Osoba * element =( Osoba * ) data;
   
    g_print( "%s %s %d\n", prefiks, element->imie, element->wiek );
}

void wyswietl( GSList * lista )
{
    GSList * iterator = NULL;
    guint lp = 1;
   
    for( iterator = lista; iterator; iterator = iterator->next )
         g_print( "[%d]. %s %d\n", lp++,(( Osoba * ) iterator->data )->imie,(( Osoba * ) iterator->data )->wiek );
   
}



void zwolnij_pamiec( gpointer data )
{
    Osoba * element =( Osoba * ) data;
   
    g_free( element->imie );
    g_free( element );
   
}

int main( int argc, char * argv[] )
{
    GSList * lista_jednokierunkowa = NULL;
    Osoba * profesor =( Osoba * ) g_malloc( sizeof( Osoba ) ); // Przydzielenie pamieci dla struktury Osoba
    Osoba * element;
    gint wybor, pozycja;
    gchar imie[ 20 ];
   
   
    profesor->imie = "Tomasz";
    profesor->wiek = 46;
    lista_jednokierunkowa = g_slist_append( lista_jednokierunkowa, profesor );
   
    do {
        menu();
        g_print( "Lista zwiera %d elementow\n", g_slist_length( lista_jednokierunkowa ) );
        scanf( "%d", & wybor );
       
        switch( wybor )
        {
        case 1:
            element = utworz_element();
            lista_jednokierunkowa = g_slist_append( lista_jednokierunkowa, element );
            break;
        case 2:
            element = utworz_element();
            lista_jednokierunkowa = g_slist_prepend( lista_jednokierunkowa, element );
            break;
        case 3:
            element = utworz_element();
            g_print( "Podaj pozycje na, ktora wstawic: " );
            scanf( "%d", & pozycja );
            lista_jednokierunkowa = g_slist_insert( lista_jednokierunkowa, element, --pozycja );
            break;
        case 4:
            element = utworz_element();
            lista_jednokierunkowa = g_slist_insert_sorted( lista_jednokierunkowa, element, porownaj_imiona );
            break;
        case 5:
            lista_jednokierunkowa = g_slist_sort( lista_jednokierunkowa, porownaj_imiona );
            break;
        case 6:
            znajdz_element( lista_jednokierunkowa );
            break;
        case 7:
            g_slist_foreach( lista_jednokierunkowa, wyswietl_elementy, "-->" );
            break;
        case 8:
            wyswietl( lista_jednokierunkowa );
            break;
        }
       
    } while( wybor );
   
   
    /* Zwalnia przydzielona pamiec dla dynamicznie zalokowanej pamieci elementow */
    g_slist_free_full( lista_jednokierunkowa, zwolnij_pamiec );
    /* Zwalnia pamiec przydzielona na potzreby struktury listy */
    g_slist_free( lista_jednokierunkowa );
   
    system( "pause" );
    return 0;
}

W przykładzie funkcja porownaj_imiona używa funkcji, która porównuje znaki w zakresie znaków ASCII, więc łańcuch tekstowe używające polskich znaków diakrytycznych nie będą prawdiłowo sortowane.
Jako zadanie domowe przystosuj funkcję porownaj_imiona, aby operowała na polskich znakach diakrytycznych.

Lista dwukierunkowa łączona

Biblioteka GLib została wyposażona w zbiór funkcji operujących na listach dwukierunkowych, które wykorzystują typ danych GList. Funkcje te przypominają w działaniu funkcje operujące na listach jednokierunkowych, ale wszystkie posiadają w nazwie przedrostek g_list zamiast g_slist, oraz jak już wspomniałem używają typu danych GList zamiast GSList. Typ danych GList posiada podobnie jak w przypadku listy jednokierunkowej połączenie z następnym oraz dodatkowo poprzednim elementem listy, co znacząco ułatwia poruszanie się w tył listy.


Poniżej opisane zostaną funkcję, które nie występowały w przypadku list jednokierunkowych:

GList * g_list_first( GList * list );

Zwraca wskaźnik na pierwszy element listy. Argumentem wskaźnik na listę.

GList * g_list_nth_prev( GList * list, guint n );

Funkcja zwraca wskaźnik na poprzedni element wskazywany przez pozycje drugiego argumentu n, jeżeli poprzedni nie istnieje zwraca wartość NULL. Pierwszym argumentem jest wskaźnik na listę, z której pobierany jest ten element.

Mając wskaźnik konkretnego elementu za pomocą makra:
#define g_list_previous(list)

można pobrać wskaźnik na element poprzedzający jeśli istnieje, a w przeciwnym razie zwrócona zostanie wartość NULL. Jako list należy podać wskaźnik na listę, której to dotyczy.

Umieścić przykład

Tablice przemieszczania

Czy są tablice przemieszczania (ang. hash tables) ? Są to tablice umożliwiające szybkie dodawanie i pobieranie informacji: elementy są dodawane przy użyciu klucza, który służy do późniejszego pobrania wartości. Jeżeli nadal nie wiesz, co to za struktura danych, a poznałeś już np. język C++ to podpowiem Ci: tablica asocjacyjna, w przypadku C++ implementuje je szablon map. Istnie także pod nazwami tablica skojarzeniowa, mapa, słownik (ang. associative array, map, dictionary).
Tablice przemieszczania z biblioteki GLib wymagają napisania funkcji zwrotnej, która będzie obliczać wartość skrótu (hash value). Powinna ona zwracać możliwie unikalną wartość.

Najprostsza funkcja wyznaczająca wartość skrótu może wyglądać tak:
C/C++
guint funkcja_mieszajaca( gpointer klucz )
{
    gchar * wartosc =( gchar * ) klucz;
   
    return( wartosc[ 0 ] + wartosc[ 1 ] );
}

Ponieważ powyższa funkcja oblicza wartość skrótu na podstawie dwóch pierwszych znaków nie jest najlepszą funkcją mieszającą, dla słów aura, autobus, autostrada i  automatyczne  wartość skrótu byłaby taka sama. Funkcja mieszająca powinna robić lepszy użytek z danych i zwracać bardziej unikalny skrót. Kolejna przykładowa funkcja mieszająca używa całego łańcucha, aby obliczyć skrót; pracuje jednak dłużej, niż poprzednia funkcja mieszająca:
C/C++
guint funkcja_mieszajaca( gpointer klucz )
{
    gchar * lancuch_klucz =( gchar * ) klucz;
    guint wartosc_skrotu = 0;
    gint i;
   
    if( klucz == NULL ) return( 0 );
   
    for( i = 0; i < strlen( lancuch_klucz ); i++ )
    {
        wartosc_skrotu =( wartosc_skrotu >> 4 ) +( wartosc_skrotu ^( guint ) lancuch_klucz[ i ] );
    }
    return wartosc_skrotu;
}

Dodatkowo należy napisać funkcję porównywującą dla tablicy przemieszczenia. Krok ten jest potrzebny, ponieważ funkcja mieszająca może zwrócić taki sam skrót dla dwóch różnych łańcuchów. Funkcja porównująca skróty łańcuchowe może wykorzystać funckcję biblioteki GLib g_strcmp0, bądź stanadrdową funkcję jezyka C strcmp:
C/C++
gint porownaj_skrot( gpointer tekst1, gpointer tekst2 )
{
    return( !g_strcmp0(( char * ) tekst1,( char * ) tekst2 ) );
}

Uff. Napisane zostały odpowiednie funkcję teraz można utworzyć tablice przemieszczania:

GHashTable * g_hash_table_new( GHashFunc hash_func, GEqualFunc key_equal_func );

Zwraca wskaźnik na nową utworzoną tablice przemieszczania. Argumentami są funkcja wyznaczająca wartość skrótu, oraz funkcja porównywująca wartość skrótu.

void g_hash_table_insert( GHashTable * hash_table, gpointer key, gpointer value );

Funkcja dodaje element do tablicy. Pierwszym argumentem jest wskaźnik na tablice przemieszczania, drugim wskaźnik na klucz, trzecim wskaźnik na wartość. W przypadku kiedy klucz już istniał, wartość zostanie zastąpiona nową wartością.

gboolean g_hash_table_remove( GHashTable * hash_table, gconstpointer key );

Usuwa klucz podany w drugim argumencie jako wskaźnik na klucz, w tablicy podanej jako wskaźnik w pierwszym argumencie. Funkcja zwraca wartość TRUE kiedy klucz istniał i został usunięty.

guint g_hash_table_size( GHashTable * hash_table );

Zwraca ilość elementów w tablicy. Argumentem jest wskaźnik do tablicy przemieszczania.

Aby przeglądnąć wszystkie elementy tablicy przemieszczania należy użyć funkcji:
void g_hash_table_foreach( GHashTable * hash_table, GHFunc func, gpointer user_data );

Pierwszym argumentem jest wskaźnik na tablice przemieszczania, drugim funkcja wywoływana dla każdego elementu w tablicy z parametrem podanym jako trzeci argument.

Kiedy tablica mieszająca jest już nie potrzebna należy zwolnić pamięć przez nią zajmowaną:
void g_hash_table_destroy( GHashTable * hash_table );

Argumentem jest wskaźnik na tablice mieszającą.

Poniżej przykład z zastosowaniem powyższych funkcji:
C/C++
#include <glib.h>

void wyswietl( gpointer klucz, gpointer wartosc, gpointer dane )
{
    g_print( dane, klucz, wartosc );
}

int main( int argc, char * argv[] )
{
    GHashTable * tablica_mieszajaca;
   
    tablica_mieszajaca = g_hash_table_new(( GHashFunc ) funkcja_mieszajaca,( GEqualFunc ) porownaj_skrot );
    g_hash_table_insert( tablica_mieszajaca, "Ssak", "Małpa" );
    g_hash_table_insert( tablica_mieszajaca, "Płaz", "Żaba" );
    g_print( "Ilość elementów w tablicy: %d\n", g_hash_table_size( tablica_mieszajaca ) );
    g_hash_table_insert( tablica_mieszajaca, "Ssak", "Delfin" );
    g_print( "Dodałem element o kluczu 'Ssak' i wartości 'Delfin'. Teraz jest elementów w tablicy: %d\n", g_hash_table_size( tablica_mieszajaca ) );
    g_hash_table_insert( tablica_mieszajaca, "Gad", "Krokodyl" );
    g_print( "Ilość elementów w tablicy: %d\n", g_hash_table_size( tablica_mieszajaca ) );
    g_hash_table_foreach( tablica_mieszajaca,( GHFunc ) wyswietl, "Tablica zawiera klucz %s z wartością %s \n" );
    g_hash_table_remove( tablica_mieszajaca, "Płaz" );
    g_print( "Usunąłem klucz 'Płaz', aktualnie tablica zawiera %d elementy", g_hash_table_size( tablica_mieszajaca ) );
   
    g_hash_table_destroy( tablica_mieszajaca );
   
    //system ("pause");
   
    return( 0 );
}

Wcześniej napisałem, że należy napisać samemu swoje funkcję: mieszającą i  porównujące. Nawet przykład powyżej korzystał z własnych funkcji funkcja_mieszajaca i porownaj_skrot. Nie musisz ich pisać ponieważ biblioteka zawiera własne funkcję mieszające, które w większości przypadków są wystarczające:
  • g_direct_hash - wyznacza wartość skrótu na podstawie wskaźników
  • g_int_hash - wyznacza wartość skrótu na podstawie liczb int
  • g_str_hash - wyznacza wartość skrótu na podstawie łańcuchów tekstowych

oraz wbudowane funkcję porównujące:
  • g_direct_equal - porównuje wskaźniki
  • g_int_hash - porównuje liczby int
  • g_str_hash - porównuje łańcuchy tekstowe

Kolejny przykład wykorzystujący funkcje wbudowane w biblioteke GLib:
C/C++
#include <glib.h>

void wyswietl( gpointer klucz, gpointer wartosc, gpointer dane )
{
    g_print( "Miesiąc %s, dni %d\n", klucz, GPOINTER_TO_INT( wartosc ) );
}

int main( int argc, char * argv[] )
{
    GHashTable * tablica_mieszajaca;
    gint liczba_dni;
    gchar miesiac[ 20 ];
   
    tablica_mieszajaca = g_hash_table_new(( GHashFunc ) g_str_hash,( GEqualFunc ) g_str_equal );
    g_hash_table_insert( tablica_mieszajaca, "Styczeń", GINT_TO_POINTER( 31 ) );
    g_hash_table_insert( tablica_mieszajaca, "Luty", GINT_TO_POINTER( 28 ) );
    g_hash_table_insert( tablica_mieszajaca, "Marzec", GINT_TO_POINTER( 31 ) );
    g_hash_table_insert( tablica_mieszajaca, "Kwiecień", GINT_TO_POINTER( 30 ) );
    g_hash_table_insert( tablica_mieszajaca, "Maj", GINT_TO_POINTER( 31 ) );
    g_hash_table_insert( tablica_mieszajaca, "Czerwiec", GINT_TO_POINTER( 30 ) );
    g_hash_table_insert( tablica_mieszajaca, "Lipiec", GINT_TO_POINTER( 31 ) );
    g_hash_table_insert( tablica_mieszajaca, "Sierpień", GINT_TO_POINTER( 30 ) );
    g_hash_table_insert( tablica_mieszajaca, "Wrzesień", GINT_TO_POINTER( 31 ) );
    g_hash_table_insert( tablica_mieszajaca, "Listopad", GINT_TO_POINTER( 30 ) );
    g_hash_table_insert( tablica_mieszajaca, "Grudzień", GINT_TO_POINTER( 31 ) );
   
    g_hash_table_foreach( tablica_mieszajaca,( GHFunc ) wyswietl, NULL );
   
    g_print( "\nPodaj nazwę miesiąca: " );
    scanf( "%s", miesiac );
    liczba_dni = GPOINTER_TO_INT( g_hash_table_lookup( tablica_mieszajaca, miesiac ) );
    if( liczba_dni == NULL )
         g_print( "Nie ma takiego miesiąca\n" );
    else
         g_print( "Miesiąc %s ma %d dni\n", miesiac, liczba_dni );
   
    g_hash_table_destroy( tablica_mieszajaca );
   
    //system ("pause");
   
    return( 0 );
}

Tablice GArray

Tablice GArray są bardzo podobne do zwykłych tablic języka C. Struktura ta zawiera informacje o rozmiarze, dzięki temu dodanie elementu do tablicy GArray automatycznie inkrementuje rozmiar (ilość danych w tablicy), analogicznie usunięcie elementu dekrementuje rozmiar. Tablica GArray jest o wiele szybsza w przypadku dostępu losowego do elementów niż struktura GList. Elementy można dodawać pojedynczo lub "hurtowo".

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

Funkcja tworzy nową pustą tablice GArray, zwracając wskaźnik na nią. Pierwszy argument decyduje czy tablica ma mieć dodatkowy element zwany "terminatorem" na jej końcu ustawiony na 0, ustawienie drugiego elementu na wartość TRUE ustawia wszystkie elementy tablicy na wartość 0, trzecim argumentem jest rozmiar elementu w bajtach.

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

Funkcja ustawia / zmienia ilość elementów w tablicy. Zwraca wskaźnik na tablice GArray. Pierwszym argumentem jest wskaźnik na tablice, która będzie zmieniana. Drugim jest ilość elementów.

GArray * g_array_sized_new( gboolean zero_terminated, gboolean clear_, guint element_size, guint reserved_size );

Funkcja tworzy nową tablice GArray o zadanej ilości elementów. Zwracając wskaźnik na nowo utworzoną tablice GArray. Pierwszy argument decyduje czy tablica ma mieć dodatkowy element zwany "terminatorem" na jej końcu ustawiony na 0, ustawienie drugiego elementu na wartość TRUE ustawia wszystkie elementy tablicy na wartość 0, trzecie argument element_size ustawia rozmiar elementu. Ostatni reserved_size ustawia ilość elementów w tablicy. Należy pamiętać, że pomimo zarezerwowania pamięci dla elementów ilość elementów (array->len) to 0.

#define g_array_append_val(a, v)

Makro dodaje element wskazywany przez v na koniec tablicy GArray wskazywanej przez a.

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

Funkcja dodaje elementy na koniec tablicy zwracając wskaźnik na nią. Pierwszym argumentem jest wskaźnik na tablice GArray,drugim wskaźnik na elmenty do dodania, trzecim ilość elementów, które mają być dodane.

Analogicznie do powyższych, które dodają element / elementy  na koniec tablicy istnieją analogiczne, które dodają element /elementy  na początek tablicy:
C/C++
#define g_array_prepend_val(a, v)

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

Chcąc dodać element / elementy  w określone miejsce tablicy GArray należy skorzystać:
C/C++
#define g_array_insert_val(a, i, v)

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

Argumenty takie same jak w powyższych poza i w makrze oraz index_ w funkcji, które decydują o miejscu, w które należy dodać.

Chcąc pobrać określony element z tablicy GArray należy skorzystać z makra:
#define g_array_index(a, t, i)

Zwraca element z tablicy a, wskazywany przez i. Wartość zwracana jest typu t. Uwaga: powyższe makro można również wykorzystać do zmiany wartość istniejącego już elementu tablicy.

Kiedy istnieje konieczność usunięcia elementu / elementów należy użyć następujących funkcji:
GArray * g_array_remove_index( GArray * array, guint index_ );

Usuwa element z tablicy wskazywanej w pierwszym argumencie na pozycji wskazywanej przez drugi argument. Zwraca wskaźnik na tablice GArray.

GArray * g_array_remove_range( GArray * array, guint index_, guint length );

Funkcja usuwa lenght elementów z tablicy wskazywanej w pierwszym argumencie na pozycji wskazywanej przez drugi argument. wraca wskaźnik na tablice GArray.

Kolejne funkcje związane są z sortowaniem elementów w tablicy GArray:
void g_array_sort( GArray * array, GCompareFunc compare_func );

Pierwszym argumentem jest wskaźnik na tablice GArray, która ma być posortowana, drugi argument wskazuje na funkcję porównowywującą, której prototyp został szerzej opisany w punkcie 5.1.1 dotyczącym list jednokierunkowych.

void g_array_sort_with_data( GArray * array, GCompareDataFunc compare_func, gpointer user_data );

Podobna do powyższej funkcji sortującej, jednak funkcja sortująca compare_func otrzymuje dodatkowe dane wskazywany przez argument user_data.

Kiedy tablica GArray nie jest już potrzebna należy skorzystać z funkcji:
gchar * g_array_free( GArray * array, gboolean free_segment );


Poniżej programiki wykorzystujące poznane funkcje:
C/C++
#include <glib.h>

void wyswietl_tablice( GArray * tab )
{
    guint i;
   
    for( i = 0; i < tab->len; i++ )
         g_print( "%d ", g_array_index( tab, gint, i ) );
   
    g_print( "\n" );
}

int main( int argc, char ** argv )
{
    GArray * tablica;
    gint a, b, c;
   
    /* Tworzy nową tablice GArray, która przechowuje elementy o rozmiarze gint, bez NULL terminatora oraz bez zerowania alokowanych elemetów */
    tablica = g_array_new( FALSE, FALSE, sizeof( gint ) );
   
    /* Dodaje element a do tablicy GArray */
    a = 2;
    g_array_append_val( tablica, a );
   
    /* Dodaje element b do tablicy GArray */
    b = 4;
    g_array_append_val( tablica, b );
   
    /* Dodaje element c do tablicy GArray */
    c = 5;
    g_array_append_val( tablica, c );
   
    wyswietl_tablice( tablica );
   
    /* Zwonienie pamięci zalokowanej przez g_array_new */
    g_array_free( tablica, FALSE );
   
    return( 0 );
}


C/C++
#include <glib.h>

int main( int argc, char * argv[] )
{
    GArray * tablica;
    gchar * pierwszy = "Witaj", * drugi = "piekny", * trzeci = "swiecie";
   
    /* Tworzy nową tablice GArray, która przechowuje elementy o rozmiarze gchar*, bez NULL terminatora oraz bez zerowania alokowanych elemetów */
    tablica = g_array_new( FALSE, FALSE, sizeof( gchar * ) );
   
    /* Dodanie kolejnych elementów do tablicy GArray */
    g_array_append_val( tablica, pierwszy );
    g_array_append_val( tablica, drugi );
    g_array_append_val( tablica, trzeci );
   
    g_print( "W tablicy GArray jest %d elementy\n", tablica->len );
    /* Wyświetla 1 element tablicy GArray */
    g_print( "Pierwszy element: '%s'\n", g_array_index( tablica, gchar *, 0 ) );
   
    /* Wyświetla 3 element tablicy GArray */
    g_print( "Trzeci element: '%s'\n", g_array_index( tablica, char *, 2 ) );
   
    /* Usuwa 2 element tablicy GArray */
    g_array_remove_index( tablica, 1 );
   
    g_print( "W tablicy GArray jest %d elementy\n", tablica->len );
   
    g_array_free( tablica, FALSE );
   
    return 0;
}

C/C++
int main( int argc, char * argv[] )
{
    GArray * tablica;
   
    /* Tworzy nową tablice GArray, która przechowuje elementy o rozmiarze gchar*, bez NULL terminatora oraz bez zerowania alokowanych elemetów */
    tablica = g_array_new( FALSE, FALSE, sizeof( gchar * ) );
   
    /* Tworzy nową tablice GArray, która przechowuje 10 elementów o rozmiarze gint, z NULL terminatorem oraz zeruje alokowane elemety */
    tablica = g_array_sized_new( TRUE, TRUE, sizeof( gint ), 10 );
   
    g_print( "Prealokacja tablicy GArray ukryta, dlatego jej rozmiar == %d\n", tablica->len );
    g_print( "Tablica GArray zainicjowana zerami, czwarty elemnet ma wartosc: %d\n", g_array_index( tablica, gint, 3 ) );
   
    g_array_free( tablica, FALSE );
   
    /* Tworzy nową tablice GArray, która przechowuje elementy o rozmiarze gchar, bez NULL terminatora oraz bez zerowania alokowanych elemetów */
    tablica = g_array_new( FALSE, FALSE, sizeof( char ) );
   
    /* Zmienia ilość elmentów w tablicy na 12 */
    g_array_set_size( tablica, 12 );
   
    g_print( "Tablica GArray zawiera %d elemnetow\n", tablica->len );
   
    g_array_free( tablica, FALSE );
   
    /* Tworzy nową tablice GArray, która przechowuje elementy o rozmiarze gchar, bez NULL terminatora oraz bez zerowania alokowanych elemetów */
    tablica = g_array_new( FALSE, FALSE, sizeof( gchar * ) );
    gchar * tekst1 = g_strdup( "Witaj swiecie" );
    gchar * tekst2 = g_strdup( "Witaj piekny swiecie" );
   
    /* Dodanie elemntu tekst1 do tablicy */
    g_array_append_val( tablica, tekst1 );
   
    /* Dodanie elemntu tekst2 do tablicy */
    g_array_append_val( tablica, tekst2 );
   
    g_print( "Pierwszy element tablicy GArray zawiera: %s\n", g_array_index( tablica, gchar *, 0 ) );
   
    g_array_free( tablica, TRUE );
   
    return 0;
}

Proszę w powyższym przykładzie zwrócić na przedostatnią funkcje
g_array_free( tablica, TRUE );
 zawiera wartość TRUE. Znaczy to, że ma dodatkowo poza zwolnieniem pamięci zajmowanej przez GArray, zwolnić pamięć zajmowaną przez elementy tablicy.
Pamięć dla kolejnych elemnetów była przydzielana funkcją g_strdup, więc dla każdego elementu należałoby zwolnić pamięć funkcją g_free, ustawienie wartości TRUE spowoduje automatyczne wywołanie funkcji g_free dla wszystkich elementów w tablicy GArray.

Wstawianie elementów na określone pozycje:
C/C++
#include <glib.h>
void wyswietl_tablice( GArray * tab )
{
    guint i;
   
    for( i = 0; i < tab->len; i++ )
         g_print( "%d ", g_array_index( tab, gint, i ) );
   
    g_print( "\n" );
}

int main( int argc, char * argv[] )
{
    GArray * tablica;
   
    tablica = g_array_new( FALSE, FALSE, sizeof( gint ) );
   
    gint x[ 2 ] = { 1, 6 };
    /* Dodaje 2 elementy na koniec tablicy */
    g_array_append_vals( tablica, & x, 2 );
    wyswietl_tablice( tablica );
   
    /* Wstawienie elementu od na określoną pozycje */
    g_print( "Wstawiam w pozycji drugiej element '2'\n" );
    gint y = 2;
    g_array_insert_val( tablica, 1, y );
    wyswietl_tablice( tablica );
   
    /* Wstawienie kilku elementów od na określoną pozycje */
    g_print( "Wstawiam  trzy elementy '3 4 5' od trzeciej pozycji\n" );
    gint z[ 3 ] = { 3, 4, 5 };
    g_array_insert_vals( tablica, 2, & z, 3 );
    wyswietl_tablice( tablica );
   
    /* Dodanie elementu na początek tablicy */
    gint v = 0;
    g_array_prepend_val( tablica, v );
    wyswietl_tablice( tablica );
   
    g_array_free( tablica, FALSE );
   
    return 0;
}

Usuwanie elementów z tablicy GArray:
C/C++
#include <glib.h>
void wyswietl_tablice( GArray * tab )
{
    guint i;
   
    for( i = 0; i < tab->len; i++ )
         g_print( "%d ", g_array_index( tab, gint, i ) );
   
    g_print( "\n" );
}

int main( int argc, char * argv[] )
{
    GArray * tablica;
   
    tablica = g_array_new( FALSE, FALSE, sizeof( gint ) );
   
    gint x[ 6 ] = { 1, 2, 3, 4, 5, 6 };
   
    g_array_append_vals( tablica, & x, 6 );
    wyswietl_tablice( tablica );
   
    g_print( "Usuwam pierwszy element\n" );
    g_array_remove_index( tablica, 0 );
    wyswietl_tablice( tablica );
   
    g_print( "Usuwam elementy od drugiego do czwartego\n" );
    g_array_remove_range( tablica, 1, 3 );
    wyswietl_tablice( tablica );
   
    g_print( "Usuwam pierwszy elementy - funkcja, ktora jest bardzo szybka :)\n" );
    g_array_remove_index_fast( tablica, 0 );
    wyswietl_tablice( tablica );
   
    g_array_free( tablica, FALSE );
   
    return 0;
}

Sortowanie tablic GArray:
C/C++
#include <glib.h>
void wyswietl_tablice( GArray * tab )
{
    guint i;
   
    for( i = 0; i < tab->len; i++ )
         g_print( "%d ", g_array_index( tab, gint, i ) );
   
    g_print( "\n" );
}

gint porownaj_wartosci( gpointer a, gpointer b )
{
    gint * x =( gint * ) a;
    gint * y =( gint * ) b;
   
    return * x - * y;
}

int main( int argc, char * argv[] )
{
    GArray * tablica;
   
    tablica = g_array_new( FALSE, FALSE, sizeof( gint ) );
   
    gint x[ 6 ] = { 2, 1, 4, 3, 6, 5 };
   
    g_array_append_vals( tablica, & x, 6 );
    wyswietl_tablice( tablica );
   
    g_print( "Sortuje tablice GArray\n" );
    g_array_sort( tablica,( GCompareFunc ) porownaj_wartosci );
    wyswietl_tablice( tablica );
   
    g_array_free( tablica, FALSE );
   
    return 0;
}