Programmierkurs: Delphi: Pascal: Typumwandlung

Typumwandlung

Bearbeiten

Von einer Typumwandlung spricht man, wenn Daten eines Typs in einen anderen, kompatiblen Typ konvertiert werden sollen. Einige solcher Typumwandlungen erledigt Delphi bereits automatisch, zum Beispiel wenn man eine Ganzzahl einer Gleitkommavariablen zuweisen möchte. Da die Menge der reellen Zahlen auch die ganzen Zahlen enthält, ist hierbei nichts besonderes zu beachten.

Wie führt man nun eine Typumwandlung durch? Bei der Zuweisung an eine Variable des Zieltyps wird dabei der Typbezeichner wie ein Funktionsname verwendet. Als „Parameter“ wird ein Wert oder eine Variable des Ausgangstyps mitgegeben:

Var2 := Zieltyp(Var1);

Das Ergebnis der Umwandlung kann im Übrigen auch direkt verwendet werden, z.B. bei der Datenausgabe oder als Parameter einer weiteren Funktion.

Eine Typumwandlung ist immer dann möglich, wenn die Daten sowohl bei dem einen als auch bei dem anderen Datentyp die gleiche interne Darstellung im Arbeitsspeicher haben. So ist zum Beispiel eine Typumwandlung zwischen Char und Byte möglich, auch wenn diese für uns in der Betrachtung völlig verschiedene Daten, nämlich ein Textzeichen und eine Zahl darstellen. Im Arbeitsspeicher bleibt der Wert in beiden Fällen der Gleiche. Hierzu ein Beispiel:

var
  b: Byte;
  c: Char;

begin
  b := 88;
  c := Char(b);
  Writeln(c);          // gibt X aus, da X den ASCII-Wert 88 (dezimal) besitzt
  Writeln(Byte('o'));  // gibt 111 aus, den ASCII-Wert des Zeichens o
end.

Hinweis: Für die Umwandlung von Byte in Char gibt es ebenfalls die Funktion Chr, für den umgekehrten Weg die Funktion Ord.

Aufzählungen

Bearbeiten

Bei Aufzählungen wurden ja, wir erinnern uns, jeder Position in der Aufzählungsliste ein Name zugeordnet. In dem entsprechenden Kapitel hatten wir schon kurz die Typumwandlung angesprochen.

Gerade bei Aufzählungen kann es von Interesse sein, den Namen in eine Zahl oder andersherum zu konvertieren, z.B. wenn man bestimmte Programmeinstellungen in einer Konfigurationsdatei speichern möchte. Da in diesem Falle eine automatische Umwandlung in eine Zahl (oder beim Einlesen zurück zum Bezeichner) nicht funktioniert, müssen wir hier eine explizite Typumwandlung anwenden. Als Beispiel soll uns noch einmal die Ampel dienen:

type
  TAmpel = (rot, gelb, gruen);

var
  Ampel: TAmpel;
  Zahl: Byte;

begin
  Ampel := gelb;

// Writeln(Ampel);       <-- würde eine Fehlermeldung ergeben (ungültiger Typ)
  Writeln(Byte(Ampel));  // gibt 1 aus - oder: Writeln(Ord(Ampel));

  Write('Bitte eine Zahl von 0 bis 2 eingeben: ');
  Readln(Zahl);
  Ampel := TAmpel(Zahl);
end.

Im ersten Teil des Beispiels wird der Positionswert des Aufzählungsbezeichners gelb in seinen Zahlwert 1 umgewandelt. Auch hier können wir wieder für die Typumwandlung Byte oder Ord verwenden. Im zweiten Teil wird die vom Benutzer eingegebene Zahl wieder in den entsprechenden Wert vom Typ TAmpel, also rot, gelb oder gruen umgewandelt.

Zeichenketten

Bearbeiten

Bei Zeichenketten ist eine Typumwandlung normalerweise nicht nötig. Wer jedoch zum Beispiel mit Windows-Systemfunktionen arbeitet, kommt an dem Datentyp PChar nicht vorbei. Da dies ein besonderer Datentyp ist, haben wir ihn bisher nicht behandelt.

Die Betriebssystem-Bibliotheken sind in den Programmiersprachen C bzw. C++ geschrieben. In diesen Sprachen gibt es keinen eigenständigen Datentyp für Zeichenketten. Daher wird ein Zeiger auf ein bei Null beginnendes Array von Zeichen verwendet, das mit dem ASCII-Zeichen #0 endet. Diese Form der Zeichenketten heißt nullterminierter String. PChar ist eine solche nullterminierte Zeichenkette.

Um die Arbeit zu erleichtern, kann man einer Variablen vom Typ PChar zwar einen konstanten Zeichenkettenwert direkt zuweisen, jedoch keine Variable vom Typ String. Dazu benötigen wir eine Typumwandlung:

var
  p: PChar;
  s: string;

begin
  p := 'Hallo';             // kein Problem, konstanter String-Wert
  s := 'Welt';
// p := s;                  Fehler, inkompatible Typen
  p := PChar(s);            // so funktioniert's
  Writeln(p);               // gibt "Welt" aus

  p := 'Hallo ' + 'Welt!';  // auch das ist möglich
  s := p;                   // in diese Richtung geht's auch ohne Typumwandlung
end.

Da PChar lediglich den Zeiger auf das erste Zeichen der Zeichenkette darstellt, kann man einen String auch auf folgende Weise in den Typ PChar umwandeln:

p := @s[1];

Zeiger und Adressen

Bearbeiten

Mit einem Zeiger erhalten wir ja für gewöhnlich Zugriff auf die Daten, auf welche dieser verweist. Die Speicheradresse eines Zeigers selbst erhalten wir wiederum durch Typumwandlung. Hierbei sollte man eine Umwandlung in den Typ LongWord vornehmen, da dieser eine 32-Bit-Zahl ohne Vorzeichen zurückgibt:

var
  p: ^Integer;

begin
  p^ := 666;
  Writeln(LongWord(p));   // gibt die Speicheradresse von p aus, nicht 666
end.

Hinweis: Bei einem 64-Bit-Compiler bietet sich eine Typumwandlung nach Int64 an.

Achtung: Obwohl auch per Typumwandlung eine Zahl in eine Speicheradresse konvertiert und diese einem Zeiger zugewiesen werden könnte, sollte man davon tunlichst die Finger lassen (es sei denn, man weiß genau, was man da tut). Das Auslesen von Adressen, z.B. für Zwecke der Fehlersuche oder um bestimmte Funktionsweisen nachzuvollziehen, ist dagegen jedoch völlig unbedenklich.

Zu Objekten kommen wir zwar erst später, aber gerade bei diesen werden Sie die Typumwandlung regelmäßig benötigen. Als kurze Vorschau sei schonmal gesagt, das ein Objekt die initialisierte Variable eines Klassen-Typs ist.

Klassen können vererbt und dabei immer mehr im Funktionsumfang erweitert werden. Die Nachkommen einer Klasse enthalten immer alle Felder und Methoden der Basisklasse. Man kann also generische Methoden schreiben, die einen Parameter der Basisklasse erwarten und kann diesen auch alle Erben der Basisklasse übergeben. Um auf den erweiterten Funktionsumfang der Nachkommen zuzugreifen, kann man dann wiederum die Typumwandlung einsetzen. Dazu wollen wir uns nur ein kurzes Beispiel ansehen, quasi als Vorgeschmack. Hoffentlich werden Sie von dessen Umfang nicht gleich abgeschreckt:

type
 TBasis = class
   Name: string;
 end;

 // erbt Name von TBasis und fügt Zahl hinzu
 TErbe = class(TBasis)
   Zahl: Byte;
 end;

{ Gibt den Namen der Klasse aus und bei Nachkommen
  vom Typ TErbe auch den Wert des Feldes Zahl }
procedure SchreibeDaten(Klasse: TBasis);
begin
  Writeln(Klasse.Name);
  if Klasse is TErbe then
    Writeln(TErbe(Klasse).Zahl);   // hier erfolgt die Typumwandlung
  Writeln;
end;

var
  b: TBasis;
  e: TErbe;

begin
  b := TBasis.Create;              // Objekt erstellen
  b.Name := 'Basis-Klasse';        // und mit Daten füllen

  e := TErbe.Create;               // nochmal mit dem Nachkommen
  e.Name := 'Nachkommen-Klasse';
  e.Zahl := 71;

  SchreibeDaten(b);                // gibt nur den Namen aus
  SchreibeDaten(e);                // gibt den Namen und die Zahl aus

  b.Free;                          // Speicher wieder freigeben
  e.Free;
end.

Was nicht geht

Bearbeiten

Typumwandlungen sind immer dann nicht einsetzbar, wenn wie gesagt die Daten im Speicher dazu geändert werden müssten. Man kann zum Beispiel nicht per Typumwandlung die Zeichenkette '1234' in den Integer-Wert 1234 konvertieren. Die Zeichenkette liegt als Hexadezimalfolge $31323334 vor, der Integer-Wert hätte hingegen den Hexadezimalwert $04D2. Für eine solche tatsächliche Datenumwandlung müssen entsprechende Funktionen eingesetzt werden, in diesem Falle erhalten Sie mit IntToStr das gewünschte Ergebnis.


  Pascal: Typdefinition Inhaltsverzeichnis Pascal: Prozedurale Typen