Informatik B, Sommer 1998

Werbung
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
1
13. Gleichung 2. Grades
Aufgabe
Berechne x aus 2 x 2 + 5 x − 3 = 0
Algorithmus
Nach der ‘‘Mitternachtsformel’’ gilt
x 1,2 =
−5 ± √
52 − 4 (−3) 2

2×2
Programm — scalar/qgl0.c
#include <math.h>
#include <stdio.h>
int main (void)
{
printf("Loesungen:\n");
printf("%g\n", (−5.0 + sqrt(5.0 * 5.0 − 4.0 * (−3.0) * 2.0))
/ (2.0 * 2.0));
printf("%g\n", (−5.0 − sqrt(5.0 * 5.0 − 4.0 * (−3.0) * 2.0))
/ (2.0 * 2.0));
return 0;
}
Hilfsmittel
#include <math.h>
printf("%g\n", ... );
\n
5.0 * 5.0
sqrt( positiv )
gcc ... -lm
mathematische Büchereifunktionen deklarieren
double Ausgabe
Zeilentrenner (in Ausgabetext)
Potenzieren geht nicht
berechnet Quadratwurzel (von double)
mathematische Büchereifunktionen einbinden
Analyse
math.h deklariert double sqrt(double); folglich könnte man (in ANSI C) auch mit Integer-Konstanten arbeiten
(siehe scalar/qgl1.c).
Die Lösung ist viel zu speziell!
•
Unabhängigkeit des Programms von Koeffizienten.
•
Erkennung besonderer Fälle: keine quadratische Gleichung; keine reellen Wurzeln; zwei gleiche Wurzeln.
14. Gleichung 2. Grades
Allgemeinere Aufgabe
Wir lösen
a x2 + b x + c = 0
Algorithmus
2
15. Arithmetik-Begriffe
Festlegen der Zahlen
a, b, c
Bestimmen der Wurzel
w=√
b2 − 4ac

(denn sie kommt in beiden Lösungen vor)
Ausgabe der Lösungen
x1 = (-b + w) / 2a
x2 = (-b - w) / 2a
Programm — scalar/qgl2.c
#include <math.h>
#include <stdio.h>
int main (void)
{
double a, b, c;
double w;
double x1, x2;
/* Koeffizienten */
/* Wurzel */
/* Loesungen */
a = 2.0; b = 5.0; c = − 3.0;
printf("Koeffizienten %g %g %g\n", a, b, c);
w = sqrt(b * b − 4 * a * c);
x1 = (−b + w) / (2 * a);
x2 = (−b − w) / (2 * a);
printf("Loesungen %g %g\n", x1, x2);
return 0;
}
Funktionen
Siehe
$ man sqrt
$ man sin
15. Arithmetik-Begriffe
Variable
Eine modifizierbare Größe, deren Namen und Typ vor Gebrauch vereinbart werden:
double a, b, c;
a = 1.2;
printf("%g\n", a);
/* Vereinbarung */
/* Zuweisung */
/* Gebrauch */
Name beginnt mit Buchstabe, besteht aus Buchstaben und Ziffern. Kann kein reserviertes Wort sein (double, o.ä.).
Große und kleine Buchstaben sind verschieden, _ (Unterstrich) ist Buchstabe. Traditionellerweise verwendet man in C
Kleinbuchstaben für Variablennamen und Großbuchstaben für symbolische Konstanten.
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
3
Konstante
Eine unveränderliche Größe, deren Name sich oft direkt aus ihrem Wert ergibt:
1 2.3 ’a’ "abc"
Mit dem Attribut const können Konstanten auch als nicht modifizierbare Variablen vereinbart werden:
const double a = 1.2;
/* muss initialisiert sein */
Zuweisung
Aktion, durch die eine Variable einen neuen Wert erhält:
double a;
a = 1.3;
An Konstanten (const-Variablen) darf man nicht zuweisen.
Zuweisung ist keine Gleichung
double a = 1.0; /* initialisiert */
a = a + 1.0;
/* anschliessend gilt a == 2.0 */
Bewertet wird immer der arithmetische Ausdruck rechts, und zwar mit den alten Variablenwerten; erst dann wird an die
Variable links zugewiesen.
Das Resultat der Zuweisung ist der zugewiesene Wert.
16. Arithmetik-Begriffe
Definition
Vereinbarung eines Namens (frei erfunden), Verknüpfung des Namens mit einem Datentyp und mit Attributen wie
const, Erzeugung der Datenfläche für den Wert, evtl. Initialisierung.
Das Ganze steht meistens am Anfang einer Funktion, vor Aktionen, die darauf Bezug nehmen:
int main (void)
{
double a; const double b = 1.3;
a = b;
Definitionen in C++ — scalar/qgl3.c
In C++ kann man Definitionen und Anweisungen mischen. Eine Definition gilt dann bis zum Ende des Blocks.
#include <math.h>
#include <stdio.h>
int main (void)
{
const double a = 2.0, b = 5.0, c = − 3.0;
printf("Koeffizienten %g %g %g\n", a, b, c);
const double w = sqrt(b * b − 4 * a * c);
const double x1 = (−b + w) / (2 * a);
const double x2 = (−b − w) / (2 * a);
printf("Loesungen %g %g\n", x1, x2);
return 0;
4
17. Ausgabe
}
Da man Konstanten initialisieren darf (nur nicht später verändern!), kommt man hier total mit Konstanten aus.
Vorteil: Man kann Variablen direkt am Ort der Tat vereinbaren.
$ g++ qgl3.c −o qgl3 −lm
17. Ausgabe
printf()
Mit dieser Funktion kann man formatiert ausgeben, das heißt, (konstanten) Text vermischt mit den Werten von arithmetischen Ausdrücken:
const double a = 1.2;
const int b = 10;
printf("a hat den Wert %g\n", a);
printf("b hat den Wert %d\n", b);
Formatelemente
printf(Format, Wert ...);
Format ist ein Text, der unverändert erscheint, mit Formatelementen dort, wo jeweils ein Wert erscheinen soll:
%c
%d
%g
%s
%%
’a’
int o.ä.
double
"abc"
Zeichen
dezimal, ganzzahliger Wert
dezimal, Gleitkomma-Wert
Text
% selber
Man kann dabei Breite, Ausrichtung, Auffüllen mit führenden Nullen, Anzahl Dezimalstellen, usw. kontrollieren.
Siehe
$ man printf
Vorsicht
Für jedes Formatelement muß unbedingt ein Wert als Argument für printf() vorhanden sein — sonst gibt es im
Ernstfall Tränen.
18. Eingabe
Kochrezept
int i; double d;
scanf("%d", & i);
scanf("%lf", & d);
scanf() wandelt Text aus der Eingabe um und weist die Werte an Variablen zu, die (mit &) als Argumente angegeben
sind.
Zwischenraum und Zeilentrenner werden ignoriert, andere Zeichen müßten im Format vorkommen.
Formatelemente
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
Element
Wert-Typ
%c
%d
%lf
char int o.ä.
int o.ä.
double
5
Umwandlung
Zeichen (auch Zwischenraum)
dezimal, ganzzahliger Wert
dezimal, Gleitkomma-Wert
Man kann dabei Breite u.ä. kontrollieren.
Siehe
$ man scanf
Vorsicht
Für jedes Formatelement muß unbedingt ein Name (mit &) als Argument für scanf() vorhanden sein — sonst gibt es
im Ernstfall Tränen.
19. Gleichung 2. Grades
Analyse
Der bisherige Algorithmus hat einen massiven Fehler. Die Gleichung
x2 + x + 1 = 0
hat keine reellen Lösungen, die Eingabe
1.0
1.0
1.0
führt in
w = sqrt(b * b − 4 * a * c);
zur Berechnung der Wurzel von -3.0, also zu einer Fehlermeldung.
Verbesserter Algorithmus
Festlegen der Zahlen
a, b, c
Bestimmen der Diskriminante
d = b2 − 4ac
Entscheidung: gilt
d≥0
ja
nein
w=√
d
Lösungen
x1 = (-b - w) / 2a
x2 = (-b + w) / 2a
Es gibt keine
reellen Lösungen
6
20. Gleichung 2. Grades
20. Gleichung 2. Grades
Programm — scalar/qgl4.c
#include <math.h>
#include <stdio.h>
int main (void)
{
double a, b, c;
double d;
double x1, x2;
/* Koeffizienten */
/* Diskriminante */
/* Loesungen */
printf("Koeffizienten ? ");
scanf("%lf %lf %lf", & a, & b, & c);
d = b * b − 4 * a * c;
if (d < 0.0)
printf("Es gibt keine reellen Loesungen\n");
else
{
d = sqrt(d);
x1 = (−b + d) / (2 * a);
x2 = (−b − d) / (2 * a);
printf("Loesungen %g %g\n", x1, x2);
}
return d < 0.0;
/* 0: ok, 1: keine Loesungen */
}
Weitere Schwachstellen
a == 0.0
Dann liegt eine lineare Gleichung vor — mit einer Lösung.
a und b == 0.0
Dann sollte c Null sein — Lösungen gibt’s keine.
d == 0.0
Dann gibt es eine (zweifache) Nullstelle.
b ungefähr == d
Dann löscht sich die Differenz einmal fast aus, d.h., dann ist eine Lösung arg falsch.
Beispiel:
x 2 + 10n x + 1 = 0
21. Kontrollstrukturen
Zusammenfassung
Aktion 1
Aktion 2
Aktion 3
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
7
{
Aktion 1 ;
Aktion 2 ;
Aktion 3 ;
}
Die Kontrollstruktur { Aktionen } faßt viele Aktionen (Anweisungen) in eine zusammen.
Entscheidungen
Bedingung
ja
nein
Aktion
if ( Bedingung )
Aktion ;
Die if-Kontrollstruktur führt eine Aktion in Abhängigkeit vom Zutreffen einer Bedingung (Vergleich o.ä.) aus, oder
auch nicht.
Bedingung
ja
nein
Aktion 1
Aktion 2
if ( Bedingung )
Aktion 1 ;
else
Aktion 2 ;
Die if-else-Kontrollstruktur führt eine von zwei Aktionen in Abhängigkeit vom Zutreffen einer Bedingung (Vergleich o.ä.) aus.
Hinweis
Die abhängige Anweisung kann immer eine Zusammenfassung sein, kann also aus mehreren Aktionen in { } bestehen.
22. Kommandozeile
Koeffizienten im Aufruf — scalar/qgl5.c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
/* sqrt */
/* atof */
int main (int argc, char * argv[])
{
double a, b, c;
/* Koeffizienten */
double d;
/* Diskriminante */
double x1, x2;
/* Loesungen */
if (argc < 4)
printf("Aufruf: %s a b c\n", argv[0]);
else
{
a = atof(argv[1]);
8
23. Quadratwurzeln bestimmen
b = atof(argv[2]);
c = atof(argv[3]);
printf("Koeffizienten %g %g %g\n", a, b, c);
d = b * b − 4 * a * c;
if (d < 0.0)
printf("Es gibt keine reellen Loesungen\n");
else
{
d = sqrt(d);
x1 = (−b + d) / (2 * a);
x2 = (−b − d) / (2 * a);
printf("Loesungen %g %g\n", x1, x2);
return 0;
}
}
return 1;
}
Redewendungen
int main (int argc, char * argv[])
Jedes C Programm erhält die Worte der Kommandozeile als Parameter des Hauptprogramms.
argc
ist die Anzahl der Worte (also wenigstens 1 für den Kommandonamen).
argv
ist der Vektor der Worte als ‘‘Strings’’.
23. Quadratwurzeln bestimmen
Geometrische Idee
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
9
y
y0
x
x1
x0
−A
Gesucht ist eine Nullstelle der Funktion
y = x2 − A
Ausgehend von einem beliebigen Punkt
x0 ≥ √
A
ersetzen wir die Kurve durch ihre Tangente
y − y 0 = y 0 ′ (x − x 0 )
und finden als Schnittpunkt mit der x-Achse
y1 = 0
x1 = x0 −
y0
y0 ′
Hier gilt also
x1 = x0 −
(Newton Verfahren)
24. Quadratwurzeln bestimmen
Algorithmus
x02 − A
A
= (x 0 + ) / 2
2 x0
x0
10
25. Quadratwurzeln bestimmen
Argument der Wurzel: A
Genauigkeit: t
Erste Approximation
w1 = A
wiederholen
Approximation merken
w0 = w1
Neue Approximation berechnen
w1 = ( w0 + A / w0 ) / 2
bis w0 - w1 < t
Lösung ist (etwa) w1
Mathematisches
Natürlich gilt für A als erste Approximation A ≥ √
A (falls A ≥ 1).
Man kann zeigen, daß immer
w0 > w1 > √
A
und
w0 − w1 > w1 − √
A
gilt — damit kann man den Fehler abschätzen.
25. Quadratwurzeln bestimmen
Programm — scalar/newton.c
int main (void)
{
double A, t;
double w0, w1;
/* Argument, Fehler */
printf("Argument der Wurzel ? ");
scanf("%lf", & A);
printf("Fehlergrenze ? ");
scanf("%lf", & t);
w1 = A;
do
{
w0 = w1;
w1 = (w0 + A / w0) / 2.0;
} while (w0 − w1 > t);
printf("Wurzel: %g\n", w1);
printf("Quadrat: %g\n", w1 * w1);
return 0;
}
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
11
26. Kontrollstrukturen
Wiederholungen
wiederholen
Aktion
während
Bedingung erfüllt
do
Aktion ;
while ( Bedingung );
Die do-while-Kontrollstruktur führt eine Aktion wenigstens einmal aus, und dann noch solange eine Bedingung erfüllt
ist.
während
Bedingung erfüllt
Aktion
while ( Bedingung )
Aktion ;
Die while-Kontrollstruktur führt eine Aktion aus, falls und dann solange eine Bedingung erfüllt ist. (Also auch gar
nicht.)
Vorsicht
while und do-while unterscheiden sich nur in einem Semikolon.
Nur ein Semikolon (keine Aktion) ist auch eine Aktion!
27. Euklid’s Algorithmus
Aufgabe
Größter gemeinsamer Teiler zweier natürlicher Zahlen.
Idee: Primzahlzerlegung
ggT besteht aus den Primfaktoren, die beide Zahlen gemeinsam haben.
54 ≡ 2 × 3 × 3 × 3
36 ≡ 2 × 2 × 3 × 3
Sehr problematisch zu berechnen.
Idee: Differenzen
Jeder Teiler von a und b teilt auch die (positive) Differenz:
12
28. Euklid’s Algorithmus
a und b festlegen
a und b verschieden
a ?? b
>
<
neue Aufgabe:
b und (a − b)
neue Aufgabe:
a und (b − a)
ggT ist a
Abbruchkriterium
Alte Aufgabe: a und b, verschieden.
Neue Aufgabe: a und (b − a) falls b > a.
Die Summe der Zahlen fällt monoton gegen Null:
a + (b − a) ≡ b < a + b
Das geht nur für eine endliche Anzahl von Schritten.
28. Euklid’s Algorithmus
Programm — scalar/euklid0.c
int main (void)
{
int x, y;
scanf("%d %d", & x, & y);
printf("ggT(%d, %d) == ", x, y);
while (x != y)
if (x > y)
x = x − y;
else
y = y − x;
printf("%d\n", x);
return 0;
}
Robustere Eingabe — scalar/euklid1.c
while (1)
{
printf("Bitte zwei Zahlen eingeben: ");
if (scanf("%d %d", & x, & y) < 2)
break;
if (x <= 0 || y <= 0)
printf("beide muessen positiv sein\n");
else
{
...
}
}
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
13
Nicht-positive Werte werden abgewehrt.
Immer neue Aufgaben — Abbruch nur mit Interrupt-Taste oder am Dateiende (control-D).
scanf() liefert die Anzahl der zugewiesenen Werte.
break
break; bricht die umgebende Schleife ab.
break vereinfacht oft Schleifenbedingungen.
29. Rekursion
Lösung mit rekursiver Funktion — scalar/euklid2.c
#include <stdio.h>
#include <stdlib.h>
/* atoi */
int ggT (int x, int y)
{
if (x == y)
return x;
if (x > y)
return ggT(x−y, y);
return ggT(x, y−x);
}
int main (int argc, char * argv[])
{
int x, y;
if (argc < 3)
return printf("Aufruf: %s x y\n", argv[0]);
x = atoi(argv[1]);
y = atoi(argv[2]);
if (x <= 0 || y <= 0)
return printf("Zahlen muessen positiv sein\n");
printf("ggT(%d, %d) == %d\n", x, y, ggT(x, y));
return 0;
}
Funktionen können rekursiv aufgerufen werden.
30. Bit-Operationen
Wertebereich der Integer-Typen
Verschiebt man Bits nach links und schiebt Bits mit Wert 1 nach, wird der repräsentierte Wert zunächst immer größer.
0 0
1 1
3
0 0
1 1 1
7
Wenn das letzte Bit erreicht wird, entsteht entweder ein kleiner Wert (0 bei 1-Komplement oder -1 bei 2-Komplement),
oder der letzte Wert ist positiv und wird nicht mehr größer.
14
31. Bit-Operationen
1 1
1 1 1
≤0
Gibt es negative Werte, so kann man dann Bits mit Wert 0 nachschieben, bis der kleinste mögliche Wert entsteht.
1 0
0 0 0
←0
int — scalar/int0.c
int main (void)
{
int i, j = 1;
do
{
i = j;
j = i << 1 | 1;
} while (j > i);
printf("max: %d, ", i);
do
{
i = j;
j = i << 1;
} while (j < i);
printf("min: %d\n", i);
return 0;
}
<< schiebt nach links.
| ist die Bit-ODER-Verknüpfung.
Vorrang: Arithmetik vor Verschieben vor Vergleichen vor Bit-Operationen.
31. Bit-Operationen
unsigned — scalar/int1.c
unsigned stellt nur natürliche Zahlen dar. Die negativen Werte von int repräsentieren dabei entsprechend größere
Zahlen.
int main (void)
{
unsigned i, j = 1;
...
if (i == j)
i = 0;
else
...
}
Integer-Typen
Ganzzahlige Werte gibt es als char, short und long, jeweils signed und unsigned.
Ob char ein Vorzeichen hat oder nicht, hängt von der Maschine ab. Mit signed kann man es erzwingen, mit unsigned kann man es verhindern.
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
15
int und unsigned sind je nach Maschine entweder long oder short.
Falls die Anwendung es zuläßt (Wertebereich klein genug, als char nur ASCII-Zeichen) sollte man aus Effizienzgründen int, unsigned und char einsetzen.
Konstanten
Vorsicht, die Regeln sind trickreich:
•
dezimale Konstanten sind je nach Größe und Maschine entweder int, long oder unsigned long.
•
hexadezimale und oktale Konstanten können dazwischen auch noch unsigned sein.
•
nnnU ist unsigned oder auch unsigned long.
•
nnnL ist long oder auch unsigned long.
•
nnnUL ist unsigned long.
Im Zweifelsfall sollte man mit et üben...
32. Bit-Operationen
Umwandlung
Kombiniert man verschiedene Integer-Typen, wird jeweils der ‘‘größere’’ Typ verwendet. Beim Übergang dazu ändern
sich Null und positive Werte nicht.
Bei negativen Werten wird das Vorzeichen propagiert. Bei short zu int zu long ändert sich der Wert nicht, bei Übergang zu unsigned resultieren stark positive Werte!
Leider ist der Begriff des ‘‘größeren’’ Typs sehr trickreich:
char
short
ushort
int
unsigned
long
ulong
int
int
int
?
?
?
int
int
?
int
unsigned
unsigned
unsigned
unsigned
unsigned
long
long
long
long
?
long
ulong
ulong
ulong
ulong
ulong
ulong
ulong
char
short
ushort
int
unsigned
long
ulong
•
short und alle char werden immer in int verwandelt.
•
unsigned short wird in int oder in unsigned verwandelt (wenn int nicht den kompletten Wertebereich von
unsigned short umfaßt, d.h. bei kleinen int).
•
werden unsigned und long Operanden verknüpft, ist das Resultat unsigned long oder auch long (wenn
long den kompletten Wertebereich von unsigned umfaßt; kleine int).
Das kann böse Tränen geben:
$ et −v
short
−3L < 2U
− (long) 3 −> (long) −3
(unsigned) 2 −> (long) 2
(long) −3 < (long) 2 −> (int) 1
−> (int) 1
long
−3L < 2U
− (long) 3 −> (long) −3
(long) −3 −> (unsigned long) 4294967293
(unsigned) 2 −> (unsigned long) 2
(unsigned long) 4294967293 < (unsigned long) 2 −> (int) 0
−> (int) 0
16
33. Bit-Operationen
33. Bit-Operationen
Alle Wertebereiche — scalar/int2.c
Prinzipiell kann man scalar/int1.c für die verschiedenen Integer-Typen vervielfältigen. Einfacher geht das mit dem C
Preprozessor:
int main (void)
{
TEST(char);
TEST(unsigned char);
TEST(signed char);
TEST(short);
TEST(unsigned short);
TEST(int);
TEST(unsigned);
TEST(long);
TEST(unsigned long);
return 0;
}
TEST hat einen Datentyp als Argument, kann also keine C Funktion sein.
34. Bit-Operationen
Der C Preprozessor
#define name ersatztext
#define name(parm, ...) ersatztext
Mit #define vereinbart man einfachen oder auch parametrisierten Ersatztext. Zeilen setzt man mit \ fort.
#define TEST(INTEGER)
{
INTEGER i, j = 1;
printf("%−20s", #INTEGER ":");
do
i = j, j = i << 1 | 1;
while (j > i);
printf("max: %lu, ", (unsigned long) i);
if (i == j)
i = 0;
else
do
i = j, j = i << 1;
while (j < i);
printf("min: %ld\n", (long) i);
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
}
Bei ANSI C werden Parameter-Namen im Ersatztext nur außerhalb von Strings erkannt.
Mit # kann man sie aber in Strings verwandeln. Aufeinanderfolgende String-Konstanten werden bei der Übersetzung
verkettet.
%-20s gibt eine Zeichenkette auf 20 Spalten linksbündig aus.
C-Kurs −− © 1991, 1997 Axel T. Schreiner, © 1998 Bernd Kühl
17
Damit die Ausgabe unabhängig von der Maschine korrekt funktioniert, muß man die Werte in (long) oder (unsigned
long) verwandeln und mit %ld oder %lu formatieren.
( typ ) ausdruck
ist eine unitäre Umwandlungsoperation (mit hohem Vorrang).
35. Bit-Operationen
Weitere Bit-Operationen
Für alle Integer-Typen gibt es folgende Operationen:
˜
<<
>>
&
ˆ
|
Bit-Komplement
nach links schieben
nach rechts schieben
UND-Verknüpfung
exklusive ODER-Verknüpfung
(inklusive) ODER-Verknüpfung
Bei unsigned-Typen wird bei >> von links her 0 nachgeschoben, das heißt, es resultiert eine Division durch Zweierpotenzen.
Andernfalls kann auch 1 nachgeschoben werden, das heißt, das Resultat ist bei negativen Werten maschinenabhängig.
Versuche mit et:
>>1
signed
(char) 128 >> 3
−> (int) −16
unsigned
(char) 128 >> 3
−> (int) 16
// 1 nachschieben
// char ist signed
// char ist unsigned
>>0
// 0 nachschieben
signed
// char ist signed
long
// int ist long
(char) 128 >> 3
−> (int) 536870896
hex
// hexadezimale Ausgabe
+
// mit Ablaufverfolgung
(char) 128 >> 3
(char) (int) 0x80 −> (int) 0xffffff80
(int) 0xffffff80 >> (int) 0x3 −> (int) 0x1ffffff0
−> (int) 0x1ffffff0
36. Bit-Operationen
Wertebereiche als Definitionen — scalar/int3.c
Mit den Bit-Operationen kann man die Wertebereiche ohne Schleifen definieren:
#define max(type, utype)
\
((type) ˜0L > 0 ? (type) ˜0L : (utype) ˜0L >> 1)
#define min(type, utype)
\
((type) ˜0L > 0 ? 0 : (type) ˜ ((utype) ˜0L >> 1))
#define TEST(type, utype)
\
18
36. Bit-Operationen
printf("%−20smax: %lu, min: %ld\n", #type ":",
(unsigned long) max(type, utype),
(long) min(type, utype))
\
\
int main (void)
{
TEST(char, unsigned char);
TEST(unsigned char, unsigned char);
TEST(signed char, unsigned char);
TEST(short, unsigned short);
TEST(unsigned short, unsigned short);
TEST(int, unsigned);
TEST(unsigned, unsigned);
TEST(long, unsigned long);
TEST(unsigned long, unsigned long);
return 0;
}
Das Maximum besteht aus Bits mit Wert 1; bei signed-Typen muß man das Vorzeichen löschen.
Das Minimum ist Null; bei signed-Typen muß man das Maximum genau komplementieren.
Das Vorzeichen löscht man durch Verschieben eines unsigned-Typs.
Die 1-Bits erhält man als Komplement von Null. Ein signed-Typ liegt vor, wenn dieses Komplement nicht positiv ist.
bedingung ? ausdruck1 : ausdruck2
Diese bedingte Bewertung liefert den Wert eines der beiden Ausdrücke in Abhängigkeit von der Bedingung.
Herunterladen