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

Vorbereitung 5.11.2018

parent 2a67a1a3
No related branches found
No related tags found
No related merge requests found
Showing
with 1505 additions and 1 deletion
File added
% hp-musterloesung-20181029.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: Strings, Primzahlen, Datum-Bibliothek
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 29.\ Oktober 2018}
\exercise{Strings}
Strings werden in der Programmiersprache C
durch Zeiger auf \lstinline{char}-Variable realisiert.
Wir betrachten die folgende Funktion (Datei: \gitfile{hp}{20181029}{aufgabe-1.c}):
\begin{center}
\begin{minipage}{8cm}
\begin{lstlisting}[gobble=8]
int fun_1 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
result = 0;
return result;
}
\end{lstlisting}
\end{minipage}%
\end{center}
\begin{itemize}
\item[(a)]
Was bewirkt die Funktion? % \points{3}
\item[(b)]
Welchen Sinn hat die Bedingung "`\lstinline{s1[i] && s2[i]}"'
in der \lstinline{for}-Schleife? % \points{2}
\item[(c)]
Was würde sich ändern, wenn die Bedingung "`\lstinline{s1[i] && s2[i]}"'
in der \lstinline{for}-Schleife\\
zu "`\lstinline{s1[i]}"' verkürzt würde? % \points{3}
% \item[(d)]
% Von welcher Ordnung (Landau-Symbol) ist die Funktion \lstinline{fun_1()}
% hinsichtlich der Anzahl ihrer Zugriffe auf die Zeichen in den Strings
% -- und warum? % \points{2}
\item[(d)]
Schreiben Sie eine eigene Funktion,
die dieselbe Aufgabe erledigt wie \lstinline{fun_1()}, nur effizienter.
% und geben Sie die Ordnung (Landau-Symbol) der von Ihnen geschriebenen Funktion an. % \points{5}
\end{itemize}
\solution
\begin{itemize}
\item[(a)]
\textbf{Was bewirkt die Funktion?}
Sie vergleicht zwei Strings miteinander bis zur Länge des kürzeren Strings
und gibt bei Gleichheit 1 zurück, ansonsten 0.
Mit anderen Worten:
Die Funktion prüft, ob zwei Strings bis zur Länge des kürzeren übereinstimmen,
und gibt bei Gleichheit 1 zurück, ansonsten 0.
Die Funktion prüft insbesondere \textbf{nicht} zwei Strings auf Gleichheit,
und sie ist \textbf{nicht} funktionsgleich zur
Standard-Bibliotheksfunktion \lstinline{strcmp()}.
\item[(b)]
\textbf{Welchen Sinn hat die Bedingung "`\lstinline{s1[i] && s2[i]}"'
in der \lstinline{for}-Schleife?}
Die Bedingung prüft, ob \emph{bei einem der beiden Strings\/}
die Ende-Markierung (Null-Symbol) erreicht ist.
Falls ja, wird die Schleife beendet.
\item[(c)]
\textbf{Was würde sich ändern, wenn die Bedingung "`\lstinline{s1[i] && s2[i]}"'
in der \lstinline{for}-Schleife\\
zu "`\lstinline{s1[i]}"' verkürzt würde?}
In diesem Fall würde nur für \lstinline{s1} geprüft,
ob das Ende erreicht ist.
Wenn \lstinline{s1} länger ist als \lstinline{s2},
würde \lstinline{s2} über sein Ende hinaus ausgelesen.
Dies kann zu Lesezugriffen auf Speicher außerhalb des Programms
und damit zu einem Absturz führen
("`Speicherzugriffsfehler"', "`Schutzverletzung"').
\item[(d)]
\textbf{Schreiben Sie eine eigene Funktion,
die dieselbe Aufgabe erledigt wie \lstinline{fun_1()}, nur effizienter.}
Die Effizienz läßt sich steigern, indem man die Schleife abbricht,
sobald das Ergebnis feststeht.
Es folgen drei Möglichkeiten, dies zu realisieren.
\end{itemize}
\begin{center}
\begin{minipage}[t]{8cm}
Erweiterung der Schleifenbedingung:
\begin{lstlisting}[gobble=8]
int fun_2 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i] && result; i++)
if (s1[i] != s2[i])
result = 0;
return result;
}
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{6cm}
Verwendung von \lstinline{return}:
\begin{lstlisting}[gobble=8]
int fun_3 (char *s1, char *s2)
{
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
return 0;
return 1;
}
\end{lstlisting}
\end{minipage}
\end{center}
\vspace*{-1cm}\goodbreak
\begin{center}
\begin{minipage}{9cm}
Die nebenstehende Lösung unter Verwendung von \lstinline{break}
ist zwar ebenfalls richtig, aber länger und weniger übersichtlich
als die beiden anderen Lösungen.
\smallskip
Die Datei \gitfile{hp}{20181029}{loesung-1.c} enthält ein Testprogramm
für alle o.\,a.\ Lösungen.
Das Programm testet nur die offensichtlichsten Fälle;
für den Einsatz der Funktionen in einer Produktivumgebung
wären weitaus umfassendere Tests erforderlich.
\smallskip
Das Testprogramm enthält String-Zuweisungen wie z.\,B.\
\lstinline{s2 = "Apfel"}.
Dies funktioniert, weil wir damit einen Zeiger (\lstinline{char *s2})
auf einen neuen Speicherbereich (\lstinline{"Apfel"}) zeigen lassen.
Eine entsprechende Zuweisung zwischen Arrays
(\lstinline{char s3[] = "Birne"; s3 = "Pfirsich";)}
funktioniert \emph{nicht}.
\end{minipage}\hspace*{1cm}%
\begin{minipage}{6cm}
\begin{lstlisting}[gobble=8]
int fun_4 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
{
result = 0;
break;
}
return result;
}
\end{lstlisting}
\end{minipage}
\end{center}
\exercise{Fehlerhaftes Programm: Primzahlen}
\begin{minipage}[t]{5.5cm}
Das nebenstehende Primzahlsuchprogramm (Datei: \gitfile{hp}{20181029}{aufgabe-2.c})
soll Zahlen ausgeben, die genau zwei Teiler haben, ist aber fehlerhaft.
\smallskip
Korrigieren Sie das Programm derart, daß ein Programm entsteht,
welches alle Primzahlen kleiner 100 ausgibt.% \points 5
\end{minipage}\hfill
\begin{minipage}[t]{9cm}
\vspace*{-0.5cm}
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int n, i, divisors;
for (n = 0; n < 100; n++)
divisors = 0;
for (i = 0; i < n; i++)
if (n % i == 0)
divisors++;
if (divisors = 2)
printf ("%d ist eine Primzahl.\n", n);
return 0;
}
\end{lstlisting}
\end{minipage}
\solution
Beim Compilieren des Beispiel-Programms mit
\lstinline[style=cmd]{gcc -Wall} erhalten wir die folgende Warnung:
\begin{lstlisting}[style=terminal]
aufgabe-2.c:11:5: warning: suggest parentheses around assignment
used as truth value [-Wparentheses]
\end{lstlisting}
Beim Ausführen gibt das Programm die folgende (falsche) Behauptung aus:
\begin{lstlisting}[style=terminal]
100 ist eine Primzahl.
\end{lstlisting}
Einen ersten Hinweis auf den Fehler im Programm liefert die Warnung.
Die Bedingung \lstinline{if (divisors = 2)} in Zeile 11
steht \emph{nicht\/} für einen Vergleich
der Variablen \lstinline{divisors} mit der Zahl 2,
sondern für eine Zuweisung der Zahl 2 an die Variable \lstinline{divisors}.
Neben dem \emph{Seiteneffekt\/} der Zuweisung gibt \lstinline{divisors = 2}
den Wert \lstinline{2} zurück.
Als Bedingung interpretiert, hat \lstinline{2} den Wahrheitswert "`wahr"' ("`true"');
die \lstinline{printf()}-Anweisung wird daher in jedem Fall ausgeführt.
Korrektur dieses Fehlers: \lstinline{if (divisors == 2)}
-- siehe die Datei \gitfile{hp}{20181029}{loesung-2-1.c}.
\bigskip
Nach der Korrektur dieses Fehlers compiliert das Programm ohne Warnung,
gibt aber beim Ausführen die folgende Fehlermeldung aus:
\begin{lstlisting}[style=terminal]
Gleitkomma-Ausnahme
\end{lstlisting}
(Bemerkung: Bei ausgeschalteter Optimierung
-- \lstinline[style=cmd]{gcc} ohne \lstinline[style=cmd]{-O} --
kommt diese Fehlermeldung bereits beim ersten Versuch, das Programm auszuführen.
Der Grund für dieses Verhalten ist, daß bei eingeschalteter Optimierung
irrelevante Teile des Programms entfernt und gar nicht ausgeführt werden,
so daß der Fehler nicht zum Tragen kommt.
In diesem Fall wurde die Berechnung von \lstinline{divisors} komplett wegoptimiert,
da der Wert dieser Variablen nirgendwo abgefragt,
sondern durch die Zuweisung \lstinline{if (divisors = 2)}
sofort wieder überschrieben wurde.)
Die Fehlermeldung "`\lstinline[style=terminal]{Gleitkomma-Ausnahme}"'
ist insofern irreführend, als daß hier gar keine Gleitkommazahlen im Spiel sind;
andererseits deutet sie auf einen Rechenfehler hin, was auch tatsächlich zutrifft.
Durch Untersuchen aller Rechenoperationen
-- z.\,B.\ durch das Einfügen zusätzlicher \lstinline{printf()} --
finden wir den Fehler in Zeile 9:
Die Modulo-Operation \lstinline{n % i} ist eine Division,
die dann fehlschlägt, wenn der Divisor \lstinline{i} den Wert 0 hat.
Die Fehlerursache ist die bei 0 beginnende \lstinline{for}-Schleife in Zeile 8:
\lstinline{for (i = 0; i < n; i++)}.
Korrektur dieses Fehlers: Beginn der Schleife mit \lstinline{i = 1}
statt \lstinline{i = 0} -- siehe die Datei \gitfile{hp}{20181029}{loesung-2-2.c}.
\bigskip
Nach der Korrektur dieses Fehlers gibt das Programm überhaupt nichts mehr aus.
Durch Untersuchen des Verhaltens des Programms
-- z.\,B.\ durch das Einfügen zusätzlicher \lstinline{printf()} --
stellen wir fest, daß die Zeilen 8 bis 12 des Programms nur einmal ausgeführt werden
und nicht, wie die \lstinline{for}-Schleife in Zeile 6 vermuten ließe, 100mal.
Der Grund dafür ist, daß sich die \lstinline{for}-Schleife
nur auf die unmittelbar folgende Anweisung \lstinline{divisors = 0} bezieht.
Nur diese Zuweisung wird 100mal ausgeführt;
alles andere befindet sich außerhalb der \lstinline{for}-Schleife.
(Die Einrückung hat in C keine inhaltliche Bedeutung,
sondern dient nur zur Verdeutlichung der Struktur des Programms.
In diesem Fall entsprach die tatsächliche Struktur nicht der beabsichtigten.)
Korrektur dieses Fehlers:
geschweifte Klammern um den Inhalt der äußeren \lstinline{for}-Schleife
-- siehe die Datei \gitfile{hp}{20181029}{loesung-2-3.c}.
\bigskip
Nach der Korrektur dieses Fehlers gibt das Programm folgendes aus:
\begin{lstlisting}[style=terminal]
4 ist eine Primzahl.
9 ist eine Primzahl.
25 ist eine Primzahl.
49 ist eine Primzahl.
\end{lstlisting}
Diese Zahlen sind keine Primzahlen (mit zwei Teilern),
sondern sie haben drei Teiler.
Demnach findet das Programm einen Teiler zu wenig.
(Um diesen Fehler zu finden, kann man sich zu jeder Zahl
die gefundene Anzahl der Teiler \lstinline{divisors} ausgeben lassen.)
Der nicht gefundene Teiler ist jeweils die Zahl selbst.
Dies kommt daher, daß die Schleife
\lstinline{for (i = 1; i < n; i++)} nur bis \lstinline{n - 1} geht,
also keine Division durch \lstinline{n} stattfindet.
Korrektur dieses Fehlers: Schleifenbedingung \lstinline{i <= n}
statt \lstinline{i < n}
-- siehe die Datei \gitfile{hp}{20181029}{loesung-2-4.c}.
\bigskip
Nach der Korrektur dieses Fehlers verhält sich das Programm korrekt.
Die Datei \gitfile{hp}{20181029}{loesung-2-4.c} enthält somit das korrigierte Programm.
\exercise{Datum-Bibliothek}
Schreiben Sie eine Bibliothek (= Sammlung von Deklarationen und Funktionen)
zur Behandlung von Datumsangaben.
Diese soll enthalten:
\begin{itemize}
\item
einen \lstinline{struct}-Datentyp \lstinline{date},
der eine Datumsangabe speichert,
\item
eine Funktion \lstinline{void date_print (date *d)}, die ein Datum ausgibt,
\item
eine Funktion \lstinline{int date_set (date *d, int day, int month, int year)},
die ein Datum auf einen gegebenen Tag setzt
und zurückgibt, ob es sich um ein gültiges Datum handelt (0 = nein, 1 = ja),
\item
eine Funktion \lstinline{void date_next (date *d)},
die ein Datum auf den nächsten Tag vorrückt.
\end{itemize}
\solution
Die Datei \gitfile{hp}{20181029}{loesung-3.c}
enthält die Bibliothek zusammen mit einem Test-Programm.
Eine detaillierte Anleitung,
wie man auf die Funktion \lstinline{date_next()} kommt,
finden Sie im Skript zur Lehrveranstaltung, Datei \gitfile{hp}{script}{hp-2018ws.pdf},
ab Seite 29.
(Die Vorgehensweise,
die Bibliothek und das Hauptprogramm in dieselbe Datei zu schreiben,
hat den Nachteil,
daß man die Bibliothek in jedes weitere Programm, das sie benutzt,
kopieren und auch dort aktuell halten muß.
Eine sinnvollere Lösung wird demnächst in der Vorlesung vorgestellt werden.)
\end{document}
No preview for this file type
......@@ -30,7 +30,7 @@
\thispagestyle{empty}
\section*{Hardwarenahe Programmierung / Angewandte Informatik\\
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 29.\ Oktober 2018}
\exercise{Strings}
......
#include <stdio.h>
int fun_1 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
result = 0;
return result;
}
int fun_2 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i] && result; i++)
if (s1[i] != s2[i])
result = 0;
return result;
}
int fun_3 (char *s1, char *s2)
{
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
return 0;
return 1;
}
int fun_4 (char *s1, char *s2)
{
int result = 1;
for (int i = 0; s1[i] && s2[i]; i++)
if (s1[i] != s2[i])
{
result = 0;
break;
}
return result;
}
int main (void)
{
char *s1 = "Apfel";
char *s2 = "Apfelkuchen";
if (fun_1 (s1, s2) && fun_2 (s1, s2) && fun_3 (s1, s2) && fun_4 (s1, s2))
printf ("OK\n");
else
printf ("failed\n");
s1 = "Apfelkuchen";
s2 = "Apfel";
if (fun_1 (s1, s2) && fun_2 (s1, s2) && fun_3 (s1, s2) && fun_4 (s1, s2))
printf ("OK\n");
else
printf ("failed\n");
s2 = "Birnenmarmelade";
if (fun_1 (s1, s2) || fun_2 (s1, s2) || fun_3 (s1, s2) || fun_4 (s1, s2))
printf ("failed\n");
else
printf ("OK\n");
s1 = s2;
s2 = "Apfelkuchen";
if (fun_1 (s1, s2) || fun_2 (s1, s2) || fun_3 (s1, s2) || fun_4 (s1, s2))
printf ("failed\n");
else
printf ("OK\n");
return 0;
}
#include <stdio.h>
int main (void)
{
int n, i, divisors;
for (n = 0; n < 100; n++)
divisors = 0;
for (i = 0; i < n; i++)
if (n % i == 0)
divisors++;
if (divisors == 2)
printf ("%d ist eine Primzahl.\n", n);
return 0;
}
#include <stdio.h>
int main (void)
{
int n, i, divisors;
for (n = 0; n < 100; n++)
divisors = 0;
for (i = 1; i < n; i++)
if (n % i == 0)
divisors++;
if (divisors == 2)
printf ("%d ist eine Primzahl.\n", n);
return 0;
}
#include <stdio.h>
int main (void)
{
int n, i, divisors;
for (n = 0; n < 100; n++)
{
divisors = 0;
for (i = 1; i < n; i++)
if (n % i == 0)
divisors++;
if (divisors == 2)
printf ("%d ist eine Primzahl.\n", n);
}
return 0;
}
#include <stdio.h>
int main (void)
{
int n, i, divisors;
for (n = 0; n < 100; n++)
{
divisors = 0;
for (i = 1; i <= n; i++)
if (n % i == 0)
divisors++;
if (divisors == 2)
printf ("%d ist eine Primzahl.\n", n);
}
return 0;
}
#include <stdio.h>
typedef struct
{
char day, month;
int year;
}
date;
int is_leap_year (int year)
{
if (year % 4 == 0)
if (year % 100 == 0)
if (year % 400 == 0)
return 1;
else
return 0;
else
return 1;
else
return 0;
}
int days_in_month (int month, int year)
{
if (month == 2)
if (is_leap_year (year))
return 29;
else
return 28;
else if (month == 4 || month == 6 || month == 9 || month == 11)
return 30;
else
return 31;
}
void date_print (date *d)
{
printf ("%02d.%02d.%04d", d->day, d->month, d->year);
}
int date_set (date *d, char day, char month, int year)
{
d->year = year;
if (month > 0 && month <= 12)
d->month = month;
else
return 0;
if (day > 0 && day <= days_in_month (month, year))
d->day = day;
else
return 0;
return 1;
}
void date_next (date *d)
{
d->day++;
if (d->day > days_in_month (d->month, d->year))
{
d->month++;
d->day = 1;
if (d->month > 12)
{
d->year++;
d->month = 1;
}
}
}
void check (char day, char month, int year)
{
date d;
if (date_set (&d, day, month, year))
{
date_print (&d);
printf (" --> ");
date_next (&d);
date_print (&d);
printf ("\n");
}
else
printf ("%02d.%02d.%04d: invalid date\n", day, month, year);
}
int main (void)
{
check (6, 11, 2018);
check (29, 11, 2018);
check (30, 11, 2018);
check (31, 11, 2018);
check (29, 12, 2018);
check (30, 12, 2018);
check (31, 12, 2018);
check (28, 2, 2016);
check (29, 2, 2016);
check (30, 2, 2016);
check (28, 2, 2015);
check (29, 2, 2015);
check (30, 2, 2015);
check (31, 12, 2008);
check (28, 2, 2000);
check (29, 2, 2000);
check (30, 2, 2000);
check (28, 2, 1900);
check (29, 2, 1900);
check (30, 2, 1900);
return 0;
}
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
target[i+1] = target[i];
target[pos] = src;
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
File added
% hp-20181105.pdf - Lecture Slides on Low-Level Programming
% Copyright (C) 2012, 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: String-Operationen
\documentclass[10pt,t]{beamer}
\usepackage{pgslides}
\usepackage{pdftricks}
\usepackage{tikz}
\begin{psinputs}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel}
\usepackage[T1]{fontenc}
\usepackage{helvet}
\renewcommand*\familydefault{\sfdefault}
\usepackage{pstricks,pst-grad}
\end{psinputs}
\title{Hardwarenahe Programmierung}
\author{Prof.\ Dr.\ rer.\ nat.\ Peter Gerwinski}
\date{5.\ November 2018}
\begin{document}
\maketitleframe
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp.git}}}
\item[\textbf{2}] \textbf{Einführung in C}
\begin{itemize}
% \item[2.1] Hello, world!
\vspace*{-0.1cm}
\item[\dots]
\item[2.10] Zeiger
\item[2.11] Arrays und Strings
\item[2.12] Strukturen
\color{medgreen}
\item[2.13] Dateien und Fehlerbehandlung
\item[2.14] Parameter des Hauptprogramms
\color{red}
\item[2.15] String-Operationen
\end{itemize}
\item[\textbf{3}] \textbf{Bibliotheken}
\begin{itemize}
\color{red}
\item[3.1] Der Präprozessor
\item[3.2] Bibliotheken einbinden
\item[3.3] Bibliotheken verwenden
\vspace*{-0.1cm}
\item[\dots]
% \item[3.4] Projekt organisieren: make
\end{itemize}
\item[\textbf{\dots}]
% \item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
% \item[\textbf{5}] \textbf{Algorithmen}
% \item[\textbf{6}] \textbf{Ergänzungen und Ausblicke}
\end{itemize}
\vspace*{-1cm}
\end{frame}
\setcounter{section}{1}
\section{Einführung in C}
\setcounter{subsection}{12}
\subsection{Dateien und Fehlerbehandlung}
\begin{frame}[fragile]
\showsubsection
\vspace*{-0.2925cm}
\begin{minipage}[t]{6cm}
\begin{onlyenv}<1>
\begin{lstlisting}[gobble=8]
¡#include <stdio.h>
int main (void)
{
FILE *f = fopen ("fhello.txt", "w");
fprintf (f, "Hello, world!\n");
fclose (f);
return 0;
}¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\begin{lstlisting}[gobble=8]
¡#include <stdio.h>
int main (void)
{
FILE *f = fopen ("fhello.txt", "w");
if (f)
{
fprintf (f, "Hello, world!\n");
fclose (f);
}
return 0;
}¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<3>
\begin{lstlisting}[gobble=8]
¡#include <stdio.h>
#include <errno.h>
int main (void)
{
FILE *f = fopen ("fhello.txt", "w");
if (f)
{
fprintf (f, "Hello, world!\n");
fclose (f);
}
else
fprintf (stderr, "error #%d\n", errno);
return 0;
}¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<4>
\begin{lstlisting}[gobble=8]
¡#include <stdio.h>
#include <errno.h>
#include <string.h>
int main (void)
{
FILE *f = fopen ("fhello.txt", "w");
if (f)
{
fprintf (f, "Hello, world!\n");
fclose (f);
}
else
{
char *msg = strerror (errno);
fprintf (stderr, "%s\n", msg);
}
return 0;
}¿
\end{lstlisting}
\vspace*{-1cm}
\end{onlyenv}
\begin{onlyenv}<5->
\begin{lstlisting}[gobble=8]
¡#include <stdio.h>
#include <errno.h>
#include <er¡ror.h>
int main (void)
{
FILE *f = fopen ("fhello.txt", "w");
if (!f)
error (-1, errno, "cannot open file");
fprintf (f, "Hello, world!\n");
fclose (f);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\end{minipage}\pause\hspace*{-1.5cm}%
\begin{minipage}[t]{8.5cm}
\bigskip
\only<3->{\bigskip}
\begin{itemize}
\item
Wenn die Datei nicht geöffnet werden kann,\\
gibt \lstinline{fopen()} den Wert \lstinline{NULL} zurück.
\pause
\medskip
\item
\addtolength{\leftskip}{1cm}
Die globale Variable \lstinline{int errno}\\
enthält dann die Nummer des Fehlers.\\
Benötigt: \lstinline{#include <errno.h>}
\pause
\medskip
\only<5->{\bigskip}
\item
Die Funktion \lstinline{strerror()} wandelt \lstinline{errno}\\
in einen Fehlermeldungstext um.\\
Benötigt: \lstinline{#include <string.h>}
\pause
\medskip
\item
\addtolength{\leftskip}{-1.5cm}
Die Funktion \lstinline{error()} gibt eine Fehlermeldung aus\\
und beendet das Programm.\\
Benötigt: \lstinline{#include <er¡¿ror.h>}
% \pause
\medskip
\item
\textbf{Niemals Fehler einfach ignorieren!}
\end{itemize}
\addtolength{\leftskip}{0.5cm}
\end{minipage}
\end{frame}
\subsection{Parameter des Hauptprogramms}
\begin{frame}[fragile]
\showsubsection
\vspace*{-1.4\medskipamount}
\vbox to 0pt{%
\begin{onlyenv}<1-2>
\begin{lstlisting}[gobble=8]
#include <stdio.h>
int main (int argc, char **argv)
{
printf ("argc = %d\n", argc);
for (int i = 0; i < argc; i++)
printf ("argv[%d] = \"%s\"\n", i, argv[i]);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<3>
\begin{lstlisting}[gobble=8]
#include <stdio.h>
int main (int argc, char **argv)
{
printf ("argc = %d\n", argc);
for (int i = 0; *argv; i++, argv++)
printf ("argv[%d] = \"%s\"\n", i, *argv);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<4>
\begin{lstlisting}[gobble=8]
#include <stdio.h>
int main (int argc, char **argv)
{
printf ("argc = %d\n", argc);
int i = 0;
while (*argv)
printf ("argv[%d] = \"%s\"\n",
i++, *argv++);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<5->
\begin{lstlisting}[gobble=8]
#include <stdio.h>
int main (int argc, char **argv)
{
printf ("argc = %d\n", argc);
int i = 0;
while (*argv)
{
printf ("argv[%d] = \"%s\"\n", i, *argv);
i++;
argv++;
}
return 0;
}
\end{lstlisting}
\end{onlyenv}
\vss
}
\pause
\strut\hfill
\begin{minipage}{5.5cm}
\begin{itemize}
\item
\lstinline{argc}: Anzahl der Parameter
\item
\lstinline{argv}: Array von Strings\\
(= Zeiger auf Zeiger auf \lstinline{char}s)\\
mit den Parametern
\item
Parameter Nr.\ 0:\\
Name des Programms selbst,\\
wie es aufgerufen wurde
\pause
\item
letzter Eintrag im Array:\\
\lstinline{NULL}-Zeiger\\
(als Ende-Markierung)
\end{itemize}
\end{minipage}
\end{frame}
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp.git}}}
\item[\textbf{2}] \textbf{Einführung in C}
\begin{itemize}
% \item[2.1] Hello, world!
\vspace*{-0.1cm}
\item[\dots]
\item[2.10] Zeiger
\item[2.11] Arrays und Strings
\item[2.12] Strukturen
\color{medgreen}
\item[2.13] Dateien und Fehlerbehandlung
\item[2.14] Parameter des Hauptprogramms
\color{red}
\item[2.15] String-Operationen
\end{itemize}
\item[\textbf{3}] \textbf{Bibliotheken}
\begin{itemize}
\color{red}
\item[3.1] Der Präprozessor
\item[3.2] Bibliotheken einbinden
\item[3.3] Bibliotheken verwenden
\vspace*{-0.1cm}
\item[\dots]
% \item[3.4] Projekt organisieren: make
\end{itemize}
\item[\textbf{\dots}]
% \item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
% \item[\textbf{5}] \textbf{Algorithmen}
% \item[\textbf{6}] \textbf{Ergänzungen und Ausblicke}
\end{itemize}
\vspace*{-1cm}
\end{frame}
\subsection{String-Operationen}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdio.h>
int main (void)
{
char name[100];
printf ("Ihr Name: ");
fgets (name, 100, stdin);
printf ("Hallo, %s!\n", name);
return 0;
}
\end{lstlisting}
\vspace*{-4.5cm}\strut\hfill
\begin{minipage}{6cm}
Probleme mit \lstinline{scanf ("%s", name)}:
\begin{itemize}
\item
Leerzeichen beendet Eingabe
\item
keine Prüfung der Puffergröße
\end{itemize}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\vspace*{-1.4\medskipamount}
\vbox to 0pt{%
\begin{lstlisting}[gobble=6]
#include <stdio.h>
#include <string.h>
int main (void)
{
char hello[] = "Hello, world!\n";
printf ("%s\n", hello);
printf ("%zd\n", strlen (hello));
printf ("%s\n", hello + 7);
printf ("%zd\n", strlen (hello + 7));
hello[5] = 0;
printf ("%s\n", hello);
printf ("%zd\n", strlen (hello));
return 0;
}
\end{lstlisting}
\vss
}
\pause
\strut\hfill
\begin{minipage}{5.5cm}
\begin{itemize}
\item
\lstinline{strlen()} gibt die\\
Länge eines Strings zurück.
\item
Es enthält eine Schleife.
\pause
\item
Typ des Rückgabewerts:\\
\lstinline{size_t} = ganze Zahl\\
von der Größe eines Zeigers
\item
in \lstinline{printf()}: \lstinline{%zd} (\emph{si\textbf{z}e})
\pause
\bigskip
\item
Zeiger erhöhen:\\
String vorne abschneiden
\pause
\item
\lstinline{0}-Symbol (= Ende-Markierung)\\
in den String schreiben:\\
String hinten abschneiden
\pause
\item
\textbf{Der für den String reservierte Speicherplatz bleibt derselbe!}
\end{itemize}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\vspace*{-1.4\medskipamount}
\vbox to 0pt{%
\begin{lstlisting}[gobble=6]
#include <stdio.h>
#include <string.h>
int main (void)
{
char *anton = "Anton";
char *zacharias = "Zacharias";
printf ("%d\n", strcmp (anton, zacharias));
printf ("%d\n", strcmp (zacharias, anton));
printf ("%d\n", strcmp (anton, anton));
char buffer[100] = "Huber ";
strcat (buffer, anton);
printf ("%s\n", buffer);
return 0;
}
\end{lstlisting}
\vss
}
\pause
\strut\hfill
\begin{minipage}{5.5cm}
\begin{itemize}
\item
\lstinline{strcmp()}: Strings vergleichen
\item
alphabetisch nach ASCII\\
(Groß- $<$ Kleinbuchstaben,\\
ohne Umlaute usw.)
\item
Rückgabewert:\\
$-1$, wenn linker String kleiner,\\
$+1$, wenn rechter String kleiner,\\
$0$, wenn beide Strings gleich
\pause
\bigskip
\item
\lstinline{strcat()}: String anhängen\\
(\emph{con\textbf{cat}enate})
\pause
\item
\textbf{Ob der Speicherplatz reicht, wird nicht geprüft!}
\end{itemize}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\vspace*{-1.4\medskipamount}
\vbox to 0pt{%
\begin{lstlisting}[gobble=6]
#include <stdio.h>
#include <string.h>
int main (void)
{
char buffer[100] = "";
sprintf (buffer, "Die Antwort lautet: %d", 42);
printf ("%s\n", buffer);
char *answer = strstr (buffer, "Antwort");
printf ("%s\n", answer);
printf ("found at: %zd\n", answer - buffer);
return 0;
}
\end{lstlisting}
\vss
}
\pause
\strut\hfill
\begin{minipage}{5.5cm}
\begin{itemize}
\item
\lstinline{sprintf()}: in einen\\
String schreiben
\pause
\item
\textbf{Ob der Speicherplatz reicht, wird nicht geprüft!}
\pause
\vspace{4\bigskipamount}
\item
\lstinline{strstr()}: String in String suchen
\item
Rückgabewert: Zeiger auf den gefundenen String
\item
Index berechnen:\\
Zeiger $-$ Zeiger $=$ Zahl\\
von der Größe eines Zeigers
\end{itemize}
\end{minipage}
\end{frame}
\begin{frame}
\showsection
Sprachelemente weitgehend komplett
\bigskip
Es fehlen:
\begin{itemize}
\item
Ergänzungen (z.\,B.\ ternärer Operator, \lstinline{union}, \lstinline{unsigned}, \lstinline{volatile})
\item
Bibliotheksfunktionen (z.\,B.\ \lstinline{malloc()})
\arrowitem
werden eingeführt, wenn wir sie brauchen
\bigskip
\item
Konzepte (z.\,B.\ rekursive Datenstrukturen, Klassen selbst bauen)
\arrowitem
werden eingeführt, wenn wir sie brauchen, oder:
\arrowitem
Literatur\\[\smallskipamount]
(z.\,B.\ Wikibooks: C-Programmierung,\\
Dokumentation zu Compiler und Bibliotheken)
\bigskip
\item
Praxiserfahrung
\arrowitem
Übung und Praktikum: nur Einstieg
\arrowitem
selbständig arbeiten
\end{itemize}
\end{frame}
\nosectionnonumber{\inserttitle}
\begin{frame}
\shownosectionnonumber
\begin{itemize}
\item[\textbf{1}] \textbf{Einführung}
\hfill\makebox(0,0)[br]{\raisebox{2.25ex}{\url{https://gitlab.cvh-server.de/pgerwinski/hp.git}}}
\item[\textbf{2}] \textbf{Einführung in C}
\begin{itemize}
% \item[2.1] Hello, world!
\vspace*{-0.1cm}
\item[\dots]
\item[2.10] Zeiger
\item[2.11] Arrays und Strings
\item[2.12] Strukturen
\item[2.13] Dateien und Fehlerbehandlung
\item[2.14] Parameter des Hauptprogramms
\color{medgreen}
\item[2.15] String-Operationen
\end{itemize}
\item[\textbf{3}] \textbf{Bibliotheken}
\begin{itemize}
\color{red}
\item[3.1] Der Präprozessor
\item[3.2] Bibliotheken einbinden
\item[3.3] Bibliotheken verwenden
\vspace*{-0.1cm}
\item[\dots]
% \item[3.4] Projekt organisieren: make
\end{itemize}
\item[\textbf{\dots}]
% \item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
% \item[\textbf{5}] \textbf{Algorithmen}
% \item[\textbf{6}] \textbf{Ergänzungen und Ausblicke}
\end{itemize}
\vspace*{-1cm}
\end{frame}
\end{document}
File added
% hp-uebung-20181105.pdf - 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: Ausgabe von Hexadezimalzahlen, Einfügen in Strings, Länge von Strings
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
% \thispagestyle{empty}
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 5.\ November 2018}
Diese Übung enthält Punkteangaben wie in einer Klausur.
Um zu "`bestehen"', müssen Sie innerhalb von 80 Minuten
unter Verwendung ausschließlich zugelassener Hilfsmittel
14 Punkte (von insgesamt \totalpoints) erreichen.
\exercise{Ausgabe von Hexadezimalzahlen}
Schreiben Sie eine Funktion \lstinline{void print_hex (uint32_t x)},
die eine gegebene vorzeichenlose 32-Bit-Ganzzahl \lstinline{x}
als Hexadezimalzahl ausgibt.
(Der Datentyp \lstinline{uint32_t} ist mit \lstinline{#include <stdint.h>} verfügbar.)
Verwenden Sie dafür \emph{nicht\/} \lstinline{printf()} mit
der Formatspezifikation \lstinline{%x} als fertige Lösung,
sondern programmieren Sie die nötige Ausgabe selbst.
(Für Tests ist \lstinline{%x} hingegen erlaubt und sicherlich nützlich.)
Die Verwendung von \lstinline{printf()}
mit anderen Formatspezifikationen wie z.\,B.\ \lstinline{%d}
oder \lstinline{%c} oder \lstinline{%s} ist hingegen zulässig.
\points{8}
(Hinweis für die Klausur: Abgabe auf Datenträger ist erlaubt und erwünscht,
aber nicht zwingend.)
\exercise{Einfügen in Strings}
Wir betrachten das folgende Programm (\file{aufgabe-2.c}):
% \begin{lstlisting}[style=numbered]
\begin{lstlisting}
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
target[i+1] = target[i];
target[pos] = src;
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
\end{lstlisting}
Die Ausgabe des Programms lautet:
\lstinline[style=terminal]{Hochschhhhhhhhhhh}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die Ausgabe.
\points{3}
% \workspace{12}
\item
Schreiben Sie die Funktion \lstinline|insert_into_string()| so um,
daß sie den Buchstben \lstinline{src} an der Stelle \lstinline{pos}
in den String \lstinline{target} einfügt.\par
Die Ausgabe des Programms müßte dann
\lstinline[style=terminal]{Hochschule Bochum} lauten.
\points{2}
% \workspace{13}
\item
Was kann passieren, wenn Sie die Zeile
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char test[] = "Hochshule Bochum";}
ersetzen und warum?
\points{2}
% \workspace{10}
\item
Was passiert, wenn Sie
\lstinline{char test[100] = "Hochshule Bochum";}\\
durch
\lstinline{char *test = "Hochshule Bochum";}
ersetzen und warum?
\points{2}
% \workspace{10}
% \item
% Schreiben Sie eine Funktion
% \lstinline{void insert_into_string_sorted (char src, char *target)},
% die voraussetzt, daß der String \lstinline{target} alphabetisch sortiert ist
% und den Buchstaben \lstinline{src} an der alphabetisch richtigen Stelle
% einfügt. Diese Funktion darf die bereits vorhandene Funktion
% \lstinline|insert_into_string()| aufrufen.\\
% \points{4}\par
% Zum Testen eignen sich die folgenden Zeilen im Hauptprogramm:
% \begin{lstlisting}[gobble=8]
% char test[100] = "";
% insert_into_string_sorted ('c', test);
% insert_into_string_sorted ('a', test);
% insert_into_string_sorted ('d', test);
% insert_into_string_sorted ('b', test);
% \end{lstlisting}
% Danach sollte \lstinline{test[]} die Zeichenfolge \lstinline{"abcd"} enthalten.
% \workspace{14}
% \item
% Wie schnell (Landau-Symbol in Abhängigkeit von der Länge $n$ des Strings)
% arbeitet Ihre Funktion
% \lstinline{void insert_into_string_sorted (char src, char *target)}
% und warum?
% \points{1}
% \workspace{10}
% \item
% Beschreiben Sie -- in Worten oder als C-Quelltext --, wie man die Funktion\\
% \lstinline{void insert_into_string_sorted (char src, char *target)}
% so gestalten kann,\\
% daß sie in $\mathcal{O}(\log n)$ arbeitet.
% \points{3}
% \workspace{35}
\end{enumerate}
\exercise{Länge von Strings}
Strings werden in der Programmiersprache C durch Zeiger auf \lstinline{char}-Variable realisiert.
Beispiel: \lstinline{char *hello_world = "Hello, world!\n"}
Die Systembibliothek stellt eine Funktion \lstinline{strlen()} zur Ermittlung der Länge von Strings\\
zur Verfügung (\lstinline{#include <string.h>}).
\begin{itemize}
\item[(a)]
Auf welche Weise ist die Länge eines Strings gekennzeichnet?
\points{1}
\item[(b)]
Wie lang ist die Beispiel-String-Konstante \lstinline{"Hello, world!\n"},
und wieviel Speicherplatz belegt sie?\\
\points{2}
\item[(c)]
Schreiben Sie eine eigene Funktion \lstinline{int strlen (char *s)},
die die Länge eines Strings zurückgibt.\\
\points{3}
\end{itemize}
Wir betrachten nun die folgenden Funktionen (Datei: \gitfile{hp}{20171120}{aufgabe-3.c}):
\begin{center}
\begin{minipage}{8cm}
\begin{lstlisting}[gobble=8]
int fun_1 (char *s)
{
int x = 0;
for (int i = 0; i < strlen (s); i++)
x += s[i];
return x;
}
\end{lstlisting}
\end{minipage}%
\begin{minipage}{6cm}
\vspace*{-1cm}
\begin{lstlisting}[gobble=8]
int fun_2 (char *s)
{
int i = 0, x = 0;
int len = strlen (s);
while (i < len)
x += s[i++];
return x;
}
\end{lstlisting}
\vspace*{-1cm}
\end{minipage}
\end{center}
\begin{itemize}
\item[(d)]
Was bewirken die beiden Funktionen?
\points{2}
\item[(e)]
% Von welcher Ordnung (Landau-Symbol) sind die beiden Funktionen
% hinsichtlich der Anzahl ihrer Zugriffe auf die Zeichen im String -- und warum?
% Sie dürfen für \lstinline{strlen()} Ihre eigene Version der Funktion voraussetzen.
% \points 3
% \item[(f)]
Schreiben Sie eine eigene Funktion,
die dieselbe Aufgabe erledigt wie \lstinline{fun_2()},\\
nur effizienter.
\points{4}
\end{itemize}
\bigskip
\begin{flushright}
\textit{Viel Erfolg!}
\end{flushright}
\makeatletter
\immediate\write\@mainaux{\string\gdef\string\totalpoints{\arabic{points}}}
\makeatother
\end{document}
../common/logo-hochschule-bochum-cvh-text-v2.pdf
\ No newline at end of file
../common/logo-hochschule-bochum.pdf
\ No newline at end of file
../common/pgscript.sty
\ No newline at end of file
../common/pgslides.sty
\ No newline at end of file
......@@ -21,6 +21,7 @@ Vortragsfolien:
* [15.10.2018: Seiteneffekte, Funktionen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181015/hp-20181015.pdf)
* [22.10.2018: Zeiger, Arrays und Strings, Strukturen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181022/hp-20181022.pdf)
* [29.10.2018: Dateien und Fehlerbehandlung, Parameter des Hauptprogramms](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181029/hp-20181029.pdf)
* [05.11.2018: String-Operationen](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181105/hp-20181105.pdf)
* [alle in 1 Datei](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/hp-slides-2017ws.pdf)
Übungsaufgaben:
......@@ -29,12 +30,14 @@ Vortragsfolien:
* [15.10.2018: Fibonacci-Zahlen, fehlerhaftes Programm, "Hello, world!"](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181015/hp-uebung-20181015.pdf)
* [22.10.2018: ROT13-Verschlüsselung, Programm analysieren, Kalender-Berechnung](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181022/hp-uebung-20181022.pdf)
* [29.10.2018: Strings, Primzahlen, Datum-Bibliothek](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181029/hp-uebung-20181029.pdf)
* [05.11.2018: Ausgabe von Hexadezimalzahlen, Einfügen in Strings, Länge von Strings](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181105/hp-uebung-20181105.pdf)
Musterlösungen:
---------------
* [08.10.2018: ](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181008/hp-musterloesung-20181008.pdf)
* [15.10.2018: Fibonacci-Zahlen, fehlerhaftes Programm, "Hello, world!"](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181015/hp-musterloesung-20181015.pdf)
* [22.10.2018: ROT13-Verschlüsselung, Programm analysieren, Kalender-Berechnung](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181022/hp-musterloesung-20181022.pdf)
* [29.10.2018: Strings, Primzahlen, Datum-Bibliothek](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20181029/hp-musterloesung-20181029.pdf)
Tafelbilder:
------------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment