Einführung in C/C++ unter Voraussetzung von Programmierkenntnissen in Java.
Hier gibt es das Skriptum zum herunterladen: PDF, 476K. Außerdem noch die Beispiel-Programme aus dem Skriptum.
Jeder bekommt eine individuelle Programmieraufgabe, die für C
und C++ gilt, in einer Datei aufgabe.txt
in seinem
Subversion-Verzeichnis. Die erste Zeile dieser Datei ist
programm bahnhof.dat zug.dat fahrplan.dat [weitere
parameter]
. Das heißt: Du sollst ein Programm namens
programm
erzeugen, das als erstes
Kommandozeilenargument einen Dateinamen akzeptiert und als
zweiten und dritten auch. Dahinter kommen möglicherweise noch weitere Parameter,
wenn es die Aufgabe verlangt. Der Name des Programms ist ganz
wichtig (also programm
), sonst muss ich bei 50
Leuten rätseln, was für ein Programm ich nun aufrufen soll.
Bis zum Mo 2. Mai soll das C-Programm programmiert werden.
Das Programm wird von mir mit evtl. Verbesserungsvorschlägen
kommentiert. Bis Mo 16. Mai kann das Programm dann noch
verbessert werden. Alle C-Files und Header-Files sollen in einem
Unterverzeichnis c/
sein. Bis zum Mo 6. Juni
soll das C++-Programm programmiert werden, und zwar im
Unterverzeichnis cpp/
. Bis zum Mo 27. Juni kann
dann noch verbessert werden. Erstelle in diesen Unterverzeichnissen
auch ein Makefile
. Mittels make
ohne
Argumente muss das Programm programm
erzeugt werden.
Adde nur .c
-Files, .h
-Files und das
Makefile
(und natürlich die Verzeichnisse c/
und cpp/
) ins Subversion-Repository. Ich übersetze das
Programm selbst.
Die zu erstellenden Programme sollen auf die Dateien bahnhof.dat
, zug.dat
und fahrplan.dat
zugreifen. (Dateien jetzt mit
mehr Inhalt und ohne doppelte Bahnhofs-Ids!) Der exakte Filename wird allerdings auf der
Kommandozeile übergeben. Die Dateien zum Testen bitte hier
herunterladen (und nicht ins Subversion einchecken). Die erste
Datei bahnhof.dat
enthält:
BahnhofId Name
Die Einträge sind durch ein Leerzeichen getrennt, Name
enthält keine Leerzeichen. Ein Bahnhof ist eindeutig durch BahnhofId
(natürliche Zahl) identifiziert. Wichtig: Wenn bei
Programmaufruf in der Kommandozeile ein Bahnhof angegeben wird, wird
dort immer der Name angegeben. Bei der Ausgabe von Bahnhöfen soll auch
immer der Name ausgegeben werden. Die zweite Datei zug.dat
enthält ein Verzeichnis von Zügen:
ZugId AnzahlWaggons
Die dritte Datei fahrplan.dat
enthält für jeden Halt eines
Zugs in einem Bahnhof eine Zeile:
ZugId BahnhofId An-Stunde An-Minute Ab-Stunde Ab-Minute AnzahlAussteiger AnzahlEinsteiger
Es ist sinnvoll, diese Datei nach dem Einlesen nach ZugId und An-Stunde und An-Minute zu sortieren. So ergeben sich aufeinander folgende Halte eines Zuges als aufeinander folgende Einträge der Liste. Es ist wahrscheinlich auch sinnvoll, An-Stunde und An-Minute durch Minuten seit 0 Uhr zu ersetzen, damit im Algorithmus leichter Zeiten miteinander verglichen und verknüpft werden können. Vor dem Ausgeben aber wieder zurück umwandeln!
Grundsätzlich muss das Programm funktionieren, damit es eine positive Note gibt. Das heißt, es muss compilierbar sein und das gewünschte Ergebnis liefern. Davon abgesehen, sollten folgende Kriterien beachtet werden:
Nicht alles in ein .c-File geben, das Hauptprogramm
programm.c
soll nur die Funktion main
enthalten, in C++ am besten für jede Klasse ein Header-File und (falls
notwendig) ein .cc-File. Die Aufteilung der Code-Files sollte nach
Problem-orientierten Kriterien organisiert sein (z.B. bahnhof.c,
route.c), nicht Implementierungs-orientiert (z.B. io.c, algo.c, ...).
Bitte keinen Spaghetticode, und schon gar nicht in main(). Funktionen, die sehr lang sind, können immer sinnvoll aufgeteilt werden, das Programm wird dadurch lesbarer und wartbarer.
Zusammengehörige Daten als Typ oder Klasse kapseln. Insbesondere beim
Einlesen der Dateien am besten für jede Zeile ein
struct
-Objekt bzw. in C++ ein Klassenobjekt erzeugen.
Bufferüberläufe verhindern.
Wenn in C ein Array mit fixer Größe angelegt wird, dann muss bei jedem Einfügen überprüft werden, ob die Größe überschritten wird.
Besser noch verwendet man dynamisch wachsende Arrays (siehe Beispielprogramme) bzw. STL-Container.
Das trifft auch auf Strings zu: beim Einlesen in C immer %100s
o.ä. verwenden statt nur %s
.
Keine globalen Variablen verwenden. Das ist schlechte Programmierpraxis, erweiterungsfeindlich und auch nicht thread-safe.
In C liegt das Augenmerk noch nicht auf Performance, Suchen und Sortieren in Arrays kann durch lineares Iterieren und Bubble-Sort o.ä. implementiert werden, es ist allerdings geschickt, die Id-Referenzen nach dem Einlesen in Pointer umzuwandeln, um ständiges Suchen zu vermeiden. In C++ müssen aber passende STL-Container oder Algorithmen verwendet werden, die logarithmische Komplexität für das Suchen haben.
Allozierten Speicher wieder freigeben.
Wer malloc
oder new
verwendet, muss die Daten immer mit free
oder delete
wieder freigeben.
In C/C++ gibt es keine Garbage-Collection.
In C++ Member-Variablen private machen. Zugriff nur über get- und set-Methoden, oder noch besser größere Zugriffsmuster in Methoden auslagern.
Das Makefile muss so aufgebaut sein, dass bei Veränderung eines .c-Files nicht das gesamte Projekt übersetzt wird sondern nur das eine .c-File,
die Objektfiles werden dann zusammengelinkt.
In C müssen dazu die Abhängigkeiten von den .o-Files richtig angegeben werden.
In C++ müssen auch die Abhängigkeiten von den .h-Files angegeben sein (am besten mit der cc -MM
-Methode).
Aussagekräftige Variablen- und Typnamen.
Z.B. fahrplanListe
statt myData
.
Das Kopieren ganzer Arrays und Container ist zu vermeiden.
Man beachte, dass eine Funktion vector<Bahnhof> removeBahnhof (vector<Bahnhof> bahnhoefe, string name)
sowohl beim Übergeben der Argumente als auch beim zurückgeben des Ergebnisses das ganze Array kopiert.
Und ein Aufruf wie bahnhoefe = removeBahnhof (bahnhoefe, "Salzburg")
produziert dann vollkommen unnötigen Rechenaufwand.
Sowas ist dann besser mit Pointern oder References zu lösen, etwa so: void removeBahnhof(vector<Bahnhof> &bahnhoefe, string &name)
.
Zur Programmabgabe und daher zur effektiven Anmeldung wird Subversion verwendet. Anmelden bis spätestens 21.3.. Folgende Schritte sind zu tun:
Ein Verzeichnis mit dem Usernamen im Repository anlegen:
svn mkdir https://svn.cosy.sbg.ac.at/lv/ccpp/rkutil -m Anmeldung
Das Verzeichnis auschecken:
svn checkout https://svn.cosy.sbg.ac.at/lv/ccpp/rkutil
Anmeldungsdaten (Nachname, Vorname, Matrikelnummer,
Emailadresse, jeweils in eigene Zeile) in die Datei
anmeldung.txt
eintragen:
cd rkutil
cat >anmeldung.txt
Kutil
Rade
9420697
rkutil@cosy.sbg.ac.at
Anmeldungsdaten commiten:
svn add anmeldung.txt
svn commit -m "Anmeldungsdaten eingetragen"
Statt rkutil
natürlich den eigenen Usernamen
verwenden.