Variable

Aus C64-Wiki
Zur Navigation springenZur Suche springen

Eine Variable ist - genauso wie in der Mathematik - ein Platzhalter in Programmen für beliebige Zahlen und Zeichen. Beim Umgang mit ihr in der Programmierung gibt es allerdings einiges zu beachten, worauf dieser Artikel detailliert eingeht.

Einleitung[Bearbeiten | Quelltext bearbeiten]

Programme dienen in der Regel dazu, Daten einzulesen, zu verarbeiten und wieder auszugeben. Dies sind entweder konstante Werte (z.B. aus DATA-Zeilen oder per INPUT# bzw. GET# eingelesene Werte aus Dateien) oder veränderliche Werte, z.B. durch Berechnung erzeugte oder durch Eingabe (INPUT, GET) im Eingabemodus des Programms erhaltene. Damit Programme mit diesen Daten arbeiten können, werden sie mittels Variablen temporär im Speicher des Rechners abgelegt. Eine Variable dient in diesem Sinn als Platzhalter für die gespeicherten Werte. Das Programm kann jederzeit über den Variablennamen auf die nunmehr vorgehaltenen Daten zugreifen. Dabei muss der Programmierer berücksichtigen, für welche Art von Daten er welche Variablentypen benutzt (siehe unten).

Beispiel: Werden häufig wiederkehrende Zahlenwerte, z.B. 53280 (die Speicheradresse für die Bildschirmrahmenfarbe) in einer Variablen "A" gespeichert, so kann dieser Zahlenwert im weiteren Programm mit "A" jederzeit benutzt werden. Dies hat für den Programmierer den Vorteil, dass es weniger Tipparbeit gibt (und damit auch weniger Tippfehler) und außerdem spart die Verwendung von Variablen mit kurzem Namen wertvollen BASIC-Speicherplatz für das Programm ein.

Variablen in BASIC V2[Bearbeiten | Quelltext bearbeiten]

Folgende Grundvoraussetzungen gelten beim BASIC der Version 2, wie es beim C64 verwendet wird.

Variablennamen[Bearbeiten | Quelltext bearbeiten]

  • Variablennamen müssen mit einem Buchstaben (A-Z) anfangen. Nachfolgend können Buchstaben und Zahlen (0-9) benutzt werden.
  • Variablennamen können nahezu beliebig lang gewählt werden, jedoch sind nur die ersten beiden Zeichen für den BASIC-Interpreter signifikant. Beispiel: Die beiden Variablen A und AA sind zwei unterschiedliche Variablen mit potenziell unterschiedlichen Werten. ABC und ABD hingegen sind identisch mit der Variablen AB.
  • Zur Kennzeichnung des Variablentyps werden die Sonderzeichen "%" (für ganzzahlige Variablen) und "$" (für Zeichenkettenvariablen) an den Namen der Variable angehängt.
    • BASIC V2 unterstützt drei verschiedene Variablentypen:
      • Gleitpunkt- oder Fließkommavariablen (Real-Variablen), keine Kennzeichnung, Beispiel A=1.234
      • Ganzzahlige Variablen (Integer-Variablen), Kennzeichnung durch %, Beispiel AB%=1000
      • Zeichenkettenvariablen (String-Variablen), Kennzeichnung durch $, Beispiel BA$="TEXT"
    • Die Typinformation wird zur Unterscheidung zwischen Variablen herangezogen. Somit sind die Variablen AB, AB% und AB$ voneinander unabhängig. Das gilt auch für Felder: A(1) ist von A unabhängig.
  • Normalerweise ist es gute Praxis, Variablennamen "sprechend" zu wählen, also so, dass der Name auf die Verwendung der Variablen hindeutet, z.B. BGCOLADR = 53281 für die Adresse der Bildschirmhintergrundfarbe. Zu beachten ist, dass nur die ersten beiden Stellen des Namens signifikant sind, und dass die Benutzung von BASIC-Schlüsselwörtern (Auflistung siehe hier) als Variablennamen oder als Bestandteil davon nicht erlaubt ist. So führen z.B. Variablennamen wie SINUS oder ERSTANWAHL zur BASIC-Fehlermeldung ?SYNTAX ERROR, da sie die BASIC-Schlüsselwörter SIN bzw. TAN enthalten. Die Variablennamen ST (STATUS), TI (TIME) und TI$ (TIME$) benutzt der Interpreter bereits als Systemvariablen, was zu Überraschungen führen kann, wenn diese von einem Programmierer als "normale" Variablen verwendet werden sollen. Weiterhin darf auch FN als Variablenname nicht benutzt werden, da dies das Schlüsselwort für die Funktion FN ist.
  • Da der BASIC-Interpreter Leerzeichen außerhalb von Anführungszeichen ignoriert, dürfen Variablennamen von Leerzeichen durchsetzt sein. Das ist mehr ein Kuriosum als etwas, das mit einem besonderen Nutzen verbunden wäre, abgesehen von der Verwendung solcher Variablennamen, die eigentlich einem Schlüsselwort entsprechen und deren Nutzung ohne Leerzeichen sonst nicht möglich wäre:
F N 1 = 123 : PRINT F N 1
Dies entspricht tatsächlich der Variable mit Namen FN (auf die zwei ersten, signifikanten Zeichen beschränkt).

Zur Fehlervermeidung und einfacheren Fehlersuche (Debugging) ist es sinnvoll, nur höchstens zweibuchstabige Variablennamen zu verwenden.

Beispiel für signifikante Stellen bei Variablennamen:

10 A$="1"
20 AA$="2"
30 AAA$="3"
40 PRINT A$, AA$, AAA$

führt zur Bildschirmausgabe

 1     3     3


Die mangelnde Unterscheidbarkeit von langen Variablennamen gilt auch für Systemvariablen. Beispielsweise sprechen die Varianten TIME, TICKER oder TIMES allesamt die gleiche Systemvariable 'TI' an. Der Gebrauch der Namen STATUS, TIME und TIME$ sind jedoch die übliche Konvention, die man eines einheitlichen Programmbildes wegen beibehalten sollte.

Zuweisung bzw. Ein-/Ausgabe[Bearbeiten | Quelltext bearbeiten]

Die Wertzuweisung an eine Variable kann durch verschiedene Methoden erfolgen:

  • Vorschriftsmäßig mit dem Befehl LET, z.B. so: LET A = 123.
  • Kürzer ohne den Zuweisungsbefehl LET, also so: A = 123. Beide Schreibweisen sind syntaktisch gleichwertig.
  • Auslesen von Werten aus DATA-Zeilen, die im Programmkörper selbst eingebettet sind, mit dem BASIC-Befehl READ.
  • Auslesen von Werten aus Dateien auf Band oder Disk mit den Befehlen GET# oder INPUT#.
  • Auslesen von Werten aus Speicherzellen mit PEEK.
  • Eingabe von Werten über die Tastatur während des Programmlaufs mit INPUT oder GET.

Die Ausgabe einer Variablen erfolgt ebenfalls durch verschiedene Methoden:

  • Die einfachste ist das Anzeigen des Variableninhalts auf dem Bildschirm mit dem BASIC-Befehl PRINT.
  • Per Zuweisung an eine andere Variable, evtl. bei gleichzeitiger Verarbeitung, z.B. B = A + 1.
  • Das Ausdrucken der Werte auf Papier mit dem BASIC-Befehl PRINT#.
  • Das Schreiben der Variablenwerte in eine Datei, ebenfalls mit PRINT#. Die Werte liegen in der Datei im Klartext vor (so wie ausgedruckt).

Erfolgen weitere Zuweisungen an die gleiche Variable in (logisch) nachfolgenden Programmzeilen, so wird der bisherige Variableninhalt durch den neuen überschrieben.

Wenn eine Variable ohne ausdrückliche Wertzuweisung erstmalig verwendet wird, legt der BASIC-Interpreter automatisch die entsprechende Speicherstelle für die Variable an. Bei Zahlenvariablen weist er ihr dann den Wert 0 zu. Eine Zeichenkettenvariable erhält eine leere Zeichenkette (Leerstring) als Wert. Die Variablen werden in der Reihenfolge ihrer ersten Verwendung im Speicher abgelegt. Bei der Verarbeitung und Ausgabe durchsucht der Interpreter dann diesen Speicherbereich von vorne nach hinten nach der jeweiligen Variablen. Hinten liegende werden dementsprechend zuletzt gefunden. Häufig verwendete Variablen sollten daher als (logisch) erste im Programm definiert werden (siehe auch weiter unten), um das Laufzeitverhalten des Programms zu verbessern.

Fließkomma-/Integerzahlen[Bearbeiten | Quelltext bearbeiten]

Der Unterschied zwischen einer Zahl im Fließkommaformat (auch: Gleitpunkt- oder Realzahl) und einer Integerzahl (auch: Ganzzahl) ist, dass eine Fließkommazahl Nachkommastellen besitzen kann und einen größeren Wertebereich hat. Ganzzahlvariablen werden durch ein abschließendes "%" im Namen gekennzeichnet (z.B. "K%"). Fließkommavariablen weisen keine besondere Kennzeichnung im Namen auf (z.B. "A" oder "BACKGROUND" usw.)

Bei der Ausgabe (auf den Bildschirm oder in eine Datei) werden bei Fließkommazahlen höchstens 10 signifikante Zeichen dargestellt, einschließlich dem Vorkommaanteil der Zahl und dem Dezimalpunkt. Weist die zugrunde liegende Zahl mehr Stellen auf, schaltet der Rechner um in die Exponentialschreibweise (verdeutlicht durch das Zeichen "e" für "Exponent" in der Bedeutung "mal 10 hoch").

Bei Ganzzahlen werden eventuelle Nachkommaanteile ohne weitere Behandlung einfach abgeschnitten, genau wie bei der BASIC-Funktion INT (keine echte Rundung).

Beispiel für die Darstellungsweise von Integer- und Fließkommazahlen:

10 I=123.123456089: PRINT I
20 E%=I: PRINT E%
30 I=0.0000000000000123: PRINT I
40 I=12345678901: PRINT I

Bildschirmausgabe:
123.123456
123
1.23e-14
1.23456789e+10

Zeichenketten/Strings[Bearbeiten | Quelltext bearbeiten]

Variablen für Zeichenketten dürfen Zeichen des gesamten Zeichensatzes des C64 enthalten. Die Länge eines solchen Strings darf zwischen 0 (Leerstring, Zeichenkette ohne Inhalt) und 255 variieren. Das Typkennzeichen von Strings im Namen ist "$", wie z.B. bei "A$" oder "BACKGROUND$".

Die nicht von der Tastatur aus produzierbaren Zeichen, z.B. die Steuerzeichen (mit einem ASCII-Code zwischen 0 und 32 sowie 128 und 160) können über die BASIC-Funktion CHR$ in Zeichenketten eingebaut werden.

Beispiele: Soll das Anführungszeichen ("), das ja im Programmtext Strings umschließt und kenntlich macht, innerhalb eines Strings vorkommen, muss es als Code CHR$(34) mit dem Rest der Zeichenkette verbunden werden. Auch das Steuerzeichen für die Taste <RETURN> (Code 13) kann über CHR$(..) in Strings genutzt werden.

10 A$ = "Hier das Anfuehrungszeichen: " + CHR$(34)
20 B$ = "Und hier eine Zeile tiefer durch das RETURN-Steuerzeichen"+ CHR$(13)
30 PRINT "1. Teil: "; A$
40 PRINT "2. Teil: "; B$; A$

Bildschirmausgabe:
1. Teil: Hier das Anfuehrungszeichen: "
2. Teil: Und hier eine Zeile tiefer durc
h das RETURN-Steuerzeichen
Hier das Anfuehrungszeichen: "

Felder/Arrays[Bearbeiten | Quelltext bearbeiten]

Bei großen variablen Datenmengen bietet das C64-BASIC die Möglichkeit, viele zusammengehörige Werte unter einem einzigen Namen zu verwalten, das sind die Datenfelder oder Arrays. Arrays gibt es für die drei schon genannten Variablentypen (Fließkomma, Integer und String). Sie können ein- oder mehrdimensional sein. Ein eindimensionales Datenfeld muss man sich vorstellen wie eine Liste, die Daten stehen "untereinander" und sind durch die Nummer der Zeile auf dieser Liste einzeln ansprechbar (z.B. könnte in der Liste EINKAUF$(10) an Position 5 der Wert "Grillkohle" eingetragen sein, er wäre abrufbar per PRINT EINKAUF$(5) usf.) Zweidimensionale Felder sind vergleichbar mit dem Plan für ein Spielfeld, etwa für Schach: im Array BRETT%(8,8) könnten in den einzelnen Zellen die Positionen der Spielfiguren abgelegt sein. Hätte der König die Kennzahl 1, könnte er z.B. irgendwann an Position BRETT%(5,3) = 1 gesetzt werden. Entsprechendes gälte für ein Schiffe-versenken-Spiel (je Spieler ein Feld). Zweidimensionale Felder können aber auch sehr gut Tabellen abbilden (z.B. könnte BUNDESLIGA%(18,2) für jeden der 18 Vereine die Tore und Punkte enthalten, dazu käme eine Liste BUNDESLIGA$(18), die die Namen der Vereine enthielte; Ausgabe: PRINT BUNDESLIGA$(1), BUNDESLIGA%(1,1), BUNDESLIGA%(1,2)). Theoretisch sind maximal 255 Dimensionen möglich, was allerdings an Speicherplatzgrenzen stoßen dürfte, denn der Platzbedarf wächst mit jeder Dimension stark an.

Der BASIC-Befehl DIM dient zur Deklaration eines Arrays, also z.B. so: DIM BUNDESLIGA%(18,2). Zu beachten ist, dass die Positionszählung intern für jede Dimension mit 0 beginnt, sodass im genannten Bundesliga-Beispiel 19 * 3 = 57 Tabellenzellen im Speicher reserviert werden, in diesem Fall für Integerzahlen, also mit zwei Bytes Platzbedarf pro Zelle, was einen Speicherbedarf von 114 Bytes (plus einige Verwaltungsbytes) ergäbe. Ein Fließkomma-Array gleicher Dimensionierung würde schon 285 Bytes verbrauchen. Ein Stringarray enthält bei der Anlage (vor Wertezuweisungen) außer den Verwaltungsbytes gar nichts, verschlingt daher auch keinen Platz.

Wird die Dimensionierung mit DIM unterlassen, legt der Interpreter beim ersten Aufruf einer beliebigen Zelle eines Arrays das ganze Array mit einer Größe von 11 Zellen pro Dimension an - also etwa A(10,10,10) beim erstmaligen Zugriff auf die Zelle A(1,5,0) - und belegt entsprechend viel Speicher (hier: 6655 Bytes plus 9 Verwaltungsbytes). Arrays von einem Zahlentyp werden mit dem Wert 0 pro Zelle initialisiert, Stringarrays mit Leerstrings.

Zu den Fehlermeldungen und sonstigen Details siehe beim BASIC-Befehl DIM.

Operatoren[Bearbeiten | Quelltext bearbeiten]

Operatoren dienen dazu, Daten gleicher Art nach einer festgelegten Regel miteinander zu verknüpfen. Entsprechend den drei Datentypen gibt es drei verschiedene Arten von Operatoren: numerische, logische und Zeichenketten-Operatoren. Sie werden dargestellt als bestimmte Zeichen oder Schlüsselworte: +, -, *, /, ↑, <, >, =, AND, OR und NOT. Dabei dienen Klammern dazu, die vorgegebene Ranghierarchie zu verändern. Der Inhalt des innersten Klammerpaares eines Ausdrucks wird zuerst abgearbeitet. Die höchste Verschachtelungstiefe beträgt hierbei 10 geöffnete Klammern, ansonsten erfolgt die BASIC-Fehlermeldung ?OUT OF MEMORY ERROR.

Die Rangstufung der Operatoren ist beim C64 folgendermaßen festgelegt (höchste Priorität zuoberst, dann absteigend wie angezeigt):

1 Potenzierung
2 +
-
Vorzeichen
3 *
/
Multiplikation
Division
4 +
-
Addition und Stringverkettung
Subtraktion
5 <
<=
=
>=
>
<>
Vergleich
6 NOT Verneinung
7 AND Sowohl-als-auch-Verknüpfung
8 OR Oder-Verknüpfung

Die logischen Operatoren AND, OR und NOT können auch zur bitweisen Verknüpfung von Zahlen herangezogen werden (beschränkt auf 16 Bit entsprechend des zulässigen Integer-Wertebereichs).

Geschwindigkeit: Als grobe Abschätzung kann gelten, dass das BASIC des C64 bis zu ca. 380 Additionen pro Sekunde und ca. 200 Multiplikationen bzw. Divisionen pro Sekunde schafft.

Beispiele:

  • Mathematische Operationen:
    • Addition: C=A+B
    • Subtraktion: C=A-B
    • Multiplikation: C=A*B
    • Division: C=A/B
      Eine Division durch 0 wird nicht zugelassen und die BASIC-Fehlermeldung ?DIVISION BY ZERO ERROR ausgegeben.
    • Negatives Vorzeichen: C=-B oder C=2-(-4)
    • Potenzen: C=2↑2
    • Verwendung von Exponenten: C=A+1E+6
    • Verwendung von PI ("π"; Konstante mit dem Wert 3,141592654): C=D*π
  • Vergleichoperatoren erlauben die Werte von Variablen und Funktionen zu verknüpfen, um üblicherweise in einer IF-THEN- oder ON-GOTO/GOSUB-Konstruktion den Programmlauf zu steuern: IF A < B THEN 200
  • Logische Operationen AND, OR und NOT bilden eigenständige oder mit Vergleichsoperatoren komplexere Ausdrücke für die Steuerung des Programmlaufs (Entscheidungen nach Wahrheitsgehalt):
10 ON (X AND Y)+2 GOTO 100, 200
99 REM X, Y: 0 | -1 ... FALSCH | WAHR
100 PRINT "WAHR":END
200 PRINT "FALSCH":END
  • Logische Bitverknüpfungen mit AND, OR und NOT:
REM BIT 6 EINER SPEICHERSTELLE SETZEN: 
POKE 53269, PEEK(53269) OR 64
  • Stringverkettung: C$ = A$ + B$

Funktionen[Bearbeiten | Quelltext bearbeiten]

Variablen können durch folgende mathematische Funktionen verarbeitet werden: ABS, ATN, COS, EXP, FN, INT, LOG, RND, SGN, SIN, SQR, TAN. Darüber hinaus stehen die Systemfunktionen FRE, POS und USR, sowie die Systemvariablen STATUS (ST), TIME (TI) und TIME$ (TI$) zur Verfügung. Zur Bearbeitung von Zeichenkettenvariablen, u.a. Teile von Zeichenketten zuschneiden, gibt es LEFT$, MID$, RIGHT$.

Um Variablentypen ineinander umzuwandeln, stellt der Interpreter die Funktionen ASC, VAL (Zeichen oder Zeichenkette in Zahl), CHR$, STR$ (Zahl in Zeichen oder Zeichenkette) bereit und schließlich dient LEN dazu, die Länge einer Zeichenkette als Zahl zu ermitteln.

Platzverbrauch[Bearbeiten | Quelltext bearbeiten]

Der Platzverbrauch beträgt für Variablen unabhängig vom Typ 7 Bytes: 2 Bytes für den Variablennamen und die Typcodierung, sowie 5 weitere Bytes, die unterschiedlich genutzt werden:

  • Gleitpunkt- oder Fließkommavariablen: 1 Byte Exponent plus 4 Bytes Mantisse,
  • Ganzzahlige Variablen: 2 Bytes für einen 16-Bit-Integer-Wert (Rest enthält 0),
  • Zeichenkettenvariablen: 3 Bytes für den String-Descriptor (1 Byte Längenangabe und 2 Bytes als Zeiger auf den eigentlichen Zeichenketteninhalt, Rest enthält 0)

Variablen in einem Feld/Array werden platzsparender verwaltet. Neben den Verwaltungsdaten fallen pro Zelle nur noch die Daten für den jeweiligen Variablentypen (siehe oben) je Feldelement an. D.h. eine Integerzelle verbraucht in einem Array zwei, eine Stringzelle drei und eine Fließkommazelle fünf Bytes.

Interne Organisation der Variablen[Bearbeiten | Quelltext bearbeiten]

  • Variablen werden im BASIC-Speicher anschließend an das BASIC-Programm verwaltet (bis maximal MEMSIZ). Das Editieren des Programms löscht alle Variablen (implizites CLR).
    • Im Variablenbereich werden zusätzlich zu den eigentlichen Variablen auch mit DEF erstellte FN-Funktionen verwaltet.
    • Intern unterscheidet der BASIC-Interpreter die Variablentypen und Funktionsnamen anhand von Bit 7 der beiden signifikanten Zeichen des Variablennamens:
      • in beiden Zeichen nicht gesetzt: Fließkommavariable
      • im zweiten gesetzt: Stringvariable
      • im ersten gesetzt: FN-Funktionsname
      • in beiden gesetzt: Integer-Variable
  • Variablenzugriff und dessen Laufzeitverhalten:
    • Die Erstverwendung einer Variable bestimmt auch die Reihenfolge der Variablen im jeweiligen Variablenspeicherbereich. Da der BASIC-Interpreter Variablen stets sequentiell vom Variablenbereichsanfang her durchsucht, wird eine früher definierte oder verwendete Variable schneller gefunden. Häufig genutzte Variablen (z.B. Indizes/Zähler) sollten daher explizit möglichst früh (beispielsweise mit DIM auch für einfache Variablen verwendbar) im Programmablauf definiert werden, um Laufzeit zu sparen.
    • Die erste Zuweisung einer bisher nicht angelegten, einfachen Variablen kann im Vergleich zu weiteren Zuweisungen deutlich länger dauern, wenn bereits sehr umfangreiche bzw. große Arrays existieren. Denn der Bereich für indizierte Variablen muss nämlich für jede neu angelegte einfache Variable im Speicher vollständig verschoben werden, um Platz im Bereich für einfache Variablen zu schaffen. Das kann z.B. bei einem String-Array von 9000 Elementen bereits 0,5s in Anspruch nehmen (die Zuweisung dauert dann mehr als 30 mal länger).
  • Steht kein freies Basic-RAM mehr zur Verfügung, führt der Interpreter eine Garbage Collection durch, die den Speicherplatz nicht mehr referenzierter Strings freigibt, der dann von neuen Variablen, aber auch erneut von String-Werten verwendet werden kann.

Weblinks[Bearbeiten | Quelltext bearbeiten]

WP-W11.png Wikipedia: Mantisse
WP-W11.png Wikipedia: Variable