Datentypen in C

Datentypen der Programmiersprache C

Der Artikel Datentypen in C beschreibt die Verwendung von Datentypen in der Programmiersprache C. Datentypen sind in C Deklarationen für Speicheradressen oder Variablen, die bestimmen, welche Operatoren und Funktionen auf die Adressen und Variablen angewendet werden können.

C bietet grundlegende arithmetische Datentypen zur Speicherung von Ganzzahlen und Gleitkommazahlen sowie die nötige Syntax zur Erstellung von Feldern und Verbundtypen. Etliche Header-Dateien der C-Standard-Bibliothek bieten darüber hinaus Definitionen weiterer Datentypen, die jeweils über bestimmte nützliche Eigenschaften verfügen.

Grundlegende arithmetische Datentypen

Bearbeiten

C verfügt über die vier arithmetischen Datentypen char, int (beide für ganze Zahlen), float und double (beide für Kommazahlen). Die Auswahl eines dieser Datentypen beeinflusst die Größe des reservierten Speichers und die Größe der darstellbaren Werte. Darüber hinaus sind für die verschiedenen Datentypen unterschiedliche Operatoren und Funktionen zugelassen.

Der Verzicht auf festgeschriebene Größen und Wertebereiche, um möglichst viele Architekturen zu unterstützen, wird durch definierte minimale Wertebereiche und die folgende feste Relation abgemildert:

signed charshort intintlong intlong long int.
(„≤“ bedeutet dabei, dass der rechts stehende Typ alle Werte des links stehenden Typs aufnehmen kann.)

Character

Bearbeiten

Zum Speichern eines Zeichens verwendet man in C den Datentyp Character, geschrieben als char. Vom Computer tatsächlich gespeichert wird nicht das Zeichen, sondern eine gleichbedeutende, in der Regel acht Bit lange, Binärzahl (dadurch ergeben sich in der Regel 256 verschiedene Werte, die einem Character zugewiesen werden können). Der Programmierer kann sich die Binärzahl leicht vom Computer in ein Zeichen oder eine dezimale Ganzzahl übersetzen lassen. Die Übersetzung einer Zahl in ein Zeichen und umgekehrt geschieht dabei anhand einer Tabelle (z. B.: ASCII-Tabelle oder EBCDIC).

char zeichen = 'A';    /* intern gespeichert wird nicht der Buchstabe „A“ sondern die
                        * entsprechende Binärzahl, meist anhand der ASCII-Tabelle 0b01000001 */
printf("%d", zeichen); /* gibt 0b01000001 als Dezimalzahl aus, also: „65“ */
printf("%c", zeichen); /* gibt 0b01000001 als ASCII-Zeichen aus, also: „A“ */

Ein Character repräsentiert die kleinste adressierbare Einheit in C, in der Regel acht Bit. Deshalb wird die Größe von Objekten und Typen oft als ganzzahliges Vielfaches eines Characters angegeben. Je nach Compiler kann char entweder gleichbedeutend sein mit signed char (meistens -128 bis 127) oder mit unsigned char (meistens 0 bis 255). Um auch Zeichen aus Zeichensätzen aufnehmen zu können, die mehr Zeichen umfassen als der relativ kleine ASCII-Zeichensatz, wurde mit wchar_t bald ein zweiter für Zeichen konzipierter Datentyp eingeführt. Er umfasst in fast allen Implementierungen mehr als acht Bit.

Zu beachten ist, dass ein Zeichenliteral wie 'A' in C den Typ int hat und nicht char, wie es bei C++ der Fall ist. C schreibt keinen Zeichensatz vor. Daher sind Implementierungen möglich, die kein ASCII verwenden, diese sind jedoch sehr selten.

Zum Speichern einer ganzen Zahl verwendet man eine Variable vom Datentyp Integer, geschrieben als int. Um den Wertebereich eines Integers zu verkleinern oder zu vergrößern, stellt man ihm einen der Qualifizierer short, long oder long long voran. Das Schlüsselwort int kann dann auch weggelassen werden, so ist long gleichbedeutend mit long int. Um zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen zu wechseln, gibt es die beiden Qualifizierer signed und unsigned. Für einen vorzeichenbehafteten Integer kann der Qualifizierer aber auch weggelassen werden, so ist signed int gleichbedeutend mit int.

char ganzzahl = 1;      /* mindestens 8 Bit, also 256 mögliche Werte */
short ganzzahl = 2;     /* mindestens 16 Bit, also 65536 mögliche Werte */
int ganzzahl = 3;       /* mindestens 16 Bit, also 65536 mögliche Werte */
long ganzzahl = 4;      /* mindestens 32 Bit, also 4294967296 mögliche Werte */
long long ganzzahl = 5; /* mindestens 64 Bit, also 18446744073709551616 mögliche Werte */

Die Größe eines Integers ist vom jeweiligen Compiler abhängig, der C-Standard garantiert aber eine minimale Größe von 16 Bit. Die tatsächliche Größe beträgt heutzutage (je nach Prozessorarchitektur und Betriebssystem) meist 32 Bit, oft aber auch schon 64 und manchmal noch 16 Bit. In 16 Bit lassen sich 65536 verschiedene Werte speichern. Um die Verwendung von negativen Zahlen zu ermöglichen, reicht der Wertebereich bei 16 Bit gewöhnlich von -32768 bis 32767. Werden keine negativen Zahlen benötigt, kann der Programmierer mit unsigned int aber einen vorzeichenlosen Integer verwenden. Bei 16 Bit großen Integern ergibt das einen Wertebereich von 0 bis 65535.

Die tatsächliche Größe eines Integers ist in der Headerdatei <limits.h> abgelegt. INT_MAX ersetzt der C-Präprozessor beispielsweise durch den Wert, den der Typ int maximal annehmen kann.

Die C-Standard-Bibliothek ergänzt diese Datentypen über die plattformunabhängige Header-Datei <stdint.h> in der eine Fülle von Ganzzahltypen mit fester Länge definiert ist.

float, double und long double

Bearbeiten

Gleitkommazahlen werden in einem der drei Datentypen float, double und long double gespeichert.

float kommazahl = 0.000001f;            /* Genauigkeit ist implementierungsabhängig */
double kommazahl = 0.000000000000002;   /* Genauigkeit ist implementierungsabhängig */
long double kommazahl = 0.3l;           /* Genauigkeit ist implementierungsabhängig */

Unter Genauigkeit ist hierbei nicht die Anzahl der signifikanten Nachkommastellen zu verstehen – vielmehr schreibt der Standard mit FLT_DIG,DBL_DIG,LDBL_DIG lediglich die Anzahl der (direkt aufeinanderfolgenden) signifikanten Dezimalziffern als Genauigkeitskriterium vor, und hierbei mind. jeweils 6/10/10. Die Lage dieser Ziffernfolge (vor Komma, nach Komma, geteilt über Komma) legt der Standard nicht fest.

In den meisten C-Implementierungen entsprechen die Datentypen float und double dem international gültigen Standard für binäre Gleitpunktarithmetiken (IEC 559, im Jahr 1989 aus dem älteren amerikanischen Standard IEEE 754 hervorgegangen). Unter dieser Annahme implementiert float das „einfach lange Format“, double das „doppelt lange Format“. Dabei umfasst ein float 32 Bit, double 64 Bit, doubles sind also genauer. Die Größe von long doubles ist je nach Implementierung unterschiedlich, ein long double darf aber auf keinen Fall kleiner sein als ein double.

Die genauen Eigenschaften und Wertebereiche auf der benutzten Architektur können über die Headerdatei <float.h> ermittelt werden.

Komplexe Zahlen

Bearbeiten

Zusätzlich existieren seit C99 noch drei Gleitkomma-Datentypen für komplexe Zahlen, welche aus den drei Gleitkommatypen abgeleitet sind: float _Complex, double _Complex und long double _Complex. Ebenfalls in C99 eingeführt wurden Gleitkomma-Datentypen für rein imaginäre Zahlen: float _Imaginary, double _Imaginary und long double _Imaginary.

In einer hosted-Umgebung müssen die _Complex-Datentypen vorhanden sein; die _Imaginary-Typen sind optional. In einer freestanding-Umgebung sind diese sechs Datentypen optional.

Bis zum C99-Standard gab es keinen Datentyp zum Speichern eines Wahrheitswerts. Erst seit 1999 können Variablen als _Bool deklariert werden und einen der beiden Werte 0 (falsch) oder 1 (wahr) aufnehmen. Die Größe einer _Bool-Variablen ist plattformabhängig und kann 8 Bit übersteigen. Inkludiert man den Header stdbool.h kann auch der Alias bool statt _Bool verwendet werden, sowie false und true statt 0 und 1.

_Bool a = 23;   /* Alle zugewiesenen Werte ungleich 0 werden von einer _Bool-Variablen als 1 gespeichert */

#include <stdbool.h>
bool b = false;

Der Datentyp void wird im C-Standard als „unvollständiger Typ“ bezeichnet. Man kann keine Variablen von diesem Typ erzeugen. Verwendet wird void erstens, wenn eine Funktion keinen Wert zurückgeben soll, zweitens für die Deklarationen einer leeren Parameterliste für eine Funktion und drittens als Teil des regulären aber anonymen Datenzeigertyps void*, der Zeiger aller Datentypen (keine Funktionen) aufnehmen kann.

void funktionsname();      /* Deklaration einer Funktion, die keinen Wert zurückgibt */
void* zeigername;          /* Zeiger auf ein Objekt von beliebigem Typ */

Datenmodell

Bearbeiten

Die C-Sprachnorm legt die Größe (und damit den Wertebereich) der einzelnen Basisdatentypen nicht fest, sondern definiert lediglich Relationen zwischen den Größen der Basisdatentypen und fordert für jeden Basisdatentyp jeweils Mindestgrößen. Daraus ergeben sich in der Praxis mehrere Ausgestaltungsmöglichkeiten, welche man Datenmodell oder auch Programmiermodell nennt.

Der Datentyp int wird auf einer Plattform in der Regel so festgelegt, dass seine Größe der natürlichen Datenwortgröße der CPU entspricht. Die Größe der Zeigertypen richtet sich nach der Größe des Speicherbereichs, der vom Programm aus adressierbar sein soll. Dieser Speicherbereich kann kleiner aber auch größer sein als der von der CPU-Architektur adressierbare Speicherbereich.

Auf heutigen Architekturen ist ein char meist 8 Bit groß, die anderen Datentypen müssen somit ein ganzzahliges Vielfaches von 8 Bit groß sein. Damit ergeben sich folgende mögliche Datenmodelle:

Bits pro Datentyp
Datenmodell Datentyp Plattformen (Auswahl)
char short int long long long void*
IP16 8 16 16 32 64 16 MS-DOS im SMALL memory model
LP32 8 16 16 32 64 32 MS-DOS im LARGE memory model
ILP32 8 16 32 32 64 32 die meisten 32-Bit-Betriebssysteme
LLP64 8 16 32 32 64 64 Windows auf x86-64 und IA64
LP64 8 16 32 64 64 64 die meisten unixoiden Betriebssysteme auf 64-Bit-Plattformen
ILP64 8 16 64 64 64 64 SPARC64
SILP64 8 64 64 64 64 64 Unicos (Cray)
Bearbeiten