diff --git a/20200123/hp-musterloesung-20200123.pdf b/20200123/hp-musterloesung-20200123.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e9a62c761f76ad691e9f502887840b9d8be789b6 Binary files /dev/null and b/20200123/hp-musterloesung-20200123.pdf differ diff --git a/20200123/hp-musterloesung-20200123.tex b/20200123/hp-musterloesung-20200123.tex new file mode 100644 index 0000000000000000000000000000000000000000..9cc611bff8d3d854ac2a2cb8c3791e322d91e4a0 --- /dev/null +++ b/20200123/hp-musterloesung-20200123.tex @@ -0,0 +1,393 @@ +% hp-musterloesung-20191128.pdf - Solutions to the Exercises on Low-Level Programming +% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 Peter Gerwinski +% +% This document is free software: you can redistribute it and/or +% modify it either under the terms of the Creative Commons +% Attribution-ShareAlike 3.0 License, or under the terms of the +% GNU General Public License as published by the Free Software +% Foundation, either version 3 of the License, or (at your option) +% any later version. +% +% This document is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this document. If not, see <http://www.gnu.org/licenses/>. +% +% You should have received a copy of the Creative Commons +% Attribution-ShareAlike 3.0 Unported License along with this +% document. If not, see <http://creativecommons.org/licenses/>. + +% README: Datum-Bibliothek, Text-Grafik-Bibliothek, LED-Blinkmuster + +\documentclass[a4paper]{article} + +\usepackage{pgscript} +\usepackage{sfmath} + +\begin{document} + + \section*{Hardwarenahe Programmierung\\ + Musterlösung zu den Übungsaufgaben -- 23.\ Januar 2020} + + \exercise{Stack-Operationen} + + Das folgende Programm (\gitfile{hp}{20200123}{aufgabe-1.c}) + implementiert einen Stapelspeicher (Stack). + Dies ist ein Array, das nur bis zu einer variablen Obergrenze (Stack-Pointer) + tatsächlich genutzt wird. + An dieser Obergrenze kann man Elemente hinzufügen (push). + + In dieser Aufgabe sollen zusätzlich Elemente + in der Mitte eingefügt werden (insert). + Die dafür bereits existierenden Funktionen \lstinline{insert()} + und \lstinline{insert_sorted()} sind jedoch fehlerhaft. + + \begin{minipage}[t]{0.5\textwidth} + \begin{lstlisting}[gobble=6] + #include <stdio.h> + + #define STACK_SIZE 10 + + int stack[STACK_SIZE]; + int stack_pointer = 0; + + void push (int x) + { + stack[stack_pointer++] = x; + } + + void show (void) + { + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); + } + \end{lstlisting} + \end{minipage}\hfill + \begin{minipage}[t]{0.5\textwidth} + \begin{lstlisting}[gobble=6] + void insert (int x, int pos) + { + for (int i = pos; i < stack_pointer; i++) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; + } + + void insert_sorted (int x) + { + int i = 0; + while (i < stack_pointer && x < stack[i]) + i++; + insert (x, i); + } + + int main (void) + { + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + return 0; + } + \end{lstlisting} + \end{minipage} + + \begin{enumerate}[\quad(a)] + \item + Korrigieren Sie das Programm so, + daß die Funktion \lstinline{insert()} ihren Parameter \lstinline{x} + an der Stelle \lstinline{pos} in den Stack einfügt + und den sonstigen Inhalt des Stacks verschiebt, aber nicht zerstört. + \points{3} + \item + Korrigieren Sie das Programm so, + daß die Funktion \lstinline{insert_sorted()} ihren Parameter \lstinline{x} + an derjenigen Stelle einfügt, an die er von der Sortierung her gehört. + (Der Stack wird hierbei vor dem Funktionsaufruf als sortiert vorausgesetzt.) + \points{2} + \item + Schreiben Sie eine zusätzliche Funktion \lstinline{int search (int x)}, + die die Position (Index) des Elements \lstinline{x} + innerhalb des Stack zurückgibt -- oder die Zahl + \lstinline{-1}, wenn \lstinline{x} nicht im Stack enthalten ist. + Der Rechenaufwand darf höchstens $\mathcal{O}(n)$ betragen. + \points{3} + \item + Wie (c), aber der Rechenaufwand darf höchstens $\mathcal{O}(\log n)$ betragen. + \points{4} + \end{enumerate} + + \clearpage + + \solution + + \begin{enumerate}[\quad(a)] + \item + \textbf{Korrigieren Sie das Programm so, + daß die Funktion \lstinline{insert()} ihren Parameter \lstinline{x} + an der Stelle \lstinline{pos} in den Stack einfügt, + und den sonstigen Inhalt des Stacks verschiebt, aber nicht zerstört.} + + Die \lstinline{for}-Schleife in der Funktion \lstinline{insert()} + durchläuft das Array von unten nach oben. + Um den Inhalt des Arrays von unten nach oben zu verschieben, + muß man die Schleife jedoch von oben nach unten durchlaufen. + + \goodbreak + Um die Funktion zu reparieren, ersetze man also + \begin{lstlisting}[gobble=8] + for (int i = pos; i < stack_pointer; i++) + \end{lstlisting} + durch: + \begin{lstlisting}[gobble=8] + for (int i = stack_pointer - 1; i >= pos; i--) + \end{lstlisting} + (Siehe auch: \gitfile{hp}{20200123}{loesung-1.c}) + + \item + \textbf{Korrigieren Sie das Programm so, + daß die Funktion \lstinline{insert_sorted()} ihren Parameter \lstinline{x} + an derjenigen Stelle einfügt, an die er von der Sortierung her gehört. + (Der Stack wird hierbei vor dem Funktionsaufruf als sortiert vorausgesetzt.)} + + Der Vergleich \lstinline{x < stack[i]} + als Bestandteil der \lstinline{while}-Bedingung + paßt nicht zur Durchlaufrichtung der Schleife (von unten nach oben). + + Um die Funktion zu reparieren, kann man daher entweder + das Kleinerzeichen durch ein Größerzeichen ersetzen + (\lstinline{x > stack[i]} -- siehe \gitfile{hp}{20200123}{loesung-1b-1.c}) + oder die Schleife von oben nach unten durchlaufen + (siehe \gitfile{hp}{20200123}{loesung-1b-2.c}). + + Eine weitere Möglichkeit besteht darin, + das Suchen nach der Einfügeposition + mit dem Verschieben des Arrays zu kombinieren + (siehe \gitfile{hp}{20200123}{loesung-1.c}). + Hierdurch spart man sich eine Schleife; das Programm wird schneller. + (Es bleibt allerdings bei $\mathcal{O}(n)$.) + + \item + \textbf{Schreiben Sie eine zusätzliche Funktion \lstinline{int search (int x)}, + die die Position (Index) des Elements \lstinline{x} + innerhalb des Stack zurückgibt + -- oder \lstinline{-1}, wenn \lstinline{x} nicht im Stack enthalten ist. + Der Rechenaufwand darf höchstens $\mathcal{O}(n)$ betragen.} + + Man geht in einer Schleife den Stack (= den genutzten Teil des Arrays) durch. + Bei Gleichheit gibt man direkt mit \lstinline{return} den Index zurück. + Nach dem Schleifendurchlauf steht fest, + daß \lstinline{x} nicht im Stack vorhanden ist; + man kann dann direkt \lstinline{-1} zurückgeben + (siehe \gitfile{hp}{20200123}{loesung-1c.c}). + + Da es sich um eine einzelne Schleife handelt, + ist die Ordnung $\mathcal{O}(n)$. + + \item + \textbf{Wie (c), aber der Rechenaufwand darf höchstens $\mathcal{O}(\log n)$ betragen.} + + Um $\mathcal{O}(\log n)$ zu erreichen, + halbiert man fortwährend das Intervall von (einschließlich) \lstinline{0} + bis (ausschließlich) \lstinline{stack_pointer} + (siehe \gitfile{hp}{20200123}{loesung-1d.c}) -- + wie in der Funktion \lstinline{push_sorted()} + im Beispiel-Programm \gitfile{hp}{20200123}{stack-11.c}. + + Ein wichtiger Unterschied besteht darin, + daß man nach dem Durchlauf der Schleife noch auf die Gleichheit + \lstinline{x == stack[left]} (insbesondere nicht: \lstinline{stack[right]}) + prüfen und ggf.\ \lstinline{left} bzw.\ \lstinline{-1} zurückgeben muß. + \end{enumerate} + + \clearpage + + \exercise{Iterationsfunktionen} + + Wir betrachten das folgende Programm (\gitfile{hp}{20200123}{aufgabe-2.c}): + + \begin{minipage}[t]{0.4\textwidth} + \begin{lstlisting}[gobble=6] + #include <stdio.h> + + void foreach (int *a, void (*fun) (int x)) + { + for (int *p = a; *p >= 0; p++) + fun (*p); + } + + void even_or_odd (int x) + { + if (x % 2) + printf ("%d ist ungerade.\n", x); + else + printf ("%d ist gerade.\n", x); + } + \end{lstlisting} + \end{minipage}\hfill + \begin{minipage}[t]{0.52\textwidth} + \begin{lstlisting}[gobble=6] + int main (void) + { + int numbers[] = { 12, 17, 32, 1, 3, 16, 19, 18, -1 }; + foreach (numbers, even_or_odd); + return 0; + } + \end{lstlisting} + \begin{enumerate}[\quad(a)] + \item + Was bedeutet \lstinline{void (*fun) (int x)}, + und welchen Sinn hat seine Verwendung in der Funktion \lstinline{foreach()}? + \points{2} + \item + Schreiben Sie das Hauptprogramm \lstinline{main()} so um, + daß es unter Verwendung der Funktion \lstinline{foreach()} + die Summe aller positiven Zahlen in dem Array berechnet. + Sie dürfen dabei weitere Funktionen sowie globale Variable einführen. + \points{4} + \end{enumerate} + \end{minipage} + + \solution + + \begin{enumerate}[\quad(a)] + \item + \textbf{Was bedeutet \lstinline{void (*fun) (int x)}, + und welchen Sinn hat seine Verwendung in der Funktion \lstinline{foreach()}?} + + \lstinline{void (*fun) (int x)} deklariert einen Zeiger \lstinline{fun}, + der auf Funktionen zeigen kann, die einen Parameter \lstinline{x} + vom Typ \lstinline{int} erwarten und keinen Wert zurückgeben (\lstinline{void}). + + Durch die Übergabe eines derartigen Parameters an die Funktion \lstinline{foreach()} + lassen wir dem Aufrufer die Wahl, + welche Aktion für alle Elemente des Arrays aufgerufen werden soll. + + \item + \textbf{Schreiben Sie das Hauptprogramm \lstinline{main()} so um, + daß es unter Verwendung der Funktion \lstinline{foreach()} + die Summe aller positiven Zahlen in dem Array berechnet. + Sie dürfen dabei weitere Funktionen sowie globale Variable einführen.} + + Siehe: \gitfile{hp}{20200123}{loesung-1.c} + + Damit die Funktion \lstinline{add_up()} Zugriff auf die Variable \lstinline{sum} hat, + muß diese global sein + und vor der Funktion \lstinline{add_up()} deklariert werden. + + Die Bedingung, daß nur positive Zahlen summiert werden sollen, + ist durch die Arbeitsweise der Funktion \lstinline{foreach()} + bereits gewährleistet, da negative Zahlen als Ende-Markierungen dienen. + + Wichtig ist, daß die Variable \lstinline{sum} + vor dem Aufruf der Funktion \lstinline{foreach()} + auf den Wert \lstinline{0} gesetzt wird. + In \gitfile{hp}{20200123}{loesung-1.c} geschieht dies + durch die Initialisierung von \lstinline{sum}. + Wenn mehrere Summen berechnet werden sollen, + muß dies durch explizite Zuweisungen \lstinline{sum = 0} + vor den Aufrufen von \lstinline{foreach()} erfolgen. + \end{enumerate} + + \exercise{Dynamisches Bit-Array} + + Schreiben Sie die folgenden Funktionen zur Verwaltung eines dynamischen Bit-Arrays: + \begin{itemize} + \item + \lstinline{void bit_array_init (int n)}\\ + Das Array initialisieren, so daß man \lstinline{n} Bits darin speichern kann.\\ + Die Array-Größe \lstinline{n} ist keine Konstante, sondern erst im laufenden Programm bekannt.\\ + Die Bits sollen auf den Anfangswert 0 initialisiert werden. + \item + \lstinline{void bit_array_set (int i, int value)}\\ + Das Bit mit dem Index \lstinline{i} auf den Wert \lstinline{value} setzen.\\ + Der Index \lstinline{i} darf von \lstinline{0} bis \lstinline{n - 1} gehen; + der Wert \lstinline{value} darf 1 oder 0 sein. + \item + \lstinline{void bit_array_flip (int i)}\\ + Das Bit mit dem Index \lstinline{i} auf den entgegengesetzten Wert setzen,\\ + also auf 1, wenn er vorher 0 ist, bzw.\ auf 0, wenn er vorher 1 ist.\\ + Der Index \lstinline{i} darf von \lstinline{0} bis \lstinline{n - 1} gehen. + \item + \lstinline{int bit_array_get (int i)}\\ + Den Wert des Bit mit dem Index \lstinline{i} zurückliefern.\\ + Der Index \lstinline{i} darf von \lstinline{0} bis \lstinline{n - 1} gehen. + \item + \lstinline{void bit_array_resize (int new_n)}\\ + Die Größe des Arrays auf \lstinline{new_n} Bits ändern.\\ + Dabei soll der Inhalt des Arrays, soweit er in die neue Größe paßt, erhalten bleiben.\\ + Neu hinzukommende Bits sollen auf 0 initialisiert werden. + \item + \lstinline{void bit_array_done (void)}\\ + Den vom Array belegten Speicherplatz wieder freigeben. + \end{itemize} + Bei Bedarf dürfen Sie den Funktionen zusätzliche Parameter mitgeben, + beispielsweise um mehrere Arrays parallel verwalten zu können. + (In der objektorientierten Programmierung wäre dies der implizite Parameter \lstinline{this}, + der auf die Objekt-Struktur zeigt.) + + Die Bits sollen möglichst effizient gespeichert werden, + z.\,B.\ jeweils 8 Bits in einer \lstinline{uint8_t}-Variablen. + + Die Funktionen sollen möglichst robust sein, + d.\,h.\ das Programm darf auch bei unsinnigen Parameterwerten nicht abstürzen, + sondern soll eine Fehlermeldung ausgeben. + + \medskip + + Die \textbf{Hinweise} auf der nächsten Seite beschreiben + einen möglichen Weg, die Aufgabe zu lösen.\\ + Es seht Ihnen frei, die Aufgabe auch auf andere Weise zu lösen. + + \goodbreak + + Hinweise: + \begin{itemize} + \item + Setzen Sie zunächst voraus, daß das Array die konstante Länge 8 hat, + und schreiben Sie zunächst nur die Funktionen + \lstinline{bit_array_set()}, \lstinline{bit_array_flip()} und + \lstinline{bit_array_get()}. + \item + Verallgemeinern Sie nun auf eine konstante Länge, + bei der es sich um ein Vielfaches von 8 handelt. + \item + Implementieren Sie nun die Überprüfung auf unsinnige Parameterwerte. + Damit können Sie sich gleichzeitig von der Bedingung lösen, + daß die Länge des Arrays ein Vielfaches von 8 sein muß. + \item + Gehen Sie nun von einem statichen zu einem dynamischen Array über, + und implementieren sie die Funktionen \lstinline{bit_array_init()}, + \lstinline{bit_array_done()} und \lstinline{bit_array_reseize()}. + \end{itemize} + + \points{14} + + \medskip + + (Hinweis für die Klausur: + Abgabe in digitaler Form ist erwünscht, aber nicht zwingend.) + + \solution + + (wird nachgereicht) + + \begin{flushright} + \textit{Viel Erfolg -- auch in der Klausur!} + \end{flushright} + +\end{document} diff --git a/20200123/loesung-1.c b/20200123/loesung-1.c new file mode 100644 index 0000000000000000000000000000000000000000..9b87d6c2f977af2843bcc2b75896fb5f00d8fc35 --- /dev/null +++ b/20200123/loesung-1.c @@ -0,0 +1,62 @@ +#include <stdio.h> + +#define STACK_SIZE 10 + +int stack[STACK_SIZE]; +int stack_pointer = 0; + +void push (int x) +{ + stack[stack_pointer++] = x; +} + +int pop (void) +{ + return stack[--stack_pointer]; +} + +void show (void) +{ + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); +} + +void insert (int x, int pos) +{ + for (int i = stack_pointer - 1; i >= pos; i--) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; +} + +void insert_sorted (int x) +{ + int i = stack_pointer - 1; + while (i >= 0 && x < stack[i]) + { + stack[i + 1] = stack[i]; + i--; + } + stack[i + 1] = x; + stack_pointer++; +} + +int main (void) +{ + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + return 0; +} diff --git a/20200123/loesung-1b-1.c b/20200123/loesung-1b-1.c new file mode 100644 index 0000000000000000000000000000000000000000..cbbe37055109d203105a7ad4acb63d5bef61f4c0 --- /dev/null +++ b/20200123/loesung-1b-1.c @@ -0,0 +1,58 @@ +#include <stdio.h> + +#define STACK_SIZE 10 + +int stack[STACK_SIZE]; +int stack_pointer = 0; + +void push (int x) +{ + stack[stack_pointer++] = x; +} + +int pop (void) +{ + return stack[--stack_pointer]; +} + +void show (void) +{ + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); +} + +void insert (int x, int pos) +{ + for (int i = stack_pointer - 1; i >= pos; i--) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; +} + +void insert_sorted (int x) +{ + int i = 0; + while (i < stack_pointer && x > stack[i]) + i++; + insert (x, i); +} + +int main (void) +{ + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + return 0; +} diff --git a/20200123/loesung-1b-2.c b/20200123/loesung-1b-2.c new file mode 100644 index 0000000000000000000000000000000000000000..b1e1ae846f15a29e9cd53a8d1d689e3ccbefacce --- /dev/null +++ b/20200123/loesung-1b-2.c @@ -0,0 +1,58 @@ +#include <stdio.h> + +#define STACK_SIZE 10 + +int stack[STACK_SIZE]; +int stack_pointer = 0; + +void push (int x) +{ + stack[stack_pointer++] = x; +} + +int pop (void) +{ + return stack[--stack_pointer]; +} + +void show (void) +{ + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); +} + +void insert (int x, int pos) +{ + for (int i = stack_pointer - 1; i >= pos; i--) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; +} + +void insert_sorted (int x) +{ + int i = stack_pointer - 1; + while (i >= 0 && x < stack[i]) + i--; + insert (x, i + 1); +} + +int main (void) +{ + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + return 0; +} diff --git a/20200123/loesung-1c.c b/20200123/loesung-1c.c new file mode 100644 index 0000000000000000000000000000000000000000..79d061e3d7b78a2bec05b632e74b083a16d5a326 --- /dev/null +++ b/20200123/loesung-1c.c @@ -0,0 +1,72 @@ +#include <stdio.h> + +#define STACK_SIZE 10 + +int stack[STACK_SIZE]; +int stack_pointer = 0; + +void push (int x) +{ + stack[stack_pointer++] = x; +} + +int pop (void) +{ + return stack[--stack_pointer]; +} + +void show (void) +{ + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); +} + +void insert (int x, int pos) +{ + for (int i = stack_pointer - 1; i >= pos; i--) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; +} + +void insert_sorted (int x) +{ + int i = stack_pointer - 1; + while (i >= 0 && x < stack[i]) + { + stack[i + 1] = stack[i]; + i--; + } + stack[i + 1] = x; + stack_pointer++; +} + +int search (int x) +{ + for (int i = 0; i < stack_pointer; i++) + if (stack[i] == x) + return i; + return -1; +} + +int main (void) +{ + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + printf ("%d\n", search (42)); + printf ("%d\n", search (1117)); + return 0; +} diff --git a/20200123/loesung-1d.c b/20200123/loesung-1d.c new file mode 100644 index 0000000000000000000000000000000000000000..9079d8d3be7a500498aede2cf8e89a7d7db48a35 --- /dev/null +++ b/20200123/loesung-1d.c @@ -0,0 +1,86 @@ +#include <stdio.h> + +#define STACK_SIZE 10 + +int stack[STACK_SIZE]; +int stack_pointer = 0; + +void push (int x) +{ + stack[stack_pointer++] = x; +} + +int pop (void) +{ + return stack[--stack_pointer]; +} + +void show (void) +{ + printf ("stack content:"); + for (int i = 0; i < stack_pointer; i++) + printf (" %d", stack[i]); + if (stack_pointer) + printf ("\n"); + else + printf (" (empty)\n"); +} + +void insert (int x, int pos) +{ + for (int i = stack_pointer - 1; i >= pos; i--) + stack[i + 1] = stack[i]; + stack[pos] = x; + stack_pointer++; +} + +void insert_sorted (int x) +{ + int i = stack_pointer - 1; + while (i >= 0 && x < stack[i]) + { + stack[i + 1] = stack[i]; + i--; + } + stack[i + 1] = x; + stack_pointer++; +} + +int search (int x) +{ + int left = 0; + int right = stack_pointer; + while (left < right - 1) + { + int middle = (left + right) / 2; + if (x < stack[middle]) + right = middle; + else + left = middle; + } + if (x == stack[left]) + return left; + else + return -1; +} + +int main (void) +{ + push (3); + push (7); + push (137); + show (); + insert (5, 1); + show (); + insert_sorted (42); + show (); + insert_sorted (2); + show (); + printf ("%d\n", search (2)); + printf ("%d\n", search (4)); + printf ("%d\n", search (42)); + printf ("%d\n", search (67)); + printf ("%d\n", search (137)); + printf ("%d\n", search (1117)); + return 0; +}