Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Access Violation bei SetLength


der organist - Di 24.02.09 18:54
Titel: Access Violation bei SetLength
Hi Leutz,

ich hab keine Idee mehr, woran es liegen kann.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
type
  TSpieler=Class
    private
    public
      Insel:Record
        Koord:Array of TPoint;
        Name:Array of String;
      end;
      Punkte,ID:Integer;
      Name:String;
      constructor Create(AName:String;APunkte,AID:Integer);
      procedure AddIsland(AKoord:TPoint;AName:String);
      procedure DeleteIsland(AKoord:TPoint;AName:String);
  End;



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:
procedure TSpieler.AddIsland(AKoord: TPoint; AName: String);
var
  k: Integer;
  Name,Koord:Boolean;
begin
  Name:=true;
  Koord:=true;
  if Length(Insel.Koord)>0 then begin
    for k := 0 to Length(Insel.Koord) - 1 do begin
      if Insel.Name[k]=AName then
        Name:=false;
      if (Insel.Koord[k].X=AKoord.X) AND (Insel.Koord[k].Y=AKoord.Y) then
        Koord:=false;
    end;
  end;
  if Name AND Koord then begin
    SetLength(Insel.Koord,Length(Insel.Koord)+1);
    SetLength(Insel.Name,Length(Insel.Name)+1);
    Insel.Koord[Length(Insel.Koord)-1].X:=AKoord.X;
    Insel.Koord[Length(Insel.Koord)-1].Y:=AKoord.Y;
    Insel.Name[Length(Insel.Name)-1]:=AName;
  end
  else
    if (NOT Name) AND (NOT Koord) then
      ShowMessage('Koordinaten sind schon unter diesem Name eingetragen')
    else
      if (NOT Name) AND Koord then
        ShowMessage('Name existiert schon mit anderen Koordinaten')
      else
        ShowMessage('Koordinaten existieren schon unter anderem Inselnamen');
end;


überall, wo ich Insel.Koord aufrufe, gibt es ne Violation, da kann ich die eine auskommentieren, dann stehts halt beim nächsten mal.

Des Weiteren ist dies nicht die erste Version des Quelltextes. Zuvor hatte ich, anstatt die Länge des Koordinaten-Arrays zu zählen, eine Variable "Anzahl", im Record definiert, was allerdings den selben Fehler hervorruft.

Außerdem lässt sich sagen, dass beim ersten Mal Insel hinzufügen, keine Violation auftritt; Erst, wenn ich eine zweite hinzufügen will, gibts Probleme.

MfG,


Xentar - Di 24.02.09 20:02

1. Instanziierst du die Klasse auch, wenn du sie benutzt? Zeig mal den Aufruf..
2. Warum ein Record, bestehend aus 2 Arrays? Wenn beide zusammengehören, mach doch lieber ein array of record.
3. Äh.. wieso deklarierst du das Record IM Public Bereich? Ich kenn das eigentlich nur so, dass man einen Type erstellt, und diesen dann nur noch verwendet.


der organist - Di 24.02.09 20:06

ok, ich seh' die Lösung zwar noch nicht, aber:
zu 1. Was meinst du damit? Ob ich den Spieler auch created hab?
zu 2. Hab noch nie drüber nachgedacht, aber wie mach ich das? Wo defnier ich das Record um danach ein Array daraus zu
machen? (Quelltext erwünscht..)
zu 3. Naja, Hobbyprogrammierung eben, hab nie Unterricht drin gehabt und außerdem greife ich regelmäßig in anderen Units
drauf zu...

MfG,


Xentar - Di 24.02.09 20:11

user profile iconder organist hat folgendes geschrieben Zum zitierten Posting springen:
ok, ich seh' die Lösung zwar noch nicht, aber:
zu 1. Was meinst du damit? Ob ich den Spieler auch created hab?

Richtig.

Zu den anderen Sachen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Type
  TInsel = Record
    Koord: TPoint;
    Name: String;
  end;
  TInseln = array of TInsel;

  TSpieler = class
    .......
    Inseln: TInseln;


der organist - Di 24.02.09 20:16

Ok, das mit dem Inselrecord, das ist iwie besser, werde mich dann mal auf viele Änderungen einstellen, wenn ich im Interface-Teil etwas ändere.....aber ich probiere es aus.

Ja, den Spieler hab ich Created und die erste Insel "erschaffen" funzt auch richtig, die zweite will nicht mehr.


der organist - Di 17.03.09 20:19

*push* Problem tritt immer noch auf und stört momentan wirklich

(a) Hab mir ein Record angelegt
(b) ich hab mal alles auskommentiert, was nicht den Fehler verursacht, Problemzeile hab ich markiert
(c) Wenn ich bei Spieler A eine Insel hinzufüge, dann bei Spieler B und dann wieder bei A, dann gibt es keine Probleme, nur wenn ich nacheinander zwei bei Spieler A hinzufügen will.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Type
  TInsel=Record
    Koord: TPoint;
    Name: String;
  end;
  TSpieler=Class
    private
      .....
    public
      Insel:Array of Tinsel;
      .....
  end;



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:
procedure TSpieler.AddIsland(AKoord: TPoint; AName: String);
var
  k: Integer;
  TName,TKoord:Boolean;
begin
  //TName:=true;
  //TKoord:=true;
  {if Length(Insel)>0 then begin
    for k := 0 to Length(Insel) - 1 do begin
      if Insel[k].Name=AName then
        TName:=false;
      if (Insel[k].Koord.X=AKoord.X) AND (Insel[k].Koord.Y=AKoord.Y) then
        TKoord:=false;
    end;
  end;}

  //if TName AND TKoord then begin
    SetLength(Insel,Length(Insel)+1);
    //Insel[Length(Insel)-1].Koord.X:=AKoord.X;
    //Insel[Length(Insel)-1].Koord.Y:=AKoord.Y;
    //Insel[Length(Insel)-1].Name:=AName;
  {end
  else
    if (NOT TName) AND (NOT TKoord) then
      ShowMessage('Koordinaten sind schon unter diesem Name eingetragen')
    else
      if (NOT TName) AND TKoord then
        ShowMessage('Name existiert schon mit anderen Koordinaten')
      else
        ShowMessage('Koordinaten existieren schon unter anderem Inselnamen');   }

end;


Xentar - Di 17.03.09 20:53

Zeig mal mehr Code.. also, wie du die Spieler erzeugst, wo du die Methode AddIsland aufrufst, usw.


Yogu - Di 17.03.09 21:34

Ich glaube, du hast vergessen, eine Instanz von TSpieler zu erstellen, nach der Fehlermeldung und dem wenigen Code zu urteilen.


dummzeuch - Di 17.03.09 22:48

user profile iconYogu hat folgendes geschrieben Zum zitierten Posting springen:
Ich glaube, du hast vergessen, eine Instanz von TSpieler zu erstellen, nach der Fehlermeldung und dem wenigen Code zu urteilen.


Das glaube ich auch. Vermutlich der Klassiker:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  Spieler: TSpieler;
// ...
begin
  Spieler.Create;

// richtig waere:
  Spieler := TSpieler.Create;


Passiert mir peinlicherweise auch immer wieder mal.

twm


Boldar - Di 17.03.09 22:50

Ich glaube, dass gibt aber schon eine Fehlermeldung beim Compilieren...


Xentar - Di 17.03.09 22:53

user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Ich glaube, dass gibt aber schon eine Fehlermeldung beim Compilieren...

Nö, nur eine Warnung.
Zitat:
[DCC Warnung] W1036 Variable 'blah' ist möglicherweise nicht initialisiert worden

und es gibt genug Leute, die eine Warnung nicht ernst nehmen..


dummzeuch - Di 17.03.09 22:57

user profile iconXentar hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Ich glaube, dass gibt aber schon eine Fehlermeldung beim Compilieren...

Nö, nur eine Warnung.
Zitat:
[DCC Warnung] W1036 Variable 'blah' ist möglicherweise nicht initialisiert worden

und es gibt genug Leute, die eine Warnung nicht ernst nehmen..


Und diese Warnung gibt es nicht, wenn die Variable vorher mal initialisiert wurde, z.B. durch Zweisung von NIL oder wenn es sich um ein Feld einer Klasse handelt.

twm


der organist - Mi 18.03.09 19:58

Für die Ungläubigen (nehmts mir nich übel):

Instanzierung vom Spieler:


Delphi-Quelltext
1:
2:
3:
4:
SetLength(Player,Length(Player)+1);
Player[Length(Player)-1]:=TSpieler.Create(Name,
                                          StrToInt(Punkte),
                                          Length(Player)-1);


Das ganze sieht ein wenig chaotisch aus, weil ich ein Array of TSpieler hab.

Aufrufen von AddIsland:



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TDialogAddIsland.AddClick(Sender: TObject);
begin
  UMain.Main.Player[Players.Itemindex].AddIsland(Point(StrToInt(Koord1.Text),
                                                        StrToInt(Koord2.Text)),
                                                 IslandName.Text);
  ReFill;
end;


Ist mehr gewünscht? Kann auch alle Units kopieren :D .....aber das dürfte dann doch zu viel sein.


Yogu - Mi 18.03.09 20:16

Hallo,

bitte benutze, um das oberste Element eines dynamischen Arrays zu bekommen, die Funktion High anstatt Length(Arr)-1. Das dürfte aber nicht das Problem sein, da dynamische Arrays für gewöhnlich bei Null beginnen.

Zur Fehlersuche: Schau mal, ob in der Funktion AddClick UMain.Main.Player[Players.Itemindex] assigned ist. Und ob es auf eine gültige Instanz zeigt. Dazu könntest du ein einfaches privates Feld der Klasse erstellen (vom Typ Integer oder so), und auf es zugreifen. Sollte dort auch eine Zugriffsverletzung kommen, liegt es sicher daran.

Noch was: initialisierst du die dynamischen Arrays im Konstruktor? Du solltest SetLength(Arr, 0); aufrufen, um den Pointer erstmal richtig zu setzen.

Grüße,
Yogu


der organist - Mi 18.03.09 20:24

Was meinst du mit assigned? Hab noch nie was davon gehört, also von vorne erklären...


Yogu - Mi 18.03.09 20:28

user profile iconder organist hat folgendes geschrieben Zum zitierten Posting springen:
Was meinst du mit assigned? Hab noch nie was davon gehört, also von vorne erklären...

Ich meinte, ob das Objekt nil ist, oder ob es exisitert. Einfach mal ein Haltepunkt an eine günstige Stelle setzen und mit der Maus über den Namen fahren, dann siehst du entweder eine Adresse (das ist gut) oder nil (dann hast du was falsch gemacht).


der organist - Mi 18.03.09 20:33

z.B. ich bewege die Maus über dsa Itemindex aus myCombobox.Itemindex. Bei mir erscheint dann:

Property
System.Integer

(oder so ähnlich...)

ich ersetze mal das Players.itemindex durch ne konstante und schau ob es daran liegt.


Xentar - Mi 18.03.09 20:40

user profile iconder organist hat folgendes geschrieben Zum zitierten Posting springen:
z.B. ich bewege die Maus über dsa Itemindex aus myCombobox.Itemindex. Bei mir erscheint dann:

Property
System.Integer

Er meint zur Laufzeit (Haltepunkt setzen - F5)


der organist - Mi 18.03.09 20:46

ok, hab ich gemacht. wenn ich dann z.B. über UMain gehe, dann wird ne Box angezeigt, mit ner Menge wirrwarr, die ich mir nicht lange angetan habe, bei dem Itemindex nicht. (Hat auch mit auskommentieren und durch Konstante ersetzten gefunzt... :D )ok, was mach ich jetzt?

Was ich noch gefunden habe ist:

Wenn ich die ReFill-prozedur nicht aufrufe, dann funzt es:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TDialogAddIsland.AddClick(Sender: TObject);
begin
  UMain.Main.Player[Players.Itemindex].AddIsland(Point(StrToInt(Koord1.Text),
                                                        StrToInt(Koord2.Text)),
                                                 IslandName.Text);
  ReFill;                                                      //<==die hier meine ich
end;


und das ist die ReFillprozedur:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TDialogAddIsland.ReFill;
var k:Integer;
begin
    IslandName.Text:='IslandName';
    Koord1.Text:='Koord:X';
    Koord2.Text:='Koord:Y';
    Players.Items.Clear;
    for k := 0 to Length(UMain.Main.Player)-1 do begin
      UDialogAddIsland.DialogAddIsland.Players.Items.Add(UMain.Main.Player[k].Name);
    end;
end;


und ich glaube, mir ist gerade jetzt der Fehler aufgefallen:
Beim neufüllen der Combobox wird der Itemindex wieder auf (-1) oder was weiss ich gesetzt, und ein Player[-1] gibbet es natürlich nicht.

EDIT: DAS WARS LEUTE!!! Wir haben ihn gefunden. Danke allen, die sich hier die Mühe gemacht haben und dann die Anstöße gebracht haben.