AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Zugriffsverletzung bei Zeiger auf statisches Array
Thema durchsuchen
Ansicht
Themen-Optionen

Zugriffsverletzung bei Zeiger auf statisches Array

Ein Thema von Codehunter · begonnen am 26. Aug 2019 · letzter Beitrag vom 26. Aug 2019
Antwort Antwort
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#1

Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 09:52
Hallo!

Ich versuche gerade, eine Klassenstruktur zu erfinden, die sich über ein statisches Array initialisieren lässt. Nur leider geht die Runtime nicht mit meiner Theorie konform und wirft eine AV.
Delphi-Quellcode:
unit Unit1;

interface

type
  TMyRecord = record
    A: Integer;
    B: Integer;
    C: Byte;
  end;

  TMyRecords = array of TMyRecord;
  PMyRecords = ^TMyRecords;

  TMyClass = class
  private
    FRecords: PMyRecords;
  protected
    function GetRecord(const AIndex: Integer): TMyRecord;
  public
    property Records[const AIndex: Integer]: TMyRecord read GetRecord; default;
  end;

  TMyClassA = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6)
    );
  public
    constructor Create;
  end;

  TMyClassB = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6),
      (A: 7; B: 8; C: 9)
    );
  public
    constructor Create;
  end;

implementation

{ TMyClass }

function TMyClass.GetRecord(const AIndex: Integer): TMyRecord;
begin
  Result := FRecords^[AIndex]; // <-- Hier die AV
end;

{ TMyClassA }

constructor TMyClassA.Create;
begin
  Records := @RECS;
end;

{ TMyClassB }

constructor TMyClassB.Create;
begin
  Records := @RECS;
end;

// ---------- Formular-Unit ---------

procedure TForm1.Button1Click(Sender: TObject);
var
  LClassA: TMyClassA;
begin
  LClassA := TMyClassA.Create;
  try
    ShowMessage(LClassA[1].A.ToString);
  finally
    FreeAndNil(LClassA);
  end;
end;

end.
Ich vermute mal, es kommt daher, dass ich hier TMyClass.FRecords als Zeiger auf ein dynamisches Array definiere und quasi als Platzhalter für ein statisches Array missbrauche.

In der Praxis ist die Array-Initialisierung um einiges komplexer. Bisher habe ich das über eine Klasse statt Record, ein TObjectList<TMyClass>, eine Schleife, ein case-Schleifenzähler-of und eine lange Codesequenz (ca. 670 Zeilen) gelöst. Mit den statischen Array-Initialisierungen im Klassenkopf dampft das Ganze auf 55 Zeilen ein, sollte weniger Laufzeit benötigen und ist sogar noch übersichtlicher.

Jetzt müsste nur noch die Runtime mitspielen

Grüße
Cody
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
9.345 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:09
Ein einfacher Trick um die unterschiedlichen Arraytypen anzugleichen ist die Deklaration so zu ändern:
Delphi-Quellcode:
type
  TMyRecords = array[0..100] of TMyRecord;
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.060 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:10
Delphi-Quellcode:
unit Codehunter.View;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  type
  TMyRecord = record
    A: Integer;
    B: Integer;
    C: Byte;
  end;

  TMyRecords = array of TMyRecord;
  PMyRecords = ^TMyRecords;

  TMyClass = class
  private
    FRecords: TMyRecords;
  protected
    function GetRecord(const AIndex: Integer): TMyRecord;
  public
    property Records[const AIndex: Integer]: TMyRecord read GetRecord; default;
  end;

  TMyClassA = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6)
    );
  public
    constructor Create;
  end;

  TMyClassB = class(TMyClass)
  strict private const
    RECS: array[0..2] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6),
      (A: 7; B: 8; C: 9)
    );
  public
    constructor Create;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMyClass }

function TMyClass.GetRecord(const AIndex: Integer): TMyRecord;
begin
  Result := FRecords[AIndex];
end;

{ TMyClassA }

constructor TMyClassA.Create;
var
  I: Integer;
begin
  System.SetLength(FRecords, Length(RECS));
  for I := System.Low(RECS) to System.High(RECS) do
    System.Move(RECS[I], FRecords[I], SizeOf(RECS[I]));
end;

{ TMyClassB }

constructor TMyClassB.Create;
var
  I: Integer;
begin
  System.SetLength(FRecords, Length(RECS));
  for I := System.Low(RECS) to System.High(RECS) do
    System.Move(RECS[I], FRecords[I], SizeOf(RECS[I]));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  LClassA: TMyClassA;
begin
  LClassA := TMyClassA.Create;
  try
    ShowMessage(LClassA[1].A.ToString);
  finally
    LClassA.Free;
  end;
end;

end.
Aber nur wenn du versprichst, dass TMyRecord keine referenzgezählten Typen verwendet.
Okay sind ordinale Typen (Byte, Integer, Cardinal, Int64). Pfui alles andere (string, Interface, dynamisches Array).
  Mit Zitat antworten Zitat
Benutzerbild von Neutral General
Neutral General

Registriert seit: 16. Jan 2004
Ort: Bendorf
5.219 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#4

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:12
Deklarier TMyRecords mal so:

TMyRecords = Array[0..0] of TMyRecord;
Michael
"Programmers talk about software development on weekends, vacations, and over meals not because they lack imagination,
but because their imagination reveals worlds that others cannot see."
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:22
Delphi-Quellcode:
{ TMyClassB }

constructor TMyClassA.Create;
var
  I: Integer;
begin
  System.SetLength(FRecords, Length(RECS));
  for I := System.Low(RECS) to System.High(RECS) do
    System.Move(RECS[I], FRecords[I], SizeOf(RECS[I]));
end;

{ TMyClassB }

constructor TMyClassB.Create;
var
  I: Integer;
begin
  System.SetLength(FRecords, Length(RECS));
  for I := System.Low(RECS) to System.High(RECS) do
    System.Move(RECS[I], FRecords[I], SizeOf(RECS[I]));
end;
Genau DAS wollte ich eigentlich vermeiden. Erstens weils wieder eine Schleife wäre, zweitens weil ich dann Copy&Paste-Programmierung hätte in jeder Ableitung von TMyClass.

Deklarier TMyRecords mal so:

TMyRecords = Array[0..0] of TMyRecord;
Ein einfacher Trick um die unterschiedlichen Arraytypen anzugleichen ist die Deklaration so zu ändern:
Delphi-Quellcode:
type
  TMyRecords = array[0..100] of TMyRecord;
In beiden Fällen erhalte ich nun Kompilerfehler:
Delphi-Quellcode:
  TMyClassB = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6),
      (A: 7; B: 8; C: 9)
    ); //<-- "Anzahl der Elemente (3) weicht von der Deklaration (2) ab"
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.007 Beiträge
 
Delphi 12 Athens
 
#6

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:23
Ich vermute mal, es kommt daher, dass ich hier TMyClass.FRecords als Zeiger auf ein dynamisches Array definiere und quasi als Platzhalter für ein statisches Array missbrauche.
Da hast du recht: das kann so nicht gehen! Ein dynamisches Array ist ein Zeiger auf das Array, das noch mit ein paar Zusatzinformationen garniert ist, die ein statisches Array nicht hat.

Eventuell hilft dieser Ansatz bei deinem Problem:
Delphi-Quellcode:
unit Unit455;

interface

type
  TMyRecord = record
    A: Integer;
    B: Integer;
    C: Byte;
  end;

  TMyRecords = TArray<TMyRecord>;

type
  TMyClass = class
  private
    FRecords: TMyRecords;
  protected
    function GetRecord(const AIndex: Integer): TMyRecord;
    procedure InitRecords(const Source: array of TMyRecord);
  public
    property Records[const AIndex: Integer]: TMyRecord read GetRecord; default;
  end;

  TMyClassA = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6)
    );
  public
    constructor Create;
  end;

  TMyClassB = class(TMyClass)
  strict private const
    RECS: array[0..2] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6),
      (A: 7; B: 8; C: 9)
    );
  public
    constructor Create;
  end;

implementation

uses
  System.Generics.Collections;

{ TMyClass }

function TMyClass.GetRecord(const AIndex: Integer): TMyRecord;
begin
  Result := FRecords[AIndex];
end;

procedure TMyClass.InitRecords(const Source: array of TMyRecord);
begin
  SetLength(FRecords, Length(Source));
  TArray.Copy<TMyRecord>(Source, FRecords, Length(Source));
end;

{ TMyClassA }

constructor TMyClassA.Create;
begin
  InitRecords(Recs);
end;

{ TMyClassB }

constructor TMyClassB.Create;
begin
  InitRecords(Recs);
end;

end.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming

Geändert von Uwe Raabe (26. Aug 2019 um 10:32 Uhr) Grund: fehlendes SetLength ergänzt
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:39
@Uwe: Perfekt! So gehts. An generische Arrays hatte ich auch schon gedacht, bekam dann aber auch da nicht die generischen mit den statischen übereinander. Dass es eine statische Copy-Methode gibt war mir gar nicht bewusst. So müsste das doch eigentlich sogar dann funktionieren, wenn im Record ein String-Element vorkommt, oder?

@TiGü und Neutral General: Der Compilerfehler mit der Elementanzahl war eigene Blödheit, ich hatte (natürlich) auch per Copy&Paste vergessen, die Elementanzahl zu ändern. Aber Uwes Lösung passt am besten, weil ich dann die Objektorientierung am besten einhalten kann.
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Benutzerbild von p80286
p80286

Registriert seit: 28. Apr 2008
Ort: Stolberg (Rhl)
6.659 Beiträge
 
FreePascal / Lazarus
 
#8

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:40
Versuch es mal so:
Delphi-Quellcode:
 TMyRecords = array of TMyRecord;
  PMyRecords = ^TMyRecords[0];
Geht natürlich schief wenn nichts im Array ist.

Gruß
K-H
Programme gehorchen nicht Deinen Absichten sondern Deinen Anweisungen
R.E.D retired error detector
  Mit Zitat antworten Zitat
Benutzerbild von gubbe
gubbe

Registriert seit: 8. Okt 2005
Ort: Schleswig-Holstein
74 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Zugriffsverletzung bei Zeiger auf statisches Array

  Alt 26. Aug 2019, 10:42
Warum nicht klassisch objektorientiert, ganz ohne hässliche Pointer?

Code:

unit Unit1;

interface

type
  TMyRecord = record
    A: Integer;
    B: Integer;
    C: Byte;
  end;

  TMyClass = class
  private
  protected
    function GetRecord(const AIndex: Integer): TMyRecord; virtual; abstract;
  public
    property Records[const AIndex: Integer]: TMyRecord read GetRecord; default;
  end;

  TMyClassA = class(TMyClass)
  strict private const
    RECS: array[0..1] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6)
    );
  protected
    function GetRecord(const AIndex: Integer): TMyRecord; override;
  public
  end;

  TMyClassB = class(TMyClass)
  strict private const
    RECS: array[0..2] of TMyRecord = (
      (A: 1; B: 2; C: 3),
      (A: 4; B: 5; C: 6),
      (A: 7; B: 8; C: 9)
    );
  protected
    function GetRecord(const AIndex: Integer): TMyRecord; override;
  end;

implementation

function TMyClassA.GetRecord(const AIndex: Integer): TMyRecord;
begin
  result := Recs[AIndex];
end;

function TMyClassB.GetRecord(const AIndex: Integer): TMyRecord;
begin
  result := Recs[AIndex];
end;

end.
Und so kannst Du auch die Übergabeparameter noch prüfen.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz