Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 15 GTK+
  gp 15.1 Was ist GTK+?
    gp 15.1.1 Was sind GDK und Glib?
    gp 15.1.2 Schnittstellen von GTK+ zu anderen Programmiersprachen
    gp 15.1.3 GTK+ und GNOME
    gp 15.1.4 GTK+ Version 1.2 und 2.x
    gp 15.1.5 GTK+-Aufbau des Kapitels
  gp 15.2 GTK+-Anwendungen übersetzen
  gp 15.3 Eine Einführung in die Glib-Bibliothek
    gp 15.3.1 Datentypen
    gp 15.3.2 Routinen
    gp 15.3.3 Assertions-Funktionen
    gp 15.3.4 Speicherverwaltung
    gp 15.3.5 Stringbearbeitung
    gp 15.3.6 Selbstverwaltender Stringpuffer
    gp 15.3.7 Timer
    gp 15.3.8 Dynamische Arrays
    gp 15.3.9 Listen, Hashtabellen und binäre Bäume
    gp 15.3.10 Ausblick Glib
  gp 15.4 Grundlagen der GTK+-Programmierung
    gp 15.4.1 Die Umgebung initialisieren
    gp 15.4.2 Widgets erzeugen und ggf. die Attribute setzen
    gp 15.4.3 Eine Callback-Funktion einrichten, um Events abzufangen
    gp 15.4.4 Eine GTK+-Anwendung beenden
    gp 15.4.5 Die hierarchische Anordnung der Widgets definieren
    gp 15.4.6 Widgets anzeigen
    gp 15.4.7 Signale und Events abfangen und bearbeiten – (Events-)Verarbeitungsschleife
    gp 15.4.8 GTK+ und Umlaute (Zeichenkodierung)
  gp 15.5 Fenster – GtkWindow
    gp 15.5.1 Dialogfenster (Dialogboxen)
    gp 15.5.2 GtkMessageDialog
  gp 15.6 Anzeige-Elemente
    gp 15.6.1 Text – GtkLabel
    gp 15.6.2 Trennlinie – GtkSeparator
    gp 15.6.3 Grafiken – GtkImage
    gp 15.6.4 Statusleiste – GtkStatusbar
    gp 15.6.5 Fortschrittsbalken – GtkProgressBar
  gp 15.7 Behälter
    gp 15.7.1 Boxen – GtkBox
    gp 15.7.2 Aufteilungen, Register und Button-Box
    gp 15.7.3 Tabellen – GtkTable
    gp 15.7.4 Ausrichtung – GtkAlignment
  gp 15.8 Buttons und Toogled-Buttons
    gp 15.8.1 Buttons allgemein
    gp 15.8.2 Radio-Buttons (GtkRadioButton)
    gp 15.8.3 GtkRadioButton, GtkCheckButton und GtkToggleButton
    gp 15.8.4 Signale für Buttons (GtkButton)
  gp 15.9 Dateneingabe
    gp 15.9.1 Textfelder – GtkEntry
    gp 15.9.2 Schieberegler – GtkScale
    gp 15.9.3 Zahlenfelder – GtkSpinButton
    gp 15.9.4 Einstellungen – GtkAdjustment
    gp 15.9.5 GtkEditable
  gp 15.10 Menü und Toolbar
    gp 15.10.1 Menü – GtkItemFactory
    gp 15.10.2 Toolbar – GtkToolbar
    gp 15.10.3 Options-Menü – GtkOptionsMenu
    gp 15.10.4 Combo-Boxen – GtkCombo
  gp 15.11 Mehrzeiliger Text
    gp 15.11.1 Text(editor) – GtkTextView, GtkTextBuffer
    gp 15.11.2 Scrollendes Fenster – GtkScrolledWindow
  gp 15.12 Auswählen (Selection)
    gp 15.12.1 Dateiauswahl – GtkFileSelection
  gp 15.13 Events
  gp 15.14 Weitere Widget- und GTK+-Elemente im Überblick


Rheinwerk Computing

15.3 Eine Einführung in die Glib-Bibliothek  downtop

GTK+ und GDK sind für die grafische Seite einer Anwendung verantwortlich, und Glib ist die nicht sichtbare Schnittstelle für den Anwender. Alles, was hinter den grafischen Kulissen so passiert, wird gewöhnlich über die Glib gemacht. Diese Bibliothek beinhaltet Funktionen für die Speicherverwaltung, Stringmanipulationen, verkettete Listen, Arrays, Bäumen und eine Menge mehr. Natürlich finden Sie im Buch nur wieder einen kleinen Einblick in die Glib-Welt, was aber für die weitere Verwendung mithilfe der Dokumentation zur Glib völlig ausreichen sollte. Trotzdem kommen Sie, sofern Ihre Programme GTK+- oder GNOME-Bibliotheken verwenden werden, nicht um das Erlernen der Glib herum. Um Glib zu verwenden, müssen Sie die Headerdatei glib.h in die Programme mit einbinden. Es kann außerdem sein, wenn Sie eine Anwendung mit GTK+ oder GNOME schreiben, dass die Glib-Bibliothek benötigt wird, obwohl Sie keine dieser Funktionen in der Anwendung verwendet haben.

Für Funktionen der Glib wurde folgende Schreibweise festgelegt: Alle Glib-Funktionen beginnen mit dem Präfix g_ – die Datentypen mit dem Präfix g (ohne Unterstrich) –, und Makros werden meist beginnend mit einem großen G oder GLIB gekennzeichnet.


Rheinwerk Computing

15.3.1 Datentypen  downtop

Jeder, der schon einmal Software auf ein anderes System portieren musste, weiß, wie das enden kann. Spätestens wenn auf dem einen System eine vorzeichenlose 16-Bit-Ganzzahl deklariert ist, lernt man beim Portieren die andere Seite von C wieder kennen. Um die richtige Darstellung von Datentypen kümmert sich die Glib für Sie. Somit wäre das Problem schon weg. Folgende Datentypen und deren Gegenstück (falls vorhanden) in Standard-C sind in der Headerdatei glib.h definiert:


Tabelle 15.1    Datentypen der Glib-Bibliothek

Glib-Datentyp Standard-C-Datentyp
gchar char
guchar unsigned char
gint int
guint unsigned int
gshort short
gushort unsigned short
glong long
gulong unsigned long
gfloat float
gdouble double
gintN int mit genau n Bit Breite (möglicher Wert für n = 8, 16, 32, 64)
gpointer void *, typloser Zeiger
gconstpointer const void *, konstanter typloser Zeiger
gboolean Wahrheitswert, entweder TRUE oder FALSE
gsize und gssize signed und unsigned Werte, welche die Größe einer Datenstruktur repräsentieren

Die Datentypen gboolean, gsize und gssize sind keinen Standarddatentypen von C. Die 8-, 16-, 32- und 64-Bit-Integer (gintN) sind sehr sinnvoll, wenn garantiert werden muss, dass ein Integer eine bestimmte Bitlänge haben muss. Da nicht auf jedem System eine Darstellung von 64-Bit-Werten möglich ist, müssen Sie bei Verwendung eines solchen Typs erst die symbolische Konstante G_HAVE_GINT64 überprüfen, ob diese gesetzt ist.


Hinweis   Es ist auch möglich und somit nicht falsch, wenn Sie bei einer auf Glib basierenden Anwendung die Datentypen mit denen der Standard-C-Funktionen vermischen. Sinnvoll ist dies aber nicht.



Rheinwerk Computing

15.3.2 Routinen  downtop

Für den täglichen Gebrauch finden Sie in der Glib eine Menge Routinen, die meistens sicherere Wrapper-Funktionen zu den Standardfunktionen von C sind. Aufgeteilt werden diese Funktionen in Hilfsfunktionen und Funktionen zur Manipulation von Datenstrukturen. Zuerst werden die einfachen Hilfsfunktionen beschrieben.

Ausgabe von Fehlern und anderen Nachrichten

Die häufigste Methode, um ein Programm zu debuggen bzw. eine Meldung auszugeben, ist die, eine Nachricht auf die Konsole oder in eine Datei zu schreiben. Die Glib hat hierfür zum Glück mehrere Möglichkeiten zur Verfügung.

Die wohl einfachste und am häufigsten verwendete Methode dürften wohl die Funktionen g_print() und g_printerr() sein:

void g_print(const gchar *format, ...);
void g_printerr(const gchar *format, ...);

Die Funktion g_print() ist als Ersatz für die Standardfunktion printf() zu sehen. Mit g_print() wird der formatierte, variabel lange Textstring auf die Standardausgabe (stdout) ausgegeben (sofern diese nicht umgeleitet wurde). Die Funktion g_printerr() hingegen schreibt die Ausgabe auf die Standardfehlerausgabe (bekannt als stderr). Sicherlich stellen Sie sich hierbei die Frage, warum sollten Sie jetzt diese beiden Funktionen im Gegensatz zu printf() und fprintf() verwenden? Der Grund ist simpel und einfach. Mit den beiden Funktionen können Sie noch zusätzlich einen Handler einrichten. Mit diesem Handler wiederum können Sie die Ausgabe in eine Datei umleiten, in ein GTK+-Widget oder in ein anderes Programm. Um allerdings die Standardausgabe der beiden Funktionen (g_print() und g_printerr()) zu verändern, müssen Sie die entsprechenden Funktionen g_set_print_handler() bzw. g_set_printerr_handler() verwenden. Jede der beiden Funktionen erhält einen Zeiger auf eine selbst geschriebene Funktion, die als Argument einen String erwartet. Mehr dazu entnehmen Sie bei Bedarf bitte der Dokumentation.

Weitere Funktionen, die meistens noch einen Zusatz zum eigentlichen Formatstring ausgeben, sind:

void g_message(const gchar *format, ...);
void g_warning(const gchar *format, ...);
void g_error(const gchar *format, ...);
const gchar *g_strerror(gint errnum); 

Die Funktionen g_message() und g_warning() sind der Funktion g_print() recht ähnlich. Mit g_message() wird vor dem String die Zeichenfolge Message: ausgegeben, und bei g_warning() wird die Textfolge *** (process:1234) WARNING *** dem eigentlichen String vorangestellt. Die Funktion g_error() hingegen setzt vor den Formatstring noch den String *** ERROR *** und beendet anschließend mit dem Signal SIGABRT das Programm. g_strerror() erwartet als Parameter die Fehlervariable errno und gibt einen dementsprechenden String aus, wofür der Fehlercode der Ganzzahl steht. Diese Funktion ist das Pendant zur Funktion perror(). Es wird empfohlen, diese Funktionen immer bei der Ausgabe von Fehlermeldungen zu verwenden.

Hierzu folgt ein einfaches Listing, das die eben vorgestellten Funktionen in der Praxis zeigt.

/* glib1.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <gtk/gtk.h>
int main(void) {
   FILE *f1;
   const gchar *text = "Ausgabe mit g_message";
   
   g_print("Ausgabe mit g_print\n");
   g_printerr("Ausgabe mit g_printerr\n");
   g_message("%s\n",text);
   g_warning("Ausgabe mit g_warning\n");
   f1 = fopen("abcd", "r");
   if( f1 == NULL ) {
      printf("Fehler : %s\n", g_strerror(errno));
   }   
   g_error("Ausgabe mit g_error (FATALER FEHLER - Ende)\n");
   return EXIT_SUCCESS;   
}

Das Programm bei der Ausführung:

$ gcc -o glib1 glib1.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib1
Ausgabe mit g_print
Ausgabe mit g_printerr
** Message: Ausgabe mit g_message
** (process:2272): WARNING **: Ausgabe mit g_warning
Fehler : No such file or directory
** ERROR **: Ausgabe mit g_error (FATALER FEHLER - Ende)
aborting...
Abgebrochen

Rheinwerk Computing

15.3.3 Assertions-Funktionen  downtop

Natürlich gibt es bei der Glib auch eine assert()-Version mit g_assert(), wobei hier die Funktion g_assert() voll und ganz der Funktion von assert() entspricht, z. B.:

int x=5, y=4, z=3;
g_assert( x > y );  /* OK */
g_assert( x > z );  /* OK */
g_assert( z > y );  /* Fehler !!! */

Folgende Fehlermeldung wird dabei ausgegeben:

** ERROR **:
file glibx.c: line xx (main): assertion failed: (z > y)
aborting...
Abgebrochen

In Standard-C mussten Sie assert() mit einem Flag im Programmcode außer Kraft setzen. Glib macht es Ihnen dabei noch einfacher, indem Sie beim Übersetzen das Flag -DG_DISABLE_ASSERT verwenden:

$ gcc -o -DG_DISABLE_ASSERT glibx glibx.c \
  `pkg-config --libs --cflags gtk+-2.0`

Die Glib stellt Ihnen außerdem mit g_assert_not_reached() noch ein anderes Assert ohne Argumente zur Verfügung. Dieses kann z. B. verwendet werden, wenn eine Situation eintrifft, die einfach niemals hätte eintreffen sollen/können. not reached eignet sich in der Regel nur für die Fehlersuche, z. B.

while(1) {
    ...
}
g_assert_not_reached();

Hier könnte es ja durch irgendeinen Speicherüberlauf passieren, dass die while-Schleife abgebrochen wird, wie immer das auch passieren kann. In einem solchen Fall kann man sagen »line should never be reached«. Dabei wird folgende Fehlermeldung ausgegeben:

** ERROR **:
file glibx.c: line 9 (main): should not be reached aborting...

Weitere Routinen, um z. B. eine Funktion zu beenden, wenn eine bestimmte Bedingung nicht eintraf, wären die Funktionen g_return_if_fail() und g_return_val_if_fail(). Beide Funktionen können wie g_assert() verwendet werden, nur ohne dass das Programm beendet wird. Ein Beispiel mit g_return_if_fail().

/* glib2.c */
#include <gtk/gtk.h>
static void myfunc( gint val1, gint val2 ) {
   g_return_if_fail( (val1 > 0 && val2 > 0) );
   /* Tu was ... */
   g_print("val1 und val2 haben nicht den Wert 0\n");
   return;
}
int main(void) {
   myfunc(5, 4);
   myfunc(3, 1);
   myfunc(0, 3); /* Fehler - aber nicht kritisch */
   g_print("Kein Programmabbruch!\n");   
   return 0;   
}

Das Programm bei der Ausführung:

$ gcc -o glib2 glib2.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib2
val1 und val2 haben nicht den Wert 0
val1 und val2 haben nicht den Wert 0
** CRITICAL **: 
file glib2.c: line 6 (myfunc): assertion `(val1 > 0 && val2 > 0)' failed.
Kein Programmabbruch!

Rheinwerk Computing

15.3.4 Speicherverwaltung  downtop

Folgende Funktionen werden Ihnen von der Glib zur Reservierung und zum Freigeben von Speicherplatz zur Verfügung gestellt. Als Vergleich sei hier auch das Gegenstück aus der Standardbibliothek angegeben:


Tabelle 15.2    Funktionen zum Reservieren und Freigeben von Speicherplatz

Glib-Funktion entspricht …
gpointer g_malloc(gulong n_bytes) void *malloc(size_t size)
gpointer g_malloc0(gulong n_bytes) wie malloc(), initialisiert Speicher wie calloc()
gpointer g_try_malloc(gulong n_bytes) wie malloc()
gpointer g_realloc( gpointer mem, gulong n_bytes ) wie void *realloc(void *ptr, size_ t size)
gpointer g_try_realloc(gpointer mem, gulong n_bytes) wie realloc()
void g_free(gpointer mem) void free(void *ptr)

Folgende besonderen Merkmale weist g_malloc() im Gegensatz zu malloc() in Standard-C auf:

gp  g_malloc() gibt NULL zurück, wenn die Größe des zu reservierenden Speichers 0 ist. Das liegt aber daran, weil man sich aus Portabilitätsgründen innerhalb der GNU libc dazu entschlossen hat, malloc(0) gültig zu machen.
gp  g_malloc() beendet das Programm von selbst, wenn ein Fehler (NULL) bei der Allozierung aufgetreten ist. Sie müssen nicht mehr selbst auf NULL überprüfen. g_malloc() eignet sich daher nicht für eigene Bibliotheken, da diese NULL zurückgeben müssen.
gp  Die _try_-Versionen g_try_malloc() und g_try_realloc() beenden sich im Gegensatz zu den Versionen ohne _try_ (g_malloc(); g_realloc()) nicht selbstständig bei einem Fehler - sondern geben C-typisch NULL (bei Auftreten eines Fehlers) zurück.

Hierzu ein Listing, das Ihnen die g-Familie der malloc()-Fraktion demonstriert:

/* glib3.c */
#include <gtk/gtk.h>
#define MMAX 100
int main(void) {
   gpointer *mem, *dup;
   gchar *text = "Text zum Kopieren";
   /* Speicher reservieren */
   mem = g_malloc(MMAX);
   /* Etwas in Speicher schreiben */
   g_snprintf((gchar *)mem, MMAX, "%s\n",text);
   /* Inhalt des Speichers ausgeben */
   g_print("mem: %s",(gchar *)mem);
   /* Speicherplatz duplizieren */
   dup = g_memdup(mem, MMAX);
   /* Inhalt des kopierten Speichers ausgeben */
   g_print("dup: %s",(gchar *)dup);   
   /* Kopierten Speicherplatz freigeben */
   g_free(dup);   
   /* Ein Fehler mit Absicht - Programmabbruch */
   mem = g_malloc( -1 );
   return 0;   
}

Das Programm bei der Ausführung:

$ gcc -o glib3 glib3.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib3
mem: Text zum Kopieren
dup: Text zum Kopieren
GLib-ERROR **: gmem.c:140: failed to allocate 4294967295 bytes
aborting...
Abgebrochen

Speicherblöcke kopieren

Der einfachste Weg, um einen Speicherblock zu kopieren, ist (wie im Beispiel oben gesehen) die Verwendung der Funktion g_memdup(). Diese Funktion reserviert die Menge an Speicherplatz, die Sie als zweiten Parameter angegeben haben, und kopiert die Anzahl der Bytes von der Adresse des ersten Parameters in den neuen Block. Als Rückgabewert erhalten Sie die Anfangsadresse des neuen Blocks oder NULL.

Makros zur Speicherverwaltung

Neben malloc()-ähnlichen Funktionen der Glib bietet Ihnen diese Bibliothek auch einige Makros zur Speicherbeschaffung an. Da ja die Speicherblockgröße beim Aufruf mit z. B. g_malloc() nicht nummerisch angegeben werden sollte, sondern als Größe des Datentyps oder der Struktur mit dem sizeof()-Operator, finden sich in der Glib Makros, die Ihnen die Arbeit des Castens abnehmen (was immer noch eine häufige Fehlerquelle ist, weshalb Sie es, wenn nicht nötig, sein lassen sollten). Ein Beispiel:

Strukturzeiger = (structurtyp *)g_malloc(sizeof(structurtyp)*10);

Hiermit werden für den Strukturzeiger zehn Elemente vom Typ structurtyp mit der entsprechenden Größe reserviert. Es ist jetzt im Prinzip egal, wie diese Struktur aussieht. Diese Casterei wird Ihnen jetzt mit dem Makro g_new() abgenommen:

Strukturzeiger = g_new(strukturtyp, 10);

Auch wenn das Konstrukt dem C++-Pendant recht ähnlich sieht, es hat nichts damit zu tun! Speicherplatz müssen Sie hierbei weiterhin mit g_free() freigeben und nicht etwa mit einem C++-ähnlichen delete(). Hier ein Überblick zu den Makros und den gleichwertigen Gegenstücken:


Tabelle 15.3    Makros zur Speicherreservierung

Makro Gleichwertig zu ...
g_new(type , size); g_malloc(sizeof(type) * size);
g_new0(type, size); g_malloc0(sizeof(type) * size );
g_renew(type, ptr, size); g_realloc(ptr, sizeof(type) * size );

Weitere Funktionen zur Speicherbeschaffung

Die Glib bietet noch eine Menge mehr Funktionen und Makros zur Speicherbeschaffung und -verwaltung an. Alle hier zu diskutieren würde den Umfang des Buchs sprengen. Die gängigen kennen Sie zumindest jetzt, und sie sind in der Regel auch völlig ausreichend, um damit arbeiten zu können.

Erwähnen muss ich allerdings noch einen speziellen Mechanismus der Glib – den Speicherklumpen (GmemChunk). Dieser wird für das Belegen immer gleichartiger Speicherblöcke verwendet, was bei einer GUI-Software recht häufig vorkommt. Der Klumpen setzt sich dabei aus Atomen (Speicherblöcken) zusammen. GmemChunk ist allerdings keine Datenstruktur, sondern ein Hilfsmittel zu Verwaltung der Speicherblöcke. Sinn hat es natürlich, dadurch die Geschwindigkeit von GUI-Anwendungen zu beschleunigen. Eine weitere Speichertechnik sind die Quarks mit GQuark, die erst einmal nichts mit Physik oder der Speicherverwaltung zu tun haben, aber intern irgendwie doch, da mit dieser Technik Speicherplatz gespart werden kann.

Sofern Sie also umfangreiche und speicherintensive Anwendungen erstellen, sollten Sie sich GMemChunk und GQuark auf jeden Fall in der Dokumentation ansehen. Als Tipp kann ich Ihnen hierfür auch das Buch von M. Warkus, GNOME 2.0, empfehlen, das auf diese Themen genauer eingeht.


Rheinwerk Computing

15.3.5 Stringbearbeitung  downtop

Die Behandlung und Bearbeitung von Strings ist häufig eine der umfangreicheren Aufgaben für den C-Programmierer. Die meiste Zeit muss der Programmierer dabei häufig für den Puffer des Strings aufwenden. Die Glib hat Ihnen hierfür viele Funktionen zur Verfügung gestellt, die Ihnen (fast) die ganze Arbeit abnehmen. Die Glib bietet Ihnen eine unglaubliche Anzahl von Stringfunktionen, so dass ich Sie auch hier mit einem Tut mir Leid für den kurzen Überblick ... abspeisen muss. Aber die wichtigsten werden erwähnt – versprochen. Sofern Sie anschließend weiter nach Stringfunktionen forschen wollen, müssen Sie lediglich nach Funktionen mit dem Präfix g_str Ausschau halten. Alle Stringfunktionen der Glib beginnen damit. Dann gibt es noch Funktionen, die im Zusammenhang mit dem Datentyp GString stehen, die alle mit dem Präfix g_string beginnen. Sämtliche Funktionen, die Sie vom Standard-C her kennen, sind natürlich auch in verbesserter Form in der g_-Variante vorhanden – worauf allerdings nicht näher eingegangen wird, da diese ähnlich angewendet werden, wie Sie dies vom Standard-C her kennen.

Stringkonvertierungs- und Modifikationsfunktionen

Um alle Zeichen eines Strings in Groß- bzw. Kleinbuchstaben umzuwandeln, können Sie die folgenden Funktionen verwenden:

void g_strup(const gchar * string);/* alles in Großbuchstaben  */
void g_strdown(gchar * sring);     /* alles in Kleinbuchstaben */

Beachten Sie, dass hierbei die Strings direkt verändert werden. Sofern der String NULL ist, kehren beide Funktionen einfach zurück. Wollen Sie zwei Strings unabhängig von der Groß- bzw. Kleinschreibung vergleichen, können Sie folgende Funktionen verwenden:

gint g_strcasecmp (gchar *str1, gchar *str2);
gint g_strncasecmp(gchar *str1, gchar *str2, guint n);

Mit der Funktion g_strncasecmp() können Sie im Gegensatz zur Funktion g_strcasecmp() die ersten n-Bytes, anstatt die beiden kompletten Strings, miteinander vergleichen. Sind beide Strings gleich, gibt die Funktion 0, ansonsten die Adressen der Position zurück, die nicht mehr miteinander übereinstimmen.

Einen String umdrehen können Sie mit der Funktion:

void g_strreverse(gchar *str);

Hierbei wird der String verändert und in umgekehrter Reihenfolge ausgegeben. Bei einer Übergabe von NULL kehrt die Funktion unwiderruflich zurück.

Hierzu ein Listing, das diese Funktionen demonstrieren soll:

/* glib4.c */
#include <gtk/gtk.h>
int main(void) {
   gchar text[]  = { "Text zum Manipulieren" };
   gchar text2[] = { "Text (noch mehr)" };
   
   g_strup(text);
   g_print("g_strup()  : %s\n",text);
   g_strdown(text);
   g_print("g_strdown(): %s\n",text);  
 
   if(g_strcasecmp( text, text2 ) )
      g_print("'text' und 'text2' sind ungleich\n");
   else 
      g_print("Beide Zeichenketten sind gleich\n");
   if(g_strncasecmp(text, text2, 5) )
      g_print("'text' und 'text2' sind ungleich"
              " (erste 5 Zeichen)\n");
   else
      g_print("Die ersten 5 Zeichen der beiden "
              "Strings sind gleich\n"); 
  
   g_strreverse( text );
   g_print("g_strreverse() : %s\n",text);
   return 0;   
}

Das Programm bei der Ausführung:

$ gcc -o glib4 glib4.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib4
g_strup()  : TEXT ZUM MANIPULIEREN
g_strdown(): text zum manipulieren
'text' und 'text2' sind ungleich
Die ersten 5 Zeichen der beiden Strings sind gleich
g_strreverse() : nereilupinam muz txet

Um eine Kopie einer Zeichenkette anzulegen, sind folgende Funktionen vorhanden:

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

Beide Funktionen geben eine Kopie der Zeichenkette str zurück. Mit g_strndup() werden nur die ersten n Bytes kopiert und zurückgegeben. Ist str kürzer als n Bytes, wird der Rest mit \0 aufgefüllt.

Weitere interessante Funktionen hierzu sind:

gchar* g_strdup_printf (const gchar *format, ...);
gchar* g_strdup_vprintf(const gchar *format, va_list args);

Beide Funktionen erstellen ein Format und eine Werteliste wie printf(). Anschließend legen diese Funktionen eine neue Zeichenkette mit passender Länge an und schreiben die Ausgabe hinein. Dies ist die sicherere und komfortablere Alternative zu sprintf(). Die Funktion g_strdup_vprintf() unterscheidet sich dadurch, dass eine va_list entgegengenommen wird, wie dies zum Beispiel bei der Verwendung einer selbst geschriebenen Funktion mit variabler Argumentenanzahl der Fall ist.

Beliebig viele Strings aneinander hängen, ohne sich um den Speicherplatz zu kümmern, können Sie mit der Funktion:

gchar* g_strconcat(const gchar *str1, ...);

Damit diese Funktion auch funktioniert, muss die Liste mit NULL abgeschlossen werden. Zurückgegeben wird die Anfangsadresse aller zusammengehängter Strings.

Wollen Sie selbiges machen und dabei noch ein Zwischenraumzeichen (Feldtrenner) zwischen den einzelnen Strings einfügen, können Sie die Funktion g_strjoin() verwenden:

gchar* g_strjoin(const gchar *trenner, ...);

Ansonsten funktioniert diese Funktion genauso wie g_strconcat() – also muss die Liste auch mit NULL abgeschlossen werden.

Die snprintf()-Funktion der Glib sei in diesem Zusammenhang ebenfalls erwähnt, da diese recht häufig in der Praxis eingesetzt wird:

gint g_snprintf( gchar *zielstring,
                 gulong n,
                 const gchar *format, ... );

Funktioniert ähnlich wie snprintf() und schreibt einen durch format formatierten String mit der sich anschließenden Werteliste in den Puffer zielstring. Geschrieben werden n Bytes Zeichen mit dem abschließenden \0. Der Rückgabewert von g_snprintf() ist die Anzahl Zeichen, welche die Funktion ausgäbe, wäre der Puffer groß genug. Dies entspricht nicht dem Verhalten traditioneller sprintf()-Implementationen.

Das letzte Zeichen einer Zeichenkette entfernen können Sie mit der Funktion:

gchar* g_strchomp(gchar *zkette);

Gerne eingesetzt wird diese Funktion wohl bei der Eingabe mittels fgets(), um das Newline-Zeichen am Ende loszuwerden. Es wird aber nicht das Stringterminierungszeichen entfernt! Es gibt auch eine Version, um das erste Zeichen eines Strings zu entfernen:

gchar *g_strchug(gchar * str);

Um Zeichen, die im String vorkommen, durch andere zu ersetzen, wird diese Funktion verwendet:

gchar* g_strdelimit( gchar *str,
                     const gchar *trenner,
                     gchar neuer_trenner ); 

Ersetzt alle in trenner vorkommenden Zeichen in str durch das Zeichen neuer_trenner. Ist der trenner NULL, wird eine Standardzeichenkette verwendet, die als symbolische Konstante mit G_STR_DELIMITERS definiert ist.

Hierzu wieder ein Listing, das Ihnen die einzelnen Funktionen in der Praxis zeigt:

/* glib5.c */
#include <stdio.h>
#include <gtk/gtk.h>
#define MMAX 255
int main(void) {
   const gchar *text = "Hallo ";
   const gchar *tex2 = "Welt! ";
   gchar *copy, *more;
   gchar *str, *test;
   const gchar str1[] = { "String1" };
   const gchar str2[] = { "String2" };
   const gchar str3[] = { "String3" };   
   char buf[MMAX];
   
   copy = g_strdup(text);
   g_print("g_strdup() : %s\n",copy);
   
   more = g_strdup_printf("%s %s",text, tex2);
   g_print("g_strdup_printf(): %s\n", more);
   
   str = g_strconcat(str1,"|", str2,"|", str3, NULL);
   g_print("g_strconcat() : %s\n",str);   
   
   str = g_strjoin("|", str1, str2, str3, NULL);
   g_print("g_strjoin() : %s\n", str);
   
   test = g_malloc(MMAX);
   g_snprintf(test, MMAX, "PIN-Code: 432–557–32 (speichern)");
   
   g_strdelimit(test, "0123456789", '*');
   g_print("%s\n",test);
   g_print("G_STR_DELIMITERS : %s\n",G_STR_DELIMITERS);
   
   g_print("Text eingeben : ");
   fgets(buf, MMAX, stdin);
   /* Weg mit dem Newline-Zeichen */
   g_strchomp(buf);
   g_print("%s\n",buf);
   g_print("Hinter buf\n");   
   return 0;   
}

Das Programm bei der Ausführung:

$ gcc -o glib5 glib5.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib5
g_strdup() : Hallo
g_strdup_printf(): Hallo Welt!
g_strconcat() : String1|String2|String3
g_strjoin() : String1|String2|String3
PIN-Code: ***-***-** (speichern)
G_STR_DELIMITERS : _-|> <.
Text eingeben : Die GLib ist genial
Die GLib ist genial
Hinter buf

Hinweis   Nicht behandelt wurden außerdem die Unmengen an Funktionen für Unicode- und Zeichencodierung. Bei Bedarf sei wieder die Dokumentation empfohlen oder eben entsprechende Literatur.



Rheinwerk Computing

15.3.6 Selbstverwaltender Stringpuffer  downtop

In der Glib wurde ein neuer Datentyp mit dem Namen GString eingeführt, der zwar gleichwertig wie ein gewöhnlicher String ist, nur dass dieser sich automatisch um den Puffer kümmert. Damit gehören Buffer Overflows in Ihrem Programm der Vergangenheit an. Realisiert wird dieser Puffer mit einer einfachen Struktur namens GString:

struct GString {
  /* Zeiger auf einen \0-terminierten String */
  gchar *str; 
  /* Aktuelle Länge */
  gint len; 
};

Einen neuen Textpuffer können Sie mit den folgenden Funktionen erstellen:

GString *g_string_new(const gchar *init);
Gstring *g_string_sized_new(guint size);

Die erste Funktion erstellt einen neuen Textpuffer, wobei der neue Puffer gleich mit einer Kopie von init initialisiert wird, worauf dieser verweist. Mit der zweiten Funktion (g_string_sized_new()) reservieren Sie einen Speicherplatz für einen neuen String mit size Bytes Länge. Der Puffer wird dabei automatisch mit '\0' Zeichen initialisiert. Den Speicherplatz für GString geben Sie mit der Funktion g_string_free() wieder frei.

void g_string_free(GString *string, gint free_segment);

Ist free_segment TRUE, werden auch die Daten von str der GString-Struktur freigegeben.

Jetzt folgen einige Funktionen, mit denen Sie den Textpuffer bearbeiten können. Hierzu erst einmal eine - natürlich nicht komplette - Liste einiger Funktionen, die mit dem Datentypen GString zusammenarbeiten.

GString *g_string_assign(GString  *lval,  const gchar *rval);
GString *g_string_append(GString *string, gchar *val);
GString *g_string_append_c(GString *string, gchar c);
GString *g_string_prepend(GString *string, gchar *val);
GString *g_string_prepend_c(GString *string, gchar c);
GString *g_string_insert( 
                  GString *string, gint offset, gchar *val);
GString *g_string_insert_c(
                  GString *string, gint offset, gchar val);
GString *g_string_erase( GString *string, gint pos, gint length);

Mit der Funktion g_string_assign() kopieren Sie den String rval in den GString lval. Wie bei der Standard-C-Funktion strcpy() wird dabei der Originaltext (falls einer vorhanden ist) in lval überschrieben. Um den Speicherplatz müssen Sie sich keine Gedanken machen, das erledigt Glib für Sie. Als Rückgabewert erhalten Sie bei Erfolg einen Zeiger auf lval.

Mit den Funktionen g_string_append() und g_string_append_c() hängen Sie an den Quellstring string (erster Parameter) einen String oder (bei g_string_append_c()) ein Zeichen (zweiter Parameter) an. Als Rückgabewert erhalten Sie einen Zeiger auf die Anfangsadresse von string.

Mit g_string_prepend() oder g_string_prepend_c() fügen Sie am Anfang des Zielstrings string einen String oder ein Zeichen hinzu. Auch hier wird als Rückgabewert ein Zeiger auf die Anfangsadresse von string zurückgegeben.

Mit den Funktionen g_string_insert() und g_string_insert_c() fügen Sie einen String oder ein Zeichen in den Zielstring string ein. Wo dieser String oder das Zeichen eingefügt wird, geben Sie mit offset an. Als Rückgabewert erhalten Sie einen Zeiger auf die Anfangsadresse von string.

Mit g_string_erase() können Sie im Zielstring string von der Position pos bis zu length Zeichen löschen. Bei falscher Angabe von pos oder length wird string unverändert zurückgegeben. Die Rückgabe ist auch hierbei ein Zeiger auf den Anfang von string.

Alle hier erwähnten Funktionen liefern im Fehlerfalle NULL zurück, wenn der Zielstring NULL ist. Ist allerdings der Quellstring NULL, wird der Zielstring nicht verändert und auch so zurückgegeben.

Eine besondere Funktion sollte noch erwähnt werden:

void g_string_sprintf(GString *string, const gchar *fmt ...);

Diese Funktion ist der sprintf()-Funktion von C sehr ähnlich und hat den Vorteil, dass der Puffer automatisch erweitert wird, falls nötig. Befinden sich im GString string noch Zeichen, werden diese unwiderruflich überschrieben.

Das folgende Listing soll Ihnen diese Funktionen und den neuen Glib-Datentyp GString in der Praxis zeigen:

/* glib6.c */
#include <gtk/gtk.h>
#include <string.h>
#define MMAX 255
int main(void) {
   GString *gstr;
   guint size;
   gint val = 1;
   
   gstr = g_string_new("GString initialisiert");
   g_print("%s mit %d Bytes\n\n",gstr->str, gstr->len);
   
   g_string_assign(gstr, 
           "g_string_assign() ueberschreibt den Originalstring");
   g_print("%s\n\n", gstr->str);
   
   g_string_append(gstr, 
            "\n-> hinzugefuegt mit g_string_append()");
   g_print("%s\n\n", gstr->str);
   /* Einzelnes Zeichen hinzufügen am Ende */
   g_string_append_c(gstr, '!');
   g_print("%s\n\n", gstr->str);
   
   g_string_prepend(gstr, 
            "Vorne angefuegt mit g_string_prepend()\n");
   g_print("%s\n\n", gstr->str);
   /* Einzelnes Zeichen hinzufügen am Anfang */
   g_string_prepend_c(gstr, '!');
   g_print("%s\n\n", gstr->str);
   
   g_string_insert(gstr, 10, " xxxEINGEFUEGTxxx " );
   g_print("%s\n\n", gstr->str);   
   
   size = strlen(gstr->str);
   /* String löschen */
   g_string_erase(gstr, 0, size);
   
   g_string_sprintf(gstr, 
            "Puffer wird mit g_string_sprintf() automatisch"
            " verwaltet (%d)\n",val);
   g_print("%s\n\n", gstr->str);      
   return 0;   
}

Das Programm bei der Ausführung:

$ gcc -o glib6 glib6.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib6
GString initialisiert mit 21 Bytes
g_string_assign() überschreibt den Originalstring
g_string_assign() überschreibt den Originalstring
-> hinzugefügt mit g_string_append()
g_string_assign() überschreibt den Originalstring
-> hinzugefügt mit g_string_append()!
Vorne angefügt mit g_string_prepend()
g_string_assign() überschreibt den Originalstring
-> hinzugefügt mit g_string_append()!
!Vorne angefügt mit g_string_prepend()
g_string_assign() überschreibt den Originalstring
-> hinzugefügt mit g_string_append()!
!Vorne ang xxxEINGEFÜGTxxx efügt mit g_string_prepend()
g_string_assign() überschreibt den Originalstring
-> hinzugefügt mit g_string_append()!
Puffer wird mit g_string_sprintf() automatisch verwaltet (1)

Rheinwerk Computing

15.3.7 Timer  downtop

Im Großen und Ganzen ist ein Timer nichts anderes als eine Stoppuhr, die so genau wie die Systemuhr ist. Timer werden gerne verwendet, wenn z. B. für eine bestimmte Zeit auf ein Event gewartet werden soll – oder einfach als Verzögerung (wie Sie dies im Beispiel der X-Programmierung gesehen haben). Diese sind eigentlich recht einfach zu verwenden. Hier ein Überblick zu einigen Funktionen, die Ihnen dazu zur Verfügung stehen:


Tabelle 15.4    Timer-Funktionen der Glib

Bedeutung Funktion
Timer erzeugen GTimer *g_timer_new();
Timer starten void g_timer_start( GTimer *timer );
Zeit abfragen (Sekunden und Millisekunden), die seit dem Start des Timers vergangen ist gdouble g_timer_elapsed( GTimer *timer, gulong *msecs );
Die abgelaufene Zeit auf 0 zurücksetzen (und neustarten) void g_timer_reset( GTimer *timer);
Timer stoppen g_timer_stop( GTimer *stop );
Timer zerstören (freigeben) void g_timer_destroy(GTimer *timer);

Ein einfaches Listing, das die Timer-Funktionen demonstrieren soll.

/* glib7.c */
#include <gtk/gtk.h>
#include <stdio.h>
int main (void) {
   GTimer *timer = NULL;
   gdouble time;
   gulong us;
   gint i=300000000;
   gint j=300000000;
   timer = g_timer_new ();
   
   g_timer_start (timer);
   g_print ("Timer laeuft\n");
   
   while(i--);      
   time = g_timer_elapsed (timer, &us);
   g_print ("Schleife dekrementiert %g sek == %ld usek\n",
             time, us);   
   g_timer_reset( timer );   
   while(i++ <= j);
   time = g_timer_elapsed (timer, &us);
   g_print ("Schleife inkrementieren %g sek == %ld usek\n",
             time, us);     
   g_timer_reset( timer );   
   g_print("Timer von Hand starten <ENTER>\n");
   getchar();
   g_timer_start (timer);
   
   g_print("Timer von Hand stoppen <ENTER>\n");
   getchar();
   g_timer_stop( timer );
   
   time = g_timer_elapsed (timer, &us);
   g_print ("Vergangene Zeit zwischen Start und Stopp"
            " %g sek == %ld usek\n", time, us);
   g_timer_destroy (timer);
   return 0;
}

Das Programm bei der Ausführung:

$ gcc -o glib7 glib7.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib7
Timer läuft
Schleife dekrementiert 0.769323 sek == 769323 usek
Schleife inkrementieren 0.775894 sek == 775894 usek
Timer von Hand starten <ENTER>
ENTER
Timer von Hand stoppen <ENTER>
ENTER
Vergangene Zeit zwischen Start und Stopp 2.26975 sek == 269751 usek

Rheinwerk Computing

15.3.8 Dynamische Arrays  downtop

Die Glib bietet Ihnen mit GArray einen Datentyp für ein C-typisches Arrayfeld, nur mit dem Unterschied, dass dieses nicht von fester Größe sein muss. Mit dem flexibleren Array-Datentyp können Sie beliebig neue Elemente einfügen, und das sowohl am Anfang und am Ende wie auch mittendrin – natürlich ist auch das Löschen inbegriffen.

Dabei bietet Ihnen die Glib gleich drei Formen von dynamischen Arrays an:

gp  gewöhnliche Arrays
gp  Zeiger-Arrays
gp  Byte-Arrays

Durchgenommen werden hier nur die gewöhnlichen Arrays, welche praktisch mit allen Elementen beliebigen Typs – inklusive Strukturen – verwendet werden können. Dazu eine kurze Zusammenstellung von einigen Funktionen für GArray, welche gewöhnlich alle mit dem Präfix g_array_ beginnen.

GArray *g_array_new(
        gboolean zero_terminated, gboolean clear, guint size );
void g_array_free( GArray *array, gboolean free_segment );
GArray *g_array_append_vals( 
        GArray *array, gconstpointer *data, guint len );
GArray *g_array_prepend_vals( 
        GArray *array, gconstpointer *data, guint len );
GArray *g_array_insert_vals( 
    GArray *array, guint index, gconstpointer *data, guint len );
GArray *g_array_remove_index( GArray *array, guint index );
g_array_index( array, datentyp, index );

Zuerst muss mit der Funktion g_array_new() der Array alloziiert werden. Mit den beiden gboolean-Parametern geben Sie an, ob ein Nullwert als Terminierungszeichen angehängt wird und ob neue Elemente auf null gesetzt werden. Der letzte Parameter muss die Größe des Elements in Bytes (Datentyp) enthalten, wovon Sie einen dynamischen Array erzeugen wollen. Zurückgegeben wird bei Erfolg ein Zeiger auf den neuen GArray.

Die Speicherverwaltung wird von nun an mit den entsprechenden Funktionen von der Glib übernommen. Allerdings freigeben müssen Sie nicht verwendeten Speicherplatz mit der Funktion g_array_free() wieder selbst.

Wollen Sie ein neues Element am Anfang bzw. Ende des GArray hinzufügen, können Sie hierfür die Funktionen g_array_prepend_vals() bzw. g_array_append_vals() verwenden. Beide Funktionen erwarten als ersten Parameter einen Zeiger auf den Array, wo entsprechende Daten, die mit dem zweiten Parameter angegeben werden, hinzugefügt werden. Mit dem dritten Parameter geben Sie die Anzahl der Elemente an, die Sie hinzufügen wollen.

Die Funktion g_array_insert_vals() hat im Gegensatz zu den Funktionen g_array_prepend_vals() und g_array_append_vals() noch einen zusätzlichen Parameter (zweiter Parameter), womit Sie die Indexposition angeben und das neue Element einfügen können.

Ein Element aus der Liste des GArray (erster Parameter) können Sie unter Angabe der Indexnummer des zweiten Parameters mit der Funktion g_array_remove_index() entfernen.

Um anschließend wieder auf die einzelnen Elemente des GArray zurückzugreifen, benötigen Sie das Makro g_array_index(). Als Argument übergeben Sie dieser Funktion das Array, den Datentyp der Elemente und die Indexnummer. Zurückgegeben wird dann der Wert des entsprechenden Elementes.

Das folgende Listing soll Ihnen die Verwendung vom dynamischen GArray mit der Glib demonstrieren. Sie können gerne versuchen, das Beispiel in Standard-C in dieser kurzen Form und mit solch einfachem Code zu schreiben.

/* glib8.c */
#include <gtk/gtk.h>
#include <stdio.h>
#define MMAX 255
static struct daten {
   gchar ort[MMAX];
   guint plz;
};
static struct daten help;
static guint count = 0;
static void read_data (void) {
   g_print ("Ort eingeben          : ");
   fgets (help.ort, MMAX, stdin);
   g_strchomp (help.ort);
   g_print ("Postleitzahl eingeben : ");
   scanf ("%d", &help.plz);
}
static void insert_beginn (GArray * arr) {
   read_data ();
   g_array_prepend_vals (arr, &help, 1);
   count++;
}
static void insert_end (GArray * arr) {
   read_data ();
   g_array_append_vals (arr, &help, 1);
   count++;
}
static void insert_middle (GArray * arr) {
   guint pos;
   read_data();
   g_print ("Position angeben      : ");
   scanf ("%d", &pos);
   if (pos < count) {
      g_array_insert_vals (arr, pos, &help, 1);
      count++;
   }
   else
      g_print ("Konnte Array nicht einfuegen (%d < %d)\n",
                pos, count);
}
static void remove_array (GArray * arr) {
   guint pos;
   g_print ("Position zum Loeschen angeben    : ");
   scanf ("%d", &pos);
   if (pos < count) {
      g_array_remove_index (arr, pos);
      count--;
   }
   else
      g_print ("Konnte Array nicht entfernen (%d < %d)\n",
                pos, count);
}
static void show_arrays (GArray * arr) {
   struct daten test;
   guint pos = 0;
   while (pos < count) {
      test = g_array_index (arr, struct daten, pos);
      g_print ("Ort     :  %s\n", test.ort);
      g_print ("PLZ     :  %d\n", test.plz);
      pos++;
   }
}
int main (int argc, char **argv) {
   GArray *darray;
   guint auswahl;
   darray = g_array_new (FALSE, FALSE, sizeof (struct daten));
   do {
      printf ("Demonstration: dynamische Arrays (Glib)\n\n");
      printf ("1 - Neue Daten am Anfang einfuegen\n");
      printf ("2 - Neue Daten am Ende einfuegen\n");
      printf ("3 - Neue Daten an gewuenschte Pos. einfuegen\n");
      printf ("4 - Daten entfernen\n");
      printf ("5 - Daten ausgeben\n");
      printf ("0 - Programmende\n");
      printf ("\nIhre Auswahl : ");
      scanf ("%d", &auswahl);
      getchar ();
      switch (auswahl) {
      case 1:
         insert_beginn (darray);
         break;
      case 2:
         insert_end (darray);
         break;
      case 3:
         insert_middle (darray);
         break;
      case 4:
         remove_array (darray);
         break;
      case 5:
         show_arrays (darray);
         break;
      }
   }
   while (auswahl != 0);
   g_array_free (darray, TRUE);
   return 0;
}

Das Programm bei der Ausführung:

$ gcc -o glib8 glib8.c `pkg-config --libs --cflags gtk+-2.0`
$ ./glib8
Demonstration von dynamischen Arrays mit GLib
1 - Neue Daten am Anfang einfuegen
2 - Neue Daten am Ende einfuegen
3 - Neue Daten an gewuenschte Pos. einfügen
4 - Daten entfernen
5 - Daten ausgeben
0 - Programmende
Ihre Auswahl :
...

Hinweis   Wenn Sie das GArray auch noch sortieren wollen, können Sie sich die Funktionen g_array_sort() und g_array_sort_with_data() ansehen.



Rheinwerk Computing

15.3.9 Listen, Hashtabellen und binäre Bäume  downtop

Weiterhin bietet die Glib eine Unmenge an Funktionen für dynamische Datenstrukturen wie die einfachen und doppelt verketteten Listen. Für die einfach verketteten Listen werden der Datentyp GSList und die Funktionen mit dem Präfix g_slist_ verwendet und für die doppelt verketteten Listen der Typ GList und die Funktionen mit dem Präfix g_list_. Der Vorteil dieser Funktionen ist natürlich, dass man eben schnell auf abgepackte Routinen zugreifen kann und nicht eigene und somit auch wieder fehleranfällige Versionen der Listen schreiben muss.

Ebenfalls eine klassische, aber auch lästig zu schreibende Datenstruktur stellt die Glib Ihnen mit den Hashtabellen zur Verfügung. Hashtabellen sind Tabellen, die Schlüsseln einen sog. Hash zuordnen. Mit diesem Hash werden neue Werte eingefügt und vor allem auch schneller wieder gefunden. Der Glib-Datentyp für Hashtabellen ist GHashTable, und die Routinen haben das Präfix g_hash_. Bei dem GNOME-Desktop und dessen Software werden Hashtabellen zum Beispiel intensiv eingesetzt.

Der eingebaute Typ Gtree ist eine Implementierung binärer Bäume in der Glib mit allen möglichen Routinen dazu. Besser noch, bei den Bäumen der Glib handelt es sich zusätzlich noch um ausbalancierte Bäume. Wer schon einmal versucht hat, eine eigene Routine für ausbalancierte Bäume zu schreiben, weiß, wie schwierig und vor allem fehleranfällig das ist. Des Weiteren sind die einzelnen Elemente im binären Baum eben nicht nur einfache Elemente, die aus einem Datensatz bestehen, sondern außerdem auch einen Schlüssel besitzen, anhand dessen die Datensätze sortiert und gefunden werden – sprich, eine Hashtabelle von binären Bäumen, wenn Sie so wollen. Die Routinen, die sich auf den Typ Gtree beziehen, beginnen alle mit dem Präfix g_tree_.

Auf die beiden klassischen Datenstrukturen soll hierbei nicht mehr näher eingegangen werden, da sich die Einführung in die Glib ohnehin schon ziemlich umfangreich gestaltet und mehr Platz für das eigentliche Thema der GUI-Programmierung reserviert werden sollte. Für den weiteren Verlauf des Buchs benötigen Sie dieses Wissen ohnehin nicht.


Hinweis   Sollten Sie mit Begriffen wie Listen, Hashtabellen oder binären Bäumen nichts anzufangen wissen, dann muss ich Sie auf ein Einsteigerbuch zu C verweisen. Auf der Buch-CD finden Sie diesbezüglich mit dem Buch »C von A bis Z« einen einfachen Einstieg in dieses mächtige Thema.



Rheinwerk Computing

15.3.10 Ausblick Glib  toptop

Hier angekommen, lässt sich nur sagen, dass die Glib noch eine Menge mehr hilfreicher Routinen und Typen anbietet. Ich würde teilweise sogar so weit gehen und Ihnen empfehlen, die Glib zur Erstellung von Konsolenprogrammen zu verwenden. Zu den Vorteilen gehören Dinge wie die Sicherheit und ein Einsparen an Overhead bei der Programmierung. Natürlich sollte es nicht unerwähnt bleiben, dass Sie die Glib nicht verwenden müssen, um GTK+-Programme zu schreiben – Sie können natürlich weiterhin für diese Aufgaben die Standard-C-Funktionen verwenden oder im Falle von Algorithmen und Datenstrukturen selbst zum x-ten Mal etwas neu schreiben. Fakt ist, die Glib ist eine ausgereifte und sehr hilfreiche Bibliothek, die einem das Leben enorm erleichtern kann und vor allem bei dem Thema Portieren auf anderen Systemen erste Wahl sein sollte.

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern