Eine letzte Information noch vor dem Beispiel, die zum Verständnis wichtig ist, mit Variablen aber nur teilweise etwas zu tun hat: die im letzten Artikel vorgestellte Funktion printf akzeptiert neben dem ersten Argument, welches die auszugebende Zeichenkette beschreibt, noch weitere Argumente. Diese enthalten Daten, die zusammen mit der Zeichenkette ausgegeben werden sollen, wobei die Zeichenkette die Informationen enthält, wie die Daten auszugeben sind; man spricht von der Formatierung der Daten (das f in printf steht für formatted). Über kurze Sonderzeichen in der Zeichenkette kann angegeben werden, in welchem Format die zusätzlichen Daten ausgegeben werden sollen; das Zeichen %i steht dabei etwa für ganze Zahlen. Die Reihenfolge der Formatierungs-Informationen entspricht dabei der Reihenfolge der übergebenen Argumente. Die folgende Anweisung gibt dementsprechend die Zeichenkette 1 + 2 = 3 auf dem Bildschirm aus (gern auch einmal im Programm vom letzten mal ausprobieren):

printf( "%i + %i = %i", 1, 2, 3 );

Nun aber genug der Theorie, hier kommt endlich ein kleines Beispiel.

#include <cstdio> int main() { int x = 42; int y = 2; int z = x + y; printf( "%i + %i = %i\n", x, y, z ); }

Zuerst werden 3 Variablen x, y und z deklariert und ihnen gleich Werte zugewiesen. Diese Werte werden anschließend genutzt, um eine passende Bildschirmausgabe – nämlich 42 + 2 = 44, zu generieren.

Das Beispiel ist natürlich denkbar simpel und man mag sich zurecht fragen, warum hier mit Variablen hantiert werden soll – man könnte schließlich auch die Werte direkt benutzen. Etwas anschaulicher wird es vielleicht mit diesem Beispiel:

#include <cstdio> int x = 42; int main() { printf( "%i * %i = %i\n", x, x, x * x ); }

Anstatt hier die Zahl 42 mehrmals in dem printf-Aufruf angeben zu müssen, reicht es, sie an einer Stelle zu notieren (in diesem Fall übrigens im globalen Scope – die Variable x wäre im gesamten weiteren Programm gültig) und dann mehrfach wiederzuverwenden. Das ist dann praktisch, wenn sie später einmal geändert werden soll, da sie lediglich an einer Stelle geändert werden muss. Noch verständlicher wird ihr Nutzen, wenn wir die im letzten Artikel erwähnte Möglichkeit berücksichtigen, dass Funktionen Werte als Ergebnis zurückliefern können. Schauen wir uns das folgende Beispiel an:

#include <cstdio> int f() { return 42; } int main() { int x = f(); printf( "%i * %i = %i\n", x, x, x * x ); }

Wir sehen hier eine Funktion f, welche den Werte 42 als Ergebnis zurückliefert. 42 ist hier natürlich nur ein Platzhalter für eine beliebig komplexe Berechnung. Das Ergebnis der Berechnung wird in der Funktion main in der Variablen x zwischengespeichert und dann für den printf-Aufruf in bekannter Manier wiederverwendet. Je komplexer eine Funktion wird, desto größer wird auch der Nutzen der Variablen, da sich ein Programm mit ihnen weitaus übersichtlicher gestalten lässt; zudem lassen sich Werte in Variablen zwischenspeichern, so dass eine erneute (unter Umständen aufwändige) Neuberechnung für jede Verwendung ausbleiben kann.

Abschließend noch 2 wichtige Bemerkungen zu Variablen.

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. Zwar kann auf einer Scope-Ebene jeder Variablenname nur einmal vorkommen, aber in tiefer geschachtelten Scopes können die Namen mehrfach auftreten. Geschachtelte Scopes sind Scopes, die in einen anderen eingebettet sind; der Funktions-Scope ist etwa in den globalen Scope eingebettet. Damit kann sowohl eine globale als auch eine lokale Variable x in einer Funktion existieren, wobei immer die “lokalere” Variable Vorrang hat und die globalere überschreibt bzw. verdeckt. Bei längeren Funktionen kann es da durchaus einmal passieren, dass eine lokale Variable unabsichtlich eine globale verdeckt und das Programm damit ein unerwartetes Verhalten zeigt. Zudem gilt beim Programmieren weitgehend (nicht immer – dazu später auch noch etwas) der Grundsatz “Deklarationen so lokal wie möglich”, einfach, um die Übersichtlichkeit des Programmcodes zu erhalten. Wenn Variablen nur in der Nähe der Stellen deklariert werden, wo sie auch benutzt werden, lässt sich ein Programm viel leichter lesen, als wenn sämtliche Variablen global gehalten werden. Zudem lassen sich so Probleme bei der Rekursion vermeiden (dazu später mehr). Also, ganz wichtig: so wenige globale Variablen wie möglich (manchmal benötigt man welche, deswegen gibt es sie; das soll hier aber erst einmal nicht interessieren)!

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