Lebenszeit und Sichtbarkeit der Variablen-Klassen

Aus Wiki des Deutschsprachige Xbaseentwickler e. V.
Zur Navigation springen Zur Suche springen

Übersicht der Speicherklassen

Speicherklasse erzeugt durch zerstört durch
PUBLIC PUBLIC RELEASE
PRIVATE PRIVATE / PARAMETERS / Zuweisung RELEASE
STATIC STATIC nicht möglich
LOCAL LOCAL / () nicht möglich
FIELD FIELD nicht möglich

Lebensdauer

dynamische Variablen

PUBLIC

Eine PUBLIC Variable existert ab dem Moment, in dem sie mittels PUBLIC deklariert wird.

Die Lebenszeit endet in dem Moment, in dem das Programm endet, oder die Variable explizit durch eine RELEASE Anweisung aus dem Speicher entfernt wird.


PRIVATE

Eine PRIVATE Variable existiert ab dem Moment, in dem sie mittels PRIVATE deklariert wird, oder indem einer nicht existierenden Variablen ein Wert zugewiesen wird.

Die Lebenszeit einer PRIVATE Variablen endet in dem Moment, indem die Callstack-Ebene verlassen wird, in der die Variable erzeugt wurde:

Main()
+-> Func1()
    +-> Func2()
          PRIVATE cHugo

In dem Moment, indem Func2() endet, wird cHugo aus dem Speicher entfernt.


MEMVAR

Die Anweisung MEMVAR nimmt eine Sonderstellung ein. Der Compiler-Schalter /w warnt, wenn nicht deklarierte Variablen im Programmcode verwendet werden. Da PUBLIC und PRIVATE (sowie PARAMETER) ausführbare Anweisungen sind, erkennt der Compiler Variablen, die auf diesem Wege deklariert wurden, nicht.

Die Anweisung MEMVAR deklariert Variablen als dynamische Variablen. Wird ein entsprechender Variablen-Name im Programmcode gefunden, versieht der Compiler die Variable implizit mit dem M-> Operator, und die Variable wird als ordnungsgemäss deklariert betrachtet, d.h. /w erzeugt für diese Variable keine Warnung.


PARAMETER

Es gibt zwei Wege, die Parameter zu definieren, die an eine Funktion übergeben werden. Abhängig von der Art, wie die Parameter definiert werden, ergibt sich die Speicherklasse der Variablen:


(Parameter, Parameter, Parameter, ...)

Parameter, die hinter dem Namen der Funktion in runden Klammern angegeben werden, legt der Compiler als LOCAL Variablen an.

PARAMETER parameter, parameter, parameter, ...

Die ausführbare Anweisung PARAMETER erzeugt Variablen der Speicherklasse PRIVATE.

Da die Anweisung ausführbar ist, darf sie erst nach der Deklaration aller Variablen der Speicherklassen LOCAL und STATIC im Programmcode erscheinen.


lexikalische Variablen

STATIC

Eine STATIC Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext sichtbar.

Der Kontext einer STATIC Variablen ist entweder die Funktion, in der sie definiert ist - dann ist die STATIC Variable nur in dieser Funktion sichtbar. Alternativ kann eine STATIC Variable in einer Programmquelle ausserhalb einer Funktion definiert werden, dann ist sie in allen Funktionen sichtbar, die in dieser Programmquelle definiert sind.

LOCAL

Eine LOCAL Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext, d.h. der Funktion in der sie definiert ist, sichtbar.

Parameter

Die an eine Funktion übergebenen Parameter können in () hinter dem Funktionsnamen deklariert werden. In diesem Fall werden die Variablen als LOCAL Variablen erzeugt.

FIELD

Eine FIELD Variable existiert ab Programmstart, sie ist jedoch nur in ihrem jeweiligen Kontext sichtbar.

Der Kontext einer Field Variablen ist entweder die Funktion, in der sie definiert ist - dann ist die FIELD Variable nur in dieser Funktion sichtbar. Alternativ kann eine FIELD Variable in einer Programmquelle ausserhalb einer Funktion definiert werden, dann ist sie in allen Funktionen sichtbar, die in dieser Programmquelle definiert sind.

Sichtbarkeit

dynamische Variablen

PUBLIC

Ab ihrer Erzeugung ist eine PUBLIC Variable im ganzen Programm sichtbar.


PRIVATE

Ab ihrer Erzeugung ist eine PRIVATE Variable auf ihrer Callstack-Ebene sichtbar, sowie in allen Funktionen, die nach ihrer Erzeugung und vor dem Ende der Funktion, in der sie erzeugt wurde, aufgerufen werden:

Main()
+-> Func1()
    +-> Func2()
          PRIVATE cHugo
        +-> Func3()
        +-> Func4()

Die Variable cHugo ist in Func2(), Func3() und Func4() sichtbar.


lexikalische Variablen

Lexikalische Variablen werden durch den Compiler angelegt und sind damit ab Programmstart verfügbar, wenn sich das Programm im entsprechenden Kontext befindet.


STATIC (definiert vor der ersten Funktion, Prozedur, Klasse)

STATIC, die in einer Programmdatei ganz zu Anfang definiert werden, sind in dieser Programmdatei sichtbar.


STATIC (definiert innerhalb einer Funktion, Prozedur, Klasse)

STATIC, die innerhalb einer Funktion etc. definiert werden, sind auch nur in dieser Funktion sichtbar. Im Gegensatz zu LOCAL Variablen behalten sie ihren Wert aber bei, wenn die Funktion verlassen wird.


LOCAL

LOCAL Variablen sind nur in der Funktion sichtbar, in der sie definiert sind. Sie sind nicht in aufgerufenen Funktionen sichtbar (man kann sie zwar als Parameter übergeben, aber das hat nichts mit Sichtbarkeit der ursprünglichen Variablen zu tun), und mit dem Verlassen der Funktion, in der sie definiert wurden, verlieren sie den ihnen zugewiesenen Wert.

Besonderheiten

MEMVAR

Die Deklaration MEMVAR dient dazu, den Compiler zu informieren, dass es dynamische Variablen gibt, damit keine Warnungen über nicht definierte Variablen gibt, wenn eine Quelle mit dem Schalter /w compiliert wird und die betreffende(n) Variable(n) durch MEMVAR deklariert wurde(n).

PARAMETERS

PARAMETERS ist eine ausführbare Anweisung und kann daher erst nach der Deklaration von LOCAL bzw . STATIC Variablen erfolgen.

Die Anweisung PARAMETERS definiert Parameter, die an eine Funktion übergeben werden. Durch PARAMETERS erzeugte Variablen haben den Speichertyp PRIVATE.

Überlagerung

Was passiert, wenn dynamische und lexikalische Variablen mit gleichem Namen aufeinander treffen wie in diesem Beispiel:

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo
  cHugo := 88
  ? cHugo
RETURN (.T.)

Was wird in der letzten Zeile von Func1() ausgegeben?

88

Um diese Antwort zu verstehen, muss man sich überlegen, was der Compiler tut. In der Funktion Func1() wird eine lexikalische Variable cHugo definiert. Der Compiler nimmt nun alle Bezüge auf diese Variable und mappt sie auf den gleichen Speicherbereich. Danach verbleibt in Func1() kein Verweis mehr auf cHugo.

Zur Laufzeit ist cHugo in Func1() nicht sichtbar.

Jetzt ändern wir das Beispiel ein wenig ab:

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo, bBlock
  bBlock := "{|| cHugo}"
  bBlock := &bBlock
  cHugo := 88
  ? Eval(bBlock)
RETURN (.T.)

Diesmal lautet das Ergebnis

Hugo

Warum? Der Codeblock wird zur Laufzeit durch die Laufzeitumgebung compiliert: zuerst erzeugen wir ein Literal, das danach compiliert wird. Zur Laufzeit gibt es keinen Bezug zu der LOCAL cHugo mehr, so dass beim Eval(bBlock) im dynamischen Variablenverzeichnis nach cHugo gesucht wird, und auch gefunden wird. Und damit wird dann der Wert der PRIVATE Variablen cHugo ausgegeben.

FUNCTION Main()
  cHugo := "Hugo"
  Func1()
  ? cHugo
RETURN (.T.)
FUNCTION Func1()
  Local cHugo, bBlock
  bBlock := {|| cHugo}
  cHugo := 88
  ? Eval(bBlock)
RETURN (.T.)

Und jetzt?

88

Da der Codeblock zur Compile-Zeit definiert ist, kann ihn der Compiler übersetzen und tut dies auch, indem die Referenz auf cHugo lexikalisch interpretiert wird.

Das Verhalten gilt unabhängig davon, ob die dynamische Variable als PRIVATE oder PUBLIC definiert ist, und es ist auch egal, ob die lexikalische Variable LOCAL oder STATIC ist.

Viele Probleme und Missverständnisse beim Einsatz von dynamischen und lexikalischen Variablen haben an dieser Stelle ihre Ursache.

der M-> (bzw. MEMVAR->) Operator

Wenn explizit auf eine dynamische Variable zugegriffen werden soll, kann der M-> bzw. MEMVAR-> Operator verwendet werden. Dieser weist den Compiler an, für diesen Zugriff explizit eine dynamische Variable zu verwenden und nicht eine eventuell existierende lexikalische Variable:

FUNCTION MAIN
   PRIVATE cHugo
   TueIrgendEtwas()
RETURN(.T.)
FUNCTION TueIrgendEtwas()
   LOCAL cHugo
   cHugo := 123
   M->cHugo := "Heute"
   ? cHugo
   ? M->cHugo
   ? MEMVAR->cHugo
   WAIT
RETURN(.T.)

Dieses kleine Programm liefert diesen Output:

     123
Heute
Heute
Press any key to continue ...