Logo

Marco Cantù's
Essential Pascal

Kapitel 3
Typen, Variablen und Konstanten

Die ursprüngliche Sprache Pascal basiert auf einigen einfachen Begriffen, welche inzwischen in Programmiersprachen allgemein benutzt werden. Der erste ist der Begriff des Datentyps. Der Typ bestimmt die Werte, die eine Variable annehmen kann und die Operationen, die mit ihr durchgeführt werden können. Das Konzept der Typen ist in Pascal strenger als in C, wo arithmetische Datentypen fast beliebig verwendbar sind und viel strenger als in der ursprünglichen Version von BASIC, welches kein ähnliches Konzept besessen hatte.

Variablen

Pascal verlangt, dass alle Variablen deklariert werden bevor sie verwendet werden können. Jedes mal wenn Sie eine Variable deklarieren müssen Sie einen Datentyp festlegen. Hier folgen einige einfache Variablendeklarationen:

var
  Value: Integer;
  IsCorrect: Boolean;
  A, B: Char;

Das Schlüsselwort var kann an verschiedenen Stellen im Code verwendet werden wie z.B. am Anfang des Codes einer Funktion oder Prozedur, um Variablen lokal zu dieser Routine zu deklarieren, oder innerhalb einer Unit, um globale Variablen zu deklarieren. Nach dem Schlüsselwort var folgt eine Liste von Variablennamen gefolgt von einem Doppelpunkt und dem Namen des Datentyps. Sie können auch mehr als eine Variable in einer Zeile angeben, wie oben in der letzten Anweisung.

Wenn Sie eine Variable von einem bestimmten Typ definiert haben können Sie mit ihr nur die Operationen anwenden, die für diesen Datentyp unterstützt werden. Beispielsweise können Sie den Booleschen Wert in einem Test und den Integerwert in einem numerischen Ausdruck verwenden. Sie können nicht Boolean und Integerwerte vermischen (wie es in der Sprache C möglich ist).

Unter Verwendung einfacher Zuweisungen können wir den folgenden Code schreiben:

Value := 10;
IsCorrect := True;

Aber die nächste Anweisung ist nicht korrekt, da die zwei Variablen unterschiedliche Datentypen besitzen:

Value := IsCorrect; // Fehler

Wenn Sie versuchen diesen Code zu übersetzen, so erzeugt Delphi einen Compiler-Fehler mit der Beschreibung: Inkompatible Datentypen: 'Integer' und 'Boolean'. Gewöhnlich sind derartige Fehler Programmierfehler, da es keinen Sinn ergibt, einem Integer Datentyp den Wert True oder False zuzuweisen. Sie sollten nicht Delphi für diesen Fehler verantwortlich machen. Es warnt Sie nur, dass mit dem Code etwas nicht in Ordnung ist.

Natürlich ist es oft möglich, den Wert einer Variable von einem Typ in einen anderen Typ umzuwandeln. In einigen Fällen erfolgt diese Umwandlung automatisch, aber gewöhnlich müssen Sie eine spezielle Systemfunktion aufrufen, die die interne Darstellung der Daten umwandelt.

In Delphi können Sie einer globalen Variable bei ihrer Deklaration einen Anfangswert zuweisen. Beispielsweise können Sie schreiben:

var
  Value: Integer = 10;
  Correct: Boolean = True;

Diese Initialisierungstechnik funktioniert nur für globale Variablen, nicht für Variablen, die im Gültigkeitsbereich einer Prozedur oder Methode deklariert wurde.

Konstanten

Pascal gestattet auch die Deklaration von Konstanten zu Namenswerten, die während der Programmausführung nicht verändert werden. Um eine Konstante zu deklarieren, müssen Sie keinen Datentyp angeben, sondern nur einen Initialwert zuweisen. Der Compiler wird den Wert erkennen und automatisch den geeigneten Datentyp verwenden. Hier sind einige Beispieldeklarationen:

const
  Thousand = 1000;
  Pi = 3.14;
  AuthorName = 'Marco Cantù';

Delphi ermittelt den Datentyp der Konstanten basierend auf deren Wert. Im oberen Beispiel wird die Konstante Tausend mit dem Typ SmallInt abgebildet, dem kleinsten ganzzahligen Datentyp, der den Wert aufnehmen kann. Wenn Sie Delphi anweisen möchten, einen bestimmten Typ zu benutzen, so können Sie einfach den Typnamen in die Deklaration einfügen, wie in:

const
  Thousand: Integer = 1000;

Wenn Sie eine Konstante deklarieren, so kann der Compiler wählen, ob er der Konstanten eine Speicherstelle zuordnet und den Wert dort speichert, oder den tatsächlichen Wert überall dort dupliziert, wo die Konstante verwendet wird. Diese zweite Variante ist besonders sinnvoll für einfache Konstanten.

Hinweis: Die 16-Bit Version von Delphi gestattet Ihnen, den Wert einer typisierten Konstante zur Laufzeit zu verändern als ob es eine Variable ist. Die 32-Bit Version gestattet diese Verhalten immer noch aus Gründe der Abwärtskompatibilität, wenn Sie die Compiler-Direktive $J freigegeben haben oder das entsprechende Kontrollkästchen Assignable typed constants auf der Seite Compiler des Dialogs Projektoptionen verwenden. Obwohl dies der Vorgabewert ist, sind Sie gut beraten, diesen Trick nicht als allgemeine Programmiertechnik zu verwenden. Die Zuweisung eines neuen Werts an eine Konstante verhindert alle Compiler-Optimierungen für Konstanten. Vereinbaren Sie in solchem Fall einfach eine Variable.

Ressourcen-Strings

Wenn Sie eine String-Konstante definieren, so können Sie folgendes eingeben:

const
  AuthorName = 'Marco Cantù';

Ab der Delphi-Version 3 können Sie aber auch folgendes schrieben:

resourcestring
  AuthorName = 'Marco Cantù';

In beiden Fällen definieren Sie eine Konstante, d.h. einen Wert den Sie während der Programmausführung nicht verändern. Der Unterschied besteht nur in der Realisierung. Eine String-Konstante, die mit der Direktive resourcestring definiert ist, wird in den Ressourcen des Programms und dort in einer String-Tabelle gespeichert.

Um diese Fähigkeit in Aktion zu sehen, können Sie einen Blick auf das Beispiel ResStr werfen, welches eine Schaltfläche mit dem folgenden Code besitzt:

resourcestring
  AuthorName = 'Marco Cantù';
  BookName = 'Essential Pascal';

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage (BookName + #13 + AuthorName);
end;

Die Ausgabe der zwei Strings erfolgt in zwei Zeilen, da die Strings durch das Zeichen Newline (angegeben durch dessen numerischen Wert in der Zeichentypkonstante #13) getrennt werden.

Der interessante Aspekt dieses Programms ist, dass Sie die neuen Strings in den Resourcen finden werden, wenn Sie es mit einem Resourcen-Explorer untersuchen (unter den Beispielprogrammen, die mit Delphi geliefert werden, befindet sich auch ein Resourcen-Explorer). Das bedeutet, dass die Strings nicht Teil des übersetzten Codes sind, sondern in einem getrennten Bereich der ausführbaren Datei (der EXE-Datei) gespeichert werden.

Hinweis: Zusammengefasst besteht der Vorteil von Ressourcen in einer effizienten Speicherverwaltung, die durch Windows vorgenommen wird und in der einfachen Lokalisierung eines Programms (Übersetzung der Strings in eine andere Sprache), ohne dabei dessen Quellcode verändern zu müssen.

Datentypen

In Pascal existieren verschieden vordefinierte Datentypen, welche in drei Gruppen unterteilt werden können: ordinale Typen, reelle Typen und Strings. Wir weden ordinale und reelle Typen in den folgenden Abschnitten besprechen, während Strings später in diesem Kapitel behandelt werden. In diesem Abschnitt werde ich auch einige Typen vorstellen, die durch die Delphi-Bibliotheken definiert werden (und nicht durch den Compiler vordefiniert sind) aber auch als vordefinierte Typen betrachtet werden können.

Delphi beinhaltet auch einen untypisierten Datentyp, der als Variante bezeichnet wird und im Kapitel 10 dieses Buchs besprochen wird. Seltsamerweise ist eine Variante ein Datentyp ohne korrekte Typprüfung. Er wurde in Delphi 2 eingeführt, um die OLE-Automatisierung zu handhaben.

Ordinale Typen

Ordinale Typen basieren auf den Konzept einer Reihenfolge oder Sequenz. Sie können damit nicht nur zwei Werte vergleichen, um festzustellen, welcher größer ist, sondern auch nach dem folgenden oder vorhergehenden Wert zu einem gegebenen Wert fragen oder den kleinsten bzw. größten möglichen Wert berechnen lassen.

Die drei wichtigsten vordefinierten ordinalen Typen sind Integer, Boolean und Char (Zeichen). Es gibt jedoch eine Reihe weiterer verwandter Typen, die die selbe Bedeutung aber eine unterschiedliche interne Darstellung und einen unterschiedlichen Wertebereich besitzen. Die folgenden Tabelle 3.1 gibt die ordinalen Datentypen an, die für die Darstellung von Zahlen verwendet werden:

Tabelle 3.1: Ordinale Datentypen für Zahlen

Größe

vorzeichenbehaftet
Wertebereich

vorzeichenlos
Wertebereich

8 Bit ShortInt
-128 bis 127
Byte
0 bis 255
16 Bit SmallInt
-32.768 bis 32.767
Word
0 bis 65.535
32 Bit LongInt
-2.147.483.648 bis 2.147.483.647
LongWord (seit Delphi 4)
0 bis 4.294.967.295
64 Bit Int64
-9.223.372.036.854.775.808 bis 9.223.372.036.854.775.807
 
16/32 Bit Integer Cardinal

Wie Sie sehen können, entsprechen diese Typen unterschiedlichen Zahlendarstellungen, abhängig von der Anzahl von Bits, die verwendet werden, um die Werte und das Vorhandensein oder Abwesenheit eines Vorzeichen-Bits auszudrücken. Vorzeichenbehaftete Werte können positiv oder negativ sein, besitzen aber einen kleineren Wertebereich, da ein Bit weniger für den Zahlenwert selbst verfügbar ist. Sie können den verfügbaren Wertebereich für jeden Typ mit dem Beispiel Range überprüfen, welches im nächsten Abschnitt besprochen wird.

Die letzte Gruppe (gekennzeichnet als 16/32) umfasst Werte, die in den 16-Bit und 32-Bit Versionen von Delphi eine unterschiedliche Darstellung besitzen. Integer und Cardinal werden häufig verwendet, da diese der natürlichen Darstellung von Zahlen in der CPU (dem Mikroprozessor) entsprechen.

Ganzzahlige Typen in Delphi 4

In Delphi 3 sind die 32-Bit vorzeichenlosen Zahlen, die durch den Cardinal-Typ bezeichnet werden, eigentlich nur 31-Bit Werte mit einem Bereich bis zu 2 Gigabyte. Delphi 4 führte den neuen vorzeichenlosen Zahlentyp LongWord ein. Dieser verwendet einen echten 32-Bit Wert bis zu 4 Gigabyte. Der Cardinal-Typ ist nun ein Alias auf den neuen LongWord-Typ. Wie oben angegeben, gestattet es LongWord, 2GB mehr Datenwerte als eine vorzeichenlose Zahl zu beschreiben. Darüber hinaus entspricht es der natürlichen Darstellung von Zahlen in der CPU.

Ein weiterer neuer Typ, der in Delphi 4 eingeführt wurde, ist der Int64-Type, der Integer-Zahlen mit bis zu 18 Stellen darstellen kann. Dieser neue Typ wird durch einige der Ordinaltyproutinen (wie z.B. High und Low), numerischen Routinen (wie z.B. Inc und Dec) und String-Umwandlungsroutinen (wie z.B. IntToStr) voll unterstützt. Für die entgegengesetzte Umwandlung, aus einem String in eine Zahl, existieren zwei neue, spezielle Funktionen: StrToInt64 und StrToInt64Def.

Boolean

Boolesche Werte werden im Gegensatz zum Boolean-Typ selten verwendet. Einige Boolesche Werte mit spezieller Darstellung sind für die Windows API-Funktionen erforderlich. Diese Typen sind ByteBool, WordBool und LongBool.

In Delphi 3 wurden die Datentypen ByteBool, WordBool und LongBool aus Kompatibilitätsgründen mit Visual Basic und der OLE-Automation dahingehend modifiziert, dass der Wert True mit -1 dargestellt wird während der Wert False weiterhin 0 ist. Der Datentyp Boolean blieb unverändert (True ist 1, False ist 0). Falls Sie explizite Typumwandlungen in Ihrem Delphi 2-Code verwendet haben, so kann eine Portierung des Codes zu späteren Versionen von Delphi zu Fehlern führen.

Zeichen

Abschließend gibt es zwei unterschiedliche Darstellungen für Zeichen ANSIChar und WideChar. Der erste Typ stellt 8-Bit Zeichen dar, die dem traditionell von Windows benutzen ANSI-Zeichensatz entsprechen; der zweite stellt 16-Bit Zeichen dar, die dem neuen, von Windows NT unterstützten Unicode-Zeichensatz entsprechen, aber nur teilweise von Windows 95 und 98 unterstützt werden. Die meiste Zeit werden Sie einfach den Char-Typ verwenden, der in Delphi 3 mit ANSIChar übereinstimmt. Denken Sie dennoch immer daran, dass die ersten 256 Unicode-Zeichen exakt mit den ANSI-Zeichen übereinstimmen.

Zeichenkonstanten können mit ihrer symbolischen Schreibweise dargestellt werden, wie in 'k', oder mit einer numerischen Schreibweise, wie in #78. Letztere kann auch durch Verwendung der Chr-Funktion ausgedrückt werden, wie in Chr(78). Die entgegengesetzte Umwandlung kann mit der Ord-Funktion durchgeführt werden.

Es ist im allgemeinen besser, die symbolische Schreibweise zu verwenden, wenn Buchstaben, Ziffern oder Symbole darzustellen sind. Wenn jedoch Sonderzeichen darzustellen sind, so verwenden Sie im allgemeinen die numerische Schreibweise. Die folgeden Liste beinhaltet einige der am Häufigsten verwendeten Sonderzeichen:

Das Beispiel Range

Um Ihnen eine Vorstellung über die unterschiedlichen Wertebereiche von einigen der ordinalen Typen zu geben, habe ich ein einfaches Delphi-Programm mit dem Namen Range geschrieben. Einige Ergebnisse werden in Abbildung 3.1 gezeigt.

Abbildung 3.1: Das Beispiel Range zeigt einige Informationen über ordinale Datentypen (in diesem Falle den Integer-Typ).

Das Programm Range basiert auf einem einfachen Formular, welches sechs Schaltflächen besitzt (jedes nach einem ordinalen Datentyp benannt) und einigen Beschriftungen für die Informationsarten, wie Sie in Abbildung 3.1 sehen können. Einige dieser Beschriftungen dienen zur Aufnahme von statischem Text, während andere zur Darstellung der Informationen über den Typ dienen, wenn eine der Schaltflächen betätigt wird.

Jedesmal wenn Sie eine der Schaltflächen auf der rechten Seite drücken, so aktualisiert das Programm die Beschriftungen mit den Ausgabewerten. Die einzelnen Beschriftungen zeigen den Datentyp, die Anzahl der verwendeten Bytes und den minimalen sowie maximalen Wert, den der Datentyp speichern kann. Jede Schaltfläche besitzt ihre eigene OnClick-Ereignisbehandlungsroutine, da sich der verwendete Code zur Berechnung der drei Werte von Schalter zu Schalter geringfügig unterscheidet. Hier ist beispielsweise der Quellcode des OnClick-Ereignis für den Integer-Schalter (BtnInteger):

procedure TFormRange.BtnIntegerClick(Sender: TObject);
begin
  LabelType.Caption := 'Ganzzahl';
  LabelSize.Caption := IntToStr (SizeOf (Integer));
  LabelMax.Caption := IntToStr (High (Integer));
  LabelMin.Caption := IntToStr (Low (Integer));
end;

Wenn Sie etwas Erfahrung mit der Delpi-Programmierung besitzen, so können Sie den Quellcode des Programms untersuchen, um zu verstehen, wie er arbeitet. Für Anfänger genügt es zu bemerken, dass drei Funktionen verwendet werden: SizeOf, High und Low. Die Ergebnisse der letzten beiden Funktionen sind ordinale Werte der selben Art (in diesem Fall Integer-Werte) und das Ergebnis der SizeOf-Funktion ist immer ein Integer-Wert. Das Ergebnis von jeder dieser Funktionen wird zunächst, unter Verwendung der IntToStr-Funktion, in einen String umgewandelt und dann in die Caption-Eigenschaft der drei Beschriftungen kopiert.

Die den anderen Schaltflächen zugeordneten Methoden ähneln sehr stark der oben angegebenen. Der einzige echte Unterschied besteht im Datentyp der den verschiedenen Funktionen als Parameter übergeben wird. Abbildung 3.2 zeigt die Ergebnisse bei Ausführung des selben Programms unter Windows 95, nachdem es mit der 16-Bit Version von Delphi neu übersetzt wurde. Wenn Sie Abbildung 3.1 mit Abbildung 3.2 vergleichen, so können Sie den Unterschied zwischen dem 16-Bit und 32-Bit Integer-Datentyp erkennen.

Abbildung 3.2: Die Ausgabe der 16-Bit Version des Range-Beispiels, wobei wieder die Informationen zum Integer-Typ angezeigt werden.

Die Größe des Integer-Typs variiert in Abhängigkeit der CPU und des Betriebssystems, das Sie gerade verwenden. Im 16-Bit Windows ist eine Integer-Variable zwei Byte groß. Im 32-Bit Windows dagegen ist eine Integer-Variable vier Byte groß. Aus diesem Grund erhalten Sie eine unterschiedliche Ausgabe, wenn Sie das Range-Beispiel neu übersetzen.

Die zwei unterschiedlichen Darstellungen des Integer-Typs stellen kein Problem dar, solange Ihr Programm keine Annahmen über die Größe des Integer-Typs vornimmt. Wenn Sie jedoch einen Integer-Wert mit der einen Version in eine Datei speichern und ihn mit der anderen wieder auslesen, so können Sie einige Probleme bekommen. In diesem Fall sollten Sie einen Platform-unabhängigen Datentyp wählen (wie z.B. LongInt oder SmallInt). Für mathematische Berechnungen oder allgemeinen Code ist es jedoch die bessere Variante, die Standarddarstellung ganzzahliger Werte der jeweiligen Platform zu verwenden - d.h. verwenden Sie den Integer-Typ, da er das ist, was die CPU am Liebsten mag. Der Integer-Typ sollte ihre erste Wahl sein, wenn Integer-Zahlen verwendet werden. Verwenden Sie eine andere Darstellung nur dann, wenn es einen vernünftigen Grund gibt, so etwas zu tun.

Routinen für ordinale Typen

Es existieren einige Systemroutinen (Routinen, die in der Sprache Pascal und in der Delphi-Unit System definiert sind), die mit ordinalen Typen arbeiten. Diese sind in Tabelle 3.2 aufgeführt. C++ Programmierer sollten beachten, dass die zwei Versionen der Inc-Prozedur, mit einem oder mit zwei Parametern, den ++ und += Operatoren entsprechen (das selbe gilt auch für die Dec-Prozedur).

Tabelle 3.2: Systemroutinen für ordinale Typen

Routine

Verwendungszweck

Dec Verringert die als Parameter übergebene Variable um Eins oder um den Wert des optionalen zweiten Parameters.
Inc Erhöht die als Parameter übergebene Variable um Eins oder um den angegebenen Wert.
Odd Liefert True, wenn das Argument eine ungerade Zahl ist.
Pred Liefert den Wert vor dem Argument in der Reihenfolge, die durch den Datentyp bestimmt wird: den Vorgänger.
Succ Liefert den Wert hinter dem Argument: den Nachfolger.
Ord Liefert eine Zahl, die der Ordnungszahl des Arguments innerhalb der Wertemenge seines Datentyps.
Low Liefert den kleinsten Wert im Wertebereich des ordinalen Typs der als Parameter angegeben wird.
High Liefert den größten Wert im Wertebereich des ordinalen Datentyps.

Beachten Sie, das einige dieser Routinen, wenn auf Konstanten angewendet, automatisch durch den Compiler ausgewertet und durch das Ergebnis ersetzt werden. Wenn Sie beispielsweise High(X) aufrufen, wobei X als Integer definiert ist, so kann der Compiler auf einfache Weise den Ausdruck durch den größtmöglichen Wert des Integer-Datentyps ersetzen.

Reelle Typen

Reelle Datentypen repräsentieren Gleitkommazahlen in verschiedenen Formaten. Die kleinste Speichergröße ist bei Single-Zahlen gegeben, die mit einem 4-Byte Wert implementiert werden. Weiterhin existieren Double-Gleitkommazahlen, die mit 8 Byte implementiert werden und Extended-Zahlen, die mit 10 Byte implementiert werden. Dieses sind alles Gleitkommazahlen mit unterschiedlicher Präzision, welche dem IEEE-Standard zur Gleitkommadarstellung entsprechen und für eine maximale Geschwindigkeit direkt durch den numerischen Coprozessor unterstützt werden.

In Delphi 2 und Delphi 3 hatte der Real-Typ die gleiche Definition wie in der 16-Bit Version; es war ein 48-Bit Typ. Borland hat jedoch von seiner Verwendung abgeraten und empfohlen, statt dessen die Typen Single, Double und Extended zu verwenden. Der Grund für diese Empfehlung besteht darin, dass dieses alte 6-Byte Format weder durch die Intel CPU unterstützt wird, noch unter den offiziellen IEEE Gleitkommatypen aufgeführt ist. Um dieses Problem zu umgehen modifiziert Delphi 4 die Definition des Real-Typ derart, dass er eine standardisierte 8-Byte (64-Bit) Gleitkommazahl darstellt.

Zusätzlich zu dem Vorteil der Verwendung eines standardisierten Formats gestattet diese Veränderung den Komponenten die Veröffentlichung von Eigenschaften, die auf dem Real-Typ basieren, etwas das unter Delphi 3 nicht erlaubt war. Unter den Nachteilen dieses Verfahrens können jedoch Kompatibilitätsprobleme auftreten. Wenn erforderlich, können Sie mögliche Inkompatibilitäten beseitigen, indem Sie auf die alte Delphi 2 und 3 Definition des Typs zurücksetzen. Unter Verwendung der folgenden Compiler-Option können Sie dies erreichen:

{$REALCOMPATIBILITY ON}

Es existieren auch zwei ungewöhnliche Datentypen: Comp beschreibt sehr große Integer-Werte unter Verwendung von 8 Byte (welche Zahlen mit 18 Dezimalstellen aufnehmen kann) und Currency (nicht verfügbar im 16-Bit Delphi), das einen dezimalen Festkommawert mit vier Nachkommastellen aufnimmt und die selbe 64-Bit Darstllung wie der Comp-Typ verwendet. Wie der Name bereits andeutet, wurde der Datentyp Currency eingeführt, um sehr genaue Währungsangaben mit vier Nachkommastellen zu handhaben.

Wir können kein analoges Programm ähnlich dem Range-Beispiel mit reellen Datentypen erstellen, da wir nicht die Funktionen High, Low und Ord auf Variablen mit reellen Typen anwenden können. Reelle Typen repräsentieren (theoretisch) eine unendliche Menge von Zahlen, ordinale Typen hingegen repräsentieren eine feste Menge von Werten.

Hinweis: Lassen Sie mich das noch näher erklären. Wenn Sie den Integer-Wert 23 haben, so können Sie bestimmen, welches der Nachfolger ist. Integer-Werte sind endlich (sie haben einen bestimmten Bereich und sie haben auch eine Reihenfolge). Gleitkommazahlen sind unendlich, auch innerhalb eines kleinen Bereichs, und besitzen keine Reihenfolge: Wie viele Werte liegen z.B. zwischen 23 und 24? Und welche Zahl folgt auf 23,46? Ist es die 23,47 , 23,461 oder 23,4601? Das lässt sich nicht bestimmen!

Aus diesem Grund hat es einen Sinn, nach der ordinalen Position des Zeichens w im Bereich des Datentyps Char zu fragen, aber es hat überhaupt keinen Sinn, die gleiche Frage nach 7143,1562 im Bereich der Gleitkommadatentypen zu fragen. Obwohl Sie tatsächlich feststellen können, ob eine reelle Zahl größer als eine andere ist, so hat es keinen Sinn danach zu fragen, wieviele reelle Zahlen vor einer gegebenen Zahl liegen (denn das ist die Bedeutung der Ord-Funktion).

Reelle Typen besitzen im Code-Teil für die Bedienoberfläche (auf der Windows Seite) nur eine begrenzte Rolle, aber sie werden durch Delphi voll unterstützt, einschließlich der Datenbankseite. Die Unterstützung der IEEE-Standard Gleitkommatypen macht die Sprache Objekt-Pascal vollständig geeignet für den breiten Bereich von Programmen, die numerische Berechnungen erfordern. Wenn Sie sich für diesen Aspekt interessieren, so können Sie sich die arithmetischen Funktionen ansehen, die von Delphi in der Unit System bereitgestellt werden (verwenden Sie die Delphi Hilfe für weitere Details).

Hinweis: Delphi besitzt auch eine Unit Math, die weiterführende mathematische Routinen definiert. Hier existieren trigonometrische Funktionen (wie die ArcCosh-Funktion), finanzmathematische Funktionen (wie die InterestPayment-Funktion) und statistische Funktionen (wie die MeanAndStdDev-Prozedur). Dort gibt es eine Reihe derartiger Funktionen, von denen einige für mich recht seltsam klingen, wie z.B. die MomentSkewKurtosis-Prozedur (ich überlasse es Ihnen herauszufinden, was es damit auf sich hat).

Datum und Uhrzeit

Delphi verwendet auch reelle Typen zur Handhabung von Datums- und Uhrzeitinformationen. Um genauer zu sein, definiert Delphi einen besonderen Datentyp TDateTime. Dieser ist ein Gleitkommatyp, da der Typ groß genug sein muss, um Jahre, Monate, Tage, Stunden Minuten und Sekunden bis hinunter zur Millisekundenauflösung in einer einzelnen Variable zu speichern. Das Datum wird als Anzahl der Tage seit dem 30.12.1899 (mit negativen Werten, um ein Datum vor 1899 anzuzeigen) im ganzzahligen Teil des TDateTime-Wertes gespeichert. Uhrzeiten werden als Bruchteile eines Tages im Nachkommateil des Wertes gespeichert.

TDateTime ist kein vordefinierter Typ, den der Compiler versteht, sondern wird in der Unit System definiert als:

type
  TDateTime = type Double;

Die Verwendung des Typs TDateTime ist ganz einfach, da Delphi eine Reihe von Funtionen beinhaltet, die mit diesem Datentyp arbeiten. Sie können eine Liste dieser Funktionen in Tabelle 3.3 finden.

Tabelle 3.3: Systemroutinen für den Typ TDateTime

Routine

Beschreibung

Now Liefert das aktuelle Datum und die Uhrzeit in einem einzigen TDateTime-Wert.
Date Liefert nur das aktuelle Datum.
Time Liefert nur die aktuelle Uhrzeit.
DateTimeToStr Wandelt einen Datums- und Uhrzeitwert in einen String unter Verwendung der Vorgabeformatierung um; verwenden Sie die statt dessen die Funktion FormatDateTime, um eine stärkere Kontrolle über die Umwandlung zu bekommen.
DateTimeToString Kopiert den Datums- Uhrzeitwert in einen String-Puffer in der Standardformatierung.
DateToStr Wandelt den Datumsteil einer TDateTime-Variable in einen String.
TimeToStr Wandelt den Uhrzeitanteil einer TDateTime-Variable in einen String.
FormatDateTime Formatiert einen Datums- und Uhrzeitwert unter Verwendung des angegebenen Formats; Sie können festlegen, welche Werte Sie sehen möchten und welches Format dafür verwendet wird indem Sie einen komplexen Formatierungs-String angeben.
StrToDateTime Wandelt einen String mit Datums- und Uhrzeitinformationen in einen TDateTime-Wert um und erzeugt eine Exception, falls der String ein fehlerhaftes Format besitzt.
StrToDate Wandelt einen String mit einem Datumswert in das TDateTime-Format.
StrToTime Wandelt einen String mit einem Uhrzeitwert in das TDateTime-Format.
DayOfWeek Liefert die dem Wochentag entsprechende Zahl des TDateTime-Werte, der als Parameter übergeben wurde.
DecodeDate Ermittelt die Werte Jahr, Monat und Tag von einem Datumswert.
DecodeTime Zerlegt den Uhrzeitanteil eines TDateTime-Wertes in Stunden, Minuten, Sekunden und Millisekunden.
EncodeDate Wandelt die Werte Jahr, Monat und Tag in einen TDateTime-Wert.
EncodeTime Wandelt die Werte Stunden, Minuten, Sekunden und Millisekunden in einen TDatetime-Wert.

Um Ihnen die Verwendung dieses Datentyps und einiger der damit im Zusammenhang stehenden Funktionen zu zeigen, habe ich ein einfaches Beispiel mit dem Namen TimeNow aufgebaut. Das Hauptformular dieses Beispiels besitzt eine Schaltfläche und eine ListBox-Komponente. Wenn das Programm startet, berechnet es automatisch die aktuelle Zeit sowie das Datum, und zeigt diese an. Jedes mal wenn die Schaltfläche betätigt wird, zeigt das Programm die vergangene Zeit seit dem Programmstart an.

Hier ist der Code, der mit dem Ereignis OnCreate des Formulars verbunden ist:

procedure TFormTimeNow.FormCreate(Sender: TObject);
begin
  StartTime := Now;
  ListBox1.Items.Add (TimeToStr (StartTime));
  ListBox1.Items.Add (DateToStr (StartTime));
  ListBox1.Items.Add ('Drücke Schalter zur Zeitmessung');
end;

Die erste Anweisung ist ein Aufruf der Funktion Now, welche das momentane Datum und die Uhrzeit zurückliefert. Dieser Wert wird in der Variablen StartTime gespeichert, die als eine globale Variable wie folgt vereinbart wurde:

var
  FormTimeNow: TFormTimeNow;
  StartTime: TDateTime;

Ich habe nur die zweite Deklaration hinzugefügt, da die erste von Delphi angelegt wurde. Standardmäßig sieht sie folgendermaßen aus:

var
  Form1: TForm1;

Wenn der Name des Formulars geändert wird, so erfolgt automatisch eine Aktualisierung dieser Deklaration. Die Verwendung von globalen Variablen ist eigentlich nicht die beste Lösung, Es wäre besser ein privates Feld innerhalb der Form-Klasse zu benutzen. Das ist ein Thema das mit der objektorientierten Programmierung zusammenhängt und in den Büchern "Mastering Delphi 4'' und "5" behandelt wird.

Die nächsten drei Anweisungen fügen der rechts auf den Formular befindlichen ListBox-Komponente drei Positionen hinzu. Das Ergebnis können Sie in der Abbildung 3.3 sehen. Die erste Zeile enthält den in einen String umgewandelten Zeitanteil des TDateTime-Wertes, die zweite den Datumsanteil des selben Wertes. An Ende fügt der Code einen einfachen Hinweis an.

Abbildung 3.3: Die Ausgabe des Beispiels TimeNow beim Programmstart.

Der dritte String wird durch das Programm ersetzt, wenn der Anwender auf die Schaltfläche Elapsed klickt:

procedure TFormTimeNow.ButtonElapsedClick(Sender: TObject);
var
  StopTime: TDateTime;
begin
  StopTime := Now;
  ListBox1.Items [2] :=  FormatDateTime ('hh:nn:ss',
    StopTime - StartTime);
end;

Dieser Code ermittelt die aktuelle Zeit und berechnet die Differenz zu dem gespeicherten Zeitpunkt, der zum Programmstart ermittelt wurde. Da wir einen Wert benötigen, der in einer anderen Ereignisbehandlungsmethode berechnet wurde, müssen wir ihn in einer globalen Variable speichern. Es gibt dafür jedoch bessere Alternativen, die auf der Verwendung von Klassen beruhen.

Hinweis: Der Code, der den aktuellen Wert der dritten Zeile ersetzt, verwendet den Index 2. Der Grund besteht darin, dass die Zeilen einer ListBox nullbasiert sind: der erste Eintrag besitzt die Nummer 0, der zweite die Nummer 1 und der dritte die Nummer 2. Mehr dazu wenn wir Arrays behandeln.

Neben dem Aufruf der Funktionen TimeToStr und DateToStr können Sie die noch leistungsfähigere Funktion FormatDateTime verwenden, wie ich es in der letzten Methode getan habe (vergl. die Delphi Hilfedatei für nähere Informationen über die Formatierungsparameter). Beachten Sie auch, dass Datums- und Zeitwerte in Abhängigkeit der internationalen Einstellungen von Windows in Strings umgewandelt werden. Delphi liest diese Werte vom System und kopiert diese in eine Reihe von globalen Konstanten, die in der Unit SysUtils deklariert werden. Einige davon sind:

DateSeparator: Char;
ShortDateFormat: string;
LongDateFormat: string;
TimeSeparator: Char;
TimeAMString: string;
TimePMString: string;
ShortTimeFormat: string;
LongTimeFormat: string;
ShortMonthNames: array [1..12] of string;
LongMonthNames: array [1..12] of string;
ShortDayNames: array [1..7] of string;
LongDayNames: array [1..7] of string;

Weitere globale Konstanten beziehen sich auf die Formatierung von Währungs- und Gleitkommazahlen. Sie können die vollständige Liste in der Delphi-Hilfedatei unter dem Stichwort Währungswerte (Formatvariablen) finden.

Hinweis: Delphi enthält eine Komponente DateTimePicker, die eine elegante Eingabemöglichkeit für ein Datum besitzt, nämlich die Auswahl aus einem Kalender.

Windows-spezifische Typen

Die vordefinierten Datentypen, die wir bisher gesehen haben, sind Teil der Pascal-Sprache. Delphi beinhaltet auch weitere Datentypen, die durch Windows definiert sind. Diese Datentypen sind nicht integraler Bestandteil der Sprache, sondern sie sind Teil der Windows-Bibliotheken. Die Windows-Typen enthalten neue Vorgabetypen (wie DWORD oder UINT), viele Records (oder Stukturen), verschieden Zeigertypen usw..

Den wichtigste Typ unter den Windows-Datentypen stellen Handles dar, die in Kapitel 9 besprochen werden.

Typumwandlung und Typkonvertierung

Wie wir bereits gesehen haben, ist es nicht möglich, eine Variable an eine andere mit einem unterschiedlichen Typ zuzuweisen. Falls Sie so etwas tun müssen gibt es dafür zwei Möglichkeiten. Die erste Möglichkeit ist die Typumwandlung, welche eine einfache Funktionsnotation mit dem Namen des gewünschten Datentyps verwendet:

var
  N: Integer;
  C: Char;
  B: Boolean;
begin
  N := Integer ('X');
  C := Char (N);
  B := Boolean (0);

Sie können die Typumwandlung zwischen Datentypen verwenden, die die gleiche Größe besitzen. Gewöhnlich ist es sicher, Typumwandlungen zwischen ordinalen Typen oder zwischen reellen Typen durchzuführen, es ist aber auch möglich Typumwandlungen zwischen Zeigertypen (und auch Objekten) durchzuführen, solange Sie wissen, was Sie tun.

Im allgemeinen ist jedoch eine Typumwandlung eine gefährliche Programmiermethode, da sie Ihnen gestattet, einen Wert zu verwenden als ob er etwas vollig anderes darstellen würde. Wenn die interne Darstellung der Daten nicht passt, riskieren Sie schwer aufzuspürende Fehler. Aus diesem Grunde sollten Sie die Typumwandlung im allgemeinen vermeiden.

Die zweite Möglichkeit besteht in der Verwendung von Typkonvertierungsroutinen. Diese Routinen für unterschiedliche Arten der Konvertierung sind in der Tabelle 3.4 zusammengefasst. Einige dieser Routinen arbeiten mit Datentypen, die wir in den folgenden Abschnitten behandeln werden. Beachten Sie, dass die Tabelle keine Routinen für spezielle Typen beinhaltet (wie TDateTime oder Variant) sowie auch keine Routinen enthält, die speziell für die Formatierung gedacht sind. Hierzu würden die leistungsfähigen Routinen Format und FormatFloat zählen.

Tabelle 3.4: Systemroutinen für die Typkonvertierung

Routine

Beschreibung

Chr Wandelt eine ordinale Zahl in ein ANSI-Zeichen.
Ord Wandelt einen ordinalen Typ in die Zahl, die seine Reihenfolge kennzeichnet.
Round Wandelt einen reellen Typ in einen Integer-Wert und rundet dabei den Wert.
Trunc Wandelt einen reellen Typ in einen Integer-Wert und schneidet dabei den Wert ab.
Int Liefert den ganzzahligen Anteil aus dem Gleitkommawert des Arguments.
IntToStr Wandelt eine Zahl in einen String um.
IntToHex Wandelt eine Zahl in einen String mit der hexadezimalen Darstellung um.
StrToInt Wandelt einen String in eine Zahl um und erzeugt eine Exception, wenn der String keinen gültigen Integer-Wert darstellt.
StrToIntDef Wandelt einen String in ein Zahl um und verwendet einen Vorgabewert, wenn der String nicht korrekt ist.
Val Wandelt einen Sting in eine Zahl um (traditionelle Turbo Pascal Routine, verfügbar aus Kompatibilitätsgründen).
Str Wandelt eine Zahl in einen String unter Verwendung von Formatierungsparametern um (traditionelle Turbo Pascal Routine, verfügbar aus Kompatibilitätsgründen).
StrPas Wandelt einen nullterminierten String in einen String im Pascal-Stil. Diese Umwandlung wird im 32-Bit Delphi für AnsiStrings automatisch durchgeführt (Vergleichen Sie dazu das Kapitel über Strings).
StrPCopy Kopiert einen String im Pascal-Stil in einen nullterminierten String. Diese Konvertierung wird im 32-Bit Pascal durch eine einfache PChar-Umwandlung durchgeführt (Vergleichen Sie dazu das Kapitel über Strings).
StrPLCopy Kopiert einen Teil eines Pascal-Stil Strings in einen nullterminierten String.
FloatToDecimal Wandelt einen Gleitkommawert in einen Record um, der dessen dezimale Darstellung aufnimmt (Exponent, Nachkommastellen und Vorzeichen).
FloatToStr Wandelt einen Gleitkommawert in seine String-Darstellung unter Verwendung des Standardformats um.
FloatToStrF Wandelt einen Gleitkommawert in seine String-Darstellung unter Verwendung des angegebenen Formats um.
FloatToText Kopiert einen Gleitkommawert in einen String-Puffer unter Verwendung des Standardformats.
FloatToTextFmt Kopiert wie die vorherige Routine einen Gleitkommawert in einen String-Puffer aber unter Verwendung des angegebenen Formats.
StrToFloat Wandelt den angegebenen Pascal-String in einen Gleitkommawert um.
TextToFloat Wandelt den angegebenen nullterminierten String in einen Gleitkommawert um.

Zusammenfassung

In diesem Kapitel haben wir die grundlegende Notation von Typen in Pascal untersucht. Aber diese Sprache besitzt ein weiteres sehr wichtiges Merkmal: Sie gestattet dem Programmierer neue anwendungsspezifische Datentypen zu definieren, die als Benutzerdefinierte Datentypen bezeichnet werden. Dies ist das Thema des nächsten Kapitels.

Nächstes Kapitel: Benutzerdefinierte Datentypen

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000
© Copyright der deutschen Übersetzung Immo Wache, 2000