Bisher wurden mit Arrays Datenstrukturen des selben Typs verwendet. In den folgenden Kapitel werden jetzt unterschiedliche Datentypen zu einer Struktur zusammengefasst. Damit kann anschließen auf diese Struktur zugegriffen werden, wie auf einfache Variablen. C C++ C/C++ Strukturen Arrays von Strukuren Strukturen in Strukturen Datenstrukturen enum typedef Bitfelder Unions Strukturen - Arrays von Strukuren Strukturen in Strukturen Datenstrukturen enum typedef Bitfelder Unions Kapitel 18: Strukturen

18.6. Arrays von Strukturen            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Bei Arrays von Strukturen gilt dasselbe Prinzip wie im Abschnitt zuvor dargestellt. Die Wertzuweisung funktioniert ebenfalls wie bei den normalen Arrays, nämlich mithilfe des Indizierungsoperators ([]). Hierzu ein Beispiel:

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

struct index {
               int seite;
               char titel[30];
             };

int main()
{
   int i;
   struct index lib[3];

   lib[0].seite=312;
   strcpy(lib[0].titel, "Arrays von Strukturen");

   lib[1].seite=320;
   strcpy(lib[1].titel, "Strukturen in Strukturen");

   lib[2].seite=900;
   strcpy(lib[2].titel, "Anhang");

   for(i=0; i<3; i++)
      printf("Seite %3d\t %-30s\n",lib[i].seite, lib[i].titel);
   return 0;
}

Abbildung 18.4: Array von Strukturen
Abbildung 18.4: Array von Strukturen

Die Abbildung verdeutlicht dies nochmals. Auch hierbei erfolgt die Initialisierung der einzelnen Werte über den Punkteoperator. Ein Fehler, der dabei gern gemacht wird, ist folgende falsche Wertübergabe:

/* falsche Position des Feldindex */
strcpy(lib.titel[1], "Hallo"); //falsch

strcpy(lib[1].titel, "Hallo"); //richtig

Der Variablenname der Struktur lautet schließlich lib und nicht titel.

Jetzt soll das Listing zur Adressverwaltung erweitert werden. Diesmal wird das Programm so umgeschrieben, dass es mit Arrays von Strukturen ausgeführt wird:

struct adres {
               /*Variablen*/
             }adressen[100];

In dieser Struktur von Arrays lassen sich somit 100 Daten von Adressen speichern. Hierzu das vollständige Listing:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30

static int x=0;

struct adres {
               char vname[MAX];
               char nname[MAX];
               long PLZ;
               char ort[MAX];
               int geburtsjahr;
             }adressen[100];

void Eingabe(int nr, struct adres neu[])
{
   printf("Vorname : ");
   fgets(neu[nr].vname, MAX, stdin);
   printf("Nachname : ");
   fgets(neu[nr].nname, MAX, stdin);
   printf("Postleitzahl: ");
   do {scanf("%5ld",&neu[nr].PLZ);} while(getchar()!= '\n');
   printf("Wohnort : ");
   fgets(neu[nr].ort, MAX, stdin);
   printf("Geburtsjahr : ");
   do {
       scanf("%4ld",&neu[nr].geburtsjahr);
      }while(getchar()!= '\n');
}


void Suche(struct adres search[],char buchstabe,int nr)
{
   int i;
   for(i=0; i<=nr; i++)
      {
         if(search[i].nname[0] == buchstabe)
            {
               printf("\n\nGefunden unter Buchstabe "
                      ":\"%c\"\n\n",buchstabe);
               printf("Vorname......:%s",search[i].vname);
               printf("Nachname.....:%s",search[i].nname);
               printf("Postleitzahl.:%ld\n",search[i].PLZ);
               printf("Ort..........:%s",search[i].ort);
               printf("Geburtsjahr..:%d\n",
                                  search[i].geburtsjahr);
               printf("\n\tWeiter mit <ENTER>\n");
               getchar();
            }
      }
}

void Ausgabe(struct adres all[],int nr)
{
   int i;

   for(i=0; i<nr; i++)
      {
         printf("Vorname.........:%s",all[i].vname);
         printf("Nachname........:%s",all[i].nname);
         printf("Postleitzahl....:%ld\n",all[i].PLZ);
         printf("Ort.............:%s",all[i].ort);
         printf("Geburtsjahr.....:%d\n\n",all[i].geburtsjahr);

         if((!(i%2))&& i!=0)
            {
               printf("\n\tWeiter mit <Enter>\n\n");
               getchar();
            }
      }
}

void Sort(struct adres sort[],int nr)
{
   int i,j;
   struct adres *temp;
   temp=(struct adres *)malloc(sizeof(struct adres *));
   if(NULL == temp)
      {
         printf("Konnte keinen Speicher reservieren...\n");
         return;
      }

   for(i=0; i<nr; i++)
      {
         for(j=i+1;j<nr;j++)
            {
               if(strcmp(sort[i].nname, sort[j].nname)>0)
                  {
                     *temp=sort[j];
                     sort[j]=sort[i];
                     sort[i]=*temp;
                  }
            }
      }
   printf(".....Sortiert!!\n\n");
}

int main()
{
   int auswahl;
   char c;

   do {
        printf("-1- Neue Adresse eingeben\n");
        printf("-2- Bestimmte Adresse ausgeben\n");
        printf("-3- Alle Adressen ausgeben\n");
        printf("-4- Adressen sortieren\n");
        printf("-5- Programm beenden\n");
        printf("\nIhre Auswahl : ");
        scanf("%d",&auswahl);
        /* fflush(stdin); */
        getchar();

        switch(auswahl)
           {
              case 1 : Eingabe(x++,adressen);
                       break;
              case 2 : printf("Anfangsbuchstabe d. Nachnamen :");
                       do {
                           scanf("%c",&c);
                          } while(getchar()!= '\n');
                       Suche(adressen,c,x);
                       break;
              case 3 : Ausgabe(adressen,x);
                       break;
              case 4 : Sort(adressen,x);
                       break;
              case 5 : printf("Ende....\n");
                       break;
              default: printf("Falsche Eingabe\n");

           }
     }while(auswahl <5);
   return 0;
}

Der erste Funktionsaufruf des Programms lautet:

Eingabe(x++,adressen);

Damit wird die Funktion Eingabe() zur Eingabe eines Adresssatzes aufgerufen. Als Argument wird dieser Funktion die globale Variable x und die Struktur adressen vom Typ struct adres übergeben. Die globale Variable x dient als Zähler und Indexfeld, wie viele und welche Strukturen gespeichert wurden. Der Funktionskopf sieht so aus:

void Eingabe(int nr,struct adres neu[])

Wenn Sie das Buch genau durchgelesen haben, wissen Sie, dass Sie auch folgende Schreibweise hätten verwenden können:

/* struct adres *neu und struct adres neu[]==call-by-reference */
void Eingabe(int nr,struct adres *neu)

Jetzt werden die ersten Daten in die Struktur eingegeben:

printf("Vorname : ");
fgets(neu[nr].vname, MAX, stdin);

fgets() liest den Vornamen des ersten Adresssatzes ein. Genauso werden auch die nächsten Daten an den einzelnen Strukturelementen übergeben. Danach können Sie einen zweiten Adresssatz eingeben.

Benötigen Sie z.B. den Nachnamen des 20. Adresssatzes (falls vorhanden), können Sie diesen wie folgt ausgeben lassen:

printf("%s",adressen[19].nname); 

Die zweite Funktion des Programms lautet:

Suche(adressen,c,x);

Sie stellt eine primitive Suchfunktion nach dem Anfangsbuchstaben des Nachnamens dar. Die Funktion erhält als Argument die Anfangsadresse des ersten Strukturarrays, den Anfangsbuchstaben des Nachnamens (c) und den Indexzähler für die Anzahl der vorhandenen Strukturen (x).

void Suche(struct adres search[],char buchstabe,int nr)  

Die Suche selbst ist relativ einfach aufgebaut:

for(i=0; i<=nr; i++)
   {
      if(search[i].nname[0] == buchstabe)

In der for-Schleife wird die Anzahl der vorhandenen Adressen hochgezählt, um jeden Adresssatz mit dem eingegebenen Anfangsbuchstaben des Nachnamens zu vergleichen. Als Beispiel sei der Buchstabe "M" gegeben. Der Programmablauf sieht damit folgendermaßen aus:

for...i=0
if(search[0].nname[0] == 'M')
falls ja, werden dessen Daten ausgegeben
for..i++ ..i<=nr
if(search[1].nname[0] == 'M')
falls ja, werden dessen Daten ausgegeben
for..i++..i<=nr
if(search[2].nname[0] == 'M')
u.s.w.

Diese Suchfunktion sucht nur anhand des ersten Buchstaben des Nachnamens. Bei der Suche nach ganzen Namen ist die Implementierung nicht viel komplizierter. Dazu verwenden Sie am besten die Funktion strcmp() aus der Headerdatei <string.h>.

Die Funktion Ausgabe() ist die einfachste von allen. Hiermit wird lediglich der Indexzähler hochgezählt, um alle vorhandenen Adressen auf dem Bildschirm auszugeben.

Als etwas schwieriger dürfte sich die Sortierfunktion erweisen:

void Sort(struct adres sort[],int nr)  

Mit

struct adres *temp;
temp=(struct adres *)malloc(sizeof(struct adres *));

wird ein Zeiger temp vom Typ struct adres * definiert. Dieser dient als temporärer Zwischenspeicher dem Austausch zweier Strukturen. Damit dieser Zwischenspeicher auch verwendet werden kann, muss Speicherplatz von der Größe der Struktur reserviert werden. Danach wird die Struktur mit dem Algorithmus "Selektion Sort" nach dem Anfangsbuchstaben des Nachnamens sortiert:

for(i=0; i<nr; i++)
   {
      for(j=i+1;j<nr;j++)
         {
            if(strcmp(sort[i].nname, sort[j].nname)
              {
                 *temp=sort[j];
                  sort[j]=sort[i];
                  sort[i]=*temp;
              }

Bei den Strukturen von Arrays tritt irgendwann das gleiche Problem wie bei den Arrays auf. Nach 100 Adressen ist bei diesem Programmbeispiel Schluss. Dann müssen Sie sich wieder um neuen Speicherplatz Gedanken machen. Vom Aufwand einmal abgesehen, sind Strukturarrays eine erhebliche Bremse für ein Programm. Beispielsweise das Sortieren: Alle Elemente einer Struktur müssen erst in einen temporären extra reservierten Speicher kopiert werden. Danach müsste Speicherplatz für neue Elemente der Struktur reserviert werden. Jetzt können Sie die Daten aus dem temporären Speicher wieder zurückkopieren, und zwar in den neu angelegten Speicher für Strukturen von Arrays. Von dem Fall, dass ein Element mitten in der Liste gelöscht werden soll, ganz zu schweigen. Schließlich müssen Sie diese Lücke auch wieder füllen. Somit müssten Sie z.B. alle Daten hinter dem gelöschten Element eine Position nach vorn setzen. Wenn dies bei einer Datenbank mit mehreren tausend Adressen vorgenommen wird, wäre die Laufzeit katastrophal. Wie Sie diesen Aufwand umgehen und sich das Leben einfacher machen können, erfahren Sie in Kapitel 24, Dynamische Datenstrukturen.

18.7. Strukturen in Strukturen (Nested Structurs)            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Neben der Kombination von Arrays und Strukturen können auch Strukturen innerhalb von Strukturen verwendet werden. Ein einfaches Beispiel sind Uhrzeit und Datum:

struct uhrzeit {
                 unsigned int stunde;
                 unsigned int minute;
                 unsigned int sekunde;
               };

struct datum   {
                 unsigned int tag;
                 unsigned int monat;
                 int jahr;
               };

struct termin {
                struct datum d;
                struct uhrzeit z;
              }t;

Abbildung 18.5: Strukturen in Strukturen
Abbildung 18.5: Strukturen in Strukturen

Anhand der Abbildung lässt sich deutlich erkennen, was Strukturen in Strukturen bedeuten. Es wird dabei auch von "Nested Structurs" gesprochen.

Der Zugriff auf die einzelnen Strukturen ist ein wenig aufwändiger. Hier ein Programmbeispiel, wie auf die einzelnen Elemente und die gesamte Struktur zugegriffen werden kann:

#include <stdio.h>

struct uhrzeit {
                 unsigned int stunde;
                 unsigned int minute;
                 unsigned int sekunde;
               };

struct datum   {
                 unsigned int tag;
                 unsigned int monat;
                 int jahr;
               };

struct termin  {
                 struct datum d;
                 struct uhrzeit z;
               }t;

int main()
{
   struct termin t = {{19,8,2003},{20,15,0}};

   printf("Termin am ");
   printf("%u.%u.%d um ",t.d.tag,t.d.monat,t.d.jahr);
   printf("%u.%u.%u0 Uhr \n\n",t.z.stunde,
                               t.z.minute,t.z.sekunde);

   printf("Neuen Termin eingeben !!\n\n");
   printf("Tag.............: ");
   scanf("%u",&t.d.tag);
   printf("Monat...........: ");
   scanf("%u",&t.d.monat);
   printf("Jahr............: ");
   scanf("%d",&t.d.jahr);
   printf("\n");
   printf("Stunde..........: ");
   scanf("%u",&t.z.stunde);
   printf("Minuten.........: ");
   scanf("%u",&t.z.minute);
   printf("Sekunden........: ");
   scanf("%u",&t.z.sekunde);
   printf("\n");
   printf("Neuer Termin am ");
   printf("%02u.%02u.%04d um ",t.d.tag,t.d.monat,t.d.jahr);
   printf("%02u.%02u.%02u Uhr \n",t.z.stunde,
                                  t.z.minute,t.z.sekunde);
   return 0;
}

Dieser geringe Mehraufwand stellt eher eine Erleichterung dar. Zuerst werden in diesem Programm zur Demonstration die Werte direkt übergeben:

struct termin t = {{19,8,2003},{20,15,0}};
/* struct termin t = {{struct datum}{struct uhrzeit}} */

Danach werden die Werte über die Tastatur eingelesen, z.B. die Minuten:

scanf("%u",&t.z.minute); 

Das Listing wird kaum jemanden überfordern. Daher soll das Programm zur Adressverwaltung mit dem eben gezeigten Programm verbunden werden. Damit hätte die Struktur termin folgendes Aussehen:

struct termin {
                struct datum d;
                struct uhrzeit z;
                struct adressen a;
              }t[20];

Abbildung 18.6: Eine weitere Struktur wurde zur Struktur hinzugefügt
Abbildung 18.6: Eine weitere Struktur wurde zur Struktur hinzugefügt

So können Sie 20 Termine mit Datum, Uhrzeit und der Adresse verwalten. Hierzu das vollständige Listing:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30

static int dates=0;
static int nr=0;

struct uhrzeit {
                 unsigned int stunde;
                 unsigned int minute;
                 unsigned int sekunde;
               };

struct datum   {
                 unsigned int tag;
                 unsigned int monat;
                 int jahr;
               };

struct adressen {
                  char vname[MAX];
                  char nname[MAX];
                  long PLZ;
                  char ort[MAX];
                  int geburtsjahr;
                }xyz[100];

struct termin {
                struct datum d;
                struct uhrzeit z;
                struct adressen a;
              }t[20];


void newdate(struct termin *);
int suche(char *);
void listdate(struct termin *,int);
void replacedate(struct termin *,int);
void sortdate(struct termin *,int);
void Eingabe(struct adressen *);
void Ausgabe(struct adressen *);
void Sortadress(struct adressen *);


void newdate(struct termin t[])
{
   int auswahl,ret;
   char such_name[MAX];

   printf("Tag.......: ");
   scanf("%u",&t[dates].d.tag);
   printf("Monat.....: ");
   scanf("%u",&t[dates].d.monat);
   printf("Jahr......: ");
   scanf("%d",&t[dates].d.jahr);
   printf("---------------------\n");
   printf("Stunde....: ");
   scanf("%u",&t[dates].z.stunde);
   printf("Minute(n).: ");
   scanf("%u",&t[dates].z.minute);
   printf("---------------------\n");

   printf("\nTermin mit :\n -1- Neuer Adresse\n");
   printf(" -2- Vorhandener Adresse\n");
   printf("Ihre Auswahl : ");
   do {
       scanf("%d",&auswahl);
      } while(getchar()!= '\n');

   if(auswahl==1)
      {
         printf("Vorname.....: ");
         fgets(t[dates].a.vname, MAX, stdin);
         printf("Nachname....: ");
         fgets(t[dates].a.nname, MAX, stdin);
         printf("Postleitzahl: ");
         do {
             scanf("%ld",&t[dates].a.PLZ);
            } while(getchar()!= '\n');
         printf("ORT.........: ");
         fgets(t[dates].a.ort, MAX, stdin);
         printf("Geburtsjahr..: ");
         do {
             scanf("%ld",&t[dates].a.geburtsjahr);
            } while(getchar()!= '\n');
         /* Neue Adresse kommt auch zum neuen Adresssatz */
         strcpy(xyz[nr].vname, strtok(t[dates].a.vname, "\n"));
         strcpy(xyz[nr].nname, strtok(t[dates].a.nname, "\n"));
         xyz[nr].PLZ = t[dates].a.PLZ;
         strcpy(xyz[nr].ort, t[dates].a.ort);
         xyz[nr].geburtsjahr=t[dates].a.geburtsjahr;
         dates++;
         nr++;
      }
   else
      {
         printf("Bitte geben Sie den Nachnamen ein : ");
         fgets(such_name, MAX, stdin);
         ret=suche(strtok(such_name,"\n"));
         strcpy(t[dates].a.vname,xyz[ret].vname);
         strcpy(t[dates].a.nname,xyz[ret].nname);
         t[dates].a.PLZ=xyz[ret].PLZ;
         strcpy(t[dates].a.ort,xyz[ret].ort);
         t[dates].a.geburtsjahr=xyz[ret].geburtsjahr;
         dates++;
      }
}

int suche(char suchname[])
{
   int n,found=0;
   for(n=0; n<=nr; n++)
      {
         if(strcmp(xyz[n].nname,suchname)==0)
            {
               found=1;
               break;
            }
      }
   if(found==1)
      return n;
   else
      {
         printf("Keine Eintrage mit dem Namen %s "
                "gefunden\n",suchname);
         return 0;
      }
}

void listdate(struct termin list[],int dates)
{
   int i;
   for(i=0;i<dates;i++)
      {
         printf("Termin am %02u.%02u.%04d ",
                   list[i].d.tag,list[i].d.monat,list[i].d.jahr);
         printf("um %02u.%02u Uhr\n",
                              list[i].z.stunde,list[i].z.minute);
         printf("mit %s %s\n\n",list[i].a.vname,list[i].a.nname);
      }
}

void replacedate(struct termin aendern[],int nt)
{
    if(nt < 20)
      {
        printf("Bitte neue Terminzeit eingeben!!\n");
        printf("Tag..........: ");
        scanf("%u",&aendern[nt].d.tag);
        printf("Monat........: ");
        scanf("%u",&aendern[nt].d.monat);
        printf("Jahr.........: ");
        scanf("%d",&aendern[nt].d.jahr);
        printf("------------------------\n");
        printf("Stunden......: ");
        scanf("%u",&aendern[nt].z.stunde);
        printf("Minuten......: ");
        scanf("%u",&aendern[nt].z.minute);
      }
    else
       printf("Falsche Eingabe\n");

}

void sortdate(struct termin sort[],int dates)
{
   struct termin *temp;
   int i,j;
   temp=(struct termin *)malloc(sizeof(struct termin *));
   if(NULL == temp)
      {
         printf("Konnte keinen Speicher reservieren...\n");
         return;
      }
   for(i=0; i<dates; i++)
      {
         for(j=i+1;j<dates;j++)
            {
               if(sort[i].d.jahr>sort[j].d.jahr)
                  {
                     *temp=sort[j];
                     sort[j]=sort[i];
                     sort[i]=*temp;
                  }
            }
      }
   printf(".....Sortiert!!\n");
}

void Eingabe(struct adressen neu[])
{
   unsigned int size;
   printf("Vorname : ");
   fgets(neu[nr].vname, MAX, stdin);
   /* newline-Zeichen entfernen */
   size = strlen(neu[nr].vname);
   neu[nr].vname[size-1] = '\0';

   printf("Nachname : ");
   fgets(neu[nr].nname, MAX, stdin);
   /* newline-Zeichen entfernen */
   size = strlen(neu[nr].nname);
   neu[nr].nname[size-1] = '\0';

   printf("Postleitzahl: ");
   do {
       scanf("%ld",&neu[nr].PLZ);
      } while(getchar()!= '\n');
   printf("Wohnort : ");
   fgets(neu[nr].ort, MAX, stdin);
   printf("Geburtsjahr : ");
   do {
        scanf("%d",&neu[nr].geburtsjahr);
      } while(getchar()!= '\n');
   nr++;
}

void Ausgabe(struct adressen all[])
{
   int i;

   for(i=0; i<nr; i++)
      {
         printf("Vorname.........:%s\n",all[i].vname);
         printf("Nachname........:%s\n",all[i].nname);
         printf("Postleitzahl....:%ld\n",all[i].PLZ);
         printf("Ort.............:%s",all[i].ort);
         printf("Geburtsjahr.....:%d\n\n",all[i].geburtsjahr);
         if((!(i%2))&& i!=0)
            {
              // fflush(stdin);
               printf("\n\tWeiter mit <Enter>\n\n");
               getchar();
            }
      }
}

void Sortadress(struct adressen sort[])
{
   struct adressen *temp;
   int i,j;
   temp=(struct adressen *)malloc(sizeof(struct adressen *));
   if(NULL == temp)
      {
         printf("Konnte keinen Speicher reservieren...\n");
         return;
      }
   for(i=0; i<nr; i++)
      {
         for(j=i+1;j<nr;j++)
            {
               if(strcmp(sort[i].nname, sort[j].nname)>0)
                  {
                     *temp=sort[j];
                     sort[j]=sort[i];
                     sort[i]=*temp;
                  }
            }
      }
   printf(".....Sortiert!!\n");
}


int main()
{
 int eingabe,aendern;

   do {
         printf("\tTerminverwaltung\n");
         printf("\t----------------\n\n");
         printf("\t-1- Neuer Termin\n");
         printf("\t-2- Termine auflisten\n");
         printf("\t-3- Termin ändern\n");
         printf("\t-4- Termine sortieren\n");
         printf("\t-5- Neue Adresse eingeben\n");
         printf("\t-6- Adressen ausgeben\n");
         printf("\t-7- Adressen sortieren\n");
         printf("\t-8- Programm beenden\n");
         printf("\n\tIhre Auswahl : ");
         scanf("%d",&eingabe);
         /* fflush(stdin); */
         getchar();
         switch(eingabe)
            {
               case 1 : newdate(t);
                        break;
               case 2 : listdate(t,dates);
                        break;
               case 3 : listdate(t,dates);
                        printf("Welchen Termin ändern(Nr.?):");
                        scanf("%d",&aendern);
                        replacedate(t,--aendern);
                        break;
               case 4 : sortdate(t,dates);
                        break;
               case 5 : Eingabe(xyz);
                        break;
               case 6 : Ausgabe(xyz);
                        break;
               case 7 : Sortadress(xyz);
                        break;
              default : break;
            }
      }while(eingabe<8);
   printf("Bye\n");
   return 0;
}

Abbildung 18.7: Terminverwaltungsprogramm während der Ausführung
Abbildung 18.7: Terminverwaltungsprogramm während der Ausführung

Eine kurze Erklärung: Bei der ersten Funktion newdate() wird zuerst der neue Termin eingegeben. Anschließend wird erfragt, ob ein neuer Adresseintrag für den Termin vorgenommen werden soll oder ob ein bereits vorhandener Adresssatz verwendet wird. Die nächste Funktion listdate() ist bereits bekannt aus einem anderen Programm. Mit der Funktion replacedate() kann ein Termin verschoben werden. Die Funktion sortdate() wurde ebenfalls schon ähnlich verwendet. Hier wurde nur nach Jahresdatum sortiert. Als Übung kann dies ja bis auf die Minute genau umgeschrieben werden. Der Rest des Programms wurde zum Teil schon bei der Adressverwaltung erklärt. Die Suchfunktion wurde dabei verändert. Diese sucht jetzt nach einem ganzen Nachnamen, nicht mehr nur nach dem Anfangsbuchstaben. Dem Programm fehlen noch eine Menge Fehlerüberprüfungen bei der Eingabe, auf diese wurde aus Gründen der Übersichtlichkeit verzichtet (Sie können sie zur Übung ja selbst schreiben).

18.8. Kurze Zusammenfassung zu den Strukturen            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Da das Kapitel über Strukturen ein recht umfangreiches ist, sollen hier nochmals einige Punkte zusammengefasst werden.

Der Datensatz einer Struktur wird folgendermaßen zusammengefasst:

struct daten{
              int var1;
              char var2[100];
            };

Hiermit haben Sie eine Struktur mit dem Namen daten mit zwei Elementen (var1, var2) deklariert. Eine Variable dieses Strukturtyps können Sie folgendermaßen deklarieren:

/* Variable vom Typ struct daten */
struct daten d1;
/* Ein Zeiger auf eine Struktur vom Typ stuct daten */
struct daten *d2;
/* Strukturarray mit 10 Elementen vom Typ struct daten */
struct daten d3[10];

Weiterhin können Sie diese Variablen vom Typ struct daten auch so deklarieren:

struct daten{
              int var1;
              char var2[100];
            }d1, *d2, d3[10];

Die Initialisierung einer Strukturvariablen kann über eine Initialisierungsliste erfolgen, welche für jedes Element einen Anfangswert enthält:

struct daten d1 = { 111, "Test" }; 

Auf ein einzelnes Strukturelement greifen Sie mit dem Punktoperator zu:

struct daten d1;
d1.var1 = 111;
stcpy(d1.var2, "Test");

/* Array von Strukturen */
d3[2].var1 = 123;  /* 3. Element des Strukturarrays */
stcpy(d3[2].var2, "Test");

Mit Zeigern auf Strukturen greifen Sie auf die einzelnen Elemente mithilfe des Pfeiloperators zu:

struct daten *d2;
…
d2->var1 = 111;
stcpy(d2->var2, "Test");

18.9. Union            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Eine weitere Möglichkeit, Daten zu strukturieren, sind Unions (auch Variante genannt). Abgesehen von einem anderen Schlüsselwort, bestehen zwischen Unions und Strukturen keine syntaktischen Unterschiede. Der Unterschied liegt in der Art und Weise, wie mit dem Speicherplatz der Daten umgegangen wird. Hierzu ein Beispiel der Speicherplatzbelegung einer Struktur:

struct test1 {
                 char a;
                 int b;
                 double c;
             };

Abbildung 18.8: Speicherbelegung bei einer Struktur
Abbildung 18.8: Speicherbelegung bei einer Struktur

Diese Struktur benötigt 16 Byte an Speicher. Das gleiche Beispiel jetzt mit dem Schlüsselwort union:

union test2 {
                 char a;
                 int b;
                 double c;
            };

Intern sieht dies mit union folgendermaßen aus:

Abbildung 18.9: Speicherbelegung einer Struktur mit dem Schlüsselwort union
Abbildung 18.9: Speicherbelegung einer Struktur mit dem Schlüsselwort union

Hierzu ein Listing, welches den Unterschied demonstrieren soll:

#include <stdio.h>

struct test1 {
                 char a;
                 int b;
                 double c;
             };

union test2 {
                 char a;
                 int b;
                 double c;
             };

int main()
{
   printf("struct benoetigt %d Bytes\n",sizeof(struct test1));
   printf("Union  benoetigt %d Bytes\n",sizeof(union test2));
   return 0;
}

Die Struktur mit dem Schlüsselwort union besitzt jetzt nur noch acht Bytes?! Dies verursacht das Schlüsselwort union und das größte Element in der Struktur, double. Dieser ersparte Speicherplatz wird allerdings mit dem Nachteil erkauft, dass immer nur ein Element in dieser Struktur verwendet werden kann. Beispielsweise wird dem Strukturelement int b der Wert 100 übergeben:

text.b=100;

Somit beträgt die Speichergröße der Struktur trotzdem acht Bytes für einen Integer, da der größte Datentyp in der union nämlich double lautet. Übergeben Sie jetzt an double c den Wert 10.55:

text.c=10.55;

Jetzt können Sie auf den Wert von int b nicht mehr zugreifen, da dieser von double c überlappt wurde. Zwar kann es immer noch sein, dass int b weiterhin den Wert 100 ausgibt, aber dies wäre Zufall, denn der Speicherbereich wurde überdeckt. Das Verhalten ist in diesem Fall undefiniert.

Welchen Vorteil hat es, wenn immer auf ein Element einer Struktur zugegriffen werden kann? Der wesentliche Vorteil liegt in der Anwendung von Union zum Einsparen von Speicherplatz bei der Verarbeitung großer Strukturen; beispielsweise bei Strukturen, wo bestimmte Elemente niemals miteinander auftreten. Ein Computerspiel z.B., bei dem sich immer einer gegen einen, Auge um Auge, duelliert. In dem folgenden Codeabschnitt wurde eine Struktur mit vier verschiedenen Gegnercharakteren erstellt:

struct gegner{
              union{
                     struct{
                              char name[20];
                              int power;
                              unsigned char leben;
                              unsigned int geschwindigkeit;
                           }fighter1;
                     struct{
                              char name[20];
                              int power;
                              unsigned char leben;
                              unsigned int geschwindigkeit;
                           }fighter2;
                     struct{
                              char name[20];
                              int power;
                              unsigned char leben;
                              unsigned int geschwindigkeit;
                           }fighter3;
                     struct{
                              char name[20];
                              int power;
                              unsigned char leben;
                              unsigned int geschwindigkeit;
                               }fighter4;
                          };
               }dat;

Damit wird immer nur auf eine Struktur zugegriffen, was in diesem Beispiel ausreichend ist, denn es sollten immer nur die Daten eines Gegners im Speicher sein, eben die, die im Augenblick nötig sind. So könnten Sie z.B. den fighter4 mit Werten initialisieren:

strcpy(dat.fighter4.name,"Superman");
dat.fighter4.power=5;
dat.fighter4.leben=1;
dat.fighter4.geschwindigkeit=NORMAL;

Der Zugriff auf die einzelnen Elemente erfolgt wie bei den normalen Strukturen. Zurück zum Programm der Adressverwaltung, denn dieses eignet sich auch prima für eine Union:

struct adres {
              union{
                   struct{
                             char vname[20];
                             char nname[20];
                             long PLZ;
                             char ort[20];
                             int geburtsjahr;
                         }privat;
                   struct{
                             char vname[20];
                             char nname[20];
                             long PLZ;
                             char ort[20];
                             char sternzeichen[20];
                             int geburtsjahr;
                         }geschaeftlich;
                    };
         }adressen[100];

Hiermit können 100 Adressen gespeichert werden. Mithilfe dieser Union wird das Private vom Geschäftlichen getrennt.

Natürlich können Sie mit dem Schlüsselwort union auch ein Array von Unions realisieren. Der Zugriff und die Initialisierung erfolgen genauso wie bei den Strukturen bereits beschrieben. Ein Beispiel:

#include <stdio.h>

union number {
               float x;
               int y;
             };

int main()
{
   union number mixed[2];

   mixed[0].x = 1.123;
   mixed[1].y = 123;
   mixed[2].x = 2.345;
   printf("%.2f\t%d\t%.2f\n", mixed[0].x,
                              mixed[1].y, mixed[2].x);
   return 0;
}

Wenn Sie eine Union initialisieren, erlaubt ANSI C nur einen Initialisierer. Und standardmäßig bedeutet dies, dass immer das erste Element der Union initialisiert wird. Es gibt keine Standardlösung, um die anderen Elemente zu initialisieren. Ein Beispiel:

union 2_D  {
               int x;
               int y;
             };
/* coordinate.x hat den Wert 123 */
 union 2_D coordinate = { 123 };

Achtung
 

Sollten Sie vorhaben, Ihre Programme auf andere Systemen zu portieren, so müssen sie sich hundertprozentig mit dem Alignment diverser Systeme auskennen, sofern Sie Unions einsetzen wollen. Ansonsten dürften Sie Probleme mit der Portierung bekommen.

18.10. Aufzählungstyp enum            zum Inhaltsverzeichnis