Numerische Datentypen und Verwendung

Werbung
Grundlagen der Programmiersprache C für
Studierende der Naturwissenschaften
Teil 2: Numerische Datentypen und Verwendung von Variablen
Patrick Schreier
Abteilung für Angewandte Mathematik
Vorlesung vom 27. April 2015
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
Berechnung der Quadratwurzel
Quelltext (Quadratwurzel)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <math.h>
#include <stdio.h>
int main(void)
{
/* declare floating point variable */
double x;
/* read from user input */
printf ("x = ");
scanf("%lf", &x);
/* print value of x and square root */
printf ("x = %f\n", x);
printf ("sqrt(x) = %f\n", sqrt(x));
return 0;
}
Variablen
I
Variable = symbolischer Name für einen Speicherbereich.
I
Variablen in Mathematik und Informatik sind verschieden:
I
I
I
Mathematik: Sei x ∈ R fixiert x
Informatik: int x definiert eine Variable vom Type int.
x = 5 weist x den Wert 5 zu.
Jede Variable muss vor Ihrer Verwendung vereinbart werden.
Eine Vereinbarung besteht aus Angabe von Namen und Typ der
Variablen.
Namen: Namen identifizieren Variablen in eindeutiger Weise.
Typen: Der Typ einer Variablen legt fest, welche Werte in der
Variablen gespeichert werden können und welche
Operationen auf Ihnen möglich sind.
Der Datentyp int
I
Der Datentyp int ist vorgesehen, um ganze Zahlen (engl.
integers) darzustellen:
. . . , − 2, − 1, 0, 1, 2, . . .
I
Integer-Datentypen haben einen endlichen Wertebereich, d. h.
Variablen vom Typ int können nicht beliebig (betragsmäßig)
große Werte annehmen.
I
Neben dem Typ int gibt es eine Reihe weiterer
Integer-Datentypen:
char, short int, unsigned int, long int, ...
Sie unterscheiden sich hinsichtlich ihres darstellbaren
Wertebereichs und ihres Speicheraufwands.
Der Datentyp double
I
Gleitpunktzahlen oder auch Gleitkommazahlen (engl. floating
point numbers) sind eine Teilmenge der rationalen Zahlen.
Arithmetische Operationen auf Gleitkommazahlen sind nicht
exakt, sondern mit einem kleinen Fehler behaftet.
I
Gleitpunktzahlen werden in einem der folgenden Typen
abgespeichert:
float
double
long double
I
Wir beschäftigen uns heute ausschließlich mit dem Typ double.
Bezeichner
Konstruktion von Bezeichnern:
I
Bezeichner dürfen aus Buchstaben, Ziffern und dem Unterstrich
bestehen.
I
Das erste Zeichen darf keine Ziffer sein.
I
Reservierte Schlüsselwörter dürfen nicht verwendet werden.
Bemerkungen:
I
I
Zwischen Groß- und Kleinschreibung wird unterschieden, d. h.
value und Value sind verschiedene Bezeichner.
Bezeichner dürfen beliebig lang sein. Der Standard garantiert
allerdings nur, dass mindestens 31 Zeichen signifikant sind (in
der Praxis heute irrelevant).
Reservierte Schlüsselwörter
auto
default
float
register
struct
volatile
break
do
for
return
switch
while
case
double
goto
short
typedef
char
else
if
signed
union
const
enum
int
sizeof
unsigned
continue
extern
long
static
void
Tabelle : Reservierte Schüsselwörter in ANSI C89
inline
_Bool
_Complex
_Imaginary
Tabelle : Reservierte Schüsselwörter in ISO C99
Beispiele für Bezeichner
Betrachten wir einige Beispiele:
x, i, n, ...
Okay
Sehr kurze Variablennamen sind üblich in arithmetischen
Ausdrücken oder in Schleifen. Aussagekräftige Namen helfen allerdings beim Programmieren und Debuggen.
_a_very_long_long_identifier
Okay
Sehr lange Bezeichnern sind allerdings auch unpraktisch
(mögliche Schreibfehler, Zeilenlänge).
größe
Falsch
Der Umlaut ö und der Buchstabe ß sind keine erlaubten
Zeichen.
case
Falsch
Bei case handelt es sich um ein reserviertes Schlüsselwort.
Deklaration von Variablen
Die Deklaration entspricht der Zuweisung von einem Speicherbereich
auf einen symbolischen Namen. Die Grösse des Speicherbereichs
entspricht dem Typ der Variable.
Syntax:
typename identifier;
wobei
I
typename den Namen eines Typs (bisher: int oder double)
bezeichnet,
I
identifier ein gültiger Bezeichner sein muss.
Beispiele:
int a;
double x;
Gleichzeitige Deklaration mehrerer Variablen
I
Mehrere Variablen desselben Typs können auch in nur einer
Zeile definiert werden.
Beispiele:
int a, b;
double x, y;
I
Durch Deklaration einer Variablen wird lediglich (uninitialisierter)
Speicherbereich zugewiesen. Ist noch kein konkreter Wert
zugewiesen ist der Wert einer Variable zufällig.
Initialisierung von Variablen
I
Nach Deklaration kann der Variable ein konstanter Wert
zugewiesen werden:
I
I
I
int n; (Deklaration)
n = 0; (Initialisierung)
Konstanten haben (wie Variablen auch) einen Typ. Dieser muss
nicht explizit angegeben werden. Integer- und
Floating-Point-Konstanten werden durch die Angabe eines
Punkts voneinander unterschieden:
1
1.0
1.
I
/* constant expression of type int */
/* constant expression of type double */
/* constant expression of type double */
Deklaration und Initialisierung können auch in einer Zeile
erfolgen:
I
I
int n = 1;
double x = 1.;
Platzierung von Definition
I
Jede Variable muss vor Ihrer ersten Verwendung definiert
werden. Andernfalls bricht der Übersetzungsvorgang mit einem
Fehler ab.
I
Ältere C-Standards (bis C99) schreiben vor, dass sämtliche
Variablendefinitionen am Anfang von main (eigentlich: ihres
Gültigkeitsbereichs) stehen müssen:
int main(void)
{
/* list of all definitions */
...
/* subsequent statements */
...
}
Moderne Compiler erlauben die Vermischung von Definitionen
und Anweisungen.
Formatierte Ausgabe mit printf
Der Funktion printf kann eine beliebige Zahl von Variablen und
konstanten Ausdrücken übergeben werden:
int n = 1;
double pi = 3.14159265359;
printf("n = %d \n", n);
printf("pi = %lf \n", pi);
printf("n = %d, pi = %lf \n", n, pi);
printf("n = %d, pi = %lf \n", 1, 3.1415);
I
Neben wörtlich wiederzugebendem Text enthält der
Format-String (von doppelten Anführungszeichen
eingeschlossen) Formatelemente (engl. conversion
specifications).
Formatierte Ausgabe mit printf
I
Formatelemente sind Platzhalter für Variablen oder Konstanten
verschiedene Typs:
%d /* conversion specification for type int */
%lf /* conversion specification for type double */
I
Bei der Ausgabe von Gleitpunktzahlen kann man die Anzahl der
Nachkommastellen festlegen. Der Code
double pi = 3.14159265359;
printf("%lf\n", pi);
printf("%.3lf\n", pi);
printf("%.12lf\n", pi);
produziert die Ausgabe:
3.141593
3.142
3.141592653590
Formatierte Eingabe mit scanf
Variableninhalte können zur Laufzeit des Programms von der Konsole
eingelesen werden. Dazu verwenden wir die Funktion scanf:
int a;
double
scanf(
scanf(
scanf(
x;
"%d", &a );
"%lf", &x );
"%d %lf", &a, &x );
Der Funktion können eine oder mehrere Variablen als Parameter
übergegeben werden. Ihre Anzahl und Typen müssen den
Formatelemente entsprechen:
%d /* conversion specification for type int */
%lf /* conversion specification for type double */
Vor jedem Element der Parameterliste muss ein "&" stehen. Seine
Bedeutung werden wir erst später klären.
Arithmetische Operationen
I
I
Bedeutung eines Operators kann vom Datentyp abhängen.
Operatoren auf Gleitpunktzahlen: (Typ double)
I
I
I
I
Operatoren auf Ganzzahlen: (Typ int)
I
I
I
I
x=y; (Zuweisung)
x+y, x-y, x*y (Addition, Subtraktion, Multiplikation)
x/y, (Division)
n=m; (Zuweisung)
n+m, n-m, n*m (Addition, Subtraktion, Multiplikation)
n/m, n%m (Division ohne Rest, Divisionsrest)
Die Konstanten oder Variablen, die ein Operator verknüpft
werden als Operanden bezeichnet, bspw. hat x+y die
Operanden x und y.
Ganzzahlige Division mit Rest
I
Für Integer-Werte entspricht das Operatorpaar /,% der
ganzzahligen Division mit Rest:
int quotient = 1/2;
int remainder = 1%2;
I
/* quotient = 0 */
/* remainder = 1 */
Das Teilen durch die Null führt nicht etwa automatisch zu einem
Laufzeitfehler, das Programm bricht nicht ab. Der Ausdruck hat
den ausgezeichneten Wert inf:
printf("%lf\n", 1./0.);
I
/* Output: inf */
Der Wert nan (not a number) dient als Hinweis für ”dubiose”
Rechen-Operationen wie 0/0 oder inf/inf:
printf("%lf\n", 0./0.);
/* Output: -nan */
Implizite Typkonversion
Operatoren können Variablen verschiedener Datentypen verbinden.
int x = 1;
double y = 2.5;
double sumDouble = x + y;
int sumInt = x + y;
printf("sumInt = %d\n", sumInt);
/* Output: sumInt = 3 */
printf("sumDouble = %lf\n", sumDouble);
/* Output: sumDouble = 3.500000 */
I
x + y hat den Datentyp double, bei der Addition wird x in ein
double konvertiert.
I
sumInt hat den Typ int, d.h. x+y wird in ein int konvertiert.
Implizite Typkonversion
I
Haben die Operanden eines arithmetischen Operators den
gleichen Typ, so ist auch das Ergebnis von diesem Datentypen.
I
Sind die Operanden von unterschiedlichem Typ, so wird der
Operand mit ”ungenauerem” Typ, zuerst zum ”genaueren” Typ
konvertiert, bspw. ist 1 / 5. eine Konstante vom Typ
double.
I
Konvertierung von double nach int erfolgt durch Abschneiden,
nicht durch Rundung!
int n = 3.7;
printf("n = %d\n", n); /* Output: n = 3 */
Implizite Typkonversion
I
Klammern entscheiden die Reihenfolge in der die Operatoren
angewendet werden.
double
double
double
double
x_1
x_2
x_3
x_4
=
=
=
=
printf("x_1
printf("x_2
printf("x_3
printf("x_4
=
=
=
=
2 /
2 /
10.
10.
4;
4.;
* 2 / 4;
* (2 / 4);
%lf\n",
%lf\n",
%lf\n",
%lf\n",
x_1);
x_2);
x_3);
x_4);
Implizite Typkonversion
I
Klammern entscheiden die Reihenfolge in der die Operatoren
angewendet werden.
double
double
double
double
x_1
x_2
x_3
x_4
=
=
=
=
printf("x_1
printf("x_2
printf("x_3
printf("x_4
I
=
=
=
=
2 /
2 /
10.
10.
4;
4.;
* 2 / 4;
* (2 / 4);
%lf\n",
%lf\n",
%lf\n",
%lf\n",
x_1);
x_2);
x_3);
x_4);
Der Code produziert den Output:
x_1
x_2
x_3
x_4
=
=
=
=
0.000000
0.500000
5.000000
0.000000
Explizite Typkonversion
I
Man kann dem Compiler mitteilen, in welcher Form eine Variable
interpretiert werden muss.
I
Man stellt dazu den Ziel-Typ in Klammern voran
double x_1 = (double) (2 / 4);
double x_2 = (double) 2 / 4;
double x_3 = 2 / (double) 4;
Explizite Typkonversion
I
Man kann dem Compiler mitteilen, in welcher Form eine Variable
interpretiert werden muss.
I
Man stellt dazu den Ziel-Typ in Klammern voran
double x_1 = (double) (2 / 4);
double x_2 = (double) 2 / 4;
double x_3 = 2 / (double) 4;
I
Die Variablen haben den Wert:
x_1 = 0.000000
x_2 = 0.500000
x_3 = 0.500000
Vordefinierte Funktionen aus math.h
Die Datei math.h gehört zur C-Standardbibliothek. Mit
#include <math.h>
wird sie einem Programm hinzugefügt.
Die Datei enthält eine Reihe von wichtigen mathematischen
Funktionen, z. B.
double x, base, exponent;
fabs(x)
/* absolute value of x */
sqrt(x)
/* square root of x */
sin(x), cos(x)
/* sine, cosine of x */
exp(x)
/* exponential function of x */
pow(base,exponent)/* base raised to the power exponent*/
Die genannten Beispiele erwarten als Parameter Gleitpunktzahlen
und geben auch einen double-Wert zurück:
double x = fabs(-2.);
double y = pow(4.,2.);
/* x = 2. */
/* x = 16. */
Explizites Linken der Mathematik-Bibliothek
Allein das Hinzufügen von math.h durch die #include-Direktive
reicht in der Regel nicht aus, damit ein Programm kompiliert wird.
Fehlermeldungen der Form
$ gcc -o math math.c
math.c:(.text+0x6b): undefined reference to ‘sqrt’
werden beim Linken (letzter Schritt im Übersetzungsvorgang)
verursacht.
Anders als etwa die Bibliotheksdatei stdio.h muss die
Mathematik-Bibliothek im Compileraufruf explizit gelinkt werden:
$ gcc -o math math.c -lm
Berechnung der Quadratwurzel
Quelltext (Quadratwurzel)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <math.h>
#include <stdio.h>
int main(void)
{
/* declare floating point variable */
double x;
/* read from user input */
printf ("x = ");
scanf("%lf", &x);
/* print value of x and square root */
printf ("x = %f\n", x);
printf ("sqrt(x) = %f\n", sqrt(x));
return 0;
}
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
Binärsystem
Das Binärsystem ist ein Zahlensystem, das zur Darstellung von
Zahlen nur die Ziffern 0 und 1 benutzt:
Sei n ∈ N, 0 ≤ n < 2m , dann ist die Darstellung
n=
m−1
X
xi 2i
(xi ∈ {0, 1})
i=0
eindeutig. Im Binärsystem hat n die Darstellung
xm−1 xm−2 . . . x1 x0 |2
Beispiel: Es gilt
378|10 = 1 · 28 + 0 · 27 + 1 · 26 + 1 · 25 + 1 · 24
+ 1 · 23 + 0 · 22 + 1 · 21 + 0 · 20
Damit ist: 378|10 = 101111010|2 .
Bits und Bytes
I
Unter einem Bit stellen wir uns eine Binärziffer
x ∈ {0, 1}
vor (d. h. x nimmt nur die Werte 0 oder 1 an).
I
Ein Byte ist eine zusammenhängende Folge von Bits. Alle
Objekte in C werden in einer zusammenhängenden Folge von
Bytes gespeichert.
I
Der C-Standard schreibt eine Länge von mindestens 8 Bit für ein
Byte vor. Die Größ eines Zeichens vom Typ char entspricht
einem Byte.
Endlicher Wertebereich
Ein Objekt vom Typ int belegt eine endliche Zahl an Byte im
Speicher. Damit lassen sich nicht beliebig große Zahlen in einem
Integer-Datentyp darstellen.
Angenommen, eine vorzeichenlose Integer-Größe belegt M Bit. Die
größte darstellbare Zahl lässt sich sofort angeben:
. . 111} |2 ,
|111 .{z
M
das entspricht der Zahl
M−1
X
2i = 2M − 1.
i=0
Sollen positive wie negative Zahlen dargestellt werden, muss ein Bit
für das Vorzeichen reserviert werden.
Integer-Datentypen
Die Integer-Datentypen zerfallen in zwei Klassen: solche, die
negative wie positive Werte darstellen können (signed), und solche,
die ausschließlich positive Werte annehmen (unsigned).
Sämtliche Integer-Datentypen in C sind:
/* signed integer types */
/* unsigned integer types */
signed char
unsigned char
short int
unsigned short int
int
unsigned int
long int
unsigned long int
long long int
unsigned long long int
Die verschiedenen Datentypen haben zum Teil eine unterschiedliche
Speichergröße und damit auch unterschiedlich große Wertebereiche.
Speichergrößen ermitteln
Die Speichergröße in Byte eines Datentyps, einer Variablen oder
Konstanten lässt sich mit dem sizeof-Operator ermitteln, z. B.
printf("sizeof(int) = %zu Byte\n", sizeof(int));
printf("sizeof(double) = %zu Byte\n", sizeof(double));
Der Standard macht nur „weiche“ Vorgaben zu den Größen der
einzelnen Datentypen. Je nach System können diese unterschiedlich
ausfallen:
Datentyp
char
short int
int
long int
long long int
Größe (PC, 32 Bit)
Größe (PC, 64 Bit)
1 Byte
2 Byte
4 Byte
4 Byte
8 Byte
1 Byte
2 Byte
4 Byte
8 Byte
8 Byte
Tabelle : Größe in Byte der Integer-Datentypen
Ganzzahlüberlauf
Lässt sich eine Zahl nicht mehr darstellen, spricht man von einem
Ganzzahlüberlauf.
Wir können diesen Fall künstlich herbeiführen:
int n = (int)(pow(2.,31.)-1.); /* n = 2^31 - 1
printf("%d\n", n );
/* Output: 2147483647
printf("%d\n", n+1);
/* Output: -2147483648
*/
*/
*/
Ganzzahlüberlaufe können insbesondere bei Schleifen-Anweisungen
mit grossen Datenmengen auftreten.
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
Normalisierte Gleitpunktzahlen
zu x ∈ R existiert die Darstellung:
∞
X
x = (−1) (
xk 2−k )2e
s
k =1
mit s ∈ {0, 1}, Ziffern xk ∈ {0, 1} und Exponent e ∈ Z.
normierte Gleitpunktzahlen eine Teilmenge von R für die die
Darstellung:
P
X
x = (−1)s (
xk 2−k )2e
k =1
existiert, mit P ∈ N, s ∈ {0, 1}, x0 = 1 und x1 , . . . , xP ∈ {0, 1} und
Exponent e ∈ [−emin , emax ].
Normalisierte Gleitpunktzahlen
I
Die Zahl
m = x1 .x2 . . . xP
heißt Mantisse von x. Die festgewählte Zahl P nennt man
Mantissenlänge.
I
Eine Gleitpunktzahl x ist festgelegt durch Vorzeichen, Mantisse
und Exponent:
x = (s, m, e).
Normalisierte Gleitpunktzahlen
Anweisung:
double x;
I
Bedeutet Deklaration einer Variablen, bedeuted Angabe eines
Datentyps und eines Bezeichners:
I
Bei Deklaration wird dem Datentyp (double = 64 Bit)
entsprechend Speicher reserviert:
e0
e1
e2
e3
e4
e5
e6
e7
e8
e9
e10
e11
e12
e13
e14
e15
e16
e17
e18
e19
e20
e21
e22
e23
e24
e25
e26
e27
e28
e29
e30
e31
e32
e33
e34
e35
e36
e37
e38
e39
e40
e41
e42
e43
e44
e45
e46
e47
e48
e49
e50
e51
e52
e53
e54
e55
e56
e57
e58
e59
e60
e61
e62
e63
I
mit ei ∈ {0, 1} zufällige (uninitialisierte) Werte.
Normalisierte Gleitpunktzahlen
Anweisung:
double x = constValue;
constValue wird gerundet auf die nächstegelegene Gleitpunktzahl:
x = (−1)s (
52
X
xk 2−k )2e , mit e ∈ [−1022, 1023]
k =1
s
n0
n1
n2
n3
n4
n5
n6
n7
n8
n9
n10
x1
x2
x3
x4
x5
x6
x7
x8
x9
x10
x11
x12
x13
x14
x15
x16
x17
x18
x19
x20
x21
x22
x23
x24
x25
x26
x27
x28
x29
x30
x31
x32
x33
x34
x35
x36
x37
x38
x39
x40
x41
x42
x43
x44
x45
x46
x47
x48
x49
x50
x51
x52
I
Vorzeichen (1 Bit), s ∈ {0, 1} ⇒ (−1)s = ±1
I
Exponent (11 Bit), n0 , . . . , n10 ∈ {0, 1} entspricht
Binärdarstellung von e.
I
Mantisse (52 Bit), x1 , . . . , x52 ∈ {0, 1}
Arithmetik von Gleitpunkt-Zahlen
Aufgrund ihrer Darstellung kommt es bei Gleitpunkt-Zahlen zu einer
Reihe unintuitiver Phänomene, die das Ergebnis einer Berechnung
beeinflussen.
Die Addition von Gleitpunkt-Zahlen ist nicht assoziativ, d. h. im
Allgemeinen gilt nicht:
(a + b) + c = a + (b + c).
1
#include <stdio.h>
2
3
4
5
int main(void)
{
double a = 0.1, b = 0.2, c = 0.3;
6
/* print up to 20 decimal places */
printf("(a+b)+c = %1.20lf\n", (a+b)+c);
printf("a+(b+c) = %1.20lf\n", a+(b+c));
7
8
9
10
return 0;
11
12
}
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
Ausdrücke
Ein Ausdruck ist eine Kombination von Variablen, Konstanten und
Operatoren:
I
Beispiel: x = 3 + y;
I
Durch Operatoren werden bestehende Ausdrücke (Argumente)
zu komplexeren Ausdrücken verknüpft.
Die Auswertung eines Ausdrucks liefert immer
I
einen Typ
I
und einen Wert.
Wir müssen in der Lage sein, zusammengesetzte Ausdrücke zu
zerlegen und ihren Typ und Wert zu bestimmen.
Elementare Ausdrücke – Konstanten
Bei Angaben von Werten, z. B.
0
1
3.14159265358979
/* integer type constant */
/* integer type constant */
/* floating point type constant */
handelt es sich um konstante (numerische) Ausdrücke.
Konstanten haben einen Typ, der sich aus ihrem Wert ableiten lässt.
Typ und Wert des Ausdrucks stimmen mit denen der Konstante
überein.
Durch Angabe zusätzlicher Attribute kann ein Integer-Typ näher
spezifiziert werden:
0u
1l
/* constant of type unsigned int */
/* constant of type long int */
Elementare Ausdrücke – Variablen
Variablen haben vom Moment ihrer Definition an einen Typ und einen
Wert:
int a = 1;
double x; /* value of x is unspecified */
Namen von Variablen
a
x
/* expression of type int, value 1 */
/* expression of type double and random value */
entsprechen Ausdrücken, die in Typ und Wert mit denen der Variable
übereinstimmen.
Elementare Ausdrücke – Funktionsaufrufe
Funktionen in C haben immer einen Rückgabetyp. Im Moment
können wir auf den Rückgabetyp nur schließen:
I
Rückgabetyp double:
ceil, fabs, floor, sqrt, ...
I
Rückgabetyp int:
abs, printf, ...
Funktionsaufrufe sind Ausdrücke der Form
sqrt(2.)
abs(-1)
die in Typ und Wert stimmt mit Rückgabetyp und -wert (falls
vorhanden) der Funktion übereinstimmen.
Unäre, binäre und ternäre Operatoren
Durch Operatoren werden Ausdrücke (Argumente, Operanden)
verknüpft und zu zusammengesetzten Ausdrücken. Operatoren
können ein, zwei oder drei Argumente haben.
Ein Operator heißt. . .
unär, falls der Operator auf nur einen Operanden wirkt.
Beispiel: +, - (Vorzeichenoperatoren),
binär, falls der Operator auf zwei Operanden wirkt.
Beispiel: +, -, *, / % (arithmetische Operatoren),
ternär, falls der Operator auf drei Operanden wirkt.
Beispiel: ?: (Konditionaloperator).
Beispiele zusammengesetzter Ausdrücke
Die arithmetischen Operatoren für Addition, Subtraktion,
Multiplikation und Division sind binäre Operatoren:
1+2
1./2.
Ist x eine Gleitpunktzahl-Variable, so ist auch der folgende Ausdruck
gültig:
x = -sqrt(2)
Der Zuweisungsoperator = ist ebenfalls binär.
Auch komplexe arithmetische Ausdrücke werden korrekt ausgewertet
(Punkt-vor-Strich-Rechnung, Klammern werden berücksichtigt):
1+(2+4)*8
/* result is 49 */
Priorität und Assoziativität von Operatoren
Typ und Wert eines zusammengesetzten Ausdrücks müssen
eindeutig bestimmt sein.
Ihre Auswertung erfolgt unter Berücksichtigung von. . .
Priorität Rangfolge, die festlegt, in welcher Reihenfolge
Operatoren innerhalb eines Ausdrucks ausgewertet
werden
Assoziativität gibt an, in welcher Richtung Operatoren und
Operanden zusammengefasst werden
Beispiel (Assoziativität von rechts nach links):
int a, b, c, d, e;
e = 2;
a = b = c = d = e;
/* a = 2, b = 2, c = 2, d = 2 */
Klammer-Operator
Der Klammer-Operator gehört zu einer Gruppen von Operatoren der
höchsten Priorität (s. Appendix). So wird sichergestellt, dass
Ausdrücke innerhalb der Klammern zuerst ausgewertet werden.
Syntax:
(expr)
Hier und im folgenden steht expr für einen gültigen Ausdruck.
Beispiel:
(2+4)*8
/* evaluates to 6*8, result is 48 */
Zuweisungen
In Zuweisung werden Werte in Variablen gespeichert. Entscheidend
ist, dass auf der linken Seite einer Zuweisung nur Variablen
(allgemeiner sog. lvalues) zugelassen sind:
x
= 1. /* Okay. */
1. = 1. /* Error! */
a*x = 1. /* Error! */
Zuweisungen sind nicht kommutativ! Die Variable, die mit einem Wert
belegt werden soll, steht links:
a = b
b = a
/* assign value of b to a */
/* assign value of a to b */
Zuweisungsoperator
Der Zuweisungsoperator
=
ist binär und hat einen linken und einen recheten Operanden.
Nach der Zuweisung, z. B.
x = 2
hat der Ausdruck den Wert und Typ des linken Operanden.
Zuweisungsoperatoren haben mit die geringste Priorität aller
Operatoren. So wird sichergestellt, dass zuerst der Ausdruck auf der
rechten Seite der Zuweisung ausgewertet wird.
Weitere Zuweisungsoperatoren
Neben dem einfachen Zuweisungsoperator
=
gibt es weitere Zuweisungsoperatoren, die wie Kurzschreibweisen für
häufig verwendete Operationen funktionieren:
x
x
x
x
x
+=
-=
*=
/=
%=
y
y
y
y
y
/*
/*
/*
/*
/*
equals:
x = x x = x *
x = x /
x = x %
x = x + y */
y */
y */
y */
y, for integers x, y only */
Priorität und Assoziativität sind genau wie bei =.
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
Vergleichsoperatoren
Objekte von numerischen Datentypen können miteinander verglichen
werden. Hierzu dienen die binären Operatoren:
expr1
expr1
expr1
expr1
expr1
expr1
< expr2
<= expr2
> expr2
>= expr2
== expr2
!= expr2
/*
/*
/*
/*
/*
/*
less than */
less than or
greater than
greater than
equal to */
not equal to
equal to */
*/
or equal to */
*/
Beispiel:
double x = 1., y = 2.;
x < y;
x >= y;
x == y;
x != y;
Frage: Welchen Typ und Wert haben die obigen Ausdrücke?
Logische Ausdrücke
Vergleichsoperatoren liefern einen logischen Ausdruck.
Ihre Auswertung resultiert in genau einem von zwei möglichen
Werten:
I
Der Rückgabewert ist 0, falls der Ausdruck unwahr ist,
I
Der Rückgabewert ist 1, falls der Ausdruck wahr ist.
Logische Ausdrücke sind damit vom Typ int.
Umgekehrt gilt jeder von von Null verschiedene Ausdruck als
„wahre“ Aussage. Jeder Ausdruck mit Wert Null wird als
„unwahr“ interpretiert.
Andere Programmiersprachen (z. B. C++, Java) reservieren einen
eigenen Datentyp für Wahrheitswerte.
Logische Operatoren
In C gibt es die folgenden Operatoren zur Verknüpfung logischer
Ausdrücke:
!expr
expr && expr
expr || expr
/* logical negation */
/* logical conjunction */
/* logical disjunction */
Ihr Wert ist jeweils vom Typ int und entweder 0 (falls die Aussage
unwahr ist) oder 1 (sonst).
Sogenannten Wahrheitstafeln kann man den Wert einer logischen
Verknüpfung entnehmen:
x
y
| !x
| x && y | x || y
------------+-------+---------+-------true true | false | true
| true
true false | false | false
| true
false true | true | false
| true
false false | true | false
| false
------------+-------+---------+--------
Mehrfache Vergleiche
Mehrfache Vergleiche sind in C nicht sinnvoll. Wir geben ein
einfaches Beispiel.
Es gilt
1
1
1
< < .
8
4
2
Der folgende Ausdruck hat aber den Wert 0 (die Aussage ist
„unwahr“):
0.125 < 0.25 < 0.5
denn die Vergleichsoperatoren werden von links nach rechts
abgearbeitet.
Gliederung
Rechnen mit Variablen
Darstellung von Integer-Datentypen
Gleitpunktzahlen
Ausdrücke
Bedingte Anweisungen
Einfache Verzweigung
if-Anweisung
Mit der if-Anweisung kann der Ablauf eines Programms von
Bedingungen abhängig gemacht werden.
Syntax:
if(expr)
statement
Die Semantik der Anweisung ist naheliegend:
I
Der Ausdruck expr wird ausgewertet. Der Ausdruck heißt
„wahr“, falls er ungleich Null ist.
I
Falls expr wahr ist, wird die folgende Anweisung statement
ausgeführt.
Der Ausdruck expr heißt Kontrollausdruck.
Zusammenfassen von Anweisungen, Blöcke
Sollen mehrere Anweisungen abhängig vom Kontrollausdruck
ausgeführt werden, müssen sie in einem Block zusammengefasst
werden:
Syntax:
if(expr)
{
statements
...
}
Beispiel:
int a, b, c, d;
...
if(a > b)
{
c = a - b;
d = sqrt(c);
return d;
}
Programmbeispiel
Quelltext (Wurzelberechnung mit Fehlerbehandlung)
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
#include <math.h>
#include <stdio.h>
int main(void)
{
/* declare floating point variable */
double x;
/* read from user input */
printf ("x = ");
scanf("%lf", &x);
/* check if value greater than 0 */
if( x < 0 )
{
printf("value x is less than zero\n");
return 0;
}
/* print value of x and square root */
printf ("x = %f\n", x);
printf ("sqrt(x) = %f\n", sqrt(x));
return 0;
}
if-else-Anweisung
Optional kann die if-Anweisung um den sog. else-Zweig erweitert
werden.
if(expr)
statement
else
statement
Ob die Anweisungen des if- oder des else-Zweigs bearbeitet
werden, hängt vom Kontrollausdruck ab:
I
Ist der Kontrollausdruck wahr, so wird der if-Zweig bearbeitet,
I
sonst werden die Anweisungen des else-Zweigs bearbeitet.
Sowohl im if- als auch im else-Zweig können mehrere
Anweisungen zu jeweils einem Block zusammengefasst werden.
Programmbeispiel
Quelltext (Wurzelberechnung mit Fehlerbehandlung)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <math.h>
#include <stdio.h>
int main(void)
{
/* declare floating point variable */
double x;
/* read from user input */
printf ("x = ");
scanf("%lf", &x);
if( x < 0 )
{
printf("value x is less than zero\n");
}
else
{
printf ("x = %f\n", x);
printf ("sqrt(x) = %f\n", sqrt(x));
}
return 0;
}
Herunterladen