Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Fehler mit Pointern


ShadeMoon - Di 02.06.09 12:53
Titel: Fehler mit Pointern
Ich bin gerade dabei, für die Schule eine kleine Datenbank zu schreiben. Leider funktioniert eine Methode nicht so, wie sie sollte. Ich habe bestimmt eine halbe Stunde lang versucht, herauszufinden, woran es liegt. So langsam glaube ich schon, es handelt sich um einen Bug in Delphi.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
unit SaveStructs;

interface

uses SysUtils;

type

Integers = array of Integer;

PPartner = ^TPartner;
TPartner = record
  Name, Vorname: string;
  Klasse: string;

  Sponsoring: string;
  Praktikumsplatz: Boolean;

  Nachhilfe: record
    Fach: string;
    StundenProWoche: Byte;
  end;

  Unterstuetzung: record
    Schulfest: Boolean;
    Klassenfahrt: Boolean;
    Unterrichtsbeitrag: Boolean;
    EDV: Boolean;
  end;

  Organisation: Byte;// in Stunden pro Woche
  Bibliotheksaufsicht: Byte; // in Stunden pro Woche
  Suchtberatung: Byte; // in Stunden je Woche
  Rechtskunde: Byte; // in Stunden je Woche

  Handwerk: string;

  ExperteFuer: string;

  Sonstiges: string;
end;

PListElm = ^TListElm;
TListElm = record
  Partner: PPartner;
  Next: PListElm;
end;

TPartnerList = class
private
  First: PListElm;
  Curr: PListElm;
  CurrID: Integer;
// ...
public
// ...
  procedure Add(const Partner: TPartner);
// ...
end;

// ...

implementation

// ...

procedure TPartnerList.Add(const Partner: TPartner);
var
  AddAfter, AddBefore: PListElm;
  New: PListElm;
begin
  GetMem(New, SizeOf(TListElm));
  GetMem(New^.Partner, SizeOf(TPartner));
  New^.Partner^ := Partner;
{FEHLER in vorheriger Zeile:
  […]
  Exception der Klasse EAccessViolation […]
  Zugriffverletzung bei Adresse […]
  Lesen von Adresse 0000002C […]
}

  if First <> nil then
  begin
    AddAfter := First;
    AddBefore := AddAfter^.Next;
    while (AddBefore <> niland Priority(AddBefore^.Partner^, Partner) do
    begin
      AddAfter := AddBefore;
      AddBefore := AddBefore^.Next;
    end;
    New^.Next := AddBefore;
    AddAfter^.Next := New;
  end
  else
  begin
    New^.Next := nil;
    First := New;
  end;
end;

// ...

end.

Wenn die Methode Add aufgerufen wird, tritt in der dritten Zeile (Zeile 74 des Codes) der Methode ein Fehler wegen Zugriffsverletzung auf.
Die Variable New^.Partner^ soll zu diesem Zeitpunkt undefiniert sein. Mit Ausnahme von Name und Vorname sind aber alle ihre Elemente definiert. Ich frage mich, wie das gehen soll.

Ich programmiere jetzt schon seit einigen Jahren mit Delphi, aber so etwas ist mir noch nicht begegnet.
Würde mich freuen, wenn jemand Rat weiß.

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


Sinspin - Di 02.06.09 16:02

Hi und :welcome: in der EE.

Einen Fehler in Delphi kann ich schonmal ausschließen, denke ich, auch wenn ich so adhoc auch keinen Fehler in deinem Quelltext sehe. Aber du könntest mal noch ein paar Sachen ausprobieren, falls du das noch nicht gemacht hast.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TPartnerList.Add(const Partner: TPartner);
var
  AddAfter, AddBefore: PListElm;
  NewEntry: PListElm; // New ist eine Procedure
  NewPartner: PPartner; 

begin
  GetMem(NewEntry, SizeOf(TListElm));
  GetMem(NewPartner, SizeOf(TPartner)); // Zeiger auf TPartner erstmal in eigener Variable erstellen
  NewPartner^ := Partner; // inhalt der variable in den speicherbereich kopieren
  NewEntry^.Partner := NewPartner; // 
// ...
end;

Das was du geschrieben hast klingt auf jeden Fall danach als wenn du dir Speicher überschrieben hättest.
Versuch es mal mit meinen Änderungen und berichte.


Delete - Di 02.06.09 16:14

Und der Variablenname New ist unglücklich gewählt.


ShadeMoon - Di 02.06.09 16:25

Danke!

Hm, ich verwende aus Prinzip GetMem statt New. Also, ursprünglich war der Grund, dass ich von New erst erfahren habe, als sich mein Informatiklehrer gewundert hat, was denn GetMem sei.^^ Und nun habe ich mich halt daran gewöhnt.

Ich habe die Variable jetzt mal umbenannt, aber der Fehler besteht, wie erwartet, leider weiterhin.


Sinspin - Di 02.06.09 16:36

Hast du auch die anderen Sachen geändert die ich dir vorgeschlagen habe?


AndyB - Di 02.06.09 17:59

user profile iconShadeMoon hat folgendes geschrieben Zum zitierten Posting springen:
Hm, ich verwende aus Prinzip GetMem statt New.

Und darin liegt wohl der Fehler. New() ruft zusätzlich noch InitializeRecord auf, das die String-Felder initialisiert. Auch Dispose() ruft im Gegensatz zu FreeMem auch noch FinalizeRecord auf, dass die String-Felder wieder aufräumt.

Moderiert von user profile iconNarses: Zitat repariert


Sinspin - Di 02.06.09 21:50

user profile iconAndyB hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconShadeMoon hat folgendes geschrieben Zum zitierten Posting springen:
Hm, ich verwende aus Prinzip GetMem statt New.

Und darin liegt wohl der Fehler. New() ruft zusätzlich noch InitializeRecord auf, das die String-Felder initialisiert. Auch Dispose() ruft im Gegensatz zu FreeMem auch noch FinalizeRecord auf, dass die String-Felder wieder aufräumt.

Oh ja, daran habe ich garnicht gedacht! Lange Strings und dynamische Arrays kannst du, soweit ich weis, nicht ohne Probleme verwenden wenn du mit selbst reserviertem Speicher arbeitest. Pass mal deine Records so an das du statt String überall ShortString verwendest, das ist zwar erstmal Speicherverschwendung, aber wenn der Fehler dann weg ist weißt du woran es lag.


ShadeMoon - Mo 08.06.09 07:11

Danke für die Hilfe, auch wenn ich den Fehler damit auch nicht beheben konnte, denn ich hatte vor lauter Pointern ganz vergessen, die Klasse zu instantiïeren.