Falsche Ergebnisse bei sizeof()

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Antworten
aro
Beiträge: 130
Registriert: Di 26. Jul 2011, 19:58
OS, Lazarus, FPC: Deepin 20.2; Lazarus 2.0.0 + dfsg-2
CPU-Target: 64Bit

Falsche Ergebnisse bei sizeof()

Beitrag von aro »

Lzarus Version ':2.1.0
Datum 2021-06-05
FPC_Version 3.3.1
SVN-Revision:65172
x86_64-linux-gtk2



Dies ist ein Record der vom Syntax her richtig ist. Er kann normal Kompiliert und problemlos benutzt werden.

Code: Alles auswählen

XYRec = record  // oder auch XYRec = packed record
  XY_ComNr :Integer;
  XY_F:Integer; //Vorschub
  XY_X : Integer;//Absolutposition der X-Achse
  XY_Y : Integer;//Absolutposition der Y-Achse
  XY_Z : Integer;//Absolutposition der Z-Achse
  XY_Schritte :Integer;
  case Integer of
    0: (XY_Schritt_X :Double;
        XY_Schritt_Y :Double;
        XY_Schritt_Z :Double; );
    1: (KR_SchrittWi   :Double;
        KR_CentX   :Integer;
        KR_CentY   :Integer;
        KR_Radius  :Integer;
        KR_StartWi :Integer;);
end;         

Var xy : XYRec;


Aber wenn ich den Record speichern möchte und die Länge mit L := sizeof(xy); ermitteln möchte, bekomme ich als Ergebnis 48, was natürlich falsch ist !

Alles nach XY_Schritte wird ignoriert !

Unter Delphi habe ich das sehr oft verwendet und es klappte immer problemlos. Auch wenn Jahren mit Lazarus auf dem Raspi das Problem bestanden hätte, währe mir das sicher aufgefallen.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Falsche Ergebnisse bei sizeof()

Beitrag von Michl »

aro hat geschrieben:
Sa 14. Aug 2021, 22:16
Aber wenn ich den Record speichern möchte und die Länge mit L := sizeof(xy); ermitteln möchte, bekomme ich als Ergebnis 48, was natürlich falsch ist !
Was meinst du damit?

6*4 = 24
3*8 = 24
24+24 = 48

Ist doch gut. Zumindest für einen 32bit Integer (Longint).

Siehe auch https://wiki.freepascal.org/Variables_and_Data_Types

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Falsche Ergebnisse bei sizeof()

Beitrag von siro »

Code: Alles auswählen

type  XYRec = record  // oder auch XYRec = packed record
  XY_ComNr :Integer;                                    // 4
  XY_F:Integer; //Vorschub                              // 4
  XY_X : Integer;//Absolutposition der X-Achse          // 4
  XY_Y : Integer;//Absolutposition der Y-Achse          // 4
  XY_Z : Integer;//Absolutposition der Z-Achse          // 4
  XY_Schritte :Integer;                                 // 4
  case Integer of
    0: (XY_Schritt_X :Double;        // 8
        XY_Schritt_Y :Double;        // 8
        XY_Schritt_Z :Double; );     // 8
    1: (KR_SchrittWi   :Double;           // 8
        KR_CentX   :Integer;              // 4
        KR_CentY   :Integer;              // 4
        KR_Radius  :Integer;              // 4
        KR_StartWi :Integer;);            // 4
end;       
Der Variantenrecord besteht aus 24 Bytes egal ob case 0 oder 1, diese Bytes liegen ja übereinander, also im gleichen Speicher.
entweder aus 3*8 Bytes = 24 oder aus 8+4*4 Bytes = 24
Somit komme ich auch auf insgesamt 48 und das ist korrekt.

Anmerkung:
evtl. möchtest Du das garnicht dass sich die Daten überschreiben ?
Wenn Du KR_SchrittWi setzt/veränderst, dann setzt/veränderst Du automatisch auch XY_Schritt_X auf den gleichen Wert,
da sie sich ja im gleichen Speicher befinden.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1423
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Falsche Ergebnisse bei sizeof()

Beitrag von fliegermichl »

48 ist die richtige Größe. Was man dabei aber auch beachten muß, ist wie records "gepackt" sind.

Code: Alles auswählen

{$packrecords default}
type
 tmyrec = record
  a : byte;
  b : word;
 end;

begin
 writeln(sizeOf(tmyrec));
end.
Hier wird 4 ausgegeben, da die Felder immer an geraden Adressen ausgerichtet werden. Hätte ich statt default 1 angegeben, so wäre die Ausgabe 3.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Falsche Ergebnisse bei sizeof()

Beitrag von Winni »

Hi!


Ich hab versucht für @aro einmal darzustellen, wie sich das im Speicher darstellt.
Man achte auf das ODER

Code: Alles auswählen

type
XYRec = record
  XY_ComNr :Integer;                                    // 4
  XY_F:Integer; //Vorschub                              // 4
  XY_X : Integer;//Absolutposition der X-Achse          // 4
  XY_Y : Integer;//Absolutposition der Y-Achse          // 4
  XY_Z : Integer;//Absolutposition der Z-Achse          // 4
  XY_Schritte :Integer;                                 // 4
  case Integer of                    [b]ODER[/b]
    0: (XY_Schritt_X :Double;   // 8   |1: (KR_SchrittWi   :Double;// 8
        XY_Schritt_Y :Double;   // 8   |    KR_CentX   :Integer;   // 4
                                       |    KR_CentY   :Integer;   // 4
        XY_Schritt_Z :Double; );// 8   |    KR_Radius  :Integer;   // 4
                                       |    KR_StartWi :Integer;); // 4
end;
Ansonsten nach varianten Records in Pascal suchen. Gibt es seit Ende der 70er. Dürfte also genug im Netz stehen.

Winni

aro
Beiträge: 130
Registriert: Di 26. Jul 2011, 19:58
OS, Lazarus, FPC: Deepin 20.2; Lazarus 2.0.0 + dfsg-2
CPU-Target: 64Bit

Re: Falsche Ergebnisse bei sizeof()

Beitrag von aro »

Hallo Michl,
Hallo siro,

Das ist doch Unsinn.

Als erstes belegen

Code: Alles auswählen

   XY_ComNr :Integer;
  XY_F:Integer; //Vorschub
  XY_X : Integer;//Absolutposition der X-Achse
  XY_Y : Integer;//Absolutposition der Y-Achse
  XY_Z : Integer;//Absolutposition der Z-Achse
  XY_Schritte :Integer; 
48 Bytes Speicher.

danach belegen

Code: Alles auswählen

0: (XY_Schritt_X :Double;
        XY_Schritt_Y :Double;
        XY_Schritt_Z :Double;
       tttt : Integer;        );
oder

Code: Alles auswählen

   1: (KR_SchrittWi   :Double;
        KR_CentX   :Integer;
        KR_CentY   :Integer;
        KR_Radius  :Integer;
        KR_StartWi :Integer;);  
gemeinsam weitere 48 Bytes an Speicher. Diese liegen übereinander und können deshalb auch nicht gleichzeitig benutzt werden.

aber XY_F:Integer; //Vorschub und XY_Schritt_X :Double; liegen nicht übereinander und können gleichzeitig und unabhängig von einander genutzt werden !

Außerdem funktioniert das ganze, wenn ich statt sizeof(xy) den richtigen Wert 96 einsetze. Dann kann ich den Record speichern und lesen. Alle Variablen haben dann genau den Wert den ich gespeichert habe!

Es gibt nur zwei Möglichkeiten:

1. sollte man schnellstens den Fehler beheben ( bessere Wahl) oder
2. Explizit und sehr deutlich darauf hinweisen, das sizeof() auf Records mit Case nicht benutzbar ist. ( ganz schlechte Notlösung )

Bei packed record kann man relativ einfach die Länge selbst berechnen.
Bei record kann es kompliziert werden. Nur der Kompiler weis, wie er die Daten ausrichtet und welche Zwischenräume er frei lässt.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Falsche Ergebnisse bei sizeof()

Beitrag von Winni »

aro hat geschrieben:
So 15. Aug 2021, 10:52

Das ist doch Unsinn.

Als erstes belegen

Code: Alles auswählen

   XY_ComNr :Integer;
  XY_F:Integer; //Vorschub
  XY_X : Integer;//Absolutposition der X-Achse
  XY_Y : Integer;//Absolutposition der Y-Achse
  XY_Z : Integer;//Absolutposition der Z-Achse
  XY_Schritte :Integer; 
48 Bytes Speicher.

6 mal 4 ist 24.
Nicht 48.

Zurück ins 2. Schuljahr.

Winni

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Falsche Ergebnisse bei sizeof()

Beitrag von Winni »

aro hat geschrieben:
So 15. Aug 2021, 10:52

Bei packed record kann man relativ einfach die Länge selbst berechnen.
Bei record kann es kompliziert werden. Nur der Kompiler weis, wie er die Daten ausrichtet und welche Zwischenräume er frei lässt.

Wenn man sich die Mühe macht, etwas Grundlagenwissen anzueignen, so ist im fpc Manual (prog) zu finden:

----------------------------------
3 Data alignment

1 Typed constants and variable alignment

All static data (variables and typed constants) which are greater than a byte are usually aligned on a power of two boundary. This alignment applies only to the start address of the variables, and not the alignment of fields within structures or objects for example. For more information on structured alignment, section StructuredAlignment. The alignment is similar across the different target processors. [*]


Table: Data alignment
Data size (bytes) Alignment (small size) Alignment (fast)
1 1 1
2-3 2 2
4-7 2 4
8+ 2 4

The alignment columns indicates the address alignment of the variable, i.e the start address of the variable will be aligned on that boundary. The small size alignment is valid when the code generated should be optimized for size and not speed, otherwise and by default, the fast alignment is used to align the data.


2 Structured types alignment

By default all elements in a structure are aligned to a 2 byte boundary, unless the $PACKRECORDS directive or packed modifier is used to align the data in another way. For example a record or object having a 1 byte element, will have its size rounded up to 2, so the size of the structure will actually be 2 bytes.

-----------------------------

Grüße,
Winni

PascalDragon
Beiträge: 823
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Falsche Ergebnisse bei sizeof()

Beitrag von PascalDragon »

aro hat geschrieben:
So 15. Aug 2021, 10:52
Als erstes belegen

Code: Alles auswählen

   XY_ComNr :Integer;
  XY_F:Integer; //Vorschub
  XY_X : Integer;//Absolutposition der X-Achse
  XY_Y : Integer;//Absolutposition der Y-Achse
  XY_Z : Integer;//Absolutposition der Z-Achse
  XY_Schritte :Integer; 
48 Bytes Speicher.
SizeOf(Integer) ist je nach Mode entweder 2 (fpc, tp) oder 4 (objfpc, delphi), aber niemals 8.
FPC Compiler Entwickler

wp_xyz
Beiträge: 4864
Registriert: Fr 8. Apr 2011, 09:01

Re: Falsche Ergebnisse bei sizeof()

Beitrag von wp_xyz »

Man kann auch ein Experiment machen:

Ich deklariere einen etwas vereinfachten varianten Record als

Code: Alles auswählen

type
  TVarRec = record
    case Integer of
      0: (i1, i2: Integer);
      1: (d: Double);
  end;  
und eine Variable v von diesem Typ.

Ich beschreibe das Double-Feld v.d mit dem Wert 1.234567 (es kann aber auch eine andere Zahl sein). Lasse ich mir v.d per WriteLn ausgeben erhalte ich genau den eingetragenen Wert.

Nun schreibe ich die Zahl -1 in die Felder v.i1 und v.i2, das heißt alle Bits in diesen beiden Integern werden gesetzt. Ich lasse mir wieder den double-Wert v.d des varianten Records ausgeben, obwohl die Schreibaktion die Integer-Teile betroffen hat.

Wenn, wie du behauptest, die beiden Cases des varianten Records im Speicher getrennt sind, dürfte sich der Inhalt von v.d1 nicht ändern. Wenn die beiden Cases im Speicher übereinanderliegen, müssten auch alle Bits im Feld v.d gesetzt werden und die Ausgabe etwas anderes als 1.234567 anzeigen.

Probier's aus mit diesem Code:

Code: Alles auswählen

program Project1;
type
  TVarRec = record
    case Integer of
      0: (i1, i2: Integer);
      1: (d: Double);
  end;
var
  v: TVarRec;
begin
  v.i1 := 0;
  v.i2 := 0;
  v.d := 1.234567;
  WriteLn('Ausgangswerte:');
  WriteLn('  v.i1 = ', v.i1);
  WriteLn('  v.i2 = ', v.i2);
  WriteLn('  v.d = ', v.d:0:6);
  WriteLn;
  WriteLn('Setze v.i1 und v.i2 jeweils auf -1');
  v.i1 := -1;
  v.i2 := -1;
  WriteLn('  v.i1 = ', v.i1);
  WriteLn('  v.i2 = ', v.i2);
  WriteLn('  v.d = ', v.d:0:6);
  WriteLn('Inhalt von v.d. als Integer bewertet:');
  WriteLn('  int64(v.d) =', Int64(v.d));
  ReadLn;
end.

aro
Beiträge: 130
Registriert: Di 26. Jul 2011, 19:58
OS, Lazarus, FPC: Deepin 20.2; Lazarus 2.0.0 + dfsg-2
CPU-Target: 64Bit

Re: Falsche Ergebnisse bei sizeof()

Beitrag von aro »

Sorry,

Der Fehler lag nicht bei sizeof() sondern bei mir. Einfach nur falsch nachgezählt.

Keine Ahnung, wie ich so einen Fehler machen konnte. Eigentlich halte ich mich von Drogen und Sekten fern.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Falsche Ergebnisse bei sizeof()

Beitrag von Winni »

aro hat geschrieben:
So 15. Aug 2021, 20:01
Eigentlich halte ich mich von Drogen .... fern.
Vieleicht ist das das Problem.

Winni

Antworten