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
|
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 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 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 )
struct ganz {
char *zeichen; /* nun weisen wir den Einzelelementen Werte zu */
struktur[1].zahl = 5;
/* schlieszlich geben wir das Ganze noch aus */
|
Nach dem starten erscheint auf dem Bildschirm:
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
{
char *zeichen; |
ist die Abkürzung für
struct ganz
{
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
|
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
{
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;} 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;
oderVariablenName.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 */
void main (void)
struct UnionDaten} |
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; } Kunde;char Name[50];} Kundendaten; |
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} |
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,}; |
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 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!