Für den Wert einer Zahl Z = an an-1 ... a0 a-1 ... a-m in einem Stellenwertsystem zur Basis B gilt:

wobei für die Ziffern a gilt: 0 <= a < B. Als Basis B bezeichnet man die kleinste, nicht mehr durch eine Ziffer darstellbare Zahl.
Am geläufigsten ist uns das Dezimalsystem mit der Basis 10 (die Festlegung ist rein willkürlich und auf die Zahl der Finger beider menschlicher Hände zurückzuführen).
Die verkürzte Schreibweise durch Aneinanderreihung von Ziffern ist eine abkürzende Schreibweise der Summenformel. Damit kann jede positive und negative reelle Zahl dargestellt werden, indem jede Stelle der Ziffernfolge mit einer Zehnerpotenz gewichtet wird.
allgemein:
an an-1 ... a1 a0 = an * 10n + an-1 * 10n-1 + ... + a1 * 101 + a0 * 100
allgemein: 0, a-1 a-2 ... a-m = a-1 * 10-1 + a-2 * 10-1 + ... + a-m * 10-m
Dezimalzahl:
Radixschreibweise: Z = an ... a0 a-1 ... a-m
Potenzschreibweise: Z = an * 10n + ... + a1 * 101 + a0 * 100 + ... + a-m * 10-m

Bei der Eingabe von Oktalzahlen müssen diese - zur Unterscheidung von Dezimalzahlen - gekennzeichnet werden. Dies geschieht durch Hinzustellen der Basis, z. B.: 125178. Häufig (besonders bei Programmiersprachen) auch durch Voranstellen von @ oder Anfügen von O,Q,C z.B.: @154 154O 154Q 154C und bei C durch eine führende Null; es gilt also 37 = 37 dezimal, 037 = 37 oktal = 31 dezimal.
Übliche Darstellung der eigentlich binären Information in einem Rechner (Kurzschreibweise binärer Info). Kennzeichnung bei der Programmierung durch Voranstellen von $, Anfügen von H z.B.: $FFC2 FFC2H und bei C durch Voranstellen von "0x" bzw. "0X", z. B. 0xFFC2.
Dezimal-Dual-Wandlung ganzer Zahlen (Basis B = 2)
Beispiel: Umwandlung aus dem Dualsystem
Z = 101,11 dual = ? dez.
Z = 1 * 22 + 0 * 21 + 1 * 20 + 1 * 2-1 + 1 * 212
= 4 + 0 + 1 + 1/2 + 1/4
= 5,75
Eine andere Möglichkeit ist die Anwendung des Hornerschemas. Beginnend bei der höchstwertigen Ziffer wird bei ganzen Zahlen mit der Basis des Ausgangssystems (dargestellt im Zielsystem) multipliziert und zur nachfolgenden Stelle addiert. Dies wird bis zur niederwertigsten Ziffer fortgesetzt. Das letzte Ergebnis ist die Zahl im Zielsystem.
Beispiel: 11011101 dual = ? dez.
1 1 0 1 1 1 0 1
2 * 1 + 1 = 3
2 * 3 + 0 = 6
2 * 6 + 1 = 13
2 * 13 + 1 = 27
2 * 27 + 1 = 55
2 * 55 + 0 = 110
2 * 110 + 1 = 221
Beispiel: 7C2H = ? dez.
7 C 2
16 * 7 + 12 = 124
16 * 124 + 2 = 1986
0, 1 0 1 1
1 / 2 = 0,5
( 1 + 0,5) / 2 = 0,75
( 0 + 0,75) / 2 = 0,375
( 1 + 0,375) / 2 = 0,6875
In der Datenverarbeitung führt man die Subtraktion auf die Addition einer negativen Zahl zurück. Die Darstellung negativer Zahlen erfolgt durch die sogenannte Komplementdarstellung.
Rückführung der Subtraktion auf die Addition:
_ _
a - b = a + b b ist das Komplement von b
Es stellt sich nun sofort die Frage, wie man das Komplement einer
Zahl erhält. Dazu wird die Gleichung erweitert:
a - b = a + (K - b) - K)
K ist die "Komplementärzahl". Dazu ein Beispiel:
835 835
-267 --> +733 (K - b) = 1000 - 267, K = 1000
------ -------
568 1568
-1000 (K subtrahieren)
-------
568
Die Subtraktion von K zum Schluß kann man recht einfach durch
Weglassen der vordersten Stelle beim Ergebnis realisieren.
K ist im Prinzip beliebig wählbar, jedoch würde für K = 1000 eine Zahl in Komplementdarstellung nicht von einer natürlichen Zahl unterscheidbar. Wir treffen daher zwei Vereinbarungen:
835 0835
-267 --> +9733
------ --------
568 10568
||_____Vorzeichenstelle = 0 --> Erg. positiv
|______Weglassen des ?erlaufs
267 --> 0267
-835 +9165
------ -------
-568 9432
|_____Vorzeichenstelle = 9 --> Erg. negativ
Re-Komplement: -0568
-535 9465
-267 --> +9733
------ -------
-802 19198
||_____Vorzeichenstelle = 9 --> Erg. negativ
|______Weglassen des ?erlaufs
Re-Komplement: -0802
Nun ist also auch ein negatives Ergebnis erkennbar und der Betrag
(bzw. das Endergebnis) kann durch nochmaliges Komplementieren
ermittelt werden. Interessant wird diese Methode jedoch erst im Dualsystem.
Zunächst eine allgemeine Definition. Es sind zwei Arten von Komplementen gebräuchlich. X sei eine n-stellige positive Zahl im Zahlensystem zur Basis B, dann ist:
_ n+1
B-Komplement (Zweierkomplement): X = B - X
("echtes Komplement")
_ n+1
(B-1)-Komplement (Einerkomplement): X = (B - 1) - X
("unechtes Komplement, Stellenkomplement")
Auch hier gilt: Werden alle n Stellen für den Zahlenwert benutzt,
dann ist nicht unterscheidbar, ob eine pos. Zahl oder das Komple-
ment dargestellt wird --> (n+1)-te Stelle hat VZ-Funktion.
Die Bildung des Einerkomplements geschieht durch Invertieren
jeder einzelnen Stellen (sehr einfach). Es gibt jedoch zwei "Nul-
len", für n=4: 0000 = +0, 1111 = -0. Vor allem bei EDV-Systemen
wird deshalb das Zweierkomplement verwendet. Die Bildung kann auf
zwei Wegen erfolgen:
0110 1100 X
1001 0011 1-Kompl.
+ 1 addieren
1001 0100 2-Kompl.
0110 1100 X
1001 0100 übernehmen/invertieren
1001 0100 2-Kompl.
Beispiel: 4 Stellen
0111 +7
0110 +6
0101 +5
0100 +4
0011 +3
0010 +2
0001 +1
0000 0
1111 -1
1110 -2
1101 -3
1100 -4
1011 -5
1010 -6
1001 -7
1000 -8
Die Gesamtheit der so dargestellten Zahlen nennt man konegative Zahlen (gesprochen "ko-negativ", kommt von "Komplement-negativ"). Der Vorteil der Komplementdarstellung von negativen Zahlen liegt in der Ausführbarkeit der Subtraktion als Addition.
Addiert man die beiden vierstelligen Dualzahlen 0101 und 0100, ergibt sich 1001, also -7. Das ist sicher falsch. Grund dafür ist die Überschreitung des zulässigen Zahlenbereichs. Welche Ergebnisse der Addition/Subtraktion nicht im darstellbaren Zahlenbereichzeigt folgende Tabelle:
| Vorzeichen Operanden | Vorzeichen Ergebnis |
Bereichs- Überschreitung |
| beide positiv |
positiv | nein |
| negativ | ja | |
| positiv und negativ |
positiv | nein |
| negativ | nein | |
| beide negativ |
positiv | ja |
| negativ | nein |
Merksatz: Wenn beide Operanden das gleiche Vorzeichen haben und das Ergebnis ein davon abweichendes Vorzeichen, dann gab es eine Bereichsüberschreitung (Overflow).
Die bisher gesammelten Erkenntnisse führen dann zur Rechenvorschrift für die Subtraktion durch Komplementaddition (B-Kompl.):
Darstellung mit Vorzeichen, 8 Stellen vor dem Komma und 3 Stellen dahinter (dezimal).

Die Zahl wird mit führenden Nullen dargestellt und gegebenenfalls gerundet. Im obigen Beispiel reicht der darstellbare Bereich von -99999999,999 bis +99999999,999. Der Nachteil liegt im begrenzten Zahlenbereich --> bei sehr kleinen Zahlen gehen durch die Rundung Stellen verloren (0,0009 würde z. B. zu 0), sehr große Zahlen sind nicht mehr darstellbar.
Bei der Gleitpunkt-Darstellung wird die Zahl so gespeichert, daß der Radixpunkt immer zur ersten von Null verschiedenen Ziffer gleitet. Dies erreicht man durch Abspalten einer entsprechenden Potenz der Basis:
Z = M * BE
M = 0.xxxxxxx.... 1/B <= M < 1
Da die Basis bekannt ist, kann die Zahl durch die Mantisse M und den Exponenten E dargestellt werden. --> halblogarithmische Darstellung --> normalisierte Darstellung. Die Anpassung der Gleitpunkt-Zahl an die angegebene Darstellungsform wird als "Normalisieren" bezeichnet. Beispiel:
Z = 42.5456 --> 0.425456 * 102 --> M = 425456, E = 2
Exponent: (bzw. Charakteristik) Größenordnung der Zahl
Mantisse: Angabe der gültigen Zifferndarstellungen
Die Art der Darstellung von Mantisse und Exponent ist recht unterschiedlich. Für Mantisse und Exponent wird jeweils eine feste Stellenzahl vorgegeben, in der auch das Vorzeichen von Mantisse und Exponent untergebracht werden muß. Am üblichsten ist Betrags-Vorzeichen-Darstellung der Mantisse und Exponentendarstellung mit verschobenem Wertebereich (Charakteristik) so, daß der Wert der Charakteristik immer positiv ist. Sie stellt also eine einfache Verschiebung des Wertebereichs dar und hat keinen Einfluß auf die Berechnung. Beispiel:
| Wertebereich Exponent: | -128 ... 127 |
| Charakteristik (C = E + V): | 0 ... 255 (V = 128) |
Umwandlung einer Dezimalzahl in eine Gleitpunkt-Dualzahl:
Allgemein gilt:
a + (b + c) != (a + b) + c a * (b * c) =/ (a * b) * c (a + b) * c =/ (a * c) + (b * c)Einfaches Beispiel im Dualsystem: 10,11 + 11101,0
Schon beim Angleichen geht die letzte Stelle des ersten Summanden verloren.
| 00 nul| 01 soh| 02 stx| 03 etx| 04 eot| 05 enq| 06 ack| 07 bel|
| 08 bs | 09 ht | 0a nl | 0b vt | 0c np | 0d cr | 0e so | 0f si |
| 10 dle| 11 dc1| 12 dc2| 13 dc3| 14 dc4| 15 nak| 16 syn| 17 etb|
| 18 can| 19 em | 1a sub| 1b esc| 1c fs | 1d gs | 1e rs | 1f us |
| 20 sp | 21 ! | 22 " | 23 # | 24 $ | 25 % | 26 & | 27 ' |
| 28 ( | 29 ) | 2a * | 2b + | 2c , | 2d - | 2e . | 2f / |
| 30 0 | 31 1 | 32 2 | 33 3 | 34 4 | 35 5 | 36 6 | 37 7 |
| 38 8 | 39 9 | 3a : | 3b ; | 3c < | 3d = | 3e > | 3f ? |
| 40 @ | 41 A | 42 B | 43 C | 44 D | 45 E | 46 F | 47 G |
| 48 H | 49 I | 4a J | 4b K | 4c L | 4d M | 4e N | 4f O |
| 50 P | 51 Q | 52 R | 53 S | 54 T | 55 U | 56 V | 57 W |
| 58 X | 59 Y | 5a Z | 5b [ | 5c \ | 5d ] | 5e ^ | 5f _ |
| 60 ` | 61 a | 62 b | 63 c | 64 d | 65 e | 66 f | 67 g |
| 68 h | 69 i | 6a j | 6b k | 6c l | 6d m | 6e n | 6f o |
| 70 p | 71 q | 72 r | 73 s | 74 t | 75 u | 76 v | 77 w |
| 78 x | 79 y | 7a z | 7b { | 7c | | 7d } | 7e ~ | 7f del|
| 128 | 144 | 160 | 176 ° | 192 | 208 ?/td> | 224 ?td> | 240 ?d> |
| 129 | 145 | 161 ¡ | 177 ± | 193 |/td> | 209 ?/td> | 225 ?td> | 241 ?d> |
| 130 | 146 | 162 ¢ | 178 ² | 194 ¼/td> | 210 ?/td> | 226 ?td> | 242 ?d> |
| 131 | 147 | 163 £ | 179 ³ | 195 ü/td> | 211 ?/td> | 227 ?td> | 243 ?d> |
| 132 | 148 | 164 ¤ | 180 ´ | 196 ?/td> | 212 ?/td> | 228 ?td> | 244 ?d> |
| 133 | 149 | 165 ¥ | 181 µ | 197 ?/td> | 213 ?/td> | 229 ?td> | 245 ?d> |
| 134 | 150 | 166 ¦ | 182 ¶ | 198 ?/td> | 214 ?/td> | 230 ?td> | 246 ?d> |
| 135 | 151 | 167 § | 183 · | 199 ?/td> | 215 ?/td> | 231 ?td> | 247 ?d> |
| 136 | 152 | 168 ¨ | 184 ¸ | 200 ?/td> | 216 ?/td> | 232 ?td> | 248 ?d> |
| 137 | 153 | 169 © | 185 ¹ | 201 ?/td> | 217 ?/td> | 233 ?td> | 249 ?d> |
| 138 | 154 | 170 ª | 186 º | 202 ?/td> | 218 ?/td> | 234 ?td> | 250 ?d> |
| 139 | 155 | 171 « | 187 » | 203 ?/td> | 219 ?/td> | 235 ?td> | 251 ?d> |
| 140 | 156 | 172 ¬ | 188 ¼ | 204 ?/td> | 220 ?/td> | 236 ?td> | 252 ?d> |
| 141 | 157 | 173 | 189 ½ | 205 ?/td> | 221 ?/td> | 237 ?td> | 253 ?d> |
| 142 | 158 | 174 ® | 190 ¾ | 206 ?/td> | 222 ?/td> | 238 ?td> | 254 ?d> |
| 143 | 159 | 175 ¯ | 191 ¿ | 207 ?/td> | 223 ?/td> | 239 ?td> | 255 ?d> |
| char | Character, ASCII-Zeichen; Konstanten werden in single quotes eingeschlossen, z. B. 'c'; formal: Integer mit kleinem Wertebereich; (= 1 Byte) |
| int | Integer; ganze Zahl, typischerweise von der Länge eines Maschinenwortes; (2 Byte) |
| float | Fließkommazahl (einfach genau); (4 Byte) |
| double | Fließkommazahl (doppelt genau); (8 Byte) |
| void | leer (vor allem bei Zeigern und Funktionen wichtig); |
| auto | "normale" lokale Variable (Standard-(default-)Speicherklasse,
zufällig initialisiert)
Beispiele:
int y = 1; /* global */
int main(void)
{
int x = 5; /* lokal */
printf("x=%d\n",x); /* Ausgabe: 5 */
{
int x=10;
printf("x=%d\n",x); /* Ausgabe: 10 */
}
printf("x=%d\n",x); /* Ausgabe: 5 */
}
Das folgende Programm erzeugt einen Compilerfehler: "x nicht definiert".
Da x nicht global ist, kann print() nicht darauf zugreifen.
void print(void);
int main(void)
{
int x=5;
print();
}
void print(void)
{
printf("x=%d\n",x);
}
|
| register | der "Wunsch", char- oder int-Variable
im Maschinenregister zu speichern (zufällig initialisiert)
|
| extern | lokale Deklaration von globalen Variablen (initialisiert mit 0)
Beispiel: Text einlesen und die Häufigkeit der Zeichen zählen
#include <stdio.h>
#define MAXCHARS 255
int count[MAXCHARS]; /* global */
void print_alle (void);
void main(void)
{
extern int count[]; /* Deklaration von count */
int c;
while((c=getchar()) != EOF)
count[(char) c]++ ;
print_alle();
}
void print_alle(void) /* kann z. B. in einer anderen Datei stehen */
{
extern int count[]; /* Deklaration von count */
int i;
for(i = 0; i < MAXCHARS; i++)
printf("%d: %d\n",i,count[i]);
}
|
| static | "statische" Variable, Inhalt bleibt erhalten (initialisiert mit 0)
#include <stdio.h>
char *monatsname(int); /* Gibt Zeiger auf ein Feld zurueck */
int main(void)
{
int i;
for(i = 1; i <= 12; i++)
printf("%s\n",monatsname(i));
}
char *monatsname(int n)
{
static char *name[] = {"falscher Monat",
"Januar",
"Februar",
"Maerz",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
};
if ((n < 1) || (n > 12))
return(name[0];
else
return(name[n]);
}
|
| signed | vorzeichenbehaftet (default für integer, bei char implemantationsabhängig; nur für ganzzahlige Datentypen) |
| unsigned | nicht vorzeichenbehaftet (nur für ganzzahlige Datentypen) |
| short int | int mit evtl. kleinerem Wertebereich (2 Byte) |
| long int | int mit evtl. größerem Wertebereich (4 Byte) |
| long double | double mit evtl. größerem Wertebereich |
| const | Variable kann nur gelesen werden (wird u.U. compilerabhängig ignoriert, allerdings liefert ein Verstoß eine Compiler-Warnungen) |
| volatile | Wert wird möglicherweise durch parallel laufende Prozesse verändert. Q. verhindert "Wegoptimieren" (kann ebenfalls compilerabhängig ignoriert werden) |
Außer eventuell bei char sind diese Größen
nicht festgelegt, sondern abhängig vom jeweiligen Compiler. Definitiv
festgelegt sind die angegebenen Mindestgrößen und die folgenden Relationen:
short <= int <= long;
float <= double <= long double.
Die tatsächliche Größe kann man den Header-files <limits.h>
und <float.h> entnehmen.
Neben der Verwendung von Konstanten in beliebigen Anweisungen (z. B. if (a > 1.0)
oder Nachname := "unbekannt";) besteht in C die Möglichkeit, Bezeichner
für Konstante zu vergeben.
Bei der Programmiersprache C findet man zwei Varianten der Konstantenvereinbarungen:
const Pi = 3.1415 #define Pi 3.1415Die erste Form entspricht dem aktuellen ANSI-Standard und ist zu bevorzugen. Die zweite Variante ist älter und definiert eigentlich nur ein Makro.
Wichtig:Bei der Verwendung von Gleitpunktkonstanten muß der Dezimalpunkt angegeben werden. 2000 ist eine Integer-Konstante, 2000. oder 2000.0 sind Gleitpunkt-Konstante (float oder double).
/* Aufzaehlung */
enum lichtzeichenanlage {rot, gelb, gruen};
enum wochentag {MO, DI, MI, DO, FR, SA, SO} termin;
enum wochentag ruhetag;
enum lichtzeichenanlage ampel;
termin = DI;
ruhetag = FR;
ampel = rot;
Die Integer-Zuweisung erfolgt durch Weiterzählen vom letzten zugewiesenen
Wert (default: 0) MO = 0, DI = 1, ...
Es lassen sich auch alle Werte individuell belegen, wobei auch mehrere Konstanten
den selben Wert besitzen dürfen (was aber selten sinvoll ist).
enum lichtzeichenanlage {rot = 10, gelb = 20, gruen = 30};
if (z = x + y) { .... }
z wird der Wert der Addition von x und y zugewiesen. Gleichzeitig ergibt der
Zuweisungsoperator einen Wert, der von der if-Anweisung ausgewertet werden
kann. Ist x + y != 0, dann ist das Ergenis WAHR und der Anweisungsblock
wird ausgeführt. Ob die obige KOnstruktion umbedingt eleganter und lesbarer
als
z = x + y;
if (z) { ... }
ist, mag dahingestellt sein.
Da die Zuweisung ein Operator ist, sind auch Mehrfachzuweisungen wie z. B. a = b = c = 12 möglich. Die Zuweisung bindet schwächer als andere Operatoren (arithmetische oder Vergleichs-Operatoren). Daher sollte man Zuweisungen, die in Ausdrücken stehen, zur Sicherheit klammern. Erst durch den abschließenden Strichpunkt wird der Zuweisungsoperator zu einer Anweisung!
Es gibt sechs arithmetische Operatoren. Sie stehen für die vier Grundrechenarten (+ - * /), die Modulofunktion (Rest nach Division; % ) und die arithmetische Negation (das Vorzeichen).
| - | Vorzeichen (Unär) |
| * | Multiplikation |
| / | Division |
| % | Modulofunktion |
| + | Addition |
| - | Subtraktion |
Die Modulofunktion ist nicht auf float oder double Operanden anwendbar. Die Rangfolge ist wie gewohnt: *, / und % gleich und höher als + und -. Das Minuszeichen als unärer Operator (Vorzeichen) hat einen höheren Vorrang als *, / und %. Es gibt kein positives Vorzeichen.
int h, i=3, j=5, k=10; float x, y=5.0, z=3.0; h = i+j+k; /* 18 */ h = k-j-i; /* 2 */ h = i-k; /* -7 */ h = k/i; /* 3 */ h = k/i+j; /* 8 */ h = k/(i+j); /* 1 */ h = k*i+j; /* 35 */ h = k*(i+j); /* 80 */ h = k%i; /* 1 */ x = y+z; /* 8. */ x = y/z; /* 1.666... */ x = y*z; /*15. */ x = k/z; /* 3.333... */ x = k/z + y/z; /* 5. */ x = k/i + y/i; /* 4.666... */ x = k%z; /* verboten! */
Höhere Rechenoperationen wie Wurzelziehen, Potenzieren, Exponentiation, usw. fehlen in C als Operator ganz; sie sind über Bibliotheksfunktionen realisiert. Bei ihrer Verwendung muß in jedem Fall die Inlcude-Datei math.h mit eingebunden werden.
| ++ | Inkrement |
| -- | Dekrement |
Sie sind (wie der Vorzeichenoperator -) rechts assoziativ, werden also von rechts her zusammengefaßt. Sie können vor oder nach ihrem Operanden stehen. Im ersten Fall wird der Operand zunächst in- oder dekrementiert und dann weiterverwendet, im zweiten Fall wird der Operand erst verwendet und dann in- oder dekrementiert.
int i;
int k;
i=1;
if (++i > 1)
k = 5; /* wird durchlaufen */
if (i++ > 2)
k = 10; /* wird nicht durchlaufen */
| ! | logische Negation |
| && | logisches UND |
| || | logisches ODER |
Sie sind hier in ihrer Rangfolge geordnet aufgeführt, logisches UND hat also einen höheren Vorrang als das logische ODER. In Ausdrücken erfolgt die Abarbeitung von links nach rechts aber nur solange, bis das Ergebnis eindeutig feststeht. Das führt zu einer kürzeren Ausführungszeit, wenn man die häufigst verwendete Bedingung an den Anfang einer Bedingungsabfrage stellt. Bei Operationen, die Nebeneffekte haben können (z.B. Inkrementation), muß man allerdings vorsichtig sein.
if (a<b && (d=(a+b)) != c) /* diese 2 Zeilen */ if ((d=(a+b)) != c && a<b) /* wirken nicht gleich */
Beispiel:
#include <stdio.h>
int main (void)
{
enum logik { false = 0, true = 1} a, b;
printf ("| a b | a&&b | a||b |\n");
printf ("|-----|------|------|\n");
for (a = false; a <= true; a++)
for (b = false; b <= true; b++)
printf ("| %d %d | %d | %d |\n",a, b, (a && b), (a || b));
return 0;
}
Ergebnis:
| a b | a&&b | a||b |
|-----|------|------|
| 0 0 | 0 | 0 |
| 0 1 | 0 | 1 |
| 1 0 | 0 | 1 |
| 1 1 | 1 | 1 |
C stellt auch Operatoren für Bitmanipulationen zur Verfügung. Die Verknüpfung der beiden Operanden erfolgt bitweise. Sie können nicht auf Variablen oder Konstanten vom Typ float oder double angewendet werden. Die Operatoren sind:
| ~ | Komplement |
| << | Linksshift |
| >> | Rechtsshift |
| & | bitweises UND |
| ^ | bitweises EXKLUSIVES ODER |
| | | bitweises ODER |
int i=7;
int j=9;
int k;
k = i & j; /* k=1 */
k = i | j; /* k=15 */
k = i ^ j; /* k=14 */
k = ~0 /* k=max(unsigned int) */
k = ~i; /* k=max(unsigned int) -7 */
k = i << 3; /* k=56 */
k = i >> 3; /* k=0 */
k = i & 0x03; /* k=3; alle Bits bis auf die beiden */
/* unteren ausmaskiert */
Beispiel: Zählen der Anzahl der 1-Bits in n
bitcount(unsigned int n)
{
int b;
for (b=0; n!=0; n >>= 1)
if (n & 01)
b++;
return (b);
}
Zweite, schnellere Version:
bitcount(unsigned int n)
{
int b;
b=0;
while (n != 0)
{
b++;
n = n & (n-1);
}
return (b);
}
Das folgende Beispiel ermittelt die Länge eines Maschinenwortes durch Zählen der Bits.
#include <stdio.h>
int wordlen()
{
int i;
unsigned wort;
i = wort = 0;
wort = ~wort; /* alle Bits auf 1 setzen */
while (wort != 0)
{
wort = wort >> 1; /* rechtsschieben */
i++;
}
return(i);
}
main()
{
printf("Anzahl der Bits: %d\n",wordlen());
}
Die folgenden "Einzeiler" manipulieren jeweils ein bestimmtes Bit in einem Integer-Wort:
/* Teste, ob ein bestimmtes Bit 0 oder 1 ist */
int tstbit( int word, int bit ) { return (word & (1 << bit) != 0); }
/* Loesche ein bestimmtes Bit */
int clrbit( int word, int bit ) { return (word & ~(1 << bit)); }
/* Setze ein bestimmtes Bit */
int setbit( int word, int bit ) { return (word | (1 << bit)); }
/* Invertiere ein bestimmtes Bit */
int xorbit( int word, int bit ) { return (word ^ (1 << bit)); }
| Operator- zeichen | 1. Bedeutung | 2. Bedeutung |
| () | Klammerung | typecast |
| * | Pointerdeklaration | Multiplikation |
| - | Vorzeichen | Subtraktion |
| & | Adresse von | bitweises UND |
i=1;
printf("%d %d",++i,i*3) /* kann 2,6 oder 4,3 ausgeben */
| < | kleiner |
| <= | kleiner gleich |
| > | größer |
| >= | größer gleich |
| == | gleich (identisch) |
| != | ungleich |
Die ersten vier haben untereinander gleichen Rang, stehen aber eine Rangstufe höher als die beiden letzten. Es gibt in C grundsätzlich keinen Datentyp BOOLEAN; WAHR oder FALSCH werden einfach über den numerischen Integer-Wert entschieden. Dabei gilt:
| ungleich 0 | WAHR (erhält den Wert eins) |
| gleich 0 | FALSCH (Wert 0) |
Arbeitet man in einem Programm viel mit diesen Werten, kann man folgende Konstantendefinitionen zu benutzen:
#define FALSE 0
#define TRUE ! FALSE
int a=5; int b=12; int c=7; a < b /* WAHR */ b < c /* FALSCH */ a+c <= b /* WAHR */ b-a >= c /* WAHR */ a == b /* FALSCH */ a+c == b /* WAHR */ a != b /* WAHR */ a = b<c; /* möglich: a=0 */ a = c<b; /* -"- a=1 */
Wie schon erwähnt, bindet die Zuweisung schwächer als andere Operatoren (arithmetische oder Vergleichs-Operatoren). Daher sollte man Zuweisungen, die in Ausdrücken stehen, zur Sicherheit klammern. Dazu einige Beispiele:
if (x = y) tuwas();Die Anweisung besagt nicht: "Wenn x gleich y ist, dann tuwas();", sondern "x erhalte den Wert y; Wenn das Ergebnis der Operation (x = y) ungleich Null ist, dann tuwas();". Es gibt also zwei Möglichkeiten:
if (x == y) /* Vergleich x == y (doppeltes Gleichheitszeichen) */ tuwas();
if ((x = y) != 0) tuwas();Die Klammerung von (x = y) ist notwendig, da der Vergleichoperator stärker bindet als die Zuweisung. Fehlt die Klammer, wäre das schon wieder ein schwer zu findender Fehler. x = y != 0 bedeutet: x = (y !=0). x hätte nachher nicht den Wert von y, sondern 0 oder 1.
Ausdruck1 op= Ausdruck2ist äquivalent zu (beachten Sie die Klammern!):
Ausdruck1 = (Ausdruck1) op (Ausdruck2)op kann einer der Operatoren: + - * / % << >> & ^ oder | sein. Die Klammern sind sehr wichtig, damit keine unerwünschten Nebeneffekte auftreten:
i *= k+1; /* wird wie */ i = i * (k+1); /* behandelt und nicht wie */ i = i * k +1;
Der Vorteil der abgekürzten Schreibweise wird an folgendem Beispiel deutlich:
feld[i*3+j-7] = feld[i*3+j-7] + 10; /* normal */ feld[i*3+j-7] += 10; /* abgekürzt */
Zusammengesetzte Zuweisungsoperatoren:
| += | x += ausdr | <-- | x = x + ausdr |
| -= | x -= ausdr | <-- | x = x - ausdr |
| *= | x *= ausdr | <-- | x = x * ausdr |
| /= | x /= ausdr | <-- | x = x / ausdr |
| %= | x %= ausdr | <-- | x = x % ausdr |
| &= | x &= ausdr | <-- | x = x & ausdr |
| |= | x |= ausdr | <-- | x = x | ausdr |
| ^= | x ^= ausdr | <-- | x = x ^ ausdr |
| <<= | x <<= ausdr | <-- | x = x << ausdr |
| = | x >>= ausdr | <-- | x = x >> ausdr |
Zusammenfassung:
| Variable | = | Variable | durchgeführte Typumwandlung |
| int int |
<-- <-- |
float double |
Weglassen des gebrochenen Anteils |
| int char char |
<-- <-- <-- |
long int short |
Weglassen der höherwertigen Bits |
| float | <-- | double | Runden oder Abschneiden (implementierungsabhängig) |
| float double |
<-- <-- |
long, int, short,char | Wenn keine exakte Darstellung möglich Runden oder Abschneiden (implementierungsabhängig) |
Bei Parameterübergabe an Funktionen erfolgt die Umwandlung des Typs des aktuellen Parameters in den Typ des formalen Parameters nach obigen Regeln.
Den genauen Ablauf der Wandlungen bei der Auswertung eines Ausdrucks oder einer Zuweisung beschreiben folgende Regeln (übliche arithmetische Umwandlungen):
Beispiel:

Vorsicht:
int j = 12, k = 18; float f; ... f = k/j; /* in f steht 1.0 */ ...Beide Operanden der Division k/j sind int-Variablen. Also ist auch das Ergebnis der Division vom Typ int (18/12 = 1 Rest 6). Die Zuweisung erfolgt nach der Division. Mit der impliziten Typkonversion erfolgt dann die Expansion von 1 zu 1.0. Sollte das "echte" Ergebnis gewünscht werden, so wird dies durch eine explizite Typumwandlung möglich.
Die explizite Typumwandlung (type cast) erfolgt mittels des Cast-Operators, der unmittelbar vor den Ausdruck zu setzen ist, dessen Typ konvertiert werden soll. Der Cast-Operator ist ganz einfach eine in Klammern gesetzte Typangabe. Obiges Beispiel wird geändert zu:
int j = 12, k = 18; float f; ... f = (float)k/j; ...Die Variable k wird nun zu einer float-Variablen typkonvertiert (Neudeutsch: "gecastet"). Bei der Division von float durch intwird j implizit auf float typkonvertiert und so ergibt sich das korrekte Ergebnis 1.5.
Beispiel: Wandelt die nächsten x Ziffern aus dem String s in eine Integerzahl
atoi(char s[])
{
int i,n;
n=0;
for (i=0; s[i]>='0' && s[i]<='9'; i++)
n = 10*n + s[i]-'0';
return (n);
}
Beispiel: Wie atoi, aber für Hexadezimalzahlen.
htoi(char s[])
{
int i,n;
n=0;
for (i=0; ; i++)
if (s[i] >= '0' && s[i] <= '9')
n = 16*n + s[i]-'0';
else if (s[i] >= 'A' && s[i] <= 'F')
n = 16*n + s[i]-'A'+10;
else if (s[i] >= 'a' && s[i] <= 'f')
n = 16*n + s[i]-'a'+10;
else
break;
return (n);
}
