Selber Programmieren Wenn es für ein Problem keine fertige Lösung gibt, oder wenn man keine fertige Lösung finden kann, dann muss man sich das benötigte Werkzeug selbst herstellen: Ein eigenes Programm schreiben. Bevor man anfängt zu programmieren, sollte man unbedingt über das zu lösende Problem nachdenken. Danach muss man nochmal darüber nachdenken. Dann kann man erste Entscheidungen treffen und einen ersten Programmentwurf machen. Man muss bei der Programmentwicklung jederzeit bereit sein, alles wieder wegzuwerfen und von vorn anzufangen, wenn man einen neuen und besseren Ansatz oder Programmentwurf gefunden hat. Wer sich für eine quick-n-dirty-lösung entscheidet, der hat schon verloren. Quick-n-dirty-Lösungen sind Zeitverschwendung. Immer. Niemals schreibt man ein Programm nur für sich selbst. Im Physik-Umfeld wird dein Programm immer von anderen benutzt, begutachtet, gewartet und verändert und weiter entwickelt werden. Beweis: Wir arbeiten innerhalb einer größeren oder kleineren Kollaboration oder Gruppe. Wenn wir ein Programm geschrieben haben, das etwas nützliches tut, dann ist das Programm auch für andere in der Kollaboration nützlich, und wird deshalb von anderen benutzt oder für deren Programmentwicklung verwendet werden. Wissenschaftliche Resultate müssen nachvollziehbar und reproduzierbar sein - was nicht nachvollziehbar ist, ist kein wissenschaftliches Resultat. Also müssen wir die Werkzeuge / die Programme, mit deren Hilfe wir zu einem Resultat gekommen sind, offenlegen. Folgerung und Erfahrungstatsache: Wenn man es nicht gleich ordentlich macht, macht man es (mindestens) zweimal. Und das ist Verschwendung von Zeit und Arbeit.
Vom Quellcode zum lauffähigen Programm Programmierer & Editor Quelldateien.cc.h Compiler Im Folgenden: git zur Verwaltung der Quelldateien g++ als Compiler und als Linker stdlib und BOOST als nützliche Bibliotheken gdb und valgrind zur Fehlersuche Object-Dateien.o Object-Bibliotheken.a.so Linker Ausführbares Programm Shared Libs Loader Wie der loader ein Programm mit den shared libs verbindet und es zum laufen bringt, haben wir schon gehört. Laufendes Programm
Programmiersprachen Welche Programmiersprache eignet sich zur Lösung meines Problems? In der HEP verbreitete Programmiersprachen: Assembler, C++, C, Java, Python, Perl Im Rest der Welt anzutreffende Sprachen: Scala, Lisp, Basic, Forth, Javascript, PHP, C#, Objective C, go, Dinosaurier-Sprachen: Cobol, Fortran, Pascal, Modula, Kinder- und Lern-Sprachen: Pilot, Script-Sprachen - Compiler-Sprachen Prozedurale Sprachen - OO Sprachen Sprachen der 0., 1. und 2. Generation 0. Generation: Assembler, Fortran 1. und 2. Generation: Hochsprachen Aus unterschiedlichen Sprachen kompilierter Code läßt sich beim Linken häufig mischen und somit in einem Programm nutzen.
Externe Pakete Wenn es für unser Problem auch keine fertige Lösung gibt, dann vielleicht für Teile davon! Das gesamte Problem in (möglichst voneinander unabhängige) Teilprobleme zerlegen Die Software in Pakete unterteilen Gibt es in der Kollaboration oder in der Welt fertige Lösungen für einige Teilprobleme? Externe Pakete unseres Software-Projektes Vorteile: Was es schon gibt, müssen wir nicht selber programmieren und nicht so intensiv testen. Fehlerbehebung und neue Features in neuen Versionen werden von anderen gemacht. Die Auswahl externer Pakete kann die Wahl der Programmiersprache beeinflussen. Beispiel: Entwicklung eines Reglers für die Kühler des Proto192 Idee: Ein Fuzzy-Regler könnte geeignet sein. Neben dem Regler brauchen wir eine thermische Simulation um unsere Regelsoftware testen zu können. Recherche: Es gibt eine standardisierte Sprache zur Definition von Fuzzy-Reglern, und es gibt Open-Source Implementierungen davon: Die holen wir als externes Paket ins Boot und probieren sie aus. Es gibt auf Matlab basierende Software für thermische Simulationen: Ausprobieren, und dann als externes Paket benutzen Die externen Pakete sind mit C++-Code linkbar / haben ein C++-API Wir können unsere Lieblingssprache verwenden
Datentypen Primitive Datentypen: Ganze Zahlen: int, unsigned int, (unsigned) short, (unsigned) char, (unsigned) long Achtung: Bei hardware-naher Programmierung unbedingt Typen mit definierter Länge verwenden: (u)int8_t, (u)int16_t, (u)int32_t, (u)int64_t (in C, nicht in C++) Gleitkommazahlen: float, double Bool'sche Variablen: bool Felder: typ name[anzahlderelemente]: reservierter Speicherplatz, in den anzahlderelemente Variablen vom Typ typ passen. Zeiger/pointer: der Inhalt eines Zeigers ist die Adresse einer Variablen indirekte Adressierung; typ* name oder typ *name
Datentypen Die fogenden Folien können den Stroustrup nicht ersetzen!! Komplexere Datentypen: Strukturen: struct: mehrere Variablen unterschiedlichen Typs werden zu einem neuen Datentyp zusammen gefasst Beispiel: struct Adresse {char strasse[80]; unsigned int hausnummer; unsigned int plz; char stadt[80]; Frage: was ist in diesem Beispiel nicht gut gemacht? Aufzählungen: enum: Liste von Alternativen; Variablen können einen Wert aus dieser Liste annehmen typedef: ein zweiter Name für einen Datentyp Klassen: class: Klassen sind auch Strukturen, also auch Datentypen. Mehr zu Klassen später. C++ ist typsicher. Einer Variablen kann nicht ein Wert eines anderen Typs zugewiesen werden. Das gilt auch und gerade für pointer-variablen. Es gibt Mittel, um die Typsicherheit zu durchbrechen, aber: Wenn irgend möglich, die Finger davon lassen!!
Programmablauf-Steuerung - Control Structures Der Einfachheit halber verwende ich ab jetzt im Wesentlichen englische Ausdrücke compound-statement or block: { : fasst Anweisungen zu einem Block zusammen, der wie eine Anweisung behandelt wird. Die {-Klammern legen den Gültigkeitsbereich von Variablen fest. ( )-Klammern umschliessen Argument-Listen, expressions und conditions. conditional structure: if / else: bedingte Ausführung eines Blockes: if (condition) statement1 else statement2; if (x == 100) { else y = 25; std::cout << x is 100 << std::endl; y = 10; Schleifen: Nicht abweisende Schleife: while loop: while (expression) statement; Abweisende Schleife: do-while loop: do statement while (condition); Zählschleife: for loop: for (initialization; condition; increase) statement; initialization und increase können mehrere, mit Komma getrennte Anweisungen enthalten. Vielseitig verwendbar for (n=0, i=100; n!=i; n++, i--) { sum += n;
Programmablauf-Steuerung - Control Structures break statement: Verlasse diese Schleife! #include <iostream> using namespace std; int main () { int n; for (n=10; n>0; n--) { cout << n << ", "; if (n==3) { cout << "countdown aborted!"; return 0; continue statement: continue; Breche diese Iteration hier ab! #include <iostream> using namespace std; int main () { for (int n=10; n>0; n--) { if (n==5) continue; cout << n << ", "; cout << "FIRE!\n"; return 0; Was erwartet ihr, was die beiden Programme ausgeben? Filenamen ausdenken und Programme eintippen Übersetzen mit: g++ -o breaktest <filename1> g++ -o continuetest <filename2> Wie sehen die Ausgaben der Programme aus? Wie könnte man statt ausführbarer Programme.o-Files produzieren? Was hat es mit dem assemble in der man-page auf sich?
Programmablauf-Steuerung - Control Structures selective structure: switch: Wähle eine aus mehrere Optionen switch (expression) { case constant1: group of statements 1; case constant2: group of statements 2;... default: default group of statements switch (x) { case 1: std::cout << "x is 1"; counter++; case 2: std::cout << "x is 2"; default: std::cout << "value of x unknown"; std::cout << std::endl; switch-konstrukte sind deutlich einfacher zu lesen und zu schreiben als if-else-ketten, und deshalb zu bevorzugen. exit functiion: exit(exitcode): Beende das Programm und liefer exitcode als Resultat zurück. Konvention: exitcode = 0: Programm erfolgreich beendet exitcode!= 0: Ein Fehler ist aufgetreten
Operatoren Operatoren haben einen rechten, oder einen linken, oder einen rechten und einen linken Operanden Operatoren berechnen aus den Operanden irgend etwas und liefern das Ergebnis zurück verändern den linken Operanden Zuweisungen: variable = expression; variable++; ++variable; variable--; --variable; Arithmetische Operatoren: + - * / % Vergleichsoperatoren: ==!= < > <= >= Tippfehlergefahr: = und == Bool'sche Operatoren: &&! Bit-Operatoren: & ^ ~ << >> Tippfehlergefahr: & und &&, und Mit Zuweisung kombinierte Operatoren: linkem Operanden wir das Resultat der Operation zugewiesen += -= *= /= %= &&= =!= &= = ^= ~= <<= >>= Adressoperator: & liefert Adresse einer Variablen, die in einem Pointer gespeichert werden kann Zugriffsoperatoren: [ ]. ->.* ->* Einige weitere Operatoren, auf die wir später stoßen werden.
Input und Output input und output in C++ läuft über streams #include <iostream> : stellt alle benötigten Definitionen bereit Standard input stream: std::cin Standard output stream: std::cout Standard error output stream: std::cerr Der eigentliche input und output erfolgt über die Operatoren << und >> Um eigene Datentypen einlesen und ausgeben zu können, müssen die diese Operatoren implementieren Int x; std::cout << enter x: ; std::cin >> x; switch (x) { case 1: std::cout << "x is 1"; counter++; case 2: std::cout << "x is 2"; default: std::cerr << "value of x unknown" << std::endl; std::cout << std::endl; Gibt ein Prompt auf dem Bildschirm aus Liest eine ganze Zahl ein Gibt eine Meldung auf dem Standard-Output aus Gibt eine Meldung auf dem Standard-Error-Output aus