| ### 2. Abend
| Ziele : Wir werden nach diesem Abend in der Lage sein selbstständig ein kleines C++-Programm zu schreiben. Wir können einfache Daten zwischenspeichern und den Begriff der Variablen kennenlernen. Wir können Daten von der Tastatur in Variablen einlesen.
### Programm erstellen
| Am ersten Abend haben wir bereits ein einfaches Programm "Hello World" erstellt. Wir haben den Programmcode mit dem Editor erstellt. Der Editor erlaubt es uns Text zu editieren, wobei gewisse Editoren in der Lage sind C++ Code speziell einzufärben ! Im Grunde genommen genügt aber ein einfacher Editor wie der Notepad um C++ Quellcode zu erstellen. Hier also ein wenig Code (zur Entspannung).
    #include <iostream>

    using namespace std;

    int main()
    {
        cout << "Ich habe Hunger" << endl;
        return 0;
    }
| Diese Datei ist wie gesagt eine einfache Textdatei. Wir machen sie ein wenig aussergewöhnlich, indem wir ihr die Endung ".cpp" geben ! Diese Quellcodedatei übergeben wir jetzt dem C++-Compiler. Wie das mit der Entwicklungsumgebung, die wir verwenden funktioniert, könnt Ihr auch noch einmal nachlesen.
| Der Compiler hat nun die Aufgabe die Header Datei <iostream> einzubinden. Im Detail ist das der Präprozessor, der einfach den Inhalt der Datei die nach #include angegeben wird, in die Datei "hineinkopiert". Durch das Einbinden der iostream-Datei haben wir die Möglichkeit "cout" zu verwenden. cout ist die Kurzform für "console output". Wir schieben mit den << Zeichen den Text "Ich habe Hunger" in den Ausgabestrom (output stream). Text, der für die Ausgabe bestimmt ist, wird in Anführungszeichen gesetzt, so versucht der Compiler die Zeichen zu compilieren. Mit den Anführungszeichen behandelt der Compiler den Text als Text in unserem Sinne.
| Das Compilieren erzeugt eine sogenannte Objekt-Datei (häufig mit der Endung .obj). Diese Datei enthält bereits den Anweisungen die der Prozessor des Computers ausführen könnte. Gewisse Dinge, die wir verwenden sind aber in Bibliotheken enthalten. Zusätzlich muss ein ausführbares Programm gewisse Dinge enthalten. Die Aufgabe unser Programm fertig zusammenzubauen, das heisst mit den Bibliotheken zu verbinden und eine ausführbare Datei (.exe unter Windows) zu erstellen übernimmt der Linker. Betrachtet hierzu auch die Abbildung auf Seite 26 in unserem Buch. Nun haben wir also eine ausführbare Datei, unter Windows ein .exe. Bevor wir nun zum Kapitel Elementare Datentypen, Konstanten und Variablen übergehen, schieben wir ein allgemeineres Kapitel über den Computer ein.
### Der PC und mein Programm
| Hier also ein Kapitel zum allgemeinen Verständnis.
| Die CPU (Central Processing Unit) oder - auf Deutsch - der Prozessor in einem PC kann nicht viel mehr als Daten im Speicher (RAM) lesen oder schreiben. Weiter kann die CPU diese Daten in interne Register holen und mit diesen einfache Berechnungen anstellen.
| Ein Programm ist ein Haufen Befehle, die in den Speicher gelesen werden und die dann Schritt für Schritt abgearbeitet werden, wobei ein Programm während dem es läuft noch mehr Speicher beanspruchen kann um Zwischenergebnisse (als Variablen) zu speichern. Früher mussten Programme noch in Assembler geschrieben werden, für eine einfache Addition musste zuerst ein Wert irgendwo in den Speicher geschrieben werden. Dieser Wert kann dann in ein Register des Prozessors geladen werden, danach kann ein zweiter Wert aus einer anderen Speicherstelle hinzu addiert werden und das Ergebnis wieder zurück an eine Speicherstelle geschrieben werden. Zum Glück gibt es jetzt C++, so dass wir einfach schreiben können:
    int Ergebnis = Zahl1 + Zahl2;
| Dabei ist ein Variablenname wie Zahl1 nichts anderes als eine Speicherstelle, der man einen Namen gegeben hat. Der Speicher kann dann ungefähr so aussehen:
Adresse | Inhalt
1000 | Programmcode
1001 | Programmcode
1002 | Programmcode
1003 | Programmcode
... | ...
2000 | Programmcode
... | leer
3000 | Variable z.B. Ergebnis
3004 | weitere Variablen z.B. Zahl1
3008 | weitere Variablen z.B. Zahl2
... | andere Variablen
| Ihr habt vielleicht bemerkt, dass die Adressen bei den Variablen immer um 4 erhöht werden. Das kommt daher, dass in einer Adresse immer nur 8 Bit Platz haben. Ein Bit kann dabei nur einen Wert 0 oder 1 haben. Im Zweiersystem ergibt das nur Zahlen von 0 bis 255 :
Bit | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0
Wert | 2^7 | 2^6 | 2^5 | 2^4 | 2^3 | 2^2 | 2^1 | 2^0
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1
| Will man grössere Zahlen bearbeiten braucht man mehr als 8 Bit oder ein Byte. Bei meinem System meistens 4 Byte oder 32 Bit.
| Variablen bedeuten Speicherplatz Wollen wir also Zahlen in Variablen speichern, die grösser sind als 255, brauchen wir mehr bits. Damit der Compiler weiss wieviel Platz eine Variable brauchen wird, um sie mit anderen Variablen im Speicher anzuordnen, ist er auf die Entscheidung des Programmierers angewiesen, der bei der Definition angibt wieviel Speicher seine Variable brauchen soll. Will er also eine grössere Zahl in einer Variable wählt er einen Datentyp, der mehr Speicher braucht.
| Vorzeichen oder nicht ? Mit dem Datentyp wird aber auch noch etwas anderes bestimmt, nämlich ob eine Zahl ein Vorzeichen haben kann oder nicht. Dabei wird das höchstwertige Bit, also das mit dem grössten Exponenten als Vorzeichenbit verwendet. Wenn wir also einen Datentyp mit 8 Bit verwenden sieht die Wertigkeit der Bits wie folgt aus:
Bit | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0
Wert | Vorzeichenbit | 2^6 | 2^5 | 2^4 | 2^3 | 2^2 | 2^1 | 2^0
64 | 32 | 16 | 8 | 4 | 2 | 1
| Und die Werte, die sich aus den Bitmustern ergeben sehen wie folgt aus :
Bitmuster | Wert
'00000000' | 0
'00000001' | 1
'00000010' | 2
'00000011' | 3
... | ...
'01111111' | 127
'10000000' | -128
'10000001' | -127
'10000010' | -126
... |
'11111111' | -1
| Das heisst mit 8 Bit können wir die Zahlen von -128 bis 127 abbilden. Die Unterscheidung von Vorzeichenlosen Variablen und solchen mit Vorzeichen ist für den Compiler wichtig, weil der erzeugte Assemblercode sich je nachdem unterscheidet ob die CPU mit oder ohne Vorzeichen rechnen muss. Der Datentyp, der nur ein Byte verwendet wird wird char genannt, was der Kurzform von character (Buchstaben) entspricht. Das kommt daher, dass man einmal dachte, dass man alle Buchstaben und Sonderzeichen mit 8 bits darstellen kann. Den 255 möglichen Kombinationen hat man allen Buchstaben, Ziffern und Sonderzeichen zugeordnet. Der gebräuchliche Zeichensatz ist der ASCII Zeichensatz. Gemäss ASCII Zeichensatz ist folgendes Bitmuster gleich dem 'A' im Alphabet : 01000001 (Dezimahl 65). Der float und der double Datentyp werden vom Compiler und von der CPU noch einmal anders behandelt, die Bitmuster haben ein ganz andere Bedeutung und müssen anders verrechnet werden. Das sieht man auch daran, dass solche Berechnungen in vielen Systemen (z.B. im PC) von einer FPU (Floating Point Unit) erledigt werden.
Datentyp | Vorzeichen | Anzahl Bits/Anzahl Bytes | Wertebereich
char | ja | 8/1 | -128 bis 127
unsigned char | nein | 8/1 | 0 bis 255
short | ja | 16/2 | -32768 bis 32767
unsigned short | nein | 16/2 | 0 bis 65535
int | ja | je nach Maschine oder Betriebssystem |
unsigned int | nein | je nach Maschine oder Betriebssystem |
long | ja | 32/4 | -2147483648 bis 2147483647
unsigned long | nein | 32/4 | 0 bis 4'294'967'296
float | ja | 32/4 |
double | ja | 64/8 |
### Variablen und Datentypen
| Je nach Aufgabe, die wir lösen wollen, müssen wir Daten verschieden behandeln. So macht es für die CPU einen Unterschied ob zwei vorzeichenlose Zahlen addiert werden sollen oder solche mit Vorzeichen. Sind die Zahlen, mit denen wir rechnen wollen sogar Gleitkommazahlen, können Berechnungen in der FPU (floating point unit) des Rechners gemacht werden. Da der Compiler beim Übersetzen unseres Programmes die richtigen Maschinenbefehle erzeugen muss (um z.B. die FPU anzusprechen) müssen wir in unserem Programm angeben mit was für Daten wir arbeiten wollen. Der Compiler muss auch wissen, wieviel Platz gebraucht wird um die Daten zu speichern. Nehmen wir an wir wollen eine Addition ausführen ! Wir addieren zwei Ganzzahlen und speichern das Ergebnis in einer Variable. Eine Variable ist ein wenig Speicherplatz, in dem wir Daten speichern dürfen (siehe auch oben).
| Für C++ bedeutet es, dass wir um Variablen zu erzeugen auch einen sogenannten Datentypen angeben müssen. Hier eine gültige Variablendefinition:
    short EineZahl;
| short bezeichnet den Datentypen, "eineZahl" ist der Name der Variable. Wir können also in der Variable "eineZahl" eine Zahl im Bereich zwischen -32768 und 32767 speichern. Betrachten wie ein einfaches Beispiel :
    #include <iostream>

    using namespace std;

    int main()
    {
        // definiere eine Variable
        // vom Datentyp int
        int meineErsteVariable;

        // die Variable bekommt einen
        // Wert
        meineErsteVariable = 666;

        // Variable auf dem Bildschirm
        // ausgeben
        cout << meineErsteVariable << endl;

        return 0;
    }
| Die Zeile nach dem Kommentar ("int meineErsteVariable"), sagt dem Compiler, dass er Speicherplatz für eine Variable vom Datentyp int anlegen soll. Bei meinem Compiler weiss ich, dass das vier Byte (=32 Bit) sind. Dieser Speicherplatz bekommt in meinem Programm stellvertretend den Namen "meineErsteVariable". Die nächste Zeile veranlasst, dass an die Speicherstelle, die den Namen "meineErsteVariable" hat, die Zahl 666 geschrieben wird. Die Zahl 666 ist dabei fest codiert (man spricht auch von hart-codiert). Sie stellt eine ganzzahlige Konstante dar. Die Zeile mit "cout" gibt den Variableninhalt im Konsolenfenster aus !
| Ein weiteres Beispiel zeigt gleich noch wie man einer Variablen gleich beim Erzeugen einen Wert geben kann. Man bezeichnet das als "initialisieren".
    int main()
    {
        // definiere eine Variable
        // und initialisiere sie
        unsigned long zahl = 0;

        // Variable auf dem Bildschirm
        // ausgeben
        cout << zahl << endl;

        return 0;
    }
| Das initialisieren der Variablen ist etwas wichtiges, denn nur wenn die Variable initialisiert wurde hat sie einen definierten Wert. Ansonsten hat eine Variable einen zufälligen Wert. Wir dürfen aber beim Programmieren nichts dem Zufall überlassen!
| Versuchen wir auch noch gleich ein Programm mit mehreren Variablen zu erstellen !
    int main()
    {
        // definiere eine Variable
        // und initialisiere sie
        int zahl1 = 0;
        int zahl2 = 0;
        int zahl3 = 0;

        zahl1 = 10;
        zahl2 = 20;

        zahl3 = zahl1 + zahl2;

        // Variable auf dem Bildschirm
        // ausgeben
        cout << zahl3 << endl;

        return 0;
    }
| Den Variablen werden zuerst mit der Konstanten 0 initialisiert. Danach werden den Variablen "zahl1" und "zahl2" der Wert 10 bzw. 20 "zugewiesen". 10 und 20 sind wieder ganzzahlige Konstanten. Variablen von verschiedenen Datentypen sollten im Allgemeinen nicht vermischt werden ! Was im ersten Augeblick wie eine Einschränkung tönt, ist eine wichtige Eigenschaft von sicheren Programmiersprachen, denn das Mischen von verschiedenen Daten weist auf Fehler im Design des Programmes hin. "Birnen sind nun mal keine Äpfel !". Weist man einer Variablen von einem Datentyp eine von einem anderen Datentyp zu wird die Zahl bestmöglich umgewandelt. Hier ein Beispiel. Beachte, dass der Compiler Warnungen ausgibt, die auf mögliche Datenverluste hinweisen.
    #include <climits>

    int main()
    {
        long zahl1 = LONG_MAX;
        short zahl2 = 33;
        float zahl3 = 3.25;

        zahl2 = zahl1;
        // zahl2 ist jetzt -1

        zahl2 = zahl3;
        // zahl2 ist jetzt 3

        return 0;
    }
| Die Variable zahl3 ist eine Fliesskommazahl. Sie wird mit der Konstanten 3.25 initialisiert. Diese Konstante ist also eine Gleitkommakonstante. Ein short kann nicht alle Zahlen enthalten, die ein Long enthalten kann. Das liegt daran, dass ein long mehr Bytes zur Verfügung hat um Zahlen darzustellen. Du kannst die Anzahl Bytes, die ein Datentyp braucht mit dem sizeof-operator ausrechnen lassen !
    #include <iostream>

    using namespace std;

    int main()
    {
        cout << "long  : " << sizeof(long);
        cout << "Bytes" << endl;
        cout << "short : " << sizeof(short);
        cout << "Bytes" << endl;
        cout << "float : " << sizeof(float);
        cout << "Bytes" << endl;
        cout << "int   : " << sizeof(int);
        cout << "Bytes" << endl;

        return 0;
    }
### Von der Tastatur einlesen
| Bis jetzt haben wir Daten ausgegeben, indem wir sie an die Konsole "geschickt" haben, mittels :
    cout << "Hallo" << endl;
| Genauso wie es ein "console out" gibt, gibt es einen "console in" : cin ! Hier gleich ein kleines Beispiel :
    #include <iostream>

    using namespace std;

    int main()
    {
        cout << "Gib eine Zahl ein : ";

        int z;

        cin >> z;

        cout << "Eingabe : " << z << endl;

        return 0;
    }
| Die fette Zeile zeigt wie man cin verwendet. Man lässt die Daten von der Tastatur mittels ">>" in die Variable "strömen". Also : Viel Spass beim experimentieren.