Variablen überlagern

Für Fragen von Einsteigern und Programmieranfängern...
Antonia01
Beiträge: 2
Registriert: Fr 19. Aug 2022, 07:47

Variablen überlagern

Beitrag von Antonia01 »

Hallo Forum,

ich bin die ganze Zeit am grübeln, wie folgendes in Lazarus umgesetzt werden kann:

Ich habe eine Variable Test :Array[2] of char;
Und möchte die Variable Test1:Byte auf die Adresse von Test legen.

Var
Test :Array[2];
Test1[@Test]:Byte;

So in der Art.

Kann ich das irgendwie bewerkstelligen?

Danke an Alle!

Thandor
Beiträge: 144
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 2.2.0 mit FPC 3.2.2 (32Bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Variablen überlagern

Beitrag von Thandor »

Hallo,

dafür brauchts du eine Pointer-Variable, in der du dann die Adresse des Array-Feldes speichern kannst. Wenn du dann auf den Wert dieses Feldes über die Pointervariable zugreifen möchtest, muss du dem Compiler mitteilen, dass du den Wert an der Adress meinst (derefernzieren). Das machst du, indem du das ^-Zeichen hinter der Variablennamen anhängst.

Code: Alles auswählen

var Test :Array[0..2] of char;
       pTest1 : ^Char;
       
 ...
 
 pTest1 := @Test[0];
 pTest1^ := 'H';
 WriteLn(pTest1^);

Antonia01
Beiträge: 2
Registriert: Fr 19. Aug 2022, 07:47

Re: Variablen überlagern

Beitrag von Antonia01 »

Hallo Thandor,

vielen Dank!

Ich dachte, man kann das schon während der Deklaration dem Compiler mitteilen.

Viele Grüße

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5235
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Variablen überlagern

Beitrag von af0815 »

Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 573
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: Variablen überlagern

Beitrag von PascalDragon »

Antonia01 hat geschrieben:
Fr 19. Aug 2022, 07:57
Kann ich das irgendwie bewerkstelligen?
Wie af0815 angedeutet hat, das Zauberwort ist absolute:

Code: Alles auswählen

program tabs;

var
  test: array[0..1] of Byte;
  test1: Byte absolute test[0];
begin
  test[0] := 42;
  test[1] := 21;
  Writeln(test1);
end.
FPC Compiler Entwickler

Thandor
Beiträge: 144
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 2.2.0 mit FPC 3.2.2 (32Bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Variablen überlagern

Beitrag von Thandor »

Das Schlüsselwort absolute kannte ich bisher noch nicht. Gut ich habe diese Mechanik noch nie gebraucht, also habe ich noch nie nach diesr Möglichkeit gesucht.

Aber wo ist sowas eigentlich sinvoll? Macht das am Ende den Code nicht erhr unleserlich? Da wird doch dann der eigentliche Typ (hier ein Array) verschleiert?

Warf
Beiträge: 1679
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Variablen überlagern

Beitrag von Warf »

Thandor hat geschrieben:
Fr 19. Aug 2022, 11:10
Aber wo ist sowas eigentlich sinvoll? Macht das am Ende den Code nicht erhr unleserlich? Da wird doch dann der eigentliche Typ (hier ein Array) verschleiert?
Nein ist es nicht, vor allem weil es compilerchecks ausschaltet:

Code: Alles auswählen

var
  b: Byte;
  i: Integer absolute b;
begin
  i := $0FFFFFFF;
end.  
Das ist ein Fehler und der Compiler schluckt es ohne probleme. Mit typedpointern wirft der compiler da einen fehler:

Code: Alles auswählen

{$T+}
var
  b: Byte;
  i: PInteger = @b; // Incompatible types: got "^Byte" expected "PInteger"
begin
  i^ := $0FFFFFFF;
end. 
Man sollte immer versuchen seinen code so zu schreiben das der compiler direkt merkt wenn man was falsch macht. Jeder Fehler den der Compiler fängt ist ein fehler um den du dich nicht kümmern musst.

Das heist nicht das man keinen Fehleranfälligen code mehr schreiben können sollte, also z.B. wenn man unbedingt auf einen byte typen als integer zugreifen will dann gibt es da möglichkeiten. Aber wenn man was spezielles macht, sollte das auch speziell gekennzeichnet sein. Wenn man absolute benutzt sieht jeder zugrif auf die variable so aus als würde man eine normale variable verändern, aber eigentlich ist es ein pointer. Bei einem "echten" pointer, hat man die pointer syntax die ganz klar kennzeichnet das es ein pointer ist:

Code: Alles auswählen

var
  b: Byte;
  p: PByte = @b;
  a: Byte absolute b;
begin
  p^ := 42; // ^ zeigt ganz klar das hier ein wert geschrieben wird der woanders liegt
  a := 42; // wenn man nicht weis das a absolute ist lässt es das so aussehen als wäre a eine eigene variable
Manchmal will man aber auf den selben speicher unterschiedlich zugreifen, hier gibt es 2 explizite möglichkeiten, 1. casts:

Code: Alles auswählen

arr: Array[0..3] of Byte;
begin
  PInteger(@arr[0])^ := 42;
Hier wird durch den cast explizit ausgesagt: ich weiß das das hier kein integer ist, aber ich wills als integer behandeln. So etwas explizit aufzuschreiben ist eine form der "Ich weis was ich tue" abfrage.

Mein Favorit ist aber eigentlich die verwendung von Varianten Records:

Code: Alles auswählen

var
  data: record
  case Boolean of
  True: (asArray[0..3] of Byte);
  False: (asInteger: Integer);
  end;
begin
  data.asInteger := 42;
  WriteLn(data.asArray[0]);
Das ist m.E. die mit Abstand beste Lösung denn
1. es ist sicher, der variante record ist immer so groß wie das größte case, also selbst wenn asArray 0..2 wäre, also kleiner als ein integer, würde der variante record immer mindestens die größe des Integers belegen, und man schreibt niemals über in fremden speicher, sowohl mit pointern als auch mit absolute kann man über die speicher boundaries schreiben, mit varianten records nicht.
2. es ist explizit, data.asInteger und data.asArray zeigen durch das "data" das sie zum selben speicherobjekt gehören, und durch explizite namensgebung "asXXX" ist auch klar das das eine reinterpretation der daten ist.
3. die deklarationen sind am selben Ort. wenn du jetzt von 32 bit auf 63 bit upgradest und daher aus i: Integer ein asInteger: Int64 machst, bist du direkt an dem ort an dem auch asArray deklariert ist und siehst direkt: Hey ich muss das auch noch ändern. Bei absolute oder pointern können hunderte zeilen code zwischen den beiden punkten liegen, sodass du, weil du vielleicht neu an dem projekt bist, nicht einmal weist das diese aboslute referenz existiert

Daher faustregel: Benutz absolute niemals, pointer für referenzen auf den selben datentypen und für zugriff mittels verschiedenen datentypen veriante records. Es gibt Fälle da sind Pointer einfacher (stichwort Pointerarithmethik), aber variante records sind eigentlich fast immer zu bevorzugen

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1118
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Variablen überlagern

Beitrag von fliegermichl »

Ich verwende absolute ziemlich oft.
Normalerweise wenn ich auf einzelne Bytes einer größeren Variable zugreifen will.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1118
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Variablen überlagern

Beitrag von fliegermichl »

Oder wenn ich auf spezielle Properties von Klassen zugreifen will, die von der gleichen Basisklasse abgeleitet sind.

Code: Alles auswählen

type
 TBase = class;
 
 TPunkt = class ( TBase )
 end;
 
 TFlaeche = class ( TBase )
 end;
 
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);  
var b : TBase;
      p : TPunkt absolute b;
      f : TFlaeche absolute b;
begin
  b := FangeObjekt(x, y);
  if (b is TPunkt) then
    p.xxx
   else if (b is TFlaeche) then
    f.xxx;
end;

Benutzeravatar
theo
Beiträge: 9265
Registriert: Mo 11. Sep 2006, 19:01

Re: Variablen überlagern

Beitrag von theo »

fliegermichl hat geschrieben:
Fr 19. Aug 2022, 13:14
Oder wenn ich auf spezielle Properties von Klassen zugreifen will, die von der gleichen Basisklasse abgeleitet sind.
Kannst du mir erklären, wozu das gut ist?
Warum nicht einfach:

Code: Alles auswählen

if b is TPunkt then (b as TPunkt).xxx;

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

Re: Variablen überlagern

Beitrag von siro »

Thandor hat geschrieben:
Fr 19. Aug 2022, 11:10
Das Schlüsselwort absolute kannte ich bisher noch nicht. Gut ich habe diese Mechanik noch nie gebraucht, also habe ich noch nie nach diesr Möglichkeit gesucht.

Aber wo ist sowas eigentlich sinvoll? Macht das am Ende den Code nicht erhr unleserlich? Da wird doch dann der eigentliche Typ (hier ein Array) verschleiert?
Absolute ist eigentlich unumgänglich für Embedded Anwendungen.
So werden die Register des Controllers definiert, die ja immer an einer festen Adresse liegen.
Beispielsweise so:

Code: Alles auswählen

const SYSTICK_BASE_ADDRESS = $E000E010;   // Basisadresse beim STM32Fxxx

var STK_CTRL  : DWORD absolute SYSTICK_BASE_ADDRESS + 0;    // Steuerung interupt Clock select usw.
var STK_LOAD  : DWORD absolute SYSTICK_BASE_ADDRESS + 4;    // Zaehlerwert welcher beim Erreichen von 0 wieder geladen werden soll
var STK_VAL   : DWORD absolute SYSTICK_BASE_ADDRESS + 8;    // aktuelle Zählerstand des 24 Bit Zählers

begin
  STK_CTRL := 7;    // counter enable, enable interrupt
  .....
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1118
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Variablen überlagern

Beitrag von fliegermichl »

theo hat geschrieben:
Fr 19. Aug 2022, 13:27
fliegermichl hat geschrieben:
Fr 19. Aug 2022, 13:14
Oder wenn ich auf spezielle Properties von Klassen zugreifen will, die von der gleichen Basisklasse abgeleitet sind.
Kannst du mir erklären, wozu das gut ist?
Warum nicht einfach:

Code: Alles auswählen

if b is TPunkt then (b as TPunkt).xxx;
Aus Gründen der Faulheit. Meistens muss man mehr als nur eine Operation auf der Variablen ausfuehren.

Thandor
Beiträge: 144
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 2.2.0 mit FPC 3.2.2 (32Bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Variablen überlagern

Beitrag von Thandor »

Danke für eure Antworten.
Ja, dass es ein paar Fälle gibt, die das "absolute" benötigen habe ich mir schon gedacht. Aber der Anwendungsfall, wie hier am Threadanfang eingeführt sollte mann meiden.

Warf
Beiträge: 1679
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Variablen überlagern

Beitrag von Warf »

fliegermichl hat geschrieben:
Fr 19. Aug 2022, 13:14
Oder wenn ich auf spezielle Properties von Klassen zugreifen will, die von der gleichen Basisklasse abgeleitet sind.
Auch hier ist absolute eine schlechte idee, da es compilerchecks ausschaltet:

Code: Alles auswählen

  TBase = class
  public
    Foo: Integer;
  end;

  TChild1 = class(TBase);

  TOther = class(TObject);

var
  o: TOther;
  b: TBase;
  c: TChild1 absolute b;
  wrong: TChild1 absolute o; // Fehler aber keine fehlermeldung
Aber mit typecasts:

Code: Alles auswählen

var
  o: TOther;
begin
  TChild1(o).Foo := 42; // Warning: Class types "TOther" and "TChild1" are not related
  (o as TChild1).Foo := 42; // Error: Class or Object types "TOther" and "TChild1" are not related
Du nimmst dir damit aktiv die möglichkeit fehler frühzeitig zu finden. Casts sind besser als absolute da Casts ein paar Compilerchecks macht und wenigstens eine Warning wirft, aber AS ist der absolute goldstandard hier, das macht strikte compiler checks mit Fehlermeldung, und macht auch noch zur runtime checks:

Code: Alles auswählen

  TBase = class
  public
    Foo: Integer;
  end;

  TChild1 = class(TBase);
  TChild2 = class(TBase);

var
  b: TBase;
begin
  b := TChild2.Create;
  (b as TChild1).Foo := 42; // Runtime Error
end.
Der einzige grund nicht as zu verwenden ist wenn man entweder performance kritischen code schreibt und da die zusätzlichen runtime checks zu viel overhead wären, oder man vorher ein "is" gemacht hat und daher sicher sein kann das es sich um den entsprechenden typen handelt (z.b. wenn man mehrfach casten will ohne den check overhead mehrfach zu haben)

PascalDragon
Beiträge: 573
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: Variablen überlagern

Beitrag von PascalDragon »

theo hat geschrieben:
Fr 19. Aug 2022, 13:27
fliegermichl hat geschrieben:
Fr 19. Aug 2022, 13:14
Oder wenn ich auf spezielle Properties von Klassen zugreifen will, die von der gleichen Basisklasse abgeleitet sind.
Kannst du mir erklären, wozu das gut ist?
Warum nicht einfach:

Code: Alles auswählen

if b is TPunkt then (b as TPunkt).xxx;
Der as-Operator ist vergleichsweise teuer (genauso wie der is-Operator) und den Hauptteil davon hast du ja schon mit is gemacht, warum also nochmal? Also entweder 'nen harten Cast oder absolute.
FPC Compiler Entwickler

Antworten