Einführung in die Programmiersprache C: HelloWorld und erste Programme
Die wichtigsten Konzepte der Programmiersprache C werden an einfachen Programm-Beispielen erläutert: HelloWorld-Anwendung zum Testen der Arbeitsumgebung, Konsolenein- und ausgaben, Deklaration und Initialisierung von Variablen, arithmetische und logische Operationen, Ablaufsteuerung mit Bedingung und Alternative. Zudem werden einige methodische Hinweise gegeben, wie man gerade als Anfänger beim Programmieren strukturiert vorgehen sollte.
- Einordnung des Artikels
- Einführung
- Hello World
- Anlegen eines ersten Projektes in der Entwicklungsumgebung
- Der Quelltext für HelloWorld
- Ausführen aus der Konsole
- Methodischer Hinweis: Wie geht man bei der Entwicklung eines Programms vor?
- Ein erstes Programm: arithmetische Operationen
- Überblick über die Aufgabenstellung
- Die Aufgabenstellung
- Ein Lösungsvorschlag
- Konsolenausgaben und -eingaben
- Die Funktion printf()
- Die Funktion scanf()
- Das Leeren des Ein- und Ausgabe-Puffers mit fflush()
- Deklaration und Initialisierung von Variablen
- Arithmetische Operationen
- Ein zweites Programm: logische Operationen und Ablaufsteuerung
- Die Aufgabenstellung: Sortieren dreier Zahlen
- Vorschlag zur Lösung
Einordnung des Artikels
- Einführung in die Informatik
- Einführung in die Programmiersprache C und in C-Zeiger
- Einführung in die Programmiersprache C: HelloWorld und erste Programme
- Einführung in die Programmiersprache C und in C-Zeiger
Einführung
Da hier nur eine kurze Einführung in die Programmiersprache C gegeben werden soll, werden die Voraussetzungen zum Programmieren nicht besprochen:
- Es wird nicht beschrieben, wie ein C-Compiler installiert wird.
- Es wird keine Entwicklungsumgebung ausdrücklich empfohlen und ihre Installation beschrieben.
Stattdessen wird vorausgesetzt, dass Sie über eine Arbeitsumgebung verfügen, die erlaubt C-Programme zu erstellen, zu übersetzen und auszuführen. Entwicklungsumgebungen, die dies ermöglichen und die es bereits mit einem "eingebauten" Compiler gibt, sind etwa Code::Blocks oder Eclipse.
Ebenso werden nicht alle Konzepte von C ausführlich vorgestellt. Es werden lediglich Beispiel-Programme für die wichtigsten Konzepte vorgestellt und erläutert.
In diesem ersten Abschnitt wird – wie immer – mit dem Programm HelloWorld begonnen. Es dient weniger dazu, erste Programmier-Konzepte einzuüben, vielmehr soll damit getestet werden, ob die Programmier-Umgebung fehlerfrei arbeitet.
Weiter werden einige Programme vorgestellt, in denen:
- Ein- und Ausgaben über die Konsole gemacht werden.
- Variablen angelegt werden und einige Datentypen vorgestellt werden.
- Einige arithmetische und logische Operationen eingesetzt werden.
- Die Ablaufsteuerung eines Programms mit if und else erfolgt.
Hello World
Um zu testen, ob in der Arbeitsumgebung alle Schritte zum erfolgreichen Programmieren ausgeführt werden können, wird zunächst eine einfache HelloWorld-Anwendung erstellt.
Die nötigen Schritte sind:
- Anlegen eines Projektes in der Entwicklungsumgebung; dazu sollte Sie bereits einen geeigneten Ordner im Arbeitsverzeichnis angelegt haben.
- Erstellen des Quelltextes; dieser kann auch von der Entwicklungsumgebung erzeugt werden.
- Compilieren des Programms (dieser Schritt stellt sicher, dass die Entwicklungsumgebung den richtigen Compiler enthält und anspricht).
- Ausführen des Programms (dieser Schritt stellt sicher, dass Programme aus der Entwicklungsumgebung heraus ausgeführt werden können).
Um die Entwicklungsumgebung und ihre Arbeitsweise besser kennenzulernen, sollten Sie bei allen Schritten beobachten, welche Dateien angelegt werden. Dies sollten Sie nicht nur in der Entwicklungsumgebung beobachten, sondern – und dies ist sogar wichtiger – im Datei-Explorer. Denn dort sehen Sie auch diejenigen Dateien, die die Entwicklungsumgebung anlegt und zur Verwaltung des Projektes verwendet.
Anlegen eines ersten Projektes in der Entwicklungsumgebung
Aufgabe:
- Bereiten Sie einen geeigneten workspace für C-Programme vor.
- Erzeugen Sie dort eine Ordnerstruktur für Ihre Programme. Wichtig ist dabei, dass Sie Ihre selbst geschriebenen Programme dort schnell auffinden können – Sie werden Ihren workspace häufig als Nachschlagewerk einsetzen.
- Öffnen Sie Ihre Entwicklungsumgebung.
- Legen Sie ein neues C-Projekt an. Achten Sie dabei darauf, ob der Ordnername und der Projektname übereinstimmen müssen.
- Beobachten Sie ab jetzt bei jeder Ihrer Aktionen, welche Dateien angelegt werden. Verwenden Sie dazu den Datei-Explorer, da die Entwicklungsumgebung diejenigen Dateien nicht anzeigt, die zur Projektverwaltung benötigt werden (und die Sie nicht editieren sollten).
- Falls es Ihre Entwicklungsumgebung anbietet: Wählen Sie für Ihr Projekt als Schablone ein HelloWorld-Projekt.
- Wählen Sie ansonsten immer die von Ihrer Entwicklungsumgebung vorgeschlagenen Einstellungen aus – beim Anlegen des nächsten Projektes können Sie die Alternativen näher betrachten.
- Compilieren Sie das Programm.
- Führen Sie das Programm aus.
Beschreiben Sie, welche Dateien im Laufe dieser Schritte im Projekt-Ordner erzeugt wurden und welche Funktion diese Dateien haben.
Können Sie eine Erklärung geben, warum die .exe-Datei ein Vielfaches des Speicherplatzes der Quelltext-Datei benötigt!
Identifizieren Sie diejenige Datei, die zur Projekt-Verwaltung verwendet wird. Öffnen Sie diese Datei mit einem Text-Editor und versuchen Sie die Einträge zu verstehen. Editieren sollten Sie die Datei besser nicht – womöglich wird dadurch das Projekt "zerstört".
Der Quelltext für HelloWorld
In Eclipse wird als Schablone für ein HelloWorld-Projekt folgender Quelltext erzeugt:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
puts("Hello World!!!"); /* prints Hello World!!!
return EXIT_SUCCESS;
}
In Code::Blocks wird ein ähnlicher Quelltext erzeugt:
#include<stdio.h>
int main(void) {
printf("Hello World!");
return 0;
}
Da man die Schablonen selber anpassen kann, ist es möglich, dass Sie in Ihrer Entwicklungsumgebung einen leicht abgewandelten Quelltext sehen – die wichtigsten Elemente müssen aber enthalten sein (die Zeilenangaben beziehen sich auf den Eclipse-Quelltext):
- Inkludieren der Datei stdio.h (Zeile 1).
- Implementierung der main()-Funktion (Zeile 4 bis 8).
- Eine Ausgabe der Zeichenkette
HelloWorld
(Zeile 5; wie auch immer es geschrieben wird). - Das return-statement der main()-Funktion (Zeile 7).
Eine kurze Erklärung:
- Die Datei stdio.h wird benötigt, um Funktionen wie printf() oder puts() einsetzen zu können. Die Funktionen (und viele weitere mit verwandten Aufgaben) sind dort implementiert. Durch die Präprozessor-Direktive
#include<stdio.h>
wird die Datei geladen. Präprozessor-Direktive bedeutet, dass dies vor dem Compilieren geschieht. - Die main()-Funktion hat ihre besondere Bedeutung als Einstiegspunkt eines Programms: wird das Programm ausgeführt, wird automatisch die main()-Funktion aufgerufen. Der Datentyp int vor dem Namen main() bedeutet, dass die Funktion eine ganze Zahl als Rückgabewert besitzt. Die Konvention ist, dass der Rückgabewert 0 für eine fehlerfreie Ausführung des Programms steht. Im Argument von main() steht void, der "leere Datentyp" – soll heißen, dass main() keinen Eingabewert erwartet.
- Die Funktionen puts() und printf() werden eingesetzt, um die Zeichenkette (alles innerhalb der Anführungsstriche) auf der Konsole auszugeben. Die Funktion printf() hat weitere Funktionalitäten, die in HelloWorld noch nicht verwendet werden.
- Durch das return-statement wird die Abarbeitung der Funktion main() abgeschlossen. Damit ist zugleich die Abarbeitung des Programms beendet.
Ausführen aus der Konsole
Aufgabe:
Versuchen Sie Ihr HelloWorld-Programm aus der Konsole zu starten.
Methodischer Hinweis: Wie geht man bei der Entwicklung eines Programms vor?
Gerade Anfänger machen oft den Fehler, dass sie eine Aufgabenstellung lesen, sich sofort an die Entwicklungsumgebung setzen – und dann mit Erstaunen feststellen, dass
- die Entwicklungsumgebung keinerlei Hilfe anbietet, die Aufgabe zu lösen,
- die Aufgabenstellung eine Vielzahl von Detailfragen aufwirft,
- sie die nötigen Programmier-Konzepte nicht genügend beherrschen und
- ihnen daher völlig unklar ist, womit man eigentlich beginnen soll.
Abbildung 1 soll die Vorgehensweise beim Programmieren erläutern und Sie sollen sich möglichst früh eine strukturierte Arbeitsweise angewöhnen. Dass man sich an die Entwicklungsumgebung setzt und "sofort loslegt" ist auch für erfahrene Programmierer die Ausnahme – das erlauben nur echte Routine-Aufgaben, nicht aber ernsthafte Programmier-Aufgaben.
Links ist die grobe Vorgehensweise zu sehen:
- Sie sollten immer zuerst für Ihr Programm einen ersten Entwurf anstreben, in dem Sie das Problem möglichst unabhängig von einer Programmiersprache analysieren. Dazu gehört insbesondere, dass man sämtliche Formeln bereitstellt, die zur Lösung benötigt werden. Das Ergebnis dieser Entwurfsphase könnte ein Programmablaufplan (PAP) oder ein Struktogramm sein. Viele Programmierer begnügen sich auch mit einer Formulierung des Algorithmus in Pseudocode.
- Nachdem der Algorithmus trocken entwickelt wurde, sollten Sie sich fragen, welche Programmier-Konzepte in Ihrem Programm benötigt werden. Es ist keine Schande für einen Programmierer, wenn er sich eingestehen muss, dass er einige der benötigten Konzepte nicht ausreichend oder gar nicht kennt. Es ist umgekehrt eine wichtige Arbeitstechnik, dass man seine Lücken benennen und schnell schließen kann. Sie sollten daher immer Lehrbücher und eine Dokumentation der Programmiersprache zur Hand haben und vor dem eigentlichen Programmieren die nötigen Konzepte studieren.
- Erst dann sollten Sie sich an die Umsetzung des trocken entwickelten Algorithmus in die gewählte Programmiersprache machen.
Rechts in Abbildung 1 wird versucht, den Prozess des eigentlichen Programmierens darzustellen:
Es ist schwer hier eine zeitliche Abfolge anzugeben. Da sich meist erst bei der Umsetzung des Entwurfs zeigt, ob Sie die nötigen Programmier-Konzepte tatsächlich verstanden haben, und sich im Verlauf der Arbeit neue Probleme ergeben, werden Sie auch jetzt immer wieder in der Theorie nachschlagen müssen. Auch dies sollten Sie als den Normalfall ansehen – oder besser als Gelegenheit, um mit neuen Konzepten vertraut zu werden.
Zudem sollten Sie möglichst schnell jeden neu geschriebenen Quelltext testen – manchmal geschieht dies sogar bei jeder neuen Zeile. Erst erfahrene Programmierer schreiben längere Abschnitte ohne sie zu testen – aber auch nur, wenn sie mit den verwendeten Konzepten gut vertraut sind.
Zu den Tests gehört dann auch, dass man Grenzfälle untersucht, insbesondere Eingaben, die eigentlich nicht vorgesehen sind. Man muss immer davon ausgehen, dass der Nutzer die Implementierung nicht kennt und daher nicht weiß, welche Fehler auftreten können. Er wird irgendwann Eingaben machen, die nicht der eigentlichen Aufgabe entsprechen. Sie als Programmierer sind verantwortlich dafür, diese Fehler abzufangen und einen Programm-Absturz zu vermeiden.
Ein erstes Programm: arithmetische Operationen
Überblick über die Aufgabenstellung
Mit dem ersten eigenen Programm sollen Sie folgende Arbeitstechniken und Programmier-Konzepte kennenlernen:
- Anlegen eines Projektes in der Entwicklungsumgebung.
- Innerhalb der Funktion main() sollen:
- Ein- und Ausgaben auf der Konsole gemacht werden.
- Variablen deklariert und initialisiert werden.
- Arithmetische Operationen mit den Variablen ausgeführt werden.
Die Aufgabenstellung
- Legen Sie ein Projekt mit einem Namen wie etwa Arithmetik an. Da Sie schon in Ihrem Arbeitsverzeichnis eine geeignete Ordnerstruktur angelegt haben, können Sie dort das Projekt passend einordnen.
- Verwenden Sie beim Anlegen des Projektes die Voreinstellungen, die für HelloWorld verwendet wurden. Achten Sie jetzt beim Anlegen des Projektes genauer darauf, welche Voreinstellungen Sie auswählen und ob es vielleicht nützliche Alternativen oder zusätzliche Einstellungen gibt. Verfolgen Sie das Anlegen des Projektes wieder im Datei-Explorer.
- Das Programm soll eine Reihe von Anweisungen enthalten, die alle innerhalb der Funktion main() stehen. Diese Anweisungen lauten:
- Es werden zwei Variablen a und b vom Datentyp int deklariert.
- Der Nutzer wird aufgefordert die beiden Variablen einzugeben und a und b werden durch die Konsoleneingaben initialisiert.
- Folgende Werte sollen auf der Konsole ausgegeben werden:
- Die eingegebenen Zahlen a und b.
- Summe: a + b
- Differenz: a - b
- Produkt: a * b
- Division: a / b (das Ergebnis ist im Allgemeinen eine Fließkommazahl)
- Ganzzahldivision: a / b (das Ergebnis ist eine ganze Zahl)
- Rest bei Division: a % b (dies wird in der Mathematik mit a mod b bezeichnet; das Ergebnis ist eine ganze Zahl, die die Werte 0, 1, ..., b-1 annehmen kann).
Führen Sie das Programm aus und testen Sie insbesondere:
- Wie reagieren die Divisionen, wenn b = 0.
- Liefern die Ganzzahldivision und die Berechnung des Restes sinnvolle Ergebnisse, wenn a oder b (oder beide gleichzeitig) negativ sind?
Der grobe Entwurf ist größtenteils durch die Aufgabenstellung vorgegeben; für einen Programmier-Anfänger sind hier – gegenüber HelloWorld – zahlreiche neue Programmier-Konzepte enthalten.
Mit Ganzzahl-Division ist diejenige Division gemeint, die Sie zuerst in der Schule gelernt haben; so ist etwa
20 / 6 = 3 Rest 2, weil 3 · 6 + 2 = 20.
Aufgaben:
1. Benennen Sie die für die Lösung der Aufgabenstellung benötigten Programmier-Konzepte und beurteilen Sie, inwieweit Sie mit ihnen vertraut sind.
2. Entwickeln Sie das geforderte Programm!
Ein Lösungsvorschlag
Das folgende Listing macht einen Vorschlag, wie man die Programmier-Aufgabe lösen kann. Beachten Sie dabei:
- Die Lösung einer Programmier-Aufgabe ist niemals eindeutig. Üblicherweise werden an jedes Programm Zusatzanforderungen gestellt; man kann sich dann nur fragen: wurde die Aufgabe richtig gelöst und wurden die Zusatzanforderungen erfüllt. Hier wurde versucht, den Quelltext möglichst übersichtlich und leicht nachvollziehbar zu gestalten.
- Zum Erstellen wurde die HelloWorld-Schablone von Eclipse verwendet und anschließend abgeändert.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
// Deklaration:
int a;
int b;
// Eingabe:
puts("Geben Sie zwei ganze Zahlen ein!");
fflush(stdout);
scanf("%i \n %i \n", &a, &b);
fflush(stdin);
// Ausgabe a, b:
printf("a = %i, b = %i \n", a, b);
// Arithmetische Operationen:
printf("a + b = %i \n", a + b);
printf("a - b = %i \n", a - b);
printf("a * b = %i \n", a * b);
printf("Ganzzahl-Division: a / b = %i \n", a / b);
printf("Division: a / b = %f \n", (double) a / b);
int r = a % b;
printf("Rest: a %% b = %i \n", r);
return EXIT_SUCCESS;
}
Für a = 20 und b = 6 lauten die Ausgaben:
a = 20, b = 6
a + b = 26
a - b = 14
a * b = 120
Ganzzahl-Division: a / b = 3
Division: a / b = 3.333333
Rest: a % b = 2
Welche Programmier-Konzepte werden hier verwendet, die über das HelloWorld-Programm hinausgeben?
- Es werden nicht nur Ausgaben auf der Konsole gemacht, sondern auch Eingaben (mit scanf()).
- Dadurch dass die Ein- und Ausgaben abwechselnd erfolgen, ist es nötig, die Ein- und Ausgabe-Puffer zu leeren (mit fflush()).
- Es werden Variable angelegt (hier mit Datentyp int) und zwar zuerst deklariert und mit der Konsoleneingabe initialisiert.
- Es werden arithmetische Operationen ausgeführt; die Divisionen beinhalten einige Spitzfindigkeiten, insbesondere eine Typumwandlung.
Aufgabe:
Diskutieren Sie: In Zeile 26 wird zur Berechnung des Restes die Variable r angelegt, die in Zeile 27 ausgegeben wird. Bei den anderen Operationen wurde keinen eigene Variable verwendet. Welches Verfahren ist vorzuziehen?
Lösung:
Da mit den Ergebnissen wie a + b
nicht weitergerechnet wird, ist es nicht nötig, sie in einer Variable abzuspeichern. Dadurch wird nur Rechenzeit und Speicherplatz verschwendet.
Für den Leser ist lediglich besser nachzuvollziehen, welche Berechnungen durchgeführt werden, wenn eine Anweisung wie in Zeile 26 explizit angegeben wird.
In Zukunft sollten Sie bei allen Berechnungen überlegen, ob das Anlegen einer neuen Variable nötig ist; gerade wenn es in langen Schleifen vorkommt, kann dies die Speicherauslastung wesentlich beeinflussen.
Konsolenausgaben und -eingaben
Die Funktion printf()
Im HelloWorld-Programm wird die Funktion printf() eingesetzt wie puts(): sie erwartet eine Zeichenkette als Argument und diese Zeichenkette wird auf der Konsole ausgegeben.
Das Arithmetik-Programm oben zeigt die Verwendung von printf(), die puts() nicht leisten kann: Vergleicht man etwa Zeile 18 und Zeile 21, so sieht man, dass printf() auch zwei oder drei Argumente aufnehmen kann. Im Allgemeinen können es beliebig viele Argumente sein, allerdings mit der Einschränkung:
- das erste Argument muss eine Zeichenkette sein und
- die weiteren Argumente sind Variablen.
Dabei kann die Zeichenkette auch Formatierungsanweisungen enthalten.
Eine Anweisung wie printf("a = %i, b = %i \n", a, b);
ist dann folgendermaßen zu lesen:
- Die Zeichenkette enthält die beiden Formatierungsanweisungen
%i
; sie sind Platzhalter für die beiden Variablen, die in der folgenden Liste stehen (hier die Variablen a und b). Das i in der Formatierungsanweisung steht dafür, dass eine ganze Zahl (i wie integer) ausgegeben werden soll, also ohne Nachkommastellen. - Das Zeichen
\n
sorgt für einen Zeilenumbruch am Ende der Zeichenkette.
Damit sollte die Ausgabe a = 20, b = 6
verständlich sein.
Möchte man Fließkommazahlen ausgeben, lautet die Formatierungsanweisung %f
(f wie floating), siehe etwa Zeile 25.
Es gibt noch eine Vielzahl weiterer Formatierungsanweisungen, die man in der C-Dokumentation zu printf() nachlesen kann.
Die Funktion scanf()
Die Funktion scanf() erwartet wie printf() eine Zeichenkette und eine Liste von Variablen wie in scanf("%i \n %i \n", &a, &b);
(Zeile 14). Der Unterschied besteht aber darin, dass nicht die Variablen in der Liste stehen, sondern deren Adressen wie &a
, was an dem "kaufmännischen UND" erkennbar ist. Was genau mit den Adressen der Variable gemeint ist, wird erst verständlich, wenn Zeiger besprochen werden. Zunächst sollte man es so verstehen: ein Anweisung wie in Zeile 14 sorgt dafür, dass die Variablen a und b die Werte annehmen, die auf der Konsole eingegeben werden.
Das Leeren des Ein- und Ausgabe-Puffers mit fflush()
Im HelloWorld-Programm war keine fflush()-Anweisung enthalten. Erst wenn abwechselnd Ein- und Ausgaben gemacht werden ist es nötig, die entsprechenden Puffer zu löschen, ganauer:
- Nach einer Ausgabe muss der Ausgabe-Puffer geleert werden, damit anschließend Eingaben erfolgen können; dies geschieht mit
fflush(stdout);
, wobei stdout für den Standard-Ausgabestrom steht (siehe Zeile 12). - Nach einer Eingabe muss der Eingabe-Puffer geleert werden mit
fflush(stdin);
(Standard-Eingabestrom), um neue Ausgaben zu ermöglichen (siehe Zeile 15).
Aufgabe.
Testen Sie obiges Programm, wenn Sie die fflush()-Anweisungen weglassen!
Deklaration und Initialisierung von Variablen
Mit einer Anweisung wie int a;
(siehe Zeile 7) wird eine Variable vom Datentyp int (Abkürzung für integer) deklariert. Damit ist gemeint, dass
- ein Speicherplatz reserviert wird, dessen Größe einer int-Variable entspricht, und
- dieser Speicherplatz mit Hilfe des Namens der Variable (hier a) angesprochen werden kann.
Die Initialisierung der Variable a geschieht oben mit Hilfe der scanf()-Anweisung, das heißt in der Speicherzelle von a wird der Wert abgelegt, der auf der Konsole eingegeben wird. Am häufigsten geschieht die Initialisierung sofort mit der Deklaration, etwa mit int n = 17;
.
Aufgabe:
Testen Sie, welchen Wert man erhält, wenn man eine Variable deklariert, noch nicht initialisiert und sofort ihren Wert mit printf() ausgibt.
Testen Sie dies mit verschiedenen Variablen. Ist des Ergebnis reproduzierbar?
Man sagt nicht-initialisierte Variablen haben einen "unbestimmten Wert". Können Sie eine Erklärung geben, in welchem Sinn der Wert "unbestimmt" ist?
Arithmetische Operationen
Die Operationen +
, -
und *
erklären sich selbst. Deutlich schwieriger ist die Division, da man zwischen der Ganzzahl-Division und der Division von Fließkommazahlen unterscheiden muss. In vielen Programmiersprachen gibt es eigene Operatoren für diese beiden Divisionen, in C wird immer das Symbol /
verwendet, aber seine Bedeutung hängt vom Datentyp der Variablen ab, die dividiert werden.
Es gelten die Regeln:
- Sind Zähler und Nenner von ganzzahligem Datentyp, steht der Operator
/
für die Ganzzahl-Division. - Sind Zähler oder Nenner (oder beide) Fließkommazahlen, dann steht der Operator
/
für die Division von Fließkommazahlen.
Daher wird in Zeile 25 die Typumwandlung (double) a
vorgenommen, womit a in eine Fließkommazahl (double) verwandelt wird. Man hätte auch Zähler und Nenner umwandeln können.
Wirkungslos wäre aber (double) (a / b)
; denn jetzt wird zuerst die Ganzzahl-Division ausgeführt (mit ganzzahligem Ergebnis) und dann erst die Typumwandlung vorgenommen.
Der Rest einer Ganzzahl-Division wird mit dem Operator %
berechnet (siehe Zeile 26). In der Mathematik bezeichnet man den Operator meist mit mod (Abkürzung für modulo).
Aufgabe:
Nehmen Sie jetzt Ihren eigenen Quelltext unter die Lupe und prüfen Sie:
- Enthält Ihr Quelltext logische Fehler?
- Haben Sie einige Anweisungen unnötig umständlich realisiert?
Versuchen Sie Ihren Quelltext zu verbessern!
Methodischer Hinweis:
Auch ein gesundes Maß an Selbstkritik gehört zu den Arbeitstechniken eines Programmierers. Man sollte sich nicht mit einer Lösung zufrieden geben, die zwar den gestellten Anforderungen gerecht wird, aber offensichtlich "schlecht" programmiert ist. Gerade wenn Programme weiterentwickelt werden sollen, wird man auf "saubere" Quelltexte achten.
Ein zweites Programm: logische Operationen und Ablaufsteuerung
Um den Ablauf eines Programms zu steuern, setzt man
- logische Operationen (logisches UND
&&
, logisches ODER||
, Verneinung!
) ein und - verwendet die Bedingungsprüfung und Alternative.
Die Bedingungsprüfung erfolgt mittels if, eine Alternative wird mit if else realisiert.
Das folgende Listing zeigt zwei einfache Beispiele:
// Bedingungsprüfung:
// x ist eine bereits initialisierte Variable
if(x < 0){
x = -x;
}
// Alternative:
// x ist initialisiert, y deklariert
if(x < 0){
y = -x;
} else {
y = x;
}
In der Bedingungsprüfung if()
kann natürlich auch ein komplexer Ausdruck stehen, der logische Operationen einsetzt. Wieder zwei einfache Beispiele:
// x und y initialisiert
// Logisches UND &&
if(x > 0 && y > 0){
puts("x * y ist positiv");
}
// Logisches ODER ||
if( (x > 0 && y < 0) || (x < 0 && y > 0) ){
puts("x * y ist negativ");
}
Die folgende Aufgabe soll den Umgang mit logischen Operationen und der Ablaufsteuerung einüben.
Die Aufgabenstellung: Sortieren dreier Zahlen
Der Nutzer soll drei ganze Zahlen eingeben. Die Zahlen sollen anschließend sortiert ausgegeben werden – und zwar mit der größten Zahl zuerst.
Vorschlag zur Lösung
Es gibt unzählige Möglichkeiten, wie die Lösung der Aufgabe aussehen kann. Im Folgenden wird eine Lösung gezeigt,
- die zwar eine einfache Logik besitzt, da alle möglichen Fälle der Reihe nach abgearbeitet werden,
- die aber zu einem Wust an if und else Zweigen führt.
Sie sollten sich bereits jetzt merken:
- Derart aufgeblähte Quelltexte sind unbedingt zu vermeiden: Die Fehlersuche darin ist nahezu unmöglich.
- Sie sollten beim Entwurf eines Programms daran denken, dass Sie if else nicht zu oft einsetzen.
- Sie werden später Konzepte kennenlernen, die den folgenden Quelltext auf etwa ein Drittel verkürzen (entweder mit Hilfe der Funktion swap() oder mit einer Rekursion).
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int a = 20;
int b = 30;
int c = 40;
int max, med, min;
if (a > b && a > c) { // a ist Max.
max = a;
if (b > c) { // a > b > c
med = b;
min = c;
} else { // a > c > b
med = c;
min = b;
}
} else { // b oder c ist Max.
if (b > c) { // b ist Max.
max = b;
if (a > c) { // b > a > c
med = a;
min = c;
} else { // b > c > a
med = c;
min = a;
}
} else { // c ist Max.
max = c;
if(a > b){ // c > a > b
med = a;
min = b;
} else { // c > b > a
med = b;
min = a;
}
}
}
// Ausgaben:
printf("a = %i, b = %i, c = %i \n", a, b, c);
printf("max = %i, med = %i, min = %i", max, med, min);
return EXIT_SUCCESS;
}
Bemerkung:
Laut Aufgabenstellung muss die Eingabe mit scanf() realisiert werden. Der Einfachheit halber wurde es vermieden – wie man dies realisiert, wurde in der letzten Aufgabe gezeigt.
Die Ausgaben lauten:
a = 20, b = 30, c = 40
max = 40, med = 30, min = 20
Methodischer Hinweis:
Wie gesagt ist der Quelltext unnötig aufgebläht; mit den bis jetzt zur Verfügung stehenden Mitteln ist es aber schwer, eine einfachere Lösung anzugeben. Sie sollten aber aus dem Beispiel unbedingt lernen:
Eine konsequente Quelltext-Formatierung erleichtert das Lesen.
Zur Quelltext-Formatierung gehören:
- Einrückungen, die untergeordnete Anweisungen kennzeichnen.
- Leerzeilen, um Absätze (wie in einem Text) zu erzeugen.
- Leerzeichen um Operatoren, also etwa
a > b
und nichta>b
. - Kurze, hilfreiche Kommentare, die das Lesen des Quelltextes nicht stören. Ausführliche Erklärungen sollten am Anfang oder Ende der Dateien stehen oder in einer eigenen Dokumentation.