y = x * x;

Nehmen wir an, dass x und y zwei aufeinanderfolgende Speicherzellen an den Adressen 123456 und 123457 bezeichnen; beim Ausführen der Anweisungen passiert im Speicher das Folgende:

Vorher x = 42; y = x * x;
Adresse Inhalt
123456
123457
123458
Adresse Inhalt
123456 42
123457
123458
Adresse Inhalt
123456 42
123457 1764
123458

Wir sehen – keine große Magie! Variablen sind also lediglich Platzhalter für Adressen im Speicher, die unser Programm besser lesbar machen. Im Gegensatz zur Mathematik ist übrigens auch das folgende in der Programmierung erlaubt:

x = 42; y = x * x; x = 43; y = x + x;

x bekommt hier nacheinander 2 verschiedene Werte zugewiesen (in der Mathematik ist das natürlich unsinnig – hier liegt der große Unterschied im Verhalten von Variablen zur Informatik), die dann entsprechend für die Berechnung von y verwendet werden; dadurch spielt natürlich auch die Reihenfolge der Ausführung der Anweisungen eine Rolle. Im Speicher passiert das Folgende:

x = 42; y = x * x; x = 43; y = x + x;
Addr. Inhalt
123456 42
123457
123458
Addr. Inhalt
123456 42
123457 1764
123458
Addr. Inhalt
123456 43
123457 1764
123458
Addr. Inhalt
123456 43
123457 86
123458

Die Programmiersprache C++ bringt es mit sich, dass Variablen vor ihrer Verwendung immer deklariert werden müssen. Deklaration heißt, dass dem Compiler mitgeteilt wird, dass es von nun an eine Variable mit dem deklarierten Namen (dem Variablennamen) gibt; zusätzlich muss in C++ (im Gegensatz zu einigen anderen Sprachen) auch immer der Wertebereich der Variablen, der sogenannte Typ bzw. Datentyp, spezifiziert werden. Das hat den Vorteil, dass der Compiler gleich prüfen kann, ob man nur gültige Operationen ausführt – eine Zahl mit einer Zeichenkette zu addieren ist schließlich nicht sonderlich sinnvoll. Außerdem kann der Compiler über den Datentyp die passenden Speicheroperationen generieren, da es auf Prozessorebene ein Unterschied ist, ob man etwa eine natürliche Zahl oder eine reelle Zahl in den Speicher schreiben möchte.

Die Deklaration einer Variablen gestaltet sich in C++ etwa so; hier wird eine Variable x vom Typ int – das sind die ganzen Zahlen – deklariert:

int x;

Erst nachdem eine Variable deklariert wurde, kann sie auch benutzt (sprich gelesen und geschrieben) werden; was aber funktioniert, ist die kombinierte Deklaration und erste Zuweisung. Das sieht dann so aus:

int x = 42;

Bei der Zuweisung muss beachtet werden, dass nur Werte passender Typen einander zugewiesen werden, also etwa eine natürliche Zahl an eine Variable vom Typ der natürlichen Zahlen oder eine Zeichenkette an eine Variable vom Typ der Zeichenketten. Passend heißt hier zuweisungskompatibel; natürliche, ganze und reelle Zahlen sind etwa zuweisungskompatibel, natürliche und komplexe Zahlen natürlich nicht. Zu diesem Thema aber später noch etwas mehr. Auf der rechten Seite einer Zuweisung kann dabei ein beliebiger Ausdruck stehen, der ein Ergebnis vom passenden Datentyp liefert; nicht nur konstante Zahlen (etwa 42) oder simple Berechnungen (x * x) sind möglich, sondern auch komplexe Ausdrücke und Funktionsaufrufe – dazu gleich etwas.

Noch ein Fakt über Variablen, bevor wir uns ein kurzes Beispiel in konkretem Code ansehen. Jede Variable hat einen sogenannten Gültigkeitsbereich oder Scope. Dieser spezifiziert, in welchen Programmteilen eine Variable “bekannt” ist und damit verwendet werden kann. Für die meisten Variablen gilt, dass sie in dem Scope gültig sind, in welchem sie auch deklariert wurden. Typische Gültigkeitsbereiche sind etwa der globale Scope (das heißt, in der gesamten Datei – man spricht dann von globalen Variablen) oder innerhalb einer Funktion (das wären dann lokale Variablen); auch hier gilt: eine Variable kann in C++ erst benutzt werden, nachdem sie deklariert wurde. In jedem Scope kann auf der gleichen Scope-Ebene jeweils nur eine Variable mit einem bestimmten Namen deklariert werden; zwei globale Variablen x in der gleichen Datei sind etwa ungültig.

1 / 2 / 3 / 4

Kommentare (8)

  1. #1 Andreas
    April 16, 2013

    Sehr schön. Als Info-Dozent freue ich mich immer über die Möglichkeit, die eigene “Vermittlung” der Info-Inhalte vergleichen zu können. Deine Reihe hier ist wunderbar leicht zugänglich. Darf man Sie mit Quellenhinweis verlinken oder eventuell auch im Skript einbauen ?

  2. #2 Marcus Frenkel
    April 16, 2013

    @Andreas

    Klar, kein Problem. So lange die Quellen mit angegeben werden, kann das gern genutzt werden. Ich würde mich freuen, wenn mir noch gesagt wird, wo es genutzt wird, einfach damit ich weiß, wer so alles Interesse an den Inhalten hat und wo sie genutzt werden. 🙂

  3. #3 Doomtrain
    April 16, 2013

    Hm, ich glaub ich hab nen Fehler gefunden.
    Weiter oben steht, der Datenspeicher wird in Heap uns Stack unterteilt. Ich persöhnlich habe mal gelernt, dass sich die Daten im Buffer befinden und Heap die Bezeichnung für den Programmspeicher ist (daher auch der Bufferoverflow, Buffer läuft über, die Daten landen im Heap -> Supergau). Zudem würde ich den Stack auch eher dem Programmspeicher zuordnen.

    Ich kann mich natürlich auch irren, bin kein “echter” Informatiker.

  4. #4 Marcus Frenkel
    April 16, 2013

    @Doomtrain
    Heap und Stack sind beides Datenspeicher, da in beiden Speicherbereichen neue Speicherzellen reserviert und wieder freigegeben werden können (wobei sich nur der Mechanismus dafür in beiden unterscheidet). Der Programmspeicher ist (in der Regel) fest und ändert sich nicht während der Programmausführung.
    Mit Buffer ist im Allgemeinen nur ein zusammenhängender, eventuell erweiterbarer Speicherbereich gemeint; in der Regel sind Buffer im Heap reserviert (wenn man von der Datenstruktur des Buffers redet), können aber auch Stack-Bereiche bezeichnen.

  5. #5 Regina
    April 17, 2013

    42=Douglas Adams, die schönste aller Zahlen 😉

  6. #6 Dr. Webbaer
    April 20, 2013

    Kleine Anmerkung noch hierzu:

    Der globale Scope sollte nach Möglichkeit so selten wie möglich genutzt werden; für die meisten Variablen benötigt man ihn nicht und jede globale Variable macht ein Programm schwerer wartbar, da man die Übersicht über sie behalten muss. Ein übliches Problem mit globalen Variablen sind die Namenskonflikte.

    Hat schon mal wer mit aus Quellcode-Sicht großen Programmen gearbeitet, die ausschließlich mit globalen Variablen arbeiten? – Das ist gar nicht so-o schlecht.

    MFG
    Dr. W (der allerdings nicht so weit gehen würde per GoTo adressierte Programmblöcke (die vielleicht noch in sogenannten Copy-Dateien liegen) Funktionen vorzuziehen 😉 )

  7. #7 Stefan W.
    https://demystifikation.wordpress.com
    April 24, 2013

    Ein übliches Problem mit globalen Variablen sind die Namenskonflikte.

    Das ist auch ein Problem, aber das kleinere. Das Problem an globalen Variablen ist, dass man nicht verfolgen kann, wer sie wann zuletzt geändert hat. Bei nebenläufigen Programmen kann jederzeit jemand anderes dazwischengefunkt haben, und der Wert hat sich seit dem letzten Zugriff, den man verfolgen kann, geändert.

    Nebenläufige Programme, bei denen zwei Instanzen von etwas die gleiche Variable manipulieren – der Horror.

    Das ist nicht nur so-o schlecht, das ist noch viel schlechter.

  8. #8 Dr. Webbaer
    April 25, 2013

    Ein übliches Problem mit globalen Variablen sind die Namenskonflikte.

    Das ist auch ein Problem, aber das kleinere. Das Problem an globalen Variablen ist, dass man nicht verfolgen kann, wer sie wann zuletzt geändert hat.

    Ischt eine Frage der Organisation, des Divide et Impera, der Verteilung der Logik. Man kann so oder so versuchen glücklich zu werden.
    Probleme der beschriebenen Art kann es immer geben.

    Ischt ein wenig so wie mit dem Automechaniker, der in einem Raum nur unter ganz bestimmten Bedingungen zwei Motoren gleichzeitig zerlegen würde, der zudem mehrere Taschen für seine Werkzeuge hat.

    HTH
    Dr. W