U S E R - A R C H I V

Seite: Programmieren in C / C++

|Programmieren in C, C++ und turbo C++ - Tutorials, Einführungen, programming, libraries und Onlineliteratur

Programmieren in C++

Online-Literatur zum Thema Programmieren in C und C++ (Tutorials, Einführungen, Kurse, FAQs)

Einführungen/Tutorials

http://de.wikibooks.org/wiki/C%2B%2B-Programmierung
Rubrik: Programmieren; Typ: Tutorials
http://www.mathematik.uni-marburg.de/%7Ecpp/
Rubrik: Programmieren; Typ: Tutorials
http://www.cpp-programming.de/
Rubrik: Programmieren; Typ: Tutorials
http://www.cpp-tutor.de
Rubrik: Programmieren; Typ: Tutorials
http://cpp.pletschette.net/
Rubrik: Programmieren; Typ: Tutorials
http://www.cpp-entwicklung.de/
Rubrik: Programmieren; Typ: Tutorials
http://www.red-inferno.de
Rubrik: Programmieren; Typ: Tutorials
http://www.ba-stuttgart.de/%7Ekfg/cpp/cpp.pdf
Rubrik: Programmieren; Typ: Tutorials
http://www.buva.sowi.uni-bamberg.de/%7Ead/how2C/
Rubrik: Programmieren; Typ: Tutorials
http://cpp.solars.de
Rubrik: Programmieren; Typ: Tutorials
http://tutorial.schornboeck.net/inhalt.htm
Rubrik: Programmieren; Typ: Tutorials
http://www.mut.de/media/buecher/vcplus6/data/start.htm
Rubrik: Programmieren; Typ: Tutorials
http://home.wtal.de/ranzurmall/visualc
Rubrik: Programmieren; Typ: Tutorials
http://www.heise.de/ix/artikel/2001/08/143/
Rubrik: Programmieren; Typ: Tutorials
http://www.softgames.de/developia/viewarticle.php?cid=19547
Rubrik: Programmieren; Typ: Tutorials
http://www.onlinetutorials.de/c-index.htm
Rubrik: Programmieren; Typ: Tutorials
http://www.onlinetutorials.de/cpp-005.htm
Rubrik: Programmieren; Typ: Tutorials
http://www.online-tutorial.de/?meineTutorials=CPlusPlus
Rubrik: Programmieren; Typ: Tutorials
http://www.3000interaktiv.de/files/tutorials/MD3.zip
Rubrik: Programmieren; Typ: Tutorials
http://www.cpp-tutor.de/cpp/index.html
Rubrik: Programmieren; Typ: Tutorials
http://www.highscore.de/cpp/einfuehrung/
Rubrik: Programmieren; Typ: Tutorials
http://www.highscore.de/cpp/einfuehrung/
Rubrik: Programmieren; Typ: Tutorials
http://www.mathematik.uni-marburg.de/~cpp/
Rubrik: Programmieren; Typ: Tutorials
http://www.volkard.de/vcppkold/inhalt.html
Rubrik: Programmieren; Typ: Tutorials
http://www.red-inferno.de/
Rubrik: Programmieren; Typ: Tutorials
http://www.home.unix-ag.org/martin/c++.ring.buch.html
Rubrik: Programmieren; Typ: Tutorials



Sonstige Online-Literatur

http://de.wikibooks.org/wiki/Qt_f%C3%BCr_C%2B%2B-Anf%C3%A4nger
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://de.wikibooks.org/wiki/C%2B%2B-Referenz
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://de.wikibooks.org/wiki/C%2B%2B-Programmierung
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://de.wikibooks.org/wiki/Arbeiten_mit_.NET:_Grundlagen:_C%2B%2B_f%C3%BCr_.NET
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.nickles.de/c/s/21-0007-124-1.htm
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.it-academy.cc/article/1435/ArrayList+in+C++.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.it-academy.cc/article/1345/Binaere+Darstellung+in+C+C++.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.it-academy.cc/article/274/c++.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.it-academy.cc/article/1295/C+++Grundlagen.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.it-academy.cc/article/889/InlineAssembler%28x86%29+in+C+C++.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.plog4u.de/index.php/Benutzung:Eclipse:FAQ_CDT
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.pythonwiki.de//PythonErweitern
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://wwwai.wu-wien.ac.at/%7Ehahsler/CPPAG/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.dregenprogs.de
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www-users.rwth-aachen.de/Jan.Herling/builder/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.chaho.de
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.informatik.uni-leipzig.de/%7Epantec/khepera/libkhepera/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.ini-sc.net/mswin/awfcsem/inhalt.htm
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.pl-berichte.de/work/kde/index.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.informatik.hs-bremen.de/%7Ebrey/stlb.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.nickles.de//c/s/21-0007-124-1.htm
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.datasource.de/programmierung/toolbox_cpp_01.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.datasource.de/programmierung/toolbox_cpp_02.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.datasource.de/programmierung/toolbox_cpp_03.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.cpp-tutor.de/mfc/index.htmlhttp://www.heise.de/ix/artikel/2001/08/143/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.highscore.de/cpp/aufbau/index.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.buva.sowi.uni-bamberg.de/~ad/how2C/progMeth.pdf
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.buva.sowi.uni-bamberg.de/~ad/how2C/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.ges-training.de/cgi-bin/sem.pl?tip=6
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.comnets.rwth-aachen.de/doc/handout/handout.html
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.ianywhere.com/developer/product_manuals/sqlanywhere/0901/de/html/dbpgde9/dbpgde9.htm
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.zehelein.de/
Rubrik: Programmieren; Typ: Sonstige_Literatur
http://www.ifs.uni-linz.ac.at/~sre/c++/
Rubrik: Programmieren; Typ: Sonstige_Literatur



FAQs




Referenzsammlungen

http://www-home.fh-konstanz.de/~bittel/prog/style.pdf
Rubrik: Programmieren; Typ: Referenzen
http://www.datasource.de/programmierung/tab33_cppdatentypen.html
Rubrik: Programmieren; Typ: Referenzen
http://www.datasource.de/programmierung/tab33_cppdatentypen.html
Rubrik: Programmieren; Typ: Referenzen
http://cpp.programmersbase.net/
Rubrik: Programmieren; Typ: Referenzen

Beispielssammlungen bzw. Klassenbibliotheken

http://www.bcb-box.de/
Rubrik: Programmieren; Typ: Skriptarchiv
http://www.c-worker.ch/
Rubrik: Programmieren; Typ: Skriptarchiv
http://kingleo.home.pages.at/index_cpp.xml.php?language=DE&show=/development/cpp
Rubrik: Programmieren; Typ: Skriptarchiv
http://www.uni-koeln.de/rrzk/software/grafik/visualization/vtk/
Rubrik: Programmieren; Typ: Skriptarchiv
http://www.tchaikine.de/
Rubrik: Programmieren; Typ: Skriptarchiv
http://www.open-sources.de/
Rubrik: Programmieren; Typ: Skriptarchiv

Portale

http://www.c-plus-plus-archiv.de/
Rubrik: Programmieren; Typ: Portal
http://cplus.kompf.de/
Rubrik: Programmieren; Typ: Portal
http://www.c-plusplus.de/
Rubrik: Programmieren; Typ: Portal
http://www.cplusplus-forum.de/
Rubrik: Programmieren; Typ: Portal
http://cppwebring.toosten.de/
Rubrik: Programmieren; Typ: Portal
http://www.cbuilder.de/
Rubrik: Programmieren; Typ: Portal
http://toosten.de/index.php?seite=tutorials
Rubrik: Programmieren; Typ: Portal
http://www.wikiservice.at/dse/wiki.cgi?StartSeite
Rubrik: Programmieren; Typ: Portal
http://programmers-club.de
Rubrik: Programmieren; Typ: Portal

Bücher bei Amazon


Diskussionsforum zum Thema Programmieren in C

http://www.programmiersprachen.de/wbb2/board.php?boardid=11
Rubrik: Programmieren; Typ: Diskussionsforen
http://www.programmiersprachen.de/wbb2/board.php?boardid=78
Rubrik: Programmieren; Typ: Diskussionsforen
http://www.easy-coding.de/c-cpp-f6.html
Rubrik: Programmieren; Typ: Diskussionsforen
http://www.spotlight.de/zforen/cpp/t/forum_cpp_1.html
Rubrik: Programmieren; Typ: Diskussionsforen
http://www.digital-inn.de/forumdisplay.php?f=101
Rubrik: Programmieren; Typ: Diskussionsforen
http://www.paules-pc-forum.de/phpBB2/index.php
Rubrik: Programmieren; Typ: Diskussionsforen

ebay

Aus der User-Archiv Datenbank: Alle

Alle Einträge zum Suchbegriff C++
(Tutorials, Einführungen, Kurse, FAQs, Bugsammlungen, Diskussionsforen, Referenzen, Portale, Onlinetools)
http://www.microsoft.com/germany/msdn/rss/DC_Cpp.xml
Rubrik: Sonstige Themen; Typ: RSS-Feeds

Bücher bei Amazon


C-Programmierung: Kompilierung

http://de.wikibooks.org/wiki/C-Programmierung:_Kompilierung

Um C-Programme ausführen zu können, müssen diese erst in die Maschinensprache übersetzt werden. Diesen Vorgang nennt man kompilieren.

Anschließend wird die beim Kompilieren entstandene Objektcode mit einem Linker gelinkt, so dass alle eingebundenen Bibliotheksfunktionen verfügbar sind. Das gelinkte Produkt aus einer oder verschiedenen Objektcode-Dateien und den Bibliotheken ist dann das ausführbare Programm.

Compiler

Um die erstellten Code-Dateien zu kompilieren, benötigt man selbstverständlich auch einen Compiler. Je nach Plattform hat man verschiedene Alternativen:


Microsoft Windows

Wer zu Anfang nicht all zu viel Aufwand betreiben will, kann mit relativ kleinen Compilern (ca. 2-5 MByte) inkl. IDE/Editor anfangen:

Wer etwas mehr Aufwand (finanziell oder an Download) nicht scheut, kann zu größeren Paketen inkl. IDE greifen:

Wer einen (kostenlosen) Kommandozeilen-Compiler bevorzugt, kann zusätzlich zu obigen noch auf folgende Compiler zugreifen:


Unix und Linux

Für alle Unix Systeme existieren C-Compiler, die meist auch schon vorinstalliert sind. Insbesondere, bzw. darüber hinaus, existieren folgende Compiler:


Macintosh

Seit Mac OS X alle genannten Unix- und Linux-Compiler und außerdem:

Neben diesen gibt es noch zahllose andere C-Compiler, von optimierten Intel- oder AMD-Compilern bis hin zu Compilern für ganz exotische Plattformen. Microsoft Visual Studio (.NET) ist die Entwicklungsumgebung von Microsoft und enthält neben dem C-Compiler auch Compiler für C#, C++ und VisualBasic.


GNU C Compiler

Der GNU C Compiler, Teil der GCC (GNU Compiler Collection), ist wohl der populärste Open-Source-C-Compiler und ist für viele verschiedene Plattformen verfügbar. Er ist in der GNU Compiler Collection enthalten und der Standard-Compiler für GNU/Linux und die BSD-Varianten.

Compileraufruf: gcc Quellcode.c -o Programm

Der GCC kompiliert und linkt nun die "Quellcode.c" und gibt es als "Programm" aus. Das Flag -c sorgt dafür, dass nicht gelinkt wird und bei -S wird auch nicht assembliert. Der GCC enthält nämlich einen eigenen Assembler, den GNU Assembler, der als Backend für die verschiedenen Compiler dient. Um Informationen über weitere Parameter zu erhalten, verwenden Sie bitte man 1 gcc.


Microsoft Visual Studio

Die Microsoft Entwicklungsumgebung enthält eine eigene Dokumentation und ruft den Compiler nicht über die Kommandozeile auf, sondern ermöglicht die Bedienung über ihre Oberfläche.

Bevor Sie allerdings mit der Programmierung beginnen können, müssen Sie ein neues Projekt anlegen. Dazu wählen Sie in den Menüleiste den Eintrag "Datei" und "Neu..." aus. Im folgenden Fenster wählen Sie im Register "Projekte" den Eintrag "Win32-Konsolenanwendung" aus und geben einen Projektnamen ein. Verwechseln Sie nicht den Projektnamen mit dem Dateinamen! Die Endung .c darf hier deshalb noch nicht angegeben werden. Anschließen klicken Sie auf "OK" und "Fertigstellen" und nochmals auf "OK".

Nachdem Sie das Projekt erstellt haben, müssen Sie diesem noch eine Datei hinzufügen. Rufen Sie dazu nochmals den Menüeintrag "Datei" - "Neu..." auf und wählen Sie in der Registerkarte "Dateien" den Eintrag "C++ Quellcodedateien" aus. Dann geben Sie den Dateinamen ein, diesmal mit der Endung .c und bestätigen mit "OK". Der Dateiname muss nicht gleich dem Projektname sein.

In Visual Studio 6 ist das Kompilieren im Menü "Erstellen" unter "Alles neu erstellen" möglich. Das Programm können Sie anschließend in der "Eingabeaufforderung" von Windows ausführen.

Programmiersprache C Sprachbeschreibung: Anweisungen

Aus Wikibooks http://de.wikibooks.org/wiki/C_Sprachbeschreibung:_Anweisungen



Anweisungen

Diese Darstellungen stützen sich auf die Sprache C gemäß ISO/IEC 9899:1999 (C99). Auf Dinge, die mit C99 neu eingeführt wurden, wird im Folgenden gesondert hingewiesen.

Anweisungen und Blöcke sind Thema des Kapitels 6.8 Statements and blocks in C99.


Benannte Anweisung

Benannte Anweisungen sind Thema des Kapitels 6.8.1 Labeled statements in C99.

Syntax:

Bezeichner : Anweisung
case konstanter Ausdruck : Anweisung
default : Anweisung

Sowohl die case-Anweisung, wie auch die default-Anweisung dürfen nur in einer switch-Anweisung verwendet werden.

Siehe auch: switch.

Der Bezeichner der benannten Anweisung kann in der gesamten Funktion angesprochen werden. Sein Gültigkeitsbereich ist die Funktion. Dies bedeutet, dass der Bezeichner in einer goto-Anweisung noch vor seiner Deklaration verwendet werden kann.

Siehe auch: goto, if


Zusammengesetzte Anweisung

Das Kapitel 6.8.2 Compound statement in C99 hat die zusammengesetzten Anweisungen zum Thema.

Syntax (C89):

{ declaration-listopt statement-listopt }

declaration-list:

Deklaration
declaration-list Deklaration

statement-list:

Anweisung
statement-list Anweisung

Syntax (C99):

{ block-item-listopt }

block-item-list:

block-item
block-item-list block-item

block-item:

Deklaration
Anweisung

Eine zusammengesetzte Anweisung bildet einen Block.

Zusammengesetzte Anweisungen dienen dazu, mehrere Anweisungen zu einer einzigen Anweisung zusammenzufassen. So verlangen viele Anweisungen eine Anweisung als Unteranweisung. Sollen jedoch mehrere Anweisungen als Unteranweisung angegeben werden, so steht oft nur der Weg zur Verfügung, diese Anweisungen als eine Anweisung zusammenzufassen.

Wesentliches Merkmal der Syntax zusammengesetzter Anweisungen sind die umschließenden geschweiften Klammern ({}). Bei Anweisungen, die Unteranweisungen erwarten, wie beispielsweise Schleifen oder Verzweigungen, werden geschweifte Klammern so häufig eingesetzt, dass leicht der falsche Eindruck entsteht, sie seien Teil der Syntax der Anweisung. Lediglich die Syntax einer Funktionsdefinition verlangt (in C99) die Verwendung einer zusammengesetzen Anweisung.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
printf("Es wurden %d Parameter angegeben.\n", argc-1);
printf("Der erste Parameter ist '%s'.\n", argv[1]);

return 0;
}

Das eben gezeigte Beispiel lässt sich übersetzen, jedoch ist sein Verhalten nicht das Gewünschte. Der erste Parameter der Applikation soll nur ausgegeben werden, wenn er angegeben wurde. Jedoch wird nur die erste printf-Anweisung (eine Ausdrucksanweisung) bedingt ausgeführt. Die zweite printf-Anweisung wird stets ausgeführt, auch wenn die Formatierung des Quelltextes einen anderen Eindruck vermittelt. Repräsentiert der Ausdruck argv[1] keinen gültigen Zeiger, so führt seine Verwendung beim Aufruf der Funktion printf zu einem undefinierten Verhalten der Applikation.

Siehe auch: if

Es soll also auch der zweite Aufruf von printf nur dann erfolgen, wenn mindestens ein Parameter angegeben wurde. Dies kann erreicht werden, indem beide (Ausdrucks-)Anweisungen zu einer Anweisung zusammengesetzt werden. So arbeitet das folgende Beispiel wie gewünscht.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
{
printf("Es wurden %d Parameter angegeben.\n", argc-1);
printf("Der erste Parameter ist '%s'.\n", argv[1]);
}

return 0;
}

In zusammengesetzten Anweisungen können neue Bezeichner deklariert werden. Diese Bezeichner gelten ab dem Zeitpunkt ihrer Deklaration und bis zum Ende des sie umschließenden Blocks. Die wesentliche Änderung von C89 zu C99 ist, dass in C89 alle Deklarationen vor allen Anweisungen stehen mussten. Im aktuellen Standard C99 ist dieser Zwang aufgehoben.


Ausdrucksanweisung

Ausdrucksanweisungen werden im Kapitel 6.8.3 Expression and null statements in C99 beschrieben.

Syntax:

Ausdruckopt ;

Der Ausdruck einer Ausdrucksanweisung wird als void-Ausdruck und wegen seiner Nebeneffekte ausgewertet. Alle Nebeneffekte des Ausdrucks sind zum Ende der Anweisung abgeschossen.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
int a = 1, b = 2, c; /* Deklarationen */

c = a + b; /* Ausdrucksanweisung */
printf("%d + %d = %d\n",a,b,c); /* Ausdrucksanweisung */

return 0; /* Sprung-Anweisung */
}

In der ersten Ausdrucksanweisung c = a + b; wird der Ausdruck c = a + b mit seinem Teilausdruck a + b ausgewertet. Als Nebeneffekt wird die Summe aus den Werten in a und b gebildet und in dem Objekt c gespeichert. Der Ausdruck der zweiten Ausdrucksanweisung ist der Aufruf der Funktion printf. Als Nebeneffekt gibt diese Funktion einen Text auf der Standardausgabe aus. Häufige Ausdrucksanweisungen sind Zuweisungen und Funktionsaufrufe.


Leere Anweisung

Wird der Ausdruck in der Ausdrucksanweisung weggelassen, so wird von einer leeren Anweisung gesprochen. Leere Anweisungen werden verwendet, wenn die Syntax der Sprache C eine Anweisung verlangt, jedoch keine gewünscht ist.

Beispiel:

void foo (char * sz)
{
if (!sz) goto ende;

/* ... */
while(getchar() != '\n' && !feof(stdin))
;

/* ... */
ende: ;
}

Verzweigungen

Die Auswahl-Anweisungen werden in C99 im Kapitel 6.8.4 Selection statements beschrieben.


if

Syntax:

if (Ausdruck) Anweisung
if (Ausdruck) Anweisung else Anweisung

In beiden Formen der if-Anweisung muss der Kontroll-Ausdruck von einem skalaren Datentypen sein. Bei beiden Formen wird die erste Unteranweisung nur dann ausgeführt, wenn der Wert des Ausdruckes ungleich 0 (null) ergibt. In der zweiten Form der if-Anweisung wird die Unteranweisung nach dem Schlüsselwort else nur dann ausgeführt, wenn der Kontrollausdruck den Wert 0 darstellt. Wird die erste Unteranweisung über einen Sprung zu einer benannten Anweisung erreicht, so wird die Unteranweisung im else-Zweig nicht ausgeführt. Das Schlüsselwort else wird stets jenem if zugeordnet, das vor der vorangegangenen Anweisung steht.

Beispiel:

 #include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
printf("Erster Parameter: %s\n", argv[1]);

return 0;
}

Im vorangegangenen Beispiel prüft das Programm, ob mindestens ein Parameter dem Programm übergeben wurde und gibt ggf. diesen Parameter aus. Wurde jedoch kein Parameter dem Programm übergeben, so wird die Ausdrucksanweisung printf("Erstes Argument: %s\n", argv[1]); nicht ausgeführt.

Soll auch auf den Fall eingegangen werden, dass der Kontrollausdruck 0 ergeben hatte, so kann die zweite Form der if-Anweisung verwendet werden. Im folgenden Beispiel genau eine der beiden Unteranweisungen ausgeführt.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if(argc > 1)
printf("Erster Parameter: %s\n", argv[1]);
else
puts("Es wurde kein Parameter übergeben.");

return 0;
}

Die if-Anweisung stellt, wie der Name schon sagt, eine Anweisung dar. Daher kann eine if-Anweisung ebenso als Unteranweisung einer anderen Anweisung verwendet werden. Wird eine if-Anweisung als Unteranweisung einer anderen if-Anweisung verwendet, so ist darauf zu achten, dass sich ein eventuell vorhandenes else stets an das voranstehende if bindet.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
if (argc == 2)
puts("Es wurde genau ein Parameter übergeben.");
else
puts("Es wurde kein Parameter übergeben.");

return 0;
}

Die Formatierung des Quelltextes im letzten Beispiel erweckt den Eindruck, dass der Text Es wurde kein Argument übergeben. nur dann ausgegeben wird, wenn der Ausdruck argc > 1 den Wert 0 ergibt. Jedoch ist Ausgabe des Textes davon abhängig, ob der Ausdruck argc == 2 den Wert 0 ergibt. Somit wird beim Fehlen eines Parameters kein Text ausgegeben und im Fall von mehreren Parametern erhalten wir die Fehlinformation, dass keine Parameter übergeben worden seien.

Soll das gewünschte Verhalten erreicht werden, so kann die if-Anweisung, welche die erste Unteranweisung darstellt, durch eine zusammengesetzte Anweisung ersetzt werden. Noch einmal auf deutsch: sie kann geklammert werden. So findet im folgenden Beispiel das Schlüsselwort else vor sich eine zusammengesetzte Anweisung und vor dieser Anweisung jenes if, welches mit dem Kontrollausdruck argc > 1 behaftet ist.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
{
if(argc == 2)
puts("Es wurde genau ein Parameter übergeben.");
} else
puts("Es wurde kein Parameter übergeben.");

return 0;
}

Ebenso wird die zusammengesetzte Anweisung verwendet, wenn mehrere Anweisungen bedingt ausgeführt werden sollen. Da die if-Anweisung stets nur eine Anweisung als Unteranweisung erwartet, können zum bedingten Ausführen mehrerer Anweisungen, diese wiederum geklammert werden.

Im nächsten Beispiel findet das letzte else als erste Unteranweisung eine if-Anweisung mit einem else-Zweig vor. Diese (Unter-)Anweisung ist abgeschlossen und kann nicht mehr erweitert werden. Daher kann sich an eine solche if-Anweisung das letzte else nicht binden. Es gibt keine Form der If-Anweisung mit zwei Else-Zweigen. Somit arbeitet das folgende Programm auch ohne Klammern wie erwartet.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
if (argc > 1)
if (argc == 2)
puts("Es wurde genau ein Parameter übergeben.");
else
puts("Es wurden mehr als ein Parameter übergeben.");
else
puts("Es wurde kein Argument übergeben.");

return 0;
}

Im letzten Beispiel zum Thema der if-Anweisung soll noch gezeigt werden, wie sich ein Programm verhält, bei dem in eine Unteranweisung über einen Sprung aufgerufen wird.

Beispiel:

#include <stdio.h>

int main(void)
{
goto marke;

if (1==2)
marke: puts("Ich werde ausgegeben.");
else
puts("Ich werde nicht ausgegeben!");

return 0;
}

Obwohl der Ausdruck 1==2 offensichtlich den Wert 0 liefert, wird der else-Zweig nicht ausgeführt.


switch

Die switch-Anweisung wird im Kapitel 6.8.4.2 The switch statement in C99 besprochen.

Syntax:

switch (Ausdruck) Anweisung 

Die switch-Anweisung erlaubt eine Verzweigung mit mehreren Sprungzielen. Ihr Vorteil gegenüber der if-Anweisung ist eine bessere Übersicht über den Programmfluss.

Der Ausdruck muss einen ganzzahligen Datentyp haben. Er ist der Kontrollausdruck. Der ganzzahlige Datentyp des Kontrollausdrucks ist eine Einschränkung gegenüber der if-Anweisung, die in dem Bedingungs-Ausdruck einen skalaren Datentyp verlangt.

Siehe auch: if

Der Wert des Kontrollausdrucks bestimmt, zu welcher case-Anweisung einer zugehörigen Anweisung gesprungen wird. In fast allen Fällen ist die zugehörige Anweisung eine zusammengesetzte Anweisung ({}), welche die verschiedenen case-Anweisungen aufnimmt.

Beispiel:

#include <stdio.h>
#include <stdlib.h>

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

switch(argc)
case 0: puts("Kein Programmname verfügbar.");

switch(argc)
{
case 3: b = atoi (argv[2]);
case 2: a = atoi (argv[1]);
printf("%d + %d = %d\n.", a, b, a+b);
}

return 0;
}

Im letzten Beispiel werden zwei switch-Anweisungen gezeigt. Die erste switch-Anweisung zeigt, dass geschweifte Klammern nach dem Standard C99 nicht zwangsläufig notwendig sind. Jedoch wird in einem solchen Fall wohl eher eine if-Anweisung verwendet werden. In der zweiten Ausgabe von Programmieren in C werden die geschweiften Klammern bei der switch-Anweisung noch verlangt.

Eine case-Anweisung bildet ein mögliches Sprungziel, bei dem die Programmausführung fortgesetzt wird. Die zweite switch-Anweisung im letzten Beispiel definiert zwei case-Anweisungen. Es werden zwei Sprungziele für den Fall definiert, dass argc den Wert 3 bzw. den Wert 2 hat. Der Ausdruck von case muss eine Konstante sein. Dabei darf der Wert des Ausdruck nicht doppelt vorkommen.

switch(var)
{
case 2+3: ;
case 1+4: ; /* illegal */
}

Hat ein case-Ausdruck einen anderen Typen als der Kontrollausdruck, so wird der Wert des case-Ausdruckes in den Typen des Kontrollausdruckes gewandelt.

Wird zu einem der beiden case-Anweisungen gesprungen, so werden auch alle nachfolgenden Anweisungen ausgeführt. Soll die Abarbeitung innerhalb der zugehörigen Anweisung abgebrochen werden, so kann die break-Anweisung eingesetzt werden. So verhindert die Anweisung break; im nachfolgenden Beispiel, dass beide Meldungen ausgegeben werden, wenn kein Parameter beim Programmaufruf angegeben worden sein sollte. Jedoch fehlt eine Anweisung break; zwischen case -1: und case 0:. Dies hat zur Folge, dass in beiden Fällen die Meldung Kein Parameter angegeben ausgegeben wird. Der Ausdruck argc - 1 nimmt den Wert -1 an, wenn auch kein Programmname verfügbar ist.

Beispiel:

#include <stdio.h>

int main (int argc, char *argv[])
{
switch (argc - 1)
{
case -1: case 0: puts("Kein Parameter angegeben");
break;
case 1: puts("Ein Parameter angegeben.");
}
return 0;
}

Siehe auch: break

Ergibt der Bedingungsausdruck einen Wert, zu dem es keinen entsprechenden Wert in einer Case-Anweisung gibt, so wird auch in keinen case-Zweig der zugehörigen Anweisung von switch gesprungen. Für diesen Fall kann eine Anweisung mit der Marke default: benannt werden. Bei der so benannten Anweisung wird die Programmausführung fortgesetzt, wenn kein case-Zweig als Sprungziel gewählt werden konnte. Es darf jeweils nur eine Marke default in einer switch-Anweisung angeben werden.

Siehe auch: Benannte Anweisung


Schleifen

Schleifen (Iterations-Anweisungen) werden im Kapitel 6.8.5 Iteration statements in C99 beschrieben.


while

Die while-Anweisung ist Thema des Kapitels 6.8.5.1 The while statement in C99.

Syntax:

while (Ausdruck) Anweisung 

Der Kontrollausdruck muss von einem skalaren Datentypen sein. Er wird vor einer eventuellen Ausführung der zugehörigen Anweisung (Schleifenrumpf) ausgewertet. Der Schleifenrumpf wird ausgeführt, wenn der Kontrollausdruck einen Wert ungleich 0 ergeben hatte. Nach jeder Ausführung des Schleifenrumpfs wird Kontrollausdruck erneut ausgewertet um zu prüfen, ob mit der Abarbeitung des Schleifenrumpfs fortgefahren werden soll. Erst wenn der Kontrollausdruck den Wert 0 ergibt, wird die Abarbeitung abgebrochen. Ergibt der Kontrollausdruck schon bei der ersten Auswertung den Wert 0, so wird der Schleifenrumpf überhaupt nicht ausgeführt. Die while-Anweisung ist eine kopfgesteuerte Schleife.

Sowohl die while-Anweisung, wie auch deren zugehörige Anweisung (Schleifenrumpf) bilden je einen Block.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
while (argc > 0 && *++argv)
puts(*argv);

return 0;
}

Siehe auch: do, for, Block


do

Die do-Anweisung wird im Kapitel 6.8.5.2 The do statement in C99 beschrieben.

Syntax:

do Anweisung while (Ausdruck);

Im Unterschied zur while-Anweisung wertet die do-Anweisung den Kontrollausdruck erst nach der Ausführung einer zugehörigen Anweisung (Schleifenrumpf) aus. Die Ausführung des Schleifenrumpfs wird solange wiederholt, bis der Kontrollausdruck den Wert 0 ergibt. Dadurch, dass der Kontrollausdruck erst nach der Ausführung des Schleifenrumpfes ausgewertet wird, ist mindestens eine einmalige Ausführung des Schleifenrumpfes garantiert. Die do-Anweisung ist eine fußgesteuerte Schleife.

Sowohl die do-Anweisung, wie auch deren zugehörige Anweisung (Schleifenrumpf) bilden je einen Block.

Siehe auch: while, for, Block

Neben der klassischen Rolle einer fußgesteuerten Schleife bietet sich die do-Anweisung an, wenn ein Makro mit mehreren Anweisungen geschrieben werden soll. Dabei soll das Makro der Anforderung genügen, wie eine Anweisung, also wie im folgenden Codefragment verwendet werden zu können.

#include <stdio.h>
#define makro(sz) do { puts(sz); exit(0); } while(0)

/* ... */
if (bedingung)
makro("Die Bedingung trifft zu");
else
pust("Die Bedingung trifft nicht zu");
/* ... */

Bei der Definition des Makros fehlt das abschließende Semikolon, das in der Syntax der do-Anweisung verlangt wird. Dieses Semikolon wird bei der Verwendung des Makros (in der if-Anweisung) angegeben. Die do-Anweisung ist eine Anweisung und da der Kontrollausdruck bei der Auswertung den (konstanten) Wert 0 ergibt, wird der Schleifenrumpf (zusammengesetzte Anweisung) genau einmal ausgeführt. Zu diesem Thema äußert sich auch das Kapitel 6.3 der FAQ von dclc.


for

Die for-Anweisung ist Thema des Kapitels 6.8.5.3 The for statement in C99.

Syntax:

for (Ausdruck-1opt; Ausdruck-2opt; Ausdruck-3opt) Anweisung 

Syntax (C99):

for (Ausdruck-1opt; Ausdruck-2opt; Ausdruck-3opt) Anweisung
for (Deklaration Ausdruck-2opt; Ausdruck-3opt) Anweisung

Die for-Anweisung (erste Form bei C99) verlangt bis zu drei Ausdrücke. Alle drei Ausdrücke sind optional. Werden die Ausdrücke Ausdruck-1 oder Ausdruck-3 angegeben, so werden sie als void-Ausdrücke ausgewertet. Ihre Werte haben also keine weitere Bedeutung. Der Wert von Ausdruck-2 stellt hingegen den Kontrollausdruck dar. Wird dieser ausgelassen, so wird ein konstanter Wert ungleich 0 angenommen. Dies stellt eine Endlosschleife dar, die nur durch eine Sprunganweisung verlassen werden kann.

Zu Beginn der for-Anweisung wird der Ausdruck-1 ausgewertet. Der Wert von Ausdruck-2 muss von einem skalaren Datentypen sein und sein Wert wird vor einer eventuellen Ausführung der zugehörigen Anweisung (Schleifenrumpf) ausgewertet. Nach einer Ausführung des Schleifenrumpfs wird der Ausdruck-3 ausgewertet. Wird der Schleifenrumpf nicht ausgeführt, so wird auch nicht der Ausdruck-3 ausgewertet.

Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
int i;

for (i = 0; i < 10; ++i)
printf("In der for-Anweisung: i = %2d\n", i);

printf("Nach der for-Anweisung: i = %d\n\n", i);

i = 0;
while ( i < 10)
{
printf("In der while-Anweisung: i = %2d\n", i);
++i;
}

printf("Nach der while-Anweisung: i = %d\n", i);

return 0;
}

Das letzte Beispiel zeigt, wie eine for-Anweisung durch eine while-Anweisung ersetzt werden könnte. Es soll nicht unerwähnt bleiben, dass die beiden Formen nicht das Selbe darstellen. Zum Einen stellt die for-Anweisung eine Anweisung dar, während in dem Beispiel die while-Anweisung von einer Ausdrucksanweisung begleitet wurde. Würden wir beide Anweisungen zusammenfassen, so würden wir einen Block mehr definieren.

Sowohl die for-Anweisung, wie auch deren Schleifenrumpf bilden je einen Block.

Auch wenn alle drei Ausdrücke optional sind, so sind es die Semikola (;) nicht. Die Semikola müssen alle angegeben werden.

Mit C99 ist die Möglichkeit eingeführt worden, eine Deklaration angeben zu können. Dabei ersetzt die Deklaration den Ausdruck-1 und das erste Semikolon. Bei der Angabe der Definition wurde keineswegs die Angabe eines Semikolons zwischen der Deklaration und dem Ausdruck-2 vergessen. In dieser Form ist die Angabe der Deklaration nicht optional, sie muss also angegeben werden. Eine Deklaration wird immer mit einem Semikolon abgeschlossen. Wird diese Form der for-Anweisung verwendet, so ergibt sich das oben fehlende Semikolon durch die Angabe einer Deklaration.

Der Geltungsbereich der mit Deklaration deklarierten Bezeichner umfasst sowohl Ausdruck-2, Ausdruck-3 wie auch Anweisung, dem Schleifenrumpf. Der Bezeichner steht auch innerhalb von Deklaration zur Verfügung, soweit dies nach der Syntax von Deklarationen in C definiert ist.

Beispiel:

#include <stdio.h>

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

/* i und j sind hier nicht verfügbar */

return 0;
}

Siehe auch: while, do, Block


Sprunganweisungen

Die Sprunganweisungen werden im Kapitel 6.8.6 Jump statements von C99 besprochen. Sie haben einen (bedingungslosen) Sprung zu einer anderen Stelle im Programm zur Folge.


goto

Die goto-Anweisung ist Thema des Kapitels 6.8.6.1 The goto statement in C99.

Syntax:

goto Bezeichner ;

Mit der goto-Anweisung kann die Programmausführung bei einer benannten Anweisung fortgesetzt werden. Dabei muss die benannte Anweisung in der gleichen Funktion angegeben worden sein. Da der Name der Anweisung in der gesamten Funktion gültig ist, kann auch "nach vorne" gesprungen werden.

Siehe auch: Benannte Anweisung

Bei einem Sprung in den Geltungsbereich eines Bezeichners darf dieser Bezeichner nicht ein Feld mit variabler Länge bezeichnen. Entsprechend der Regeln für einen Block werden die Objekte angelegt, diese dürfen jedoch nicht initialisiert worden sein, da die Deklaration übersprungen wurde.

Beispiel:

/* Vorsicht! undefiniertes Verhalten */
#include <stdio.h>

int main (void)
{
goto weiter;
{
int i = 99;
weiter:
printf("i = %d\n", i); /* i ist nicht initialisiert! */
}
return 0;
}

In dem letzten Beispiel ist das Objekt i in der Funktion printf nicht initialisiert. Daher ist das Verhalten des Programms undefiniert.

Siehe auch: Block

Die Verwendung der goto-Anweisung hat gelegentlich einen, für den Menschen schwer lesbaren Programmcode zur Folge. Wie hoch die Lesbarkeit eines Quelltextes für einen Menschen als Qualitätsmerkmal für ein Programm bewertet wird, dass für einen Rechner geschrieben wurde, ist individuell verschieden. Dennoch soll nicht verschwiegen werden, dass die goto-Anweisung einen schlechten Ruf besitzt und viele Programmierer von ihrer Verwendung abraten. Dieser sollte jedoch nicht dazu führen, dass auf die goto-Anweisung verzichtet wird, obwohl ihre Verwendung eine einfachere Programmstruktur zur Folge gehabt hätte.


continue

Syntax:

continue ;

Die Anweisung continue; darf nur in den Schleifenanweisungen while, do und for verwendet werden. Sie wird im oder als Schleifenrumpf angegeben. Die continue-Anweisung bricht die Abarbeitung des Schleifenrumpfs ab und prüft die Bedingungsanweisung der Schleife erneut. So ist in den folgenden Quelltextfragmenten die continue-Anweisung mit der Anweisung goto weiter; austauschbar.

Hinweis: Die Sprungmarke weiter: ist für die Funktion der continue-Anweisung nicht erforderlich.

        for (int i = 0; i < 10; ++i)
{
/* ... */
continue;
/* ... */
weiter: ;
}
        int i = 0;
while (i < 10)
{
/* ... */
continue;
/* ... */
++i;
weiter: ;
}
        int i = 0; 
do
{
/* ... */
continue;
/* ... */
++i;
weiter: ;
} while (i < 10);

Das folgende Beispiel gibt eine Folge von Multiplikationen aus. Dabei wird jedoch die Multiplikation ausgelassen, bei der das Ergebnis eine 10 ergeben hat.

Beispiel:

#include <stdio.h>

int main (void)
{
for(int i = 0; i < 10; ++i)
{
int erg = 2 * i;

if(erg == 10) continue;

printf("2 * %2d = %2d\n", i, erg);
}

return 0;
}

Siehe auch: break, Schleifen


break

Die break-Anweisung ist Thema der Kapitels 6.8.6.3 The break statement in C99.

Syntax:

break ;

Die Anweisung break; bricht die for, do, while oder switch-Anweisung ab, die der break-Anweisung am nächsten ist. Bei einer for-Anweisung wird nach einer break-Anweisung der Ausdruck-3 nicht mehr ausgewertet.

Beispiel:

#include <stdio.h>

int main (void)
{
int i;
for(i = 0; i < 10; ++i)
if ( i == 5)
break;

printf("i = %d\n", i);

return 0;
}

Das Programm in letzten Beispiel gibt eine 4 auf der Standardausgabe aus.


return

Die Anweisung return; wird in dem Kapitel 6.8.6.4 The return statement in C99 beschrieben.

Syntax:

return Ausdruckopt ;

Die return-Anweisung beendet die Abarbeitung der aktuellen Funktion. Wenn eine return-Anweisung mit einem Ausdruck angegeben wird, dann wird der Wert des Ausdrucks an die aufrufende Funktion als Rückgabewert geliefert. In einer Funktion können beliebig viele return-Anweisungen angegeben werden. Jedoch muss dabei darauf geachtet werden, dass nachfolgender Programmcode durch den Programmfluss noch erreichbar bleibt.

Beispiel:

#include <stdio.h>
#include <string.h>

int IsFred(const char *sz)
{
if (!sz)
return 0;
if (!strcmp(sz, "Fred Feuerstein"))
return 1;
return 0;
puts("Dies wird nie ausgeführt.");
}

Der Ausdruck in der return-Anweisung ist optional. Dennoch gelten für ihn besondere Regeln. So darf der Ausdruck in Funktionen vom Typ void nicht angegeben werden. Ebenso darf der Ausdruck nur in Funktionen vom Typ void weggelassen werden.

Hat der Ausdruck der return-Anweisung einen anderen Typ als die Funktion, so wird der Wert des Ausdruckes in den Typ gewandelt, den die Funktion hat. Dies geschieht nach den gleichen Regeln, wie bei einer Zuweisung in ein Objekt vom gleichen Typen wie die Funktion.


Begriffserklärungen

Die Begriffe in diesem Abschnitt werden im Kapitel 6.8 Statements and blocks in C99 erklärt.

 

Anweisung

Eine Anweisung ist eine Aktion, die ausgeführt wird.

Eine Anweisung wird ein einer Sequenz (sequence point) ausgeführt. Jedoch kann eine Anweisung in mehrere Sequenzen aufgeteilt sein.

Gelegentlich ist die Aussage zu lesen, dass in der Sprache C alle Anweisungen mit einem Semikolon abgeschlossen werden. Dies ist nicht richtig. Lediglich die Ausdrucksanweisung, die do-Anweisung und die Sprung-Anweisungen werden mit einem Semikolon abgeschlossen. So verwendet folgendes Programm kein Semikolon.

#include <stdio.h>

int main (void)
{
if (printf("Hallo Welt\n")) {}
}

Block

Ein Block ist eine Gruppe von möglichen Deklarationen und Anweisungen. Bei jedem Eintritt in einen Block werden die Objekte der Deklarationen neu gebildet. Davon sind lediglich Felder mit einer variablen Länge (neu in C99) ausgenommen. Initialisiert werden die Objekte mit der Speicherdauer automatic storage duration und Felder mit variabler Länge jeweils zu dem Zeitpunkt, wenn die Programmausführung zu der entsprechenden Deklaration kommt und innerhalb der Deklaration selbst nach den Regeln für Deklarationen der Sprache C. Die Deklaration wird dann wie eine Anweisung betrachtet. Die Speicherdauer der Objekte ist automatic storage duration, wenn nicht der Speicherklassenspezifizierer static angegeben wurde.

Nach 5.2.4.1 Translation limits aus C99 wird eine Verschachtelungstiefe bei Blöcken von mindestens 127 Ebenen garantiert.

wikibook: C-Programmierung: Kontrollstrukturen


Bedingungen

Um auf Ereignisse zu reagieren, die erst bei der Programmausführung bekannt sind, werden Bedingungsanweisungen eingesetzt. Eine Bedingungsanweisung wird beispielsweise verwendet, um auf Eingaben des Benutzers reagieren zu können. Je nachdem, was der Benutzer eingibt, ändert sich der Programmablauf.


if

Beginnen wir mit der if -Anweisung. Sie hat die folgende Syntax:

if(expression)
statement;
else if(expression)
statement;
else if(expression)
statement;
...
else
statement;

Hinweis: Die else if - und else -Anweisungen sind optional.

Wenn der Ausdruck (engl. expression) nach seiner Auswertung wahr ist, d.h. von Null verschieden, so wird die folgende Anweisung bzw. der folgende Anweisungsblock ausgeführt (statement). Sind alle Ausdrücke gleich null und somit die Bedingungen nicht erfüllt, wird der letzte else -Zweig ausgeführt.

Klingt kompliziert, deshalb werden wir uns dies nochmals an zwei Beispielen anssehen:

 01 int main(void)
02 {
03 int zahl;
04 scanf("%i", &zahl);
05 if(zahl > 5)
06 printf("Die Zahl ist größer als 5");
07 printf("\nTschüß! Bis zum nächsten mal");
07 }

Wir nehmen zunächst einmal an, dass der Benutzer die Zahl 7 eingibt. In diesem Fall ist der Ausdruck zahl > 5 true (wahr) und liefert eine 1 zurück. Da dies ein Wert ungleich 0 ist, wird die auf if folgende Zeile ausgeführt und "Die Zahl ist größer als 5" ausgegeben. Anschließend wird die Bearbeitung mit der Anweisung printf("\nTschüß! Bis zum nächsten mal") fortgesetzt .

Wenn wir annehmen, dass der Benutzer eine 3 eingegeben hat, so ist der Ausdruck zahl > 5 false (falsch) und liefert eine 0 zurück. Deshalb wird printf("Die Zahl ist größer als 5") nicht ausgeführt und nur "Tschüß! Bis zum nächsten mal" ausgegeben.

Wir können die if -Anweisung auch einfach lesen als: "Wenn zahl größer als 5 ist, dann gib "Die Zahl ist größer als 5" aus". In der Praxis wird man sich keine Gedanken machen, welches Resultat der Ausdruck zahl > 5 hat.

Das zweite Beispiel, das wir uns ansehen, besitzt neben if auch ein else if und ein else :

01 #include <stdio.h>
02
03 int main(void)
04 {
05 int zahl;
06 printf("Bitte geben Sie eine Zahl ein: ");
07 scanf("%d", &zahl);
08 if(zahl > 0)
09 printf("Positive Zahl");
10 else if(zahl < 0)
11 printf("Negative Zahl");
12 else
13 printf("Zahl gleich Null");
14 return 0;
15 }

Nehmen wir an, dass der Benutzer die Zahl -5 eingibt. Der Ausdruck zahl > 0 ist in diesem Fall falsch, weshalb der Ausdruck ein false liefert (was einer 0 entspricht). Deshalb wird die darauffolgende Anweisung nicht ausgeführt. Der Ausdruck zahl < 0 ist dagegen erfüllt, was wiederum bedeutet, dass der Ausdruck wahr ist (und damit eine 1 liefert) und so die folgende Anweisung ausgeführt wird.

Nehmen wir nun einmal an, der Benutzer gibt eine 0 ein. Sowohl der Ausdruck zahl > 0 als auch der Ausdruck zahl < 0 sind dann nicht erfüllt. Der if - und der if - else -Block werden deshalb nicht ausgeführt. Der Compiler trifft anschließend allerdings auf die else -Anweisung. Da keine vorherige Bedingung zutraf, wird die anschließende Anweisung ausgeführt.

Wir können die if - else if - else –Anweisung auch lesen als: "Wenn zahl größer ist als 0, gib "Positive Zahl" aus, ist zahl kleiner als 0, gib "Negative Zahl" aus, ansonsten gib "Zahl gleich Null" aus."

Fassen wir also nochmals zusammen: Ist der Ausdruck in der if oder if - else -Anweisung erfüllt (wahr), so wird die nächste Anweisung bzw. der nächste Anweisungsblock ausgeführt. Trifft keiner der Ausdrücke zu, so wird die Anweisung bzw. der Anweisungsblock, die else folgen, ausgeführt.


Bedingter Ausdruck

Mit dem bedingten Ausdruck kann man eine if - else -Anweisung wesentlich kürzer formulieren. Sie hat die Syntax

exp1 ? exp2 : exp3 

Zunächst wird das Ergebnis von exp1 ermittelt. Liefert dies einen Wert ungleich 0 und ist somit true, dann ist der Ausdruck exp2 das Resultat der bedingten Anweisung, andernfalls ist exp3 das Resultat.

Beispiel:

int x = 20;
(x >= 10) ? 100 : 200

Der Ausdruck x >= 10 ist wahr und liefert deshalb eine 1. Da dies ein Wert ungleich 0 ist, ist das Resultat des bedingten Ausdrucks 100.

Der obige bedingte Ausdruck entspricht

if(x >= 10)
x = 100;
else
x = 200;

Die Klammern in unserem Beispiel sind nicht unbedingt notwendig, da Vergleichsoperatoren einen höheren Vorrang haben als der ?: -Operator. Allerdings werden sie von vielen Programmierern verwendet, da sie die Lesbarkeit verbessern.


switch

Eine weitere Auswahlanweisung ist die switch -Anweisung. Sie wird in der Regel verwendet, wenn eine unter vielen Bedingungen ausgewählt werden soll. Sie hat die folgende Syntax:

 switch(expression)
{
case const-expr: statements
case const-expr: statements
...
default: statements
}

In den runden Klammern der switch -Anweisung steht der Ausdruck, welcher mit den case –Anweisungen getestet wird. Die Vergleichswerte (const-expr) sind jeweils Konstanten. Eine break -Anweisung beendet die switch -Verzweigung und setzt bei der Anweisung nach der schließenden geschweiften Klammer fort. Es erfolgt keine weitere Überprüfung bei dem nächsten case . Optional kann eine default -Anweisung angegeben werden, die aufgerufen wird, wenn keiner der Werte passt.

Sehen wir uns dies an einem textbasierenden Rechner an, bei dem der Benutzer durch die Eingabe eines Zeichens eine der Grundrechenarten auswählen kann:

01 #include <stdio.h>
02
03 int main(void)
04 {
05 double zahl1, zahl2;
06 char auswahl;
07 printf("\nMinitaschenrecher\n");
08 printf("-----------------\n\n");
09 do
10 {
11 printf("\nBitte geben Sie die erste Zahl ein: ");
12 scanf("%lf", &zahl1);
13 printf("Bitte geben Sie die zweite Zahl ein: ");
14 scanf("%lf", &zahl2);
15 printf("\nZahl (a)addieren, (s)subtrahieren, (d) dividieren oder (m)multiplizieren?");
16 printf("\nFür beenden wählen Sie (b) ");
17 scanf("%s",&auswahl);
18 switch(auswahl)
19 {
20 case('a'):
21 case('A'):
22 printf("Ergebnis: %lf", zahl1 + zahl2);
23 break;
24 case('s'):
25 case('S'):
26 printf("Ergebnis: %lf", zahl1 - zahl2);
27 break;
28 case('D'):
29 case('d'):
30 if(zahl2 == 0)
31 printf("Division durch 0 nicht möglich!");
32 else
33 printf("Ergebnis: %lf", zahl1 / zahl2);
34 break;
35 case('M'):
36 case('m'):
37 printf("Ergebnis: %lf", zahl1 * zahl2);
38 break;
39 case('B'):
40 case('b'):
41 break;
42 default:
43 printf("Fehler: Diese Eingabe ist nicht möglich!");
44 break;
45 }
46 }
47 while(auswahl != 'B' && auswahl != 'b');
48 return 0;
49 }

Mit der do-while -Schleife wollen wir uns erst später beschäftigen. Nur so viel: Sie dient dazu, dass der in den Blockklammern eingeschlossene Teil nur solange ausgeführt wird, bis der Benutzer b oder B zum Beenden eingegeben hat.

Die Variable auswahl erhält die Entscheidung des Benutzers für eine der vier Grundrechenarten oder den Abbruch des Programms. Gibt der Anwender beispielsweise ein kleines 's' ein, fährt das Programm bei der Anweisung case('s') fort und es werden solange alle folgenden Anweisungen bearbeitet, bis das Programm auf ein break stößt. Wenn keine der case Anweisungen zutrifft, wird die default -Anweisung ausgeführt und eine Fehlermeldung ausgegeben.

Etwas verwirrend mögen die Anweisungen case('B') und case('b') sein, denen unmittelbar break folgt. Sie sind notwendig, damit bei der Eingabe von B oder b nicht die default -Anweisung ausgeführt wird.


Schleifen

Schleifen werden verwendet um einen Programmabschnitt mehrmals zu wiederholen. Sie kommen in praktisch jedem größeren Programm vor.


For-Schleife

Die for-Schleife wird in der Regel dann verwendet, wenn von vornherein bekannt ist, wie oft die Schleife durchlaufen werden soll. Die for-Schleife hat die folgende Syntax:

for (expressionopt; expressionopt; expressionopt)
statement

In der Regel besitzen for-Schleifen einen Schleifenzähler. Dies ist eine Variable zu der bei jedem Durchgang ein Wert addiert oder subtrahiert wird (oder die durch andere Rechenoperationen verändert wird). Der Schleifenzähler wird über den ersten Ausdruck initialisiert. Mit dem zweiten Ausdruck wird überprüft, ob die Schleife fortgesetzt oder abgebrochen werden soll. Letzterer Fall tritt ein, wenn dieser den Wert 0 annimmt - also der Ausdruck false (falsch) ist. Der letzte Ausdruck dient schließlich dazu, den Schleifenzähler zu verändern.

Mit einem Beispiel sollte dies verständlicher werden. Das folgende Programm zählt von 1 bis 5:

 01  #include <stdio.h>
02
03 int main()
04 {
05 int i;
06
07 for(i = 1; i <= 5; ++i)
08 printf("%d ",i);
09 return 0;
10 }

Die Schleife beginnt mit dem Wert 1 (i=1) und erhöht den Schleifenzähler i bei jedem Durchgang um 1 (++i). Solange der Wert i kleiner oder gleich 5 ist (i<=5), wird die Schleife durchlaufen. Ist i gleich 6 und daher die Aussage i<=5 falsch, wird der Wert 0 zurückgegeben und die Schleife abgebrochen. Insgesamt wird also die Schleife 5mal durchlaufen.

Wenn das Programm kompiliert und ausgeführt wird, erscheint die folgende Ausgabe auf dem Monitor:

1  2  3  4  5

Anstelle des Präfixoperators hätte man auch den Postfixoperator i++ benutzen und for(i = 1; i <= 5; i++) schreiben können. Diese Variante unterscheidet sich nicht von der oben verwendeten. Eine weitere Möglichkeit wäre, for(i = 1; i <= 5; i = i + 1) oder for(i = 1; i <= 5; i += 1) zu schreiben. Die meisten Programmierer benutzen eine der ersten beiden Varianten, da sie der Meinung sind, dass schneller ersichtlicht wird, dass i um eins erhöht wird und dass durch den Inkrementoperator Tipparbeit gesparen werden kann.

Damit die for -Schleife noch etwas klarer wird, wollen wir uns noch ein paar Beispiele ansehen:

for(i = 0;  i < 7; i += 1.5)

Der einzige Unterschied zum letzen Beispiel besteht darin, dass die Schleife nun in 1,5er Schritten durchlaufen wird. Der nachfolgende Befehl oder Anweisungsblock wird insgesamt 5mal durchlaufen. Dabei nimmt der Schleifenzähler i die Werte 0, 1.5, 3, 4.5 und 6 an. (Die Variable i muss hier natürlich einen Gleitkommadatentyp haben.)

for(i = 20; i > 5; i -= 5)

Diesmal zählt die Schleife rückwärts. Sie wird dreimal durchlaufen. Der Schleifenzähler nimmt dabei die Werte 20, 15 und 10 an. Und noch ein letztes Beispiel:

for(i = 1; i < 20; i *= 2)

Prinzipiell lassen sich für die Schleife alle Rechenoperationen benutzen. In diesem Fall wird in der Schleife die Multiplikation benutzt. Sie wird 5mal durchlaufen. Dabei nimmt der Schleifenzähler die Werte 1, 2, 4, 8 und 16 an.

Wie Sie aus der Syntax unschwer erkennen können, sind die Ausdrücke in den runden Klammern optional. So ist beispielsweise

 for(;;)

korrekt. Da nun der zweite Ausdruck immer wahr ist, und damit der Schleifenkopf niemals den Wert 0 annehmen kann, wird die Schleife unendlich oft durchlaufen. Eine solche Schleife wird auch als Endlosschleife bezeichnet, da sie niemals endet (in den meisten Betriebssystemen gibt es eine Möglichkeit das dadurch "stillstehende" Programm mit einer Tastenkombination abzubrechen). Endlosschleifen können beabsichtigt sein (siehe dazu auch weiter unten die break-Anweisung) oder unbeabsichtigte Programmierfehler sein.

Mehrere Befehle hinter einer for-Anweisung müssen immer in Blockklammern eingeschlossen werden:

for(i = 1; i < 5; i++)
{
printf("\nEine Schleife: ");
printf("%d ", i);
}

Schleifen lassen sich auch schachteln, das heißt, innerhalb einer Schleife dürfen sich eine oder mehrere weitere Schleifen befinden. Beispiel:

01  #include <stdio.h>
02
03 int main()
04 {
05 int i, j, Zahl=1;
06 for (i = 1; i <= 10; i++)
07 {
08 for (j = 1; j <= 10; j++)
09 printf ("%4i", Zahl++);
10 printf ("\n");
11 }
12 return 0;
13 }

Nach der Kompilierung und Übersetzung des Programms erscheint die folgende Ausgabe:

   1   2   3   4   5   6   7   8   9  10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100

Damit bei der Ausgabe alle 10 Einträge eine neue Zeile beginnt, wird die innere Schleife nach 10 Durchläufen beendet. Anschließend wird ein Zeilenumbruch ausgegeben und die innere Schleife von der äußeren Schleife wiederum insgesamt 10mal aufgerufen.


While-Schleife

Häufig kommt es vor, dass eine Schleife, beispielsweise bei einem bestimmte Ereignis, abgebrochen werden soll. Ein solches Ereignis kann z.B. die Eingabe eines bestimmen Wertes sein. Hierfür verwendet man meist die while-Schleife, welche die folgende Syntax hat:

while (expression)
statement

Im folgenden Beispiel wird ein Text solange von der Tastatur eingelesen, bis der Benutzer die Eingabe abbricht (In der Microsoft-Welt geschieht dies durch <Strg>-<Z>, in der UNIX-Welt über die Tastenkombination <Strg>-<D>). Als Ergebnis liefert das Programm die Anzahl der Leerzeichen:

01 #include <stdio.h>
02
03 int main()
04 {
05 int c;
06 int zaehler = 0;
07
08 while((c = getchar()) != EOF)
09 {
10 if(c == ' ')
11 zaehler++;
12 }
13 printf("Anzahl der Leerzeichen: %d", zaehler);
14 }

Die Schleife wird abgebrochen, wenn der Benutzer die Eingabe (mit <Strg>-<Z> oder <Strg>-<D>) abbricht und somit das nächste zuliefernde Zeichen das EOF-Zeichen ist. In diesem Fall ist der Ausdruck (c = getchar()) != EOF) nicht mehr wahr, liefert 0 zurück und die Schleife wird beendet.

Bitte beachten Sie, dass die Klammer um c = getchar() nötig ist, da der Ungleichheitsoperator eine höhere Priorität hat als der Zuweisungsoperator = . Neben den Zuweisungsoperatoren besitzen auch die logischen Operatoren Und (&), Oder (|) sowie XOR (^) eine niedrigere Priorität.

Noch eine Anmerkung zu diesem Programm: Wie Sie vielleicht bereits festgestellt haben, wird das Zeichen, das getchar zurückliefert in einer Variable des Typs Integer gespeichert. Für die Speicherung eines Zeichenwertes genügt, wie wir bereits gesehen haben, eine Variable vom Typ Character. Der Grund dafür, dass wir dies hier nicht können, liegt im ominösen EOF-Zeichen. Es dient normalerweise dazu, das Ende einer Datei zu markieren - auf Englisch das End of File - oder kurz EOF. Allerdings ist EOF ein negativer Wert vom Typ int , so dass kein "Platz" mehr in einer Variable vom Typ char ist. Viele Implementierungen benutzen -1 um das EOF-Zeichen darzustellen, was der ANSI-C-Standard allerdings nicht vorschreibt (der tatsächliche Wert ist in der Headerdatei <stdio.h> abgelegt).

Eine for-Schleife kann immer durch eine while-Schleife ersetzt werden. So ist beispielsweise unser for-Schleifenbeispiel aus dem ersten Abschnitt mit der folgenden while-Schleife äquivalent:

01 #include <stdio.h>
02
03 int main()
04 {
05 int x = 1;
06 while(x <= 5)
07 {
08 printf("%d ", x);
09 ++x;
10 }
11 return 0;
12 }

Ob man while oder for benutzt, hängt letztlich von der Vorliebe des Programmierers ab. In diesem Fall würde man aber vermutlich eher eine for -Schleife verwenden, da diese Schleife eine Zählervariable enthält, die bei jedem Schleifendurchgang um eins erhöht wird.


Do-While-Schleife

Im Gegensatz zur while -Schleife findet bei der Do-while-Schleife die Überprüfung der Wiederholungsbedingung am Schleifenende statt. So kann garantiert werden, dass die Schleife mindestens einmal durchlaufen wird. Sie hat die folgende Syntax:

do
statement
while (expression);

Das folgende Programm addiert solange Zahlen auf, bis der Anwender eine 0 eingibt:

01 #include <stdio.h>
02
03 int main(void)
04 {
05 float zahl;
06 float ergebnis = 0;
07 do
08 {
09 printf ("Bitte Zahl eingeben (0 für beenden):");
10 scanf("%f",&zahl);
11 ergebnis += zahl;
12 }
13 while (zahl != 0);
14 printf("Das Ergebnis ist %f", ergebnis);
15 return 0;
16 }

Die Überprüfung, ob die Schleife fortgesetzt werden soll, findet in Zeile 13 statt. Mit do in Zeile 7 wird die Schleife begonnen, eine Prüfung findet dort nicht statt, weshalb der Block von Zeile 8 bis 12 in jedem Fall mindestens einmal ausgeführt wird.

Wichtig: Beachten Sie, dass das while mit einem Semikolon abgeschlossen werden muss!


Schleifen abbrechen


continue

Eine continue-Anweisung beendet den aktuellen Schleifendurchlauf und setzt, sofern die Schleifen-Bedingung noch erfüllt ist, beim nächsten Durchlauf fort.

01 #include <stdio.h>
02
03 int main(void)
04 {
05 double i;
06 for(i = -10; i <=10; i++)
07 {
08 if(i == 0)
09 continue;
10 printf("\n %lf", 1/i);
11 }
12 return 0;
13 }

Das Programm berechnet in ganzzahligen Schritten die Werte für 1/i im Intervall [-10, 10]. Da die Division durch Null nicht erlaubt ist, springen wir mit Hilfe der if-Bedingung wieder zum Schleifenkopf.


break

Die break -Anweisung beendet eine Schleife und setzt bei der ersten Anweisung das Programm nach der Schleife fort. Sehen wir uns dies an einem kleinen Spiel an: Der Computer ermittelt eine Zufallszahl zwischen 1 und 100, die der Nutzer dann erraten soll. Dabei gibt es immer einen Hinweis, ob die Zahl kleiner oder größer als die eingegebene Zahl ist.

01 #include <stdio.h>
02 #include <stdlib.h>
03 #include <time.h>
04
05 int main(void)
06 {
07 int zufallszahl, eingabe;
08 int durchgaenge = 0;
09 char auswahl;
10 srand(time(0));
11 zufallszahl = (rand() % 100 + 1);
12 printf("\nLustiges Zahlenraten");
13 printf("\n--------------------");
14 printf("\nErraten Sie die Zufallszahl in möglichst wenigen Schritten!");
15 printf("\nDie Zahl kann zwischen 1 und 100 liegen");
16
17 do
18 {
19 while(1)
20 {
21 printf("\nBitte geben Sie eine Zahl ein: ");
22 scanf("%d", &eingabe);
23 if(eingabe > zufallszahl)
24 {
25 printf("Leider falsch! Die zu erratende Zahl ist kleiner");
26 durchgaenge++;
27 }
28 else if(eingabe < zufallszahl)
29 {
30 printf("Leider falsch! Die zu erratende Zahl ist größer");
31 durchgaenge++;
32 }
33 else
34 {
35 printf("Glückwunsch! Sie haben die Zahl in %d",durchgaenge);
36 printf(" Schritten erraten.");
37 zufallszahl = (rand() % 100 + 1);
38 break;
39 }
40 }
41 printf("\nNoch ein Spiel? (J/j für weiteres Spiel)");
42 scanf("%s", &auswahl);
43 }
44 while(auswahl == 'j' || auswahl == 'J');
45 return 0;
46 }

Wie Sie sehen ist die innere while -Schleife als Endlosschleife konzipiert. Hat der Spieler die richtige Zahl erraten, so wird der else-Block ausgeführt. In diesem wird die Endlosschleife schließlich mit break abgebrochen. Die nächste Anweisung, die dann ausgeführt wird, ist die printf Anweisung unmittelbar nach der Schleife.

Die äußere while -Schleife in Zeile 44 wird solange wiederholt, bis der Benutzer nicht mehr mit einem kleinen oder großen j antwortet. Beachten Sie, dass im Gegensatz zu den Operatoren & und | die Operatoren && und || streng von links nach rechts bewertet werden.

In diesem Beispiel hat dies keine Auswirkungen. Allerdings schreibt der Standard auch vor, dass, wenn der erste Operand des Ausdrucks verschieden von 0 (wahr) ist, der Rest nicht mehr ausgewertet wird. Die Folgen soll dieses Beispiel verdeutlichen:

int c, a = 5;
while (a == 5 || (c = getchar()) != EOF )

Da der Ausdruck a == 5 true ist, liefert er also einen von 0 verschieden Wert zurück. Der Ausdruck getchar = c wird deshalb erst gar nicht mehr ausgewertet, da bereits nach der Auswertung des ersten Operanden feststeht, dass die ODER-Verknüpfung den Wahrheitswert true besitzten muss (Wenn ihnen dies nicht klar geworden ist, sehen Sie sich nochmals die Wahrheitstabelle der ODER-Verknüpfung an). Dies hat zur Folge, dass getchar nicht mehr ausgeführt und deshalb kein Zeichen eingelesen wird. Wenn wir wollen, dass getchar aufgerufen wird, so müssen wir die Reihenfolge der Operanden umdrehen.

Dasselbe gilt natürlich auch für den && -Operator, nur dass in diesem Fall der zweite Operand nicht mehr ausgewertet wird, wenn der erste Operand bereits 0 ist.

Beim || und && -Operator handelt es sich um einen Sequenzpunkt: Wie wir gesehen haben, ist dies ein Punkt, bis zu dem alle Nebenwirkungen vom Compiler ausgewertet sein müssen. Auch hierzu ein Beispiel:

 i = 7;
if( i++ == 5 || (i += 3) == 4)

Zunächst wird der erste Operand ausgewertet. Es wird i um eins erhöht und mit dem Wert 5 verglichen. Wie wir gerade gesehen haben, wird der zweite Operand nur dann ausgewertet, wenn feststeht, dass der erste Operand einen von 0 verschieden Wert liefert: Dabei wird i um 3 erhöht und mit dem Wert 4 verglichen. Der Ausdruck ist also keineswegs undefiniert wie man vielleicht vermuten könnte. Die Variable i nimmt nach Auswertung des ersten Operanden den Wert 8 an und nach Auswertung des zweiten Operanden den Wert 11. Die Auswertung findet auf keinen Fall umgekehrt statt: es ist also nicht möglich, dass zu i zunächst die 3 addiert wird und i so den Wert 10 annimmt und es anschließend um 1 erhöht wird.

Bevor wir uns weiter mit Kontrollstrukturen beschäftigen, lassen Sie uns aber noch einen Blick auf den Zufallsgenerator werfen, da er eine interessante Anwendung für den Modulo–Operator darstellt. Damit der Zufallsgenerator nicht immer die gleichen Zahlen ermittelt, muss zunächst der Zufallsgenerator über srand(time(0)) mit der Systemzeit initialisiert werden (wenn Sie diese Bibliotheksfunktionen in ihrem Programm benutzen wollen, beachten Sie, dass Sie für die Funktion time(0) die Headerdatei <time.h> und für die Benutzung des Zufallsgenerators die Headerdatei <stdlib.h> einbinden müssen). Aber wozu braucht man nun den Modulo-Operator? Die Funktion rand() liefert einen Wert zwischen 0 und mindestens 32767. Um nun einen Zufallswert zwischen 1 und 100 zu erhalten, teilen wir den Wert durch hundert und addieren 1. Den Rest, der ja nun zwischen eins und hundert liegen muss, verwenden wir als Zufallszahl.

Bitte beachten Sie, dass rand() in der Regel keine sehr gute Streuung liefert. Für statistische Zwecke sollten Sie deshalb nicht auf die Standardbibliothek zurückgreifen.


Sonstiges


goto

Mit einer goto-Anweisung setzt man die Ausführung des Programms an einer anderen Stelle des Programms fort. Diese Stelle im Programmcode wird mit einem sogenannten Label definiert:

LabelName:

Zu einem Label springt man mit

goto LabelName;

In der Anfangszeit der Programmierung wurde goto anstelle der eben vorgestellten Kontrollstrukturen verwendet. Das Ergebnis war eine sehr unübersichtliche Programmstruktur, die auch häufig als Spagetticode bezeichnet wurde. Bis auf wenige Ausnahmen ist es möglich, auf die goto-Anweisung zu verzichten (neuere Sprachen wie Java kennen sogar überhaupt kein goto mehr). Einige der wenigen Anwendungsgebiete von goto werden Sie im Kapitel Programmierstil finden, darüber hinaus werden Sie aber keine weiteren Beispiele in diesem Buch finden.

wikibook: C-Programmierung: Funktionen

http://de.wikibooks.org/wiki/C-Programmierung:_Funktionen

Eine wichtige Forderung der strukturierten Programmierung ist die Vermeidung von Sprüngen innerhalb des Programms. Wie wir gesehen haben, ist dies in den meisten Fällen mit Kontrollstrukturen möglich.

Die zweite Forderung der strukturierten Programmierung ist die Modularisierung. Dabei wird ein Programm in mehrere Programmabschnitte, den Modulen zerlegt. In C werden solche Module auch als Funktionen bezeichnet. Andere Programmiersprachen bezeichnen Module als Unterprogramme oder unterscheiden zwischen Funktionen (Module mit Rückgabewert) und Prozeduren (Module ohne Rückgabewert). Trotz dieser unterschiedlichen Bezeichnungen ist aber das Selbe gemeint.

Objektorientierte Programmiersprachen gehen noch einen Schritt weiter und verwenden Klassen zur Modularisierung. Vereinfacht gesagt bestehen Klassen aus Methoden (vergleichbar mit Funktionen) und Attributen (Variablen). C selbst unterstützt keine Objektorientierte Programmierung, im Gegensatz zu C++, das auf C aufbaut.

Die Modularisierung hat eine Reihe von Vorteilen:

Bessere Lesbarkeit

Der Quellcode eines Programms kann schnell mehrere tausend Zeilen umfassen. Beim Linux Kernel sind es sogar über 100.000 Zeilen und Windows, das ebenfalls zum Großteil in C geschrieben wurde, umfasst sogar mehrere Millionen Zeilen. Um dennoch die Lesbarkeit des Programms zu gewährleisten, ist die Modularisierung unerlässlich.

Wiederverwendbarkeit

In fast jedem Programm werden gleiche Problemstellungen mehrmals verwendet. Oft gilt dies auch für unterschiedliche Applikationen. Da nur Parameter und Rückgabetyp für die Benutzung einer Funktion bekannt sein müssen, erleichtert dies die Wiederverwendbarkeit. Um die Implementierungsdetails muss sich der Entwickler dann nicht mehr kümmern.

Wartbarkeit

Fehler lassen sich durch die Modularisierung leichter finden und beheben. Darüber hinaus ist es leichter weitere Funktionalitäten hinzuzufügen oder zu ändern.

Inhaltsverzeichnis

[Anzeigen]


Funktionsdefinition

Im Kapitel Was sind Variablen haben wir die Quaderoberfläche berechnet. Nun wollen wir eine Funktion schreiben, die diese Aufgabe für uns übernimmt. Dazu schauen wir uns zunächst den Syntax einer Funktion an:

Funktionstyp Funktionsname (Parameterdeklaration)
{
Anweisungen
}

Die Anweisungen werden auch als Funktionsrumpf bezeichnet, die erste Zeile als Funktionskopf.

Wenn wir eine Funktion zur Zylinderoberflächenberechnung schreiben und diese benutzen sieht unser Programm wie folgt aus:

01 #include <stdio.h>
02
03 float zylinder_oberflaeche(float h, float r)
04 {
05 float o;
06 o=2*3.141*r*(r+h);
07 return(o);
08 }
09
10 int main(void)
11 {
12 float r,h;
13 printf("Programm zur Berechnung einer Zylinderoberfläche");
14 printf("\n\nHöhe des Zylinders: ");
15 scanf("%f",&h);
16 printf("\nRadius des Zylinders: ");
17 scanf("%f",&r);
18 printf("Oberfläche: %f",zylinder_oberflaeche(h,r));
19 return 0;
20 }
  • In Zeile 3 beginnt die Funktionsdefinition. Das float ganz am Anfang der Funktion, der sogenannte Funktionstyp, sagt dem Compiler, dass ein Wert mit dem Typ float zurückgegeben wird. In Klammern werden die Übergabeparameter h und r deklariert, die der Funktion übergeben werden.
  • Mit return wird die Funktion beendet und ein Wert an die aufrufende Funktion zurückgegeben (hier: main ). In unserem Beispiel geben wir den Wert von o zurück, also das Ergebnis unserer Berechnung. Der Datentyp des Ausdrucks muss mit dem Typ des Rückgabewertes des Funktionskopfs übereinstimmen. Würden wir hier beispielsweise versuchen, den Wert einer int-Variable zurückzugeben, würden wir vom Compiler eine Fehlermeldung erhalten.
    Soll der aufrufenden Funktion kein Wert zurückgegeben werden, muss als Typ der Rückgabewert void angegeben werden. Eine Funktion, die lediglich einen Text ausgibt hat beispielsweise den Rückgabetyp void , da sie keinen Wert zurückgibt.
  • In Zeile 18 wird die Funktion zylinder_oberflaeche aufgerufen. Ihr werden die beiden Parameter h und r übergeben. Der zurückgegebene Wert wird ausgegeben. Es wäre aber genauso denkbar, dass der Wert einer Variable zugewiesen, mit einem anderen Wert verglichen oder mit dem Rückgabewert weitergerechnet wird.
    Der Rückgabewert muss aber nicht ausgewertet werden. Es ist kein Fehler, wenn der Rückgabewert unberücksichtig bleibt.

In unserem Beispiel haben wir den Rückgabetyp in return geklammert (Zeile 7). Die Klammerung ist aber optional und kann weggelassen werden (Zeile 19).

Auch die Funktion main hat einen Rückgabewert. Ist der Wert 0, so bedeutet dies, dass das Programm ordnungsgemäß beendet wurde, ist der Wert -1, so bedeutet dies, dass ein Fehler aufgetreten ist.

Jede Funktion muss einen Rückgabetyp besitzen. In der ursprünglichen Sprachdefinition von K&R wurde dies noch nicht gefordert. Wenn der Rückgabetyp fehlte, wurde defaultmäßig int angenommen. Dies ist aber inzwischen nicht mehr erlaubt. Jede Funktion muss einen Rückgabetyp explizit angeben.

Die folgenden Beispiele enthalten Funktionsdefinitionen, die einige typische Anfänger(denk)fehler zeigen:

void foo()
{
/* Code */
return 5;
}

Eine Funktion, die als void deklariert wurde, darf keinen Rückgabetyp erhalten. Der Compiler sollte hier eine Fehlermeldung oder zumindest eine Warnung ausgeben.

int foo()
{
/* Code */
return 5;
printf("Diese Zeile wird nie ausgeführt");
}

Bei diesem Beispiel wird der Compiler weder eine Warnung noch eine Fehlermeldung ausgeben. Allerdings wird die printf Funktion niemals ausgeführt, da return nicht nur einen Wert zurückgibt sondern die Funktion foo() auch beendet.

Das folgende Programm arbeitet hingegen völlig korrekt:

int max(int a, int b)
{
if(a >= b)
return a;
if(a < b)
return b;
}


Bei diesem Beispiel gibt der Compiler keine Fehlermeldung oder Warnung aus, da eine Funktion zwar nur einen Rückgabewert erhalten darf, aber mehrere return Anweisungen besitzen kann. In diesem Beispiel wird in Abhängigkeit der übergebenen Parameter entweder a oder b zurückgegeben.


Prototypen

Auch bei Funktionen unterscheidet man wie bei Variablen zwischen Definition und Deklaration. Mit

float zylinder_oberflaeche(float h, float r)
{
float o;
o=2*3.141*r*(r+h);
return(o);
}

wird die Funktion zylinder_oberflaeche definiert.

Bei einer Funktionsdeklaration wird nur der Funktionskopf gefolgt von einem Semikolon angeben. Die Funktion zylinder_oberflaeche beispielsweise wird wie folgt deklariert:

float zylinder_oberflaeche(float h, float r);

Dies ist identisch mit

extern float zylinder_oberflaeche(float h, float r);

Die Meinungen, welche Variante benutzt werden soll gehen hier auseinander: Einige Entwickler sind der Meinung, dass das Schlüsselwort extern die Lesbarkeit verbessert andere wiederum nicht. Wir werden im Folgenden das Schlüsselwort extern in diesem Zusammenhang nicht verwenden.

Eine Trennung von Definition und Deklaration ist notwendig, wenn die Definition der Funktion erst nach der Benutzung erfolgen soll. Eine Deklaration einer Funktion wird auch als Prototyp bezeichnet. Damit kann der Compiler überprüfen, ob die Funktion überhaupt existiert und Rückgabetyp und Typ der Argumente korrekt sind. Stimmen Prototyp und Funktionsdefinition nicht überein oder wird eine Funktion aufgerufen, die noch nicht definiert wurde oder keinen Prototyp besitzt, so ist dies ein Fehler.

Das folgende Programm ist eine weitere Abwandlung des Programms zur Berechnung der Quaderoberfläche. Die Funktion zylinder_oberflaeche wurde dabei verwendet, bevor sie definiert wurde:

01 #include <stdio.h>
02
03 float zylinder_oberflaeche(float h, float r);
04
05 int main(void)
06 {
07 float r,h,o;
08 printf("Programm zur Berechnung einer Zylinderoberfläche");
09 printf("\n\nHöhe des Zylinders:");
10 scanf("%f",&h);
11 printf("\nRadius des Zylinders:");
12 scanf("%f",&r);
13 printf("Oberfläche: %f",zylinder_oberflaeche(h,r));
14 return 0;
15 }
16
17 float zylinder_oberflaeche(float h, float r)
18 {
19 float o;
20 o=2*3.141*r*(r+h);
21 return(o);
22 }

Der Prototyp wird in Zeile 3 definiert, damit die Funktion in Zeile 13 verwendet werden kann. An dieser Stelle kann der Compiler auch prüfen, ob der Typ und die Anzahl der übergebenen Parameter richtig ist (dies könnte er nicht, hätten wir keinen Funktionsprototyp deklariert). Ab Zeile 17 wird die Funktion zylinder_oberflaeche definiert.

Die Bezeichner der Parameter müssen im Prototyp und der Funktionsdefinition nicht übereinstimmen. Sie können sogar ganz weggelassen werden. So kann Zeile 3 auch ersetzt werden durch:

03 float zylinder_oberflaeche(float, float);

Wichtig: Bei Prototypen unterscheidet C zwischen einer leeren Parameterliste und einer Parameterliste mit void . Ist die Parameterliste leer, so bedeutet dies, dass die Funktion eine nicht definierte Anzahl an Parametern besitzt. Das Schlüsselwort void gibt an, dass der Funktion keine Werte übergeben werden dürfen. Beispiel:

 01  int foo1();
02 int foo2(void);
03
04 int main(void)
05 {
06 foo1(1, 2, 3); // kein Fehler
07 foo2(1, 2, 3); // Fehler
08 return 0;
09 }

Während der Compiler beim Aufruf der Funktion foo1 in Zeile 6 keine Fehlermeldung ausgegeben wird, gibt der Compiler beim Aufruf der Funktion foo2 in Zeile 7 eine Fehlermeldung aus. (Der Compiler wird höchstwahrscheinlich noch zwei weitere Warnungen oder Fehler ausgeben, da wir zwar Prototypen für die Funktionen foo1 und foo2 haben, die Funktion aber nicht definiert haben.)

Diese Aussage gilt übrigens nur für Prototypen: Laut C Standard bedeutet eine leer Liste bei Funktionsdeklarationen die Teil einer Definition sind, dass die Funktion keine Parameter hat. Im Gegensatz dazu bedeutet eine leere Liste in einer Funktionsdeklaration, die nicht Teil einer Definition sind (also Prototypen), dass keine Informationen über die Anzahl oder Typen der Parameter vorliegt - so wie wir das eben am Beispiel der Funktin foo1 gesehen haben.

Noch ein Hinweis für Leser, die ihre C Programme mit einem C++ Compiler compilieren: Bei C++ würde auch im Fall von foo1 eine Fehlermeldung ausgegeben, da dort auch eine leere Parameterliste bedeutet, dass der Funktion keine Parameter übergeben werden können.

Übrigens haben auch Bibliotheksfunktionen wie printf oder scanf einen Prototyp. Dieser befindet sich üblicherweise in der Headerdatei stdio.h oder anderen Headerdateien. Damit kann der Compiler überprüfen ob die Anweisungen die richtige Syntax haben. Der Prototyp der printf Anweisung hat beispielsweise die folgende Form (oder ähnlich) in der stdio.h :

int printf(const char *, ...);

Findet der Compiler nun beispielsweise die folgende Zeile im Programm, gibt er einen Fehler aus:

printf(45);

Der Compiler vergleicht den Typ des Parameters mit dem des Prototypen in der Headerdatei stdio.h und findet dort keine Übereinstimmung. Nun "weiß" er, dass der Anweisung ein falscher Parameter übergeben wurde und gibt eine Fehlermeldung aus.

Das Konzept der Prototypen wurde als erstes in C++ eingeführt und war in der ursprünglichen Sprachdefinition von Kernighan und Ritchie noch nicht vorhanden. Deshalb kam auch beispielsweise das "Hello World" Programm in der ersten Auflage von "The C Programming Language" ohne include Anweisung aus. Erst mit der Einführung des ANSI Standards wurden auch in C Prototypen eingeführt.


Inline Funktionen

Neu im C99 Standard sind Inline- Funktionen. Sie werden definiert, indem ihr das Schlüsselwort inline vorangestellt wird. Beispiel:

 inline float zylinder_oberflaeche(float h, float r)
{
float o;
o = 2 * 3.141 * r * (r + h);
return(o);
}

Eine Funktion, die als inline definiert ist, soll gemäß dem C-Standard so schnell wie möglich aufgerufen werden. Die genaue Umsetzung ist der Implementierung überlassen. Beispielsweise kann der Funktionsaufruf dadurch beschleunigt werden, dass die Funktion nicht mehr als eigenständiger Code vorliegt, sondern an der Stelle des Funktionsaufrufs eingefügt wird. Dadurch entfällt eine Sprunganweisung in die Funktion und wieder zurück. Allerdings muss der Compiler das Schlüsselwort inline nicht beachten, wenn der Compiler keinen Optimierungsbedarf feststellt.


Globale und lokale Variablen

Alle bisherigen Beispielprogramme verwendeten lokale Variablen. Sie wurden am Beginn einer Funktion deklariert und galten nur innerhalb dieser Funktion. Sobald die Funktion verlassen wird verliert sie ihre Gültigkeit. Eine Globale Variable dagegen wird außerhalb einer Funktion deklariert (in der Regel am Anfang des Programms) und behält bis zum Beenden des Programms ihre Gültigkeit und dementsprechend einen Wert.

 01  #include <stdio.h>
02
03 int global_a = 43;
04 float global_b= 12;
05
06 void funktion1();
07 void funktion2();
08
09 int main(void)
10 {
11 printf("Beispiele für lokale und globale Variablen:\n\n");
12 funktion1();
13 funktion2();
14 return 0;
15 }
16
17 void funktion1()
18 {
19 int lokal_a = 18;
20 printf("\nGlobale Variable a: %i", global_a);
21 printf("\nGlobale Variable b: %f", global_b);
22 printf("\nLokale Variable a: %i", lokal_a);
23 }
24
25 void funktion2()
26 {
27 int lokal_b= 65;
28 printf("\n Lokale Variable b: %i", lokal_b);
29 printf("\n Globale Variable b: %f", global_b);
30 // printf("\n Lokale Variable a: %i", lokal_a); erzeugt einen Compilerfehler!!
31 }

Die Variablen global_a und global_b sind zu Beginn des Programms und außerhalb der Funktion deklariert worden und gelten deshalb im ganzen Programm. Sie können innerhalb jeder Funktion benutzt werden. Lokale Variablen wie lokal_a und lokal_b dagegen gelten nur innerhalb der Funktion in der sie deklariert wurden. Sie verlieren außerhalb dieser Funktion ihren Gültigkeit. Der Compiler erzeugt deshalb beim Aufruf der Variable lokal_a einen Fehler, da die Variable in Funktion1 deklariert wurde.

Globale Variablen unterscheiden sich in einem weiteren Punkt von den lokalen Variablen: Sie werden automatisch mit dem Wert 0 initialisiert wenn ihnen kein Wert zugewiesen wird. Lokale Variablen dagegen erhalten immer einen zufälligen Wert, der sich gerade an der vom Compiler reservierten Speicherstelle befindet. Diesen Umstand macht das folgende Programm deutlich:

 01  #include <stdio.h>
02
03 int zahl_global;
04
05 int main(void)
06 {
07 int zahl_lokal;
08 printf("Lokale Variable: %i", zahl_lokal);
09 printf("\nGlobale Variable: %i", zahl_global);
10 return 0;
11 }

Das Ergebnis:

Lokale Variable: 296
Globale Variable: 0

Sind eine Variable mit dem selben Namen als globale und lokale Variable definiert, wird immer die lokale Variable bevorzugt. Das nächste Beispiel zeigt eine solche "Doppeldekleration":

 01  #include <stdio.h>
02
03 int zahl=5;
04
05 void func();
06
07 int main(void)
08 {
09 int zahl=3;
10 printf("Global oder Lokal? %i",zahl);
11 func();
12 return 0;
13 }
14
15 void func()
16 {
17 printf("\nGlobale Variable: %i", zahl);
18 }

Neben der globalen Variable zahl wird in der Hauptfunktion main eine weitere Variable mit dem Namen zahl deklariert. Da nun zwei Variablen mit dem selben Namen existieren, gibt die printf Anweisung die lokale Variable mit dem Wert 3 aus. Die Funktion func soll lediglich verdeutlichen, dass die globale Variable zahl nicht von der lokalen Variablendeklaration gelöscht oder überschrieben wurde.

In C++ gibt es einen Operator der es ermöglicht explizit auf die globale Variable zuzugreifen. In C gibt es keine vergleichbare Möglichkeit.


exit()

Mit der Bibliotheksfunktion exit() kann ein Programm an einer beliebigen Stelle beendet werden. In Klammern muss ein Wert übergeben werden, der an die Umgebung - also in der Regel das Betriebssystem - zurückgegeben wird. Der Wert 0 wird dafür verwendet, um zu signalisieren, dass das Programm korrekt beendet wurde. Ist der Wert von 0 verschieden, so ist es abhängig von Implementierung welchen Bedeutung der Rückgabewert hat. Beispiel:

 exit(2);

Beendet das Programm und gibt den Wert 2 an das Betriebssystem zurück. Alternativ dazu können auch die Makros EXIT_SUCCESS und EXIT_FAILURE verwendet werden, um eine erfolgreiche bzw. fehlerhafte Beendigung des Programms zurückzuliefern.

Anmerkung: Unter DOS kann dieser Rückgabewert beispielsweise mittels IF ERRORLEVEL in einer Batchdatei ausgewertet werden, unter Unix/Linux enthält die spezielle Variable $? den Rückgabewert des letzten aufgerufenen Programms. Andere Betriebssysteme haben ähnliche Möglichkeiten; damit sind eigene Miniprogramme möglich, welche bestimmte Begrenzungen (von z.B. Batch- oder anderen Scriptsprachen) umgehen können. Sie sollten daher immer Fehlercodes verwenden, um das Ergebnis auch anderen Programmen zugänglich zu machen.

ebay-Angebote


Stellenangebote bei JobScout24

Auf Amazon suchen:c++




Linktipps

Einkaufen

Internet-Einkaufszentrum

Computer, Elektronik

Freizeit, Sport, Fitness

Mobilfunk, Handy

Apple iPhone

Mobilfunk Handytarife

Fotografie

Filmen

Rund um's Auto

Mehr? Sieh selbst:

Verbraucherschutz

Verbrauchermagazin

Nachrichten

Stiftung Warentest

Ökotest

Verbraucherschutz-Ministerium

Wer ist unter uns?



Bücher




Software


Nutzen Sie die Vorteile beim Softwarekauf Über Amazon: Software wird von Kunden bewertet - günstiges Versandkostensystem - Amazon ist zuverlässig





Handy/Telefon


O2 Inklusiv Pakete Online

Empfehlung: Top-Services

Kredite und Finanzierung

Tagesgeld und Girokonten

Partnersuche

Reisen

Hotelsuche

Handytarif-Vergleich

Stromtarif-Vergleich

Gastarif-Vergleich

Internet Flatrate Vergleich

Versicherungsvergleich

Immobilien

Verbraucherschutz



TERRE DES LANGUES e.V. - 25 Jahre Erfahrung in Sprachreisen, Schuljahresaufenthalte, Highschool-Years und Gastfamilienaufenthalte


www.user-archiv.de (seit 1999)