diff --git a/exams/20170206/hp-musterloesung-20170206.pdf b/exams/20170206/hp-musterloesung-20170206.pdf new file mode 100644 index 0000000000000000000000000000000000000000..994b2f7fc7ea0b3f587d1c9b20749dd07c59c4ee Binary files /dev/null and b/exams/20170206/hp-musterloesung-20170206.pdf differ diff --git a/exams/20170206/hp-musterloesung-20170206.tex b/exams/20170206/hp-musterloesung-20170206.tex new file mode 100644 index 0000000000000000000000000000000000000000..a3eba568496f820c3f42989481bb0713d3dbe53d --- /dev/null +++ b/exams/20170206/hp-musterloesung-20170206.tex @@ -0,0 +1,514 @@ +% 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}