Moin!
Zitat von
jfheins:
Für ich beinhaltet ein Cast immer ein Konvertierung :gruebel:
Ein Cast ist die Anweisung für den Compiler etwas entgegen seines bekannten Typs anders zu interpretieren. Der Compiler weiss, es ist ein Integer, aber du kannst ihm sagen, nein, betrachte es als double oder string. Dabei wird nur die Betrachtungsweise der Daten im Speicher an der Stelle geändert. Es findet jedoch keine Wandlung der Daten in irgendeiner Form statt.
Ein Konvertierung wandelt vorhandene Daten und erzeugt neue. Diese können zwar wieder auf die Originale Speicherstelle abgelegt werden, aber die zugrunde liegenden Daten dienen als Ausgangspunkt für eine explizite Arbeitsanweisung.
//EDIT: Einschub Anfang - Beispiel in elektronischer Form -
Ein Integer liegt im Speicher in seiner binären Form vor, also eine Basis von 2 und das linear hintereinander. Um diesen mit der Basis von 10 auszugeben, muss er konvertiert werden und ist mit einer Basis von 10 nicht mehr binär als Integer im System speicherbar, da dieses fest mit der Basis von 2 arbeitet. Somit muss zwangsweise ein Typ her, der die Daten mit der Basis von 10 aufnehmen kann, also ein String. Gleiches gilt für die BCD Kodierung, welche zwar auch die Basis von 2 nutzt, aber eine anderen Einteilung hat. Hintergrund ist hierbei, dass pro Stelle 6 mögliche Werte ungenutzt bleiben (1 Nibble pro Dezimalstelle = 4 Bit = 2^4 = 16 Kodierungsmöglichkeiten von denen nur 0..9, also 10 Möglichkeiten genutzt werden). Die hexadezimale Schreibweise nutzt das volle Nibble zur Darstellung.
Ok, damit haben wir 4 Bits, also 4 digitale Steuerleitungen die 0 & 1 sein können. Wenn ich diese nun ausgeben will, dann kann ich 4 Leuchtdioden nehmen und man kann den Wert ablesen. Hier ist kein Typecast nötig, alles 1:1 verdrahtet. Pro Bit eine LED - passt.
Wenn ich nun aber eine 7 Segmentanzeige habe, dann habe ich nun die Möglichkeit für einen Typecast und einer Konvertierung, da ich 4 Ausgänge habe aber 7 Eingänge an der Anzeige. Eine Konvertierung wäre ein 4-zu-7 Dekoder. Dieser hat einen Eingang von 4 Signalen und 7 Ausgangssignale und der konvertiert die Signale entsprechend. Alles angeschlossen und die Anzeige zeigt die BCD Wert an. Abhängig vom 4-zu-7 Dekoder kann sie auch die hexadezimale Darstellung.
Und nun zum Typecast: ein Typecast wäre hier nun die 4 Leitungen an 4 Segmente der Segmentanzeige zu legen. Die 3 noch nicht beschalteten Eingänge kann man nun freilassen oder man schaltet welche zusammen. Dies wäre ein Typecast, weil man sagt, er soll die 4 Leitungen als 7 Leitungen betrachten. Die Anzeige wird dann schöne Symbole anzeigen, aber selten was was verständliches. Abhängig von der Beschaltung kann das geübte Auge wieder die 4 Ausgänge den LEDs der Segmente zuordnen und man wäre wieder beim ersten Beispiel mit der 1:1 Beschaltung auf 4 LEDs.
//EDIT: Einschub Ende
Alles was in Delphi mit Compilermagic gemacht wird, ist meistens eine Konvertierung. Die Compilermagic selbst fügt diese Konvertierung hinzu. Wäre keine Konvertierung nötig, wäre auch kein Compilermagic nötig.
Ein Beispiel für eine strukturierten Cast wäre eine Union, welche u.a. in Delphi auch genau für einen Typecast eingesetzt wird.
Delphi-Quellcode:
var
lHighDWord, lLowDWord: LongWord;
lInt64: Int64;
begin
lInt64 := $1234567890abcdef;
lHighDWord := Int64Rec(lInt64).Hi;
lLowDWord := Int64Rec(lInt64).Lo;
end;
Der Int64Rec beinhaltet eine Definition um ein und die selbe Speicherstelle in einer Größe von 8 Byte unterschiedlich zu betrachten. Einmal in Form von 2 DWords und einmal in Form von einem Int64. Die Daten im Speicher werden nicht konvertiert, es werden keine neuen Daten erzeugt. Die Zugriffe auf Int64Rec.Hi bzw. Int64Rec.Lo sind Zugriffe direkt auf die Bytestellen des lInt64. Die Union definiert nur eine Menschenlesbare Form bzw. Definition von Byte Offsets.
Ein anderer Fall mit Compilermagic:
Delphi-Quellcode:
var
lDouble: double;
lInt: integer;
begin
lInt := 4556;
lDouble := lInt;
end;
Hier wird intern eine Konvertierung durchgeführt: der Compiler erweitert eine Ganzzahl um den fraktionalen Teil und macht ihn zu einer Fliesskommazahl. Der Wert an sich wird (eigentlich) nicht verändert, aber es ist eine Konvertierung, da aus den alten Daten (lInt) neue Daten (lDouble) generiert werden.
Ein anderer Fall, harter Typecast, keine Compilermagic:
Delphi-Quellcode:
var
lHigherObject: TStrings;
lBaseObject: TObject;
begin
lBaseObject := TObject.Create;
lHigherObject := TStrings(lBaseObject);
end;
Ohne den typecast wird der Delphicompiler korrekterweise meckern, da die Typen unterschiedlich und nicht zuweisungskompatibel sind. Der
type-
cast wiederrum macht keine Konvertierung - aus der TObject Instanz wird keine TStrings Instanz. Hier sagt man dem Compiler nur: ich weiss, das ist ein TObject, aber ich sage dir: betrachte es als TStrings Typ. Da TStrings zu TStrings typengleich sind, hat der Compiler nichts dagegen. Er macht es, aber es wird nichts konvertiert bzw. überprüft.
Der ordentliche Weg wäre hier eine Überprüfung der Typen, aber das ist in diesem Beispiel mal keine Compilermagic sondern versteckt sich in einem Operator (bzw. zwei, da AS IS beinhaltet).
Das bei dem Beispiel nichts konvertiert wurde ist einfach zu sehen beim Zugriff auf die von TStrings eingeführten Methoden und Eigenschaften.
Ein oft genutzter typecast ist auch die Vorzeichenbehaftung zu entfernen. Beispiel:
Delphi-Quellcode:
var
lSigned: LongInt;
lUnsigned: LongWord;
begin
lSigned := -1;
lUnsigned := $ffffffff;
if lSigned = lUnsigned then
;
end;
Der Compiler warnt hier, dass er extra Code eingeführt hat, da der Vergleich so nicht gut durchführbar ist. Er warnt über den Vergleich von signed und unsigned Werten. Er sagt auch, dass er beide Werte erweitert hat. Der Programmierer will hier die Zahlenwerte vergleichen und Delphi erkennt das Dilemma, denn lUnsigned kann Werte bis 4,2 Milliarden annehmen, aber dafür keine unter 0 und lSigned nur bis 2,1 Milliarden, aber das mit und ohne Vorzeichen. Delphi kann nun aber schlecht den lSigned zu einem unsigned machen um es zu vergleichen, weil dann würden alle negativen Zahlen wegfallen. Auch kann er den lUnsigned nicht zu einem signed machen, dann würden alle Werte über 2,1 Milliarden wegfallen. Also erweitert er beide auf den nächst grösseren Typ und vergleicht denn. Um die Warnung wegzubekommen greifen viele Programmierer aber schnell mal zu typecast:
Delphi-Quellcode:
var
lSigned: LongInt;
lUnsigned: LongWord;
begin
lSigned := -1;
lUnsigned := $ffffffff;
if LongWord(lSigned) = lUnsigned then
;
end;
Und dabei tritt dann genau das oben genannte Problem auf: Die negativen Zahlen falle weg. In diesem Falle wäre hier sogar von Gleichheit auszugehen, da die binäre Form von -1 eines signed 32 Bit Wertes so aussieht, dass das MSB gesetzt ist (Bit 31) (Vorzeichen Bit) und alle anderen Bits negiert sind. Somit wäre es bei -1 genau $ffffffff. Somit würde ein Vergleich der oben nach der Korrektur von Delphi mit der Erweiterung noch fehlschlägt bei einem Typecast richtig sein. Hier sieht man vor allem, dass Delphi hier keine Konvertierung der Daten vornimmt sondern einfach den Speicherinhalt neu interpretiert bzw. betrachtet mit der aufgezwungenen Brille eines unsigned dwords.
Und zu den Dingen bezüglich harter und weicher Cast, so wäre dies am besten auch in C++ erklärbar (dynamic_cast<>() && static_cast<>()), wobei Delphi sich da nichts nimmt (IS && TXXXX()).
Und explizit und impliziter Cast: explizit bedeutet, es muss unbedingt was angegeben werden, damit ein Cast gemacht wird. Somit ist der explizite Cast durch die Schreibweise mit neuen Type gut erkennbar. Dieser kann, muss aber keine Compilermagic beinhalten. Ein impliziter Cast wiederrum benötigt keine Angabe von Typen oder extra Code sondern die Konvertierung wird still und heimlich durchgeführt. Dieser beinhaltet meist die Verwendung von Compilermagic.
MfG
Muetze1