Skip to content
Snippets Groups Projects
Commit c0fd3518 authored by Peter Gerwinski's avatar Peter Gerwinski
Browse files

Musterlösung 23.1.2020

parent b5394ee0
No related branches found
No related tags found
No related merge requests found
File added
% 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}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment