3.4    Datentypen für Ganzzahlen

Für die Speicherung von vorzeichenbehafteten Ganzzahlen (hier zunächst: standard signed integer types) bietet C die folgenden in Tabelle 3.1 aufgelisteten Datentypen. Zusätzlich finden Sie in der folgenden Tabelle die Mindestgrößen von Werten und das Formatzeichen, um den Typ mit den Funktionen der printf()-Familien formatiert auszugeben oder mit Funktionen der scanf()-Familie einzulesen. Die tatsächlichen Wertebereiche sind besonders beim Typ int meistens größer.

Datentyp

Wertebereich (min.)

Formatzeichen

signed char

–128
+127

%hhd (für dezimal)
%c (für Zeichen)

short

–32768
+32767

%hd oder %hi

int

–32768
+32767

%d oder %i

long

–2.147.483.648
+2.147.483.647

%ld oder %li

long long (seit C99)

–9.223.372.036.854.775.808
+9.223.372.036.854.775.807

%lld oder %lli

Tabelle 3.1    Grundlegende Datentypen für Ganzzahlen

Neben diesen vorzeichenbehafteten fünf signed-Ganzzahltypen (standard signed integer types) kann es abhängig von der Implementation auch erweiterte signed-Ganzzahltypen (extended signed integer types) wie etwa __int128 geben, worauf hier allerdings nicht näher eingegangen wird.

Der bevorzugte Datentyp für Ganzzahlen lautet für gewöhnlich int, weil per Definition die meisten Systeme damit am natürlichsten umgehen und häufig auch am schnellsten rechnen können. Benötigen Sie mehr Bits (bis zu 128), steht Ihnen long oder long long zur Verfügung. Bei kleineren Werten können Sie hingegen short verwenden. Eigentlich heißen die korrekten Typnamen short int, int, long int und long long int. In der Praxis werden normalerweise nur short, long und long long verwendet (aber nicht immer).

Überlauf vorzeichenbehafteter signed-Ganzzahlwerte

Bei signed-Ganzzahltypen kann es zu einem Werteüberlauf kommen. Dies bedeutet, dass z. B. bei einer Multiplikation mehr Bits entstehen, als die verwendete Variable noch aufnehmen kann. Der Standard schreibt hier nicht vor, wie das System auf einen Überlauf von signed-Ganzzahltypen reagieren soll. Deshalb lässt sich nicht voraussagen, wie sich das Programm weiter verhält (undefined behavior). Wenn Sie beispielsweise zum maximalen Wert einer positiven Ganzzahl einen positiven Wert hinzuaddieren, befinden Sie sich unter Umständen plötzlich im negativen Bereich und haben einen Überlauf (engl. overflow) erzeugt. Ein Beispiel:

int iVal = INT_MAX; // INT_MAX enthält max. Wert für int
iVal += 2; // Überlauf (undefined behavior)

Sie als Programmierer sind selbst dafür verantwortlich, dass es nicht so weit kommt.

Die Größe der Typen int, short und long ist nicht festgelegt. int ist mindestens so groß wie short. Der Datentyp long hat hingegen mindestens die Ausdehnung eines int. Daraus können Sie auch folgern, dass nicht hundertprozentig gesagt werden kann, wie viele Bits ein bestimmter Typ verwendet. Allerdings können Sie sich mit Sicherheit auf die folgende Reihenfolge verlassen:

signed char <= short <= int <= long <= long long

<= bedeutet hier: ist kleiner oder gleich.

Ähnlich sieht dies beim Abspeichern von einzelnen Zeichen aus. Auch hier hängt die Anzahl der Bits pro Zeichen von Ihrem System ab. Der Standard schreibt hier ebenfalls nur vor, dass signed char der kleinste und long long der größte Typ für Ganzzahlen ist. Die anderen eingebauten Typen short, int und long liegen irgendwo dazwischen. Auf vielen modernen Betriebssystemen ist heute ein char 16 Bit und ein long long 64 Bit breit. Dies liegt daran, dass für Zeichen mittlerweile nicht mehr der ASCII-Code, sondern der internationale Unicode verwendet wird, der einem Zeichen statt einem Byte nun zwei Bytes zuordnet.

Das folgende Beispiel zeigt die einfache Ausgabe von vorzeichenbehafteten Ganzzahltypen mit printf():

00  // Kapitel3/printf_beispiel.c
01 #include <stdio.h>

02 int main(void) {
03 signed char cVal = 100;
04 short sVal = 10000;
05 int iVal = 123456;
06 long lVal = 123456;
07 long long llVal = 123456;

08 printf("%hhd\n", cVal);
09 printf("%hd\n", sVal);
10 printf("%d\n", iVal);
11 printf("%ld\n", lVal);
12 printf("%lld\n", llVal);
13 return 0;
14 }

3.4.1    Vorzeichenlos und vorzeichenbehaftet

Für jeden der soeben vorgestellten signed-Ganzzahltypen steht ein entsprechender unsigned-Ganzzahltyp (standard unsigned integer type) zur Verfügung, der die Zahlen vorzeichenlos betrachtet. Wenn Sie beispielsweise eine Integer-Variable vereinbaren, ist diese (wenn auch implementationsabhängig) meistens vorzeichenbehaftet. Nehmen wir also an, Sie vereinbaren die folgende Variable:

int var;

Hier beträgt der Wertebereich von int abhängig von der Implementierung (siehe INT_MIN und INT_MAX) beispielsweise –2147483648 bis +2147483647. Mit dem Schlüsselwort unsigned können Sie jetzt eine ganzzahlige Variable ohne Vorzeichen vereinbaren. Dies sähe dann beispielsweise so aus:

unsigned int var;

In diesem Fall könnten Sie jedoch keine negativen Werte mehr speichern. Dafür wird der positive Wertebereich von int (abhängig von der Implementierung von UINT_MAX in der Header-Datei limits.h) natürlich größer.

Größer ist nicht gleich größer

Damit Sie das jetzt nicht falsch verstehen: Der Datentyp int bleibt natürlich weiterhin gleich breit. Mit unsigned verschiebt sich lediglich der Wertebereich (abhängig von der Implementierung) von beispielsweise –2147483647 bis +2147483647 auf mindestens 0 bis 4294967295. Man kann auch sagen, dass es in diesem Fall kein Vorzeichenbit (dies ist oft das oberste Bit) mehr gibt, und dass sich dadurch der Wertebereich verdoppelt.

Gleiches wie für int gilt auch für die Datentypen short, long und long long. Bei dem Datentyp char ist es etwas komplizierter. char kann entweder signed char oder unsigned char sein. Der Datentyp char wird gesondert in Abschnitt 3.5.1, »Der Datentyp char«, behandelt.

Mit signed gibt es auch ein Schlüsselwort, um eine Variable explizit als vorzeichenbehaftet zu vereinbaren. Allerdings entspricht die Schreibweise von

signed int var;

der von

int var;

Somit ist die Verwendung des Schlüsselwortes signed überflüssig (außer bei char), weil ganzzahlige Datentypen ohne Verwendung von unsigned immer vorzeichenbehaftet sind.

Mit »unsigned« ändert sich nicht die Bitbreite eines Datentyps, sondern es wird lediglich der Wertebereich verschoben.

Abbildung 3.1    Mit »unsigned« ändert sich nicht die Bitbreite eines Datentyps, sondern es wird lediglich der Wertebereich verschoben.

Der Vollständigkeit halber finden Sie in Tabelle 3.2 einen Überblick über die unsigned-Gegenstücke bei den Ganzzahlen. Zusätzlich finden Sie den Datentyp _Bool, der ebenfalls zu den unsigned-Ganzzahltypen gehört. Die Wertebereiche in der Tabelle entsprechen auch hier wieder dem Mindestwert. Der tatsächliche Wert hängt von der Implementierung ab.

Datentyp

Wertebereich (min.)

Formatzeichen

_Bool
(seit C99)

0 und 1

%u

unsigned char

0 bis 255

%hhu (für dezimal)
%c (für Zeichen)

unsigned short

0 bis 65.535

%hu

unsigned int

0 bis 65.535

%u

unsigned long

0 bis 4.294.967.295

%lu

unsigned long long (seit C99)

0 bis 18.446.744.073.709.551.615

%llu

Tabelle 3.2    Grunddatentypen für vorzeichenlose Ganzzahlen

Überlauf bei unsigned-Ganzzahlwerten

Bei unsigned-Ganzzahlwerten kann es im Falle eines Überlaufs nicht zu einem undefinierten Verhalten kommen. Wenn hier der Wertbereich überschritten wird, wird mit einer sogenannten Modulo-Arithmetik weitergerechnet. Addieren Sie also zu einem maximalen unsigned-Ganzzahltyp z. B. 2 hinzu, wird mit 1 weitergerechnet:

unsigned int uVal = UINT_MAX; // UINT_MAX enthält max. Wert
uVal += 1; // = 0 (kein Überlauf)

Am Rande sei noch erwähnt, dass es auch hier – abhängig von der Implementation – erweiterte unsigned-Ganzzahltypen (extended unsigned integer types) geben kann (z. B. __uint128).

Ganzzahltypen mit fester Breite

Seit C99 werden diese Datentypen über die plattformunabhängige Header-Datei stdint.h um Ganzzahltypen wie etwa int8_t, int16_t, int32_t, int64_t usw. mit fester Länge ergänzt.

3.4.2    Suffixe für Ganzzahlen

Wenn Sie den Compiler informieren müssen, wie er ein bestimmtes Literal interpretieren oder von welchem Typ dieses sein soll, können Sie ein dem Typ entsprechendes Präfix oder Suffix zum Literal hinzufügen. Das Suffix u oder U deutet beispielsweise an, dass es sich um eine vorzeichenlose (unsigned) Zahl handelt. l bzw. L gibt an, dass es sich um eine long-Zahl handelt. Den Datentyp long long zeigen Sie mit ll bzw. LL an. Das können Sie auch mit unsigned kombinieren, indem Sie ein ul oder UL für unsigned long und ull oder ULL für unsigned long long verwenden. Verzichten Sie auf das Suffix, verwendet der Compiler int, sofern der Wert passt. Einige Beispiele wären:

unsigned int uVal = 1000u;
long lVal = 100000L;
unsigned long ulVal = 2222222UL;
long long llVal = 1234LL;
unsigned long long ullVal = 12341234323ULL;
unsigned int uHexVal = 0X42U;