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

Musterlösung zu Aufgabe 2 der Klausur vom 6.2.2017

parent 73405528
Branches
No related tags found
No related merge requests found
File added
% hp-musterloesung-20191205.pdf - Solutions to the Exercises on Low-Level Programming
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020 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: Löschen aus Strings, Hexdumps
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zur Klausur vom 6.\ Februar 2017}
\addtocounter{exercise}{1}
\iffalse
\exercise{Strings umsortieren}
Wir betrachten das folgende Programm (\file{aufgabe-1.c}):
\begin{lstlisting}[style=numbered]
#include <stdio.h>
#include <string.h>
void fun (char *s)
{
int len = strlen (s);
for (int i = 0; i < len; i++)
{
for (int j = i - 1; j >= 0; j--)
{
if (s[i] < s[j])
{
char sx = s[i];
s[i] = s[j];
s[j] = sx;
}
}
}
}
int main (void)
{
char s[] = "BAECD";
fun (s);
printf ("%s\n", s);
return 0;
}
\end{lstlisting}
Auf einem Rechner, der den ASCII-Zeichensatz verwendet, lautet die Ausgabe:
\lstinline[style=terminal]{ABCDE}
\begin{enumerate}[\quad(a)]
% \item
% Was bewirkt die Funktion \lstinline{fun}?
% \points{4}
% \workspace{12}
\item
Beweisen Sie (z.\,B.\ anhand eines Gegenbeispiels),
daß die Funktion allgemein \lstinline{fun} \emph{nicht\/} dazu geeignet ist,
die Buchstaben eines Strings gemäß ihrer ASCII-Reihenfolge zu sortieren.
\points{2}
% \workspace{9}
\item
Was kann passieren, wenn die Zeile \lstinline{char s[] = "BAECD";}
durch \lstinline|char s[] = { 'B', 'A', 'E', 'C', 'D'|~\lstinline|};| ersetzt wird, und warum?
\points{2}
% \workspace{10}
\item
Von welcher Ordnung (Landau-Symbol) ist die Funktion \lstinline{fun} und warum?
Wir beziehen uns hierbei auf die Anzahl der Vergleiche \lstinline{s[i] < s[j]}
in Abhängigkeit von der Länge des Eingabe-Strings \lstinline{"BAECD"}.
\points{1}
% \workspace{10}
\item
Beschreiben Sie -- in Worten und/oder als C-Quelltext --, wie
sich die Funktion \lstinline{fun} so abwandeln läßt,
daß sie die Buchstaben des Strings \lstinline{s} gemäß ihrer ASCII-Reihenfolge sortiert.
Von welcher Ordnung (Landau-Symbol) ist Ihre Version der Funktion und warum?
\points{3}
% \workspace{22}
\end{enumerate}
\fi
\exercise{Speicherformate von Zahlen}
Wir betrachten das folgende Programm (\file{aufgabe-2.c}):
\begin{lstlisting}[style=numbered]
#include <stdio.h>
#include <stdint.h>
int main (void)
{
uint16_t numbers[] = { 25928, 27756, 11375, 30496, 29295, 25708, 2593, 0 };
printf ("%s", numbers);
return 0;
}
\end{lstlisting}
Das Programm wird compiliert und ausgeführt:
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function ‘main’:
aufgabe-2.c:7:3: warning: format ‘%s’ expects argument of type ‘char *’,
but argument 2 has type ‘uint16_t *[-Wformat]
$ ¡./aufgabe-2¿
Hello, world!
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die beim Compilieren auftretende Warnung.
\points{2}
% \workspace{8}
\item
Erklären Sie die Ausgabe des Programms.\\
Welche Endianness hat der verwendete Rechner?
\points{4}
% \workspace{16}
% \item
% Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?
% \points{2}
% \workspace{11}
\item
Erklären Sie die Ausgabe des Programms,
wenn Sie den Datentyp \lstinline{uint16_t} durch \lstinline{uint32_t} ersetzen.
\points{3}
% \workspace{12}
\item
Erklären Sie die Ausgabe des Programms
und die beim Compilieren auftretenden Warnungen,
wenn Sie den Datentyp \lstinline{uint16_t} durch \lstinline{uint8_t} ersetzen.
Warum tritt die Warnung aus den vorherigen Aufgabenteilen nicht mehr auf?
\points{3}
% \workspace{14}
\end{enumerate}
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Erklären Sie die beim Compilieren auftretende Warnung.}
Die Funktion \lstinline{printf()} mit der Formatspezifikation \lstinline{%s}
erwartet als Parameter einen String, d.\,h.\ einen Zeiger auf \lstinline{char}.
Der übergebene Parameter \lstinline{numbers} ist hingegen
der Name eines Arrays und somit kompatibel zu Zeigern auf Elemente des Arrays,
also auf \lstinline{uint16_t}, insbesondere nicht auf \lstinline{char}.
\item
\textbf{Erklären Sie die Ausgabe des Programms.\\
Welche Endianness hat der verwendete Rechner?}
Die Funktion \lstinline{printf()}
hat einen Zeiger auf das Array \lstinline{numbers} übergeben bekommen,
gibt aber den String \lstinline{"Hello, world!\n"} aus.
Demnach muß sich in den Speicherzellen des Arrays dieser String befinden:
\begin{center}
\begin{picture}(14,1.5)(0,-0.5)
\put(0,0){\line(1,0){14}}
\put(0,1){\line(1,0){14}}
\multiput(0,0)(1,0){15}{\line(0,1){1}}
\put(0.35,0.38){\lstinline{'H'}}
\put(1.35,0.38){\lstinline{'e'}}
\put(2.35,0.38){\lstinline{'l'}}
\put(3.35,0.38){\lstinline{'l'}}
\put(4.35,0.38){\lstinline{'o'}}
\put(5.35,0.38){\lstinline{','}}
\put(6.35,0.38){\lstinline{' '}}
\put(7.35,0.38){\lstinline{'w'}}
\put(8.35,0.38){\lstinline{'o'}}
\put(9.35,0.38){\lstinline{'r'}}
\put(10.35,0.38){\lstinline{'l'}}
\put(11.35,0.38){\lstinline{'d'}}
\put(12.35,0.38){\lstinline{'!'}}
\put(13.30,0.38){\lstinline{'\\n'}}
\put(1.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[0]}}}$}}
\put(3.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[1]}}}$}}
\put(5.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[2]}}}$}}
\put(7.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[3]}}}$}}
\put(9.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[4]}}}$}}
\put(11.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[5]}}}$}}
\put(13.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[6]}}}$}}
\end{picture}
\end{center}
\goodbreak
Wenn wir die Zeichen gemäß ASCII in Hexadezimalzahlen umrechnen, erhalten wir:
\begin{center}
\begin{picture}(14,1.5)(0,-0.5)
\put(0,0){\line(1,0){14}}
\put(0,1){\line(1,0){14}}
\multiput(0,0)(1,0){15}{\line(0,1){1}}
\put(0.5,0.5){\makebox(0,0){\lstinline{0x48}}}
\put(1.5,0.5){\makebox(0,0){\lstinline{0x65}}}
\put(2.5,0.5){\makebox(0,0){\lstinline{0x6c}}}
\put(3.5,0.5){\makebox(0,0){\lstinline{0x6c}}}
\put(4.5,0.5){\makebox(0,0){\lstinline{0x6f}}}
\put(5.5,0.5){\makebox(0,0){\lstinline{0x2c}}}
\put(6.5,0.5){\makebox(0,0){\lstinline{0x20}}}
\put(7.5,0.5){\makebox(0,0){\lstinline{0x77}}}
\put(8.5,0.5){\makebox(0,0){\lstinline{0x6f}}}
\put(9.5,0.5){\makebox(0,0){\lstinline{0x72}}}
\put(10.5,0.5){\makebox(0,0){\lstinline{0x6c}}}
\put(11.5,0.5){\makebox(0,0){\lstinline{0x64}}}
\put(12.5,0.5){\makebox(0,0){\lstinline{0x21}}}
\put(13.5,0.5){\makebox(0,0){\lstinline{0x0a}}}
\put(1.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[0]}}}$}}
\put(3.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[1]}}}$}}
\put(5.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[2]}}}$}}
\put(7.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[3]}}}$}}
\put(9.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[4]}}}$}}
\put(11.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[5]}}}$}}
\put(13.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{\lstinline{numbers[6]}}}$}}
\end{picture}
\end{center}
Auf einem Big-Endian-Rechner gälte dann:
\begin{lstlisting}
numbers[0] = 0x4865 = 18533
numbers[1] = 0x6c6c = 27756
numbers[2] = 0x6f2c = 28460
numbers[3] = 0x2077 = 8311
numbers[4] = 0x6f72 = 28530
numbers[5] = 0x6c64 = 27748
numbers[6] = 0x210a = 8458
\end{lstlisting}
Dies stimmt nicht mit dem Quelltext des Programms überein.
Auf einem Little-Endian-Rechner gälte dann:
\begin{lstlisting}
numbers[0] = 0x6548 = 25928
numbers[1] = 0x6c6c = 27756
numbers[2] = 0x2c6f = 11375
numbers[3] = 0x7720 = 30496
numbers[4] = 0x726f = 29295
numbers[5] = 0x646c = 25708
numbers[6] = 0x0a21 = 2593
\end{lstlisting}
Dies stimmt mit dem Quelltext des Programms überein.
Zusammenfassung: \lstinline{printf()} interpretiert die Speicherzellen
des Arrays \lstinline{numbers} als ASCII-Zeichen.
Wenn man die Zahlen Little-Endian im Speicher ablegt,
ergibt sich daraus der String \lstinline{"Hello, world!\n"}.
Der verwendete Rechner hat daher insbesondere die Endianness Little-Endian.
Die oben nicht dargestellte Zahl \lstinline{numbers[7]} enthält eine Null.
Diese dient als Ende-Markierung des Strings
und sieht in Big-Endian und in Little-Endian gleich aus
(\lstinline{0x00 0x00}).
\item
\textbf{Erklären Sie die Ausgabe des Programms,
wenn Sie den Datentyp \lstinline{uint16_t} durch \lstinline{uint32_t} ersetzen.}
Die Ausgabe lautet dann: \lstinline[style=terminal]{He}.
Wenn wir 32-Bit-Zahlen mit 16-Bit-Werten initialisieren,
werden die Zahlen von links mit Nullen aufgefüllt.
Auf einem Little-Endian-Rechner bedeutet das,
daß sie rechts angehängt werden.
Nachfolgende Speicherzellen enthalten daher Nullen:
\begin{center}
\begin{picture}(13,1.5)(0,-0.5)
\put(0,0){\line(1,0){13}}
\put(0,1){\line(1,0){13}}
\multiput(0,0)(1,0){14}{\line(0,1){1}}
\put(0.35,0.38){\lstinline{'H'}}
\put(1.35,0.38){\lstinline{'e'}}
\put(2.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(3.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(4.35,0.38){\lstinline{'l'}}
\put(5.35,0.38){\lstinline{'l'}}
\put(6.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(7.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(8.35,0.38){\lstinline{'o'}}
\put(9.35,0.38){\lstinline{','}}
\put(10.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(11.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(12.2,0.38){usw.}
\put(2.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{3.95cm}{0pt}}_{\mbox{\lstinline{numbers[0]}}}$}}
\put(6.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{3.95cm}{0pt}}_{\mbox{\lstinline{numbers[1]}}}$}}
\put(10.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{3.95cm}{0pt}}_{\mbox{\lstinline{numbers[2]}}}$}}
\end{picture}
\end{center}
Die Funktion \lstinline{printf()} interpretiert das Null-Zeichen
hinter \lstinline[style=terminal]{He} als String-Ende-Markierung
und hört entsprechend an dieser Stelle mit der Ausgabe auf.
\item
\textbf{Erklären Sie die Ausgabe des Programms
und die beim Compilieren auftretenden Warnungen,
wenn Sie den Datentyp \lstinline{uint16_t} durch \lstinline{uint8_t} ersetzen.
Warum tritt die Warnung aus den vorherigen Aufgabenteilen nicht mehr auf?}
Die Ausgabe lautet: \lstinline[style=terminal]{Hlo ol!}. Sie enthält
nur jeden zweiten Buchstaben des Strings \lstinline{"Hello, world!"}.
Die Warnungen lauten:
\begin{lstlisting}[style=terminal]
aufgabe-2d.c:6:25: warning: unsigned conversion from 'int' to
'unsigned char' changes value from '25928' to '72' [-Woverflow]
aufgabe-2d.c:6:32: warning: unsigned conversion from 'int' to
'unsigned char' changes value from '27756' to '108' [-Woverflow]
...
\end{lstlisting}
Die Warnungen kommen daher, daß \lstinline{uint8_t}-Variable
nur vorzeichenlose 8-Bit-Zahlen, also Zahlen von 0 bis 255, aufnehmen können.
Zusätzliche Bits werden abgeschnitten.
Im Falle der ersten Zahl \lstinline{25928}, die ja die Buchstaben
\lstinline{'H'} und \lstinline{'e'} enthält,
sind die unteren 8 Bit allein das \lstinline{'H'} (ASCII-Wert: 72).
Daher landet auch nur das \lstinline{'H'} im Array.
Die zweite Zahl \lstinline{27756}, die die nächsten beiden Buchstaben
(beide \lstinline{'l'}) enthält, wird als \lstinline{108} gespeichert,
also nur als ein einzelnes \lstinline{'l'}.
Dies setzt sich über alle 16-Bit-Zahlen fort und erklärt,
wieso nur jeder zweite Buchstabe in der Ausgabe erscheint.
Die Warnung aus den vorherigen Aufgabenteilen tritt hier nicht mehr auf.
Dies liegt daran, daß \lstinline{printf()} ein Array
von \lstinline{char}-Variablen erwartet
und ein Array von \lstinline{uint8_t}-Variablen bekommt.
\lstinline{char}-Variable haben typischerweise 8 Bit und sind daher kompatibel
mit \lstinline{uint8_t}-Variablen.
\end{enumerate}
\iffalse
\exercise{Allgemeine Sortierfunktion}
Ein Zeiger auf \lstinline{void} ist ein \emph{generischer Zeiger},
der auch ohne explizite Typumwandlung zu allen anderen Zeigertypen
zuweisungskompatibel ist.
Wir betrachten das folgende Fragment (\file{aufgabe-3.c})
eines Sortier-Programms:
\begin{lstlisting}
#include <stdio.h>
void sort (void **data, int (*compare) (void *x1, void *x2))
{
for (int i = 0; data[i]; i++)
for (int j = i + 1; data[j]; j++)
if (compare (data[i], data[j]) > 0)
{
void *tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
}
[...]
int main (void)
{
char *strings[] = { "Thomas", "Dora", "Konrad", "Adalbert", "Sophie", NULL };
sort (strings, compare_strings);
for (int i = 0; strings[i]; i++)
printf ("%s\n", strings[i]);
printf ("\n");
int two = 2, ten = 10, zero = 0, three = 3, one = 1;
int *numbers[] = { &two, &ten, &zero, &three, &one, NULL };
sort (numbers, compare_numbers);
for (int i = 0; numbers[i]; i++)
printf ("%d\n", *numbers[i]);
return 0;
}
\end{lstlisting}
Dieses Programm stellt eine allgemeine Sortier-Funktion \lstinline{sort} zur Verfügung,
die prinzipiell beliebige Arrays sortieren kann
-- also z.\,B.\ sowohl Strings als auch Zahlen.
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die Struktur des Arrays \lstinline{numbers}.
Was ist in dem Array gespeichert, und wo befinden sich die Zahlen?
Was ist der Unterschied zwischen \lstinline{&zero} und dem Wert \lstinline{NULL}?
\points{3}
% \workspace{15}
\item
Was bedeutet \lstinline{int (*compare) (void *x1, void *x2)},
und für welchen Zweck wird es verwendet?\\
Stellen Sie einen Bezug zur objektorientierten Programmierung her.
\points{3}
% \workspace{12}
\item
Was kann passieren, wenn man in den Arrays auf das letzte Element \lstinline{NULL} verzichtet
und warum?\\
\points{1}
% \workspace{8}
\item
Ergänzen Sie das Fragment zu einem funktionsfähigen Programm,
das beide Arrays \lstinline{strings} und \lstinline{numbers}
sortiert ausgibt.
\points{5}
Abgabe über das Klausur-WLAN oder auf Datenträger ist erwünscht,
aber nicht zwingend.
Für Notizen verwenden Sie nötigenfalls
die Rückseite des letzten Klausurbogens und/oder zusätzliche Blätter.
\end{enumerate}
\exercise{PBM-Grafik}
Bei einer PBM-Grafikdatei handelt es sich
um ein abgespeichertes C-Array von Bytes (\lstinline{uint8_t}),
das die Bildinformationen enthält:
\begin{itemize}\itemsep0pt
\item Die Datei beginnt mit der Kennung \lstinline{P4},
danach folgen Breite und Höhe in Pixel als ASCII-Zahlen,
danach ein Trennzeichen und die eigentlichen Bilddaten.
\item Jedes Bit entspricht einem Pixel.
\item Nullen stehen für Weiß, Einsen für Schwarz.
\item MSB first.
\item Jede Zeile des Bildes wird auf ganze Bytes aufgefüllt.
\end{itemize}
Viele Grafikprogramme können PBM-Dateien öffnen und bearbeiten.
Der Anfang der Datei (Kennung, Breite und Höhe)
ist auch in einem Texteditor lesbar.
Beispiel (\file{aufgabe-4.pbm}):\hfill
\makebox(0,0)[tr]{\framebox{\includegraphics[scale=3]{aufgabe-4.png}}}
\begin{lstlisting}
P4
14 14
<Bilddaten>
\end{lstlisting}
In dem untenstehenden Programmfragment (\file{aufgabe-4.c})
wird eine Grafik aus Textzeilen zusammengesetzt,
so daß man mit einem Texteditor "`malen"' kann:
\begin{lstlisting}
#include <stdio.h>
[...]
int main (void)
{
pbm_open (14, 14, "test.pbm");
pbm_line (" ");
pbm_line (" XXXXXX ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X X ");
pbm_line (" X XX XX X ");
pbm_line (" X X X X ");
pbm_line (" X X ");
pbm_line (" X X X X ");
pbm_line (" X X X X ");
pbm_line (" X XXXX X ");
pbm_line (" X X ");
pbm_line (" XXXXXX ");
pbm_line (" ");
pbm_close ();
return 0;
}
\end{lstlisting}
Ergänzen Sie das Programmfragment so,
daß es eine Datei \file{test.pbm} erzeugt,
die die Grafik enthält.
Das Programm soll typische Benutzerfehler abfangen
(z.\,B.\ weniger Zeilen als in \lstinline{pbm_open} angegeben),
keine fehlerhaften Ausgaben produzieren oder abstürzen,
sondern stattdessen sinnvolle Fehlermeldungen ausgeben.
Zum Vergleich liegt eine Datei \file{aufgabe-4.pbm}
mit dem gewünschten Ergebnis bei,\\
und die Datei \file{aufgabe-4.png} enthält dasselbe Bild.
\points{10}
Abgabe über das Klausur-WLAN oder auf Datenträger ist erwünscht,
aber nicht zwingend.
Für Notizen verwenden Sie nötigenfalls
die Rückseite des letzten Klausurbogens und/oder zusätzliche Blätter.
\fi
\end{document}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment