forked from pittlerf/ckurs2017
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvariablen.tex
384 lines (345 loc) · 18.5 KB
/
variablen.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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
\section{Variablen und Operatoren}
Wie schon angedeutet werden Daten in \texttt{C} in Variablen bzw. Objekten gespeichert.
Variablen haben immer einen Typ, einen Namen und einen Wert.
Namen und Typen von Variablen müssen dem Compiler bekannt gemacht werden.
\subsection{Deklaration und Initialisierung}
Dies geschieht bei der Deklaration von Variablen, die allgemein wie folgt aussieht\index{Variable!Deklaration}
\begin{lstlisting}
Datatype name;
\end{lstlisting}
Die Variable muss dann noch initialisiert werden
\begin{lstlisting}
name = value;
\end{lstlisting}
Es geht auch beides zusammen in einem Schritt
\begin{lstlisting}
Datatype name = value;
\end{lstlisting}
Dies wird am folgenden Beispiel an Hand des ganzzahligen Datentyps \texttt{int} veranschaulicht\index{Datentypen!\texttt{ind}}
\begin{lstlisting}[caption={Erste Variablendeklaration und -zuweisung}, belowcaptionskip=0.3em]
#include <stido.h>
int main()
{
int n;
n = 4;
printf("Wir werden n=%d zahlen sortieren\n", n);
return(0);
}
\end{lstlisting}
Im Einzelnen geschieht das folgende:
\begin{itemize}
\item \texttt{int n;}\\
Diese Anweisung stellt eine Deklaration dar.\index{Variable!Deklaration}\index{Variable!Definition}
Sie teilt dem Compiler mit, ab jetzt den ent\-sprechenden Speicherplatz für eine ganze Zahl vom Typ \texttt{int} bereitzustellen, also $4$ byte.
Außerdem kennt der Compiler ab jetzt den Namen \texttt{n} innerhalb des Blockes, der durch \texttt{\{\}} begrenzt wird.
Streng genommen findet gleichzeitig auch eine Definition statt, weil der ent\-sprechende Speicherplatz reserviert wird.
Man unterscheidet Deklaration und Definition, weil es auch Deklarationen ohne Bereitstellung von Speicher gibt.
In einem solchen Fall wird nur das Objekt bekannt gemacht.
\item \verb|n = 4;|\\
Diese Anweisung stellt eine Zuweisung dar.
Der Variable \texttt{n} wird der Wert $4$ zugewiesen.
An der entsprechenden Stelle im Speicher wird dieser Wert abgelegt.
Bei einer Definition führt \texttt{C} keine Initialisierung durch.\index{Variable!Initialisierung}
Nach der reinen Definition (ohne Zuweisung) ist der Wert der Variablen \texttt{n} also rein zufällig.
\item \verb|printf(...)|\\
Diese Anweisung gibt den Text auf der Konsole aus und ersetzt den Platzhalter \verb|%d| mit dem Wert der Variablen \verb|n|, mehr Details dazu in Abschnitten \ref{subsec:Operatoren} und \ref{subsec:FormattedInAndOutput}.
\end{itemize}
Es ist wichtig, dass jeder Benutzung einer Variablen, beispielsweise in einer Zuweisung, die Deklaration der Variablen vorangehen muss.
\subsection{Sichtbarkeitsbereich}
Wie oben ersichtlich, haben Variablen einen Sichtbarkeitsbereicht und damit auch eine Lebensdauer.
Innerhalb eines Blockes kann man nicht zwei Variablen mit gleichem Namen deklarieren, dies führt zu einer Fehlermeldung des Compilers.
Deklariert man in einem Unterblock eine Variable mit dem Namen einer Variablen aus dem darüberliegenden Block, so ist die Variable aus dem darüberliegenden Block verdeckt.
In unserem Beispiel von oben heißt das, dass die Variable \texttt{n} nur in der Funktion \texttt{main} sichtbar ist.Variablen, die außerhalb aller Funktionen deklariert werden, bezeichnet man als \emph{global}.
Sie sind in allen Funktionen, die nach der globalen Deklaration definiert werden, sichtbar und zugreifbar.
\subsection{Datentypen und Wertebereiche}
Variablen haben immer einen Datentyp und einen Wert.
Der Datentyp entscheidet, welche Werte eine Variable annehmen kann und wie viel Arbeitsspeicher dafür reserviert wird.\index{Datentypen}
In der Tabelle~\ref{tab:PPer} sind die elementaren \texttt{C}-Datentypen mit ihren Wertebereichen beispielhaft aufgelistet, wobei man bedenken muss, dass sowohl die Wertebereiche, als auch die Größe in Byte architekturabhängig sein können.
Ferner gibt es für ganzzahlige Datentypen auch \verb|long long| Varianten, welche meist eine Größe von $8$ Byte aufweisen und entsprechend große Wertebereiche haben.
\begin{table}[t]
\caption{Elementare Datentypen\label{tabelle1}} % title name of the table
\centering
% centering table
\begin{tabular}{|l c c rrr|}
% creating 10 columns
\hline
Name & & Varianten & Größe in Byte & Minimaler Wert & Maximaler Wert
% inserting double-line Audio &Audibility & Decision & \multicolumn{7}{c}{Sum of Extracted Bits}
\\[0.5ex]
\hline % inserts single-line % Entering 1 st row
& & int &4 & $-2\,147\,483\,648$ & $2\,147\,483\,647$ \\[-0.0ex]
& & short & 2 & $-32\,768$ & $32\,767$ \\[-0.0ex]
\raisebox{1ex}{int} & & unsigned short& 2 & $0$ & $65\,535$ \\[-0.0ex]
& &unsigned& 4 & $0$ & $ +4\,294\,967\,295$ \\[1ex]
& &long& 4 & $-2\,147\,483\,648$ & $2\,147\,483\,647$ \\
\hline
% Entering 2nd row
& &signed & 1 & $-128$ & $127$ \\[-1ex]
\raisebox{1.5ex}{char} & & unsigned &1 & $0$ & $255$ \\[1ex]
\hline
% Entering 3rd row
float & & & 4 & & \\
double& & & 8 & & \\
long double& & &8 & & \\[1ex]
% [1ex] adds vertical space
\hline % inserts single-line
\end{tabular}
\label{tab:PPer}
\end{table}
\texttt{C} Compiler führen im Prinzip eine strenge Typenkontrolle durch.
Das ist eine sehr nützliche Eigenschaft von Compilern, wenn es auch manchmal etwas mühsam ist.
Man kann dies durch einen expliziten \emph{cast} umgehen.\index{cast}\index{cast!explizit}
Beispielsweise wandelt \texttt{int n=4; double x = (double)n;} die ganze Zahl \texttt{n} in eine Fließkommazahl mit \texttt{double} Genauigkeit um.
Dafür sollte man aber sehr genau wissen, was man tut.
Leider ist die Typenkontrolle vom Compiler abhängig und meist wird bei einer Zuweisung ein impliziter \emph{cast} durchgeführt, wenn nötig und möglich.\index{cast!implicit}
Beispielsweise wird folgender Code ohne Beanstandung übersetzt
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={Ungenaue Variablenzuweisung, implicit cast}, belowcaptionskip=0.3em]
#include <stido.h>
int main()
{
// so etwas sollte man nicht schreiben!
int n = 4.5; // implicit cast
return 0;
}
\end{lstlisting}
\end{minipage}
obwohl hier implizit die reelle Zahl $4.5$ durch Abschneiden in eine ganze Zahl umgewandelt wird.
\texttt{n} hat den Wert $4$.
Im allgemeinen sollte man versuchen immer den Datentyp zu verwenden, der der Nutzung der entsprechenden Variablen am ehesten entspricht.
Wenn man also beispielsweise weiß, dass eine ganze Zahl nicht negativ werden kann, sollte man \texttt{unsigend int} verwenden.
Dies macht es möglich, dass der Compiler Fehler in der Nutzung von Datentypen finden kann.
\subsection{\texttt{C} Schlüsselwörter und Regeln für Namen}
\index{Schlüsselwörter}
Es gibt einige Regeln für die Namen von Objekten in \texttt{C}.
Schlüsselworte der Sprache \texttt{C}, wie z.B. \texttt{main} dürfen nicht verwendet werden.
Auch dürfen die Namen nicht mit einer Zahl beginnen, auch wenn Zahlen generell erlaubt sind.
Operatornamen, wie z.B. \verb|+|, \verb|-| oder \verb|=|, dürfen ebenfalls nicht verwendet werden.
Es ist ratsam, Variablen mit sinnvollen Namen zu versehen.
Das macht den Quelltext lesbarer und erhöht die Verständlichkeit des Programms.
Für den Algorithmus Einfügesortieren könnte man beispielsweise die beiden Liste mit \texttt{sortiert} und \texttt{unsortiert} benennen.
Im folgenden Quelltext sind einige Beispiele für richtige und falsche Variablendeklarationen zu finden:
\begin{lstlisting}
int main()
{
int m1 = 4, n1 = 5, l1 = 6; // Richtig
int m2 = 4, char n2 = 'a', float m2 = 4.; // Falsch
char m3 = 'a'; // Richtig
double n3 = 18.9; // Richtig
float 4m = 1.; // Falsch
return (0);
}
\end{lstlisting}
Wie man sieht, kann man mehrere Variable in einer Anweisung deklarieren, definieren und initialisieren, wenn sie den gleichen Typ haben.
Die Variablen werden dabei durch ein Komma getrennt.
Wie schon oben erwähnt, können mehrere Anweisung in der gleichen Zeile stehen, solange sie mit dem Semikolon abgeschlossen werden.
Bei folgenden Wörtern handelt es sich um C99 Schlüsselworte:\index{Schlüsselwörter}\\
\textbf{auto, double, int, struct, break, else, long, switch, case,
enum, register, typedef, char, extern, inline, return, union, const, float,
short, unsigned, continue, for, restrict, signed, void, default, got, sizeof,
volatile, do, if, static, while, \_Bool, \_Complex, \_Imaginary}
Der überwiegende Teil dieser Schlüsselwörter wird in diesem Dokument vorgestellt.
\subsection{Konstanten}
\texttt{C} erlaubt auch, Variablen als konstant zu deklarieren.
Dies bedeutet, dass sich der einmal zugewiesene Wert einer solchen als \verb|const| deklarierten Variablen nicht ändern darf.\index{\texttt{const}}
Im Quelltext sieht das wie folgt aus
\begin{lstlisting}
const int n = 5;
\end{lstlisting}
Notwedigerweise müssen als \verb|const| deklarierte Variablen immer initialisiert werden, denn eine spätere Zuweisung ist nicht erlaubt.
Die Benutzung von \verb|const| kann große Vorteile haben.
Erstens, wenn wir wissen, dass sich eine Variable nicht mehr ändern wird und wir sie als \verb|const| deklariert haben, dann kann der Compiler das überprüfen und eine Fehlermeldung ausgeben, wenn wir versehentlich den Wert der Variablen doch ändern.
Zweitens ist der Wert von \verb|const| Variablen zur Zeit der Übersetzung bekannt und erlaubt dem Compiler einige Optimierungen.
Im Allgemeinen sollte man \verb|const| immer verwenden, wenn die Variable sich nicht mehr ändern soll.
\subsection{Operatoren} \label{subsec:Operatoren}
\index{Operator}
Variablen können mit Hilfe von Operationen manipuliert werden.
Natürlich hängt es vom Variablentyp ab, welche Operationen dafür definiert sind.
Man unterscheidet drei verschiedene Typen von Operationen:
\begin{itemize}
\item Infix:\index{Operator!Infix}\\
Der Operator steht zwischen den Variablen. Zum Beispiel: \verb|a+b|.
Dieser Ausdruck nimmt die jeweiligen Werte von \verb|a| und \verb|b|, summiert sie und gibt das Ergebnis zurück.
\item Präfix:\index{Operator!Präfix}\\
Der Operator steht vor der Variablen. Zum Beispiel: \verb|++a|.
Dieser Ausdruck erhöht den Wert von \verb|a| um $1$ und gibt danach den neuen Wert von \verb|a| zurück.
\item Postfix:\index{Operator!Postfix}\\
Der Operator steht nach der Variablen. Zum Beispiel: \verb|a--|.
Dieser Ausdruck reduziert den Wert von \verb|a| um $1$, aber gibt den originalen Wert von \verb|a| zurück.
\end{itemize}
Wieder sieht man es am einfachsten an einem Beispiel:
\begin{minipage}{\linewidth}
\begin{lstlisting}
#include <stdio.h>
int main()
{
int a = 2;
printf("%d\n", a++); // gibt 2 aus
printf("%d\n", a); // gibt 3 aus
printf("%d\n", ++a); // gibt 4 aus
printf("%d\n", a); // gibt 4 aus
return 0;
}
\end{lstlisting}
\end{minipage}
In diesem Beispiel wird zunächst eine Variable \texttt{a} mit dem Wert $2$ initialisiert.\index{\texttt{++}}
Dann nutzen wir die Funktion \texttt{printf}, um den Wert der Variablen bzw. von Ausdrücken auszugeben.
Das erste Argument von \texttt{printf} ist immer ein String, also eine Zeichenkette.
Diese Zeichenkette wird unverändert in den standard output kopiert.
Die Ausnahme sind Zeichenfolgen, die mit einem Prozentzeichen \% beginnen.
Die auf das \% folgenden Zeichen werden von \texttt{printf} in bestimmter Weise interpretiert.\index{\texttt{printf}}
\verb|%d| beispielsweise steht für eine ganze Zahl vom Typ \texttt{int}.
Bei genau einem \% in der Zeichenkette erwartet \texttt{printf} dann genau eine Variable als zweiten Parameter nach der Zeichenkette vom entsprechenden Typ, hier also vom Typ \texttt{int}.
Man kann sich leicht überlegen, dass obiges Programm die Zahlenfolge $2,3,4,4$ ausgibt.
Die Bedeutung der mit \% markierten Platzhalter wird in Teil~\ref{sec:ein_ausgabe} noch ausführlich behandelt werden.
In Bezug auf die Anzahl ihrer Argumente gibt es drei verschiedene Typen von Operatoren
\begin{itemize}
\item binäre Operatoren: Operatoren mit zwei Argumenten, wie z.B. \verb|+|.
\item unäre Operatoren: Die Operatoren haben nur ein Argument, wie z.B. \verb|++|.
\item trinäre Operatoren: Operatoren mit drei Argumenten. In \texttt{C} gibt es davon nur einen, nämlich \verb|?:|.%\index{\texttt{\?:}}
\end{itemize}
Neben arithmetischen Operatoren gibt es auch noch solche, die bit--weise wirken.
Außerdem gibt es logische Operatoren.
Bit--weise Operatoren sind binäre Operatoren und wirken auf jedes bit des Arguments.
In den Tabellen~\ref{oper}, \ref{vergoper} und \ref{vergoper2} sind die wichtigsten arithmetischen Operatoren und logischen Operatoren zusammen gefasst. \index{Operator!arithmentisch}\index{Operator!logisch}
Es gibt einige Dinge, die man sich bei der Benutzung von Operatoren bewusst machen sollte.
An dieser Stelle weisen wir auf eine davon explizit hin:
\begin{itemize}
\item Die Division ist sowohl für ganze, als auch für reelle Zahlen definiert.
Eine ganzzahlige Division von $7$ durch $2$ ergibt $3$.
Dagegen liefert eine Division von reellen Zahlen $7{,}0$ und $2{,}0$ das Ergebnis $3{,}5$.
Dementsprechend liefert
\begin{lstlisting}
float a = 7 / 3;
\end{lstlisting}
\verb|2.0| als Ergebnis.
Man kann \texttt{C} mitteilen, dass man eine reellwertige Division durchführen möchte, indem man den Dezimalpunkt mit angibt
\begin{lstlisting}
float a = 7. / 3;
\end{lstlisting}
Es ist empfehlenswert, dies immer explizit zu tun, damit es nicht durch spätere Änderungen zu schwer aufzufindenden Fehlern eines Programms kommt.
\end{itemize}
\subsection{Logische Ausdrücke}
Vergleichsoperatoren (siehe Tabelle~\ref{vergoper}) und logische Operatoren (siehe Tabelle~\ref{vergoper2}) werden zum Bilden sogenannter logischer Ausdrücke verwendet.
Logische Ausdrücke werden entweder zu wahr (\emph{true}) oder falsch (\emph{false}) ausgewertet.
Intern repräsentiert \texttt{C} jeden logischen Ausdruck als ganze Zahl.
Dabei entspricht $\neq 0$ wahr (\emph{true}) und $0$ falsch (\emph{false}).
Man kann anstelle des logischen Ausdrucks also auch einen ganzzahligen Ausdruck verwenden.
Ein sehr einfacher logischer Ausdruck ist also beispielsweise
\begin{lstlisting}
1; // -> wahr
\end{lstlisting}
Etwas komplexer geht es mit Vergleichsoperatoren\index{Operator!Vergleich}
\begin{lstlisting}
double x = 5.;
int n = 3;
(x > 1.); // wahr
(x < 0.); // falsch
(n == 3); // wahr
(x == 4.9); // falsch, aber mit Fliesskommanzahlen nicht empfehlenswert
\end{lstlisting}
Das Prüfen auf Gleichheit ist für reelle Maschinenzahlen nicht wohl definiert.
Der Grund dafür ist, wie oben diskutiert, die Maschinengenauigkeit.
Zwei reelle Zahlen werden vom Rechner als gleich ausgewertet, falls sie sich ihr Betrag um weniger als $|\delta_M|$ unterscheidet.
Oder anders formuliert, $|\delta_M|$ ist als die größte reelle Zahl definiert, für die
\begin{lstlisting}
(1 == 1 + delta_M);
\end{lstlisting}
zu wahr ausgewertet wird.
Deshalb sollte man wenn irgend möglich zwei reelle Zahlen nicht auf Gleichheit prüfen.
Stattdessen sollte man wenn möglich größer oder kleiner verwenden.
Logische Ausdrücke können natürlich auch verkettet werden.
Also beispielsweise
\begin{lstlisting}
double x = 5.;
int n = 3;
((x > 3) && !n); // falsch
\end{lstlisting}
Hierbei werden die einzelnen logischen Ausdrücke von links nach rechts ausgewertet.
Hätten wir also geschrieben
\begin{lstlisting}
double x = 5.;
int n = 3;
(!n && (x > 3)); // falsch
\end{lstlisting}
wird \verb|x > 3| nicht mehr ausgewertet, da \verb|!n| bereits zu falsch ausgewertet wurde.
Dies kann dann von Bedeutung sein, wenn das Auswerten eine teure Operation ist.
Durch geschickte Ordnung der logischen Ausdrücke kann man dann unter Umständen Zeit sparen.
\begin{table}
\centering
\begin{tabular}{l c c}
\hline
Operator & Ausdruck & Auswertung \\
\hline
Zuweisung & \verb|a = b| & Wert von \verb|b| \\
Addition & \verb|a + b| & Summe von \verb|a| und \verb|b| \\
Subtraktion & \verb|a - b| & Differenz von \verb|a| und \verb|b| \\
Multiplikation & \verb|a * b| & Produkt von \verb|a| und \verb|b| \\
Division & \verb|a / b| & Quotient von \verb|a| und \verb|b| \\
Zuweisung und Addition & \verb|a += b| & Wert von \verb|a+b| \\
Zuweisung und Subtraktion & \verb|a -= b| & Wert von \verb|a-b| \\
Zuweisung und Multiplikation & \verb|a *= b| & Wert von \verb|a*b| \\
Zuweisung und Division & \verb|a /= b| & Wert von \verb|a/b| \\
Modulo & \verb|a % b| & \verb|a| modulo \verb|b| \\
Inkrement & \verb|++a, a++| & Präfix: \verb|a|+1, Postfix: \verb|a| \\
Dekrement & \verb|--a, a--| & Präfix: \verb|a|-1, Postfix: \verb|a| \\
Positiver Vorzeichenoperator & \verb|+a| & Wert von \verb|a| \\
Negativer Vorzeichenoperator & \verb|-a| & Wert von \verb|-a| \\
\hline
\end{tabular}
\caption{Arithmetische Operatoren \label{oper}}
\end{table}
\begin{table}
\centering
\begin{tabular}{l c}
\hline
Operator & Ausdruck \\
\hline
Prüft auf Gleichheit & \verb|a == b| \\
Prüft auf Ungleichheit & \verb|a != b| \\
Prüft, ob \verb|a| echt größer als \verb|b| ist & \verb|a > b| \\
Prüft, ob \verb|a| echt kleiner als \verb|b| ist & \verb|a < b| \\
Prüft, ob \verb|a| größer gleich \verb|b| ist & \verb|a >= b| \\
Prüft, ob \verb|a| kleiner gleich \verb|b| ist & \verb|a <= b| \\
\hline
\end{tabular}
\caption{Vergleichsoperatoren \label{vergoper}}
\end{table}
\begin{table}
\centering
\begin{tabular}{l c c}
\hline
Operator & Ausdruck & Wert \\
\hline
Logisches UND & \verb|a && b| & \verb|a| und \verb|b| \\
Logisches ODER & \verb'a || b' & \verb|a| oder \verb|b| \\
Negation & \verb|!a| & nicht \verb|a| \\
\hline
\end{tabular}
\caption{Logischen Operatoren \label{vergoper2}}
\end{table}
\subsubsection{Regeln zum Bilden von Ausdrücken}
Der \texttt{C} Compiler geht bei der Auswertung von Operatoren in einer bestimmten Reihenfolge vor.
Es gelten im Allgemeinen die Vorrangregeln der Algebra beim Auswerten eines Ausdrucks, inklusive der Klammerregeln.
So werden z.B. \verb|*|, \verb|/| und \verb|%| vor \verb|+| und \verb|-| ausgewertet.
Ein unvollständiger Auszug aus der Prioritätenliste ist in Tabelle~\ref{tab:prior} zusammengefasst.
Kleinerer Rang bedeutet dabei höhere Priorität.
\begin{table}
\centering
\begin{tabular}{l r}
\hline
Rang & Operatoren \\
\hline
0 & \texttt{., ->, [], ()}\\
1 & \texttt{\&} (Adressoperator), \texttt{*} (Dereferenzierung)\\
2 & \texttt{*, / \%}\\
3 & \texttt{<, >, <=, >=}\\
4 & \texttt{==, !=}\\
5 & \texttt{\&\&}\\
6 & \texttt{||}\\
7 & alle Zuweisungen \texttt{=, +=, -=, ...}\\
\hline
\end{tabular}
\caption{Priorität von Operatoren}
\label{tab:prior}
\end{table}