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

Beispiel-Programme 21.1.2019, Musterlösung 19.11.2018

parent 85c91609
No related branches found
No related tags found
No related merge requests found
Showing with 906 additions and 0 deletions
File added
% hp-musterloesung-20181112.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018 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: Arrays mit Zahlen, hüpfender Ball
\documentclass[a4paper]{article}
\usepackage{pgscript}
\usepackage{gnuplot-lua-tikz}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 19.\ November 2018}
\exercise{Arrays mit Zahlen}
\begin{minipage}[t]{0.5\textwidth}
Wir betrachten das folgende Programm\\
(Datei: \gitfile{hp}{20181119}{aufgabe-1.c}):
\begin{lstlisting}[gobble=6]
#include <stdio.h>
void f (int *s0, int *s1)
{
while (*s0 >= 0)
{
int *s = s1;
while (*s >= 0)
if (*s0 == *s++)
printf ("%d ", *s0);
s0++;
}
printf ("\n");
}
int main (void)
{
int a[] = { 10, 4, 3, 7, 12, 0, 1, -1 };
int b[] = { 7, 14, 0, 8, 9, 22, 10, -1 };
f (a, b);
return 0;
}
\end{lstlisting}
\end{minipage}\hfill
\begin{minipage}[t]{0.5\textwidth}
\vspace*{-\bigskipamount}
\begin{enumerate}[\quad(a)]
\item
Was bewirkt die Funktion \lstinline{f},\\
und wie funktioniert sie?
\points{4}
% \item
% Von welcher Ordnung (Landau-Symbol) ist die Funktion und warum?
%
% Wir beziehen uns hierbei auf die Anzahl der Vergleiche
% in Abhängigkeit von der Länge der Eingabedaten \lstinline{s0} und \lstinline{s1}.
% Für die Rechnung dürfen Sie beide Längen mit $n$ gleichsetzen,
% obwohl sie normalerweise nicht gleich sind.
% \points{2}
\item
Was passiert, wenn Sie beim Aufruf der Funktion für einen der
Parameter den Wert \lstinline{NULL} übergeben und warum?
\points{2}
\item
Was kann passieren, wenn Sie das Hauptprogramm wie folgt abändern
(\gitfile{hp}{20181119}{aufgabe-1d.c}) und warum?
\begin{lstlisting}[gobble=8]
int main (void)
{
int a[] = { 10, 4, 3, 7, 12, 0, 1 };
int b[] = { 7, 14, 0, 8, 9, 22, 10 };
f (a, b);
return 0;
}
\end{lstlisting}
\points{2}
% \item
% Beschreiben Sie -- in Worten und/oder als C-Quelltext --, wie
% sich die Funktion \lstinline{f} effizienter gestalten läßt,
% wenn man die ihr übergebenen Arrays \lstinline{s0} und
% \lstinline{s1} als sortiert voraussetzt.
% \points{5}
%
% Hinweis: Wie würden Sie als Mensch die Aufgabe erledigen?
% \item
% Von welcher
% Ordnung (Landau-Symbol) ist Ihre effizientere Version der Funktion und warum?
% \points{2}
\end{enumerate}
\end{minipage}
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Was bewirkt die Funktion \lstinline{f} und wie funktioniert sie?}
Die Funktion gibt alle Zahlen aus, die sowohl im Array \lstinline{s0}
als auch im Array \lstinline{s1} vorkommen (Schnittmenge).
Dies geschieht, indem der Zeiger \lstinline{s0} das gesamte Array durchläuft
(äußere Schleife).
Für jedes Element des ersten Arrays durchläuft der Zeiger \lstinline{s}
das gesamte zweite Array (innere Schleife).
Auf diese Weise wird jedes Element von \lstinline{s0}
mit jedem von \lstinline{s1} verglichen und bei Gleichheit ausgegeben.
Um die Schleifen abbrechen zu können, enthalten beide Arrays
als Ende-Markierung eine negative Zahl (\lstinline{-1}).
\item
\textbf{Was passiert, wenn Sie beim Aufruf der Funktion für einen der
Parameter den Wert \lstinline{NULL} übergeben und warum?}
In dem Moment, wo auf den jeweiligen Parameter-Zeiger zugegriffen wird
(\lstinline{while (*s0 >= 0)} für \lstinline{s0} bzw.\
\lstinline{int *s = s1; while (*s >= 0)} für \lstinline{s1}),
kommt es zu einem Absturz (Speicherzugriffsfehler).
Die Dereferenzierung eines Zeigers mit dem Wert \lstinline{NULL}
ist nicht zulässig.
\item
\textbf{Was kann passieren, wenn Sie das Hauptprogramm wie folgt abändern
(\gitfile{hp}{20181119}{aufgabe-1d.c}) und warum?}
\begin{minipage}{0.35\textwidth}
\begin{lstlisting}[gobble=10]
int main (void)
{
int a[] = { 10, 4, 3, 7, 12, 0, 1 };
int b[] = { 7, 14, 0, 8, 9, 22, 10 };
f (a, b);
return 0;
}
\end{lstlisting}
\end{minipage}\hfill
\begin{minipage}{0.575\textwidth}
Durch die fehlenden Ende-Markierungen der Arrays
laufen die Schleifen immer weiter,
bis sie irgendwann zufällig auf Speicherzellen stoßen,
die sich als Ende-Markierungen interpretieren lassen (negative Zahlen).
Dadurch kann es zu einem Lesezugriff auf Speicher kommen,
für den das Programm kein Lesezugriffsrecht hat,
also zu einem Absturz (Speicherzugriffsfehler).
\end{minipage}
\end{enumerate}
\exercise{Fehlerhaftes Programm: Hüpfender Ball}
Das auf der nächsten Seite abgedruckte GTK+-Programm
(Datei: \gitfile{hp}{20181119}{aufgabe-2.c}) soll einen
hüpfenden Ball darstellen, ist jedoch fehlerhaft.
\begin{enumerate}[\quad(a)]
\item
Warum sieht man lediglich ein leeres Fenster?
Welchen Befehl muß man ergänzen, um diesen Fehler zu beheben?
\points{3}
\item
Nach der Fehlerbehebung in Aufgabenteil (a)
zeigt das Programm einen unbeweglichen Ball.
Welchen Befehl muß man ergänzen, um diesen Fehler zu beheben, und warum?
\points{2}
\item
Erklären Sie das merkwürdige Hüpfverhalten des Balls.
Wie kommt es zustande?
Was an diesem Verhalten ist korrekt, und was ist fehlerhaft? \points{5}
\item
Welche Befehle muß man in welcher Weise ändern,
um ein realistischeres Hüpf-Verhalten zu bekommen? \points{2}
\end{enumerate}
Hinweis: Das Hinzuziehen von Beispiel-Programmen aus der Vorlesung
ist ausdrücklich erlaubt -- auch in der Klausur.
Allgemeiner Hinweis:
Wenn Sie die Übungsaufgaben zu dieser Lehrveranstaltung
als PDF-Datei betrachten und darin auf die Dateinamen klicken,
können Sie die Beispiel-Programme direkt herunterladen.
Dadurch vermeiden Sie Übertragungsfehler.
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Warum sieht man lediglich ein leeres Fenster?
Welchen Befehl muß man ergänzen, um diesen Fehler zu beheben?}
Die für das Zeichnen zuständige Callback-Funktion wurde zwar geschrieben,
aber nicht installiert.
Um dies zu beheben, ergänze man den folgenden Befehl im Hauptprogramm:
\lstinline{g_signal_connect (drawing_area, "draw", G_CALLBACK (draw), NULL);}
Dies erkennt man sehr schnell durch Vergleich mit dem Beispiel-Programm
\gitfile{hp}{20181112}{gtk-13.c}.
\item
\textbf{Nach der Fehlerbehebung in Aufgabenteil (a)
zeigt das Programm einen unbeweglichen Ball.
Welchen Befehl muß man ergänzen, um diesen Fehler zu beheben, und warum?}
Die Timer-Callback-Funktion wurde zwar geschrieben, aber nicht installiert.
Um dies zu beheben, ergänze man den folgenden Befehl im Hauptprogramm:
\lstinline{g_timeout_add (50, (GSourceFunc) timer, drawing_area);}
Auch dies erkennt man sehr schnell durch Vergleich mit dem Beispiel-Programm
\gitfile{hp}{20181112}{gtk-13.c}.
\item
\textbf{Erklären Sie das merkwürdige Hüpfverhalten des Balls.
Wie kommt es zustande?
Was an diesem Verhalten ist korrekt, und was ist fehlerhaft?}
Die Geschwindigkeit in $y$-Richtung wächst immer weiter.
Der Grund dafür ist, daß die $y$-Komponente der Geschwindigkeit
nicht auf physikalisch sinnvolle Weise berechnet wird.
In der dafür zuständigen Zeile
\lstinline{vy = 0.5 * g * (t * t);}
wird stattdessen der Weg in $y$-Richtung bei einer gleichmäßig
beschleunigten Bewegung berechnet und als Geschwindigkeit verwendet.
\item
\textbf{Welche Befehle muß man in welcher Weise ändern,
um ein realistischeres Hüpf-Verhalten zu bekommen?}
Da der Ball am Boden abprallen soll, ist es \emph{nicht\/} sinnvoll,
die $y$-Komponente der Geschwindigkeit über die bekannte physikalische
Formel $v_y = -g\cdot t$ für die Geschwindigkeit in einer
gleichmäßig beschleunigten Bewegung zu berechnen.
Stattdessen ist es sinnvoll, die \emph{Geschwindigkeitsänderung\/}
innerhalb des Zeitintervalls \lstinline{dt}
zur Geschwindigkeitskomponente zu addieren:
\lstinline{vy += g * dt;}
Auch dies erkennt man sehr schnell durch Vergleich mit dem Beispiel-Programm
\gitfile{hp}{20181112}{gtk-13.c}.
\end{enumerate}
\clearpage
\vbox to \textheight{\vspace*{-0.5cm}\begin{lstlisting}
#include <gtk/gtk.h>
#define WIDTH 320
#define HEIGHT 240
double t = 0.0;
double dt = 0.2;
int r = 5;
double x = 10;
double y = 200;
double vx = 20;
double vy = -60;
double g = 9.81;
gboolean draw (GtkWidget *widget, cairo_t *c, gpointer data)
{
GdkRGBA blue = { 0.0, 0.5, 1.0, 1.0 };
gdk_cairo_set_source_rgba (c, &blue);
cairo_arc (c, x, y, r, 0, 2 * G_PI);
cairo_fill (c);
return FALSE;
}
gboolean timer (GtkWidget *widget)
{
t += dt;
x += vx * dt;
y += vy * dt;
vx = vx;
vy = 0.5 * g * (t * t);
if (y + r >= HEIGHT)
vy = -vy * 0.9;
if (x + r >= WIDTH)
vx = -vx * 0.9;
if (x - r <= 0)
vx = -vx * 0.9;
gtk_widget_queue_draw_area (widget, 0, 0, WIDTH, HEIGHT);
g_timeout_add (50, (GSourceFunc) timer, widget);
return FALSE;
}
int main (int argc, char **argv)
{
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (window);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
GtkWidget *drawing_area = gtk_drawing_area_new ();
gtk_widget_show (drawing_area);
gtk_container_add (GTK_CONTAINER (window), drawing_area);
gtk_widget_set_size_request (drawing_area, WIDTH, HEIGHT);
gtk_main ();
return 0;
}
\end{lstlisting}\vss}
\end{document}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
return fifo[0];
fifo[0] = fifo[1];
fifo[1] = fifo[2];
fifo[2] = fifo[3];
/* ... */
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
fifo[0] = fifo[1];
fifo[1] = fifo[2];
fifo[2] = fifo[3];
/* ... */
return fifo[0];
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
int result = fifo[0];
fifo[0] = fifo[1];
fifo[1] = fifo[2];
fifo[2] = fifo[3];
/* ... */
return result;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
int result = fifo[0];
for (int i = 1; i < FIFO_SIZE; i++)
fifo[i - 1] = fifo[i];
return result;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
int result = fifo[0];
for (int i = 1; i < FIFO_SIZE; i++)
fifo[i - 1] = fifo[i];
return result;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
push (42);
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_pointer = 0;
void push (int x)
{
fifo[fifo_pointer++] = x;
}
int pop (void)
{
int result = fifo[0];
for (int i = 1; i < FIFO_SIZE; i++)
fifo[i - 1] = fifo[i];
fifo_pointer--;
return result;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
push (42);
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_write_pointer = 0;
int fifo_read_pointer = 0;
void push (int x)
{
fifo[fifo_write_pointer++] = x;
if (fifo_write_pointer >= FIFO_SIZE)
fifo_write_pointer = 0;
}
int pop (void)
{
int result = fifo[fifo_read_pointer++];
if (fifo_read_pointer >= FIFO_SIZE)
fifo_read_pointer = 0;
return result;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
push (42);
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_write_pointer = 0;
int fifo_read_pointer = 0;
void push (int x)
{
fifo[fifo_write_pointer++] = x;
if (fifo_write_pointer >= FIFO_SIZE)
fifo_write_pointer = 0;
if (fifo_write_pointer == fifo_read_pointer)
{
fprintf (stderr, "fifo overflow\n");
exit (1);
}
}
int pop (void)
{
if (fifo_read_pointer == fifo_write_pointer)
{
fprintf (stderr, "fifo underflow\n");
exit (1);
}
else
{
int result = fifo[fifo_read_pointer++];
if (fifo_read_pointer >= FIFO_SIZE)
fifo_read_pointer = 0;
return result;
}
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
push (42);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define FIFO_SIZE 10
int fifo[FIFO_SIZE];
int fifo_write_pointer = 0;
int fifo_read_pointer = 0;
void push (int x)
{
int old_fifo_write_pointer = fifo_write_pointer;
fifo_write_pointer++;
if (fifo_write_pointer >= FIFO_SIZE)
fifo_write_pointer = 0;
if (fifo_write_pointer == fifo_read_pointer)
{
fprintf (stderr, "fifo overflow\n");
exit (1);
}
else
fifo[old_fifo_write_pointer] = x;
}
int pop (void)
{
if (fifo_read_pointer == fifo_write_pointer)
{
fprintf (stderr, "fifo underflow\n");
exit (1);
}
else
{
int result = fifo[fifo_read_pointer++];
if (fifo_read_pointer >= FIFO_SIZE)
fifo_read_pointer = 0;
return result;
}
}
int main (void)
{
push (3);
push (7);
push (137);
for (int i = 0; i < 42; i++)
push (i);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
push (42);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
#include <stdio.h>
typedef struct
{
int content;
node *next;
} node;
int main (void)
{
return 0;
}
#include <stdio.h>
typedef struct node
{
int content;
struct node *next;
} node;
int main (void)
{
return 0;
}
#include <stdio.h>
typedef struct node
{
int content;
struct node *next;
} node;
int main (void)
{
node node3 = { 3, NULL };
node node7 = { 7, NULL };
node node137 = { 137, NULL };
node *first = &node3;
for (node *p = first; p; p = p->next)
printf ("%d\n", p->content);
return 0;
}
#include <stdio.h>
typedef struct node
{
int content;
struct node *next;
} node;
int main (void)
{
node node3 = { 3, NULL };
node node7 = { 7, NULL };
node node137 = { 137, NULL };
node3.next = &node7;
node7.next = &node137;
node137.next = NULL;
node *first = &node3;
for (node *p = first; p; p = p->next)
printf ("%d\n", p->content);
return 0;
}
#include <stdio.h>
typedef struct node
{
int content;
struct node *next;
} node;
int main (void)
{
node node3 = { 3, NULL };
node node7 = { 7, NULL };
node node137 = { 137, NULL };
node3.next = &node7;
node7.next = &node137;
node137.next = NULL;
node node5 = { 5, NULL };
node5.next = node3.next;
node3.next = &node5;
node *first = &node3;
for (node *p = first; p; p = p->next)
printf ("%d\n", p->content);
return 0;
}
#include <stdio.h>
void push (int x)
{
}
int pop (void)
{
return 42;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
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;
stack_pointer++;
}
int pop (void)
{
return 42;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
.file "stack-1.c"
.text
.globl push
.type push, @function
push:
.LFB11:
.cfi_startproc
movl stack_pointer(%rip), %eax
movslq %eax, %rcx
leaq stack(%rip), %rdx
movl %edi, (%rdx,%rcx,4)
addl $1, %eax
movl %eax, stack_pointer(%rip)
ret
.cfi_endproc
.LFE11:
.size push, .-push
.globl pop
.type pop, @function
pop:
.LFB12:
.cfi_startproc
movl $42, %eax
ret
.cfi_endproc
.LFE12:
.size pop, .-pop
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB13:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movl $3, %edi
call push
movl $7, %edi
call push
movl $137, %edi
call push
movl $42, %esi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $42, %esi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $42, %esi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
addq $8, %rsp
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE13:
.size main, .-main
.globl stack_pointer
.bss
.align 4
.type stack_pointer, @object
.size stack_pointer, 4
stack_pointer:
.zero 4
.comm stack,40,32
.ident "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"
.section .note.GNU-stack,"",@progbits
#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 42;
}
int main (void)
{
push (3);
push (7);
push (137);
printf ("%d\n", pop ());
printf ("%d\n", pop ());
printf ("%d\n", pop ());
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment