| #include <pronix.de> |
|
|
Bisher wurden mit den Arrays Datenstrukturen desselben Typs verwendet. In dem folgenden Kapitel werden jetzt unterschiedliche Datentypen zu einer Struktur zusammengefasst. Anschließend können Sie auf diese Struktur zugreifen wie auf einfache Variablen.
18.1. Struktur deklarieren
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Hinweis |
|
Vielleicht haben Sie schon mit dem sizeof-Operator die wirkliche Größe dieser Struktur getestet. Auf 32-Bit-Systemen dürften dies 36 Bytes sein. Dies liegt an der Fragmentierung des Betriebssystems. Meistens werden die Daten im Vier-Byte-Alignment gespeichert. Mehr dazu erfahren Sie ein paar Seiten später. |
|
Zugreifen können Sie auf die einzelnen Variablen einer Struktur mithilfe des Punktoperators (.). Ansonsten erfolgen die Initialisierung und der Zugriff wie bei normalen Variablen. Beispielsweise:
#include <stdio.h>
#include <string.h>
struct index {
int seite;
char titel[30];
};
int main()
{
struct index lib;
lib.seite = 23;
strcpy(lib.titel, "C-Programmieren");
printf("%d, %s\n",lib.seite, lib.titel);
return 0;
}

Abbildung 18.2: Strukturelemente wurden mit Werten initialisiert
Mit
struct index lib;
wird eine Struktur mit der Bezeichnung lib vom Typ index deklariert. Diese Extra-Deklaration hätten Sie auch mit folgender Schreibweise erzielt:
struct index {
int seite;
char titel[30];
}lib;
Wenn Sie den Typennamen dieser Struktur nicht benötigen, kann sie auch ohne deklariert werden:
struct {
int seite;
char titel[30];
}lib;
Es spricht auch nichts dagegen, mehrere Typen auf einmal zu deklarieren:
struct index {
int seite;
char titel[30];
}lib1, lib2, lib3;
Hiermit wurden drei Variablen vom Typ index deklariert. Strukturen können natürlich ebenso wie normale Datentypen direkt bei der Deklaration mit Werten initialisiert werden:
struct index {
int seite;
char titel[30];
}lib = { 308, "Strukturen" };
Oder auch bei der Deklaration in der main()-Funktion:
struct index lib = { 55, "Einführung in C" };
Zur Demonstration folgt ein Listing, welches zeigt, wie Sie auf den Inhalt einer Struktur zugreifen können:
#include <stdio.h>
#define MAX 30
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen;
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres x)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s", x.vname);
printf("Nachname........:%s", x.nname);
printf("Postleitzahl....:%ld\n",x.PLZ);
printf("Ort.............:%s", x.ort);
printf("Geburtsjahr.....:%d\n", x.geburtsjahr);
}
int main()
{
printf("Vorname : ");
fgets(adressen.vname, MAX, stdin);
printf("Nachname : ");
fgets(adressen.nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%5ld",&adressen.PLZ);
} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(adressen.ort, MAX, stdin);
printf("Geburtsjahr : ");
do {scanf("%4ld",&adressen.geburtsjahr);
}while(getchar()!='\n' );
ausgabe(adressen);
return 0;
}

Abbildung 18.3: Einlesen und Ausgeben von Strukturen
Die erste Eingabe in der main()-Funktion lautet:
fgets(adressen.vname, MAX, stdin);
Damit wird der Vorname eingelesen. Der Zugriff erfolgt über den Namen der Struktur, gefolgt vom Punkteoperator. Dahinter folgt das entsprechende Strukturelement, welches diese Daten erhalten soll. Dies funktioniert genauso, wenn Sie einen String direkt mit der Funktion strcpy() in ein Strukturelement einkopieren wollen:
strcpy(adressen.vname , "Tux");
Die direkte Initialisierung numerischer Werte an Strukturelementen lässt sich ebenfalls variablentypisch durchführen:
adressen.PLZ = 89000;
Wenn Sie alle Strukturelemente eingegeben haben, wird die Funktion ausgabe() aufgerufen:
ausgabe(adressen);
Als Argument erhält diese Funktion die Struktur adressen. Danach werden die einzelnen Elemente der Struktur auf dem Bildschirm ausgegeben.
Eine weitere Möglichkeit, die Struktur im Listing sofort mit einem Inhalt zu initialisieren, ist diese:
stuct adres {
char vname[20];
char nname[20];
long PLZ;
char ort[20];
int geburtsjahr;
}adressen = {"Ernest", "Hemming" ,3434, "Havanna" ,1913};
Folgende Wertzuweisung von Strukturen sollten Sie allerdings vermeiden:
struct {
int a1;
int a2;
int a3;
}werte1,werte2;
werte1.a1=8;
werte1.a2=16;
werte1.a3=32;
werte2=werte1; /*Bitte vermeiden Sie solche Zuweisungen*/
Das ist zwar erlaubt in C, kann aber zu Fehlern führen, wenn ein Compiler dies nicht unterstützt. Sicherer wäre da die folgende Möglichkeit:
memcpy(&werte2,&wert1, sizeof(werte1));
Dies soll nur ein Hinweis sein und keine Regel!
Es folgt ein kleiner Tipp, wie Sie sich die Strukturen vielleicht noch besser vorstellen können. Als Vergleich dienen dazu die Variablen int x und char c.
| Typ | Name |
| int | x |
| char | c |
| struct adres | adressen |
Wird die Variable x mit einem Wert initialisiert, gehen Sie bekannterweise so vor:
x=1999;
Bei einer Struktur kommt noch ein kleiner Zusatz hinzu:
adressen.geburtsjahr=1999;
Anhand des Funktionsaufrufs vom Beispiel zuvor konnten Sie sehen, dass Strukturen genauso wie jeder andere Datentyp an Funktionen per call-by-value übergeben werden können. Die Funktion bekommt dabei eine Kopie der vollständigen Struktur übergeben. Das Anlegen einer Kopie kann bei häufigen Funktionsaufrufen mit umfangreichen Strukturen die Laufzeit des Programms erheblich beeinträchtigen. Um diesen Mehraufwand zu sparen, empfehle ich Ihnen, Zeiger auf Strukturen als Parameter zu verwenden. Das folgende Listing soll dies demonstrieren:
#include <stdio.h>
#define MAX 30
struct adres { char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
}adressen;
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres *struct_ptr)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",(*struct_ptr).vname);
printf("Nachname........:%s",(*struct_ptr).nname);
printf("Postleitzahl....:%ld\n",(*struct_ptr).PLZ);
printf("Ort.............:%s",(*struct_ptr).ort);
printf("Geburtsjahr.....:%d\n",(*struct_ptr).geburtsjahr);
}
int main()
{
printf("Vorname : ");
fgets(adressen.vname, MAX, stdin);
printf("Nachname : ");
fgets(adressen.nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%5ld",&adressen.PLZ);
} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(adressen.ort, MAX, stdin);
printf("Geburtsjahr : ");
do {scanf("%4ld",&adressen.geburtsjahr);
}while(getchar()!='\n' );
ausgabe(&adressen);
return 0;
}
Dies ist dasselbe Listing wie oben, nur wird dieses Mal das Argument der Funktion ausgabe() mit call-by-reference übergeben:
ausgabe(&adressen);
Die Funktion ausgabe() selbst musste dabei auch ein wenig verändert werden:
void ausgabe(struct adres *struct_ptr)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",(*struct_ptr).vname);
printf("Nachname........:%s",(*struct_ptr).nname);
printf("Postleitzahl....:%ld\n",(*struct_ptr).PLZ);
printf("Ort.............:%s",(*struct_ptr).ort);
printf("Geburtsjahr.....:%d\n",(*struct_ptr).geburtsjahr);
}
Außer dem Zeiger struct_ptr als Parameter, der auf eine Struktur vom Typ adress zeigen kann, musste auch der Zugriff auf die Strukturelemente geändert werden. Dass Sie bei Call-by-reference-Variablen mit dem Dereferenzierungsoperator arbeiten müssen, ist Ihnen ja bekannt. Da aber hier der Punkteoperator verwendet wird, muss der Referenzzeiger struct_ptr zwischen zwei Klammern gestellt werden, da der Ausdruck zwischen den Klammern die höhere Bindungskraft hat und zuerst ausgewertet wird:
printf("Vorname.........:%s",(*struct_ptr).vname);
Die Hersteller von C haben aber auch gemerkt, dass eine solche Schreibweise - speziell wenn mehrere Referenzen folgen - schwer lesbar ist. Daher wurde der so genannte Elementkennzeichnungsoperator (->) eingeführt. Mit diesem würde die Ausgabe des Vornamens folgendermaßen vorgenommen werden:
printf("Vorname.........:%s",(struct_ptr->vname);
Dies lässt sich auch recht einfach lesen, da dieser Operator aussieht wie ein Pfeil oder auch ein Zeiger. Diesen Operator werden Sie noch häufiger in diesem Buch benötigen als Ihnen lieb sein wird. Speziell, wenn es um die dynamischen Datenstrukturen geht (Kapitel 24).
Wie bei der Werteübergabe von Strukturen, sollten Sie auch bei der Werterückgabe von Strukturen Zeiger verwenden. Dafür soll wieder das eben geschriebene Listing verwendet werden. Es fehlt nämlich noch eine Funktion zur Eingabe der einzelnen Strukturelemente. Hier das Listing mit der Funktion eingabe(), welche die Anfangsadresse der Struktur zurückgibt:
#include <stdio.h>
#include <stdlib.h>
#define MAX 30
struct adres {
char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
};
/*Funktion zur Ausgabe des Satzes*/
void ausgabe(struct adres *struct_ptr)
{
printf("\n\nSie gaben ein:\n\n");
printf("Vorname.........:%s",struct_ptr->vname);
printf("Nachname........:%s",struct_ptr->nname);
printf("Postleitzahl....:%ld\n",struct_ptr->PLZ);
printf("Ort.............:%s",struct_ptr->ort);
printf("Geburtsjahr.....:%d\n",struct_ptr->geburtsjahr);
}
struct adres *eingabe(void)
{
struct adres *adressen;
adressen=(struct adres *)malloc(sizeof(struct adres));
printf("Vorname : ");
fgets(adressen->vname, MAX, stdin);
printf("Nachname : ");
fgets(adressen->nname, MAX, stdin);
printf("Postleitzahl : ");
do {scanf("%ld",&adressen->PLZ);} while(getchar()!= '\n');
printf("Wohnort : ");
fgets(adressen->ort, MAX, stdin);
printf("Geburtsjahr : ");
do {
scanf("%ld",&adressen->geburtsjahr);
}while(getchar()!='\n' );
return adressen;
}
int main()
{
struct adres *adresse1, *adresse2;
adresse1=eingabe();
adresse2=eingabe();
ausgabe(adresse1);
ausgabe(adresse2);
return 0;
}
Bei diesem Listing verwenden Sie bereits nur noch Zeiger und kommen zum ersten Mal mit den dynamischen Datenstrukturen in Berührung. Aufgerufen wird diese Funktion zur Eingabe von Strukturelementen mit:
adresse1=eingabe();
Einen Adressoperator benötigen Sie hier nicht, da Sie ja bereits einen Zeiger verwenden. Und da Sie hier einen Zeiger verwenden, benötigen Sie in der Funktion erst einmal Speicherplatz für eine Struktur. Dieser wird mit der Funktion malloc() angefordert:
struct adres *adressen; adressen=(struct adres *)malloc(sizeof(struct adres));
Jetzt können Sie mithilfe des Elementkennzeichnungsoperators die Daten der einzelnen Strukturelemente einlesen. Am Ende der Funktion wird die Adresse dieser reservierten und mit neuem Inhalt versehenen Struktur mit return an den Aufrufer zurückgegeben. In der main()-Funktion zeigt jetzt der Zeiger adresse1 auf diesen Speicherbereich.
|
Hinweis für Anfänger |
|
In den letzten beiden Kapiteln ist der Schwierigkeitsgrad enorm gestiegen. Falls Sie dabei nur Bahnhof verstanden haben, machen Sie sich keine Sorgen, im weiteren Verlauf dieses Buchs fügen sich auch für Sie die einzelnen Puzzle-Teile zu einem Ganzen. Die nächsten Abschnitte werden jetzt wieder um einiges verständlicher sein. |
|
Natürlich lässt sich eine Funktion mit Strukturen als Rückgabewert auch einfacher deklarieren:
struct typNAME Funktionsname()
Es gibt leider keinen portablen oder sinnvollen Weg, zwei Strukturen direkt miteinander zu vergleichen. Das Problem liegt darin, dass ein Compiler aus Optimierungsgründen Lücken zwischen den einzelnen Elementen lassen kann. Mehr zu diesem so genannten Padding erfahren Sie in Abschnitt 18.12.
Es bleibt Ihnen also letztendlich nichts anderes übrig, als eine eigene Funktion zu schreiben. Hier ein Beispiel, wie Sie dabei vorgehen können:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 30
struct adres { char vname[MAX];
char nname[MAX];
long PLZ;
char ort[MAX];
int geburtsjahr;
};
int cmp_structs(struct adres *str1, struct adres *str2)
{
/* Vorname gleich und */
if(strcmp(str1->vname, str2->vname) == 0 &&
/* Nachname gleich und */
strcmp(str1->nname, str2->nname) == 0 &&
/* Postleitzahl gleich und */
(str1->PLZ-str2->PLZ) == 0 &&
/* Wohnort gleich und */
strcmp(str1->ort, str2->ort) == 0 &&
/* geburtsjahr gleich */
(str1->geburtsjahr-str2->geburtsjahr) == 0)
return 0; /* Beide Strukturen gleich */
else
return 1; /* Strukturen nicht gleich */
}
int main()
{
struct adres adresse1={"John","Leroy",1234,"New York",1980 };
struct adres adresse2={"John","Leroy",1234,"New York",1980 };
if(cmp_structs(&adresse1, &adresse2) == 0)
printf("Beide Strukturen sind gleich?!?!\n");
else
printf("Die Strukturen weisen Unterschiede auf\n");
return 0;
}