zurück zum News-Archiv
> Anfang zurück vorwärts Ende ## ANSI C - Ein Programmierkurs - Teil III ### Datentypen In C stehen folgende einfache Datentypen zur Verfügung:
>     char, short, int, long, float, double
Ein `char` ist ein Datentyp, der genau ein Zeichen (Buchstaben im Sinne des ASCII-Zeichensatzes) aufnehmen kann. Da C nicht explizit zwischen Zeichen und Zahlen unterscheidet, kann dieser Datentyp auch eine Zahl (in Zeichenform) aufnehmen. Ein `int` ist ein Datentyp, der eine ganze Zahl aufnehmen kann, wobei der Wertebereich dem konkreten Computer angepasst ist. Ein `short` bzw. `long` ist eine kurze bzw. lange ganze Zahl. Es gilt, dass ein `short` kleiner als ein `long` ist. Ein `int` entspricht üblicherweise entweder einem `short` oder einem `long`. Es ist in ANSI C nicht definiert, wieviel Bytes diese Datentypen zu belegen haben! Der Wertebereich der einzelnen Datentypen steht in der Datei `limits.h`. Die Verwendung von Include-Dateien wird später erklärt. Üblicherweise belegt ein `char` ein Byte, ein `short` sind 2 Byte und ein `long` belegt 4 Byte. Ein `int` belegt, je nach Compiler, 2 oder 4 Byte. Die Datentypen `short`, `int` und `long` sind vorzeichenbehaftet, bei `char` ist es implementationsabhängig. Die meisten Compiler sind allerdings konsequent und betrachten `char` als vorzeichenbehaftet. Zusätzlich kann durch die Schlüsselworte `signed` und `unsigned` vor dem Datentyp explizit angegeben werden, dass der Datentyp vorzeichenbehaftet oder vorzeichenlos ist. Damit vergrößert oder verkleinert sich außerdem der darstellbare Bereich. Mit einem Short, der 16 Bit belegt, kann man den Bereich von -32768 bis 32767 abdecken. Ein `unsigned short` kann den Bereich von 0 bis 65535 abdecken. #### Wertebereiche unterschiedlicher Compiler
Typ | TC | Sozobon | GCC
--- | --- | --- | ---
char | 8 Bit | 8 Bit | 8 Bit
| -128 | -128 | -128
| 127 | 127 | 127
short | 16 Bit | 16 Bit | 16 Bit
| -32768 | -32768 | -32768
| 32767 | 32767 | 32767
int | 16 Bit | 16 Bit | 32 Bit
| -32768 | -32768 | -2147483648
| 32767 | 32767 | 2147483647
long | 32 Bit | 32 Bit | 32 Bit
| -2147483648 | -2147483648 | -2147483648
| 2147483647 | 2147483647 | 2147483647
> Bei den Datentypen `float` und `double` handelt es sich um Fließkommazahlen. Damit lassen sich Kommazahlen wie z.B. Preise darstellen. Aufgrund der speziellen Darstellung der Form `0.aaa * 10bb` lassen sich auch sehr große Werte darstellen. Zum Beispiel benutzt TC den IEEE-Standard, der für `float` den Wertebereich von 10E-38 bis 10E+38 ermöglicht. Es steht aber nicht eine Genauigkeit von 38 Stellen zur Verfügung! Um die Genauigkeit zu erhöhen, gibt es den Datentyp `double`, der zusätzlich noch einen größeren Wertebereich bietet. Der genaue Wertebereich hängt von der Implementierung des Compilers ab. Der Wertebereich von `float` kann aus der Includedatei `float.h` ermittelt werden. ### Initialisierung Es ist möglich, einer Variablen gleich bei der Deklaration einen Wert zuzuweisen, d. h. sie zu initialisieren. Dazu schreibt man hinter der Variablen ein Gleichheitszeichen =, gefolgt von dem gewünschten Wert. Der Wert ist eine Zahlenkonstante. Im nächsten Kapitel wird erklärt, wie Konstanten gebildet werden. ### `const` Um anzuzeigen, dass sich der Wert einer Variablen nicht ändert, kann sie mit dem Attribut `const` versehen werden. Dies macht allerdings nur bei einer initialisierten Variablen Sinn.
>     const double e = 2.71828182845905;
>     const char ich[] = "Michael Bernstein";
> Versucht man den Wert einer `const`-Variablen zu ändern, ist das Ergebnis implementierungsabhängig. ### Konstanten Wird in C eine ganzzahlige Zahl hingeschrieben, handelt es sich um den Datentyp `int`, sofern keine weitere Angabe gemacht wird. Beginnt die Zahl mit einer Ziffer ungleich 0 (Null), wird von einer Dezimalzahl ausgegangen. Wenn die erste Ziffer eine 0 (Null) ist, handelt es sich um eine Oktalzahl und wenn die Zahl mit 0x eingeleitet wird, ist es eine Hexadezimalzahl. Es ist zusätzlich noch möglich, den Datentyp zu spezifizieren. Wenn sich direkt an die Ziffern ein `l` (kleiner Buchstabe L) oder `L` (wir erinnern uns: Groß- und Kleinschreibung ist in C signifikant!) anschließt, handelt es sich um eine `long`-Konstante, ein `u` oder `U` steht für eine `unsigned`-Konstante. Ein `ul` oder `UL` kennzeichnet eine `unsigned`-`long`-Konstante. Weiterhin gibt es auch Zeichenkonstanten. Dies sind Zeichen in einfachen Hochkommata wie z.B. 'h' und können jeder ganzzahligen Variable zugewiesen werden. Da jedem Zeichen im ASCII-Code auch eine Nummer zugeordnet ist, kann der Compiler eine Zeichenkonstante auch als Zahl betrachten. Für Zeichen, die nicht darstellbar sind, wie z.B. Steuerzeichen für einen Zeilenvorschub, gibt es zwei Möglichkeiten. Zum einen ist es möglich, mit Hilfe des Backslash '\' als Escapezeichen eine Ersatzdarstellung zu benutzen. Da das '\' eine Ersatzdarstellung einleitet, muss dieses Zeichen selbst auch über eine Ersatzdarstellung eingegeben werden. Weiterhin ist es möglich, über den Backslash '\' und einer Folge von ein bis drei Oktalziffern oder dem Backslash '\' einem 'x' und einer Folge von einer oder mehreren hexadezimalen Ziffern jedes gewünschte Zeichen über seine Ordnungszahl einzugeben. Achtung: welches Zeichen letztendlich auf dem Bildschirm erscheint, hängt dann natürlich auch von dem Zeichensatz ab, den der Computer verwendet. Beispiel: Klingel = '\007'; Bell = '\x7'; Es ist auch möglich, Zeichenkonstanten mit mehr als einem Zeichen zu bilden, z.B. für die Zuweisung an ein `short`, das ja zwei Byte belegt. Da es aber von der Implementierung abhängt, was der Compiler daraus macht, ist meiner Meinung nach der Nutzen nur sehr eingeschränkt. Ganze Zeichenketten werden in doppelte Hochkommata eingeschlossen. Es gibt in C keinen eigenen Datentyp für Zeichenketten. Im Kapitel Felder wird erklärt, wie man dennoch Zeichenketten in C benutzt. Boolesche Werte gibt es nicht in C. Ein Wert einer ganzzahligen Variablen von ungleich 0 entspricht einem `TRUE`, ein Wert von 0 (Null) entspricht einem `FALSE`. Wir werden bei der Besprechung von Kontrollstrukturen nochmals darauf zurückkommen. Konstanten vom Typ `float` oder `double` kann man, wie man es von reellen Zahlen gewohnt ist, in der Kommadarstellung schreiben, wobei anstelle des Kommas der in den USA gebräuchliche Punkt tritt. Dies sieht dann so aus: `3.14159`. Zusätzlich gibt es für sehr kleine oder sehr große Zahlen die wissenschaftliche Darstellung: `34.57E12`, was für `34,57 * 1012` steht oder `1.456E-23`, was für `1,456 * 10-23` bzw. `1 / (1,456 * 1023 )` steht. ### Typkonvertierung C kann implizit von einem kleineren in einen größeren Datentyp umwandeln. Wenn z.B. einer `int`-Variablen eine `char`-Variable zugewiesen wird, kann C automatisch `char` zu `int` konvertieren, da der Wertebereich von `char` in den Wertebereich von `int` paßt. Auch bei Verknüpfungen in Anweisungen kann diese Konvertierung automatisch erfolgen. Werden z.B. ein `int` und ein `char` addiert und einem `int` zugewiesen, so wird der `char` automatisch zu einem `int` konvertiert. In anderen Fällen, also bei einer Typumwandlung zu einem kleineren Datentyp, muss dem Compiler dies explizit angegeben werden. Dazu dient der `cast`. Der gewünschte Datentyp wird in Klammern der Variable oder Konstante oder auch einem gesamten Ausdruck vorangestellt.
>     ...
>     int i = 10;
>     char j;

>     j = (char)i;
> Die Variable `i` wird zu einem `char` gecastet, um sie `j` zuweisen zu können. Hierbei muss der Anwender selbst darauf achten, dass der Ergebnisdatentyp auch den kompletten Wert aufnehmen kann. Wird der Wertebereich des Ziels überschritten, kann ein Overflow zu negativen Zahlen auftreten, wenn das Ziel vorzeichenbehaftet ist. Achtung: beim Datentyp `char` ist nicht definiert, ob er vorzeichenbehaftet oder vorzeichenlos ist! Schreiben Sie ruhig ein paar kleine Testprogramme, nachdem Sie auch das Kapitel über Anweisungen durchgelesen haben, um sich das Verhalten zu verdeutlichen. Benutzen Sie auch Werte, die den Wertebereich des Ziels überschreiten. ### Beispiele für Deklarationen Wie sieht das Ganze jetzt aus? Dazu folgt ein Miniprogramm, das einfach mal ein paar Variablen deklariert.
>     char taste;

>     int main()
>     {
>        int i=0, lauf_index;
>        unsigned long anzahl_tests;
>     }
> ### Gültigkeitsbereich Eine Variable auf globaler Ebene, also außerhalb einer Funktion, ist in allen Funktionen darunter bekannt, eine Variable innerhalb eines Blockes jedoch nur innerhalb des Blocks, in dem sie deklariert wurde. Innerhalb einer Ebene kann es keine zwei Bezeichner mit dem gleichen Namen geben. Eine Variable innerhalb eines Blocks überdeckt andere Variable gleichen Namens außerhalb. Damit das Ganze etwas anschaulicher wird und weil schon genug Theorie kam, folgen hierzu einige Beispiele. Testen Sie ruhig alle Beispiele; erst durch eigenes Probieren kommt die nötige Anschauung. Und mit Anschauung kann man sich Dinge leichter merken. Was passiert bei folgendem Beispiel?
>     #include <stdio.h>

>     int i=3;
>     int main()
>     {
>        printf("i = %d\n",i);
>     }
>     int i;
> Tja, da hier zweimal die Variable `i` deklariert ist, beschwert sich schon der Compiler und das Programm lässt sich nicht übersetzen. Was passiert bei folgendem Beispiel?
>     #include <stdio.h>

>     float main=3.5;
>     int main()
>     {
>        printf("main = %d\n",main);
>     }
> Auch hier beschwert sich der Compiler. Denn hier haben wir den Namen main zweimal vergeben. Was passiert bei folgendem Beispiel?
>     #include <stdio.h>

>     int i=10;
>     int main()
>     {  int i=15;
>        printf("i = %d\n",i);
>     }
> Dieses Beispiel sollte sich problemlos übersetzen lassen. Die Ausgabe des Programms ist `i = 15`. Innerhalb der Funktion `main` wird die mit `15` initialisierte Variable benutzt. Die global deklarierte und mit `10` initialisierte Variable `i` ist innerhalb von main unsichtbar, da sie von der inneren Variablen desselben Namens überdeckt wird. Was passiert bei folgendem Beispiel?
>     #include <stdio.h>

>     int i=3;
>     int main()
>     {
>        printf("j = %d\n",j);
>     }
>     int j=5;
Hier sollte sich wieder der Compiler beschweren. Eine globale Variable ist in allen darunter liegenden Funktionen bekannt. Die Funktion `main` liegt aber über der Deklaration von `j` und damit kennt `main j` nicht. Im nächsten Teil werden wir uns mit Speicherklassen beschäftigen. Michael Bernstein

Anfang zurück vorwärts Ende Seitenanfang

Copyright und alle Rechte beim ATOS-Magazin. Nachdruck und Veröffentlichung von Inhalten nur mit schriftlicher Zustimmung der Redaktion.
Impressum - Rückmeldung via Mail oder Formular - Nachricht an webmaster