Bit-Flags erklärt

  • Über Bit-Flags lassen sich mehrere aufeinanderfolgende Variablen des Typs Boolean in einer einzigen Variable ohne die Verwendung von Arrays speichern.

    Angenommen, wir möchten die Zustände AUF und ZU von mehreren Türen übertragen. Für einen Bus mit vier Türen mag es noch praktikabel sein, für jede Tür eine eigene Variable zu pflegen. Für eine U-Bahn mit sechs Türen pro Seite und Einheit wird das dann schon deutlich unübersichtlicher...


    Aus diesem Grunde werden manche Variablen als sogenannte Bit-Flags gespeichert und übertragen. Die so gekennzeichneten Variablen sind zum Beispiel vom Typ Integer: ein einziger Zahlenwert codiert dabei den Zustand aller - in diesem Fall - Türen.


    Nehmen wir an, wir haben vier Türen in unserem Fahrzeug. Jede dieser Türen kann exakt zwei Zustände haben, AUF und ZU, wobei das in Bits ausgedrückt 0 für ZU und 1 für AUF bedeutet. Die Türen heißen Tür0, Tür1, Tür2 und Tür3 - und die Türen 1 und 2 sind offen. In Bits ausgedrückt also:

    Code
    1. Tür0 Tür1 Tür2 Tür3 |Bezeichnung
    2. ----------------------
    3. 0 1 1 0 |Zustand


    Nun ordnen wir jede Tür einer festen Zweier-Potenz zu (zwei, weil es zwei Zustände gibt). Der Übersicht halber beginnen wir hier mit 20, aber es dürfte theoretisch auch jede andere Zweier-Potenz verwendet werden. Übrigens: Es liegt der Gedanke nahe, hier für Systeme mit mehr als zwei Zuständen einfach eine andere Basis zu nehmen. Diese Anleitung verwendet aber später die dem Pascal-Script (also unserem Script-System!) angeborene Funktion "Bitweises AND", und die kann eben nur mit zwei Zuständen umgehen.

    Code
    1. Tür0 Tür1 Tür2 Tür3 |Bezeichnung
    2. 2^0 2^1 2^2 2^3 |Zweier-Potenz
    3. 1 2 4 8 |Ergebnis
    4. ----------------------
    5. 0 1 1 0 |Zustand


    Diese Zuordnung wird in Konstanten gespeichert, die wir später noch benötigen.

    Code
    1. const
    2. Tuer0 = 1;
    3. Tuer1 = 2;
    4. Tuer2 = 4;
    5. Tuer3 = 8;


    Um unsere Codierungs-Variable zu schreiben, bauen wir jetzt ein zugegebenermaßen recht umfangreiches Konstrukt, das sich aber spätestens bei der Übertragung wieder auszahlen wird, versprochen! Die zugehörigen Variablen in LOTUS heißen RC_DoorsOpen_Right und RC_DoorsOpen_Left. Wir verwenden hier jetzt RC_DoorsOpen_Right.

    Code
    1. RC_DoorsOpen_Right := 0;
    2. if <Tür0 offen> then //dieser Zustand kann sich je nach Script-Autor unterschiedlich
    3. //darstellen und ist daher hier nur ein Platzhalter
    4. RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer0; //Tuer0 ist hier unsere Konstante vom vorigen Schritt!
    5. if <Tür1 offen> then
    6. RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer1;
    7. if <Tür2 offen> then
    8. RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer2;
    9. if <Tür3 offen> then
    10. RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer3;


    Da in unserem Script derzeit die Türen 1 und 2 offen sind, welche die Werte 2 und 4 haben, hat unser RC_DoorsOpen_Right nun den Wert 2 + 4 = 6. Dieser Wert wird schlussendlich übertragen und vom Empfänger nun wiederum im Script interpretiert. Um den Zustand jeder einzelnen Tür des Sender-Fahrzeuges zu überprüfen, nehmen wir die an uns übertragene Codierungs-Variable und überprüfen sie folgendermaßen:

    Code
    1. <Tür0 offen> := (RC_DoorsOpen_Right and Tuer0) > 0; //Auch hier ist dieser Zustand wieder ein Platzhalter und Tuer0 unsere Konstante von weiter oben.
    2. <Tür1 offen> := (RC_DoorsOpen_Right and Tuer1) > 0;
    3. <Tür2 offen> := (RC_DoorsOpen_Right and Tuer2) > 0;
    4. <Tür3 offen> := (RC_DoorsOpen_Right and Tuer3) > 0;


    Warum funktioniert der Zauber? Das sei hier noch kurz erklärt. Dazu müssen wir uns die Funktion bitweises AND genauer anschauen. Voraussetzung für das Verständnis ist, dass Euch klar ist, dass der Computer unseren Zahlen (und auch alles andere) nicht als Zahlen 12345 speichert, sondern als Bits. Unsere oben erwähnte 6 im Gesamtzustand steht im Arbeitsspeicher also als 0 1 1 0, was so viel bedeutet wie 0 + 2 + 4 + 0 = 6. Und unsere Konstante 4 der Tür3 steht im Arbeitsspeicher als 0 0 1 0. Damit ergeben sich also die folgenden Tabellen zur Erläuterung:

    Code
    1. 1 2 4 8 |Zweier-Potenzen unserer Türen
    2. ----------------------
    3. 0 1 1 0 |Zustand unserer Türen (binär geschrieben, wie im Arbeitsspeicher)
    4. 0 0 1 0 |Überprüfung auf Zustand von Tür2 (binär geschrieben) mittels AND
    5. ----------------------
    6. 0 0 1 0 |Ergebnis (binär geschrieben), hier = 4

    oder

    Code
    1. 1 2 4 8 |Zweier-Potenzen unserer Türen
    2. ----------------------
    3. 0 1 1 0 |Zustand unserer Türen (binär geschrieben, wie im Arbeitsspeicher)
    4. 0 0 0 1 |Überprüfung auf Zustand von Tür3 (binär geschrieben) mittels AND
    5. ----------------------
    6. 0 0 0 0 |Ergebnis (binär geschrieben), hier = 0

    Im Script sehen diese Zeilen übrigens jeweils folgendermaßen aus:

    Code
    1. Tuer0 = 1; //Zweier-Potenzen unserer Türen
    2. ...
    3. if <Tür0 offen> then RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer0; //Zustand unserer Türen
    4. ...
    5. <Tür0 offen> := (RC_DoorsOpen_Right and Tuer0) > 0; //Überprüfung auf Zustand von Tür0 mittels AND
    6. ...

    Wir verknüpfen also die Gesamtzustands-Variable der Türen RC_DoorsOpen_Right mittels AND mit der Zweier-Potenz der Tür, die uns interessiert. Das bitweise AND ergibt 0 für die Verknüpfung von 0 und 0 sowie die Verknüpfung von 0 und 1 und ergibt wiederum 1 für die Verknüpfung von 1 und 1. Im obigen Code ergibt eine offene Tür nach der Verknüpfung einen Wert > 0, nämlich genaugenommen immer die Zweier-Potenz der betrachteten Tür, sofern diese Tür in der Gesamtzustands-Variable beim Addieren eine Rolle gespielt hatte. Hat sie das nicht (weil sie 0 war und daher nicht addiert wurde, siehe if <Tür0 offen> then RC_DoorsOpen_Right := RC_DoorsOpen_Right + Tuer0;), ergibt das bitweise AND auch 0. Aus diesem Grund können wir mit der Abfrage, ob Gesamtzustand AND Konstante der betrachteten Tür > 0 ist, herausfinden, ob die Tür geöffnet ist oder nicht.