Einführung in C

Dieser Text befindet sich in der neusten Version auf http://www.eggdrop.ch/texts/c/index.htm.

20.03.04: Version 0.6
03.11.03: Version 0.5
14.10.03: Version 0.4
20.09.03: Version 0.3
22.08.03: Version 0.2
14.08.03: Version 0.1

Inhalt

1 Einleitung
1.1 Was ist C?
1.2 Was kann man mit C alles schreiben?
1.3 Und was ist mit C++?
1.4 Was brauche ich zum Programmieren?
1.5 Anmerkungen
2 Hallo, Welt!
2.1 Code
2.2 Funktionen
2.3 Anweisungen
2.4 Struktur eines C-Programms
2.5 Steuerzeichen
2.6 Kommentare
3 Variablen
3.1 Was sind Variablen?
3.2 Ein Variablentyp: int
3.3 Formatierte Ausgabe mit printf
4 Operatoren
4.1 Arithmetische Operatoren
4.2 Aufgabe: Variablen und Operatoren
4.3 Anweisungen und Operatoren
4.4 Die Operatoren ++ und --
5 Entscheidungen
5.1 Eingabe mit scanf
5.2 Entscheidungen mit if und else
5.3 Entscheidungen mit switch
5.4 Der Bedingungsoperator ? :
5.5 Aufgabe: Entscheidungen
6 Schleifen
6.1 Schleifen mit for
6.2 Schleifen mit while
6.3 Springen mit goto
7 Funktionen
7.1 Einleitung
7.2 Eigene Funktionen
7.3 Funktionen mit Argumenten
7.4 Funktionen mit Rückgabewert
7.5 Gültigkeitsbereich von Variablen
A Lösungen zu den Übungen
A.1 Variablen und Operatoren
A.2 Entscheidungen

1 Einleitung

1.1 Was ist C?

C ist eine Programmiersprache, die Anfang der 70er Jahre von Dennis Ritchie in den Bell Labs entwickelt wurde, um das Betriebssystem UNIX unabhänging von der Hardware zu schreiben. C ist sehr mächtig, flexibel und portabel, läuft also auf fast jeder Architektur. C ist heute eine der am meisten verbreiteten Programmiersprachen. Damit C-Programme überall ohne grosse Modifikationen laufen, wurde der sogenannte ANSI-C-Standard eingeführt. ANSI-C-Programme lassen sich auf jedem ANSI-C-Compiler unverändert kompilieren.

1.2 Was kann man mit C alles schreiben?

In C ist alles möglich: Grafische Oberflächen (GUIs), Berechnungsprogramme, Datenbanken, 2D und 3D Spiele, Betriebsysteme, Simulationen, Internetanwendungen und viel mehr! Dem Programmierer sind in C keine Grenzen gesetzt.

1.3 Und was ist mit C++?

C++ ist nichts anderes als ein erweitertes C. Alles was in C erlaubt ist geht auch mit C++. C++ enthält Zusätzliches für die objektorientierte Programmierung und ist somit etwas komplizierter als C. Wer C kann, kann ohne weiteres C++ lernen.

1.4 Was brauche ich zum Programmieren?

Zuerst braucht man einen Editor, wo man den C-Code eingeben kann. Theoretisch ist jeder Editor möglich, empfehlenswert ist aber einer, der den C-Code farbig hervorhebt. Ich persönlich benutze den Unix-Editor vim, der mit dem Befehl :syntax on den C-Code hervorhebt und auch automatisch einrückt. Wenn man einen Editor hat, benötigt man einen C-Compiler, um aus dem C-Code eine ausführbare Datei zu erstellen. C-Compiler gibt es auch genug. Ich verwende unter Linux den Compiler gcc, der meistens schon standardmässig dabei ist. Für Windows gibt es z.B. den freien Compiler lcc und mehr (in einer Suchmaschine z.B. windows c compiler eingeben). Alternativ zu einem Editor und einem Compiler kann man auch eine komplette Entwicklungsumgebung verwenden. Die bekanntesten für Windows sind Visual C++ und Borland C++ Builder. Diese unterstützen sowohl C, als auch C++. Wie man den Editor bedient und die Dateien kompiliert, erfährst du in den dazugehörigen Handbüchern usw. Für gcc-Fans sag ich doch noch schnell den Aufruf, um eine Datei zu kompilieren: gcc -o datei datei.c und Ausführen mit ./datei.

1.5 Anmerkungen

Begriffe, die du dir merken solltest, werden Kursiv gekennzeichnet.

2 Hallo, Welt!

2.1 Code

Wir fangen gleich mit einem einfachen Programm an. Das Programm soll uns die Zeichenfolge "Hallo Welt" auf den Bildschirm ausgeben. Hier schon mal der Quellcode:

#include <stdio.h>

int main(int argc, char *argv[])
{
        printf("Hallo, Welt!\n");
        return 0;
}

Tippe den Quellcode in deinen Editor bzw. deine Entwicklungsumgebung ein und speichere ihn z.b. unter hello.c. C-Dateien haben grundsätzlich die Dateiendung .c. Kompilier das Programm und führe es aus (unter Windows in einer DOS-Box). Wenn sich das Programm nicht kompilieren lässt, dann überprüf noch einmal den Quellcode auf Schreibfehler: z.B. vergessene Abstände oder Semikola (;) usw. Beachte auch, dass C Gross- und Kleinschreibung unterscheidet. Ich werde das Programm nun in den nächsten Abschnitten beschreiben. Die nächsten zwei Abschnitte sind wahrscheinlich die schwierigsten und die wichtigsten in diesem Kapitel. Also nicht sofort aufgeben, lieber mehrere Male lesen!

2.2 Funktionen

Wir fangen mit der Zeile int main(int argc, char *argv[]) an. Diese Zeile definiert eine Funktion. Die Befehle der Funktion werden in den geschweiften Klammern { und } - man nennt das Block oder Anweisungsblock - geschrieben. Jede Funktion hat einen Rückgabewert, der mit return zurückgegeben wird. Der Rückgabewert ist jedoch im Moment nicht sehr wichtig. Nimm einfach mal an, dass der Rückgabewert die Funktion verlässt und hier also das Programm beendet (weil das hier ja die einzige Funktion ist). Eine Funktion sieht so aus:

Typ Name(Argumente)
{
        Befehle;
        return Wert;
}

Typ: Das ist der Typ des Rückgabewerts. main gibt immer den Typ int zurück. int-Werte sind ganze Zahlen. Auf diese Datentypen werde ich später noch zurückkommen.

Name: Wir sehen hier, dass der Name unserer Funktion main ist. Diese Funktion wird beim Aufrufen des Programms immer zuerst aufgerufen. Man kann beliebig viele andere Funktionen mit (fast) beliebigen Namen machen. Diese Funktionen kann man dann aus der Funktion main oder aus anderen Funktionen aufrufen.

Argumente: Jede Funktion kann Argumente haben. Die Argumente werden durch Kommas voneinander getrennt. Die Funktion main hat die Argumente int argc und char *argv[]. Eigene Funktionen können eigene Argumente haben. Auf die Argumente von Funktionen wird aber erst später eingegangen.

Befehle: Hier kommt der eigentliche Code der Funktion hin.

Wert: Das ist jetzt eben Rückgabewert. In der Funktion main verwendet man immer den Rückgabewert 0 (Null), wenn das Programm korrekt beendet wurde. Bei einem Fehler verwendet man meistens 1. Normalerweise wird dieser Rückgabewert nicht weiter vom Betriebssystem beachtet. Er kann aber nützlich sein, wenn man das Programm von anderen Programmen aus startet und dann wissen muss, ob das Programm einen Fehler verursacht hat oder nicht. Momentan werden wir uns aber nicht weiter mit diesem Rückgabewert den Kopf zerbrechen.

2.3 Anweisungen

Im Anweisungsblock der Funktion main befinden sich folgende Anweisungen:

printf("Hallo, Welt!\n");
return 0;

Hier möchte ich festhalten, dass jede Anweisung mit einem Semikolon (;) endet. Ausnahme bilden die Anweisungen, denen ein Adressblock folgt. Doch zu denen später mehr. Das vergessene Semikolon ist einer der häufigsten Fehler von C-Programmierern. Die Anweisung return kennen wir ja bereits schon. Sie gibt den Rückgabewert der Funktion zurück. Ausserdem sollte man wissen, dass sie eventuelle Anweisungen danach nicht mehr ausführen wird. Im folgenden Beispiel würde der Text "Hallo Welt" nicht ausgegeben werden:

#include <stdio.h>

int main(int argc, char *argv[])
{
        return 0;
        printf("Hallo, Welt!\n");
}

Nun zur Anweisung printf. Sie ähnelt ein bisschem einem Funktionskopf, da hinter dem Namen runde Klammern sind - genau wie bei einer Funktion auch. Sie hat aber ein Semikolon am Schluss und befindet sich innerhalb einer Funktion (es gibt keine verschachtelten Funktionen innerhalb von Funktionen). Es ist ein Funktionsaufruf. Diese Anweisung ruft die Funktion printf auf mit dem Argument "Hallo, Welt!\n" auf. Nun, wo befindet sich diese Funktion? Wir haben im Code keine Funktion mit dem Namen printf. Lösung: Sie befindet sich in der Datei stdio.h (genauer gesagt, sie befindet sich in der C-Library, die während dem Kompilieren automatisch mitgelinkt wird; in stdio.h befindet sich nur der sogenannte Prototyp der Funktion; ich möchte dich nicht damit verwirren, denn das war nur vollständigkeitshalber zu erwähnen) . Diese Datei wird mit dem Compiler mitgeliefert. Und die erste Zeile (#include <stdio.h>) bewirkt, dass diese Datei mit eingebunden wird. Diese Zeile nennt man Präprozessordirektive. Man erkennt eine Präprozessordirektive daran, dass sie mit einem # beginnt. Vor dem Kompilieren werden diese Befehle durch den Präprozessor abgearbeitet und dann zum eigentlichen C-Compiler geschickt. Die Anweisung #include wird durch den Inhalt der angegebenen Datei ersetzt. Wird der Dateiname bei #include in spitzen Klammern (< und >) angegeben, dann wird im sogenannten Include-Verzeichnis vom Compiler diese Datei gesucht (das ist bei stdio.h der Fall). Wird der Name aber in Anführungszeichen angegeben (z.B. #include "test.h"), dann wird zuerst im Verzeichnis, wo sich das Programm befindet, gesucht und erst dann, wenn nichts gefunden wurde, im Include-Verzeichnis. So kann man eigene Dateien (sogenannte Header-Dateien) einbinden. Zu beachten ist, dass Header-Dateien gewöhnlich die Endung .h haben.

Der letzte Abschnitt zusammengefasst: Die Funktion printf wird mit dem Argument "Hallo Welt\n" aufgerufen. Ich denke ich muss gar nicht erklären, dass diese Funktion Text auf den Bildschirm schreibt. Der Text (eine Zeichenfolge oder String) wird in C immer in Anführungszeichen angegeben. Was ich vielleicht noch erklären muss, ist das \n am Schluss der Zeichenfolge. Das ist ein sogenanntes Steuerzeichen. Steuerzeichen beginnen immer mit einem Backslash (\). Das Steuerzeichen \n macht eine neue Zeile. Beispielsweise erzeugt printf("Dieser Text\nerstreckt\nsich\n\n über mehrere Zeilen\n"); folgende Ausgabe:

Dieser Text
erstreckt
sich

 über mehrere Zeilen

Man könnte das obere Programm auch auf mehrere printf-Anweisungen verteilen:

printf("Dieser Text\n");
printf("erstreckt\n");
printf("sich\n\n");
printf(" über mehrere Zeilen\n");

2.4 Struktur eines C-Programms
In der Programmiersprache C hat man eine grosse Freiheit in der Struktur des Programms. Das heisst, man könnte das obige Programm auch so schreiben:

#include <stdio.h>
int main(int argc,char *argv[]){printf("Hallo, Welt!\n");return 0;}

oder auch mit Leerzeichen und Tabulatoren so "aufpeppen":

                        #include         <stdio.h>
        int
main(


int argc
,
char *argv[   ]
) { printf  ("Hallo, Welt!\n"
)
;                return 0;}

Bei diesen zwei Beispielen leidet jedoch stark die Übersichtlichkeit. Also schreibe den Code am besten immer so, wie er hier geschrieben wird. Folgende Regeln muss man noch beachten:

2.5 Steuerzeichen
Hier werde ich auf die Steuerzeichen noch genauer eingehen. Wir kennen ja schon das Steuerzeichen \n, welches eine neue Zeile erzeugt.
 
Zuerst eine Tabelle der wichtigsten Steuerzeichen:

Steuerzeichen Beschreibung
\a Gibt ein akustisches Signal aus
\b Setzt den Cursor um eine Position nach links
\f Erzeugt einen Seitenvorschub beim Drucken
\n Setzt den Cursor an den Anfang der nächsten Zeile
\r Setzt den Cursor an den Anfang der aktuellen Zeile
\t Setzt den Cursor zur nächsten horizontalen Tabulatorposition. Meistens wird alle 8 Zeichen ein Tabulator gesetzt.
\v Setzt den Cursor zur nächsten vertikalen Tabulatorposition.
\" Gibt das Anführungszeichen (") aus.
\nnn Gibt ein Zeichen mit dem Oktalwert (Basis 8) nnn aus.
\xnn Gibt ein Zeichen mit dem Hexadezimalwert (Basis 16) nn aus. Folgende Ziffern werden dazu verwendet:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A (10), B (11), C (12), D (13), E (14), F (15)

Hier einige Beispiele zu den Steuerzeichen:

Gibt den Text "Ungültige Eingabe" aus und piepst:

printf("Ungültige Eingabe!\n\a");

Gibt eine Tabelle aus (am besten selber mal kompilieren):

printf("\tA\tB\tC\tD\tE\n");
printf("1\tA1\tB1\tC1\tD1\tE1\n");
printf("2\tA2\tB2\tC2\tD2\tE2\n");
printf("3\tA3\tB3\tC3\tD3\tE3\n");

Hier die Tabelle, die ausgegeben wird:

        A       B       C       D       E
1       A1      B1      C1      D1      E1
2       A2      B2      C2      D2      E2
3       A3      B3      C3      D3      E3

2.6 Kommentare
Kommentare werden vom Compiler ignoriert und dienen also nur den Programmierern, um den Code einfacher zu lesen und verstehen. Kommentare sind immer sinnvoll, vor allem bei grösseren Projekten, wenn mehrere Leute zusammenarbeiten. Durch Kommentare können auch Anfänger komplexe Programme besser verstehen. In C gibt es zwei Typen von Kommentaren:

Der C-Style Kommentar beginnt mit einem /* und endet mit einem */. Er kann sich über mehrere Zeilen erstrecken. Wenn man einmal mehrere Zeilen schnell rausnehmen will, eignet sich dieser Kommentar am besten, weil man am Anfang der ersten Zeile ein /* setzen kann und am Ende der letzten Zeile ein */. Ich bevorzuge diesen Kommentar. Achtung: Dieser Kommentartyp darf nicht verschachtelt werden. Die Zeile /* Kommentar /* FALSCH! */ */ gäbe einen Fehler

Der C++-Style Kommentar heisst C++, weil er ursprünglich nur in C++ erlaubt war. Er ist aber seit dem ANSI C99-Standard (seit 1999) auch in C erlaubt. Ältere Compiler können unter Umständen Fehlermeldungen ausgeben. Der Kommentar beginnt mit // und endet am Ende der Zeile.

Ein Kommentar ist nicht in einem String gültig.

Hier ein Beispiel mit beiden Kommentar-Typen:

/* bindet die Header-Datei stdio.h für printf ein */
#include <stdio.h>

int main(int argc, char *argv[]) // Hauptfunktion
{
        // gibt eine Zeile aus
        printf("Das ist ein Text. /* Das ist auch ein Text. */ // Das auch\n");
        // Kommentar
        // über
        // mehrere
        // Zeilen
        /* Kommentar
           über
           mehrere
           Zeilen */
} /* ende des Programms */

 

3 Variablen

3.1 Was sind Variablen?
Variablen sind in C veränderbare Speicherstellen. Man kann also den Wert einer Variable verändern (man nennt das Zuweisung) und den dann später verarbeiten und/oder wieder verändern. Eine Variable besteht aus dem Namen und dem Inhalt. In C gibt es verschiedene Typen von Variablen (sogenannte Datentypen): z.B. für Ganzzahlen, Zeichen usw. Das erstellen einer Variable nennen wir Deklaration. Wir deklarieren eine Variable, indem wir den Datentyp der Variable und den Namen der Variable mit einem Abstand getrennt angeben und die Anweisung mit einem Semikolon abschliessen:

typ name;

Der Name muss mit einem Gross- oder Kleinbuchstaben oder mit einem Unterstrich (_) beginnen und kann danach auch Zahlen beinhalten. Ich erwähne hier nochmal, dass C zwischen Gross- und Kleinschreibung unterscheidet. Also sind die Variablen name, Name und NAME alles unterschiedliche Variablen. Bei Variablen werden nur die ersten 32 Zeichen zur Unterscheidung benötigt. Das heisst, dass man Variablennamen, die länger als 32 Zeichen sind, vermeiden sollte, da sie zu Verwechslungen führen können. Ausserdem gibt es in C eine Liste sogenannter Schlüsselwörter. Diese Wörter dürfen nicht von Variablen benutzt werden.

Hier die Liste der 32 C-Schlüsselwörter:

auto      break     case      char      const     continue  default   do
double    else      enum      extern    float     for       goto      if
int       long      register  return    short     signed    sizeof    static
struct    switch    typedef   union     unsigned  void      volatile  while

Im ANSI C99-Standard wurden noch folgende zusätzliche Schlüsselwörter definiert:

inline    restrict  _Bool     _Complex  _Imaginary

Dazu gibt es noch C++ Schlüsselwörter, die den Compiler auch stören können, da die meisten Compiler sowohl C, als auch C++ beherrschen. Wenn also ein unerklärlicher Fehler bei einer Deklaration auftretet, könnte es sich um ein Schlüsselwort handeln. Ausserdem stellen einige Editoren die Schlüsselwörter mit einer anderen Farbe dar, so dass man diese schnell erkennen kann.

Gültige Variablennamen wären z.B.:

das_ist_EinTest
ABC123
_for_

Ungültige Variablennamen wären z.B.:

while (Schlüsselwort)
123zahl (Variable darf nicht mit einer Zahl anfangen)
sonder-zeichen (Bindestrich ist nicht erlaubt)

Genug der Theorie: Im nächsten Abschintt lernen wir den ersten Variablentyp kennen.

3.2 Ein Variablentyp: int
Der Datentyp int kann ganze Zahlen (also ohne Nachkommastellen) speichern. Sein Bereich geht auf 16Bit-Maschinen von -32768 bis +32767 und auf 32Bit-Maschinen von -2147483648 bis +2147483647. Wir verwenden höchstwahrscheinlich eine 32Bit-Maschine (Windows 95 bis XP sind 32Bit-Betriebssysteme). Wenn wir den Bereich überschreiten, zählt C wieder von vorne. Also +2147483648 wäre in C -2147483648. Um eine int-Variable zu deklarieren, geben wir folgendes ein:

int name;

Wenn wir diese Variable in unserem Beispielprogramm nach der printf-Anweisung versuchen zu deklarieren, bekommen wir eine Fehlermeldung (ausser es handelt sich um einen C++-Compiler).

Hier wäre das fehlerhafte Programm:

#include <stdio.h>

int main(int argc, char *argv[])
{
        printf("Hallo, Welt!\n");
        int name;
        return 0;
}

Das liegt daran, dass in C eine Variable nur am Anfang eines Anweisungsblockes deklariert werden darf. Das heisst, dass am Anfang des Blockes die Deklarationen kommen und erst dann die Anweisungen. Hier ein Beispiel-Block zur Veranschaulichung:

{
        Deklarationen
        Befehle
}

Korrekt müsste unser Programm also folgendermassen sein:

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* Deklarationen */
        int name;

        /* Befehle */
        printf("Hallo, Welt!\n");
        return 0;
}

Wir können auch mehrere Variablen deklarieren:

        /* Deklarationen */
        int eins;
        int zwei;
        int drei;

Oder durch Kommas (,) getrennt in einer Anweisung:

        /* Deklarationen */
        int eins, zwei, drei;

Jetzt wollen wir den Variablen einen Wert zuweisen. Das geschieht mit dem Zuweisungsoperator "=". Die Zuweisungen gehören nicht mehr in den Abschnitt Deklarationen. Hier ein Beisipel, wie man den Variablen einen Wert zuweisen könnte:

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* Deklarationen */
        int eins, zwei, drei;

        /* Befehle */
        eins = 1;
        zwei = 2;
        drei = 3;

        printf("Hallo, Welt!\n");
        return 0;
}

Übrigens nennt man das erste Zuweisen eines Werts einer Variable Initialisierung. Denn eine Variable hat nach der Deklarataion einen zufälligen, unvorhersehbaren Wert (nämlich den, der vorher an dieser Speicherstelle war). Nachdem wir die Variable initialisiert haben, hat die Variable einen vorhersehbaren Wert. Meistens initialisiert man Variablen mit 0 (Null).

Wir können Variablen auch während der Deklaration sofort initialisieren:

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* Deklarationen */
        int eins = 1, zwei = 2, drei = 3;

        /* Befehle */
        printf("Hallo, Welt!\n");
        return 0;
}

Da C viel Freiheit bietet, wäre auch das möglich:

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* Deklarationen */
        int eins = 1, zwei;
        int drei = 3;

        /* Befehle */
        zwei = 2;
        printf("Hallo, Welt!\n");
        return 0;
}

Den Abstand zwischen dem Gleichheitszeichen und dem Wert kann man weglassen. Genauso ist es mit dem Abstand nach dem Komma.

3.3 Formatierte Ausgabe mit printf
Nun beschäftigen wir uns genauer mit der Ausgabeanweisung printf. Bis jetzt haben wir nur einen gegebenen Text (String) ausgegeben. Nun wollen wir aber auch Variablen ausgeben können. Dazu verwenden wir auch die Anweisung printf. printf hat eine veränderbare Anzahl Argumente (siehe Kapitel 2.2). Das erste Argument bestimmt den Formatstring. Der Formatstring unterscheidet sich dadurch von einem normalen String, dass er Formatelemente enthalten kann. Formatelemente erkennt man am Prozentzeichen (%). Der Datentyp int hat das Formatelement %d. Wollen wir uns nun die drei Zahlen ausgeben lassen, müssten wir also folgendes in der printf-Anweisung angeben:

printf("eins: %d zwei: %d drei: %d\n");

Ersetzen wir also die "Hallo Welt"-Anweisung durch unsere "neue" Anweisung. Kompilieren wir jetzt das Programm und führen es aus. Eventuell gibt es Compilerwarnungen oder gar einen Fehler. Bei mir kam die folgende Ausgabe:

eins: -1073743176 zwei: 1074056984 drei: 1075023432

Das ist nicht so ganz das, was wir wollten, oder? Wir haben ja gar nicht angegeben, welche Variable er ausgeben soll. Wir haben nur das Formatelement %d als "Platzhalter" genommen. Die Lösung: Wir müssen die Variablen, die ausgegeben werden sollen als zusätzliche Argumente angeben. Und zwar in der Reihenfolge, in der die Formatelemente sind. Wir wir vielleicht noch wissen, werden die Argumente durch Kommas getrennt. Daraus ergibt sich die folgende Anweisung:

printf("eins: %d zwei: %d drei: %d\n", eins, zwei, drei);

Und siehe da, die Ausgabe stimmt:

eins: 1 zwei: 2 drei: 3

Hier zur Veranschaulichung folgende Abbildung. Die Formatelemente (%d) werden durch den Inhalt der Variablen ersetzt:

                                  ,-----------------.
                                 |                  |
                         ,-------+------------.     |
                        |        |            |     |
                ,-------+--------+------.     |     |
               |        |        |      |     |     |
              \|/      \|/      \|/     |     |     | 
printf("eins: %d zwei: %d drei: %d\n", eins, zwei, drei);

                          |
                          |
                          |
                         \|/

               eins: 1 zwei: 2 drei: 3                         

 

4 Operatoren

4.1 Arithmetische Operatoren
Jetzt werden wir ein bisschen mit den Zahlen rechnen. Wir beschäftigen uns jetzt mit einigen C-Operatoren. Hier die Tabelle dieser Operatoren:

Operator Beschreibung
+ Addition
- Subtraktion
* Multiplikation
/ Division
% Modulo (Rest einer Division)

Die Anwendung dieser Operatoren ist sehr einfach in C. Man kann einer Variable einfach so einen Ausdruck (z.B. 1+1) zuweisen:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a;
        a = 1+1;
        printf("a ist %d\n", a);
        /* Ausgabe: a ist 2 */
        return 0;
}

Hier noch einige weitere Beispiele für die oben genannten Operatoren:

a = 5-3; /* a ist 2 */
a = 8*8; /* a ist 64 */
a = 6/2; /* a ist 3 */
a = 7/2; /* a ist 3 */
a = 7%2; /* a ist 1 */

Bei der zweitletzten Anweisung, werden die Nachkommastellen einfach abgeschnitten (nicht gerundet). Das liegt daran, dass der Datentyp int nur Ganzzahlen beherrscht und Nachkommastellen gar nicht beachtet. Die letzte Anweisung gibt den Rest der Division 7/2 aus, also 1.

Anstelle von Zahlen können wir aber auch Variablen verwenden:

int a,b,c;
a = 5;
b = 3
c = a + b - 2;
/* c ist 6 */

Es gibt noch Operatoren, die einen Ausdruck verkürzen. Wir können also anstelle den folgenden Anweisungen...

a = a - 2;
b = b * 5;
c = c % 6

... auch das schreiben:

a -= 2;
b *= 5;
c %= 6;

Wenn wir nun Operatoren vermischen, müssen wir auf die Priorität der Operatoren achten. Dazu folgende Vorrang-Tabelle von den Operatoren:

Priorität Operatoren
1. ()
2. * / %
3. + -
4. = += -= *= /= %=

Wie wir sehen, hat die Klammer () die erste Priorität. Wollen wir also den Vorrang von bestimmten Operatoren umgehen, setzen wir Klammern. Hier ein Beispiel mit und ohne Klammern:

a = 4 + 6 * 2   /* der Computer rechnet wie folgt: 4 + (6 * 2) = 4 + 12 = 16 */
a = (4 + 6) * 2 /* der Computer rechnet wie folgt: (4 + 6) * 2 = 10 * 2 = 20 */

4.2 Aufgabe: Variablen und Operatoren
Schreibe ein Programm, welches drei int-Variablen a, b und c deklariert. Weise den ersten zwei Variablen den Wert 13 und -300 zu. Die Variable c soll den Inhalt von a mit dem Inhalt von b addieren und dann mit a multiplizieren. Erhöhe anschliessend den Wert von c um 2 und gib folgenden Text mit den Anführungszeichen aus, wobei a, b und c durch den jeweiligen Inhalt ersetzt werden sollen: "a addiert mit b und multipliziert mit a gibt c". Die Lösung gibts hier.

4.3 Anweisungen und Operatoren
Bis jetzt haben wir mit printf nur Variablen ausgegeben. Wichtig zu wissen ist, dass man auch in Anweisungen die Operatoren verwenden kann. Das ist nicht weiter schwierig, hier einige Beispiele:

printf("%d\n", 2+3); // Ausgabe: 5

int a=2,b=3;
printf("%d\n", 2*(a+b)); // Ausgabe: 10

4.4 Die Operatoren ++ und --

Die Operatoren ++ und -- entsprechen dem Aufruf +=1 bzw. -=1. ++ heisst Inkrementierungsoperator und -- heisst Dekrementierungsoperator. Diese beiden Operatoren erhöhen bzw. erniedrigen also den Wert einer Variable um eins. Jedoch gibt es zwei leicht unterschiedliche Schreibweisen dieser Operatoren (ich gehe hier jetzt auf ++ ein), wobei i hier die Variable ist:

i++;
++i;

Um den Unterschied zwischen den beiden Schreibweisen zu erläutern, zeige ich folgendes Programm:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i=0;
        printf("i = %d\n", i);
        printf("i = %d\n", i++);
        printf("i = %d\n", i);
        printf("i = %d\n", ++i);
        printf("i = %d\n", i);
        return 0;
}

Das Programm erzeugt die folgende Aufgabe:

i = 0
i = 0
i = 1
i = 2
i = 2

Daraus können wir folgendes schliessen: Bei i++ wird i nachdem die Anweisung ausgeführt wurde inkrementiert (um 1 erhöht). Bei ++i wird i schon in der Anweisung inkrementiert.

 

5 Entscheidungen

5.1 Eingabe mit scanf
Bevor wir Entscheidungen treffen werden, beschäftigen wir uns kurz mit der Eingabe von Zahlen. Bis jetzt kannten wir nur den Funktionsaufuf printf, der zur Ausgabe von Text diente. Die Funktion zur Eingabe heisst scanf. Sie funktioniert ähnlich wie die Funktion printf, dient aber nur zur Eingabe.

Wollen wir zum Beispiel eine Zahl auslesen und in die Variable abc schreiben, dann benötigen wir folgenden Aufruf:

scanf("%d", &abc);

Das Formatzeichen %d sagt der Anweisung, dass sie eine Zahl einlesen soll. Das komische & ist der sogenannte Adressoperator. Dieser Operator liefert die Adresse der Variablen, damit scanf auch etwas reinschreiben kann. Ich werde jedoch diesen Operator erst später ausführlich beschreiben.

Wir programmieren nun ein Programm, welches zwei Zahlen einliest, sie addiert und dann das Ergebnis ausgibt. Der Quellcode sollte verständlich und nachvollziehbar sein:

#include <stdio.h>

int main(int argc, char *argv[])
{
        /* Variablen deklarieren */
        int a=0, b=0;
        int ergebnis=0;

        /* Zahlen einlesen */
        printf("Erste Zahl eingeben:\n");
        scanf("%d", &a);
        printf("Zweite Zahl eingeben:\n");
        scanf("%d", &b);

        /* Zahlen addieren */
        ergebnis = a+b;

        /* Ergebnis ausgeben */
        printf("%d + %d = %d\n", a, b, ergebnis);

        /* Programm sauber verlassen */
        return 0;
}

5.2 Entscheidungen mit if und else
In diesem Abschnitt lassen wir das erste mal unseren Computer Entscheidungen treffen! Dies geschieht mit einer der wichtigsten C-Anweisungen, nämlich if, was soviel wie "wenn" bedeutet.So sieht ein if-Konstrukt aus:

if (Bedingung)
{
        Anweisungen
}

Die Bedingung ist ein Ausdruck, der wahr sein muss, damit die Anweisungen ausgeführt werden. Ein Ausdruck ist wahr, wenn er ungleich 0 (Null) ist und in den anderen Fällen falsch. Das heisst, dass Ausdrücke, die einen Wert unter Null haben, auch wahr sind. Bis jetzt kannten wir Ausdrücke wie 2+3 oder 15/3. Diese Ausdrücke können wir auch in if-Anweisungen verwenden, jedoch werden fast immer Vergleichsoperatoren verwendet. Diese Operatoren geben entweder den Wert 1 für wahr oder 0 für falsch zurück. Dazu die Tabelle der Vergleichsoperatoren:

Operator Bedeutung
a < b kleiner: wahr, wenn a kleiner als b
a <= b kleiner gleich: wahr, wenn a kleiner oder gleich b
a > b grösser: wahr, wenn a grösser als b
a >= b grösser gleich: wahr, wenn a grösser oder gleich b
a == b gleich: wahr, wenn a gleich b
a != b ungleich: wahr, wenn a nicht b

Wenn wir jetzt also den Benutzer einen Wert eingeben lassen wollen, welcher höchstens 100 sein soll, könnten wir folgende Anweisungen verwenden:

/* ... */
scanf("%d", &a);
if (a > 100)
{
        a=100;
}
/* ... */

Wenn nun ein Wert eingegeben wird, welcher grösser als 100 ist, wird a zurück auf 100 gesetzt. Besteht der Anweisungsblock aus einer Anweisung (also wie im oberen Beispiel), dürfen wir die geschweiften Klammern weglassen:

/* ... */
scanf("%d", &a);
if (a > 100)
        a=100;
/* ... */

Gefährlich ist dabei, dass man sich vergessen kann und versehentlich mehrere Anweisungen in den Anweisungsblock stopft und sich dann wundert, warum das Programm nicht richtig funktioniert. Beispielsweise würde sich das folgende Programm immer beenden, auch wenn a kleiner als 100 ist:

/* ... */
scanf("%d", &a);
if (a > 100)
        printf("a darf nicht grösser als 100 sein!\n"); return 1;

printf("a ist kleiner als 100!\n");
/* ... */

Das Gegenstück zu if ist else, welches so viel wie "sonst" bedeutet. else muss immer einem if folgen. Dazu gleich ein Beispiel:

/* ... */
scanf("%d", &a);
if (a > 100)
{
        printf("a grösser als 100\n");
}
else
{
        printf("a kleiner oder gleich 100\n");
}
/* ... */

Die geschweiften Klammern könnten wir hier auch weglassen. Folgendes Beispiel zeigt aber ein Problem, welches sich bei verschachtelten if-Konstrukten ergeben kann:

/* ... */
scanf("%d", &a);
scanf("%d", &b);
if (a > 100)
        if (b > 100)
                printf("a und b grösser als 100\n");
        else
                printf("a grösser als 100, b kleiner oder gleich 100\n");
/* ... */

Das Problem stellt sich hier ja, zu welchem if die else-Anweisung gehört. Dank meiner Einrückung des zweiten else wird deutlich, dass ein else immer zum letzten if gehört, dem noch kein else zugeordnet ist.

Wenn man in einem else-Block nur eine if-Abfrage hat, lässt man meistens die geschweiften Klammern von else weg und schreibt else und if auf eine Zeile. Dazu ein Beispiel:

/* ... */
scanf("%d", &a);
if (a > 100)
{
        printf("a grösser als 100\n");
}
else if (a == 100)
{
        printf("a ist 100\n");
}
else
{
        printf("a kleiner als 100\n");
}
/* ... */

Im oberen Beispiel könnten wir natürlich auch die geschweiften Klammern weglassen.

5.3 Entscheidungen mit switch
Wenn man beispielsweise ein Auswahlmenü ausgibt (bei jedem Eintrag steht eine Nummer, die man eingeben muss) und dann überprüfen will, welche Zahl ein Benutzer eingegeben hat, könnte man mehrere else if-Anweisungen verwenden. Weil das aber nicht nur mühsam ist, sondern auch unübersichtlich aussieht, verwendet man die sogenannte switch-Anweisung. Ein switch-Konstrukt sieht folgendermassen aus:

switch(Variable)
{
        case Wert1:
                Anweisungen
                break;
        case Wert2:
                Anweisungen
                break;
        ...
        default:
                Anweisungen
}

Ich vermute die Funktionsweise von switch ist grösstenteils schon klar. Der Wert, welcher in der Variablen gespeichert ist, wird von switch ausgewertet und wenn er mit dem bei case angegebenen Wert übereinstimmt, werden die entsprechenden Anweisungen ausgeführt. Nach der letzten Anweisung folgt die Anweisung break, welche switch verlässt. Wenn man break weglässt, dann werden auch die Anweisungen bei den nachfolgenden cases ausgeführt. Der Doppelpunkt nach dem Wert darf auch nicht vergessen werden. Was noch zu erklären ist, ist das default. default wird ausgeführt, wenn keines der oberen cases gültig ist. Nach default braucht es kein break, weil der Computer ja sowieso switch verlässt.

Nun denke ich, dass dringend ein Beispiel nötig ist. Hier ein Beispielprogramm:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int x;
        printf("Gib eine 1, 2 oder 3 ein!\n");
        scanf("%d", &x);
        switch(x)
        {
                case 1:
                        printf("Du hast eins eingegeben!\n");
                        break;
                case 2:
                        printf("Du hast zwei eingegeben!\n");
                        break;
                case 3:
                        printf("Du hast drei eingegeben!\n");
                        break;
                default:
                        printf("Du hast nicht 1, 2 oder 3 eingegeben!\n");
        }
        return 0;
}

Kompiliere das Programm und probiere es aus. Nun wollen wir ein kleines Experiment machen. Wir lassen die break-Anweisungen weg und kompilieren das Programm noch einmal. Was würde jetzt kommen?

Wer richtig aufgepasst hat ahnt es schon. Weil das break ausgelassen wurde, verlässt das Programm nicht die switch-Anweisung sondern führt alle folgenden Anweisungen auch aus. Hier die Ausgabe des Programms auf meinem Computer mit den Werten 1 und 3:

tom@workstation:~/testing$ ./switch 
Gib eine 1, 2 oder 3 ein!
1
Du hast eins eingegeben!
Du hast zwei eingegeben!
Du hast drei eingegeben!
Du hast nicht 1, 2 oder 3 eingegeben!
tom@workstation:~/testing$ ./switch 
Gib eine 1, 2 oder 3 ein!
3
Du hast drei eingegeben!
Du hast nicht 1, 2 oder 3 eingegeben!
tom@workstation:~/testing$ 

5.4 Der Bedingungsoperator ? :
Der Bedingungsoperator ? : ist ein Operator, der ähnlich wie if und else aufgebaut ist. So funktioniert er:

Bedingung ? Erfüllt : Nicht erfüllt

Dazu möchte ich ein Beispiel zu der Verwendung dieses Operators geben.

#include <stdio.h>

int main(int argc, char *argv[])
{
        int x, ergebnis;
        printf("Zahl eingeben: ");
        scanf("%d", &x);
        ergebnis = x>100 ? 100 : x;
        printf("ergebnis = %d\n", ergebnis);
}

In der printf-Anweisung wird keine Zahl über 100 ausgegeben. Die Zeile ergebnis = x>100 ? 100 : x; entspricht hier also dem folgenden if-Konstrukt:

if (x>100)
        ergebnis=100;
else
        ergebnis=x;

5.5 Aufgabe: Entscheidungen
Schreibe einen Rechner, der beim Starten folgendes Menü anzeigt:

Welche Operation möchtest du durchführen?
1. Addition
2. Subtraktion
3. Multiplikation
4. Division
5. Programm beenden

Wenn z.B. 1 eingegeben wurde, wird folgendes angezeigt (Beispiel; das fette ist das, was eingegeben wird):

Eingabe: 1
Gib die 1. Zahl ein: 3
Gib die 2. Zahl ein: 4
Ergebnis: 3 + 4 = 7

Anschliessend wird das Programm beendet. Zeige bei der Division zusätzlich den Rest (nur, wenn vorhanden!). Wenn 1 bis 5 eingegeben wird, wird das Programm mit der folgenden Meldung beendet:

Vielen Dank für das Benutzen dieses Programms!

Wird eine Zahl unter 1 oder über 5 eingegeben, gibt das Programm folgende Fehlermeldung aus und beendet sich:

Falsche Eingabe!

Verwende if, else, switch und ? : und vermeide soweit wie möglich sich wiederholende printf-Anweisungen. Die Lösung gibts hier.

 

6 Schleifen

6.1 Schleifen mit for
Nehmen wir einmal an, wir wollen, dass unser Programm die Zahlen 1 bis 10 ausgibt. Mit unserem Programmierwissen müssten wir folgende Anweisungen verwenden:

printf("1");
printf("2");
printf("3");
printf("4");
printf("5");
printf("6");
printf("7");
printf("8");
printf("9");
printf("10");

Wie du sicherlich schon vermutest, lässt sich das mit einer Schleife lösen. Dann schauen wir uns jetzt die for-Schleife an. Die Schleife ist folgendermassen aufgebaut;

for (Initialisierung ; Bedingung ; Reinitialisierung)
{
        Anweisungen
}

Die Anweisungen in der Schleife werden solange ausgeführt, bis die Bedingung im Kopf der Schleife wahr wird. Dann wird die Schleife verlassen. Nach jedem Durchlauf der Schleife, kann einer Variable ein Wert zugewiesen werden. Hier ein Beispiel, wie man das obere Programm lösen könnte:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i;
        for (i=1 ; i<=10 ; i++)
        {
                printf("%d\n", i);
        }
        return 0;
}

Nicht so schwer, oder? Vor dem Eintreten der Schleife wird i mit 1 initialisiert. Beim ersten Durchlauf ist i also 1, was man an der printf-Anweisung erkennt. Danach wird i++ auusgeführt. Das bewirkt, dass (siehe 4.4) i um eins erhöht wird. Die Anweisung printf zeigt uns also korrekt den Wert 1 an. Beim zweiten Durchlauf wird i wieder um eins erhöht. Weil die Bedingung immer wahr ist, wird die Schleife ausgeführt, bis i 11 ist. Dann ist die Bedingung nicht mehr wahr und die Schleife wird verlassen.

6.2 Schleifen mit while
while-Schleifen sind folgendermassen aufgebaut:

while (Bedingung)
{
        Anweisungen
}

Weil wir die for-Schleife ja schon können, können wir uns while als folgende for-Schleife vorstellen:

for ( ; Bedingung ; )
{
        Anweisungen
}

Das bedeutet also, dass die Anweisungen bei while so lange ausgeführt werden, bis die Bedingung falsch ist.

Neben dieser einfachen while-Schleife gibt es noch eine do while-Schleife:

do
{
        Anweisungen
} while ( Bedingung );

Der Unterschied zur normalen while-Schleife ist, dass hier die Anweisungen in der Schleife mindestens ein mal ausgeführt werden. Die Bedingung wird erst nach dem Verarbeiten der Anweisungen überprüft. Ist sie wahr, werden die Anweisungen erneut ausgeführt etc., wenn nicht, dann wird die Schleife verlassen.

Hier ein Beispiel zur Verwendung der do while-Schleife. Das Programm sollte keine Probleme beim Verstädnis haben:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int x;
        int ergebnis=0;

        printf("Gib 0 zum Beenden ein\n");

        do
        {
                printf("Zahl: ");
                scanf("%d", &x);
                ergebnis+=x;
                printf("Ergebnis: %d\n", ergebnis);
        } while (x != 0);
}

6.3 Springen mit goto
Die goto-Anweisung sollte wenn möglich vermieden werden. Ich führe sie hier nur vollständigkeitshalber auf. Mit goto kann man in eine (nicht ganz) beliebige Stelle des Codes springen. Das kann nützlich sein, wenn man in vielen Schleifen und Abfragen drinn ist und die mit einem Sprung verlassen möchte. Aber warum sollte man goto nicht verwenden? Mit goto wird der Ablauf des Programms unterbrochen, was sich auf die Performance auswirkt. Ausserdem: Was passiert mit den lokalen Variablen eines Anweisungsblocks, in den gerade gesprungen wird? Das Verhalten ist Systemabhängig! Hier die Verwendung von goto:

/* ... */
goto Label;
/* ... */
Label:
/* ... */

Wie man sehen kann springt hier die goto-Anweisung bis zur Stelle, die mit Label: markiert ist. Ein Beispiel werde ich mir hier sparen, weil ich von goto abrate.

 

7 Funktionen

7.1 Einleitung
Auf Funktionen bin ich schon im Kapitel 2.2 eingegangen. Ich werde sie hier jedoch nochmals genau und ausführlich beschreiben, weil du bis jetzt schon einige Grundlagen von C kannst. Lies trotzdem den Abschnitt 2.2, wenn du Probleme hast.

7.2 Eigene Funktionen
Ich fange sofort mit einem Beispielprogramm an:

#include <stdio.h>

void ausgabe(void)
{
        printf("Die Funktion ausgabe wurde aufgerufen!\n");
}

int main(int argc, char *argv[])
{
        printf("start\n");
        ausgabe();
        printf("ende\n");
}

Hier sehen wir zwei Funktionen: die Funktion main, die am Anfang aufgerufen wird und die eigene Funktion ausgabe. Ich habe die Funktion absichtlich ganz oben aufgeführt, weil sie der Compiler ansonsten nicht "sieht" und eine Warnung oder einen Fehler ausgibt. Der Compiler ist nur dann zufrieden, wenn er die Funktion (bzw. deren Prototyp) vor dem ersten Aufruf sieht. Das Programm liefert folgende Ausgabe:

start
Die Funktion ausgabe wurde aufgerufen!
ende

Ich habe vorher das Wort "Prototyp" verwendet. Der Prototyp einer Funktion ist nichts anderes als deren Funktionskopf, in unserem Beispiel void ausgabe(void), mit einem Semikolon am Schluss, um ihn von der eigentlichen Funktion zu unterscheiden. Wenn wir die Funktion unter die main-Funktion schreiben, müssen wir den Prototyp oberhalb der main-Funktion schreiben:

#include <stdio.h>

/* der Compiler kennt jetzt die Funktion ausgabe */
void ausgabe(void);

int main(int argc, char *argv[])
{
        printf("start\n");
        ausgabe();
        printf("ende\n");
}

void ausgabe(void)
{
        printf("Die Funktion ausgabe wurde aufgerufen!\n");
}

Was bedeuted aber das Wort void im Funktionskopf? Nichts. Das Wort void zeigt an, dass die Funktion weder etwas zurückliefert (void am Anfang des Kopfes) noch Argumente erwartet (void in den Klammern). Im nächsten Abschnitt werden wir Funktionen mit Argumenten behandeln.

7.3 Funktionen mit Argumenten
Zuerst das Beispiel, dann die Erklärung:

void auswertung(int zahl);

int main(int argc, char *argv[])
{
        int eingabe = 0;
        printf("Gib die Zahl ein:\n");
        scanf("%d", &eingabe);
        auswertung(eingabe);
}

void auswertung(int zahl)
{
        printf("Eingegeben wurde: %d\n", zahl);
}

Dieses Beispiel könnte auf den ersten Blick komisch aussehen. Nachdem du es kompiliert und ausgeführt hast, erscheint die eingegebene Zahl. Warum muss die Variable zahl in der Funktion auswertung nicht auch eingabe heissen? Weil jede Funktion eigene Variablen haben darf und nicht direkt auf die von anderen Funktionen zugreifen kann! In der Zeile auswertung(eingabe); wird die Variable eingabe an die Funktion auswertung übergeben. Die Funktion auswertung kann dieser Variable einen eigenen Namen geben, hier zahl, und die Variable dann unter diesem Namen ansprechen. Sie kann aber nicht direkt auf die Variable eingabe der Funktion main zugreifen! Man muss also alle Variablen, die die Funktion benötigt, übergeben. Die folgende Skizze verdeutlicht das:

auswertung(eingabe);
              |
              `-----.
                   \|/
void auswertung(int zahl)
{                    |
                     `--------------------.
                                         \|/
        printf("Eingegeben wurde: %d\n", zahl);
}

Die Funktion auswertung kann aber durchaus eine eigene Variable eingabe haben, welche aber nichts mit der in der Funktion main zu tun hat:

void auswertung(int zahl)
{
	int ergebnis = 123;
        printf("Eingegeben wurde: %d; Ergebnis = %d\n", zahl, ergebnis);
}

Falls du alles verstanden hast, können wir weitermachen mit Funktionen, die mehrere Argumente erfordern.

Das funktioniert im Prinzip ganz einfach. Die Argumente werden mit Komma getrennt angegeben. Dazu ein Beispiel mit drei Argumenten:

void zahlen_addieren(int a, int b, int c);

int main(int argc, char *argv[])
{
	zahlen_addieren(100, 20, 3);
	zahlen_addieren(721, 9, 10);
	return 0;
}

void zahlen_addieren(int a, int b, int c)
{
        printf("%d + %d + %d = %d\n", a, b, c, a + b + c);
}

7.4 Funktionen mit Rückgabewert
Oft ist es sinnvoll, dass eine Funktion den Wert zurückgibt, damit ihn die aufrufende Funktion verwenden kann. Dies geschieht mit der Anweisung return. return verlässt die Funktion und kehrt zur vorherigen Funktion zurück. Ein return ohne Argumente kann auch bei void-Funktionen, also Funktionen, welche keinen Wert zurückgeben, verwendet werden. Am Anfang der Funktion muss der Rückgabewert definiert sein, int blah(void) ist also eine Funktion, die einen int-Wert zurückliefert (aber keine Argumente hat wegen dem void in den Klammern). Dazu ein Beispiel:

int zahlen_addieren(int a, int b, int c);

int main(int argc, char *argv[])
{
        int ergebnis = zahlen_addieren(100, 20, 3);
	printf("ergebnis = %d\n", ergebnis);
        return 0;
}

int zahlen_addieren(int a, int b, int c)
{
        int summe;
        summe = a + b + c;
        return summe;
}

Die Funktion main erhält hier also den Wert, den die Funktion zahlen_addieren zurückliefert.

7.5 Gültigkeitsbereich von Variablen
In diesem Abschnitt wird auf den Gültigkeitsbereich bzw. die Lebensdauer von Variablen eingegangen. Wie du sicher schon bemerkt hast, sind die Variablen, die wir bisher deklariert haben nur in der jeweiligen Funktion gültig. Es ist also unmöglich von der Funktion main direkt auf die Variablen einer anderen Funktion zuzugreifen.

Nun, man unterscheidet grundsätzlich zwischen folgenden drei Typen von Variablen:

Daneben gibt es noch Zeiger auf dynamisch allozierte Variablen, auf welche aber in diesem Kapitel noch nicht eingegangen wird.

Die Variablen die wir bisher verwendet haben, waren lokale Variablen, denn sie waren nur in einer Funktion gültig. Dass lokale Variablen nur innerhalb der Funktion gültig sind und beim Verlassen der Funktion "absterben", wollte ich mit diesem Beispiel zeigen:

void beispiel(int num)
{
        int test;
        printf("test (uninitialisiert): %d\n", test);
        test = num;
        printf("test (initialisiert): %d\n", test);
}
int main(int argc, char *argv[])
{
        beispiel(1);
        beispiel(2);
        beispiel(3);
}

Das Programm liefert bei mir die folgende Ausgabe (je nach Computer kann die Ausgabe der uninitialisierten Variable anders sein):

test (uninitialisiert): -809511240
test (initialisiert): 1
test (uninitialisiert): 1
test (initialisiert): 2
test (uninitialisiert): 2
test (initialisiert): 3

Nun, man könnte vermuten, dass die Variablen bei einem erneuten Aufruf der Funktion bestehen bleiben, aber das ist in diesem Beispiel nur Zufall und man sollte immer davon ausgehen, dass uninitialisierte Variablen einen zufälligen Wert haben können.

Ich habe bisher immer von Funktionen gesprochen. Man kann aber auch innerhalb der Funktion Anweisungsblöcke erstellen, die dann wieder eigene lokale Variablen haben. Im Grunde genommen befinden sich die Anweisungen einer Funktion auch in einem Anweisungsblock, denn Anweisungsblöcke beginnen mit einem { und enden mit einem }. Dazu habe ich ein Beispiel gemacht:

void funktion(void)
{ // erster Anweisungsblock
        int var_1 = 10; // var_1 ist 10

        printf("1. var_1 = %d\n", var_1);

        { // zweiter Anweisungsblock
                // var_1 wird erneut deklariert. Wollen wir nun innerhalb
                // dieses Blockes auf var_1 zugreifen, wird auf die jetzt
                // deklarierte Variable zugegriffen und nicht auf die weiter
                // oben deklarierte Variable.
                int var_1 = 123;

                int var_2 = 1; // var_2 ist 1

                printf("2. var_1 = %d var_2 = %d\n", var_1, var_2);

                var_1 = 1; // var_1 ist jetzt 1

                printf("3. var_1 = %d var_2 = %d\n", var_1, var_2);
        } // ende des zweiten Anweisungsblockes

        // var_2 ist jetzt ungültig, folgendes gäbe einen Fehler:
        // var_2 = 2;

        // var_1 ist immer noch 10, weil beim Verlassen des Anweisungsblockes
        // die lokalen Variablen "zerstört" werden und var_1 wieder die
        // ursprünglich deklarierte Variable ist

        printf("4. var_1 = %d\n", var_1);

} // ende des ersten Anweisungsblockes; var_1 ist jetzt auch ungültig

Das Programm sollte die folgende Ausgabe liefern:

1. var_1 = 10
2. var_1 = 123 var_2 = 1
3. var_1 = 1 var_2 = 1
4. var_1 = 10

Die Ausgabe sollte anhand der Kommentare nachvollziehbar sein.

 

A Lösungen zu den Übungen

A.1 Variablen und Operatoren
Hier ist der Quellcode des Programms:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int a,b,c;

        a=13;
        b=-300;

        c=(a+b)*a;
        
        c+=2;

        printf("\"%d addiert mit %d und multipliziert mit %d gibt %d\"\n", \
        a, b, a, c);
        
        return 0;
}

Das Programm sollte folgende Zeile ausgeben:

"13 addiert mit -300 und multipliziert mit 13 gibt -3729"

A.2 Entscheidungen
Hier ist der Quellcode des Programms:

#include <stdio.h>

int main(int argc, char *argv[])
{
        int x,z1,z2,ergebnis;
        
        printf("Welche Operation möchtest du durchführen?\n");
        printf("1. Addition\n");
        printf("2. Subtraktion\n");
        printf("3. Multiplikation\n");
        printf("4. Division\n");
        printf("5. Programm beenden\n");
        scanf("%d", &x);
        
        if (x>4?0:x<1?0:1)
        {
                printf("Eingabe: %d\n", x);
                printf("Gib die 1. Zahl ein: ");
                scanf("%d", &z1);
                printf("Gib die 2. Zahl ein: ");
                scanf("%d", &z2);
        
                printf("Ergebnis: ");
                switch(x)
                {
                        case 1:
                                ergebnis=z1+z2;
                                printf("%d + %d = %d\n", z1, z2, ergebnis);
                                break;
                        case 2:
                                ergebnis=z1-z2;
                                printf("%d - %d = %d\n", z1, z2, ergebnis);
                                break;
                        case 3:
                                ergebnis=z1*z2;
                                printf("%d * %d = %d\n", z1, z2, ergebnis);
                                break;
                        case 4:
                                ergebnis=z1/z2;
                                printf("%d / %d = %d", z1, z2, ergebnis);
                                if (z1%z2!=0)
                                {
                                        ergebnis=z1%z2;
                                        printf(" Rest %d", ergebnis);
                                }
                                printf("\n");
                }
        }
        else
        {
                if (x!=5)
                {
                        printf("Falsche Eingabe!\n");
                        return 1;
                }
        }
        printf("Vielen Dank für das Benutzen dieses Programms!\n");
        return 0;
}

Wenn dir die Zeile if (x>4?0:x<1?0:1) Probleme bereitet, dann zerleg sie mal auseinander:

if (  (x>4) ? 0 : ( (x<1) ? 0 : 1 )  )

Wenn du weisst, wie der Operator ? : funktioniert, dann solltest du diese Zeile jetzt verstehen können.


Zurück zur Hauptseite
 
Letzte Änderung: 20.03.04
Copyright (C) 2003, 2004 by Thomas "tom" S. <tom at eggdrop.ch>
Valid HTML 4.0!

Dieser Text gehört zu www.eggdrop.ch und ist urheberrechtlich geschützt. Das Weitergeben und Kopieren dieses Textes ist erwünscht unter folgenden Bedingungen: 1. Der Text muss so sein wie er ist und darf in keiner Weise verändert oder in die eigene Homepage integriert werden. 2. Es muss ein deutlicher Hinweis auf die Herkunft (www.eggdrop.ch) vorhanden sein. 3. Der Text sollte wenigstens alle 3 Monate auf Updates überprüft werden (siehe die Versionsnummer ganz am Anfang des Textes!)