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

Hier kannst du häufig gestellte Fragen/Antworten und Tutorials lesen und schreiben.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

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

Beitrag 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)
Zuletzt geändert von NicTheQuick am 23.05.2005 15:41, insgesamt 1-mal geändert.
Bild
Benutzeravatar
dige
Beiträge: 1182
Registriert: 08.09.2004 08:53

Beitrag 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
"Papa, mein Wecker funktioniert nicht! Der weckert immer zu früh."
Benutzeravatar
Ynnus
Beiträge: 855
Registriert: 29.08.2004 01:37
Kontaktdaten:

Beitrag 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.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Beitrag 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
Zuletzt geändert von NicTheQuick am 23.05.2005 15:43, insgesamt 1-mal geändert.
Bild
Antworten