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

Musterlösung 17.10.2019

parent 23f79df3
Branches
No related tags found
No related merge requests found
File added
% hp-musterloesung-20191017.pdf - Solutions to the Exercises on Low-Level Programming / Applied Computer Sciences
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019 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: Schaltjahr ermitteln, Multiplikationstabelle, Fibonacci-Zahlen, fehlerhaftes Programm
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 17.\ Oktober 2019}
\exercise{Schaltjahr ermitteln}
Schreiben Sie ein C-Programm, das eine Jahreszahl erfragt
und ausgibt, ob es sich um ein Schaltjahr handelt.
\begin{itemize}
\item Wenn die Jahreszahl durch 4 teilbar ist, ist das Jahr zunächst einmal ein Schaltjahr.
\item Ausnahme: Wenn die Jahreszahl durch 100 teilbar ist, ist das Jahr kein Schaltjahr.
\item Ausnahme von der Ausnahme: Wenn die Jahreszahl durch 400 teilbar ist,\\
ist das Jahr doch wieder ein Schaltjahr.
\end{itemize}
\solution
Am einfachsten ist es, die Aufgabenstellung in geschachtelte
\lstinline{if}-Verzweigungen zu übersetzen.
Im folgenden finden Sie eine Funktion \lstinline{is_leap_year()},
der man das Jahr übergibt und die für Schaltjahre \lstinline{1}
zurückgibt und für Nicht-Schaltjahre \lstinline{0}.
\begin{lstlisting}
#include <stdio.h>
int is_leap_year (int year)
{
int leap_year = 0;
if (year % 4 == 0)
{
leap_year = 1;
if (year % 100 == 0)
{
leap_year = 0;
if (year % 400 == 0)
leap_year = 1;
}
}
return leap_year;
}
\end{lstlisting}
(In C steht \lstinline{0} für den Wahrheitswert "`falsch"'
und jeder Wert ungleich \lstinline{0} für den Wahrheitswert "`wahr'";
die Zeile \lstinline{leap_year = 0} steht daher wörtlich und
selbsterklärend für "`ist kein Schaltjahr"'.)
Unter Verwendung von \lstinline{else} läßt sich dies verkürzen zu:
\begin{lstlisting}
#include <stdio.h>
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;
}
\end{lstlisting}
Eine andere Möglichkeit ist es, die Schaltjahr-Bedingung in eine
Kette von "`und"'- und "`oder"'-Verknüpfungen
(C-Operatoren \lstinline{&&} und \lstinline{||}) zu übersetzen:
\begin{lstlisting}
int is_leap_year (int year)
{
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
return 1;
else
return 0;
}
\end{lstlisting}
Dies ist zwar kürzer, aber nicht unbedingt übersichtlicher.
Der erzeugte Code ist übrigens \emph{nicht\/} kürzer und/oder
effizienter als bei der Verwendung mehrerer
\lstinline{if}-Verzweigungen.
Wir empfehlen, daß Sie immer so programmieren,
daß Sie selbst den maximalen Überblick über Ihr Programm behalten.
\goodbreak
Ein Hauptprogramm, das die o.\,a.\ Funktion aufruft,
könnte dann wie folgt aussehen:
\begin{lstlisting}
int main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (is_leap_year (year))
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
\end{lstlisting}
In den Dateien \gitfile{hp}{20191017}{loesung-1-1.c} bis \gitfile{hp}{20191017}{loesung-1-3.c}
finden Sie lauffähige Programme, die die o.\,a.\ Funktionen aufrufen.
Beachten Sie, daß die Funktion \emph{vor\/} dem Hauptprogramm
deklariert werden muß, damit das Hauptprogramm sie kennt.
(Es gibt Tricks, mit denen es auch anders geht,
aber was hätten wir in diesem Zusammenhang davon?)
In \gitfile{hp}{20191017}{loesung-1-4.c} und \gitfile{hp}{20191017}{loesung-1-5.c}
findet die Schaltjahr-Prüfung direkt im Hauptprogramm statt.
Dies ist ebenfalls eine richtige Lösung der Aufgabe,
schränkt aber die Wiederverwertbarkeit des Codes ein.
Die Datei \gitfile{hp}{20191017}{loesung-1-4.c} enthält darüberhinaus Codeverdopplungen,
nämlich mehrere identische \lstinline{printf()}-Auf"-rufe
an unterschiedlichen Stellen.
Dies ist schlechter Programmierstil ("`Cut-and-paste-Programmierung"').
Die besten Lösungen sind \gitfile{hp}{20191017}{loesung-1-2.c}
und \gitfile{hp}{20191017}{loesung-1-3.c}.
\goodbreak
Zum Testen:\vspace*{-\medskipamount}
\begin{itemize}\itemsep0pt
\item 1900 ist kein Schaltjahr.
\item 1902 ist kein Schaltjahr.
\item 1904 ist ein Schaltjahr.
\item 1996 ist ein Schaltjahr.
\item 1998 ist kein Schaltjahr.
\item 2000 ist ein Schaltjahr.
\item 2002 ist kein Schaltjahr.
\item 2004 ist ein Schaltjahr.
\item 2016 ist ein Schaltjahr.
\item 2017 ist kein Schaltjahr.
\item 2018 ist kein Schaltjahr.
\item 2019 ist kein Schaltjahr.
\end{itemize}
\goodbreak
Hier noch ein Hinweis für Unix-Shell-Experten:
\begin{lstlisting}[style=cmd]
for y in 1 2 3 4 5; do
clear
for x in 1900 1902 1904 1996 1998 2000 2002 2004 2016 2017 2018 2019; do
echo $x | ./loesung-1-$y
done
sleep 2s
done
\end{lstlisting}
\exercise{Multiplikationstabelle}
Geben Sie mit Hilfe einer Schleife ein "`Einmaleins"' aus.\\
Dabei sollen die Faktoren und Ergebnisse rechtsbündig untereinander stehen:
\begin{lstlisting}[style=terminal]
1 * 7 = 7
2 * 7 = 14
...
10 * 7 = 70
\end{lstlisting}
Hinweis: Verwenden Sie Formatspezifikationen wie z.\,B.\ \lstinline{%3d}\\
(siehe dazu die Dokumentation zu \lstinline{printf()},
z.\,B.\ \,\lstinline[style=cmd]{man 3 printf}\,)
\solution
Drei verschiedene richtige Lösungen finden Sie in den Dateien
\gitfile{hp}{20191017}{loesung-2-1.c}, \gitfile{hp}{20191017}{loesung-2-2.c} und \gitfile{hp}{20191017}{loesung-2-3.c}.
(Zum Compilieren von \gitfile{hp}{20191017}{loesung-2-2.c} und \gitfile{hp}{20191017}{loesung-2-3.c}
ist mindestens der C99-Standard erforderlich; bitte nötigenfalls
in \file{gcc} die Option \lstinline[style=cmd]{-std=c99} mit angeben.)
Die Lösung in \gitfile{hp}{20191017}{loesung-2-3.c} ist zwar richtig,
aber unnötig kompliziert und daher nicht empfohlen.
Eine \textbf{falsche} Lösung finden Sie in der Datei \gitfile{hp}{20191017}{loesung-2-f4.c}:
In der Ausgabe dieses Programms stehen die Faktoren und Ergebnisse
nicht rechtsbündig untereinander.
\exercise{Fibonacci-Zahlen}
Die Folge der Fibonacci-Zahlen ist definiert durch:
\begin{quote}
1.\ Zahl: 0\\
2.\ Zahl: 1\\
nächste Zahl = Summe der beiden vorherigen
\end{quote}
Schreiben Sie ein Programm, das die ersten 50 Fibonacci-Zahlen ausgibt.
Falls Ihnen dabei irgendwelche Besonderheiten auffallen
und/oder Sie irgendwelche besondere Maßnahmen treffen,
dokumentieren Sie diese.
(Wem dies zu einfach ist, kann auch gerne
die ersten 100 Fibonacci-Zahlen ausgeben.)
\solution
Zwei verschiedene richtige Lösungen finden Sie in den Dateien
\gitfile{hp}{20191017}{loesung-3-1.c} und \gitfile{hp}{20191017}{loesung-3-2.c}.
Die Lösung in \gitfile{hp}{20191017}{loesung-3-2.c}
speichert alle berechneten Zahlen in einem Array,
die in \gitfile{hp}{20191017}{loesung-3-1.c} hingegen
speichert immer nur maximal drei Zahlen gleichzeitig.
Sofern nicht alle berechneten Zahlen später noch benötigt werden,
ist daher \gitfile{hp}{20191017}{loesung-3-1.c} zu bevorzugen.
Wichtig in \gitfile{hp}{20191017}{loesung-3-1.c} ist, daß \lstinline{f0 + f1} berechnet wird,
\emph{bevor\/} \lstinline{f0} oder \lstinline{f1} ein neuer Wert zugewiesen wird.
Dies ist nur möglich, weil das Programm
eine zusätzliche Variable (hier: \lstinline{f2}) verwendet.
\emph{(Fortsetzung folgt.)}
\exercise{Fehlerhaftes Programm}
\begin{minipage}[t]{0.65\textwidth}
Wir betrachten das nebenstehende C-Programm
(Datei: \gitfile{hp}{20191017}{aufgabe-4.c}).
\begin{itemize}
\item[(a)]
Was bewirkt dieses Programm? Begründen Sie Ihre Antwort.
Schreiben Sie Ihre Begründung so auf,
daß man sie auch dann versteht,
wenn man gerade nicht die Möglichkeit hat,
bei Ihnen persönlich nachzufragen
(z.\,B.\ weil man gerade eine Klausur korrigiert).
Die Schwierigkeit dieser Aufgabe besteht
nicht allein darin, die Problematik zu verstehen,
sondern auch darin, dieses Verständnis für andere aufzuschreiben.
\item[(b)]
Ändern Sie das Programm so um,
daß es einen "`Countdown"' von 10 bis 0 ausgibt.
\end{itemize}
\end{minipage}\hfill
\begin{minipage}[t]{0.3\textwidth}
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
for (int i = 10; i = 0; i - 1)
printf ("%d\n", i);
return 0;
}
\end{lstlisting}
\end{minipage}
\solution
\begin{itemize}
\item[(a)]
\textbf{Was bewirkt dieses Programm und warum?}
Dieses Programm bewirkt nichts.
Die \lstinline{for}-Schleife wird nicht ausgeführt.
Begründung: Die \lstinline{for}-Bedingung ist eine Zuweisung
des Werts \lstinline{0} an die Variable \lstinline{i}.
Neben dem Seiteneffekt der Zuweisung liefert der Ausdruck
einen Wert zurück, nämlich den zugewiesenen Wert
\lstinline{0}. Dieser wird von \lstinline{for} als eine
Bedingung mit dem konstanten Wert "`falsch"' interpretiert.
(Hinweis: Ohne diese Begründung ist die Aufgabe nur zu einem
kleinen Teil gelöst.)
Darüberhinaus ist die Zähl-Anwendung unwirksam: Sie berechnet
den Wert \lstinline{i - 1} und vergißt ihn wieder, ohne ihn
einer Variablen (z.\,B.\ \lstinline{i}) zuzuweisen.
\item[(b)]
\textbf{Ändern Sie das Programm so, daß es einen "`Countdown"' von 10 bis 0 ausgibt.}
Datei \gitfile{hp}{20191017}{loesung-4.c}:
\begin{lstlisting}[gobble=8]
#include <stdio.h>
int main (void)
{
for (int i = 10; i >= 0; i--)
printf ("%d\n", i);
return 0;
}
\end{lstlisting}
\end{itemize}
\end{document}
...@@ -26,8 +26,6 @@ ...@@ -26,8 +26,6 @@
\usepackage{pgscript} \usepackage{pgscript}
\thispagestyle{empty}
\begin{document} \begin{document}
\thispagestyle{empty} \thispagestyle{empty}
......
#include <stdio.h>
int is_leap_year (int year)
{
int leap_year = 0;
if (year % 4 == 0)
{
leap_year = 1;
if (year % 100 == 0)
{
leap_year = 0;
if (year % 400 == 0)
leap_year = 1;
}
}
return leap_year;
}
int main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (is_leap_year (year))
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
#include <stdio.h>
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 main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (is_leap_year (year))
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
#include <stdio.h>
int is_leap_year (int year)
{
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
return 1;
else
return 0;
}
int main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (is_leap_year (year))
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
#include <stdio.h>
int main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (year % 4 == 0)
{
if (year % 100 == 0)
{
if (year % 400 == 0)
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
}
else
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
}
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
#include <stdio.h>
int main (void)
{
int year;
printf ("Bitte geben Sie eine Jahreszahl ein: ");
scanf ("%d", &year);
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
printf ("Das Jahr %d ist ein Schaltjahr.\n", year);
else
printf ("Das Jahr %d ist kein Schaltjahr.\n", year);
return 0;
}
#include <stdio.h>
int main (void)
{
int a = 1;
int b = 7;
while (a <= 10)
{
printf ("%2d * %d = %2d\n", a, b, a * b);
a++;
}
return 0;
}
#include <stdio.h>
int main (void)
{
int x = 7;
for (int i = 1; i <= 10; i++)
printf ("%2d *%2d =%3d\n", i, x, i * x);
return 0;
}
#include <stdio.h>
int main (void)
{
int x = 7;
for (int i = 1; i <= 10; i++)
{
if (i >= 10)
printf ("%d", i);
else
printf (" %d", i);
printf (" * %d = ", x);
int y = i * x;
if (y >= 10)
printf ("%d", y);
else
printf (" %d", y);
printf ("\n");
}
return 0;
}
#include <stdio.h>
int main (void)
{
int x = 7;
for (int i = 1; i <= 10; i++)
printf ("%d * %d = %d\n", i, x, i * x);
return 0;
}
#include <stdio.h>
int main (void)
{
int f0 = 0;
int f1 = 1;
for (int i = 0; i < 50; i++)
{
printf ("f[%d] = %d\n", i, f0);
int f2 = f0 + f1;
f0 = f1;
f1 = f2;
}
return 0;
}
#include <stdio.h>
int main (void)
{
int f[50];
f[0] = 0;
f[1] = 1;
for (int i = 2; i < 50; i++)
f[i] = f[i - 2] + f[i - 1];
for (int i = 0; i < 50; i++)
printf ("f[%d] = %d\n", i, f[i]);
return 0;
}
#include <stdio.h>
int main (void)
{
for (int i = 10; i >= 0; i--)
printf ("%d\n", i);
return 0;
}
...@@ -30,7 +30,7 @@ Vortragsfolien und Beispiele: ...@@ -30,7 +30,7 @@ Vortragsfolien und Beispiele:
Musterlösungen: Musterlösungen:
--------------- ---------------
(keine) * [17.10.2019: Schaltjahr ermitteln, Multiplikationstabelle, Fibonacci-Zahlen, fehlerhaftes Programm](https://gitlab.cvh-server.de/pgerwinski/hp/raw/master/20191017/hp-musterloesung-20191017.pdf)
Tafelbilder: Tafelbilder:
------------ ------------
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment