-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathvariablen.tex
383 lines (354 loc) · 16.8 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
\section{Variablen}
Wie schon angedeutet werden Daten in C in Variablen bzw. Objekten gespeichert.
Auch dies ist am einfachsten an Hand eines Beispiels zu verstehen
\begin{lstlisting}{Erste Variablendeklaration und -zuweisung}
#include <stido.h>
int main()
{
int n;
n = 4;
printf("Wir werden n zahlen sortieren:\n");
return(0);
}
\end{lstlisting}
Im Vergleich zu unserem letzten Beispiel sind zwei Zeilen hinzugekommen.
\begin{itemize}
\item \texttt{int n;}\\
Diese Anweisung stellt eine Deklaration dar.
Sie teilt dem Compiler mit, ab jetzt den entsprechenden 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 entsprechende 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 C keine Initialisierung durch.
Nach der reinen Definition (ohne Zuweisung) ist der Wert der Variablen \texttt{n} also rein zufällig.
\end{itemize}
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.
Es ist wichtig, dass jeder Benutzung einer Variablen, beispielsweise in einer Zuweisung, die Deklaration der Variablen vorangehen muss.
Deklaration und Zuweisung können auch in einer Anweisung geschehen, man kann also auch
\begin{lstlisting}{Erste Variablendeklaration und -zuweisung: Variante 2}
#include <stido.h>
int main()
{
int n = 4;
printf("Wir werden n zahlen sortieren :\n");
return(0);
}
\end{lstlisting}
schreiben.
Man spricht dann auch von der Initialisierung der Variablen \texttt{n}.
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.
%\textbf{das ist zu früh hier!}
%Es gibt zwei verschiedene Möglichkeiten eine globale Variable zu definieren.
%\begin{enumerate}
%\item Stichwort static: In diesem Fall mann kann nutzten die Variable in ganzen File, wo es definiert war. Anderen
%Files in unserem Code kann natürlich nutzten eine andere Variable mit dem gleichen Name.
%\item Stichwort extern: In diesem Fall mann kann nutzten die Variable in dem ganzen Programm. Aber das
%variable muss deklariert werden in allen Files, wo wir ihn nutzten wollen.
%\end{enumerate} die für alle
%Wir haben die Sichtbarkeitbereich der Variablen
%in der Abbildung \ref{sicht} zusammen gefasst. Das bedeutet, das wir können Variablen mit
%gleichen Namen in verschiedenen Funkcionen nutzten, wenn wir definieren sie als lokalen Variablen.
%
%% Generated with LaTeXDraw 2.0.8
%% Tue Feb 21 10:59:44 CET 2017
%% \usepackage[usenames,dvipsnames]{pstricks}
%% \usepackage{epsfig}
%% \usepackage{pst-grad} % For gradients
%% \usepackage{pst-plot} % For axes
%%\scalebox{0.5} % Change this value to rescale the drawing.
%%{
%\begin{figure}[!ht]
%\centering
%\scalebox{0.5}
%{
%\begin{pspicture}(2,-9.1)(17.3,9.12)
%\psframe[linewidth=0.04,dimen=outer](12.6,9.1)(2.0,-9.1)
%%\usefont{T1}{ptm}{m}{n}
%\rput(3.6692188,8.61){Source code}
%\psline[linewidth=0.04](5.2,9.1)(5.2,8.1)(2.0,8.1)
%%\usefont{T1}{ptm}{m}{it}
%\rput(7.8229685,-7.565){globalen Variablen: Deklarierten mit dem Stichwort: extern}
%\psframe[linewidth=0.04,dimen=outer](11.6,7.1)(3.2,0.1)
%\psframe[linewidth=0.04,dimen=outer](11.6,-0.5)(3.2,-7.1)
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.0503125,6.81){File 1}
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.0489063,-0.79){File 2}
%\psline[linewidth=0.04](5.0,7.1)(5.0,6.5)(3.2,6.5)(3.2,6.5)
%\psline[linewidth=0.04](4.8,-0.5)(4.8,-1.1)(3.2,-1.1)
%%\usefont{T1}{ptm}{m}{n}
%\rput(6.7009373,0.81){variable mit static Stichwort}
%%\usefont{T1}{ptm}{m}{n}
%\rput(6.9809375,-6.59){variablen mit static stichwort}
%\psframe[linewidth=0.04,dimen=outer](6.8,5.7)(3.8,2.7)
%\psframe[linewidth=0.04,dimen=outer](11.0,5.5)(8.0,2.7)
%\psframe[linewidth=0.04,dimen=outer](7.0,-1.7)(3.6,-5.1)
%\psframe[linewidth=0.04,dimen=outer](11.2,-1.9)(8.0,-5.3)
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.9203124,5.41){Funkcion 1}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.3189063,5.21){Funkcion 2}
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.9203124,-1.99){Funkcion 1}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.3189063,-2.19){Funkcion 2}
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.7434375,4.61){Lokale }
%%\usefnt{T1}{ptm}{m}{n}
%\rput(5.4034376,4.21){variablen}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.3434377,4.41){Lokale}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.8173437,4.01){Variablen}
%%\usefont{T1}{ptm}{m}{n}
%\rput(4.7434375,-3.39){Lokale}
%%\usefont{T1}{ptm}{m}{n}
%\rput(5.2034376,-3.79){variablen}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.3434377,-3.39){Lokale}
%%\usefont{T1}{ptm}{m}{n}
%\rput(9.8034377,-3.79){variablen}
%\end{pspicture}
%}
%\caption{\label{sicht} Sichtbarkeitbereich der Variablen}
%\end{figure}
Variablen haben immer einerseits einen Datentyp und einen Wert.
Der Datentyp entscheidet, welche Werte eine Variable annehmen kann und wie viel Arbeitsspeicher dafür reserviert wird.
In der Tabelle~\ref{tab:PPer} sind die elementaren C-Datentypen mit ihren Wertebereichen aufgelistet.
\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}
C Compiler führen im Prinzip eine strenge Typenkontrolle durch.
Das ist eine sehr nützliche Eigenschaft der Compiler, wenn es auch manchmal etwas mühsam ist.
Man kann dies durch einen expliziten \emph{cast} umgehen.
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.
Beispielsweise wird folgender Code ohne Beanstandung übersetzt
\begin{lstlisting}{Ungenaue Variablenzuweisung}
#include <stido.h>
int main()
{
// so etwas sollte man nicht schreiben!
int n = 4.5;
printf("Wir werden n Zahlen sortieren :\n");
return 0;
}
\end{lstlisting}
obwohl hier implizit die reelle Zahl $4.5$ durch Abschneiden in eine ganze Zahl umgewandelt wird.
\texttt{n} hat den Wert $4$.
Es gibt einige Regeln für die Namen von Objekten in C.
C eigene Schlüsselworte, 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 \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.
Für den Algorithmus Einfügesortieren sollte 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';
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.
\subsection{Konstanten}
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.
Im Quelltext sieht das wie folgt aus
\begin{lstlisting}
const int n = 5;
\end{lstlisting}
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}
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:\\
Derx 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:\\
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:\\
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 vom \verb|a| zurück.
\end{itemize}
Wieder sieht man es am einfachsten an einem Beispiel:
\begin{lstlisting}
#include <stdio.h>
int main()
{
int a = 2;
printf("%d\n", a++);
printf("%d\n", a);
printf("%d\n", ++a);
printf("%d\n", a);
return 0;
}
\end{lstlisting}
In diesem Beispiel wird zunächst eine Variable \texttt{a} mit dem Wert $2$ initialisiert.
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.
\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.
Im Allgemeinen 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 C gibt es davon nur einen, nämlich \verb|?:|.
\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.
Logische Operatoren liefern als Wert entweder \emph{true} or \emph{false}, richtig oder falsch, $1$ oder $0$.
In den Tabellen~\ref{oper}, \ref{vergoper} und \ref{vergoper2} fassen wir die wichtigsten arithmetischen Operatoren und logischen Operatoren zusammen.
Es gibt einige Dinge, die man sich bei der Benutzung von Operatoren bewusst machen sollte.
An dieser Stelle weisen wir auf zwei davon 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 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}
\item 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.
Deshalb sollte man wenn irgend möglich zwei reelle Zahlen nicht auf Gleichheit prüfen.
\end{itemize}
\begin{table}
\centering
\begin{tabular}{l c c}
\hline
Operator & Ausdruck & Auswertung \\
\hline
Zuweisung & \verb|a = b| & Werte 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| & Werte von \verb|a+b| \\
Zuweisung und Subtraktion & \verb|a -= b| & Werte von \verb|a-b| \\
Zuweisung und Multiplikation & \verb|a *= b| & Werte von \verb|a*b| \\
Zuweisung und Division & \verb|a /= b| & Werte 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}
Eine wichtige Frage bei Operationen ist natürlich die nach der Reihenfolge.
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}