Skip to content

Commit

Permalink
initial commit. Structure has already been partially implemented in the
Browse files Browse the repository at this point in the history
intro.tex files
  • Loading branch information
fschledorn committed Sep 28, 2024
1 parent 7330fd9 commit 808d25a
Show file tree
Hide file tree
Showing 27 changed files with 2,012 additions and 0 deletions.
13 changes: 13 additions & 0 deletions appendix/intro.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
\chapter{Appendix}
\pagestyle{empty}


\pagestyle{fancy}
\input{appendix/cheatsheet_shortcuts}
\input{appendix/cheatsheet_shellcomands}
\input{appendix/coding_style}
\input{appendix/fehler_fehlermeldungen}

\clearpage
\pagestyle{empty}

89 changes: 89 additions & 0 deletions lektionen/arith.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
\lesson{Arithmetik}

Wir haben in der vergangenen Lektion Variablen vom Typ \texttt{std::string}
kennengelernt. Zeichenketten zu speichern ist schon einmal ein guter Anfang,
aber wir wollen auch rechnen können, wir brauchen also mehr Typen für
Variablen.

\Cpp unterstützt eine Unmenge an Datentypen und hat auch die Möglichkeit,
eigene zu definieren. Wir wollen uns hier nur mit den wichtigsten beschäftigen.

Fangen wir mit dem wohl meist genutzten Datentyp an: Einem \texttt{int}, oder
\texttt{integer}. Dieser speichert eine ganze Zahl (mit bestimmten Grenzen, an
die wir aber erst einmal nicht stossen werden, von daher ignorieren wir sie
erst einmal frech). Mit \texttt{int}s können wir rechnen, das funktioniert in
\Cpp mit ganz normalen Rechenausdrücken, wie wir sie aus der Schule kennen,
plus den bereits angetroffenen Zuweisungen:

\inputcpp{arith1.cpp}

Wichtig ist hier, zu beachten, dass wir dem Computer ein in Reihenfolge
abgearbeitetes Programm geben, keine Reihe von Aussagen. Das bedeutet in diesem
konkreten Fall, dass wir z.B. nicht die Aussage treffen „\texttt{a} ist gleich
7“, sondern dass wir sagen „lasse zuerst \texttt{a} den Wert 7 haben. Lasse
dann \texttt{b} den Wert 19 haben. Lasse dann \texttt{c} den Wert haben, der
heraus kommt, wenn man den Wert von \texttt{b} vom Wert von \texttt{a}
abzieht“. Besonders deutlich wird dieser Unterschied bei einem Beispiel wie
diesem:

\inputcpp{arith2.cpp}

\begin{praxis}
\begin{enumerate}
\item Was gibt dieses Programm aus? Überlegt es euch zuerst und kompiliert
es dann, um es auszuprobieren.
\end{enumerate}

Obwohl \texttt{a = a + 19} mathematisch überhaupt keinen Sinn ergibt, ist doch
klar, was passiert, wenn man sich den Quellcode eben nicht als Reihe von
Aussagen, sondern als Folge von \emph{Anweisungen} vorstellt. Das
Gleichheitszeichen bedeutet dann nicht, dass beide Seiten gleich sein sollen,
sondern dass der Wert auf der linken Seite den Wert auf der rechten Seite
annehmen soll.

Wie wir in diesem Beispiel ausserdem sehen, können wir nicht nur Strings
ausgeben, sondern auch Zahlen. \texttt{std::cout} gibt sie in einer Form aus,
in der wir etwas damit anfangen können. Genauso können wir auch über
\texttt{std::cin} Zahlen vom Benutzer entgegen nehmen:

\inputcpp{arith3.cpp}

Langsam aber sicher tasten wir uns an nützliche Programme heran!

\begin{enumerate}[resume]
\item Schreibt ein Programm, welches von der Nutzerin zwei ganze Zahlen
entgegen nimmt und anschließend Summe, Differenz, Produkt und Quotient
ausspuckt.
\item Was fällt auf, wenn ihr z.B. 19 und 7 eingebt?
\item Findet heraus (Google ist euer Freund), wie man in \Cpp Division mit
Rest durchführt und gebt diese zusätzlich zu den bisherigen Operationen
mit aus\footnote{Falls ihr nicht weiterkommt, hilft euch vielleicht das
Stichwort „modulo“ oder „modulo-operator“ weiter.}.
\item Was passiert, wenn ihr als zweite Zahl eine 0 eingebt?
\end{enumerate}
\end{praxis}

\begin{spiel}
\begin{enumerate}
\item Findet heraus, was die größte positive (und was die kleinste
negative) Zahl ist, die ihr in einem \texttt{int} speichern könnt.
Faulpelze nutzen Google, Lernbegierige versuchen sie experimentell zu
ermitteln. Was passiert, wenn ihr eine größere Zahl eingebt?
\item Wir arbeiten bisher nur mit \texttt{int}s für ganze Zahlen. Wenn wir
mit gebrochenen Zahlen rechnen wollen brauchen wir den Datentyp
\texttt{double}. Schreibt euer Mini Rechenprogramm so um, dass es statt
\texttt{int}s nur noch \texttt{double} benutzt und probiert es aus.
Achtet darauf, dass es Dezimalpunkte und Dezimalkommata gibt, wenn ihr
überraschende Ergebnisse erhaltet.
\end{enumerate}
\end{spiel}

\textbf{Quiz 7}\\
\textit{Was passiert, wenn ihr \texttt{int} verwendet, aber eine Kommazahl eingebt?}
\begin{enumerate}[label=\alph*)]
\item Alles hinter dem Komma wird abgeschnitten
\item Es tritt ein Fehler auf
\item Das Programm kompiliert nicht
\item statt \texttt{int} wird automatisch \texttt{double} genommen
\end{enumerate}

85 changes: 85 additions & 0 deletions lektionen/arrays.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
\lesson{\texttt{C}-Arrays}

Als nächstes wichtiges Konzept in \Cpp werden wir uns \emph{Arrays} anschauen.
Arrays sind eine Möglichkeit, mehrere Elemente des gleichen Typs zusammen zu
fassen. Statt also einer Stelle im Speicher, an der ein \texttt{int} liegt,
habt ihr einen ganzen Speicherbereich, in dem 100 (oder eine beliebige andere
Anzahl an) \texttt{int}s liegen.

Die Elemente in einem Array sind durchnummeriert, man nennt die Nummer eines
Arrayelements seinen \emph{Index}. Das erste Element hat den Index 0, das
zweite den Index 1 und das 100te hat den Index 99 -- Vorsicht also, der höchste
Index in einem Array mit 100 Elementen ist 99, nicht 100! Um ein Array zu
definieren, schreibt ihr hinter seinen Namen eine eckige Klammer auf, die
Anzahl an Elementen, die es enthalten soll, und eine eckige Klammer zu. Auf ein
bestimmtes Arrayelement zuzugreifen könnt ihr tun, indem ihr seinen Index in
eckigen Klammern hinter den Namen schreibt. Folgendes Programm macht
hoffentlich die Syntax klar:
\inputcpp{array.cpp}

Es gibt einige Dinge, zu beachnten, wenn ihr mit Arrays arbeitet. Das
wichtigste ist oben schon genannt -- sich davon verwirren zu lassen, dass
Indizes bei 0 anfangen und aus Versehen über das Array hinaus schreiben oder
lesen ist ein so häufiger Fehler, dass er seinen eigenen Namen bekommen hat:
„Off-by-one error“. Wichtig ist, dass der Compiler diesen Zugriff nicht
verhindern wird! Das ist von daher eine sehr fiese Sache, als dass dieser
Fehler auch beim Ausführen nicht immer Probleme machen wird -- aber manchmal
lässt er auch euer Programm spontan abstürzen in einem so genannten
\emph{segmentation fault}.

Eine Limitation von Arrays, die ihr beachten solltet, ist, dass bereits zur
Compilezeit fest stehen muss, wie viele Elemente sie enthalten sollen. Ihr
könnt also z.B. nicht die Nutzerin fragen, wie viele Elemente in das Array
passen soll, denn dies würde erst zur Laufzeit feststehen (wir werden später
noch Wege um diese Limitation kennen lernen).

Ihr könnt auch Arrays von Arrays (so genannte zweidimensionale Arrays)
erstellen, indem ihr zweimal in eckigen Klammern die Größe des Arrays
hinschreibt. Die erste Größe gibt dann die Anzahl der Zeilen an, die zweite die
Anzahl der Spalten. Auch beim Zugriff auf Arrayelemente müsst ihr dann zwei
Indizes angeben. Wir werden dies später noch nutzen, hier sei erst einmal nur
die generelle Möglichkeit genannt.

\textbf{Praxis:}
Wir wollen die Seite \url{http://www.ich-kann-mich-nicht-entscheiden.de/}
nachmachen und eine Entscheidungshilfe programmieren, die aus mehreren von der
Nutzerin gegebenen Möglichkeiten eine per Zufall auswählt.

\begin{enumerate}
\item Schreibt zunächst ein Programm, welches ein Array aus 10 Strings
erstellt und die Nutzerin 10 mal nach einer Antwortmöglichkeit fragt
und die gegebenen Antworten nacheinander in das Array schreibt.
\item Fügt nun die Möglichkeit zu, weniger Antworten anzugeben. Dazu könnt
ihr zum Beispiel zuerst fragen, wie viele Antwortmöglichkeiten es geben
soll und dann so oft fragen (und natürlich einen Fehler ausgeben, wenn
es mehr als 10 Antworten geben soll).
\item Ihr könnt dann (so wie in dem Programm oben) eine Zufallszahl
erzeugen. Um sicher zu gehen, dass sie nicht zu groß wird, könnt ihr
den Rest bei Teilung durch Anzahl der eingegebenen Antworten nehmen
(sind z.B. 7 Antworten angegeben und die Zufallszahl ist 25778, so wäre
der resultierende Index \texttt{25778 \% 7 == 4}). Gebt dann die
Antwortmöglichkeit aus, die dem zufallsgeneriertem Index
entspricht.
\end{enumerate}

Sollte euer Programm einmal nicht korrekt kompilieren, denkt daran die
Fehlermeldung sorgfältig zu lesen, damit sie euch Aufschluss über die
Fehlerursache gibt. Sollte euer Programm zwar kompilieren, sich dann aber
komisch verhalten, denkt daran, den debugger zu benutzen und es Schritt für
Schritt durchzugehen, um die Fehlerquelle zu finden. Solltet ihr trotz alledem
nicht weiter kommen, oder nicht wissen, was von euch erwartet wird, fragt einen
von uns.

\textbf{Spiel:}
\begin{enumerate}
\item Schreibt ein Progamm, welches ein Array beliebiger Größe erstellt und
dann auf einen Index weit ausserhalb des erlaubten Bereichs schreibt.
Was beobachtet ihr?\footnote{Es wird natürlich Quark sein was dabei
rauskommt, es geht hier haupsächlich darum das ihr seht was für
einen Fehler das gibt}
\item Implementiert das \emph{Sieb des Eratosthenes}
\footnote{\url{https://de.wikipedia.org/wiki/Sieb_des_Eratosthenes}},
wenn ihr noch nicht ausgelastet seid.
Denkt daran, es initial zu befüllen und denkt euch eine clevere
Möglichkeit aus, das „Streichen“ zu realisieren.
\end{enumerate}
122 changes: 122 additions & 0 deletions lektionen/compiling.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
\lesson{Preprocessing, Compiler, Assembler, Linker}

In der letzten Lektion klang es bereits an -- was der Befehl \texttt{g++}
eigentlich tut, ist mehr, als nur Kompilieren im strengen Sinne des Wortes. Wir
wollen jetzt kurz erkunden, welche anderen Schritte in den Prozess vom
Quellcode in die ausführbare Datei notwendig sind und wie sie geschehen. Das
ist im Alltag nicht sehr wichtig, kann uns aber helfen, einige Fehlermeldungen
besser zu verstehen. Von daher müsst ihr auch nicht alles hier beschriebene
vollständig verstehen.

In Lektion 1 haben wir vereinfacht dargestellt, dass der Compiler eine
Quelltextdatei mit der Endung \texttt{.cpp} nimmt und daraus direkt eine
ausführbare Datei erstellt. Die Schritte, die hier eigentlich in einem Befehl
durchgeführt werden, aber zu trennen sind, sind das \emph{Preprocessing}, das \emph{Kompilieren}, das
\emph{Assemblieren} und das \emph{Linken}.

Beim Preprocessing werden alle \texttt{\#include}-Anweisungen aufgelöst und so etwas wie Makros ersetzt. Das ist der erste Schritt, der passiert, wenn wir \texttt{g++} aufrufen. Das Ergebnis des Preprocessings ist ein \Cpp-Programm, das nur noch die \Cpp-Features enthält, die wir auch wirklich benutzen. Das ist der Grund, warum wir in den bisherigen Lektionen immer \texttt{g++ -o helloworld helloworld.cpp} benutzt haben, obwohl wir in \texttt{helloworld.cpp} auch \texttt{\#include <iostream>} stehen haben -- der Compiler hat das schon für uns erledigt.

Das Kompilieren übersetzt unseren \Cpp-Code in eine Zwischenform, so genannten
\emph{Assembler}. In Lektion 1 haben wir den Maschinencode angesprochen, in der
Befehle und Parameter an Befehle in 1en und 0en dargestellt werden. Assembler
ist quasi die nächst höhere Evolutionsstufe -- statt die Befehle binär zu
kodieren, gibt es für jeden Befehl ein so genanten \emph{mnemonic}, also ein
merkbares kurzes Wort. Ein Befehl ist allerdings deutlich weniger mächtig, als
z.B. eine Anweisung in \Cpp. Früher wurden ganze Betriebssysteme in Assembler
geschrieben, da es einfach nichts Besseres gab, heutzutage ist Assembler bis
auf die exotischsten Anwendungsfelder eigentlich ausgestorben, da es viel zu
anstrengend und Fehleranfällig ist. Der Compiler tut aber noch mehr, als
einfach nur in diese Zwischensprache zu übersetzen -- er führt auch
\emph{Optimierungen} durch, d.h. er sortiert Anweisungen um, damit der Code
schneller läuft, aber das Gleiche tut. Dieser Prozess ist sehr umständlich,
aber heutige Compiler sind tatsächlich so gut im Optimieren, dass sie meistens
deutlich schnelleren Code erzeugen, als ein Mensch es je könnte.

Der nächste Schritt ist dann das Assemblieren. Das übersetzt den Assembler des
ersten Schrittes tatsächlich in Maschinensprache (genauer: In ein Dateiformat,
welches ELF heißt, welches dann die Maschinensprache plus einiger
Meta-Informationen enthält). Der Assembler erzeugt so genannte
\emph{Objectfiles}, die meistens die Endung \texttt{.o} haben (und im
ELF-Format sind). Ein Objectfile enthält dann mehrere Funktionen (in
Maschinencode) und Variablen, die es \emph{exportieren} kann, d.h. Funktionen
anderer Objectfiles die dagegen (im nächsten Schritt) gelinkt werden, können
diese Variablen und Funktionen sehen. Der Vorteil, diesen Schritt vom
vorhergehenden zu trennen ist, dass wir wenn wir wollen auch nur kompilieren
können und den resultierenden Assembler betrachten -- das kann uns helfen,
Engpässe in unserem Code, an denen der Compiler nicht hinreichend gut optimiert
zu erkennen und möglicherweise zu verbessern. z.B. in der Spielentwicklung ist
sehr schnell laufender Code wichtig.

Der letzte Schritt ist das Linken. Hier werden mehrere Objectfiles genommen und
miteinander verbunden, zu einer ausführbaren Datei. Wenn in einer der
Objectfiles eine \texttt{main}-Funktion existiert, wird diese als
Eintrittspunkt für das Programm genommen, sonst gibt es einen
\emph{Linkerfehler}. Ein Linkerfehler tritt auch auf, wenn wir versuchen, eine
Funktion zu verwenden, die es nicht gibt (z.B. indem wir sie mittels
\texttt{extern} deklarieren, ohne später das relevante Objectfile mit
anzugeben). Linkerfehler deuten also darauf hin, dass wir vergessen haben, alle
relevanten Dateien auf der Kommandozeile anzugeben, oder dass eine
\texttt{main}-Funktion fehlt, oder dass wir in mehren Dateien eine Funktion
gleichen Namens haben, oder\dots

Um das Diagramm aus der ersten Lektion zu ergänzen, dies ist der Weg, den euer
Programm durch die verschiedenen Phasen geht:

\begin{tikzpicture}
\tikzstyle{block} = [ shape=rectangle, rounded corners = 0.1cm, draw=black, inner xsep=0.5cm, inner ysep = 0.3cm ];
\tikzstyle{arr} = [ ->, thick, shorten >= 2pt, shorten <= 2pt ];

\node (nHelloWorldCpp) [block] {\texttt{helloworld.cpp}};
\node (nHelloWorldS) [block, right of = nHelloWorldCpp, node distance = 12cm] {\texttt{helloworld.S}};
\draw [arr] (nHelloWorldCpp) -- (nHelloWorldS) node [midway,above,font=\small] {\texttt{g++ -S -o helloworld.S helloworld.cpp}};
\node (nHelloWorldO) [block, below of = nHelloWorldCpp, node distance = 2cm] {\texttt{helloworld.o}};
\draw [arr] (nHelloWorldS) -- (nHelloWorldO) node [pos=0.56,above,sloped,font=\small] {\texttt{g++ -c -o helloworld.o helloworld.S}};
\node (nAnderesO) [block, below of = nHelloWorldO, node distance = 1cm] {\texttt{anderes.o}};
\node (nHelloWorld) [block, below of = nHelloWorldS, node distance = 2cm] {\texttt{helloworld}};
\draw [arr] (nHelloWorldO) -- (nHelloWorld) node [midway,below,font=\small] {\texttt{g++ -o helloworld helloworld.o anderes.o}};
\draw [arr] (nAnderesO) -| (nHelloWorld) node {};
\end{tikzpicture}

Der bisherige Befehl, den wir zum Kompilieren benutzt haben, ist tatsächlich
nur ein Spezialfall von diesem: Geben wir nämlich auf der Kommandozeile eine
input-Datei an, so rät \texttt{g++} anhand der Dateierweiterung und der
Parameter, was wir damit tun wollen. Er führt dann alle Schritte, um von
unserer input-Datei zu der gewünschten zu kommen automatisch aus, wenn wir also
\texttt{g++ -o helloworld helloworld.cpp} eingeben, dann weiß der Compiler,
dass wir eine ausführbare Datei wünschen (da wir weder \texttt{-c} noch
\texttt{-S} angegeben haben) und dass er dafür kompilieren, assemblieren und
linken muss (da wir ihm eine \texttt{.cpp} Datei gegeben haben). Genauso konnte
er in der vorigen Lektion raten, dass \texttt{g++ -o tictactoe tictactoe.cpp
tictactoe.o} heißt, dass wir eine ausführbare Datei wollen, die aus einem
kompilierten und assemblierten \texttt{tictactoe.cpp} zusammen gelinkt mit
\texttt{tictactoe.o} bestehen soll.

\begin{praxis}
\begin{enumerate}
\item \texttt{assemble.cpp} enthält ein kleines (ziemlich nutzloses)
Programm, welches zwei Zahlen addiert und das Ergebnis ausgibt.
Kompiliert es (nun nur der erste Schritt in dem Diagramm, nicht so, wie
in den vergangenen Lektionen) und schaut euch das resultierende
\texttt{.S}-file in einem Editor an. Ihr müsst nicht verstehen,
was genau hier überall passiert, aber vielleicht findet ihr ja die
\texttt{main}-Funktion, die Definition der Variablen und die Addition?

Wir können nun mal Optimierung anschalten -- gebt dazu zusätzlich den
Parameter \texttt{-O3} direkt nach dem \texttt{g++} an. Schaut euch das
\texttt{.S}-file nun wieder im Editor an. Was fällt euch
(im Vergleich zu vorher) auf?
\item Assembliert eines der im vorigen Schritt erzeugten \texttt{.S} files
in ein \texttt{.o}-File.
\item Benennt in einem eurer bisherigen Programme die
\texttt{main}-Funktion um und versucht, es zu kompilieren (wie in den
bisherigen Lektionen, also alle Schritte auf einmal). Schaut euch die
resultierenden Fehlermeldungen an. Wo wird euch der Linkerfehler
ausgegeben?
\item Macht die Umbenennung wieder rückgängig und kompiliert das Programm
erneut -- übergebt aber dieses mal den Quellcode doppelt (also z.B.
\texttt{g++ -o helloworld helloworld.cpp helloworld.cpp}). Was
beobachtet ihr? Könnt ihr die Beobachtung erklären?
\end{enumerate}

\inputcpp{assemble.cpp}
\end{praxis}
10 changes: 10 additions & 0 deletions lektionen/eigene_computer/intro.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
\setcounter{chapter}{-1}
\chapter{Vorbereitung eigener Computer}
\pagestyle{empty}
Dieses Kapitel dient der Vorbereitung privater Computer, um daran den Kurs zu bearbeiten.
Wir werden in diesem Fall den proprietären Editor „Visual Studio Code“ verwenden, welcher \href{https://code.visualstudio.com/Download}{hier} heruntergeladen werden kann.\\

\pagestyle{fancy}
\input{eigene_computer/windows.tex}
\input{eigene_computer/macos.tex}
\input{eigene_computer/linux.tex}
13 changes: 13 additions & 0 deletions lektionen/eigene_computer/linux.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
\textbf{Linux}

\pagestyle{empty}

Falls ihr privat bereits ein Linux-System nutzt.
\begin{enumerate}
\item Optional: Alternativ zu Visual Studio Code, könnt ihr auch eine Quelloffene Version des Editors verwenden.
Da die Installation dieser Version je nach Distribution variiert, verweisen wir euch an dieser Stelle an eine kurze Internetrecherche.
\item Installiert mit eurem Packagemanager \texttt{g++} und ggf. \texttt{unzip}, sowie \texttt{wget}.
\item Das Archiv mit den Vorkursdateien könnt ihr mit \\
\texttt{wget https://mathphys.info/vorkurs/pvk/vorkurs.zip} herunterladen.
\item Mit \texttt{unzip vorkurs.zip} könnt ihr dieses entpacken.
\end{enumerate}
14 changes: 14 additions & 0 deletions lektionen/eigene_computer/macos.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
\textbf{MacOS}

\pagestyle{empty}

Das Setup unter MacOS ist im Vergleich zu Windows recht einfach.

\begin{enumerate}
\item Öffnet ein Terminal.
\item Tippt \texttt{g++} ein.
\item Bestätigt in dem erscheinenden Fenster die Installation.
\item Die Dateien des Vorkurses können \href{https://mathphys.info/vorkurs/pvk/vorkurs.zip}{hier} heruntergeladen werden.
\item Entpackt die Dateien in ein Verzeichnis eurer Wahl.
\item In Visual Studio Code könnt ihr dann über den Explorer auf die Dateien des Kurses zugreifen.
\end{enumerate}
Loading

0 comments on commit 808d25a

Please sign in to comment.