13.3. Strukturen

Wenn eine Serie zusammengehöriger Daten bearbeitet werden soll, erweisen sich Arrays und Pointer als geeignet. Beide unterliegen aber der Einschränkung, daß alle Elemente vom gleichen Datentyp sein müssen. Bei vielen Problemstellungen ergibt sich aber die Notwendigkeit, daß Daten verschiedenen Typs innerhalb eines zusammengesetzten Datenobjekts Platz finden müssen.

Ein typisches Beispiel dafür sind die Datensätze einer Adreßverwaltung. Jeder Eintrag muß zumindest den Namen und Vornamen der betreffenden Person (i.a. als Arrays vom Typ char) und ihre Telefonnummer und Postleitzahl (als ganzzahlige Werte) aufnehmen können. Dabei ist es wünschenswert, daß Sie auf einzelne Daten genauso zugreifen können wie auf den Datensatz als ganzen, beispielweise um ihn in eine Datei zu schreiben.

C bietet Ihnen für diesen Zweck die Struktur, mit der Sie ein Konglomerat unterschiedlicher Datentypen zu einem Datenobjekt zusammenfassen und dabei auf jedes einzelne Element zugreifen können.

Deklaration von Strukturen

Da Arrays nur über Elemente gleichen Datentyps verfügen, unterscheiden sich alle Arrays eines bestimmten Datentyps nur in der Anzahl ihrer Elemente. Diese geben Sie aber gleich bei der Defintion bekannt, so daß der Compiler ab diesem Zeitpunkt alle Informationen über den Aufbau eines bestimmten Arrays besitzt. Anders verhält es sich bei Strukturen: Da jede in ihrem Aufbau völlig einzigartig sein kann, müssen Sie den Compiler vor dem Gebrauch einer Struktur über deren Zusammensetzung aufklären. Dies geschieht in Form einer Deklaration, die quasi eine Schablone der betreffenden Struktur zur Verfügung stellt. Erst dann können Sie Variablen vom ihrem Typ definieren.

Die Deklaration einer Struktur muß unter Verwendung des reservierten Wortes struct erfolgen und hat die allgemeine Form:


struct [Name]{
	Datentyp VariablenName;
	Datentyp VariablenName;
	 Datentyp VariablenName;
	.
	 .
}[Variablenliste];

Eine Struktur darf Elemente beliebigen Datentyps enthalten, also auch mit typedef definierte, Aufzählungstypen oder sogar Strukturen. Wie schon bei den Aufzählungstypen kann auch bei der Deklaration von Strukturen entweder der Name oder die Variablenliste fehlen. Verzichten Sie auf die Angabe des Strukturnamens, dann nehmen Sie eine sogenannte "anonyme Deklaration" vor. In diesem Fall stehen nur die Variablen zur Verfügung, die Sie bei der Deklaration durch Angabe in der Variablenliste definieren. Nachträgliche Definitionen von Variablen dieses Typs sind nicht möglich, da Sie sich ja mangels Namen nicht mehr auf diese Struktur beziehen können. Beispiel:


struct{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	} Kunde, Lieferant;

Nach dieser Deklaration stehen Ihnen erwartungsgemäß zwei Variablen zur Verfügung, die beide vom Typ der angegebenen Struktur sind. Diese Art der Deklaration von Strukturen ist eher ungewöhnlich und recht selten anzutreffen. Gängiger ist schon die Variante, bei der keine Variablen definiert werden und dafür der Strukturname vorhanden ist. Der anfängliche Mangel an Variablen ist nicht weiter schlimm, da nachträglich beliebig viele davon definiert werden können. Das obige Beispiel würde in dieser Variante so aussehen:


/* Durch diese Deklaration wird noch kein
 * Speicherplatz reserviert. Sie dient nur
 * als Schablone für den nachfolgenden Ge-
 * brauch dieses Datentyps
 */
struct adresse{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	};

/* anschließende Variablen-Definitionen vom
 * Typ dieser Struktur könnten so ausehen:
 */
struct adresse Kunde, Lieferant;

Erst bei der Defintion einer Variablen vom Typ struct adresse reserviert der Compiler ausreichend Platz, um darin alle Elemente der Struktur unterzubringen. In unserem Fall beläuft sich der reservierte Speicherplatz zumindest auf 2 * 30 * sizeof(char) + 2 * sizeof(unsigned long) Byte. Tatsächlich ist der Speicherbedarf aber meist größer als die aufaddierten Größen aller Elemente, da der Compiler abhängig vom System den Anfang der einzelnen Elemente an günstigen Speicheradressen ausrichtet (z.B. an geraden Adressen). Wenn Sie daher mit einer Funktion zur dynamischen Speicherverwaltung Platz für eine Struktur anfordern, dann sollten Sie unbedingt den sizeof-Operator verwenden, um ihren tatsächlichen Platzbedarf zu ermitteln! Die Größe der Struktur adresse ließe sich also duch sizeof( struct adresse) feststellen.

Es ist angenehm und daher auch weit verbreitet, den Typ von Strukturen durch die Verwendung von typedef unter einem eigenen Namen einzuführen. Auf diese Weise entfällt die Notwendigkeit, bei der Definition von Variablen oder beim Einsatz von sizeof das Schlüsselwort struct anzugeben. Um die Struktur adresse als Datentyp Address verfügbar zu machen, müßten Sie die Anweisung


typedef struct adresse Address;

einfügen. Für die Namensvergabe mittels typedef besteht bei Strukturen eine Kurzform, die schon bei der Deklaration den Einsatz von typdef zuläßt:


typedef struct{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	}ADDRESS;

Diese Deklaration hält sich erst gar nicht mit der Vergabe eines Namens für die Struktur auf, sondern macht sie dank typedef gleich als Datentyp Address verfügbar. Variablen vom Typ ADDRESS stehen nach dieser Deklaration natürlich noch nicht zur Verfügung, können aber nachträglich recht einfach in der Form


ADDRESS Kunde, Lieferant;

definiert werden.

Geltungsbereich von Strukturen

Im Prinzip gelten für den Geltungsbereich und die "Lebensdauer" von Strukturen die gleichen Regeln wie für einfache Variablen. Recht einfach stellt sich die Sache dar, wenn Variablen gleich im Zuge der Strukturdeklaration definiert werden. Abhängig vom Ort der Deklaration sind die Variablen dann global, innerhalb einer Funktion oder innerhalb eines Anweisungsblocks verfügbar. Wenn allerdings — wie meist der Fall - die Deklaration einer Struktur und die Definition der Variablen getrennt erfolgen, dann muß zwischen der Verfügbarkeit der Struktur als Datentyp und dem Geltungsbereich einzelner Variablen unterschieden werden. Wenn Sie eine Struktur noch vor main(), also global deklarieren, dann bedeutet das, daß die "Schablone" dieser Struktur in allen Funktionen bekannt ist und daher überall Variablen dieses Typs definiert werden können:


#include <stdio.h>

/* Deklaration der Struktur adresse
 * erfolgt global, ...
 */
struct adresse{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	};

int main(void){
/* ... daher können auch innerhalb von main()
 * Variablen dieses Typs definiert werden. Die
 * Variablen selbst sind aber nur in main() gültig.
 * Als lokale Variablen gehören sie zudem der Speicher-
 * klasse auto an und werden deshalb nach Verlassen
 * von main() zerstört. Auch bei Strukturen kann dies 
 * durch das Schlüsselwort static verhindert werden,
 * also z.B.
 * static struct adresse Kunde;
 */
struct adresse Kunde, Lieferant;
.
.

Anders sieht es aus, sobald eine Struktur innerhalb einer Funktion deklariert wird. Dann ist sie auch als Datentyp nur lokal verfügbar und Variablen können nur dort definiert werden:


#include <stdio.h>

int main(void){

/* Deklaration des Datentyps address
 * erfolgt lokal in main(), ...
 */
typedef struct{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	}ADDRESS;
.
.
}
void MyFunc(int iCount){

/* ... daher können innerhalb von MyFunc()
 * keine Variablen des Typs Address
 * definiert werden.
 * Folgende Definition führt daher zu einer
 * Fehlermeldung des Compilers
 */
ADDRESS Kunde, Lieferant;
.
.

Initialisierung von Strukturen

Einzelne Elemente können im Rahmen einer Strukturdeklaration nicht initialisiert werden, da ja bei diesem Vorgang kein Speicherplatz reserviert wird und daher unklar ist, an welcher Stelle denn die Anfangswerte gespeichert werden sollen. Darüber hinaus können im Zuge der Deklaration mehrere Variablen definiert werden, so daß nicht festzustellen ist, welcher Variablen die Werte zuzuordnen sind. Dieses Beispiel zeigt eine nicht erlaubte Initialisierung:


/* unzulässige Initialisierung */
typedef struct{
	char szName[30] = {"Meier"};

	char szVorname[30] = {"Johann"};
	unsigned long ulTel;
	 unsigned long ulPlz = 75389;
	}ADDRESS;

Das korrekte Verfahren weist den einzelnen Elementen erst bei der Definition der Variablen Werte zu; dies geschieht analog zur Initialisierung von Arrays:


/* Da der Wert für die Postleitzahl fehlt,
 * erhält diese den Anfangswert 0
 */
ADDRESS Kunde = {"Meier", "Johann", 2731005)

Struktur-Zuweisungen

Die Zuweisung von Werten mit Hilfe des Zuweisungsoperators beschränkt sich auf einfache Variablen, weil Arrays in dieser Form nicht Serien von Daten entgegennehmen können. Stattdessen müssen bei ihnen die Werte für die einzelnen Elemente separat zugewiesen werden, entweder durch eigenen Code oder durch Bibliotheksfunktionen (hauptsächlich bei Strings).

Überraschenderweise erlaubt C beim noch komplexeren Datentyp, der Struktur, die direkte Zuweisung aller darin enthaltener Werte an eine Struktur desselben Typs. Dies funktioniert selbst dann, wenn eine Struktur Arrays und Pointer enthält! Die folgende Struktur ist unter dem Namen Address verfügbar:


typedef struct{
	char szName[30];
	char szVorname[30];
	 unsigned long ulTel;
	unsigned long ulPlz;
	}ADDRESS;

Wenn nun zwei Variablen vom Typ Address definiert werden,


ADDRESS Kunde;
ADDRESS Lieferant = {"Fischer", "Peter", 2723499, 83342};

dann kann der gesamte Inhalt von Lieferant durch


Kunde = Lieferant;

an Kunde zugewiesen werden. Der Aufruf


/* Punkt-Operator s. Abschnitt 13.3.5 */
printf("%s %s\n", Kunde.szVorname, Kunde.szName);

hat erwartungsgemäß das Ergebnis "Peter Fischer".

Zugriff auf einzelne Elemente

Damit die einzelnen Elemente einer Struktur z.B. innerhalb von Ausdrücken verwendet oder als Argumente an Funktionen übergeben werden können, müssen sie gezielt ansprechbar sein. Der Name eines Elements besteht aus dem Namen der Struktur und seinem eigenen Variablennamen; beide Komponenten werden durch den Punktoperator getrennt.

Beispielsweise ist die Variable Lieferant vom Typ ADDRESS definiert. Dann kann der Wert für die Telefonnummer folgendermaßen zugewiesen werden:


Lieferant.ulTel = 297225;

Es liegt auf der Hand, daß nur solche Operationen mit einem Element zulässig sind, die auch mit einer alleinstehenden Variablen dieses Typs möglich wären. So kann in einem Character-Array innerhalb einer Struktur genausowenig mittels Zuweisung eine Zeichenkette gespeichert werden, wie dies bei einem "gewöhnlichen" Array möglich wäre. In beiden Fällen benötigt man dafür eine str..()- oder mem...()-Funktion. Im Falle unserer Variablen Lieferant müssen wir den Namen "Meier" z.B. folgendermaßen im Array szName speichern:


strcpy(Lieferant.szName, "Meier")

Da ein "gewöhnlicher", nicht initialisierter Pointer einen wilden Pointer darstellt, können Sie sicher sein, daß ein solcher auch dann vorliegt, wenn er Element einer Struktur ist. Im Sinne funktionsfähiger Programme sollten Sie daher davon absehen, einem wilden Pointer Daten zuzuweisen. Wir gehen davon aus, daß die bisher verwendete Struktur adresse so definiert ist:


struct adresse{
	char *pszName;
	char *pszVorname;
	unsigned long ulTel;
	unsigned long ulPlz;
	};

Dann versteht es sich in diesem Fall von selbst, daß Daten für Name und Vorname nur dann gespeichert werden können, wenn zuvor der nötige Speicher angefordert wurde, also


.
.
/* Variable Lieferant definieren */
struct adresse Lieferant;

.
Lieferant.pszName = (char *) malloc (30 * sizeof(char));
if(Lieferant.pszName == NULL){
	/* Erfolg von malloc() überprüfen */
	.
	}
strcpy(Lieferant.pszName, "Müller");
.
.

Da es sich bei Arrays um zusammengesetzte Datentypen handelt, bieten sie einen Mechanismus an, mit dessen Hilfe auf ihre Elemente zugegriffen werden kann. Dabei handelt es sich bekanntlich um den Index, der im Anschluß an den Array-Namen angegeben werden muß. Befindet sich ein Array innerhalb einer Struktur, dann erfordert das Ansprechen eines Array-Elements zwei Angaben: eine darüber, daß das Array Element einer Struktur ist, und eine zweite, die sich in Form eines Indexes auf ein Element des Arrays beziehen muß. Um den Anfangsbuchstaben des Vornamens in der Struktur adresse auf 'X' zu setzen, muß folgende Zuweisung vorgenommen werden:


Kunde.szVorname[0] = 'X';

Entsprechendes gilt natürlich auch für Pointer, wenn sie mit Hilfe eines Indexes dereferenziert werden sollen:


Kunde.pszVorname[0] = 'X';

Arrays aus Strukturen

Prinzipiell sind in C Arrays mit allen gültigen Datentypen zulässig, entsprechend auch mit Strukturen. Bedingung ist natürlich, daß eine gültige Deklaration der Struktur vorliegt, bevor ein Array dieses Datentyps definiert werden kann. Angenommen, eine Adreßverwaltung soll bis zu 100 Einträge im Speicher aufnehmen können, dann empfiehlt sich — basierend auf unserer Struktur adresse — folgendes Array:


struct adresse{
	char szName[30];
	char szVorname[30];
	unsigned long ulTel;
	unsigned long ulPlz;
	};

/* Array aus 100 Strukturen vom
 * Typ adresse
 */
struct adresse kunden[100];

Wenn ein bestimmes Element dieses Arrays angesprochen werden soll, so kann dies in der gewohnten Manier (kunden[0] bis kunden[99]) erfolgen. Wollen Sie aber darüber hinaus ein Element einer Struktur benutzen, dann muß der Strukturname einen Index enthalten. Während aber der erste Buchstabe der Zeichenkette szName in der Struktur Lieferant mit Lieferant.szName[0] bezeichnet wird, kommen Sie an die Telefonnummer des ersten Kunden mit Kunden[0].ulTel. Die Namenskomponente vor dem Punktoperator steht für die Struktur: Sind Strukturen Elemente eines Arrays, dann gehört der Index zum Namen und muß deshalb noch im vorderen Teil für den Variablennamen erscheinen. Umgekehrt verhält es sich bei Arrays in Strukturen: Dort ist der Index Teil der Variablen, die Element der Struktur ist:


/* nur eine Struktur initialisieren */
struct adresse kunden[100]={
	{"Enn", "Georg", 236324, 43788}
	};

/* Namen von 1. in 2. Struktur kopieren */
strcpy(kunden[1].szName, kunden[0].szName);

/* 1. Zeichen des Namens in 2. Struktur
 * ausgeben
 */
putchar(kunden[1].szName[0]);

Zeiger auf Strukturen

Pointer auf Strukturen finden besondes häufig als Argumente von Funktionsaufrufen Verwendung. Wenn Sie einfach Strukturen an eine Funktion übergeben, dann erfolgt ja bekanntlich ein "call by value", bei dem die aufgerufene Funktion an den Argumentwerten des Aufrufers nichts ändern kann, weil sie bekanntlich eine Kopie enthält. Ganz im Gegensatz dazu bewirkt ein Pointer auf eine Struktur einen "call by reference".

Die Definition eines Pointers auf eine Struktur erfolgt nach dem üblichen Muster. Im folgenden ist wieder die Struktur adresse deklariert, dann erzeugt


struct adresse *pAdresse;

einen Pointer auf eine Struktur vom Typ adresse. Die Speicheradresse einer Struktur kann wie gewohnt mit Hilfe des Adreßoperators zugewiesen werden, also z.B.


pAdresse = &Lieferant;

Bis hierher unterscheiden sich Pointer auf Strukturen nicht von solchen auf andere Datentypen. Besonderheiten gilt es beim Zugriff auf die Elemente einer Struktur via Pointer zu berücksichtigen. Soll mit dem oben definierten Pointer pAdresse ein Wert für die Telefonnummer zugewiesen werden, dann kann dies in folgender Form passieren:


/* mit dem Inhaltsoperator muß zuerst
 * der Pointer dereferenziert werden,
 * anschließend erfolgt der Zugriff auf
 * ulTel unter Verwendung des Punkt-
 * operators
 */
(*pAdresse).ulTel = 7460688;

Diese Methode ist korrekt, wenn auch unüblich. Da mit Hilfe von Pointern sehr häufig auf Elemente von Strukturen zugegriffen wird, existiert für diesen Zweck ein eigener Operator, der Pfeiloperator (er setzt sich aus einem '-' und einem '>' zusammen). Anstelle der vorhin gewählten Formulierung würde ein typischer C-Programmierer folgende Variante wählen:


pAdresse->ulTel = 7460688;

Bei der Verwendung des Pfeiloperators entfällt die Anwendung des Inhaltsoperators.

Das nächste Beispielprogramm macht großzügigen Gebrauch von Pointern auf Strukturen. Es implementiert eine Wortzähl-Funktion, die alle Wörter des Eingabestroms alphabetisch sortiert ausgibt und dabei über die Anzahl aller Vorkommnisse informiert. Die Verwendung binärer Bäume und rekursiver Datentypen geht eigentlich schon über die Möglichkeiten des Basiswissens hinaus und wird hier nicht mehr erläutert. Deshalb versteht sich dieses Beispiel mehr als Ausblick auf die Möglichkeiten fortgeschrittener C-Programmierung. Nichtsdestotrotz können alle sprachlichen Konstruktionen nach der bisherigen Lektüre verstanden werden. Dem interessierten Leser eröffnet sich daher die Möglichkeit, die Arbeitsweise des Programms bei eingehender Beschäftigung nachzuvollziehen.


/* wcount.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXWORD 30
#define LETTER 1
#define UMLAUT(cInput) ((cInput == 'ä') || \
	 (cInput == 'ö') || (cInput == 'ü') ||	\
	 (cInput == 'Ä') || (cInput == 'Ö') ||	\
	 (cInput == 'Ü') || (cInput == 'ß'))

/* Deklaration der Struktur TreeNode.
 * Sie bildet die Knoten des binären
 * Baumes.
 * Die Zeiger left und right zeigen
 * auf Strukturen vom Typ TreeNode.
 */
struct TreeNode{
		char *pszWord; 	/* enthält Wort */
		int iCount;		 /* Häufigkeit */
		struct TreeNode *left;
		struct TreeNode *right;
	 	};

/* Funktionsprototypen */
struct TreeNode *tree(struct TreeNode *pNode,
	 char *pszWord);
char getword(int iLim, FILE *handle,
	char *pszWord);
char *str_dup(char *pszWord);
void treeprint(struct TreeNode *pNode);
void freeNode(struct TreeNode *pNode);

/****************************** *************
 *
 *	Funktion main()
 *
 *******************************************/% 
int main(int argc, char **argv){

/* pRoot ist Wurzel des Baumes */
struct TreeNode *pRoot = NULL;
char pszWord[MAXWORD];
char cLocWord;
FILE *handle;

/* Bei Aufruf ohne Argumente von stdin lesen */
 if(argc == 1)
	 handle = stdin;

/* Bei Angabe eines Dateinamens aus dieser
 * Datei lesen */
 else if(argc == 2){
	handle = fopen(argv[1], "r");
	if( handle == NULL){
		 fprintf(stderr,
			"Fehler beim Öffnen von %s",
			argv[1]);
	 	exit(EXIT_FAILURE);
		}
	}
 /* Bei jeder anderen Anzahl an Argumenten liegt
  * ein falscher Aufruf vor */
 else{
	fputs("Aufruf: wcount <Dateiname>", stderr);
	 exit(EXIT_FAILURE);
	}

/* Bis zum Auftreten von EOF mit Hilfe von
 * getword() Wörter einlesen
 */
 while (( cLocWord =
	getword(MAXWORD, handle, pszWord )) != EOF){
		
	if ( cLocWord == LETTER )
	/* Wort mit der Funktion tree()
	 * in Baum einsortieren
	 */
		 pRoot = tree(pRoot,pszWord);
   }

 fclose(handle);

/* Wortliste ausgeben */	
 treeprint(pRoot);

/* Speicher aller Knoten freigegeben */
if(pRoot!=NULL)
      freeNode(pRoot);

return 0;
}

/******************************** ***********
 *
 * Funktion tree()
 * erstellt einen binären Baum und ordnet
 * neue Elemente ein, indem sie sich selbst
 * (also rekursiv) aufruft
 *
 *******************************************/% 
struct TreeNode *tree(
	struct TreeNode *pNode,
	char *pszWord){

int iCond;

 /* Falls ein neuer Knoten eingerichtet
  * werden muß, Speicher mit malloc()
  * anfordern,...
  */
 if( pNode== NULL ){
	pNode=(struct TreeNode *) malloc(
		sizeof(struct TreeNode));
	 
	if(pNode == NULL){
		fputs("Kein Speicher mehr frei!",
			stderr);
	 exit(EXIT_FAILURE);
	}
	/*...pszWord duplizieren,... */
	pNode->pszWord = str_dup(pszWord);
	/* ...den Zähler mit 1 initialisieren...*/
	pNode->iCount = 1;
	/* und die Zeiger auf linken und rechten
	 * Nachfolger mit NULL initialisieren */
	pNode->left = pNode->right = NULL;
}
/* Wenn das aktuelle Wort aus der Datei mit
 * dem Wort übereinstimmt, das in diesem
 * Knoten gespeichert ist, dann Zähler
 * erhöhen
 */
else if(( iCond =
	strcmp(pszWord, pNode->pszWord)) == 0)
	
	 pNode->iCount++;

/* Wenn das aktuelle Wort kleiner ist als
 * pNode->pszWord, dann im Baum nach links
 * verzweigen, indem tree() rekursiv aufge-
 * rufen wird
 */
else if(iCond < 0)
	 pNode->left = tree( pNode->left, pszWord);
/*...ansonsten im Baum nach rechts verzweigen */
else
	pNode->right = tree(pNode->right,pszWord);

 return(pNode);
}

/********************* **********************
 *
 * Funktion treeprint()
 * gibt Wortliste alphabetisch aus, indem
 * sie den Baum rekursiv durchläuft
 *
 *******************************************/ 
void treeprint(struct TreeNode *pNode){

 if(pNode != NULL){
	 treeprint( pNode->left);
	 printf("%4d %s\n", pNode->iCount, pNode->pszWord);
	 treeprint( pNode->right);
	}
 }

/************************************* ******
 *
 * Funktion getword()
 * extrahiert Wörter aus dem Eingabestrom
 *
 *******************************************/% 
char getword( int iLim, FILE *handle, char *pszWord ){

char cInput;

 cInput= *(pszWord++) = (char) fgetc(handle);

 /* wenn eingelesenes Zeichen kein Buchstabe */
 if( isalpha(cInput) == 0 && !UMLAUT(cInput) ){
	 pszWord = '\0';
	 return (cInput);
	 }

 /* vom aktuellen Zeichen, das ein Buchstabe sein
  * muß, bis auf eine Länge von MAXWORD Ende des
  * Wortes suchen. Dies liegt vor, wenn ein Zeichen
  * auftritt, das kein Buchstabe (und auch kein
  * Umlaut) ist
  */
 while (--iLim > 0 ){
	cInput= *(pszWord++) = (char) fgetc(handle);
	if( isalpha(cInput) == 0 && !UMLAUT(cInput) ){
		break;
		}
	 }

/* Wort mit '\0' abschließen */
 *(pszWord-1) = '\0';

 return(LETTER);
}

/*******************************************
 *
 * Funktion str_dup()
 * dupliziert den als pszWord übergegebenen
 * String und liefert einen Pointer auf den
 * Beginn der neuen Zeichenkette
 *
 *******************************************/ 
char *str_dup(char *pszWord){

char *pszDup;

 pszDup = (char *) malloc(
	 strlen(pszWord) + sizeof('\0'));
 
 if( pszDup == NULL){
	fputs("Kein Speicher mehr frei!", stderr);
	exit(EXIT_FAILURE);
	}

 strcpy( pszDup, pszWord );

return(pszDup);
 }

/*******************************************
 *
 * Funktion freeNode()
 * Speicher aller Knoten des Baumes freigeben.
 * Dies geschieht ebenfalls rekursiv
 *
 *******************************************/ 
void freeNode(struct TreeNode *pNode){

 if( pNode->left != NULL){
	 freeNode( pNode->left);
	 pNode->left = NULL;
	}
 if( pNode->right != NULL){
	freeNode( pNode->right);
	pNode->right = NULL;
	}

 if( pNode->left == NULL && pNode->right == NULL){
	free(pNode->pszWord);
	free(pNode);
	pNode = NULL;
	}
 }