Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - AV bei SetLength


Silas - Di 17.07.07 14:18
Titel: AV bei SetLength
Hallo :wave:

Ich hab' da ein Problem :)

Mein Code sieht (gekürzt) so aus:

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:
...

type
  TFIniValue = record
    ...
  end;

  TFIniSection = record
    ...
    Values: array of TFIniValue;
    ValuesLength: Cardinal;
    ...
  end;

  TFIniFile = class
    private
      Sections: array of TFIniSection;
      SectionsLength: Cardinal;
      ...
      CurrentSection: Integer;
      ...
    public
      constructor Create(FileName: String);
      ...
  end;

  ...

implementation

constructor TFIniFile.Create(FileName: String);
var Buf, Buf1, Buf2: String;
begin
  ...
      SetLength(Sections, SectionsLength+1);
      inc(SectionsLength);
      ...
      CurrentSection := SectionsLength-1;
    ...
      with Sections[CurrentSection] do begin
        SetLength(Values, ValuesLength+1);
        inc(ValuesLength);
        ...
      end;
...
end;

...

Nun bekomme ich in der markierten Zeile eine AV: Access violation at adress 0045B13A in module 'test.exe'. Read of address 00000008

Was hab' ich falsch gemacht?


BenBE - Di 17.07.07 21:14

Du hast grad genau die Zeilen rausgelassen, die für die Fehlerdiagnose interessant gewesen wären. Poste mal bitte den Gesamten Constructor.


Delete - Di 17.07.07 21:52

SetLength löst keine AccessViolation aus. Ist die Instanz von Sections gültig?


Silas - Mi 18.07.07 15:20

Hier mal der Komplette Constructor inkl. den zwei Funktionen, die er noch verwendet:

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:
constructor TFIniFile.Create(FileName: String);
var Buf, Buf1, Buf2: String;
begin
  AssignFile(IniFile, FileName);
  Reset(IniFile);
  while not EOF(IniFile) do begin
    readln(IniFile, Buf);
    DeleteSpaces(Buf);
    if (Pos('[', Buf) <> -1 ) and (Buf[Length(Buf)-1] = ']'then begin
      SetLength(Sections, SectionsLength+1);
      inc(SectionsLength);
      Sections[SectionsLength-1].Deleted := false;
      Sections[SectionsLength-1].Name := Copy(Buf, 1, Length(Buf)-3);
      CurrentSection := SectionsLength-1;
    end else begin
      if Pos('=', Buf) = -1 then continue;
      SplitAt(Buf, Buf1, Buf2, '=');
      DeleteSpaces(Buf1);
      DeleteSpaces(Buf2);
      with Sections[CurrentSection] do begin
        SetLength(Values, ValuesLength+1);
        inc(ValuesLength);
        Values[ValuesLength-1].Name := Buf1;
        Values[ValuesLength-1].Value := Buf2;
        Values[ValuesLength-1].Deleted := false;
      end;
    end;
  end;
  CurrentSection := -1;
end;

//---------------

procedure SplitAt(const Str: Stringvar Sub1, Sub2: Stringconst Chr: Char);
var ChrPos: Integer;
begin
  ChrPos := Pos(Chr, Str);
  if ChrPos = -1 then begin
    Sub1 := Str;
    Sub2 := '';
  end else begin
    Sub1 := Copy(Str, 0, ChrPos-1);
    Sub2 := Copy(Str, ChrPos+1, Length(Str)-ChrPos);
  end;
end;

procedure DeleteSpaces(var Str: String);
var LStrPos, i: Cardinal;
    LastNSpc, FirstNSpc: Integer;
begin
  if (Pos(' ', Str) = -1or (Length(Str) = 0then exit;
  LStrPos := Length(Str)-1;
  FirstNSpc := -1;
  for i := 0 to LStrPos do begin
    if Str[i] <> ' ' then begin
      FirstNSpc := i;
      break;
    end;
  end;
  LastNSpc := -1;
  for i := LStrPos downto 0 do
    if Str[i] <> ' ' then begin
      LastNSpc := i;
      break;
    end;
  Str := Copy(Str, FirstNSpc, LastNSpc-FirstNSpc+1);
end;


Zum leichteren verständnis: Create liest eine Zeile aus einer Ini-Datei entweder in einen Sektionen- oder einen Wertepuffer ein; SplitAt teilt einen String an einem Char (der dabei wegfällt) und DeleteSpaces löscht Leerzeichen, die am Anfang und am Ende eines Strings stehen.


alzaimar - Mi 18.07.07 15:26

Wo wird Sectionslength und ValuesLength initialisiert? Schalte mal deine Compilerwarnungen EIN! Dann siehst Du solche Schusseligkeitsfehler schneller.


Silas - Mi 18.07.07 15:35

Ich hab's mal mit Initialisierung versucht, die einzige Veränderung ist, dass die AV jetzt Read of Address FFFFFFF8 heißt.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
constructor TFIniFile.Create(FileName: String);
var Buf, Buf1, Buf2: String;
begin
  AssignFile(IniFile, FileName);
  Reset(IniFile);
  SectionsLength := 0;
  CurrentSection := -1;
  while not EOF(IniFile) do begin
    ...
      inc(SectionsLength);
      Sections[SectionsLength-1].ValuesLength := 0;
      Sections[SectionsLength-1].Deleted := false;
      Sections[SectionsLength-1].Name := Copy(Buf, 1, Length(Buf)-3);
    ...
  end;
  CurrentSection := -1;
end;


Meine Compilerwarnungen waren eingeschaltet, eine Meldung (außer der Erfolgsmeldung) hab ich nicht bekommen.


alzaimar - Mi 18.07.07 15:52

user profile iconSilas hat folgendes geschrieben:
Ich hab's mal mit Initialisierung versucht, die einzige Veränderung ist, dass die AV jetzt Read of Address FFFFFFF8 heißt.

Ein Indiz dafür, das das auch gefehlt hat... Und zwar initialisierst Du 'CurrentSection' auf -1.. Wenn nun CurrentSection vorher nicht gesetzt wird, dann geht dieser Aufruf in die Pampa ('FFFFFF8') vorher war CurrentSection mangels Initialisierung bei 0 (schätze ich) und dann könnte das passen.

Delphi-Quelltext
1:
2:
3:
4:
...
  with Sections[CurrentSection] do begin
     SetLength(Values, ValuesLength+1);
 ...


BenBE - Mi 18.07.07 19:53

user profile iconSilas hat folgendes geschrieben:
Hier mal der Komplette Constructor inkl. den zwei Funktionen, die er noch verwendet:

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:
constructor TFIniFile.Create(FileName: String);
var Buf, Buf1, Buf2: String;
begin
  AssignFile(IniFile, FileName);
  Reset(IniFile);
  SectionsLength := 0;
  while not EOF(IniFile) do begin
    readln(IniFile, Buf);
    Buf := Trim(Buf);
    if (Pos('[', Buf) <> -1 ) and (Buf[Length(Buf)-1] = ']'then begin
      SetLength(Sections, SectionsLength+1);
      inc(SectionsLength);
      Sections[SectionsLength-1].Deleted := false;
      Sections[SectionsLength-1].Name := Copy(Buf, 1, Length(Buf)-3);
      CurrentSection := SectionsLength-1;
      Sections[CurrentSection] := TINISection.Create(Self);
      ValuesLength := 0;
    end else begin
      if Pos('=', Buf) = -1 then 
        continue;
      SplitAt(Buf, Buf1, Buf2, '=');
      Buf1 := Trim(Buf1);
      Buf2 := Trim(Buf2);
      with Sections[CurrentSection] do begin
        SetLength(Values, ValuesLength+1);
        inc(ValuesLength);
        Values[ValuesLength-1] := TINIValue.Create;
        Values[ValuesLength-1].Name := Buf1;
        Values[ValuesLength-1].Value := Buf2;
        Values[ValuesLength-1].Deleted := false;
      end;
    end;
  end;
  CurrentSection := -1;
end;

//---------------

procedure SplitAt(const Str: Stringvar Sub1, Sub2: Stringconst Chr: Char);
var ChrPos: Integer;
begin
  ChrPos := Pos(Chr, Str);
  if ChrPos = -1 then begin
    Sub1 := Str;
    Sub2 := '';
  end else begin
    Sub1 := Copy(Str, 0, ChrPos-1);
    Sub2 := Copy(Str, ChrPos+1, Length(Str)-ChrPos);
  end;
end;

procedure DeleteSpaces(var Str: String);
var LStrPos, i: Cardinal;
    LastNSpc, FirstNSpc: Integer;
begin
  if (Pos(' ', Str) = -1or (Length(Str) = 0then exit;
  LStrPos := Length(Str)-1;
  FirstNSpc := -1;
  for i := 0 to LStrPos do begin
    if Str[i] <> ' ' then begin
      FirstNSpc := i;
      break;
    end;
  end;
  LastNSpc := -1;
  for i := LStrPos downto 0 do
    if Str[i] <> ' ' then begin
      LastNSpc := i;
      break;
    end;
  Str := Copy(Str, FirstNSpc, LastNSpc-FirstNSpc+1);
end;


Zum leichteren verständnis: Create liest eine Zeile aus einer Ini-Datei entweder in einen Sektionen- oder einen Wertepuffer ein; SplitAt teilt einen String an einem Char (der dabei wegfällt) und DeleteSpaces löscht Leerzeichen, die am Anfang und am Ende eines Strings stehen.


Hab mal ein wenig rumgebastelt ... im Kopf ... Sollte so funzen ... Ggf. die Aufrufe der Konstruktoren mal anpassen :P


alzaimar - Do 19.07.07 10:51

... und wenn der else-Teil zuerst angesprungen wird, passiert das Gleiche (weil 'CurrentSection' undefiniert ist).


BenBE - Do 19.07.07 12:45

So viel Denken trau ich ihm zu, dass er die dafür benötigte Denkleistung alleine aufbringt ...


Silas - Do 19.07.07 15:39

Danke Dir, BenBE! Funzt jetzt wunderbar. :)