Array Murks

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Antworten
Mathias
Beiträge: 6210
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Array Murks

Beitrag von Mathias »

Ich habe mal ganz etwas verrücktes probiert und wollte mal wissen, wie der Compiler darauf reagiert.

Code: Alles auswählen

{$modeswitch typehelpers on}
{$modeswitch arrayoperators on}
//{$modeswitch multihelpers}
uses
  SysUtils;

type
  TBytes = array of byte;
  TWords = array of word;

var
  bytes: TBytes = nil;
  words: TWords = nil;
  i: integer;

begin
  //  words := [1, 2, 3, 4, 5, 6, 7, 8];

  WriteLn(Length(words));

  TBytes(words) += [$22];
  TBytes(words) += [$33];
  TBytes(words) += [$44];
  TBytes(words) += [$55];
  WriteLn(Length(words));    // -> 4
  WriteLn(Length(TBytes(words)));   // -4 
  WriteLn();

  for i := 0 to Length(words) - 1 do begin
    WriteLn(words[i], '    ', words[i].ToHexString);
  end;
Ausgabe:

Code: Alles auswählen

0
4
4

13090    3322
21828    5544
30062    756E
120    0078
Was mich verwunder, das die überhaupt compilierbar ohne irgendwelche Warnungen ist.
So wie es scheint addiert der Compiler einfach die Array-Länge, ohne das er prüft ob dies zulässig ist.
Bei der Ausgabe findet man die addierten Bytes im Word.
Ist dies so gewollt, oder wurde sowas merkwürdiges gar noch nie probiert ?
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: Array Murks

Beitrag von Socke »

Mathias hat geschrieben:
Do 23. Nov 2023, 16:36
So wie es scheint addiert der Compiler einfach die Array-Länge, ohne das er prüft ob dies zulässig ist.
Bei der Ausgabe findet man die addierten Bytes im Word.
Ist dies so gewollt, oder wurde sowas merkwürdiges gar noch nie probiert ?
Der Compilter ändert gar nicht die Länge - zumindest nicht bezogen auf die Anzahl der Elemente.
Vor den Array-Elementen wird ein Header abgelegt, der für jeden Array-Typen identisch ist. Darin ist der Referenzzähler und die Anzahl der Elemente abgelegt (siehe FPC Datei <fpcsrc>/rtl/inc/dynarr.inc, Record tdynarray).

Jetzt passiert folgendes:
Du erstellst einen Array vom Typen TBytes. Mit vier Elementen. Der Compiler geht dabei von 1 Byte pro Array-Element aus und reserviert dazu entsprechend den Header (tdynarray) und 4 weitere Bytes.
Wenn du auf den Array als TWords zugreifst, ändert sich nur der Zugriff, nicht aber der Inhalt des Speichers. Der Array ist immer noch 4 Elemente groß. Du weißt den Compiler aber an, diese als Words (2 Bytes) also insgesamt 8 Bytes auszulegen.
In der Ausgabe werden daher zunächst die von dir geschriebenen 4 Bytes ($3322 und $5544). Die nächsten 4 Bytes stehen einfach nach deinen Array-Elementen im Speicher. Beir mir wird z.B. $F00D $BAAD ausgegeben - wodurch klar sein sollte, dass ich hier auf nicht einen initialisierten Speicherbereich zugreife.

Solche Zugriff als andere Datentypen nutzt man insbesondere, wenn es unterschiedliche Darstellungsformen der Daten gibt. Dazu nutzt man aber in der Regel Variant Records. Bei dynamischen Arrays musst du sehr stark aufpassen, dass du in den gültigen Speicherbereichen bleibst.
Im konkreten Fall, liest du über das Array-Ende (4 x 1 Byte) hinaus. Das kann zu unerwartetem Verhalten führen. Einen Absturz (Segmentation Fault), würde ich in der Regel nicht erwarten, da am Ende eines dynamischen Arrays üblicherweise noch ein wenig (noch) nicht verwendeter Speicher kommt, der für Vergrößerungen des Arrays genutzt werden kann.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Array Murks

Beitrag von Mathias »

Solche Zugriff als andere Datentypen nutzt man insbesondere, wenn es unterschiedliche Darstellungsformen der Daten gibt. Dazu nutzt man aber in der Regel Variant Records. Bei dynamischen Arrays musst du sehr stark aufpassen, dass du in den gültigen Speicherbereichen bleibst.
Im konkreten Fall, liest du über das Array-Ende (4 x 1 Byte) hinaus. Das kann zu unerwartetem Verhalten führen. Einen Absturz (Segmentation Fault), würde ich in der Regel nicht erwarten, da am Ende eines dynamischen Arrays üblicherweise noch ein wenig (noch) nicht verwendeter Speicher kommt, der für Vergrößerungen des Arrays genutzt werden kann.
Einfach gesagt, man darf solches Zeugs machen, man muss aber genau wissen was man tut ?

glBufferData(..) ist da ein gutes Beispiele, da kann man ziemlich alles mit geben. Von einer einfach Byte-Array bis zum komplexen Gemisch aus Array und Records.
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: Array Murks

Beitrag von Socke »

Mathias hat geschrieben:
Fr 24. Nov 2023, 13:47
Einfach gesagt, man darf solches Zeugs machen, man muss aber genau wissen was man tut ?
Man kann so etwas machen. Dort ist man aber sehr fehleranfällig, sodass man es besser vermeidet oder nur verwendet, wenn es wirklich keine andere Möglichkeit gibt.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Array Murks

Beitrag von Mathias »

Zu diesem Thema habe ich gerade was entdeckt.
Ich habe folgendes ausprobiert.
Hat es alles kleiner werte als 256 in der Array, wird eine Byte Array übergeben, sobald aber was grösseres drin steht, werden Words genommen.
Erzwingt man es mit einer Typenumwandlung, kommt es richtig. Auch wen man es nur bei einem wert macht,

Bei dem Versuch mit Singles kommt nur "0.00" raus, ausser man erzwingt alle Werte in der array.

Hinter dem ganzen sehe ich keinen Sinn, ausser das es Fehleranfällig ist.

Code: Alles auswählen

program project1;

type
  TWordArray = array [0..15] of Word;
  PWordArray = ^TWordArray;

  TSingleArray = array [0..15] of Single;
  PSingleArray = ^TSingleArray;

procedure WordTest(a: PWordArray);
var
  i: integer;
begin
  for i := 0 to 7 do begin
    Write(a^[i],' ');
  end;
  WriteLn();
end;

procedure SingleTest(a: PSingleArray);
var
  i: integer;
begin
  for i := 0 to 7 do begin
    Write(a^[i]:4:2,' ');
  end;
  WriteLn();
end;

begin
  WordTest(@[0, 1, 2, 3, 4, 5, 6, 7]);    // Es werden Byte genommen
  WordTest(@[0, 1000, 2, 3, 4, 5, 6, 7]); // Es wird in Word umgewandelt
  WordTest(@[word(0), word(1), word(2), word(3), word(4), word(5), word(6), word(7)]); // io,
  WordTest(@[word(0), 1, 2, 3, 4, 5, 6, 7]); // io.

  SingleTest(@[0, 1, 2, 3, 4, 5, 6, 7]);
  SingleTest(@[1, 1000, 2, 3, 4, 5, 6, 7]);
end.  
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten