Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Ungültige Zeigeroperation bzw AV beim Freigeben eines Arrays
Flamefire - Di 18.08.09 14:00
Titel: Ungültige Zeigeroperation bzw AV beim Freigeben eines Arrays
Ich habe einen Traffic logger geschrieben, der alle Packete aus einer Anwendung mitschneiden soll (àla WPE Pro aber als Proxy)
Die Packete speichere ich in einem Array, um sie filtern zu können, und zeige sie in einem Listview an.
Das Problem: Wenn ich das Array beim 2. mal freigeben möchte bekomme ich "Ungültige Zeigeroperation" beim SetLength(...,0) oder eine Access Violation beim Freigeben der Einträge im Listview.
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:
| TEntry = record opcode:Word; data:TBytes; direction:Byte; end; var Entry:Array[0..999] of TEntry; procedure TFrameAnalyzer.Add(opcode: Word; direction: Byte; data: pointer;size:word); var i:Integer; begin if(curEntry=1000) then begin for i := 0 to 500 - 1 do SetLength(Entry[i].data,0); Move(Entry[500],Entry[0],SizeOf(TEntry)*500); for i := 500 to 1000 - 1 do Entry[i].data:=nil; curEntry:=500; UpdateListView; end;
Entry[curEntry].opcode := opcode; Setlength(Entry[curEntry].data,size); if(size>0) then Move(data^,Entry[curEntry].data[0],size); Entry[curEntry].direction := direction; Inc(curEntry); end; procedure TFrameAnalyzer.UpdateListView; var i:Integer; begin sListView1.Items.BeginUpdate; sListView1.Items.Clear; for i := 0 to curEntry - 1 do begin if(not ElemBlocked(Entry[i])) then with sListView1.Items.Add do begin Caption:=IntToHex(Entry[i].opcode,4); SubItems.Add(Self.ByteToString(Entry[i])); SubItems.Add(StrDirection[Entry[i].direction]); end; end; sListView1.Items.EndUpdate; end; |
wenn ich "for i := 0 to 500 - 1 do SetLength(Entry[i].data,0);" entferne kommt kein Fehler mehr
edit:
Delphi-Quelltext
1: 2: 3:
| dass hier statt der schleifen funktioniert: for i := 0 to 500 - 1 do Entry[i]:=Entry[i+500]; |
aber warum?
ist ein dynamisches array in einem record nicht einfach ein pointer auf die array structur?
wird es dadurch nicht zum speicherleak? FastMM4 meldet zumindest nichts
BenBE - Di 18.08.09 14:18
Bitte mit Finalize(Entry[i]) arbeiten, da das noch ein paar Dinge mehr macht als nur die Länge des Arrays auf 0 zu setzen. Sonst hast Du da u.U. ein Memleak drin.
Aber machst Du nicht einfach folgendes?
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:
| procedure TFrameAnalyzer.Add(opcode: Word; direction: Byte; data: pointer;size:word); var i:Integer; begin if(curEntry=1000) then begin for i := 0 to 500 - 1 do begin Entry[i] := Entry[i+500]; finalize(Entry[i+500]); end;
curEntry:=500; UpdateListView; end;
Entry[curEntry].opcode := opcode; Setlength(Entry[curEntry].data, size); if size>0 then Move(data^, Entry[curEntry].data[0],size); Entry[curEntry].direction := direction; Inc(curEntry); end; |
Flamefire - Di 18.08.09 14:59
ich glaube bei dir ist beim zitieren was schief gegangen ;-)
erstmal: Deine Lösung funktioniert ebenfalls, danke dafür
würde es nur gern verstehen:
1) was macht Finalize?
2) warum Finalize auf entry[i+500]?
wenn das array tatsächlich ein pointer auf die array structur ist, dann würde entry[x]:=entry[y] den inhalt des records kopieren. vermutlich macht der noch intern eine überprüfung bevor das array in x überschrieben wird (der hat ja einen referenzzähler drin. wenn der 0 ist gibt der das array frei, zumindest soweit ich das im debugger gesehen habe)
finalize auf entry[i+500] müsste mir doch dann das array, dess pointer ich nach entry[i] kopiert habe freigeben, wodurch es auf einmal fehlt, oder?
3) warum hat das mit dem Move nicht funktioniert?
PS: So weit ich das gesehen habe, wird finalize von setlength aufgerufen
BenBE - Di 18.08.09 15:29
Flamefire hat folgendes geschrieben : |
| ich glaube bei dir ist beim zitieren was schief gegangen ;-) |
Nope, hatte beim Posten vergessen, überflüssige Teile deines Posts zu eliminieren ;-)
Flamefire hat folgendes geschrieben : |
| erstmal: Deine Lösung funktioniert ebenfalls, danke dafür |
Als ob jemals Source von mir nicht funktioniert hätte :mrgreen:
Flamefire hat folgendes geschrieben : |
würde es nur gern verstehen:
1) was macht Finalize? |
F1, da werden Sie geHOLFEn ;-)
Flamefire hat folgendes geschrieben : |
| 2) warum Finalize auf entry[i+500]? |
Die Zuweisung in der Zeile zuvor erzeugt eine Kopie des Eintrags und inkrementiert somit den Referenzzählers im Array. Um diese Kopie des Arrays freizugeben - also Delphi bescheid zu geben, dass dieser Eintrag gelöscht werden soll - muss der Aufruf dort hin. Danach findet sich an dieser Stelle ein blanker, uninitialisierter Eintrag, den SetLength beim Beschreiben korrekterweise als leer erkennt und somit wieder korrekt Speicher zuordnen kann.
Flamefire hat folgendes geschrieben : |
| wenn das array tatsächlich ein pointer auf die array structur ist, dann würde entry[x]:=entry[y] den inhalt des records kopieren. vermutlich macht der noch intern eine überprüfung bevor das array in x überschrieben wird (der hat ja einen referenzzähler drin. wenn der 0 ist gibt der das array frei, zumindest soweit ich das im debugger gesehen habe) |
Die Zuweisung macht eine ganze Menge Compiler Magic intern. Intern wird für [X] einmal Finalize aufgerufen, danach folgt ein Move, gefolgt von einem Update der Referenzzähler ALLER Member der Struktur, die dies benötigen.
Flamefire hat folgendes geschrieben : |
| finalize auf entry[i+500] müsste mir doch dann das array, dess pointer ich nach entry[i] kopiert habe freigeben, wodurch es auf einmal fehlt, oder? |
Nope. Siehe Erklärung, was diese Zuweisung intern macht ...
Flamefire hat folgendes geschrieben : |
| 3) warum hat das mit dem Move nicht funktioniert? |
Weil Move die Referenzzähler nicht aktualisiert und du damit auf freigegebenen Speicher zugegriffen hast (daher die Exception mit der ungültigen Zeiger-Operation).
Flamefire hat folgendes geschrieben : |
| PS: So weit ich das gesehen habe, wird finalize von setlength aufgerufen |
Korrekt. SetLength macht aber intern noch ne ganze Menge mehr. u.a. eine Initialisierung des zugewiesenen Speichers mit 0-Bytes.
Flamefire - Di 18.08.09 17:06
gut soweit.
nochmal zu dem move:
ich brauche doch den referenzzähler nicht verändern
wenn ich das so mache(was ich zumindest vorhatte):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| a,b:ptr; New(a);New(b); a:=iwas; b:=iwas; Free(a); a:=b; b:=nil; |
dann sollte in der theorie doch alles i.o. sein, oder?
BenBE - Di 18.08.09 17:59
Flamefire hat folgendes geschrieben : |
gut soweit.
nochmal zu dem move:
ich brauche doch den referenzzähler nicht verändern
wenn ich das so mache(was ich zumindest vorhatte):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| a,b:ptr; New(a);New(b); a:=iwas; b:=iwas; Free(a); a:=b; b:=nil; |
dann sollte in der theorie doch alles i.o. sein, oder? |
Du vergisst, dass der Compiler auch noch ein Wörtchen mitzureden hat.Bei deinem ursprünglichen Code sind a[0] und a[500] identisch, d.h. du hast 2! Kopien deines speichers, der Referenzzähler des Arrays ist aber 1. Wenn Du dann mit SetLength in einem der Arrays den Speicher freigibst, was wird dann wohl mit dem Zeiger auf des anderen Eintrags??? Korrekt: Der wird ungültig.
Flamefire - Di 18.08.09 18:18
ich habe aber doch kein a[500] mehr nach:
Delphi-Quelltext
1: 2: 3: 4: 5:
| for i := 0 to 500 - 1 do SetLength(Entry[i].data,0); Move(Entry[500],Entry[0],SizeOf(TEntry)*500); for i := 500 to 1000 - 1 do Entry[i].data:=nil; |
danach habe ich ein ordentliches array in a[0] und ein nil statt eines arrays in 500
BenBE - Di 18.08.09 18:44
In a[0] steht dann aber ein finalisiertes Array, weil der Compiler die Referenz freigegeben hat. nil zuweisen bei einem Array führt implizit ein Finalize aus ...
Flamefire - Di 18.08.09 18:50
geht klar danke
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!