Seite 1 von 1

(Pointer) Nie Pointer zu lokalen Variablen zurückgeben!

Verfasst: 22.05.2005 17:18
von NicTheQuick
Ich habe das letztens in einem Code von Deeem2031 entdeckt. Er hat Pointer zu lokalen Variablen einer Procedure zurückgegeben, was in vielen Fällen zunächst mal kein Problem darstellt, wie man hier sieht:

Code: Alles auswählen

Procedure.l pGetValue()
  Protected a.l
  a = 123
  ProcedureReturn @a
EndProcedure
*a.LONG = pGetValue()
Debug *a\l
Fügt man jetzt allerdings noch eine Zeile an, die neuen Speicher allokiert, sieht es so aus:

Code: Alles auswählen

Procedure.l pGetValue()
  Protected a.l
  a = 123
  ProcedureReturn @a
EndProcedure
*a.LONG = pGetValue()
Debug *a\l
Dim b.l(1)
Debug *a\l
Man könnte jetzt natürlich direkt den Wert der Procedure mit [c]PeekL()[/c] auslesen und in eine globale Variable lesen, was zunächst funktioniert:

Code: Alles auswählen

Procedure.l pGetValue()
  Protected a.l
  a = 123
  ProcedureReturn @a
EndProcedure
a.l = PeekL(pGetValue())
Debug a
Dim b.l(1)
Debug a
Aber in Zusammenhang mit Threads kann es durchaus passieren, dass in dieser einen Zeile, die ja trotzdem noch aus mehreren ASM-Befehlen besteht, die Speicherstelle, der die lokale Variable zugeordnet war, geändert wird, bevor [c]PeekL()[/c] den Wert ausgelesen und zurückgegeben hat.
Leider lässt sich dafür kein Code erfinden, der nach wenigen Sekunden Laufzeit direkt Fehler ausgibt. Aber im Grunde würde das dann so aussehen:

Code: Alles auswählen

Procedure Thread(a.l)
  Repeat
    Dim b.l(Random(10))
  ForEver
EndProcedure

Procedure.l pGetValue()
  Protected a.l
  a = 123
  ProcedureReturn @a
EndProcedure

hT.l = CreateThread(@Thread(), 0)

For a.l = 1 To 1000
  a.l = PeekL(pGetValue())
  If a <> 123 : Debug a : EndIf
Next

KillThread(hT)

Verfasst: 23.05.2005 08:51
von dige
Wenn man Variablen-Inhalte aus einer Procedure verwenden möchte,
muss man diese mit Static deklarieren, sonst wird dieser Speicherplatz
natürlich wieder freigegeben ...

Code: Alles auswählen

Procedure.l pGetValue()
  Static a.l
  a = 123
  ProcedureReturn @a
EndProcedure
*a.LONG = pGetValue()
Debug *a\l
Dim b.l(1)
Debug *a\l

Verfasst: 23.05.2005 14:44
von Ynnus
Ist doch eigentlich allgemein bekannt, oder nicht? Ich meine, in C ist es genauso, dass Variablen nur innerhalb des Scopes gültig sind, also nur innerhalb des Blocks der geschweiften Klammern. Und in Funktionen gilt dies auch, dass die Variablen eben danach wieder freigegeben werden. Demnach sollte man keine Pointer auf lokale Variablen legen, wenn man diese Pointer nicht danach auch wieder NULL setzt.

Verfasst: 23.05.2005 15:24
von NicTheQuick
Klar ist das klar. Aber anscheinend nicht jedem. Und den Anfängern sicherlich erstmal gar nicht. (Wobei die wahrscheinlich eher weniger mit Pointern arbeiten).

@dige: Deine Version ist jetzt zwar kein illegaler Speicherzugriff mehr, der in meinem Beispiel nur zufällig funktioniert, aber verwenden darf man diese Methode nur dann, wenn die Funktion nicht noch von anderen Threads verwendet wird, weil dort dann diese Static-Variable auch wieder verändert werden kann.

Wesentlich sicherer und eleganter ist dafür die folgende Methode:

Code: Alles auswählen

Procedure.l pGetValue(*get.LONG)
  Protected a.l
  a = 123
  If *get 
    *get\l = a
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

a.l
pGetValue(@a)
Debug a