C-Tutorial (C oder C++, Vorwort, Installation, Kapitel 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

Variablen und Konstanten, Zuweisung

2.1. Variablen und Konstanten deklarieren

Laufend muss während des Programmablaufs eine Datenspeicherung erfolgen oder es müssen Daten eingelesen werden. Wenn der Benutzer eine Eingabe tätigt, muss diese Eingabe irgendwo gespeichert werden, um sie später auswerten zu können. Um Daten zu speichern, gibt es prinzipiell zwei Möglichkeiten: Die Speicherung im Arbeitsspeicher oder in einer Datei. Eine Datenbank ist letztlich auch nur eine Datei.

Der Arbeitsspeicher hat im Wesentlichen zwei Probleme: Er ist flüchtig, die Daten gehen also spätestens dann verloren, wenn Sie den PC ausschalten. Und seine Kapazität ist meist zu begrenzt, um größere Datenmengen darin unterzubringen. Letzteres relativiert sich zwar zunehmend durch sinkende Preise für Speichermodule, doch im Vergleich zu Festplattengrößen ist er nach wie vor "klein". Daneben steigen mit den Rechenleistungen natürlich auch immer die Anforderungen. :-)

Der Arbeitsspeicher hat aber einen Vorteil: Er ist schnell! Viel schneller als jede Festplatte. Wie schnell hängt von der Art des Arbeitsspeichers und dessen Bauweise ab. Prinzipiell: Je näher dem Prozessor, desto schneller sollte das Speichermodul sein. Also L1-Cache vor L2-Cache vor RAM-Modulen. Dieses Thema soll uns hier aber nicht weiter beschäftigen.

Um Daten zu verarbeiten, wird man sie immer in den Arbeitsspeicher laden. Zumindest einen Teil davon, je nach Größe der Daten und dem verfügbaren Speicher. Sollen Daten auch nach dem Ende des Programms verfügbar sein, werden sie in einer Datei abgelegt bzw. an ein Datenbankmanagementsystem übergeben. Den Dateizugriff behandle ich in Kapitel 13 dieses Tutorials.

Nun zum eigentlichen Thema: Um Daten im Arbeitsspeicher abzulegen, braucht man in der Programmierung Variablen. Variablen sind Ihnen sicherlich aus der Mathematik bekannt. Hier stehen sie für Platzhalter. In der Programmierung ist das ähnlich. Eine Variable reserviert einen gewissen Speicherplatz im Arbeitsspeicher. So kann eine Variable etwa zwei Bytes reservieren, in denen dann Daten abgelegt werden können.

Variablen können von verschiedenen Typen (Datentypen) sein. Je nach Typ ist der reservierte Speicherplatz verschieden groß und es können unterschiedliche Daten abgespeichert werden. So haben Variablen vom Typ int eine Größe von 2 oder 4 Bytes. Wieviel genau, hängt von der Wortlänge des Prozessorregisters und dem verwendeten Compiler ab. Ein Register ist ein interner Speicherplatz eines Prozessors. Für die CPU ist das der schnellste verfügbare Speicher. Dafür ist die Anzahl der Register und damit die Speichergröße stark beschränkt. Sprich: Es gibt nur ein paar.

Wieviel Speicher eine Variable auf Ihrem System tatsächlich benötigt, können Sie mit sizeof-Operator feststellen (mehr dazu später). Je nach Typ wird aber nicht nur ein unterschiedlich großer Speicherplatz reserviert, sondern es können auch unterschiedliche Werte abgespeichert werden. So kann int etwa nur Ganzzahlen (etwa -10, 12 oder 20007; Sie werden später sehen, dass auch Zeichen wie "a" als Zahl gespeichert werden können) speichern, während Variablen vom Typ float Fließkommazahlen (Gleitkommazahlen; etwa 1.3, 40.345313) aufnehmen können.

Von Variablen sind Konstanten zu unterscheiden. Eine Konstante entspricht einer Variable, allerdings mit festem, nicht-veränderbarem Wert. Das bedeutet, dass eine Konstante bei ihrer Deklaration initialisiert (definiert) werden muss. Wenn Sie also eine Konstante wollen, müssen Sie dieser gleich einen Wert zuweisen. Und diesen behält sie dann, solange sie existiert (was nicht unbedingt bis zum Programmende sein muss).

C-Compiler beschweren sich zwar nicht darüber (kein Fehler, keine Warnung), wenn Sie die Initialisierung unterlassen, später können Sie mit der Konstante aber nichts mehr anfangen. Versuchen Sie etwa der Konstante a einen Wert zuzuweisen, erhalten Sie etwa von GCC die Fehlermeldung: Fehler: Zuweisung der schreibgeschützten Variable »a«. Die "Chance" haben Sie also nur bei der Deklaration.

Unter einer Deklaration versteht man die Bekanntgabe an den Compiler, dass etwas (hier: die Variable) existiert. Wird a vom Typ int deklariert, weiß der Compiler: Die Variable a existiert und hat den Typ int. Wird einer Variable vor deren Verwendung, vor allem gleich bei der Deklaration ein Wert zugewiesen (also ein Anfangswert), spricht man von einer Initialisierung.

Wird ein Wert zugewiesen, spricht man von einer Definition. Eine Initialisierung ist eine besondere Form der Definition. Soweit zu den sprachlichen Feinheiten, die erwähnt werden mussten. :-)

Deklaration Bekanntgabe, z.B. dem Compiler bekannt geben, dass eine Variable existiert, welchen Bezeichner (Namen) sie trägt sowie von welchem Typ sie ist.
Definition Wertzuweisung, z.B. einer zuvor deklarierten Variable wird nun ein Wert zugewiesen.

Damit Sie eine Variable deklarieren können, müssen Sie zwei Dinge wissen:

  1. Wie will ich meine Variable nennen? Jede Variable benötigt einen Namen (Bezeichner), über den sie angesprochen werden kann.
  2. Von welchem Typ soll meine Variable sein? Je nachdem, welche Werte abgespeichert werden sollen, muss ein anderer Typ gewählt werden. Manchmal ist es auch nötig, den Speicherplatzverbrauch zu berücksichtigen.

Bei der Wahl des Namens (Bezeichners) sind Sie an einige Regeln gebunden. Der Name darf nur aus alphanumerischen Zeichen (wie etwa "a", "X" oder "3"; ABER NICHT "§" oder "$") sowie dem Unterstrich "_" bestehen. Der Name muss mit einem Buchstaben oder dem Unterstrich beginnen. Es darf also keine Zahl am Anfang des Namens stehen.

Laut Standard sollte der Bezeichner nicht mehr als 32 Zeichen enthalten. Daneben gilt es noch einige andere Dinge zu bedenken, die Sinn machen, aber nicht Pflicht sind. Der Bezeichner sollte so aussagekräftig wie nur möglich sein, damit Sie auch später noch wissen, wozu die Variable gut ist. Bei Variablen wie a, k1, j und y3 ist das schwierig.

Aus dem Bezeichner sollte also hervorgehen, welchem Zweck die Variable dient. Der Zweck der Datenspeicherung ist natürlich klar, die Frage stellt sich jedoch, welche Daten gespeichert werden sollen und wann und wozu die Variable eingesetzt wird. Versuchen Sie dennoch, die Länge des Bezeichners so kurz wie möglich zu halten. Also so kurz wie möglich, so lang wie nötig.

Also besser zaehler als Dies_ist_meine_Zaehlvariable als Bezeichner. Zählvariablen bezeichnet man häufig auch mit den Buchstaben i, j, k, usw. Hier täte es also auch nur i (wie index).

Sehen wir uns eine Variablendeklaration an:

int i;

Den Ganzzahlentyp int kennen Sie bereits. Von diesem existiert nach dieser Zeile die Variable i. Die Deklaration wird mit Strichpunkt ; abgeschlossen. Allgemein lässt sich also sagen:

Typ Bezeichner;

In einer Zeile lassen sich auf einfache Weise mehrere Variablen deklarieren. Beispiel:

int a, b, c;

Wie Sie sehen, müssen die einzelnen Bezeichner nur durch Beistriche getrennt werden. Im oberen Beispiel wurden die Variablen a, b und c vom Typ int deklariert (jede hat den Typ int). Benötigt man allerdings auch eine oder mehrere Variablen von einem anderen Typ, so muss diese Deklaration in einer extra Zeile stehen. Wie etwa:

int a, b, c;
char z;

Hier wurde zusätzlich die Variable z vom Typ char deklariert. Da char ein anderer Typ als int ist, muss die Deklaration in einer eigenen Zeile stehen. Prinzipiell könnten Sie die zweite Deklaration auch in dieselbe Zeile schreiben, sofern sie hinter dem Semikolon steht. Dazu gibt es ja da das Semikolon.

WICHTIG: In ANSI-C (C89/C90) ist es nicht erlaubt, die Variablendeklaration einfach irgendwo im Anweisungsblock unterzubringen. Deklarationen müssen IMMER am Anfang eines Anweisungsblockes stehen. Beispiel:

int main()
{
  int a, b, c;
  char z;
 
  /* Erst jetzt folgen weitere Anweisungen ... */
 
  return 0;
}

Achtung: Die nächsten beiden Programme enthalten jeweils einen Fehler!
Diese Beispiele werden mit echten (älteren) C-Compilern nicht funktionieren.

Beispiel 1:

#include <stdio.h>
 
int main()
{
  /* FEHLER: Programmcode VOR der Variablendeklaration! */
  printf ("Das Programm wurde gestartet...");
 
  int a, b, c;    /* Die Deklaration erfolgt erst hier ... */
  char z;
 
  /* Erst jetzt folgen weitere Anweisungen ... */
 
  return 0;
}
 

Beispiel 2:

int main()
{
  ;int a, b, c;    /* achten Sie auf das Semikolon am Anfang! */
  char z;
 
  /* Erst jetzt folgen weitere Anweisungen ... */
 
  return 0;
} 

In Beispiel 1 werden Sie den Fehler vermutlich leichter finden als in Beispiel 2. Im ersten Beispiel steht eine gesamte "sinnvolle" Anweisung vor der Variablendeklaration. Im zweiten Beispiel hat es der fehlersuchende Programmierer schon wesentlich schwerer. Das Einzige, was hier fehl am Platz ist, ist ein "verloren gegangenes" Semikolon. Es steht vor der Variablendeklaration und wird daher vom Compiler auch als gesamte Anweisung betrachtet. Daher auch ein Fehler.

Übrigens: Die Zeile /* Erst jetzt folgen weitere Anweisungen */ ist mit /* und */ als Kommentar markiert. Kommentare werden vom Compiler ignoriert. Darauf gehe ich in Kapitel 4 noch genauer ein.

Nun zur Praxis: Alle oberen "falschen" Beispiele funktionieren mit GCC - ohne Fehlermeldungen, ohne Warnungen. Das liegt daran, dass GCC auch ein C++-Compiler ist und auch die neueren C-Standards unterstützt. Ab C99 ist es nicht mehr notwendig, dass Variablendeklarationen am Anfang des Anweisungsblockes stehen. In C++ sowieso nicht. Selbst mit dem Kommandozeilen-Parameter -std=c89 lässt sich der Quellcode problemlos übersetzen. GCC dürfte neuere Erweiterungen trotzdem zulassen, solange sie nicht mit dem alten C-Standard in Konflikt stehen. Die "Fehler-Simulation" wird erst dann perfekt, wenn Sie zusätzlich noch den Parameter -pedantic ergänzen.

Wenn Sie einen alten, antiken C-Compiler testen wollen, können Sie Borland Turbo C ausprobieren. Aber bitte keine Beschwerden an mich richten, sollte etwas nicht wie gewünscht funktionieren (oder Turbo C gar nicht laufen). ;-) Turbo C stammt in Version 2 aus dem Jahr 1989. Turbo C kennt auch garantiert keine neuen Standards.

Obwohl es also in der Praxis egal ist, stehen in diesem Tutorial Variablendeklarationen immer am Anfang eines Anweisungsblocks. Wenn Sie reinen C-Code schreiben wollen, sollten Sie das auch tun.

Eine Konstante wird deklariert, indem Sie vor den Typ das Schlüsselwort const stellen. Schlüsselwörter sind reservierte Bezeichner. Das sind Namen, die Sie nicht als Bezeichner verwenden dürfen. Sie dürfen also z.B. keine Variable mit dem Namen "const" deklarieren. Ein Beispiel für eine Konstante:

const float PI = 3.14159;

Hier wurde die Konstante PI deklariert und initialisiert (definiert). Ansonsten gilt hier das Gleiche wie bei der Deklaration von Variablen.

2.2. Datentypen

Sehen wir uns nun die einzelnen Datentypen an. In C wird zwischen Ganzzahlen- und Gleitkommatypen unterschieden. Zu den Ganzzahlentypen gehören char, short int, int und long int. Zu den Gleitkommatypen gehören float, double und long double.

Von jedem der 4 Ganzzahlentypen kann eine vorzeichenbehaftete (signed) und eine nicht-vorzeichenbehaftete (unsigned) Variable oder Konstante deklariert werden. Standardmäßig sind alle Ganzzahlentypen vorzeichenbehaftet (signed). Das bedeutet, dass Sie sowohl negative als auch positive Zahlen abspeichern können. Mit dem Schlüsselwort signed vor dem Typ können Sie das noch extra kennzeichnen. Ob Sie nun

int i;

oder

signed int i;

schreiben, ist gleichgültig.

Wollen Sie aber nur positive ganze Zahlen speichern, können Sie einen nicht-vorzeichenbehafteten Typ mit unsigned deklarieren:

unsigned int i;

Der Vorteil liegt darin, dass unsigned-Typen größere Zahlen darstellen können. Die Ursache dafür ist, dass bei signed-Typen das höchstwertigste Bit als Vorzeichenbit verwendet wird. Bei unsigned kann es hingegen zur Darstellung der Zahl genutzt werden.

Übrigens ist es auch egal, ob Sie long int oder long schreiben. Ebenso kann statt short int auch nur short verwendet werden.

In der folgenden Tabelle sehen Sie alle Datentypen aufgelistet, die Sie bei der Deklaration einer Variablen oder Konstanten als Typ angeben können. Der Bereich gibt die kleinste und größte mögliche Zahl an, die gespeichert werden kann. Die Größe bezeichnet den Speicherplatzverbrauch im Arbeitsspeicher.

Ganzzahlentypen:

Typ Bereich Übliche Größe
signed char -128 bis 127 1 Byte
unsigned char 0 bis 255 1 Byte
short int -32768 bis 32767 (wenn 2 Bytes) 2 oder 4 Bytes
unsigned short int 0 bis 65535 (wenn 2 Bytes) 2 oder 4 Bytes
int -2147483648 bis 2147483647 (wenn 4 Bytes) 2 oder 4 Bytes
unsigned int 0 bis 4294967295 (wenn 4 Bytes) 2 oder 4 Bytes
long int -2147483648 bis 2147483647 (wenn 4 Bytes) 4 oder 8 Bytes
unsigned long int 0 bis 4294967296 (wenn 4 Bytes) 4 oder 8 Bytes

Der Datentyp long hat auf 64-Bit-Systemen häufig 8 Bytes (= 64 Bit). Die Größe der Ganzzahlentypen ist in ANSI-C nicht festgelegt. Es gilt aber die Reihenfolge:

char <= short <= int <= long

Der Typ short muss dabei mindestens 2 Bytes und long mindestens 4 Bytes groß sein. Wenn Sie die genaue Größe auf Ihrem System interessiert, können Sie sich die Headerdatei limits.h näher ansehen. Einfacher geht es aber, wie erwähnt, mit dem sizeof-Operator. Mehr dazu in Kapitel 4.

Gleitkommatypen:

Typ Bereich Genauigkeit Größe
float 1.2 * 10-38 bis 3.4 * 1038 6 Stellen 4 Bytes
double 2.3 * 10-308 bis 1.7 * 10308 15 Stellen 8 Bytes
long double 3.4 * 10-4932 bis 1.1 * 104932 19 Stellen 10 Bytes

Da Ihnen die Datentypen im Laufe dieses Tutorials immer wieder begegnen werden, erspare ich mir an dieser Stelle genauere Beispiele dazu.

2.3. Zuweisung

Nachdem eine Variable deklariert wurde, kann man festlegen, welchen Wert sie speichern soll. Diesen Vorgang, bei dem man der Variable mitteilt, welchen Wert sie speichern soll, bezeichnet man als Zuweisung. Beispiel:

i = 10;

Nehmen wir an, die Variable i sei vom Typ int, dann wird dieser der Wert 10 (eine Ganzzahl) zugewiesen. Nachher speichert die Variable i diesen Wert. Allgemein lässt sich also sagen:

Variable = Wert;

Anstelle von Variable setzen Sie den Bezeichner der Variable ein, der Sie einen Wert zuweisen möchten. Rechts vom Bezeichner folgt der Zuweisungsoperator =. Der Zuweisungsoperator sorgt dafür, dass Daten zur Variable "transportiert" werden und signalisiert, dass es sich um eine Zuweisung handelt. Rechts vom Zuweisungsoperator steht der Wert, den die Variable speichern soll. Hier kann auch eine andere Variable stehen, dann wird der Wert der rechten Variable der linken Variable zugewiesen, also in die linke Variable kopiert.

Es wird also immer von rechts nach links übertragen. WICHTIG: Der Zuweisungsoperator = ist KEIN Vergleich auf Gleichheit! Zwar besitzt nach der Zuweisung die Variable, die links steht, den rechts stehenden Wert, jedoch ist davon der Gleichheitsoperator (==), den Sie später kennen lernen werden, strikt zu unterscheiden!

Die Bedeutung des = in C ist also eine andere, als Sie es wahrscheinlich aus der Mathematik kennen.

Die Initialisierung von Variablen bei ihrer Deklaration haben Sie bereits kennen gelernt. Hier noch einmal zur Wiederholung:

Typ Bezeichner = Wert;

Zum Beispiel:

int i = 0;

Werden mehrere Variablen vom gleichen Typ vereinbart, so ist es auch möglich, nur manche davon zu initialisieren. Ein Beispiel:

int i, j = 20, k = 100, l;

Hier deklarieren wir die Variablen i, j, k und l. Den Variablen j und k weisen wir sofort Werte zu, den anderen nicht.

Sehr wichtig ist die Zuweisung bei der Deklaration von Konstanten. Ohne Zuweisung wären Konstanten nämlich unbrauchbar.

const int wiederholungen = 10, modus = 1;

Danach existiert die Konstante wiederholungen mit dem fixen Wert 10 und die Konstante modus mit dem Wert 1.

2.4. Gültigkeitsbereiche

In C besteht die Möglichkeit, lokale und globale Variablen zu deklarieren. Es gibt also zwei verschiedene Gültigkeitsbereiche. Der Gültigkeitsbereich gibt an, wo eine Variable existiert und "sichtbar" ist.

Eine lokale Variable (oder Konstante) wird innerhalb eines Anweisungsblocks, wie etwa dem einer Funktion, deklariert. Bisher haben wir nur lokale Variablen in der main()-Funktion deklariert. Diese sind auch nur in dieser Funktion gültig. Das heißt, eine andere Funktion (wenn Sie eine hätten) kann auf diese Variablen nicht zugreifen. Für Konstanten gilt das Gleiche. Für eine andere Funktion sind diese Variablen und Konstanten nicht existent.

Wichtig ist, dass Sie lokale Variablen vor deren Verwendung initialisieren! Der Wert, den lokale Variablen haben, ist nämlich nicht vorhersehbar! Mit anderen Worten: Zu Beginn steht irgendein Wert in einer lokalen Variable. Das kann 0, aber auch jeder andere Wert sein. Und das ist selten gewünscht.

Eine lokale Variable ist erst dann gültig (verfügbar), wenn die Funktion, in der Sie deklariert wurde, aufgerufen wird. Wird die Funktion beendet, ist die Variable nicht mehr verfügbar. Darum ist es bei lokalen Variablen günstig, wenn sie diesen bei deren Deklaration sofort einen Wert zuweisen, um keine unvorhersehbaren Ergebnisse zu erhalten. Am besten initialisieren Sie jede Variable einfach mit 0.

Mit 0 (Null) werden nämlich globale Variablen standardmäßig initialisiert. Globale Variablen sind im gesamten Programm, daher in allen Funktionen gültig. Schreiben Sie die Deklaration am besten so weit wie möglich oben im Quellcode, um die Übersicht zu bewahren.

#include <stdio.h>
 
int globale_variable;     /* wird mit 0 initialisiert und ist im gesamten Programm gültig */
 
int main()
{
  int lokale_variable;    /* wird mit einem nicht-vorhersehbaren Wert initialisiert und ist nur in main() gültig */
 
  printf ("Lokal: %d\n", lokale_variable);     /* den Wert der lokalen Variable ausgeben */
  printf ("Global: %d\n", globale_variable);   /* den Wert der globalen Variable ausgeben */
 
  return 0;
}

Die genaue Erklärung zu printf() liefere ich in Kapitel 3 nach. Hier sei nur soviel vorweggenommen: Das %d sorgt dafür, dass eine Ganzzahl ausgegeben wird. In diesem Fall der erste Parameter, der nach dem Text (String) angegeben wurde. Mit \n wird ein Zeilenumbruch (new line) erzeugt.

Sie können lokale Variablen deklarieren, die denselben Namen wie bereits deklarierte globale Variablen besitzen. In diesem Fall "sehen" Sie immer die lokale Variable, wie das folgende Beispiel zeigt:

#include <stdio.h>
 
int i = 10;   /* globale Variable i mit dem Wert 10 */
 
int main()
{
   int i = 50;   /* lokale Variable, die ebenfalls i heisst */
                 /* i wird mit 50 initialisiert */
 
   printf ("Wert von i: %d\n", i);   /* i ausgeben */
 
   return 0;
}

Welcher Wert wird im Beispiel angezeigt werden? Testen Sie es selbst!

Vorheriges Kapitel Nächstes Kapitel