forked from pittlerf/ckurs2017
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathkontrollstrukturen.tex
290 lines (262 loc) · 11.7 KB
/
kontrollstrukturen.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
\section{Kontrollstrukturen: Verzweigungen und Schleifen}
Bisher haben wir einfache Anweisungen kennen gelernt, die vom Rechner nacheinander ausgeführt werden.
Wir werden nun Kontrollstrukturen einführen, die es erlauben, den Fluss eines Programmes zu beeinflussen.
Beispielsweise kann man, in Abhängigkeit von Werten von Variablen entweder einen, oder einen anderen Block von Anweisungen ausführen.
Solche Kontrollstrukturen nennt man Verzweigungen.
Das kann beispielsweise bedeuten, dass der Programmteil \texttt{A} ausgeführt wird, falls eine Variable $x$ größer als Null ist, und sonst der Programmteil \texttt{B}.
Die Entscheidung wird auf Grundlage logischer Ausdrücke gefällt, siehe den vorangegangenen Abschnitt.
\subsection{Einfache Verzweigung: \texttt{if-else}}
Im Allgemeinen hat das \emph{if-else} Konstrukt folgendes Aussehen:
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={if-else Statement}, belowcaptionskip=0.3em]
if (logischer Ausdruck)
{
// falls wahr
Anweisung1;
Anweisung2;
...;
}
else // optional
{
// sonst
Anweisung3;
Anweisung4;
...;
}
\end{lstlisting}
\end{minipage}
In Abhängigkeit vom logischen Ausdruck wird entweder der erste (nach \emph{if}) oder der zweite (nach \emph{else}) Codeblock ausgeführt.
Diese beiden Codeblöcke sind durch geschweifte Klammern definiert.
Die Klammern können auch weggelassen werden, wenn ein Block nur aus genau einer Anweisung besteht.
Wir empfehlen trotzdem auch in diesem Fall Klammern zu setzen.
Dies verhindert spätere Fehler, und macht den Quelltext lesbarer.
Der logische Ausdruck kann prinzipiell alle Operatoren enthalten, bzw. auch verkettete Ausdrücke von Operatoren, die wir in Tabellen~\ref{vergoper} und \ref{vergoper2} zusammengestellt haben.
\verb|Anweisung1| kann explizit auch wieder ein \emph{if-else} Ausdruck sein.
Man kann \emph{if-else} also beliebig schachteln.
Außerdem ist der \emph{else} Block optional.
Wird der \emph{else} Block ausgelassen, so wird in dem Fall, in dem der logische Ausdruck zu \emph{falsch} ausgewertet wird, der Block mit Anweisungen nicht ausgeführt.
Nehmen wir beispielsweise an, wir wollen den Absolutwert einer Variablen \verb|a| ausgeben.
Dafür kann man einen \verb|if-else| Ausdruck verwenden.\index{\texttt{if-else}}
Der entsprechende Quelltext könnte so aussehen:
\begin{minipage}{\linewidth}
\begin{lstlisting}
#include <stdio.h>
int main()
{
int a = 4;
unsigned int absolutevalue = 0;
if (a > 0) // a ist positiv
{
absolutevalue = a; // Betrag ist direkt gleich a
}
else // a ist negativ
{
// falls a <= 0
absolutvalue = -a; // Betrag ist gleich -a
}
printf("Der Absolutwert von a ist %u\n", absolutevalue);
return (0);
}
\end{lstlisting}
\end{minipage}
In Abhängigkeit davon, ob \verb|a > 0| zu wahr ausgewertet wird oder nicht, wird in diesem Beispiel entweder der Codeblock nach dem Schlüsselwort \verb|if| oder der nach dem Schlüsselwort \verb|else| ausgeführt.
Die Variable \verb|absolutevalue| ist außerhalb beider Blöcke deklariert, und damit in beiden sichtbar.
Die Zuweisung, die \verb|absolutevalue| innerhalb der Blöcke bekommt, ist damit auch nach Ende der Blöcke weiterhin erhalten.
\subsection{Mehrfache Verzweigung: \texttt{switch}}
\index{\texttt{switch}}
Ein dem \emph{if-else} Konstrukt verwandtes Konstrukt ist das \emph{switch} Konstrukt.
Es erlaubt eine ganze Liste von \emph{ganzzahligen} Alternativen abzuarbeiten.
Im Allgemeinen sieht das also so aus:
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={switch statement}, belowcaptionskip=0.3em]
switch (int)
{
case Wert1:
Anweisung1;
....;
break; // optional
case Wert2:
Anweisung2;
....;
break; // optional
....
default: // optional
Anweisung3;
break; // optional
}
\end{lstlisting}
\end{minipage}
Das Konstrukt wird mit dem Schlüsselwort \verb|switch| eingeleitet.\index{\texttt{switch}}
Jede der Möglichkeiten beginnt mit dem Schlüsselwort \verb|case|, gefolgt vom möglichen Wert des Ausdruckes und einem Doppelpunkt.
Falls die entsprechende Möglichkeit realisiert ist, werden alle noch folgenden Anweisungen ausgeführt, bis ein \verb|break| Schlüsselwort kommt, oder der \verb|switch| Block zu Ende ist.\index{\texttt{break}}
Der \verb|default| Zweig wird dann ausgeführt, wenn keiner der anderen Fälle gepasst hat.\index{\texttt{default}}
Im folgende Bespiel wird eine ganze Zahl von der Standardeingabe eingelesen, und zwar mit Hilfe der \verb|scanf| Funktion, die eine \verb|printf| sehr ähnliche Syntax hat.\index{\texttt{printf}}\index{\texttt{scanf}}
Wir diskutieren die Details zu \verb|scanf| später.
Dann wird, in Abhängigkeit vom eingegebenen Wert eine Ausgabe auf dem Bildschirm gemacht.
\begin{myexampleprogram}{Beispiel: \texttt{switch} statement}
\begin{lstlisting}[numbers=left]
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
switch (n)
{
case 0:
printf("Der Eingabewert ist 0\n");
break;
case 1:
printf("Der Eingabewert ist 1\n");
case 2:
printf("Der Eingabewert ist 1 oder 2\n");
break;
case 3:
printf("Der Eingabewert ist 3\n");
break;
default:
printf("Der Eingabewert ist groesser als 3 oder kleiner als 0\n");
break;
}
return (0);
}
\end{lstlisting}
\end{myexampleprogram}
D.h., wenn $0$ eingegeben wird, wird Zeile $10$ ausgeführt.
Wenn $1$ eingegeben wird, wird Zeile $13$ und $15$ ausgeführt.
Und, wenn keines von $0,1,2,3$ eingegeben wird, also keine der Möglichkeiten passt, so wird der \verb|default| Zweig ausgeführt, und damit Zeile $21$.
\subsection{Schleifen: \texttt{for}}
\index{Schleife!\texttt{for}}\index{\texttt{for}}
Eine wesentlich wichtigere Kontrollstruktur sind sogenannte \emph{for} Schleifen.
Allgemein sieht die \emph{for} Schleife wie folgt aus:
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={for Schleife}, belowcaptionskip=0.3em]
for (Ausdruck1; Ausdruck2; Ausdruck3)
{
Anweisung1;
Anweisung2;
...;
}
\end{lstlisting}
\end{minipage}
Im Einzelnen bedeutet das:
\begin{enumerate}
\item Zuerst wird \texttt{Ausdruck1} ausgewertet.\\
In diesem ersten Ausdruck wird typischerweise die Schleifenvaraible deklariert und / oder initialisiert.
(Es können auch mehrere Variablen deklariert und initialisiert werden.)
\item Dann wird \texttt{Ausdruck2} ausgewertet.\\
Dieser zweite Ausdruck wird zu Beginn jeder Ausführung der Schleife ausgewertet und wird als logischer Ausdruck interpretiert.
Er stellt die Abbruchbedingung dar.
Wenn \texttt{Ausdruck2} zu wahr ausgewertet wird, werden die Anweisungen im Körper der Schleife ausgeführt.
\item Nach Ausführung des Schleifenkörpers wird \texttt{Ausdruck3} ausgewertet.\\
Hier werden typischerweise Schleifenvariablen modifiziert.
\end{enumerate}
Als Beispiel nehmen wir an, wir möchten die ganzen Zahlen von $0$ bis $n-1$ aufsummieren und schreiben dazu folgenden Quelltext:
\begin{minipage}{\linewidth}
\begin{lstlisting}
#include <stdio.h>
int main()
{
int n = 173;
int summe = 0;
for (int i = 0; i < n; i++) {
summe += i;
}
printf("Die Summe der Zahlen von 0 bis %d ist %d\n", n - 1, summe);
return (0);
}
\end{lstlisting}
\end{minipage}
Dabei ist \texttt{Ausdruck1} die Deklaration und Initialisierung von \verb|i|
\begin{lstlisting}
int i = 0;
\end{lstlisting}
Das Abbruchkrieterium ist der logische Ausdruck
\begin{lstlisting}
i < n;
\end{lstlisting}
Der Körper der Schleife besteht in diesem Fall nur aus der Zeile
\begin{lstlisting}
summe += i;
\end{lstlisting}
die für \verb|i=0,1,... n-1| nacheinander ausgeführt wird.
Diese Zeile wird also $n-1$--Mal ausgeführt, mit jeweils anderen Werten für \verb|summe| und \verb|i|.
Da \verb|summe| außerhalb der Schleife deklariert und initialisiert wurde, ist der entsprechende Wert auch nach der Schleife verfügbar.
\verb|i| dagegen ist in diesem Beispiel nur innerhalb der Schleife verfügbar!
\texttt{Ausdruck3} erhöht bei jedem Aufruf die Schleifenvariable \verb|i| um eins.
\begin{lstlisting}
i++
\end{lstlisting}
Interessanterweise dürfen auch alle drei Ausdrücke leer sein.
Dann wird die Schleife im Prinzip unendlich oft ausgeführt.
In einem solchen Fall kann man die Schleife mit Hilfe von \verb|break| explizit abbrechen, wie man im folgenden modifizierten Beispiel sieht:\index{\texttt{break}}
\begin{minipage}{\linewidth}
\begin{lstlisting}
#include <stdio.h>
int main()
{
int n = 173;
int summe = 0;
int i = 0;
for (;;) { // korrekt, aber schlechter Stil!
if (i == n) break;
summe += i;
i++;
}
printf("Die Summe der Zahlen von 0 bis %d ist %d\n", n - 1, summe);
return (0);
}
\end{lstlisting}
\end{minipage}
Der Quelltext führt immer noch die gleiche Aufgabe aus.
Allerdings stellt das Verwenden einer \texttt{for} Schleife in dieser Weise sehr schlechten Stil dar.
Der Grund ist, dass man am Schleifenkopf nicht mehr erkennen kann, wann die Schleife abgebrochen wird und was die Schleifenvariablen sind.
Der Quelltext wird also wesentlich unleserlicher und fehleranfälliger.
\index{\texttt{break}}
Die Benutzung von \verb|break| kann und ist aber an vielen Stellen sinnvoll.
Soll beispielsweise innerhalb einer Schleife eine externe Funktion aufgerufen werden, so sollte man immer überprüfen, ob diese Funktion auch ohne Fehler ausgeführt wurde.
Falls die Funktion einen Fehler meldet, kann man mit Hilfe von \verb|break| die Ausführung der Schleife unterbrechen:
\begin{minipage}{\linewidth}
\begin{lstlisting}
for (int i = 0; i < n; i++) {
int errorcode = myFunction(i);
if( errorcode != 0 ) {
// take appropriate action
break;
}
}
\end{lstlisting}
\end{minipage}
Dies lässt sich für Fälle verallgemeinern, in denen unerwartete Bedingungen innerhalb der Ausführung einer Schleife auftreten.
\subsection{Schleifen: \texttt{while} und \texttt{do-while}}
\index{\texttt{do-while}}\index{\texttt{while}}\index{Schleife!\texttt{do-while}}\index{Schleife!\texttt{while}}
C kennt zwei weitere Schleifenkonstrukte, nämlich die \verb|while| und die \verb|do-while| Schleife.
\texttt{for} Schleifen haben in der Regel eine fest vorgegebene Anzahl von Durchläufen.
Die \texttt{while} Schleifen Familie ist da flexibler: Die Anzahl an Durchläufen hängt nur von einem logischen Ausdruck ab.
Während die \verb|while| Schleife die Abbruchbedingung vor dem Ausführen des Schleifenkörpers überprüft und im Zweifel abbricht, wird bei der \verb|do-while| Schleife der Körper immer mindestens einmal ausgeführt.
Das macht den großen Unterschied zwischen den beiden aus.
Allgemein hat die \verb|while| Schleife folgende Form:
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={while Schleife}, belowcaptionskip=0.3em]
while (logischer Ausdruck)
{
Anweisungen;
}
\end{lstlisting}
\end{minipage}
Hier ist der \texttt{logische Ausdruck} die Abbruchbedingung.
So lange der logische Ausdruck zu wahr ausgewertet wird, werden die Anweisungen im Schleifenkörper ausgeführt.
Die Abbruckbedingung steht bei der \verb|do-while| Schleife am Ende, wie man an der allgemeinen Form sieht:
\begin{minipage}{\linewidth}
\begin{lstlisting}[caption={do-while Schleife}, belowcaptionskip=0.3em]
do
{
Anweisungen;
}
while (logischer Ausdruck);
\end{lstlisting}
\end{minipage}
Die Anweisungen werden also mindestens einmal ausgeführt bis der \texttt{logische Ausdruck} das erste Mal ausgewertet wird.
Ausführungen von \verb|while| und \verb|do-while| Schleifen können ebenfalls mit \verb|break| abgebrochen werden.
Natürlich können \verb|while|, \verb|do-while| und \verb|for| Schleifen immer ineinander überführt werden.
Allerdings ist dies manchmal mit Aufwand verbunden und es erscheint fast immer natürlich die eine oder andere Schleifenform zu verwenden.
\endinput