Tomaes' C-Tutorial
Tomaes/TAP/IMAgE

Willkommen! ... Vorwort

Yessss. Ein (Visual) C-Kurs. Noch einer. ;-) Aber diesmal ein Kurs, der sich vor allem an Einsteiger bzw. (vor allem) an Umsteiger wendet. Man kennt ja diese Tutorials: Meist in Englisch gehalten, setzen sie irgendwo an (bloß nicht am Anfang...) und man versteht höchstens die Hälfte. Deutsche Tutorials sind zumeist sehr rar, und gute und verständliche Kurse erst recht. Auch die Tutorials, die schon in diversen Diskmags veröffentlicht worden sind, sind imho weder für den Einsteiger noch dem Umsteiger besonders geeignet (oder es handelt sich "nur" um klassisches C/C++, das bezüglich Win-Programmierung ja nur die Hälfte der Wahrheit ist). Und da ich gerade selber dabei bin, mir ein paar C-Kenntnisse einzuverleiben (mit dem Endziel, eine neues Mag-Sys für's TAP.MAG zu coden...), will ich euch da draußen ein bischen teilhaben lassen... ;-)

Ok, wie gesagt will ich vor allem diejenigen unter euch ansprechen, die schon Programmiererfahrung in einer Hochsprache (z.B. Pascal, (Visual) Basic ect.) haben. Es gibt bestimmt eine Menge Leute, denen Visual Basic zu primitiv ist, und die endlich mal "richtig" unter Windows programmieren würden (Hi Mathias! *g*). Und es gibt bestimmt noch mehr Coder, die jahrelang unter DOS gearbeitet haben und die endlich auf Win umsteigen wollen.. Ich werde also nicht im absoluten "Urschleim" wühlen: Ich setzte einfach mal vorraus, daß man weiß, was Variablen, Funktionen, Prozeduren ect. sind. Hin und wieder werde ich natürlich ein paar Erläuterungen einschieben, aber eben nicht überall... Am besten, ihr habt Kenntnisse in irgendeiner Hochsprache, aber hattet noch wenig (oder gar nichts) mit Windows bzw. C-Programmierung zu tun...

Jaaaa, was brauchen wir erstmal, um ein paar Codezeilen in den Rechenknecht zu hämmern? Ein Compiler wär' nicht schlecht. Noch besser eine komplette Entwicklungsumgebung. Visual C++ von Microsoft ist in der Windows-Programmierung der Quasi-Standard. Es gibt natürlich auch Konkurrenzprodukte von Borland ect. Ich werde mich an Visual C++ 5.0 (englische Version) orientieren, aber es dürfte nicht besonders schwer sein, das hier Beschriebene auf die deutsche Version zu übertragen. Natürlich gibt es zu Version 6.0 einige Unterschiede, aber die müssten zu überbrücken sein. ;-)

Hallo Welt ... Erste Schritte

Es ist ja schon fast eine Tradition! Immer, wenn man zum ersten Mal mit einer neuen, bis dato unbekannten Programmiersprache zu tun hat, schreibt man ein kleines Programm, das nichts weiter tut, als die Worte "Hallo Welt!" auf dem Bildschirm zu bringen. Wir alle kennen das. (Ok, die Jüngeren vielleicht nicht mehr...)

    10 print "hallo welt"
    20 goto 10

So war's beim C64. Und wie geht das in C++ auf dem PC (erstmal ohne die Ausgabe unendlich zu wiederholen)? Erst einmal starten wir unser tolles (?) Visual C++. Wenn wir den "Tip of the day" weggeklickt haben, haben wir ein großes Auswahlfenster vor uns. Es gibt diverse Registerkarten (Files, Projects, Workspaces, ...). Zur Zeit befinden wir uns im Bereich Projects. Falls dieses Fenster (noch) nicht zu sehen ist, kann man es über File / New aus der Menuleiste auswählen. Zum Anfang wählen wir am besten Win32 Console Application aus. Jetzt geben wir dem Kind einen Namen: Dazu einfach das Eingabefeld Project name ausfüllen. Tippen wir einfach mal test1. Unter Location stellen wir den Pfad ein. Das Häkchen bei Win32 (unter Platforms) lassen wir erstmal so, auch der Rest kann so bleiben. Also schnell auf den OK-Button geklickt...

Jetzt haben wir das sogenannte Workspace-Fenster vor uns. Ein Workspace ist mit einem Tisch vergleichbar, auf dem man schreibt. Da man natürlich nicht direkt auf den Tisch kritzelt (nun ja, manche tun das, he he), braucht man noch ein Project, quasi die Arbeitsmappe. In dieser Arbeitsmappe befinden sich die einzelnen "Blätter", unter anderem die eigentlichen Quelltextdateien. Man kann also nicht einfach auf File / New gehen, hat dann seinen Screen vor sich, auf dem sich alles abspielt, und kann lostippen.

Es gibt einen ClassView, FileView und InfoView. Zur Zeit befinden wir uns im ClassView. Wenn wir jetzt auf test1 classes klicken, passiert erstmal nicht viel. Kein Wunder: Wir haben zwar einen Tisch (Workspace) und ein Project haben wir eigentlich auch schon, aber uns fehlt erstmal eine Quelltextdatei. Gehen wir also nochmal unter File / New. Sinnvollerweise befinden wir uns diesmal im Bereich Files. Und was nehmen wir da? Mmh... Nein, nicht 2 Pfund Gehacktes... (he he), sondern ein C++ Source File. Unter File name wird natürlich der Name der Quelltextdatei festgelegt. Tippen wir einfach mal "Hallo".

Nachdem wir den OK-Button gequält haben, sehen wir unterhalb unseres Workspace-Fensters ein neues, weiteres Fenster: Hier können wir also erstmal ein paar Zeilen Quellcode reinquetschen. Tippen wir einfach mal folgendes:

    // Unser erstes C-Programm...
    #include <iostream.h>
    void main()
    {
      cout << "Hallo Welt!" << endl;
      return;
    }

Um das Programm zu compilieren, zu linken und auszuführen, klicken wir auf das rote Ausführungszeichen-Icon. Jetzt erscheint am unteren Rand ein Fenster, in dem uns eventuelle Fehlermeldungen angezeigt werden. Normalerweise müssten hier 0 Errors und 0 Warnings anzeigt werden. Unmittelbar darauf haben wir die Früchte unserer Arbeit vor uns: In einer Dos-Box wird die Meldung "Hallo Welt!" angezeigt. Nach einem Tastendruck wird das Fenster wieder geschlossen.

Ok. Am besten, wir gehen mal Zeile für Zeile durch unser Programm. In der ersten Zeile steht:

`// Unser erstes C-Programm...`

Die beiden Schrägstriche (Slash) leiten einen Kommentar ein. Alles, was in einer Zeile nach dieser Kommentar-Kennung steht, wird vom Compiler ignoriert. Neben dieser Version des Kommentars gibt es noch eine Weitere, die mehrere Zeilen umfassen kann. Beispiel:

    /* Dies ist ein
      Kommentar */

Der Anfang des Kommentars wird hier durch einen Schrägstrich und ein Sternchen makiert, während das Kommentar-Ende durch eine Sternchen-Schrägstrich Zeichenfolge angegeben wird. (In Pascal wurden solche Kommentare durch geschweifte Klammern angegeben...)

In der näxten Zeile steht:

`#include <iostream.h>`

Ja, was haben wir denn hier? Offensichtlich wird hier was eingebunden (include). Und zwar eine Datei namens iostream.h . Diese stellt verschiedene Funktionen zur Ein/Ausgabe (io = input/output) zur Verfügung. Man kann diese sogenannte Header-Dateien vielleicht mit den aus Pascal bekannten Units vergleichen. Es handelt sich also um Code-Bibliotheken, die vorher definierte Funktionen (in C gibt es keine Prozeduren) bereitstellen, die man nicht noch mal selber "von Hand" neu programmieren muss. Die Header-Datei iostream.h wird für den cout-Befehl (siehe weiter unten) gebraucht. Lassen wir diese Zeile also weg, "erkennt" der Compiler diesen Befehl nicht mehr, weil die Bibliothek, in der diese Funktionalität enthalten ist, dann nicht mehr eingebunden wäre... Diese Header-Dateien müssen im eigentlichen Sinne keine Funktionen enthalten. Hier kann beliebiges Zeux stehen: Variablen-Deklarationen ect... Will man eine eigene Header-Datei einbinden, muss man den Namen der Datei in Anführungszeichen setzen (z.B. `#include "meinedaten.h"`).

Dann folgt:

`void main()`

Wie schon erwähnt, gibt es in C keine Prozeduren, sondern nur Funktionen. `Main()` ist quasi die Ausgangsfunktion. Von hier aus wird unser Programm gestartet. `Void` bedeutet, daß diese Funktion keinen Wert zurück gibt. In den Klammern könnten die Parameter stehen, die unserer Basis-Funktion (und damit unserem Programm) übergeben werden, z.B. Kommandozeilen-Parameter... Eine Funktion wird durch einen Block eingeschlossen. Alles, was innerhalb der geschweiften Klammern steht, gehört zur Funktion (in Pascal durch begin bzw. end; gekennzeichnet).

`cout << "Hallo Welt!" << endl;`

cout steht für "character out", womit schon viel über diesen Befehl gesagt ist. Er ist in der Header-Datei iostream.h enthalten. Das "<<" zeigt quasi den Datenfluss auf. Der String "Hallo Welt!" wird mittels cout angezeigt, endl bewirkt lediglich einen Zeilenumbruch. In Pascal würde man also schreiben:

`writeln ("Hallo Welt");`

Was weiter auffällt ist, das (wie in Pascal) hinter jedem abgeschlossenen Befehl ein Semikolon stehen muss (Ausnahmen bestätigen die Regel ;-) ). Will man zwischendurch eine Variable ausgeben, sieht das so aus:

`cout << "Hallo Welt, dies ist Programm Nr." << nummer << "... Wahnsinn!" << endl;`

Weiter geht's mit:

    return;

Dieser Befehl sorgt dafür, daß unser tolles Programm ordnungsgemäß beendet wird. Jede Funktion gibt ja bekanntlich einen Wert zurück. Da unsere `main()`-Funktion aber keinen Wert (void) zurückgibt, folgt unmittelbar auf `return` schon das Simikolon.

Deeper into the mud ... Variablen & Co.

Das war's also schon. War doch nicht schwer, oder? Immerhin haben wir schon mal ein paar wesentliche Grundelemente eines C-Programms kennengelernt... Im Folgenden werde ich die wichtigsten noch nicht erklärten Elemente (Variablendeklaration, Schleifen ect.) mit euch durchgehen...

Also los! ;-) Wie definiert man in C eigentlich Variablen? In Basic ist es häufig nicht nötig, überhaupt Variablen zu deklarieren (in Visual Basic muß erst das Schlüsselwort option explicit fallen, eh man gezwungen wird, eine einzige Variablen zu deklarieren). Um eine Integer-Variable zu deklarieren, schreibt man dann (in VB) z.B.

`Dim a as integer, b as integer`

In Pascal sieht's schon besser aus:

`var a, b: integer;`

In jedem Fall wird die Variablen-Deklaration in fast allen Prospen (kurz für Programmiersprachen) mit einen Schlüsselwort eingeleitet. Nicht so in C; hier sieht's so aus:

`int a, b;`

Zuerst kommt der Variablentyp (int = integer), dann folgen die Variablennamen (hier: a und b). Man kann zeitgleich zur eigentlichen Deklaration auch gleich einen "Startwert" mit zuweisen.

`int a, b, c = 10;`

Und die anderen Datentypen? Hier sind die wichtigsten... In Klammern jeweils das Pascal-Äquvivalent und ein Beispiel:

    int (integer)   ..ganze Zahl (z.B. 299)
    long (longint)  ..eine große, ganze Zahl (z.B. 102047568)
    float (real)    ..Zahl mit Nachkommastellen (z.B. 3.14)
    char (char)     ..ein Zeichen (character)
    bool (boolean)  ..kann nur 2 Zustände haben: true oder false

Und Zeichenketten, strings? Naja, dieses Thema ist ein bissl verzwickt. ;-) Eine Möglichkeit wäre z.B., ein Feld von char-Variablen zu definieren (ein string ist ja im Prinzip nix anderes als ein char-Feld). Also so:

`char zeichen[6];`

Das bringt uns zum nächsten (sonst schreib' ich immer näxten, ha ha) Thema. Felder, oder auch Arrays genannt. Um ein 10x10 großes Feld vom Typ Integer zu deklarieren, schreibt man:

`int feld[9][9];`

Wieso 9 und nicht 10? Ganz einfach: Der Index beginnt bei Null, nicht bei 1! Es stehen uns also 10 Felder in jeder Dimension zur Verfügung (0..9). Um einen Wert zuzuweisen, schreibt man z.B.:

`feld[0][0] = 1;`

Zum Vergleich noch mal ein wenig Pascal:

    var feld: array[0..9, 0..9] of integer;
      { Deklaration des 10x10 Feldes }

und die Zuweisung:

`feld[0,0] := 1;`

Natürlich gibt es noch Dutzende andere Datentypen, aber für den Anfang sollen uns diese (eh am meisten genutzten) reichen...

Zur Übung können wir ja mal ein kleines Programm schreiben, um das bisher Erlernte zu festigen:

    #include <iostream.h>
    #include <string.h>
    // In string.h befindet sich die Funktion strcpy
    // (siehe weiter unten)
    char name[10];
    int alter=50;
    float pi=3.14;
    void main()
    {
      strcpy (name, "Heinz");
      // Leider können wir nicht schreiben: name = "Heinz"
      // Wir müssen den string "Heinz" in unser char-Array
      // kopieren. Dies geschieht mittels der Funktion strcpy
      // (string-copy).
      cout << "Name:" << name << endl;
      cout << "Alter:" << alter << endl;
      cout << "Pi:" << pi << endl;
      return;
    }

... noch Fragen, Hauser? Ja, Kienzle! Geh't das mit den Strings nicht besser? Tja schon, aber den Quark mit den Zeigern heb' ich mir für später auf... Jetzt wissen wir also in groben Zügen, wie das mit den Variablen läuft. Man könnte noch ein paar Zeilen zum Thema "Gültigkeit" verlieren. Aber ich denke, da belassen wir es bei einer kurzen Bemerkung. Wie in anderen Prospen auch, gilt: Je nachdem, wo eine Variable deklariert wird, ist auch ihre Dauer bzw. Gültigkeit. Eine lokale Variable (deklariert in einer Funktion/ Anweisungsblock) gilt natürlich nur dort. Die von uns bis jetzt benutzten Variablen waren alle global (überall im Programm gültig).

Jetzt aber los! ... Schleifen, Vergleiche und mehr

Schleifen dienen bekanntlich dazu, irgendwas zu wiederholen. Da gibt es zunächst mal die while-Schleife.

    int alter = 0;
    while (alter < 50)
     {
       cout << alter;
       alter++;
     }

Zuerst wird die integer-Variable `alter` deklariert und auf 0 gesetzt. Dann kommt unsere while-Schleife zum Einsatz: Solange alter kleiner als 50 ist, wird der Code im folgenden Anweisungsblock (in den geschweiften Klammern) ausgeführt. In diesem Block passiert zweierlei: Zuerst wird der Inhalt der Variable ausgegeben und anschließend um eins erhöht. `alter++` ist also gleichbedeutend mit `alter = alter + 1` (Basic), oder `inc(alter);` (Pascal). Wenn wir die Variable "alter" nicht erhöhen würden, würde die while-Schleife zu einer Endlos-Schleife mutieren, weil die Abbruchbedingung (alter < 50) nie erreicht werden würde (bliebe ja immer auf null).

An dieser Stelle wäre es vielleicht passend, zwei Themen anzuhandeln.
1.) Vergleichsoperatoren und
2.) Inkrementierung bzw. Dekrementierung.

Also erstmal zum 1. Punkt: Vergleichsoperatoren. Im obrigen Beispiel haben wir in der while-Schleife getestet, ob eine Variable (alter) kleiner (<) als 50 ist. "Kleiner" (<) ist dabei ein Vergleichs-Operator. Normalerweise gibt es in jeder Prospe die gleichen Standart-Operatoren.

    =   (gleich)
    <   (kleiner)
    >   (grösser)
    <=  (kleiner oder gleich..)
    >=  (grösser oder gleich..)
    <>  (ungleich)

In C sieht's mal wieder anders aus. Und zwar schreibt man statt einem normalen ein doppeltes Ist-gleich ("==") und statt einem "<>" einfach "!=". Also statt:

`while (alter <> 50) // Error...`

muss man Folgendes schreiben:

`while (alter != 50)`

Und zum 2. Punkt: Inkrementierung / Dekrementierung...

Man unterscheidet die Inkrementierungs / Dekrementierungs Anweisungen darin, ob sie vor oder nach der Variable stehen:

`I++;`

bedeutet, daß die Variable I erst nach einer Operation erhöht wird. Folglich bedeutet

`++I;`

das genaue Gegenteil. Die Variable wird vor einer weiteren Operation erhöht. Um das Ganze verständlicher zu machen, gucken wir uns mal folgendes Beispiel an:

    int a = 5;
    int b = 0;
    a = b++;

Zuerst (nach der Deklaration) ist a = 5 und b = 0. Dann wird die Zuweisung a = b durchgeführt, und erst danach wird b um eins erhöht. Am Ende ist a = 0 und b = 1. Im umgekehrten Fall

    int a = 5;
    int b = 0;
    a = ++b;

wird erst b erhöht (b = 1) und dann wird der Wert auf die Variable a geschoben. Am Ende ist also sowohl a = 1, als auch b = 1! Der ganze Quark geht natürlich auch mit einem Minus (z.B. a--). Hier wird nicht um Eins erhöht, sondern um Eins vermindert...

Jetzt aber zurück zu unserer While-Schleife. Sie geht nämlich auch noch "andersrum"...

    int alter = 0;
    do
    {
      cout << alter;
      alter++;
    }
    while (alter < 50)

Hier wird die Schleife durch ein `do` eingeleitet. Am Ende steht dann die Abbruchbedingung - ganz im Gegensatz zur ersten while-Schleife, bei der die Abbruchbedingung zuerst kam. Eigentlich sollte es nicht "Abbruchbedingung" heißen sondern "Weitermach-Bedingung" ;-). Schließlich wird (wenn die Bedingung erfüllt ist) nicht abgebrochen, sondern "weiter gemacht".

Ebenfalls sehr beliebt: Die allseits bekannte For-Next Schleife. In Pascal sieht's so aus:

    For i := 1 to 9 Do Begin
      ...
    End;

In den meisten Basic's ungefähr so:

    FOR i=1 TO 9
      ...
    NEXT

Auch hier kocht C seine eigene Suppe. Hier müsste es folgendermaßen heißen:

    for (int i = 0; i < 10; i++)
    {
      ...
    }

Die einzige Gemeinsamkeit ist das Schlüsselwort `for`. In Klammern folgt zuerst die Initialisierung der Laufvariable (`i` wird hier erst deklariert... ja, das geht mitten in einer anderen Anweisung). Dann folgt die Bedingung, unter der die in geschweiften Klammern angegebenen Befehle wiederholt werden sollen (solange i kleiner ist als 10). Also wieder eine "Weitermach-Bedingung" und keine (wie in anderen Prospen üblich) Abbruch-Bedingung. Es folgt als drittes die loop-expression, also ein Ausdruck, der die Veränderung der Laufvariablen betrifft (aber u.U. nicht nur der). Dies ist wichtig, da wir sonst eventuell wieder eine Endlos-Schleife erzeugen würden. In den meisten Fällen wird die Laufvariable um eins erhöht. Um unser Beispiel mit der while-Schleife noch einmal aufzugreifen, hier nun das Selbe mit Hilfe einer for-Anweisung:

`for (int alter = 0; alter < 50; alter++) cout << alter;`

Warum keine geschweiften Klammern? Ganz einfach: Wir wiederholen nur eine Anweisung! Da kann man die Klammern getrost weglassen, es muss nicht extra ein Block benutzt werden. Und warum steht das `cout` Statement direkt nach der `for`-Anweisung? C ist eine formfreie Sprache; Ich kann auch 100 Leerzeilen dazwischen machen (würde natürlich keinen Sinn machen). Dem Compiler is' das schnuppe...

Soweit die Schleifen... Und nun noch die Verzweigungen: Zunächst If-Then-Else:

`If (a == 0) b = 3;`

Von der Warte eines Basic-Programmierers aus gesehen sieht eine solche Anweisung natürlich ein wenig komisch aus. Als erstes fällt auf, daß es kein `then` gibt (ja, wo laufen sie denn?). Zweitens: Die Verzweigungs-Bedingung muss in Klammern stehen. Alles, was nach dieser Bedingung steht, wird ausgeführt falls die Bedingung wahr ist. Ist a also gleich 0, dann wird b gleich 3. Die Sache mit dem doppelten "=" ist kein Fehler (siehe Vergleichs-Operatoren). Würde hier ein normales "=" stehen, würde der Compiler diese Anweisung als Zuweisung interpretieren (toller Anfängerfehler, he he). Und hier nochmal das Ganze mit einem else-Zweig:

    If (a == 0) b = 3;
      else b = 2;

Wenn man mehr als eine Anweisung hat, bedarf es natürlich mal wieder eines Blockes:

    If (a == 0)
      {
        b = 3;
        a = 2;
      }
    else
      {
        b = 2;
        c = 3;
      }

Und wie sieht's aus, wenn ich in C folgendes schreiben will?

    If (a = b) and (c = 0) then
    Begin
     d = 1;
     c = 2;
    End Else c = 3;

Dann wird das and durch ein `&&` ersetzt. Das or wird im übrigen durch `||` ersetzt (<Alt Gr> + "Taste neben y"). Also:

    if (a == b && c == 0)
    {
      d = 1;
      c = 2;
    } else c = 3;

Eine andere gebräuchliche Verzweigungs-Anweisung ist `switch` (in Pascal und Basic case).

    switch (a)
    {
      case 3:
        cout << "a ist drei" << endl;
        break;
      case 4:
        cout << "a ist vier" << endl;
        break;
      default:
        cout << "a ist weder drei noch vier" << endl;
        break;
    }

Dieses Beispiel dürfte fast selbsterklärend sein: `switch (a)` bedeutet, daß wir - abhängig vom Zustand der Variable a - entsprechende Aktionen durchführen. Das Programm führt alle Befehle nach dem entsprechenden `case` aus, wenn a den Wert hat, der direkt nach `case` steht. `Break` dient dazu, einen Abbruch zu erzwingen (so daß nicht die folgenden case-Statements ausgewertet werden). Das Programm steigt dann nach einem break aus der switch-Verzweigung aus. Wenn keiner der Fälle zutrifft (a ist nicht 3 und auch nicht vier), treten die Anweisungen nach `default` in Kraft.

Wie immer ohne Gewähr ... Das Zahlenrate-Spiel als Komplex-Beispiel

Puh, das war erstmal ein grober Einstieg. *Ächz*... Abschließend dazu ein etwas komplexeres Beispiel: Neben der "Hallo Welt" Tradition gibt es noch eine weitere (zumindest bei mir ;-) ). Das "Errate meine Zahl" Spiel. Das geht so: Der Computer "denkt" sich eine Zahl aus (zwischen 1 und 100), die man erraten muss. Der Dialog soll ungefähr so ablaufen: Man gibt eine Zahl ein und der Computer erzählt mir, ob seine Zahl grösser oder kleiner als meine ist. Dann tippe ich erneut. Das Ganze geht so lange, bis ich seine Zahl "eingekreist" und herrausgefunden bzw. erraten habe. Also ungefähr so:

    Errate meine Zahl (zwischen 1 und 100)...
    Dein 1. Versuch: 50
     ..meine Zahl ist kleiner..
    Dein 2. Versuch: 30
     ..meine Zahl ist grösser..
    Dein 3. Versuch: 40
     ..meine Zahl ist grösser..
    Dein 4. Versuch: 45
     ..meine Zahl ist kleiner..
    Dein 5. Versuch: 42
     ..meine Zahl ist kleiner..
    Dein 6. Versuch: 41
     ..na bitte, in nur 6 Versuchen erraten!

Dazu muss man natürlich noch eine Kleinigkeit wissen: Wie erzeuge ich die Zufallszahlen? In fast jeder Prospe gibt es eine Funktion namens `rand`, so auch in C. Bevor man diese aber nutzen kann, sollte man noch den Zufallszahlengenerator anschmeißen. In Pascal geht das mit randomize, in Qbasic (soweit ich mich entsinne) mit randomize timer. In C läuft das ungefähr so:

    srand( (unsigned) time(NULL) );   // randomize
    int zufall = rand();
      // Zufallszahl in Variable speichern...

Um die Funktion srand benutzen zu können, muss man die Header-Datei stdlib einbinden. Und da wir auch noch die Funktion `time` benutzen wollen, brauchen wir noch die time.h. Im Gegensatz zu Basic oder Pascal erzeugt die Funktion `rand()` keine Zahl zwischen 0 und 1! Statt dessen gibt sie einen echten int-Wert zurück. Hier ein komplettes Beipiel:

    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    void main( void )
    {
      int i;
      // init randomizer, damit wir bei
      // jedem Start eine andere Zahl bekommen.
      srand( (unsigned) time(NULL) );
      // 10 Zufallszahlen werden ausgegeben...
      for( i = 0; i < 10; i++ )
        cout << rand() << endl;
    }

Als Output erhält man dann beispielsweise...

    21987
    6699
    22034
    ...

Um nun eine Zahl zwischen 0 und 99 zu erzeugen, gibt man statt `rand()` einfach `rand() % 100` an.

    // 10 Zufallszahlen zwischen 0 und 99
    for( i = 0; i < 10; i++ )
      cout << rand() % 100 << endl;

Ok, und nun an die Arbeit: Programmiert ein kleines "Zahlenrate"-Spiel! Nehmt euch ruhig eine Stunde Zeit und tüfftelt an eurem Werk. Wenn ihr es geschaft habt und eure Lösung funktioniert, kann ich euch mal mein Programm zeigen.

    /////////////////////////////////////////////
    //  Zahlenraten in C++
    /////////////////////////////////////////////

    // Libaries
     #include <iostream.h>
     #include <stdlib.h>
     #include <time.h>

    // Konstanten + var-definitionen
     const wertebereich = 100;
     int compi_zahl, user_zahl;
     int versuch=0;

    // Funktion
    int zufall (int max_zufall=wertebereich)
    {
      return 1 + rand() % max_zufall;
    }

    // Programm
    void main()
    {
      srand( (unsigned) time(NULL) );   // randomize
      compi_zahl = zufall();
      cout << "Errate meine Zahl zwischen 1 und 100!\n";
      do
      {
        versuch++;
        cout << "Dies ist dein " << versuch << ". Versuch:";
        cin >> user_zahl;
        if (user_zahl < compi_zahl)
          cout << "..meine Zahl ist groesser\n";
        if (user_zahl > compi_zahl)
          cout << "..meine Zahl ist kleiner\n";
      }
      while ( user_zahl != compi_zahl );
      cout << "Supi! In nur " << versuch;
      cout << " Versuchen geschafft!\n";
      return;
    }

Hier tauchen ein paar Dinge auf, die wir noch nicht besprochen haben: "\n" innerhalb eines Ausgabe-Strings bedeutet das selbe wie "endl", nämlich einen Zeilenumbruch. Nach der Einbindung der Header-Dateien wird eine Konstante definiert. Eine Konstante ändert seinen Inhalt im Laufe des Programms nicht. Eine andere Möglichkeit, eine Konstante zu deklarieren, wäre auch `const int wertebereich = 2000;`. Hier wird die Konstante direkt als Wert vom Typ Integer definiert. Erklärungsbedürftig vielleicht noch die Funktion, in der die Zufallszahl erzeugt wird. Im Grunde aber passiert hier nichts Unbekanntes:

    int zufall (int max_zufall=wertebereich)
    {
      return 1 + rand() % max_zufall;
    }

Die Funktion wird im Hauptteil des Programms mit `compi_zahl = zufall();` aufgerufen. Der von der Funktion erzeugte Zufallswert wird also gleich in die Variable compi-Zahl geschoben. Die Funktion selbst hat einen Rückgabe-Wert vom Typ int (int zufall). Wir errinnern uns: `void main()` bedeutete, daß unsere Hauptfunktion keinen Wert (void) zurückgab. Hier wird von der Funktion zufall ein Wert vom Typ int zurückgegeben. Die Variable max_zufall wird im Funktionskopf deklariert und anschließend wird ihr die Konstante wertebereich (100) zugewiesen. Hinter `return` stand bei unserer main()-Funktion nichts. Kein Wunder: Der Rückgabewert war ja auch void, also "nichts". Und deshalb war ja auch nichts da, was man zurückgeben hatte können. Hier hingegen wird ein int-Wert zurückgegeben und genau dieser Wert wird unmittelbar hinter dem return zugewiesen bzw. erst mal berechnet. Da wir einen Wert zwischen 1 und 100 wollen (bzw. zwischen 1 und max_zufall) und nicht zwischen 0 und 99 (bzw. zwischen 0 und max_zufall-1), müssen wir noch eine 1 hinzuaddieren.

Und was passiert als nächstes? ... Coming soon

Knapp 30 Seiten... Puh, daß muss man erstmal sacken lassen. ;-) Wenn ihr das letzte Beispiel ohne Hilfe bewältigt habt, seid ihr schon ziemlich gut dabei und bereit für den näxten Schritt. Bis hier hin hat das alles noch nichts mit Windows-Programmierung zu tun gehabt. Es waren lediglich die wesentlichen Grundlagen der Programmiersprache C++. Im 2. Teil geht's dann schon ein wenig ans "Eingemachte", und wir steigen in die Untiefen des Windoze-Codings ein... ;-))

Tomaes/TAP/IMAgE