-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcomplexdatatypen.tex
195 lines (186 loc) · 6.82 KB
/
complexdatatypen.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
\section{Komplexe Datentypen}
Je nach Problemstellung ist es manchmal sehr hilfreich, Datenstrukturen selbst erzeugen zu können.
C bietet dafür die Möglichkeit, und zwar auf verschiedene Weisen.
Zunächst kann man in C einen neuen Typ definieren, z.B. einen eigenen Typ für reelle Zahlen:
\begin{lstlisting}
typedef float real;
\end{lstlisting}
Dieser kann im Quelltext danach wie native C Typen verwendet werden
\begin{lstlisting}
typedef float real;
int main()
{
real x, y = 3.718;
return 0;
}
\end{lstlisting}
Auf den ersten Blick mag eine eigene Definition für eine Datentyp für reelle Zahle nicht besonders sinnvoll erscheinen.
Allerdings kann man mit dieser Typdefinition zu einem späteren Zeitpunkt sehr einfach die verwendete Genauigkeit für reelle Zahlen ändern.
Nämlich einfach, indem man die Zeile für die Typdefinition durch
\begin{lstlisting}
typedef double real;
\end{lstlisting}
ersetzt.
Eine weitere Art, einen neuen Datentyp zu definieren, ist zusammengesetzte Datentypen zu definieren.
Ein einfaches mathematisches Beispiel ist eine Datentyp für komplexe Zahlen.
Eine komplexe Zahl muss dabei eine Real- und Imaginärteil haben.
Genau für solche Fälle stellt C die Möglichkeit zur Verfügung, zusammengesetzte Datentypen zu definieren.
Das entsprechende Schlüsselwort ist \verb|struct|.
Solche zusammengesetzten Datentypen in C kann man sich wie Kontainer vorstellen.
Die allgemeine Benutzung von \verb|struct| ist wie folgt:
\begin{myalertblock}{Deklaration eines struct}
\begin{lstlisting}
struct name
{
Type1 name1;
Type2 name2;
Type3 name3;
...
};
\end{lstlisting}
\vspace{-0.4cm}
Die Verwendung als Typ für eine Variable:
\begin{lstlisting}
struct name variablename;
\end{lstlisting}
\vspace{-0.4cm}
\end{myalertblock}
Ein solcher Kontainer für komplexe Zahlen könnte also wie folgt aussehen:
\begin{lstlisting}
struct cmplx
{
double re, im;
};
\end{lstlisting}
mit zwei \verb|double| Elementen für den Real- und den Imaginärteil.
Der Zugriff auf die einzelnen Elemente erfolgt über den Namen des \verb|struct| und den Namen des Elements, getrennt durch einen Punkt.
In unserem Beispiel für komplexe Zahlen also
\begin{lstlisting}
#include <stdio.h>
struct cmplx
{
double re, im;
};
int main()
{
struct cmplx z;
z.re = 1.0;
z.im = 3.4;
printf("z hat Realteil %e und Imaginaerteil %e\n", z.re, z.im);
return (0);
}
\end{lstlisting}
Also, allgemein gesprochen greift man auf die Elemente über
\begin{lstlisting}
structname.elementname = wert;
\end{lstlisting}
zu.
Elemente können selbst wieder ein \verb|struct| sein.
Einen Unterschied gibt es beim Zugriff auf Elemente, wenn man einen Zeiger auf einen \verb|struct| benutzt.
Dort kann man direkt mit dem Pfeil Operator \verb|->| auf die Elemente zugreifen, also etwas
\begin{lstlisting}
#include <stdio.h>
struct cmplx
{
double re, im;
};
int main()
{
struct cmplx z;
struct cmplx *v = &z;
v->re = 1.0;
v->im = 3.4;
printf("z hat Realteil %e und Imaginaerteil %e\n", v->re, v->im);
return (0);
}
\end{lstlisting}
Elemente eines \verb|struct| können natürlich auch Zeiger sein.
Allerdings muss dann Speicher außerhalb der Deklaration des \texttt{struct} reserviert werden.
Die Verwendung von \verb|struct| ist sehr hilfreich, wenn beispielsweise eine Liste von logisch zusammengehörigen Daten bearbeiten soll, also etwa den Real- und Imaginärteil.
Es muss dann nicht mehr jedes Element einzeln übergeben werden, sondern nur noch einmal der Kontainer.
Der Zusammenhang der einzelnen Elemente bleibt damit erhalten.
Mit einem \verb|struct| wird ebenfalls einer neuer Typ bereitgestellt.
Der entsprechende Name enthält aber immer das Schlüsselwort \verb|struct|, in unserem Fall \verb|struct cmplx|.
Man kann einen \verb|struct| mit einem \verb|typedef| kombinieren, um nicht immer \verb|struct| schreiben zu müssen.
Im Quelltext sähe das so aus:
\begin{lstlisting}
struct cmplx
{
double re, im;
};
typedef struct cmplx cmplx;
\end{lstlisting}
Das sieht etwas verwirrend aus, weil \verb|cmplx| zweimal auftaucht.
Aber der Ausdruck wird klar, wenn man sich erinnert, dass \verb|struct cmplx| im Prinzip ein Typ ist.
Und damit ist der Name \verb|cmplx| noch nicht vergeben.
Die etwas gebräuchlichere Art obiger Typdefinition geht über die Möglichekeit eines anonymen \verb|struct|.
Man kann nämlich auch schreiben:
\begin{lstlisting}
typedef struct
{
double re, im;
} cmplx;
\end{lstlisting}
Damit ist natürlich \verb|struct cmplx| nicht definiert, aber man kann \verb|cmplx| wie einen C Typ verwenden.
\textbf{Bemerkung:} Die C Standardbibliothek stellt einen Typ für komplexe Zahlen zu Verfügung.
Ein Beispiel aus der C Standardbibliothek \verb|time.h| ist das \verb|struct tm| für Datum und Zeit:
\begin{lstlisting}
struct tm
{
int tm_sec; // Sekunde
int tm_min; // Minute
int tm_hour; // Stunde
int tm_mday; // Tag
int tm_mon; // Monat
int tm_year; // Jahr
int tm_wday; // Wochentag
int tm_yday; // Tag im Jahr
int tm_isdst; // Sommerzeit oder nicht
};
\end{lstlisting}
Damit kann über die Funktion
\begin{myexampleblock}{Funktion: \texttt{time}}
\begin{lstlisting}
time_t time(time_t *t)
\end{lstlisting}
\vspace{-0.4cm}
\verb|time| gibt die Zeit in Sekunden seit 1970-01-01 00:00:00 +0000 (UTC) zurück.
Falls \verb|t| nicht \verb|NULL| ist, wird das Ergebnis zusätzlich in \verb|t| gespeichert.
\verb|time_t| ist vom Typ \verb|long int|.
\end{myexampleblock}
und die Funktion
\begin{myexampleblock}{Funktion: \texttt{localtime}}
\begin{lstlisting}
struct tm *localtime(time_t *timer);
\end{lstlisting}
\vspace{-0.4cm}
Eingabeparameter: Zeiger auf eine Variable vom Typ \verb|time_t|.
Rückgabeparameter: Zeiger auf ein \verb|struct tm|.
\end{myexampleblock}
die beide in \verb|time.h| deklariert sind.
Folgendes Beispiel illustriert deren Benutzung:
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
time_t now;
struct tm *local_date_time;
char *tagarray[7] = {"Sonntag", "Montag", "Dienstag", "Mittwoch",
"Donnerstag", "Freitag", "Samstag"};
now = time(NULL);
local_date_time = localtime(&now);
printf("Jahr: %d\n", local_date_time->tm_year + 1900);
printf("Monat: %d\n", local_date_time->tm_mon);
printf("Tag: %s\n", tagarray[local_date_time->tm_wday]);
printf("Stunde: %d\n", local_date_time->tm_hour);
printf("Minute: %d\n", local_date_time->tm_min);
printf("Sekunde: %d\n", local_date_time->tm_sec);
return (0);
}
\end{lstlisting}
Es gibt das aktuelle Datum und die aktuelle Zeit aus.
Dabei wird zunächst die Funktion \verb|time| aufgerufen, und desse Rückgabewert dann an die Funktion \verb|localtime| übergeben.
In beiden Funktionsaufrufen mussten wir nicht mit den $9$ Elementen von \verb|struct tm| hantieren, sondern konnten bequem den Container übergeben.
Da \verb|local_date_time| ein Zeiger ist, greifen wir auf dessen Elemente wieder mit dem \verb|->| Operator zu.