diff --git a/20191205/hp-musterloesung-20191205.pdf b/20191205/hp-musterloesung-20191205.pdf new file mode 100644 index 0000000000000000000000000000000000000000..04cabb8d820e60a4e8eea3be1d596e9dafb89ffc Binary files /dev/null and b/20191205/hp-musterloesung-20191205.pdf differ diff --git a/20191205/hp-musterloesung-20191205.tex b/20191205/hp-musterloesung-20191205.tex new file mode 100644 index 0000000000000000000000000000000000000000..7c1c21a6fa0de9ba870510a2c1c9d6986027e662 --- /dev/null +++ b/20191205/hp-musterloesung-20191205.tex @@ -0,0 +1,235 @@ +% 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 zu den Übungsaufgaben -- 5.\ Dezember 2019} + + \exercise{Löschen aus Strings} + + Wir betrachten das folgende Programm (\gitfile{hp}{20191205}{aufgabe-1.c}): + \begin{lstlisting}[style=numbered] + #include <stdio.h> + #include <string.h> + + void delete_char_from_string (char *s, int pos) + { + for (int i = strlen (s) - 1; i > pos; i--) + s[i - 1] = s[i]; + } + + int main (void) + { + char test[] = "Hochschule Boochum"; + delete_char_from_string (test, 12); + printf ("%s\n", test); + return 0; + } + \end{lstlisting} + Die Ausgabe des Programms lautet: + \lstinline[style=terminal]{Hochschule Bmmmmmm} + + \begin{enumerate}[\quad(a)] + \item + Erklären Sie, wie die Ausgabe zustandekommt. + \points{3} +% \workspace{12} + \item + Schreiben Sie die Funktion \lstinline{delete_char_from_string()} so um, + daß sie aus dem String \lstinline{s} das Zeichen an der Stelle \lstinline{pos} + löscht.\par + Die Ausgabe des Programms müßte dann + \lstinline[style=terminal]{Hochschule Bochum} lauten. + \points{4} +% \workspace{15} + \item + Was kann passieren, wenn Sie + \lstinline{char test[] = "Hochschule Boochum";} \\ + durch + \lstinline{char *test = "Hochschule Boochum";} + ersetzen? Begründen Sie Ihre Antwort. + \points{2} +% \workspace{9} + \item + Schreiben Sie eine Funktion + \lstinline{void delete_from_string (char *s, int pos, int length)}, + die \lstinline{length} Zeichen ab der Stelle \lstinline{pos} + aus dem String \lstinline{s} löscht, + \emph{indem sie die in Aufgabenteil (b) korrigierte Funktion\/} + \lstinline{void delete_char_from_string (char *s, int pos)} + \emph{wiederholt aufruft}. \par +% Wie schnell (Landau-Symbol in Abhängigkeit von der Länge $n$ des Strings) +% arbeitet diese Funktion +% \lstinline{delete_from_string()}? +% Begründen Sie Ihre Antwort. +% \points{3} + \points{2} +% \workspace{15} + \item + Schreiben Sie eine \emph{optimierte\/} Funktion + \lstinline{void quick_delete_from_string (char *s, int pos, int length)}, + die ebenfalls \lstinline{length} Zeichen ab der Stelle \lstinline{pos} + aus dem String \lstinline{s} löscht, +% dafür aber höchstens $\mathcal{O}(n)$ an Rechenzeit benötigt. + aber mit einer einzigen Schleife auskommt. + \points{4} +% \workspace{15} + \end{enumerate} + + \solution + + \begin{enumerate}[\quad(a)] + \item + \textbf{Erklären Sie, wie die Ausgabe zustandekommt.} + + Die Zuweisung innerhalb der Schleife (Zeile 7) + kopiert ein Zeichen des Strings von rechts nach links. + Die Schleife läuft ebenfalls von rechts nach links + (\lstinline{i--} in Zeile 6). + Beides zusammen bewirkt, daß das Zeichen ganz rechts im String + (\lstinline{'m'}) immer weiter nach links kopiert wird + bis einschließlich zu Stelle \lstinline{pos}. + (Die Schleife endet zwar bereits vor \lstinline{pos} (\lstinline{i > pos}), + aber die Zuweisung erfolgt an die Stelle \lstinline{i - 1}). + + Da das Stringendesymbol nicht verschoben wird, + ändert sich die Länge des Strings nicht. + + \item + \textbf{Schreiben Sie die Funktion \lstinline{delete_char_from_string()} so um, + daß sie aus dem String \lstinline{s} das Zeichen an der Stelle \lstinline{pos} + löscht.}\par + \textbf{Die Ausgabe des Programms müßte dann + \lstinline[style=terminal]{Hochschule Bochum} lauten.} + + Siehe \gitfile{hp}{20191205}{loesung-1b-1.c}. + Geändert wurden die Zeilen 6 und 7. + Die Schleife muß von links nach rechts gehen anstatt von rechts nach links, + und sie muß das Stringendesymbol mit erfassen. + + Alternativ kann man auch nur Zeile 6 ändern, + siehe \gitfile{hp}{20191205}{loesung-1b-2.c}. + + \item + \textbf{Was kann passieren, wenn Sie + \lstinline{char test[] = "Hochschule Boochum";} \\ + durch + \lstinline{char *test = "Hochschule Boochum";} + ersetzen? Begründen Sie Ihre Antwort.} + + Das Array \lstinline{test[]} ist eine für das Programm schreibbare Variable. + Dies ist notwendig, um aus dem String ein Zeichen entfernen zu können. + + Bei \lstinline{*test} ist nur der Zeiger selbst eine schreibbare Variable. + Die String-Konstante, auf die der Zeiger zeigt, ist typischerweise nur lesbar. + Der Versuch, aus diesem String ein Zeichen zu entfernen, + endet daher typischerweise in einem Speicherzugriffsfehler. + + \item + \textbf{Schreiben Sie eine Funktion + \lstinline{void delete_from_string (char *s, int pos, int length)}, + die \lstinline{length} Zeichen ab der Stelle \lstinline{pos} + aus dem String \lstinline{s} löscht, + \emph{indem sie die in Aufgabenteil (b) korrigierte Funktion\/} + \lstinline{void delete_char_from_string (char *s, int pos)} + \emph{wiederholt aufruft}.} + + Siehe \gitfile{hp}{20191205}{loesung-1d.c}. + + \item + \textbf{Schreiben Sie eine \emph{optimierte\/} Funktion + \lstinline{void quick_delete_from_string (char *s, int pos, int}\break + \lstinline{length)}, + die ebenfalls \lstinline{length} Zeichen ab der Stelle \lstinline{pos} + aus dem String \lstinline{s} löscht, + aber mit einer einzigen Schleife auskommt.} + + Die Funktion \lstinline{strlen()} ermittelt die Länge eines Strings, + indem sie den String von seinem Anfang bis zum Stringendesymbol durchgeht. + Der Aufruf von \lstinline{strlen()} innerhalb der Schleifenbedingung + (Zeile 6) enthält daher implizit eine weitere Schleife. + + Wesentlich effizienter ist es, + wenn wir während des Durchlaufs unserer eigenen Schleife + selbst auf das Stringendesymbol achten und es als Abbruchkriterium verwenden + -- siehe \gitfile{hp}{20191205}{loesung-1e.c}. + \end{enumerate} + + \exercise{Hexdumps} + + Das folgende Programm (\gitfile{hp}{20191205}{aufgabe-2.c}) liest + einen String ein und gibt die ASCII-Werte der Buchstaben hexadezimal aus. + (Anders als z.\,B.\ \lstinline{scanf()} + akzeptiert die Funktion \lstinline{fgets()} zum Lesen von Strings auch Leerzeichen, + und sie vermeidet Pufferüberläufe.) + \begin{lstlisting}[style=numbered] + #include <stdio.h> + + int main (void) + { + char buffer[100]; + fgets (buffer, 100, stdin); + for (char *p = buffer; *p; p++) + printf ("%02x", *p); + printf ("\n"); + } + \end{lstlisting} + Beispiel: Bei der Eingabe von \lstinline[style=cmd]{Dies ist ein Test.} + erscheint die Ausgabe\\ + \lstinline[style=terminal]{44696573206973742065696e20546573742e0a}. + + Schreiben Sie ein Programm, das diese Umwandlung in umgekehrter Richtung vornimmt, + also z.\,B.\ bei Eingabe von \lstinline[style=cmd]{44696573206973742065696e20546573742e0a} + wieder \lstinline[style=terminal]{Dies ist ein Test.} ausgibt. + + \points{6} + + Hinweis für die Klausur: + Abgabe in digitaler Form ist erwünscht, aber nicht zwingend. + + \solution + + Siehe \gitfile{hp}{20191205}{loesung-2.c}. + + Das Programm macht mehrfach davon Gebrauch, + daß in C Zeichen und Zahlen äquivalent sind. + Wenn z.\,B.\ die \lstinline{char}-Variable \lstinline{c} + den Wert \lstinline{'3'} (Ziffer 3) enthält, + dann hat der Ausdruck \lstinline{c - '0'} den Wert \lstinline{3} (Zahlenwert 3). + Hierfür ist es insbesondere nicht nötig, vorauszusetzen, + daß wir den ASCII-Zeichensatz verwenden und \lstinline{'0'} + den Wert \lstinline{48} hat. + + Bei Eingabe von \lstinline[style=cmd]{44696573206973742065696e20546573742e0a} + gibt das Programm zusätzlich eine Leerzeile aus. + Die liegt daran, daß das \lstinline[style=cmd]{0a} am Ende + bereits eine Zeilenschaltung enthält und das Programm mit + \lstinline{printf ("\n")} eine zusätzliche Zeilenschaltung ausgibt. + +\end{document} diff --git a/20191205/loesung-1b-1.c b/20191205/loesung-1b-1.c new file mode 100644 index 0000000000000000000000000000000000000000..9fe99b843731d937878761b6217635506d2821d4 --- /dev/null +++ b/20191205/loesung-1b-1.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <string.h> + +void delete_char_from_string (char *s, int pos) +{ + for (int i = pos; i < strlen (s); i++) + s[i] = s[i + 1]; +} + +int main (void) +{ + char test[] = "Hochschule Boochum"; + delete_char_from_string (test, 12); + printf ("%s\n", test); + return 0; +} diff --git a/20191205/loesung-1b-2.c b/20191205/loesung-1b-2.c new file mode 100644 index 0000000000000000000000000000000000000000..04c152e34637ae7f8c0abfec8ac0e3cd8faae2eb --- /dev/null +++ b/20191205/loesung-1b-2.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <string.h> + +void delete_char_from_string (char *s, int pos) +{ + for (int i = pos + 1; i <= strlen (s); i++) + s[i - 1] = s[i]; +} + +int main (void) +{ + char test[] = "Hochschule Boochum"; + delete_char_from_string (test, 12); + printf ("%s\n", test); + return 0; +} diff --git a/20191205/loesung-1d.c b/20191205/loesung-1d.c new file mode 100644 index 0000000000000000000000000000000000000000..445d49ed2d21751b7a051a5d9e223f64726b21c6 --- /dev/null +++ b/20191205/loesung-1d.c @@ -0,0 +1,22 @@ +#include <stdio.h> +#include <string.h> + +void delete_char_from_string (char *s, int pos) +{ + for (int i = pos; i < strlen (s); i++) + s[i] = s[i + 1]; +} + +void delete_from_string (char *s, int pos, int length) +{ + for (int i = 0; i < length; i++) + delete_char_from_string (s, pos); +} + +int main (void) +{ + char test[] = "Hochschule Boochum"; + delete_from_string (test, 12, 3); + printf ("%s\n", test); + return 0; +} diff --git a/20191205/loesung-1e.c b/20191205/loesung-1e.c new file mode 100644 index 0000000000000000000000000000000000000000..060d2031759e8cd754ab67a764b727c27a731c44 --- /dev/null +++ b/20191205/loesung-1e.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <string.h> + +void quick_delete_char_from_string (char *s, int pos) +{ + for (int i = pos; s[i]; i++) + s[i] = s[i + 1]; +} + +int main (void) +{ + char test[] = "Hochschule Boochum"; + quick_delete_char_from_string (test, 12); + printf ("%s\n", test); + return 0; +} diff --git a/20191205/loesung-2.c b/20191205/loesung-2.c new file mode 100644 index 0000000000000000000000000000000000000000..872058ac9fecdcb59ac1104ca67841cb3dc974a9 --- /dev/null +++ b/20191205/loesung-2.c @@ -0,0 +1,28 @@ +#include <stdio.h> + +int read_hex (char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + { + fprintf (stderr, "invalid hex digit '%c'\n", c); + return 0; + } +} + +int main (void) +{ + char buffer[100]; + fgets (buffer, 100, stdin); + for (char *p = buffer; p[0] && p[1]; p += 2) + { + char c = 16 * read_hex (p[0]) + read_hex (p[1]); + printf ("%c", c); + } + printf ("\n"); +} diff --git a/README.md b/README.md index ee2e6d2e317ffa3eb4c8b672fd8ad627f0cc79e2..1794e588716837622313a0a601a0413eea08661d 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Musterlösungen: * [14.11.2019: Ausgabe von Hexadezimalzahlen, Einfügen in Strings, Länge von Strings](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191114/hp-musterloesung-20191114.pdf) * [21.11.2019: Zahlensysteme, Mikrocontroller](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191121/hp-musterloesung-20191121.pdf) * [28.11.2019: Datum-Bibliothek, Text-Grafik-Bibliothek, LED-Blinkmuster](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191128/hp-musterloesung-20191128.pdf) + * [05.12.2019: Löschen aus Strings, Hexdumps](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191205/hp-musterloesung-20191205.pdf) * [12.12.2019: Kondensator, hüpfender Ball](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191212/hp-musterloesung-20191212.pdf) * [19.12.2019: Trickprogrammierung, Thermometer-Baustein an I²C-Bus](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191219/hp-musterloesung-20191219.pdf) * [09.01.2020: Speicherformate von Zahlen, Zeigerarithmetik, Personen-Datenbank](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20200109/hp-musterloesung-20200109.pdf)