[gelöst] Zugriffsverletzung Array

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

[gelöst] Zugriffsverletzung Array

Beitrag von Michl »

Hallo Forum,

ich bin über ein Problem gestolpert, das ich einfach umgehen kann. Mir ist jedoch nicht klar, ob es sich dabei um ein Bug oder Feature des Compilers handelt:

Code: Alles auswählen

type
 
  TListRec = Record
    ID: Integer;
    Zahl: Integer;
  end;
 
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    List: Array of TListRec;
    function GetListPos(aID: Integer): Int32;
    procedure IncZahl(aID: Integer);
  public
  end;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
  IncZahl(Random(100));
end;
 
function TForm1.GetListPos(aID: Integer): Int32;
var
  i: Int32;
begin
  i:=0;
  while (i < Length(List)) and (List[i].ID <> aID) do
    inc(i);
 
  if i < Length(List) then begin
    Result:=i;
    Exit;
  end;
 
  SetLength(List, Length(List) + 1);
  Result:=High(List);
  List[Result].Zahl:=0;
  List[Result].ID:=aID;
end;
 
procedure TForm1.IncZahl(aID: Integer);
var
  aPos: Integer;
begin
//  aPos:=GetListPos(aID);         //Das ist kein Problem
//  Inc(List[aPos].Zahl);
 
  Inc(List[GetListPos(aID)].Zahl); //hier kommt es zu einer Zugriffsverletzung
end; 
Zuletzt geändert von Michl am Di 2. Dez 2014, 20:00, insgesamt 1-mal geändert.

Code: Alles auswählen

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

ruewa
Beiträge: 153
Registriert: Sa 12. Apr 2014, 14:43

Re: Zugriffsverletzung Array

Beitrag von ruewa »

Verschwindet der Fehler, wenn Du der Unit ein {$mode objfpc} spendierst?

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Zugriffsverletzung Array

Beitrag von mse »

Michl hat geschrieben:

Code: Alles auswählen

type
 
function TForm1.GetListPos(aID: Integer): Int32;
[...] 
  SetLength(List, Length(List) + 1); <<<<--- kann die position von List im speicher verändern.
[...]
 
 
procedure TForm1.IncZahl(aID: Integer);
var
  aPos: Integer;
begin
//  aPos:=GetListPos(aID);         //Das ist kein Problem
//  Inc(List[aPos].Zahl);
 
  Inc(List[GetListPos(aID)].Zahl); //hier kommt es zu einer Zugriffsverletzung
        ^---  weil der ursprüngliche pointer auf List[0] nicht mehr gültig ist
 
end; 

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

Re: Zugriffsverletzung Array

Beitrag von Michl »

mse hat geschrieben:
Michl hat geschrieben:

Code: Alles auswählen

[...] 
  SetLength(List, Length(List) + 1); <<<<--- kann die position von List im speicher verändern.
[...]
Das verstehe ich, aber müsste der Compiler nicht als erstes GetListPos abarbeiten, bevor er die Adresse vom Array "List" nachfragt?! Bzw. könnte man das dem Compiler nicht beibringen?

Sollte ich dazu ein Feature Request machen oder ist das halt einfach so und man muss das einfach beherzigen?!
ruewa hat geschrieben:Verschwindet der Fehler, wenn Du der Unit ein {$mode objfpc} spendierst?
Es ist in dem Modus so.

Code: Alles auswählen

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

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

Re: Zugriffsverletzung Array

Beitrag von Mathias »

Ich habe alles Überflüssige entfernt, der Effekt bleibt erhalten.

Code: Alles auswählen

program Project1;
 
var
  List: array of integer;
 
  function GetListPos(aID: integer): integer;
  begin
    SetLength(List, aID + 1);
    Result := aID;
    List[aID]:=aID;
  end;
 
var
  aPos: integer;
begin
//  aPos := GetListPos(10);         //Das ist kein Problem
//  Inc(List[aPos]);
 
  Inc(List[GetListPos(10)]); //hier kommt es zu einer Zugriffsverletzung
  Writeln(List[10]);
  Readln;
end.     
PS: Ich habe es gerade mit Delphi 6 getestet, dort gehen beide Varianten und es kommt 11 raus.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Zugriffsverletzung Array

Beitrag von Michl »

Mathias hat geschrieben:Ich habe alles Überflüssige entfernt, der Effekt bleibt erhalten.
Danke für die Vereinfachung, soweit hatte ich das auf die Schnelle gar nicht reduzieren vermocht.
Mathias hat geschrieben:PS: Ich habe es gerade mit Delphi 6 getestet, dort gehen beide Varianten und es kommt 11 raus.
Interessant, dann scheint das tatsächlich ein Feature Request / Bugreport wert.

Ich habe unter Win7 Lazarus 1.2.6 und Lazarus Lazarus 1.3 r46988M FPC 2.7.1 i386-win32-win32/win64 getestet. Kann jemand noch ein Test unter Linux machen?

Vielen Dank

Michael

Code: Alles auswählen

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

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

Re: Zugriffsverletzung Array

Beitrag von wp_xyz »

Ubuntu 14.04 mit Lazarus 1.2.6/fpc 2.4.6 zeigt bei mir dasselbe Problem.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: Zugriffsverletzung Array

Beitrag von mse »

Michl hat geschrieben: Sollte ich dazu ein Feature Request machen oder ist das halt einfach so und man muss das einfach beherzigen?!
Ich glaube nicht, die Reihenfolge der Abarbeitung der einzelnen Operationen eines Ausdruckes ist nicht definiert und auch von den Optimierungsmöglichkeiten des Compilers abhängig. Dies muss man beim Verwenden von Funktionen mit Seiteneffekten berücksichtigen.

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

Re: Zugriffsverletzung Array

Beitrag von theo »

mse hat geschrieben: Ich glaube nicht, die Reihenfolge der Abarbeitung der einzelnen Operationen eines Ausdruckes ist nicht definiert und auch von den Optimierungsmöglichkeiten des Compilers abhängig. Dies muss man beim Verwenden von Funktionen mit Seiteneffekten berücksichtigen.
Hmm, aber schön ist das nicht, oder?
Wenn das so wirklich richtig sein soll, ergeben sich daraus mMn schwer zu entdeckende Bugs.

Das sieht doch erstmal unverdächtig aus:

Code: Alles auswählen

 Inc(List[GetListPos(aID)].Zahl);
Ich bin beim durchsehen der Funktion zwar auch auf die Idee gekommen, dass es mit dem SetLength auf der List zu tun haben könnte, habe den Gedanken wieder verworfen, da ich der Meinung war, dass der Compiler das der Reihe nach "abarbeiten" muss.

Mir gefällt das nicht.

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

Re: Zugriffsverletzung Array

Beitrag von Mathias »

laz hat geschrieben:Ich glaube nicht, die Reihenfolge der Abarbeitung der einzelnen Operationen eines Ausdruckes ist nicht definiert und auch von den Optimierungsmöglichkeiten des Compilers abhängig. Dies muss man beim Verwenden von Funktionen mit Seiteneffekten berücksichtigen.
Ich habe bei Optimierungen auf Stufe 0 gestellt, bringt aber nichts.
dass es mit dem SetLength auf der List zu tun haben könnte,
Kann ich mit nicht vorstellen, die Länge wird noch richtig ausgegben.

Code: Alles auswählen

  function GetListPos(aID: integer): integer;
  begin
    SetLength(List, aID + 1);
    Result := aID;
    List[aID]:=aID;
    writeln('xxxx', Length(List)); // xxxx11 wird noch ausgegeben, somit stimmt die Länge.
  end;   
Ich bin auch der Meinung, das dies ein Bug ist.
Unter Delphi läuft es ja auch.

Ich habe noch was anderes probiert.
Wen ich SetLength(List, 1) einfüge, geht es ohne Probleme. Auch Werte > 1 gehen, nur 0 geht nicht.

Code: Alles auswählen

program Project1;
var
  List: array of integer;
 
  function GetListPos(aID: integer): integer;
  begin
    SetLength(List, aID + 1);
    Result := aID;
    List[aID]:=aID;
  end;
 
begin
  SetLength(List, 1);  
 
  Inc(List[GetListPos(10)]); // Es gibt keine Verletzung mehr.
  Writeln(List[10]);
  Readln;
end.  
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Zugriffsverletzung Array

Beitrag von Michl »

Mathias hat geschrieben:
laz hat geschrieben:
dass es mit dem SetLength auf der List zu tun haben könnte,
Kann ich mit nicht vorstellen, die Länge wird noch richtig ausgegben.
Wie es zu dem SIGSEGV kommt, hatte mse erklärt:

Code: Alles auswählen

program Project1;
 
var
  List: array of integer;
 
function GetListPos(aID: integer): integer;
begin
  SetLength(List, aID + 1);
  Result := aID;
  List[aID]:=aID;
end;
 
begin
  SetLength(List, 1);
 
  Inc(List[GetListPos(10)]);      // Es gibt keine Verletzung mehr.
  Inc(List[GetListPos(100000)]);  //Nachdem das Array keinen Platz mehr an der alten Stelle hat, gibt es wieder ein SIGSEGV.
  Writeln(List[10]);
  Readln;
end. 
Habe mal ein Bugreport erstellt: http://bugs.freepascal.org/view.php?id=27111
Mal sehen, was die FPC-Entwickler dazu sagen.

Danke für Eure Meinungen und eine schönen RestErstenAdventSonntag mit vielen leckeren Plätzchen!!!
Zuletzt geändert von Michl am So 30. Nov 2014, 17:51, insgesamt 1-mal geändert.

Code: Alles auswählen

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

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

Re: Zugriffsverletzung Array

Beitrag von Mathias »

Wen ich dich richtig verstehe, dann wird beim SetLength in GetListPos der Zeiger auf die Array verändert, wen die Array grösser wird, und dadurch gibt es einen Crash, weil Inc(List[? noch auf den alten Zeiger zugreift.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Zugriffsverletzung Array

Beitrag von Michl »

Ja, so wird es wohl sein. Ich hatte eben mal einen Test gemacht, wohin der Zeiger des Arrays zeigt (zeigte immer noch auf die alte Stelle), hat FPC wahrscheinlich diesen Speicherbereich noch nicht für sich reserviert - ist evtl auch blos Zufall gewesen?!

Hatte eben noch den Bugreport erstellt, mal sehen, was man dazu sagt (wenn man mich mit meinem "Englisch" versteht).

Code: Alles auswählen

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

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

Re: Zugriffsverletzung Array

Beitrag von wp_xyz »

Michl, das ist der Record: dein Report ist schon "resolved"

[EDIT]: zu früh gefreut: Beim näheren Hinsehen sehe ich "Resolution: duplicate". Der andere Report hängt schon sein April rum...

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

Re: [gelöst] Zugriffsverletzung Array

Beitrag von Michl »

Klarheit nötigt zur Einsicht - in diesem Fall leider nicht. "The order of evaluation of the index-expressions of an indexed-variable is implementation-dependent" bedeutet, dass nach Pascal-Standard, so wie mse schon schrieb, nicht vorgegeben ist, ob zuerst der Index des Arrays oder das Array an sich ausgewertet werden. Hier hat FreePascal freie Hand und so, wie Jonas Maebe schrieb, will man sich da nichts aufzwingen lassen (weder von Delphi noch von mir :wink: ).

Es kann sein, sollte einmal der Compiler aus Performancegründen oder sonstwie umgeschrieben werden, dieses Verhalten "zufällig" mit geändert wird.

Ich nehme für mich dieses Verhalten als Feature zur Kenntnis, so richtig glücklich bin ich darüber nicht.

Code: Alles auswählen

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

Antworten