Einfache Datentypen

C kennt im wesentlichen die von Pascal gewohnten einfachen Datentypen - mit Ausnahme von boolean. Eine Variable wird deklariert in der syntaktischen Form

typname variablenname;

Der Operator sizeof dient zur Feststellung, wieviele Bytes ein Datentyp oder eine Variable dieses Datentyps benötigen. Beispiele hierzu finden Sie nachfolgend.

Zeichen (char)

Eine Variable vom Datentyp char benötigt 8 Bits (1 Byte) Speicherplatz und kann jeweils ein Zeichen aus dem der Maschine zugrundeliegenden Code (bei uns i.d.R. ASCII[2]) aufnehmen.

Beispiel

void main(void)
{                /* Die Variable zeichen wird */
   char zeichen; /* vom Datentyp char */
   zeichen = 'A';/* deklariert und erhält per */
                /* Zuweisung den Wert 'A'. */
}

Die Variable zeichen kann jeweils eines der 256 Zeichen des (in der Regel ASCII-)Codes aufnehmen. C unterscheidet zwei Varianten: signed bedeutet, daß der Datentyp char wie der numerische Bereich -128..127 behandelt wird, das ASCII-Zeichen 255 wird also (numerisch) als -1 interpretiert; demgegenüber bedeutet unsigned, daß char wie der Bereich 0..255 verwendet wird, das ASCII-Zeichen 255 wird also auch numerisch als 255 interpretiert. Welche Interpretation ein konkreter Compiler vornimmt, ist maschinenabhängig, aber auch vom Programmierer oder von der Programmiererin einstellbar. Auf dem HP9000-System von Hewlett-Packard ist char beispielsweise als signed voreingestellt. Aus diesem Grund wird in C häufig, z.B. bei der Bibliotheksfunktion getch(), der (stets vorzeichenbehaftete) Datentyp int (statt char) für ein Zeichen verwendet! Diese Betrachtung mag einem treuen Pascalianer seltsam vorkommen; wir werden aber sehr schnell sehen, daß C es anscheinend nicht so genau nimmt mit der Unterscheidung von char und numerischen (ganzzahligen) Datentypen: in Wahrheit ist char jedoch nichts anderes als ein numerischer Datentyp, dessen Ausprägungen lediglich bedarfsweise als Repräsentanten des dem Rechner zugrundeliegenden Codes (z.B. ASCII) interpretiert werden!

Will man von der jeweiligen Voreinstellung abweichen, kann man eine Variable explizit als signed char oder unsigned char vereinbaren.

Numerische Datentypen

Der Datentyp int (integer) vertritt den (vorzeichenbehafteten) Ganzzahlbereich, entspricht also auch der Angabe signed int. Der Speicherplatzbedarf ist von ANSI nicht vorgeschrieben, also maschinenabhängig; bei HP-UX 9.0 beträgt dieser 4 Bytes, bei PC-Compilern in der Regel 2 Bytes. In der unten auszugsweise abgedruckten Headerdatei limits.h (hier in der Version von Hewlett Packards UNIX-Derivat HP-UX) werden symbolische Konstanten bereitgestellt, z.B. INT_MAX, dem größten Wert aus dem Bereich des Datentyps int. (Dies entspricht dem MAXINT von Pascal.)

/* /usr/include/limits.h .. stark gekürzt .. */
#define CHAR_BIT 8 /* Number of bits in a char */
#define CHAR_MAX 127 /* Max integer value of a char */
#define CHAR_MIN (-128)/* Min integer value of a char */
#define INT_MAX 2147483647 /* Max decimal value of an int */
#define INT_MIN (-2147483648)/* Min decimal value of an int */

Mit unsigned int kann erwartungsgemäß angegeben werden, daß der Speicherplatz ohne Vorzeichenbit interpretiert wird, der Wertebereich also bei 0 (statt bei INT_MIN) beginnt. Statt der Deklaration unsigned int i; genügt im übrigen bereits unsigned i;.

short und long (int) sind weitere Ganzzahldatentypen, jeweils defaultmäßig als signed interpretiert. Die Größen für diese Datentypen sind wiederum maschinenabhängig, bei HP-UX 9.0 wie bei den gängigen PC-Compilern belegt ein short-Speicherplatz 2 Bytes und einer vom Typ long (int) 4 Bytes. Analog zu unsigned int kann auch hier über das vorangestellte Wort unsigned eine entsprechende andere Interpretation erzwungen werden.

Beispiel:

void main(void)
{
   unsigned short us;
   short ss; /* = signed short */
   unsigned long ul;
   long sl; /* = signed long */
}

Die ganzzahligen Datentypen und der Datentyp char (sowie die später erläuterten Aufzählungstypen) werden (entsprechend wie in Pascal) auch als ordinale oder, etwas irreführend vielleicht, als integer-Datentypen bezeichnet.

Mit float, double und long double stehen drei verschiedene Gleitkommatypen zur Verfügung. Wieder sind die Bereiche maschinenabhängig. Zu den Gleitkommatypen stehen in der Datei float.h (sh. unten) entsprechende vordefinierte Konstanten.

/* /usr/include/float.h .. stark gekürzt .. */
#define FLT_MAX 3.40282347E+38
#define FLT_MAX_10_EXP 38
#define DBL_MAX 1.7976931348623157E+308
#define DBL_MAX_10_EXP 308

Konstanten (Literale)

Eine ganzzahlige Konstante wie 123 hat den Typ int; eine long-Konstante wird (zur Betonung) mit der Endung l oder L geschrieben: 123L ist damit vom Typ long[3]. Ist eine ganzzahlige Konstante zu groß für int, so wird sie jedoch implizit als long interpretiert. Für vorzeichenlose (unsigned) Konstanten kann das Suffix U (oder u) verwendet werden: 123U ist eine Konstante vom Typ unsigned int. Entsprechend ist 123UL ein Konstante vom Typ unsigned long.

Ganzzahlkonstanten können auch oktal und hexadezimal angegeben werden. (Freude kommt auf!) Beginnt eine ganzzahlige Konstante mit einer 0, so wird sie als Oktalzahl interpretiert: 007 ist dezimal 7, 010 ist dezimal 8. Beginnt sie dagegen mit 0x oder 0X, so wird sie hexadezimal interpretiert: 0x1f oder 0x1F stehen für den dezimalen Wert 1*16+15*1=31.

Gleitpunktkonstanten enthalten einen Dezimalpunkt (123.45) und/oder einen Exponenten (12E-2 steht für 0.12, 1.234E2 steht für 123.4). Ohne ein spezielles Suffix sind diese vom Typ double, mit dem Suffix F (oder f) float, mit L vom Typ long double!

Eine Zeichenkonstante (z.B. 'A') wird stets als ganzzahlig angesehen. So ist etwa im ASCII-Code 'A' der Wert 65, '0' ist 48. Häufig werden Zeichenkonstanten zum Vergleich mit anderen Zeichen herangezogen.

Gewisse Sonderzeichen können auch in sogenannten Ersatzdarstellungen angegeben werden: der Zeilenvorschub (LineFeed) oder der Wagenrücklauf (Carriage Return) können als '\r' bzw. '\n' (auch als NewLine gelesen) geschrieben werden. Daneben kann in der Form '\0???' bzw. '\x??' ein Zeichen oktal oder hexadezimal angegeben werden[4].

Beispiel: '\007' und '\x7' stehen gleichermaßen für das ASCII-Zeichen Nr. 7 (Klingelzeichen).

Hier die Ersatzdarstellungen im Überblick:

\a Klingelzeichen (Bell)
\b Backspace
\f Seitenvorschub (FormFeed)
\n Zeilenvorschub (LineFeed)
\r Wagenrücklauf (Carriage Return)
\t Tabulatorzeichen
\v Vertikaler Tabulator
\\ steht für den Backslash \
\? Fragezeichen
\' Anführungszeichen
\" doppeltes Anführungszeichen

Warnung: Bitte unterscheiden Sie künftig zwischen 'x' und "x"! Das einfache Hochkomma steht in C für ein einzelnes Zeichen, das doppelte Hochkomma wird jedoch für Zeichenketten (Strings) stehen!

Aufzählungstypen (enum)

C kennt wie Pascal Aufzählungstypen (enumerated types), auch wenn diese in C seltener eingesetzt werden. Mit der Deklaration

enum boolean { FALSE, TRUE };

wird ein Datentyp (enum) boolean vereinbart; implizit sind damit die (letztlich numerischen) Konstanten FALSE (=0) und TRUE (=1) vereinbart worden[5]. Allerdings hat C aufgrund seiner äußerst liberalen Lebenseinstellung keine Probleme damit, einer in diesem Sinne deklarierten boolean-Variablen auch Werte wie 2, 17 oder 'A' zuzuweisen!

Zwei Beispiele solcher enum-Deklarationen:

enum wochentage { MO=1, DI, MI, DO, FR, SA, SO };

Das geht auch: MO wird damit auf 1 statt auf 0 festgelegt; alle anderen Werte folgen: DI=2 usw.

enum wochentage tag; /* Deklaration einer Variablen*/
tag=DI; /* und Wertzuweisung */

enum faecher { BIOLOGIE, CHEMIE, MATHEMATIK, PHYSIK } fach;
/* ... */
for (fach=BIOLOGIE; fach<=PHYSIK; fach++)
/* tueirgendwas */

Selbstdefinierte Datentypen (typedef)

Wiederum wie bei Pascal können auch in C eigene Typ(nam)en geschaffen werden. Die Syntax hierfür ist einfach: typedef <alter-Typ> <neuer-Typname>; Mit der Deklaration

typedef int INTEGER;

wird ein Datentyp namens INTEGER geschaffen, der synonym mit int verwendet wird. Die Verwendung von typedef wird später sinnvoller werden bei höheren und strukturierten Datentypen.

Der Datentyp void

Einen etwas eigenwilligen Datentypen kennt (ANSI) C unter dem Namen void. Hiermit kann explizit gesagt werden, daß eine Funktion keinen Rückgabewert besitzt, oder daß eine Parameterliste leer ist. Das haben wir bereits im Eingangsbeispiel hello.c gesehen.

Eine Variable kann (natürlich) nicht vom Datentyp void sein: der Compiler meldet dann "unknown size for ...", denn der Datentyp void hat keine Größe!

Typenkonversion (casting)

Hat ein mehrwertiger Operator mit Werten oder Variablen von verschiedenen Typen zu tun, so wird (häufig) eine implizite oder explizite Typkonversion (sogenanntes Casting) durchgeführt.

Hierzu ein kleines konkretes Beispielprogramm.

/* casting.c */
void main(void)
{
   char c;
   int i;
   float x;
   i=65;

   c=i; /* funktioniert, c=65='A' */
   c=i+32; /* geht auch, c=97='a' */
   i=7/9*c; /* geht ebenfalls, ist aber 0, */
              /* denn 7/9 ist (als int!) 0 !!! */
   x=3.45;
   i=x; /* geht, i wird auf 3 gesetzt */
   x=i; /* geht natuerlich auch */
}

Neben den impliziten Typumwandlungen gibt es auch explizite, sogenannte Casts oder Castings[6]: mit der Syntax ( <typname> ) expression wird der entsprechende Ausdruck auf den angegebenen Datentyp projiziert.

Ein Beispiel:

float x;

x= 7/9; /* damit wird x auf 0 gesetzt! */
x= (float)7/9; /* damit wird x auf 0,777778 gesetzt! */