![]() |
Programmieren in Cvon Prof. Dr. Rainer Thomas |
9.1 Aufbau und Vereinbarungen von Strukturen ("structures")
--> mehrere Daten der unterschiedlichsten Art lassen sich somit zu einer einzigen Variablen zusammenfassen und damit unter einem einzigen Namen ansprechen.
| --> | Aufbau von sehr effektiv handhabbaren problemangepaßten
Datenstrukturen.
|
Die einzelnen Komponenten (Felder) einer structure werden nicht - wie bei Arrays - durch einen Index sondern durch einen Namen gekennzeichnet.
Komponentenangabe
struct Strukturname dient als Datentypangabe.
/* -------------------------------------------------------------------- */
/* Definition eines Structure-Typ-Namens */
struct datum
{ int tag;
char monat[4];
int jahr;
};
/* -------------------------------------------------------------------- */
/* Variablenvereinbarung unter Verwendung eines vorher definierten
Structure-Typ-Namens */
struct datum geb_datum;
/* -------------------------------------------------------------------- */
/* Vereinbarung einer Variablen eines "namenlosen" Structure-Typs */
struct
{ int tag;
char monat[4];
int jahr;
} geb_datum;
/* -------------------------------------------------------------------- */
/* Definition eines Structure-Typ-Namens und Vereinbarung einer
Variablen dieses Typs */
struct datum
{ int tag;
char monat[4];
int jahr;
} geb_datum;
/* -------------------------------------------------------------------- */
/* Struktur mit einer Struktur als Komponente */
struct person
{ char name[20],
vorname[20];
struct
{ int tag;
char monat[4];
int jahr;
} geb_datum;
char verheiratet;
} student, diplomand;
/* -------------------------------------------------------------------- */
/* Struktur mit einer Struktur als Komponente
Verwendung eines vorher definierten Typ-Namens fuer diese Struktur */
struct datum
{ int tag;
char monat[4];
int jahr;
};
struct person
{ char name[20],
vorname[20];
struct datum geb_datum;
char verheiratet;
} student, diplomand;
/* -------------------------------------------------------------------- */
/* ------------------------------------------------------------------- */
/* */
/* KuR-C : Nur für Speicherklassen "extern" und "static" zulässig */
/* (wie bei arrays) */
/* ANSI-C : Auch für Speicherklasse "auto" zulässig */
/* (Initialisierung durch konst. Ausdrücke) */
/* */
/* ------------------------------------------------------------------- */
/* */
/* Achtung : Eine Initialisierung ist nicht zulässig in einer reinen */
/* Strukturtypdefinition */
/* */
/* ------------------------------------------------------------------- */
/* Beispiel 1 */
struct
{ int tag;
char monat[4];
int jahr;
} aktuell_datum = {1, "jan", 1987};
/* -------------------------------------------------------------------- */
/* Beispiel 2 */
struct datum
{ int tag;
char monat[4];
int jahr;
};
struct datum aktuell_datum = {1, "jan", 1987};
/* -------------------------------------------------------------------- */
9.2 Operationen mit Strukturen
Auf Strukturvariable läßt sich - wie auf einfache Variable - der
Adreßoperator "&" anwenden. Die dadurch gebildete Adresse der Variablen läßt
sich geeignet definierten Struktur-Pointer-Variablen zuweisen.
Der Objekttyp dieser Pointer-Variablen muß mit dem Typ der Strukturvariablen
übereinstimmen
Beispiel :
struct person student, *persptr;
persptr=&student;
z.B.
scanf("%s", student.name);
student.vorname[0]='F';
student.geb_datum.jahr=1991;
sptr->komp ist äquivalent mit (*sptr).komp
z.B.
scanf("%s", persptr->name);
persptr->vorname[0]='F';
persptr->geb_datum.jahr=1991;
z.B.
diplomand=student; /* alle Komponenten von student wer-
den nach diplomand übertragen */
/* Programm CPLMUL1 */
/* Multiplikation zweier komplexer Zahlen */
/* Pointer auf Strukturen als Parameter */
#include <stdio.h>
struct complex
{ double re;
double im;
};
void liescomplex(struct complex *px)
/* px ist Pointer auf Variable vom Typ complex */
{ struct complex xh;
scanf("%lf%lf",&xh.re,&xh.im);
*px=xh;
}
void multcomplex(struct complex *px1, struct complex *px2,
struct complex *pz)
/* px1, px2, pz sind Pointer auf Variable vom Typ complex */
{ (*pz).re = (*px1).re * (*px2).re - (*px1).im * (*px2).im;
(*pz).im = (*px1).re * (*px2).im + (*px1).im * (*px2).re;
}
void auscomplex(struct complex *px)
/* px ist Pointer auf Variable vom Typ complex */
{ printf("( %.4f , %.4f )",px->re,px->im);
}
void main(void)
{ struct complex czahl1,czahl2,czahl3;
int zeich;
printf("\n? ");
while ((zeich=getchar())!=EOF)
{ ungetc(zeich,stdin);
liescomplex(&czahl1);
liescomplex(&czahl2);
multcomplex(&czahl1,&czahl2,&czahl3);
auscomplex(&czahl1);
printf(" * ");
auscomplex(&czahl2);
printf(" = ");
auscomplex(&czahl3);
printf("\n\n? ");
getchar();
}
}
Bildschirmausgabe :
E:\RT\C\VORL>cplmul1
? 1 1 1 1
( 1.0000 , 1.0000 ) * ( 1.0000 , 1.0000 ) = ( 0.0000 , 2.0000 )
? 2 1 -2 3
( 2.0000 , 1.0000 ) * ( -2.0000 , 3.0000 ) = ( -7.0000 , 4.0000 )
? ^Z
/* Programm CPLMUL2 */
/* Multiplikation zweier komplexer Zahlen */
/* Strukturen als Parameter und Funktionswert */
#include <stdio.h>
struct complex
{ double re;
double im;
};
struct complex liescomplex(void)
{ struct complex x;
scanf("%lf%lf",&x.re,&x.im);
return(x);
}
struct complex multcomplex(struct complex x1, struct complex x2)
/* Parameter x1 und x2 sind Variable vom Typ complex */
{ struct complex z;
z.re = x1.re * x2.re - x1.im * x2.im;
z.im = x1.re * x2.im + x1.im * x2.re;
return(z);
}
void auscomplex(struct complex x)
/* Parameter x ist Variable vom Typ complex */
{ printf("( %.4f , %.4f )",x.re,x.im);
}
void main(void)
{ struct complex czahl1,czahl2,czahl3;
int zeich;
printf("\n? ");
while ((zeich=getchar())!=EOF)
{ ungetc(zeich,stdin);
czahl1=liescomplex();
czahl2=liescomplex();
czahl3=multcomplex(czahl1,czahl2);
auscomplex(czahl1);
printf(" * ");
auscomplex(czahl2);
printf(" = ");
auscomplex(czahl3);
printf("\n\n? ");
getchar();
}
}
Bildschirmausgabe :
E:\RT\C\VORL>cplmul2
? 2 1 -2 3
( 2.0000 , 1.0000 ) * ( -2.0000 , 3.0000 ) = ( -7.0000 , 4.0000 )
? ^Z
Auswertung zur Compilezeit.
Operator dient zur Ermittlung der Größe des Speicherplatzes in Bytes, den ein Objekt des Typs seines Operanden belegt.
Der Ausdruck wird nicht ausgewertet, es wird nur sein Typ bestimmt
Die genaue Größe des von Objekten belegten Speicherplatzes wird z.B. für eine dynamische Speicherallokation benötigt.
/* Programm TYPESIZE */
/* Demonstrationsprogramm zum sizeof-Operator */
/* Ausgabe der Speichergrößen der einfachen Datentypen */
#include <stdio.h>
void main(void)
{
int i=6;
printf("\nchar %2u Bytes\n", sizeof(char)); /* --> 1 */
printf("short %2u Bytes\n", sizeof(short)); /* --> 2 */
printf("int %2u Bytes\n", sizeof i); /* --> 2 */
printf("unsigned %2u Bytes\n", sizeof(unsigned)); /* --> 2 */
printf("long %2u Bytes\n", sizeof(long)); /* --> 4 */
printf("float %2u Bytes\n", sizeof(float)); /* --> 4 */
printf("double %2u Bytes\n", sizeof(double)); /* --> 8 */
printf("long double %2u Bytes\n", sizeof(long double)); /* --> 10 */
}
/* --------------- Variablendefinition (1.Moeglichkeit) ------------ */
#define MAX 1000
struct
{ char *name;
char *vorname;
char *anschrift;
long nummer;
} telefonbuch[MAX];
/* --------------- Variablendefinition (2. Moeglichkeit) ----------- */
#define MAX 1000
struct eintrag
{ char *name;
char *vorname;
char *anschrift;
long nummer;
};
struct eintrag telefonbuch[MAX];
/* ----------------------------------------------------------------- */
/* */
/* Komponentenzugriff ueber Indizierung */
/* */
/* z.B. : Suchen der Telefonnummer einer bestimmten Person */
/* . . . */
char suchname[20],
suchvorn[20];
int i,gefunden;
scanf("%s%s",suchname,suchvorn);
i=gefunden=0;
while (i<MAX && !gefunden)
{ gefunden = !strcmp(suchname,telefonbuch[i].name)
&& !strcmp(suchvorn,telefonbuch[i].vorname);
i++;
}
if (gefunden)
printf("\n%ld\n",telefonbuch[i-1].nummer);
/* . . . */
/* ----------------------------------------------------------------- */
/* */
/* Komponentenzugriff ueber Pointer-Ausdruck */
printf("\n%ld\n",(*(telefonbuch+i-1)).nummer);
printf("\n%ld\n",(telefonbuch+i-1)->nummer);
/* ----------------------------------------------------------------- */
Dynamische Datenstrukturen in C
Der Speicherplatz für einen Knoten wird erst bei Bedarf zur Programmlaufzeit allokiert --> dynamische Speicherallokation mittels spezieller Speicherallokationsfunktionen.
Da der Speicherort der einzelnen Knoten im voraus nicht bekannt ist
und auch für "benachbarte" Knoten weit auseinander liegen kann,
läßt sich eine ("Nachbarschafts"-) Beziehung nicht durch
den Speicherort abbilden.
Vielmehr muß die Beziehung der einzelnen Knoten untereinander über
Zeiger - die jeweils auf den Speicherort des "Nachbarn" zeigen
- hergestellt werden.
| --> | Jeder Knoten wird daher neben den jeweils zu speichernden Nutzdaten Zeiger auf die jeweiligen "Nachbar"-Knoten - also auf Elemente des gleichen Typs wie der des Knotens sebst enthalten |
| --> | Verkettete Datenstrukturen |
struct listelement
{ int inhalt;
struct listelement *next;
};
struct listelement
{ int inhalt;
struct listelement *next;
struct listelement *back;
};
struct baumelement
{ int inhalt;
struct baumelement *rechts;
struct baumelement *links;
};
| ---> | Typdefinition des Knotens : |
struct dknoten
{ double wert;
int anzahl;
struct dknoten *rechts;
struct dknoten *links;
};
Die Ausgabe erfolgt mittels einer rekursiven Funktion:
Beginnend mit dem Wurzelknoten wird zuerst der linke Unterbaum, dann
der Wurzelknoten und dann der rechte Unterbaum ausgegeben.
/* Programm SORTD */
/* Sortierte Ausgabe (aufsteigende Wertefolge) einer beliebigen
Anzahl in beliebiger Reihenfolge ueber stdin eingegebenen Folge
von double-Werten nach stdout.
Speicherung der Werte in einem Binär-Baum */
#include <stdio.h>
#include <stdlib.h>
struct dknoten /* Typ-Definition des Knotens */
{ double wert;
int anzahl;
struct dknoten *rechts;
struct dknoten *links;
};
void main(void)
{ struct dknoten *wurzel;
double d;
struct dknoten *einf_baum(struct dknoten *, double);
void baumaus(struct dknoten *);
wurzel=NULL;
printf("\nBitte Zahlen eingeben !\n");
while (scanf("%lf",&d)!=EOF)
wurzel=einf_baum(wurzel,d);
printf("\nSortierte Folge der eingegeben Zahlen :\n\n");
baumaus(wurzel);
}
struct dknoten *einf_baum(struct dknoten *pk, double a)
/* Einfügen Wert in Baum */ /* pk Zeiger auf akt. Knoten des Baums */
{ /* a neuer einzufuegender Wert */
if (pk==NULL) /* neuer Wert ist einzufuegen */
{ pk=malloc(sizeof(struct dknoten));
pk->wert = a;
pk->anzahl = 1;
pk->links = pk->rechts = NULL;
}
else if (a==pk->wert) /* Wert bereits vorhanden */
pk->anzahl++;
else if (a<pk->wert) /* Wert ist kleiner als Knotenwert */
pk->links = einf_baum(pk->links,a);
else /* Wert ist groesser als Knotenwert */
pk->rechts = einf_baum(pk->rechts,a);
return pk;
}
void baumaus(struct dknoten *pk) /* Rekursive Ausgabe der Baumwerte */
{ /* pk Zeiger auf akt. Knoten des Baums */
if (pk!=NULL)
{ baumaus(pk->links);
printf("%13.6lf",pk->wert);
if (pk->anzahl > 1)
printf(" (%2d)",pk->anzahl);
putchar('\n');
baumaus(pk->rechts);
}
} /* Ergebnis Probelauf s. umseitig */
Bildschirmausgabe :
C>sortd
Bitte Zahlen eingeben !
1.2 4.5 3.1 -4.5 6.3 1.2 -2.3 -2.4 -2.3
10.4 1.2 -8 -4.5 0 2.0 9.5 10.4 0
^Z
Sortierte Folge der eingegeben Zahlen :
-8.000000
-4.500000 ( 2)
-2.400000
-2.300000 ( 2)
0.000000 ( 2)
1.200000 ( 3)
2.000000
3.100000
4.500000
6.300000
9.500000
10.400000 ( 2)
9.4 Typedefinition mit "typedef"
/* --------------------------------------------------------------------* /
/* statt : */
struct datum /* Typdefinition */
{ int tag;
char monat[4];
int jahr;
};
struct datum geb_datum; /* Variablenvereinbarung */
/* kann man auch formulieren : */
typedef struct /* Typdefinition */
{ int tag;
char monat[4];
int jahr;
} datum; /* Typname ! */
datum geb_datum; /* Variablenvereinbarung */
/* -------------------------------------------------------------------- */
/* Die Anwendung von "typedef" ist nicht auf struct-/union-Typen
beschraenkt,
sondern ist fuer alle Datentypen zulaessig */
/* zum Beispiel : */
typedef char wort[80]; /* Typdefinition */
typedef float real; /* Typdefinition */
wort name; /* Variablenvereinbarung */
real a; /* Variablenvereinbarung */
/* -------------------------------------------------------------------- */
/* Auch die Definition von Pointertypen (auch Pointer auf Funktionen)
ist mit "typedef" moeglich */
/* zum Beispiel : */ /* oder : */
typedef struct /* typedef struct */
{ double re; /* { double re; */
double im; /* double im; */
} complex; /* } complex, *complptr; */
typedef complex *complptr; /* Definition des Typs "complptr" */
/* als Pointer auf complex */
typedef double (*pfd)(char *, /* Definition des Typs "pfd" als */
char *); /* Pointer auf eine Funktion, die */
/* einen double-Wert liefert und */
/* zwei char-Pointer-Parameter hat */
/*--------------------------------------------------------------------- */
9.5 Variante Strukturen ("unions")
| --> | Die Komponenten einer Union liegen nicht wie bei einer structure hintereinander - an verschiedenen Adressen - im Arbeisspeicher, sondern sie liegen "übereinander" im gleichen Speicherbereich, der am Unionanfang beginnt |
| --> | d.h. der Inhalt des gleichen Speicherbereichs wird je nach union- Komponente unterschiedlich interpretiert |
struct
{ int i;
float f;
char *pc;
} svar;
|
union
{ int i;
float f;
char *pc;
} uvar;
|
![]() |
![]() |
die gleichen Operationen wie bei Strukturen :
Komponentenangabe
union unionname dient als Datentypangabe.
/*---------------------------------------------------------------------*/
/* Programm FLOATBYTE */
/*---------------------------------------------------------------------*/
/* Ausgabe der internen Darstellung von float-Werten als sedezimale */
/* Bytefolge (MS-Byte zuerst) */
/*---------------------------------------------------------------------*/
#include <stdio.h>
union floathex
{ float f;
char b[sizeof(float)]; /* unabhaengig von interner Darstellung */
};
void main(void)
{ union floathex flvar;
float r;
int i;
printf("\nInterne Darstellung von FLOAT-Werten als Bytefolge");
printf(" (MS-Byte zuerst).\n");
printf("Bitte geben Sie Zahlenwerte ein :\n\n");
while (scanf("%f",&r)!=EOF)
{ flvar.f=r;
for (i=sizeof(float)-1; i>=0; i--)
printf("%02x ",flvar.b[i]&0xff);
printf("\n\n");
}
}
/*---------------------------------------------------------------------*/
Bildschirmausgabe :
E:\RT\C\VORL>floatbyte
Interne Darstellung von FLOAT-Werten als Bytefolge (MS-Byte zuerst).
Bitte geben Sie Zahlenwerte ein :
0
00 00 00 00
1.0
3f 80 00 00
-1.0
bf 80 00 00
2.6
40 26 66 66
2.6e-6
36 2e 7b a9
^Z
9.6 Bitfelder
In C lassen sich innerhalb von - implementierungsabhängigen -
Speicherobjekten (als "Wort" bezeichnet, meist int) einzelne Bits bzw.
Bitgruppen als kleinste über einen Namen ansprechbare Objekte definieren.
Diese Objekte werden Bitfelder genannt.
struct zeichen
{ unsigned wert : 9;
unsigned dummy : 2;
unsigned helligkeit : 2;
unsigned unterstr : 1;
unsigned blink : 1;
unsigned sichtbar : 1;
};
struct zeichen bildschirm[25][80];
Beispiel :
bildschirm[0][0].wert = 'A';
bildschirm[0][0].blink = 1;
| Speziell : | Namenloses Bitfeld der Länge 0 --> das folgende Bitfeld beginnt an einer neuen "Wort"-Grenze |
Die einzelnen Bitfelder werden zwar innerhalb eines "Worts" in der
Reihenfolge ihrer Auflistung angeordnet.
Es ist aber implementierungsabhängig, ob diese Anordnung beim
niederwertigsten Bit des "Worts" beginnt, im Wort also
aufsteigend ist, oder beim höchstwertigsten Bit des "Worts"
beginnt, im Wort also absteigend ist. (TURBO-C : bei niederwertigsten Bit beginnend)
z.B. :
struct datum
{ unsigned tag : 5;
unsigned monat : 4;
unsigned jahr : 7; /* Darstellung nur der letzten */
}; /* beiden Ziffern */
z.B. :
struct line_ctrl_reg /* Line Control Register der */
{ unsigned w_laenge : 2; /* ACE NS16450 */
unsigned stop_bits: 1;
unsigned parity : 3;
unsigned set_break: 1;
unsigned dlab : 1;
};
9.7 Vorwärtsdeklaration von Struktur-Typen
Diese Typ-Vorwärtsdeklaration besteht lediglich aus dem Wortsymbol
struct bzw. union und dem Struktur- bzw. Union-Name.
Die Auflistung der Komponenten fehlt.
Syntax :
Die Typ-Vorwärtsdeklaration ermöglicht die Definition von structure- bzw. union-Typen, die gegenseitig aufeinander bezugnehmen wobei wenigstens einer der Typnamen bereits für einen anderen structure- bzw. union-Typ in einem übergeordneten Block vereinbart ist.
Beispiel :
struct st2
{ int ba;
int lu;
};
void func(void)
{
struct st2; /* Typ-Vorwärtsdeklaration : setzt globale */
/* Vereinbarung von struct st2 außer Kraft */
struct st1
{ struct st2 *s2p; /* Pointer auf lokal definierte struct st2 */
int a;
} v1;
struct st2
{ struct st1 *s1p;
int b;
}v2;
/* ... */
(v1.s2p)->b=0; /* ohne Typ-Vorwärtsdeklaration falsch */
/* ... */
}
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |