Wenn man in einem Programm mit ganzen Zahlen arbeiten möchte, also Zahlen ohne Nachkommastellen wie 5, -12 oder 1000, dann verwendet man einen Integer-Typ (vom lateinischen *integer* = „ganz“). Integer-Typen sind die am häufigsten genutzten Datentypen in der Programmierung überhaupt, denn viele alltägliche Dinge lassen sich damit abbilden: Zähler, Indizes, Ergebnisse von Berechnungen, Alter, Anzahl von Elementen usw.
In Pascal deklariert man eine Variable so:
var MeineZahl: Integer;
Danach kann man ihr einen Wert zuweisen und mit ihr rechnen:
MeineZahl := 42; MeineZahl := MeineZahl + 8; // MeineZahl ist jetzt 50
Eine naheliegende Frage ist: Warum reicht nicht ein einziger Integer-Typ für alle Zwecke? Die Antwort liegt im Speicher. Jeder Datentyp belegt eine bestimmte Anzahl an Bits im Arbeitsspeicher, und die Anzahl der Bits bestimmt, wie große (oder wie kleine) Zahlen man darin speichern kann.
Ein Bit kann nur zwei Zustände annehmen: 0 oder 1. Mit 8 Bit (= 1 Byte) kann man 2⁸ = 256 verschiedene Werte darstellen. Mit 16 Bit sind es 2¹⁶ = 65.536 Werte, mit 32 Bit schon 2³² = über 4 Milliarden, und mit 64 Bit entsprechend 2⁶⁴ - eine astronomisch große Zahl.
Außerdem gibt es für jeden Größenbereich zwei Varianten:
- Vorzeichenbehaftet (signed): Die Zahl kann negativ oder positiv sein. Die Hälfte der möglichen Werte wird für negative Zahlen genutzt.
- Vorzeichenlos (unsigned): Die Zahl ist immer ≥ 0. Dafür ist der positive Wertebereich doppelt so groß.
Diese Überlegungen führen zu einer ganzen Familie von Integer-Typen, die Pascal bereitstellt.
In Delphi und FreePascal ist `Integer` der Standard-Integer-Typ und der, den man im Alltag am häufigsten sieht. Er ist vorzeichenbehaftet und hat einen Wertebereich von:
> -2.147.483.648 bis 2.147.483.647
Das sind gut ±2 Milliarden. Für die allermeisten Alltagsaufgaben - Zähler, Indizes, Berechnungen - ist dieser Bereich mehr als ausreichend.
Hier gibt es einen wichtigen Unterschied zwischen Delphi und FreePascal:
- In Delphi ist `Integer` seit Delphi 2 fest als 32-Bit-Typ definiert und bleibt das auch auf 64-Bit-Systemen. Delphi hat sich bewusst für diese Stabilität entschieden.
- In FreePascal ist `Integer` ebenfalls standardmäßig ein 32-Bit-Typ (ein Alias für `LongInt`), allerdings kann das in bestimmten Compiler-Modi oder auf exotischen Plattformen abweichen.
Für die tägliche Arbeit unter Windows kann man davon ausgehen: `Integer` = 32 Bit = 4 Byte.
program IntegerBeispiel;
var
Alter: Integer;
Punkte: Integer;
Summe: Integer;
begin
Alter := 25;
Punkte := 1500;
Summe := Alter + Punkte;
WriteLn('Alter: ', Alter);
WriteLn('Punkte: ', Punkte);
WriteLn('Summe: ', Summe);
end.
Diese Typen haben eine garantierte, plattformunabhängige Größe - egal ob man für 32-Bit oder 64-Bit kompiliert, sie verhalten sich immer gleich. Das macht sie besonders zuverlässig, wenn man genaue Kontrolle über den Speicher braucht.
var s: ShortInt;- Größe: 1 Byte (8 Bit) - Wertebereich: -128 bis 127 - Verwendung: Selten im Alltag. Sinnvoll wenn sehr viele kleine Zahlenwerte gespeichert werden sollen und Speicher knapp ist, z.B. in großen Arrays.
var Temperatur: ShortInt; begin Temperatur := -15; // Passt problemlos Temperatur := 200; // FEHLER! 200 > 127, Überlauf! end;
var s: SmallInt;
- Größe: 2 Byte (16 Bit)
- Wertebereich: -32.768 bis 32.767
- Verwendung: Für Werte, die etwas größer als ein Byte sein müssen, aber nie die 32.767 überschreiten. Beispielsweise Koordinaten in kleinen Grafikprogrammen.
var X, Y: SmallInt; begin X := 1920; // Bildschirmkoordinate - passt noch Y := 1080; // Passt auch X := 40000; // FEHLER! Überlauf! end;
var l: LongInt;
- Größe: 4 Byte (32 Bit)
- Wertebereich: -2.147.483.648 bis 2.147.483.647
- Verwendung: Identisch mit `Integer` (in Delphi und FreePascal ist `Integer` ein Alias dafür). Wenn man explizit 32 Bit garantieren möchte, nimmt man `LongInt`.
var i: Int64;
- Größe: 8 Byte (64 Bit)
- Wertebereich: -9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807
- Verwendung: Wenn 32 Bit nicht reichen. Typische Einsatzgebiete: Dateigrößen, Zeitstempel (Unix-Zeit in Millisekunden), sehr große Zähler oder wissenschaftliche Berechnungen.
var
Dateigroesse: Int64;
begin
Dateigroesse := 10 * 1024 * 1024 * 1024; // 10 Gigabyte in Bytes
WriteLn('Dateigröße: ', Dateigroesse, ' Bytes');
end;
Hinweis: Bei `Int64`-Berechnungen muss man aufpassen, dass Zwischenergebnisse nicht überlaufen. Der Ausdruck `10 * 1024 * 1024 * 1024` wird vom Compiler standardmäßig als 32-Bit-Berechnung durchgeführt, was einen Überlauf erzeugen kann. Besser: mindestens einen Operanden explizit als `Int64` schreiben: `Int64(10) * 1024 * 1024 * 1024`.
Vorzeichenlose Typen können keine negativen Zahlen speichern. Dafür ist ihr positiver Wertebereich doppelt so groß wie der des entsprechenden vorzeichenbehafteten Typs.
var b: Byte;
- Größe: 1 Byte (8 Bit)
- Wertebereich: 0 bis 255
- Verwendung: Sehr häufig! Einzelne Bytes in Binärdaten, RGB-Farbwerte (0-255), kleine Flags.
var Rot, Gruen, Blau: Byte; begin Rot := 255; // Maximale Intensität Gruen := 128; Blau := 0; // Rot := -1; // FEHLER! Unsigned kann nicht negativ sein end;
var w: Word;
- Größe: 2 Byte (16 Bit)
- Wertebereich: 0 bis 65.535
- Verwendung: Portnummern im Netzwerk (0-65535), Zeichencodes, ältere Dateiformate.
var Port: Word; begin Port := 8080; // Typischer HTTP-Alternativport Port := 443; // HTTPS end;
var lw: LongWord;
- Größe: 4 Byte (32 Bit)
- Wertebereich: 0 bis 4.294.967.295
- Verwendung: IPv4-Adressen (intern als 32-Bit-Zahl), Hashwerte, Windows-API-Typen (`DWORD` entspricht diesem Typ).
- Aliase: In Delphi und FreePascal auch als `Cardinal` bekannt (ebenfalls 32 Bit, unsigned).
var IPAdresse: LongWord; begin // 192.168.1.1 als 32-Bit-Zahl (Big-Endian) IPAdresse := (192 shl 24) or (168 shl 16) or (1 shl 8) or 1; end;
var q: QWord;
- Größe: 8 Byte (64 Bit)
- Wertebereich: 0 bis 18.446.744.073.709.551.615
- Verwendung: Sehr große vorzeichenlose Werte, Bitmasks in 64-Bit-Systemen, kryptografische Berechnungen.
| Typ | Vorzeichen | Bits | Bytes | Minimum | Maximum |
|---|---|---|---|---|---|
| `ShortInt` | ja | 8 | 1 | -128 | 127 |
| `Byte` | nein | 8 | 1 | 0 | 255 |
| `SmallInt` | ja | 16 | 2 | -32.768 | 32.767 |
| `Word` | nein | 16 | 2 | 0 | 65.535 |
| `LongInt` | ja | 32 | 4 | -2.147.483.648 | 2.147.483.647 |
| `LongWord` | nein | 32 | 4 | 0 | 4.294.967.295 |
| `Int64` | ja | 64 | 8 | -9,2 × 10¹⁸ | 9,2 × 10¹⁸ |
| `QWord` | nein | 64 | 8 | 0 | 1,8 × 10¹⁹ |
Pascal kennt einige Aliase, also alternative Namen für denselben Typ. Es ist gut, sie zu kennen, damit man beim Lesen von fremdem Code nicht verwirrt wird:
| Alias | Entspricht | Bits |
|---|---|---|
| `Integer` | `LongInt` | 32 |
| `Cardinal` | `LongWord` | 32 |
| `DWord` | `LongWord` | 32 |
| `UInt64` | `QWord` | 64 |
Einer der häufigsten Anfängerfehler ist der Integer-Überlauf (*overflow*). Er passiert, wenn das Ergebnis einer Berechnung den Wertebereich des Typs überschreitet. Der Wert „rollt“ dann um - ähnlich wie ein Kilometerzähler, der nach 99.999 wieder bei 0 beginnt.
var b: Byte; begin b := 255; b := b + 1; // Überlauf! b ist jetzt 0, nicht 256 WriteLn(b); // Gibt 0 aus end;
Standardmäßig prüft FreePascal nicht zur Laufzeit auf Überläufe. Man kann die Prüfung aber aktivieren:
{$R+} // Range-Checking einschalten
{$Q+} // Overflow-Checking einschalten
Mit diesen Compiler-Direktiven bricht das Programm bei einem Überlauf mit einer Fehlermeldung ab, anstatt stillschweigend falsche Ergebnisse zu liefern. Das ist besonders beim Entwickeln und Testen sehr hilfreich.
Manchmal muss man einen Wert von einem Typ in einen anderen umwandeln. Dafür gibt es verschiedene Möglichkeiten:
Pascal erlaubt automatische Umwandlung von einem kleineren in einen größeren Typ, wenn das verlustfrei möglich ist:
var b: Byte; i: Integer; begin b := 100; i := b; // Kein Problem: Byte passt immer in Integer end;
Umgekehrt - vom größeren in den kleineren Typ - muss man explizit umwandeln. Pascal warnt hier, weil Datenverlust möglich ist:
var i: Integer; b: Byte; begin i := 200; b := Byte(i); // Expliziter Cast: funktioniert hier, weil 200 in Byte passt i := 300; b := Byte(i); // Überlauf! 300 mod 256 = 44 - b ist 44 end;
Es ist gute Praxis, vor einer Umwandlung zu prüfen, ob der Wert in den Zieltyp passt:
var
i: Integer;
b: Byte;
begin
i := 200;
if (i >= 0) and (i <= 255) then
b := Byte(i)
else
WriteLn('Wert passt nicht in ein Byte!');
end;
Bisher haben alle besprochenen Typen eine feste Größe - egal auf welcher Plattform. Es gibt aber auch Typen, deren Größe sich an die Zielplattform anpasst. Das klingt zunächst unsicher, hat aber einen wichtigen Vorteil: Der Typ ist immer genau so groß wie die native Wortbreite des Prozessors, was oft die effizienteste Wahl ist.
Diese Typen sind genau so groß wie ein Zeiger auf der jeweiligen Plattform:
- Auf einem 32-Bit-System: 32 Bit (4 Byte)
- Auf einem 64-Bit-System: 64 Bit (8 Byte)
var
n: NativeInt;
begin
n := SizeOf(Pointer); // 4 auf 32-Bit, 8 auf 64-Bit
WriteLn('Zeigergröße: ', n, ' Bytes');
end;
`NativeInt` ist vorzeichenbehaftet, `NativeUInt` vorzeichenlos. Diese Typen nutzt man vor allem dann, wenn man Zeigerarithmetik betreibt oder direkt mit Speicheradressen arbeitet.
`SizeInt` ist ein FreePascal-spezifischer Typ. Er ist ebenfalls plattformabhängig und hat dieselbe Größe wie `NativeInt`. Der Name deutet auf seinen Hauptverwendungszweck hin: Er ist der Typ, den FreePascal intern für Größenangaben und Indizes verwendet.
- In der FreePascal-Standardbibliothek ist `SizeInt` der Rückgabetyp von `SizeOf()` und `Length()`.
- `SizeUInt` ist die vorzeichenlose Variante, entsprechend dem C-Typ `size_t`.
var
Laenge: SizeInt;
MeinArray: array of Integer;
begin
SetLength(MeinArray, 100);
Laenge := Length(MeinArray); // Length gibt SizeInt zurück
WriteLn('Länge: ', Laenge);
end;
Wann sollte man `SizeInt` verwenden? Immer dann, wenn man mit Längen, Größen oder Indizes von Datenstrukturen arbeitet, die theoretisch auf 64-Bit-Systemen sehr groß werden könnten. Ein `Integer` (32 Bit) kann maximal ~2 Milliarden als Index darstellen - auf einem 64-Bit-System mit `SizeInt` sind deutlich größere Strukturen möglich.
Diese Typen sind ebenfalls plattformabhängig und in FreePascal speziell für das Speichern von Zeigerwerten in Integer-Variablen gedacht. Sie sind genauso groß wie ein Zeiger und können deshalb verlustfrei eine Speicheradresse aufnehmen.
var
Adresse: PtrUInt;
Zahl: Integer;
begin
Zahl := 42;
Adresse := PtrUInt(@Zahl); // Adresse der Variable als Zahl
WriteLn('Adresse: ', Adresse);
end;
Das ist vor allem in der Systemprogrammierung relevant, z.B. wenn man mit der Windows-API oder mit dynamisch alloziiertem Speicher arbeitet.
| Typ | Vorzeichen | 32-Bit-System | 64-Bit-System | Verwendung |
|---|---|---|---|---|
| `NativeInt` | ja | 32 Bit | 64 Bit | Zeigerarithmetik |
| `NativeUInt` | nein | 32 Bit | 64 Bit | Zeigerarithmetik |
| `SizeInt` | ja | 32 Bit | 64 Bit | Längen, Indizes |
| `SizeUInt` | nein | 32 Bit | 64 Bit | Längen (unsigned) |
| `PtrInt` | ja | 32 Bit | 64 Bit | Zeiger als Integer |
| `PtrUInt` | nein | 32 Bit | 64 Bit | Zeiger als Integer |
Als Faustregel für den Alltag:
- Integer ist die erste Wahl für normale ganzzahlige Variablen. Er ist leicht zu lesen, gut verstanden und reicht für die meisten Aufgaben.
- Byte nimmt man für einzelne Datenbytes, Farbwerte oder wenn man sich auf den Bereich 0-255 verlassen möchte.
- LongInt / LongWord verwendet man, wenn man die 32-Bit-Größe explizit und plattformunabhängig garantieren möchte - z.B. beim Lesen und Schreiben von Binärdateien mit festem Format.
- Int64 greift man, wenn 32 Bit nicht reichen: Dateigrößen > 2 GB, große Zähler, Zeitstempel.
- SizeInt ist die richtige Wahl für Längen und Indizes, wenn Code sowohl auf 32-Bit- als auch auf 64-Bit-Systemen korrekt und effizient laufen soll.
- NativeInt / PtrInt gehören in die Systemprogrammierung, wenn man direkt mit Zeigern und Speicheradressen hantiert.
Mit diesem Werkzeugkasten ist man für praktisch alle Aufgaben gerüstet, die in der Pascal-Programmierung mit ganzen Zahlen auftauchen.