[Inhalt][0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17]
Der verflixte 7. Teil :-)

Wie schon am Ende des letzten Teil des C-Kurses angesprochen wurde, wollen wir uns unsere eigene kleine Datenbank schreiben. Was eignet sich dazu besser als eine Adressverwaltung ? Am Anfang kümmern wir uns erstmal um eine effiziente Datenverwaltung.
 
Strukturen

Wenn wir unsere Adressen verwalten, so sind die Angaben in der Regel
 

  • Name 
  • Vorname 
  • Straßenname 
  • Hausnummer 
  • Postleitzahl 
  • Ort 
  • Telefonnummer

Mit unseren bisherigen Kenntnissen würden wir für jeden Eintrag ein Feld benutzen Das das auf die Dauer recht umständlich und nervenraubend ist,kann man sich vorstellen. Da wir in unserer Adressverwaltung immer einen feststehenden Datensatz haben können wir uns auch eine eigene Datenstruktur anlegen, die unsere gewünschten, und bekannten, Datentypen enthält. Folgende Anweisung muß nun im Vereinbarungsteil stehen
 

struct datensatz {
      char Name[50]; 
      char Vorname[50]; 
      char Strassenname[50]; 
      char Hausnummer[50]; 
      char Postleitzahl[50]; 
      char Ort[50];
      char Telefonnummer[50];
};

Die Telefonnummer legen wir als ein Feld der Größe 50 auf char an. Das tun wir, da wir auch führende Nullen kennen und die schwer darstellbar sind, mit reinem int - Variablen.
Damit hätten wir nun eine Benennung & Reihenfolge der Datentypen vorgegeben der wir einen Namen gaben. Prinzipiell sieht eine Strukturdeklarierung in C wie folgt aus.
 

struct Strukturname 
      Komponenten 
    };

Komponenten sind hier eine Ansammlung von definierten Variablen die Strukturelemente sind und fest zu der erstellten Struktur gehören. Mit der Struktur können wir im Prinzip wie mit einem Variablentypen umgehen, da es sich hierbei um eine Ansammlung von Datentypen handelt.
 

struct datensatz freunde[10];

Damit legen wir im Vereinbarungsteil fest, daß das Feld freunde 10 Einträge umfaßt. (s. Felder). Man kann sich vorstellen, das ein Datentyp "struct datensatz" erzeugt wird und die Variable Freunde vom Typ "struct datensatz" ist.
 
 
 
Beispielprogramm für Strukturen

Damit wir von der grauen Theorie etwas Abstand gewinnen, schreiben wir eine Struktur und spielen ein wenig mit ihr herum.
 

#include <stdio.h>

void main ( void
{

    /* die Variable struktur wird mittels einer Struktur deklariert */
    struct ganz 
    {
      int zahl; 
      char *zeichen;
    } struktur[2];

    /* nun weisen wir den Einzelelementen Werte zu */
    struktur[0].zahl = 2;
    struktur[0].zeichen = "unsere erste Struktur";

    struktur[1].zahl = 5; 
    struktur[1].zeichen = "unsere zweite Struktur";

    /* schlieszlich geben wir das Ganze noch aus */
    printf ("\n %s enthaelt %d\n",struktur[0].zeichen, struktur[0].zahl);
    printf ("\n %s enthaelt %d\n",struktur[1].zeichen, struktur[1].zahl);

}

Nach dem starten erscheint auf dem Bildschirm:

Screenshot des ausgeführten Programms

Sicherlich sind einem zwei Dinge ins Auge gefallen: Die Deklaration und das Auslesen und Zuweisen der einzelnen Strukturelemente. Bei der Deklaration gibt es folgende Vereinfachung
 

struct ganz 
    int zahl; 
    char *zeichen;
} struktur[2];

ist die Abkürzung für
 

struct ganz 
    int zahl;
    char *zeichen;
};

struct ganz struktur[2];


 
Datenausgabe ( Zugriff auf Strukturelemente )

Wie in dem Beispielprogramm zu sehen ist, kann man gezielt auf einzelne Daten zugreifen. Auf die einzelne Variable kann man mittels Strukturname.Variablenname zugegriffen werden. Es ist wichtig, daß zwischen den Elementen der Punkt steht. Er trennt in der Hierarchie die einzelnen Elemente. Der Variablenname ist immer der, der in der Strukturerstellung benutzt wird!
 

Strukturname.Variablenname

Zugriff auf die einzelnen Variablen erhält man z.B. mittels

struktur[0].zahl = 2; 

oder
 

struktur[0].zeichen = "unsere erste Struktur";

weisen den Elementen die Werte zu. Dabei ist stets darauf zu achten,ob der Datentyp korrekt ist. Man kann, wie bisher auch, auf Felder innerhalb von Strukturen zugreifen:
 

struct strukturname 
      {
        int zahl [10];
      } struktur[2];
printf ("%c",struktur[0].zahl[5] );

gibt uns z.B. die 5. Zahl des Zahlfeldes aus "struktur [0]" aus. Immer an den Stolperstein denken: Indizes fangen bei 0 an ! Das selbe Verhalten gilt analog zu fprintf !
 
 
Dateneingabe ( Zugriff auf Strukturelemente )

Analog zur bisherigen Eingabe können auch Strukturelemente eingelesen werden.
 

scanf ("%d",&struktur[0].zahl);

liest z.B. das Integer-Element "zahl" von struktur[0] ein.Gleiches gilt analog für fscanf ! Da gibt es also nichts neues :-)
 
 
Zeiger als Strukturelemente

Jetzt kommen wir wieder zu dem beliebtesten Teil von C , den Zeigern. Wie bisher können beliebige Variablentypen innerhalb einer Struktur stehen. Das schließt auch Zeiger mit ein. Folgende Struktur ist also durchaus denkbar
 

struct struktur 
{
    int zahl; 
    char *zeichenkette; 
}

 
 
Zeiger auf Strukturelemente

Als Beispiel benutzen wir die Struktur des vorigen Beispiels und generieren folgenden Zeiger
 

struct struktur *zeiger;

Wenn wir nun auf ein Element zugreifen wollen, müssen wir
 

(*zeiger).zahl;

schreiben. Wieso eigentlich die Klammer um den Strukturnamen ? Da der Punkt eine größere Priorität gegenüber dem *-Operator hat, muß der erste Ausdruck in Klammern gesetzt werden. Da das auch umständlich und unübersichtlich ist gibt es eine Vereinfachung.
 

zeiger->zahl;

ist äquivalent zu dem vorigen Elementzugriff. Der -> Operator sorgt letztlich für ein mehr an Leserlichkeit.
 
 
Variantenreichtum mit Unions

Was macht man, wenn man sich nicht im klaren ist, was für ein Typ eine Variable haben soll ? Man würde sagen: Mach's noch einmal, Sam. Aber im Ernst, manchmal ist es ganz sinnvoll, wenn die Möglichkeit besteht einer Variablen verschiedene Werte zuweisen zu können. In C kann man dies mit den sogenannten unions machen. Sie werden manchmal auch als Varianten bezeichnet. Gebildet werden sie ähnlich wie dies bei struct geschah, beispielsweise mit
 

union TypenName
{
int Integer;
float Fliesskomma;
char Zeichen;
} VariablenName;

Es wird bei union nur jeweils immer ein Datenwert verwaltet! Daher kann man nie nur auf das zuletzt beschriebene zugreifen. Jeder Datentyp hat hierbei seinen eigenen Bezeichner.Greifen wir auf einen Wert zu, können wir dies, wie schon bei den Strukturen, mittels der Angabe des Bezeichners machen. Wenn wir also wie oben eine Variable erzeugt haben, so können wir ihr wie folgt einen Werte zuweisen.
 

VariablenName.Integer = 3;
oder
VariablenName.Zeichen = 'u';

Wenn man auf diese Weise verschiedene Datentypen benutzt, sollte man zusätzlich in einer zweiten Variable den Typ kodieren oder, was noch etwas geschickter ist, wenn man schon unions benutzt, dann über eine Struktur, wie dies im folgenden Beispiel gemacht wurde. In der Variable Typ wird der aktuelle Union-Datentyp gespeichert. Dies kann, wie im Beispiel, über Precompilerbefehle geschehen.
 

/* Beispiel fuer union */
#include <stdio.h>

/* Definitionen per Precompiler */
#define INT 1
#define DOUBLE 2
#define CHAR 3

void main (void)
{

struct UnionDaten
{
int Typ;
union UTypen
{
int Integer;
double Double;
char Zeichen;
} TypenWert;
} Daten;

/* Integer */
Daten.Typ = INT;
Daten.TypenWert.Integer = 3;
printf ("\n Integerwert = %d\n", Daten.TypenWert.Integer);

/* Double */
Daten.Typ = DOUBLE;
Daten.TypenWert.Double = 1.1415;
printf ("\n Double = %f\n", Daten.TypenWert.Double);

/* Zeichen */
Daten.Typ = CHAR;
Daten.TypenWert.Zeichen = 'x';
printf ("\n Zeichen = %c\n", Daten.TypenWert.Zeichen);

}

Screenshot des ausgeführten Programms

Wie man sieht kann man union innerhalb von struct erzeugen und genauso auch umgekehrt einen struct in einer union. Dies kann man auch benutzen, wenn man Datensätze verwaltet und die Wahl hat zwischen Benutzernummern und Kundendaten. Dann könnte eine union wie folgt aussehen
 

union DatenSatz
{
int Datentyp;
long int Benutzernummer;
struct KundenStruktur
{
char Name[50];
char Vorname[50];
char Telefon[50];
} Kundendaten;
} Kunde;

Nun könnte man in Kunde.Datentyp festhalten, welche Variante gerade aktuell ist, also ob der entweder den Betreffenden über die Kundendaten oder über die Kundennummer verwaltet. Eine Abfrage könnte bei oberer Struktur dann wie folgt aussehen
 

#define NUMMER 1
#define NAME 2
....
struct KundenStruktur AktuellerKunde;
long int KundenNummer;
....
if ( Kunde.Datentyp == NUMMER )
KundenNummer = Kunde.Benutzernummer;
else if ( Kunde.Datentyp == NAME )
AktuellerKunde = Kunde.Kundenaten;
....

 
Aufzählungskonstanten mit Enum

Vielfach hat man das Problem, das man ein paar einfache aufzählbare Dinge, wie z.B. Wochentage, unkompliziert verwalten möchte. Da diese sozugen Konstante Werte eines Typs sind, den wir erstmal nicht in der Form haben, müssen wir eine Möglichkeit finden diese darzustellen. Diese Möglichkeit ist uns mit enum gegeben. Damit werden sogenannte Aufzählungskonstanten erzeugt. Die Bezeichnung kommt daher, das die Werte Konstanten entsprechen.
 

#include <stdio.h>

void main (void)
{

enum Wochentag
{
Montag, Dienstag, Mittwoch, 
Donnerstag, Freitag, Samstag, Sonntag
};
enum Wochentag Tag = Dienstag;

if (Tag == Dienstag) printf("\nheute ist Dienstag\n");
if (Tag == 1 ) printf ("\nDienstag\n");

}

Screenshot des ausgeführten Programms

Wie wir verwundert feststellen, kann man die Variable Tag sowohl mit der Konstante Dienstag als auch mit dem Wert 1 testen. Dies liegt daran, das den Konstanten, wenn keine explizite Zuweisung vorliegt, in aufsteigender Folge die Werte 0 , 1 , u.s.w. zugeordnet werden. In unserem Fall würde also gelten Montag = 0 , Dienstag = 1 , Mittwoch = 2 , u.s.w. . Diese aufsteigende Zuweisung von nichtinitialisierten Konstanten wird auch beibehalten, wenn wir z.B. die erste Konstante einen Wert zuweisen.
 

enum Wochentag
{
Montag = 1 , Dienstag, Mittwoch, 
Donnerstag, Freitag, Samstag, Sonntag
};

Bei dieser Zuweisung werden den nachfolgenden Konstanten wieder in aufsteigender Zählweise Werte zugeordnet, also Dienstag = 2 , Mittwoch = 3 , u.s.w. Wer also Boolsche Werte in C vermißte, also Variablen mit den Wahrheitswerten wahr oder falsch , kann sie sich nun selber definieren.
 

enum Bool { wahr , falsch };

Natürlich kann man nicht nur Zahlenwerte als Konstante definieren. Zu beachten ist nur, das es sich um eine der Datentypen handelt. Beispielsweise könnte man so die Sonderzeichen verwalten.
 

enum SonderZeichen { NullZeichen = '\0' , Tab = '\t' };

 
Übung

Für unsere private Datenbank ist die folgende Aufgabe zu dem Thema hilfreich. Als Struktur sei gegeben:
 

struct datensatz 
{
    char Name[50];
    char Vorname[50]; 
    char Strassenname[50]; 
    char Hausnummer[50];
    char Postleitzahl[50]; 
    char Ort[50]; 
    char Telefonnummer[50];
};

Schreiben sie ein Programm, welches 5 Datensätze einliest. Jeder Datensatz besteht aus der obigen Struktur. Das Programm sollte folgende Punkte erfüllen:

a) Schreiben der Daten in ein File.
b) Lesen der Daten aus einem File
c) Auswahl eines Datensatzes und Ausgabe des Datensatzes auf dem Bildschirm

Da das Programm etwas größer als die bisherigen wird, sei sie diesmal die einzige Aufgabe. Für den weiteren Ablauf ist ein erfolgreiches programmieren der Aufgabe wichtig - und scheuen sie nicht davor Ihr Wissen aus früheren Kursen aufzufrischen. Obwohl die Aufgabe etwas schwer erscheint, übt sie dennoch den Umgang mit Zeigern und Strukturen. Und denken sie immer an das Semikolon ;-)
Und nächstes mal programmieren wir weiter an unserer privaten Datenbank.
 
 
 
 
...das Obligatorische

Autor: Sebastian Cyris \ PCD Bascht

Dieser C-Kurs dient nur zu Lehrzwecken! Eine Vervielfältigung ist ohne vorherige Absprache mit dem Autor verboten! Die verwendete Software unterliegt der GPL und unterliegt der Software beiliegenden Bestimmungen zu deren Nutzung! Jede weitere Lizenzbestimmung die der benutzten Software beiliegt, ist zu beachten!