15.6 Arrays von Strukturen 

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:
/* struct_arr1.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> struct index { int seite; char titel[30]; }; int main(void) { 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 EXIT_SUCCESS; }
Abbildung 15.4 Array von Strukturen
Abbildung 15.4 verdeutlicht dies nochmals. Auch hierbei erfolgt die Initialisierung der einzelnen Werte über den Punktoperator. Ein Fehler, der dabei oft 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. Hier sehen Sie das vollständige Listing:
/* struct_arr2.c */ #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("%4d",&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 = 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(void) { 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 des Nachnamens :"); 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 EXIT_SUCCESS; }
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 und gibt an, 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 Struktur-Arrays, 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') usw.
Diese Suchfunktion sucht nur anhand des ersten Buchstabens 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)
So wird ein Zeiger temp vom Typ struct adres * definiert:
struct adres *temp; temp = malloc(sizeof(struct adres *));
Dieser dient als temporärer Zwischenspeicher zum 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) > 0) { *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 Gedanken über neuen Speicherplatz machen. Vom Aufwand einmal abgesehen, sind Struktur-Arrays eine erhebliche Bremse für ein Programm. Nehmen wie das Sortieren als Beispiel: 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 geschieht, wäre die Laufzeit katastrophal. Wie Sie diesen Aufwand umgehen und sich das Leben einfacher machen können, erfahren Sie in Kapitel 21, »Dynamische Datenstrukturen«.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.