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

Vorbereitung 26.11.2020

parent 7560cc49
No related branches found
No related tags found
No related merge requests found
Showing
with 1476 additions and 0 deletions
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;
}
char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}
#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;
}
File added
% hp-20201126.pdf - Lecture Slides on Low-Level Programming
% Copyright (C) 2012, 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: Einführung in C: Arrays und Strings und Zeichen, Strukturen, Dateien und Fehlerbehandlung
\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{26.\ November 2020}
\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}}}
\item[\textbf{2}] \textbf{Einführung in C}
\begin{itemize}
\vspace*{-\smallskipamount}
\item[\dots]
\item[2.5] Verzweigungen
\item[2.6] Schleifen
\item[2.7] Strukturierte Programmierung
\item[2.8] Seiteneffekte
\item[2.9] Funktionen
\color{medgreen}
\item[2.10] Zeiger
\color{orange}
\item[2.11] Arrays und Strings
\color{red}
\item[2.12] Strukturen
\item[2.13] Dateien und Fehlerbehandlung
\color{black}
\item[2.14] Parameter des Hauptprogramms
\item[2.15] String-Operationen
\end{itemize}
\color{gray}
\item[\textbf{3}] \textbf{Bibliotheken}
\vspace*{-\smallskipamount}
\item[\textbf{\dots}]
% \item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
% \item[\textbf{5}] \textbf{Algorithmen}
% \item[\textbf{6}] \textbf{Ergänzungen und Ausblicke}
\end{itemize}
\end{frame}
\setcounter{section}{1}
\section{Einführung in C}
\setcounter{subsection}{9}
\subsection{Zeiger}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdio.h>
void calc_answer (int *a)
{
*a = 42;
}
int main (void)
{
int answer;
calc_answer (&answer);
printf ("The answer is %d.\n", answer);
return 0;
}
\end{lstlisting}
% \pause
\vspace{-5cm}\hspace{5cm}%
\begin{minipage}{7cm}
\begin{itemize}
\item
\lstinline{*a} ist eine \lstinline{int}.
% \pause
\item
unärer Operator \lstinline{*}:\\
Pointer-Dererefenzierung
% \pause
\arrowitem
\lstinline{a} ist ein Zeiger (Pointer) auf eine \lstinline{int}.
% \pause
\bigskip
\item
unärer Operator \lstinline{&}: Adresse
\end{itemize}
\end{minipage}
\end{frame}
\subsection{Arrays und Strings}
\begin{frame}[fragile]
\showsubsection
Ein Zeiger zeigt auf eine Variable\only<2->{ und deren Nachbarn}.
\bigskip
\pause
\pause
\begin{onlyenv}<1-8>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[5] = { 2, 3, 5, 7, 11 };
int *p = prime;
for (int i = 0; i < 5; i++)
printf ("%d\n", *(p + i));
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<9>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[5] = { 2, 3, 5, 7, 11 };
int *p = prime;
for (int i = 0; i < 5; i++)
printf ("%d\n", p[i]);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<10>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[5] = { 2, 3, 5, 7, 11 };
for (int i = 0; i < 5; i++)
printf ("%d\n", prime[i]);
return 0;
}
¡ ¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<11>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[5] = { 2, 3, 5, 7, 11 };
for (int *p = prime;
p < prime + 5; p++)
printf ("%d\n", *p);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<12>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[6] = { 2, 3, 5, 7, 11, 0 };
for (int *p = prime; *p; p++)
printf ("%d\n", *p);
return 0;
}
¡ ¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<13->
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
int prime[] = { 2, 3, 5, 7, 11, 0 };
for (int *p = prime; *p; p++)
printf ("%d\n", *p);
return 0;
}
¡ ¿
\end{lstlisting}
\end{onlyenv}
\pause
\vspace{-3.05cm}\hspace{5.5cm}%
\begin{minipage}{6.5cm}
\begin{itemize}
\item
\lstinline{prime} ist \alt<5->{ein Array}{eine Ansammlung} von\\fünf ganzen Zahlen.
\pause
\pause
\item
\only<6-9>{\begin{picture}(0,0)
\color{red}
\put(-1.6,0.1){\tikz{\draw[-latex](0.0,0.0)--(-1,0);}}
\end{picture}}%
\lstinline{prime} ist ein Zeiger auf eine \lstinline{int}.
\pause
\item
\lstinline{p + i} ist ein Zeiger\\
auf den \lstinline{i}-ten Nachbarn von \lstinline{*p}.
\pause
\item
\lstinline{*(p + i)} ist der \lstinline{i}-te Nachbar von \lstinline{*p}.
\pause
\item
Andere Schreibweise:\\
\lstinline{p[i]} statt \lstinline{*(p + i)}
\pause
\pause
\item
Zeiger-Arithmetik:\\
\lstinline{p++} rückt den Zeiger \lstinline{p}\\
um eine \lstinline{int} weiter.
\pause
\pause
\item
Array ohne \only<14->{explizite }Längenangabe:\\
Compiler zählt selbst
\vspace*{-1cm}
\pause
\begin{picture}(0,0)
\put(-5.2,1.0){\makebox(0,0)[br]{\color{red}\bf\shortstack{Die Länge des Arrays\\ist \emph{nicht\/} veränderlich!}}}
\end{picture}
\end{itemize}
\end{minipage}
\end{frame}
% \begin{frame}[fragile]
% \showsubsection
%
% \begin{lstlisting}
% #include <stdio.h>
%
% int main (void)
% {
% char hello_world[] = "Hello, world!\n";
% int i = 0;
% while (hello_world[i] != 0)
% printf ("%d", hello_world[i++]);
% return 0;
% }
% \end{lstlisting}
% \end{frame}
% \begin{frame}[fragile]
% \showsubsection
%
% \begin{lstlisting}
% #include <stdio.h>
%
% int main (void)
% {
% char hello_world[] = "Hello, world!\n";
% int i = 0;
% while (hello_world[i])
% printf ("%d", hello_world[i++]);
% return 0;
% }
% \end{lstlisting}
% \end{frame}
% \begin{frame}[fragile]
% \showsubsection
%
% \begin{lstlisting}
% #include <stdio.h>
%
% int main (void)
% {
% char hello_world[] = "Hello, world!\n";
% char *p = hello_world;
% while (*p)
% printf ("%c", *p++);
% return 0;
% }
% \end{lstlisting}
% \end{frame}
\begin{frame}[fragile]
\showsubsection
\begin{onlyenv}<1-6>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
char hello[] = "Hello, world!\n";
for (char *p = hello; *p; p++)
printf ("%d", *p);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<7>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
char hello[] = "Hello, world!\n";
for (char *p = hello; *p; p++)
printf ("%c", *p);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<8>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
char hello[] = "Hello, world!\n";
printf ("%s", hello);
return 0;
}
¡ ¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<9>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
char *hello = "Hello, world!\n";
printf ("%s", hello);
return 0;
}
¡ ¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<10>
\begin{lstlisting}[gobble=6]
#include <stdio.h>
int main (void)
{
char *hello = "Hello, world!\n";
while (*hello)
printf ("%c", *hello++);
return 0;
}
\end{lstlisting}
\end{onlyenv}
\vspace{-1.7cm}\hfill
\begin{minipage}{6.8cm}
\begin{itemize}
\pause[2]
\item
Ein \lstinline{char} ist eine kleinere \lstinline{int}.
\pause
\item
Ein "`String"' in C ist ein Array von \lstinline{char}s\only<4->{,\\
also ein Zeiger auf \lstinline{char}s}\only<5->{\\
also ein Zeiger auf (kleinere) Integer}.
\pause
\pause
\pause
\item
Der letzte \lstinline{char} muß 0 sein.\\
Er kennzeichnet das Ende des Strings.
\pause
\item
Die Formatspezifikation\\
entscheidet über die Ausgabe:\\[\smallskipamount]
\begin{tabular}{ll}
\lstinline|%d|\hspace*{0.5em}dezimal
& \lstinline|%c|\hspace*{0.5em}Zeichen\\
\lstinline|%x|\hspace*{0.5em}hexadezimal
\pause
& \lstinline|%s|\hspace*{0.5em}String
\end{tabular}
\vspace*{-1cm}
\end{itemize}
\end{minipage}
\end{frame}
\addtocounter{subsection}{-1}
\subsection{Arrays und Strings \protect\color{gray}und Zeichen}
\begin{frame}[fragile]
\showsubsection
\emph{"`Alles ist Zahl."'\/} -- Schule der Pythagoreer, 6.\ Jh.\ v.\,Chr.
\medskip
\begin{center}
\renewcommand{\arraystretch}{1.5}
\begin{tabular}{r}
\lstinline|"Hello"|\\
\lstinline|'H'|\\
\lstinline|'a' + 4|
\end{tabular}
\renewcommand{\arraystretch}{1.0}
\begin{tabular}{c}
ist nur eine andere\\
Schreibweise für
\end{tabular}
\renewcommand{\arraystretch}{1.5}
\begin{tabular}{l}
\lstinline|{ 72, 101, 108, 108, 111, 0 }|\\
\lstinline|72|\\
\lstinline|'e'|
\end{tabular}
\renewcommand{\arraystretch}{1.0}
\end{center}
\begin{itemize}
\item
Welchen Zahlenwert hat \lstinline{'*'} im Zeichensatz (normalerweise: ASCII)?\\
Welches Zeichen entspricht dem Zahlenwert \lstinline{71}?
\smallskip
\begin{lstlisting}[gobble=8]
printf ("%d\n", '*');
printf ("%c\n", 71);
\end{lstlisting}
\medskip
\item
Ist \lstinline{char ch} ein Großbuchstabe?
\smallskip
\begin{lstlisting}[gobble=8]
if (ch >= 'A' && ch <= 'Z')
...
\end{lstlisting}
\smallskip
\item
Groß- in Kleinbuchstaben umwandeln
\smallskip
\begin{lstlisting}[gobble=8]
ch += 'a' - 'A';
\end{lstlisting}
\end{itemize}
\vspace*{-1cm}
\end{frame}
\subsection{Strukturen}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdio.h>
typedef struct
{
char day, month;
int year;
}
date;
int main (void)
{
date today = { 24, 10, 2019 };
printf ("%d.%d.%d\n", today.day, today.month, today.year);
return 0;
}
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\vspace*{0.9mm}
\begin{minipage}[b]{6cm}
\begin{lstlisting}[gobble=6]
¡#include <stdio.h>
typedef struct
{
char day, month;
int year;
}
date;
void set_date (date *d)
{
(*d).day = 24;
(*d).month = 10;
(*d).year = 2019;
}¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[b]{6cm}
\begin{lstlisting}[gobble=6]
¡int main (void)
{
date today;
set_date (&today);
printf ("%d.%d.%d\n", today.day,
today.month, today.year);
return 0;
}¿
\end{lstlisting}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\vspace*{0.9mm}
\begin{minipage}[b]{6cm}
\begin{lstlisting}[gobble=6]
¡#include <stdio.h>
typedef struct
{
char day, month;
int year;
}
date;
void set_date (date *d)
{
d->day = 24;
d->month = 10;
d->year = 2019;
}¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[b]{6cm}
\hspace*{-1cm}%
\lstinline{foo->bar}
ist Abkürzung für
\lstinline{(*foo).bar}
\bigskip
\visible<2->{%
\hspace*{-1cm}%
Eine Funktion, die mit einem \lstinline{struct} arbeitet,\\
\hspace*{-1cm}%
kann man eine \newterm{Methode\/} des \lstinline{struct} nennen.}
\bigskip
\bigskip
\begin{lstlisting}[gobble=6]
¡int main (void)
{
date today;
set_date (&today);
printf ("%d.%d.%d\n", today.day,
today.month, today.year);
return 0;
}¿
\end{lstlisting}
\end{minipage}
\end{frame}
\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}
\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}}}
\item[\textbf{2}] \textbf{Einführung in C}
\begin{itemize}
\vspace*{-\smallskipamount}
\item[\dots]
\item[2.5] Verzweigungen
\item[2.6] Schleifen
\item[2.7] Strukturierte Programmierung
\item[2.8] Seiteneffekte
\item[2.9] Funktionen
\item[2.10] Zeiger
\color{medgreen}
\item[2.11] Arrays und Strings
\item[2.12] Strukturen
\item[2.13] Dateien und Fehlerbehandlung
\color{red}
\item[2.14] Parameter des Hauptprogramms
\item[2.15] String-Operationen
\end{itemize}
\item[\textbf{3}] \textbf{Bibliotheken}
\vspace*{-\smallskipamount}
\item[\textbf{\dots}]
% \item[\textbf{4}] \textbf{Hardwarenahe Programmierung}
% \item[\textbf{5}] \textbf{Algorithmen}
% \item[\textbf{6}] \textbf{Ergänzungen und Ausblicke}
\end{itemize}
\end{frame}
\end{document}
File added
% hp-musterloesung-20201126.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: Strings, Programm analysieren, fehlerhaftes Primzahl-Programm
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 26.\ November 2020}
\exercise{Strings}
Strings werden in der Programmiersprache C
durch Zeiger auf \lstinline{char}-Variable realisiert.
Wir betrachten die folgende Funktion (Datei: \gitfile{hp}{20201126}{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}{20201126}{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{Programm analysieren}
Wir betrachten das folgende C-Programm (Datei: \gitfile{hp}{20201126}{aufgabe-2.c}):
\begin{lstlisting}
char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}
\end{lstlisting}
\vspace{-\medskipamount}
\begin{itemize}
\item[(a)]
Was bewirkt dieses Programm?
\item[(b)]
Wofür stehen die Zahlen?
\item[(c)]
Ergänzen Sie das Programm derart, daß seine \lstinline{main()}-Funktion
\lstinline{int main (void)} lautet und eine \lstinline{return}-Anweisung hat,
wobei die in Aufgabenteil (a) festgestellte Eigenschaft erhalten bleiben soll.
\end{itemize}
\solution
\begin{itemize}
\item[(a)]
\textbf{Was bewirkt dieses Programm?}
Es gibt \emph{seinen eigenen Quelltext\/} aus.
(Wichtig ist die Bezugnahme auf den eigenen Quelltext.
Die Angabe\\
"`Es gibt
\lstinline|char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}|
aus"'\\
genügt insbesondere nicht.)
\item[(b)]
\textbf{Wofür stehen die Zahlen?}
Die 34 steht für ein Anführungszeichen und die 10 für ein
Zeilenendezeichen (\lstinline{\n}).
Hintergrund: Um den eigenen Quelltext ausgeben zu können, muß
das Programm auch Anführungszeichen und Zeilenendezeichen
ausgeben. Dies geschieht normalerweise mit vorangestelltem
Backslash: \lstinline{\"} bzw.\ \lstinline{\n}. Um dann aber
den Backslash ausgeben zu können, müßte man diesem ebenfalls
einen Backslash voranstellen: \lstinline{\\}. Damit dies nicht
zu einer Endlosschleife wird, verwendet der Programmierer
dieses Programms den Trick mit den Zahlen, die durch
\lstinline{%c} als Zeichen ausgegeben werden.
\item[(c)]
\textbf{Ergänzen Sie das Programm derart, daß seine \lstinline{main()}-Funktion
\lstinline{int main (void)} lautet und eine \lstinline{return}-Anweisung hat,
wobei die in Aufgabenteil (a) festgestellte Eigenschaft erhalten bleiben soll.}
Datei: \gitfile{hp}{20201126}{loesung-2.c}
\begin{lstlisting}[gobble=8]
char*f="char*f=%c%s%c;int main(void){printf(f,34,f,34,10);return 0;}%c";
int main(void){printf(f,34,f,34,10);return 0;}
\end{lstlisting}
Das Programm ist eine einzige, lange Zeile, die hier nur aus
Platzgründen als zwei Zeilen abgedruckt wird. Auf das
Semikolon am Ende der "`ersten Zeile"' folgt unmittelbar -- ohne Leerzeichen --
das Schlüsselwort \lstinline{int} am Anfang der "`zweiten Zeile"'.
Mit "`die in Aufgabenteil (a) festgestellte Eigenschaft"' ist
gemeint, daß das Programm weiterhin seinen eigenen Quelltext
ausgeben soll. Die Herausforderung dieser Aufgabe besteht
darin, das Programm zu modifizieren, ohne diese Eigenschaft zu
verlieren.
Zusatzaufgabe für Interessierte: Ergänzen Sie das Programm so,
daß es auch mit \lstinline[style=cmd]{-Wall} ohne Warnungen
compiliert werden kann.
Hinweis dazu: \lstinline{#include<stdio.h>}
(ohne Leerzeichen, um Platz zu sparen)
Lösung der Zusatzaufgabe: \gitfile{hp}{20201126}{loesung-2x.c}
\end{itemize}
\exercise{Fehlerhaftes Primzahl-Programm}
\begin{minipage}[t]{5.5cm}
Das nebenstehende Primzahlsuchprogramm (Datei: \gitfile{hp}{20201126}{aufgabe-3.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}{20201126}{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}{20201126}{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}{20201126}{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}{20201126}{loesung-2-4.c}.
\bigskip
Nach der Korrektur dieses Fehlers verhält sich das Programm korrekt.
Die Datei \gitfile{hp}{20201126}{loesung-2-4.c} enthält somit das korrigierte Programm.
\end{document}
File added
% hp-uebung-20201126.pdf - 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: Strings, Programm analysieren, fehlerhaftes Primzahl-Programm
\documentclass[a4paper]{article}
\usepackage{pgscript}
\thispagestyle{empty}
\begin{document}
\thispagestyle{empty}
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 26.\ November 2020}
\exercise{Strings}
Strings werden in der Programmiersprache C
durch Zeiger auf \lstinline{char}-Variable realisiert.
Wir betrachten die folgende Funktion (Datei: \gitfile{hp}{20201126}{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}
\exercise{Programm analysieren}
Wir betrachten das folgende C-Programm (Datei: \gitfile{hp}{20201126}{aufgabe-2.c}):
\begin{lstlisting}
char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}
\end{lstlisting}
\vspace{-\medskipamount}
\begin{itemize}
\item[(a)]
Was bewirkt dieses Programm?
\item[(b)]
Wofür stehen die Zahlen?
\item[(c)]
Ergänzen Sie das Programm derart, daß seine \lstinline{main()}-Funktion
\lstinline{int main (void)} lautet und eine \lstinline{return}-Anweisung hat,
wobei die in Aufgabenteil (a) festgestellte Eigenschaft erhalten bleiben soll.
\end{itemize}
\exercise{Fehlerhaftes Primzahl-Programm}
\begin{minipage}[t]{5.5cm}
Das nebenstehende Primzahlsuchprogramm (Datei: \gitfile{hp}{20201126}{aufgabe-3.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}
\end{document}
#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;
}
char*f="char*f=%c%s%c;int main(void){printf(f,34,f,34,10);return 0;}%c";int main(void){printf(f,34,f,34,10);return 0;}
#include<stdio.h>
char*f="#include<stdio.h>%cchar*f=%c%s%c;int main(void){printf(f,10,34,f,34,10);return 0;}%c";int main(void){printf(f,10,34,f,34,10);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;
}
../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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment