![]() |
Algorithmen & Datenstrukturen
|
int n[5];
Diese 5 Variablen liegen dann im Speicher hintereinander:
| n[0] | n[1] | n[2] | n[3] | n[4] |
Wichtig: Die Angabe der Feldgröße muß in diesem Fall durch eine Konstante erfolgen, deshalb die Bezeichnung "statisch"! Die so deklarierten Felder beginnen immer mit Index [0] und enden mit Index [Feldgröße-1].
| Es erfolgt keine Überprüfung auf gültigen Speicherbereich von Seiten des Compilers! |
Ansonsten gilt für Feldelemente das gleiche, wie für sonstige Variable dieses Typs.
char msg[] = "Hello world!", format[] = "%s\n"; printf(format, msg);oder:
char msg[100]; /* sicherheitshalber ueberdimensioniert */
scanf("%s", msg); /* hier vor msg KEIN '&', weil
'msg' eine Adresse ist */

Analog lassen sich auch Felder mit mehr als zwei Dimensionen definieren. Angesprochen
werden die Komponenten über den Index:
x[8][30] =12.5;
Anstelle der Zahlen kann für den Index auch ein beliebiger Integer-Ausdruck
angegeben werden, z. B.:
x[i-1][j*k+2] =32.5;
Der Compiler kann aber selbstständig nur die Zahl der Zeilen feststellen; die notwendige (Mindest-)Zahl der Spalten muß ihm mitgeteilt werden.
Bemerkung: intern werden mehrdimensionale Felder (wenn statisch allociert!) eindimensional angelegt; wenn also z. B. ein Array deklariert ist mit int a[4][3];, dann sind die Aufrufe a[i][j], a[0][i*3+j] und a[k][(i-k)*3+j] völlig äquivalent:
| a[0][0] | a[0][1] | a[0][2] | a[1][0] | a[1][1] | a[1][2] | a[2][0] | ... |
| a[0][0] | a[0][1] | a[0][2] | a[0][3] | a[0][4] | a[0][5] | a[0][6] | ... |

Auch hier gibt es bei der Deklaration wieder die bequeme Möglichkeit der Initialisierung:
static int md[5][10] =
{
{0,1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9,10},
{2,3,4,5,6,7,8,9,10,11},
{3,4,5,6,7,8,9,10,11,12},
{4,5,6,7,8,9,10,11,12,13}
};
Die Angaben für die zweite (und weitere) Dimension werden also jeweils in eigene geschweifte Klammern geschrieben. Innerhalb der geschweiften Klammern gilt, daß eventuell nicht initialisierte Elemente auf null gesetzt werden. Die Kennzeichnung der Array-Struktur bei den Initialisierungswerten ist notwendig, wenn Elemente "innerhalb" des Arrays durch fehlende Angabe mit 0 initialisiert werden sollen. Beispiele:
int mat[3][4] = { { 1, 4, 3, -4 },
{ 2, 0, -3, 1 } };
Die letzte Zeile (mat[2][0] ... mat[2][3]) wird mit 0 initialisiert.
int mat[3][4] = { { 1, 4, 3 },
{ 2, 0, -3 },
{ 0, -5, 6 } };
Die letzte Spalte (mat[0][3], mat[1][3], mat[2][3]) wird mit 0
initialisiert. Die Kennzeichnung der Arraystruktur bei den Initialisierungswerten
ist notwendig.
Man kann die inneren geschweiften Klammern auch weglassen, dann wird der ganze Vektor Element für Element initialisiert.
int mat[3][4] = { 1, 4, 3, -4, 2, 0, -3, 1, 0, -5 };
oder:
int mat[ ][4] = { 1, 4, 3, -4, 2, 0, -3, 1, 0, -5 };
Die beiden letzten Elemente der letzten Zeile (mat[2][2], mat[2][3])
werden mit 0 initialisiert. Ein weiteres Beispiel:
/* Tag im Jahr aus Monat und Tag bestimmen */
day_of_year(int year, int month, int day)
{
static int day_tab[2][13] = {
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int i, leap;
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
for (i=1; i < month; i++)
day += day_tab[leap][i];
return (day);
}
Die folgende Funktion liefert den Namen eines Monats, falls der Parameter im Bereich 1..12 liegt und andernfalls einen Fehlertext
char *month_name(int n)
{
static char *name[] = { /* Vektor von Zeigern */
"*** gibt's nicht ***", /* String ist ein char- */
"Januar", /* Vektor und daher durch */
"Februar", /* seine Anfangsadresse */
"Maerz", /* charakterisiert */
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
};
return ((n < 1 || n > 12) ? name[0] : name[n]);
}
Beispiele für die Verwendung von zweidimensionalen Feldern, die als Felder von Feldern aufgefaßt werden können.
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
/* Deklaration eines Schachbretts von Zeichen */
char brett[8][8];
/* Belegen des Schachbretts */
for(i=0;i<8;i++)
{
for(j=0;j<8;j++)
{
brett[i][j]=' ';
}
}
for(i=0;i<8;i++) /* Bauern */
{
brett[1][i]='b';
brett[6][i]='B';
}
/* weitere Figuren */
brett[0][0]=brett[0][7]='t';
brett[0][1]=brett[0][6]='s';
brett[0][2]=brett[0][5]='l';
brett[0][3]='d';
brett[0][4]='k';
brett[7][0]=brett[7][7]='T';
brett[7][1]=brett[7][6]='S';
brett[7][2]=brett[7][5]='L';
brett[7][3]='D';
brett[7][4]='K';
/* Ausgabe */
printf(" A B C D E F G H\n");
for(i=7;i>=0;i=i-1)
{
printf(" +-+-+-+-+-+-+-+-+\n%d|",i+1);
for(j=0;j<=7;j=j+1)
{
printf("%c|",brett[i][j]);
}
printf("%d\n",i+1);
}
printf(" +-+-+-+-+-+-+-+-+\n A B C D E F G H\n");
}
Demonstrationsprogramm zu 2-dimensionalen Arrays: Matrixaddition
#include <stdio.h>
#define ZEILEN 3
#define SPALTEN 4
void mat_add (float mat1[ZEILEN][SPALTEN],
float mat2[ZEILEN][SPALTEN],
float erg[ZEILEN][SPALTEN])
{
int i, j;
for (i=0; i<ZEILEN; i++)
for (j=0; j<SPALTEN; j++)
erg[i][j]=mat1[i][j] + mat2[i][j];
}
void mat_aus(float fmat[ZEILEN][SPALTEN])
{
int i,j;
for(i=0; i<ZEILEN; i++)
{
printf("\n{");
for (j=0; j<SPALTEN; j++)
printf("%6.2f", fmat[i][j]);
printf("}");
}
printf("\n");
}
int main(void)
{
float fmat1[ZEILEN][SPALTEN] = {
{ 1.5, 4.1, 3.4, -4.0 },
{ 2.2, 0, -3.7, 1.1 },
{ 0, -5.1, 6.6, 0.2 }
};
float fmat2[ZEILEN][SPALTEN] =
{-1.5, 2.3, 3.7, 2.1, 0.7,5.5, 3.3,0,0,0,0,0};
float fmat3[ZEILEN][SPALTEN];
mat_add(fmat1, fmat2, fmat3);
mat_aus(fmat3);
return 0;
}
Ein matadd-Probelauf ergibt:
{ 0.00, 6.40, 7.10, -1.90, }
{ 2.90, 5.50, -0.40, 1.10, }
{ 0.00, -5.10, 6.60, 0.20, }
char s[] = {'s','t','r','i','n','g','\0'};
char s[] = "string"; /* \0 intern angehaengt */
char s[10] = "string"; /* restl. Elemente mit 0 init. */
Im Übrigen werden Zeichenketten wie normale Vektoren behandelt.
Insbesondere ist der Zugriff auf Vektorelemente gleich:
s[0] hat den Wert 's' s[3] hat den Wert 'i'Wichtig:Die Zeichenkettenkonstante "x" unterscheidet sich signifikant von der Zeichenkonstante 'x'. Letztere ist ein einzelnes Zeichen, das man z. B. durch char ch = 'x'; definieren könnte. Bei "x" handelt es sich um ein Zeichenkettenarray, das aus zwei Zeichen besteht ('x' und '\0').
char-Arrays können auch mit einer String-Konstanten initialisiert werden. Statt
char s[] = {'s','t','r','i','n','g','\0'};
kann man auch schreiben
char s[] = "string";
In C gibt es keine speziellen Sprachelemente für die Manipulation von Zeichenketten. Es existieren jedoch in der C-Standardbibliothek eine Reihe von Funktionen zur Stringbearbeitung.
Die entsprechenden Funktions-Deklarationen befinden sich in der Standard-Header-Datei <string.h>. Diese ist daher bei Verwendung der Funktionen mittels #include <string.h> einzubinden. In <string.h> ist auch der von einigen Funktionen verwendete Datentyp size_t definiert. Dies ist der "natürliche" unsigned-Typ, d. h. der Typ, den der sizeof-Operator liefert.
Da in C Array-Grenzen-Überschreitungen nicht überprüft werden, liegt es am Programmierer einen Array-Überlauf zu verhindern. Im ANSI-Standard ist lediglich festgelegt, daß das Überschreiten von Array-Grenzen zu einem undefinierten Verhalten führt. Alle Kopierfunktionen (siehe unten) setzen voraus, daß sich der Quell- und der Zielbereich nicht überlappen. Im Falle der Überlappung ist das Verhalten undefiniert.
| islower(c) | Kleinbuchstabe |
| isupper(c) | Großbuchstabe |
| isalpha(c) | Klein- oder Großbuchstabe |
| isdigit(c) | Dezimalzahl |
| isalnum(c) | Klein- oder Großbuchstabe oder Dezimalzahl |
| iscntrl(c) | Control-Zeichen |
| isgraph(c) | druckbares Zeichen außer space |
| isprint(c) | druckbares Zeichen mit space |
| ispunct(c) | druckbares Zeichen außer space, Buchstabe und Ziffern |
| isspace(c) | space, formfeed, newline, carriage return, tab, vertical tab |
| isxdigit(c) | Hexadezimalzahl |
Achtung: Die deutschen Umlaute und das "ß" sind keine Buchstaben im obigen Sinne!!
In diesem Headerfile sind außerdem noch zwei Funktionen zur Konvertierung von Buchstaben enthalten:
| int tolower(int c) | konvertiert c zu Kleinbuchstaben |
| int toupper(int c) | konvertiert c zu Großbuchstaben |
Das folgende Programm gibt für eine eingegebene ganze Zahl zwischen 0 und 999 das entsprechende Zahlwort aus (z. B. für den automatischen Druck von Schecks). Es demonstriert die Initialisierung und Anwendung von Character-Arrays (Zeichenketten). Die Denkarbeit bei der Konzeption besteht darin, daß man bei der Einerstelle nicht bei "9" aufhören darf, sondern man die Werte von 0 bis 19 verwenden muß.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
/****************** Zahlwörtertabellen ***************************/
char low [20][10] = { "null", "eins", "zwei", "drei", "vier", "fünf",
"sechs", "sieben", "acht", "neun", "zehn",
"elf", "zwölf", "dreizehn", "vierzehn",
"fünfzehn", "sechzehn", "siebzehn",
"achtzehn", "neunzehn" };
char ten [10][10] = { "", "", "zwanzig", "dreißig", "vierzig",
"fünfzig", "sechzig", "siebzig",
"achtzig", "neunzig" };
char hun [10][15] = { "", "einhundert", "zweihundert", "dreihundert",
"vierhundert", "fünfhundert", "sechshundert",
"siebenhundert", "achthundert", "neunhundert" };
/*****************************************************************/
int num;
printf("\nZahl: ");
scanf("%d",&num);
if (num < 20) /* 0 <= num < 20 */
printf("%s\n", low[num]);
else if (num < 100) /* 20 <= num < 100 */
{
if ((num % 10) == 0) /* Keine Einer */
printf("%s\n", ten[num/10]);
else if ((num % 10) == 1) /* Einerziffer 1 */
printf("einund%s\n", ten[num/10]);
else /* andere Einer */
printf("%sund%s\n", low[num%10], ten[num/10]);
}
else /* 100 <= num < 1000 */
{
if ((num%100) == 0) /* keine 1er, keine 10er */
printf("%s\n", hun[num/100]);
else if ((num%100) < 20) /* H01 bis H19 */
printf("%s%s\n", hun[num/100], low[num%100]);
else if ((num%10) == 0) /* letzte Ziffer 0 */
printf("%s%s\n", hun[num/100], ten[(num%100)/10]);
else if ((num%10) == 1) /* letzte Ziffer 1 */
printf("%seinund%s\n", hun[num/100], ten[(num%100)/10]);
else /* sonst. Zahlen */
printf("%s%sund%s\n", hun[num/100], low[num%10], ten[(num%100)/10]);
}
return(0);
}
Für die Bearbeitung von Zeichenketten gibt es eine umfangreiche Funktionsbibliothek:
char *s, *t; const char *cs, *ct; int c; size_t n;
| char *strcpy(s,ct) | kopiert ct zu s inklusive dem abschließenden '\0'. Zurückgegeben wird s. |
| char *strncpy(s,ct,n) | kopiert höchstens n Zeichen des Strings ct zu s und liefert s zurück, wobei wie üblich mit '\0' terminiert wird. |
| char *strcat(s,ct) | hängt ct an s an und liefert s zurück. |
| char *strncat(s,ct,n) | hängt höchstens n Zeichen von ct an s an, terminiert mit '\0' und liefert s zurück. |
| int strcmp(cs,ct) | vergleicht cs und ct. Der Rückgabewert ist 0, wenn die beiden Strings identisch sind, negativ, wenn cs<ct ist und positiv, wenn cs>ct ist, wobei das < und > im lexikalischen Sinne verstanden wird, d.h. es wird Buchstabe für Buchstabe verglichen und geprüft, welcher als erster im Alphabet steht, wobei wiederum Großbuchstaben vor Kleinbuchstaben kommen (ASCII-Code). |
| int strncmp(cs,ct,n) | Im wesentlichen dasselbe wie zuvor, nur daß diesmal höchstens n Zeichen verglichen werden. |
| char *strchr(cs,c) | liefert Pointer zum ersten Auftreten von c in cs zurück, oder NULL wenn c nicht auftritt. |
| char *strrchr(cs,c) | liefert Pointer zum letzten Auftreten von c in cs zurück, oder NULL wenn c nicht auftritt. |
| char *strstr(cs,ct) | liefert Pointer zum ersten Auftreten des Strings ct in cs zurück oder NULL wenn ct nicht auftritt. |
| size_t strlen(cs) | liefert die Länge des Strings cs zurück. |
Beispiel für Strungbearbeitung: Stringposition ermitteln
Alle Operationen werden ohne Bibliotheksfunktionen und ohne Pointer ausgeführt.
#include <stdio.h>
#include <stdlib.h>
main()
{
int i, c;
char object[120], target[120], /* Suchstring, Quellstring */
hilf[120]; /* Hilfs-String fuer Tausch */
int opos, tpos, ergpos, /* Positionen */
olen, tlen, hlen; /* Laengen */
do
{
printf("Erster String : ");
i = 0; /* String zeichenweise lesen */
while ((c = getchar()) != EOF && c != '\n')
object[i++] = c;
object[i] = '\0'; olen = i; /* Laenge gleich speichern */
if (c != EOF) /* Ctrl-D eingegeben ? */
{
printf("Zweiter String: ");
i = 0;
while ((c = getchar()) != EOF && c != '\n')
target[i++] = c;
target[i] = '\0'; tlen = i;
/* Suche immer den kuerzeren im laengeren String */
if (strlen(object) > strlen(target))
/* dann beide Strings vertauschen */
{
i = 0; /* object --> hilf */
while ((hilf[i]=object[i]) != '\0') i++;
i = 0; /* target --> object */
while ((object[i]=target[i]) != '\0') i++;
i = 0; /* hilf --> target */
while ((target[i]=hilf[i]) != '\0') i++;
hlen = olen; olen = tlen; /* und die Laengen auch */
tlen = hlen;
}
/* Beginn der Suche */
tpos = 0;
while (tlen != 0 && tpos < (tlen-olen+1))
{ /* Solange objet in target Platz hat */
opos = 0;
while ((target[tpos+opos] == object[opos]) && (opos < olen))
opos++; /* vergleichen ab tpos */
if (opos >= olen) /* object in target - fertig */
{
ergpos = tpos; /* Position merken */
tpos = tlen; /* sorgt fuer Abbruch */
}
else
ergpos = -1; /* nicht gefunden */
tpos++; /* sonst naechste Pos. */
}/*endwhile (tlen != 0...) */
/* Ausgabe */
if (ergpos < 0)
printf("Kein String ist Teilstring des anderen!");
else
printf("'%s' in '%s' ab Pos. %d enthalten",
object,target,ergpos);
}/* endif (c != EOF) */
printf("\n");
}
while (c != EOF); /* bis Ctrl-D eingegeben */
}
Dieses Programm ist sehr ""ausführlich" programmiert. Das folgende Programm ist nicht nur etwas kürzer, sondern es werden vor allem zwei sinnvolle Funktionen definiert. Das Programm sucht alle Zeilen aus der Standardeingabe, die ein bestimmtes Suchmuster enthalten.
# include <stdio.h>
# define MAXLINE 1000 /* Maximale Zeilenlaenge */
int main(void)
{
char line[MAXLINE];
while (getline(line,MAXLINE) > 0)
if (index(line,"the") > 0)
printf("%s",line);
return 0;
}
int getline(char s[], int lim)
/* Eingabezeile mit max. lom Zeichen in s ablegen, Laenge liefern */
{
int c,i;
i = 0;
while (--lim>0 && (c=getchar()) != EOF && c!='\n')
s[i++] = c;
if (c == '\n') s[i++] = c;
s[i] = '\0';
return(i);
}
index (char s[],char t[])
/* Position von t in s liefern, -1 falls nicht da */
{
int i, j, k;
for (i = 0; s[i] != '\0'; i++)
{
for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++,k++)
;
if (t[k] == '\0') return(i);
}
return(-1);
}
Zum Abschluß noch ein völlig unnützes Programm: Es gibt auf die Eingabe eines Geburtstages das entsprechende Tierkreiszeichen aus.
#include <stdio.h>
int main(void)
{
/* Sternzeichentabelle: */
char zodiac [12] [11] = { "STEINBOCK" , "WASSERMANN", "FISCHE",
"WIDDER", "STIER", "ZWILLINGE",
"KREBS", "LÖWE", "JUNGFRAU",
"WAAGE", "SKORPION", "SCHÜTZE"};
/* Tabelle der Tage, die bis zu einem bestimmten Monat vergangen sind: */
int monsum [13] = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
/* Nummer des Anfangstages (im Intervall 1 - 365) eines Sternzeichens: */
int start [12] = { 1, 31, 62, 90, 121, 151, 183, 214, 246, 277, 307, 337 };
/* Nummer des Endtages (im Intervall 1 - 365) eines Sternzeichens
(hier nicht noetig): */
int end [12] = { 30, 61, 89, 120, 150, 182, 213, 245, 276, 306, 336, 365 };
int i;
int day; /* Geburtstag */
int mon; /* Geburtsmonat*/
int tnum; /* Geburtstag und Geburtsmonat umgerechnet in den
entsprechenden Tag des Tierkreisjahres (1-365) */
printf("\n\nTag und Monat des Geburtsdatums eingeben (tt mm): ");
scanf("%d %d", &day, &mon);
/* Datum umrechnen in Anzahl der vergangenen Tage seit dem 22.12.,
dem ersten Zeichen im Turnus (STEINBOCK) */
if (monsum[mon] + day + 10 > 365)
tnum = monsum[mon] + day + 10 - 365;
else
tnum = monsum[mon] + day + 10;
i = 0;
while (tnum > start[i]) i++;
printf("%d %d Ihr Sternzeichen ist %s\n", tnum,i-1,zodiac[i-1]);
return(0);
}
Strukturen werden mit Hilfe des Schlüsselwortes struct vereinbart
struct Strukturname { Komponente(n) } Strukturvariable(n) Init. ;
Strukturname ist optional und kann nach seiner Definition
für die Form (den Datentyp) dieser speziellen Struktur verwendet
werden, d. h. als Abkürzung für die Angaben in den geschweiften
Klammern. Strukturkomponenten werden wie normale Variable
vereinbart. Struktur- und Komponentennamen können
mit anderen Variablennamen identisch sein ohne daß Konflikte
auftreten, da sie vom Compiler in einer separaten Tabelle geführt
werden.
Der Aufruf der einzelnen Elemente erfolgt dann nicht über Indizes, sondern über deren Namen. Beispiel (für Definition/Deklaration):
struct datum
{ int tag;
int monat;
int jahr;
char mon_name[4];
};
| Legt nur die Form der Struktur datum fest |
struct datum {
int tag;
int monat;
int jahr;
char mon_name[4];
} geb_dat, heute;
| Erzeugt zusätzlich die Strukturvariablen geb_dat und heute |
struct point
{ double spx, spy;
int farbe;
char label;
} spot1;
| point ist der Strukturname, spx, spy, etc. sind Elementnamen und spot1 ist die deklarierte Variable |
struct point punkt1, punkt2; | ebenfalls so deklarierte Variablen |
Durch die Angabe einer (oder mehrerer) Strukturvariablen wird diese Struktur erzeugt (d. h. Speicherplatz dafür bereitgestellt). Strukturvereinbarungen ohne Angabe einer Strukturvariablen legen nur die Form (den Prototyp) der Struktur fest.
Die geschlossene Initialisierung erfolgt (analog zu den Arrays) bei der Deklaration. Zum Beispiel:
struct datum heute = {26,9,1987,"jun"};
struct point spot2 = {2.8, -33.7, 15, 'A'};
Für den Elementzugriff gibt es zwei eigene Operatoren. Der direkte Zugriff wird dabei mit dem Punktoperator . nach folgendem Schema durchgeführt (Der Operator -> wird bei den Pointern besprochen):
Strukturvariable . KomponenteBeispiel:
punkt1.farbe = 11; punkt2.spy = spot2.spy; heute.tag = 22; heute.monat = 1; heute.jahr = 2000;
Strukturvariable können an Funktionen übergeben werden, und Funktionen können Strukturen als Rückgabetyp haben. Beispiel (mit obiger Definition):
/* createpoint : bepackt Struktur 'point' */
struct point createpoint(double x, double y, int farbe, char label)
{
struct point dummy;
dummy.spx = x;
dummy.spy = y;
dummy.farbe = farbe; /* gleiche Bezeichnungen */
dummy.label = label; /* interferieren NICHT */
return dummy;
}
Strukturen können als Elemente ebenfalls wieder Strukturen enthalten (allerdings nicht sich selbst) und Strukturen können zu Vektoren zusammengefaßt werden:
struct kunde
{
char name[NAMLAE];
char adresse[ADRLAE];
int kund_nr;
struct datum liefer_dat;
struct datum rech_dat;
struct datum bez_dat;
};
struct kunde kunde1, kunde2, ... ;
struct kunde kunden[KUNANZ];
Programmbeispiel: Komplexe Arithmetik (typedef siehe unten)
#include <stdio.h>
struct complex
{
double r;
double i;
};
typedef struct complex cpx;
cpx makecpx(double, double);
cpx sum(cpx, cpx);
cpx product(cpx, cpx);
cpx power(cpx, int);
void compprint(cpx);
int main(void)
/* Berechnet Potenzen komplexer Zahlen */
{
int k;
cpx basis, result;
basis = makecpx(1,1);
for (k=0; k < 10; ++k)
{
result = power(basis, k);
printf("%2d ", k);
compprint(result);
}
return(0);
}
cpx makecpx(double r, double i)
/* Komplexe Zahl erzeugen */
{
cpx tmp;
tmp.r = r; tmp.i = i;
return (tmp);
}
cpx sum(cpx a, cpx b)
/* Summe zweier komplexer Zahlen */
{
a.r += b.r;
a.i += b.i;
return (a);
}
cpx product(cpx x, cpx y)
/* Produkt zweier komplexer Zahlen */
{
cpx u;
u.r = x.r * y.r - x.i * y.i;
u.i = x.r * y.i + x.i * y.r;
return (u);
}
cpx power( cpx basis, int expo)
/* Potenz einer komplexen Zahl */
{
cpx u = {1, 0};
while (expo > 0)
{
if (expo % 2)
{
expo--;
u = product(basis, u);
}
else
{
expo = expo/2;
basis = product(basis, basis);
}
}
return(u);
}
void compprint(cpx z)
/* Druckt eine komplexe Zahl */
{
if ((z.r != 0) && (z.i != 0))
printf("%5.2f + %5.2f * i\n", z.r, z.i);
else if ((z.r == 0) && (z.i != 0))
printf("%13.2f * i\n", z.i);
else if ((z.r != 0) && (z.i == 0))
printf("%5.2f\n", z.r);
else printf("0\n");
}
union utype
{ int n;
double d; } irgendwas;
irgendwas.n = 3;
irgendwas.d = 11.7;
zahl = irgendwas.n; /* in diesem Fall: Fehler! */
Allerdings ist Buchführung notwendig, um zu wissen, welcher Datentyp
in welcher union-Variablen zuletzt abgespeichert wurde (deshalb
der obige Fehler). Beispiel:
if (utype == INT)
printf("%d\n",uval.ival);
else if (utype == FLOAT)
printf("%f\n",uval.fval);
else if (utype == STRING)
printf("%s\n",uval.pval);
else
printf("bad type %d in utype\n",utype);
Ein Beispiel für Struktur- und Variantenvereinbarung ist die Definition der Strukturen WORDREGS und BYTEREGS sowie der Varianten REGS für MS-DOS Funktionsaufrufe:
struct WORDREGS {
unsigned int ax;
unsigned int bx;
unsigned int cx;
unsigned int dx;
unsigned int si;
unsigned int di;
unsigned int cflag;
};
struct BYTEREGS {
unsigned char al,ah;
unsigned char bl,bh;
unsigned char cl,ch;
unsigned char dl,dh;
};
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
union REGS inregs,outregs;
inregs.x.bx = 0x12; /* BX Register auf Hex 12 stellen */
inregs.h.ah = 0x10 /* AH Register auf Hex 10 stellen */
c = outregs.x.cx /* CX Register nach c kopieren */
typedef int t_count; typedef unsigned long int bigint;Auch wenn der neue Typ t_count dem int entspricht, kann er Programme "fehlerbewusster" machen. Definiert man eine Funktion mit einem Parameter von Typ t_count wird - bei entsprechend strikter Compilereinstellung bereits bei der Übersetzung ein Fehler gemeldet, wenn ein int-Parameter übergeben wird.
Durch typedef wird aber auch der Aufbau von Structures verschleiert, so daß viele Programmierer keinen Gebrauch davon machen. Zur Syntax: Der neue Typname steht an genau der Stelle, an der ohne typedef der Variablenname stünde. Beispiele:
typedef struct Verbund
{
int i;
float f;
double df;
} collect;
collect ist hier also keine Strukturvariable, sondern der so neu
definierte Typname!
<type> <variable_name> : <bit_zahl> ;Wird kein Variablenname angegeben, sind diese bits unbenutzt. (Dies kann sinnvoll sein beim Nachbilden von bestimmten Registern.) Ist die angegebene Bitzahl 0, so werden evtl. weitere Bitfelder an den Anfang der Adresse des nächsten Maschinenwortes geschrieben. Der Platzbedarf solcher Strukturen ist das nächste ganzzahlige Vielfache eines solchen Maschinenwortes, dessen Größe man demzufolge auch bestimmen kann mit:
sizeof(struct { int : 0; })
Bitfelder sind von Bedeutung, wenn Speicherplatz gespart werden soll. Allerdings gelten gegenüber "normalen" Strukturelementen die folgenden Einschränkungen:
typedef struct
{ unsigned b1 : 1;
unsigned b2 : 1;
unsigned b3 : 1;
unsigned b4 : 1;
int : 6;
int farbe : 4;
igned flags : 2;
} bitpack;
Die bit-Belegung im Speicher sieht dann etwa folgendermaßen aus:
Wesentliche Eigenschaften von Bitfeldern sind implementationsabhängig;:
Zum Inhaltsverzeichnis |
Zum nächsten Abschnitt |