Verschiedene Variablen auf gleiche Speicherstelle

Rund um die LCL und andere Komponenten
Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Mathias »

Wieso probierst du die erste Varianta nicht aus.
Ansonsten ein verschachtelten Record mit einem Dummy.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Timm Thaler hat geschrieben:Bösartige Frage: Ist es möglich, den Record so zu modifizieren, dass ich drei Bytewerte habe, aber nur aus 2en ein Word mache?

hh, mm, ss : uint8 // so sieht meine Uhrzeit aus, die ich von der RTC bekomme
hhmm // das ist mein Zeitwert, auf den ich vergleiche

Ich würde also gern "time" so auf hh,mm,ss mappen, dass im Highbyte hh, im Lowbyte mm stehen und ss nicht beachtet wird.

Code: Alles auswählen

type
  TTimeRec = packed record
    ss: uint8;
    case integer of
      0: (mm, hh: uint8);
      1: (time: utin16);
  end

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Mathias »

@wp_xyz

Geniale Lösung. :shock:
Sowas bringt man in C++ nicht mal mit union hin.

Aber eins will ich doch mal wissen, für was ist der Bezeichner hinter case gut ?

Sowas wird auch kompiliert:

Code: Alles auswählen

  TTimeRec = packed record
    ss: uint8;
    case char of
      0: (mm, hh: uint8);
      1: (time: uint16);
  end;
  TTimeRec = packed record
    ss: uint8;
    case char of
      '0': (mm, hh: uint8);
      '1': (time: uint16);
  end;
 

Oder gar so etwas komisches:

Code: Alles auswählen

  TTimeRec = packed record
    ss: uint8;
    case Boolean of
      '0': (mm, hh: uint8);
      '=': (time: uint16);
      'x': (t: uint16);
  end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Socke »

Die Typangabe ist immer dann sinnvoll, wenn du auch ein Feld dazu anlegst:

Code: Alles auswählen

  TTimeRec = packed record
    ss: uint8;
    case myvar: Boolean of
      '0': (mm, hh: uint8);
      '=': (time: uint16);
      'x': (t: uint16);
  end;

Die Werte haben dann aber immer noch nur dokumentarische Funktion. Gültig sind alle ordinale Typen (Char, Ganzzahl, Boolean); auch der Wertebereich wird nicht geprüft.

Der Inhalt des Feldes myvar muss dann aber immer noch durch den Entwickler gesetzt und geprüft werden.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Der im case angegebene Bezeichner ist Bestandteil des varianten Records. Je nach dessen Wert werden die Elemente des case-Teils entsprechend interpretiert, wobei der eigentliche Wert aber nicht geprüft wird.

Und daher ist mein Vorschlag auch Käse, weil sich die Byte-Folge nicht mit der Eingangssequenz überdeckt (ss mm hh). Richtig wöre, die Sekunden als Unterscheidungselement zu nehmen:

Code: Alles auswählen

type
  TTimeRec = packed record
    case ss:byte of
      0: (mm: byte; hh: byte);
      1: (time: word);
  end;   

Hier eine kleine Demo zur Verdeutlichung:

Code: Alles auswählen

program Project1;
 
{$mode objfpc}{$H+}
 
const
  s = '012345';
 
type
  TTimeRec = packed record
    case ss:byte of
      0: (mm: byte; hh: byte);
      1: (time: word);
  end;   
{
  TTimeRec = packed record
    ss: byte;
    case x:byte of
      0: (mm: byte; hh: byte);
      1: (time:word);
  end;
}

  PTimeRec = ^TTimeRec;
 
var
  t: TTimeRec;
  i: Integer;
begin
  t := PTimeRec(@s[1])^;
 
  for i := 1 to Length(s) do
    Write(ord(s[i]), ' ');
  WriteLn;
  WriteLn;
  WriteLn('      ss = ', t.ss);
//  WriteLn('       x = ', t.x);
  WriteLn('      mm = ', t.mm);
  Writeln('      hh = ', t.hh);
  WriteLn('Lo(time) = ', Lo(t.time));
  WriteLn('Hi(time) = ', Hi(t.time));
 
  WriteLn('SizeOf(TTimeRec) = ', SizeOf(TTimeRec));
 
  ReadLn;
end.

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Mathias »

Und daher ist mein Vorschlag auch Käse, weil sich die Byte-Folge nicht mit der Eingangssequenz überdeckt (ss mm hh). Richtig wöre, die Sekunden als Unterscheidungselement zu nehmen:

Und was machst du, wen noch ms dazu kommt ?

Code: Alles auswählen

TTimeRec2 = packed record
  ms, ss: byte;
  case byte of
    0: (mm: byte; hh: byte);
    1: (time:word);
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Also, zunächst ist für 999 ms ein Byte zu klein. Nehmen wir ein Word. Dann wäre die Byteabfolge von low nach high: "xxxxssmmhh"(x = ms). Wenn dann ein Record darüber gelegt werden soll, der einmal die beiden Bytes mit "mm" und "hh" getrennt und einmal gemeinsam zugänglich macht, dann müsste dieser so deklariert werden:

Code: Alles auswählen

type
  TTimeRec = packed record
    ms: word;             // xxxx
    case ss: byte of      // ss
      0: (mm: byte;       // mm
          hh: byte);      // hh
      1: (time: word);    // mmhh
  end;

Eine Bemerkung aber noch: Welchen Sinn soll es haben, im Record-Element Minuten und Stunden als gemeinsame 16-bit Variable herauszuholen? Mit der Zahl kann man nichts machen, höchstens auf Gleichheit prüfen. Die ganze Übung erscheint mir relativ - hmmm - "wertfrei".

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Mathias »

Also, zunächst ist für 999 ms ein Byte zu klein
Das stimmt, es könnte auch etwas anderes als Zeit-Einheiten sein.
Dann sieht diese Deklaration doch übersichtlicher aus:

Code: Alles auswählen

type
  TTimeRec = packed record
    ms: word;             // xxxx
    ss: Byte;             // ss
    case byte of
      0: (mm: byte;       // mm
          hh: byte);      // hh
      1: (time: word);    // mmhh
  end;

Eine Bemerkung aber noch: Welchen Sinn soll es haben, im Record-Element Minuten und Stunden als gemeinsame 16-bit Variable herauszuholen?

Egal um Zeit oder sonst was, es ging mir nur drum, wie man so etwas deklariert,
Aber irgendwie ist das mit case ordinal doch etwas merkwürdig gelöst. 8)
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Timm Thaler »

wp_xyz hat geschrieben:Welchen Sinn soll es haben, im Record-Element Minuten und Stunden als gemeinsame 16-bit Variable herauszuholen? Mit der Zahl kann man nichts machen, höchstens auf Gleichheit prüfen. Die ganze Übung erscheint mir relativ - hmmm - "wertfrei".


Genau DAS ist der Sinn. Ich habe ein Array von hh:mm Werten, gespeichert als Packed BCD. Das durchsuche ich auf die aktuelle Zeit, um Ereignisse auszulösen.

Warum Packed BCD? Weil die RTC (DS1307) das so liefert. Weil man das prima in Ziffern wandeln kann (nur shift, and und add). Weil man prima damit rechnen kann, ohne mit mul und div rummachen zu müssen. Weil man prima darauf vergleichen kann (die ungenutzten Zwischenwerte stören dabei nicht). Klar könnte ich die Zeit von der RTC erstmal in Sekunden wandeln (3600 * hh + 60 * mm + ss). Wenn ich dann daraus die hh und mm brauche, muss ich durch 60 teilen. Da ein Tag nicht in 16 Bit passt, brauche ich 32bit-Operationen.

Wenn ich auf hh und mm einzeln habe, ist ein größer / kleiner Vergleich schwierig, weil er mit Übertrag erfolgen muss. Also muss ich entweder die Zeit zusammenrechnen - oder das halt direkt in ein Word packen. Der Vergleich ist dann ein cp und cpc auf dem AVR.

Die Verwendung von Packed BCD als Zeit scheint erstmal antiquiert, aber auf einem Mikrocontroller ist das tatsächlich praktisch.

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Mathias hat geschrieben:

Code: Alles auswählen

type
  TTimeRec = packed record
    ms: word;             // xxxx
    ss: Byte;             // ss
    case byte of
      0: (mm: byte;       // mm
          hh: byte);      // hh
      1: (time: word);    // mmhh


Nur zur Sicherstellung: Das ist nicht richtig, das Feld ss muss weg, weil auch das unter "case" erwähnte Byte ebenfalls im Stream auftaucht. Dein Record ist 2 (ms) + 1 (ss) + 1 (case byte) + 2 (mmhh) = 6 Bytes lang, meiner nur 5. Wiegesagt, die nach "case" genannten Werte 0 oder 1 sind ohne Bedeutung, es ist nur wichtig, dass sie unterschiedlich sind.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Socke »

wp_xyz hat geschrieben:Nur zur Sicherstellung: Das ist nicht richtig, das Feld ss muss weg, weil auch das unter "case" erwähnte Byte ebenfalls im Stream auftaucht. Dein Record ist 2 (ms) + 1 (ss) + 1 (case byte) + 2 (mmhh) = 6 Bytes lang, meiner nur 5. Wiegesagt, die nach "case" genannten Werte 0 oder 1 sind ohne Bedeutung, es ist nur wichtig, dass sie unterschiedlich sind.

Dann hab ich hier einen Compilerfehler (3.1.1, rev. 37316)?

Code: Alles auswählen

program Project1;
 
type
  TTimeRec1 = packed record
    ms: word;             // xxxx
    ss: byte;             // ss
    case byte of
      0: (mm: byte;       // mm
          hh: byte);      // hh
      1: (time: word);    // mmhh
  end;
 
  TTimeRec2 = packed record
    ms: word;             // xxxx
    case ss: byte of      // ss
      0: (mm: byte;       // mm
          hh: byte);      // hh
      1: (time: word);    // mmhh
  end;
 
begin
  WriteLn('TTimeRec1', ' ', SizeOf(TTimeRec1));
  WriteLn('TTimeRec2', ' ', SizeOf(TTimeRec2));
end.

Ausgabe:

Code: Alles auswählen

TTimeRec1 5
TTimeRec2 5


Die Dokumentation kann man nach deinem Verständnis lesen: das Feld ist immer da, aber es gibt keinen Zugriff darauf, wenn man keinen Namen angibt.
FPC Language Reference Guide, Records hat geschrieben:The optional identifier in the case statement serves to access the tag field value, which otherwise would be invisible to the programmer. It can be used to see which variant is active at a certain time. In effect, it introduces a new field in the record.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Ah! Mit fpc 3.04 sehe ich das auch, und auch mit Delphi.

Das wusste ich nicht - offenbar wird das case-Byte nur mitgezählt, wenn es benannt ist. Dadurch wird das ganze wieder viel lesbarer.

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Mathias »

Das wusste ich nicht - offenbar wird das case-Byte nur mitgezählt, wenn es benannt ist. Dadurch wird das ganze wieder viel lesbarer.

Alles andere würde auch keine Sinn machen. Das mit dem case ist nach meiner Meinung etwas unglücklich in Pascal gelöst. Vor allem der Bezeichner zwischen case und of.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von Timm Thaler »

Also erstens: Funktioniert auf dem AVR hervorragend und mit optimalem Code.

Und noch eine kleine Frage: Kann ich auch nach dem Case noch weitere Variablen in den Record einfügen? Wenn ich einfach welche dazuschreibe, meckert der Compiler, weil er den Case fortsetzen will. Wenn ich den Case mit End beende, wird der Record geschlossen.

Nicht dass ich das jetzt bräuchte, aber es würde mich um des Prinzips willen interessieren.

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

Re: Verschiedene Variablen auf gleiche Speicherstelle

Beitrag von wp_xyz »

Timm Thaler hat geschrieben:Und noch eine kleine Frage: Kann ich auch nach dem Case noch weitere Variablen in den Record einfügen?

Ja, aber die müssen VOR dem CASE stehen, oder anders herum ausgedrückt: CASE muss das letzte Element des Record sein.
https://de.wikibooks.org/wiki/Programmi ... in_Records

Antworten