Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Problem beim übergeben von nil
Ruditschka - Mi 25.02.04 23:33
Titel: Problem beim übergeben von nil
Hallo zusammen!
Ich habe folgendes Problem: Wenn ich einer Prozedur als Zeiger den Wert nil übergeben möchte, mekkert Delphi.
Zitat: |
[Fehler] OptionsForm.pas(148): Die Typen der tatsächlichen und formalen var-Parameter müssen übereinstimmen |
Die Fehlermeldungen kommen aber erst, seit ich diese und andere Prozeduren in eine eigene Unit verschoben habe.
Die Prozedur ist folgendermaßen definiert:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| type PBankData = ^TBankData; TBankData = record (...)
procedure BankSort(var a: TBankSet; Lo, Hi: Byte; Vergleich: TBankVergleich; var BankPointer: PBankData); |
Im Programm wird die Prozedur so aufgerufen:
Delphi-Quelltext
1:
| BankSort(BankSet, 0, High(BankSet), bvPosition, nil); |
Bei dem Parameter BankPointer(=nil) mekkert Delphi. Wenn ich folgendes mache, klappt es komischerweise:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type PCurrentBankData: PBankData; begin (...) PCurrentBankData := nil; BankSort(BankSet, 0, High(BankSet), bvPosition, PCurrentBankData); (...) end; |
In der Prozedur wird überprüft, ob BankPointer den Wert nil hat. Trifft dies zu, so wird er ignoriert, ansonsten beim Sortieren entsprechend mit verbogen, so dass er nach dem Sortieren immer noch auf den selben Datensatz zeigt.
Über Hilfe oder Tipps bezügl. Fehlerherkunft (warum???) wäre ich sehr dankbar.
Gruß
Christoph
Motzi - Do 26.02.04 00:23
Also wenn du dir anschaust welche Parameter die Prozedur verlangt ist eigentlich eh schon alles ersichtlich:
Delphi-Quelltext
1:
| procedure BankSort(var a: TBankSet; Lo, Hi: Byte; Vergleich: TBankVergleich; var BankPointer: PBankData); |
Der letzte Parameter ist ein var-Parameter, dh der Compiler übergibt nur eine Referenz die dann in der aufgerufenen Prozedur implizit wieder dereferenziert wird. Da du hier aber ohnehin mit einem Pointer arbeitest frag ich mich ob du überhaupt einen var-Parameter brauchst... aber man kann auch nil-Pointer an einen var-Parameter übergeben und zwar indem man den nil-Pointer dereferenziert und auf den entsprechenden Type castet.. in deinem Fall zB so:
Delphi-Quelltext
1:
| BankSort(BankSet, 0, High(BankSet), bvPosition, PBankData(nil^)); |
Ruditschka - Fr 27.02.04 19:13
Zuerst mal Danke für den Hinweis. Die Version mit dem PBankData(nil^) funktioniert so weit.
Dazu habe ich auch noch zwei Fragen:
1. In der Delphi-Hilfe steht, dass der nil-Pointer zu allen anderen Pointern zuweisungskompatibel ist. Warum ist er das nicht zu PBankData?
2. Wenn der Pointer nicht als var-Parameter übergeben wird und in der Sort-Prozedur beim Sortieren umgebogen wird, müsste doch nach/außerhalb der Prozedur (also im Rest des Programms) der Zeiger immer noch der selbe sein, wie vor dem Prozeduraufruf? Oder habe ich da was Entscheidendes übersehen (wahrscheinlich :) )?
Gruß
Christoph
Delete - Fr 27.02.04 20:09
Zuweisungskompatibel ist er doch, da hat Motzi doch gar nichts anderes behauptet.
Das Problem ist das 'var' vor einem Parameter ja immer heißt, dass der Prozedur eigentlich (Im Hintergrund) ein Pointer auf dieses Objekt übergeben wird.
Die Prozedur erwartet also einen Pointer auf einen Ort, an dem ein Pointer auf dein TBankData steht...
Kann ja sinvoll sein, wenn der Pointer, auf den Verwiesen wird, während der Prozedur verändert werden soll.
Heißt im Klartext:
Du kannst 'nil' übergeben, aber nur indem du eine Pointer-Variable übergibst, in der 'nil' drinsteht.
Motzis Trick mit dem PBankData(nil^) funktioniert zwar, aber ich glaue nicht, dass die Prozedur mit dieser Möglichkeit rechnet, denn der Test auf nil bezieht sich garantiert auf den Wert in der übergebenen Variable und nicht auf den vesteckten Pointer auf diese Variable selber.
Ich gehe mal nicht davon aus, dass du die Prozedur selber geschrieben hast, oder?
Ruditschka - Fr 27.02.04 21:17
Die Prozedur ist teilweise von mir. Es ist eine "aufegbohrte" Quick-Sort Prozedur aus der Schule:
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:
| procedure BankSort(var a: TBankSet; Lo, Hi: Byte; Vergleich: TBankVergleich; var BankPointer: PBankData);
function IsGroesser(var Erster, Zweiter : Byte):boolean; begin case Vergleich of bvNumber : Result := a[Erster].Number > A[Zweiter].Number; bvPosition : Result := a[Erster].Position > A[Zweiter].Position; end; end;
procedure QuickSort(l, r: Byte); var i, j, x: Byte; y: TBankData; begin i := l; j := r; x := ((l+r) DIV 2); repeat while IsGroesser(x, i) do inc(i); while IsGroesser(j, x) do dec(j); if i <= j then begin y := a[i]; a[i] := a[j]; a[j] := y; if (BankPointer <> nil) then if (BankPointer = @a[i]) then BankPointer := @a[j]; inc(i); if j > 0 then dec(j); end; until i>j; if l < j then QuickSort(l, j); if i < r then QuickSort(i, r); end; begin if Length(a) > 0 then QuickSort(Lo,Hi); end; |
Die Abfrage
Delphi-Quelltext
1:
| if (BankPointer <> nil) then |
greift doch auf den Inhalt des Parameters zu?! Also den dahinter liegenden Pointer (PBankData)...
P.S.: Irgendwie erschließt sich mir der Sinn der Zeile "if BankPointer <> nil then" beim zweiten Durchlesen nicht mehr so ganz. Wahrscheinlich war die Überprüfung auf nil schneller als der Vergleich mit einer Adresse, die sich ständig ändern kann... - vielleicht auch nicht...
Delete - Mo 01.03.04 16:55
Ruditschka hat folgendes geschrieben: |
Die Abfrage
Delphi-Quelltext 1:
| if (BankPointer <> nil) then |
greift doch auf den Inhalt des Parameters zu?! Also den dahinter liegenden Pointer (PBankData)...
|
Also intern greift sie erstmal auf den übergebenen Pointer zu, der auf den Speicherort von PBankData verweisen soll. Erst dann greift sie auf diesen Ort im Speicher zu, liest den dort gespeicherten Pointer aus und vergleicht ihn mit nil.
Ich weiß jetzt halt nicht, was passieren würde, wenn dieser erste Pointer schon nil ist, denn dann würde die Abfrage wahrscheinlich probieren, nil auszulesen und eine AccessViolation wäre die Folge.
Loryn - Mo 01.03.04 19:51
Titel: Re: Problem beim übergeben von nil
Ruditschka hat folgendes geschrieben: |
Hallo zusammen!
Ich habe folgendes Problem: Wenn ich einer Prozedur als Zeiger den Wert nil übergeben möchte, mekkert Delphi.
Zitat: | [Fehler] OptionsForm.pas(148): Die Typen der tatsächlichen und formalen var-Parameter müssen übereinstimmen |
Die Fehlermeldungen kommen aber erst, seit ich diese und andere Prozeduren in eine eigene Unit verschoben habe.
Die Prozedur ist folgendermaßen definiert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| type PBankData = ^TBankData; TBankData = record (...)
procedure BankSort(var a: TBankSet; Lo, Hi: Byte; Vergleich: TBankVergleich; var BankPointer: PBankData); |
|
Ich habe noch eine andere Erklärung:
Wie bereits erwähnt wurde, ist BankPointer als
var Parameter definiert. Dazu muss aber auch eine Variable an BankSort übergeben werden, damit der evt. veränderte Wert für BankPointer an die aufrufende Stelle zurückgegeben werden kann. Meines Erachtens nach ist
nil aber eine Konstante und keine Variable, weswegen der Compiler (zu Recht) meckert.
Gruß Loryn
Delete - Mo 01.03.04 21:37
Und was ist jetzt an der Erklärung anders?
Motzi - Mo 01.03.04 22:32
@Loryn: die Erklärung ist im Prinzip richtig, aber wie man das umgeht hab ich ja bereits gezeigt...
Ich benutze diesen kleinen Trick im Prinzip aber nur um nil an API-Funktionen zu übergeben die zwar mit einem nil-Pointer umgehen können, aber von Borland mit einem var-Parameter importiert wurden.
In diesem Fall erzeugt er wohl einen EAccessViolation, da der var-Parameter zwar als Pointer übergeben wird aber automatisch implizit dereferenziert wird. Falls also in diesem Fall wirklich ein Pointer auf einen Pointer gebraucht wird (was es ja praktisch schon ist) würde ich dies mit einem eigenen Typ machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| type PPBankData = ^PBankData; PBankData = ^TBankData; TBankData = record (...)
procedure BankSort(var a: TBankSet; Lo, Hi: Byte; Vergleich: TBankVergleich; BankPointer: PPBankData); |
Dann kann man auch ohne Probleme (und ohne Typecast) nil übergeben und innerhalb der Funktion den Pointer auch auf nil überprüfen...
Delete - Mo 01.03.04 22:38
Also ich hatte Funktionen, die dann gestreikt haben... was macht der Compiler denn da bitte intern?
wenn ein Pointer als var übergeben wird, wird doch eine Referenz auf den Pointer übergeben, wie übergibt man also bitte eine Referenz auf nil?
Motzi - Mo 01.03.04 23:07
Brainiac hat folgendes geschrieben: |
wenn ein Pointer als var übergeben wird, wird doch eine Referenz auf den Pointer übergeben, wie übergibt man also bitte eine Referenz auf nil? |
Gar nicht.. der Typecast sorgt nicht dafür, dass eine Referenz auf nil übergeben wird, sondern dass eine "nil-Referenz" übergeben wird. Daher auch das Problem, dass wenn man innerhalb der Funktion auf den Parameter zugreift die Referenz intern automatisch dereferenziert wird, aber eine Dereferenzierung von nil endet gewöhnlicherweise in einem EAccessViolation...
Daher auch mein Vorschlag mit dem neuen Typen als Pointer auf den Pointer...
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 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!