![]() |
Zugriff auf Variante Teile in Record-Typen
Beim Zugriff auf die Varianten Teile eines Records sollten diese normalerweise auf der gleichen Adresse liegen, oder?
Delphi-Quellcode:
Die Adresse von T2 ist 4 Bytes größer als die Adresse von T1! Wieso?
TTest = record
case Typ:Boolean of false: (T1 :Integer); true: (T2 :Double); end; Da es kein packed record ist gehe ich davon aus, dass der Compiler die varianten Teile des Records gleich ausrichtet (Compilerschalter "Recordfelder ausrichten" ein [Delphi5] bzw. auf 8 [Delphi7 und Delphi2005]). Ändert man obriges Record wie folgt, dann ergeben sich gleiche Adressen für T1 und T2:
Delphi-Quellcode:
Hintergrund:
TTest = record
Dummy: Integer; case Typ:Boolean of false: (T1 :Integer); true: (T2 :Double); end; Meine Anwendung übergibt ein (sinnvolles) Record einer DLL die in C++ geschrieben ist. Die DLL definiert das gleiche Record, wobei der Variante Teil als "union" definiert wird. Interessanterweise richtet der C++ Compiler bei varianten Teile des Record gleich aus. Da es Delphi nicht gleich ausrichtete kommt es zu einer Verschiebung um 4 Bytes beim Zugriff auf Teil mit der kleineren Adresse. Das original Record definiert übrigens im varianten Teil weitere Records. Die Beispiele habe ich exemplarisch vereinfacht um das Problem zu verdeutlichen. Für eine Erklärung wäre ich dankbar. |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
Delphi-Quellcode:
...ist das gleiche in grün.
type
TTest = record Typ : Boolean; case Boolean of False: (T1: Integer); True : (T2: Double); { end; } end; |
Re: Zugriff auf Variante Teile in Record-Typen
Korrekt, ist das selbe:
Debugger meldet @Test.T1: $12F3E8 @Test.T2: $12F3EC wobei var Test:TTest; ... immernoch 4 Byte unterschied! |
Re: Zugriff auf Variante Teile in Record-Typen
Eventuell richtet Delphi den Record andersrum aus, obwohl dieses eigentlich falsch wäre?
Versuch es mal so (Variante Teile mit der selben Größe):
Delphi-Quellcode:
oder:
TTest = record
case Typ: Boolean of false: (T1, dummy: Integer); true: (T2: Double); end;
Delphi-Quellcode:
TTest = packed record
case Typ: Boolean of false: (T1, dummy: Integer); true: (T2: Double); end; |
Re: Zugriff auf Variante Teile in Record-Typen
Variante 1 - Fehler:
@Test.Typ: $12F3E4 @Test.T1: $12F3E8 @Test.T2: $12F3EC Variante 2 - OK, aber ist ja auch packed record: @Test.Typ: $12F3EB @Test.T1: $12F3EC @Test.T2: $12F3EC ... Ziel ist es ohne Dummy auszukommen UND ohne packed (C++ DLL, die das Record verarbeitet, ist auch nicht packed) |
Re: Zugriff auf Variante Teile in Record-Typen
Ich würde mich nicht auf das Alignment verlassen (ist auch der Grund für den Compiler-Bug).
Du solltest die Struktur als 'packed' deklarieren und die Zwischenräume durch die benötigte 'Ausrichtung' mit Variablen füllen... Ein Workaround könnte so aussehen:
Delphi-Quellcode:
edit:
type
TTest = record case Integer of 0: (Typ : Boolean); // LongBool? (check C/C++ code) 1: (Reserved: Double; // Alignment { end; } case {Typ: }Boolean of False: (T1: Integer); True: (T2: Double)); { end; } end; Zitat:
ps: muss es denn auf der C/C++-Seite unbedingt eine unbenannte Union sein? (ist ohnehin nicht standard-konform...) |
Re: Zugriff auf Variante Teile in Record-Typen
Als Workaround kann ich natürlich alle records auf gerade Adressen trimmen indem ich Dummy-Bytes einführe. Das ist auch die derzeitige Lösung. Leider besteht das original Projekt aus ein paar Duzend Records die miteinander verschachtelt sind. Hier manuell eine Ausrichtung auf die korrketen Adressen durchzuführen ist aufwendig und frustrierend. Ich denke schon, dass das die Aufgabe des Compilers ist. Ich hoffe immernoch auf eine Lösung per Compiler (der lieben Codesicherheit wegen) bzw. einer Erklärung, ob diese krumme Adressausrichtung möglicherweise beabsichtigt ist.
|
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
Ich habe meinen letzten Beitrag editiert; kanst du bitte die Frage nach den unbenannten Unions beantworten? |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
|
Re: Zugriff auf Variante Teile in Record-Typen
ahh, es könnte tatsächlich an der Speicherausrichtung liegen ^^
Code:
Boolean/ByteBool:
************************ // Ausrichtung alle 1 Byte Integer/LongInt: *---*---*---*---*---*--- // Ausrichtung alle 4 Byte Double: *-------*-------*------- // Ausrichtung alle 8 Byte unpacked: * *--- // Boolean + Integer * *------- // Boolean + Double packed: **--- // Boolean + Integer **------- // Boolean + Double |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
Code:
Würde übersetzt werden mit:
union {
int T1; double T2; } u1;
Delphi-Quellcode:
...unbenannte Unions sind eine Strafe für jeden Entwickler der C/C++ nach Delphi portieren muss...
u1: record
case Boolean of False: (T1: Integer); True: (T2: Double); { end; } end; |
Re: Zugriff auf Variante Teile in Record-Typen
Es ist offensichtlich, dass das Problem die Speicherausrichtung ist.
Delphi-Quellcode:
- TTest wird je nach Compilerschalter gerade ausgerichtet (Startadresse)
TTest = record
case Typ:Boolean of false: (T1 :Integer); true: (T2 :Double); end; - Typ (Boolean) ist 1Byte lang (wäre es ein Aufzählungstyp, dann auch 1Byte, es sei denn man aktiviert {$Z4} (4Bytes)) - Es folgt speicherüberlappend T1 (4Bytes lang) und T2 (8Bytes lang) -> nur warum nicht an der gleichen Anfangsadresse??? @Nico Bendlin Der Code in C++ sieht wie folgt aus (wenn ich mich nicht irre)
Code:
Jedenfalls ist hier die Adresse von T1 == Adresse von T2!
struct {
Bool Typ; union { int T1; double T2; }; } TTest; Siehe hierzu: ![]() |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
Zitat:
...in einer Typ-Definition haben anonyme Unions eigentlich nichts zu suchen. ( siehe ![]() Es wäre besser gewesen, wenn die Deklaration so ausgesehen hätte:
Code:
Wie auch immer, es hält dich niemand davon ab, die Struktur in Delphi anders zu übersetzen (zum Beispiel mit u1 als inline-record)...
typedef struct tag_TTest {
bool Typ; union { int T1; double T2; } u1; } TTest, * PTest; |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
|
Re: Zugriff auf Variante Teile in Record-Typen
Nunja, wir kommen wohl nicht weiter ...
Mein Eindruck ist, dass hier ein Delphi-Bug vorliegt. Meine Versuche zeigten, dass die Anfangsadressen von T1 und T2 abhängig von der Position des Typ sind, egal ob der Typ vor der case oder in der case steht. Es ist auch egal wie lang die varianten Teile des Record sind. Es bleibt also als Workaround nur das Einfügen von Bytes bis ein gerade Adresse vorliegt (getestet und OK) oder die Verwendung eines weiteren lokalen record um die case (getestet und OK, aber dadurch lokaler Bezeichner -> umständlich). Die Deklaration der union in C++ ist davon unabhängig. Hier liegen in allen Varianten gleiche Adressen vor! Vielleicht werde ich mal bei Borland nachfragen ... Danke für die Diskussion. |
Re: Zugriff auf Variante Teile in Record-Typen
Moin ma2xx,
je nach Delphi-Version könntest Du es auch einmal mit $A bzw. $ALIGN probieren den Compiler zum "mitspielen" zu bewegen. |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
|
Re: Zugriff auf Variante Teile in Record-Typen
{$A-}, {$A1}, oder {$ALIGN OFF} sollte wohl helfen?
Zitat:
wobei hierfür der Abschitt "Record-Typen" (wird von dem oben angegebenen Abschitt weiterverlinkt) zutreffender ist. |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
|
Re: Zugriff auf Variante Teile in Record-Typen
Ich bin für neue Ideen immer dankbar ...
Wenn ich folgenden Code teste ...
Delphi-Quellcode:
... dann erhalte ich tatsächlich gleiche Adressen für T1 und T2:
TTest = record
case Typ:Boolean of {$A-} false: (T1 :Integer); true: (T2 :Double); {$A+} end; @Test.T1: $12F3D5 @Test.T2: $12F3D5 Diese Vorgehensweise macht prinzipiell Sinn, wenn man sich als Programmierer dem Problem unterschiedlicher Adressen für die varianten Teile des Records bewusst ist (zumindest muss ich nicht alle Records manuell nachrechnen). Nur jetzt muss man noch klären, ob C++ für die varianten Teile immer eine gerade (ausgerichtete) Adresse standardmäßig verlangt, denn wie unschwer zu erkennen, sind in meinem Beispiel die Adressen ungerade. Ich werde das auch nochmal klären ... |
Re: Zugriff auf Variante Teile in Record-Typen
Zitat:
Test-Code:
Delphi-Quellcode:
Ergebnis (Delphi 6):
type
TTest = record case Typ: Boolean of {$A-} False: (T1: Integer); True: (T2: Double); {$A+} end; function FieldOffset(const Struct, Field): Cardinal; begin Result := Cardinal(Addr(Field)) - Cardinal(Addr(Struct)); end; procedure TForm1.Button1Click(Sender: TObject); var Test: TTest; begin ShowMessage( 'T1: ' + IntToStr(FieldOffset(Test, Test.T1)) + #13#10 + 'T2: ' + IntToStr(FieldOffset(Test, Test.T2)) ); end;
Code:
Ziel sollte aber sein, dass beide Offsets 8 sind (wie in der C-Referenzstruktur).
T1: 1
T2: 1 Gruß Nico |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz